refactor: refactor planning prompts to resolve line number issues and indentation issues (#382)
This commit is contained in:
parent
07c1d707da
commit
2997d4669a
@ -351,6 +351,7 @@ We would like to express our heartfelt gratitude to the contributors of the foll
|
|||||||
| [ChatGPT.nvim](https://github.com/jackMort/ChatGPT.nvim) | Apache 2.0 License | Calculation of tokens count | https://github.com/yetone/avante.nvim/blob/main/lua/avante/utils/tokens.lua |
|
| [ChatGPT.nvim](https://github.com/jackMort/ChatGPT.nvim) | Apache 2.0 License | Calculation of tokens count | https://github.com/yetone/avante.nvim/blob/main/lua/avante/utils/tokens.lua |
|
||||||
| [img-clip.nvim](https://github.com/HakonHarnes/img-clip.nvim) | MIT License | Clipboard image support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/clipboard.lua |
|
| [img-clip.nvim](https://github.com/HakonHarnes/img-clip.nvim) | MIT License | Clipboard image support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/clipboard.lua |
|
||||||
| [copilot.lua](https://github.com/zbirenbaum/copilot.lua) | MIT License | Copilot support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/providers/copilot.lua |
|
| [copilot.lua](https://github.com/zbirenbaum/copilot.lua) | MIT License | Copilot support | https://github.com/yetone/avante.nvim/blob/main/lua/avante/providers/copilot.lua |
|
||||||
|
| [Claude Engineer](https://github.com/Doriandarko/claude-engineer) | No License | Some prompts | https://github.com/yetone/avante.nvim/blob/main/lua/avante/llm.lua |
|
||||||
|
|
||||||
The high quality and ingenuity of these projects' source code have been immensely beneficial throughout our development process. We extend our sincere thanks and respect to the authors and contributors of these projects. It is the selfless dedication of the open-source community that drives projects like avante.nvim forward.
|
The high quality and ingenuity of these projects' source code have been immensely beneficial throughout our development process. We extend our sincere thanks and respect to the authors and contributors of these projects. It is the selfless dedication of the open-source community that drives projects like avante.nvim forward.
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ M.defaults = {
|
|||||||
model = "claude-3-5-sonnet-20240620",
|
model = "claude-3-5-sonnet-20240620",
|
||||||
timeout = 30000, -- Timeout in milliseconds
|
timeout = 30000, -- Timeout in milliseconds
|
||||||
temperature = 0,
|
temperature = 0,
|
||||||
max_tokens = 4096,
|
max_tokens = 8000,
|
||||||
["local"] = false,
|
["local"] = false,
|
||||||
},
|
},
|
||||||
---@type AvanteSupportedProvider
|
---@type AvanteSupportedProvider
|
||||||
|
@ -13,71 +13,97 @@ M.CANCEL_PATTERN = "AvanteLLMEscape"
|
|||||||
|
|
||||||
------------------------------Prompt and type------------------------------
|
------------------------------Prompt and type------------------------------
|
||||||
|
|
||||||
---@alias AvanteSystemPrompt string
|
-- Copy from: https://github.com/Doriandarko/claude-engineer/blob/15c94963cbf9d01b8ae7bbb5d42d7025aa0555d5/main.py#L276
|
||||||
local system_prompt = [[
|
|
||||||
You are an excellent programming expert.
|
|
||||||
]]
|
|
||||||
|
|
||||||
---@alias AvanteBasePrompt string
|
---@alias AvanteBasePrompt string
|
||||||
local planning_mode_prompt = [[
|
local planning_mode_system_prompt_tpl = [[
|
||||||
Your primary task is to suggest code modifications with precise line number ranges. Follow these instructions meticulously:
|
You are an AI coding agent that generates code according to the instructions. Follow these steps:
|
||||||
|
|
||||||
1. Carefully analyze the original code, paying close attention to its structure and line numbers. Line numbers start from 1 and include ALL lines, even empty ones.
|
1. Review the entire file content to understand the context:
|
||||||
|
${file_content}
|
||||||
|
|
||||||
2. When suggesting modifications:
|
2. Carefully analyze the selected code:
|
||||||
a. Use the language in the question to reply. If there are non-English parts in the question, use the language of those parts.
|
${selected_code}
|
||||||
b. Explain why the change is necessary or beneficial.
|
|
||||||
c. If an image is provided, make sure to use the image in conjunction with the code snippet.
|
|
||||||
d. Provide the exact code snippet to be replaced using this format:
|
|
||||||
|
|
||||||
Replace lines: {{start_line}}-{{end_line}}
|
3. Carefully analyze the specific instructions:
|
||||||
```{{language}}
|
${instructions}
|
||||||
{{suggested_code}}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Crucial guidelines for suggested code snippets:
|
4. Take into account the overall project context:
|
||||||
- Only apply the change(s) suggested by the most recent assistant message (before your generation).
|
${project_context}
|
||||||
- Do not make any unrelated changes to the code.
|
|
||||||
- Produce a valid full rewrite of the entire original file without skipping any lines. Do not be lazy!
|
|
||||||
- Do not arbitrarily delete pre-existing comments/empty Lines.
|
|
||||||
- Do not omit large parts of the original file for no reason.
|
|
||||||
- Do not omit any needed changes from the requisite messages/code blocks.
|
|
||||||
- If there is a clicked code block, bias towards just applying that (and applying other changes implied).
|
|
||||||
- Please keep your suggested code changes minimal, and do not include irrelevant lines in the code snippet.
|
|
||||||
- Maintain the SAME indentation in the returned code as in the source code
|
|
||||||
|
|
||||||
4. Crucial guidelines for line numbers:
|
5. Consider the memory of previous edits:
|
||||||
- The content regarding line numbers MUST strictly follow the format "Replace lines: {{start_line}}-{{end_line}}". Do not be lazy!
|
${memory_context}
|
||||||
- The range {{start_line}}-{{end_line}} is INCLUSIVE. Both start_line and end_line are included in the replacement.
|
|
||||||
- Count EVERY line, including empty lines and comments lines, comments. Do not be lazy!
|
|
||||||
- For single-line changes, use the same number for start and end lines.
|
|
||||||
- For multi-line changes, ensure the range covers ALL affected lines, from the very first to the very last.
|
|
||||||
- Double-check that your line numbers align perfectly with the original code structure.
|
|
||||||
|
|
||||||
5. Final check:
|
6. Consider the full context of all files in the project:
|
||||||
- Review all suggestions, ensuring each line number is correct, especially the start_line and end_line.
|
${full_file_contents_context}
|
||||||
- Confirm that no unrelated code is accidentally modified or deleted.
|
|
||||||
- Verify that the start_line and end_line correctly include all intended lines for replacement.
|
|
||||||
- Perform a final alignment check to ensure your line numbers haven't shifted, especially the start_line.
|
|
||||||
- Double-check that your line numbers align perfectly with the original code structure.
|
|
||||||
- Do not show the full content after these modifications.
|
|
||||||
|
|
||||||
Remember: Accurate line numbers are CRITICAL. The range start_line to end_line must include ALL lines to be replaced, from the very first to the very last. Double-check every range before finalizing your response, paying special attention to the start_line to ensure it hasn't shifted down. Ensure that your line numbers perfectly match the original code structure without any overall shift.
|
7. Generate SEARCH/REPLACE blocks for each necessary change. Each block should:
|
||||||
|
- Include enough context to uniquely identify the code to be changed
|
||||||
|
- Provide the exact replacement code, maintaining correct INDENTATION and FORMATTING
|
||||||
|
- Focus on specific, targeted changes rather than large, sweeping modifications
|
||||||
|
- The content in the SEARCH tag MUST NOT contain any of your generated content
|
||||||
|
- The content in the SEARCH tag MUST be based on the original content of the source file
|
||||||
|
- The content in the SEARCH tag needs to ensure a certain context to guarantee its UNIQUENESS
|
||||||
|
- The content in the REPLACE tag should also correspond to the context of the SEARCH tag
|
||||||
|
- There should be NO OVERLAP between the code of each SEARCH tag.
|
||||||
|
- DO NOT use ``` to wrap code blocks
|
||||||
|
|
||||||
|
8. Ensure that your SEARCH/REPLACE blocks:
|
||||||
|
- Address all relevant aspects of the instructions
|
||||||
|
- Maintain or enhance code readability and efficiency
|
||||||
|
- Consider the overall structure and purpose of the code
|
||||||
|
- Follow best practices and coding standards for the language
|
||||||
|
- Maintain consistency with the project context and previous edits
|
||||||
|
- Take into account the full context of all files in the project
|
||||||
|
|
||||||
|
IMPORTANT: MUST TO ADD EXPLANATIONS BEFORE AND AFTER EACH SEARCH/REPLACE BLOCK.
|
||||||
|
USE THE FOLLOWING FORMAT FOR EACH BLOCK:
|
||||||
|
|
||||||
|
<SEARCH>
|
||||||
|
Code to be replaced
|
||||||
|
</SEARCH>
|
||||||
|
<REPLACE>
|
||||||
|
New code to insert
|
||||||
|
</REPLACE>
|
||||||
|
|
||||||
|
If no changes are needed, return an empty list.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local editing_mode_prompt = [[
|
local editing_mode_system_prompt_tpl = [[
|
||||||
Your task is to modify the provided code according to the user's request. Follow these instructions precisely:
|
You are an AI coding agent that generates code according to the instructions. Follow these steps:
|
||||||
|
|
||||||
1. Carefully analyze the original code and the user's request.
|
1. Review the entire file content to understand the context:
|
||||||
2. Make the necessary modifications to the code as requested.
|
${file_content}
|
||||||
3. Return ONLY the complete modified code.
|
|
||||||
4. Do not include any explanations, comments, or line numbers in your response.
|
2. Carefully analyze the selected code:
|
||||||
5. Ensure the returned code is complete and can be directly used as a replacement for the original code.
|
${selected_code}
|
||||||
6. Preserve the original structure, indentation, and formatting of the code as much as possible.
|
|
||||||
7. Do not omit any parts of the code, even if they are unchanged.
|
3. Carefully analyze the specific instructions:
|
||||||
8. Maintain the SAME indentation in the returned code as in the source code
|
${instructions}
|
||||||
9. Do NOT include three backticks: ```
|
|
||||||
10. Only return code part, do NOT return the context part!
|
4. Take into account the overall project context:
|
||||||
|
${project_context}
|
||||||
|
|
||||||
|
5. Consider the memory of previous edits:
|
||||||
|
${memory_context}
|
||||||
|
|
||||||
|
6. Consider the full context of all files in the project:
|
||||||
|
${full_file_contents_context}
|
||||||
|
|
||||||
|
7. Return ONLY the complete modified code.
|
||||||
|
|
||||||
|
8. Do not include any explanations, comments, or line numbers in your response.
|
||||||
|
|
||||||
|
9. Ensure the returned code is complete and can be directly used as a replacement for the original code.
|
||||||
|
|
||||||
|
11. Preserve the original structure, indentation, and formatting of the code as much as possible.
|
||||||
|
|
||||||
|
12. Do not omit any parts of the code, even if they are unchanged.
|
||||||
|
|
||||||
|
13. Maintain the SAME indentation in the returned code as in the source code
|
||||||
|
|
||||||
|
14. Do NOT include three backticks: ```
|
||||||
|
|
||||||
|
15. Only return code part, do NOT return the context part!
|
||||||
|
|
||||||
Remember: Your response should contain nothing but ONLY the modified code, ready to be used as a direct replacement for the original file.
|
Remember: Your response should contain nothing but ONLY the modified code, ready to be used as a direct replacement for the original file.
|
||||||
]]
|
]]
|
||||||
@ -85,41 +111,48 @@ Remember: Your response should contain nothing but ONLY the modified code, ready
|
|||||||
local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
||||||
local active_job = nil
|
local active_job = nil
|
||||||
|
|
||||||
---@param question string
|
---@class StreamOptions
|
||||||
---@param code_lang string
|
---@field file_content string
|
||||||
---@param code_content string
|
---@field selected_code string | nil
|
||||||
---@param selected_content_content string | nil
|
---@field instructions string
|
||||||
---@param mode "planning" | "editing"
|
---@field project_context string | nil
|
||||||
---@param on_chunk AvanteChunkParser
|
---@field memory_context string | nil
|
||||||
---@param on_complete AvanteCompleteParser
|
---@field full_file_contents_context string | nil
|
||||||
M.stream = function(question, code_lang, code_content, selected_content_content, mode, on_chunk, on_complete)
|
---@field mode "planning" | "editing"
|
||||||
mode = mode or "planning"
|
---@field on_chunk AvanteChunkParser
|
||||||
|
---@field on_complete AvanteCompleteParser
|
||||||
|
|
||||||
|
---@param opts StreamOptions
|
||||||
|
M.stream = function(opts)
|
||||||
|
local mode = opts.mode or "planning"
|
||||||
local provider = Config.provider
|
local provider = Config.provider
|
||||||
|
|
||||||
|
local system_prompt_tpl = mode == "planning" and planning_mode_system_prompt_tpl or editing_mode_system_prompt_tpl
|
||||||
|
|
||||||
-- Check if the question contains an image path
|
-- Check if the question contains an image path
|
||||||
local image_path = nil
|
local image_paths = {}
|
||||||
local original_question = question
|
local original_instructions = opts.instructions
|
||||||
if question:match("image: ") then
|
if opts.instructions:match("image: ") then
|
||||||
local lines = vim.split(question, "\n")
|
local lines = vim.split(opts.instructions, "\n")
|
||||||
for i, line in ipairs(lines) do
|
for i, line in ipairs(lines) do
|
||||||
if line:match("^image: ") then
|
if line:match("^image: ") then
|
||||||
image_path = line:gsub("^image: ", "")
|
local image_path = line:gsub("^image: ", "")
|
||||||
|
table.insert(image_paths, image_path)
|
||||||
table.remove(lines, i)
|
table.remove(lines, i)
|
||||||
original_question = table.concat(lines, "\n")
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
original_instructions = table.concat(lines, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local system_prompt =
|
||||||
|
system_prompt_tpl:gsub("%${(.-)}", vim.tbl_deep_extend("force", opts, { instructions = original_instructions }))
|
||||||
|
|
||||||
---@type AvantePromptOptions
|
---@type AvantePromptOptions
|
||||||
local code_opts = {
|
local code_opts = {
|
||||||
base_prompt = mode == "planning" and planning_mode_prompt or editing_mode_prompt,
|
|
||||||
system_prompt = system_prompt,
|
system_prompt = system_prompt,
|
||||||
question = original_question,
|
user_prompt = opts.selected_code and "Please suggest modifications to the selected code."
|
||||||
image_path = image_path,
|
or "Please suggest modifications to the file coontent.",
|
||||||
code_lang = code_lang,
|
image_paths = image_paths,
|
||||||
code_content = code_content,
|
|
||||||
selected_code_content = selected_content_content,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
---@type string
|
---@type string
|
||||||
@ -129,7 +162,7 @@ M.stream = function(question, code_lang, code_content, selected_content_content,
|
|||||||
local Provider = P[provider]
|
local Provider = P[provider]
|
||||||
|
|
||||||
---@type AvanteHandlerOptions
|
---@type AvanteHandlerOptions
|
||||||
local handler_opts = { on_chunk = on_chunk, on_complete = on_complete }
|
local handler_opts = { on_chunk = opts.on_chunk, on_complete = opts.on_complete }
|
||||||
---@type AvanteCurlOutput
|
---@type AvanteCurlOutput
|
||||||
local spec = Provider.parse_curl_args(Provider, code_opts)
|
local spec = Provider.parse_curl_args(Provider, code_opts)
|
||||||
|
|
||||||
@ -163,7 +196,7 @@ M.stream = function(question, code_lang, code_content, selected_content_content,
|
|||||||
stream = function(err, data, _)
|
stream = function(err, data, _)
|
||||||
if err then
|
if err then
|
||||||
completed = true
|
completed = true
|
||||||
on_complete(err)
|
opts.on_complete(err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not data then
|
if not data then
|
||||||
@ -189,7 +222,7 @@ M.stream = function(question, code_lang, code_content, selected_content_content,
|
|||||||
end,
|
end,
|
||||||
on_error = function(err)
|
on_error = function(err)
|
||||||
completed = true
|
completed = true
|
||||||
on_complete(err)
|
opts.on_complete(err)
|
||||||
end,
|
end,
|
||||||
callback = function(result)
|
callback = function(result)
|
||||||
if result.status >= 400 then
|
if result.status >= 400 then
|
||||||
@ -201,7 +234,9 @@ M.stream = function(question, code_lang, code_content, selected_content_content,
|
|||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
if not completed then
|
if not completed then
|
||||||
completed = true
|
completed = true
|
||||||
on_complete("API request failed with status " .. result.status .. ". Body: " .. vim.inspect(result.body))
|
opts.on_complete(
|
||||||
|
"API request failed with status " .. result.status .. ". Body: " .. vim.inspect(result.body)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -7,54 +7,24 @@ local M = {}
|
|||||||
|
|
||||||
M.api_key_name = "ANTHROPIC_API_KEY"
|
M.api_key_name = "ANTHROPIC_API_KEY"
|
||||||
|
|
||||||
M.parse_message = function(opts)
|
---@param prompt_opts AvantePromptOptions
|
||||||
local code_prompt_obj = {
|
M.parse_message = function(prompt_opts)
|
||||||
type = "text",
|
local message_content = {}
|
||||||
text = string.format("<code>```%s\n%s```</code>", opts.code_lang, opts.code_content),
|
|
||||||
}
|
|
||||||
|
|
||||||
if Utils.tokens.calculate_tokens(code_prompt_obj.text) > 1024 then
|
if Clipboard.support_paste_image() and prompt_opts.image_paths then
|
||||||
code_prompt_obj.cache_control = { type = "ephemeral" }
|
for _, image_path in ipairs(prompt_opts.image_paths) do
|
||||||
end
|
|
||||||
|
|
||||||
if opts.selected_code_content then
|
|
||||||
code_prompt_obj.text = string.format("<code_context>```%s\n%s```</code_context>", opts.code_lang, opts.code_content)
|
|
||||||
end
|
|
||||||
|
|
||||||
local message_content = {
|
|
||||||
code_prompt_obj,
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.selected_code_content then
|
|
||||||
local selected_code_obj = {
|
|
||||||
type = "text",
|
|
||||||
text = string.format("<code>```%s\n%s```</code>", opts.code_lang, opts.selected_code_content),
|
|
||||||
}
|
|
||||||
|
|
||||||
if Utils.tokens.calculate_tokens(selected_code_obj.text) > 1024 then
|
|
||||||
selected_code_obj.cache_control = { type = "ephemeral" }
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(message_content, selected_code_obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
if Clipboard.support_paste_image() and opts.image_path then
|
|
||||||
table.insert(message_content, {
|
table.insert(message_content, {
|
||||||
type = "image",
|
type = "image",
|
||||||
source = {
|
source = {
|
||||||
type = "base64",
|
type = "base64",
|
||||||
media_type = "image/png",
|
media_type = "image/png",
|
||||||
data = Clipboard.get_base64_content(opts.image_path),
|
data = Clipboard.get_base64_content(image_path),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
table.insert(message_content, {
|
local user_prompt = prompt_opts.user_prompt
|
||||||
type = "text",
|
|
||||||
text = string.format("<question>%s</question>", opts.question),
|
|
||||||
})
|
|
||||||
|
|
||||||
local user_prompt = opts.base_prompt
|
|
||||||
|
|
||||||
local user_prompt_obj = {
|
local user_prompt_obj = {
|
||||||
type = "text",
|
type = "text",
|
||||||
@ -91,9 +61,9 @@ M.parse_response = function(data_stream, event_state, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param provider AvanteProviderFunctor
|
---@param provider AvanteProviderFunctor
|
||||||
---@param code_opts AvantePromptOptions
|
---@param prompt_opts AvantePromptOptions
|
||||||
---@return table
|
---@return table
|
||||||
M.parse_curl_args = function(provider, code_opts)
|
M.parse_curl_args = function(provider, prompt_opts)
|
||||||
local base, body_opts = P.parse_config(provider)
|
local base, body_opts = P.parse_config(provider)
|
||||||
|
|
||||||
local headers = {
|
local headers = {
|
||||||
@ -112,7 +82,14 @@ M.parse_curl_args = function(provider, code_opts)
|
|||||||
headers = headers,
|
headers = headers,
|
||||||
body = vim.tbl_deep_extend("force", {
|
body = vim.tbl_deep_extend("force", {
|
||||||
model = base.model,
|
model = base.model,
|
||||||
messages = M.parse_message(code_opts),
|
system = {
|
||||||
|
{
|
||||||
|
type = "text",
|
||||||
|
text = prompt_opts.system_prompt,
|
||||||
|
cache_control = { type = "ephemeral" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messages = M.parse_message(prompt_opts),
|
||||||
stream = true,
|
stream = true,
|
||||||
}, body_opts),
|
}, body_opts),
|
||||||
}
|
}
|
||||||
|
@ -31,37 +31,9 @@ local M = {}
|
|||||||
M.api_key_name = "CO_API_KEY"
|
M.api_key_name = "CO_API_KEY"
|
||||||
|
|
||||||
M.parse_message = function(opts)
|
M.parse_message = function(opts)
|
||||||
local user_prompt = opts.base_prompt
|
|
||||||
.. "\n\nCODE:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nQUESTION:\n"
|
|
||||||
.. opts.question
|
|
||||||
|
|
||||||
if opts.selected_code_content ~= nil then
|
|
||||||
user_prompt = opts.base_prompt
|
|
||||||
.. "\n\nCODE CONTEXT:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nCODE:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.selected_code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nQUESTION:\n"
|
|
||||||
.. opts.question
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
preamble = opts.system_prompt,
|
preamble = opts.system_prompt,
|
||||||
message = user_prompt,
|
message = opts.user_prompt,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -8,41 +8,24 @@ local M = {}
|
|||||||
M.api_key_name = "GEMINI_API_KEY"
|
M.api_key_name = "GEMINI_API_KEY"
|
||||||
|
|
||||||
M.parse_message = function(opts)
|
M.parse_message = function(opts)
|
||||||
local code_prompt_obj = {
|
local message_content = {}
|
||||||
text = string.format("<code>```%s\n%s```</code>", opts.code_lang, opts.code_content),
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.selected_code_content then
|
if Clipboard.support_paste_image() and opts.image_paths then
|
||||||
code_prompt_obj.text = string.format("<code_context>```%s\n%s```</code_context>", opts.code_lang, opts.code_content)
|
for _, image_path in ipairs(opts.image_paths) do
|
||||||
end
|
|
||||||
|
|
||||||
-- parts ready
|
|
||||||
local message_content = {
|
|
||||||
code_prompt_obj,
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.selected_code_content then
|
|
||||||
local selected_code_obj = {
|
|
||||||
text = string.format("<code>```%s\n%s```</code>", opts.code_lang, opts.selected_code_content),
|
|
||||||
}
|
|
||||||
|
|
||||||
table.insert(message_content, selected_code_obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
if Clipboard.support_paste_image() and opts.image_path then
|
|
||||||
local image_data = {
|
local image_data = {
|
||||||
inline_data = {
|
inline_data = {
|
||||||
mime_type = "image/png",
|
mime_type = "image/png",
|
||||||
data = Clipboard.get_base64_content(opts.image_path),
|
data = Clipboard.get_base64_content(image_path),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
table.insert(message_content, image_data)
|
table.insert(message_content, image_data)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- insert a part into parts
|
-- insert a part into parts
|
||||||
table.insert(message_content, {
|
table.insert(message_content, {
|
||||||
text = string.format("<question>%s</question>", opts.question),
|
text = opts.user_prompt,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -50,7 +33,7 @@ M.parse_message = function(opts)
|
|||||||
role = "user",
|
role = "user",
|
||||||
parts = {
|
parts = {
|
||||||
{
|
{
|
||||||
text = opts.system_prompt .. "\n" .. opts.base_prompt,
|
text = opts.system_prompt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -9,13 +9,9 @@ local Dressing = require("avante.ui.dressing")
|
|||||||
---@field on_complete AvanteCompleteParser
|
---@field on_complete AvanteCompleteParser
|
||||||
---
|
---
|
||||||
---@class AvantePromptOptions: table<[string], string>
|
---@class AvantePromptOptions: table<[string], string>
|
||||||
---@field base_prompt AvanteBasePrompt
|
---@field system_prompt string
|
||||||
---@field system_prompt AvanteSystemPrompt
|
---@field user_prompt string
|
||||||
---@field question string
|
---@field image_paths? string[]
|
||||||
---@field image_path? string
|
|
||||||
---@field code_lang string
|
|
||||||
---@field code_content string
|
|
||||||
---@field selected_code_content? string
|
|
||||||
---
|
---
|
||||||
---@class AvanteBaseMessage
|
---@class AvanteBaseMessage
|
||||||
---@field role "user" | "system"
|
---@field role "user" | "system"
|
||||||
@ -34,7 +30,7 @@ local Dressing = require("avante.ui.dressing")
|
|||||||
---@alias AvanteMessageParser fun(opts: AvantePromptOptions): AvanteChatMessage[]
|
---@alias AvanteMessageParser fun(opts: AvantePromptOptions): AvanteChatMessage[]
|
||||||
---
|
---
|
||||||
---@class AvanteCurlOutput: {url: string, proxy: string, insecure: boolean, body: table<string, any> | string, headers: table<string, string>}
|
---@class AvanteCurlOutput: {url: string, proxy: string, insecure: boolean, body: table<string, any> | string, headers: table<string, string>}
|
||||||
---@alias AvanteCurlArgsParser fun(opts: AvanteProvider, code_opts: AvantePromptOptions): AvanteCurlOutput
|
---@alias AvanteCurlArgsParser fun(opts: AvanteProvider | AvanteProviderFunctor, code_opts: AvantePromptOptions): AvanteCurlOutput
|
||||||
---
|
---
|
||||||
---@class ResponseParser
|
---@class ResponseParser
|
||||||
---@field on_chunk fun(chunk: string): any
|
---@field on_chunk fun(chunk: string): any
|
||||||
@ -337,7 +333,7 @@ M.commands = function()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param opts AvanteProvider | AvanteSupportedProvider
|
---@param opts AvanteProvider | AvanteSupportedProvider | AvanteProviderFunctor
|
||||||
---@return AvanteDefaultBaseProvider, table<string, any>
|
---@return AvanteDefaultBaseProvider, table<string, any>
|
||||||
M.parse_config = function(opts)
|
M.parse_config = function(opts)
|
||||||
---@type AvanteDefaultBaseProvider
|
---@type AvanteDefaultBaseProvider
|
||||||
|
@ -27,53 +27,22 @@ local M = {}
|
|||||||
|
|
||||||
M.api_key_name = "OPENAI_API_KEY"
|
M.api_key_name = "OPENAI_API_KEY"
|
||||||
|
|
||||||
---@param opts AvantePromptOptions
|
|
||||||
M.get_user_message = function(opts)
|
|
||||||
local user_prompt = opts.base_prompt
|
|
||||||
.. "\n\nCODE:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nQUESTION:\n"
|
|
||||||
.. opts.question
|
|
||||||
|
|
||||||
if opts.selected_code_content ~= nil then
|
|
||||||
user_prompt = opts.base_prompt
|
|
||||||
.. "\n\nCODE CONTEXT:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nCODE:\n"
|
|
||||||
.. "```"
|
|
||||||
.. opts.code_lang
|
|
||||||
.. "\n"
|
|
||||||
.. opts.selected_code_content
|
|
||||||
.. "\n```"
|
|
||||||
.. "\n\nQUESTION:\n"
|
|
||||||
.. opts.question
|
|
||||||
end
|
|
||||||
|
|
||||||
return user_prompt
|
|
||||||
end
|
|
||||||
|
|
||||||
M.parse_message = function(opts)
|
M.parse_message = function(opts)
|
||||||
---@type string | OpenAIMessage[]
|
---@type string | OpenAIMessage[]
|
||||||
local user_content
|
local user_content
|
||||||
if Config.behaviour.support_paste_from_clipboard and opts.image_path then
|
if Config.behaviour.support_paste_from_clipboard and opts.image_paths then
|
||||||
user_content = {}
|
user_content = {}
|
||||||
|
for _, image_path in ipairs(opts.image_paths) do
|
||||||
table.insert(user_content, {
|
table.insert(user_content, {
|
||||||
type = "image_url",
|
type = "image_url",
|
||||||
image_url = {
|
image_url = {
|
||||||
url = "data:image/png;base64," .. Clipboard.get_base64_content(opts.image_path),
|
url = "data:image/png;base64," .. Clipboard.get_base64_content(image_path),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
table.insert(user_content, { type = "text", text = M.get_user_message(opts) })
|
end
|
||||||
|
table.insert(user_content, { type = "text", text = opts.user_prompt })
|
||||||
else
|
else
|
||||||
user_content = M.get_user_message(opts)
|
user_content = opts.user_prompt
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -277,7 +277,6 @@ function Selection:create_editing_input()
|
|||||||
local code_wind = api.nvim_get_current_win()
|
local code_wind = api.nvim_get_current_win()
|
||||||
self.cursor_pos = api.nvim_win_get_cursor(code_wind)
|
self.cursor_pos = api.nvim_win_get_cursor(code_wind)
|
||||||
self.code_winid = code_wind
|
self.code_winid = code_wind
|
||||||
local filetype = api.nvim_get_option_value("filetype", { buf = code_bufnr })
|
|
||||||
local code_lines = api.nvim_buf_get_lines(code_bufnr, 0, -1, false)
|
local code_lines = api.nvim_buf_get_lines(code_bufnr, 0, -1, false)
|
||||||
local code_content = table.concat(code_lines, "\n")
|
local code_content = table.concat(code_lines, "\n")
|
||||||
|
|
||||||
@ -408,7 +407,14 @@ function Selection:create_editing_input()
|
|||||||
end, 0)
|
end, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
Llm.stream(input, filetype, code_content, self.selection.content, "editing", on_chunk, on_complete)
|
Llm.stream({
|
||||||
|
file_content = code_content,
|
||||||
|
selected_code = self.selection.content,
|
||||||
|
instructions = input,
|
||||||
|
mode = "editing",
|
||||||
|
on_chunk = on_chunk,
|
||||||
|
on_complete = on_complete,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.keymap.set("i", Config.mappings.submit.insert, submit_input, { buffer = bufnr, noremap = true, silent = true })
|
vim.keymap.set("i", Config.mappings.submit.insert, submit_input, { buffer = bufnr, noremap = true, silent = true })
|
||||||
|
@ -134,53 +134,130 @@ function Sidebar:toggle()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function realign_line_numbers(code_lines, snippet)
|
---@class AvanteReplacementResult
|
||||||
local snippet_lines = vim.split(snippet.content, "\n")
|
---@field content string
|
||||||
local snippet_lines_count = #snippet_lines
|
---@field is_searching boolean
|
||||||
|
---@field is_replacing boolean
|
||||||
|
---@field last_search_tag_start_line integer
|
||||||
|
---@field last_replace_tag_start_line integer
|
||||||
|
|
||||||
local start_line = snippet.range[1]
|
---@param original_content string
|
||||||
|
---@param result_content string
|
||||||
|
---@param code_lang string
|
||||||
|
---@return AvanteReplacementResult
|
||||||
|
local function transform_result_content(original_content, result_content, code_lang)
|
||||||
|
local transformed = ""
|
||||||
|
local original_lines = vim.split(original_content, "\n")
|
||||||
|
local result_lines = vim.split(result_content, "\n")
|
||||||
|
|
||||||
local correct_start
|
local is_searching = false
|
||||||
for i = start_line, math.max(1, start_line - snippet_lines_count + 1), -1 do
|
local is_replacing = false
|
||||||
local matched = true
|
local last_search_tag_start_line = 0
|
||||||
for j = 1, math.min(snippet_lines_count, start_line - i + 1) do
|
local last_replace_tag_start_line = 0
|
||||||
if code_lines[i + j - 1] ~= snippet_lines[j] then
|
|
||||||
matched = false
|
local trim_breakline_suffix = false
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
while i <= #result_lines do
|
||||||
|
if result_lines[i] == "<SEARCH>" then
|
||||||
|
is_searching = true
|
||||||
|
last_search_tag_start_line = i
|
||||||
|
local search_start = i + 1
|
||||||
|
local search_end = search_start
|
||||||
|
while search_end <= #result_lines and result_lines[search_end] ~= "</SEARCH>" do
|
||||||
|
search_end = search_end + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if search_end > #result_lines then
|
||||||
|
trim_breakline_suffix = false
|
||||||
|
-- <SEARCH> tag is not closed, add remaining content and return
|
||||||
|
transformed = transformed .. table.concat(result_lines, "\n", i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local replace_start = search_end + 2 -- Skip </SEARCH> and <REPLACE>
|
||||||
|
if replace_start > #result_lines or result_lines[replace_start - 1] ~= "<REPLACE>" then
|
||||||
|
trim_breakline_suffix = false
|
||||||
|
-- <REPLACE> tag is missing, add remaining content and return
|
||||||
|
transformed = transformed .. table.concat(result_lines, "\n", i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
is_replacing = true
|
||||||
|
last_replace_tag_start_line = replace_start - 1
|
||||||
|
local replace_end = replace_start
|
||||||
|
while replace_end <= #result_lines and result_lines[replace_end] ~= "</REPLACE>" do
|
||||||
|
replace_end = replace_end + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if replace_end > #result_lines then
|
||||||
|
trim_breakline_suffix = false
|
||||||
|
-- </REPLACE> tag is missing, add remaining content and return
|
||||||
|
transformed = transformed .. table.concat(result_lines, "\n", i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find the corresponding lines in the original content
|
||||||
|
local start_line, end_line
|
||||||
|
for j = 1, #original_lines - (search_end - search_start) + 1 do
|
||||||
|
local match = true
|
||||||
|
for k = 0, search_end - search_start - 1 do
|
||||||
|
if
|
||||||
|
Utils.remove_indentation(original_lines[j + k]) ~= Utils.remove_indentation(result_lines[search_start + k])
|
||||||
|
then
|
||||||
|
match = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if matched then
|
if match then
|
||||||
correct_start = i
|
start_line = j
|
||||||
|
end_line = j + (search_end - search_start) - 1
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local end_line = snippet.range[2]
|
if start_line and end_line then
|
||||||
|
transformed = transformed .. string.format("Replace lines: %d-%d\n```%s\n", start_line, end_line, code_lang)
|
||||||
|
for j = replace_start, replace_end - 1 do
|
||||||
|
transformed = transformed .. result_lines[j] .. "\n"
|
||||||
|
end
|
||||||
|
transformed = transformed .. "```\n"
|
||||||
|
end
|
||||||
|
|
||||||
local correct_end
|
i = replace_end + 1 -- Move to the line after </REPLACE>
|
||||||
for i = snippet_lines_count - 1, 1, -1 do
|
is_searching = false
|
||||||
local matched = true
|
is_replacing = false
|
||||||
for j = 1, i do
|
else
|
||||||
if code_lines[end_line + j - 1] ~= snippet_lines[snippet_lines_count - j] then
|
trim_breakline_suffix = true
|
||||||
matched = false
|
transformed = transformed .. result_lines[i] .. "\n"
|
||||||
break
|
i = i + 1
|
||||||
end
|
|
||||||
end
|
|
||||||
if matched then
|
|
||||||
correct_end = end_line + i
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if correct_start then
|
return {
|
||||||
snippet.range[1] = correct_start
|
content = trim_breakline_suffix and transformed:sub(1, -2) or transformed, -- Remove trailing newline
|
||||||
end
|
is_searching = is_searching,
|
||||||
|
is_replacing = is_replacing,
|
||||||
|
last_search_tag_start_line = last_search_tag_start_line,
|
||||||
|
last_replace_tag_start_line = last_replace_tag_start_line,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
if correct_end then
|
local searching_hint = "\n 🔍 Searching..."
|
||||||
snippet.range[2] = correct_end
|
|
||||||
end
|
|
||||||
|
|
||||||
return snippet
|
---@param replacement AvanteReplacementResult
|
||||||
|
---@return string
|
||||||
|
local function generate_display_content(replacement)
|
||||||
|
if replacement.is_searching then
|
||||||
|
return table.concat(
|
||||||
|
vim.list_slice(vim.split(replacement.content, "\n"), 1, replacement.last_search_tag_start_line - 1),
|
||||||
|
"\n"
|
||||||
|
) .. searching_hint
|
||||||
|
end
|
||||||
|
if replacement.is_replacing then
|
||||||
|
return replacement.content .. "\n```"
|
||||||
|
end
|
||||||
|
return replacement.content
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class AvanteCodeSnippet
|
---@class AvanteCodeSnippet
|
||||||
@ -191,11 +268,9 @@ end
|
|||||||
---@field start_line_in_response_buf integer
|
---@field start_line_in_response_buf integer
|
||||||
---@field end_line_in_response_buf integer
|
---@field end_line_in_response_buf integer
|
||||||
|
|
||||||
---@param code_content string
|
|
||||||
---@param response_content string
|
---@param response_content string
|
||||||
---@return AvanteCodeSnippet[]
|
---@return AvanteCodeSnippet[]
|
||||||
local function extract_code_snippets(code_content, response_content)
|
local function extract_code_snippets(response_content)
|
||||||
local code_lines = vim.split(code_content, "\n")
|
|
||||||
local snippets = {}
|
local snippets = {}
|
||||||
local current_snippet = {}
|
local current_snippet = {}
|
||||||
local in_code_block = false
|
local in_code_block = false
|
||||||
@ -219,7 +294,6 @@ local function extract_code_snippets(code_content, response_content)
|
|||||||
start_line_in_response_buf = start_line_in_response_buf,
|
start_line_in_response_buf = start_line_in_response_buf,
|
||||||
end_line_in_response_buf = idx,
|
end_line_in_response_buf = idx,
|
||||||
}
|
}
|
||||||
snippet = realign_line_numbers(code_lines, snippet)
|
|
||||||
table.insert(snippets, snippet)
|
table.insert(snippets, snippet)
|
||||||
end
|
end
|
||||||
current_snippet = {}
|
current_snippet = {}
|
||||||
@ -346,7 +420,7 @@ end
|
|||||||
function Sidebar:apply(current_cursor)
|
function Sidebar:apply(current_cursor)
|
||||||
local content = table.concat(Utils.get_buf_lines(0, -1, self.code.bufnr), "\n")
|
local content = table.concat(Utils.get_buf_lines(0, -1, self.code.bufnr), "\n")
|
||||||
local response, response_start_line = self:get_content_between_separators()
|
local response, response_start_line = self:get_content_between_separators()
|
||||||
local snippets = extract_code_snippets(content, response)
|
local snippets = extract_code_snippets(response)
|
||||||
if current_cursor then
|
if current_cursor then
|
||||||
if self.result and self.result.winid then
|
if self.result and self.result.winid then
|
||||||
local cursor_line = Utils.get_cursor_pos(self.result.winid)
|
local cursor_line = Utils.get_cursor_pos(self.result.winid)
|
||||||
@ -876,17 +950,6 @@ function Sidebar:update_content(content, opts)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
local function prepend_line_number(content, start_line)
|
|
||||||
start_line = start_line or 1
|
|
||||||
local lines = vim.split(content, "\n")
|
|
||||||
local result = {}
|
|
||||||
for i, line in ipairs(lines) do
|
|
||||||
i = i + start_line - 1
|
|
||||||
table.insert(result, "L" .. i .. ": " .. line)
|
|
||||||
end
|
|
||||||
return table.concat(result, "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Function to get current timestamp
|
-- Function to get current timestamp
|
||||||
local function get_timestamp()
|
local function get_timestamp()
|
||||||
return os.date("%Y-%m-%d %H:%M:%S")
|
return os.date("%Y-%m-%d %H:%M:%S")
|
||||||
@ -1079,12 +1142,11 @@ function Sidebar:create_input()
|
|||||||
self:update_content(content_prefix .. "🔄 **Generating response ...**\n")
|
self:update_content(content_prefix .. "🔄 **Generating response ...**\n")
|
||||||
|
|
||||||
local content = table.concat(Utils.get_buf_lines(0, -1, self.code.bufnr), "\n")
|
local content = table.concat(Utils.get_buf_lines(0, -1, self.code.bufnr), "\n")
|
||||||
local content_with_line_numbers = prepend_line_number(content)
|
local filetype = api.nvim_get_option_value("filetype", { buf = self.code.bufnr })
|
||||||
|
|
||||||
local selected_code_content_with_line_numbers = nil
|
local selected_code_content = nil
|
||||||
if self.code.selection ~= nil then
|
if self.code.selection ~= nil then
|
||||||
selected_code_content_with_line_numbers =
|
selected_code_content = self.code.selection.content
|
||||||
prepend_line_number(self.code.selection.content, self.code.selection.range.start.line)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if request:sub(1, 1) == "/" then
|
if request:sub(1, 1) == "/" then
|
||||||
@ -1113,10 +1175,8 @@ function Sidebar:create_input()
|
|||||||
Utils.error("Invalid end line number", { once = true, title = "Avante" })
|
Utils.error("Invalid end line number", { once = true, title = "Avante" })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
selected_code_content_with_line_numbers = prepend_line_number(
|
selected_code_content =
|
||||||
table.concat(api.nvim_buf_get_lines(self.code.bufnr, start_line - 1, end_line, false), "\n"),
|
table.concat(api.nvim_buf_get_lines(self.code.bufnr, start_line - 1, end_line, false), "\n")
|
||||||
start_line
|
|
||||||
)
|
|
||||||
request = question
|
request = question
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
@ -1129,25 +1189,31 @@ function Sidebar:create_input()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local full_response = ""
|
local original_response = ""
|
||||||
|
local transformed_response = ""
|
||||||
local filetype = api.nvim_get_option_value("filetype", { buf = self.code.bufnr })
|
local displayed_response = ""
|
||||||
|
|
||||||
local is_first_chunk = true
|
local is_first_chunk = true
|
||||||
|
|
||||||
---@type AvanteChunkParser
|
---@type AvanteChunkParser
|
||||||
local on_chunk = function(chunk)
|
local on_chunk = function(chunk)
|
||||||
full_response = full_response .. chunk
|
original_response = original_response .. chunk
|
||||||
|
local transformed = transform_result_content(content, transformed_response .. chunk, filetype)
|
||||||
|
transformed_response = transformed.content
|
||||||
|
local cur_displayed_response = generate_display_content(transformed)
|
||||||
if is_first_chunk then
|
if is_first_chunk then
|
||||||
is_first_chunk = false
|
is_first_chunk = false
|
||||||
self:update_content(content_prefix .. chunk, { stream = false, scroll = true })
|
self:update_content(content_prefix .. chunk, { stream = false, scroll = true })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self:update_content(chunk, { stream = true, scroll = true })
|
if cur_displayed_response ~= displayed_response then
|
||||||
|
displayed_response = cur_displayed_response
|
||||||
|
self:update_content(content_prefix .. displayed_response, { stream = false, scroll = true })
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
vim.cmd("redraw")
|
vim.cmd("redraw")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---@type AvanteCompleteParser
|
---@type AvanteCompleteParser
|
||||||
local on_complete = function(err)
|
local on_complete = function(err)
|
||||||
@ -1157,13 +1223,18 @@ function Sidebar:create_input()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Execute when the stream request is actually completed
|
-- Execute when the stream request is actually completed
|
||||||
self:update_content("\n\n🎉🎉🎉 **Generation complete!** Please review the code suggestions above.", {
|
self:update_content(
|
||||||
stream = true,
|
content_prefix
|
||||||
|
.. displayed_response
|
||||||
|
.. "\n\n🎉🎉🎉 **Generation complete!** Please review the code suggestions above.",
|
||||||
|
{
|
||||||
|
stream = false,
|
||||||
scroll = true,
|
scroll = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
api.nvim_exec_autocmds("User", { pattern = VIEW_BUFFER_UPDATED_PATTERN })
|
api.nvim_exec_autocmds("User", { pattern = VIEW_BUFFER_UPDATED_PATTERN })
|
||||||
end,
|
end,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
vim.defer_fn(function()
|
vim.defer_fn(function()
|
||||||
if self.result and self.result.winid and api.nvim_win_is_valid(self.result.winid) then
|
if self.result and self.result.winid and api.nvim_win_is_valid(self.result.winid) then
|
||||||
@ -1177,20 +1248,20 @@ function Sidebar:create_input()
|
|||||||
provider = Config.provider,
|
provider = Config.provider,
|
||||||
model = model,
|
model = model,
|
||||||
request = request,
|
request = request,
|
||||||
response = full_response,
|
response = displayed_response,
|
||||||
|
original_response = original_response,
|
||||||
})
|
})
|
||||||
History.save(self.code.bufnr, chat_history)
|
History.save(self.code.bufnr, chat_history)
|
||||||
end
|
end
|
||||||
|
|
||||||
Llm.stream(
|
Llm.stream({
|
||||||
request,
|
file_content = content,
|
||||||
filetype,
|
selected_code = selected_code_content,
|
||||||
content_with_line_numbers,
|
instructions = request,
|
||||||
selected_code_content_with_line_numbers,
|
mode = "planning",
|
||||||
"planning",
|
on_chunk = on_chunk,
|
||||||
on_chunk,
|
on_complete = on_complete,
|
||||||
on_complete
|
})
|
||||||
)
|
|
||||||
|
|
||||||
if Config.behaviour.auto_apply_diff_after_generation then
|
if Config.behaviour.auto_apply_diff_after_generation then
|
||||||
self:apply(false)
|
self:apply(false)
|
||||||
|
@ -479,4 +479,9 @@ function M.get_indentation(code)
|
|||||||
return code:match("^%s*") or ""
|
return code:match("^%s*") or ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- remove indentation from code: spaces or tabs
|
||||||
|
function M.remove_indentation(code)
|
||||||
|
return code:gsub("^%s*", "")
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
Loading…
x
Reference in New Issue
Block a user