Skip to content

Commit 60c6e2a

Browse files
committed
fix(chat): simplify chat window reuse and command handling
Avoid toggling chat buffers through a separate entry point and fix tab-local chat reuse when buffers are hidden or visible elsewhere. Reuse or create chat windows in one code path, track tab chat buffers consistently, and open escaped scratch buffer names safely. - Removed the unused `force_new` parameter from `s:OpenChatWindow()`. - Removed the forwarding-only `vim_ai#AIChatToggleRun()`. - Removed the duplicate `settabvar()` from `vim_ai#AIChatRun()`. - Added `abort` to `s:ReuseOrCreateChatWindow()`. - Kept tab ownership based on `bufnr`, not scratch names. - Escaped scratch names with `fnameescape()` when calling `:file`. - Inlined trivial one-use window visibility logic into `s:ReuseOrCreateChatWindow()`.
1 parent ae113b8 commit 60c6e2a

File tree

2 files changed

+65
-94
lines changed

2 files changed

+65
-94
lines changed

autoload/vim_ai.vim

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -25,69 +25,42 @@ function! vim_ai#ImportPythonModules() abort
2525
call s:ImportPythonModules()
2626
endfunction
2727

28-
function! s:GetBufferWinIdInTab(bufnr, tabnr) abort
29-
for l:win_id in win_findbuf(a:bufnr)
30-
if win_id2tabwin(l:win_id)[0] == a:tabnr
31-
return l:win_id
32-
endif
33-
endfor
34-
return -1
35-
endfunction
36-
3728
function! s:GetTabChatBuffer(tabnr) abort
3829
let l:chat_bufnr = gettabvar(a:tabnr, 'vim_ai_chat_bufnr', -1)
3930
if l:chat_bufnr == -1 || !bufexists(l:chat_bufnr)
4031
return -1
4132
endif
42-
if getbufvar(l:chat_bufnr, "&filetype") !=# "aichat"
33+
if getbufvar(l:chat_bufnr, '&filetype') !=# 'aichat'
4334
return -1
4435
endif
4536
return l:chat_bufnr
4637
endfunction
4738

48-
function! s:IsBufferVisibleOutsideTab(bufnr, tabnr) abort
49-
for l:win_id in win_findbuf(a:bufnr)
50-
if win_id2tabwin(l:win_id)[0] != a:tabnr
51-
return 1
52-
endif
53-
endfor
54-
return 0
55-
endfunction
56-
5739
" Configures ai-chat scratch window.
5840
" - scratch_buffer_keep_open = 0
5941
" - opens new ai-chat every time
6042
" - excludes buffer from buffer list
6143
" - scratch_buffer_keep_open = 1
6244
" - keeps hidden ai-chat buffer for later reuse in the same tab
6345
" - keeps the buffer in the buffer list
64-
function! s:OpenChatWindow(open_conf, force_new) abort
65-
" open new buffer that will be used as a chat
66-
let l:open_cmd = has_key(g:vim_ai_open_chat_presets, a:open_conf)
67-
\ ? g:vim_ai_open_chat_presets[a:open_conf]
68-
\ : a:open_conf
46+
function! s:OpenChatWindow(open_conf) abort
47+
let l:open_cmd = get(g:vim_ai_open_chat_presets, a:open_conf, a:open_conf)
6948
execute l:open_cmd
7049

71-
let l:keep_open = g:vim_ai_chat['ui']['scratch_buffer_keep_open'] == '1'
50+
let l:keep_open = g:vim_ai_chat['ui']['scratch_buffer_keep_open'] ==# '1'
7251

7352
setlocal buftype=nofile
7453
setlocal noswapfile
75-
setlocal ft=aichat
76-
if l:keep_open
77-
setlocal bufhidden=hide
78-
else
79-
setlocal bufhidden=wipe
80-
endif
81-
if bufexists(s:scratch_buffer_name)
82-
" spawn another window if chat already exist
83-
let l:index = 2
84-
while bufexists(s:scratch_buffer_name . " " . l:index)
85-
let l:index += 1
86-
endwhile
87-
execute "file " . s:scratch_buffer_name . " " . l:index
88-
else
89-
execute "file " . s:scratch_buffer_name
90-
endif
54+
setlocal filetype=aichat
55+
execute 'setlocal bufhidden=' . (l:keep_open ? 'hide' : 'wipe')
56+
57+
let l:buffer_name = s:scratch_buffer_name
58+
let l:index = 2
59+
while bufexists(l:buffer_name)
60+
let l:buffer_name = s:scratch_buffer_name . ' ' . l:index
61+
let l:index += 1
62+
endwhile
63+
execute 'file ' . fnameescape(l:buffer_name)
9164
endfunction
9265

