Review and cleanup
This commit is contained in:
parent
81c200df66
commit
2c641faf11
@ -14,6 +14,26 @@ local current_suggestion = {
|
|||||||
max_context_lines = 10, -- Maximum number of context lines to send
|
max_context_lines = 10, -- Maximum number of context lines to send
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Helper function to get visible lines around cursor
|
||||||
|
local function get_visible_lines()
|
||||||
|
local win = vim.api.nvim_get_current_win()
|
||||||
|
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||||
|
local top_line = vim.fn.line('w0')
|
||||||
|
local bottom_line = vim.fn.line('w$')
|
||||||
|
|
||||||
|
-- Get all visible lines
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(0, top_line - 1, bottom_line, false)
|
||||||
|
local relative_cursor = cursor_line - top_line + 1
|
||||||
|
|
||||||
|
return lines, relative_cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to get current line indent
|
||||||
|
local function get_line_indent(line)
|
||||||
|
local indent = line:match("^%s+") or ""
|
||||||
|
return indent
|
||||||
|
end
|
||||||
|
|
||||||
-- Debug function
|
-- Debug function
|
||||||
local function debug_print(...)
|
local function debug_print(...)
|
||||||
vim.notify(string.format(...), vim.log.levels.INFO)
|
vim.notify(string.format(...), vim.log.levels.INFO)
|
||||||
@ -52,44 +72,27 @@ end
|
|||||||
local function show_suggestion(suggestion, start_col)
|
local function show_suggestion(suggestion, start_col)
|
||||||
clear_suggestion()
|
clear_suggestion()
|
||||||
|
|
||||||
-- Split suggestion into lines
|
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||||
|
local line = cursor[1] - 1
|
||||||
|
local current_line = vim.api.nvim_get_current_line()
|
||||||
|
|
||||||
|
-- Get current line indent
|
||||||
|
local indent = get_line_indent(current_line)
|
||||||
|
|
||||||
|
-- Split suggestion into lines and apply indent to all lines except first
|
||||||
local suggestion_lines = vim.split(suggestion, "\n")
|
local suggestion_lines = vim.split(suggestion, "\n")
|
||||||
|
for i = 2, #suggestion_lines do
|
||||||
|
suggestion_lines[i] = indent .. suggestion_lines[i]
|
||||||
|
end
|
||||||
|
|
||||||
if #suggestion_lines == 0 then return end
|
if #suggestion_lines == 0 then return end
|
||||||
|
|
||||||
-- Get current line info
|
-- Show ghost text
|
||||||
local line = vim.api.nvim_win_get_cursor(0)[1] - 1
|
vim.api.nvim_buf_set_extmark(0, current_suggestion.namespace_id, line, start_col, {
|
||||||
local line_text = vim.api.nvim_buf_get_lines(0, line, line + 1, true)[1]
|
virt_text = {{suggestion_lines[1], 'GeminiSuggestion'}},
|
||||||
|
virt_text_pos = 'overlay',
|
||||||
-- Validate start_col
|
hl_mode = 'combine',
|
||||||
if not line_text then return end
|
|
||||||
start_col = math.min(start_col, #line_text)
|
|
||||||
if start_col < 0 then start_col = 0 end
|
|
||||||
|
|
||||||
-- Get text before cursor on current line
|
|
||||||
local input_before_cursor = string.sub(line_text, 1, start_col)
|
|
||||||
local first_line = suggestion_lines[1]
|
|
||||||
|
|
||||||
-- If the suggestion starts with what's already typed, remove that part
|
|
||||||
if vim.startswith(first_line, input_before_cursor) then
|
|
||||||
first_line = string.sub(first_line, #input_before_cursor + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if first_line == "" and #suggestion_lines == 1 then return end
|
|
||||||
|
|
||||||
current_suggestion.text = suggestion
|
|
||||||
current_suggestion.start_col = start_col
|
|
||||||
|
|
||||||
-- Show first line as virtual text
|
|
||||||
if first_line ~= "" then
|
|
||||||
-- Ensure we're not exceeding line length
|
|
||||||
local safe_col = math.min(start_col, #line_text)
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_extmark(0, current_suggestion.namespace_id, line, safe_col, {
|
|
||||||
virt_text = {{first_line, 'GeminiSuggestion'}},
|
|
||||||
virt_text_pos = 'inline',
|
|
||||||
virt_text_hide = true,
|
|
||||||
})
|
})
|
||||||
end
|
|
||||||
|
|
||||||
-- Show remaining lines as virtual lines
|
-- Show remaining lines as virtual lines
|
||||||
if #suggestion_lines > 1 then
|
if #suggestion_lines > 1 then
|
||||||
@ -98,16 +101,14 @@ local function show_suggestion(suggestion, start_col)
|
|||||||
table.insert(virt_lines, {{suggestion_lines[i], 'GeminiSuggestion'}})
|
table.insert(virt_lines, {{suggestion_lines[i], 'GeminiSuggestion'}})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use safe column position for virtual lines
|
vim.api.nvim_buf_set_extmark(0, current_suggestion.namespace_id, line, start_col, {
|
||||||
local safe_col = math.min(start_col, #line_text)
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_extmark(0, current_suggestion.namespace_id, line, safe_col, {
|
|
||||||
virt_lines = virt_lines,
|
virt_lines = virt_lines,
|
||||||
virt_lines_above = false,
|
virt_lines_above = false,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
debug_print("Showing suggestion: '%s'", first_line)
|
current_suggestion.text = table.concat(suggestion_lines, "\n")
|
||||||
|
current_suggestion.start_col = start_col
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.accept_suggestion()
|
function M.accept_suggestion()
|
||||||
@ -206,66 +207,45 @@ local function get_context_around_cursor(lines, current_line, max_lines)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.trigger_completion()
|
function M.trigger_completion()
|
||||||
debug_print("Triggering completion...")
|
|
||||||
|
|
||||||
-- Clear any existing timer
|
|
||||||
if current_suggestion.timer then
|
if current_suggestion.timer then
|
||||||
vim.fn.timer_stop(current_suggestion.timer)
|
vim.fn.timer_stop(current_suggestion.timer)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set up debounce timer
|
|
||||||
current_suggestion.timer = vim.fn.timer_start(150, function()
|
current_suggestion.timer = vim.fn.timer_start(150, function()
|
||||||
local cursor = vim.api.nvim_win_get_cursor(0)
|
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||||
local line = cursor[1]
|
local line = cursor[1] - 1
|
||||||
local col = cursor[2]
|
local col = cursor[2]
|
||||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
|
||||||
local current_line = lines[line]
|
|
||||||
|
|
||||||
-- Don't trigger if at end of file or empty line
|
-- Get visible context
|
||||||
|
local visible_lines, relative_cursor = get_visible_lines()
|
||||||
|
local current_line = visible_lines[relative_cursor]
|
||||||
|
|
||||||
if not current_line or current_line == "" then
|
if not current_line or current_line == "" then
|
||||||
clear_suggestion()
|
clear_suggestion()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get text before cursor
|
-- Don't trigger in comments or strings
|
||||||
local prefix = string.sub(current_line, 1, col)
|
local syntax_group = vim.fn.synIDattr(vim.fn.synID(cursor[1], col, 1), "name")
|
||||||
|
|
||||||
-- Don't trigger if we're in comments or strings
|
|
||||||
local syntax_group = vim.fn.synIDattr(vim.fn.synID(line, col, 1), "name")
|
|
||||||
if syntax_group:match("Comment") or syntax_group:match("String") then
|
if syntax_group:match("Comment") or syntax_group:match("String") then
|
||||||
debug_print("Skipping: in comment or string")
|
|
||||||
clear_suggestion()
|
clear_suggestion()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check minimum character threshold
|
-- Prepare context for AI
|
||||||
local trimmed_prefix = vim.trim(prefix)
|
local context = table.concat(visible_lines, "\n")
|
||||||
if #trimmed_prefix < current_suggestion.min_chars then
|
|
||||||
debug_print("Skipping: not enough characters")
|
|
||||||
clear_suggestion()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get focused context around cursor
|
|
||||||
local context = get_context_around_cursor(lines, line, current_suggestion.max_context_lines)
|
|
||||||
|
|
||||||
-- Add cursor position marker
|
|
||||||
local cursor_marker = string.rep(" ", col) .. "^"
|
|
||||||
table.insert(context, string.format("L%d (current): %s", line, current_line))
|
|
||||||
table.insert(context, cursor_marker)
|
|
||||||
|
|
||||||
local full_context = table.concat(context, "\n")
|
|
||||||
local file_type = vim.bo.filetype
|
local file_type = vim.bo.filetype
|
||||||
|
|
||||||
-- Improved prompt for better completions
|
local prompt = string.format([[
|
||||||
local prompt = string.format([[In this %s file, complete the code at the cursor (^) position:
|
In this %s file, complete the code at line %d, column %d:
|
||||||
|
|
||||||
%s
|
%s
|
||||||
|
|
||||||
Return ONLY the completion text that would naturally continue from the cursor position.
|
Return ONLY the completion text that would naturally continue from the cursor position.
|
||||||
Focus on completing the current statement or block.
|
Focus on completing the current statement or block.
|
||||||
Consider the syntax, style, and patterns in the surrounding code.
|
Consider the visible context, syntax, and code style.
|
||||||
Do not repeat any text that appears before the cursor.]], file_type, full_context)
|
Do not repeat any text that appears before the cursor.]],
|
||||||
|
file_type, cursor[1], col + 1, context)
|
||||||
|
|
||||||
-- Check cache and rate limiting
|
-- Check cache and rate limiting
|
||||||
local cache_key = get_cache_key(prompt)
|
local cache_key = get_cache_key(prompt)
|
||||||
@ -274,28 +254,19 @@ Do not repeat any text that appears before the cursor.]], file_type, full_contex
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if should_rate_limit() then
|
if should_rate_limit() then return end
|
||||||
debug_print("Rate limited")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get completion from Gemini
|
-- Get completion from Gemini
|
||||||
api.get_response(prompt, nil, function(response, error)
|
api.get_response(prompt, nil, function(response, error)
|
||||||
if error then
|
if error then return end
|
||||||
debug_print("Completion error: %s", error)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(response) == "string" and #response > 0 then
|
if type(response) == "string" and #response > 0 then
|
||||||
-- Clean up response and remove any leading whitespace/indentation
|
|
||||||
response = vim.trim(response)
|
response = vim.trim(response)
|
||||||
response = response:gsub("^%s+", "")
|
|
||||||
current_suggestion.cache[cache_key] = response
|
current_suggestion.cache[cache_key] = response
|
||||||
|
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
-- Verify cursor position hasn't changed significantly
|
|
||||||
local new_cursor = vim.api.nvim_win_get_cursor(0)
|
local new_cursor = vim.api.nvim_win_get_cursor(0)
|
||||||
if new_cursor[1] == line and math.abs(new_cursor[2] - col) <= 1 then
|
if new_cursor[1] == cursor[1] and math.abs(new_cursor[2] - col) <= 1 then
|
||||||
show_suggestion(response, col)
|
show_suggestion(response, col)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -310,12 +281,12 @@ function M.setup()
|
|||||||
vim.api.nvim_set_hl(0, 'GeminiSuggestion', {
|
vim.api.nvim_set_hl(0, 'GeminiSuggestion', {
|
||||||
fg = '#666666',
|
fg = '#666666',
|
||||||
italic = true,
|
italic = true,
|
||||||
|
blend = 15 -- Makes the ghost text slightly transparent
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Map Tab to accept suggestion or behave normally
|
-- Map Tab to accept suggestion
|
||||||
vim.keymap.set('i', '<Tab>', function()
|
vim.keymap.set('i', '<Tab>', function()
|
||||||
if not M.accept_suggestion() then
|
if not M.accept_suggestion() then
|
||||||
-- If no suggestion to accept, send regular Tab key
|
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Tab>', true, true, true), 'n', true)
|
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Tab>', true, true, true), 'n', true)
|
||||||
end
|
end
|
||||||
end, { expr = false, noremap = true })
|
end, { expr = false, noremap = true })
|
||||||
@ -324,7 +295,6 @@ function M.setup()
|
|||||||
vim.api.nvim_create_autocmd("TextChangedI", {
|
vim.api.nvim_create_autocmd("TextChangedI", {
|
||||||
pattern = "*",
|
pattern = "*",
|
||||||
callback = function()
|
callback = function()
|
||||||
-- Only trigger if enabled
|
|
||||||
if vim.b.gemini_completion_enabled then
|
if vim.b.gemini_completion_enabled then
|
||||||
M.trigger_completion()
|
M.trigger_completion()
|
||||||
end
|
end
|
||||||
@ -338,8 +308,6 @@ function M.setup()
|
|||||||
clear_suggestion()
|
clear_suggestion()
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
debug_print("Gemini completion setup complete")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
Loading…
x
Reference in New Issue
Block a user