From d5fa4f1651956b9298b36eaba1c4b987d0ded27c Mon Sep 17 00:00:00 2001 From: Samir Ettali Date: Sat, 9 Aug 2025 05:24:06 +0200 Subject: [PATCH] feat!: add keymaps to options --- README.md | 69 +++++++++++++++-------------------- lsp/copilot_ls.lua | 41 --------------------- lua/copilot-lsp/config.lua | 15 ++++++++ lua/copilot-lsp/init.lua | 73 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index d3d24a0..8b85aaa 100644 --- a/README.md +++ b/README.md @@ -19,63 +19,52 @@ ## Usage -To use the plugin, add the following to your Neovim configuration: - ```lua -return { +{ "copilotlsp-nvim/copilot-lsp", - init = function() - vim.g.copilot_nes_debounce = 500 - vim.lsp.enable("copilot_ls") - vim.keymap.set("n", "", function() - local bufnr = vim.api.nvim_get_current_buf() - local state = vim.b[bufnr].nes_state - if state then - -- Try to jump to the start of the suggestion edit. - -- If already at the start, then apply the pending suggestion and jump to the end of the edit. - local _ = require("copilot-lsp.nes").walk_cursor_start_edit() - or ( - require("copilot-lsp.nes").apply_pending_nes() - and require("copilot-lsp.nes").walk_cursor_end_edit() - ) - return nil - else - -- Resolving the terminal's inability to distinguish between `TAB` and `` in normal mode - return "" - end - end, { desc = "Accept Copilot NES suggestion", expr = true }) - end, + opts = {}, } ``` -#### Clearing suggestions with Escape +## Default configuration -You can map the `` key to clear suggestions while preserving its other functionality: +You don't need to configure anything, but you can customize the defaults: `move_count_threshold` is the most important setting - it controls how many cursor moves happen before suggestions are cleared. Higher values make suggestions persist longer. ```lua --- Clear copilot suggestion with Esc if visible, otherwise preserve default Esc behavior -vim.keymap.set("n", "", function() - if not require("copilot-lsp.nes").clear() then - -- fallback to other functionality - end -end, { desc = "Clear Copilot suggestion or fallback" }) +require('copilot-lsp').setup({ + nes = { + auto_trigger = false, + debounce = 500, + move_count_threshold = 3, + distance_threshold = 40, + clear_on_large_distance = true, + count_horizontal_moves = true, + reset_on_approaching = true, + } + keymaps = { + request_nes = nil, + accept_nes = nil, + clear_nes = nil, + }, +}) ``` -## Default Configuration - -### NES (Next Edit Suggestion) Smart Clearing +### Keymap Configuration -You don’t need to configure anything, but you can customize the defaults: -`move_count_threshold` is the most important. It controls how many cursor moves happen before suggestions are cleared. Higher = slower to clear. +**By default, no keymaps are set** to avoid breaking existing configurations, you need to explicitly set them: ```lua require('copilot-lsp').setup({ - nes = { - move_count_threshold = 3, -- Clear after 3 cursor movements - } + keymaps = { + request_nes = "re", + accept_nes = "", + clear_nes = "", + }, }) ``` +When `accept_nes` is set to ``, it will fallback to `` in normal mode when no suggestion is active (resolving the terminal's inability to distinguish between Tab and Ctrl-I). + ### Blink Integration ```lua diff --git a/lsp/copilot_ls.lua b/lsp/copilot_ls.lua index d5ac4c8..ce461f3 100644 --- a/lsp/copilot_ls.lua +++ b/lsp/copilot_ls.lua @@ -29,45 +29,4 @@ return { end, }), root_dir = vim.uv.cwd(), - on_init = function(client) - local au = vim.api.nvim_create_augroup("copilotlsp.init", { clear = true }) - --NOTE: Inline Completions - --TODO: We dont currently use this code path, so comment for now until a UI is built - -- vim.api.nvim_create_autocmd("TextChangedI", { - -- callback = function() - -- inline_completion.request_inline_completion(2) - -- end, - -- group = au, - -- }) - - -- TODO: make this configurable for key maps, or just expose commands to map in config - -- vim.keymap.set("i", "", function() - -- inline_completion.request_inline_completion(1) - -- end) - - --NOTE: NES Completions - local debounced_request = require("copilot-lsp.util").debounce( - require("copilot-lsp.nes").request_nes, - vim.g.copilot_nes_debounce or 500 - ) - vim.api.nvim_create_autocmd({ "TextChangedI", "TextChanged" }, { - callback = function() - debounced_request(client) - end, - group = au, - }) - - --NOTE: didFocus - vim.api.nvim_create_autocmd("BufEnter", { - callback = function() - local td_params = vim.lsp.util.make_text_document_params() - client:notify("textDocument/didFocus", { - textDocument = { - uri = td_params.uri, - }, - }) - end, - group = au, - }) - end, } diff --git a/lua/copilot-lsp/config.lua b/lua/copilot-lsp/config.lua index b4320a1..e08e53f 100644 --- a/lua/copilot-lsp/config.lua +++ b/lua/copilot-lsp/config.lua @@ -1,4 +1,11 @@ +---@class copilotlsp.config.keymaps +---@field request_nes? string? Keymap to request a suggestion +---@field accept_nes? string? Keymap to accept a suggestion +---@field clear_nes? string? Keymap to clear a suggestion + ---@class copilotlsp.config.nes +---@field auto_trigger boolean Whether to automatically trigger suggestions +---@field debounce integer Debounce time in milliseconds ---@field move_count_threshold integer Number of cursor movements before clearing suggestion ---@field distance_threshold integer Maximum line distance before clearing suggestion ---@field clear_on_large_distance boolean Whether to clear suggestion when cursor is far away @@ -9,14 +16,22 @@ local M = {} ---@class copilotlsp.config ---@field nes copilotlsp.config.nes +---@field keymaps copilotlsp.config.keymaps M.defaults = { nes = { + auto_trigger = false, + debounce = 500, move_count_threshold = 3, distance_threshold = 40, clear_on_large_distance = true, count_horizontal_moves = true, reset_on_approaching = true, }, + keymaps = { + request_nes = nil, + accept_nes = nil, + clear_nes = nil, + }, } ---@type copilotlsp.config diff --git a/lua/copilot-lsp/init.lua b/lua/copilot-lsp/init.lua index 19bd0f0..e4c5653 100644 --- a/lua/copilot-lsp/init.lua +++ b/lua/copilot-lsp/init.lua @@ -13,6 +13,79 @@ M.config = config.config function M.setup(opts) config.setup(opts) M.config = config.config + + vim.lsp.config("copilot_ls", { + on_init = function(client) + vim.api.nvim_create_autocmd("BufEnter", { + callback = function() + local td_params = vim.lsp.util.make_text_document_params() + client:notify("textDocument/didFocus", { + textDocument = { + uri = td_params.uri, + }, + }) + end, + group = vim.api.nvim_create_augroup("copilot_ls", { clear = true }), + desc = "Trigger when entering a buffer", + }) + + if M.config.nes.auto_trigger then + local debounced_request = + require("copilot-lsp.util").debounce(require("copilot-lsp.nes").request_nes, M.config.nes.debounce) + vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { + group = vim.api.nvim_create_augroup("copilot-lsp", { clear = true }), + callback = function() + debounced_request(client) + end, + desc = "Debounced request for Copilot NES", + }) + end + + if M.config.keymaps.request_nes then + vim.keymap.set("n", M.config.keymaps.request_nes, function() + require("copilot-lsp.nes").request_nes(client) + end, { desc = "Request Copilot NES" }) + end + + if M.config.keymaps.accept_nes then + local is_tab = M.config.keymaps.accept_nes:lower() == "" + vim.keymap.set("n", M.config.keymaps.accept_nes, function() + local bufnr = vim.api.nvim_get_current_buf() + local state = vim.b[bufnr].nes_state + if state then + local _ = require("copilot-lsp.nes").walk_cursor_start_edit() + or ( + require("copilot-lsp.nes").apply_pending_nes() + and require("copilot-lsp.nes").walk_cursor_end_edit() + ) + return nil + else + -- Only fallback to if the mapped key is + -- This resolves terminal's inability to distinguish between TAB and + if is_tab then + return "" + end + return nil + end + end, { + desc = "Accept Copilot NES", + expr = is_tab, + }) + end + + if M.config.keymaps.clear_nes then + vim.keymap.set("n", M.config.keymaps.clear_nes, function() + if not require("copilot-lsp.nes").clear() then + return M.config.keymaps.clear_nes + end + + return nil + end, { desc = "Clear Copilot NES", expr = true }) + end + end, + }) + + vim.lsp.enable("copilot_ls") end return M