Skip to content

Commit

Permalink
feat: add ghost text
Browse files Browse the repository at this point in the history
closes #8
  • Loading branch information
max397574 committed Jun 23, 2024
1 parent 9491261 commit 18ffde7
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 11 deletions.
32 changes: 26 additions & 6 deletions docs/config.norg
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ version: 1.1.1
---@field snippet_expansion fun(string): nil
@end

** Selection behavior
With the selection behavior the user can determine what happens when selecting an entry. This can
either be `select` or `insert`. Selecting will just select the entry and do nothing else. Insert
will actually insert the text of the entry (this is not necessarily the whole text).

#tangle
@code lua
--- Behavior when selecting entry
---@field selection_behavior "select"|"insert"
@end

** Keyword pattern
Pattern used to determine keywords, used to determine what to use for filtering and what to
remove if insert text is used.
Expand Down Expand Up @@ -104,6 +115,15 @@ version: 1.1.1
---@field type_icons neocomplete.config.ui.type_icons
@end

*** Ghost text
with this option the user can determine if ghost text should be displayed. Ghost text is just
virtual text which shows a preview of the entry.
#tangle
@code lua
--- Whether to show ghost text
---@field ghost_text boolean
@end

** Menu
This configuration should allow you to completely adapt the completion menu to your likings.
#tangle
Expand Down Expand Up @@ -144,12 +164,12 @@ version: 1.1.1
You could do that like this:
@code lua
format_entry = function(entry)
return {
-- The first section with the two chunks for the label and the icon
{ { entry.label .. " ", "MyRedHlGroup" }, { entry.kind, "HighlightKind" .. entry.kind } }
-- The second section for the source
{ { entry.source, "MyBlueHlGroup" } }
}
return {
-- The first section with the two chunks for the label and the icon
{ { entry.label .. " ", "MyRedHlGroup" }, { entry.kind, "HighlightKind" .. entry.kind } }
-- The second section for the source
{ { entry.source, "MyBlueHlGroup" } }
}
end,
alignment = { "left", "right" }
@end
Expand Down
15 changes: 13 additions & 2 deletions docs/entry.norg
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ categories: [
types
]
created: 2023-11-15T17:42:46+0100
updated: 2024-06-18T19:19:29+0100
updated: 2024-06-23T14:32:14+0100
tangle: {
languages: {
lua: ../lua/neocomplete/types/entry.lua
Expand Down Expand Up @@ -39,7 +39,7 @@ version: 1.1.1
---@field new fun(completion_item: lsp.CompletionItem, source: neocomplete.internal_source): neocomplete.entry
@end

* Get insert text
** Get insert text
This function is used to get the text that will be inserted for the entry. This is important for
the ghost text.
#tangle
Expand All @@ -48,6 +48,17 @@ version: 1.1.1
---@field get_insert_text fun(self: neocomplete.entry): string
@end

** Get insert word
This function is used to get part of the text that will be inserted for the entry. It just uses
a pattern to match the insert text and get the beginning of it which matches a vim `word`. This
is often e.g. the method name but without the parentheses and parameter names. That function is
used for the `insert` selection behavior.
#tangle
@code lua
--- Get insert word
---@field get_insert_word fun(self: neocomplete.entry): string
@end

* Fields
** Source
This is the source from which the entry came. This is important for using the right keyword
Expand Down
2 changes: 2 additions & 0 deletions lua/neocomplete/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ config.defaults = {
Value = " 󰎠 ",
Variable = "",
},
ghost_text = true,
},
snippet_expansion = function(snippet_body)
vim.snippet.expand(snippet_body)
end,
selection_behavior = "select",
keyword_pattern = [[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]],
enabled = function()
local enabled = true
Expand Down
11 changes: 11 additions & 0 deletions lua/neocomplete/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ function core.new()
local self = setmetatable({}, { __index = core })
self.context = require("neocomplete.context").new()
self.menu = require("neocomplete.menu").new()
self.blocked = false
return self
end

Expand Down Expand Up @@ -51,7 +52,17 @@ function core.setup(self)
})
end

function core:block()
self.blocked = true
return vim.schedule_wrap(function()
self.blocked = false
end)
end

function core.on_change(self)
if self.blocked then
return
end
self.context = require("neocomplete.context").new(self.context)
if not self.context:changed() then
return
Expand Down
18 changes: 18 additions & 0 deletions lua/neocomplete/entry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,22 @@ function Entry:get_insert_text()
return text
end

function Entry:get_insert_word()
local completion_item = self.completion_item
local text
if completion_item.textEdit then
text = completion_item.textEdit.newText
elseif completion_item.insertText then
text = completion_item.insertText
else
text = completion_item.label
end
-- Snippet
if completion_item.insertTextFormat == 2 then
text = vim.fn.matchstrpos(text, [[^\w\+]])[1]
end
---@type string
return text
end

return Entry
41 changes: 41 additions & 0 deletions lua/neocomplete/ghost_text.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
local ghost_text = {}

local ns = vim.api.nvim_create_namespace("neocomplete.ghost_text")

