From 10557a6fcf7c070909e8399cbc14b14245a69696 Mon Sep 17 00:00:00 2001 From: Jonas Widen Date: Sun, 16 Mar 2025 21:51:06 +0100 Subject: [PATCH] Added completion --- lua/gemini/completion.lua | 115 ++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/lua/gemini/completion.lua b/lua/gemini/completion.lua index cbcdecd..0374c29 100644 --- a/lua/gemini/completion.lua +++ b/lua/gemini/completion.lua @@ -1,15 +1,21 @@ local api = require("gemini.api") local M = {} --- State for managing current suggestion +-- State for managing current suggestion and debounce timer local current_suggestion = { text = nil, start_col = nil, - namespace_id = vim.api.nvim_create_namespace('gemini_suggestion') + namespace_id = vim.api.nvim_create_namespace('gemini_suggestion'), + timer = nil } --- Helper function to clear current suggestion +-- Helper function to clear suggestion local function clear_suggestion() + if current_suggestion.timer then + vim.fn.timer_stop(current_suggestion.timer) + current_suggestion.timer = nil + end + if current_suggestion.text then vim.api.nvim_buf_clear_namespace(0, current_suggestion.namespace_id, 0, -1) current_suggestion.text = nil @@ -39,8 +45,8 @@ local function show_suggestion(suggestion, start_col) -- Make the text virtual (doesn't affect actual buffer content) vim.api.nvim_buf_set_extmark(0, current_suggestion.namespace_id, line, start_col, { virt_text = {{suggestion, 'GeminiSuggestion'}}, - virt_text_pos = 'inline', -- Changed from 'overlay' to 'inline' - virt_text_hide = true, -- Hide when there's other virtual text + virt_text_pos = 'inline', + virt_text_hide = true, }) end @@ -72,47 +78,65 @@ function M.accept_suggestion() end function M.trigger_completion() - local cursor = vim.api.nvim_win_get_cursor(0) - local line = cursor[1] - local col = cursor[2] - - -- Get entire buffer content - local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) - - -- Split into before and after cursor - local before_lines = {} - local after_lines = {} - - -- Copy lines before cursor - for i = 1, line - 1 do - table.insert(before_lines, lines[i]) + -- Clear any existing timer + if current_suggestion.timer then + vim.fn.timer_stop(current_suggestion.timer) end - -- Add current line up to cursor - local current_line = lines[line] - table.insert(before_lines, string.sub(current_line, 1, col)) - -- Combine all lines before cursor - local prefix = table.concat(before_lines, "\n") - - -- Construct prompt for Gemini - local prompt = string.format( - "Complete this code. Only provide the completion, no explanation:\n%s", - prefix - ) - - -- Get completion from Gemini - api.get_response(prompt, nil, function(response, error) - if error then return end + -- Start a new timer for debouncing + current_suggestion.timer = vim.fn.timer_start(100, function() + local cursor = vim.api.nvim_win_get_cursor(0) + local line = cursor[1] + local col = cursor[2] - if type(response) == "string" then - -- Get first non-empty line as suggestion - local suggestion = response:match("^%s*(.-)%s*$") - if suggestion and #suggestion > 0 then - vim.schedule(function() - show_suggestion(suggestion, col) - end) - end + -- Get entire buffer content + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + local current_line = lines[line] + + -- Don't trigger completion if line is empty or cursor is at the start + if col == 0 or current_line:match("^%s*$") then + clear_suggestion() + return end + + -- Split into before and after cursor + local before_lines = {} + + -- Copy lines before current line + for i = 1, line - 1 do + table.insert(before_lines, lines[i]) + end + + -- Add current line (complete line for context) + table.insert(before_lines, current_line) + + -- Combine all lines + local prefix = table.concat(before_lines, "\n") + + -- Construct prompt for Gemini + local prompt = string.format( + "Complete this code. Only provide the completion, no explanation:\n%s", + prefix + ) + + -- Get completion from Gemini + api.get_response(prompt, nil, function(response, error) + if error then return end + + if type(response) == "string" then + -- Get first non-empty line as suggestion + local suggestion = response:match("^%s*(.-)%s*$") + if suggestion and #suggestion > 0 then + vim.schedule(function() + -- Check if cursor position is still the same + local new_cursor = vim.api.nvim_win_get_cursor(0) + if new_cursor[1] == line and new_cursor[2] == col then + show_suggestion(suggestion, col) + end + end) + end + end + end) end) end @@ -132,6 +156,13 @@ function M.setup() end end, { expr = false, noremap = true }) + -- Set up autocommand for real-time completion + vim.api.nvim_create_autocmd("TextChangedI", { + callback = function() + M.trigger_completion() + end + }) + -- Clear suggestion on cursor move vim.api.nvim_create_autocmd({'CursorMovedI', 'CursorMoved'}, { callback = function()