diff --git a/README.md b/README.md index 80b0485..e156963 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,12 @@ Specify preselect mode. The following modes are available. Default: `cmp.PreselectMode.Item` +#### experimental.ghost_text (type: boolean) + +Specify whether to display ghost text. + +Default: `false` + Programatic API ==================== diff --git a/lua/cmp/config/default.lua b/lua/cmp/config/default.lua index 39fe287..19535ce 100644 --- a/lua/cmp/config/default.lua +++ b/lua/cmp/config/default.lua @@ -61,6 +61,10 @@ return function() end }, + experimental = { + ghost_text = false, + }, + sources = {}, } end diff --git a/lua/cmp/core.lua b/lua/cmp/core.lua index 39aed11..db59028 100644 --- a/lua/cmp/core.lua +++ b/lua/cmp/core.lua @@ -1,5 +1,6 @@ local debug = require('cmp.utils.debug') local char = require('cmp.utils.char') +local str = require('cmp.utils.str') local pattern = require('cmp.utils.pattern') local async = require('cmp.utils.async') local keymap = require('cmp.utils.keymap') @@ -18,6 +19,16 @@ core.SOURCE_TIMEOUT = 500 ---Suspending state. 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 core.menu = menu.new({ on_select = function(e) @@ -27,6 +38,43 @@ core.menu = menu.new({ 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 core.sources = {} diff --git a/lua/cmp/menu.lua b/lua/cmp/menu.lua index 088c3ba..1e7e7e5 100644 --- a/lua/cmp/menu.lua +++ b/lua/cmp/menu.lua @@ -193,58 +193,39 @@ end ---Geta current active entry ---@return cmp.Entry|nil menu.get_active_entry = function(self) - local completed_item = vim.v.completed_item or {} - if vim.fn.pumvisible() == 0 or not completed_item.user_data then + if vim.fn.pumvisible() == 0 or not (vim.v.completed_item or {}).user_data then return nil end - - local id = completed_item.user_data.cmp - if id then - return self.entry_map[id] - end - return nil + return self:get_selected_entry() end ---Get current selected entry ---@return cmp.Entry|nil menu.get_selected_entry = function(self) - local e = self:get_active_entry() - if e then - return e + if not self:is_valid_mode() then + return nil end local selected = vim.fn.complete_info({ 'selected' }).selected if selected == -1 then return nil end - local items = vim.fn.complete_info({ 'items' }).items - - 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 + return self.entries[math.max(selected, 0) + 1] end ---Get first entry ---@param self cmp.Entry|nil menu.get_first_entry = function(self) - local info = vim.fn.complete_info({ 'items' }) - local completed_item = info.items[1] or {} - if not completed_item.user_data then + if not self:is_valid_mode() then return nil end + return self.entries[1] +end - local id = completed_item.user_data.cmp - if id then - return self.entry_map[id] - end - return nil +---Return the completion menu is visible or not. +---@return boolean +menu.is_valid_mode = function() + return vim.fn.complete_info({ 'mode' }).mode == 'eval' end return menu diff --git a/lua/cmp/types/cmp.lua b/lua/cmp/types/cmp.lua index 86128fc..d013849 100644 --- a/lua/cmp/types/cmp.lua +++ b/lua/cmp/types/cmp.lua @@ -57,6 +57,7 @@ cmp.PreselectMode.None = 'none' ---@field public event cmp.EventConfig ---@field public mapping table ---@field public sources cmp.SourceConfig[] +---@field public experimental cmp.ExperimentalConfig ---@class cmp.CompletionConfig ---@field public autocomplete cmp.TriggerEvent[] @@ -88,6 +89,9 @@ cmp.PreselectMode.None = 'none' ---@class cmp.EventConfig ---@field on_confirm_done function(e: cmp.Entry) +---@class cmp.ExperimentalConfig +---@field public ghost_text boolean + ---@class cmp.SourceConfig ---@field public name string ---@field public opts table