Merge #11604 "LSP: shrink API, improve docs"

This commit is contained in:
Justin M. Keyes 2020-01-01 06:30:29 -08:00 committed by GitHub
commit 070bd3ea23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1247 additions and 768 deletions

View File

@ -1,8 +1,7 @@
[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io) [![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io)
[Wiki](https://github.com/neovim/neovim/wiki) |
[Documentation](https://neovim.io/doc) | [Documentation](https://neovim.io/doc) |
[Chat/Discussion](https://gitter.im/neovim/neovim) | [Chat](https://gitter.im/neovim/neovim) |
[Twitter](https://twitter.com/Neovim) [Twitter](https://twitter.com/Neovim)
[![Travis build status](https://travis-ci.org/neovim/neovim.svg?branch=master)](https://travis-ci.org/neovim/neovim) [![Travis build status](https://travis-ci.org/neovim/neovim.svg?branch=master)](https://travis-ci.org/neovim/neovim)
@ -114,17 +113,9 @@ Project layout
License License
------- -------
Neovim is licensed under the terms of the Apache 2.0 license, except for Neovim contributions since [b17d96][license-commit] are licensed under the
parts that were contributed under the Vim license. Apache 2.0 license, except for contributions copied from Vim (identified by the
`vim-patch` token). See LICENSE for details.
- Contributions committed before [b17d96][license-commit] remain under the Vim
license.
- Contributions committed after [b17d96][license-commit] are licensed under
Apache 2.0 unless those contributions were copied from Vim (identified in
the commit logs by the `vim-patch` token).
See `LICENSE` for details.
Vim is Charityware. You can use and copy it as much as you like, but you are Vim is Charityware. You can use and copy it as much as you like, but you are
encouraged to make a donation for needy children in Uganda. Please see the encouraged to make a donation for needy children in Uganda. Please see the

View File

@ -569,7 +569,8 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
occurred, the values from all preceding calls will still occurred, the values from all preceding calls will still
be returned. be returned.
nvim_call_dict_function({dict}, {fn}, {args}) *nvim_call_dict_function()* *nvim_call_dict_function()*
nvim_call_dict_function({dict}, {fn}, {args})
Calls a VimL |Dictionary-function| with the given arguments. Calls a VimL |Dictionary-function| with the given arguments.
On execution error: fails with VimL error, does not update On execution error: fails with VimL error, does not update
@ -1878,7 +1879,8 @@ nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()*
Return: ~ Return: ~
Variable value Variable value
nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()* *nvim_buf_get_virtual_text()*
nvim_buf_get_virtual_text({buffer}, {lnum})
Get the virtual text (annotation) for a buffer line. Get the virtual text (annotation) for a buffer line.
The virtual text is returned as list of lists, whereas the The virtual text is returned as list of lists, whereas the
@ -2300,7 +2302,8 @@ nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()*
Return: ~ Return: ~
List of windows in `tabpage` List of windows in `tabpage`
nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()* *nvim_tabpage_set_var()*
nvim_tabpage_set_var({tabpage}, {name}, {value})
Sets a tab-scoped (t:) variable Sets a tab-scoped (t:) variable
Parameters: ~ Parameters: ~

View File

@ -3771,8 +3771,8 @@ feedkeys({string} [, {mode}]) *feedkeys()*
and "\..." notation |expr-quote|. For example, and "\..." notation |expr-quote|. For example,
feedkeys("\<CR>") simulates pressing of the <Enter> key. But feedkeys("\<CR>") simulates pressing of the <Enter> key. But
feedkeys('\<CR>') pushes 5 characters. feedkeys('\<CR>') pushes 5 characters.
A special code that might be useful is <Ignore>, it exits the The |<Ignore>| keycode may be used to exit the
wait for a character without doing anything. *<Ignore>* wait-for-character without doing anything.
{mode} is a String, which can contain these character flags: {mode} is a String, which can contain these character flags:
'm' Remap keys. This is default. If {mode} is absent, 'm' Remap keys. This is default. If {mode} is absent,

View File

@ -339,6 +339,8 @@ notation meaning equivalent decimal value(s) ~
<EOL> end-of-line (can be <CR>, <LF> or <CR><LF>, <EOL> end-of-line (can be <CR>, <LF> or <CR><LF>,
depends on system and 'fileformat') *<EOL>* depends on system and 'fileformat') *<EOL>*
<Ignore> cancel wait-for-character *<Ignore>*
<NOP> no-op: do nothing (useful in mappings) *<Nop>*
<Up> cursor-up *cursor-up* *cursor_up* <Up> cursor-up *cursor-up* *cursor_up*
<Down> cursor-down *cursor-down* *cursor_down* <Down> cursor-down *cursor-down* *cursor_down*

File diff suppressed because it is too large Load Diff

View File

@ -522,10 +522,9 @@ single CTRL-V (you have to type CTRL-V two times).
You can create an empty {rhs} by typing nothing after a single CTRL-V (you You can create an empty {rhs} by typing nothing after a single CTRL-V (you
have to type CTRL-V two times). Unfortunately, you cannot do this in a vimrc have to type CTRL-V two times). Unfortunately, you cannot do this in a vimrc
file. file.
*<Nop>* |<Nop>|
An easier way to get a mapping that doesn't produce anything, is to use An easier way to get a mapping that doesn't produce anything, is to use
"<Nop>" for the {rhs}. This only works when the |<>| notation is enabled. "<Nop>" for the {rhs}. For example, to disable function key 8: >
For example, to make sure that function key 8 does nothing at all: >
:map <F8> <Nop> :map <F8> <Nop>
:map! <F8> <Nop> :map! <F8> <Nop>
< <

View File

@ -671,21 +671,20 @@ being disabled. Remove the 'C' flag from the 'cpoptions' option to enable it.
*E471* > *E471* >
Argument required Argument required
This happens when an Ex command with mandatory argument(s) was executed, but Ex command was executed without a mandatory argument(s).
no argument has been specified.
*E474* *E475* *E983* > *E474* *E475* *E983* >
Invalid argument Invalid argument
Invalid argument: {arg} Invalid argument: {arg}
Duplicate argument: {arg} Duplicate argument: {arg}
Ex command or function has been executed, but an invalid argument was Ex command or function was given an invalid argument. Or |jobstart()| or
specified. Or a non-executable command was given to |system()|. |system()| was given a non-executable command.
*E488* > *E488* >
Trailing characters Trailing characters
An argument has been added to an Ex command that does not permit one. An argument was given to an Ex command that does not permit one.
*E477* *E478* > *E477* *E478* >
No ! allowed No ! allowed

View File

@ -1105,7 +1105,7 @@ Finally, navigate to a different webpage, new.com. The history is
- third.com - third.com
- new.com <-- - new.com <--
When the jumpoptions includes "stack", this is the behavior of neovim as well. When the jumpoptions includes "stack", this is the behavior of Nvim as well.
That is, given a jumplist like the following in which CTRL-O has been used to That is, given a jumplist like the following in which CTRL-O has been used to
move back three times to location X move back three times to location X
@ -1117,8 +1117,8 @@ move back three times to location X
2 213 2 src/nvim/mark.c 2 213 2 src/nvim/mark.c
3 181 0 src/nvim/mark.c 3 181 0 src/nvim/mark.c
jumping to location Y results in the locations after the current locations being jumping to (new) location Y results in the locations after the current
removed: locations being removed:
jump line col file/text jump line col file/text
3 1260 8 src/nvim/mark.c 3 1260 8 src/nvim/mark.c

View File

@ -4543,15 +4543,15 @@ A jump table for the options with a short description can be found at |Q_op|.
*'pumheight'* *'ph'* *'pumheight'* *'ph'*
'pumheight' 'ph' number (default 0) 'pumheight' 'ph' number (default 0)
global global
Determines the maximum number of items to show in the popup menu for Maximum number of items to show in the popup menu
Insert mode completion. When zero as much space as available is used. (|ins-completion-menu|). Zero means "use available screen space".
|ins-completion-menu|.
*'pumwidth'* *'pw'* *'pumwidth'* *'pw'*
'pumwidth' 'pw' number (default 15) 'pumwidth' 'pw' number (default 15)
global global
Determines the minium width to use for the popup menu for Insert mode Minimum width for the popup menu (|ins-completion-menu|). If the
completion. |ins-completion-menu|. cursor column + 'pumwidth' exceeds screen width, the popup menu is
nudged to fit on the screen.
*'pyxversion'* *'pyx'* *'pyxversion'* *'pyx'*
'pyxversion' 'pyx' number (default depends on the build) 'pyxversion' 'pyx' number (default depends on the build)

View File

@ -195,20 +195,20 @@ Normal commands:
"Outline": Type |gO| in |:Man| and |:help| pages to see a document outline. "Outline": Type |gO| in |:Man| and |:help| pages to see a document outline.
Options: Options:
'cpoptions' flags: |cpo-_| 'cpoptions' flags: |cpo-_|
'display' flag `msgsep` to minimize scrolling when showing messages 'display' flags: "msgsep" minimizes scrolling when showing messages
'guicursor' works in the terminal 'guicursor' works in the terminal
'fillchars' local to window. flags: `msgsep` (see 'display' above) and `eob` 'fillchars' flags: "msgsep" (see 'display'), "eob" for |hl-EndOfBuffer|
for |hl-EndOfBuffer| marker marker, "foldopen", "foldsep", "foldclose"
'inccommand' shows interactive results for |:substitute|-like commands 'inccommand' shows interactive results for |:substitute|-like commands
'listchars' local to window 'listchars' local to window
'pumblend' pseudo-transparent popupmenu 'pumblend' pseudo-transparent popupmenu
'scrollback' 'scrollback'
'signcolumn' supports up to 9 dynamic/fixed columns 'signcolumn' supports up to 9 dynamic/fixed columns
'statusline' supports unlimited alignment sections 'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click 'tabline' %@Func@foo%X can call any function on mouse-click
'wildoptions' `pum` flag to use popupmenu for wildmode completion 'wildoptions' "pum" flag to use popupmenu for wildmode completion
'winblend' pseudo-transparency in floating windows |api-floatwin| 'winblend' pseudo-transparency in floating windows |api-floatwin|
'winhighlight' window-local highlights 'winhighlight' window-local highlights
Signs: Signs:
@ -336,16 +336,12 @@ Macro/|recording| behavior
Motion: Motion:
The |jumplist| avoids useless/phantom jumps. The |jumplist| avoids useless/phantom jumps.
When the new option |jumpoptions| includes 'stack', the jumplist behaves
like the tagstack or history in a web browser--jumping from the middle of
the jumplist discards the locations after the jumped-from position
(|jumplist-stack|).
Normal commands: Normal commands:
|Q| is the same as |gQ| |Q| is the same as |gQ|
Options: Options:
'ttimeout', 'ttimeoutlen' behavior was simplified 'ttimeout', 'ttimeoutlen' behavior was simplified
|jumpoptions| "stack" behavior
Shell: Shell:
Shell output (|:!|, |:make|, …) is always routed through the UI, so it Shell output (|:!|, |:make|, …) is always routed through the UI, so it

View File

@ -204,95 +204,153 @@ local function text_document_did_open_handler(bufnr, client)
client.notify('textDocument/didOpen', params) client.notify('textDocument/didOpen', params)
end end
--- LSP client object.
---
--- - Methods:
---
--- - request(method, params, [callback])
--- Send a request to the server. If callback is not specified, it will use
--- {client.callbacks} to try to find a callback. If one is not found there,
--- then an error will occur.
--- This is a thin wrapper around {client.rpc.request} with some additional
--- checking.
--- Returns a boolean to indicate if the notification was successful. If it
--- is false, then it will always be false (the client has shutdown).
--- If it was successful, then it will return the request id as the second
--- result. You can use this with `notify("$/cancel", { id = request_id })`
--- to cancel the request. This helper is made automatically with
--- |vim.lsp.buf_request()|
--- Returns: status, [client_id]
---
--- - notify(method, params)
--- This is just {client.rpc.notify}()
--- Returns a boolean to indicate if the notification was successful. If it
--- is false, then it will always be false (the client has shutdown).
--- Returns: status
---
--- - cancel_request(id)
--- This is just {client.rpc.notify}("$/cancelRequest", { id = id })
--- Returns the same as `notify()`.
---
--- - stop([force])
--- Stop a client, optionally with force.
--- By default, it will just ask the server to shutdown without force.
--- If you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown.
---
--- - is_stopped()
--- Returns true if the client is fully stopped.
---
--- - Members
--- - id (number): 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.
---
--- - offset_encoding (string): The encoding used for communicating
--- with the server. You can modify this in the `on_init` method
--- before text is sent to the server.
---
--- - callbacks (table): The callbacks used by the client as
--- described in |lsp-callbacks|.
---
--- - config (table): copy of the table that was passed by the user
--- to |vim.lsp.start_client()|.
---
--- - server_capabilities (table): Response from the server sent on
--- `initialize` describing the server's capabilities.
---
--- - resolved_capabilities (table): Normalized table of
--- capabilities that we have detected based on the initialize
--- response from the server in `server_capabilities`.
function lsp.client()
error()
end
--- Start a client and initialize it. --- Starts and initializes a client with the given configuration.
-- Its arguments are passed via a configuration object. ---
-- --- Parameters `cmd` and `root_dir` are required.
-- Mandatory parameters: ---
-- --@param root_dir: (required, string) Directory where the LSP server will base
-- root_dir: {string} specifying the directory where the LSP server will base --- its rootUri on initialization.
-- as its rootUri on initialization. ---
-- --@param cmd: (required, string or list treated like |jobstart()|) Base command
-- cmd: {string} or {list} which is the base command to execute for the LSP. A --- that initiates the LSP client.
-- string will be run using |'shell'| and a list will be interpreted as a bare ---
-- command with arguments passed. This is the same as |jobstart()|. --@param cmd_cwd: (string, default=|getcwd()|) Directory to launch
-- --- the `cmd` process. Not related to `root_dir`.
-- Optional parameters: ---
--@param cmd_env: (table) Environment flags to pass to the LSP on
-- cmd_cwd: {string} specifying the directory to launch the `cmd` process. This --- spawn. Can be specified using keys like a map or as a list with `k=v`
-- is not related to `root_dir`. By default, |getcwd()| is used. --- pairs or both. Non-string values are coerced to string.
-- --- Example:
-- cmd_env: {table} specifying the environment flags to pass to the LSP on --- <pre>
-- spawn. This can be specified using keys like a map or as a list with `k=v` --- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }
-- pairs or both. Non-string values are coerced to a string. --- </pre>
-- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`. ---
-- --@param capabilities Map overriding the default capabilities defined by
-- capabilities: A {table} which will be used instead of --- |vim.lsp.protocol.make_client_capabilities()|, passed to the language
-- `vim.lsp.protocol.make_client_capabilities()` which contains neovim's --- server on initialization. Hint: use make_client_capabilities() and modify
-- default capabilities and passed to the language server on initialization. --- its result.
-- You'll probably want to use make_client_capabilities() and modify the --- - Note: To send an empty dictionary use
-- result. --- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an
-- NOTE: --- array.
-- To send an empty dictionary, you should use ---
-- `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as --@param callbacks Map of language server method names to
-- an array. --- `function(err, method, params, client_id)` handler. Invoked for:
-- --- - Notifications from the server, where `err` will always be `nil`.
-- callbacks: A {table} of whose keys are language server method names and the --- - Requests initiated by the server. For these you can respond by returning
-- values are `function(err, method, params, client_id)`. --- two values: `result, err` where err must be shaped like a RPC error,
-- This will be called for: --- i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to
-- - notifications from the server, where `err` will always be `nil` --- help with this.
-- - requests initiated by the server. For these, you can respond by returning --- - Default callback for client requests not explicitly specifying
-- two values: `result, err`. The err must be in the format of an RPC error, --- a callback.
-- which is `{ code, message, data? }`. You can use |vim.lsp.rpc_response_error()| ---
-- to help with this. --@param init_options Values to pass in the initialization request
-- - as a callback for requests initiated by the client if the request doesn't --- as `initializationOptions`. See `initialize` in the LSP spec.
-- explicitly specify a callback. ---
-- --@param name (string, default=client-id) Name in log messages.
-- init_options: A {table} of values to pass in the initialization request ---
-- as `initializationOptions`. See the `initialize` in the LSP spec. --@param offset_encoding (default="utf-16") One of "utf-8", "utf-16",
-- --- or "utf-32" which is the encoding that the LSP server expects. Client does
-- name: A {string} used in log messages. Defaults to {client_id} --- not verify this is correct.
-- ---
-- offset_encoding: One of 'utf-8', 'utf-16', or 'utf-32' which is the --@param on_error Callback with parameters (code, ...), invoked
-- encoding that the LSP server expects. By default, it is 'utf-16' as --- when the client operation throws an error. `code` is a number describing
-- specified in the LSP specification. The client does not verify this --- the error. Other arguments may be passed depending on the error kind. See
-- is correct. --- |vim.lsp.client_errors| for possible errors.
-- --- Use `vim.lsp.client_errors[code]` to get human-friendly name.
-- on_error(code, ...): A function for handling errors thrown by client ---
-- operation. {code} is a number describing the error. Other arguments may be --@param before_init Callback with parameters (initialize_params, config)
-- passed depending on the error kind. @see |vim.lsp.client_errors| for --- invoked before the LSP "initialize" phase, where `params` contains the
-- possible errors. `vim.lsp.client_errors[code]` can be used to retrieve a --- parameters being sent to the server and `config` is the config that was
-- human understandable string. --- passed to `start_client()`. You can use this to modify parameters before
-- --- they are sent.
-- before_init(initialize_params, config): A function which is called *before* ---
-- the request `initialize` is completed. `initialize_params` contains --@param on_init Callback (client, initialize_result) invoked after LSP
-- the parameters we are sending to the server and `config` is the config that --- "initialize", where `result` is a table of `capabilities` and anything else
-- was passed to `start_client()` for convenience. You can use this to modify --- the server may send. For example, clangd sends
-- parameters before they are sent. --- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was
-- --- sent to it. You can only modify the `client.offset_encoding` here before
-- on_init(client, initialize_result): A function which is called after the --- any notifications are sent.
-- request `initialize` is completed. `initialize_result` contains ---
-- `capabilities` and anything else the server may send. For example, `clangd` --@param on_exit Callback (code, signal, client_id) invoked on client
-- sends `result.offsetEncoding` if `capabilities.offsetEncoding` was sent to --- exit.
-- it. --- - code: exit code of the process
-- --- - signal: number describing the signal used to terminate (if any)
-- on_exit(code, signal, client_id): A function which is called after the --- - client_id: client handle
-- client has exited. code is the exit code of the process, and signal is a ---
-- number describing the signal used to terminate (if any). --@param on_attach Callback (client, bufnr) invoked when client
-- --- attaches to a buffer.
-- on_attach(client, bufnr): A function which is called after the client is ---
-- attached to a buffer. --@param trace: "off" | "messages" | "verbose" | nil passed directly to the language
-- --- server in the initialize request. Invalid/empty values will default to "off"
-- trace: 'off' | 'messages' | 'verbose' | nil passed directly to the language ---
-- server in the initialize request. Invalid/empty values will default to 'off' --@returns Client id. |vim.lsp.get_client_by_id()| Note: client is only
-- --- available after it has been initialized, which may happen after a small
-- @returns client_id You can use |vim.lsp.get_client_by_id()| to get the --- delay (or never if there is an error). Use `on_init` to do any actions once
-- actual client. --- the client has been initialized.
--
-- NOTE: The client is only available *after* it has been initialized, which
-- may happen after a small delay (or never if there is an error).
-- For this reason, you may want to use `on_init` to do any actions once the
-- client has been initialized.
function lsp.start_client(config) function lsp.start_client(config)
local cleaned_config = validate_client_config(config) local cleaned_config = validate_client_config(config)
local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding
@ -402,8 +460,8 @@ function lsp.start_client(config)
initializationOptions = config.init_options; initializationOptions = config.init_options;
-- The capabilities provided by the client (editor or tool) -- The capabilities provided by the client (editor or tool)
capabilities = config.capabilities or protocol.make_client_capabilities(); capabilities = config.capabilities or protocol.make_client_capabilities();
-- The initial trace setting. If omitted trace is disabled ('off'). -- The initial trace setting. If omitted trace is disabled ("off").
-- trace = 'off' | 'messages' | 'verbose'; -- trace = "off" | "messages" | "verbose";
trace = valid_traces[config.trace] or 'off'; trace = valid_traces[config.trace] or 'off';
-- The workspace folders configured in the client when the server starts. -- The workspace folders configured in the client when the server starts.
-- This property is only available if the client supports workspace folders. -- This property is only available if the client supports workspace folders.
@ -634,10 +692,13 @@ function lsp._text_document_did_save_handler(bufnr)
end) end)
end end
-- Implements the textDocument/did* notifications required to track a buffer --- Implements the `textDocument/did…` notifications required to track a buffer
-- for any language server. --- for any language server.
-- @param bufnr [number] buffer handle or 0 for current ---
-- @param client_id [number] the client id --- Without calling this, the server won't be notified of changes to a buffer.
---
--- @param bufnr (number) Buffer handle, or 0 for current
--- @param client_id (number) Client id
function lsp.buf_attach_client(bufnr, client_id) function lsp.buf_attach_client(bufnr, client_id)
validate { validate {
bufnr = {bufnr, 'n', true}; bufnr = {bufnr, 'n', true};
@ -683,62 +744,58 @@ function lsp.buf_attach_client(bufnr, client_id)
return true return true
end end
-- Check if a buffer is attached for a particular client. --- Checks if a buffer is attached for a particular client.
-- @param bufnr [number] buffer handle or 0 for current ---
-- @param client_id [number] the client id ---@param bufnr (number) Buffer handle, or 0 for current
---@param client_id (number) the client id
function lsp.buf_is_attached(bufnr, client_id) function lsp.buf_is_attached(bufnr, client_id)
return (all_buffer_active_clients[bufnr] or {})[client_id] == true return (all_buffer_active_clients[bufnr] or {})[client_id] == true
end end
-- Look up an active client by its id, returns nil if it is not yet initialized --- Gets an active client by id, or nil if the id is invalid or the
-- or is not a valid id. --- client is not yet initialized.
-- @param client_id number the client id. ---
--@param client_id client id number
---
--@return |vim.lsp.client| object, or nil
function lsp.get_client_by_id(client_id) function lsp.get_client_by_id(client_id)
return active_clients[client_id] return active_clients[client_id]
end end
-- Stop a client by its id, optionally with force. --- Stops a client(s).
-- You can also use the `stop()` function on a client if you already have ---
-- access to it. --- You can also use the `stop()` function on a |vim.lsp.client| object.
-- By default, it will just ask the server to shutdown without force. --- To stop all clients:
-- If you request to stop a client which has previously been requested to shutdown, ---
-- it will automatically force shutdown. --- <pre>
-- @param client_id number the client id. --- vim.lsp.stop_client(lsp.get_active_clients())
-- @param force boolean (optional) whether to use force or request shutdown --- </pre>
---
--- By default asks the server to shutdown, unless stop was requested
--- already for this client, then force-shutdown is attempted.
---
--@param client_id client id or |vim.lsp.client| object, or list thereof
--@param force boolean (optional) shutdown forcefully
function lsp.stop_client(client_id, force) function lsp.stop_client(client_id, force)
local client local ids = type(client_id) == 'table' and client_id or {client_id}
client = active_clients[client_id] for _, id in ipairs(ids) do
if client then if type(id) == 'table' and id.stop ~= nil then
client.stop(force) id.stop(force)
return elseif active_clients[id] then
end active_clients[id].stop(force)
client = uninitialized_clients[client_id] elseif uninitialized_clients[id] then
if client then uninitialized_clients[id].stop(true)
client.stop(true) end
end end
end end
-- Returns a list of all the active clients. --- Gets all active clients.
---
--@return Table of |vim.lsp.client| objects
function lsp.get_active_clients() function lsp.get_active_clients()
return vim.tbl_values(active_clients) return vim.tbl_values(active_clients)
end end
-- Stop all the clients, optionally with force.
-- You can also use the `stop()` function on a client if you already have
-- access to it.
-- By default, it will just ask the server to shutdown without force.
-- If you request to stop a client which has previously been requested to shutdown,
-- it will automatically force shutdown.
-- @param force boolean (optional) whether to use force or request shutdown
function lsp.stop_all_clients(force)
for _, client in pairs(uninitialized_clients) do
client.stop(true)
end
for _, client in pairs(active_clients) do
client.stop(force)
end
end
function lsp._vim_exit_handler() function lsp._vim_exit_handler()
log.info("exit_handler", active_clients) log.info("exit_handler", active_clients)
for _, client in pairs(uninitialized_clients) do for _, client in pairs(uninitialized_clients) do
@ -761,17 +818,21 @@ end
nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
---
--- Buffer level client functions.
---
--- Send a request to a server and return the response --- Sends an async request for all active clients attached to the
-- @param bufnr [number] Buffer handle or 0 for current. --- buffer.
-- @param method [string] Request method name ---
-- @param params [table|nil] Parameters to send to the server --@param bufnr (number) Buffer handle, or 0 for current.
-- @param callback [function|nil] Request callback (or uses the client's callbacks) --@param method (string) LSP method name
--@param params (optional, table) Parameters to send to the server
--@param callback (optional, functionnil) Handler
-- `function(err, method, params, client_id)` for this request. Defaults
-- to the client callback in `client.callbacks`. See |lsp-callbacks|.
-- --
-- @returns: client_request_ids, cancel_all_requests --@returns 2-tuple:
--- - Map of client-id:request-id pairs for all successful requests.
--- - Function which can be used to cancel all the requests. You could instead
--- iterate all clients and call their `cancel_request()` methods.
function lsp.buf_request(bufnr, method, params, callback) function lsp.buf_request(bufnr, method, params, callback)
validate { validate {
bufnr = { bufnr, 'n', true }; bufnr = { bufnr, 'n', true };
@ -789,31 +850,39 @@ function lsp.buf_request(bufnr, method, params, callback)
end end
end) end)
local function cancel_all_requests() local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do for client_id, request_id in pairs(client_request_ids) do
local client = active_clients[client_id] local client = active_clients[client_id]
client.cancel_request(request_id) client.cancel_request(request_id)
end end
end end
return client_request_ids, cancel_all_requests return client_request_ids, _cancel_all_requests
end end
--- Send a request to a server and wait for the response. --- Sends a request to a server and waits for the response.
-- @param bufnr [number] Buffer handle or 0 for current. ---
-- @param method [string] Request method name --- Calls |vim.lsp.buf_request()| but blocks Nvim while awaiting the result.
-- @param params [string] Parameters to send to the server --- Parameters are the same as |vim.lsp.buf_request()| but the return result is
-- @param timeout_ms [number|100] Maximum ms to wait for a result --- different. Wait maximum of {timeout_ms} (default 100) ms.
-- ---
-- @returns: The table of {[client_id] = request_result} --@param bufnr (number) Buffer handle, or 0 for current.
--@param method (string) LSP method name
--@param params (optional, table) Parameters to send to the server
--@param timeout_ms (optional, number, default=100) Maximum time in
--- milliseconds to wait for a result.
---
--@returns Map of client_id:request_result. On timeout, cancel or error,
--- returns `(nil, err)` where `err` is a string describing the failure
--- reason.
function lsp.buf_request_sync(bufnr, method, params, timeout_ms) function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
local request_results = {} local request_results = {}
local result_count = 0 local result_count = 0
local function callback(err, _method, result, client_id) local function _callback(err, _method, result, client_id)
request_results[client_id] = { error = err, result = result } request_results[client_id] = { error = err, result = result }
result_count = result_count + 1 result_count = result_count + 1
end end
local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, callback) local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _callback)
local expected_result_count = 0 local expected_result_count = 0
for _ in pairs(client_request_ids) do for _ in pairs(client_request_ids) do
expected_result_count = expected_result_count + 1 expected_result_count = expected_result_count + 1
@ -828,12 +897,13 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
return request_results return request_results
end end
--- Send a notification to a server --- Sends a notification to all servers attached to the buffer.
-- @param bufnr [number] (optional): The number of the buffer ---
-- @param method [string]: Name of the request method --@param bufnr (optional, number) Buffer handle, or 0 for current
-- @param params [string]: Arguments to send to the server --@param method (string) LSP method name
-- --@param params (string) Parameters to send to the server
-- @returns nil ---
--@returns nil
function lsp.buf_notify(bufnr, method, params) function lsp.buf_notify(bufnr, method, params)
validate { validate {
bufnr = { bufnr, 'n', true }; bufnr = { bufnr, 'n', true };
@ -888,12 +958,10 @@ function lsp.client_is_stopped(client_id)
return active_clients[client_id] == nil return active_clients[client_id] == nil
end end
--- Gets a map of client_id:client pairs for the given buffer, where each value
--- is a |vim.lsp.client| object.
--- ---
--- Miscellaneous utilities. --@param bufnr (optional, number): Buffer handle, or 0 for current
---
-- Retrieve a map from client_id to client of all active buffer clients.
-- @param bufnr [number] (optional): buffer handle or 0 for current
function lsp.buf_get_clients(bufnr) function lsp.buf_get_clients(bufnr)
bufnr = resolve_bufnr(bufnr) bufnr = resolve_bufnr(bufnr)
local result = {} local result = {}
@ -903,30 +971,24 @@ function lsp.buf_get_clients(bufnr)
return result return result
end end
-- Print some debug information about the current buffer clients.
-- The output of this function should not be relied upon and may change.
function lsp.buf_print_debug_info(bufnr)
print(vim.inspect(lsp.buf_get_clients(bufnr)))
end
-- Print some debug information about all LSP related things.
-- The output of this function should not be relied upon and may change.
function lsp.print_debug_info()
print(vim.inspect({ clients = active_clients }))
end
-- Log level dictionary with reverse lookup as well. -- Log level dictionary with reverse lookup as well.
-- --
-- Can be used to lookup the number from the name or the -- Can be used to lookup the number from the name or the
-- name from the number. -- name from the number.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' -- Levels by name: "trace", "debug", "info", "warn", "error"
-- Level numbers begin with 'trace' at 0 -- Level numbers begin with "trace" at 0
lsp.log_levels = log.levels lsp.log_levels = log.levels
-- Set the log level for lsp logging. --- Sets the global log level for LSP logging.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' ---
-- Level numbers begin with 'trace' at 0 --- Levels by name: "trace", "debug", "info", "warn", "error"
-- @param level [number|string] the case insensitive level name or number @see |vim.lsp.log_levels| --- Level numbers begin with "trace" at 0
---
--- Use `lsp.log_levels` for reverse lookup.
---
--@see |vim.lsp.log_levels|
---
--@param level [number|string] the case insensitive level name or number
function lsp.set_log_level(level) function lsp.set_log_level(level)
if type(level) == 'string' or type(level) == 'number' then if type(level) == 'string' or type(level) == 'number' then
log.set_level(level) log.set_level(level)
@ -935,7 +997,7 @@ function lsp.set_log_level(level)
end end
end end
-- Return the path of the logfile used by the LSP client. --- Gets the path of the logfile used by the LSP client.
function lsp.get_log_path() function lsp.get_log_path()
return log.get_filename() return log.get_filename()
end end

View File

@ -603,6 +603,8 @@ export interface WorkspaceClientCapabilities {
} }
--]=] --]=]
--- Gets a new ClientCapabilities object describing the LSP client
--- capabilities.
function protocol.make_client_capabilities() function protocol.make_client_capabilities()
return { return {
textDocument = { textDocument = {
@ -821,6 +823,8 @@ interface ServerCapabilities {
experimental?: any; experimental?: any;
} }
--]] --]]
--- Creates a normalized object describing LSP server capabilities.
function protocol.resolve_capabilities(server_capabilities) function protocol.resolve_capabilities(server_capabilities)
local general_properties = {} local general_properties = {}
local text_document_sync_properties local text_document_sync_properties

View File

@ -166,9 +166,14 @@ local function format_rpc_error(err)
return table.concat(message_parts, ' ') return table.concat(message_parts, ' ')
end end
--- Creates an RPC response object/table.
---
--@param code RPC error code defined in `vim.lsp.protocol.ErrorCodes`
--@param message (optional) arbitrary message to send to server
--@param data (optional) arbitrary data to send to server
local function rpc_response_error(code, message, data) local function rpc_response_error(code, message, data)
-- TODO should this error or just pick a sane error (like InternalError)? -- TODO should this error or just pick a sane error (like InternalError)?
local code_name = assert(protocol.ErrorCodes[code], 'Invalid rpc error code') local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code')
return setmetatable({ return setmetatable({
code = code; code = code;
message = message or code_name; message = message or code_name;

View File

@ -55,6 +55,7 @@ if sys.version_info[0] < 3 or sys.version_info[1] < 5:
sys.exit(1) sys.exit(1)
DEBUG = ('DEBUG' in os.environ) DEBUG = ('DEBUG' in os.environ)
TARGET = os.environ.get('TARGET', None)
INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ) INCLUDE_C_DECL = ('INCLUDE_C_DECL' in os.environ)
INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ) INCLUDE_DEPRECATED = ('INCLUDE_DEPRECATED' in os.environ)
@ -62,13 +63,14 @@ fmt_vimhelp = False # HACK
text_width = 78 text_width = 78
script_path = os.path.abspath(__file__) script_path = os.path.abspath(__file__)
base_dir = os.path.dirname(os.path.dirname(script_path)) base_dir = os.path.dirname(os.path.dirname(script_path))
out_dir = os.path.join(base_dir, 'tmp-{mode}-doc') out_dir = os.path.join(base_dir, 'tmp-{target}-doc')
filter_cmd = '%s %s' % (sys.executable, script_path) filter_cmd = '%s %s' % (sys.executable, script_path)
seen_funcs = set() seen_funcs = set()
lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter') lua2dox_filter = os.path.join(base_dir, 'scripts', 'lua2dox_filter')
CONFIG = { CONFIG = {
'api': { 'api': {
'mode': 'c',
'filename': 'api.txt', 'filename': 'api.txt',
# String used to find the start of the generated part of the doc. # String used to find the start of the generated part of the doc.
'section_start_token': '*api-global*', 'section_start_token': '*api-global*',
@ -85,17 +87,24 @@ CONFIG = {
# file patterns used by doxygen # file patterns used by doxygen
'file_patterns': '*.h *.c', 'file_patterns': '*.h *.c',
# Only function with this prefix are considered # Only function with this prefix are considered
'func_name_prefix': 'nvim_', 'fn_name_prefix': 'nvim_',
# Section name overrides. # Section name overrides.
'section_name': { 'section_name': {
'vim.c': 'Global', 'vim.c': 'Global',
}, },
# For generated section names.
'section_fmt': lambda name: f'{name} Functions',
# Section helptag.
'helptag_fmt': lambda name: f'*api-{name.lower()}*',
# Per-function helptag.
'fn_helptag_fmt': lambda fstem, name: f'*{name}()*',
# Module name overrides (for Lua). # Module name overrides (for Lua).
'module_override': {}, 'module_override': {},
# Append the docs for these modules, do not start a new section. # Append the docs for these modules, do not start a new section.
'append_only': [], 'append_only': [],
}, },
'lua': { 'lua': {
'mode': 'lua',
'filename': 'lua.txt', 'filename': 'lua.txt',
'section_start_token': '*lua-vim*', 'section_start_token': '*lua-vim*',
'section_order': [ 'section_order': [
@ -107,8 +116,13 @@ CONFIG = {
os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), os.path.join(base_dir, 'runtime/lua/vim/shared.lua'),
]), ]),
'file_patterns': '*.lua', 'file_patterns': '*.lua',
'func_name_prefix': '', 'fn_name_prefix': '',
'section_name': {}, 'section_name': {
'lsp.lua': 'core',
},
'section_fmt': lambda name: f'Lua module: {name.lower()}',
'helptag_fmt': lambda name: f'*lua-{name.lower()}*',
'fn_helptag_fmt': lambda fstem, name: f'*{fstem}.{name}()*',
'module_override': { 'module_override': {
# `shared` functions are exposed on the `vim` module. # `shared` functions are exposed on the `vim` module.
'shared': 'vim', 'shared': 'vim',
@ -117,6 +131,48 @@ CONFIG = {
'shared.lua', 'shared.lua',
], ],
}, },
'lsp': {
'mode': 'lua',
'filename': 'lsp.txt',
'section_start_token': '*lsp-core*',
'section_order': [
'lsp.lua',
'protocol.lua',
'buf.lua',
'callbacks.lua',
'log.lua',
'rpc.lua',
'util.lua'
],
'files': ' '.join([
os.path.join(base_dir, 'runtime/lua/vim/lsp'),
os.path.join(base_dir, 'runtime/lua/vim/lsp.lua'),
]),
'file_patterns': '*.lua',
'fn_name_prefix': '',
'section_name': {},
'section_fmt': lambda name: (
'Lua module: vim.lsp'
if name.lower() == 'lsp'
else f'Lua module: vim.lsp.{name.lower()}'),
'helptag_fmt': lambda name: (
'*lsp-core*'
if name.lower() == 'lsp'
else f'*lsp-{name.lower()}*'),
'fn_helptag_fmt': lambda fstem, name: (
f'*vim.lsp.{name}()*'
if fstem == 'lsp' and name != 'client'
else (
'*vim.lsp.client*'
# HACK. TODO(justinmk): class/structure support in lua2dox
if 'lsp.client' == f'{fstem}.{name}'
else f'*vim.lsp.{fstem}.{name}()*')),
'module_override': {
# Combine are exposed on the `vim` module.
'shared': 'vim',
},
'append_only': [],
},
} }
param_exclude = ( param_exclude = (
@ -516,7 +572,7 @@ def fmt_node_as_vimhelp(parent, width=62, indent=''):
return clean_lines('\n'.join(rendered_blocks).strip()) return clean_lines('\n'.join(rendered_blocks).strip())
def extract_from_xml(filename, mode, width): def extract_from_xml(filename, target, width):
"""Extracts Doxygen info as maps without formatting the text. """Extracts Doxygen info as maps without formatting the text.
Returns two maps: Returns two maps:
@ -567,12 +623,12 @@ def extract_from_xml(filename, mode, width):
if not fmt_vimhelp: if not fmt_vimhelp:
pass pass
elif mode == 'lua':
fstem = compoundname.split('.')[0]
fstem = CONFIG[mode]['module_override'].get(fstem, fstem)
vimtag = '*{}.{}()*'.format(fstem, name)
else: else:
vimtag = '*{}()*'.format(name) fstem = '?'
if '.' in compoundname:
fstem = compoundname.split('.')[0]
fstem = CONFIG[target]['module_override'].get(fstem, fstem)
vimtag = CONFIG[target]['fn_helptag_fmt'](fstem, name)
params = [] params = []
type_length = 0 type_length = 0
@ -583,8 +639,8 @@ def extract_from_xml(filename, mode, width):
declname = get_child(param, 'declname') declname = get_child(param, 'declname')
if declname: if declname:
param_name = get_text(declname).strip() param_name = get_text(declname).strip()
elif mode == 'lua': elif CONFIG[target]['mode'] == 'lua':
# that's how it comes out of lua2dox # XXX: this is what lua2dox gives us...
param_name = param_type param_name = param_type
param_type = '' param_type = ''
@ -614,7 +670,7 @@ def extract_from_xml(filename, mode, width):
' ') ' ')
# Minimum 8 chars between signature and vimtag # Minimum 8 chars between signature and vimtag
lhs = (width - 8) - len(prefix) lhs = (width - 8) - len(vimtag)
if len(prefix) + len(suffix) > lhs: if len(prefix) + len(suffix) > lhs:
signature = vimtag.rjust(width) + '\n' signature = vimtag.rjust(width) + '\n'
@ -663,7 +719,7 @@ def extract_from_xml(filename, mode, width):
if 'Deprecated' in str(xrefs): if 'Deprecated' in str(xrefs):
deprecated_fns[name] = fn deprecated_fns[name] = fn
elif name.startswith(CONFIG[mode]['func_name_prefix']): elif name.startswith(CONFIG[target]['fn_name_prefix']):
fns[name] = fn fns[name] = fn
xrefs.clear() xrefs.clear()
@ -673,7 +729,7 @@ def extract_from_xml(filename, mode, width):
return (fns, deprecated_fns) return (fns, deprecated_fns)
def fmt_doxygen_xml_as_vimhelp(filename, mode): def fmt_doxygen_xml_as_vimhelp(filename, target):
"""Entrypoint for generating Vim :help from from Doxygen XML. """Entrypoint for generating Vim :help from from Doxygen XML.
Returns 3 items: Returns 3 items:
@ -684,7 +740,7 @@ def fmt_doxygen_xml_as_vimhelp(filename, mode):
fmt_vimhelp = True fmt_vimhelp = True
fns_txt = {} # Map of func_name:vim-help-text. fns_txt = {} # Map of func_name:vim-help-text.
deprecated_fns_txt = {} # Map of func_name:vim-help-text. deprecated_fns_txt = {} # Map of func_name:vim-help-text.
fns, _ = extract_from_xml(filename, mode, width=text_width) fns, _ = extract_from_xml(filename, target, width=text_width)
for name, fn in fns.items(): for name, fn in fns.items():
# Generate Vim :help for parameters. # Generate Vim :help for parameters.
@ -714,7 +770,7 @@ def fmt_doxygen_xml_as_vimhelp(filename, mode):
if 'Deprecated' in xrefs: if 'Deprecated' in xrefs:
deprecated_fns_txt[name] = func_doc deprecated_fns_txt[name] = func_doc
elif name.startswith(CONFIG[mode]['func_name_prefix']): elif name.startswith(CONFIG[target]['fn_name_prefix']):
fns_txt[name] = func_doc fns_txt[name] = func_doc
xrefs.clear() xrefs.clear()
@ -730,9 +786,13 @@ def delete_lines_below(filename, tokenstr):
""" """
lines = open(filename).readlines() lines = open(filename).readlines()
i = 0 i = 0
found = False
for i, line in enumerate(lines, 1): for i, line in enumerate(lines, 1):
if tokenstr in line: if tokenstr in line:
found = True
break break
if not found:
raise RuntimeError(f'not found: "{tokenstr}"')
i = max(0, i - 2) i = max(0, i - 2)
with open(filename, 'wt') as fp: with open(filename, 'wt') as fp:
fp.writelines(lines[0:i]) fp.writelines(lines[0:i])
@ -746,21 +806,28 @@ def main(config):
Doxygen is called and configured through stdin. Doxygen is called and configured through stdin.
""" """
for mode in CONFIG: for target in CONFIG:
if TARGET is not None and target != TARGET:
continue
mpack_file = os.path.join( mpack_file = os.path.join(
base_dir, 'runtime', 'doc', base_dir, 'runtime', 'doc',
CONFIG[mode]['filename'].replace('.txt', '.mpack')) CONFIG[target]['filename'].replace('.txt', '.mpack'))
if os.path.exists(mpack_file): if os.path.exists(mpack_file):
os.remove(mpack_file) os.remove(mpack_file)
output_dir = out_dir.format(mode=mode) output_dir = out_dir.format(target=target)
p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE) p = subprocess.Popen(
['doxygen', '-'],
stdin=subprocess.PIPE,
# silence warnings
# runtime/lua/vim/lsp.lua:209: warning: argument 'foo' not found
stderr=subprocess.DEVNULL)
p.communicate( p.communicate(
config.format( config.format(
input=CONFIG[mode]['files'], input=CONFIG[target]['files'],
output=output_dir, output=output_dir,
filter=filter_cmd, filter=filter_cmd,
file_patterns=CONFIG[mode]['file_patterns']) file_patterns=CONFIG[target]['file_patterns'])
.encode('utf8') .encode('utf8')
) )
if p.returncode: if p.returncode:
@ -797,24 +864,20 @@ def main(config):
if filename.endswith('.c') or filename.endswith('.lua'): if filename.endswith('.c') or filename.endswith('.lua'):
# Extract unformatted (*.mpack). # Extract unformatted (*.mpack).
fn_map, _ = extract_from_xml(os.path.join(base, '{}.xml'.format( fn_map, _ = extract_from_xml(os.path.join(base, '{}.xml'.format(
compound.getAttribute('refid'))), mode, width=9999) compound.getAttribute('refid'))), target, width=9999)
# Extract formatted (:help). # Extract formatted (:help).
functions_text, deprecated_text = fmt_doxygen_xml_as_vimhelp( functions_text, deprecated_text = fmt_doxygen_xml_as_vimhelp(
os.path.join(base, '{}.xml'.format( os.path.join(base, '{}.xml'.format(
compound.getAttribute('refid'))), mode) compound.getAttribute('refid'))), target)
if not functions_text and not deprecated_text: if not functions_text and not deprecated_text:
continue continue
else: else:
name = os.path.splitext(os.path.basename(filename))[0] name = os.path.splitext(
if name == 'ui': os.path.basename(filename))[0].lower()
name = name.upper() sectname = name.upper() if name == 'ui' else name.title()
else:
name = name.title()
doc = '' doc = ''
intro = intros.get(f'api-{name}')
intro = intros.get('api-%s' % name.lower())
if intro: if intro:
doc += '\n\n' + intro doc += '\n\n' + intro
@ -822,36 +885,31 @@ def main(config):
doc += '\n\n' + functions_text doc += '\n\n' + functions_text
if INCLUDE_DEPRECATED and deprecated_text: if INCLUDE_DEPRECATED and deprecated_text:
doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name doc += f'\n\n\nDeprecated {sectname} Functions: ~\n\n'
doc += deprecated_text doc += deprecated_text
if doc: if doc:
filename = os.path.basename(filename) filename = os.path.basename(filename)
name = CONFIG[mode]['section_name'].get(filename, name) sectname = CONFIG[target]['section_name'].get(
filename, sectname)
if mode == 'lua': title = CONFIG[target]['section_fmt'](sectname)
title = 'Lua module: {}'.format(name.lower()) helptag = CONFIG[target]['helptag_fmt'](sectname)
helptag = '*lua-{}*'.format(name.lower())
else:
title = '{} Functions'.format(name)
helptag = '*api-{}*'.format(name.lower())
sections[filename] = (title, helptag, doc) sections[filename] = (title, helptag, doc)
fn_map_full.update(fn_map) fn_map_full.update(fn_map)
if not sections: assert sections
return if len(sections) > len(CONFIG[target]['section_order']):
raise RuntimeError(
'found new modules "{}"; update the "section_order" map'.format(
set(sections).difference(CONFIG[target]['section_order'])))
docs = '' docs = ''
i = 0 i = 0
for filename in CONFIG[mode]['section_order']: for filename in CONFIG[target]['section_order']:
if filename not in sections:
raise RuntimeError(
'found new module "{}"; update the "section_order" map'.format(
filename))
title, helptag, section_doc = sections.pop(filename) title, helptag, section_doc = sections.pop(filename)
i += 1 i += 1
if filename not in CONFIG[mode]['append_only']: if filename not in CONFIG[target]['append_only']:
docs += sep docs += sep
docs += '\n%s%s' % (title, docs += '\n%s%s' % (title,
helptag.rjust(text_width - len(title))) helptag.rjust(text_width - len(title)))
@ -862,9 +920,9 @@ def main(config):
docs += ' vim:tw=78:ts=8:ft=help:norl:\n' docs += ' vim:tw=78:ts=8:ft=help:norl:\n'
doc_file = os.path.join(base_dir, 'runtime', 'doc', doc_file = os.path.join(base_dir, 'runtime', 'doc',
CONFIG[mode]['filename']) CONFIG[target]['filename'])
delete_lines_below(doc_file, CONFIG[mode]['section_start_token']) delete_lines_below(doc_file, CONFIG[target]['section_start_token'])
with open(doc_file, 'ab') as fp: with open(doc_file, 'ab') as fp:
fp.write(docs.encode('utf8')) fp.write(docs.encode('utf8'))

View File

@ -17,61 +17,28 @@
-- Free Software Foundation, Inc., -- -- Free Software Foundation, Inc., --
-- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- -- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --
----------------------------------------------------------------------------]] ----------------------------------------------------------------------------]]
--[[!
\file
\brief a hack lua2dox converter
]]
--[[! --[[!
\mainpage Lua-to-Doxygen converter
Introduction Partially from lua2dox
------------ http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm
A hack lua2dox converter
Version 0.2
This lets us make Doxygen output some documentation to let
us develop this code.
It is partially cribbed from the functionality of lua2dox
(http://search.cpan.org/~alec/Doxygen-Lua-0.02/lib/Doxygen/Lua.pm).
Found on CPAN when looking for something else; kinda handy.
Improved from lua2dox to make the doxygen output more friendly.
Also it runs faster in lua rather than Perl.
Because this Perl based system is called "lua2dox"., I have decided to add ".lua" to the name
to keep the two separate.
Running Running
------- -------
<ol> This file "lua2dox.lua" gets called by "lua2dox_filter" (bash).
<li> Ensure doxygen is installed on your system and that you are familiar with its use.
Best is to try to make and document some simple C/C++/PHP to see what it produces.
You can experiment with the enclosed example code.
<li> Run "doxygen -g" to create a default Doxyfile. Doxygen must be on your system. You can experiment like so:
Then alter it to let it recognise lua. Add the two following lines: - Run "doxygen -g" to create a default Doxyfile.
- Then alter it to let it recognise lua. Add the two following lines:
\code{.bash} FILE_PATTERNS = *.lua
FILE_PATTERNS = *.lua FILTER_PATTERNS = *.lua=lua2dox_filter
- Then run "doxygen".
FILTER_PATTERNS = *.lua=lua2dox_filter
\endcode
Either add them to the end or find the appropriate entry in Doxyfile.
There are other lines that you might like to alter, but see further documentation for details.
<li> When Doxyfile is edited run "doxygen"
The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language. The core function reads the input file (filename or stdin) and outputs some pseudo C-ish language.
It only has to be good enough for doxygen to see it as legal. It only has to be good enough for doxygen to see it as legal.
Therefore our lua interpreter is fairly limited, but "good enough".
One limitation is that each line is treated separately (except for long comments). One limitation is that each line is treated separately (except for long comments).
The implication is that class and function declarations must be on the same line. The implication is that class and function declarations must be on the same line.
@ -81,40 +48,8 @@ so it will probably not document accurately if we do do this.
However I have put in a hack that will insert the "missing" close paren. However I have put in a hack that will insert the "missing" close paren.
The effect is that you will get the function documented, but not with the parameter list you might expect. The effect is that you will get the function documented, but not with the parameter list you might expect.
</ol>
Installation
------------
Here for linux or unix-like, for any other OS you need to refer to other documentation.
This file is "lua2dox.lua". It gets called by "lua2dox_filter"(bash).
Somewhere in your path (e.g. "~/bin" or "/usr/local/bin") put a link to "lua2dox_filter".
Documentation
-------------
Read the external documentation that should be part of this package.
For example look for the "README" and some .PDFs.
]] ]]
-- we won't use our library code, so this becomes more portable
-- require 'elijah_fix_require'
-- require 'elijah_class'
--
--! \brief ``declare'' as class
--!
--! use as:
--! \code{.lua}
--! TWibble = class()
--! function TWibble.init(this,Str)
--! this.str = Str
--! -- more stuff here
--! end
--! \endcode
--!
function class(BaseClass, ClassInitialiser) function class(BaseClass, ClassInitialiser)
local newClass = {} -- a new class newClass local newClass = {} -- a new class newClass
if not ClassInitialiser and type(BaseClass) == 'function' then if not ClassInitialiser and type(BaseClass) == 'function' then
@ -165,8 +100,6 @@ function class(BaseClass, ClassInitialiser)
return newClass return newClass
end end
-- require 'elijah_clock'
--! \class TCore_Clock --! \class TCore_Clock
--! \brief a clock --! \brief a clock
TCore_Clock = class() TCore_Clock = class()
@ -201,9 +134,6 @@ function TCore_Clock.getTimeStamp(this,T0)
end end
--require 'elijah_io'
--! \class TCore_IO
--! \brief io to console --! \brief io to console
--! --!
--! pseudo class (no methods, just to keep documentation tidy) --! pseudo class (no methods, just to keep documentation tidy)
@ -225,8 +155,6 @@ function TCore_IO_writeln(Str)
end end
--require 'elijah_string'
--! \brief trims a string --! \brief trims a string
function string_trim(Str) function string_trim(Str)
return Str:match("^%s*(.-)%s*$") return Str:match("^%s*(.-)%s*$")
@ -257,8 +185,6 @@ function string_split(Str, Pattern)
end end
--require 'elijah_commandline'
--! \class TCore_Commandline --! \class TCore_Commandline
--! \brief reads/parses commandline --! \brief reads/parses commandline
TCore_Commandline = class() TCore_Commandline = class()
@ -279,9 +205,6 @@ function TCore_Commandline.getRaw(this,Key,Default)
return val return val
end end
--require 'elijah_debug'
------------------------------- -------------------------------
--! \brief file buffer --! \brief file buffer
--! --!

View File

@ -179,8 +179,8 @@ void setpcmark(void)
} }
if (jop_flags & JOP_STACK) { if (jop_flags & JOP_STACK) {
// If we're somewhere in the middle of the jumplist discard everything // jumpoptions=stack: if we're somewhere in the middle of the jumplist
// after the current index. // discard everything after the current index.
if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1) { if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1) {
// Discard the rest of the jumplist by cutting the length down to // Discard the rest of the jumplist by cutting the length down to
// contain nothing beyond the current index. // contain nothing beyond the current index.
@ -1214,14 +1214,14 @@ void cleanup_jumplist(win_T *wp, bool checktail)
break; break;
} }
} }
bool mustfree; bool mustfree;
if (i >= wp->w_jumplistlen) { // not duplicate if (i >= wp->w_jumplistlen) { // not duplicate
mustfree = false; mustfree = false;
} else if (i > from + 1) { // non-adjacent duplicate } else if (i > from + 1) { // non-adjacent duplicate
// When the jump options include "stack", duplicates are only removed from // jumpoptions=stack: remove duplicates only when adjacent.
// the jumplist when they are adjacent.
mustfree = !(jop_flags & JOP_STACK); mustfree = !(jop_flags & JOP_STACK);
} else { // adjacent duplicate } else { // adjacent duplicate
mustfree = true; mustfree = true;
} }

