@@ -9,7 +9,15 @@ local Timer = require('opencode.ui.timer')
99
1010local M = {}
1111
12- local active_sessions = {}
12+ --- @class OpencodeQuickChatRunningSession
13+ --- @field buf integer Buffer handle
14+ --- @field row integer Row position for spinner
15+ --- @field col integer Column position for spinner
16+ --- @field spinner CursorSpinner Spinner instance
17+ --- @field timestamp integer Timestamp when session started
18+
19+ --- @type table<string , OpencodeQuickChatRunningSession>
20+ local running_sessions = {}
1321
1422--- Simple cursor spinner using the same animation logic as loading_animation.lua
1523local CursorSpinner = {}
@@ -126,7 +134,7 @@ local function cleanup_session(session_info, session_id, message)
126134 if session_info and session_info .spinner then
127135 session_info .spinner :stop ()
128136 end
129- active_sessions [session_id ] = nil
137+ running_sessions [session_id ] = nil
130138 if message then
131139 vim .notify (message , vim .log .levels .WARN )
132140 end
@@ -150,15 +158,12 @@ end
150158--- @param response_text string Response text that may contain JSON in code blocks
151159--- @return table | nil replacement_data Parsed replacement data or nil if invalid
152160local function parse_replacement_json (response_text )
153- -- Try to extract JSON from response text (handle cases where JSON is in code blocks)
154161 local json_text = response_text
155- -- Look for JSON in code blocks
156162 local json_match = response_text :match (' ```json\n (.-)\n ```' ) or response_text :match (' ```\n (.-)\n ```' )
157163 if json_match then
158164 json_text = json_match
159165 end
160166
161- -- Try to parse JSON format
162167 local ok , replacement_data = pcall (vim .json .decode , json_text )
163168 if not ok then
164169 return nil
@@ -176,6 +181,7 @@ local function parse_replacement_json(response_text)
176181end
177182
178183--- Converts object format like {"1": "line1", "2": "line2"} to array
184+ --- Some LLMs may return line replacements in this format instead of an array
179185--- @param obj_lines table Object with string keys representing line numbers
180186--- @return string[] lines_array Array of lines in correct order
181187local function convert_object_to_lines_array (obj_lines )
@@ -236,7 +242,6 @@ local function apply_line_replacements(buf, replacement_data)
236242 end
237243 end
238244
239- -- Apply replacement if valid
240245 if start_line and start_line >= 1 and start_line <= buf_line_count and new_lines and # new_lines > 0 then
241246 local start_idx = math.floor (math.max (0 , start_line - 1 ))
242247 local end_idx = math.floor (math.min (end_line , buf_line_count ))
@@ -274,28 +279,28 @@ local function process_response(session_info, messages)
274279end
275280
276281--- Hook function called when a session is done thinking (no more pending messages)
277- --- @param session_obj Session The session object
278- local on_done = Promise .async (function (session_obj )
279- if not (session_obj .title and vim .startswith (session_obj .title , ' [QuickChat]' )) then
282+ --- @param active_session Session The session object
283+ local on_done = Promise .async (function (active_session )
284+ if not (active_session .title and vim .startswith (active_session .title , ' [QuickChat]' )) then
280285 return
281286 end
282287
283- local session_info = active_sessions [ session_obj .id ]
284- if not session_info then
288+ local running_session = running_sessions [ active_session .id ]
289+ if not running_session then
285290 return
286291 end
287292
288- local messages = session .get_messages (session_obj ):await () --[[ @as OpencodeMessage[] ]]
293+ local messages = session .get_messages (active_session ):await () --[[ @as OpencodeMessage[] ]]
289294 if not messages then
290- cleanup_session (session_info , session_obj .id , ' Failed to update file with quick chat response' )
295+ cleanup_session (running_session , active_session .id , ' Failed to update file with quick chat response' )
291296 return
292297 end
293298
294- local success = process_response (session_info , messages )
299+ local success = process_response (running_session , messages )
295300 if success then
296- cleanup_session (session_info , session_obj .id ) -- Success cleanup (no error message )
301+ cleanup_session (running_session , active_session .id )
297302 else
298- cleanup_session (session_info , session_obj .id , ' Failed to update file with quick chat response' ) -- Error cleanup
303+ cleanup_session (running_session , active_session .id , ' Failed to update file with quick chat response' )
299304 end
300305
301306 -- @TODO: enable session deletion after testing
@@ -305,7 +310,6 @@ local on_done = Promise.async(function(session_obj)
305310 -- end)
306311end )
307312
308- --- Validates quick chat prerequisites
309313--- @param message string | nil The message to validate
310314--- @return boolean valid
311315--- @return string | nil error_message
329333--- @param context_config OpencodeContextConfig Context configuration
330334--- @param range table | nil Range information
331335--- @return table context_instance
332- local function setup_quick_chat_context (buf , context_config , range )
336+ local function init_context (buf , context_config , range )
333337 local context_instance = context .new_instance (context_config )
334338
335339 if range and range .start and range .stop then
@@ -383,7 +387,6 @@ local create_message = Promise.async(function(message, context_instance, options
383387 local parts = context .format_message_quick_chat (message , context_instance ):await ()
384388 local params = { parts = parts , system = table.concat (instructions , ' \n ' ), synthetic = true }
385389
386- -- Set model if specified
387390 local current_model = core .initialize_current_model ():await ()
388391 local target_model = options .model or quick_chat_config .default_model or current_model
389392 if target_model then
413416M .quick_chat = Promise .async (function (message , options , range )
414417 options = options or {}
415418
416- -- Validate prerequisites
417419 local valid , error_msg = validate_quick_chat_prerequisites (message )
418420 if not valid then
419421 vim .notify (error_msg or ' Unknown error' , vim .log .levels .ERROR )
@@ -426,17 +428,15 @@ M.quick_chat = Promise.async(function(message, options, range)
426428 local row , col = cursor_pos [1 ] - 1 , cursor_pos [2 ] -- Convert to 0-indexed
427429 local spinner = CursorSpinner .new (buf , row , col )
428430
429- -- Setup context
430431 local context_config = vim .tbl_deep_extend (' force' , create_context_config (range ~= nil ), options .context_config or {})
431- local context_instance = setup_quick_chat_context (buf , context_config , range )
432- -- Check prompt guard
432+ local context_instance = init_context (buf , context_config , range )
433+
433434 local allowed , err_msg = util .check_prompt_allowed (config .values .prompt_guard , context_instance :get_mentioned_files ())
434435 if not allowed then
435436 spinner :stop ()
436437 return Promise .new ():reject (err_msg or ' Prompt denied by prompt_guard' )
437438 end
438439
439- -- Create session
440440 local title = create_session_title (buf )
441441 local quick_chat_session = core .create_new_session (title ):await ()
442442 if not quick_chat_session then
@@ -447,15 +447,14 @@ M.quick_chat = Promise.async(function(message, options, range)
447447 -- TODO only for debug
448448 state .active_session = quick_chat_session
449449
450- active_sessions [quick_chat_session .id ] = {
450+ running_sessions [quick_chat_session .id ] = {
451451 buf = buf ,
452452 row = row ,
453453 col = col ,
454454 spinner = spinner ,
455455 timestamp = vim .uv .now (),
456456 }
457457
458- -- Create and send message
459458 local params = create_message (message , context_instance , options ):await ()
460459 spinner :stop ()
461460
@@ -466,7 +465,7 @@ M.quick_chat = Promise.async(function(message, options, range)
466465
467466 if not success then
468467 spinner :stop ()
469- active_sessions [quick_chat_session .id ] = nil
468+ running_sessions [quick_chat_session .id ] = nil
470469 vim .notify (' Error in quick chat: ' .. vim .inspect (err ), vim .log .levels .ERROR )
471470 end
472471end )
@@ -479,12 +478,12 @@ function M.setup()
479478 group = augroup ,
480479 callback = function (ev )
481480 local buf = ev .buf
482- for session_id , session_info in pairs (active_sessions ) do
481+ for session_id , session_info in pairs (running_sessions ) do
483482 if session_info .buf == buf then
484483 if session_info .spinner then
485484 session_info .spinner :stop ()
486485 end
487- active_sessions [session_id ] = nil
486+ running_sessions [session_id ] = nil
488487 end
489488 end
490489 end ,
@@ -493,12 +492,12 @@ function M.setup()
493492 vim .api .nvim_create_autocmd (' VimLeavePre' , {
494493 group = augroup ,
495494 callback = function ()
496- for _session_id , session_info in pairs (active_sessions ) do
495+ for _session_id , session_info in pairs (running_sessions ) do
497496 if session_info .spinner then
498497 session_info .spinner :stop ()
499498 end
500499 end
501- active_sessions = {}
500+ running_sessions = {}
502501 end ,
503502 })
504503end
0 commit comments