-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(ghost-text): flickering using autocmds (#255)
* fix(ghost-text): flickering using decoration provider and redraw function * feat: use autocmds for ghost text redraw --------- Co-authored-by: Liam Dyer <[email protected]>
- Loading branch information
1 parent
e9c9b41
commit a94bbaf
Showing
1 changed file
with
62 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,98 @@ | ||
local config = require('blink.cmp.config') | ||
local autocomplete = require('blink.cmp.windows.autocomplete') | ||
local text_edits_lib = require('blink.cmp.accept.text-edits') | ||
local snippets_utils = require('blink.cmp.sources.snippets.utils') | ||
|
||
local ghost_text_config = config.windows.ghost_text | ||
|
||
--- @class blink.cmp.windows.ghost_text | ||
--- @field win integer? | ||
--- @field selected_item blink.cmp.CompletionItem? | ||
--- @field extmark_id integer? | ||
local ghost_text = { | ||
enabled = ghost_text_config and ghost_text_config.enabled, | ||
extmark_id = 1, | ||
ns_id = config.highlight.ns, | ||
win = nil, | ||
selected_item = nil, | ||
} | ||
|
||
--- @param textEdit lsp.TextEdit | ||
local function get_still_untyped_text(textEdit) | ||
local type_text_length = textEdit.range['end'].character - textEdit.range.start.character | ||
local result = textEdit.newText:sub(type_text_length + 1) | ||
return result | ||
end | ||
|
||
function ghost_text.setup() | ||
-- immediately re-draw the preview when the cursor moves/text changes | ||
vim.api.nvim_create_autocmd({ 'CursorMovedI', 'TextChangedI' }, { | ||
callback = function() | ||
if not ghost_text_config.enabled or ghost_text.win == nil then return end | ||
ghost_text.draw_preview(vim.api.nvim_win_get_buf(ghost_text.win)) | ||
end, | ||
}) | ||
|
||
autocomplete.listen_on_select(function(item) | ||
if ghost_text.enabled ~= true then return end | ||
ghost_text.show_preview(item) | ||
if ghost_text_config.enabled then ghost_text.show_preview(item) end | ||
end) | ||
autocomplete.listen_on_close(function() ghost_text.clear_preview() end) | ||
|
||
return ghost_text | ||
end | ||
|
||
--- @param textEdit lsp.TextEdit | ||
local function get_still_untyped_text(textEdit) | ||
local type_text_length = textEdit.range['end'].character - textEdit.range.start.character | ||
local result = textEdit.newText:sub(type_text_length + 1) | ||
return result | ||
end | ||
|
||
--- @param selected_item? blink.cmp.CompletionItem | ||
function ghost_text.show_preview(selected_item) | ||
if selected_item == nil then return end | ||
local text_edits_lib = require('blink.cmp.accept.text-edits') | ||
local text_edit = text_edits_lib.get_from_item(selected_item) | ||
-- nothing to show, clear the preview | ||
if not selected_item then | ||
ghost_text.clear_preview() | ||
return | ||
end | ||
|
||
-- update state and redraw | ||
local changed = ghost_text.selected_item ~= selected_item | ||
ghost_text.selected_item = selected_item | ||
ghost_text.win = vim.api.nvim_get_current_win() | ||
if changed then ghost_text.draw_preview(vim.api.nvim_win_get_buf(ghost_text.win)) end | ||
end | ||
|
||
if selected_item.insertTextFormat == vim.lsp.protocol.InsertTextFormat.Snippet then | ||
local expanded_snippet = require('blink.cmp.sources.snippets.utils').safe_parse(text_edit.newText) | ||
function ghost_text.clear_preview() | ||
ghost_text.selected_item = nil | ||
ghost_text.win = nil | ||
if ghost_text.extmark_id ~= nil then | ||
vim.api.nvim_buf_del_extmark(0, config.highlight.ns, ghost_text.extmark_id) | ||
ghost_text.extmark_id = nil | ||
end | ||
end | ||
|
||
function ghost_text.draw_preview(bufnr) | ||
if not ghost_text.selected_item then return end | ||
|
||
local text_edit = text_edits_lib.get_from_item(ghost_text.selected_item) | ||
|
||
if ghost_text.selected_item.insertTextFormat == vim.lsp.protocol.InsertTextFormat.Snippet then | ||
local expanded_snippet = snippets_utils.safe_parse(text_edit.newText) | ||
text_edit.newText = expanded_snippet and tostring(expanded_snippet) or text_edit.newText | ||
end | ||
|
||
local display_lines = vim.split(get_still_untyped_text(text_edit), '\n', { plain = true }) or {} | ||
|
||
--- @type vim.api.keyset.set_extmark | ||
local extmark = { | ||
id = ghost_text.extmark_id, | ||
virt_text_pos = 'inline', | ||
virt_text = { { display_lines[1], 'BlinkCmpGhostText' } }, | ||
hl_mode = 'combine', | ||
} | ||
|
||
local virt_lines = {} | ||
if #display_lines > 1 then | ||
extmark.virt_lines = {} | ||
for i = 2, #display_lines do | ||
extmark.virt_lines[i - 1] = { { display_lines[i], 'BlinkCmpGhostText' } } | ||
virt_lines[i - 1] = { { display_lines[i], 'BlinkCmpGhostText' } } | ||
end | ||
end | ||
|
||
local cursor_pos = { | ||
text_edit.range.start.line, | ||
text_edit.range['end'].character, | ||
} | ||
vim.api.nvim_buf_set_extmark(0, ghost_text.ns_id, cursor_pos[1], cursor_pos[2], extmark) | ||
end | ||
|
||
function ghost_text.clear_preview() vim.api.nvim_buf_del_extmark(0, ghost_text.ns_id, ghost_text.extmark_id) end | ||
ghost_text.extmark_id = vim.api.nvim_buf_set_extmark(bufnr, config.highlight.ns, cursor_pos[1], cursor_pos[2], { | ||
id = ghost_text.extmark_id, | ||
virt_text_pos = 'inline', | ||
virt_text = { { display_lines[1], 'BlinkCmpGhostText' } }, | ||
virt_lines = virt_lines, | ||
hl_mode = 'combine', | ||
}) | ||
end | ||
|
||
return ghost_text |