Skip to content
This repository has been archived by the owner on Jul 21, 2024. It is now read-only.

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
glepnir committed Oct 17, 2023
1 parent 838cf55 commit 2180d69
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 77 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: panvimdoc
uses: kdheepak/panvimdoc@main
with:
vimdoc: nvim-plugin-template
vimdoc: epo
treesitter: true
- uses: stefanzweifel/git-auto-commit-action@v4
with:
Expand Down Expand Up @@ -61,3 +61,4 @@ jobs:
luarocks install luacheck
luarocks install vusted
vusted ./test
vusted ./test
37 changes: 10 additions & 27 deletions README.md
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.
294 changes: 294 additions & 0 deletions lua/epo/init.lua
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,
}
7 changes: 0 additions & 7 deletions lua/nvim-plugin-template/init.lua

This file was deleted.

Empty file removed plugin/nvim-plugin-template.lua
Empty file.
34 changes: 0 additions & 34 deletions rename.py

This file was deleted.

Loading

0 comments on commit 2180d69

Please sign in to comment.