Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ vim.keymap.set("n", "<esc>", function()
end, { desc = "Clear Copilot suggestion or fallback" })
```


#### Restoring previous suggestions

The history automatically stores the last 2 suggestions. Each time you call restore, it cycles to the next previous suggestion. When you reach the end, it wraps back to the most recent one.

You can restore and cycle through the last 2 suggestions:
```lua
-- Restore previous suggestions (cycles through last 2)
vim.keymap.set("n", "<leader>cr", function()
require('copilot-lsp.nes').restore_suggestion()
end, { desc = "Restore previous Copilot suggestion" })
```


## Default Configuration


Expand Down
13 changes: 13 additions & 0 deletions lua/copilot-lsp/nes/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,17 @@ function M.clear()
return false
end

--- Restore the last suggestion from history
---@param bufnr? integer
---@return boolean -- true if suggestion was restored, false otherwise
function M.restore_suggestion(bufnr)
bufnr = bufnr and bufnr > 0 and bufnr or vim.api.nvim_get_current_buf()
if not nes_ui.has_history(bufnr) then
return false
end
-- Set flag to indicate this is a restoration, not a new suggestion
vim.b[bufnr].copilotlsp_nes_restoring = true
return nes_ui.restore_suggestion(bufnr, nes_ns)
end

return M
85 changes: 77 additions & 8 deletions lua/copilot-lsp/nes/ui.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
local M = {}
local config = require("copilot-lsp.config").config

local buffer_histories = {}

---@param bufnr integer
---@param ns_id integer
local function _dismiss_suggestion(bufnr, ns_id)
pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns_id, 0, -1)
end

---@private
---@param bufnr integer
---@param state copilotlsp.InlineEdit
local function _store_suggestion_history(bufnr, state)
if not buffer_histories[bufnr] then
buffer_histories[bufnr] = vim.ringbuf(2)
end
buffer_histories[bufnr]:push(vim.deepcopy(state))
end

---@private
---@param bufnr integer
local function _clear_suggestion_history(bufnr)
buffer_histories[bufnr] = nil
vim.b[bufnr].copilotlsp_nes_restore_index = nil
end

---@param bufnr? integer
---@param ns_id integer
function M.clear_suggestion(bufnr, ns_id)
bufnr = bufnr and bufnr > 0 and bufnr or vim.api.nvim_get_current_buf()
-- Validate buffer exists before accessing buffer-scoped variables
if not vim.api.nvim_buf_is_valid(bufnr) then
return
end
Expand All @@ -21,18 +39,57 @@ function M.clear_suggestion(bufnr, ns_id)
end
_dismiss_suggestion(bufnr, ns_id)
---@type copilotlsp.InlineEdit
local state = vim.b[bufnr].nes_state
if not state then
return
end

-- Clear buffer variables
vim.b[bufnr].nes_state = nil
vim.b[bufnr].copilotlsp_nes_cursor_moves = nil
vim.b[bufnr].copilotlsp_nes_last_line = nil
vim.b[bufnr].copilotlsp_nes_last_col = nil
end

--- Check if there's history for a buffer
---@param bufnr integer
---@return boolean
function M.has_history(bufnr)
local history = buffer_histories[bufnr]
if not history then
return false
end
-- Check if ringbuf has any items
local item = history:peek()
return item ~= nil
end

---@param bufnr? integer
---@param ns_id integer
---@return boolean -- true if suggestion was restored, false otherwise
function M.restore_suggestion(bufnr, ns_id)
bufnr = bufnr and bufnr > 0 and bufnr or vim.api.nvim_get_current_buf()
if not vim.api.nvim_buf_is_valid(bufnr) then
return false
end
local history = buffer_histories[bufnr]
if not history then
return false
end
local suggestion = history:pop()
if not suggestion then
return false
end
-- Validate suggestion is still applicable
local start_line = suggestion.range.start.line
if start_line >= vim.api.nvim_buf_line_count(bufnr) then
_clear_suggestion_history(bufnr)
return false
end
_dismiss_suggestion(bufnr, ns_id)
local preview = M._calculate_preview(bufnr, suggestion)
M._display_preview(bufnr, ns_id, preview)
vim.b[bufnr].nes_state = suggestion
vim.b[bufnr].copilotlsp_nes_namespace_id = ns_id
vim.b[bufnr].copilotlsp_nes_cursor_moves = 1
history:push(suggestion)
return true
end

---@private
---@param bufnr integer
---@param edit lsp.TextEdit
Expand Down Expand Up @@ -166,10 +223,11 @@ end
---@param ns_id integer
---@param edits copilotlsp.InlineEdit[]
function M._display_next_suggestion(bufnr, ns_id, edits)
M.clear_suggestion(bufnr, ns_id)
if not edits or #edits == 0 then
return
end
-- Clear current suggestion first
M.clear_suggestion(bufnr, ns_id)

local suggestion = edits[1]
local preview = M._calculate_preview(bufnr, suggestion)
Expand All @@ -179,6 +237,10 @@ function M._display_next_suggestion(bufnr, ns_id, edits)
vim.b[bufnr].copilotlsp_nes_namespace_id = ns_id
vim.b[bufnr].copilotlsp_nes_cursor_moves = 1

-- Store this suggestion in history immediately after displaying it
_store_suggestion_history(bufnr, suggestion)
vim.b[bufnr].copilotlsp_nes_restore_index = 0

vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
buffer = bufnr,
callback = function()
Expand Down Expand Up @@ -276,4 +338,11 @@ function M._display_next_suggestion(bufnr, ns_id, edits)
})
end

-- Clean up history when buffer is deleted
vim.api.nvim_create_autocmd("BufDelete", {
callback = function(ev)
buffer_histories[ev.buf] = nil
end,
})

return M
Loading
Loading