@@ -377,19 +377,81 @@ function ChatHandler:Cmd(params)
377377end
378378
379379--- Stops all ongoing processes by killing associated jobs.
380- --- @param signal number | nil Signal to send to the processes.
381- function ChatHandler :stop (signal )
380+ --- @param options table | number | nil Options table or signal number for backwards compatibility
381+ --- - options.signal: Signal to send to processes (default: 15)
382+ --- - options.buffer: Buffer number to stop jobs for (nil = stop all)
383+ --- - options.notify: Show notification after stopping (default: true)
384+ function ChatHandler :stop (options )
385+ -- Backwards compatibility: if options is a number, treat it as signal
386+ if type (options ) == " number" then
387+ options = { signal = options }
388+ end
389+ options = options or {}
390+ local signal = options .signal or 15
391+ local target_buf = options .buffer
392+ local show_notification = options .notify ~= false
393+
382394 if self .pool :is_empty () then
395+ if show_notification then
396+ logger .warning (" No active Parrot processes to stop" )
397+ end
383398 return
384399 end
385400
386- for _ , process_info in self .pool :ipairs () do
387- if process_info .job .handle ~= nil and not process_info .job .handle :is_closing () then
388- vim .uv .kill (process_info .job .pid , signal or 15 )
401+ local stopped_count = 0
402+ local cancelled_queries = {}
403+
404+ -- Collect jobs to stop (either for specific buffer or all)
405+ for i = # self .pool ._processes , 1 , - 1 do
406+ local process_info = self .pool ._processes [i ]
407+ local should_stop = target_buf == nil or process_info .buf == target_buf
408+
409+ if should_stop then
410+ -- Mark associated query as cancelled
411+ if process_info .qid then
412+ self .queries :mark_cancelled (process_info .qid , " user" )
413+ table.insert (cancelled_queries , process_info .qid )
414+ end
415+
416+ -- Kill the job
417+ if process_info .job .handle ~= nil and not process_info .job .handle :is_closing () then
418+ vim .uv .kill (process_info .job .pid , signal )
419+ stopped_count = stopped_count + 1
420+ end
421+
422+ -- Remove from pool
423+ table.remove (self .pool ._processes , i )
389424 end
390425 end
391426
392- self .pool = Pool :new ()
427+ -- Clean up extmarks only (preserve generated text)
428+ vim .schedule (function ()
429+ for _ , qid in ipairs (cancelled_queries ) do
430+ local qt = self .queries :get (qid )
431+ if qt then
432+ -- Clear namespace/extmarks only
433+ if qt .ns_id and qt .buf and vim .api .nvim_buf_is_valid (qt .buf ) then
434+ pcall (vim .api .nvim_buf_clear_namespace , qt .buf , qt .ns_id , 0 , - 1 )
435+ end
436+ end
437+ end
438+
439+ -- Show notification
440+ if show_notification then
441+ if stopped_count > 0 then
442+ local msg = stopped_count == 1 and " Stopped 1 process" or string.format (" Stopped %d processes" , stopped_count )
443+ if target_buf then
444+ msg = msg .. " in current buffer"
445+ end
446+ logger .info (msg )
447+ else
448+ logger .warning (" No active processes found" .. (target_buf and " in current buffer" or " " ))
449+ end
450+ end
451+
452+ -- Fire autocmd event for user hooks
453+ vim .cmd (" doautocmd User PrtCancelled" )
454+ end )
393455end
394456
395457--- Context command
@@ -1889,9 +1951,21 @@ function ChatHandler:query(buf, provider, payload, handler, on_exit)
18891951 end ,
18901952 })
18911953 job :start ()
1892- self .pool :add (job , buf )
1954+
1955+ -- Determine target type for better tracking
1956+ local target_type = " unknown"
1957+ if buf and vim .api .nvim_buf_is_valid (buf ) then
1958+ local file_name = vim .api .nvim_buf_get_name (buf )
1959+ local utils_module = require (" parrot.utils" )
1960+ if utils_module .is_chat (buf , file_name , self .options .chat_dir ) then
1961+ target_type = " chat"
1962+ end
1963+ end
1964+
1965+ self .pool :add (job , buf , qid , target_type )
18931966 logger .debug (" ChatHandler:query pool updated" , {
18941967 pool = self .pool ,
1968+ qid = qid ,
18951969 })
18961970end
18971971
0 commit comments