This commit is contained in:
hrsh7th 2022-03-22 01:41:28 +09:00
parent 5cbce79074
commit 86467b2012
5 changed files with 303 additions and 206 deletions

View file

@ -275,16 +275,16 @@ You can also use built-in mapping helpers.
*cmp.mapping.abort* ()
Same as |cmp.abort|
*cmp.mapping.select_next_item* (option: { behavior = cmp.SelectBehavior })
*cmp.mapping.select_next_item* (option: cmp.SelectOption)
Same as |cmp.select_next_item|
*cmp.mapping.select_prev_item* (option: { behavior = cmp.SelectBehavior })
*cmp.mapping.select_prev_item* (option: cmp.SelectOption)
Same as |cmp.select_prev_item|
*cmp.mapping.scroll_docs* (delta: number)
Same as |cmp.scroll_docs|
*cmp.mapping.complete* (option: cmp.CompleteParams)
*cmp.mapping.complete* (option: cmp.CompleteOption)
Same as |cmp.complete|
*cmp.mapping.complete_common_string* ()
@ -376,7 +376,7 @@ mapping~
*cmp-config.snippet.expand*
snippet.expand~
`fun(option: cmp.SnippetExpansionParams)`
`fun(option: { body: string, insert_text_mode: lsp.InsertTextMode })`
The snippet expansion function. You must integrate your snippet engine plugin via this.
*cmp-config.completion.keyword_length*
@ -513,69 +513,28 @@ sources[n].group_index~
<
*cmp-config.view*
view~
`{ entries: cmp.EntriesConfig|string }`
Specify the view class to customize appearance.
`cmp.ViewConfig`
Specify the view class and custom options.
Currently, the possible configurations are:
*cmp-config.window.completion.border*
window.completion.border~
`string | string[] | nil`
Border characters used for the completion popup menu when
|experimental.native_menu| is disabled.
*cmp-config.view.completion*
view.completion~
`cmp.CompletionViewConfig`
Specify the completion menu view settings.
*cmp-config.window.completion.winhighlight*
window.completion.winhighlight~
`string | cmp.WinhighlightConfig`
Is either a `string` matching the 'winhighlight' syntax, or a `table` of
`bordered` and `default` options which both specify 'winhighlight' settings
for when the completion window has and does not have a border, respectively.
*cmp-config.window.completion.zindex*
window.completion.zindex~
`number`
The completion window's zindex.
*cmp-config.window.documentation*
window.documentation~
`false | cmp.DocumentationConfig`
If set to `false`, the documentation of each item will not be shown.
Else, a table representing documentation configuration should be provided.
The following are possible options.
*cmp-config.window.documentation.border*
window.documentation.border~
`string | string[] | nil`
Border characters used for documentation window.
*cmp-config.window.documentation.max_width*
window.documentation.max_width~
`number`
The documentation window's max width.
*cmp-config.window.documentation.max_height*
window.documentation.max_height~
`number`
The documentation window's max height.
*cmp-config.window.documentation.winhighlight*
window.documentation.winhighlight~
`string | cmp.WinhighlightConfig`
Is either a `string` matching the 'winhighlight' syntax, or a `table` of
`bordered` and `default` options which both specify 'winhighlight' settings
for when the documentation window has and does not have a border,
respectively.
*cmp-config.window.documentation.zindex*
window.documentation.zindex~
`number`
The documentation window's zindex.
*cmp-config.view.completion*
view.documentation~
`table`
Specify the view class and custom options.
Currently, the possible configurations are:
*cmp-config.experimental.ghost_text*
experimental.ghost_text~
`boolean | { hl_group = string }`
The boolean value to enable or disable the ghost_text feature.
==============================================================================
Develop *cmp-develop*
@ -592,36 +551,41 @@ NOTE:
You can create custom source like the following example.
>
local source = {}
local Source = {}
---Create source instance.
Source.new = function()
return setmetatable({}, { __index = Source })
end
---Return this source is available in current context or not. (Optional)
---@return boolean
function source:is_available()
function Source:is_available()
return true
end
---Return the debug name of this source. (Optional)
---@return string
function source:get_debug_name()
function Source:get_debug_name()
return 'debug name'
end
---Return keyword pattern for triggering completion. (Optional)
---If this is ommited, nvim-cmp will use default keyword pattern. See |cmp-config.completion.keyword_pattern|
---@return string
function source:get_keyword_pattern()
function Source:get_keyword_pattern()
return [[\k\+]]
end
---Return trigger characters for triggering completion. (Optional)
function source:get_trigger_characters()
function Source:get_trigger_characters()
return { '.' }
end
---Invoke completion. (Required)
---@param params cmp.SourceCompletionApiParams
---@param callback fun(response: lsp.CompletionResponse|nil)
function source:complete(params, callback)
function Source:complete(params, callback)
callback({
{ label = 'January' },
{ label = 'February' },
@ -641,21 +605,72 @@ You can create custom source like the following example.
---Resolve completion item. (Optional)
---@param completion_item lsp.CompletionItem
---@param callback fun(completion_item: lsp.CompletionItem|nil)
function source:resolve(completion_item, callback)
function Source:resolve(completion_item, callback)
callback(completion_item)
end
---Execute command after item was accepted.
---@param completion_item lsp.CompletionItem
---@param callback fun(completion_item: lsp.CompletionItem|nil)
function source:execute(completion_item, callback)
function Source:execute(completion_item, callback)
callback(completion_item)
end
---Register custom source to nvim-cmp.
require('cmp').register_source('month', source.new())
require('cmp').register_source('month', Source.new())
<
Create custom completion view~
You can create custom source like the following example.
>
local CompletionView = {}
---Create
CompletionView.new = function()
return setmetatable({}, { __index = CompletionView })
end
---Show completion menu.
function CompletionView:on_open(offset, entries)
...
end
---Apply `close` behavior to the view.
---The `close` means `just closed the view` not like an `<C-e>`.
function CompletionView:on_close()
...
end
---Apply `abort` behavior to the view.
---The `abort` means the behavior like native `<C-e>`.
---The native `<C-e>` will close the menu and restore the current line to the state of before selecting the item.
function CompletionView:on_abort()
...
end
---Return current viewport.
---If this view renders the `border`, this method should return the viewport that contains the border's width/height.
---This result will be used to render DocumentationView.
function CompletionView:get_viewport()
return { row: number, col: number, width: number, height: number }
end
---Apply selection change if needed.
---The `index` can be `-1` that means `nothing to select`.
---@param index number
---@param behavior cmp.SelectBehavior
function CompletionView:select(index, behavior
...
end
require('cmp').register_completion_view('fancy', CompletionView.new())
<
==============================================================================
FAQ *cmp-faq*

View file

@ -190,7 +190,7 @@ end
---Return view information.
---@param suggest_offset number
---@param entries_buf number The buffer this entry will be rendered into.
---@return { abbr: { text: string, bytes: number, width: number, hl_group: string }, kind: { text: string, bytes: number, width: number, hl_group: string }, menu: { text: string, bytes: number, width: number, hl_group: string } }
---@return { abbr: { text: string, bytes: number, width: number, hl_group: string }, kind: { text: string, bytes: number, width: number, hl_group: string }, menu: { text: string, bytes: number, width: number, hl_group: string }, dup: "1" | "0" }
entry.get_view = function(self, suggest_offset, entries_buf)
local item = self:get_vim_item(suggest_offset)
return self.cache:ensure({ 'get_view', self.resolved_completion_item and 1 or 0, entries_buf }, function()

View file

@ -33,6 +33,17 @@ cmp.ItemField.Abbr = 'abbr'
cmp.ItemField.Kind = 'kind'
cmp.ItemField.Menu = 'menu'
---
--- core
---
---@class cmp.WindowOption
---@field public max_width number
---@field public max_height number
---@field public zindex number
---@field public border string|string[]
---@field public highlight string
---@class cmp.ContextOption
---@field public reason cmp.ContextReason|nil
@ -43,19 +54,29 @@ cmp.ItemField.Menu = 'menu'
---@class cmp.SelectOption
---@field public behavior cmp.SelectBehavior
---@class cmp.SnippetExpansionParams
---@field public body string
---@field public insert_text_mode number
---@class cmp.CompleteParams
---@class cmp.CompleteOption
---@field public reason? cmp.ContextReason
---@field public config? cmp.ConfigSchema
---@class cmp.Setup
---@field public __call fun(c: cmp.ConfigSchema)
---@field public buffer fun(c: cmp.ConfigSchema)
---@field public global fun(c: cmp.ConfigSchema)
---@field public cmdline fun(type: string, c: cmp.ConfigSchema)
---@class cmp.Mapping
---@field public i nil|function(fallback: function): void
---@field public c nil|function(fallback: function): void
---@field public x nil|function(fallback: function): void
---@field public s nil|function(fallback: function): void
---
--- Custom
---
---@class cmp.CompletionView
---@field public on_open function(offset: number, entries: cmp.Entry[]): void
---@field public on_close fun()
---@field public on_abort fun()
---@field public select fun(index: number, behavior: cmp.SelectBehavior)
---
--- Source API
---
---@class cmp.SourceApiParams: cmp.SourceConfig
@ -64,33 +85,31 @@ cmp.ItemField.Menu = 'menu'
---@field public context cmp.Context
---@field public completion_context lsp.CompletionContext
---@class cmp.Mapping
---@field public i nil|function(fallback: function): void
---@field public c nil|function(fallback: function): void
---@field public x nil|function(fallback: function): void
---@field public s nil|function(fallback: function): void
---@class cmp.Setup
---@field public __call fun(c: cmp.ConfigSchema)
---@field public buffer fun(c: cmp.ConfigSchema)
---@field public global fun(c: cmp.ConfigSchema)
---@field public cmdline fun(type: string, c: cmp.ConfigSchema)
---
--- Configuration Schema
---
---@class cmp.ConfigSchema
---@field private revision number
---@field public enabled fun():boolean|boolean
---@field public enabled boolean|fun():boolean
---@field public preselect cmp.PreselectMode
---@field public mapping table<string, cmp.Mapping>
---@field public snippet cmp.SnippetConfig
---@field public completion cmp.CompletionConfig
---@field public window cmp.WindowConfig|nil
---@field public documentation cmp.DocumentationConfig|"false"
---@field public confirmation cmp.ConfirmationConfig
---@field public formatting cmp.FormattingConfig
---@field public matching cmp.MatchingConfig
---@field public sorting cmp.SortingConfig
---@field public formatting cmp.FormattingConfig
---@field public snippet cmp.SnippetConfig
---@field public mapping table<string, cmp.Mapping>
---@field public confirmation cmp.ConfirmationConfig
---@field public sources cmp.SourceConfig[]
---@field public view cmp.ViewConfig
---@field public experimental cmp.ExperimentalConfig
--- @class cmp.WindowConfig
--- @field completion cmp.CompletionWindowConfig
--- @field documentation cmp.DocumentationConfig
---@class cmp.CompletionConfig
---@field public autocomplete cmp.TriggerEvent[]
---@field public completeopt string
@ -98,22 +117,6 @@ cmp.ItemField.Menu = 'menu'
---@field public keyword_length number
---@field public keyword_pattern string
---@class cmp.CompletionWindowConfig
---@field public border string|string[]
---@field public winhighlight string|cmp.WinhighlightConfig
---@field public zindex number|nil
---@class cmp.DocumentationConfig
---@field public border string|string[]
---@field public max_height number|nil
---@field public max_width number|nil
---@field public winhighlight string|cmp.WinhighlightConfig
---@field public zindex number|nil
---@class cmp.WinhighlightConfig
---@field public bordered string
---@field public default string
---@class cmp.ConfirmationConfig
---@field public default_behavior cmp.ConfirmBehavior
---@field public get_commit_characters fun(commit_characters: string[]): string[]
@ -132,14 +135,10 @@ cmp.ItemField.Menu = 'menu'
---@field public format fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem
---@class cmp.SnippetConfig
---@field public expand fun(args: cmp.SnippetExpansionParams)
---@field public expand fun(args: { body: string, insert_text_mode: lsp.InsertTextMode })
---@class cmp.ExperimentalConfig
---@field public native_menu boolean
---@field public ghost_text cmp.GhostTextConfig|"false"
---@class cmp.GhostTextConfig
---@field hl_group string
---@field public ghost_text { hl_group: string }|"false"
---@class cmp.SourceConfig
---@field public name string
@ -152,18 +151,16 @@ cmp.ItemField.Menu = 'menu'
---@field public group_index number|nil
---@class cmp.ViewConfig
---@field public entries cmp.EntriesConfig
---@field public completion cmp.CompletionViewConfig
---@field public documentation cmp.DocumentationViewConfig
---@alias cmp.EntriesConfig cmp.CustomEntriesConfig|cmp.NativeEntriesConfig|cmp.WildmenuEntriesConfig|string
---@class cmp.CompletionViewConfig
---@field name string
---@field option table|nil
---@class cmp.CustomEntriesConfig
---@field name "'custom'"
---@class cmp.NativeEntriesConfig
---@field name "'native'"
---@class cmp.WildmenuEntriesConfig
---@field name "'wildmenu'"
---@field separator string|nil
---@class cmp.DocumentationViewConfig
---@field name string
---@field option table|nil
return cmp

View file

@ -7,10 +7,13 @@ local custom_entries_view = require('cmp.view.custom_entries_view')
local wildmenu_entries_view = require('cmp.view.wildmenu_entries_view')
local native_entries_view = require('cmp.view.native_entries_view')
local ghost_text_view = require('cmp.view.ghost_text_view')
local default_completion_view = require('cmp.view.default_completion_view')
---@class cmp.View
---@field public event cmp.Event
---@field private resolve_dedup cmp.AsyncDedup
---@field private completion_view cmp.CompletionView
---@field private native_entries_view cmp.NativeEntriesView
---@field private custom_entries_view cmp.CustomEntriesView
---@field private wildmenu_entries_view cmp.CustomEntriesView
@ -30,6 +33,8 @@ view.new = function()
self.ghost_text_view = ghost_text_view.new()
self.event = event.new()
self.completion_view = default_completion_view.new()
return self
end
@ -48,6 +53,91 @@ end
---@param ctx cmp.Context
---@param sources cmp.Source[]
view.open = function(self, ctx, sources)
local offset, entries = self:_get_completion(ctx, sources)
-- complete_done.
if #entries ~= 0 then
self.completion_view:on_open(offset, entries)
self:_get_entries_view():open(offset, entries)
else
self:close()
end
end
---Close menu
view.close = function(self)
if self:visible() then
self.event:emit('complete_done', {
entry = self:_get_entries_view():get_selected_entry(),
})
end
self.completion_view:on_close()
self:_get_entries_view():close()
self.docs_view:close()
self.ghost_text_view:hide()
end
---Abort menu
view.abort = function(self)
self.completion_view:on_abort()
self:_get_entries_view():abort()
self.docs_view:close()
self.ghost_text_view:hide()
end
---Return the view is visible or not.
---@return boolean
view.visible = function(self)
return self:_get_entries_view():visible()
end
---Scroll documentation window if possible.
---@param delta number
view.scroll_docs = function(self, delta)
self.docs_view:scroll(delta)
end
---Select prev menu item.
---@param option cmp.SelectOption
view.select_next_item = function(self, option)
self:_get_entries_view():select_next_item(option)
end
---Select prev menu item.
---@param option cmp.SelectOption
view.select_prev_item = function(self, option)
self:_get_entries_view():select_prev_item(option)
end
---Get offset.
view.get_offset = function(self)
return self:_get_entries_view():get_offset()
end
---Get entries.
---@return cmp.Entry[]
view.get_entries = function(self)
return self:_get_entries_view():get_entries()
end
---Get first entry
---@param self cmp.Entry|nil
view.get_first_entry = function(self)
return self:_get_entries_view():get_first_entry()
end
---Get current selected entry
---@return cmp.Entry|nil
view.get_selected_entry = function(self)
return self:_get_entries_view():get_selected_entry()
end
---Get current active entry
---@return cmp.Entry|nil
view.get_active_entry = function(self)
return self:_get_entries_view():get_active_entry()
end
view._get_completion = function(_, ctx, sources)
local source_group_map = {}
for _, s in ipairs(sources) do
local group_index = s:get_source_config().group_index or 0
@ -107,87 +197,18 @@ view.open = function(self, ctx, sources)
-- open
if #entries > 0 then
self:_get_entries_view():open(offset, entries)
break
local deduped = {}
local actual = {}
for _, e in ipairs(entries) do
if view.dup == 1 or not deduped[e.completion_item.label] then
table.insert(actual, e)
deduped[e.completion_item.label] = true
end
end
return offset, entries
end
end
-- complete_done.
if #entries == 0 then
self:close()
end
end
---Close menu
view.close = function(self)
if self:visible() then
self.event:emit('complete_done', {
entry = self:_get_entries_view():get_selected_entry(),
})
end
self:_get_entries_view():close()
self.docs_view:close()
self.ghost_text_view:hide()
end
---Abort menu
view.abort = function(self)
self:_get_entries_view():abort()
self.docs_view:close()
self.ghost_text_view:hide()
end
---Return the view is visible or not.
---@return boolean
view.visible = function(self)
return self:_get_entries_view():visible()
end
---Scroll documentation window if possible.
---@param delta number
view.scroll_docs = function(self, delta)
self.docs_view:scroll(delta)
end
---Select prev menu item.
---@param option cmp.SelectOption
view.select_next_item = function(self, option)
self:_get_entries_view():select_next_item(option)
end
---Select prev menu item.
---@param option cmp.SelectOption
view.select_prev_item = function(self, option)
self:_get_entries_view():select_prev_item(option)
end
---Get offset.
view.get_offset = function(self)
return self:_get_entries_view():get_offset()
end
---Get entries.
---@return cmp.Entry[]
view.get_entries = function(self)
return self:_get_entries_view():get_entries()
end
---Get first entry
---@param self cmp.Entry|nil
view.get_first_entry = function(self)
return self:_get_entries_view():get_first_entry()
end
---Get current selected entry
---@return cmp.Entry|nil
view.get_selected_entry = function(self)
return self:_get_entries_view():get_selected_entry()
end
---Get current active entry
---@return cmp.Entry|nil
view.get_active_entry = function(self)
return self:_get_entries_view():get_active_entry()
return -1, {}
end
---Return current configured entries_view

View file

@ -0,0 +1,64 @@
local window = require('cmp.utils.window')
local DefaultCompletionView = {}
function DefaultCompletionView.new()
local self = setmetatable({}, { __index = DefaultCompletionView })
self.window = window.new()
self.entries = {}
return self
end
---@param offset number
---@param entries cmp.Entry[]
function DefaultCompletionView:on_open(offset, entries)
self.offset = offset
self.entries = entries
local widths = {
abbr = 0,
kind = 0,
menu = 0,
}
for _, e in ipairs(entries) do
local view = e:get_view(offset, self.window:get_buffer())
widths.abbr = math.max(view.abbr.width, widths.abbr)
widths.kind = math.max(view.kind.width, widths.kind)
widths.menu = math.max(view.menu.width, widths.menu)
end
local lines = {}
for _, e in ipairs(entries) do
local view = e:get_view(offset, self.window:get_buffer())
local line = {}
table.insert(line, view.abbr.text .. (' '):rep(widths.abbr - view.abbr.width))
table.insert(line, view.kind.text .. (' '):rep(widths.kind - view.kind.width))
table.insert(line, view.menu.text .. (' '):rep(widths.menu - view.menu.width))
table.insert(lines, table.concat(line, ''))
end
vim.api.nvim_buf_set_lines(self.window:get_buffer(), 0, -1, false, lines)
vim.api.nvim_buf_set_option(self.window:get_buffer(), 'modified', false)
self.window:open({
relative = 'editor',
style = 'minimal',
row = vim.api.nvim_win_get_cursor(0)[1] + 1,
col = offset,
width = widths.abbr + widths.kind + widths.menu,
height = math.min(#lines, 8),
})
end
function DefaultCompletionView:on_close()
self.window:close()
end
function DefaultCompletionView:on_abort()
self.window:close()
end
function DefaultCompletionView:select(index, behavior)
end
return DefaultCompletionView