Browse Source

vim: update coc.nvim

master
Maxim Likhachev 3 years ago
parent
commit
1832ed7095
  1. 32
      etc/soft/nvim/+plugins/aw-watcher-vim/README.md
  2. 163
      etc/soft/nvim/+plugins/aw-watcher-vim/plugin/activitywatch.vim
  3. 10
      etc/soft/nvim/+plugins/coc.nvim/CONTRIBUTING.md
  4. 582
      etc/soft/nvim/+plugins/coc.nvim/README.md
  5. 144
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc.vim
  6. 154
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/api.vim
  7. 43
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/client.vim
  8. 436
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/color.vim
  9. 44
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/compat.vim
  10. 6
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/cursor.vim
  11. 667
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/dialog.vim
  12. 32
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/dict.vim
  13. 1391
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/float.vim
  14. 533
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/highlight.vim
  15. 53
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/list.vim
  16. 11
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/math.vim
  17. 532
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/notify.vim
  18. 18
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/prompt.vim
  19. 583
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/pum.vim
  20. 2
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/rpc.vim
  21. 113
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/snippet.vim
  22. 126
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/string.vim
  23. 473
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/ui.vim
  24. 878
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/util.vim
  25. 36
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/vtext.vim
  26. 118
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/window.vim
  27. 12
      etc/soft/nvim/+plugins/coc.nvim/autoload/health/coc.vim
  28. 114468
      etc/soft/nvim/+plugins/coc.nvim/build/index.js
  29. 2167
      etc/soft/nvim/+plugins/coc.nvim/data/schema.json
  30. 1754
      etc/soft/nvim/+plugins/coc.nvim/doc/coc-config.txt
  31. 2774
      etc/soft/nvim/+plugins/coc.nvim/doc/coc.txt
  32. 15
      etc/soft/nvim/+plugins/coc.nvim/esbuild.js
  33. 358
      etc/soft/nvim/+plugins/coc.nvim/history.md
  34. 8
      etc/soft/nvim/+plugins/coc.nvim/jest.js
  35. 85
      etc/soft/nvim/+plugins/coc.nvim/lua/coc/highlight.lua
  36. 48
      etc/soft/nvim/+plugins/coc.nvim/package.json
  37. 334
      etc/soft/nvim/+plugins/coc.nvim/plugin/coc.vim
  38. 17
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/autoload/coc/source/email.vim
  39. 177
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/configuration.test.ts
  40. 139
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/connection.test.ts
  41. 99
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/converter.test.ts
  42. 334
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/diagnostics.test.ts
  43. 393
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/dynamic.test.ts
  44. 1658
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/features.test.ts
  45. 132
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/fileSystemWatcher.test.ts
  46. 654
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/integration.test.ts
  47. 99
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/progressPart.test.ts
  48. 36
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/configServer.js
  49. 13
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/crashOnShutdownServer.js
  50. 17
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/crashServer.js
  51. 21
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/customServer.js
  52. 118
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/diagnosticServer.js
  53. 221
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/dynamicServer.js
  54. 14
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/errorServer.js
  55. 104
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/eventServer.js
  56. 51
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/fileWatchServer.js
  57. 12
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/nullServer.js
  58. 16
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/startStopServer.js
  59. 102
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testDocuments.js
  60. 35
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testFileWatcher.js
  61. 36
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testInitializeResult.js
  62. 602
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testServer.js
  63. 13
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/timeoutOnShutdownServer.js
  64. 324
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/textSynchronization.test.ts
  65. 103
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/utils.test.ts
  66. 106
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/workspaceFolder.test.ts
  67. 4
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/coc-settings.json
  68. 1325
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/basic.test.ts
  69. 179
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/float.test.ts
  70. 617
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/language.test.ts
  71. 120
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/sources.test.ts
  72. 317
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/util.test.ts
  73. 1126
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/configurationModel.test.ts
  74. 424
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/configurations.test.ts
  75. 12
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/settings.json
  76. 218
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/util.test.ts
  77. 68
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/autocmds.test.ts
  78. 143
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/documents.test.ts
  79. 169
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/editors.test.ts
  80. 431
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/fileSystemWatcher.test.ts
  81. 840
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/files.test.ts
  82. 120
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/funcs.test.ts
  83. 79
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/keymaps.test.ts
  84. 148
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/locations.test.ts
  85. 145
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/terminals.test.ts
  86. 117
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/ui.test.ts
  87. 315
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/workspaceFolder.test.ts
  88. 401
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/callHierarchy.test.ts
  89. 449
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codeActions.test.ts
  90. 324
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codelens.test.ts
  91. 295
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/colors.test.ts
  92. 82
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/commands.test.ts
  93. 113
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/fold.test.ts
  94. 285
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/format.test.ts
  95. 166
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/highlights.test.ts
  96. 238
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/hover.test.ts
  97. 93
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/index.test.ts
  98. 410
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/inlayHint.test.ts
  99. 63
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/inlineValue.test.ts
  100. 169
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/linkedEditing.test.ts
  101. Some files were not shown because too many files have changed in this diff Show More

32
etc/soft/nvim/+plugins/aw-watcher-vim/README.md

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
aw-watcher-vim
==============
### Installation
This plugin depends on curl, so make sure that it's installed and available in your PATH
It is recommended to have a vim runtime manager to make it easier to install (such as Pathogen or Vundle)
Then simply clone this repository to the bundle folder in your vim config folder (usually `~/.vim/bundle` or `~/.config/nvim/bundle` for neovim)
### Usage
Once installed in the bundle directory, it should load automatically if you have a vim runtime manager
```
:AWStart - start logging if not already logging
:AWStop - stop logging if logging
:AWStatus - verify that the watcher is running
```
If aw-watcher-vim loses connection it will give you an error message and stop logging. You then need to either run :AWStart or restart vim to start logging again
### Configuration
The following global variables are available:
| Variable Name | Description | Default Value |
|--------------------|------------------------------------------------|---------------|
| `g:aw_apiurl_host` | Sets the _host_ of the Api Url | `127.0.0.1` |
| `g:aw_apiurl_port` | Sets the _port_ of the Api Url | `5600` |
| `g:aw_api_timeout` | Sets the _timeout_ seconds of the Api request | `2.0` |

163
etc/soft/nvim/+plugins/aw-watcher-vim/plugin/activitywatch.vim

@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
if exists("g:loaded_activitywatch")
finish
endif
let g:loaded_activitywatch = 1
" compatibility mode which set this script to run with default vim settings
let s:save_cpo = &cpo
set cpo&vim
let s:nvim = has('nvim')
let s:last_heartbeat = localtime()
let s:file = ''
let s:language = ''
let s:project = ''
let s:connected = 0
let s:apiurl_host = get(g:, 'aw_apiurl_host', '127.0.0.1')
let s:apiurl_port = get(g:, 'aw_apiurl_port', '5600')
let s:api_timeout = get(g:, 'aw_api_timeout', 2)
let s:base_apiurl = printf('http://%s:%s/api/0', s:apiurl_host, s:apiurl_port)
let s:hostname = get(g:, 'aw_hostname', hostname())
let s:bucketname = printf('aw-watcher-vim_%s', s:hostname)
let s:bucket_apiurl = printf('%s/buckets/%s', s:base_apiurl, s:bucketname)
let s:heartbeat_apiurl = printf('%s/heartbeat?pulsetime=30', s:bucket_apiurl)
" dict of all responses
" the key is the jobid and the value the HTTP status code
let s:http_response_code = {}
function! HTTPPostJson(url, data)
let l:req = ['curl', '-s', a:url,
\ '-H', 'Content-Type: application/json',
\ '-X', 'POST',
\ '-d', json_encode(a:data),
\ '-o', '/dev/null',
\ '-m', s:api_timeout,
\ '-w', "%{http_code}"]
if s:nvim
let l:req_job = jobstart(l:req,
\ {"detach": 1,
\ "on_stdout": "HTTPPostOnStdoutNeovim",
\ "on_exit": "HTTPPostOnExitNeovim",
\ })
else
let l:req_job = job_start(l:req,
\ {"out_cb": "HTTPPostOnStdoutVim",
\ "close_cb": "HTTPPostOnExitVim",
\ "in_mode": "raw",
\ })
endif
endfunc
function! HTTPPostOnExitNeovim(jobid, exitcode, eventtype)
let l:jobid_str = printf('%d', a:jobid)
let l:status_code = str2nr(s:http_response_code[l:jobid_str][0])
call HTTPPostOnExit(l:jobid_str, l:status_code)
endfunc
function! HTTPPostOnExitVim(jobmsg)
" cut out channelnum from string 'channel X running'
let l:jobid_str = substitute(a:jobmsg, '[ A-Za-z]*', '', "g")
let l:status_code = str2nr(s:http_response_code[l:jobid_str])
call HTTPPostOnExit(l:jobid_str, l:status_code)
endfunc
function! HTTPPostOnExit(jobid_str, status_code)
if a:status_code == 0
" We cannot connect to aw-server
echoerr "aw-watcher-vim: Failed to connect to aw-server, logging will be disabled. You can retry to connect with ':AWStart'"
let s:connected = 0
elseif a:status_code >= 100 && a:status_code < 300 || a:status_code == 304
" We are connected!
let s:connected = 1
else
" aw-server didn't like our request
echoerr printf("aw-watcher-vim: aw-server did not accept our request with status code %d. See aw-server logs for reason or stop aw-watcher-vim with :AWStop", a:status_code)
endif
" Cleanup response code
unlet s:http_response_code[a:jobid_str]
endfunc
function! HTTPPostOnStdoutVim(jobmsg, data)
" cut out channelnum from string 'channel X running'
let l:jobid_str = substitute(a:jobmsg, '[ A-Za-z]*', '', "g")
let s:http_response_code[l:jobid_str] = a:data
"echo printf('aw-watcher-vim job %d stdout: %s', l:jobid_str, json_encode(a:data))
endfunc
function! HTTPPostOnStdoutNeovim(jobid, data, event)
if a:data != ['']
let l:jobid_str = printf('%d', a:jobid)
let s:http_response_code[l:jobid_str] = a:data
"echo printf('aw-watcher-vim job %d stdout: %s', a:jobid, json_encode(a:data))
endif
endfunc
function! s:CreateBucket()
let l:body = {
\ 'name': s:bucketname,
\ 'hostname': s:hostname,
\ 'client': 'aw-watcher-vim',
\ 'type': 'app.editor.activity'
\}
call HTTPPostJson(s:bucket_apiurl, l:body)
endfunc
function! s:Heartbeat()
" Only send heartbeats if we can connect to aw-server
if s:connected < 1
return
endif
let l:duration = 0
let l:localtime = localtime()
let l:timestamp = strftime('%FT%H:%M:%S%z')
let l:file = expand('%p')
let l:language = &filetype
let l:project = getcwd()
" Only send heartbeat if data was changed or more than 1 second has passed
" since last heartbeat
if s:file != l:file ||
\ s:language != l:language ||
\ s:project != l:project ||
\ l:localtime - s:last_heartbeat > 1
let l:req_body = {
\ 'duration': 0,
\ 'timestamp': l:timestamp,
\ 'data': {
\ 'file': l:file,
\ 'language': l:language,
\ 'project': l:project
\ }
\}
call HTTPPostJson(s:heartbeat_apiurl, l:req_body)
let s:file = l:file
let s:language = l:language
let s:project = l:project
let s:last_heartbeat = l:localtime
endif
endfunc
function! AWStart()
call s:CreateBucket()
endfunc
function! AWStop()
let s:connected = 0
endfunc
augroup ActivityWatch
autocmd VimEnter * call AWStart()
autocmd BufEnter,CursorMoved,CursorMovedI * call s:Heartbeat()
autocmd CmdlineEnter,CmdlineChanged * call s:Heartbeat()
augroup END
command! AWHeartbeat call s:Heartbeat()
command! AWStart call AWStart()
command! AWStop call AWStop()
command! AWStatus echom printf('aw-watcher-vim running: %b', s:connected)
" reset compatibility mode
let &cpo = s:save_cpo

10
etc/soft/nvim/+plugins/coc.nvim/CONTRIBUTING.md

@ -37,8 +37,8 @@ Then in your terminal: @@ -37,8 +37,8 @@ Then in your terminal:
- `yarn install`
- Install [coc-tsserver](https://github.com/neoclide/coc-tsserver) by
`:CocInstall coc-tsserver` in your vim
- Install [coc-tslint-plugin](https://github.com/neoclide/coc-tslint-plugin) by
`:CocInstall coc-tslint-plugin` in your vim.
- Install [coc-eslint](https://github.com/neoclide/coc-eslint) by
`:CocInstall coc-eslint` in your vim.
And you should be ready to go!
@ -86,8 +86,8 @@ Once you've filed the PR: @@ -86,8 +86,8 @@ Once you've filed the PR:
- Barring special circumstances, maintainers will not review PRs until all checks pass (Travis, AppVeyor, etc).
- One or more maintainers will use GitHub's review feature to review your PR.
- If the maintainer asks for any changes, edit your changes, push, and ask for another review. Additional tags (such as `needs-tests`) will be added depending on the review.
- If the maintainer decides to pass on your PR, they will thank you for the contribution and explain why they won't be accepting the changes. That's ok! We still really appreciate you taking the time to do it, and we don't take that lightly. 💚
- If your PR gets accepted, it will be marked as such, and merged into the `latest` branch soon after. Your contribution will be distributed to the masses next time the maintainers [tag a release](#tag-a-release)
- If the maintainer decides not to pass on your PR, they will thank you for the contribution and explain why they won't be accepting the changes. Please don't feel offended. We still really appreciate you taking the time to do it, and we don't take that lightly. 💚
- If your PR gets accepted, it will be marked as such, and merged into the `latest` branch soon after. Your contribution will be distributed to the masses next time the maintainers tag a release
## Provide Support on Issues
@ -137,6 +137,6 @@ You can spot a collaborator on the repo by looking for the `[Collaborator]` or ` @@ -137,6 +137,6 @@ You can spot a collaborator on the repo by looking for the `[Collaborator]` or `
| Permission | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Issue Tracker | Granted to contributors who express a strong interest in spending time on the project's issue tracker. These tasks are mainly [labeling issues](#label-issues), [cleaning up old ones](#clean-up-issues-and-prs), and [reviewing pull requests](#review-pull-requests), as well as all the usual things non-team-member contributors can do. Issue handlers should not merge pull requests, tag releases, or directly commit code themselves: that should still be done through the usual pull request process. Becoming an Issue Handler means the project team trusts you to understand enough of the team's process and context to implement it on the issue tracker. |
| Issue Tracker | Granted to contributors who express a strong interest in spending time on the project's issue tracker. These tasks are mainly labeling issues, cleaning up old ones, and [reviewing pull requests](#review-pull-requests), as well as all the usual things non-team-member contributors can do. Issue handlers should not merge pull requests, tag releases, or directly commit code themselves: that should still be done through the usual pull request process. Becoming an Issue Handler means the project team trusts you to understand enough of the team's process and context to implement it on the issue tracker. |
| Committer | Granted to contributors who want to handle the actual pull request merges, tagging new versions, etc. Committers should have a good level of familiarity with the codebase, and enough context to understand the implications of various changes, as well as a good sense of the will and expectations of the project team. |
| Admin/Owner | Granted to people ultimately responsible for the project, its community, etc. |

582
etc/soft/nvim/+plugins/coc.nvim/README.md

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<p align="center">
<a href="https://www.vim.org/scripts/script.php?script_id=5779">
<img alt="Coc Logo" src="https://user-images.githubusercontent.com/251450/55009068-f4ed2780-501c-11e9-9a3b-cf3aa6ab9272.png" height="160" />
<img alt="Logo" src="https://alfs.chigua.cn/dianyou/data/platform/default/20220525/coc.png" height="240" />
</a>
<p align="center">Make your Vim/Neovim as smart as VSCode.</p>
<p align="center">
@ -14,38 +14,22 @@ @@ -14,38 +14,22 @@
---
<img alt="Gif" src="https://user-images.githubusercontent.com/251450/55285193-400a9000-53b9-11e9-8cff-ffe4983c5947.gif" width="60%" />
<img alt="Gif" src="https://alfs.chigua.cn/dianyou/data/platform/default/20220801/2022-08-01%2002-14-03.2022-08-01%2002_15_16.gif" width="60%" />
_True snippet and additional text editing support_
_Custom popup menu with snippet support_
## Why?
- 🚀 **Fast**: [instant increment completion](https://github.com/neoclide/coc.nvim/wiki/Completion-with-sources), increment buffer sync using buffer update events.
- 🚀 **Fast**: separated NodeJS process that does not block your vim most of the time.
- 💎 **Reliable**: typed language, tested with CI.
- 🌟 **Featured**: [full LSP support](https://github.com/neoclide/coc.nvim/wiki/Language-servers#supported-features)
- 🌟 **Featured**: all LSP 3.16 features are supported, see `:h coc-lsp`.
- ❤ **Flexible**: [configured like VSCode](https://github.com/neoclide/coc.nvim/wiki/Using-the-configuration-file), [extensions work like in VSCode](https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions)
**Gold Sponsors**
<a href="https://opencollective.com/cocnvim#platinum-sponsors">
<img src="https://opencollective.com/cocnvim/tiers/gold-sponsors.svg?avatarHeight=36&width=600">
</a>
**Silver Sponsors**
<a href="https://opencollective.com/cocnvim#platinum-sponsors">
<img src="https://opencollective.com/cocnvim/tiers/silver-sponsors.svg?avatarHeight=36&width=600">
</a>
**Bronze Sponsors**
<a href="https://opencollective.com/cocnvim#platinum-sponsors">
<img src="https://opencollective.com/cocnvim/tiers/bronze-sponsors.svg?avatarHeight=36&width=600">
</a>
## Quick Start
Install [nodejs](https://nodejs.org/en/download/) >= 12.12:
Make sure use vim >= 8.1.1719 or neovim >= 0.4.0.
Install [nodejs](https://nodejs.org/en/download/) >= 14.14:
```bash
curl -sL install-node.vercel.app/lts | bash
@ -110,46 +94,35 @@ possible to avoid conflict with your other plugins. @@ -110,46 +94,35 @@ possible to avoid conflict with your other plugins.
command like`:verbose imap <tab>` to make sure that your keymap has taken effect.
```vim
" Set internal encoding of vim, not needed on neovim, since coc.nvim using some
" unicode characters in the file autoload/float.vim
set encoding=utf-8
" TextEdit might fail if hidden is not set.
set hidden
" Some servers have issues with backup files, see #649.
set nobackup
set nowritebackup
" Give more space for displaying messages.
set cmdheight=2
" Having longer updatetime (default is 4000 ms = 4 s) leads to noticeable
" delays and poor user experience.
set updatetime=300
" Don't pass messages to |ins-completion-menu|.
set shortmess+=c
" Always show the signcolumn, otherwise it would shift the text each time
" diagnostics appear/become resolved.
if has("nvim-0.5.0") || has("patch-8.1.1564")
" Recently vim can merge signcolumn and number column into one
set signcolumn=number
else
set signcolumn=yes
endif
set signcolumn=yes
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: There's always complete item selected by default, you may want to enable
" no select by `"suggest.noselect": true` in your configuration file.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <TAB>
\ pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<TAB>" :
\ coc#pum#visible() ? coc#pum#next(1) :
\ CheckBackspace() ? "\<Tab>" :
\ coc#refresh()
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>"
inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
function! s:check_back_space() abort
function! CheckBackspace() abort
let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s'
endfunction
@ -161,11 +134,6 @@ else @@ -161,11 +134,6 @@ else
inoremap <silent><expr> <c-@> coc#refresh()
endif
" Make <CR> auto-select the first completion item and notify coc.nvim to
" format on enter, <cr> could be remapped by other vim plugin
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
\: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
@ -178,15 +146,13 @@ nmap <silent> gi <Plug>(coc-implementation) @@ -178,15 +146,13 @@ nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)
" Use K to show documentation in preview window.
nnoremap <silent> K :call <SID>show_documentation()<CR>
nnoremap <silent> K :call ShowDocumentation()<CR>
function! s:show_documentation()
if (index(['vim','help'], &filetype) >= 0)
execute 'h '.expand('<cword>')
elseif (coc#rpc#ready())
function! ShowDocumentation()
if CocAction('hasProvider', 'hover')
call CocActionAsync('doHover')
else
execute '!' . &keywordprg . " " . expand('<cword>')
call feedkeys('K', 'in')
endif
endfunction
@ -280,6 +246,194 @@ nnoremap <silent><nowait> <space>k :<C-u>CocPrev<CR> @@ -280,6 +246,194 @@ nnoremap <silent><nowait> <space>k :<C-u>CocPrev<CR>
nnoremap <silent><nowait> <space>p :<C-u>CocListResume<CR>
```
## Example lua configuration
NOTE: This works in Neovim 0.7.0dev+ only.
```lua
-- Some servers have issues with backup files, see #649.
vim.opt.backup = false
vim.opt.writebackup = false
-- Having longer updatetime (default is 4000 ms = 4 s) leads to noticeable
-- delays and poor user experience.
vim.opt.updatetime = 300
-- Always show the signcolumn, otherwise it would shift the text each time
-- diagnostics appear/become resolved.
vim.opt.signcolumn = "yes"
local keyset = vim.keymap.set
-- Auto complete
function _G.check_back_space()
local col = vim.fn.col('.') - 1
return col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') ~= nil
end
-- Use tab for trigger completion with characters ahead and navigate.
-- NOTE: There's always complete item selected by default, you may want to enable
-- no select by `"suggest.noselect": true` in your configuration file.
-- NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
-- other plugin before putting this into your config.
local opts = {silent = true, noremap = true, expr = true, replace_keycodes = false}
keyset("i", "<TAB>", 'coc#pum#visible() ? coc#pum#next(1) : v:lua.check_back_space() ? "<TAB>" : coc#refresh()', opts)
keyset("i", "<S-TAB>", [[coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"]], opts)
-- Make <CR> to accept selected completion item or notify coc.nvim to format
-- <C-g>u breaks current undo, please make your own choice.
keyset("i", "<cr>", [[coc#pum#visible() ? coc#pum#confirm() : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"]], opts)
-- Use <c-j> to trigger snippets
keyset("i", "<c-j>", "<Plug>(coc-snippets-expand-jump)")
-- Use <c-space> to trigger completion.
keyset("i", "<c-space>", "coc#refresh()", {silent = true, expr = true})
-- Use `[g` and `]g` to navigate diagnostics
-- Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
keyset("n", "[g", "<Plug>(coc-diagnostic-prev)", {silent = true})
keyset("n", "]g", "<Plug>(coc-diagnostic-next)", {silent = true})
-- GoTo code navigation.
keyset("n", "gd", "<Plug>(coc-definition)", {silent = true})
keyset("n", "gy", "<Plug>(coc-type-definition)", {silent = true})
keyset("n", "gi", "<Plug>(coc-implementation)", {silent = true})
keyset("n", "gr", "<Plug>(coc-references)", {silent = true})
-- Use K to show documentation in preview window.
function _G.show_docs()
local cw = vim.fn.expand('<cword>')
if vim.fn.index({'vim', 'help'}, vim.bo.filetype) >= 0 then
vim.api.nvim_command('h ' .. cw)
elseif vim.api.nvim_eval('coc#rpc#ready()') then
vim.fn.CocActionAsync('doHover')
else
vim.api.nvim_command('!' .. vim.o.keywordprg .. ' ' .. cw)
end
end
keyset("n", "K", '<CMD>lua _G.show_docs()<CR>', {silent = true})
-- Highlight the symbol and its references when holding the cursor.
vim.api.nvim_create_augroup("CocGroup", {})
vim.api.nvim_create_autocmd("CursorHold", {
group = "CocGroup",
command = "silent call CocActionAsync('highlight')",
desc = "Highlight symbol under cursor on CursorHold"
})
-- Symbol renaming.
keyset("n", "<leader>rn", "<Plug>(coc-rename)", {silent = true})
-- Formatting selected code.
keyset("x", "<leader>f", "<Plug>(coc-format-selected)", {silent = true})
keyset("n", "<leader>f", "<Plug>(coc-format-selected)", {silent = true})
-- Setup formatexpr specified filetype(s).
vim.api.nvim_create_autocmd("FileType", {
group = "CocGroup",
pattern = "typescript,json",
command = "setl formatexpr=CocAction('formatSelected')",
desc = "Setup formatexpr specified filetype(s)."
})
-- Update signature help on jump placeholder.
vim.api.nvim_create_autocmd("User", {
group = "CocGroup",
pattern = "CocJumpPlaceholder",
command = "call CocActionAsync('showSignatureHelp')",
desc = "Update signature help on jump placeholder"
})
-- Applying codeAction to the selected region.
-- Example: `<leader>aap` for current paragraph
local opts = {silent = true, nowait = true}
keyset("x", "<leader>a", "<Plug>(coc-codeaction-selected)", opts)
keyset("n", "<leader>a", "<Plug>(coc-codeaction-selected)", opts)
-- Remap keys for applying codeAction to the current buffer.
keyset("n", "<leader>ac", "<Plug>(coc-codeaction)", opts)
-- Apply AutoFix to problem on the current line.
keyset("n", "<leader>qf", "<Plug>(coc-fix-current)", opts)
-- Run the Code Lens action on the current line.
keyset("n", "<leader>cl", "<Plug>(coc-codelens-action)", opts)
-- Map function and class text objects
-- NOTE: Requires 'textDocument.documentSymbol' support from the language server.
keyset("x", "if", "<Plug>(coc-funcobj-i)", opts)
keyset("o", "if", "<Plug>(coc-funcobj-i)", opts)
keyset("x", "af", "<Plug>(coc-funcobj-a)", opts)
keyset("o", "af", "<Plug>(coc-funcobj-a)", opts)
keyset("x", "ic", "<Plug>(coc-classobj-i)", opts)
keyset("o", "ic", "<Plug>(coc-classobj-i)", opts)
keyset("x", "ac", "<Plug>(coc-classobj-a)", opts)
keyset("o", "ac", "<Plug>(coc-classobj-a)", opts)
-- Remap <C-f> and <C-b> for scroll float windows/popups.
---@diagnostic disable-next-line: redefined-local
local opts = {silent = true, nowait = true, expr = true}
keyset("n", "<C-f>", 'coc#float#has_scroll() ? coc#float#scroll(1) : "<C-f>"', opts)
keyset("n", "<C-b>", 'coc#float#has_scroll() ? coc#float#scroll(0) : "<C-b>"', opts)
keyset("i", "<C-f>",
'coc#float#has_scroll() ? "<c-r>=coc#float#scroll(1)<cr>" : "<Right>"', opts)
keyset("i", "<C-b>",
'coc#float#has_scroll() ? "<c-r>=coc#float#scroll(0)<cr>" : "<Left>"', opts)
keyset("v", "<C-f>", 'coc#float#has_scroll() ? coc#float#scroll(1) : "<C-f>"', opts)
keyset("v", "<C-b>", 'coc#float#has_scroll() ? coc#float#scroll(0) : "<C-b>"', opts)
-- Use CTRL-S for selections ranges.
-- Requires 'textDocument/selectionRange' support of language server.
keyset("n", "<C-s>", "<Plug>(coc-range-select)", {silent = true})
keyset("x", "<C-s>", "<Plug>(coc-range-select)", {silent = true})
-- Add `:Format` command to format current buffer.
vim.api.nvim_create_user_command("Format", "call CocAction('format')", {})
-- " Add `:Fold` command to fold current buffer.
vim.api.nvim_create_user_command("Fold", "call CocAction('fold', <f-args>)", {nargs = '?'})
-- Add `:OR` command for organize imports of the current buffer.
vim.api.nvim_create_user_command("OR", "call CocActionAsync('runCommand', 'editor.action.organizeImport')", {})
-- Add (Neo)Vim's native statusline support.
-- NOTE: Please see `:h coc-status` for integrations with external plugins that
-- provide custom statusline: lightline.vim, vim-airline.
vim.opt.statusline:prepend("%{coc#status()}%{get(b:,'coc_current_function','')}")
-- Mappings for CoCList
-- code actions and coc stuff
---@diagnostic disable-next-line: redefined-local
local opts = {silent = true, nowait = true}
-- Show all diagnostics.
keyset("n", "<space>a", ":<C-u>CocList diagnostics<cr>", opts)
-- Manage extensions.
keyset("n", "<space>e", ":<C-u>CocList extensions<cr>", opts)
-- Show commands.
keyset("n", "<space>c", ":<C-u>CocList commands<cr>", opts)
-- Find symbol of current document.
keyset("n", "<space>o", ":<C-u>CocList outline<cr>", opts)
-- Search workspace symbols.
keyset("n", "<space>s", ":<C-u>CocList -I symbols<cr>", opts)
-- Do default action for next item.
keyset("n", "<space>j", ":<C-u>CocNext<cr>", opts)
-- Do default action for previous item.
keyset("n", "<space>k", ":<C-u>CocPrev<cr>", opts)
-- Resume latest coc list.
keyset("n", "<space>p", ":<C-u>CocListResume<cr>", opts)
```
## Articles
- [coc.nvim 插件体系介绍](https://zhuanlan.zhihu.com/p/65524706)
@ -287,7 +441,7 @@ nnoremap <silent><nowait> <space>p :<C-u>CocListResume<CR> @@ -287,7 +441,7 @@ nnoremap <silent><nowait> <space>p :<C-u>CocListResume<CR>
- [Create coc.nvim extension to improve Vim experience](https://medium.com/@chemzqm/create-coc-nvim-extension-to-improve-vim-experience-4461df269173)
- [How to write a coc.nvim extension (and why)](https://samroeca.com/coc-plugin.html)
## Trouble shooting
## Troubleshooting
Try these steps when you have problem with coc.nvim.
@ -307,7 +461,7 @@ Try these steps when you have problem with coc.nvim. @@ -307,7 +461,7 @@ Try these steps when you have problem with coc.nvim.
## Backers
[Become a backer](https://opencollective.com/cocnvim#backer) and get your image on our README on Github with a link to your site.
[Become a backer](https://opencollective.com/cocnvim#backer) and get your image on our README on GitHub with a link to your site.
<a href="https://opencollective.com/cocnvim/backer/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/0/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/1/avatar.svg?requireActive=false"></a>
@ -355,12 +509,314 @@ Try these steps when you have problem with coc.nvim. @@ -355,12 +509,314 @@ Try these steps when you have problem with coc.nvim.
<a href="https://opencollective.com/cocnvim/backer/43/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/43/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/44/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/44/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/45/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/45/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/46/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/46/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/47/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/47/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/48/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/48/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/49/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/49/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim#backer" target="_blank"><img src="https://images.opencollective.com/static/images/become_backer.svg"></a>
## Support the project
Buy cloud service from [www.vultr.com](https://www.vultr.com/?ref=8890170-6G)
## Contributors
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center"><a href="https://github.com/chemzqm"><img src="https://avatars.githubusercontent.com/u/251450?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Qiming zhao</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=chemzqm" title="Code">💻</a></td>
<td align="center"><a href="https://fann.im/"><img src="https://avatars.githubusercontent.com/u/345274?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Heyward Fann</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=fannheyward" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/weirongxu"><img src="https://avatars.githubusercontent.com/u/1709861?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Raidou</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=weirongxu" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kevinhwang91"><img src="https://avatars.githubusercontent.com/u/17562139?v=4?s=50" width="50px;" alt=""/><br /><sub><b>kevinhwang91</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kevinhwang91" title="Code">💻</a></td>
<td align="center"><a href="http://yuuko.cn/"><img src="https://avatars.githubusercontent.com/u/5492542?v=4?s=50" width="50px;" alt=""/><br /><sub><b>年糕小豆汤</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=iamcco" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Avi-D-coder"><img src="https://avatars.githubusercontent.com/u/29133776?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Avi Dessauer</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Avi-D-coder" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/voldikss"><img src="https://avatars.githubusercontent.com/u/20282795?v=4?s=50" width="50px;" alt=""/><br /><sub><b>最上川</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=voldikss" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.microsoft.com/en-us/research/people/yatli/"><img src="https://avatars.githubusercontent.com/u/20684720?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Yatao Li</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=yatli" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/xiyaowong"><img src="https://avatars.githubusercontent.com/u/47070852?v=4?s=50" width="50px;" alt=""/><br /><sub><b>wongxy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=xiyaowong" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sam-mccall"><img src="https://avatars.githubusercontent.com/u/548993?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sam McCall</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sam-mccall" title="Code">💻</a></td>
<td align="center"><a href="https://samroeca.com/pages/about.html#about"><img src="https://avatars.githubusercontent.com/u/3723671?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Samuel Roeca</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=pappasam" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/amiralies"><img src="https://avatars.githubusercontent.com/u/13261088?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Amirali Esmaeili</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=amiralies" title="Code">💻</a></td>
<td align="center"><a href="https://bit.ly/3cLKGE4"><img src="https://avatars.githubusercontent.com/u/3051781?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jack Rowlingson</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jrowlingson" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tomtomjhj"><img src="https://avatars.githubusercontent.com/u/19489738?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jaehwang Jung</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=tomtomjhj" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/antoinemadec"><img src="https://avatars.githubusercontent.com/u/10830594?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Antoine</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=antoinemadec" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/cosminadrianpopescu"><img src="https://avatars.githubusercontent.com/u/5187873?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Cosmin Popescu</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=cosminadrianpopescu" title="Code">💻</a></td>
<td align="center"><a href="https://ducnx.com/"><img src="https://avatars.githubusercontent.com/u/1186411?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Duc Nghiem Xuan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=xuanduc987" title="Code">💻</a></td>
<td align="center"><a href="https://nosubstance.me/"><img src="https://avatars.githubusercontent.com/u/1269815?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Francisco Lopes</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=oblitum" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/daquexian"><img src="https://avatars.githubusercontent.com/u/11607199?v=4?s=50" width="50px;" alt=""/><br /><sub><b>daquexian</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=daquexian" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4?s=50" width="50px;" alt=""/><br /><sub><b>dependabot[bot]</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=dependabot[bot]" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/apps/greenkeeper"><img src="https://avatars.githubusercontent.com/in/505?v=4?s=50" width="50px;" alt=""/><br /><sub><b>greenkeeper[bot]</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=greenkeeper[bot]" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://chris-kipp.io/"><img src="https://avatars.githubusercontent.com/u/13974112?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chris Kipp</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ckipp01" title="Code">💻</a></td>
<td align="center"><a href="https://dmitmel.github.io/"><img src="https://avatars.githubusercontent.com/u/15367354?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Dmytro Meleshko</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=dmitmel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kirillbobyrev"><img src="https://avatars.githubusercontent.com/u/3352968?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kirill Bobyrev</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kirillbobyrev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/gbcreation"><img src="https://avatars.githubusercontent.com/u/454315?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Gontran Baerts</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=gbcreation" title="Code">💻</a></td>
<td align="center"><a href="https://andys8.de/"><img src="https://avatars.githubusercontent.com/u/13085980?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Andy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=andys8" title="Code">💻</a></td>
<td align="center"><a href="https://www.alexcj96.com/"><img src="https://avatars.githubusercontent.com/u/33961674?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Cheng JIANG</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=GopherJ" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/cpearce-py"><img src="https://avatars.githubusercontent.com/u/53532946?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Corin</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=cpearce-py" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/wodesuck"><img src="https://avatars.githubusercontent.com/u/3124581?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Daniel Zhang</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=wodesuck" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Ferdi265"><img src="https://avatars.githubusercontent.com/u/4077106?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ferdinand Bachmann</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Ferdi265" title="Code">💻</a></td>
<td align="center"><a href="https://goushi.me/"><img src="https://avatars.githubusercontent.com/u/16915589?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Guangqing Chen</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=gou4shi1" title="Code">💻</a></td>
<td align="center"><a href="http://jademeskill.com/"><img src="https://avatars.githubusercontent.com/u/2108?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jade Meskill</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=iamruinous" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jpoppe"><img src="https://avatars.githubusercontent.com/u/65505?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jasper Poppe</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jpoppe" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jean"><img src="https://avatars.githubusercontent.com/u/84800?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jean Jordaan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jean" title="Code">💻</a></td>
<td align="center"><a href="https://xuann.wang/"><img src="https://avatars.githubusercontent.com/u/44045911?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kid</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kidonng" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Kavantix"><img src="https://avatars.githubusercontent.com/u/6243755?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Pieter van Loon</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Kavantix" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rliebz"><img src="https://avatars.githubusercontent.com/u/5321575?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Robert Liebowitz</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=rliebz" title="Code">💻</a></td>
<td align="center"><a href="https://megalithic.io/"><img src="https://avatars.githubusercontent.com/u/3678?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Seth Messer</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=megalithic" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/UncleBill"><img src="https://avatars.githubusercontent.com/u/1141198?v=4?s=50" width="50px;" alt=""/><br /><sub><b>UncleBill</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=UncleBill" title="Code">💻</a></td>
<td align="center"><a href="http://zsaber.com/"><img src="https://avatars.githubusercontent.com/u/6846867?v=4?s=50" width="50px;" alt=""/><br /><sub><b>ZERO</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ZSaberLv0" title="Code">💻</a></td>
<td align="center"><a href="https://fsouza.blog/"><img src="https://avatars.githubusercontent.com/u/108725?v=4?s=50" width="50px;" alt=""/><br /><sub><b>fsouza</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=fsouza" title="Code">💻</a></td>
<td align="center"><a href="https://onichandame.com/"><img src="https://avatars.githubusercontent.com/u/23728505?v=4?s=50" width="50px;" alt=""/><br /><sub><b>XiaoZhang</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=onichandame" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/whyreal"><img src="https://avatars.githubusercontent.com/u/2084642?v=4?s=50" width="50px;" alt=""/><br /><sub><b>whyreal</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=whyreal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yehuohan"><img src="https://avatars.githubusercontent.com/u/17680752?v=4?s=50" width="50px;" alt=""/><br /><sub><b>yehuohan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=yehuohan" title="Code">💻</a></td>
<td align="center"><a href="http://www.bakudan.farm/"><img src="https://avatars.githubusercontent.com/u/4504807?v=4?s=50" width="50px;" alt=""/><br /><sub><b>バクダンくん</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Bakudankun" title="Code">💻</a></td>
<td align="center"><a href="https://blog.gopherhub.org/"><img src="https://avatars.githubusercontent.com/u/41671631?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Raphael</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=glepnir" title="Code">💻</a></td>
<td align="center"><a href="https://tbodt.com/"><img src="https://avatars.githubusercontent.com/u/5678977?v=4?s=50" width="50px;" alt=""/><br /><sub><b>tbodt</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=tbodt" title="Code">💻</a></td>
<td align="center"><a href="https://aaronmcdaid.github.io/"><img src="https://avatars.githubusercontent.com/u/64350?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Aaron McDaid</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=aaronmcdaid" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/versi786"><img src="https://avatars.githubusercontent.com/u/7347942?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Aasif Versi</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=versi786" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/abnerf"><img src="https://avatars.githubusercontent.com/u/56300?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Abner Silva</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=abnerf" title="Code">💻</a></td>
<td align="center"><a href="http://sheerun.net/"><img src="https://avatars.githubusercontent.com/u/292365?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Adam Stankiewicz</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sheerun" title="Code">💻</a></td>
<td align="center"><a href="https://wirow.io/"><img src="https://avatars.githubusercontent.com/u/496683?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Adamansky Anton</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=adamansky" title="Code">💻</a></td>
<td align="center"><a href="https://gabri.me/"><img src="https://avatars.githubusercontent.com/u/63876?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ahmed El Gabri</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ahmedelgabri" title="Code">💻</a></td>
<td align="center"><a href="http://theg4sh.ru/"><img src="https://avatars.githubusercontent.com/u/5094691?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Alexandr Kondratev</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=theg4sh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/andrewkshim"><img src="https://avatars.githubusercontent.com/u/1403410?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Andrew Shim</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=andrewkshim" title="Code">💻</a></td>
<td align="center"><a href="http://andylindeman.com/"><img src="https://avatars.githubusercontent.com/u/395621?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Andy Lindeman</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=alindeman" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Augustin82"><img src="https://avatars.githubusercontent.com/u/2370810?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Augustin</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Augustin82" title="Code">💻</a></td>
<td align="center"><a href="https://bananium.fr/"><img src="https://avatars.githubusercontent.com/u/3650385?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Bastien Orivel</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Eijebong" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ayroblu"><img src="https://avatars.githubusercontent.com/u/4915682?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ben Lu</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ayroblu" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vantreeseba"><img src="https://avatars.githubusercontent.com/u/316782?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ben</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=vantreeseba" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bmon"><img src="https://avatars.githubusercontent.com/u/2115272?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Brendan Roy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=bmon" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/brianembry"><img src="https://avatars.githubusercontent.com/u/35347666?v=4?s=50" width="50px;" alt=""/><br /><sub><b>brianembry</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=brianembry" title="Code">💻</a></td>
<td align="center"><a href="https://keybase.io/bri_"><img src="https://avatars.githubusercontent.com/u/284789?v=4?s=50" width="50px;" alt=""/><br /><sub><b>br</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=b-" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/casonadams"><img src="https://avatars.githubusercontent.com/u/17597548?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Cason Adams</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=casonadams" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/y9c"><img src="https://avatars.githubusercontent.com/u/5415510?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chang Y</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=y9c" title="Code">💻</a></td>
<td align="center"><a href="https://yous.be/"><img src="https://avatars.githubusercontent.com/u/853977?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chayoung You</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=yous" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/chenlijun99"><img src="https://avatars.githubusercontent.com/u/20483759?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chen Lijun</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=chenlijun99" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/beeender"><img src="https://avatars.githubusercontent.com/u/449296?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chen Mulong</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=beeender" title="Code">💻</a></td>
<td align="center"><a href="http://weyl.io/"><img src="https://avatars.githubusercontent.com/u/59620?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Chris Weyl</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=rsrchboy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dezza"><img src="https://avatars.githubusercontent.com/u/402927?v=4?s=50" width="50px;" alt=""/><br /><sub><b>dezza</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=dezza" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ceedubs"><img src="https://avatars.githubusercontent.com/u/977929?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Cody Allen</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ceedubs" title="Code">💻</a></td>
<td align="center"><a href="https://www.25.wf/"><img src="https://avatars.githubusercontent.com/u/145502?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Damien Rajon</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=pyrho" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/daern91"><img src="https://avatars.githubusercontent.com/u/6084427?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Daniel Eriksson</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=daern91" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/danjenson"><img src="https://avatars.githubusercontent.com/u/4793438?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Daniel Jenson</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=danjenson" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/davidmh"><img src="https://avatars.githubusercontent.com/u/594302?v=4?s=50" width="50px;" alt=""/><br /><sub><b>David Mejorado</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=davidmh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/pderichai"><img src="https://avatars.githubusercontent.com/u/13430946?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Deric Pang</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=pderichai" title="Code">💻</a></td>
<td align="center"><a href="https://www.dingtao.org/blog"><img src="https://avatars.githubusercontent.com/u/12852587?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ding Tao</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=miyatsu" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/doronbehar"><img src="https://avatars.githubusercontent.com/u/10998835?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Doron Behar</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=doronbehar" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kovetskiy"><img src="https://avatars.githubusercontent.com/u/8445924?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Egor Kovetskiy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kovetskiy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/elkowar"><img src="https://avatars.githubusercontent.com/u/5300871?v=4?s=50" width="50px;" alt=""/><br /><sub><b>ElKowar</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=elkowar" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/demelev"><img src="https://avatars.githubusercontent.com/u/3952209?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Emeliov Dmitrii</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=demelev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sawmurai"><img src="https://avatars.githubusercontent.com/u/6454986?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Fabian Becker</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sawmurai" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/FallenWarrior2k"><img src="https://avatars.githubusercontent.com/u/20320149?v=4?s=50" width="50px;" alt=""/><br /><sub><b>FallenWarrior2k</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=FallenWarrior2k" title="Code">💻</a></td>
<td align="center"><a href="https://fnune.com/"><img src="https://avatars.githubusercontent.com/u/16181067?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Fausto Núñez Alberro</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=fnune" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/FelipeCRamos"><img src="https://avatars.githubusercontent.com/u/7572843?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Felipe Ramos</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=FelipeCRamos" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/frbor"><img src="https://avatars.githubusercontent.com/u/2320183?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Fredrik Borg</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=frbor" title="Code">💻</a></td>
<td align="center"><a href="http://www.gavinsim.co.uk/"><img src="https://avatars.githubusercontent.com/u/812273?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Gavin Sim</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=gavsim" title="Code">💻</a></td>
<td align="center"><a href="https://fahn.co/"><img src="https://avatars.githubusercontent.com/u/15943089?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Gibson Fahnestock</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=gibfahn" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/giovannigiordano"><img src="https://avatars.githubusercontent.com/u/15145952?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Giovanni Giordano</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=giovannigiordano" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/qubbit"><img src="https://avatars.githubusercontent.com/u/1987473?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Gopal Adhikari</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=qubbit" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hanh090"><img src="https://avatars.githubusercontent.com/u/3643657?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Hanh Le</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hanh090" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hedyhli"><img src="https://avatars.githubusercontent.com/u/50042066?v=4?s=50" width="50px;" alt=""/><br /><sub><b>hedy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hedyhli" title="Code">💻</a></td>
<td align="center"><a href="https://www.hendriklammers.com/"><img src="https://avatars.githubusercontent.com/u/754556?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Hendrik Lammers</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hendriklammers" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/henrybarreto"><img src="https://avatars.githubusercontent.com/u/23109089?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Henry Barreto</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=henrybarreto" title="Code">💻</a></td>
<td align="center"><a href="https://hugo.barrera.io/"><img src="https://avatars.githubusercontent.com/u/730811?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Hugo</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=WhyNotHugo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jackieli-tes"><img src="https://avatars.githubusercontent.com/u/64778297?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jackie Li</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jackieli-tes" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MrQubo"><img src="https://avatars.githubusercontent.com/u/16545322?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jakub Nowak</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=MrQubo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/euoia"><img src="https://avatars.githubusercontent.com/u/1271216?v=4?s=50" width="50px;" alt=""/><br /><sub><b>James Pickard</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=euoia" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/jsfaint"><img src="https://avatars.githubusercontent.com/u/571829?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jia Sui</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jsfaint" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/expipiplus1"><img src="https://avatars.githubusercontent.com/u/857308?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ellie Hermaszewska</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=expipiplus1" title="Code">💻</a></td>
<td align="center"><a href="https://cincodenada.com/"><img src="https://avatars.githubusercontent.com/u/479715?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Joel Bradshaw</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=cincodenada" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/irizwaririz"><img src="https://avatars.githubusercontent.com/u/10111643?v=4?s=50" width="50px;" alt=""/><br /><sub><b>John Carlo Roberto</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=irizwaririz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Jomik"><img src="https://avatars.githubusercontent.com/u/699655?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jonas Holst Damtoft</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Jomik" title="Code">💻</a></td>
<td align="center"><a href="http://inlehmansterms.net/"><img src="https://avatars.githubusercontent.com/u/3144695?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jonathan Lehman</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jdlehman" title="Code">💻</a></td>
<td align="center"><a href="https://joosep.xyz/"><img src="https://avatars.githubusercontent.com/u/9450943?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Joosep Alviste</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=JoosepAlviste" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/josa42"><img src="https://avatars.githubusercontent.com/u/423234?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Josa Gesell</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=josa42" title="Code">💻</a></td>
<td align="center"><a href="https://jawa.dev/"><img src="https://avatars.githubusercontent.com/u/194275?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Joshua Rubin</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=joshuarubin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/perrin4869"><img src="https://avatars.githubusercontent.com/u/5774716?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Julian Grinblat</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=perrin4869" title="Code">💻</a></td>
<td align="center"><a href="https://valentjn.github.io/"><img src="https://avatars.githubusercontent.com/u/19839841?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Julian Valentin</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=valentjn" title="Code">💻</a></td>
<td align="center"><a href="https://kabbamine.github.io/"><img src="https://avatars.githubusercontent.com/u/5658084?v=4?s=50" width="50px;" alt=""/><br /><sub><b>KabbAmine</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=KabbAmine" title="Code">💻</a></td>
<td align="center"><a href="https://moncargo.io/"><img src="https://avatars.githubusercontent.com/u/10719495?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kay Gosho</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=acro5piano" title="Code">💻</a></td>
<td align="center"><a href="https://kennyvh.com/"><img src="https://avatars.githubusercontent.com/u/29909203?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kenny Huynh</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hkennyv" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kevinrambaud"><img src="https://avatars.githubusercontent.com/u/7501477?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kevin Rambaud</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kevinrambaud" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kiancross"><img src="https://avatars.githubusercontent.com/u/11011464?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kian Cross</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kiancross" title="Code">💻</a></td>
<td align="center"><a href="https://ko-fi.com/kristijanhusak"><img src="https://avatars.githubusercontent.com/u/1782860?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Kristijan Husak</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kristijanhusak" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/NullVoxPopuli"><img src="https://avatars.githubusercontent.com/u/199018?v=4?s=50" width="50px;" alt=""/><br /><sub><b>NullVoxPopuli</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=NullVoxPopuli" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lassepe"><img src="https://avatars.githubusercontent.com/u/10076790?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Lasse Peters</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=lassepe" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Linerre"><img src="https://avatars.githubusercontent.com/u/49512984?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Noel Errenil</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Linerre" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/LinArcX"><img src="https://avatars.githubusercontent.com/u/10884422?v=4?s=50" width="50px;" alt=""/><br /><sub><b>LinArcX</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=LinArcX" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://paypal.me/liuchengxu"><img src="https://avatars.githubusercontent.com/u/8850248?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Liu-Cheng Xu</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=liuchengxu" title="Code">💻</a></td>
<td align="center"><a href="https://malloc.me/"><img src="https://avatars.githubusercontent.com/u/4153572?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Marc</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=foxtrot" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mgaw"><img src="https://avatars.githubusercontent.com/u/2177016?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Marius Gawrisch</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=mgaw" title="Code">💻</a></td>
<td align="center"><a href="http://www.markhz.com/"><img src="https://avatars.githubusercontent.com/u/2789742?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Mark Hintz</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=mhintz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MatElGran"><img src="https://avatars.githubusercontent.com/u/1052778?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Mathieu Le Tiec</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=MatElGran" title="Code">💻</a></td>
<td align="center"><a href="https://matt-w.net/"><img src="https://avatars.githubusercontent.com/u/8656127?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Matt White</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=matt-fff" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ml-evs"><img src="https://avatars.githubusercontent.com/u/7916000?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Matthew Evans</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ml-evs" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Me1onRind"><img src="https://avatars.githubusercontent.com/u/19531270?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Me1onRind</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Me1onRind" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Qyriad"><img src="https://avatars.githubusercontent.com/u/1542224?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Qyriad</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Qyriad" title="Code">💻</a></td>
<td align="center"><a href="https://leo.is-a.dev/"><img src="https://avatars.githubusercontent.com/u/35312043?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Narcis B.</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=leonardssh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Neur1n"><img src="https://avatars.githubusercontent.com/u/17579247?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Neur1n</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Neur1n" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nicoder"><img src="https://avatars.githubusercontent.com/u/365210?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Nicolas Dermine</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=nicoder" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/NoahTheDuke"><img src="https://avatars.githubusercontent.com/u/603677?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Noah</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=NoahTheDuke" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/IndexXuan"><img src="https://avatars.githubusercontent.com/u/6322673?v=4?s=50" width="50px;" alt=""/><br /><sub><b>PENG Rui</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=IndexXuan" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://liaoph.com/"><img src="https://avatars.githubusercontent.com/u/6123425?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Paco</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=paco0x" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/peng1999"><img src="https://avatars.githubusercontent.com/u/12483662?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Peng Guanwen</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=peng1999" title="Code">💻</a></td>
<td align="center"><a href="https://www.twitter.com/badeip"><img src="https://avatars.githubusercontent.com/u/1106732?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Petter Wahlman</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ilAYAli" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/pvonmoradi"><img src="https://avatars.githubusercontent.com/u/1058151?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Pooya Moradi</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=pvonmoradi" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/QuadeMorrison"><img src="https://avatars.githubusercontent.com/u/10917383?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Quade Morrison</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=QuadeMorrison" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vogler"><img src="https://avatars.githubusercontent.com/u/493741?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ralf Vogler</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=vogler" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/crccw"><img src="https://avatars.githubusercontent.com/u/41463?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ran Chen</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=crccw" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://bigardone.dev/"><img src="https://avatars.githubusercontent.com/u/1090272?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ricardo García Vega</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=bigardone" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nomasprime"><img src="https://avatars.githubusercontent.com/u/140855?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Rick Jones</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=nomasprime" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rschristian"><img src="https://avatars.githubusercontent.com/u/33403762?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ryan Christian</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=rschristian" title="Code">💻</a></td>
<td align="center"><a href="http://salo.so/"><img src="https://avatars.githubusercontent.com/u/4694263?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Salo</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=winterbesos" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Hazelfire"><img src="https://avatars.githubusercontent.com/u/13807753?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sam Nolan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Hazelfire" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rickysaurav"><img src="https://avatars.githubusercontent.com/u/13986039?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Saurav</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=rickysaurav" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/smackesey"><img src="https://avatars.githubusercontent.com/u/1531373?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sean Mackesey</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=smackesey" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/sheeldotme"><img src="https://avatars.githubusercontent.com/u/6991406?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sheel Patel</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sheeldotme" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/solomonwzs"><img src="https://avatars.githubusercontent.com/u/907942?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Solomon Ng</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=solomonwzs" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kadimisetty"><img src="https://avatars.githubusercontent.com/u/535947?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sri Kadimisetty</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kadimisetty" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/stephenprater"><img src="https://avatars.githubusercontent.com/u/149870?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Stephen Prater</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=stephenprater" title="Code">💻</a></td>
<td align="center"><a href="https://kibs.dk/"><img src="https://avatars.githubusercontent.com/u/14085?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sune Kibsgaard</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kibs" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Aquaakuma"><img src="https://avatars.githubusercontent.com/u/31891793?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Aquaakuma</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Aquaakuma" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/coil398"><img src="https://avatars.githubusercontent.com/u/7694377?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Takumi Kawase</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=coil398" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/theblobscp"><img src="https://avatars.githubusercontent.com/u/81673375?v=4?s=50" width="50px;" alt=""/><br /><sub><b>The Blob SCP</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=theblobscp" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/przepompownia"><img src="https://avatars.githubusercontent.com/u/11404453?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Tomasz N</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=przepompownia" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/gasuketsu"><img src="https://avatars.githubusercontent.com/u/15703757?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Tomoyuki Harada</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=gasuketsu" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tonyfettes"><img src="https://avatars.githubusercontent.com/u/29998228?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Tony Fettes</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=tonyfettes" title="Code">💻</a></td>
<td align="center"><a href="https://www.git-pull.com/"><img src="https://avatars.githubusercontent.com/u/26336?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Tony Narlock</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=tony" title="Code">💻</a></td>
<td align="center"><a href="https://blog.wwwjfy.net/"><img src="https://avatars.githubusercontent.com/u/126527?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Tony Wang</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=wwwjfy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Varal7"><img src="https://avatars.githubusercontent.com/u/8019486?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Victor Quach</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=Varal7" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/whisperity"><img src="https://avatars.githubusercontent.com/u/1969470?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Whisperity</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=whisperity" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/willtrnr"><img src="https://avatars.githubusercontent.com/u/1878110?v=4?s=50" width="50px;" alt=""/><br /><sub><b>William Turner</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=willtrnr" title="Code">💻</a></td>
<td align="center"><a href="https://drafts.damnever.com/"><img src="https://avatars.githubusercontent.com/u/6223594?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Xiaochao Dong</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=damnever" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hyhugh"><img src="https://avatars.githubusercontent.com/u/16500351?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Hugh Hou</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hyhugh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jackielii"><img src="https://avatars.githubusercontent.com/u/360983?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Jackie Li</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=jackielii" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/TheConfuZzledDude"><img src="https://avatars.githubusercontent.com/u/3160203?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Zachary Freed</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=TheConfuZzledDude" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/akiyosi"><img src="https://avatars.githubusercontent.com/u/8478977?v=4?s=50" width="50px;" alt=""/><br /><sub><b>akiyosi</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=akiyosi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/alexjg"><img src="https://avatars.githubusercontent.com/u/224635?v=4?s=50" width="50px;" alt=""/><br /><sub><b>alexjg</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=alexjg" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/aste4"><img src="https://avatars.githubusercontent.com/u/47511385?v=4?s=50" width="50px;" alt=""/><br /><sub><b>aste4</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=aste4" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/clyfish"><img src="https://avatars.githubusercontent.com/u/541215?v=4?s=50" width="50px;" alt=""/><br /><sub><b>clyfish</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=clyfish" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dev7ba"><img src="https://avatars.githubusercontent.com/u/93706552?v=4?s=50" width="50px;" alt=""/><br /><sub><b>dev7ba</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=dev7ba" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/diartyz"><img src="https://avatars.githubusercontent.com/u/4486152?v=4?s=50" width="50px;" alt=""/><br /><sub><b>diartyz</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=diartyz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/doza-daniel"><img src="https://avatars.githubusercontent.com/u/13752683?v=4?s=50" width="50px;" alt=""/><br /><sub><b>doza-daniel</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=doza-daniel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/equal-l2"><img src="https://avatars.githubusercontent.com/u/8597717?v=4?s=50" width="50px;" alt=""/><br /><sub><b>equal-l2</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=equal-l2" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/FongHou"><img src="https://avatars.githubusercontent.com/u/13973254?v=4?s=50" width="50px;" alt=""/><br /><sub><b>fong</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=FongHou" title="Code">💻</a></td>
<td align="center"><a href="https://blog.hexuhua.vercel.app/"><img src="https://avatars.githubusercontent.com/u/26080416?v=4?s=50" width="50px;" alt=""/><br /><sub><b>hexh</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hexh250786313" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hhiraba"><img src="https://avatars.githubusercontent.com/u/4624806?v=4?s=50" width="50px;" alt=""/><br /><sub><b>hhiraba</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=hhiraba" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ic-768"><img src="https://avatars.githubusercontent.com/u/83115125?v=4?s=50" width="50px;" alt=""/><br /><sub><b>ic-768</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ic-768" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/javiertury"><img src="https://avatars.githubusercontent.com/u/1520320?v=4?s=50" width="50px;" alt=""/><br /><sub><b>javiertury</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=javiertury" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/seiyeah78"><img src="https://avatars.githubusercontent.com/u/6185139?v=4?s=50" width="50px;" alt=""/><br /><sub><b>karasu</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=seiyeah78" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kevineato"><img src="https://avatars.githubusercontent.com/u/13666221?v=4?s=50" width="50px;" alt=""/><br /><sub><b>kevineato</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=kevineato" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/m4c0"><img src="https://avatars.githubusercontent.com/u/1664510?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Eduardo Costa</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=m4c0" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/micchy326"><img src="https://avatars.githubusercontent.com/u/23257067?v=4?s=50" width="50px;" alt=""/><br /><sub><b>micchy326</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=micchy326" title="Code">💻</a></td>
<td align="center"><a href="https://keybase.io/midchildan"><img src="https://avatars.githubusercontent.com/u/7343721?v=4?s=50" width="50px;" alt=""/><br /><sub><b>midchildan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=midchildan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/minefuto"><img src="https://avatars.githubusercontent.com/u/46558834?v=4?s=50" width="50px;" alt=""/><br /><sub><b>minefuto</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=minefuto" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/robokomy"><img src="https://avatars.githubusercontent.com/u/20733354?v=4?s=50" width="50px;" alt=""/><br /><sub><b>miyanokomiya</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=miyanokomiya" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/miyaviee"><img src="https://avatars.githubusercontent.com/u/15247561?v=4?s=50" width="50px;" alt=""/><br /><sub><b>miyaviee</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=miyaviee" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/monkoose"><img src="https://avatars.githubusercontent.com/u/6261276?v=4?s=50" width="50px;" alt=""/><br /><sub><b>monkoose</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=monkoose" title="Code">💻</a> <a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Amonkoose" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/mujx"><img src="https://avatars.githubusercontent.com/u/6430350?v=4?s=50" width="50px;" alt=""/><br /><sub><b>mujx</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=mujx" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mvilim"><img src="https://avatars.githubusercontent.com/u/40682862?v=4?s=50" width="50px;" alt=""/><br /><sub><b>mvilim</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=mvilim" title="Code">💻</a></td>
<td align="center"><a href="https://naruaway.com/"><img src="https://avatars.githubusercontent.com/u/2931577?v=4?s=50" width="50px;" alt=""/><br /><sub><b>naruaway</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=naruaway" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piersy"><img src="https://avatars.githubusercontent.com/u/5087847?v=4?s=50" width="50px;" alt=""/><br /><sub><b>piersy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=piersy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ryantig"><img src="https://avatars.githubusercontent.com/u/324810?v=4?s=50" width="50px;" alt=""/><br /><sub><b>ryantig</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=ryantig" title="Code">💻</a></td>
<td align="center"><a href="https://catcat.cc/"><img src="https://avatars.githubusercontent.com/u/19602440?v=4?s=50" width="50px;" alt=""/><br /><sub><b>rydesun</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=rydesun" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sc00ter"><img src="https://avatars.githubusercontent.com/u/1271025?v=4?s=50" width="50px;" alt=""/><br /><sub><b>sc00ter</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sc00ter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/smhc"><img src="https://avatars.githubusercontent.com/u/6404304?v=4?s=50" width="50px;" alt=""/><br /><sub><b>smhc</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=smhc" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/stkaplan"><img src="https://avatars.githubusercontent.com/u/594990?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Sam Kaplan</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=stkaplan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tasuten"><img src="https://avatars.githubusercontent.com/u/1623176?v=4?s=50" width="50px;" alt=""/><br /><sub><b>tasuten</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=tasuten" title="Code">💻</a></td>
<td align="center"><a href="http://todesking.com/"><img src="https://avatars.githubusercontent.com/u/112881?v=4?s=50" width="50px;" alt=""/><br /><sub><b>todesking</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=todesking" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/typicode"><img src="https://avatars.githubusercontent.com/u/5502029?v=4?s=50" width="50px;" alt=""/><br /><sub><b>typicode</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=typicode" title="Code">💻</a></td>
<td align="center"><a href="https://limingfei56.github.io/"><img src="https://avatars.githubusercontent.com/u/8553407?v=4?s=50" width="50px;" alt=""/><br /><sub><b>李鸣飞</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=LiMingFei56" title="Code">💻</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=eltociear" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/rammiah"><img src="https://avatars.githubusercontent.com/u/26727562?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Rammiah</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Arammiah" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://keybase.io/lambdalisue"><img src="https://avatars.githubusercontent.com/u/546312?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Alisue</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Alambdalisue" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://bigshans.github.io"><img src="https://avatars.githubusercontent.com/u/26884666?v=4?s=50" width="50px;" alt=""/><br /><sub><b>bigshans</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=bigshans" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/rob-3"><img src="https://avatars.githubusercontent.com/u/24816247?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Robert Boyd III</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Arob-3" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://creasty.com"><img src="https://avatars.githubusercontent.com/u/1695538?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Yuki Iwanaga</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=creasty" title="Code">💻</a></td>
<td align="center"><a href="https://www.dosk.win/"><img src="https://avatars.githubusercontent.com/u/2389889?v=4?s=50" width="50px;" alt=""/><br /><sub><b>SpringHack</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Aspringhack" title="Bug reports">🐛</a></td>
<td align="center"><a href="http://git.lmburns.com"><img src="https://avatars.githubusercontent.com/u/44355502?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Lucas Burns</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=lmburns" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://qiqi.boy.im"><img src="https://avatars.githubusercontent.com/u/3774036?v=4?s=50" width="50px;" alt=""/><br /><sub><b>qiqiboy</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=qiqiboy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/timsu92"><img src="https://avatars.githubusercontent.com/u/33785401?v=4?s=50" width="50px;" alt=""/><br /><sub><b>timsu92</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=timsu92" title="Documentation">📖</a></td>
<td align="center"><a href="https://sartak.org"><img src="https://avatars.githubusercontent.com/u/45430?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Shawn M Moore</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=sartak" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/aauren"><img src="https://avatars.githubusercontent.com/u/1392295?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Aaron U'Ren</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/issues?q=author%3Aaauren" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/SirCharlieMars"><img src="https://avatars.githubusercontent.com/u/31679231?v=4?s=50" width="50px;" alt=""/><br /><sub><b>SeniorMars</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=SirCharlieMars" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/CollieIsCute"><img src="https://avatars.githubusercontent.com/u/43088530?v=4?s=50" width="50px;" alt=""/><br /><sub><b>牧羊犬真Q</b></sub></a><br /><a href="https://github.com/neoclide/coc.nvim/commits?author=CollieIsCute" title="Documentation">📖</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://allcontributors.org) specification.
Contributions of any kind are welcome!
## License

144
etc/soft/nvim/+plugins/coc.nvim/autoload/coc.vim

@ -9,8 +9,8 @@ let s:is_vim = !has('nvim') @@ -9,8 +9,8 @@ let s:is_vim = !has('nvim')
let s:error_sign = get(g:, 'coc_status_error_sign', has('mac') ? '❌ ' : 'E')
let s:warning_sign = get(g:, 'coc_status_warning_sign', has('mac') ? '⚠ ' : 'W')
let s:select_api = exists('*nvim_select_popupmenu_item')
let s:complete_info_api = exists('*complete_info')
let s:callbacks = {}
let s:hide_pum = has('nvim-0.6.1') || has('patch-8.2.3389')
function! coc#expandable() abort
return coc#rpc#request('snippetCheck', [1, 0])
@ -32,10 +32,6 @@ function! coc#add_command(id, cmd, ...) @@ -32,10 +32,6 @@ function! coc#add_command(id, cmd, ...)
call coc#rpc#notify('addCommand', [config])
endfunction
function! coc#refresh() abort
return "\<c-r>=coc#start()\<CR>"
endfunction
function! coc#on_enter()
call coc#rpc#notify('CocAutocmd', ['Enter', bufnr('%')])
return ''
@ -45,8 +41,7 @@ function! coc#_insert_key(method, key, ...) abort @@ -45,8 +41,7 @@ function! coc#_insert_key(method, key, ...) abort
let prefix = ''
if get(a:, 1, 1)
if pumvisible()
call coc#rpc#notify('CocAutocmd', ['ClosePum'])
if has('nvim-0.6.0') || has('patch-8.2.3389')
if s:hide_pum
let prefix = "\<C-x>\<C-z>"
else
let g:coc_disable_space_report = 1
@ -67,74 +62,30 @@ function! coc#_complete() abort @@ -67,74 +62,30 @@ function! coc#_complete() abort
" use <cmd> specific key to preselect item at once
call feedkeys("\<Cmd>\<CR>" , 'i')
else
if pumvisible()
let g:coc_disable_complete_done = 1
endif
call complete(startcol, items)
endif
return ''
endfunction
function! coc#_do_complete(start, items, preselect)
function! coc#_do_complete(start, items, preselect, changedtick)
if b:changedtick != a:changedtick
return
endif
let g:coc#_context = {
\ 'start': a:start,
\ 'candidates': a:items,
\ 'preselect': a:preselect
\}
if mode() =~# 'i' && &paste != 1
call feedkeys("\<Plug>CocRefresh", 'i')
if mode() =~# 'i'
call coc#_complete()
endif
endfunction
function! coc#_select_confirm() abort
if !exists('*complete_info')
throw 'coc#_select_confirm requires complete_info function to work'
endif
let selected = complete_info()['selected']
if selected != -1
return "\<C-y>"
elseif pumvisible()
return "\<down>\<C-y>"
endif
return ''
endfunction
function! coc#_selected()
if !pumvisible() | return 0 | endif
return coc#rpc#request('hasSelected', [])
endfunction
" Deprecated
function! coc#_hide() abort
if pumvisible()
" Make input as it is, it's not possible by `<C-e>` and `<C-p>`
call coc#rpc#notify('CocAutocmd', ['ClosePum'])
call feedkeys("\<C-x>\<C-z>", 'in')
endif
endfunction
function! coc#_cancel()
" hack for close pum
" Use of <C-e> could cause bad insert when cursor just moved.
if pumvisible()
call coc#rpc#notify('CocAutocmd', ['ClosePum'])
if has('nvim-0.6.0') || has('patch-8.2.3389')
call feedkeys("\<C-x>\<C-z>", 'in')
elseif exists('*complete_info') && get(complete_info(['selected']), 'selected', -1) == -1
call feedkeys("\<C-e>", 'in')
else
let g:coc_disable_space_report = 1
call feedkeys("\<space>\<bs>", 'in')
endif
endif
endfunction
function! coc#_select() abort
if !pumvisible() | return | endif
call feedkeys("\<C-y>", 'in')
endfunction
function! coc#start(...)
let opt = coc#util#get_complete_option()
call CocActionAsync('startCompletion', extend(opt, get(a:, 1, {})))
return ''
function! coc#_cancel(...)
call coc#pum#close()
endfunction
" used for statusline
@ -147,14 +98,7 @@ function! coc#status() @@ -147,14 +98,7 @@ function! coc#status()
if !empty(info) && get(info, 'warning', 0)
call add(msgs, s:warning_sign . info['warning'])
endif
return s:trim(join(msgs, ' ') . ' ' . get(g:, 'coc_status', ''))
endfunction
function! s:trim(str)
if exists('*trim')
return trim(a:str)
endif
return substitute(a:str, '\s\+$', '', '')
return coc#compat#trim(join(msgs, ' ') . ' ' . get(g:, 'coc_status', ''))
endfunction
function! coc#config(section, value)
@ -188,24 +132,10 @@ function! s:GlobalChange(dict, key, val) @@ -188,24 +132,10 @@ function! s:GlobalChange(dict, key, val)
call coc#rpc#notify('GlobalChange', [a:key, get(a:val, 'old', v:null), get(a:val, 'new', v:null)])
endfunction
function! coc#_map()
if !s:select_api | return | endif
for i in range(1, 9)
exe 'inoremap <buffer> '.i.' <Cmd>call nvim_select_popupmenu_item('.(i - 1).', v:true, v:true, {})<cr>'
endfor
endfunction
function! coc#_unmap()
if !s:select_api | return | endif
for i in range(1, 9)
exe 'silent! iunmap <buffer> '.i
endfor
endfunction
function! coc#on_notify(id, method, Cb)
let key = a:id. '-'.a:method
let s:callbacks[key] = a:Cb
call coc#rpc#notify('registNotification', [a:id, a:method])
call coc#rpc#notify('registerNotification', [a:id, a:method])
endfunction
function! coc#do_notify(id, method, result)
@ -216,24 +146,38 @@ function! coc#do_notify(id, method, result) @@ -216,24 +146,38 @@ function! coc#do_notify(id, method, result)
endif
endfunction
function! coc#complete_indent() abort
let l:curpos = getcurpos()
let l:indent_pre = indent('.')
function! coc#start(...)
let opt = coc#util#get_complete_option()
call CocActionAsync('startCompletion', extend(opt, get(a:, 1, {})))
return ''
endfunction
function! coc#refresh() abort
return "\<c-r>=coc#start()\<CR>"
endfunction
function! coc#_select_confirm() abort
call timer_start(10, { -> coc#pum#select_confirm()})
return s:is_vim || has('nvim-0.5.0') ? "\<Ignore>" : "\<space>\<bs>"
endfunction
let l:startofline = &startofline
let l:virtualedit = &virtualedit
function! coc#complete_indent() abort
let curpos = getcurpos()
let indent_len = len(matchstr(getline('.'), '^\s*'))
let startofline = &startofline
let virtualedit = &virtualedit
set nostartofline
set virtualedit=all
normal! ==
let &startofline = l:startofline
let &virtualedit = l:virtualedit
let l:shift = indent('.') - l:indent_pre
let l:curpos[2] += l:shift
let l:curpos[4] += l:shift
call cursor(l:curpos[1:])
if l:shift != 0
return 1
let &startofline = startofline
let &virtualedit = virtualedit
let shift = len(matchstr(getline('.'), '^\s*')) - indent_len
let curpos[2] += shift
let curpos[4] += shift
call cursor(curpos[1:])
if shift != 0
if s:is_vim
call timer_start(0, { -> execute('redraw')})
endif
endif
return 0
endfunction

154
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/api.vim

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
" Description: Client api used by vim8
" Author: Qiming Zhao <chemzqm@gmail.com>
" Licence: Anti 996 licence
" Last Modified: Mar 08, 2022
" Last Modified: Jun 03, 2022
" ============================================================================
if has('nvim') | finish | endif
scriptencoding utf-8
@ -10,6 +10,11 @@ let s:funcs = {} @@ -10,6 +10,11 @@ let s:funcs = {}
let s:prop_offset = get(g:, 'coc_text_prop_offset', 1000)
let s:namespace_id = 1
let s:namespace_cache = {}
let s:max_src_id = 1000
" bufnr => max textprop id
let s:buffer_id = {}
" srcId => list of types
let s:id_types = {}
" helper {{
function! s:buf_line_count(bufnr) abort
@ -96,7 +101,7 @@ function! s:funcs.call_atomic(calls) @@ -96,7 +101,7 @@ function! s:funcs.call_atomic(calls)
try
call add(res, call(s:funcs[name], arglist))
catch /.*/
return [res, [i, "VimException(".s:inspect_type(v:exception).")", v:exception]]
return [res, [i, "VimException(".s:inspect_type(v:exception).")", v:exception . ' on '.v:throwpoint]]
endtry
endfor
return [res, v:null]
@ -270,42 +275,29 @@ function! s:funcs.buf_add_highlight(bufnr, srcId, hlGroup, line, colStart, colEn @@ -270,42 +275,29 @@ function! s:funcs.buf_add_highlight(bufnr, srcId, hlGroup, line, colStart, colEn
if !has('patch-8.1.1719')
return
endif
let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
let type = 'CocHighlight'.a:hlGroup
if empty(prop_type_get(type))
let opts = get(a:, 1, 0)
let priority = get(opts, 'priority', 0)
call prop_type_add(type, {
\ 'highlight': a:hlGroup,
\ 'priority': type(priority) == 0 ? priority : 0,
\ 'combine': get(opts, 'combine', 1),
\ 'start_incl': get(opts, 'start_incl', 0),
\ 'end_incl': get(opts, 'end_incl', 0),
\ })
endif
let total = strlen(getbufline(bufnr, a:line + 1)[0])
let end = a:colEnd
if end == -1
let end = total
if a:srcId == 0
let srcId = s:max_src_id + 1
let s:max_src_id = srcId
else
let end = min([end, total])
let srcId = a:srcId
endif
if end <= a:colStart
return
let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
let type = srcId == -1 ? a:hlGroup : a:hlGroup.'_'.srcId
let types = get(s:id_types, srcId, [])
if index(types, type) == -1
call add(types, type)
let s:id_types[srcId] = types
if empty(prop_type_get(type))
call prop_type_add(type, extend({'highlight': a:hlGroup}, get(a:, 1, {})))
endif
endif
let srcId = a:srcId
if srcId == 0
while v:true
let srcId = srcId + 1
if empty(prop_find({'id': s:prop_offset + srcId, 'lnum' : 1}))
break
endif
endwhile
" generate srcId
let end = a:colEnd == -1 ? strlen(getbufline(bufnr, a:line + 1)[0]) + 1 : a:colEnd + 1
if end < a:colStart + 1
return
endif
let id = srcId == -1 ? 0 : s:prop_offset + srcId
let id = s:generate_id(a:bufnr)
try
call prop_add(a:line + 1, a:colStart + 1, {'length': end - a:colStart, 'bufnr': bufnr, 'type': type, 'id': id})
call prop_add(a:line + 1, a:colStart + 1, {'bufnr': bufnr, 'type': type, 'id': id, 'end_col': end})
catch /^Vim\%((\a\+)\)\=:E967/
" ignore 967
endtry
@ -323,13 +315,18 @@ function! s:funcs.buf_clear_namespace(bufnr, srcId, startLine, endLine) abort @@ -323,13 +315,18 @@ function! s:funcs.buf_clear_namespace(bufnr, srcId, startLine, endLine) abort
let start = a:startLine + 1
let end = a:endLine == -1 ? len(getbufline(bufnr, 1, '$')) : a:endLine
if a:srcId == -1
if has_key(s:buffer_id, a:bufnr)
unlet s:buffer_id[a:bufnr]
endif
call prop_clear(start, end, {'bufnr' : bufnr})
else
try
call prop_remove({'bufnr': bufnr, 'all': 1, 'id': s:prop_offset + a:srcId}, start, end)
catch /^Vim\%((\a\+)\)\=:E968/
" ignore 968
endtry
for type in get(s:id_types, a:srcId, [])
try
call prop_remove({'bufnr': bufnr, 'all': 1, 'type': type}, start, end)
catch /^Vim\%((\a\+)\)\=:E968/
" ignore 968
endtry
endfor
endif
endfunction
@ -358,11 +355,12 @@ function! s:funcs.buf_get_lines(bufnr, start, end, strict) abort @@ -358,11 +355,12 @@ function! s:funcs.buf_get_lines(bufnr, start, end, strict) abort
endfunction
function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort
if !bufloaded(a:bufnr)
let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
if !bufloaded(bufnr)
return
endif
let replacement = get(a:, 1, [])
let lineCount = s:buf_line_count(a:bufnr)
let lineCount = s:buf_line_count(bufnr)
let startLnum = a:start >= 0 ? a:start + 1 : lineCount + a:start + 2
let end = a:end >= 0 ? a:end : lineCount + a:end + 1
if end == lineCount + 1
@ -371,11 +369,11 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort @@ -371,11 +369,11 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort
let delCount = end - (startLnum - 1)
let changeBuffer = 0
let curr = bufnr('%')
if a:bufnr != curr && !exists('*setbufline')
if bufnr != curr && !exists('*setbufline')
let changeBuffer = 1
exe 'buffer '.a:bufnr
exe 'buffer '.bufnr
endif
if a:bufnr == curr || changeBuffer
if bufnr == curr || changeBuffer
" replace
let storeView = winsaveview()
if delCount == len(replacement)
@ -387,14 +385,18 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort @@ -387,14 +385,18 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort
if delCount
let start = startLnum + len(replacement)
let saved_reg = @"
let system_reg = @*
if has('clipboard')
let system_reg = @*
endif
if exists('*deletebufline')
silent call deletebufline(curr, start, start + delCount - 1)
else
silent execute start . ','.(start + delCount - 1).'d'
endif
let @" = saved_reg
let @* = system_reg
if has('clipboard')
let @* = system_reg
endif
endif
endif
call winrestview(storeView)
@ -405,20 +407,24 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort @@ -405,20 +407,24 @@ function! s:funcs.buf_set_lines(bufnr, start, end, strict, ...) abort
" replace
if delCount == len(replacement)
" 8.0.1039
call setbufline(a:bufnr, startLnum, replacement)
call setbufline(bufnr, startLnum, replacement)
else
if len(replacement)
" 8.10037
call appendbufline(a:bufnr, startLnum - 1, replacement)
call appendbufline(bufnr, startLnum - 1, replacement)
endif
if delCount
let start = startLnum + len(replacement)
let saved_reg = @"
let system_reg = @*
if has('clipboard')
let system_reg = @*
endif
"8.1.0039
silent call deletebufline(a:bufnr, start, start + delCount - 1)
silent call deletebufline(bufnr, start, start + delCount - 1)
let @" = saved_reg
let @* = system_reg
if has('clipboard')
let @* = system_reg
endif
endif
endif
endif
@ -520,12 +526,12 @@ function! s:funcs.win_get_cursor(win_id) abort @@ -520,12 +526,12 @@ function! s:funcs.win_get_cursor(win_id) abort
return get(ref, 'out', 0)
endfunction
function! s:funcs.win_get_var(win_id, name) abort
function! s:funcs.win_get_var(win_id, name, ...) abort
let tabnr = s:get_tabnr(a:win_id)
if tabnr
return gettabwinvar(tabnr, a:win_id, a:name)
return gettabwinvar(tabnr, a:win_id, a:name, get(a:, 1, v:null))
endif
throw 'window '.a:win_id. ' not a valid window'
throw 'window '.a:win_id. ' not a visible window'
endfunction
function! s:funcs.win_set_width(win_id, width) abort
@ -568,7 +574,7 @@ function! s:funcs.win_set_var(win_id, name, value) abort @@ -568,7 +574,7 @@ function! s:funcs.win_set_var(win_id, name, value) abort
if tabnr
call settabwinvar(tabnr, a:win_id, a:name, a:value)
else
throw 'window '.a:win_id. ' not a valid window'
throw "Invalid window id ".a:win_id
endif
endfunction
@ -639,8 +645,35 @@ function! s:funcs.tabpage_get_win(tabnr) @@ -639,8 +645,35 @@ function! s:funcs.tabpage_get_win(tabnr)
let wnr = tabpagewinnr(a:tabnr)
return win_getid(wnr, a:tabnr)
endfunction
function! s:generate_id(bufnr) abort
let max = get(s:buffer_id, a:bufnr, s:prop_offset)
let id = max + 1
let s:buffer_id[a:bufnr] = id
return id
endfunction
" }}
function! coc#api#get_types(srcId) abort
return get(s:id_types, a:srcId, [])
endfunction
function! coc#api#get_id_types() abort
return s:id_types
endfunction
function! coc#api#create_type(srcId, hlGroup, opts) abort
let type = a:hlGroup.'_'.a:srcId
let types = get(s:id_types, a:srcId, [])
if index(types, type) == -1
call add(types, type)
let s:id_types[a:srcId] = types
let combine = get(a:opts, 'hl_mode', 'combine') ==# 'combine'
call prop_type_add(type, {'highlight': a:hlGroup, 'combine': combine})
endif
return type
endfunction
function! coc#api#func_names() abort
return keys(s:funcs)
endfunction
@ -651,12 +684,21 @@ function! coc#api#call(method, args) abort @@ -651,12 +684,21 @@ function! coc#api#call(method, args) abort
try
let res = call(s:funcs[a:method], a:args)
catch /.*/
let err = v:exception
let err = v:exception .' on api "'.a:method.'" '.json_encode(a:args)
endtry
return [err, res]
endfunction
function! coc#api#exec(method, args) abort
return call(s:funcs[a:method], a:args)
endfunction
function! coc#api#notify(method, args) abort
call call(s:funcs[a:method], a:args)
try
call call(s:funcs[a:method], a:args)
catch /.*/
let g:b = v:exception
call coc#rpc#notify('nvim_error_event', [0, v:exception.' on api "'.a:method.'" '.json_encode(a:args)])
endtry
endfunction
" vim: set sw=2 ts=2 sts=2 et tw=78 foldmarker={{,}} foldmethod=marker foldlevel=0:

43
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/client.vim

@ -5,7 +5,7 @@ let s:is_win = has("win32") || has("win64") @@ -5,7 +5,7 @@ let s:is_win = has("win32") || has("win64")
let s:clients = {}
if get(g:, 'node_client_debug', 0)
echohl WarningMsg | echon '[coc.nvim] Enable g:node_client_debug could impact your vim experience' | echohl None
echohl WarningMsg | echo '[coc.nvim] Enable g:node_client_debug could impact your vim experience' | echohl None
let $NODE_CLIENT_LOG_LEVEL = 'debug'
if exists('$NODE_CLIENT_LOG_FILE')
let s:logfile = resolve($NODE_CLIENT_LOG_FILE)
@ -43,9 +43,13 @@ function! s:start() dict @@ -43,9 +43,13 @@ function! s:start() dict
return
endif
let timeout = string(get(g:, 'coc_channel_timeout', 30))
let disable_warning = string(get(g:, 'coc_disable_startup_warning', 0))
let tmpdir = fnamemodify(tempname(), ':p:h')
if s:is_vim
if get(g:, 'node_client_debug', 0)
let file = tmpdir . '/coc.log'
call ch_logfile(file, 'w')
echohl MoreMsg | echo '[coc.nvim] channel log to '.file | echohl None
endif
let options = {
\ 'in_mode': 'json',
\ 'out_mode': 'json',
@ -125,12 +129,43 @@ endfunction @@ -125,12 +129,43 @@ endfunction
function! s:on_stderr(name, msgs)
if get(g:, 'coc_vim_leaving', 0) | return | endif
if get(g:, 'coc_disable_uncaught_error', 0) | return | endif
let data = filter(copy(a:msgs), '!empty(v:val)')
if empty(data) | return | endif
let client = a:name ==# 'coc' ? '[coc.nvim]' : '['.a:name.']'
let data[0] = client.': '.data[0]
call coc#util#echo_messages('Error', data)
if a:name ==# 'coc' && len(filter(copy(data), 'v:val =~# "SyntaxError: Unexpected token"'))
call coc#client#check_version()
endif
if get(g:, 'coc_disable_uncaught_error', 0) | return | endif
call coc#ui#echo_messages('Error', data)
endfunction
function! coc#client#check_version() abort
if (has_key(g:, 'coc_node_path'))
let node = expand(g:coc_node_path)
else
let node = $COC_NODE_PATH == '' ? 'node' : $COC_NODE_PATH
endif
let output = system(node . ' --version')
let msgs = []
if v:shell_error
let msgs = ['Unexpected result from node --version'] + split(output, '\n')
else
let ms = matchlist(output, 'v\(\d\+\).\(\d\+\).\(\d\+\)')
if empty(ms)
let msgs = ['Unable to detect version of node, make sure your node executable is http://nodejs.org/']
elseif str2nr(ms[1]) < 14 || (str2nr(ms[1]) == 14 && str2nr(ms[2]) < 14)
let msgs = ['Current Node.js version '.trim(output).' < 14.14.0 ', 'Please upgrade your node.js']
endif
endif
if !empty(msgs)
call coc#notify#create(msgs, {
\ 'borderhighlight': 'CocErrorSign',
\ 'highlight': 'Normal',
\ 'timeout': 50000,
\ 'kind': 'error',
\ })
endif
endfunction
function! s:on_exit(name, code) abort

436
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/color.vim

@ -1,4 +1,250 @@ @@ -1,4 +1,250 @@
scriptencoding utf-8
let s:activate = ""
let s:quit = ""
if has("gui_macvim") && has('gui_running')
let s:app = "MacVim"
elseif $TERM_PROGRAM ==# "Apple_Terminal"
let s:app = "Terminal"
elseif $TERM_PROGRAM ==# "iTerm.app"
let s:app = "iTerm2"
elseif has('mac')
let s:app = "System Events"
let s:quit = "quit"
let s:activate = 'activate'
endif
let s:patterns = {}
let s:patterns['hex'] = '\v#?(\x{2})(\x{2})(\x{2})'
let s:patterns['shortHex'] = '\v#(\x{1})(\x{1})(\x{1})'
let s:xterm_colors = {
\ '0': '#000000', '1': '#800000', '2': '#008000', '3': '#808000', '4': '#000080',
\ '5': '#800080', '6': '#008080', '7': '#c0c0c0', '8': '#808080', '9': '#ff0000',
\ '10': '#00ff00', '11': '#ffff00', '12': '#0000ff', '13': '#ff00ff', '14': '#00ffff',
\ '15': '#ffffff', '16': '#000000', '17': '#00005f', '18': '#000087', '19': '#0000af',
\ '20': '#0000df', '21': '#0000ff', '22': '#005f00', '23': '#005f5f', '24': '#005f87',
\ '25': '#005faf', '26': '#005fdf', '27': '#005fff', '28': '#008700', '29': '#00875f',
\ '30': '#008787', '31': '#0087af', '32': '#0087df', '33': '#0087ff', '34': '#00af00',
\ '35': '#00af5f', '36': '#00af87', '37': '#00afaf', '38': '#00afdf', '39': '#00afff',
\ '40': '#00df00', '41': '#00df5f', '42': '#00df87', '43': '#00dfaf', '44': '#00dfdf',
\ '45': '#00dfff', '46': '#00ff00', '47': '#00ff5f', '48': '#00ff87', '49': '#00ffaf',
\ '50': '#00ffdf', '51': '#00ffff', '52': '#5f0000', '53': '#5f005f', '54': '#5f0087',
\ '55': '#5f00af', '56': '#5f00df', '57': '#5f00ff', '58': '#5f5f00', '59': '#5f5f5f',
\ '60': '#5f5f87', '61': '#5f5faf', '62': '#5f5fdf', '63': '#5f5fff', '64': '#5f8700',
\ '65': '#5f875f', '66': '#5f8787', '67': '#5f87af', '68': '#5f87df', '69': '#5f87ff',
\ '70': '#5faf00', '71': '#5faf5f', '72': '#5faf87', '73': '#5fafaf', '74': '#5fafdf',
\ '75': '#5fafff', '76': '#5fdf00', '77': '#5fdf5f', '78': '#5fdf87', '79': '#5fdfaf',
\ '80': '#5fdfdf', '81': '#5fdfff', '82': '#5fff00', '83': '#5fff5f', '84': '#5fff87',
\ '85': '#5fffaf', '86': '#5fffdf', '87': '#5fffff', '88': '#870000', '89': '#87005f',
\ '90': '#870087', '91': '#8700af', '92': '#8700df', '93': '#8700ff', '94': '#875f00',
\ '95': '#875f5f', '96': '#875f87', '97': '#875faf', '98': '#875fdf', '99': '#875fff',
\ '100': '#878700', '101': '#87875f', '102': '#878787', '103': '#8787af', '104': '#8787df',
\ '105': '#8787ff', '106': '#87af00', '107': '#87af5f', '108': '#87af87', '109': '#87afaf',
\ '110': '#87afdf', '111': '#87afff', '112': '#87df00', '113': '#87df5f', '114': '#87df87',
\ '115': '#87dfaf', '116': '#87dfdf', '117': '#87dfff', '118': '#87ff00', '119': '#87ff5f',
\ '120': '#87ff87', '121': '#87ffaf', '122': '#87ffdf', '123': '#87ffff', '124': '#af0000',
\ '125': '#af005f', '126': '#af0087', '127': '#af00af', '128': '#af00df', '129': '#af00ff',
\ '130': '#af5f00', '131': '#af5f5f', '132': '#af5f87', '133': '#af5faf', '134': '#af5fdf',
\ '135': '#af5fff', '136': '#af8700', '137': '#af875f', '138': '#af8787', '139': '#af87af',
\ '140': '#af87df', '141': '#af87ff', '142': '#afaf00', '143': '#afaf5f', '144': '#afaf87',
\ '145': '#afafaf', '146': '#afafdf', '147': '#afafff', '148': '#afdf00', '149': '#afdf5f',
\ '150': '#afdf87', '151': '#afdfaf', '152': '#afdfdf', '153': '#afdfff', '154': '#afff00',
\ '155': '#afff5f', '156': '#afff87', '157': '#afffaf', '158': '#afffdf', '159': '#afffff',
\ '160': '#df0000', '161': '#df005f', '162': '#df0087', '163': '#df00af', '164': '#df00df',
\ '165': '#df00ff', '166': '#df5f00', '167': '#df5f5f', '168': '#df5f87', '169': '#df5faf',
\ '170': '#df5fdf', '171': '#df5fff', '172': '#df8700', '173': '#df875f', '174': '#df8787',
\ '175': '#df87af', '176': '#df87df', '177': '#df87ff', '178': '#dfaf00', '179': '#dfaf5f',
\ '180': '#dfaf87', '181': '#dfafaf', '182': '#dfafdf', '183': '#dfafff', '184': '#dfdf00',
\ '185': '#dfdf5f', '186': '#dfdf87', '187': '#dfdfaf', '188': '#dfdfdf', '189': '#dfdfff',
\ '190': '#dfff00', '191': '#dfff5f', '192': '#dfff87', '193': '#dfffaf', '194': '#dfffdf',
\ '195': '#dfffff', '196': '#ff0000', '197': '#ff005f', '198': '#ff0087', '199': '#ff00af',
\ '200': '#ff00df', '201': '#ff00ff', '202': '#ff5f00', '203': '#ff5f5f', '204': '#ff5f87',
\ '205': '#ff5faf', '206': '#ff5fdf', '207': '#ff5fff', '208': '#ff8700', '209': '#ff875f',
\ '210': '#ff8787', '211': '#ff87af', '212': '#ff87df', '213': '#ff87ff', '214': '#ffaf00',
\ '215': '#ffaf5f', '216': '#ffaf87', '217': '#ffafaf', '218': '#ffafdf', '219': '#ffafff',
\ '220': '#ffdf00', '221': '#ffdf5f', '222': '#ffdf87', '223': '#ffdfaf', '224': '#ffdfdf',
\ '225': '#ffdfff', '226': '#ffff00', '227': '#ffff5f', '228': '#ffff87', '229': '#ffffaf',
\ '230': '#ffffdf', '231': '#ffffff', '232': '#080808', '233': '#121212', '234': '#1c1c1c',
\ '235': '#262626', '236': '#303030', '237': '#3a3a3a', '238': '#444444', '239': '#4e4e4e',
\ '240': '#585858', '241': '#606060', '242': '#666666', '243': '#767676', '244': '#808080',
\ '245': '#8a8a8a', '246': '#949494', '247': '#9e9e9e', '248': '#a8a8a8', '249': '#b2b2b2',
\ '250': '#bcbcbc', '251': '#c6c6c6', '252': '#d0d0d0', '253': '#dadada', '254': '#e4e4e4',
\ '255': '#eeeeee'}
let s:xterm_16colors = {
\ 'black': '#000000',
\ 'darkblue': '#00008B',
\ 'darkgreen': '#00CD00',
\ 'darkcyan': '#00CDCD',
\ 'darkred': '#CD0000',
\ 'darkmagenta': '#8B008B',
\ 'brown': '#CDCD00',
\ 'darkyellow': '#CDCD00',
\ 'lightgrey': '#E5E5E5',
\ 'lightgray': '#E5E5E5',
\ 'gray': '#E5E5E5',
\ 'grey': '#E5E5E5',
\ 'darkgrey': '#7F7F7F',
\ 'darkgray': '#7F7F7F',
\ 'blue': '#5C5CFF',
\ 'lightblue': '#5C5CFF',
\ 'green': '#00FF00',
\ 'lightgreen': '#00FF00',
\ 'cyan': '#00FFFF',
\ 'lightcyan': '#00FFFF',
\ 'red': '#FF0000',
\ 'lightred': '#FF0000',
\ 'magenta': '#FF00FF',
\ 'lightmagenta': '#FF00FF',
\ 'yellow': '#FFFF00',
\ 'lightyellow': '#FFFF00',
\ 'white': '#FFFFFF',
\ }
let s:w3c_color_names = {
\ 'aliceblue': '#F0F8FF',
\ 'antiquewhite': '#FAEBD7',
\ 'aqua': '#00FFFF',
\ 'aquamarine': '#7FFFD4',
\ 'azure': '#F0FFFF',
\ 'beige': '#F5F5DC',
\ 'bisque': '#FFE4C4',
\ 'black': '#000000',
\ 'blanchedalmond': '#FFEBCD',
\ 'blue': '#0000FF',
\ 'blueviolet': '#8A2BE2',
\ 'brown': '#A52A2A',
\ 'burlywood': '#DEB887',
\ 'cadetblue': '#5F9EA0',
\ 'chartreuse': '#7FFF00',
\ 'chocolate': '#D2691E',
\ 'coral': '#FF7F50',
\ 'cornflowerblue': '#6495ED',
\ 'cornsilk': '#FFF8DC',
\ 'crimson': '#DC143C',
\ 'cyan': '#00FFFF',
\ 'darkblue': '#00008B',
\ 'darkcyan': '#008B8B',
\ 'darkgoldenrod': '#B8860B',
\ 'darkgray': '#A9A9A9',
\ 'darkgreen': '#006400',
\ 'darkkhaki': '#BDB76B',
\ 'darkmagenta': '#8B008B',
\ 'darkolivegreen': '#556B2F',
\ 'darkorange': '#FF8C00',
\ 'darkorchid': '#9932CC',
\ 'darkred': '#8B0000',
\ 'darksalmon': '#E9967A',
\ 'darkseagreen': '#8FBC8F',
\ 'darkslateblue': '#483D8B',
\ 'darkslategray': '#2F4F4F',
\ 'darkturquoise': '#00CED1',
\ 'darkviolet': '#9400D3',
\ 'deeppink': '#FF1493',
\ 'deepskyblue': '#00BFFF',
\ 'dimgray': '#696969',
\ 'dodgerblue': '#1E90FF',
\ 'firebrick': '#B22222',
\ 'floralwhite': '#FFFAF0',
\ 'forestgreen': '#228B22',
\ 'fuchsia': '#FF00FF',
\ 'gainsboro': '#DCDCDC',
\ 'ghostwhite': '#F8F8FF',
\ 'gold': '#FFD700',
\ 'goldenrod': '#DAA520',
\ 'gray': '#808080',
\ 'green': '#008000',
\ 'greenyellow': '#ADFF2F',
\ 'honeydew': '#F0FFF0',
\ 'hotpink': '#FF69B4',
\ 'indianred': '#CD5C5C',
\ 'indigo': '#4B0082',
\ 'ivory': '#FFFFF0',
\ 'khaki': '#F0E68C',
\ 'lavender': '#E6E6FA',
\ 'lavenderblush': '#FFF0F5',
\ 'lawngreen': '#7CFC00',
\ 'lemonchiffon': '#FFFACD',
\ 'lightblue': '#ADD8E6',
\ 'lightcoral': '#F08080',
\ 'lightcyan': '#E0FFFF',
\ 'lightgoldenrodyellow': '#FAFAD2',
\ 'lightgray': '#D3D3D3',
\ 'lightgreen': '#90EE90',
\ 'lightpink': '#FFB6C1',
\ 'lightsalmon': '#FFA07A',
\ 'lightseagreen': '#20B2AA',
\ 'lightskyblue': '#87CEFA',
\ 'lightslategray': '#778899',
\ 'lightsteelblue': '#B0C4DE',
\ 'lightyellow': '#FFFFE0',
\ 'lime': '#00FF00',
\ 'limegreen': '#32CD32',
\ 'linen': '#FAF0E6',
\ 'magenta': '#FF00FF',
\ 'maroon': '#800000',
\ 'mediumaquamarine': '#66CDAA',
\ 'mediumblue': '#0000CD',
\ 'mediumorchid': '#BA55D3',
\ 'mediumpurple': '#9370D8',
\ 'mediumseagreen': '#3CB371',
\ 'mediumslateblue': '#7B68EE',
\ 'mediumspringgreen': '#00FA9A',
\ 'mediumturquoise': '#48D1CC',
\ 'mediumvioletred': '#C71585',
\ 'midnightblue': '#191970',
\ 'mintcream': '#F5FFFA',
\ 'mistyrose': '#FFE4E1',
\ 'moccasin': '#FFE4B5',
\ 'navajowhite': '#FFDEAD',
\ 'navy': '#000080',
\ 'oldlace': '#FDF5E6',
\ 'olive': '#808000',
\ 'olivedrab': '#6B8E23',
\ 'orange': '#FFA500',
\ 'orangered': '#FF4500',
\ 'orchid': '#DA70D6',
\ 'palegoldenrod': '#EEE8AA',
\ 'palegreen': '#98FB98',
\ 'paleturquoise': '#AFEEEE',
\ 'palevioletred': '#D87093',
\ 'papayawhip': '#FFEFD5',
\ 'peachpuff': '#FFDAB9',
\ 'peru': '#CD853F',
\ 'pink': '#FFC0CB',
\ 'plum': '#DDA0DD',
\ 'powderblue': '#B0E0E6',
\ 'purple': '#800080',
\ 'red': '#FF0000',
\ 'rosybrown': '#BC8F8F',
\ 'royalblue': '#4169E1',
\ 'saddlebrown': '#8B4513',
\ 'salmon': '#FA8072',
\ 'sandybrown': '#F4A460',
\ 'seagreen': '#2E8B57',
\ 'seashell': '#FFF5EE',
\ 'sienna': '#A0522D',
\ 'silver': '#C0C0C0',
\ 'skyblue': '#87CEEB',
\ 'slateblue': '#6A5ACD',
\ 'slategray': '#708090',
\ 'snow': '#FFFAFA',
\ 'springgreen': '#00FF7F',
\ 'steelblue': '#4682B4',
\ 'tan': '#D2B48C',
\ 'teal': '#008080',
\ 'thistle': '#D8BFD8',
\ 'tomato': '#FF6347',
\ 'turquoise': '#40E0D0',
\ 'violet': '#EE82EE',
\ 'wheat': '#F5DEB3',
\ 'white': '#FFFFFF',
\ 'whitesmoke': '#F5F5F5',
\ 'yellow': '#FFFF00',
\ 'yellowgreen': '#9ACD32'
\ }
" Returns an approximate grey index for the given grey level
fun! s:grey_number(x)
if &t_Co == 88
@ -184,9 +430,197 @@ fun! s:colour(r, g, b) @@ -184,9 +430,197 @@ fun! s:colour(r, g, b)
endif
endfun
function! coc#color#term2rgb(term) abort
if a:term < 0 || a:term > 255
return '#000000'
endif
return s:xterm_colors[a:term]
endfunction
function! coc#color#rgb2term(rgb)
let l:r = ("0x" . strpart(a:rgb, 0, 2)) + 0
let l:g = ("0x" . strpart(a:rgb, 2, 2)) + 0
let l:b = ("0x" . strpart(a:rgb, 4, 2)) + 0
return s:colour(l:r, l:g, l:b)
endfun
endfunction
function! coc#color#rgbToHex(...)
let [r, g, b] = ( a:0==1 ? a:1 : a:000 )
let num = printf('%02x', float2nr(r)) . ''
\ . printf('%02x', float2nr(g)) . ''
\ . printf('%02x', float2nr(b)) . ''
return '#' . num
endfunction
function! coc#color#hexToRgb(color)
if type(a:color) == 2
let color = printf('%x', a:color)
else
let color = a:color
end
let matches = matchlist(color, s:patterns['hex'])
let factor = 0x1
if empty(matches)
let matches = matchlist(color, s:patterns['shortHex'])
let factor = 0x10
end
if len(matches) < 4
echohl Error
echom 'Couldnt parse ' . string(color) . ' ' . string(matches)
echohl None
return
end
let r = str2nr(matches[1], 16) * factor
let g = str2nr(matches[2], 16) * factor
let b = str2nr(matches[3], 16) * factor
return [r, g, b]
endfunction
" @params String color The color
" @params {Number|String|Float} [amount=5] The percentage of light
function! coc#color#lighten(color, ...)
let amount = a:0 ?
\(type(a:1) < 2 ?
\str2float(a:1) : a:1 )
\: 5.0
if(amount < 1.0)
let amount = 1.0 + amount
else
let amount = 1.0 + (amount / 100.0)
end
let rgb = coc#color#hexToRgb(a:color)
let rgb = map(rgb, 'v:val * amount')
let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val')
let rgb = map(rgb, 'float2nr(v:val)')
let hex = coc#color#rgbToHex(rgb)
return hex
endfunction
" @params String color The color
" @params {Number|String|Float} [amount=5] The percentage of darkness
function! coc#color#darken(color, ...)
let amount = a:0 ?
\(type(a:1) < 2 ?
\str2float(a:1) : a:1 )
\: 5.0
if(amount < 1.0)
let amount = 1.0 - amount
else
let amount = 1.0 - (amount / 100.0)
end
if(amount < 0.0)
let amount = 0.0 | end
let rgb = coc#color#hexToRgb(a:color)
let rgb = map(rgb, 'v:val * amount')
let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val')
let rgb = map(rgb, 'float2nr(v:val)')
let hex = coc#color#rgbToHex(rgb)
return hex
endfu
function! coc#color#luminance(rgb) abort
let vals = []
for val in a:rgb
let val = (val + 0.0)/255
if val <= 0.03928
call add(vals, val/12.92)
else
call add(vals, pow((val + 0.055)/1.055, 2.4))
endif
endfor
return vals[0] * 0.2126 + vals[1] * 0.7152 + vals[2] * 0.0722
endfunction
function! coc#color#contrast(rgb1, rgb2) abort
let lnum1 = coc#color#luminance(a:rgb1)
let lnum2 = coc#color#luminance(a:rgb2)
let brightest = lnum1 > lnum2 ? lnum1 : lnum2
let darkest = lnum1 < lnum2 ? lnum1 : lnum2
return (brightest + 0.05) / (darkest + 0.05)
endfunction
function! coc#color#hex_contrast(hex1, hex2) abort
return coc#color#contrast(coc#color#hexToRgb(a:hex1), coc#color#hexToRgb(a:hex2))
endfunction
function! coc#color#nameToHex(name, term) abort
if a:term
return has_key(s:xterm_16colors, a:name) ? s:xterm_16colors[a:name] : v:null
endif
return has_key(s:w3c_color_names, a:name) ? s:w3c_color_names[a:name] : v:null
endfunction
" [r, g, b] ['255', '255', '255']
" return ['65535', '65535', '65535'] or return v:false to cancel
function! coc#color#pick_color(default_color)
if has('mac')
let default_color = map(a:default_color, {idx, val -> str2nr(val) * 65535 / 255 })
" This is the AppleScript magic:
let ascrpt = ['-e "tell application \"' . s:app . '\""',
\ '-e "' . s:activate . '"',
\ "-e \"set AppleScript's text item delimiters to {\\\",\\\"}\"",
\ '-e "set theColor to (choose color default color {' . default_color[0] . ", " . default_color[1] . ", " . default_color[2] . '}) as text"',
\ '-e "' . s:quit . '"',
\ '-e "end tell"',
\ '-e "return theColor"']
let res = trim(system("osascript " . join(ascrpt, ' ') . " 2>/dev/null"))
if empty(res)
return v:false
else
return split(trim(res), ',')
endif
endif
let hex_color = printf('#%02x%02x%02x', a:default_color[0], a:default_color[1], a:default_color[2])
if has('unix')
if executable('zenity')
let res = trim(system('zenity --title="Select a color" --color-selection --color="' . hex_color . '" 2> /dev/null'))
if empty(res)
return v:false
else
" res format is rgb(255,255,255)
return map(split(res[4:-2], ','), {idx, val -> string(str2nr(trim(val)) * 65535 / 255)})
endif
endif
endif
let rgb = v:false
if !has('python')
echohl Error | echom 'python support required, checkout :echo has(''python'')' | echohl None
return
endif
try
execute 'py import gtk'
catch /.*/
echohl Error | echom 'python gtk module not found' | echohl None
return
endtry
python << endpython
import vim
import gtk, sys
# message strings
wnd_title_insert = "Insert a color"
csd = gtk.ColorSelectionDialog(wnd_title_insert)
cs = csd.colorsel
cs.set_current_color(gtk.gdk.color_parse(vim.eval("hex_color")))
cs.set_current_alpha(65535)
cs.set_has_opacity_control(False)
# cs.set_has_palette(int(vim.eval("s:display_palette")))
if csd.run()==gtk.RESPONSE_OK:
c = cs.get_current_color()
s = [str(int(c.red)),',',str(int(c.green)),',',str(int(c.blue))]
thecolor = ''.join(s)
vim.command(":let rgb = split('%s',',')" % thecolor)
csd.destroy()
endpython
return rgb
endfunction

44
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/compat.vim

@ -13,7 +13,7 @@ endfunction @@ -13,7 +13,7 @@ endfunction
function! coc#compat#buf_set_lines(bufnr, start, end, replacement) abort
if s:is_vim
call coc#api#notify('buf_set_lines', [a:bufnr, a:start, a:end, 0, a:replacement])
call coc#api#exec('buf_set_lines', [a:bufnr, a:start, a:end, 0, a:replacement])
else
call nvim_buf_set_lines(a:bufnr, a:start, a:end, 0, a:replacement)
endif
@ -98,13 +98,7 @@ function! coc#compat#matchaddpos(group, pos, priority, winid) abort @@ -98,13 +98,7 @@ function! coc#compat#matchaddpos(group, pos, priority, winid) abort
call matchaddpos(a:group, a:pos, a:priority, -1, {'window': a:winid})
endif
else
if has('nvim-0.4.0')
call matchaddpos(a:group, a:pos, a:priority, -1, {'window': a:winid})
elseif exists('*nvim_set_current_win')
noa call nvim_set_current_win(a:winid)
call matchaddpos(a:group, a:pos, a:priority, -1)
noa call nvim_set_current_win(curr)
endif
call matchaddpos(a:group, a:pos, a:priority, -1, {'window': a:winid})
endif
endif
endfunction
@ -129,25 +123,9 @@ endfunction @@ -129,25 +123,9 @@ endfunction
" hlGroup, pos, priority
function! coc#compat#matchaddgroups(winid, groups) abort
" add by winid
if has('patch-8.1.0218') || has('nvim-0.4.0')
for group in a:groups
call matchaddpos(group['hlGroup'], [group['pos']], group['priority'], -1, {'window': a:winid})
endfor
return
endif
let curr = win_getid()
if curr == a:winid
for group in a:groups
call matchaddpos(group['hlGroup'], [group['pos']], group['priority'], -1)
endfor
elseif exists('*nvim_set_current_win')
noa call nvim_set_current_win(a:winid)
for group in a:groups
call matchaddpos(group['hlGroup'], [group['pos']], group['priority'], -1)
endfor
noa call nvim_set_current_win(curr)
endif
for group in a:groups
call matchaddpos(group['hlGroup'], [group['pos']], group['priority'], -1, {'window': a:winid})
endfor
endfunction
function! coc#compat#del_var(name) abort
@ -167,7 +145,7 @@ function! coc#compat#buf_del_keymap(bufnr, mode, lhs) abort @@ -167,7 +145,7 @@ function! coc#compat#buf_del_keymap(bufnr, mode, lhs) abort
try
call nvim_buf_del_keymap(a:bufnr, a:mode, a:lhs)
catch /^Vim\%((\a\+)\)\=:E5555/
" ignore keymap not exists.
" ignore keymap doesn't exist
endtry
return
endif
@ -231,6 +209,14 @@ function! coc#compat#execute(winid, command, ...) abort @@ -231,6 +209,14 @@ function! coc#compat#execute(winid, command, ...) abort
endif
noa keepalt call nvim_set_current_win(curr)
else
throw 'win_execute not exists, please upgrade vim.'
throw 'win_execute does not exist, please upgrade vim.'
endif
endfunc
function! coc#compat#trim(str)
if exists('*trim')
return trim(a:str)
endif
" TODO trim from beginning
return substitute(a:str, '\s\+$', '', '')
endfunction

6
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/cursor.vim

@ -39,7 +39,7 @@ function! coc#cursor#char_offset() abort @@ -39,7 +39,7 @@ function! coc#cursor#char_offset() abort
return offset
endfunction
" Returns lastest selection range
" Returns latest selection range
function! coc#cursor#get_selection(char) abort
let m = a:char ? 'char' : visualmode()
if empty(m)
@ -47,12 +47,12 @@ function! coc#cursor#get_selection(char) abort @@ -47,12 +47,12 @@ function! coc#cursor#get_selection(char) abort
endif
let [_, sl, sc, soff] = getpos(m ==# 'char' ? "'[" : "'<")
let [_, el, ec, eoff] = getpos(m ==# 'char' ? "']" : "'>")
let start_idx = coc#helper#get_charactor(getline(sl), sc)
let start_idx = coc#string#get_character(getline(sl), sc)
if m ==# 'V'
return [sl - 1, start_idx, el, 0]
endif
let line = getline(el)
let end_idx = coc#helper#get_charactor(line, ec)
let end_idx = coc#string#get_character(line, ec)
if m !=# 'char'
let end_idx = end_idx == strchars(line) ? end_idx : end_idx + 1
endif

667
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/dialog.vim

@ -0,0 +1,667 @@ @@ -0,0 +1,667 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:root = expand('<sfile>:h:h:h')
let s:prompt_win_bufnr = 0
let s:list_win_bufnr = 0
let s:prompt_win_width = get(g:, 'coc_prompt_win_width', 32)
let s:frames = ['· ', '·· ', '···', ' ··', ' ·', ' ']
let s:sign_group = 'PopUpCocDialog'
let s:detail_bufnr = 0
" Float window aside pum
function! coc#dialog#create_pum_float(lines, config) abort
let winid = coc#float#get_float_by_kind('pumdetail')
if empty(a:lines) || !coc#pum#visible()
if winid
call coc#float#close(winid)
endif
return
endif
let pumbounding = coc#pum#info()
let border = get(a:config, 'border', [])
let pw = pumbounding['width'] + (pumbounding['border'] ? 0 : get(pumbounding, 'scrollbar', 0))
let rp = &columns - pumbounding['col'] - pw
let showRight = pumbounding['col'] > rp ? 0 : 1
let maxWidth = showRight ? coc#math#min(rp - 1, a:config['maxWidth']) : coc#math#min(pumbounding['col'] - 1, a:config['maxWidth'])
let bh = get(border, 0 ,0) + get(border, 2, 0)
let maxHeight = &lines - pumbounding['row'] - &cmdheight - 1 - bh
if maxWidth <= 2 || maxHeight < 1
return v:null
endif
let width = 0
for line in a:lines
let dw = max([1, strdisplaywidth(line)])
let width = max([width, dw + 2])
endfor
let width = float2nr(coc#math#min(maxWidth, width))
let ch = coc#string#content_height(a:lines, width - 2)
let height = float2nr(coc#math#min(maxHeight, ch))
let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width - 2 + (s:is_vim && ch > height ? -1 : 0)) : s})
let opts = {
\ 'lines': lines,
\ 'highlights': get(a:config, 'highlights', []),
\ 'relative': 'editor',
\ 'col': showRight ? pumbounding['col'] + pw : pumbounding['col'] - width,
\ 'row': pumbounding['row'],
\ 'height': height,
\ 'width': width - 2 + (s:is_vim && ch > height ? -1 : 0),
\ 'scrollinside': showRight ? 0 : 1,
\ 'codes': get(a:config, 'codes', []),
\ }
for key in ['border', 'highlight', 'borderhighlight', 'winblend', 'focusable', 'shadow', 'rounded']
if has_key(a:config, key)
let opts[key] = a:config[key]
endif
endfor
call s:close_auto_hide_wins(winid)
let result = coc#float#create_float_win(winid, s:detail_bufnr, opts)
if empty(result)
return
endif
let s:detail_bufnr = result[1]
call setwinvar(result[0], 'kind', 'pumdetail')
if !s:is_vim
call coc#float#nvim_scrollbar(result[0])
endif
endfunction
" Float window below/above cursor
function! coc#dialog#create_cursor_float(winid, bufnr, lines, config) abort
if coc#prompt#activated()
return v:null
endif
let pumAlignTop = get(a:config, 'pumAlignTop', 0)
let modes = get(a:config, 'modes', ['n', 'i', 'ic', 's'])
let mode = mode()
let currbuf = bufnr('%')
let pos = [line('.'), col('.')]
if index(modes, mode) == -1
return v:null
endif
if !s:is_vim && !has('nvim-0.5.0') && mode ==# 'i'
" helps to fix undo issue, don't know why.
call feedkeys("\<C-g>u", 'n')
endif
if mode ==# 's' && has('patch-8.2.4969') && !has('patch-8.2.4996')
echohl WarningMsg | echon 'Popup not created to avoid issue #10466 on vim >= 8.2.4969' | echohl None
return v:null
endif
let dimension = coc#dialog#get_config_cursor(a:lines, a:config)
if empty(dimension)
return v:null
endif
if coc#pum#visible() && ((pumAlignTop && dimension['row'] <0)|| (!pumAlignTop && dimension['row'] > 0))
return v:null
endif
let width = dimension['width']
let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width) : s})
let config = extend(extend({'lines': lines, 'relative': 'cursor'}, a:config), dimension)
call s:close_auto_hide_wins(a:winid)
let res = coc#float#create_float_win(a:winid, a:bufnr, config)
if empty(res)
return v:null
endif
let alignTop = dimension['row'] < 0
let winid = res[0]
let bufnr = res[1]
redraw
if has('nvim')
call coc#float#nvim_scrollbar(winid)
endif
return [currbuf, pos, winid, bufnr, alignTop]
endfunction
" Create float window for input
function! coc#dialog#create_prompt_win(title, default, opts) abort
call s:close_auto_hide_wins()
let bufnr = has('nvim') ? s:prompt_win_bufnr : 0
if s:is_vim
execute 'hi link CocPopupTerminal '.get(a:opts, 'highlight', 'CocFloating')
let node = expand(get(g:, 'coc_node_path', 'node'))
let bufnr = term_start([node, s:root . '/bin/prompt.js', a:default], {
\ 'term_rows': 1,
\ 'term_highlight': 'CocPopupTerminal',
\ 'hidden': 1,
\ 'term_finish': 'close'
\ })
call term_setapi(bufnr, 'Coc')
call setbufvar(bufnr, 'current', type(a:default) == v:t_string ? a:default : '')
endif
let config = s:get_prompt_dimension(a:title, a:default, a:opts)
let res = coc#float#create_float_win(0, bufnr, extend(config, {
\ 'style': 'minimal',
\ 'border': get(a:opts, 'border', [1,1,1,1]),
\ 'rounded': get(a:opts, 'rounded', 1),
\ 'prompt': 1,
\ 'title': a:title,
\ 'lines': s:is_vim ? v:null : [a:default],
\ 'highlight': get(a:opts, 'highlight', 'CocFloating'),
\ 'borderhighlight': [get(a:opts, 'borderhighlight', 'CocFloating')],
\ }))
if empty(res)
return
endif
let winid = res[0]
let bufnr = res[1]
if has('nvim')
let s:prompt_win_bufnr = res[1]
call sign_unplace(s:sign_group, { 'buffer': s:prompt_win_bufnr })
call nvim_set_current_win(winid)
inoremap <buffer> <C-a> <Home>
inoremap <buffer><expr><C-e> pumvisible() ? "\<C-e>" : "\<End>"
exe 'imap <silent><nowait><buffer> <esc> <esc><esc>'
exe 'nnoremap <silent><buffer> <esc> :call coc#float#close('.winid.')<CR>'
exe 'inoremap <silent><expr><nowait><buffer> <cr> "\<C-r>=coc#dialog#prompt_insert(getline(''.''))\<cr>\<esc>"'
if get(a:opts, 'list', 0)
for key in ['<C-j>', '<C-k>', '<C-n>', '<C-p>', '<up>', '<down>', '<C-f>', '<C-b>', '<C-space>']
" Can't use < in remap
let escaped = key ==# '<C-space>' ? "C-@" : strcharpart(key, 1, strchars(key) - 2)
exe 'inoremap <nowait><buffer> '.key.' <Cmd>call coc#rpc#notify("PromptKeyPress", ['.bufnr.', "'.escaped.'"])<CR>'
endfor
endif
call feedkeys('A', 'in')
endif
call coc#util#do_autocmd('CocOpenFloatPrompt')
if s:is_vim
let pos = popup_getpos(winid)
" width height row col
let dimension = [pos['width'], pos['height'], pos['line'] - 1, pos['col'] - 1]
else
let id = coc#float#get_related(winid, 'border')
if !has('nvim-0.6.0')
redraw
endif
let pos = nvim_win_get_position(id)
let dimension = [nvim_win_get_width(id), nvim_win_get_height(id), pos[0], pos[1]]
endif
return [bufnr, winid, dimension]
endfunction
" Create list window under target window
function! coc#dialog#create_list(target, dimension, opts) abort
let maxHeight = get(a:opts, 'maxHeight', 10)
let height = max([1, len(get(a:opts, 'lines', []))])
let height = min([maxHeight, height, &lines - &cmdheight - 1 - a:dimension['row'] + a:dimension['height']])
let chars = get(a:opts, 'rounded', 1) ? ['╯', '╰'] : ['┘', '└']
let config = extend(copy(a:opts), {
\ 'relative': 'editor',
\ 'row': a:dimension['row'] + a:dimension['height'],
\ 'col': a:dimension['col'],
\ 'width': a:dimension['width'] - 2,
\ 'height': height,
\ 'border': [1, 1, 1, 1],
\ 'scrollinside': 1,
\ 'borderchars': extend(['─', '│', '─', '│', '├', '┤'], chars)
\ })
let bufnr = 0
let result = coc#float#create_float_win(0, s:list_win_bufnr, config)
if empty(result)
return
endif
let winid = result[0]
call coc#float#add_related(winid, a:target)
call setwinvar(winid, 'auto_height', get(a:opts, 'autoHeight', 1))
call setwinvar(winid, 'max_height', maxHeight)
call setwinvar(winid, 'target_winid', a:target)
call setwinvar(winid, 'kind', 'list')
call coc#dialog#check_scroll_vim(a:target)
return result
endfunction
" Create menu picker for pick single item
function! coc#dialog#create_menu(lines, config) abort
call s:close_auto_hide_wins()
let highlight = get(a:config, 'highlight', 'CocFloating')
let borderhighlight = get(a:config, 'borderhighlight', [highlight])
let relative = get(a:config, 'relative', 'cursor')
let lines = copy(a:lines)
let content = get(a:config, 'content', '')
let maxWidth = get(a:config, 'maxWidth', 80)
let highlights = get(a:config, 'highlights', [])
let contentCount = 0
if !empty(content)
let contentLines = coc#string#reflow(split(content, '\r\?\n'), maxWidth)
let contentCount = len(contentLines)
let lines = extend(contentLines, lines)
if !empty(highlights)
for item in highlights
let item['lnum'] = item['lnum'] + contentCount
endfor
endif
endif
let opts = {
\ 'lines': lines,
\ 'highlight': highlight,
\ 'title': get(a:config, 'title', ''),
\ 'borderhighlight': borderhighlight,
\ 'maxWidth': maxWidth,
\ 'maxHeight': get(a:config, 'maxHeight', 80),
\ 'rounded': get(a:config, 'rounded', 0),
\ 'border': [1, 1, 1, 1],
\ 'highlights': highlights,
\ 'relative': relative,
\ }
if relative ==# 'editor'
let dimension = coc#dialog#get_config_editor(lines, opts)
else
let dimension = coc#dialog#get_config_cursor(lines, opts)
endif
call extend(opts, dimension)
let ids = coc#float#create_float_win(0, s:prompt_win_bufnr, opts)
if empty(ids)
return
endif
let s:prompt_win_bufnr = ids[1]
call coc#dialog#set_cursor(ids[0], ids[1], contentCount + 1)
redraw
if has('nvim')
call coc#float#nvim_scrollbar(ids[0])
endif
return [ids[0], ids[1], contentCount]
endfunction
" Create dialog at center of screen
function! coc#dialog#create_dialog(lines, config) abort
call s:close_auto_hide_wins()
" dialog always have borders
let title = get(a:config, 'title', '')
let buttons = get(a:config, 'buttons', [])
let highlight = get(a:config, 'highlight', 'CocFloating')
let borderhighlight = get(a:config, 'borderhighlight', [highlight])
let opts = {
\ 'title': title,
\ 'rounded': get(a:config, 'rounded', 0),
\ 'relative': 'editor',
\ 'border': [1,1,1,1],
\ 'close': get(a:config, 'close', 1),
\ 'highlight': highlight,
\ 'highlights': get(a:config, 'highlights', []),
\ 'buttons': buttons,
\ 'borderhighlight': borderhighlight,
\ 'getchar': get(a:config, 'getchar', 0)
\ }
call extend(opts, coc#dialog#get_config_editor(a:lines, a:config))
let bufnr = coc#float#create_buf(0, a:lines)
let res = coc#float#create_float_win(0, bufnr, opts)
if empty(res)
return
endif
if get(a:config, 'cursorline', 0)
call coc#dialog#place_sign(bufnr, 1)
endif
if has('nvim')
redraw
call coc#float#nvim_scrollbar(res[0])
endif
return res
endfunction
function! coc#dialog#prompt_confirm(title, cb) abort
call s:close_auto_hide_wins()
if s:is_vim && exists('*popup_dialog')
try
call popup_dialog(a:title. ' (y/n)?', {
\ 'highlight': 'Normal',
\ 'filter': 'popup_filter_yesno',
\ 'callback': {id, res -> a:cb(v:null, res)},
\ 'borderchars': get(g:, 'coc_borderchars', ['─', '│', '─', '│', '╭', '╮', '╯', '╰']),
\ 'borderhighlight': ['MoreMsg']
\ })
catch /.*/
call a:cb(v:exception)
endtry
return
endif
let text = ' '. a:title . ' (y/n)? '
let maxWidth = coc#math#min(78, &columns - 2)
let width = coc#math#min(maxWidth, strdisplaywidth(text))
let maxHeight = &lines - &cmdheight - 1
let height = coc#math#min(maxHeight, float2nr(ceil(str2float(string(strdisplaywidth(text)))/width)))
let arr = coc#float#create_float_win(0, s:prompt_win_bufnr, {
\ 'col': &columns/2 - width/2 - 1,
\ 'row': maxHeight/2 - height/2 - 1,
\ 'width': width,
\ 'height': height,
\ 'border': [1,1,1,1],
\ 'focusable': v:false,
\ 'relative': 'editor',
\ 'highlight': 'Normal',
\ 'borderhighlight': 'MoreMsg',
\ 'style': 'minimal',
\ 'lines': [text],
\ })
if empty(arr)
call a:cb('Window create failed!')
return
endif
let winid = arr[0]
let s:prompt_win_bufnr = arr[1]
call sign_unplace(s:sign_group, { 'buffer': s:prompt_win_bufnr })
let res = 0
redraw
" same result as vim
while 1
let key = nr2char(getchar())
if key == "\<C-c>"
let res = -1
break
elseif key == "\<esc>" || key == 'n' || key == 'N'
let res = 0
break
elseif key == 'y' || key == 'Y'
let res = 1
break
endif
endw
call coc#float#close(winid)
call a:cb(v:null, res)
endfunction
function! coc#dialog#get_config_editor(lines, config) abort
let title = get(a:config, 'title', '')
let maxheight = min([get(a:config, 'maxHeight', 78), &lines - &cmdheight - 6])
let maxwidth = min([get(a:config, 'maxWidth', 78), &columns - 2])
let buttons = get(a:config, 'buttons', [])
let minwidth = s:min_btns_width(buttons)
if maxheight <= 0 || maxwidth <= 0 || minwidth > maxwidth
throw 'Not enough spaces for float window'
endif
let ch = 0
let width = min([strdisplaywidth(title) + 1, maxwidth])
for line in a:lines
let dw = max([1, strdisplaywidth(line)])
if dw < maxwidth && dw > width
let width = dw
elseif dw >= maxwidth
let width = maxwidth
endif
let ch += float2nr(ceil(str2float(string(dw))/maxwidth))
endfor
let width = max([minwidth, width])
let height = coc#math#min(ch ,maxheight)
return {
\ 'row': &lines/2 - (height + 4)/2,
\ 'col': &columns/2 - (width + 2)/2,
\ 'width': width,
\ 'height': height,
\ }
endfunction
function! coc#dialog#prompt_insert(text) abort
call coc#rpc#notify('PromptInsert', [a:text, bufnr('%')])
return ''
endfunction
" Dimension of window with lines relative to cursor
" Width & height excludes border & padding
function! coc#dialog#get_config_cursor(lines, config) abort
let preferTop = get(a:config, 'preferTop', 0)
let title = get(a:config, 'title', '')
let border = get(a:config, 'border', [])
if empty(border) && len(title)
let border = [1, 1, 1, 1]
endif
let bh = get(border, 0, 0) + get(border, 2, 0)
let vh = &lines - &cmdheight - 1
if vh <= 0
return v:null
endif
let maxWidth = coc#math#min(get(a:config, 'maxWidth', &columns - 1), &columns - 1)
if maxWidth < 3
return v:null
endif
let maxHeight = coc#math#min(get(a:config, 'maxHeight', vh), vh)
let width = coc#math#min(40, strdisplaywidth(title)) + 3
for line in a:lines
let dw = max([1, strdisplaywidth(line)])
let width = max([width, dw + 2])
endfor
let width = coc#math#min(maxWidth, width)
let ch = coc#string#content_height(a:lines, width - 2)
let [lineIdx, colIdx] = coc#cursor#screen_pos()
" How much we should move left
let offsetX = coc#math#min(get(a:config, 'offsetX', 0), colIdx)
let showTop = 0
let hb = vh - lineIdx -1
if lineIdx > bh + 2 && (preferTop || (lineIdx > hb && hb < ch + bh))
let showTop = 1
endif
let height = coc#math#min(maxHeight, ch + bh, showTop ? lineIdx - 1 : hb)
if height <= bh
return v:null
endif
let col = - max([offsetX, colIdx - (&columns - 1 - width)])
let row = showTop ? - height + bh : 1
return {
\ 'row': row,
\ 'col': col,
\ 'width': width - 2,
\ 'height': height - bh
\ }
endfunction
function! coc#dialog#change_border_hl(winid, hlgroup) abort
if !hlexists(a:hlgroup)
return
endif
if s:is_vim
if coc#float#valid(a:winid)
call popup_setoptions(a:winid, {'borderhighlight': repeat([a:hlgroup], 4)})
redraw
endif
else
let winid = coc#float#get_related(a:winid, 'border')
if winid > 0
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup)
endif
endif
endfunction
function! coc#dialog#change_title(winid, title) abort
if s:is_vim
if coc#float#valid(a:winid)
call popup_setoptions(a:winid, {'title': a:title})
redraw
endif
else
let winid = coc#float#get_related(a:winid, 'border')
if winid > 0
let bufnr = winbufnr(winid)
let line = getbufline(bufnr, 1)[0]
let top = strcharpart(line, 0, 1)
\.repeat('─', strchars(line) - 2)
\.strcharpart(line, strchars(line) - 1, 1)
if !empty(a:title)
let top = coc#string#compose(top, 1, a:title.' ')
endif
call nvim_buf_set_lines(bufnr, 0, 1, v:false, [top])
endif
endif
endfunction
function! coc#dialog#change_loading(winid, loading) abort
if coc#float#valid(a:winid)
let winid = coc#float#get_related(a:winid, 'loading')
if !a:loading && winid > 0
call coc#float#close(winid)
endif
if a:loading && winid == 0
let bufnr = s:create_loading_buf()
if s:is_vim
let pos = popup_getpos(a:winid)
let winid = popup_create(bufnr, {
\ 'line': pos['line'] + 1,
\ 'col': pos['col'] + pos['width'] - 4,
\ 'maxheight': 1,
\ 'maxwidth': 3,
\ 'zindex': 999,
\ 'highlight': get(popup_getoptions(a:winid), 'highlight', 'CocFloating')
\ })
else
let pos = nvim_win_get_position(a:winid)
let width = nvim_win_get_width(a:winid)
let opts = {
\ 'relative': 'editor',
\ 'row': pos[0],
\ 'col': pos[1] + width - 3,
\ 'focusable': v:false,
\ 'width': 3,
\ 'height': 1,
\ 'style': 'minimal',
\ }
if has('nvim-0.5.1')
let opts['zindex'] = 900
endif
let winid = nvim_open_win(bufnr, v:false, opts)
call setwinvar(winid, '&winhl', getwinvar(a:winid, '&winhl'))
endif
call setwinvar(winid, 'kind', 'loading')
call setbufvar(bufnr, 'target_winid', a:winid)
call setbufvar(bufnr, 'popup', winid)
call coc#float#add_related(winid, a:winid)
endif
endif
endfunction
" Update list with new lines and highlights
function! coc#dialog#update_list(winid, bufnr, lines, highlights) abort
if coc#window#tabnr(a:winid) == tabpagenr()
if getwinvar(a:winid, 'auto_height', 0)
let row = coc#float#get_row(a:winid)
" core height
let height = max([1, len(copy(a:lines))])
let height = min([getwinvar(a:winid, 'max_height', 10), height, &lines - &cmdheight - 1 - row])
let curr = s:is_vim ? popup_getpos(a:winid)['core_height'] : nvim_win_get_height(a:winid)
let delta = height - curr
if delta != 0
call coc#float#change_height(a:winid, delta)
endif
endif
call coc#compat#buf_set_lines(a:bufnr, 0, -1, a:lines)
call coc#highlight#add_highlights(a:winid, [], a:highlights)
if s:is_vim
let target = getwinvar(a:winid, 'target_winid', -1)
if target != -1
call coc#dialog#check_scroll_vim(target)
endif
call win_execute(a:winid, 'exe 1')
endif
endif
endfunction
" Fix width of prompt window same as list window on scrollbar change
function! coc#dialog#check_scroll_vim(winid) abort
if s:is_vim && coc#float#valid(a:winid)
let winid = coc#float#get_related(a:winid, 'list')
if winid
redraw
let pos = popup_getpos(winid)
let width = pos['width'] + (pos['scrollbar'] ? 1 : 0)
if width != popup_getpos(a:winid)['width']
call popup_move(a:winid, {
\ 'minwidth': width - 2,
\ 'maxwidth': width - 2,
\ })
endif
endif
endif
endfunction
function! coc#dialog#set_cursor(winid, bufnr, line) abort
if s:is_vim
call coc#compat#execute(a:winid, 'exe '.a:line, 'silent!')
call popup_setoptions(a:winid, {'cursorline' : 1})
call popup_setoptions(a:winid, {'cursorline' : 0})
else
call nvim_win_set_cursor(a:winid, [a:line, 0])
endif
call coc#dialog#place_sign(a:bufnr, a:line)
endfunction
function! coc#dialog#place_sign(bufnr, line) abort
call sign_unplace(s:sign_group, { 'buffer': a:bufnr })
if a:line > 0
call sign_place(6, s:sign_group, 'CocCurrentLine', a:bufnr, {'lnum': a:line})
endif
endfunction
" Could be center(with optional marginTop) or cursor
function! s:get_prompt_dimension(title, default, opts) abort
let relative = get(a:opts, 'position', 'cursor') ==# 'cursor' ? 'cursor' : 'editor'
let curr = win_screenpos(winnr())[1] + wincol() - 2
let minWidth = get(a:opts, 'minWidth', s:prompt_win_width)
let width = min([max([strwidth(a:default) + 2, strwidth(a:title) + 2, minWidth]), &columns - 2])
if get(a:opts, 'maxWidth', 0)
let width = min([width, a:opts['maxWidth']])
endif
if relative ==# 'cursor'
let [lineIdx, colIdx] = coc#cursor#screen_pos()
if width == &columns - 2
let col = 0 - curr
else
let col = curr + width <= &columns - 2 ? 0 : curr + width - &columns + 2
endif
let config = {
\ 'row': lineIdx == 0 ? 1 : 0,
\ 'col': colIdx == 0 ? 0 : col - 1,
\ }
else
let marginTop = get(a:opts, 'marginTop', v:null)
if marginTop is v:null
let row = (&lines - &cmdheight - 2) / 2
else
let row = marginTop < 2 ? 1 : min([marginTop, &columns - &cmdheight])
endif
let config = {
\ 'col': float2nr((&columns - width) / 2),
\ 'row': row - s:is_vim,
\ }
endif
return extend(config, {'relative': relative, 'width': width, 'height': 1})
endfunction
function! s:min_btns_width(buttons) abort
if empty(a:buttons)
return 0
endif
let minwidth = len(a:buttons)*3 - 1
for txt in a:buttons
let minwidth = minwidth + strdisplaywidth(txt)
endfor
return minwidth
endfunction
" Close windows that should auto hide
function! s:close_auto_hide_wins(...) abort
let winids = coc#float#get_float_win_list()
let except = get(a:, 1, 0)
for id in winids
if except && id == except
continue
endif
if coc#window#get_var(id, 'autohide', 0)
call coc#float#close(id)
endif
endfor
endfunction
function! s:create_loading_buf() abort
let bufnr = coc#float#create_buf(0)
call s:change_loading_buf(bufnr, 0)
return bufnr
endfunction
function! s:change_loading_buf(bufnr, idx) abort
if bufloaded(a:bufnr)
let target = getbufvar(a:bufnr, 'target_winid', v:null)
if !empty(target) && !coc#float#valid(target)
call coc#float#close(getbufvar(a:bufnr, 'popup'))
return
endif
let line = get(s:frames, a:idx, ' ')
call setbufline(a:bufnr, 1, line)
call coc#highlight#add_highlight(a:bufnr, -1, 'CocNotificationProgress', 0, 0, -1)
let idx = a:idx == len(s:frames) - 1 ? 0 : a:idx + 1
call timer_start(100, { -> s:change_loading_buf(a:bufnr, idx)})
endif
endfunction

32
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/dict.vim

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
scriptencoding utf-8
function! coc#dict#equal(one, two) abort
for key in keys(a:one)
if a:one[key] != a:two[key]
return 0
endif
endfor
return 1
endfunction
" Return new dict with keys removed
function! coc#dict#omit(dict, keys) abort
let res = {}
for key in keys(a:dict)
if index(a:keys, key) == -1
let res[key] = a:dict[key]
endif
endfor
return res
endfunction
" Return new dict with keys only
function! coc#dict#pick(dict, keys) abort
let res = {}
for key in keys(a:dict)
if index(a:keys, key) != -1
let res[key] = a:dict[key]
endif
endfor
return res
endfunction

1391
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/float.vim

File diff suppressed because it is too large Load Diff

533
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/highlight.vim

@ -1,19 +1,21 @@ @@ -1,19 +1,21 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:clear_match_by_window = has('nvim-0.5.0') || has('patch-8.1.1084')
let s:set_extmark = exists('*nvim_buf_set_extmark')
let s:clear_match_by_window = has('nvim-0.6.0') || has('patch-8.1.1084')
let s:set_extmark = has('nvim') && exists('*nvim_buf_set_extmark')
let s:prop_offset = get(g:, 'coc_text_prop_offset', 1000)
let s:namespace_map = {}
let s:ns_id = 1
let g:coc_highlight_batch_lines = get(g:, 'coc_highlight_batch_lines', 300)
" Maxium count to highlight each time.
let g:coc_highlight_maximum_count = get(g:, 'coc_highlight_batch_count', 300)
let s:diagnostic_hlgroups = ['CocErrorHighlight', 'CocWarningHighlight', 'CocInfoHighlight', 'CocHintHighlight', 'CocDeprecatedHighlight', 'CocUnusedHighlight']
" Maximum count to highlight each time.
let g:coc_highlight_maximum_count = get(g:, 'coc_highlight_maximum_count', 100)
let s:term = &termguicolors == 0 && !has('gui_running')
if has('nvim-0.5.0')
if has('nvim-0.5.0') && s:clear_match_by_window == 0
try
call getmatches(0)
let s:clear_match_by_window = 1
catch /^Vim\%((\a\+)\)\=:E118/
let s:clear_match_by_window = 0
" ignored
endtry
endif
@ -32,82 +34,17 @@ function! coc#highlight#buffer_update(bufnr, key, highlights, ...) abort @@ -32,82 +34,17 @@ function! coc#highlight#buffer_update(bufnr, key, highlights, ...) abort
return
endif
let hls = map(copy(a:highlights), "{'hlGroup':v:val[0],'lnum':v:val[1],'colStart':v:val[2],'colEnd':v:val[3],'combine':get(v:val,4,1),'start_incl':get(v:val,5,0),'end_incl':get(v:val,6,0)}")
let total = coc#compat#buf_line_count(a:bufnr)
if total <= g:coc_highlight_batch_lines || get(g:, 'coc_node_env', '') ==# 'test'
if len(hls) <= g:coc_highlight_maximum_count || get(g:, 'coc_node_env', '') ==# 'test'
call coc#highlight#update_highlights(a:bufnr, a:key, hls, 0, -1, priority)
return
endif
if bufnr('%') == a:bufnr
" Highlight visible region first
let ls = line('w0')
let le = line('w$')
let exclude = [ls, le]
let highlights = filter(copy(hls), 'v:val["lnum"]>='.(ls - 1).'&& v:val["lnum"] <='.(le - 1))
call coc#highlight#update_highlights(a:bufnr, a:key, highlights, ls - 1, le, priority)
let re = s:get_highlight_region(0, total, exclude)
if !empty(re)
call timer_start(50, { -> s:update_highlights_timer(a:bufnr, changedtick, a:key, priority, re[0], re[1], total, hls, exclude)})
endif
else
let re = s:get_highlight_region(0, total, v:null)
call s:update_highlights_timer(a:bufnr, changedtick, a:key, priority, re[0], re[1], total, hls, v:null)
endif
endfunction
" Get namespaced coc highlights from range of bufnr
" start - 0 based start line index
" end - 0 based end line index, could be -1 for last line (exclusive)
function! coc#highlight#get(bufnr, key, start, end) abort
if !has_key(s:namespace_map, a:key) || !bufloaded(a:bufnr)
return {}
endif
let ns = coc#highlight#create_namespace(a:key)
let current = {}
if has('nvim-0.5.0')
let end = a:end == -1 ? [-1, -1] : [a:end - 1, -1]
let markers = nvim_buf_get_extmarks(a:bufnr, ns, [a:start, 0], end, {'details': v:true})
let linecount = nvim_buf_line_count(a:bufnr)
for [id, row, start_col, details] in markers
let delta = details['end_row'] - row
if row >= linecount || delta > 1 || (delta == 1 && details['end_col'] != 0)
" Remove unexpected multiple line extmark.
call nvim_buf_del_extmark(a:bufnr, ns, id)
continue
endif
let text = get(getbufline(a:bufnr, row + 1), 0, '')
let curr = get(current, string(row), [])
call add(curr, {
\ 'hlGroup': details['hl_group'],
\ 'lnum': row,
\ 'colStart': start_col,
\ 'colEnd': delta == 1 ? strlen(text) : details['end_col']
\ })
let current[string(row)] = curr
endfor
elseif exists('*prop_list')
let id = s:prop_offset + ns
" we could only get textprops line by line
let end = a:end == -1 ? coc#compat#buf_line_count(a:bufnr) : a:end
for line in range(a:start + 1, end)
let items = []
for prop in prop_list(line, {'bufnr': a:bufnr, 'id': id})
" vim have support for cross line text props, but we're not using
call add(items, {
\ 'hlGroup': s:prop_type_hlgroup(prop['type']),
\ 'lnum': line - 1,
\ 'colStart': prop['col'] - 1,
\ 'colEnd': prop['col'] - 1 + prop['length'] - (prop['end'] == 0 ? 1 : 0),
\ })
endfor
if !empty(items)
let current[string(line - 1)] = items
endif
endfor
endif
return current
let linecount = coc#compat#buf_line_count(a:bufnr)
let groups = s:group_hls(hls, linecount)
call s:update_highlights_timer(a:bufnr, changedtick, a:key, priority, groups, 0)
endfunction
" Update highlights by check exists highlights.
" 0 based, end exclusive start and end
function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
if !bufloaded(bufnr)
@ -115,6 +52,9 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort @@ -115,6 +52,9 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
endif
let start = get(a:, 1, 0)
let end = get(a:, 2, -1)
if end == 0
return
endif
let linecount = coc#compat#buf_line_count(a:bufnr)
if end >= linecount
let end = -1
@ -124,53 +64,60 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort @@ -124,53 +64,60 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
return
endif
let priority = get(a:, 3, v:null)
if type(get(a:, 4, v:null)) == 0 && getbufvar(bufnr, 'changedtick') > a:4
return
endif
let total = len(a:highlights)
" index list that exists with current highlights
let exists = []
let ns = coc#highlight#create_namespace(a:key)
let currIndex = 0
if has('nvim-0.5.0') || exists('*prop_list')
let current = coc#highlight#get(bufnr, a:key, start, end)
for lnum in sort(map(keys(current), 'str2nr(v:val)'), {a, b -> a - b})
let items = get(current, lnum, [])
let indexes = []
let nextIndex = currIndex
if currIndex != total
for item in items
for i in range(currIndex, total - 1)
let hi = a:highlights[i]
if hi['lnum'] > item['lnum']
let nextIndex = i
break
endif
if s:same_highlight(item, hi)
call add(indexes, i)
let nextIndex = max([nextIndex, i + 1])
let endLnum = end < 0 ? linecount - 1 : end - 1
let firstLnum = a:highlights[0]['lnum']
if firstLnum > start
call coc#highlight#clear_highlight(bufnr, a:key, start, firstLnum)
let start = firstLnum
endif
let lastLnum = a:highlights[total - 1]['lnum']
if lastLnum < endLnum
call coc#highlight#clear_highlight(bufnr, a:key, lastLnum + 1, endLnum + 1)
let endLnum = lastLnum
endif
let current = coc#highlight#get_highlights(bufnr, a:key, start, endLnum)
let currIndex = 0
if !empty(current)
for [lnum, items] in s:to_group(current)
let indexes = []
let currIndexes = range(0, len(items) - 1)
let removeIndexes = []
while currIndex != total
let hi = a:highlights[currIndex]
if hi['lnum'] == lnum
let findIndex = -1
for idx in currIndexes
let item = items[idx]
if hi['hlGroup'] ==# item[0] && hi['colStart'] == item[2] && hi['colEnd'] == item[3]
call add(indexes, currIndex)
let findIndex = idx
break
elseif item[2] > hi['colStart']
break
endif
endfor
if findIndex != -1
call filter(currIndexes, 'v:val != '.findIndex)
endif
endfor
elseif hi['lnum'] > lnum
break
endif
let currIndex = currIndex + 1
endwhile
for idx in currIndexes
if s:is_vim
call prop_remove({'bufnr': bufnr, 'id': items[idx][4]})
else
call nvim_buf_del_extmark(bufnr, ns, items[idx][4])
endif
endfor
endif
let currIndex = nextIndex
" all highlights of current line exists, not clear.
if len(indexes) == len(items)
call extend(exists, indexes)
else
if has('nvim')
call nvim_buf_clear_namespace(bufnr, ns, lnum, lnum + 1)
else
call coc#api#call('buf_clear_namespace', [bufnr, ns, lnum, lnum + 1])
endif
endif
endfor
if has('nvim')
let count = nvim_buf_line_count(bufnr)
if end == -1 || end == count
" remove highlights exceed last line.
call nvim_buf_clear_namespace(bufnr, ns, count, -1)
endif
endfor
endif
else
call coc#highlight#clear_highlight(bufnr, a:key, start, end)
@ -179,82 +126,72 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort @@ -179,82 +126,72 @@ function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
if !empty(exists)
let indexes = filter(indexes, 'index(exists, v:val) == -1')
endif
for i in indexes
let hi = a:highlights[i]
let opts = {}
for idx in indexes
let hi = a:highlights[idx]
let opts = {
\ 'combine': get(hi, 'combine', 0),
\ 'start_incl': get(hi, 'start_incl', 0),
\ 'end_incl': get(hi, 'end_incl', 0),
\ }
if type(priority) == 0
let opts['priority'] = priority
let opts['priority'] = s:get_priority(a:key, hi['hlGroup'], priority)
endif
for key in ['combine', 'start_incl', 'end_incl']
if has_key(hi, key)
let opts[key] = hi[key]
endif
endfor
call coc#highlight#add_highlight(bufnr, ns, hi['hlGroup'], hi['lnum'], hi['colStart'], hi['colEnd'], opts)
endfor
endfunction
" Get list of highlights by range or all buffer.
" 0 based line, start_col and end_col
function! coc#highlight#get_highlights(bufnr, key) abort
" 0 based start & end line, end inclusive.
function! coc#highlight#get_highlights(bufnr, key, ...) abort
if !bufloaded(a:bufnr)
return v:null
endif
if !has_key(s:namespace_map, a:key)
return []
endif
let start = get(a:, 1, 0)
let end = get(a:, 2, -1)
if has('nvim-0.5.0')
return v:lua.require('coc.highlight').getHighlights(a:bufnr, a:key, start, end)
endif
let res = []
let ns = s:namespace_map[a:key]
if exists('*prop_list')
" Could filter by end_line and types
let types = coc#api#get_types(ns)
if empty(types)
return res
endif
" Could filter by end_lnum and types
if has('patch-8.2.3652')
for prop in prop_list(1, {'bufnr': a:bufnr, 'ids': [s:prop_offset + ns], 'end_lnum': -1})
let endLnum = end == -1 ? -1 : end + 1
for prop in prop_list(start + 1, {'bufnr': a:bufnr, 'types': types, 'end_lnum': endLnum})
if prop['start'] == 0 || prop['end'] == 0
" multi line textprop are not supported, simply ignore it
continue
endif
let hlGroup = s:prop_type_hlgroup(prop['type'])
let startCol = prop['col'] - 1
let endCol = startCol + prop['length']
call add(res, [hlGroup, prop['lnum'] - 1, startCol, endCol])
call add(res, [s:prop_type_hlgroup(prop['type']), prop['lnum'] - 1, startCol, endCol, prop['id']])
endfor
else
let linecount = coc#compat#buf_line_count(a:bufnr)
for line in range(1, linecount)
for prop in prop_list(line, {'bufnr': a:bufnr, 'id': s:prop_offset + ns})
if prop['start'] == 0 || prop['end'] == 0
if end == -1
let end = coc#compat#buf_line_count(a:bufnr)
else
let end = end + 1
endif
for line in range(start + 1, end)
for prop in prop_list(line, {'bufnr': a:bufnr})
if index(types, prop['type']) == -1 || prop['start'] == 0 || prop['end'] == 0
" multi line textprop are not supported, simply ignore it
continue
endif
let startCol = prop['col'] - 1
let endCol = startCol + prop['length']
call add(res, [s:prop_type_hlgroup(prop['type']), line - 1, startCol, endCol])
call add(res, [s:prop_type_hlgroup(prop['type']), line - 1, startCol, endCol, prop['id']])
endfor
endfor
endfor
endif
elseif has('nvim-0.5.0')
let markers = nvim_buf_get_extmarks(a:bufnr, ns, 0, -1, {'details': v:true})
let total = nvim_buf_line_count(a:bufnr)
for [marker_id, line, start_col, details] in markers
if line >= total
" Could be markers exceed end of line
continue
endif
let delta = details['end_row'] - line
if delta > 1 || (delta == 1 && details['end_col'] != 0)
" can't handle, single line only
continue
endif
let endCol = details['end_col']
if endCol == start_col
call nvim_buf_del_extmark(a:bufnr, ns, marker_id)
continue
endif
if delta == 1
let text = get(nvim_buf_get_lines(a:bufnr, line, line + 1, 0), 0, '')
let endCol = strlen(text)
endif
call add(res, [details['hl_group'], line, start_col, endCol, marker_id])
endfor
else
throw 'Get highlights requires neovim 0.5.0 or vim support prop_list'
endif
@ -267,17 +204,21 @@ function! coc#highlight#set(bufnr, key, highlights, priority) abort @@ -267,17 +204,21 @@ function! coc#highlight#set(bufnr, key, highlights, priority) abort
if !bufloaded(a:bufnr)
return
endif
let ns = coc#highlight#create_namespace(a:key)
let ns = coc#highlight#create_namespace(a:key)
if has('nvim-0.5.0')
call v:lua.require('coc.highlight').set(a:bufnr, ns, a:highlights, a:priority)
else
if len(a:highlights) > g:coc_highlight_maximum_count
call s:add_highlights_timer(a:bufnr, ns, a:highlights, a:priority)
else
call s:add_highlights(a:bufnr, ns, a:highlights, a:priority)
endif
endif
endfunction
" Clear highlights by 0 based line numbers.
function! coc#highlight#clear(bufnr, key, lnums) abort
if !bufloaded(a:bufnr)
if !bufloaded(a:bufnr) || empty(a:lnums)
return
endif
let ns = coc#highlight#create_namespace(a:key)
@ -285,7 +226,7 @@ function! coc#highlight#clear(bufnr, key, lnums) abort @@ -285,7 +226,7 @@ function! coc#highlight#clear(bufnr, key, lnums) abort
if has('nvim')
call nvim_buf_clear_namespace(a:bufnr, ns, lnum, lnum + 1)
else
call coc#api#call('buf_clear_namespace', [a:bufnr, ns, lnum, lnum + 1])
call coc#api#exec('buf_clear_namespace', [a:bufnr, ns, lnum, lnum + 1])
endif
endfor
" clear highlights in invalid line.
@ -301,11 +242,14 @@ function! coc#highlight#del_markers(bufnr, key, ids) abort @@ -301,11 +242,14 @@ function! coc#highlight#del_markers(bufnr, key, ids) abort
endif
let ns = coc#highlight#create_namespace(a:key)
for id in a:ids
call nvim_buf_del_extmark(a:bufnr, ns, id)
if s:is_vim
call prop_remove({'bufnr': a:bufnr, 'id': id})
else
call nvim_buf_del_extmark(a:bufnr, ns, id)
endif
endfor
endfunction
" highlight LSP range,
function! coc#highlight#ranges(bufnr, key, hlGroup, ranges, ...) abort
let bufnr = a:bufnr == 0 ? bufnr('%') : a:bufnr
@ -345,7 +289,7 @@ endfunction @@ -345,7 +289,7 @@ endfunction
function! coc#highlight#add_highlight(bufnr, src_id, hl_group, line, col_start, col_end, ...) abort
let opts = get(a:, 1, {})
let priority = get(opts, 'priority', v:null)
if has('nvim')
if !s:is_vim
if s:set_extmark && a:src_id != -1
" get(opts, 'start_incl', 0) ? v:true : v:false,
try
@ -364,7 +308,7 @@ function! coc#highlight#add_highlight(bufnr, src_id, hl_group, line, col_start, @@ -364,7 +308,7 @@ function! coc#highlight#add_highlight(bufnr, src_id, hl_group, line, col_start,
call nvim_buf_add_highlight(a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end)
endif
else
call coc#api#call('buf_add_highlight', [a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end, opts])
call coc#api#exec('buf_add_highlight', [a:bufnr, a:src_id, a:hl_group, a:line, a:col_start, a:col_end, opts])
endif
endfunction
@ -377,7 +321,7 @@ function! coc#highlight#clear_highlight(bufnr, key, start_line, end_line) abort @@ -377,7 +321,7 @@ function! coc#highlight#clear_highlight(bufnr, key, start_line, end_line) abort
if has('nvim')
call nvim_buf_clear_namespace(a:bufnr, src_id, a:start_line, a:end_line)
else
call coc#api#call('buf_clear_namespace', [a:bufnr, src_id, a:start_line, a:end_line])
call coc#api#exec('buf_clear_namespace', [a:bufnr, src_id, a:start_line, a:end_line])
endif
endfunction
@ -395,6 +339,9 @@ endfunction @@ -395,6 +339,9 @@ endfunction
" endLine: number
" }
function! coc#highlight#add_highlights(winid, codes, highlights) abort
if get(g:, 'coc_node_env', '') ==# 'test'
call setwinvar(a:winid, 'highlights', a:highlights)
endif
" clear highlights
call coc#compat#execute(a:winid, 'syntax clear')
let bufnr = winbufnr(a:winid)
@ -404,7 +351,9 @@ function! coc#highlight#add_highlights(winid, codes, highlights) abort @@ -404,7 +351,9 @@ function! coc#highlight#add_highlights(winid, codes, highlights) abort
endif
if !empty(a:highlights)
for item in a:highlights
call coc#highlight#add_highlight(bufnr, -1, item['hlGroup'], item['lnum'], item['colStart'], item['colEnd'])
let hlGroup = item['hlGroup']
let opts = hlGroup =~# 'Search$' ? {'priority': 999, 'combine': 1} : {}
call coc#highlight#add_highlight(bufnr, -1, hlGroup, item['lnum'], item['colStart'], item['colEnd'])
endfor
endif
endfunction
@ -450,17 +399,9 @@ function! coc#highlight#highlight_lines(winid, blocks) abort @@ -450,17 +399,9 @@ function! coc#highlight#highlight_lines(winid, blocks) abort
endif
endfunction
" Compose hlGroups with foreground and background colors.
function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort
let hlGroup = 'Fg'.a:fgGroup.'Bg'.a:bgGroup
if a:fgGroup ==# a:bgGroup
return a:fgGroup
endif
if hlexists(hlGroup)
return hlGroup
endif
let fgId = synIDtrans(hlID(a:fgGroup))
let bgId = synIDtrans(hlID(a:bgGroup))
function! coc#highlight#compose(fg, bg) abort
let fgId = synIDtrans(hlID(a:fg))
let bgId = synIDtrans(hlID(a:bg))
let isGuiReversed = synIDattr(fgId, 'reverse', 'gui') !=# '1' || synIDattr(bgId, 'reverse', 'gui') !=# '1'
let guifg = isGuiReversed ? synIDattr(fgId, 'fg', 'gui') : synIDattr(fgId, 'bg', 'gui')
let guibg = isGuiReversed ? synIDattr(bgId, 'bg', 'gui') : synIDattr(bgId, 'fg', 'gui')
@ -470,7 +411,7 @@ function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort @@ -470,7 +411,7 @@ function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort
let bold = synIDattr(fgId, 'bold') ==# '1'
let italic = synIDattr(fgId, 'italic') ==# '1'
let underline = synIDattr(fgId, 'underline') ==# '1'
let cmd = 'silent hi ' . hlGroup
let cmd = ''
if !empty(guifg)
let cmd .= ' guifg=' . guifg
endif
@ -494,13 +435,105 @@ function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort @@ -494,13 +435,105 @@ function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort
elseif underline
let cmd .= ' cterm=underline gui=underline'
endif
if cmd ==# 'silent hi ' . hlGroup
return cmd
endfunction
" Compose hlGroups with foreground and background colors.
function! coc#highlight#compose_hlgroup(fgGroup, bgGroup) abort
let hlGroup = 'Fg'.a:fgGroup.'Bg'.a:bgGroup
if a:fgGroup ==# a:bgGroup
return a:fgGroup
endif
if hlexists(hlGroup) && match(execute('hi '.hlGroup, 'silent!'), 'cleared') == -1
return hlGroup
endif
let cmd = coc#highlight#compose(a:fgGroup, a:bgGroup)
if empty(cmd)
return 'Normal'
endif
execute cmd
execute 'silent hi ' . hlGroup . cmd
return hlGroup
endfunction
" hlGroup id, key => 'fg' | 'bg', kind => 'cterm' | 'gui'
function! coc#highlight#get_color(id, key, kind) abort
if synIDattr(a:id, 'reverse', a:kind) !=# '1'
return synIDattr(a:id, a:key, a:kind)
endif
return synIDattr(a:id, a:key ==# 'bg' ? 'fg' : 'bg', a:kind)
endfunction
function! coc#highlight#get_hl_command(id, key, cterm, gui) abort
let cterm = coc#highlight#get_color(a:id, a:key, 'cterm')
let gui = coc#highlight#get_color(a:id, a:key, 'gui')
let cmd = ' cterm'.a:key.'=' . (empty(cterm) ? a:cterm : cterm)
let cmd .= ' gui'.a:key.'=' . (empty(gui) ? a:gui : gui)
return cmd
endfunction
function! coc#highlight#reversed(id) abort
let gui = has('gui_running') || &termguicolors == 1
if synIDattr(synIDtrans(a:id), 'reverse', gui ? 'gui' : 'cterm') == '1'
return 1
endif
return 0
endfunction
function! coc#highlight#get_contrast(group1, group2) abort
let normal = coc#highlight#get_hex_color(synIDtrans(hlID('Normal')), 'bg', '#000000')
let bg1 = coc#highlight#get_hex_color(synIDtrans(hlID(a:group1)), 'bg', normal)
let bg2 = coc#highlight#get_hex_color(synIDtrans(hlID(a:group2)), 'bg', normal)
return coc#color#hex_contrast(bg1, bg2)
endfunction
" Darken or lighten background
function! coc#highlight#create_bg_command(group, amount) abort
let id = synIDtrans(hlID(a:group))
let normal = coc#highlight#get_hex_color(synIDtrans(hlID('Normal')), 'bg', &background ==# 'dark' ? '#282828' : '#fefefe')
let bg = coc#highlight#get_hex_color(id, 'bg', normal)
let hex = a:amount > 0 ? coc#color#darken(bg, a:amount) : coc#color#lighten(bg, -a:amount)
let ctermbg = coc#color#rgb2term(strpart(hex, 1))
if s:term && !s:check_ctermbg(id, ctermbg) && abs(a:amount) < 20.0
return coc#highlight#create_bg_command(a:group, a:amount * 2)
endif
return 'ctermbg=' . ctermbg.' guibg=' . hex
endfunction
function! coc#highlight#get_hex_color(id, kind, fallback) abort
let attr = coc#highlight#get_color(a:id, a:kind, s:term ? 'cterm' : 'gui')
let hex = s:to_hex_color(attr, s:term)
if empty(hex) && !s:term
let attr = coc#highlight#get_color(a:id, a:kind, 'cterm')
let hex = s:to_hex_color(attr, 1)
endif
return empty(hex) ? a:fallback : hex
endfunction
function! s:check_ctermbg(id, cterm) abort
let attr = coc#highlight#get_color(a:id, 'bg', 'cterm')
if empty(attr)
let attr = coc#highlight#get_color(synIDtrans(hlID('Normal')), 'bg', 'cterm')
endif
if attr ==# a:cterm
return 0
endif
return 1
endfunction
function! s:to_hex_color(color, term) abort
if empty(a:color)
return ''
endif
if a:color =~# '^#\x\+$'
return a:color
endif
if a:term && a:color =~# '^\d\+$'
return coc#color#term2rgb(a:color)
endif
let hex = coc#color#nameToHex(tolower(a:color), a:term)
return empty(hex) ? '' : hex
endfunction
" add matches for winid, use 0 for current window.
function! coc#highlight#match_ranges(winid, bufnr, ranges, hlGroup, priority) abort
let winid = a:winid == 0 ? win_getid() : a:winid
@ -630,20 +663,13 @@ function! coc#highlight#clear_matches(winid, ids) @@ -630,20 +663,13 @@ function! coc#highlight#clear_matches(winid, ids)
endif
endfunction
function! s:prop_type_hlgroup(type) abort
if a:type=~# '^CocHighlight'
return strpart(a:type, 12)
endif
return get(prop_type_get(a:type), 'highlight', '')
endfunction
function! coc#highlight#clear_all() abort
for src_id in values(s:namespace_map)
for bufnr in map(getbufinfo({'bufloaded': 1}), 'v:val["bufnr"]')
if has('nvim')
call nvim_buf_clear_namespace(bufnr, src_id, 0, -1)
else
call coc#api#call('buf_clear_namespace', [bufnr, src_id, 0, -1])
call coc#api#exec('buf_clear_namespace', [bufnr, src_id, 0, -1])
endif
endfor
endfor
@ -669,70 +695,26 @@ function! coc#highlight#get_syntax_name(lnum, col) @@ -669,70 +695,26 @@ function! coc#highlight#get_syntax_name(lnum, col)
return synIDattr(synIDtrans(synID(a:lnum,a:col,1)),"name")
endfunction
" TODO support check for virt_text
function! s:same_highlight(one, other) abort
if a:one['hlGroup'] !=# a:other['hlGroup']
return 0
endif
if a:one['lnum'] != a:other['lnum']
return 0
endif
if a:one['colStart'] !=# a:other['colStart']
return 0
endif
if a:one['colEnd'] !=# a:other['colEnd']
return 0
endif
return 1
function! s:prop_type_hlgroup(type) abort
return substitute(a:type, '_\d\+$', '', '')
endfunction
function! s:update_highlights_timer(bufnr, changedtick, key, priority, start, end, total, highlights, exclude) abort
function! s:update_highlights_timer(bufnr, changedtick, key, priority, groups, idx) abort
if getbufvar(a:bufnr, 'changedtick', 0) != a:changedtick
return
endif
let highlights = filter(copy(a:highlights), 'v:val["lnum"] >='.a:start.' && v:val["lnum"] <'.a:end)
let end = a:end
if empty(highlights) && end > 0
" find maximum lnum to clear
let till = type(a:exclude) == 3 && end < get(a:exclude, 0, 0) ? get(a:exclude, 0, 0) : a:total
if till > end
let minimal = till
for hl in filter(copy(a:highlights), 'v:val["lnum"] >='.end.' && v:val["lnum"] <'.till)
let minimal = min([minimal, hl['lnum']])
endfor
let end = minimal
endif
endif
"call coc#rpc#notify('log', ['update_timer', a:bufnr, a:changedtick, a:key, a:start, end, a:total, highlights, a:exclude])
call coc#highlight#update_highlights(a:bufnr, a:key, highlights, a:start, end, a:priority)
let re = s:get_highlight_region(end, a:total, a:exclude)
if !empty(re)
call timer_start(50, { -> s:update_highlights_timer(a:bufnr, a:changedtick, a:key, a:priority, re[0], re[1], a:total, a:highlights, a:exclude)})
endif
endfunction
" Get 0 based, end exclusive region to highlight.
function! s:get_highlight_region(start, total, exclude) abort
if a:start >= a:total
return v:null
endif
if empty(a:exclude)
let end = min([a:total, a:start + g:coc_highlight_batch_lines])
return [a:start, end]
endif
if a:start < a:exclude[0] - 1
let end = min([a:exclude[0] - 1, a:start + g:coc_highlight_batch_lines])
return [a:start, end]
let group = get(a:groups, a:idx, v:null)
if empty(group)
return
endif
let start = a:start
if a:start >= a:exclude[0] - 1 && a:start <= a:exclude[1] - 1
let start = a:exclude[1]
if empty(group['highlights'])
call coc#highlight#clear_highlight(a:bufnr, a:key, group['start'], group['end'])
else
call coc#highlight#update_highlights(a:bufnr, a:key, group['highlights'], group['start'], group['end'], a:priority)
endif
if start >= a:total
return v:null
if a:idx < len(a:groups) - 1
call timer_start(50, { -> s:update_highlights_timer(a:bufnr, a:changedtick, a:key, a:priority, a:groups, a:idx + 1)})
endif
let end = min([a:total, start + g:coc_highlight_batch_lines])
return [start, end]
endfunction
function! s:add_highlights_timer(bufnr, ns, highlights, priority) abort
@ -762,3 +744,60 @@ function! s:add_highlights(bufnr, ns, highlights, priority) abort @@ -762,3 +744,60 @@ function! s:add_highlights(bufnr, ns, highlights, priority) abort
call coc#highlight#add_highlight(a:bufnr, a:ns, item[0], item[1], item[2], item[3], opts)
endfor
endfunction
function! s:to_group(items) abort
let res = []
let before = v:null
for item in a:items
if empty(before) || before[0] != item[1]
let before = [item[1], [item]]
call add(res, before)
else
call add(before[1], item)
endif
endfor
return res
endfunction
function! s:get_priority(key, hlGroup, priority) abort
if a:hlGroup ==# 'CocListSearch'
return 2048
endif
if a:hlGroup ==# 'CocSearch'
return 999
endif
if strpart(a:key, 0, 10) !=# 'diagnostic'
return a:priority
endif
return a:priority - index(s:diagnostic_hlgroups, a:hlGroup)
endfunction
function! s:group_hls(hls, linecount) abort
" start, end, highlights
let groups = []
if empty(a:hls)
call add(groups, {'start': 0, 'end': a:linecount, 'highlights': []})
return groups
endif
let start = 0
let highlights = []
let lastLnum = -1
for item in a:hls
let lnum = item['lnum']
if lnum >= a:linecount
break
endif
if len(highlights) < g:coc_highlight_maximum_count || lnum == lastLnum
call add(highlights, item)
let lastLnum = lnum
else
call add(groups, {'start': start, 'end': lastLnum + 1, 'highlights': highlights})
let highlights = []
let start = lastLnum + 1
call add(highlights, item)
let lastLnum = lnum
endif
endfor
call add(groups, {'start': start, 'end': a:linecount, 'highlights': highlights})
return groups
endfunction

53
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/list.vim

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:prefix = '[List Preview]'
let s:sign_group = 'CocList'
" filetype detect could be slow.
let s:filetype_map = {
\ 'c': 'c',
@ -66,7 +67,6 @@ function! coc#list#create(position, height, name, numberSelect) @@ -66,7 +67,6 @@ function! coc#list#create(position, height, name, numberSelect)
else
setl nonumber
setl norelativenumber
setl signcolumn=yes
endif
return [bufnr('%'), win_getid(), tabpagenr()]
endfunction
@ -84,15 +84,12 @@ endfunction @@ -84,15 +84,12 @@ endfunction
function! coc#list#setup(source)
let b:list_status = {}
setl buftype=nofile nobuflisted nofen nowrap
setl norelativenumber bufhidden=wipe cursorline winfixheight
setl norelativenumber bufhidden=wipe nocursorline winfixheight
setl tabstop=1 nolist nocursorcolumn undolevels=-1
setl signcolumn=auto
if has('nvim-0.5.0') || has('patch-8.1.0864')
setl scrolloff=0
endif
if exists('&cursorlineopt')
setl cursorlineopt=both
endif
setl filetype=list
syntax case ignore
let source = a:source[8:]
@ -104,6 +101,15 @@ function! coc#list#setup(source) @@ -104,6 +101,15 @@ function! coc#list#setup(source)
endif
endfunction
function! coc#list#select(bufnr, line) abort
if !empty(a:bufnr) && bufloaded(a:bufnr)
call sign_unplace(s:sign_group, { 'buffer': a:bufnr })
if a:line > 0
call sign_place(6, s:sign_group, 'CocListCurrent', a:bufnr, {'lnum': a:line})
endif
endif
endfunction
" Check if previewwindow exists on current tab.
function! coc#list#has_preview()
for i in range(1, winnr('$'))
@ -163,20 +169,12 @@ endfunction @@ -163,20 +169,12 @@ endfunction
" config.hlGroup - (optional) highlight group.
" config.maxHeight - (optional) max height of window, valid for 'below' & 'top' position.
function! coc#list#preview(lines, config) abort
if s:is_vim && !exists('*win_execute')
throw 'win_execute function required for preview, please upgrade your vim.'
return
endif
let name = fnamemodify(get(a:config, 'name', ''), ':.')
let lines = a:lines
if empty(lines)
if get(a:config, 'scheme', 'file') != 'file'
let bufnr = s:load_buffer(name)
if bufnr != 0
let lines = getbufline(bufnr, 1, '$')
else
let lines = ['']
endif
let lines = bufnr == 0 ? [''] : getbufline(bufnr, 1, '$')
else
" Show empty lines so not close window.
let lines = ['']
@ -196,6 +194,7 @@ function! coc#list#preview(lines, config) abort @@ -196,6 +194,7 @@ function! coc#list#preview(lines, config) abort
let filetype = get(s:filetype_map, extname, '')
endif
let range = get(a:config, 'range', v:null)
let targetRange = get(a:config, 'targetRange', v:null)
let hlGroup = get(a:config, 'hlGroup', 'Search')
let lnum = get(a:config, 'lnum', 1)
let position = get(a:config, 'position', 'below')
@ -221,11 +220,7 @@ function! coc#list#preview(lines, config) abort @@ -221,11 +220,7 @@ function! coc#list#preview(lines, config) abort
let winid = win_getid()
endif
noa call winrestview({"lnum": lnum ,"topline":s:get_topline(a:config, lnum, winid)})
call setwinvar(winid, '&signcolumn', 'no')
call setwinvar(winid, '&number', 1)
call setwinvar(winid, '&cursorline', 0)
call setwinvar(winid, '&relativenumber', 0)
call setwinvar(winid, 'previewwindow', 1)
call s:set_preview_options(winid)
noa call win_gotoid(curr)
else
let height = s:get_height(lines, a:config)
@ -262,17 +257,22 @@ function! coc#list#preview(lines, config) abort @@ -262,17 +257,22 @@ function! coc#list#preview(lines, config) abort
let s:filetype_map[extname] = ft
endif
endif
call sign_unplace('coc', {'buffer': bufnr})
call sign_unplace('CocCursorLine', {'buffer': bufnr})
call coc#compat#execute(winid, 'call clearmatches()')
if !s:is_vim
" vim send <esc> to buffer on FocusLost, <C-w> and other cases
call coc#compat#execute(winid, 'nnoremap <silent><nowait><buffer> <esc> :call CocActionAsync("listCancel")<CR>')
endif
if !empty(targetRange)
for lnum in range(targetRange['start']['line'] + 1, targetRange['end']['line'] + 1)
call sign_place(0, 'CocCursorLine', 'CocListCurrent', bufnr, {'lnum': lnum})
endfor
else
call sign_unplace('CocCursorLine', { 'buffer': bufnr })
endif
if !empty(range)
call sign_place(1, 'coc', 'CocCurrentLine', bufnr, {'lnum': lnum})
call coc#highlight#match_ranges(winid, bufnr, [range], hlGroup, 10)
endif
redraw
endfunction
function! s:get_height(lines, config) abort
@ -301,3 +301,12 @@ function! s:get_topline(config, lnum, winid) abort @@ -301,3 +301,12 @@ function! s:get_topline(config, lnum, winid) abort
let toplineOffset = get(a:config, 'toplineOffset', 3)
return max([1, a:lnum - toplineOffset])
endfunction
function! s:set_preview_options(winid) abort
call setwinvar(a:winid, '&foldmethod', 'manual')
call setwinvar(a:winid, '&signcolumn', 'no')
call setwinvar(a:winid, '&number', 1)
call setwinvar(a:winid, '&cursorline', 0)
call setwinvar(a:winid, '&relativenumber', 0)
call setwinvar(a:winid, 'previewwindow', 1)
endfunction

11
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/math.vim

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
" support for float values
function! coc#math#min(first, ...) abort
let val = a:first
for i in range(0, len(a:000) - 1)
if a:000[i] < val
let val = a:000[i]
endif
endfor
return val
endfunction

532
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/notify.vim

@ -0,0 +1,532 @@ @@ -0,0 +1,532 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:utf = &encoding =~# '^utf'
let s:error_icon = get(g:, 'coc_notify_error_icon', s:utf ? '' : 'E')
let s:warning_icon = get(g:, 'coc_notify_warning_icon', s:utf ? '⚠' : 'W')
let s:info_icon = get(g:, 'coc_notify_info_icon', s:utf ? '' : 'I')
let s:interval = get(g:, 'coc_notify_interval', s:is_vim ? 50 : 20)
let s:phl = 'CocNotificationProgress'
let s:progress_char = '─'
let s:duration = 300.0
let s:winids = []
" Valid notify winids on current tab
function! coc#notify#win_list() abort
call filter(s:winids, 'coc#float#valid(v:val)')
return filter(copy(s:winids), '!empty(getwinvar(v:val,"float"))')
endfunction
function! coc#notify#close_all() abort
for winid in coc#notify#win_list()
call coc#notify#close(winid)
endfor
endfunction
" Do action for winid or first notify window with actions.
function! coc#notify#do_action(...) abort
let winids = a:0 > 0 ? a:000 : coc#notify#win_list()
for winid in winids
if coc#float#valid(winid) && getwinvar(winid, 'closing', 0) != 1
let actions = getwinvar(winid, 'actions', [])
if !empty(actions)
let items = map(copy(actions), '(v:key + 1).". ".v:val')
let msg = join(getbufline(winbufnr(winid), 1, '$'), ' ')
call coc#ui#quickpick(msg, items, {err, res -> s:on_action(err, res, winid) })
break
endif
endif
endfor
endfunction
" Copy notification contents
function! coc#notify#copy() abort
let lines = []
for winid in coc#notify#win_list()
let key = getwinvar(winid, 'key', v:null)
if type(key) == v:t_string
call extend(lines, json_decode(key)['lines'])
endif
endfor
if empty(lines)
echohl WarningMsg | echon 'No content to copy' | echohl None
return
endif
call setreg('*', join(lines, "\n"))
endfunction
" Show source name in window
function! coc#notify#show_sources() abort
if !exists('*getbufline') || !exists('*appendbufline')
throw "getbufline and appendbufline functions required, please upgrade your vim."
endif
let winids = filter(coc#notify#win_list(), 'coc#window#get_var(v:val,"closing") != 1')
for winid in winids
let key = getwinvar(winid, 'key', v:null)
if type(key) == v:t_string
let bufnr = winbufnr(winid)
let obj = json_decode(key)
let sourcename = get(obj, 'source', '')
let lnum = get(obj, 'kind', '') ==# 'progress' ? 1 : 0
let content = get(getbufline(bufnr, lnum + 1), 0, '')
if empty(sourcename) || content ==# sourcename
continue
endif
call appendbufline(bufnr, lnum, sourcename)
call coc#highlight#add_highlight(bufnr, -1, 'Title', lnum, 0, -1)
call coc#float#scroll_win(winid, 0, 1)
endif
endfor
redra
endfunction
function! coc#notify#close_by_source(source) abort
let winids = filter(coc#notify#win_list(), 'coc#window#get_var(v:val,"closing") != 1')
for winid in winids
let key = getwinvar(winid, 'key', v:null)
if type(key) == v:t_string
let obj = json_decode(key)
if get(obj, 'source', '') ==# a:source
call coc#notify#close(winid)
endif
endif
endfor
endfunction
" Cancel auto hide
function! coc#notify#keep() abort
for winid in coc#notify#win_list()
call s:cancel(winid, 'close_timer')
endfor
endfunction
" borderhighlight - border highlight [string]
" maxWidth - max content width, default 60 [number]
" minWidth - minimal width [number]
" maxHeight - max content height, default 10 [number]
" highlight - default highlight [string]
" winblend - winblend [number]
" timeout - auto close timeout, default 5000 [number]
" title - title text
" marginRight - margin right, default 10 [number]
" focusable - focusable [number]
" source - source name [string]
" kind - kind for create icon [string]
" actions - action names [string[]]
function! coc#notify#create(lines, config) abort
let actions = get(a:config, 'actions', [])
let key = json_encode(extend({'lines': a:lines}, a:config))
let winid = s:find_win(key)
let kind = get(a:config, 'kind', '')
let row = 0
if winid != -1
let row = getwinvar(winid, 'top', 0)
call filter(s:winids, 'v:val != '.winid)
call coc#float#close(winid, 1)
let winid = v:null
endif
let opts = coc#dict#pick(a:config, ['highlight', 'borderhighlight', 'focusable', 'shadow'])
let border = has_key(opts, 'borderhighlight') ? [1, 1, 1, 1] : []
let icon = s:get_icon(kind, get(a:config, 'highlight', 'CocFloating'))
let margin = get(a:config, 'marginRight', 10)
let maxWidth = min([&columns - margin - 2, get(a:config, 'maxWidth', 80)])
if maxWidth <= 0
throw 'No enough spaces for notification'
endif
let lines = map(copy(a:lines), 'tr(v:val, "\t", " ")')
if has_key(a:config, 'title')
if !empty(border)
let opts['title'] = a:config['title']
else
let lines = [a:config['title']] + lines
endif
endif
let width = max(map(copy(lines), 'strwidth(v:val)')) + (empty(icon) ? 1 : 3)
if width > maxWidth
let lines = coc#string#reflow(lines, maxWidth)
let width = max(map(copy(lines), 'strwidth(v:val)')) + (empty(icon) ? 1 : 3)
endif
let highlights = []
if !empty(icon)
let ic = icon['text']
if empty(lines)
call add(lines, ic)
else
let lines[0] = ic.' '.lines[0]
endif
call add(highlights, {'lnum': 0, 'hlGroup': icon['hl'], 'colStart': 0, 'colEnd': strlen(ic)})
endif
let actionText = join(actions, ' ')
call map(lines, 'v:key == 0 ? v:val : repeat(" ", '.(empty(icon) ? 0 : 2).').v:val')
let minWidth = get(a:config, 'minWidth', kind ==# 'progress' ? 30 : 10)
let width = max(extend(map(lines + [get(opts, 'title', '').' '], 'strwidth(v:val)'), [minWidth, strwidth(actionText) + 1]))
let width = min([maxWidth, width])
let height = min([get(a:config, 'maxHeight', 3), len(lines)])
if kind ==# 'progress'
let lines = [repeat(s:progress_char, width)] + lines
let height = height + 1
endif
if !empty(actions)
let before = max([width - strwidth(actionText), 0])
let lines = lines + [repeat(' ', before).actionText]
let height = height + 1
call s:add_action_highlights(before, height - 1, highlights, actions)
endif
if row == 0
let wintop = coc#notify#get_top()
let row = wintop - height - (empty(border) ? 0 : 2) - 1
if !s:is_vim && !empty(border)
let row = row + 1
endif
endif
let col = &columns - margin - width
if s:is_vim && !empty(border)
let col = col - 2
endif
let winblend = 60
" Avoid animate for transparent background.
if get(a:config, 'winblend', 30) == 0 && empty(synIDattr(synIDtrans(hlID(get(opts, 'highlight', 'CocFloating'))), 'bg', 'gui'))
let winblend = 0
endif
call extend(opts, {
\ 'relative': 'editor',
\ 'width': width,
\ 'height': height,
\ 'col': col,
\ 'row': row + 1,
\ 'lines': lines,
\ 'rounded': 1,
\ 'highlights': highlights,
\ 'winblend': winblend,
\ 'border': border,
\ })
let result = coc#float#create_float_win(0, 0, opts)
if empty(result)
throw 'Unable to create notify window'
endif
let winid = result[0]
let bufnr = result[1]
call setwinvar(winid, 'right', 1)
call setwinvar(winid, 'kind', 'notification')
call setwinvar(winid, 'top', row)
call setwinvar(winid, 'key', key)
call setwinvar(winid, 'actions', actions)
call setwinvar(winid, 'source', get(a:config, 'source', ''))
call setwinvar(winid, 'border', !empty(border))
call coc#float#nvim_scrollbar(winid)
call add(s:winids, winid)
let from = {'row': opts['row'], 'winblend': opts['winblend']}
let to = {'row': row, 'winblend': get(a:config, 'winblend', 30)}
call timer_start(s:interval, { -> s:animate(winid, from, to, 0)})
if kind ==# 'progress'
call timer_start(s:interval, { -> s:progress(winid, width, 0, -1)})
endif
if !s:is_vim
call coc#compat#buf_add_keymap(bufnr, 'n', '<LeftRelease>', ':call coc#notify#nvim_click('.winid.')<CR>', {
\ 'silent': v:true,
\ 'nowait': v:true
\ })
endif
" Enable auto close
if empty(actions) && kind !=# 'progress'
let timer = timer_start(get(a:config, 'timeout', 10000), { -> coc#notify#close(winid)})
call setwinvar(winid, 'close_timer', timer)
endif
return [winid, bufnr]
endfunction
function! coc#notify#nvim_click(winid) abort
if getwinvar(a:winid, 'closing', 0)
return
endif
call s:cancel(a:winid, 'close_timer')
let actions = getwinvar(a:winid, 'actions', [])
if !empty(actions)
let character = strpart(getline('.'), col('.') - 1, 1)
if character =~# '^\k'
let word = expand('<cword>')
let idx = index(actions, word)
if idx != -1
call coc#rpc#notify('FloatBtnClick', [winbufnr(a:winid), idx])
call coc#notify#close(a:winid)
endif
endif
endif
endfunction
function! coc#notify#on_close(winid) abort
if index(s:winids, a:winid) >= 0
call filter(s:winids, 'v:val != '.a:winid)
call coc#notify#reflow()
endif
endfunction
function! coc#notify#get_top() abort
let mintop = min(map(coc#notify#win_list(), 'coc#notify#get_win_top(v:val)'))
if mintop != 0
return mintop
endif
return &lines - &cmdheight - (&laststatus == 0 ? 0 : 1 )
endfunction
function! coc#notify#get_win_top(winid) abort
let row = getwinvar(a:winid, 'top', 0)
if row == 0
return row
endif
return row - (s:is_vim ? 0 : getwinvar(a:winid, 'border', 0))
endfunction
" Close with timer
function! coc#notify#close(winid) abort
if !coc#float#valid(a:winid) || coc#window#get_var(a:winid, 'closing', 0) == 1
return
endif
if !coc#window#visible(a:winid)
call coc#float#close(a:winid, 1)
return
endif
let row = coc#window#get_var(a:winid, 'top')
if type(row) != v:t_number
call coc#float#close(a:winid)
return
endif
call coc#window#set_var(a:winid, 'closing', 1)
call s:cancel(a:winid)
let winblend = coc#window#get_var(a:winid, 'winblend', 0)
let curr = s:is_vim ? {'row': row} : {'winblend': winblend}
let dest = s:is_vim ? {'row': row + 1} : {'winblend': winblend == 0 ? 0 : 60}
call s:animate(a:winid, curr, dest, 0, 1)
endfunction
function! s:add_action_highlights(before, lnum, highlights, actions) abort
let colStart = a:before
for text in a:actions
let w = strwidth(text)
call add(a:highlights, {
\ 'lnum': a:lnum,
\ 'hlGroup': 'CocNotificationButton',
\ 'colStart': colStart,
\ 'colEnd': colStart + w
\ })
let colStart = colStart + w + 1
endfor
endfunction
function! s:on_action(err, idx, winid) abort
if !empty(a:err)
throw a:err
endif
if a:idx > 0
call coc#rpc#notify('FloatBtnClick', [winbufnr(a:winid), a:idx - 1])
call coc#notify#close(a:winid)
endif
endfunction
function! s:cancel(winid, ...) abort
let name = get(a:, 1, 'timer')
let timer = coc#window#get_var(a:winid, name)
if !empty(timer)
call timer_stop(timer)
call coc#window#set_var(a:winid, name, v:null)
endif
endfunction
function! s:progress(winid, total, curr, index) abort
if !coc#float#valid(a:winid)
return
endif
if coc#window#visible(a:winid)
let total = a:total
let idx = float2nr(a:curr/5.0)%total
if idx != a:index
" update percent
let bufnr = winbufnr(a:winid)
let percent = coc#window#get_var(a:winid, 'percent')
if !empty(percent)
let width = strchars(get(getbufline(bufnr, 1), 0, ''))
let line = repeat(s:progress_char, width - 4).printf('%4s', percent)
let total = width - 4
call setbufline(bufnr, 1, line)
endif
let message = coc#window#get_var(a:winid, 'message')
if !empty(message)
let linecount = coc#compat#buf_line_count(bufnr)
let hasAction = !empty(coc#window#get_var(a:winid, 'actions', []))
if getbufvar(bufnr, 'message', 0) == 0
call appendbufline(bufnr, linecount - hasAction, message)
call setbufvar(bufnr, 'message', 1)
call coc#float#change_height(a:winid, 1)
let tabnr = coc#window#tabnr(a:winid)
call coc#notify#reflow(tabnr)
else
call setbufline(bufnr, linecount - hasAction, message)
endif
endif
let bytes = strlen(s:progress_char)
call coc#highlight#clear_highlight(bufnr, -1, 0, 1)
let colStart = bytes * idx
if idx + 4 <= total
let colEnd = bytes * (idx + 4)
call coc#highlight#add_highlight(bufnr, -1, s:phl, 0, colStart, colEnd)
else
let colEnd = bytes * total
call coc#highlight#add_highlight(bufnr, -1, s:phl, 0, colStart, colEnd)
call coc#highlight#add_highlight(bufnr, -1, s:phl, 0, 0, bytes * (idx + 4 - total))
endif
endif
call timer_start(s:interval, { -> s:progress(a:winid, total, a:curr + 1, idx)})
else
" Not block CursorHold event
call timer_start(&updatetime + 100, { -> s:progress(a:winid, a:total, a:curr, a:index)})
endif
endfunction
" Optional row & winblend
function! s:config_win(winid, props) abort
let change_row = has_key(a:props, 'row')
if s:is_vim
if change_row
call popup_move(a:winid, {'line': a:props['row'] + 1})
endif
else
if change_row
let [row, column] = nvim_win_get_position(a:winid)
call nvim_win_set_config(a:winid, {
\ 'row': a:props['row'],
\ 'col': column,
\ 'relative': 'editor',
\ })
call s:nvim_move_related(a:winid, a:props['row'])
endif
call coc#float#nvim_set_winblend(a:winid, get(a:props, 'winblend', v:null))
call coc#float#nvim_refresh_scrollbar(a:winid)
endif
endfunction
function! s:nvim_move_related(winid, row) abort
let winids = coc#window#get_var(a:winid, 'related')
if empty(winids)
return
endif
for winid in winids
if nvim_win_is_valid(winid)
let [row, column] = nvim_win_get_position(winid)
let delta = coc#window#get_var(winid, 'delta', 0)
call nvim_win_set_config(winid, {
\ 'row': a:row + delta,
\ 'col': column,
\ 'relative': 'editor',
\ })
endif
endfor
endfunction
function! s:animate(winid, from, to, prev, ...) abort
if !coc#float#valid(a:winid)
return
endif
let curr = a:prev + s:interval
let percent = coc#math#min(curr / s:duration, 1)
let props = s:get_props(a:from, a:to, percent)
call s:config_win(a:winid, props)
let close = get(a:, 1, 0)
if percent < 1
call timer_start(s:interval, { -> s:animate(a:winid, a:from, a:to, curr, close)})
elseif close
call filter(s:winids, 'v:val != '.a:winid)
let tabnr = coc#window#tabnr(a:winid)
if tabnr != -1
call coc#float#close(a:winid, 1)
call coc#notify#reflow(tabnr)
endif
endif
endfunction
function! coc#notify#reflow(...) abort
let tabnr = get(a:, 1, tabpagenr())
let winids = filter(copy(s:winids), 'coc#window#tabnr(v:val) == '.tabnr.' && coc#window#get_var(v:val,"closing") != 1')
if empty(winids)
return
endif
let animate = tabnr == tabpagenr()
let wins = map(copy(winids), {_, val -> {
\ 'winid': val,
\ 'row': coc#window#get_var(val,'top',0),
\ 'top': coc#window#get_var(val,'top',0) - (s:is_vim ? 0 : coc#window#get_var(val, 'border', 0)),
\ 'height': coc#float#get_height(val),
\ }})
call sort(wins, {a, b -> b['top'] - a['top']})
let bottom = &lines - &cmdheight - (&laststatus == 0 ? 0 : 1 )
let moved = 0
for item in wins
let winid = item['winid']
let delta = bottom - (item['top'] + item['height'] + 1)
if delta != 0
call s:cancel(winid)
let dest = item['row'] + delta
call coc#window#set_var(winid, 'top', dest)
if animate
call s:move_win_timer(winid, {'row': item['row']}, {'row': dest}, 0)
else
call s:config_win(winid, {'row': dest})
endif
let moved = moved + delta
endif
let bottom = item['top'] + delta
endfor
endfunction
function! s:move_win_timer(winid, from, to, curr) abort
if !coc#float#valid(a:winid)
return
endif
if coc#window#get_var(a:winid, 'closing', 0) == 1
return
endif
let percent = coc#math#min(a:curr / s:duration, 1)
let next = a:curr + s:interval
if a:curr > 0
call s:config_win(a:winid, s:get_props(a:from, a:to, percent))
endif
if percent < 1
let timer = timer_start(s:interval, { -> s:move_win_timer(a:winid, a:from, a:to, next)})
call coc#window#set_var(a:winid, 'timer', timer)
endif
endfunction
function! s:find_win(key) abort
for winid in coc#notify#win_list()
if getwinvar(winid, 'key', '') ==# a:key
return winid
endif
endfor
return -1
endfunction
function! s:get_icon(kind, bg) abort
if a:kind ==# 'info'
return {'text': s:info_icon, 'hl': coc#highlight#compose_hlgroup('CocInfoSign', a:bg)}
endif
if a:kind ==# 'warning'
return {'text': s:warning_icon, 'hl': coc#highlight#compose_hlgroup('CocWarningSign', a:bg)}
endif
if a:kind ==# 'error'
return {'text': s:error_icon, 'hl': coc#highlight#compose_hlgroup('CocErrorSign', a:bg)}
endif
return v:null
endfunction
" percent should be float
function! s:get_props(from, to, percent) abort
let obj = {}
for key in keys(a:from)
let changed = a:to[key] - a:from[key]
if !s:is_vim && key ==# 'row'
" Could be float
let obj[key] = a:from[key] + changed * a:percent
else
let obj[key] = a:from[key] + float2nr(round(changed * a:percent))
endif
endfor
return obj
endfunction

18
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/prompt.vim

@ -83,7 +83,7 @@ let s:char_map = { @@ -83,7 +83,7 @@ let s:char_map = {
function! coc#prompt#getc() abort
let c = getchar()
return type(c) == type(0) ? nr2char(c) : c
return type(c) is 0 ? nr2char(c) : c
endfunction
function! coc#prompt#getchar() abort
@ -92,9 +92,13 @@ function! coc#prompt#getchar() abort @@ -92,9 +92,13 @@ function! coc#prompt#getchar() abort
return input
endif
"a language keymap is activated, so input must be resolved to the mapped values.
let partial_keymap = mapcheck(input, "l")
while partial_keymap !=# ""
let full_keymap = maparg(input, "l")
let partial_keymap = mapcheck(input, 'l')
while partial_keymap !=# ''
let dict = maparg(input, 'l', 0, 1)
if empty(dict) || get(dict, 'expr', 0)
return input
endif
let full_keymap = get(dict, 'rhs', '')
if full_keymap ==# "" && len(input) >= 3 "HACK: assume there are no keymaps longer than 3.
return input
elseif full_keymap ==# partial_keymap
@ -109,7 +113,7 @@ function! coc#prompt#getchar() abort @@ -109,7 +113,7 @@ function! coc#prompt#getchar() abort
return input
endif
let input .= c
let partial_keymap = mapcheck(input, "l")
let partial_keymap = mapcheck(input, 'l')
endwhile
return input
endfunction
@ -161,7 +165,9 @@ function! s:start_prompt() @@ -161,7 +165,9 @@ function! s:start_prompt()
endwhile
catch /^Vim:Interrupt$/
let s:activated = 0
call coc#rpc#notify('InputChar', [s:current_session(), '<esc>'])
call coc#rpc#notify('InputChar', [s:current_session(), '<esc>', 0])
let s:session_names = []
call s:reset()
return
endtry
let s:activated = 0

583
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/pum.vim

@ -0,0 +1,583 @@ @@ -0,0 +1,583 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:pum_bufnr = 0
let s:pum_winid = 0
let s:pum_index = -1
let s:pum_size = 0
let s:inserted = 0
let s:virtual_text = 0
let s:virtual_text_ns = coc#highlight#create_namespace('pum-virtual')
let s:ignore = s:is_vim || has('nvim-0.5.0') ? "\<Ignore>" : "\<space>\<bs>"
let s:hide_pum = has('nvim-0.6.1') || has('patch-8.2.3389')
let s:virtual_text_support = has('nvim-0.5.0') || has('patch-9.0.0067')
" bufnr, &indentkeys
let s:saved_indenetkeys = []
let s:prop_id = 0
let s:reversed = 0
let s:check_hl_group = 0
if s:is_vim && s:virtual_text_support
if empty(prop_type_get('CocPumVirtualText'))
call prop_type_add('CocPumVirtualText', {'highlight': 'CocPumVirtualText'})
endif
endif
function! coc#pum#visible() abort
if !s:pum_winid
return 0
endif
return getwinvar(s:pum_winid, 'float', 0) == 1
endfunction
function! coc#pum#winid() abort
return s:pum_winid
endfunction
function! coc#pum#close_detail() abort
let winid = coc#float#get_float_by_kind('pumdetail')
if winid
call coc#float#close(winid, 1)
if s:is_vim
call timer_start(0, { -> execute('redraw')})
endif
endif
endfunction
function! coc#pum#close(...) abort
if coc#float#valid(s:pum_winid)
let kind = get(a:, 1, '')
if kind ==# 'cancel'
let input = getwinvar(s:pum_winid, 'input', '')
let s:pum_index = -1
call s:insert_word(input)
call s:on_pum_change(0)
doautocmd <nomodeline> TextChangedI
elseif kind ==# 'confirm'
let words = getwinvar(s:pum_winid, 'words', [])
if s:pum_index >= 0
let word = get(words, s:pum_index, '')
call s:insert_word(word)
call s:restore_indentkeys()
endif
doautocmd <nomodeline> TextChangedI
endif
call s:close_pum()
if !get(a:, 2, 0)
" vim possible have unexpected text inserted without timer.
call timer_start(1, { -> coc#rpc#notify('CompleteStop', [kind])})
endif
endif
endfunction
function! coc#pum#select_confirm() abort
if s:pum_index < 0
let s:pum_index = 0
call s:on_pum_change(0)
endif
call coc#pum#close('confirm')
endfunction
function! coc#pum#insert() abort
call timer_start(1, { -> s:insert_current()})
return s:ignore
endfunction
" Add one more character from the matched complete item(or first one),
" the word should starts with input, the same as vim's CTRL-L behavior.
function! coc#pum#one_more() abort
call timer_start(1, { -> s:insert_one_more()})
return ''
endfunction
function! coc#pum#_close() abort
if coc#float#valid(s:pum_winid)
call s:close_pum()
if s:is_vim
call timer_start(0, { -> execute('redraw')})
endif
endif
endfunction
function! s:insert_one_more() abort
if coc#float#valid(s:pum_winid)
let parts = getwinvar(s:pum_winid, 'parts', [])
let input = strpart(getline('.'), strchars(parts[0]), col('.') - 1)
let words = getwinvar(s:pum_winid, 'words', [])
let word = get(words, s:pum_index == -1 ? 0 : s:pum_index, '')
if !empty(word) && strcharpart(word, 0, strchars(input)) ==# input
let ch = strcharpart(word, strchars(input), 1)
if !empty(ch)
call feedkeys(ch, "int")
endif
endif
endif
endfunction
function! s:insert_current() abort
if coc#float#valid(s:pum_winid)
if s:pum_index >= 0
let words = getwinvar(s:pum_winid, 'words', [])
let word = get(words, s:pum_index, '')
call s:insert_word(word)
call s:restore_indentkeys()
endif
doautocmd <nomodeline> TextChangedI
call s:close_pum()
call coc#rpc#notify('CompleteStop', [''])
endif
endfunction
function! s:close_pum() abort
call s:clear_virtual_text()
call coc#float#close(s:pum_winid, 1)
let s:pum_winid = 0
let s:pum_size = 0
let winid = coc#float#get_float_by_kind('pumdetail')
if winid
call coc#float#close(winid, 1)
endif
call s:restore_indentkeys()
endfunction
function! s:restore_indentkeys() abort
if get(s:saved_indenetkeys, 0, 0) == bufnr('%')
call setbufvar(s:saved_indenetkeys[0], '&indentkeys', get(s:saved_indenetkeys, 1, ''))
let s:saved_indenetkeys = []
endif
endfunction
function! coc#pum#next(insert) abort
call timer_start(1, { -> s:navigate(1, a:insert)})
return s:ignore
endfunction
function! coc#pum#prev(insert) abort
call timer_start(1, { -> s:navigate(0, a:insert)})
return s:ignore
endfunction
function! coc#pum#stop() abort
call timer_start(1, { -> coc#pum#close()})
return s:ignore
endfunction
function! coc#pum#cancel() abort
call timer_start(1, { -> coc#pum#close('cancel')})
return s:ignore
endfunction
function! coc#pum#confirm() abort
call timer_start(1, { -> coc#pum#close('confirm')})
return s:ignore
endfunction
function! coc#pum#select(index, insert, confirm) abort
if !coc#float#valid(s:pum_winid)
return ''
endif
if a:index == -1
call coc#pum#close('cancel')
return ''
endif
if a:index < 0 || a:index >= s:pum_size
throw 'index out of range ' . a:index
endif
call s:select_by_index(a:index, a:insert)
if a:confirm
call coc#pum#close('confirm')
endif
return ''
endfunction
function! coc#pum#info() abort
let bufnr = winbufnr(s:pum_winid)
let words = getwinvar(s:pum_winid, 'words', [])
let word = s:pum_index < 0 ? '' : get(words, s:pum_index, '')
let pretext = strpart(getline('.'), 0, col('.') - 1)
if s:is_vim
let pos = popup_getpos(s:pum_winid)
let border = has_key(popup_getoptions(s:pum_winid), 'border')
let add = pos['scrollbar'] && border ? 1 : 0
return {
\ 'word': word,
\ 'index': s:pum_index,
\ 'scrollbar': pos['scrollbar'],
\ 'row': pos['line'] - 1,
\ 'col': pos['col'] - 1,
\ 'width': pos['width'] + add,
\ 'height': pos['height'],
\ 'size': s:pum_size,
\ 'border': border,
\ 'inserted': s:inserted ? v:true : v:false,
\ 'reversed': s:reversed ? v:true : v:false,
\ }
else
let scrollbar = coc#float#get_related(s:pum_winid, 'scrollbar')
let winid = coc#float#get_related(s:pum_winid, 'border', s:pum_winid)
let pos = nvim_win_get_position(winid)
return {
\ 'word': word,
\ 'index': s:pum_index,
\ 'scrollbar': scrollbar && nvim_win_is_valid(scrollbar) ? 1 : 0,
\ 'row': pos[0],
\ 'col': pos[1],
\ 'width': nvim_win_get_width(winid),
\ 'height': nvim_win_get_height(winid),
\ 'size': s:pum_size,
\ 'border': winid != s:pum_winid,
\ 'inserted': s:inserted ? v:true : v:false,
\ 'reversed': s:reversed ? v:true : v:false,
\ }
endif
endfunction
function! coc#pum#scroll(forward) abort
if coc#pum#visible()
let height = s:get_height(s:pum_winid)
if s:pum_size > height
call timer_start(10, { -> s:scroll_pum(a:forward, height, s:pum_size)})
endif
endif
return s:ignore
endfunction
function! s:get_height(winid) abort
if has('nvim')
return nvim_win_get_height(a:winid)
endif
return get(popup_getpos(a:winid), 'core_height', 0)
endfunction
function! s:scroll_pum(forward, height, size) abort
let topline = s:get_topline(s:pum_winid)
if !a:forward && topline == 1
if s:pum_index >= 0
call s:select_line(s:pum_winid, 1)
call s:on_pum_change(1)
endif
return
endif
if a:forward && topline + a:height - 1 >= a:size
if s:pum_index >= 0
call s:select_line(s:pum_winid, a:size)
call s:on_pum_change(1)
endif
return
endif
call coc#float#scroll_win(s:pum_winid, a:forward, a:height)
if s:pum_index >= 0
let lnum = s:pum_index + 1
let topline = s:get_topline(s:pum_winid)
if lnum >= topline && lnum <= topline + a:height - 1
return
endif
call s:select_line(s:pum_winid, topline)
call s:on_pum_change(1)
endif
endfunction
function! s:get_topline(winid) abort
if has('nvim')
let info = getwininfo(a:winid)[0]
return info['topline']
else
let pos = popup_getpos(a:winid)
return pos['firstline']
endif
endfunction
function! s:navigate(next, insert) abort
if !coc#float#valid(s:pum_winid)
return
endif
let index = s:get_index(a:next)
call s:select_by_index(index, a:insert)
endfunction
function! s:select_by_index(index, insert) abort
let lnum = a:index == -1 ? 0 : s:index_to_lnum(a:index)
call s:set_cursor(s:pum_winid, lnum)
if !s:is_vim
call coc#float#nvim_scrollbar(s:pum_winid)
endif
if a:insert
let s:inserted = 1
if a:index < 0
let input = getwinvar(s:pum_winid, 'input', '')
call s:insert_word(input)
call coc#pum#close_detail()
else
let words = getwinvar(s:pum_winid, 'words', [])
let word = get(words, a:index, '')
call s:insert_word(word)
endif
doautocmd <nomodeline> TextChangedP
endif
call s:on_pum_change(1)
endfunction
function! s:get_index(next) abort
if a:next
let index = s:pum_index + 1 == s:pum_size ? -1 : s:pum_index + 1
else
let index = s:pum_index == -1 ? s:pum_size - 1 : s:pum_index - 1
endif
return index
endfunction
function! s:insert_word(word) abort
let parts = getwinvar(s:pum_winid, 'parts', [])
if !empty(parts) && mode() ==# 'i'
let curr = getline('.')
let saved_completeopt = &completeopt
if saved_completeopt =~ 'menuone'
noa set completeopt=menu
endif
noa call complete(strlen(parts[0]) + 1, [{ 'empty': v:true, 'word': a:word }])
" exit complete state
if s:hide_pum
call feedkeys("\<C-x>\<C-z>", 'in')
else
let g:coc_disable_space_report = 1
call feedkeys("\<space>\<bs>", 'in')
endif
execute 'noa set completeopt='.saved_completeopt
endif
endfunction
" create or update pum with lines, CompleteOption and config.
" return winid & dimension
function! coc#pum#create(lines, opt, config) abort
if mode() !=# 'i' || a:opt['line'] != line('.')
return
endif
let len = col('.') - a:opt['col'] - 1
if len < 0
return
endif
let input = len == 0 ? '' : strpart(getline('.'), a:opt['col'], len)
if input !=# a:opt['input']
return
endif
let config = s:get_pum_dimension(a:lines, a:opt['col'], a:config)
if empty(config)
return
endif
let s:reversed = get(a:config, 'reverse', 0) && config['row'] < 0
let s:virtual_text = s:virtual_text_support && a:opt['virtualText']
let s:pum_size = len(a:lines)
let s:pum_index = a:opt['index']
let lnum = s:index_to_lnum(s:pum_index)
call extend(config, {
\ 'lines': s:reversed ? reverse(copy(a:lines)) : a:lines,
\ 'relative': 'cursor',
\ 'nopad': 1,
\ 'cursorline': 1,
\ 'index': lnum - 1,
\ 'focusable': v:false
\ })
call extend(config, coc#dict#pick(a:config, ['highlight', 'rounded', 'highlights', 'winblend', 'shadow', 'border', 'borderhighlight']))
if s:reversed
for item in config['highlights']
let item['lnum'] = s:pum_size - item['lnum'] - 1
endfor
endif
if empty(get(config, 'winblend', 0)) && exists('&pumblend')
let config['winblend'] = &pumblend
endif
let result = coc#float#create_float_win(s:pum_winid, s:pum_bufnr, config)
if empty(result)
return
endif
let s:inserted = 0
let s:pum_winid = result[0]
let s:pum_bufnr = result[1]
call setwinvar(s:pum_winid, 'above', config['row'] < 0)
let firstline = s:get_firstline(lnum, s:pum_size, config['height'])
if s:is_vim
call popup_setoptions(s:pum_winid, { 'firstline': firstline })
else
call coc#compat#execute(s:pum_winid, 'call winrestview({"lnum":'.lnum.',"topline":'.firstline.'})')
endif
call coc#dialog#place_sign(s:pum_bufnr, s:pum_index == -1 ? 0 : lnum)
" content before col and content after cursor
let linetext = getline('.')
let parts = [strpart(linetext, 0, a:opt['col']), strpart(linetext, col('.') - 1)]
call setwinvar(s:pum_winid, 'input', input)
call setwinvar(s:pum_winid, 'parts', parts)
call setwinvar(s:pum_winid, 'words', a:opt['words'])
call setwinvar(s:pum_winid, 'kind', 'pum')
if !s:is_vim
if s:pum_size > config['height']
redraw
call coc#float#nvim_scrollbar(s:pum_winid)
else
call coc#float#close_related(s:pum_winid, 'scrollbar')
endif
endif
call s:on_pum_change(0)
let bufnr = bufnr('%')
if !empty(&indentexpr) && get(s:saved_indenetkeys, 0, 0) != bufnr
let s:saved_indenetkeys = [bufnr, &indentkeys]
execute 'setl indentkeys='
endif
endfunction
function! s:get_firstline(lnum, total, height) abort
if a:lnum <= a:height
return 1
endif
return min([a:total - a:height + 1, a:lnum - (a:height*2/3)])
endfunction
function! s:on_pum_change(move) abort
if coc#float#valid(s:pum_winid)
if s:virtual_text
call s:insert_virtual_text()
endif
let ev = extend(coc#pum#info(), {'move': a:move ? v:true : v:false})
call coc#rpc#notify('CocAutocmd', ['MenuPopupChanged', ev, win_screenpos(winnr())[0] + winline() - 2])
endif
endfunction
function! s:index_to_lnum(index) abort
if s:reversed
if a:index <= 0
return s:pum_size
endif
return s:pum_size - a:index
endif
return max([1, a:index + 1])
endfunction
function! s:get_pum_dimension(lines, col, config) abort
let linecount = len(a:lines)
let [lineIdx, colIdx] = coc#cursor#screen_pos()
let bh = empty(get(a:config, 'border', [])) ? 0 : 2
let columns = &columns
let pumwidth = max([15, exists('&pumwidth') ? &pumwidth : 0])
let width = min([columns, max([pumwidth, a:config['width']])])
let vh = &lines - &cmdheight - 1 - !empty(&tabline)
if vh <= 0
return v:null
endif
let pumheight = empty(&pumheight) ? vh : &pumheight
let showTop = getwinvar(s:pum_winid, 'above', v:null)
if type(showTop) != v:t_number
if vh - lineIdx - bh - 1 < min([pumheight, linecount]) && vh - lineIdx < min([10, vh/2])
let showTop = 1
else
let showTop = 0
endif
endif
let height = showTop ? min([lineIdx - bh - !empty(&tabline), linecount, pumheight]) : min([vh - lineIdx - bh - 1, linecount, pumheight])
if height <= 0
return v:null
endif
let col = - (col('.') - a:col - 1) - 1
let row = showTop ? - height : 1
let delta = colIdx + col
if width > pumwidth && delta + width > columns
let width = max([columns - delta, pumwidth])
endif
if delta < 0
let col = col - delta
elseif delta + width > columns
let col = max([-colIdx, col - (delta + width - columns)])
endif
return {
\ 'row': row,
\ 'col': col,
\ 'width': width,
\ 'height': height
\ }
endfunction
" can't use coc#dialog#set_cursor on vim8, don't know why
function! s:set_cursor(winid, line) abort
if s:is_vim
let pos = popup_getpos(a:winid)
let core_height = pos['core_height']
let lastline = pos['firstline'] + core_height - 1
if a:line > lastline
call popup_setoptions(a:winid, {
\ 'firstline': pos['firstline'] + a:line - lastline,
\ })
elseif a:line < pos['firstline']
if s:reversed
call popup_setoptions(a:winid, {
\ 'firstline': a:line == 0 ? s:pum_size - core_height + 1 : a:line - core_height + 1,
\ })
else
call popup_setoptions(a:winid, {
\ 'firstline': max([1, a:line]),
\ })
endif
endif
endif
call s:select_line(a:winid, a:line)
endfunction
function! s:select_line(winid, line) abort
let s:pum_index = s:reversed ? (a:line == 0 ? -1 : s:pum_size - a:line) : a:line - 1
let lnum = s:reversed ? (a:line == 0 ? s:pum_size : a:line) : max([1, a:line])
if s:is_vim
call coc#compat#execute(a:winid, 'exe '.lnum)
else
call nvim_win_set_cursor(a:winid, [lnum, 0])
endif
call coc#dialog#place_sign(s:pum_bufnr, a:line == 0 ? 0 : lnum)
endfunction
function! s:insert_virtual_text() abort
let bufnr = bufnr('%')
if !s:virtual_text || s:pum_index < 0
call s:clear_virtual_text()
else
" Check if could create
let insert = ''
let line = line('.') - 1
let words = getwinvar(s:pum_winid, 'words', [])
let word = get(words, s:pum_index, '')
let parts = getwinvar(s:pum_winid, 'parts', [])
let start = strlen(parts[0])
let input = strpart(getline('.'), start, col('.') - 1 - start)
if strchars(word) > strchars(input) && strcharpart(word, 0, strchars(input)) ==# input
let insert = strcharpart(word, strchars(input))
endif
if s:is_vim
if s:prop_id != 0
call prop_remove({'id': s:prop_id}, line + 1, line + 1)
endif
if !empty(insert)
let s:prop_id = prop_add(line + 1, col('.'), {
\ 'text': insert,
\ 'type': 'CocPumVirtualText'
\ })
endif
else
call nvim_buf_clear_namespace(bufnr, s:virtual_text_ns, line, line + 1)
if !empty(insert)
let opts = {
\ 'hl_mode': 'combine',
\ 'virt_text': [[insert, 'CocPumVirtualText']],
\ 'virt_text_pos': 'overlay',
\ 'virt_text_win_col': virtcol('.') - 1,
\ }
call nvim_buf_set_extmark(bufnr, s:virtual_text_ns, line, col('.') - 1, opts)
endif
endif
endif
endfunction
function! s:clear_virtual_text() abort
if s:virtual_text_support
if s:is_vim
if s:prop_id != 0
call prop_remove({'id': s:prop_id})
endif
else
call nvim_buf_clear_namespace(bufnr('%'), s:virtual_text_ns, 0, -1)
endif
endif
endfunction

2
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/rpc.vim

@ -78,8 +78,10 @@ function! coc#rpc#restart() @@ -78,8 +78,10 @@ function! coc#rpc#restart()
call coc#rpc#start_server()
else
call coc#highlight#clear_all()
call coc#ui#sign_unplace()
call coc#float#close_all()
call coc#rpc#request('detach', [])
let g:coc_service_initialized = 0
sleep 100m
let s:client['command'] = coc#util#job_command()
call coc#client#restart(s:name)

113
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/snippet.vim

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
scriptencoding utf-8
let s:is_vim = !has('nvim')
let s:map_next = 1
let s:map_prev = 1
let s:cmd_mapping = has('nvim') || has('patch-8.2.1978')
function! coc#snippet#_select_mappings()
@ -26,34 +27,68 @@ function! coc#snippet#_select_mappings() @@ -26,34 +27,68 @@ function! coc#snippet#_select_mappings()
snoremap <c-r> <c-g>"_c<c-r>
endfunction
function! coc#snippet#show_choices(lnum, col, len, values) abort
function! coc#snippet#cursor(lnum, col) abort
let m = mode()
call cursor(a:lnum, a:col + a:len)
call cursor(a:lnum, a:col)
if m !=# 'i'
call feedkeys("\<Esc>i")
call feedkeys("\<Esc>i", 'in')
endif
call timer_start(20, { -> coc#_do_complete(a:col - 1, a:values, 0)})
endfunction
function! coc#snippet#show_choices(lnum, col, len, values) abort
call coc#snippet#cursor(a:lnum, a:col + a:len)
let changedtick = b:changedtick
call timer_start(20, { -> coc#_do_complete(a:col - 1, a:values, 0, changedtick)})
redraw
endfunction
function! coc#snippet#enable()
function! coc#snippet#enable(...)
if get(b:, 'coc_snippet_active', 0) == 1
return
endif
let complete = get(a:, 1, 0)
let b:coc_snippet_active = 1
call coc#snippet#_select_mappings()
let nextkey = get(g:, 'coc_snippet_next', '<C-j>')
let prevkey = get(g:, 'coc_snippet_prev', '<C-k>')
if maparg(nextkey, 'i') =~# 'expand-jump'
if maparg(nextkey, 'i') =~# 'snippet'
let s:map_next = 0
endif
if s:map_next
execute 'inoremap <buffer><nowait><silent>'.nextkey." <C-R>=coc#rpc#request('snippetNext', [])<cr>"
if maparg(prevkey, 'i') =~# 'snippet'
let s:map_prev = 0
endif
if !empty(nextkey)
if s:map_next
execute 'inoremap <buffer><nowait><silent>'.nextkey." <C-R>=coc#snippet#jump(1, ".complete.")<cr>"
endif
execute 'snoremap <buffer><nowait><silent>'.nextkey." <Esc>:call coc#snippet#jump(1, ".complete.")<cr>"
endif
if !empty(prevkey)
if s:map_prev
execute 'inoremap <buffer><nowait><silent>'.prevkey." <C-R>=coc#snippet#jump(0, ".complete.")<cr>"
endif
execute 'snoremap <buffer><nowait><silent>'.prevkey." <Esc>:call coc#snippet#jump(0, ".complete.")<cr>"
endif
endfunction
function! coc#snippet#prev() abort
call coc#rpc#request('snippetPrev', [])
return ''
endfunction
function! coc#snippet#next() abort
call coc#rpc#request('snippetNext', [])
return ''
endfunction
function! coc#snippet#jump(direction, complete) abort
if a:direction == 1 && a:complete && pumvisible()
let pre = exists('*complete_info') && complete_info()['selected'] == -1 ? "\<C-n>" : ''
call feedkeys(pre."\<C-y>", 'in')
return ''
endif
let pre = s:cmd_mapping ? '<Cmd>' : '<Esc>'
execute 'inoremap <buffer><nowait><silent>'.prevkey." <C-R>=coc#rpc#request('snippetPrev', [])<cr>"
execute 'snoremap <buffer><nowait><silent>'.prevkey." ".pre.":call coc#rpc#request('snippetPrev', [])<cr>"
execute 'snoremap <buffer><nowait><silent>'.nextkey." ".pre.":call coc#rpc#request('snippetNext', [])<cr>"
call coc#rpc#request(a:direction == 1 ? 'snippetNext' : 'snippetPrev', [])
return ''
endfunction
function! coc#snippet#disable()
@ -66,7 +101,59 @@ function! coc#snippet#disable() @@ -66,7 +101,59 @@ function! coc#snippet#disable()
if s:map_next
silent! execute 'iunmap <buffer> <silent> '.nextkey
endif
silent! execute 'iunmap <buffer> <silent> '.prevkey
if s:map_prev
silent! execute 'iunmap <buffer> <silent> '.prevkey
endif
silent! execute 'sunmap <buffer> <silent> '.prevkey
silent! execute 'sunmap <buffer> <silent> '.nextkey
endfunction
function! coc#snippet#select(start, end, text) abort
if coc#pum#visible()
call coc#pum#close()
endif
if mode() == 's'
call feedkeys("\<Esc>", 'in')
endif
if &selection ==# 'exclusive'
let cursor = coc#snippet#to_cursor(a:start)
call cursor([cursor[0], cursor[1]])
let cmd = ''
let cmd .= mode()[0] ==# 'i' ? "\<Esc>".(col('.') == 1 ? '' : 'l') : ''
let cmd .= printf('v%s', strchars(a:text) . 'l')
let cmd .= "\<C-g>"
else
let cursor = coc#snippet#to_cursor(a:end)
call cursor([cursor[0], cursor[1] - 1])
let len = strchars(a:text) - 1
let cmd = ''
let cmd .= mode()[0] ==# 'i' ? "\<Esc>l" : ''
let cmd .= printf('v%s', len > 0 ? len . 'h' : '')
let cmd .= "o\<C-g>"
endif
call feedkeys(cmd, 'n')
endfunction
function! coc#snippet#move(position) abort
let m = mode()
if m == 's'
call feedkeys("\<Esc>", 'in')
elseif coc#pum#visible()
call coc#pum#close()
endif
let pos = coc#snippet#to_cursor(a:position)
call cursor(pos)
if pos[1] > strlen(getline(pos[0]))
startinsert!
else
startinsert
endif
endfunction
function! coc#snippet#to_cursor(position) abort
let line = getline(a:position.line + 1)
if line is v:null
return [a:position.line + 1, a:position.character + 1]
endif
return [a:position.line + 1, byteidx(line, a:position.character) + 1]
endfunction

126
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/helper.vim → etc/soft/nvim/+plugins/coc.nvim/autoload/coc/string.vim

@ -1,25 +1,77 @@ @@ -1,25 +1,77 @@
scriptencoding utf-8
" Helper methods for viml
function! coc#helper#get_charactor(line, col) abort
function! coc#string#get_character(line, col) abort
return strchars(strpart(a:line, 0, a:col - 1))
endfunction
function! coc#helper#last_character(line) abort
function! coc#string#last_character(line) abort
return strcharpart(a:line, strchars(a:line) - 1, 1)
endfunction
function! coc#helper#obj_equal(one, two) abort
for key in keys(a:one)
if a:one[key] != a:two[key]
return 0
function! coc#string#reflow(lines, width) abort
let lines = []
let currlen = 0
let parts = []
for line in a:lines
for part in split(line, '\s\+')
let w = strwidth(part)
if currlen + w + 1 >= a:width
if len(parts) > 0
call add(lines, join(parts, ' '))
endif
if w >= a:width
call add(lines, part)
let currlen = 0
let parts = []
else
let currlen = w
let parts = [part]
endif
continue
endif
call add(parts, part)
let currlen = currlen + w + 1
endfor
endfor
if len(parts) > 0
call add(lines, join(parts, ' '))
endif
return empty(lines) ? [''] : lines
endfunction
" Used when 'wrap' and 'linebreak' is enabled
function! coc#string#content_height(lines, width) abort
let len = 0
let pattern = empty(&breakat) ? '.\zs' : '['.substitute(&breakat, '\([\[\]]\)', '\\\1', 'g').']\zs'
for line in a:lines
if strwidth(line) <= a:width
let len += 1
else
let currlen = 0
for part in split(line, pattern)
let wl = strwidth(part)
if currlen == 0 && wl > 0
let len += 1
endif
let delta = currlen + wl - a:width
if delta >= 0
let len = len + (delta > 0)
let currlen = delta == 0 ? 0 : wl
if wl >= a:width
let currlen = wl%a:width
let len += float2nr(ceil(wl/(a:width + 0.0))) - (currlen == 0)
endif
else
let currlen = currlen + wl
endif
endfor
endif
endfor
return 1
return len
endfunction
" get change between two lines
function! coc#helper#str_diff(curr, previous, col) abort
function! coc#string#diff(curr, previous, col) abort
let end = strpart(a:curr, a:col - 1)
let start = strpart(a:curr, 0, a:col -1)
let endOffset = 0
@ -53,7 +105,7 @@ function! coc#helper#str_diff(curr, previous, col) abort @@ -53,7 +105,7 @@ function! coc#helper#str_diff(curr, previous, col) abort
\ }
endfunction
function! coc#helper#str_apply(content, diff) abort
function! coc#string#apply(content, diff) abort
let totalLen = strchars(a:content)
let endLen = totalLen - a:diff['end']
return strcharpart(a:content, 0, a:diff['start']).a:diff['text'].strcharpart(a:content, a:diff['end'], endLen)
@ -61,7 +113,7 @@ endfunction @@ -61,7 +113,7 @@ endfunction
" insert inserted to line at position, use ... when result is too long
" line should only contains character has strwidth equals 1
function! coc#helper#str_compose(line, position, inserted) abort
function! coc#string#compose(line, position, inserted) abort
let width = strwidth(a:line)
let text = a:inserted
let res = a:line
@ -77,7 +129,7 @@ function! coc#helper#str_compose(line, position, inserted) abort @@ -77,7 +129,7 @@ function! coc#helper#str_compose(line, position, inserted) abort
let a = strwidth(c)
if w + a <= width - 1
let w = w + a
let res = res.c
let res = res . c
endif
endfor
let res = res.strcharpart(a:line, w)
@ -89,60 +141,16 @@ function! coc#helper#str_compose(line, position, inserted) abort @@ -89,60 +141,16 @@ function! coc#helper#str_compose(line, position, inserted) abort
let a = strwidth(c)
if w + a <= width - 3
let w = w + a
let res = res.c
let res = res . c
endif
endfor
let res = res.'..'
let w = w + 2
let res = res.strcharpart(a:line, w)
let res = res . strcharpart(a:line, w)
endif
else
let first = strcharpart(a:line, 0, a:position)
let res = first.text.strcharpart(a:line, a:position + strwidth(text))
let res = first . text . strcharpart(a:line, a:position + strwidth(text))
endif
return res
endfunction
" Return new dict with keys removed
function! coc#helper#dict_omit(dict, keys) abort
let res = {}
for key in keys(a:dict)
if index(a:keys, key) == -1
let res[key] = a:dict[key]
endif
endfor
return res
endfunction
" Return new dict with keys only
function! coc#helper#dict_pick(dict, keys) abort
let res = {}
for key in keys(a:dict)
if index(a:keys, key) != -1
let res[key] = a:dict[key]
endif
endfor
return res
endfunction
" support for float values
function! coc#helper#min(first, ...) abort
let val = a:first
for i in range(0, len(a:000) - 1)
if a:000[i] < val
let val = a:000[i]
endif
endfor
return val
endfunction
" support for float values
function! coc#helper#max(first, ...) abort
let val = a:first
for i in range(0, len(a:000) - 1)
if a:000[i] > val
let val = a:000[i]
endif
endfor
return val
endfunction

473
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/ui.vim

@ -0,0 +1,473 @@ @@ -0,0 +1,473 @@
let s:is_vim = !has('nvim')
let s:is_win = has('win32') || has('win64')
let s:is_mac = has('mac')
let s:sign_api = exists('*sign_getplaced') && exists('*sign_place')
let s:sign_groups = []
let s:outline_preview_bufnr = 0
" Check <Tab> and <CR>
function! coc#ui#check_pum_keymappings(trigger) abort
if a:trigger !=# 'none'
for key in ['<cr>', '<tab>', '<c-y>', '<s-tab>']
let arg = maparg(key, 'i', 0, 1)
if get(arg, 'expr', 0)
let rhs = get(arg, 'rhs', '')
if rhs =~# '\<pumvisible()' && rhs !~# '\<coc#pum#visible()'
let rhs = substitute(rhs, '\Cpumvisible()', 'coc#pum#visible()', 'g')
let rhs = substitute(rhs, '\c"\\<C-n>"', 'coc#pum#next(1)', '')
let rhs = substitute(rhs, '\c"\\<C-p>"', 'coc#pum#prev(1)', '')
let rhs = substitute(rhs, '\c"\\<C-y>"', 'coc#pum#confirm()', '')
execute 'inoremap <silent><nowait><expr> '.arg['lhs'].' '.rhs
endif
endif
endfor
endif
endfunction
function! coc#ui#quickpick(title, items, cb) abort
if exists('*popup_menu')
function! s:QuickpickHandler(id, result) closure
call a:cb(v:null, a:result)
endfunction
function! s:QuickpickFilter(id, key) closure
for i in range(1, len(a:items))
if a:key == string(i)
call popup_close(a:id, i)
return 1
endif
endfor
" No shortcut, pass to generic filter
return popup_filter_menu(a:id, a:key)
endfunction
try
call popup_menu(a:items, {
\ 'title': a:title,
\ 'filter': function('s:QuickpickFilter'),
\ 'callback': function('s:QuickpickHandler'),
\ })
redraw
catch /.*/
call a:cb(v:exception)
endtry
else
let res = inputlist([a:title] + a:items)
call a:cb(v:null, res)
endif
endfunction
" cmd, cwd
function! coc#ui#open_terminal(opts) abort
if s:is_vim && !exists('*term_start')
echohl WarningMsg | echon "Your vim doesn't have terminal support!" | echohl None
return
endif
if get(a:opts, 'position', 'bottom') ==# 'bottom'
let p = '5new'
else
let p = 'vnew'
endif
execute 'belowright '.p.' +setl\ buftype=nofile '
setl buftype=nofile
setl winfixheight
setl norelativenumber
setl nonumber
setl bufhidden=wipe
if exists('#User#CocTerminalOpen')
exe 'doautocmd <nomodeline> User CocTerminalOpen'
endif
let cmd = get(a:opts, 'cmd', '')
let autoclose = get(a:opts, 'autoclose', 1)
if empty(cmd)
throw 'command required!'
endif
let cwd = get(a:opts, 'cwd', getcwd())
let keepfocus = get(a:opts, 'keepfocus', 0)
let bufnr = bufnr('%')
let Callback = get(a:opts, 'Callback', v:null)
function! s:OnExit(status) closure
let content = join(getbufline(bufnr, 1, '$'), "\n")
if a:status == 0 && autoclose == 1
execute 'silent! bd! '.bufnr
endif
if !empty(Callback)
call call(Callback, [a:status, bufnr, content])
endif
endfunction
if has('nvim')
call termopen(cmd, {
\ 'cwd': cwd,
\ 'on_exit': {job, status -> s:OnExit(status)},
\})
else
if s:is_win
let cmd = 'cmd.exe /C "'.cmd.'"'
endif
call term_start(cmd, {
\ 'cwd': cwd,
\ 'exit_cb': {job, status -> s:OnExit(status)},
\ 'curwin': 1,
\})
endif
if keepfocus
wincmd p
endif
return bufnr
endfunction
" run command in terminal
function! coc#ui#run_terminal(opts, cb)
let cmd = get(a:opts, 'cmd', '')
if empty(cmd)
return a:cb('command required for terminal')
endif
let opts = {
\ 'cmd': cmd,
\ 'cwd': get(a:opts, 'cwd', getcwd()),
\ 'keepfocus': get(a:opts, 'keepfocus', 0),
\ 'Callback': {status, bufnr, content -> a:cb(v:null, {'success': status == 0 ? v:true : v:false, 'bufnr': bufnr, 'content': content})}
\}
call coc#ui#open_terminal(opts)
endfunction
function! coc#ui#echo_hover(msg)
echohl MoreMsg
echo a:msg
echohl None
let g:coc_last_hover_message = a:msg
endfunction
function! coc#ui#echo_messages(hl, msgs)
if a:hl !~# 'Error' && (mode() !~# '\v^(i|n)$')
return
endif
let msgs = filter(copy(a:msgs), '!empty(v:val)')
if empty(msgs)
return
endif
execute 'echohl '.a:hl
echom a:msgs[0]
redraw
echo join(msgs, "\n")
echohl None
endfunction
function! coc#ui#preview_info(lines, filetype, ...) abort
pclose
keepalt new +setlocal\ previewwindow|setlocal\ buftype=nofile|setlocal\ noswapfile|setlocal\ wrap [Document]
setl bufhidden=wipe
setl nobuflisted
setl nospell
exe 'setl filetype='.a:filetype
setl conceallevel=0
setl nofoldenable
for command in a:000
execute command
endfor
call append(0, a:lines)
exe "normal! z" . len(a:lines) . "\<cr>"
exe "normal! gg"
wincmd p
endfunction
function! coc#ui#open_files(files)
let bufnrs = []
" added on latest vim8
if exists('*bufadd') && exists('*bufload')
for file in a:files
let file = fnamemodify(file, ':.')
if bufloaded(file)
call add(bufnrs, bufnr(file))
else
let bufnr = bufadd(file)
call bufload(file)
call add(bufnrs, bufnr)
call setbufvar(bufnr, '&buflisted', 1)
endif
endfor
else
noa keepalt 1new +setl\ bufhidden=wipe
for file in a:files
let file = fnamemodify(file, ':.')
execute 'noa edit +setl\ bufhidden=hide '.fnameescape(file)
if &filetype ==# ''
filetype detect
endif
call add(bufnrs, bufnr('%'))
endfor
noa close
endif
doautocmd BufEnter
return bufnrs
endfunction
function! coc#ui#echo_lines(lines)
echo join(a:lines, "\n")
endfunction
function! coc#ui#echo_signatures(signatures) abort
if pumvisible() | return | endif
echo ""
for i in range(len(a:signatures))
call s:echo_signature(a:signatures[i])
if i != len(a:signatures) - 1
echon "\n"
endif
endfor
endfunction
function! s:echo_signature(parts)
for part in a:parts
let hl = get(part, 'type', 'Normal')
let text = get(part, 'text', '')
if !empty(text)
execute 'echohl '.hl
execute "echon '".substitute(text, "'", "''", 'g')."'"
echohl None
endif
endfor
endfunction
function! coc#ui#iterm_open(dir)
return s:osascript(
\ 'if application "iTerm2" is not running',
\ 'error',
\ 'end if') && s:osascript(
\ 'tell application "iTerm2"',
\ 'tell current window',
\ 'create tab with default profile',
\ 'tell current session',
\ 'write text "cd ' . a:dir . '"',
\ 'write text "clear"',
\ 'activate',
\ 'end tell',
\ 'end tell',
\ 'end tell')
endfunction
function! s:osascript(...) abort
let args = join(map(copy(a:000), '" -e ".shellescape(v:val)'), '')
call s:system('osascript'. args)
return !v:shell_error
endfunction
function! s:system(cmd)
let output = system(a:cmd)
if v:shell_error && output !=# ""
echohl Error | echom output | echohl None
return
endif
return output
endfunction
function! coc#ui#set_lines(bufnr, changedtick, original, replacement, start, end, changes, cursor, col) abort
if !bufloaded(a:bufnr)
return
endif
let delta = 0
if !empty(a:col)
let delta = col('.') - a:col
endif
if getbufvar(a:bufnr, 'changedtick') > a:changedtick && bufnr('%') == a:bufnr
" try apply current line change
let lnum = line('.')
" change for current line
if a:end - a:start == 1 && a:end == lnum && len(a:replacement) == 1
let idx = a:start - lnum + 1
let previous = get(a:original, idx, 0)
if type(previous) == 1
let content = getline('.')
if previous !=# content
let diff = coc#string#diff(content, previous, col('.'))
let changed = get(a:replacement, idx, 0)
if type(changed) == 1 && strcharpart(previous, 0, diff['end']) ==# strcharpart(changed, 0, diff['end'])
let applied = coc#string#apply(changed, diff)
let replacement = copy(a:replacement)
let replacement[idx] = applied
call coc#compat#buf_set_lines(a:bufnr, a:start, a:end, replacement)
return
endif
endif
endif
endif
endif
if exists('*nvim_buf_set_text') && !empty(a:changes)
for item in reverse(copy(a:changes))
call nvim_buf_set_text(a:bufnr, item[1], item[2], item[3], item[4], item[0])
endfor
else
call coc#compat#buf_set_lines(a:bufnr, a:start, a:end, a:replacement)
endif
if !empty(a:cursor)
call cursor(a:cursor[0], a:cursor[1] + delta)
endif
endfunction
function! coc#ui#change_lines(bufnr, list) abort
if !bufloaded(a:bufnr) | return v:null | endif
undojoin
if exists('*setbufline')
for [lnum, line] in a:list
call setbufline(a:bufnr, lnum + 1, line)
endfor
elseif a:bufnr == bufnr('%')
for [lnum, line] in a:list
call setline(lnum + 1, line)
endfor
else
let bufnr = bufnr('%')
exe 'noa buffer '.a:bufnr
for [lnum, line] in a:list
call setline(lnum + 1, line)
endfor
exe 'noa buffer '.bufnr
endif
endfunction
function! coc#ui#open_url(url)
if has('mac') && executable('open')
call system('open '.a:url)
return
endif
if executable('xdg-open')
call system('xdg-open '.a:url)
return
endif
call system('cmd /c start "" /b '. substitute(a:url, '&', '^&', 'g'))
if v:shell_error
echohl Error | echom 'Failed to open '.a:url | echohl None
return
endif
endfunction
function! coc#ui#rename_file(oldPath, newPath, write) abort
let bufnr = bufnr(a:oldPath)
if bufnr == -1
throw 'Unable to get bufnr of '.a:oldPath
endif
if a:oldPath =~? a:newPath && (s:is_mac || s:is_win)
return coc#ui#safe_rename(bufnr, a:oldPath, a:newPath, a:write)
endif
if bufloaded(a:newPath)
execute 'silent bdelete! '.bufnr(a:newPath)
endif
let current = bufnr == bufnr('%')
let bufname = fnamemodify(a:newPath, ":~:.")
let filepath = fnamemodify(bufname(bufnr), '%:p')
let winid = coc#compat#buf_win_id(bufnr)
let curr = -1
if winid == -1
let curr = win_getid()
let file = fnamemodify(bufname(bufnr), ':.')
execute 'keepalt tab drop '.fnameescape(bufname(bufnr))
let winid = win_getid()
endif
call coc#compat#execute(winid, 'keepalt file '.fnameescape(bufname), 'silent')
call coc#compat#execute(winid, 'doautocmd BufEnter')
if a:write
call coc#compat#execute(winid, 'noa write!', 'silent')
call delete(filepath, '')
endif
if curr != -1
call win_gotoid(curr)
endif
return bufnr
endfunction
" System is case in sensitive and newPath have different case.
function! coc#ui#safe_rename(bufnr, oldPath, newPath, write) abort
let winid = win_getid()
let lines = getbufline(a:bufnr, 1, '$')
execute 'keepalt tab drop '.fnameescape(fnamemodify(a:oldPath, ':.'))
let view = winsaveview()
execute 'keepalt bwipeout! '.a:bufnr
if a:write
call delete(a:oldPath, '')
endif
execute 'keepalt edit '.fnameescape(fnamemodify(a:newPath, ':~:.'))
let bufnr = bufnr('%')
call coc#compat#buf_set_lines(bufnr, 0, -1, lines)
if a:write
execute 'noa write'
endif
call winrestview(view)
call win_gotoid(winid)
return bufnr
endfunction
function! coc#ui#sign_unplace() abort
if exists('*sign_unplace')
for group in s:sign_groups
call sign_unplace(group)
endfor
endif
endfunction
function! coc#ui#update_signs(bufnr, group, signs) abort
if !s:sign_api || !bufloaded(a:bufnr)
return
endif
call sign_unplace(a:group, {'buffer': a:bufnr})
for def in a:signs
let opts = {'lnum': def['lnum']}
if has_key(def, 'priority')
let opts['priority'] = def['priority']
endif
call sign_place(0, a:group, def['name'], a:bufnr, opts)
endfor
endfunction
function! coc#ui#outline_preview(config) abort
let view_id = get(w:, 'cocViewId', '')
if view_id !=# 'OUTLINE'
return
endif
let wininfo = get(getwininfo(win_getid()), 0, v:null)
if empty(wininfo)
return
endif
let border = get(a:config, 'border', v:true)
let th = &lines - &cmdheight - 2
let range = a:config['range']
let height = min([range['end']['line'] - range['start']['line'] + 1, th - 4])
let to_left = &columns - wininfo['wincol'] - wininfo['width'] < wininfo['wincol']
let start_lnum = range['start']['line'] + 1
let end_lnum = range['end']['line'] + 1 - start_lnum > &lines ? start_lnum + &lines : range['end']['line'] + 1
let lines = getbufline(a:config['bufnr'], start_lnum, end_lnum)
let content_width = max(map(copy(lines), 'strdisplaywidth(v:val)'))
let width = min([content_width, a:config['maxWidth'], to_left ? wininfo['wincol'] - 3 : &columns - wininfo['wincol'] - wininfo['width']])
let filetype = getbufvar(a:config['bufnr'], '&filetype')
let cursor_row = coc#cursor#screen_pos()[0]
let config = {
\ 'relative': 'editor',
\ 'row': cursor_row - 1 + height < th ? cursor_row - (border ? 1 : 0) : th - height - (border ? 1 : -1),
\ 'col': to_left ? wininfo['wincol'] - 4 - width : wininfo['wincol'] + wininfo['width'],
\ 'width': width,
\ 'height': height,
\ 'lines': lines,
\ 'border': border ? [1,1,1,1] : v:null,
\ 'rounded': get(a:config, 'rounded', 1) ? 1 : 0,
\ 'winblend': a:config['winblend'],
\ 'highlight': a:config['highlight'],
\ 'borderhighlight': a:config['borderhighlight'],
\ }
let winid = coc#float#get_float_by_kind('outline-preview')
let result = coc#float#create_float_win(winid, s:outline_preview_bufnr, config)
if empty(result)
return v:null
endif
call setwinvar(result[0], 'kind', 'outline-preview')
let s:outline_preview_bufnr = result[1]
if !empty(filetype)
call coc#compat#execute(result[0], 'setfiletype '.filetype)
endif
return result[1]
endfunction
function! coc#ui#outline_close_preview() abort
let winid = coc#float#get_float_by_kind('outline-preview')
if winid
call coc#float#close(winid)
endif
endfunction

878
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/util.vim

File diff suppressed because it is too large Load Diff

36
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/vtext.vim

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
let s:is_vim = !has('nvim')
let s:virtual_text_support = has('nvim-0.5.0') || has('patch-9.0.0067')
let s:text_options = has('patch-9.0.0121')
" opts.hl_mode default to 'combine'.
" opts.col not used on neovim.
" opts.virt_text_win_col neovim only.
" opts.text_align could be 'after' 'right' 'below', vim9 only.
" opts.text_wrap could be 'wrap' and 'truncate', vim9 only.
function! coc#vtext#add(bufnr, src_id, line, blocks, opts) abort
if !s:virtual_text_support
return
endif
if s:is_vim
for [text, hl] in a:blocks
let type = coc#api#create_type(a:src_id, hl, a:opts)
let column = get(a:opts, 'col', 0)
let opts = { 'text': text, 'type': type }
if s:text_options && column == 0
let opts['text_align'] = get(a:opts, 'text_align', 'after')
let opts['text_wrap'] = get(a:opts, 'text_wrap', 'truncate')
endif
call prop_add(a:line + 1, column, opts)
endfor
else
let opts = {
\ 'virt_text': a:blocks,
\ 'hl_mode': get(a:opts, 'hl_mode', 'combine'),
\ }
if has('nvim-0.5.1') && has_key(a:opts, 'virt_text_win_col')
let opts['virt_text_win_col'] = a:opts['virt_text_win_col']
let opts['virt_text_pos'] = 'overlay'
endif
call nvim_buf_set_extmark(a:bufnr, a:src_id, a:line, 0, opts)
endif
endfunction

118
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/window.vim

@ -1,20 +1,102 @@ @@ -1,20 +1,102 @@
let g:coc_max_treeview_width = get(g:, 'coc_max_treeview_width', 40)
let s:is_vim = !has('nvim')
" Get tabpagenr of winid, return -1 if window doesn't exist
function! coc#window#tabnr(winid) abort
if exists('*nvim_win_get_tabpage')
try
return nvim_win_get_tabpage(a:winid)
catch /Invalid window id/
return -1
endtry
endif
if exists('*win_execute')
let ref = {}
call win_execute(a:winid, 'let ref["out"] = tabpagenr()')
return get(ref, 'out', -1)
elseif has('nvim')
elseif !s:is_vim
let info = getwininfo(a:winid)
return empty(info) ? -1 : info[0]['tabnr']
else
throw 'win_execute() not exists, please upgrade your vim.'
throw 'win_execute() does not exist, please upgrade your vim.'
endif
endfunction
function! coc#window#get_cursor(winid) abort
if exists('*nvim_win_get_cursor')
return nvim_win_get_cursor(a:winid)
endif
return coc#api#exec('win_get_cursor', [a:winid])
endfunction
" Check if winid visible on current tabpage
function! coc#window#visible(winid) abort
if s:is_vim
if coc#window#tabnr(a:winid) != tabpagenr()
return 0
endif
" Check possible hidden popup
try
return get(popup_getpos(a:winid), 'visible', 0) == 1
catch /^Vim\%((\a\+)\)\=:E993/
return 1
endtry
endif
if !nvim_win_is_valid(a:winid)
return 0
endif
return coc#window#tabnr(a:winid) == tabpagenr()
endfunction
" Return v:null when name or window doesn't exist,
" 'getwinvar' only works on window of current tab
function! coc#window#get_var(winid, name, ...) abort
if !s:is_vim
try
if a:name =~# '^&'
return nvim_win_get_option(a:winid, a:name[1:])
else
return nvim_win_get_var(a:winid, a:name)
endif
catch /E5555/
return get(a:, 1, v:null)
endtry
else
try
return coc#api#exec('win_get_var', [a:winid, a:name, get(a:, 1, v:null)])
catch /.*/
return get(a:, 1, v:null)
endtry
endif
endfunction
" Not throw like setwinvar
function! coc#window#set_var(winid, name, value) abort
try
if !s:is_vim
if a:name =~# '^&'
call nvim_win_set_option(a:winid, a:name[1:], a:value)
else
call nvim_win_set_var(a:winid, a:name, a:value)
endif
else
call coc#api#exec('win_set_var', [a:winid, a:name, a:value])
endif
catch /Invalid window id/
" ignore
endtry
endfunction
function! coc#window#is_float(winid) abort
if !has('nvim')
if s:is_vim
if exists('*popup_list')
return index(popup_list(), a:winid) != -1
else
try
return !empty(popup_getpos(a:winid))
catch /^Vim\%((\a\+)\)\=:E993/
return 0
endtry
endif
return 0
elseif exists('*nvim_win_get_config')
@ -23,7 +105,7 @@ function! coc#window#is_float(winid) abort @@ -23,7 +105,7 @@ function! coc#window#is_float(winid) abort
endif
endfunction
function! coc#window#set_heigth(winid, height) abort
function! coc#window#set_height(winid, height) abort
if empty(getwininfo(a:winid))
return
endif
@ -34,6 +116,26 @@ function! coc#window#set_heigth(winid, height) abort @@ -34,6 +116,26 @@ function! coc#window#set_heigth(winid, height) abort
endif
endfunction
function! coc#window#adjust_width(winid) abort
let bufnr = winbufnr(a:winid)
if bufloaded(bufnr)
let maxwidth = 0
let lines = getbufline(bufnr, 1, '$')
if len(lines) > 2
call coc#compat#execute(a:winid, 'setl nowrap')
for line in lines
let w = strwidth(line)
if w > maxwidth
let maxwidth = w
endif
endfor
endif
if maxwidth > winwidth(a:winid)
call coc#compat#execute(a:winid, 'vertical resize '.min([maxwidth, g:coc_max_treeview_width]))
endif
endif
endfunction
" Get single window by window variable, current tab only
function! coc#window#find(key, val) abort
for i in range(1, winnr('$'))
@ -56,14 +158,6 @@ function! coc#window#bufnrs() abort @@ -56,14 +158,6 @@ function! coc#window#bufnrs() abort
return uniq(map(winids, 'winbufnr(v:val)'))
endfunction
" Make sure window exists
function! coc#window#gotoid(winid) abort
noa let res = win_gotoid(a:winid)
if res == 0
throw 'Invalid window number'
endif
endfunction
" Avoid errors
function! coc#window#close(winid) abort
if empty(a:winid) || a:winid == -1

12
etc/soft/nvim/+plugins/coc.nvim/autoload/health/coc.vim

@ -16,8 +16,8 @@ endfunction @@ -16,8 +16,8 @@ endfunction
function! s:checkEnvironment() abort
let valid
\ = s:checkVim(has('nvim'), 'nvim', 'nvim-0.3.2')
\ + s:checkVim(!has('nvim'), 'vim', 'patch-0.8.1453')
\ = s:checkVim(has('nvim'), 'nvim', 'nvim-0.4.0')
\ + s:checkVim(!has('nvim'), 'vim', 'patch-8.1.1719')
let node = get(g:, 'coc_node_path', $COC_NODE_PATH == '' ? 'node' : $COC_NODE_PATH)
if !executable(node)
let valid = 0
@ -32,9 +32,9 @@ function! s:checkEnvironment() abort @@ -32,9 +32,9 @@ function! s:checkEnvironment() abort
if empty(ms)
let valid = 0
call health#report_error('Unable to detect version of node, make sure your node executable is http://nodejs.org/')
elseif str2nr(ms[1]) < 12 || (str2nr(ms[1]) == 12 && str2nr(ms[2]) < 12)
elseif str2nr(ms[1]) < 14 || (str2nr(ms[1]) == 14 && str2nr(ms[2]) < 14)
let valid = 0
call health#report_warn('Node.js version '.trim(output).' < 12.12.0, please upgrade node.js')
call health#report_warn('Node.js version '.trim(output).' < 14.14.0, please upgrade node.js')
endif
if valid
call health#report_ok('Environment check passed')
@ -80,7 +80,7 @@ function! s:checkAutocmd() @@ -80,7 +80,7 @@ function! s:checkAutocmd()
endfor
endfunction
function! s:checkInitailize() abort
function! s:checkInitialize() abort
if coc#client#is_running('coc')
call health#report_ok('Service started')
return 1
@ -95,6 +95,6 @@ endfunction @@ -95,6 +95,6 @@ endfunction
function! health#coc#check() abort
call s:checkEnvironment()
call s:checkCommand()
call s:checkInitailize()
call s:checkInitialize()
call s:checkAutocmd()
endfunction

114468
etc/soft/nvim/+plugins/coc.nvim/build/index.js

File diff suppressed because it is too large Load Diff

2167
etc/soft/nvim/+plugins/coc.nvim/data/schema.json

File diff suppressed because it is too large Load Diff

1754
etc/soft/nvim/+plugins/coc.nvim/doc/coc-config.txt

File diff suppressed because it is too large Load Diff

2774
etc/soft/nvim/+plugins/coc.nvim/doc/coc.txt

File diff suppressed because it is too large Load Diff

15
etc/soft/nvim/+plugins/coc.nvim/esbuild.js

@ -38,22 +38,11 @@ async function start(watch) { @@ -38,22 +38,11 @@ async function start(watch) {
watch,
minify: process.env.NODE_ENV === 'production',
sourcemap: process.env.NODE_ENV === 'development',
define: {REVISION: '"' + revision + '"', ESBUILD: 'true'},
define: {REVISION: '"' + revision + '"', ESBUILD: 'true', 'global.__TEST__': false},
mainFields: ['module', 'main'],
platform: 'node',
target: 'node12.12',
target: 'node14.14',
outfile: 'build/index.js',
banner: {
js: `(function () {
var v = process.version
var parts = v.slice(1).split('.')
var major = parseInt(parts[0], 10)
var minor = parseInt(parts[1], 10)
if (major < 12 || (major == 12 && minor < 12)) {
throw new Error('coc.nvim requires node >= v12.12.0, current version: ' + v)
}
})(); `
},
plugins: [envPlugin]
})
}

358
etc/soft/nvim/+plugins/coc.nvim/history.md

@ -1,3 +1,299 @@ @@ -1,3 +1,299 @@
# 2022-10-20
- Add `coc#pum#one_more()`
# 2022-10-19
- Trigger for trigger sources when no filter results available.
# 2022-10-18
- Change `suggest.maxCompleteItemCount` default to 256.
# 2022-10-17
- Set `g:coc_service_initialized` to `0` before service restart.
- Show warning when diagnostic jump failed.
- Use strwidth.wasm module for string display width.
- Add API `workspace.getDisplayWidth`.
# 2022-10-15
- Add configuration `inlayHint.display`.
# 2022-10-07
- Use `CocFloatActive` for highlight active parameters.
# 2022-09-28
- Limit popupmenu width when exceed screen to &pumwidth, instead of change
completion column.
- Make escape of `${name}` for ultisnip snippets the same behavior as
Ultisnip.vim.
# 2022-09-27
- Use fuzzy.wasm for native fuzzy match.
- Add `binarySearch` and `isFalsyOrEmpty` functions for array.
- `suggest.localityBonus` works like VSCode, using selection ranges.
- Add and export `workspace.computeWordRanges`.
- Rework keywords parse for better performance (parse changed lines only and use
yield to reduce iteration).
# 2022-09-12
- All configurations are now scoped #4185
- No `onDidChangeConfiguration` event fired when workspace folder changed.
- Deprecated configuration `suggest.detailMaxLength`, use `suggest.labelMaxLength` instead.
- Deprecated configuration `inlayHint.filetypes`, use `inlayHint.enable` with scoped languages instead.
- Deprecated configuration `semanticTokens.filetypes`, use `semanticTokens.enable` with scoped languages instead.
- Use `workspaceFolderValue` instead of `workspaceValue` for `ConfigurationInspect` returned by `WorkspaceConfiguration.inspect()`.
# 2022-09-04
- Add configuration "snippet.choicesMenuPicker".
# 2022-09-03
- Send "WinClosed" event to node client.
- Add `onDidFilterStateChange` and `onDidCursorMoved` to `TreeView`.
- Support `autoPreview` for outline.
# 2022-09-02
- Support `diagnostic.virtualTextFormat`.
- Add command `workspace.writeHeapSnapshot`.
# 2022-09-01
- Add configuration "suggest.asciiMatch"
- Support `b:coc_force_attach`.
# 2022-08-31
- Add configuration "suggest.reversePumAboveCursor".
- Use `DiagnosticSign*` highlight groups when possible.
- Use `DiagnosticUnderline*` highlight groups when possible.
# 2022-08-30
- Export `LineBuilder` class.
# 2022-08-29
- Fix semanticTokens highlights unexpected cleared
- Fix range of `doQuickfix` action.
- Check reverse of `CocFloating`, use `border` and `Normal` highlight when reversed.
- Make `CocInlayHint` use background of `SignColumn`.
- Add command `document.toggleInlayHint`.
# 2022-08-28
- Make `CocMenuSel` use background of `PmenuSel`.
- Snippet related configuration changed (old configuration still works until next release)
- "coc.preferences.snippetStatusText" -> "snippet.statusText"
- "coc.preferences.snippetHighlight" -> "snippet.highlight"
- "coc.preferences.nextPlaceholderOnDelete" -> "snippet.nextPlaceholderOnDelete"
- Add configuration `"list.smartCase"`
- Add configurations for inlay hint
- "inlayHint.refreshOnInsertMode"
- "inlayHint.enableParameter"
- "inlayHint.typeSeparator"
- "inlayHint.parameterSeparator"
- "inlayHint.subSeparator"
# 2022-08-27
- Avoid use `EasyMotion#is_active`, use autocmd to disable linting.
- Show message when call hierarchy provider not found or bad position.
# 2022-08-26
- Remove `completeOpt` from `workspace.env`.
- Add configuration `"diagnostic.virtualTextAlign"`.
- Add warning when required features not compiled with vim.
- Not echo error for semanticTokens request (log only).
- Merge results form providers when possible.
# 2022-08-24
- Virtual text of suggest on vim9.
- Virtual text of diagnostics on vim9.
- Add configuration `inlayHint.filetypes`.
- Inlay hint support on vim9.
# 2022-08-23
- Retry semanticTokens request on server cancel (LSP 3.17).
- `RelativePattern` support for `workspace.createFileSystemWatcher()`.
- `relativePatternSupport` for `DidChangeWatchedFiles` (LSP 3.17).
- Not echo error on `doComplete()`.
# 2022-08-21
- Added `window.createFloatFactory()`, deprecated `FloatFactory` class.
- Support `labelDetails` field of `CompleteItem`(LSP 3.17).
- Added `triggerKind` to `CodeActionContext`, export `CodeActionTriggerKind`.
# 2022-08-20
- Support pull diagnostics `:h coc-pullDiagnostics`.
- Break change: avoid extension overwrite builtin configuration defaults.
- Change default value of configuration "diagnostic.format".
- 'line' changes to 'currline' for `CocAction('codeAction')`.
- Check NodeJS version on syntax error.
# 2022-08-10
- Change "notification.highlightGroup" default to "Normal".
# 2022-08-07
- Add configuration 'suggest.pumFloatConfig'.
# 2022-08-04
- Make diagnostic float window with the same background as CocFloating.
# 2022-08-03
- Add highlight group 'CocFloatingDividingLine'.
# 2022-08-01
- Use custom popup menu, #3862.
- Use "first" instead of "none" for configuration `suggest.selection`.
- Make "first" default for `suggest.selection`, like VSCode.
- Add default blue color for hlgroup `CocMenuSel`.
# 2022-06-14
- Add highlight groups `CocListLine` and `CocListSearch`.
# 2022-06-11
- Add configuration "notification.disabledProgressSources"
- Add "rounded" property to "floatConfig"
# 2022-06-04
- Add configuration `workspace.openOutputCommand`.
- Log channel message of vim when `g:node_client_debug` enabled.
# 2022-05-30
- Disable `progressOnInitialization` for language client by default.
# 2022-05-28
- Support `repeat#set` for commands that make changes only.
# 2022-05-24
- Add transition and annotation support for `workspace.applyEdits()`.
- Add command `workspace.undo` and `workspace.redo`.
- Remove configuration `coc.preferences.promptWorkspaceEdit`.
- Remove command `CocAction` and `CocFix`.
# 2022-05-22
- Check for previous position when not able to find completion match.
- Add `content` support to `window.showMenuPicker()`
# 2022-05-17
- Add `QuickPick` module.
- Add API `window.showQuickPick()` and `window.createQuickPick()`.
# 2022-05-16
- Add properties `title`, `loading` & `borderhighlight` to `InputBox`
# 2022-05-14
- Add `InputOption` support to `window.requestInput`
- Add API `window.createInputBox()`.
# 2022-05-13
- Notification support like VSCode https://github.com/neoclide/coc.nvim/discussions/3813
- Add configuration `notification.minProgressWidth`
- Add configuration `notification.preferMenuPicker`
- Support `source` in notification windows.
# 2022-05-07
- Show sort method as description in outline view.
- Add configuration `outline.switchSortKey`, default to `<C-s>`.
- Add configuration `outline.detailAsDescription`, default to `true`.
- Add variable `g:coc_max_treeview_width`.
- Add `position: 'center'` support to `window.showMenuPicker()`
# 2022-05-06
- Use menu for `window.showQuickpick()`.
- Add configuration `outline.autoWidth`, default to `true`.
# 2022-05-05
- Add key bindings to dialog (created by `window.showDialog()`) on neovim.
# 2022-05-04
- Add `languages.registerInlayHintsProvider()` for inlay hint support.
# 2022-04-25
- Add `LinkedEditing` support
# 2022-04-23
- Add `WinScrolled` event to events.
# 2022-04-20
- Select recent item when input is empty and selection is `recentUsedByPrefix`.
- Add `coc#snippet#prev()` and `coc#snippet#next()`.
- Add command `document.checkBuffer`.
- Add `region` param to `window.diffHighlights()`.
# 2022-04-06
- `workspace.onDidOpenTextDocument` fire `contentChanges` as empty array when
document changed with same lines.
# 2022-04-04
- Avoid `CompleteDone` cancel next completion.
- Avoid indent change on `<C-n>` and `<C-p>` during completion.
- Support `joinUndo` and `move` with `document.applyEdits()`.
# 2022-04-02
- Change `suggest.triggerCompletionWait` default to `0`.
- Not trigger completion on `TextChangedP`.
- Remove configuration `suggest.echodocSupport`.
- Fix complettion triggered after `<C-e>`.
# 2022-03-31
- Check buffer rename on write.
# 2022-03-30
- Improve words parse performance.
- Remove configurations `coc.source.around.firstMatch` and `coc.source.buffer.firstMatch`.
- Fix `coc.source.buffer.ignoreGitignore` not works
# 2022-03-30
- Check document reload on detach.
# 2022-03-29
- Add menu actions to refactor buffer.
# 2022-03-12
- Avoid use `<sapce><bs>` for cancel completion.
@ -212,7 +508,7 @@ @@ -212,7 +508,7 @@
- feat(plugin): add highligher module
- feat(refactor): add `<Plug>(coc-refactor)` for refactor window
- feat(extension): use mv module for folder rename
- feat(extension): support install taged extension
- feat(extension): support install tagged extension
- feat(extension): support custom extension root `g:coc_extension_root`
- feat(handler): close signature float window on ')'
- feat(list): support `g:coc_quickfix_open_command`
@ -284,7 +580,7 @@ @@ -284,7 +580,7 @@
- Add `disableDiagnostics` & `disableCompletion` to languageclient configuration.
- Add `signature.triggerSignatureWait` configuration.
- Add vim-repeat support for run command and quickfix.
- Add prefered `codeAction` support.
- Add preferred `codeAction` support.
- Add `prompt.paste` action to list.
- Add title as argument support for `codeAction` action.
- Add `suggest.floatEnable` configuration.
@ -335,16 +631,16 @@ @@ -335,16 +631,16 @@
- **Break change** logic for resolve workspace folder changed.
- Add `Task` module.
- Add `getCurrentFunctionSymbol` action.
- Add `list.source.outline.ctagsFilestypes` setting.
- Add `list.source.outline.ctagsFiletypes` setting.
- Add `suggest.disableMenu` and `suggest.disableMenu` settings.
- Add `equal` support for complete items.
- Add support for do action with visual select lines of list.
- Add expand tilder support for language server command.
- Add switch matcher support to list.
- Add select all support to lsit.
- Add select all support to list.
- Add quickfix action to list.
- Add `selectionRanges` of LSP.
- Add load extentions for &rtp support.
- Add load extensions for &rtp support.
- Add `coc#on_enter()` for formatOnType and add new lines on enter.
- Improve completion by support trigger completion when pumvisible.
- Remove document check on `BufWritePre`.
@ -354,7 +650,7 @@ @@ -354,7 +650,7 @@
- **Break change** not using vim-node-rpc from npm modules any more.
- **Break change** rename `<Plug>_` to `<Plug>CocRefresh`.
- Fix wrong format options send to server.
- Fix throw eror when extention root not created.
- Fix throw error when extension root not created.
- Fix MarkedString not considered as markdown.
- Fix echo message on vim exit.
- Fix error throw on file watch.
@ -365,11 +661,11 @@ @@ -365,11 +661,11 @@
- Add `workspace.resolveRootFolder`.
- Add `diagnostic.joinMessageLines` setting.
- Add `suggest.completionItemKindLabels` setting.
- Add `memento` support for extention.
- Add `memento` support for extension.
- Add `workspace.getSelectedRange`.
- Add `Terminal` module.
- Add command `workbench.action.reloadWindow`.
- Fix extention not activated by command.
- Fix extension not activated by command.
- Fix broken undo with floating window.
- Fix document create possible wrong uri & filetype.
- Improve highlight with floating window.
@ -400,7 +696,7 @@ @@ -400,7 +696,7 @@
- Fix critical performance issue on diff text.
- Improve color of `CocHighlightText`.
- Improve sort of complete items.
- Improve extention list with version and open action.
- Improve extension list with version and open action.
# 2019-03-16
@ -420,14 +716,14 @@ @@ -420,14 +716,14 @@
- **Break change** change buffers instead of disk file for `workspace.applyEdits`.
- **Break change** add config errors to diagnostic list instead of jump locations.
- **Break change** hack for popup menu flicker is remvoed, use `suggest.reloadPumOnInsertChar` to enable it.
- **Break change** hack for popup menu flicker is removed, use `suggest.reloadPumOnInsertChar` to enable it.
- **Break change** use `nvim_select_popupmenu_item` for number select completion.
- Add floating window for completion items.
- Add floating window support for diagnostics.
- Add floating window support for hover documentation.
- Add `coc#on_enter()` for notify enter pressed.
- Add setting `coc.preferences.useQuickfixForLocations`.
- Add support of `g:coc_watch_extensions` for automatic reload extentions.
- Add support of `g:coc_watch_extensions` for automatic reload extensions.
- Add command: `editor.action.doCodeAction`.
- Fix service on restarted on windows after rebuild.
- Fix config of airline.
@ -452,7 +748,7 @@ @@ -452,7 +748,7 @@
- **Break change** default of `suggest.detailMaxLength` changed to 100.
- **Break change** option of `workspace.registerKeymap` changed.
- Add settings: `suggest.defailField`.
- Add settings: `suggest.detailField`.
- Add check for autocmd in health check.
- Add trigger patterns support for complete sources.
- Add support of `coc-snippets-expand-jump`
@ -465,7 +761,7 @@ @@ -465,7 +761,7 @@
- **Break change** no longer automatic trigger for CursorHoldI #452.
- **Break change** add preview option of `completeopt` according to `suggest.enablePreview`.
- Add statusItem for CocUpdate.
- Add `-sycn` option for `:CocInstall`
- Add `-sync` option for `:CocInstall`
- Add support for floating preview window.
- Add more module export.
- Fix check of vim-node-rpc throw error.
@ -522,15 +818,15 @@ @@ -522,15 +818,15 @@
not found.
- Add support for configure sign in statusline.
- Add help action for list.
- Fix parse error on extentions update.
- Fix parse error on extensions update.
- Fix wrong uri on windows.
- Fix cancel list without close ui.
- Improve startup time by remove jobwait.
# 2019-02-02
- **Break change:** extentions now update automatically, prompt is removed.
- Add check for extention compatibility.
- **Break change:** extensions now update automatically, prompt is removed.
- Add check for extension compatibility.
- Add transform support for placeholder.
- Add check for node version.
- Add error check for list.
@ -538,7 +834,7 @@ @@ -538,7 +834,7 @@
- Fix preview window not shown.
- Fix highlight not cleared on vim.
- Fix highlight commands of list block vim on start.
- Improve extention load.
- Improve extension load.
- Improve list experience.
# 2019-01-28
@ -553,7 +849,7 @@ @@ -553,7 +849,7 @@
# 2019-01-24
- **Break change:** python code for denite support moved to seperated repo.
- **Break change:** python code for denite support moved to separated repo.
- **Break change:** Quickfix list no longer used.
- Add list support.
- Add configuration: `coc.preferences.diagnostic.virtualText`.
@ -598,7 +894,7 @@ @@ -598,7 +894,7 @@
- **Break change:** created keymaps use rpcrequest instead of rpcnotify.
- **Break change:** snippets provider is removed, use `coc-snippets` for
extention snippets.
extension snippets.
- Add command: `coc.action.insertSnippet`
- Fix position of snippets.
- Fix modifier of registered keymaps.
@ -616,7 +912,7 @@ @@ -616,7 +912,7 @@
# 2018-12-27
- **Break change:** no more message on service ready.
- **Break change:** vim source now registered as extention.
- **Break change:** vim source now registered as extension.
- **Break change:** complete item sort have reworked.
- **Break change:** request send to coc would throw when service not ready.
- Add support for check current state on diagnostic update.
@ -646,7 +942,7 @@ @@ -646,7 +942,7 @@
- Add command `workspace.clearWatchman`.
- Add `quickfixs`, `doCodeAction` and `doQuickfix` actions.
- Add `g:vim_node_rpc_args` for debug purpose.
- Add `coc#add_extension()` for specify extentions to install.
- Add `coc#add_extension()` for specify extensions to install.
- Fix clients not restarted on CocRestart.
- Fix `execArgv` and `runtime` not work for node language server.
- Fix detail of complete item not echoed sometimes.
@ -657,7 +953,7 @@ @@ -657,7 +953,7 @@
# 2018-12-17
- **Break change** `vim-node-rpc` now upgrade in bacground.
- **Break change** `vim-node-rpc` now upgrade in background.
- Add `ignoredRootPaths` to `languageserver` option.
- Add detect of vim running state.
- Add `client.vim` for create clients.
@ -671,7 +967,7 @@ @@ -671,7 +967,7 @@
- **Break change** `fixInsertedWord` fix inserted word which ends with word
after.
- **Break change** `onCompleteSelect` is removed.
- Add `workspace.registerKeymap` for regist keymap.
- Add `workspace.registerKeymap` for register keymap.
- Add match score for sort complete items.
- Fix possible connection lost.
- Fix priority of diagnostic signs.
@ -698,7 +994,7 @@ @@ -698,7 +994,7 @@
`never`.
- Fix can't install on windows vim.
- Fix `displayByAle` not clearing diagnostics.
- Add check for `vim-node-rpc` udpate on vim.
- Add check for `vim-node-rpc` update on vim.
- Add `Resolver` module.
- Improve apply `WorkspaceEdit`, support `0` as document version and merge
edits for same document.
@ -710,7 +1006,7 @@ @@ -710,7 +1006,7 @@
- Add setting: `coc.preferences.hoverTarget`, support use echo.
- Add setting `coc.preferences.diagnostic.displayByAle` for use ale to display errors.
- Add setting `coc.preferences.extensionUpdateCheck` for control update check of
extentions.
extensions.
- Add `coc#config` for set configuration in vim.
- Fix rootPath not resolved on initialize.
- Fix possible wrong `tabSize` by use `shiftwidth` option.
@ -734,7 +1030,7 @@ @@ -734,7 +1030,7 @@
- Add echo message when provider not found for some actions.
- Add support for `formatexpr`
- Add support for locality bonus like VSCode.
- Add support of `applyAdditionaLEdits` on item selected by `<esc>`
- Add support of `applyAdditionalLEdits` on item selected by `<esc>`
- Add `coc.preferences.useQuickfixForLocations`
- Add `coc.preferences.messageLevel`
- Add support for trigger command which not registered by server.
@ -751,13 +1047,13 @@ @@ -751,13 +1047,13 @@
# 2018-11-24
- **Break change** sources exluding `around`, `buffer` or `file` are extracted
- **Break change** sources excluding `around`, `buffer` or `file` are extracted
as extensions.
- **Break change** custom source not exists any more.
- **Break change** custom source doesn't exist any more.
- Add `coc.preferences.preferCompleteThanJumpPlaceholder` to make jump
placeholder behavior as confirm completion when possible.
- Add `CocDiagnosticChange` autocmd for force statusline update.
- Add `onDidUnloadExtension` event on extention unload.
- Add `onDidUnloadExtension` event on extension unload.
- Fix `getDiagnosticsInRange`, consider all interactive ranges.
- Fix completion throw when `data` on complete item is `string`.
- Fix `commitCharacters` not works.
@ -1021,7 +1317,7 @@ @@ -1021,7 +1317,7 @@
# 2018-09-07
- **Break change**: all extension all seperated from core, checkout
- **Break change**: all extension all separated from core, checkout
[Using coc extension](https://github.com/neoclide/coc.nvim/wiki/Using-coc-extensions)
- Fix `textDocumentSync` option not work when received as object.
- Fix wrong diagnostic info when using multiple lint servers.
@ -1141,7 +1437,7 @@ @@ -1141,7 +1437,7 @@
- Add support filetype change of buffer.
- Add basic test for completion.
- Improve loading speed, use child process to initialize vim sources.
- Improve install.sh, install node when not exists.
- Improve install.sh, install node when it doesn't exist.
- Improve interface of workspace.
- Fix loading of configuration content.

8
etc/soft/nvim/+plugins/coc.nvim/jest.js

@ -2,14 +2,18 @@ const path = require('path') @@ -2,14 +2,18 @@ const path = require('path')
const os = require('os')
const fs = require('fs')
process.on('uncaughtException', function (err) {
process.on('uncaughtException', err => {
let msg = 'Uncaught exception: ' + err.stack
console.error(msg)
})
process.on('exit', () => {
fs.rmdirSync(process.env.TMPDIR, { recursive: true, force: true })
})
module.exports = async () => {
let dataHome = path.join(os.tmpdir(), `coc-test/${process.pid}`)
fs.mkdirSync(dataHome, {recursive: true})
fs.mkdirSync(dataHome, { recursive: true })
process.env.NODE_ENV = 'test'
process.env.COC_DATA_HOME = dataHome
process.env.COC_VIMCONFIG = path.join(__dirname, 'src/__tests__')

85
etc/soft/nvim/+plugins/coc.nvim/lua/coc/highlight.lua

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
local api = vim.api
local M = {}
-- Get single line extmarks
function M.getHighlights(bufnr, key, s, e)
if not api.nvim_buf_is_loaded(bufnr) then
return nil
end
s = s or 0
e = e or -1
local max = e == -1 and api.nvim_buf_line_count(bufnr) or e + 1
local ns = api.nvim_create_namespace('coc-' .. key)
local markers = api.nvim_buf_get_extmarks(bufnr, ns, {s, 0}, {e, -1}, {details = true})
local res = {}
for _, mark in ipairs(markers) do
local id = mark[1]
local line = mark[2]
local startCol = mark[3]
local details = mark[4]
local endCol = details.end_col
if line < max then
local delta = details.end_row - line
if delta <= 1 and (delta == 0 or endCol == 0) then
if startCol == endCol then
api.nvim_buf_del_extmark(bufnr, ns, id)
else
if delta == 1 then
local text = api.nvim_buf_get_lines(bufnr, line, line + 1, false)[1] or ''
endCol = #text
end
table.insert(res, {details.hl_group, line, startCol, endCol, id})
end
end
end
end
return res
end
local function addHighlights(bufnr, ns, highlights, priority)
for _, items in ipairs(highlights) do
local hlGroup = items[1]
local line = items[2]
local startCol = items[3]
local endCol = items[4]
local hlMode = items[5] and 'combine' or 'replace'
-- Error: col value outside range
pcall(api.nvim_buf_set_extmark, bufnr, ns, line, startCol, {
end_col = endCol,
hl_group = hlGroup,
hl_mode = hlMode,
right_gravity = true,
priority = type(priority) == 'number' and math.min(priority, 4096) or 4096
})
end
end
local function addHighlightTimer(bufnr, ns, highlights, priority, maxCount)
local hls = {}
local next = {}
for i, v in ipairs(highlights) do
if i < maxCount then
table.insert(hls, v)
else
table.insert(next, v)
end
end
addHighlights(bufnr, ns, hls, priority)
if #next > 0 then
vim.defer_fn(function()
addHighlightTimer(bufnr, ns, next, priority, maxCount)
end, 30)
end
end
function M.set(bufnr, ns, highlights, priority)
local maxCount = vim.g.coc_highlight_maximum_count
if #highlights > maxCount then
addHighlightTimer(bufnr, ns, highlights, priority, maxCount)
else
addHighlights(bufnr, ns, highlights, priority)
end
end
return M

48
etc/soft/nvim/+plugins/coc.nvim/package.json

@ -1,13 +1,12 @@ @@ -1,13 +1,12 @@
{
"name": "coc.nvim-master",
"version": "0.0.80",
"version": "0.0.82",
"description": "LSP based intellisense engine for neovim & vim8.",
"main": "./build/index.js",
"engines": {
"node": ">=12.12.0"
"node": ">=14.14.0"
},
"scripts": {
"clean": "rimraf lib build",
"lint": "eslint . --ext .ts --quiet",
"lint:typecheck": "tsc -p tsconfig.json",
"build": "node esbuild.js",
@ -61,35 +60,37 @@ @@ -61,35 +60,37 @@
"coverageDirectory": "./coverage/"
},
"devDependencies": {
"@swc/core": "^1.2.136",
"@swc/jest": "^0.2.17",
"@swc/core": "^1.2.233",
"@swc/jest": "^0.2.22",
"@types/bser": "^2.0.1",
"@types/cli-table": "^0.3.0",
"@types/debounce": "^3.0.0",
"@types/fb-watchman": "^2.0.0",
"@types/fs-extra": "^9.0.6",
"@types/follow-redirects": "^1.14.1",
"@types/glob": "^7.2.0",
"@types/jest": "^27.0.3",
"@types/marked": "^4.0.1",
"@types/minimatch": "^3.0.3",
"@types/mkdirp": "^1.0.1",
"@types/node": "12.12.12",
"@types/node": "14.14",
"@types/semver": "^7.3.4",
"@types/tar": "^4.0.5",
"@types/uuid": "^8.3.0",
"@types/which": "^1.3.2",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"browserslist": "^4.21.3",
"bser": "^2.1.1",
"esbuild": "^0.14.25",
"eslint": "^8.8.0",
"eslint-plugin-jest": "^26.0.0",
"eslint-plugin-jsdoc": "^37.7.0",
"esbuild": "^0.15.9",
"eslint": "^8.14.0",
"eslint-plugin-jest": "^26.1.5",
"eslint-plugin-jsdoc": "^39.2.8",
"jest": "27.4.5",
"typescript": "^4.5.4",
"vscode-languageserver": "7.0.0"
"typescript": "^4.6.3",
"vscode-languageserver": "^8.0.2"
},
"dependencies": {
"@chemzqm/neovim": "^5.7.5",
"@chemzqm/neovim": "^5.7.10",
"ansi-styles": "^5.0.0",
"bytes": "^3.1.0",
"cli-table": "^0.3.4",
@ -98,15 +99,13 @@ @@ -98,15 +99,13 @@
"decompress-response": "^6.0.0",
"fast-diff": "^1.2.0",
"fb-watchman": "^2.0.1",
"follow-redirects": "^1.14.8",
"fs-extra": "^9.0.1",
"follow-redirects": "^1.15.2",
"glob": "^7.2.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
"isuri": "^2.0.3",
"jsonc-parser": "^3.0.0",
"log4js": "^6.4.0",
"marked": "^4.0.10",
"marked": "^4.0.12",
"minimatch": "^3.0.4",
"semver": "^7.3.2",
"strip-ansi": "^6.0.0",
@ -115,11 +114,10 @@ @@ -115,11 +114,10 @@
"unidecode": "^0.1.8",
"unzip-stream": "^0.3.1",
"uuid": "^7.0.3",
"vscode-jsonrpc": "^6.0.0",
"vscode-languageserver-protocol": "^3.16.0",
"vscode-languageserver-textdocument": "^1.0.3",
"vscode-languageserver-types": "^3.16.0",
"vscode-uri": "^2.1.2",
"vscode-languageserver-protocol": "^3.17.2",
"vscode-languageserver-textdocument": "^1.0.5",
"vscode-languageserver-types": "^3.17.2",
"vscode-uri": "^3.0.3",
"which": "^2.0.2"
}
}

334
etc/soft/nvim/+plugins/coc.nvim/plugin/coc.vim

@ -7,25 +7,33 @@ function! s:checkVersion() abort @@ -7,25 +7,33 @@ function! s:checkVersion() abort
let l:unsupported = 0
if get(g:, 'coc_disable_startup_warning', 0) != 1
if has('nvim')
let l:unsupported = !has('nvim-0.3.2')
let l:unsupported = !has('nvim-0.4.0')
else
let l:unsupported = !has('patch-8.0.1453')
let l:unsupported = !has('patch-8.1.1719')
endif
if l:unsupported == 1
echohl Error
echom "coc.nvim requires at least Vim 8.0.1453 or Neovim 0.3.2, but you're using an older version."
echom "coc.nvim requires at least Vim 8.1.1719 or Neovim 0.4.0, but you're using an older version."
echom "Please upgrade your (neo)vim."
echom "You can add this to your vimrc to avoid this message:"
echom " let g:coc_disable_startup_warning = 1"
echom "Note that some features may error out or behave incorrectly."
echom "Please do not report bugs unless you're using at least Vim 8.0.1453 or Neovim 0.3.2."
echom "Please do not report bugs unless you're using at least Vim 8.1.1719 or Neovim 0.4.0."
echohl None
sleep 2
else
if !has('nvim-0.4.0') && !has('patch-8.1.1719')
if !has('nvim-0.5.0') && !has('patch-8.2.0750')
echohl WarningMsg
echom "coc.nvim works best on vim >= 8.1.1719 and neovim >= 0.4.0, consider upgrade your vim."
echom "coc.nvim works best on vim >= 8.2.0750 and neovim >= 0.5.0, consider upgrade your vim."
echom "You can add this to your vimrc to avoid this message:"
echom " let g:coc_disable_startup_warning = 1"
echom "Note that some features may behave incorrectly."
echohl None
sleep 2
elseif !has('nvim') && (!has('job') || !has('popupwin') || !has('textprop'))
echohl WarningMsg
echom "coc.nvim requires job, popupwin and textprop features of vim, consider recompile your vim."
echom "You can add this to your vimrc to avoid this message:"
echom " let g:coc_disable_startup_warning = 1"
echom "Note that some features may behave incorrectly."
@ -60,10 +68,28 @@ endfunction @@ -60,10 +68,28 @@ endfunction
function! CocPopupCallback(bufnr, arglist) abort
if len(a:arglist) == 2
if a:arglist[0] == 'confirm'
call coc#rpc#notify('PromptInsert', [a:arglist[1]])
call coc#rpc#notify('PromptInsert', [a:arglist[1], a:bufnr])
elseif a:arglist[0] == 'exit'
execute 'silent! bd! '.a:bufnr
"call coc#rpc#notify('PromptUpdate', [a:arglist[1]])
elseif a:arglist[0] == 'change'
let text = a:arglist[1]
let current = getbufvar(a:bufnr, 'current', '')
if text !=# current
call setbufvar(a:bufnr, 'current', text)
let cursor = term_getcursor(a:bufnr)
let info = {
\ 'lnum': cursor[0],
\ 'col': cursor[1],
\ 'line': text,
\ 'changedtick': 0
\ }
call coc#rpc#notify('CocAutocmd', ['TextChangedI', a:bufnr, info])
endif
elseif a:arglist[0] == 'send'
let key = a:arglist[1]
let escaped = strcharpart(key, 1, strchars(key) - 2)
call coc#rpc#notify('PromptKeyPress', [a:bufnr, escaped])
endif
endif
endfunction
@ -91,6 +117,11 @@ function! CocNotify(...) abort @@ -91,6 +117,11 @@ function! CocNotify(...) abort
return coc#rpc#request('sendNotification', a:000)
endfunction
function! CocRegisterNotification(id, method, cb) abort
call coc#on_notify(a:id, a:method, a:cb)
endfunction
" Deprecated, use CocRegisterNotification instead
function! CocRegistNotification(id, method, cb) abort
call coc#on_notify(a:id, a:method, a:cb)
endfunction
@ -157,7 +188,7 @@ function! s:OpenConfig() @@ -157,7 +188,7 @@ function! s:OpenConfig()
let home = coc#util#get_config_home()
if !isdirectory(home)
echohl MoreMsg
echom 'Config directory "'.home.'" not exists, create? (y/n)'
echom 'Config directory "'.home.'" does not exist, create? (y/n)'
echohl None
let confirm = nr2char(getchar())
redraw!
@ -213,6 +244,15 @@ function! s:AddAnsiGroups() abort @@ -213,6 +244,15 @@ function! s:AddAnsiGroups() abort
endtry
endfunction
function! s:CreateHighlight(group, fg, bg) abort
let cmd = coc#highlight#compose(a:fg, a:bg)
if !empty(trim(cmd))
exe 'hi default '.a:group.' '.cmd
else
exe 'hi default link '.a:group.' '.a:fg
endif
endfunction
function! s:CursorRangeFromSelected(type, ...) abort
" add range by operator
call coc#rpc#request('cursorsSelect', [bufnr('%'), 'operator', a:type])
@ -259,6 +299,19 @@ function! s:HandleCharInsert(char, bufnr) abort @@ -259,6 +299,19 @@ function! s:HandleCharInsert(char, bufnr) abort
call s:Autocmd('InsertCharPre', a:char, a:bufnr)
endfunction
function! s:HandleWinScrolled(winid) abort
if getwinvar(a:winid, 'float', 0)
call coc#float#nvim_scrollbar(a:winid)
endif
call s:Autocmd('WinScrolled', a:winid)
endfunction
function! s:HandleWinClosed(winid) abort
call coc#float#on_close(a:winid)
call coc#notify#on_close(a:winid)
call s:Autocmd('WinClosed', a:winid)
endfunction
function! s:SyncAutocmd(...)
if !get(g:, 'coc_workspace_initialized', 0)
return
@ -266,6 +319,14 @@ function! s:SyncAutocmd(...) @@ -266,6 +319,14 @@ function! s:SyncAutocmd(...)
call coc#rpc#request('CocAutocmd', a:000)
endfunction
function! s:CheckHighlight() abort
let fgId = synIDtrans(hlID('CocSelectedText'))
let guifg = synIDattr(fgId, 'fg', 'gui')
if empty(guifg)
call s:Highlight()
endif
endfunction
function! s:Enable(initialize)
if get(g:, 'coc_enabled', 0) == 1
return
@ -275,18 +336,16 @@ function! s:Enable(initialize) @@ -275,18 +336,16 @@ function! s:Enable(initialize)
augroup coc_nvim
autocmd!
if exists('##MenuPopupChanged') && exists('*nvim_open_win')
autocmd MenuPopupChanged * call s:Autocmd('MenuPopupChanged', get(v:, 'event', {}), win_screenpos(winnr())[0] + winline() - 2)
endif
if exists('##CompleteChanged')
autocmd CompleteChanged * call s:Autocmd('MenuPopupChanged', get(v:, 'event', {}), win_screenpos(winnr())[0] + winline() - 2)
endif
if coc#rpc#started()
autocmd VimEnter * call coc#rpc#notify('VimEnter', [])
elseif get(g:, 'coc_start_at_startup', 1)
autocmd VimEnter * call coc#rpc#start_server()
endif
if v:vim_did_enter
call s:CheckHighlight()
else
autocmd VimEnter * call timer_start(0, { -> s:CheckHighlight()})
endif
if s:is_vim
if exists('##DirChanged')
autocmd DirChanged * call s:Autocmd('DirChanged', getcwd())
@ -297,14 +356,20 @@ function! s:Enable(initialize) @@ -297,14 +356,20 @@ function! s:Enable(initialize)
else
autocmd DirChanged * call s:Autocmd('DirChanged', get(v:event, 'cwd', ''))
autocmd TermOpen * call s:Autocmd('TermOpen', +expand('<abuf>'))
autocmd CursorMoved * call coc#float#nvim_refresh_scrollbar(win_getid())
autocmd WinEnter * call coc#float#nvim_win_enter(win_getid())
if exists('##WinClosed')
autocmd WinClosed * call coc#float#close_related(+expand('<afile>'))
endif
endif
if has('nvim-0.4.0') || has('patch-8.1.1719')
autocmd CursorHold * call coc#float#check_related()
if exists('##CompleteChanged')
autocmd CompleteChanged * call coc#pum#stop()
endif
autocmd CursorMoved list:///* call coc#list#select(bufnr('%'), line('.'))
autocmd CursorHold * call coc#float#check_related()
if exists('##WinClosed')
autocmd WinClosed * call s:HandleWinClosed(+expand('<amatch>'))
elseif exists('##TabEnter')
autocmd TabEnter * call coc#notify#reflow()
endif
if exists('##WinScrolled')
autocmd WinScrolled * call s:HandleWinScrolled(+expand('<amatch>'))
endif
autocmd TabNew * call s:Autocmd('TabNew', tabpagenr())
autocmd TabClosed * call s:Autocmd('TabClosed', +expand('<afile>'))
@ -313,32 +378,32 @@ function! s:Enable(initialize) @@ -313,32 +378,32 @@ function! s:Enable(initialize)
autocmd BufWinLeave * call s:Autocmd('BufWinLeave', +expand('<abuf>'), bufwinid(+expand('<abuf>')))
autocmd BufWinEnter * call s:Autocmd('BufWinEnter', +expand('<abuf>'), win_getid())
autocmd FileType * call s:Autocmd('FileType', expand('<amatch>'), +expand('<abuf>'))
autocmd CompleteDone * call s:Autocmd('CompleteDone', get(v:, 'completed_item', {}))
autocmd InsertCharPre * call s:HandleCharInsert(v:char, bufnr('%'))
if exists('##TextChangedP')
autocmd TextChangedP * call s:Autocmd('TextChangedP', +expand('<abuf>'), {'lnum': line('.'), 'col': col('.'), 'line': getline('.'), 'changedtick': b:changedtick})
autocmd TextChangedP * call s:Autocmd('TextChangedP', +expand('<abuf>'), coc#util#change_info())
endif
autocmd TextChangedI * call s:Autocmd('TextChangedI', +expand('<abuf>'), {'lnum': line('.'), 'col': col('.'), 'pre': strpart(getline('.'), 0, col('.') - 1), 'changedtick': b:changedtick})
autocmd TextChangedI * call s:Autocmd('TextChangedI', +expand('<abuf>'), coc#util#change_info())
autocmd InsertLeave * call s:Autocmd('InsertLeave', +expand('<abuf>'))
autocmd InsertEnter * call s:Autocmd('InsertEnter', +expand('<abuf>'))
autocmd BufHidden * call s:Autocmd('BufHidden', +expand('<abuf>'))
autocmd BufEnter * call s:Autocmd('BufEnter', +expand('<abuf>'))
autocmd TextChanged * call s:Autocmd('TextChanged', +expand('<abuf>'), getbufvar(+expand('<abuf>'), 'changedtick'))
autocmd BufWritePost * call s:Autocmd('BufWritePost', +expand('<abuf>'))
autocmd BufWritePost * call s:Autocmd('BufWritePost', +expand('<abuf>'), getbufvar(+expand('<abuf>'), 'changedtick'))
autocmd CursorMoved * call s:Autocmd('CursorMoved', +expand('<abuf>'), [line('.'), col('.')])
autocmd CursorMovedI * call s:Autocmd('CursorMovedI', +expand('<abuf>'), [line('.'), col('.')])
autocmd CursorHold * call s:Autocmd('CursorHold', +expand('<abuf>'), [line('.'), col('.')])
autocmd CursorHoldI * call s:Autocmd('CursorHoldI', +expand('<abuf>'), [line('.'), col('.')])
autocmd BufNewFile,BufReadPost * call s:Autocmd('BufCreate', +expand('<abuf>'))
autocmd BufUnload * call s:Autocmd('BufUnload', +expand('<abuf>'))
autocmd BufWritePre * call s:SyncAutocmd('BufWritePre', +expand('<abuf>'))
autocmd BufWritePre * call s:SyncAutocmd('BufWritePre', +expand('<abuf>'), bufname(+expand('<abuf>')), getbufvar(+expand('<abuf>'), 'changedtick'))
autocmd FocusGained * if mode() !~# '^c' | call s:Autocmd('FocusGained') | endif
autocmd FocusLost * call s:Autocmd('FocusLost')
autocmd VimResized * call s:Autocmd('VimResized', &columns, &lines)
autocmd VimLeavePre * let g:coc_vim_leaving = 1
autocmd VimLeavePre * call s:Autocmd('VimLeavePre')
autocmd BufReadCmd,FileReadCmd,SourceCmd list://* call coc#list#setup(expand('<amatch>'))
autocmd BufWriteCmd __coc_refactor__* :call coc#rpc#notify('saveRefactor', [+expand('<abuf>')])
autocmd ColorScheme * call s:Hi()
autocmd ColorScheme * call s:Highlight()
augroup end
if a:initialize == 0
call coc#rpc#request('attach', [])
@ -348,44 +413,53 @@ function! s:Enable(initialize) @@ -348,44 +413,53 @@ function! s:Enable(initialize)
endif
endfunction
function! s:Hi() abort
hi default CocErrorSign ctermfg=Red guifg=#ff0000 guibg=NONE
hi default CocWarningSign ctermfg=Brown guifg=#ff922b guibg=NONE
hi default CocInfoSign ctermfg=Yellow guifg=#fab005 guibg=NONE
hi default CocHintSign ctermfg=Blue guifg=#15aabf guibg=NONE
function! s:Highlight() abort
hi default CocSelectedText ctermfg=Red guifg=#fb4934 guibg=NONE
hi default CocCodeLens ctermfg=Gray guifg=#999999 guibg=NONE
hi default CocUnderline term=underline cterm=underline gui=underline
hi default CocUnderline term=underline cterm=underline gui=underline guisp=#ebdbb2
hi default CocBold term=bold cterm=bold gui=bold
hi default CocItalic term=italic cterm=italic gui=italic
if s:is_vim || has('nvim-0.4.0')
hi default CocStrikeThrough term=strikethrough cterm=strikethrough gui=strikethrough
hi default CocStrikeThrough term=strikethrough cterm=strikethrough gui=strikethrough
hi default CocMarkdownLink ctermfg=Blue guifg=#15aabf guibg=NONE
hi default CocDisabled guifg=#999999 ctermfg=gray
hi default CocSearch ctermfg=Blue guifg=#15aabf guibg=NONE
if coc#highlight#get_contrast('Normal', has('nvim') ? 'NormalFloat' : 'Pmenu') > 2.0
exe 'hi default CocFloating '.coc#highlight#create_bg_command('Normal', &background ==# 'dark' ? -0.4 : 0.1)
exe 'hi default CocMenuSel '.coc#highlight#create_bg_command('CocFloating', &background ==# 'dark' ? -0.2 : 0.05)
exe 'hi default CocFloatThumb '.coc#highlight#create_bg_command('CocFloating', &background ==# 'dark' ? -0.3 : 0.2)
exe 'hi default CocFloatSbar '.coc#highlight#create_bg_command('CocFloatThumb', &background ==# 'dark' ? -0.5 : 0.3)
else
hi default CocStrikeThrough guifg=#989898 ctermfg=gray
exe 'hi default link CocFloating '.(has('nvim') ? 'NormalFloat' : 'Pmenu')
if coc#highlight#get_contrast('CocFloating', 'PmenuSel') > 2.0
exe 'hi default CocMenuSel '.coc#highlight#create_bg_command('CocFloating', &background ==# 'dark' ? -0.2 : 0.05)
else
exe 'hi default CocMenuSel '.coc#highlight#get_hl_command(synIDtrans(hlID('PmenuSel')), 'bg', '237', '#13354A')
endif
hi default link CocFloatThumb PmenuThumb
hi default link CocFloatSbar PmenuSbar
endif
hi default CocMarkdownLink ctermfg=Blue guifg=#15aabf guibg=NONE
hi default CocDisabled guifg=#999999 ctermfg=gray
hi default link CocFloatActive CocSearch
hi default link CocFadeOut Conceal
hi default link CocMarkdownCode markdownCode
hi default link CocMarkdownHeader markdownH1
hi default link CocMenuSel PmenuSel
hi default link CocErrorFloat CocErrorSign
hi default link CocWarningFloat CocWarningSign
hi default link CocInfoFloat CocInfoSign
hi default link CocHintFloat CocHintSign
hi default link CocErrorHighlight CocUnderline
hi default link CocWarningHighlight CocUnderline
hi default link CocInfoHighlight CocUnderline
hi default link CocHintHighlight CocUnderline
hi default link CocDeprecatedHighlight CocStrikeThrough
hi default link CocUnusedHighlight CocFadeOut
hi default link CocListLine CursorLine
hi default link CocListSearch CocSearch
hi default link CocListMode ModeMsg
hi default link CocListPath Comment
hi default link CocHighlightText CursorColumn
hi default link CocHoverRange Search
hi default link CocCursorRange Search
hi default link CocLinkedEditing CocCursorRange
hi default link CocHighlightRead CocHighlightText
hi default link CocHighlightWrite CocHighlightText
" Notification
hi default CocNotificationProgress ctermfg=Blue guifg=#15aabf guibg=NONE
hi default link CocNotificationButton CocUnderline
hi default link CocNotificationError CocErrorFloat
hi default link CocNotificationWarning CocWarningFloat
hi default link CocNotificationInfo CocInfoFloat
" Snippet
hi default link CocSnippetVisual Visual
" Tree view highlights
@ -396,41 +470,21 @@ function! s:Hi() abort @@ -396,41 +470,21 @@ function! s:Hi() abort
hi default link CocSelectedRange CocHighlightText
" Symbol highlights
hi default link CocSymbolDefault MoreMsg
hi default link CocSymbolFile Statement
hi default link CocSymbolModule Statement
hi default link CocSymbolNamespace Statement
hi default link CocSymbolPackage Statement
hi default link CocSymbolClass Statement
hi default link CocSymbolMethod Function
hi default link CocSymbolProperty Keyword
hi default link CocSymbolField CocSymbolDefault
hi default link CocSymbolConstructor Function
hi default link CocSymbolEnum CocSymbolDefault
hi default link CocSymbolInterface CocSymbolDefault
hi default link CocSymbolFunction Function
hi default link CocSymbolVariable CocSymbolDefault
hi default link CocSymbolConstant Constant
hi default link CocSymbolString String
hi default link CocSymbolNumber Number
hi default link CocSymbolBoolean Boolean
hi default link CocSymbolArray CocSymbolDefault
hi default link CocSymbolObject CocSymbolDefault
hi default link CocSymbolKey Keyword
hi default link CocSymbolNull Type
hi default link CocSymbolEnumMember CocSymbolDefault
hi default link CocSymbolStruct Keyword
hi default link CocSymbolEvent Keyword
hi default link CocSymbolOperator Operator
hi default link CocSymbolTypeParameter Operator
if has('nvim')
hi default link CocFloating NormalFloat
else
hi default link CocFloating Pmenu
endif
"Pum
hi default link CocPumSearch CocSearch
hi default link CocPumDetail Comment
hi default link CocPumMenu CocFloating
hi default link CocPumShortcut Comment
hi default link CocPumDeprecated CocStrikeThrough
hi default CocPumVirtualText ctermfg=12 guifg=#504945
hi default CocFloatDividingLine ctermfg=12 guifg=#504945
if !exists('*sign_getdefined') || empty(sign_getdefined('CocCurrentLine'))
sign define CocCurrentLine linehl=CocMenuSel
endif
if !exists('*sign_getdefined') || empty(sign_getdefined('CocListCurrent'))
sign define CocListCurrent linehl=CocListLine
endif
if !exists('*sign_getdefined') || empty(sign_getdefined('CocTreeSelected'))
sign define CocTreeSelected linehl=CocTreeSelected
endif
@ -438,14 +492,41 @@ function! s:Hi() abort @@ -438,14 +492,41 @@ function! s:Hi() abort
hi default CocCursorTransparent gui=strikethrough blend=100
endif
if has('nvim')
let names = ['Error', 'Warning', 'Info', 'Hint']
for name in names
if !hlexists('Coc'.name.'VirtualText')
exe 'hi default link Coc'.name.'VirtualText Coc'.name.'Sign'
endif
endfor
endif
let sign_colors = {
\ 'Error': ['Red', '#ff0000'],
\ 'Warn': ['Brown', '#ff922b'],
\ 'Info': ['Yellow', '#fab005'],
\ 'Hint': ['Blue', '#15aabf']
\ }
for name in ['Error', 'Warning', 'Info', 'Hint']
let suffix = name ==# 'Warning' ? 'Warn' : name
if hlexists('DiagnosticUnderline'.suffix)
exe 'hi default link Coc'.name.'Highlight DiagnosticUnderline'.suffix
else
exe 'hi default link Coc'.name.'Highlight CocUnderline'
endif
if hlexists('DiagnosticSign'.suffix)
exe 'hi default link Coc'.name.'Sign DiagnosticSign'.suffix
else
exe 'hi default Coc'.name.'Sign ctermfg='.sign_colors[suffix][0].' guifg='.sign_colors[suffix][1]
endif
if hlexists('DiagnosticVirtualText'.suffix)
exe 'hi default link Coc'.name.'VirtualText DiagnosticVirtualText'.suffix
else
call s:CreateHighlight('Coc'.name.'VirtualText', 'Coc'.name.'Sign', 'Normal')
endif
if hlexists('Diagnostic'.suffix)
exe 'hi default link Coc'.name.'Float Diagnostic'.suffix
else
call s:CreateHighlight('Coc'.name.'Float', 'Coc'.name.'Sign', 'CocFloating')
endif
endfor
call s:CreateHighlight('CocInlayHint', 'CocHintSign', 'SignColumn')
for name in ['Parameter', 'Type']
exe 'hi default link CocInlayHint'.name.' CocInlayHint'
endfor
call s:AddAnsiGroups()
if get(g:, 'coc_default_semantic_highlight_groups', 1)
@ -482,6 +563,48 @@ function! s:Hi() abort @@ -482,6 +563,48 @@ function! s:Hi() abort
execute 'hi default link CocSem'.key.' '.(hlexists(ts) ? ts : fallback)
endfor
endif
let symbolMap = {
\ 'Keyword': ['TSKeyword', 'Keyword'],
\ 'Namespace': ['TSNamespace', 'Include'],
\ 'Class': ['TSConstructor', 'Special'],
\ 'Method': ['TSMethod', 'Function'],
\ 'Property': ['TSProperty', 'Identifier'],
\ 'Text': ['TSText', 'CocSymbolDefault'],
\ 'Unit': ['TSUnit', 'CocSymbolDefault'],
\ 'Value': ['TSValue', 'CocSymbolDefault'],
\ 'Snippet': ['TSSnippet', 'CocSymbolDefault'],
\ 'Color': ['TSColor', 'Float'],
\ 'Reference': ['TSTextReference', 'Constant'],
\ 'Folder': ['TSFolder', 'CocSymbolDefault'],
\ 'File': ['TSFile', 'Statement'],
\ 'Module': ['TSModule', 'Statement'],
\ 'Package': ['TSPackage', 'Statement'],
\ 'Field': ['TSField', 'Identifier'],
\ 'Constructor': ['TSConstructor', 'Special'],
\ 'Enum': ['TSEnum', 'CocSymbolDefault'],
\ 'Interface': ['TSInterface', 'CocSymbolDefault'],
\ 'Function': ['TSFunction', 'Function'],
\ 'Variable': ['TSVariableBuiltin', 'Special'],
\ 'Constant': ['TSConstant', 'Constant'],
\ 'String': ['TSString', 'String'],
\ 'Number': ['TSNumber', 'Number'],
\ 'Boolean': ['TSBoolean', 'Boolean'],
\ 'Array': ['TSArray', 'CocSymbolDefault'],
\ 'Object': ['TSObject', 'CocSymbolDefault'],
\ 'Key': ['TSKey', 'Identifier'],
\ 'Null': ['TSNull', 'Type'],
\ 'EnumMember': ['TSEnumMember', 'Identifier'],
\ 'Struct': ['TSStruct', 'Keyword'],
\ 'Event': ['TSEvent', 'Constant'],
\ 'Operator': ['TSOperator', 'Operator'],
\ 'TypeParameter': ['TSParameter', 'Identifier'],
\ }
for [key, value] in items(symbolMap)
let hlGroup = hlexists(value[0]) ? value[0] : get(value, 1, 'CocSymbolDefault')
if hlexists(hlGroup)
execute 'hi default CocSymbol'.key.' '.coc#highlight#get_hl_command(synIDtrans(hlID(hlGroup)), 'fg', '223', '#ebdbb2')
endif
endfor
endfunction
function! s:FormatFromSelected(type)
@ -504,8 +627,8 @@ function! s:ShowInfo() @@ -504,8 +627,8 @@ function! s:ShowInfo()
else
let output = trim(system(node . ' --version'))
let ms = matchlist(output, 'v\(\d\+\).\(\d\+\).\(\d\+\)')
if empty(ms) || str2nr(ms[1]) < 12 || (str2nr(ms[1]) == 12 && str2nr(ms[2]) < 12)
call add(lines, 'Error: Node version '.output.' < 12.12.0, please upgrade node.js')
if empty(ms) || str2nr(ms[1]) < 14 || (str2nr(ms[1]) == 14 && str2nr(ms[2]) < 14)
call add(lines, 'Error: Node version '.output.' < 14.14.0, please upgrade node.js')
endif
endif
" check bundle
@ -549,14 +672,38 @@ command! -nargs=? -complete=custom,coc#list#names CocPrev :call coc#rpc# @@ -549,14 +672,38 @@ command! -nargs=? -complete=custom,coc#list#names CocPrev :call coc#rpc#
command! -nargs=? -complete=custom,coc#list#names CocNext :call coc#rpc#notify('listNext', [<f-args>])
command! -nargs=? -complete=custom,coc#list#names CocFirst :call coc#rpc#notify('listFirst', [<f-args>])
command! -nargs=? -complete=custom,coc#list#names CocLast :call coc#rpc#notify('listLast', [<f-args>])
command! -nargs=* -range CocAction :call coc#rpc#notify('codeActionRange', [<line1>, <line2>, <f-args>])
command! -nargs=* -range CocFix :call coc#rpc#notify('codeActionRange', [<line1>, <line2>, 'quickfix'])
command! -nargs=0 CocUpdate :call coc#util#update_extensions(1)
command! -nargs=0 -bar CocUpdateSync :call coc#util#update_extensions()
command! -nargs=* -bar -complete=custom,s:InstallOptions CocInstall :call coc#util#install_extension([<f-args>])
call s:Highlight()
call s:Enable(1)
call s:Hi()
" Default key-mappings for completion
if empty(mapcheck('<C-n>', 'i'))
inoremap <silent><expr> <C-n> coc#pum#visible() ? coc#pum#next(1) : "\<C-n>"
endif
if empty(mapcheck('<C-p>', 'i'))
inoremap <silent><expr> <C-p> coc#pum#visible() ? coc#pum#prev(1) : "\<C-p>"
endif
if empty(mapcheck('<down>', 'i'))
inoremap <silent><expr> <down> coc#pum#visible() ? coc#pum#next(0) : "\<down>"
endif
if empty(mapcheck('<up>', 'i'))
inoremap <silent><expr> <up> coc#pum#visible() ? coc#pum#prev(0) : "\<up>"
endif
if empty(mapcheck('<C-e>', 'i'))
inoremap <silent><expr> <C-e> coc#pum#visible() ? coc#pum#cancel() : "\<C-e>"
endif
if empty(mapcheck('<C-y>', 'i'))
inoremap <silent><expr> <C-y> coc#pum#visible() ? coc#pum#confirm() : "\<C-y>"
endif
if empty(mapcheck('<PageDown>', 'i'))
inoremap <silent><expr> <PageDown> coc#pum#visible() ? coc#pum#scroll(1) : "\<PageDown>"
endif
if empty(mapcheck('<PageUp>', 'i'))
inoremap <silent><expr> <PageUp> coc#pum#visible() ? coc#pum#scroll(0) : "\<PageUp>"
endif
vnoremap <silent> <Plug>(coc-range-select) :<C-u>call CocActionAsync('rangeSelect', visualmode(), v:true)<CR>
vnoremap <silent> <Plug>(coc-range-select-backward) :<C-u>call CocActionAsync('rangeSelect', visualmode(), v:false)<CR>
@ -566,7 +713,7 @@ vnoremap <silent> <Plug>(coc-format-selected) :<C-u>call CocActionAs @@ -566,7 +713,7 @@ vnoremap <silent> <Plug>(coc-format-selected) :<C-u>call CocActionAs
vnoremap <silent> <Plug>(coc-codeaction-selected) :<C-u>call CocActionAsync('codeAction', visualmode())<CR>
nnoremap <Plug>(coc-codeaction-selected) :<C-u>set operatorfunc=<SID>CodeActionFromSelected<CR>g@
nnoremap <Plug>(coc-codeaction) :<C-u>call CocActionAsync('codeAction', '')<CR>
nnoremap <Plug>(coc-codeaction-line) :<C-u>call CocActionAsync('codeAction', 'line')<CR>
nnoremap <Plug>(coc-codeaction-line) :<C-u>call CocActionAsync('codeAction', 'currline')<CR>
nnoremap <Plug>(coc-codeaction-cursor) :<C-u>call CocActionAsync('codeAction', 'cursor')<CR>
nnoremap <silent> <Plug>(coc-rename) :<C-u>call CocActionAsync('rename')<CR>
nnoremap <silent> <Plug>(coc-format-selected) :<C-u>set operatorfunc=<SID>FormatFromSelected<CR>g@
@ -588,7 +735,6 @@ nnoremap <silent> <Plug>(coc-float-hide) :<C-u>call coc#float#c @@ -588,7 +735,6 @@ nnoremap <silent> <Plug>(coc-float-hide) :<C-u>call coc#float#c
nnoremap <silent> <Plug>(coc-float-jump) :<c-u>call coc#float#jump()<cr>
nnoremap <silent> <Plug>(coc-command-repeat) :<C-u>call CocAction('repeatCommand')<CR>
nnoremap <silent> <Plug>(coc-refactor) :<C-u>call CocActionAsync('refactor')<CR>
inoremap <silent> <Plug>CocRefresh <C-r>=coc#_complete()<CR>
nnoremap <silent> <Plug>(coc-cursors-operator) :<C-u>set operatorfunc=<SID>CursorRangeFromSelected<CR>g@
vnoremap <silent> <Plug>(coc-cursors-range) :<C-u>call CocAction('cursorsSelect', bufnr('%'), 'range', visualmode())<CR>

17
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/autoload/coc/source/email.vim

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
" vim source for emails
function! coc#source#email#init() abort
return {
\ 'priority': 9,
\ 'shortcut': 'Email',
\ 'triggerCharacters': ['@']
\}
endfunction
function! coc#source#email#should_complete(opt) abort
return 1
endfunction
function! coc#source#email#complete(opt, cb) abort
let items = ['foo@gmail.com', 'bar@yahoo.com']
call a:cb(items)
endfunction

177
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/configuration.test.ts

@ -0,0 +1,177 @@ @@ -0,0 +1,177 @@
import path from 'path'
import { DidChangeConfigurationNotification, DocumentSelector } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import { SyncConfigurationFeature } from '../../language-client/configuration'
import { LanguageClient, LanguageClientOptions, Middleware, ServerOptions, TransportKind } from '../../language-client/index'
import workspace from '../../workspace'
import helper from '../helper'
function createClient(section: string | string[] | undefined, middleware: Middleware = {}, opts: Partial<LanguageClientOptions> = {}): LanguageClient {
const serverModule = path.join(__dirname, './server/configServer.js')
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6014'] } }
}
const documentSelector: DocumentSelector = [{ scheme: 'file' }]
const clientOptions: LanguageClientOptions = Object.assign({
documentSelector,
synchronize: {
configurationSection: section
},
initializationOptions: {},
middleware
}, opts)
const result = new LanguageClient('test', 'Test Language Server', serverOptions, clientOptions)
return result
}
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
describe('pull configuration feature', () => {
let client: LanguageClient
beforeAll(async () => {
client = createClient(undefined)
await client.start()
})
afterAll(async () => {
await client.stop()
})
it('should request all configuration', async () => {
let config: any
client.middleware.workspace = client.middleware.workspace ?? {}
client.middleware.workspace.configuration = (params, token, next) => {
config = next(params, token)
return config
}
await client.sendNotification('pull0')
await helper.waitValue(() => {
return config != null
}, true)
expect(config[0].http).toBeDefined()
})
it('should request configurations with sections', async () => {
let config: any
client.middleware.workspace = client.middleware.workspace ?? {}
client.middleware.workspace.configuration = (params, token, next) => {
config = next(params, token)
return config
}
await client.sendNotification('pull1')
await helper.waitValue(() => {
return config?.length
}, 3)
expect(config[1]).toBeNull()
expect(config[0].proxy).toBeDefined()
expect(config[2]).toBeNull()
})
})
describe('publish configuration feature', () => {
it('should send configuration for languageserver', async () => {
let client: LanguageClient
client = createClient('languageserver.cpp.settings')
let changed
client.onNotification('configurationChange', params => {
changed = params
})
await client.start()
await helper.waitValue(() => {
return changed != null
}, true)
expect(changed).toEqual({ settings: {} })
await client.stop()
})
it('should get configuration from workspace folder', async () => {
let folder = path.resolve(__dirname, '../sample')
workspace.workspaceFolderControl.addWorkspaceFolder(folder, false)
let configFilePath = path.join(folder, '.vim/coc-settings.json')
workspace.configurations.addFolderFile(configFilePath, false, folder)
let client = createClient('coc.preferences', {}, {
workspaceFolder: { name: 'sample', uri: URI.file(folder).toString() }
})
let changed
client.onNotification('configurationChange', params => {
changed = params
})
await client.start()
await helper.waitValue(() => {
return changed != null
}, true)
expect(changed.settings.coc.preferences.rootPath).toBe('./src')
workspace.workspaceFolderControl.removeWorkspaceFolder(folder)
let feature = client.getFeature(DidChangeConfigurationNotification.method)
feature.dispose()
await client.stop()
})
it('should send configuration for specific sections', async () => {
let client: LanguageClient
let called = false
client = createClient(['coc.preferences', 'npm', 'unknown'], {
workspace: {
didChangeConfiguration: (sections, next) => {
called = true
return next(sections)
}
}
})
let changed
client.onNotification('configurationChange', params => {
changed = params
})
await client.start()
await helper.waitValue(() => {
return called
}, true)
await helper.waitValue(() => {
return changed != null
}, true)
expect(changed.settings.coc).toBeDefined()
expect(changed.settings.npm).toBeDefined()
let { configurations } = workspace
configurations.updateMemoryConfig({ 'npm.binPath': 'cnpm' })
await helper.waitValue(() => {
return changed.settings.npm?.binPath
}, 'cnpm')
await client.stop()
})
it('should catch reject error', async () => {
let client: LanguageClient
let called = false
client = createClient(['cpp'], {
workspace: {
didChangeConfiguration: () => {
return Promise.reject(new Error('custom error'))
}
}
})
let changed
client.onNotification('configurationChange', params => {
changed = params
})
await client.start()
await helper.wait(50)
expect(called).toBe(false)
void client.stop()
await client.stop()
})
it('should extractSettingsInformation', async () => {
let res = SyncConfigurationFeature.extractSettingsInformation(['http.proxy', 'http.proxyCA'])
expect(res.http).toBeDefined()
expect(res.http.proxy).toBeDefined()
})
})

139
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/connection.test.ts

@ -0,0 +1,139 @@ @@ -0,0 +1,139 @@
import { Duplex } from 'stream'
import { createProtocolConnection, ProgressType, DocumentSymbolParams, DocumentSymbolRequest, InitializeParams, InitializeRequest, InitializeResult, ProtocolConnection, StreamMessageReader, StreamMessageWriter } from 'vscode-languageserver-protocol/node'
import { SymbolInformation, SymbolKind } from 'vscode-languageserver-types'
import { NullLogger } from '../../language-client/client'
class TestStream extends Duplex {
public _write(chunk: string, _encoding: string, done: () => void): void {
this.emit('data', chunk)
done()
}
public _read(_size: number): void {
}
}
let serverConnection: ProtocolConnection
let clientConnection: ProtocolConnection
let progressType: ProgressType<any> = new ProgressType()
beforeEach(() => {
const up = new TestStream()
const down = new TestStream()
const logger = new NullLogger()
serverConnection = createProtocolConnection(new StreamMessageReader(up), new StreamMessageWriter(down), logger)
clientConnection = createProtocolConnection(new StreamMessageReader(down), new StreamMessageWriter(up), logger)
serverConnection.listen()
clientConnection.listen()
})
afterEach(() => {
serverConnection.dispose()
clientConnection.dispose()
})
describe('Connection Tests', () => {
it('should ensure proper param passing', async () => {
let paramsCorrect = false
serverConnection.onRequest(InitializeRequest.type, params => {
paramsCorrect = !Array.isArray(params)
let result: InitializeResult = {
capabilities: {
}
}
return result
})
const init: InitializeParams = {
rootUri: 'file:///home/dirkb',
processId: 1,
capabilities: {},
workspaceFolders: null,
}
await clientConnection.sendRequest(InitializeRequest.type, init)
expect(paramsCorrect).toBe(true)
})
it('should provide token', async () => {
serverConnection.onRequest(DocumentSymbolRequest.type, params => {
expect(params.partialResultToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
return []
})
const params: DocumentSymbolParams = {
textDocument: { uri: 'file:///abc.txt' },
partialResultToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2'
}
await clientConnection.sendRequest(DocumentSymbolRequest.type, params)
})
it('should report result', async () => {
let result: SymbolInformation = {
name: 'abc',
kind: SymbolKind.Class,
location: {
uri: 'file:///abc.txt',
range: { start: { line: 0, character: 1 }, end: { line: 2, character: 3 } }
}
}
serverConnection.onRequest(DocumentSymbolRequest.type, async params => {
expect(params.partialResultToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
await serverConnection.sendProgress(progressType, params.partialResultToken, [result])
return []
})
const params: DocumentSymbolParams = {
textDocument: { uri: 'file:///abc.txt' },
partialResultToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2'
}
let progressOK = false
clientConnection.onProgress(progressType, '3b1db4c9-e011-489e-a9d1-0653e64707c2', values => {
progressOK = (values !== undefined && values.length === 1)
})
await clientConnection.sendRequest(DocumentSymbolRequest.type, params)
expect(progressOK).toBeTruthy()
})
it('should provide workDoneToken', async () => {
serverConnection.onRequest(DocumentSymbolRequest.type, params => {
expect(params.workDoneToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
return []
})
const params: DocumentSymbolParams = {
textDocument: { uri: 'file:///abc.txt' },
workDoneToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2'
}
await clientConnection.sendRequest(DocumentSymbolRequest.type, params)
})
it('should report work done progress', async () => {
serverConnection.onRequest(DocumentSymbolRequest.type, async params => {
expect(params.workDoneToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
await serverConnection.sendProgress(progressType, params.workDoneToken, {
kind: 'begin',
title: 'progress'
})
await serverConnection.sendProgress(progressType, params.workDoneToken, {
kind: 'report',
message: 'message'
})
await serverConnection.sendProgress(progressType, params.workDoneToken, {
kind: 'end',
message: 'message'
})
return []
})
const params: DocumentSymbolParams = {
textDocument: { uri: 'file:///abc.txt' },
workDoneToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2'
}
let result = ''
clientConnection.onProgress(progressType, '3b1db4c9-e011-489e-a9d1-0653e64707c2', value => {
result += value.kind
})
await clientConnection.sendRequest(DocumentSymbolRequest.type, params)
expect(result).toBe('beginreportend')
})
})

99
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/converter.test.ts

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
import { CompletionTriggerKind, Position, TextDocumentItem, TextDocumentSaveReason } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { URI } from 'vscode-uri'
import * as cv from '../../language-client/utils/converter'
describe('converter', () => {
function createDocument(): TextDocument {
return TextDocument.create('file:///1', 'css', 1, '')
}
it('should convertToTextDocumentItem', () => {
let doc = createDocument()
expect(cv.convertToTextDocumentItem(doc).uri).toBe(doc.uri)
expect(TextDocumentItem.is(cv.convertToTextDocumentItem(doc))).toBe(true)
})
it('should asCloseTextDocumentParams', () => {
let doc = createDocument()
expect(cv.asCloseTextDocumentParams(doc).textDocument.uri).toBe(doc.uri)
})
it('should asRelativePattern', async () => {
let rp = cv.asRelativePattern({
baseUri: 'file:///a',
pattern: '**/*'
})
expect(rp.baseUri.fsPath).toBe('/a')
rp = cv.asRelativePattern({
baseUri: { uri: 'file:///a', name: 'name' },
pattern: '**/*'
})
expect(rp.baseUri.fsPath).toBe('/a')
})
it('should asChangeTextDocumentParams', () => {
let doc = createDocument()
expect(cv.asFullChangeTextDocumentParams(doc).textDocument.uri).toBe(doc.uri)
})
it('should asWillSaveTextDocumentParams', () => {
let res = cv.asWillSaveTextDocumentParams({ document: createDocument(), reason: TextDocumentSaveReason.Manual, waitUntil: () => {} })
expect(res.textDocument).toBeDefined()
expect(res.reason).toBeDefined()
})
it('should asVersionedTextDocumentIdentifier', () => {
let res = cv.asVersionedTextDocumentIdentifier(createDocument())
expect(res.uri).toBeDefined()
expect(res.version).toBeDefined()
})
it('should asSaveTextDocumentParams', () => {
let res = cv.asSaveTextDocumentParams(createDocument(), true)
expect(res.textDocument.uri).toBeDefined()
expect(res.text).toBeDefined()
res = cv.asSaveTextDocumentParams(createDocument(), false)
expect(res.text).toBeUndefined()
})
it('should asUri', () => {
let uri = URI.file('/tmp/a')
expect(cv.asUri(uri)).toBe(uri.toString())
})
it('should asCompletionParams', () => {
let params = cv.asCompletionParams(createDocument(), Position.create(0, 0), { triggerKind: CompletionTriggerKind.Invoked })
expect(params.textDocument).toBeDefined()
expect(params.position).toBeDefined()
expect(params.context).toBeDefined()
})
it('should asTextDocumentPositionParams', () => {
let params = cv.asTextDocumentPositionParams(createDocument(), Position.create(0, 0))
expect(params.textDocument).toBeDefined()
expect(params.position).toBeDefined()
})
it('should asTextDocumentIdentifier', () => {
let doc = cv.asTextDocumentIdentifier(createDocument())
expect(doc.uri).toBeDefined()
})
it('should asReferenceParams', () => {
let params = cv.asReferenceParams(createDocument(), Position.create(0, 0), { includeDeclaration: false })
expect(params.textDocument.uri).toBeDefined()
expect(params.position).toBeDefined()
})
it('should asDocumentSymbolParams', () => {
let doc = cv.asDocumentSymbolParams(createDocument())
expect(doc.textDocument.uri).toBeDefined()
})
it('should asCodeLensParams', () => {
let doc = cv.asCodeLensParams(createDocument())
expect(doc.textDocument.uri).toBeDefined()
})
})

334
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/diagnostics.test.ts

@ -0,0 +1,334 @@ @@ -0,0 +1,334 @@
import { Neovim } from '@chemzqm/neovim'
import os from 'os'
import path from 'path'
import { v4 as uuid } from 'uuid'
import { CancellationToken, DocumentDiagnosticRequest, Position, TextEdit } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { URI } from 'vscode-uri'
import * as lsclient from '../../language-client'
import { BackgroundScheduler, DocumentPullStateTracker, PullState } from '../../language-client/diagnostic'
import workspace from '../../workspace'
import helper from '../helper'
function getId(uri: string): number {
let ms = uri.match(/\d+$/)
return ms ? Number(ms[0]) : undefined
}
function createDocument(id: number, version = 1): TextDocument {
let uri = `file:///${id}`
return TextDocument.create(uri, '', version, '')
}
function createUri(id: number): URI {
return URI.file(id.toString())
}
describe('BackgroundScheduler', () => {
it('should schedule documents by add', async () => {
let uris: string[] = []
let s = new BackgroundScheduler({
pull(document) {
uris.push(document.uri)
}
})
s.add(createDocument(1))
s.add(createDocument(1))
s.add(createDocument(2))
s.add(createDocument(3))
await helper.waitValue(() => {
return uris.length
}, 3)
let ids = uris.map(u => getId(u))
expect(ids).toEqual([1, 2, 3])
})
it('should schedule documents by remove', async () => {
let uris: string[] = []
let s = new BackgroundScheduler({
pull(document) {
uris.push(document.uri)
}
})
s.add(createDocument(1))
s.add(createDocument(2))
s.remove(createDocument(2))
s.add(createDocument(3))
s.remove(createDocument(3))
s.remove(createDocument(1))
await helper.waitValue(() => {
return uris.length
}, 3)
let ids = uris.map(u => getId(u))
expect(ids).toEqual([2, 3, 1])
s.dispose()
})
})
describe('DocumentPullStateTracker', () => {
it('should track document', async () => {
let tracker = new DocumentPullStateTracker()
let state = tracker.track(PullState.document, createDocument(1))
let other = tracker.track(PullState.document, createDocument(1))
expect(state).toBe(other)
tracker.track(PullState.workspace, createDocument(3))
let id = 'dcf06d3b-79f6-4a5e-bc8d-d3334f7b4cad'
tracker.update(PullState.document, createDocument(1, 2), id)
tracker.update(PullState.document, createDocument(2, 2), 'f758ae47-c94e-406e-ba41-0f3bb2fe4fc7')
let curr = tracker.getResultId(PullState.document, createDocument(1, 2))
expect(curr).toBe(id)
expect(tracker.getResultId(PullState.workspace, createDocument(1, 2))).toBeUndefined()
tracker.unTrack(PullState.document, createDocument(2, 2))
expect(tracker.trackingDocuments()).toEqual(['file:///1'])
tracker.update(PullState.workspace, createDocument(3, 2), 'fcb905e2-8edb-4239-9150-198c8175ed4a')
tracker.update(PullState.workspace, createDocument(1, 2), 'fe96d175-c19f-4705-bff1-101bf83b2953')
expect(tracker.tracks(PullState.workspace, createDocument(3, 1))).toBe(true)
expect(tracker.tracks(PullState.document, createDocument(4, 1))).toBe(false)
let res = tracker.getAllResultIds()
expect(res.length).toBe(2)
})
it('should track URI', async () => {
let tracker = new DocumentPullStateTracker()
let state = tracker.track(PullState.document, createUri(1), undefined)
let other = tracker.track(PullState.document, createUri(1), undefined)
expect(state).toBe(other)
tracker.track(PullState.workspace, createUri(3), undefined)
let id = 'dcf06d3b-79f6-4a5e-bc8d-d3334f7b4cad'
tracker.update(PullState.document, createUri(1), undefined, id)
tracker.update(PullState.document, createUri(2), undefined, 'f758ae47-c94e-406e-ba41-0f3bb2fe4fc7')
let curr = tracker.getResultId(PullState.document, createUri(1))
expect(curr).toBe(id)
tracker.unTrack(PullState.document, createUri(2))
expect(tracker.trackingDocuments()).toEqual(['file:///1'])
tracker.update(PullState.workspace, createUri(3), undefined, undefined)
tracker.update(PullState.workspace, createUri(1), undefined, 'fe96d175-c19f-4705-bff1-101bf83b2953')
expect(tracker.tracks(PullState.workspace, createUri(3))).toBe(true)
expect(tracker.tracks(PullState.document, createUri(4))).toBe(false)
let res = tracker.getAllResultIds()
expect(res.length).toBe(1)
})
})
describe('DiagnosticFeature', () => {
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = workspace.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
async function createServer(interFileDependencies: boolean, workspaceDiagnostics = false, middleware: lsclient.Middleware = {}, fun?: (opt: lsclient.LanguageClientOptions) => void) {
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: [{ language: '*' }],
middleware,
initializationOptions: {
interFileDependencies: interFileDependencies == true,
workspaceDiagnostics
}
}
if (fun) fun(clientOptions)
let serverModule = path.join(__dirname, './server/diagnosticServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
function getUri(s: number | string): string {
let fsPath = path.join(os.tmpdir(), s.toString())
return URI.file(fsPath).toString()
}
it('should work when change visible editor', async () => {
let doc = await workspace.loadFile(getUri(1), 'edit')
await workspace.loadFile(getUri(3), 'tabe')
let client = await createServer(true)
await helper.wait(30)
await workspace.loadFile(getUri(2), 'edit')
await helper.wait(30)
await workspace.loadFile(getUri(3), 'tabe')
await helper.wait(30)
let feature = client.getFeature(DocumentDiagnosticRequest.method)
expect(feature).toBeDefined()
let provider = feature.getProvider(doc.textDocument)
let res = provider.knows(PullState.document, doc.textDocument)
expect(res).toBe(false)
await client.stop()
})
it('should filter by document selector', async () => {
let client = await createServer(true, false, {}, opt => {
opt.documentSelector = [{ language: 'vim' }]
})
let doc = await workspace.loadFile(getUri(1), 'edit')
await helper.wait(10)
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let provider = feature.getProvider(TextDocument.create('file:///1', 'vim', 1, ''))
let res = provider.knows(PullState.document, doc.textDocument)
expect(res).toBe(false)
await client.stop()
})
it('should filter by ignore', async () => {
let client = await createServer(true, false, {}, opt => {
opt.diagnosticPullOptions = {
ignored: ['**/*.ts']
}
})
let doc = await workspace.loadFile(getUri('a.ts'), 'edit')
await helper.wait(10)
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let provider = feature.getProvider(doc.textDocument)
let res = provider.knows(PullState.document, doc.textDocument)
expect(res).toBe(false)
await client.stop()
})
it('should not throw on request error', async () => {
let client = await createServer(true)
await workspace.loadFile(getUri('error'), 'edit')
await workspace.loadFile(getUri('cancel'), 'tabe')
await workspace.loadFile(getUri('retrigger'), 'tabe')
await helper.wait(10)
await nvim.command('normal! 2gt')
await workspace.loadFile(getUri('unchanged'), 'edit')
await helper.wait(20)
await client.stop()
})
it('should pull diagnostic on change', async () => {
let doc = await workspace.loadFile(getUri('change'), 'edit')
let client = await createServer(true, false, {}, opt => {
opt.diagnosticPullOptions = {
onChange: true,
filter: doc => {
return doc.uri.endsWith('filtered')
}
}
})
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let provider = feature.getProvider(doc.textDocument)
await helper.waitValue(() => {
return provider.knows(PullState.document, doc.textDocument)
}, true)
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
await helper.wait(30)
let changeCount = await client.sendRequest('getChangeCount')
expect(changeCount).toBe(2)
await nvim.call('setline', [1, 'foo'])
let d = await workspace.loadFile(getUri('filtered'), 'tabe')
await d.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
await helper.wait(30)
await nvim.command(`bd! ${doc.bufnr}`)
await client.stop()
})
it('should pull diagnostic on save', async () => {
let doc = await workspace.loadFile(getUri(uuid() + 'filtered'), 'edit')
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
doc = await workspace.loadFile(getUri(uuid() + 'save'), 'tabe')
let client = await createServer(true, false, {}, opt => {
opt.diagnosticPullOptions = {
onSave: true,
filter: doc => {
return doc.uri.endsWith('filtered')
}
}
})
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let provider = feature.getProvider(doc.textDocument)
await helper.waitValue(() => {
return provider.knows(PullState.document, doc.textDocument)
}, true)
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
await nvim.command('wa')
await helper.wait(10)
await client.stop()
})
it('should use provideDiagnostics middleware', async () => {
let called = false
let callHandle = false
let client = await createServer(true, false, {
provideDiagnostics: (doc, id, token, next) => {
called = true
return next(doc, id, token)
},
handleDiagnostics: (uri, diagnostics, next) => {
callHandle = true
return next(uri, diagnostics)
}
})
let feature = client.getFeature(DocumentDiagnosticRequest.method)
expect(feature).toBeDefined()
let textDocument = TextDocument.create(getUri('empty'), 'e', 1, '')
let provider = feature.getProvider(textDocument)
let res = await provider.diagnostics.provideDiagnostics(textDocument, '', CancellationToken.None)
expect(called).toBe(true)
expect(res).toEqual({ kind: 'full', items: [] })
await helper.waitValue(() => {
return callHandle
}, true)
await client.stop()
})
it('should use provideWorkspaceDiagnostics middleware', async () => {
let called = false
let client = await createServer(false, true, {
provideWorkspaceDiagnostics: (resultIds, token, resultReporter, next) => {
called = true
return next(resultIds, token, resultReporter)
}
})
expect(called).toBe(true)
await helper.waitValue(async () => {
let count = await client.sendRequest('getWorkspceCount')
return count > 1
}, true)
await client.stop()
})
it('should receive partial result', async () => {
let client = await createServer(false, true, {}, opt => {
opt.diagnosticPullOptions = {
workspace: false
}
})
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let textDocument = TextDocument.create(getUri('empty'), 'e', 1, '')
let provider = feature.getProvider(textDocument)
let n = 0
await provider.diagnostics.provideWorkspaceDiagnostics([{ uri: 'uri', value: '1' }], CancellationToken.None, chunk => {
n++
})
expect(n).toBe(4)
await client.stop()
})
it('should fire refresh event', async () => {
let client = await createServer(true, false, {})
let feature = client.getFeature(DocumentDiagnosticRequest.method)
let textDocument = TextDocument.create(getUri('1'), 'e', 1, '')
let provider = feature.getProvider(textDocument)
let called = false
provider.onDidChangeDiagnosticsEmitter.event(() => {
called = true
})
await client.sendNotification('fireRefresh')
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
})

393
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/dynamic.test.ts

@ -0,0 +1,393 @@ @@ -0,0 +1,393 @@
import path from 'path'
import { CancellationToken, CodeActionRequest, CodeLensRequest, CompletionRequest, DidChangeWorkspaceFoldersNotification, DidCreateFilesNotification, DidDeleteFilesNotification, DidRenameFilesNotification, DocumentSymbolRequest, ExecuteCommandRequest, InlineValueRequest, Position, Range, RenameRequest, SemanticTokensRegistrationType, SymbolInformation, SymbolKind, WillDeleteFilesRequest, WillRenameFilesRequest, WorkspaceFolder, WorkspaceSymbolRequest } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import * as lsclient from '../../language-client'
import helper from '../helper'
import commands from '../../commands'
import { URI } from 'vscode-uri'
import workspace from '../../workspace'
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('DynamicFeature', () => {
let textDocument = TextDocument.create('file:///1', 'vim', 1, '\n')
let position = Position.create(1, 1)
let token = CancellationToken.None
async function startServer(opts: any = {}, middleware: lsclient.Middleware = {}): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: [{ language: '*' }],
initializationOptions: opts,
synchronize: {
configurationSection: 'languageserver.vim.settings'
},
middleware
}
let serverModule = path.join(__dirname, './server/dynamicServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
describe('RenameFeature', () => {
it('should start server', async () => {
let client = await startServer({ prepareRename: false })
let feature = client.getFeature(RenameRequest.method)
let provider = feature.getProvider(textDocument)
expect(provider.prepareRename).toBeUndefined()
feature.unregister('')
await client.stop()
})
it('should handle different result', async () => {
let client = await startServer({ prepareRename: true }, {
provideRenameEdits: (doc, pos, newName, token, next) => {
return next(doc, pos, newName, token)
},
prepareRename: (doc, pos, token, next) => {
return next(doc, pos, token)
}
})
let feature = client.getFeature(RenameRequest.method)
let provider = feature.getProvider(textDocument)
expect(provider.prepareRename).toBeDefined()
let res = await provider.prepareRename(textDocument, position, token)
expect(res).toBeNull()
await client.sendRequest('setPrepareResponse', { defaultBehavior: true })
res = await provider.prepareRename(textDocument, position, token)
expect(res).toBeNull()
await client.sendRequest('setPrepareResponse', { range: Range.create(0, 0, 0, 3), placeholder: 'placeholder' })
res = await provider.prepareRename(textDocument, position, token)
expect((res as any).placeholder).toBe('placeholder')
await expect(async () => {
await client.sendRequest('setPrepareResponse', { defaultBehavior: false })
res = await provider.prepareRename(textDocument, position, token)
}).rejects.toThrow(Error)
await client.stop()
})
})
describe('WorkspaceSymbolFeature', () => {
it('should use middleware', async () => {
let client = await startServer({}, {
provideWorkspaceSymbols: (query, token, next) => {
return next(query, token)
},
resolveWorkspaceSymbol: (item, token, next) => {
return next(item, token)
}
})
let feature = client.getFeature(WorkspaceSymbolRequest.method)
await helper.waitValue(() => {
return feature.getProviders().length
}, 2)
let provider = feature.getProviders().find(o => typeof o.resolveWorkspaceSymbol === 'function')
expect(provider).toBeDefined()
let token = CancellationToken.None
let res = await provider.provideWorkspaceSymbols('', token)
expect(res.length).toBe(0)
let sym = SymbolInformation.create('name', SymbolKind.Array, Range.create(0, 1, 0, 1), 'file:///1')
let resolved = await provider.resolveWorkspaceSymbol(sym, token)
expect(resolved.name).toBe(sym.name)
await client.stop()
})
})
describe('SemanticTokensFeature', () => {
it('should register semanticTokens', async () => {
let client = await startServer({})
let feature = client.getFeature(SemanticTokensRegistrationType.method)
let provider: any
await helper.waitValue(() => {
provider = feature.getProvider(textDocument)
return provider != null
}, true)
expect(provider.range).toBeUndefined()
await client.stop()
})
it('should use middleware', async () => {
let client = await startServer({ rangeTokens: true, delta: true }, {})
let feature = client.getFeature(SemanticTokensRegistrationType.method)
await helper.waitValue(() => {
return feature.getProvider(textDocument) != null
}, true)
let provider = feature.getProvider(textDocument)
expect(provider).toBeDefined()
expect(provider.range).toBeDefined()
let res = await provider.full.provideDocumentSemanticTokensEdits(textDocument, '2', CancellationToken.None)
expect(res.resultId).toBe('3')
await client.stop()
})
})
describe('CodeActionFeature', () => {
it('should use registered command', async () => {
let client = await startServer({})
let feature = client.getFeature(CodeActionRequest.method)
await helper.waitValue(() => {
return feature.getProvider(textDocument) != null
}, true)
let provider = feature.getProvider(textDocument)
let actions = await provider.provideCodeActions(textDocument, Range.create(0, 1, 0, 1), { diagnostics: [] }, token)
expect(actions.length).toBe(1)
await client.stop()
})
})
describe('PullConfigurationFeature', () => {
it('should pull configuration for configured languageserver', async () => {
helper.updateConfiguration('languageserver.vim.settings.foo', 'bar')
let client = await startServer({})
await helper.wait(50)
await client.sendNotification('pullConfiguration')
await helper.wait(50)
let res = await client.sendRequest('getConfiguration')
expect(res).toEqual(['bar'])
helper.updateConfiguration('suggest.noselect', true)
await helper.wait(50)
await client.stop()
})
})
describe('CodeLensFeature', () => {
it('should use codeLens middleware', async () => {
let fn = jest.fn()
let client = await startServer({}, {
provideCodeLenses: (doc, token, next) => {
fn()
return next(doc, token)
},
resolveCodeLens: (codelens, token, next) => {
fn()
return next(codelens, token)
}
})
let feature = client.getFeature(CodeLensRequest.method)
let provider = feature.getProvider(textDocument).provider
expect(provider).toBeDefined()
let res = await provider.provideCodeLenses(textDocument, token)
expect(res.length).toBe(2)
let resolved = await provider.resolveCodeLens(res[0], token)
expect(resolved.command).toBeDefined()
expect(fn).toBeCalledTimes(2)
await client.stop()
})
})
describe('InlineValueFeature', () => {
it('should fire refresh', async () => {
let client = await startServer({})
let feature = client.getFeature(InlineValueRequest.method)
expect(feature).toBeDefined()
await helper.waitValue(() => {
return feature.getProvider(textDocument) != null
}, true)
let provider = feature.getProvider(textDocument)
let called = false
provider.onDidChangeInlineValues.event(() => {
called = true
})
await client.sendNotification('fireInlineValueRefresh')
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
})
describe('ExecuteCommandFeature', () => {
it('should register command with middleware', async () => {
let called = false
let client = await startServer({}, {
executeCommand: (cmd, args, next) => {
called = true
return next(cmd, args)
}
})
await helper.waitValue(() => {
return commands.has('test_command')
}, true)
let feature = client.getFeature(ExecuteCommandRequest.method)
expect(feature).toBeDefined()
expect(feature.getState().kind).toBe('workspace')
let res = await commands.executeCommand('test_command')
expect(res).toEqual({ success: true })
expect(called).toBe(true)
await client.sendNotification('unregister')
await helper.wait(30)
await client.stop()
})
it('should register command without middleware', async () => {
let client = await startServer({}, {})
await helper.wait(50)
let res = await commands.executeCommand('test_command')
expect(res).toEqual({ success: true })
await client.stop()
})
})
describe('DocumentSymbolFeature', () => {
it('should provide documentSymbols without middleware', async () => {
let client = await startServer({}, {})
let feature = client.getFeature(DocumentSymbolRequest.method)
expect(feature).toBeDefined()
expect(feature.getState()).toBeDefined()
let provider = feature.getProvider(textDocument)
let res = await provider.provideDocumentSymbols(textDocument, token)
expect(res).toEqual([])
await client.stop()
})
it('should provide documentSymbols with middleware', async () => {
let called = false
let client = await startServer({ label: true }, {
provideDocumentSymbols: (doc, token, next) => {
called = true
return next(doc, token)
}
})
let feature = client.getFeature(DocumentSymbolRequest.method)
let provider = feature.getProvider(textDocument)
expect(provider.meta).toEqual({ label: 'test' })
let res = await provider.provideDocumentSymbols(textDocument, token)
expect(res).toEqual([])
expect(called).toBe(true)
await client.stop()
})
})
describe('FileOperationFeature', () => {
it('should use middleware for FileOperationFeature', async () => {
let n = 0
let client = await startServer({}, {
workspace: {
didCreateFiles: (ev, next) => {
n++
return next(ev)
},
didRenameFiles: (ev, next) => {
n++
return next(ev)
},
didDeleteFiles: (ev, next) => {
n++
return next(ev)
},
willRenameFiles: (ev, next) => {
n++
return next(ev)
},
willDeleteFiles: (ev, next) => {
n++
return next(ev)
}
}
})
let createFeature = client.getFeature(DidCreateFilesNotification.method)
await createFeature.send({ files: [URI.file('/a/b')] })
let renameFeature = client.getFeature(DidRenameFilesNotification.method)
await renameFeature.send({ files: [{ oldUri: URI.file('/a/b'), newUri: URI.file('/c/d') }] })
let deleteFeature = client.getFeature(DidDeleteFilesNotification.method)
await deleteFeature.send({ files: [URI.file('/x/y')] })
let willRename = client.getFeature(WillRenameFilesRequest.method)
await willRename.send({ files: [{ oldUri: URI.file(__dirname), newUri: URI.file(path.join(__dirname, 'x')) }], waitUntil: () => {} })
let willDelete = client.getFeature(WillDeleteFilesRequest.method)
await willDelete.send({ files: [URI.file('/x/y')], waitUntil: () => {} })
await helper.waitValue(() => {
return n
}, 5)
await client.stop()
})
})
describe('CompletionItemFeature', () => {
it('should register multiple completion sources', async () => {
let client = await startServer({}, {})
let feature = client.getFeature(CompletionRequest.method)
await helper.waitValue(() => {
return feature.registrationLength
}, 2)
await client.stop()
})
})
describe('WorkspaceFoldersFeature', () => {
it('should register listeners', async () => {
let client = await startServer({}, {})
let feature = client.getFeature(DidChangeWorkspaceFoldersNotification.method)
expect(feature).toBeDefined()
let state = feature.getState() as any
expect(state.registrations).toBe(true)
await client.stop()
})
it('should handle WorkspaceFoldersRequest', async () => {
let client = await startServer({ changeNotifications: true }, {})
let folders = workspace.workspaceFolders
expect(folders.length).toBe(0)
await client.sendNotification('requestFolders')
await helper.wait(30)
let res = await client.sendRequest('getFolders')
expect(res).toBeNull()
await client.stop()
})
it('should use workspaceFolders middleware', async () => {
await workspace.loadFile(__filename)
let folders = workspace.workspaceFolders
expect(folders.length).toBe(1)
let called = false
let client = await startServer({ changeNotifications: true }, {
workspace: {
workspaceFolders: (token, next) => {
called = true
return next(token)
}
}
})
await client.sendNotification('requestFolders')
await helper.waitValue(async () => {
let res = await client.sendRequest('getFolders') as WorkspaceFolder[]
return Array.isArray(res) && res.length == 1
}, true)
expect(called).toBe(true)
await client.stop()
})
it('should send folders event with middleware', async () => {
let called = false
let client = await startServer({ changeNotifications: true }, {
workspace: {
didChangeWorkspaceFolders: (ev, next) => {
called = true
return next(ev)
}
}
})
let folders = workspace.workspaceFolders
expect(folders.length).toBe(0)
await workspace.loadFile(__filename)
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
})
})

1658
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/features.test.ts

File diff suppressed because it is too large Load Diff

132
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/fileSystemWatcher.test.ts

@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
import path from 'path'
import { DidChangeWatchedFilesNotification, DocumentSelector, Emitter, Event, FileChangeType } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import { LanguageClient, LanguageClientOptions, Middleware, ServerOptions, TransportKind } from '../../language-client/index'
import { FileSystemWatcher } from '../../types'
import helper from '../helper'
function createClient(fileEvents: FileSystemWatcher | FileSystemWatcher[] | undefined, middleware: Middleware = {}): LanguageClient {
const serverModule = path.join(__dirname, './server/fileWatchServer.js')
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6014'] } }
}
const documentSelector: DocumentSelector = [{ scheme: 'file' }]
const clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: { fileEvents },
initializationOptions: {},
middleware
};
(clientOptions as ({ $testMode?: boolean })).$testMode = true
const result = new LanguageClient('test', 'Test Language Server', serverOptions, clientOptions)
return result
}
class CustomWatcher implements FileSystemWatcher {
public ignoreCreateEvents = false
public ignoreChangeEvents = false
public ignoreDeleteEvents = false
private readonly _onDidCreate = new Emitter<URI>()
public readonly onDidCreate: Event<URI> = this._onDidCreate.event
private readonly _onDidChange = new Emitter<URI>()
public readonly onDidChange: Event<URI> = this._onDidChange.event
private readonly _onDidDelete = new Emitter<URI>()
public readonly onDidDelete: Event<URI> = this._onDidDelete.event
constructor() {
}
public fireCreate(uri: URI): void {
this._onDidCreate.fire(uri)
}
public fireChange(uri: URI): void {
this._onDidChange.fire(uri)
}
public fireDelete(uri: URI): void {
this._onDidDelete.fire(uri)
}
public dispose() {
}
}
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
describe('FileSystemWatcherFeature', () => {
it('should hook file events from client configuration', async () => {
let client: LanguageClient
let watcher = new CustomWatcher()
let called = false
let changes: FileChangeType[] = []
client = createClient([watcher], {
workspace: {
didChangeWatchedFile: async (event, next): Promise<void> => {
called = true
changes.push(event.type)
return next(event)
}
}
})
let received: any[]
client.onNotification('filesChange', params => {
received = params.changes
})
await client.start()
expect(called).toBe(false)
let uri = URI.file(__filename)
watcher.fireCreate(uri)
expect(called).toBe(true)
watcher.fireChange(uri)
watcher.fireDelete(uri)
expect(changes).toEqual([1, 2, 3])
await helper.wait(100)
await client.stop()
expect(received.length).toBe(1)
expect(received[0]).toEqual({
uri: uri.toString(),
type: 3
})
})
it('should work with single watcher', async () => {
let client: LanguageClient
let watcher = new CustomWatcher()
client = createClient(watcher, {})
let received: any[]
client.onNotification('filesChange', params => {
received = params.changes
})
await client.start()
let uri = URI.file(__filename)
watcher.fireCreate(uri)
await helper.wait(100)
expect(received.length).toBe(1)
await client.stop()
})
it('should support dynamic registration', async () => {
let client: LanguageClient
client = createClient(undefined)
await client.start()
await helper.wait(50)
let feature = client.getFeature(DidChangeWatchedFilesNotification.method)
await (feature as any)._notifyFileEvent()
let state = feature.getState()
expect((state as any).registrations).toBe(true)
await client.sendNotification('unwatch')
await helper.wait(50)
state = feature.getState()
expect((state as any).registrations).toBe(false)
await client.stop()
})
})

654
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/integration.test.ts

@ -0,0 +1,654 @@ @@ -0,0 +1,654 @@
import * as assert from 'assert'
import cp from 'child_process'
import path from 'path'
import { CancellationToken, CancellationTokenSource, DidCreateFilesNotification, LSPErrorCodes, MessageType, ResponseError, Trace, WorkDoneProgress } from 'vscode-languageserver-protocol'
import { IPCMessageReader, IPCMessageWriter } from 'vscode-languageserver-protocol/node'
import { Diagnostic, MarkupKind, Range } from 'vscode-languageserver-types'
import { URI } from 'vscode-uri'
import * as lsclient from '../../language-client'
import { CloseAction, ErrorAction, HandleDiagnosticsSignature } from '../../language-client'
import { InitializationFailedHandler } from '../../language-client/utils/errorHandler'
import { CancellationError } from '../../util/errors'
import window from '../../window'
import workspace from '../../workspace'
import helper from '../helper'
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
describe('global functions', () => {
it('should get working directory', async () => {
let cwd = await lsclient.getServerWorkingDir()
expect(cwd).toBeDefined()
cwd = await lsclient.getServerWorkingDir({ cwd: 'not_exists' })
expect(cwd).toBeUndefined()
})
it('should get main root', async () => {
expect(lsclient.mainGetRootPath()).toBeUndefined()
let uri = URI.file(__filename)
await workspace.openResource(uri.toString())
expect(lsclient.mainGetRootPath()).toBeDefined()
})
it('should get runtime path', async () => {
expect(lsclient.getRuntimePath(__filename, undefined)).toBeDefined()
let uri = URI.file(__filename)
await workspace.openResource(uri.toString())
expect(lsclient.getRuntimePath('package.json', undefined)).toBeDefined()
let name = path.basename(__filename)
expect(lsclient.getRuntimePath(name, __dirname)).toBeDefined()
})
it('should check debug mode', async () => {
expect(lsclient.startedInDebugMode(['--debug'])).toBe(true)
expect(lsclient.startedInDebugMode(undefined)).toBe(false)
})
})
describe('Client events', () => {
it('should start server', async () => {
let clientOptions: lsclient.LanguageClientOptions = {}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
await client.stop()
})
it('should register events before server start', async () => {
let clientOptions: lsclient.LanguageClientOptions = {}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
let fn = jest.fn()
let disposable = client.onRequest('customRequest', () => {
fn()
disposable.dispose()
return {}
})
let dispose = client.onNotification('customNotification', () => {
fn()
dispose.dispose()
})
let dis = client.onProgress(WorkDoneProgress.type, '4fb247f8-0ede-415d-a80a-6629b6a9eaf8', p => {
expect(p).toEqual({ kind: 'end', message: 'end message' })
fn()
dis.dispose()
})
await client.start()
await client.sendNotification('send')
await helper.wait(60)
expect(fn).toBeCalledTimes(3)
// let client = await testEventServer({ initEvent: true })
await client.stop()
})
it('should register events after server start', async () => {
let clientOptions: lsclient.LanguageClientOptions = {
synchronize: {},
initializationOptions: { initEvent: true }
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
let fn = jest.fn()
let disposable = client.onRequest('customRequest', () => {
fn()
disposable.dispose()
return {}
})
let dispose = client.onNotification('customNotification', () => {
fn()
dispose.dispose()
})
let dis = client.onProgress(WorkDoneProgress.type, '4fb247f8-0ede-415d-a80a-6629b6a9eaf8', p => {
expect(p).toEqual({ kind: 'end', message: 'end message' })
fn()
dis.dispose()
})
await client.sendNotification('send')
await helper.wait(50)
expect(fn).toBeCalledTimes(3)
// let client = await testEventServer({ initEvent: true })
await client.stop()
})
it('should send progress', async () => {
let clientOptions: lsclient.LanguageClientOptions = {
synchronize: {},
initializationOptions: { initEvent: true }
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
let fn = jest.fn()
client.onNotification('progressResult', res => {
fn()
expect(res).toEqual({ kind: 'begin', title: 'begin progress' })
})
await client.sendProgress(WorkDoneProgress.type, '4b3a71d0-2b3f-46af-be2c-2827f548579f', { kind: 'begin', title: 'begin progress' })
await client.start()
await helper.wait(50)
let p = client.stop()
await expect(async () => {
await client._start()
}).rejects.toThrow(Error)
await p
expect(fn).toBeCalled()
})
it('should handle error', async () => {
let called = false
let clientOptions: lsclient.LanguageClientOptions = {
synchronize: {},
errorHandler: {
error: () => {
return ErrorAction.Shutdown
},
closed: () => {
called = true
return CloseAction.DoNotRestart
}
},
initializationOptions: { initEvent: true }
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.sendRequest('doExit')
await client.start()
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
it('should handle message events', async () => {
let clientOptions: lsclient.LanguageClientOptions = {
synchronize: {},
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
await client.sendNotification('logMessage')
await client.sendNotification('showMessage')
let types = [MessageType.Error, MessageType.Warning, MessageType.Info, MessageType.Log]
for (const t of types) {
await client.sendNotification('requestMessage', { type: t })
await helper.wait(30)
if (t == MessageType.Error) {
await workspace.nvim.input('1')
} else {
await workspace.nvim.input('<cr>')
}
}
let uri = URI.file(__filename)
await client.sendNotification('showDocument', { external: true, uri: 'lsptest:///1' })
await client.sendNotification('showDocument', { uri: 'lsptest:///1', takeFocus: false })
await client.sendNotification('showDocument', { uri: uri.toString() })
await client.sendNotification('showDocument', { uri: uri.toString(), selection: Range.create(0, 0, 1, 0) })
await helper.wait(300)
expect(client.hasPendingResponse).toBe(false)
await client.stop()
})
it('should invoke showDocument middleware', async () => {
let fn = jest.fn()
let clientOptions: lsclient.LanguageClientOptions = {
synchronize: {},
middleware: {
window: {
showDocument: async (params, next) => {
fn()
let res = await next(params, CancellationToken.None)
return res as any
}
}
}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
let uri = URI.file(__filename)
await client.start()
await client.sendNotification('showDocument', { uri: uri.toString() })
await helper.wait(50)
expect(fn).toBeCalled()
await client.restart()
await client.stop()
})
})
describe('Client integration', () => {
async function testLanguageServer(serverOptions: lsclient.ServerOptions, clientOpts?: lsclient.LanguageClientOptions): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: ['css'],
initializationOptions: {}
}
if (clientOpts) Object.assign(clientOptions, clientOpts)
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
await client.start()
expect(client.initializeResult).toBeDefined()
expect(client.started).toBe(true)
return client
}
it('should initialize from function', async () => {
async function testServer(serverOptions: lsclient.ServerOptions) {
let clientOptions: lsclient.LanguageClientOptions = {}
let client = new lsclient.LanguageClient('HTML', serverOptions, clientOptions)
await client.start()
await client.dispose()
}
await testServer(() => {
let module = path.join(__dirname, './server/eventServer.js')
let sp = cp.fork(module, ['--node-ipc'], { cwd: process.cwd() })
return Promise.resolve({ reader: new IPCMessageReader(sp), writer: new IPCMessageWriter(sp) })
})
await testServer(() => {
let module = path.join(__dirname, './server/eventServer.js')
let sp = cp.fork(module, ['--stdio'], {
cwd: process.cwd(),
execArgv: [],
silent: true,
})
return Promise.resolve({ reader: sp.stdout, writer: sp.stdin })
})
await testServer(() => {
let module = path.join(__dirname, './server/eventServer.js')
let sp = cp.fork(module, ['--stdio'], {
cwd: process.cwd(),
execArgv: [],
silent: true,
})
return Promise.resolve({ process: sp, detached: false })
})
await testServer(() => {
let module = path.join(__dirname, './server/eventServer.js')
let sp = cp.fork(module, ['--stdio'], {
cwd: process.cwd(),
execArgv: [],
silent: true,
})
return Promise.resolve(sp)
})
})
it('should initialize use IPC channel', async () => {
helper.updateConfiguration('css.trace.server.verbosity', 'verbose')
helper.updateConfiguration('css.trace.server.format', 'json')
let uri = URI.file(__filename)
await workspace.loadFile(uri.toString())
let serverModule = path.join(__dirname, './server/testInitializeResult.js')
let serverOptions: lsclient.ServerOptions = {
run: { module: serverModule, transport: lsclient.TransportKind.ipc },
debug: { module: serverModule, transport: lsclient.TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6014'] } }
}
let clientOptions: lsclient.LanguageClientOptions = {
rootPatterns: ['.vim'],
requireRootPattern: true,
documentSelector: ['css'],
synchronize: {}, initializationOptions: {},
middleware: {
handleDiagnostics: (uri, diagnostics, next) => {
assert.equal(uri, "uri:/test.ts")
assert.ok(Array.isArray(diagnostics))
assert.equal(diagnostics.length, 0)
next(uri, diagnostics)
}
}
}
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
await client.start()
let expected = {
capabilities: {
textDocumentSync: 1,
completionProvider: { resolveProvider: true, triggerCharacters: ['"', ':'] },
hoverProvider: true,
renameProvider: {
prepareProvider: true
}
},
customResults: {
hello: "world"
}
}
assert.deepEqual(client.initializeResult, expected)
await client.stop()
})
it('should initialize use stdio', async () => {
helper.updateConfiguration('css.trace.server.verbosity', 'verbose')
helper.updateConfiguration('css.trace.server.format', 'text')
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
let client = await testLanguageServer(serverOptions, {
workspaceFolder: { name: 'test', uri: URI.file(__dirname).toString() },
outputChannel: window.createOutputChannel('test'),
markdown: {},
disabledFeatures: ['pullDiagnostic'],
revealOutputChannelOn: lsclient.RevealOutputChannelOn.Info,
outputChannelName: 'custom',
connectionOptions: {
cancellationStrategy: {} as any,
maxRestartCount: 10,
},
stdioEncoding: 'utf8',
errorHandler: {
error: (): lsclient.ErrorAction => {
return lsclient.ErrorAction.Continue
},
closed: () => {
return lsclient.CloseAction.DoNotRestart
}
},
progressOnInitialization: true,
disableMarkdown: true,
disableDiagnostics: true
})
assert.deepStrictEqual(client.supportedMarkupKind, [MarkupKind.PlainText])
assert.strictEqual(client.name, 'Test Language Server')
assert.strictEqual(client.diagnostics, undefined)
client.trace = Trace.Verbose
let d = client.start()
let s = new CancellationTokenSource()
s.cancel()
client.handleFailedRequest(DidCreateFilesNotification.type, s.token, undefined, '')
await expect(async () => {
let error = new ResponseError(LSPErrorCodes.RequestCancelled, 'request cancelled')
client.handleFailedRequest(DidCreateFilesNotification.type, undefined, error, '')
}).rejects.toThrow(CancellationError)
let error = new ResponseError(LSPErrorCodes.ContentModified, 'content changed')
client.handleFailedRequest(DidCreateFilesNotification.type, undefined, error, '')
await client.stop()
client.info('message', new Error('my error'), true)
client.warn('message', 'error', true)
client.warn('message', 0, true)
client.logFailedRequest()
assert.strictEqual(client.diagnostics, undefined)
d.dispose()
})
it('should initialize use pipe', async () => {
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.pipe
}
let client = await testLanguageServer(serverOptions, {
ignoredRootPaths: [workspace.root]
})
expect(client.serviceState).toBeDefined()
await client.stop()
})
it('should initialize use socket', async () => {
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
options: {
env: {
NODE_SOCKET_TEST: 1
}
},
transport: {
kind: lsclient.TransportKind.socket,
port: 8088
}
}
let client = await testLanguageServer(serverOptions)
await client.stop()
})
it('should initialize as command', async () => {
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
command: 'node',
args: [serverModule, '--stdio']
}
let client = await testLanguageServer(serverOptions)
await client.stop()
})
it('should not throw as command', async () => {
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
command: 'not_exists',
args: [serverModule, '--stdio']
}
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: ['css'],
initializationOptions: {}
}
await expect(async () => {
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
await client.start()
await client.stop()
}).rejects.toThrow(Error)
})
it('should logMessage', async () => {
let called = false
let outputChannel = {
name: 'empty',
content: '',
append: () => {
called = true
},
appendLine: () => {},
clear: () => {},
show: () => {},
hide: () => {},
dispose: () => {}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
command: 'node',
args: [serverModule, '--stdio']
}
let client = await testLanguageServer(serverOptions, { outputChannel })
client.logMessage('message')
client.logMessage(Buffer.from('message', 'utf8'))
expect(called).toBe(true)
await client.stop()
})
it('should separate diagnostics', async () => {
async function startServer(disable?: boolean, handleDiagnostics?: (uri: string, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => void): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
disableDiagnostics: disable,
separateDiagnostics: true,
initializationOptions: {},
middleware: {
handleDiagnostics
}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio,
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
let client = await startServer()
await client.sendNotification('diagnostics')
await helper.waitValue(() => {
let collection = client.diagnostics
let res = collection.get('lsptest:/2')
return res.length
}, 2)
await client.stop()
client = await startServer(true)
await client.sendNotification('diagnostics')
await helper.wait(50)
let collection = client.diagnostics
expect(collection).toBeUndefined()
await client.stop()
let called = false
client = await startServer(false, (uri, diagnostics, next) => {
called = true
next(uri, diagnostics)
})
await client.sendNotification('diagnostics')
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
it('should check version on apply workspaceEdit', async () => {
let uri = URI.file(__filename)
await workspace.loadFile(uri.toString())
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: [{ scheme: 'file' }],
initializationOptions: {},
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio,
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
let res
client.onNotification('result', p => {
res = p
})
await client.start()
await client.sendNotification('edits')
await helper.wait(50)
expect(res).toBeDefined()
expect(res).toEqual({ applied: false })
await client.stop()
})
it('should apply simple workspaceEdit', async () => {
let clientOptions: lsclient.LanguageClientOptions = {
initializationOptions: {},
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio,
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
let res
client.onNotification('result', p => {
res = p
})
await client.start()
await client.sendNotification('simpleEdit')
await helper.wait(30)
expect(res).toBeDefined()
expect(res).toEqual({ applied: true })
await client.stop()
})
it('should handle error on initialize', async () => {
let client: lsclient.LanguageClient
async function startServer(handler: InitializationFailedHandler | undefined, key = 'throwError'): Promise<lsclient.LanguageClient> {
let clientOptions: lsclient.LanguageClientOptions = {
initializationFailedHandler: handler,
initializationOptions: {
[key]: true
}
}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc,
}
client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
return client
}
let n = 0
let fn = async () => {
await startServer(() => {
n++
return n == 1
})
}
await expect(fn()).rejects.toThrow(Error)
await helper.waitValue(() => {
return n
}, 5)
fn = async () => {
await startServer(undefined, 'normalThrow')
}
await expect(fn()).rejects.toThrow(Error)
fn = async () => {
await startServer(undefined, 'utf8')
}
await expect(fn()).rejects.toThrow(Error)
fn = async () => {
await client.stop()
}
await expect(fn()).rejects.toThrow(Error)
let spy = jest.spyOn(window, 'showErrorMessage').mockImplementation(() => {
return undefined
})
fn = async () => {
await startServer(undefined)
}
await expect(fn()).rejects.toThrow(Error)
spy.mockRestore()
await helper.wait(100)
})
})
describe('SettingMonitor', () => {
it('should setup SettingMonitor', async () => {
let clientOptions: lsclient.LanguageClientOptions = {}
let serverModule = path.join(__dirname, './server/eventServer.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let client = new lsclient.LanguageClient('html', 'Test Language Server', serverOptions, clientOptions)
await client.start()
let monitor = new lsclient.SettingMonitor(client, 'html.enabled')
let disposable = monitor.start()
helper.updateConfiguration('html.enabled', false)
await helper.wait(30)
expect(client.state).toBe(lsclient.State.Stopped)
helper.updateConfiguration('html.enabled', true)
await helper.wait(30)
expect(client.state).toBe(lsclient.State.Starting)
await client.onReady()
disposable.dispose()
})
})

99
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/progressPart.test.ts

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
import { Neovim } from '@chemzqm/neovim'
import { Emitter, Event, NotificationHandler, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport } from 'vscode-languageserver-protocol'
import { ProgressContext, ProgressPart } from '../../language-client/progressPart'
import helper from '../helper'
type ProgressType = WorkDoneProgressBegin | WorkDoneProgressReport | WorkDoneProgressEnd
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterEach(async () => {
await helper.reset()
})
afterAll(async () => {
await helper.shutdown()
})
describe('ProgressPart', () => {
function createClient(): ProgressContext & { fire: (ev: ProgressType) => void, token: string | undefined } {
let _onDidProgress = new Emitter<ProgressType>()
let onDidProgress: Event<ProgressType> = _onDidProgress.event
let notificationToken: string | undefined
return {
id: 'test',
get token() {
return notificationToken
},
fire(ev) {
_onDidProgress.fire(ev)
},
onProgress<ProgressType>(_, __, handler: NotificationHandler<ProgressType>) {
return onDidProgress(ev => {
handler(ev as any)
})
},
sendNotification(_, params) {
notificationToken = (params as any).token
}
}
}
it('should not start if cancelled', async () => {
let client = createClient()
let p = new ProgressPart(client, '0c7faec8-e36c-4cde-9815-95635c37d696')
p.cancel()
expect(p.begin({ kind: 'begin', title: 'canceleld' })).toBe(false)
})
it('should report progress', async () => {
let client = createClient()
let p = new ProgressPart(client, '0c7faec8-e36c-4cde-9815-95635c37d696')
p.begin({ kind: 'begin', title: 'p', percentage: 1, cancellable: true })
await helper.wait(30)
p.report({ kind: 'report', message: 'msg', percentage: 10 })
await helper.wait(10)
p.report({ kind: 'report', message: 'msg', percentage: 50 })
await helper.wait(10)
p.done('finised')
})
it('should close notification on cancel', async () => {
helper.updateConfiguration('notification.statusLineProgress', false)
let client = createClient()
let p = new ProgressPart(client, '0c7faec8-e36c-4cde-9815-95635c37d696')
let started = p.begin({ kind: 'begin', title: 'canceleld' })
expect(started).toBe(true)
p.cancel()
p.cancel()
let winids = await nvim.call('coc#notify#win_list') as number[]
await helper.wait(30)
expect(winids.length).toBe(1)
let win = nvim.createWindow(winids[0])
let closing = await win.getVar('closing')
expect(closing).toBe(1)
})
it('should send notification on cancel', async () => {
helper.updateConfiguration('notification.statusLineProgress', false)
let client = createClient()
let token = '0c7faec8-e36c-4cde-9815-95635c37d696'
let p = new ProgressPart(client, token)
let started = p.begin({ kind: 'begin', title: 'canceleld', cancellable: true })
expect(started).toBe(true)
for (let i = 0; i < 10; i++) {
await helper.wait(30)
let winids = await nvim.call('coc#notify#win_list') as number[]
if (winids.length == 1) break
}
await helper.wait(30)
nvim.call('coc#float#close_all', [], true)
await helper.waitValue(() => {
return client.token
}, token)
})
})

36
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/configServer.js

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
'use strict'
const {createConnection, ConfigurationRequest, DidChangeConfigurationNotification} = require('vscode-languageserver')
const {URI} = require('vscode-uri')
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
connection.onInitialize((_params) => {
return {capabilities: {}}
})
connection.onNotification('pull0', () => {
connection.sendRequest(ConfigurationRequest.type, {
items: [{
scopeUri: URI.file(__filename).toString()
}]
})
})
connection.onNotification('pull1', () => {
connection.sendRequest(ConfigurationRequest.type, {
items: [{
section: 'http'
}, {
section: 'editor.cpp.format'
}, {
section: 'unknown'
}]
})
})
connection.onNotification(DidChangeConfigurationNotification.type, params => {
connection.sendNotification('configurationChange', params)
})
connection.listen()

13
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/crashOnShutdownServer.js

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
'use strict'
const {createConnection} = require('vscode-languageserver')
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
connection.onInitialize((_params) => {
return {capabilities: {}}
})
connection.onShutdown(() => {
process.exit(100)
})
connection.listen()

17
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/crashServer.js

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
const node_1 = require('vscode-languageserver')
var CrashNotification;
(function (CrashNotification) {
CrashNotification.type = new node_1.NotificationType0('test/crash')
})(CrashNotification || (CrashNotification = {}))
const connection = (0, node_1.createConnection)()
connection.onInitialize((_params) => {
return {
capabilities: {}
}
})
connection.onNotification(CrashNotification.type, () => {
process.exit(100)
})
connection.listen()

21
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/customServer.js

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
const node_1 = require("vscode-languageserver")
const connection = (0, node_1.createConnection)()
connection.onInitialize((_params) => {
return {
capabilities: {}
}
})
connection.onRequest('request', (param) => {
return param.value + 1
})
connection.onNotification('notification', () => {
})
connection.onRequest('triggerRequest', async () => {
await connection.sendRequest('request')
})
connection.onRequest('triggerNotification', async () => {
await connection.sendNotification('notification')
})
connection.listen()

118
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/diagnosticServer.js

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
'use strict'
const {createConnection, ResponseError, LSPErrorCodes, DiagnosticRefreshRequest, DocumentDiagnosticReportKind, Diagnostic, Range, DiagnosticSeverity, TextDocuments, TextDocumentSyncKind} = require('vscode-languageserver')
const {TextDocument} = require('vscode-languageserver-textdocument')
let documents = new TextDocuments(TextDocument)
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
let options
documents.listen(connection)
connection.onInitialize((params) => {
options = params.initializationOptions || {}
const interFileDependencies = options.interFileDependencies !== false
const workspaceDiagnostics = options.workspaceDiagnostics === true
const identifier = options.identifier ?? '6d52eff6-96c7-4fd1-910f-f060bcffb23f'
return {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
diagnosticProvider: {
identifier,
interFileDependencies,
workspaceDiagnostics
}
}
}
})
let count = 0
let saveCount = 0
connection.languages.diagnostics.on((params) => {
let uri = params.textDocument.uri
if (uri.endsWith('error')) return Promise.reject(new Error('server error'))
if (uri.endsWith('cancel')) return new ResponseError(LSPErrorCodes.ServerCancelled, 'cancel', {retriggerRequest: false})
if (uri.endsWith('retrigger')) return new ResponseError(LSPErrorCodes.ServerCancelled, 'retrigger', {retriggerRequest: true})
if (uri.endsWith('change')) count++
if (uri.endsWith('save')) saveCount++
if (uri.endsWith('empty')) return null
if (uri.endsWith('unchanged')) return {
kind: DocumentDiagnosticReportKind.Unchanged,
resultId: '1'
}
return {
kind: DocumentDiagnosticReportKind.Full,
items: [
Diagnostic.create(Range.create(1, 1, 1, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}
})
let workspaceCount = 0
connection.languages.diagnostics.onWorkspace((params, _, __, reporter) => {
if (params.previousResultIds.length > 0) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reporter.report({
items: [{
kind: DocumentDiagnosticReportKind.Full,
uri: 'uri1',
version: 1,
items: [
Diagnostic.create(Range.create(1, 0, 1, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}]
})
}, 10)
setTimeout(() => {
reporter.report(null)
}, 15)
setTimeout(() => {
reporter.report({
items: [{
kind: DocumentDiagnosticReportKind.Full,
uri: 'uri2',
version: 1,
items: [
Diagnostic.create(Range.create(2, 0, 2, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}]
})
}, 20)
setTimeout(() => {
resolve({items: []})
}, 50)
})
}
workspaceCount++
if (workspaceCount == 2) {
return new ResponseError(LSPErrorCodes.ServerCancelled, 'changed')
}
return {
items: [{
kind: DocumentDiagnosticReportKind.Full,
uri: 'uri',
version: 1,
items: [
Diagnostic.create(Range.create(1, 1, 1, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}]
}
})
connection.onNotification('fireRefresh', () => {
connection.sendRequest(DiagnosticRefreshRequest.type)
})
connection.onRequest('getChangeCount', () => {
return count
})
connection.onRequest('getSaveCount', () => {
return saveCount
})
connection.onRequest('getWorkspceCount', () => {
return workspaceCount
})
connection.listen()

221
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/dynamicServer.js

@ -0,0 +1,221 @@ @@ -0,0 +1,221 @@
'use strict'
const {createConnection, ProtocolRequestType, Range, TextDocumentSyncKind, Command, RenameRequest, WorkspaceSymbolRequest, CodeAction, SemanticTokensRegistrationType, CodeActionRequest, ConfigurationRequest, DidChangeConfigurationNotification, InlineValueRefreshRequest, ExecuteCommandRequest, CompletionRequest, WorkspaceFoldersRequest} = require('vscode-languageserver')
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
let options
let disposables = []
let prepareResponse
let configuration
let folders
let foldersEvent
connection.onInitialize((params) => {
options = params.initializationOptions || {}
let changeNotifications = options.changeNotifications ?? 'b346648e-88e0-44e3-91e3-52fd6addb8c7'
return {
capabilities: {
inlineValueProvider: {},
executeCommandProvider: {
},
documentSymbolProvider: options.label ? {label: 'test'} : true,
textDocumentSync: TextDocumentSyncKind.Full,
renameProvider: options.prepareRename ? {prepareProvider: true} : true,
workspaceSymbolProvider: true,
codeLensProvider: {
resolveProvider: true
},
workspace: {
workspaceFolders: {
changeNotifications
},
fileOperations: {
// Static reg is folders + .txt files with operation kind in the path
didCreate: {
filters: [
{scheme: 'lsptest', pattern: {glob: '**/*', matches: 'file', options: {}}},
{scheme: 'file', pattern: {glob: '**/*', matches: 'file', options: {ignoreCase: false}}}
]
},
didRename: {
filters: [
{scheme: 'file', pattern: {glob: '**/*', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/*', matches: 'file'}}
]
},
didDelete: {
filters: [{scheme: 'file', pattern: {glob: '**/*'}}]
},
willCreate: {
filters: [{scheme: 'file', pattern: {glob: '**/*'}}]
},
willRename: {
filters: [
{scheme: 'file', pattern: {glob: '**/*', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/*', matches: 'file'}}
]
},
willDelete: {
filters: [{scheme: 'file', pattern: {glob: '**/*'}}]
},
}
},
}
}
})
connection.onInitialized(() => {
connection.client.register(RenameRequest.type, {
prepareProvider: options.prepareRename
}).then(d => {
disposables.push(d)
})
connection.client.register(WorkspaceSymbolRequest.type, {
resolveProvider: true
}).then(d => {
disposables.push(d)
})
let full = false
if (options.delta) {
full = {delta: true}
}
connection.client.register(SemanticTokensRegistrationType.method, {
full,
range: options.rangeTokens,
legend: {
tokenTypes: [],
tokenModifiers: []
},
})
connection.client.register(CodeActionRequest.method, {
resolveProvider: false
})
connection.client.register(DidChangeConfigurationNotification.type, {section: undefined})
connection.client.register(ExecuteCommandRequest.type, {
commands: ['test_command']
}).then(d => {
disposables.push(d)
})
connection.client.register(CompletionRequest.type, {
documentSelector: [{language: 'vim'}]
}).then(d => {
disposables.push(d)
})
connection.client.register(CompletionRequest.type, {
triggerCharacters: ['/'],
}).then(d => {
disposables.push(d)
})
})
let lastFileOperationRequest
connection.workspace.onDidCreateFiles(params => {lastFileOperationRequest = {type: 'create', params}})
connection.workspace.onDidRenameFiles(params => {lastFileOperationRequest = {type: 'rename', params}})
connection.workspace.onDidDeleteFiles(params => {lastFileOperationRequest = {type: 'delete', params}})
connection.workspace.onWillRenameFiles(params => {lastFileOperationRequest = {type: 'willRename', params}})
connection.workspace.onWillDeleteFiles(params => {lastFileOperationRequest = {type: 'willDelete', params}})
// connection.onDidChangeWorkspaceFolders(e => {
// foldersEvent = params
// })
connection.onCompletion(_params => {
return [
{label: 'item', insertText: 'text'}
]
})
connection.onCompletionResolve(item => {
item.detail = 'detail'
return item
})
connection.onRequest(
new ProtocolRequestType('testing/lastFileOperationRequest'),
() => {
return lastFileOperationRequest
},
)
connection.onNotification('unregister', () => {
for (let d of disposables) {
d.dispose()
disposables = []
}
})
connection.onDocumentSymbol(() => {
return []
})
connection.onExecuteCommand(param => {
if (param.command = 'test_command') {
return {success: true}
}
})
connection.languages.semanticTokens.onDelta(() => {
return {
resultId: '3',
data: []
}
})
connection.onRequest('setPrepareResponse', param => {
prepareResponse = param
})
connection.onNotification('pullConfiguration', () => {
configuration = connection.sendRequest(ConfigurationRequest.type, {
items: [{section: 'foo'}]
})
})
connection.onRequest('getConfiguration', () => {
return configuration
})
connection.onRequest('getFolders', () => {
return folders
})
connection.onRequest('getFoldersEvent', () => {
return foldersEvent
})
connection.onNotification('fireInlineValueRefresh', () => {
connection.sendRequest(InlineValueRefreshRequest.type)
})
connection.onNotification('requestFolders', async () => {
folders = await connection.sendRequest(WorkspaceFoldersRequest.type)
})
connection.onPrepareRename(() => {
return prepareResponse
})
connection.onCodeAction(() => {
return [
Command.create('title', 'editor.action.triggerSuggest')
]
})
connection.onWorkspaceSymbol(() => {
return []
})
connection.onWorkspaceSymbolResolve(item => {
return item
})
connection.onCodeLens(params => {
return [{range: Range.create(0, 0, 0, 3)}, {range: Range.create(1, 0, 1, 3)}]
})
connection.onCodeLensResolve(codelens => {
return {range: codelens.range, command: {title: 'format', command: 'format'}}
})
connection.listen()

14
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/errorServer.js

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
"use strict"
const {createConnection, ResponseError} = require("vscode-languageserver")
const connection = createConnection()
connection.onInitialize((_params) => {
return {
capabilities: {}
}
})
connection.onSignatureHelp(_params => {
return new ResponseError(-32803, 'failed')
})
connection.listen()

104
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/eventServer.js

@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
'use strict'
const {createConnection, TextEdit, TextDocuments, Range, DiagnosticSeverity, Location, Diagnostic, DiagnosticRelatedInformation, PositionEncodingKind, WorkDoneProgress, ResponseError, LogMessageNotification, MessageType, ShowMessageNotification, ShowMessageRequest, ShowDocumentRequest, ApplyWorkspaceEditRequest, TextDocumentSyncKind, Position} = require('vscode-languageserver')
const {TextDocument} = require('vscode-languageserver-textdocument')
let documents = new TextDocuments(TextDocument)
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
let options
documents.listen(connection)
connection.onInitialize((params) => {
options = params.initializationOptions || {}
if (options.throwError) {
setTimeout(() => {
process.exit()
}, 10)
return new ResponseError(1, 'message', {retry: true})
}
if (options.normalThrow) {
setTimeout(() => {
process.exit()
}, 10)
throw new Error('message')
}
if (options.utf8) {
return {capabilities: {positionEncoding: PositionEncodingKind.UTF8}}
}
return {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full
}
}
})
connection.onNotification('diagnostics', () => {
let diagnostics = []
let related = []
let uri = 'lsptest:///2'
related.push(DiagnosticRelatedInformation.create(Location.create(uri, Range.create(0, 0, 0, 1)), 'dup'))
related.push(DiagnosticRelatedInformation.create(Location.create(uri, Range.create(0, 0, 1, 0)), 'dup'))
diagnostics.push(Diagnostic.create(Range.create(0, 0, 1, 0), 'msg', DiagnosticSeverity.Error, undefined, undefined, related))
connection.sendDiagnostics({uri: 'lsptest:///1', diagnostics})
connection.sendDiagnostics({uri: 'lsptest:///3', version: 1, diagnostics})
})
connection.onNotification('simpleEdit', async () => {
let res = await connection.sendRequest(ApplyWorkspaceEditRequest.type, {edit: {documentChanges: []}})
connection.sendNotification('result', res)
})
connection.onNotification('edits', async () => {
let uris = documents.keys()
let res = await connection.sendRequest(ApplyWorkspaceEditRequest.type, {
edit: {
documentChanges: uris.map(uri => {
return {
textDocument: {uri, version: documents.get(uri).version + 1},
edits: [TextEdit.insert(Position.create(0, 0), 'foo')]
}
})
}
})
connection.sendNotification('result', res)
})
connection.onNotification('send', () => {
connection.sendRequest('customRequest')
connection.sendNotification('customNotification')
connection.sendProgress(WorkDoneProgress.type, '4fb247f8-0ede-415d-a80a-6629b6a9eaf8', {kind: 'end', message: 'end message'})
})
connection.onNotification('logMessage', () => {
connection.sendNotification(LogMessageNotification.type, {type: MessageType.Error, message: 'msg'})
connection.sendNotification(LogMessageNotification.type, {type: MessageType.Info, message: 'msg'})
connection.sendNotification(LogMessageNotification.type, {type: MessageType.Log, message: 'msg'})
connection.sendNotification(LogMessageNotification.type, {type: MessageType.Warning, message: 'msg'})
})
connection.onNotification('showMessage', () => {
connection.sendNotification(ShowMessageNotification.type, {type: MessageType.Error, message: 'msg'})
connection.sendNotification(ShowMessageNotification.type, {type: MessageType.Info, message: 'msg'})
connection.sendNotification(ShowMessageNotification.type, {type: MessageType.Log, message: 'msg'})
connection.sendNotification(ShowMessageNotification.type, {type: MessageType.Warning, message: 'msg'})
})
connection.onNotification('requestMessage', async params => {
await connection.sendRequest(ShowMessageRequest.type, {type: params.type, message: 'msg', actions: [{title: 'open'}]})
})
connection.onNotification('showDocument', async params => {
await connection.sendRequest(ShowDocumentRequest.type, params)
})
connection.onProgress(WorkDoneProgress.type, '4b3a71d0-2b3f-46af-be2c-2827f548579f', (params) => {
connection.sendNotification('progressResult', params)
})
connection.onRequest('doExit', () => {
setTimeout(() => {
process.exit(1)
}, 30)
})
connection.listen()

51
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/fileWatchServer.js

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
'use strict'
const {createConnection, DidChangeWatchedFilesNotification} = require('vscode-languageserver')
const {URI} = require('vscode-uri')
const connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
connection.onInitialize((_params) => {
return {capabilities: {}}
})
let disposables = []
connection.onInitialized(() => {
connection.client.register(DidChangeWatchedFilesNotification.type, {
watchers: [{
globPattern: '**/jsconfig.json',
}, {
globPattern: '**/*.ts',
kind: 1
}, {
globPattern: {
baseUri: URI.file(process.cwd()).toString(),
pattern: '**/*.vim'
},
kind: 1
}, {
globPattern: '**/*.js',
kind: 2
}, {
globPattern: -1
}]
}).then(d => {
disposables.push(d)
})
connection.client.register(DidChangeWatchedFilesNotification.type, {
watchers: null
}).then(d => {
disposables.push(d)
})
})
connection.onNotification(DidChangeWatchedFilesNotification.type, params => {
connection.sendNotification('filesChange', params)
})
connection.onNotification('unwatch', () => {
for (let d of disposables) {
d.dispose()
}
})
connection.listen()

12
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/nullServer.js

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
const node_1 = require("vscode-languageserver")
const connection = (0, node_1.createConnection)()
connection.onInitialize((_params) => {
return {
capabilities: {}
}
})
connection.onShutdown(() => {
})
connection.listen()

16
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/startStopServer.js

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
const node_1 = require("vscode-languageserver")
const connection = (0, node_1.createConnection)()
connection.onInitialize((_params) => {
return {
capabilities: {
executeCommandProvider: {
commands: ['foo.command'],
}
}
}
})
connection.onShutdown(() => {
})
connection.listen()

102
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testDocuments.js

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
const {ResponseError, LSPErrorCodes} = require('vscode-languageserver')
const ls = require('vscode-languageserver')
const {TextDocument} = require('vscode-languageserver-textdocument')
let connection = ls.createConnection()
let documents = new ls.TextDocuments(TextDocument)
let lastOpenEvent
let lastCloseEvent
let lastChangeEvent
let lastWillSave
let lastDidSave
documents.onDidOpen(e => {
lastOpenEvent = {uri: e.document.uri, version: e.document.version}
})
documents.onDidClose(e => {
lastCloseEvent = {uri: e.document.uri}
})
documents.onDidChangeContent(e => {
lastChangeEvent = {uri: e.document.uri, text: e.document.getText()}
})
documents.onWillSave(e => {
lastWillSave = {uri: e.document.uri}
})
documents.onWillSaveWaitUntil(e => {
let uri = e.document.uri
if (uri.endsWith('error.vim')) throw new ResponseError(LSPErrorCodes.ContentModified, 'content changed')
if (!uri.endsWith('foo.vim')) return []
return [ls.TextEdit.insert(ls.Position.create(0, 0), 'abc')]
})
documents.onDidSave(e => {
lastDidSave = {uri: e.document.uri}
})
documents.listen(connection)
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
let opts
connection.onInitialize(params => {
opts = params.initializationOptions
let capabilities = {
textDocumentSync: {
openClose: true,
change: ls.TextDocumentSyncKind.Full,
willSave: true,
willSaveWaitUntil: true,
save: true
}
}
return {capabilities}
})
connection.onRequest('getLastOpen', () => {
return lastOpenEvent
})
connection.onRequest('getLastClose', () => {
return lastCloseEvent
})
connection.onRequest('getLastChange', () => {
return lastChangeEvent
})
connection.onRequest('getLastWillSave', () => {
return lastWillSave
})
connection.onRequest('getLastDidSave', () => {
return lastDidSave
})
let disposables = []
connection.onNotification('registerDocumentSync', () => {
let opt = {documentSelector: [{language: 'vim'}]}
connection.client.register(ls.DidOpenTextDocumentNotification.type, opt).then(d => {
disposables.push(d)
})
connection.client.register(ls.DidCloseTextDocumentNotification.type, opt).then(d => {
disposables.push(d)
})
connection.client.register(ls.DidChangeTextDocumentNotification.type, Object.assign({
syncKind: opts.none === true ? ls.TextDocumentSyncKind.None : ls.TextDocumentSyncKind.Incremental
}, opt)).then(d => {
disposables.push(d)
})
connection.client.register(ls.WillSaveTextDocumentNotification.type, opt).then(d => {
disposables.push(d)
})
connection.client.register(ls.WillSaveTextDocumentWaitUntilRequest.type, opt).then(d => {
disposables.push(d)
})
})
connection.onNotification('unregisterDocumentSync', () => {
for (let dispose of disposables) {
dispose.dispose()
}
disposables = []
})
connection.listen()

35
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testFileWatcher.js

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
const languageserver = require('vscode-languageserver')
let connection = languageserver.createConnection()
let documents = new languageserver.TextDocuments()
documents.listen(connection)
connection.onInitialize(() => {
let capabilities = {
textDocumentSync: documents.syncKind
}
return { capabilities }
})
connection.onInitialized(() => {
connection.sendRequest('client/registerCapability', {
registrations: [{
id: 'didChangeWatchedFiles',
method: 'workspace/didChangeWatchedFiles',
registerOptions: {
watchers: [{ globPattern: "**" }]
}
}]
})
})
let received
connection.onNotification('workspace/didChangeWatchedFiles', params => {
received = params
})
connection.onRequest('custom/received', async () => {
return received
})
connection.listen()

36
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testInitializeResult.js

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
'use strict'
Object.defineProperty(exports, "__esModule", {value: true})
const tslib_1 = require("tslib")
const assert = tslib_1.__importStar(require("assert"))
const vscode_languageserver_1 = require("vscode-languageserver")
let connection = vscode_languageserver_1.createConnection()
let documents = new vscode_languageserver_1.TextDocuments()
documents.listen(connection)
connection.onInitialize((params) => {
assert.equal(params.capabilities.workspace.applyEdit, true)
assert.equal(params.capabilities.workspace.workspaceEdit.documentChanges, true)
assert.deepEqual(params.capabilities.workspace.workspaceEdit.resourceOperations, [vscode_languageserver_1.ResourceOperationKind.Create, vscode_languageserver_1.ResourceOperationKind.Rename, vscode_languageserver_1.ResourceOperationKind.Delete])
assert.equal(params.capabilities.workspace.workspaceEdit.failureHandling, vscode_languageserver_1.FailureHandlingKind.Undo)
assert.equal(params.capabilities.textDocument.completion.completionItem.deprecatedSupport, true)
assert.equal(params.capabilities.textDocument.completion.completionItem.preselectSupport, true)
assert.equal(params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport, true)
assert.equal(params.capabilities.textDocument.rename.prepareSupport, true)
let valueSet = params.capabilities.textDocument.completion.completionItemKind.valueSet
assert.equal(valueSet[0], 1)
assert.equal(valueSet[valueSet.length - 1], vscode_languageserver_1.CompletionItemKind.TypeParameter)
let capabilities = {
textDocumentSync: 1,
completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']},
hoverProvider: true,
renameProvider: {
prepareProvider: true
}
}
return {capabilities, customResults: {"hello": "world"}}
})
connection.onInitialized(() => {
connection.sendDiagnostics({uri: "uri:/test.ts", diagnostics: []})
})
// Listen on the connection
connection.listen()

602
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testServer.js

@ -0,0 +1,602 @@ @@ -0,0 +1,602 @@
const assert = require('assert')
const {URI} = require('vscode-uri')
const {
createConnection, CompletionItemKind, ResourceOperationKind, FailureHandlingKind,
DiagnosticTag, CompletionItemTag, TextDocumentSyncKind, MarkupKind, SignatureInformation, ParameterInformation,
Location, Range, DocumentHighlight, DocumentHighlightKind, CodeAction, Command, TextEdit, Position, DocumentLink,
ColorInformation, Color, ColorPresentation, FoldingRange, SelectionRange, SymbolKind, ProtocolRequestType, WorkDoneProgress,
SignatureHelpRequest, SemanticTokensRefreshRequest, WorkDoneProgressCreateRequest, CodeLensRefreshRequest, InlayHintRefreshRequest, WorkspaceSymbolRequest, DidChangeConfigurationNotification} = require('vscode-languageserver')
const {
DidCreateFilesNotification,
DidRenameFilesNotification,
DidDeleteFilesNotification,
WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest, InlayHint, InlayHintLabelPart, InlayHintKind, DocumentDiagnosticReportKind, Diagnostic, DiagnosticSeverity, InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression
} = require('vscode-languageserver-protocol')
let connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
let disposables = []
connection.onInitialize(params => {
assert.equal((params.capabilities.workspace).applyEdit, true)
assert.equal(params.capabilities.workspace.workspaceEdit.documentChanges, true)
assert.deepEqual(params.capabilities.workspace.workspaceEdit.resourceOperations, ['create', 'rename', 'delete'])
assert.equal(params.capabilities.workspace.workspaceEdit.failureHandling, FailureHandlingKind.Undo)
assert.equal(params.capabilities.workspace.workspaceEdit.normalizesLineEndings, true)
assert.equal(params.capabilities.workspace.workspaceEdit.changeAnnotationSupport.groupsOnLabel, false)
assert.equal(params.capabilities.workspace.symbol.resolveSupport.properties[0], 'location.range')
assert.equal(params.capabilities.textDocument.completion.completionItem.deprecatedSupport, true)
assert.equal(params.capabilities.textDocument.completion.completionItem.preselectSupport, true)
assert.equal(params.capabilities.textDocument.completion.completionItem.tagSupport.valueSet.length, 1)
assert.equal(params.capabilities.textDocument.completion.completionItem.tagSupport.valueSet[0], CompletionItemTag.Deprecated)
assert.equal(params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport, true)
assert.equal(params.capabilities.textDocument.definition.linkSupport, true)
assert.equal(params.capabilities.textDocument.declaration.linkSupport, true)
assert.equal(params.capabilities.textDocument.implementation.linkSupport, true)
assert.equal(params.capabilities.textDocument.typeDefinition.linkSupport, true)
assert.equal(params.capabilities.textDocument.rename.prepareSupport, true)
assert.equal(params.capabilities.textDocument.publishDiagnostics.relatedInformation, true)
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet.length, 2)
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet[0], DiagnosticTag.Unnecessary)
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet[1], DiagnosticTag.Deprecated)
assert.equal(params.capabilities.textDocument.documentLink.tooltipSupport, true)
assert.equal(params.capabilities.textDocument.inlineValue.dynamicRegistration, true)
assert.equal(params.capabilities.textDocument.inlayHint.dynamicRegistration, true)
assert.equal(params.capabilities.textDocument.inlayHint.resolveSupport.properties[0], 'tooltip')
let valueSet = params.capabilities.textDocument.completion.completionItemKind.valueSet
assert.equal(valueSet[0], 1)
assert.equal(valueSet[valueSet.length - 1], CompletionItemKind.TypeParameter)
assert.deepEqual(params.capabilities.workspace.workspaceEdit.resourceOperations, [ResourceOperationKind.Create, ResourceOperationKind.Rename, ResourceOperationKind.Delete])
assert.equal(params.capabilities.workspace.fileOperations.willCreate, true)
let diagnosticClientCapabilities = params.capabilities.textDocument.diagnostic
assert.equal(diagnosticClientCapabilities.dynamicRegistration, true)
assert.equal(diagnosticClientCapabilities.relatedDocumentSupport, true)
const capabilities = {
textDocumentSync: TextDocumentSyncKind.Full,
definitionProvider: true,
hoverProvider: true,
signatureHelpProvider: {
triggerCharacters: [','],
retriggerCharacters: [';']
},
completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']},
referencesProvider: true,
documentHighlightProvider: true,
codeActionProvider: {
resolveProvider: true
},
codeLensProvider: {
resolveProvider: true
},
documentFormattingProvider: true,
documentRangeFormattingProvider: true,
documentOnTypeFormattingProvider: {
firstTriggerCharacter: ':'
},
renameProvider: {
prepareProvider: true
},
documentLinkProvider: {
resolveProvider: true
},
colorProvider: true,
declarationProvider: true,
foldingRangeProvider: true,
implementationProvider: {
documentSelector: [{language: '*'}]
},
selectionRangeProvider: true,
inlineValueProvider: {},
inlayHintProvider: {
resolveProvider: true
},
typeDefinitionProvider: {
id: '82671a9a-2a69-4e9f-a8d7-e1034eaa0d2e',
documentSelector: [{language: '*'}]
},
callHierarchyProvider: true,
semanticTokensProvider: {
legend: {
tokenTypes: [],
tokenModifiers: []
},
range: true,
full: {
delta: true
}
},
workspace: {
fileOperations: {
// Static reg is folders + .txt files with operation kind in the path
didCreate: {
filters: [{scheme: 'file', pattern: {glob: '**/created-static/**{/,/*.txt}'}}]
},
didRename: {
filters: [
{scheme: 'file', pattern: {glob: '**/renamed-static/**/', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/renamed-static/**/*.txt', matches: 'file'}}
]
},
didDelete: {
filters: [{scheme: 'file', pattern: {glob: '**/deleted-static/**{/,/*.txt}'}}]
},
willCreate: {
filters: [{scheme: 'file', pattern: {glob: '**/created-static/**{/,/*.txt}'}}]
},
willRename: {
filters: [
{scheme: 'file', pattern: {glob: '**/renamed-static/**/', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/renamed-static/**/*.txt', matches: 'file'}}
]
},
willDelete: {
filters: [{scheme: 'file', pattern: {glob: '**/deleted-static/**{/,/*.txt}'}}]
},
},
},
linkedEditingRangeProvider: true,
diagnosticProvider: {
identifier: 'da348dc5-c30a-4515-9d98-31ff3be38d14',
interFileDependencies: true,
workspaceDiagnostics: true
},
typeHierarchyProvider: true,
workspaceSymbolProvider: {
resolveProvider: true
},
notebookDocumentSync: {
notebookSelector: [{
notebook: {notebookType: 'jupyter-notebook'},
cells: [{language: 'python'}]
}]
}
}
return {capabilities, customResults: {hello: 'world'}}
})
connection.onInitialized(() => {
// Dynamic reg is folders + .js files with operation kind in the path
connection.client.register(DidCreateFilesNotification.type, {
filters: [{scheme: 'file', pattern: {glob: '**/created-dynamic/**{/,/*.js}'}}]
})
connection.client.register(DidRenameFilesNotification.type, {
filters: [
{scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/*.js', matches: 'file'}}
]
})
connection.client.register(DidDeleteFilesNotification.type, {
filters: [{scheme: 'file', pattern: {glob: '**/deleted-dynamic/**{/,/*.js}'}}]
})
connection.client.register(WillCreateFilesRequest.type, {
filters: [{scheme: 'file', pattern: {glob: '**/created-dynamic/**{/,/*.js}'}}]
})
connection.client.register(WillRenameFilesRequest.type, {
filters: [
{scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/', matches: 'folder'}},
{scheme: 'file', pattern: {glob: '**/renamed-dynamic/**/*.js', matches: 'file'}}
]
})
connection.client.register(WillDeleteFilesRequest.type, {
filters: [{scheme: 'file', pattern: {glob: '**/deleted-dynamic/**{/,/*.js}'}}]
})
connection.client.register(SignatureHelpRequest.type, {
triggerCharacters: [':'],
retriggerCharacters: [':']
}).then(d => {
disposables.push(d)
})
connection.client.register(WorkspaceSymbolRequest.type, {
workDoneProgress: false,
resolveProvider: true
}).then(d => {
disposables.push(d)
})
connection.client.register(DidChangeConfigurationNotification.type, {
section: 'http'
}).then(d => {
disposables.push(d)
})
connection.client.register(DidCreateFilesNotification.type, {
filters: [{
pattern: {
glob: '**/renamed-dynamic/**/',
matches: 'folder',
options: {
ignoreCase: true
}
}
}]
}).then(d => {
disposables.push(d)
})
})
connection.onNotification('unregister', () => {
for (let d of disposables) {
d.dispose()
disposables = []
}
})
connection.onCodeLens(params => {
return [{range: Range.create(0, 0, 0, 3)}, {range: Range.create(1, 0, 1, 3)}]
})
connection.onNotification('fireCodeLensRefresh', () => {
connection.sendRequest(CodeLensRefreshRequest.type)
})
connection.onNotification('fireSemanticTokensRefresh', () => {
connection.sendRequest(SemanticTokensRefreshRequest.type)
})
connection.onNotification('fireInlayHintsRefresh', () => {
connection.sendRequest(InlayHintRefreshRequest.type)
})
connection.onCodeLensResolve(codelens => {
return {range: codelens.range, command: {title: 'format', command: 'editor.action.format'}}
})
connection.onDeclaration(params => {
assert.equal(params.position.line, 1)
assert.equal(params.position.character, 1)
return {uri: params.textDocument.uri, range: {start: {line: 1, character: 1}, end: {line: 1, character: 2}}}
})
connection.onDefinition(params => {
assert.equal(params.position.line, 1)
assert.equal(params.position.character, 1)
return {uri: params.textDocument.uri, range: {start: {line: 0, character: 0}, end: {line: 0, character: 1}}}
})
connection.onHover(_params => {
return {
contents: {
kind: MarkupKind.PlainText,
value: 'foo'
}
}
})
connection.onCompletion(_params => {
return [
{label: 'item', insertText: 'text'}
]
})
connection.onCompletionResolve(item => {
item.detail = 'detail'
return item
})
connection.onSignatureHelp(_params => {
const result = {
signatures: [
SignatureInformation.create('label', 'doc', ParameterInformation.create('label', 'doc'))
],
activeSignature: 1,
activeParameter: 1
}
return result
})
connection.onReferences(params => {
return [
Location.create(params.textDocument.uri, Range.create(0, 0, 0, 0)),
Location.create(params.textDocument.uri, Range.create(1, 1, 1, 1))
]
})
connection.onDocumentHighlight(_params => {
return [
DocumentHighlight.create(Range.create(2, 2, 2, 2), DocumentHighlightKind.Read)
]
})
connection.onCodeAction(params => {
if (params.textDocument.uri.endsWith('empty.bat')) return undefined
return [
CodeAction.create('title', Command.create('title', 'test_command'))
]
})
connection.onExecuteCommand(params => {
if (params.command == 'test_command') {
return {success: true}
}
})
connection.onCodeActionResolve(codeAction => {
codeAction.title = 'resolved'
return codeAction
})
connection.onDocumentFormatting(_params => {
return [
TextEdit.insert(Position.create(0, 0), 'insert')
]
})
connection.onDocumentRangeFormatting(_params => {
return [
TextEdit.del(Range.create(1, 1, 1, 2))
]
})
connection.onDocumentOnTypeFormatting(_params => {
return [
TextEdit.replace(Range.create(2, 2, 2, 3), 'replace')
]
})
connection.onPrepareRename(_params => {
return Range.create(1, 1, 1, 2)
})
connection.onRenameRequest(_params => {
return {documentChanges: []}
})
connection.onDocumentLinks(_params => {
return [
DocumentLink.create(Range.create(1, 1, 1, 2))
]
})
connection.onDocumentLinkResolve(link => {
link.target = URI.file('/target.txt').toString()
return link
})
connection.onDocumentColor(_params => {
return [
ColorInformation.create(Range.create(1, 1, 1, 2), Color.create(1, 1, 1, 1))
]
})
connection.onColorPresentation(_params => {
return [
ColorPresentation.create('label')
]
})
connection.onFoldingRanges(_params => {
return [
FoldingRange.create(1, 2)
]
})
connection.onImplementation(params => {
assert.equal(params.position.line, 1)
assert.equal(params.position.character, 1)
return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}}
})
connection.onSelectionRanges(_params => {
return [
SelectionRange.create(Range.create(1, 2, 3, 4))
]
})
let lastFileOperationRequest
connection.workspace.onDidCreateFiles(params => {lastFileOperationRequest = {type: 'create', params}})
connection.workspace.onDidRenameFiles(params => {lastFileOperationRequest = {type: 'rename', params}})
connection.workspace.onDidDeleteFiles(params => {lastFileOperationRequest = {type: 'delete', params}})
connection.onRequest(
new ProtocolRequestType('testing/lastFileOperationRequest'),
() => {
return lastFileOperationRequest
},
)
connection.workspace.onWillCreateFiles(params => {
const createdFilenames = params.files.map(f => `${f.uri}`).join('\n')
return {
documentChanges: [{
textDocument: {uri: '/dummy-edit', version: null},
edits: [
TextEdit.insert(Position.create(0, 0), `WILL CREATE:\n${createdFilenames}`),
]
}],
}
})
connection.workspace.onWillRenameFiles(params => {
const renamedFilenames = params.files.map(f => `${f.oldUri} -> ${f.newUri}`).join('\n')
return {
documentChanges: [{
textDocument: {uri: '/dummy-edit', version: null},
edits: [
TextEdit.insert(Position.create(0, 0), `WILL RENAME:\n${renamedFilenames}`),
]
}],
}
})
connection.workspace.onWillDeleteFiles(params => {
const deletedFilenames = params.files.map(f => `${f.uri}`).join('\n')
return {
documentChanges: [{
textDocument: {uri: '/dummy-edit', version: null},
edits: [
TextEdit.insert(Position.create(0, 0), `WILL DELETE:\n${deletedFilenames}`),
]
}],
}
})
connection.onTypeDefinition(params => {
assert.equal(params.position.line, 1)
assert.equal(params.position.character, 1)
return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}}
})
connection.languages.callHierarchy.onPrepare(params => {
return [
{
kind: SymbolKind.Function,
name: 'name',
range: Range.create(1, 1, 1, 1),
selectionRange: Range.create(2, 2, 2, 2),
uri: params.textDocument.uri
}
]
})
connection.languages.callHierarchy.onIncomingCalls(params => {
return [
{
from: params.item,
fromRanges: [Range.create(1, 1, 1, 1)]
}
]
})
connection.languages.callHierarchy.onOutgoingCalls(params => {
return [
{
to: params.item,
fromRanges: [Range.create(1, 1, 1, 1)]
}
]
})
connection.languages.semanticTokens.onRange(() => {
return {
resultId: '1',
data: []
}
})
connection.languages.semanticTokens.on(() => {
return {
resultId: '2',
data: []
}
})
connection.languages.semanticTokens.onDelta(() => {
return {
resultId: '3',
data: []
}
})
connection.languages.diagnostics.on(() => {
return {
kind: DocumentDiagnosticReportKind.Full,
items: [
Diagnostic.create(Range.create(1, 1, 1, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}
})
connection.languages.diagnostics.onWorkspace(() => {
return {
items: [{
kind: DocumentDiagnosticReportKind.Full,
uri: 'uri',
version: 1,
items: [
Diagnostic.create(Range.create(1, 1, 1, 1), 'diagnostic', DiagnosticSeverity.Error)
]
}]
}
})
const typeHierarchySample = {
superTypes: [],
subTypes: []
}
connection.languages.typeHierarchy.onPrepare(params => {
const currentItem = {
kind: SymbolKind.Class,
name: 'ClazzB',
range: Range.create(1, 1, 1, 1),
selectionRange: Range.create(2, 2, 2, 2),
uri: params.textDocument.uri
}
typeHierarchySample.superTypes = [{...currentItem, name: 'classA', uri: 'uri-for-A'}]
typeHierarchySample.subTypes = [{...currentItem, name: 'classC', uri: 'uri-for-C'}]
return [currentItem]
})
connection.languages.typeHierarchy.onSupertypes(_params => {
return typeHierarchySample.superTypes
})
connection.languages.typeHierarchy.onSubtypes(_params => {
return typeHierarchySample.subTypes
})
connection.languages.inlineValue.on(_params => {
return [
InlineValueText.create(Range.create(1, 2, 3, 4), 'text'),
InlineValueVariableLookup.create(Range.create(1, 2, 3, 4), 'variableName', false),
InlineValueEvaluatableExpression.create(Range.create(1, 2, 3, 4), 'expression'),
]
})
connection.languages.inlayHint.on(() => {
const one = InlayHint.create(Position.create(1, 1), [InlayHintLabelPart.create('type')], InlayHintKind.Type)
one.data = '1'
const two = InlayHint.create(Position.create(2, 2), [InlayHintLabelPart.create('parameter')], InlayHintKind.Parameter)
two.data = '2'
return [one, two]
})
connection.languages.inlayHint.resolve(hint => {
if (typeof hint.label === 'string') {
hint.label = 'tooltip'
} else {
hint.label[0].tooltip = 'tooltip'
}
return hint
})
connection.languages.onLinkedEditingRange(() => {
return {
ranges: [Range.create(1, 1, 1, 1)],
wordPattern: '\\w'
}
})
connection.onRequest(
new ProtocolRequestType('testing/sendSampleProgress'),
async (_, __) => {
const progressToken = 'TEST-PROGRESS-TOKEN'
await connection.sendRequest(WorkDoneProgressCreateRequest.type, {token: progressToken})
void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'begin', title: 'Test Progress'})
void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'report', percentage: 50, message: 'Halfway!'})
void connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'end', message: 'Completed!'})
},
)
connection.onRequest(
new ProtocolRequestType('testing/beginOnlyProgress'),
async (_, __) => {
const progressToken = 'TEST-PROGRESS-BEGIN'
await connection.sendRequest(WorkDoneProgressCreateRequest.type, {token: progressToken})
},
)
connection.onWorkspaceSymbol(() => {
return [
{name: 'name', kind: SymbolKind.Array, location: {uri: 'file:///abc.txt'}}
]
})
connection.onWorkspaceSymbolResolve(symbol => {
symbol.location = Location.create(symbol.location.uri, Range.create(1, 2, 3, 4))
return symbol
})
// Listen on the connection
connection.listen()

13
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/timeoutOnShutdownServer.js

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
const node_1 = require("vscode-languageserver")
const connection = (0, node_1.createConnection)()
connection.onInitialize((_params) => {
return {capabilities: {}}
})
connection.onShutdown(async () => {
return new Promise((resolve) => {
setTimeout(resolve, 200000)
})
})
connection.listen()

324
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/textSynchronization.test.ts

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { v4 as uuidv4 } from 'uuid'
import { DidChangeTextDocumentNotification, DidCloseTextDocumentNotification, DidOpenTextDocumentNotification, DocumentSelector, Position, Range, TextDocumentSaveReason, TextEdit, WillSaveTextDocumentNotification } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { URI } from 'vscode-uri'
import { LanguageClient, LanguageClientOptions, Middleware, ServerOptions, TransportKind } from '../../language-client/index'
import { TextDocumentContentChange } from '../../types'
import workspace from '../../workspace'
import helper from '../helper'
function createClient(documentSelector: DocumentSelector | undefined | null, middleware: Middleware = {}, opts: any = {}): LanguageClient {
const serverModule = path.join(__dirname, './server/testDocuments.js')
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6014'] } }
}
if (documentSelector === undefined) documentSelector = [{ scheme: 'file' }]
const clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {},
initializationOptions: opts,
middleware
};
(clientOptions as ({ $testMode?: boolean })).$testMode = true
const result = new LanguageClient('test', 'Test Language Server', serverOptions, clientOptions)
return result
}
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = workspace.nvim
})
afterAll(async () => {
await helper.shutdown()
})
describe('TextDocumentSynchronization', () => {
describe('DidOpenTextDocumentFeature', () => {
it('should register with empty documentSelector', async () => {
let client = createClient(undefined)
await client.start()
let feature = client.getFeature(DidOpenTextDocumentNotification.method)
feature.register({ id: uuidv4(), registerOptions: { documentSelector: null } })
let res = await client.sendRequest('getLastOpen')
expect(res).toBe(null)
await client.stop()
})
it('should send event on document create', async () => {
let client = createClient([{ language: 'vim' }])
await client.start()
let uri = URI.file(path.join(os.tmpdir(), 't.vim'))
let doc = await workspace.loadFile(uri.toString())
expect(doc.languageId).toBe('vim')
let res = await client.sendRequest('getLastOpen') as any
expect(res.uri).toBe(doc.uri)
expect(res.version).toBe(doc.version)
await client.stop()
})
it('should work with middleware', async () => {
let called = false
let client = createClient([{ language: 'vim' }], {
didOpen: (doc, next) => {
called = true
return next(doc)
}
})
await client.start()
let uri = URI.file(path.join(os.tmpdir(), 't.js'))
let doc = await workspace.loadFile(uri.toString())
expect(doc.languageId).toBe('javascript')
let feature = client.getFeature(DidOpenTextDocumentNotification.method)
feature.register({ id: uuidv4(), registerOptions: { documentSelector: [{ language: 'javascript' }] } })
let res = await client.sendRequest('getLastOpen') as any
expect(res.uri).toBe(doc.uri)
expect(called).toBe(true)
await client.stop()
})
})
describe('DidCloseTextDocumentFeature', () => {
it('should send close event', async () => {
let uri = URI.file(path.join(os.tmpdir(), 't.vim'))
let doc = await workspace.loadFile(uri.toString())
let client = createClient([{ language: 'vim' }])
await client.start()
await workspace.nvim.command(`bd! ${doc.bufnr}`)
await helper.wait(30)
let res = await client.sendRequest('getLastClose') as any
expect(res.uri).toBe(doc.uri)
await client.stop()
})
it('should unregister document selector', async () => {
let called = false
let client = createClient([{ language: 'javascript' }], {
didClose: (e, next) => {
called = true
return next(e)
}
})
await client.start()
let openFeature = client.getFeature(DidOpenTextDocumentNotification.method)
let id = uuidv4()
let options = { id, registerOptions: { documentSelector: [{ language: 'vim' }] } }
openFeature.register(options)
let feature = client.getFeature(DidCloseTextDocumentNotification.method)
feature.register(options)
let uri = URI.file(path.join(os.tmpdir(), 't.vim'))
let doc = await workspace.loadFile(uri.toString())
await helper.wait(30)
feature.unregister(id)
feature.unregister('unknown')
await helper.wait(30)
let res = await client.sendRequest('getLastClose') as any
expect(res.uri).toBe(doc.uri)
expect(called).toBe(true)
await client.stop()
})
})
describe('DidChangeTextDocumentFeature', () => {
it('should send full change event ', async () => {
let called = false
let client = createClient([{ language: 'vim' }], {
didChange: (e, next) => {
called = true
return next(e)
}
})
await client.start()
let uri = URI.file(path.join(os.tmpdir(), 'x.vim'))
let doc = await workspace.loadFile(uri.toString())
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'bar')])
await client.forceDocumentSync()
let res = await client.sendRequest('getLastChange') as any
expect(res.text).toBe('bar\n')
expect(called).toBe(true)
await client.stop()
})
it('should send incremental change event', async () => {
let client = createClient([{ scheme: 'lsptest' }])
await client.start()
await client.sendNotification('registerDocumentSync')
await helper.wait(30)
let feature = client.getFeature(DidChangeTextDocumentNotification.method)
let fn = jest.fn()
feature.onNotificationSent(() => {
fn()
})
await nvim.command(`edit ${uuidv4()}.vim`)
let doc = await workspace.document
await nvim.call('setline', [1, 'foo'])
await doc.synchronize()
await client.forceDocumentSync()
await helper.wait(50)
await nvim.call('setline', [1, 'foo'])
await doc.synchronize()
expect(fn).toBeCalled()
let res = await client.sendRequest('getLastChange') as any
expect(res.uri).toBe(doc.uri)
expect(res.text).toBe('foo\n')
let provider = feature.getProvider(doc.textDocument)
expect(provider).toBeDefined()
await provider.send({ contentChanges: [], textDocument: { uri: doc.uri, version: doc.version }, bufnr: doc.bufnr, original: '', originalLines: [] })
await client.sendNotification('unregisterDocumentSync')
await helper.wait(10)
await client.stop()
})
it('should not send change event when syncKind is none', async () => {
let client = createClient([{ scheme: 'lsptest' }], {}, { none: true })
await client.start()
await client.sendNotification('registerDocumentSync')
await nvim.command('edit x.vim')
let doc = await workspace.document
let feature = client.getFeature(DidChangeTextDocumentNotification.method)
await helper.waitValue(() => {
return feature.getProvider(doc.textDocument) != null
}, true)
let provider = feature.getProvider(doc.textDocument)
let changes: TextDocumentContentChange[] = [{
range: Range.create(0, 0, 0, 0),
text: 'foo'
}]
await provider.send({ contentChanges: changes, textDocument: { uri: doc.uri, version: doc.version }, bufnr: doc.bufnr } as any)
let res = await client.sendRequest('getLastChange') as any
expect(res.text).toBe('\n')
await client.stop()
})
})
describe('WillSaveFeature', () => {
it('should will save event', async () => {
let called = false
let client = createClient([{ language: 'vim' }], {
willSave: (e, next) => {
called = true
return next(e)
}
})
await client.start()
let fsPath = path.join(os.tmpdir(), `${uuidv4()}.vim`)
let uri = URI.file(fsPath)
await workspace.openResource(uri.toString())
let doc = await workspace.document
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'bar')])
let feature = client.getFeature(WillSaveTextDocumentNotification.method)
let provider = feature.getProvider(doc.textDocument)
expect(provider).toBeDefined()
await provider.send({ document: doc.textDocument, reason: TextDocumentSaveReason.Manual, waitUntil: () => {} })
let res = await client.sendRequest('getLastWillSave') as any
expect(res.uri).toBe(doc.uri)
await client.stop()
expect(called).toBe(true)
if (fs.existsSync(fsPath)) {
fs.unlinkSync(fsPath)
}
})
})
describe('WillSaveWaitUntilFeature', () => {
it('should send will save until request', async () => {
let client = createClient([{ scheme: 'lsptest' }])
await client.start()
await client.sendNotification('registerDocumentSync')
await helper.wait(30)
let fsPath = path.join(os.tmpdir(), `${uuidv4()}-foo.vim`)
let uri = URI.file(fsPath)
await workspace.openResource(uri.toString())
let doc = await workspace.document
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'x')])
nvim.command('w', true)
await helper.waitValue(() => {
return doc.getDocumentContent()
}, 'abcx\n')
await client.sendNotification('unregisterDocumentSync')
await client.stop()
if (fs.existsSync(fsPath)) {
fs.unlinkSync(fsPath)
}
})
it('should not throw on response error', async () => {
let called = false
let client = createClient([], {
willSaveWaitUntil: (event, next) => {
called = true
return next(event)
}
})
await client.start()
await client.sendNotification('registerDocumentSync')
let fsPath = path.join(os.tmpdir(), `${uuidv4()}-error.vim`)
let uri = URI.file(fsPath)
await helper.waitValue(() => {
let feature = client.getFeature(DidOpenTextDocumentNotification.method)
let provider = feature.getProvider(TextDocument.create(uri.toString(), 'vim', 1, ''))
return provider != null
}, true)
await workspace.openResource(uri.toString())
let doc = await workspace.document
await doc.synchronize()
nvim.command('w', true)
await helper.waitValue(() => {
return called
}, true)
await client.stop()
})
it('should unregister event handler', async () => {
let client = createClient(null)
await client.start()
await client.sendNotification('registerDocumentSync')
await helper.waitValue(() => {
let feature = client.getFeature(DidOpenTextDocumentNotification.method)
let provider = feature.getProvider(TextDocument.create('file:///f.vim', 'vim', 1, ''))
return provider != null
}, true)
await client.sendNotification('unregisterDocumentSync')
await helper.waitValue(() => {
let feature = client.getFeature(DidOpenTextDocumentNotification.method)
let provider = feature.getProvider(TextDocument.create('file:///f.vim', 'vim', 1, ''))
return provider == null
}, true)
await client.stop()
})
})
describe('DidSaveTextDocumentFeature', () => {
it('should send did save notification', async () => {
let called = false
let client = createClient([{ language: 'vim' }], {
didSave: (e, next) => {
called = true
return next(e)
}
})
await client.start()
let fsPath = path.join(os.tmpdir(), `${uuidv4()}.vim`)
let uri = URI.file(fsPath)
await workspace.openResource(uri.toString())
let doc = await workspace.document
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'bar')])
nvim.command('w', true)
await helper.waitValue(() => {
return called
}, true)
let res = await client.sendRequest('getLastWillSave') as any
expect(res.uri).toBe(doc.uri)
await client.stop()
fs.unlinkSync(fsPath)
})
})
})

103
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/utils.test.ts

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
/* eslint-disable */
import assert from 'assert'
import { Delayer } from '../../language-client/utils/async'
import { ConsoleLogger, NullLogger } from '../../language-client/utils/logger'
import { wait } from '../../util/index'
import { CloseAction, DefaultErrorHandler, ErrorAction } from '../../language-client/utils/errorHandler'
import helper from '../helper'
test('Logger', async () => {
const logger = new ConsoleLogger()
logger.error('error')
logger.warn('warn')
logger.info('info')
logger.log('log')
const nullLogger = new NullLogger()
nullLogger.error('error')
nullLogger.warn('warn')
nullLogger.info('info')
nullLogger.log('log')
})
test('DefaultErrorHandler', async () => {
const handler = new DefaultErrorHandler('test', 2)
expect(handler.error(new Error('test'), { jsonrpc: '' }, 1)).toBe(ErrorAction.Continue)
expect(handler.error(new Error('test'), { jsonrpc: '' }, 5)).toBe(ErrorAction.Shutdown)
handler.closed()
handler.milliseconds = 1
await helper.wait(10)
let res = handler.closed()
expect(res).toBe(CloseAction.Restart)
handler.milliseconds = 10 * 1000
res = handler.closed()
expect(res).toBe(CloseAction.DoNotRestart)
})
test('Delayer', () => {
let count = 0
let factory = () => {
return Promise.resolve(++count)
}
let delayer = new Delayer(0)
let promises: Thenable<any>[] = []
assert(!delayer.isTriggered())
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) }))
assert(delayer.isTriggered())
return Promise.all(promises).then(() => {
assert(!delayer.isTriggered())
}).finally(() => {
delayer.dispose()
})
})
test('Delayer - forceDelivery', async () => {
let count = 0
let factory = () => {
return Promise.resolve(++count)
}
let delayer = new Delayer(150)
delayer.forceDelivery()
delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) })
await wait(10)
delayer.forceDelivery()
expect(count).toBe(1)
void delayer.trigger(factory)
delayer.trigger(factory, -1)
await wait(10)
delayer.cancel()
expect(count).toBe(1)
})
test('Delayer - last task should be the one getting called', function() {
let factoryFactory = (n: number) => () => {
return Promise.resolve(n)
}
let delayer = new Delayer(0)
let promises: Thenable<any>[] = []
assert(!delayer.isTriggered())
promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.equal(n, 3) }))
promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.equal(n, 3) }))
promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.equal(n, 3) }))
const p = Promise.all(promises).then(() => {
assert(!delayer.isTriggered())
})
assert(delayer.isTriggered())
return p
})

106
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/workspaceFolder.test.ts

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
'use strict'
import * as assert from 'assert'
import { WorkspaceFoldersFeature } from '../../language-client/workspaceFolders'
import { BaseLanguageClient, MessageTransports } from '../../language-client/client'
import { Disposable, DidChangeWorkspaceFoldersParams } from 'vscode-languageserver-protocol'
import * as proto from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
class TestLanguageClient extends BaseLanguageClient {
protected createMessageTransports(): Promise<MessageTransports> {
throw new Error('Method not implemented.')
}
public onRequest(): Disposable {
return {
dispose: () => {}
}
}
}
type MaybeFolders = proto.WorkspaceFolder[] | undefined
class TestWorkspaceFoldersFeature extends WorkspaceFoldersFeature {
public sendInitialEvent(currentWorkspaceFolders: MaybeFolders): void {
super.sendInitialEvent(currentWorkspaceFolders)
}
public initializeWithFolders(currentWorkspaceFolders: MaybeFolders) {
super.initializeWithFolders(currentWorkspaceFolders)
}
}
function testEvent(initial: MaybeFolders, then: MaybeFolders, added: proto.WorkspaceFolder[], removed: proto.WorkspaceFolder[]) {
const client = new TestLanguageClient('foo', 'bar', {})
let arg: any
let spy = jest.spyOn(client, 'sendNotification').mockImplementation((_p1, p2) => {
arg = p2
return Promise.resolve()
})
const feature = new TestWorkspaceFoldersFeature(client)
feature.initializeWithFolders(initial)
feature.sendInitialEvent(then)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledTimes(1)
const notification: DidChangeWorkspaceFoldersParams = arg
assert.deepEqual(notification.event.added, added)
assert.deepEqual(notification.event.removed, removed)
}
function testNoEvent(initial: MaybeFolders, then: MaybeFolders) {
const client = new TestLanguageClient('foo', 'bar', {})
let spy = jest.spyOn(client, 'sendNotification').mockImplementation(() => {
return Promise.resolve()
})
const feature = new TestWorkspaceFoldersFeature(client)
feature.initializeWithFolders(initial)
feature.sendInitialEvent(then)
expect(spy).toHaveBeenCalledTimes(0)
}
describe('Workspace Folder Feature Tests', () => {
const removedFolder = { uri: URI.parse('file://xox/removed').toString(), name: 'removedName', index: 0 }
const addedFolder = { uri: URI.parse('file://foo/added').toString(), name: 'addedName', index: 0 }
const addedProto = { uri: 'file://foo/added', name: 'addedName' }
const removedProto = { uri: 'file://xox/removed', name: 'removedName' }
test('remove/add', async () => {
assert.ok(!MessageTransports.is({}))
testEvent([removedFolder], [addedFolder], [addedProto], [removedProto])
})
test('remove', async () => {
testEvent([removedFolder], [], [], [removedProto])
})
test('remove2', async () => {
testEvent([removedFolder], undefined, [], [removedProto])
})
test('add', async () => {
testEvent([], [addedFolder], [addedProto], [])
})
test('add2', async () => {
testEvent(undefined, [addedFolder], [addedProto], [])
})
test('noChange1', async () => {
testNoEvent([addedFolder, removedFolder], [addedFolder, removedFolder])
})
test('noChange2', async () => {
testNoEvent([], [])
})
test('noChange3', async () => {
testNoEvent(undefined, undefined)
})
})

4
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/coc-settings.json

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
{
"suggest.timeout": 5000,
"tslint.enable": false
}

1325
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/basic.test.ts

File diff suppressed because it is too large Load Diff

179
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/float.test.ts

@ -0,0 +1,179 @@ @@ -0,0 +1,179 @@
import { Neovim } from '@chemzqm/neovim'
import Floating from '../../completion/floating'
import { fixFollow } from '../../completion/pum'
import sources from '../../sources'
import { CompleteResult, FloatConfig, ISource, SourceType } from '../../types'
import helper from '../helper'
let nvim: Neovim
let source: ISource
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
source = {
name: 'float',
priority: 10,
enable: true,
sourceType: SourceType.Native,
doComplete: (): Promise<CompleteResult> => Promise.resolve({
items: [{
word: 'foo',
info: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
}, {
word: 'foot',
info: 'foot'
}, {
word: 'football',
}]
})
}
sources.addSource(source)
})
afterAll(async () => {
sources.removeSource(source)
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('completion float', () => {
it('should fix word by check follow', async () => {
expect(fixFollow('foo', '', '')).toBe('foo')
expect(fixFollow('foobar', '', 'oobar')).toBe('f')
expect(fixFollow('foobar', 'f', 'oobar')).toBe('f')
expect(fixFollow('foobar', 'foo', 'oobar')).toBe('foobar')
})
it('should cancel float window', async () => {
await helper.edit()
await nvim.input('if')
await helper.visible('foo', 'float')
let items = await helper.getItems()
expect(items[0].word).toBe('foo')
expect(items[0].info.length > 0).toBeTruthy()
await helper.confirmCompletion(0)
let hasFloat = await nvim.call('coc#float#has_float')
expect(hasFloat).toBe(0)
})
it('should adjust float window position', async () => {
await helper.edit()
await nvim.setLine(' '.repeat(70))
await nvim.input('Af')
await helper.visible('foo', 'float')
let floatWin = await helper.getFloat('pumdetail')
let config = await floatWin.getConfig()
expect(config.col + config.width).toBeLessThan(180)
})
it('should redraw float window on item change', async () => {
await helper.edit()
await nvim.setLine(' '.repeat(70))
await nvim.input('Af')
await helper.visible('foo', 'float')
await nvim.call('coc#pum#select', [1, 1, 0])
let floatWin = await helper.getFloat('pumdetail')
let buf = await floatWin.buffer
let lines = await buf.lines
expect(lines.length).toBeGreaterThan(0)
expect(lines[0]).toMatch('foot')
})
it('should hide float window when item info is empty', async () => {
await helper.edit()
await nvim.setLine(' '.repeat(70))
await nvim.input('Af')
await helper.visible('foo', 'float')
await nvim.call('coc#pum#select', [2, 1, 0])
let floatWin = await helper.getFloat('pumdetail')
expect(floatWin).toBeUndefined()
})
it('should hide float window after completion', async () => {
await helper.edit()
await nvim.setLine(' '.repeat(70))
await nvim.input('Af')
await helper.visible('foo', 'float')
await nvim.input('<C-n>')
await helper.wait(30)
await nvim.input('<C-y>')
await helper.wait(30)
let floatWin = await helper.getFloat('pumdetail')
expect(floatWin).toBeUndefined()
})
})
describe('float config', () => {
beforeEach(async () => {
await nvim.input('of')
await helper.waitPopup()
})
async function createFloat(config: Partial<FloatConfig>, docs = [{ filetype: 'txt', content: 'doc' }]): Promise<Floating> {
let floating = new Floating(nvim, {
floatConfig: {
border: true,
...config
}
})
floating.show(docs)
return floating
}
async function getFloat(): Promise<number> {
let win = await helper.getFloat('pumdetail')
return win ? win.id : -1
}
async function getRelated(winid: number, kind: string): Promise<number> {
if (!winid || winid == -1) return -1
let win = nvim.createWindow(winid)
let related = await win.getVar('related') as number[]
if (!related || !related.length) return -1
for (let id of related) {
let w = nvim.createWindow(id)
let v = await w.getVar('kind')
if (v == kind) {
return id
}
}
return -1
}
it('should not shown with empty lines', async () => {
await createFloat({}, [{ filetype: 'txt', content: '' }])
let floatWin = await helper.getFloat('pumdetail')
expect(floatWin).toBeUndefined()
})
it('should show window with border', async () => {
await createFloat({ border: true, rounded: true, focusable: true })
let winid = await getFloat()
expect(winid).toBeGreaterThan(0)
let id = await getRelated(winid, 'border')
expect(id).toBeGreaterThan(0)
})
it('should change window highlights', async () => {
await createFloat({ border: true, highlight: 'WarningMsg', borderhighlight: 'MoreMsg' })
let winid = await getFloat()
expect(winid).toBeGreaterThan(0)
let win = nvim.createWindow(winid)
let res = await win.getOption('winhl') as string
expect(res).toMatch('WarningMsg')
let id = await getRelated(winid, 'border')
expect(id).toBeGreaterThan(0)
win = nvim.createWindow(id)
res = await win.getOption('winhl') as string
expect(res).toMatch('MoreMsg')
})
it('should add shadow and winblend', async () => {
await createFloat({ shadow: true, winblend: 30 })
let winid = await getFloat()
expect(winid).toBeGreaterThan(0)
})
})

617
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/language.test.ts

@ -0,0 +1,617 @@ @@ -0,0 +1,617 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import { CompletionItem, CompletionList, InsertReplaceEdit, InsertTextFormat, InsertTextMode, Position, Range, TextEdit } from 'vscode-languageserver-types'
import completion from '../../completion'
import languages from '../../languages'
import { CompletionItemProvider } from '../../provider'
import snippetManager from '../../snippets/manager'
import { getRange, emptLabelDetails, getStartColumn, ItemDefaults } from '../../sources/source-language'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('LanguageSource util', () => {
describe('emptLabelDetails', () => {
it('should check emptLabelDetails', async () => {
expect(emptLabelDetails(null)).toBe(true)
expect(emptLabelDetails({})).toBe(true)
expect(emptLabelDetails({ detail: '' })).toBe(true)
expect(emptLabelDetails({ detail: 'detail' })).toBe(false)
expect(emptLabelDetails({ description: 'detail' })).toBe(false)
})
})
describe('getStartColumn()', () => {
it('should get start col', async () => {
expect(getStartColumn('', [{ label: 'foo' }])).toBe(undefined)
expect(getStartColumn('', [{ label: 'foo' }], { editRange: Range.create(0, 0, 0, 3) })).toBe(0)
expect(getStartColumn('', [
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') },
{ label: 'bar' }])).toBe(undefined)
expect(getStartColumn('foo', [
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') },
{ label: 'bar', textEdit: TextEdit.insert(Position.create(0, 1), 'b') }])).toBe(undefined)
expect(getStartColumn('foo', [
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 2), 'a') },
{ label: 'bar', textEdit: TextEdit.insert(Position.create(0, 2), 'b') }])).toBe(2)
})
})
describe('getRange()', () => {
it('should use range from textEdit', async () => {
let item = { label: 'foo', textEdit: TextEdit.replace(Range.create(0, 1, 0, 3), 'foo') }
let res = getRange(item, { editRange: Range.create(0, 0, 0, 0) })
expect(res).toEqual(Range.create(0, 1, 0, 3))
})
it('should use range from itemDefaults', async () => {
let item = { label: 'foo' }
expect(getRange(item, { editRange: Range.create(0, 0, 0, 1) })).toEqual(Range.create(0, 0, 0, 1))
expect(getRange(item, { editRange: InsertReplaceEdit.create('', Range.create(0, 0, 0, 0), Range.create(0, 0, 0, 1)) })).toEqual(Range.create(0, 0, 0, 1))
})
})
})
describe('language source', () => {
describe('resolveCompletionItem()', () => {
async function getDetailContent(): Promise<string | undefined> {
let winid = await nvim.call('coc#float#get_float_by_kind', ['pumdetail'])
if (!winid) return
let bufnr = await nvim.call('winbufnr', [winid])
let lines = await (nvim.createBuffer(bufnr)).lines
return lines.join('\n')
}
it('should add detail to preview when no resolve exists', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
detail: 'detail of foo'
}, {
label: 'bar',
detail: 'bar()'
}]
}
disposables.push(languages.registerCompletionItemProvider('foo', 'f', null, provider))
let mode = await nvim.mode
if (mode.mode !== 'i') {
await nvim.input('i')
}
nvim.call('coc#start', { source: 'foo' }, true)
await helper.waitPopup()
await helper.waitValue(async () => {
let content = await getDetailContent()
return content && /foo/.test(content)
}, true)
await nvim.input('<C-n>')
await helper.waitValue(async () => {
let content = await getDetailContent()
return content && /bar/.test(content)
}, true)
})
it('should add documentation to preview when no resolve exists', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
labelDetails: {},
documentation: 'detail of foo'
}, {
label: 'bar',
documentation: {
kind: 'plaintext',
value: 'bar'
}
}]
}
disposables.push(languages.registerCompletionItemProvider('foo', 'f', null, provider))
await nvim.input('i')
await nvim.call('coc#start', { source: 'foo' })
await helper.waitPopup()
await helper.wait(10)
let content = await getDetailContent()
expect(content).toMatch('foo')
await nvim.input('<C-n>')
await helper.wait(30)
content = await getDetailContent()
expect(content).toMatch('bar')
})
it('should resolve again when request cancelled', async () => {
let count = 0
let cancelled = false
let resolved = false
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'this'
}, {
label: 'other',
}, {
label: 'third',
}],
resolveCompletionItem: (item, token) => {
if (item.label === 'this') {
count++
if (count == 1) {
return new Promise(resolve => {
token.onCancellationRequested(() => {
cancelled = true
clearTimeout(timer)
resolve(undefined)
})
let timer = setTimeout(() => {
resolve(item)
}, 1000)
})
} else {
resolved = true
item.documentation = 'doc of this'
}
}
return item
}
}
disposables.push(languages.registerCompletionItemProvider('foo', 'f', null, provider))
await nvim.input('i')
await nvim.call('coc#start', { source: 'foo' })
await helper.waitPopup()
await nvim.input('<C-n>')
await helper.waitValue(() => {
return cancelled
}, true)
await nvim.input('<C-p>')
await helper.waitValue(() => {
return resolved
}, true)
})
it('should resolve once for same CompletionItem', async () => {
let count = 0
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'this',
documentation: 'detail of this'
}],
resolveCompletionItem: item => {
if (item.label === 'this') {
count++
}
return item
}
}
disposables.push(languages.registerCompletionItemProvider('foo', 'f', null, provider))
await nvim.input('i')
await nvim.call('coc#start', { source: 'foo' })
await helper.waitPopup()
await nvim.input('h')
await helper.wait(20)
await nvim.input('i')
await helper.wait(20)
expect(count).toBe(1)
})
})
describe('labelDetails', () => {
it('should show labelDetails to documentation window', async () => {
helper.updateConfiguration('suggest.labelMaxLength', 10)
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
labelDetails: {
detail: 'foo'.repeat(5)
}
}, {
label: 'bar',
labelDetails: {
description: 'bar'.repeat(5)
}
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('i')
await nvim.call('coc#start', { source: 'edits' })
let winid: number
await helper.waitValue(async () => {
winid = await nvim.call('coc#float#get_float_by_kind', ['pumdetail'])
return winid > 0
}, true)
let lines = await helper.getLines(winid)
expect(lines[0]).toMatch('foo')
await nvim.call('coc#pum#next', [1])
await helper.waitValue(async () => {
lines = await helper.getLines(winid)
return lines.join(' ').includes('bar')
}, true)
})
})
describe('additionalTextEdits', () => {
it('should fix cursor position with plain text on additionalTextEdits', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
filterText: 'foo',
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'a\nbar')]
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('if')
await helper.waitPopup()
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'barfoo')
})
it('should fix cursor position with snippet on additionalTextEdits', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'if',
insertTextFormat: InsertTextFormat.Snippet,
textEdit: { range: Range.create(0, 0, 0, 1), newText: 'if($1)' },
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')],
preselect: true
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('ii')
await helper.waitPopup()
let res = await helper.getItems()
let idx = res.findIndex(o => o.source == 'edits')
await helper.confirmCompletion(idx)
await helper.waitFor('col', ['.'], 8)
})
it('should fix cursor position with plain text snippet on additionalTextEdits', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'if',
insertTextFormat: InsertTextFormat.Snippet,
textEdit: { range: Range.create(0, 0, 0, 2), newText: 'do$0' },
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')],
preselect: true
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('iif')
await helper.waitPopup()
let items = await helper.getItems()
let idx = items.findIndex(o => o.word == 'do' && o.source == 'edits')
await helper.confirmCompletion(idx)
await helper.waitFor('getline', ['.'], 'bar do')
await helper.waitFor('col', ['.'], 7)
})
it('should fix cursor position with nested snippet on additionalTextEdits', async () => {
let res = await snippetManager.insertSnippet('func($1)$0')
expect(res).toBe(true)
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'if',
insertTextFormat: InsertTextFormat.Snippet,
insertText: 'do$0',
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')],
preselect: true
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('if')
await helper.waitPopup()
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'bar func(do)')
let [, lnum, col] = await nvim.call('getcurpos')
expect(lnum).toBe(1)
expect(col).toBe(12)
})
it('should fix cursor position and keep placeholder with snippet on additionalTextEdits', async () => {
let text = 'foo0bar1'
await nvim.setLine(text)
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'var',
insertTextFormat: InsertTextFormat.Snippet,
textEdit: { range: Range.create(0, text.length + 1, 0, text.length + 1), newText: '${1:foo} = foo0bar1' },
additionalTextEdits: [TextEdit.del(Range.create(0, 0, 0, text.length + 1))],
preselect: true
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider, ['.']))
await nvim.input('A.')
await helper.waitPopup()
let res = await helper.getItems()
let idx = res.findIndex(o => o.source == 'edits')
await helper.confirmCompletion(idx)
await helper.waitFor('getline', ['.'], 'foo = foo0bar1')
await helper.wait(50)
expect(snippetManager.session).toBeDefined()
let [, lnum, col] = await nvim.call('getcurpos')
expect(lnum).toBe(1)
expect(col).toBe(3)
})
it('should cancel current snippet session when additionalTextEdits inside snippet', async () => {
await nvim.input('i')
await snippetManager.insertSnippet('foo($1, $2)$0', true)
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'bar',
insertTextFormat: InsertTextFormat.Snippet,
textEdit: { range: Range.create(0, 4, 0, 5), newText: 'bar($1)' },
additionalTextEdits: [TextEdit.del(Range.create(0, 0, 0, 3))]
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider, ['.']))
await nvim.input('b')
await helper.waitPopup()
let res = await helper.getItems()
let idx = res.findIndex(o => o.source == 'edits')
await helper.confirmCompletion(idx)
await helper.waitFor('getline', ['.'], '(bar(), )')
let col = await nvim.call('col', ['.'])
expect(col).toBe(6)
})
})
describe('filterText', () => {
it('should fix input for snippet item', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
filterText: 'foo',
insertText: '${1:foo}($2)',
insertTextFormat: InsertTextFormat.Snippet,
}]
}
disposables.push(languages.registerCompletionItemProvider('snippets-test', 'st', null, provider))
await nvim.input('if')
await helper.waitPopup()
await nvim.call('coc#pum#select', [0, 1, 0])
await helper.waitFor('getline', ['.'], 'foo')
})
it('should fix filterText of complete item', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'name',
sortText: '11',
textEdit: {
range: Range.create(0, 1, 0, 2),
newText: '?.name'
}
}]
}
disposables.push(languages.registerCompletionItemProvider('name', 'N', null, provider, ['.']))
await nvim.setLine('t')
await nvim.input('A.')
await helper.waitPopup()
await helper.confirmCompletion(0)
let line = await nvim.line
expect(line).toBe('t?.name')
})
})
describe('inComplete result', () => {
it('should filter in complete request', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (doc, pos, token, context): Promise<CompletionList> => {
let option = (context as any).option
if (context.triggerCharacter == '.') {
return {
isIncomplete: true,
items: [
{
label: 'foo'
}, {
label: 'bar'
}
]
}
}
if (option.input == 'f') {
if (token.isCancellationRequested) return
return {
isIncomplete: true,
items: [
{
label: 'foo'
}
]
}
}
if (option.input == 'fo') {
if (token.isCancellationRequested) return
return {
isIncomplete: false,
items: [
{
label: 'foo'
}
]
}
}
}
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider, ['.']))
await nvim.input('i.')
await helper.waitPopup()
await nvim.input('fo')
await helper.waitValue(async () => {
let items = await helper.getItems()
return items.length
}, 1)
})
})
describe('itemDefaults', () => {
async function start(item: CompletionItem, itemDefaults: ItemDefaults): Promise<void> {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionList> => {
return { items: [item], itemDefaults, isIncomplete: false }
}
}
disposables.push(languages.registerCompletionItemProvider('test', 't', null, provider))
await nvim.input('i')
nvim.call('coc#start', [{ source: 'test' }], true)
await helper.waitPopup()
}
it('should use commitCharacters from itemDefaults', async () => {
helper.updateConfiguration('suggest.acceptSuggestionOnCommitCharacter', true)
await start({ label: 'foo' }, { commitCharacters: ['.'] })
await nvim.input('.')
await helper.waitFor('getline', ['.'], 'foo.')
})
it('should use range of editRange from itemDefaults', async () => {
await nvim.call('setline', ['.', 'bar'])
await start({ label: 'foo' }, {
editRange: Range.create(0, 0, 0, 3)
})
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'foo')
})
it('should use replace range of editRange from itemDefaults', async () => {
await nvim.call('setline', ['.', 'bar'])
await start({ label: 'foo' }, {
editRange: {
insert: Range.create(0, 0, 0, 0),
replace: Range.create(0, 0, 0, 3),
}
})
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'foo')
})
it('should use insertTextFormat from itemDefaults', async () => {
await start({ label: 'foo', insertText: 'foo($1)$0' }, {
insertTextFormat: InsertTextFormat.Snippet,
insertTextMode: InsertTextMode.asIs,
data: {}
})
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'foo()')
})
})
describe('textEdit', () => {
it('should fix bad range', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'foo',
filterText: 'foo',
textEdit: { range: Range.create(0, 0, 0, 0), newText: 'foo' },
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('i')
nvim.call('coc#start', [{ source: 'edits' }], true)
await helper.waitPopup()
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'foo')
})
it('should applyEdits for empty word', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: '',
filterText: '!',
textEdit: { range: Range.create(0, 0, 0, 1), newText: 'foo' },
data: { word: '' }
}]
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider, ['!']))
await nvim.input('i!')
await helper.waitPopup()
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'foo')
})
it('should provide word when textEdit after startcol', async () => {
// some LS would send textEdit after first character,
// need fix the word from newText
let provider: CompletionItemProvider = {
provideCompletionItems: async (_, position): Promise<CompletionItem[]> => {
if (position.line != 0) return null
return [{
label: 'bar',
textEdit: {
range: Range.create(0, 1, 0, 1),
newText: 'bar'
}
}, {
label: 'bad',
textEdit: {
replace: Range.create(0, 1, 0, 1),
insert: Range.create(0, 1, 0, 1),
newText: 'bad'
}
}]
}
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('ib')
await helper.waitPopup()
let items = completion.activeItems
expect(items[0].word).toBe('bar')
})
it('should adjust completion position by textEdit start position', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (_document, _position, _token, context): Promise<CompletionItem[]> => {
if (!context.triggerCharacter) return
return [{
label: 'foo',
textEdit: {
range: Range.create(0, 0, 0, 1),
newText: '?foo'
}
}]
}
}
disposables.push(languages.registerCompletionItemProvider('fix', 'f', null, provider, ['?']))
await nvim.input('i?')
await helper.waitPopup()
await helper.confirmCompletion(0)
let line = await nvim.line
expect(line).toBe('?foo')
})
it('should fix range of removed text range', async () => {
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => {
return [{
label: 'React',
textEdit: {
range: Range.create(0, 0, 0, 8),
newText: 'import React$1 from "react"'
},
insertTextFormat: InsertTextFormat.Snippet
}]
}
}
disposables.push(languages.registerCompletionItemProvider('fix', 'f', null, provider, ['?']))
await nvim.call('setline', ['.', 'import r;'])
await nvim.call('cursor', [1, 8])
await nvim.input('a')
await nvim.call('coc#start', { source: 'fix' })
await helper.waitPopup()
await helper.confirmCompletion(0)
await helper.waitFor('getline', ['.'], 'import React from "react";')
})
})
})

120
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/sources.test.ts

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
import { Neovim } from '@chemzqm/neovim'
import { Position, Range, TextEdit } from 'vscode-languageserver-types'
import sources from '../../sources/index'
import { matchLine } from '../../sources/keywords'
import workspace from '../../workspace'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('utils', () => {
it('should matchLine', async () => {
let doc = await workspace.document
let text = 'a'.repeat(2048)
expect(matchLine(text, doc.chars)).toEqual(['a'.repeat(1024)])
expect(matchLine('a b c', doc.chars)).toEqual([])
expect(matchLine('foo bar', doc.chars)).toEqual(['foo', 'bar'])
expect(matchLine('?foo bar', doc.chars)).toEqual(['foo', 'bar'])
expect(matchLine('?foo $', doc.chars)).toEqual(['foo'])
expect(matchLine('?foo foo foo', doc.chars)).toEqual(['foo'])
})
})
describe('KeywordsBuffer', () => {
it('should parse keywords', async () => {
let filepath = await createTmpFile(' ab')
let doc = await helper.createDocument(filepath)
let b = sources.getKeywordsBuffer(doc.bufnr)
let words = b.getWords()
expect(words).toEqual(['ab'])
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')])
words = b.getWords()
expect(words).toEqual(['foo', 'bar', 'ab'])
await doc.applyEdits([TextEdit.replace(Range.create(0, 0, 1, 3), 'def ')])
words = b.getWords()
expect(words).toEqual(['def', 'ab'])
})
it('should fuzzy for matchKeywords', async () => {
let filepath = await createTmpFile(`_foo\nbar\n`)
let doc = await helper.createDocument(filepath)
let b = sources.getKeywordsBuffer(doc.bufnr)
const getResults = (iterable: Iterable<string>) => {
let res: string[] = []
for (let word of iterable) {
res.push(word)
}
return res
}
let iterable = b.matchWords(0, 'br', true)
expect(getResults(iterable)).toEqual(['bar'])
iterable = b.matchWords(0, 'f', true)
expect(getResults(iterable)).toEqual(['_foo'])
iterable = b.matchWords(0, '_', true)
expect(getResults(iterable)).toEqual(['_foo'])
})
it('should match by unicode', async () => {
let filepath = await createTmpFile(`aéà\nàçé\n`)
let doc = await helper.createDocument(filepath)
let b = sources.getKeywordsBuffer(doc.bufnr)
const getResults = (iterable: Iterable<string>) => {
let res: string[] = []
for (let word of iterable) {
res.push(word)
}
return res
}
let iterable = b.matchWords(0, 'ae', true)
expect(getResults(iterable)).toEqual([
'aéà', 'àçé'
])
})
})
describe('native sources', () => {
it('should works for around source', async () => {
let doc = await workspace.document
await nvim.setLine('foo ')
await doc.synchronize()
let { mode } = await nvim.mode
expect(mode).toBe('n')
await nvim.input('Af')
await helper.waitPopup()
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
await nvim.input('<esc>')
})
it('should works for buffer source', async () => {
await helper.createDocument()
await nvim.command('set hidden')
let doc = await helper.createDocument()
await nvim.setLine('other')
await nvim.command('bp')
await doc.synchronize()
let { mode } = await nvim.mode
expect(mode).toBe('n')
await nvim.input('io')
let res = await helper.visible('other', 'buffer')
expect(res).toBe(true)
})
it('should works with file source', async () => {
await helper.edit()
await nvim.input('i/')
await helper.waitPopup()
})
})

317
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/util.test.ts

@ -0,0 +1,317 @@ @@ -0,0 +1,317 @@
import { Neovim } from '@chemzqm/neovim'
import { CompletionItemKind, Disposable, Position, Range } from 'vscode-languageserver-protocol'
import { caseScore, matchScore, matchScoreWithPositions } from '../../completion/match'
import { checkIgnoreRegexps, indentChanged, createKindMap, getInput, getKindText, getResumeInput, getValidWord, highlightOffert, shouldIndent, shouldStop } from '../../completion/util'
import { WordDistance } from '../../completion/wordDistance'
import languages from '../../languages'
import { CompleteOption } from '../../types'
import { disposeAll } from '../../util'
import { getCharCodes } from '../../util/fuzzy'
import workspace from '../../workspace'
import events from '../../events'
import helper, { createTmpFile } from '../helper'
let disposables: Disposable[] = []
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(() => {
disposeAll(disposables)
})
describe('caseScore()', () => {
it('should get caseScore', () => {
expect(typeof caseScore(10, 10, 2)).toBe('number')
})
})
describe('indentChanged()', () => {
it('should check indentChanged', async () => {
expect(indentChanged(undefined, [1, 1, ''], '')).toBe(false)
expect(indentChanged({ word: 'foo' }, [1, 4, 'foo'], ' foo')).toBe(true)
expect(indentChanged({ word: 'foo' }, [1, 4, 'bar'], ' foo')).toBe(false)
})
})
describe('highlightOffert()', () => {
it('should get highlight offset', () => {
let n = highlightOffert(3, { abbr: 'abc', word: '', filterText: 'def' })
expect(n).toBe(-1)
expect(highlightOffert(3, { abbr: 'abc', word: '', filterText: 'abc' })).toBe(3)
expect(highlightOffert(3, { abbr: 'xy abc', word: '', filterText: 'abc' })).toBe(6)
})
})
describe('getKindText()', () => {
it('should getKindText', async () => {
expect(getKindText('t', new Map(), '')).toBe('t')
let m = new Map()
m.set(CompletionItemKind.Class, 'C')
expect(getKindText(CompletionItemKind.Class, m, 'D')).toBe('C')
expect(getKindText(CompletionItemKind.Class, new Map(), 'D')).toBe('D')
})
})
describe('createKindMap()', () => {
it('should createKindMap', async () => {
let map = createKindMap({ constructor: 'C' })
expect(map.get(CompletionItemKind.Constructor)).toBe('C')
map = createKindMap({ constructor: undefined })
expect(map.get(CompletionItemKind.Constructor)).toBe('')
})
})
describe('getValidWord()', () => {
it('should getValidWord', async () => {
expect(getValidWord('label', [])).toBe('label')
})
})
describe('checkIgnoreRegexps()', () => {
it('should checkIgnoreRegexps', async () => {
expect(checkIgnoreRegexps([], '')).toBe(false)
expect(checkIgnoreRegexps(['^^*^^'], 'input')).toBe(false)
expect(checkIgnoreRegexps(['^inp', '^ind'], 'input')).toBe(true)
})
})
describe('getResumeInput()', () => {
it('should getResumeInput', async () => {
let opt = { line: 'foo', colnr: 4, col: 1 }
expect(getResumeInput(opt, 'f')).toBeNull()
expect(getResumeInput(opt, 'bar')).toBeNull()
expect(getResumeInput(opt, 'foo f')).toBeNull()
})
})
describe('shouldStop()', () => {
function createOption(bufnr: number, linenr: number, line: string, colnr: number): Pick<CompleteOption, 'bufnr' | 'linenr' | 'line' | 'colnr'> {
return { bufnr, linenr, line, colnr }
}
it('should check stop', async () => {
let opt = createOption(1, 1, 'a', 2)
expect(shouldStop(1, 'foo', { line: '', col: 2, lnum: 1, changedtick: 1, pre: '' }, opt)).toBe(true)
expect(shouldStop(1, 'foo', { line: '', col: 2, lnum: 1, changedtick: 1, pre: ' ' }, opt)).toBe(true)
expect(shouldStop(1, 'foo', { line: '', col: 2, lnum: 1, changedtick: 1, pre: 'fo' }, opt)).toBe(true)
expect(shouldStop(2, 'foo', { line: '', col: 2, lnum: 1, changedtick: 1, pre: 'foob' }, opt)).toBe(true)
expect(shouldStop(1, 'foo', { line: '', col: 2, lnum: 2, changedtick: 1, pre: 'foob' }, opt)).toBe(true)
expect(shouldStop(1, 'foo', { line: '', col: 2, lnum: 1, changedtick: 1, pre: 'barb' }, opt)).toBe(true)
})
})
describe('shouldIndent()', () => {
it('should check indent', async () => {
let res = shouldIndent('0{,0},0),0],!^F,o,O,e,=endif,=enddef,=endfu,=endfor', 'endfor')
expect(res).toBe(true)
res = shouldIndent('', 'endfor')
expect(res).toBe(false)
res = shouldIndent('0{,0},0),0],!^F,o,O,e,=endif,=enddef,=endfu,=endfor', 'foo bar')
expect(res).toBe(false)
res = shouldIndent('=~endif,=enddef,=endfu,=endfor', 'Endif')
expect(res).toBe(true)
res = shouldIndent(' ', '')
expect(res).toBe(false)
res = shouldIndent('*=endif', 'endif')
expect(res).toBe(false)
})
})
describe('getInput()', () => {
it('should consider none word character as input', async () => {
let doc = await helper.createDocument('t.vim')
let res = getInput(doc, 'a#b#', false)
expect(res).toBe('a#b#')
res = getInput(doc, '你b#', true)
expect(res).toBe('b#')
})
})
describe('matchScore', () => {
function score(word: string, input: string): number {
return matchScore(word, getCharCodes(input))
}
it('should match score for last letter', () => {
expect(score('#!3', '3')).toBe(1)
expect(score('bar', 'f')).toBe(0)
})
it('should return 0 when not matched', async () => {
expect(score('and', '你')).toBe(0)
expect(score('你and', '你的')).toBe(0)
expect(score('fooBar', 'Bt')).toBe(0)
expect(score('thisbar', 'tihc')).toBe(0)
})
it('should match first letter', () => {
expect(score('abc', '')).toBe(0)
expect(score('abc', 'a')).toBe(5)
expect(score('Abc', 'a')).toBe(2.5)
expect(score('__abc', 'a')).toBe(2)
expect(score('$Abc', 'a')).toBe(1)
expect(score('$Abc', 'A')).toBe(2)
expect(score('$Abc', '$A')).toBe(6)
expect(score('$Abc', '$a')).toBe(5.5)
expect(score('foo_bar', 'b')).toBe(2)
expect(score('foo_Bar', 'b')).toBe(1)
expect(score('_foo_Bar', 'b')).toBe(0.5)
expect(score('_foo_Bar', 'f')).toBe(2)
expect(score('bar', 'a')).toBe(1)
expect(score('fooBar', 'B')).toBe(2)
expect(score('fooBar', 'b')).toBe(1)
expect(score('fobtoBar', 'bt')).toBe(2)
})
it('should match follow letters', () => {
expect(score('abc', 'ab')).toBe(6)
expect(score('adB', 'ab')).toBe(5.75)
expect(score('adb', 'ab')).toBe(5.1)
expect(score('adCB', 'ab')).toBe(5.05)
expect(score('a_b_c', 'ab')).toBe(6)
expect(score('FooBar', 'fb')).toBe(3.25)
expect(score('FBar', 'fb')).toBe(3)
expect(score('FooBar', 'FB')).toBe(6)
expect(score('FBar', 'FB')).toBe(6)
expect(score('a__b', 'a__b')).toBe(8)
expect(score('aBc', 'ab')).toBe(5.5)
expect(score('a_B_c', 'ab')).toBe(5.75)
expect(score('abc', 'abc')).toBe(7)
expect(score('abc', 'aC')).toBe(0)
expect(score('abc', 'ac')).toBe(5.1)
expect(score('abC', 'ac')).toBe(5.75)
expect(score('abC', 'aC')).toBe(6)
})
it('should only allow search once', () => {
expect(score('foobar', 'fbr')).toBe(5.2)
expect(score('foobaRow', 'fbr')).toBe(5.85)
expect(score('foobaRow', 'fbR')).toBe(6.1)
expect(score('foobar', 'fa')).toBe(5.1)
})
it('should have higher score for strict match', () => {
expect(score('language-client-protocol', 'lct')).toBe(6.1)
expect(score('language-client-types', 'lct')).toBe(7)
})
it('should find highest score', () => {
expect(score('ArrayRotateTail', 'art')).toBe(3.6)
})
})
describe('matchScoreWithPositions', () => {
function assertMatch(word: string, input: string, res: [number, ReadonlyArray<number>] | undefined): void {
let result = matchScoreWithPositions(word, getCharCodes(input))
if (!res) {
expect(result).toBeUndefined()
} else {
expect(result).toEqual(res)
}
}
it('should return undefined when not match found', async () => {
assertMatch('a', 'abc', undefined)
assertMatch('a', '', undefined)
assertMatch('ab', 'ac', undefined)
})
it('should find matches by position fix', async () => {
assertMatch('this', 'tih', [5.6, [0, 1, 2]])
assertMatch('globalThis', 'tihs', [2.6, [6, 7, 8, 9]])
})
it('should find matched positions', async () => {
assertMatch('this', 'th', [6, [0, 1]])
assertMatch('foo_bar', 'fb', [6, [0, 4]])
assertMatch('assertMatch', 'am', [5.75, [0, 6]])
})
})
describe('wordDistance', () => {
it('should empty when not enabled', async () => {
let w = await WordDistance.create(false, {} as any)
expect(w.distance(Position.create(0, 0), {} as any)).toBe(0)
})
it('should empty when selectRanges is empty', async () => {
let opt = await nvim.call('coc#util#get_complete_option') as CompleteOption
let w = await WordDistance.create(true, opt)
expect(w).toBe(WordDistance.None)
})
it('should empty when timeout', async () => {
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
return [{
range: Range.create(0, 0, 0, 1)
}]
}
}))
let spy = jest.spyOn(workspace, 'computeWordRanges').mockImplementation(() => {
return new Promise(resolve => {
setTimeout(() => {
resolve(null)
}, 50)
})
})
let opt = await nvim.call('coc#util#get_complete_option') as CompleteOption
let w = await WordDistance.create(true, opt)
spy.mockRestore()
expect(w).toBe(WordDistance.None)
})
it('should get distance', async () => {
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
return [{
range: Range.create(0, 0, 1, 0),
parent: {
range: Range.create(0, 0, 3, 0)
}
}]
}
}))
let filepath = await createTmpFile('foo bar\ndef', disposables)
await helper.edit(filepath)
let opt = await nvim.call('coc#util#get_complete_option') as CompleteOption
let w = await WordDistance.create(true, opt)
expect(w.distance(Position.create(1, 0), {} as any)).toBeGreaterThan(0)
expect(w.distance(Position.create(0, 0), { word: '', kind: CompletionItemKind.Keyword })).toBeGreaterThan(0)
expect(w.distance(Position.create(0, 0), { word: 'not_exists' })).toBeGreaterThan(0)
expect(w.distance(Position.create(0, 0), { word: 'bar' })).toBe(0)
expect(w.distance(Position.create(0, 0), { word: 'def' })).toBeGreaterThan(0)
await nvim.call('cursor', [1, 2])
await events.fire('CursorMoved', [opt.bufnr, [1, 2]])
expect(w.distance(Position.create(0, 0), { word: 'bar' })).toBe(0)
})
it('should get same range', async () => {
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
return [{
range: Range.create(0, 0, 1, 0),
parent: {
range: Range.create(0, 0, 3, 0)
}
}]
}
}))
let spy = jest.spyOn(workspace, 'computeWordRanges').mockImplementation(() => {
return Promise.resolve({ foo: [Range.create(0, 0, 0, 0)] })
})
let opt = await nvim.call('coc#util#get_complete_option') as any
opt.word = ''
let w = await WordDistance.create(true, opt)
spy.mockRestore()
let res = w.distance(Position.create(0, 0), { word: 'foo' })
expect(res).toBe(0)
})
})

1126
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/configurationModel.test.ts

File diff suppressed because it is too large Load Diff

424
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/configurations.test.ts

@ -0,0 +1,424 @@ @@ -0,0 +1,424 @@
import fs from 'fs'
import os from 'os'
import path from 'path'
import { v1 as uuid } from 'uuid'
import { Disposable } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import Configurations from '../../configuration'
import { ConfigurationModel } from '../../configuration/model'
import ConfigurationProxy from '../../configuration/shape'
import { ConfigurationTarget, ConfigurationUpdateTarget } from '../../types'
import { CONFIG_FILE_NAME, disposeAll, wait } from '../../util'
import { remove } from '../../util/fs'
import helper from '../helper'
const workspaceConfigFile = path.resolve(__dirname, `../sample/.vim/${CONFIG_FILE_NAME}`)
function U(fsPath: string): string {
return URI.file(fsPath).toString()
}
function createConfigurations(): Configurations {
let userConfigFile = path.join(__dirname, './settings.json')
return new Configurations(userConfigFile)
}
const disposables: Disposable[] = []
afterEach(() => {
disposeAll(disposables)
})
function generateTmpDir(): string {
return path.join(os.tmpdir(), uuid())
}
describe('Configurations', () => {
describe('ConfigurationProxy', () => {
it('should create file and parent folder when necessary', async () => {
let folder = generateTmpDir()
let uri = URI.file(path.join(folder, 'a/b/settings.json'))
let proxy = new ConfigurationProxy({}, false)
await proxy.modifyConfiguration(uri.fsPath, 'foo', true)
let content = fs.readFileSync(uri.fsPath, 'utf8')
expect(JSON.parse(content)).toEqual({ foo: true })
await proxy.modifyConfiguration(uri.fsPath, 'foo', false)
content = fs.readFileSync(uri.fsPath, 'utf8')
expect(JSON.parse(content)).toEqual({ foo: false })
await remove(folder)
})
it('should get folder from resolver', async () => {
let proxy = new ConfigurationProxy({
getWorkspaceFolder: (uri: string) => {
let fsPath = URI.parse(uri).fsPath
if (fsPath.startsWith(os.tmpdir())) {
return { uri: URI.file(os.tmpdir()).toString(), name: 'tmp' }
}
if (fsPath.startsWith(os.homedir())) {
return { uri: URI.file(os.homedir()).toString(), name: 'home' }
}
return undefined
},
root: __dirname
})
let uri = proxy.getWorkspaceFolder(URI.file(path.join(os.tmpdir(), 'foo')).toString())
expect(uri.fsPath.startsWith(os.tmpdir())).toBe(true)
uri = proxy.getWorkspaceFolder(URI.file('abc').toString())
expect(uri).toBeUndefined()
proxy = new ConfigurationProxy({})
uri = proxy.getWorkspaceFolder(URI.file(path.join(os.tmpdir(), 'foo')).toString())
expect(uri).toBeUndefined()
})
})
describe('watchFile', () => {
it('should watch user config file', async () => {
let userConfigFile = path.join(os.tmpdir(), `settings-${uuid()}.json`)
fs.writeFileSync(userConfigFile, '{"foo.bar": true}', { encoding: 'utf8' })
let conf = new Configurations(userConfigFile, undefined, false)
disposables.push(conf)
await wait(50)
fs.writeFileSync(userConfigFile, '{"foo.bar": false}', { encoding: 'utf8' })
await helper.waitValue(() => {
let c = conf.getConfiguration('foo')
return c.get('bar')
}, false)
if (fs.existsSync(userConfigFile)) fs.unlinkSync(userConfigFile)
})
it('should watch folder config file', async () => {
let dir = generateTmpDir()
let configFile = path.join(dir, '.vim/coc-settings.json')
fs.mkdirSync(path.dirname(configFile), { recursive: true })
fs.writeFileSync(configFile, '{"foo.bar": true}', { encoding: 'utf8' })
let conf = new Configurations('', {
get root() {
return dir
},
modifyConfiguration: async () => {},
getWorkspaceFolder: () => {
return URI.file(dir)
}
}, false)
disposables.push(conf)
let uri = U(dir)
let resolved = conf.locateFolderConfigution(uri)
expect(resolved).toBeDefined()
await wait(20)
fs.writeFileSync(configFile, '{"foo.bar": false}', { encoding: 'utf8' })
await helper.waitValue(() => {
let c = conf.getConfiguration('foo')
return c.get('bar')
}, false)
})
})
describe('loadDefaultConfigurations', () => {
it('should not throw', async () => {
let fn = fs.readFileSync
let spy = jest.spyOn(fs, 'readFileSync').mockImplementation((path, opt) => {
if (typeof path === 'string' && path.endsWith('/data/schema.json')) {
return '{"properties":{"x":{"default":1},"x.y":{"default":{}}}}'
}
return fn(path, opt)
})
let called = false
let s = jest.spyOn(console, 'error').mockImplementation(() => {
called = true
})
new Configurations(undefined, undefined, true, os.homedir())
s.mockRestore()
spy.mockRestore()
expect(called).toBe(true)
})
})
describe('addFolderFile()', () => {
it('should not add invalid folder from cwd', async () => {
let userConfigFile = path.join(__dirname, '.vim/coc-settings.json')
let conf = new Configurations(userConfigFile, undefined, true, os.homedir())
let res = conf.folderToConfigfile(os.homedir())
expect(res).toBeUndefined()
res = conf.folderToConfigfile(__dirname)
expect(res).toBeUndefined()
})
it('should add folder as workspace configuration', () => {
let configurations = createConfigurations()
disposables.push(configurations)
let fired = false
configurations.onDidChange(() => {
fired = true
})
configurations.addFolderFile(workspaceConfigFile)
let resource = URI.file(path.resolve(workspaceConfigFile, '../../tmp'))
let c = configurations.getConfiguration('coc.preferences', resource)
let res = c.inspect('rootPath')
expect(res.key).toBe('coc.preferences.rootPath')
expect(res.workspaceFolderValue).toBe('./src')
expect(c.get('rootPath')).toBe('./src')
})
it('should not add invalid folders', async () => {
let configurations = createConfigurations()
expect(configurations.addFolderFile('ab')).toBe(false)
})
it('should resolve folder configuration when possible', async () => {
let configurations = createConfigurations()
expect(configurations.locateFolderConfigution('test:///foo')).toBe(false)
let fsPath = path.join(__dirname, `../sample/abc`)
expect(configurations.locateFolderConfigution(URI.file(fsPath).toString())).toBe(true)
fsPath = path.join(__dirname, `../sample/foo`)
expect(configurations.locateFolderConfigution(URI.file(fsPath).toString())).toBe(true)
})
})
describe('getConfiguration()', () => {
it('should load default configurations', () => {
let conf = new Configurations(undefined, {
modifyConfiguration: async () => {}
})
disposables.push(conf)
expect(conf.configuration.defaults.contents.coc).toBeDefined()
let c = conf.getConfiguration('languageserver')
expect(c).toEqual({})
expect(c.has('not_exists')).toBe(false)
})
it('should load configuration without folder configuration', async () => {
let conf = new Configurations(undefined, {
root: path.join(path.dirname(__dirname), 'sample'),
modifyConfiguration: async () => {}
})
disposables.push(conf)
conf.addFolderFile(workspaceConfigFile)
let c = conf.getConfiguration('coc.preferences')
expect(c.rootPath).toBeDefined()
c = conf.getConfiguration('coc.preferences', null)
expect(c.rootPath).toBeUndefined()
})
it('should inspect configuration', async () => {
let conf = new Configurations()
let c = conf.getConfiguration('suggest')
let res = c.inspect('not_exists')
expect(res.defaultValue).toBeUndefined()
expect(res.globalValue).toBeUndefined()
expect(res.workspaceValue).toBeUndefined()
c = conf.getConfiguration()
res = c.inspect('not_exists')
expect(res.key).toBe('not_exists')
})
it('should update memory config #1', () => {
let conf = new Configurations()
let fn = jest.fn()
conf.onDidChange(e => {
expect(e.affectsConfiguration('x')).toBe(true)
fn()
})
conf.updateMemoryConfig({ x: 1 })
let config = conf.configuration.memory
expect(config.contents).toEqual({ x: 1 })
expect(fn).toBeCalled()
expect(conf.configuration.workspace).toBeDefined()
})
it('should update memory config #2', () => {
let conf = new Configurations()
conf.updateMemoryConfig({ x: 1 })
conf.updateMemoryConfig({ x: undefined })
let config = conf.configuration.user
expect(config.contents).toEqual({})
})
it('should update memory config #3', () => {
let conf = new Configurations()
conf.updateMemoryConfig({ 'suggest.floatConfig': { border: true } })
conf.updateMemoryConfig({ 'x.y': { foo: 1 } })
let val = conf.getConfiguration()
let res = val.get('suggest') as any
expect(res.floatConfig).toEqual({ border: true })
res = val.get('x.y') as any
expect(res).toEqual({ foo: 1 })
})
it('should handle errors', () => {
let tmpFile = path.join(os.tmpdir(), uuid())
fs.writeFileSync(tmpFile, '{"x":', 'utf8')
let conf = new Configurations(tmpFile)
disposables.push(conf)
let errors = conf.errorItems
expect(errors.length > 1).toBe(true)
})
it('should get nested property', () => {
let config = createConfigurations()
disposables.push(config)
let conf = config.getConfiguration('servers.c')
let res = conf.get<string>('trace.server', '')
expect(res).toBe('verbose')
})
it('should get user and workspace configuration', () => {
let userConfigFile = path.join(__dirname, './settings.json')
let configurations = new Configurations(userConfigFile)
disposables.push(configurations)
let data = configurations.configuration.toData()
expect(data.user).toBeDefined()
expect(data.workspace).toBeDefined()
expect(data.defaults).toBeDefined()
let value = configurations.configuration.getValue(undefined, {})
expect(value.foo).toBeDefined()
expect(value.foo.bar).toBe(1)
})
it('should extends defaults', () => {
let configurations = createConfigurations()
disposables.push(configurations)
configurations.extendsDefaults({ 'a.b': 1 })
configurations.extendsDefaults({ 'a.b': 2 })
let o = configurations.configuration.defaults.contents
expect(o.a.b).toBe(2)
configurations.configuration.defaults.freeze()
configurations.extendsDefaults({ 'a.b': 3 })
o = configurations.configuration.defaults.contents
expect(o.a.b).toBe(3)
})
it('should not extends builtin keys', async () => {
let configurations = new Configurations(undefined, {
modifyConfiguration: async () => {}
})
disposables.push(configurations)
configurations.extendsDefaults({ 'npm.binPath': 'cnpm' }, 'test')
let o = configurations.configuration.defaults.contents
expect(o.npm.binPath).toBe('npm')
})
it('should update configuration', async () => {
let configurations = createConfigurations()
disposables.push(configurations)
configurations.addFolderFile(workspaceConfigFile)
let resource = URI.file(path.resolve(workspaceConfigFile, '../..'))
let fn = jest.fn()
configurations.onDidChange(e => {
expect(e.affectsConfiguration('foo')).toBe(true)
expect(e.affectsConfiguration('foo.bar')).toBe(true)
expect(e.affectsConfiguration('foo.bar', 'file://tmp/foo.js')).toBe(false)
fn()
})
let config = configurations.getConfiguration('foo', resource)
let o = config.get<number>('bar')
expect(o).toBe(1)
await config.update('bar', 6)
config = configurations.getConfiguration('foo', resource)
expect(config.get<number>('bar')).toBe(6)
expect(fn).toBeCalledTimes(1)
})
it('should remove configuration', async () => {
let configurations = createConfigurations()
disposables.push(configurations)
configurations.addFolderFile(workspaceConfigFile)
let resource = URI.file(path.resolve(workspaceConfigFile, '../..'))
let fn = jest.fn()
configurations.onDidChange(e => {
expect(e.affectsConfiguration('foo')).toBe(true)
expect(e.affectsConfiguration('foo.bar')).toBe(true)
fn()
})
let config = configurations.getConfiguration('foo', resource)
let o = config.get<number>('bar')
expect(o).toBe(1)
await config.update('bar', null, true)
config = configurations.getConfiguration('foo', resource)
expect(config.get<any>('bar')).toBeUndefined()
expect(fn).toBeCalledTimes(1)
})
})
describe('changeConfiguration', () => {
it('should change workspace configuration', async () => {
let con = createConfigurations()
let m = new ConfigurationModel({ x: { a: 1 } }, ['x.a'])
con.changeConfiguration(ConfigurationTarget.Workspace, m, undefined)
let res = con.getConfiguration('x')
expect(res.a).toBe(1)
})
it('should change default configuration', async () => {
let m = new ConfigurationModel({ x: { a: 1 } }, ['x.a'])
let con = createConfigurations()
con.changeConfiguration(ConfigurationTarget.Default, m, undefined)
let res = con.getConfiguration('x')
expect(res.a).toBe(1)
})
})
describe('update()', () => {
it('should update workspace configuration', async () => {
let target = ConfigurationUpdateTarget.Workspace
let con = createConfigurations()
let res = con.getConfiguration()
await res.update('x', 3, target)
let val = con.getConfiguration().get('x')
expect(val).toBe(3)
})
it('should show error when workspace folder not resolved', async () => {
let called = false
let s = jest.spyOn(console, 'error').mockImplementation(() => {
called = true
})
let con = new Configurations(undefined, {
modifyConfiguration: async () => {},
getWorkspaceFolder: () => {
return undefined
}
})
let conf = con.getConfiguration(undefined, 'file:///1')
await conf.update('x', 3, ConfigurationUpdateTarget.WorkspaceFolder)
s.mockRestore()
expect(called).toBe(true)
})
})
describe('getWorkspaceConfigUri()', () => {
it('should not get config uri for undefined resource', async () => {
let conf = createConfigurations()
let res = conf.resolveWorkspaceFolderForResource()
expect(res).toBeUndefined()
})
it('should not get config folder same as home', async () => {
let conf = new Configurations(undefined, {
modifyConfiguration: async () => {},
getWorkspaceFolder: () => {
return URI.file(os.homedir())
}
})
let uri = U(__filename)
let res = conf.resolveWorkspaceFolderForResource(uri)
expect(res).toBeUndefined()
})
it('should create config file for workspace folder', async () => {
let folder = path.join(os.tmpdir(), `test-workspace-folder-${uuid()}`)
let conf = new Configurations(undefined, {
modifyConfiguration: async () => {},
getWorkspaceFolder: () => {
return URI.file(folder)
}
})
let res = conf.resolveWorkspaceFolderForResource('file:///1')
expect(res).toBe(folder)
let configFile = path.join(folder, '.vim/coc-settings.json')
expect(fs.existsSync(configFile)).toBe(true)
res = conf.resolveWorkspaceFolderForResource('file:///1')
expect(res).toBe(folder)
})
})
})

12
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/settings.json

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
{
"foo.bar": 1,
"bar.foo": 2,
"schema": {
"https://example.com": "*.yaml"
},
"servers": {
"c": {
"trace.server": "verbose"
}
}
}

218
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/configuration/util.test.ts

@ -0,0 +1,218 @@ @@ -0,0 +1,218 @@
import * as assert from 'assert'
import { ParseError } from 'jsonc-parser'
import { getConfigurationValue, overrideIdentifiersFromKey, scopeToOverrides, mergeConfigProperties, convertTarget, removeFromValueTree, mergeChanges, toJSONObject, addToValueTree } from '../../configuration/util'
import { ConfigurationTarget, ConfigurationUpdateTarget } from '../../types'
describe('Configuration utils', () => {
it('should convertTarget', async () => {
expect(convertTarget(ConfigurationUpdateTarget.Global)).toBe(ConfigurationTarget.User)
expect(convertTarget(ConfigurationUpdateTarget.Workspace)).toBe(ConfigurationTarget.Workspace)
expect(convertTarget(ConfigurationUpdateTarget.WorkspaceFolder)).toBe(ConfigurationTarget.WorkspaceFolder)
})
it('should scopeToOverrides', async () => {
expect(scopeToOverrides(null)).toBeUndefined()
})
it('should get overrideIdentifiersFromKey', async () => {
let res = overrideIdentifiersFromKey('[ ]')
expect(res).toEqual([])
})
it('should merge preperties', async () => {
let res = mergeConfigProperties({
foo: 'bar',
"x.y.a": "x",
"x.y.b": "y",
"x.t": "z"
})
expect(res).toEqual({
foo: 'bar', x: { y: { a: 'x', b: 'y' }, t: 'z' }
})
})
it('should addToValueTree conflict #1', async () => {
let fn = jest.fn()
let obj = { x: 66 }
addToValueTree(obj, 'x.y', '3', () => {
fn()
})
expect(fn).toBeCalled()
})
it('should addToValueTree conflict #2', async () => {
let fn = jest.fn()
addToValueTree(undefined, 'x', '3', () => {
fn()
})
expect(fn).toBeCalled()
})
it('should addToValueTree conflict #3', async () => {
let obj = { x: true }
let fn = jest.fn()
addToValueTree(obj, 'x.y', ['foo'], () => {
fn()
})
expect(fn).toBeCalled()
})
it('removeFromValueTree: remove a non existing key', () => {
let target = { a: { b: 2 } }
removeFromValueTree(target, 'c')
assert.deepStrictEqual(target, { a: { b: 2 } })
removeFromValueTree(target, 'c.d.e')
assert.deepStrictEqual(target, { a: { b: 2 } })
})
it('removeFromValueTree: remove a multi segmented key from an object that has only sub sections of the key', () => {
let target = { a: { b: 2 } }
removeFromValueTree(target, 'a.b.c')
assert.deepStrictEqual(target, { a: { b: 2 } })
})
it('removeFromValueTree: remove a single segmented key', () => {
let target = { a: 1 }
removeFromValueTree(target, 'a')
assert.deepStrictEqual(target, {})
})
it('removeFromValueTree: remove a single segmented key when its value is undefined', () => {
let target = { a: undefined }
removeFromValueTree(target, 'a')
assert.deepStrictEqual(target, {})
})
it('removeFromValueTree: remove a multi segmented key when its value is undefined', () => {
let target = { a: { b: 1 } }
removeFromValueTree(target, 'a.b')
assert.deepStrictEqual(target, {})
})
it('removeFromValueTree: remove a multi segmented key when its value is array', () => {
let target = { a: { b: [1] } }
removeFromValueTree(target, 'a.b')
assert.deepStrictEqual(target, {})
})
it('removeFromValueTree: remove a multi segmented key first segment value is array', () => {
let target = { a: [1] }
removeFromValueTree(target, 'a.0')
assert.deepStrictEqual(target, { a: [1] })
})
it('removeFromValueTree: remove when key is the first segment', () => {
let target = { a: { b: 1 } }
removeFromValueTree(target, 'a')
assert.deepStrictEqual(target, {})
})
it('removeFromValueTree: remove a multi segmented key when the first node has more values', () => {
let target = { a: { b: { c: 1 }, d: 1 } }
removeFromValueTree(target, 'a.b.c')
assert.deepStrictEqual(target, { a: { d: 1 } })
})
it('removeFromValueTree: remove a multi segmented key when in between node has more values', () => {
let target = { a: { b: { c: { d: 1 }, d: 1 } } }
removeFromValueTree(target, 'a.b.c.d')
assert.deepStrictEqual(target, { a: { b: { d: 1 } } })
})
it('removeFromValueTree: remove a multi segmented key when the last but one node has more values', () => {
let target = { a: { b: { c: 1, d: 1 } } }
removeFromValueTree(target, 'a.b.c')
assert.deepStrictEqual(target, { a: { b: { d: 1 } } })
})
it('should convert errors', () => {
let errors: ParseError[] = []
for (let i = 0; i < 17; i++) {
errors.push({
error: i,
offset: 0,
length: 10
})
}
// let res = convertErrors('file:///1', 'abc', errors)
// expect(res.length).toBe(17)
})
it('should get configuration value', () => {
let root = {
foo: {
bar: 1,
from: {
to: 2
}
},
bar: [1, 2]
}
let res = getConfigurationValue(root, 'foo.from.to', 1)
expect(res).toBe(2)
res = getConfigurationValue(root, 'foo.from', 1)
expect(res).toEqual({ to: 2 })
})
it('should get json object', async () => {
let obj = [{ x: 1 }, { y: 2 }]
expect(toJSONObject(obj)).toEqual(obj)
})
})
describe('mergeChanges', () => {
test('merge only keys', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] })
assert.deepStrictEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] })
})
test('merge only keys with duplicates', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] }, { keys: ['a', 'd', 'e'], overrides: [] })
assert.deepStrictEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] })
})
test('merge only overrides', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']]] }, { keys: [], overrides: [['b', ['3', '4']]] })
assert.deepStrictEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] })
})
test('merge only overrides with duplicates', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: [], overrides: [['b', ['3', '4']]] }, { keys: [], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] })
assert.deepStrictEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] })
})
test('merge', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: ['b'], overrides: [['b', ['3', '4']]] }, { keys: ['c', 'a'], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] })
assert.deepStrictEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] })
})
test('merge single change', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] })
assert.deepStrictEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] })
})
test('merge no changes', () => {
const actual = mergeChanges()
assert.deepStrictEqual(actual, { keys: [], overrides: [] })
})
})

68
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/autocmds.test.ts

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
import { Neovim } from '@chemzqm/neovim'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('setupDynamicAutocmd()', () => {
it('should setup autocmd on vim', async () => {
await nvim.setLine('foo')
let fn = nvim.hasFunction
nvim.hasFunction = () => {
return false
}
let called = false
workspace.registerAutocmd({
event: 'CursorMoved',
request: true,
callback: () => {
called = true
}
})
await helper.wait(50)
await nvim.command('normal! $')
await helper.wait(100)
nvim.hasFunction = fn
expect(called).toBe(true)
nvim.command(`augroup coc_dynamic_autocmd| autocmd!|augroup end`, true)
})
it('should setup user autocmd', async () => {
let called = false
workspace.registerAutocmd({
event: 'User CocJumpPlaceholder',
request: true,
callback: () => {
called = true
}
})
workspace.autocmds.setupDynamicAutocmd(true)
await helper.wait(50)
await nvim.command('doautocmd <nomodeline> User CocJumpPlaceholder')
await helper.wait(100)
expect(called).toBe(true)
})
})
describe('doAutocmd()', () => {
it('should not throw when command id does not exist', async () => {
await workspace.autocmds.doAutocmd(999, [])
})
it('should dispose', async () => {
workspace.autocmds.dispose()
})
})

143
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/documents.test.ts

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { v4 as uuid } from 'uuid'
import { LocationLink, Position, Range, TextEdit } from 'vscode-languageserver-types'
import { URI } from 'vscode-uri'
import Documents from '../../core/documents'
import events from '../../events'
import workspace from '../../workspace'
import helper from '../helper'
let documents: Documents
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
documents = workspace.documentsManager
})
afterEach(async () => {
await helper.reset()
})
afterAll(async () => {
await helper.shutdown()
})
describe('documents', () => {
it('should get document', async () => {
await helper.createDocument('bar')
let doc = await helper.createDocument('foo')
let res = documents.getDocument(doc.uri)
expect(res.uri).toBe(doc.uri)
})
it('should consider lisp option for iskeyword', async () => {
await nvim.command(`e +setl\\ lisp t`)
let doc = await workspace.document
expect(doc.isWord('-')).toBe(true)
})
it('should get languageId', async () => {
await helper.createDocument('t.vim')
expect(documents.getLanguageId('/a/b')).toBe('')
expect(documents.getLanguageId('/a/b.vim')).toBe('vim')
expect(documents.getLanguageId('/a/b.c')).toBe('')
})
it('should get lines', async () => {
let doc = await helper.createDocument('tmp')
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')])
let lines = await documents.getLines(doc.uri)
expect(lines).toEqual(['foo', 'bar'])
lines = await documents.getLines('lsptest:///1')
expect(lines).toEqual([])
lines = await documents.getLines('file:///not_exists_file')
expect(lines).toEqual([])
let uri = URI.file(__filename).toString()
lines = await documents.getLines(uri)
expect(lines.length).toBeGreaterThan(0)
})
it('should read empty string from none file', async () => {
let res = await documents.readFile('test:///1')
expect(res).toBe('')
})
it('should get empty line from none file', async () => {
let res = await documents.getLine('test:///1', 1)
expect(res).toBe('')
let uri = URI.file(path.join(__dirname, 'not_exists_file')).toString()
res = await documents.getLine(uri, 1)
expect(res).toBe('')
})
it('should get QuickfixItem from location link', async () => {
let doc = await helper.createDocument('quickfix')
let loc = LocationLink.create(doc.uri, Range.create(0, 0, 3, 0), Range.create(0, 0, 0, 3))
let res = await documents.getQuickfixItem(loc, 'text', 'E', 'module')
expect(res.targetRange).toBeDefined()
expect(res.type).toBe('E')
expect(res.module).toBe('module')
expect(res.bufnr).toBe(doc.bufnr)
})
it('should create document', async () => {
await helper.createDocument()
let bufnrs = await nvim.call('coc#ui#open_files', [[__filename]]) as number[]
let bufnr = bufnrs[0]
let doc = workspace.getDocument(bufnr)
expect(doc).toBeUndefined()
doc = await documents.createDocument(bufnr)
expect(doc).toBeDefined()
})
it('should check buffer rename on save', async () => {
let doc = await workspace.document
let bufnr = doc.bufnr
let name = `${uuid()}.vim`
let tmpfile = path.join(os.tmpdir(), name)
await nvim.command(`write ${tmpfile}`)
doc = workspace.getDocument(bufnr)
expect(doc).toBeDefined()
expect(doc.filetype).toBe('vim')
expect(doc.bufname).toMatch(name)
fs.unlinkSync(tmpfile)
})
it('should get current document', async () => {
let p1 = workspace.document
let p2 = workspace.document
let arr = await Promise.all([p1, p2])
expect(arr[0]).toBe(arr[1])
})
it('should get bufnrs', async () => {
await workspace.document
let bufnrs = documents.bufnrs
expect(bufnrs.length).toBe(1)
})
it('should get uri', async () => {
let doc = await workspace.document
expect(documents.uri).toBe(doc.uri)
})
it('should attach events on vim', async () => {
await documents.attach(nvim, workspace.env)
let env = Object.assign(workspace.env, { isVim: true })
documents.detach()
await documents.attach(nvim, env)
documents.detach()
await events.fire('CursorMoved', [1, [1, 1]])
})
it('should compute word ranges', async () => {
expect(await workspace.computeWordRanges('file:///1', Range.create(0, 0, 1, 0))).toBeNull()
let doc = await workspace.document
expect(await workspace.computeWordRanges(doc.uri, Range.create(0, 0, 1, 0))).toBeDefined()
})
})

169
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/editors.test.ts

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
import { Neovim } from '@chemzqm/neovim'
import Editors, { TextEditor } from '../../core/editors'
import workspace from '../../workspace'
import window from '../../window'
import events from '../../events'
import helper from '../helper'
import { disposeAll } from '../../util'
import { Disposable } from 'vscode-languageserver-protocol'
let editors: Editors
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
editors = workspace.editors
})
afterEach(async () => {
await helper.reset()
})
afterAll(async () => {
disposeAll(disposables)
await helper.shutdown()
})
describe('editors', () => {
function assertEditor(editor: TextEditor, tabpagenr: number, winid: number) {
expect(editor).toBeDefined()
expect(editor.tabpagenr).toBe(tabpagenr)
expect(editor.winid).toBe(winid)
}
it('should have active editor', async () => {
let winid = await nvim.call('win_getid')
let editor = window.activeTextEditor
assertEditor(editor, 1, winid)
let editors = window.visibleTextEditors
expect(editors.length).toBe(1)
})
it('should change active editor on split', async () => {
let promise = new Promise<TextEditor>(resolve => {
editors.onDidChangeActiveTextEditor(e => {
resolve(e)
}, null, disposables)
})
await nvim.command('vnew')
let editor = await promise
let winid = await nvim.call('win_getid')
expect(editor.winid).toBe(winid)
})
it('should change active editor on tabe', async () => {
let promise = new Promise<TextEditor>(resolve => {
editors.onDidChangeActiveTextEditor(e => {
if (e.document.uri.includes('foo')) {
resolve(e)
}
}, null, disposables)
})
await nvim.command('tabe a | tabe b | tabe foo')
let editor = await promise
let winid = await nvim.call('win_getid')
expect(editor.winid).toBe(winid)
})
it('should change active editor on edit', async () => {
await nvim.call('win_getid')
let fn = jest.fn()
window.onDidChangeVisibleTextEditors(() => {
fn()
}, null, disposables)
let promise = new Promise<TextEditor>(resolve => {
editors.onDidChangeActiveTextEditor(e => {
resolve(e)
})
})
await nvim.command('edit foo')
let editor = await promise
expect(editor.document.uri).toMatch('foo')
expect(fn).toBeCalled()
})
it('should change active editor on window switch', async () => {
let winid = await nvim.call('win_getid')
await nvim.command('vs foo')
await nvim.command('wincmd p')
let curr = editors.activeTextEditor
expect(curr.winid).toBe(winid)
expect(editors.visibleTextEditors.length).toBe(2)
})
it('should not create editor for float window', async () => {
let fn = jest.fn()
await nvim.call('win_getid')
editors.onDidChangeActiveTextEditor(e => {
fn()
})
let res = await nvim.call('coc#float#create_float_win', [0, 0, {
relative: 'editor',
row: 1,
col: 1,
width: 10,
height: 1,
lines: ['foo']
}])
await nvim.call('win_gotoid', [res[0]])
await events.fire('CursorHold', [res[1]])
await nvim.command('wincmd p')
expect(fn).toBeCalledTimes(0)
expect(editors.visibleTextEditors.length).toBe(1)
})
it('should cleanup on CursorHold', async () => {
let winid = await nvim.call('win_getid')
let promise = new Promise<TextEditor>(resolve => {
editors.onDidChangeActiveTextEditor(e => {
if (e.document.uri.includes('foo')) {
resolve(e)
}
}, null, disposables)
})
await nvim.command('tabe foo')
await promise
await nvim.call('win_execute', [winid, 'noa close'])
let bufnr = await nvim.eval("bufnr('%')")
await events.fire('CursorHold', [bufnr])
expect(editors.visibleTextEditors.length).toBe(1)
})
it('should cleanup on create', async () => {
let winid = await nvim.call('win_getid')
let promise = new Promise<TextEditor>(resolve => {
editors.onDidChangeActiveTextEditor(e => {
if (e.document.uri.includes('foo')) {
resolve(e)
}
}, null, disposables)
})
await nvim.command('tabe foo')
await promise
await nvim.call('win_execute', [winid, 'noa close'])
await nvim.command('edit bar')
expect(editors.visibleTextEditors.length).toBe(2)
})
it('should have current tabnr after tab changed', async () => {
await nvim.command('tabe')
await helper.waitValue(() => {
return editors.visibleTextEditors.length
}, 2)
let editor = editors.visibleTextEditors.find(o => o.tabpagenr == 2)
await nvim.command('normal! 1gt')
await nvim.command('tabe')
await helper.waitValue(() => {
return editors.visibleTextEditors.length
}, 3)
expect(editor.tabpagenr).toBe(3)
await nvim.command('tabc')
await helper.waitValue(() => {
return editors.visibleTextEditors.length
}, 2)
expect(editor.tabpagenr).toBe(2)
})
})

431
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/fileSystemWatcher.test.ts

@ -0,0 +1,431 @@ @@ -0,0 +1,431 @@
import bser from 'bser'
import fs from 'fs'
import net from 'net'
import os from 'os'
import path from 'path'
import { v4 as uuid } from 'uuid'
import { Disposable } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import Configurations from '../../configuration/index'
import { FileSystemWatcher, FileSystemWatcherManager } from '../../core/fileSystemWatcher'
import Watchman, { FileChangeItem, isValidWatchRoot } from '../../core/watchman'
import WorkspaceFolderController from '../../core/workspaceFolder'
import RelativePattern from '../../model/relativePattern'
import { GlobPattern } from '../../types'
import { disposeAll } from '../../util'
import helper from '../helper'
let server: net.Server
let client: net.Socket
const cwd = process.cwd()
const sockPath = path.join(os.tmpdir(), `watchman-fake-${uuid()}`)
process.env.WATCHMAN_SOCK = sockPath
let workspaceFolder: WorkspaceFolderController
let watcherManager: FileSystemWatcherManager
let configurations: Configurations
let disposables: Disposable[] = []
function wait(ms: number): Promise<any> {
return new Promise(resolve => {
setTimeout(() => {
resolve(undefined)
}, ms)
})
}
function createFileChange(file: string, isNew = true, exists = true): FileChangeItem {
return {
size: 1,
name: file,
exists,
new: isNew,
type: 'f',
mtime_ms: Date.now()
}
}
function sendResponse(data: any): void {
client.write(bser.dumpToBuffer(data))
}
function sendSubscription(uid: string, root: string, files: FileChangeItem[]): void {
client.write(bser.dumpToBuffer({
subscription: uid,
root,
files
}))
}
let capabilities: any
let watchResponse: any
beforeAll(async () => {
await helper.setup()
})
beforeAll(done => {
let userConfigFile = path.join(process.env.COC_VIMCONFIG, 'coc-settings.json')
configurations = new Configurations(userConfigFile, undefined)
workspaceFolder = new WorkspaceFolderController(configurations)
watcherManager = new FileSystemWatcherManager(workspaceFolder, '')
watcherManager.attach(helper.createNullChannel())
// create a mock sever for watchman
server = net.createServer(c => {
client = c
c.on('data', data => {
let obj = bser.loadFromBuffer(data)
if (obj[0] == 'watch-project') {
sendResponse(watchResponse || { watch: obj[1], warning: 'warning' })
} else if (obj[0] == 'unsubscribe') {
sendResponse({ path: obj[1] })
} else if (obj[0] == 'clock') {
sendResponse({ clock: 'clock' })
} else if (obj[0] == 'version') {
let { optional, required } = obj[1]
let res = {}
for (let key of optional) {
res[key] = true
}
for (let key of required) {
res[key] = true
}
sendResponse({ capabilities: capabilities || res })
} else if (obj[0] == 'subscribe') {
sendResponse({ subscribe: obj[2] })
} else {
sendResponse({})
}
})
})
server.on('error', err => {
throw err
})
server.listen(sockPath, () => {
done()
})
})
afterEach(async () => {
disposeAll(disposables)
capabilities = undefined
watchResponse = undefined
})
afterAll(async () => {
await helper.shutdown()
watcherManager.dispose()
server.removeAllListeners()
server.close()
if (fs.existsSync(sockPath)) {
fs.unlinkSync(sockPath)
}
})
describe('watchman', () => {
it('should throw error when not watching', async () => {
let client = new Watchman(null)
disposables.push(client)
let fn = async () => {
await client.subscribe('**/*', () => {})
}
await expect(fn()).rejects.toThrow(/not watching/)
})
it('should checkCapability', async () => {
let client = new Watchman(null)
let res = await client.checkCapability()
expect(res).toBe(true)
capabilities = { relative_root: false }
res = await client.checkCapability()
expect(res).toBe(false)
client.dispose()
})
it('should watchProject', async () => {
let client = new Watchman(null)
disposables.push(client)
let res = await client.watchProject(__dirname)
expect(res).toBe(true)
client.dispose()
})
it('should unsubscribe', async () => {
let client = new Watchman(null)
disposables.push(client)
await client.watchProject(cwd)
let fn = jest.fn()
let disposable = await client.subscribe(`${cwd}/*`, fn)
disposable.dispose()
client.dispose()
})
})
describe('Watchman#subscribe', () => {
it('should subscribe file change', async () => {
let client = new Watchman(null, helper.createNullChannel())
disposables.push(client)
await client.watchProject(cwd)
let fn = jest.fn()
let disposable = await client.subscribe(`${cwd}/*`, fn)
let changes: FileChangeItem[] = [createFileChange(`${cwd}/a`)]
sendSubscription(disposable.subscribe, cwd, changes)
await wait(30)
expect(fn).toBeCalled()
let call = fn.mock.calls[0][0]
disposable.dispose()
expect(call.root).toBe(cwd)
client.dispose()
})
it('should subscribe with relative_path', async () => {
let client = new Watchman(null, helper.createNullChannel())
watchResponse = { watch: cwd, relative_path: 'foo' }
await client.watchProject(cwd)
let fn = jest.fn()
let disposable = await client.subscribe(`${cwd}/*`, fn)
let changes: FileChangeItem[] = [createFileChange(`${cwd}/a`)]
sendSubscription(disposable.subscribe, cwd, changes)
await wait(30)
expect(fn).toBeCalled()
let call = fn.mock.calls[0][0]
disposable.dispose()
expect(call.root).toBe(path.join(cwd, 'foo'))
client.dispose()
})
it('should not subscribe invalid response', async () => {
let c = new Watchman(null, helper.createNullChannel())
disposables.push(c)
watchResponse = { watch: cwd, relative_path: 'foo' }
await c.watchProject(cwd)
let fn = jest.fn()
let disposable = await c.subscribe(`${cwd}/*`, fn)
let changes: FileChangeItem[] = [createFileChange(`${cwd}/a`)]
sendSubscription('uuid', cwd, changes)
await wait(10)
sendSubscription(disposable.subscribe, cwd, [])
await wait(10)
client.write(bser.dumpToBuffer({
subscription: disposable.subscribe,
root: cwd
}))
await wait(10)
expect(fn).toBeCalledTimes(0)
})
})
describe('Watchman#createClient', () => {
it('should not create client when capabilities not match', async () => {
capabilities = { relative_root: false }
await expect(async () => {
await Watchman.createClient(null, cwd)
}).rejects.toThrow(Error)
})
it('should not create when watch failed', async () => {
watchResponse = {}
await expect(async () => {
await Watchman.createClient(null, cwd)
}).rejects.toThrow(Error)
})
it('should create client', async () => {
let client = await Watchman.createClient(null, cwd)
disposables.push(client)
expect(client).toBeDefined()
})
it('should not create client for root', async () => {
await expect(async () => {
await Watchman.createClient(null, '/')
}).rejects.toThrow(Error)
})
})
describe('isValidWatchRoot()', () => {
it('should check valid root', async () => {
expect(isValidWatchRoot('/')).toBe(false)
expect(isValidWatchRoot(os.homedir())).toBe(false)
expect(isValidWatchRoot('/tmp/a/b/c')).toBe(false)
expect(isValidWatchRoot(os.tmpdir())).toBe(false)
})
})
describe('fileSystemWatcher', () => {
function createWatcher(pattern: GlobPattern, ignoreCreateEvents = false, ignoreChangeEvents = false, ignoreDeleteEvents = false): FileSystemWatcher {
let watcher = watcherManager.createFileSystemWatcher(
pattern,
ignoreCreateEvents,
ignoreChangeEvents,
ignoreDeleteEvents
)
disposables.push(watcher)
return watcher
}
beforeAll(async () => {
workspaceFolder.addWorkspaceFolder(cwd, true)
await watcherManager.waitClient(cwd)
})
it('should use relative pattern #1', async () => {
let folder = workspaceFolder.workspaceFolders[0]
expect(folder).toBeDefined()
let pattern = new RelativePattern(folder, '**/*')
let watcher = createWatcher(pattern, false, true, true)
let fn = jest.fn()
watcher.onDidCreate(fn)
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(fn).toBeCalled()
})
it('should use relative pattern #2', async () => {
let called = false
let pattern = new RelativePattern(__dirname, '**/*')
let watcher = createWatcher(pattern, false, true, true)
watcher.onDidCreate(() => {
called = true
})
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(called).toBe(false)
})
it('should use relative pattern #3', async () => {
let called = false
let root = path.join(os.tmpdir(), 'not_exists')
let pattern = new RelativePattern(root, '**/*')
let watcher = createWatcher(pattern, false, true, true)
watcher.onDidCreate(() => {
called = true
})
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(called).toBe(false)
})
it('should watch for file create', async () => {
let watcher = createWatcher('**/*', false, true, true)
let fn = jest.fn()
watcher.onDidCreate(fn)
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(fn).toBeCalled()
})
it('should watch for file delete', async () => {
let watcher = createWatcher('**/*', true, true, false)
let fn = jest.fn()
watcher.onDidDelete(fn)
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`, false, false)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(fn).toBeCalled()
})
it('should watch for file change', async () => {
let watcher = createWatcher('**/*', false, false, false)
let fn = jest.fn()
watcher.onDidChange(fn)
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`, false, true)]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(fn).toBeCalled()
})
it('should watch for file rename', async () => {
let watcher = createWatcher('**/*', false, false, false)
let fn = jest.fn()
watcher.onDidRename(fn)
await helper.wait(50)
let changes: FileChangeItem[] = [
createFileChange(`a`, false, false),
createFileChange(`b`, true, true),
]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(fn).toBeCalled()
})
it('should not watch for events', async () => {
let watcher = createWatcher('**/*', true, true, true)
let called = false
let onChange = () => { called = true }
watcher.onDidCreate(onChange)
watcher.onDidChange(onChange)
watcher.onDidDelete(onChange)
await helper.wait(50)
let changes: FileChangeItem[] = [
createFileChange(`a`, false, false),
createFileChange(`b`, true, true),
createFileChange(`c`, false, true),
]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.wait(50)
expect(called).toBe(false)
})
it('should watch for folder rename', async () => {
let watcher = createWatcher('**/*')
let newFiles: string[] = []
let count = 0
watcher.onDidRename(e => {
count++
newFiles.push(e.newUri.fsPath)
})
await helper.wait(50)
let changes: FileChangeItem[] = [
createFileChange(`a/1`, false, false),
createFileChange(`a/2`, false, false),
createFileChange(`b/1`, true, true),
createFileChange(`b/2`, true, true),
]
sendSubscription(watcher.subscribe, cwd, changes)
await helper.waitValue(() => {
return count
}, 2)
})
it('should watch for new folder', async () => {
let watcher = createWatcher('**/*')
expect(watcher).toBeDefined()
workspaceFolder.renameWorkspaceFolder(cwd, __dirname)
await helper.wait(50)
let uri: URI
watcher.onDidCreate(e => {
uri = e
})
await helper.wait(50)
let changes: FileChangeItem[] = [createFileChange(`a`)]
sendSubscription(watcher.subscribe, __dirname, changes)
await helper.wait(50)
expect(uri.fsPath).toEqual(path.join(__dirname, 'a'))
})
})
describe('create FileSystemWatcherManager', () => {
it('should attach to existing workspace folder', async () => {
let workspaceFolder = new WorkspaceFolderController(configurations)
workspaceFolder.addWorkspaceFolder(cwd, false)
let watcherManager = new FileSystemWatcherManager(workspaceFolder, '')
watcherManager.attach(helper.createNullChannel())
await helper.wait(100)
await watcherManager.createClient(os.tmpdir())
await watcherManager.createClient(cwd)
await watcherManager.waitClient(cwd)
watcherManager.dispose()
})
})

840
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/files.test.ts

@ -0,0 +1,840 @@ @@ -0,0 +1,840 @@
import { Buffer, Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { v4 as uuid } from 'uuid'
import { CancellationTokenSource, Disposable } from 'vscode-languageserver-protocol'
import { CreateFile, DeleteFile, Position, Range, RenameFile, TextDocumentEdit, TextEdit, VersionedTextDocumentIdentifier, WorkspaceEdit } from 'vscode-languageserver-types'
import { URI } from 'vscode-uri'
import { RecoverFunc, getOriginalLine } from '../../model/editInspect'
import RelativePattern from '../../model/relativePattern'
import { disposeAll } from '../../util'
import { readFile } from '../../util/fs'
import window from '../../window'
import workspace from '../../workspace'
import events from '../../events'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('RelativePattern', () => {
function testThrow(fn: () => void) {
let err
try {
fn()
} catch (e) {
err = e
}
expect(err).toBeDefined()
}
it('should throw for invalid arguments', async () => {
testThrow(() => {
new RelativePattern('', undefined)
})
testThrow(() => {
new RelativePattern({ uri: undefined } as any, '')
})
})
it('should create relativePattern', async () => {
for (let base of [__filename, URI.file(__filename), { uri: URI.file(__dirname).toString(), name: 'test' }]) {
let p = new RelativePattern(base, '**/*')
expect(URI.isUri(p.baseUri)).toBe(true)
expect(p.toJSON()).toBeDefined()
}
})
})
describe('findFiles()', () => {
beforeEach(() => {
workspace.workspaceFolderControl.setWorkspaceFolders([__dirname])
})
it('should use glob pattern', async () => {
let res = await workspace.findFiles('**/*.ts')
expect(res.length).toBeGreaterThan(0)
})
it('should use relativePattern', async () => {
let relativePattern = new RelativePattern(URI.file(__dirname), '**/*.ts')
let res = await workspace.findFiles(relativePattern)
expect(res.length).toBeGreaterThan(0)
})
it('should respect exclude as glob pattern', async () => {
let arr = await workspace.findFiles('**/*.ts', 'files*')
let res = arr.find(o => path.relative(__dirname, o.fsPath).startsWith('files'))
expect(res).toBeUndefined()
})
it('should respect exclude as relativePattern', async () => {
let relativePattern = new RelativePattern(URI.file(__dirname), 'files*')
let arr = await workspace.findFiles('**/*.ts', relativePattern)
let res = arr.find(o => path.relative(__dirname, o.fsPath).startsWith('files'))
expect(res).toBeUndefined()
})
it('should respect maxResults', async () => {
let arr = await workspace.findFiles('**/*.ts', undefined, 1)
expect(arr.length).toBe(1)
})
it('should respect token', async () => {
let source = new CancellationTokenSource()
source.cancel()
let arr = await workspace.findFiles('**/*.ts', undefined, 1, source.token)
expect(arr.length).toBe(0)
})
it('should cancel findFiles', async () => {
let source = new CancellationTokenSource()
let p = workspace.findFiles('**/*.ts', undefined, 1, source.token)
source.cancel()
let arr = await p
expect(arr.length).toBe(0)
})
})
describe('applyEdits()', () => {
it('should not throw when unable to undo & redo', async () => {
await workspace.files.undoWorkspaceEdit()
await workspace.files.redoWorkspaceEdit()
})
it('should apply TextEdit of documentChanges', async () => {
let doc = await helper.createDocument()
let versioned = VersionedTextDocumentIdentifier.create(doc.uri, doc.version)
let edit = TextEdit.insert(Position.create(0, 0), 'bar')
let change = TextDocumentEdit.create(versioned, [edit])
let workspaceEdit: WorkspaceEdit = {
documentChanges: [change]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
let line = await nvim.getLine()
expect(line).toBe('bar')
})
it('should apply edit with out change buffers', async () => {
let doc = await helper.createDocument()
await nvim.setLine('bar')
await doc.synchronize()
let version = doc.version
let versioned = VersionedTextDocumentIdentifier.create(doc.uri, doc.version)
let edit = TextEdit.replace(Range.create(0, 0, 0, 3), 'bar')
let change = TextDocumentEdit.create(versioned, [edit])
let workspaceEdit: WorkspaceEdit = {
documentChanges: [change]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
expect(doc.version).toBe(version)
})
it('should not apply TextEdit if version miss match', async () => {
let doc = await helper.createDocument()
let versioned = VersionedTextDocumentIdentifier.create(doc.uri, 10)
let edit = TextEdit.insert(Position.create(0, 0), 'bar')
let change = TextDocumentEdit.create(versioned, [edit])
let workspaceEdit: WorkspaceEdit = {
documentChanges: [change]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(false)
})
it('should apply edits with changes to buffer', async () => {
let doc = await helper.createDocument()
let changes = {
[doc.uri]: [TextEdit.insert(Position.create(0, 0), 'bar')]
}
let workspaceEdit: WorkspaceEdit = { changes }
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
let line = await nvim.getLine()
expect(line).toBe('bar')
})
it('should apply edits with changes to file not in buffer list', async () => {
let filepath = await createTmpFile('bar')
let uri = URI.file(filepath).toString()
let changes = {
[uri]: [TextEdit.insert(Position.create(0, 0), 'foo')]
}
let res = await workspace.applyEdit({ changes })
expect(res).toBe(true)
let doc = workspace.getDocument(uri)
let content = doc.getDocumentContent()
expect(content).toMatch(/^foobar/)
await nvim.command('silent! %bwipeout!')
})
it('should apply edits when file does not exist', async () => {
let filepath = path.join(__dirname, 'not_exists')
disposables.push({
dispose: () => {
if (fs.existsSync(filepath)) {
fs.unlinkSync(filepath)
}
}
})
let uri = URI.file(filepath).toString()
let changes = {
[uri]: [TextEdit.insert(Position.create(0, 0), 'foo')]
}
let res = await workspace.applyEdit({ changes })
expect(res).toBe(true)
})
it('should adjust cursor position after applyEdits', async () => {
let doc = await helper.createDocument()
let pos = await window.getCursorPosition()
expect(pos).toEqual({ line: 0, character: 0 })
let edit = TextEdit.insert(Position.create(0, 0), 'foo\n')
let versioned = VersionedTextDocumentIdentifier.create(doc.uri, null)
let documentChanges = [TextDocumentEdit.create(versioned, [edit])]
let res = await workspace.applyEdit({ documentChanges })
expect(res).toBe(true)
pos = await window.getCursorPosition()
expect(pos).toEqual({ line: 1, character: 0 })
})
it('should support null version of documentChanges', async () => {
let file = path.join(__dirname, 'foo')
await workspace.createFile(file, { ignoreIfExists: true, overwrite: true })
let uri = URI.file(file).toString()
let versioned = VersionedTextDocumentIdentifier.create(uri, null)
let edit = TextEdit.insert(Position.create(0, 0), 'bar')
let change = TextDocumentEdit.create(versioned, [edit])
let workspaceEdit: WorkspaceEdit = {
documentChanges: [change]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
await nvim.command('wa')
let content = await readFile(file, 'utf8')
expect(content).toMatch(/^bar/)
await workspace.deleteFile(file, { ignoreIfNotExists: true })
})
it('should support CreateFile edit', async () => {
let file = path.join(__dirname, 'foo')
let uri = URI.file(file).toString()
let workspaceEdit: WorkspaceEdit = {
documentChanges: [CreateFile.create(uri, { overwrite: true })]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
await workspace.deleteFile(file, { ignoreIfNotExists: true })
})
it('should support DeleteFile edit', async () => {
let file = path.join(__dirname, 'foo')
await workspace.createFile(file, { ignoreIfExists: true, overwrite: true })
let uri = URI.file(file).toString()
let workspaceEdit: WorkspaceEdit = {
documentChanges: [DeleteFile.create(uri)]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
})
it('should check uri for CreateFile edit', async () => {
let workspaceEdit: WorkspaceEdit = {
documentChanges: [CreateFile.create('term://.', { overwrite: true })]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(false)
})
it('should support RenameFile edit', async () => {
let file = path.join(__dirname, 'foo')
await workspace.createFile(file, { ignoreIfExists: true, overwrite: true })
let newFile = path.join(__dirname, 'bar')
let uri = URI.file(file).toString()
let workspaceEdit: WorkspaceEdit = {
documentChanges: [RenameFile.create(uri, URI.file(newFile).toString())]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
await workspace.deleteFile(newFile, { ignoreIfNotExists: true })
})
it('should support changes with edit and rename', async () => {
let fsPath = await createTmpFile('test')
let doc = await helper.createDocument(fsPath)
let newFile = path.join(os.tmpdir(), `coc-${process.pid}/new-${uuid()}`)
let newUri = URI.file(newFile).toString()
let edit: WorkspaceEdit = {
documentChanges: [
{
textDocument: {
version: null,
uri: doc.uri,
},
edits: [
{
range: {
start: {
line: 0,
character: 0
},
end: {
line: 0,
character: 4
}
},
newText: 'bar'
}
]
},
{
oldUri: doc.uri,
newUri,
kind: 'rename'
}
]
}
let res = await workspace.applyEdit(edit)
expect(res).toBe(true)
let curr = await workspace.document
expect(curr.uri).toBe(newUri)
expect(curr.getline(0)).toBe('bar')
let line = await nvim.line
expect(line).toBe('bar')
})
it('should support edit new file with CreateFile', async () => {
let file = path.join(os.tmpdir(), 'foo')
let uri = URI.file(file).toString()
let workspaceEdit: WorkspaceEdit = {
documentChanges: [
CreateFile.create(uri, { overwrite: true }),
TextDocumentEdit.create({ uri, version: 0 }, [
TextEdit.insert(Position.create(0, 0), 'foo bar')
])
]
}
let res = await workspace.applyEdit(workspaceEdit)
expect(res).toBe(true)
let doc = workspace.getDocument(uri)
expect(doc).toBeDefined()
let line = doc.getline(0)
expect(line).toBe('foo bar')
await workspace.deleteFile(file, { ignoreIfNotExists: true })
})
it('should undo and redo workspace edit', async () => {
const folder = path.join(os.tmpdir(), uuid())
const pathone = path.join(folder, 'a')
const pathtwo = path.join(folder, 'b')
await workspace.files.createFile(pathone, { overwrite: true })
await workspace.files.createFile(pathtwo, { overwrite: true })
let uris = [URI.file(pathone).toString(), URI.file(pathtwo).toString()]
const assertContent = (one: string, two: string) => {
let doc = workspace.getDocument(uris[0])
expect(doc.getDocumentContent()).toBe(one)
doc = workspace.getDocument(uris[1])
expect(doc.getDocumentContent()).toBe(two)
}
let edits: TextDocumentEdit[] = []
edits.push(TextDocumentEdit.create({ uri: uris[0], version: null }, [
TextEdit.insert(Position.create(0, 0), 'foo')
]))
edits.push(TextDocumentEdit.create({ uri: uris[1], version: null }, [
TextEdit.insert(Position.create(0, 0), 'bar')
]))
await workspace.applyEdit({ documentChanges: edits })
assertContent('foo\n', 'bar\n')
await workspace.files.undoWorkspaceEdit()
assertContent('\n', '\n')
await workspace.files.redoWorkspaceEdit()
assertContent('foo\n', 'bar\n')
})
it('should should support annotations', async () => {
async function assertEdit(confirm: boolean): Promise<void> {
let doc = await helper.createDocument(uuid())
let edit: WorkspaceEdit = {
documentChanges: [
{
textDocument: { version: doc.version, uri: doc.uri },
edits: [
{
range: Range.create(0, 0, 0, 0),
newText: 'bar',
annotationId: '85bc78e2-5ef0-4949-b10c-13f476faf430'
}
]
},
],
changeAnnotations: {
'85bc78e2-5ef0-4949-b10c-13f476faf430': {
needsConfirmation: true,
label: 'Text changes',
description: 'description'
}
}
}
let p = workspace.files.applyEdit(edit)
await helper.waitPrompt()
if (confirm) {
await nvim.input('<cr>')
} else {
await nvim.input('<esc>')
}
await p
let content = doc.getDocumentContent()
if (confirm) {
expect(content).toBe('bar\n')
} else {
expect(content).toBe('\n')
}
}
await assertEdit(true)
await assertEdit(false)
})
})
describe('getOriginalLine', () => {
it('should get original line', async () => {
let item = { index: 0, filepath: '' }
expect(getOriginalLine(item, undefined)).toBeUndefined()
expect(getOriginalLine({ index: 0, filepath: '', lnum: 1 }, undefined)).toBe(1)
let doc = await helper.createDocument()
let change = {
textDocument: { version: doc.version, uri: doc.uri },
edits: [
{
range: Range.create(0, 0, 0, 0),
newText: 'bar',
}
]
}
expect(getOriginalLine({ index: 0, filepath: '', lnum: 1 }, change)).toBe(1)
})
describe('inspectEdit', () => {
async function inspect(edit: WorkspaceEdit): Promise<Buffer> {
await workspace.applyEdit(edit)
await workspace.files.inspectEdit()
let buf = await nvim.buffer
return buf
}
it('should show wanring when edit not exists', async () => {
(workspace.files as any).editState = undefined
await workspace.files.inspectEdit()
})
it('should render with changes', async () => {
let fsPath = await createTmpFile('foo\n1\n2\nbar')
let doc = await helper.createDocument(fsPath)
let newFile = path.join(os.tmpdir(), `coc-${process.pid}/new-${uuid()}`)
let newUri = URI.file(newFile).toString()
let createFile = path.join(os.tmpdir(), `coc-${process.pid}/create-${uuid()}`)
let deleteFile = await createTmpFile('delete')
disposables.push(Disposable.create(() => {
if (fs.existsSync(newFile)) fs.unlinkSync(newFile)
if (fs.existsSync(createFile)) fs.unlinkSync(createFile)
if (fs.existsSync(deleteFile)) fs.unlinkSync(deleteFile)
}))
let edit: WorkspaceEdit = {
documentChanges: [
{
textDocument: { version: null, uri: doc.uri, },
edits: [
TextEdit.del(Range.create(0, 0, 1, 0)),
TextEdit.replace(Range.create(3, 0, 3, 3), 'xyz'),
]
},
{
kind: 'rename',
oldUri: doc.uri,
newUri
}, {
kind: 'create',
uri: URI.file(createFile).toString()
}, {
kind: 'delete',
uri: URI.file(deleteFile).toString()
}
]
}
let buf = await inspect(edit)
let lines = await buf.lines
let content = lines.join('\n')
expect(content).toMatch('Change')
expect(content).toMatch('Rename')
expect(content).toMatch('Create')
expect(content).toMatch('Delete')
await nvim.command('exe 5')
await nvim.input('<CR>')
await helper.waitFor('expand', ['%:p'], newFile)
let line = await nvim.call('line', ['.'])
expect(line).toBe(3)
})
it('should render annotation label', async () => {
let doc = await helper.createDocument(uuid())
let edit: WorkspaceEdit = {
documentChanges: [
{
textDocument: { version: doc.version, uri: doc.uri },
edits: [
{
range: Range.create(0, 0, 0, 0),
newText: 'bar',
annotationId: 'dd866f37-a24c-4503-9c35-c139fb28e25b'
}
]
}, {
textDocument: { version: 1, uri: doc.uri },
edits: [
{
range: Range.create(0, 0, 0, 0),
newText: 'bar',
annotationId: '9468b9bf-97b6-4b37-b21f-aba8df3ce658'
}
]
}],
changeAnnotations: {
'dd866f37-a24c-4503-9c35-c139fb28e25b': {
needsConfirmation: false,
label: 'Text changes'
}
}
}
let buf = await inspect(edit)
await events.fire('BufUnload', [buf.id + 1])
let winid = await nvim.call('win_getid')
let lines = await buf.lines
expect(lines[0]).toBe('Text changes')
await nvim.command('exe 1')
await nvim.input('<CR>')
let bufnr = await nvim.call('bufnr', ['%'])
expect(bufnr).toBe(buf.id)
await nvim.command('exe 3')
await nvim.input('<CR>')
let fsPath = URI.parse(doc.uri).fsPath
await helper.waitFor('expand', ['%:p'], fsPath)
await nvim.call('win_gotoid', [winid])
await nvim.input('<esc>')
await helper.wait(10)
})
})
describe('createFile()', () => {
it('should create and revert parent folder', async () => {
const folder = path.join(os.tmpdir(), uuid())
const filepath = path.join(folder, 'bar')
disposables.push(Disposable.create(() => {
fs.rmSync(folder, { recursive: true, force: true })
}))
let fns: RecoverFunc[] = []
expect(fs.existsSync(folder)).toBe(false)
await workspace.files.createFile(filepath, {}, fns)
expect(fs.existsSync(filepath)).toBe(true)
for (let i = fns.length - 1; i >= 0; i--) {
await fns[i]()
}
expect(fs.existsSync(folder)).toBe(false)
})
it('should throw when file already exists', async () => {
let filepath = await createTmpFile('foo', disposables)
let fn = async () => {
await workspace.createFile(filepath, {})
}
await expect(fn()).rejects.toThrow(Error)
})
it('should not create file if file exists with ignoreIfExists', async () => {
let file = await createTmpFile('foo')
await workspace.createFile(file, { ignoreIfExists: true })
let content = fs.readFileSync(file, 'utf8')
expect(content).toBe('foo')
})
it('should create file if does not exist', async () => {
await helper.edit()
let filepath = path.join(__dirname, 'foo')
await workspace.createFile(filepath, { ignoreIfExists: true })
let exists = fs.existsSync(filepath)
expect(exists).toBe(true)
fs.unlinkSync(filepath)
})
it('should revert file create', async () => {
let filepath = path.join(os.tmpdir(), uuid())
disposables.push(Disposable.create(() => {
if (fs.existsSync(filepath)) fs.unlinkSync(filepath)
}))
let fns: RecoverFunc[] = []
await workspace.files.createFile(filepath, { overwrite: true }, fns)
expect(fs.existsSync(filepath)).toBe(true)
let bufnr = await nvim.call('bufnr', [filepath])
expect(bufnr).toBeGreaterThan(0)
let doc = workspace.getDocument(bufnr)
expect(doc).toBeDefined()
for (let fn of fns) {
await fn()
}
expect(fs.existsSync(filepath)).toBe(false)
let loaded = await nvim.call('bufloaded', [filepath])
expect(loaded).toBe(0)
})
})
describe('renameFile', () => {
it('should throw when oldPath not exists', async () => {
let filepath = path.join(__dirname, 'not_exists_file')
let newPath = path.join(__dirname, 'bar')
let fn = async () => {
await workspace.renameFile(filepath, newPath)
}
await expect(fn()).rejects.toThrow(Error)
})
it('should rename file on disk', async () => {
let filepath = await createTmpFile('test')
let newPath = path.join(path.dirname(filepath), 'new_file')
disposables.push(Disposable.create(() => {
if (fs.existsSync(newPath)) fs.unlinkSync(newPath)
if (fs.existsSync(filepath)) fs.unlinkSync(filepath)
}))
let fns: RecoverFunc[] = []
await workspace.files.renameFile(filepath, newPath, { overwrite: true }, fns)
expect(fs.existsSync(newPath)).toBe(true)
for (let fn of fns) {
await fn()
}
expect(fs.existsSync(newPath)).toBe(false)
expect(fs.existsSync(filepath)).toBe(true)
})
it('should rename if file does not exist', async () => {
let filepath = path.join(__dirname, 'foo')
let newPath = path.join(__dirname, 'bar')
await workspace.createFile(filepath)
await workspace.renameFile(filepath, newPath)
expect(fs.existsSync(newPath)).toBe(true)
expect(fs.existsSync(filepath)).toBe(false)
fs.unlinkSync(newPath)
})
it('should rename current buffer with same bufnr', async () => {
let file = await createTmpFile('test')
let doc = await helper.createDocument(file)
await nvim.setLine('bar')
await doc.patchChange()
let newFile = path.join(os.tmpdir(), `coc-${process.pid}/new-${uuid()}`)
disposables.push(Disposable.create(() => {
if (fs.existsSync(newFile)) fs.unlinkSync(newFile)
}))
await workspace.renameFile(file, newFile)
let bufnr = await nvim.call('bufnr', ['%'])
expect(bufnr).toBe(doc.bufnr)
let line = await nvim.line
expect(line).toBe('bar')
let exists = fs.existsSync(newFile)
expect(exists).toBe(true)
})
it('should overwrite if file exists', async () => {
let filepath = path.join(os.tmpdir(), uuid())
let newPath = path.join(os.tmpdir(), uuid())
await workspace.createFile(filepath)
await workspace.createFile(newPath)
await workspace.renameFile(filepath, newPath, { overwrite: true })
expect(fs.existsSync(newPath)).toBe(true)
expect(fs.existsSync(filepath)).toBe(false)
fs.unlinkSync(newPath)
})
it('should rename buffer in directory and revert', async () => {
let folder = path.join(os.tmpdir(), uuid())
let newFolder = path.join(os.tmpdir(), uuid())
fs.mkdirSync(folder)
disposables.push(Disposable.create(() => {
fs.rmSync(folder, { recursive: true, force: true })
fs.rmSync(newFolder, { recursive: true, force: true })
}))
let filepath = path.join(folder, 'new_file')
await workspace.createFile(filepath)
let bufnr = await nvim.call('bufnr', [filepath])
expect(bufnr).toBeGreaterThan(0)
let fns: RecoverFunc[] = []
await workspace.files.renameFile(folder, newFolder, { overwrite: true }, fns)
bufnr = await nvim.call('bufnr', [path.join(newFolder, 'new_file')])
expect(bufnr).toBeGreaterThan(0)
for (let i = fns.length - 1; i >= 0; i--) {
await fns[i]()
}
bufnr = await nvim.call('bufnr', [filepath])
expect(bufnr).toBeGreaterThan(0)
})
})
describe('loadResource()', () => {
it('should load file as hidden buffer', async () => {
helper.updateConfiguration('workspace.openResourceCommand', '')
let filepath = await createTmpFile('foo')
let uri = URI.file(filepath).toString()
let doc = await workspace.files.loadResource(uri)
let bufnrs = await nvim.call('coc#window#bufnrs') as number[]
expect(bufnrs.indexOf(doc.bufnr)).toBe(-1)
})
})
describe('deleteFile()', () => {
it('should throw when file not exists', async () => {
let filepath = path.join(__dirname, 'not_exists')
let fn = async () => {
await workspace.deleteFile(filepath)
}
await expect(fn()).rejects.toThrow(Error)
})
it('should ignore when ignoreIfNotExists set', async () => {
let filepath = path.join(__dirname, 'not_exists')
let fns: RecoverFunc[] = []
await workspace.files.deleteFile(filepath, { ignoreIfNotExists: true }, fns)
expect(fns.length).toBe(0)
})
it('should unload loaded buffer', async () => {
let filepath = await createTmpFile('file to delete')
disposables.push(Disposable.create(() => {
if (fs.existsSync(filepath)) fs.unlinkSync(filepath)
}))
await workspace.files.loadResource(URI.file(filepath).toString())
let fns: RecoverFunc[] = []
await workspace.files.deleteFile(filepath, {}, fns)
let loaded = await nvim.call('bufloaded', [filepath])
expect(loaded).toBe(0)
for (let i = fns.length - 1; i >= 0; i--) {
await fns[i]()
}
expect(fs.existsSync(filepath)).toBe(true)
loaded = await nvim.call('bufloaded', [filepath])
expect(loaded).toBe(1)
})
it('should delete and recover folder', async () => {
let folder = path.join(os.tmpdir(), uuid())
disposables.push(Disposable.create(() => {
if (fs.existsSync(folder)) fs.rmdirSync(folder)
}))
fs.mkdirSync(folder)
expect(fs.existsSync(folder)).toBe(true)
let fns: RecoverFunc[] = []
await workspace.files.deleteFile(folder, {}, fns)
expect(fs.existsSync(folder)).toBe(false)
for (let i = fns.length - 1; i >= 0; i--) {
await fns[i]()
}
expect(fs.existsSync(folder)).toBe(true)
await workspace.files.deleteFile(folder, {})
})
it('should delete and recover folder recursive', async () => {
let folder = path.join(os.tmpdir(), uuid())
disposables.push(Disposable.create(() => {
fs.rmSync(folder, { recursive: true, force: true })
}))
fs.mkdirSync(folder)
fs.writeFileSync(path.join(folder, 'new_file'), '', 'utf8')
let fns: RecoverFunc[] = []
await workspace.files.deleteFile(folder, { recursive: true }, fns)
expect(fs.existsSync(folder)).toBe(false)
for (let i = fns.length - 1; i >= 0; i--) {
await fns[i]()
}
expect(fs.existsSync(folder)).toBe(true)
expect(fs.existsSync(path.join(folder, 'new_file'))).toBe(true)
await workspace.files.deleteFile(folder, { recursive: true })
})
it('should delete file if exists', async () => {
let filepath = path.join(__dirname, 'foo')
await workspace.createFile(filepath)
expect(fs.existsSync(filepath)).toBe(true)
await workspace.deleteFile(filepath)
expect(fs.existsSync(filepath)).toBe(false)
})
})
describe('loadFile()', () => {
it('should single loadFile', async () => {
let doc = await helper.createDocument()
let newFile = URI.file(path.join(__dirname, 'abc')).toString()
let document = await workspace.loadFile(newFile)
let bufnr = await nvim.call('bufnr', '%')
expect(document.uri.endsWith('abc')).toBe(true)
expect(bufnr).toBe(doc.bufnr)
})
})
describe('loadFiles', () => {
it('should loadFiles', async () => {
let files = ['a', 'b', 'c'].map(key => URI.file(path.join(__dirname, key)).toString())
let docs = await workspace.loadFiles(files)
let uris = docs.map(o => o.uri)
expect(uris).toEqual(files)
})
it('should load empty files array', async () => {
await workspace.loadFiles([])
})
})
describe('openTextDocument()', () => {
it('should open document already exists', async () => {
let doc = await helper.createDocument('a')
await nvim.command('enew')
await workspace.openTextDocument(URI.parse(doc.uri))
let curr = await workspace.document
expect(curr.uri).toBe(doc.uri)
})
it('should throw when file does not exist', async () => {
let err
try {
await workspace.openTextDocument('/a/b/c')
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should open untitled document', async () => {
let doc = await workspace.openTextDocument(URI.parse(`untitled:///a/b.js`))
expect(doc.uri).toBe('file:///a/b.js')
})
it('should load file that exists', async () => {
let doc = await workspace.openTextDocument(URI.file(__filename))
expect(URI.parse(doc.uri).fsPath).toBe(__filename)
let curr = await workspace.document
expect(curr.uri).toBe(doc.uri)
})
})
})

120
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/funcs.test.ts

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
import os from 'os'
import fs from 'fs'
import path from 'path'
import Configurations from '../../configuration/index'
import * as funcs from '../../core/funcs'
import Resolver from '../../model/resolver'
import which from 'which'
import { v4 as uuid } from 'uuid'
let configurations: Configurations
beforeAll(async () => {
let userConfigFile = path.join(process.env.COC_VIMCONFIG, 'coc-settings.json')
configurations = new Configurations(userConfigFile, undefined)
})
describe('Resolver()', () => {
it('should resolve null', async () => {
let r = new Resolver()
let spy = jest.spyOn(which, 'sync').mockImplementation(() => {
throw new Error('not found')
})
let res = await r.resolveModule('mode')
expect(res).toBe(null)
spy.mockRestore()
})
it('should resolve npm module', async () => {
let r = new Resolver()
let folder = path.join(os.tmpdir(), uuid())
Object.assign(r, {
_npmFolder: folder,
_yarnFolder: __dirname,
})
fs.mkdirSync(path.join(folder, 'name'), { recursive: true })
fs.writeFileSync(path.join(folder, 'name', 'package.json'), '', 'utf8')
let res = await r.resolveModule('name')
expect(res).toBe(path.join(folder, 'name'))
})
})
describe('has()', () => {
it('should throw for invalid argument', async () => {
let env = {
isVim: true,
version: '8023956'
}
let err
try {
expect(funcs.has(env, '0.5.0')).toBe(true)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should detect version on vim8', async () => {
let env = {
isVim: true,
version: '8023956'
}
expect(funcs.has(env, 'patch-7.4.248')).toBe(true)
expect(funcs.has(env, 'patch-8.5.1')).toBe(false)
})
it('should delete version on neovim', async () => {
let env = {
isVim: false,
version: '0.6.1'
}
expect(funcs.has(env, 'nvim-0.5.0')).toBe(true)
expect(funcs.has(env, 'nvim-0.7.0')).toBe(false)
})
})
describe('createNameSpace()', () => {
it('should create namespace', async () => {
let nr = funcs.createNameSpace('ns')
expect(nr).toBeDefined()
expect(nr).toBe(funcs.createNameSpace('ns'))
})
})
describe('getWatchmanPath()', () => {
it('should get watchman path', async () => {
let res = funcs.getWatchmanPath(configurations)
expect(typeof res === 'string' || res == null).toBe(true)
})
})
describe('findUp()', () => {
it('should return null when can not find ', async () => {
let nvim: any = {
call: () => {
return __filename
}
}
let res = await funcs.findUp(nvim, os.homedir(), ['file_not_exists'])
expect(res).toBeNull()
})
it('should return null when unable find cwd in cwd', async () => {
let nvim: any = {
call: () => {
return ''
}
}
let res = await funcs.findUp(nvim, os.homedir(), ['file_not_exists'])
expect(res).toBeNull()
})
})
describe('score()', () => {
it('should return score', async () => {
expect(funcs.score(undefined, 'untitled:///1', '')).toBe(0)
expect(funcs.score({ scheme: '*' }, 'untitled:///1', '')).toBe(3)
expect(funcs.score('vim', 'untitled:///1', 'vim')).toBe(10)
expect(funcs.score('*', 'untitled:///1', '')).toBe(5)
expect(funcs.score('', 'untitled:///1', 'vim')).toBe(0)
})
})

79
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/keymaps.test.ts

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
import { Neovim } from '@chemzqm/neovim'
import workspace from '../../workspace'
import Keymaps from '../../core/keymaps'
import helper from '../helper'
import { Disposable } from 'vscode-languageserver-protocol'
import { disposeAll } from '../../util'
let nvim: Neovim
let keymaps: Keymaps
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
keymaps = workspace.keymaps
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('doKeymap()', () => {
it('should not throw when key not mapped', async () => {
await keymaps.doKeymap('<C-a>', '', '{C-a}')
})
})
describe('registerKeymap()', () => {
it('should throw for invalid key', async () => {
let err
try {
keymaps.registerKeymap(['i'], '', jest.fn())
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should throw for duplicated key', async () => {
keymaps.registerKeymap(['i'], 'tmp', jest.fn())
let err
try {
keymaps.registerKeymap(['i'], 'tmp', jest.fn())
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should register insert key mapping', async () => {
let fn = jest.fn()
disposables.push(keymaps.registerKeymap(['i'], 'test', fn))
await helper.wait(10)
let res = await nvim.call('execute', ['verbose imap <Plug>(coc-test)'])
expect(res).toMatch('coc#_insert_key')
})
})
describe('registerExprKeymap()', () => {
it('should visual key mapping', async () => {
await nvim.setLine('foo')
let called = false
let fn = () => {
called = true
return ''
}
disposables.push(keymaps.registerExprKeymap('x', 'x', fn, true))
await helper.wait(50)
await nvim.command('normal! viw')
await nvim.input('x<esc>')
await helper.wait(50)
expect(called).toBe(true)
})
})

148
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/locations.test.ts

@ -0,0 +1,148 @@ @@ -0,0 +1,148 @@
import { Neovim } from '@chemzqm/neovim'
import os from 'os'
import path from 'path'
import { Location, Range } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
await nvim.command(`source ${path.join(process.cwd(), 'autoload/coc/ui.vim')}`)
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
function createLocations(): Location[] {
let uri = URI.file(__filename).toString()
return [Location.create(uri, Range.create(0, 0, 1, 0)), Location.create(uri, Range.create(2, 0, 3, 0))]
}
describe('showLocations()', () => {
it('should show location list by default', async () => {
let locations = createLocations()
await workspace.showLocations(locations)
await helper.waitFor('bufname', ['%'], 'list:///location')
})
it('should fire autocmd when location list disabled', async () => {
Object.assign(workspace.env, {
locationlist: false
})
await nvim.exec(`
function OnLocationsChange()
let g:called = 1
endfunction
autocmd User CocLocationsChange :call OnLocationsChange()`)
let locations = createLocations()
await workspace.showLocations(locations)
await helper.waitFor('eval', [`get(g:,'called',0)`], 1)
})
it('should show quickfix when quickfix enabled', async () => {
helper.updateConfiguration('coc.preferences.useQuickfixForLocations', true)
let locations = createLocations()
await workspace.showLocations(locations)
await helper.waitFor('eval', [`&buftype`], 'quickfix')
})
it('should use customized quickfix open command', async () => {
await nvim.setVar('coc_quickfix_open_command', 'copen 1')
helper.updateConfiguration('coc.preferences.useQuickfixForLocations', true)
let locations = createLocations()
await workspace.showLocations(locations)
await helper.waitFor('eval', [`&buftype`], 'quickfix')
let win = await nvim.window
let height = await win.height
expect(height).toBe(1)
})
})
describe('jumpTo()', () => {
it('should jumpTo position', async () => {
let uri = URI.file('/tmp/foo').toString()
await workspace.jumpTo(uri, { line: 1, character: 1 })
await nvim.command('setl buftype=nofile')
let buf = await nvim.buffer
let name = await buf.name
expect(name).toMatch('/foo')
await buf.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
await workspace.jumpTo(uri, { line: 1, character: 1 })
let pos = await nvim.call('getcurpos')
expect(pos.slice(1, 3)).toEqual([2, 2])
})
it('should jumpTo uri without normalize', async () => {
let uri = 'zipfile:///tmp/clojure-1.9.0.jar::clojure/core.clj'
await workspace.jumpTo(uri)
let buf = await nvim.buffer
let name = await buf.name
expect(name).toBe(uri)
})
it('should jump without position', async () => {
let uri = URI.file('/tmp/foo').toString()
await workspace.jumpTo(uri)
let buf = await nvim.buffer
let name = await buf.name
expect(name).toMatch('/foo')
})
it('should jumpTo custom uri scheme', async () => {
let uri = 'jdt://foo'
await workspace.jumpTo(uri, { line: 1, character: 1 })
let buf = await nvim.buffer
let name = await buf.name
expect(name).toBe(uri)
})
})
describe('openResource()', () => {
it('should open resource', async () => {
let uri = URI.file(path.join(os.tmpdir(), 'bar')).toString()
await workspace.openResource(uri)
let buf = await nvim.buffer
let name = await buf.name
expect(name).toMatch('bar')
})
it('should open none file uri', async () => {
workspace.registerTextDocumentContentProvider('jd', {
provideTextDocumentContent: () => 'jd'
})
let uri = 'jd://abc'
await workspace.openResource(uri)
let buf = await nvim.buffer
let name = await buf.name
expect(name).toBe('jd://abc')
})
it('should open opened buffer', async () => {
let buf = await helper.edit()
let doc = workspace.getDocument(buf.id)
await workspace.openResource(doc.uri)
await helper.wait(30)
let bufnr = await nvim.call('bufnr', '%')
expect(bufnr).toBe(buf.id)
})
it('should open url', async () => {
await helper.mockFunction('coc#ui#open_url', 0)
let buf = await helper.edit()
let uri = 'http://example.com'
await workspace.openResource(uri)
await helper.wait(30)
let bufnr = await nvim.call('bufnr', '%')
expect(bufnr).toBe(buf.id)
})
})

145
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/terminals.test.ts

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
import { Neovim } from '@chemzqm/neovim'
import os from 'os'
import path from 'path'
import which from 'which'
import Terminals from '../../core/terminals'
import window from '../../window'
import TerminalModel from '../../model/terminal'
import helper from '../helper'
import { v4 as uuid } from 'uuid'
let nvim: Neovim
let terminals: Terminals
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
terminals = new Terminals()
})
afterEach(() => {
terminals.reset()
})
afterAll(async () => {
await helper.shutdown()
})
describe('create terminal', () => {
it('should use cleaned env', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash'),
strictEnv: true
})
await helper.wait(50)
terminal.sendText(`echo $NODE_ENV`, true)
await helper.wait(50)
let buf = nvim.createBuffer(terminal.bufnr)
let lines = await buf.lines
expect(lines.includes('test')).toBe(false)
})
it('should use custom shell command', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash')
})
let bufnr = terminal.bufnr
let bufname = await nvim.call('bufname', [bufnr]) as string
expect(bufname.includes('bash')).toBe(true)
})
it('should use custom cwd', async () => {
let basename = path.basename(os.tmpdir())
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
cwd: os.tmpdir()
})
let bufnr = terminal.bufnr
let bufname = await nvim.call('bufname', [bufnr]) as string
expect(bufname.includes(basename)).toBe(true)
})
it('should have exit code', async () => {
let exitStatus
terminals.onDidCloseTerminal(terminal => {
exitStatus = terminal.exitStatus
})
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash'),
strictEnv: true
})
terminal.sendText('exit', true)
await helper.waitFor('bufloaded', [terminal.bufnr], 0)
await helper.waitValue(() => {
return exitStatus != null
}, true)
expect(exitStatus.code).toBeDefined()
})
it('should return false on show when buffer unloaded', async () => {
let model = new TerminalModel('bash', [], nvim)
await model.start()
expect(model.bufnr).toBeDefined()
await nvim.command(`bd! ${model.bufnr}`)
let res = await model.show()
expect(res).toBe(false)
})
it('should not throw when show & hide disposed terminal', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash')
})
terminal.dispose()
await terminal.show()
await terminal.hide()
})
it('should show terminal on current window', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash')
})
let winid = await nvim.call('bufwinid', [terminal.bufnr])
expect(winid).toBeGreaterThan(0)
await nvim.call('win_gotoid', [winid])
await terminal.show()
})
it('should show terminal that shown', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash')
})
let res = await terminal.show(true)
expect(res).toBe(true)
expect(terminal.bufnr).toBeDefined()
let winid = await nvim.call('bufwinid', [terminal.bufnr])
let curr = await nvim.call('win_getid', [])
expect(winid != curr).toBe(true)
})
it('should show hidden terminal', async () => {
let terminal = await terminals.createTerminal(nvim, {
name: `test-${uuid()}`,
shellPath: which.sync('bash')
})
await terminal.hide()
await terminal.show()
})
it('should create terminal', async () => {
let terminal = await window.createTerminal({
name: `test-${uuid()}`,
})
expect(terminal).toBeDefined()
expect(terminal.processId).toBeDefined()
expect(terminal.name).toBeDefined()
terminal.dispose()
await helper.wait(30)
expect(terminal.exitStatus).toEqual({ code: undefined })
})
})

117
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/ui.test.ts

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
import { Neovim } from '@chemzqm/neovim'
import { Position, Range } from 'vscode-languageserver-types'
import * as ui from '../../core/ui'
import helper from '../helper'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('getCursorPosition()', () => {
it('should get cursor position', async () => {
await nvim.call('cursor', [1, 1])
let res = await ui.getCursorPosition(nvim)
expect(res).toEqual({
line: 0,
character: 0
})
})
})
describe('moveTo()', () => {
it('should moveTo position', async () => {
await nvim.setLine('foo')
await ui.moveTo(nvim, Position.create(0, 1), true)
let res = await ui.getCursorPosition(nvim)
expect(res).toEqual({ line: 0, character: 1 })
})
})
describe('getCursorScreenPosition()', () => {
it('should get cursor screen position', async () => {
let res = await ui.getCursorScreenPosition(nvim)
expect(res).toBeDefined()
expect(typeof res.row).toBe('number')
expect(typeof res.col).toBe('number')
})
})
describe('createFloatFactory()', () => {
it('should create FloatFactory', async () => {
let f = ui.createFloatFactory(nvim, { border: true, autoHide: false }, { close: true })
await f.show([{ content: 'shown', filetype: 'txt' }])
let activated = await f.activated()
expect(activated).toBe(true)
expect(f.window != null).toBe(true)
let win = await helper.getFloat()
expect(win).toBeDefined()
let id = await nvim.call('coc#float#get_related', [win.id, 'border', 0]) as number
expect(id).toBeGreaterThan(0)
id = await nvim.call('coc#float#get_related', [win.id, 'close', 0]) as number
expect(id).toBeGreaterThan(0)
await f.show([{ content: 'shown', filetype: 'txt' }], { offsetX: 10 })
let curr = await helper.getFloat()
expect(curr.id).toBe(win.id)
})
})
describe('showMessage()', () => {
it('should showMessage on vim', async () => {
ui.showMessage(nvim, 'my message', 'MoreMsg', true)
await helper.wait(100)
let cmdline = await helper.getCmdline()
expect(cmdline).toMatch(/my message/)
})
})
describe('getSelection()', () => {
it('should return null when no selection exists', async () => {
let res = await ui.getSelection(nvim, 'v')
expect(res).toBeNull()
})
it('should return range for line selection', async () => {
await nvim.setLine('foo')
await nvim.input('V')
await nvim.input('<esc>')
let res = await ui.getSelection(nvim, 'V')
expect(res).toEqual({ start: { line: 0, character: 0 }, end: { line: 1, character: 0 } })
})
it('should return range of current line', async () => {
await nvim.command('normal! gg')
let res = await ui.getSelection(nvim, 'currline')
expect(res).toEqual(Range.create(0, 0, 1, 0))
})
})
describe('selectRange()', () => {
it('should select range #1', async () => {
await nvim.call('setline', [1, ['foo', 'b']])
await nvim.command('set selection=inclusive')
await nvim.command('set virtualedit=onemore')
await ui.selectRange(nvim, Range.create(0, 0, 1, 1), true)
await nvim.input('<esc>')
let res = await ui.getSelection(nvim, 'v')
expect(res).toEqual(Range.create(0, 0, 1, 1))
})
it('should select range #2', async () => {
await nvim.call('setline', [1, ['foo', 'b']])
await ui.selectRange(nvim, Range.create(0, 0, 1, 0), true)
await nvim.input('<esc>')
let res = await ui.getSelection(nvim, 'v')
expect(res).toEqual(Range.create(0, 0, 0, 3))
})
})

315
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/core/workspaceFolder.test.ts

@ -0,0 +1,315 @@ @@ -0,0 +1,315 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { Disposable, WorkspaceFolder, WorkspaceFoldersChangeEvent } from 'vscode-languageserver-protocol'
import { URI } from 'vscode-uri'
import Configurations from '../../configuration/index'
import WorkspaceFolderController from '../../core/workspaceFolder'
import { PatternType } from '../../types'
import { disposeAll } from '../../util'
import { CancellationError } from '../../util/errors'
import workspace from '../../workspace'
import helper from '../helper'
let workspaceFolder: WorkspaceFolderController
let configurations: Configurations
let disposables: Disposable[] = []
let nvim: Neovim
function updateConfiguration(key: string, value: any, defaults: any): void {
configurations.updateMemoryConfig({ [key]: value })
disposables.push({
dispose: () => {
configurations.updateMemoryConfig({ [key]: defaults })
}
})
}
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
let userConfigFile = path.join(process.env.COC_VIMCONFIG, 'coc-settings.json')
configurations = new Configurations(userConfigFile, undefined)
workspaceFolder = new WorkspaceFolderController(configurations)
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
workspaceFolder.reset()
})
afterAll(async () => {
await helper.shutdown()
})
describe('WorkspaceFolderController', () => {
describe('asRelativePath()', () => {
function assertAsRelativePath(input: string, expected: string, includeWorkspace?: boolean) {
const actual = workspaceFolder.getRelativePath(input, includeWorkspace)
expect(actual).toBe(expected)
}
it('should get relative path', async () => {
workspaceFolder.addWorkspaceFolder(`/Coding/Applications/NewsWoWBot`, false)
assertAsRelativePath('/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot')
assertAsRelativePath('/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart',
'/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart')
assertAsRelativePath('', '')
assertAsRelativePath('/foo/bar', '/foo/bar')
assertAsRelativePath('in/out', 'in/out')
})
it('should asRelativePath, same paths, #11402', async () => {
const root = '/home/aeschli/workspaces/samples/docker'
const input = '/home/aeschli/workspaces/samples/docker'
workspaceFolder.addWorkspaceFolder(root, false)
assertAsRelativePath(input, input)
const input2 = '/home/aeschli/workspaces/samples/docker/a.file'
assertAsRelativePath(input2, 'a.file')
})
it('should asRelativePath, not workspaceFolder', async () => {
expect(workspace.getRelativePath('')).toBe('')
assertAsRelativePath('/foo/bar', '/foo/bar')
})
it('should asRelativePath, multiple folders', () => {
workspaceFolder.addWorkspaceFolder(`/Coding/One`, false)
workspaceFolder.addWorkspaceFolder(`/Coding/Two`, false)
assertAsRelativePath('/Coding/One/file.txt', 'One/file.txt')
assertAsRelativePath('/Coding/Two/files/out.txt', 'Two/files/out.txt')
assertAsRelativePath('/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt')
})
it('should slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', async () => {
workspaceFolder.addWorkspaceFolder(`/Coding/One`, false)
workspaceFolder.addWorkspaceFolder(`/Coding/Two`, false)
assertAsRelativePath('/Coding/One/file.txt', 'One/file.txt')
assertAsRelativePath('/Coding/One/file.txt', 'One/file.txt', true)
assertAsRelativePath('/Coding/One/file.txt', 'file.txt', false)
assertAsRelativePath('/Coding/Two/files/out.txt', 'Two/files/out.txt')
assertAsRelativePath('/Coding/Two/files/out.txt', 'Two/files/out.txt', true)
assertAsRelativePath('/Coding/Two/files/out.txt', 'files/out.txt', false)
assertAsRelativePath('/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt')
assertAsRelativePath('/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true)
assertAsRelativePath('/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false)
})
})
describe('setWorkspaceFolders()', () => {
it('should set valid folders', async () => {
workspaceFolder.setWorkspaceFolders([os.tmpdir(), '/a/not_exists'])
let folders = workspaceFolder.workspaceFolders
expect(folders.length).toBe(2)
})
})
describe('getWorkspaceFolder()', () => {
it('should get workspaceFolder by uri', async () => {
let res = workspaceFolder.getWorkspaceFolder(URI.parse('untitled://1'))
expect(res).toBeUndefined()
res = workspaceFolder.getWorkspaceFolder(URI.file('/a/b'))
expect(res).toBeUndefined()
let filepath = path.join(process.cwd(), 'a/b')
workspaceFolder.setWorkspaceFolders([process.cwd()])
res = workspaceFolder.getWorkspaceFolder(URI.file(filepath))
expect(URI.parse(res.uri).fsPath).toBe(process.cwd())
})
})
describe('getRootPatterns()', () => {
it('should get patterns from b:coc_root_patterns', async () => {
await nvim.command('edit t.vim | let b:coc_root_patterns=["foo"]')
await nvim.command('setf vim')
let doc = await workspace.document
let res = workspaceFolder.getRootPatterns(doc, PatternType.Buffer)
expect(res).toEqual(['foo'])
})
it('should get patterns from languageserver', async () => {
updateConfiguration('languageserver', {
test: {
filetypes: ['vim'],
rootPatterns: ['bar']
}
}, {})
workspaceFolder.addRootPattern('vim', ['foo'])
await nvim.command('edit t.vim')
await nvim.command('setf vim')
let doc = await workspace.document
let res = workspaceFolder.getRootPatterns(doc, PatternType.LanguageServer)
expect(res).toEqual(['bar', 'foo'])
})
it('should get patterns from user configuration', async () => {
let doc = await workspace.document
let res = workspaceFolder.getRootPatterns(doc, PatternType.Global)
expect(res.includes('.git')).toBe(true)
})
})
describe('resolveRoot()', () => {
const cwd = process.cwd()
const expand = (input: string) => {
return workspace.expand(input)
}
it('should resolve to cwd for file in cwd', async () => {
updateConfiguration('coc.preferences.rootPatterns', [], ['.git', '.hg', '.projections.json'])
let file = path.join(os.tmpdir(), 'foo')
await nvim.command(`edit ${file}`)
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, os.tmpdir(), false, expand)
expect(res).toBe(os.tmpdir())
})
it('should not fallback to cwd as workspace folder', async () => {
updateConfiguration('coc.preferences.rootPatterns', [], ['.git', '.hg', '.projections.json'])
updateConfiguration('workspace.workspaceFolderFallbackCwd', false, true)
let file = path.join(os.tmpdir(), 'foo')
await nvim.command(`edit ${file}`)
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, os.tmpdir(), false, expand)
expect(res).toBe(null)
})
it('should return null for untitled buffer', async () => {
await nvim.command('enew')
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, cwd, false, expand)
expect(res).toBe(null)
})
it('should respect ignored filetypes', async () => {
updateConfiguration('workspace.ignoredFiletypes', ['vim'], [])
await nvim.command('edit t.vim')
await nvim.command('setf vim')
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, cwd, false, expand)
expect(res).toBe(null)
})
it('should respect workspaceFolderCheckCwd', async () => {
let called = 0
disposables.push(workspaceFolder.onDidChangeWorkspaceFolders(() => {
called++
}))
workspaceFolder.addRootPattern('vim', ['.vim'])
await nvim.command('edit a/.vim/t.vim')
await nvim.command('setf vim')
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, cwd, true, expand)
expect(res).toBe(process.cwd())
await nvim.command('edit a/foo')
doc = await workspace.document
res = workspaceFolder.resolveRoot(doc, cwd, true, expand)
expect(res).toBe(process.cwd())
expect(called).toBe(1)
})
it('should respect ignored folders', async () => {
updateConfiguration('workspace.ignoredFolders', ['$HOME/foo', '$HOME'], [])
let file = path.join(os.homedir(), '.vim/bar')
workspaceFolder.addRootPattern('vim', ['.vim'])
await nvim.command(`edit ${file}`)
await nvim.command('setf vim')
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, path.join(os.homedir(), 'foo'), true, expand)
expect(res).toBe(null)
})
describe('bottomUpFileTypes', () => {
it('should respect specific filetype', async () => {
updateConfiguration('coc.preferences.rootPatterns', ['.vim'], ['.git', '.hg', '.projections.json'])
updateConfiguration('workspace.bottomUpFiletypes', ['vim'], [])
let root = path.join(os.tmpdir(), 'a')
let dir = path.join(root, '.vim')
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
let file = path.join(dir, 'foo.vim')
await nvim.command(`edit ${file}`)
let doc = await workspace.document
expect(doc.filetype).toBe('vim')
let res = workspaceFolder.resolveRoot(doc, file, true, expand)
expect(res).toBe(root)
})
it('should respect wildcard', async () => {
updateConfiguration('coc.preferences.rootPatterns', ['.vim'], ['.git', '.hg', '.projections.json'])
updateConfiguration('workspace.bottomUpFiletypes', ['*'], [])
let root = path.join(os.tmpdir(), 'a')
let dir = path.join(root, '.vim')
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
await helper.wait(30)
}
let file = path.join(dir, 'foo')
await nvim.command(`edit ${file}`)
let doc = await workspace.document
let res = workspaceFolder.resolveRoot(doc, file, true, expand)
expect(res).toBe(root)
})
})
})
describe('renameWorkspaceFolder()', () => {
it('should rename workspaceFolder', async () => {
let e: WorkspaceFoldersChangeEvent
disposables.push(workspaceFolder.onDidChangeWorkspaceFolders(ev => {
e = ev
}))
let cwd = process.cwd()
workspaceFolder.addWorkspaceFolder(cwd, false)
workspaceFolder.addWorkspaceFolder(cwd, false)
workspaceFolder.renameWorkspaceFolder(cwd, path.join(cwd, '.vim'))
expect(e.removed.length).toBe(1)
expect(e.added.length).toBe(1)
})
})
describe('removeWorkspaceFolder()', () => {
it('should remote workspaceFolder', async () => {
let e: WorkspaceFoldersChangeEvent
disposables.push(workspaceFolder.onDidChangeWorkspaceFolders(ev => {
e = ev
}))
let cwd = process.cwd()
workspaceFolder.addWorkspaceFolder(cwd, false)
workspaceFolder.removeWorkspaceFolder(cwd)
workspaceFolder.removeWorkspaceFolder('/a/b')
expect(e.removed.length).toBe(1)
expect(e.added.length).toBe(0)
})
})
describe('checkPatterns()', () => {
it('should check if pattern exists', async () => {
expect(await workspaceFolder.checkPatterns([], ['p'])).toBe(false)
let folder: WorkspaceFolder = { name: '', uri: URI.file(process.cwd()).toString() }
let res = await workspaceFolder.checkPatterns([folder], ['package.json', '**/not_exists'])
expect(res).toBe(true)
res = await workspaceFolder.checkPatterns([folder], ['**/not_exists'])
expect(res).toBe(false)
})
it('should not throw on timeout', async () => {
let spy = jest.spyOn(workspaceFolder, 'checkFolder').mockImplementation((_dir, _patterns, token) => {
return new Promise((resolve, reject) => {
let timer = setTimeout(resolve, 200)
token.onCancellationRequested(() => {
clearTimeout(timer)
reject(new CancellationError())
})
})
})
let folder: WorkspaceFolder = { name: '', uri: URI.file(process.cwd()).toString() }
let res = await workspaceFolder.checkPatterns([folder], ['**/schema.json'])
spy.mockRestore()
expect(res).toBe(false)
})
})
})

401
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/callHierarchy.test.ts

@ -0,0 +1,401 @@ @@ -0,0 +1,401 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, CallHierarchyItem, SymbolKind, Range, SymbolTag, CancellationToken, Position } from 'vscode-languageserver-protocol'
import CallHierarchyHandler from '../../handler/callHierarchy'
import languages from '../../languages'
import workspace from '../../workspace'
import { disposeAll } from '../../util'
import { URI } from 'vscode-uri'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
let callHierarchy: CallHierarchyHandler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
callHierarchy = helper.plugin.getHandler().callHierarchy
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
function createCallItem(name: string, kind: SymbolKind, uri: string, range: Range): CallHierarchyItem {
return {
name,
kind,
uri,
range,
selectionRange: range
}
}
describe('CallHierarchy', () => {
it('should throw when provider does not exist', async () => {
let err
try {
await callHierarchy.getIncoming()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should return null when provider not exist', async () => {
let token = CancellationToken.None
let doc = await workspace.document
let res: any
res = await languages.prepareCallHierarchy(doc.textDocument, Position.create(0, 0), token)
expect(res).toBeNull()
let item = createCallItem('name', SymbolKind.Class, doc.uri, Range.create(0, 0, 1, 0))
res = await languages.provideOutgoingCalls(doc.textDocument, item, token)
expect(res).toBeNull()
res = await languages.provideIncomingCalls(doc.textDocument, item, token)
expect(res).toBeNull()
})
it('should throw when prepare failed', async () => {
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return undefined
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
return []
}
}))
let fn = async () => {
await callHierarchy.getOutgoing()
}
await expect(fn()).rejects.toThrow(Error)
})
it('should get incoming & outgoing callHierarchy items', async () => {
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, 'test:///foo', Range.create(0, 0, 0, 5))
},
provideCallHierarchyIncomingCalls() {
return [{
from: createCallItem('bar', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)),
fromRanges: [Range.create(0, 0, 0, 5)]
}]
},
provideCallHierarchyOutgoingCalls() {
return [{
to: createCallItem('bar', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)),
fromRanges: [Range.create(1, 0, 1, 5)]
}]
}
}))
let res = await callHierarchy.getIncoming()
expect(res.length).toBe(1)
expect(res[0].from.name).toBe('bar')
let outgoing = await callHierarchy.getOutgoing()
expect(outgoing.length).toBe(1)
res = await callHierarchy.getIncoming(outgoing[0].to)
expect(res.length).toBe(1)
})
it('should show warning when provider does not exist', async () => {
await callHierarchy.showCallHierarchyTree('incoming')
let line = await helper.getCmdline()
expect(line).toMatch('not found')
})
it('should show message when no result returned.', async () => {
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return null
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
return []
}
}))
await callHierarchy.showCallHierarchyTree('incoming')
let line = await helper.getCmdline()
expect(line).toMatch('Unable')
})
it('should render description and support default action', async () => {
helper.updateConfiguration('callHierarchy.enableTooltip', false)
let doc = await workspace.document
let bufnr = doc.bufnr
await doc.buffer.setLines(['foo'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(1, 0, 1, 3))
item.detail = 'Detail'
item.tags = [SymbolTag.Deprecated]
return [{
from: item,
fromRanges: [Range.create(2, 0, 2, 5)]
}]
},
provideCallHierarchyOutgoingCalls() {
return []
}
}))
await callHierarchy.showCallHierarchyTree('incoming')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'INCOMING CALLS',
'- c foo',
' + c bar Detail'
])
await nvim.command('exe 3')
await nvim.input('t')
await helper.waitFor('getline', ['.'], ' - c bar Detail')
await nvim.input('<cr>')
await helper.waitFor('expand', ['%:p'], fsPath)
let res = await nvim.call('coc#cursor#position')
expect(res).toEqual([1, 0])
let matches = await nvim.call('getmatches') as any[]
expect(matches.length).toBe(2)
await nvim.command(`b ${bufnr}`)
await helper.wait(50)
matches = await nvim.call('getmatches')
expect(matches.length).toBe(0)
await nvim.command(`wincmd o`)
})
it('should invoke open in new tab action', async () => {
let doc = await workspace.document
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1))
item.detail = 'Detail'
return [{
to: item,
fromRanges: [Range.create(1, 0, 1, 3)]
}]
}
}))
let win = await nvim.window
await callHierarchy.showCallHierarchyTree('outgoing')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' + c bar Detail'
])
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('<cr>')
await helper.waitFor('tabpagenr', [], 2)
doc = await workspace.document
expect(doc.uri).toBe(uri)
await helper.waitValue(async () => {
let res = await nvim.call('getmatches', [win.id])
return res.length
}, 1)
})
it('should invoke show incoming calls action', async () => {
let doc = await workspace.document
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
return [{
from: createCallItem('test', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)),
fromRanges: [Range.create(0, 0, 0, 5)]
}]
},
provideCallHierarchyOutgoingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1))
item.detail = 'Detail'
return [{
to: item,
fromRanges: [Range.create(1, 0, 1, 3)]
}]
}
}))
await callHierarchy.showCallHierarchyTree('outgoing')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' + c bar Detail'
])
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('3')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'INCOMING CALLS',
'- c bar Detail',
' + c test'
])
})
it('should invoke show outgoing calls action', async () => {
let doc = await workspace.document
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
return [{
from: createCallItem('test', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)),
fromRanges: [Range.create(0, 0, 0, 5)]
}]
},
provideCallHierarchyOutgoingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1))
item.detail = 'Detail'
return [{
to: item,
fromRanges: [Range.create(1, 0, 1, 3)]
}]
}
}))
await callHierarchy.showCallHierarchyTree('incoming')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'INCOMING CALLS',
'- c foo',
' + c test'
])
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('4')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c test',
' + c bar Detail'
])
})
it('should invoke dismiss action #1', async () => {
let doc = await workspace.document
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1))
item.detail = 'Detail'
return [{
to: item,
fromRanges: [Range.create(1, 0, 1, 3)]
}]
}
}))
await callHierarchy.showCallHierarchyTree('outgoing')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' + c bar Detail'
])
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('2')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo'
])
await nvim.command('exe 2')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('2')
await helper.wait(30)
})
it('should invoke dismiss action #2', async () => {
let doc = await workspace.document
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let fsPath = await createTmpFile('foo\nbar\ncontent\n')
let uri = URI.file(fsPath).toString()
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3))
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1))
item.detail = 'Detail'
return [{
to: item,
fromRanges: [Range.create(1, 0, 1, 3)]
}]
}
}))
await callHierarchy.showCallHierarchyTree('outgoing')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' + c bar Detail'
])
await nvim.command('exe 3')
await nvim.input('t')
await helper.waitFor('line', ['$'], 4)
await nvim.command('exe 4')
await nvim.input('<tab>')
await helper.waitPrompt()
await nvim.input('2')
await helper.waitFor('line', ['$'], 3)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' - c bar Detail'
])
})
})

449
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codeActions.test.ts

@ -0,0 +1,449 @@ @@ -0,0 +1,449 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, CodeAction, Command, CodeActionContext, CodeActionKind, TextEdit, Disposable, Range, Position } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import commands from '../../commands'
import ActionsHandler from '../../handler/codeActions'
import languages from '../../languages'
import { ProviderResult } from '../../provider'
import { disposeAll } from '../../util'
import { rangeInRange } from '../../util/position'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let codeActions: ActionsHandler
let currActions: CodeAction[]
let resolvedAction: CodeAction
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
codeActions = helper.plugin.getHandler().codeActions
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
_range: Range,
_context: CodeActionContext,
_token: CancellationToken
) => currActions,
resolveCodeAction: (
_action: CodeAction,
_token: CancellationToken
): ProviderResult<CodeAction> => resolvedAction
}, undefined))
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('handler codeActions', () => {
describe('organizeImport', () => {
it('should throw error when organize import action not found', async () => {
currActions = []
await helper.createDocument()
let err
try {
await codeActions.organizeImport()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should perform organize import action', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let edits: TextEdit[] = []
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar'))
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports)
currActions = [action, CodeAction.create('another action')]
await codeActions.organizeImport()
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar', 'foo'])
})
it('should register editor.action.organizeImport command', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false })
let edits: TextEdit[] = []
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar'))
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports)
currActions = [action, CodeAction.create('another action')]
await commands.executeCommand('editor.action.organizeImport')
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar', 'foo'])
})
})
describe('codeActionRange', () => {
it('should show warning when no action available', async () => {
await helper.createDocument()
currActions = []
await codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix)
let line = await helper.getCmdline()
expect(line).toMatch(/No quickfix code action/)
})
it('should apply chosen action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
let p = codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix)
await helper.waitPrompt()
await nvim.input('<CR>')
await p
let buf = nvim.createBuffer(doc.bufnr)
let lines = await buf.lines
expect(lines[0]).toBe('bar')
})
})
describe('getCodeActions', () => {
it('should get empty actions', async () => {
currActions = []
let doc = await helper.createDocument()
let res = await codeActions.getCodeActions(doc)
expect(res.length).toBe(0)
})
it('should not filter disabled actions', async () => {
currActions = []
let action = CodeAction.create('foo', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
action = CodeAction.create('bar', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
let doc = await helper.createDocument()
let res = await codeActions.getCodeActions(doc)
expect(res.length).toBe(2)
})
it('should get all actions', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false })
let action = CodeAction.create('curr action', CodeActionKind.Empty)
currActions = [action]
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: () => {
return [CodeAction.create('a')]
},
}, undefined))
let res = await codeActions.getCodeActions(doc)
expect(range).toEqual(Range.create(0, 0, 3, 0))
expect(res.length).toBe(4)
})
it('should filter actions by range', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false })
currActions = []
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
if (rangeInRange(r, Range.create(0, 0, 1, 0))) return [CodeAction.create('a')]
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
let res = await codeActions.getCodeActions(doc, Range.create(0, 0, 0, 0))
expect(range).toEqual(Range.create(0, 0, 0, 0))
expect(res.length).toBe(1)
})
it('should filter actions by kind prefix', async () => {
let doc = await helper.createDocument()
let action = CodeAction.create('my action', CodeActionKind.SourceFixAll)
currActions = [action]
let res = await codeActions.getCodeActions(doc, undefined, [CodeActionKind.Source])
expect(res.length).toBe(1)
expect(res[0].kind).toBe(CodeActionKind.SourceFixAll)
})
})
describe('getCurrentCodeActions', () => {
let range: Range
beforeEach(() => {
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
})
it('should get codeActions by line', async () => {
currActions = []
await helper.createDocument()
let res = await codeActions.getCurrentCodeActions('line')
expect(range).toEqual(Range.create(0, 0, 1, 0))
expect(res.length).toBe(3)
})
it('should get codeActions by cursor', async () => {
currActions = []
await helper.createDocument()
let res = await codeActions.getCurrentCodeActions('cursor')
expect(range).toEqual(Range.create(0, 0, 0, 0))
expect(res.length).toBe(3)
})
it('should get codeActions by visual mode', async () => {
currActions = []
await helper.createDocument()
await nvim.setLine('foo')
await nvim.command('normal! 0v$')
await nvim.input('<esc>')
let res = await codeActions.getCurrentCodeActions('v')
expect(range).toEqual(Range.create(0, 0, 0, 3))
expect(res.length).toBe(3)
})
})
describe('doCodeAction', () => {
it('should not throw when no action exists', async () => {
currActions = []
await helper.createDocument()
let err
try {
await codeActions.doCodeAction(undefined)
} catch (e) {
err = e
}
expect(err).toBeUndefined()
})
it('should apply single code action when only is title', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
await codeActions.doCodeAction(undefined, 'code fix')
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should apply single code action when only is codeAction array', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action]
await codeActions.doCodeAction(undefined, [CodeActionKind.QuickFix])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should show disabled code action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let refactorAction = CodeAction.create('code refactor', edit, CodeActionKind.Refactor)
refactorAction.disabled = { reason: 'invalid position' }
let fixAction = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [refactorAction, fixAction]
let p = codeActions.doCodeAction(undefined)
let winid = await helper.waitFloat()
let win = nvim.createWindow(winid)
let buf = await win.buffer
let lines = await buf.lines
expect(lines.length).toBe(2)
expect(lines[1]).toMatch(/code refactor/)
await nvim.input('2')
await helper.wait(50)
await nvim.input('j')
await nvim.input('<cr>')
await helper.wait(50)
let valid = await win.valid
expect(valid).toBe(true)
let cmdline = await helper.getCmdline()
expect(cmdline).toMatch(/invalid position/)
await nvim.input('<esc>')
})
it('should action dialog to choose action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
currActions = [action, CodeAction.create('foo')]
let promise = codeActions.doCodeAction(null)
await helper.wait(50)
let ids = await nvim.call('coc#float#get_float_win_list') as number[]
expect(ids.length).toBeGreaterThan(0)
await nvim.input('<CR>')
await promise
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should choose code actions by range', async () => {
let range: Range
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document: TextDocument,
r: Range,
_context: CodeActionContext, _token: CancellationToken
) => {
range = r
return [CodeAction.create('my title'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined))
await helper.createDocument()
await nvim.setLine('abc')
await nvim.command('normal! 0v$')
await nvim.input('<esc>')
await codeActions.doCodeAction('v', 'my title')
expect(range).toEqual({ start: { line: 0, character: 0 }, end: { line: 0, character: 3 } })
})
it('should filter by provider kinds', async () => {
currActions = []
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: () => {
return [CodeAction.create('my title'), CodeAction.create('b'), CodeAction.create('c')]
},
}, undefined, [CodeActionKind.QuickFix]))
let doc = await workspace.document
let res = await languages.getCodeActions(doc.textDocument, Range.create(0, 0, 1, 1), { only: [CodeActionKind.Refactor], diagnostics: [] }, CancellationToken.None)
expect(res).toEqual([])
})
it('should filter by codeAction kind', async () => {
currActions = []
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: () => {
return [
CodeAction.create('my title', CodeActionKind.QuickFix),
CodeAction.create('b'),
Command.create('command', 'command')
]
},
resolveCodeAction: () => {
return null
}
}, undefined))
let doc = await workspace.document
let res = await languages.getCodeActions(doc.textDocument, Range.create(0, 0, 1, 1), { only: [CodeActionKind.QuickFix], diagnostics: [] }, CancellationToken.None)
expect(res.length).toBe(2)
let resolved = await languages.resolveCodeAction(res[0], CancellationToken.None)
expect(resolved).toBeDefined()
})
})
describe('doQuickfix', () => {
it('should show message when quickfix action does not exist', async () => {
currActions = []
await helper.createDocument()
await codeActions.doQuickfix()
let msg = await helper.getCmdline()
expect(msg).toMatch('No quickfix')
})
it('should do preferred quickfix action', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [CodeAction.create('foo', CodeActionKind.QuickFix), action, CodeAction.create('bar')]
await codeActions.doQuickfix()
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
})
describe('applyCodeAction', () => {
it('should resolve codeAction', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [action]
resolvedAction = Object.assign({ edit }, action)
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix])
await codeActions.applyCodeAction(arr[0])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
})
it('should throw for disabled action', async () => {
let action: any = CodeAction.create('my action', CodeActionKind.Empty)
action.disabled = { reason: 'disabled', providerId: 'x' }
let err
try {
await codeActions.applyCodeAction(action)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should invoke registered command after apply edit', async () => {
let called
disposables.push(commands.registerCommand('test.execute', async (s: string) => {
called = s
await nvim.command(s)
}))
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push(TextEdit.insert(Position.create(0, 0), 'bar'))
let edit = { changes: { [doc.uri]: edits } }
let action = CodeAction.create('code fix', CodeActionKind.QuickFix)
action.isPreferred = true
currActions = [action]
resolvedAction = Object.assign({
edit,
command: Command.create('run vim command', 'test.execute', 'normal! $')
}, action)
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix])
await codeActions.applyCodeAction(arr[0])
let lines = await doc.buffer.lines
expect(lines).toEqual(['bar'])
expect(called).toBe('normal! $')
})
})
})

324
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codelens.test.ts

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, CodeLens, Command, Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import commands from '../../commands'
import events from '../../events'
import CodeLensBuffer, { getCommands } from '../../handler/codelens/buffer'
import CodeLensHandler from '../../handler/codelens/index'
import languages from '../../languages'
import { disposeAll } from '../../util'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let codeLens: CodeLensHandler
let disposables: Disposable[] = []
let srcId: number
jest.setTimeout(10000)
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
srcId = await nvim.createNamespace('coc-codelens')
codeLens = helper.plugin.getHandler().codeLens
})
beforeEach(() => {
helper.updateConfiguration('codeLens.enable', true)
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
})
async function createBufferWithCodeLens(): Promise<CodeLensBuffer> {
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1)
}]
},
resolveCodeLens: codeLens => {
codeLens.command = Command.create('save', '__save', 1, 2, 3)
return codeLens
}
}))
let doc = await helper.createDocument('e.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await doc.synchronize()
await codeLens.checkProvider()
return codeLens.buffers.getItem(doc.bufnr)
}
describe('codeLenes featrue', () => {
it('should return codeLenes when not resolve exists', async () => {
let codeLens = CodeLens.create(Range.create(0, 0, 1, 1))
let resolved = await languages.resolveCodeLens(codeLens, CancellationToken.None)
expect(resolved).toBeDefined()
})
it('should do codeLenes request and resolve codeLenes', async () => {
let buf = await createBufferWithCodeLens()
let doc = await workspace.document
let codelens = buf.currentCodeLens
expect(codelens).toBeDefined()
expect(codelens[0].command).toBeDefined()
let markers = await helper.getMarkers(doc.bufnr, srcId)
expect(markers.length).toBe(1)
})
it('should refresh on empty changes', async () => {
await createBufferWithCodeLens()
let doc = await workspace.document
await nvim.call('setline', [1, ['a', 'b', 'c']])
await doc.synchronize()
let markers = await helper.getMarkers(doc.bufnr, srcId)
expect(markers.length).toBe(1)
})
it('should work with empty codeLens', async () => {
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return []
}
}))
let doc = await helper.createDocument('t.js')
let buf = codeLens.buffers.getItem(doc.bufnr)
let codelens = buf.currentCodeLens
expect(codelens).toBeUndefined()
})
it('should change codeLenes position', async () => {
let fn = jest.fn()
helper.updateConfiguration('codeLens.position', 'eol')
disposables.push(commands.registerCommand('__save', (...args) => {
fn(...args)
}))
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1)
}]
},
resolveCodeLens: codeLens => {
codeLens.command = Command.create('save', '__save', 1, 2, 3)
return codeLens
}
}))
let doc = await helper.createDocument('example.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await codeLens.checkProvider()
let res = await doc.buffer.getExtMarks(srcId, 0, -1, { details: true })
expect(res.length).toBeGreaterThan(0)
let arr = res[0][3]['virt_text']
expect(arr[0][0]).toBe('save')
})
it('should refresh codeLens on CursorHold', async () => {
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: document => {
let n = document.lineCount
let arr: any[] = []
for (let i = 0; i <= n - 2; i++) {
arr.push({
range: Range.create(i, 0, i, 1),
command: Command.create('save', '__save', i)
})
}
return arr
}
}))
let doc = await helper.createDocument('example.js')
await helper.wait(100)
let markers = await helper.getMarkers(doc.bufnr, srcId)
await nvim.call('setline', [1, ['a', 'b', 'c']])
await doc.synchronize()
await events.fire('CursorHold', [doc.bufnr])
await helper.wait(200)
markers = await helper.getMarkers(doc.bufnr, srcId)
expect(markers.length).toBe(3)
})
it('should cancel codeLenes request on document change', async () => {
let cancelled = false
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: (_, token) => {
return new Promise(resolve => {
token.onCancellationRequested(() => {
cancelled = true
clearTimeout(timer)
resolve(null)
})
let timer = setTimeout(() => {
resolve([{
range: Range.create(0, 0, 0, 1)
}, {
range: Range.create(1, 0, 1, 1)
}])
}, 2000)
disposables.push({
dispose: () => {
clearTimeout(timer)
}
})
})
},
resolveCodeLens: codeLens => {
codeLens.command = Command.create('save', '__save')
return codeLens
}
}))
let doc = await helper.createDocument('codelens.js')
await helper.wait(50)
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'a\nb\nc')])
expect(cancelled).toBe(true)
})
it('should resolve on CursorMoved', async () => {
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return [{
range: Range.create(90, 0, 90, 1)
}, {
range: Range.create(91, 0, 91, 1)
}]
},
resolveCodeLens: async codeLens => {
codeLens.command = Command.create('save', '__save')
return codeLens
}
}))
let doc = await helper.createDocument('example.js')
let arr = new Array(100)
arr.fill('')
await nvim.call('setline', [1, arr])
await doc.synchronize()
await codeLens.checkProvider()
await nvim.command('normal! gg')
await nvim.command('normal! G')
await helper.wait(100)
let buf = codeLens.buffers.getItem(doc.bufnr)
let codelens = buf.currentCodeLens
expect(codelens).toBeDefined()
expect(codelens[0].command).toBeDefined()
expect(codelens[1].command).toBeDefined()
})
it('should invoke codeLenes action', async () => {
let fn = jest.fn()
disposables.push(commands.registerCommand('__save', (...args) => {
fn(...args)
}))
await createBufferWithCodeLens()
await helper.doAction('codeLensAction')
expect(fn).toBeCalledWith(1, 2, 3)
await nvim.command('normal! G')
await helper.doAction('codeLensAction')
})
it('should use picker for multiple codeLenses', async () => {
let fn = jest.fn()
disposables.push(commands.registerCommand('__save', (...args) => {
fn(...args)
}))
disposables.push(commands.registerCommand('__delete', (...args) => {
fn(...args)
}))
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1),
command: Command.create('save', '__save', 1, 2, 3)
}, {
range: Range.create(0, 1, 0, 2),
command: Command.create('save', '__delete', 4, 5, 6)
}]
}
}))
let doc = await helper.createDocument('example.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await doc.synchronize()
await codeLens.checkProvider()
let p = helper.doAction('codeLensAction')
await helper.waitPrompt()
await nvim.input('<cr>')
await p
expect(fn).toBeCalledWith(1, 2, 3)
})
it('should refresh for failed codeLens request', async () => {
let called = 0
let fn = jest.fn()
disposables.push(commands.registerCommand('__save', (...args) => {
fn(...args)
}))
disposables.push(commands.registerCommand('__foo', (...args) => {
fn(...args)
}))
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], {
provideCodeLenses: () => {
called++
if (called == 1) {
return null
}
return [{
range: Range.create(0, 0, 0, 1),
command: Command.create('foo', '__foo')
}]
}
}))
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1),
command: Command.create('save', '__save')
}]
}
}))
let doc = await helper.createDocument('example.js')
await helper.wait(50)
await nvim.call('setline', [1, ['a', 'b', 'c']])
await codeLens.checkProvider()
let markers = await helper.getMarkers(doc.buffer.id, srcId)
expect(markers.length).toBeGreaterThan(0)
let codeLensBuffer = codeLens.buffers.getItem(doc.buffer.id)
await codeLensBuffer.forceFetch()
let curr = codeLensBuffer.currentCodeLens
expect(curr.length).toBeGreaterThan(1)
})
it('should use custom separator & position', async () => {
helper.updateConfiguration('codeLens.separator', '|')
helper.updateConfiguration('codeLens.position', 'eol')
let doc = await helper.createDocument('example.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await doc.synchronize()
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 1, 0),
command: Command.create('save', '__save')
}, {
range: Range.create(0, 0, 1, 0),
command: Command.create('save', '__save')
}]
}
}))
await codeLens.checkProvider()
let res = await doc.buffer.getExtMarks(srcId, 0, -1, { details: true })
expect(res.length).toBe(1)
})
it('should get commands from codeLenses', async () => {
expect(getCommands(1, undefined)).toEqual([])
let codeLenses = [CodeLens.create(Range.create(0, 0, 0, 0))]
expect(getCommands(0, codeLenses)).toEqual([])
codeLenses = [CodeLens.create(Range.create(0, 0, 1, 0)), CodeLens.create(Range.create(2, 0, 3, 0))]
codeLenses[0].command = Command.create('save', '__save')
expect(getCommands(0, codeLenses).length).toEqual(1)
})
})

295
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/colors.test.ts

@ -0,0 +1,295 @@ @@ -0,0 +1,295 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, Color, ColorInformation, ColorPresentation, Disposable, Position, Range } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import commands from '../../commands'
import { toHexString } from '../../util/color'
import Colors from '../../handler/colors/index'
import languages from '../../languages'
import { ProviderResult } from '../../provider'
import { disposeAll } from '../../util'
import path from 'path'
import helper from '../helper'
import workspace from '../../workspace'
let nvim: Neovim
let state = 'normal'
let colors: Colors
let disposables: Disposable[] = []
let colorPresentations: ColorPresentation[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
await nvim.command(`source ${path.join(process.cwd(), 'autoload/coc/color.vim')}`)
colors = helper.plugin.getHandler().colors
languages.registerDocumentColorProvider([{ language: '*' }], {
provideColorPresentations: (
_color: Color,
_context: { document: TextDocument; range: Range },
_token: CancellationToken
): ColorPresentation[] => colorPresentations,
provideDocumentColors: (
document: TextDocument,
_token: CancellationToken
): ProviderResult<ColorInformation[]> => {
if (state == 'empty') return []
if (state == 'error') return Promise.reject(new Error('no color'))
let matches = Array.from((document.getText() as any).matchAll(/#\w{6}/g)) as any
return matches.map(o => {
let start = document.positionAt(o.index)
let end = document.positionAt(o.index + o[0].length)
return {
range: Range.create(start, end),
color: getColor(255, 255, 255)
}
})
}
})
})
beforeEach(() => {
helper.updateConfiguration('colors.filetypes', ['*'])
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
state = 'normal'
colorPresentations = []
disposeAll(disposables)
await helper.reset()
})
function getColor(r: number, g: number, b: number): Color {
return { red: r / 255, green: g / 255, blue: b / 255, alpha: 1 }
}
describe('Colors', () => {
describe('utils', () => {
it('should get hex string', () => {
let color = getColor(255, 255, 255)
let hex = toHexString(color)
expect(hex).toBe('ffffff')
})
})
describe('configuration', () => {
it('should toggle enable state on configuration change', async () => {
let doc = await helper.createDocument()
helper.updateConfiguration('colors.filetypes', [])
let enabled = colors.isEnabled(doc.bufnr)
helper.updateConfiguration('colors.filetypes', ['*'])
expect(enabled).toBe(false)
})
})
describe('commands', () => {
it('should register editor.action.pickColor command', async () => {
await helper.mockFunction('coc#color#pick_color', [0, 0, 0])
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
await commands.executeCommand('editor.action.pickColor')
let line = await nvim.getLine()
expect(line).toBe('#000000')
})
it('should register editor.action.colorPresentation command', async () => {
colorPresentations = [ColorPresentation.create('red'), ColorPresentation.create('#ff0000')]
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
await doc.synchronize()
await colors.doHighlight(doc.bufnr)
let p = commands.executeCommand('editor.action.colorPresentation')
await helper.waitPrompt()
await nvim.input('1')
await p
let line = await nvim.getLine()
expect(line).toBe('red')
})
})
describe('doHighlight', () => {
it('should merge colors of providers', async () => {
disposables.push(languages.registerDocumentColorProvider([{ language: '*' }], {
provideColorPresentations: (): ColorPresentation[] => colorPresentations,
provideDocumentColors: (
): ProviderResult<ColorInformation[]> => {
return [{
range: Range.create(0, 0, 1, 0),
color: getColor(0, 0, 0)
}, {
range: Range.create(0, 0, 0, 7),
color: getColor(1, 1, 1)
}]
}
}))
disposables.push(languages.registerDocumentColorProvider([{ language: '*' }], {
provideColorPresentations: (): ColorPresentation[] => colorPresentations,
provideDocumentColors: (
): ProviderResult<ColorInformation[]> => {
return null
}
}))
let doc = await workspace.document
await nvim.setLine('#ffffff #ff0000')
await doc.synchronize()
let colors = await languages.provideDocumentColors(doc.textDocument, CancellationToken.None)
expect(colors.length).toBe(3)
let color = ColorInformation.create(Range.create(0, 0, 1, 0), getColor(0, 0, 0))
let presentation = await languages.provideColorPresentations(color, doc.textDocument, CancellationToken.None)
expect(presentation).toBeNull()
})
it('should clearHighlight on empty result', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
state = 'empty'
await colors.doHighlight(doc.bufnr)
let res = colors.hasColor(doc.bufnr)
expect(res).toBe(false)
})
it('should not throw on error result', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
state = 'error'
let err
try {
await colors.doHighlight(doc.bufnr)
} catch (e) {
err = e
}
expect(err).toBeUndefined()
})
it('should highlight after document changed', async () => {
let doc = await helper.createDocument()
await colors.doHighlight(doc.bufnr)
expect(colors.hasColor(doc.bufnr)).toBe(false)
expect(colors.hasColorAtPosition(doc.bufnr, Position.create(0, 1))).toBe(false)
await nvim.setLine('#ffffff #ff0000')
await doc.synchronize()
await helper.waitValue(() => {
return colors.hasColorAtPosition(doc.bufnr, Position.create(0, 1))
}, true)
expect(colors.hasColor(doc.bufnr)).toBe(true)
})
it('should clearHighlight on clearHighlight', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff #ff0000')
await doc.synchronize()
await colors.doHighlight(doc.bufnr)
expect(colors.hasColor(doc.bufnr)).toBe(true)
colors.clearHighlight(doc.bufnr)
expect(colors.hasColor(doc.bufnr)).toBe(false)
})
it('should highlight colors', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
await colors.doHighlight(doc.bufnr)
let exists = await nvim.call('hlexists', 'BGffffff')
expect(exists).toBe(1)
})
})
describe('hasColor()', () => {
it('should return false when bufnr does not exist', async () => {
let res = colors.hasColor(99)
colors.clearHighlight(99)
expect(res).toBe(false)
})
})
describe('getColorInformation()', () => {
it('should return null when highlighter does not exist', async () => {
let res = await colors.getColorInformation(99)
expect(res).toBe(null)
})
it('should return null when color not found', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff foo ')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
await nvim.call('cursor', [1, 12])
let res = await colors.getColorInformation(doc.bufnr)
expect(res).toBe(null)
})
})
describe('hasColorAtPosition()', () => {
it('should return false when bufnr does not exist', async () => {
let res = colors.hasColorAtPosition(99, Position.create(0, 0))
expect(res).toBe(false)
})
})
describe('pickPresentation()', () => {
it('should show warning when color does not exist', async () => {
await helper.createDocument()
await colors.pickPresentation()
let msg = await helper.getCmdline()
expect(msg).toMatch('Color not found')
})
it('should not throw when presentations do not exist', async () => {
colorPresentations = []
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
doc.forceSync()
await colors.doHighlight(99)
await colors.doHighlight(doc.bufnr)
await helper.doAction('colorPresentation')
})
it('should pick presentations', async () => {
colorPresentations = [ColorPresentation.create('red'), ColorPresentation.create('#ff0000')]
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
let p = helper.doAction('colorPresentation')
await helper.waitPrompt()
await nvim.input('1')
await p
let line = await nvim.getLine()
expect(line).toBe('red')
})
})
describe('pickColor()', () => {
it('should show warning when color does not exist', async () => {
await helper.createDocument()
await colors.pickColor()
let msg = await helper.getCmdline()
expect(msg).toMatch('not found')
})
it('should pickColor', async () => {
await helper.mockFunction('coc#color#pick_color', [0, 0, 0])
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
await helper.doAction('pickColor')
let line = await nvim.getLine()
expect(line).toBe('#000000')
})
it('should not throw when pick color return 0', async () => {
await helper.mockFunction('coc#color#pick_color', 0)
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
await helper.doAction('pickColor')
let line = await nvim.getLine()
expect(line).toBe('#ffffff')
})
})
})

82
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/commands.test.ts

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import CommandsHandler from '../../handler/commands'
import commandManager from '../../commands'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let commands: CommandsHandler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
commands = (helper.plugin as any).handler.commands
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('Commands', () => {
describe('addVimCommand', () => {
it('should register global vim commands', async () => {
await commandManager.executeCommand('vim.config')
await helper.wait(50)
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toMatch('coc-settings.json')
let list = commands.getCommandList()
expect(list.includes('vim.config')).toBe(true)
})
it('should add vim command with title', async () => {
commands.addVimCommand({ id: 'list', cmd: 'CocList', title: 'list of coc.nvim' })
let res = commandManager.titles.get('vim.list')
expect(res).toBe('list of coc.nvim')
commandManager.unregister('vim.list')
})
})
describe('getCommands', () => {
it('should get command items', async () => {
let res = commands.getCommands()
let idx = res.findIndex(o => o.id == 'workspace.showOutput')
expect(idx != -1).toBe(true)
})
})
describe('repeat', () => {
it('should repeat command', async () => {
// let buf = await nvim.buffer
await nvim.call('setline', [1, ['a', 'b', 'c']])
await nvim.call('cursor', [1, 1])
commands.addVimCommand({ id: 'remove', cmd: 'normal! dd' })
await commands.runCommand('vim.remove')
await helper.wait(50)
let res = await nvim.call('getline', [1, '$'])
expect(res).toEqual(['b', 'c'])
await commands.repeat()
await helper.wait(50)
res = await nvim.call('getline', [1, '$'])
expect(res).toEqual(['c'])
})
})
describe('runCommand', () => {
it('should open command list without id', async () => {
await commands.runCommand()
await helper.wait(100)
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toBe('list:///commands')
})
})
})

113
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/fold.test.ts

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, CancellationTokenSource, Disposable, FoldingRange } from 'vscode-languageserver-protocol'
import FoldHandler from '../../handler/fold'
import languages from '../../languages'
import { disposeAll } from '../../util'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let folds: FoldHandler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
folds = helper.plugin.getHandler().fold
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('Folds', () => {
it('should return empty array when provider does not exist', async () => {
let doc = await workspace.document
let token = (new CancellationTokenSource()).token
expect(await languages.provideFoldingRanges(doc.textDocument, {}, token)).toEqual([])
})
it('should return false when no fold ranges found', async () => {
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges(_doc) {
return []
}
}))
let res = await folds.fold()
expect(res).toBe(false)
})
it('should fold all fold ranges', async () => {
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges(_doc) {
return [FoldingRange.create(1, 3), FoldingRange.create(4, 6, 0, 0, 'comment')]
}
}))
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']])
let res = await folds.fold()
expect(res).toBe(true)
let closed = await nvim.call('foldclosed', [2])
expect(closed).toBe(2)
closed = await nvim.call('foldclosed', [5])
expect(closed).toBe(5)
})
it('should merge folds from all providers', async () => {
let doc = await workspace.document
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges() {
return [FoldingRange.create(2, 3), FoldingRange.create(4, 6)]
}
}))
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges() {
return [FoldingRange.create(1, 2), FoldingRange.create(5, 6), FoldingRange.create(7, 8)]
}
}))
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']])
await doc.synchronize()
let foldingRanges = await languages.provideFoldingRanges(doc.textDocument, {}, CancellationToken.None)
expect(foldingRanges.length).toBe(4)
})
it('should ignore range start at the same line', async () => {
let doc = await workspace.document
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges() {
return [FoldingRange.create(2, 3), FoldingRange.create(4, 6)]
}
}))
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges() {
return [FoldingRange.create(4, 5)]
}
}))
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']])
await doc.synchronize()
let foldingRanges = await languages.provideFoldingRanges(doc.textDocument, {}, CancellationToken.None)
expect(foldingRanges.length).toBe(2)
})
it('should fold comment ranges', async () => {
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], {
provideFoldingRanges(_doc) {
return [FoldingRange.create(1, 3), FoldingRange.create(4, 6, 0, 0, 'comment')]
}
}))
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']])
let res = await folds.fold('comment')
expect(res).toBe(true)
let closed = await nvim.call('foldclosed', [2])
expect(closed).toBe(-1)
closed = await nvim.call('foldclosed', [5])
expect(closed).toBe(5)
})
})

285
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/format.test.ts

@ -0,0 +1,285 @@ @@ -0,0 +1,285 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, CancellationTokenSource, Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import Format from '../../handler/format'
import languages from '../../languages'
import { disposeAll } from '../../util'
import window from '../../window'
import workspace from '../../workspace'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let format: Format
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
format = helper.plugin.getHandler().format
})
beforeEach(() => {
helper.updateConfiguration('coc.preferences.formatOnType', true)
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
})
afterAll(async () => {
await helper.shutdown()
})
describe('format handler', () => {
describe('documentFormat', () => {
it('should return null when format provider not exists', async () => {
let doc = await workspace.document
let res = await languages.provideDocumentFormattingEdits(doc.textDocument, { insertSpaces: false, tabSize: 2 }, CancellationToken.None)
expect(res).toBeNull()
})
it('should throw when provider not found', async () => {
let doc = await workspace.document
let err
try {
await format.documentFormat(doc)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should return false when get empty edits ', async () => {
disposables.push(languages.registerDocumentFormatProvider(['*'], {
provideDocumentFormattingEdits: () => {
return []
}
}))
let doc = await helper.createDocument()
let res = await format.documentFormat(doc)
expect(res).toBe(false)
})
it('should use provider that have higher score', async () => {
disposables.push(languages.registerDocumentFormatProvider([{ language: 'vim' }], {
provideDocumentFormattingEdits: () => {
return [TextEdit.insert(Position.create(0, 0), ' ')]
}
}))
disposables.push(languages.registerDocumentFormatProvider(['*'], {
provideDocumentFormattingEdits: () => {
return []
}
}))
let doc = await helper.createDocument('t.vim')
let res = await languages.provideDocumentFormattingEdits(doc.textDocument, { tabSize: 2, insertSpaces: false }, CancellationToken.None)
expect(res.length).toBe(1)
})
})
describe('formatOnSave', () => {
it('should not throw when provider not found', async () => {
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['javascript'])
let filepath = await createTmpFile('')
await helper.edit(filepath)
await nvim.command('setf javascript')
await nvim.setLine('foo')
await nvim.command('silent w')
})
it('should invoke format on save', async () => {
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['text'])
disposables.push(languages.registerDocumentFormatProvider(['text'], {
provideDocumentFormattingEdits: document => {
let lines = document.getText().replace(/\n$/, '').split(/\n/)
let edits: TextEdit[] = []
for (let i = 0; i < lines.length; i++) {
let text = lines[i]
if (!text.startsWith(' ')) {
edits.push(TextEdit.insert(Position.create(i, 0), ' '))
}
}
return edits
}
}))
let filepath = await createTmpFile('a\nb\nc\n')
let buf = await helper.edit(filepath)
await nvim.command('setf text')
await nvim.command('w')
let lines = await buf.lines
expect(lines).toEqual([' a', ' b', ' c'])
})
it('should cancel when timeout', async () => {
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['*'])
let timer
disposables.push(languages.registerDocumentFormatProvider(['*'], {
provideDocumentFormattingEdits: () => {
return new Promise(resolve => {
timer = setTimeout(() => {
resolve(undefined)
}, 2000)
})
}
}))
let filepath = await createTmpFile('a\nb\nc\n')
await helper.edit(filepath)
let n = Date.now()
await nvim.command('w')
expect(Date.now() - n).toBeLessThan(1000)
clearTimeout(timer)
})
})
describe('rangeFormat', () => {
it('should return null when provider does not exist', async () => {
let doc = (await workspace.document).textDocument
let range = Range.create(0, 0, 1, 0)
let options = await workspace.getFormatOptions()
let token = (new CancellationTokenSource()).token
expect(await languages.provideDocumentRangeFormattingEdits(doc, range, options, token)).toBe(null)
expect(languages.hasProvider('onTypeEdit', doc)).toBe(false)
let edits = await languages.provideDocumentFormattingEdits(doc, options, token)
expect(edits).toBe(null)
})
it('should invoke range format', async () => {
disposables.push(languages.registerDocumentRangeFormatProvider(['text'], {
provideDocumentRangeFormattingEdits: (_document, range) => {
let lines: number[] = []
for (let i = range.start.line; i <= range.end.line; i++) {
lines.push(i)
}
return lines.map(i => {
return TextEdit.insert(Position.create(i, 0), ' ')
})
}
}, 1))
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['a', 'b', 'c']])
await nvim.command('setf text')
await nvim.command('normal! ggvG')
await nvim.input('<esc>')
expect(languages.hasFormatProvider(doc.textDocument)).toBe(true)
expect(languages.hasProvider('format', doc.textDocument)).toBe(true)
await helper.doAction('formatSelected', 'v')
let buf = nvim.createBuffer(doc.bufnr)
let lines = await buf.lines
expect(lines).toEqual([' a', ' b', ' c'])
let options = await workspace.getFormatOptions(doc.uri)
let token = (new CancellationTokenSource()).token
let edits = await languages.provideDocumentFormattingEdits(doc.textDocument, options, token)
expect(edits.length).toBeGreaterThan(0)
})
it('should format range by formatexpr option', async () => {
let range: Range
disposables.push(languages.registerDocumentRangeFormatProvider(['text'], {
provideDocumentRangeFormattingEdits: (_document, r) => {
range = r
return []
}
}))
await helper.createDocument()
await nvim.call('setline', [1, ['a', 'b', 'c']])
await nvim.command('setf text')
await nvim.command(`setl formatexpr=CocAction('formatSelected')`)
await nvim.command('normal! ggvGgq')
expect(range).toEqual({
start: { line: 0, character: 0 }, end: { line: 3, character: 0 }
})
})
})
describe('formatOnType', () => {
it('should invoke format', async () => {
disposables.push(languages.registerDocumentFormatProvider(['text'], {
provideDocumentFormattingEdits: () => {
return [TextEdit.insert(Position.create(0, 0), ' ')]
}
}))
await helper.createDocument()
await nvim.setLine('foo')
await nvim.command('setf text')
await helper.doAction('format')
let line = await nvim.line
expect(line).toEqual(' foo')
})
it('should does format on type', async () => {
let doc = await workspace.document
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], {
provideOnTypeFormattingEdits: () => {
return [TextEdit.insert(Position.create(0, 0), ' ')]
}
}, ['|']))
let res = languages.canFormatOnType('a', doc.textDocument)
expect(res).toBe(false)
await helper.edit()
await nvim.command('setf text')
await nvim.input('i|')
await helper.waitFor('getline', ['.'], ' |')
let cursor = await window.getCursorPosition()
expect(cursor).toEqual({ line: 0, character: 3 })
})
it('should return null when provider not found', async () => {
let doc = await workspace.document
let res = await languages.provideDocumentOnTypeEdits('|', doc.textDocument, Position.create(0, 0), CancellationToken.None)
expect(res).toBeNull()
})
it('should adjust cursor after format on type', async () => {
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], {
provideOnTypeFormattingEdits: () => {
return [
TextEdit.insert(Position.create(0, 0), ' '),
TextEdit.insert(Position.create(0, 2), 'end')
]
}
}, ['|']))
disposables.push(languages.registerOnTypeFormattingEditProvider([{ language: '*' }], {
provideOnTypeFormattingEdits: () => {
return []
}
}))
await helper.edit()
await nvim.command('setf text')
await nvim.setLine('"')
await nvim.input('i|')
await helper.waitFor('getline', ['.'], ' |"end')
let cursor = await window.getCursorPosition()
expect(cursor).toEqual({ line: 0, character: 3 })
})
})
describe('bracketEnterImprove', () => {
afterEach(() => {
nvim.command('iunmap <CR>', true)
})
it('should format vim file on enter', async () => {
let buf = await helper.edit('foo.vim')
await nvim.command(`inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\\<C-g>u\\<CR>\\<c-r>=coc#on_enter()\\<CR>"`)
await nvim.setLine('let foo={}')
await nvim.command(`normal! gg$`)
await nvim.input('i')
await nvim.eval(`feedkeys("\\<CR>", 'im')`)
await helper.waitFor('getline', [1], 'let foo={')
let lines = await buf.lines
expect(lines).toEqual(['let foo={', ' \\ ', ' \\ }'])
})
it('should add new line between bracket', async () => {
let buf = await helper.edit()
await nvim.command(`inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\\<C-g>u\\<CR>\\<c-r>=coc#on_enter()\\<CR>"`)
await nvim.setLine(' {}')
await nvim.command(`normal! gg$`)
await nvim.input('i')
await nvim.eval(`feedkeys("\\<CR>", 'im')`)
await helper.waitFor('getline', [2], ' ')
let lines = await buf.lines
expect(lines).toEqual([' {', ' ', ' }'])
})
})
})

166
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/highlights.test.ts

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, DocumentHighlightKind, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import Highlights from '../../handler/highlights'
import languages from '../../languages'
import workspace from '../../workspace'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let highlights: Highlights
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
highlights = helper.plugin.getHandler().documentHighlighter
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
function registerProvider(): void {
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: async document => {
let word = await nvim.eval('expand("<cword>")')
// let word = document.get
let matches = Array.from((document.getText() as any).matchAll(/\w+/g)) as any[]
let filtered = matches.filter(o => o[0] == word)
return filtered.map((o, i) => {
let start = document.positionAt(o.index)
let end = document.positionAt(o.index + o[0].length)
return {
range: Range.create(start, end),
kind: i % 2 == 0 ? DocumentHighlightKind.Read : DocumentHighlightKind.Write
}
})
}
}))
}
describe('document highlights', () => {
function registerTimerProvider(fn: Function, timeout: number): void {
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: (_document, _position, token) => {
return new Promise(resolve => {
token.onCancellationRequested(() => {
clearTimeout(timer)
fn()
resolve([])
})
let timer = setTimeout(() => {
resolve([{ range: Range.create(0, 0, 0, 3) }])
}, timeout)
})
}
}))
}
it('should not throw when provide throws', async () => {
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: () => {
return null
}
}))
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: () => {
throw new Error('fake error')
}
}))
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: () => {
return [{
range: Range.create(0, 0, 0, 3),
kind: DocumentHighlightKind.Read
}]
}
}))
let doc = await workspace.document
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
let res = await highlights.getHighlights(doc, Position.create(0, 0))
expect(res).toBeDefined()
})
it('should return null when highlights provide not exist', async () => {
let doc = await workspace.document
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo')])
let res = await highlights.getHighlights(doc, Position.create(0, 0))
expect(res).toBeNull()
})
it('should cancel request on CursorMoved', async () => {
let fn = jest.fn()
registerTimerProvider(fn, 3000)
await helper.edit()
await nvim.setLine('foo')
let p = highlights.highlight()
await helper.wait(50)
await nvim.call('cursor', [1, 2])
await p
expect(fn).toBeCalled()
})
it('should cancel on timeout', async () => {
helper.updateConfiguration('documentHighlight.timeout', 10)
let fn = jest.fn()
registerTimerProvider(fn, 3000)
await helper.edit()
await nvim.setLine('foo')
await highlights.highlight()
expect(fn).toBeCalled()
})
it('should add highlights to symbols', async () => {
registerProvider()
await helper.createDocument()
await nvim.setLine('foo bar foo')
await helper.doAction('highlight')
let winid = await nvim.call('win_getid') as number
expect(highlights.hasHighlights(winid)).toBe(true)
})
it('should return highlight ranges', async () => {
registerProvider()
await helper.createDocument()
await nvim.setLine('foo bar foo')
let res = await helper.doAction('symbolRanges')
expect(res.length).toBe(2)
})
it('should return null when cursor not in word range', async () => {
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], {
provideDocumentHighlights: () => {
return [{ range: Range.create(0, 0, 0, 3) }]
}
}))
let doc = await helper.createDocument()
await nvim.setLine(' oo')
await nvim.call('cursor', [1, 2])
let res = await highlights.getHighlights(doc, Position.create(0, 0))
expect(res).toBeNull()
})
it('should not throw when document is command line', async () => {
await nvim.call('feedkeys', ['q:', 'in'])
let doc = await workspace.document
expect(doc.isCommandLine).toBe(true)
await highlights.highlight()
await nvim.input('<C-c>')
})
it('should not throw when provider not found', async () => {
disposeAll(disposables)
await helper.createDocument()
await nvim.setLine(' oo')
await nvim.call('cursor', [1, 2])
await highlights.highlight()
})
})

238
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/hover.test.ts

@ -0,0 +1,238 @@ @@ -0,0 +1,238 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, MarkedString, Hover, Range, TextEdit, Position, CancellationToken, MarkupKind } from 'vscode-languageserver-protocol'
import HoverHandler, { addDefinitions, addDocument, isDocumentation, readLines } from '../../handler/hover'
import { URI } from 'vscode-uri'
import languages from '../../languages'
import { disposeAll } from '../../util'
import helper, { createTmpFile } from '../helper'
import workspace from '../../workspace'
import { Documentation } from '../../types'
let nvim: Neovim
let hover: HoverHandler
let disposables: Disposable[] = []
let hoverResult: Hover
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
hover = helper.plugin.getHandler().hover
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return hoverResult
}
}))
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
async function getDocumentText(): Promise<string> {
let lines = await nvim.call('getbufline', ['coc://document', 1, '$']) as string[]
return lines.join('\n')
}
describe('Hover', () => {
describe('utils', () => {
it('should addDocument', async () => {
let docs: Documentation[] = []
addDocument(docs, '', '')
expect(docs.length).toBe(0)
})
it('should check documentation', async () => {
expect(isDocumentation({})).toBe(false)
expect(isDocumentation({ filetype: '', content: '' })).toBe(true)
})
it('should readLines', async () => {
let res = await readLines('file:///not_exists', 0, 1)
expect(res).toEqual([])
})
it('should addDefinitions', async () => {
let hovers = []
let range = Range.create(0, 0, 0, 0)
await addDefinitions(hovers, [undefined, {} as any, { targetUri: 'file:///not_exists', targetRange: range, targetSelectionRange: range }], '')
expect(hovers.length).toBe(0)
let file = await createTmpFile(' foo\n bar\n', disposables)
range = Range.create(0, 0, 300, 0)
await addDefinitions(hovers, [{ targetUri: URI.file(file).toString(), targetRange: range, targetSelectionRange: range }], '')
expect(hovers.length).toBe(1)
})
})
describe('onHover', () => {
it('should return false when hover not found', async () => {
hoverResult = null
let res = await hover.onHover('preview')
expect(res).toBe(false)
})
it('should show MarkupContent hover', async () => {
helper.updateConfiguration('hover.target', 'preview')
hoverResult = { contents: { kind: 'plaintext', value: 'my hover' } }
await hover.onHover()
let res = await getDocumentText()
expect(res).toMatch('my hover')
})
it('should merge hover results', async () => {
hoverResult = { contents: { kind: 'plaintext', value: 'my hover' } }
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return null
}
}))
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return { contents: { kind: 'plaintext', value: 'my hover' } }
}
}))
let doc = await workspace.document
let hovers = await languages.getHover(doc.textDocument, Position.create(0, 0), CancellationToken.None)
expect(hovers.length).toBe(1)
})
it('should show MarkedString hover', async () => {
hoverResult = { contents: 'string hover' }
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return { contents: { language: 'typescript', value: 'language hover' } }
}
}))
await hover.onHover('preview')
let res = await getDocumentText()
expect(res).toMatch('string hover')
expect(res).toMatch('language hover')
})
it('should show MarkedString hover array', async () => {
hoverResult = { contents: ['foo', { language: 'typescript', value: 'bar' }] }
await hover.onHover('preview')
let res = await getDocumentText()
expect(res).toMatch('foo')
expect(res).toMatch('bar')
})
it('should highlight hover range', async () => {
await nvim.setLine('var')
await nvim.command('normal! 0')
hoverResult = { contents: ['foo'], range: Range.create(0, 0, 0, 3) }
await hover.onHover('preview')
let res = await nvim.call('getmatches') as any[]
expect(res.length).toBe(1)
expect(res[0].group).toBe('CocHoverRange')
await helper.wait(600)
res = await nvim.call('getmatches')
expect(res.length).toBe(0)
})
})
describe('previewHover', () => {
it('should echo hover message', async () => {
hoverResult = { contents: ['foo'] }
let res = await hover.onHover('echo')
expect(res).toBe(true)
let msg = await helper.getCmdline()
expect(msg).toMatch('foo')
})
it('should show hover in float window', async () => {
hoverResult = { contents: { kind: 'markdown', value: '```typescript\nconst foo:number\n```' } }
await hover.onHover('float')
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await nvim.eval(`getbufline(winbufnr(${win.id}),1,'$')`)
expect(lines).toEqual(['const foo:number'])
})
})
describe('getHover', () => {
it('should get hover from MarkedString array', async () => {
hoverResult = { contents: ['foo', { language: 'typescript', value: 'bar' }] }
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return { contents: { language: 'typescript', value: 'MarkupContent hover' } }
}
}))
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return { contents: MarkedString.fromPlainText('MarkedString hover') }
}
}))
let res = await hover.getHover()
expect(res.includes('foo')).toBe(true)
expect(res.includes('bar')).toBe(true)
expect(res.includes('MarkupContent hover')).toBe(true)
expect(res.includes('MarkedString hover')).toBe(true)
})
it('should filter empty hover message', async () => {
hoverResult = { contents: [''] }
disposables.push(languages.registerHoverProvider([{ language: '*' }], {
provideHover: (_doc, _pos, _token) => {
return { contents: { kind: MarkupKind.PlainText, value: 'value' } }
}
}))
let res = await hover.getHover()
expect(res).toEqual(['value'])
})
})
describe('definitionHover', () => {
it('should load definition from buffer', async () => {
hoverResult = { contents: 'string hover' }
let doc = await helper.createDocument()
await nvim.call('cursor', [1, 1])
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')])
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], {
provideDefinition() {
return [{
targetUri: doc.uri,
targetRange: Range.create(0, 0, 1, 3),
targetSelectionRange: Range.create(0, 0, 0, 3),
}]
}
}))
await hover.definitionHover('preview')
let res = await getDocumentText()
expect(res).toBe('string hover\n\nfoo\nbar')
})
it('should load definition link from file', async () => {
let fsPath = await createTmpFile('foo\nbar\n')
hoverResult = { contents: 'string hover', range: Range.create(0, 0, 0, 3) }
let doc = await helper.createDocument()
await nvim.call('cursor', [1, 1])
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')])
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], {
provideDefinition() {
return [{
targetUri: URI.file(fsPath).toString(),
targetRange: Range.create(0, 0, 1, 3),
targetSelectionRange: Range.create(0, 0, 0, 3),
}]
}
}))
await hover.definitionHover('preview')
let res = await getDocumentText()
expect(res).toBe('string hover\n\nfoo\nbar')
})
it('should return false when hover not found', async () => {
hoverResult = undefined
let res = await hover.definitionHover('float')
expect(res).toBe(false)
})
})
})

93
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/index.test.ts

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import Handler from '../../handler/index'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let handler: Handler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
handler = (helper.plugin as any).handler
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('Handler', () => {
describe('hasProvider', () => {
it('should check provider for document', async () => {
let res = await handler.hasProvider('definition')
expect(res).toBe(false)
})
})
describe('checkProvier', () => {
it('should throw error when provider not found', async () => {
let doc = await helper.createDocument()
let err
try {
handler.checkProvier('definition', doc.textDocument)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
})
describe('withRequestToken', () => {
it('should cancel previous request when called again', async () => {
let cancelled = false
let p = handler.withRequestToken('test', token => {
return new Promise(s => {
token.onCancellationRequested(() => {
cancelled = true
clearTimeout(timer)
s(undefined)
})
let timer = setTimeout(() => {
s(undefined)
}, 3000)
})
}, false)
setTimeout(async () => {
await handler.withRequestToken('test', () => {
return Promise.resolve(undefined)
}, false)
}, 50)
await p
expect(cancelled).toBe(true)
})
it('should cancel request on insert start', async () => {
let cancelled = false
let p = handler.withRequestToken('test', token => {
return new Promise(s => {
token.onCancellationRequested(() => {
cancelled = true
clearTimeout(timer)
s(undefined)
})
let timer = setTimeout(() => {
s(undefined)
}, 3000)
})
}, false)
await nvim.input('i')
await p
expect(cancelled).toBe(true)
})
})
})

410
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/inlayHint.test.ts

@ -0,0 +1,410 @@ @@ -0,0 +1,410 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationTokenSource, Disposable, InlayHint, InlayHintKind, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import commands from '../../commands'
import InlayHintHandler from '../../handler/inlayHint/index'
import languages from '../../languages'
import { InlayHintWithProvider, isInlayHint, isValidInlayHint, sameHint } from '../../provider/inlayHintManager'
import { disposeAll } from '../../util'
import { CancellationError } from '../../util/errors'
import workspace from '../../workspace'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
let handler: InlayHintHandler
let disposables: Disposable[] = []
let ns: number
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
handler = helper.plugin.getHandler().inlayHintHandler
ns = await nvim.createNamespace('coc-inlayHint')
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
async function registerProvider(content: string): Promise<Disposable> {
let doc = await workspace.document
let disposable = languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: (document, range) => {
let content = document.getText(range)
let lines = content.split(/\r?\n/)
let hints: InlayHint[] = []
for (let i = 0; i < lines.length; i++) {
let line = lines[i]
if (!line.length) continue
let parts = line.split(/\s+/)
let kind: InlayHintKind = i == 0 ? InlayHintKind.Type : InlayHintKind.Parameter
hints.push(...parts.map(s => InlayHint.create(Position.create(range.start.line + i, line.length), s, kind)))
}
return hints
}
})
await doc.buffer.setLines(content.split(/\n/), { start: 0, end: -1 })
await doc.synchronize()
return disposable
}
async function waitRefresh(bufnr: number) {
let buf = handler.getItem(bufnr)
return new Promise<void>((resolve, reject) => {
let timer = setTimeout(() => {
reject(new Error('not refresh after 1s'))
}, 1000)
buf.onDidRefresh(() => {
clearTimeout(timer)
resolve()
})
})
}
describe('InlayHint', () => {
describe('utils', () => {
it('should check same hint', () => {
let hint = InlayHint.create(Position.create(0, 0), 'foo')
expect(sameHint(hint, InlayHint.create(Position.create(0, 0), 'bar'))).toBe(false)
expect(sameHint(hint, InlayHint.create(Position.create(0, 0), [{ value: 'foo' }]))).toBe(true)
})
it('should check valid hint', () => {
let hint = InlayHint.create(Position.create(0, 0), 'foo')
expect(isValidInlayHint(hint, Range.create(0, 0, 1, 0))).toBe(true)
expect(isValidInlayHint(InlayHint.create(Position.create(0, 0), ''), Range.create(0, 0, 1, 0))).toBe(false)
expect(isValidInlayHint(InlayHint.create(Position.create(3, 0), 'foo'), Range.create(0, 0, 1, 0))).toBe(false)
expect(isValidInlayHint({ label: 'f' } as any, Range.create(0, 0, 1, 0))).toBe(false)
})
it('should check inlayHint instance', async () => {
expect(isInlayHint(null)).toBe(false)
let position = Position.create(0, 0)
expect(isInlayHint({ position, label: null })).toBe(false)
expect(isInlayHint({ position, label: [{ value: '' }] })).toBe(true)
})
})
describe('provideInlayHints', () => {
it('should throw when failed', async () => {
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return Promise.reject(new Error('Test failure'))
}
}))
let doc = await workspace.document
let fn = async () => {
let tokenSource = new CancellationTokenSource()
await languages.provideInlayHints(doc.textDocument, Range.create(0, 0, 1, 0), tokenSource.token)
}
await expect(fn()).rejects.toThrow(Error)
})
it('should merge provide results', async () => {
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return [InlayHint.create(Position.create(0, 0), 'foo')]
}
}))
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return [
InlayHint.create(Position.create(0, 0), 'foo'),
InlayHint.create(Position.create(1, 0), 'bar'),
InlayHint.create(Position.create(5, 0), 'bad')]
}
}))
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return null
}
}))
let doc = await workspace.document
let tokenSource = new CancellationTokenSource()
let res = await languages.provideInlayHints(doc.textDocument, Range.create(0, 0, 3, 0), tokenSource.token)
expect(res.length).toBe(2)
})
it('should resolve inlay hint', async () => {
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return [InlayHint.create(Position.create(0, 0), 'foo')]
},
resolveInlayHint: hint => {
hint.tooltip = 'tooltip'
return hint
}
}))
let doc = await workspace.document
let tokenSource = new CancellationTokenSource()
let res = await languages.provideInlayHints(doc.textDocument, Range.create(0, 0, 1, 0), tokenSource.token)
let resolved = await languages.resolveInlayHint(res[0], tokenSource.token)
expect(resolved.tooltip).toBe('tooltip')
resolved = await languages.resolveInlayHint(resolved, tokenSource.token)
expect(resolved.tooltip).toBe('tooltip')
})
it('should not resolve when cancelled', async () => {
disposables.push(languages.registerInlayHintsProvider([{ language: '*' }], {
provideInlayHints: () => {
return [InlayHint.create(Position.create(0, 0), 'foo')]
},
resolveInlayHint: (hint, token) => {
return new Promise(resolve => {
token.onCancellationRequested(() => {
clearTimeout(timer)
resolve(null)
})
let timer = setTimeout(() => {
resolve(Object.assign({}, hint, { tooltip: 'tooltip' }))
}, 200)
})
}
}))
let doc = await workspace.document
let tokenSource = new CancellationTokenSource()
let res = await languages.provideInlayHints(doc.textDocument, Range.create(0, 0, 1, 0), tokenSource.token)
let p = languages.resolveInlayHint(res[0], tokenSource.token)
tokenSource.cancel()
let resolved = await p
expect(resolved.tooltip).toBeUndefined()
})
})
describe('env & options', () => {
it('should not create when virtualText not supported', async () => {
Object.assign(workspace.env, {
virtualText: false
})
disposables.push(Disposable.create(() => {
Object.assign(workspace.env, {
virtualText: true
})
}))
let doc = await helper.createDocument()
let item = handler.getItem(doc.bufnr)
expect(item).toBeUndefined()
})
it('should not enabled when disabled by configuration', async () => {
helper.updateConfiguration('inlayHint.filetypes', [])
let doc = await workspace.document
let item = handler.getItem(doc.bufnr)
item.clearVirtualText()
expect(item.enabled).toBe(false)
helper.updateConfiguration('inlayHint.filetypes', ['dos'])
doc = await helper.createDocument()
item = handler.getItem(doc.bufnr)
expect(item.enabled).toBe(false)
})
})
describe('configuration', () => {
it('should refresh on insert mode', async () => {
helper.updateConfiguration('inlayHint.refreshOnInsertMode', true)
let doc = await helper.createDocument()
let disposable = await registerProvider('foo\nbar')
disposables.push(disposable)
await nvim.input('i')
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'baz\n')])
await waitRefresh(doc.bufnr)
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
let obj = markers[0][3].virt_text
expect(obj).toEqual([['baz', 'CocInlayHintType']])
expect(markers[1][3].virt_text).toEqual([['foo', 'CocInlayHintParameter']])
})
it('should disable parameter inlayHint', async () => {
helper.updateConfiguration('inlayHint.enableParameter', false)
let doc = await helper.createDocument()
let disposable = await registerProvider('foo\nbar')
disposables.push(disposable)
await waitRefresh(doc.bufnr)
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(1)
})
it('should use custom subseparator', async () => {
helper.updateConfiguration('inlayHint.subSeparator', '|')
let doc = await helper.createDocument()
let disposable = await registerProvider('foo bar')
disposables.push(disposable)
await waitRefresh(doc.bufnr)
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
let virt_text = markers[0][3].virt_text
expect(virt_text[1]).toEqual(['|', 'CocInlayHintType'])
})
})
describe('toggle inlayHint', () => {
it('should not throw when buffer not exists', async () => {
handler.toggle(9)
await commands.executeCommand('document.toggleInlayHint', 9)
})
it('should show message when inlayHint not supported', async () => {
let doc = await workspace.document
handler.toggle(doc.bufnr)
let cmdline = await helper.getCmdline()
expect(cmdline).toMatch(/not\sfound/)
})
it('should show message when not enabled', async () => {
helper.updateConfiguration('inlayHint.filetypes', [])
let doc = await helper.createDocument()
let disposable = await registerProvider('')
disposables.push(disposable)
handler.toggle(doc.bufnr)
let cmdline = await helper.getCmdline()
expect(cmdline).toMatch(/not\senabled/)
})
it('should toggle inlayHints', async () => {
let doc = await helper.createDocument()
let disposable = await registerProvider('foo\nbar')
disposables.push(disposable)
handler.toggle(doc.bufnr)
handler.toggle(doc.bufnr)
await helper.waitValue(async () => {
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
return markers.length
}, 2)
})
})
describe('render()', () => {
it('should refresh on vim mode', async () => {
let doc = await workspace.document
await nvim.setLine('foo bar')
let item = handler.getItem(doc.bufnr)
let r = Range.create(0, 0, 1, 0)
item.setVirtualText(r, [], true)
let hint: InlayHintWithProvider = {
label: 'string',
position: Position.create(0, 0),
providerId: ''
}
let paddingHint: InlayHintWithProvider = {
label: 'string',
position: Position.create(0, 3),
providerId: '',
paddingLeft: true,
paddingRight: true
}
item.setVirtualText(r, [hint, paddingHint], true)
await helper.waitValue(async () => {
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
return markers.length
}, 2)
})
it('should not refresh when languageId not match', async () => {
let doc = await workspace.document
disposables.push(languages.registerInlayHintsProvider([{ language: 'javascript' }], {
provideInlayHints: () => {
let hint = InlayHint.create(Position.create(0, 0), 'foo')
return [hint]
}
}))
await nvim.setLine('foo')
await doc.synchronize()
await helper.wait(30)
let markers = await doc.buffer.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(0)
})
it('should refresh on text change', async () => {
let buf = await nvim.buffer
let disposable = await registerProvider('foo')
disposables.push(disposable)
await waitRefresh(buf.id)
await buf.setLines(['a', 'b', 'c'], { start: 0, end: -1 })
await waitRefresh(buf.id)
let markers = await buf.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(3)
let item = handler.getItem(buf.id)
await item.renderRange()
expect(item.current.length).toBe(3)
})
it('should refresh on insert leave', async () => {
let doc = await helper.createDocument()
let buf = doc.buffer
let disposable = await registerProvider('foo')
disposables.push(disposable)
await nvim.input('i')
await helper.wait(10)
await buf.setLines(['a', 'b', 'c'], { start: 0, end: -1 })
await helper.wait(30)
let markers = await buf.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(0)
await nvim.input('<esc>')
await waitRefresh(doc.bufnr)
markers = await buf.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(3)
})
it('should refresh on provider dispose', async () => {
let buf = await nvim.buffer
let disposable = await registerProvider('foo bar')
await waitRefresh(buf.id)
disposable.dispose()
let markers = await buf.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBe(0)
let item = handler.getItem(buf.id)
expect(item.current.length).toBe(0)
await item.renderRange()
expect(item.current.length).toBe(0)
})
it('should refresh on scroll', async () => {
let arr = new Array(200)
let content = arr.fill('foo').join('\n')
let buf = await nvim.buffer
let disposable = await registerProvider(content)
disposables.push(disposable)
await waitRefresh(buf.id)
let markers = await buf.getExtMarks(ns, 0, -1, { details: true })
let len = markers.length
await nvim.command('normal! G')
await waitRefresh(buf.id)
await nvim.input('<C-y>')
await waitRefresh(buf.id)
markers = await buf.getExtMarks(ns, 0, -1, { details: true })
expect(markers.length).toBeGreaterThan(len)
})
it('should cancel previous render', async () => {
let buf = await nvim.buffer
let disposable = await registerProvider('foo')
disposables.push(disposable)
await waitRefresh(buf.id)
let item = handler.getItem(buf.id)
await item.renderRange()
await item.renderRange()
expect(item.current.length).toBe(1)
})
it('should resend request on CancellationError', async () => {
let called = 0
let disposable = languages.registerInlayHintsProvider([{ language: 'vim' }], {
provideInlayHints: () => {
if (called == 0) {
called++
throw new CancellationError()
}
return []
}
})
disposables.push(disposable)
let filepath = await createTmpFile('a\n\b\nc\n', disposables)
let doc = await helper.createDocument(filepath)
await nvim.command('setfiletype vim')
await waitRefresh(doc.buffer.id)
expect(called).toBe(1)
})
})
})

63
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/inlineValue.test.ts

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken, Disposable, InlineValueText, Range } from 'vscode-languageserver-protocol'
import languages from '../../languages'
import { disposeAll } from '../../util'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
// hover = helper.plugin.getHandler().hover
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('InlineValue', () => {
describe('InlineValueManager', () => {
it('should return false when provider not exists', async () => {
let doc = await workspace.document
let res = languages.hasProvider('inlineValue', doc.textDocument)
expect(res).toBe(false)
})
it('should return merged results', async () => {
disposables.push(languages.registerInlineValuesProvider([{ language: '*' }], {
provideInlineValues: () => {
return null
}
}))
disposables.push(languages.registerInlineValuesProvider([{ language: '*' }], {
provideInlineValues: () => {
return [
InlineValueText.create(Range.create(0, 0, 0, 1), 'foo'),
InlineValueText.create(Range.create(0, 3, 0, 5), 'bar'),
]
}
}))
disposables.push(languages.registerInlineValuesProvider([{ language: '*' }], {
provideInlineValues: () => {
return [
InlineValueText.create(Range.create(0, 0, 0, 1), 'foo'),
]
}
}))
let doc = await workspace.document
let res = await languages.provideInlineValues(doc.textDocument, Range.create(0, 0, 3, 0), { frameId: 3, stoppedLocation: Range.create(0, 0, 0, 3) }, CancellationToken.None)
expect(res.length).toBe(2)
})
})
})

169
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/linkedEditing.test.ts

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Range, Position } from 'vscode-languageserver-protocol'
import LinkedEditingHandler from '../../handler/linkedEditing'
import languages from '../../languages'
import workspace from '../../workspace'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let handler: LinkedEditingHandler
let disposables: Disposable[] = []
let wordPattern: string | undefined
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
handler = helper.plugin.getHandler().linkedEditingHandler
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
helper.updateConfiguration('coc.preferences.enableLinkedEditing', true)
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
async function registerProvider(content: string, position: Position): Promise<void> {
let doc = await workspace.document
disposables.push(languages.registerLinkedEditingRangeProvider([{ language: '*' }], {
provideLinkedEditingRanges: (doc, pos) => {
let document = workspace.getDocument(doc.uri)
let range = document.getWordRangeAtPosition(pos)
if (!range) return null
let text = doc.getText(range)
let ranges: Range[] = document.getSymbolRanges(text)
return { ranges, wordPattern }
}
}))
await nvim.setLine(content)
await doc.synchronize()
await handler.enable(doc, position)
}
async function matches(): Promise<number> {
let res = await nvim.call('getmatches') as any[]
res = res.filter(o => o.group === 'CocLinkedEditing')
return res.length
}
describe('LinkedEditing', () => {
it('should active and cancel on cursor moved', async () => {
await registerProvider('foo foo a ', Position.create(0, 0))
expect(await matches()).toBe(2)
await nvim.command(`normal! $`)
await helper.waitValue(() => {
return matches()
}, 0)
})
it('should active when moved to another word', async () => {
await registerProvider('foo foo bar bar bar', Position.create(0, 0))
await nvim.call('cursor', [1, 9])
await helper.waitValue(() => {
return matches()
}, 3)
})
it('should active on text change', async () => {
let doc = await workspace.document
await registerProvider('foo foo a ', Position.create(0, 0))
await nvim.call('cursor', [1, 1])
await nvim.call('nvim_buf_set_text', [doc.bufnr, 0, 0, 0, 0, ['i']])
await doc.synchronize()
let line = await nvim.line
expect(line).toBe('ifoo ifoo a ')
await nvim.call('nvim_buf_set_text', [doc.bufnr, 0, 0, 0, 1, []])
await doc.synchronize()
line = await nvim.line
expect(line).toBe('foo foo a ')
})
it('should cancel when change out of range', async () => {
let doc = await workspace.document
await registerProvider('foo foo bar', Position.create(0, 0))
await helper.waitValue(() => {
return matches()
}, 2)
await nvim.call('nvim_buf_set_text', [doc.bufnr, 0, 9, 0, 10, ['']])
await doc.synchronize()
await helper.waitValue(() => {
return matches()
}, 0)
})
it('should cancel on editor change', async () => {
await registerProvider('foo foo a ', Position.create(0, 0))
await nvim.command(`enew`)
await helper.wait(50)
await helper.waitValue(() => {
return matches()
}, 0)
})
it('should cancel when insert none word character', async () => {
await registerProvider('foo foo a ', Position.create(0, 0))
await nvim.call('cursor', [1, 4])
await nvim.input('i')
await nvim.input('a')
await helper.waitValue(() => {
return matches()
}, 2)
await nvim.input('i')
await nvim.input('@')
await helper.waitValue(() => {
return matches()
}, 0)
})
it('should cancel when insert not match wordPattern', async () => {
wordPattern = '[A-Z]'
await registerProvider('foo foo a ', Position.create(0, 0))
await nvim.call('cursor', [1, 4])
await nvim.input('i')
await nvim.input('A')
await helper.waitValue(() => {
return matches()
}, 2)
await nvim.input('i')
await nvim.input('3')
await helper.waitValue(() => {
return matches()
}, 0)
})
it('should cancel request on cursor moved', async () => {
disposables.push(languages.registerLinkedEditingRangeProvider([{ language: '*' }], {
provideLinkedEditingRanges: (doc, pos, token) => {
return new Promise(resolve => {
token.onCancellationRequested(() => {
clearTimeout(timer)
resolve(null)
})
let timer = setTimeout(() => {
let document = workspace.getDocument(doc.uri)
let range = document.getWordRangeAtPosition(pos)
if (!range) return resolve(null)
let text = doc.getText(range)
let ranges: Range[] = document.getSymbolRanges(text)
resolve({ ranges, wordPattern })
}, 1000)
})
}
}))
let doc = await workspace.document
await nvim.setLine('foo foo ')
await doc.synchronize()
await nvim.call('cursor', [1, 2])
await helper.wait(10)
await nvim.call('cursor', [1, 9])
await helper.waitValue(() => {
return matches()
}, 0)
})
})

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save