fix: AvanteEdit command (#1799)
This commit is contained in:
@@ -156,13 +156,15 @@ function M.ask(opts)
|
|||||||
return ask()
|
return ask()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param question? string
|
---@param request? string
|
||||||
function M.edit(question)
|
---@param line1? integer
|
||||||
|
---@param line2? integer
|
||||||
|
function M.edit(request, line1, line2)
|
||||||
local _, selection = require("avante").get()
|
local _, selection = require("avante").get()
|
||||||
if not selection then return end
|
if not selection then return end
|
||||||
selection:create_editing_input()
|
selection:create_editing_input(request, line1, line2)
|
||||||
if question ~= nil or question ~= "" then
|
if request ~= nil and request ~= "" then
|
||||||
vim.api.nvim_exec_autocmds("User", { pattern = "AvanteEditSubmitted", data = { request = question } })
|
vim.api.nvim_exec_autocmds("User", { pattern = "AvanteEditSubmitted", data = { request = request } })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ M._defaults = {
|
|||||||
height = 8, -- Height of the input window in vertical layout
|
height = 8, -- Height of the input window in vertical layout
|
||||||
},
|
},
|
||||||
edit = {
|
edit = {
|
||||||
border = "rounded",
|
border = { " ", " ", " ", " ", " ", " ", " ", " " },
|
||||||
start_insert = true, -- Start insert mode when opening the edit window
|
start_insert = true, -- Start insert mode when opening the edit window
|
||||||
},
|
},
|
||||||
ask = {
|
ask = {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ local Llm = require("avante.llm")
|
|||||||
local Provider = require("avante.providers")
|
local Provider = require("avante.providers")
|
||||||
local RepoMap = require("avante.repo_map")
|
local RepoMap = require("avante.repo_map")
|
||||||
local PromptInput = require("avante.ui.prompt_input")
|
local PromptInput = require("avante.ui.prompt_input")
|
||||||
|
local SelectionResult = require("avante.selection_result")
|
||||||
|
local Range = require("avante.range")
|
||||||
|
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
local fn = vim.fn
|
local fn = vim.fn
|
||||||
@@ -19,6 +21,7 @@ local PRIORITY = vim.highlight.priorities.user
|
|||||||
---@field selected_code_extmark_id integer | nil
|
---@field selected_code_extmark_id integer | nil
|
||||||
---@field augroup integer | nil
|
---@field augroup integer | nil
|
||||||
---@field code_winid integer | nil
|
---@field code_winid integer | nil
|
||||||
|
---@field code_bufnr integer | nil
|
||||||
---@field prompt_input avante.ui.PromptInput | nil
|
---@field prompt_input avante.ui.PromptInput | nil
|
||||||
local Selection = {}
|
local Selection = {}
|
||||||
Selection.__index = Selection
|
Selection.__index = Selection
|
||||||
@@ -34,6 +37,7 @@ function Selection:new(id)
|
|||||||
selection = nil,
|
selection = nil,
|
||||||
cursor_pos = nil,
|
cursor_pos = nil,
|
||||||
code_winid = nil,
|
code_winid = nil,
|
||||||
|
code_bufnr = nil,
|
||||||
prompt_input = nil,
|
prompt_input = nil,
|
||||||
}, Selection)
|
}, Selection)
|
||||||
end
|
end
|
||||||
@@ -88,7 +92,7 @@ function Selection:close_editing_input()
|
|||||||
self.selected_code_extmark_id = nil
|
self.selected_code_extmark_id = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.cursor_pos and self.code_winid then
|
if self.cursor_pos and self.code_winid and api.nvim_win_is_valid(self.code_winid) then
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
local bufnr = api.nvim_win_get_buf(self.code_winid)
|
local bufnr = api.nvim_win_get_buf(self.code_winid)
|
||||||
local line_count = api.nvim_buf_line_count(bufnr)
|
local line_count = api.nvim_buf_line_count(bufnr)
|
||||||
@@ -100,7 +104,124 @@ function Selection:close_editing_input()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Selection:create_editing_input()
|
function Selection:submit_input(input)
|
||||||
|
if not input then
|
||||||
|
Utils.error("No input provided", { once = true, title = "Avante" })
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self.prompt_input and self.prompt_input.spinner_active then
|
||||||
|
Utils.error(
|
||||||
|
"Please wait for the previous request to finish before submitting another",
|
||||||
|
{ once = true, title = "Avante" }
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local code_lines = api.nvim_buf_get_lines(self.code_bufnr, 0, -1, false)
|
||||||
|
local code_content = table.concat(code_lines, "\n")
|
||||||
|
|
||||||
|
local full_response = ""
|
||||||
|
local start_line = self.selection.range.start.lnum
|
||||||
|
local finish_line = self.selection.range.finish.lnum
|
||||||
|
|
||||||
|
local original_first_line_indentation = Utils.get_indentation(code_lines[self.selection.range.start.lnum])
|
||||||
|
|
||||||
|
local need_prepend_indentation = false
|
||||||
|
|
||||||
|
if self.prompt_input then self.prompt_input:start_spinner() end
|
||||||
|
|
||||||
|
---@type AvanteLLMStartCallback
|
||||||
|
local function on_start(_) end
|
||||||
|
|
||||||
|
---@type AvanteLLMChunkCallback
|
||||||
|
local function on_chunk(chunk)
|
||||||
|
full_response = full_response .. chunk
|
||||||
|
local response_lines_ = vim.split(full_response, "\n")
|
||||||
|
local response_lines = {}
|
||||||
|
local in_code_block = false
|
||||||
|
for _, line in ipairs(response_lines_) do
|
||||||
|
if line:match("^<code>") then
|
||||||
|
in_code_block = true
|
||||||
|
line = line:gsub("^<code>", "")
|
||||||
|
if line ~= "" then table.insert(response_lines, line) end
|
||||||
|
elseif line:match("</code>") then
|
||||||
|
in_code_block = false
|
||||||
|
line = line:gsub("</code>.*$", "")
|
||||||
|
if line ~= "" then table.insert(response_lines, line) end
|
||||||
|
elseif in_code_block then
|
||||||
|
table.insert(response_lines, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #response_lines == 1 then
|
||||||
|
local first_line = response_lines[1]
|
||||||
|
local first_line_indentation = Utils.get_indentation(first_line)
|
||||||
|
need_prepend_indentation = first_line_indentation ~= original_first_line_indentation
|
||||||
|
end
|
||||||
|
if need_prepend_indentation then
|
||||||
|
for i, line in ipairs(response_lines) do
|
||||||
|
response_lines[i] = original_first_line_indentation .. line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
api.nvim_buf_set_lines(self.code_bufnr, start_line - 1, finish_line, true, response_lines)
|
||||||
|
finish_line = start_line + #response_lines - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
---@type AvanteLLMStopCallback
|
||||||
|
local function on_stop(stop_opts)
|
||||||
|
if stop_opts.error then
|
||||||
|
-- NOTE: in Ubuntu 22.04+ you will see this ignorable error from ~/.local/share/nvim/lazy/avante.nvim/lua/avante/llm.lua `on_error = function(err)`, check to avoid showing this error.
|
||||||
|
if type(stop_opts.error) == "table" and stop_opts.error.exit == nil and stop_opts.error.stderr == "{}" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
Utils.error(
|
||||||
|
"Error occurred while processing the response: " .. vim.inspect(stop_opts.error),
|
||||||
|
{ once = true, title = "Avante" }
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self.prompt_input then self.prompt_input:stop_spinner() end
|
||||||
|
vim.defer_fn(function() self:close_editing_input() end, 0)
|
||||||
|
Utils.debug("full response:", full_response)
|
||||||
|
end
|
||||||
|
|
||||||
|
local filetype = api.nvim_get_option_value("filetype", { buf = self.code_bufnr })
|
||||||
|
local file_ext = api.nvim_buf_get_name(self.code_bufnr):match("^.+%.(.+)$")
|
||||||
|
|
||||||
|
local mentions = Utils.extract_mentions(input)
|
||||||
|
input = mentions.new_content
|
||||||
|
local project_context = mentions.enable_project_context and RepoMap.get_repo_map(file_ext) or nil
|
||||||
|
|
||||||
|
local diagnostics = Utils.get_current_selection_diagnostics(self.code_bufnr, self.selection)
|
||||||
|
|
||||||
|
---@type AvanteSelectedCode | nil
|
||||||
|
local selected_code = nil
|
||||||
|
|
||||||
|
if self.selection then
|
||||||
|
selected_code = {
|
||||||
|
content = self.selection.content,
|
||||||
|
file_type = self.selection.filetype,
|
||||||
|
path = self.selection.filepath,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
Llm.stream({
|
||||||
|
ask = true,
|
||||||
|
project_context = vim.json.encode(project_context),
|
||||||
|
diagnostics = vim.json.encode(diagnostics),
|
||||||
|
selected_files = { { content = code_content, file_type = filetype, path = "" } },
|
||||||
|
code_lang = filetype,
|
||||||
|
selected_code = selected_code,
|
||||||
|
instructions = input,
|
||||||
|
mode = "editing",
|
||||||
|
on_start = on_start,
|
||||||
|
on_chunk = on_chunk,
|
||||||
|
on_stop = on_stop,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param request? string
|
||||||
|
---@param line1? integer
|
||||||
|
---@param line2? integer
|
||||||
|
function Selection:create_editing_input(request, line1, line2)
|
||||||
self:close_editing_input()
|
self:close_editing_input()
|
||||||
|
|
||||||
if not vim.g.avante_login or vim.g.avante_login == false then
|
if not vim.g.avante_login or vim.g.avante_login == false then
|
||||||
@@ -108,14 +229,24 @@ function Selection:create_editing_input()
|
|||||||
vim.g.avante_login = true
|
vim.g.avante_login = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local code_bufnr = api.nvim_get_current_buf()
|
self.code_bufnr = api.nvim_get_current_buf()
|
||||||
local code_winid = api.nvim_get_current_win()
|
self.code_winid = api.nvim_get_current_win()
|
||||||
self.cursor_pos = api.nvim_win_get_cursor(code_winid)
|
self.cursor_pos = api.nvim_win_get_cursor(self.code_winid)
|
||||||
self.code_winid = code_winid
|
local code_lines = api.nvim_buf_get_lines(self.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")
|
|
||||||
|
|
||||||
self.selection = Utils.get_visual_selection_and_range()
|
if line1 ~= nil and line2 ~= nil then
|
||||||
|
local filepath = vim.fn.expand("%:p")
|
||||||
|
local filetype = Utils.get_filetype(filepath)
|
||||||
|
local content_lines = vim.list_slice(code_lines, line1, line2)
|
||||||
|
local content = table.concat(content_lines, "\n")
|
||||||
|
local range = Range:new(
|
||||||
|
{ lnum = line1, col = #content_lines[1] },
|
||||||
|
{ lnum = line2, col = #content_lines[#content_lines] }
|
||||||
|
)
|
||||||
|
self.selection = SelectionResult:new(filepath, filetype, content, range)
|
||||||
|
else
|
||||||
|
self.selection = Utils.get_visual_selection_and_range()
|
||||||
|
end
|
||||||
|
|
||||||
if self.selection == nil then
|
if self.selection == nil then
|
||||||
Utils.error("No visual selection found", { once = true, title = "Avante" })
|
Utils.error("No visual selection found", { once = true, title = "Avante" })
|
||||||
@@ -138,116 +269,18 @@ function Selection:create_editing_input()
|
|||||||
end_col = math.min(self.selection.range.finish.col, #code_lines[self.selection.range.finish.lnum])
|
end_col = math.min(self.selection.range.finish.col, #code_lines[self.selection.range.finish.lnum])
|
||||||
end
|
end
|
||||||
|
|
||||||
self.selected_code_extmark_id = api.nvim_buf_set_extmark(code_bufnr, SELECTED_CODE_NAMESPACE, start_row, start_col, {
|
self.selected_code_extmark_id =
|
||||||
hl_group = "Visual",
|
api.nvim_buf_set_extmark(self.code_bufnr, SELECTED_CODE_NAMESPACE, start_row, start_col, {
|
||||||
hl_mode = "combine",
|
hl_group = "Visual",
|
||||||
end_row = end_row,
|
hl_mode = "combine",
|
||||||
end_col = end_col,
|
end_row = end_row,
|
||||||
priority = PRIORITY,
|
end_col = end_col,
|
||||||
})
|
priority = PRIORITY,
|
||||||
|
|
||||||
local function submit_input(input)
|
|
||||||
local full_response = ""
|
|
||||||
local start_line = self.selection.range.start.lnum
|
|
||||||
local finish_line = self.selection.range.finish.lnum
|
|
||||||
|
|
||||||
local original_first_line_indentation = Utils.get_indentation(code_lines[self.selection.range.start.lnum])
|
|
||||||
|
|
||||||
local need_prepend_indentation = false
|
|
||||||
|
|
||||||
self.prompt_input:start_spinner()
|
|
||||||
|
|
||||||
---@type AvanteLLMStartCallback
|
|
||||||
local function on_start(start_opts) end
|
|
||||||
|
|
||||||
---@type AvanteLLMChunkCallback
|
|
||||||
local function on_chunk(chunk)
|
|
||||||
full_response = full_response .. chunk
|
|
||||||
local response_lines_ = vim.split(full_response, "\n")
|
|
||||||
local response_lines = {}
|
|
||||||
local in_code_block = false
|
|
||||||
for _, line in ipairs(response_lines_) do
|
|
||||||
if line:match("^<code>") then
|
|
||||||
in_code_block = true
|
|
||||||
line = line:gsub("^<code>", "")
|
|
||||||
if line ~= "" then table.insert(response_lines, line) end
|
|
||||||
elseif line:match("</code>") then
|
|
||||||
in_code_block = false
|
|
||||||
line = line:gsub("</code>.*$", "")
|
|
||||||
if line ~= "" then table.insert(response_lines, line) end
|
|
||||||
elseif in_code_block then
|
|
||||||
table.insert(response_lines, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #response_lines == 1 then
|
|
||||||
local first_line = response_lines[1]
|
|
||||||
local first_line_indentation = Utils.get_indentation(first_line)
|
|
||||||
need_prepend_indentation = first_line_indentation ~= original_first_line_indentation
|
|
||||||
end
|
|
||||||
if need_prepend_indentation then
|
|
||||||
for i, line in ipairs(response_lines) do
|
|
||||||
response_lines[i] = original_first_line_indentation .. line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
api.nvim_buf_set_lines(code_bufnr, start_line - 1, finish_line, true, response_lines)
|
|
||||||
finish_line = start_line + #response_lines - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type AvanteLLMStopCallback
|
|
||||||
local function on_stop(stop_opts)
|
|
||||||
if stop_opts.error then
|
|
||||||
-- NOTE: in Ubuntu 22.04+ you will see this ignorable error from ~/.local/share/nvim/lazy/avante.nvim/lua/avante/llm.lua `on_error = function(err)`, check to avoid showing this error.
|
|
||||||
if type(stop_opts.error) == "table" and stop_opts.error.exit == nil and stop_opts.error.stderr == "{}" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
Utils.error(
|
|
||||||
"Error occurred while processing the response: " .. vim.inspect(stop_opts.error),
|
|
||||||
{ once = true, title = "Avante" }
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self.prompt_input:stop_spinner()
|
|
||||||
vim.defer_fn(function() self:close_editing_input() end, 0)
|
|
||||||
Utils.debug("full response:", full_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
local filetype = api.nvim_get_option_value("filetype", { buf = code_bufnr })
|
|
||||||
local file_ext = api.nvim_buf_get_name(code_bufnr):match("^.+%.(.+)$")
|
|
||||||
|
|
||||||
local mentions = Utils.extract_mentions(input)
|
|
||||||
input = mentions.new_content
|
|
||||||
local project_context = mentions.enable_project_context and RepoMap.get_repo_map(file_ext) or nil
|
|
||||||
|
|
||||||
local diagnostics = Utils.get_current_selection_diagnostics(code_bufnr, self.selection)
|
|
||||||
|
|
||||||
---@type AvanteSelectedCode | nil
|
|
||||||
local selected_code = nil
|
|
||||||
|
|
||||||
if self.selection then
|
|
||||||
selected_code = {
|
|
||||||
content = self.selection.content,
|
|
||||||
file_type = self.selection.filetype,
|
|
||||||
path = self.selection.filepath,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
Llm.stream({
|
|
||||||
ask = true,
|
|
||||||
project_context = vim.json.encode(project_context),
|
|
||||||
diagnostics = vim.json.encode(diagnostics),
|
|
||||||
selected_files = { { content = code_content, file_type = filetype, path = "" } },
|
|
||||||
code_lang = filetype,
|
|
||||||
selected_code = selected_code,
|
|
||||||
instructions = input,
|
|
||||||
mode = "editing",
|
|
||||||
on_start = on_start,
|
|
||||||
on_chunk = on_chunk,
|
|
||||||
on_stop = on_stop,
|
|
||||||
})
|
})
|
||||||
end
|
|
||||||
|
|
||||||
local prompt_input = PromptInput:new({
|
local prompt_input = PromptInput:new({
|
||||||
submit_callback = submit_input,
|
default_value = request,
|
||||||
|
submit_callback = function(input) self:submit_input(input) end,
|
||||||
cancel_callback = function() self:close_editing_input() end,
|
cancel_callback = function() self:close_editing_input() end,
|
||||||
win_opts = {
|
win_opts = {
|
||||||
border = Config.windows.edit.border,
|
border = Config.windows.edit.border,
|
||||||
@@ -285,6 +318,13 @@ end
|
|||||||
|
|
||||||
function Selection:setup_autocmds()
|
function Selection:setup_autocmds()
|
||||||
Selection.did_setup = true
|
Selection.did_setup = true
|
||||||
|
|
||||||
|
api.nvim_create_autocmd("User", {
|
||||||
|
group = self.augroup,
|
||||||
|
pattern = "AvanteEditSubmitted",
|
||||||
|
callback = function(ev) self:submit_input(ev.data.request) end,
|
||||||
|
})
|
||||||
|
|
||||||
api.nvim_create_autocmd({ "ModeChanged" }, {
|
api.nvim_create_autocmd({ "ModeChanged" }, {
|
||||||
group = self.augroup,
|
group = self.augroup,
|
||||||
pattern = { "n:v", "n:V", "n:" }, -- Entering Visual mode from Normal mode
|
pattern = { "n:v", "n:V", "n:" }, -- Entering Visual mode from Normal mode
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ local SelectionResult = {}
|
|||||||
SelectionResult.__index = SelectionResult
|
SelectionResult.__index = SelectionResult
|
||||||
|
|
||||||
-- Create a selection content and range
|
-- Create a selection content and range
|
||||||
|
---@param filepath string Filepath of the selected content
|
||||||
|
---@param filetype string Filetype of the selected content
|
||||||
---@param content string Selected content
|
---@param content string Selected content
|
||||||
---@param range avante.Range Selection range
|
---@param range avante.Range Selection range
|
||||||
function SelectionResult:new(filepath, filetype, content, range)
|
function SelectionResult:new(filepath, filetype, content, range)
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ function PromptInput:open()
|
|||||||
self.winid = winid
|
self.winid = winid
|
||||||
|
|
||||||
api.nvim_set_option_value("wrap", false, { win = winid })
|
api.nvim_set_option_value("wrap", false, { win = winid })
|
||||||
|
api.nvim_set_option_value("winblend", 5, { win = winid })
|
||||||
|
api.nvim_set_option_value("winhighlight", "FloatBorder:NormalFloat", { win = winid })
|
||||||
api.nvim_set_option_value("cursorline", true, { win = winid })
|
api.nvim_set_option_value("cursorline", true, { win = winid })
|
||||||
api.nvim_set_option_value("modifiable", true, { buf = bufnr })
|
api.nvim_set_option_value("modifiable", true, { buf = bufnr })
|
||||||
|
|
||||||
@@ -200,6 +202,7 @@ function PromptInput:show_shortcuts_hints()
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.shortcuts_hints_winid = api.nvim_open_win(buf, false, opts)
|
self.shortcuts_hints_winid = api.nvim_open_win(buf, false, opts)
|
||||||
|
api.nvim_set_option_value("winblend", 10, { win = self.shortcuts_hints_winid })
|
||||||
end
|
end
|
||||||
|
|
||||||
function PromptInput:close_shortcuts_hints()
|
function PromptInput:close_shortcuts_hints()
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ end
|
|||||||
---@param n string
|
---@param n string
|
||||||
---@param c vim.api.keyset.user_command.callback
|
---@param c vim.api.keyset.user_command.callback
|
||||||
---@param o vim.api.keyset.user_command.opts
|
---@param o vim.api.keyset.user_command.opts
|
||||||
local cmd = function(n, c, o)
|
local function cmd(n, c, o)
|
||||||
o = vim.tbl_extend("force", { nargs = 0 }, o or {})
|
o = vim.tbl_extend("force", { nargs = 0 }, o or {})
|
||||||
api.nvim_create_user_command("Avante" .. n, c, o)
|
api.nvim_create_user_command("Avante" .. n, c, o)
|
||||||
end
|
end
|
||||||
@@ -102,8 +102,8 @@ end, {
|
|||||||
})
|
})
|
||||||
cmd(
|
cmd(
|
||||||
"Edit",
|
"Edit",
|
||||||
function(opts) require("avante.api").edit(vim.trim(opts.args)) end,
|
function(opts) require("avante.api").edit(vim.trim(opts.args), opts.line1, opts.line2) end,
|
||||||
{ desc = "avante: edit selected block", nargs = "*" }
|
{ desc = "avante: edit selected block", nargs = "*", range = 2 }
|
||||||
)
|
)
|
||||||
cmd("Refresh", function() require("avante.api").refresh() end, { desc = "avante: refresh windows" })
|
cmd("Refresh", function() require("avante.api").refresh() end, { desc = "avante: refresh windows" })
|
||||||
cmd("Focus", function() require("avante.api").focus() end, { desc = "avante: switch focus windows" })
|
cmd("Focus", function() require("avante.api").focus() end, { desc = "avante: switch focus windows" })
|
||||||
|
|||||||
Reference in New Issue
Block a user