9366
let s:is_handling_paste_mode = 0
@@ -249,56 +222,59 @@ function! vim_ai#AIImageRun(uses_range, config, ...) range abort
249222
py3 run_ai_image(unwrap('l:context'))
250223
endfunction
251224

252-
function! s:ReuseOrCreateChatWindow(config)
253-
let l:open_conf = a:config['ui']['open_chat_command']
225+
function! s:ReuseOrCreateChatWindow(config) abort
254226
let l:tabnr = tabpagenr()
255-
let l:chat_bufnr = s:GetTabChatBuffer(l:tabnr)
256-
let l:force_new = a:config['ui']['force_new_chat'] == '1'
227+
let l:force_new = a:config['ui']['force_new_chat'] ==# '1'
257228

258-
if &filetype != 'aichat'
259-
if l:chat_bufnr != -1
260-
if s:IsBufferVisibleOutsideTab(l:chat_bufnr, l:tabnr)
261-
let l:chat_bufnr = -1
262-
endif
263-
endif
229+
if &filetype ==# 'aichat'
230+
call settabvar(l:tabnr, 'vim_ai_chat_bufnr', bufnr('%'))
231+
return
232+
endif
264233

265-
if l:force_new
266-
call s:OpenChatWindow(l:open_conf, 1)
267-
let l:new_tabnr = tabpagenr()
268-
let l:new_chat_bufnr = bufnr('%')
269-
call settabvar(l:new_tabnr, 'vim_ai_chat_bufnr', l:new_chat_bufnr)
270-
if l:new_tabnr == l:tabnr
271-
\ && l:chat_bufnr != -1
272-
\ && l:chat_bufnr != l:new_chat_bufnr
273-
\ && !s:IsBufferVisibleOutsideTab(l:chat_bufnr, l:tabnr)
274-
execute "bwipeout " . l:chat_bufnr
234+
let l:chat_bufnr = s:GetTabChatBuffer(l:tabnr)
235+
let l:chat_win_id = -1
236+
let l:is_visible_outside_tab = 0
237+
238+
if l:chat_bufnr != -1
239+
for l:win_id in win_findbuf(l:chat_bufnr)
240+
let l:win_tabnr = win_id2tabwin(l:win_id)[0]
241+
if l:win_tabnr == l:tabnr
242+
let l:chat_win_id = l:win_id
243+
else
244+
let l:is_visible_outside_tab = 1
275245
endif
276-
return
277-
endif
246+
endfor
278247

279-
if l:chat_bufnr != -1
280-
let l:chat_win_id = s:GetBufferWinIdInTab(l:chat_bufnr, l:tabnr)
281-
if l:chat_win_id != -1
282-
call win_gotoid(l:chat_win_id)
283-
return
284-
endif
285-
call s:OpenChatWindow(l:open_conf, 1)
286-
let l:new_buffer = bufnr('%')
287-
execute "buffer " . l:chat_bufnr
288-
execute "bd " . l:new_buffer
289-
return
248+
if l:is_visible_outside_tab
249+
let l:chat_bufnr = -1
250+
let l:chat_win_id = -1
290251
endif
252+
endif
291253

292-
call s:OpenChatWindow(l:open_conf, 0)
254+
if !l:force_new && l:chat_win_id != -1
255+
call win_gotoid(l:chat_win_id)
293256
call settabvar(tabpagenr(), 'vim_ai_chat_bufnr', bufnr('%'))
257+
return
294258
endif
295-
endfunction
296259

