This repository has been archived by the owner on Jul 21, 2024. It is now read-only.
generated from nvimdev/nvim-plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
306 additions
and
77 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
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,34 +1,17 @@ | ||
# nvim-plugin-template | ||
neovim plugin template integration test and doc publish | ||
## Epo | ||
|
||
## Usage | ||
|
||
1. click `use this template` button generate a repo on your github. | ||
2. clone your plugin repo.open terminal then cd plugin directory. | ||
3. run `python3 rename.py your-plugin-name` this will replace all `nvim-plugin-template` to your `pluing-name`. | ||
then it will prompt you input `y` or `n` to remove example codes in `init.lua` and | ||
`test/plugin_spec.lua`. if you are familiar this repo just input y. if you are first look at this | ||
template I suggest you look at them first. after these step the `rename.py` will also auto | ||
remove. | ||
|
||
now you have a clean plugin env . enjoy! | ||
|
||
## Format | ||
a blazing fast neovim lsp auto-completion plugin | ||
|
||
format use `stylua` and provide `.stylua.toml`. | ||
|
||
## Test | ||
use vusted for test install by using `luarocks --lua-version=5.1 install vusted` then run `vusted test` | ||
for your test cases. | ||
|
||
create test case in test folder file rule is `foo_spec.lua` with `_spec` more usage please check | ||
[busted usage](https://lunarmodules.github.io/busted/) | ||
## Usage | ||
|
||
## Ci | ||
Ci support auto generate doc from README and integration test and lint check by `stylua`. | ||
invoke it on `on_attach` function like: | ||
|
||
```lua | ||
on_attach = function(client, bufnr) | ||
require('epo').auto_complete(client, bufnr, true or false) | ||
end | ||
``` | ||
|
||
## More | ||
Other usage you can look at my plugins | ||
third param is fuzzy match enable. | ||
|
||
## License MIT |
File renamed without changes.
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 |
---|---|---|
@@ -0,0 +1,294 @@ | ||
local api, vfn = vim.api, vim.fn | ||
local snippet = require('vim.lsp._snippet_grammar') | ||
local protocol = require('vim.lsp.protocol') | ||
local lsp = vim.lsp | ||
local util = require('vim.lsp.util') | ||
local ms = protocol.Methods | ||
|
||
local cmp_data = {} | ||
local match_fuzzy = false | ||
|
||
local function buf_data_init(bufnr) | ||
cmp_data[bufnr] = { | ||
incomplete = {}, | ||
omni_pending = false, | ||
} | ||
end | ||
|
||
local function parse_snippet(input) | ||
local ok, parsed = pcall(function() | ||
return tostring(snippet.parse(input)) | ||
end) | ||
if not ok then | ||
return input | ||
end | ||
return parsed | ||
end | ||
|
||
local function charidx_without_comp(bufnr, pos) | ||
if pos.character <= 0 then | ||
return pos.character | ||
end | ||
local text = api.nvim_buf_get_lines(bufnr, pos.line, pos.line + 1, false)[1] | ||
if #text == 0 then | ||
return pos.character | ||
end | ||
local idx = vfn.byteidxcomp(text, pos.character) | ||
if idx ~= -1 then | ||
if idx == #text then | ||
return vfn.strcharlen(text) | ||
else | ||
return vfn.charidx(text, idx, false) | ||
end | ||
end | ||
return pos.character | ||
end | ||
|
||
local function completion_handler(_, result, ctx) | ||
local client = lsp.get_clients({ id = ctx.client_id }) | ||
if not result or not client or not api.nvim_buf_is_valid(ctx.bufnr) then | ||
return | ||
end | ||
|
||
local entrys = {} | ||
|
||
local compitems | ||
if vim.tbl_islist(result) then | ||
compitems = result | ||
else | ||
compitems = result.items | ||
cmp_data[ctx.bufnr].incomplete[ctx.client_id] = result.isIncomplete or false | ||
end | ||
|
||
local col = vfn.charcol('.') | ||
local line = api.nvim_get_current_line() | ||
local before_text = col == 1 and '' or line:sub(1, col - 1) | ||
|
||
-- Get the start position of the current keyword | ||
local ok, retval = pcall(vfn.matchstrpos, before_text, '\\k*$') | ||
if not ok or not #retval == 0 then | ||
return | ||
end | ||
local prefix, start_idx = unpack(retval) | ||
local startcol = start_idx + 1 | ||
prefix = prefix:lower() | ||
|
||
for _, item in ipairs(compitems) do | ||
local entry = { | ||
abbr = item.label, | ||
kind = protocol.CompletionItemKind[item.kind] or 'Unknown', | ||
icase = 1, | ||
dup = 1, | ||
empty = 1, | ||
user_data = { | ||
nvim = { | ||
lsp = { | ||
completion_item = item, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
local textEdit = vim.tbl_get(item, 'textEdit') | ||
if textEdit then | ||
local start_col = #prefix ~= 0 and vfn.charidx(before_text, start_idx) + 1 or col | ||
local range = {} | ||
if textEdit.range then | ||
range = textEdit.range | ||
elseif textEdit.insert then | ||
range = textEdit.insert | ||
end | ||
local te_startcol = charidx_without_comp(ctx.bufnr, range.start) | ||
if te_startcol ~= start_col then | ||
local offset = start_col - te_startcol - 1 | ||
entry.word = textEdit.newText:sub(offset) | ||
else | ||
entry.word = textEdit.newText | ||
end | ||
elseif vim.tbl_get(item, 'insertText') then | ||
entry.word = item.insertText | ||
else | ||
entry.word = item.label | ||
end | ||
|
||
local register = true | ||
if lsp.protocol.InsertTextFormat[item.insertTextFormat] == 'snippet' then | ||
entry.word = parse_snippet(item.textEdit.newText) | ||
elseif not cmp_data[ctx.bufnr].incomplete then | ||
if #prefix ~= 0 then | ||
local filter = item.filterText or entry.word | ||
if | ||
filter and (match_fuzzy and #vfn.matchfuzzy({ filter }, prefix) == 0) | ||
or (not vim.startswith(filter:lower(), prefix) or not vim.startswith(filter, prefix)) | ||
then | ||
register = false | ||
end | ||
end | ||
end | ||
|
||
if register then | ||
if item.detail and #item.detail > 0 then | ||
entry.menu = vim.split(item.detail, '\n', { trimempty = true })[1] | ||
end | ||
|
||
if item.documentation and #item.documentation > 0 then | ||
entry.info = item.info | ||
end | ||
|
||
entry.sortText = item.sortText or item.label | ||
entrys[#entrys + 1] = entry | ||
end | ||
end | ||
|
||
table.sort(entrys, function(a, b) | ||
return (a.sortText or a.label) < (b.sortText or b.label) | ||
end) | ||
|
||
if not cmp_data[ctx.bufnr].omni_pending then | ||
local mode = api.nvim_get_mode()['mode'] | ||
if mode == 'i' or mode == 'ic' then | ||
vfn.complete(startcol, entrys) | ||
end | ||
return | ||
end | ||
|
||
cmp_data[ctx.bufnr].omni_pending = false | ||
cmp_data[ctx.bufnr].compitems = vim.list_extend(cmp_data[ctx.bufnr].compitems or {}, entrys) | ||
end | ||
|
||
local function completion_request(client, bufnr, trigger_kind, trigger_char) | ||
local params = util.make_position_params(api.nvim_get_current_win(), client.offset_encoding) | ||
params.context = { | ||
triggerKind = trigger_kind, | ||
triggerCharacter = trigger_char, | ||
} | ||
client.request(ms.textDocument_completion, params, completion_handler, bufnr) | ||
end | ||
|
||
_G.omnifunc = function(findstart, _) | ||
local curbuf = api.nvim_get_current_buf() | ||
local clients = lsp.get_clients({ bufnr = curbuf, method = ms.textDocument_completion }) | ||
|
||
if not cmp_data[curbuf] then | ||
buf_data_init(curbuf) | ||
end | ||
|
||
if findstart then | ||
cmp_data[curbuf].omni_pending = true | ||
for _, client in ipairs(clients) do | ||
completion_request(client, curbuf, 1, '') | ||
end | ||
|
||
local line = api.nvim_get_current_line() | ||
local win = vim.api.nvim_get_current_win() | ||
local pos = api.nvim_win_get_cursor(win) | ||
local before_text = vfn.strpart(line, 0, pos[2]) | ||
local prefix = vfn.matchstr(before_text, '\\k\\+$') | ||
cmp_data[curbuf]['cmp_prefix'] = prefix | ||
return before_text:len() - prefix:len() | ||
end | ||
|
||
local count = 0 | ||
while cmp_data[curbuf].omni_pending and count < 1000 do | ||
if vfn.complete_check() then | ||
return -2 | ||
end | ||
vim.uv.sleep(200) | ||
count = count + 1 | ||
end | ||
|
||
if cmp_data[curbuf].omni_pending then | ||
return -2 | ||
end | ||
|
||
local compitems = {} | ||
local incomplete = false | ||
for _, v in pairs(cmp_data[curbuf]['incomplete']) do | ||
if v then | ||
incomplete = true | ||
end | ||
end | ||
|
||
if #cmp_data[curbuf]['cmp_prefix'] == 0 or incomplete then | ||
return compitems | ||
end | ||
|
||
return vim.tbl_filter(function(item) | ||
return vim.startwith(item, cmp_data[curbuf]['cmp_prefix']) | ||
end, compitems) | ||
end | ||
|
||
local function complete_ondone(bufnr) | ||
api.nvim_create_autocmd('CompleteDone', { | ||
group = api.nvim_create_augroup('lsp_auto_complete', { clear = false }), | ||
buffer = bufnr, | ||
callback = function() | ||
local textedits = vim.tbl_get( | ||
vim.v.completed_item, | ||
'user_data', | ||
'nvim', | ||
'lsp', | ||
'completion_item', | ||
'additionalTextEdits' | ||
) | ||
if textedits then | ||
lsp.util.apply_text_edits(textedits, bufnr, 'utf-16') | ||
end | ||
end, | ||
}) | ||
end | ||
|
||
local function auto_complete(client, bufnr, fuzzy) | ||
match_fuzzy = fuzzy or false | ||
api.nvim_set_option_value('completeopt', 'menuone,noselect', { scope = 'global' }) | ||
api.nvim_create_autocmd('TextChangedI', { | ||
group = api.nvim_create_augroup('lsp_auto_complete', { clear = false }), | ||
buffer = bufnr, | ||
callback = function(args) | ||
if | ||
not lsp.get_clients({ | ||
bufnr = args.buf, | ||
method = ms.textDocument_completion, | ||
id = client.id, | ||
}) | ||
then | ||
return | ||
end | ||
|
||
local col = vfn.charcol('.') | ||
local line = api.nvim_get_current_line() | ||
if col == 0 or #line == 0 then | ||
return | ||
end | ||
|
||
local triggerKind = lsp.protocol.CompletionTriggerKind.Invoked | ||
local triggerChar = '' | ||
|
||
local ok, val = pcall(api.nvim_eval, ([['%s' !~ '\k']]):format(line:sub(col - 1, col - 1))) | ||
if not ok then | ||
return | ||
end | ||
|
||
if val ~= 0 then | ||
local triggerCharacters = client.server_capabilities.completionProvider.triggerCharacters | ||
or {} | ||
if not vim.tbl_contains(triggerCharacters, line:sub(col - 1, col - 1)) then | ||
return | ||
end | ||
triggerKind = lsp.protocol.CompletionTriggerKind.TriggerCharacter | ||
triggerChar = line:sub(col - 1, col - 1) | ||
end | ||
|
||
if not cmp_data[args.buf] then | ||
buf_data_init(args.buf) | ||
end | ||
|
||
completion_request(client, args.buf, triggerKind, triggerChar) | ||
end, | ||
}) | ||
complete_ondone() | ||
end | ||
|
||
return { | ||
auto_complete = auto_complete, | ||
} |
This file was deleted.
Oops, something went wrong.
Empty file.
Oops, something went wrong.