mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	Merge pull request #12708 from runiq/lsp-doc
Add docs for some methods in vim.lsp
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -63,6 +63,8 @@ local.mk | ||||
| /runtime/doc/errors.log | ||||
| # Don't include the mpack files. | ||||
| /runtime/doc/*.mpack | ||||
| # Also don't include intermediary doc output | ||||
| /tmp-*-doc | ||||
|  | ||||
| # CLion | ||||
| /.idea/ | ||||
|   | ||||
| @@ -734,13 +734,14 @@ nvim_feedkeys({keys}, {mode}, {escape_csi})                  *nvim_feedkeys()* | ||||
|  | ||||
|                 On execution error: does not fail, but updates v:errmsg. | ||||
|  | ||||
|                 If you need to input sequences like <C-o> use nvim_replace_termcodes | ||||
|                 to replace the termcodes and then pass the resulting string to | ||||
|                 nvim_feedkeys. You'll also want to enable escape_csi. | ||||
|                 If you need to input sequences like <C-o> use | ||||
|                 |nvim_replace_termcodes| to replace the termcodes and then | ||||
|                 pass the resulting string to nvim_feedkeys. You'll also want | ||||
|                 to enable escape_csi. | ||||
|  | ||||
|                 Example: > | ||||
| 		    :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) | ||||
| 		    :call nvim_feedkeys(key, 'n', v:true) | ||||
|                     :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) | ||||
|                     :call nvim_feedkeys(key, 'n', v:true) | ||||
| < | ||||
|  | ||||
|                 Parameters: ~ | ||||
|   | ||||
										
											
												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. | ||||
|  | ||||
| --@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(...) | ||||
|   nvim_err_writeln(table.concat(vim.tbl_flatten{...})) | ||||
|   nvim_command("redraw") | ||||
| 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) | ||||
|   validate { bufnr = { bufnr, 'n', true } } | ||||
|   if bufnr == nil or bufnr == 0 then | ||||
| @@ -40,6 +50,11 @@ local function resolve_bufnr(bufnr) | ||||
|   return bufnr | ||||
| 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) | ||||
|   validate{filename={filename,'s'}} | ||||
|   local stat = uv.fs_stat(filename) | ||||
| @@ -55,6 +70,10 @@ local valid_encodings = { | ||||
| } | ||||
|  | ||||
| local client_index = 0 | ||||
| --@private | ||||
| --- Returns a new, unused client id. | ||||
| --- | ||||
| --@returns (number) client id | ||||
| local function next_client_id() | ||||
|   client_index = client_index + 1 | ||||
|   return client_index | ||||
| @@ -64,6 +83,12 @@ local active_clients = {} | ||||
| local all_buffer_active_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) | ||||
|   validate { | ||||
|     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; | ||||
| }) | ||||
|  | ||||
| --@private | ||||
| --- Normalizes {encoding} to valid LSP encoding names. | ||||
| --- | ||||
| --@param encoding (string) Encoding to normalize | ||||
| --@returns (string) normalized encoding name | ||||
| local function validate_encoding(encoding) | ||||
|   validate { | ||||
|     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)) | ||||
| 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) | ||||
|   vim.validate{cmd={ | ||||
|     input, | ||||
| @@ -114,12 +151,27 @@ function lsp._cmd_parts(input) | ||||
|   return cmd, cmd_args | ||||
| 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) | ||||
|   return function(v) | ||||
|     return v == nil or fn(v) | ||||
|   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) | ||||
|   validate { | ||||
|     config = { config, 't' }; | ||||
| @@ -148,6 +200,11 @@ local function validate_client_config(config) | ||||
|   } | ||||
| 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 text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') | ||||
|   if nvim_buf_get_option(bufnr, 'eol') then | ||||
| @@ -156,6 +213,11 @@ local function buf_get_full_text(bufnr) | ||||
|   return text | ||||
| 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) | ||||
|   if not client.resolved_capabilities.text_document_open_close then | ||||
|     return | ||||
| @@ -176,74 +238,90 @@ local function text_document_did_open_handler(bufnr, client) | ||||
|   util.buf_versions[bufnr] = params.textDocument.version | ||||
| end | ||||
|  | ||||
| --- LSP client object. | ||||
| -- FIXME: DOC: Shouldn't need to use a dummy function | ||||
| -- | ||||
| --- LSP client object. You can get an active client object via | ||||
| --- |vim.lsp.get_client_by_id()| or |vim.lsp.get_active_clients()|. | ||||
| --- | ||||
| --- - Methods: | ||||
| --- | ||||
| ---  - request(method, params, [callback]) | ||||
| ---     Send a request to the server. If callback is not specified, it will use | ||||
| ---     {client.callbacks} to try to find a callback. If one is not found there, | ||||
| ---     then an error will occur. | ||||
| ---  - request(method, params, [callback], bufnr) | ||||
| ---     Sends a request to the server. | ||||
| ---     This is a thin wrapper around {client.rpc.request} with some additional | ||||
| ---     checking. | ||||
| ---     Returns a boolean to indicate if the notification was successful. If it | ||||
| ---     is false, then it will always be false (the client has shutdown). | ||||
| ---     If it was successful, then it will return the request id as the second | ||||
| ---     result. You can use this with `notify("$/cancel", { id = request_id })` | ||||
| ---     to cancel the request. This helper is made automatically with | ||||
| ---     |vim.lsp.buf_request()| | ||||
| ---     Returns: status, [client_id] | ||||
| ---     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. | ||||
| ---     Returns: {status}, {[client_id]}. {status} is a boolean indicating if | ||||
| ---     the notification was successful. If it is `false`, then it will always | ||||
| ---     be `false` (the client has shutdown). | ||||
| ---     If {status} is `true`, the function returns {request_id} as the second | ||||
| ---     result. You can use this with `client.cancel_request(request_id)` | ||||
| ---     to cancel the request. | ||||
| --- | ||||
| ---  - notify(method, params) | ||||
| ---     This is just {client.rpc.notify}() | ||||
| ---     Returns a boolean to indicate if the notification was successful. If it | ||||
| ---     is false, then it will always be false (the client has shutdown). | ||||
| ---     Returns: status | ||||
| ---     Sends a notification to an LSP server. | ||||
| ---     Returns: a boolean to indicate if the notification was successful. If | ||||
| ---     it is false, then it will always be false (the client has shutdown). | ||||
| --- | ||||
| ---  - cancel_request(id) | ||||
| ---     This is just {client.rpc.notify}("$/cancelRequest", { id = id }) | ||||
| ---     Returns the same as `notify()`. | ||||
| ---     Cancels a request with a given request id. | ||||
| ---     Returns: same as `notify()`. | ||||
| --- | ||||
| ---  - 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. | ||||
| ---     If you request to stop a client which has previously been requested to | ||||
| ---     shutdown, it will automatically escalate and force shutdown. | ||||
| --- | ||||
| ---  - is_stopped() | ||||
| ---     Returns true if the client is fully stopped. | ||||
| ---     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 | ||||
| ---  - 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 | ||||
| ---    logs and messages. | ||||
| --- | ||||
| ---  - offset_encoding (string): The encoding used for communicating | ||||
| ---    with the server. You can modify this in the `on_init` method | ||||
| ---  - {rpc} (table): RPC client object, for low level interaction with the | ||||
| ---    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. | ||||
| --- | ||||
| ---  - callbacks (table): The callbacks used by the client as | ||||
| ---  - {callbacks} (table): The callbacks used by the client as | ||||
| ---    described in |lsp-callbacks|. | ||||
| --- | ||||
| ---  - config (table): copy of the table that was passed by the user | ||||
| ---  - {config} (table): copy of the table that was passed by the user | ||||
| ---    to |vim.lsp.start_client()|. | ||||
| --- | ||||
| ---  - server_capabilities (table): Response from the server sent on | ||||
| ---  - {server_capabilities} (table): Response from the server sent on | ||||
| ---    `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 | ||||
| ---    response from the server in `server_capabilities`. | ||||
| function lsp.client() | ||||
|   error() | ||||
| end | ||||
|  | ||||
| -- FIXME: DOC: 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. | ||||
| --- | ||||
| --- 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 | ||||
| --- its rootUri on initialization. | ||||
| --- | ||||
| @@ -271,8 +349,8 @@ end | ||||
| --- | ||||
| --@param callbacks Map of language server method names to | ||||
| --- `function(err, method, params, client_id)` handler. Invoked for: | ||||
| --- - Notifications from the server, where `err` will always be `nil`. | ||||
| --- - Requests initiated by the server. For these you can respond by returning | ||||
| --- - Notifications to the server, where `err` will always be `nil`. | ||||
| --- - Requests by the server. For these you can respond by returning | ||||
| ---   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 | ||||
| ---   help with this. | ||||
| @@ -297,7 +375,7 @@ end | ||||
| --@param before_init Callback with parameters (initialize_params, config) | ||||
| --- invoked before the LSP "initialize" phase, where `params` contains the | ||||
| --- parameters being sent to the server and `config` is the config that was | ||||
| --- passed to `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. | ||||
| --- | ||||
| --@param on_init Callback (client, initialize_result) invoked after LSP | ||||
| @@ -335,10 +413,23 @@ function lsp.start_client(config) | ||||
|  | ||||
|   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) | ||||
|     return callbacks[method] or default_callbacks[method] | ||||
|   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) | ||||
|     local _ = log.debug() and log.debug('notification', method, params) | ||||
|     local callback = resolve_callback(method) | ||||
| @@ -348,6 +439,12 @@ function lsp.start_client(config) | ||||
|     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) | ||||
|     local _ = log.debug() and log.debug('server_request', method, params) | ||||
|     local callback = resolve_callback(method) | ||||
| @@ -359,6 +456,13 @@ function lsp.start_client(config) | ||||
|     return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) | ||||
|   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) | ||||
|     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)) | ||||
| @@ -371,6 +475,11 @@ function lsp.start_client(config) | ||||
|     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) | ||||
|     active_clients[client_id] = nil | ||||
|     uninitialized_clients[client_id] = nil | ||||
| @@ -411,6 +520,7 @@ function lsp.start_client(config) | ||||
|   -- initialize finishes. | ||||
|   uninitialized_clients[client_id] = client; | ||||
|  | ||||
|   --@private | ||||
|   local function initialize() | ||||
|     local valid_traces = { | ||||
|       off = 'off'; messages = 'messages'; verbose = 'verbose'; | ||||
| @@ -488,6 +598,12 @@ function lsp.start_client(config) | ||||
|     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 msg = "server doesn't support "..method | ||||
|     local _ = log.warn() and log.warn(msg) | ||||
| @@ -495,8 +611,28 @@ function lsp.start_client(config) | ||||
|     return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) | ||||
|   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) | ||||
|     -- FIXME: callback is optional, but bufnr is apparently not? Shouldn't that | ||||
|     -- require a `select('#', ...)` call? | ||||
|     if not callback then | ||||
|       callback = resolve_callback(method) | ||||
|         or error(string.format("not found: %q request callback for client %q.", method, client.name)) | ||||
| @@ -521,10 +657,25 @@ function lsp.start_client(config) | ||||
|     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(...) | ||||
|     return rpc.notify(...) | ||||
|   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) | ||||
|     validate{id = {id, 'n'}} | ||||
|     return rpc.notify("$/cancelRequest", { id = id }) | ||||
| @@ -533,6 +684,14 @@ function lsp.start_client(config) | ||||
|   -- Track this so that we can escalate automatically if we've alredy tried a | ||||
|   -- graceful shutdown | ||||
|   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) | ||||
|     local handle = rpc.handle | ||||
|     if handle:is_closing() then | ||||
| @@ -554,10 +713,18 @@ function lsp.start_client(config) | ||||
|     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() | ||||
|     return rpc.handle:is_closing() | ||||
|   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) | ||||
|     text_document_did_open_handler(bufnr, client) | ||||
|     if config.on_attach then | ||||
| @@ -571,6 +738,12 @@ function lsp.start_client(config) | ||||
|   return client_id | ||||
| 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 value | ||||
|   return function(...) | ||||
| @@ -579,6 +752,9 @@ local function once(fn) | ||||
|   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 | ||||
| do | ||||
|   local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; } | ||||
| @@ -735,7 +911,7 @@ end | ||||
| --- | ||||
| --@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) | ||||
|   return active_clients[client_id] | ||||
| end | ||||
| @@ -769,7 +945,7 @@ end | ||||
|  | ||||
| --- Gets all active clients. | ||||
| --- | ||||
| --@return Table of |vim.lsp.client| objects | ||||
| --@returns Table of |vim.lsp.client| objects | ||||
| function lsp.get_active_clients() | ||||
|   return vim.tbl_values(active_clients) | ||||
| end | ||||
| @@ -904,7 +1080,7 @@ end | ||||
| --@param findstart 0 or 1, decides behavior | ||||
| --@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=1: list of matches (actually just calls |complete()|) | ||||
| function lsp.omnifunc(findstart, base) | ||||
| @@ -948,6 +1124,10 @@ function lsp.omnifunc(findstart, base) | ||||
|   return -2 | ||||
| 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) | ||||
|   return active_clients[client_id] == nil | ||||
| end | ||||
| @@ -992,12 +1172,17 @@ function lsp.set_log_level(level) | ||||
| end | ||||
|  | ||||
| --- Gets the path of the logfile used by the LSP client. | ||||
| --@returns (String) Path to logfile. | ||||
| function lsp.get_log_path() | ||||
|   return log.get_filename() | ||||
| end | ||||
|  | ||||
| -- Define the LspDiagnostics signs if they're not defined already. | ||||
| -- Defines the LspDiagnostics signs if they're not defined already. | ||||
| 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) | ||||
|     if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then | ||||
|       vim.fn.sign_define(name, properties) | ||||
|   | ||||
| @@ -7,14 +7,41 @@ local list_extend = vim.list_extend | ||||
|  | ||||
| local M = {} | ||||
|  | ||||
| --@private | ||||
| --- Returns nil if {status} is false or nil, otherwise returns the rest of the | ||||
| --- arguments. | ||||
| local function ok_or_nil(status, ...) | ||||
|   if not status then return end | ||||
|   return ... | ||||
| 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, ...) | ||||
|   return ok_or_nil(pcall(fn, ...)) | ||||
| 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) | ||||
|   validate { | ||||
|     method = {method, 's'}; | ||||
| @@ -23,9 +50,10 @@ local function request(method, params, callback) | ||||
|   return vim.lsp.buf_request(0, method, params, callback) | ||||
| end | ||||
|  | ||||
| --- Sends a notification through all clients associated with current buffer. | ||||
| -- | ||||
| --@return `true` if server responds. | ||||
| --- Checks whether the language servers attached to the current buffer are | ||||
| --- ready. | ||||
| --- | ||||
| --@returns `true` if server responds. | ||||
| function M.server_ready() | ||||
|   return not not vim.lsp.buf_notify(0, "window/progress", {}) | ||||
| end | ||||
| @@ -74,6 +102,12 @@ end | ||||
|  | ||||
| --- Retrieves the completion items at the current cursor position. Can only be | ||||
| --- 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) | ||||
|   local params = util.make_position_params() | ||||
|   params.context = context | ||||
| @@ -82,20 +116,27 @@ end | ||||
|  | ||||
| --- Formats the current buffer. | ||||
| --- | ||||
| --- The optional {options} table can be used to specify FormattingOptions, a | ||||
| --- list of which is available at | ||||
| --- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting. | ||||
| --@param options (optional, table) Can be used to specify FormattingOptions. | ||||
| --- Some unspecified options will be automatically derived from the current | ||||
| --- Neovim options. | ||||
| -- | ||||
| --@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting | ||||
| function M.formatting(options) | ||||
|   local params = util.make_formatting_params(options) | ||||
|   return request('textDocument/formatting', params) | ||||
| 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 | ||||
| --- 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) | ||||
|   local params = util.make_formatting_params(options) | ||||
|   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) | ||||
| 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) | ||||
|   validate { | ||||
|     options = {options, 't', true}; | ||||
| @@ -138,8 +186,10 @@ function M.range_formatting(options, start_pos, end_pos) | ||||
|   return request('textDocument/rangeFormatting', params) | ||||
| end | ||||
|  | ||||
| --- Renames all references to the symbol under the cursor. If {new_name} is not | ||||
| --- provided, the user will be prompted for a new name using |input()|. | ||||
| --- Renames all references to the symbol under the cursor. | ||||
| --- | ||||
| --@param new_name (string) If not provided, the user will be prompted for a new | ||||
| ---name using |input()|. | ||||
| function M.rename(new_name) | ||||
|   -- 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. | ||||
| @@ -152,6 +202,8 @@ end | ||||
|  | ||||
| --- 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) | ||||
|   validate { context = { context, 't', true } } | ||||
|   local params = util.make_position_params() | ||||
| @@ -169,6 +221,7 @@ function M.document_symbol() | ||||
|   request('textDocument/documentSymbol', params) | ||||
| end | ||||
|  | ||||
| --@private | ||||
| local function pick_call_hierarchy_item(call_hierarchy_items) | ||||
|   if not call_hierarchy_items then return end | ||||
|   if #call_hierarchy_items == 1 then | ||||
| @@ -186,6 +239,9 @@ local function pick_call_hierarchy_item(call_hierarchy_items) | ||||
|   return choice | ||||
| 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() | ||||
|   local params = util.make_position_params() | ||||
|   request('textDocument/prepareCallHierarchy', params, function(_, _, result) | ||||
| @@ -194,6 +250,9 @@ function M.incoming_calls() | ||||
|   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() | ||||
|   local params = util.make_position_params() | ||||
|   request('textDocument/prepareCallHierarchy', params, function(_, _, result) | ||||
| @@ -204,9 +263,11 @@ end | ||||
|  | ||||
| --- Lists all symbols in the current workspace in the quickfix window. | ||||
| --- | ||||
| --- The list is filtered against the optional argument {query}; | ||||
| --- if the argument is omitted from the call, the user is prompted to enter a string on the command line. | ||||
| --- An empty string means no filtering is done. | ||||
| --- The list is filtered against {query}; if the argument is omitted from the | ||||
| --- call, the user is prompted to enter a string on the command line. An empty | ||||
| --- string means no filtering is done. | ||||
| --- | ||||
| --@param query (string, optional) | ||||
| function M.workspace_symbol(query) | ||||
|   query = query or npcall(vfn.input, "Query: ") | ||||
|   local params = {query = query} | ||||
| @@ -227,10 +288,17 @@ function M.document_highlight() | ||||
|   request('textDocument/documentHighlight', params) | ||||
| end | ||||
|  | ||||
| --- Removes document highlights from current buffer. | ||||
| --- | ||||
| function M.clear_references() | ||||
|   util.buf_clear_references() | ||||
| 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) | ||||
|   validate { context = { context, 't', true } } | ||||
|   context = context or { diagnostics = util.get_line_diagnostics() } | ||||
| @@ -239,6 +307,10 @@ function M.code_action(context) | ||||
|   request('textDocument/codeAction', params) | ||||
| 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) | ||||
|   validate { | ||||
|     command = { command.command, 's' }, | ||||
|   | ||||
| @@ -7,17 +7,24 @@ local buf = require 'vim.lsp.buf' | ||||
|  | ||||
| local M = {} | ||||
|  | ||||
| -- FIXME: DOC: Expose in vimdocs | ||||
|  | ||||
| --@private | ||||
| --- Writes to error buffer. | ||||
| --@param ... (table of strings) Will be concatenated before being written | ||||
| local function err_message(...) | ||||
|   api.nvim_err_writeln(table.concat(vim.tbl_flatten{...})) | ||||
|   api.nvim_command("redraw") | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand | ||||
| M['workspace/executeCommand'] = function(err, _) | ||||
|   if err then | ||||
|     error("Could not execute code action: "..err.message) | ||||
|   end | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction | ||||
| M['textDocument/codeAction'] = function(_, _, actions) | ||||
|   if actions == nil or vim.tbl_isempty(actions) then | ||||
|     print("No code actions available") | ||||
| @@ -51,6 +58,7 @@ M['textDocument/codeAction'] = function(_, _, actions) | ||||
|   end | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit | ||||
| M['workspace/applyEdit'] = function(_, _, workspace_edit) | ||||
|   if not workspace_edit then return end | ||||
|   -- TODO(ashkan) Do something more with label? | ||||
| @@ -64,6 +72,7 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) | ||||
|   } | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_publishDiagnostics | ||||
| M['textDocument/publishDiagnostics'] = function(_, _, result) | ||||
|   if not result then return end | ||||
|   local uri = result.uri | ||||
| @@ -102,6 +111,7 @@ M['textDocument/publishDiagnostics'] = function(_, _, result) | ||||
|   vim.api.nvim_command("doautocmd User LspDiagnosticsChanged") | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references | ||||
| M['textDocument/references'] = function(_, _, result) | ||||
|   if not result then return end | ||||
|   util.set_qflist(util.locations_to_items(result)) | ||||
| @@ -109,6 +119,13 @@ M['textDocument/references'] = function(_, _, result) | ||||
|   api.nvim_command("wincmd p") | ||||
| 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) | ||||
|   if not result or vim.tbl_isempty(result) then return end | ||||
|  | ||||
| @@ -116,24 +133,30 @@ local symbol_callback = function(_, _, result, _, bufnr) | ||||
|   api.nvim_command("copen") | ||||
|   api.nvim_command("wincmd p") | ||||
| end | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol | ||||
| M['textDocument/documentSymbol'] = symbol_callback | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol | ||||
| M['workspace/symbol'] = symbol_callback | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename | ||||
| M['textDocument/rename'] = function(_, _, result) | ||||
|   if not result then return end | ||||
|   util.apply_workspace_edit(result) | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting | ||||
| M['textDocument/rangeFormatting'] = function(_, _, result) | ||||
|   if not result then return end | ||||
|   util.apply_text_edits(result) | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting | ||||
| M['textDocument/formatting'] = function(_, _, result) | ||||
|   if not result then return end | ||||
|   util.apply_text_edits(result) | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||
| M['textDocument/completion'] = function(_, _, result) | ||||
|   if vim.tbl_isempty(result or {}) then return end | ||||
|   local row, col = unpack(api.nvim_win_get_cursor(0)) | ||||
| @@ -146,6 +169,7 @@ M['textDocument/completion'] = function(_, _, result) | ||||
|   vim.fn.complete(textMatch+1, matches) | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover | ||||
| M['textDocument/hover'] = function(_, method, result) | ||||
|   util.focusable_float(method, function() | ||||
|     if not (result and result.contents) then | ||||
| @@ -166,6 +190,12 @@ M['textDocument/hover'] = function(_, method, result) | ||||
|   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) | ||||
|   if result == nil or vim.tbl_isempty(result) then | ||||
|     local _ = log.info() and log.info(method, 'No location found') | ||||
| @@ -188,11 +218,16 @@ local function location_callback(_, method, result) | ||||
|   end | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration | ||||
| M['textDocument/declaration'] = location_callback | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition | ||||
| M['textDocument/definition'] = location_callback | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition | ||||
| M['textDocument/typeDefinition'] = location_callback | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation | ||||
| M['textDocument/implementation'] = location_callback | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp | ||||
| M['textDocument/signatureHelp'] = function(_, method, result) | ||||
|   util.focusable_preview(method, function() | ||||
|     if not (result and result.signatures and result.signatures[1]) then | ||||
| @@ -208,15 +243,21 @@ M['textDocument/signatureHelp'] = function(_, method, result) | ||||
|   end) | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight | ||||
| M['textDocument/documentHighlight'] = function(_, _, result, _) | ||||
|   if not result then return end | ||||
|   local bufnr = api.nvim_get_current_buf() | ||||
|   util.buf_highlight_references(bufnr, result) | ||||
| 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) | ||||
|   -- result is a CallHierarchy{Incoming,Outgoing}Call[] | ||||
|   return function(_, _, result) | ||||
|     if not result then return end | ||||
|     local items = {} | ||||
| @@ -237,10 +278,13 @@ local make_call_hierarchy_callback = function(direction) | ||||
|   end | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/incomingCalls | ||||
| 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') | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage | ||||
| M['window/logMessage'] = function(_, _, result, client_id) | ||||
|   local message_type = result.type | ||||
|   local message = result.message | ||||
| @@ -261,6 +305,7 @@ M['window/logMessage'] = function(_, _, result, client_id) | ||||
|   return result | ||||
| end | ||||
|  | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/showMessage | ||||
| M['window/showMessage'] = function(_, _, result, client_id) | ||||
|   local message_type = result.type | ||||
|   local message = result.message | ||||
|   | ||||
| @@ -2,6 +2,9 @@ | ||||
|  | ||||
| local log = {} | ||||
|  | ||||
| -- FIXME: DOC | ||||
| -- Should be exposed in the vim docs. | ||||
| -- | ||||
| -- Log level dictionary with reverse lookup as well. | ||||
| -- | ||||
| -- Can be used to lookup the number from the name or the name from the number. | ||||
| @@ -21,12 +24,14 @@ local log_date_format = "%FT%H:%M:%S%z" | ||||
|  | ||||
| do | ||||
|   local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" | ||||
|   --@private | ||||
|   local function path_join(...) | ||||
|     return table.concat(vim.tbl_flatten{...}, path_sep) | ||||
|   end | ||||
|   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() | ||||
|     return logfilename | ||||
|   end | ||||
| @@ -36,6 +41,9 @@ do | ||||
|   for level, levelnr in pairs(log.levels) do | ||||
|     -- Also export the log level on the root object. | ||||
|     log[level] = levelnr | ||||
|     -- FIXME: DOC | ||||
|     -- Should be exposed in the vim docs. | ||||
|     -- | ||||
|     -- Set the lowercase name as the main use function. | ||||
|     -- If called without arguments, it will check whether the log level is | ||||
|     -- greater than or equal to this one. When called with arguments, it will | ||||
| @@ -74,6 +82,8 @@ end | ||||
| -- interfere with iterating the 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) | ||||
|   if type(level) == 'string' then | ||||
|     current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) | ||||
| @@ -84,8 +94,9 @@ function log.set_level(level) | ||||
|   end | ||||
| end | ||||
|  | ||||
| -- Return whether the level is sufficient for logging. | ||||
| -- @param level number log level | ||||
| --- Checks whether the level is sufficient for logging. | ||||
| --@param level number log level | ||||
| --@returns (bool) true if would log, false if not | ||||
| function log.should_log(level) | ||||
|   return level >= current_log_level | ||||
| end | ||||
|   | ||||
| @@ -2,6 +2,11 @@ | ||||
|  | ||||
| local protocol = {} | ||||
|  | ||||
| --@private | ||||
| --- Returns {a} if it is not nil, otherwise returns {b}. | ||||
| --- | ||||
| --@param a | ||||
| --@param b | ||||
| local function ifnil(a, b) | ||||
|   if a == nil then return b end | ||||
|   return a | ||||
| @@ -9,12 +14,14 @@ end | ||||
|  | ||||
|  | ||||
| --[=[ | ||||
| -- Useful for interfacing with: | ||||
| -- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md | ||||
| --@private | ||||
| --- Useful for interfacing with: | ||||
| --- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md | ||||
| function transform_schema_comments() | ||||
|   nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] | ||||
|   nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] | ||||
| end | ||||
| --@private | ||||
| function transform_schema_to_table() | ||||
|   transform_schema_comments() | ||||
|   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 | ||||
|  | ||||
| -- 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 status, result = pcall(vim.fn.json_encode, data) | ||||
|   if status then | ||||
| @@ -13,6 +18,11 @@ local function json_encode(data) | ||||
|     return nil, result | ||||
|   end | ||||
| end | ||||
| --@private | ||||
| --- Decodes from JSON. | ||||
| --- | ||||
| --@param data (string) Data to decode | ||||
| --@returns (table) Decoded JSON object | ||||
| local function json_decode(data) | ||||
|   local status, result = pcall(vim.fn.json_decode, data) | ||||
|   if status then | ||||
| @@ -22,17 +32,26 @@ local function json_decode(data) | ||||
|   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 stat = vim.loop.fs_stat(filename) | ||||
|   return stat and stat.type == 'directory' or false | ||||
| end | ||||
|  | ||||
| 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) | ||||
|   if v == NIL then return nil end | ||||
|   return v | ||||
| end | ||||
|  | ||||
| --@private | ||||
| --- Merges current process env with the given env and returns the result as | ||||
| --- a list of "k=v" strings. | ||||
| --- | ||||
| @@ -42,6 +61,8 @@ end | ||||
| ---  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", } | ||||
| --- </pre> | ||||
| --@param env (table) table of environment variable assignments | ||||
| --@returns (table) list of `"k=v"` strings | ||||
| local function env_merge(env) | ||||
|   if env == nil then | ||||
|     return env | ||||
| @@ -56,6 +77,11 @@ local function env_merge(env) | ||||
|   return final_env | ||||
| 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) | ||||
|   return table.concat { | ||||
|     'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; | ||||
| @@ -63,8 +89,11 @@ local function format_message_with_content_length(encoded_message) | ||||
|   } | ||||
| end | ||||
|  | ||||
| --- Parse an LSP Message's header | ||||
| -- @param header: The header to parse. | ||||
| --@private | ||||
| --- Parses an LSP Message's header | ||||
| --- | ||||
| --@param header: The header to parse. | ||||
| --@returns Parsed headers | ||||
| local function parse_headers(header) | ||||
|   if type(header) ~= 'string' then | ||||
|     return nil | ||||
| @@ -92,6 +121,8 @@ end | ||||
| -- case insensitive pattern. | ||||
| local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) | ||||
|  | ||||
| --@private | ||||
| --- The actual workhorse. | ||||
| local function request_parser_loop() | ||||
|   local buffer = '' | ||||
|   while true do | ||||
| @@ -138,6 +169,10 @@ local client_errors = vim.tbl_add_reverse_lookup { | ||||
|   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) | ||||
|   validate { | ||||
|     err = { err, 't' }; | ||||
| @@ -182,23 +217,69 @@ local function rpc_response_error(code, message, data) | ||||
| end | ||||
|  | ||||
| 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) | ||||
|   local _ = log.debug() and log.debug('notification', method, params) | ||||
| 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) | ||||
|   local _ = log.debug() and log.debug('server_request', method, params) | ||||
|   return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) | ||||
| 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) | ||||
|   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 | ||||
| --@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) | ||||
|   local _ = log.error() and log.error('client_error:', client_errors[code], err) | ||||
| end | ||||
|  | ||||
| --- Create and start an RPC client. | ||||
| -- @param cmd [ | ||||
| local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params) | ||||
| --- Starts an LSP server process and create an LSP RPC client object to | ||||
| --- interact with it. | ||||
| --- | ||||
| --@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}) | ||||
|   validate { | ||||
|     cmd = { cmd, 's' }; | ||||
| @@ -242,6 +323,11 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|  | ||||
|   local handle, pid | ||||
|   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) | ||||
|       stdin: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) | ||||
|   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 _ = log.debug() and log.debug("rpc.send.payload", payload) | ||||
|     if handle:is_closing() then return false end | ||||
| @@ -276,7 +368,14 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|     return true | ||||
|   end | ||||
|  | ||||
|   local function send_notification(method, params) | ||||
|   -- FIXME: DOC: 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) | ||||
|     return encode_and_send { | ||||
|       jsonrpc = "2.0"; | ||||
| @@ -285,6 +384,8 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|     } | ||||
|   end | ||||
|  | ||||
|   --@private | ||||
|   --- sends an error object to the remote LSP process. | ||||
|   local function send_response(request_id, err, result) | ||||
|     return encode_and_send { | ||||
|       id = request_id; | ||||
| @@ -294,7 +395,16 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|     } | ||||
|   end | ||||
|  | ||||
|   local function send_request(method, params, callback) | ||||
|   -- FIXME: DOC: 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 { | ||||
|       callback = { callback, 'f' }; | ||||
|     } | ||||
| @@ -320,11 +430,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|     end | ||||
|   end) | ||||
|  | ||||
|   --@private | ||||
|   local function on_error(errkind, ...) | ||||
|     assert(client_errors[errkind]) | ||||
|     -- TODO what to do if this fails? | ||||
|     pcall(handlers.on_error, errkind, ...) | ||||
|   end | ||||
|   --@private | ||||
|   local function pcall_handler(errkind, status, head, ...) | ||||
|     if not status then | ||||
|       on_error(errkind, head, ...) | ||||
| @@ -332,6 +444,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|     end | ||||
|     return status, head, ... | ||||
|   end | ||||
|   --@private | ||||
|   local function try_call(errkind, fn, ...) | ||||
|     return pcall_handler(errkind, pcall(fn, ...)) | ||||
|   end | ||||
| @@ -340,6 +453,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 | ||||
|   -- them with an error then, perhaps. | ||||
|  | ||||
|   --@private | ||||
|   local function handle_body(body) | ||||
|     local decoded, err = json_decode(body) | ||||
|     if not decoded then | ||||
| @@ -458,13 +572,13 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para | ||||
|   return { | ||||
|     pid = pid; | ||||
|     handle = handle; | ||||
|     request = send_request; | ||||
|     notify = send_notification; | ||||
|     request = request; | ||||
|     notify = notify | ||||
|   } | ||||
| end | ||||
|  | ||||
| return { | ||||
|   start = create_and_start_client; | ||||
|   start = start; | ||||
|   rpc_response_error = rpc_response_error; | ||||
|   format_rpc_error = format_rpc_error; | ||||
|   client_errors = client_errors; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ local highlight = require 'vim.highlight' | ||||
|  | ||||
| local M = {} | ||||
|  | ||||
| -- FIXME: DOC: Expose in vimdocs | ||||
| --- Diagnostics received from the server via `textDocument/publishDiagnostics` | ||||
| -- by buffer. | ||||
| -- | ||||
| @@ -33,18 +34,30 @@ local M = {} | ||||
| M.diagnostics_by_buf = {} | ||||
|  | ||||
| local split = vim.split | ||||
| --@private | ||||
| local function split_lines(value) | ||||
|   return split(value, '\n', true) | ||||
| end | ||||
|  | ||||
| --@private | ||||
| local function ok_or_nil(status, ...) | ||||
|   if not status then return end | ||||
|   return ... | ||||
| end | ||||
| --@private | ||||
| local function npcall(fn, ...) | ||||
|   return ok_or_nil(pcall(fn, ...)) | ||||
| 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) | ||||
|   -- 0-indexing to 1-indexing | ||||
|   local i_0 = A[1] + 1 | ||||
| @@ -78,6 +91,7 @@ function M.set_lines(lines, A, B, new_lines) | ||||
|   return lines | ||||
| end | ||||
|  | ||||
| --@private | ||||
| local function sort_by_key(fn) | ||||
|   return function(a,b) | ||||
|     local ka, kb = fn(a), fn(b) | ||||
| @@ -91,13 +105,15 @@ local function sort_by_key(fn) | ||||
|     return false | ||||
|   end | ||||
| end | ||||
| --@private | ||||
| local edit_sort_key = sort_by_key(function(e) | ||||
|   return {e.A[1], e.A[2], e.i} | ||||
| end) | ||||
|  | ||||
| --@private | ||||
| --- 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 | ||||
| -- 1-indexed | ||||
| --- Returns a zero-indexed column, since set_lines() does the conversion to | ||||
| --- 1-indexed | ||||
| local function get_line_byte_from_position(bufnr, position) | ||||
|   -- LSP's line and characters are 0-indexed | ||||
|   -- Vim's line and columns are 1-indexed | ||||
| @@ -114,6 +130,9 @@ local function get_line_byte_from_position(bufnr, position) | ||||
|   return col | ||||
| 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) | ||||
|   if not next(text_edits) then return end | ||||
|   if not api.nvim_buf_is_loaded(bufnr) then | ||||
| @@ -168,20 +187,30 @@ end | ||||
| -- function M.glob_to_regex(glob) | ||||
| -- end | ||||
|  | ||||
| -- textDocument/completion response returns one of CompletionItem[], CompletionList or null. | ||||
| -- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion | ||||
| --- Can be used to extract the completion items from a | ||||
| --- `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) | ||||
|   if type(result) == 'table' and result.items then | ||||
|     -- result is a `CompletionList` | ||||
|     return result.items | ||||
|   elseif result ~= nil then | ||||
|     -- result is `CompletionItem[]` | ||||
|     return result | ||||
|   else | ||||
|     -- result is `null` | ||||
|     return {} | ||||
|   end | ||||
| end | ||||
|  | ||||
| --- Apply the TextDocumentEdit response. | ||||
| -- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification | ||||
| --- Applies a `TextDocumentEdit`, which is a list of changes to a single | ||||
| -- 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) | ||||
|   local text_document = text_document_edit.textDocument | ||||
|   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) | ||||
| 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 res = "" | ||||
|  | ||||
| @@ -248,15 +284,20 @@ local function parse_snippet_rec(input, inner) | ||||
|   return res, input | ||||
| 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) | ||||
|   local res, _ = parse_snippet_rec(input, false) | ||||
|  | ||||
|   return res | ||||
| end | ||||
|  | ||||
| -- Sort by CompletionItem.sortText | ||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||
| --@private | ||||
| --- Sorts by CompletionItem.sortText. | ||||
| --- | ||||
| --@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||
| local function sort_completion_items(items) | ||||
|   if items[1] and items[1].sortText then | ||||
|     table.sort(items, function(a, b) return a.sortText < b.sortText | ||||
| @@ -264,9 +305,10 @@ local function sort_completion_items(items) | ||||
|   end | ||||
| end | ||||
|  | ||||
| -- Returns text that should be inserted when selecting completion item. The precedence is as follows: | ||||
| -- textEdit.newText > insertText > label | ||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||
| --@private | ||||
| --- Returns text that should be inserted when selecting completion item. The | ||||
| --- 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) | ||||
|   if item.textEdit ~= nil and item.textEdit.newText ~= nil then | ||||
|     if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then | ||||
| @@ -284,8 +326,10 @@ local function get_completion_word(item) | ||||
|   return item.label | ||||
| end | ||||
|  | ||||
| -- Some language servers return complementary candidates whose prefixes do not match are also returned. | ||||
| -- So we exclude completion candidates whose prefix does not match. | ||||
| --@private | ||||
| --- 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) | ||||
|   return vim.tbl_filter(function(item) | ||||
|     local word = get_completion_word(item) | ||||
| @@ -293,16 +337,26 @@ local function remove_unmatch_completion_items(items, prefix) | ||||
|   end, items) | ||||
| end | ||||
|  | ||||
| -- 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. | ||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion | ||||
| --- 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. | ||||
| --- | ||||
| --@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) | ||||
|   return protocol.CompletionItemKind[completion_item_kind] or "Unknown" | ||||
| end | ||||
|  | ||||
| --- Getting vim complete-items with incomplete flag. | ||||
| -- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) | ||||
| -- @return { matches = complete-items table, incomplete = boolean  } | ||||
| --- Turns the result of a `textDocument/completion` request into vim-compatible | ||||
| --- |complete-items|. | ||||
| --- | ||||
| --@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) | ||||
|   local items = M.extract_completion_items(result) | ||||
|   if vim.tbl_isempty(items) then | ||||
| @@ -350,7 +404,10 @@ function M.text_document_completion_list_to_complete_items(result, prefix) | ||||
|   return matches | ||||
| 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) | ||||
|   if workspace_edit.documentChanges then | ||||
|     for _, change in ipairs(workspace_edit.documentChanges) do | ||||
| @@ -375,9 +432,15 @@ function M.apply_workspace_edit(workspace_edit) | ||||
|   end | ||||
| end | ||||
|  | ||||
| --- Convert any of MarkedString | MarkedString[] | MarkupContent into markdown text lines | ||||
| -- see https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_hover | ||||
| -- Useful for textDocument/hover, textDocument/signatureHelp, and potentially others. | ||||
| --- Converts any of `MarkedString` | `MarkedString[]` | `MarkupContent` into | ||||
| --- a list of lines containing valid markdown. Useful to populate the hover | ||||
| --- 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) | ||||
|   contents = contents or {} | ||||
|   -- MarkedString variation 1 | ||||
| @@ -416,8 +479,11 @@ function M.convert_input_to_markdown_lines(input, contents) | ||||
|   return contents | ||||
| end | ||||
|  | ||||
| --- Convert SignatureHelp response to markdown lines. | ||||
| -- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp | ||||
| --- Converts `textDocument/SignatureHelp` response to markdown lines. | ||||
| --- | ||||
| --@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) | ||||
|   if not signature_help.signatures then | ||||
|     return | ||||
| @@ -475,6 +541,13 @@ function M.convert_signature_help_to_markdown_lines(signature_help) | ||||
|   return contents | ||||
| 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) | ||||
|   validate { | ||||
|     opts = { opts, 't', true }; | ||||
| @@ -520,6 +593,10 @@ function M.make_floating_popup_options(width, height, opts) | ||||
|   } | ||||
| end | ||||
|  | ||||
| --- Jumps to a location. | ||||
| --- | ||||
| --@param location (`Location`|`LocationLink`) | ||||
| --@returns `true` if the jump succeeded | ||||
| function M.jump_to_location(location) | ||||
|   -- location may be Location or LocationLink | ||||
|   local uri = location.uri or location.targetUri | ||||
| @@ -543,14 +620,14 @@ function M.jump_to_location(location) | ||||
|   return true | ||||
| end | ||||
|  | ||||
| --- Preview a location in a floating windows | ||||
| --- Previews a location in a floating window | ||||
| --- | ||||
| --- behavior depends on type of location: | ||||
| ---   - for Location, range is shown (e.g., function definition) | ||||
| ---   - for LocationLink, targetRange is shown (e.g., body of function definition) | ||||
| --- | ||||
| --@param location a single Location or LocationLink | ||||
| --@return bufnr,winnr buffer and window number of floating window or nil | ||||
| --@param location a single `Location` or `LocationLink` | ||||
| --@returns (bufnr,winnr) buffer and window number of floating window or nil | ||||
| function M.preview_location(location) | ||||
|   -- location may be LocationLink or Location (more useful for the former) | ||||
|   local uri = location.targetUri or location.uri | ||||
| @@ -565,6 +642,7 @@ function M.preview_location(location) | ||||
|   return M.open_floating_preview(contents, filetype) | ||||
| end | ||||
|  | ||||
| --@private | ||||
| local function find_window_by_var(name, value) | ||||
|   for _, win in ipairs(api.nvim_list_wins()) do | ||||
|     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 | ||||
|  | ||||
| -- Check if a window with `unique_name` tagged is associated with the current | ||||
| -- buffer. If not, make a new preview. | ||||
| -- | ||||
| -- fn()'s return bufnr, winnr | ||||
| -- case that a new floating window should be created. | ||||
| --- Enters/leaves the focusable window associated with the current buffer via the | ||||
| --window - variable `unique_name`. If no such window exists, run the function | ||||
| --{fn}. | ||||
| --- | ||||
| --@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) | ||||
|   -- Go back to previous window if we are in a focusable one | ||||
|   if npcall(api.nvim_win_get_var, 0, unique_name) then | ||||
|     return api.nvim_command("wincmd p") | ||||
|   end | ||||
| @@ -598,18 +682,21 @@ function M.focusable_float(unique_name, fn) | ||||
|   end | ||||
| end | ||||
|  | ||||
| -- Check if a window with `unique_name` tagged is associated with the current | ||||
| -- buffer. If not, make a new preview. | ||||
| -- | ||||
| -- fn()'s return values will be passed directly to open_floating_preview in the | ||||
| -- case that a new floating window should be created. | ||||
| --- Focuses/unfocuses the floating preview window associated with the current | ||||
| --- buffer via the window variable `unique_name`. If no such preview window | ||||
| --- exists, makes a new one. | ||||
| --- | ||||
| --@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) | ||||
|   return M.focusable_float(unique_name, function() | ||||
|     return M.open_floating_preview(fn()) | ||||
|   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 opts dictionary with optional fields | ||||
| @@ -617,7 +704,7 @@ end | ||||
| --             - 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_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) | ||||
|   validate { | ||||
|     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. | ||||
| --- This will by default insert a blank line separator after those code block | ||||
| --- regions to improve readability. | ||||
| --- The result is shown in a floating preview | ||||
| --- TODO: refactor to separate stripping/converting and make use of open_floating_preview | ||||
| --- The result is shown in a floating preview. | ||||
| --- | ||||
| --@param contents table of lines to show in window | ||||
| --@param opts dictionary with optional fields | ||||
| @@ -664,7 +752,7 @@ end | ||||
| --             - pad_top    number of lines to pad contents at top | ||||
| --             - pad_bottom number of lines to pad contents at bottom | ||||
| --             - 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) | ||||
|   validate { | ||||
|     contents = { contents, 't' }; | ||||
| @@ -738,6 +826,7 @@ function M.fancy_floating_markdown(contents, opts) | ||||
|  | ||||
|   vim.cmd("ownsyntax markdown") | ||||
|   local idx = 1 | ||||
|   --@private | ||||
|   local function apply_syntax_to_region(ft, start, finish) | ||||
|     if ft == '' then return end | ||||
|     local name = ft..idx | ||||
| @@ -763,11 +852,17 @@ function M.fancy_floating_markdown(contents, opts) | ||||
|   return bufnr, winnr | ||||
| 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) | ||||
|   api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") | ||||
| 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 opts dictionary with optional fields | ||||
| @@ -776,7 +871,7 @@ end | ||||
| --             - wrap_at character to wrap at for computing height | ||||
| --             - max_width  maximal width 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) | ||||
|   validate { | ||||
|     contents = { contents, 't' }; | ||||
| @@ -827,7 +922,7 @@ function M._make_floating_popup_size(contents, opts) | ||||
|   return width, height | ||||
| end | ||||
|  | ||||
| --- Show contents in a floating window | ||||
| --- Shows contents in a floating window. | ||||
| --- | ||||
| --@param contents table of lines to show in window | ||||
| --@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_top    number of lines to pad contents at top | ||||
| --             - 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) | ||||
|   validate { | ||||
|     contents = { contents, 't' }; | ||||
| @@ -912,6 +1008,9 @@ do | ||||
|     severity_floating_highlights[severity] = floating_highlight_name | ||||
|   end | ||||
|  | ||||
|   --- Clears diagnostics for a buffer. | ||||
|   --- | ||||
|   --@param bufnr (number) buffer id | ||||
|   function M.buf_clear_diagnostics(bufnr) | ||||
|     validate { bufnr = {bufnr, 'n', true} } | ||||
|     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) | ||||
|   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) | ||||
|     return severity_highlights[severity] | ||||
|   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() | ||||
|     local bufnr = api.nvim_get_current_buf() | ||||
|     local linenr = api.nvim_win_get_cursor(0)[1] - 1 | ||||
| @@ -941,6 +1048,8 @@ do | ||||
|     return diagnostics_by_line[linenr] or {} | ||||
|   end | ||||
|  | ||||
|   --- Displays the diagnostics for the current line in a floating hover | ||||
|   --- window. | ||||
|   function M.show_line_diagnostics() | ||||
|     -- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {}) | ||||
|     -- if #marks == 0 then | ||||
| @@ -977,10 +1086,10 @@ do | ||||
|     return popup_bufnr, winnr | ||||
|   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 diagnostics Diagnostics[] received from the language server. | ||||
|   --@param bufnr (number) buffer id for which the diagnostics are for | ||||
|   --@param diagnostics list of `Diagnostic`s received from the LSP server | ||||
|   function M.buf_diagnostics_save_positions(bufnr, diagnostics) | ||||
|     validate { | ||||
|       bufnr = {bufnr, 'n', true}; | ||||
| @@ -1000,6 +1109,10 @@ do | ||||
|     M.diagnostics_by_buf[bufnr] = diagnostics | ||||
|   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) | ||||
|     for _, diagnostic in ipairs(diagnostics) do | ||||
|       local start = diagnostic.range["start"] | ||||
| @@ -1020,11 +1133,18 @@ do | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   --- Removes document highlights from a buffer. | ||||
|   --- | ||||
|   --@param bufnr buffer id | ||||
|   function M.buf_clear_references(bufnr) | ||||
|     validate { bufnr = {bufnr, 'n', true} } | ||||
|     api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) | ||||
|   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) | ||||
|     validate { bufnr = {bufnr, 'n', true} } | ||||
|     for _, reference in ipairs(references) do | ||||
| @@ -1040,11 +1160,19 @@ do | ||||
|     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) | ||||
|     if not diagnostics then return end | ||||
|     local diagnostics_by_line = {} | ||||
|     for _, diagnostic in ipairs(diagnostics) do | ||||
|       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] | ||||
|       if not line_diagnostics then | ||||
|         line_diagnostics = {} | ||||
| @@ -1055,6 +1183,11 @@ do | ||||
|     return diagnostics_by_line | ||||
|   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) | ||||
|     if not diagnostics then | ||||
|       return | ||||
| @@ -1093,8 +1226,7 @@ do | ||||
|   --- </pre> | ||||
|   --- | ||||
|   --@param kind Diagnostic severity kind: See |vim.lsp.protocol.DiagnosticSeverity| | ||||
|   --- | ||||
|   --@return Count of diagnostics | ||||
|   --@returns Count of diagnostics | ||||
|   function M.buf_diagnostics_count(kind) | ||||
|     local bufnr = vim.api.nvim_get_current_buf() | ||||
|     local diagnostics = M.diagnostics_by_buf[bufnr] | ||||
| @@ -1115,7 +1247,7 @@ do | ||||
|     [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: | ||||
|   --- | ||||
| @@ -1136,8 +1268,11 @@ local position_sort = sort_by_key(function(v) | ||||
|   return {v.start.line, v.start.character} | ||||
| end) | ||||
|  | ||||
| -- Returns the items with the byte position calculated correctly and in sorted | ||||
| -- order. | ||||
| --- Returns the items with the byte position calculated correctly and in sorted | ||||
| --- 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) | ||||
|   local items = {} | ||||
|   local grouped = setmetatable({}, { | ||||
| @@ -1180,6 +1315,10 @@ function M.locations_to_items(locations) | ||||
|   return items | ||||
| 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) | ||||
|   vim.fn.setloclist(0, {}, ' ', { | ||||
|     title = 'Language Server'; | ||||
| @@ -1187,6 +1326,10 @@ function M.set_loclist(items) | ||||
|   }) | ||||
| 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) | ||||
|   vim.fn.setqflist({}, ' ', { | ||||
|     title = 'Language Server'; | ||||
| @@ -1201,10 +1344,11 @@ function M._get_symbol_kind_name(symbol_kind) | ||||
|   return protocol.SymbolKind[symbol_kind] or "Unknown" | ||||
| end | ||||
|  | ||||
| --- Convert symbols to quickfix list items | ||||
| --- Converts symbols to quickfix list items. | ||||
| --- | ||||
| --@param symbols DocumentSymbol[] or SymbolInformation[] | ||||
| function M.symbols_to_items(symbols, bufnr) | ||||
|   --@private | ||||
|   local function _symbols_to_items(_symbols, _items, _bufnr) | ||||
|     for _, symbol in ipairs(_symbols) do | ||||
|       if symbol.location then -- SymbolInformation type | ||||
| @@ -1239,7 +1383,9 @@ function M.symbols_to_items(symbols, bufnr) | ||||
|   return _symbols_to_items(symbols, {}, bufnr) | ||||
| 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) | ||||
|   local start = 1 | ||||
|   for i = 1, #lines do | ||||
| @@ -1258,11 +1404,13 @@ function M.trim_empty_lines(lines) | ||||
|   return vim.list_extend({}, lines, start, finish) | ||||
| end | ||||
|  | ||||
| -- Accepts markdown lines and tries to reduce it to a filetype if it is | ||||
| -- just a single code block. | ||||
| -- Note: This modifies the input. | ||||
| -- | ||||
| -- Returns: filetype or 'markdown' if it was unchanged. | ||||
| --- Accepts markdown lines and tries to reduce them to a filetype if they | ||||
| --- comprise just a single code block. | ||||
| --- | ||||
| --- CAUTION: Modifies the input in-place! | ||||
| --- | ||||
| --@param lines (table) list of lines | ||||
| --@returns (string) filetype or 'markdown' if it was unchanged. | ||||
| function M.try_trim_markdown_code_blocks(lines) | ||||
|   local language_id = lines[1]:match("^```(.*)") | ||||
|   if language_id then | ||||
| @@ -1285,6 +1433,7 @@ function M.try_trim_markdown_code_blocks(lines) | ||||
| end | ||||
|  | ||||
| local str_utfindex = vim.str_utfindex | ||||
| --@private | ||||
| local function make_position_param() | ||||
|   local row, col = unpack(api.nvim_win_get_cursor(0)) | ||||
|   row = row - 1 | ||||
| @@ -1293,6 +1442,10 @@ local function make_position_param() | ||||
|   return { line = row; character = col; } | ||||
| 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() | ||||
|   return { | ||||
|     textDocument = M.make_text_document_params(); | ||||
| @@ -1300,6 +1453,13 @@ function M.make_position_params() | ||||
|   } | ||||
| 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() | ||||
|   local position = make_position_param() | ||||
|   return { | ||||
| @@ -1308,11 +1468,15 @@ function M.make_range_params() | ||||
|   } | ||||
| 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() | ||||
|   return { uri = vim.uri_from_bufnr(0) } | ||||
| end | ||||
|  | ||||
| --- Get visual width of tabstop. | ||||
| --- Returns visual width of tabstop. | ||||
| --- | ||||
| --@see |softtabstop| | ||||
| --@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 | ||||
| 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) | ||||
|   validate { options = {options, 't', true} } | ||||
|   options = vim.tbl_extend('keep', options or {}, { | ||||
| @@ -1336,9 +1505,12 @@ function M.make_formatting_params(options) | ||||
|   } | ||||
| end | ||||
|  | ||||
| -- @param buf buffer handle or 0 for current. | ||||
| -- @param row 0-indexed line | ||||
| -- @param col 0-indexed byte offset in line | ||||
| --- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. | ||||
| --- | ||||
| --@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) | ||||
|   local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1] | ||||
|   -- If the col is past the EOL, use the line length. | ||||
|   | ||||
| @@ -39,6 +39,7 @@ Each function :help block is formatted as follows: | ||||
|     parameter is marked as [out]. | ||||
|   - Each function documentation is separated by a single line. | ||||
| """ | ||||
| import argparse | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| @@ -841,7 +842,7 @@ def delete_lines_below(filename, tokenstr): | ||||
|         fp.writelines(lines[0:i]) | ||||
|  | ||||
|  | ||||
| def main(config): | ||||
| def main(config, args=None): | ||||
|     """Generates: | ||||
|  | ||||
|     1. Vim :help docs | ||||
| @@ -960,7 +961,11 @@ def main(config): | ||||
|  | ||||
|         i = 0 | ||||
|         for filename in CONFIG[target]['section_order']: | ||||
|             title, helptag, section_doc = sections.pop(filename) | ||||
|             try: | ||||
|                 title, helptag, section_doc = sections.pop(filename) | ||||
|             except KeyError: | ||||
|                 print("Warning:", filename, "has empty docs, skipping") | ||||
|                 continue | ||||
|             i += 1 | ||||
|             if filename not in CONFIG[target]['append_only']: | ||||
|                 docs += sep | ||||
| @@ -983,7 +988,8 @@ def main(config): | ||||
|         with open(mpack_file, 'wb') as fp: | ||||
|             fp.write(msgpack.packb(fn_map_full, use_bin_type=True)) | ||||
|  | ||||
|         shutil.rmtree(output_dir) | ||||
|         if not args.keep_tmpfiles: | ||||
|             shutil.rmtree(output_dir) | ||||
|  | ||||
|  | ||||
| def filter_source(filename): | ||||
| @@ -1001,6 +1007,15 @@ def filter_source(filename): | ||||
|                          fp.read(), flags=re.M)) | ||||
|  | ||||
|  | ||||
| def parse_args(): | ||||
|     ap = argparse.ArgumentParser() | ||||
|     ap.add_argument('source_filter', nargs='*', | ||||
|                     help="Filter source file(s)") | ||||
|     ap.add_argument('-k', '--keep-tmpfiles', action='store_true', | ||||
|                     help="Keep temporary files") | ||||
|     return ap.parse_args() | ||||
|  | ||||
|  | ||||
| Doxyfile = textwrap.dedent(''' | ||||
|     OUTPUT_DIRECTORY       = {output} | ||||
|     INPUT                  = {input} | ||||
| @@ -1037,9 +1052,10 @@ Doxyfile = textwrap.dedent(''' | ||||
| ''') | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     if len(sys.argv) > 1: | ||||
|         filter_source(sys.argv[1]) | ||||
|     args = parse_args() | ||||
|     if len(args.source_filter) > 0: | ||||
|         filter_source(args.source_filter[0]) | ||||
|     else: | ||||
|         main(Doxyfile) | ||||
|         main(Doxyfile, args) | ||||
|  | ||||
| # vim: set ft=python ts=4 sw=4 tw=79 et : | ||||
|   | ||||
| @@ -203,10 +203,10 @@ Integer nvim_get_hl_id_by_name(String name) | ||||
| /// flags. This is a blocking call, unlike |nvim_input()|. | ||||
| /// | ||||
| /// On execution error: does not fail, but updates v:errmsg. | ||||
| // | ||||
| //  If you need to input sequences like <C-o> use nvim_replace_termcodes | ||||
| //  to replace the termcodes and then pass the resulting string to | ||||
| //  nvim_feedkeys. You'll also want to enable escape_csi. | ||||
| /// | ||||
| ///  If you need to input sequences like <C-o> use |nvim_replace_termcodes| | ||||
| ///  to replace the termcodes and then pass the resulting string to | ||||
| ///  nvim_feedkeys. You'll also want to enable escape_csi. | ||||
| /// | ||||
| /// Example: | ||||
| /// <pre> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 TJ DeVries
					TJ DeVries