From 224beb19d720dc4d928d2978493fdd8915a4f7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Sulymosi?= Date: Fri, 18 Jul 2025 10:25:44 +0200 Subject: [PATCH 1/2] Implement the kitty strategy The strategy adds support for :Make/:Dispatch and :Start/:Spawn task execution using Kitty windows and tabs. :Make/:Dispatch tasks use kitty's get-text[^1] feature to avoid piping command outputs. This allows interactive debugging sessions and complex TUIs to function. It also allows outputs to display in color during execution and capture the output without ANSI codes. Environment isolation is implemented using the `--copy-env` and `--env` flags. Environment variables are copied over except the ones set by (neo)vim itself. [^1]: https://sw.kovidgoyal.net/kitty/remote-control/#kitten-get-text --- autoload/dispatch/kitty.vim | 113 ++++++++++++++++++++++++++++++++++++ plugin/dispatch.vim | 1 + 2 files changed, 114 insertions(+) create mode 100644 autoload/dispatch/kitty.vim diff --git a/autoload/dispatch/kitty.vim b/autoload/dispatch/kitty.vim new file mode 100644 index 0000000..686b7e0 --- /dev/null +++ b/autoload/dispatch/kitty.vim @@ -0,0 +1,113 @@ +" dispatch.vim kitty strategy +" +" This handler requires you to enable kitty's remote control feature. +" See https://sw.kovidgoyal.net/kitty/remote-control/ + +if exists('g:autoloaded_dispatch_kitty') + finish +endif +let g:autoloaded_dispatch_kitty = 1 + +let s:waiting = {} + +" ----------------------------------------------------------------------------- +" Public handler interface + +function! dispatch#kitty#handle(request) abort + if empty($KITTY_LISTEN_ON) || !executable('kitten') + return 0 + endif + + if a:request.action ==# 'make' + return s:make(a:request) + elseif a:request.action ==# 'start' + return s:start(a:request) + endif +endfunction + +function! dispatch#kitty#activate(pid) abort + call system('kitten @ focus-window --match pid:' . a:pid) + return !v:shell_error +endfunction + +" ----------------------------------------------------------------------------- +" Private implementation of the "kitty" handler + +" Handle: :Start, :Spawn +function! s:start(request) abort + let cmd = s:kitty_launch_cmd('tab', a:request, dispatch#prepare_start(a:request)) + + call system(cmd) + return 1 +endfunction + +" Handle: :Dispatch, :Make +function! s:make(request) abort + let qf_height = get(g:, 'dispatch_quickfix_height', 10) + if get(a:request, 'background', 0) || (qf_height <= 0 && dispatch#has_callback()) + let type = 'tab' + else + let type = 'window' + endif + + let cmd_with_capturing = a:request.expanded . + \ '; echo ' . dispatch#status_var() . ' > ' . a:request.file . '.complete' . + \ '; kitten @ get-text --match=id:$KITTY_WINDOW_ID --ansi=no --extent all > ' . a:request.file + + let cmd = s:kitty_launch_cmd(type, a:request, dispatch#prepare_start(a:request, cmd_with_capturing, 'make')) + let output = system(cmd) + let window_id = matchstr(output, '^\d\+$') + + if !empty(window_id) + let s:waiting[window_id] = a:request + return 1 + endif +endfunction + +" https://sw.kovidgoyal.net/kitty/launch/ +function! s:kitty_launch_cmd(type, request, command) abort + let cmd = 'kitten @ launch' + let cmd .= ' --cwd=' . shellescape(a:request.directory) + let cmd .= ' --copy-env --env SHLVL --env PWD --env VIM --env VIMRUNTIME --env MYVIMRC --env NVIM_LOG_FILE' + + if a:request.background || a:request.action ==# 'make' + let cmd .= ' --keep-focus' + endif + + if a:type ==# 'tab' + let cmd .= ' --type=tab --tab-title=' . shellescape(a:request.title) + else + let cmd .= ' --type=window --location=hsplit --window-title=' . shellescape(a:request.title) + let bias = get(g:, 'dispatch_kitty_bias', 0) + if bias != 0 + let cmd .= ' --bias=' . bias + endif + endif + + return cmd . ' sh -c ' . shellescape(a:command) +endfunction + +" Section: Without callback - polling + +function! s:poll() abort + if empty(s:waiting) + return + endif + + for [window_id, request] in items(s:waiting) + if !s:kitty_win_exists(window_id) + call remove(s:waiting, window_id) + call dispatch#complete(request) + endif + endfor +endfunction + +function! s:kitty_win_exists(wid) abort + call system('kitten @ ls --match id:' . a:wid) + return !v:shell_error +endfunction + +augroup dispatch_kitty + autocmd! + autocmd VimResized * nested if !dispatch#has_callback() | call s:poll() | endif +augroup END diff --git a/plugin/dispatch.vim b/plugin/dispatch.vim index d639e9c..1bd0ca1 100644 --- a/plugin/dispatch.vim +++ b/plugin/dispatch.vim @@ -87,6 +87,7 @@ endfunction if !exists('g:dispatch_handlers') let g:dispatch_handlers = [ \ 'tmux', + \ 'kitty', \ 'job', \ 'screen', \ 'terminal', From 53defe1587224ce5a111a2f71c51f086dd51b1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Sulymosi?= Date: Sun, 10 Aug 2025 17:02:17 +0200 Subject: [PATCH 2/2] Add help section for kitty --- doc/dispatch.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/dispatch.txt b/doc/dispatch.txt index 2ee97b6..712f668 100644 --- a/doc/dispatch.txt +++ b/doc/dispatch.txt @@ -183,6 +183,27 @@ The tmux strategy can be used from the GUI as well. Either start Vim from inside of tmux or assign g:tmux_session. This will use a new window for foreground makes rather than a split. +Kitty ~ + +Foreground makes open in a split respecting the current layout. The closure +of the pane triggers a |VimResized| event which loads the results into the +quickfix list if callbacks are not supported. + +This handler requires you to enable kitty's remote control feature via Unix +socket because (neo)vim has no /dev/tty configured (neovim/neovim#1496). +> + allow_remote_control yes + listen_on unix:/tmp/kitty +< +See https://sw.kovidgoyal.net/kitty/remote-control/ + +Kitty does not have a way to specify window size and direction explicitly +because those depend on the layout. To controll the window size, adjust the +bias Kitty uses to calculate the window size. It is passed as "--bias=" +to the "kitten @ launch" command. +> + let g:dispatch_kitty_bias = 20 +< Job ~ Uses the |job| support in Vim 8 and Neovim to update the quickfix list in real