Repository: haorenW1025/completion-nvim
Branch: master
Commit: 87b0f86da3df
Files: 26
Total size: 101.2 KB
Directory structure:
gitextract_3s7g_cxh/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .luacheckrc
├── .travis.yml
├── LICENSE
├── README.md
├── autoload/
│ ├── completion.vim
│ └── health/
│ └── completion_nvim.vim
├── doc/
│ └── completion-nvim.txt
├── lua/
│ ├── completion/
│ │ ├── chain_completion.lua
│ │ ├── complete.lua
│ │ ├── health.lua
│ │ ├── hover.lua
│ │ ├── manager.lua
│ │ ├── matching.lua
│ │ ├── option.lua
│ │ ├── signature_help.lua
│ │ ├── source/
│ │ │ ├── ins_complete.lua
│ │ │ ├── lsp.lua
│ │ │ ├── path.lua
│ │ │ └── snippet.lua
│ │ ├── source.lua
│ │ └── util.lua
│ └── completion.lua
└── plugin/
└── completion.vim
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
- Please read through [this section](https://github.com/haorenW1025/completion-nvim/wiki/trouble-shooting) before posting a bug report.
**My testing minimal init.vim**
Post your init.vim to help me reproduce this issue
**How to reproduce**
Detailed step to reproduce this issue.
**Expected behavior**
A clear and concise description of what you expected to happen.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
================================================
FILE: .gitignore
================================================
doc/tags
.vim
================================================
FILE: .luacheckrc
================================================
globals = {
"vim",
}
================================================
FILE: .travis.yml
================================================
language: generic
os:
- linux
before_install:
- sudo apt-get update
- sudo apt-get install
- sudo apt install -y lua5.1 luarocks
- sudo luarocks install luacheck
jobs:
include:
- stage: luacheck
# build plugin first, then run the test from neovim
script: luacheck lua/*
os: linux
git:
depth: 3
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
## WARNING: completion.nvim is no longer maintained
If you are looking for an autocompletion plugin, the neovim LSP team recommends either [nvim-cmp](https://github.com/hrsh7th/nvim-cmp/) or [coq_nvim](https://github.com/ms-jpq/coq_nvim).
[](https://travis-ci.com/haorenW1025/completion-nvim)
[](https://gitter.im/completion-nvim/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
# completion-nvim
completion-nvim is an auto completion framework that aims to provide a better
completion experience with neovim's built-in LSP. Other LSP functionality is not
supported.
## Features
- Asynchronous completion using the `libuv` api.
- Automatically open hover windows when popupmenu is available.
- Automatically open signature help if it's available.
- Snippets integration with UltiSnips, Neosnippet, vim-vsnip, and snippets.nvim.
- Apply *additionalTextEdits* in LSP spec if it's available.
- Chain completion support inspired by [vim-mucomplete](https://github.com/lifepillar/vim-mucomplete)
## Demo
Demo using `sumneko_lua`

## Prerequisites
- Neovim nightly
- You should set up your language server of choice with the help of [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
## Install
- Install with any plugin manager by using the path on GitHub.
```vim
Plug 'nvim-lua/completion-nvim'
```
## Setup
- completion-nvim requires several autocommands set up to work properly. You should
set it up using the `on_attach` function like this.
```vim
lua require'lspconfig'.pyls.setup{on_attach=require'completion'.on_attach}
```
- Change `pyls` to whichever language server you're using.
- If you want completion-nvim to be set up for all buffers instead of only being
used when lsp is enabled, call the `on_attach` function directly:
```vim
" Use completion-nvim in every buffer
autocmd BufEnter * lua require'completion'.on_attach()
```
*NOTE* It's okay to set up completion-nvim without lsp. It will simply use
another completion source instead(Ex: snippets).
## Supported Completion Source
- built-in sources
* lsp: completion source for neovim's built-in LSP.
* snippet: completion source for snippet.
* path: completion source for path from current file.
- ins-complete sources
* See `:h ins-completion` and [wiki](https://github.com/haorenW1025/completion-nvim/wiki/chain-complete-support)
- external sources
* [completion-buffers](https://github.com/steelsojka/completion-buffers): completion for
buffers word.
* [completion-treesitter](https://github.com/nvim-treesitter/completion-treesitter): treesitter
based completion sources.
* [vim-dadbod-completion](https://github.com/kristijanhusak/vim-dadbod-completion): completion sources
for `vim-dadbod`.
* [completion-tabnine](https://github.com/aca/completion-tabnine): AI code completion tool TabNine integration.
* [completion-tags](https://github.com/kristijanhusak/completion-tags): Slightly improved ctags completion
* [completion-tmux](https://github.com/albertoCaroM/completion-tmux): tmux panels completion
* [completion-vcard](https://github.com/cbarrete/completion-vcard): email completion from vCards
## Configuration
### Recommended Setting
```vim
" Use <Tab> and <S-Tab> to navigate through popup menu
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
" Set completeopt to have a better completion experience
set completeopt=menuone,noinsert,noselect
" Avoid showing message extra message when using completion
set shortmess+=c
```
### Enable/Disable auto popup
- By default auto popup is enabled, turn it off by
```vim
let g:completion_enable_auto_popup = 0
```
- Or you can toggle auto popup on the fly by using command `CompletionToggle`
- You can manually trigger completion with mapping key by
```vim
"map <c-p> to manually trigger completion
imap <silent> <c-p> <Plug>(completion_trigger)
```
- Or you want to use `<Tab>` as trigger keys
```vim
imap <tab> <Plug>(completion_smart_tab)
imap <s-tab> <Plug>(completion_smart_s_tab)
```
### Enable Snippets Support
- By default other snippets source support are disabled, turn them on by
```vim
" possible value: 'UltiSnips', 'Neosnippet', 'vim-vsnip', 'snippets.nvim'
let g:completion_enable_snippet = 'UltiSnips'
```
- Supports `UltiSnips`, `Neosnippet`, `vim-vsnip` and `snippets.nvim`
### LSP Based Snippet parsing
- Some language server have snippet support but neovim couldn't handle that for now, `completion-nvim` can integrate
with other LSP snippet parsing plugin for this support.
Right now, [vim-vsnip](https://github.com/hrsh7th/vim-vsnip) (requiring [vim-vsnip-integ](https://github.com/hrsh7th/vim-vsnip-integ)) and [snippets.nvim](https://github.com/norcalli/snippets.nvim) are supported.
### Chain Completion Support
- completion-nvim supports chain completion, which use other completion sources
and `ins-completion` as a fallback for lsp completion.
- See [wiki](https://github.com/haorenW1025/completion-nvim/wiki/chain-complete-support) for
details on how to set this up.
### Changing Completion Confirm key
- By default `<CR>` is used to confirm completion and expand snippets, change it by
```vim
let g:completion_confirm_key = "\<C-y>"
```
- Make sure to use `" "` and add escape key `\` to avoid parsing issues.
- If the confirm key has a fallback mapping, for example when using the auto
pairs plugin, it maps to `<CR>`. You can avoid using the default confirm key option and
use a mapping like this instead.
```.vim
let g:completion_confirm_key = ""
imap <expr> <cr> pumvisible() ? complete_info()["selected"] != "-1" ?
\ "\<Plug>(completion_confirm_completion)" : "\<c-e>\<CR>" : "\<CR>"
```
### Enable/Disable auto hover
- By default when navigating through completion items, LSP's hover is automatically
called and displays in a floating window. Disable it by
```vim
let g:completion_enable_auto_hover = 0
```
### Enable/Disable auto signature
- By default signature help opens automatically whenever it's available. Disable
it by
```vim
let g:completion_enable_auto_signature = 0
```
### Sorting completion items
- You can decide how your items being sorted in the popup menu. The default value
is `"alphabet"`, change it by
```vim
" possible value: "length", "alphabet", "none"
let g:completion_sorting = "length"
```
- If you don't want any sorting, you can set this value to `"none"`.
### Matching Strategy
- There are three different kind of matching technique implement in
completion-nvim: `substring`, `fuzzy`, `exact` or `all`.
- You can specify a list of matching strategy, completion-nvim will loop through the list and
assign priority from high to low. For example
```vim
let g:completion_matching_strategy_list = ['exact', 'substring', 'fuzzy', 'all']
```
*NOTE* Fuzzy match highly dependent on what language server you're using. It might not
work as you expect on some language server.
- You can also enable ignore case matching by
```vim
g:completion_matching_ignore_case = 1
```
- Or smart case matching by
```vim
g:completion_matching_smart_case = 1
```
### Trigger Characters
- By default, `completion-nvim` respect the trigger character of your language server, if you
want more trigger characters, add it by
```vim
let g:completion_trigger_character = ['.', '::']
```
*NOTE* use `:lua print(vim.inspect(vim.lsp.buf_get_clients()[1].server_capabilities.completionProvider.triggerCharacters))`
to see the trigger character of your language server.
- If you want different trigger character for different languages, wrap it in an autocommand like
```vim
augroup CompletionTriggerCharacter
autocmd!
autocmd BufEnter * let g:completion_trigger_character = ['.']
autocmd BufEnter *.c,*.cpp let g:completion_trigger_character = ['.', '::']
augroup end
```
### Trigger keyword length
- You can specify keyword length for triggering completion, if the current word is less then keyword length, completion won't be
triggered.
```vim
let g:completion_trigger_keyword_length = 3 " default = 1
```
**NOTE** `completion-nvim` will ignore keyword length if you're on trigger character.
### Trigger on delete
- `completion-nvim` doesn't trigger completion on delete by default because sometimes I've found it annoying. However,
you can enable it by
```vim
let g:completion_trigger_on_delete = 1
```
### Timer Adjustment
- completion-nvim uses a timer to control the rate of completion. You can adjust the timer rate by
```vim
let g:completion_timer_cycle = 200 "default value is 80
```
### Per Server Setup
- You can have different setup for each server in completion-nvim using lua, see [wiki]
(https://github.com/nvim-lua/completion-nvim/wiki/per-server-setup-by-lua) for more guide.
## Trouble Shooting
- This plugin is in the early stages and might have unexpected issues.
Please follow [wiki](https://github.com/haorenW1025/completion-nvim/wiki/trouble-shooting)
for trouble shooting.
- Feel free to post issues on any unexpected behavior or open a feature request!
================================================
FILE: autoload/completion.vim
================================================
" Perform a Hack to confirm completion
function! completion#completion_confirm() abort
lua require'completion'.confirmCompletion()
call nvim_feedkeys("\<C-Y>", "n", v:true)
endfunction
function! completion#wrap_completion() abort
if pumvisible() != 0 && complete_info()["selected"] != "-1"
call completion#completion_confirm()
else
call nvim_feedkeys("\<c-g>\<c-g>", "n", v:true)
let key = g:completion_confirm_key
call nvim_feedkeys(key, "n", v:true)
endif
endfunction
" Depracated
" Wrapper to get manually trigger working
" Please send me a pull request if you know how to do this properly...
function! completion#completion_wrapper()
lua require'completion'.triggerCompletion()
return ''
endfunction
" Depracated
function! completion#trigger_completion()
return "\<c-r>=completion#completion_wrapper()\<CR>"
endfunction
" Depracated
" Wrapper of getting buffer variable
" Avoid accessing to unavailable variable
function! completion#get_buffer_variable(str)
return get(b:, a:str, v:null)
endfunction
function! completion#enable_in_comment()
let l:list = g:completion_chain_complete_list
if type(l:list) == v:t_dict && has_key(l:list, 'default')
\ && type(l:list.default) == v:t_dict
\ && has_key(l:list.default, 'comment')
call remove(g:completion_chain_complete_list, 'comment')
endif
endfunction
================================================
FILE: autoload/health/completion_nvim.vim
================================================
function! health#completion_nvim#check()
lua require 'completion.health'.checkHealth()
endfunction
================================================
FILE: doc/completion-nvim.txt
================================================
*completion-nvim.txt*
Async completion framework that aims to provide completion for neovim's
built-in LSP, written in Lua.
CONTENTS *completion-nvim*
0. Introduction ......... |completion-introduction|
1. Features ............. |completion-feature|
1. Prerequisite ......... |completion-prerequisite|
2. Setup ................ |completion-setup|
3. Options .............. |completion-option|
==============================================================================
INTRODUCTION *completion-introduction*
completion-nvim is an auto completion framework that aims to provide a better
completion experience with neovim's built-in LSP. Other LSP functionality is
not supported.
==============================================================================
FEATURES *completion-features*
- Asynchronous completion using libuv api.
- Automatically open hover windows when popupmenu is available.
- Automatically open signature help if it's available.
- Snippets integration with UltiSnips and Neosnippet and vim-vsnip.
- ins-complete method integration
- Apply additionalTextEdits in LSP spec if it's available.
- Chain completion support inspired by vim-mucomplete
==============================================================================
PREREQUISITES *completion-prerequisites*
- Neovim 5.0
- You should be setting up language server with the help of nvim-lspconfig
==============================================================================
SETUP *completion-setup*
- completion-nvim requires several autocommands set up to work properly, you
should set it up using the `on_attach` function like this.
>
lua require'lspconfig'.pyls.setup{on_attach=require'completion'.on_attach}
- Change `pyls` to whichever language server you are using.
- If you want completion-nvim to be set up for all buffers instead of only
being used when lsp is enabled, call the `on_attach` function directly:
>
" Use completion-nvim in every buffer
autocmd BufEnter * lua require'completion'.on_attach()
<
Note: It's okay to set up completion-nvim without lsp. It will simply use
another completion source instead(Ex: snippets).
==============================================================================
OPTION *completion-option*
g:completion_enable_auto_popup *g:completion_enable_auto_popup*
This variable enable automatically popup window for completion. Set
this value to 0 if you don't want automatically popup window.
If you disable auto popup menu, you can manually trigger completion by
mapping keys. For example:
>
" map <c-p> to manually trigger completion
imap <silent> <c-p> <Plug>(completion_trigger)
<
Or you want to use <tab> to trigger completion without modifying the
usage to <tab> keys.
>
imap <tab> <Plug>(completion_smart_tab)
imap <s-tab> <Plug>(completion_smart_s_tab)
<
default value: 1
g:completion_enable_snippet *g:completion_enable_snippet*
You can specify which snippet engines you want to use. Possible values
are |UltiSnips| and |Neosnippet| and |vim-vsnip|.
Note: Snippet engines will not work without setting this variables.
default value: v:null
g:completion_confirm_key *g:completion_confirm_key*
You can specify which keys to use for confirm completion(which will
select the completion items and expand snippets if available).
Note: Make sure to use a proper escape sequence to avoid parsing
issues, for example:
>
" Change confirm key to <C-y>
let g:completion_confirm_key = "\<C-y>"
<
default value: "\<CR>"
If the confirm key has a fallback mapping, for example when using the
auto pairs plugin, it maps to `<cr>`. You can avoid using the default
confirm key option and use a mapping like this instead:
>
let g:completion_confirm_key = ""
imap <expr> <cr> pumvisible() ? complete_info()["selected"] != "-1" ?
\ "\<Plug>(completion_confirm_completion)" :
\ "\<c-e>\<CR>" : "\<CR>"
<
g:completion_enable_auto_hover *g:completion_enable_auto_hover*
By default, completion-nvim will automatically open a hover window
when you navigate through the complete items(including basic
information of snippets). You can turn this off by setting this option
to zero.
default value: 1
g:completion_enable_auto_signature *g:completion_enable_auto_signature*
By default signature help opens automatically whenever it is
available. You can turn it off by setting this option to zero.
default value: 1
g:completion_popup_border *g:completion_popup_border*
This variable sets border for auto hover popup and signature help popup.
The variable is not created by default so that there is no border by
default. You can set it as per neovim's popup/preview
window sepcifications.
available options: 'single', 'double', 'rounded', 'solid' and 'shadow'
default value: variable not declared
g:completion_enable_auto_paren *g:completion_enable_auto_paren*
Enable the auto insert parenthesis feature. completion-nvim will
insert parenthesis when completing methods or functions.
default value: 0
g:completion_trigger_character *g:completion_trigger_character*
You can add or disable a trigger character that will trigger
completion.
For example, disable trigger character:
>
let g:completion_trigger_character = []
<
Or having multiple trigger characters:
>
let g:completion_trigger_character = ['.', '::']
<
Use an autocmd if you want a different trigger character for different
languages:
>
augroup CompletionTriggerCharacter
autocmd!
autocmd BufEnter * let g:completion_trigger_character = ['.']
autocmd BufEnter *.c,*.cpp let g:completion_trigger_character = ['.', '::']
augroup end
<
default value: ['.']
g:completion_enable_server_trigger *g:completion_enable_server_trigger*
Whether or not to use the trigger characters provided by the language
server for triggering the popup menu.
You can turn it off by setting this option to zero.
default value: 1
g:completion_trigger_keyword_length *g:completion_trigger_keyword_length*
You can specify keyword length for triggering completion, if the
current word is less than keyword length, completion won't be
triggered.
Note: completion-nvim will ignore keyword length if you're on trigger
character.
default value: 1
g:completion_trigger_on_delete *g:completion_trigger_on_delete*
completion-nvim doesn't trigger completion on delete by default,
as this can be a nuisance. However, you can enable it via:
>
let g:completion_trigger_on_delete = 1
<
g:completion_timer_cycle *g:completion_timer_cycle*
completion-nvim uses a timer to control the rate of completion. Adjust
the timer rate by setting this value.
Note: any values lower than the default is not recommended.
default value: 80
g:completion_chain_complete_list *g:completion_chain_complete_list*
completion-nvim has chain completion support inspired by
vim-mucomplete. In short, you can divide completion sources in groups
and have an ins-completion method as backup completion.
You can specify different completion list for different filetypes.
By default, possible sources are 'lsp', 'snippet', 'path' and various
ins-complete sources. Specify 'mode' as your key for ins-complete
sources, 'complete_items' for other sources. For example:
>
let g:completion_chain_complete_list = {
\'default' : [
\ {'complete_items': ['lsp', 'snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}
\]
\}
<
You can easily switch to next or previous sources by mapping keys in
insert mode. For example, using <c-j> to switch to previous sources
and <c-k> to switch to next sources:
>
imap <c-j> <Plug>(completion_next_source)
imap <c-k> <Plug>(completion_prev_source)
<
Customizing your completion sources is easy. For non ins-complete
items, you can choose to put them in the same source or separate them.
For example, if you want to separate lsp and snippet into two
different sources:
>
let g:completion_chain_complete_list = {
\'default' : [
\ {'complete_items': ['lsp']},
\ {'complete_items': ['snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}
\]
\}
<
There is a few completion source built in for now, here's a list.
>
"lsp": lsp completion
"snippet": snippet sources based on g:completion_enable_snippet
"path": path completion relative to the current file.
"UltiSnips": ultisnips source
"Neosnippet": neosnippet source
"vim-vsnip": vim-vsnip source
<
For ins-complete sources, possible 'mode' to the actual key in vim are
listed below.
>
"<c-n>" : i_CTRL-N
"<c-p>" : i_CTRL-P
"cmd" : i_CTRL-X_CTRL-V
"defs": i_CTRL-X_CTRL-D
"dict": i_CTRL-X_CTRL-K
"file": i_CTRL-X_CTRL-F
"incl": i_CTRL-X_CTRL-I
"keyn": i_CTRL-X_CTRL-N
"keyp": i_CTRL-X_CTRL-P
"omni": i_CTRL-X_CTRL-O
"line": i_CTRL-X_CTRL-L
"spel": i_CTRL-X_s
"tags": i_CTRL-X_CTRL-]
"thes": i_CTRL-X_CTRL-T
"user": i_CTRL-X_CTRL-U
<
You can also specify different completion lists for different
filetypes, for example:
>
let g:completion_chain_complete_list = {
\ 'vim': [
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}
\],
\ 'lua': [
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}
\],
\ 'default': [
\ {'complete_items': ['lsp', 'snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}
\]
\}
<
You can take a step further to specify different 'scope' of different
filetypes. 'scope' is literally syntax in your file. Say that you want
different completion lists in comments and function calls, strings,
etc, you can do that easily. Here is an example
>
let g:completion_chain_complete_list = {
\ 'lua': [
\ 'string': [
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}],
\ 'func' : [
\ {'complete_items': ['lsp']}],
\ 'default': [
\ {'complete_items': ['lsp', 'snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}],
\],
\ 'default' : {
\ 'default': [
\ {'complete_items': ['lsp', 'snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}],
\ 'comment': []
\ }
\}
<
For every completion source, you can specify `triggered_only` key.
This completion source will only trigger when this key is press. For
example:
>
let g:completion_chain_complete_list = {
\ 'default' : {
\ 'default': [
\ {'complete_items': ['lsp', 'snippet']},
\ {'complete_items': ['path'], 'triggered_only': ['/']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}],
\ 'comment': []
\ }
\}
<
Note: Every syntax highlighter has a different syntax name
defined(most of them are similar though). You can check your syntax
name under your cursor by this command:
>
:echo synIDattr(synID(line('.'), col('.'), 1), "name")
<
You just need to specify a part of a result in the scope since it uses
a regex pattern to match it (For example: if the result is 'luaComment'
you only need to specified 'comment', case doesn't matter).
g:completion_auto_change_source *g:completion_auto_change_source*
You can let completion-nvim changes source whenever current source has
no complete items by setting this option to 1.
default value: 0
g:completion_matching_strategy_list *g:completion_matching_strategy_list*
There are three different kind of matching technique implement in
completion-nvim: 'substring', 'fuzzy', 'exact' or 'all'. You can
specify a list of matching strategy, completion-nvim will loop through
the list and assign priority from high to low. For example:
>
let g:completion_matching_strategy_list = ['exact', 'substring', 'fuzzy', 'all']
<
default value: ['exact']
g:completion_matching_ignore_case *g:completion_matching_ignore_case*
Enable ignore case matching in all matching strategy. For example:
>
let g:completion_matching_ignore_case = 1
<
default value: &ignorecase
g:completion_matching_smart_case *g:completion_matching_smart_case*
Enable smart case matching in all matching strategy. For example
>
let g:completion_matching_smart_case = 1
<
default value: &smartcase
g:completion_sorting *g:completion_sorting*
You can determine how you want to sort the completion items in popup
menu. Possible values are 'alphabet', 'length', 'none'
default value: 'alphabet'
g:completion_abbr_length *g:completion_abbr_length*
Some language server have long snippets items, which can make your
completion menu super long. This option enable you to truncate
item.abbr with a maximum length.
default value: 0(which means no truncates)
g:completion_menu_length *g:completion_menu_length*
Similar to `g:completion_abbr_length`, language server may populate
the completion menus with long menu items. This option enable you
truncate item.menu with a maximum length.
default value: 0(which means no truncates)
==============================================================================
vim:tw=78:ts=8:ft=help:norl:noet:fen:noet:
================================================
FILE: lua/completion/chain_completion.lua
================================================
local vim = vim
local api = vim.api
local util = require 'completion.util'
local opt = require 'completion.option'
local M ={}
--------------------------------------------------------------
-- local function to parse completion_chain_complete_list --
--------------------------------------------------------------
local function chain_list_to_tree(complete_list)
if util.is_list(complete_list) then
return {
default = {
default= complete_list
}
}
else
local complete_tree = {}
for ft, c_list in pairs(complete_list) do
if util.is_list(c_list) then
complete_tree[ft] = {
default=c_list
}
else
complete_tree[ft] = c_list
end
end
-- Be sure that default.default exists
if not complete_tree.default then
complete_tree.default = {
default = {
{ complete_items={ 'lsp', 'snippet' } }
}
}
end
return complete_tree
end
end
local function getScopedChain(ft_subtree)
local syntax_getter = function()
local pos = api.nvim_win_get_cursor(0)
return vim.fn.synIDattr(vim.fn.synID(pos[1], pos[2]-1, 1), "name")
end
-- If this option is effectively a function, use it to determine syntax group at point
local syntax_at_point = opt.get_option("syntax_at_point")
if syntax_at_point then
if vim.is_callable(syntax_at_point) then
syntax_getter = syntax_at_point
elseif type(syntax_at_point) == "string" and vim.fn.exists("*" .. syntax_at_point) then
syntax_getter = vim.fn[syntax_at_point]
end
end
local atPoint = syntax_getter():lower()
for syntax_regex, complete_list in pairs(ft_subtree) do
if type(syntax_regex) == "string" and string.match(atPoint, '.*' .. syntax_regex:lower() .. '.*') ~= nil and syntax_regex ~= "default" then
return complete_list
end
end
return nil
end
-- preserve compatiblity of completion_chain_complete_list
function M.getChainCompleteList(filetype)
local chain_complete_list = chain_list_to_tree(opt.get_option('chain_complete_list'))
-- check if chain_complete_list is a array
if chain_complete_list[filetype] then
return getScopedChain(chain_complete_list[filetype])
or getScopedChain(chain_complete_list.default)
or chain_complete_list[filetype].default
or chain_complete_list.default.default
else
return getScopedChain(chain_complete_list.default) or chain_complete_list.default.default
end
end
function M.checkHealth(complete_items_map)
local completion_list = vim.g.completion_chain_complete_list
local health_ok = vim.fn['health#report_ok']
local health_error = vim.fn['health#report_error']
local error = false
for filetype, _ in pairs(completion_list) do
local chain_complete_list
if filetype ~= 'default' then
chain_complete_list = M.getChainCompleteList(filetype)
else
chain_complete_list = getScopedChain(completion_list.default) or completion_list.default.default
end
if chain_complete_list ~= nil then
for _,complete_source in ipairs(chain_complete_list) do
if vim.fn.has_key(complete_source, "complete_items") > 0 then
for _,item in ipairs(complete_source.complete_items) do
if complete_items_map[item] == nil then
health_error(item.." is not a valid completion source (in filetype "..filetype..")")
error = true
end
end
else
local ins = require 'completion.source.ins_complete'
if ins.checkHealth(complete_source.mode) then
health_error(complete_source.mode.." is not a valid insert completion mode")
end
end
end
end
end
if not error then
health_ok("all completion sources are valid")
end
end
return M
================================================
FILE: lua/completion/complete.lua
================================================
local vim = vim
local api = vim.api
local util = require 'completion.util'
local ins = require 'completion.source.ins_complete'
local match = require'completion.matching'
local lsp = require'completion.source.lsp'
local opt = require 'completion.option'
local manager = require 'completion.manager'
local M = {}
local cache_complete_items = {}
local function checkCallback(callback_array)
for _,val in ipairs(callback_array) do
if not val then return false end
if type(val) == 'function' then
if val() == false then return end
end
end
return true
end
local function getCompletionItems(items_array, prefix)
local complete_items = {}
for _,func in ipairs(items_array) do
vim.list_extend(complete_items, func(prefix))
end
return complete_items
end
M.clearCache = function()
cache_complete_items = {}
lsp.isIncomplete = true
end
-- perform completion
M.performComplete = function(complete_source, complete_items_map, params)
manager.insertChar = false
if vim.fn.has_key(complete_source, "mode") > 0 then
-- ins-complete source
ins.triggerCompletion(complete_source.mode)
elseif vim.fn.has_key(complete_source, "complete_items") > 0 then
local callback_array = {}
local items_array = {}
-- collect getCompleteItems function of current completion source
for _, item in ipairs(complete_source.complete_items) do
-- check isIncomplete for lsp
local complete_items = complete_items_map[item]
-- special case to handle lsp isIncomplete flag
if item == 'lsp' then
if lsp.isIncomplete then
cache_complete_items = {}
table.insert(callback_array, complete_items.callback)
complete_items.trigger(manager, params)
table.insert(items_array, complete_items.item)
end
else
if complete_items ~= nil then
if complete_items.callback == nil then
table.insert(callback_array, true)
else
table.insert(callback_array, complete_items.callback)
-- TODO: still pass in manager here because there's external sources using it
-- will remove it when refactoring aysnc sources
complete_items.trigger(manager, params)
end
table.insert(items_array, complete_items.item)
end
end
end
if #cache_complete_items == 0 then
-- use callback_array to handle async behavior
local timer = vim.loop.new_timer()
timer:start(20, 50, vim.schedule_wrap(function()
if manager.insertChar == true and not timer:is_closing() then
timer:stop()
timer:close()
end
-- only perform complete when callback_array are all true
if checkCallback(callback_array) == true and timer:is_closing() == false then
if api.nvim_get_mode()['mode'] == 'i' or api.nvim_get_mode()['mode'] == 'ic' then
local items = getCompletionItems(items_array, params.prefix)
if opt.get_option('sorting') ~= "none" then
util.sort_completion_items(items)
end
if #items ~= 0 then
-- reset insertChar and handle auto changing source
cache_complete_items = items
vim.fn.complete(params.textMatch+1, items)
manager.changeSource = false
else
manager.changeSource = true
end
end
timer:stop()
timer:close()
end
end))
else
if api.nvim_get_mode()['mode'] == 'i' or api.nvim_get_mode()['mode'] == 'ic' then
local items = {}
for _, item in ipairs(cache_complete_items) do
match.matching(items, params.prefix, item)
end
if opt.get_option('sorting') ~= "none" then
util.sort_completion_items(items)
end
if #items ~= 0 then
local matching_strategy = opt.get_option("matching_strategy_list")
-- don't re-trigger complete when exact matching to avoid flickering
-- reset insertChar and handle auto changing source
cache_complete_items = items
if #matching_strategy == 1 and matching_strategy[1] == 'exact' then
return
else
vim.fn.complete(params.textMatch+1, items)
end
manager.changeSource = false
else
cache_complete_items = {}
manager.changeSource = true
end
end
end
end
end
return M
================================================
FILE: lua/completion/health.lua
================================================
local vim = vim
local api = vim.api
local source = require 'completion.source'
local opt = require 'completion.option'
local health_start = vim.fn["health#report_start"]
local health_ok = vim.fn['health#report_ok']
local health_info = vim.fn['health#report_info']
local health_error = vim.fn['health#report_error']
local M = {}
local checkCompletionSource = function()
source.checkHealth()
end
local checkSnippetSource = function()
local snippet_source = opt.get_option('enable_snippet')
if snippet_source == nil then
health_info("You haven't set up any snippet source")
else
local rtp = string.lower(api.nvim_get_option("rtp"))
local unknown_snippet_source = true
local snippet_sources = {
["UltiSnips"] = "ultisnips",
["Neosnippet"] = "neosnippet.vim",
["vim-vsnip"] = "vsnip",
["snippets.nvim"] = "snippets.nvim"
}
for k,v in pairs(snippet_sources) do
if snippet_source == k then
unknown_snippet_source = false
if string.match(rtp, ".*"..v..".*") then
health_ok("You are using "..k.." as your snippet source")
else
health_error(k.." is not available! Check if you installed "..k.." correctly")
end
break
end
end
if unknown_snippet_source then
health_error("Your snippet source is not available! Possible values are: UltiSnips, Neosnippet, vim-vsnip, snippets.nvim")
end
end
end
function M.checkHealth()
health_start("general")
if vim.tbl_filter == nil then
health_error("vim.tbl_filter is not found!", {'consider recompiling neovim from the latest master branch'})
else
health_ok("neovim version is supported")
end
health_start("completion source")
checkCompletionSource()
health_start("snippet source")
checkSnippetSource()
end
return M
================================================
FILE: lua/completion/hover.lua
================================================
-- define some hover related function modified from neovim source code
local vim = vim
local validate = vim.validate
local api = vim.api
local opt = require 'completion.option'
local manager = require 'completion.manager'
local M = {}
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
local function find_window_by_var(name, value)
for _, win in ipairs(api.nvim_list_wins()) do
if npcall(api.nvim_win_get_var, win, name) == value then
return win
end
end
end
M.focusable_float = function(unique_name, fn)
if npcall(api.nvim_win_get_var, 0, unique_name) then
return api.nvim_command("wincmd p")
end
local bufnr = api.nvim_get_current_buf()
do
local win = find_window_by_var(unique_name, bufnr)
if win then
api.nvim_win_close(win, true)
end
end
local pbufnr, pwinnr = fn()
if pbufnr then
api.nvim_win_set_var(pwinnr, unique_name, bufnr)
return pbufnr, pwinnr
end
end
---------------------------------
-- floating window for hover --
---------------------------------
local make_floating_popup_options = function(width, height, opts)
validate {
opts = { opts, 't', true };
}
opts = opts or {}
validate {
["opts.offset_x"] = { opts.offset_x, 'n', true };
["opts.offset_y"] = { opts.offset_y, 'n', true };
}
local lines_above = vim.fn.winline() - 1
local lines_below = vim.fn.winheight(0) - lines_above
local col
if lines_above < lines_below then
height = math.min(lines_below, height)
else
height = math.min(lines_above, height)
end
if opts.align == 'right' then
col = opts.col + opts.width
else
col = opts.col - width - 1
end
local default_border = {
{"", "NormalFloat"},
{"", "NormalFloat"},
{"", "NormalFloat"},
{" ", "NormalFloat"},
{"", "NormalFloat"},
{"", "NormalFloat"},
{"", "NormalFloat"},
{" ", "NormalFloat"},
}
return {
col = col,
height = height,
relative = 'editor',
row = opts.row,
focusable = false,
style = 'minimal',
width = width,
border = vim.g.completion_popup_border or default_border
}
end
local fancy_floating_markdown = function(contents, opts)
local pad_left = opts and opts.pad_left
local pad_right = opts and opts.pad_right
local stripped = {}
local highlights = {}
local max_width
if opts.align == 'right' then
local columns = api.nvim_get_option('columns')
max_width = columns - opts.col - opts.width
else
max_width = opts.col - 1
end
do
local i = 1
while i <= #contents do
local line = contents[i]
local ft = line:match("^```([a-zA-Z0-9_]*)$")
if ft then
local start = #stripped
i = i + 1
while i <= #contents do
line = contents[i]
if line == "```" then
i = i + 1
break
end
if #line > max_width then
while #line > max_width do
local trimmed_line = string.sub(line, 1, max_width)
local index = trimmed_line:reverse():find(" ")
if index == nil or index > #trimmed_line/2 then
break
else
table.insert(stripped, string.sub(line, 1, max_width-index))
line = string.sub(line, max_width-index+2, #line)
end
end
table.insert(stripped, line)
else
table.insert(stripped, line)
end
i = i + 1
end
table.insert(highlights, {
ft = ft;
start = start + 1;
finish = #stripped + 1 - 1
})
else
if #line > max_width then
while #line > max_width do
local trimmed_line = string.sub(line, 1, max_width)
-- local index = math.max(trimmed_line:reverse():find(" "), trimmed_line:reverse():find("/"))
local index = trimmed_line:reverse():find(" ")
if index == nil or index > #trimmed_line/2 then
break
else
table.insert(stripped, string.sub(line, 1, max_width-index))
line = string.sub(line, max_width-index+2, #line)
end
end
table.insert(stripped, line)
else
table.insert(stripped, line)
end
i = i + 1
end
end
end
local width = 0
for i, v in ipairs(stripped) do
v = v:gsub("\r", "")
if pad_left then v = (" "):rep(pad_left)..v end
if pad_right then v = v..(" "):rep(pad_right) end
stripped[i] = v
width = math.max(width, #v)
end
if opts.align == 'right' then
local columns = api.nvim_get_option('columns')
if opts.col + opts.row + width > columns then
width = columns - opts.col - opts.width -1
end
else
if width > opts.col then
width = opts.col - 1
end
end
local insert_separator = true
if insert_separator then
for i, h in ipairs(highlights) do
h.start = h.start + i - 1
h.finish = h.finish + i - 1
if h.finish + 1 <= #stripped then
table.insert(stripped, h.finish + 1, string.rep("─", width))
end
end
end
-- Make the floating window.
local height = #stripped
local bufnr = api.nvim_create_buf(false, true)
local winnr
if opt.get_option('docked_hover') == 1 then
if height > opt.get_option('docked_maximum_size') then
height = opt.get_option('docked_maximum_size')
elseif height < opt.get_option('docked_minimum_size') then
height = opt.get_option('docked_minimum_size')
end
local row
if vim.fn.winline() > api.nvim_get_option('lines')/2 then
row = 0
else
row = api.nvim_get_option('lines') - height
end
winnr = api.nvim_open_win(bufnr, false, {
col = 0,
height = height,
relative = 'editor',
row = row,
focusable = true,
style = 'minimal',
width = api.nvim_get_option('columns'),
})
else
local opt = make_floating_popup_options(width, height, opts)
if opt.width <= 0 then return end
winnr = api.nvim_open_win(bufnr, false, opt)
end
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped)
-- setup a variable for floating window, fix #223
vim.api.nvim_buf_set_var(bufnr, "lsp_floating", true)
local cwin = vim.api.nvim_get_current_win()
vim.api.nvim_set_current_win(winnr)
vim.cmd("ownsyntax markdown")
local idx = 1
local function highlight_region(ft, start, finish)
if ft == '' then return end
local name = ft..idx
idx = idx + 1
local lang = "@"..ft:upper()
-- TODO(ashkan): better validation before this.
if not pcall(vim.cmd, string.format("syntax include %s syntax/%s.vim", lang, ft)) then
return
end
vim.cmd(string.format("syntax region %s start=+\\%%%dl+ end=+\\%%%dl+ contains=%s", name, start, finish + 1, lang))
end
for _, h in ipairs(highlights) do
highlight_region(h.ft, h.start, h.finish)
end
vim.api.nvim_set_current_win(cwin)
return bufnr, winnr
end
local function handler_function(_, method, result)
if vim.fn.pumvisible() == 1 then
M.focusable_float(method, function()
if not (result and result.contents) then
-- return { 'No information available' }
return
end
local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents)
markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
-- return { 'No information available' }
return
end
local bufnr, winnr
-- modified to open hover window align to popupmenu
local position = vim.fn.pum_getpos()
-- Set max width option to avoid overlapping with popup menu
local total_column = api.nvim_get_option('columns')
local align
local col = position['col']
if position['col'] < total_column/2 then
align = 'right'
if position['scrollbar'] then
col = col + 1
end
else
align = 'left'
end
bufnr, winnr = fancy_floating_markdown(markdown_lines, {
pad_left = 0; pad_right = 1;
col = col; width = position['width']; row = position['row']-1;
align = align
})
M.winnr = winnr
if winnr ~= nil and api.nvim_win_is_valid(winnr) then
vim.lsp.util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, winnr)
end
local hover_len = #vim.api.nvim_buf_get_lines(bufnr,0,-1,false)[1]
local win_width = vim.api.nvim_win_get_width(0)
if hover_len > win_width then
vim.api.nvim_win_set_width(winnr,math.min(hover_len,win_width))
vim.api.nvim_win_set_height(winnr,math.ceil(hover_len/win_width))
vim.wo[winnr].wrap = true
end
return bufnr, winnr
end)
end
end
M.autoOpenHoverInPopup = function()
if vim.fn.pumvisible() ~= 1 then return end
local bufnr = api.nvim_get_current_buf()
if api.nvim_call_function('pumvisible', {}) == 1 then
-- Auto open hover
local items = api.nvim_call_function('complete_info', {{"eval", "selected", "items", "user_data"}})
if items['selected'] ~= manager.selected then
manager.textHover = true
if M.winnr ~= nil and api.nvim_win_is_valid(M.winnr) then
api.nvim_win_close(M.winnr, true)
end
M.winnr = nil
end
if manager.textHover == true and items['selected'] ~= -1 then
if items['selected'] == -2 then
items['selected'] = 0
end
local item = items['items'][items['selected']+1]
local user_data = item['user_data']
if user_data ~= nil and #user_data ~= 0 then
user_data = vim.fn.json_decode(item['user_data'])
end
if user_data ~= nil and user_data['lsp'] == nil then
if user_data['hover'] ~= nil and type(user_data['hover']) == 'string' and #user_data['hover'] ~= 0 then
local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(user_data['hover'])
markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines)
local position = vim.fn.pum_getpos()
-- Set max width option to avoid overlapping with popup menu
local total_column = api.nvim_get_option('columns')
local align
if position['col'] < total_column/2 then
align = 'right'
else
align = 'left'
end
local _, winnr = fancy_floating_markdown(markdown_lines, {
pad_left = 1; pad_right = 1;
col = position['col']; width = position['width']; row = position['row']-1;
align = align
})
M.winnr = winnr
end
else
local has_hover = false
for _, value in pairs(vim.lsp.buf_get_clients(0)) do
if value.resolved_capabilities.hover then
has_hover = true
break
end
end
if not has_hover then return end
local row, col = unpack(api.nvim_win_get_cursor(0))
row = row - 1
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = vim.str_utfindex(line, col)
local params = {
textDocument = vim.lsp.util.make_text_document_params();
position = { line = row; character = col-string.len(item.word); }
}
vim.lsp.buf_request(bufnr, 'textDocument/hover', params, handler_function)
end
manager.textHover = false
end
manager.selected = items['selected']
end
end
return M
================================================
FILE: lua/completion/manager.lua
================================================
local manager = {}
------------------------------------------------------------------------
-- plugin variables and states --
------------------------------------------------------------------------
-- Global variables table, accessed in scripts as manager.variable_name
manager = {
-- not used for now...
-- canTryCompletion = true,
-- chains = {}, -- here we store validated chains for each buffer
-- activeChain = nil, -- currently used completion chain
insertChar = false, -- flag for InsertCharPre event, turn off imediately when performing completion
insertLeave = false, -- flag for InsertLeave, prevent every completion if true
textHover = false, -- handle auto hover
selected = -1, -- handle selected items in v:complete-items for auto hover
changedTick = 0, -- handle changeTick
confirmedCompletion = false, -- flag for manual confirmation of completion
forceCompletion = false, -- flag for forced manual completion/source change
chainIndex = 1, -- current index in loaded chain
}
-- reset manager
-- called on insertEnter
function manager.init()
-- manager.activeChain = nil
manager.insertLeave = false
-- manager.canTryCompletion = true
manager.insertChar = false
manager.textHover = false
manager.selected = -1
manager.confirmedCompletion = false
manager.forceCompletion = false
manager.chainIndex = 1
end
-- TODO: change this when we have proper logger
function manager.debug()
print(
'canTryCompletion = ' .. vim.inspect(manager.canTryCompletion) .. '\n' ..
'insertChar = ' .. vim.inspect(manager.insertChar) .. '\n' ..
'insertLeave = ' .. vim.inspect(manager.insertLeave) .. '\n' ..
'textHover = ' .. vim.inspect(manager.textHover) .. '\n' ..
'selected = ' .. vim.inspect(manager.selected) .. '\n' ..
'changedTick = ' .. vim.inspect(manager.changedTick) .. '\n' ..
'confirmedCompletion = ' .. vim.inspect(manager.confirmedCompletion) .. '\n' ..
'forceCompletion = ' .. vim.inspect(manager.forceCompletion) .. '\n' ..
'chainIndex = ' .. vim.inspect(manager.chainIndex)
)
end
return manager
================================================
FILE: lua/completion/matching.lua
================================================
local vim = vim
local util = require 'completion.util'
local opt = require 'completion.option'
local M = {}
local function setup_case(prefix, word)
local ignore_case = opt.get_option('matching_ignore_case') == 1
if ignore_case and opt.get_option('matching_smart_case') == 1 and prefix:match('[A-Z]') then
ignore_case = false
end
if ignore_case then
return string.lower(prefix), string.lower(word)
end
return prefix, word
end
local function fuzzy_match(prefix, word)
prefix, word = setup_case(prefix, word)
local score = util.fuzzy_score(prefix, word)
if score < 1 then
return true, score
else
return false
end
end
local function substring_match(prefix, word)
prefix, word = setup_case(prefix, word)
if string.find(word, prefix, 1, true) then
return true
else
return false
end
end
local function exact_match(prefix, word)
prefix, word = setup_case(prefix, word)
if vim.startswith(word, prefix) then
return true
else
return false
end
end
local function all_match()
return true
end
local matching_strategy = {
fuzzy = fuzzy_match,
substring = substring_match,
exact = exact_match,
all = all_match,
}
M.matching = function(complete_items, prefix, item)
local matcher_list = opt.get_option('matching_strategy_list')
local matching_priority = 2
for _, method in ipairs(matcher_list) do
local is_match, score = matching_strategy[method](prefix, item.word)
if is_match then
if item.abbr == nil then
item.abbr = item.word
end
item.score = score
if item.priority ~= nil then
item.priority = item.priority + 10*matching_priority
else
item.priority = 10*matching_priority
end
util.addCompletionItems(complete_items, item)
break
end
matching_priority = matching_priority - 1
end
end
return M
================================================
FILE: lua/completion/option.lua
================================================
local M = {}
-- fallback to using global variable as default
local completion_opt_metatable = {
__index = function(_, key)
key = 'completion_'..key
return vim.g[key]
end
}
local option_table = setmetatable({}, completion_opt_metatable)
M.set_option_table = function(opt)
if opt ~= nil then
option_table = setmetatable(opt, completion_opt_metatable)
else
option_table = setmetatable({}, completion_opt_metatable)
end
end
M.get_option = function(opt)
return option_table[opt]
end
return M
================================================
FILE: lua/completion/signature_help.lua
================================================
local vim = vim
local validate = vim.validate
local api = vim.api
local util = require 'completion.util'
local M = {}
----------------------
-- signature help --
----------------------
M.autoOpenSignatureHelp = function()
local pos = api.nvim_win_get_cursor(0)
local line = api.nvim_get_current_line()
local line_to_cursor = line:sub(1, pos[2])
if vim.lsp.buf_get_clients() == nil then return end
local triggered
for _, value in pairs(vim.lsp.buf_get_clients(0)) do
if value.resolved_capabilities.signature_help == false or
value.server_capabilities.signatureHelpProvider == nil then
return
end
line_to_cursor = vim.trim(line_to_cursor)
triggered = util.checkTriggerCharacter(line_to_cursor,
value.server_capabilities.signatureHelpProvider.triggerCharacters)
end
if triggered then
-- overwrite signature help here to disable "no signature help" message
local params = vim.lsp.util.make_position_params()
local filetype = vim.api.nvim_buf_get_option(0, 'filetype')
vim.lsp.buf_request(0, 'textDocument/signatureHelp', params, function(err, method, result, client_id)
local client = vim.lsp.get_client_by_id(client_id)
local handler = client and client.handlers['textDocument/signatureHelp']
if handler then
handler(err, method, result, client_id)
return
end
if not (result and result.signatures and result.signatures[1]) then
return
end
local lines = vim.lsp.util.convert_signature_help_to_markdown_lines(result, filetype)
if vim.tbl_isempty(lines) then
return
end
-- if `lines` can be trimmed, it is modified in place
local trimmed_lines_filetype = vim.lsp.util.try_trim_markdown_code_blocks(lines)
local opts = {}
if vim.g.completion_popup_border then
opts.border = vim.g.completion_popup_border
end
local bufnr, _ = vim.lsp.util.open_floating_preview(
-- TODO show popup when signatures is empty?
vim.lsp.util.trim_empty_lines(lines),
trimmed_lines_filetype,
opts
)
-- setup a variable for floating window, fix #223
vim.api.nvim_buf_set_var(bufnr, "lsp_floating", true)
end)
end
end
return M
================================================
FILE: lua/completion/source/ins_complete.lua
================================================
-- luacheck: globals vim
local vim = vim
local api = vim.api
local manager = require 'completion.manager'
local M = {}
local ins_complete_table = {
['line'] = "<c-x><c-l>",
['cmd'] = "<c-x><c-v>",
['defs'] = "<c-x><c-d>",
['dict'] = "<c-x><c-k>",
['file'] = "<c-x><c-f>",
['incl'] = "<c-x><c-i>",
['keyn'] = "<c-x><c-n>",
['keyp'] = "<c-x><c-p>",
['omni'] = "<c-x><c-o>",
['spel'] = "<c-x>s",
['tags'] = "<c-x><c-]>",
['thes'] = "<c-x><c-t>",
['user'] = "<c-x><c-u>",
['<c-p>'] = "<c-g><c-g><c-p>",
['<c-n>'] = "<c-g><c-g><c-n>",
}
-- HACK workaround to handle delay of ins-complete
local checkEmptyCompletion = function()
local timer = vim.loop.new_timer()
timer:start(200, 0, vim.schedule_wrap(function()
if vim.fn.pumvisible() == 0 then
manager.changeSource = true
else
manager.changeSource = false
end
timer:stop()
timer:close()
end))
end
M.checkHealth = function(mode)
if ins_complete_table[mode] == nil then
return false
end
end
M.triggerCompletion = function(mode)
if ins_complete_table[mode] == nil then return end
if vim.fn.pumvisible() == 0 then
if vim.api.nvim_get_mode()['mode'] == 'i' or vim.api.nvim_get_mode()['mode'] == 'ic' then
local mode_keys = ins_complete_table[mode]
-- See https://github.com/neovim/neovim/issues/12297.
mode_keys = api.nvim_replace_termcodes(mode_keys, true, false, true)
api.nvim_feedkeys(mode_keys, 'n', true)
checkEmptyCompletion()
end
else
manager.insertChar = false
end
end
return M
================================================
FILE: lua/completion/source/lsp.lua
================================================
local vim = vim
local protocol = require 'vim.lsp.protocol'
local util = require 'completion.util'
local match = require 'completion.matching'
local opt = require 'completion.option'
local M = {}
M.callback = false
M.isIncomplete = true
M.getCompletionItems = function(_, _)
return M.items
end
local function sort_completion_items(items)
table.sort(items, function(a, b)
return (a.sortText or a.label) < (b.sortText or b.label)
end)
end
local function get_completion_word(item, prefix, suffix)
if item.textEdit ~= nil and item.textEdit ~= vim.NIL
and item.textEdit.newText ~= nil and (item.insertTextFormat ~= 2 or vim.fn.exists('g:loaded_vsnip_integ')) then
local start_range = item.textEdit.range["start"]
local end_range = item.textEdit.range["end"]
local newText
if start_range.line == end_range.line and start_range.character == end_range.character then
newText = prefix .. item.textEdit.newText
else
newText = item.textEdit.newText
end
if not item.insertTextFormat
or protocol.InsertTextFormat[item.insertTextFormat] == "PlainText"
or opt.get_option('enable_snippet') == "snippets.nvim" then
return newText
else
return vim.lsp.util.parse_snippet(newText)
end
elseif item.insertText ~= nil and item.insertText ~= vim.NIL then
if not item.insertTextFormat
or protocol.InsertTextFormat[item.insertTextFormat] == "PlainText"
or opt.get_option('enable_snippet') == "snippets.nvim" then
return item.insertText
else
return vim.lsp.util.parse_snippet(item.insertText)
end
end
return item.label
end
local function get_context_aware_snippets(item, completion_item, line_to_cursor)
if protocol.InsertTextFormat[completion_item.insertTextFormat] == "PlainText" then
return
end
local line = vim.api.nvim_get_current_line()
local nextWord = line:sub(#line_to_cursor+1, #line_to_cursor+1)
if #nextWord == 0 then
return
end
for _,ch in ipairs(vim.g.completion_expand_characters) do
if nextWord == ch then
return
end
end
item.user_data = {}
local matches, word
word, matches = item.word:gsub("%(.*%)$", "")
if matches == 0 then
word, matches = item.word:gsub("<.*>$", "")
end
if matches ~= 0 then
item.word = word
end
end
local function text_document_completion_list_to_complete_items(result, params)
local items = vim.lsp.util.extract_completion_items(result)
if vim.tbl_isempty(items) then
return {}
end
local customize_label = opt.get_option('customize_lsp_label')
-- items = remove_unmatch_completion_items(items, prefix)
sort_completion_items(items)
local matches = {}
for _, completion_item in ipairs(items) do
local item = {}
local info = ' '
local documentation = completion_item.documentation
if documentation then
if type(documentation) == 'string' and documentation ~= '' then
info = documentation
elseif type(documentation) == 'table' and type(documentation.value) == 'string' then
info = documentation.value
end
end
item.info = info
item.word = get_completion_word(completion_item, params.prefix, params.suffix)
item.word = item.word:gsub('\n', ' ')
item.word = vim.trim(item.word)
item.dup = opt.get_option("items_duplicate")['lsp']
item.user_data = {
lsp = {
completion_item = completion_item,
}
}
if protocol.InsertTextFormat[completion_item.insertTextFormat] == 'Snippet'
and opt.get_option('enable_snippet') == "snippets.nvim" then
item.user_data.actual_item = item.word
item.word = vim.trim(completion_item.label)
end
local kind = protocol.CompletionItemKind[completion_item.kind]
item.kind = customize_label[kind] or kind
item.abbr = vim.trim(completion_item.label)
if params.suffix ~= nil and #params.suffix ~= 0 then
local index = item.word:find(params.suffix)
if index ~= nil then
local newWord = item.word
newWord = newWord:sub(1, index-1)
item.word = newWord
item.user_data = {}
end
end
get_context_aware_snippets(item, completion_item, params.line_to_cursor)
item.priority = opt.get_option('items_priority')[item.kind] or opt.get_option('items_priority')[kind]
item.menu = completion_item.detail or ''
match.matching(matches, params.prefix, item)
end
return matches
end
M.getCallback = function()
return M.callback
end
M.triggerFunction = function(_, params)
local position_param = vim.lsp.util.make_position_params()
M.callback = false
M.items = {}
if vim.tbl_isempty(vim.lsp.buf_get_clients()) then
M.callback = true
return
end
vim.lsp.buf_request(params.bufnr, 'textDocument/completion', position_param, function(err, _, result)
if err or not result then
M.callback = true
return
end
local matches = text_document_completion_list_to_complete_items(result, params)
M.items = matches
M.isIncomplete = result.isIncomplete
M.callback = true
end)
end
return M
================================================
FILE: lua/completion/source/path.lua
================================================
local M = {}
local vim = vim
local loop = vim.loop
local api = vim.api
local util = require 'completion.util'
local opt = require 'completion.option'
M.items = {}
M.callback = false
-- onDirScanned handler for vim.loop
local function onDirScanned(_, data)
if data then
local function iter()
return vim.loop.fs_scandir_next(data)
end
for name, type in iter do
table.insert(M.items, {type = type, name=name})
end
end
M.callback = true
end
local fileTypesMap = setmetatable({
file = "(file)",
directory = "(dir)",
char = "(char)",
link = "(link)",
block = "(block)",
fifo = "(pipe)",
socket = "(socket)"
}, {__index = function()
return '(unknown)'
end
})
M.getCompletionItems = function(prefix)
local complete_items = {}
local kind = 'Path'
kind = opt.get_option('customize_lsp_label')[kind] or kind
for _, val in ipairs(M.items) do
local score = util.fuzzy_score(prefix, val.name)
if score < #prefix/3 or #prefix == 0 then
table.insert(complete_items, {
word = val.name,
kind = kind,
menu = fileTypesMap[val.type],
score = score,
icase = 1,
dup = 1,
empty = 1,
})
end
end
return complete_items
end
M.getCallback = function()
return M.callback
end
M.triggerFunction = function(_, opt)
local keyword
if vim.v.completed_item ~= nil and vim.v.completed_item.kind == 'Path' and
opt.line_to_cursor:find(vim.v.completed_item.word) then
keyword = M.keyword..vim.v.completed_item.word..'/'
else
M.keyword = nil
keyword = opt.line_to_cursor:match("[^%s\"\']+%S*/?$")
end
if keyword ~= nil and keyword ~= '/' then
local index = string.find(keyword:reverse(), '/')
if index == nil then index = keyword:len() + 1 end
local length = string.len(keyword) - index + 1
keyword = string.sub(keyword, 1, length)
end
local path = vim.fn.expand('%:p:h')
if keyword ~= nil then
local expanded_keyword = vim.fn.glob(keyword)
local home = vim.fn.expand("$HOME")
if expanded_keyword:sub(1, 1) == '/' or string.find(expanded_keyword, home) ~= nil then
path = expanded_keyword
else
path = vim.fn.expand('%:p:h')
path = path..'/'..keyword
end
end
M.keyword = keyword
M.items = {}
loop.fs_scandir(path, onDirScanned)
end
return M
================================================
FILE: lua/completion/source/snippet.lua
================================================
local vim = vim
local api = vim.api
local match = require'completion.matching'
local opt = require 'completion.option'
local M = {}
M.getUltisnipItems = function(prefix)
if vim.fn.exists("*UltiSnips#SnippetsInCurrentScope") == 0 then return {} end
local snippetsList = vim.call('UltiSnips#SnippetsInCurrentScope')
local complete_items = {}
if vim.tbl_isempty(snippetsList) then
return {}
end
local priority = vim.g.completion_items_priority['UltiSnips'] or 1
local kind = 'UltiSnips'
local dup = opt.get_option('items_duplicate')[kind] or 1
kind = opt.get_option('customize_lsp_label')[kind] or kind
for key, val in pairs(snippetsList) do
local item = {}
item.word = key
item.kind = kind
item.priority = priority
item.dup = dup
local user_data = {snippet_source = 'UltiSnips', hover = val}
item.user_data = user_data
match.matching(complete_items, prefix, item)
end
return complete_items
end
M.getNeosnippetItems = function(prefix)
if vim.fn.exists("*neosnippet#helpers#get_completion_snippets") == 0 then return {} end
local snippetsList = vim.call('neosnippet#helpers#get_completion_snippets')
local complete_items = {}
if vim.tbl_isempty(snippetsList) == 0 then
return {}
end
local kind = 'Neosnippet'
kind = opt.get_option('customize_lsp_label')[kind] or kind
local dup = opt.get_option('items_duplicate')[kind] or 1
local priority = vim.g.completion_items_priority['Neosnippet']
for key, val in pairs(snippetsList) do
local description
if val == nil or type(val) ~= "table" then description = nil else description = val.description end
local user_data = {snippet_source = 'Neosnippet', hover = description}
local item = {}
item.word = key
item.kind = kind
item.priority = priority
item.dup = dup
item.user_data = user_data
match.matching(complete_items, prefix, item)
end
return complete_items
end
M.getVsnipItems = function(prefix)
if vim.fn.exists('g:loaded_vsnip') == 0 then return {} end
local snippetsList = api.nvim_call_function('vsnip#source#find', {api.nvim_get_current_buf()})
local complete_items = {}
if vim.tbl_isempty(snippetsList) == 0 then
return {}
end
local kind = 'vim-vsnip'
kind = opt.get_option('customize_lsp_label')[kind] or kind
local priority = vim.g.completion_items_priority['vim-vsnip']
local dup = opt.get_option('items_duplicate')[kind] or 1
for _, source in pairs(snippetsList) do
for _, snippet in pairs(source) do
for _, word in pairs(snippet.prefix) do
local user_data = {snippet_source = 'vim-vsnip', snippet_body = snippet.body, hover = snippet.description}
local item = {}
item.word = word
item.kind = kind
item.menu = snippet.label
item.dup = dup
item.priority = priority
item.user_data = user_data
match.matching(complete_items, prefix, item)
end
end
end
return complete_items
end
-- Cribbed almost wholesale from snippets.lookup_snippet()
M.getSnippetsNvimItems = function(prefix)
local snippets = require 'snippets'
if not snippets then return {} end
local ft = vim.bo.filetype
local snippetsList = vim.tbl_extend('force', snippets.snippets._global or {}, snippets.snippets[ft] or {})
local complete_items = {}
if vim.tbl_isempty(snippetsList) == 0 then
return {}
end
local priority = vim.g.completion_items_priority['snippets.nvim'] or 1
local kind = 'snippets.nvim'
local dup = opt.get_option('items_duplicate')[kind] or 1
kind = opt.get_option('customize_lsp_label')[kind] or kind
for short, long in pairs(snippetsList) do
-- TODO: We cannot put the parsed snippet itself in userdata, since it may
-- contain Lua functions (see
-- https://github.com/norcalli/snippets.nvim#notes-because-this-is-beta-release-software)
local user_data = {snippet_source = 'snippets.nvim'}
local item = {}
item.word = short
item.kind = kind
item.dup = dup
-- TODO: Turn actual snippet text into label/description?
item.menu = short
item.priority = priority
item.user_data = user_data
match.matching(complete_items, prefix, item)
end
return complete_items
end
M.getCompletionItems = function(prefix)
local source = opt.get_option('enable_snippet')
local snippet_list = {}
if source == 'UltiSnips' then
snippet_list = M.getUltisnipItems(prefix)
elseif source == 'Neosnippet' then
snippet_list = M.getNeosnippetItems(prefix)
elseif source == 'vim-vsnip' then
snippet_list = M.getVsnipItems(prefix)
elseif source == 'snippets.nvim' then
snippet_list = M.getSnippetsNvimItems(prefix)
end
return snippet_list
end
return M
================================================
FILE: lua/completion/source.lua
================================================
local vim = vim
local api = vim.api
local util = require 'completion.util'
local complete = require 'completion.complete'
local chain_completion = require 'completion.chain_completion'
local lsp = require 'completion.source.lsp'
local snippet = require 'completion.source.snippet'
local path = require 'completion.source.path'
local opt = require 'completion.option'
local manager = require 'completion.manager'
local M = {}
local complete_items_map = {
['lsp'] = {
trigger = lsp.triggerFunction,
callback = lsp.getCallback,
item = lsp.getCompletionItems
},
['snippet'] = {
item = snippet.getCompletionItems
},
['path'] = {
item = path.getCompletionItems,
callback = path.getCallback,
trigger = path.triggerFunction,
trigger_character = {'/'}
},
['UltiSnips'] = {
item = snippet.getUltisnipItems
},
['vim-vsnip'] = {
item = snippet.getVsnipItems
},
['Neosnippet'] = {
item = snippet.getNeosnippetItems
},
['snippets.nvim'] = {
item = snippet.getSnippetsNvimItems
}
}
M.prefixLength = 0
M.stop_complete = false
------------------------------------------------------------------------
-- local function --
------------------------------------------------------------------------
local getTriggerCharacter = function()
local triggerCharacter = {}
local complete_source = M.chain_complete_list[manager.chainIndex]
if complete_source ~= nil and vim.fn.has_key(complete_source, "complete_items") > 0 then
for _, item in ipairs(complete_source.complete_items) do
local complete_items = complete_items_map[item]
if complete_items ~= nil and complete_items.trigger_character ~= nil then
for _,val in ipairs(complete_items.trigger_character) do
table.insert(triggerCharacter, val)
end
end
end
end
return triggerCharacter
end
local triggerCurrentCompletion = function(bufnr, line_to_cursor, prefix, textMatch, suffix, force)
-- avoid rebundant calling of completion
if manager.insertChar == false then return end
-- get current completion source
M.chain_complete_list = chain_completion.getChainCompleteList(api.nvim_buf_get_option(0, 'filetype'))
M.chain_complete_length = #M.chain_complete_list
local complete_source = M.chain_complete_list[manager.chainIndex]
if complete_source == nil then return end
-- handle source trigger character and user defined trigger character
local source_trigger_character = getTriggerCharacter(complete_source)
local triggered
triggered = util.checkTriggerCharacter(line_to_cursor, source_trigger_character) or
util.checkTriggerCharacter(line_to_cursor, opt.get_option('trigger_character'))
if complete_source.complete_items ~= nil then
for _, source in ipairs(complete_source.complete_items) do
if source == 'lsp' and vim.lsp.buf_get_clients() ~= nil then
for _, value in pairs(vim.lsp.buf_get_clients()) do
if value.server_capabilities.completionProvider == nil then
break
end
if opt.get_option('enable_server_trigger') == 1 then
triggered = triggered or util.checkTriggerCharacter(line_to_cursor,
value.server_capabilities.completionProvider.triggerCharacters)
end
end
break
end
end
end
-- handle user defined only triggered character
if complete_source['triggered_only'] ~= nil then
local triggered_only = util.checkTriggerCharacter(line_to_cursor, complete_source['triggered_only'])
if not triggered_only then
if opt.get_option('auto_change_source') == 1 then
manager.changeSource = true
end
return
end
end
local length = opt.get_option('trigger_keyword_length')
if #prefix < length and not triggered and not force then
return
end
if triggered then
complete.clearCache()
manager.chainIndex = 1
end
complete.performComplete(complete_source, complete_items_map, {bufnr=bufnr, prefix=prefix, textMatch=textMatch, suffix=suffix, line_to_cursor=line_to_cursor})
end
local getPositionParam = function()
local bufnr = api.nvim_get_current_buf()
local pos = api.nvim_win_get_cursor(0)
local line = api.nvim_get_current_line()
local line_to_cursor = line:sub(1, pos[2])
local cursor_to_end = line:sub(pos[2]+1, #line)
return bufnr, line_to_cursor, cursor_to_end
end
------------------------------------------------------------------------
-- member function --
------------------------------------------------------------------------
-- Activate when manually triggered completion or manually changing completion source
function M.triggerCompletion(force)
complete.clearCache()
if force then
manager.chainIndex = 1
end
local bufnr, line_to_cursor, cursor_to_end = getPositionParam()
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
local prefix = line_to_cursor:sub(textMatch+1)
local suffix = cursor_to_end:sub(1, vim.fn.matchend(cursor_to_end, '^\\k*'))
manager.insertChar = true
-- force is used when manually trigger, so it doesn't repect the trigger word length
triggerCurrentCompletion(bufnr, line_to_cursor, prefix, textMatch, suffix, force)
end
-- Handler for auto completion
function M.autoCompletion()
local bufnr, line_to_cursor, cursor_to_end = getPositionParam()
local textMatch = vim.fn.match(line_to_cursor, '\\k*$')
local prefix = line_to_cursor:sub(textMatch+1)
local suffix = cursor_to_end:sub(1, vim.fn.matchend(cursor_to_end, '^\\k*'))
local length = opt.get_option('trigger_keyword_length')
-- reset completion when deleting character in insert mode
if #prefix < M.prefixLength and vim.fn.pumvisible() == 0 then
manager.chainIndex = 1
-- not sure if I should clear cache here
complete.clearCache()
-- api.nvim_input("<c-g><c-g>")
if opt.get_option('trigger_on_delete') == 1 then
M.triggerCompletion(false)
end
M.stop_complete = false
end
M.prefixLength = #prefix
-- force reset chain completion
if (#prefix < length) then
complete.clearCache()
manager.chainIndex = 1
M.stop_complete = false
manager.changeSource = false
end
if (#prefix == 0) then
complete.clearCache()
end
-- stop auto completion when all sources return no complete-items
if M.stop_complete == true then return end
triggerCurrentCompletion(bufnr, line_to_cursor, prefix, textMatch, suffix)
end
-- provide api for custom complete items
function M.addCompleteItems(key, complete_item)
complete_items_map[key] = complete_item
end
function M.nextCompletion()
if manager.chainIndex ~= #M.chain_complete_list then
manager.chainIndex = manager.chainIndex + 1
else
manager.chainIndex = 1
end
end
function M.prevCompletion()
if manager.chainIndex ~= 1 then
manager.chainIndex = manager.chainIndex - 1
else
manager.chainIndex = #M.chain_complete_list
end
end
function M.checkHealth()
chain_completion.checkHealth(complete_items_map)
end
return M
================================================
FILE: lua/completion/util.lua
================================================
------------------------------------------------------------------------
-- utility function that are modified from neovim's source --
------------------------------------------------------------------------
local vim = vim
local api = vim.api
local opt = require 'completion.option'
local M = {}
function M.is_list(thing)
return vim.fn.type(thing) == api.nvim_get_vvar("t_list")
end
------------------------
-- completion items --
------------------------
local function compare_strings(a, b)
if opt.get_option("sorting") == 'alphabet' then
return a.word < b.word
elseif opt.get_option("sorting") == 'length_desc' then
return string.len(a.word) > string.len(b.word)
else
return string.len(a.word) < string.len(b.word)
end
end
local function compare_scores_then_strings(a, b)
if a.score == b.score or a.score == nil or b.score == nil then
return compare_strings(a, b);
end
return a.score < b.score
end
function M.sort_completion_items(items)
table.sort(items, function(a, b)
if a.priority == b.priority or a.priority == nil or b.priority == nil then
return compare_scores_then_strings(a, b)
end
return a.priority > b.priority
end)
end
function M.addCompletionItems(item_table, item)
-- word cannot be nil
if item.word == nil then return end
local abbr_length = opt.get_option('abbr_length')
local menu_length = opt.get_option('menu_length')
if menu_length ~= 0 then
if item.menu ~= nil and string.len(item.menu) > menu_length then
item.menu = string.sub(item.menu, 0, menu_length).."..."
end
end
if item.abbr ~= nil and abbr_length ~= 0 then
if string.len(item.abbr) > abbr_length then
item.abbr = string.sub(item.abbr, 0, abbr_length).."..."
end
end
table.insert(item_table, {
word = item.word,
abbr = item.abbr or '',
kind = item.kind or '',
menu = item.menu or '',
info = item.info or '',
priority = item.priority or 1,
icase = 1,
dup = item.dup or 1,
empty = 1,
user_data = item.user_data or {},
})
end
-- Levenshtein algorithm for fuzzy matching
-- https://gist.github.com/james2doyle/e406180e143da3bdd102
function M.fuzzy_score(str1, str2)
local len1 = #str1
local len2 = #str2
local matrix = {}
local cost
local min = math.min;
-- quick cut-offs to save time
if (len1 == 0) then
return len2
elseif (len2 == 0) then
return len1
elseif (str1 == str2) then
return 0
end
-- initialise the base matrix values
for i = 0, len1, 1 do
matrix[i] = {}
matrix[i][0] = i
end
for j = 0, len2, 1 do
matrix[0][j] = j
end
-- actual Levenshtein algorithm
for i = 1, len1, 1 do
for j = 1, len2, 1 do
if (str1:byte(i) == str2:byte(j)) then
cost = 0
else
cost=1
end
matrix[i][j] = min(matrix[i-1][j] + 2, matrix[i][j-1], matrix[i-1][j-1] + cost)
end
end
-- return the last value - this is the Levenshtein distance
return matrix[len1][len2]
end
-- Check trigger character
M.checkTriggerCharacter = function(line_to_cursor, trigger_character)
if trigger_character == nil then return end
for _, ch in ipairs(trigger_character) do
local current_char = string.sub(line_to_cursor, #line_to_cursor-#ch+1, #line_to_cursor)
if current_char == ch then
return true
end
end
return false
end
return M
================================================
FILE: lua/completion.lua
================================================
local vim = vim
local api = vim.api
local match = require'completion.matching'
local source = require 'completion.source'
local signature = require'completion.signature_help'
local hover = require'completion.hover'
local opt = require'completion.option'
local manager = require'completion.manager'
local M = {}
------------------------------------------------------------------------
-- external commands --
------------------------------------------------------------------------
M.insertCompletionItems = function(completed_items, prefix, item)
match.matching(completed_items, prefix, item)
end
M.addCompletionSource = function(key, completed_item)
source.addCompleteItems(key, completed_item)
end
M.nextSource = function()
source.nextCompletion()
end
M.prevSource = function()
source.prevCompletion()
end
M.triggerCompletion = function()
source.triggerCompletion(true, manager)
end
M.completionToggle = function()
local enable = vim.b.completion_enable
if enable == nil then
M.on_attach()
elseif enable == 0 then
vim.b.completion_enable = 1
else
vim.b.completion_enable = 0
end
end
------------------------------------------------------------------------
-- smart tab --
------------------------------------------------------------------------
function M.smart_tab()
if vim.fn.pumvisible() ~= 0 then
api.nvim_eval([[feedkeys("\<c-n>", "n")]])
return
end
local col = vim.fn.col('.') - 1
if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
api.nvim_eval([[feedkeys("\<tab>", "n")]])
return
end
source.triggerCompletion(true, manager)
end
function M.smart_s_tab()
if vim.fn.pumvisible() ~= 0 then
api.nvim_eval([[feedkeys("\<c-p>", "n")]])
return
end
api.nvim_eval([[feedkeys("\<s-tab>", "n")]])
end
------------------------------------------------------------------------
-- confirm completion --
------------------------------------------------------------------------
-- I want to deprecate this...
local function autoAddParens(completed_item)
if completed_item.kind == nil then return end
if string.match(completed_item.kind, '.*Function.*') ~= nil or string.match(completed_item.kind, '.*Method.*') then
api.nvim_input("()<left>")
end
end
-- Workaround to avoid expand snippets when not confirm
-- confirmCompletion is now triggered by CompleteDone autocmd to solve issue with noselect
-- Will cause snippets to expand with not pressing confirm key
-- Add a flag completionConfirm to avoid this issue
function M.confirmCompletion(completed_item)
manager.confirmedCompletion = true
end
-- apply additionalTextEdits in LSP specs
local function applyAddtionalTextEdits(completed_item)
local lnum = api.nvim_win_get_cursor(0)[1]
if completed_item.user_data.lsp ~= nil then
local item = completed_item.user_data.lsp.completion_item
-- vim-vsnip have better additional text edits...
if vim.fn.exists('g:loaded_vsnip_integ') == 1 then
api.nvim_call_function('vsnip_integ#do_complete_done', {
{
completed_item = completed_item,
completion_item = item,
apply_additional_text_edits = true
}
})
else
if item.additionalTextEdits then
local bufnr = api.nvim_get_current_buf()
local edits = vim.tbl_filter(
function(x) return x.range.start.line ~= (lnum - 1) end,
item.additionalTextEdits
)
vim.lsp.util.apply_text_edits(edits, bufnr)
end
end
end
end
-- handle completeDone stuff here
local function hasConfirmedCompletion()
local completed_item = api.nvim_get_vvar('completed_item')
if completed_item.user_data == nil then return end
if completed_item.user_data.lsp ~= nil then
applyAddtionalTextEdits(completed_item)
if opt.get_option('enable_snippet') == "snippets.nvim" then
require 'snippets'.expand_at_cursor(completed_item.user_data.actual_item, completed_item.word)
end
end
if opt.get_option('enable_auto_paren') == 1 then
autoAddParens(completed_item)
end
if completed_item.user_data.snippet_source == 'UltiSnips' then
api.nvim_call_function('UltiSnips#ExpandSnippet', {})
elseif completed_item.user_data.snippet_source == 'Neosnippet' then
api.nvim_input("<c-r>".."=neosnippet#expand('"..completed_item.word.."')".."<CR>")
elseif completed_item.user_data.snippet_source == 'vim-vsnip' then
api.nvim_call_function('vsnip#anonymous', {
table.concat(completed_item.user_data.snippet_body, "\n"),
{
prefix = completed_item.word
}
})
elseif completed_item.user_data.snippet_source == 'snippets.nvim' then
require'snippets'.expand_at_cursor()
end
end
------------------------------------------------------------------------
-- autocommands --
------------------------------------------------------------------------
function M.on_InsertCharPre()
manager.insertChar = true
manager.textHover = true
manager.selected = -1
end
function M.on_InsertLeave()
manager.insertLeave = true
end
-- TODO: need further refactor, very messy now:(
function M.on_InsertEnter()
local enable = vim.b.completion_enable
if enable == nil or enable == 0 then
return
end
local timer = vim.loop.new_timer()
-- setup variable
manager.init()
-- TODO: remove this
local autoChange = false
if opt.get_option('auto_change_source') == 1 then
autoChange = true
end
-- reset source
manager.chainIndex = 1
source.stop_complete = false
local l_complete_index = manager.chainIndex
local timer_cycle = opt.get_option('timer_cycle')
timer:start(100, timer_cycle, vim.schedule_wrap(function()
local l_changedTick = api.nvim_buf_get_changedtick(0)
-- complete if changes are made
if l_changedTick ~= manager.changedTick then
manager.changedTick = l_changedTick
if opt.get_option('enable_auto_popup') == 1 then
source.autoCompletion()
end
if opt.get_option('enable_auto_hover') == 1 then
hover.autoOpenHoverInPopup(manager)
end
if opt.get_option('enable_auto_signature') == 1 then
signature.autoOpenSignatureHelp()
end
end
-- change source if no item is available
if manager.changeSource and autoChange then
manager.changeSource = false
if manager.chainIndex ~= source.chain_complete_length then
manager.chainIndex = manager.chainIndex + 1
l_complete_index = manager.chainIndex
manager.insertChar = true
source.triggerCompletion(false, manager)
else
source.stop_complete = true
end
end
-- force trigger completion when manaully chaging source
if l_complete_index ~= manager.chainIndex then
-- force clear completion
if vim.api.nvim_get_mode()['mode'] == 'i' or vim.api.nvim_get_mode()['mode'] == 'ic' then
vim.fn.complete(vim.api.nvim_win_get_cursor(0)[2], {})
end
source.triggerCompletion(false, manager)
l_complete_index = manager.chainIndex
end
-- closing timer if leaving insert mode
if manager.insertLeave == true and timer:is_closing() == false then
timer:stop()
timer:close()
end
end))
end
-- handle completion confirmation and dismiss hover popup
function M.on_CompleteDone()
if manager.confirmedCompletion then
manager.confirmedCompletion = false
hasConfirmedCompletion()
-- auto trigger signature help when we confirm completion
if vim.g.completion_enable_auto_signature ~= 0 then
signature.autoOpenSignatureHelp()
end
end
if hover.winnr ~= nil and api.nvim_win_is_valid(hover.winnr) then
api.nvim_win_close(hover.winnr, true)
end
end
M.on_attach = function(option)
-- setup completion_option tables
opt.set_option_table(option)
local disable_filetypes = opt.get_option("disable_filetypes")
local ft = vim.bo.filetype
for _, disable_ft in ipairs(disable_filetypes) do
if ft == disable_ft then
return
end
end
-- setup autocommand
-- TODO: Modified this if lua callbacks for autocmd is merged
api.nvim_command("augroup CompletionCommand")
api.nvim_command("autocmd! * <buffer>")
api.nvim_command("autocmd InsertEnter <buffer> lua require'completion'.on_InsertEnter()")
api.nvim_command("autocmd InsertLeave <buffer> lua require'completion'.on_InsertLeave()")
api.nvim_command("autocmd InsertCharPre <buffer> lua require'completion'.on_InsertCharPre()")
api.nvim_command("autocmd CompleteDone <buffer> lua require'completion'.on_CompleteDone()")
api.nvim_command("augroup end")
if string.len(opt.get_option('confirm_key')) ~= 0 then
api.nvim_buf_set_keymap(0, 'i', opt.get_option('confirm_key'),
'pumvisible() ? complete_info()["selected"] != "-1" ? "\\<Plug>(completion_confirm_completion)" :'..
' "\\<c-e>\\<CR>" : "\\<CR>"',
{silent=false, noremap=false, expr=true})
end
vim.b.completion_enable = 1
end
return M
================================================
FILE: plugin/completion.vim
================================================
" Last Change: 2020 avr 01
if exists('g:loaded_completion') | finish | endif
let s:save_cpo = &cpo
set cpo&vim
if ! exists('g:completion_enable_snippet')
let g:completion_enable_snippet = v:null
endif
if ! exists('g:completion_confirm_key')
let g:completion_confirm_key = "\<CR>"
endif
if ! exists('g:completion_confirm_key_rhs')
let g:completion_confirm_key_rhs = ''
endif
if ! exists('g:completion_enable_auto_paren')
let g:completion_enable_auto_paren = 0
endif
if ! exists('g:completion_enable_auto_hover')
let g:completion_enable_auto_hover = 1
endif
if ! exists('g:completion_docked_hover')
let g:completion_docked_hover = 0
endif
if ! exists('g:completion_docked_minimum_size')
let g:completion_docked_minimum_size = 5
endif
if ! exists('g:completion_docked_maximum_size')
let g:completion_docked_maximum_size = 20
endif
if ! exists('g:completion_enable_focusable_hover')
let g:completion_enable_focusable_hover = 0
endif
if ! exists('g:completion_enable_auto_signature')
let g:completion_enable_auto_signature = 1
endif
if ! exists('g:completion_trigger_character')
let g:completion_trigger_character = []
endif
if ! exists('g:completion_enable_server_trigger')
let g:completion_enable_server_trigger = 1
endif
if ! exists('g:completion_enable_auto_popup')
let g:completion_enable_auto_popup = 1
endif
if ! exists('g:completion_trigger_on_delete')
let g:completion_trigger_on_delete = 0
end
if ! exists('g:completion_trigger_keyword_length')
let g:completion_trigger_keyword_length = 1
endif
if ! exists('g:completion_auto_change_source')
let g:completion_auto_change_source = 0
endif
if !exists('g:completion_timer_cycle')
let g:completion_timer_cycle = 80
endif
if ! exists('g:completion_sorting')
let g:completion_sorting = 'alphabet'
endif
if ! exists('g:completion_fuzzy_match')
let g:completion_enable_fuzzy_match = 0
endif
if ! exists('g:completion_expand_characters')
let g:completion_expand_characters = [' ', '\t', '>', ';']
endif
if ! exists('g:completion_matching_ignore_case')
let g:completion_matching_ignore_case = &ignorecase
endif
if ! exists('g:completion_matching_smart_case')
let g:completion_matching_smart_case = &smartcase
endif
if ! exists('g:completion_disable_filetypes')
let g:completion_disable_filetypes = []
endif
if ! exists('g:completion_matching_strategy_list')
let g:completion_matching_strategy_list = ['exact']
endif
if ! exists('g:completion_chain_complete_list')
let g:completion_chain_complete_list = {
\ 'default' : {
\ 'default': [
\ {'complete_items': ['lsp', 'snippet']},
\ {'mode': '<c-p>'},
\ {'mode': '<c-n>'}],
\ 'comment': []
\ }
\}
endif
if ! exists('g:completion_customize_lsp_label')
let g:completion_customize_lsp_label = {}
endif
if ! exists('g:completion_items_priority')
let g:completion_items_priority = {}
endif
if ! exists('g:completion_abbr_length')
let g:completion_abbr_length = 0
endif
if ! exists('g:completion_menu_length')
let g:completion_menu_length = 0
endif
if ! exists('g:completion_items_duplicate')
let g:completion_items_duplicate = {}
endif
inoremap <silent> <Plug>(completion_confirm_completion)
\ <cmd>call completion#wrap_completion()<CR>
inoremap <silent> <Plug>(completion_next_source)
\ <cmd>lua require'completion'.nextSource()<CR>
inoremap <silent> <Plug>(completion_prev_source)
\ <cmd>lua require'completion'.prevSource()<CR>
inoremap <silent> <Plug>(completion_smart_tab)
\ <cmd>lua require'completion'.smart_tab()<CR>
inoremap <silent> <Plug>(completion_smart_s_tab)
\ <cmd>lua require'completion'.smart_s_tab()<CR>
inoremap <silent> <Plug>(completion_trigger)
\ <cmd>lua require'completion'.triggerCompletion()<CR>
command! -nargs=0 CompletionToggle lua require'completion'.completionToggle()
let &cpo = s:save_cpo
unlet s:save_cpo
let g:loaded_completion = 1
gitextract_3s7g_cxh/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .luacheckrc
├── .travis.yml
├── LICENSE
├── README.md
├── autoload/
│ ├── completion.vim
│ └── health/
│ └── completion_nvim.vim
├── doc/
│ └── completion-nvim.txt
├── lua/
│ ├── completion/
│ │ ├── chain_completion.lua
│ │ ├── complete.lua
│ │ ├── health.lua
│ │ ├── hover.lua
│ │ ├── manager.lua
│ │ ├── matching.lua
│ │ ├── option.lua
│ │ ├── signature_help.lua
│ │ ├── source/
│ │ │ ├── ins_complete.lua
│ │ │ ├── lsp.lua
│ │ │ ├── path.lua
│ │ │ └── snippet.lua
│ │ ├── source.lua
│ │ └── util.lua
│ └── completion.lua
└── plugin/
└── completion.vim
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (109K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 472,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n- Please read th"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 502,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".gitignore",
"chars": 14,
"preview": "doc/tags\n.vim\n"
},
{
"path": ".luacheckrc",
"chars": 23,
"preview": "globals = {\n \"vim\",\n}\n"
},
{
"path": ".travis.yml",
"chars": 336,
"preview": "language: generic\n\nos:\n - linux\n\nbefore_install:\n - sudo apt-get update\n - sudo apt-get install\n - sudo apt install "
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 9368,
"preview": "## WARNING: completion.nvim is no longer maintained\n\nIf you are looking for an autocompletion plugin, the neovim LSP tea"
},
{
"path": "autoload/completion.vim",
"chars": 1427,
"preview": "\" Perform a Hack to confirm completion\nfunction! completion#completion_confirm() abort\n lua require'completion'.confi"
},
{
"path": "autoload/health/completion_nvim.vim",
"chars": 101,
"preview": "function! health#completion_nvim#check()\n lua require 'completion.health'.checkHealth()\nendfunction\n"
},
{
"path": "doc/completion-nvim.txt",
"chars": 13755,
"preview": "*completion-nvim.txt*\nAsync completion framework that aims to provide completion for neovim's\nbuilt-in LSP, written in L"
},
{
"path": "lua/completion/chain_completion.lua",
"chars": 3810,
"preview": "local vim = vim\nlocal api = vim.api\nlocal util = require 'completion.util'\nlocal opt = require 'completion.option'\nlocal"
},
{
"path": "lua/completion/complete.lua",
"chars": 4484,
"preview": "local vim = vim\nlocal api = vim.api\nlocal util = require 'completion.util'\nlocal ins = require 'completion.source.ins_co"
},
{
"path": "lua/completion/health.lua",
"chars": 1867,
"preview": "local vim = vim\nlocal api = vim.api\nlocal source = require 'completion.source'\nlocal opt = require 'completion.option'\n\n"
},
{
"path": "lua/completion/hover.lua",
"chars": 11610,
"preview": "-- define some hover related function modified from neovim source code\nlocal vim = vim\nlocal validate = vim.validate\nloc"
},
{
"path": "lua/completion/manager.lua",
"chars": 2397,
"preview": "local manager = {}\n\n------------------------------------------------------------------------\n-- plugi"
},
{
"path": "lua/completion/matching.lua",
"chars": 1867,
"preview": "local vim = vim\nlocal util = require 'completion.util'\nlocal opt = require 'completion.option'\nlocal M = {}\n\nlocal funct"
},
{
"path": "lua/completion/option.lua",
"chars": 521,
"preview": "local M = {}\n\n\n-- fallback to using global variable as default\nlocal completion_opt_metatable = {\n __index = function(_"
},
{
"path": "lua/completion/signature_help.lua",
"chars": 2240,
"preview": "local vim = vim\nlocal validate = vim.validate\nlocal api = vim.api\nlocal util = require 'completion.util'\nlocal M = {}\n\n-"
},
{
"path": "lua/completion/source/ins_complete.lua",
"chars": 1560,
"preview": "-- luacheck: globals vim\nlocal vim = vim\nlocal api = vim.api\nlocal manager = require 'completion.manager'\nlocal M = {}\n\n"
},
{
"path": "lua/completion/source/lsp.lua",
"chars": 5072,
"preview": "local vim = vim\nlocal protocol = require 'vim.lsp.protocol'\nlocal util = require 'completion.util'\nlocal match = require"
},
{
"path": "lua/completion/source/path.lua",
"chars": 2365,
"preview": "local M = {}\nlocal vim = vim\nlocal loop = vim.loop\nlocal api = vim.api\nlocal util = require 'completion.util'\nlocal opt "
},
{
"path": "lua/completion/source/snippet.lua",
"chars": 4718,
"preview": "local vim = vim\nlocal api = vim.api\nlocal match = require'completion.matching'\nlocal opt = require 'completion.option'\nl"
},
{
"path": "lua/completion/source.lua",
"chars": 7106,
"preview": "local vim = vim\nlocal api = vim.api\nlocal util = require 'completion.util'\nlocal complete = require 'completion.complete"
},
{
"path": "lua/completion/util.lua",
"chars": 3420,
"preview": "------------------------------------------------------------------------\n-- utility function that are modified from"
},
{
"path": "lua/completion.lua",
"chars": 9152,
"preview": "local vim = vim\nlocal api = vim.api\nlocal match = require'completion.matching'\nlocal source = require 'completion.source"
},
{
"path": "plugin/completion.vim",
"chars": 4107,
"preview": "\" Last Change: 2020 avr 01\n\nif exists('g:loaded_completion') | finish | endif\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nif ! e"
}
]
About this extraction
This page contains the full source code of the haorenW1025/completion-nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (101.2 KB), approximately 25.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.