mirror of
https://github.com/neovim/neovim
synced 2025-07-29 01:41:52 +00:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
a73904168a | |||
58460e2d52 | |||
3a0d37681f | |||
4cb2b19197 | |||
d1ca551983 | |||
5e0ef6afc7 | |||
cefc91a82e | |||
334d8f506f | |||
ff83c712cf | |||
b07bffdc47 | |||
b868257ef9 | |||
e304677993 | |||
4e5af2f5a6 | |||
917f496f75 | |||
a242902430 | |||
12ae7aa846 | |||
27abf5c81b | |||
d50f71d2f1 | |||
f623fad9c4 | |||
3e83a33108 | |||
0c2bf55e90 | |||
6b140ae899 | |||
1921dda92e | |||
30fa1c5f8c | |||
6417ba0c2f | |||
3c102303f5 | |||
034d3c8f6c | |||
9965cfb84c | |||
bdd8498ed7 | |||
8daffd0cbb | |||
fd8e0ae62d | |||
a56dcbbad7 | |||
6a87b57c06 | |||
5c6ee251a6 | |||
0db89468d7 | |||
dc87a0d80a | |||
c753e70abb | |||
0ed06d7271 | |||
e512c9589e | |||
47686a1454 | |||
3b3cf1d7ef | |||
472d41b5b6 | |||
216c56b7e0 | |||
968947b3c3 | |||
9b3426691c | |||
4e43264cd3 | |||
3a4d3934c4 | |||
2b2a3449f7 | |||
6b69b3217b | |||
cf73f21c07 | |||
e5e69f758d | |||
901eeeb2e6 | |||
6563c6bde7 | |||
465c181581 | |||
710d561f88 | |||
81233a41d7 | |||
c4b9bdbdf4 | |||
560c6ca947 | |||
4b6caa913c | |||
0fbc78caa1 | |||
533ec6d492 | |||
9dfb429e93 | |||
051e14d347 | |||
714622fb45 | |||
4296511087 | |||
f9c7a69cec | |||
fa292e6f61 | |||
bc66a5ff6f | |||
f25f6c8d13 | |||
ad7211ac8f | |||
95ee908c40 | |||
d68d212ad4 | |||
3273c595c0 | |||
3db39ed21f | |||
f184c562c5 | |||
32842b0ee3 | |||
4f0e828190 |
@ -141,7 +141,7 @@ endif()
|
||||
# version string, else they are combined with the result of `git describe`.
|
||||
set(NVIM_VERSION_MAJOR 0)
|
||||
set(NVIM_VERSION_MINOR 11)
|
||||
set(NVIM_VERSION_PATCH 1)
|
||||
set(NVIM_VERSION_PATCH 2)
|
||||
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
|
||||
|
||||
# API level
|
||||
|
@ -120,6 +120,8 @@ endfunction
|
||||
" Tutor Cmd: {{{1
|
||||
|
||||
function! s:Locale()
|
||||
" Make sure l:lang exists before returning.
|
||||
let l:lang = 'en_US'
|
||||
if exists('v:lang') && v:lang =~ '\a\a'
|
||||
let l:lang = v:lang
|
||||
elseif $LC_ALL =~ '\a\a'
|
||||
@ -132,8 +134,6 @@ function! s:Locale()
|
||||
endif
|
||||
elseif $LANG =~ '\a\a'
|
||||
let l:lang = $LANG
|
||||
else
|
||||
let l:lang = 'en_US'
|
||||
endif
|
||||
return split(l:lang, '_')
|
||||
endfunction
|
||||
@ -220,6 +220,7 @@ function! tutor#TutorCmd(tutor_name)
|
||||
|
||||
call tutor#SetupVim()
|
||||
exe "edit ".l:to_open
|
||||
call tutor#EnableInteractive(v:true)
|
||||
call tutor#ApplyTransform()
|
||||
endfunction
|
||||
|
||||
@ -229,6 +230,27 @@ function! tutor#TutorCmdComplete(lead,line,pos)
|
||||
return join(l:names, "\n")
|
||||
endfunction
|
||||
|
||||
" Enables/disables interactive mode.
|
||||
function! tutor#EnableInteractive(enable)
|
||||
let enable = a:enable
|
||||
if enable
|
||||
setlocal buftype=nofile
|
||||
setlocal concealcursor+=inv
|
||||
setlocal conceallevel=2
|
||||
call tutor#ApplyMarks()
|
||||
augroup tutor_interactive
|
||||
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
|
||||
augroup END
|
||||
else
|
||||
setlocal buftype<
|
||||
setlocal concealcursor<
|
||||
setlocal conceallevel<
|
||||
if exists('#tutor_interactive')
|
||||
autocmd! tutor_interactive * <buffer>
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! tutor#ApplyTransform()
|
||||
if has('win32')
|
||||
sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\2/g
|
||||
|
4
runtime/doc/builtin.txt
generated
4
runtime/doc/builtin.txt
generated
@ -3244,7 +3244,7 @@ getcharsearch() *getcharsearch()*
|
||||
Return: ~
|
||||
(`table`)
|
||||
|
||||
getcharstr([{expr}]) *getcharstr()*
|
||||
getcharstr([{expr} [, {opts}]]) *getcharstr()*
|
||||
The same as |getchar()|, except that this always returns a
|
||||
String, and "number" isn't allowed in {opts}.
|
||||
|
||||
@ -12029,7 +12029,7 @@ winlayout([{tabnr}]) *winlayout()*
|
||||
• {tabnr} (`integer?`)
|
||||
|
||||
Return: ~
|
||||
(`any[]`)
|
||||
(`vim.fn.winlayout.ret`)
|
||||
|
||||
winline() *winline()*
|
||||
The result is a Number, which is the screen line of the cursor
|
||||
|
@ -311,7 +311,7 @@ Nvim's filetype detection behavior matches Vim, but is implemented as part of
|
||||
|vim.filetype| (see `$VIMRUNTIME/lua/vim/filetype.lua`). The logic is encoded in
|
||||
three tables, listed in order of precedence (the first match is returned):
|
||||
1. `filename` for literal full path or basename lookup;
|
||||
2. `pattern` for matching filenames or paths against |lua-patterns|, optimized
|
||||
2. `pattern` for matching filenames or paths against |lua-pattern|s, optimized
|
||||
for fast lookup;
|
||||
3. `extension` for literal extension lookup.
|
||||
|
||||
|
@ -269,6 +269,26 @@ DiagnosticVirtualTextHint
|
||||
DiagnosticVirtualTextOk
|
||||
Used for "Ok" diagnostic virtual text.
|
||||
|
||||
*hl-DiagnosticVirtualLinesError*
|
||||
DiagnosticVirtualLinesError
|
||||
Used for "Error" diagnostic virtual lines.
|
||||
|
||||
*hl-DiagnosticVirtualLinesWarn*
|
||||
DiagnosticVirtualLinesWarn
|
||||
Used for "Warn" diagnostic virtual lines.
|
||||
|
||||
*hl-DiagnosticVirtualLinesInfo*
|
||||
DiagnosticVirtualLinesInfo
|
||||
Used for "Info" diagnostic virtual lines.
|
||||
|
||||
*hl-DiagnosticVirtualLinesHint*
|
||||
DiagnosticVirtualLinesHint
|
||||
Used for "Hint" diagnostic virtual lines.
|
||||
|
||||
*hl-DiagnosticVirtualLinesOk*
|
||||
DiagnosticVirtualLinesOk
|
||||
Used for "Ok" diagnostic virtual lines.
|
||||
|
||||
*hl-DiagnosticUnderlineError*
|
||||
DiagnosticUnderlineError
|
||||
Used to underline "Error" diagnostics.
|
||||
|
@ -714,11 +714,14 @@ list of the current window.
|
||||
omitted the current entry is used.
|
||||
Also see |++opt| and |+cmd|.
|
||||
|
||||
:[count]n[ext] [++opt] [+cmd] *:n* *:ne* *:next* *]a* *E165* *E163*
|
||||
:[count]n[ext] [++opt] [+cmd] *:n* *:ne* *:next* *E165* *E163*
|
||||
Edit [count] next file. This fails when changes have
|
||||
been made and Vim does not want to |abandon| the
|
||||
current buffer. Also see |++opt| and |+cmd|.
|
||||
|
||||
*]a*
|
||||
]a Mapped to |:next|. |default-mappings|
|
||||
|
||||
:[count]n[ext]! [++opt] [+cmd]
|
||||
Edit [count] next file, discard any changes to the
|
||||
buffer. Also see |++opt| and |+cmd|.
|
||||
@ -740,16 +743,22 @@ list of the current window.
|
||||
any changes to the buffer. Also see |++opt| and
|
||||
|+cmd|.
|
||||
|
||||
:[count]prev[ious] [count] [++opt] [+cmd] *:prev* *:previous* *[a*
|
||||
:[count]prev[ious] [count] [++opt] [+cmd] *:prev* *:previous*
|
||||
Same as :Next. Also see |++opt| and |+cmd|.
|
||||
|
||||
*:rew* *:rewind* *[A*
|
||||
*[a*
|
||||
[a Mapped to |:previous|. |default-mappings|
|
||||
|
||||
*:rew* *:rewind*
|
||||
:rew[ind] [++opt] [+cmd]
|
||||
Start editing the first file in the argument list.
|
||||
This fails when changes have been made and Vim does
|
||||
not want to |abandon| the current buffer.
|
||||
Also see |++opt| and |+cmd|.
|
||||
|
||||
*[A*
|
||||
[A Mapped to |:rewind|. |default-mappings|
|
||||
|
||||
:rew[ind]! [++opt] [+cmd]
|
||||
Start editing the first file in the argument list.
|
||||
Discard any changes to the buffer. Also see |++opt|
|
||||
@ -759,13 +768,16 @@ list of the current window.
|
||||
:fir[st][!] [++opt] [+cmd]
|
||||
Other name for ":rewind".
|
||||
|
||||
*:la* *:last* *]A*
|
||||
*:la* *:last*
|
||||
:la[st] [++opt] [+cmd]
|
||||
Start editing the last file in the argument list.
|
||||
This fails when changes have been made and Vim does
|
||||
not want to |abandon| the current buffer.
|
||||
Also see |++opt| and |+cmd|.
|
||||
|
||||
*]A*
|
||||
]A Mapped to |:last|. |default-mappings|
|
||||
|
||||
:la[st]! [++opt] [+cmd]
|
||||
Start editing the last file in the argument list.
|
||||
Discard any changes to the buffer. Also see |++opt|
|
||||
|
@ -9,18 +9,18 @@
|
||||
==============================================================================
|
||||
Checkhealth *vim.health* *health*
|
||||
|
||||
|
||||
vim.health is a minimal framework to help users troubleshoot configuration and
|
||||
any other environment conditions that a plugin might care about. Nvim ships
|
||||
with healthchecks for configuration, performance, python support, ruby
|
||||
support, clipboard support, and more.
|
||||
|
||||
To run all healthchecks, use: >vim
|
||||
|
||||
:checkhealth
|
||||
:checkhealth
|
||||
<
|
||||
|
||||
Plugin authors are encouraged to write new healthchecks. |health-dev|
|
||||
|
||||
|
||||
COMMANDS *health-commands*
|
||||
|
||||
*:che* *:checkhealth*
|
||||
@ -56,7 +56,6 @@ Local mappings in the healthcheck buffer:
|
||||
q Closes the window.
|
||||
|
||||
Global configuration:
|
||||
|
||||
*g:health*
|
||||
g:health Dictionary with the following optional keys:
|
||||
- `style` (`'float'|nil`) Set to "float" to display :checkhealth in
|
||||
@ -65,16 +64,26 @@ g:health Dictionary with the following optional keys:
|
||||
Example: >lua
|
||||
vim.g.health = { style = 'float' }
|
||||
|
||||
|
||||
Local configuration:
|
||||
|
||||
Checkhealth sets its buffer filetype to "checkhealth". You can customize the
|
||||
buffer by handling the |FileType| event. For example if you don't want emojis
|
||||
in the health report: >vim
|
||||
autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
|
||||
<
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Create a healthcheck *health-dev*
|
||||
|
||||
Healthchecks are functions that check the user environment, configuration, or
|
||||
any other prerequisites that a plugin cares about. Nvim ships with
|
||||
healthchecks in:
|
||||
- $VIMRUNTIME/autoload/health/
|
||||
- $VIMRUNTIME/lua/vim/lsp/health.lua
|
||||
- $VIMRUNTIME/lua/vim/treesitter/health.lua
|
||||
- and more...
|
||||
• $VIMRUNTIME/autoload/health/
|
||||
• $VIMRUNTIME/lua/vim/lsp/health.lua
|
||||
• $VIMRUNTIME/lua/vim/treesitter/health.lua
|
||||
• and more...
|
||||
|
||||
To add a new healthcheck for your own plugin, simply create a "health.lua"
|
||||
module on 'runtimepath' that returns a table with a "check()" function. Then
|
||||
@ -82,35 +91,35 @@ module on 'runtimepath' that returns a table with a "check()" function. Then
|
||||
|
||||
For example if your plugin is named "foo", define your healthcheck module at
|
||||
one of these locations (on 'runtimepath'):
|
||||
- lua/foo/health/init.lua
|
||||
- lua/foo/health.lua
|
||||
• lua/foo/health/init.lua
|
||||
• lua/foo/health.lua
|
||||
|
||||
If your plugin also provides a submodule named "bar" for which you want
|
||||
a separate healthcheck, define the healthcheck at one of these locations:
|
||||
- lua/foo/bar/health/init.lua
|
||||
- lua/foo/bar/health.lua
|
||||
If your plugin also provides a submodule named "bar" for which you want a
|
||||
separate healthcheck, define the healthcheck at one of these locations:
|
||||
• lua/foo/bar/health/init.lua
|
||||
• lua/foo/bar/health.lua
|
||||
|
||||
All such health modules must return a Lua table containing a `check()`
|
||||
function.
|
||||
|
||||
Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path
|
||||
with your plugin name: >lua
|
||||
local M = {}
|
||||
|
||||
local M = {}
|
||||
M.check = function()
|
||||
vim.health.start("foo report")
|
||||
-- make sure setup function parameters are ok
|
||||
if check_setup() then
|
||||
vim.health.ok("Setup is correct")
|
||||
else
|
||||
vim.health.error("Setup is incorrect")
|
||||
end
|
||||
-- do some more checking
|
||||
-- ...
|
||||
end
|
||||
|
||||
M.check = function()
|
||||
vim.health.start("foo report")
|
||||
-- make sure setup function parameters are ok
|
||||
if check_setup() then
|
||||
vim.health.ok("Setup is correct")
|
||||
else
|
||||
vim.health.error("Setup is incorrect")
|
||||
end
|
||||
-- do some more checking
|
||||
-- ...
|
||||
end
|
||||
|
||||
return M
|
||||
return M
|
||||
<
|
||||
|
||||
|
||||
error({msg}, {...}) *vim.health.error()*
|
||||
|
@ -1923,7 +1923,7 @@ These commands are used to start inserting text. You can end insert mode with
|
||||
<Esc>. See |mode-ins-repl| for the other special characters in Insert mode.
|
||||
The effect of [count] takes place after Insert mode is exited.
|
||||
|
||||
The following commands insert text, but stay in normal mode:
|
||||
The following |default-mappings| insert text, but stay in normal mode:
|
||||
|
||||
*]<Space>*
|
||||
]<Space> Insert an empty line below the cursor without leaving
|
||||
|
@ -41,7 +41,8 @@ Follow these steps to get LSP features:
|
||||
-- current buffer that contains either a ".luarc.json" or a
|
||||
-- ".luarc.jsonc" file. Files that share a root directory will reuse
|
||||
-- the connection to the same LSP server.
|
||||
root_markers = { '.luarc.json', '.luarc.jsonc' },
|
||||
-- Nested lists indicate equal priority, see |vim.lsp.Config|.
|
||||
root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
|
||||
|
||||
-- Specific settings to send to the server. The schema for this is
|
||||
-- defined by the server. For example the schema for lua-language-server
|
||||
@ -286,38 +287,33 @@ They are also listed below.
|
||||
|
||||
- `'callHierarchy/incomingCalls'`
|
||||
- `'callHierarchy/outgoingCalls'`
|
||||
- `'textDocument/codeAction'`
|
||||
- `'client/registerCapability'`
|
||||
- `'client/unregisterCapability'`
|
||||
- `'signature_help'`
|
||||
- `'textDocument/codeLens'`
|
||||
- `'textDocument/completion'`
|
||||
- `'textDocument/declaration'`
|
||||
- `'textDocument/definition'`
|
||||
- `'textDocument/diagnostic'`
|
||||
- `'textDocument/documentHighlight'`
|
||||
- `'textDocument/documentSymbol'`
|
||||
- `'textDocument/foldingRange'`
|
||||
- `'textDocument/formatting'`
|
||||
- `'textDocument/hover'`
|
||||
- `'textDocument/implementation'`
|
||||
- `'textDocument/inlayHint'`
|
||||
- `'textDocument/prepareTypeHierarchy'`
|
||||
- `'textDocument/publishDiagnostics'`
|
||||
- `'textDocument/rangeFormatting'`
|
||||
- `'textDocument/rangesFormatting'`
|
||||
- `'textDocument/references'`
|
||||
- `'textDocument/rename'`
|
||||
- `'textDocument/semanticTokens/full'`
|
||||
- `'textDocument/semanticTokens/full/delta'`
|
||||
- `'textDocument/signatureHelp'`
|
||||
- `'textDocument/typeDefinition*'`
|
||||
- `'typeHierarchy/subtypes'`
|
||||
- `'typeHierarchy/supertypes'`
|
||||
- `'window/logMessage'`
|
||||
- `'window/showMessage'`
|
||||
- `'window/showDocument'`
|
||||
- `'window/showMessage'`
|
||||
- `'window/showMessageRequest'`
|
||||
- `'window/workDoneProgress/create'`
|
||||
- `'workspace/applyEdit'`
|
||||
- `'workspace/configuration'`
|
||||
- `'workspace/executeCommand'`
|
||||
- `'workspace/inlayHint/refresh'`
|
||||
- `'workspace/semanticTokens/refresh'`
|
||||
- `'workspace/symbol'`
|
||||
- `'workspace/workspaceFolders'`
|
||||
|
||||
@ -552,10 +548,19 @@ LspAttach *LspAttach*
|
||||
|autocmd-pattern| is the buffer name. The client ID is passed in the
|
||||
Lua handler |event-data| argument.
|
||||
|
||||
Example: >lua
|
||||
vim.api.nvim_create_autocmd('LspAttach', {
|
||||
callback = function(ev)
|
||||
local client = vim.lsp.get_client_by_id(ev.data.client_id)
|
||||
-- ...
|
||||
end
|
||||
})
|
||||
<
|
||||
Note: If the LSP server performs dynamic registration, capabilities may be
|
||||
registered any time _after_ LspAttach. In that case you may want to handle
|
||||
the "registerCapability" event. Example: >lua
|
||||
the "registerCapability" event.
|
||||
|
||||
Example: >lua
|
||||
vim.lsp.handlers['client/registerCapability'] = (function(overridden)
|
||||
return function(err, res, ctx)
|
||||
local result = overridden(err, res, ctx)
|
||||
@ -563,8 +568,10 @@ LspAttach *LspAttach*
|
||||
if not client then
|
||||
return
|
||||
end
|
||||
-- Call your custom on_attach logic...
|
||||
-- my_on_attach(client, vim.api.nvim_get_current_buf())
|
||||
for bufnr, _ in pairs(client.attached_buffers) do
|
||||
-- Call your custom on_attach logic...
|
||||
-- my_on_attach(client, bufnr)
|
||||
end
|
||||
return result
|
||||
end
|
||||
end)(vim.lsp.handlers['client/registerCapability'])
|
||||
@ -572,8 +579,9 @@ LspAttach *LspAttach*
|
||||
LspDetach *LspDetach*
|
||||
Just before an LSP client detaches from a buffer. The |autocmd-pattern| is
|
||||
the buffer name. The client ID is passed in the Lua handler |event-data|
|
||||
argument. Example: >lua
|
||||
argument.
|
||||
|
||||
Example: >lua
|
||||
vim.api.nvim_create_autocmd('LspDetach', {
|
||||
callback = function(args)
|
||||
-- Get the detaching client
|
||||
@ -595,8 +603,9 @@ LspNotify *LspNotify*
|
||||
LSP server.
|
||||
|
||||
The client_id, LSP method, and parameters are sent in the Lua handler
|
||||
|event-data| table argument. Example: >lua
|
||||
|event-data| table argument.
|
||||
|
||||
Example: >lua
|
||||
vim.api.nvim_create_autocmd('LspNotify', {
|
||||
callback = function(args)
|
||||
local bufnr = args.buf
|
||||
@ -642,8 +651,9 @@ LspRequest *LspRequest*
|
||||
The Lua handler |event-data| argument has the client ID, request ID, and
|
||||
request (described at |vim.lsp.Client|, {requests} field). If the request
|
||||
type is `complete`, the request will be deleted from the client's pending
|
||||
requests table after processing the event handlers. Example: >lua
|
||||
requests table after processing the event handlers.
|
||||
|
||||
Example: >lua
|
||||
vim.api.nvim_create_autocmd('LspRequest', {
|
||||
callback = function(args)
|
||||
local bufnr = args.buf
|
||||
@ -670,8 +680,9 @@ LspTokenUpdate *LspTokenUpdate*
|
||||
when an existing token becomes visible for the first time. The
|
||||
|autocmd-pattern| is the buffer name. The Lua handler |event-data|
|
||||
argument has the client ID and token (see
|
||||
|vim.lsp.semantic_tokens.get_at_pos()|). Example: >lua
|
||||
|vim.lsp.semantic_tokens.get_at_pos()|).
|
||||
|
||||
Example: >lua
|
||||
vim.api.nvim_create_autocmd('LspTokenUpdate', {
|
||||
callback = function(args)
|
||||
local token = args.data.token
|
||||
@ -697,25 +708,53 @@ Lua module: vim.lsp *lsp-core*
|
||||
• {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
|
||||
See `cmd` in |vim.lsp.ClientConfig|.
|
||||
• {filetypes}? (`string[]`) Filetypes the client will attach to, if
|
||||
activated by `vim.lsp.enable()`. If not provided,
|
||||
then the client will attach to all filetypes.
|
||||
• {root_markers}? (`string[]`) Directory markers (.e.g. '.git/') where
|
||||
the LSP server will base its workspaceFolders,
|
||||
rootUri, and rootPath on initialization. Unused if
|
||||
`root_dir` is provided.
|
||||
• {root_dir}? (`string|fun(bufnr: integer, cb:fun(root_dir?:string))`)
|
||||
Directory where the LSP server will base its
|
||||
workspaceFolders, rootUri, and rootPath on
|
||||
initialization. If a function, it is passed the
|
||||
buffer number and a callback argument which must be
|
||||
called with the value of root_dir to use. The LSP
|
||||
server will not be started until the callback is
|
||||
called.
|
||||
activated by `vim.lsp.enable()`. If not provided, the
|
||||
client will attach to all filetypes.
|
||||
• {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`)
|
||||
Predicate used to decide if a client should be
|
||||
Predicate which decides if a client should be
|
||||
re-used. Used on all running clients. The default
|
||||
implementation re-uses a client if name and root_dir
|
||||
matches.
|
||||
• {root_dir}? (`string|fun(bufnr: integer, on_dir:fun(root_dir?:string))`)
|
||||
*lsp-root_dir()* Directory where the LSP server will
|
||||
base its workspaceFolders, rootUri, and rootPath on
|
||||
initialization. The function form receives a buffer
|
||||
number and `on_dir` callback which it must call to
|
||||
provide root_dir, or LSP will not be activated for
|
||||
the buffer. Thus a `root_dir()` function can
|
||||
dynamically decide per-buffer whether to activate (or
|
||||
skip) LSP. See example at |vim.lsp.enable()|.
|
||||
• {root_markers}? (`(string|string[])[]`) Directory markers (.e.g.
|
||||
'.git/') where the LSP server will base its
|
||||
workspaceFolders, rootUri, and rootPath on
|
||||
initialization. Unused if `root_dir` is provided.
|
||||
|
||||
The list order decides the priority. To indicate
|
||||
"equal priority", specify names in a nested list
|
||||
(`{ { 'a', 'b' }, ... }`) Each entry in this list is
|
||||
a set of one or more markers. For each set, Nvim will
|
||||
search upwards for each marker contained in the set.
|
||||
If a marker is found, the directory which contains
|
||||
that marker is used as the root directory. If no
|
||||
markers from the set are found, the process is
|
||||
repeated with the next set in the list.
|
||||
|
||||
Example: >lua
|
||||
root_markers = { 'stylua.toml', '.git' }
|
||||
<
|
||||
|
||||
Find the first parent directory containing the file
|
||||
`stylua.toml`. If not found, find the first parent
|
||||
directory containing the file or directory `.git`.
|
||||
|
||||
Example: >lua
|
||||
root_markers = { { 'stylua.toml', '.luarc.json' }, '.git' }
|
||||
<
|
||||
|
||||
Find the first parent directory containing EITHER
|
||||
`stylua.toml` or `.luarc.json`. If not found, find
|
||||
the first parent directory containing the file or
|
||||
directory `.git`.
|
||||
|
||||
|
||||
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
|
||||
@ -871,14 +910,24 @@ config({name}, {cfg}) *vim.lsp.config()*
|
||||
• {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|.
|
||||
|
||||
enable({name}, {enable}) *vim.lsp.enable()*
|
||||
Enable an LSP server to automatically start when opening a buffer.
|
||||
|
||||
Uses configuration defined with `vim.lsp.config`.
|
||||
Auto-starts LSP when a buffer is opened, based on the |lsp-config|
|
||||
`filetypes`, `root_markers`, and `root_dir` fields.
|
||||
|
||||
Examples: >lua
|
||||
vim.lsp.enable('clangd')
|
||||
vim.lsp.enable('clangd')
|
||||
vim.lsp.enable({'luals', 'pyright'})
|
||||
<
|
||||
|
||||
vim.lsp.enable({'luals', 'pyright'})
|
||||
Example: To dynamically decide whether LSP is activated, define a
|
||||
|lsp-root_dir()| function which calls `on_dir()` only when you want that
|
||||
config to activate: >lua
|
||||
vim.lsp.config('lua_ls', {
|
||||
root_dir = function(bufnr, on_dir)
|
||||
if not vim.fn.bufname(bufnr):match('%.txt$') then
|
||||
on_dir(vim.fn.getcwd())
|
||||
end
|
||||
end
|
||||
})
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
@ -993,6 +1042,15 @@ get_log_path() *vim.lsp.get_log_path()*
|
||||
Return: ~
|
||||
(`string`) path to log file
|
||||
|
||||
is_enabled({name}) *vim.lsp.is_enabled()*
|
||||
Checks if the given LSP config is enabled (globally, not per-buffer).
|
||||
|
||||
Parameters: ~
|
||||
• {name} (`string`) Config name
|
||||
|
||||
Return: ~
|
||||
(`boolean`)
|
||||
|
||||
omnifunc({findstart}, {base}) *vim.lsp.omnifunc()*
|
||||
Implements 'omnifunc' compatible LSP completion.
|
||||
|
||||
@ -1128,65 +1186,17 @@ Lua module: vim.lsp.client *lsp-client*
|
||||
*vim.lsp.Client*
|
||||
|
||||
Fields: ~
|
||||
• {id} (`integer`) The id allocated to the client.
|
||||
• {name} (`string`) If a name is specified on creation,
|
||||
that will be used. Otherwise it is just the
|
||||
client id. This is used for logs and messages.
|
||||
• {rpc} (`vim.lsp.rpc.PublicClient`) RPC client
|
||||
object, for low level interaction with the
|
||||
client. See |vim.lsp.rpc.start()|.
|
||||
• {offset_encoding} (`string`) Called "position encoding" in LSP
|
||||
spec, the encoding used for communicating with
|
||||
the server. You can modify this in the
|
||||
`config`'s `on_init` method before text is
|
||||
sent to the server.
|
||||
• {handlers} (`table<string,lsp.Handler>`) The handlers
|
||||
used by the client as described in
|
||||
|lsp-handler|.
|
||||
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
|
||||
The current pending requests in flight to the
|
||||
server. Entries are key-value pairs with the
|
||||
key being the request id while the value is a
|
||||
table with `type`, `bufnr`, and `method`
|
||||
key-value pairs. `type` is either "pending"
|
||||
for an active request, or "cancel" for a
|
||||
cancel request. It will be "complete"
|
||||
ephemerally while executing |LspRequest|
|
||||
autocmds when replies are received from the
|
||||
server.
|
||||
• {config} (`vim.lsp.ClientConfig`) copy of the table
|
||||
that was passed by the user to
|
||||
|vim.lsp.start()|. See |vim.lsp.ClientConfig|.
|
||||
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
|
||||
server sent on `initialize` describing the
|
||||
server's capabilities.
|
||||
• {server_info} (`lsp.ServerInfo?`) Response from the server
|
||||
sent on `initialize` describing information
|
||||
about the server.
|
||||
• {progress} (`vim.lsp.Client.Progress`) A ring buffer
|
||||
(|vim.ringbuf()|) containing progress messages
|
||||
sent by the server. See
|
||||
|vim.lsp.Client.Progress|.
|
||||
• {initialized} (`true?`)
|
||||
• {workspace_folders} (`lsp.WorkspaceFolder[]?`) The workspace
|
||||
folders configured in the client when the
|
||||
server starts. This property is only available
|
||||
if the client supports workspace folders. It
|
||||
can be `null` if the client supports workspace
|
||||
folders but none are configured.
|
||||
• {root_dir} (`string?`)
|
||||
• {attached_buffers} (`table<integer,true>`)
|
||||
• {capabilities} (`lsp.ClientCapabilities`) Capabilities
|
||||
provided by the client (editor or tool), at
|
||||
startup.
|
||||
• {commands} (`table<string,fun(command: lsp.Command, ctx: table)>`)
|
||||
Table of command name to function which is
|
||||
called if any LSP action (code action, code
|
||||
lenses, ...) triggers the command. Client
|
||||
commands take precedence over the global
|
||||
command registry.
|
||||
• {settings} (`lsp.LSPObject`) Map with language server
|
||||
specific settings. These are returned to the
|
||||
language server if requested via
|
||||
`workspace/configuration`. Keys are
|
||||
case-sensitive.
|
||||
Client commands. See |vim.lsp.ClientConfig|.
|
||||
• {config} (`vim.lsp.ClientConfig`) Copy of the config
|
||||
passed to |vim.lsp.start()|. See
|
||||
|vim.lsp.ClientConfig|.
|
||||
• {dynamic_capabilities} (`lsp.DynamicCapabilities`) Capabilities
|
||||
provided at runtime (after startup).
|
||||
• {flags} (`table`) A table with flags for the client.
|
||||
The current (experimental) flags are:
|
||||
• {allow_incremental_sync}? (`boolean`,
|
||||
@ -1203,9 +1213,41 @@ Lua module: vim.lsp.client *lsp-client*
|
||||
false, nvim exits immediately after sending
|
||||
the "shutdown" request to the server.
|
||||
• {get_language_id} (`fun(bufnr: integer, filetype: string): string`)
|
||||
• {capabilities} (`lsp.ClientCapabilities`) The capabilities
|
||||
provided by the client (editor or tool)
|
||||
• {dynamic_capabilities} (`lsp.DynamicCapabilities`)
|
||||
See |vim.lsp.ClientConfig|.
|
||||
• {handlers} (`table<string,lsp.Handler>`) See
|
||||
|vim.lsp.ClientConfig|.
|
||||
• {id} (`integer`) The id allocated to the client.
|
||||
• {initialized} (`true?`)
|
||||
• {name} (`string`) See |vim.lsp.ClientConfig|.
|
||||
• {offset_encoding} (`string`) See |vim.lsp.ClientConfig|.
|
||||
• {progress} (`vim.lsp.Client.Progress`) A ring buffer
|
||||
(|vim.ringbuf()|) containing progress messages
|
||||
sent by the server. See
|
||||
|vim.lsp.Client.Progress|.
|
||||
• {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
|
||||
The current pending requests in flight to the
|
||||
server. Entries are key-value pairs with the
|
||||
key being the request id while the value is a
|
||||
table with `type`, `bufnr`, and `method`
|
||||
key-value pairs. `type` is either "pending"
|
||||
for an active request, or "cancel" for a
|
||||
cancel request. It will be "complete"
|
||||
ephemerally while executing |LspRequest|
|
||||
autocmds when replies are received from the
|
||||
server.
|
||||
• {root_dir} (`string?`) See |vim.lsp.ClientConfig|.
|
||||
• {rpc} (`vim.lsp.rpc.PublicClient`) RPC client
|
||||
object, for low level interaction with the
|
||||
client. See |vim.lsp.rpc.start()|.
|
||||
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
|
||||
server sent on `initialize` describing the
|
||||
server's capabilities.
|
||||
• {server_info} (`lsp.ServerInfo?`) Response from the server
|
||||
sent on `initialize` describing server
|
||||
information (e.g. version).
|
||||
• {settings} (`lsp.LSPObject`) See |vim.lsp.ClientConfig|.
|
||||
• {workspace_folders} (`lsp.WorkspaceFolder[]?`) See
|
||||
|vim.lsp.ClientConfig|.
|
||||
• {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`)
|
||||
See |Client:request()|.
|
||||
• {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`)
|
||||
@ -1235,6 +1277,23 @@ Lua module: vim.lsp.client *lsp-client*
|
||||
*vim.lsp.ClientConfig*
|
||||
|
||||
Fields: ~
|
||||
• {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`)
|
||||
Callback invoked before the LSP "initialize"
|
||||
phase, where `params` contains the parameters
|
||||
being sent to the server and `config` is the
|
||||
config that was passed to |vim.lsp.start()|.
|
||||
You can use this to modify parameters before
|
||||
they are sent.
|
||||
• {capabilities}? (`lsp.ClientCapabilities`) Map overriding the
|
||||
default capabilities defined by
|
||||
|vim.lsp.protocol.make_client_capabilities()|,
|
||||
passed to the language server on
|
||||
initialization. Hint: use
|
||||
make_client_capabilities() and modify its
|
||||
result.
|
||||
• Note: To send an empty dictionary use
|
||||
|vim.empty_dict()|, else it will be encoded
|
||||
as an array.
|
||||
• {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
|
||||
command string[] that launches the language
|
||||
server (treated as in |jobstart()|, must be
|
||||
@ -1250,97 +1309,24 @@ Lua module: vim.lsp.client *lsp-client*
|
||||
|vim.lsp.rpc.connect()|
|
||||
• {cmd_cwd}? (`string`, default: cwd) Directory to launch
|
||||
the `cmd` process. Not related to `root_dir`.
|
||||
• {cmd_env}? (`table`) Environment flags to pass to the LSP
|
||||
on spawn. Must be specified using a table.
|
||||
Non-string values are coerced to string.
|
||||
Example: >lua
|
||||
{ PORT = 8080; HOST = "0.0.0.0"; }
|
||||
• {cmd_env}? (`table`) Environment variables passed to the
|
||||
LSP process on spawn. Non-string values are
|
||||
coerced to string. Example: >lua
|
||||
{ PORT = 8080; HOST = '0.0.0.0'; }
|
||||
<
|
||||
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
|
||||
Client commands. Map of command names to
|
||||
user-defined functions. Commands passed to
|
||||
`start()` take precedence over the global
|
||||
command registry. Each key must be a unique
|
||||
command name, and the value is a function which
|
||||
is called if any LSP action (code action, code
|
||||
lenses, …) triggers the command.
|
||||
• {detached}? (`boolean`, default: true) Daemonize the server
|
||||
process so that it runs in a separate process
|
||||
group from Nvim. Nvim will shutdown the process
|
||||
on exit, but if Nvim fails to exit cleanly this
|
||||
could leave behind orphaned server processes.
|
||||
• {workspace_folders}? (`lsp.WorkspaceFolder[]`) List of workspace
|
||||
folders passed to the language server. For
|
||||
backwards compatibility rootUri and rootPath
|
||||
will be derived from the first workspace folder
|
||||
in this list. See `workspaceFolders` in the LSP
|
||||
spec.
|
||||
• {workspace_required}? (`boolean`) (default false) Server requires a
|
||||
workspace (no "single file" support).
|
||||
• {capabilities}? (`lsp.ClientCapabilities`) Map overriding the
|
||||
default capabilities defined by
|
||||
|vim.lsp.protocol.make_client_capabilities()|,
|
||||
passed to the language server on
|
||||
initialization. Hint: use
|
||||
make_client_capabilities() and modify its
|
||||
result.
|
||||
• Note: To send an empty dictionary use
|
||||
|vim.empty_dict()|, else it will be encoded
|
||||
as an array.
|
||||
• {handlers}? (`table<string,function>`) Map of language
|
||||
server method names to |lsp-handler|
|
||||
• {settings}? (`lsp.LSPObject`) Map with language server
|
||||
specific settings. See the {settings} in
|
||||
|vim.lsp.Client|.
|
||||
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
|
||||
Table that maps string of clientside commands
|
||||
to user-defined functions. Commands passed to
|
||||
`start()` take precedence over the global
|
||||
command registry. Each key must be a unique
|
||||
command name, and the value is a function which
|
||||
is called if any LSP action (code action, code
|
||||
lenses, ...) triggers the command.
|
||||
• {init_options}? (`lsp.LSPObject`) Values to pass in the
|
||||
initialization request as
|
||||
`initializationOptions`. See `initialize` in
|
||||
the LSP spec.
|
||||
• {name}? (`string`, default: client-id) Name in log
|
||||
messages.
|
||||
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
|
||||
Language ID as string. Defaults to the buffer
|
||||
filetype.
|
||||
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position
|
||||
encoding" in LSP spec, the encoding that the
|
||||
LSP server expects. Client does not verify this
|
||||
is correct.
|
||||
• {on_error}? (`fun(code: integer, err: string)`) Callback
|
||||
invoked when the client operation throws an
|
||||
error. `code` is a number describing the error.
|
||||
Other arguments may be passed depending on the
|
||||
error kind. See `vim.lsp.rpc.client_errors` for
|
||||
possible errors. Use
|
||||
`vim.lsp.rpc.client_errors[code]` to get
|
||||
human-friendly name.
|
||||
• {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`)
|
||||
Callback invoked before the LSP "initialize"
|
||||
phase, where `params` contains the parameters
|
||||
being sent to the server and `config` is the
|
||||
config that was passed to |vim.lsp.start()|.
|
||||
You can use this to modify parameters before
|
||||
they are sent.
|
||||
• {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>`)
|
||||
Callback invoked after LSP "initialize", where
|
||||
`result` is a table of `capabilities` and
|
||||
anything else the server may send. For example,
|
||||
clangd sends `init_result.offsetEncoding` if
|
||||
`capabilities.offsetEncoding` was sent to it.
|
||||
You can only modify the
|
||||
`client.offset_encoding` here before any
|
||||
notifications are sent.
|
||||
• {on_exit}? (`elem_or_list<fun(code: integer, signal: integer, client_id: integer)>`)
|
||||
Callback invoked on client exit.
|
||||
• code: exit code of the process
|
||||
• signal: number describing the signal used to
|
||||
terminate (if any)
|
||||
• client_id: client handle
|
||||
• {on_attach}? (`elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>`)
|
||||
Callback invoked when client attaches to a
|
||||
buffer.
|
||||
• {trace}? (`'off'|'messages'|'verbose'`, default: "off")
|
||||
Passed directly to the language server in the
|
||||
initialize request. Invalid/empty values will
|
||||
• {flags}? (`table`) A table with flags for the client.
|
||||
The current (experimental) flags are:
|
||||
• {allow_incremental_sync}? (`boolean`,
|
||||
@ -1356,9 +1342,72 @@ Lua module: vim.lsp.client *lsp-client*
|
||||
request before sending kill -15. If set to
|
||||
false, nvim exits immediately after sending
|
||||
the "shutdown" request to the server.
|
||||
• {get_language_id}? (`fun(bufnr: integer, filetype: string): string`)
|
||||
Language ID as string. Defaults to the buffer
|
||||
filetype.
|
||||
• {handlers}? (`table<string,function>`) Map of LSP method
|
||||
names to |lsp-handler|s.
|
||||
• {init_options}? (`lsp.LSPObject`) Values to pass in the
|
||||
initialization request as
|
||||
`initializationOptions`. See `initialize` in
|
||||
the LSP spec.
|
||||
• {name}? (`string`) (default: client-id) Name in logs
|
||||
and user messages.
|
||||
• {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position
|
||||
encoding" in LSP spec. The encoding that the
|
||||
LSP server expects, used for communication. Not
|
||||
validated. Can be modified in `on_init` before
|
||||
text is sent to the server.
|
||||
• {on_attach}? (`elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>`)
|
||||
Callback invoked when client attaches to a
|
||||
buffer.
|
||||
• {on_error}? (`fun(code: integer, err: string)`) Callback
|
||||
invoked when the client operation throws an
|
||||
error. `code` is a number describing the error.
|
||||
Other arguments may be passed depending on the
|
||||
error kind. See `vim.lsp.rpc.client_errors` for
|
||||
possible errors. Use
|
||||
`vim.lsp.rpc.client_errors[code]` to get
|
||||
human-friendly name.
|
||||
• {on_exit}? (`elem_or_list<fun(code: integer, signal: integer, client_id: integer)>`)
|
||||
Callback invoked on client exit.
|
||||
• code: exit code of the process
|
||||
• signal: number describing the signal used to
|
||||
terminate (if any)
|
||||
• client_id: client handle
|
||||
• {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>`)
|
||||
Callback invoked after LSP "initialize", where
|
||||
`result` is a table of `capabilities` and
|
||||
anything else the server may send. For example,
|
||||
clangd sends `init_result.offsetEncoding` if
|
||||
`capabilities.offsetEncoding` was sent to it.
|
||||
You can only modify the
|
||||
`client.offset_encoding` here before any
|
||||
notifications are sent.
|
||||
• {root_dir}? (`string`) Directory where the LSP server will
|
||||
base its workspaceFolders, rootUri, and
|
||||
rootPath on initialization.
|
||||
• {settings}? (`lsp.LSPObject`) Map of language
|
||||
server-specific settings, decided by the
|
||||
client. Sent to the LS if requested via
|
||||
`workspace/configuration`. Keys are
|
||||
case-sensitive.
|
||||
• {trace}? (`'off'|'messages'|'verbose'`, default: "off")
|
||||
Passed directly to the language server in the
|
||||
initialize request. Invalid/empty values will
|
||||
• {workspace_folders}? (`lsp.WorkspaceFolder[]`) List of workspace
|
||||
folders passed to the language server. For
|
||||
backwards compatibility rootUri and rootPath
|
||||
are derived from the first workspace folder in
|
||||
this list. Can be `null` if the client supports
|
||||
workspace folders but none are configured. See
|
||||
`workspaceFolders` in LSP spec.
|
||||
• {workspace_required}? (`boolean`) (default false) Server requires a
|
||||
workspace (no "single file" support). Note:
|
||||
Without a workspace, cross-file features
|
||||
(navigation, hover) may or may not work
|
||||
depending on the language server, even if the
|
||||
server doesn't require a workspace.
|
||||
|
||||
|
||||
Client:cancel_request({id}) *Client:cancel_request()*
|
||||
@ -1544,7 +1593,8 @@ clear_references() *vim.lsp.buf.clear_references()*
|
||||
Removes document highlights from current buffer.
|
||||
|
||||
code_action({opts}) *vim.lsp.buf.code_action()*
|
||||
Selects a code action available at the current cursor position.
|
||||
Selects a code action (LSP: "textDocument/codeAction" request) available
|
||||
at cursor position.
|
||||
|
||||
Parameters: ~
|
||||
• {opts} (`table?`) A table with the following fields:
|
||||
@ -2507,6 +2557,27 @@ symbols_to_items({symbols}, {bufnr}, {position_encoding})
|
||||
==============================================================================
|
||||
Lua module: vim.lsp.log *lsp-log*
|
||||
|
||||
The `vim.lsp.log` module provides logging for the Nvim LSP client.
|
||||
|
||||
When debugging language servers, it is helpful to enable extra-verbose logging
|
||||
of the LSP client RPC events. Example: >lua
|
||||
vim.lsp.set_log_level 'trace'
|
||||
require('vim.lsp.log').set_format_func(vim.inspect)
|
||||
<
|
||||
|
||||
Then try to run the language server, and open the log with: >vim
|
||||
:lua vim.cmd('tabnew ' .. vim.lsp.get_log_path())
|
||||
<
|
||||
|
||||
(Or use `:LspLog` if you have nvim-lspconfig installed.)
|
||||
|
||||
Note:
|
||||
• Remember to DISABLE verbose logging ("debug" or "trace" level), else you may
|
||||
encounter performance issues.
|
||||
• "ERROR" messages containing "stderr" only indicate that the log was sent to
|
||||
stderr. Many servers send harmless messages via stderr.
|
||||
|
||||
|
||||
get_filename() *vim.lsp.log.get_filename()*
|
||||
Returns the log filename.
|
||||
|
||||
|
@ -93,10 +93,9 @@ Finally, you can include Lua code in a Vimscript file by putting it inside a
|
||||
Using Lua files on startup *lua-guide-config*
|
||||
|
||||
Nvim supports using `init.vim` or `init.lua` as the configuration file, but
|
||||
not both at the same time. This should be placed in your |config| directory,
|
||||
which is typically `~/.config/nvim` for Linux, BSD, or macOS, and
|
||||
`~/AppData/Local/nvim/` for Windows. Note that you can use Lua in `init.vim`
|
||||
and Vimscript in `init.lua`, which will be covered below.
|
||||
not both at the same time. This should be placed in your |config| directory
|
||||
(run `:echo stdpath('config')` to see where it is). Note that you can also use
|
||||
Lua in `init.vim` and Vimscript in `init.lua`, which will be covered below.
|
||||
|
||||
If you'd like to run any other Lua script on |startup| automatically, then you
|
||||
can simply put it in `plugin/` in your |'runtimepath'|.
|
||||
|
@ -171,7 +171,7 @@ added. But visually, this small bit of sugar gets reasonably close to a
|
||||
|
||||
*lua-regex*
|
||||
Lua intentionally does not support regular expressions, instead it has limited
|
||||
|lua-patterns| which avoid the performance pitfalls of extended regex. Lua
|
||||
|lua-pattern|s which avoid the performance pitfalls of extended regex. Lua
|
||||
scripts can also use Vim regex via |vim.regex()|.
|
||||
|
||||
Examples: >lua
|
||||
@ -2022,7 +2022,7 @@ vim.gsplit({s}, {sep}, {opts}) *vim.gsplit()*
|
||||
See also: ~
|
||||
• |string.gmatch()|
|
||||
• |vim.split()|
|
||||
• |lua-patterns|
|
||||
• |lua-pattern|s
|
||||
• https://www.lua.org/pil/20.2.html
|
||||
• http://lua-users.org/wiki/StringLibraryTutorial
|
||||
|
||||
@ -2115,7 +2115,7 @@ vim.list_slice({list}, {start}, {finish}) *vim.list_slice()*
|
||||
(`any[]`) Copy of table sliced from start to finish (inclusive)
|
||||
|
||||
vim.pesc({s}) *vim.pesc()*
|
||||
Escapes magic chars in |lua-patterns|.
|
||||
Escapes magic chars in |lua-pattern|s.
|
||||
|
||||
Parameters: ~
|
||||
• {s} (`string`) String to escape
|
||||
@ -2370,7 +2370,7 @@ vim.trim({s}) *vim.trim()*
|
||||
(`string`) String with whitespace removed from its beginning and end
|
||||
|
||||
See also: ~
|
||||
• |lua-patterns|
|
||||
• |lua-pattern|s
|
||||
• https://www.lua.org/pil/20.2.html
|
||||
|
||||
*vim.validate()*
|
||||
@ -2670,7 +2670,7 @@ vim.filetype.add({filetypes}) *vim.filetype.add()*
|
||||
Filetype mappings can be added either by extension or by filename (either
|
||||
the "tail" or the full file path). The full file path is checked first,
|
||||
followed by the file name. If a match is not found using the filename,
|
||||
then the filename is matched against the list of |lua-patterns| (sorted by
|
||||
then the filename is matched against the list of |lua-pattern|s (sorted by
|
||||
priority) until a match is found. Lastly, if pattern matching does not
|
||||
find a filetype, then the file extension is used.
|
||||
|
||||
@ -2898,7 +2898,7 @@ Use |uv.fs_stat()| to check a file's type, and whether it exists.
|
||||
|
||||
Example: >lua
|
||||
if vim.uv.fs_stat(file) then
|
||||
vim.print("file exists")
|
||||
vim.print('file exists')
|
||||
end
|
||||
<
|
||||
|
||||
@ -2953,7 +2953,8 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
|
||||
"fifo", "socket", "char", "block", "unknown".
|
||||
|
||||
vim.fs.dirname({file}) *vim.fs.dirname()*
|
||||
Return the parent directory of the given path
|
||||
Gets the parent directory of the given path (not expanded/resolved, the
|
||||
caller must do that).
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.8.0
|
||||
@ -2978,16 +2979,17 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
|
||||
narrow the search to find only that type.
|
||||
|
||||
Examples: >lua
|
||||
-- list all test directories under the runtime directory
|
||||
local test_dirs = vim.fs.find(
|
||||
{'test', 'tst', 'testdir'},
|
||||
{limit = math.huge, type = 'directory', path = './runtime/'}
|
||||
-- List all test directories under the runtime directory.
|
||||
local dirs = vim.fs.find(
|
||||
{ 'test', 'tst', 'testdir' },
|
||||
{ limit = math.huge, type = 'directory', path = './runtime/' }
|
||||
)
|
||||
|
||||
-- get all files ending with .cpp or .hpp inside lib/
|
||||
local cpp_hpp = vim.fs.find(function(name, path)
|
||||
-- Get all "lib/*.cpp" and "lib/*.hpp" files, using Lua patterns.
|
||||
-- Or use `vim.glob.to_lpeg(…):match(…)` for glob/wildcard matching.
|
||||
local files = vim.fs.find(function(name, path)
|
||||
return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
|
||||
end, {limit = math.huge, type = 'file'})
|
||||
end, { limit = math.huge, type = 'file' })
|
||||
<
|
||||
|
||||
Attributes: ~
|
||||
@ -3087,19 +3089,20 @@ vim.fs.normalize({path}, {opts}) *vim.fs.normalize()*
|
||||
(`string`) Normalized path
|
||||
|
||||
vim.fs.parents({start}) *vim.fs.parents()*
|
||||
Iterate over all the parents of the given path.
|
||||
Iterate over all the parents of the given path (not expanded/resolved, the
|
||||
caller must do that).
|
||||
|
||||
Example: >lua
|
||||
local root_dir
|
||||
for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
|
||||
if vim.fn.isdirectory(dir .. "/.git") == 1 then
|
||||
if vim.fn.isdirectory(dir .. '/.git') == 1 then
|
||||
root_dir = dir
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if root_dir then
|
||||
print("Found git repository at", root_dir)
|
||||
print('Found git repository at', root_dir)
|
||||
end
|
||||
<
|
||||
|
||||
@ -3737,19 +3740,25 @@ vim.regex({re}) *vim.regex()*
|
||||
Lua module: vim.secure *vim.secure*
|
||||
|
||||
vim.secure.read({path}) *vim.secure.read()*
|
||||
Attempt to read the file at {path} prompting the user if the file should
|
||||
be trusted. The user's choice is persisted in a trust database at
|
||||
If {path} is a file: attempt to read the file, prompting the user if the
|
||||
file should be trusted.
|
||||
|
||||
If {path} is a directory: return true if the directory is trusted
|
||||
(non-recursive), prompting the user as necessary.
|
||||
|
||||
The user's choice is persisted in a trust database at
|
||||
$XDG_STATE_HOME/nvim/trust.
|
||||
|
||||
Attributes: ~
|
||||
Since: 0.9.0
|
||||
|
||||
Parameters: ~
|
||||
• {path} (`string`) Path to a file to read.
|
||||
• {path} (`string`) Path to a file or directory to read.
|
||||
|
||||
Return: ~
|
||||
(`string?`) The contents of the given file if it exists and is
|
||||
trusted, or nil otherwise.
|
||||
(`boolean|string?`) If {path} is not trusted or does not exist,
|
||||
returns `nil`. Otherwise, returns the contents of {path} if it is a
|
||||
file, or true if {path} is a directory.
|
||||
|
||||
See also: ~
|
||||
• |:trust|
|
||||
|
@ -4150,7 +4150,7 @@ string.upper({s}) *string.upper()*
|
||||
locale.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
5.4.1 Patterns *lua-patterns*
|
||||
5.4.1 Patterns *lua-pattern*
|
||||
|
||||
A character class is used to represent a set of characters. The following
|
||||
combinations are allowed in describing a character class:
|
||||
@ -4207,6 +4207,7 @@ A pattern item may be
|
||||
- a single character class followed by `+`, which matches 1 or more
|
||||
repetitions of characters in the class. These repetition items will
|
||||
always match the longest possible sequence;
|
||||
*lua-nongreedy*
|
||||
- a single character class followed by `-`, which also matches 0 or
|
||||
more repetitions of characters in the class. Unlike `*`, these
|
||||
repetition items will always match the shortest possible sequence;
|
||||
@ -4221,7 +4222,7 @@ A pattern item may be
|
||||
`y` where the count reaches 0. For instance, the item `%b()` matches
|
||||
expressions with balanced parentheses.
|
||||
|
||||
PATTERN *lua-pattern*
|
||||
PATTERN
|
||||
|
||||
A pattern is a sequence of pattern items. A `^` at the beginning of a pattern
|
||||
anchors the match at the beginning of the subject string. A `$` at the end of
|
||||
|
@ -19,12 +19,7 @@ For changing the language of messages and menus see |mlang.txt|.
|
||||
==============================================================================
|
||||
Getting started *mbyte-first*
|
||||
|
||||
This is a summary of the multibyte features in Vim. If you are lucky it works
|
||||
as described and you can start using Vim without much trouble. If something
|
||||
doesn't work you will have to read the rest. Don't be surprised if it takes
|
||||
quite a bit of work and experimenting to make Vim use all the multibyte
|
||||
features. Unfortunately, every system has its own way to deal with multibyte
|
||||
languages and it is quite complicated.
|
||||
This is a summary of the multibyte features in Nvim.
|
||||
|
||||
|
||||
LOCALE
|
||||
@ -54,14 +49,14 @@ See |mbyte-locale| for details.
|
||||
|
||||
ENCODING
|
||||
|
||||
Nvim always uses UTF-8 internally. Thus 'encoding' option is always set
|
||||
to "utf-8" and cannot be changed.
|
||||
Nvim always uses UTF-8 internally. Thus 'encoding' is always set to "utf-8"
|
||||
and cannot be changed.
|
||||
|
||||
All the text that is used inside Vim will be in UTF-8. Not only the text in
|
||||
the buffers, but also in registers, variables, etc.
|
||||
|
||||
You can edit files in different encodings than UTF-8. Nvim
|
||||
will convert the file when you read it and convert it back when you write it.
|
||||
You can edit files in different encodings than UTF-8. Nvim will convert the
|
||||
file when you read it and convert it back when you write it.
|
||||
See 'fileencoding', 'fileencodings' and |++enc|.
|
||||
|
||||
|
||||
@ -184,9 +179,9 @@ You could make a small shell script for this.
|
||||
==============================================================================
|
||||
Encoding *mbyte-encoding*
|
||||
|
||||
In Nvim UTF-8 is always used internally to encode characters.
|
||||
This applies to all the places where text is used, including buffers (files
|
||||
loaded into memory), registers and variables.
|
||||
UTF-8 is always used internally to encode characters. This applies to all the
|
||||
places where text is used, including buffers (files loaded into memory),
|
||||
registers and variables.
|
||||
|
||||
*charset* *codeset*
|
||||
Charset is another name for encoding. There are subtle differences, but these
|
||||
@ -609,25 +604,25 @@ Combining forms:
|
||||
Using UTF-8 *mbyte-utf8* *UTF-8* *utf-8* *utf8*
|
||||
*Unicode* *unicode*
|
||||
The Unicode character set was designed to include all characters from other
|
||||
character sets. Therefore it is possible to write text in any language using
|
||||
Unicode (with a few rarely used languages excluded). And it's mostly possible
|
||||
to mix these languages in one file, which is impossible with other encodings.
|
||||
character sets. Therefore it is possible to write text in (almost) any
|
||||
language using Unicode. And it's mostly possible to mix these languages in
|
||||
one file, which is impossible with other encodings.
|
||||
|
||||
Unicode can be encoded in several ways. The most popular one is UTF-8, which
|
||||
uses one or more bytes for each character and is backwards compatible with
|
||||
ASCII. On MS-Windows UTF-16 is also used (previously UCS-2), which uses
|
||||
16-bit words. Vim can support all of these encodings, but always uses UTF-8
|
||||
ASCII. On MS-Windows UTF-16 is also used (previously UCS-2), which uses
|
||||
16-bit words. Nvim supports all of these encodings, but always uses UTF-8
|
||||
internally.
|
||||
|
||||
Vim has comprehensive UTF-8 support. It works well in:
|
||||
- xterm with UTF-8 support enabled
|
||||
- MS-Windows GUI
|
||||
- several other platforms
|
||||
|
||||
Double-width characters are supported. Works best with 'guifontwide'. When
|
||||
Nvim supports double-width characters; works best with 'guifontwide'. When
|
||||
using only 'guifont' the wide characters are drawn in the normal width and
|
||||
a space to fill the gap.
|
||||
|
||||
EMOJI *emoji*
|
||||
|
||||
You can list emoji characters using this script: >vim
|
||||
:source $VIMRUNTIME/scripts/emoji_list.lua
|
||||
<
|
||||
*bom-bytes*
|
||||
When reading a file a BOM (Byte Order Mark) can be used to recognize the
|
||||
Unicode encoding:
|
||||
@ -698,7 +693,7 @@ You need to specify a font to be used. For double-wide characters another
|
||||
font is required, which is exactly twice as wide. There are three ways to do
|
||||
this:
|
||||
|
||||
1. Set 'guifont' and let Vim find a matching 'guifontwide'
|
||||
1. Set 'guifont' and let Nvim find a matching 'guifontwide'
|
||||
2. Set 'guifont' and 'guifontwide'
|
||||
|
||||
See the documentation for each option for details. Example: >
|
||||
@ -730,7 +725,7 @@ COMMAND ARGUMENTS *utf-8-char-arg*
|
||||
|
||||
Commands like |f|, |F|, |t| and |r| take an argument of one character. For
|
||||
UTF-8 this argument may include one or two composing characters. These need
|
||||
to be produced together with the base character, Vim doesn't wait for the next
|
||||
to be produced together with the base character, Nvim doesn't wait for the next
|
||||
character to be typed to find out if it is a composing character or not.
|
||||
Using 'keymap' or |:lmap| is a nice way to type these characters.
|
||||
|
||||
@ -741,7 +736,6 @@ searching for a character with a composing character, this will only find
|
||||
matches with that composing character. It was implemented this way, because
|
||||
not everybody is able to type a composing character.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Overview of options *mbyte-options*
|
||||
|
||||
|
@ -171,6 +171,11 @@ API
|
||||
aligned text that truncates before covering up buffer text.
|
||||
• `virt_lines_overflow` field accepts value `scroll` to enable horizontal
|
||||
scrolling for virtual lines with 'nowrap'.
|
||||
• |vim.secure.read()| now returns `true` for trusted directories. Previously
|
||||
it would return `nil`, which made it impossible to tell if the directory was
|
||||
actually trusted.
|
||||
• Added |vim.lsp.is_enabled()| to check if a given LSP config has been enabled
|
||||
by |vim.lsp.enable()|.
|
||||
|
||||
DEFAULTS
|
||||
|
||||
@ -277,6 +282,7 @@ LSP
|
||||
• The `textDocument/completion` request now includes the completion context in
|
||||
its parameters.
|
||||
• |vim.lsp.Config| gained `workspace_required`.
|
||||
• `root_markers` in |vim.lsp.Config| can now be ordered by priority.
|
||||
|
||||
LUA
|
||||
|
||||
@ -343,6 +349,8 @@ PLUGINS
|
||||
• 'commentstring' values can now be specified in a Treesitter capture's
|
||||
`bo.commentstring` metadata field, providing finer grained support for
|
||||
languages like `JSX`.
|
||||
• Customize :checkhealth by handling a `FileType checkhealth` event.
|
||||
|health-usage|
|
||||
|
||||
STARTUP
|
||||
|
||||
@ -437,6 +445,7 @@ UI
|
||||
• |ui-messages| content chunks now also contain the highlight group ID.
|
||||
• |:checkhealth| can display in a floating window, controlled by the
|
||||
|g:health| variable.
|
||||
• |:checkhealth| shows a summary in the header for every healthcheck.
|
||||
|
||||
VIMSCRIPT
|
||||
|
||||
|
@ -75,6 +75,16 @@ the same Nvim configuration on all of your machines, by creating
|
||||
==============================================================================
|
||||
What next? *nvim-quickstart*
|
||||
|
||||
If you want to use Lua to configure Nvim, you can copy an example
|
||||
configuration to your |init.lua|
|
||||
>vim
|
||||
:exe 'edit' stdpath('config') .. '/init.lua'
|
||||
:read $VIMRUNTIME/example_init.lua
|
||||
<
|
||||
See |lua-guide| for practical notes on using Lua to configure Nvim.
|
||||
|
||||
"IDE" features in Nvim are provided by Language Server Protocol. See |lsp|
|
||||
|
||||
If you are just trying out Nvim for a few minutes, and want to see the
|
||||
extremes of what it can do, try one of these popular "extension packs" or
|
||||
"distributions" (Note: Nvim is not affiliated with these projects, and does
|
||||
|
@ -2361,6 +2361,12 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
- 'exrc' can execute any code; editorconfig only specifies settings.
|
||||
- 'exrc' is Nvim-specific; editorconfig works in other editors.
|
||||
|
||||
To achieve project-local LSP configuration:
|
||||
1. Enable 'exrc'.
|
||||
2. Place LSP configs at ".nvim/lsp/*.lua" in your project root.
|
||||
3. Create ".nvim.lua" in your project root directory with this line: >lua
|
||||
vim.cmd[[set runtimepath+=.nvim]]
|
||||
<
|
||||
This option cannot be set from a |modeline| or in the |sandbox|, for
|
||||
security reasons.
|
||||
|
||||
@ -5946,8 +5952,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
*'statusline'* *'stl'* *E540* *E542*
|
||||
'statusline' 'stl' string (default "")
|
||||
global or local to window |global-local|
|
||||
When non-empty, this option determines the content of the status line.
|
||||
Also see |status-line|.
|
||||
Sets the |status-line|.
|
||||
|
||||
The option consists of printf style '%' items interspersed with
|
||||
normal text. Each status line item is of the form:
|
||||
|
@ -4,6 +4,8 @@
|
||||
vim-tutor-mode provides a system to follow and create interactive tutorials
|
||||
for vim and third party plugins. It replaces the venerable `vimtutor` system.
|
||||
|
||||
Original Author: Felipe Morales <https://github.com/fmoralesc>
|
||||
|
||||
==============================================================================
|
||||
1. Usage *vim-tutor-usage*
|
||||
|
||||
@ -39,12 +41,5 @@ to be detected by the :Tutor command.
|
||||
It is recommended to use a less formal style when writing tutorials than in
|
||||
regular documentation (unless the content requires it).
|
||||
|
||||
============================================================================
|
||||
3. Contributing
|
||||
|
||||
Development of the plugin is done over at github [1]. Feel free to report
|
||||
issues and make suggestions.
|
||||
|
||||
[1]: https://github.com/fmoralesc/vim-tutor-mode
|
||||
|
||||
" vim: set ft=help :
|
||||
=============================================================================
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||
|
@ -91,28 +91,40 @@ processing a quickfix or location list command, it will be aborted.
|
||||
:ll[!] [nr] Same as ":cc", except the location list for the
|
||||
:[nr]ll[!] current window is used instead of the quickfix list.
|
||||
|
||||
*:cn* *:cne* *:cnext* *E553* *]q*
|
||||
*:cn* *:cne* *:cnext* *E553*
|
||||
:[count]cn[ext][!] Display the [count] next error in the list that
|
||||
includes a file name. If there are no file names at
|
||||
all, go to the [count] next error. See |:cc| for
|
||||
[!] and 'switchbuf'.
|
||||
|
||||
*:lne* *:lnext* *]l*
|
||||
*]q*
|
||||
]q Mapped to |:cnext|. |default-mappings|
|
||||
|
||||
*:lne* *:lnext*
|
||||
:[count]lne[xt][!] Same as ":cnext", except the location list for the
|
||||
current window is used instead of the quickfix list.
|
||||
|
||||
:[count]cN[ext][!] *:cp* *:cprevious* *:cprev* *:cN* *:cNext* *[q*
|
||||
*]l*
|
||||
]l Mapped to |:lnext|. |default-mappings|
|
||||
|
||||
:[count]cN[ext][!] *:cp* *:cprevious* *:cprev* *:cN* *:cNext*
|
||||
:[count]cp[revious][!] Display the [count] previous error in the list that
|
||||
includes a file name. If there are no file names at
|
||||
all, go to the [count] previous error. See |:cc| for
|
||||
[!] and 'switchbuf'.
|
||||
|
||||
*[q*
|
||||
[q Mapped to |:cprevious|. |default-mappings|
|
||||
|
||||
:[count]lN[ext][!] *:lp* *:lprevious* *:lprev* *:lN* *:lNext* *[l*
|
||||
|
||||
:[count]lN[ext][!] *:lp* *:lprevious* *:lprev* *:lN* *:lNext*
|
||||
:[count]lp[revious][!] Same as ":cNext" and ":cprevious", except the location
|
||||
list for the current window is used instead of the
|
||||
quickfix list.
|
||||
|
||||
*[l*
|
||||
[l Mapped to |:lprevious|. |default-mappings|
|
||||
|
||||
*:cabo* *:cabove*
|
||||
:[count]cabo[ve] Go to the [count] error above the current line in the
|
||||
current buffer. If [count] is omitted, then 1 is
|
||||
@ -171,52 +183,76 @@ processing a quickfix or location list command, it will be aborted.
|
||||
:[count]laf[ter] Same as ":cafter", except the location list for the
|
||||
current window is used instead of the quickfix list.
|
||||
|
||||
*:cnf* *:cnfile* *]CTRL-Q*
|
||||
*:cnf* *:cnfile*
|
||||
:[count]cnf[ile][!] Display the first error in the [count] next file in
|
||||
the list that includes a file name. If there are no
|
||||
file names at all or if there is no next file, go to
|
||||
the [count] next error. See |:cc| for [!] and
|
||||
'switchbuf'.
|
||||
|
||||
*:lnf* *:lnfile* *]CTRL-L*
|
||||
*]CTRL-Q*
|
||||
]CTRL-Q Mapped to |:cnfile|. |default-mappings|
|
||||
|
||||
*:lnf* *:lnfile*
|
||||
:[count]lnf[ile][!] Same as ":cnfile", except the location list for the
|
||||
current window is used instead of the quickfix list.
|
||||
|
||||
:[count]cNf[ile][!] *:cpf* *:cpfile* *:cNf* *:cNfile* *[CTRL-Q*
|
||||
*]CTRL-L*
|
||||
]CTRL-L Mapped to |:lnfile|. |default-mappings|
|
||||
|
||||
:[count]cNf[ile][!] *:cpf* *:cpfile* *:cNf* *:cNfile*
|
||||
:[count]cpf[ile][!] Display the last error in the [count] previous file in
|
||||
the list that includes a file name. If there are no
|
||||
file names at all or if there is no next file, go to
|
||||
the [count] previous error. See |:cc| for [!] and
|
||||
'switchbuf'.
|
||||
|
||||
*[CTRL-Q*
|
||||
[CTRL-Q Mapped to |:cpfile|. |default-mappings|
|
||||
|
||||
:[count]lNf[ile][!] *:lpf* *:lpfile* *:lNf* *:lNfile* *[CTRL-L*
|
||||
|
||||
:[count]lNf[ile][!] *:lpf* *:lpfile* *:lNf* *:lNfile*
|
||||
:[count]lpf[ile][!] Same as ":cNfile" and ":cpfile", except the location
|
||||
list for the current window is used instead of the
|
||||
quickfix list.
|
||||
|
||||
*:crewind* *:cr* *[Q*
|
||||
*[CTRL-L*
|
||||
[CTRL-L Mapped to |:lpfile|. |default-mappings|
|
||||
|
||||
*:crewind* *:cr*
|
||||
:cr[ewind][!] [nr] Display error [nr]. If [nr] is omitted, the FIRST
|
||||
error is displayed. See |:cc|.
|
||||
|
||||
*:lrewind* *:lr* *[L*
|
||||
*[Q*
|
||||
[Q Mapped to |:crewind|. |default-mappings|
|
||||
|
||||
*:lrewind* *:lr*
|
||||
:lr[ewind][!] [nr] Same as ":crewind", except the location list for the
|
||||
current window is used instead of the quickfix list.
|
||||
|
||||
*[L*
|
||||
[L Mapped to |:lrewind|. |default-mappings|
|
||||
|
||||
*:cfirst* *:cfir*
|
||||
:cfir[st][!] [nr] Same as ":crewind".
|
||||
|
||||
*:lfirst* *:lfir*
|
||||
:lfir[st][!] [nr] Same as ":lrewind".
|
||||
|
||||
*:clast* *:cla* *]Q*
|
||||
*:clast* *:cla*
|
||||
:cla[st][!] [nr] Display error [nr]. If [nr] is omitted, the LAST
|
||||
error is displayed. See |:cc|.
|
||||
|
||||
*:llast* *:lla* *]L*
|
||||
*]Q*
|
||||
]Q Mapped to |:clast|.
|
||||
|
||||
*:llast* *:lla*
|
||||
:lla[st][!] [nr] Same as ":clast", except the location list for the
|
||||
current window is used instead of the quickfix list.
|
||||
|
||||
*]L*
|
||||
]L Mapped to |:llast|.
|
||||
|
||||
*:cq* *:cquit*
|
||||
:cq[uit][!]
|
||||
:{N}cq[uit][!]
|
||||
|
@ -1382,8 +1382,8 @@ The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, $XDG_STATE_HOME,
|
||||
$XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables
|
||||
are used if defined, else default values (listed below) are used.
|
||||
|
||||
Throughout the help pages these defaults are used as placeholders, e.g.
|
||||
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
|
||||
Note: In the help these defaults are used as placeholders, e.g. "~/.config" is
|
||||
understood as "$XDG_CONFIG_HOME or ~/.config".
|
||||
|
||||
CONFIG DIRECTORY (DEFAULT) ~
|
||||
*$XDG_CONFIG_HOME* Nvim: stdpath("config")
|
||||
@ -1437,12 +1437,17 @@ configuration files in `$XDG_CONFIG_HOME/foo` instead of
|
||||
`$XDG_CONFIG_HOME/nvim`. `$NVIM_APPNAME` must be a name, such as "foo", or a
|
||||
relative path, such as "foo/bar".
|
||||
|
||||
Note: In the help wherever `$XDG_CONFIG_…/nvim` is mentioned it is understood
|
||||
as `$XDG_CONFIG_…/$NVIM_APPNAME`.
|
||||
|
||||
*state-isolation*
|
||||
One use-case for $NVIM_APPNAME is to "isolate" Nvim applications.
|
||||
Alternatively, for true isolation, on Linux you can use cgroups namespaces: >
|
||||
systemd-run --user -qt -p PrivateUsers=yes -p BindPaths=/home/user/profile_xy:/home/user/.config/nvim nvim
|
||||
|
||||
Note: Throughout the help pages, wherever `$XDG_CONFIG_…/nvim` is mentioned it
|
||||
is understood to mean `$XDG_CONFIG_…/$NVIM_APPNAME`.
|
||||
<
|
||||
*stateless*
|
||||
To run Nvim without creating any directories or data files: >
|
||||
NVIM_LOG_FILE=/dev/null nvim -n -i NONE
|
||||
|
||||
LOG FILE *log* *$NVIM_LOG_FILE* *E5430*
|
||||
Besides 'debug' and 'verbose', Nvim keeps a general log file for internal
|
||||
|
@ -274,27 +274,39 @@ g CTRL-] Like CTRL-], but use ":tjump" instead of ":tag".
|
||||
{Visual}g CTRL-] Same as "g CTRL-]", but use the highlighted text as
|
||||
the identifier.
|
||||
|
||||
*:tn* *:tnext* *]t*
|
||||
*:tn* *:tnext*
|
||||
:[count]tn[ext][!] Jump to [count] next matching tag (default 1). See
|
||||
|tag-!| for [!].
|
||||
|
||||
*:tp* *:tprevious* *[t*
|
||||
*]t*
|
||||
]t Mapped to |:tnext|. |default-mappings|
|
||||
|
||||
*:tp* *:tprevious*
|
||||
:[count]tp[revious][!] Jump to [count] previous matching tag (default 1).
|
||||
See |tag-!| for [!].
|
||||
|
||||
*[t*
|
||||
[t Mapped to |:tprevious|. |default-mappings|
|
||||
|
||||
*:tN* *:tNext*
|
||||
:[count]tN[ext][!] Same as ":tprevious".
|
||||
|
||||
*:tr* *:trewind* *[T*
|
||||
*:tr* *:trewind*
|
||||
:[count]tr[ewind][!] Jump to first matching tag. If [count] is given, jump
|
||||
to [count]th matching tag. See |tag-!| for [!].
|
||||
|
||||
*[T*
|
||||
[T Mapped to |:trewind|. |default-mappings|
|
||||
|
||||
*:tf* *:tfirst*
|
||||
:[count]tf[irst][!] Same as ":trewind".
|
||||
|
||||
*:tl* *:tlast* *]T*
|
||||
*:tl* *:tlast*
|
||||
:tl[ast][!] Jump to last matching tag. See |tag-!| for [!].
|
||||
|
||||
*]T*
|
||||
]T Mapped to |:tlast|. |default-mappings|
|
||||
|
||||
*:lt* *:ltag*
|
||||
:lt[ag][!] [name] Jump to tag [name] and add the matching tags to a new
|
||||
location list for the current window. [name] can be
|
||||
@ -335,12 +347,18 @@ the same as above, with a "p" prepended.
|
||||
:ptj[ump][!] [name] Does ":tjump[!] [name]" and shows the new tag in a
|
||||
"Preview" window. See |:ptag| for more info.
|
||||
|
||||
*:ptn* *:ptnext* *]CTRL-T*
|
||||
*:ptn* *:ptnext*
|
||||
:[count]ptn[ext][!] ":tnext" in the preview window. See |:ptag|.
|
||||
|
||||
*:ptp* *:ptprevious* *[CTRL-T*
|
||||
*]CTRL-T*
|
||||
]CTRL-T Mapped to |:ptnext|. |default-mappings|
|
||||
|
||||
*:ptp* *:ptprevious*
|
||||
:[count]ptp[revious][!] ":tprevious" in the preview window. See |:ptag|.
|
||||
|
||||
*[CTRL-T*
|
||||
[CTRL-T Mapped to |:ptprevious|. |default-mappings|
|
||||
|
||||
*:ptN* *:ptNext*
|
||||
:[count]ptN[ext][!] Same as ":ptprevious".
|
||||
|
||||
|
@ -120,7 +120,7 @@ The following predicates are built in:
|
||||
match.
|
||||
|
||||
`lua-match?` *treesitter-predicate-lua-match?*
|
||||
Match |lua-patterns| against the text corresponding to a node,
|
||||
Match |lua-pattern|s against the text corresponding to a node,
|
||||
similar to `match?`
|
||||
|
||||
`any-lua-match?` *treesitter-predicate-any-lua-match?*
|
||||
|
@ -103,11 +103,11 @@ g8 Print the hex values of the bytes used in the
|
||||
*gx*
|
||||
gx Opens the current filepath or URL (decided by
|
||||
|<cfile>|, 'isfname') at cursor using the system
|
||||
default handler, by calling |vim.ui.open()|.
|
||||
default handler. Mapped to |vim.ui.open()|.
|
||||
|
||||
*v_gx*
|
||||
{Visual}gx Opens the selected text using the system default
|
||||
handler, by calling |vim.ui.open()|.
|
||||
handler. Mapped to |vim.ui.open()|.
|
||||
|
||||
*:p* *:pr* *:print* *E749*
|
||||
:[range]p[rint] [flags]
|
||||
@ -611,6 +611,8 @@ to look up the value of 'commentstring' corresponding to the cursor position.
|
||||
(This can be different from the buffer's 'commentstring' in case of
|
||||
|treesitter-language-injections|.)
|
||||
|
||||
The following |default-mappings| are defined:
|
||||
|
||||
*gc* *gc-default*
|
||||
gc{motion} Comment or uncomment lines covered by {motion}.
|
||||
|
||||
|
@ -383,6 +383,7 @@ Options:
|
||||
- 'showcmdloc' cannot be set to empty.
|
||||
- 'signcolumn' can show multiple signs (dynamic or fixed columns)
|
||||
- 'statuscolumn' full control of columns using 'statusline' format
|
||||
- 'statusline' default is exposed as a statusline expression.
|
||||
- 'splitkeep' cannot be set to empty.
|
||||
- 'tabline' middle-click on tabpage label closes tabpage,
|
||||
and %@Func@foo%X can call any function on mouse-click
|
||||
|
@ -439,18 +439,17 @@ CTRL-W l Move cursor to Nth window right of current one. Uses the
|
||||
|
||||
CTRL-W w *CTRL-W_w* *CTRL-W_CTRL-W*
|
||||
CTRL-W CTRL-W Without count: move cursor to the |focusable| window
|
||||
below/right of the current one. If there is no (focusable)
|
||||
window below or right, go to top-left window. With count: go
|
||||
to Nth window (windows are numbered from top-left to
|
||||
bottom-right). To obtain the window number see |bufwinnr()|
|
||||
and |winnr()|. When N is larger than the number of windows go
|
||||
to the last window.
|
||||
below/right of the current one. If none, go to the top-left
|
||||
window. With count: go to Nth window (numbered top-left to
|
||||
bottom-right), skipping unfocusable windows. To obtain the
|
||||
window number see |bufwinnr()| and |winnr()|. When N is
|
||||
larger than the number of windows go to the last focusable
|
||||
window.
|
||||
|
||||
*CTRL-W_W*
|
||||
CTRL-W W Without count: move cursor to the |focusable| window
|
||||
above/left of current one. If there is no window above or
|
||||
left, go to bottom-right window. With count: go to Nth
|
||||
window, like with CTRL-W w.
|
||||
above/left of current one. If none, go to the bottom-right
|
||||
window. With count: go to Nth window, like CTRL-W w.
|
||||
|
||||
CTRL-W t *CTRL-W_t* *CTRL-W_CTRL-T*
|
||||
CTRL-W CTRL-T Move cursor to top-left window.
|
||||
@ -1273,7 +1272,7 @@ list of buffers. |unlisted-buffer|
|
||||
:w foobar | sp #
|
||||
< Also see |+cmd|.
|
||||
|
||||
:[N]bn[ext][!] [+cmd] [N] *:bn* *:bnext* *]b* *E87*
|
||||
:[N]bn[ext][!] [+cmd] [N] *:bn* *:bnext* *E87*
|
||||
Go to [N]th next buffer in buffer list. [N] defaults to one.
|
||||
Wraps around the end of the buffer list.
|
||||
See |:buffer-!| for [!].
|
||||
@ -1285,13 +1284,20 @@ list of buffers. |unlisted-buffer|
|
||||
the way when you're browsing code/text buffers. The next three
|
||||
commands also work like this.
|
||||
|
||||
*]b*
|
||||
]b Mapped to |:bnext|. |default-mappings|
|
||||
|
||||
*:sbn* *:sbnext*
|
||||
:[N]sbn[ext] [+cmd] [N]
|
||||
Split window and go to [N]th next buffer in buffer list.
|
||||
Wraps around the end of the buffer list. Uses 'switchbuf'
|
||||
Also see |+cmd|.
|
||||
|
||||
:[N]bN[ext][!] [+cmd] [N] *:bN* *:bNext* *:bp* *:bprevious* *[b* *E88*
|
||||
:[N]bN[ext][!] [+cmd] [N] *:bN* *:bNext* *:bp* *:bprevious* *E88*
|
||||
|
||||
*[b*
|
||||
[b Mapped to |:bprevious|. |default-mappings|
|
||||
|
||||
:[N]bp[revious][!] [+cmd] [N]
|
||||
Go to [N]th previous buffer in buffer list. [N] defaults to
|
||||
one. Wraps around the start of the buffer list.
|
||||
@ -1305,11 +1311,14 @@ list of buffers. |unlisted-buffer|
|
||||
Uses 'switchbuf'.
|
||||
Also see |+cmd|.
|
||||
|
||||
:br[ewind][!] [+cmd] *:br* *:bre* *:brewind* *[B*
|
||||
:br[ewind][!] [+cmd] *:br* *:bre* *:brewind*
|
||||
Go to first buffer in buffer list. If the buffer list is
|
||||
empty, go to the first unlisted buffer.
|
||||
See |:buffer-!| for [!].
|
||||
|
||||
*[B*
|
||||
[B Mapped to |:brewind|. |default-mappings|
|
||||
|
||||
:bf[irst] [+cmd] *:bf* *:bfirst*
|
||||
Same as |:brewind|.
|
||||
Also see |+cmd|.
|
||||
@ -1323,11 +1332,14 @@ list of buffers. |unlisted-buffer|
|
||||
:sbf[irst] [+cmd] *:sbf* *:sbfirst*
|
||||
Same as ":sbrewind".
|
||||
|
||||
:bl[ast][!] [+cmd] *:bl* *:blast* *]B*
|
||||
:bl[ast][!] [+cmd] *:bl* *:blast*
|
||||
Go to last buffer in buffer list. If the buffer list is
|
||||
empty, go to the last unlisted buffer.
|
||||
See |:buffer-!| for [!].
|
||||
|
||||
*]B*
|
||||
]B Mapped to |:blast|. |default-mappings|
|
||||
|
||||
:sbl[ast] [+cmd] *:sbl* *:sblast*
|
||||
Split window and go to last buffer in buffer list. If the
|
||||
buffer list is empty, go to the last unlisted buffer.
|
||||
|
88
runtime/example_init.lua
Normal file
88
runtime/example_init.lua
Normal file
@ -0,0 +1,88 @@
|
||||
-- Set <space> as the leader key
|
||||
-- See `:help mapleader`
|
||||
-- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
|
||||
vim.g.mapleader = ' '
|
||||
|
||||
-- [[ Setting options ]] See `:h vim.o`
|
||||
-- NOTE: You can change these options as you wish!
|
||||
-- For more options, you can see `:help option-list`
|
||||
-- To see documentation for an option, you can use `:h 'optionname'`, for example `:h 'number'`
|
||||
-- (Note the single quotes)
|
||||
|
||||
-- Print the line number in front of each line
|
||||
vim.o.number = true
|
||||
|
||||
-- Use relative line numbers, so that it is easier to jump with j, k. This will affect the 'number'
|
||||
-- option above, see `:h number_relativenumber`
|
||||
vim.o.relativenumber = true
|
||||
|
||||
-- Sync clipboard between OS and Neovim. Schedule the setting after `UiEnter` because it can
|
||||
-- increase startup-time. Remove this option if you want your OS clipboard to remain independent.
|
||||
-- See `:help 'clipboard'`
|
||||
vim.api.nvim_create_autocmd('UIEnter', {
|
||||
callback = function()
|
||||
vim.o.clipboard = 'unnamedplus'
|
||||
end,
|
||||
})
|
||||
|
||||
-- Case-insensitive searching UNLESS \C or one or more capital letters in the search term
|
||||
vim.o.ignorecase = true
|
||||
vim.o.smartcase = true
|
||||
|
||||
-- Highlight the line where the cursor is on
|
||||
vim.o.cursorline = true
|
||||
|
||||
-- Minimal number of screen lines to keep above and below the cursor.
|
||||
vim.o.scrolloff = 10
|
||||
|
||||
-- Show <tab> and trailing spaces
|
||||
vim.o.list = true
|
||||
|
||||
-- if performing an operation that would fail due to unsaved changes in the buffer (like `:q`),
|
||||
-- instead raise a dialog asking if you wish to save the current file(s) See `:help 'confirm'`
|
||||
vim.o.confirm = true
|
||||
|
||||
-- [[ Set up keymaps ]] See `:h vim.keymap.set()`, `:h mapping`, `:h keycodes`
|
||||
|
||||
-- Use <Esc> to exit terminal mode
|
||||
vim.keymap.set('t', '<Esc>', '<C-\\><C-n>')
|
||||
|
||||
-- Map <A-j>, <A-k>, <A-h>, <A-l> to navigate between windows in any modes
|
||||
vim.keymap.set({ 't', 'i' }, '<A-h>', '<C-\\><C-n><C-w>h')
|
||||
vim.keymap.set({ 't', 'i' }, '<A-j>', '<C-\\><C-n><C-w>j')
|
||||
vim.keymap.set({ 't', 'i' }, '<A-k>', '<C-\\><C-n><C-w>k')
|
||||
vim.keymap.set({ 't', 'i' }, '<A-l>', '<C-\\><C-n><C-w>l')
|
||||
vim.keymap.set({ 'n' }, '<A-h>', '<C-w>h')
|
||||
vim.keymap.set({ 'n' }, '<A-j>', '<C-w>j')
|
||||
vim.keymap.set({ 'n' }, '<A-k>', '<C-w>k')
|
||||
vim.keymap.set({ 'n' }, '<A-l>', '<C-w>l')
|
||||
|
||||
-- [[ Basic Autocommands ]].
|
||||
-- See `:h lua-guide-autocommands`, `:h autocmd`, `:h nvim_create_autocmd()`
|
||||
|
||||
-- Highlight when yanking (copying) text.
|
||||
-- Try it with `yap` in normal mode. See `:h vim.hl.on_yank()`
|
||||
vim.api.nvim_create_autocmd('TextYankPost', {
|
||||
desc = 'Highlight when yanking (copying) text',
|
||||
callback = function()
|
||||
vim.hl.on_yank()
|
||||
end,
|
||||
})
|
||||
|
||||
-- [[ Create user commands ]]
|
||||
-- See `:h nvim_create_user_command()` and `:h user-commands`
|
||||
|
||||
-- Create a command `:GitBlameLine` that print the git blame for the current line
|
||||
vim.api.nvim_create_user_command('GitBlameLine', function()
|
||||
local line_number = vim.fn.line('.') -- Get the current line number. See `:h line()`
|
||||
local filename = vim.api.nvim_buf_get_name(0)
|
||||
print(vim.fn.system({ 'git', 'blame', '-L', line_number .. ',+1', filename }))
|
||||
end, { desc = 'Print the git blame for the current line' })
|
||||
|
||||
-- [[ Add optional packages ]]
|
||||
-- Nvim comes bundled with a set of packages that are not enabled by
|
||||
-- default. You can enable any of them by using the `:packadd` command.
|
||||
|
||||
-- For example, to add the "nohlsearch" package to automatically turn off search highlighting after
|
||||
-- 'updatetime' and when going to insert mode
|
||||
vim.cmd('packadd! nohlsearch')
|
@ -78,4 +78,7 @@ function s:LuaInclude(fname) abort
|
||||
return fname
|
||||
endfunction
|
||||
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: nowrap sw=2 sts=2 ts=8 noet:
|
||||
|
@ -1,19 +1,18 @@
|
||||
" vim: fdm=marker
|
||||
" Tutor filetype plugin
|
||||
" Language: Tutor (the new tutor plugin)
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Last Change: 2025 May 10
|
||||
" Contributors: Phạm Bình An <phambinhanctb2004@gmail.com>
|
||||
" Original Author: Felipe Morales <hel.sheep@gmail.com>
|
||||
" Last Change:
|
||||
" 2025 May 10 set b:undo_ftplugin
|
||||
" 2025 May 12 update b:undo_ftplugin
|
||||
|
||||
" Base: {{{1
|
||||
call tutor#SetupVim()
|
||||
|
||||
" Buffer Settings: {{{1
|
||||
setlocal noreadonly
|
||||
if !exists('g:tutor_debug') || g:tutor_debug == 0
|
||||
setlocal buftype=nofile
|
||||
setlocal concealcursor+=inv
|
||||
setlocal conceallevel=2
|
||||
else
|
||||
setlocal buftype=
|
||||
setlocal concealcursor&
|
||||
setlocal conceallevel=0
|
||||
endif
|
||||
setlocal noundofile
|
||||
|
||||
setlocal keywordprg=:help
|
||||
@ -39,7 +38,7 @@ call tutor#SetNormalMappings()
|
||||
sign define tutorok text=✓ texthl=tutorOK
|
||||
sign define tutorbad text=✗ texthl=tutorX
|
||||
|
||||
if !exists('g:tutor_debug') || g:tutor_debug == 0
|
||||
call tutor#ApplyMarks()
|
||||
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
|
||||
endif
|
||||
let b:undo_ftplugin = "setl foldmethod< foldexpr< foldlevel< undofile< keywordprg< iskeyword< |"
|
||||
\ . "call tutor#EnableInteractive(v:false) |"
|
||||
|
||||
" vim: fdm=marker
|
||||
|
@ -810,6 +810,8 @@ function M.show_toc()
|
||||
fn.setloclist(0, {}, 'a', { title = 'Table of contents' })
|
||||
vim.cmd.lopen()
|
||||
vim.w.qf_toc = bufname
|
||||
-- reload syntax file after setting qf_toc variable
|
||||
vim.bo.filetype = 'qf'
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -3,13 +3,15 @@ local M = {}
|
||||
--- @param module string
|
||||
---@return string
|
||||
function M.includeexpr(module)
|
||||
local fname = module:gsub('%.', '/')
|
||||
module = module:gsub('%.', '/')
|
||||
|
||||
local root = vim.fs.root(vim.api.nvim_buf_get_name(0), 'lua') or vim.fn.getcwd()
|
||||
for _, suf in ipairs { '.lua', '/init.lua' } do
|
||||
local path = vim.fs.joinpath(root, 'lua', fname .. suf)
|
||||
if vim.uv.fs_stat(path) then
|
||||
return path
|
||||
for _, fname in ipairs { module, vim.fs.joinpath(root, 'lua', module) } do
|
||||
for _, suf in ipairs { '.lua', '/init.lua' } do
|
||||
local path = fname .. suf
|
||||
if vim.uv.fs_stat(path) then
|
||||
return path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -297,3 +297,18 @@
|
||||
--- A list of dictionaries with information about
|
||||
--- undo blocks.
|
||||
--- @field entries vim.fn.undotree.entry[]
|
||||
|
||||
--- @class vim.fn.winlayout.leaf
|
||||
--- @field [1] "leaf" Node type
|
||||
--- @field [2] integer winid
|
||||
|
||||
--- @class vim.fn.winlayout.branch
|
||||
--- @field [1] "row" | "col" Node type
|
||||
--- @field [2] (vim.fn.winlayout.leaf|vim.fn.winlayout.branch)[] children
|
||||
|
||||
--- @class vim.fn.winlayout.empty
|
||||
|
||||
--- @alias vim.fn.winlayout.ret
|
||||
--- | vim.fn.winlayout.leaf
|
||||
--- | vim.fn.winlayout.branch
|
||||
--- | vim.fn.winlayout.empty
|
||||
|
12
runtime/lua/vim/_meta/options.lua
generated
12
runtime/lua/vim/_meta/options.lua
generated
@ -2019,6 +2019,15 @@ vim.bo.et = vim.bo.expandtab
|
||||
--- - 'exrc' can execute any code; editorconfig only specifies settings.
|
||||
--- - 'exrc' is Nvim-specific; editorconfig works in other editors.
|
||||
---
|
||||
--- To achieve project-local LSP configuration:
|
||||
--- 1. Enable 'exrc'.
|
||||
--- 2. Place LSP configs at ".nvim/lsp/*.lua" in your project root.
|
||||
--- 3. Create ".nvim.lua" in your project root directory with this line:
|
||||
---
|
||||
--- ```lua
|
||||
--- vim.cmd[[set runtimepath+=.nvim]]
|
||||
--- ```
|
||||
---
|
||||
--- This option cannot be set from a `modeline` or in the `sandbox`, for
|
||||
--- security reasons.
|
||||
---
|
||||
@ -6343,8 +6352,7 @@ vim.o.stc = vim.o.statuscolumn
|
||||
vim.wo.statuscolumn = vim.o.statuscolumn
|
||||
vim.wo.stc = vim.wo.statuscolumn
|
||||
|
||||
--- When non-empty, this option determines the content of the status line.
|
||||
--- Also see `status-line`.
|
||||
--- Sets the `status-line`.
|
||||
---
|
||||
--- The option consists of printf style '%' items interspersed with
|
||||
--- normal text. Each status line item is of the form:
|
||||
|
2
runtime/lua/vim/_meta/vimfn.lua
generated
2
runtime/lua/vim/_meta/vimfn.lua
generated
@ -10929,7 +10929,7 @@ function vim.fn.winheight(nr) end
|
||||
--- <
|
||||
---
|
||||
--- @param tabnr? integer
|
||||
--- @return any[]
|
||||
--- @return vim.fn.winlayout.ret
|
||||
function vim.fn.winlayout(tabnr) end
|
||||
|
||||
--- The result is a Number, which is the screen line of the cursor
|
||||
|
@ -245,7 +245,13 @@ local function spawn(cmd, opts, on_exit, on_error)
|
||||
local handle, pid_or_err = uv.spawn(cmd, opts, on_exit)
|
||||
if not handle then
|
||||
on_error()
|
||||
error(('%s: "%s"'):format(pid_or_err, cmd))
|
||||
if opts.cwd and not uv.fs_stat(opts.cwd) then
|
||||
error(("%s (cwd): '%s'"):format(pid_or_err, opts.cwd))
|
||||
elseif vim.fn.executable(cmd) == 0 then
|
||||
error(("%s (cmd): '%s'"):format(pid_or_err, cmd))
|
||||
else
|
||||
error(pid_or_err)
|
||||
end
|
||||
end
|
||||
return handle, pid_or_err --[[@as integer]]
|
||||
end
|
||||
|
@ -2605,7 +2605,7 @@ end
|
||||
--- Filetype mappings can be added either by extension or by filename (either
|
||||
--- the "tail" or the full file path). The full file path is checked first,
|
||||
--- followed by the file name. If a match is not found using the filename, then
|
||||
--- the filename is matched against the list of |lua-patterns| (sorted by priority)
|
||||
--- the filename is matched against the list of |lua-pattern|s (sorted by priority)
|
||||
--- until a match is found. Lastly, if pattern matching does not find a
|
||||
--- filetype, then the file extension is used.
|
||||
---
|
||||
|
@ -6,7 +6,7 @@
|
||||
---
|
||||
--- >lua
|
||||
--- if vim.uv.fs_stat(file) then
|
||||
--- vim.print("file exists")
|
||||
--- vim.print('file exists')
|
||||
--- end
|
||||
--- <
|
||||
|
||||
@ -19,21 +19,21 @@ local sysname = uv.os_uname().sysname:lower()
|
||||
local iswin = not not (sysname:find('windows') or sysname:find('mingw'))
|
||||
local os_sep = iswin and '\\' or '/'
|
||||
|
||||
--- Iterate over all the parents of the given path.
|
||||
--- Iterate over all the parents of the given path (not expanded/resolved, the caller must do that).
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- local root_dir
|
||||
--- for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
|
||||
--- if vim.fn.isdirectory(dir .. "/.git") == 1 then
|
||||
--- if vim.fn.isdirectory(dir .. '/.git') == 1 then
|
||||
--- root_dir = dir
|
||||
--- break
|
||||
--- end
|
||||
--- end
|
||||
---
|
||||
--- if root_dir then
|
||||
--- print("Found git repository at", root_dir)
|
||||
--- print('Found git repository at', root_dir)
|
||||
--- end
|
||||
--- ```
|
||||
---
|
||||
@ -55,7 +55,7 @@ function M.parents(start)
|
||||
start
|
||||
end
|
||||
|
||||
--- Return the parent directory of the given path
|
||||
--- Gets the parent directory of the given path (not expanded/resolved, the caller must do that).
|
||||
---
|
||||
---@since 10
|
||||
---@generic T : string|nil
|
||||
@ -234,16 +234,17 @@ end
|
||||
--- Examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- -- list all test directories under the runtime directory
|
||||
--- local test_dirs = vim.fs.find(
|
||||
--- {'test', 'tst', 'testdir'},
|
||||
--- {limit = math.huge, type = 'directory', path = './runtime/'}
|
||||
--- -- List all test directories under the runtime directory.
|
||||
--- local dirs = vim.fs.find(
|
||||
--- { 'test', 'tst', 'testdir' },
|
||||
--- { limit = math.huge, type = 'directory', path = './runtime/' }
|
||||
--- )
|
||||
---
|
||||
--- -- get all files ending with .cpp or .hpp inside lib/
|
||||
--- local cpp_hpp = vim.fs.find(function(name, path)
|
||||
--- -- Get all "lib/*.cpp" and "lib/*.hpp" files, using Lua patterns.
|
||||
--- -- Or use `vim.glob.to_lpeg(…):match(…)` for glob/wildcard matching.
|
||||
--- local files = vim.fs.find(function(name, path)
|
||||
--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
|
||||
--- end, {limit = math.huge, type = 'file'})
|
||||
--- end, { limit = math.huge, type = 'file' })
|
||||
--- ```
|
||||
---
|
||||
---@since 10
|
||||
|
@ -1,16 +1,16 @@
|
||||
--- @brief
|
||||
---<pre>help
|
||||
--- vim.health is a minimal framework to help users troubleshoot configuration and
|
||||
--- any other environment conditions that a plugin might care about. Nvim ships
|
||||
--- with healthchecks for configuration, performance, python support, ruby
|
||||
--- support, clipboard support, and more.
|
||||
---
|
||||
--- To run all healthchecks, use: >vim
|
||||
--- vim.health is a minimal framework to help users troubleshoot configuration and any other
|
||||
--- environment conditions that a plugin might care about. Nvim ships with healthchecks for
|
||||
--- configuration, performance, python support, ruby support, clipboard support, and more.
|
||||
---
|
||||
--- :checkhealth
|
||||
--- <
|
||||
--- To run all healthchecks, use:
|
||||
--- ```vim
|
||||
--- :checkhealth
|
||||
--- ```
|
||||
--- Plugin authors are encouraged to write new healthchecks. |health-dev|
|
||||
---
|
||||
---<pre>help
|
||||
--- COMMANDS *health-commands*
|
||||
---
|
||||
--- *:che* *:checkhealth*
|
||||
@ -46,7 +46,6 @@
|
||||
--- q Closes the window.
|
||||
---
|
||||
--- Global configuration:
|
||||
---
|
||||
--- *g:health*
|
||||
--- g:health Dictionary with the following optional keys:
|
||||
--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
|
||||
@ -55,57 +54,69 @@
|
||||
--- Example: >lua
|
||||
--- vim.g.health = { style = 'float' }
|
||||
---
|
||||
---</pre>
|
||||
---
|
||||
--- Local configuration:
|
||||
---
|
||||
--- Checkhealth sets its buffer filetype to "checkhealth". You can customize the buffer by handling
|
||||
--- the |FileType| event. For example if you don't want emojis in the health report:
|
||||
--- ```vim
|
||||
--- autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
|
||||
--- ```
|
||||
---
|
||||
---<pre>help
|
||||
--- --------------------------------------------------------------------------------
|
||||
--- Create a healthcheck *health-dev*
|
||||
---</pre>
|
||||
---
|
||||
--- Healthchecks are functions that check the user environment, configuration, or
|
||||
--- any other prerequisites that a plugin cares about. Nvim ships with
|
||||
--- healthchecks in:
|
||||
--- - $VIMRUNTIME/autoload/health/
|
||||
--- - $VIMRUNTIME/lua/vim/lsp/health.lua
|
||||
--- - $VIMRUNTIME/lua/vim/treesitter/health.lua
|
||||
--- - and more...
|
||||
--- Healthchecks are functions that check the user environment, configuration, or any other
|
||||
--- prerequisites that a plugin cares about. Nvim ships with healthchecks in:
|
||||
--- - $VIMRUNTIME/autoload/health/
|
||||
--- - $VIMRUNTIME/lua/vim/lsp/health.lua
|
||||
--- - $VIMRUNTIME/lua/vim/treesitter/health.lua
|
||||
--- - and more...
|
||||
---
|
||||
--- To add a new healthcheck for your own plugin, simply create a "health.lua"
|
||||
--- module on 'runtimepath' that returns a table with a "check()" function. Then
|
||||
--- |:checkhealth| will automatically find and invoke the function.
|
||||
--- To add a new healthcheck for your own plugin, simply create a "health.lua" module on
|
||||
--- 'runtimepath' that returns a table with a "check()" function. Then |:checkhealth| will
|
||||
--- automatically find and invoke the function.
|
||||
---
|
||||
--- For example if your plugin is named "foo", define your healthcheck module at
|
||||
--- one of these locations (on 'runtimepath'):
|
||||
--- - lua/foo/health/init.lua
|
||||
--- - lua/foo/health.lua
|
||||
--- - lua/foo/health/init.lua
|
||||
--- - lua/foo/health.lua
|
||||
---
|
||||
--- If your plugin also provides a submodule named "bar" for which you want
|
||||
--- a separate healthcheck, define the healthcheck at one of these locations:
|
||||
--- - lua/foo/bar/health/init.lua
|
||||
--- - lua/foo/bar/health.lua
|
||||
--- If your plugin also provides a submodule named "bar" for which you want a separate healthcheck,
|
||||
--- define the healthcheck at one of these locations:
|
||||
--- - lua/foo/bar/health/init.lua
|
||||
--- - lua/foo/bar/health.lua
|
||||
---
|
||||
--- All such health modules must return a Lua table containing a `check()`
|
||||
--- function.
|
||||
--- All such health modules must return a Lua table containing a `check()` function.
|
||||
---
|
||||
--- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path
|
||||
--- with your plugin name: >lua
|
||||
--- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path with your plugin
|
||||
--- name:
|
||||
---
|
||||
--- local M = {}
|
||||
--- ```lua
|
||||
--- local M = {}
|
||||
---
|
||||
--- M.check = function()
|
||||
--- vim.health.start("foo report")
|
||||
--- -- make sure setup function parameters are ok
|
||||
--- if check_setup() then
|
||||
--- vim.health.ok("Setup is correct")
|
||||
--- else
|
||||
--- vim.health.error("Setup is incorrect")
|
||||
--- end
|
||||
--- -- do some more checking
|
||||
--- -- ...
|
||||
--- end
|
||||
--- M.check = function()
|
||||
--- vim.health.start("foo report")
|
||||
--- -- make sure setup function parameters are ok
|
||||
--- if check_setup() then
|
||||
--- vim.health.ok("Setup is correct")
|
||||
--- else
|
||||
--- vim.health.error("Setup is incorrect")
|
||||
--- end
|
||||
--- -- do some more checking
|
||||
--- -- ...
|
||||
--- end
|
||||
---
|
||||
--- return M
|
||||
---</pre>
|
||||
--- return M
|
||||
--- ```
|
||||
|
||||
local M = {}
|
||||
|
||||
local s_output = {} ---@type string[]
|
||||
local check_summary = { warn = 0, error = 0 }
|
||||
|
||||
-- From a path return a list [{name}, {func}, {type}] representing a healthcheck
|
||||
local function filepath_to_healthcheck(path)
|
||||
@ -286,6 +297,7 @@ end
|
||||
function M.warn(msg, ...)
|
||||
local input = format_report_message('⚠️ WARNING', msg, ...)
|
||||
collect_output(input)
|
||||
check_summary['warn'] = check_summary['warn'] + 1
|
||||
end
|
||||
|
||||
--- Reports an error.
|
||||
@ -295,6 +307,7 @@ end
|
||||
function M.error(msg, ...)
|
||||
local input = format_report_message('❌ ERROR', msg, ...)
|
||||
collect_output(input)
|
||||
check_summary['error'] = check_summary['error'] + 1
|
||||
end
|
||||
|
||||
local path2name = function(path)
|
||||
@ -341,6 +354,23 @@ M._complete = function()
|
||||
return rv
|
||||
end
|
||||
|
||||
--- Gets the results heading for the current report section.
|
||||
---
|
||||
---@return string
|
||||
local function get_summary()
|
||||
local s = ''
|
||||
local errors = check_summary['error']
|
||||
local warns = check_summary['warn']
|
||||
|
||||
s = s .. (warns > 0 and (' %2d ⚠️'):format(warns) or '')
|
||||
s = s .. (errors > 0 and (' %2d ❌'):format(errors) or '')
|
||||
if errors == 0 and warns == 0 then
|
||||
s = s .. '✅'
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
--- Runs the specified healthchecks.
|
||||
--- Runs all discovered healthchecks if plugin_names is empty.
|
||||
---
|
||||
@ -353,7 +383,7 @@ function M._check(mods, plugin_names)
|
||||
|
||||
local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$')
|
||||
|
||||
local bufnr = vim.api.nvim_create_buf(true, true)
|
||||
local bufnr ---@type integer
|
||||
if
|
||||
vim.g.health
|
||||
and type(vim.g.health) == 'table'
|
||||
@ -361,7 +391,8 @@ function M._check(mods, plugin_names)
|
||||
then
|
||||
local max_height = math.floor(vim.o.lines * 0.8)
|
||||
local max_width = 80
|
||||
local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
|
||||
local float_winid
|
||||
bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
|
||||
height = max_height,
|
||||
width = max_width,
|
||||
offset_x = math.floor((vim.o.columns - max_width) / 2),
|
||||
@ -369,9 +400,10 @@ function M._check(mods, plugin_names)
|
||||
relative = 'editor',
|
||||
})
|
||||
vim.api.nvim_set_current_win(float_winid)
|
||||
vim.bo[float_bufnr].modifiable = true
|
||||
vim.bo[bufnr].modifiable = true
|
||||
vim.wo[float_winid].list = false
|
||||
else
|
||||
bufnr = vim.api.nvim_create_buf(true, true)
|
||||
-- When no command modifiers are used:
|
||||
-- - If the current buffer is empty, open healthcheck directly.
|
||||
-- - If not specified otherwise open healthcheck in a tab.
|
||||
@ -383,7 +415,6 @@ function M._check(mods, plugin_names)
|
||||
vim.cmd.bwipe('health://')
|
||||
end
|
||||
vim.cmd.file('health://')
|
||||
vim.cmd.setfiletype('checkhealth')
|
||||
|
||||
-- This should only happen when doing `:checkhealth vim`
|
||||
if next(healthchecks) == nil then
|
||||
@ -397,9 +428,9 @@ function M._check(mods, plugin_names)
|
||||
local func = value[1]
|
||||
local type = value[2]
|
||||
s_output = {}
|
||||
check_summary = { warn = 0, error = 0 }
|
||||
|
||||
if func == '' then
|
||||
s_output = {}
|
||||
M.error('No healthcheck found for "' .. name .. '" plugin.')
|
||||
end
|
||||
if type == 'v' then
|
||||
@ -420,10 +451,12 @@ function M._check(mods, plugin_names)
|
||||
M.error('The healthcheck report for "' .. name .. '" plugin is empty.')
|
||||
end
|
||||
|
||||
local report = get_summary()
|
||||
local replen = vim.fn.strwidth(report)
|
||||
local header = {
|
||||
string.rep('=', 78),
|
||||
-- Example: `foo.health: [ …] require("foo.health").check()`
|
||||
('%s: %s%s'):format(name, (' '):rep(76 - name:len() - func:len()), func),
|
||||
-- Example: `foo.health: [ …] 1 ⚠️ 5 ❌`
|
||||
('%s: %s%s'):format(name, (' '):rep(76 - name:len() - replen), report),
|
||||
'',
|
||||
}
|
||||
|
||||
@ -461,6 +494,7 @@ function M._check(mods, plugin_names)
|
||||
|
||||
-- Once we're done writing checks, set nomodifiable.
|
||||
vim.bo[bufnr].modifiable = false
|
||||
vim.cmd.setfiletype('checkhealth')
|
||||
end
|
||||
|
||||
return M
|
||||
|
@ -147,7 +147,7 @@ end
|
||||
--- @param config vim.lsp.ClientConfig
|
||||
--- @return boolean
|
||||
local function reuse_client_default(client, config)
|
||||
if client.name ~= config.name then
|
||||
if client.name ~= config.name or client:is_stopped() then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -277,24 +277,52 @@ end
|
||||
--- See `cmd` in [vim.lsp.ClientConfig].
|
||||
--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
|
||||
---
|
||||
--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`.
|
||||
--- If not provided, then the client will attach to all filetypes.
|
||||
--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`. If not provided, the
|
||||
--- client will attach to all filetypes.
|
||||
--- @field filetypes? string[]
|
||||
---
|
||||
--- Predicate which decides if a client should be re-used. Used on all running clients. The default
|
||||
--- implementation re-uses a client if name and root_dir matches.
|
||||
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
|
||||
---
|
||||
--- [lsp-root_dir()]() Directory where the LSP server will base its workspaceFolders, rootUri, and
|
||||
--- rootPath on initialization. The function form receives a buffer number and `on_dir` callback
|
||||
--- which it must call to provide root_dir, or LSP will not be activated for the buffer. Thus
|
||||
--- a `root_dir()` function can dynamically decide per-buffer whether to activate (or skip) LSP. See
|
||||
--- example at |vim.lsp.enable()|.
|
||||
--- @field root_dir? string|fun(bufnr: integer, on_dir:fun(root_dir?:string))
|
||||
---
|
||||
--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders,
|
||||
--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
|
||||
--- @field root_markers? string[]
|
||||
---
|
||||
--- Directory where the LSP server will base its workspaceFolders, rootUri, and
|
||||
--- rootPath on initialization. If a function, it is passed the buffer number
|
||||
--- and a callback argument which must be called with the value of root_dir to
|
||||
--- use. The LSP server will not be started until the callback is called.
|
||||
--- @field root_dir? string|fun(bufnr: integer, cb:fun(root_dir?:string))
|
||||
--- The list order decides the priority. To indicate "equal priority", specify names in a nested list (`{ { 'a', 'b' }, ... }`)
|
||||
--- Each entry in this list is a set of one or more markers. For each set, Nvim
|
||||
--- will search upwards for each marker contained in the set. If a marker is
|
||||
--- found, the directory which contains that marker is used as the root
|
||||
--- directory. If no markers from the set are found, the process is repeated
|
||||
--- with the next set in the list.
|
||||
---
|
||||
--- Predicate used to decide if a client should be re-used. Used on all
|
||||
--- running clients. The default implementation re-uses a client if name and
|
||||
--- root_dir matches.
|
||||
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- root_markers = { 'stylua.toml', '.git' }
|
||||
--- ```
|
||||
---
|
||||
--- Find the first parent directory containing the file `stylua.toml`. If not
|
||||
--- found, find the first parent directory containing the file or directory
|
||||
--- `.git`.
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- root_markers = { { 'stylua.toml', '.luarc.json' }, '.git' }
|
||||
--- ```
|
||||
---
|
||||
--- Find the first parent directory containing EITHER `stylua.toml` or
|
||||
--- `.luarc.json`. If not found, find the first parent directory containing the
|
||||
--- file or directory `.git`.
|
||||
---
|
||||
--- @field root_markers? (string|string[])[]
|
||||
|
||||
--- Update the configuration for an LSP client.
|
||||
---
|
||||
@ -429,7 +457,8 @@ lsp.config = setmetatable({ _configs = {} }, {
|
||||
--- @param cfg vim.lsp.Config
|
||||
__newindex = function(self, name, cfg)
|
||||
validate_config_name(name)
|
||||
validate('cfg', cfg, 'table')
|
||||
local msg = ('table (hint: to resolve a config, use vim.lsp.config["%s"])'):format(name)
|
||||
validate('cfg', cfg, 'table', msg)
|
||||
invalidate_enabled_config(name)
|
||||
self._configs[name] = cfg
|
||||
end,
|
||||
@ -439,7 +468,8 @@ lsp.config = setmetatable({ _configs = {} }, {
|
||||
--- @param cfg vim.lsp.Config
|
||||
__call = function(self, name, cfg)
|
||||
validate_config_name(name)
|
||||
validate('cfg', cfg, 'table')
|
||||
local msg = ('table (hint: to resolve a config, use vim.lsp.config["%s"])'):format(name)
|
||||
validate('cfg', cfg, 'table', msg)
|
||||
invalidate_enabled_config(name)
|
||||
self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
|
||||
end,
|
||||
@ -498,6 +528,15 @@ local function lsp_enable_callback(bufnr)
|
||||
return
|
||||
end
|
||||
|
||||
-- Stop any clients that no longer apply to this buffer.
|
||||
local clients = lsp.get_clients({ bufnr = bufnr, _uninitialized = true })
|
||||
for _, client in ipairs(clients) do
|
||||
if lsp.config[client.name] and not can_start(bufnr, client.name, lsp.config[client.name]) then
|
||||
lsp.buf_detach_client(bufnr, client.id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Start any clients that apply to this buffer.
|
||||
for name in vim.spairs(lsp._enabled_configs) do
|
||||
local config = lsp.config[name]
|
||||
if config and can_start(bufnr, name, config) then
|
||||
@ -520,16 +559,27 @@ local function lsp_enable_callback(bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
--- Enable an LSP server to automatically start when opening a buffer.
|
||||
---
|
||||
--- Uses configuration defined with `vim.lsp.config`.
|
||||
--- Auto-starts LSP when a buffer is opened, based on the |lsp-config| `filetypes`, `root_markers`,
|
||||
--- and `root_dir` fields.
|
||||
---
|
||||
--- Examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- vim.lsp.enable('clangd')
|
||||
--- vim.lsp.enable('clangd')
|
||||
--- vim.lsp.enable({'luals', 'pyright'})
|
||||
--- ```
|
||||
---
|
||||
--- vim.lsp.enable({'luals', 'pyright'})
|
||||
--- Example: To _dynamically_ decide whether LSP is activated, define a |lsp-root_dir()| function
|
||||
--- which calls `on_dir()` only when you want that config to activate:
|
||||
---
|
||||
--- ```lua
|
||||
--- vim.lsp.config('lua_ls', {
|
||||
--- root_dir = function(bufnr, on_dir)
|
||||
--- if not vim.fn.bufname(bufnr):match('%.txt$') then
|
||||
--- on_dir(vim.fn.getcwd())
|
||||
--- end
|
||||
--- end
|
||||
--- })
|
||||
--- ```
|
||||
---
|
||||
--- @param name string|string[] Name(s) of client(s) to enable.
|
||||
@ -546,21 +596,42 @@ function lsp.enable(name, enable)
|
||||
end
|
||||
|
||||
if not next(lsp._enabled_configs) then
|
||||
-- If there are no remaining LSPs enabled, remove the enable autocmd.
|
||||
if lsp_enable_autocmd_id then
|
||||
api.nvim_del_autocmd(lsp_enable_autocmd_id)
|
||||
lsp_enable_autocmd_id = nil
|
||||
end
|
||||
return
|
||||
else
|
||||
-- Only ever create autocmd once to reuse computation of config merging.
|
||||
lsp_enable_autocmd_id = lsp_enable_autocmd_id
|
||||
or api.nvim_create_autocmd('FileType', {
|
||||
group = api.nvim_create_augroup('nvim.lsp.enable', {}),
|
||||
callback = function(args)
|
||||
lsp_enable_callback(args.buf)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Only ever create autocmd once to reuse computation of config merging.
|
||||
lsp_enable_autocmd_id = lsp_enable_autocmd_id
|
||||
or api.nvim_create_autocmd('FileType', {
|
||||
group = api.nvim_create_augroup('nvim.lsp.enable', {}),
|
||||
callback = function(args)
|
||||
lsp_enable_callback(args.buf)
|
||||
end,
|
||||
})
|
||||
-- Ensure any pre-existing buffers start/stop their LSP clients.
|
||||
if enable ~= false then
|
||||
if vim.v.vim_did_enter == 1 then
|
||||
vim.cmd.doautoall('nvim.lsp.enable FileType')
|
||||
end
|
||||
else
|
||||
for _, nm in ipairs(names) do
|
||||
for _, client in ipairs(lsp.get_clients({ name = nm })) do
|
||||
client:stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Checks if the given LSP config is enabled (globally, not per-buffer).
|
||||
---
|
||||
--- @param name string Config name
|
||||
--- @return boolean
|
||||
function lsp.is_enabled(name)
|
||||
return lsp._enabled_configs[name] ~= nil
|
||||
end
|
||||
|
||||
--- @class vim.lsp.start.Opts
|
||||
@ -582,7 +653,7 @@ end
|
||||
--- Suppress error reporting if the LSP server fails to start (default false).
|
||||
--- @field silent? boolean
|
||||
---
|
||||
--- @field package _root_markers? string[]
|
||||
--- @field package _root_markers? (string|string[])[]
|
||||
|
||||
--- Create a new LSP client and start a language server or reuses an already
|
||||
--- running client if one is found matching `name` and `root_dir`.
|
||||
@ -629,8 +700,16 @@ function lsp.start(config, opts)
|
||||
local bufnr = vim._resolve_bufnr(opts.bufnr)
|
||||
|
||||
if not config.root_dir and opts._root_markers then
|
||||
validate('root_markers', opts._root_markers, 'table')
|
||||
config = vim.deepcopy(config)
|
||||
config.root_dir = vim.fs.root(bufnr, opts._root_markers)
|
||||
|
||||
for _, marker in ipairs(opts._root_markers) do
|
||||
local root = vim.fs.root(bufnr, marker)
|
||||
if root ~= nil then
|
||||
config.root_dir = root
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if
|
||||
|
@ -1211,8 +1211,7 @@ local function on_code_action_results(results, opts)
|
||||
vim.ui.select(actions, select_opts, on_user_choice)
|
||||
end
|
||||
|
||||
--- Selects a code action available at the current
|
||||
--- cursor position.
|
||||
--- Selects a code action (LSP: "textDocument/codeAction" request) available at cursor position.
|
||||
---
|
||||
---@param opts? vim.lsp.buf.code_action.Opts
|
||||
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
|
||||
|
@ -30,6 +30,19 @@ local validate = vim.validate
|
||||
--- @field exit_timeout integer|false
|
||||
|
||||
--- @class vim.lsp.ClientConfig
|
||||
---
|
||||
--- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters
|
||||
--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
|
||||
--- You can use this to modify parameters before they are sent.
|
||||
--- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
|
||||
---
|
||||
--- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|,
|
||||
--- passed to the language server on initialization. Hint: use make_client_capabilities() and modify
|
||||
--- its result.
|
||||
--- - Note: To send an empty dictionary use |vim.empty_dict()|, else it will be encoded as an
|
||||
--- array.
|
||||
--- @field capabilities? lsp.ClientCapabilities
|
||||
---
|
||||
--- command string[] that launches the language
|
||||
--- server (treated as in |jobstart()|, must be absolute or on `$PATH`, shell constructs like
|
||||
--- "~" are not expanded), or function that creates an RPC client. Function receives
|
||||
@ -43,123 +56,129 @@ local validate = vim.validate
|
||||
--- (default: cwd)
|
||||
--- @field cmd_cwd? string
|
||||
---
|
||||
--- Environment flags to pass to the LSP on spawn.
|
||||
--- Must be specified using a table.
|
||||
--- Non-string values are coerced to string.
|
||||
--- Environment variables passed to the LSP process on spawn. Non-string values are coerced to
|
||||
--- string.
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- { PORT = 8080; HOST = "0.0.0.0"; }
|
||||
--- { PORT = 8080; HOST = '0.0.0.0'; }
|
||||
--- ```
|
||||
--- @field cmd_env? table
|
||||
---
|
||||
--- Client commands. Map of command names to user-defined functions. Commands passed to `start()`
|
||||
--- take precedence over the global command registry. Each key must be a unique command name, and
|
||||
--- the value is a function which is called if any LSP action (code action, code lenses, …) triggers
|
||||
--- the command.
|
||||
--- @field commands? table<string,fun(command: lsp.Command, ctx: table)>
|
||||
---
|
||||
--- Daemonize the server process so that it runs in a separate process group from Nvim.
|
||||
--- Nvim will shutdown the process on exit, but if Nvim fails to exit cleanly this could leave
|
||||
--- behind orphaned server processes.
|
||||
--- (default: true)
|
||||
--- @field detached? boolean
|
||||
---
|
||||
--- List of workspace folders passed to the language server.
|
||||
--- For backwards compatibility rootUri and rootPath will be derived from the first workspace
|
||||
--- folder in this list. See `workspaceFolders` in the LSP spec.
|
||||
--- @field workspace_folders? lsp.WorkspaceFolder[]
|
||||
--- A table with flags for the client. The current (experimental) flags are:
|
||||
--- @field flags? vim.lsp.Client.Flags
|
||||
---
|
||||
--- (default false) Server requires a workspace (no "single file" support).
|
||||
--- @field workspace_required? boolean
|
||||
--- Language ID as string. Defaults to the buffer filetype.
|
||||
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
|
||||
---
|
||||
--- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|,
|
||||
--- passed to the language server on initialization. Hint: use make_client_capabilities() and modify
|
||||
--- its result.
|
||||
--- - Note: To send an empty dictionary use |vim.empty_dict()|, else it will be encoded as an
|
||||
--- array.
|
||||
--- @field capabilities? lsp.ClientCapabilities
|
||||
---
|
||||
--- Map of language server method names to |lsp-handler|
|
||||
--- Map of LSP method names to |lsp-handler|s.
|
||||
--- @field handlers? table<string,function>
|
||||
---
|
||||
--- Map with language server specific settings.
|
||||
--- See the {settings} in |vim.lsp.Client|.
|
||||
--- @field settings? lsp.LSPObject
|
||||
---
|
||||
--- Table that maps string of clientside commands to user-defined functions.
|
||||
--- Commands passed to `start()` take precedence over the global command registry. Each key
|
||||
--- must be a unique command name, and the value is a function which is called if any LSP action
|
||||
--- (code action, code lenses, ...) triggers the command.
|
||||
--- @field commands? table<string,fun(command: lsp.Command, ctx: table)>
|
||||
---
|
||||
--- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
|
||||
--- the LSP spec.
|
||||
--- @field init_options? lsp.LSPObject
|
||||
---
|
||||
--- Name in log messages.
|
||||
--- (default: client-id)
|
||||
--- (default: client-id) Name in logs and user messages.
|
||||
--- @field name? string
|
||||
---
|
||||
--- Language ID as string. Defaults to the buffer filetype.
|
||||
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
|
||||
---
|
||||
--- Called "position encoding" in LSP spec, the encoding that the LSP server expects.
|
||||
--- Client does not verify this is correct.
|
||||
--- Called "position encoding" in LSP spec. The encoding that the LSP server expects, used for
|
||||
--- communication. Not validated. Can be modified in `on_init` before text is sent to the server.
|
||||
--- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32'
|
||||
---
|
||||
--- Callback invoked when client attaches to a buffer.
|
||||
--- @field on_attach? elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>
|
||||
---
|
||||
--- Callback invoked when the client operation throws an error. `code` is a number describing the error.
|
||||
--- Other arguments may be passed depending on the error kind. See `vim.lsp.rpc.client_errors`
|
||||
--- for possible errors. Use `vim.lsp.rpc.client_errors[code]` to get human-friendly name.
|
||||
--- @field on_error? fun(code: integer, err: string)
|
||||
---
|
||||
--- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters
|
||||
--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
|
||||
--- You can use this to modify parameters before they are sent.
|
||||
--- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
|
||||
---
|
||||
--- Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and
|
||||
--- anything else the server may send. For example, clangd sends `init_result.offsetEncoding` if
|
||||
--- `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding`
|
||||
--- here before any notifications are sent.
|
||||
--- @field on_init? elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>
|
||||
---
|
||||
--- Callback invoked on client exit.
|
||||
--- - code: exit code of the process
|
||||
--- - signal: number describing the signal used to terminate (if any)
|
||||
--- - client_id: client handle
|
||||
--- @field on_exit? elem_or_list<fun(code: integer, signal: integer, client_id: integer)>
|
||||
---
|
||||
--- Callback invoked when client attaches to a buffer.
|
||||
--- @field on_attach? elem_or_list<fun(client: vim.lsp.Client, bufnr: integer)>
|
||||
--- Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and
|
||||
--- anything else the server may send. For example, clangd sends `init_result.offsetEncoding` if
|
||||
--- `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding`
|
||||
--- here before any notifications are sent.
|
||||
--- @field on_init? elem_or_list<fun(client: vim.lsp.Client, init_result: lsp.InitializeResult)>
|
||||
---
|
||||
--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on initialization.
|
||||
--- @field root_dir? string
|
||||
---
|
||||
--- Map of language server-specific settings, decided by the client. Sent to the LS if requested via
|
||||
--- `workspace/configuration`. Keys are case-sensitive.
|
||||
--- @field settings? lsp.LSPObject
|
||||
---
|
||||
--- Passed directly to the language server in the initialize request. Invalid/empty values will
|
||||
--- (default: "off")
|
||||
--- @field trace? 'off'|'messages'|'verbose'
|
||||
---
|
||||
--- A table with flags for the client. The current (experimental) flags are:
|
||||
--- @field flags? vim.lsp.Client.Flags
|
||||
--- List of workspace folders passed to the language server. For backwards compatibility rootUri and
|
||||
--- rootPath are derived from the first workspace folder in this list. Can be `null` if the client
|
||||
--- supports workspace folders but none are configured. See `workspaceFolders` in LSP spec.
|
||||
--- @field workspace_folders? lsp.WorkspaceFolder[]
|
||||
---
|
||||
--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on initialization.
|
||||
--- @field root_dir? string
|
||||
--- (default false) Server requires a workspace (no "single file" support). Note: Without
|
||||
--- a workspace, cross-file features (navigation, hover) may or may not work depending on the
|
||||
--- language server, even if the server doesn't require a workspace.
|
||||
--- @field workspace_required? boolean
|
||||
|
||||
--- @class vim.lsp.Client.Progress: vim.Ringbuf<{token: integer|string, value: any}>
|
||||
--- @field pending table<lsp.ProgressToken,lsp.LSPAny>
|
||||
|
||||
--- @class vim.lsp.Client
|
||||
---
|
||||
--- @field attached_buffers table<integer,true>
|
||||
---
|
||||
--- Capabilities provided by the client (editor or tool), at startup.
|
||||
--- @field capabilities lsp.ClientCapabilities
|
||||
---
|
||||
--- Client commands. See [vim.lsp.ClientConfig].
|
||||
--- @field commands table<string,fun(command: lsp.Command, ctx: table)>
|
||||
---
|
||||
--- Copy of the config passed to |vim.lsp.start()|.
|
||||
--- @field config vim.lsp.ClientConfig
|
||||
---
|
||||
--- Capabilities provided at runtime (after startup).
|
||||
--- @field dynamic_capabilities lsp.DynamicCapabilities
|
||||
---
|
||||
--- A table with flags for the client. The current (experimental) flags are:
|
||||
--- @field flags vim.lsp.Client.Flags
|
||||
---
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field get_language_id fun(bufnr: integer, filetype: string): string
|
||||
---
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field handlers table<string,lsp.Handler>
|
||||
---
|
||||
--- The id allocated to the client.
|
||||
--- @field id integer
|
||||
---
|
||||
--- If a name is specified on creation, that will be used. Otherwise it is just
|
||||
--- the client id. This is used for logs and messages.
|
||||
--- @field initialized true?
|
||||
---
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field name string
|
||||
---
|
||||
--- RPC client object, for low level interaction with the client.
|
||||
--- See |vim.lsp.rpc.start()|.
|
||||
--- @field rpc vim.lsp.rpc.PublicClient
|
||||
---
|
||||
--- Called "position encoding" in LSP spec,
|
||||
--- the encoding used for communicating with the server.
|
||||
--- You can modify this in the `config`'s `on_init` method
|
||||
--- before text is sent to the server.
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field offset_encoding string
|
||||
---
|
||||
--- The handlers used by the client as described in |lsp-handler|.
|
||||
--- @field handlers table<string,lsp.Handler>
|
||||
--- A ring buffer (|vim.ringbuf()|) containing progress messages
|
||||
--- sent by the server.
|
||||
--- @field progress vim.lsp.Client.Progress
|
||||
---
|
||||
--- The current pending requests in flight to the server. Entries are key-value
|
||||
--- pairs with the key being the request id while the value is a table with
|
||||
@ -169,34 +188,25 @@ local validate = vim.validate
|
||||
--- are received from the server.
|
||||
--- @field requests table<integer,{ type: string, bufnr: integer, method: string}?>
|
||||
---
|
||||
--- copy of the table that was passed by the user
|
||||
--- to |vim.lsp.start()|.
|
||||
--- @field config vim.lsp.ClientConfig
|
||||
---
|
||||
--- Response from the server sent on `initialize` describing the server's
|
||||
--- capabilities.
|
||||
--- @field server_capabilities lsp.ServerCapabilities?
|
||||
---
|
||||
--- Response from the server sent on `initialize` describing information about
|
||||
--- the server.
|
||||
--- @field server_info lsp.ServerInfo?
|
||||
---
|
||||
--- A ring buffer (|vim.ringbuf()|) containing progress messages
|
||||
--- sent by the server.
|
||||
--- @field progress vim.lsp.Client.Progress
|
||||
---
|
||||
--- @field initialized true?
|
||||
---
|
||||
--- The workspace folders configured in the client when the server starts.
|
||||
--- This property is only available if the client supports workspace folders.
|
||||
--- It can be `null` if the client supports workspace folders but none are
|
||||
--- configured.
|
||||
--- @field workspace_folders lsp.WorkspaceFolder[]?
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field root_dir string?
|
||||
---
|
||||
--- @field attached_buffers table<integer,true>
|
||||
--- RPC client object, for low level interaction with the client.
|
||||
--- See |vim.lsp.rpc.start()|.
|
||||
--- @field rpc vim.lsp.rpc.PublicClient
|
||||
---
|
||||
--- Response from the server sent on `initialize` describing the server's capabilities.
|
||||
--- @field server_capabilities lsp.ServerCapabilities?
|
||||
---
|
||||
--- Response from the server sent on `initialize` describing server information (e.g. version).
|
||||
--- @field server_info lsp.ServerInfo?
|
||||
---
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field settings lsp.LSPObject
|
||||
---
|
||||
--- See [vim.lsp.ClientConfig].
|
||||
--- @field workspace_folders lsp.WorkspaceFolder[]?
|
||||
---
|
||||
--- @field private _log_prefix string
|
||||
---
|
||||
--- Track this so that we can escalate automatically if we've already tried a
|
||||
--- graceful shutdown
|
||||
@ -206,26 +216,8 @@ local validate = vim.validate
|
||||
--- trace = "off" | "messages" | "verbose";
|
||||
--- @field private _trace 'off'|'messages'|'verbose'
|
||||
---
|
||||
--- Table of command name to function which is called if any LSP action
|
||||
--- (code action, code lenses, ...) triggers the command.
|
||||
--- Client commands take precedence over the global command registry.
|
||||
--- @field commands table<string,fun(command: lsp.Command, ctx: table)>
|
||||
---
|
||||
--- Map with language server specific settings. These are returned to the
|
||||
--- language server if requested via `workspace/configuration`. Keys are
|
||||
--- case-sensitive.
|
||||
--- @field settings lsp.LSPObject
|
||||
---
|
||||
--- A table with flags for the client. The current (experimental) flags are:
|
||||
--- @field flags vim.lsp.Client.Flags
|
||||
---
|
||||
--- @field get_language_id fun(bufnr: integer, filetype: string): string
|
||||
---
|
||||
--- The capabilities provided by the client (editor or tool)
|
||||
--- @field capabilities lsp.ClientCapabilities
|
||||
--- @field private registrations table<string,lsp.Registration[]>
|
||||
--- @field dynamic_capabilities lsp.DynamicCapabilities
|
||||
---
|
||||
--- @field private _log_prefix string
|
||||
--- @field private _before_init_cb? vim.lsp.client.before_init_cb
|
||||
--- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[]
|
||||
--- @field private _on_init_cbs vim.lsp.client.on_init_cb[]
|
||||
@ -395,6 +387,7 @@ function Client.create(config)
|
||||
capabilities = config.capabilities,
|
||||
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
|
||||
root_dir = config.root_dir,
|
||||
_is_stopping = false,
|
||||
_before_init_cb = config.before_init,
|
||||
_on_init_cbs = vim._ensure_list(config.on_init),
|
||||
_on_exit_cbs = vim._ensure_list(config.on_exit),
|
||||
@ -678,6 +671,12 @@ function Client:request(method, params, handler, bufnr)
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
local version = lsp.util.buf_versions[bufnr]
|
||||
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
|
||||
|
||||
-- Detect if request resolved synchronously (only possible with in-process servers).
|
||||
local already_responded = false
|
||||
local request_registered = false
|
||||
|
||||
-- NOTE: rpc.request might call an in-process (Lua) server, thus may be synchronous.
|
||||
local success, request_id = self.rpc.request(method, params, function(err, result)
|
||||
handler(err, result, {
|
||||
method = method,
|
||||
@ -688,11 +687,15 @@ function Client:request(method, params, handler, bufnr)
|
||||
})
|
||||
end, function(request_id)
|
||||
-- Called when the server sends a response to the request (including cancelled acknowledgment).
|
||||
self:_process_request(request_id, 'complete')
|
||||
if request_registered then
|
||||
self:_process_request(request_id, 'complete')
|
||||
end
|
||||
already_responded = true
|
||||
end)
|
||||
|
||||
if success and request_id then
|
||||
if success and request_id and not already_responded then
|
||||
self:_process_request(request_id, 'pending', bufnr, method)
|
||||
request_registered = true
|
||||
end
|
||||
|
||||
return success, request_id
|
||||
@ -802,12 +805,13 @@ end
|
||||
---
|
||||
--- @param force? boolean
|
||||
function Client:stop(force)
|
||||
local rpc = self.rpc
|
||||
|
||||
if rpc.is_closing() then
|
||||
if self:is_stopped() then
|
||||
return
|
||||
end
|
||||
|
||||
self._is_stopping = true
|
||||
local rpc = self.rpc
|
||||
|
||||
vim.lsp._watchfiles.cancel(self.id)
|
||||
|
||||
if force or not self.initialized or self._graceful_shutdown_failed then
|
||||
@ -933,7 +937,7 @@ end
|
||||
--- @return boolean # true if client is stopped or in the process of being
|
||||
--- stopped; false otherwise
|
||||
function Client:is_stopped()
|
||||
return self.rpc.is_closing()
|
||||
return self.rpc.is_closing() or self._is_stopping
|
||||
end
|
||||
|
||||
--- Execute a lsp command, either via client command function (if available)
|
||||
|
@ -370,7 +370,7 @@ end
|
||||
local function adjust_start_col(lnum, line, items, encoding)
|
||||
local min_start_char = nil
|
||||
for _, item in pairs(items) do
|
||||
if item.textEdit and item.textEdit.range.start.line == lnum then
|
||||
if item.textEdit and item.textEdit.range and item.textEdit.range.start.line == lnum then
|
||||
if min_start_char and min_start_char ~= item.textEdit.range.start.character then
|
||||
return nil
|
||||
end
|
||||
@ -506,14 +506,19 @@ local function trigger(bufnr, clients, ctx)
|
||||
local matches = {}
|
||||
local server_start_boundary --- @type integer?
|
||||
for client_id, response in pairs(responses) do
|
||||
local client = lsp.get_client_by_id(client_id)
|
||||
if response.err then
|
||||
vim.notify_once(response.err.message, vim.log.levels.WARN)
|
||||
local msg = ('%s: %s %s'):format(
|
||||
client and client.name or 'UNKNOWN',
|
||||
response.err.code or 'NO_CODE',
|
||||
response.err.message
|
||||
)
|
||||
vim.notify_once(msg, vim.log.levels.WARN)
|
||||
end
|
||||
|
||||
local result = response.result
|
||||
if result then
|
||||
Context.isIncomplete = Context.isIncomplete or result.isIncomplete
|
||||
local client = lsp.get_client_by_id(client_id)
|
||||
local encoding = client and client.offset_encoding or 'utf-16'
|
||||
local client_matches
|
||||
client_matches, server_start_boundary = M._convert_results(
|
||||
|
@ -93,12 +93,16 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
|
||||
message = diagnostic.message.value
|
||||
end
|
||||
local line = buf_lines and buf_lines[start.line + 1] or ''
|
||||
local end_line = line
|
||||
if _end.line > start.line then
|
||||
end_line = buf_lines and buf_lines[_end.line + 1] or ''
|
||||
end
|
||||
--- @type vim.Diagnostic
|
||||
return {
|
||||
lnum = start.line,
|
||||
col = vim.str_byteindex(line, position_encoding, start.character, false),
|
||||
end_lnum = _end.line,
|
||||
end_col = vim.str_byteindex(line, position_encoding, _end.character, false),
|
||||
end_col = vim.str_byteindex(end_line, position_encoding, _end.character, false),
|
||||
severity = severity_lsp_to_vim(diagnostic.severity),
|
||||
message = message,
|
||||
source = diagnostic.source,
|
||||
|
@ -1,4 +1,25 @@
|
||||
-- Logger for language client plugin.
|
||||
--- @brief
|
||||
--- The `vim.lsp.log` module provides logging for the Nvim LSP client.
|
||||
---
|
||||
--- When debugging language servers, it is helpful to enable extra-verbose logging of the LSP client
|
||||
--- RPC events. Example:
|
||||
--- ```lua
|
||||
--- vim.lsp.set_log_level 'trace'
|
||||
--- require('vim.lsp.log').set_format_func(vim.inspect)
|
||||
--- ```
|
||||
---
|
||||
--- Then try to run the language server, and open the log with:
|
||||
--- ```vim
|
||||
--- :lua vim.cmd('tabnew ' .. vim.lsp.get_log_path())
|
||||
--- ```
|
||||
---
|
||||
--- (Or use `:LspLog` if you have nvim-lspconfig installed.)
|
||||
---
|
||||
--- Note:
|
||||
--- - Remember to DISABLE verbose logging ("debug" or "trace" level), else you may encounter
|
||||
--- performance issues.
|
||||
--- - "ERROR" messages containing "stderr" only indicate that the log was sent to stderr. Many
|
||||
--- servers send harmless messages via stderr.
|
||||
|
||||
local log = {}
|
||||
|
||||
|
@ -1793,7 +1793,7 @@ function M.symbols_to_items(symbols, bufnr, position_encoding)
|
||||
'symbols_to_items must be called with valid position encoding',
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
position_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding
|
||||
position_encoding = vim.lsp.get_clients({ bufnr = bufnr })[1].offset_encoding
|
||||
end
|
||||
|
||||
local items = {} --- @type vim.quickfix.entry[]
|
||||
|
@ -21,6 +21,50 @@ local function read_trust()
|
||||
return trust
|
||||
end
|
||||
|
||||
--- If {fullpath} is a file, read the contents of {fullpath} (or the contents of {bufnr}
|
||||
--- if given) and returns the contents and a hash of the contents.
|
||||
---
|
||||
--- If {fullpath} is a directory, then nothing is read from the filesystem, and
|
||||
--- `contents = true` and `hash = "directory"` is returned instead.
|
||||
---
|
||||
---@param fullpath (string) Path to a file or directory to read.
|
||||
---@param bufnr (number?) The number of the buffer.
|
||||
---@return string|boolean? contents the contents of the file, or true if it's a directory
|
||||
---@return string? hash the hash of the contents, or "directory" if it's a directory
|
||||
local function compute_hash(fullpath, bufnr)
|
||||
local contents ---@type string|boolean?
|
||||
local hash ---@type string
|
||||
if vim.fn.isdirectory(fullpath) == 1 then
|
||||
return true, 'directory'
|
||||
end
|
||||
|
||||
if bufnr then
|
||||
local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n'
|
||||
contents =
|
||||
table.concat(vim.api.nvim_buf_get_lines(bufnr --[[@as integer]], 0, -1, false), newline)
|
||||
if vim.bo[bufnr].endofline then
|
||||
contents = contents .. newline
|
||||
end
|
||||
else
|
||||
do
|
||||
local f = io.open(fullpath, 'r')
|
||||
if not f then
|
||||
return nil, nil
|
||||
end
|
||||
contents = f:read('*a')
|
||||
f:close()
|
||||
end
|
||||
|
||||
if not contents then
|
||||
return nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
hash = vim.fn.sha256(contents)
|
||||
|
||||
return contents, hash
|
||||
end
|
||||
|
||||
--- Writes provided {trust} table to trust database at
|
||||
--- $XDG_STATE_HOME/nvim/trust.
|
||||
---
|
||||
@ -37,17 +81,22 @@ local function write_trust(trust)
|
||||
f:close()
|
||||
end
|
||||
|
||||
--- Attempt to read the file at {path} prompting the user if the file should be
|
||||
--- trusted. The user's choice is persisted in a trust database at
|
||||
--- If {path} is a file: attempt to read the file, prompting the user if the file should be
|
||||
--- trusted.
|
||||
---
|
||||
--- If {path} is a directory: return true if the directory is trusted (non-recursive), prompting
|
||||
--- the user as necessary.
|
||||
---
|
||||
--- The user's choice is persisted in a trust database at
|
||||
--- $XDG_STATE_HOME/nvim/trust.
|
||||
---
|
||||
---@since 11
|
||||
---@see |:trust|
|
||||
---
|
||||
---@param path (string) Path to a file to read.
|
||||
---@param path (string) Path to a file or directory to read.
|
||||
---
|
||||
---@return (string|nil) The contents of the given file if it exists and is
|
||||
--- trusted, or nil otherwise.
|
||||
---@return (boolean|string|nil) If {path} is not trusted or does not exist, returns `nil`. Otherwise,
|
||||
--- returns the contents of {path} if it is a file, or true if {path} is a directory.
|
||||
function M.read(path)
|
||||
vim.validate('path', path, 'string')
|
||||
local fullpath = vim.uv.fs_realpath(vim.fs.normalize(path))
|
||||
@ -62,26 +111,25 @@ function M.read(path)
|
||||
return nil
|
||||
end
|
||||
|
||||
local contents ---@type string?
|
||||
do
|
||||
local f = io.open(fullpath, 'r')
|
||||
if not f then
|
||||
return nil
|
||||
end
|
||||
contents = f:read('*a')
|
||||
f:close()
|
||||
local contents, hash = compute_hash(fullpath, nil)
|
||||
if not contents then
|
||||
return nil
|
||||
end
|
||||
|
||||
local hash = vim.fn.sha256(contents)
|
||||
if trust[fullpath] == hash then
|
||||
-- File already exists in trust database
|
||||
return contents
|
||||
end
|
||||
|
||||
local dir_msg = ''
|
||||
if hash == 'directory' then
|
||||
dir_msg = ' DIRECTORY trust is decided only by its name, not its contents.'
|
||||
end
|
||||
|
||||
-- File either does not exist in trust database or the hash does not match
|
||||
local ok, result = pcall(
|
||||
vim.fn.confirm,
|
||||
string.format('%s is not trusted.', fullpath),
|
||||
string.format('%s is not trusted.%s', fullpath, dir_msg),
|
||||
'&ignore\n&view\n&deny\n&allow',
|
||||
1
|
||||
)
|
||||
@ -169,13 +217,10 @@ function M.trust(opts)
|
||||
local trust = read_trust()
|
||||
|
||||
if action == 'allow' then
|
||||
local newline = vim.bo[bufnr].fileformat == 'unix' and '\n' or '\r\n'
|
||||
local contents =
|
||||
table.concat(vim.api.nvim_buf_get_lines(bufnr --[[@as integer]], 0, -1, false), newline)
|
||||
if vim.bo[bufnr].endofline then
|
||||
contents = contents .. newline
|
||||
local contents, hash = compute_hash(fullpath, bufnr)
|
||||
if not contents then
|
||||
return false, string.format('could not read path: %s', fullpath)
|
||||
end
|
||||
local hash = vim.fn.sha256(contents)
|
||||
|
||||
trust[fullpath] = hash
|
||||
elseif action == 'deny' then
|
||||
|
@ -95,7 +95,7 @@ end
|
||||
---
|
||||
--- @see |string.gmatch()|
|
||||
--- @see |vim.split()|
|
||||
--- @see |lua-patterns|
|
||||
--- @see |lua-pattern|s
|
||||
--- @see https://www.lua.org/pil/20.2.html
|
||||
--- @see http://lua-users.org/wiki/StringLibraryTutorial
|
||||
---
|
||||
@ -784,7 +784,7 @@ end
|
||||
|
||||
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
|
||||
---
|
||||
---@see |lua-patterns|
|
||||
---@see |lua-pattern|s
|
||||
---@see https://www.lua.org/pil/20.2.html
|
||||
---@param s string String to trim
|
||||
---@return string String with whitespace removed from its beginning and end
|
||||
@ -793,7 +793,7 @@ function vim.trim(s)
|
||||
return s:match('^%s*(.*%S)') or ''
|
||||
end
|
||||
|
||||
--- Escapes magic chars in |lua-patterns|.
|
||||
--- Escapes magic chars in |lua-pattern|s.
|
||||
---
|
||||
---@see https://github.com/rxi/lume
|
||||
---@param s string String to escape
|
||||
@ -854,7 +854,7 @@ do
|
||||
--- @param param_name string
|
||||
--- @param val any
|
||||
--- @param validator vim.validate.Validator
|
||||
--- @param message? string
|
||||
--- @param message? string "Expected" message
|
||||
--- @param allow_alias? boolean Allow short type names: 'n', 's', 't', 'b', 'f', 'c'
|
||||
--- @return string?
|
||||
local function is_valid(param_name, val, validator, message, allow_alias)
|
||||
@ -866,18 +866,18 @@ do
|
||||
end
|
||||
|
||||
if not is_type(val, expected) then
|
||||
return string.format('%s: expected %s, got %s', param_name, expected, type(val))
|
||||
return ('%s: expected %s, got %s'):format(param_name, message or expected, type(val))
|
||||
end
|
||||
elseif vim.is_callable(validator) then
|
||||
-- Check user-provided validation function
|
||||
local valid, opt_msg = validator(val)
|
||||
if not valid then
|
||||
local err_msg =
|
||||
string.format('%s: expected %s, got %s', param_name, message or '?', tostring(val))
|
||||
|
||||
if opt_msg then
|
||||
err_msg = string.format('%s. Info: %s', err_msg, opt_msg)
|
||||
end
|
||||
local err_msg = ('%s: expected %s, got %s'):format(
|
||||
param_name,
|
||||
message or '?',
|
||||
tostring(val)
|
||||
)
|
||||
err_msg = opt_msg and ('%s. Info: %s'):format(err_msg, opt_msg) or err_msg
|
||||
|
||||
return err_msg
|
||||
end
|
||||
|
@ -86,6 +86,7 @@ end
|
||||
--- Shows an Outline (table of contents) of the current buffer, in the loclist.
|
||||
function M.show_toc()
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local bufname = api.nvim_buf_get_name(bufnr)
|
||||
local headings = get_headings(bufnr)
|
||||
if #headings == 0 then
|
||||
return
|
||||
@ -102,6 +103,9 @@ function M.show_toc()
|
||||
vim.fn.setloclist(0, headings, ' ')
|
||||
vim.fn.setloclist(0, {}, 'a', { title = 'Table of contents' })
|
||||
vim.cmd.lopen()
|
||||
vim.w.qf_toc = bufname
|
||||
-- reload syntax file after setting qf_toc variable
|
||||
vim.bo.filetype = 'qf'
|
||||
end
|
||||
|
||||
--- Jump to section
|
||||
|
@ -18,11 +18,14 @@ error('Cannot require a meta file')
|
||||
---@field captures string[]
|
||||
---@field patterns table<integer, (integer|string)[][]>
|
||||
---
|
||||
---@class TSLangMetadata
|
||||
---@field major_version integer
|
||||
---@field minor_version integer
|
||||
---@field patch_version integer
|
||||
---
|
||||
---@class TSLangInfo
|
||||
---@field abi_version integer
|
||||
---@field major_version? integer
|
||||
---@field minor_version? integer
|
||||
---@field patch_version? integer
|
||||
---@field metadata? TSLangMetadata -- ABI 15 only
|
||||
---@field state_count integer
|
||||
---@field fields string[]
|
||||
---@field symbols table<string,boolean>
|
||||
|
@ -451,6 +451,8 @@ function M.inspect_tree(opts)
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_buf_set_keymap(b, 'n', 'q', '<C-w>c', { desc = 'Close language tree window' })
|
||||
|
||||
local group = api.nvim_create_augroup('nvim.treesitter.dev', {})
|
||||
|
||||
api.nvim_create_autocmd('CursorMoved', {
|
||||
|
@ -131,6 +131,7 @@ function TSHighlighter.new(tree, opts)
|
||||
self.redraw_count = 0
|
||||
self._conceal_checked = {}
|
||||
self._queries = {}
|
||||
self._highlight_states = {}
|
||||
|
||||
-- Queries for a specific language can be overridden by a custom
|
||||
-- string query... if one is not provided it will be looked up by file.
|
||||
@ -380,6 +381,7 @@ local function on_line_impl(self, buf, line, on_spell, on_conceal)
|
||||
api.nvim_buf_set_extmark(buf, ns, start_row, 0, {
|
||||
end_line = end_row,
|
||||
conceal_lines = '',
|
||||
invalidate = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
@ -459,19 +461,37 @@ end
|
||||
---@param buf integer
|
||||
---@param topline integer
|
||||
---@param botline integer
|
||||
function TSHighlighter._on_win(_, _, buf, topline, botline)
|
||||
function TSHighlighter._on_win(_, win, buf, topline, botline)
|
||||
local self = TSHighlighter.active[buf]
|
||||
if not self or self.parsing then
|
||||
if not self then
|
||||
return false
|
||||
end
|
||||
self.parsing = self.tree:parse({ topline, botline + 1 }, function(_, trees)
|
||||
if trees and self.parsing then
|
||||
self.parsing = false
|
||||
api.nvim__redraw({ buf = buf, valid = false, flush = false })
|
||||
end
|
||||
end) == nil
|
||||
self.redraw_count = self.redraw_count + 1
|
||||
self:prepare_highlight_states(topline, botline)
|
||||
self.parsing = self.parsing
|
||||
or nil
|
||||
== self.tree:parse({ topline, botline + 1 }, function(_, trees)
|
||||
if trees and self.parsing then
|
||||
self.parsing = false
|
||||
api.nvim__redraw({ win = win, valid = false, flush = false })
|
||||
end
|
||||
end)
|
||||
if not self.parsing then
|
||||
self.redraw_count = self.redraw_count + 1
|
||||
self:prepare_highlight_states(topline, botline)
|
||||
else
|
||||
self:for_each_highlight_state(function(state)
|
||||
-- TODO(ribru17): Inefficient. Eventually all marks should be applied in on_buf, and all
|
||||
-- non-folded ranges of each open window should be merged, and iterators should only be
|
||||
-- created over those regions. This would also fix #31777.
|
||||
--
|
||||
-- Currently this is not possible because the parser discards previously parsed injection
|
||||
-- trees upon parsing a different region.
|
||||
--
|
||||
-- It would also be nice if rather than re-querying extmarks for old trees, we could tell the
|
||||
-- decoration provider to not clear previous ephemeral marks for this redraw cycle.
|
||||
state.iter = nil
|
||||
state.next_row = 0
|
||||
end)
|
||||
end
|
||||
return #self._highlight_states > 0
|
||||
end
|
||||
|
||||
|
@ -1268,12 +1268,13 @@ end
|
||||
local function tree_contains(tree, range)
|
||||
local tree_ranges = tree:included_ranges(false)
|
||||
|
||||
return Range.contains({
|
||||
tree_ranges[1][1],
|
||||
tree_ranges[1][2],
|
||||
tree_ranges[#tree_ranges][3],
|
||||
tree_ranges[#tree_ranges][4],
|
||||
}, range)
|
||||
for _, tree_range in ipairs(tree_ranges) do
|
||||
if Range.contains(tree_range, range) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Determines whether {range} is contained in the |LanguageTree|.
|
||||
|
@ -26,6 +26,7 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release date="2025-05-30" version="0.11.2"/>
|
||||
<release date="2025-04-26" version="0.11.1"/>
|
||||
<release date="2025-03-26" version="0.11.0"/>
|
||||
<release date="2025-01-29" version="0.10.4"/>
|
||||
|
@ -78,7 +78,7 @@ Comment[wa]=Asspougnî des fitchîs tecses
|
||||
Comment[zh_CN]=编辑文本文件
|
||||
Comment[zh_TW]=編輯文字檔
|
||||
TryExec=nvim
|
||||
Exec=nvim "%F"
|
||||
Exec=nvim %F
|
||||
Terminal=true
|
||||
Type=Application
|
||||
Keywords=Text;editor;
|
||||
|
@ -1,3 +1,9 @@
|
||||
" Tutor: New Style Tutor Plugin :h vim-tutor-mode
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Contributors: Phạm Bình An <phambinhanctb2004@gmail.com>
|
||||
" Original Author: Felipe Morales <hel.sheep@gmail.com>
|
||||
" Date: 2025 May 12
|
||||
|
||||
if exists('g:loaded_tutor_mode_plugin') || &compatible
|
||||
finish
|
||||
endif
|
||||
|
@ -464,7 +464,7 @@ Now go on to the next lesson.
|
||||
# Lesson 4.1: CURSOR LOCATION AND FILE STATUS
|
||||
|
||||
** Type `<C-g>`{normal} to show your location in a file and the file status.
|
||||
Type `G`{normal} to move to a line in the file. **
|
||||
Type `{count}G`{normal} to move to line {count} in the file. **
|
||||
|
||||
NOTE: Read the entire lesson before executing any of these steps!!
|
||||
|
||||
@ -905,26 +905,7 @@ You can find help on just about any subject, by giving an argument to the
|
||||
:help insert-index
|
||||
:help user-manual
|
||||
~~~
|
||||
# Lesson 7.2: CREATE A STARTUP SCRIPT
|
||||
|
||||
** Enable Neovim features. **
|
||||
|
||||
Neovim is a very configurable editor. You can customise it any way you like.
|
||||
To start using more features create an "init.vim" file.
|
||||
|
||||
1. Start editing the "init.vim" file.
|
||||
|
||||
`:call mkdir(stdpath('config'),'p')`{vim}
|
||||
`:exe 'edit' stdpath('config').'/init.vim'`{vim}
|
||||
|
||||
2. Write the file with:
|
||||
|
||||
`:w`{vim}
|
||||
|
||||
You can add all your preferred settings to this "init.vim" file.
|
||||
For more information type `:help init.vim`{vim}.
|
||||
|
||||
# Lesson 7.3: COMPLETION
|
||||
# Lesson 7.2: COMPLETION
|
||||
|
||||
** Command line completion with `<C-d>`{normal} and `<Tab>`{normal}. **
|
||||
|
||||
@ -934,14 +915,46 @@ For more information type `:help init.vim`{vim}.
|
||||
|
||||
3. Press `<C-d>`{normal} and Neovim will show a list of commands beginning with "e".
|
||||
|
||||
4. Press `<Tab>`{normal} and Neovim will complete the command name to ":edit".
|
||||
4. Press `<Tab>`{normal} and Neovim will show a menu with possible completions
|
||||
(or complete the match, if the entered command is unique, e.g.
|
||||
":ed`<Tab>`{normal}" will be completed to ":edit").
|
||||
|
||||
5. Now add a space and the start of an existing file name: `:edit FIL`{vim}
|
||||
5. Use `<Tab>`{normal} or `<C-n>`{normal} to go to the next match. Or use
|
||||
`<S-Tab>`{normal} or `<C-p>`{normal} to go to the previous match.
|
||||
|
||||
6. Press `<Tab>`{normal}. Neovim will complete the name ("FIL" -> "FILE", if it is unique).
|
||||
6. Choose the entry `edit`{vim}. Now you can see that the word `edit`{vim}
|
||||
have been automatically inserted to the command line.
|
||||
|
||||
7. Now add a space and the start of an existing file name: `:edit FIL`{vim}
|
||||
|
||||
8. Press `<Tab>`{normal}. Vim will show a completion menu with list of file
|
||||
names that start with `FIL`
|
||||
|
||||
NOTE: Completion works for many commands. It is especially useful for `:help`{vim}.
|
||||
|
||||
# Lesson 7.3: CONFIGURING NVIM
|
||||
|
||||
Neovim is a very configurable editor. You can customise it any way you like. To
|
||||
start using more features, create a vimrc file, which can be "init.lua" if you
|
||||
want to use Lua, or "init.vim" if you want to use Vimscript. We'll use
|
||||
"init.lua" in this lesson.
|
||||
|
||||
1. Start editing the "init.lua" file.
|
||||
|
||||
`:exe 'edit' stdpath('config')..'/init.lua'`{vim}
|
||||
|
||||
2. Copy the example configuration in Lua to your "init.lua" file.
|
||||
|
||||
`:read $VIMRUNTIME/example_init.lua`{vim}
|
||||
|
||||
3. Write the file (also creates any missing parent directories):
|
||||
|
||||
`:w ++p`{vim}
|
||||
|
||||
4. Next time you start Neovim, you can quickly open this vimrc file with:
|
||||
|
||||
`:e $MYVIMRC`{vim}
|
||||
|
||||
# Lesson 7 SUMMARY
|
||||
|
||||
1. Type `:help`{vim}
|
||||
@ -953,10 +966,15 @@ NOTE: Completion works for many commands. It is especially useful for `:help`{vi
|
||||
|
||||
4. Type `:q`{vim} to close the help window
|
||||
|
||||
5. Create an init.vim startup script to keep your preferred settings.
|
||||
5. While in command mode, press `<C-d>`{normal} to see possible completions.
|
||||
Press `<Tab>`{normal} to use the completion menu and select a match.
|
||||
|
||||
6. While in command mode, press `<C-d>`{normal} to see possible completions.
|
||||
Press `<Tab>`{normal} to use one completion.
|
||||
6. Create your configuration file to save your preferred settings. You can
|
||||
revisit it with `:e $MYVIMRC`{vim}.
|
||||
|
||||
# What's next?
|
||||
|
||||
Run `:help nvim-quickstart`{vim} for more information on extending Nvim.
|
||||
|
||||
# CONCLUSION
|
||||
|
||||
|
@ -31,7 +31,7 @@ NOTE: 以下の練習用コマンドにはこの文章を変更するものも
|
||||
また、次のようにコマンドを実行するよう求められることや、(後で詳しく説明します。)
|
||||
|
||||
`:help`{vim} `<Enter>`{normal}
|
||||
|
||||
|
||||
キーシークエンスを押すこともあります。
|
||||
~~~ normal
|
||||
<Esc>0f<Space>d3wP$P
|
||||
@ -465,8 +465,8 @@ NOTE: タイプ中の間違いはバックスペースキーを使って直す
|
||||
|
||||
# レッスン 4.1: 位置とファイルの情報
|
||||
|
||||
** ファイル内での位置とファイルの状態を表示するには `<C-g>`{normal} をタイプします。ファイ
|
||||
ル内のある行に移動するには `G`{normal} をタイプします。 **
|
||||
** ファイル内での位置とファイルの状態を表示するには `<C-g>`{normal} をタイプします。
|
||||
ファイル内の{count}行に移動するには `{count}G`{normal} をタイプします。 **
|
||||
|
||||
NOTE: ステップを実行する前に、このレッスン全てに目を通しましょう!!
|
||||
|
||||
@ -875,7 +875,7 @@ NOTE: 1つの検索コマンドだけ大文字小文字の区別をやめたい
|
||||
~~~
|
||||
# レッスン 7.1: ヘルプコマンド
|
||||
|
||||
** Use the online help system. **
|
||||
** オンラインヘルプシステムを使用する **
|
||||
|
||||
Neovim には広範にわたるオンラインヘルプシステムがあります。
|
||||
|
||||
@ -895,27 +895,7 @@ Neovim には広範にわたるオンラインヘルプシステムがありま
|
||||
:help insert-index
|
||||
:help user-manual
|
||||
~~~
|
||||
|
||||
# レッスン 7.2: 起動スクリプトの作成
|
||||
|
||||
** Neovim の特徴を発揮する **
|
||||
|
||||
Neovim はとても自由度の高いエディタです。あなたの好きなようにカスタマイズするこ
|
||||
とができます。より多くの機能を使いはじめるには "init.vim" ファイルを作成します。
|
||||
|
||||
1. "init.vim" ファイルの編集を開始します。
|
||||
|
||||
`:call mkdir(stdpath('config'),'p')`{vim}
|
||||
`:exe 'edit' stdpath('config').'/init.vim'`{vim}
|
||||
|
||||
2. 以下のようにしてファイルを保存します。
|
||||
|
||||
`:w`{vim}
|
||||
|
||||
この "init.vim" ファイルへ、お好みの設定を追加することができます。
|
||||
より多くの情報を得るには `:help init.vim`{vim} とタイプします。
|
||||
|
||||
# レッスン 7.3: 補完
|
||||
# レッスン 7.2: 補完
|
||||
|
||||
** `<C-d>`{normal} と `<Tab>`{normal} でコマンドラインを補完する **
|
||||
|
||||
@ -925,14 +905,45 @@ Neovim はとても自由度の高いエディタです。あなたの好きな
|
||||
|
||||
3. `<C-d>`{normal} を押すと Neovim は "e" から始まるコマンドの一覧を表示します。
|
||||
|
||||
4. `<Tab>`{normal} とタイプすると Neoim は ":edit" というコマンド名を補完します。
|
||||
4. `<Tab>`{normal} を押すと、Neovim は可能な補完候補のメニューを表示します
|
||||
(または、ただの一つのマッチがあるときは、補完します。
|
||||
例: ":ed`<Tab>`{normal}" は":edit"に補完されます)。
|
||||
|
||||
5. さらに空白と、既存のファイル名の始まりを加えます: `:edit FIL`{vim}
|
||||
5. 次のマッチに移動するには `<Tab>`{normal} または `<C-n>`{normal}を使用し、
|
||||
前のマッチに移動するには `<S-Tab>`{normal} または `<C-p>`{normal}を使用します。
|
||||
|
||||
6. `<Tab>`{normal} を押すと Neovim は名前を補完します。("FIL" -> "FILE"、重複しない場合)
|
||||
6. `edit`{vim} を選択すると、コマンドラインに`edit` が挿入されます。
|
||||
|
||||
7. さらに空白と、既存のファイル名の始まりを加えます: `:edit FIL`{vim}
|
||||
|
||||
8. `<Tab>`{normal}を押すと、Vim は `FIL` で始まるファイル名の候補リストを含む補完メニューを
|
||||
表示します。
|
||||
|
||||
NOTE: 補完は多くのコマンドで動作します。特に `:help`{vim} の際に役立ちます。
|
||||
|
||||
# レッスン 7.3: 設定ファイルの作成
|
||||
|
||||
Neovim はとても自由度の高いエディタです。あなたの好きなようにカスタマイズするこ
|
||||
とができます。より多くの機能を使い始めるには設定ファイルを作成します。 Lua を使
|
||||
いたい場合は "init.lua" にし、Vimscript を使いたい場合は "init.vim" にします。
|
||||
このレッスンでは "init.lua" を使います。
|
||||
|
||||
1. `"init.lua"` ファイルを編集します。
|
||||
|
||||
`:exe 'edit' stdpath('config')..'/init.lua'`{vim}
|
||||
|
||||
2. Lua の例の設定を "init.lua" にコピーします。
|
||||
|
||||
`:read $VIMRUNTIME/example_init.lua`{vim}
|
||||
|
||||
3. ファイルを書き込みます(必要に応じて親ディレクトリも作成されます):
|
||||
|
||||
`:w ++p`{vim}
|
||||
|
||||
4. 次回 Neovim を起動したときに、以下のコマンドでこの設定ファイルを開けます:
|
||||
|
||||
`:e $MYVIMRC`{vim}
|
||||
|
||||
# レッスン 7 要約
|
||||
|
||||
1. ヘルプウィンドウを開くには `:help`{vim} とするか `<F1>`{normal} を押す。
|
||||
@ -943,11 +954,12 @@ NOTE: 補完は多くのコマンドで動作します。特に `:help`{vim} の
|
||||
|
||||
4. ヘルプウィンドウを閉じるには `:q`{vim} とタイプする。
|
||||
|
||||
5. お好みの設定を保つには init.vim 起動スクリプトを作成する。
|
||||
|
||||
6. : command で可能な補完を見るには `<C-d>`{normal} をタイプする。
|
||||
5. コマンドラインモードで可能な補完を見るには `<C-d>`{normal} をタイプする。
|
||||
補完を使用するには `<Tab>`{normal} を押す。
|
||||
|
||||
6. お好みの設定を保つには設定ファイルを作成する。作成した設定ファイルは
|
||||
`:e $MYVIMRC`{vim} でいつでも開き直せます。
|
||||
|
||||
# おわりに
|
||||
|
||||
これにて Neovim のチュートリアルを終わります。エディタを簡単に、しかも充分に使う
|
||||
|
@ -17,15 +17,8 @@ Table of contents:
|
||||
|
||||
## SETTING UP *setting-up*
|
||||
|
||||
First, you'll need to enable "debug" mode
|
||||
~~~ cmd
|
||||
:let g:tutor_debug = 1
|
||||
~~~
|
||||
This will allow saving changes to the tutor files and will disable conceals, so
|
||||
you can more easily check your changes.
|
||||
|
||||
After this, create a new .tutor file (we will be practicing on this very file, so you
|
||||
don't need to do this now):
|
||||
Create a new .tutor file (we will be practicing on this very file, so you don't
|
||||
need to do this now):
|
||||
~~~ cmd
|
||||
:e new-tutorial.tutor
|
||||
~~~
|
||||
|
@ -1268,9 +1268,10 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
|
||||
{
|
||||
win_T *win;
|
||||
bool need_append = true; // Append `aucmd_win` to the window list.
|
||||
const bool same_buffer = buf == curbuf;
|
||||
|
||||
// Find a window that is for the new buffer
|
||||
if (buf == curbuf) { // be quick when buf is curbuf
|
||||
if (same_buffer) { // be quick when buf is curbuf
|
||||
win = curwin;
|
||||
} else {
|
||||
win = NULL;
|
||||
@ -1360,9 +1361,11 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
|
||||
aco->new_curwin_handle = curwin->handle;
|
||||
set_bufref(&aco->new_curbuf, curbuf);
|
||||
|
||||
// disable the Visual area, the position may be invalid in another buffer
|
||||
aco->save_VIsual_active = VIsual_active;
|
||||
VIsual_active = false;
|
||||
if (!same_buffer) {
|
||||
// disable the Visual area, position may be invalid in another buffer
|
||||
VIsual_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleanup after executing autocommands for a (hidden) buffer.
|
||||
@ -1635,11 +1638,12 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|
||||
// into "buf" are ignoring the event.
|
||||
if (buf == curbuf && event_names[event].event <= 0) {
|
||||
win_ignore = event_ignored(event, curwin->w_p_eiw);
|
||||
} else if (buf != NULL && event_names[event].event <= 0) {
|
||||
for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
|
||||
WinInfo *wip = kv_A(buf->b_wininfo, i);
|
||||
if (wip->wi_win != NULL && wip->wi_win->w_buffer == buf) {
|
||||
win_ignore = event_ignored(event, wip->wi_win->w_p_eiw);
|
||||
} else if (buf != NULL && event_names[event].event <= 0 && buf->b_nwindows > 0) {
|
||||
win_ignore = true;
|
||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||
if (wp->w_buffer == buf && !event_ignored(event, wp->w_p_eiw)) {
|
||||
win_ignore = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -480,11 +480,6 @@ static bool can_unload_buffer(buf_T *buf)
|
||||
return can_unload;
|
||||
}
|
||||
|
||||
bool buf_locked(buf_T *buf)
|
||||
{
|
||||
return buf->b_locked || buf->b_locked_split;
|
||||
}
|
||||
|
||||
/// Close the link to a buffer.
|
||||
///
|
||||
/// @param win If not NULL, set b_last_cursor.
|
||||
@ -1300,11 +1295,17 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (action == DOBUF_GOTO
|
||||
&& buf != curbuf
|
||||
&& !check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) ? true : false)) {
|
||||
// disallow navigating to another buffer when 'winfixbuf' is applied
|
||||
return FAIL;
|
||||
if (action == DOBUF_GOTO && buf != curbuf) {
|
||||
if (!check_can_set_curbuf_forceit((flags & DOBUF_FORCEIT) != 0)) {
|
||||
// disallow navigating to another buffer when 'winfixbuf' is applied
|
||||
return FAIL;
|
||||
}
|
||||
if (buf->b_locked_split) {
|
||||
// disallow navigating to a closing buffer, which like splitting,
|
||||
// can result in more windows displaying it
|
||||
emsg(_(e_cannot_switch_to_a_closing_buffer));
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
|
||||
@ -1772,6 +1773,10 @@ void enter_buffer(buf_T *buf)
|
||||
}
|
||||
curbuf->b_last_used = time(NULL);
|
||||
|
||||
if (curbuf->terminal != NULL) {
|
||||
terminal_check_size(curbuf->terminal);
|
||||
}
|
||||
|
||||
redraw_later(curwin, UPD_NOT_VALID);
|
||||
}
|
||||
|
||||
@ -1899,18 +1904,21 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
|
||||
// buffer.)
|
||||
buf = NULL;
|
||||
if ((flags & BLN_CURBUF) && curbuf_reusable()) {
|
||||
bufref_T bufref;
|
||||
|
||||
assert(curbuf != NULL);
|
||||
buf = curbuf;
|
||||
set_bufref(&bufref, buf);
|
||||
// It's like this buffer is deleted. Watch out for autocommands that
|
||||
// change curbuf! If that happens, allocate a new buffer anyway.
|
||||
buf_freeall(buf, BFA_WIPE | BFA_DEL);
|
||||
if (buf != curbuf) { // autocommands deleted the buffer!
|
||||
return NULL;
|
||||
}
|
||||
if (aborting()) { // autocmds may abort script processing
|
||||
xfree(ffname);
|
||||
return NULL;
|
||||
}
|
||||
if (!bufref_valid(&bufref)) {
|
||||
buf = NULL; // buf was deleted; allocate a new buffer
|
||||
}
|
||||
}
|
||||
if (buf != curbuf || curbuf == NULL) {
|
||||
buf = xcalloc(1, sizeof(buf_T));
|
||||
|
@ -366,7 +366,7 @@ struct file_buffer {
|
||||
int b_locked; // Buffer is being closed or referenced, don't
|
||||
// let autocommands wipe it out.
|
||||
int b_locked_split; // Buffer is being closed, don't allow opening
|
||||
// a new window with it.
|
||||
// it in more windows.
|
||||
int b_ro_locked; // Non-zero when the buffer can't be changed.
|
||||
// Used for FileChangedRO
|
||||
|
||||
|
@ -493,7 +493,7 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
|
||||
return true; // TODO(bfredl): check if available in the region
|
||||
}
|
||||
|
||||
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
static void decor_state_pack(DecorState *state)
|
||||
{
|
||||
int count = (int)kv_size(state->ranges_i);
|
||||
int const cur_end = state->current_end;
|
||||
@ -513,6 +513,11 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
|
||||
kv_size(state->ranges_i) = (size_t)count;
|
||||
state->future_begin = fut_beg;
|
||||
}
|
||||
|
||||
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
{
|
||||
decor_state_pack(state);
|
||||
|
||||
if (state->row == -1) {
|
||||
decor_redraw_start(wp, row, state);
|
||||
@ -525,7 +530,7 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
state->col_until = -1;
|
||||
state->eol_col = -1;
|
||||
|
||||
if (cur_end != 0 || fut_beg != count) {
|
||||
if (state->current_end != 0 || state->future_begin != (int)kv_size(state->ranges_i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1447,7 +1447,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
? (startrow == 0 ? wp->w_skipcol : 0)
|
||||
: wp->w_leftcol;
|
||||
|
||||
if (start_col > 0 && col_rows == 0) {
|
||||
if (has_foldtext) {
|
||||
wlv.vcol = start_col;
|
||||
} else if (start_col > 0 && col_rows == 0) {
|
||||
char *prev_ptr = ptr;
|
||||
CharSize cs = { 0 };
|
||||
|
||||
@ -1490,12 +1492,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// - 'cuc' is set, or
|
||||
// - 'colorcolumn' is set, or
|
||||
// - 'virtualedit' is set, or
|
||||
// - the visual mode is active,
|
||||
// - the visual mode is active, or
|
||||
// - drawing a fold
|
||||
// the end of the line may be before the start of the displayed part.
|
||||
if (wlv.vcol < start_col && (wp->w_p_cuc
|
||||
|| wlv.color_cols
|
||||
|| virtual_active(wp)
|
||||
|| (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
|
||||
|| (VIsual_active && wp->w_buffer == curwin->w_buffer)
|
||||
|| has_fold)) {
|
||||
wlv.vcol = start_col;
|
||||
}
|
||||
|
||||
@ -1899,6 +1903,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
wlv.n_extra = (int)strlen(wlv.p_extra);
|
||||
|
||||
if (wlv.p_extra != buf_fold) {
|
||||
assert(foldtext_free == NULL);
|
||||
foldtext_free = wlv.p_extra;
|
||||
}
|
||||
wlv.sc_extra = NUL;
|
||||
@ -1910,11 +1915,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
ptr = line + v;
|
||||
}
|
||||
|
||||
if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols && (has_foldtext || *ptr == NUL)) {
|
||||
// Draw 'fold' fillchar after 'foldtext', or after 'eol' listchar for transparent 'foldtext'.
|
||||
if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols
|
||||
&& (has_foldtext || (*ptr == NUL && (!wp->w_p_list || !lcs_eol_todo || lcs_eol == NUL)))) {
|
||||
// Fill rest of line with 'fold'.
|
||||
wlv.sc_extra = wp->w_p_fcs_chars.fold;
|
||||
wlv.sc_final = NUL;
|
||||
wlv.n_extra = grid->cols - wlv.col;
|
||||
// Don't continue search highlighting past the first filler char.
|
||||
search_attr = 0;
|
||||
}
|
||||
|
||||
if (draw_folded && wlv.n_extra != 0 && wlv.col >= grid->cols) {
|
||||
@ -2671,7 +2680,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// Add a blank character to highlight.
|
||||
linebuf_char[wlv.off] = schar_from_ascii(' ');
|
||||
}
|
||||
if (area_attr == 0 && !has_foldtext) {
|
||||
if (area_attr == 0 && !has_fold) {
|
||||
// Use attributes from match with highest priority among
|
||||
// 'search_hl' and the match list.
|
||||
get_search_match_hl(wp,
|
||||
@ -2789,7 +2798,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
curwin->w_cline_height = wlv.row - startrow;
|
||||
curwin->w_cline_folded = has_fold;
|
||||
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
|
||||
conceal_cursor_used = conceal_cursor_line(curwin);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -19,8 +19,6 @@ typedef struct {
|
||||
} WinExtmark;
|
||||
EXTERN kvec_t(WinExtmark) win_extmark_arr INIT( = KV_INITIAL_VALUE);
|
||||
|
||||
EXTERN bool conceal_cursor_used INIT( = false);
|
||||
|
||||
/// Spell checking variables passed from win_update() to win_line().
|
||||
typedef struct {
|
||||
bool spv_has_spell; ///< drawn window has spell checking
|
||||
|
@ -135,6 +135,7 @@ typedef enum {
|
||||
static bool redraw_popupmenu = false;
|
||||
static bool msg_grid_invalid = false;
|
||||
static bool resizing_autocmd = false;
|
||||
static bool conceal_cursor_used = false;
|
||||
|
||||
/// Check if the cursor line needs to be redrawn because of 'concealcursor'.
|
||||
///
|
||||
@ -2047,6 +2048,9 @@ static void win_update(win_T *wp)
|
||||
|
||||
foldinfo_T cursorline_fi = { 0 };
|
||||
win_update_cursorline(wp, &cursorline_fi);
|
||||
if (wp == curwin) {
|
||||
conceal_cursor_used = conceal_cursor_line(curwin);
|
||||
}
|
||||
|
||||
win_check_ns_hl(wp);
|
||||
|
||||
@ -2123,21 +2127,13 @@ static void win_update(win_T *wp)
|
||||
|
||||
// If the line is concealed and has no filler lines, go to the next line.
|
||||
bool concealed = decor_conceal_line(wp, lnum - 1, false);
|
||||
if (concealed) {
|
||||
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
|
||||
conceal_cursor_used = conceal_cursor_line(curwin);
|
||||
}
|
||||
if (win_get_fill(wp, lnum) == 0) {
|
||||
if (idx > 0) {
|
||||
wp->w_lines[idx - 1].wl_lastlnum = lnum + foldinfo.fi_lines - (foldinfo.fi_lines != 0);
|
||||
}
|
||||
if (lnum == mod_top && lnum < mod_bot) {
|
||||
mod_top += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
|
||||
}
|
||||
lnum += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
|
||||
spv.spv_capcol_lnum = 0;
|
||||
continue;
|
||||
if (concealed && win_get_fill(wp, lnum) == 0) {
|
||||
if (lnum == mod_top && lnum < mod_bot) {
|
||||
mod_top += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
|
||||
}
|
||||
lnum += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
|
||||
spv.spv_capcol_lnum = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// When at start of changed lines: May scroll following lines
|
||||
@ -2189,8 +2185,8 @@ static void win_update(win_T *wp)
|
||||
// rows, and may insert/delete lines
|
||||
int j = idx;
|
||||
for (l = lnum; l < mod_bot; l++) {
|
||||
int n = plines_win_full(wp, l, &l, NULL, true, false);
|
||||
n -= (l == wp->w_topline ? adjust_plines_for_skipcol(wp) : 0);
|
||||
int n = (l == wp->w_topline ? -adjust_plines_for_skipcol(wp) : 0);
|
||||
n += plines_win_full(wp, l, &l, NULL, true, false);
|
||||
new_rows += MIN(n, wp->w_height_inner);
|
||||
j += n > 0; // don't count concealed lines
|
||||
if (new_rows > wp->w_grid.rows - row - 2) {
|
||||
@ -2306,23 +2302,19 @@ static void win_update(win_T *wp)
|
||||
spv.spv_capcol_lnum = 0;
|
||||
}
|
||||
|
||||
if (foldinfo.fi_lines == 0) {
|
||||
wp->w_lines[idx].wl_folded = false;
|
||||
wp->w_lines[idx].wl_foldend = lnum;
|
||||
wp->w_lines[idx].wl_lastlnum = lnum;
|
||||
did_update = DID_LINE;
|
||||
} else {
|
||||
foldinfo.fi_lines--;
|
||||
wp->w_lines[idx].wl_folded = true;
|
||||
wp->w_lines[idx].wl_foldend = lnum + foldinfo.fi_lines;
|
||||
wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
|
||||
did_update = DID_FOLD;
|
||||
}
|
||||
linenr_T lastlnum = lnum + foldinfo.fi_lines - (foldinfo.fi_lines > 0);
|
||||
wp->w_lines[idx].wl_folded = foldinfo.fi_lines > 0;
|
||||
wp->w_lines[idx].wl_foldend = lastlnum;
|
||||
wp->w_lines[idx].wl_lastlnum = lastlnum;
|
||||
did_update = foldinfo.fi_lines > 0 ? DID_FOLD : DID_LINE;
|
||||
|
||||
// Adjust "wl_lastlnum" for concealed lines below the last line in the window.
|
||||
while (row == wp->w_grid.rows
|
||||
&& wp->w_lines[idx].wl_lastlnum < buf->b_ml.ml_line_count
|
||||
// Adjust "wl_lastlnum" for concealed lines below this line, unless it should
|
||||
// still be drawn for below virt_lines attached to the current line. Below
|
||||
// virt_lines attached to a second adjacent concealed line are concealed.
|
||||
bool virt_below = decor_virt_lines(wp, lastlnum, lastlnum + 1, NULL, NULL, true) > 0;
|
||||
while (!virt_below && wp->w_lines[idx].wl_lastlnum < buf->b_ml.ml_line_count
|
||||
&& decor_conceal_line(wp, wp->w_lines[idx].wl_lastlnum, false)) {
|
||||
virt_below = false;
|
||||
wp->w_lines[idx].wl_lastlnum++;
|
||||
hasFolding(wp, wp->w_lines[idx].wl_lastlnum, NULL, &wp->w_lines[idx].wl_lastlnum);
|
||||
}
|
||||
@ -2342,17 +2334,15 @@ static void win_update(win_T *wp)
|
||||
if (dollar_vcol == -1) {
|
||||
wp->w_lines[idx].wl_size = (uint16_t)(row - srow);
|
||||
}
|
||||
idx++;
|
||||
lnum += foldinfo.fi_lines + 1;
|
||||
lnum = wp->w_lines[idx++].wl_lastlnum + 1;
|
||||
} else {
|
||||
// If:
|
||||
// - 'number' is set and below inserted/deleted lines, or
|
||||
// - 'relativenumber' is set and cursor moved vertically,
|
||||
// the text doesn't need to be redrawn, but the number column does.
|
||||
if (((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
|
||||
&& buf->b_mod_set && buf->b_mod_xlines != 0)
|
||||
|| (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum))
|
||||
&& !decor_conceal_line(wp, lnum - 1, true)) {
|
||||
if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
|
||||
&& buf->b_mod_set && buf->b_mod_xlines != 0)
|
||||
|| (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) {
|
||||
foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum
|
||||
? cursorline_fi : fold_info(wp, lnum);
|
||||
win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, false, &spv, info);
|
||||
|
@ -188,6 +188,7 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")
|
||||
EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
|
||||
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
|
||||
EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type"));
|
||||
EXTERN const char e_cannot_switch_to_a_closing_buffer[] INIT( = N_("E1546: Cannot switch to a closing buffer"));
|
||||
|
||||
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
|
||||
|
||||
|
@ -3652,7 +3652,7 @@ M.funcs = {
|
||||
name = 'getcharstr',
|
||||
params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
|
||||
returns = 'string',
|
||||
signature = 'getcharstr([{expr}])',
|
||||
signature = 'getcharstr([{expr} [, {opts}]])',
|
||||
},
|
||||
getcmdcomplpat = {
|
||||
desc = [=[
|
||||
@ -13239,7 +13239,7 @@ M.funcs = {
|
||||
]=],
|
||||
name = 'winlayout',
|
||||
params = { { 'tabnr', 'integer' } },
|
||||
returns = 'any[]',
|
||||
returns = 'vim.fn.winlayout.ret',
|
||||
signature = 'winlayout([{tabnr}])',
|
||||
},
|
||||
winline = {
|
||||
|
@ -2270,14 +2270,16 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
|
||||
if (buf == NULL) {
|
||||
goto theend;
|
||||
}
|
||||
// autocommands try to edit a file that is going to be removed, abort
|
||||
if (buf_locked(buf)) {
|
||||
// autocommands try to edit a closing buffer, which like splitting, can
|
||||
// result in more windows displaying it; abort
|
||||
if (buf->b_locked_split) {
|
||||
// window was split, but not editing the new buffer, reset b_nwindows again
|
||||
if (oldwin == NULL
|
||||
&& curwin->w_buffer != NULL
|
||||
&& curwin->w_buffer->b_nwindows > 1) {
|
||||
curwin->w_buffer->b_nwindows--;
|
||||
}
|
||||
emsg(_(e_cannot_switch_to_a_closing_buffer));
|
||||
goto theend;
|
||||
}
|
||||
if (curwin->w_alt_fnum == buf->b_fnum && prev_alt_fnum != 0) {
|
||||
@ -2726,6 +2728,9 @@ theend:
|
||||
if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->terminal != NULL) {
|
||||
terminal_check_size(old_curbuf.br_buf->terminal);
|
||||
}
|
||||
if ((!bufref_valid(&old_curbuf) || curbuf != old_curbuf.br_buf) && curbuf->terminal != NULL) {
|
||||
terminal_check_size(curbuf->terminal);
|
||||
}
|
||||
|
||||
if (did_inc_redrawing_disabled) {
|
||||
RedrawingDisabled--;
|
||||
|
@ -792,7 +792,6 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
|
||||
}
|
||||
|
||||
setmouse();
|
||||
setcursor();
|
||||
|
||||
Error err = ERROR_INIT;
|
||||
char firstcbuf[2];
|
||||
|
@ -326,10 +326,11 @@ char *get_special_key_name(int c, int modifiers)
|
||||
string[idx++] = (char)(uint8_t)KEY2TERMCAP1(c);
|
||||
} else {
|
||||
// Not a special key, only modifiers, output directly.
|
||||
if (utf_char2len(c) > 1) {
|
||||
idx += utf_char2bytes(c, string + idx);
|
||||
} else if (vim_isprintc(c)) {
|
||||
int len = utf_char2len(c);
|
||||
if (len == 1 && vim_isprintc(c)) {
|
||||
string[idx++] = (char)(uint8_t)c;
|
||||
} else if (len > 1) {
|
||||
idx += utf_char2bytes(c, string + idx);
|
||||
} else {
|
||||
char *s = transchar(c);
|
||||
while (*s) {
|
||||
|
@ -1926,7 +1926,17 @@ int put_escstr(FILE *fd, const char *strstart, int what)
|
||||
if (str[1] == KS_MODIFIER) {
|
||||
modifiers = str[2];
|
||||
str += 3;
|
||||
c = *str;
|
||||
|
||||
// Modifiers can be applied too to multi-byte characters.
|
||||
p = mb_unescape((const char **)&str);
|
||||
|
||||
if (p == NULL) {
|
||||
c = *str;
|
||||
} else {
|
||||
// retrieve codepoint (character number) from unescaped string
|
||||
c = utf_ptr2char(p);
|
||||
str--;
|
||||
}
|
||||
}
|
||||
if (c == K_SPECIAL) {
|
||||
c = TO_SPECIAL(str[1], str[2]);
|
||||
|
@ -2011,6 +2011,11 @@ void msg_prt_line(const char *s, bool list)
|
||||
} else {
|
||||
hl_id = 0;
|
||||
int c = (uint8_t)(*s++);
|
||||
if (c >= 0x80) { // Illegal byte
|
||||
col += utf_char2cells(c);
|
||||
msg_putchar(c);
|
||||
continue;
|
||||
}
|
||||
sc_extra = NUL;
|
||||
sc_final = NUL;
|
||||
if (list) {
|
||||
|
@ -1331,56 +1331,55 @@ bool scrolldown(win_T *wp, linenr_T line_count, int byfold)
|
||||
hasFolding(wp, wp->w_topline, &wp->w_topline, NULL);
|
||||
validate_cursor(wp); // w_wrow needs to be valid
|
||||
for (int todo = line_count; todo > 0; todo--) {
|
||||
if (wp->w_topfill < win_get_fill(wp, wp->w_topline)
|
||||
&& wp->w_topfill < wp->w_height_inner - 1) {
|
||||
bool can_fill = wp->w_topfill < wp->w_height_inner - 1
|
||||
&& wp->w_topfill < win_get_fill(wp, wp->w_topline);
|
||||
// break when at the very top
|
||||
if (wp->w_topline == 1 && !can_fill && (!do_sms || wp->w_skipcol < width1)) {
|
||||
break;
|
||||
}
|
||||
if (do_sms && wp->w_skipcol >= width1) {
|
||||
// scroll a screen line down
|
||||
if (wp->w_skipcol >= width1 + width2) {
|
||||
wp->w_skipcol -= width2;
|
||||
} else {
|
||||
wp->w_skipcol -= width1;
|
||||
}
|
||||
redraw_later(wp, UPD_NOT_VALID);
|
||||
done++;
|
||||
} else if (can_fill) {
|
||||
wp->w_topfill++;
|
||||
done++;
|
||||
} else {
|
||||
// break when at the very top
|
||||
if (wp->w_topline == 1 && (!do_sms || wp->w_skipcol < width1)) {
|
||||
break;
|
||||
}
|
||||
if (do_sms && wp->w_skipcol >= width1) {
|
||||
// scroll a screen line down
|
||||
if (wp->w_skipcol >= width1 + width2) {
|
||||
wp->w_skipcol -= width2;
|
||||
} else {
|
||||
wp->w_skipcol -= width1;
|
||||
// scroll a text line down
|
||||
wp->w_topline--;
|
||||
wp->w_skipcol = 0;
|
||||
wp->w_topfill = 0;
|
||||
// A sequence of folded lines only counts for one logical line
|
||||
linenr_T first;
|
||||
if (hasFolding(wp, wp->w_topline, &first, NULL)) {
|
||||
done += !decor_conceal_line(wp, first - 1, false);
|
||||
if (!byfold) {
|
||||
todo -= wp->w_topline - first - 1;
|
||||
}
|
||||
redraw_later(wp, UPD_NOT_VALID);
|
||||
done++;
|
||||
wp->w_botline -= wp->w_topline - first;
|
||||
wp->w_topline = first;
|
||||
} else if (decor_conceal_line(wp, wp->w_topline - 1, false)) {
|
||||
todo++;
|
||||
} else {
|
||||
// scroll a text line down
|
||||
wp->w_topline--;
|
||||
wp->w_skipcol = 0;
|
||||
wp->w_topfill = 0;
|
||||
// A sequence of folded lines only counts for one logical line
|
||||
linenr_T first;
|
||||
if (hasFolding(wp, wp->w_topline, &first, NULL)) {
|
||||
done += !decor_conceal_line(wp, first - 1, false);
|
||||
if (!byfold) {
|
||||
todo -= wp->w_topline - first - 1;
|
||||
if (do_sms) {
|
||||
int size = linetabsize_eol(wp, wp->w_topline);
|
||||
if (size > width1) {
|
||||
wp->w_skipcol = width1;
|
||||
size -= width1;
|
||||
redraw_later(wp, UPD_NOT_VALID);
|
||||
}
|
||||
wp->w_botline -= wp->w_topline - first;
|
||||
wp->w_topline = first;
|
||||
} else if (decor_conceal_line(wp, wp->w_topline - 1, false)) {
|
||||
todo++;
|
||||
while (size > width2) {
|
||||
wp->w_skipcol += width2;
|
||||
size -= width2;
|
||||
}
|
||||
done++;
|
||||
} else {
|
||||
if (do_sms) {
|
||||
int size = linetabsize_eol(wp, wp->w_topline);
|
||||
if (size > width1) {
|
||||
wp->w_skipcol = width1;
|
||||
size -= width1;
|
||||
redraw_later(wp, UPD_NOT_VALID);
|
||||
}
|
||||
while (size > width2) {
|
||||
wp->w_skipcol += width2;
|
||||
size -= width2;
|
||||
}
|
||||
done++;
|
||||
} else {
|
||||
done += plines_win_nofill(wp, wp->w_topline, true);
|
||||
}
|
||||
done += plines_win_nofill(wp, wp->w_topline, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2333,6 +2332,7 @@ void cursor_correct(win_T *wp)
|
||||
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
|
||||
}
|
||||
}
|
||||
check_cursor_moved(wp);
|
||||
wp->w_valid |= VALID_TOPLINE;
|
||||
wp->w_viewport_invalid = true;
|
||||
}
|
||||
@ -2423,11 +2423,16 @@ static bool scroll_with_sms(Direction dir, int count, int *curscount)
|
||||
if (labs(curwin->w_topline - prev_topline) > (dir == BACKWARD)) {
|
||||
fixdir = dir * -1;
|
||||
}
|
||||
while (curwin->w_skipcol > 0
|
||||
&& curwin->w_topline < curbuf->b_ml.ml_line_count) {
|
||||
scroll_redraw(fixdir == FORWARD, 1);
|
||||
*curscount += (fixdir == dir ? 1 : -1);
|
||||
|
||||
int width1 = curwin->w_width_inner - win_col_off(curwin);
|
||||
int width2 = width1 + win_col_off2(curwin);
|
||||
count = 1 + (curwin->w_skipcol - width1 - 1) / width2;
|
||||
if (fixdir == FORWARD) {
|
||||
count = 1 + (linetabsize_eol(curwin, curwin->w_topline)
|
||||
- curwin->w_skipcol - width1 + width2 - 1) / width2;
|
||||
}
|
||||
scroll_redraw(fixdir == FORWARD, count);
|
||||
*curscount += count * (fixdir == dir ? 1 : -1);
|
||||
}
|
||||
curwin->w_p_sms = prev_sms;
|
||||
|
||||
|
@ -247,8 +247,7 @@ static void parse_msgpack(Channel *channel)
|
||||
Unpacker *p = channel->rpc.unpacker;
|
||||
while (unpacker_advance(p)) {
|
||||
if (p->type == kMessageTypeRedrawEvent) {
|
||||
// When exiting, ui_client_stop() has already been called, so don't handle UI events.
|
||||
if (ui_client_channel_id && !exiting) {
|
||||
if (ui_client_attached) {
|
||||
if (p->has_grid_line_event) {
|
||||
ui_client_event_raw_line(&p->grid_line_event);
|
||||
p->has_grid_line_event = false;
|
||||
|
@ -2672,6 +2672,12 @@ local options = {
|
||||
- 'exrc' can execute any code; editorconfig only specifies settings.
|
||||
- 'exrc' is Nvim-specific; editorconfig works in other editors.
|
||||
|
||||
To achieve project-local LSP configuration:
|
||||
1. Enable 'exrc'.
|
||||
2. Place LSP configs at ".nvim/lsp/*.lua" in your project root.
|
||||
3. Create ".nvim.lua" in your project root directory with this line: >lua
|
||||
vim.cmd[[set runtimepath+=.nvim]]
|
||||
<
|
||||
This option cannot be set from a |modeline| or in the |sandbox|, for
|
||||
security reasons.
|
||||
]=],
|
||||
@ -8420,8 +8426,7 @@ local options = {
|
||||
cb = 'did_set_statusline',
|
||||
defaults = '',
|
||||
desc = [=[
|
||||
When non-empty, this option determines the content of the status line.
|
||||
Also see |status-line|.
|
||||
Sets the |status-line|.
|
||||
|
||||
The option consists of printf style '%' items interspersed with
|
||||
normal text. Each status line item is of the form:
|
||||
|
@ -856,7 +856,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
|
||||
{
|
||||
out_data_decide_throttle(0); // Initialize throttle decider.
|
||||
out_data_ring(NULL, 0); // Initialize output ring-buffer.
|
||||
bool has_input = (input != NULL && input[0] != NUL);
|
||||
bool has_input = (input != NULL && len > 0);
|
||||
|
||||
// the output buffer
|
||||
StringBuilder buf = KV_INITIAL_VALUE;
|
||||
|
@ -5789,7 +5789,9 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
|
||||
aucmd_restbuf(&aco);
|
||||
|
||||
if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) {
|
||||
wipe_buffer(newbuf_to_wipe.br_buf, false);
|
||||
block_autocmds();
|
||||
wipe_dummy_buffer(newbuf_to_wipe.br_buf, NULL);
|
||||
unblock_autocmds();
|
||||
}
|
||||
|
||||
// Add back the "dummy" flag, otherwise buflist_findname_file_id()
|
||||
@ -5813,11 +5815,11 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
// Wipe out the dummy buffer that load_dummy_buffer() created. Restores
|
||||
// directory to "dirname_start" prior to returning, if autocmds or the
|
||||
// 'autochdir' option have changed it.
|
||||
/// Wipe out the dummy buffer that load_dummy_buffer() created. Restores
|
||||
/// directory to "dirname_start" if not NULL prior to returning, if autocmds or
|
||||
/// the 'autochdir' option have changed it.
|
||||
static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
FUNC_ATTR_NONNULL_ARG(1)
|
||||
{
|
||||
// If any autocommand opened a window on the dummy buffer, close that
|
||||
// window. If we can't close them all then give up.
|
||||
@ -5835,7 +5837,7 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
|
||||
}
|
||||
}
|
||||
if (!did_one) {
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5852,14 +5854,23 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
|
||||
// Restore the error/interrupt/exception state if not discarded by a
|
||||
// new aborting error, interrupt, or uncaught exception.
|
||||
leave_cleanup(&cs);
|
||||
// When autocommands/'autochdir' option changed directory: go back.
|
||||
restore_start_dir(dirname_start);
|
||||
|
||||
if (dirname_start != NULL) {
|
||||
// When autocommands/'autochdir' option changed directory: go back.
|
||||
restore_start_dir(dirname_start);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fail:
|
||||
// Keeping the buffer, remove the dummy flag.
|
||||
buf->b_flags &= ~BF_DUMMY;
|
||||
}
|
||||
|
||||
// Unload the dummy buffer that load_dummy_buffer() created. Restores
|
||||
// directory to "dirname_start" prior to returning, if autocmds or the
|
||||
// 'autochdir' option have changed it.
|
||||
/// Unload the dummy buffer that load_dummy_buffer() created. Restores
|
||||
/// directory to "dirname_start" prior to returning, if autocmds or the
|
||||
/// 'autochdir' option have changed it.
|
||||
static void unload_dummy_buffer(buf_T *buf, char *dirname_start)
|
||||
{
|
||||
if (curbuf == buf) { // safety check
|
||||
|
@ -2722,12 +2722,9 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
|
||||
|| (dirc == '/' && lt(p, lastpos)));
|
||||
|
||||
// If anything relevant changed the count has to be recomputed.
|
||||
// STRNICMP ignores case, but we should not ignore case.
|
||||
// Unfortunately, there is no STRNICMP function.
|
||||
// XXX: above comment should be "no MB_STRCMP function" ?
|
||||
if (!(chgtick == buf_get_changedtick(curbuf)
|
||||
&& (lastpat != NULL // suppress clang/NULL passed as nonnull parameter
|
||||
&& mb_strnicmp(lastpat, spats[last_idx].pat, lastpatlen) == 0
|
||||
&& strncmp(lastpat, spats[last_idx].pat, lastpatlen) == 0
|
||||
&& lastpatlen == spats[last_idx].patlen)
|
||||
&& equalpos(lastpos, *cursor_pos)
|
||||
&& lbuf == curbuf)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "nvim/tui/input_defs.h"
|
||||
#include "nvim/tui/termkey/driver-csi.h"
|
||||
#include "nvim/tui/termkey/termkey.h"
|
||||
#include "nvim/tui/termkey/termkey_defs.h"
|
||||
#include "nvim/tui/tui.h"
|
||||
#include "nvim/ui_client.h"
|
||||
|
||||
@ -144,8 +145,8 @@ void tinput_init(TermInput *input, Loop *loop)
|
||||
term = ""; // termkey_new_abstract assumes non-null (#2745)
|
||||
}
|
||||
|
||||
input->tk = termkey_new_abstract(term,
|
||||
TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART);
|
||||
input->tk = termkey_new_abstract(term, (TERMKEY_FLAG_UTF8 | TERMKEY_FLAG_NOSTART
|
||||
| TERMKEY_FLAG_KEEPC0));
|
||||
termkey_set_buffer_size(input->tk, INPUT_BUFFER_SIZE);
|
||||
termkey_hook_terminfo_getstr(input->tk, input->tk_ti_hook_fn, input);
|
||||
termkey_start(input->tk);
|
||||
@ -248,6 +249,13 @@ static size_t handle_termkey_modifiers(TermKeyKey *key, char *buf, size_t buflen
|
||||
return len;
|
||||
}
|
||||
|
||||
enum {
|
||||
KEYMOD_SUPER = 1 << 3,
|
||||
KEYMOD_META = 1 << 5,
|
||||
KEYMOD_RECOGNIZED = (TERMKEY_KEYMOD_SHIFT | TERMKEY_KEYMOD_ALT | TERMKEY_KEYMOD_CTRL
|
||||
| KEYMOD_SUPER | KEYMOD_META),
|
||||
};
|
||||
|
||||
/// Handle modifiers not handled by libtermkey.
|
||||
/// Currently only Super ("D-") and Meta ("T-") are supported in Nvim.
|
||||
///
|
||||
@ -256,10 +264,10 @@ static size_t handle_more_modifiers(TermKeyKey *key, char *buf, size_t buflen)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
size_t len = 0;
|
||||
if (key->modifiers & 8) { // Super
|
||||
if (key->modifiers & KEYMOD_SUPER) {
|
||||
len += (size_t)snprintf(buf + len, buflen - len, "D-");
|
||||
}
|
||||
if (key->modifiers & 32) { // Meta
|
||||
if (key->modifiers & KEYMOD_META) {
|
||||
len += (size_t)snprintf(buf + len, buflen - len, "T-");
|
||||
}
|
||||
assert(len < buflen);
|
||||
@ -444,7 +452,7 @@ static void tk_getkeys(TermInput *input, bool force)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
|
||||
if (key.type == TERMKEY_TYPE_UNICODE && !(key.modifiers & KEYMOD_RECOGNIZED)) {
|
||||
forward_simple_utf8(input, &key);
|
||||
} else if (key.type == TERMKEY_TYPE_UNICODE
|
||||
|| key.type == TERMKEY_TYPE_FUNCTION
|
||||
@ -651,8 +659,10 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
|
||||
case '?':
|
||||
// Primary Device Attributes (DA1) response
|
||||
if (input->callbacks.primary_device_attr) {
|
||||
input->callbacks.primary_device_attr(input->tui_data);
|
||||
void (*cb_save)(TUIData *) = input->callbacks.primary_device_attr;
|
||||
// Clear the callback before invoking it, as it may set a new callback. #34031
|
||||
input->callbacks.primary_device_attr = NULL;
|
||||
cb_save(input->tui_data);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -507,7 +507,7 @@ static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len,
|
||||
present = 0;
|
||||
argi++;
|
||||
|
||||
if (argi > 16) {
|
||||
if (argi >= 16) {
|
||||
break;
|
||||
}
|
||||
} else if (c >= 0x20 && c <= 0x2f) {
|
||||
|
@ -719,7 +719,7 @@ static void emit_codepoint(TermKey *tk, int codepoint, TermKeyKey *key)
|
||||
key->type = TERMKEY_TYPE_KEYSYM;
|
||||
key->code.sym = TERMKEY_SYM_SPACE;
|
||||
key->modifiers = TERMKEY_KEYMOD_CTRL;
|
||||
} else if (codepoint < 0x20) {
|
||||
} else if (codepoint < 0x20 && !(tk->flags & TERMKEY_FLAG_KEEPC0)) {
|
||||
// C0 range
|
||||
key->code.codepoint = 0;
|
||||
key->modifiers = 0;
|
||||
@ -750,7 +750,7 @@ static void emit_codepoint(TermKey *tk, int codepoint, TermKeyKey *key)
|
||||
key->type = TERMKEY_TYPE_KEYSYM;
|
||||
key->code.sym = TERMKEY_SYM_DEL;
|
||||
key->modifiers = 0;
|
||||
} else if (codepoint >= 0x20 && codepoint < 0x80) {
|
||||
} else if (codepoint > 0 && codepoint < 0x80) {
|
||||
// ASCII lowbyte range
|
||||
key->type = TERMKEY_TYPE_UNICODE;
|
||||
key->code.codepoint = codepoint;
|
||||
|
@ -151,6 +151,7 @@ enum {
|
||||
TERMKEY_FLAG_CTRLC = 1 << 6, // Allow Ctrl-C to be read as normal, disabling SIGINT
|
||||
TERMKEY_FLAG_EINTR = 1 << 7, // Return ERROR on signal (EINTR) rather than retry
|
||||
TERMKEY_FLAG_NOSTART = 1 << 8, // Do not call termkey_start() in constructor
|
||||
TERMKEY_FLAG_KEEPC0 = 1 << 9, // Keep raw C0 control codes
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -360,6 +360,7 @@ static void terminfo_start(TUIData *tui)
|
||||
tui->overflow = false;
|
||||
tui->set_cursor_color_as_str = false;
|
||||
tui->cursor_has_color = false;
|
||||
tui->did_set_grapheme_cluster_mode = false;
|
||||
tui->showing_mode = SHAPE_IDX_N;
|
||||
tui->unibi_ext.enable_mouse = -1;
|
||||
tui->unibi_ext.disable_mouse = -1;
|
||||
|
@ -186,6 +186,7 @@ void ui_client_run(bool remote_ui)
|
||||
|
||||
void ui_client_stop(void)
|
||||
{
|
||||
ui_client_attached = false;
|
||||
if (!tui_is_stopped(tui)) {
|
||||
tui_stop(tui);
|
||||
}
|
||||
|
@ -347,12 +347,23 @@ newwindow:
|
||||
} else {
|
||||
win_T *wp;
|
||||
if (Prenum) { // go to specified window
|
||||
win_T *last_focusable = firstwin;
|
||||
for (wp = firstwin; --Prenum > 0;) {
|
||||
if (!wp->w_floating || (!wp->w_config.hide && wp->w_config.focusable)) {
|
||||
last_focusable = wp;
|
||||
}
|
||||
if (wp->w_next == NULL) {
|
||||
break;
|
||||
}
|
||||
wp = wp->w_next;
|
||||
}
|
||||
while (wp != NULL && wp->w_floating
|
||||
&& (wp->w_config.hide || !wp->w_config.focusable)) {
|
||||
wp = wp->w_next;
|
||||
}
|
||||
if (wp == NULL) { // went past the last focusable window
|
||||
wp = last_focusable;
|
||||
}
|
||||
} else {
|
||||
if (nchar == 'W') { // go to previous window
|
||||
wp = curwin->w_prev;
|
||||
|
@ -1769,10 +1769,10 @@ describe('API/win', function()
|
||||
pcall_err(api.nvim_win_close, w, true)
|
||||
)
|
||||
|
||||
-- OK when using window to different buffer than `win`s.
|
||||
-- OK when using a buffer that isn't closing.
|
||||
w = api.nvim_get_current_win()
|
||||
command(
|
||||
'only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: '
|
||||
'only | autocmd BufHidden * ++once call nvim_open_win(bufnr("#"), 0, #{split: "left", win: '
|
||||
.. w
|
||||
.. '})'
|
||||
)
|
||||
|
@ -10,18 +10,18 @@ local eq = t.eq
|
||||
---@param type string
|
||||
---@return string
|
||||
local function stdpath(type)
|
||||
return exec_lua([[return vim.fs.normalize(vim.fn.stdpath(...))]], type)
|
||||
return exec_lua([[return vim.fs.abspath(vim.fn.stdpath(...))]], type)
|
||||
end
|
||||
|
||||
---@return string
|
||||
local function vimruntime()
|
||||
return exec_lua [[ return vim.fs.normalize(vim.env.VIMRUNTIME) ]]
|
||||
return exec_lua [[ return vim.fs.abspath(vim.env.VIMRUNTIME) ]]
|
||||
end
|
||||
|
||||
---@param module string
|
||||
---@return string
|
||||
local function lua_includeexpr(module)
|
||||
return exec_lua([[return require('vim._ftplugin.lua').includeexpr(...)]], module)
|
||||
return exec_lua([[return vim.fs.abspath(require 'vim._ftplugin.lua'.includeexpr(...))]], module)
|
||||
end
|
||||
|
||||
describe("ftplugin: Lua 'includeexpr'", function()
|
||||
@ -59,7 +59,12 @@ describe("ftplugin: Lua 'includeexpr'", function()
|
||||
write ++p
|
||||
edit %s/lua/runtime-foo/bar.lua
|
||||
write ++p
|
||||
]]):format(temp_dir, temp_dir))
|
||||
|
||||
edit %s/general-foo/bar/init.lua
|
||||
write ++p
|
||||
edit %s/general-foo/bar/baz.lua
|
||||
write ++p
|
||||
]]):format(temp_dir, temp_dir, temp_dir, temp_dir))
|
||||
end)
|
||||
|
||||
it('finds module in current repo', function()
|
||||
@ -94,4 +99,11 @@ describe("ftplugin: Lua 'includeexpr'", function()
|
||||
eq(temp_dir .. '/lua/runtime-foo/init.lua', lua_includeexpr('runtime-foo'))
|
||||
eq(temp_dir .. '/lua/runtime-foo/bar.lua', lua_includeexpr('runtime-foo.bar'))
|
||||
end)
|
||||
|
||||
it('non-Nvim-style Lua modules', function()
|
||||
command('cd ' .. temp_dir)
|
||||
eq(temp_dir .. '/general-foo/bar/init.lua', lua_includeexpr('general-foo.bar'))
|
||||
eq(temp_dir .. '/general-foo/bar/baz.lua', lua_includeexpr('general-foo.bar.baz'))
|
||||
command('cd -')
|
||||
end)
|
||||
end)
|
||||
|
@ -1,7 +1,9 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local clear, eq, command, fn = n.clear, t.eq, n.command, n.fn
|
||||
local assert_alive = n.assert_alive
|
||||
|
||||
describe(':z^', function()
|
||||
before_each(clear)
|
||||
@ -11,3 +13,20 @@ describe(':z^', function()
|
||||
eq(1, fn.line('.'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':print', function()
|
||||
before_each(clear)
|
||||
|
||||
it('does not crash when printing 0xFF byte #34044', function()
|
||||
local screen = Screen.new()
|
||||
-- Needs raw 0xFF byte, not 0xFF char
|
||||
command('call setline(1, "foo\\xFFbar")')
|
||||
command('%print')
|
||||
screen:expect([[
|
||||
^foo{18:<ff>}bar |
|
||||
{1:~ }|*12
|
||||
fooÿbar |
|
||||
]])
|
||||
assert_alive()
|
||||
end)
|
||||
end)
|
||||
|
@ -450,8 +450,8 @@ pcall(vim.cmd.edit, 'Xtest_swapredraw.lua')
|
||||
screen:expect({
|
||||
any = table.concat({
|
||||
pesc('{2:E325: ATTENTION}'),
|
||||
'file name: .*Xswaptest',
|
||||
'process ID: %d* %(STILL RUNNING%)',
|
||||
'\n process ID: %d* %(STILL RUNNING%)',
|
||||
'\nWhile opening file "Xswaptest"',
|
||||
pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
|
||||
}, '.*'),
|
||||
})
|
||||
|
@ -11,6 +11,30 @@ local eq = t.eq
|
||||
|
||||
before_each(clear)
|
||||
|
||||
local function expected_empty()
|
||||
eq({}, api.nvim_get_vvar('errors'))
|
||||
end
|
||||
|
||||
-- oldtest: Test_get_Visual_selection_in_curbuf_autocmd()
|
||||
it('autocmd can get Visual selection when using setbufvar() on curbuf', function()
|
||||
n.exec([[
|
||||
new
|
||||
autocmd OptionSet list let b:text = getregion(getpos('.'), getpos('v'))
|
||||
call setline(1, 'foo bar baz')
|
||||
|
||||
normal! gg0fbvtb
|
||||
setlocal list
|
||||
call assert_equal(['bar '], b:text)
|
||||
exe "normal! \<Esc>"
|
||||
|
||||
normal! v0
|
||||
call setbufvar('%', '&list', v:false)
|
||||
call assert_equal(['foo bar '], b:text)
|
||||
exe "normal! \<Esc>"
|
||||
]])
|
||||
expected_empty()
|
||||
end)
|
||||
|
||||
-- oldtest: Test_autocmd_invalidates_undo_on_textchanged()
|
||||
it('no E440 in quickfix window when autocommand invalidates undo', function()
|
||||
write_file(
|
||||
|
@ -184,4 +184,28 @@ describe('search stat', function()
|
||||
{19:search hit TOP, continuing at BOTTOM} |
|
||||
]])
|
||||
end)
|
||||
|
||||
-- oldtest: Test_search_stat_smartcase_ignorecase()
|
||||
it('when changing case of pattern', function()
|
||||
exec([[
|
||||
set shm-=S ignorecase smartcase
|
||||
call setline(1, [' MainmainmainmmmainmAin', ''])
|
||||
]])
|
||||
|
||||
feed('/main<cr>nnnn')
|
||||
screen:expect([[
|
||||
{10:Mainmainmain}mm{10:main^mAin} |
|
||||
|
|
||||
{1:~ }|*7
|
||||
/main [5/5] |
|
||||
]])
|
||||
|
||||
feed('/mAin<cr>')
|
||||
screen:expect([[
|
||||
Mainmainmainmmmain{10:^mAin} |
|
||||
|
|
||||
{1:~ }|*7
|
||||
/mAin [1/1] |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
@ -20,25 +20,33 @@ local read_file = t.read_file
|
||||
describe('vim.secure', function()
|
||||
describe('read()', function()
|
||||
local xstate = 'Xstate'
|
||||
local screen ---@type test.functional.ui.screen
|
||||
|
||||
setup(function()
|
||||
before_each(function()
|
||||
clear { env = { XDG_STATE_HOME = xstate } }
|
||||
n.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim'))
|
||||
|
||||
t.mkdir('Xdir')
|
||||
t.mkdir('Xdir/Xsubdir')
|
||||
t.write_file('Xdir/Xfile.txt', [[hello, world]])
|
||||
|
||||
t.write_file(
|
||||
'Xfile',
|
||||
[[
|
||||
let g:foobar = 42
|
||||
]]
|
||||
)
|
||||
screen = Screen.new(500, 8)
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
after_each(function()
|
||||
screen:detach()
|
||||
os.remove('Xfile')
|
||||
n.rmdir('Xdir')
|
||||
n.rmdir(xstate)
|
||||
end)
|
||||
|
||||
it('works', function()
|
||||
local screen = Screen.new(500, 8)
|
||||
it('regular file', function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = { bold = true, foreground = Screen.colors.Blue1 },
|
||||
[2] = { bold = true, reverse = true },
|
||||
@ -47,7 +55,8 @@ describe('vim.secure', function()
|
||||
})
|
||||
|
||||
local cwd = fn.getcwd()
|
||||
if #cwd + 23 > 500 then
|
||||
local msg = cwd .. pathsep .. 'Xfile is not trusted.'
|
||||
if #msg >= screen._width then
|
||||
pending('path too long')
|
||||
return
|
||||
end
|
||||
@ -59,7 +68,7 @@ describe('vim.secure', function()
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xfile'){MATCH: +}|
|
||||
{3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('d')
|
||||
@ -81,7 +90,7 @@ describe('vim.secure', function()
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xfile'){MATCH: +}|
|
||||
{3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('a')
|
||||
@ -94,7 +103,7 @@ describe('vim.secure', function()
|
||||
local hash = fn.sha256(assert(read_file('Xfile')))
|
||||
trust = assert(read_file(stdpath('state') .. pathsep .. 'trust'))
|
||||
eq(string.format('%s %s', hash, cwd .. pathsep .. 'Xfile'), vim.trim(trust))
|
||||
eq(vim.NIL, exec_lua([[vim.secure.read('Xfile')]]))
|
||||
eq('let g:foobar = 42\n', exec_lua([[return vim.secure.read('Xfile')]]))
|
||||
|
||||
os.remove(stdpath('state') .. pathsep .. 'trust')
|
||||
|
||||
@ -104,7 +113,7 @@ describe('vim.secure', function()
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xfile'){MATCH: +}|
|
||||
{3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('i')
|
||||
@ -123,7 +132,7 @@ describe('vim.secure', function()
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xfile'){MATCH: +}|
|
||||
{3:]] .. cwd .. pathsep .. [[Xfile is not trusted.}{MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('v')
|
||||
@ -144,6 +153,114 @@ describe('vim.secure', function()
|
||||
pcall_err(command, 'write')
|
||||
eq(true, api.nvim_get_option_value('readonly', {}))
|
||||
end)
|
||||
|
||||
it('directory', function()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = { bold = true, foreground = Screen.colors.Blue1 },
|
||||
[2] = { bold = true, reverse = true },
|
||||
[3] = { bold = true, foreground = Screen.colors.SeaGreen },
|
||||
[4] = { reverse = true },
|
||||
})
|
||||
|
||||
local cwd = fn.getcwd()
|
||||
local msg = cwd
|
||||
.. pathsep
|
||||
.. 'Xdir is not trusted. DIRECTORY trust is decided only by its name, not its contents.'
|
||||
if #msg >= screen._width then
|
||||
pending('path too long')
|
||||
return
|
||||
end
|
||||
|
||||
-- Need to use feed_command instead of exec_lua because of the confirmation prompt
|
||||
feed_command([[lua vim.secure.read('Xdir')]])
|
||||
screen:expect([[
|
||||
{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xdir'){MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('d')
|
||||
screen:expect([[
|
||||
^{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*6
|
||||
{MATCH: +}|
|
||||
]])
|
||||
|
||||
local trust = assert(read_file(stdpath('state') .. pathsep .. 'trust'))
|
||||
eq(string.format('! %s', cwd .. pathsep .. 'Xdir'), vim.trim(trust))
|
||||
eq(vim.NIL, exec_lua([[return vim.secure.read('Xdir')]]))
|
||||
|
||||
os.remove(stdpath('state') .. pathsep .. 'trust')
|
||||
|
||||
feed_command([[lua vim.secure.read('Xdir')]])
|
||||
screen:expect([[
|
||||
{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xdir'){MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('a')
|
||||
screen:expect([[
|
||||
^{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*6
|
||||
{MATCH: +}|
|
||||
]])
|
||||
|
||||
-- Directories aren't hashed in the trust database, instead a slug ("directory") is stored
|
||||
-- instead.
|
||||
local expected_hash = 'directory'
|
||||
trust = assert(read_file(stdpath('state') .. pathsep .. 'trust'))
|
||||
eq(string.format('%s %s', expected_hash, cwd .. pathsep .. 'Xdir'), vim.trim(trust))
|
||||
eq(true, exec_lua([[return vim.secure.read('Xdir')]]))
|
||||
|
||||
os.remove(stdpath('state') .. pathsep .. 'trust')
|
||||
|
||||
feed_command([[lua vim.secure.read('Xdir')]])
|
||||
screen:expect([[
|
||||
{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xdir'){MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('i')
|
||||
screen:expect([[
|
||||
^{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*6
|
||||
{MATCH: +}|
|
||||
]])
|
||||
|
||||
-- Trust database is not updated
|
||||
eq(nil, read_file(stdpath('state') .. pathsep .. 'trust'))
|
||||
|
||||
feed_command([[lua vim.secure.read('Xdir')]])
|
||||
screen:expect([[
|
||||
{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*3
|
||||
{2:{MATCH: +}}|
|
||||
:lua vim.secure.read('Xdir'){MATCH: +}|
|
||||
{3:]] .. msg .. [[}{MATCH: +}|
|
||||
{3:[i]gnore, (v)iew, (d)eny, (a)llow: }^{MATCH: +}|
|
||||
]])
|
||||
feed('v')
|
||||
screen:expect([[
|
||||
^{MATCH: +}|
|
||||
{1:~{MATCH: +}}|*2
|
||||
{2:]] .. fn.fnamemodify(cwd, ':~') .. pathsep .. [[Xdir [RO]{MATCH: +}}|
|
||||
{MATCH: +}|
|
||||
{1:~{MATCH: +}}|
|
||||
{4:[No Name]{MATCH: +}}|
|
||||
{MATCH: +}|
|
||||
]])
|
||||
|
||||
-- Trust database is not updated
|
||||
eq(nil, read_file(stdpath('state') .. pathsep .. 'trust'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('trust()', function()
|
||||
@ -160,10 +277,12 @@ describe('vim.secure', function()
|
||||
|
||||
before_each(function()
|
||||
t.write_file('test_file', 'test')
|
||||
t.mkdir('test_dir')
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
os.remove('test_file')
|
||||
n.rmdir('test_dir')
|
||||
end)
|
||||
|
||||
it('returns error when passing both path and bufnr', function()
|
||||
@ -275,5 +394,15 @@ describe('vim.secure', function()
|
||||
exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])
|
||||
)
|
||||
end)
|
||||
|
||||
it('trust directory bufnr', function()
|
||||
local cwd = fn.getcwd()
|
||||
local full_path = cwd .. pathsep .. 'test_dir'
|
||||
command('edit test_dir')
|
||||
|
||||
eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]]))
|
||||
local trust = read_file(stdpath('state') .. pathsep .. 'trust')
|
||||
eq(string.format('directory %s', full_path), vim.trim(trust))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
@ -55,9 +55,14 @@ describe('vim.system', function()
|
||||
describe('(' .. name .. ')', function()
|
||||
it('failure modes', function()
|
||||
t.matches(
|
||||
'ENOENT%: no such file .*: "non%-existent%-cmd"',
|
||||
"ENOENT%: no such file .* %(cmd%): 'non%-existent%-cmd'",
|
||||
t.pcall_err(system, { 'non-existent-cmd', 'arg1', 'arg2' }, { text = true })
|
||||
)
|
||||
|
||||
t.matches(
|
||||
"ENOENT%: no such file .* %(cwd%): 'non%-existent%-cwd'",
|
||||
t.pcall_err(system, { 'echo', 'hello' }, { cwd = 'non-existent-cwd', text = true })
|
||||
)
|
||||
end)
|
||||
|
||||
it('can run simple commands', function()
|
||||
|
@ -1528,11 +1528,16 @@ describe('lua stdlib', function()
|
||||
pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})")
|
||||
)
|
||||
|
||||
-- Pass an additional message back.
|
||||
-- Validator func can return an extra "Info" message.
|
||||
matches(
|
||||
'arg1: expected %?, got 3. Info: TEST_MSG',
|
||||
pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)")
|
||||
)
|
||||
-- Caller can override the "expected" message.
|
||||
eq(
|
||||
'arg1: expected TEST_MSG, got nil',
|
||||
pcall_err(exec_lua, "vim.validate('arg1', nil, 'table', 'TEST_MSG')")
|
||||
)
|
||||
end)
|
||||
|
||||
it('vim.validate (spec form)', function()
|
||||
@ -3925,6 +3930,17 @@ stack traceback:
|
||||
]]
|
||||
)
|
||||
end)
|
||||
|
||||
it('can get Visual selection in current buffer #34162', function()
|
||||
insert('foo bar baz')
|
||||
feed('gg0fbvtb')
|
||||
local text = exec_lua([[
|
||||
return vim.api.nvim_buf_call(0, function()
|
||||
return vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v'))
|
||||
end)
|
||||
]])
|
||||
eq({ 'bar ' }, text)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim.api.nvim_win_call', function()
|
||||
|
@ -78,6 +78,15 @@ describe(':checkhealth', function()
|
||||
]])
|
||||
)
|
||||
end)
|
||||
|
||||
it("vim.provider works with a misconfigured 'shell'", function()
|
||||
clear()
|
||||
command([[set shell=echo\ WRONG!!!]])
|
||||
command('let g:loaded_perl_provider = 0')
|
||||
command('let g:loaded_python3_provider = 0')
|
||||
command('checkhealth vim.provider')
|
||||
eq(nil, string.match(curbuf_contents(), 'WRONG!!!'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim.health', function()
|
||||
@ -91,7 +100,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug.full_render: require("test_plug.full_render.health").check()
|
||||
test_plug.full_render: 1 ⚠️ 1 ❌
|
||||
|
||||
report 1 ~
|
||||
- ✅ OK life is fine
|
||||
@ -109,12 +118,39 @@ describe('vim.health', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
it('user FileType handler can modify report', function()
|
||||
-- Define a FileType autocmd that removes emoji chars.
|
||||
source [[
|
||||
autocmd FileType checkhealth :set modifiable | silent! %s/\v( ?[^\x00-\x7F])//g
|
||||
checkhealth full_render
|
||||
]]
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug.full_render: 1 1
|
||||
|
||||
report 1 ~
|
||||
- OK life is fine
|
||||
- WARNING no what installed
|
||||
- ADVICE:
|
||||
- pip what
|
||||
- make what
|
||||
|
||||
report 2 ~
|
||||
- stuff is stable
|
||||
- ERROR why no hardcopy
|
||||
- ADVICE:
|
||||
- :help |:hardcopy|
|
||||
- :help |:TOhtml|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('concatenates multiple reports', function()
|
||||
command('checkhealth success1 success2 test_plug')
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug: require("test_plug.health").check()
|
||||
test_plug: ✅
|
||||
|
||||
report 1 ~
|
||||
- ✅ OK everything is fine
|
||||
@ -123,7 +159,7 @@ describe('vim.health', function()
|
||||
- ✅ OK nothing to see here
|
||||
|
||||
==============================================================================
|
||||
test_plug.success1: require("test_plug.success1.health").check()
|
||||
test_plug.success1: ✅
|
||||
|
||||
report 1 ~
|
||||
- ✅ OK everything is fine
|
||||
@ -132,7 +168,7 @@ describe('vim.health', function()
|
||||
- ✅ OK nothing to see here
|
||||
|
||||
==============================================================================
|
||||
test_plug.success2: require("test_plug.success2.health").check()
|
||||
test_plug.success2: ✅
|
||||
|
||||
another 1 ~
|
||||
- ✅ OK ok
|
||||
@ -144,7 +180,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug.submodule: require("test_plug.submodule.health").check()
|
||||
test_plug.submodule: ✅
|
||||
|
||||
report 1 ~
|
||||
- ✅ OK everything is fine
|
||||
@ -159,7 +195,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug.submodule_empty: require("test_plug.submodule_empty.health").check()
|
||||
test_plug.submodule_empty: 1 ❌
|
||||
|
||||
- ❌ ERROR The healthcheck report for "test_plug.submodule_empty" plugin is empty.
|
||||
]])
|
||||
@ -185,7 +221,7 @@ describe('vim.health', function()
|
||||
- ❌ {Error:ERROR} No healthcheck found for "foo" plugin. |
|
||||
|
|
||||
{Bar: }|
|
||||
{h1:test_plug.success1: require("test_pl}|
|
||||
{h1:test_plug.success1: }|
|
||||
|
|
||||
{h2:report 1} |
|
||||
- ✅ {Ok:OK} everything is fine |
|
||||
@ -200,7 +236,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
non_existent_healthcheck:
|
||||
non_existent_healthcheck: 1 ❌
|
||||
|
||||
- ❌ ERROR No healthcheck found for "non_existent_healthcheck" plugin.
|
||||
]])
|
||||
@ -218,7 +254,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
test_plug.lua: require("test_plug.lua.health").check()
|
||||
test_plug.lua: ✅
|
||||
|
||||
nested lua/ directory ~
|
||||
- ✅ OK everything is ok
|
||||
@ -236,7 +272,7 @@ describe('vim.health', function()
|
||||
n.expect([[
|
||||
|
||||
==============================================================================
|
||||
nest: require("nest.health").check()
|
||||
nest: ✅
|
||||
|
||||
healthy pack ~
|
||||
- ✅ OK healthy ok
|
||||
@ -245,17 +281,6 @@ describe('vim.health', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':checkhealth vim.provider', function()
|
||||
it("works correctly with a wrongly configured 'shell'", function()
|
||||
clear()
|
||||
command([[set shell=echo\ WRONG!!!]])
|
||||
command('let g:loaded_perl_provider = 0')
|
||||
command('let g:loaded_python3_provider = 0')
|
||||
command('checkhealth vim.provider')
|
||||
eq(nil, string.match(curbuf_contents(), 'WRONG!!!'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':checkhealth window', function()
|
||||
before_each(function()
|
||||
clear { args = { '-u', 'NORC', '+set runtimepath+=test/functional/fixtures' } }
|
||||
@ -281,14 +306,14 @@ describe(':checkhealth window', function()
|
||||
^ |
|
||||
{14: }|
|
||||
{14: } |
|
||||
{h1:test_plug.success1: }|
|
||||
{h1:require("test_plug.success1.health").check()} |
|
||||
{h1:test_plug. }|
|
||||
{h1:success1: }|
|
||||
{h1: ✅} |
|
||||
|
|
||||
{h2:report 1} |
|
||||
- ✅ {32:OK} everything is fine |
|
||||
|
|
||||
{h2:report 2} |
|
||||
- ✅ {32:OK} nothing to see here |
|
||||
## grid 3
|
||||
|
|
||||
]],
|
||||
@ -324,8 +349,8 @@ describe(':checkhealth window', function()
|
||||
{14: } |
|
||||
{h1:test_plug. }|
|
||||
{h1:success1: }|
|
||||
{h1:require("test_plug. }|
|
||||
{h1:success1.health").check()}|
|
||||
{h1: }|
|
||||
{h1: ✅} |
|
||||
|
|
||||
{h2:report 1} |
|
||||
- ✅ {32:OK} everything is |
|
||||
@ -383,15 +408,15 @@ describe(':checkhealth window', function()
|
||||
^ |
|
||||
|
|
||||
|
|
||||
test_plug.success1: |
|
||||
require("test_plug.success1.health").check() |
|
||||
test_plug. |
|
||||
success1: |
|
||||
✅ |
|
||||
|
|
||||
report 1 |
|
||||
- ✅ OK everything is fine |
|
||||
|
|
||||
report 2 |
|
||||
- ✅ OK nothing to see here |
|
||||
|
|
||||
]]):format(
|
||||
top
|
||||
and [[
|
||||
|
@ -618,24 +618,53 @@ describe('vim.lsp.completion: item conversion', function()
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label = 'insert_replace_edit',
|
||||
kind = 9,
|
||||
textEdit = {
|
||||
newText = 'foobar',
|
||||
insert = {
|
||||
start = { line = 0, character = 7 },
|
||||
['end'] = { line = 0, character = 11 },
|
||||
},
|
||||
replace = {
|
||||
start = { line = 0, character = 0 },
|
||||
['end'] = { line = 0, character = 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
local expected = {
|
||||
abbr = ' this_thread',
|
||||
dup = 1,
|
||||
empty = 1,
|
||||
icase = 1,
|
||||
info = '',
|
||||
kind = 'Module',
|
||||
menu = '',
|
||||
abbr_hlgroup = '',
|
||||
word = 'this_thread',
|
||||
{
|
||||
abbr = ' this_thread',
|
||||
dup = 1,
|
||||
empty = 1,
|
||||
icase = 1,
|
||||
info = '',
|
||||
kind = 'Module',
|
||||
menu = '',
|
||||
abbr_hlgroup = '',
|
||||
word = 'this_thread',
|
||||
},
|
||||
{
|
||||
abbr = 'insert_replace_edit',
|
||||
dup = 1,
|
||||
empty = 1,
|
||||
icase = 1,
|
||||
info = '',
|
||||
kind = 'Module',
|
||||
menu = '',
|
||||
abbr_hlgroup = '',
|
||||
word = 'foobar',
|
||||
},
|
||||
}
|
||||
local result = complete(' std::this|', completion_list)
|
||||
eq(7, result.server_start_boundary)
|
||||
local item = result.items[1]
|
||||
item.user_data = nil
|
||||
eq(expected, item)
|
||||
for _, item in ipairs(result.items) do
|
||||
item.user_data = nil
|
||||
end
|
||||
eq(expected, result.items)
|
||||
end)
|
||||
|
||||
it('should search from start boundary to cursor position', function()
|
||||
|
@ -108,7 +108,7 @@ describe('vim.lsp.diagnostic', function()
|
||||
|
||||
exec_lua(function()
|
||||
diagnostic_bufnr = vim.uri_to_bufnr(fake_uri)
|
||||
local lines = { '1st line of text', '2nd line of text', 'wow', 'cool', 'more', 'lines' }
|
||||
local lines = { '1st line', '2nd line of text', 'wow', 'cool', 'more', 'lines' }
|
||||
vim.fn.bufload(diagnostic_bufnr)
|
||||
vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines)
|
||||
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
|
||||
@ -283,6 +283,36 @@ describe('vim.lsp.diagnostic', function()
|
||||
eq('Pull Diagnostic', diags[1].message)
|
||||
end)
|
||||
|
||||
it('handles multiline diagnostic ranges #33782', function()
|
||||
local diags = exec_lua(function()
|
||||
vim.lsp.diagnostic.on_diagnostic(nil, {
|
||||
kind = 'full',
|
||||
items = {
|
||||
_G.make_error('Pull Diagnostic', 0, 6, 1, 10),
|
||||
},
|
||||
}, {
|
||||
params = {
|
||||
textDocument = { uri = fake_uri },
|
||||
},
|
||||
uri = fake_uri,
|
||||
client_id = client_id,
|
||||
bufnr = diagnostic_bufnr,
|
||||
}, {})
|
||||
|
||||
return vim.diagnostic.get(diagnostic_bufnr)
|
||||
end)
|
||||
local lines = exec_lua(function()
|
||||
return vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, false)
|
||||
end)
|
||||
-- This test case must be run over a multiline diagnostic in which the start line is shorter
|
||||
-- than the end line, and the end_col exceeds the start line's length.
|
||||
eq(#lines[1], 8)
|
||||
eq(#lines[2], 16)
|
||||
eq(1, #diags)
|
||||
eq(6, diags[1].col)
|
||||
eq(10, diags[1].end_col)
|
||||
end)
|
||||
|
||||
it('severity defaults to error if missing', function()
|
||||
---@type vim.Diagnostic[]
|
||||
local diagnostics = exec_lua(function()
|
||||
|
@ -5,7 +5,6 @@ local t_lsp = require('test.functional.plugin.lsp.testutil')
|
||||
|
||||
local assert_log = t.assert_log
|
||||
local buf_lines = n.buf_lines
|
||||
local clear = n.clear
|
||||
local command = n.command
|
||||
local dedent = t.dedent
|
||||
local exec_lua = n.exec_lua
|
||||
@ -219,6 +218,25 @@ describe('LSP', function()
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('does not reuse an already-stopping client #33616', function()
|
||||
-- we immediately try to start a second client with the same name/root
|
||||
-- before the first one has finished shutting down; we must get a new id.
|
||||
local clients = exec_lua([[
|
||||
local client1 = vim.lsp.start({
|
||||
name = 'dup-test',
|
||||
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||
}, { attach = false })
|
||||
vim.lsp.get_client_by_id(client1):stop()
|
||||
local client2 = vim.lsp.start({
|
||||
name = 'dup-test',
|
||||
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||
}, { attach = false })
|
||||
return { client1, client2 }
|
||||
]])
|
||||
local c1, c2 = clients[1], clients[2]
|
||||
eq(false, c1 == c2, 'Expected a fresh client while the old one is stopping')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('basic_init test', function()
|
||||
@ -290,7 +308,6 @@ describe('LSP', function()
|
||||
it(
|
||||
"should set the client's offset_encoding when positionEncoding capability is supported",
|
||||
function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -573,7 +590,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('should detach buffer on bufwipe', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server()
|
||||
@ -606,7 +622,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('should not re-attach buffer if it was deleted in on_init #28575', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -637,7 +652,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('should allow on_lines + nvim_buf_delete during LSP initialization #28575', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
exec_lua(function()
|
||||
local initialized = false
|
||||
@ -812,7 +826,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('BufWritePre does not send notifications if server lacks willSave capabilities', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local messages = exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -837,7 +850,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('BufWritePre sends willSave / willSaveWaitUntil, applies textEdits', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -1252,6 +1264,65 @@ describe('LSP', function()
|
||||
}
|
||||
end)
|
||||
|
||||
it('request should not be pending for sync responses (in-process LS)', function()
|
||||
--- @type boolean
|
||||
local pending_request = exec_lua(function()
|
||||
local function server(dispatchers)
|
||||
local closing = false
|
||||
local srv = {}
|
||||
local request_id = 0
|
||||
|
||||
function srv.request(method, _params, callback, notify_reply_callback)
|
||||
if method == 'textDocument/formatting' then
|
||||
callback(nil, {})
|
||||
elseif method == 'initialize' then
|
||||
callback(nil, {
|
||||
capabilities = {
|
||||
textDocument = {
|
||||
formatting = true,
|
||||
},
|
||||
},
|
||||
})
|
||||
elseif method == 'shutdown' then
|
||||
callback(nil, nil)
|
||||
end
|
||||
request_id = request_id + 1
|
||||
if notify_reply_callback then
|
||||
notify_reply_callback(request_id)
|
||||
end
|
||||
return true, request_id
|
||||
end
|
||||
|
||||
function srv.notify(method)
|
||||
if method == 'exit' then
|
||||
dispatchers.on_exit(0, 15)
|
||||
end
|
||||
end
|
||||
function srv.is_closing()
|
||||
return closing
|
||||
end
|
||||
function srv.terminate()
|
||||
closing = true
|
||||
end
|
||||
|
||||
return srv
|
||||
end
|
||||
|
||||
local client_id = assert(vim.lsp.start({ cmd = server }))
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
|
||||
local ok, request_id = client:request('textDocument/formatting', {})
|
||||
assert(ok)
|
||||
|
||||
local has_pending = client.requests[request_id] ~= nil
|
||||
vim.lsp.stop_client(client_id)
|
||||
|
||||
return has_pending
|
||||
end)
|
||||
|
||||
eq(false, pending_request, 'expected no pending requests')
|
||||
end)
|
||||
|
||||
it('should trigger LspRequest autocmd when requests table changes', function()
|
||||
local expected_handlers = {
|
||||
{ NIL, {}, { method = 'finish', client_id = 1 } },
|
||||
@ -3810,7 +3881,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('opens the quickfix list with the right subtypes', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local qflist = exec_lua(function()
|
||||
local clangd_response = {
|
||||
@ -3958,7 +4028,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('opens the quickfix list with the right subtypes and details', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local qflist = exec_lua(function()
|
||||
local jdtls_response = {
|
||||
@ -4059,7 +4128,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('opens the quickfix list with the right supertypes', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local qflist = exec_lua(function()
|
||||
local clangd_response = {
|
||||
@ -4208,7 +4276,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('opens the quickfix list with the right supertypes and details', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local qflist = exec_lua(function()
|
||||
local jdtls_response = {
|
||||
@ -4562,7 +4629,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('fallback to command execution on resolve error', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -4607,7 +4673,6 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
it('resolves command property', function()
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
@ -4817,7 +4882,6 @@ describe('LSP', function()
|
||||
['file:///fake/uri1'] = 'Lens1',
|
||||
['file:///fake/uri2'] = 'Lens2',
|
||||
}
|
||||
clear()
|
||||
exec_lua(create_server_definition)
|
||||
|
||||
-- setup lsp
|
||||
@ -5327,7 +5391,6 @@ describe('LSP', function()
|
||||
|
||||
describe('vim.lsp.tagfunc', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
---@type lsp.Location[]
|
||||
local mock_locations = {
|
||||
{
|
||||
@ -5668,7 +5731,7 @@ describe('LSP', function()
|
||||
local created, changed, deleted
|
||||
|
||||
setup(function()
|
||||
clear()
|
||||
n.clear()
|
||||
created = exec_lua([[return vim.lsp.protocol.FileChangeType.Created]])
|
||||
changed = exec_lua([[return vim.lsp.protocol.FileChangeType.Changed]])
|
||||
deleted = exec_lua([[return vim.lsp.protocol.FileChangeType.Deleted]])
|
||||
@ -6247,7 +6310,7 @@ describe('LSP', function()
|
||||
end)
|
||||
|
||||
describe('vim.lsp.config() and vim.lsp.enable()', function()
|
||||
it('can merge settings from "*"', function()
|
||||
it('merges settings from "*"', function()
|
||||
eq(
|
||||
{
|
||||
name = 'foo',
|
||||
@ -6263,6 +6326,15 @@ describe('LSP', function()
|
||||
)
|
||||
end)
|
||||
|
||||
it('config("bogus") shows a hint', function()
|
||||
matches(
|
||||
'hint%: to resolve a config',
|
||||
pcall_err(exec_lua, function()
|
||||
vim.print(vim.lsp.config('non-existent-config'))
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
it('sets up an autocmd', function()
|
||||
eq(
|
||||
1,
|
||||
@ -6280,7 +6352,7 @@ describe('LSP', function()
|
||||
)
|
||||
end)
|
||||
|
||||
it('attaches to buffers', function()
|
||||
it('attaches to buffers when they are opened', function()
|
||||
exec_lua(create_server_definition)
|
||||
|
||||
local tmp1 = t.tmpname(true)
|
||||
@ -6329,6 +6401,67 @@ describe('LSP', function()
|
||||
)
|
||||
end)
|
||||
|
||||
it('attaches/detaches preexisting buffers', function()
|
||||
exec_lua(create_server_definition)
|
||||
|
||||
local tmp1 = t.tmpname(true)
|
||||
local tmp2 = t.tmpname(true)
|
||||
|
||||
exec_lua(function()
|
||||
vim.cmd.edit(tmp1)
|
||||
vim.bo.filetype = 'foo'
|
||||
_G.foo_buf = vim.api.nvim_get_current_buf()
|
||||
|
||||
vim.cmd.edit(tmp2)
|
||||
vim.bo.filetype = 'bar'
|
||||
_G.bar_buf = vim.api.nvim_get_current_buf()
|
||||
|
||||
local server = _G._create_server({
|
||||
handlers = {
|
||||
initialize = function(_, _, callback)
|
||||
callback(nil, { capabilities = {} })
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
vim.lsp.config('foo', {
|
||||
cmd = server.cmd,
|
||||
filetypes = { 'foo' },
|
||||
root_markers = { '.foorc' },
|
||||
})
|
||||
|
||||
vim.lsp.config('bar', {
|
||||
cmd = server.cmd,
|
||||
filetypes = { 'bar' },
|
||||
root_markers = { '.foorc' },
|
||||
})
|
||||
|
||||
vim.lsp.enable('foo')
|
||||
vim.lsp.enable('bar')
|
||||
end)
|
||||
|
||||
eq(
|
||||
{ 1, 'foo', 1, 'bar' },
|
||||
exec_lua(function()
|
||||
local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
|
||||
local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
|
||||
return { #foos, foos[1].name, #bars, bars[1].name }
|
||||
end)
|
||||
)
|
||||
|
||||
-- Now disable the 'foo' lsp and confirm that it's detached from the buffer it was previous
|
||||
-- attached to.
|
||||
exec_lua([[vim.lsp.enable('foo', false)]])
|
||||
eq(
|
||||
{ 0, 'foo', 1, 'bar' },
|
||||
exec_lua(function()
|
||||
local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
|
||||
local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
|
||||
return { #foos, 'foo', #bars, bars[1].name }
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
it('does not attach to buffers more than once if no root_dir', function()
|
||||
exec_lua(create_server_definition)
|
||||
|
||||
@ -6396,6 +6529,58 @@ describe('LSP', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
it('starts correct LSP and stops incorrect LSP when filetype changes', function()
|
||||
exec_lua(create_server_definition)
|
||||
|
||||
local tmp1 = t.tmpname(true)
|
||||
|
||||
exec_lua(function()
|
||||
local server = _G._create_server({
|
||||
handlers = {
|
||||
initialize = function(_, _, callback)
|
||||
callback(nil, { capabilities = {} })
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
vim.lsp.config('foo', {
|
||||
cmd = server.cmd,
|
||||
filetypes = { 'foo' },
|
||||
root_markers = { '.foorc' },
|
||||
})
|
||||
|
||||
vim.lsp.config('bar', {
|
||||
cmd = server.cmd,
|
||||
filetypes = { 'bar' },
|
||||
root_markers = { '.foorc' },
|
||||
})
|
||||
|
||||
vim.lsp.enable('foo')
|
||||
vim.lsp.enable('bar')
|
||||
|
||||
vim.cmd.edit(tmp1)
|
||||
end)
|
||||
|
||||
local count_clients = function()
|
||||
return exec_lua(function()
|
||||
local foos = vim.lsp.get_clients({ name = 'foo', bufnr = 0 })
|
||||
local bars = vim.lsp.get_clients({ name = 'bar', bufnr = 0 })
|
||||
return { #foos, 'foo', #bars, 'bar' }
|
||||
end)
|
||||
end
|
||||
|
||||
-- No filetype on the buffer yet, so no LSPs.
|
||||
eq({ 0, 'foo', 0, 'bar' }, count_clients())
|
||||
|
||||
-- Set the filetype to 'foo', confirm a LSP starts.
|
||||
exec_lua([[vim.bo.filetype = 'foo']])
|
||||
eq({ 1, 'foo', 0, 'bar' }, count_clients())
|
||||
|
||||
-- Set the filetype to 'bar', confirm a new LSP starts, and the old one goes away.
|
||||
exec_lua([[vim.bo.filetype = 'bar']])
|
||||
eq({ 0, 'foo', 1, 'bar' }, count_clients())
|
||||
end)
|
||||
|
||||
it('validates config on attach', function()
|
||||
local tmp1 = t.tmpname(true)
|
||||
exec_lua(function()
|
||||
@ -6491,5 +6676,135 @@ describe('LSP', function()
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
it('does not allow wildcards in config name', function()
|
||||
local err =
|
||||
'.../lsp.lua:0: name: expected non%-wildcard string, got foo%*%. Info: LSP config name cannot contain wildcard %("%*"%)'
|
||||
|
||||
matches(
|
||||
err,
|
||||
pcall_err(exec_lua, function()
|
||||
local _ = vim.lsp.config['foo*']
|
||||
end)
|
||||
)
|
||||
matches(
|
||||
err,
|
||||
pcall_err(exec_lua, function()
|
||||
vim.lsp.config['foo*'] = {}
|
||||
end)
|
||||
)
|
||||
matches(
|
||||
err,
|
||||
pcall_err(exec_lua, function()
|
||||
vim.lsp.config('foo*', {})
|
||||
end)
|
||||
)
|
||||
-- Exception for '*'
|
||||
pcall(exec_lua, function()
|
||||
vim.lsp.config('*', {})
|
||||
end)
|
||||
end)
|
||||
|
||||
it('correctly handles root_markers', function()
|
||||
--- Setup directories for testing
|
||||
-- root/
|
||||
-- ├── dir_a/
|
||||
-- │ ├── dir_b/
|
||||
-- │ │ ├── target
|
||||
-- │ │ └── marker_d
|
||||
-- │ ├── marker_b
|
||||
-- │ └── marker_c
|
||||
-- └── marker_a
|
||||
|
||||
---@param filepath string
|
||||
local function touch(filepath)
|
||||
local file = io.open(filepath, 'w')
|
||||
if file then
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
local tmp_root = tmpname(false)
|
||||
local marker_a = tmp_root .. '/marker_a'
|
||||
local dir_a = tmp_root .. '/dir_a'
|
||||
local marker_b = dir_a .. '/marker_b'
|
||||
local marker_c = dir_a .. '/marker_c'
|
||||
local dir_b = dir_a .. '/dir_b'
|
||||
local marker_d = dir_b .. '/marker_d'
|
||||
local target = dir_b .. '/target'
|
||||
|
||||
mkdir(tmp_root)
|
||||
touch(marker_a)
|
||||
mkdir(dir_a)
|
||||
touch(marker_b)
|
||||
touch(marker_c)
|
||||
mkdir(dir_b)
|
||||
touch(marker_d)
|
||||
touch(target)
|
||||
|
||||
exec_lua(create_server_definition)
|
||||
exec_lua(function()
|
||||
_G._custom_server = _G._create_server()
|
||||
end)
|
||||
|
||||
---@param root_markers (string|string[])[]
|
||||
---@param expected_root_dir string?
|
||||
local function markers_resolve_to(root_markers, expected_root_dir)
|
||||
exec_lua(function()
|
||||
vim.lsp.config['foo'] = {}
|
||||
vim.lsp.config('foo', {
|
||||
cmd = _G._custom_server.cmd,
|
||||
reuse_client = function()
|
||||
return false
|
||||
end,
|
||||
filetypes = { 'foo' },
|
||||
root_markers = root_markers,
|
||||
})
|
||||
vim.lsp.enable('foo')
|
||||
vim.cmd.edit(target)
|
||||
vim.bo.filetype = 'foo'
|
||||
end)
|
||||
retry(nil, 1000, function()
|
||||
eq(
|
||||
expected_root_dir,
|
||||
exec_lua(function()
|
||||
local clients = vim.lsp.get_clients()
|
||||
return clients[#clients].root_dir
|
||||
end)
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
markers_resolve_to({ 'marker_d' }, dir_b)
|
||||
markers_resolve_to({ 'marker_b' }, dir_a)
|
||||
markers_resolve_to({ 'marker_c' }, dir_a)
|
||||
markers_resolve_to({ 'marker_a' }, tmp_root)
|
||||
markers_resolve_to({ 'foo' }, nil)
|
||||
markers_resolve_to({ { 'marker_b', 'marker_a' }, 'marker_d' }, dir_a)
|
||||
markers_resolve_to({ 'marker_a', { 'marker_b', 'marker_d' } }, tmp_root)
|
||||
markers_resolve_to({ 'foo', { 'bar', 'baz' }, 'marker_d' }, dir_b)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('vim.lsp.is_enabled()', function()
|
||||
it('works', function()
|
||||
exec_lua(function()
|
||||
vim.lsp.config('foo', {
|
||||
cmd = { 'foo' },
|
||||
root_markers = { '.foorc' },
|
||||
})
|
||||
end)
|
||||
|
||||
-- LSP config defaults to disabled.
|
||||
eq(false, exec_lua([[return vim.lsp.is_enabled('foo')]]))
|
||||
|
||||
-- Confirm we can enable it.
|
||||
exec_lua([[vim.lsp.enable('foo')]])
|
||||
eq(true, exec_lua([[return vim.lsp.is_enabled('foo')]]))
|
||||
|
||||
-- And finally, disable it again.
|
||||
exec_lua([[vim.lsp.enable('foo', false)]])
|
||||
eq(false, exec_lua([[return vim.lsp.is_enabled('foo')]]))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
@ -131,7 +131,7 @@ describe(':Tutor', function()
|
||||
{0: }University. E-mail: {2:bware@mines.colorado.edu}. |
|
||||
]]
|
||||
|
||||
feed(':960<CR>zt')
|
||||
feed(':978<CR>zt')
|
||||
screen:expect(expected)
|
||||
end)
|
||||
end)
|
||||
|
@ -322,6 +322,27 @@ describe(':terminal buffer', function()
|
||||
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||
command('bd!')
|
||||
end)
|
||||
|
||||
it('correct size when switching buffers', function()
|
||||
local term_buf = api.nvim_get_current_buf()
|
||||
command('file foo | enew | vsplit')
|
||||
api.nvim_set_current_buf(term_buf)
|
||||
screen:expect([[
|
||||
tty ready │ |
|
||||
^rows: 5, cols: 25 │{4:~ }|
|
||||
│{4:~ }|*3
|
||||
{17:foo }{1:[No Name] }|
|
||||
|
|
||||
]])
|
||||
|
||||
feed('<C-^><C-W><C-O><C-^>')
|
||||
screen:expect([[
|
||||
tty ready |
|
||||
^rows: 5, cols: 25 |
|
||||
rows: 6, cols: 50 |
|
||||
|*4
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':terminal buffer', function()
|
||||
|
@ -440,19 +440,32 @@ describe('TUI', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
it('interprets <Esc>[27u as <Esc>', function()
|
||||
it('interprets <Esc> encoded with kitty keyboard protocol', function()
|
||||
child_session:request(
|
||||
'nvim_exec2',
|
||||
[[
|
||||
nnoremap <M-;> <Nop>
|
||||
nnoremap <Esc> AESC<Esc>
|
||||
nnoremap <C-Esc> ACtrlEsc<Esc>
|
||||
nnoremap <D-Esc> ASuperEsc<Esc>
|
||||
nnoremap ; Asemicolon<Esc>
|
||||
]],
|
||||
{}
|
||||
)
|
||||
-- Works with no modifier
|
||||
feed_data('\027[27u;')
|
||||
expect_child_buf_lines({ 'ESCsemicolon' })
|
||||
-- Works with Ctrl modifier
|
||||
feed_data('\027[27;5u')
|
||||
expect_child_buf_lines({ 'ESCsemicolonCtrlEsc' })
|
||||
-- Works with Super modifier
|
||||
feed_data('\027[27;9u')
|
||||
expect_child_buf_lines({ 'ESCsemicolonCtrlEscSuperEsc' })
|
||||
-- Works with NumLock modifier (which should be the same as no modifier) #33799
|
||||
feed_data('\027[27;129u')
|
||||
expect_child_buf_lines({ 'ESCsemicolonCtrlEscSuperEscESC' })
|
||||
screen:expect([[
|
||||
ESCsemicolo^n |
|
||||
ESCsemicolonCtrlEscSuperEscES^C |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] [+] }|
|
||||
|
|
||||
@ -461,6 +474,7 @@ describe('TUI', function()
|
||||
-- <Esc>; should be recognized as <M-;> when <M-;> is mapped
|
||||
feed_data('\027;')
|
||||
screen:expect_unchanged()
|
||||
expect_child_buf_lines({ 'ESCsemicolonCtrlEscSuperEscESC' })
|
||||
end)
|
||||
|
||||
it('interprets <Esc><Nul> as <M-C-Space> #17198', function()
|
||||
@ -486,6 +500,38 @@ describe('TUI', function()
|
||||
{3:-- INSERT --} |
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
child_session:request('nvim_set_keymap', 'i', '\031', '!!!', {})
|
||||
feed_data('\031')
|
||||
screen:expect([[
|
||||
{6:^G^V^M}!!!^ |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] [+] }|
|
||||
{3:-- INSERT --} |
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
child_session:request('nvim_buf_delete', 0, { force = true })
|
||||
child_session:request('nvim_set_option_value', 'laststatus', 0, {})
|
||||
child_session:request(
|
||||
'nvim_call_function',
|
||||
'jobstart',
|
||||
{ { testprg('shell-test'), 'INTERACT' }, { term = true } }
|
||||
)
|
||||
screen:expect([[
|
||||
interact $ ^ |
|
||||
|*4
|
||||
{3:-- TERMINAL --} |*2
|
||||
]])
|
||||
-- mappings for C0 control codes should work in Terminal mode #33750
|
||||
child_session:request('nvim_set_keymap', 't', '\031', '<Cmd>new<CR>', {})
|
||||
feed_data('\031')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{4:~ }|
|
||||
{5:[No Name] }|
|
||||
interact $ |
|
||||
|*2
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
local function test_mouse_wheel(esc)
|
||||
@ -1198,7 +1244,6 @@ describe('TUI', function()
|
||||
pending('tty-test complains about not owning the terminal -- actions/runner#241')
|
||||
end
|
||||
screen:set_default_attr_ids({
|
||||
[1] = { reverse = true }, -- focused cursor
|
||||
[3] = { bold = true },
|
||||
[19] = { bold = true, background = 121, foreground = 0 }, -- StatusLineTerm
|
||||
})
|
||||
|
@ -2950,6 +2950,22 @@ describe('extmark decorations', function()
|
||||
{1:~ }|*4
|
||||
|
|
||||
]])
|
||||
-- Correct relativenumber for line below concealed line #33694
|
||||
feed('4Gk')
|
||||
screen:expect([[
|
||||
{2: 2 }for _,item in ipairs(items) do |
|
||||
{2:3 } if h^l_id_cell ~= nil then |
|
||||
{2: 1 } hl_id = hl_id_cell |
|
||||
{2: 3 } for _ = 1, (count or 1) do |
|
||||
{2: 4 } local cell = line[colpos] |
|
||||
{2: 5 } cell.text = text |
|
||||
{2: 6 } cell.hl_id = hl_id |
|
||||
{2: 7 } colpos = colpos+1 |
|
||||
{2: 8 } end |
|
||||
{2: 9 }end |
|
||||
{1:~ }|*4
|
||||
|
|
||||
]])
|
||||
-- Also with above virtual line #32744
|
||||
command('set nornu')
|
||||
api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = { { { "virt_below 4" } } } })
|
||||
@ -3050,6 +3066,28 @@ describe('extmark decorations', function()
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('redraws extmark that starts and ends outisde the screen', function()
|
||||
local lines = vim.split(('1'):rep(20), '', { plain = true })
|
||||
api.nvim_buf_set_lines(0, 0, -1, true, lines)
|
||||
api.nvim_buf_set_extmark(0, ns, 0, 0, { hl_group = 'ErrorMsg', end_row = 19, end_col = 0 })
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{4:^1} |
|
||||
{4:1} |*13
|
||||
|
|
||||
]]
|
||||
})
|
||||
feed('<C-e>')
|
||||
-- Newly visible line should also have the highlight.
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{4:^1} |
|
||||
{4:1} |*13
|
||||
|
|
||||
]]
|
||||
})
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('decorations: inline virtual text', function()
|
||||
@ -5913,6 +5951,33 @@ if (h->n_buckets < new_n_buckets) { // expand
|
||||
]]
|
||||
})
|
||||
end)
|
||||
|
||||
it("not revealed before skipcol scrolling up with 'smoothscroll'", function()
|
||||
api.nvim_set_option_value('smoothscroll', true, {})
|
||||
api.nvim_buf_set_lines(0, 0, -1, false, { ('x'):rep(screen._width * 2) })
|
||||
api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines_above = true, virt_lines = { { { 'VIRT1' } } } } )
|
||||
feed('<C-E>')
|
||||
screen:expect([[
|
||||
{1:<<<}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^x|
|
||||
{1:~ }|*10
|
||||
|
|
||||
]])
|
||||
feed('<C-Y>')
|
||||
screen:expect([[
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^x|
|
||||
{1:~ }|*9
|
||||
|
|
||||
]])
|
||||
feed('<C-Y>')
|
||||
screen:expect([[
|
||||
VIRT1 |
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx^x|
|
||||
{1:~ }|*8
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('decorations: signs', function()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user