297-
function! vim_ai#AIChatToggleRun(uses_range, config, ...) range abort
298-
if a:0 > 0
299-
execute a:firstline . "," . a:lastline . "call vim_ai#AIChatRun(" . a:uses_range . ", " . string(a:config) . ", " . string(a:1) . ")"
300-
else
301-
execute a:firstline . "," . a:lastline . "call vim_ai#AIChatRun(" . a:uses_range . ", " . string(a:config) . ")"
260+
call s:OpenChatWindow(a:config['ui']['open_chat_command'])
261+
let l:new_tabnr = tabpagenr()
262+
let l:new_chat_bufnr = bufnr('%')
263+
264+
if !l:force_new && l:chat_bufnr != -1
265+
execute 'buffer ' . l:chat_bufnr
266+
execute 'bwipeout ' . l:new_chat_bufnr
267+
call settabvar(tabpagenr(), 'vim_ai_chat_bufnr', bufnr('%'))
268+
return
269+
endif
270+
271+
call settabvar(l:new_tabnr, 'vim_ai_chat_bufnr', l:new_chat_bufnr)
272+
273+
if l:force_new
274+
\ && l:new_tabnr == l:tabnr
275+
\ && l:chat_bufnr != -1
276+
\ && l:chat_bufnr != l:new_chat_bufnr
277+
execute 'bwipeout ' . l:chat_bufnr
302278
endif
303279
endfunction
304280

@@ -343,12 +319,12 @@ function! vim_ai#AIChatRun(uses_range, config, ...) range abort
343319
let l:started_from_chat = &filetype == 'aichat'
344320

345321
let l:config_input = {
346-
\ "config_default": g:vim_ai_chat,
347-
\ "config_extension": a:config,
348-
\ "user_instruction": l:instruction,
349-
\ "user_selection": l:selection,
350-
\ "is_selection": l:is_selection,
351-
\ "command_type": 'chat',
322+
\ 'config_default': g:vim_ai_chat,
323+
\ 'config_extension': a:config,
324+
\ 'user_instruction': l:instruction,
325+
\ 'user_selection': l:selection,
326+
\ 'is_selection': l:is_selection,
327+
\ 'command_type': 'chat',
352328
\}
353329
let l:context = py3eval("make_ai_context(unwrap('l:config_input'))")
354330
let l:config = l:context['config']
@@ -358,7 +334,6 @@ function! vim_ai#AIChatRun(uses_range, config, ...) range abort
358334
try
359335
call s:set_paste(l:config)
360336
call s:ReuseOrCreateChatWindow(l:config)
361-
call settabvar(tabpagenr(), 'vim_ai_chat_bufnr', bufnr('%'))
362337

363338
let l:context['bufnr'] = bufnr()
364339
let l:bufnr = bufnr()
@@ -368,15 +343,12 @@ function! vim_ai#AIChatRun(uses_range, config, ...) range abort
368343
return
369344
endif
370345

371-
let s:last_command = "chat"
346+
let s:last_command = 'chat'
372347
let s:last_config = a:config
373348

374349
if py3eval("run_ai_chat(unwrap('l:context'))")
375350
if g:vim_ai_async_chat == 1
376-
377351
call setbufvar(l:bufnr, 'vim_ai_chat_start_last_line', line('$'))
378-
" if user switches to a different buffer, setup autocommand that
379-
" will clean undo history after returning back
380352
augroup AichatUndo
381353
au!
382354
autocmd BufEnter <buffer> call s:AIChatUndoCleanup()
@@ -402,7 +374,6 @@ function! vim_ai#AIChatStopRun() abort
402374
call s:AIChatUndoCleanup()
403375
endfunction
404376

405-
406377
" Function called in a timer that check if there are new lines from AI and
407378
" appned them in a buffer. It ends when AI thread is finished (or when
408379
" stopped).

plugin/vim-ai.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ cal vim_ai_provider#Register('openai', {
1313

1414
command! -range -nargs=? -complete=customlist,vim_ai#RoleCompletionComplete AI <line1>,<line2>call vim_ai#AIRun(<range>, {}, <q-args>)
1515
command! -range -nargs=? -complete=customlist,vim_ai#RoleCompletionEdit AIEdit <line1>,<line2>call vim_ai#AIEditRun(<range>, {}, <q-args>)
16-
command! -range -nargs=? -complete=customlist,vim_ai#RoleCompletionChat AIChat <line1>,<line2>call vim_ai#AIChatToggleRun(<range>, {}, <q-args>)
16+
command! -range -nargs=? -complete=customlist,vim_ai#RoleCompletionChat AIChat <line1>,<line2>call vim_ai#AIChatRun(<range>, {}, <q-args>)
1717
command! -range -nargs=? -complete=customlist,vim_ai#RoleCompletionImage AIImage <line1>,<line2>call vim_ai#AIImageRun(<range>, {}, <q-args>)
1818
command! -nargs=? AINewChat call vim_ai#AINewChatDeprecatedRun(<f-args>)
1919
command! AIRedo call vim_ai#AIRedoRun()

0 commit comments

Comments
 (0)