feat: ask selected code block (#39)
This commit is contained in:
		
							parent
							
								
									dea737bf05
								
							
						
					
					
						commit
						3dca5f4764
					
				@ -89,7 +89,7 @@ Default setup configuration:
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mappings = {
 | 
					  mappings = {
 | 
				
			||||||
    show_sidebar = "<leader>aa",
 | 
					    ask = "<leader>aa",
 | 
				
			||||||
    diff = {
 | 
					    diff = {
 | 
				
			||||||
      ours = "co",
 | 
					      ours = "co",
 | 
				
			||||||
      theirs = "ct",
 | 
					      theirs = "ct",
 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,9 @@ Your primary task is to suggest code modifications with precise line number rang
 | 
				
			|||||||
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. 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2. When suggesting modifications:
 | 
					2. When suggesting modifications:
 | 
				
			||||||
   a. Explain why the change is necessary or beneficial.
 | 
					   a. Use the language in the question to reply. If there are non-English parts in the question, use the language of those parts.
 | 
				
			||||||
   b. Provide the exact code snippet to be replaced using this format:
 | 
					   b. Explain why the change is necessary or beneficial.
 | 
				
			||||||
 | 
					   c. Provide the exact code snippet to be replaced using this format:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Replace lines: {{start_line}}-{{end_line}}
 | 
					Replace lines: {{start_line}}-{{end_line}}
 | 
				
			||||||
```{{language}}
 | 
					```{{language}}
 | 
				
			||||||
@ -58,14 +59,12 @@ Replace lines: {{start_line}}-{{end_line}}
 | 
				
			|||||||
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.
 | 
					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.
 | 
				
			||||||
]]
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function call_claude_api_stream(question, code_lang, code_content, on_chunk, on_complete)
 | 
					local function call_claude_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
 | 
				
			||||||
  local api_key = os.getenv("ANTHROPIC_API_KEY")
 | 
					  local api_key = os.getenv("ANTHROPIC_API_KEY")
 | 
				
			||||||
  if not api_key then
 | 
					  if not api_key then
 | 
				
			||||||
    error("ANTHROPIC_API_KEY environment variable is not set")
 | 
					    error("ANTHROPIC_API_KEY environment variable is not set")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local user_prompt = base_user_prompt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  local tokens = Config.claude.max_tokens
 | 
					  local tokens = Config.claude.max_tokens
 | 
				
			||||||
  local headers = {
 | 
					  local headers = {
 | 
				
			||||||
    ["Content-Type"] = "application/json",
 | 
					    ["Content-Type"] = "application/json",
 | 
				
			||||||
@ -79,33 +78,56 @@ local function call_claude_api_stream(question, code_lang, code_content, on_chun
 | 
				
			|||||||
    text = string.format("<code>```%s\n%s```</code>", code_lang, code_content),
 | 
					    text = string.format("<code>```%s\n%s```</code>", code_lang, code_content),
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if Tiktoken.count(code_prompt_obj.text) > 1024 then
 | 
				
			||||||
 | 
					    code_prompt_obj.cache_control = { type = "ephemeral" }
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if selected_code_content then
 | 
				
			||||||
 | 
					    code_prompt_obj.text = string.format("<code_context>```%s\n%s```</code_context>", code_lang, code_content)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local message_content = {
 | 
				
			||||||
 | 
					    code_prompt_obj,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if selected_code_content then
 | 
				
			||||||
 | 
					    local selected_code_obj = {
 | 
				
			||||||
 | 
					      type = "text",
 | 
				
			||||||
 | 
					      text = string.format("<code>```%s\n%s```</code>", code_lang, selected_code_content),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if Tiktoken.count(selected_code_obj.text) > 1024 then
 | 
				
			||||||
 | 
					      selected_code_obj.cache_control = { type = "ephemeral" }
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    table.insert(message_content, selected_code_obj)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  table.insert(message_content, {
 | 
				
			||||||
 | 
					    type = "text",
 | 
				
			||||||
 | 
					    text = string.format("<question>%s</question>", question),
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local user_prompt = base_user_prompt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local user_prompt_obj = {
 | 
					  local user_prompt_obj = {
 | 
				
			||||||
    type = "text",
 | 
					    type = "text",
 | 
				
			||||||
    text = user_prompt,
 | 
					    text = user_prompt,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if Tiktoken.count(code_prompt_obj.text) > 1024 then
 | 
					 | 
				
			||||||
    code_prompt_obj.cache_control = { type = "ephemeral" }
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if Tiktoken.count(user_prompt_obj.text) > 1024 then
 | 
					  if Tiktoken.count(user_prompt_obj.text) > 1024 then
 | 
				
			||||||
    user_prompt_obj.cache_control = { type = "ephemeral" }
 | 
					    user_prompt_obj.cache_control = { type = "ephemeral" }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  table.insert(message_content, user_prompt_obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local body = {
 | 
					  local body = {
 | 
				
			||||||
    model = Config.claude.model,
 | 
					    model = Config.claude.model,
 | 
				
			||||||
    system = system_prompt,
 | 
					    system = system_prompt,
 | 
				
			||||||
    messages = {
 | 
					    messages = {
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        role = "user",
 | 
					        role = "user",
 | 
				
			||||||
        content = {
 | 
					        content = message_content,
 | 
				
			||||||
          code_prompt_obj,
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            type = "text",
 | 
					 | 
				
			||||||
            text = string.format("<question>%s</question>", question),
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          user_prompt_obj,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    stream = true,
 | 
					    stream = true,
 | 
				
			||||||
@ -154,21 +176,39 @@ local function call_claude_api_stream(question, code_lang, code_content, on_chun
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function call_openai_api_stream(question, code_lang, code_content, on_chunk, on_complete)
 | 
					local function call_openai_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
 | 
				
			||||||
  local api_key = os.getenv("OPENAI_API_KEY")
 | 
					  local api_key = os.getenv("OPENAI_API_KEY")
 | 
				
			||||||
  if not api_key and Config.provider == "openai" then
 | 
					  if not api_key and Config.provider == "openai" then
 | 
				
			||||||
    error("OPENAI_API_KEY environment variable is not set")
 | 
					    error("OPENAI_API_KEY environment variable is not set")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local user_prompt = base_user_prompt
 | 
					  local user_prompt = base_user_prompt
 | 
				
			||||||
    .. "\n\nQUESTION:\n"
 | 
					 | 
				
			||||||
    .. question
 | 
					 | 
				
			||||||
    .. "\n\nCODE:\n"
 | 
					    .. "\n\nCODE:\n"
 | 
				
			||||||
    .. "```"
 | 
					    .. "```"
 | 
				
			||||||
    .. code_lang
 | 
					    .. code_lang
 | 
				
			||||||
    .. "\n"
 | 
					    .. "\n"
 | 
				
			||||||
    .. code_content
 | 
					    .. code_content
 | 
				
			||||||
    .. "\n```"
 | 
					    .. "\n```"
 | 
				
			||||||
 | 
					    .. "\n\nQUESTION:\n"
 | 
				
			||||||
 | 
					    .. question
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if selected_code_content then
 | 
				
			||||||
 | 
					    user_prompt = base_user_prompt
 | 
				
			||||||
 | 
					      .. "\n\nCODE CONTEXT:\n"
 | 
				
			||||||
 | 
					      .. "```"
 | 
				
			||||||
 | 
					      .. code_lang
 | 
				
			||||||
 | 
					      .. "\n"
 | 
				
			||||||
 | 
					      .. code_content
 | 
				
			||||||
 | 
					      .. "\n```"
 | 
				
			||||||
 | 
					      .. "\n\nCODE:\n"
 | 
				
			||||||
 | 
					      .. "```"
 | 
				
			||||||
 | 
					      .. code_lang
 | 
				
			||||||
 | 
					      .. "\n"
 | 
				
			||||||
 | 
					      .. selected_code_content
 | 
				
			||||||
 | 
					      .. "\n```"
 | 
				
			||||||
 | 
					      .. "\n\nQUESTION:\n"
 | 
				
			||||||
 | 
					      .. question
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local url, headers, body
 | 
					  local url, headers, body
 | 
				
			||||||
  if Config.provider == "azure" then
 | 
					  if Config.provider == "azure" then
 | 
				
			||||||
@ -258,13 +298,14 @@ end
 | 
				
			|||||||
---@param question string
 | 
					---@param question string
 | 
				
			||||||
---@param code_lang string
 | 
					---@param code_lang string
 | 
				
			||||||
---@param code_content string
 | 
					---@param code_content string
 | 
				
			||||||
 | 
					---@param selected_content_content string | nil
 | 
				
			||||||
---@param on_chunk fun(chunk: string): any
 | 
					---@param on_chunk fun(chunk: string): any
 | 
				
			||||||
---@param on_complete fun(err: string|nil): any
 | 
					---@param on_complete fun(err: string|nil): any
 | 
				
			||||||
function M.call_ai_api_stream(question, code_lang, code_content, on_chunk, on_complete)
 | 
					function M.call_ai_api_stream(question, code_lang, code_content, selected_content_content, on_chunk, on_complete)
 | 
				
			||||||
  if Config.provider == "openai" or Config.provider == "azure" then
 | 
					  if Config.provider == "openai" or Config.provider == "azure" then
 | 
				
			||||||
    call_openai_api_stream(question, code_lang, code_content, on_chunk, on_complete)
 | 
					    call_openai_api_stream(question, code_lang, code_content, selected_content_content, on_chunk, on_complete)
 | 
				
			||||||
  elseif Config.provider == "claude" then
 | 
					  elseif Config.provider == "claude" then
 | 
				
			||||||
    call_claude_api_stream(question, code_lang, code_content, on_chunk, on_complete)
 | 
					    call_claude_api_stream(question, code_lang, code_content, selected_content_content, on_chunk, on_complete)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,8 @@ M.defaults = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mappings = {
 | 
					  mappings = {
 | 
				
			||||||
    show_sidebar = "<leader>aa",
 | 
					    ask = "<leader>aa",
 | 
				
			||||||
 | 
					    edit = "<leader>ae",
 | 
				
			||||||
    diff = {
 | 
					    diff = {
 | 
				
			||||||
      ours = "co",
 | 
					      ours = "co",
 | 
				
			||||||
      theirs = "ct",
 | 
					      theirs = "ct",
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ local Tiktoken = require("avante.tiktoken")
 | 
				
			|||||||
local Sidebar = require("avante.sidebar")
 | 
					local Sidebar = require("avante.sidebar")
 | 
				
			||||||
local Config = require("avante.config")
 | 
					local Config = require("avante.config")
 | 
				
			||||||
local Diff = require("avante.diff")
 | 
					local Diff = require("avante.diff")
 | 
				
			||||||
 | 
					local Selection = require("avante.selection")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---@class Avante
 | 
					---@class Avante
 | 
				
			||||||
local M = {
 | 
					local M = {
 | 
				
			||||||
@ -11,6 +12,7 @@ local M = {
 | 
				
			|||||||
  sidebars = {},
 | 
					  sidebars = {},
 | 
				
			||||||
  ---@type avante.Sidebar
 | 
					  ---@type avante.Sidebar
 | 
				
			||||||
  current = nil,
 | 
					  current = nil,
 | 
				
			||||||
 | 
					  selection = nil,
 | 
				
			||||||
  _once = false,
 | 
					  _once = false,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,7 +37,7 @@ H.commands = function()
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
H.keymaps = function()
 | 
					H.keymaps = function()
 | 
				
			||||||
  vim.keymap.set({ "n" }, Config.mappings.show_sidebar, M.toggle, { noremap = true })
 | 
					  vim.keymap.set({ "n", "v" }, Config.mappings.ask, M.toggle, { noremap = true })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
H.autocmds = function()
 | 
					H.autocmds = function()
 | 
				
			||||||
@ -76,7 +78,9 @@ H.autocmds = function()
 | 
				
			|||||||
      if s then
 | 
					      if s then
 | 
				
			||||||
        s:destroy()
 | 
					        s:destroy()
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
      M.sidebars[tab] = nil
 | 
					      if tab ~= nil then
 | 
				
			||||||
 | 
					        M.sidebars[tab] = nil
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end,
 | 
					    end,
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -137,6 +141,10 @@ function M.setup(opts)
 | 
				
			|||||||
    highlights = Config.highlights.diff,
 | 
					    highlights = Config.highlights.diff,
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local selection = Selection:new()
 | 
				
			||||||
 | 
					  selection:setup()
 | 
				
			||||||
 | 
					  M.selection = selection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  -- setup helpers
 | 
					  -- setup helpers
 | 
				
			||||||
  H.autocmds()
 | 
					  H.autocmds()
 | 
				
			||||||
  H.commands()
 | 
					  H.commands()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								lua/avante/range.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lua/avante/range.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					--@class avante.Range
 | 
				
			||||||
 | 
					--@field start table Selection start point
 | 
				
			||||||
 | 
					--@field start.line number Line number of the selection start
 | 
				
			||||||
 | 
					--@field start.col number Column number of the selection start
 | 
				
			||||||
 | 
					--@field finish table Selection end point
 | 
				
			||||||
 | 
					--@field finish.line number Line number of the selection end
 | 
				
			||||||
 | 
					--@field finish.col number Column number of the selection end
 | 
				
			||||||
 | 
					local Range = {}
 | 
				
			||||||
 | 
					Range.__index = Range
 | 
				
			||||||
 | 
					-- Create a selection range
 | 
				
			||||||
 | 
					-- @param start table Selection start point
 | 
				
			||||||
 | 
					-- @param start.line number Line number of the selection start
 | 
				
			||||||
 | 
					-- @param start.col number Column number of the selection start
 | 
				
			||||||
 | 
					-- @param finish table Selection end point
 | 
				
			||||||
 | 
					-- @param finish.line number Line number of the selection end
 | 
				
			||||||
 | 
					-- @param finish.col number Column number of the selection end
 | 
				
			||||||
 | 
					function Range.new(start, finish)
 | 
				
			||||||
 | 
					  local self = setmetatable({}, Range)
 | 
				
			||||||
 | 
					  self.start = start
 | 
				
			||||||
 | 
					  self.finish = finish
 | 
				
			||||||
 | 
					  return self
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return Range
 | 
				
			||||||
							
								
								
									
										95
									
								
								lua/avante/selection.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lua/avante/selection.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					local Config = require("avante.config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local api = vim.api
 | 
				
			||||||
 | 
					local fn = vim.fn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local NAMESPACE = api.nvim_create_namespace("avante_selection")
 | 
				
			||||||
 | 
					local PRIORITY = vim.highlight.priorities.user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Selection = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:new()
 | 
				
			||||||
 | 
					  return setmetatable({
 | 
				
			||||||
 | 
					    hints_popup_extmark_id = nil,
 | 
				
			||||||
 | 
					    edit_popup_renderer = nil,
 | 
				
			||||||
 | 
					    augroup = api.nvim_create_augroup("avante_selection", { clear = true }),
 | 
				
			||||||
 | 
					  }, { __index = self })
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:get_virt_text_line()
 | 
				
			||||||
 | 
					  local current_pos = fn.getpos(".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Get the current and start position line numbers
 | 
				
			||||||
 | 
					  local current_line = current_pos[2] - 1 -- 0-indexed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Ensure line numbers are not negative and don't exceed buffer range
 | 
				
			||||||
 | 
					  local total_lines = api.nvim_buf_line_count(0)
 | 
				
			||||||
 | 
					  if current_line < 0 then
 | 
				
			||||||
 | 
					    current_line = 0
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  if current_line >= total_lines then
 | 
				
			||||||
 | 
					    current_line = total_lines - 1
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Take the first line of the selection to ensure virt_text is always in the top right corner
 | 
				
			||||||
 | 
					  return current_line
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:show_hints_popup()
 | 
				
			||||||
 | 
					  self:close_hints_popup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local hint_text = string.format(" [Ask %s] ", Config.mappings.ask)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local virt_text_line = self:get_virt_text_line()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  self.hints_popup_extmark_id = vim.api.nvim_buf_set_extmark(0, NAMESPACE, virt_text_line, -1, {
 | 
				
			||||||
 | 
					    virt_text = { { hint_text, "Keyword" } },
 | 
				
			||||||
 | 
					    virt_text_pos = "eol",
 | 
				
			||||||
 | 
					    priority = PRIORITY,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:close_hints_popup()
 | 
				
			||||||
 | 
					  if self.hints_popup_extmark_id then
 | 
				
			||||||
 | 
					    vim.api.nvim_buf_del_extmark(0, NAMESPACE, self.hints_popup_extmark_id)
 | 
				
			||||||
 | 
					    self.hints_popup_extmark_id = nil
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:setup()
 | 
				
			||||||
 | 
					  vim.api.nvim_create_autocmd({ "ModeChanged" }, {
 | 
				
			||||||
 | 
					    group = self.augroup,
 | 
				
			||||||
 | 
					    pattern = { "n:v", "n:V", "n:" }, -- Entering Visual mode from Normal mode
 | 
				
			||||||
 | 
					    callback = function()
 | 
				
			||||||
 | 
					      self:show_hints_popup()
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
 | 
				
			||||||
 | 
					    group = self.augroup,
 | 
				
			||||||
 | 
					    callback = function()
 | 
				
			||||||
 | 
					      if vim.fn.mode() == "v" or vim.fn.mode() == "V" or vim.fn.mode() == "" then
 | 
				
			||||||
 | 
					        self:show_hints_popup()
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        self:close_hints_popup()
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  api.nvim_create_autocmd({ "ModeChanged" }, {
 | 
				
			||||||
 | 
					    group = self.augroup,
 | 
				
			||||||
 | 
					    pattern = { "v:n", "v:i", "v:c" }, -- Switching from visual mode back to normal, insert, or other modes
 | 
				
			||||||
 | 
					    callback = function()
 | 
				
			||||||
 | 
					      self:close_hints_popup()
 | 
				
			||||||
 | 
					    end,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Selection:delete_autocmds()
 | 
				
			||||||
 | 
					  if self.augroup then
 | 
				
			||||||
 | 
					    vim.api.nvim_del_augroup_by_id(self.augroup)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  self.augroup = nil
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return Selection
 | 
				
			||||||
							
								
								
									
										17
									
								
								lua/avante/selection_result.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lua/avante/selection_result.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					--@class avante.SelectionResult
 | 
				
			||||||
 | 
					--@field content string Selected content
 | 
				
			||||||
 | 
					--@field range avante.Range Selection range
 | 
				
			||||||
 | 
					local SelectionResult = {}
 | 
				
			||||||
 | 
					SelectionResult.__index = SelectionResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Create a selection content and range
 | 
				
			||||||
 | 
					--@param content string Selected content
 | 
				
			||||||
 | 
					--@param range avante.Range Selection range
 | 
				
			||||||
 | 
					function SelectionResult.new(content, range)
 | 
				
			||||||
 | 
					  local self = setmetatable({}, SelectionResult)
 | 
				
			||||||
 | 
					  self.content = content
 | 
				
			||||||
 | 
					  self.range = range
 | 
				
			||||||
 | 
					  return self
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return SelectionResult
 | 
				
			||||||
@ -20,6 +20,7 @@ local Sidebar = {}
 | 
				
			|||||||
---@class avante.SidebarState
 | 
					---@class avante.SidebarState
 | 
				
			||||||
---@field win integer
 | 
					---@field win integer
 | 
				
			||||||
---@field buf integer
 | 
					---@field buf integer
 | 
				
			||||||
 | 
					---@field selection avante.SelectionResult | nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---@class avante.Sidebar
 | 
					---@class avante.Sidebar
 | 
				
			||||||
---@field id integer
 | 
					---@field id integer
 | 
				
			||||||
@ -33,11 +34,11 @@ local Sidebar = {}
 | 
				
			|||||||
function Sidebar:new(id)
 | 
					function Sidebar:new(id)
 | 
				
			||||||
  return setmetatable({
 | 
					  return setmetatable({
 | 
				
			||||||
    id = id,
 | 
					    id = id,
 | 
				
			||||||
    code = { buf = 0, win = 0 },
 | 
					    code = { buf = 0, win = 0, selection = nil },
 | 
				
			||||||
    winid = { result = 0, input = 0 },
 | 
					    winid = { result = 0, input = 0 },
 | 
				
			||||||
    view = View:new(),
 | 
					    view = View:new(),
 | 
				
			||||||
    renderer = nil,
 | 
					    renderer = nil,
 | 
				
			||||||
  }, { __index = Sidebar })
 | 
					  }, { __index = self })
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- This function should only be used on TabClosed, nothing else.
 | 
					--- This function should only be used on TabClosed, nothing else.
 | 
				
			||||||
@ -63,10 +64,17 @@ function Sidebar:reset()
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Sidebar:open()
 | 
					function Sidebar:open()
 | 
				
			||||||
 | 
					  local in_visual_mode = Utils.in_visual_mode() and self:in_code_win()
 | 
				
			||||||
  if not self.view:is_open() then
 | 
					  if not self.view:is_open() then
 | 
				
			||||||
    self:intialize()
 | 
					    self:intialize()
 | 
				
			||||||
    self:render()
 | 
					    self:render()
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
 | 
					    if in_visual_mode then
 | 
				
			||||||
 | 
					      self:close()
 | 
				
			||||||
 | 
					      self:intialize()
 | 
				
			||||||
 | 
					      self:render()
 | 
				
			||||||
 | 
					      return self
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
    self:focus()
 | 
					    self:focus()
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return self
 | 
					  return self
 | 
				
			||||||
@ -86,8 +94,13 @@ function Sidebar:focus()
 | 
				
			|||||||
  return false
 | 
					  return false
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Sidebar:in_code_win()
 | 
				
			||||||
 | 
					  return self.code.win == api.nvim_get_current_win()
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Sidebar:toggle()
 | 
					function Sidebar:toggle()
 | 
				
			||||||
  if self.view:is_open() then
 | 
					  local in_visual_mode = Utils.in_visual_mode() and self:in_code_win()
 | 
				
			||||||
 | 
					  if self.view:is_open() and not in_visual_mode then
 | 
				
			||||||
    self:close()
 | 
					    self:close()
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
@ -108,6 +121,7 @@ end
 | 
				
			|||||||
function Sidebar:intialize()
 | 
					function Sidebar:intialize()
 | 
				
			||||||
  self.code.win = api.nvim_get_current_win()
 | 
					  self.code.win = api.nvim_get_current_win()
 | 
				
			||||||
  self.code.buf = api.nvim_get_current_buf()
 | 
					  self.code.buf = api.nvim_get_current_buf()
 | 
				
			||||||
 | 
					  self.code.selection = Utils.get_visual_selection_and_range()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local split_command = "botright vs"
 | 
					  local split_command = "botright vs"
 | 
				
			||||||
  local layout = Config.get_renderer_layout_options()
 | 
					  local layout = Config.get_renderer_layout_options()
 | 
				
			||||||
@ -123,15 +137,15 @@ function Sidebar:intialize()
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  self.renderer:on_mount(function()
 | 
					  self.renderer:on_mount(function()
 | 
				
			||||||
    local components = self.renderer:get_focusable_components()
 | 
					    self.winid.result = self.renderer:get_component_by_id("result").winid
 | 
				
			||||||
    -- current layout is a
 | 
					    self.winid.input = self.renderer:get_component_by_id("input").winid
 | 
				
			||||||
    -- [  chat  ]
 | 
					 | 
				
			||||||
    -- <gap>
 | 
					 | 
				
			||||||
    -- [ input ]
 | 
					 | 
				
			||||||
    self.winid.result = components[1].winid
 | 
					 | 
				
			||||||
    self.winid.input = components[2].winid
 | 
					 | 
				
			||||||
    self.augroup = api.nvim_create_augroup("avante_" .. self.id .. self.view.win, { clear = true })
 | 
					    self.augroup = api.nvim_create_augroup("avante_" .. self.id .. self.view.win, { clear = true })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local filetype = api.nvim_get_option_value("filetype", { buf = self.code.buf })
 | 
				
			||||||
 | 
					    local selected_code_buf = self.renderer:get_component_by_id("selected_code").bufnr
 | 
				
			||||||
 | 
					    api.nvim_buf_set_option(selected_code_buf, "filetype", filetype)
 | 
				
			||||||
 | 
					    api.nvim_set_option_value("wrap", false, { win = self.renderer:get_component_by_id("selected_code").winid })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    api.nvim_create_autocmd("BufEnter", {
 | 
					    api.nvim_create_autocmd("BufEnter", {
 | 
				
			||||||
      group = self.augroup,
 | 
					      group = self.augroup,
 | 
				
			||||||
      buffer = self.view.buf,
 | 
					      buffer = self.view.buf,
 | 
				
			||||||
@ -215,7 +229,11 @@ function Sidebar:update_content(content, focus, callback)
 | 
				
			|||||||
        -- XXX: omit error for now, but should fix me why it can't jump here.
 | 
					        -- XXX: omit error for now, but should fix me why it can't jump here.
 | 
				
			||||||
        return err
 | 
					        return err
 | 
				
			||||||
      end)
 | 
					      end)
 | 
				
			||||||
      api.nvim_win_set_cursor(self.winid.result, { api.nvim_buf_line_count(self.view.buf), 0 })
 | 
					      xpcall(function()
 | 
				
			||||||
 | 
					        api.nvim_win_set_cursor(self.winid.result, { api.nvim_buf_line_count(self.view.buf), 0 })
 | 
				
			||||||
 | 
					      end, function(err)
 | 
				
			||||||
 | 
					        return err
 | 
				
			||||||
 | 
					      end)
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end, 0)
 | 
					  end, 0)
 | 
				
			||||||
  return self
 | 
					  return self
 | 
				
			||||||
@ -260,10 +278,12 @@ local function is_cursor_in_codeblock(codeblocks)
 | 
				
			|||||||
  return nil
 | 
					  return nil
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function prepend_line_number(content)
 | 
					local function prepend_line_number(content, start_line)
 | 
				
			||||||
 | 
					  start_line = start_line or 1
 | 
				
			||||||
  local lines = vim.split(content, "\n")
 | 
					  local lines = vim.split(content, "\n")
 | 
				
			||||||
  local result = {}
 | 
					  local result = {}
 | 
				
			||||||
  for i, line in ipairs(lines) do
 | 
					  for i, line in ipairs(lines) do
 | 
				
			||||||
 | 
					    i = i + start_line - 1
 | 
				
			||||||
    table.insert(result, "L" .. i .. ": " .. line)
 | 
					    table.insert(result, "L" .. i .. ": " .. line)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return table.concat(result, "\n")
 | 
					  return table.concat(result, "\n")
 | 
				
			||||||
@ -560,25 +580,53 @@ function Sidebar:render()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    local content = self:get_code_content()
 | 
					    local content = self:get_code_content()
 | 
				
			||||||
    local content_with_line_numbers = prepend_line_number(content)
 | 
					    local content_with_line_numbers = prepend_line_number(content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local selected_code_content_with_line_numbers = nil
 | 
				
			||||||
 | 
					    if self.code.selection ~= nil then
 | 
				
			||||||
 | 
					      selected_code_content_with_line_numbers =
 | 
				
			||||||
 | 
					        prepend_line_number(self.code.selection.content, self.code.selection.range.start.line)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local full_response = ""
 | 
					    local full_response = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    signal.is_loading = true
 | 
					    signal.is_loading = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local filetype = api.nvim_get_option_value("filetype", { buf = self.code.buf })
 | 
					    local filetype = api.nvim_get_option_value("filetype", { buf = self.code.buf })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    AiBot.call_ai_api_stream(user_input, filetype, content_with_line_numbers, function(chunk)
 | 
					    AiBot.call_ai_api_stream(
 | 
				
			||||||
      full_response = full_response .. chunk
 | 
					      user_input,
 | 
				
			||||||
      self:update_content(
 | 
					      filetype,
 | 
				
			||||||
        "## " .. timestamp .. "\n\n> " .. user_input:gsub("\n", "\n> ") .. "\n\n" .. full_response,
 | 
					      content_with_line_numbers,
 | 
				
			||||||
        true
 | 
					      selected_code_content_with_line_numbers,
 | 
				
			||||||
      )
 | 
					      function(chunk)
 | 
				
			||||||
      vim.schedule(function()
 | 
					        full_response = full_response .. chunk
 | 
				
			||||||
        vim.cmd("redraw")
 | 
					        self:update_content(
 | 
				
			||||||
      end)
 | 
					          "## " .. timestamp .. "\n\n> " .. user_input:gsub("\n", "\n> ") .. "\n\n" .. full_response,
 | 
				
			||||||
    end, function(err)
 | 
					          true
 | 
				
			||||||
      signal.is_loading = false
 | 
					        )
 | 
				
			||||||
 | 
					        vim.schedule(function()
 | 
				
			||||||
 | 
					          vim.cmd("redraw")
 | 
				
			||||||
 | 
					        end)
 | 
				
			||||||
 | 
					      end,
 | 
				
			||||||
 | 
					      function(err)
 | 
				
			||||||
 | 
					        signal.is_loading = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if err ~= nil then
 | 
					        if err ~= nil then
 | 
				
			||||||
 | 
					          self:update_content(
 | 
				
			||||||
 | 
					            "## "
 | 
				
			||||||
 | 
					              .. timestamp
 | 
				
			||||||
 | 
					              .. "\n\n> "
 | 
				
			||||||
 | 
					              .. user_input:gsub("\n", "\n> ")
 | 
				
			||||||
 | 
					              .. "\n\n"
 | 
				
			||||||
 | 
					              .. full_response
 | 
				
			||||||
 | 
					              .. "\n\n🚨 Error: "
 | 
				
			||||||
 | 
					              .. vim.inspect(err),
 | 
				
			||||||
 | 
					            true
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          return
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- Execute when the stream request is actually completed
 | 
				
			||||||
        self:update_content(
 | 
					        self:update_content(
 | 
				
			||||||
          "## "
 | 
					          "## "
 | 
				
			||||||
            .. timestamp
 | 
					            .. timestamp
 | 
				
			||||||
@ -586,37 +634,23 @@ function Sidebar:render()
 | 
				
			|||||||
            .. user_input:gsub("\n", "\n> ")
 | 
					            .. user_input:gsub("\n", "\n> ")
 | 
				
			||||||
            .. "\n\n"
 | 
					            .. "\n\n"
 | 
				
			||||||
            .. full_response
 | 
					            .. full_response
 | 
				
			||||||
            .. "\n\n🚨 Error: "
 | 
					            .. "\n\n**Generation complete!** Please review the code suggestions above.\n\n\n\n",
 | 
				
			||||||
            .. vim.inspect(err),
 | 
					          true,
 | 
				
			||||||
          true
 | 
					          function()
 | 
				
			||||||
 | 
					            api.nvim_exec_autocmds("User", { pattern = VIEW_BUFFER_UPDATED_PATTERN })
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return
 | 
					
 | 
				
			||||||
 | 
					        api.nvim_set_current_win(self.winid.result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- Display notification
 | 
				
			||||||
 | 
					        -- show_notification("Content generation complete!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- Save chat history
 | 
				
			||||||
 | 
					        table.insert(chat_history or {}, { timestamp = timestamp, requirement = user_input, response = full_response })
 | 
				
			||||||
 | 
					        save_chat_history(self, chat_history)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
      -- Execute when the stream request is actually completed
 | 
					 | 
				
			||||||
      self:update_content(
 | 
					 | 
				
			||||||
        "## "
 | 
					 | 
				
			||||||
          .. timestamp
 | 
					 | 
				
			||||||
          .. "\n\n> "
 | 
					 | 
				
			||||||
          .. user_input:gsub("\n", "\n> ")
 | 
					 | 
				
			||||||
          .. "\n\n"
 | 
					 | 
				
			||||||
          .. full_response
 | 
					 | 
				
			||||||
          .. "\n\n**Generation complete!** Please review the code suggestions above.\n\n\n\n",
 | 
					 | 
				
			||||||
        true,
 | 
					 | 
				
			||||||
        function()
 | 
					 | 
				
			||||||
          api.nvim_exec_autocmds("User", { pattern = VIEW_BUFFER_UPDATED_PATTERN })
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      api.nvim_set_current_win(self.winid.result)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      -- Display notification
 | 
					 | 
				
			||||||
      -- show_notification("Content generation complete!")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      -- Save chat history
 | 
					 | 
				
			||||||
      table.insert(chat_history or {}, { timestamp = timestamp, requirement = user_input, response = full_response })
 | 
					 | 
				
			||||||
      save_chat_history(self, chat_history)
 | 
					 | 
				
			||||||
    end)
 | 
					 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  local body = function()
 | 
					  local body = function()
 | 
				
			||||||
@ -625,15 +659,38 @@ function Sidebar:render()
 | 
				
			|||||||
    local code_file_fullpath = api.nvim_buf_get_name(self.code.buf)
 | 
					    local code_file_fullpath = api.nvim_buf_get_name(self.code.buf)
 | 
				
			||||||
    local code_filename = fn.fnamemodify(code_file_fullpath, ":t")
 | 
					    local code_filename = fn.fnamemodify(code_file_fullpath, ":t")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local input_label = string.format(" 🙋 with %s %s (<Tab> switch focus): ", icon, code_filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if self.code.selection ~= nil then
 | 
				
			||||||
 | 
					      input_label = string.format(
 | 
				
			||||||
 | 
					        " 🙋 with selected code in %s %s(%d:%d) (<Tab> switch focus): ",
 | 
				
			||||||
 | 
					        icon,
 | 
				
			||||||
 | 
					        code_filename,
 | 
				
			||||||
 | 
					        self.code.selection.range.start.line,
 | 
				
			||||||
 | 
					        self.code.selection.range.finish.line
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local selected_code_lines_count = 0
 | 
				
			||||||
 | 
					    local selected_code_max_lines_count = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local selected_code_size = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if self.code.selection ~= nil then
 | 
				
			||||||
 | 
					      local selected_code_lines = vim.split(self.code.selection.content, "\n")
 | 
				
			||||||
 | 
					      selected_code_lines_count = #selected_code_lines
 | 
				
			||||||
 | 
					      selected_code_size = math.min(selected_code_lines_count, selected_code_max_lines_count) + 4
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return N.rows(
 | 
					    return N.rows(
 | 
				
			||||||
      { flex = 0 },
 | 
					      { flex = 0 },
 | 
				
			||||||
      N.box(
 | 
					      N.box(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          direction = "column",
 | 
					          direction = "column",
 | 
				
			||||||
          size = vim.o.lines - 4,
 | 
					          size = vim.o.lines - 4 - selected_code_size,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        N.buffer({
 | 
					        N.buffer({
 | 
				
			||||||
          id = "response",
 | 
					          id = "result",
 | 
				
			||||||
          flex = 1,
 | 
					          flex = 1,
 | 
				
			||||||
          buf = self.view.buf,
 | 
					          buf = self.view.buf,
 | 
				
			||||||
          autoscroll = true,
 | 
					          autoscroll = true,
 | 
				
			||||||
@ -650,16 +707,35 @@ function Sidebar:render()
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      N.gap(1),
 | 
					      N.gap(1),
 | 
				
			||||||
 | 
					      N.paragraph({
 | 
				
			||||||
 | 
					        hidden = self.code.selection == nil,
 | 
				
			||||||
 | 
					        id = "selected_code",
 | 
				
			||||||
 | 
					        lines = self.code.selection and self.code.selection.content or "",
 | 
				
			||||||
 | 
					        border_label = {
 | 
				
			||||||
 | 
					          text = "💻 Selected Code"
 | 
				
			||||||
 | 
					            .. (
 | 
				
			||||||
 | 
					              selected_code_lines_count > selected_code_max_lines_count
 | 
				
			||||||
 | 
					                and " (Show only the first " .. tostring(selected_code_max_lines_count) .. " lines)"
 | 
				
			||||||
 | 
					              or ""
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          align = "center",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        align = "left",
 | 
				
			||||||
 | 
					        is_focusable = false,
 | 
				
			||||||
 | 
					        max_lines = selected_code_max_lines_count,
 | 
				
			||||||
 | 
					        padding = {
 | 
				
			||||||
 | 
					          top = 1,
 | 
				
			||||||
 | 
					          bottom = 1,
 | 
				
			||||||
 | 
					          left = 1,
 | 
				
			||||||
 | 
					          right = 1,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
      N.columns(
 | 
					      N.columns(
 | 
				
			||||||
        { flex = 0 },
 | 
					        { flex = 0 },
 | 
				
			||||||
        N.text_input({
 | 
					        N.text_input({
 | 
				
			||||||
          id = "text-input",
 | 
					          id = "input",
 | 
				
			||||||
          border_label = {
 | 
					          border_label = {
 | 
				
			||||||
            text = string.format(
 | 
					            text = input_label,
 | 
				
			||||||
              " 🙋 with %s %s (<Tab> key to switch between result and input): ",
 | 
					 | 
				
			||||||
              icon,
 | 
					 | 
				
			||||||
              code_filename
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          placeholder = "Enter your question",
 | 
					          placeholder = "Enter your question",
 | 
				
			||||||
          autofocus = true,
 | 
					          autofocus = true,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,57 @@
 | 
				
			|||||||
 | 
					local Range = require("avante.range")
 | 
				
			||||||
 | 
					local SelectionResult = require("avante.selection_result")
 | 
				
			||||||
local M = {}
 | 
					local M = {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
function M.trim_suffix(str, suffix)
 | 
					function M.trim_suffix(str, suffix)
 | 
				
			||||||
  return string.gsub(str, suffix .. "$", "")
 | 
					  return string.gsub(str, suffix .. "$", "")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					 | 
				
			||||||
function M.trim_line_number_prefix(line)
 | 
					function M.trim_line_number_prefix(line)
 | 
				
			||||||
  return line:gsub("^L%d+: ", "")
 | 
					  return line:gsub("^L%d+: ", "")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					function M.in_visual_mode()
 | 
				
			||||||
 | 
					  local current_mode = vim.fn.mode()
 | 
				
			||||||
 | 
					  return current_mode == "v" or current_mode == "V" or current_mode == ""
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					-- Get the selected content and range in Visual mode
 | 
				
			||||||
 | 
					-- @return avante.SelectionResult | nil Selected content and range
 | 
				
			||||||
 | 
					function M.get_visual_selection_and_range()
 | 
				
			||||||
 | 
					  if not M.in_visual_mode() then
 | 
				
			||||||
 | 
					    return nil
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  -- Get the start and end positions of Visual mode
 | 
				
			||||||
 | 
					  local start_pos = vim.fn.getpos("v")
 | 
				
			||||||
 | 
					  local end_pos = vim.fn.getpos(".")
 | 
				
			||||||
 | 
					  -- Get the start and end line and column numbers
 | 
				
			||||||
 | 
					  local start_line = start_pos[2]
 | 
				
			||||||
 | 
					  local start_col = start_pos[3]
 | 
				
			||||||
 | 
					  local end_line = end_pos[2]
 | 
				
			||||||
 | 
					  local end_col = end_pos[3]
 | 
				
			||||||
 | 
					  -- If the start point is after the end point, swap them
 | 
				
			||||||
 | 
					  if start_line > end_line or (start_line == end_line and start_col > end_col) then
 | 
				
			||||||
 | 
					    start_line, end_line = end_line, start_line
 | 
				
			||||||
 | 
					    start_col, end_col = end_col, start_col
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  local content = ""
 | 
				
			||||||
 | 
					  local range = Range.new({ line = start_line, col = start_col }, { line = end_line, col = end_col })
 | 
				
			||||||
 | 
					  -- Check if it's a single-line selection
 | 
				
			||||||
 | 
					  if start_line == end_line then
 | 
				
			||||||
 | 
					    -- Get partial content of a single line
 | 
				
			||||||
 | 
					    local line = vim.fn.getline(start_line)
 | 
				
			||||||
 | 
					    -- content = string.sub(line, start_col, end_col)
 | 
				
			||||||
 | 
					    content = line
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    -- Multi-line selection: Get all lines in the selection
 | 
				
			||||||
 | 
					    local lines = vim.fn.getline(start_line, end_line)
 | 
				
			||||||
 | 
					    -- Extract partial content of the first line
 | 
				
			||||||
 | 
					    -- lines[1] = string.sub(lines[1], start_col)
 | 
				
			||||||
 | 
					    -- Extract partial content of the last line
 | 
				
			||||||
 | 
					    -- lines[#lines] = string.sub(lines[#lines], 1, end_col)
 | 
				
			||||||
 | 
					    -- Concatenate all lines in the selection into a string
 | 
				
			||||||
 | 
					    content = table.concat(lines, "\n")
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  if not content then
 | 
				
			||||||
 | 
					    return nil
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  -- Return the selected content and range
 | 
				
			||||||
 | 
					  return SelectionResult.new(content, range)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
return M
 | 
					return M
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user