---@param entry neocomplete.entry
---@param offset integer
function ghost_text.show(entry, offset)
-- TODO: allow multiline text
vim.api.nvim_buf_clear_namespace(vim.api.nvim_get_current_buf(), ns, 0, -1)
local cursor = vim.api.nvim_win_get_cursor(0)
local select_behavior = require("neocomplete.config").options.selection_behavior
if select_behavior == "select" then
local text = entry:get_insert_text()
local text_after_filter = text:sub(offset + 1)
vim.api.nvim_buf_set_extmark(vim.api.nvim_get_current_buf(), ns, cursor[1] - 1, cursor[2], {
virt_text = { { text_after_filter, "@neocomplete.ghost_text" } },
virt_text_pos = "inline",
})
elseif select_behavior == "select" then
vim.o.ul = vim.o.ul
-- TODO: allow going back to original (filter) text
local word = entry:get_insert_word()
local unblock = require("neocomplete").core:block()
-- vim.api.nvim_buf_set_text(0, cursor[1] - 1, cursor[2] - offset, cursor[1] - 1, cursor[2], { word })
-- vim.api.nvim_win_set_cursor(0, { cursor[1], cursor[2] - offset + #word })
vim.api.nvim_feedkeys(vim.keycode(string.rep("<BS>", offset) .. word), "i", false)
unblock()
local text = entry:get_insert_text()
local text_after_word = text:sub(#word + 1)
vim.api.nvim_buf_set_extmark(vim.api.nvim_get_current_buf(), ns, cursor[1] - 1, cursor[2], {
virt_text = { { text_after_word, "@neocomplete.ghost_text" } },
virt_text_pos = "inline",
})
end
end

function ghost_text.hide()
vim.api.nvim_buf_clear_namespace(vim.api.nvim_get_current_buf(), ns, 0, -1)
end

return ghost_text
3 changes: 2 additions & 1 deletion lua/neocomplete/highlights.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ hl("@neocomplete.selected", { link = "Visual", default = true })
hl("@neocomplete.match", { link = "Special", default = true })
hl("@neocomplete.menu", { link = "NormalFloat", default = true })
hl("@neocomplete.scrollbar", { link = "PmenuSbar", default = true })
hl("@neocomplete.entry", { italic = true })
hl("@neocomplete.entry", { italic = true, default = true })
hl("@neocomplete.ghost_text", { link = "Comment", default = true })
22 changes: 21 additions & 1 deletion lua/neocomplete/menu/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ function Menu:close()
-- TODO: reset more things?
pcall(vim.api.nvim_win_close, self.winnr, true)
pcall(vim.api.nvim_win_close, self.scrollbar.win, true)
require("neocomplete.ghost_text").hide()
Menu.winnr = nil
end

Expand All @@ -96,7 +97,6 @@ function Menu:set_scroll(direction)
local bottom_visible = vim.fn.line("w$", self.winnr)
local visible_amount = bottom_visible - top_visible + 1
local selected_line = self.index
print(visible_amount)
if selected_line == 0 then
scroll_to_line(1)
return
Expand All @@ -119,6 +119,26 @@ function Menu:select_next(count)
end
self:set_scroll(1)
self:draw()
local line = vim.api.nvim_get_current_line()
local cursor = vim.api.nvim_win_get_cursor(0)
local cursor_col = cursor[2]
local line_to_cursor = line:sub(1, cursor_col)
local entry = self:get_active_entry()
if not entry then
return
end
local word_boundary = vim.fn.match(line_to_cursor, entry.source:get_keyword_pattern() .. "$")

local prefix
if word_boundary == -1 then
prefix = ""
else
prefix = line:sub(word_boundary + 1, cursor_col)
end

if entry and self.config.ui.ghost_text then
require("neocomplete.ghost_text").show(entry, #prefix)
end
end

function Menu:select_prev(count)
Expand Down
6 changes: 5 additions & 1 deletion lua/neocomplete/types/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
---@field ui neocomplete.config.ui
--- Function used to expand snippets
---@field snippet_expansion fun(string): nil
--- Behavior when selecting entry
---@field selection_behavior "select"|"insert"
--- Pattern used to determine keywords
---@field keyword_pattern string
--- Configuration for the ui of neocomplete
Expand All @@ -16,14 +18,16 @@
---@field docs_view neocomplete.config.ui.docs
--- The icons for the different compltion item kinds
---@field type_icons neocomplete.config.ui.type_icons
--- Whether to show ghost text
---@field ghost_text boolean
--- Configuration of the completion menu of neocomplete.nvim
---@class neocomplete.config.ui.menu
--- Maximum height of the menu
---@field max_height integer
--- The border of the completion menu
---@field border string|string[]|string[][]
--- Character used for the scrollbar
---@field scrollbar string
---@field scrollbar string?
--- Position of the menu
---@field position "auto"|"bottom"|"top"
--- How an entry should be formatted
Expand Down
2 changes: 2 additions & 0 deletions lua/neocomplete/types/entry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
---@field new fun(completion_item: lsp.CompletionItem, source: neocomplete.internal_source): neocomplete.entry
--- Get insert text
---@field get_insert_text fun(self: neocomplete.entry): string
--- Get insert word
---@field get_insert_word fun(self: neocomplete.entry): string
--- Source from which the entry came
---@field source neocomplete.internal_source
--- Matches in filter text
Expand Down

0 comments on commit 18ffde7

Please sign in to comment.