View File

@ -48,7 +48,7 @@ describe('jumplist', function()
end) end)
end) end)
describe('jumpoptions=stack behaves like browser history', function() describe("jumpoptions=stack behaves like 'tagstack'", function()
before_each(function() before_each(function()
clear() clear()
feed(':clearjumps<cr>') feed(':clearjumps<cr>')

View File

@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear local clear = helpers.clear
local exec_lua = helpers.exec_lua local exec_lua = helpers.exec_lua
local eq = helpers.eq local eq = helpers.eq
local iswin = helpers.iswin
local retry = helpers.retry
local NIL = helpers.NIL local NIL = helpers.NIL
-- Use these to get access to a coroutine so that I can run async tests and use -- Use these to get access to a coroutine so that I can run async tests and use
@ -11,9 +13,8 @@ local run, stop = helpers.run, helpers.stop
if helpers.pending_win32(pending) then return end if helpers.pending_win32(pending) then return end
local is_windows = require'luv'.os_uname().sysname == "Windows"
local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua"
if is_windows then if iswin() then
lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\") lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\")
end end
@ -101,8 +102,8 @@ local function test_rpc_server(config)
end end
end end
describe('Language Client API', function() describe('LSP', function()
describe('server_name is specified', function() describe('server_name specified', function()
before_each(function() before_each(function()
clear() clear()
-- Run an instance of nvim on the file which contains our "scripts". -- Run an instance of nvim on the file which contains our "scripts".
@ -111,14 +112,17 @@ describe('Language Client API', function()
exec_lua([=[ exec_lua([=[
lsp = require('vim.lsp') lsp = require('vim.lsp')
local test_name, fixture_filename = ... local test_name, fixture_filename = ...
TEST_RPC_CLIENT_ID = lsp.start_client { function test__start_client()
cmd = { return lsp.start_client {
vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', cmd = {
"-c", string.format("lua TEST_NAME = %q", test_name), vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
"-c", "luafile "..fixture_filename; "-c", string.format("lua TEST_NAME = %q", test_name),
}; "-c", "luafile "..fixture_filename;
root_dir = vim.loop.cwd(); };
} root_dir = vim.loop.cwd();
}
end
TEST_CLIENT1 = test__start_client()
]=], test_name, lsp_test_rpc_server_file) ]=], test_name, lsp_test_rpc_server_file)
end) end)
@ -127,25 +131,48 @@ describe('Language Client API', function()
-- exec_lua("lsp.stop_all_clients(true)") -- exec_lua("lsp.stop_all_clients(true)")
end) end)
describe('start_client and stop_client', function() it('start_client(), stop_client()', function()
it('should return true', function() retry(nil, 4000, function()
for _ = 1, 20 do eq(1, exec_lua('return #lsp.get_active_clients()'))
helpers.sleep(10) end)
if exec_lua("return #lsp.get_active_clients()") > 0 then eq(2, exec_lua([[
break TEST_CLIENT2 = test__start_client()
end return TEST_CLIENT2
end ]]))
eq(1, exec_lua("return #lsp.get_active_clients()")) eq(3, exec_lua([[
eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil")) TEST_CLIENT3 = test__start_client()
eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).is_stopped()")) return TEST_CLIENT3
exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).stop()") ]]))
for _ = 1, 20 do retry(nil, 4000, function()
helpers.sleep(10) eq(3, exec_lua('return #lsp.get_active_clients()'))
if exec_lua("return #lsp.get_active_clients()") == 0 then end)
break
end eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil'))
end eq(false, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).is_stopped()'))
eq(true, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil")) exec_lua('return lsp.get_client_by_id(TEST_CLIENT1).stop()')
retry(nil, 4000, function()
eq(2, exec_lua('return #lsp.get_active_clients()'))
end)
eq(true, exec_lua('return lsp.get_client_by_id(TEST_CLIENT1) == nil'))
exec_lua('lsp.stop_client({TEST_CLIENT2, TEST_CLIENT3})')
retry(nil, 4000, function()
eq(0, exec_lua('return #lsp.get_active_clients()'))
end)
end)
it('stop_client() also works on client objects', function()
exec_lua([[
TEST_CLIENT2 = test__start_client()
TEST_CLIENT3 = test__start_client()
]])
retry(nil, 4000, function()
eq(3, exec_lua('return #lsp.get_active_clients()'))
end)
-- Stop all clients.
exec_lua('lsp.stop_client(lsp.get_active_clients())')
retry(nil, 4000, function()
eq(0, exec_lua('return #lsp.get_active_clients()'))
end) end)
end) end)
end) end)