mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Add docs for most vim.lsp methods
Most of the lsp.log will be addressed in a separate PR.
This commit is contained in:
		| @@ -734,9 +734,10 @@ nvim_feedkeys({keys}, {mode}, {escape_csi})                  *nvim_feedkeys()* | |||||||
|  |  | ||||||
|                 On execution error: does not fail, but updates v:errmsg. |                 On execution error: does not fail, but updates v:errmsg. | ||||||
|  |  | ||||||
|                 If you need to input sequences like <C-o> use nvim_replace_termcodes |                 If you need to input sequences like <C-o> use | ||||||
|                 to replace the termcodes and then pass the resulting string to |                 |nvim_replace_termcodes| to replace the termcodes and then | ||||||
|                 nvim_feedkeys. You'll also want to enable escape_csi. |                 pass the resulting string to nvim_feedkeys. You'll also want | ||||||
|  |                 to enable escape_csi. | ||||||
|  |  | ||||||
|                 Example: > |                 Example: > | ||||||
|                     :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) |                     :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -27,11 +27,21 @@ local lsp = { | |||||||
|  |  | ||||||
| -- TODO improve handling of scratch buffers with LSP attached. | -- TODO improve handling of scratch buffers with LSP attached. | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Concatenates and writes a list of strings to the Vim error buffer. | ||||||
|  | --- | ||||||
|  | --@param {...} (List of strings) List to write to the buffer | ||||||
| local function err_message(...) | local function err_message(...) | ||||||
|   nvim_err_writeln(table.concat(vim.tbl_flatten{...})) |   nvim_err_writeln(table.concat(vim.tbl_flatten{...})) | ||||||
|   nvim_command("redraw") |   nvim_command("redraw") | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Returns the buffer number for the given {bufnr}. | ||||||
|  | --- | ||||||
|  | --@param bufnr (number) Buffer number to resolve. Defaults to the current | ||||||
|  | ---buffer if not given. | ||||||
|  | --@returns bufnr (number) Number of requested buffer | ||||||
| local function resolve_bufnr(bufnr) | local function resolve_bufnr(bufnr) | ||||||
|   validate { bufnr = { bufnr, 'n', true } } |   validate { bufnr = { bufnr, 'n', true } } | ||||||
|   if bufnr == nil or bufnr == 0 then |   if bufnr == nil or bufnr == 0 then | ||||||
| @@ -40,6 +50,11 @@ local function resolve_bufnr(bufnr) | |||||||
|   return bufnr |   return bufnr | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Checks whether a given path is a directory. | ||||||
|  | --- | ||||||
|  | --@param filename (string) path to check | ||||||
|  | --@returns true if {filename} exists and is a directory, false otherwise | ||||||
| local function is_dir(filename) | local function is_dir(filename) | ||||||
|   validate{filename={filename,'s'}} |   validate{filename={filename,'s'}} | ||||||
|   local stat = uv.fs_stat(filename) |   local stat = uv.fs_stat(filename) | ||||||
| @@ -55,6 +70,10 @@ local valid_encodings = { | |||||||
| } | } | ||||||
|  |  | ||||||
| local client_index = 0 | local client_index = 0 | ||||||
|  | --@private | ||||||
|  | --- Returns a new, unused client id. | ||||||
|  | --- | ||||||
|  | --@returns (number) client id | ||||||
| local function next_client_id() | local function next_client_id() | ||||||
|   client_index = client_index + 1 |   client_index = client_index + 1 | ||||||
|   return client_index |   return client_index | ||||||
| @@ -64,6 +83,12 @@ local active_clients = {} | |||||||
| local all_buffer_active_clients = {} | local all_buffer_active_clients = {} | ||||||
| local uninitialized_clients = {} | local uninitialized_clients = {} | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Invokes a callback for each LSP client attached to the buffer {bufnr}. | ||||||
|  | --- | ||||||
|  | --@param bufnr (Number) of buffer | ||||||
|  | --@param callback (function({client}, {client_id}, {bufnr}) Function to run on | ||||||
|  | ---each client attached to that buffer. | ||||||
| local function for_each_buffer_client(bufnr, callback) | local function for_each_buffer_client(bufnr, callback) | ||||||
|   validate { |   validate { | ||||||
|     callback = { callback, 'f' }; |     callback = { callback, 'f' }; | ||||||
| @@ -88,6 +113,11 @@ lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_rever | |||||||
|   ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1; |   ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1; | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Normalizes {encoding} to valid LSP encoding names. | ||||||
|  | --- | ||||||
|  | --@param encoding (string) Encoding to normalize | ||||||
|  | --@returns (string) normalized encoding name | ||||||
| local function validate_encoding(encoding) | local function validate_encoding(encoding) | ||||||
|   validate { |   validate { | ||||||
|     encoding = { encoding, 's' }; |     encoding = { encoding, 's' }; | ||||||
| @@ -96,6 +126,13 @@ local function validate_encoding(encoding) | |||||||
|       or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) |       or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@internal | ||||||
|  | --- Parses a command invocation into the command itself and its args. If there | ||||||
|  | --- are no arguments, an empty table is returned as the second argument. | ||||||
|  | --- | ||||||
|  | --@param input (List) | ||||||
|  | --@returns (string) the command | ||||||
|  | --@returns (list of strings) its arguments | ||||||
| function lsp._cmd_parts(input) | function lsp._cmd_parts(input) | ||||||
|   vim.validate{cmd={ |   vim.validate{cmd={ | ||||||
|     input, |     input, | ||||||
| @@ -114,12 +151,27 @@ function lsp._cmd_parts(input) | |||||||
|   return cmd, cmd_args |   return cmd, cmd_args | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Augments a validator function with support for optional (nil) values. | ||||||
|  | --- | ||||||
|  | --@param fn (function(v)) The original validator function; should return a | ||||||
|  | ---bool. | ||||||
|  | --@returns (function(v)) The augmented function. Also returns true if {v} is | ||||||
|  | ---`nil`. | ||||||
| local function optional_validator(fn) | local function optional_validator(fn) | ||||||
|   return function(v) |   return function(v) | ||||||
|     return v == nil or fn(v) |     return v == nil or fn(v) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Validates a client configuration as given to |vim.lsp.start_client()|. | ||||||
|  | --- | ||||||
|  | --@param config (table) | ||||||
|  | --@returns (table) "Cleaned" config, containing only the command, its | ||||||
|  | ---arguments, and a valid encoding. | ||||||
|  | --- | ||||||
|  | --@see |vim.lsp.start_client()| | ||||||
| local function validate_client_config(config) | local function validate_client_config(config) | ||||||
|   validate { |   validate { | ||||||
|     config = { config, 't' }; |     config = { config, 't' }; | ||||||
| @@ -148,6 +200,11 @@ local function validate_client_config(config) | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Returns full text of buffer {bufnr} as a string. | ||||||
|  | --- | ||||||
|  | --@param bufnr (number) Buffer handle, or 0 for current. | ||||||
|  | --@returns Buffer text as string. | ||||||
| local function buf_get_full_text(bufnr) | local function buf_get_full_text(bufnr) | ||||||
|   local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') |   local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') | ||||||
|   if nvim_buf_get_option(bufnr, 'eol') then |   if nvim_buf_get_option(bufnr, 'eol') then | ||||||
| @@ -156,6 +213,11 @@ local function buf_get_full_text(bufnr) | |||||||
|   return text |   return text | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Default handler for the 'textDocument/didOpen' LSP notification. | ||||||
|  | --- | ||||||
|  | --@param bufnr (Number) Number of the buffer, or 0 for current | ||||||
|  | --@param client Client object | ||||||
| local function text_document_did_open_handler(bufnr, client) | local function text_document_did_open_handler(bufnr, client) | ||||||
|   if not client.resolved_capabilities.text_document_open_close then |   if not client.resolved_capabilities.text_document_open_close then | ||||||
|     return |     return | ||||||
| @@ -176,74 +238,88 @@ local function text_document_did_open_handler(bufnr, client) | |||||||
|   util.buf_versions[bufnr] = params.textDocument.version |   util.buf_versions[bufnr] = params.textDocument.version | ||||||
| end | end | ||||||
|  |  | ||||||
| --- LSP client object. | --- LSP client object. You can get an active client object via | ||||||
|  | --- |vim.lsp.get_client_by_id()| or |vim.lsp.get_active_clients()|. | ||||||
| --- | --- | ||||||
| --- - Methods: | --- - Methods: | ||||||
| --- | --- | ||||||
| ---  - request(method, params, [callback]) | ---  - request(method, params, [callback], bufnr) | ||||||
| ---     Send a request to the server. If callback is not specified, it will use | ---     Sends a request to the server. | ||||||
| ---     {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 | ---     This is a thin wrapper around {client.rpc.request} with some additional | ||||||
| ---     checking. | ---     checking. | ||||||
| ---     Returns a boolean to indicate if the notification was successful. If it | ---     If {callback} is not specified, it will use {client.callbacks} to try to | ||||||
| ---     is false, then it will always be false (the client has shutdown). | ---     find a callback. If one is not found there, then an error will occur. | ||||||
| ---     If it was successful, then it will return the request id as the second | ---     Returns: {status}, {[client_id]}. {status} is a boolean indicating if | ||||||
| ---     result. You can use this with `notify("$/cancel", { id = request_id })` | ---     the notification was successful. If it is `false`, then it will always | ||||||
| ---     to cancel the request. This helper is made automatically with | ---     be `false` (the client has shutdown). | ||||||
| ---     |vim.lsp.buf_request()| | ---     If {status} is `true`, the function returns {request_id} as the second | ||||||
| ---     Returns: status, [client_id] | ---     result. You can use this with `client.cancel_request(request_id)` | ||||||
|  | ---     to cancel the request. | ||||||
| --- | --- | ||||||
| ---  - notify(method, params) | ---  - notify(method, params) | ||||||
| ---     This is just {client.rpc.notify}() | ---     Sends a notification to an LSP server. | ||||||
| ---     Returns a boolean to indicate if the notification was successful. If it | ---     Returns: a boolean to indicate if the notification was successful. If | ||||||
| ---     is false, then it will always be false (the client has shutdown). | ---     it is false, then it will always be false (the client has shutdown). | ||||||
| ---     Returns: status |  | ||||||
| --- | --- | ||||||
| ---  - cancel_request(id) | ---  - cancel_request(id) | ||||||
| ---     This is just {client.rpc.notify}("$/cancelRequest", { id = id }) | ---     Cancels a request with a given request id. | ||||||
| ---     Returns the same as `notify()`. | ---     Returns: same as `notify()`. | ||||||
| --- | --- | ||||||
| ---  - stop([force]) | ---  - stop([force]) | ||||||
| ---     Stop a client, optionally with force. | ---     Stops a client, optionally with force. | ||||||
| ---     By default, it will just ask the server to shutdown without 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 | ---     If you request to stop a client which has previously been requested to | ||||||
| ---     shutdown, it will automatically escalate and force shutdown. | ---     shutdown, it will automatically escalate and force shutdown. | ||||||
| --- | --- | ||||||
| ---  - is_stopped() | ---  - is_stopped() | ||||||
| ---     Returns true if the client is fully stopped. | ---     Checks whether a client is stopped. | ||||||
|  | ---     Returns: true if the client is fully stopped. | ||||||
|  | --- | ||||||
|  | ---  - on_attach(bufnr) | ||||||
|  | ---     Runs the on_attach function from the client's config if it was defined. | ||||||
| --- | --- | ||||||
| --- - Members | --- - Members | ||||||
| ---  - id (number): The id allocated to the client. | ---  - {id} (number): The id allocated to the client. | ||||||
| --- | --- | ||||||
| ---  - name (string): If a name is specified on creation, that will be | ---  - {name} (string): If a name is specified on creation, that will be | ||||||
| ---    used. Otherwise it is just the client id. This is used for | ---    used. Otherwise it is just the client id. This is used for | ||||||
| ---    logs and messages. | ---    logs and messages. | ||||||
| --- | --- | ||||||
| ---  - offset_encoding (string): The encoding used for communicating | ---  - {rpc} (table): RPC client object, for low level interaction with the | ||||||
| ---    with the server. You can modify this in the `on_init` method | ---    client. See |vim.lsp.rpc.start()|. | ||||||
|  | --- | ||||||
|  | ---  - {offset_encoding} (string): 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. | ---    before text is sent to the server. | ||||||
| --- | --- | ||||||
| ---  - callbacks (table): The callbacks used by the client as | ---  - {callbacks} (table): The callbacks used by the client as | ||||||
| ---    described in |lsp-callbacks|. | ---    described in |lsp-callbacks|. | ||||||
| --- | --- | ||||||
| ---  - config (table): copy of the table that was passed by the user | ---  - {config} (table): copy of the table that was passed by the user | ||||||
| ---    to |vim.lsp.start_client()|. | ---    to |vim.lsp.start_client()|. | ||||||
| --- | --- | ||||||
| ---  - server_capabilities (table): Response from the server sent on | ---  - {server_capabilities} (table): Response from the server sent on | ||||||
| ---    `initialize` describing the server's capabilities. | ---    `initialize` describing the server's capabilities. | ||||||
| --- | --- | ||||||
| ---  - resolved_capabilities (table): Normalized table of | ---  - {resolved_capabilities} (table): Normalized table of | ||||||
| ---    capabilities that we have detected based on the initialize | ---    capabilities that we have detected based on the initialize | ||||||
| ---    response from the server in `server_capabilities`. | ---    response from the server in `server_capabilities`. | ||||||
| function lsp.client() | function lsp.client() | ||||||
|   error() |   error() | ||||||
| end | end | ||||||
|  |  | ||||||
|  | -- FIXME: Currently all methods on the `vim.lsp.client` object are documented | ||||||
|  | -- twice: Here, and on the methods themselves (e.g. `client.request()`). This | ||||||
|  | -- is a workaround for the vimdoc generator script not handling method names | ||||||
|  | -- correctly. If you change the documentation on either, please make sure to | ||||||
|  | -- update the other as well. | ||||||
|  |  | ||||||
| --- Starts and initializes a client with the given configuration. | --- Starts and initializes a client with the given configuration. | ||||||
| --- | --- | ||||||
| --- Parameters `cmd` and `root_dir` are required. | --- Parameters `cmd` and `root_dir` are required. | ||||||
| --- | --- | ||||||
|  | --- The following parameters describe fields in the {config} table. | ||||||
|  | --- | ||||||
| --@param root_dir: (required, string) Directory where the LSP server will base | --@param root_dir: (required, string) Directory where the LSP server will base | ||||||
| --- its rootUri on initialization. | --- its rootUri on initialization. | ||||||
| --- | --- | ||||||
| @@ -271,8 +347,8 @@ end | |||||||
| --- | --- | ||||||
| --@param callbacks Map of language server method names to | --@param callbacks Map of language server method names to | ||||||
| --- `function(err, method, params, client_id)` handler. Invoked for: | --- `function(err, method, params, client_id)` handler. Invoked for: | ||||||
| --- - Notifications from the server, where `err` will always be `nil`. | --- - Notifications to the server, where `err` will always be `nil`. | ||||||
| --- - Requests initiated by the server. For these you can respond by returning | --- - Requests by the server. For these you can respond by returning | ||||||
| ---   two values: `result, err` where err must be shaped like a RPC error, | ---   two values: `result, err` where err must be shaped like a RPC error, | ||||||
| ---   i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to | ---   i.e. `{ code, message, data? }`. Use |vim.lsp.rpc_response_error()| to | ||||||
| ---   help with this. | ---   help with this. | ||||||
| @@ -297,7 +373,7 @@ end | |||||||
| --@param before_init Callback with parameters (initialize_params, config) | --@param before_init Callback with parameters (initialize_params, config) | ||||||
| --- invoked before the LSP "initialize" phase, where `params` contains the | --- invoked before the LSP "initialize" phase, where `params` contains the | ||||||
| --- parameters being sent to the server and `config` is the config that was | --- parameters being sent to the server and `config` is the config that was | ||||||
| --- passed to `start_client()`. You can use this to modify parameters before | --- passed to |vim.lsp.start_client()|. You can use this to modify parameters before | ||||||
| --- they are sent. | --- they are sent. | ||||||
| --- | --- | ||||||
| --@param on_init Callback (client, initialize_result) invoked after LSP | --@param on_init Callback (client, initialize_result) invoked after LSP | ||||||
| @@ -335,10 +411,23 @@ function lsp.start_client(config) | |||||||
|  |  | ||||||
|   local handlers = {} |   local handlers = {} | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Returns the callback associated with an LSP method. Returns the default | ||||||
|  |   --- callback if the user hasn't set a custom one. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) LSP method name | ||||||
|  |   --@returns (fn) The callback for the given method, if defined, or the default | ||||||
|  |   ---from |lsp-callbacks| | ||||||
|   local function resolve_callback(method) |   local function resolve_callback(method) | ||||||
|     return callbacks[method] or default_callbacks[method] |     return callbacks[method] or default_callbacks[method] | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Handles a notification sent by an LSP server by invoking the | ||||||
|  |   --- corresponding callback. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) LSP method name | ||||||
|  |   --@param params (table) The parameters for that method. | ||||||
|   function handlers.notification(method, params) |   function handlers.notification(method, params) | ||||||
|     local _ = log.debug() and log.debug('notification', method, params) |     local _ = log.debug() and log.debug('notification', method, params) | ||||||
|     local callback = resolve_callback(method) |     local callback = resolve_callback(method) | ||||||
| @@ -348,6 +437,12 @@ function lsp.start_client(config) | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Handles a request from an LSP server by invoking the corresponding | ||||||
|  |   --- callback. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) LSP method name | ||||||
|  |   --@param params (table) The parameters for that method | ||||||
|   function handlers.server_request(method, params) |   function handlers.server_request(method, params) | ||||||
|     local _ = log.debug() and log.debug('server_request', method, params) |     local _ = log.debug() and log.debug('server_request', method, params) | ||||||
|     local callback = resolve_callback(method) |     local callback = resolve_callback(method) | ||||||
| @@ -359,6 +454,13 @@ function lsp.start_client(config) | |||||||
|     return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) |     return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Invoked when the client operation throws an error. | ||||||
|  |   --- | ||||||
|  |   --@param code (number) Error code | ||||||
|  |   --@param err (...) Other arguments may be passed depending on the error kind | ||||||
|  |   --@see |vim.lsp.client_errors| for possible errors. Use | ||||||
|  |   ---`vim.lsp.client_errors[code]` to get a human-friendly name. | ||||||
|   function handlers.on_error(code, err) |   function handlers.on_error(code, err) | ||||||
|     local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) |     local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) | ||||||
|     err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) |     err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) | ||||||
| @@ -371,6 +473,11 @@ function lsp.start_client(config) | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Invoked on client exit. | ||||||
|  |   --- | ||||||
|  |   --@param code (number) exit code of the process | ||||||
|  |   --@param signal (number) the signal used to terminate (if any) | ||||||
|   function handlers.on_exit(code, signal) |   function handlers.on_exit(code, signal) | ||||||
|     active_clients[client_id] = nil |     active_clients[client_id] = nil | ||||||
|     uninitialized_clients[client_id] = nil |     uninitialized_clients[client_id] = nil | ||||||
| @@ -411,6 +518,7 @@ function lsp.start_client(config) | |||||||
|   -- initialize finishes. |   -- initialize finishes. | ||||||
|   uninitialized_clients[client_id] = client; |   uninitialized_clients[client_id] = client; | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|   local function initialize() |   local function initialize() | ||||||
|     local valid_traces = { |     local valid_traces = { | ||||||
|       off = 'off'; messages = 'messages'; verbose = 'verbose'; |       off = 'off'; messages = 'messages'; verbose = 'verbose'; | ||||||
| @@ -488,6 +596,12 @@ function lsp.start_client(config) | |||||||
|     end) |     end) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Throws error for a method that is not supported by the current LSP | ||||||
|  |   --- server. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) an LSP method name not supported by the LSP server. | ||||||
|  |   --@returns (error) a 'MethodNotFound' JSON-RPC error response. | ||||||
|   local function unsupported_method(method) |   local function unsupported_method(method) | ||||||
|     local msg = "server doesn't support "..method |     local msg = "server doesn't support "..method | ||||||
|     local _ = log.warn() and log.warn(msg) |     local _ = log.warn() and log.warn(msg) | ||||||
| @@ -495,8 +609,28 @@ function lsp.start_client(config) | |||||||
|     return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) |     return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   --- Checks capabilities before rpc.request-ing. |   --@private | ||||||
|  |   --- Sends a request to the server. | ||||||
|  |   --- | ||||||
|  |   --- This is a thin wrapper around {client.rpc.request} with some additional | ||||||
|  |   --- checks for capabilities and callback availability. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) LSP method name. | ||||||
|  |   --@param params (table) LSP request params. | ||||||
|  |   --@param callback (function, optional) Response handler for this method. | ||||||
|  |   ---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. | ||||||
|  |   --@param bufnr (number) Buffer handle (0 for current). | ||||||
|  |   --@returns ({status}, [request_id]): {status} is a bool indicating | ||||||
|  |   ---whether the request was successful. If it is `false`, then it will | ||||||
|  |   ---always be `false` (the client has shutdown). If it was | ||||||
|  |   ---successful, then it will return {request_id} as the | ||||||
|  |   ---second result. You can use this with `client.cancel_request(request_id)` | ||||||
|  |   ---to cancel the-request. | ||||||
|  |   --@see |vim.lsp.buf_request()| | ||||||
|   function client.request(method, params, callback, bufnr) |   function client.request(method, params, callback, bufnr) | ||||||
|  |     -- FIXME: callback is optional, but bufnr is apparently not? Shouldn't that | ||||||
|  |     -- require a `select('#', ...)` call? | ||||||
|     if not callback then |     if not callback then | ||||||
|       callback = resolve_callback(method) |       callback = resolve_callback(method) | ||||||
|         or error(string.format("not found: %q request callback for client %q.", method, client.name)) |         or error(string.format("not found: %q request callback for client %q.", method, client.name)) | ||||||
| @@ -521,10 +655,25 @@ function lsp.start_client(config) | |||||||
|     end) |     end) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Sends a notification to an LSP server. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) LSP method name. | ||||||
|  |   --@param params (optional, table) LSP request params. | ||||||
|  |   --@param bufnr (number) Buffer handle, or 0 for current. | ||||||
|  |   --@returns {status} (bool) true if the notification was successful. | ||||||
|  |   ---If it is false, then it will always be false | ||||||
|  |   ---(the client has shutdown). | ||||||
|   function client.notify(...) |   function client.notify(...) | ||||||
|     return rpc.notify(...) |     return rpc.notify(...) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Cancels a request with a given request id. | ||||||
|  |   --- | ||||||
|  |   --@param id (number) id of request to cancel | ||||||
|  |   --@returns true if any client returns true; false otherwise | ||||||
|  |   --@see |vim.lsp.client.notify()| | ||||||
|   function client.cancel_request(id) |   function client.cancel_request(id) | ||||||
|     validate{id = {id, 'n'}} |     validate{id = {id, 'n'}} | ||||||
|     return rpc.notify("$/cancelRequest", { id = id }) |     return rpc.notify("$/cancelRequest", { id = id }) | ||||||
| @@ -533,6 +682,14 @@ function lsp.start_client(config) | |||||||
|   -- Track this so that we can escalate automatically if we've alredy tried a |   -- Track this so that we can escalate automatically if we've alredy tried a | ||||||
|   -- graceful shutdown |   -- graceful shutdown | ||||||
|   local tried_graceful_shutdown = false |   local tried_graceful_shutdown = false | ||||||
|  |   --@private | ||||||
|  |   --- Stops 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. | ||||||
|  |   --- | ||||||
|  |   --@param force (bool, optional) | ||||||
|   function client.stop(force) |   function client.stop(force) | ||||||
|     local handle = rpc.handle |     local handle = rpc.handle | ||||||
|     if handle:is_closing() then |     if handle:is_closing() then | ||||||
| @@ -554,10 +711,18 @@ function lsp.start_client(config) | |||||||
|     end) |     end) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Checks whether a client is stopped. | ||||||
|  |   --- | ||||||
|  |   --@returns (bool) true if client is stopped or in the process of being | ||||||
|  |   ---stopped; false otherwise | ||||||
|   function client.is_stopped() |   function client.is_stopped() | ||||||
|     return rpc.handle:is_closing() |     return rpc.handle:is_closing() | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Runs the on_attach function from the client's config if it was defined. | ||||||
|  |   --@param bufnr (number) Buffer number | ||||||
|   function client._on_attach(bufnr) |   function client._on_attach(bufnr) | ||||||
|     text_document_did_open_handler(bufnr, client) |     text_document_did_open_handler(bufnr, client) | ||||||
|     if config.on_attach then |     if config.on_attach then | ||||||
| @@ -571,6 +736,12 @@ function lsp.start_client(config) | |||||||
|   return client_id |   return client_id | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Memoizes a function. On first run, the function return value is saved and | ||||||
|  | --- immediately returned on subsequent runs. | ||||||
|  | --- | ||||||
|  | --@param fn (function) Function to run | ||||||
|  | --@returns (function) Memoized function | ||||||
| local function once(fn) | local function once(fn) | ||||||
|   local value |   local value | ||||||
|   return function(...) |   return function(...) | ||||||
| @@ -579,6 +750,9 @@ local function once(fn) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --@fn text_document_did_change_handler(_, bufnr, changedtick, firstline, lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size) | ||||||
|  | --- Notify all attached clients that a buffer has changed. | ||||||
| local text_document_did_change_handler | local text_document_did_change_handler | ||||||
| do | do | ||||||
|   local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; } |   local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; } | ||||||
| @@ -735,7 +909,7 @@ end | |||||||
| --- | --- | ||||||
| --@param client_id client id number | --@param client_id client id number | ||||||
| --- | --- | ||||||
| --@return |vim.lsp.client| object, or nil | --@returns |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 | ||||||
| @@ -769,7 +943,7 @@ end | |||||||
|  |  | ||||||
| --- Gets all active clients. | --- Gets all active clients. | ||||||
| --- | --- | ||||||
| --@return Table of |vim.lsp.client| objects | --@returns 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 | ||||||
| @@ -904,7 +1078,7 @@ end | |||||||
| --@param findstart 0 or 1, decides behavior | --@param findstart 0 or 1, decides behavior | ||||||
| --@param base If findstart=0, text to match against | --@param base If findstart=0, text to match against | ||||||
| --- | --- | ||||||
| --@return (number) Decided by `findstart`: | --@returns (number) Decided by `findstart`: | ||||||
| --- - findstart=0: column where the completion starts, or -2 or -3 | --- - findstart=0: column where the completion starts, or -2 or -3 | ||||||
| --- - findstart=1: list of matches (actually just calls |complete()|) | --- - findstart=1: list of matches (actually just calls |complete()|) | ||||||
| function lsp.omnifunc(findstart, base) | function lsp.omnifunc(findstart, base) | ||||||
| @@ -948,6 +1122,10 @@ function lsp.omnifunc(findstart, base) | |||||||
|   return -2 |   return -2 | ||||||
| end | end | ||||||
|  |  | ||||||
|  | ---Checks whether a client is stopped. | ||||||
|  | --- | ||||||
|  | --@param client_id (Number) | ||||||
|  | --@returns true if client is stopped, false otherwise. | ||||||
| function lsp.client_is_stopped(client_id) | function lsp.client_is_stopped(client_id) | ||||||
|   return active_clients[client_id] == nil |   return active_clients[client_id] == nil | ||||||
| end | end | ||||||
| @@ -992,12 +1170,17 @@ function lsp.set_log_level(level) | |||||||
| end | end | ||||||
|  |  | ||||||
| --- Gets the path of the logfile used by the LSP client. | --- Gets the path of the logfile used by the LSP client. | ||||||
|  | --@returns (String) Path to logfile. | ||||||
| function lsp.get_log_path() | function lsp.get_log_path() | ||||||
|   return log.get_filename() |   return log.get_filename() | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Define the LspDiagnostics signs if they're not defined already. | -- Defines the LspDiagnostics signs if they're not defined already. | ||||||
| do | do | ||||||
|  |   --@private | ||||||
|  |   --- Defines a sign if it isn't already defined. | ||||||
|  |   --@param name (String) Name of the sign | ||||||
|  |   --@param properties (table) Properties to attach to the sign | ||||||
|   local function define_default_sign(name, properties) |   local function define_default_sign(name, properties) | ||||||
|     if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then |     if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then | ||||||
|       vim.fn.sign_define(name, properties) |       vim.fn.sign_define(name, properties) | ||||||
|   | |||||||
| @@ -7,14 +7,41 @@ local list_extend = vim.list_extend | |||||||
|  |  | ||||||
| local M = {} | local M = {} | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Returns nil if {status} is false or nil, otherwise returns the rest of the | ||||||
|  | --- arguments. | ||||||
| local function ok_or_nil(status, ...) | local function ok_or_nil(status, ...) | ||||||
|   if not status then return end |   if not status then return end | ||||||
|   return ... |   return ... | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Swallows errors. | ||||||
|  | --- | ||||||
|  | --@param fn Function to run | ||||||
|  | --@param ... Function arguments | ||||||
|  | --@returns Result of `fn(...)` if there are no errors, otherwise nil. | ||||||
|  | --- Returns nil if errors occur during {fn}, otherwise returns | ||||||
| local function npcall(fn, ...) | local function npcall(fn, ...) | ||||||
|   return ok_or_nil(pcall(fn, ...)) |   return ok_or_nil(pcall(fn, ...)) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Sends an async request to all active clients attached to the current | ||||||
|  | --- buffer. | ||||||
|  | --- | ||||||
|  | --@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 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. | ||||||
|  | --- | ||||||
|  | --@see |vim.lsp.buf_request()| | ||||||
| local function request(method, params, callback) | local function request(method, params, callback) | ||||||
|   validate { |   validate { | ||||||
|     method = {method, 's'}; |     method = {method, 's'}; | ||||||
| @@ -23,9 +50,10 @@ local function request(method, params, callback) | |||||||
|   return vim.lsp.buf_request(0, method, params, callback) |   return vim.lsp.buf_request(0, method, params, callback) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Sends a notification through all clients associated with current buffer. | --- Checks whether the language servers attached to the current buffer are | ||||||
| -- | --- ready. | ||||||
| --@return `true` if server responds. | --- | ||||||
|  | --@returns `true` if server responds. | ||||||
| function M.server_ready() | function M.server_ready() | ||||||
|   return not not vim.lsp.buf_notify(0, "window/progress", {}) |   return not not vim.lsp.buf_notify(0, "window/progress", {}) | ||||||
| end | end | ||||||
| @@ -74,6 +102,12 @@ end | |||||||
|  |  | ||||||
| --- Retrieves the completion items at the current cursor position. Can only be | --- Retrieves the completion items at the current cursor position. Can only be | ||||||
| --- called in Insert mode. | --- called in Insert mode. | ||||||
|  | --- | ||||||
|  | --@param context (context support not yet implemented) Additional information | ||||||
|  | --- about the context in which a completion was triggered (how it was triggered, | ||||||
|  | --- and by which trigger character, if applicable) | ||||||
|  | --- | ||||||
|  | --@see |vim.lsp.protocol.constants.CompletionTriggerKind| | ||||||
| function M.completion(context) | function M.completion(context) | ||||||
|   local params = util.make_position_params() |   local params = util.make_position_params() | ||||||
|   params.context = context |   params.context = context | ||||||
| @@ -82,20 +116,27 @@ end | |||||||
|  |  | ||||||
| --- Formats the current buffer. | --- Formats the current buffer. | ||||||
| --- | --- | ||||||
| --- The optional {options} table can be used to specify FormattingOptions, a | --@param options (optional, table) Can be used to specify FormattingOptions. | ||||||
| --- list of which is available at |  | ||||||
| --- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting. |  | ||||||
| --- Some unspecified options will be automatically derived from the current | --- Some unspecified options will be automatically derived from the current | ||||||
| --- Neovim options. | --- Neovim options. | ||||||
|  | -- | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting | ||||||
| function M.formatting(options) | function M.formatting(options) | ||||||
|   local params = util.make_formatting_params(options) |   local params = util.make_formatting_params(options) | ||||||
|   return request('textDocument/formatting', params) |   return request('textDocument/formatting', params) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Perform |vim.lsp.buf.formatting()| synchronously. | --- Performs |vim.lsp.buf.formatting()| synchronously. | ||||||
| --- | --- | ||||||
| --- Useful for running on save, to make sure buffer is formatted prior to being | --- Useful for running on save, to make sure buffer is formatted prior to being | ||||||
| --- saved.  {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. | --- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example: | ||||||
|  | --- | ||||||
|  | --- <pre> | ||||||
|  | --- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] | ||||||
|  | --- </pre> | ||||||
|  | --- | ||||||
|  | --@param options Table with valid `FormattingOptions` entries | ||||||
|  | --@param timeout_ms (number) Request timeout | ||||||
| function M.formatting_sync(options, timeout_ms) | function M.formatting_sync(options, timeout_ms) | ||||||
|   local params = util.make_formatting_params(options) |   local params = util.make_formatting_params(options) | ||||||
|   local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms) |   local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms) | ||||||
| @@ -104,6 +145,13 @@ function M.formatting_sync(options, timeout_ms) | |||||||
|   vim.lsp.util.apply_text_edits(result) |   vim.lsp.util.apply_text_edits(result) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Formats a given range. | ||||||
|  | --- | ||||||
|  | --@param options Table with valid `FormattingOptions` entries. | ||||||
|  | --@param start_pos ({number, number}, optional) mark-indexed position. | ||||||
|  | ---Defaults to the start of the last visual selection. | ||||||
|  | --@param start_pos ({number, number}, optional) mark-indexed position. | ||||||
|  | ---Defaults to the end of the last visual selection. | ||||||
| function M.range_formatting(options, start_pos, end_pos) | function M.range_formatting(options, start_pos, end_pos) | ||||||
|   validate { |   validate { | ||||||
|     options = {options, 't', true}; |     options = {options, 't', true}; | ||||||
| @@ -138,8 +186,10 @@ function M.range_formatting(options, start_pos, end_pos) | |||||||
|   return request('textDocument/rangeFormatting', params) |   return request('textDocument/rangeFormatting', params) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Renames all references to the symbol under the cursor. If {new_name} is not | --- Renames all references to the symbol under the cursor. | ||||||
| --- provided, the user will be prompted for a new name using |input()|. | --- | ||||||
|  | --@param new_name (string) If not provided, the user will be prompted for a new | ||||||
|  | ---name using |input()|. | ||||||
| function M.rename(new_name) | function M.rename(new_name) | ||||||
|   -- TODO(ashkan) use prepareRename |   -- TODO(ashkan) use prepareRename | ||||||
|   -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. |   -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. | ||||||
| @@ -152,6 +202,8 @@ end | |||||||
|  |  | ||||||
| --- Lists all the references to the symbol under the cursor in the quickfix window. | --- Lists all the references to the symbol under the cursor in the quickfix window. | ||||||
| --- | --- | ||||||
|  | --@param context (table) Context for the request | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references | ||||||
| function M.references(context) | function M.references(context) | ||||||
|   validate { context = { context, 't', true } } |   validate { context = { context, 't', true } } | ||||||
|   local params = util.make_position_params() |   local params = util.make_position_params() | ||||||
| @@ -169,6 +221,7 @@ function M.document_symbol() | |||||||
|   request('textDocument/documentSymbol', params) |   request('textDocument/documentSymbol', params) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
| local function pick_call_hierarchy_item(call_hierarchy_items) | local function pick_call_hierarchy_item(call_hierarchy_items) | ||||||
|   if not call_hierarchy_items then return end |   if not call_hierarchy_items then return end | ||||||
|   if #call_hierarchy_items == 1 then |   if #call_hierarchy_items == 1 then | ||||||
| @@ -186,6 +239,9 @@ local function pick_call_hierarchy_item(call_hierarchy_items) | |||||||
|   return choice |   return choice | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Lists all the call sites of the symbol under the cursor in the | ||||||
|  | --- |quickfix| window. If the symbol can resolve to multiple | ||||||
|  | --- items, the user can pick one in the |inputlist|. | ||||||
| function M.incoming_calls() | function M.incoming_calls() | ||||||
|   local params = util.make_position_params() |   local params = util.make_position_params() | ||||||
|   request('textDocument/prepareCallHierarchy', params, function(_, _, result) |   request('textDocument/prepareCallHierarchy', params, function(_, _, result) | ||||||
| @@ -194,6 +250,9 @@ function M.incoming_calls() | |||||||
|   end) |   end) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Lists all the items that are called by the symbol under the | ||||||
|  | --- cursor in the |quickfix| window. If the symbol can resolve to | ||||||
|  | --- multiple items, the user can pick one in the |inputlist|. | ||||||
| function M.outgoing_calls() | function M.outgoing_calls() | ||||||
|   local params = util.make_position_params() |   local params = util.make_position_params() | ||||||
|   request('textDocument/prepareCallHierarchy', params, function(_, _, result) |   request('textDocument/prepareCallHierarchy', params, function(_, _, result) | ||||||
| @@ -204,9 +263,11 @@ end | |||||||
|  |  | ||||||
| --- Lists all symbols in the current workspace in the quickfix window. | --- Lists all symbols in the current workspace in the quickfix window. | ||||||
| --- | --- | ||||||
| --- The list is filtered against the optional argument {query}; | --- The list is filtered against {query}; if the argument is omitted from the | ||||||
| --- if the argument is omitted from the call, the user is prompted to enter a string on the command line. | --- call, the user is prompted to enter a string on the command line. An empty | ||||||
| --- An empty string means no filtering is done. | --- string means no filtering is done. | ||||||
|  | --- | ||||||
|  | --@param query (string, optional) | ||||||
| function M.workspace_symbol(query) | function M.workspace_symbol(query) | ||||||
|   query = query or npcall(vfn.input, "Query: ") |   query = query or npcall(vfn.input, "Query: ") | ||||||
|   local params = {query = query} |   local params = {query = query} | ||||||
| @@ -227,10 +288,17 @@ function M.document_highlight() | |||||||
|   request('textDocument/documentHighlight', params) |   request('textDocument/documentHighlight', params) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Removes document highlights from current buffer. | ||||||
|  | --- | ||||||
| function M.clear_references() | function M.clear_references() | ||||||
|   util.buf_clear_references() |   util.buf_clear_references() | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Selects a code action from the input list that is available at the current | ||||||
|  | --- cursor position. | ||||||
|  | -- | ||||||
|  | --@param context: (table, optional) Valid `CodeActionContext` object | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction | ||||||
| function M.code_action(context) | function M.code_action(context) | ||||||
|   validate { context = { context, 't', true } } |   validate { context = { context, 't', true } } | ||||||
|   context = context or { diagnostics = util.get_line_diagnostics() } |   context = context or { diagnostics = util.get_line_diagnostics() } | ||||||
| @@ -239,6 +307,10 @@ function M.code_action(context) | |||||||
|   request('textDocument/codeAction', params) |   request('textDocument/codeAction', params) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Executes an LSP server command. | ||||||
|  | --- | ||||||
|  | --@param command A valid `ExecuteCommandParams` object | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand | ||||||
| function M.execute_command(command) | function M.execute_command(command) | ||||||
|   validate { |   validate { | ||||||
|     command = { command.command, 's' }, |     command = { command.command, 's' }, | ||||||
|   | |||||||
| @@ -7,17 +7,22 @@ local buf = require 'vim.lsp.buf' | |||||||
|  |  | ||||||
| local M = {} | local M = {} | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Writes to error buffer. | ||||||
|  | --@param ... (table of strings) Will be concatenated before being written | ||||||
| local function err_message(...) | local function err_message(...) | ||||||
|   api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) |   api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) | ||||||
|   api.nvim_command("redraw") |   api.nvim_command("redraw") | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand | ||||||
| M['workspace/executeCommand'] = function(err, _) | M['workspace/executeCommand'] = function(err, _) | ||||||
|   if err then |   if err then | ||||||
|     error("Could not execute code action: "..err.message) |     error("Could not execute code action: "..err.message) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction | ||||||
| M['textDocument/codeAction'] = function(_, _, actions) | M['textDocument/codeAction'] = function(_, _, actions) | ||||||
|   if actions == nil or vim.tbl_isempty(actions) then |   if actions == nil or vim.tbl_isempty(actions) then | ||||||
|     print("No code actions available") |     print("No code actions available") | ||||||
| @@ -51,6 +56,7 @@ M['textDocument/codeAction'] = function(_, _, actions) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit | ||||||
| M['workspace/applyEdit'] = function(_, _, workspace_edit) | M['workspace/applyEdit'] = function(_, _, workspace_edit) | ||||||
|   if not workspace_edit then return end |   if not workspace_edit then return end | ||||||
|   -- TODO(ashkan) Do something more with label? |   -- TODO(ashkan) Do something more with label? | ||||||
| @@ -64,6 +70,7 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_publishDiagnostics | ||||||
| M['textDocument/publishDiagnostics'] = function(_, _, result) | M['textDocument/publishDiagnostics'] = function(_, _, result) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   local uri = result.uri |   local uri = result.uri | ||||||
| @@ -102,6 +109,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) | |||||||
|   vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") |   vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references | ||||||
| M['textDocument/references'] = function(_, _, result) | M['textDocument/references'] = function(_, _, result) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   util.set_qflist(util.locations_to_items(result)) |   util.set_qflist(util.locations_to_items(result)) | ||||||
| @@ -109,6 +117,13 @@ M['textDocument/references'] = function(_, _, result) | |||||||
|   api.nvim_command("wincmd p") |   api.nvim_command("wincmd p") | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Prints given list of symbols to the quickfix list. | ||||||
|  | --@param _ (not used) | ||||||
|  | --@param _ (not used) | ||||||
|  | --@param result (list of Symbols) LSP method name | ||||||
|  | --@param result (table) result of LSP method; a location or a list of locations. | ||||||
|  | ---(`textDocument/definition` can return `Location` or `Location[]` | ||||||
| local symbol_callback = function(_, _, result, _, bufnr) | local symbol_callback = function(_, _, result, _, bufnr) | ||||||
|   if not result or vim.tbl_isempty(result) then return end |   if not result or vim.tbl_isempty(result) then return end | ||||||
|  |  | ||||||
| @@ -116,24 +131,30 @@ local symbol_callback = function(_, _, result, _, bufnr) | |||||||
|   api.nvim_command("copen") |   api.nvim_command("copen") | ||||||
|   api.nvim_command("wincmd p") |   api.nvim_command("wincmd p") | ||||||
| end | end | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol | ||||||
| M['textDocument/documentSymbol'] = symbol_callback | M['textDocument/documentSymbol'] = symbol_callback | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol | ||||||
| M['workspace/symbol'] = symbol_callback | M['workspace/symbol'] = symbol_callback | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename | ||||||
| M['textDocument/rename'] = function(_, _, result) | M['textDocument/rename'] = function(_, _, result) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   util.apply_workspace_edit(result) |   util.apply_workspace_edit(result) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting | ||||||
| M['textDocument/rangeFormatting'] = function(_, _, result) | M['textDocument/rangeFormatting'] = function(_, _, result) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   util.apply_text_edits(result) |   util.apply_text_edits(result) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting | ||||||
| M['textDocument/formatting'] = function(_, _, result) | M['textDocument/formatting'] = function(_, _, result) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   util.apply_text_edits(result) |   util.apply_text_edits(result) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||||
| M['textDocument/completion'] = function(_, _, result) | M['textDocument/completion'] = function(_, _, result) | ||||||
|   if vim.tbl_isempty(result or {}) then return end |   if vim.tbl_isempty(result or {}) then return end | ||||||
|   local row, col = unpack(api.nvim_win_get_cursor(0)) |   local row, col = unpack(api.nvim_win_get_cursor(0)) | ||||||
| @@ -146,6 +167,7 @@ M['textDocument/completion'] = function(_, _, result) | |||||||
|   vim.fn.complete(textMatch+1, matches) |   vim.fn.complete(textMatch+1, matches) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover | ||||||
| M['textDocument/hover'] = function(_, method, result) | M['textDocument/hover'] = function(_, method, result) | ||||||
|   util.focusable_float(method, function() |   util.focusable_float(method, function() | ||||||
|     if not (result and result.contents) then |     if not (result and result.contents) then | ||||||
| @@ -166,6 +188,12 @@ M['textDocument/hover'] = function(_, method, result) | |||||||
|   end) |   end) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Jumps to a location. Used as a callback for multiple LSP methods. | ||||||
|  | --@param _ (not used) | ||||||
|  | --@param method (string) LSP method name | ||||||
|  | --@param result (table) result of LSP method; a location or a list of locations. | ||||||
|  | ---(`textDocument/definition` can return `Location` or `Location[]` | ||||||
| local function location_callback(_, method, result) | local function location_callback(_, method, result) | ||||||
|   if result == nil or vim.tbl_isempty(result) then |   if result == nil or vim.tbl_isempty(result) then | ||||||
|     local _ = log.info() and log.info(method, 'No location found') |     local _ = log.info() and log.info(method, 'No location found') | ||||||
| @@ -188,11 +216,16 @@ local function location_callback(_, method, result) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration | ||||||
| M['textDocument/declaration'] = location_callback | M['textDocument/declaration'] = location_callback | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition | ||||||
| M['textDocument/definition'] = location_callback | M['textDocument/definition'] = location_callback | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition | ||||||
| M['textDocument/typeDefinition'] = location_callback | M['textDocument/typeDefinition'] = location_callback | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation | ||||||
| M['textDocument/implementation'] = location_callback | M['textDocument/implementation'] = location_callback | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp | ||||||
| M['textDocument/signatureHelp'] = function(_, method, result) | M['textDocument/signatureHelp'] = function(_, method, result) | ||||||
|   util.focusable_preview(method, function() |   util.focusable_preview(method, function() | ||||||
|     if not (result and result.signatures and result.signatures[1]) then |     if not (result and result.signatures and result.signatures[1]) then | ||||||
| @@ -208,15 +241,21 @@ M['textDocument/signatureHelp'] = function(_, method, result) | |||||||
|   end) |   end) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight | ||||||
| M['textDocument/documentHighlight'] = function(_, _, result, _) | M['textDocument/documentHighlight'] = function(_, _, result, _) | ||||||
|   if not result then return end |   if not result then return end | ||||||
|   local bufnr = api.nvim_get_current_buf() |   local bufnr = api.nvim_get_current_buf() | ||||||
|   util.buf_highlight_references(bufnr, result) |   util.buf_highlight_references(bufnr, result) | ||||||
| end | end | ||||||
|  |  | ||||||
| -- direction is "from" for incoming calls and "to" for outgoing calls | --@private | ||||||
|  | --- | ||||||
|  | --- Displays call hierarchy in the quickfix window. | ||||||
|  | --- | ||||||
|  | --@param direction `"from"` for incoming calls and `"to"` for outgoing calls | ||||||
|  | --@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`, | ||||||
|  | --@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, | ||||||
| local make_call_hierarchy_callback = function(direction) | local make_call_hierarchy_callback = function(direction) | ||||||
|   -- result is a CallHierarchy{Incoming,Outgoing}Call[] |  | ||||||
|   return function(_, _, result) |   return function(_, _, result) | ||||||
|     if not result then return end |     if not result then return end | ||||||
|     local items = {} |     local items = {} | ||||||
| @@ -237,10 +276,13 @@ local make_call_hierarchy_callback = function(direction) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/incomingCalls | ||||||
| M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from') | M['callHierarchy/incomingCalls'] = make_call_hierarchy_callback('from') | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/outgoingCalls | ||||||
| M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to') | M['callHierarchy/outgoingCalls'] = make_call_hierarchy_callback('to') | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage | ||||||
| M['window/logMessage'] = function(_, _, result, client_id) | M['window/logMessage'] = function(_, _, result, client_id) | ||||||
|   local message_type = result.type |   local message_type = result.type | ||||||
|   local message = result.message |   local message = result.message | ||||||
| @@ -261,6 +303,7 @@ M['window/logMessage'] = function(_, _, result, client_id) | |||||||
|   return result |   return result | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/showMessage | ||||||
| M['window/showMessage'] = function(_, _, result, client_id) | M['window/showMessage'] = function(_, _, result, client_id) | ||||||
|   local message_type = result.type |   local message_type = result.type | ||||||
|   local message = result.message |   local message = result.message | ||||||
|   | |||||||
| @@ -21,12 +21,14 @@ local log_date_format = "%FT%H:%M:%S%z" | |||||||
|  |  | ||||||
| do | do | ||||||
|   local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" |   local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" | ||||||
|  |   --@private | ||||||
|   local function path_join(...) |   local function path_join(...) | ||||||
|     return table.concat(vim.tbl_flatten{...}, path_sep) |     return table.concat(vim.tbl_flatten{...}, path_sep) | ||||||
|   end |   end | ||||||
|   local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log') |   local logfilename = path_join(vim.fn.stdpath('data'), 'lsp.log') | ||||||
|  |  | ||||||
|   --- Return the log filename. |   --- Returns the log filename. | ||||||
|  |   --@returns (string) log filename | ||||||
|   function log.get_filename() |   function log.get_filename() | ||||||
|     return logfilename |     return logfilename | ||||||
|   end |   end | ||||||
| @@ -74,6 +76,8 @@ end | |||||||
| -- interfere with iterating the levels | -- interfere with iterating the levels | ||||||
| vim.tbl_add_reverse_lookup(log.levels) | vim.tbl_add_reverse_lookup(log.levels) | ||||||
|  |  | ||||||
|  | --- Sets the current log level. | ||||||
|  | --@param level (string or number) One of `vim.lsp.log.levels` | ||||||
| function log.set_level(level) | function log.set_level(level) | ||||||
|   if type(level) == 'string' then |   if type(level) == 'string' then | ||||||
|     current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) |     current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) | ||||||
| @@ -84,8 +88,9 @@ function log.set_level(level) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Return whether the level is sufficient for logging. | --- Checks whether the level is sufficient for logging. | ||||||
| -- @param level number log level | --@param level number log level | ||||||
|  | --@returns (bool) true if would log, false if not | ||||||
| function log.should_log(level) | function log.should_log(level) | ||||||
|   return level >= current_log_level |   return level >= current_log_level | ||||||
| end | end | ||||||
|   | |||||||
| @@ -2,6 +2,11 @@ | |||||||
|  |  | ||||||
| local protocol = {} | local protocol = {} | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Returns {a} if it is not nil, otherwise returns {b}. | ||||||
|  | --- | ||||||
|  | --@param a | ||||||
|  | --@param b | ||||||
| local function ifnil(a, b) | local function ifnil(a, b) | ||||||
|   if a == nil then return b end |   if a == nil then return b end | ||||||
|   return a |   return a | ||||||
| @@ -9,12 +14,14 @@ end | |||||||
|  |  | ||||||
|  |  | ||||||
| --[=[ | --[=[ | ||||||
| -- Useful for interfacing with: | --@private | ||||||
| -- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md | --- Useful for interfacing with: | ||||||
|  | --- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md | ||||||
| function transform_schema_comments() | function transform_schema_comments() | ||||||
|   nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] |   nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] | ||||||
|   nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] |   nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] | ||||||
| end | end | ||||||
|  | --@private | ||||||
| function transform_schema_to_table() | function transform_schema_to_table() | ||||||
|   transform_schema_comments() |   transform_schema_comments() | ||||||
|   nvim.command [[silent! '<,'>s/: \S\+//]] |   nvim.command [[silent! '<,'>s/: \S\+//]] | ||||||
|   | |||||||
| @@ -5,6 +5,11 @@ local protocol = require('vim.lsp.protocol') | |||||||
| local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap | local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap | ||||||
|  |  | ||||||
| -- TODO replace with a better implementation. | -- TODO replace with a better implementation. | ||||||
|  | --@private | ||||||
|  | --- Encodes to JSON. | ||||||
|  | --- | ||||||
|  | --@param data (table) Data to encode | ||||||
|  | --@returns (string) Encoded object | ||||||
| local function json_encode(data) | local function json_encode(data) | ||||||
|   local status, result = pcall(vim.fn.json_encode, data) |   local status, result = pcall(vim.fn.json_encode, data) | ||||||
|   if status then |   if status then | ||||||
| @@ -13,6 +18,11 @@ local function json_encode(data) | |||||||
|     return nil, result |     return nil, result | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | --@private | ||||||
|  | --- Decodes from JSON. | ||||||
|  | --- | ||||||
|  | --@param data (string) Data to decode | ||||||
|  | --@returns (table) Decoded JSON object | ||||||
| local function json_decode(data) | local function json_decode(data) | ||||||
|   local status, result = pcall(vim.fn.json_decode, data) |   local status, result = pcall(vim.fn.json_decode, data) | ||||||
|   if status then |   if status then | ||||||
| @@ -22,17 +32,26 @@ local function json_decode(data) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Checks whether a given path exists and is a directory. | ||||||
|  | --@param filename (string) path to check | ||||||
|  | --@returns (bool) | ||||||
| local function is_dir(filename) | local function is_dir(filename) | ||||||
|   local stat = vim.loop.fs_stat(filename) |   local stat = vim.loop.fs_stat(filename) | ||||||
|   return stat and stat.type == 'directory' or false |   return stat and stat.type == 'directory' or false | ||||||
| end | end | ||||||
|  |  | ||||||
| local NIL = vim.NIL | local NIL = vim.NIL | ||||||
|  | --@private | ||||||
|  | --- Returns its argument, but converts `vim.NIL` to Lua `nil`. | ||||||
|  | --@param v (any) Argument | ||||||
|  | --@returns (any) | ||||||
| local function convert_NIL(v) | local function convert_NIL(v) | ||||||
|   if v == NIL then return nil end |   if v == NIL then return nil end | ||||||
|   return v |   return v | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
| --- Merges current process env with the given env and returns the result as | --- Merges current process env with the given env and returns the result as | ||||||
| --- a list of "k=v" strings. | --- a list of "k=v" strings. | ||||||
| --- | --- | ||||||
| @@ -42,6 +61,8 @@ end | |||||||
| ---  in:    { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } | ---  in:    { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } | ||||||
| ---  out:   { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } | ---  out:   { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } | ||||||
| --- </pre> | --- </pre> | ||||||
|  | --@param env (table) table of environment variable assignments | ||||||
|  | --@returns (table) list of `"k=v"` strings | ||||||
| local function env_merge(env) | local function env_merge(env) | ||||||
|   if env == nil then |   if env == nil then | ||||||
|     return env |     return env | ||||||
| @@ -56,6 +77,11 @@ local function env_merge(env) | |||||||
|   return final_env |   return final_env | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Embeds the given string into a table and correctly computes `Content-Length`. | ||||||
|  | --- | ||||||
|  | --@param encoded_message (string) | ||||||
|  | --@returns (table) table containing encoded message and `Content-Length` attribute | ||||||
| local function format_message_with_content_length(encoded_message) | local function format_message_with_content_length(encoded_message) | ||||||
|   return table.concat { |   return table.concat { | ||||||
|     'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; |     'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; | ||||||
| @@ -63,8 +89,11 @@ local function format_message_with_content_length(encoded_message) | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Parse an LSP Message's header | --@private | ||||||
| -- @param header: The header to parse. | --- Parses an LSP Message's header | ||||||
|  | --- | ||||||
|  | --@param header: The header to parse. | ||||||
|  | --@returns Parsed headers | ||||||
| local function parse_headers(header) | local function parse_headers(header) | ||||||
|   if type(header) ~= 'string' then |   if type(header) ~= 'string' then | ||||||
|     return nil |     return nil | ||||||
| @@ -92,6 +121,8 @@ end | |||||||
| -- case insensitive pattern. | -- case insensitive pattern. | ||||||
| local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) | local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- The actual workhorse. | ||||||
| local function request_parser_loop() | local function request_parser_loop() | ||||||
|   local buffer = '' |   local buffer = '' | ||||||
|   while true do |   while true do | ||||||
| @@ -138,6 +169,10 @@ local client_errors = vim.tbl_add_reverse_lookup { | |||||||
|   SERVER_RESULT_CALLBACK_ERROR = 7; |   SERVER_RESULT_CALLBACK_ERROR = 7; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | --- Constructs an error message from an LSP error object. | ||||||
|  | --- | ||||||
|  | --@param err (table) The error object | ||||||
|  | --@returns (string) The formatted error message | ||||||
| local function format_rpc_error(err) | local function format_rpc_error(err) | ||||||
|   validate { |   validate { | ||||||
|     err = { err, 't' }; |     err = { err, 't' }; | ||||||
| @@ -182,23 +217,69 @@ local function rpc_response_error(code, message, data) | |||||||
| end | end | ||||||
|  |  | ||||||
| local default_handlers = {} | local default_handlers = {} | ||||||
|  | --@private | ||||||
|  | --- Default handler for notifications sent to an LSP server. | ||||||
|  | --- | ||||||
|  | --@param method (string) The invoked LSP method | ||||||
|  | --@param params (table): Parameters for the invoked LSP method | ||||||
| function default_handlers.notification(method, params) | function default_handlers.notification(method, params) | ||||||
|   local _ = log.debug() and log.debug('notification', method, params) |   local _ = log.debug() and log.debug('notification', method, params) | ||||||
| end | end | ||||||
|  | --@private | ||||||
|  | --- Default handler for requests sent to an LSP server. | ||||||
|  | --- | ||||||
|  | --@param method (string) The invoked LSP method | ||||||
|  | --@param params (table): Parameters for the invoked LSP method | ||||||
|  | --@returns `nil` and `vim.lsp.protocol.ErrorCodes.MethodNotFound`. | ||||||
| function default_handlers.server_request(method, params) | function default_handlers.server_request(method, params) | ||||||
|   local _ = log.debug() and log.debug('server_request', method, params) |   local _ = log.debug() and log.debug('server_request', method, params) | ||||||
|   return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) |   return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) | ||||||
| end | end | ||||||
|  | --@private | ||||||
|  | --- Default handler for when a client exits. | ||||||
|  | --- | ||||||
|  | --@param code (number): Exit code | ||||||
|  | --@param signal (number): Number describing the signal used to terminate (if | ||||||
|  | ---any) | ||||||
| function default_handlers.on_exit(code, signal) | function default_handlers.on_exit(code, signal) | ||||||
|   local _ = log.info() and log.info("client exit", { code = code, signal = signal }) |   local _ = log.info() and log.info("client_exit", { code = code, signal = signal }) | ||||||
| end | end | ||||||
|  | --@private | ||||||
|  | --- Default handler for client errors. | ||||||
|  | --- | ||||||
|  | --@param code (number): Error code | ||||||
|  | --@param err (any): Details about the error | ||||||
|  | ---any) | ||||||
| function default_handlers.on_error(code, err) | function default_handlers.on_error(code, err) | ||||||
|   local _ = log.error() and log.error('client_error:', client_errors[code], err) |   local _ = log.error() and log.error('client_error:', client_errors[code], err) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Create and start an RPC client. | --- Starts an LSP server process and create an LSP RPC client object to | ||||||
| -- @param cmd [ | --- interact with it. | ||||||
| local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params) | --- | ||||||
|  | --@param cmd (string) Command to start the LSP server. | ||||||
|  | --@param cmd_args (table) List of additional string arguments to pass to {cmd}. | ||||||
|  | --@param handlers (table, optional) Handlers for LSP message types. Valid | ||||||
|  | ---handler names are: | ||||||
|  | --- - `"notification"` | ||||||
|  | --- - `"server_request"` | ||||||
|  | --- - `"on_error"` | ||||||
|  | --- - `"on_exit"` | ||||||
|  | --@param extra_spawn_params (table, optional) Additional context for the LSP | ||||||
|  | --- server process. May contain: | ||||||
|  | --- - {cwd} (string) Working directory for the LSP server process | ||||||
|  | --- - {env} (table) Additional environment variables for LSP server process | ||||||
|  | --@returns Client RPC object. | ||||||
|  | --- | ||||||
|  | --@returns Methods: | ||||||
|  | --- - `notify()` |vim.lsp.rpc.notify()| | ||||||
|  | --- - `request()` |vim.lsp.rpc.request()| | ||||||
|  | --- | ||||||
|  | --@returns Members: | ||||||
|  | --- - {pid} (number) The LSP server's PID. | ||||||
|  | --- - {handle} A handle for low-level interaction with the LSP server process | ||||||
|  | ---   |vim.loop|. | ||||||
|  | local function start(cmd, cmd_args, handlers, extra_spawn_params) | ||||||
|   local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params}) |   local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params}) | ||||||
|   validate { |   validate { | ||||||
|     cmd = { cmd, 's' }; |     cmd = { cmd, 's' }; | ||||||
| @@ -242,6 +323,11 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|  |  | ||||||
|   local handle, pid |   local handle, pid | ||||||
|   do |   do | ||||||
|  |     --@private | ||||||
|  |     --- Callback for |vim.loop.spawn()| Closes all streams and runs the | ||||||
|  |     --- `on_exit` handler. | ||||||
|  |     --@param code (number) Exit code | ||||||
|  |     --@param signal (number) Signal that was used to terminate (if any) | ||||||
|     local function onexit(code, signal) |     local function onexit(code, signal) | ||||||
|       stdin:close() |       stdin:close() | ||||||
|       stdout:close() |       stdout:close() | ||||||
| @@ -265,6 +351,12 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     handle, pid = uv.spawn(cmd, spawn_params, onexit) |     handle, pid = uv.spawn(cmd, spawn_params, onexit) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- Encodes {payload} into a JSON-RPC message and sends it to the remote | ||||||
|  |   --- process. | ||||||
|  |   --- | ||||||
|  |   --@param payload (table) Converted into a JSON string, see |json_encode()| | ||||||
|  |   --@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. | ||||||
|   local function encode_and_send(payload) |   local function encode_and_send(payload) | ||||||
|     local _ = log.debug() and log.debug("rpc.send.payload", payload) |     local _ = log.debug() and log.debug("rpc.send.payload", payload) | ||||||
|     if handle:is_closing() then return false end |     if handle:is_closing() then return false end | ||||||
| @@ -276,7 +368,12 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     return true |     return true | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   local function send_notification(method, params) |   -- FIXME: Should be placed on the RPC client object returned by `start()` | ||||||
|  |   --- Sends a notification to the LSP server. | ||||||
|  |   --@param method (string) The invoked LSP method | ||||||
|  |   --@param params (table): Parameters for the invoked LSP method | ||||||
|  |   --@returns (bool) `true` if notification could be sent, `false` if not | ||||||
|  |   local function notify(method, params) | ||||||
|     local _ = log.debug() and log.debug("rpc.notify", method, params) |     local _ = log.debug() and log.debug("rpc.notify", method, params) | ||||||
|     return encode_and_send { |     return encode_and_send { | ||||||
|       jsonrpc = "2.0"; |       jsonrpc = "2.0"; | ||||||
| @@ -285,6 +382,8 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     } |     } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|  |   --- sends an error object to the remote LSP process. | ||||||
|   local function send_response(request_id, err, result) |   local function send_response(request_id, err, result) | ||||||
|     return encode_and_send { |     return encode_and_send { | ||||||
|       id = request_id; |       id = request_id; | ||||||
| @@ -294,7 +393,14 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     } |     } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   local function send_request(method, params, callback) |   -- FIXME: Should be placed on the RPC client object returned by `start()` | ||||||
|  |   --- Sends a request to the LSP server and runs {callback} upon response. | ||||||
|  |   --- | ||||||
|  |   --@param method (string) The invoked LSP method | ||||||
|  |   --@param params (table) Parameters for the invoked LSP method | ||||||
|  |   --@param callback (function) Callback to invoke | ||||||
|  |   --@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not | ||||||
|  |   local function request(method, params, callback) | ||||||
|     validate { |     validate { | ||||||
|       callback = { callback, 'f' }; |       callback = { callback, 'f' }; | ||||||
|     } |     } | ||||||
| @@ -320,11 +426,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     end |     end | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|   local function on_error(errkind, ...) |   local function on_error(errkind, ...) | ||||||
|     assert(client_errors[errkind]) |     assert(client_errors[errkind]) | ||||||
|     -- TODO what to do if this fails? |     -- TODO what to do if this fails? | ||||||
|     pcall(handlers.on_error, errkind, ...) |     pcall(handlers.on_error, errkind, ...) | ||||||
|   end |   end | ||||||
|  |   --@private | ||||||
|   local function pcall_handler(errkind, status, head, ...) |   local function pcall_handler(errkind, status, head, ...) | ||||||
|     if not status then |     if not status then | ||||||
|       on_error(errkind, head, ...) |       on_error(errkind, head, ...) | ||||||
| @@ -332,6 +440,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|     end |     end | ||||||
|     return status, head, ... |     return status, head, ... | ||||||
|   end |   end | ||||||
|  |   --@private | ||||||
|   local function try_call(errkind, fn, ...) |   local function try_call(errkind, fn, ...) | ||||||
|     return pcall_handler(errkind, pcall(fn, ...)) |     return pcall_handler(errkind, pcall(fn, ...)) | ||||||
|   end |   end | ||||||
| @@ -340,6 +449,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|   -- time and log them. This would require storing the timestamp. I could call |   -- time and log them. This would require storing the timestamp. I could call | ||||||
|   -- them with an error then, perhaps. |   -- them with an error then, perhaps. | ||||||
|  |  | ||||||
|  |   --@private | ||||||
|   local function handle_body(body) |   local function handle_body(body) | ||||||
|     local decoded, err = json_decode(body) |     local decoded, err = json_decode(body) | ||||||
|     if not decoded then |     if not decoded then | ||||||
| @@ -458,13 +568,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | |||||||
|   return { |   return { | ||||||
|     pid = pid; |     pid = pid; | ||||||
|     handle = handle; |     handle = handle; | ||||||
|     request = send_request; |     request = request; | ||||||
|     notify = send_notification; |     notify = notify | ||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
| return { | return { | ||||||
|   start = create_and_start_client; |   start = start; | ||||||
|   rpc_response_error = rpc_response_error; |   rpc_response_error = rpc_response_error; | ||||||
|   format_rpc_error = format_rpc_error; |   format_rpc_error = format_rpc_error; | ||||||
|   client_errors = client_errors; |   client_errors = client_errors; | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ local highlight = require 'vim.highlight' | |||||||
|  |  | ||||||
| local M = {} | local M = {} | ||||||
|  |  | ||||||
|  | -- FIXME: Expose in documentation | ||||||
| --- Diagnostics received from the server via `textDocument/publishDiagnostics` | --- Diagnostics received from the server via `textDocument/publishDiagnostics` | ||||||
| -- by buffer. | -- by buffer. | ||||||
| -- | -- | ||||||
| @@ -33,18 +34,30 @@ local M = {} | |||||||
| M.diagnostics_by_buf = {} | M.diagnostics_by_buf = {} | ||||||
|  |  | ||||||
| local split = vim.split | local split = vim.split | ||||||
|  | --@private | ||||||
| local function split_lines(value) | local function split_lines(value) | ||||||
|   return split(value, '\n', true) |   return split(value, '\n', true) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
| local function ok_or_nil(status, ...) | local function ok_or_nil(status, ...) | ||||||
|   if not status then return end |   if not status then return end | ||||||
|   return ... |   return ... | ||||||
| end | end | ||||||
|  | --@private | ||||||
| local function npcall(fn, ...) | local function npcall(fn, ...) | ||||||
|   return ok_or_nil(pcall(fn, ...)) |   return ok_or_nil(pcall(fn, ...)) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Replaces text in a range with new text. | ||||||
|  | --- | ||||||
|  | --- CAUTION: Changes in-place! | ||||||
|  | --- | ||||||
|  | --@param lines (table) Original list of strings | ||||||
|  | --@param A (table) Start position; a 2-tuple of {line, col} numbers | ||||||
|  | --@param B (table) End position; a 2-tuple of {line, col} numbers | ||||||
|  | --@param new_lines A list of strings to replace the original | ||||||
|  | --@returns (table) The modified {lines} object | ||||||
| function M.set_lines(lines, A, B, new_lines) | function M.set_lines(lines, A, B, new_lines) | ||||||
|   -- 0-indexing to 1-indexing |   -- 0-indexing to 1-indexing | ||||||
|   local i_0 = A[1] + 1 |   local i_0 = A[1] + 1 | ||||||
| @@ -78,6 +91,7 @@ function M.set_lines(lines, A, B, new_lines) | |||||||
|   return lines |   return lines | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
| local function sort_by_key(fn) | local function sort_by_key(fn) | ||||||
|   return function(a,b) |   return function(a,b) | ||||||
|     local ka, kb = fn(a), fn(b) |     local ka, kb = fn(a), fn(b) | ||||||
| @@ -91,13 +105,15 @@ local function sort_by_key(fn) | |||||||
|     return false |     return false | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | --@private | ||||||
| local edit_sort_key = sort_by_key(function(e) | local edit_sort_key = sort_by_key(function(e) | ||||||
|   return {e.A[1], e.A[2], e.i} |   return {e.A[1], e.A[2], e.i} | ||||||
| end) | end) | ||||||
|  |  | ||||||
|  | --@private | ||||||
| --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position | --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position | ||||||
| -- Returns a zero-indexed column, since set_lines() does the conversion to | --- Returns a zero-indexed column, since set_lines() does the conversion to | ||||||
| -- 1-indexed | --- 1-indexed | ||||||
| local function get_line_byte_from_position(bufnr, position) | local function get_line_byte_from_position(bufnr, position) | ||||||
|   -- LSP's line and characters are 0-indexed |   -- LSP's line and characters are 0-indexed | ||||||
|   -- Vim's line and columns are 1-indexed |   -- Vim's line and columns are 1-indexed | ||||||
| @@ -114,6 +130,9 @@ local function get_line_byte_from_position(bufnr, position) | |||||||
|   return col |   return col | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Applies a list of text edits to a buffer. | ||||||
|  | --@param text_edits (table) list of `TextEdit` objects | ||||||
|  | --@param buf_nr (number) Buffer id | ||||||
| function M.apply_text_edits(text_edits, bufnr) | function M.apply_text_edits(text_edits, bufnr) | ||||||
|   if not next(text_edits) then return end |   if not next(text_edits) then return end | ||||||
|   if not api.nvim_buf_is_loaded(bufnr) then |   if not api.nvim_buf_is_loaded(bufnr) then | ||||||
| @@ -168,20 +187,30 @@ end | |||||||
| -- function M.glob_to_regex(glob) | -- function M.glob_to_regex(glob) | ||||||
| -- end | -- end | ||||||
|  |  | ||||||
| -- textDocument/completion response returns one of CompletionItem[], CompletionList or null. | --- Can be used to extract the completion items from a | ||||||
| -- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | --- `textDocument/completion` request, which may return one of | ||||||
|  | --- `CompletionItem[]`, `CompletionList` or null. | ||||||
|  | --@param result (table) The result of a `textDocument/completion` request | ||||||
|  | --@returns (table) List of completion items | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | ||||||
| function M.extract_completion_items(result) | function M.extract_completion_items(result) | ||||||
|   if type(result) == 'table' and result.items then |   if type(result) == 'table' and result.items then | ||||||
|  |     -- result is a `CompletionList` | ||||||
|     return result.items |     return result.items | ||||||
|   elseif result ~= nil then |   elseif result ~= nil then | ||||||
|  |     -- result is `CompletionItem[]` | ||||||
|     return result |     return result | ||||||
|   else |   else | ||||||
|  |     -- result is `null` | ||||||
|     return {} |     return {} | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Apply the TextDocumentEdit response. | --- Applies a `TextDocumentEdit`, which is a list of changes to a single | ||||||
| -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification | -- document. | ||||||
|  | --- | ||||||
|  | --@param text_document_edit (table) a `TextDocumentEdit` object | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit | ||||||
| function M.apply_text_document_edit(text_document_edit) | function M.apply_text_document_edit(text_document_edit) | ||||||
|   local text_document = text_document_edit.textDocument |   local text_document = text_document_edit.textDocument | ||||||
|   local bufnr = vim.uri_to_bufnr(text_document.uri) |   local bufnr = vim.uri_to_bufnr(text_document.uri) | ||||||
| @@ -195,6 +224,13 @@ function M.apply_text_document_edit(text_document_edit) | |||||||
|   M.apply_text_edits(text_document_edit.edits, bufnr) |   M.apply_text_edits(text_document_edit.edits, bufnr) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
|  | --- Recursively parses snippets in a completion entry. | ||||||
|  | --- | ||||||
|  | --@param input (string) Snippet text to parse for snippets | ||||||
|  | --@param inner (bool) Whether this function is being called recursively | ||||||
|  | --@returns 2-tuple of strings: The first is the parsed result, the second is the | ||||||
|  | ---unparsed rest of the input | ||||||
| local function parse_snippet_rec(input, inner) | local function parse_snippet_rec(input, inner) | ||||||
|   local res = "" |   local res = "" | ||||||
|  |  | ||||||
| @@ -248,15 +284,20 @@ local function parse_snippet_rec(input, inner) | |||||||
|   return res, input |   return res, input | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Parse completion entries, consuming snippet tokens | --- Parses snippets in a completion entry. | ||||||
|  | --- | ||||||
|  | --@param input (string) unparsed snippet | ||||||
|  | --@returns (string) parsed snippet | ||||||
| function M.parse_snippet(input) | function M.parse_snippet(input) | ||||||
|   local res, _ = parse_snippet_rec(input, false) |   local res, _ = parse_snippet_rec(input, false) | ||||||
|  |  | ||||||
|   return res |   return res | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Sort by CompletionItem.sortText | --@private | ||||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | --- Sorts by CompletionItem.sortText. | ||||||
|  | --- | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||||
| local function sort_completion_items(items) | local function sort_completion_items(items) | ||||||
|   if items[1] and items[1].sortText then |   if items[1] and items[1].sortText then | ||||||
|     table.sort(items, function(a, b) return a.sortText < b.sortText |     table.sort(items, function(a, b) return a.sortText < b.sortText | ||||||
| @@ -264,9 +305,10 @@ local function sort_completion_items(items) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Returns text that should be inserted when selecting completion item. The precedence is as follows: | --@private | ||||||
| -- textEdit.newText > insertText > label | --- Returns text that should be inserted when selecting completion item. The | ||||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | --- precedence is as follows: textEdit.newText > insertText > label | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||||
| local function get_completion_word(item) | local function get_completion_word(item) | ||||||
|   if item.textEdit ~= nil and item.textEdit.newText ~= nil then |   if item.textEdit ~= nil and item.textEdit.newText ~= nil then | ||||||
|     if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then |     if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then | ||||||
| @@ -284,8 +326,10 @@ local function get_completion_word(item) | |||||||
|   return item.label |   return item.label | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Some language servers return complementary candidates whose prefixes do not match are also returned. | --@private | ||||||
| -- So we exclude completion candidates whose prefix does not match. | --- Some language servers return complementary candidates whose prefixes do not | ||||||
|  | --- match are also returned. So we exclude completion candidates whose prefix | ||||||
|  | --- does not match. | ||||||
| local function remove_unmatch_completion_items(items, prefix) | local function remove_unmatch_completion_items(items, prefix) | ||||||
|   return vim.tbl_filter(function(item) |   return vim.tbl_filter(function(item) | ||||||
|     local word = get_completion_word(item) |     local word = get_completion_word(item) | ||||||
| @@ -293,16 +337,26 @@ local function remove_unmatch_completion_items(items, prefix) | |||||||
|   end, items) |   end, items) | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Acording to LSP spec, if the client set "completionItemKind.valueSet", | --- Acording to LSP spec, if the client set `completionItemKind.valueSet`, | ||||||
| -- the client must handle it properly even if it receives a value outside the specification. | --- the client must handle it properly even if it receives a value outside the | ||||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | --- specification. | ||||||
|  | --- | ||||||
|  | --@param completion_item_kind (`vim.lsp.protocol.completionItemKind`) | ||||||
|  | --@returns (`vim.lsp.protocol.completionItemKind`) | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||||
| function M._get_completion_item_kind_name(completion_item_kind) | function M._get_completion_item_kind_name(completion_item_kind) | ||||||
|   return protocol.CompletionItemKind[completion_item_kind] or "Unknown" |   return protocol.CompletionItemKind[completion_item_kind] or "Unknown" | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Getting vim complete-items with incomplete flag. | --- Turns the result of a `textDocument/completion` request into vim-compatible | ||||||
| -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) | --- |complete-items|. | ||||||
| -- @return { matches = complete-items table, incomplete = boolean  } | --- | ||||||
|  | --@param result The result of a `textDocument/completion` call, e.g. from | ||||||
|  | ---|vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, | ||||||
|  | --- `CompletionList` or `null` | ||||||
|  | --@param prefix (string) the prefix to filter the completion items | ||||||
|  | --@returns { matches = complete-items table, incomplete = bool } | ||||||
|  | --@see |complete-items| | ||||||
| function M.text_document_completion_list_to_complete_items(result, prefix) | function M.text_document_completion_list_to_complete_items(result, prefix) | ||||||
|   local items = M.extract_completion_items(result) |   local items = M.extract_completion_items(result) | ||||||
|   if vim.tbl_isempty(items) then |   if vim.tbl_isempty(items) then | ||||||
| @@ -350,7 +404,10 @@ function M.text_document_completion_list_to_complete_items(result, prefix) | |||||||
|   return matches |   return matches | ||||||
| end | end | ||||||
|  |  | ||||||
| -- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification | --- Applies a `WorkspaceEdit`. | ||||||
|  | --- | ||||||
|  | --@param workspace_edit (table) `WorkspaceEdit` | ||||||
|  | -- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit | ||||||
| function M.apply_workspace_edit(workspace_edit) | function M.apply_workspace_edit(workspace_edit) | ||||||
|   if workspace_edit.documentChanges then |   if workspace_edit.documentChanges then | ||||||
|     for _, change in ipairs(workspace_edit.documentChanges) do |     for _, change in ipairs(workspace_edit.documentChanges) do | ||||||
| @@ -375,9 +432,15 @@ function M.apply_workspace_edit(workspace_edit) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Convert any of MarkedString | MarkedString[] | MarkupContent into markdown text lines | --- Converts any of `MarkedString` | `MarkedString[]` | `MarkupContent` into | ||||||
| -- see https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_hover | --- a list of lines containing valid markdown. Useful to populate the hover | ||||||
| -- Useful for textDocument/hover, textDocument/signatureHelp, and potentially others. | --- window for `textDocument/hover`, for parsing the result of | ||||||
|  | --- `textDocument/signatureHelp`, and potentially others. | ||||||
|  | --- | ||||||
|  | --@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) | ||||||
|  | --@param contents (table, optional, default `{}`) List of strings to extend with converted lines | ||||||
|  | --@returns {contents}, extended with lines of converted markdown. | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover | ||||||
| function M.convert_input_to_markdown_lines(input, contents) | function M.convert_input_to_markdown_lines(input, contents) | ||||||
|   contents = contents or {} |   contents = contents or {} | ||||||
|   -- MarkedString variation 1 |   -- MarkedString variation 1 | ||||||
| @@ -416,8 +479,11 @@ function M.convert_input_to_markdown_lines(input, contents) | |||||||
|   return contents |   return contents | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Convert SignatureHelp response to markdown lines. | --- Converts `textDocument/SignatureHelp` response to markdown lines. | ||||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp | --- | ||||||
|  | --@param signature_help Response of `textDocument/SignatureHelp` | ||||||
|  | --@returns list of lines of converted markdown. | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp | ||||||
| function M.convert_signature_help_to_markdown_lines(signature_help) | function M.convert_signature_help_to_markdown_lines(signature_help) | ||||||
|   if not signature_help.signatures then |   if not signature_help.signatures then | ||||||
|     return |     return | ||||||
| @@ -475,6 +541,13 @@ function M.convert_signature_help_to_markdown_lines(signature_help) | |||||||
|   return contents |   return contents | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Creates a table with sensible default options for a floating window. The | ||||||
|  | --- table can be passed to |nvim_open_win()|. | ||||||
|  | --- | ||||||
|  | --@param width (number) window width (in character cells) | ||||||
|  | --@param height (number) window height (in character cells) | ||||||
|  | --@param opts (table, optional) | ||||||
|  | --@returns (table) Options | ||||||
| function M.make_floating_popup_options(width, height, opts) | function M.make_floating_popup_options(width, height, opts) | ||||||
|   validate { |   validate { | ||||||
|     opts = { opts, 't', true }; |     opts = { opts, 't', true }; | ||||||
| @@ -520,6 +593,10 @@ function M.make_floating_popup_options(width, height, opts) | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Jumps to a location. | ||||||
|  | --- | ||||||
|  | --@param location (`Location`|`LocationLink`) | ||||||
|  | --@returns `true` if the jump succeeded | ||||||
| function M.jump_to_location(location) | function M.jump_to_location(location) | ||||||
|   -- location may be Location or LocationLink |   -- location may be Location or LocationLink | ||||||
|   local uri = location.uri or location.targetUri |   local uri = location.uri or location.targetUri | ||||||
| @@ -543,14 +620,14 @@ function M.jump_to_location(location) | |||||||
|   return true |   return true | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Preview a location in a floating windows | --- Previews a location in a floating window | ||||||
| --- | --- | ||||||
| --- behavior depends on type of location: | --- behavior depends on type of location: | ||||||
| ---   - for Location, range is shown (e.g., function definition) | ---   - for Location, range is shown (e.g., function definition) | ||||||
| ---   - for LocationLink, targetRange is shown (e.g., body of function definition) | ---   - for LocationLink, targetRange is shown (e.g., body of function definition) | ||||||
| --- | --- | ||||||
| --@param location a single Location or LocationLink | --@param location a single `Location` or `LocationLink` | ||||||
| --@return bufnr,winnr buffer and window number of floating window or nil | --@returns (bufnr,winnr) buffer and window number of floating window or nil | ||||||
| function M.preview_location(location) | function M.preview_location(location) | ||||||
|   -- location may be LocationLink or Location (more useful for the former) |   -- location may be LocationLink or Location (more useful for the former) | ||||||
|   local uri = location.targetUri or location.uri |   local uri = location.targetUri or location.uri | ||||||
| @@ -565,6 +642,7 @@ function M.preview_location(location) | |||||||
|   return M.open_floating_preview(contents, filetype) |   return M.open_floating_preview(contents, filetype) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --@private | ||||||
| local function find_window_by_var(name, value) | local function find_window_by_var(name, value) | ||||||
|   for _, win in ipairs(api.nvim_list_wins()) do |   for _, win in ipairs(api.nvim_list_wins()) do | ||||||
|     if npcall(api.nvim_win_get_var, win, name) == value then |     if npcall(api.nvim_win_get_var, win, name) == value then | ||||||
| @@ -573,12 +651,18 @@ local function find_window_by_var(name, value) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Check if a window with `unique_name` tagged is associated with the current | --- Enters/leaves the focusable window associated with the current buffer via the | ||||||
| -- buffer. If not, make a new preview. | --window - variable `unique_name`. If no such window exists, run the function | ||||||
| -- | --{fn}. | ||||||
| -- fn()'s return bufnr, winnr | --- | ||||||
| -- case that a new floating window should be created. | --@param unique_name (string) Window variable | ||||||
|  | --@param fn (function) should return create a new window and return a tuple of | ||||||
|  | ---({focusable_buffer_id}, {window_id}). if {focusable_buffer_id} is a valid | ||||||
|  | ---buffer id, the newly created window will be the new focus associated with | ||||||
|  | ---the current buffer via the tag `unique_name`. | ||||||
|  | --@returns (pbufnr, pwinnr) if `fn()` has created a new window; nil otherwise | ||||||
| function M.focusable_float(unique_name, fn) | function M.focusable_float(unique_name, fn) | ||||||
|  |   -- Go back to previous window if we are in a focusable one | ||||||
|   if npcall(api.nvim_win_get_var, 0, unique_name) then |   if npcall(api.nvim_win_get_var, 0, unique_name) then | ||||||
|     return api.nvim_command("wincmd p") |     return api.nvim_command("wincmd p") | ||||||
|   end |   end | ||||||
| @@ -598,18 +682,21 @@ function M.focusable_float(unique_name, fn) | |||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Check if a window with `unique_name` tagged is associated with the current | --- Focuses/unfocuses the floating preview window associated with the current | ||||||
| -- buffer. If not, make a new preview. | --- buffer via the window variable `unique_name`. If no such preview window | ||||||
| -- | --- exists, makes a new one. | ||||||
| -- fn()'s return values will be passed directly to open_floating_preview in the | --- | ||||||
| -- case that a new floating window should be created. | --@param unique_name (string) Window variable | ||||||
|  | --@param fn (function) The return values of this function will be passed | ||||||
|  | ---directly to |vim.lsp.util.open_floating_preview()|, in the case that a new | ||||||
|  | ---floating window should be created | ||||||
| function M.focusable_preview(unique_name, fn) | function M.focusable_preview(unique_name, fn) | ||||||
|   return M.focusable_float(unique_name, function() |   return M.focusable_float(unique_name, function() | ||||||
|     return M.open_floating_preview(fn()) |     return M.open_floating_preview(fn()) | ||||||
|   end) |   end) | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Trim empty lines from input and pad left and right with spaces | --- Trims empty lines from input and pad left and right with spaces | ||||||
| --- | --- | ||||||
| --@param contents table of lines to trim and pad | --@param contents table of lines to trim and pad | ||||||
| --@param opts dictionary with optional fields | --@param opts dictionary with optional fields | ||||||
| @@ -617,7 +704,7 @@ end | |||||||
| --             - pad_right  number of columns to pad contents at right (default 1) | --             - pad_right  number of columns to pad contents at right (default 1) | ||||||
| --             - pad_top    number of lines to pad contents at top (default 0) | --             - pad_top    number of lines to pad contents at top (default 0) | ||||||
| --             - pad_bottom number of lines to pad contents at bottom (default 0) | --             - pad_bottom number of lines to pad contents at bottom (default 0) | ||||||
| --@return contents table of trimmed and padded lines | --@returns contents table of trimmed and padded lines | ||||||
| function M._trim_and_pad(contents, opts) | function M._trim_and_pad(contents, opts) | ||||||
|   validate { |   validate { | ||||||
|     contents = { contents, 't' }; |     contents = { contents, 't' }; | ||||||
| @@ -645,12 +732,13 @@ end | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| --- Convert markdown into syntax highlighted regions by stripping the code | -- TODO: refactor to separate stripping/converting and make use of open_floating_preview | ||||||
|  | -- | ||||||
|  | --- Converts markdown into syntax highlighted regions by stripping the code | ||||||
| --- blocks and converting them into highlighted code. | --- blocks and converting them into highlighted code. | ||||||
| --- This will by default insert a blank line separator after those code block | --- This will by default insert a blank line separator after those code block | ||||||
| --- regions to improve readability. | --- regions to improve readability. | ||||||
| --- The result is shown in a floating preview | --- The result is shown in a floating preview. | ||||||
| --- TODO: refactor to separate stripping/converting and make use of open_floating_preview |  | ||||||
| --- | --- | ||||||
| --@param contents table of lines to show in window | --@param contents table of lines to show in window | ||||||
| --@param opts dictionary with optional fields | --@param opts dictionary with optional fields | ||||||
| @@ -664,7 +752,7 @@ end | |||||||
| --             - pad_top    number of lines to pad contents at top | --             - pad_top    number of lines to pad contents at top | ||||||
| --             - pad_bottom number of lines to pad contents at bottom | --             - pad_bottom number of lines to pad contents at bottom | ||||||
| --             - separator insert separator after code block | --             - separator insert separator after code block | ||||||
| --@return width,height size of float | --@returns width,height size of float | ||||||
| function M.fancy_floating_markdown(contents, opts) | function M.fancy_floating_markdown(contents, opts) | ||||||
|   validate { |   validate { | ||||||
|     contents = { contents, 't' }; |     contents = { contents, 't' }; | ||||||
| @@ -738,6 +826,7 @@ function M.fancy_floating_markdown(contents, opts) | |||||||
|  |  | ||||||
|   vim.cmd("ownsyntax markdown") |   vim.cmd("ownsyntax markdown") | ||||||
|   local idx = 1 |   local idx = 1 | ||||||
|  |   --@private | ||||||
|   local function apply_syntax_to_region(ft, start, finish) |   local function apply_syntax_to_region(ft, start, finish) | ||||||
|     if ft == '' then return end |     if ft == '' then return end | ||||||
|     local name = ft..idx |     local name = ft..idx | ||||||
| @@ -763,11 +852,17 @@ function M.fancy_floating_markdown(contents, opts) | |||||||
|   return bufnr, winnr |   return bufnr, winnr | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Creates autocommands to close a preview window when events happen. | ||||||
|  | --- | ||||||
|  | --@param events (table) list of events | ||||||
|  | --@param winnr (number) window id of preview window | ||||||
|  | --@see |autocmd-events| | ||||||
| function M.close_preview_autocmd(events, winnr) | function M.close_preview_autocmd(events, winnr) | ||||||
|   api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") |   api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Compute size of float needed to show contents (with optional wrapping) | --@internal | ||||||
|  | --- Computes size of float needed to show contents (with optional wrapping) | ||||||
| --- | --- | ||||||
| --@param contents table of lines to show in window | --@param contents table of lines to show in window | ||||||
| --@param opts dictionary with optional fields | --@param opts dictionary with optional fields | ||||||
| @@ -776,7 +871,7 @@ end | |||||||
| --             - wrap_at character to wrap at for computing height | --             - wrap_at character to wrap at for computing height | ||||||
| --             - max_width  maximal width of floating window | --             - max_width  maximal width of floating window | ||||||
| --             - max_height maximal height of floating window | --             - max_height maximal height of floating window | ||||||
| --@return width,height size of float | --@returns width,height size of float | ||||||
| function M._make_floating_popup_size(contents, opts) | function M._make_floating_popup_size(contents, opts) | ||||||
|   validate { |   validate { | ||||||
|     contents = { contents, 't' }; |     contents = { contents, 't' }; | ||||||
| @@ -827,7 +922,7 @@ function M._make_floating_popup_size(contents, opts) | |||||||
|   return width, height |   return width, height | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Show contents in a floating window | --- Shows contents in a floating window. | ||||||
| --- | --- | ||||||
| --@param contents table of lines to show in window | --@param contents table of lines to show in window | ||||||
| --@param filetype string of filetype to set for opened buffer | --@param filetype string of filetype to set for opened buffer | ||||||
| @@ -841,7 +936,8 @@ end | |||||||
| --             - pad_right  number of columns to pad contents at right | --             - pad_right  number of columns to pad contents at right | ||||||
| --             - pad_top    number of lines to pad contents at top | --             - pad_top    number of lines to pad contents at top | ||||||
| --             - pad_bottom number of lines to pad contents at bottom | --             - pad_bottom number of lines to pad contents at bottom | ||||||
| --@return bufnr,winnr buffer and window number of floating window or nil | --@returns bufnr,winnr buffer and window number of the newly created floating | ||||||
|  | ---preview window | ||||||
| function M.open_floating_preview(contents, filetype, opts) | function M.open_floating_preview(contents, filetype, opts) | ||||||
|   validate { |   validate { | ||||||
|     contents = { contents, 't' }; |     contents = { contents, 't' }; | ||||||
| @@ -912,6 +1008,9 @@ do | |||||||
|     severity_floating_highlights[severity] = floating_highlight_name |     severity_floating_highlights[severity] = floating_highlight_name | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Clears diagnostics for a buffer. | ||||||
|  |   --- | ||||||
|  |   --@param bufnr (number) buffer id | ||||||
|   function M.buf_clear_diagnostics(bufnr) |   function M.buf_clear_diagnostics(bufnr) | ||||||
|     validate { bufnr = {bufnr, 'n', true} } |     validate { bufnr = {bufnr, 'n', true} } | ||||||
|     bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr |     bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr | ||||||
| @@ -923,10 +1022,18 @@ do | |||||||
|     api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) |     api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Gets the name of a severity's highlight group. | ||||||
|  |   --- | ||||||
|  |   --@param severity A member of `vim.lsp.protocol.DiagnosticSeverity` | ||||||
|  |   --@returns (string) Highlight group name | ||||||
|   function M.get_severity_highlight_name(severity) |   function M.get_severity_highlight_name(severity) | ||||||
|     return severity_highlights[severity] |     return severity_highlights[severity] | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Gets list of diagnostics for the current line. | ||||||
|  |   --- | ||||||
|  |   --@returns (table) list of `Diagnostic` tables | ||||||
|  |   --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic | ||||||
|   function M.get_line_diagnostics() |   function M.get_line_diagnostics() | ||||||
|     local bufnr = api.nvim_get_current_buf() |     local bufnr = api.nvim_get_current_buf() | ||||||
|     local linenr = api.nvim_win_get_cursor(0)[1] - 1 |     local linenr = api.nvim_win_get_cursor(0)[1] - 1 | ||||||
| @@ -941,6 +1048,8 @@ do | |||||||
|     return diagnostics_by_line[linenr] or {} |     return diagnostics_by_line[linenr] or {} | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Displays the diagnostics for the current line in a floating hover | ||||||
|  |   --- window. | ||||||
|   function M.show_line_diagnostics() |   function M.show_line_diagnostics() | ||||||
|     -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) |     -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) | ||||||
|     -- if #marks == 0 then |     -- if #marks == 0 then | ||||||
| @@ -977,10 +1086,10 @@ do | |||||||
|     return popup_bufnr, winnr |     return popup_bufnr, winnr | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   --- Saves the diagnostics (Diagnostic[]) into diagnostics_by_buf |   --- Saves diagnostics into vim.lsp.util.diagnostics_by_buf[{bufnr}]. | ||||||
|   --- |   --- | ||||||
|   --@param bufnr bufnr for which the diagnostics are for. |   --@param bufnr (number) buffer id for which the diagnostics are for | ||||||
|   --@param diagnostics Diagnostics[] received from the language server. |   --@param diagnostics list of `Diagnostic`s received from the LSP server | ||||||
|   function M.buf_diagnostics_save_positions(bufnr, diagnostics) |   function M.buf_diagnostics_save_positions(bufnr, diagnostics) | ||||||
|     validate { |     validate { | ||||||
|       bufnr = {bufnr, 'n', true}; |       bufnr = {bufnr, 'n', true}; | ||||||
| @@ -1000,6 +1109,10 @@ do | |||||||
|     M.diagnostics_by_buf[bufnr] = diagnostics |     M.diagnostics_by_buf[bufnr] = diagnostics | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Highlights a list of diagnostics in a buffer by underlining them. | ||||||
|  |   --- | ||||||
|  |   --@param bufnr (number) buffer id | ||||||
|  |   --@param diagnostics (list of `Diagnostic`s) | ||||||
|   function M.buf_diagnostics_underline(bufnr, diagnostics) |   function M.buf_diagnostics_underline(bufnr, diagnostics) | ||||||
|     for _, diagnostic in ipairs(diagnostics) do |     for _, diagnostic in ipairs(diagnostics) do | ||||||
|       local start = diagnostic.range["start"] |       local start = diagnostic.range["start"] | ||||||
| @@ -1020,11 +1133,18 @@ do | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Removes document highlights from a buffer. | ||||||
|  |   --- | ||||||
|  |   --@param bufnr buffer id | ||||||
|   function M.buf_clear_references(bufnr) |   function M.buf_clear_references(bufnr) | ||||||
|     validate { bufnr = {bufnr, 'n', true} } |     validate { bufnr = {bufnr, 'n', true} } | ||||||
|     api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) |     api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Shows a list of document highlights for a certain buffer. | ||||||
|  |   --- | ||||||
|  |   --@param bufnr buffer id | ||||||
|  |   --@param references List of `DocumentHighlight` objects to highlight | ||||||
|   function M.buf_highlight_references(bufnr, references) |   function M.buf_highlight_references(bufnr, references) | ||||||
|     validate { bufnr = {bufnr, 'n', true} } |     validate { bufnr = {bufnr, 'n', true} } | ||||||
|     for _, reference in ipairs(references) do |     for _, reference in ipairs(references) do | ||||||
| @@ -1040,11 +1160,19 @@ do | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Groups a list of diagnostics by line. | ||||||
|  |   --- | ||||||
|  |   --@param diagnostics (table) list of `Diagnostic`s | ||||||
|  |   --@returns (table) dictionary mapping lines to lists of diagnostics valid on | ||||||
|  |   ---those lines | ||||||
|  |   --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#diagnostic | ||||||
|   function M.diagnostics_group_by_line(diagnostics) |   function M.diagnostics_group_by_line(diagnostics) | ||||||
|     if not diagnostics then return end |     if not diagnostics then return end | ||||||
|     local diagnostics_by_line = {} |     local diagnostics_by_line = {} | ||||||
|     for _, diagnostic in ipairs(diagnostics) do |     for _, diagnostic in ipairs(diagnostics) do | ||||||
|       local start = diagnostic.range.start |       local start = diagnostic.range.start | ||||||
|  |       -- TODO: Are diagnostics only valid for a single line? I don't understand | ||||||
|  |       -- why this would be okay otherwise | ||||||
|       local line_diagnostics = diagnostics_by_line[start.line] |       local line_diagnostics = diagnostics_by_line[start.line] | ||||||
|       if not line_diagnostics then |       if not line_diagnostics then | ||||||
|         line_diagnostics = {} |         line_diagnostics = {} | ||||||
| @@ -1055,6 +1183,11 @@ do | |||||||
|     return diagnostics_by_line |     return diagnostics_by_line | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   --- Given a list of diagnostics, sets the corresponding virtual text for a | ||||||
|  |   --- buffer. | ||||||
|  |   --- | ||||||
|  |   --@param bufnr buffer id | ||||||
|  |   --@param diagnostics (table) list of `Diagnostic`s | ||||||
|   function M.buf_diagnostics_virtual_text(bufnr, diagnostics) |   function M.buf_diagnostics_virtual_text(bufnr, diagnostics) | ||||||
|     if not diagnostics then |     if not diagnostics then | ||||||
|       return |       return | ||||||
| @@ -1093,8 +1226,7 @@ do | |||||||
|   --- </pre> |   --- </pre> | ||||||
|   --- |   --- | ||||||
|   --@param kind Diagnostic severity kind: See |vim.lsp.protocol.DiagnosticSeverity| |   --@param kind Diagnostic severity kind: See |vim.lsp.protocol.DiagnosticSeverity| | ||||||
|   --- |   --@returns Count of diagnostics | ||||||
|   --@return Count of diagnostics |  | ||||||
|   function M.buf_diagnostics_count(kind) |   function M.buf_diagnostics_count(kind) | ||||||
|     local bufnr = vim.api.nvim_get_current_buf() |     local bufnr = vim.api.nvim_get_current_buf() | ||||||
|     local diagnostics = M.diagnostics_by_buf[bufnr] |     local diagnostics = M.diagnostics_by_buf[bufnr] | ||||||
| @@ -1115,7 +1247,7 @@ do | |||||||
|     [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; |     [protocol.DiagnosticSeverity.Hint] = "LspDiagnosticsHintSign"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   --- Place signs for each diagnostic in the sign column. |   --- Places signs for each diagnostic in the sign column. | ||||||
|   --- |   --- | ||||||
|   --- Sign characters can be customized with the following commands: |   --- Sign characters can be customized with the following commands: | ||||||
|   --- |   --- | ||||||
| @@ -1136,8 +1268,11 @@ local position_sort = sort_by_key(function(v) | |||||||
|   return {v.start.line, v.start.character} |   return {v.start.line, v.start.character} | ||||||
| end) | end) | ||||||
|  |  | ||||||
| -- Returns the items with the byte position calculated correctly and in sorted | --- Returns the items with the byte position calculated correctly and in sorted | ||||||
| -- order. | --- order, for display in quickfix and location lists. | ||||||
|  | --- | ||||||
|  | --@param locations (table) list of `Location`s or `LocationLink`s | ||||||
|  | --@returns (table) list of items | ||||||
| function M.locations_to_items(locations) | function M.locations_to_items(locations) | ||||||
|   local items = {} |   local items = {} | ||||||
|   local grouped = setmetatable({}, { |   local grouped = setmetatable({}, { | ||||||
| @@ -1180,6 +1315,10 @@ function M.locations_to_items(locations) | |||||||
|   return items |   return items | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Fills current window's location list with given list of items. | ||||||
|  | --- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. | ||||||
|  | --- | ||||||
|  | --@param items (table) list of items | ||||||
| function M.set_loclist(items) | function M.set_loclist(items) | ||||||
|   vim.fn.setloclist(0, {}, ' ', { |   vim.fn.setloclist(0, {}, ' ', { | ||||||
|     title = 'Language Server'; |     title = 'Language Server'; | ||||||
| @@ -1187,6 +1326,10 @@ function M.set_loclist(items) | |||||||
|   }) |   }) | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Fills quickfix list with given list of items. | ||||||
|  | --- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. | ||||||
|  | --- | ||||||
|  | --@param items (table) list of items | ||||||
| function M.set_qflist(items) | function M.set_qflist(items) | ||||||
|   vim.fn.setqflist({}, ' ', { |   vim.fn.setqflist({}, ' ', { | ||||||
|     title = 'Language Server'; |     title = 'Language Server'; | ||||||
| @@ -1201,10 +1344,11 @@ function M._get_symbol_kind_name(symbol_kind) | |||||||
|   return protocol.SymbolKind[symbol_kind] or "Unknown" |   return protocol.SymbolKind[symbol_kind] or "Unknown" | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Convert symbols to quickfix list items | --- Converts symbols to quickfix list items. | ||||||
| --- | --- | ||||||
| --@param symbols DocumentSymbol[] or SymbolInformation[] | --@param symbols DocumentSymbol[] or SymbolInformation[] | ||||||
| function M.symbols_to_items(symbols, bufnr) | function M.symbols_to_items(symbols, bufnr) | ||||||
|  |   --@private | ||||||
|   local function _symbols_to_items(_symbols, _items, _bufnr) |   local function _symbols_to_items(_symbols, _items, _bufnr) | ||||||
|     for _, symbol in ipairs(_symbols) do |     for _, symbol in ipairs(_symbols) do | ||||||
|       if symbol.location then -- SymbolInformation type |       if symbol.location then -- SymbolInformation type | ||||||
| @@ -1239,7 +1383,9 @@ function M.symbols_to_items(symbols, bufnr) | |||||||
|   return _symbols_to_items(symbols, {}, bufnr) |   return _symbols_to_items(symbols, {}, bufnr) | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Remove empty lines from the beginning and end. | --- Removes empty lines from the beginning and end. | ||||||
|  | --@param lines (table) list of lines to trim | ||||||
|  | --@returns (table) trimmed list of lines | ||||||
| function M.trim_empty_lines(lines) | function M.trim_empty_lines(lines) | ||||||
|   local start = 1 |   local start = 1 | ||||||
|   for i = 1, #lines do |   for i = 1, #lines do | ||||||
| @@ -1258,11 +1404,13 @@ function M.trim_empty_lines(lines) | |||||||
|   return vim.list_extend({}, lines, start, finish) |   return vim.list_extend({}, lines, start, finish) | ||||||
| end | end | ||||||
|  |  | ||||||
| -- Accepts markdown lines and tries to reduce it to a filetype if it is | --- Accepts markdown lines and tries to reduce them to a filetype if they | ||||||
| -- just a single code block. | --- comprise just a single code block. | ||||||
| -- Note: This modifies the input. | --- | ||||||
| -- | --- CAUTION: Modifies the input in-place! | ||||||
| -- Returns: filetype or 'markdown' if it was unchanged. | --- | ||||||
|  | --@param lines (table) list of lines | ||||||
|  | --@returns (string) filetype or 'markdown' if it was unchanged. | ||||||
| function M.try_trim_markdown_code_blocks(lines) | function M.try_trim_markdown_code_blocks(lines) | ||||||
|   local language_id = lines[1]:match("^```(.*)") |   local language_id = lines[1]:match("^```(.*)") | ||||||
|   if language_id then |   if language_id then | ||||||
| @@ -1285,6 +1433,7 @@ function M.try_trim_markdown_code_blocks(lines) | |||||||
| end | end | ||||||
|  |  | ||||||
| local str_utfindex = vim.str_utfindex | local str_utfindex = vim.str_utfindex | ||||||
|  | --@private | ||||||
| local function make_position_param() | local function make_position_param() | ||||||
|   local row, col = unpack(api.nvim_win_get_cursor(0)) |   local row, col = unpack(api.nvim_win_get_cursor(0)) | ||||||
|   row = row - 1 |   row = row - 1 | ||||||
| @@ -1293,6 +1442,10 @@ local function make_position_param() | |||||||
|   return { line = row; character = col; } |   return { line = row; character = col; } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. | ||||||
|  | --- | ||||||
|  | --@returns `TextDocumentPositionParams` object | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams | ||||||
| function M.make_position_params() | function M.make_position_params() | ||||||
|   return { |   return { | ||||||
|     textDocument = M.make_text_document_params(); |     textDocument = M.make_text_document_params(); | ||||||
| @@ -1300,6 +1453,13 @@ function M.make_position_params() | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Using the current position in the current buffer, creates an object that | ||||||
|  | --- can be used as a building block for several LSP requests, such as | ||||||
|  | --- `textDocument/codeAction`, `textDocument/colorPresentation`, | ||||||
|  | --- `textDocument/rangeFormatting`. | ||||||
|  | --- | ||||||
|  | --@returns { textDocument = { uri = `current_file_uri` }, range = { start = | ||||||
|  | ---`current_position`, end = `current_position` } } | ||||||
| function M.make_range_params() | function M.make_range_params() | ||||||
|   local position = make_position_param() |   local position = make_position_param() | ||||||
|   return { |   return { | ||||||
| @@ -1308,11 +1468,15 @@ function M.make_range_params() | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Creates a `TextDocumentIdentifier` object for the current buffer. | ||||||
|  | --- | ||||||
|  | --@returns `TextDocumentIdentifier` | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier | ||||||
| function M.make_text_document_params() | function M.make_text_document_params() | ||||||
|   return { uri = vim.uri_from_bufnr(0) } |   return { uri = vim.uri_from_bufnr(0) } | ||||||
| end | end | ||||||
|  |  | ||||||
| --- Get visual width of tabstop. | --- Returns visual width of tabstop. | ||||||
| --- | --- | ||||||
| --@see |softtabstop| | --@see |softtabstop| | ||||||
| --@param bufnr (optional, number): Buffer handle, defaults to current | --@param bufnr (optional, number): Buffer handle, defaults to current | ||||||
| @@ -1324,6 +1488,11 @@ function M.get_effective_tabstop(bufnr) | |||||||
|   return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop |   return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Creates a `FormattingOptions` object for the current buffer and cursor position. | ||||||
|  | --- | ||||||
|  | --@param options Table with valid `FormattingOptions` entries | ||||||
|  | --@returns `FormattingOptions object | ||||||
|  | --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting | ||||||
| function M.make_formatting_params(options) | function M.make_formatting_params(options) | ||||||
|   validate { options = {options, 't', true} } |   validate { options = {options, 't', true} } | ||||||
|   options = vim.tbl_extend('keep', options or {}, { |   options = vim.tbl_extend('keep', options or {}, { | ||||||
| @@ -1336,9 +1505,12 @@ function M.make_formatting_params(options) | |||||||
|   } |   } | ||||||
| end | end | ||||||
|  |  | ||||||
| -- @param buf buffer handle or 0 for current. | --- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. | ||||||
| -- @param row 0-indexed line | --- | ||||||
| -- @param col 0-indexed byte offset in line | --@param buf buffer id (0 for current) | ||||||
|  | --@param row 0-indexed line | ||||||
|  | --@param col 0-indexed byte offset in line | ||||||
|  | --@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf} | ||||||
| function M.character_offset(buf, row, col) | function M.character_offset(buf, row, col) | ||||||
|   local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] |   local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] | ||||||
|   -- If the col is past the EOL, use the line length. |   -- If the col is past the EOL, use the line length. | ||||||
|   | |||||||
| @@ -960,7 +960,11 @@ def main(config): | |||||||
|  |  | ||||||
|         i = 0 |         i = 0 | ||||||
|         for filename in CONFIG[target]['section_order']: |         for filename in CONFIG[target]['section_order']: | ||||||
|  |             try: | ||||||
|                 title, helptag, section_doc = sections.pop(filename) |                 title, helptag, section_doc = sections.pop(filename) | ||||||
|  |             except KeyError: | ||||||
|  |                 print("Warning:", filename, "has empty docs, skipping") | ||||||
|  |                 continue | ||||||
|             i += 1 |             i += 1 | ||||||
|             if filename not in CONFIG[target]['append_only']: |             if filename not in CONFIG[target]['append_only']: | ||||||
|                 docs += sep |                 docs += sep | ||||||
|   | |||||||
| @@ -203,10 +203,10 @@ Integer nvim_get_hl_id_by_name(String name) | |||||||
| /// flags. This is a blocking call, unlike |nvim_input()|. | /// flags. This is a blocking call, unlike |nvim_input()|. | ||||||
| /// | /// | ||||||
| /// On execution error: does not fail, but updates v:errmsg. | /// On execution error: does not fail, but updates v:errmsg. | ||||||
| // | /// | ||||||
| //  If you need to input sequences like <C-o> use nvim_replace_termcodes | ///  If you need to input sequences like <C-o> use |nvim_replace_termcodes| | ||||||
| //  to replace the termcodes and then pass the resulting string to | ///  to replace the termcodes and then pass the resulting string to | ||||||
| //  nvim_feedkeys. You'll also want to enable escape_csi. | ///  nvim_feedkeys. You'll also want to enable escape_csi. | ||||||
| /// | /// | ||||||
| /// Example: | /// Example: | ||||||
| /// <pre> | /// <pre> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Patrice Peterson
					Patrice Peterson