@@ -5,7 +5,6 @@ local core = require('opencode.core')
55local util = require (' opencode.util' )
66local session = require (' opencode.session' )
77local Promise = require (' opencode.promise' )
8- local search_replace = require (' opencode.quick_chat.search_replace' )
98local CursorSpinner = require (' opencode.quick_chat.spinner' )
109
1110local M = {}
@@ -16,6 +15,7 @@ local M = {}
1615--- @field col integer Column position for spinner
1716--- @field spinner CursorSpinner Spinner instance
1817--- @field timestamp integer Timestamp when session started
18+ --- @field range table | nil Range information
1919
2020--- @type table<string , OpencodeQuickChatRunningSession>
2121local running_sessions = {}
@@ -130,21 +130,56 @@ end
130130--- @param message OpencodeMessage Message object
131131--- @return string response_text
132132local function extract_response_text (message )
133+ if not message then
134+ return ' '
135+ end
136+
133137 local response_text = ' '
134138 for _ , part in ipairs (message .parts or {}) do
135139 if part .type == ' text' and part .text then
136140 response_text = response_text .. part .text
137141 end
138142 end
139143
144+ -- Remove code blocks and inline code
145+ response_text = response_text :gsub (' ```[^`]*```' , ' ' )
146+ response_text = response_text :gsub (' `[^`]*`' , ' ' )
147+
140148 return vim .trim (response_text )
141149end
142150
151+ --- Applies raw code response to buffer (simple replacement)
152+ --- @param buf integer Buffer handle
153+ --- @param response_text string The raw code response
154+ --- @param row integer Row position (0-indexed )
155+ --- @param range table | nil Range information { start = number , stop = number }
156+ --- @return boolean success Whether the replacement was successful
157+ local function apply_raw_code_response (buf , response_text , row , range )
158+ if response_text == ' ' then
159+ return false
160+ end
161+
162+ local lines = vim .split (response_text , ' \n ' )
163+
164+ if range then
165+ -- Replace the selected range
166+ local start_line = range .start - 1 -- Convert to 0-indexed
167+ local end_line = range .stop - 1 -- Convert to 0-indexed
168+ vim .api .nvim_buf_set_lines (buf , start_line , end_line + 1 , false , lines )
169+ else
170+ -- Replace current line
171+ vim .api .nvim_buf_set_lines (buf , row , row + 1 , false , lines )
172+ end
173+
174+ return true
175+ end
176+
143177--- Processes response from quickchat session
144178--- @param session_info table Session tracking info
145179--- @param messages OpencodeMessage[] Session messages
180+ --- @param range table | nil Range information
146181--- @return boolean success Whether the response was processed successfully
147- local function process_response (session_info , messages )
182+ local function process_response (session_info , messages , range )
148183 local response_message = messages [# messages ]
149184 if # messages < 2 and (not response_message or response_message .info .role ~= ' assistant' ) then
150185 return false
@@ -157,39 +192,12 @@ local function process_response(session_info, messages)
157192 return false
158193 end
159194
160- local replacements , parse_warnings = search_replace .parse_blocks (response_text )
161-
162- -- Show parse warnings
163- if # parse_warnings > 0 then
164- for _ , warning in ipairs (parse_warnings ) do
165- vim .notify (' Quick chat: ' .. warning , vim .log .levels .WARN )
166- end
167- end
168-
169- if # replacements == 0 then
170- vim .notify (' Quick chat: No valid SEARCH/REPLACE blocks found in response' , vim .log .levels .WARN )
171- return false
172- end
173-
174- local success , errors , applied_count = search_replace .apply (session_info .buf , replacements , session_info .row )
175-
176- -- Provide detailed feedback
177- if applied_count > 0 then
178- local total_blocks = # replacements
179- if applied_count == total_blocks then
180- vim .notify (
181- string.format (' Quick chat: Applied %d change%s' , applied_count , applied_count > 1 and ' s' or ' ' ),
182- vim .log .levels .INFO
183- )
184- else
185- vim .notify (string.format (' Quick chat: Applied %d/%d changes' , applied_count , total_blocks ), vim .log .levels .INFO )
186- end
187- end
188-
189- if # errors > 0 then
190- for _ , err in ipairs (errors ) do
191- vim .notify (' Quick chat: ' .. err , vim .log .levels .WARN )
192- end
195+ local success = apply_raw_code_response (session_info .buf , response_text , session_info .row , range )
196+ if success then
197+ local target = range and ' selection' or ' current line'
198+ vim .notify (string.format (' Quick chat: Replaced %s with generated code' , target ), vim .log .levels .INFO )
199+ else
200+ vim .notify (' Quick chat: Failed to apply raw code response' , vim .log .levels .WARN )
193201 end
194202
195203 return success
@@ -213,7 +221,7 @@ local on_done = Promise.async(function(active_session)
213221 return
214222 end
215223
216- local success = process_response (running_session , messages )
224+ local success = process_response (running_session , messages , running_session . range )
217225 if success then
218226 cleanup_session (running_session , active_session .id )
219227 else
@@ -262,77 +270,28 @@ local function create_context_config(has_range)
262270 }
263271end
264272
265- --- Generates instructions for the LLM to follow the SEARCH/REPLACE format
266- --- This is inspired from Aider Chat approach
273+ --- Generates instructions for raw code generation mode
267274--- @param context_config OpencodeContextConfig Context configuration
268275--- @return string[] instructions Array of instruction lines
269- local generate_search_replace_instructions = Promise .async (function (context_config )
270- local base_instructions = {
271- ' You are a patch generation engine.' ,
272- ' TASK:' ,
273- ' Generate search/replace blocks to implement the requested change.' ,
274- ' ' ,
275- ' OUTPUT FORMAT (MANDATORY):' ,
276- ' <<<<<<< SEARCH' ,
277- ' [exact original code]' ,
278- ' =======' ,
279- ' [modified code]' ,
280- ' >>>>>>> REPLACE' ,
281- ' ' ,
282- ' RULES:' ,
283- ' - Output ONLY RAW patch blocks' ,
284- ' - Marker lines must match EXACTLY' ,
285- ' - Include 1-3 lines of context for unique matching' ,
286- ' - Only REPLACE may differ' ,
287- ' - Preserve whitespace' ,
288- ' - NEVER add explanations or extra text' ,
289- ' ' ,
290- ' EXAMPLES (use ONLY as reference):' ,
291- ' Example 1 - Fix function:' ,
292- ' <<<<<<< SEARCH' ,
293- ' function hello() {' ,
294- ' console.log("hello")' ,
295- ' }' ,
296- ' =======' ,
297- ' function hello() {' ,
298- ' console.log("hello");' ,
299- ' }' ,
300- ' >>>>>>> REPLACE' ,
301- ' ' ,
302- ' Example 2 - Insert at cursor:' ,
303- ' <<<<<<< SEARCH' ,
304- ' ' ,
305- ' =======' ,
306- ' local new_variable = "value"' ,
307- ' >>>>>>> REPLACE' ,
308- ' ' ,
309- }
310-
311- local context_guidance = {}
312-
313- -- Check context configuration to determine guidance
314- if context_config .diagnostics and context_config .diagnostics .enabled then
315- table.insert (context_guidance , ' Fix [DIAGNOSTICS] only (if asked)' )
316- end
276+ local function generate_raw_code_instructions (context_config )
277+ local context_info = ' '
317278
318- if context_config .selection then
319- table.insert ( context_guidance , ' Modify only [SELECTED RANGE] ' )
279+ if context_config .selection and context_config . selection . enabled then
280+ context_info = ' You have been provided with a code selection [SELECTED CODE]. '
320281 elseif context_config .cursor_data and context_config .cursor_data .enabled then
321- table.insert (context_guidance , ' Modify only [CURSOR POSITION]' )
322- end
323-
324- if context_config .git_diff and context_config .git_diff .enabled then
325- table.insert (context_guidance , " Use [GIT DIFF] only as reference (don't copy syntax)" )
282+ context_info = ' You have been provided with cursor context. [CURSOR POSITION]'
326283 end
327284
328- if # context_guidance > 0 then
329- table.insert (base_instructions , ' CONTEXT GUIDANCE: ' .. table.concat (context_guidance , ' , ' ) .. ' .' )
330- end
331-
332- table.insert (base_instructions , ' ' )
285+ local buf = vim .api .nvim_get_current_buf ()
286+ local filetype = vim .api .nvim_buf_get_option (buf , ' filetype' )
333287
334- return base_instructions
335- end )
288+ return {
289+ ' I want you to act as a senior ' .. filetype .. ' developer. ' .. context_info ,
290+ ' I will ask you specific questions and I want you to return raw code only ' ,
291+ ' (no codeblocks, no explanations). ' ,
292+ " If you can't respond with code, respond with nothing." ,
293+ }
294+ end
336295
337296--- Creates message parameters for quick chat
338297--- @param message string The user message
@@ -350,7 +309,8 @@ local create_message = Promise.async(function(message, buf, range, context_confi
350309 end
351310
352311 local result = context .format_quick_chat_message (message , context_config , format_opts ):await ()
353- local instructions = quick_chat_config .instructions or generate_search_replace_instructions (context_config ):await ()
312+
313+ local instructions = quick_chat_config .instructions or generate_raw_code_instructions (context_config )
354314
355315 local parts = {
356316 { type = ' text' , text = table.concat (instructions , ' \n ' ) },
@@ -421,6 +381,7 @@ M.quick_chat = Promise.async(function(message, options, range)
421381 col = col ,
422382 spinner = spinner ,
423383 timestamp = vim .uv .now (),
384+ range = range ,
424385 }
425386
426387 -- Set up global keymaps for quick chat
0 commit comments