Experimental: Ghost text (#119)
* Inline suggest experimental * Fix flicker * Fix ci fails * Reduce flicker * Improve menu functions * Default false * README.md * Add require * Rename inline_preview to ghost_text
This commit is contained in:
@@ -354,6 +354,12 @@ Specify preselect mode. The following modes are available.
|
|||||||
|
|
||||||
Default: `cmp.PreselectMode.Item`
|
Default: `cmp.PreselectMode.Item`
|
||||||
|
|
||||||
|
#### experimental.ghost_text (type: boolean)
|
||||||
|
|
||||||
|
Specify whether to display ghost text.
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
|
||||||
Programatic API
|
Programatic API
|
||||||
====================
|
====================
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ return function()
|
|||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
|
experimental = {
|
||||||
|
ghost_text = false,
|
||||||
|
},
|
||||||
|
|
||||||
sources = {},
|
sources = {},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
local debug = require('cmp.utils.debug')
|
local debug = require('cmp.utils.debug')
|
||||||
local char = require('cmp.utils.char')
|
local char = require('cmp.utils.char')
|
||||||
|
local str = require('cmp.utils.str')
|
||||||
local pattern = require('cmp.utils.pattern')
|
local pattern = require('cmp.utils.pattern')
|
||||||
local async = require('cmp.utils.async')
|
local async = require('cmp.utils.async')
|
||||||
local keymap = require('cmp.utils.keymap')
|
local keymap = require('cmp.utils.keymap')
|
||||||
@@ -18,6 +19,16 @@ core.SOURCE_TIMEOUT = 500
|
|||||||
---Suspending state.
|
---Suspending state.
|
||||||
core.suspending = false
|
core.suspending = false
|
||||||
|
|
||||||
|
core.GHOST_TEXT_NS = vim.api.nvim_create_namespace('cmp:GHOST_TEXT');
|
||||||
|
|
||||||
|
vim.api.nvim_set_decoration_provider(core.GHOST_TEXT_NS, {
|
||||||
|
on_win = function()
|
||||||
|
if config.get().experimental.ghost_text then
|
||||||
|
core.ghost_text(core.menu:get_first_entry())
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
---@type cmp.Menu
|
---@type cmp.Menu
|
||||||
core.menu = menu.new({
|
core.menu = menu.new({
|
||||||
on_select = function(e)
|
on_select = function(e)
|
||||||
@@ -27,6 +38,43 @@ core.menu = menu.new({
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
---Show ghost text if possible
|
||||||
|
---@param e cmp.Entry
|
||||||
|
core.ghost_text = function(e)
|
||||||
|
if not e then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local ctx = context.new()
|
||||||
|
if ctx.cursor_after_line ~= '' then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local diff = ctx.cursor.col - e:get_offset()
|
||||||
|
local text = e:get_insert_text()
|
||||||
|
if e.completion_item.insertTextFormat == types.lsp.InsertTextFormat.Snippet then
|
||||||
|
text = vim.lsp.util.parse_snippet(text)
|
||||||
|
end
|
||||||
|
text = string.sub(str.oneline(text), diff + 1)
|
||||||
|
if #text > 0 then
|
||||||
|
vim.api.nvim_buf_set_extmark(
|
||||||
|
ctx.bufnr,
|
||||||
|
core.GHOST_TEXT_NS,
|
||||||
|
ctx.cursor.row - 1,
|
||||||
|
ctx.cursor.col - 1,
|
||||||
|
{
|
||||||
|
right_gravity = true,
|
||||||
|
virt_text = { { text, 'Comment' } },
|
||||||
|
virt_text_pos = 'overlay',
|
||||||
|
virt_text_win_col = ctx.cursor.col - 1,
|
||||||
|
hl_mode = 'blend',
|
||||||
|
priority = 0,
|
||||||
|
ephemeral = true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
---@type table<number, cmp.Source>
|
---@type table<number, cmp.Source>
|
||||||
core.sources = {}
|
core.sources = {}
|
||||||
|
|
||||||
|
|||||||
@@ -193,58 +193,39 @@ end
|
|||||||
---Geta current active entry
|
---Geta current active entry
|
||||||
---@return cmp.Entry|nil
|
---@return cmp.Entry|nil
|
||||||
menu.get_active_entry = function(self)
|
menu.get_active_entry = function(self)
|
||||||
local completed_item = vim.v.completed_item or {}
|
if vim.fn.pumvisible() == 0 or not (vim.v.completed_item or {}).user_data then
|
||||||
if vim.fn.pumvisible() == 0 or not completed_item.user_data then
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
return self:get_selected_entry()
|
||||||
local id = completed_item.user_data.cmp
|
|
||||||
if id then
|
|
||||||
return self.entry_map[id]
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get current selected entry
|
---Get current selected entry
|
||||||
---@return cmp.Entry|nil
|
---@return cmp.Entry|nil
|
||||||
menu.get_selected_entry = function(self)
|
menu.get_selected_entry = function(self)
|
||||||
local e = self:get_active_entry()
|
if not self:is_valid_mode() then
|
||||||
if e then
|
return nil
|
||||||
return e
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local selected = vim.fn.complete_info({ 'selected' }).selected
|
local selected = vim.fn.complete_info({ 'selected' }).selected
|
||||||
if selected == -1 then
|
if selected == -1 then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local items = vim.fn.complete_info({ 'items' }).items
|
return self.entries[math.max(selected, 0) + 1]
|
||||||
|
|
||||||
local completed_item = items[math.max(selected, 0) + 1] or {}
|
|
||||||
if not completed_item.user_data then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local id = completed_item.user_data.cmp
|
|
||||||
if id then
|
|
||||||
return self.entry_map[id]
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get first entry
|
---Get first entry
|
||||||
---@param self cmp.Entry|nil
|
---@param self cmp.Entry|nil
|
||||||
menu.get_first_entry = function(self)
|
menu.get_first_entry = function(self)
|
||||||
local info = vim.fn.complete_info({ 'items' })
|
if not self:is_valid_mode() then
|
||||||
local completed_item = info.items[1] or {}
|
|
||||||
if not completed_item.user_data then
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
return self.entries[1]
|
||||||
|
end
|
||||||
|
|
||||||
local id = completed_item.user_data.cmp
|
---Return the completion menu is visible or not.
|
||||||
if id then
|
---@return boolean
|
||||||
return self.entry_map[id]
|
menu.is_valid_mode = function()
|
||||||
end
|
return vim.fn.complete_info({ 'mode' }).mode == 'eval'
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ cmp.PreselectMode.None = 'none'
|
|||||||
---@field public event cmp.EventConfig
|
---@field public event cmp.EventConfig
|
||||||
---@field public mapping table<string, fun(core: cmp.Core, fallback: function)>
|
---@field public mapping table<string, fun(core: cmp.Core, fallback: function)>
|
||||||
---@field public sources cmp.SourceConfig[]
|
---@field public sources cmp.SourceConfig[]
|
||||||
|
---@field public experimental cmp.ExperimentalConfig
|
||||||
|
|
||||||
---@class cmp.CompletionConfig
|
---@class cmp.CompletionConfig
|
||||||
---@field public autocomplete cmp.TriggerEvent[]
|
---@field public autocomplete cmp.TriggerEvent[]
|
||||||
@@ -88,6 +89,9 @@ cmp.PreselectMode.None = 'none'
|
|||||||
---@class cmp.EventConfig
|
---@class cmp.EventConfig
|
||||||
---@field on_confirm_done function(e: cmp.Entry)
|
---@field on_confirm_done function(e: cmp.Entry)
|
||||||
|
|
||||||
|
---@class cmp.ExperimentalConfig
|
||||||
|
---@field public ghost_text boolean
|
||||||
|
|
||||||
---@class cmp.SourceConfig
|
---@class cmp.SourceConfig
|
||||||
---@field public name string
|
---@field public name string
|
||||||
---@field public opts table
|
---@field public opts table
|
||||||
|
|||||||
Reference in New Issue
Block a user