Browse Source

vim: upgrade coc.nvim

Maxim Likhachev 4 years ago
parent
commit
b891538c22
  1. 207
      etc/soft/nvim/+plugins/coc.nvim/Backers.md
  2. 142
      etc/soft/nvim/+plugins/coc.nvim/CONTRIBUTING.md
  3. 2
      etc/soft/nvim/+plugins/coc.nvim/LICENSE.md
  4. 9
      etc/soft/nvim/+plugins/coc.nvim/Readme.md
  5. 2
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc.vim
  6. 87
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/api.vim
  7. 71
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/client.vim
  8. 60
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/compat.vim
  9. 41
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/cursor.vim
  10. 257
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/float.vim
  11. 58
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/helper.vim
  12. 312
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/highlight.vim
  13. 45
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/list.vim
  14. 14
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/task.vim
  15. 230
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/util.vim
  16. 56
      etc/soft/nvim/+plugins/coc.nvim/autoload/coc/window.vim
  17. 68687
      etc/soft/nvim/+plugins/coc.nvim/build/index.js
  18. 306
      etc/soft/nvim/+plugins/coc.nvim/data/schema.json
  19. 1346
      etc/soft/nvim/+plugins/coc.nvim/doc/coc.txt
  20. 75
      etc/soft/nvim/+plugins/coc.nvim/esbuild.js
  21. 17
      etc/soft/nvim/+plugins/coc.nvim/jest.js
  22. 83
      etc/soft/nvim/+plugins/coc.nvim/package.json
  23. 115
      etc/soft/nvim/+plugins/coc.nvim/plugin/coc.vim
  24. 17
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/autoload/coc/source/email.vim
  25. 62
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/changedFiles.test.ts
  26. 140
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/connection.test.ts
  27. 89
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/converter.test.ts
  28. 154
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/delayer.test.ts
  29. 1054
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/features.test.ts
  30. 128
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/integration.test.ts
  31. 35
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testFileWatcher.js
  32. 40
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testInitializeResult.js
  33. 414
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/server/testServer.js
  34. 5
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/coc-settings.json
  35. 446
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/basic.test.ts
  36. 122
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/float.test.ts
  37. 66
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/match.test.ts
  38. 92
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/sources.test.ts
  39. 32
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/util.test.ts
  40. 7
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/global/index.js
  41. 7
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/global/package.json
  42. 6
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/package.json
  43. 7
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/root.js
  44. 13
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/test/index.js
  45. 33
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/test/package.json
  46. 7
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/vim/local/index.js
  47. 7
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/vim/local/package.json
  48. 398
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/callHierarchy.test.ts
  49. 386
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codeActions.test.ts
  50. 269
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/codelens.test.ts
  51. 254
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/colors.test.ts
  52. 82
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/commands.test.ts
  53. 71
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/fold.test.ts
  54. 253
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/format.test.ts
  55. 138
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/highlights.test.ts
  56. 178
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/hover.test.ts
  57. 93
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/index.test.ts
  58. 99
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/links.test.ts
  59. 306
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/locations.test.ts
  60. 426
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/outline.test.ts
  61. 117
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/parser.ts
  62. 347
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/refactor.test.ts
  63. 263
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/rename.test.ts
  64. 101
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/search.test.ts
  65. 143
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/selectionRange.test.ts
  66. 180
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/semanticTokens.test.ts
  67. 376
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/signature.test.ts
  68. 284
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/symbols.test.ts
  69. 22
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/helper.test.ts
  70. 248
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/helper.ts
  71. 174
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/basicList.test.ts
  72. 134
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/commandTask.test.ts
  73. 32
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/formatting.test.ts
  74. 79
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/location.test.ts
  75. 509
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/manager.test.ts
  76. 903
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/mappings.test.ts
  77. 246
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/session.test.ts
  78. 203
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/sources.test.ts
  79. 140
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/ui.test.ts
  80. 204
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/worker.test.ts
  81. 151
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/markdown/index.test.ts
  82. 119
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/markdown/renderer.test.ts
  83. 1
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/memos.json
  84. 35
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/array.test.ts
  85. 53
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/attach.test.ts
  86. 78
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/chars.test.ts
  87. 35
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/client.test.ts
  88. 728
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/completion.test.ts
  89. 247
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/configurations.test.ts
  90. 421
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/cursors.test.ts
  91. 60
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/db.test.ts
  92. 27
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/decorator.test.ts
  93. 250
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticBuffer.test.ts
  94. 98
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticCollection.test.ts
  95. 556
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticManager.test.ts
  96. 60
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/dialog.test.ts
  97. 178
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diff.test.ts
  98. 399
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/document.test.ts
  99. 67
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/events.test.ts
  100. 221
      etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/extensions.test.ts
  101. Some files were not shown because too many files have changed in this diff Show More

207
etc/soft/nvim/+plugins/coc.nvim/Backers.md

@ -0,0 +1,207 @@ @@ -0,0 +1,207 @@
# Backers
coc.nvim? Help us keep it alive by [donating funds](https://www.bountysource.com/teams/coc-nvim)😘!
<a href="https://github.com/oblitum" target="_blank" title="oblitum">
<img src="https://github.com/oblitum.png?size=64" width="64" height="64" alt="oblitum">
</a>
<a href="https://github.com/free-easy" target="_blank" title="free-easy">
<img src="https://github.com/free-easy.png?size=64" width="64" height="64" alt="free-easy">
</a>
<a href="https://github.com/ruanyl" target="_blank" title="ruanyl">
<img src="https://github.com/ruanyl.png?size=64" width="64" height="64" alt="ruanyl">
</a>
<a href="https://github.com/robjuffermans" target="_blank" title="robjuffermans">
<img src="https://github.com/robjuffermans.png?size=64" width="64" height="64" alt="robjuffermans">
</a>
<a href="https://github.com/iamcco" target="_blank" title="iamcco">
<img src="https://github.com/iamcco.png?size=64" width="64" height="64" alt="iamcco">
</a>
<a href="https://github.com/phcerdan" target="_blank" title="phcerdan">
<img src="https://github.com/phcerdan.png?size=64" width="64" height="64" alt="phcerdan">
</a>
<a href="https://github.com/sarene" target="_blank" title="sarene">
<img src="https://github.com/sarene.png?size=64" width="64" height="64" alt="sarene">
</a>
<a href="https://github.com/robtrac" target="_blank" title="robtrac">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals89_puer8v.png" width="64" height="64" alt="robtrac">
</a>
<a href="https://github.com/raidou" target="_blank" title="raidou">
<img src="https://github.com/raidou.png?size=64" width="64" height="64" alt="raidou">
</a>
<a href="https://github.com/tomspeak" target="_blank" title="tomspeak">
<img src="https://github.com/tomspeak.png?size=64" width="64" height="64" alt="tomspeak">
</a>
<a href="https://github.com/taigacute" target="_blank" title="taigacute">
<img src="https://github.com/taigacute.png?size=64" width="64" height="64" alt="taigacute">
</a>
<a href="https://github.com/weirongxu" target="_blank" title="weirongxu">
<img src="https://github.com/weirongxu.png?size=64" width="64" height="64" alt="weirongxu">
</a>
<a href="https://github.com/tbo" target="_blank" title="tbo">
<img src="https://github.com/tbo.png?size=64" width="64" height="64" alt="tbo">
</a>
<a href="https://github.com/darthShadow" target="_blank" title="darthShadow">
<img src="https://github.com/darthShadow.png?size=64" width="64" height="64" alt="darthShadow">
</a>
<a href="https://github.com/yatli" target="_blank" title="yatli">
<img src="https://github.com/yatli.png?size=64" width="64" height="64" alt="yatli">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/gravatar/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/f8fbc5df2432deac7557cf5e111439f2" width="64" height="64" alt="Matt Greer">
</a>
<a href="#Backers">
<img src="https://avatars0.githubusercontent.com/u/2914269?v=4&s=100&s=400" width="64" height="64" alt="malob">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/gravatar/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/a8b8103b9131cdf694bea446881c05fb" width="64" height="64" alt="Emigre">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals27_bjhsl8.png" width="64" height="64" alt="OkanEsen">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals57_yatmux.png" width="64" height="64" alt="Lennaert Meijvogel">
</a>
<a href="#Backers">
<img src="https://avatars2.githubusercontent.com/u/557201?s=400&u=ac96c9da87099c27f094eec935a627cb32fdfdf2&v=4&s=400" width="64" height="64" alt="Nils Landt">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals10_mjtuws.png" width="64" height="64" alt="dlants">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals45_ecgl95.png" width="64" height="64" alt="RCVU">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals71_wi5cvo.png" width="64" height="64" alt="yatli">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/gravatar/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/2986e67e29cf2ad3de088f9f8bc131cf" width="64" height="64" alt="mikker">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/gravatar/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/8703a88e1c178112625bcb6970ed40e4" width="64" height="64" alt="Velovix">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals51_byhedz.png" width="64" height="64" alt="stCarolas">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals67_rzqguf.png" width="64" height="64" alt="Robbie Clarken">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/svdunc4lofagkaeobpar.png" width="64" height="64" alt="hallettj">
</a>
<a href="#Backers">
<img src="https://avatars0.githubusercontent.com/u/6803419?v=4&s=100&s=400" width="64" height="64" alt="appelgriebsch">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals75_a0xqeq.png" width="64" height="64" alt="cosminadrianpopescu">
</a>
<a href="#Backers">
<img src="https://avatars3.githubusercontent.com/u/301015?v=4&s=100&s=400" width="64" height="64" alt="partizan">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals24_s1h7ax.png" width="64" height="64" alt="ksaldana1">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals63_olgqd6.png" width="64" height="64" alt="jesperryom">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals70_t5kjmo.png" width="64" height="64" alt="JackCA">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals38_vwccce.png" width="64" height="64" alt="peymanmortazavi">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals92_htl0if.png" width="64" height="64" alt="jonaustin">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals33_ch4hs0.png" width="64" height="64" alt="Yuriy Ivanyuk">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals26_knlvug.png" width="64" height="64" alt="abenz1267">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals100_g8py5g.png" width="64" height="64" alt="Sh3Rm4n">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals14_bnuacq.png" width="64" height="64" alt="mwcz">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals78_hleldd.png" width="64" height="64" alt="Philipp-M">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals37_sikg8d.png" width="64" height="64" alt="gvelchuru">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals62_hxul6y.png" width="64" height="64" alt="JSamir">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals19_zafwti.png" width="64" height="64" alt="toby de havilland">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals97_iuw00n.png" width="64" height="64" alt="viniciusarcanjo">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals70_t5kjmo.png" width="64" height="64" alt="Mike Hearn">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals87_vnmrie.png" width="64" height="64" alt="darsto">
</a>
<a href="#Backers">
<img src="https://avatars2.githubusercontent.com/u/145502?v=4&s=100&s=400" width="64" height="64" alt="pyrho">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals102_hqrga7.png" width="64" height="64" alt="Frydac">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals90_qlafi0.png" width="64" height="64" alt="gsa9">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals16_qlob5k.png" width="64" height="64" alt="_andys8">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals27_bjhsl8.png" width="64" height="64" alt="iago-lito">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals44_xa5xwi.png" width="64" height="64" alt="ddaletski">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals83_ryixly.png" width="64" height="64" alt="jonatan-branting">
</a>
<a href="#Backers">
<img src="https://avatars3.githubusercontent.com/u/8683947?v=4&s=100&s=400" width="64" height="64" alt="yutakatay">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals87_vnmrie.png" width="64" height="64" alt="kevinrambaud">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals76_g3jfjp.png" width="64" height="64" alt="tomaskallup">
</a>
<a href="#Backers">
<img src="https://cloudinary-a.akamaihd.net/bountysource/image/upload/d_noaoqqwxegvmulwus0un.png,c_pad,w_400,h_400,b_white/Bountysource_Animals46_qe2ye0.png" width="64" height="64" alt="LewisSteele">
</a>
## 微信扫码赞助者
- free-easy
- sarene
- tomspeak
- robtrac
- 葫芦小金刚
- leo 陶
- 飞翔的白斩鸡
- mark_ll
- 火冷
- Solomon
- 李宇星
- Yus
- IndexXuan
- Sniper
- 陈达野
- 胖听
- Jimmy
- lightxue
- 小亦俊
- 周慎敏
- 凤鸣
- Wilson
- Abel

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

@ -0,0 +1,142 @@ @@ -0,0 +1,142 @@
# Contributing
## How do I... <a name="toc"></a>
- [Use This Guide](#introduction)?
- Make Something? 🤓👩🏽💻📜🍳
- [Project Setup](#project-setup)
- [Contribute Documentation](#contribute-documentation)
- [Contribute Code](#contribute-code)
- Manage Something ✅🙆🏼💃👔
- [Provide Support on Issues](#provide-support-on-issues)
- [Review Pull Requests](#review-pull-requests)
- [Join the Project Team](#join-the-project-team)
## Introduction
Thank you so much for your interest in contributing!. All types of contributions are encouraged and valued. See the [table of contents](#toc) for different ways to help and details about how this project handles them!📝
The [Project Team](#join-the-project-team) looks forward to your contributions. 🙌🏾✨
## Project Setup
So you wanna contribute some code! That's great! This project uses GitHub Pull Requests to manage contributions, so [read up on how to fork a GitHub project and file a PR](https://guides.github.com/activities/forking) if you've never done it before.
If this seems like a lot or you aren't able to do all this setup, you might also be able to [edit the files directly](https://help.github.com/articles/editing-files-in-another-user-s-repository/) without having to do any of this setup. Yes, [even code](#contribute-code).
If you want to go the usual route and run the project locally, though:
- [Install Node.js](https://nodejs.org/en/download/)
- [Install Yarn](https://yarnpkg.com)
- [Fork the project](https://guides.github.com/activities/forking/#fork)
Then in your terminal:
- Add coc.nvim to your vim's rtp by `set runtimepath^=/path/to/coc.nvim`
- `cd path/to/your/coc.nvim`
- `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.
And you should be ready to go!
## Contribute Documentation
Documentation is a super important, critical part of this project. Docs are how we keep track of what we're doing, how, and why. It's how we stay on the same page about our policies. And it's how we tell others everything they need in order to be able to use this project -- or contribute to it. So thank you in advance.
Documentation contributions of any size are welcome! Feel free to file a PR even if you're just rewording a sentence to be more clear, or fixing a spelling mistake!
To contribute documentation:
- [Set up the project](#project-setup).
- Edit or add any relevant documentation.
- Make sure your changes are formatted correctly and consistently with the rest of the documentation.
- Re-read what you wrote, and run a spellchecker on it to make sure you didn't miss anything.
- In your commit message(s), begin the first line with `docs:`. For example: `docs: Adding a doc contrib section to CONTRIBUTING.md`.
- Write clear, concise commit message(s) using [conventional-changelog format](https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md). Documentation commits should use `docs(<component>): <message>`.
- Go to https://github.com/neoclide/coc.nvim/pulls and open a new pull request with your changes.
- If your PR is connected to an open issue, add a line in your PR's description that says `Fixes: #123`, where `#123` is the number of the issue you're fixing.
## Contribute Code
We like code commits a lot! They're super handy, and they keep the project going and doing the work it needs to do to be useful to others.
Code contributions of just about any size are acceptable!
The main difference between code contributions and documentation contributions is that contributing code requires inclusion of relevant tests for the code being added or changed. Contributions without accompanying tests will be held off until a test is added, unless the maintainers consider the specific tests to be either impossible, or way too much of a burden for such a contribution.
To contribute code:
- [Set up the project](#project-setup).
- Make any necessary changes to the source code.
- Include any [additional documentation](#contribute-documentation) the changes might need.
- Make sure the code doesn't have lint issue by command `yarn lint` in your
terminal.
- Write tests that verify that your contribution works as expected when necessary.
- Make sure all tests passed by command `yarn jest` in your terminal.
- Write clear, concise commit message(s) using [conventional-changelog format](https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md).
- Dependency updates, additions, or removals must be in individual commits, and the message must use the format: `<prefix>(deps): PKG@VERSION`, where `<prefix>` is any of the usual `conventional-changelog` prefixes, at your discretion.
- Go to https://github.com/neoclide/coc.nvim/pulls and open a new pull request with your changes.
- If your PR is connected to an open issue, add a line in your PR's description that says `Fixes: #123`, where `#123` is the number of the issue you're fixing.
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)
## Provide Support on Issues
[Needs Collaborator](#join-the-project-team): none
Helping out other users with their questions is a really awesome way of contributing to any community. It's not uncommon for most of the issues on an open source projects being support-related questions by users trying to understand something they ran into, or find their way around a known bug.
Sometimes, the `support` label will be added to things that turn out to actually be other things, like bugs or feature requests. In that case, suss out the details with the person who filed the original issue, add a comment explaining what the bug is, and change the label from `support` to `bug` or `feature`. If you can't do this yourself, @mention a maintainer so they can do it.
In order to help other folks out with their questions:
- Go to the issue tracker and [filter open issues by the `support` label](https://github.com/neoclide/coc.nvim/issues?q=is%3Aopen+is%3Aissue+label%3Asupport).
- Read through the list until you find something that you're familiar enough with to give an answer to.
- Respond to the issue with whatever details are needed to clarify the question, or get more details about what's going on.
- Once the discussion wraps up and things are clarified, either close the issue, or ask the original issue filer (or a maintainer) to close it for you.
Some notes on picking up support issues:
- Avoid responding to issues you don't know you can answer accurately.
- As much as possible, try to refer to past issues with accepted answers. Link to them from your replies with the `#123` format.
- Be kind and patient with users -- often, folks who have run into confusing things might be upset or impatient. This is ok. Try to understand where they're coming from, and if you're too uncomfortable with the tone, feel free to stay away or withdraw from the issue. (note: if the user is outright hostile or is violating the CoC, [refer to the Code of Conduct](CODE_OF_CONDUCT.md) to resolve the conflict).
## Review Pull Requests
[Needs Collaborator](#join-the-project-team): Issue Tracker
While anyone can comment on a PR, add feedback, etc, PRs are only _approved_ by team members with Issue Tracker or higher permissions.
PR reviews use [GitHub's own review feature](https://help.github.com/articles/about-pull-request-reviews/), which manages comments, approval, and review iteration.
Some notes:
- You may ask for minor changes ("nitpicks"), but consider whether they are really blockers to merging: try to err on the side of "approve, with comments".
- _ALL PULL REQUESTS_ should be covered by a test: either by a previously-failing test, an existing test that covers the entire functionality of the submitted code, or new tests to verify any new/changed behavior. All tests must also pass and follow established conventions. Test coverage should not drop, unless the specific case is considered reasonable by maintainers.
- Please make sure you're familiar with the code or documentation being updated, unless it's a minor change (spellchecking, minor formatting, etc). You may @mention another project member who you think is better suited for the review, but still provide a non-approving review of your own.
- Be extra kind: people who submit code/doc contributions are putting themselves in a pretty vulnerable position, and have put time and care into what they've done (even if that's not obvious to you!) -- always respond with respect, be understanding, but don't feel like you need to sacrifice your standards for their sake, either. Just don't be a jerk about it?
## Join the Project Team
### Ways to Join
There are many ways to contribute! Most of them don't require any official status unless otherwise noted. That said, there's a couple of positions that grant special repository abilities, and this section describes how they're granted and what they do.
All of the below positions are granted based on the project team's needs, as well as their consensus opinion about whether they would like to work with the person and think that they would fit well into that position. The process is relatively informal, and it's likely that people who express interest in participating can just be granted the permissions they'd like.
You can spot a collaborator on the repo by looking for the `[Collaborator]` or `[Owner]` tags next to their names.
| 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. |
| 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. |

2
etc/soft/nvim/+plugins/coc.nvim/LICENSE.md

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
Copyright 2018-2018 by Qiming Zhao <chemzqm@gmail.com>aaa
Copyright 2018-2018 by Qiming Zhao <chemzqm@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

9
etc/soft/nvim/+plugins/coc.nvim/Readme.md

@ -4,9 +4,10 @@ @@ -4,9 +4,10 @@
</a>
<p align="center">Make your Vim/Neovim as smart as VSCode.</p>
<p align="center">
<a href="/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square"></a>
<a href="LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square"></a>
<a href="https://github.com/neoclide/coc.nvim/actions"><img alt="Actions" src="https://img.shields.io/github/workflow/status/neoclide/coc.nvim/coc.nvim%20CI?style=flat-square"></a>
<a href="/doc/coc.txt"><img alt="Doc" src="https://img.shields.io/badge/doc-%3Ah%20coc.txt-brightgreen.svg?style=flat-square"></a>
<a href="https://codecov.io/gh/neoclide/coc.nvim"><img alt="Codecov Coverage Status" src="https://img.shields.io/codecov/c/github/neoclide/coc.nvim.svg?style=flat-square"></a>
<a href="doc/coc.txt"><img alt="Doc" src="https://img.shields.io/badge/doc-%3Ah%20coc.txt-brightgreen.svg?style=flat-square"></a>
<a href="https://gitter.im/neoclide/coc.nvim"><img alt="Gitter" src="https://img.shields.io/gitter/room/neoclide/coc.nvim.svg?style=flat-square"></a>
</p>
</p>
@ -317,6 +318,10 @@ Try these steps when you have problem with coc.nvim. @@ -317,6 +318,10 @@ Try these steps when you have problem with coc.nvim.
<a href="https://opencollective.com/cocnvim/backer/27/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/27/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/28/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/28/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/29/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/29/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/30/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/30/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/31/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/31/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/32/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/32/avatar.svg?requireActive=false"></a>
<a href="https://opencollective.com/cocnvim/backer/33/website?requireActive=false" target="_blank"><img src="https://opencollective.com/cocnvim/backer/33/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>

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

@ -54,6 +54,8 @@ function! coc#_complete() abort @@ -54,6 +54,8 @@ function! coc#_complete() abort
if s:select_api && len(items) && preselect != -1
noa call complete(startcol, items)
call nvim_select_popupmenu_item(preselect, v:false, v:false, {})
" use <cmd> specific key to preselect item at once
call feedkeys("\<Cmd>\<CR>" , 'i')
else
call complete(startcol, items)
endif

87
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: MIT licence
" Last Modified: Nov 11, 2020
" Last Modified: Aug 10, 2021
" ============================================================================
if has('nvim') | finish | endif
scriptencoding utf-8
@ -16,6 +16,13 @@ function! s:buf_line_count(bufnr) abort @@ -16,6 +16,13 @@ function! s:buf_line_count(bufnr) abort
if bufnr('%') == a:bufnr
return line('$')
endif
if exists('*getbufinfo')
let info = getbufinfo(a:bufnr)
if empty(info)
return 0
endif
return info[0]['linecount']
endif
if exists('*getbufline')
let lines = getbufline(a:bufnr, 1, '$')
return len(lines)
@ -73,14 +80,20 @@ function! s:funcs.list_wins() abort @@ -73,14 +80,20 @@ function! s:funcs.list_wins() abort
return map(getwininfo(), 'v:val["winid"]')
endfunction
function s:inspect_type(v) abort
let types = ['Number', 'String', 'Funcref', 'List', 'Dictionary', 'Float', 'Boolean', 'Null']
return get(types, type(a:v), 'Unknown')
endfunction
function! s:funcs.call_atomic(calls)
let res = []
for [key, arglist] in a:calls
for i in range(len(a:calls))
let [key, arglist] = a:calls[i]
let name = key[5:]
try
call add(res, call(s:funcs[name], arglist))
catch /.*/
return [res, v:exception]
return [res, [i, "VimException(".s:inspect_type(v:exception).")", v:exception]]
endtry
endfor
return [res, v:null]
@ -109,6 +122,12 @@ function! s:funcs.command(command) abort @@ -109,6 +122,12 @@ function! s:funcs.command(command) abort
call timer_start(0, {-> s:execute(a:command)})
else
execute a:command
let err = get(g:, 'errmsg', '')
" get error from python script run.
if !empty(err)
unlet g:errmsg
throw err
endif
endif
endfunction
@ -187,14 +206,18 @@ endfunction @@ -187,14 +206,18 @@ endfunction
function! s:funcs.out_write(str)
echon a:str
call timer_start(0, {-> s:execute('redraw')})
endfunction
function! s:funcs.err_write(str)
echoerr a:str
"echoerr a:str
endfunction
function! s:funcs.err_writeln(str)
echoerr a:str
echohl ErrorMsg
echom a:str
echohl None
call timer_start(0, {-> s:execute('redraw')})
endfunction
function! s:funcs.create_namespace(name) abort
@ -275,7 +298,6 @@ function! s:funcs.buf_add_highlight(bufnr, srcId, hlGroup, line, colStart, colEn @@ -275,7 +298,6 @@ function! s:funcs.buf_add_highlight(bufnr, srcId, hlGroup, line, colStart, colEn
catch /^Vim\%((\a\+)\)\=:E967/
" ignore 967
endtry
let g:i = srcId
if a:srcId == 0
" return generated srcId
return srcId
@ -401,7 +423,7 @@ function! s:funcs.buf_set_var(bufnr, name, val) @@ -401,7 +423,7 @@ function! s:funcs.buf_set_var(bufnr, name, val)
endfunction
function! s:funcs.buf_del_var(bufnr, name)
call setbufvar(a:bufnr, a:name, v:null)
call coc#compat#buf_del_var(a:bufnr, a:name)
endfunction
function! s:funcs.buf_get_option(bufnr, name)
@ -458,26 +480,40 @@ else @@ -458,26 +480,40 @@ else
endfunction
endif
function! s:get_tabnr(winid) abort
let ref = {}
call s:win_execute(a:winid, 'tabpagenr()', ref)
return get(ref, 'out', 0)
endfunction
function! s:funcs.win_get_cursor(win_id) abort
let ref = {}
call s:win_execute(a:win_id, "[line('.'), col('.')-1]", ref)
return ref['out']
return get(ref, 'out', 0)
endfunction
function! s:funcs.win_get_var(win_id, name) abort
return gettabwinvar(0, a:win_id, a:name)
let tabnr = s:get_tabnr(a:win_id)
if tabnr
return gettabwinvar(tabnr, a:win_id, a:name)
endif
throw 'window '.a:win_id. ' not a valid window'
endfunction
function! s:funcs.win_set_width(win_id, width) abort
return s:win_execute(a:win_id, 'vertical resize '.a:width)
call s:win_execute(a:win_id, 'vertical resize '.a:width)
endfunction
function! s:funcs.win_set_buf(win_id, buf_id) abort
return s:win_execute(a:win_id, 'buffer '.a:buf_id)
call s:win_execute(a:win_id, 'buffer '.a:buf_id)
endfunction
function! s:funcs.win_get_option(win_id, name) abort
return gettabwinvar(0, a:win_id, '&'.a:name)
let tabnr = s:get_tabnr(a:win_id)
if tabnr
return gettabwinvar(tabnr, a:win_id, '&'.a:name)
endif
throw 'window '.a:win_id. ' not a valid window'
endfunction
function! s:funcs.win_set_height(win_id, height) abort
@ -491,20 +527,30 @@ function! s:funcs.win_set_option(win_id, name, value) abort @@ -491,20 +527,30 @@ function! s:funcs.win_set_option(win_id, name, value) abort
elseif val is v:false
let val = 0
endif
call setwinvar(a:win_id, '&'.a:name, val)
let tabnr = s:get_tabnr(a:win_id)
if tabnr
call settabwinvar(tabnr, a:win_id, '&'.a:name, val)
else
throw 'window '.a:win_id. ' not a valid window'
endif
endfunction
function! s:funcs.win_set_var(win_id, name, value) abort
call setwinvar(a:win_id, a:name, a:value)
let tabnr = s:get_tabnr(a:win_id)
if tabnr
call settabwinvar(tabnr, a:win_id, a:name, a:value)
else
throw 'window '.a:win_id. ' not a valid window'
endif
endfunction
function! s:funcs.win_del_var(win_id, name) abort
call settabwinvar(0, a:win_id, a:name, v:null)
call s:win_execute(a:win_id, 'unlet! w:'.a:name)
endfunction
function! s:funcs.win_is_valid(win_id) abort
let info = getwininfo(a:win_id)
return !empty(info)
return empty(info) ? v:false : v:true
endfunction
function! s:funcs.win_get_number(win_id) abort
@ -521,15 +567,16 @@ function! s:funcs.win_set_cursor(win_id, pos) abort @@ -521,15 +567,16 @@ function! s:funcs.win_set_cursor(win_id, pos) abort
endfunction
function! s:funcs.win_close(win_id, ...) abort
call s:win_execute(a:win_id, 'close!')
let force = get(a:, 1, 0)
call s:win_execute(a:win_id, 'close'.(force ? '!' : ''))
endfunction
function! s:funcs.win_get_tabpage(win_id) abort
let info = getwininfo(a:win_id)
if !info
let tabnr = s:get_tabnr(a:win_id)
if !tabnr
throw 'Invalid window id '.a:win_id
endif
return info[0]['tabnr']
return tabnr
endfunction
" }}

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

@ -56,7 +56,6 @@ function! s:start() dict @@ -56,7 +56,6 @@ function! s:start() dict
\ 'VIM_NODE_RPC': '1',
\ 'COC_NVIM': '1',
\ 'COC_CHANNEL_TIMEOUT': timeout,
\ 'COC_NO_WARNINGS': disable_warning,
\ 'TMPDIR': tmpdir,
\ }
\}
@ -73,35 +72,42 @@ function! s:start() dict @@ -73,35 +72,42 @@ function! s:start() dict
let self['running'] = 1
let self['channel'] = job_getchannel(job)
else
let original = {'tmpdir': $TMPDIR}
" env option not work on neovim
if exists('*setenv')
let original = {}
let opts = {
\ 'rpc': 1,
\ 'on_stderr': {channel, msgs -> s:on_stderr(self.name, msgs)},
\ 'on_exit': {channel, code -> s:on_exit(self.name, code)},
\ }
if has('nvim-0.5.0')
" could use env option
let opts['env'] = {
\ 'NODE_NO_WARNINGS': '1',
\ 'COC_CHANNEL_TIMEOUT': timeout,
\ 'TMPDIR': tmpdir
\ }
else
let original = {
\ 'NODE_NO_WARNINGS': getenv('NODE_NO_WARNINGS'),
\ 'COC_CHANNEL_TIMEOUT': getenv('COC_CHANNEL_TIMEOUT'),
\ 'COC_NO_WARNINGS': getenv('COC_NO_WARNINGS'),
\ 'TMPDIR': getenv('TMPDIR'),
\ }
call setenv('NODE_NO_WARNINGS', '1')
call setenv('COC_CHANNEL_TIMEOUT', timeout)
call setenv('COC_NO_WARNINGS', disable_warning)
call setenv('TMPDIR', tmpdir)
else
let $NODE_NO_WARNINGS = 1
let $COC_NO_WARNINGS = disable_warning
let $TMPDIR = tmpdir
if exists('*setenv')
call setenv('NODE_NO_WARNINGS', '1')
call setenv('COC_CHANNEL_TIMEOUT', timeout)
call setenv('TMPDIR', tmpdir)
else
let $NODE_NO_WARNINGS = 1
let $TMPDIR = tmpdir
endif
endif
let chan_id = jobstart(self.command, {
\ 'rpc': 1,
\ 'on_stderr': {channel, msgs -> s:on_stderr(self.name, msgs)},
\ 'on_exit': {channel, code -> s:on_exit(self.name, code)},
\})
if exists('*setenv')
for key in keys(original)
call setenv(key, original[key])
endfor
else
let $TMPDIR = original['tmpdir']
let chan_id = jobstart(self.command, opts)
if !empty(original)
if exists('*setenv')
for key in keys(original)
call setenv(key, original[key])
endfor
else
let $TMPDIR = original['TMPDIR']
endif
endif
if chan_id <= 0
echohl Error | echom 'Failed to start '.self.name.' service' | echohl None
@ -132,10 +138,6 @@ function! s:on_exit(name, code) abort @@ -132,10 +138,6 @@ function! s:on_exit(name, code) abort
let client['channel'] = v:null
let client['async_req_id'] = 1
if a:code != 0 && a:code != 143
" could be syntax error
if a:code == 1
call s:check_node()
endif
echohl Error | echom 'client '.a:name. ' abnormal exit with: '.a:code | echohl None
endif
endfunction
@ -327,14 +329,3 @@ function! coc#client#open_log() @@ -327,14 +329,3 @@ function! coc#client#open_log()
endif
execute 'vs '.s:logfile
endfunction
function! s:check_node() abort
let node = get(g:, 'coc_node_path', $COC_NODE_PATH == '' ? 'node' : $COC_NODE_PATH)
let output = trim(system(node . ' --version'))
let ms = matchlist(output, 'v\(\d\+\).\(\d\+\).\(\d\+\)')
if empty(ms) || str2nr(ms[1]) < 10 || (str2nr(ms[1]) == 10 && str2nr(ms[2]) < 12)
echohl Error
echon '[coc.nvim] Node version '.output.' < 10.12.0, please upgrade node.js or use g:coc_node_path variable.'
echohl None
endif
endfunction

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

@ -11,6 +11,14 @@ function! coc#compat#buf_win_id(bufnr) abort @@ -11,6 +11,14 @@ function! coc#compat#buf_win_id(bufnr) abort
return info[0]['winid']
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])
else
call nvim_buf_set_lines(a:bufnr, a:start, a:end, 0, a:replacement)
endif
endfunction
function! coc#compat#win_is_valid(winid) abort
if exists('*nvim_win_is_valid')
return nvim_win_is_valid(a:winid)
@ -84,10 +92,11 @@ endfunction @@ -84,10 +92,11 @@ endfunction
" hlGroup, pos, priority
function! coc#compat#matchaddgroups(winid, groups) abort
" add by winid
if s:is_vim && has('patch-8.1.0218') || has('nvim-0.4.0')
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
@ -128,25 +137,54 @@ function! coc#compat#buf_del_keymap(bufnr, mode, lhs) abort @@ -128,25 +137,54 @@ function! coc#compat#buf_del_keymap(bufnr, mode, lhs) abort
endif
endfunction
" execute command or list of commands in window
function! coc#compat#execute(winid, command) abort
if s:is_vim
if !exists('*win_execute')
throw 'win_execute function not exists, please upgrade your vim.'
function! coc#compat#buf_add_keymap(bufnr, mode, lhs, rhs, opts) abort
if !bufloaded(a:bufnr)
return
endif
if exists('*nvim_buf_set_keymap')
call nvim_buf_set_keymap(a:bufnr, a:mode, a:lhs, a:rhs, a:opts)
else
let cmd = a:mode . 'noremap '
for key in keys(a:opts)
if get(a:opts, key, 0)
let cmd .= '<'.key.'>'
endif
endfor
let cmd .= '<buffer> '.a:lhs.' '.a:rhs
if bufnr('%') == a:bufnr
execute cmd
elseif exists('*win_execute')
let winid = coc#compat#buf_win_id(a:bufnr)
if winid != -1
call win_execute(winid, cmd)
endif
endif
endif
endfunction
" execute command or list of commands in window
function! coc#compat#execute(winid, command, ...) abort
if exists('*win_execute')
if type(a:command) == v:t_string
keepalt call win_execute(a:winid, a:command)
keepalt call win_execute(a:winid, a:command, get(a:, 1, ''))
elseif type(a:command) == v:t_list
keepalt call win_execute(a:winid, join(a:command, "\n"))
keepalt call win_execute(a:winid, join(a:command, "\n"), get(a:, 1, ''))
endif
elseif has('nvim')
if !nvim_win_is_valid(a:winid)
return
endif
else
let curr = nvim_get_current_win()
noa keepalt call nvim_set_current_win(a:winid)
if type(a:command) == v:t_string
exec a:command
exe get(a:, 1, '').' '.a:command
elseif type(a:command) == v:t_list
exec join(a:command, "\n")
for cmd in a:command
exe get(a:, 1, '').' '.cmd
endfor
endif
noa keepalt call nvim_set_current_win(curr)
else
throw 'win_execute not exists, please upgrade vim.'
endif
endfunc

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

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
scriptencoding utf-8
" Position of cursor relative to screen cell
function! coc#cursor#screen_pos() abort
let nr = winnr()
let [row, col] = win_screenpos(nr)
return [row + winline() - 2, col + wincol() - 2]
endfunction
function! coc#cursor#move_by_col(delta)
let pos = getcurpos()
call cursor(pos[1], pos[2] + a:delta)
endfunction
" Get cursor position.
function! coc#cursor#position()
return [line('.') - 1, strchars(strpart(getline('.'), 0, col('.') - 1))]
endfunction
" Move cursor to position.
function! coc#cursor#move_to(line, character) abort
let content = getline(a:line + 1)
let pre = strcharpart(content, 0, a:character)
let col = strlen(pre) + 1
call cursor(a:line + 1, col)
endfunction
" Character offset of current cursor, vim provide bytes offset only.
function! coc#cursor#char_offset() abort
let offset = 0
let lnum = line('.')
for i in range(1, lnum)
if i == lnum
let offset += strchars(strpart(getline('.'), 0, col('.')-1))
else
let offset += strchars(getline(i)) + 1
endif
endfor
return offset
endfunction

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

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
scriptencoding utf-8
" Related to float window create
let s:is_vim = !has('nvim')
let s:root = expand('<sfile>:h:h:h')
let s:progresschars = get(g:, 'coc_progress_chars', ['░', '▇'])
@ -55,6 +54,10 @@ endfunction @@ -55,6 +54,10 @@ endfunction
" highlight all borders with first value.
" - close: (optional) show close button when is 1.
" - buttons: (optional) array of button text for create buttons at bottom.
" - codes: (optional) list of CodeBlock.
" - winblend: winblend option for float window, neovim only.
" - shadow: use shadow as border style, neovim only.
" - focusable: neovim only, default to true.
function! coc#float#create_float_win(winid, bufnr, config) abort
let lines = get(a:config, 'lines', v:null)
let bufnr = coc#float#create_buf(a:bufnr, lines, 'hide')
@ -78,6 +81,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -78,6 +81,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
endif
call popup_setoptions(a:winid, opts)
call coc#float#vim_buttons(a:winid, a:config)
call s:add_highlights(a:winid, a:config, 0)
return [a:winid, winbufnr(a:winid)]
else
let config = s:convert_config_nvim(a:config)
@ -85,6 +89,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -85,6 +89,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
call nvim_win_set_config(a:winid, config)
call nvim_win_set_cursor(a:winid, [1, 0])
call coc#float#nvim_create_related(a:winid, config, a:config)
call s:add_highlights(a:winid, a:config, 0)
return [a:winid, bufnr]
endif
endif
@ -111,8 +116,11 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -111,8 +116,11 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
if get(a:config, 'close', 0)
let opts['close'] = 'button'
endif
if !empty(get(a:config, 'borderhighlight', []))
let opts['borderhighlight'] = map(a:config['borderhighlight'], 'coc#highlight#compose_hlgroup(v:val,"'.hlgroup.'")')
if !empty(get(a:config, 'borderhighlight', v:null))
let borderhighlight = a:config['borderhighlight']
let opts['borderhighlight'] = type(borderhighlight) == 3
\ ? map(borderhighlight, 'coc#highlight#compose_hlgroup(v:val,"'.hlgroup.'")')
\ : [coc#highlight#compose_hlgroup(borderhighlight, hlgroup)]
endif
if !s:empty_border(get(a:config, 'border', []))
let opts['border'] = a:config['border']
@ -130,6 +138,10 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -130,6 +138,10 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
endif
else
let config = s:convert_config_nvim(a:config)
let border = get(a:config, 'border', [])
if has('nvim-0.5.0') && get(a:config, 'shadow', 0) && empty(get(a:config, 'buttons', v:null)) && empty(get(border, 2, 0))
let config['border'] = 'shadow'
endif
noa let winid = nvim_open_win(bufnr, 0, config)
if winid == 0
return []
@ -137,9 +149,13 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -137,9 +149,13 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
let hlgroup = get(a:config, 'highlight', 'CocFloating')
call setwinvar(winid, '&winhl', 'Normal:'.hlgroup.',NormalNC:'.hlgroup.',FoldColumn:'.hlgroup)
call setwinvar(winid, '&signcolumn', 'no')
call setwinvar(winid, '&foldenable', 0)
" cursorline highlight not work on old neovim
call setwinvar(winid, '&cursorline', 0)
call setwinvar(winid, 'border', get(a:config, 'border', []))
if get(a:config, 'winblend', 0)
call setwinvar(winid, '&winblend', a:config['winblend'])
endif
" no left border
if s:empty_border(get(a:config, 'border', [])) || a:config['border'][3] == 0
call setwinvar(winid, '&foldcolumn', 1)
@ -164,6 +180,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort @@ -164,6 +180,7 @@ function! coc#float#create_float_win(winid, bufnr, config) abort
call setwinvar(winid, '&wrap', 1)
call setwinvar(winid, '&linebreak', 1)
call setwinvar(winid, '&conceallevel', 0)
call s:add_highlights(winid, a:config, 0)
let g:coc_last_float_win = winid
call coc#util#do_autocmd('CocOpenFloat')
return [winid, bufnr]
@ -190,24 +207,26 @@ function! coc#float#nvim_create_related(winid, config, opts) abort @@ -190,24 +207,26 @@ function! coc#float#nvim_create_related(winid, config, opts) abort
let title = get(a:opts, 'title', '')
let buttons = get(a:opts, 'buttons', [])
let pad = empty(border) || get(border, 1, 0) == 0
let winblend = get(a:opts, 'winblend', 0)
let shadow = get(a:opts, 'shadow', 0)
if get(a:opts, 'close', 0)
call coc#float#nvim_close_btn(a:config, a:winid, border, borderhighlight, related)
call coc#float#nvim_close_btn(a:config, a:winid, border, borderhighlight, winblend, related)
elseif exists
call coc#float#close_related(a:winid, 'close')
endif
if !empty(buttons)
call coc#float#nvim_buttons(a:config, a:winid, buttons, get(border, 2, 0), pad, hlgroup, borderhighlight, related)
call coc#float#nvim_buttons(a:config, a:winid, buttons, get(border, 2, 0), pad, hlgroup, borderhighlight, winblend, shadow, related)
elseif exists
call coc#float#close_related(a:winid, 'buttons')
endif
if !s:empty_border(border)
call coc#float#nvim_border_win(a:config, a:winid, border, title, !empty(buttons), borderhighlight, related)
call coc#float#nvim_border_win(a:config, a:winid, border, title, !empty(buttons), borderhighlight, winblend, shadow, related)
elseif exists
call coc#float#close_related(a:winid, 'border')
endif
" Check right border
if pad
call coc#float#nvim_right_pad(a:config, a:winid, hlgroup, related)
call coc#float#nvim_right_pad(a:config, a:winid, hlgroup, winblend, related)
elseif exists
call coc#float#close_related(a:winid, 'pad')
endif
@ -215,7 +234,7 @@ function! coc#float#nvim_create_related(winid, config, opts) abort @@ -215,7 +234,7 @@ function! coc#float#nvim_create_related(winid, config, opts) abort
endfunction
" border window for neovim, content config with border
function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgroup, related) abort
function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgroup, winblend, shadow, related) abort
let winid = coc#float#get_related(a:winid, 'border')
let row = a:border[0] ? a:config['row'] - 1 : a:config['row']
let col = a:border[3] ? a:config['col'] - 1 : a:config['col']
@ -233,12 +252,18 @@ function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgrou @@ -233,12 +252,18 @@ function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgrou
\ 'focusable': v:false,
\ 'style': 'minimal',
\ }
if has('nvim-0.5.0') && a:shadow && !a:hasbtn && a:border[2]
let opt['border'] = 'shadow'
endif
if winid
call nvim_win_set_config(winid, opt)
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
else
noa let winid = nvim_open_win(bufnr, 0, opt)
if winid
if a:winblend
call setwinvar(winid, '&winblend', a:winblend)
endif
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
call setwinvar(winid, 'target_winid', a:winid)
call setwinvar(winid, 'kind', 'border')
@ -248,7 +273,7 @@ function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgrou @@ -248,7 +273,7 @@ function! coc#float#nvim_border_win(config, winid, border, title, hasbtn, hlgrou
endfunction
" neovim only
function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abort
function! coc#float#nvim_close_btn(config, winid, border, hlgroup, winblend, related) abort
let winid = coc#float#get_related(a:winid, 'close')
let config = {
\ 'relative': a:config['relative'],
@ -259,6 +284,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor @@ -259,6 +284,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor
\ 'focusable': v:true,
\ 'style': 'minimal',
\ }
if has('nvim-0.5.0')
let config['zindex'] = 300
endif
if winid
call nvim_win_set_config(winid, config)
else
@ -268,6 +296,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor @@ -268,6 +296,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
call setwinvar(winid, 'target_winid', a:winid)
call setwinvar(winid, 'kind', 'close')
if a:winblend
call setwinvar(winid, '&winblend', a:winblend)
endif
call add(a:related, winid)
endif
call s:nvim_create_keymap(winid)
@ -275,8 +306,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor @@ -275,8 +306,9 @@ function! coc#float#nvim_close_btn(config, winid, border, hlgroup, related) abor
endfunction
" Create padding window by config of current window & border config
function! coc#float#nvim_right_pad(config, winid, hlgroup, related) abort
function! coc#float#nvim_right_pad(config, winid, hlgroup, winblend, related) abort
let winid = coc#float#get_related(a:winid, 'pad')
let bufnr = 0
let config = {
\ 'relative': a:config['relative'],
\ 'width': 1,
@ -286,15 +318,28 @@ function! coc#float#nvim_right_pad(config, winid, hlgroup, related) abort @@ -286,15 +318,28 @@ function! coc#float#nvim_right_pad(config, winid, hlgroup, related) abort
\ 'focusable': v:false,
\ 'style': 'minimal',
\ }
if winid
if has('nvim-0.5.0')
let config['zindex'] = 300
endif
if winid && nvim_win_is_valid(winid)
let bufnr = nvim_win_get_buf(winid)
noa call nvim_win_close(winid, 1)
endif
let bufnr = coc#float#create_buf(0, repeat([''], a:config['height']))
let bufnr = coc#float#create_buf(bufnr, repeat([''], a:config['height']))
noa let winid = nvim_open_win(bufnr, 0, config)
if winid
" neovim'bug: the content shown in window could be wired.
call setwinvar(winid, '&foldcolumn', 1)
call setwinvar(winid, '&winhl', 'FoldColumn:'.a:hlgroup)
" minimal not work
if !has('nvim-0.4.3')
call setwinvar(winid, '&colorcolumn', 0)
call setwinvar(winid, '&number', 0)
call setwinvar(winid, '&relativenumber', 0)
call setwinvar(winid, '&foldcolumn', 0)
call setwinvar(winid, '&signcolumn', 0)
endif
if a:winblend
call setwinvar(winid, '&winblend', a:winblend)
endif
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
call setwinvar(winid, 'target_winid', a:winid)
call setwinvar(winid, 'kind', 'pad')
call add(a:related, winid)
@ -302,7 +347,7 @@ function! coc#float#nvim_right_pad(config, winid, hlgroup, related) abort @@ -302,7 +347,7 @@ function! coc#float#nvim_right_pad(config, winid, hlgroup, related) abort
endfunction
" draw buttons window for window with config
function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgroup, borderhighlight, related) abort
function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgroup, borderhighlight, winblend, shadow, related) abort
let winid = coc#float#get_related(a:winid, 'buttons')
let width = a:config['width'] + (a:pad ? 1 : 0)
let config = {
@ -314,6 +359,12 @@ function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgr @@ -314,6 +359,12 @@ function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgr
\ 'focusable': 1,
\ 'style': 'minimal',
\ }
if has('nvim-0.5.0')
let config['zindex'] = 300
if a:shadow
let config['border'] = 'shadow'
endif
endif
if winid
let bufnr = winbufnr(winid)
call s:create_btns_buffer(bufnr, width, a:buttons, a:borderbottom)
@ -325,6 +376,9 @@ function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgr @@ -325,6 +376,9 @@ function! coc#float#nvim_buttons(config, winid, buttons, borderbottom, pad, hlgr
call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
call setwinvar(winid, 'target_winid', a:winid)
call setwinvar(winid, 'kind', 'buttons')
if a:winblend
call setwinvar(winid, '&winblend', a:winblend)
endif
call add(a:related, winid)
call s:nvim_create_keymap(winid)
endif
@ -346,7 +400,11 @@ endfunction @@ -346,7 +400,11 @@ endfunction
" Create or refresh scrollbar for winid
" Need called on create, config, buffer change, scrolled
function! coc#float#nvim_scrollbar(winid) abort
if !has('nvim-0.4.0') || !coc#float#valid(a:winid) || getwinvar(a:winid, 'target_winid', 0)
if !has('nvim-0.4.0') || getwinvar(a:winid, 'target_winid', 0)
return
endif
let winids = nvim_tabpage_list_wins(nvim_get_current_tabpage())
if index(winids, a:winid) == -1
return
endif
let config = nvim_win_get_config(a:winid)
@ -364,18 +422,19 @@ function! coc#float#nvim_scrollbar(winid) abort @@ -364,18 +422,19 @@ function! coc#float#nvim_scrollbar(winid) abort
let ch = coc#float#content_height(bufnr, cw, getwinvar(a:winid, '&wrap'))
let closewin = coc#float#get_related(a:winid, 'close')
let border = getwinvar(a:winid, 'border', [])
let winblend = getwinvar(a:winid, '&winblend', 0)
let move_down = closewin && !get(border, 0, 0)
if move_down
let height = height - 1
endif
let id = coc#float#get_related(a:winid, 'scrollbar')
if ch <= height || height <= 0
if ch <= height || height <= 1
" no scrollbar, remove exists
if id
call s:close_win(id)
endif
return
endif
if move_down
let height = height - 1
endif
call coc#float#close_related(a:winid, 'pad')
let sbuf = id ? winbufnr(id) : 0
let sbuf = coc#float#create_buf(sbuf, repeat([' '], height))
@ -388,6 +447,9 @@ function! coc#float#nvim_scrollbar(winid) abort @@ -388,6 +447,9 @@ function! coc#float#nvim_scrollbar(winid) abort
\ 'focusable': v:false,
\ 'style': 'minimal',
\ }
if has('nvim-0.5.0')
let opts['zindex'] = 300
endif
if id
call nvim_win_set_config(id, opts)
else
@ -395,9 +457,14 @@ function! coc#float#nvim_scrollbar(winid) abort @@ -395,9 +457,14 @@ function! coc#float#nvim_scrollbar(winid) abort
if id == 0
return
endif
if winblend
call setwinvar(id, '&winblend', winblend)
endif
call setwinvar(id, 'kind', 'scrollbar')
call setwinvar(id, 'target_winid', a:winid)
call s:add_related(id, a:winid)
endif
call coc#float#nvim_scroll_adjust(a:winid)
let thumb_height = max([1, float2nr(floor(height * (height + 0.0)/ch))])
let wininfo = getwininfo(a:winid)[0]
let start = 0
@ -421,7 +488,6 @@ function! coc#float#nvim_scrollbar(winid) abort @@ -421,7 +488,6 @@ function! coc#float#nvim_scrollbar(winid) abort
call nvim_buf_add_highlight(sbuf, -1, 'PmenuSbar', idx, 0, 1)
endif
endfor
call s:add_related(id, a:winid)
endfunction
function! coc#float#create_border_lines(border, title, width, height, hasbtn) abort
@ -481,7 +547,7 @@ function! coc#float#create_cursor_float(winid, bufnr, lines, config) abort @@ -481,7 +547,7 @@ function! coc#float#create_cursor_float(winid, bufnr, lines, config) abort
return v:null
endif
let width = dimension['width']
let lines = map(a:lines, {_, s -> s =~# '^—' ? repeat('—', width) : s})
let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width) : s})
let config = extend(extend({'lines': lines, 'relative': 'cursor'}, a:config), dimension)
call coc#float#close_auto_hide_wins(a:winid)
let res = coc#float#create_float_win(a:winid, a:bufnr, config)
@ -491,7 +557,6 @@ function! coc#float#create_cursor_float(winid, bufnr, lines, config) abort @@ -491,7 +557,6 @@ function! coc#float#create_cursor_float(winid, bufnr, lines, config) abort
let alignTop = dimension['row'] < 0
let winid = res[0]
let bufnr = res[1]
call coc#highlight#add_highlights(winid, get(a:config, 'codes', []), get(a:config, 'highlights', []))
redraw
if has('nvim')
call coc#float#nvim_scrollbar(winid)
@ -510,7 +575,7 @@ function! coc#float#create_prompt_win(title, default, opts) abort @@ -510,7 +575,7 @@ function! coc#float#create_prompt_win(title, default, opts) abort
else
let col = curr + width <= &columns - 2 ? 0 : &columns - s:prompt_win_width
endif
let [lineIdx, colIdx] = coc#util#cursor_pos()
let [lineIdx, colIdx] = coc#cursor#screen_pos()
let bufnr = 0
if has('nvim')
let bufnr = s:prompt_win_bufnr
@ -534,7 +599,7 @@ function! coc#float#create_prompt_win(title, default, opts) abort @@ -534,7 +599,7 @@ function! coc#float#create_prompt_win(title, default, opts) abort
\ 'border': [1,1,1,1],
\ 'prompt': 1,
\ 'title': a:title,
\ 'lines': [a:default],
\ 'lines': s:is_vim ? v:null : [a:default],
\ 'highlight': get(a:opts, 'highlight', 'CocFloating'),
\ 'borderhighlight': [get(a:opts, 'borderhighlight', 'CocFloating')],
\ })
@ -549,21 +614,14 @@ function! coc#float#create_prompt_win(title, default, opts) abort @@ -549,21 +614,14 @@ function! coc#float#create_prompt_win(title, default, opts) abort
call nvim_set_current_win(winid)
inoremap <buffer> <C-a> <Home>
inoremap <buffer><expr><C-e> pumvisible() ? "\<C-e>" : "\<End>"
exe 'inoremap <silent><buffer> <esc> <C-r>=coc#float#close_i('.winid.')<CR><esc>'
exe 'imap <silent><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#float#prompt_insert(getline(''.''))\<cr>\<esc>"'
call feedkeys('A', 'in')
else
call setbufvar(bufnr, '&termwinkey', '<C-\>')
endif
return [bufnr, winid]
endfunction
function! coc#float#close_i(winid) abort
call coc#float#close(a:winid)
return ''
endfunction
function! coc#float#prompt_insert(text) abort
call coc#rpc#notify('PromptInsert', [a:text])
return ''
@ -780,22 +838,27 @@ endfunction @@ -780,22 +838,27 @@ endfunction
" Close related windows, or specific kind
function! coc#float#close_related(winid, ...) abort
let timer = getwinvar(a:winid, 'timer', 0)
if timer
call timer_stop(timer)
endif
let kind = get(a:, 1, '')
let winids = filter(coc#float#get_float_win_list(1), 'getwinvar(v:val, "target_winid", 0) == '.a:winid)
for id in winids
if s:is_vim
" vim doesn't throw
call popup_close(id)
elseif nvim_win_is_valid(id)
if empty(kind) || getwinvar(id, 'kind', '') ==# kind
noa call nvim_win_close(id, 1)
endif
let tabnr = coc#window#tabnr(a:winid)
if tabnr != -1
let timer = gettabwinvar(tabnr, a:winid, 'timer', 0)
if timer
call timer_stop(timer)
endif
endfor
let kind = get(a:, 1, '')
let winids = gettabwinvar(tabnr, a:winid, 'related', [])
for id in winids
if s:is_vim
" vim doesn't throw
noa call popup_close(id)
else
if empty(kind) || gettabwinvar(tabnr, id, 'kind', '') ==# kind
if nvim_win_is_valid(id)
noa call nvim_win_close(id, 1)
endif
endif
endif
endfor
endif
endfunction
" Close related windows if target window is not visible.
@ -843,7 +906,7 @@ function! coc#float#get_config_cursor(lines, config) abort @@ -843,7 +906,7 @@ function! coc#float#get_config_cursor(lines, config) abort
let ch += float2nr(ceil(str2float(string(dw))/(maxWidth - 2)))
endfor
let width = coc#helper#min(maxWidth, width)
let [lineIdx, colIdx] = coc#util#cursor_pos()
let [lineIdx, colIdx] = coc#cursor#screen_pos()
" How much we should move left
let offsetX = coc#helper#min(get(a:config, 'offsetX', 0), colIdx)
let showTop = 0
@ -874,7 +937,9 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort @@ -874,7 +937,9 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort
let rp = &columns - pumbounding['col'] - pw
let showRight = pumbounding['col'] > rp ? 0 : 1
let maxWidth = showRight ? coc#helper#min(rp - 1, a:config['maxWidth']) : coc#helper#min(pumbounding['col'] - 1, a:config['maxWidth'])
let maxHeight = &lines - pumbounding['row'] - &cmdheight - 1
let border = get(a:config, 'border', [])
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
@ -887,7 +952,7 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort @@ -887,7 +952,7 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort
endfor
let width = float2nr(coc#helper#min(maxWidth, width))
let height = float2nr(coc#helper#min(maxHeight, ch))
let lines = map(a:lines, {_, s -> s =~# '^—' ? repeat('—', width - 2 + (s:is_vim && ch > height ? -1 : 0)) : s})
let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width - 2 + (s:is_vim && ch > height ? -1 : 0)) : s})
let opts = {
\ 'lines': lines,
\ 'relative': 'editor',
@ -895,15 +960,19 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort @@ -895,15 +960,19 @@ function! coc#float#create_pum_float(winid, bufnr, lines, config) abort
\ 'row': pumbounding['row'],
\ 'height': height,
\ 'width': width - 2 + (s:is_vim && ch > height ? -1 : 0),
\ 'codes': get(a:config, 'codes', []),
\ }
for key in ['border', 'highlight', 'borderhighlight', 'winblend', 'focusable', 'shadow']
if has_key(a:config, key)
let opts[key] = a:config[key]
endif
endfor
call coc#float#close_auto_hide_wins(a:winid)
let res = coc#float#create_float_win(a:winid, a:bufnr, opts)
if empty(res)
return v:null
endif
call coc#highlight#add_highlights(res[0], a:config['codes'], a:config['highlights'])
call setwinvar(res[0], 'kind', 'pum')
redraw
if has('nvim')
call coc#float#nvim_scrollbar(res[0])
endif
@ -1378,7 +1447,7 @@ function! coc#float#cursor_relative(winid) abort @@ -1378,7 +1447,7 @@ function! coc#float#cursor_relative(winid) abort
if winid == a:winid
return v:null
endif
let [cursorLine, cursorCol] = coc#util#cursor_pos()
let [cursorLine, cursorCol] = coc#cursor#screen_pos()
if has('nvim')
let [row, col] = nvim_win_get_position(a:winid)
return {'row' : row - cursorLine, 'col' : col - cursorCol}
@ -1387,6 +1456,47 @@ function! coc#float#cursor_relative(winid) abort @@ -1387,6 +1456,47 @@ function! coc#float#cursor_relative(winid) abort
return {'row' : pos['line'] - cursorLine - 1, 'col' : pos['col'] - cursorCol - 1}
endfunction
" Change border window & close window when scrollbar is shown.
function! coc#float#nvim_scroll_adjust(winid) abort
let winid = coc#float#get_related(a:winid, 'border')
if !winid
return
endif
let bufnr = winbufnr(winid)
let lines = nvim_buf_get_lines(bufnr, 0, -1, 0)
if len(lines) > 2 && coc#helper#last_character(lines[1]) ==# s:borderchars[1]
let cw = nvim_win_get_width(a:winid)
let width = nvim_win_get_width(winid)
if width - cw != 1 + (strcharpart(lines[1], 0, 1) ==# s:borderchars[3] ? 1 : 0)
return
endif
call nvim_win_set_width(winid, width + 1)
let lastline = len(lines) - 1
for i in range(0, lastline)
let line = lines[i]
if i == 0 && strcharpart(lines[0], 0, 1) ==# s:borderchars[4]
let add = s:borderchars[0]
elseif i == lastline && strcharpart(lines[i], 0, 1) ==# s:borderchars[7]
let add = s:borderchars[2]
else
let add = ' '
endif
let prev = strcharpart(line, 0, strchars(line) - 1)
let lines[i] = prev . add . coc#helper#last_character(line)
endfor
call nvim_buf_set_lines(bufnr, 0, -1, 0, lines)
let id = coc#float#get_related(a:winid, 'close')
if id
let [row, col] = nvim_win_get_position(id)
call nvim_win_set_config(id, {
\ 'relative': 'editor',
\ 'row': row,
\ 'col': col + 1,
\ })
endif
endif
endfunction
" move winid include relative windows.
function! s:adjust_win_row(winid, changed) abort
let ids = getwinvar(a:winid, 'related', [])
@ -1535,13 +1645,20 @@ function! s:nvim_create_keymap(winid) abort @@ -1535,13 +1645,20 @@ function! s:nvim_create_keymap(winid) abort
if a:winid == 0
return
endif
let curr = win_getid()
" nvim should support win_execute so we don't break visual mode.
let m = mode()
if m == 'n' || m == 'i' || m == 'ic'
noa call win_gotoid(a:winid)
nnoremap <buffer><silent> <LeftRelease> :call coc#float#nvim_float_click()<CR>
noa call win_gotoid(curr)
if exists('*nvim_buf_set_keymap')
let bufnr = winbufnr(a:winid)
call nvim_buf_set_keymap(bufnr, 'n', '<LeftRelease>', ':call coc#float#nvim_float_click()<CR>', {
\ 'silent': v:true,
\ 'nowait': v:true
\ })
else
let curr = win_getid()
let m = mode()
if m == 'n' || m == 'i' || m == 'ic'
noa call win_gotoid(a:winid)
nnoremap <buffer><silent> <LeftRelease> :call coc#float#nvim_float_click()<CR>
noa call win_gotoid(curr)
endif
endif
endfunction
@ -1731,3 +1848,19 @@ function! s:str_compose(line, idx, text) abort @@ -1731,3 +1848,19 @@ function! s:str_compose(line, idx, text) abort
let first = strcharpart(a:line, 0, a:idx)
return first.a:text.strcharpart(a:line, a:idx + strwidth(a:text))
endfunction
function! s:add_highlights(winid, config, create) abort
let codes = get(a:config, 'codes', [])
let highlights = get(a:config, 'highlights', [])
if empty(codes) && empty(highlights) && a:create
return
endif
let bgGroup = get(a:config, 'highlight', 'CocFloating')
for obj in codes
let hlGroup = get(obj, 'hlGroup', v:null)
if !empty(hlGroup)
let obj['hlGroup'] = coc#highlight#compose_hlgroup(hlGroup, bgGroup)
endif
endfor
call coc#highlight#add_highlights(a:winid, codes, highlights)
endfunction

58
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/helper.vim

@ -1,6 +1,64 @@ @@ -1,6 +1,64 @@
scriptencoding utf-8
" Helper methods for viml
function! coc#helper#get_charactor(line, col) abort
return strchars(strpart(a:line, 0, a:col - 1))
endfunction
function! coc#helper#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
endif
endfor
return 1
endfunction
" get change between two lines
function! coc#helper#str_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
let startOffset = 0
let currLen = strchars(a:curr)
let prevLen = strchars(a:previous)
if len(end)
let endLen = strchars(end)
for i in range(min([prevLen, endLen]))
if strcharpart(end, endLen - 1 - i, 1) ==# strcharpart(a:previous, prevLen -1 -i, 1)
let endOffset = endOffset + 1
else
break
endif
endfor
endif
let remain = endOffset == 0 ? a:previous : strcharpart(a:previous, 0, prevLen - endOffset)
if len(remain)
for i in range(min([strchars(remain), strchars(start)]))
if strcharpart(remain, i, 1) ==# strcharpart(start, i ,1)
let startOffset = startOffset + 1
else
break
endif
endfor
endif
return {
\ 'start': startOffset,
\ 'end': prevLen - endOffset,
\ 'text': strcharpart(a:curr, startOffset, currLen - startOffset - endOffset)
\ }
endfunction
function! coc#helper#str_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)
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

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

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
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:prop_offset = get(g:, 'coc_text_prop_offset', 1000)
let s:namespace_map = {}
let s:ns_id = 1
@ -12,44 +13,186 @@ if has('nvim-0.5.0') @@ -12,44 +13,186 @@ if has('nvim-0.5.0')
endtry
endif
function! coc#highlight#get_highlights(bufnr, key) abort
let l:res = []
" 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('nvim-0.5.0') && !exists('*prop_list')
throw 'Get highlights requires neovim 0.5.0 or vim support prop_list()'
endif
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, 0]
let markers = nvim_buf_get_extmarks(a:bufnr, ns, [a:start, 0], end, {'details': v:true})
for [_, row, start_col, details] in markers
let delta = details['end_row'] - row
if delta > 1 || (delta == 1 && details['end_col'] != 0)
" Don't known neovim's api for multiple lines markers.
continue
endif
let lines = getbufline(a:bufnr, row + 1)
if empty(lines)
" It's possible that markers exceeded last line.
continue
endif
let text = lines[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
else
let id = s:prop_offset + ns
" we could only get textprops line by line
let end = a:end == -1 ? getbufinfo(a:bufnr)[0]['linecount'] : 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
endfunction
" Update highlights by check exists highlights.
function! coc#highlight#update_highlights(bufnr, key, highlights, ...) abort
let bufnr = a:bufnr
if a:bufnr == 0
let bufnr = bufnr('%')
endif
if !bufloaded(bufnr)
return
endif
let start = get(a:, 1, 0)
let end = get(a:, 2, -1)
if empty(a:highlights)
call coc#highlight#clear_highlight(bufnr, a:key, start, end)
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 = 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 coc#helper#obj_equal(item, hi)
call add(indexes, i)
let nextIndex = max([nextIndex, i + 1])
endif
endfor
endfor
endif
let currIndex = nextIndex
" all highlights of current line exists, not clear.
if len(indexes) == len(items)
let exists = 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') && end == -1
let count = nvim_buf_line_count(bufnr)
" remove highlights exceed last line.
call nvim_buf_clear_namespace(bufnr, ns, count, -1)
endif
else
call coc#highlight#clear_highlight(bufnr, a:key, start, end)
endif
let indexes = range(0, total - 1)
if !empty(exists)
let indexes = filter(indexes, 'index(exists, v:val) == -1')
endif
for i in indexes
let hi = a:highlights[i]
call coc#highlight#add_highlight(bufnr, ns, hi['hlGroup'], hi['lnum'], hi['colStart'], hi['colEnd'])
endfor
endfunction
if s:is_vim
let l:lines = len(getbufline(a:bufnr, 1, '$'))
for l:line in range(l:lines)
let l:list = prop_list(l:line + 1, {"bufnr": a:bufnr})
for l:prop in l:list
if l:prop["start"] == 0 || l:prop["end"] == 0
function! coc#highlight#get_highlights(bufnr, key) abort
if !has_key(s:namespace_map, a:key) || !bufloaded(a:bufnr)
return []
endif
let res = []
let ns = s:namespace_map[a:key]
if exists('*prop_list')
let lines = getbufline(a:bufnr, 1, '$')
let linecount = len(lines)
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
" multi line tokens are not supported; simply ignore it
continue
endif
let l:group = l:prop["type"]
let l:start = l:prop["col"] - 1
let l:end = l:start + l:prop["length"]
call add(l:res, {
\ "group": l:group,
\ "line": l:line,
\ "startCharacter": l:start,
\ "endCharacter": l:end
let text = lines[line - 1]
call add(res, {
\ 'hlGroup': s:prop_type_hlgroup(prop['type']),
\ 'lnum': line - 1,
\ 'colStart': coc#helper#get_charactor(text, prop['col']),
\ 'colEnd': coc#helper#get_charactor(text, prop['col'] + prop['length'])
\ })
endfor
endfor
else
let srcId = s:create_namespace(a:key)
let l:marks = nvim_buf_get_extmarks(a:bufnr, srcId, 0, -1, {"details": v:true})
for [_, l:line, l:start, l:details] in l:marks
call add(l:res, {
\ "group": l:details["hl_group"],
\ "line": l:line,
\ "startCharacter": l:start,
\ "endCharacter": l:details["end_col"]
elseif has('nvim-0.5.0')
let markers = nvim_buf_get_extmarks(a:bufnr, ns, 0, -1, {'details': v:true})
let lines = getbufline(a:bufnr, 1, '$')
let total = len(lines)
for [_, line, start_col, details] in markers
if line >= total
" Could be markers exceed end of line
continue
endif
let text = lines[line]
let delta = details['end_row'] - line
if delta > 1 || (delta == 1 && details['end_col'] != 0)
" can't handle, single line only
continue
endif
call add(res, {
\ 'hlGroup': details['hl_group'],
\ 'lnum': line,
\ 'colStart': coc#helper#get_charactor(text, start_col + 1),
\ 'colEnd': delta == 1 ? strchars(text) : coc#helper#get_charactor(text, details['end_col'] + 1)
\ })
endfor
else
throw 'Get highlights requires neovim 0.5.0 or vim support prop_list'
endif
return l:res
return res
endfunction
" highlight LSP range,
@ -63,7 +206,7 @@ function! coc#highlight#ranges(bufnr, key, hlGroup, ranges) abort @@ -63,7 +206,7 @@ function! coc#highlight#ranges(bufnr, key, hlGroup, ranges) abort
let synmaxcol = 1000
endif
let synmaxcol = min([synmaxcol, 1000])
let srcId = s:create_namespace(a:key)
let srcId = coc#highlight#create_namespace(a:key)
for range in a:ranges
let start = range['start']
let end = range['end']
@ -100,7 +243,7 @@ function! coc#highlight#clear_highlight(bufnr, key, start_line, end_line) abort @@ -100,7 +243,7 @@ function! coc#highlight#clear_highlight(bufnr, key, start_line, end_line) abort
if !bufloaded(bufnr)
return
endif
let src_id = s:create_namespace(a:key)
let src_id = coc#highlight#create_namespace(a:key)
if has('nvim')
call nvim_buf_clear_namespace(a:bufnr, src_id, a:start_line, a:end_line)
else
@ -138,7 +281,7 @@ endfunction @@ -138,7 +281,7 @@ endfunction
" Add highlights to line groups of winid, support hlGroup and filetype
" config should have startLine, endLine (1 based, end excluded) and filetype or hlGroup
" config should have startLine, endLine (0 based, end excluded) and filetype or hlGroup
" endLine should > startLine and endLine is excluded
"
" export interface CodeBlock {
@ -148,61 +291,78 @@ endfunction @@ -148,61 +291,78 @@ endfunction
" endLine: number
" }
function! coc#highlight#highlight_lines(winid, blocks) abort
let currwin = win_getid()
let switch = has('nvim') && currwin != a:winid
if switch
noa call nvim_set_current_win(a:winid)
endif
let defined = []
let region_id = 1
let defined = []
let cmds = []
for config in a:blocks
let start = config['startLine'] + 1
let end = config['endLine'] == -1 ? len(getbufline(winbufnr(a:winid), 1, '$')) + 1 : config['endLine'] + 1
let filetype = get(config, 'filetype', '')
let hlGroup = get(config, 'hlGroup', '')
if !empty(hlGroup)
call s:execute(a:winid, 'syntax region '.hlGroup.' start=/\%'.start.'l/ end=/\%'.end.'l/')
call add(cmds, 'syntax region '.hlGroup.' start=/\%'.start.'l/ end=/\%'.end.'l/')
else
let filetype = matchstr(filetype, '\v^\w+')
if empty(filetype) || filetype == 'txt' || index(get(g:, 'coc_markdown_disabled_languages', []), filetype) != -1
continue
endif
if index(defined, filetype) == -1
call s:execute(a:winid, 'syntax include @'.toupper(filetype).' syntax/'.filetype.'.vim')
if has('nvim')
unlet! b:current_syntax
elseif exists('*win_execute')
call win_execute(a:winid, 'unlet! b:current_syntax')
endif
call add(cmds, 'syntax include @'.toupper(filetype).' syntax/'.filetype.'.vim')
call add(cmds, 'unlet! b:current_syntax')
call add(defined, filetype)
endif
call s:execute(a:winid, 'syntax region CodeBlock'.region_id.' start=/\%'.start.'l/ end=/\%'.end.'l/ contains=@'.toupper(filetype).' keepend')
call add(cmds, 'syntax region CodeBlock'.region_id.' start=/\%'.start.'l/ end=/\%'.end.'l/ contains=@'.toupper(filetype).' keepend')
let region_id = region_id + 1
endif
endfor
if switch
noa call nvim_set_current_win(currwin)
if !empty(cmds)
call coc#compat#execute(a:winid, cmds, 'silent!')
endif
endfunction
" Copmpose 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
if a:fgGroup ==# a:bgGroup
return a:fgGroup
endif
if hlexists(hlGroup)
return hlGroup
endif
let fg = synIDattr(synIDtrans(hlID(a:fgGroup)), 'fg', 'gui')
let bg = synIDattr(synIDtrans(hlID(a:bgGroup)), 'bg', 'gui')
if fg =~# '^#' || bg =~# '^#'
call s:create_gui_hlgroup(hlGroup, fg, bg, '')
else
let fg = synIDattr(synIDtrans(hlID(a:fgGroup)), 'fg', 'cterm')
let bg = synIDattr(synIDtrans(hlID(a:bgGroup)), 'bg', 'cterm')
call s:create_cterm_hlgroup(hlGroup, fg, bg, '')
let fgId = synIDtrans(hlID(a:fgGroup))
let bgId = synIDtrans(hlID(a:bgGroup))
let guifg = synIDattr(fgId, 'reverse', 'gui') !=# '1' ? synIDattr(fgId, 'fg', 'gui') : synIDattr(fgId, 'bg', 'gui')
let guibg = synIDattr(bgId, 'reverse', 'gui') !=# '1' ? synIDattr(bgId, 'bg', 'gui') : synIDattr(bgId, 'fg', 'gui')
let ctermfg = synIDattr(fgId, 'reverse', 'cterm') !=# '1' ? synIDattr(fgId, 'fg', 'cterm') : synIDattr(fgId, 'bg', 'cterm')
let ctermbg = synIDattr(bgId, 'reverse', 'cterm') !=# '1' ? synIDattr(bgId, 'bg', 'cterm') : synIDattr(bgId, 'fg', 'cterm')
let bold = synIDattr(fgId, 'bold') ==# '1'
let italic = synIDattr(fgId, 'italic') ==# '1'
let underline = synIDattr(fgId, 'underline') ==# '1'
let cmd = 'silent hi ' . hlGroup
if !empty(guifg)
let cmd .= ' guifg=' . guifg
endif
if !empty(ctermfg)
let cmd .= ' ctermfg=' . ctermfg
elseif guifg =~# '^#'
let cmd .= ' ctermfg=' . coc#color#rgb2term(strpart(guifg, 1))
endif
if !empty(guibg)
let cmd .= ' guibg=' . guibg
endif
if !empty(ctermbg)
let cmd .= ' ctermbg=' . ctermbg
elseif guibg =~# '^#'
let cmd .= ' ctermbg=' . coc#color#rgb2term(strpart(guibg, 1))
endif
if bold
let cmd .= ' cterm=bold gui=bold'
elseif italic
let cmd .= ' cterm=italic gui=italic'
elseif underline
let cmd .= ' cterm=underline gui=underline'
endif
execute cmd
return hlGroup
endfunction
@ -335,40 +495,14 @@ function! coc#highlight#clear_matches(winid, ids) @@ -335,40 +495,14 @@ function! coc#highlight#clear_matches(winid, ids)
endif
endfunction
" Sets the highlighting for the given group
function! s:create_gui_hlgroup(group, fg, bg, attr)
if a:fg != ""
exec "silent hi " . a:group . " guifg=" . a:fg . " ctermfg=" . coc#color#rgb2term(strpart(a:fg, 1))
endif
if a:bg != ""
exec "silent hi " . a:group . " guibg=" . a:bg . " ctermbg=" . coc#color#rgb2term(strpart(a:bg, 1))
endif
if a:attr != ""
exec "silent hi " . a:group . " gui=" . a:attr . " cterm=" . a:attr
endif
endfun
function! s:create_cterm_hlgroup(group, fg, bg, attr) abort
if a:fg != ""
exec "silent hi " . a:group . " ctermfg=" . a:fg
endif
if a:bg != ""
exec "silent hi " . a:group . " ctermbg=" . a:bg
endif
if a:attr != ""
exec "silent hi " . a:group . " cterm=" . a:attr
endif
endfunction
function! s:execute(winid, cmd) abort
if has('nvim')
execute 'silent! ' a:cmd
else
call win_execute(a:winid, a:cmd, 'silent!')
function! s:prop_type_hlgroup(type) abort
if a:type=~# '^CocHighlight'
return a:type[12:]
endif
return prop_type_get(a:type)['highlight']
endfunction
function! s:create_namespace(key) abort
function! coc#highlight#create_namespace(key) abort
if type(a:key) == 0
return a:key
endif
@ -383,3 +517,7 @@ function! s:create_namespace(key) abort @@ -383,3 +517,7 @@ function! s:create_namespace(key) abort
endif
return s:namespace_map[a:key]
endfunction
function! coc#highlight#get_syntax_name(lnum, col)
return synIDattr(synIDtrans(synID(a:lnum,a:col,1)),"name")
endfunction

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

@ -141,15 +141,11 @@ function! coc#list#scroll_preview(dir) abort @@ -141,15 +141,11 @@ function! coc#list#scroll_preview(dir) abort
endfunction
function! coc#list#restore(winid, height)
if has('nvim')
if nvim_win_is_valid(a:winid)
call nvim_win_set_height(a:winid, a:height)
endif
else
if exists('win_execute')
call win_execute(a:winid, 'noa resize '.a:height, 'silent!')
redraw
endif
if has('nvim') && nvim_win_is_valid(a:winid)
call nvim_win_set_height(a:winid, a:height)
elseif s:is_vim && exists('*win_execute')
call win_execute(a:winid, 'noa resize '.a:height, 'silent!')
redraw
endif
endfunction
@ -164,13 +160,13 @@ function! coc#list#hide(original, height, winid) abort @@ -164,13 +160,13 @@ function! coc#list#hide(original, height, winid) abort
if !empty(arr) && arr[0] != 0
silent! pclose!
let previewwin = coc#list#get_preview(arr[0])
call s:close_win(previewwin)
call coc#window#close(previewwin)
endif
if !empty(getwininfo(a:original))
call win_gotoid(a:original)
endif
if a:winid
call s:close_win(a:winid)
silent! call coc#window#close(a:winid)
endif
if !empty(a:height) && win_getid() == a:original
if exists('*nvim_win_set_height')
@ -181,32 +177,6 @@ function! coc#list#hide(original, height, winid) abort @@ -181,32 +177,6 @@ function! coc#list#hide(original, height, winid) abort
endif
endfunction
function! s:close_win(winid) abort
if empty(a:winid) || a:winid == -1 || empty(getwininfo(a:winid))
return
endif
if s:is_vim
if exists('*win_execute')
noa call win_execute(a:winid, 'close!', 'silent!')
else
if win_getid() == a:winid
noa silent! close!
else
let winid = win_getid()
let res = win_gotoid(winid)
if res
noa silent! close!
noa wincmd p
endif
endif
endif
else
if nvim_win_is_valid(a:winid)
silent! noa call nvim_win_close(a:winid, 1)
endif
endif
endfunction
" Improve preview performance by reused window & buffer.
" lines - list of lines
" config.position - could be 'below' 'top' 'tab'.
@ -296,6 +266,7 @@ function! coc#list#preview(lines, config) abort @@ -296,6 +266,7 @@ function! coc#list#preview(lines, config) abort
endif
call coc#compat#execute(winid, ['syntax clear', 'noa call winrestview({"lnum":'.lnum.',"topline":'.max([1, lnum - 3]).'})'])
endif
call setwinvar(winid, '&foldenable', 0)
if s:prefix.' '.name != bufname(bufnr)
if s:is_vim
call win_execute(winid, 'noa file '.fnameescape(s:prefix.' '.name), 'silent!')

14
etc/soft/nvim/+plugins/coc.nvim/autoload/coc/task.vim

@ -53,11 +53,15 @@ function! coc#task#start(id, opts) @@ -53,11 +53,15 @@ function! coc#task#start(id, opts)
\ 'detach': get(a:opts, 'detach', 0),
\}
let original = {}
if !empty(env) && exists('*setenv') && exists('*getenv')
for key in keys(env)
let original[key] = getenv(key)
call setenv(key, env[key])
endfor
if !empty(env)
if has('nvim-0.5.0')
let options['env'] = env
elseif exists('*setenv') && exists('*getenv')
for key in keys(env)
let original[key] = getenv(key)
call setenv(key, env[key])
endfor
endif
endif
if get(a:opts, 'pty', 0)
let options['pty'] = 1

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

@ -3,10 +3,10 @@ let s:root = expand('<sfile>:h:h:h') @@ -3,10 +3,10 @@ let s:root = expand('<sfile>:h:h:h')
let s:is_win = has('win32') || has('win64')
let s:is_vim = !has('nvim')
let s:clear_match_by_id = has('nvim-0.5.0') || has('patch-8.1.1084')
let s:vim_api_version = 8
let s:vim_api_version = 10
let s:activate = ""
let s:quit = ""
if has("gui_macvim") && has('gui_running')
let s:app = "MacVim"
elseif $TERM_PROGRAM ==# "Apple_Terminal"
@ -19,6 +19,15 @@ elseif has('mac') @@ -19,6 +19,15 @@ elseif has('mac')
let s:activate = 'activate'
endif
function! coc#util#api_version() abort
return s:vim_api_version
endfunction
" get cursor position
function! coc#util#cursor()
return [line('.') - 1, strchars(strpart(getline('.'), 0, col('.') - 1))]
endfunction
function! coc#util#has_preview()
for i in range(1, winnr('$'))
if getwinvar(i, '&previewwindow')
@ -28,13 +37,9 @@ function! coc#util#has_preview() @@ -28,13 +37,9 @@ function! coc#util#has_preview()
return 0
endfunction
function! coc#util#api_version() abort
return s:vim_api_version
endfunction
" get cursor position
function! coc#util#cursor()
return [line('.') - 1, strchars(strpart(getline('.'), 0, col('.') - 1))]
function! coc#util#jumpTo(line, character) abort
echohl WarningMsg | echon 'coc#util#jumpTo is deprecated, use coc#cursor#move_to instead.' | echohl None
call coc#cursor#move_to(a:line, a:character)
endfunction
function! coc#util#path_replace_patterns() abort
@ -72,6 +77,31 @@ function! coc#util#check_refresh(bufnr) @@ -72,6 +77,31 @@ function! coc#util#check_refresh(bufnr)
return 1
endfunction
function! coc#util#diagnostic_info(bufnr, checkInsert) abort
let checked = coc#util#check_refresh(a:bufnr)
if !checked
return v:null
endif
if a:checkInsert && mode() =~# '^i'
return v:null
endif
let locationlist = ''
let winid = -1
for info in getwininfo()
if info['bufnr'] == a:bufnr
let winid = info['winid']
let locationlist = get(getloclist(winid, {'title': 1}), 'title', '')
break
endif
endfor
return {
\ 'bufnr': bufnr('%'),
\ 'winid': winid,
\ 'lnum': line('.'),
\ 'locationlist': locationlist
\ }
endfunction
function! coc#util#open_file(cmd, file)
let file = fnameescape(a:file)
execute a:cmd .' '.file
@ -99,7 +129,11 @@ function! coc#util#job_command() @@ -99,7 +129,11 @@ function! coc#util#job_command()
return
endif
if !filereadable(s:root.'/build/index.js')
echohl Error | echom '[coc.nvim] build/index.js not found, please compile coc.nvim by: npm run build' | echohl None
if isdirectory(s:root.'/src')
echohl Error | echom '[coc.nvim] build/index.js not found, please install dependencies and compile coc.nvim by: yarn install' | echohl None
else
echohl Error | echon '[coc.nvim] your coc.nvim is broken.' | echohl None
endif
return
endif
return [node] + get(g:, 'coc_node_args', ['--no-warnings']) + [s:root.'/build/index.js']
@ -112,16 +146,6 @@ function! coc#util#echo_hover(msg) @@ -112,16 +146,6 @@ function! coc#util#echo_hover(msg)
let g:coc_last_hover_message = a:msg
endfunction
function! coc#util#execute(cmd)
silent exe a:cmd
if &filetype ==# ''
filetype detect
endif
if s:is_vim
redraw!
endif
endfunction
function! coc#util#jump(cmd, filepath, ...) abort
if a:cmd != 'pedit'
silent! normal! m'
@ -155,20 +179,6 @@ function! coc#util#jump(cmd, filepath, ...) abort @@ -155,20 +179,6 @@ function! coc#util#jump(cmd, filepath, ...) abort
endif
endfunction
function! coc#util#jumpTo(line, character) abort
let content = getline(a:line + 1)
let pre = strcharpart(content, 0, a:character)
let col = strlen(pre) + 1
call cursor(a:line + 1, col)
endfunction
" Position of cursor relative to screen cell
function! coc#util#cursor_pos() abort
let nr = winnr()
let [row, col] = win_screenpos(nr)
return [row + winline() - 2, col + wincol() - 2]
endfunction
function! coc#util#echo_messages(hl, msgs)
if a:hl !~# 'Error' && (mode() !~# '\v^(i|n)$')
return
@ -205,11 +215,7 @@ function! coc#util#get_bufoptions(bufnr, maxFileSize) abort @@ -205,11 +215,7 @@ function! coc#util#get_bufoptions(bufnr, maxFileSize) abort
if !bufloaded(a:bufnr) | return v:null | endif
let bufname = bufname(a:bufnr)
let buftype = getbufvar(a:bufnr, '&buftype')
let previewwindow = 0
let winid = bufwinid(a:bufnr)
if winid != -1
let previewwindow = getwinvar(winid, '&previewwindow', 0)
endif
let size = -1
if bufnr('%') == a:bufnr
let size = line2byte(line("$") + 1)
@ -225,7 +231,7 @@ function! coc#util#get_bufoptions(bufnr, maxFileSize) abort @@ -225,7 +231,7 @@ function! coc#util#get_bufoptions(bufnr, maxFileSize) abort
\ 'size': size,
\ 'buftype': buftype,
\ 'winid': winid,
\ 'previewwindow': previewwindow == 0 ? v:false : v:true,
\ 'previewwindow': v:false,
\ 'variables': s:variables(a:bufnr),
\ 'fullpath': empty(bufname) ? '' : fnamemodify(bufname, ':p'),
\ 'eol': getbufvar(a:bufnr, '&eol'),
@ -255,10 +261,6 @@ function! coc#util#get_config(key) abort @@ -255,10 +261,6 @@ function! coc#util#get_config(key) abort
return coc#rpc#request('getConfig', [a:key])
endfunction
function! coc#util#on_error(msg) abort
echohl Error | echom '[coc.nvim] '.a:msg | echohl None
endfunction
function! coc#util#preview_info(info, filetype, ...) abort
pclose
keepalt new +setlocal\ previewwindow|setlocal\ buftype=nofile|setlocal\ noswapfile|setlocal\ wrap [Document]
@ -330,11 +332,6 @@ function! coc#util#get_input() @@ -330,11 +332,6 @@ function! coc#util#get_input()
return matchstr(before, '\k*$')
endfunction
function! coc#util#move_cursor(delta)
let pos = getcurpos()
call cursor(pos[1], pos[2] + a:delta)
endfunction
function! coc#util#get_complete_option()
let pos = getcurpos()
let line = getline(pos[1])
@ -401,10 +398,6 @@ function! coc#util#quickpick(title, items, cb) abort @@ -401,10 +398,6 @@ function! coc#util#quickpick(title, items, cb) abort
endif
endfunction
function! coc#util#get_syntax_name(lnum, col)
return synIDattr(synIDtrans(synID(a:lnum,a:col,1)),"name")
endfunction
function! coc#util#echo_signatures(signatures) abort
if pumvisible() | return | endif
echo ""
@ -428,13 +421,6 @@ function! s:echo_signature(parts) @@ -428,13 +421,6 @@ function! s:echo_signature(parts)
endfor
endfunction
function! coc#util#unplace_signs(bufnr, sign_ids)
if !bufloaded(a:bufnr) | return | endif
for id in a:sign_ids
execute 'silent! sign unplace '.id.' buffer='.a:bufnr
endfor
endfunction
function! coc#util#setline(lnum, line)
keepjumps call setline(a:lnum, a:line)
endfunction
@ -547,6 +533,7 @@ function! coc#util#vim_info() @@ -547,6 +533,7 @@ function! coc#util#vim_info()
\ 'locationlist': get(g:,'coc_enable_locationlist', 1),
\ 'progpath': v:progpath,
\ 'guicursor': &guicursor,
\ 'updateHighlight': has('nvim-0.5.0') || exists('*prop_list') ? v:true : v:false,
\ 'vimCommands': get(g:, 'coc_vim_commands', []),
\ 'sign': exists('*sign_place') && exists('*sign_unplace'),
\ 'textprop': has('textprop') && has('patch-8.1.1719') && !has('nvim') ? v:true : v:false,
@ -563,16 +550,31 @@ function! coc#util#highlight_options() @@ -563,16 +550,31 @@ function! coc#util#highlight_options()
\}
endfunction
function! coc#util#set_lines(bufnr, replacement, start, end) abort
if !s:is_vim
call nvim_buf_set_lines(a:bufnr, a:start, a:end, 0, a:replacement)
else
call coc#api#notify('buf_set_lines', [a:bufnr, a:start, a:end, 0, a:replacement])
function! coc#util#set_lines(bufnr, changedtick, original, replacement, start, end) abort
if !bufloaded(a:bufnr)
return
endif
return {
\ 'lines': getbufline(a:bufnr, 1, '$'),
\ 'changedtick': getbufvar(a:bufnr, 'changedtick')
\ }
if getbufvar(a:bufnr, 'changedtick') != a:changedtick && bufnr('%') == a:bufnr
" try apply current line change
let lnum = line('.')
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#helper#str_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#helper#str_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
call coc#compat#buf_set_lines(a:bufnr, a:start, a:end, a:replacement)
endfunction
function! coc#util#change_lines(bufnr, list) abort
@ -594,16 +596,14 @@ function! coc#util#change_lines(bufnr, list) abort @@ -594,16 +596,14 @@ function! coc#util#change_lines(bufnr, list) abort
endfor
exe 'noa buffer '.bufnr
endif
return {
\ 'lines': getbufline(a:bufnr, 1, '$'),
\ 'changedtick': getbufvar(a:bufnr, 'changedtick')
\ }
endfunction
" used by vim
function! coc#util#get_buf_lines(bufnr, changedtick)
if !bufloaded(a:bufnr) | return '' | endif
if !bufloaded(a:bufnr)
return v:null
endif
let changedtick = getbufvar(a:bufnr, 'changedtick')
if changedtick == a:changedtick
return v:null
@ -623,36 +623,6 @@ function! coc#util#get_changeinfo() @@ -623,36 +623,6 @@ function! coc#util#get_changeinfo()
\}
endfunction
" show diff of current buffer
function! coc#util#diff_content(lines) abort
let tmpfile = tempname()
setl foldenable
call writefile(a:lines, tmpfile)
let ft = &filetype
diffthis
execute 'vs '.tmpfile
if !empty(ft)
execute 'setf ' . ft
endif
diffthis
setl foldenable
endfunction
function! coc#util#clear_signs()
let buflist = filter(range(1, bufnr('$')), 'buflisted(v:val)')
for b in buflist
let signIds = []
let lines = split(execute('sign place buffer='.b), "\n")
for line in lines
let ms = matchlist(line, 'id=\(\d\+\)\s\+name=Coc')
if len(ms) > 0
call add(signIds, ms[1])
endif
endfor
call coc#util#unplace_signs(b, signIds)
endfor
endfunction
function! coc#util#open_url(url)
if has('mac') && executable('open')
call system('open '.a:url)
@ -730,17 +700,6 @@ function! coc#util#rebuild() @@ -730,17 +700,6 @@ function! coc#util#rebuild()
\})
endfunction
" content of first echo line
function! coc#util#echo_line()
let str = ''
let line = &lines - (&cmdheight - 1)
for i in range(1, &columns - 1)
let nr = screenchar(line, i)
let str = str . nr2char(nr)
endfor
return str
endfunction
" [r, g, b] ['255', '255', '255']
" return ['65535', '65535', '65535'] or return v:false to cancel
function! coc#util#pick_color(default_color)
@ -848,11 +807,6 @@ function! s:system(cmd) @@ -848,11 +807,6 @@ function! s:system(cmd)
return output
endfunction
function! coc#util#set_buf_var(bufnr, name, val) abort
if !bufloaded(a:bufnr) | return | endif
call setbufvar(a:bufnr, a:name, a:val)
endfunction
function! coc#util#unmap(bufnr, keys) abort
if bufnr('%') == a:bufnr
for key in a:keys
@ -899,10 +853,6 @@ function! coc#util#refactor_foldlevel(lnum) abort @@ -899,10 +853,6 @@ function! coc#util#refactor_foldlevel(lnum) abort
return 1
endfunction
function! coc#util#get_pretext() abort
return strpart(getline('.'), 0, col('.') - 1)
endfunction
function! coc#util#refactor_fold_text(lnum) abort
let range = ''
let info = get(b:line_infos, a:lnum, [])
@ -912,13 +862,6 @@ function! coc#util#refactor_fold_text(lnum) abort @@ -912,13 +862,6 @@ function! coc#util#refactor_fold_text(lnum) abort
return trim(getline(a:lnum)[3:]).' '.range
endfunction
function! coc#util#set_buf_lines(bufnr, lines) abort
let res = setbufline(a:bufnr, 1, a:lines)
if res == 0
silent call deletebufline(a:bufnr, len(a:lines) + 1, '$')
endif
endfunction
" get tabsize & expandtab option
function! coc#util#get_format_opts(bufnr) abort
if a:bufnr && bufloaded(a:bufnr)
@ -931,30 +874,3 @@ function! coc#util#get_format_opts(bufnr) abort @@ -931,30 +874,3 @@ function! coc#util#get_format_opts(bufnr) abort
let tabsize = &shiftwidth == 0 ? &tabstop : &shiftwidth
return [tabsize, &expandtab]
endfunction
function! coc#util#clearmatches(ids, ...)
let winid = get(a:, 1, win_getid())
call coc#highlight#clear_matches(winid, a:ids)
endfunction
" Character offset of current cursor
function! coc#util#get_offset() abort
let offset = 0
let lnum = line('.')
for i in range(1, lnum)
if i == lnum
let offset += strchars(strpart(getline('.'), 0, col('.')-1))
else
let offset += strchars(getline(i)) + 1
endif
endfor
return offset
endfunction
" Make sure window exists
function! coc#util#win_gotoid(winid) abort
noa let res = win_gotoid(a:winid)
if res == 0
throw 'Invalid window number'
endif
endfunction

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

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
function! coc#window#tabnr(winid) abort
if exists('*win_execute')
let ref = {}
call win_execute(a:winid, 'let ref["out"] = tabpagenr()')
return get(ref, 'out', -1)
elseif has('nvim')
let info = getwininfo(a:winid)
return empty(info) ? -1 : info[0]['tabnr']
else
throw 'win_execute() not exists, please upgrade your vim.'
endif
endfunction
" Get single window by window variable, current tab only
function! coc#window#find(key, val) abort
for i in range(1, winnr('$'))
let res = getwinvar(i, a:key)
if res == a:val
return win_getid(i)
endif
endfor
return -1
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 autocmd & errors
function! coc#window#close(winid) abort
if empty(a:winid) || a:winid == -1
return
endif
if exists('*nvim_win_is_valid') && exists('*nvim_win_close')
if nvim_win_is_valid(a:winid)
noa call nvim_win_close(a:winid, 1)
endif
elseif exists('*win_execute')
call coc#compat#execute(a:winid, 'noa close!', 'silent!')
else
let curr = win_getid()
if curr == a:winid
noa silent! close!
else
let res = win_gotoid(a:winid)
if res
noa silent! close!
noa call win_gotoid(curr)
endif
endif
endif
endfunction

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

File diff suppressed because one or more lines are too long

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

@ -3,6 +3,62 @@ @@ -3,6 +3,62 @@
"description": "Configuration file for coc.nvim",
"additionalProperties": false,
"definitions": {
"float": {
"type": "object",
"properties": {
"border": {
"type": "boolean",
"default": false,
"description": "Set to true to use borders."
},
"highlight": {
"type": "string",
"default": "CocFloating",
"description": "Background highlight group of float window."
},
"title": {
"type": "string",
"default": "",
"description": "Title used by float window."
},
"borderhighlight": {
"type": "string",
"default": "CocFloating",
"description": "Border highlight group of float window."
},
"close": {
"type": "boolean",
"default": false,
"description": "Set to true to draw close icon"
},
"maxWidth": {
"type": "integer",
"description": "Maximum width of float window, include border."
},
"maxHeight": {
"type": "integer",
"minimum": 2,
"description": "Maximum height of float window, include border."
},
"focusable": {
"type": "boolean",
"default": true,
"description": "Enable focus by user actions (wincmds, mouse events), neovim only."
},
"shadow": {
"type": "boolean",
"default": false,
"description": "Drop shadow effect by blending with the background, neovim only."
},
"winblend": {
"type": "integer",
"default": 0,
"minimum": 0,
"maximum": 100,
"description": "Enables pseudo-transparency by set 'winblend' option of window, neovim only."
}
}
},
"languageServerBase": {
"type": "object",
"properties": {
@ -304,11 +360,6 @@ @@ -304,11 +360,6 @@
"description": "Enable preselect feature of LSP, only works on neovim",
"default": false
},
"suggest.maxPreviewWidth": {
"type": "number",
"default": 80,
"description": "Maximum width of floating preview window."
},
"suggest.enablePreview": {
"type": "boolean",
"description": "Add preview option to completeopt, default: false.",
@ -319,6 +370,21 @@ @@ -319,6 +370,21 @@
"description": "Enable floating window for documentation when possible.",
"default": true
},
"suggest.floatConfig": {
"type": "object",
"description": "Configure style of documentation window for complete item.",
"allOf": [{ "$ref": "#/definitions/float" }],
"additionalProperties": false,
"properties": {
"border": {},
"highlight": {},
"borderhighlight": {},
"maxWidth": {},
"winblend": {},
"focusable": {},
"shadow": {}
}
},
"suggest.labelMaxLength": {
"type": "number",
"description": "Max length of abbr that shown as label of complete item.",
@ -404,7 +470,7 @@ @@ -404,7 +470,7 @@
"description": "Timeout for completion, in miliseconds."
},
"suggest.minTriggerInputLength": {
"type": "number",
"type": "integer",
"default": 1,
"description": "Mininal input length for trigger completion, default 1"
},
@ -515,6 +581,11 @@ @@ -515,6 +581,11 @@
"description": "Set to false to disable diagnostic display",
"default": true
},
"diagnostic.highlighLimit": {
"type": "number",
"description": "Limit count for highlighted diagnostics, too many diagnostic highlights could make vim stop responsing",
"default": 1000
},
"diagnostic.autoRefresh": {
"type": "boolean",
"description": "Enable automatically refresh diagnostics, use diagnosticRefresh action when it's disabled.",
@ -628,15 +699,23 @@ @@ -628,15 +699,23 @@
"description": "Text of hint sign",
"default": ">>"
},
"diagnostic.maxWindowHeight": {
"type": "number",
"description": "Maximum height of diagnostics floating window.",
"default": 8
},
"diagnostic.maxWindowWidth": {
"type": "number",
"description": "Maximum width of diagnostics floating window.",
"default": 80
"diagnostic.floatConfig": {
"type": "object",
"description": "Configure float window style of diagnostic message.",
"allOf": [{ "$ref": "#/definitions/float" }],
"additionalProperties": false,
"properties": {
"border": {},
"highlight": {},
"borderhighlight": {},
"title": {},
"close": {},
"maxHeight": {},
"maxWidth": {},
"winblend": {},
"focusable": {},
"shadow": {}
}
},
"diagnostic.filetypeMap": {
"type": "object",
@ -651,17 +730,17 @@ @@ -651,17 +730,17 @@
"diagnostic.separateRelatedInformationAsDiagnostics": {
"type": "boolean",
"default": false,
"description": "Separate related information as diagnostics"
"description": "Separate related information as diagnostics."
},
"diagnostic.showUnused": {
"type": "boolean",
"default": true,
"description": "Show unused variables"
"description": "Show diagnostics with unused tag."
},
"diagnostic.showDeprecated": {
"type": "boolean",
"default": true,
"description": "Show deprecated variables"
"description": "Show diagnostics with deprecated tag."
},
"signature.enable": {
"type": "boolean",
@ -681,16 +760,23 @@ @@ -681,16 +760,23 @@
"default": "float",
"enum": ["float", "echo"]
},
"signature.maxWindowWidth": {
"type": "integer",
"default": 80,
"description": "Maximum width of signature float window (or popup on vim8)."
},
"signature.maxWindowHeight": {
"type": "number",
"description": "Maximum height of signature float window (or popup on vim8).",
"minimum": 3,
"default": 8
"signature.floatConfig": {
"type": "object",
"description": "Configure float window style of signature documents.",
"allOf": [{ "$ref": "#/definitions/float" }],
"additionalProperties": false,
"properties": {
"border": {},
"highlight": {},
"borderhighlight": {},
"title": {},
"close": {},
"maxHeight": {},
"maxWidth": {},
"winblend": {},
"focusable": {},
"shadow": {}
}
},
"signature.preferShownAbove": {
"type": "boolean",
@ -742,14 +828,28 @@ @@ -742,14 +828,28 @@
"description": "Target to show hover information, default is floating window when possible.",
"enum": ["preview", "echo", "float"]
},
"hover.floatMaxWidth": {
"hover.previewMaxHeight": {
"type": "number",
"default": 80,
"description": "Maximum width of hover float window, not bigger than 80."
"default": 12,
"description": "Max height of preview window for hover."
},
"hover.floatMaxHeight": {
"type": "number",
"description": "Maximum width of hover float window, not bigger than 80."
"hover.floatConfig": {
"type": "object",
"description": "Configure float window style of hover documents.",
"allOf": [{ "$ref": "#/definitions/float" }],
"additionalProperties": false,
"properties": {
"border": {},
"highlight": {},
"borderhighlight": {},
"title": {},
"close": {},
"maxHeight": {},
"maxWidth": {},
"winblend": {},
"focusable": {},
"shadow": {}
}
},
"hover.autoHide": {
"type": "boolean",
@ -979,6 +1079,121 @@ @@ -979,6 +1079,121 @@
"default": "<C-p>",
"description": "Key used for jump to previous cursors position."
},
"tree.closedIcon": {
"type": "string",
"default": "+",
"description": "Closed icon of tree view."
},
"tree.openedIcon": {
"type": "string",
"default": "-",
"description": "Opend icon of tree view."
},
"tree.key.toggleSelection": {
"type": "string",
"default": "<space>",
"description": "Trigger key to select/unselect item"
},
"tree.key.toggle": {
"type": "string",
"default": "t",
"description": "Trigger key to toggle expand state of tree node, does nothing with leaf node."
},
"tree.key.actions": {
"type": "string",
"default": "<tab>",
"description": "Trigger key to invoke actions."
},
"tree.key.collapseAll": {
"type": "string",
"default": "M",
"description": "Trigger key to collapse all tree node."
},
"tree.key.invoke": {
"type": "string",
"default": "<cr>",
"description": "Trigger key to invoke default command of current node or selection."
},
"tree.key.close": {
"type": "string",
"default": "<esc>",
"description": "Trigger key to dispose the tree and close tree window."
},
"tree.key.activeFilter": {
"type": "string",
"default": "f",
"description": "Trigger key active filter."
},
"tree.key.selectNext": {
"type": "string",
"default": "<C-j>",
"description": "Trigger key to select next item during filter."
},
"tree.key.selectPrevious": {
"type": "string",
"default": "<C-k>",
"description": "Trigger key to select previous item during filter."
},
"outline.showLineNumber": {
"type": "boolean",
"default": true,
"description": "Show line number of symbols."
},
"outline.splitCommand": {
"type": "string",
"default": "botright 30vs",
"description": "Window split command used by outline."
},
"outline.followCursor": {
"type": "boolean",
"default": true,
"description": "Reveal item in outline tree on cursor hold."
},
"outline.keepWindow": {
"type": "boolean",
"default": false,
"description": "Jump back to original window after outline is shown."
},
"outline.sortBy": {
"type": "string",
"default": "category",
"description": "Sort method for symbols.",
"enum": ["position", "name", "category"]
},
"outline.expandLevel": {
"type": "number",
"default": 1,
"description": "Expand level of tree nodes."
},
"outline.checkBufferSwitch": {
"type": "boolean",
"default": true,
"description": "Recreate outline view after user changed to another buffer on current tab."
},
"outline.codeActionKinds": {
"type": "array",
"default": ["", "quickfix", "refactor"],
"description": "Filter code actions in actions menu by kinds.",
"items": {
"type": "string",
"enum": ["", "quickfix", "refactor", "source"]
}
},
"callHierarchy.openCommand": {
"type": "string",
"default": "edit",
"description": "Open command for callHierarchy tree view."
},
"callHierarchy.splitCommand": {
"type": "string",
"default": "botright 30vs",
"description": "Window split command used by callHierarchy tree view."
},
"callHierarchy.enableTooltip": {
"type": "boolean",
"default": true,
"description": "Enable tooltip to show relative filepath of call hierarchy."
},
"coc.preferences.enableMessageDialog": {
"type": "boolean",
"default": false,
@ -1026,16 +1241,6 @@ @@ -1026,16 +1241,6 @@
"description": "Enable semanticTokens highlight if language server support it.",
"default": true
},
"coc.preferences.previewAutoClose": {
"type": "boolean",
"description": "Auto close preview window on cursor move.",
"default": true
},
"coc.preferences.previewMaxHeight": {
"type": "number",
"default": 12,
"description": "Max height of preview window for hover."
},
"coc.preferences.currentFunctionSymbolAutoUpdate": {
"type": "boolean",
"description": "Automatically update the value of b:coc_current_function on CursorHold event",
@ -1099,7 +1304,7 @@ @@ -1099,7 +1304,7 @@
},
"coc.preferences.formatOnType": {
"type": "boolean",
"description": "Set to true to enable format on type",
"description": "Set to true to enable formatting on typing",
"default": false
},
"coc.preferences.formatOnTypeFiletypes": {
@ -1110,13 +1315,6 @@ @@ -1110,13 +1315,6 @@
"type": "string"
}
},
"coc.preferences.highlightTimeout": {
"type": "integer",
"default": 500,
"minimum": 200,
"maximum": 5000,
"description": "Highlight timeout for buffer in floating window."
},
"coc.preferences.floatActions": {
"type": "boolean",
"description": "Set to false to disable float/popup support for actions menu, won't work on vim without float or popup window support.",
@ -1134,7 +1332,7 @@ @@ -1134,7 +1332,7 @@
},
"coc.preferences.excludeImageLinksInMarkdownDocument": {
"type": "boolean",
"description": "Exclude image links from document markdown text",
"description": "Exclude image links from markdown text in float window.",
"default": true
},
"coc.preferences.silentAutoupdate": {
@ -1223,7 +1421,7 @@ @@ -1223,7 +1421,7 @@
},
"coc.source.file.triggerCharacters": {
"type": "array",
"default": ["/"],
"default": ["/", "\\"],
"items": {
"type": "string"
}

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
const cp = require('child_process')
const fs = require('fs')
const path = require('path')
let revision = ''
try {
let res = cp.execSync('git rev-parse HEAD', {encoding: 'utf8'})
revision = res.trim().slice(0, 10)
} catch (e) {
// ignore
}
let envPlugin = {
name: 'env',
setup(build) {
build.onResolve({filter: /\/appenders/}, args => {
let fullpath = path.join(args.resolveDir, args.path)
return {
path: path.relative(__dirname, fullpath).replace(/\\/g, '/'),
namespace: 'env-ns'
}
})
build.onLoad({filter: /^node_modules\/log4js\/lib\/appenders$/, namespace: 'env-ns'}, args => {
let content = fs.readFileSync(path.join(args.path, 'index.js'), 'utf8')
return {
contents: content.replace(/require\.main/g, '""'),
resolveDir: args.path
}
})
}
}
async function start(watch) {
await require('esbuild').build({
entryPoints: ['src/main.ts'],
bundle: true,
watch,
minify: process.env.NODE_ENV === 'production',
sourcemap: process.env.NODE_ENV === 'development',
define: {REVISION: '"' + revision + '"', ESBUILD: 'true'},
mainFields: ['module', 'main'],
platform: 'node',
target: 'node12.12',
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]
})
}
let watch = false
if (process.argv.length > 2 && process.argv[2] === '--watch') {
console.log('watching...')
watch = {
onRebuild(error) {
if (error) {
console.error('watch build failed:', error)
} else {
console.log('watch build succeeded')
}
},
}
}
start(watch).catch(e => {
console.error(e)
})

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

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
const path = require('path')
const os = require('os')
const fs = require('fs')
process.on('uncaughtException', function (err) {
let msg = 'Uncaught exception: ' + err.stack
console.error(msg)
})
module.exports = async () => {
let dataHome = path.join(os.tmpdir(), `coc-test/${process.pid}`)
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__')
process.env.TMPDIR = '/tmp/coc-test'
}

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

@ -1,12 +1,20 @@ @@ -1,12 +1,20 @@
{
"name": "coc.nvim",
"name": "coc.nvim-master",
"version": "0.0.80",
"description": "LSP based intellisense engine for neovim & vim8.",
"main": "./lib/index.js",
"engines": {
"node": ">=8.10.0"
"node": ">=12.12.0"
},
"scripts": {
"clean": "rimraf lib build",
"lint": "eslint . --ext .ts --quiet",
"lint:typecheck": "tsc -p tsconfig.json",
"build": "node esbuild.js",
"test": "./node_modules/.bin/jest --forceExit",
"test-build": "./node_modules/.bin/jest --coverage --forceExit",
"prepare": "node esbuild.js"
},
"scripts": {},
"repository": {
"type": "git",
"url": "git+https://github.com/neoclide/coc.nvim.git"
@ -32,6 +40,13 @@ @@ -32,6 +40,13 @@
"clearMocks": true,
"globalSetup": "./jest.js",
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"<rootDir>/src/__tests__/*"
],
"coverageReporters": [
"text",
"lcov"
],
"moduleFileExtensions": [
"ts",
"tsx",
@ -44,7 +59,63 @@ @@ -44,7 +59,63 @@
"testRegex": "src/__tests__/.*\\.(test|spec)\\.ts$",
"coverageDirectory": "./coverage/"
},
"devDependencies": {},
"dependencies": {}
"devDependencies": {
"@types/cli-table": "^0.3.0",
"@types/debounce": "^3.0.0",
"@types/fb-watchman": "^2.0.0",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^27.0.1",
"@types/marked": "^2.0.4",
"@types/minimatch": "^3.0.3",
"@types/mkdirp": "^1.0.1",
"@types/node": "12.12.12",
"@types/semver": "^7.3.4",
"@types/tar": "^4.0.5",
"@types/uuid": "^8.3.0",
"@types/which": "^1.3.2",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"bser": "^2.1.1",
"esbuild": "^0.12.7",
"eslint": "^7.15.0",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-jsdoc": "^30.7.13",
"jest": "27.0.6",
"ts-jest": "^27.0.5",
"typescript": "^4.3.5",
"vscode-languageserver": "7.0.0"
},
"dependencies": {
"@chemzqm/neovim": "^5.4.0",
"bytes": "^3.1.0",
"tslib": "^2.0.3",
"ansi-styles": "^5.0.0",
"cli-table": "^0.3.4",
"debounce": "^1.2.0",
"fast-diff": "^1.2.0",
"fb-watchman": "^2.0.1",
"fs-extra": "^9.0.1",
"follow-redirects": "^1.13.0",
"http-proxy-agent": "^4.0.1",
"https-proxy-agent": "^5.0.0",
"isuri": "^2.0.3",
"jsonc-parser": "^3.0.0",
"log4js": "^6.3.0",
"marked": "^2.1.3",
"minimatch": "^3.0.4",
"rc": "^1.2.8",
"semver": "^7.3.2",
"strip-ansi": "^6.0.0",
"tar": "^6.1.9",
"unzip-stream": "^0.3.1",
"content-disposition": "^0.5.3",
"decompress-response": "^6.0.0",
"uuid": "^7.0.3",
"which": "^2.0.2",
"vscode-uri": "^2.1.2",
"vscode-jsonrpc": "^6.0.0",
"vscode-languageserver-protocol": "^3.16.0",
"vscode-languageserver-textdocument": "^1.0.1",
"vscode-languageserver-types": "^3.16.0"
}
}

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

@ -39,7 +39,6 @@ endfunction @@ -39,7 +39,6 @@ endfunction
call s:checkVersion()
let g:did_coc_loaded = 1
let g:coc_workspace_initialized = 0
let g:coc_service_initialized = 0
let s:is_win = has('win32') || has('win64')
let s:root = expand('<sfile>:h:h')
@ -169,6 +168,7 @@ function! s:OpenConfig() @@ -169,6 +168,7 @@ function! s:OpenConfig()
end
endif
execute 'edit '.home.'/coc-settings.json'
call coc#rpc#notify('checkJsonExtension', [])
endfunction
function! s:get_color(item, fallback) abort
@ -243,21 +243,17 @@ function! s:Disable() abort @@ -243,21 +243,17 @@ function! s:Disable() abort
endfunction
function! s:Autocmd(...) abort
if !g:coc_workspace_initialized
if !g:coc_service_initialized
return
endif
call coc#rpc#notify('CocAutocmd', a:000)
endfunction
function! s:SyncAutocmd(...)
if !g:coc_workspace_initialized
if !g:coc_service_initialized
return
endif
if g:coc_service_initialized
call coc#rpc#request('CocAutocmd', a:000)
else
call coc#rpc#notify('CocAutocmd', a:000)
endif
call coc#rpc#request('CocAutocmd', a:000)
endfunction
function! s:Enable(initialize)
@ -296,6 +292,7 @@ function! s:Enable(initialize) @@ -296,6 +292,7 @@ function! s:Enable(initialize)
autocmd WinEnter * call coc#float#nvim_win_enter(win_getid())
if exists('##WinClosed')
autocmd WinClosed * call coc#float#close_related(+expand('<afile>'))
autocmd WinClosed * call s:Autocmd('WinClosed', +expand('<afile>'))
endif
endif
if has('nvim-0.4.0') || has('patch-8.1.1719')
@ -307,7 +304,7 @@ function! s:Enable(initialize) @@ -307,7 +304,7 @@ function! s:Enable(initialize)
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:Autocmd('InsertCharPre', v:char)
autocmd InsertCharPre * call s:Autocmd('InsertCharPre', v:char, bufnr('%'))
if exists('##TextChangedP')
autocmd TextChangedP * call s:Autocmd('TextChangedP', +expand('<abuf>'), {'lnum': line('.'), 'col': col('.'), 'pre': strpart(getline('.'), 0, col('.') - 1), 'changedtick': b:changedtick})
endif
@ -352,12 +349,12 @@ function! s:Hi() abort @@ -352,12 +349,12 @@ function! s:Hi() abort
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 guifg=#989898 ctermfg=gray cterm=strikethrough gui=strikethrough
hi default CocStrikeThrough cterm=strikethrough gui=strikethrough
else
hi default CocStrikeThrough guifg=#989898 ctermfg=gray
endif
hi default CocFadeOut guifg=#928374 ctermfg=245
hi default CocMarkdownLink ctermfg=Blue guifg=#15aabf guibg=NONE
hi default link CocFadeOut Conceal
hi default link CocMarkdownCode markdownCode
hi default link CocMarkdownHeader markdownH1
hi default link CocMenuSel PmenuSel
@ -369,6 +366,8 @@ function! s:Hi() abort @@ -369,6 +366,8 @@ function! s:Hi() abort
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 CocListMode ModeMsg
hi default link CocListPath Comment
hi default link CocHighlightText CursorColumn
@ -376,6 +375,41 @@ function! s:Hi() abort @@ -376,6 +375,41 @@ function! s:Hi() abort
hi default link CocCursorRange Search
hi default link CocHighlightRead CocHighlightText
hi default link CocHighlightWrite CocHighlightText
" Tree view highlights
hi default link CocTreeTitle Title
hi default link CocTreeDescription Comment
hi default link CocTreeOpenClose CocBold
hi default link CocTreeSelected CursorLine
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
@ -384,6 +418,9 @@ function! s:Hi() abort @@ -384,6 +418,9 @@ function! s:Hi() abort
if !exists('*sign_getdefined') || empty(sign_getdefined('CocCurrentLine'))
sign define CocCurrentLine linehl=CocMenuSel
endif
if !exists('*sign_getdefined') || empty(sign_getdefined('CocTreeSelected'))
sign define CocTreeSelected linehl=CocTreeSelected
endif
if has('nvim-0.5.0')
hi default CocCursorTransparent gui=strikethrough blend=100
endif
@ -397,6 +434,31 @@ function! s:Hi() abort @@ -397,6 +434,31 @@ function! s:Hi() abort
endfor
endif
call s:AddAnsiGroups()
if get(g:, 'coc_default_semantic_highlight_groups', 0) == 1
hi default link CocSem_namespace Identifier
hi default link CocSem_type Type
hi default link CocSem_class Structure
hi default link CocSem_enum Type
hi default link CocSem_interface Type
hi default link CocSem_struct Structure
hi default link CocSem_typeParameter Type
hi default link CocSem_parameter Identifier
hi default link CocSem_variable Identifier
hi default link CocSem_property Identifier
hi default link CocSem_enumMember Constant
hi default link CocSem_event Identifier
hi default link CocSem_function Function
hi default link CocSem_method Function
hi default link CocSem_macro Macro
hi default link CocSem_keyword Keyword
hi default link CocSem_modifier StorageClass
hi default link CocSem_comment Comment
hi default link CocSem_string String
hi default link CocSem_number Number
hi default link CocSem_regexp Normal
hi default link CocSem_operator Operator
endif
endfunction
function! s:FormatFromSelected(type)
@ -429,7 +491,7 @@ function! s:ShowInfo() @@ -429,7 +491,7 @@ function! s:ShowInfo()
call add(lines, 'Error: javascript bundle not found, please compile code of coc.nvim by esbuild.')
endif
if !empty(lines)
belowright vnew
botright vnew
setl filetype=nofile
call setline(1, lines)
else
@ -442,6 +504,7 @@ function! s:ShowInfo() @@ -442,6 +504,7 @@ function! s:ShowInfo()
endif
endfunction
command! -nargs=0 CocOutline :call coc#rpc#notify('showOutline', [])
command! -nargs=? CocDiagnostics :call s:OpenDiagnostics(<f-args>)
command! -nargs=0 CocInfo :call s:ShowInfo()
command! -nargs=0 CocOpenLog :call coc#rpc#notify('openLog', [])
@ -458,7 +521,7 @@ command! -nargs=+ -complete=custom,s:ExtensionList CocUninstall :call CocAction @@ -458,7 +521,7 @@ command! -nargs=+ -complete=custom,s:ExtensionList CocUninstall :call CocAction
command! -nargs=* -complete=custom,s:CommandList -range CocCommand :call coc#rpc#notify('runCommand', [<f-args>])
command! -nargs=* -complete=custom,coc#list#options CocList :call coc#rpc#notify('openList', [<f-args>])
command! -nargs=? -complete=custom,coc#list#names CocListResume :call coc#rpc#notify('listResume', [<f-args>])
command! -nargs=0 -complete=custom,coc#list#names CocListCancel :call coc#rpc#notify('listCancel', [])
command! -nargs=? -complete=custom,coc#list#names CocListCancel :call coc#rpc#notify('listCancel', [])
command! -nargs=? -complete=custom,coc#list#names CocPrev :call coc#rpc#notify('listPrev', [<f-args>])
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>])
@ -505,16 +568,16 @@ nnoremap <silent> <Plug>(coc-refactor) :<C-u>call CocActionAs @@ -505,16 +568,16 @@ nnoremap <silent> <Plug>(coc-refactor) :<C-u>call CocActionAs
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 coc#rpc#request('cursorsSelect', [bufnr('%'), 'range', visualmode()])<CR>
nnoremap <silent> <Plug>(coc-cursors-word) :<C-u>call coc#rpc#request('cursorsSelect', [bufnr('%'), 'word', 'n'])<CR>
nnoremap <silent> <Plug>(coc-cursors-position) :<C-u>call coc#rpc#request('cursorsSelect', [bufnr('%'), 'position', 'n'])<CR>
vnoremap <silent> <Plug>(coc-funcobj-i) :<C-U>call coc#rpc#request('selectSymbolRange', [v:true, visualmode(), ['Method', 'Function']])<CR>
vnoremap <silent> <Plug>(coc-funcobj-a) :<C-U>call coc#rpc#request('selectSymbolRange', [v:false, visualmode(), ['Method', 'Function']])<CR>
onoremap <silent> <Plug>(coc-funcobj-i) :<C-U>call coc#rpc#request('selectSymbolRange', [v:true, '', ['Method', 'Function']])<CR>
onoremap <silent> <Plug>(coc-funcobj-a) :<C-U>call coc#rpc#request('selectSymbolRange', [v:false, '', ['Method', 'Function']])<CR>
vnoremap <silent> <Plug>(coc-classobj-i) :<C-U>call coc#rpc#request('selectSymbolRange', [v:true, visualmode(), ['Interface', 'Struct', 'Class']])<CR>
vnoremap <silent> <Plug>(coc-classobj-a) :<C-U>call coc#rpc#request('selectSymbolRange', [v:false, visualmode(), ['Interface', 'Struct', 'Class']])<CR>
onoremap <silent> <Plug>(coc-classobj-i) :<C-U>call coc#rpc#request('selectSymbolRange', [v:true, '', ['Interface', 'Struct', 'Class']])<CR>
onoremap <silent> <Plug>(coc-classobj-a) :<C-U>call coc#rpc#request('selectSymbolRange', [v:false, '', ['Interface', 'Struct', 'Class']])<CR>
vnoremap <silent> <Plug>(coc-cursors-range) :<C-u>call CocAction('cursorsSelect', bufnr('%'), 'range', visualmode())<CR>
nnoremap <silent> <Plug>(coc-cursors-word) :<C-u>call CocAction('cursorsSelect', bufnr('%'), 'word', 'n')<CR>
nnoremap <silent> <Plug>(coc-cursors-position) :<C-u>call CocAction('cursorsSelect', bufnr('%'), 'position', 'n')<CR>
vnoremap <silent> <Plug>(coc-funcobj-i) :<C-U>call CocAction('selectSymbolRange', v:true, visualmode(), ['Method', 'Function'])<CR>
vnoremap <silent> <Plug>(coc-funcobj-a) :<C-U>call CocAction('selectSymbolRange', v:false, visualmode(), ['Method', 'Function'])<CR>
onoremap <silent> <Plug>(coc-funcobj-i) :<C-U>call CocAction('selectSymbolRange', v:true, '', ['Method', 'Function'])<CR>
onoremap <silent> <Plug>(coc-funcobj-a) :<C-U>call CocAction('selectSymbolRange', v:false, '', ['Method', 'Function'])<CR>
vnoremap <silent> <Plug>(coc-classobj-i) :<C-U>call CocAction('selectSymbolRange', v:true, visualmode(), ['Interface', 'Struct', 'Class'])<CR>
vnoremap <silent> <Plug>(coc-classobj-a) :<C-U>call CocAction('selectSymbolRange', v:false, visualmode(), ['Interface', 'Struct', 'Class'])<CR>
onoremap <silent> <Plug>(coc-classobj-i) :<C-U>call CocAction('selectSymbolRange', v:true, '', ['Interface', 'Struct', 'Class'])<CR>
onoremap <silent> <Plug>(coc-classobj-a) :<C-U>call CocAction('selectSymbolRange', v:false, '', ['Interface', 'Struct', 'Class'])<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

62
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/changedFiles.test.ts

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
/* eslint-disable */
import helper from '../helper'
// import * as assert from 'assert'
import fs from 'fs'
import * as lsclient from '../../language-client'
import * as path from 'path'
import { URI } from 'vscode-uri'
// import which from 'which'
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('Client integration', () => {
it('should send file change notification', (done) => {
if (global.hasOwnProperty('__TEST__')) return done()
let serverModule = path.join(__dirname, './server/testFileWatcher.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.ipc
}
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: ['css'],
synchronize: {}, initializationOptions: {},
middleware: {
}
}
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
let disposable = client.start()
client.onReady().then(_ => {
setTimeout(async () => {
let file = path.join(__dirname, 'test.js')
fs.writeFileSync(file, '', 'utf8')
await helper.wait(300)
let res = await client.sendRequest('custom/received')
expect(res).toEqual({
changes: [{
uri: URI.file(file).toString(),
type: 1
}]
})
fs.unlinkSync(file)
disposable.dispose()
done()
}, 200)
}, e => {
disposable.dispose()
done(e)
})
})
})

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

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
import { Duplex } from 'stream'
import { ProgressType } from 'vscode-jsonrpc'
import { createProtocolConnection, 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 provid 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, params => {
expect(params.partialResultToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
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, params => {
expect(params.workDoneToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2')
serverConnection.sendProgress(progressType, params.workDoneToken, {
kind: 'begin',
title: 'progress'
})
serverConnection.sendProgress(progressType, params.workDoneToken, {
kind: 'report',
message: 'message'
})
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')
})
})

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

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
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 asLanguageIds', () => {
let selector = ['css', { language: 'javascript' }]
expect(cv.asLanguageIds(selector)).toEqual(['css', 'javascript'])
})
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 asChangeTextDocumentParams', () => {
let doc = createDocument()
expect(cv.asChangeTextDocumentParams(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()
})
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()
})
})

154
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/client/delayer.test.ts

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
/* eslint-disable */
import assert from 'assert'
import { Delayer } from '../../language-client/utils/async'
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 - simple cancel', async () => {
let count = 0
let factory = () => {
return Promise.resolve(++count)
}
let delayer = new Delayer(10)
assert(!delayer.isTriggered())
const p = delayer.trigger(factory).then(() => {
assert(false)
}, () => {
assert(true, 'yes, it was cancelled')
})
assert(delayer.isTriggered())
delayer.cancel()
assert(!delayer.isTriggered())
await p
})
test('Delayer - cancel should cancel all calls to trigger', function() {
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(null, () => { assert(true, 'yes, it was cancelled') }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') }))
assert(delayer.isTriggered())
delayer.cancel()
return Promise.all(promises).then(() => {
assert(!delayer.isTriggered())
})
})
test('Delayer - trigger, cancel, then trigger again', function() {
let count = 0
let factory = () => {
return Promise.resolve(++count)
}
let delayer = new Delayer(0)
let promises: Thenable<any>[] = []
assert(!delayer.isTriggered())
const p = delayer.trigger(factory).then((result) => {
assert.equal(result, 1)
assert(!delayer.isTriggered())
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') }))
assert(delayer.isTriggered())
delayer.cancel()
const p = Promise.all(promises).then(() => {
promises = []
assert(!delayer.isTriggered())
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()) }))
assert(delayer.isTriggered())
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()) }))
assert(delayer.isTriggered())
const p = Promise.all(promises).then(() => {
assert(!delayer.isTriggered())
})
assert(delayer.isTriggered())
return p
})
return p
})
assert(delayer.isTriggered())
return p
})
*/
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
})

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
/* eslint-disable */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict'
import helper from '../helper'
import * as assert from 'assert'
import * as lsclient from '../../language-client'
import path from 'path'
beforeAll(async () => {
await helper.setup()
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
async function testLanguageServer(serverOptions: lsclient.ServerOptions): Promise<void> {
let clientOptions: lsclient.LanguageClientOptions = {
documentSelector: ['css'],
synchronize: {},
initializationOptions: {}
}
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
client.start()
await client.onReady()
expect(client.initializeResult).toBeDefined()
}
describe('Client integration', () => {
it('should initialize use IPC channel', (done) => {
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 = {
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)
disposable.dispose()
done()
}
}
}
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions)
let disposable = client.start()
assert.equal(client.initializeResult, undefined)
client.onReady().then(_ => {
try {
let expected = {
capabilities: {
textDocumentSync: 1,
completionProvider: { resolveProvider: true, triggerCharacters: ['"', ':'] },
hoverProvider: true,
renameProvider: {
prepareProvider: true
}
},
customResults: {
"hello": "world"
}
}
assert.deepEqual(client.initializeResult, expected)
} catch (e) {
disposable.dispose()
done(e)
}
}, e => {
disposable.dispose()
done(e)
})
})
it('should initialize use stdio', async () => {
let serverModule = path.join(__dirname, './server/testInitializeResult.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.stdio
}
await testLanguageServer(serverOptions)
})
it('should initialize use pipe', async () => {
let serverModule = path.join(__dirname, './server/testInitializeResult.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: lsclient.TransportKind.pipe
}
await testLanguageServer(serverOptions)
})
it('should initialize use socket', async () => {
let serverModule = path.join(__dirname, './server/testInitializeResult.js')
let serverOptions: lsclient.ServerOptions = {
module: serverModule,
transport: {
kind: lsclient.TransportKind.socket,
port: 8088
}
}
await testLanguageServer(serverOptions)
})
it('should initialize as command', async () => {
let serverModule = path.join(__dirname, './server/testInitializeResult.js')
let serverOptions: lsclient.ServerOptions = {
command: 'node',
args: [serverModule, '--stdio']
}
await testLanguageServer(serverOptions)
})
})

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()

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

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'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.TextOnlyTransactional)
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: documents.syncKind,
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()

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

@ -0,0 +1,414 @@ @@ -0,0 +1,414 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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,
WorkDoneProgressCreateRequest} = require('vscode-languageserver')
const {
DidCreateFilesNotification,
DidRenameFilesNotification,
DidDeleteFilesNotification,
WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest
} = require('vscode-languageserver-protocol')
let connection = createConnection()
console.log = connection.console.log.bind(connection.console)
console.error = connection.console.error.bind(connection.console)
connection.onInitialize(params => {
assert.equal((params.capabilities.workspace).applyEdit, true)
assert.equal(params.capabilities.workspace.workspaceEdit.documentChanges, true)
assert.equal(params.capabilities.workspace.workspaceEdit.failureHandling, FailureHandlingKind.TextOnlyTransactional)
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)
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 capabilities = {
textDocumentSync: TextDocumentSyncKind.Full,
definitionProvider: true,
hoverProvider: true,
completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']},
signatureHelpProvider: {
triggerCharacters: [':'],
retriggerCharacters: [':']
},
referencesProvider: true,
documentHighlightProvider: true,
codeActionProvider: {
resolveProvider: true
},
documentFormattingProvider: true,
documentRangeFormattingProvider: true,
documentOnTypeFormattingProvider: {
firstTriggerCharacter: ':'
},
renameProvider: {
prepareProvider: true
},
documentLinkProvider: {
resolveProvider: true
},
colorProvider: true,
declarationProvider: true,
foldingRangeProvider: true,
implementationProvider: true,
selectionRangeProvider: true,
typeDefinitionProvider: true,
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
}
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.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) => {
return [
CodeAction.create('title', Command.create('title', 'id'))
]
})
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.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})
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'begin', title: 'Test Progress'})
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'report', percentage: 50, message: 'Halfway!'})
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'end', message: 'Completed!'})
},
)
// Listen on the connection
connection.listen()

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

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

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

@ -0,0 +1,446 @@ @@ -0,0 +1,446 @@
import { Neovim } from '@chemzqm/neovim'
import { ISource, SourceType, CompleteResult, CompleteOption } from '../../types'
import helper from '../helper'
import sources from '../../sources'
import { CancellationToken } from 'vscode-jsonrpc'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('completion', () => {
it('should not show word of word source on empty input', async () => {
await nvim.setLine('foo bar')
await helper.wait(200)
await nvim.input('of')
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
await nvim.input('<backspace>')
await helper.wait(200)
res = await helper.notVisible('foo')
expect(res).toBe(true)
})
it('should trigger on first letter insert', async () => {
await helper.edit()
await nvim.setLine('foo bar')
await helper.wait(30)
await nvim.input('of')
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
})
it('should trigger on force refresh', async () => {
await helper.edit()
await nvim.setLine('foo f')
await helper.wait(100)
await nvim.input('A')
await helper.wait(10)
await nvim.call('coc#start')
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
})
it('should filter and sort on increment search', async () => {
await helper.edit()
await nvim.setLine('forceDocumentSync format fallback')
await helper.wait(30)
await nvim.input('of')
await helper.waitPopup()
let items = await helper.getItems()
let l = items.length
await nvim.input('oa')
await helper.wait(100)
items = await helper.getItems()
expect(items.findIndex(o => o.word == 'fallback')).toBe(-1)
expect(items.length).toBeLessThan(l)
})
it('should filter on character remove by backspace', async () => {
await helper.edit()
await nvim.setLine('forceDocumentSync format fallback')
await helper.wait(30)
await nvim.input('ofa')
await helper.waitPopup()
let items = await helper.getItems()
let words = items.map(o => o.word)
expect(words).toContain('fallback')
expect(words).toContain('format')
await nvim.input('<backspace>')
await helper.wait(100)
items = await helper.getItems()
words = items.map(o => o.word)
expect(words).toEqual([])
})
it('should not trigger on insert enter', async () => {
await helper.edit()
await nvim.setLine('foo bar')
await helper.wait(30)
await nvim.input('o')
let visible = await nvim.call('pumvisible')
expect(visible).toBe(0)
})
it('should filter on fast input', async () => {
await helper.edit()
await nvim.setLine('foo bar')
await helper.wait(60)
await nvim.input('oba')
await helper.waitPopup()
let items = await helper.getItems()
let item = items.find(o => o.word == 'foo')
expect(item).toBeFalsy()
expect(items[0].word).toBe('bar')
})
it('should fix start column', async () => {
await helper.edit()
let source: ISource = {
name: 'test',
priority: 10,
enable: true,
firstMatch: false,
sourceType: SourceType.Native,
triggerCharacters: [],
doComplete: async (): Promise<CompleteResult> => {
let result: CompleteResult = {
startcol: 0,
items: [{ word: 'foo.bar' }]
}
return Promise.resolve(result)
}
}
let disposable = sources.addSource(source)
await nvim.setLine('foo.')
await nvim.input('Ab')
await helper.waitPopup()
let val = await nvim.getVar('coc#_context') as any
expect(val.start).toBe(0)
disposable.dispose()
})
it('should stop completion when type none trigger character', async () => {
await helper.edit()
let source: ISource = {
name: 'test',
priority: 10,
enable: true,
firstMatch: false,
sourceType: SourceType.Native,
triggerCharacters: [],
doComplete: async (): Promise<CompleteResult> => {
let result: CompleteResult = {
items: [{ word: 'if(' }]
}
return Promise.resolve(result)
}
}
let disposable = sources.addSource(source)
await nvim.setLine('')
await nvim.input('iif')
await helper.waitPopup()
await nvim.input('(')
await helper.wait(300)
let res = await helper.pumvisible()
expect(res).toBe(true)
disposable.dispose()
})
it('should trigger on triggerCharacters', async () => {
await helper.edit()
let source: ISource = {
name: 'trigger',
priority: 10,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo' }]
})
}
sources.addSource(source)
await nvim.input('i')
await helper.wait(30)
await nvim.input('.')
await helper.waitPopup()
sources.removeSource(source)
let res = await helper.visible('foo', 'trigger')
expect(res).toBe(true)
})
it('should should complete items without input', async () => {
await helper.edit()
let source: ISource = {
enable: true,
name: 'trigger',
priority: 10,
sourceType: SourceType.Native,
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo' }, { word: 'bar' }]
})
}
let disposable = sources.addSource(source)
await nvim.command('inoremap <silent><expr> <c-space> coc#refresh()')
await nvim.input('i')
await helper.wait(30)
await nvim.input('<c-space>')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBeGreaterThan(1)
disposable.dispose()
await helper.wait(300)
})
it('should show float window', async () => {
await helper.edit()
let source: ISource = {
name: 'float',
priority: 10,
enable: true,
sourceType: SourceType.Native,
doComplete: (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo', info: 'bar' }]
})
}
sources.addSource(source)
await nvim.input('i')
await helper.wait(30)
await nvim.input('f')
await helper.waitPopup()
await nvim.eval('feedkeys("\\<down>","in")')
await helper.wait(800)
let hasFloat = await nvim.call('coc#float#has_float')
expect(hasFloat).toBe(1)
sources.removeSource(source)
let res = await helper.visible('foo', 'float')
expect(res).toBe(true)
})
it('should trigger on triggerPatterns', async () => {
await helper.edit()
let source: ISource = {
name: 'pattern',
priority: 10,
enable: true,
sourceType: SourceType.Native,
triggerPatterns: [/\w+\.$/],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo' }]
})
}
sources.addSource(source)
await nvim.input('i')
await helper.wait(10)
await nvim.input('.')
await helper.wait(30)
let pumvisible = await nvim.call('pumvisible')
expect(pumvisible).toBe(0)
await nvim.input('a')
await helper.wait(30)
await nvim.input('.')
await helper.waitPopup()
sources.removeSource(source)
let res = await helper.visible('foo', 'pattern')
expect(res).toBe(true)
})
it('should not trigger triggerOnly source', async () => {
await helper.edit()
await nvim.setLine('foo bar')
let source: ISource = {
name: 'pattern',
triggerOnly: true,
priority: 10,
enable: true,
sourceType: SourceType.Native,
triggerPatterns: [/^From:\s*/],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo' }]
})
}
let disposable = sources.addSource(source)
await nvim.input('o')
await helper.wait(10)
await nvim.input('f')
await helper.wait(10)
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
let items = await helper.items()
expect(items.length).toBe(1)
disposable.dispose()
})
it('should not trigger when cursor moved', async () => {
await helper.edit()
let source: ISource = {
name: 'trigger',
priority: 10,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [{ word: 'foo' }]
})
}
sources.addSource(source)
await nvim.setLine('.a')
await nvim.input('A')
await nvim.eval('feedkeys("\\<bs>")')
await helper.wait(10)
await nvim.eval('feedkeys("\\<left>")')
await helper.wait(200)
let visible = await nvim.call('pumvisible')
expect(visible).toBe(0)
sources.removeSource(source)
})
it('should trigger when completion is not completed', async () => {
await helper.edit()
let token: CancellationToken
let source: ISource = {
name: 'completion',
priority: 10,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (opt, cancellationToken): Promise<CompleteResult> => {
if (opt.triggerCharacter != '.') {
token = cancellationToken
return new Promise<CompleteResult>((resolve, reject) => {
let timer = setTimeout(() => {
resolve({ items: [{ word: 'foo' }] })
}, 200)
if (cancellationToken.isCancellationRequested) {
clearTimeout(timer)
reject(new Error('Cancelled'))
}
})
}
return Promise.resolve({
items: [{ word: 'bar' }]
})
}
}
let disposable = sources.addSource(source)
await nvim.input('if')
await helper.wait(100)
await nvim.input('.')
await helper.visible('bar', 'completion')
expect(token.isCancellationRequested).toBe(true)
disposable.dispose()
})
it('should limit results for low priority source', async () => {
helper.updateConfiguration('suggest.lowPrioritySourceLimit', 2)
await nvim.setLine('filename filepath find filter findIndex')
await helper.wait(200)
await nvim.input('of')
await helper.waitPopup()
let items = await helper.getItems()
items = items.filter(o => o.menu == '[A]')
expect(items.length).toBe(2)
})
it('should limit result for high priority source', async () => {
helper.updateConfiguration('suggest.highPrioritySourceLimit', 2)
await helper.edit()
let source: ISource = {
name: 'high',
priority: 90,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: ['filename', 'filepath', 'filter', 'file'].map(key => ({ word: key }))
})
}
let disposable = sources.addSource(source)
await nvim.input('i')
await helper.wait(30)
await nvim.input('.')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBeGreaterThan(1)
disposable.dispose()
})
it('should truncate label of complete items', async () => {
helper.updateConfiguration('suggest.labelMaxLength', 10)
await helper.edit()
let source: ISource = {
name: 'high',
priority: 90,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: ['a', 'b', 'c', 'd'].map(key => ({ word: key.repeat(20) }))
})
}
let disposable = sources.addSource(source)
await nvim.input('i')
await helper.wait(30)
await nvim.input('.')
await helper.waitPopup()
let items = await helper.getItems()
for (let item of items) {
expect(item.abbr.length).toBeLessThanOrEqual(10)
}
disposable.dispose()
})
it('should delete previous items if complete item is null', async () => {
await helper.edit()
let source1: ISource = {
name: 'source1',
priority: 90,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (): Promise<CompleteResult> => Promise.resolve({
items: [ {word: 'foo', dup: 1} ]
})
}
let source2: ISource = {
name: 'source2',
priority: 90,
enable: true,
sourceType: SourceType.Native,
triggerCharacters: ['.'],
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => {
let result: CompleteResult = opt.input == 'foo' ? null : {
items: [{ word: 'foo', dup: 1 }], isIncomplete: true
}
return Promise.resolve(result)
}
}
let disposable1 = sources.addSource(source1)
let disposable2 = sources.addSource(source2)
await nvim.input('i')
await helper.wait(30)
await nvim.input('.f')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toEqual(2)
await nvim.input('oo')
await helper.waitPopup()
items = await helper.getItems()
expect(items.length).toEqual(1)
expect(items[0].word).toBe('foo')
disposable1.dispose()
disposable2.dispose()
})
})

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

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
import { Neovim } from '@chemzqm/neovim'
import sources from '../../sources'
import { CompleteResult, 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 not show float window when disabled', async () => {
helper.updateConfiguration('suggest.floatEnable', false)
await helper.edit()
await nvim.input('if')
await helper.visible('foo', 'float')
helper.updateConfiguration('suggest.floatEnable', true)
let hasFloat = await nvim.call('coc#float#has_float')
expect(hasFloat).toBe(0)
})
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 nvim.input('<C-n>')
await helper.wait(500)
await nvim.input('<esc>')
await helper.wait(100)
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')
await nvim.input('<C-n>')
await helper.wait(300)
let floatWin = await helper.getFloat()
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.input('<C-n>')
await helper.wait(50)
await nvim.input('<C-n>')
await helper.wait(300)
let floatWin = await helper.getFloat()
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.input('<C-n>')
await helper.wait(10)
await nvim.input('<C-n>')
await helper.wait(10)
await nvim.input('<C-n>')
await helper.wait(100)
let hasFloat = await nvim.call('coc#float#has_float')
expect(hasFloat).toBe(0)
})
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(100)
await nvim.input('<C-y>')
await helper.wait(30)
let hasFloat = await nvim.call('coc#float#has_float')
expect(hasFloat).toBe(0)
})
})

66
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/completion/match.test.ts

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
import { matchScore } from '../../completion/match'
import { getCharCodes } from '../../util/fuzzy'
function score(word: string, input: string): number {
return matchScore(word, getCharCodes(input))
}
describe('matchScore', () => {
it('should match score for last letter', () => {
expect(score('#!3', '3')).toBe(1)
})
it('should match first letter', () => {
expect(score('abc', 'a')).toBe(5)
expect(score('Abc', 'a')).toBe(2.5)
expect(score('__abc', 'a')).toBe(2.5)
expect(score('$Abc', 'a')).toBe(2)
expect(score('$Abc', 'A')).toBe(2.5)
expect(score('$Abc', '$A')).toBe(6)
expect(score('$Abc', '$a')).toBe(5.5)
expect(score('foo_bar', 'b')).toBe(2.5)
expect(score('foo_Bar', 'b')).toBe(2)
expect(score('_foo_Bar', 'b')).toBe(0.5)
expect(score('_foo_Bar', 'f')).toBe(2.5)
expect(score('bar', 'a')).toBe(1)
expect(score('fooBar', 'B')).toBe(2.5)
expect(score('fooBar', 'b')).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(0)
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(4)
})
})

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

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
import { Neovim } from '@chemzqm/neovim'
import helper from '../helper'
import { ISource, SourceType, CompleteResult } from '../../types'
import sources from '../../sources'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('native sources', () => {
it('should works for around source', async () => {
await helper.createDocument()
await nvim.setLine('foo ')
await helper.wait(100)
let { mode } = await nvim.mode
expect(mode).toBe('n')
await nvim.input('Af')
let res = await helper.visible('foo', 'around')
expect(res).toBe(true)
await nvim.input('<esc>')
})
it('should works for buffer source', async () => {
await nvim.command('set hidden')
await helper.createDocument()
await helper.createDocument()
await nvim.setLine('other')
await nvim.command('bp')
await helper.wait(300)
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 for file source', async () => {
await helper.edit()
await nvim.input('i/')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBeGreaterThan(0)
let res = await helper.visible(items[0].word, 'file')
expect(res).toBe(true)
await nvim.input('<esc>')
await nvim.input('o./')
await helper.waitPopup()
items = await helper.getItems()
let item = items.find(o => o.word == 'vimrc')
expect(item).toBeTruthy()
})
it('should works for file source with other source use same triggerCharacter', async () => {
await helper.edit()
let source: ISource = {
name: 'test',
priority: 50,
enable: true,
firstMatch: false,
sourceType: SourceType.Native,
triggerCharacters: ['.', '/'],
doComplete: async (): Promise<CompleteResult> => {
let result: CompleteResult = {
items: [{ word: 'foo' }]
}
return Promise.resolve(result)
}
}
let disposable = sources.addSource(source)
await nvim.input('i.')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBe(1)
await nvim.input('/')
await helper.waitPopup()
items = await helper.getItems()
expect(items.length).toBeGreaterThan(1)
expect(items[0].word).toBe('foo')
disposable.dispose()
})
})

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

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
import { CompletionItemKind, TextEdit, Position } from 'vscode-languageserver-types'
import { getStartColumn, getKindString } from '../../sources/source-language'
describe('getKindString()', () => {
it('should get kind text', async () => {
let map = new Map()
map.set(CompletionItemKind.Enum, 'E')
let res = getKindString(CompletionItemKind.Enum, map, '')
expect(res).toBe('E')
})
it('should get default value', async () => {
let map = new Map()
let res = getKindString(CompletionItemKind.Enum, map, 'D')
expect(res).toBe('D')
})
})
describe('getStartColumn()', () => {
it('should get start col', async () => {
expect(getStartColumn('', [{ label: 'foo' }])).toBe(null)
expect(getStartColumn('', [
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') },
{ label: 'bar' }])).toBe(null)
expect(getStartColumn('foo', [
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') },
{ label: 'bar', textEdit: TextEdit.insert(Position.create(0, 1), 'b') }])).toBe(null)
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)
})
})

7
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/global/index.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
exports.activate = async context => {
return {
getContext: () => {
return context
}
}
}

7
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/global/package.json

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{
"name": "global",
"version": "1.0.0",
"engines": {
"coc": "^0.0.46"
}
}

6
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/package.json

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
{
"dependencies": {
"global": ">=1.0.0",
"test": ">=1.0.0"
}
}

7
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/root.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
exports.activate = context => {
return {
root: () => {
return context.extensionPath
}
}
}

13
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/test/index.js

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
exports.activate = async context => {
return {
asAbsolutePath: p => {
return context.asAbsolutePath(p)
},
getContext: () => {
return context
},
echo: x => {
return x
}
}
}

33
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/test/package.json

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
{
"name": "test",
"version": "1.0.0",
"engines": {
"coc": "^0.0.46"
},
"contributes": {
"rootPatterns": [
{
"filetype": "javascript",
"patterns": [
"package.json",
"jsconfig.json"
]
}
],
"commands": [
{
"title": "Test",
"command": "test.run"
}
],
"configuration": {
"properties": {
"test.enable": {
"type": "boolean",
"default": true,
"description": "Enable test"
}
}
}
}
}

7
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/vim/local/index.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
exports.activate = async context => {
return {
getContext: () => {
return context
}
}
}

7
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/extensions/vim/local/package.json

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{
"name": "local",
"version": "1.0.0",
"engines": {
"coc": "^0.0.46"
}
}

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

@ -0,0 +1,398 @@ @@ -0,0 +1,398 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, CallHierarchyItem, SymbolKind, Range, SymbolTag } 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()
})
beforeEach(async () => {
await helper.createDocument()
})
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 for when provider not exists', async () => {
let err
try {
await callHierarchy.getIncoming()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should get undefined when prepare failed', async () => {
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return undefined
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
return []
}
}))
let res = await callHierarchy.getOutgoing()
expect(res).toBeUndefined()
})
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 message when provider not exists', async () => {
await callHierarchy.showCallHierarchyTree('incoming')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines[0]).toMatch('callHierarchy provider not found')
await nvim.command('wincmd p')
})
it('should no results when no result returned.', async () => {
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], {
prepareCallHierarchy() {
return []
},
provideCallHierarchyIncomingCalls() {
return []
},
provideCallHierarchyOutgoingCalls() {
return []
}
}))
await callHierarchy.showCallHierarchyTree('incoming')
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines[0]).toBe('No results')
await nvim.command('wincmd p')
})
it('should render description and support default action', async () => {
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.wait(50)
let line = await nvim.line
expect(line).toEqual(' - c bar Detail')
await nvim.input('<cr>')
await helper.wait(50)
doc = await workspace.document
expect(doc.uri).toBe(uri)
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`)
await helper.wait(50)
})
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
let tab = await nvim.call('tabpagenr')
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.wait(50)
await nvim.input('<cr>')
await helper.wait(200)
let newTab = await nvim.call('tabpagenr')
expect(newTab != tab).toBe(true)
doc = await workspace.document
expect(doc.uri).toBe(uri)
let res = await nvim.call('getmatches', [win.id])
expect(res.length).toBe(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.wait(50)
await nvim.input('2')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'INCOMING CALLS',
'- c bar Detail',
' + c test'
])
await nvim.command('bd!')
})
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.wait(50)
await nvim.input('3')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c test',
' + c bar Detail'
])
await nvim.command('bd!')
})
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.wait(50)
await nvim.input('4')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo'
])
await nvim.command('wincmd c')
})
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.wait(50)
await nvim.command('exe 4')
await nvim.input('<tab>')
await helper.wait(50)
await nvim.input('4')
await helper.wait(200)
lines = await buf.lines
expect(lines).toEqual([
'OUTGOING CALLS',
'- c foo',
' - c bar Detail'
])
await nvim.command('wincmd c')
})
})

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

@ -0,0 +1,386 @@ @@ -0,0 +1,386 @@
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 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 choosen 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.wait(50)
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 filter disabled actions', async () => {
currActions = []
let action = CodeAction.create('foo', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
action = CodeAction.create('foo', CodeActionKind.QuickFix)
action.disabled = { reason: 'disabled' }
currActions.push(action)
let doc = await helper.createDocument()
let res = await codeActions.getCodeActions(doc)
expect(res.length).toBe(0)
})
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))
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, 4))
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 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: 4 } })
})
})
describe('doQuickfix', () => {
it('should throw when quickfix action not exists', async () => {
let err
currActions = []
await helper.createDocument()
try {
await codeActions.doQuickfix()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
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 = CodeAction.create('my action', CodeActionKind.Empty)
action.disabled = { reason: 'disabled' }
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! $')
})
})
})

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

@ -0,0 +1,269 @@ @@ -0,0 +1,269 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Range, Command, TextEdit, Position } from 'vscode-languageserver-protocol'
import { disposeAll } from '../../util'
import languages from '../../languages'
import commands from '../../commands'
import CodeLens from '../../handler/codelens/index'
import helper, { createTmpFile } from '../helper'
import events from '../../events'
let nvim: Neovim
let codeLens: CodeLens
let disposables: Disposable[] = []
let srcId: number
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
srcId = await nvim.createNamespace('coc-codelens')
codeLens = helper.plugin.getHandler().codeLens
helper.updateConfiguration('codeLens.enable', true)
})
afterAll(async () => {
helper.updateConfiguration('codeLens.enable', false)
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('codeLenes featrue', () => {
it('should do codeLenes request and resolve codeLenes', async () => {
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1)
}, {
range: Range.create(1, 0, 1, 1)
}]
},
resolveCodeLens: codeLens => {
codeLens.command = Command.create('save', '__save')
return codeLens
}
}))
let doc = await helper.createDocument('example.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await codeLens.checkProvider()
let buf = codeLens.buffers.getItem(doc.bufnr)
let codelens = buf.getCodelenses()
expect(codelens).toBeDefined()
expect(codelens[0].command).toBeDefined()
expect(codelens[1].command).toBeDefined()
let markers = await helper.getMarkers(doc.bufnr, srcId)
expect(markers.length).toBe(2)
})
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)
})
},
resolveCodeLens: codeLens => {
codeLens.command = Command.create('save', '__save')
return codeLens
}
}))
let doc = await helper.createDocument('codelens.js')
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'a\nb\nc')])
let p = codeLens.checkProvider()
await doc.applyEdits([TextEdit.replace(Range.create(0, 0, 0, 1), 'foo')])
await p
expect(cancelled).toBe(true)
let buf = codeLens.buffers.getItem(doc.bufnr)
let codelens = buf.getCodelenses()
expect(codelens).toBeUndefined()
})
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 => {
await helper.wait(50)
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 helper.wait(300)
await nvim.command('normal! G')
await helper.wait(300)
let buf = codeLens.buffers.getItem(doc.bufnr)
let codelens = buf.getCodelenses()
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)
}))
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
}
}))
await helper.createDocument('example.js')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await codeLens.checkProvider()
await helper.doAction('codeLensAction')
expect(fn).toBeCalledWith(1, 2, 3)
})
it('should use picker fo 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.wait(30)
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 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)
expect(called).toBe(2)
})
it('should refresh on configuration change', async () => {
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], {
provideCodeLenses: () => {
return [{
range: Range.create(0, 0, 0, 1),
command: Command.create('save', '__save')
}]
}
}))
let filepath = await createTmpFile('abc')
let buffer = await helper.edit(filepath)
await codeLens.checkProvider()
helper.updateConfiguration('codeLens.enable', false)
await helper.wait(10)
let markers = await helper.getMarkers(buffer.id, srcId)
expect(markers.length).toBe(0)
helper.updateConfiguration('codeLens.enable', true)
await helper.wait(300)
markers = await helper.getMarkers(buffer.id, srcId)
expect(markers.length).toBeGreaterThan(0)
})
})

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

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
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 helper from '../helper'
let nvim: Neovim
let state = 'normal'
let colors: Colors
let disposables: Disposable[] = []
let colorPresentations: ColorPresentation[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
colors = helper.plugin.getHandler().colors
disposables.push(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)
}
})
}
}))
})
afterAll(async () => {
disposeAll(disposables)
await helper.shutdown()
})
afterEach(async () => {
colorPresentations = []
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 () => {
await helper.createDocument()
helper.updateConfiguration('coc.preferences.colorSupport', false)
expect(colors.enabled).toBe(false)
helper.updateConfiguration('coc.preferences.colorSupport', true)
expect(colors.enabled).toBe(true)
})
})
describe('commands', () => {
it('should register editor.action.pickColor command', async () => {
await helper.mockFunction('coc#util#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')
doc.forceSync()
await colors.doHighlight(doc.bufnr)
let p = commands.executeCommand('editor.action.colorPresentation')
await helper.wait(100)
await nvim.input('1<enter>')
await p
let line = await nvim.getLine()
expect(line).toBe('red')
})
})
describe('doHighlight', () => {
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)
state = 'normal'
})
it('should not highlight on error result', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff')
state = 'error'
await colors.doHighlight(doc.bufnr)
let res = colors.hasColor(doc.bufnr)
expect(res).toBe(false)
state = 'normal'
})
it('should highlight after document changed', async () => {
let doc = await helper.createDocument()
doc.forceSync()
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')
doc.forceSync()
await helper.wait(300)
expect(colors.hasColorAtPosition(doc.bufnr, Position.create(0, 1))).toBe(true)
expect(colors.hasColor(doc.bufnr)).toBe(true)
})
it('should clearHighlight on clearHighlight', async () => {
let doc = await helper.createDocument()
await nvim.setLine('#ffffff #ff0000')
doc.forceSync()
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 not exists', async () => {
let res = colors.hasColor(99)
colors.clearHighlight(99)
expect(res).toBe(false)
})
})
describe('getColorInformation()', () => {
it('should return null when highlighter not exists', 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 not exists', async () => {
let res = colors.hasColorAtPosition(99, Position.create(0, 0))
expect(res).toBe(false)
})
})
describe('pickPresentation()', () => {
it('should show warning when color not exists', async () => {
await helper.createDocument()
await colors.pickPresentation()
let msg = await helper.getCmdline()
expect(msg).toMatch('Color not found')
})
it('should not throw when presentations not exists', 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.wait(100)
await nvim.input('1<enter>')
await p
let line = await nvim.getLine()
expect(line).toBe('red')
})
})
describe('pickColor()', () => {
it('should show warning when color not exists', 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#util#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#util#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')
})
})
})

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

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, FoldingRange } from 'vscode-languageserver-protocol'
import FoldHandler from '../../handler/fold'
import languages from '../../languages'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let folds: FoldHandler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
folds = (helper.plugin as any).handler.fold
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('Folds', () => {
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 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)
})
})

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

@ -0,0 +1,253 @@ @@ -0,0 +1,253 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import languages from '../../languages'
import { disposeAll } from '../../util'
import window from '../../window'
import workspace from '../../workspace'
import Format from '../../handler/format'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let format: Format
beforeAll(async () => {
let { configurations } = workspace
configurations.updateUserConfig({ 'coc.preferences.formatOnType': true })
await helper.setup()
nvim = helper.nvim
format = helper.plugin.getHandler().format
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', [])
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('format handler', () => {
describe('documentFormat', () => {
it('should throw when provider not found', async () => {
let doc = await helper.createDocument()
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)
})
})
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')
await helper.wait(100)
})
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', ['*'])
disposables.push(languages.registerDocumentFormatProvider(['*'], {
provideDocumentFormattingEdits: () => {
return new Promise(resolve => {
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)
})
})
describe('rangeFormat', () => {
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), ' ')
})
}
}))
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>')
await helper.doAction('formatSelected', 'v')
let buf = nvim.createBuffer(doc.bufnr)
let lines = await buf.lines
expect(lines).toEqual([' a', ' b', ' c'])
})
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 () => {
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], {
provideOnTypeFormattingEdits: () => {
return [TextEdit.insert(Position.create(0, 0), ' ')]
}
}, ['|']))
await helper.edit()
await nvim.command('setf text')
await nvim.input('i|')
await helper.wait(200)
let line = await nvim.line
expect(line).toBe(' |')
let cursor = await window.getCursorPosition()
expect(cursor).toEqual({ line: 0, character: 3 })
})
it('should format on new line inserted', async () => {
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], {
provideOnTypeFormattingEdits: (doc, position) => {
let text = doc.getText()
if (text.startsWith(' ')) return []
return [TextEdit.insert(Position.create(position.line, 0), ' ')]
}
}, ['\n']))
let buf = await helper.edit()
await nvim.command('setf text')
await nvim.setLine('foo')
await nvim.input('o')
await helper.wait(100)
let lines = await buf.lines
expect(lines).toEqual([' foo', ''])
})
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')
]
}
}, ['|']))
await helper.edit()
await nvim.command('setf text')
await nvim.setLine('"')
await nvim.input('i|')
await helper.wait(100)
let line = await nvim.line
expect(line).toBe(' |"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.wait(100)
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.wait(100)
let lines = await buf.lines
expect(lines).toEqual([' {', ' ', ' }'])
})
})
})

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

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, DocumentHighlightKind, Position, Range } 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 registProvider(): 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', () => {
it('should return null when highlights provide not exists', async () => {
let doc = await helper.createDocument()
let res = await highlights.getHighlights(doc, Position.create(0, 0))
expect(res).toBeNull()
})
it('should cancel request on CursorMoved', async () => {
let fn = jest.fn()
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) }])
}, 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 add highlights to symbols', async () => {
registProvider()
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 () => {
registProvider()
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)
let err
try {
await highlights.highlight()
} catch (e) {
err = e
}
await nvim.input('<C-c>')
expect(err).toBeUndefined()
})
it('should not throw when provider not found', async () => {
disposeAll(disposables)
await helper.createDocument()
await nvim.setLine(' oo')
await nvim.call('cursor', [1, 2])
let err
try {
await highlights.highlight()
} catch (e) {
err = e
}
expect(err).toBeUndefined()
})
})

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

@ -0,0 +1,178 @@ @@ -0,0 +1,178 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, MarkedString, Hover, Range, TextEdit, Position } from 'vscode-languageserver-protocol'
import HoverHandler from '../../handler/hover'
import { URI } from 'vscode-uri'
import languages from '../../languages'
import { disposeAll } from '../../util'
import helper, { createTmpFile } from '../helper'
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('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 () => {
hoverResult = { contents: { kind: 'plaintext', value: 'my hover' } }
await hover.onHover('preview')
let res = await getDocumentText()
expect(res).toMatch('my hover')
})
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: [''] }
let res = await hover.getHover()
expect(res.length).toBe(0)
})
})
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' }
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')
})
})
})

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)
})
})
})

99
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/links.test.ts

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, DocumentLink, Range } from 'vscode-languageserver-protocol'
import LinksHandler from '../../handler/links'
import languages from '../../languages'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let links: LinksHandler
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
links = (helper.plugin as any).handler.links
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('Links', () => {
it('should get document links', async () => {
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], {
provideDocumentLinks: (_doc, _token) => {
return [
DocumentLink.create(Range.create(0, 0, 0, 5), 'test:///foo'),
DocumentLink.create(Range.create(1, 0, 1, 5), 'test:///bar')
]
}
}))
let res = await links.getLinks()
expect(res.length).toBe(2)
})
it('should throw error when link target not resolved', async () => {
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], {
provideDocumentLinks(_doc, _token) {
return [
DocumentLink.create(Range.create(0, 0, 0, 5))
]
},
resolveDocumentLink(link) {
return link
}
}))
let res = await links.getLinks()
let err
try {
await links.openLink(res[0])
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should open link at current position', async () => {
await nvim.setLine('foo')
await nvim.command('normal! 0')
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], {
provideDocumentLinks(_doc, _token) {
return [
DocumentLink.create(Range.create(0, 0, 0, 5)),
]
},
resolveDocumentLink(link) {
link.target = 'test:///foo'
return link
}
}))
await links.openCurrentLink()
let bufname = await nvim.call('bufname', '%')
expect(bufname).toBe('test:///foo')
await nvim.call('setline', [1, ['a', 'b', 'c']])
await nvim.call('cursor', [3, 1])
let res = await links.openCurrentLink()
expect(res).toBe(false)
})
it('should return false when current links not found', async () => {
await nvim.setLine('foo')
await nvim.command('normal! 0')
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], {
provideDocumentLinks(_doc, _token) {
return []
}
}))
let res = await links.openCurrentLink()
expect(res).toBe(false)
})
})

306
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/locations.test.ts

@ -0,0 +1,306 @@ @@ -0,0 +1,306 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, LocationLink, Location, Range } from 'vscode-languageserver-protocol'
import LocationHandler from '../../handler/locations'
import languages from '../../languages'
import services from '../../services'
import workspace from '../../workspace'
import { disposeAll } from '../../util'
import helper from '../helper'
import { URI } from 'vscode-uri'
let nvim: Neovim
let locations: LocationHandler
let disposables: Disposable[] = []
let currLocations: Location[]
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
Object.assign(workspace.env, {
locationlist: false
})
locations = helper.plugin.getHandler().locations
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
await helper.createDocument()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
function createLocation(name: string, sl: number, sc: number, el: number, ec: number): Location {
return Location.create(`test://${name}`, Range.create(sl, sc, el, ec))
}
describe('locations', () => {
describe('reference', () => {
beforeEach(() => {
disposables.push(languages.registerReferencesProvider([{ language: '*' }], {
provideReferences: () => {
return currLocations
}
}))
})
it('should get references', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)]
let res = await locations.references()
expect(res.length).toBe(2)
})
it('should jump to references', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0)]
let res = await locations.gotoReferences('edit', true)
expect(res).toBe(true)
let name = await nvim.call('bufname', ['%'])
expect(name).toBe('test://foo')
})
it('should return false when references not found', async () => {
currLocations = []
let res = await locations.gotoReferences('edit', true)
expect(res).toBe(false)
})
})
describe('definition', () => {
beforeEach(() => {
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], {
provideDefinition: () => {
return currLocations
}
}))
})
it('should get definitions', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)]
let res = await locations.definitions()
expect(res.length).toBe(2)
})
it('should jump to definitions', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0)]
let res = await locations.gotoDefinition('edit')
expect(res).toBe(true)
let name = await nvim.call('bufname', ['%'])
expect(name).toBe('test://foo')
})
it('should return false when definitions not found', async () => {
currLocations = []
let res = await locations.gotoDefinition('edit')
expect(res).toBe(false)
})
})
describe('declaration', () => {
beforeEach(() => {
disposables.push(languages.registerDeclarationProvider([{ language: '*' }], {
provideDeclaration: () => {
return currLocations
}
}))
})
it('should get declarations', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)]
let res = await locations.declarations() as Location[]
expect(res.length).toBe(2)
})
it('should jump to declaration', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0)]
let res = await locations.gotoDeclaration('edit')
expect(res).toBe(true)
let name = await nvim.call('bufname', ['%'])
expect(name).toBe('test://foo')
})
it('should return false when declaration not found', async () => {
currLocations = []
let res = await locations.gotoDeclaration('edit')
expect(res).toBe(false)
})
})
describe('typeDefinition', () => {
beforeEach(() => {
disposables.push(languages.registerTypeDefinitionProvider([{ language: '*' }], {
provideTypeDefinition: () => {
return currLocations
}
}))
})
it('should get type definition', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)]
let res = await locations.typeDefinitions() as Location[]
expect(res.length).toBe(2)
})
it('should jump to type definition', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0)]
let res = await locations.gotoTypeDefinition('edit')
expect(res).toBe(true)
let name = await nvim.call('bufname', ['%'])
expect(name).toBe('test://foo')
})
it('should return false when type definition not found', async () => {
currLocations = []
let res = await locations.gotoTypeDefinition('edit')
expect(res).toBe(false)
})
})
describe('implementation', () => {
beforeEach(() => {
disposables.push(languages.registerImplementationProvider([{ language: '*' }], {
provideImplementation: () => {
return currLocations
}
}))
})
it('should get implementations', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)]
let res = await locations.implementations() as Location[]
expect(res.length).toBe(2)
})
it('should jump to implementation', async () => {
currLocations = [createLocation('foo', 0, 0, 0, 0)]
let res = await locations.gotoImplementation('edit')
expect(res).toBe(true)
let name = await nvim.call('bufname', ['%'])
expect(name).toBe('test://foo')
})
it('should return false when implementation not found', async () => {
currLocations = []
let res = await locations.gotoImplementation('edit')
expect(res).toBe(false)
})
})
describe('getTagList', () => {
it('should return null when cword not exists', async () => {
let res = await locations.getTagList()
expect(res).toBe(null)
})
it('should return null when provider not exists', async () => {
await nvim.setLine('foo')
await nvim.command('normal! ^')
let res = await locations.getTagList()
expect(res).toBe(null)
})
it('should return null when result is empty', async () => {
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], {
provideDefinition: () => {
return []
}
}))
await nvim.setLine('foo')
await nvim.command('normal! ^')
let res = await locations.getTagList()
expect(res).toBe(null)
})
it('should return tag definitions', async () => {
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], {
provideDefinition: () => {
return [createLocation('bar', 2, 0, 2, 5), Location.create(URI.file('/foo').toString(), Range.create(1, 0, 1, 5))]
}
}))
await nvim.setLine('foo')
await nvim.command('normal! ^')
let res = await locations.getTagList()
expect(res).toEqual([
{
name: 'foo',
cmd: 'keepjumps 3 | normal 1|',
filename: 'test://bar'
},
{ name: 'foo', cmd: 'keepjumps 2 | normal 1|', filename: '/foo' }
])
})
})
describe('findLocations', () => {
// hook result
let fn
let result: any
beforeAll(() => {
fn = services.sendRequest
services.sendRequest = () => {
return Promise.resolve(result)
}
})
afterAll(() => {
services.sendRequest = fn
})
it('should handle locations from language client', async () => {
result = [createLocation('bar', 2, 0, 2, 5)]
await locations.findLocations('foo', 'mylocation', {}, false)
let res = await nvim.getVar('coc_jump_locations')
expect(res).toEqual([{
uri: 'test://bar',
lnum: 3,
col: 1,
filename: 'test://bar',
text: '',
range: Range.create(2, 0, 2, 5)
}])
})
it('should handle nested locations', async () => {
let location: any = {
location: createLocation('file', 0, 0, 0, 0),
children: [{
location: createLocation('foo', 3, 0, 3, 5),
children: []
}, {
location: createLocation('bar', 4, 0, 4, 5),
children: []
}]
}
result = location
await locations.findLocations('foo', 'mylocation', {}, false)
let res = await nvim.getVar('coc_jump_locations') as any[]
expect(res.length).toBe(3)
})
})
describe('handleLocations', () => {
it('should not throw when location is undefined', async () => {
await locations.handleLocations(null)
})
it('should not throw when locations is empty array', async () => {
await locations.handleLocations([])
})
it('should handle single location', async () => {
await locations.handleLocations(createLocation('single', 0, 0, 0, 0))
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toBe('test://single')
})
it('should handle location link', async () => {
let link = LocationLink.create('test://link', Range.create(0, 0, 0, 3), Range.create(1, 0, 1, 3))
await locations.handleLocations([link])
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toBe('test://link')
})
})
})

426
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/outline.test.ts

@ -0,0 +1,426 @@ @@ -0,0 +1,426 @@
import { Buffer, Neovim } from '@chemzqm/neovim'
import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Disposable, Range, SymbolTag, TextEdit } from 'vscode-languageserver-protocol'
import events from '../../events'
import Symbols from '../../handler/symbols/index'
import languages from '../../languages'
import { ProviderResult } from '../../provider'
import { disposeAll } from '../../util'
import workspace from '../../workspace'
import helper from '../helper'
import Parser from './parser'
let nvim: Neovim
let symbols: Symbols
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
symbols = helper.plugin.getHandler().symbols
})
beforeEach(() => {
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'javascript' }], {
provideDocumentSymbols: document => {
let parser = new Parser(document.getText())
let res = parser.parse()
if (res.length) {
res[0].tags = [SymbolTag.Deprecated]
}
return Promise.resolve(res)
}
}))
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
async function getOutlineBuffer(): Promise<Buffer | undefined> {
let winid = await nvim.call('coc#window#find', ['cocViewId', 'OUTLINE'])
if (winid == -1) return undefined
let bufnr = await nvim.call('winbufnr', [winid])
if (bufnr == -1) return undefined
return nvim.createBuffer(bufnr)
}
describe('symbols outline', () => {
let defaultCode = `class myClass {
fun1() { }
fun2() {}
}`
async function createBuffer(code = defaultCode): Promise<Buffer> {
await helper.edit()
let buf = await nvim.buffer
await nvim.command('setf javascript')
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false })
let doc = await workspace.document
doc.forceSync()
return buf
}
describe('configuration', () => {
afterEach(() => {
let { configurations } = workspace
configurations.updateUserConfig({
'outline.splitCommand': 'botright 30vs',
'outline.followCursor': true,
'outline.keepWindow': false,
'outline.sortBy': 'category',
'outline.expandLevel': 1,
'outline.checkBufferSwitch': true
})
})
it('should follow cursor', async () => {
await createBuffer()
let curr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
let bufnr = await nvim.call('bufnr', ['%'])
await nvim.command('wincmd p')
await nvim.command('exe 3')
await events.fire('CursorHold', [curr])
await helper.wait(50)
let buf = nvim.createBuffer(bufnr)
let lines = await buf.getLines()
expect(lines).toEqual([
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3'
])
let signs = await buf.getSigns({ group: 'CocTree' })
expect(signs.length).toBe(1)
expect(signs[0]).toEqual({
lnum: 2,
id: 3001,
name: 'CocTreeSelected',
priority: 10,
group: 'CocTree'
})
})
it('should not follow cursor', async () => {
workspace.configurations.updateUserConfig({
'outline.followCursor': false,
})
await createBuffer()
let curr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
let bufnr = await nvim.call('bufnr', ['%'])
await nvim.command('wincmd p')
await nvim.command('exe 3')
await events.fire('CursorHold', [curr])
await helper.wait(50)
let buf = nvim.createBuffer(bufnr)
let signs = await buf.getSigns({ group: 'CocTree' })
expect(signs.length).toBe(0)
})
it('should keep current window', async () => {
workspace.configurations.updateUserConfig({
'outline.keepWindow': true,
})
await createBuffer()
let curr = await nvim.call('bufnr', ['%'])
await symbols.showOutline()
let bufnr = await nvim.call('bufnr', ['%'])
expect(curr).toBe(bufnr)
})
it('should check on buffer switch', async () => {
workspace.configurations.updateUserConfig({
'outline.checkBufferSwitch': true,
})
await createBuffer()
await symbols.showOutline(1)
await helper.edit('unnamed')
await helper.wait(300)
let buf = await getOutlineBuffer()
let lines = await buf.lines
expect(lines).toEqual(['Document symbol provider not found'])
})
it('should not check on buffer switch', async () => {
workspace.configurations.updateUserConfig({
'outline.checkBufferSwitch': false
})
await helper.wait(30)
await createBuffer()
await symbols.showOutline(1)
await helper.edit('unnamed')
await helper.wait(100)
let buf = await getOutlineBuffer()
let lines = await buf.lines
expect(lines).toEqual([
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3'
])
})
it('should not check on buffer reload', async () => {
workspace.configurations.updateUserConfig({
'outline.checkBufferSwitch': false
})
await symbols.showOutline(1)
await helper.wait(50)
await createBuffer()
await helper.wait(50)
let buf = await getOutlineBuffer()
expect(buf).toBeUndefined()
})
it('should sort by position', async () => {
let code = `class myClass {
fun2() { }
fun1() {}
}`
workspace.configurations.updateUserConfig({
'outline.sortBy': 'position',
})
await createBuffer(code)
await symbols.showOutline(1)
let buf = await getOutlineBuffer()
let lines = await buf.lines
expect(lines).toEqual([
'OUTLINE', '- c myClass 1', ' m fun2 2', ' m fun1 3'
])
})
it('should sort by name', async () => {
let code = `class myClass {
fun2() {}
fun1() {}
}`
workspace.configurations.updateUserConfig({
'outline.sortBy': 'name',
})
await createBuffer(code)
await symbols.showOutline(1)
let buf = await getOutlineBuffer()
let lines = await buf.lines
expect(lines).toEqual([
'OUTLINE', '- c myClass 1', ' m fun1 3', ' m fun2 2'
])
})
})
describe('events', () => {
it('should dispose on buffer unload', async () => {
await createBuffer()
let curr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
await nvim.command('tabe')
await nvim.command(`bd! ${curr}`)
await helper.wait(30)
let buf = await getOutlineBuffer()
expect(buf).toBeUndefined()
})
it('should check current window on BufEnter', async () => {
await createBuffer()
await symbols.showOutline(0)
let winid = await nvim.call('win_getid', [])
await nvim.command('enew')
await helper.wait(200)
let win = await nvim.window
expect(win.id).toBe(winid)
})
it('should recreated when original window exists', async () => {
await symbols.showOutline(1)
await helper.wait(50)
await createBuffer()
await helper.wait(50)
let buf = await getOutlineBuffer()
expect(buf).toBeDefined()
})
it('should keep old outline when new buffer not attached', async () => {
await createBuffer()
await symbols.showOutline(1)
await nvim.command(`vnew +setl\\ buftype=nofile`)
await helper.wait(50)
let buf = await getOutlineBuffer()
expect(buf).toBeDefined()
let lines = await buf.lines
expect(lines).toEqual([
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3'
])
})
it('should not reload when switch to original buffer', async () => {
await createBuffer()
await symbols.showOutline(0)
let buf = await getOutlineBuffer()
let name = await buf.name
await nvim.command('wincmd p')
await helper.wait(50)
buf = await getOutlineBuffer()
let curr = await buf.name
expect(curr).toBe(name)
})
it('should dispose provider on outline hide', async () => {
await createBuffer()
let bufnr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
await nvim.command('q')
await helper.wait(30)
let exists = symbols.hasOutline(bufnr)
expect(exists).toBe(false)
})
})
describe('show()', () => {
it('should throw when document not attached', async () => {
await nvim.command(`edit +setl\\ buftype=nofile t`)
await helper.wait(50)
let err
try {
await symbols.showOutline(1)
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should not throw when provider not exists', async () => {
await symbols.showOutline(1)
let buf = await getOutlineBuffer()
expect(buf).toBeDefined()
})
it('should not throw when symbols is empty', async () => {
await createBuffer('')
await symbols.showOutline(1)
let buf = await getOutlineBuffer()
expect(buf).toBeDefined()
})
it('should jump to selected symbol', async () => {
await createBuffer()
let bufnr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
await helper.wait(50)
await nvim.command('exe 3')
await nvim.input('<cr>')
await helper.wait(50)
let curr = await nvim.call('bufnr', ['%'])
expect(curr).toBe(bufnr)
let cursor = await nvim.call('coc#cursor#position')
expect(cursor).toEqual([1, 2])
})
it('should update symbols', async () => {
await createBuffer()
let doc = await workspace.document
let bufnr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(1)
await helper.wait(10)
let buf = nvim.createBuffer(bufnr)
let code = 'class foo{}'
await buf.setLines(code.split('\n'), {
start: 0,
end: -1,
strictIndexing: false
})
await doc.synchronize()
await helper.wait(200)
buf = await getOutlineBuffer()
let lines = await buf.lines
expect(lines).toEqual([
'No results',
'',
'OUTLINE'
])
})
})
describe('actions', () => {
it('should invoke visual select', async () => {
await createBuffer()
let bufnr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(0)
await helper.wait(50)
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.wait(50)
await nvim.input('<cr>')
await helper.wait(50)
let m = await nvim.mode
expect(m.mode).toBe('v')
let buf = await nvim.buffer
expect(buf.id).toBe(bufnr)
})
it('should invoke selected code action', async () => {
const codeAction = CodeAction.create('my action', CodeActionKind.Refactor)
let uri: string
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], {
provideCodeActions: (
_document,
_range: Range,
_context: CodeActionContext,
_token: CancellationToken
) => [codeAction],
resolveCodeAction: (action): ProviderResult<CodeAction> => {
action.edit = {
changes: {
[uri]: [TextEdit.del(Range.create(0, 0, 0, 5))]
}
}
return action
}
}, undefined))
await createBuffer()
let bufnr = await nvim.call('bufnr', ['%'])
let doc = workspace.getDocument(bufnr)
uri = doc.uri
await symbols.showOutline(0)
await helper.wait(50)
await nvim.command('exe 3')
await nvim.input('<tab>')
await helper.wait(50)
await nvim.input('<cr>')
await helper.wait(200)
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines[0]).toBe(' myClass {')
})
})
describe('hide()', () => {
it('should hide outline', async () => {
await createBuffer('')
await symbols.showOutline(0)
await symbols.hideOutline()
let buf = await getOutlineBuffer()
expect(buf).toBeUndefined()
})
it('should not throw when outline not exists', async () => {
await symbols.hideOutline()
let buf = await getOutlineBuffer()
expect(buf).toBeUndefined()
})
})
describe('dispose', () => {
it('should dispose provider and views', async () => {
await createBuffer('')
let bufnr = await nvim.call('bufnr', ['%'])
await symbols.showOutline(1)
symbols.dispose()
await helper.wait(50)
expect(symbols.hasOutline(bufnr)).toBe(false)
let buf = await getOutlineBuffer()
expect(buf).toBeUndefined()
})
})
})

117
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/parser.ts

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
import { TextDocument } from 'vscode-languageserver-textdocument'
import { DocumentSymbol, SymbolKind, Range } from 'vscode-languageserver-protocol'
/**
* A syntax parser that parse `class` and `method` only.
*/
export default class Parser {
private _curr = 0
private _symbols: DocumentSymbol[] = []
private currSymbol: DocumentSymbol | undefined
private len: number
private textDocument: TextDocument
constructor(private _content: string) {
this.len = _content.length
this.textDocument = TextDocument.create('test:///a', 'txt', 1, _content)
}
public parse(): DocumentSymbol[] {
while (this._curr <= this.len - 1) {
this.parseToken()
}
return this._symbols
}
/**
* Parse a symbol, reset currSymbol & _curr
*/
private parseToken(): void {
this.skipSpaces()
if (this.currSymbol) {
let endOffset = this.textDocument.offsetAt(this.currSymbol.range.end)
if (this._curr > endOffset) {
this.currSymbol = undefined
}
}
let remain = this.getLineRemian()
let ms = remain.match(/^(class)\s(\w+)\s\{\s*/)
if (ms) {
// find class
let start = this._curr + 6
let end = start + ms[2].length
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end))
let endPosition = this.findMatchedIndex(this._curr + ms[0].length)
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition))
let symbolInfo: DocumentSymbol = {
range,
selectionRange,
kind: SymbolKind.Class,
name: ms[2],
children: []
}
if (this.currSymbol && this.currSymbol.children) {
this.currSymbol.children.push(symbolInfo)
} else {
this._symbols.push(symbolInfo)
}
this.currSymbol = symbolInfo
} else if (this.currSymbol && this.currSymbol.kind == SymbolKind.Class) {
let ms = remain.match(/(\w+)\(.*\)\s*\{/)
if (ms) {
// find method
let start = this._curr
let end = start + ms[1].length
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end))
let endPosition = this.findMatchedIndex(this._curr + ms[0].length)
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition))
let symbolInfo: DocumentSymbol = {
range,
selectionRange,
kind: SymbolKind.Method,
name: ms[1]
}
if (this.currSymbol && this.currSymbol.children) {
this.currSymbol.children.push(symbolInfo)
} else {
this._symbols.push(symbolInfo)
}
}
}
this._curr = this._curr + remain.length + 1
}
private findMatchedIndex(start: number): number {
let level = 0
for (let i = start; i < this.len; i++) {
let ch = this._content[i]
if (ch == '{') {
level = level + 1
}
if (ch == '}') {
if (level == 0) return i
level = level - 1
}
}
throw new Error(`Can't find matched }`)
}
private getLineRemian(): string {
let chars = ''
for (let i = this._curr; i < this.len; i++) {
let ch = this._content[i]
if (ch == '\n') break
chars = chars + ch
}
return chars
}
private skipSpaces(): void {
for (let i = this._curr; i < this.len; i++) {
let ch = this._content[i]
if (!ch || /\S/.test(ch)) {
this._curr = i
break
}
}
}
}

347
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/refactor.test.ts

@ -0,0 +1,347 @@ @@ -0,0 +1,347 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import { URI } from 'vscode-uri'
import Refactor, { FileItem } from '../../handler/refactor/index'
import helper, { createTmpFile } from '../helper'
import languages from '../../languages'
import { WorkspaceEdit, Range } from 'vscode-languageserver-types'
import { Disposable } from '@chemzqm/neovim/lib/api/Buffer'
let nvim: Neovim
let refactor: Refactor
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
refactor = helper.plugin.getHandler().refactor
})
beforeEach(async () => {
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
refactor.reset()
await helper.reset()
})
describe('refactor', () => {
describe('create', () => {
it('should create from workspaceEdit', async () => {
let changes = {
[URI.file(__filename).toString()]: [{
range: Range.create(0, 0, 0, 6),
newText: ''
}, {
range: Range.create(1, 0, 1, 6),
newText: ''
}]
}
let edit: WorkspaceEdit = { changes }
let buf = await refactor.fromWorkspaceEdit(edit)
let shown = await buf.valid
expect(shown).toBe(true)
let items = buf.fileItems
expect(items.length).toBe(1)
})
it('should create from locations', async () => {
let uri = URI.file(__filename).toString()
let locations = [{
uri,
range: Range.create(0, 0, 0, 6),
}, {
uri,
range: Range.create(1, 0, 1, 6),
}]
let buf = await refactor.fromLocations(locations)
let shown = await buf.valid
expect(shown).toBe(true)
let items = buf.fileItems
expect(items.length).toBe(1)
})
})
describe('onChange', () => {
it('should ignore when change after range', async () => {
let doc = await helper.createDocument()
await doc.buffer.append(['foo', 'bar'])
await refactor.fromLocations([{ uri: doc.uri, range: Range.create(0, 0, 0, 3) }])
let lines = await nvim.call('getline', [1, '$'])
await doc.buffer.append(['def'])
doc.forceSync()
await helper.wait(100)
let newLines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(newLines)
})
it('should adjust when change before range', async () => {
let doc = await helper.createDocument()
await doc.buffer.append(['', '', '', '', 'foo', 'bar'])
await helper.wait(50)
doc.forceSync()
let buf = await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }])
await doc.buffer.setLines(['def'], { start: 0, end: 0, strictIndexing: false })
doc.forceSync()
await helper.wait(100)
let fileRange = buf.getFileRange(4)
expect(fileRange.start).toBe(2)
expect(fileRange.end).toBe(8)
})
it('should removed when lines empty', async () => {
let doc = await helper.createDocument()
await doc.buffer.append(['', '', '', '', 'foo', 'bar'])
await helper.wait(50)
doc.forceSync()
let buf = await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }])
await doc.buffer.setLines([], { start: 0, end: -1, strictIndexing: false })
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines.length).toBe(3)
let items = buf.fileItems
expect(items.length).toBe(0)
})
it('should change when liens changed', async () => {
let doc = await helper.createDocument()
await doc.buffer.append(['', '', '', '', 'foo', 'bar'])
await helper.wait(50)
doc.forceSync()
await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }])
await doc.buffer.setLines(['def'], { start: 5, end: 6, strictIndexing: false })
doc.forceSync()
await helper.wait(30)
let lines = await nvim.call('getline', [1, '$'])
expect(lines[lines.length - 2]).toBe('def')
})
})
describe('refactor#getFileChanges', () => {
it('should get changes #1', async () => {
await helper.createDocument()
let lines = `
Save current buffer to make changes
\u3000
\u3000
\u3000/a.ts
})
} `
let buf = await refactor.fromLines(lines.split('\n'))
let changes = await buf.getFileChanges()
expect(changes).toEqual([{ lnum: 5, filepath: '/a.ts', lines: [' })', ' } '] }])
})
it('should get changes #2', async () => {
let lines = `
\u3000/a.ts
})
} `
let buf = await refactor.fromLines(lines.split('\n'))
let changes = await buf.getFileChanges()
expect(changes).toEqual([{ lnum: 2, filepath: '/a.ts', lines: [' })', ' } '] }])
})
it('should get changes #3', async () => {
let lines = `
\u3000/a.ts
})
}
\u3000`
let buf = await refactor.fromLines(lines.split('\n'))
let changes = await buf.getFileChanges()
expect(changes).toEqual([{ lnum: 2, filepath: '/a.ts', lines: [' })', ' }'] }])
})
it('should get changes #4', async () => {
let lines = `
\u3000/a.ts
foo
\u3000/b.ts
bar
\u3000`
let buf = await refactor.fromLines(lines.split('\n'))
let changes = await buf.getFileChanges()
expect(changes).toEqual([
{ filepath: '/a.ts', lnum: 2, lines: ['foo'] },
{ filepath: '/b.ts', lnum: 4, lines: ['bar'] }
])
})
})
describe('Refactor#createRefactorBuffer', () => {
it('should create refactor buffer', async () => {
await helper.createDocument()
let winid = await nvim.call('win_getid')
let buf = await refactor.createRefactorBuffer()
let curr = await nvim.call('win_getid')
expect(curr).toBeGreaterThan(winid)
let valid = await buf.valid
expect(valid).toBe(true)
})
it('should jump to position by <CR>', async () => {
await helper.createDocument()
let buf = await refactor.createRefactorBuffer()
let fileItem: FileItem = {
filepath: __filename,
ranges: [{ start: 10, end: 11 }, { start: 15, end: 20 }]
}
await buf.addFileItems([fileItem])
await nvim.call('cursor', [5, 1])
await buf.splitOpen()
let line = await nvim.eval('line(".")')
let bufname = await nvim.eval('bufname("%")')
expect(bufname).toMatch('refactor.test.ts')
expect(line).toBe(11)
})
})
describe('Refactor#saveRefactor', () => {
it('should adjust line ranges after change', async () => {
let filename = await createTmpFile('foo\n\nbar\n')
let fileItem: FileItem = {
filepath: filename,
ranges: [{ start: 0, end: 1 }, { start: 2, end: 3 }]
}
let buf = await refactor.createRefactorBuffer()
await buf.addFileItems([fileItem])
nvim.pauseNotification()
nvim.call('setline', [5, ['xyz']], true)
nvim.command('undojoin', true)
nvim.call('append', [5, ['de']], true)
nvim.command('undojoin', true)
nvim.call('append', [8, ['bar']], true)
await nvim.resumeNotification()
await helper.wait(100)
let res = await refactor.save(buf.buffer.id)
expect(res).toBe(true)
let content = fs.readFileSync(filename, 'utf8')
expect(content).toBe('xyz\nde\n\nbar\nbar\n')
})
it('should not save when no change made', async () => {
let buf = await refactor.createRefactorBuffer()
let fileItem: FileItem = {
filepath: __filename,
ranges: [{ start: 10, end: 11 }, { start: 15, end: 20 }]
}
await buf.addFileItems([fileItem])
let res = await buf.save()
expect(res).toBe(false)
})
it('should sync buffer change to file', async () => {
let doc = await helper.createDocument()
await doc.buffer.replace(['foo', 'bar', 'line'], 0)
await helper.wait(30)
let filename = URI.parse(doc.uri).fsPath
let fileItem: FileItem = {
filepath: filename,
ranges: [{ start: 0, end: 2 }]
}
let buf = await refactor.createRefactorBuffer()
await buf.addFileItems([fileItem])
await nvim.call('setline', [5, 'changed'])
let res = await buf.save()
expect(res).toBe(true)
expect(fs.existsSync(filename)).toBe(true)
let content = fs.readFileSync(filename, 'utf8')
let lines = content.split('\n')
expect(lines).toEqual(['changed', 'bar', 'line', ''])
fs.unlinkSync(filename)
})
})
describe('doRefactor', () => {
let disposable: Disposable
afterEach(() => {
if (disposable) disposable.dispose()
disposable = null
})
it('should throw when rename provider not found', async () => {
await helper.createDocument()
let err
try {
await refactor.doRefactor()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should show message when prepare failed', async () => {
await helper.createDocument()
disposable = languages.registerRenameProvider(['*'], {
prepareRename: () => {
return undefined
},
provideRenameEdits: () => {
return null
}
})
await refactor.doRefactor()
let res = await helper.getCmdline()
expect(res).toMatch(/unable to rename/)
})
it('should show message when returned edits is null', async () => {
await helper.createDocument()
disposable = languages.registerRenameProvider(['*'], {
provideRenameEdits: () => {
return null
}
})
await refactor.doRefactor()
let res = await helper.getCmdline()
expect(res).toMatch(/returns null/)
})
it('should open refactor window when edits is valid', async () => {
let filepath = __filename
disposable = languages.registerRenameProvider(['*'], {
provideRenameEdits: () => {
let changes = {
[URI.file(filepath).toString()]: [{
range: Range.create(0, 0, 0, 6),
newText: ''
}, {
range: Range.create(1, 0, 1, 6),
newText: ''
}]
}
let edit: WorkspaceEdit = { changes }
return edit
}
})
await helper.createDocument(filepath)
let winid = await nvim.call('win_getid')
await refactor.doRefactor()
let currWin = await nvim.call('win_getid')
expect(currWin - winid).toBeGreaterThan(0)
let bufnr = await nvim.call('bufnr', ['%'])
let b = refactor.getBuffer(bufnr)
expect(b).toBeDefined()
})
})
describe('search', () => {
it('should open refactor buffer from search result', async () => {
let escaped = await nvim.call('fnameescape', [__dirname])
await nvim.command(`cd ${escaped}`)
await helper.createDocument()
await refactor.search(['registerRenameProvider'])
let buf = await nvim.buffer
let name = await buf.name
expect(name).toMatch(/__coc_refactor__/)
let lines = await buf.lines
expect(lines[0]).toMatch(/Save current buffer/)
})
})
})

263
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/rename.test.ts

@ -0,0 +1,263 @@ @@ -0,0 +1,263 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
import Rename from '../../handler/rename'
import languages from '../../languages'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let rename: Rename
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
rename = helper.plugin.getHandler().rename
})
function getWordRangeAtPosition(doc: TextDocument, position: Position): Range | null {
let lines = doc.getText().split(/\r?\n/)
let line = lines[position.line]
if (line.length == 0 || position.character >= line.length) return null
if (!/\w/.test(line[position.character])) return null
let start = position.character
let end = position.character + 1
if (!/\w/.test(line[start])) {
return Range.create(position, { line: position.line, character: position.character + 1 })
}
while (start >= 0) {
let ch = line[start - 1]
if (!ch || !/\w/.test(ch)) break
start = start - 1
}
while (end <= line.length) {
let ch = line[end]
if (!ch || !/\w/.test(ch)) break
end = end + 1
}
return Range.create(position.line, start, position.line, end)
}
function getSymbolRanges(textDocument: TextDocument, word: string): Range[] {
let res: Range[] = []
let str = ''
let content = textDocument.getText()
for (let i = 0, l = content.length; i < l; i++) {
let ch = content[i]
if ('-' == ch && str.length == 0) {
continue
}
let isKeyword = /\w/.test(ch)
if (isKeyword) {
str = str + ch
}
if (str.length > 0 && !isKeyword && str == word) {
res.push(Range.create(textDocument.positionAt(i - str.length), textDocument.positionAt(i)))
}
if (!isKeyword) {
str = ''
}
}
return res
}
beforeEach(() => {
disposables.push(languages.registerRenameProvider([{ language: 'javascript' }], {
provideRenameEdits: (doc, position: Position, newName: string) => {
let range = getWordRangeAtPosition(doc, position)
if (range) {
let word = doc.getText(range)
if (word) {
let ranges = getSymbolRanges(doc, word)
return {
changes: {
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName))
}
}
}
}
return undefined
},
prepareRename: (doc, position) => {
let range = getWordRangeAtPosition(doc, position)
return range ? { range, placeholder: doc.getText(range) } : null
}
}))
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('rename handler', () => {
describe('getWordEdit', () => {
it('should not throw when provider not found', async () => {
await helper.edit()
let res = await rename.getWordEdit()
expect(res).toBe(null)
})
it('should return null when prepare failed', async () => {
let doc = await helper.createDocument('t.js')
await nvim.setLine('你')
await doc.synchronize()
let res = await rename.getWordEdit()
expect(res).toBe(null)
})
it('should return workspace edit', async () => {
let doc = await helper.createDocument('t.js')
await nvim.setLine('foo foo')
await doc.synchronize()
let res = await rename.getWordEdit()
expect(res).toBeDefined()
expect(res.changes[doc.uri].length).toBe(2)
})
it('should extract words from buffer', async () => {
let doc = await helper.createDocument('t')
await nvim.setLine('你 你 你')
await doc.synchronize()
let res = await rename.getWordEdit()
expect(res).toBeDefined()
expect(res.changes[doc.uri].length).toBe(3)
})
})
describe('rename', () => {
it('should throw when provider not found', async () => {
await helper.edit()
let err
try {
await rename.rename('foo')
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should return false for invalid position', async () => {
await helper.createDocument('t.js')
let res = await rename.rename('foo')
expect(res).toBe(false)
})
it('should use newName from placeholder', async () => {
await helper.createDocument('t.js')
await nvim.setLine('foo foo foo')
let p = rename.rename()
await helper.wait(50)
await nvim.input('<C-u>')
await helper.wait(10)
await nvim.input('bar')
await nvim.input('<cr>')
let res = await p
expect(res).toBe(true)
})
it('should return false for empty name', async () => {
await helper.createDocument('t.js')
await nvim.setLine('foo foo foo')
let p = rename.rename()
await helper.wait(50)
await nvim.input('<C-u>')
await helper.wait(20)
await nvim.input('<cr>')
let res = await p
expect(res).toBe(false)
})
it('should use newName from range', async () => {
disposables.push(languages.registerRenameProvider([{ language: '*' }], {
provideRenameEdits: (doc, position: Position, newName: string) => {
let range = getWordRangeAtPosition(doc, position)
if (range) {
let word = doc.getText(range)
if (word) {
let ranges = getSymbolRanges(doc, word)
return {
changes: {
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName))
}
}
}
}
return undefined
},
prepareRename: (doc, position) => {
let range = getWordRangeAtPosition(doc, position)
return range ? range : null
}
}))
await helper.createDocument()
await nvim.setLine('foo foo foo')
let p = rename.rename()
await helper.wait(50)
await nvim.input('<C-u>')
await helper.wait(10)
await nvim.input('bar')
await nvim.input('<cr>')
let res = await p
expect(res).toBe(true)
let line = await nvim.getLine()
expect(line).toBe('bar bar bar')
})
it('should use newName from cword', async () => {
disposables.push(languages.registerRenameProvider([{ language: '*' }], {
provideRenameEdits: (doc, position: Position, newName: string) => {
let range = getWordRangeAtPosition(doc, position)
if (range) {
let word = doc.getText(range)
if (word) {
let ranges = getSymbolRanges(doc, word)
return {
changes: {
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName))
}
}
}
}
return undefined
}
}))
await helper.createDocument()
await nvim.setLine('foo foo foo')
let p = rename.rename()
await helper.wait(50)
await nvim.input('<C-u>')
await helper.wait(10)
await nvim.input('bar')
await nvim.input('<cr>')
let res = await p
expect(res).toBe(true)
let line = await nvim.getLine()
expect(line).toBe('bar bar bar')
})
it('should return false when result is empty', async () => {
disposables.push(languages.registerRenameProvider([{ language: '*' }], {
provideRenameEdits: () => {
return null
}
}))
await helper.createDocument()
await nvim.setLine('foo foo foo')
let p = rename.rename()
await helper.wait(50)
await nvim.input('<C-u>')
await helper.wait(10)
await nvim.input('bar')
await nvim.input('<cr>')
let res = await p
expect(res).toBe(false)
})
})
})

101
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/search.test.ts

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
import { Neovim } from '@chemzqm/neovim'
import Refactor from '../../handler/refactor'
import Search, { getPathFromArgs } from '../../handler/search'
import helper from '../helper'
import path from 'path'
let nvim: Neovim
let refactor: Refactor
// use fake rg command
let cmd = path.resolve(__dirname, '../rg')
let cwd = process.cwd()
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
refactor = helper.plugin.getHandler().refactor
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
refactor.reset()
await helper.reset()
})
describe('getPathFromArgs', () => {
it('should get undefined path', async () => {
let res = getPathFromArgs(['a'])
expect(res).toBeUndefined()
res = getPathFromArgs(['a', 'b', '-c'])
expect(res).toBeUndefined()
res = getPathFromArgs(['a', '-b', 'c'])
expect(res).toBeUndefined()
})
})
describe('search', () => {
it('should open refactor window', async () => {
let search = new Search(nvim, cmd)
let buf = await refactor.createRefactorBuffer()
await search.run([], cwd, buf)
await helper.wait(50)
let fileItems = buf.fileItems
expect(fileItems.length).toBe(2)
expect(fileItems[0].ranges.length).toBe(2)
})
it('should abort task', async () => {
let search = new Search(nvim, cmd)
let buf = await refactor.createRefactorBuffer()
let p = search.run(['--sleep', '1000'], cwd, buf)
search.abort()
await p
let fileItems = buf.fileItems
expect(fileItems.length).toBe(0)
})
it('should work with CocAction search', async () => {
await helper.doAction('search', ['CocAction'])
let bufnr = await nvim.call('bufnr', ['%'])
let buf = refactor.getBuffer(bufnr)
expect(buf).toBeDefined()
})
it('should fail on invalid command', async () => {
let search = new Search(nvim, 'rrg')
let buf = await refactor.createRefactorBuffer()
let err
try {
await search.run([], cwd, buf)
} catch (e) {
err = e
}
expect(err).toBeDefined()
let msg = await helper.getCmdline()
expect(msg).toMatch(/Error on command "rrg"/)
})
it('should show empty result when no result found', async () => {
await helper.doAction('search', ['should found ' + ' no result'])
let bufnr = await nvim.call('bufnr', ['%'])
let buf = refactor.getBuffer(bufnr)
expect(buf).toBeDefined()
let buffer = await nvim.buffer
let lines = await buffer.lines
expect(lines[1]).toMatch(/No match found/)
})
it('should use corrent search folder for rg', async () => {
let search = new Search(nvim, 'rg')
await helper.createDocument()
let buf = await refactor.createRefactorBuffer()
await search.run(['-w', 'createRefactorBuffer', 'src/__tests__'], cwd, buf)
let buffer = await nvim.buffer
let lines = await buffer.lines
expect(lines[1].startsWith('Files: ')).toBe(true)
})
})

143
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/selectionRange.test.ts

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import SelectionRange from '../../handler/selectionRange'
import languages from '../../languages'
import workspace from '../../workspace'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let selection: SelectionRange
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
selection = helper.plugin.getHandler().selectionRange
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('selectionRange', () => {
describe('getSelectionRanges()', () => {
it('should throw error when selectionRange provider not exists', async () => {
let doc = await helper.createDocument()
await doc.synchronize()
let err
try {
await selection.getSelectionRanges()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
it('should return ranges', async () => {
await helper.createDocument()
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
return [{
range: Range.create(0, 0, 0, 1)
}]
}
}))
let res = await selection.getSelectionRanges()
expect(res).toBeDefined()
expect(Array.isArray(res)).toBe(true)
})
})
describe('selectRange()', () => {
async function getSelectedRange(): Promise<Range> {
let m = await nvim.mode
expect(m.mode).toBe('v')
let bufnr = await nvim.call('bufnr', ['%'])
await nvim.input('<esc>')
let doc = workspace.getDocument(bufnr)
let res = await workspace.getSelectedRange('v', doc)
return res
}
it('should select ranges forward', async () => {
let doc = await helper.createDocument()
let called = 0
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar\ntest\n')])
await nvim.call('cursor', [1, 1])
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
called += 1
let arr = [{
range: Range.create(0, 0, 0, 1)
}, {
range: Range.create(0, 0, 0, 3)
}, {
range: Range.create(0, 0, 1, 3)
}]
return arr
}
}))
await doc.synchronize()
await selection.selectRange('', false)
await selection.selectRange('', true)
expect(called).toBe(1)
let res = await getSelectedRange()
expect(res).toEqual(Range.create(0, 0, 0, 1))
await selection.selectRange('v', true)
expect(called).toBe(2)
res = await getSelectedRange()
expect(res).toEqual(Range.create(0, 0, 0, 3))
await selection.selectRange('v', true)
expect(called).toBe(3)
res = await getSelectedRange()
expect(res).toEqual(Range.create(0, 0, 1, 3))
await selection.selectRange('v', true)
expect(called).toBe(4)
let m = await nvim.mode
expect(m.mode).toBe('n')
})
it('should select ranges backward', async () => {
let doc = await helper.createDocument()
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar\ntest\n')])
await nvim.call('cursor', [1, 1])
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], {
provideSelectionRanges: _doc => {
let arr = [{
range: Range.create(0, 0, 0, 1)
}, {
range: Range.create(0, 0, 0, 3)
}, {
range: Range.create(0, 0, 1, 3)
}]
return arr
}
}))
await doc.synchronize()
await selection.selectRange('', true)
let mode = await nvim.call('mode')
expect(mode).toBe('v')
await nvim.input('<esc>')
await workspace.selectRange(Range.create(0, 0, 1, 3))
await nvim.input('<esc>')
await selection.selectRange('v', false)
let r = await getSelectedRange()
expect(r).toEqual(Range.create(0, 0, 0, 3))
await nvim.input('<esc>')
await selection.selectRange('v', false)
r = await getSelectedRange()
expect(r).toEqual(Range.create(0, 0, 0, 1))
await nvim.input('<esc>')
await selection.selectRange('v', false)
mode = await nvim.call('mode')
expect(mode).toBe('n')
})
})
})

180
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/semanticTokens.test.ts

@ -0,0 +1,180 @@ @@ -0,0 +1,180 @@
import { Buffer, Neovim } from '@chemzqm/neovim'
import { Disposable, SemanticTokensLegend } from 'vscode-languageserver-protocol'
import languages from '../../languages'
import SemanticTokensHighlights from '../../handler/semanticTokensHighlights/index'
import { disposeAll } from '../../util'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let highlighter: SemanticTokensHighlights
let legend: SemanticTokensLegend = {
tokenTypes: [
"comment",
"keyword",
"string",
"number",
"regexp",
"operator",
"namespace",
"type",
"struct",
"class",
"interface",
"enum",
"enumMember",
"typeParameter",
"function",
"method",
"property",
"macro",
"variable",
"parameter",
"angle",
"arithmetic",
"attribute",
"bitwise",
"boolean",
"brace",
"bracket",
"builtinType",
"character",
"colon",
"comma",
"comparison",
"constParameter",
"dot",
"escapeSequence",
"formatSpecifier",
"generic",
"label",
"lifetime",
"logical",
"operator",
"parenthesis",
"punctuation",
"selfKeyword",
"semicolon",
"typeAlias",
"union",
"unresolvedReference"
],
tokenModifiers: [
"documentation",
"declaration",
"definition",
"static",
"abstract",
"deprecated",
"readonly",
"constant",
"controlFlow",
"injected",
"mutable",
"consuming",
"async",
"library",
"public",
"unsafe",
"attribute",
"trait",
"callable",
"intraDocLink"
]
}
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
highlighter = helper.plugin.getHandler().semanticHighlighter
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(async () => {
async function createBuffer(code: string): Promise<Buffer> {
let buf = await nvim.buffer
await nvim.command('setf rust')
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false })
let doc = await workspace.document
doc.forceSync()
return buf
}
disposables.push(languages.registerDocumentSemanticTokensProvider([{ language: 'rust' }], {
provideDocumentSemanticTokens: () => {
return {
resultId: '1',
data: [
0, 0, 2, 1, 0,
0, 3, 4, 14, 2,
0, 4, 1, 41, 0,
0, 1, 1, 41, 0,
0, 2, 1, 25, 0,
1, 4, 8, 17, 0,
0, 8, 1, 41, 0,
0, 1, 3, 2, 0,
0, 3, 1, 41, 0,
0, 1, 1, 44, 0,
1, 0, 1, 25, 0,
]
}
}
}, legend))
await createBuffer(`fn main() {
println!("H");
}`)
})
afterEach(async () => {
workspace.configurations.updateUserConfig({
'coc.preferences.semanticTokensHighlights': true
})
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('semanticTokens', () => {
describe('triggerSemanticTokens', () => {
it('should be disabled', async () => {
await helper.createDocument()
workspace.configurations.updateUserConfig({
'coc.preferences.semanticTokensHighlights': false
})
const curr = await highlighter.getCurrentItem()
let err
try {
curr.checkState()
} catch (e) {
err = e
}
expect(err).toBeDefined()
expect(err.message).toMatch('disabled by configuration')
})
it('should get legend by API', async () => {
const doc = await workspace.document
const l = languages.getLegend(doc.textDocument)
expect(l).toEqual(legend)
})
it('should get semanticTokens by API', async () => {
// const doc = await workspace.document
// const highlights = await highlighter.getHighlights(doc.bufnr)
// expect(highlights.length).toBe(11)
// expect(highlights[0].hlGroup).toBe('CocSem_keyword')
})
it('should doHighlight', async () => {
const doc = await workspace.document
await nvim.call('CocAction', 'semanticHighlight')
const highlights = await nvim.call("coc#highlight#get_highlights", [doc.bufnr, 'semanticTokens'])
expect(highlights.length).toBe(11)
expect(highlights[0].hlGroup).toBe('CocSem_keyword')
})
})
})

376
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/signature.test.ts

@ -0,0 +1,376 @@ @@ -0,0 +1,376 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Position, ParameterInformation, SignatureInformation } from 'vscode-languageserver-protocol'
import languages from '../../languages'
import { disposeAll } from '../../util'
import Signature from '../../handler/signature'
import workspace from '../../workspace'
import helper from '../helper'
let nvim: Neovim
let signature: Signature
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
signature = helper.plugin.getHandler().signature
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('signatureHelp', () => {
describe('triggerSignatureHelp', () => {
it('should show signature by api', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, []))
await helper.createDocument()
await nvim.input('foo')
await signature.triggerSignatureHelp()
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines[2]).toMatch('my signature')
})
it('should trigger by space', async () => {
let called = false
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
called = true
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, [' ']))
await helper.createDocument()
await nvim.input('i')
await helper.wait(30)
await nvim.input(' ')
await helper.wait(50)
expect(called).toBe(true)
})
it('should show signature help with param label as string', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [
SignatureInformation.create('foo()', 'my signature'),
SignatureInformation.create('foo(a, b)', 'my signature', ParameterInformation.create('a', 'description')),
],
activeParameter: 0,
activeSignature: 1
}
}
}, []))
await helper.createDocument()
await nvim.input('foo')
await signature.triggerSignatureHelp()
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines.join('\n')).toMatch(/description/)
})
it('should consider coc_last_placeholder on select mode', async () => {
let pos: Position
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, position) => {
pos = position
return {
signatures: [
SignatureInformation.create('foo(a, b)', 'my signature', ParameterInformation.create('a', 'description')),
],
activeParameter: 1,
activeSignature: null
}
}
}, []))
let doc = await helper.createDocument()
let line = await nvim.call('line', ['.'])
await nvim.setLine(' fn(abc, def)')
await nvim.command('normal! 0fave')
await nvim.input('<C-g>')
let placeholder = {
bufnr: doc.bufnr,
start: Position.create(line - 1, 5),
end: Position.create(line - 1, 8)
}
await nvim.setVar('coc_last_placeholder', placeholder)
let m = await nvim.mode
expect(m.mode).toBe('s')
await signature.triggerSignatureHelp()
let win = await helper.getFloat()
expect(win).toBeDefined()
expect(pos).toEqual(Position.create(0, 5))
})
})
describe('events', () => {
it('should trigger signature help', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo(x, y)', 'my signature')],
activeParameter: 0,
activeSignature: 0
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('foo')
await nvim.input('(')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines[2]).toMatch('my signature')
})
it('should cancel trigger on InsertLeave', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: async (_doc, _position, token) => {
await helper.wait(1000)
if (token.isCancellationRequested) return undefined
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('foo')
let p = signature.triggerSignatureHelp()
await helper.wait(10)
await nvim.command('stopinsert')
await nvim.call('feedkeys', [String.fromCharCode(27), 'in'])
let res = await p
expect(res).toBe(false)
})
it('should not close signature on type', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('foo(')
await helper.wait(100)
await nvim.input('bar')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines[2]).toMatch('my signature')
})
it('should close signature float when empty signatures returned', async () => {
let empty = false
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
if (empty) return undefined
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('foo(')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeDefined()
empty = true
await signature.triggerSignatureHelp()
await helper.wait(50)
let res = await nvim.call('coc#float#valid', [win.id])
expect(res).toBe(0)
})
})
describe('float window', () => {
it('should align signature window to top', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
let buf = await nvim.buffer
await buf.setLines(['', '', '', '', ''], { start: 0, end: -1, strictIndexing: true })
await nvim.call('cursor', [5, 1])
await nvim.input('foo(')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines[2]).toMatch('my signature')
let res = await nvim.call('coc#float#cursor_relative', [win.id]) as any
expect(res.row).toBeLessThan(0)
})
it('should show parameter docs', async () => {
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo(a, b)', 'my signature',
ParameterInformation.create('a', 'foo'),
ParameterInformation.create([7, 8], 'bar'))],
activeParameter: 1,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
let buf = await nvim.buffer
await buf.setLines(['', '', '', '', ''], { start: 0, end: -1, strictIndexing: true })
await nvim.call('cursor', [5, 1])
await nvim.input('foo(a,')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeDefined()
let lines = await helper.getWinLines(win.id)
expect(lines.join('\n')).toMatch('bar')
})
})
describe('configurations', () => {
let { configurations } = workspace
afterEach(() => {
configurations.updateUserConfig({
'signature.target': 'float',
'signature.hideOnTextChange': false,
'signature.enable': true,
'signature.triggerSignatureWait': 500
})
})
it('should cancel signature on timeout', async () => {
configurations.updateUserConfig({ 'signature.triggerSignatureWait': 50 })
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position, token) => {
return new Promise(resolve => {
token.onCancellationRequested(() => {
clearTimeout(timer)
resolve(undefined)
})
let timer = setTimeout(() => {
resolve({
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
})
}, 200)
})
}
}, ['(', ',']))
await helper.createDocument()
await signature.triggerSignatureHelp()
let win = await helper.getFloat()
expect(win).toBeUndefined()
configurations.updateUserConfig({ 'signature.triggerSignatureWait': 100 })
})
it('should hide signature window on text change', async () => {
configurations.updateUserConfig({ 'signature.hideOnTextChange': true })
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('ifoo(')
let winid = await helper.waitFloat()
await nvim.input('x')
await helper.wait(100)
let res = await nvim.call('coc#float#valid', [winid])
expect(res).toBe(0)
configurations.updateUserConfig({ 'signature.hideOnTextChange': false })
})
it('should disable signature help trigger', async () => {
configurations.updateUserConfig({ 'signature.enable': false })
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo()', 'my signature')],
activeParameter: null,
activeSignature: null
}
}
}, ['(', ',']))
await helper.createDocument()
await nvim.input('foo')
await nvim.input('(')
await helper.wait(100)
let win = await helper.getFloat()
expect(win).toBeUndefined()
})
it('should echo simple signature help', async () => {
let idx = 0
let activeSignature = null
configurations.updateUserConfig({ 'signature.target': 'echo' })
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], {
provideSignatureHelp: (_doc, _position) => {
return {
signatures: [SignatureInformation.create('foo(a, b)', 'my signature',
ParameterInformation.create('a', 'foo'),
ParameterInformation.create([7, 8], 'bar')),
SignatureInformation.create('a'.repeat(workspace.env.columns + 10))
],
activeParameter: idx,
activeSignature
}
}
}, []))
await helper.createDocument()
await nvim.input('foo(')
await signature.triggerSignatureHelp()
let line = await helper.getCmdline()
expect(line).toMatch('(a, b)')
await nvim.input('a,')
idx = 1
await signature.triggerSignatureHelp()
line = await helper.getCmdline()
expect(line).toMatch('foo(a, b)')
activeSignature = 1
await signature.triggerSignatureHelp()
line = await helper.getCmdline()
expect(line).toMatch('aaaaaa')
})
})
})

284
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/handler/symbols.test.ts

@ -0,0 +1,284 @@ @@ -0,0 +1,284 @@
import { Buffer, Neovim } from '@chemzqm/neovim'
import { Disposable, SymbolInformation, SymbolKind, Range } from 'vscode-languageserver-protocol'
import Symbols from '../../handler/symbols/index'
import languages from '../../languages'
import workspace from '../../workspace'
import events from '../../events'
import { disposeAll } from '../../util'
import helper from '../helper'
import Parser from './parser'
let nvim: Neovim
let symbols: Symbols
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
symbols = helper.plugin.getHandler().symbols
})
beforeEach(() => {
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'javascript' }], {
provideDocumentSymbols: document => {
let parser = new Parser(document.getText())
let res = parser.parse()
return Promise.resolve(res)
}
}))
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
disposeAll(disposables)
disposables = []
})
describe('Parser', () => {
it('should parse content', async () => {
let code = `class myClass {
fun1() { }
}`
let parser = new Parser(code)
let res = parser.parse()
expect(res.length).toBeGreaterThan(0)
})
})
describe('symbols handler', () => {
async function createBuffer(code: string): Promise<Buffer> {
let buf = await nvim.buffer
await nvim.command('setf javascript')
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false })
let doc = await workspace.document
doc.forceSync()
return buf
}
describe('configuration', () => {
afterEach(() => {
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', false)
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', false)
})
it('should get configuration', async () => {
let functionUpdate = symbols.functionUpdate
expect(functionUpdate).toBe(false)
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', true)
functionUpdate = symbols.functionUpdate
expect(functionUpdate).toBe(true)
})
it('should update symbols automatically', async () => {
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', true)
let code = `class myClass {
fun1() {
}
}`
let buf = await createBuffer(code)
await nvim.call('cursor', [2, 8])
await events.fire('CursorHold', [buf.id])
let val = await buf.getVar('coc_current_function')
expect(val).toBe('fun1')
await nvim.call('cursor', [1, 8])
await events.fire('CursorHold', [buf.id])
val = await buf.getVar('coc_current_function')
expect(val).toBe('myClass')
})
})
describe('documentSymbols', () => {
it('should get symbols of current buffer', async () => {
let code = `class myClass {
fun1() { }
}`
await createBuffer(code)
let res = await helper.plugin.cocAction('documentSymbols')
expect(res.length).toBe(2)
})
it('should get current function symbols', async () => {
let code = `class myClass {
fun1() {
}
fun2() {
}
}
`
await createBuffer(code)
await nvim.call('cursor', [3, 0])
let res = await helper.doAction('getCurrentFunctionSymbol')
expect(res).toBe('fun1')
await nvim.command('normal! G')
res = await helper.doAction('getCurrentFunctionSymbol')
expect(res).toBe('')
})
it('should reset coc_current_function when symbols not exists', async () => {
let code = `class myClass {
fun1() {
}
}`
await createBuffer(code)
await nvim.call('cursor', [3, 0])
let res = await helper.doAction('getCurrentFunctionSymbol')
expect(res).toBe('fun1')
await nvim.command('normal! ggdG')
res = await symbols.getCurrentFunctionSymbol()
expect(res).toBe('')
})
it('should support SymbolInformation', async () => {
disposables.push(languages.registerDocumentSymbolProvider(['*'], {
provideDocumentSymbols: () => {
return [
SymbolInformation.create('root', SymbolKind.Function, Range.create(0, 0, 0, 10)),
SymbolInformation.create('child', SymbolKind.Function, Range.create(0, 0, 0, 10), '', 'root')
]
}
}))
let doc = await helper.createDocument()
let res = await symbols.getDocumentSymbols(doc.bufnr)
expect(res.length).toBe(2)
expect(res[0].text).toBe('root')
expect(res[1].text).toBe('child')
})
})
describe('selectSymbolRange', () => {
it('should show warning when no symbols exists', async () => {
disposables.push(languages.registerDocumentSymbolProvider(['*'], {
provideDocumentSymbols: () => {
return []
}
}))
await helper.createDocument()
await nvim.call('cursor', [3, 0])
await symbols.selectSymbolRange(false, '', ['Function'])
let msg = await helper.getCmdline()
expect(msg).toMatch(/No symbols found/)
})
it('should select symbol range at cursor position', async () => {
let code = `class myClass {
fun1() {
}
}`
let buf = await createBuffer(code)
await nvim.call('cursor', [3, 0])
await helper.doAction('selectSymbolRange', false, '', ['Function', 'Method'])
let mode = await nvim.mode
expect(mode.mode).toBe('v')
let doc = workspace.getDocument(buf.id)
await nvim.input('<esc>')
let res = await workspace.getSelectedRange('v', doc)
expect(res).toEqual({ start: { line: 1, character: 6 }, end: { line: 2, character: 6 } })
})
it('should select inner range', async () => {
let code = `class myClass {
fun1() {
let foo;
}
}`
let buf = await createBuffer(code)
await nvim.call('cursor', [3, 3])
await symbols.selectSymbolRange(true, '', ['Method'])
let mode = await nvim.mode
expect(mode.mode).toBe('v')
let doc = workspace.getDocument(buf.id)
await nvim.input('<esc>')
let res = await workspace.getSelectedRange('v', doc)
expect(res).toEqual({
start: { line: 2, character: 8 }, end: { line: 2, character: 16 }
})
})
it('should reset visualmode when selection not found', async () => {
let code = `class myClass {}`
await createBuffer(code)
await nvim.call('cursor', [1, 1])
await nvim.command('normal! gg0v$')
let mode = await nvim.mode
expect(mode.mode).toBe('v')
await nvim.input('<esc>')
await symbols.selectSymbolRange(true, 'v', ['Method'])
mode = await nvim.mode
expect(mode.mode).toBe('v')
})
it('should select symbol range from select range', async () => {
let code = `class myClass {
fun1() {
}
}`
let buf = await createBuffer(code)
await nvim.call('cursor', [2, 8])
await nvim.command('normal! viw')
await nvim.input('<esc>')
await helper.doAction('selectSymbolRange', false, 'v', ['Class'])
let mode = await nvim.mode
expect(mode.mode).toBe('v')
let doc = workspace.getDocument(buf.id)
await nvim.input('<esc>')
let res = await workspace.getSelectedRange('v', doc)
expect(res).toEqual({ start: { line: 0, character: 0 }, end: { line: 3, character: 4 } })
})
})
describe('cancel', () => {
it('should cancel symbols request on insert', async () => {
let cancelled = false
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'text' }], {
provideDocumentSymbols: (_doc, token) => {
return new Promise(s => {
token.onCancellationRequested(() => {
if (timer) clearTimeout(timer)
cancelled = true
s(undefined)
})
let timer = setTimeout(() => {
s(undefined)
}, 3000)
})
}
}))
let doc = await helper.createDocument('t.txt')
let p = symbols.getDocumentSymbols(doc.bufnr)
setTimeout(async () => {
await nvim.input('i')
}, 500)
await p
expect(cancelled).toBe(true)
})
})
describe('workspaceSymbols', () => {
it('should get workspace symbols', async () => {
disposables.push(languages.registerWorkspaceSymbolProvider({
provideWorkspaceSymbols: (_query, _token) => {
return [SymbolInformation.create('far', SymbolKind.Class, Range.create(0, 0, 0, 0))]
},
resolveWorkspaceSymbol: sym => {
let res = Object.assign({}, sym)
res.location.uri = 'test:///foo'
return res
}
}))
disposables.push(languages.registerWorkspaceSymbolProvider({
provideWorkspaceSymbols: (_query, _token) => {
return [SymbolInformation.create('bar', SymbolKind.Function, Range.create(0, 0, 0, 0))]
}
}))
let res = await symbols.getWorkspaceSymbols('a')
expect(res.length).toBe(2)
let resolved = await symbols.resolveWorkspaceSymbol(res[0])
expect(resolved?.location?.uri).toBe('test:///foo')
})
})
})

22
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/helper.test.ts

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
import { Neovim } from '@chemzqm/neovim'
import Plugin from '../plugin'
import helper from './helper'
let nvim: Neovim
let plugin: Plugin
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
plugin = helper.plugin
})
describe('Helper', () => {
it('should setup', () => {
expect(nvim).toBeTruthy()
expect(plugin.isReady).toBeTruthy()
})
})
afterAll(async () => {
await helper.shutdown()
})

248
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/helper.ts

@ -0,0 +1,248 @@ @@ -0,0 +1,248 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { Buffer, Neovim, Window } from '@chemzqm/neovim'
import * as cp from 'child_process'
import { EventEmitter } from 'events'
import fs from 'fs'
import os from 'os'
import path from 'path'
import util from 'util'
import attach from '../attach'
import Document from '../model/document'
import Plugin from '../plugin'
import workspace from '../workspace'
import { v4 as uuid } from 'uuid'
import { VimCompleteItem } from '../types'
export interface CursorPosition {
bufnum: number
lnum: number
col: number
}
process.on('uncaughtException', err => {
let msg = 'Uncaught exception: ' + err.stack
console.error(msg)
})
export class Helper extends EventEmitter {
public nvim: Neovim
public proc: cp.ChildProcess
public plugin: Plugin
constructor() {
super()
this.setMaxListeners(99)
}
public setup(): Promise<void> {
const vimrc = path.resolve(__dirname, 'vimrc')
let proc = this.proc = cp.spawn('nvim', ['-u', vimrc, '-i', 'NONE', '--embed'], {
cwd: __dirname
})
let plugin = this.plugin = attach({ proc })
this.nvim = plugin.nvim
this.nvim.uiAttach(160, 80, {}).catch(_e => {
// noop
})
proc.on('exit', () => {
this.proc = null
})
this.nvim.on('notification', (method, args) => {
if (method == 'redraw') {
for (let arg of args) {
let event = arg[0]
this.emit(event, arg.slice(1))
}
}
})
return new Promise(resolve => {
plugin.once('ready', resolve)
})
}
public async shutdown(): Promise<void> {
this.plugin.dispose()
await this.nvim.quit()
if (this.proc) {
this.proc.kill('SIGKILL')
}
await this.wait(60)
}
public async waitPopup(): Promise<void> {
for (let i = 0; i < 40; i++) {
await this.wait(50)
let visible = await this.nvim.call('pumvisible')
if (visible) return
}
throw new Error('timeout after 2s')
}
public async waitFloat(): Promise<number> {
for (let i = 0; i < 40; i++) {
await this.wait(50)
let winid = await this.nvim.call('coc#float#get_float_win')
if (winid) return winid
}
throw new Error('timeout after 2s')
}
public async selectCompleteItem(idx: number): Promise<void> {
await this.nvim.call('nvim_select_popupmenu_item', [idx, true, true, {}])
}
public async doAction(method: string, ...args: any[]): Promise<any> {
return await this.plugin.cocAction(method, ...args)
}
public async reset(): Promise<void> {
let mode = await this.nvim.mode
if (mode.mode != 'n' || mode.blocking) {
await this.nvim.command('stopinsert')
await this.nvim.call('feedkeys', [String.fromCharCode(27), 'in'])
}
await this.nvim.command('silent! %bwipeout!')
await this.wait(80)
}
public async pumvisible(): Promise<boolean> {
let res = await this.nvim.call('pumvisible', []) as number
return res == 1
}
public wait(ms = 30): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, ms)
})
}
public async visible(word: string, source?: string): Promise<boolean> {
await this.waitPopup()
let context = await this.nvim.getVar('coc#_context') as any
let items = context.candidates
if (!items) return false
let item = items.find(o => o.word == word)
if (!item || !item.user_data) return false
try {
let o = JSON.parse(item.user_data)
if (source && o.source !== source) {
return false
}
} catch (e) {
return false
}
return true
}
public async notVisible(word: string): Promise<boolean> {
let items = await this.getItems()
return items.findIndex(o => o.word == word) == -1
}
public async getItems(): Promise<VimCompleteItem[]> {
let visible = await this.pumvisible()
if (!visible) return []
let context = await this.nvim.getVar('coc#_context') as any
let items = context.candidates
return items || []
}
public async edit(file?: string): Promise<Buffer> {
if (!file || !path.isAbsolute(file)) {
file = path.join(__dirname, file ? file : `${uuid()}`)
}
let escaped = await this.nvim.call('fnameescape', file) as string
await this.nvim.command(`edit ${escaped}`)
await this.wait(60)
let bufnr = await this.nvim.call('bufnr', ['%']) as number
return this.nvim.createBuffer(bufnr)
}
public async createDocument(name?: string): Promise<Document> {
let buf = await this.edit(name)
let doc = workspace.getDocument(buf.id)
if (!doc) return await workspace.document
return doc
}
public async getMarkers(bufnr: number, ns: number): Promise<[number, number, number][]> {
return await this.nvim.call('nvim_buf_get_extmarks', [bufnr, ns, 0, -1, {}]) as [number, number, number][]
}
public async getCmdline(): Promise<string> {
let str = ''
for (let i = 1, l = 70; i < l; i++) {
let ch = await this.nvim.call('screenchar', [79, i])
if (ch == -1) break
str += String.fromCharCode(ch)
}
return str.trim()
}
public updateConfiguration(key: string, value: any): () => void {
let { configurations } = workspace as any
let curr = workspace.getConfiguration(key)
configurations.updateUserConfig({ [key]: value })
return () => {
configurations.updateUserConfig({ [key]: curr })
}
}
public async mockFunction(name: string, result: string | number | any): Promise<void> {
let content = `
function! ${name}(...)
return ${typeof result == 'number' ? result : JSON.stringify(result)}
endfunction
`
let file = await createTmpFile(content)
await this.nvim.command(`source ${file}`)
}
public async items(): Promise<VimCompleteItem[]> {
let context = await this.nvim.getVar('coc#_context')
return context['candidates'] || []
}
public async screenLine(line: number): Promise<string> {
let res = ''
for (let i = 1; i <= 80; i++) {
let ch = await this.nvim.call('screenchar', [line, i])
res = res + String.fromCharCode(ch)
}
return res
}
public async getWinLines(winid: number): Promise<string[]> {
return await this.nvim.eval(`getbufline(winbufnr(${winid}), 1, '$')`) as string[]
}
public async getFloat(): Promise<Window> {
let wins = await this.nvim.windows
let floatWin: Window
for (let win of wins) {
let f = await win.getVar('float')
if (f) floatWin = win
}
return floatWin
}
public async getFloats(): Promise<Window[]> {
let ids = await this.nvim.call('coc#float#get_float_win_list', [])
if (!ids) return []
return ids.map(id => this.nvim.createWindow(id))
}
}
export async function createTmpFile(content: string): Promise<string> {
let tmpFolder = path.join(os.tmpdir(), `coc-${process.pid}`)
if (!fs.existsSync(tmpFolder)) {
fs.mkdirSync(tmpFolder)
}
let filename = path.join(tmpFolder, uuid())
await util.promisify(fs.writeFile)(filename, content, 'utf8')
return filename
}
export default new Helper()

174
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/basicList.test.ts

@ -0,0 +1,174 @@ @@ -0,0 +1,174 @@
import { Neovim } from '@chemzqm/neovim'
import { URI } from 'vscode-uri'
import { Disposable, Range, Location } from 'vscode-languageserver-protocol'
import BasicList, { getFiletype, PreviewOptions } from '../../list/basic'
import manager from '../../list/manager'
import { ListItem } from '../../types'
import { disposeAll } from '../../util'
import helper from '../helper'
let nvim: Neovim
let disposables: Disposable[] = []
let previewOptions: PreviewOptions
let list: SimpleList
class SimpleList extends BasicList {
public name = 'simple'
public defaultAction: 'preview'
constructor(nvim: Neovim) {
super(nvim)
this.addAction('preview', async (_item, context) => {
await this.preview(previewOptions, context)
})
}
public loadItems(): Promise<ListItem[]> {
return Promise.resolve(['a', 'b', 'c'].map((s, idx) => {
return { label: s, location: Location.create('test:///a', Range.create(idx, 0, idx + 1, 0)) } as ListItem
}))
}
}
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
beforeEach(() => {
list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
})
afterEach(async () => {
disposeAll(disposables)
manager.reset()
await helper.reset()
})
describe('getFiletype()', () => {
it('should get filetype', async () => {
expect(getFiletype('javascriptreact')).toBe('javascript')
expect(getFiletype('typescriptreact')).toBe('typescript')
expect(getFiletype('foo.bar')).toBe('foo')
expect(getFiletype('foo')).toBe('foo')
})
})
describe('BasicList', () => {
async function doPreview(opts: PreviewOptions): Promise<number> {
previewOptions = opts
await manager.start(['--normal', 'simple'])
await manager.session.ui.ready
await manager.doAction('preview')
let res = await nvim.call('coc#list#has_preview') as number
expect(res).toBeGreaterThan(0)
let winid = await nvim.call('win_getid', [res])
return winid
}
describe('preview()', () => {
it('should preview lines', async () => {
await doPreview({ filetype: '', lines: ['foo', 'bar'] })
})
it('should preview with bufname', async () => {
await doPreview({
bufname: 't.js',
filetype: 'typescript',
lines: ['foo', 'bar']
})
})
it('should preview with range highlight', async () => {
let winid = await doPreview({
bufname: 't.js',
filetype: 'typescript',
lines: ['foo', 'bar'],
range: Range.create(0, 0, 0, 3)
})
let res = await nvim.call('getmatches', [winid])
expect(res.length).toBeGreaterThan(0)
})
})
describe('createAction()', () => {
it('should overwrite action', async () => {
let idx: number
list.createAction({
name: 'foo',
execute: () => { idx = 0 }
})
list.createAction({
name: 'foo',
execute: () => { idx = 1 }
})
await manager.start(['--normal', 'simple'])
await manager.session.ui.ready
await manager.doAction('foo')
expect(idx).toBe(1)
})
})
describe('jumpTo()', () => {
it('should jump to uri', async () => {
let uri = URI.file(__filename).toString()
await list.jumpTo(uri, 'edit')
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toMatch('basicList.test.ts')
})
it('should jump to location', async () => {
let uri = URI.file(__filename).toString()
let loc = Location.create(uri, Range.create(0, 0, 1, 0))
await list.jumpTo(loc, 'edit')
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toMatch('basicList.test.ts')
})
it('should jump to location with empty range', async () => {
let uri = URI.file(__filename).toString()
let loc = Location.create(uri, Range.create(0, 0, 0, 0))
await list.jumpTo(loc, 'edit')
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toMatch('basicList.test.ts')
})
})
describe('convertLocation()', () => {
it('should convert uri', async () => {
let uri = URI.file(__filename).toString()
let res = await list.convertLocation(uri)
expect(res.uri).toBe(uri)
})
it('should convert location with line', async () => {
let uri = URI.file(__filename).toString()
let res = await list.convertLocation({ uri, line: 'convertLocation()', text: 'convertLocation' })
expect(res.uri).toBe(uri)
res = await list.convertLocation({ uri, line: 'convertLocation()' })
expect(res.uri).toBe(uri)
})
it('should convert location with custom schema', async () => {
let uri = 'test:///foo'
let res = await list.convertLocation({ uri, line: 'convertLocation()'})
expect(res.uri).toBe(uri)
})
})
describe('quickfix action', () => {
it('should invoke quickfix action', async () => {
list.addLocationActions()
await manager.start(['--normal', 'simple', '-arg'])
await manager.session.ui.ready
await manager.session.ui.selectAll()
await manager.doAction('quickfix')
let res = await nvim.call('getqflist')
expect(res.length).toBeGreaterThan(1)
})
})
})

134
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/commandTask.test.ts

@ -0,0 +1,134 @@ @@ -0,0 +1,134 @@
import { Neovim } from '@chemzqm/neovim'
import path from 'path'
import { ListContext, ListTask } from '../../types'
import manager from '../../list/manager'
import helper, { createTmpFile } from '../helper'
import BasicList from '../../list/basic'
import { Disposable } from 'vscode-languageserver-protocol'
import { disposeAll } from '../../util'
class DataList extends BasicList {
public name = 'data'
public async loadItems(_context: ListContext): Promise<ListTask> {
let fsPath = await createTmpFile(`console.log('foo');console.log('');console.log('bar');`)
return this.createCommandTask({
cmd: 'node',
args: [fsPath],
cwd: path.dirname(fsPath),
onLine: line => {
if (!line) return undefined
return {
label: line
}
}
})
}
}
class SleepList extends BasicList {
public name = 'sleep'
public loadItems(_context: ListContext): Promise<ListTask> {
return Promise.resolve(this.createCommandTask({
cmd: 'sleep',
args: ['10'],
cwd: __dirname,
onLine: line => {
return {
label: line
}
}
}))
}
}
class StderrList extends BasicList {
public name = 'stderr'
public async loadItems(_context: ListContext): Promise<ListTask> {
let fsPath = await createTmpFile(`console.error('stderr');console.log('stdout')`)
return Promise.resolve(this.createCommandTask({
cmd: 'node',
args: [fsPath],
cwd: path.dirname(fsPath),
onLine: line => {
return {
label: line
}
}
}))
}
}
class ErrorTask extends BasicList {
public name = 'error'
public async loadItems(_context: ListContext): Promise<ListTask> {
return Promise.resolve(this.createCommandTask({
cmd: 'NOT_EXISTS',
args: [],
cwd: __dirname,
onLine: line => {
return {
label: line
}
}
}))
}
}
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
manager.reset()
await helper.reset()
})
describe('Command task', () => {
it('should not show stderr', async () => {
disposables.push(manager.registerList(new StderrList(nvim)))
await manager.start(['stderr'])
await manager.session.ui.ready
let lines = await nvim.call('getline', [1, '$']) as string[]
expect(lines).toEqual(['stdout'])
})
it('should not show error', async () => {
disposables.push(manager.registerList(new ErrorTask(nvim)))
await manager.start(['error'])
await helper.wait(100)
let res = await helper.getCmdline()
expect(res).toMatch('NOT_EXISTS ENOENT')
})
it('should create command task', async () => {
let list = new DataList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['data'])
await helper.wait(500)
let lines = await nvim.call('getline', [1, '$']) as string[]
expect(lines).toEqual(['foo', 'bar'])
})
it('should stop command task', async () => {
let list = new SleepList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['sleep'])
manager.session.stop()
})
it('should show error for bad key', async () => {
let list = new DataList(nvim)
list.config.fixKey('<X-a>')
await helper.wait(500)
let msg = await helper.getCmdline()
expect(msg).toMatch('not supported')
})
})

32
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/formatting.test.ts

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
import { formatPath, UnformattedListItem, formatListItems } from '../../list/formatting'
describe('formatPath()', () => {
it('should format path', async () => {
expect(formatPath('hidden', 'path')).toBe('')
expect(formatPath('full', __filename)).toMatch('formatting.test.ts')
expect(formatPath('short', __filename)).toMatch('formatting.test.ts')
expect(formatPath('filename', __filename)).toMatch('formatting.test.ts')
})
})
describe('formatListItems', () => {
it('should format list items', async () => {
expect(formatListItems(false, [])).toEqual([])
let items: UnformattedListItem[] = [{
label: ['a', 'b', 'c']
}]
expect(formatListItems(false, items)).toEqual([{
label: 'a\tb\tc'
}])
items = [{
label: ['a', 'b', 'c']
}, {
label: ['foo', 'bar', 'go']
}]
expect(formatListItems(true, items)).toEqual([{
label: 'a \tb \tc '
}, {
label: 'foo\tbar\tgo'
}])
})
})

79
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/location.test.ts

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
import { Neovim } from '@chemzqm/neovim'
import manager from '../../list/manager'
import { Range } from 'vscode-languageserver-protocol'
import events from '../../events'
import helper from '../helper'
let nvim: Neovim
const locations: any[] = [{
filename: __filename,
range: Range.create(0, 0, 0, 6),
text: 'foo'
}, {
filename: __filename,
range: Range.create(2, 0, 2, 6),
text: 'Bar'
}, {
filename: __filename,
range: Range.create(3, 0, 4, 6),
text: 'multiple'
}]
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
await nvim.setVar('coc_jump_locations', locations)
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
manager.reset()
await helper.reset()
await helper.wait(100)
})
describe('list commands', () => {
it('should highlight ranges', async () => {
await manager.start(['--normal', '--auto-preview', 'location'])
await manager.session.ui.ready
await helper.wait(200)
manager.prompt.cancel()
await nvim.command('wincmd k')
let name = await nvim.eval('bufname("%")')
expect(name).toMatch('location.test.ts')
let res = await nvim.call('getmatches')
expect(res.length).toBe(1)
})
it('should change highlight on cursor move', async () => {
await manager.start(['--normal', '--auto-preview', 'location'])
await manager.session.ui.ready
await helper.wait(200)
await nvim.command('exe 2')
let bufnr = await nvim.eval('bufnr("%")')
await events.fire('CursorMoved', [bufnr, [2, 1]])
await helper.wait(300)
await nvim.command('wincmd k')
let res = await nvim.call('getmatches')
expect(res.length).toBe(1)
expect(res[0]['pos1']).toEqual([3, 1, 6])
})
it('should highlight multiple line range', async () => {
await manager.start(['--normal', '--auto-preview', 'location'])
await manager.session.ui.ready
await helper.wait(200)
await nvim.command('exe 3')
let bufnr = await nvim.eval('bufnr("%")')
await events.fire('CursorMoved', [bufnr, [2, 1]])
await helper.wait(300)
await nvim.command('wincmd k')
let res = await nvim.call('getmatches')
expect(res.length).toBe(1)
expect(res[0]['pos1']).toBeDefined()
expect(res[0]['pos2']).toBeDefined()
})
})

509
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/manager.test.ts

@ -0,0 +1,509 @@ @@ -0,0 +1,509 @@
import { Neovim } from '@chemzqm/neovim'
import path from 'path'
import manager from '../../list/manager'
import events from '../../events'
import { QuickfixItem, IList, ListItem } from '../../types'
import helper from '../helper'
let nvim: Neovim
const locations: ReadonlyArray<QuickfixItem> = [{
filename: __filename,
col: 2,
lnum: 1,
text: 'foo'
}, {
filename: __filename,
col: 1,
lnum: 2,
text: 'Bar'
}, {
filename: __filename,
col: 1,
lnum: 3,
text: 'option'
}]
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
await nvim.setVar('coc_jump_locations', locations)
})
afterEach(async () => {
manager.reset()
await helper.reset()
})
afterAll(async () => {
await helper.shutdown()
})
describe('list', () => {
describe('events', () => {
it('should cancel and enable prompt', async () => {
let winid = await nvim.call('win_getid')
await manager.start(['location'])
await manager.session.ui.ready
await nvim.call('win_gotoid', [winid])
await helper.wait(50)
let res = await nvim.call('coc#prompt#activated')
expect(res).toBe(0)
await nvim.command('wincmd p')
await helper.wait(50)
res = await nvim.call('coc#prompt#activated')
expect(res).toBe(1)
})
})
describe('list commands', () => {
it('should be activated', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(50)
expect(manager.isActivated).toBe(true)
let line = await nvim.getLine()
expect(line).toMatch(/manager.test.ts/)
})
it('should get list names', () => {
let names = manager.names
expect(names.length > 0).toBe(true)
})
it('should resume list', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await helper.wait(30)
await nvim.eval('feedkeys("j", "in")')
await helper.wait(30)
let line = await nvim.call('line', '.')
expect(line).toBe(2)
await manager.cancel()
await helper.wait(30)
await manager.resume()
await helper.wait(30)
line = await nvim.call('line', '.')
expect(line).toBe(2)
})
it('should not quit list with --no-quit', async () => {
await manager.start(['--normal', '--no-quit', 'location'])
await manager.session.ui.ready
let winnr = await nvim.eval('win_getid()') as number
await manager.doAction()
await helper.wait(100)
let wins = await nvim.windows
let ids = wins.map(o => o.id)
expect(ids).toContain(winnr)
})
it('should do default action for first item', async () => {
await manager.start(['--normal', '--first', 'location'])
await helper.wait(300)
let name = await nvim.eval('bufname("%")') as string
let filename = path.basename(__filename)
expect(name.includes(filename)).toBe(true)
let pos = await nvim.eval('getcurpos()')
expect(pos[1]).toBe(1)
expect(pos[2]).toBe(2)
})
it('should goto next & previous', async () => {
await manager.start(['location'])
await manager.session?.ui.ready
await helper.wait(60)
await manager.doAction()
await manager.cancel()
let bufname = await nvim.eval('expand("%:p")')
expect(bufname).toMatch('manager.test.ts')
await manager.next()
let line = await nvim.call('line', '.')
expect(line).toBe(2)
await helper.wait(60)
await manager.previous()
line = await nvim.call('line', '.')
expect(line).toBe(1)
})
it('should parse arguments', async () => {
await manager.start(['--input=test', '--normal', '--no-sort', '--ignore-case', '--top', '--number-select', '--auto-preview', '--strict', 'location'])
await helper.wait(30)
let opts = manager.session?.listOptions
expect(opts).toEqual({
numberSelect: true,
autoPreview: true,
first: false,
input: 'test',
interactive: false,
matcher: 'strict',
ignorecase: true,
position: 'top',
mode: 'normal',
noQuit: false,
sort: false
})
})
})
describe('list options', () => {
it('should respect input option', async () => {
await manager.start(['--input=foo', 'location'])
await manager.session.ui.ready
await helper.wait(30)
let line = await helper.getCmdline()
expect(line).toMatch('foo')
expect(manager.isActivated).toBe(true)
})
it('should respect regex filter', async () => {
await manager.start(['--input=f.o', '--regex', 'location'])
await helper.wait(200)
let item = await manager.session?.ui.item
expect(item.label).toMatch('foo')
})
it('should respect normal option', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
let line = await helper.getCmdline()
expect(line).toBe('')
})
it('should respect nosort option', async () => {
await manager.start(['--ignore-case', '--no-sort', 'location'])
await manager.session.ui.ready
expect(manager.isActivated).toBe(true)
await nvim.input('oo')
await helper.wait(100)
let line = await nvim.call('getline', ['.'])
expect(line).toMatch('foo')
})
it('should respect ignorecase option', async () => {
await manager.start(['--ignore-case', '--strict', 'location'])
await manager.session.ui.ready
expect(manager.isActivated).toBe(true)
await nvim.input('bar')
await helper.wait(100)
let n = manager.session?.ui.length
expect(n).toBe(1)
let line = await nvim.line
expect(line).toMatch('Bar')
})
it('should respect top option', async () => {
await manager.start(['--top', 'location'])
await manager.session.ui.ready
let nr = await nvim.call('winnr')
expect(nr).toBe(1)
})
it('should respect number select option', async () => {
await manager.start(['--number-select', 'location'])
await manager.session.ui.ready
await helper.wait(100)
await nvim.eval('feedkeys("2", "in")')
await helper.wait(100)
let lnum = locations[1].lnum
let curr = await nvim.call('line', '.')
expect(lnum).toBe(curr)
})
it('should respect auto preview option', async () => {
await manager.start(['--auto-preview', 'location'])
await helper.wait(300)
let previewWinnr = await nvim.call('coc#list#has_preview')
expect(previewWinnr).toBe(2)
let bufnr = await nvim.call('winbufnr', previewWinnr)
let buf = nvim.createBuffer(bufnr)
let name = await buf.name
expect(name).toMatch('manager.test.ts')
await nvim.eval('feedkeys("j", "in")')
await helper.wait(100)
let winnr = await nvim.call('coc#list#has_preview')
expect(winnr).toBe(previewWinnr)
})
it('should respect tab option', async () => {
await manager.start(['--tab', '--auto-preview', 'location'])
await manager.session.ui.ready
await helper.wait(200)
await nvim.command('wincmd l')
let previewwindow = await nvim.eval('w:previewwindow')
expect(previewwindow).toBe(1)
})
})
describe('list configuration', () => {
it('should change indicator', async () => {
helper.updateConfiguration('list.indicator', '>>')
await manager.start(['location'])
await helper.wait(200)
let line = await helper.getCmdline()
expect(line).toMatch('>>')
})
it('should split right for preview window', async () => {
helper.updateConfiguration('list.previewSplitRight', true)
let win = await nvim.window
await manager.start(['location'])
await helper.wait(100)
await manager.doAction('preview')
await helper.wait(100)
manager.prompt.cancel()
await helper.wait(10)
await nvim.call('win_gotoid', [win.id])
await nvim.command('wincmd l')
let curr = await nvim.window
let isPreview = await curr.getVar('previewwindow')
expect(isPreview).toBe(1)
helper.updateConfiguration('list.previewSplitRight', false)
})
it('should toggle selection mode', async () => {
await manager.start(['--normal', 'location'])
await manager.session?.ui.ready
await nvim.input('V')
await helper.wait(30)
await nvim.input('1')
await helper.wait(30)
await nvim.input('j')
await helper.wait(100)
await manager.session?.ui.toggleSelection()
let items = await manager.session?.ui.getItems()
expect(items.length).toBe(2)
})
it('should change next/previous keymap', async () => {
helper.updateConfiguration('list.nextKeymap', '<tab>')
helper.updateConfiguration('list.previousKeymap', '<s-tab>')
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(100)
await nvim.eval('feedkeys("\\<tab>", "in")')
await helper.wait(100)
let line = await nvim.line
expect(line).toMatch('Bar')
await nvim.eval('feedkeys("\\<s-tab>", "in")')
await helper.wait(100)
line = await nvim.line
expect(line).toMatch('foo')
})
it('should respect mouse events', async () => {
async function setMouseEvent(line: number): Promise<void> {
let winid = manager.session?.ui.winid
await nvim.command(`let v:mouse_winid = ${winid}`)
await nvim.command(`let v:mouse_lnum = ${line}`)
await nvim.command(`let v:mouse_col = 1`)
}
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await helper.wait(100)
await setMouseEvent(1)
await manager.onNormalInput('<LeftMouse>')
await setMouseEvent(2)
await manager.onNormalInput('<LeftDrag>')
await setMouseEvent(3)
await manager.onNormalInput('<LeftRelease>')
await helper.wait(100)
let items = await manager.session?.ui.getItems()
expect(items.length).toBe(3)
})
it('should toggle preview', async () => {
await manager.start(['--normal', '--auto-preview', 'location'])
await manager.session.ui.ready
await helper.wait(100)
await manager.togglePreview()
await helper.wait(100)
await manager.togglePreview()
await helper.wait(100)
let has = await nvim.call('coc#list#has_preview')
expect(has).toBeGreaterThan(0)
})
it('should show help of current list', async () => {
await manager.start(['--normal', '--auto-preview', 'location'])
await helper.wait(200)
await manager.session?.showHelp()
let bufname = await nvim.call('bufname', '%')
expect(bufname).toBe('[LIST HELP]')
})
it('should resolve list item', async () => {
let list: IList = {
name: 'test',
actions: [{
name: 'open', execute: _item => {
// noop
}
}],
defaultAction: 'open',
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }]),
resolveItem: item => {
item.label = item.label.slice(0, 1)
return Promise.resolve(item)
}
}
let disposable = manager.registerList(list)
await manager.start(['--normal', 'test'])
await manager.session.ui.ready
await helper.wait(50)
let line = await nvim.line
expect(line).toBe('f')
disposable.dispose()
})
})
describe('descriptions', () => {
it('should get descriptions', async () => {
let res = manager.descriptions
expect(res).toBeDefined()
expect(res.location).toBeDefined()
})
})
describe('loadItems()', () => {
it('should load items for list', async () => {
let res = await manager.loadItems('location')
expect(res.length).toBeGreaterThan(0)
; (manager as any).lastSession = undefined
manager.toggleMode()
manager.stop()
})
})
describe('onInsertInput()', () => {
it('should handle insert input', async () => {
await manager.onInsertInput('k')
await manager.onInsertInput('<LeftMouse>')
await manager.start(['--number-select', 'location'])
await manager.session.ui.ready
await manager.onInsertInput('1')
await helper.wait(300)
let bufname = await nvim.call('expand', ['%:p'])
expect(bufname).toMatch('manager.test.ts')
})
it('should ignore invalid input', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await manager.onInsertInput('<X-y>')
await manager.onInsertInput(String.fromCharCode(65533))
await manager.onInsertInput(String.fromCharCode(30))
expect(manager.isActivated).toBe(true)
})
it('should ignore <plug> insert', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<plug>x", "in")')
await helper.wait(50)
expect(manager.isActivated).toBe(true)
})
})
describe('parseArgs()', () => {
it('should show error for bad option', async () => {
manager.parseArgs(['$x', 'location'])
let msg = await helper.getCmdline()
expect(msg).toMatch('Invalid list option')
})
it('should show error for option not exists', async () => {
manager.parseArgs(['-xyz', 'location'])
let msg = await helper.getCmdline()
expect(msg).toMatch('Invalid option')
})
it('should show error for interactive with list not support interactive', async () => {
manager.parseArgs(['--interactive', 'location'])
let msg = await helper.getCmdline()
expect(msg).toMatch('not supported')
})
})
describe('resume()', () => {
it('should resume by name', async () => {
await events.fire('FocusGained', [])
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(50)
await manager.session.hide()
await helper.wait(100)
await manager.resume('location')
expect(manager.isActivated).toBe(true)
})
})
describe('start()', () => {
it('should show error when loadItems throws', async () => {
let list: IList = {
name: 'test',
actions: [{
name: 'open',
execute: (_item: ListItem) => {
}
}],
defaultAction: 'open',
loadItems: () => {
throw new Error('test error')
}
}
manager.registerList(list)
await manager.start(['test'])
await helper.wait(50)
let msg = await helper.getCmdline()
expect(msg).toMatch('test error')
})
})
describe('first(), last()', () => {
it('should get session by name', async () => {
let last: string
let list: IList = {
name: 'test',
actions: [{
name: 'open',
execute: (item: ListItem) => {
last = item.label
}
}],
defaultAction: 'open',
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }])
}
manager.registerList(list)
await manager.start(['test'])
await manager.session.ui.ready
await manager.first('a')
await manager.last('a')
await manager.first('test')
expect(last).toBe('foo')
await manager.last('test')
expect(last).toBe('bar')
})
})
describe('registerList()', () => {
it('should recreat list', async () => {
let list: IList = {
name: 'test',
actions: [{
name: 'open', execute: _item => {
// noop
}
}],
defaultAction: 'open',
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }])
}
manager.registerList(list)
helper.updateConfiguration('list.source.test.defaultAction', 'open')
let disposable = manager.registerList(list)
disposable.dispose()
await helper.wait(30)
let msg = await helper.getCmdline()
expect(msg).toMatch('recreated')
})
})
})

903
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/mappings.test.ts

@ -0,0 +1,903 @@ @@ -0,0 +1,903 @@
import { Neovim } from '@chemzqm/neovim'
import { CancellationToken } from 'vscode-jsonrpc'
import BasicList from '../../list/basic'
import manager from '../../list/manager'
import window from '../../window'
import { ListContext, IList, ListItem, QuickfixItem } from '../../types'
import helper from '../helper'
class TestList extends BasicList {
public name = 'test'
public timeout = 3000
public text = 'test'
public detail = 'detail'
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListItem[]> {
return new Promise(resolve => {
let timer = setTimeout(() => {
resolve([{ label: this.text }])
}, this.timeout)
token.onCancellationRequested(() => {
if (timer) {
clearTimeout(timer)
resolve([])
}
})
})
}
}
let nvim: Neovim
const locations: ReadonlyArray<QuickfixItem> = [{
filename: __filename,
col: 2,
lnum: 1,
text: 'foo'
}, {
filename: __filename,
col: 1,
lnum: 2,
text: 'Bar'
}, {
filename: __filename,
col: 1,
lnum: 3,
text: 'option'
}]
const lineList: IList = {
name: 'lines',
actions: [{
name: 'open',
execute: async item => {
await window.moveTo({
line: (item as ListItem).data.line,
character: 0
})
// noop
}
}],
defaultAction: 'open',
async loadItems(_context, _token): Promise<ListItem[]> {
let lines = []
for (let i = 0; i < 100; i++) {
lines.push(i.toString())
}
return lines.map((line, idx) => ({
label: line,
data: { line: idx }
}))
}
}
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
await nvim.setVar('coc_jump_locations', locations)
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
manager.reset()
await helper.reset()
})
describe('list normal mappings', () => {
it('should tabopen by t', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(100)
let nr = await nvim.call('tabpagenr')
expect(nr).toBe(2)
})
it('should open by <cr>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<cr>", "in")')
await helper.wait(100)
let bufname = await nvim.call('expand', ['%:p'])
expect(bufname).toMatch('mappings.test.ts')
})
it('should stop by <C-c>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-c>", "in")')
await helper.wait(50)
let loading = manager.session?.worker.isLoading
expect(loading).toBe(false)
})
it('should jump back by <C-o>', async () => {
let doc = await helper.createDocument()
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-o>", "in")')
await helper.wait(50)
let bufnr = await nvim.call('bufnr', ['%'])
expect(bufnr).toBe(doc.bufnr)
})
it('should scroll preview window by <C-e>, <C-y>', async () => {
await helper.createDocument()
await manager.start(['--auto-preview', '--normal', 'location'])
await manager.session.ui.ready
await helper.wait(100)
let winnr = await nvim.call('coc#list#has_preview') as number
expect(winnr).toBeGreaterThan(0)
let winid = await nvim.call('win_getid', [winnr])
await nvim.eval('feedkeys("\\<C-e>", "in")')
await helper.wait(50)
let res = await nvim.call('getwininfo', [winid])
expect(res[0].topline).toBeGreaterThan(1)
await nvim.eval('feedkeys("\\<C-y>", "in")')
await helper.wait(50)
res = await nvim.call('getwininfo', [winid])
expect(res[0].topline).toBeLessThan(7)
})
it('should insert command by :', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys(":", "in")')
await helper.wait(50)
await nvim.eval('feedkeys("let g:x = 1\\<cr>", "in")')
await helper.wait(50)
let res = await nvim.getVar('x')
expect(res).toBe(1)
})
it('should select action by <tab>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<tab>", "in")')
await helper.wait(100)
await nvim.input('t')
await helper.wait(300)
let nr = await nvim.call('tabpagenr')
expect(nr).toBe(2)
})
it('should preview by p', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await helper.wait(50)
await nvim.eval('feedkeys("p", "in")')
await helper.wait(200)
let winnr = await nvim.call('coc#list#has_preview')
expect(winnr).toBe(2)
})
it('should stop task by <C-c>', async () => {
let disposable = manager.registerList(new TestList(nvim))
let p = manager.start(['--normal', 'test'])
await helper.wait(200)
await nvim.input('<C-c>')
await helper.wait(200)
await p
let len = manager.session?.ui.length
expect(len).toBe(0)
disposable.dispose()
})
it('should cancel list by <esc>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<esc>", "in")')
await helper.wait(200)
expect(manager.isActivated).toBe(false)
})
it('should reload list by <C-l>', async () => {
let list = new TestList(nvim)
list.timeout = 0
let disposable = manager.registerList(list)
await manager.start(['--normal', 'test'])
await manager.session.ui.ready
list.text = 'new'
await nvim.input('<C-l>')
await helper.wait(30)
let line = await nvim.line
expect(line).toMatch('new')
disposable.dispose()
})
it('should select all items by <C-a>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.input('<C-a>')
await helper.wait(30)
let selected = manager.session?.ui.selectedItems
expect(selected.length).toBe(locations.length)
})
it('should toggle selection <space>', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<space>", "in")')
await helper.wait(100)
let selected = manager.session?.ui.selectedItems
expect(selected.length).toBe(1)
await nvim.eval('feedkeys("k", "in")')
await helper.wait(100)
await nvim.eval('feedkeys("\\<space>", "in")')
await helper.wait(100)
selected = manager.session?.ui.selectedItems
expect(selected.length).toBe(0)
})
it('should change to insert mode by i, o, a', async () => {
let keys = ['i', 'I', 'o', 'O', 'a', 'A']
for (let key of keys) {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await helper.wait(50)
await nvim.eval(`feedkeys("${key}", "in")`)
await helper.wait(100)
let mode = manager.prompt.mode
expect(mode).toBe('insert')
}
})
it('should show help by ?', async () => {
await manager.start(['--normal', 'location'])
await helper.wait(30)
await nvim.eval('feedkeys("?", "in")')
await helper.wait(30)
await nvim.input('<CR>')
await helper.wait(100)
let bufname = await nvim.call('bufname', '%')
expect(bufname).toBe('[LIST HELP]')
})
it('should drop by d', async () => {
await manager.start(['--normal', 'location'])
await helper.wait(30)
await nvim.eval('feedkeys("d", "in")')
await helper.wait(100)
let nr = await nvim.call('tabpagenr')
expect(nr).toBe(1)
})
it('should split by s', async () => {
await manager.start(['--normal', 'location'])
await helper.wait(30)
await nvim.eval('feedkeys("s", "in")')
await helper.wait(100)
let nr = await nvim.call('winnr')
expect(nr).toBe(1)
})
})
describe('list insert mappings', () => {
it('should open by <cr>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<cr>", "in")')
await helper.wait(100)
let bufname = await nvim.call('expand', ['%:p'])
expect(bufname).toMatch('mappings.test.ts')
})
it('should paste input by <C-v>', async () => {
await nvim.call('setreg', ['*', 'foo'])
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-v>", "in")')
await helper.wait(100)
let input = manager.prompt.input
expect(input).toBe('foo')
})
it('should insert register content by <C-r>', async () => {
await nvim.call('setreg', ['*', 'foo'])
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-r>", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("*", "in")')
await helper.wait(100)
let input = manager.prompt.input
expect(input).toBe('foo')
await nvim.eval('feedkeys("\\<C-r>", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("<", "in")')
await helper.wait(100)
input = manager.prompt.input
expect(input).toBe('foo')
manager.prompt.reset()
})
it('should cancel by <esc>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<esc>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(false)
})
it('should select action by <tab>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(100)
nvim.call('eval', 'feedkeys("\\<tab>", "in")', true)
await helper.wait(100)
await nvim.input('t')
await helper.wait(500)
let pages = await nvim.tabpages
expect(pages.length).toBe(2)
})
it('should select action for visual selected items', async () => {
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await helper.wait(100)
await nvim.input('V')
await helper.wait(30)
await nvim.input('2')
await helper.wait(30)
await nvim.input('j')
await helper.wait(30)
await manager.doAction('tabe')
let nr = await nvim.call('tabpagenr')
expect(nr).toBeGreaterThan(3)
})
it('should stop loading by <C-c>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-c>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
it('should reload by <C-l>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-l>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
it('should change to normal mode by <C-o>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-o>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(true)
let line = await helper.getCmdline()
expect(line).toBe('')
})
it('should select line by <down> and <up>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<down>", "in")')
await helper.wait(50)
await nvim.eval('feedkeys("\\<up>", "in")')
await helper.wait(50)
expect(manager.isActivated).toBe(true)
let line = await nvim.line
expect(line).toMatch('foo')
})
it('should move cursor by <left> and <right>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("f", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("\\<left>", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("\\<left>", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("a", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("\\<right>", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("\\<right>", "in")')
await helper.wait(10)
await nvim.eval('feedkeys("c", "in")')
await helper.wait(10)
let input = manager.prompt.input
expect(input).toBe('afc')
})
it('should move cursor by <end> and <home>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<home>", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<end>a", "in")')
await helper.wait(30)
let input = manager.prompt.input
expect(input).toBe('a')
})
it('should move cursor by <PageUp> <PageDown> <C-d>', async () => {
let disposable = manager.registerList(lineList)
await manager.start(['lines'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<PageDown>", "in")')
await helper.wait(100)
let line = await nvim.eval('line(".")')
expect(line).toBeGreaterThan(1)
await nvim.eval('feedkeys("\\<PageUp>", "in")')
await helper.wait(100)
await nvim.eval('feedkeys("\\<C-d>", "in")')
await helper.wait(100)
disposable.dispose()
})
it('should scroll window by <C-f> and <C-b>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(30)
await nvim.eval('feedkeys("\\<C-f>", "in")')
await helper.wait(100)
await nvim.eval('feedkeys("\\<C-b>", "in")')
await helper.wait(100)
})
it('should change input by <Backspace>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("f", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<Backspace>", "in")')
await helper.wait(30)
let input = manager.prompt.input
expect(input).toBe('')
})
it('should change input by <C-x>', async () => {
let revert = helper.updateConfiguration('list.insertMappings', {
'<C-b>': 'prompt:removetail',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("foo", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<C-a>", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<C-b>", "in")')
await helper.wait(30)
let input = manager.prompt.input
revert()
expect(input).toBe('')
})
it('should change input by <C-h>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("f", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<C-h>", "in")')
await helper.wait(30)
let input = manager.prompt.input
expect(input).toBe('')
})
it('should change input by <C-w>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-w>", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("f", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("a", "in")')
await helper.wait(30)
await nvim.eval('feedkeys("\\<C-w>", "in")')
await helper.wait(30)
let input = manager.prompt.input
expect(input).toBe('')
})
it('should change input by <C-u>', async () => {
await manager.start(['--input=a', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-u>", "in")')
await helper.wait(30)
let input = manager.prompt.input
expect(input).toBe('')
})
it('should change input by <C-n> and <C-p>', async () => {
async function session(input: string): Promise<void> {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("${input}", "in")`)
await helper.wait(100)
await manager.cancel()
}
await session('foo')
await session('bar')
await manager.start(['location'])
await manager.session.ui.ready
await helper.wait(50)
await nvim.eval('feedkeys("\\<C-n>", "in")')
await helper.wait(100)
let input = manager.prompt.input
expect(input.length).toBeGreaterThan(0)
await nvim.eval('feedkeys("\\<C-p>", "in")')
await helper.wait(100)
input = manager.prompt.input
expect(input.length).toBeGreaterThan(0)
})
it('should change matcher by <C-s>', async () => {
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-s>", "in")')
await helper.wait(10)
let matcher = manager.session?.listOptions.matcher
expect(matcher).toBe('strict')
await nvim.eval('feedkeys("\\<C-s>", "in")')
await helper.wait(10)
matcher = manager.session?.listOptions.matcher
expect(matcher).toBe('regex')
await nvim.eval('feedkeys("f", "in")')
await helper.wait(30)
let len = manager.session?.ui.length
expect(len).toBeGreaterThan(0)
})
})
describe('evalExpression', () => {
it('should throw for bad expression', async () => {
let revert = helper.updateConfiguration('list.normalMappings', {
t: 'expr',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(30)
revert()
let msg = await helper.getCmdline()
expect(msg).toMatch('Invalid list mapping expression')
})
it('should show help', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'do:help',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let bufname = await nvim.call('bufname', ['%'])
expect(bufname).toMatch('[LIST HELP]')
})
it('should exit list', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'do:exit',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
expect(manager.isActivated).toBe(false)
})
it('should cancel prompt', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'do:cancel',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let res = await nvim.call('coc#prompt#activated')
expect(res).toBe(0)
})
it('should jump back', async () => {
let doc = await helper.createDocument()
helper.updateConfiguration('list.normalMappings', {
t: 'do:jumpback',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let bufnr = await nvim.call('bufnr', ['%'])
expect(bufnr).toBe(doc.bufnr)
})
it('should invoke normal command', async () => {
let revert = helper.updateConfiguration('list.normalMappings', {
x: 'normal!:G'
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("x", "in")`)
await helper.wait(50)
revert()
let lnum = await nvim.call('line', ['.'])
expect(lnum).toBeGreaterThan(1)
})
it('should toggle, scroll preview', async () => {
let revert = helper.updateConfiguration('list.normalMappings', {
'<space>': 'do:toggle',
a: 'do:toggle',
b: 'do:previewtoggle',
c: 'do:previewup',
d: 'do:previewdown',
e: 'prompt:insertregister',
f: 'do:stop',
g: 'do:togglemode',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys(" ", "in")`)
await helper.wait(50)
for (let key of ['a', 'b', 'c', 'd', 'e', 'f', 'g']) {
await nvim.eval(`feedkeys("${key}", "in")`)
await helper.wait(50)
}
revert()
expect(manager.isActivated).toBe(true)
})
it('should show error when action not exists', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'do:invalid',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let cmd = await helper.getCmdline()
expect(cmd).toMatch('not supported')
})
it('should show error when prompt action not exists', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'prompt:invalid',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let cmd = await helper.getCmdline()
expect(cmd).toMatch('not supported')
})
it('should show error for invalid expression ', async () => {
helper.updateConfiguration('list.normalMappings', {
t: 'x:y',
})
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("t", "in")')
await helper.wait(50)
let cmd = await helper.getCmdline()
expect(cmd).toMatch('Invalid expression')
})
})
describe('User mappings', () => {
it('should show warning for invalid key', async () => {
let revert = helper.updateConfiguration('list.insertMappings', {
xy: 'action:tabe',
})
await helper.wait(30)
let msg = await helper.getCmdline()
revert()
await nvim.command('echo ""')
expect(msg).toMatch('Invalid list mappings key')
revert = helper.updateConfiguration('list.insertMappings', {
'<M-x>': 'action:tabe',
})
await helper.wait(30)
msg = await helper.getCmdline()
revert()
expect(msg).toMatch('Invalid list mappings key')
})
it('should execute action keymap', async () => {
await helper.wait(200)
let revert = helper.updateConfiguration('list.insertMappings', {
'<C-d>': 'action:tabe',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-d>", "in")`)
await helper.wait(200)
let nr = await nvim.call('tabpagenr')
expect(nr).toBe(2)
revert()
})
it('should execute expr keymap', async () => {
await helper.mockFunction('TabOpen', 'tabe')
helper.updateConfiguration('list.insertMappings', {
'<C-t>': 'expr:TabOpen',
})
helper.updateConfiguration('list.normalMappings', {
t: 'expr:TabOpen',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-t>", "in")`)
await helper.wait(100)
let nr = await nvim.call('tabpagenr')
expect(nr).toBe(2)
await manager.start(['--normal', 'location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("t", "in")`)
await helper.wait(100)
nr = await nvim.call('tabpagenr')
expect(nr).toBe(3)
})
it('should execute do mappings', async () => {
helper.updateConfiguration('list.previousKeymap', '<c-j>')
helper.updateConfiguration('list.nextKeymap', '<c-k>')
helper.updateConfiguration('list.insertMappings', {
'<C-r>': 'do:refresh',
'<C-a>': 'do:selectall',
'<C-s>': 'do:switch',
'<C-l>': 'do:cancel',
'<C-t>': 'do:toggle',
'<C-n>': 'do:next',
'<C-p>': 'do:previous',
'<C-x>': 'do:defaultaction',
'<C-h>': 'do:help',
'<C-d>': 'do:exit',
'<C-b>': 'do:toggleMode'
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-r>", "in")')
await helper.wait(30)
expect(manager.isActivated).toBe(true)
await nvim.eval('feedkeys("\\<C-a>", "in")')
await helper.wait(30)
expect(manager.session?.ui.selectedItems.length).toBe(locations.length)
await nvim.eval('feedkeys("\\<C-s>", "in")')
await helper.wait(30)
expect(manager.session?.listOptions.matcher).toBe('strict')
await nvim.eval('feedkeys("\\<C-n>", "in")')
await helper.wait(30)
let item = await manager.session?.ui.item
expect(item.label).toMatch(locations[1].text)
await nvim.eval('feedkeys("\\<C-p>", "in")')
await helper.wait(30)
item = await manager.session?.ui.item
expect(item.label).toMatch(locations[0].text)
await nvim.eval('feedkeys("\\<C-x>", "in")')
await helper.wait(30)
expect(manager.isActivated).toBe(false)
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-l>", "in")')
await helper.wait(50)
let res = await nvim.call('coc#prompt#activated')
expect(res).toBe(0)
await manager.session.hide()
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("?", "in")')
await helper.wait(30)
await nvim.input('<CR>')
await manager.cancel()
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-d>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(false)
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval('feedkeys("\\<C-b>", "in")')
await helper.wait(100)
expect(manager.isActivated).toBe(true)
await nvim.call('coc#prompt#stop_prompt', ['list'])
}, 20000)
it('should execute prompt mappings', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-p>': 'prompt:previous',
'<C-n>': 'prompt:next',
'<C-a>': 'prompt:start',
'<C-e>': 'prompt:end',
'<Left>': 'prompt:left',
'<Right>': 'prompt:right',
'<backspace>': 'prompt:deleteforward',
'<C-x>': 'prompt:deletebackward',
'<C-k>': 'prompt:removetail',
'<C-u>': 'prompt:removeahead',
})
await manager.start(['location'])
await manager.session.ui.ready
for (let key of ['<C-p>', '<C-n>', '<C-a>', '<C-e>', '<Left>', '<Right>', '<backspace>', '<C-x>', '<C-k>', '<C-u>']) {
await nvim.input(key)
await helper.wait(30)
}
expect(manager.isActivated).toBe(true)
})
it('should execute feedkeys keymap', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-f>': 'feedkeys:\\<C-f>',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-f>", "in")`)
await helper.wait(30)
let line = await nvim.call('line', '.')
expect(line).toBe(locations.length)
})
it('should execute normal keymap', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-g>': 'normal:G',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-g>", "in")`)
await helper.wait(30)
let line = await nvim.call('line', '.')
expect(line).toBe(locations.length)
})
it('should execute command keymap', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-w>': 'command:wincmd p',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-w>", "in")`)
await helper.wait(30)
expect(manager.isActivated).toBe(true)
let winnr = await nvim.call('winnr')
expect(winnr).toBe(1)
})
it('should execute call keymap', async () => {
await helper.mockFunction('Test', 1)
helper.updateConfiguration('list.insertMappings', {
'<C-t>': 'call:Test',
})
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-t>", "in")`)
await helper.wait(30)
expect(manager.isActivated).toBe(true)
})
it('should insert clipboard register to prompt', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-r>': 'prompt:paste',
})
await nvim.command('let @* = "foobar"')
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-r>", "in")`)
await helper.wait(100)
let { input } = manager.prompt
expect(input).toMatch('foobar')
await nvim.command('let @* = ""')
await nvim.eval(`feedkeys("\\<C-r>", "in")`)
await helper.wait(100)
expect(manager.prompt.input).toMatch('foobar')
})
it('should insert text from default register to prompt', async () => {
helper.updateConfiguration('list.insertMappings', {
'<C-v>': 'eval:@@',
})
await nvim.command('let @@ = "bar"')
await manager.start(['location'])
await manager.session.ui.ready
await nvim.eval(`feedkeys("\\<C-v>", "in")`)
await helper.wait(200)
let { input } = manager.prompt
expect(input).toMatch('bar')
})
})

246
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/session.test.ts

@ -0,0 +1,246 @@ @@ -0,0 +1,246 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import BasicList from '../../list/basic'
import manager from '../../list/manager'
import ListSession from '../../list/session'
import { ListItem } from '../../types'
import { disposeAll } from '../../util'
import helper from '../helper'
let labels: string[] = []
let lastItem: string
let lastItems: ListItem[]
class SimpleList extends BasicList {
public name = 'simple'
public detail = 'detail'
public options = [{
name: 'foo',
description: 'foo'
}]
constructor(nvim: Neovim) {
super(nvim)
this.addAction('open', item => {
lastItem = item.label
})
this.addMultipleAction('multiple', items => {
lastItems = items
})
this.addAction('parallel', async () => {
await helper.wait(100)
}, { parallel: true })
this.addAction('reload', item => {
lastItem = item.label
}, { persist: true, reload: true })
}
public loadItems(): Promise<ListItem[]> {
return Promise.resolve(labels.map(s => {
return { label: s } as ListItem
}))
}
}
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
manager.reset()
await helper.reset()
})
describe('list session', () => {
describe('doDefaultAction()', () => {
it('should throw error when default action not exists', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
list.defaultAction = 'foo'
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
let err
try {
await manager.session.first()
} catch (e) {
err = e
}
expect(err).toBeDefined()
err = null
try {
await manager.session.last()
} catch (e) {
err = e
}
expect(err).toBeDefined()
})
})
describe('doItemAction()', () => {
it('should invoke multiple action', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
await ui.selectAll()
await manager.doAction('multiple')
expect(lastItems.length).toBe(3)
lastItems = undefined
})
it('should invoke parallel action', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
await ui.selectAll()
let d = Date.now()
await manager.doAction('parallel')
expect(Date.now() - d).toBeLessThan(300)
})
it('should invoke reload action', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
labels = ['d', 'e']
await manager.doAction('reload')
await helper.wait(50)
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual(['d', 'e'])
})
})
describe('resume()', () => {
it('should do preview on resume', async () => {
labels = ['a', 'b', 'c']
let lastItem
let list = new SimpleList(nvim)
list.actions.push({
name: 'preview',
execute: item => {
lastItem = item
}
})
disposables.push(manager.registerList(list))
await manager.start(['--normal', '--auto-preview', 'simple'])
let ui = manager.session.ui
await ui.ready
await ui.selectLines(1, 2)
await helper.wait(50)
await nvim.call('coc#window#close', [ui.winid])
await helper.wait(100)
await manager.session.resume()
await helper.wait(100)
expect(lastItem).toBeDefined()
})
})
describe('jumpBack()', () => {
it('should jump back', async () => {
let win = await nvim.window
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
manager.session.jumpBack()
await helper.wait(50)
let winid = await nvim.call('win_getid')
expect(winid).toBe(win.id)
})
})
describe('doNumberSelect()', () => {
async function create(len: number): Promise<ListSession> {
labels = []
for (let i = 0; i < len; i++) {
let code = 'a'.charCodeAt(0) + i
labels.push(String.fromCharCode(code))
}
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', '--number-select', 'simple'])
let ui = manager.session.ui
await ui.ready
return manager.session
}
it('should return false for invalid number', async () => {
let session = await create(5)
let res = await session.doNumberSelect('a')
expect(res).toBe(false)
res = await session.doNumberSelect('8')
expect(res).toBe(false)
})
it('should consider 0 as 10', async () => {
let session = await create(15)
let res = await session.doNumberSelect('0')
expect(res).toBe(true)
expect(lastItem).toBe('j')
})
})
})
describe('showHelp()', () => {
it('should show description and options in help', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
await manager.session.showHelp()
let lines = await nvim.call('getline', [1, '$'])
expect(lines.indexOf('DESCRIPTION')).toBeGreaterThan(0)
expect(lines.indexOf('ARGUMENTS')).toBeGreaterThan(0)
})
})
describe('chooseAction()', () => {
it('should filter actions not have shortcuts', async () => {
labels = ['a', 'b', 'c']
let list = new SimpleList(nvim)
list.actions.push({
name: 'a',
execute: () => {
}
})
list.actions.push({
name: 'b',
execute: () => {
}
})
list.actions.push({
name: 'ab',
execute: () => {
}
})
disposables.push(manager.registerList(list))
await manager.start(['--normal', 'simple'])
let ui = manager.session.ui
await ui.ready
let p = manager.session.chooseAction()
await helper.wait(100)
await nvim.input('a')
await p
})
})

203
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/sources.test.ts

@ -0,0 +1,203 @@ @@ -0,0 +1,203 @@
import { Neovim } from '@chemzqm/neovim'
import { ListContext, ListItem, ListArgument } from '../../types'
import manager from '../../list/manager'
import languages from '../../languages'
import helper from '../helper'
import workspace from '../../workspace'
import { CancellationToken } from 'vscode-jsonrpc'
import { Location, Range } from 'vscode-languageserver-types'
import BasicList from '../../list/basic'
let listItems: ListItem[] = []
class OptionList extends BasicList {
public name = 'option'
public options: ListArgument[] = [{
name: '-w, -word',
description: 'word'
}, {
name: '-i, -input INPUT',
hasValue: true,
description: 'input'
}]
constructor(nvim) {
super(nvim)
this.addLocationActions()
}
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListItem[]> {
return Promise.resolve(listItems)
}
}
jest.setTimeout(3000)
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
manager.dispose()
await helper.shutdown()
})
afterEach(async () => {
manager.reset()
await helper.reset()
await helper.wait(100)
})
describe('BasicList', () => {
describe('parse arguments', () => {
it('should parse args #1', () => {
let list = new OptionList(nvim)
let res = list.parseArguments(['-w'])
expect(res).toEqual({ word: true })
})
it('should parse args #2', () => {
let list = new OptionList(nvim)
let res = list.parseArguments(['-word'])
expect(res).toEqual({ word: true })
})
it('should parse args #3', () => {
let list = new OptionList(nvim)
let res = list.parseArguments(['-input', 'foo'])
expect(res).toEqual({ input: 'foo' })
})
})
describe('preview()', () => {
it('should preview sketch buffer', async () => {
await nvim.command('new')
await nvim.setLine('foo')
let buffer = await nvim.buffer
await helper.wait(30)
let doc = workspace.getDocument(buffer.id)
expect(doc.uri).toMatch('untitled')
let list = new OptionList(nvim)
listItems.push({
label: 'foo',
location: Location.create(doc.uri, Range.create(0, 0, 0, 0))
})
let disposable = manager.registerList(list)
await manager.start(['option'])
await helper.wait(100)
await manager.doAction('preview')
await helper.wait(100)
await nvim.command('wincmd p')
let win = await nvim.window
let isPreview = await win.getVar('previewwindow')
expect(isPreview).toBe(1)
let line = await nvim.line
expect(line).toBe('foo')
disposable.dispose()
})
})
})
describe('list sources', () => {
describe('commands', () => {
it('should load commands source', async () => {
await manager.start(['commands'])
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
it('should do run action', async () => {
await manager.start(['commands'])
await helper.wait(100)
await manager.doAction()
})
})
describe('diagnostics', () => {
it('should load diagnostics source', async () => {
await manager.start(['diagnostics'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('extensions', () => {
it('should load extensions source', async () => {
await manager.start(['extensions'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('folders', () => {
it('should load folders source', async () => {
await manager.start(['folders'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('lists', () => {
it('should load lists source', async () => {
await manager.start(['lists'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('outline', () => {
it('should load outline source', async () => {
await manager.start(['outline'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('services', () => {
it('should load services source', async () => {
await manager.start(['services'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('sources', () => {
it('should load sources source', async () => {
await manager.start(['sources'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
})
})
describe('symbols', () => {
it('should load symbols source', async () => {
let disposable = languages.registerWorkspaceSymbolProvider({
provideWorkspaceSymbols: () => []
})
await manager.start(['symbols'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
disposable.dispose()
})
})
describe('links', () => {
it('should load links source', async () => {
let disposable = languages.registerDocumentLinkProvider([{ scheme: 'file' }, { scheme: 'untitled' }], {
provideDocumentLinks: () => []
})
await manager.start(['links'])
await manager.session?.ui.ready
await helper.wait(100)
expect(manager.isActivated).toBe(true)
disposable.dispose()
})
})
})

140
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/ui.test.ts

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import BasicList from '../../list/basic'
import events from '../../events'
import manager from '../../list/manager'
import { ListItem } from '../../types'
import { disposeAll } from '../../util'
import helper from '../helper'
let labels: string[] = []
let lastItem: string
class SimpleList extends BasicList {
public name = 'simple'
constructor(nvim: Neovim) {
super(nvim)
this.addAction('open', item => {
lastItem = item.label
})
}
public loadItems(): Promise<ListItem[]> {
return Promise.resolve(labels.map(s => {
return { label: s, ansiHighlights: [{ span: [0, 1], hlGroup: 'Search' }] } as ListItem
}))
}
}
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
manager.reset()
await helper.reset()
})
describe('list ui', () => {
describe('selectLines()', () => {
it('should select lines', async () => {
labels = ['foo', 'bar']
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple'])
let ui = manager.session.ui
await ui.ready
await ui.selectLines(3, 1)
let buf = await nvim.buffer
let res = await buf.getSigns({ group: 'coc-list' })
expect(res.length).toBe(2)
})
})
describe('resume()', () => {
it('should resume with selected lines', async () => {
labels = ['foo', 'bar']
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple'])
let ui = manager.session.ui
await ui.ready
await ui.selectLines(1, 2)
await nvim.call('coc#window#close', [ui.winid])
await helper.wait(100)
await manager.session.resume()
await helper.wait(100)
let buf = await nvim.buffer
let res = await buf.getSigns({ group: 'coc-list' })
expect(res.length).toBe(2)
})
})
describe('events', () => {
async function mockMouse(winid: number, lnum: number): Promise<void> {
await nvim.command(`let v:mouse_winid = ${winid}`)
await nvim.command(`let v:mouse_lnum = ${lnum}`)
await nvim.command('let v:mouse_col = 1')
}
it('should fire action on double click', async () => {
labels = ['foo', 'bar']
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple'])
let ui = manager.session.ui
await ui.ready
await mockMouse(ui.winid, 1)
await manager.session.onMouseEvent('<2-LeftMouse>')
await helper.wait(100)
expect(lastItem).toBe('foo')
})
it('should select clicked line', async () => {
labels = ['foo', 'bar']
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple'])
let ui = manager.session.ui
await ui.ready
await mockMouse(ui.winid, 2)
await ui.onMouse('mouseDown')
await helper.wait(50)
await mockMouse(ui.winid, 2)
await ui.onMouse('mouseUp')
await helper.wait(50)
let item = await ui.item
expect(item.label).toBe('bar')
})
it('should jump to original window on click', async () => {
labels = ['foo', 'bar']
let win = await nvim.window
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple'])
let ui = manager.session.ui
await ui.ready
await mockMouse(win.id, 1)
await ui.onMouse('mouseUp')
await helper.wait(50)
let curr = await nvim.window
expect(curr.id).toBe(win.id)
})
it('should highlights items on CursorMoved', async () => {
labels = (new Array(400)).fill('a')
disposables.push(manager.registerList(new SimpleList(nvim)))
await manager.start(['simple', '--normal'])
let ui = manager.session.ui
await ui.ready
await nvim.call('cursor', [350, 1])
await events.fire('CursorMoved', [ui.bufnr, [350, 1]])
await helper.wait(300)
let res = await nvim.call('getmatches')
expect(res.length).toBeGreaterThan(300)
})
})
})

204
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/list/worker.test.ts

@ -0,0 +1,204 @@ @@ -0,0 +1,204 @@
import { Neovim } from '@chemzqm/neovim'
import manager from '../../list/manager'
import { parseInput } from '../../list/worker'
import helper from '../helper'
import { ListContext, ListTask, ListItem } from '../../types'
import { CancellationToken, Disposable } from 'vscode-languageserver-protocol'
import { EventEmitter } from 'events'
import colors from 'colors/safe'
import BasicList from '../../list/basic'
import { disposeAll } from '../../util'
let items: ListItem[] = []
class DataList extends BasicList {
public name = 'data'
public loadItems(): Promise<ListItem[]> {
return Promise.resolve(items)
}
}
class IntervalTaskList extends BasicList {
public name = 'task'
public timeout = 3000
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListTask> {
let emitter: any = new EventEmitter()
let i = 0
let interval = setInterval(() => {
emitter.emit('data', { label: i.toFixed() })
i++
}, 50)
emitter.dispose = () => {
clearInterval(interval)
emitter.emit('end')
}
token.onCancellationRequested(() => {
emitter.dispose()
})
return emitter
}
}
class DelayTask extends BasicList {
public name = 'delay'
public interactive = true
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListTask> {
let emitter: any = new EventEmitter()
let disposed = false
setTimeout(() => {
if (disposed) return
emitter.emit('data', { label: 'ahead' })
}, 100)
setTimeout(() => {
if (disposed) return
emitter.emit('data', { label: 'abort' })
}, 200)
emitter.dispose = () => {
disposed = true
emitter.emit('end')
}
token.onCancellationRequested(() => {
emitter.dispose()
})
return emitter
}
}
class InteractiveList extends BasicList {
public name = 'test'
public interactive = true
public loadItems(context: ListContext, _token: CancellationToken): Promise<ListItem[]> {
return Promise.resolve([{
label: colors.magenta(context.input || '')
}])
}
}
class ErrorList extends BasicList {
public name = 'error'
public interactive = true
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListItem[]> {
return Promise.reject(new Error('test error'))
}
}
class ErrorTaskList extends BasicList {
public name = 'task'
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListTask> {
let emitter: any = new EventEmitter()
let timeout = setTimeout(() => {
emitter.emit('error', new Error('task error'))
}, 100)
emitter.dispose = () => {
clearTimeout(timeout)
}
return emitter
}
}
let nvim: Neovim
let disposables: Disposable[] = []
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
manager.reset()
await helper.reset()
})
describe('parseInput', () => {
it('should parse input with space', async () => {
let res = parseInput('a b')
expect(res).toEqual(['a', 'b'])
})
it('should parse input with escaped space', async () => {
let res = parseInput('a\\ b')
expect(res).toEqual(['a b'])
})
})
describe('list worker', () => {
it('should work with long running task', async () => {
disposables.push(manager.registerList(new IntervalTaskList(nvim)))
await manager.start(['task'])
await helper.wait(300)
let len = manager.session?.length
expect(len > 2).toBe(true)
await manager.cancel()
})
it('should sort by sortText', async () => {
items = [{
label: 'abc',
sortText: 'b'
}, {
label: 'ade',
sortText: 'a'
}]
disposables.push(manager.registerList(new DataList(nvim)))
await manager.start(['data'])
await helper.wait(100)
await nvim.input('a')
await helper.wait(100)
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual(['ade', 'abc'])
await manager.cancel()
})
it('should cancel task by use CancellationToken', async () => {
disposables.push(manager.registerList(new IntervalTaskList(nvim)))
await manager.start(['task'])
expect(manager.session?.worker.isLoading).toBe(true)
await helper.wait(100)
manager.session?.stop()
expect(manager.session?.worker.isLoading).toBe(false)
})
it('should render slow interactive list', async () => {
disposables.push(manager.registerList(new DelayTask(nvim)))
await manager.start(['delay'])
await nvim.input('a')
await helper.wait(600)
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines).toEqual(['ahead', 'abort'])
})
it('should work with interactive list', async () => {
disposables.push(manager.registerList(new InteractiveList(nvim)))
await manager.start(['-I', 'test'])
await manager.session?.ui.ready
expect(manager.isActivated).toBe(true)
await nvim.eval('feedkeys("f", "in")')
await helper.wait(100)
await nvim.eval('feedkeys("a", "in")')
await helper.wait(100)
await nvim.eval('feedkeys("x", "in")')
await helper.wait(300)
let item = await manager.session?.ui.item
expect(item.label).toBe('fax')
})
it('should not activate on load error', async () => {
disposables.push(manager.registerList(new ErrorList(nvim)))
await manager.start(['test'])
expect(manager.isActivated).toBe(false)
})
it('should deactivate on task error', async () => {
disposables.push(manager.registerList(new ErrorTaskList(nvim)))
await manager.start(['task'])
await helper.wait(500)
expect(manager.isActivated).toBe(false)
})
})

151
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/markdown/index.test.ts

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
import { getHighlightItems, parseMarkdown, parseDocuments } from '../../markdown/index'
describe('getHighlightItems', () => {
it('should get highlights in single line', async () => {
let res = getHighlightItems('this line has highlights', 0, [10, 15])
expect(res).toEqual([{
colStart: 10,
colEnd: 15,
lnum: 0,
hlGroup: 'CocUnderline'
}])
})
it('should get highlights when active end extended', async () => {
let res = getHighlightItems('this line', 0, [5, 30])
expect(res).toEqual([{
colStart: 5,
colEnd: 9,
lnum: 0,
hlGroup: 'CocUnderline'
}])
})
it('should get highlights across line', async () => {
let res = getHighlightItems('this line\nhas highlights', 0, [5, 15])
expect(res).toEqual([{
colStart: 5, colEnd: 9, lnum: 0, hlGroup: 'CocUnderline'
}, {
colStart: 0, colEnd: 5, lnum: 1, hlGroup: 'CocUnderline'
}])
})
})
describe('parseMarkdown', () => {
it('should parse code blocks', async () => {
let content = `
\`\`\`js
var global = globalThis
\`\`\`
\`\`\`ts
let str:string
\`\`\`
`
let res = parseMarkdown(content, {})
expect(res.lines).toEqual([
'var global = globalThis',
'',
'let str:string'
])
expect(res.codes).toEqual([
{ filetype: 'javascript', startLine: 0, endLine: 1 },
{ filetype: 'typescript', startLine: 2, endLine: 3 }
])
})
it('should parse html code block', async () => {
let content = `
example:
\`\`\`html
<div>code</div>
\`\`\`
`
let res = parseMarkdown(content, {})
expect(res.lines).toEqual(['example:', '', '<div>code</div>'])
expect(res.codes).toEqual([{ filetype: 'html', startLine: 2, endLine: 3 }])
})
it('should compose empty lines', async () => {
let content = 'foo\n\n\nbar\n\n\n'
let res = parseMarkdown(content, {})
expect(res.lines).toEqual(['foo', '', 'bar'])
})
it('should parse ansi highlights', async () => {
let content = '__foo__\n[link](link)'
let res = parseMarkdown(content, {})
expect(res.lines).toEqual(['foo', 'link'])
expect(res.highlights).toEqual([
{ hlGroup: 'CocBold', lnum: 0, colStart: 0, colEnd: 3 },
{ hlGroup: 'CocUnderline', lnum: 1, colStart: 0, colEnd: 4 }
])
})
it('should exclude images by option', async () => {
let content = 'head\n![img](img)\ncontent ![img](img) ![img](img)'
let res = parseMarkdown(content, { excludeImages: true })
expect(res.lines).toEqual(['head', '', 'content'])
})
})
describe('parseDocuments', () => {
it('should parse documents with diagnostic filetypes', async () => {
let docs = [{
filetype: 'Error',
content: 'Error text'
}, {
filetype: 'Warning',
content: 'Warning text'
}]
let res = parseDocuments(docs)
expect(res.lines).toEqual([
'Error text',
'─',
'Warning text'
])
expect(res.codes).toEqual([
{ hlGroup: 'CocErrorFloat', startLine: 0, endLine: 1 },
{ hlGroup: 'CocWarningFloat', startLine: 2, endLine: 3 }
])
})
it('should parse markdown document with filetype document', async () => {
let docs = [{
filetype: 'typescript',
content: 'const workspace'
}, {
filetype: 'markdown',
content: '**header**'
}]
let res = parseDocuments(docs)
expect(res.lines).toEqual([
'const workspace',
'─',
'header'
])
expect(res.highlights).toEqual([{
hlGroup: 'CocBold',
lnum: 2,
colStart: 0,
colEnd: 6
}])
expect(res.codes).toEqual([
{ filetype: 'typescript', startLine: 0, endLine: 1 }
])
})
it('should parse documents with active highlights', async () => {
let docs = [{
filetype: 'javascript',
content: 'func(foo, bar)',
active: [5, 8]
}, {
filetype: 'javascript',
content: 'func()',
active: [15, 20]
}]
let res = parseDocuments(docs as any)
expect(res.highlights).toEqual([{ colStart: 5, colEnd: 8, lnum: 0, hlGroup: 'CocUnderline' }
])
})
})

119
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/markdown/renderer.test.ts

@ -0,0 +1,119 @@ @@ -0,0 +1,119 @@
import marked from 'marked'
import Renderer from '../../markdown/renderer'
import * as styles from '../../markdown/styles'
import { parseAnsiHighlights, AnsiResult } from '../../util/ansiparse'
marked.setOptions({
renderer: new Renderer()
})
function parse(text: string): AnsiResult {
let m = marked(text)
let res = parseAnsiHighlights(m.split(/\n/)[0], true)
return res
}
describe('styles', () => {
it('should add styles', async () => {
let keys = ['gray', 'magenta', 'bold', 'underline', 'italic', 'strikethrough', 'yellow', 'green', 'blue']
for (let key of keys) {
let res = styles[key]('text')
expect(res).toContain('text')
}
})
})
describe('Renderer of marked', () => {
it('should create bold highlights', async () => {
let res = parse('**note**.')
expect(res.highlights[0]).toEqual({
span: [0, 4],
hlGroup: 'CocBold'
})
})
it('should create italic highlights', async () => {
let res = parse('_note_.')
expect(res.highlights[0]).toEqual({
span: [0, 4],
hlGroup: 'CocItalic'
})
})
it('should create underline highlights for link', async () => {
let res = parse('[baidu](https://baidu.com)')
expect(res.highlights[0]).toEqual({
span: [0, 5],
hlGroup: 'CocMarkdownLink'
})
res = parse('https://baidu.com')
expect(res.highlights[0]).toEqual({
span: [0, 17],
hlGroup: 'CocUnderline'
})
})
it('should parse link', async () => {
// let res = parse('https://doc.rust-lang.org/nightly/core/iter/traits/iterator/Iterator.t.html#map.v')
// console.log(JSON.stringify(res, null, 2))
let link = 'https://doc.rust-lang.org/nightly/core/iter/traits/iterator/Iterator.t.html#map.v'
let parsed = marked(link)
let res = parseAnsiHighlights(parsed.split(/\n/)[0], true)
expect(res.line).toEqual(link)
expect(res.highlights.length).toBeGreaterThan(0)
expect(res.highlights[0].hlGroup).toBe('CocUnderline')
})
it('should create highlight for code span', async () => {
let res = parse('`let foo = "bar"`')
expect(res.highlights[0]).toEqual({
span: [0, 15],
hlGroup: 'CocMarkdownCode'
})
})
it('should create header highlights', async () => {
let res = parse('# header')
expect(res.highlights[0]).toEqual({
span: [0, 8],
hlGroup: 'CocMarkdownHeader'
})
res = parse('## header')
expect(res.highlights[0]).toEqual({
span: [0, 9],
hlGroup: 'CocMarkdownHeader'
})
res = parse('### header')
expect(res.highlights[0]).toEqual({
span: [0, 10],
hlGroup: 'CocMarkdownHeader'
})
})
it('should indent blockquote', async () => {
let res = parse('> header')
expect(res.line).toBe(' header')
})
it('should preserve code block', async () => {
let text = '``` js\nconsole.log("foo")\n```'
let m = marked(text)
expect(m.split('\n')).toEqual([
'``` js',
'console.log("foo")',
'```',
''
])
})
it('should renderer table', async () => {
let text = `
| Syntax | Description |
| ----------- | ----------- |
| Header | Title |
| Paragraph | Text |
`
let res = marked(text)
expect(res).toContain('Syntax')
})
})

1
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/memos.json

@ -0,0 +1 @@ @@ -0,0 +1 @@
{}

35
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/array.test.ts

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
import * as assert from 'assert'
import * as arrays from '../../util/array'
describe('Arrays', () => {
test('distinct', () => {
function compare(a: string): string {
return a
}
assert.deepStrictEqual(arrays.distinct(['32', '4', '5'], compare), ['32', '4', '5'])
assert.deepStrictEqual(arrays.distinct(['32', '4', '5', '4'], compare), ['32', '4', '5'])
assert.deepStrictEqual(arrays.distinct(['32', 'constructor', '5', '1'], compare), ['32', 'constructor', '5', '1'])
assert.deepStrictEqual(arrays.distinct(['32', 'constructor', 'proto', 'proto', 'constructor'], compare), ['32', 'constructor', 'proto'])
assert.deepStrictEqual(arrays.distinct(['32', '4', '5', '32', '4', '5', '32', '4', '5', '5'], compare), ['32', '4', '5'])
})
test('tail', () => {
assert.strictEqual(arrays.tail([1, 2, 3]), 3)
})
test('lastIndex', () => {
let res = arrays.lastIndex([1, 2, 3], x => x < 3)
assert.strictEqual(res, 1)
})
test('flatMap', () => {
let objs: { [key: string]: number[] }[] = [{ x: [1, 2] }, { y: [3, 4] }, { z: [5, 6] }]
function values(item: { [key: string]: number[] }): number[] {
return Object.keys(item).reduce((p, c) => p.concat(item[c]), [])
}
let res = arrays.flatMap(objs, values)
assert.deepStrictEqual(res, [1, 2, 3, 4, 5, 6])
})
})

53
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/attach.test.ts

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
import { Neovim } from '@chemzqm/neovim'
import events from '../../events'
import helper from '../helper'
function wait(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, ms)
})
}
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('attach', () => {
it('should listen CocInstalled', async () => {
nvim.emit('notification', 'VimEnter')
await helper.wait(100)
})
it('should not throw on event handler error', async () => {
events.on('CursorHold', async () => {
throw new Error('error')
})
let fn = jest.fn()
nvim.emit('request', 'CocAutocmd', ['CursorHold'], {
send: fn
})
await wait(100)
expect(fn).toBeCalled()
})
it('should not throw when plugin method not found', async () => {
let fn = jest.fn()
nvim.emit('request', 'NotExists', [], {
send: fn
})
await wait(100)
expect(fn).toBeCalled()
})
})

78
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/chars.test.ts

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
import { Chars } from '../../model/chars'
describe('chars keyword option', () => {
it('should match @', () => {
let chars = new Chars('@')
expect(chars.isKeywordChar('a')).toBe(true)
expect(chars.isKeywordChar('z')).toBe(true)
expect(chars.isKeywordChar('A')).toBe(true)
expect(chars.isKeywordChar('Z')).toBe(true)
})
it('should match letter range', () => {
let chars = new Chars('a-z')
expect(chars.isKeywordChar('a')).toBe(true)
expect(chars.isKeywordChar('A')).toBe(false)
})
it('should match code range', () => {
let chars = new Chars('48-57')
expect(chars.isKeywordChar('a')).toBe(false)
expect(chars.isKeywordChar('0')).toBe(true)
expect(chars.isKeywordChar('9')).toBe(true)
})
it('should match @-@', () => {
let chars = new Chars('@-@')
expect(chars.isKeywordChar('@')).toBe(true)
})
it('should match single code', () => {
let chars = new Chars('58')
expect(chars.isKeywordChar(':')).toBe(true)
})
it('should match single character', () => {
let chars = new Chars('_')
expect(chars.isKeywordChar('_')).toBe(true)
})
})
describe('chars addKeyword', () => {
it('should add keyword', () => {
let chars = new Chars('_')
chars.addKeyword(':')
expect(chars.isKeywordChar(':')).toBe(true)
})
})
describe('chars change keyword', () => {
it('should change keyword', () => {
let chars = new Chars('_')
chars.setKeywordOption(':')
expect(chars.isKeywordChar(':')).toBe(true)
expect(chars.isKeywordChar('_')).toBe(false)
})
})
describe('chars match keywords', () => {
it('should match keywords', () => {
let chars = new Chars('@')
let res = chars.matchKeywords('foo bar')
expect(res).toEqual(['foo', 'bar'])
})
it('should consider unicode character as word', () => {
let chars = new Chars('@')
let res = chars.matchKeywords('blackкофе')
expect(res).toEqual(['blackкофе'])
})
})
describe('chars isKeyword', () => {
it('should check isKeyword', () => {
let chars = new Chars('@')
expect(chars.isKeyword('foo')).toBe(true)
expect(chars.isKeyword('f@')).toBe(false)
})
})

35
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/client.test.ts

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
import { Neovim } from '@chemzqm/neovim'
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('node client pauseNotification', () => {
it('should work with notify & request', async () => {
nvim.pauseNotification()
nvim.call('setline', [1, 'foo'], true)
nvim.call('append', [1, ['bar']], true)
await nvim.resumeNotification(false, true)
await helper.wait(500)
let buffer = await nvim.buffer
let lines = await buffer.lines
expect(lines).toEqual(['foo', 'bar'])
nvim.pauseNotification()
nvim.call('eval', ['&buftype'], true)
nvim.call('bufnr', ['%'], true)
let res = await nvim.resumeNotification()
expect(res).toEqual([['', buffer.id], null])
})
})

728
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/completion.test.ts

@ -0,0 +1,728 @@ @@ -0,0 +1,728 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-jsonrpc'
import { CompletionItem, InsertTextFormat, Position, Range, TextEdit, CompletionList } from 'vscode-languageserver-types'
import completion from '../../completion'
import languages from '../../languages'
import { CompletionItemProvider } from '../../provider'
import snippetManager from '../../snippets/manager'
import sources from '../../sources'
import { CompleteOption, CompleteResult, ISource, SourceType } from '../../types'
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
})
beforeEach(async () => {
disposables = []
await helper.createDocument()
await nvim.call('feedkeys', [String.fromCharCode(27), 'in'])
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
await helper.reset()
})
describe('completion events', () => {
it('should load preferences', () => {
let minTriggerInputLength = completion.config.minTriggerInputLength
expect(minTriggerInputLength).toBe(1)
})
it('should reload preferences onChange', () => {
let { configurations } = workspace
configurations.updateUserConfig({ 'suggest.maxCompleteItemCount': 30 })
let snippetIndicator = completion.config.maxItemCount
expect(snippetIndicator).toBe(30)
})
})
describe('completion start', () => {
it('should deactivate on doComplete error', async () => {
let fn = (completion as any)._doComplete
; (completion as any)._doComplete = async () => {
throw new Error('fake')
}
let option: CompleteOption = await nvim.call('coc#util#get_complete_option')
await completion.startCompletion(option)
; (completion as any)._doComplete = fn
expect(completion.isActivated).toBe(false)
})
it('should start completion', async () => {
await nvim.setLine('foo football')
await nvim.input('a')
await nvim.call('cursor', [1, 2])
let option: CompleteOption = await nvim.call('coc#util#get_complete_option')
await completion.startCompletion(option)
expect(completion.isActivated).toBe(true)
})
it('should show slow source', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'slow',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => new Promise(resolve => {
setTimeout(() => {
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] })
}, 600)
})
}
disposables.push(sources.addSource(source))
await helper.edit()
await nvim.input('i.')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
let items = await helper.items()
expect(items.length).toBe(2)
})
})
describe('completion resumeCompletion', () => {
it('should stop if no filtered items', async () => {
await nvim.setLine('foo ')
await helper.wait(50)
await nvim.input('Af')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
await nvim.input('d')
await helper.wait(60)
expect(completion.isActivated).toBe(false)
})
it('should deactivate without filtered items', async () => {
await nvim.setLine('foo fbi ')
await nvim.input('Af')
await helper.waitPopup()
await nvim.input('c')
await helper.wait(100)
let items = await helper.items()
expect(items.length).toBe(0)
expect(completion.isActivated).toBe(false)
})
it('should deactivate when insert space', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'empty',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => new Promise(resolve => {
resolve({ items: [{ word: 'foo bar' }] })
})
}
sources.addSource(source)
await helper.edit()
await nvim.input('i.')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
sources.removeSource(source)
let items = await helper.items()
expect(items[0].word).toBe('foo bar')
await nvim.input(' ')
await helper.wait(60)
expect(completion.isActivated).toBe(false)
})
it('should use resume input to filter', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'source',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (): Promise<CompleteResult> => new Promise(resolve => {
setTimeout(() => {
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] })
}, 60)
})
}
sources.addSource(source)
await helper.edit()
await nvim.input('i.')
await helper.wait(20)
await nvim.input('f')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
let items = await helper.items()
expect(items.length).toBe(1)
expect(items[0].word).toBe('foo')
sources.removeSource(source)
})
it('should filter slow source', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'slow',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (): Promise<CompleteResult> => new Promise(resolve => {
setTimeout(() => {
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] })
}, 600)
})
}
disposables.push(sources.addSource(source))
await helper.edit()
await nvim.input('i.')
await helper.wait(60)
await nvim.input('f')
await helper.waitPopup()
await nvim.input('o')
await helper.wait(100)
expect(completion.isActivated).toBe(true)
let items = await helper.items()
expect(items.length).toBe(1)
expect(items[0].word).toBe('foo')
})
it('should complete inComplete source', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'inComplete',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => {
await helper.wait(30)
if (opt.input.length <= 1) {
return { isIncomplete: true, items: [{ word: 'foo' }, { word: opt.input }] }
}
return { isIncomplete: false, items: [{ word: 'foo' }, { word: opt.input }] }
}
}
disposables.push(sources.addSource(source))
await helper.edit()
await nvim.input('i.')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
await nvim.input('a')
await helper.wait(30)
await nvim.input('b')
await helper.wait(100)
})
it('should not complete inComplete source when isIncomplete is false', async () => {
await helper.createDocument()
let lastOption: CompleteOption
let source: ISource = {
priority: 0,
enable: true,
name: 'inComplete',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => {
lastOption = opt
await helper.wait(30)
if (opt.input.length <= 1) {
return { isIncomplete: true, items: [{ word: 'foobar' }] }
}
return { isIncomplete: false, items: [{ word: 'foobar' }] }
}
}
disposables.push(sources.addSource(source))
await helper.edit()
await nvim.input('i.')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
await nvim.input('fo')
await helper.wait(100)
await nvim.input('b')
await helper.wait(200)
expect(completion.isActivated).toBe(true)
})
})
describe('completion InsertEnter', () => {
it('should trigger completion if triggerAfterInsertEnter is true', async () => {
await helper.createDocument()
await nvim.setLine('foo fo')
let config = workspace.getConfiguration('suggest')
config.update('triggerAfterInsertEnter', true)
await helper.wait(100)
let triggerAfterInsertEnter = completion.config.triggerAfterInsertEnter
expect(triggerAfterInsertEnter).toBe(true)
await nvim.input('A')
await helper.waitPopup()
expect(completion.isActivated).toBe(true)
config.update('triggerAfterInsertEnter', undefined)
})
it('should not trigger when input length too small', async () => {
let config = workspace.getConfiguration('suggest')
config.update('triggerAfterInsertEnter', true)
await helper.wait(100)
let triggerAfterInsertEnter = completion.config.triggerAfterInsertEnter
expect(triggerAfterInsertEnter).toBe(true)
await nvim.setLine('foo ')
await nvim.input('A')
await helper.wait(100)
expect(completion.isActivated).toBe(false)
config.update('triggerAfterInsertEnter', undefined)
})
})
describe('completion TextChangedP', () => {
it('should stop when input length below option input length', async () => {
await helper.edit()
await nvim.setLine('foo fbi ')
await nvim.input('Af')
await helper.waitPopup()
await nvim.input('<backspace>')
await helper.wait(100)
expect(completion.isActivated).toBe(false)
})
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.selectCompleteItem(0)
await helper.wait(200)
let line = await nvim.line
expect(line).toBe('barfoo')
let [, lnum, col] = await nvim.call('getcurpos')
expect(lnum).toBe(2)
expect(col).toBe(7)
})
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') {
await helper.wait(100)
if (token.isCancellationRequested) return
return {
isIncomplete: true,
items: [
{
label: 'foo'
}
]
}
}
if (option.input == 'fo') {
await helper.wait(100)
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('f')
await helper.wait(60)
await nvim.input('o')
await helper.wait(300)
let res = await helper.getItems()
expect(res.length).toBe(1)
})
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',
filterText: 'ar',
textEdit: {
range: Range.create(0, 1, 0, 1),
newText: 'ar'
}
}]
}
}
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider))
await nvim.input('ib')
await helper.waitPopup()
let context = await nvim.getVar('coc#_context') as any
expect(context.start).toBe(1)
expect(context.candidates[0].word).toBe('ar')
})
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 nvim.eval('feedkeys("\\<C-n>", "in")')
await helper.wait(200)
let line = await nvim.line
expect(line).toBe('?foo')
})
it('should fix cursor position with snippet on additionalTextEdits', async () => {
await helper.createDocument()
let provider: CompletionItemProvider = {
provideCompletionItems: async (): Promise<CompletionItem[]> => [{
label: 'if',
insertTextFormat: InsertTextFormat.Snippet,
textEdit: { range: Range.create(0, 0, 0, 2), 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.menu == '[edit]')
await helper.selectCompleteItem(idx)
await helper.wait(800)
let line = await nvim.line
expect(line).toBe('bar if()')
let [, lnum, col] = await nvim.call('getcurpos')
expect(lnum).toBe(1)
expect(col).toBe(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()
await helper.selectCompleteItem(0)
await helper.wait(200)
let line = await nvim.line
let [, lnum, col] = await nvim.call('getcurpos')
expect(line).toBe('bar do')
expect(lnum).toBe(1)
expect(col).toBe(7)
})
it('should fix cursor position with nested snippet on additionalTextEdits', async () => {
await helper.createDocument()
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.selectCompleteItem(0)
await helper.wait(200)
let line = await nvim.line
let [, lnum, col] = await nvim.call('getcurpos')
expect(line).toBe('bar func(do)')
expect(lnum).toBe(1)
expect(col).toBe(12)
})
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.input('<C-n>')
await helper.wait(100)
let line = await nvim.line
expect(line).toBe('foo')
})
it('should filter on none keyword input', async () => {
let source: ISource = {
priority: 99,
enable: true,
name: 'temp',
sourceType: SourceType.Service,
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({ items: [{ word: 'foo#abc' }] }),
}
disposables.push(sources.addSource(source))
await nvim.input('if')
await helper.waitPopup()
await nvim.input('#')
await helper.wait(100)
let items = await helper.getItems()
expect(items[0].word).toBe('foo#abc')
})
it('should use source-provided score', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'source',
sourceType: SourceType.Service,
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({
items: [
{ word: 'candidate_a', sourceScore: 0.1 },
{ word: 'candidate_b', sourceScore: 10 },
{ word: 'candidate_c' },
]
}),
}
disposables.push(sources.addSource(source))
await nvim.input('ocand')
await helper.waitPopup()
let items = await helper.getItems()
expect(items[0].word).toBe('candidate_b')
expect(items[1].word).toBe('candidate_c')
expect(items[2].word).toBe('candidate_a')
})
it('should do resolve for complete item', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'resolve',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({ items: [{ word: 'foo' }] }),
onCompleteResolve: item => {
item.info = 'detail'
}
}
sources.addSource(source)
await nvim.input('i.')
await helper.waitPopup()
await helper.wait(100)
await nvim.input('<C-n>')
await helper.wait(100)
// let items = completion.completeItems
// expect(items[0].info).toBe('detail')
sources.removeSource(source)
})
})
describe('completion done', () => {
it('should fix word on CompleteDone', async () => {
await nvim.setLine('fball football')
await nvim.input('i')
await nvim.call('cursor', [1, 2])
let option: CompleteOption = await nvim.call('coc#util#get_complete_option')
await completion.startCompletion(option)
let items = await helper.items()
expect(items.length).toBe(1)
await nvim.input('<C-n>')
await helper.wait(30)
await nvim.call('coc#_select')
await helper.wait(100)
let line = await nvim.line
expect(line).toBe('football football')
})
})
describe('completion option', () => {
it('should hide kind and menu when configured', async () => {
helper.updateConfiguration('suggest.disableKind', true)
helper.updateConfiguration('suggest.disableMenu', true)
await nvim.setLine('fball football')
await nvim.input('of')
await helper.waitPopup()
let items = await helper.getItems()
expect(items[0].kind).toBeUndefined()
expect(items[0].menu).toBeUndefined()
helper.updateConfiguration('suggest.disableKind', false)
helper.updateConfiguration('suggest.disableMenu', false)
})
})
describe('completion trigger', () => {
it('should trigger completion on type trigger character', async () => {
let source: ISource = {
priority: 1,
enable: true,
name: 'trigger',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (opt: CompleteOption): Promise<CompleteResult> => {
if (opt.triggerCharacter == '.') {
return Promise.resolve({ items: [{ word: 'bar' }] })
}
return Promise.resolve({ items: [{ word: 'foo#bar' }] })
}
}
sources.addSource(source)
await nvim.input('i')
await helper.wait(30)
await nvim.input('.')
await helper.waitPopup()
let items = await helper.items()
expect(items.length).toBeGreaterThan(0)
sources.removeSource(source)
})
it('should not trigger if autoTrigger is none', async () => {
let config = workspace.getConfiguration('suggest')
config.update('autoTrigger', 'none')
let autoTrigger = completion.config.autoTrigger
expect(autoTrigger).toBe('none')
await nvim.setLine('foo fo')
await nvim.input('A')
await helper.wait(100)
expect(completion.isActivated).toBe(false)
config.update('autoTrigger', 'always')
})
it('should trigger complete on trigger patterns match', async () => {
let source: ISource = {
priority: 99,
enable: true,
name: 'temp',
triggerPatterns: [/EM/],
sourceType: SourceType.Service,
doComplete: (opt: CompleteOption): Promise<CompleteResult> => {
if (!opt.input.startsWith('EM')) return null
return Promise.resolve({
items: [
{ word: 'foo', filterText: 'EMfoo' },
{ word: 'bar', filterText: 'EMbar' }
]
})
},
}
disposables.push(sources.addSource(source))
await nvim.input('i')
await nvim.input('EM')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBe(2)
})
it('should trigger complete when pumvisible and triggerPatterns match', async () => {
await nvim.setLine('EnumMember')
let source: ISource = {
priority: 99,
enable: true,
name: 'temp',
triggerPatterns: [/EM/],
sourceType: SourceType.Service,
doComplete: (opt: CompleteOption): Promise<CompleteResult> => {
if (!opt.input.startsWith('EM')) return null
return Promise.resolve({
items: [
{ word: 'a', filterText: 'EMa' },
{ word: 'b', filterText: 'EMb' }
]
})
},
}
disposables.push(sources.addSource(source))
await nvim.input('o')
await helper.wait(10)
await nvim.input('E')
await helper.wait(30)
await nvim.input('M')
await helper.waitPopup()
let items = await helper.getItems()
expect(items.length).toBeGreaterThan(2)
})
})
describe('completion TextChangedI', () => {
it('should respect commitCharacter on TextChangedI', async () => {
let source: ISource = {
priority: 0,
enable: true,
name: 'slow',
sourceType: SourceType.Service,
triggerCharacters: ['.'],
doComplete: (opt: CompleteOption): Promise<CompleteResult> => {
if (opt.triggerCharacter == '.') {
return Promise.resolve({ items: [{ word: 'bar' }] })
}
return Promise.resolve({ items: [{ word: 'foo' }] })
},
shouldCommit: (_item, character) => character == '.'
}
sources.addSource(source)
await nvim.input('if')
await helper.pumvisible()
await helper.wait(100)
await nvim.input('.')
await helper.wait(100)
sources.removeSource(source)
})
it('should cancel completion with same pretext', async () => {
await nvim.setLine('foo')
await nvim.input('of')
await helper.pumvisible()
await helper.wait(30)
await nvim.call('coc#_cancel', [])
let line = await nvim.line
let visible = await nvim.call('pumvisible')
expect(line).toBe('f')
expect(visible).toBe(0)
})
})

247
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/configurations.test.ts

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
import fs from 'fs'
import os from 'os'
import { ParseError } from 'jsonc-parser'
import path from 'path'
import Configurations from '../../configuration'
import { convertErrors, getChangedKeys, getConfigurationValue, getKeys, parseConfiguration } from '../../configuration/util'
import { IConfigurationModel } from '../../types'
import { URI } from 'vscode-uri'
import { v1 as uuidv1 } from 'uuid'
import { CONFIG_FILE_NAME } from '../../util'
const config = fs.readFileSync(path.join(__dirname, './settings.json'), 'utf8')
const workspaceConfigFile = path.resolve(__dirname, `../sample/.vim/${CONFIG_FILE_NAME}`)
function getConfigurationModel(): IConfigurationModel {
let [, contents] = parseConfiguration(config)
return { contents }
}
function createConfigurations(): Configurations {
let userConfigFile = path.join(__dirname, './settings.json')
return new Configurations(userConfigFile)
}
describe('Configurations', () => {
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 all keys', () => {
let res = getKeys({
foo: {
bar: 1,
from: {
to: 2
}
},
bar: [1, 2]
})
expect(res).toEqual(['foo', 'foo.bar', 'foo.from', 'foo.from.to', 'bar'])
})
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 add folder as workspace configuration', () => {
let configurations = createConfigurations()
configurations.onDidChange(e => {
let affects = e.affectsConfiguration('coc')
expect(affects).toBe(true)
})
configurations.addFolderFile(workspaceConfigFile)
let o = configurations.configuration.workspace.contents
expect(o.coc.preferences.rootPath).toBe('./src')
configurations.dispose()
})
it('should get changed keys #1', () => {
let res = getChangedKeys({ y: 2 }, { x: 1 })
expect(res).toEqual(['x', 'y'])
})
it('should get changed keys #2', () => {
let res = getChangedKeys({ x: 1, c: { d: 4 } }, { x: 1, b: { x: 5 } })
expect(res).toEqual(['b', 'b.x', 'c', 'c.d'])
})
it('should load default configurations', () => {
let conf = new Configurations()
expect(conf.defaults.contents.coc).toBeDefined()
let c = conf.getConfiguration('languageserver')
expect(c).toEqual({})
conf.dispose()
})
it('should parse configurations', () => {
let { contents } = getConfigurationModel()
expect(contents.foo.bar).toBe(1)
expect(contents.bar.foo).toBe(2)
expect(contents.schema).toEqual({ 'https://example.com': '*.yaml' })
})
it('should update user config #1', () => {
let conf = new Configurations()
let fn = jest.fn()
conf.onDidChange(e => {
expect(e.affectsConfiguration('x')).toBe(true)
fn()
})
conf.updateUserConfig({ x: 1 })
let config = conf.configuration.user
expect(config.contents).toEqual({ x: 1 })
expect(fn).toBeCalled()
})
it('should update user config #2', () => {
let conf = new Configurations()
conf.updateUserConfig({ x: 1 })
conf.updateUserConfig({ x: undefined })
let config = conf.configuration.user
expect(config.contents).toEqual({})
})
it('should update workspace config', () => {
let conf = new Configurations()
conf.updateUserConfig({ foo: { bar: 1 } })
let curr = conf.getConfiguration('foo')
curr.update('bar', 2, false)
curr = conf.getConfiguration('foo')
let n = curr.get<number>('bar')
expect(n).toBe(2)
})
it('should handle errors', () => {
let tmpFile = path.join(os.tmpdir(), uuidv1())
fs.writeFileSync(tmpFile, '{"x":', 'utf8')
let conf = new Configurations(tmpFile)
let errors = conf.errorItems
expect(errors.length > 1).toBe(true)
conf.dispose()
})
it('should change to new folder configuration', () => {
let conf = new Configurations()
conf.addFolderFile(workspaceConfigFile)
let configFile = path.join(__dirname, './settings.json')
conf.addFolderFile(configFile)
let file = path.resolve(__dirname, '../sample/tmp.js')
let fn = jest.fn()
conf.onDidChange(fn)
conf.setFolderConfiguration(URI.file(file).toString())
let { contents } = conf.workspace
expect(contents.foo).toBeUndefined()
expect(fn).toBeCalled()
conf.dispose()
})
it('should get nested property', () => {
let config = createConfigurations()
let conf = config.getConfiguration('servers.c')
let res = conf.get<string>('trace.server', '')
expect(res).toBe('verbose')
config.dispose()
})
it('should get user and workspace configuration', () => {
let userConfigFile = path.join(__dirname, './settings.json')
let configurations = new Configurations(userConfigFile)
let data = configurations.configuration.toData()
expect(data.user).toBeDefined()
expect(data.workspace).toBeDefined()
expect(data.defaults).toBeDefined()
let value = configurations.configuration.getValue()
expect(value.foo).toBeDefined()
expect(value.foo.bar).toBe(1)
configurations.dispose()
})
it('should override with new value', () => {
let configurations = createConfigurations()
configurations.configuration.defaults.setValue('foo', 1)
let { contents } = configurations.defaults
expect(contents.foo).toBe(1)
configurations.dispose()
})
it('should extends defaults', () => {
let configurations = createConfigurations()
configurations.extendsDefaults({ 'a.b': 1 })
configurations.extendsDefaults({ 'a.b': 2 })
let o = configurations.defaults.contents
expect(o.a.b).toBe(2)
configurations.dispose()
})
it('should update configuration', async () => {
let configurations = createConfigurations()
configurations.addFolderFile(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')
let o = config.get<number>('bar')
expect(o).toBe(1)
config.update('bar', 6)
config = configurations.getConfiguration('foo')
expect(config.get<number>('bar')).toBe(6)
expect(fn).toBeCalledTimes(1)
configurations.dispose()
})
it('should remove configuration', async () => {
let configurations = createConfigurations()
configurations.addFolderFile(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')
let o = config.get<number>('bar')
expect(o).toBe(1)
config.update('bar', null, true)
config = configurations.getConfiguration('foo')
expect(config.get<any>('bar')).toBeUndefined()
expect(fn).toBeCalledTimes(1)
configurations.dispose()
})
})
describe('parse configuration', () => {
it('should only split top level dot keys', () => {
let o = { 'x.y': 'foo' }
let [, contents] = parseConfiguration(JSON.stringify(o))
expect(contents).toEqual({ x: { y: 'foo' } })
let schema = { 'my.schema': { 'foo.bar': 1 } }
let [, obj] = parseConfiguration(JSON.stringify(schema))
expect(obj).toEqual({ my: { schema: { 'foo.bar': 1 } } })
})
})

421
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/cursors.test.ts

@ -0,0 +1,421 @@ @@ -0,0 +1,421 @@
import { Neovim } from '@chemzqm/neovim'
import { Range } from 'vscode-languageserver-types'
import Cursors from '../../cursors'
import Document from '../../model/document'
import helper from '../helper'
let nvim: Neovim
let cursors: Cursors
let ns: number
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
ns = await nvim.createNamespace('coc-cursors')
cursors = new Cursors(nvim)
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
nvim.pauseNotification()
cursors.reset()
await nvim.resumeNotification()
await helper.reset()
})
async function rangeCount(): Promise<number> {
let buf = await nvim.buffer
let markers = await helper.getMarkers(buf.id, ns)
return markers.length
}
describe('cursors#select', () => {
it('should select by position', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['a', 'b']])
await nvim.call('cursor', [1, 1])
await helper.wait(100)
doc.forceSync()
await helper.wait(100)
await cursors.select(doc.bufnr, 'position', 'n')
await helper.wait(30)
let n = await rangeCount()
expect(n).toBe(1)
await nvim.setOption('virtualedit', 'onemore')
await nvim.call('cursor', [2, 2])
await cursors.select(doc.bufnr, 'position', 'n')
n = await rangeCount()
expect(n).toBe(2)
await cursors.select(doc.bufnr, 'position', 'n')
n = await rangeCount()
expect(n).toBe(1)
})
it('should select by word', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['foo', 'bar']])
await nvim.call('cursor', [1, 1])
await helper.wait(30)
doc.forceSync()
await cursors.select(doc.bufnr, 'word', 'n')
let n = await rangeCount()
expect(n).toBe(1)
await nvim.call('cursor', [2, 2])
await cursors.select(doc.bufnr, 'word', 'n')
n = await rangeCount()
expect(n).toBe(2)
await cursors.select(doc.bufnr, 'word', 'n')
n = await rangeCount()
expect(n).toBe(1)
})
it('should select last character', async () => {
let doc = await helper.createDocument()
await nvim.setOption('virtualedit', 'onemore')
await nvim.call('setline', [1, ['}', '{']])
await nvim.call('cursor', [1, 2])
await helper.wait(30)
doc.forceSync()
await cursors.select(doc.bufnr, 'word', 'n')
let n = await rangeCount()
expect(n).toBe(1)
await nvim.call('cursor', [2, 1])
await helper.wait(30)
doc.forceSync()
await cursors.select(doc.bufnr, 'word', 'n')
n = await rangeCount()
expect(n).toBe(2)
})
it('should select by visual range', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['"foo"', '"bar"']])
await nvim.call('cursor', [1, 1])
await nvim.command('normal! vE')
await helper.wait(30)
doc.forceSync()
await cursors.select(doc.bufnr, 'range', 'v')
let n = await rangeCount()
expect(n).toBe(1)
await nvim.call('cursor', [2, 1])
await nvim.command('normal! vE')
await cursors.select(doc.bufnr, 'range', 'v')
n = await rangeCount()
expect(n).toBe(2)
await cursors.select(doc.bufnr, 'range', 'v')
n = await rangeCount()
expect(n).toBe(1)
})
it('should select by operator', async () => {
await nvim.command('nmap x <Plug>(coc-cursors-operator)')
await helper.createDocument()
await nvim.call('setline', [1, ['"short"', '"long"']])
await nvim.call('cursor', [1, 2])
await nvim.input('xa"')
await helper.wait(30)
await nvim.call('cursor', [2, 2])
await nvim.input('xa"')
await helper.wait(30)
await nvim.command('nunmap x')
})
})
describe('cursors#addRanges', () => {
it('should add ranges', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['foo foo foo', 'bar bar']])
await helper.wait(30)
doc.forceSync()
let ranges = [
Range.create(0, 0, 0, 3),
Range.create(0, 4, 0, 7),
Range.create(0, 8, 0, 11),
Range.create(1, 0, 1, 3),
Range.create(1, 4, 1, 7)
]
await cursors.addRanges(ranges)
let n = await rangeCount()
expect(n).toBe(5)
})
})
describe('cursors#onchange', () => {
async function setup(): Promise<Document> {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['foo foo foo', 'bar bar']])
await helper.wait(30)
doc.forceSync()
let ranges = [
Range.create(0, 0, 0, 3),
Range.create(0, 4, 0, 7),
Range.create(0, 8, 0, 11),
Range.create(1, 0, 1, 3),
Range.create(1, 4, 1, 7)
]
await cursors.addRanges(ranges)
await nvim.call('cursor', [1, 1])
return doc
}
it('should ignore change after last range', async () => {
let doc = await setup()
await doc.buffer.append(['append'])
doc.forceSync()
await helper.wait(50)
let n = await rangeCount()
expect(n).toBe(5)
})
it('should adjust ranges on change before first line', async () => {
let doc = await setup()
await doc.buffer.setLines(['prepend'], { start: 0, end: 0, strictIndexing: false })
doc.forceSync()
await helper.wait(200)
let n = await rangeCount()
expect(n).toBe(5)
await nvim.call('cursor', [2, 1])
await nvim.input('ia')
await helper.wait(100)
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['prepend', 'afoo afoo afoo', 'abar abar'])
})
it('should work when change made to unrelated line', async () => {
let doc = await setup()
await doc.buffer.setLines(['prepend'], { start: 0, end: 0, strictIndexing: false })
doc.forceSync()
await helper.wait(200)
let n = await rangeCount()
expect(n).toBe(5)
await nvim.call('cursor', [1, 1])
await nvim.input('ia')
await helper.wait(200)
doc.forceSync()
await helper.wait(100)
await nvim.call('cursor', [2, 1])
await nvim.input('a')
await helper.wait(100)
await doc.synchronize()
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['aprepend', 'afoo afoo afoo', 'abar abar'])
})
it('should add text before', async () => {
let doc = await setup()
await nvim.input('iabc')
await helper.wait(30)
await doc.synchronize()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['abcfoo abcfoo abcfoo', 'abcbar abcbar'])
})
it('should add text after', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 4])
await nvim.input('iabc')
await helper.wait(30)
await doc.synchronize()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['fooabc fooabc fooabc', 'barabc barabc'])
})
it('should add text around', async () => {
let doc = await setup()
await nvim.setLine('"foo" foo foo')
await helper.wait(30)
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['"foo" "foo" "foo"', '"bar" "bar"'])
})
it('should remove text before', async () => {
let doc = await setup()
await nvim.command('normal! x')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['oo oo oo', 'ar ar'])
})
it('should remove text middle', async () => {
let doc = await setup()
await nvim.call('cursor', [2, 2])
await nvim.command('normal! x')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['fo fo fo', 'br br'])
})
it('should remove text after', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 3])
await nvim.command('normal! x')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['fo fo fo', 'ba ba'])
})
it('should remove text around', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['"foo" "bar"']])
await helper.wait(30)
doc.forceSync()
let ranges = [
Range.create(0, 0, 0, 5),
Range.create(0, 6, 0, 11)
]
await cursors.addRanges(ranges)
await nvim.call('cursor', [1, 2])
await nvim.setLine('foo "bar"')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['foo bar'])
})
it('should replace text before', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 1])
await nvim.command('normal! ra')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['aoo aoo aoo', 'aar aar'])
})
it('should replace text after', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 3])
await nvim.command('normal! ra')
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['foa foa foa', 'baa baa'])
})
it('should replace text middle', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 2])
await nvim.input('sab')
await helper.wait(30)
doc.forceSync()
await helper.wait(100)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['fabo fabo fabo', 'babr babr'])
})
it('should adjust undo & redo on add & remove', async () => {
let doc = await setup()
await nvim.call('cursor', [1, 4])
await nvim.input('iabc')
await helper.wait(30)
doc.forceSync()
let n = await rangeCount()
expect(n).toBe(5)
await helper.wait(30)
await nvim.command('undo')
await helper.wait(30)
doc.forceSync()
n = await rangeCount()
expect(n).toBe(5)
await helper.wait(30)
await nvim.command('redo')
await helper.wait(30)
doc.forceSync()
expect(await rangeCount()).toBe(5)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['fooabc fooabc fooabc', 'barabc barabc'])
})
it('should adjust undo & redo on change around', async () => {
let doc = await setup()
await nvim.setLine('"foo" foo foo')
await helper.wait(30)
doc.forceSync()
expect(await rangeCount()).toBe(5)
await helper.wait(30)
await nvim.command('undo')
await helper.wait(30)
doc.forceSync()
expect(await rangeCount()).toBe(5)
await helper.wait(30)
await nvim.command('redo')
await helper.wait(30)
doc.forceSync()
expect(await rangeCount()).toBe(5)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['"foo" "foo" "foo"', '"bar" "bar"'])
})
})
describe('cursors#keymaps', () => {
async function setup(): Promise<void> {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['a', 'b', 'c']])
await helper.wait(30)
doc.forceSync()
await nvim.call('cursor', [1, 1])
await cursors.select(doc.bufnr, 'position', 'n')
await helper.wait(30)
await nvim.call('cursor', [2, 1])
await cursors.select(doc.bufnr, 'position', 'n')
await helper.wait(30)
await nvim.call('cursor', [3, 1])
await cursors.select(doc.bufnr, 'position', 'n')
}
async function hasKeymap(key): Promise<boolean> {
let buf = await nvim.buffer
let keymaps = await buf.getKeymap('n') as any
return keymaps.find(o => o.lhs == key) != null
}
it('should setup cancel keymap', async () => {
await setup()
let count = await rangeCount()
expect(count).toBe(3)
await nvim.input('<esc>')
await helper.wait(100)
count = await rangeCount()
expect(count).toBe(0)
let has = await hasKeymap('<Esc>')
expect(has).toBe(false)
})
it('should setup next key', async () => {
await setup()
await nvim.input('<C-n>')
await helper.wait(50)
let cursor = await nvim.call('coc#cursor#position')
expect(cursor).toEqual([0, 0])
await nvim.input('<C-n>')
await helper.wait(50)
cursor = await nvim.call('coc#cursor#position')
expect(cursor).toEqual([1, 0])
})
it('should setup previous key', async () => {
await setup()
await nvim.input('<C-p>')
await helper.wait(50)
let cursor = await nvim.call('coc#cursor#position')
expect(cursor).toEqual([1, 0])
await nvim.input('<C-p>')
await helper.wait(50)
cursor = await nvim.call('coc#cursor#position')
expect(cursor).toEqual([0, 0])
})
})

60
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/db.test.ts

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
import DB from '../../model/db'
import path from 'path'
let db: DB
beforeAll(async () => {
db = new DB(path.join(__dirname, 'db.json'))
})
afterAll(async () => {
db.destroy()
})
afterEach(async () => {
db.clear()
})
describe('DB', () => {
test('db.exists()', async () => {
let exists = db.exists('a.b')
expect(exists).toBe(false)
db.push('a.b', { foo: 1 })
exists = db.exists('a.b.foo')
expect(exists).toBe(true)
})
test('db.fetch()', async () => {
let res = await db.fetch('x')
expect(res).toBeUndefined()
db.push('x', 1)
res = await db.fetch('x')
expect(res).toBe(1)
db.push('x', { foo: 1 })
res = await db.fetch('x')
expect(res).toEqual({ foo: 1 })
})
test('db.delete()', async () => {
db.push('foo.bar', 1)
db.delete('foo.bar')
let exists = db.exists('foo.bar')
expect(exists).toBe(false)
})
test('db.push()', async () => {
db.push('foo.x', 1)
db.push('foo.y', '2')
db.push('foo.z', true)
db.push('foo.n', null)
db.push('foo.o', { x: 1 })
let res = db.fetch('foo')
expect(res).toEqual({
x: 1,
y: '2',
z: true,
n: null,
o: { x: 1 }
})
})
})

27
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/decorator.test.ts

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
import * as decorator from '../../util/decorator'
class CallTest {
public count = 0
@decorator.memorize
public async memorized(): Promise<number> { return ++this.count }
}
describe('memorize', () => {
test('overlapping', async () => {
const c = new CallTest()
const first = c.memorized()
const second = c.memorized()
expect(await first).toBe(1)
expect(await second).toBe(2)
})
test('nonoverlapping', async () => {
const c = new CallTest()
const first = c.memorized()
expect(await first).toBe(1)
const second = c.memorized()
expect(await second).toBe(1)
})
})

250
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticBuffer.test.ts

@ -0,0 +1,250 @@ @@ -0,0 +1,250 @@
import helper from '../helper'
import { Neovim } from '@chemzqm/neovim'
import { DiagnosticBuffer } from '../../diagnostic/buffer'
import { Range, DiagnosticSeverity, Diagnostic, DiagnosticTag, Position } from 'vscode-languageserver-types'
import workspace from '../../workspace'
let nvim: Neovim
const config: any = {
autoRefresh: true,
checkCurrentLine: false,
locationlistUpdate: true,
enableSign: true,
enableHighlightLineNumber: true,
enableMessage: 'always',
messageTarget: 'echo',
messageDelay: 250,
refreshOnInsertMode: false,
virtualTextSrcId: 99,
virtualText: false,
virtualTextCurrentLineOnly: true,
virtualTextPrefix: " ",
virtualTextLines: 3,
virtualTextLineSeparator: " \\ ",
displayByAle: false,
level: DiagnosticSeverity.Hint,
signPriority: 11,
errorSign: '>>',
warningSign: '>>',
infoSign: '>>',
hintSign: '>>',
filetypeMap: {
default: ''
},
}
async function createDiagnosticBuffer(): Promise<DiagnosticBuffer> {
let doc = await helper.createDocument()
return new DiagnosticBuffer(nvim, doc.bufnr, doc.uri, config, () => {
// noop
})
}
function createDiagnostic(msg: string, range?: Range, severity?: DiagnosticSeverity, tags?: DiagnosticTag[]): Diagnostic & { collection: string } {
range = range ? range : Range.create(0, 0, 0, 1)
return Object.assign(Diagnostic.create(range, msg, severity || DiagnosticSeverity.Error, 999, 'test'), { collection: 'test', tags })
}
let ns: number
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
ns = await nvim.createNamespace('coc-diagnostic')
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('diagnostic buffer', () => {
describe('refresh()', () => {
it('should add signs', async () => {
let diagnostics = [createDiagnostic('foo'), createDiagnostic('bar')]
let buf = await createDiagnosticBuffer()
buf.addSigns('a', diagnostics)
await helper.wait(30)
let res = await nvim.call('sign_getplaced', [buf.bufnr, { group: 'CocDiagnostica' }])
let signs = res[0].signs
expect(signs).toBeDefined()
expect(signs[0].name).toBe('CocError')
})
it('should set diagnostic info', async () => {
let r = Range.create(0, 1, 0, 2)
let diagnostics = [
createDiagnostic('foo', r, DiagnosticSeverity.Error),
createDiagnostic('bar', r, DiagnosticSeverity.Warning),
createDiagnostic('foo', r, DiagnosticSeverity.Hint),
createDiagnostic('bar', r, DiagnosticSeverity.Information)
]
let buf = await createDiagnosticBuffer()
await buf.refresh({ '': diagnostics })
let buffer = await nvim.buffer
let res = await buffer.getVar('coc_diagnostic_info')
expect(res).toEqual({
lnums: [1, 1, 1, 1],
information: 1,
hint: 1,
warning: 1,
error: 1
})
})
it('should add highlight', async () => {
let buf = await createDiagnosticBuffer()
let doc = workspace.getDocument(buf.bufnr)
await nvim.setLine('abc')
await doc.patchChange(true)
nvim.pauseNotification()
buf.updateHighlights('', [
createDiagnostic('foo', Range.create(0, 0, 0, 1), DiagnosticSeverity.Error),
createDiagnostic('bar', Range.create(0, 0, 0, 1), DiagnosticSeverity.Warning)
])
await nvim.resumeNotification()
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any
expect(res).toEqual([
[
1,
0,
0,
{
hl_group: 'CocWarningHighlight',
priority: 4096,
end_col: 1,
end_row: 0
}
],
[
2,
0,
0,
{
hl_group: 'CocErrorHighlight',
priority: 4096,
end_col: 1,
end_row: 0
}
]
])
nvim.pauseNotification()
buf.updateHighlights('', [])
await nvim.resumeNotification()
res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any[]
expect(res.length).toBe(0)
})
it('should add deprecated highlight', async () => {
let diagnostic = createDiagnostic('foo', Range.create(0, 0, 0, 1), DiagnosticSeverity.Information, [DiagnosticTag.Deprecated])
let buf = await createDiagnosticBuffer()
let doc = workspace.getDocument(buf.bufnr)
await nvim.setLine('foo')
await doc.patchChange(true)
nvim.pauseNotification()
buf.updateHighlights('', [diagnostic])
await nvim.resumeNotification()
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, {}]) as [number, number, number][]
expect(res.length).toBe(1)
})
})
describe('showVirtualText()', () => {
beforeEach(async () => {
config.virtualText = true
config.virtualTextSrcId = await nvim.createNamespace('diagnostics-virtualText')
})
afterEach(() => {
config.virtualText = false
config.virtualTextCurrentLineOnly = true
})
it('should show virtual text on current line', async () => {
let diagnostic = createDiagnostic('foo')
let buf = await createDiagnosticBuffer()
let diagnostics = [diagnostic]
await buf.refresh({ '': diagnostics })
let ns = config.virtualTextSrcId
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any
expect(res.length).toBe(1)
let texts = res[0][3].virt_text
expect(texts[0]).toEqual([' foo', 'CocErrorVirtualText'])
})
it('should virtual text on all lines', async () => {
config.virtualTextCurrentLineOnly = false
let buf = await createDiagnosticBuffer()
let diagnostics = [
createDiagnostic('foo', Range.create(0, 0, 0, 1)),
createDiagnostic('bar', Range.create(1, 0, 1, 1)),
]
await buf.refresh({ '': diagnostics })
let ns = config.virtualTextSrcId
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any
expect(res.length).toBe(2)
})
})
describe('updateLocationList()', () => {
beforeEach(async () => {
config.locationlistUpdate = true
})
afterEach(() => {
config.locationlistUpdate = false
})
it('should update location list', async () => {
let buf = await createDiagnosticBuffer()
await nvim.call('setloclist', [0, [], 'r', { title: 'Diagnostics of coc', items: [] }])
await buf.refresh({
a: [createDiagnostic('foo')]
})
let res = await nvim.eval(`getloclist(bufwinid(${buf.bufnr}))`) as any[]
expect(res.length).toBe(1)
expect(res[0].text).toBe('[test 999] foo [E]')
})
})
describe('clear()', () => {
let config = workspace.getConfiguration('diagnostic')
beforeEach(() => {
config.update('virtualText', true)
})
afterEach(() => {
config.update('virtualText', false)
})
it('should clear all diagnostics', async () => {
let diagnostic = createDiagnostic('foo')
let buf = await createDiagnosticBuffer()
let diagnostics = [diagnostic]
await buf.refresh({ '': diagnostics })
buf.clear()
await helper.wait(50)
let buffer = await nvim.buffer
let res = await buffer.getVar("coc_diagnostic_info")
expect(res == null).toBe(true)
})
})
describe('getDiagnostics()', () => {
it('should get sorted diagnostics', async () => {
let buf = await createDiagnosticBuffer()
let diagnostics = [
createDiagnostic('three', Range.create(0, 1, 0, 2), DiagnosticSeverity.Error),
createDiagnostic('one', Range.create(0, 0, 0, 2), DiagnosticSeverity.Warning),
createDiagnostic('two', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error),
]
diagnostics[0].tags = [DiagnosticTag.Unnecessary]
await buf.refresh({
x: diagnostics,
y: [createDiagnostic('four', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error)]
})
let res = buf.getDiagnosticsAt(Position.create(0, 1), false)
let arr = res.map(o => o.message)
expect(arr).toEqual(['four', 'two', 'three', 'one'])
})
})
})

98
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticCollection.test.ts

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
import DiagnosticCollection from '../../diagnostic/collection'
import { Diagnostic, Range } from 'vscode-languageserver-types'
function createDiagnostic(msg: string, range?: Range): Diagnostic {
range = range ? range : Range.create(0, 0, 0, 1)
return Diagnostic.create(range, msg)
}
describe('diagnostic collection', () => {
it('should create collection', () => {
let collection = new DiagnosticCollection('test')
expect(collection.name).toBe('test')
})
it('should set diagnostic with uri', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
collection.set(uri, [diagnostic])
expect(collection.get(uri).length).toBe(1)
collection.set(uri, [])
expect(collection.get(uri).length).toBe(0)
})
it('should clear diagnostics with null as diagnostics', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
collection.set(uri, [diagnostic])
expect(collection.get(uri).length).toBe(1)
collection.set(uri, null)
expect(collection.get(uri).length).toBe(0)
})
it('should clear diagnostics with undefined as diagnostics in entries', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let entries: [string, Diagnostic[] | null][] = [
['file:1', [diagnostic]],
['file:1', undefined]
]
let uri = 'file:///1'
collection.set(entries)
expect(collection.get(uri).length).toBe(0)
})
it('should set diagnostics with entries', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
let other = 'file:///2'
let entries: [string, Diagnostic[]][] = [
[uri, [diagnostic]],
[other, [diagnostic]],
[uri, [createDiagnostic('other')]]
]
collection.set(entries)
expect(collection.get(uri).length).toBe(2)
expect(collection.get(other).length).toBe(1)
})
it('should delete diagnostics for uri', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
collection.set(uri, [diagnostic])
collection.delete(uri)
expect(collection.get(uri).length).toBe(0)
})
it('should clear all diagnostics', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
collection.set(uri, [diagnostic])
collection.clear()
expect(collection.get(uri).length).toBe(0)
})
it('should call for every uri with diagnostics', () => {
let collection = new DiagnosticCollection('test')
let diagnostic = createDiagnostic('error')
let uri = 'file:///1'
let other = 'file:///2'
let entries: [string, Diagnostic[]][] = [
[uri, [diagnostic]],
[other, [diagnostic]],
[uri, [createDiagnostic('other')]]
]
collection.set(entries)
let arr: string[] = []
collection.forEach(uri => {
arr.push(uri)
})
expect(arr).toEqual([uri, other])
})
})

556
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diagnosticManager.test.ts

@ -0,0 +1,556 @@ @@ -0,0 +1,556 @@
import { Neovim } from '@chemzqm/neovim'
import path from 'path'
import { severityLevel, getNameFromSeverity } from '../../diagnostic/util'
import { Range, DiagnosticSeverity, Diagnostic, Location, DiagnosticTag } from 'vscode-languageserver-types'
import { URI } from 'vscode-uri'
import Document from '../../model/document'
import workspace from '../../workspace'
import window from '../../window'
import manager from '../../diagnostic/manager'
import helper, { createTmpFile } from '../helper'
let nvim: Neovim
function createDiagnostic(msg: string, range?: Range, severity?: DiagnosticSeverity): Diagnostic {
range = range ? range : Range.create(0, 0, 0, 1)
return Diagnostic.create(range, msg, severity || DiagnosticSeverity.Error)
}
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
manager.reset()
await helper.reset()
})
async function createDocument(name?: string): Promise<Document> {
let doc = await helper.createDocument(name)
let collection = manager.create('test')
let diagnostics: Diagnostic[] = []
await doc.buffer.setLines(['foo bar foo bar', 'foo bar', 'foo', 'bar'], {
start: 0,
end: -1,
strictIndexing: false
})
diagnostics.push(createDiagnostic('error', Range.create(0, 2, 0, 4), DiagnosticSeverity.Error))
diagnostics.push(createDiagnostic('warning', Range.create(0, 5, 0, 6), DiagnosticSeverity.Warning))
diagnostics.push(createDiagnostic('information', Range.create(1, 0, 1, 1), DiagnosticSeverity.Information))
diagnostics.push(createDiagnostic('hint', Range.create(1, 2, 1, 3), DiagnosticSeverity.Hint))
diagnostics.push(createDiagnostic('error', Range.create(2, 0, 2, 2), DiagnosticSeverity.Error))
collection.set(doc.uri, diagnostics)
doc.forceSync()
return doc
}
describe('diagnostic manager', () => {
describe('refresh()', () => {
it('should refresh on buffer create', async () => {
let uri = URI.file(path.join(path.dirname(__dirname), 'doc')).toString()
let fn = jest.fn()
let disposable = manager.onDidRefresh(() => {
fn()
})
let collection = manager.create('tmp')
let diagnostic = createDiagnostic('My Error')
collection.set(uri, [diagnostic])
let doc = await helper.createDocument('doc')
let val = await doc.buffer.getVar('coc_diagnostic_info') as any
expect(fn).toBeCalled()
expect(val).toBeDefined()
expect(val.error).toBe(1)
collection.dispose()
disposable.dispose()
})
it('should delay refresh on InsertLeave', async () => {
let doc = await helper.createDocument()
await nvim.input('i')
let collection = manager.create('test')
let diagnostics: Diagnostic[] = []
await doc.buffer.setLines(['foo bar foo bar', 'foo bar', 'foo', 'bar'], {
start: 0,
end: -1,
strictIndexing: false
})
diagnostics.push(createDiagnostic('error', Range.create(0, 2, 0, 4), DiagnosticSeverity.Error))
collection.set(doc.uri, diagnostics)
await helper.wait(10)
await nvim.input('<esc>')
await helper.wait(100)
let val = await doc.buffer.getVar('coc_diagnostic_info') as any
expect(val).toBe(null)
await helper.wait(600)
val = await doc.buffer.getVar('coc_diagnostic_info') as any
expect(val).toBeDefined()
})
})
describe('toggleDiagnostic()', () => {
it('should toggle diagnostics', async () => {
let doc = await createDocument()
await helper.wait(50)
manager.toggleDiagnostic()
await helper.wait(50)
let val = await doc.buffer.getVar('coc_diagnostic_info') as any
expect(val).toBe(null)
manager.toggleDiagnostic()
await helper.wait(50)
val = await doc.buffer.getVar('coc_diagnostic_info') as any
expect(val).toBeDefined()
expect(val.error).toBe(2)
})
})
describe('getDiagnosticList()', () => {
it('should get all diagnostics', async () => {
await createDocument()
let list = manager.getDiagnosticList()
expect(list).toBeDefined()
expect(list.length).toBeGreaterThanOrEqual(5)
expect(list[0].severity).toBe('Error')
expect(list[1].severity).toBe('Error')
expect(list[2].severity).toBe('Warning')
expect(list[3].severity).toBe('Information')
expect(list[4].severity).toBe('Hint')
})
it('should filter diagnostics by configuration', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('level', 'warning')
config.update('showUnused', false)
config.update('showDeprecated', false)
let doc = await createDocument()
let diagnostics = manager.getDiagnostics(doc.uri)['test']
diagnostics[0].tags = [DiagnosticTag.Unnecessary]
diagnostics[2].tags = [DiagnosticTag.Deprecated]
let collection = manager.getCollectionByName('test')
collection.set(doc.uri, diagnostics)
let list = manager.getDiagnosticList()
expect(list.length).toBe(1)
expect(list[0].severity).toBe('Warning')
let res = manager.getDiagnostics(doc.uri)['test']
expect(res.length).toBe(1)
helper.updateConfiguration('diagnostic.level', 'hint')
helper.updateConfiguration('diagnostic.showUnused', true)
helper.updateConfiguration('diagnostic.showDeprecated', true)
let ranges = manager.getSortedRanges(doc.uri)
expect(ranges.length).toBe(3)
})
})
describe('preview()', () => {
it('should not throw with empty diagnostics', async () => {
await helper.createDocument()
await manager.preview()
let tabpage = await nvim.tabpage
let wins = await tabpage.windows
expect(wins.length).toBe(1)
})
it('should open preview window', async () => {
await createDocument()
await nvim.call('cursor', [1, 3])
await manager.preview()
let res = await nvim.call('coc#window#find', ['&previewwindow', 1])
expect(res).toBeDefined()
await nvim.call('win_gotoid', [res])
let buf = await nvim.buffer
let lines = await buf.lines
expect(lines[0]).toEqual('[test] [E]')
})
})
describe('setLocationlist()', () => {
it('should set location list', async () => {
let doc = await createDocument()
await manager.setLocationlist(doc.bufnr)
await nvim.command('lopen')
let buftype = await nvim.eval('&buftype') as string
expect(buftype).toBe('quickfix')
})
})
describe('setConfigurationErrors()', () => {
it('should set configuration errors', async () => {
let doc = await helper.createDocument()
let errors = [{
location: Location.create(doc.uri, Range.create(0, 0, 1, 0)),
message: 'foo',
}, {
location: Location.create(doc.uri, Range.create(1, 0, 2, 0)),
message: 'bar',
}]
manager.setConfigurationErrors(errors)
await helper.wait(50)
let res = manager.getDiagnostics(doc.uri, 'config')['config']
expect(res.length).toBe(2)
manager.setConfigurationErrors()
await helper.wait(50)
res = manager.getDiagnostics(doc.uri, 'config')['config']
expect(res.length).toBe(0)
})
})
describe('create()', () => {
it('should create diagnostic collection', async () => {
let doc = await helper.createDocument()
let collection = manager.create('test')
collection.set(doc.uri, [createDiagnostic('foo')])
await helper.wait(50)
let info = await doc.buffer.getVar('coc_diagnostic_info')
expect(info).toBeDefined()
await nvim.command('bd!')
await helper.wait(50)
})
})
describe('getSortedRanges()', () => {
it('should get sorted ranges of document', async () => {
let doc = await helper.createDocument()
await nvim.call('setline', [1, ['a', 'b', 'c']])
let collection = manager.create('test')
let diagnostics: Diagnostic[] = []
diagnostics.push(createDiagnostic('x', Range.create(0, 0, 0, 1)))
diagnostics.push(createDiagnostic('y', Range.create(0, 1, 0, 2)))
diagnostics.push(createDiagnostic('z', Range.create(1, 0, 1, 2)))
collection.set(doc.uri, diagnostics)
let ranges = manager.getSortedRanges(doc.uri)
expect(ranges[0]).toEqual(Range.create(0, 0, 0, 1))
expect(ranges[1]).toEqual(Range.create(0, 1, 0, 2))
expect(ranges[2]).toEqual(Range.create(1, 0, 1, 2))
ranges = manager.getSortedRanges(doc.uri, 'error')
expect(ranges.length).toBe(3)
expect(manager.getSortedRanges(doc.uri, 'warning').length).toBe(0)
})
})
describe('getDiagnosticsInRange', () => {
it('should get diagnostics in range', async () => {
let doc = await helper.createDocument()
let collection = manager.create('test')
let diagnostics: Diagnostic[] = []
await doc.buffer.setLines(['foo bar foo bar', 'foo bar'], {
start: 0,
end: -1,
strictIndexing: false
})
await helper.wait(300)
diagnostics.push(createDiagnostic('a', Range.create(0, 0, 0, 1)))
diagnostics.push(createDiagnostic('b', Range.create(0, 2, 0, 3)))
diagnostics.push(createDiagnostic('c', Range.create(1, 0, 1, 2)))
collection.set(doc.uri, diagnostics)
let res = manager.getDiagnosticsInRange(doc.textDocument, Range.create(0, 0, 0, 3))
expect(res.length).toBe(2)
})
})
describe('getCurrentDiagnostics', () => {
it('should get diagnostics under corsor', async () => {
let config = workspace.getConfiguration('diagnostic')
await createDocument()
let diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBe(0)
await nvim.call('cursor', [1, 4])
diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBe(1)
config.update('checkCurrentLine', true)
await nvim.call('cursor', [1, 2])
diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBe(2)
config.update('checkCurrentLine', false)
})
it('should get empty diagnostic at end of line', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo')
doc.forceSync()
await nvim.command('normal! $')
let diagnostic = Diagnostic.create(Range.create(0, 3, 1, 0), 'error', DiagnosticSeverity.Error)
let collection = manager.create('empty')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.bufnr, true)
let diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBeGreaterThanOrEqual(1)
expect(diagnostics[0].message).toBe('error')
collection.dispose()
})
it('should get diagnostic next to end of line', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo')
doc.forceSync()
await nvim.command('normal! $')
let diagnostic = Diagnostic.create(Range.create(0, 3, 0, 4), 'error', DiagnosticSeverity.Error)
let collection = manager.create('empty')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.bufnr, true)
let diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBeGreaterThanOrEqual(1)
expect(diagnostics[0].message).toBe('error')
collection.dispose()
})
it('should get diagnostic with empty range at end of line', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo')
doc.forceSync()
await nvim.command('normal! $')
let diagnostic = Diagnostic.create(Range.create(0, 3, 1, 0), 'error', DiagnosticSeverity.Error)
let collection = manager.create('empty')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.bufnr, true)
let diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBeGreaterThanOrEqual(1)
expect(diagnostics[0].message).toBe('error')
collection.dispose()
})
it('should get diagnostic pass end of the buffer lines', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo')
doc.forceSync()
await nvim.command('normal! ^')
let diagnostic = Diagnostic.create(Range.create(1, 0, 1, 0), 'error', DiagnosticSeverity.Error)
let collection = manager.create('empty')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.bufnr, true)
let diagnostics = await manager.getCurrentDiagnostics()
expect(diagnostics.length).toBeGreaterThanOrEqual(1)
expect(diagnostics[0].message).toBe('error')
collection.dispose()
})
})
describe('jumpRelated', () => {
it('should jump to related position', async () => {
let doc = await helper.createDocument()
let range = Range.create(0, 0, 0, 10)
let location = Location.create(URI.file(__filename).toString(), range)
let diagnostic = Diagnostic.create(range, 'msg', DiagnosticSeverity.Error, 1000, 'test',
[{ location, message: 'test' }])
let collection = manager.create('positions')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.uri, true)
await nvim.call('cursor', [1, 1])
await manager.jumpRelated()
await helper.wait(100)
let bufname = await nvim.call('bufname', '%')
expect(bufname).toMatch('diagnosticManager')
})
it('should open location list', async () => {
let doc = await helper.createDocument()
let range = Range.create(0, 0, 0, 10)
let diagnostic = Diagnostic.create(range, 'msg', DiagnosticSeverity.Error, 1000, 'test',
[{
location: Location.create(URI.file(__filename).toString(), Range.create(1, 0, 1, 10)),
message: 'foo'
}, {
location: Location.create(URI.file(__filename).toString(), Range.create(2, 0, 2, 10)),
message: 'bar'
}])
let collection = manager.create('positions')
collection.set(doc.uri, [diagnostic])
await manager.refreshBuffer(doc.uri, true)
await nvim.call('cursor', [1, 1])
await manager.jumpRelated()
await helper.wait(100)
let bufname = await nvim.call('bufname', '%')
expect(bufname).toBe('list:///location')
})
})
describe('jumpPrevious & jumpNext', () => {
it('should jump to previous', async () => {
let doc = await createDocument()
await nvim.command('normal! G$')
let ranges = manager.getSortedRanges(doc.uri)
ranges.reverse()
for (let i = 0; i < ranges.length; i++) {
await manager.jumpPrevious()
let pos = await window.getCursorPosition()
expect(pos).toEqual(ranges[i].start)
}
await manager.jumpPrevious()
})
it('should jump to next', async () => {
let doc = await createDocument()
await nvim.call('cursor', [0, 0])
let ranges = manager.getSortedRanges(doc.uri)
for (let i = 0; i < ranges.length; i++) {
await manager.jumpNext()
let pos = await window.getCursorPosition()
expect(pos).toEqual(ranges[i].start)
}
await manager.jumpNext()
})
})
describe('diagnostic configuration', () => {
it('should use filetype map from config', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('filetypeMap', { default: 'bufferType' })
let doc = await createDocument('foo.js')
let collection = manager.getCollectionByName('test')
let diagnostics = [createDiagnostic('99', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error)]
collection.set(doc.uri, diagnostics)
await nvim.call('cursor', [1, 1])
await nvim.command('doautocmd CursorHold')
let winid = await helper.waitFloat()
await nvim.call('win_gotoid', [winid])
await nvim.command('normal! $')
let res = await nvim.eval('synIDattr(synID(line("."),col("."),1),"name")')
expect(res).toMatch(/javascript/i)
config.update('filetypeMap', {})
})
it('should show floating window on cursor hold', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('messageTarget', 'float')
await createDocument()
await nvim.call('cursor', [1, 3])
await nvim.command('doautocmd CursorHold')
let winid = await helper.waitFloat()
let bufnr = await nvim.call('nvim_win_get_buf', winid) as number
let buf = nvim.createBuffer(bufnr)
let lines = await buf.lines
expect(lines.join('\n')).toMatch('error')
})
it('should echo messages on cursor hold', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('messageTarget', 'echo')
await createDocument()
await nvim.call('cursor', [1, 3])
await helper.wait(600)
let line = await helper.getCmdline()
expect(line).toMatch('error')
config.update('messageTarget', 'float')
})
it('should show diagnostics of current line', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('checkCurrentLine', true)
await createDocument()
await nvim.call('cursor', [1, 1])
let winid = await helper.waitFloat()
let bufnr = await nvim.call('nvim_win_get_buf', winid) as number
let buf = nvim.createBuffer(bufnr)
let lines = await buf.lines
expect(lines.length).toBe(3)
config.update('checkCurrentLine', false)
})
it('should filter diagnostics by level', async () => {
helper.updateConfiguration('diagnostic.level', 'warning')
let doc = await createDocument()
let diagnosticsMap = manager.getDiagnostics(doc.uri)
for (let diagnostics of Object.values(diagnosticsMap)) {
for (let diagnostic of diagnostics) {
expect(diagnostic.severity != DiagnosticSeverity.Hint).toBe(true)
expect(diagnostic.severity != DiagnosticSeverity.Information).toBe(true)
}
}
helper.updateConfiguration('diagnostic.level', 'hint')
})
it('should send ale diagnostic items', async () => {
let config = workspace.getConfiguration('diagnostic')
config.update('displayByAle', true)
let content = `
function! MockAleResults(bufnr, collection, items)
let g:collection = a:collection
let g:items = a:items
endfunction
`
let file = await createTmpFile(content)
await nvim.command(`source ${file}`)
await createDocument()
await helper.wait(50)
let items = await nvim.getVar('items') as any[]
expect(Array.isArray(items)).toBe(true)
expect(items.length).toBeGreaterThan(0)
await nvim.command('bd!')
await helper.wait(50)
items = await nvim.getVar('items') as any[]
expect(items).toEqual([])
config.update('displayByAle', false)
})
})
describe('severityLevel & getNameFromSeverity', () => {
it('should get severity level', () => {
expect(severityLevel('hint')).toBe(DiagnosticSeverity.Hint)
expect(severityLevel('error')).toBe(DiagnosticSeverity.Error)
expect(severityLevel('warning')).toBe(DiagnosticSeverity.Warning)
expect(severityLevel('information')).toBe(DiagnosticSeverity.Information)
expect(severityLevel('')).toBe(DiagnosticSeverity.Hint)
})
it('should get severity name', () => {
expect(getNameFromSeverity(null as any)).toBe('CocError')
})
})
describe('toggleDiagnosticBuffer', () => {
it('should toggle diagnostics for buffer', async () => {
let doc = await createDocument()
// required to wait refresh finish
await helper.wait(50)
await manager.toggleDiagnosticBuffer(doc.bufnr)
await helper.wait(50)
let buf = nvim.createBuffer(doc.bufnr)
let res = await buf.getVar('coc_diagnostic_info') as any
expect(res == null).toBe(true)
await manager.toggleDiagnosticBuffer(doc.bufnr)
await helper.wait(50)
res = await buf.getVar('coc_diagnostic_info') as any
expect(res.error).toBe(2)
})
})
describe('refresh', () => {
let config = workspace.getConfiguration('diagnostic')
beforeEach(() => {
config.update('autoRefresh', false)
})
afterEach(() => {
config.update('autoRefresh', true)
})
it('should refresh by bufnr', async () => {
let doc = await createDocument()
let buf = nvim.createBuffer(doc.bufnr)
let res = await buf.getVar('coc_diagnostic_info') as any
// should not refresh
expect(res == null).toBe(true)
manager.refresh(doc.bufnr)
await helper.wait(100)
res = await buf.getVar('coc_diagnostic_info') as any
expect(res?.error).toBe(2)
})
it('should refresh all buffers', async () => {
let one = await helper.createDocument('one')
let two = await helper.createDocument('two')
let collection = manager.create('tmp')
collection.set([[one.uri, [createDiagnostic('Error one')]], [two.uri, [createDiagnostic('Error two')]]])
manager.refresh()
await helper.wait(50)
for (let bufnr of [one.bufnr, two.bufnr]) {
let buf = nvim.createBuffer(bufnr)
let res = await buf.getVar('coc_diagnostic_info') as any
expect(res?.error).toBe(1)
}
collection.dispose()
})
})
})

60
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/dialog.test.ts

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
import { Neovim } from '@chemzqm/neovim'
import Dialog, { DialogButton } from '../../model/dialog'
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('Dialog module', () => {
it('should show dialog', async () => {
let dialog = new Dialog(nvim, { content: '你好' })
await dialog.show({})
let winid = await dialog.winid
let win = nvim.createWindow(winid)
let width = await win.width
expect(width).toBe(4)
await nvim.call('coc#float#close', [winid])
})
it('should invoke callback with index -1', async () => {
let callback = jest.fn()
let dialog = new Dialog(nvim, { content: '你好', callback })
await dialog.show({})
let winid = await dialog.winid
await nvim.call('coc#float#close', [winid])
await helper.wait(50)
expect(callback).toHaveBeenCalledWith(-1)
})
it('should invoke callback on click', async () => {
let callback = jest.fn()
let buttons: DialogButton[] = [{
index: 0,
text: 'yes'
}, {
index: 1,
text: 'no'
}]
let dialog = new Dialog(nvim, { content: '你好', buttons, callback })
await dialog.show({})
let winid = await dialog.winid
let btnwin = await nvim.call('coc#float#get_related', [winid, 'buttons'])
await nvim.call('win_gotoid', [btnwin])
await nvim.call('cursor', [2, 1])
await nvim.call('coc#float#nvim_float_click', [])
await helper.wait(50)
expect(callback).toHaveBeenCalledWith(0)
})
})

178
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/diff.test.ts

@ -0,0 +1,178 @@ @@ -0,0 +1,178 @@
import { TextEdit } from 'vscode-languageserver-types'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { diffLines, getChange, patchLine, ChangedLines } from '../../util/diff'
describe('diff lines', () => {
function diff(oldStr: string, newStr: string): ChangedLines {
let oldLines = oldStr.split('\n')
return diffLines(oldLines, newStr.split('\n'), oldLines.length - 2)
}
it('should diff changed lines', () => {
let res = diff('a\n', 'b\n')
expect(res).toEqual({ start: 0, end: 1, replacement: ['b'] })
})
it('should diff added lines', () => {
let res = diff('a\n', 'a\nb\n')
expect(res).toEqual({
start: 1,
end: 1,
replacement: ['b']
})
})
it('should diff remove lines', () => {
let res = diff('a\n\n', 'a\n')
expect(res).toEqual({
start: 1,
end: 2,
replacement: []
})
})
it('should diff remove multiple lines', () => {
let res = diff('a\n\n\n', 'a\n')
expect(res).toEqual({
start: 1,
end: 3,
replacement: []
})
})
it('should diff removed line', () => {
let res = diff('a\n\n\nb', 'a\n\nb')
expect(res).toEqual({
start: 2,
end: 3,
replacement: []
})
})
it('should reduce changed lines', async () => {
let res = diffLines(['a', 'b', 'c'], ['a', 'b', 'c', 'd'], 0)
expect(res).toEqual({
start: 3,
end: 3,
replacement: ['d']
})
})
})
describe('patch line', () => {
it('should patch line', () => {
let res = patchLine('foo', 'bar foo bar')
expect(res.length).toBe(7)
expect(res).toBe(' foo')
})
})
describe('should get text edits', () => {
function applyEdits(oldStr: string, newStr: string): void {
let doc = TextDocument.create('untitled://1', 'markdown', 0, oldStr)
let change = getChange(doc.getText(), newStr)
let start = doc.positionAt(change.start)
let end = doc.positionAt(change.end)
let edit: TextEdit = {
range: { start, end },
newText: change.newText
}
let res = TextDocument.applyEdits(doc, [edit])
expect(res).toBe(newStr)
}
it('should get diff for comments ', async () => {
let oldStr = '/*\n *\n * \n'
let newStr = '/*\n *\n *\n * \n'
let doc = TextDocument.create('untitled://1', 'markdown', 0, oldStr)
let change = getChange(doc.getText(), newStr, 1)
let start = doc.positionAt(change.start)
let end = doc.positionAt(change.end)
let edit: TextEdit = {
range: { start, end },
newText: change.newText
}
let res = TextDocument.applyEdits(doc, [edit])
expect(res).toBe(newStr)
})
it('should return null for same content', () => {
let change = getChange('', '')
expect(change).toBeNull()
change = getChange('abc', 'abc')
expect(change).toBeNull()
})
it('should get diff for added', () => {
applyEdits('1\n2', '1\n2\n3\n4')
})
it('should get diff for added #0', () => {
applyEdits('\n\n', '\n\n\n')
})
it('should get diff for added #1', () => {
applyEdits('1\n2\n3', '5\n1\n2\n3')
})
it('should get diff for added #2', () => {
applyEdits('1\n2\n3', '1\n2\n4\n3')
})
it('should get diff for added #3', () => {
applyEdits('1\n2\n3', '4\n1\n2\n3\n5')
})
it('should get diff for added #4', () => {
applyEdits(' ', ' ')
})
it('should get diff for replace', () => {
applyEdits('1\n2\n3\n4\n5', '1\n5\n3\n6\n7')
})
it('should get diff for replace #1', () => {
applyEdits('1\n2\n3\n4\n5', '1\n5\n3\n6\n7')
})
it('should get diff for remove #0', () => {
applyEdits('1\n2\n3\n4', '1\n4')
})
it('should get diff for remove #1', () => {
applyEdits('1\n2\n3\n4', '1')
})
it('should get diff for remove #2', () => {
applyEdits(' ', ' ')
})
it('should prefer cursor position for change', async () => {
let res = getChange(' int n', ' n', 0)
expect(res).toEqual({ start: 1, end: 5, newText: '' })
res = getChange(' int n', ' n')
expect(res).toEqual({ start: 0, end: 4, newText: '' })
})
it('should prefer next line for change', async () => {
let res = getChange('a\nb', 'a\nc\nb')
expect(res).toEqual({ start: 2, end: 2, newText: 'c\n' })
applyEdits('a\nb', 'a\nc\nb')
})
it('should prefer previous line for change', async () => {
let res = getChange('\n\na', '\na')
expect(res).toEqual({ start: 0, end: 1, newText: '' })
})
it('should consider cursor', () => {
let res = getChange('\n\n\n', '\n\n\n\n', 1)
expect(res).toEqual({ start: 2, end: 2, newText: '\n' })
})
it('should get minimal diff', () => {
let res = getChange('foo\nbar', 'fab\nbar', 2)
expect(res).toEqual({ start: 1, end: 3, newText: 'ab' })
})
})

399
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/document.test.ts

@ -0,0 +1,399 @@ @@ -0,0 +1,399 @@
import fs from 'fs'
import path from 'path'
import { Neovim } from '@chemzqm/neovim'
import { Position, Range, TextEdit } from 'vscode-languageserver-protocol'
import workspace from '../../workspace'
import helper from '../helper'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { Disposable } from '@chemzqm/neovim/lib/api/Buffer'
import { disposeAll } from '../../util'
import Document from '../../model/document'
import { URI } from 'vscode-uri'
let nvim: Neovim
jest.setTimeout(5000)
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
await helper.reset()
})
describe('properties', () => {
it('should parse iskeyword', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo bar')
doc.forceSync()
let words = doc.words
expect(words).toEqual(['foo', 'bar'])
})
it('should applyEdits', async () => {
let doc = await helper.createDocument()
let edits: TextEdit[] = []
edits.push({
range: Range.create(0, 0, 0, 0),
newText: 'a\n'
})
edits.push({
range: Range.create(0, 0, 0, 0),
newText: 'b\n'
})
await doc.applyEdits(edits)
let content = doc.getDocumentContent()
expect(content).toBe('a\nb\n\n')
})
it('should applyEdits with changed lines', async () => {
let doc = await helper.createDocument()
await nvim.setLine('a')
await doc.patchChange()
let edits: TextEdit[] = []
edits.push({
range: Range.create(0, 1, 0, 1),
newText: `\n\nd`
})
await doc.applyEdits(edits)
let lines = await nvim.call('getline', [1, '$'])
expect(lines).toEqual(['a', '', 'd'])
})
it('should parse iskeyword of character range', async () => {
await nvim.setOption('iskeyword', 'a-z,A-Z,48-57,_')
let doc = await helper.createDocument()
let opt = await nvim.getOption('iskeyword')
expect(opt).toBe('a-z,A-Z,48-57,_')
await nvim.setLine('foo bar')
doc.forceSync()
await helper.wait(100)
let words = doc.words
expect(words).toEqual(['foo', 'bar'])
})
it('should get word range', async () => {
await helper.createDocument()
await nvim.setLine('foo bar')
await helper.wait(30)
let doc = await workspace.document
let range = doc.getWordRangeAtPosition({ line: 0, character: 0 })
expect(range).toEqual(Range.create(0, 0, 0, 3))
range = doc.getWordRangeAtPosition({ line: 0, character: 3 })
expect(range).toBeNull()
range = doc.getWordRangeAtPosition({ line: 0, character: 4 })
expect(range).toEqual(Range.create(0, 4, 0, 7))
range = doc.getWordRangeAtPosition({ line: 0, character: 7 })
expect(range).toBeNull()
})
it('should get symbol ranges', async () => {
let doc = await helper.createDocument()
await nvim.setLine('foo bar foo')
let ranges = doc.getSymbolRanges('foo')
expect(ranges.length).toBe(2)
})
it('should get localify bonus', async () => {
let doc = await helper.createDocument()
let { buffer } = doc
await buffer.setLines(['context content clearTimeout', '', 'product confirm'],
{ start: 0, end: -1, strictIndexing: false })
await helper.wait(100)
let pos: Position = { line: 1, character: 0 }
let res = doc.getLocalifyBonus(pos, pos)
expect(res.has('confirm')).toBe(true)
expect(res.has('clearTimeout')).toBe(true)
})
it('should get current line', async () => {
let doc = await helper.createDocument()
let { buffer } = doc
await buffer.setLines(['first line', 'second line'],
{ start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
let line = doc.getline(1, true)
expect(line).toBe('second line')
})
it('should get cached line', async () => {
let doc = await helper.createDocument()
let { buffer } = doc
await buffer.setLines(['first line', 'second line'],
{ start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
doc.forceSync()
let line = doc.getline(0, false)
expect(line).toBe('first line')
})
it('should get variable form buffer', async () => {
await nvim.command('autocmd BufNewFile,BufRead * let b:coc_enabled = 1')
let doc = await helper.createDocument()
let val = doc.getVar<number>('enabled')
expect(val).toBe(1)
})
it('should attach change events', async () => {
let doc = await helper.createDocument()
await nvim.setLine('abc')
await helper.wait(50)
let content = doc.getDocumentContent()
expect(content.indexOf('abc')).toBe(0)
})
it('should not attach change events when b:coc_enabled is false', async () => {
await nvim.command('autocmd BufNewFile,BufRead *.dis let b:coc_enabled = 0')
let doc = await helper.createDocument('a.dis')
let val = doc.getVar<number>('enabled', 0)
expect(val).toBe(0)
await nvim.setLine('abc')
await helper.wait(50)
let content = doc.getDocumentContent()
expect(content.indexOf('abc')).toBe(-1)
})
it('should get lineCount, previewwindow, winid', async () => {
let doc = await helper.createDocument()
let { lineCount, winid, previewwindow } = doc
expect(lineCount).toBe(1)
expect(winid != -1).toBe(true)
expect(previewwindow).toBe(false)
})
})
describe('synchronize', () => {
it('should synchronize on lines change', async () => {
let document = await helper.createDocument()
let doc = TextDocument.create('untitled:1', 'txt', 1, document.getDocumentContent())
let disposables = []
document.onDocumentChange(e => {
TextDocument.update(doc, e.contentChanges, 2)
}, null, disposables)
// document.on
await nvim.setLine('abc')
document.forceSync()
expect(doc.getText()).toBe('abc\n')
disposeAll(disposables)
})
it('should synchronize changes after applyEdits', async () => {
let document = await helper.createDocument()
let doc = TextDocument.create('untitled:1', 'txt', 1, document.getDocumentContent())
let disposables = []
document.onDocumentChange(e => {
TextDocument.update(doc, e.contentChanges, e.textDocument.version)
}, null, disposables)
await nvim.setLine('abc')
await document.patchChange()
await document.applyEdits([TextEdit.insert({ line: 0, character: 0 }, 'd')])
expect(doc.getText()).toBe('dabc\n')
disposeAll(disposables)
})
})
describe('recreate', () => {
async function assertDocument(fn: (doc: Document) => Promise<void>): Promise<void> {
let disposables: Disposable[] = []
let fsPath = path.join(__dirname, 'document.txt')
fs.writeFileSync(fsPath, '{\nfoo\n}\n', 'utf8')
await helper.edit(fsPath)
let document = await workspace.document
document.forceSync()
let doc = TextDocument.create(document.uri, 'txt', document.version, document.getDocumentContent())
let uri = doc.uri
workspace.onDidOpenTextDocument(e => {
if (e.uri == uri) {
doc = TextDocument.create(e.uri, 'txt', e.version, e.getText())
}
}, null, disposables)
workspace.onDidCloseTextDocument(e => {
if (e.uri == doc.uri) doc = null
}, null, disposables)
workspace.onDidChangeTextDocument(e => {
TextDocument.update(doc, e.contentChanges, e.textDocument.version)
}, null, disposables)
await fn(document)
document = await workspace.document
document.forceSync()
let text = document.getDocumentContent()
expect(doc).toBeDefined()
expect(doc.getText()).toBe(text)
disposeAll(disposables)
fs.unlinkSync(fsPath)
}
it('should synchronize after make changes', async () => {
await assertDocument(async () => {
await nvim.call('setline', [1, 'a'])
await nvim.call('setline', [2, 'b'])
})
})
it('should synchronize after edit', async () => {
await assertDocument(async doc => {
let fsPath = URI.parse(doc.uri).fsPath
fs.writeFileSync(fsPath, '{\n}\n', 'utf8')
await nvim.command('edit')
await helper.wait(50)
await nvim.call('deletebufline', [doc.bufnr, 1])
doc = await workspace.document
let content = doc.getDocumentContent()
expect(content).toBe('}\n')
})
})
it('should synchronize after force edit', async () => {
await assertDocument(async doc => {
let fsPath = URI.parse(doc.uri).fsPath
fs.writeFileSync(fsPath, '{\n}\n', 'utf8')
await nvim.command('edit')
await helper.wait(50)
await nvim.call('deletebufline', [doc.bufnr, 1])
doc = await workspace.document
let content = doc.getDocumentContent()
expect(content).toBe('}\n')
})
})
})
describe('getEndOffset', () => {
it('should getEndOffset #1', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['', ''], { start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
let end = doc.getEndOffset(1, 1, false)
expect(end).toBe(2)
end = doc.getEndOffset(2, 1, false)
expect(end).toBe(1)
})
it('should getEndOffset #2', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['a', ''], { start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
let end = doc.getEndOffset(1, 1, false)
expect(end).toBe(2)
})
it('should getEndOffset #3', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['a'], { start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
let end = doc.getEndOffset(1, 2, false)
expect(end).toBe(1)
})
it('should getEndOffset #4', async () => {
let doc = await helper.createDocument()
await doc.buffer.setLines(['你好', ''], { start: 0, end: -1, strictIndexing: false })
await helper.wait(30)
let end = doc.getEndOffset(1, 1, false)
expect(end).toBe(3)
end = doc.getEndOffset(1, 1, true)
expect(end).toBe(4)
})
})
describe('applyEdits', () => {
it('should synchronize content added', async () => {
let doc = await helper.createDocument()
let buffer = doc.buffer
await doc.buffer.setLines(['foo f'], { start: 0, end: -1, strictIndexing: false })
await doc.synchronize()
await nvim.command('normal! gg^2l')
await nvim.input('a')
await buffer.detach()
await nvim.input('r')
await doc.applyEdits([{
range: Range.create(0, 0, 0, 5),
newText: 'foo foo'
}])
await helper.wait(100)
let line = await nvim.line
expect(line).toBe('foor foo')
})
it('should synchronize content delete', async () => {
let doc = await helper.createDocument()
let buffer = doc.buffer
await doc.buffer.setLines(['foo f'], { start: 0, end: -1, strictIndexing: false })
await doc.synchronize()
await nvim.command('normal! gg^2l')
await nvim.input('a')
await buffer.detach()
await nvim.input('<backspace>')
await doc.applyEdits([{
range: Range.create(0, 0, 0, 5),
newText: 'foo foo'
}])
await helper.wait(100)
let line = await nvim.line
expect(line).toBe('fo foo')
})
})
describe('highlights', () => {
it('should add highlights to document', async () => {
await helper.createDocument()
let buf = await nvim.buffer
await buf.setLines(['你好', 'world'], { start: 0, end: -1, strictIndexing: false })
let ranges = [
Range.create(0, 0, 0, 2),
Range.create(1, 0, 1, 3)
]
let ns = await nvim.createNamespace('coc-highlight')
nvim.pauseNotification()
buf.highlightRanges('highlight', 'Search', ranges)
await nvim.resumeNotification()
let markers = await helper.getMarkers(buf.id, ns)
expect(markers.length).toBe(2)
nvim.pauseNotification()
buf.clearNamespace('highlight')
await nvim.resumeNotification()
markers = await helper.getMarkers(buf.id, ns)
expect(markers.length).toBe(0)
})
it('should add/clear highlights of current window', async () => {
await helper.createDocument()
let buf = await nvim.buffer
await buf.setLines(['你好', 'world'], { start: 0, end: -1, strictIndexing: false })
let win = await nvim.window
let ranges = [
Range.create(0, 0, 0, 2),
Range.create(1, 0, 1, 3)
]
let res = await win.highlightRanges('Search', ranges)
expect(res.length).toBe(2)
let matches = await nvim.call('getmatches', [win.id])
expect(matches.length).toBe(2)
nvim.pauseNotification()
win.clearMatchGroup('Search')
await nvim.resumeNotification()
matches = await nvim.call('getmatches', [win.id])
expect(matches.length).toBe(0)
})
it('should clear matches by ids', async () => {
await helper.createDocument()
let buf = await nvim.buffer
await buf.setLines(['你好', 'world'], { start: 0, end: -1, strictIndexing: false })
let win = await nvim.window
let ranges = [
Range.create(0, 0, 0, 2),
Range.create(1, 0, 1, 3)
]
let ids = await win.highlightRanges('Search', ranges)
nvim.pauseNotification()
win.clearMatches(ids)
await nvim.resumeNotification()
let matches = await nvim.call('getmatches', [win.id])
expect(matches.length).toBe(0)
})
})

67
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/events.test.ts

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
import { Neovim } from '@chemzqm/neovim'
import { Disposable } from 'vscode-languageserver-protocol'
import events from '../../events'
import { disposeAll } from '../../util'
let disposables: Disposable[] = []
import helper from '../helper'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
afterEach(async () => {
disposeAll(disposables)
})
describe('register handler', () => {
it('should register single handler', async () => {
let fn = jest.fn()
let obj = {}
let disposable = events.on('BufEnter', fn, obj)
disposables.push(disposable)
await events.fire('BufEnter', ['a', 'b'])
expect(fn).toBeCalledWith('a', 'b')
})
it('should register multiple events', async () => {
let fn = jest.fn()
let disposable = events.on(['TaskExit', 'TaskStderr'], fn)
disposables.push(disposable)
await events.fire('TaskExit', [])
await events.fire('TaskStderr', [])
expect(fn).toBeCalledTimes(2)
})
it('should resolve after timeout', async () => {
let fn = (): Promise<void> => new Promise(resolve => {
setTimeout(() => {
resolve()
}, 100)
})
let disposable = events.on('FocusGained', fn, {})
disposables.push(disposable)
let ts = Date.now()
await events.fire('FocusGained', [])
expect(Date.now() - ts >= 100).toBe(true)
})
it('should emit TextInsert after TextChangedI', async () => {
let arr: string[] = []
events.on('TextInsert', () => {
arr.push('insert')
}, null, disposables)
events.on('TextChangedI', () => {
arr.push('change')
}, null, disposables)
await nvim.input('ia')
await helper.wait(300)
expect(arr).toEqual(['change', 'insert'])
})
})

221
etc/soft/nvim/+plugins/coc.nvim/src/__tests__/modules/extensions.test.ts

@ -0,0 +1,221 @@ @@ -0,0 +1,221 @@
import { Neovim } from '@chemzqm/neovim'
import fs from 'fs'
import path from 'path'
import events from '../../events'
import extensions, { API, Extension } from '../../extensions'
import helper from '../helper'
import { v1 as uuidv1 } from 'uuid'
let nvim: Neovim
beforeAll(async () => {
await helper.setup()
nvim = helper.nvim
})
afterAll(async () => {
await helper.shutdown()
})
jest.setTimeout(30000)
describe('extensions', () => {
it('should load global extensions', async () => {
let stat = extensions.getExtensionState('test')
expect(stat).toBe('activated')
})
it('should filter global extensions', async () => {
let res = extensions.filterGlobalExtensions(['test', 'foo'])
expect(res).toEqual(['foo'])
})
it('should load local extensions from &rtp', async () => {
let folder = path.resolve(__dirname, '../extensions/vim/local')
await nvim.command(`set runtimepath^=${folder}`)
await helper.wait(300)
let stat = extensions.getExtensionState('local')
expect(stat).toBe('activated')
})
it('should install/uninstall npm extension', async () => {
await extensions.installExtensions(['coc-omni'])
let folder = path.join(__dirname, '../extensions/coc-omni')
let exists = fs.existsSync(folder)
expect(exists).toBe(true)
await helper.wait(200)
await extensions.uninstallExtension(['coc-omni'])
exists = fs.existsSync(folder)
expect(exists).toBe(false)
})
it('should install/uninstall extension by url', async () => {
await extensions.installExtensions(['https://github.com/hollowtree/vscode-vue-snippets'])
let folder = path.join(__dirname, '../extensions/vue-snippets')
let exists = fs.existsSync(folder)
expect(exists).toBe(true)
await extensions.uninstallExtension(['vue-snippets'])
exists = fs.existsSync(folder)
expect(exists).toBe(false)
})
it('should install/uninstall extension by url with branch', async () => {
await extensions.installExtensions(['https://github.com/sdras/vue-vscode-snippets@main'])
let folder = path.join(__dirname, '../extensions/vue-vscode-snippets')
let exists = fs.existsSync(folder)
expect(exists).toBe(true)
await extensions.uninstallExtension(['vue-vscode-snippets'])
exists = fs.existsSync(folder)
expect(exists).toBe(false)
})
it('should parse extension info', () => {
const installer = extensions.installer
const scoped = installer('@yaegassy/coc-intelephense').info
expect(scoped.name).toBe('@yaegassy/coc-intelephense')
const scopedVer = installer('@yaegassy/coc-intelephense@0.2.1').info
expect(scopedVer.name).toBe('@yaegassy/coc-intelephense')
expect(scopedVer.version).toBe('0.2.1')
})
it('should get all extensions', () => {
let list = extensions.all
expect(Array.isArray(list)).toBe(true)
})
it('should get extensions stat', async () => {
let stats = await extensions.getExtensionStates()
expect(stats.length).toBeGreaterThan(0)
})
it('should toggle extension', async () => {
await extensions.toggleExtension('test')
let stat = extensions.getExtensionState('test')
expect(stat).toBe('disabled')
await extensions.toggleExtension('test')
stat = extensions.getExtensionState('test')
expect(stat).toBe('activated')
})
it('should reload extension', async () => {
await extensions.reloadExtension('test')
await helper.wait(100)
let stat = extensions.getExtensionState('test')
expect(stat).toBe('activated')
})
it('should has extension', () => {
let res = extensions.has('test')
expect(res).toBe(true)
})
it('should be activated', async () => {
let res = extensions.has('test')
expect(res).toBe(true)
})
it('should activate & deactivate extension', async () => {
await extensions.deactivate('test')
let stat = extensions.getExtensionState('test')
expect(stat).toBe('loaded')
await extensions.activate('test')
stat = extensions.getExtensionState('test')
expect(stat).toBe('activated')
})
it('should call extension API', async () => {
let res = await extensions.call('test', 'echo', ['5'])
expect(res).toBe('5')
let p: string = await extensions.call('test', 'asAbsolutePath', ['..'])
expect(p.endsWith('extensions')).toBe(true)
})
it('should get extension API', () => {
let res = extensions.getExtensionApi('test') as any
expect(typeof res.echo).toBe('function')
})
it('should load single file extension', async () => {
let filepath = path.join(__dirname, '../extensions/root.js')
await extensions.loadExtensionFile(filepath)
expect(extensions.has('single-root')).toBe(true)
})
})
describe('extensions active events', () => {
function createExtension(event: string): Extension<API> {
let id = uuidv1()
let isActive = false
let packageJSON = {
name: id,
activationEvents: [event]
}
let ext = {
id,
packageJSON,
exports: void 0,
extensionPath: '',
activate: async () => {
isActive = true
}
} as any
Object.defineProperty(ext, 'isActive', {
get: () => isActive
})
extensions.registerExtension(ext, () => {
isActive = false
})
return ext
}
it('should activate on language', async () => {
let ext = createExtension('onLanguage:javascript')
expect(ext.isActive).toBe(false)
await nvim.command('edit /tmp/a.js')
await helper.wait(300)
expect(ext.isActive).toBe(true)
ext = createExtension('onLanguage:javascript')
expect(ext.isActive).toBe(true)
})
it('should activate on command', async () => {
let ext = createExtension('onCommand:test.echo')
await events.fire('Command', ['test.echo'])
await helper.wait(30)
expect(ext.isActive).toBe(true)
})
it('should activate on workspace contains', async () => {
let ext = createExtension('workspaceContains:package.json')
let root = path.resolve(__dirname, '../../..')
await nvim.command(`edit ${path.join(root, 'file.js')}`)
await helper.wait(100)
expect(ext.isActive).toBe(true)
})
it('should activate on file system', async () => {
let ext = createExtension('onFileSystem:zip')
await nvim.command('edit zip:///a')
await helper.wait(30)
expect(ext.isActive).toBe(true)
ext = createExtension('onFileSystem:zip')
expect(ext.isActive).toBe(true)
})
})
describe('extension properties', () => {
it('should get extensionPath', () => {
let ext = extensions.getExtension('test')
let p = ext.extension.extensionPath
expect(p.endsWith('test')).toBe(true)
})
it('should deactivate', async () => {
let ext = extensions.getExtension('test')
await ext.deactivate()
expect(ext.extension.isActive).toBe(false)
await extensions.activate('test')
})
})

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

Loading…
Cancel
Save