diff options
Diffstat (limited to 'runtime')
29 files changed, 1026 insertions, 604 deletions
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 4f132b6121..90d353f9de 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -197,13 +197,21 @@ function! s:extract_sect_and_name_ref(ref) abort if empty(name) throw 'manpage reference cannot contain only parentheses' endif - return ['', name] + return ['', s:spaces_to_underscores(name)] endif let left = split(ref, '(') " see ':Man 3X curses' on why tolower. " TODO(nhooyr) Not sure if this is portable across OSs " but I have not seen a single uppercase section. - return [tolower(split(left[1], ')')[0]), left[0]] + return [tolower(split(left[1], ')')[0]), s:spaces_to_underscores(left[0])] +endfunction + +" replace spaces in a man page name with underscores +" intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)'; +" while editing SQL source code, it's nice to visually select 'CREATE TABLE' +" and hit 'K', which requires this transformation +function! s:spaces_to_underscores(str) + return substitute(a:str, ' ', '_', 'g') endfunction function! s:get_path(sect, name) abort diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 1ad0a6883e..bb8e83f84a 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -531,6 +531,20 @@ nvim__get_hl_defs({ns_id}) *nvim__get_hl_defs()* nvim__get_lib_dir() *nvim__get_lib_dir()* TODO: Documentation +nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()* + Find files in runtime directories + + Attributes: ~ + {fast} + + Parameters: ~ + {pat} pattern of files to search for + {all} whether to return all matches or only the first + {options} is_lua: only search lua subdirs + + Return: ~ + list of absolute paths to the found files + nvim__id({obj}) *nvim__id()* Returns object given as argument. @@ -582,6 +596,9 @@ nvim__id_float({flt}) *nvim__id_float()* nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* TODO: Documentation +nvim__runtime_inspect() *nvim__runtime_inspect()* + TODO: Documentation + nvim__screenshot({path}) *nvim__screenshot()* TODO: Documentation @@ -802,6 +819,39 @@ nvim_eval({expr}) *nvim_eval()* Return: ~ Evaluation result or expanded object +nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()* + Evaluates statusline string. + + Attributes: ~ + {fast} + + Parameters: ~ + {str} Statusline string (see 'statusline'). + {opts} Optional parameters. + • winid: (number) |window-ID| of the window to use + as context for statusline. + • maxwidth: (number) Maximum width of statusline. + • fillchar: (string) Character to fill blank + spaces in the statusline (see 'fillchars'). + • highlights: (boolean) Return highlight + information. + • use_tabline: (boolean) Evaluate tabline instead + of statusline. When |TRUE|, {winid} is ignored. + + Return: ~ + Dictionary containing statusline information, with these + keys: + • str: (string) Characters that will be displayed on the + statusline. + • width: (number) Display width of the statusline. + • highlights: Array containing highlight information of + the statusline. Only included when the "highlights" key + in {opts} is |TRUE|. Each element of the array is a + |Dictionary| with these keys: + • start: (number) Byte index (0-based) of first + character that uses the highlight. + • group: (string) Name of highlight group. + nvim_exec({src}, {output}) *nvim_exec()* Executes Vimscript (multiline block of Ex-commands), like anonymous |:source|. @@ -1028,7 +1078,7 @@ nvim_get_keymap({mode}) *nvim_get_keymap()* Array of maparg()-like dictionaries describing mappings. The "buffer" key is always zero. -nvim_get_mark({name}) *nvim_get_mark()* +nvim_get_mark({name}, {opts}) *nvim_get_mark()* Return a tuple (row, col, buffer, buffername) representing the position of the uppercase/file named mark. See |mark-motions|. @@ -1040,6 +1090,7 @@ nvim_get_mark({name}) *nvim_get_mark()* Parameters: ~ {name} Mark name + {opts} Optional parameters. Reserved for future use. Return: ~ 4-tuple (row, col, buffer, buffername), (0, 0, 0, '') if @@ -1827,7 +1878,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* callbacks. {opts} Optional parameters. • on_lines: Lua callback invoked on change. - Return`true`to detach. Args: + Return `true` to detach. Args: • the string "lines" • buffer handle • b:changedtick @@ -1843,7 +1894,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* • on_bytes: lua callback invoked on change. This callback receives more granular information about the change compared to - on_lines. Return`true`to detach. Args: + on_lines. Return `true` to detach. Args: • the string "bytes" • buffer handle • b:changedtick @@ -2282,7 +2333,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) • hl_mode : control how highlights are combined with the highlights of the text. Currently only affects virt_text highlights, but might - affect`hl_group`in later versions. + affect `hl_group` in later versions. • "replace": only show the virt_text color. This is the default • "combine": combine with background text @@ -2370,7 +2421,7 @@ nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing}, {replacement}) {replacement} Array of lines to use as replacement *nvim_buf_set_mark()* -nvim_buf_set_mark({buffer}, {name}, {line}, {col}) +nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts}) Sets a named mark in the given buffer, all marks are allowed file/uppercase, visual, last change, etc. See |mark-motions|. @@ -2384,6 +2435,7 @@ nvim_buf_set_mark({buffer}, {name}, {line}, {col}) {name} Mark name {line} Line number {col} Column/row number + {opts} Optional parameters. Reserved for future use. Return: ~ true if the mark was set, else false. @@ -2692,28 +2744,29 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* {buffer} Buffer to display, or 0 for current buffer {enter} Enter the window (make it the current window) {config} Map defining the window configuration. Keys: - • `relative`: Sets the window layout to "floating", placed - at (row,col) coordinates relative to: + • relative: Sets the window layout to + "floating", placed at (row,col) coordinates + relative to: • "editor" The global editor grid • "win" Window given by the `win` field, or current window. • "cursor" Cursor position in current window. - • `win` : |window-ID| for relative="win". - • `anchor`: Decides which corner of the float to place - at (row,col): + • win: |window-ID| for relative="win". + • anchor: Decides which corner of the float to + place at (row,col): • "NW" northwest (default) • "NE" northeast • "SW" southwest • "SE" southeast - • `width` : Window width (in character cells). + • width: Window width (in character cells). Minimum of 1. - • `height` : Window height (in character cells). + • height: Window height (in character cells). Minimum of 1. - • `bufpos`: Places float relative to buffer text (only - when relative="win"). Takes a tuple of - zero-indexed [line, column].`row`and`col`if given are applied relative to this + • bufpos: Places float relative to buffer text + (only when relative="win"). Takes a tuple of + zero-indexed [line, column]. `row` and `col` if given are applied relative to this position, else they default to: • `row=1` and `col=0` if `anchor` is "NW" or "NE" @@ -2721,19 +2774,19 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* "SE" (thus like a tooltip near the buffer text). - • `row` : Row position in units of "screen cell + • row: Row position in units of "screen cell height", may be fractional. - • `col` : Column position in units of "screen - cell width", may be fractional. - • `focusable` : Enable focus by user actions + • col: Column position in units of "screen cell + width", may be fractional. + • focusable: Enable focus by user actions (wincmds, mouse events). Defaults to true. Non-focusable windows can be entered by |nvim_set_current_win()|. - • `external` : GUI should display the window as - an external top-level window. Currently - accepts no other positioning configuration - together with this. - • `zindex`: Stacking order. floats with higher`zindex`go on top on floats with lower indices. Must + • external: GUI should display the window as an + external top-level window. Currently accepts + no other positioning configuration together + with this. + • zindex: Stacking order. floats with higher `zindex` go on top on floats with lower indices. Must be larger than zero. The following screen elements have hard-coded z-indices: • 100: insert completion popupmenu @@ -2744,7 +2797,7 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* are recommended, unless there is a good reason to overshadow builtin elements. - • `style`: Configure the appearance of the window. + • style: Configure the appearance of the window. Currently only takes one non-empty value: • "minimal" Nvim will display the window with many UI options disabled. This is useful @@ -2759,9 +2812,9 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* and clearing the |EndOfBuffer| region in 'winhighlight'. - • `border`: Style of (optional) window border. This can - either be a string or an array. The string - values are + • border: Style of (optional) window border. + This can either be a string or an array. The + string values are • "none": No border (default). • "single": A single line box. • "double": A double line box. @@ -2791,7 +2844,7 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* It could also be specified by character: [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. - • `noautocmd` : If true then no buffer-related + • noautocmd: If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index a7ce4135af..21a34178b3 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -85,6 +85,9 @@ For each of the functions below, use the corresponding function in *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead. *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead. *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead. + +The following have been replaced by |vim.diagnostic.open_float()|. + *vim.lsp.diagnostic.show_line_diagnostics()* *vim.lsp.diagnostic.show_position_diagnostics()* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 17d317522b..d121dba435 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -75,6 +75,100 @@ Functions that take a severity as an optional parameter (e.g. The latter form allows users to specify a range of severities. ============================================================================== +HANDLERS *diagnostic-handlers* + +Diagnostics are shown to the user with |vim.diagnostic.show()|. The display of +diagnostics is managed through handlers. A handler is a table with a "show" +and (optionally) a "hide" function. The "show" function has the signature +> + function(namespace, bufnr, diagnostics, opts) +< +and is responsible for displaying or otherwise handling the given +diagnostics. The "hide" function takes care of "cleaning up" any actions taken +by the "show" function and has the signature +> + function(namespace, bufnr) +< +Handlers can be configured with |vim.diagnostic.config()| and added by +creating a new key in `vim.diagnostic.handlers` (see +|diagnostic-handlers-example|). + +The {opts} table passed to a handler is the full set of configuration options +(that is, it is not limited to just the options for the handler itself). The +values in the table are already resolved (i.e. if a user specifies a +function for a config option, the function has already been evaluated). + +Nvim provides these handlers by default: "virtual_text", "signs", and +"underline". + + *diagnostic-handlers-example* +The example below creates a new handler that notifies the user of diagnostics +with |vim.notify()|: > + + -- It's good practice to namespace custom handlers to avoid collisions + vim.diagnostic.handlers["my/notify"] = { + show = function(namespace, bufnr, diagnostics, opts) + -- In our example, the opts table has a "log_level" option + local level = opts["my/notify"].log_level + + local name = vim.diagnostic.get_namespace(namespace).name + local msg = string.format("%d diagnostics in buffer %d from %s", + #diagnostics, + bufnr, + name) + vim.notify(msg, level) + end, + } + + -- Users can configure the handler + vim.diagnostic.config({ + ["my/notify"] = { + log_level = vim.log.levels.INFO + } + }) +< +In this example, there is nothing to do when diagnostics are hidden, so we +omit the "hide" function. + +Existing handlers can be overriden. For example, use the following to only +show a sign for the highest severity diagnostic on a given line: > + + -- Create a custom namespace. This will aggregate signs from all other + -- namespaces and only show the one with the highest severity on a + -- given line + local ns = vim.api.nvim_create_namespace("my_namespace") + + -- Get a reference to the original signs handler + local orig_signs_handler = vim.diagnostic.handlers.signs + + -- Override the built-in signs handler + vim.diagnostic.handlers.signs = { + show = function(_, bufnr, _, opts) + -- Get all diagnostics from the whole buffer rather than just the + -- diagnostics passed to the handler + local diagnostics = vim.diagnostic.get(bufnr) + + -- Find the "worst" diagnostic per line + local max_severity_per_line = {} + for _, d in pairs(diagnostics) do + local m = max_severity_per_line[d.lnum] + if not m or d.severity < m.severity then + max_severity_per_line[d.lnum] = d + end + end + + -- Pass the filtered diagnostics (with our custom namespace) to + -- the original handler + local filtered_diagnostics = vim.tbl_values(max_severity_per_line) + orig_signs_handler.show(ns, bufnr, filtered_diagnostics, opts) + end, + hide = function(_, bufnr) + orig_signs_handler.hide(ns, bufnr) + end, + } +< + +============================================================================== HIGHLIGHTS *diagnostic-highlights* All highlights defined for diagnostics begin with `Diagnostic` followed by @@ -202,51 +296,6 @@ Example: > autocmd User DiagnosticsChanged lua vim.diagnostic.setqflist({open = false }) < ============================================================================== -CUSTOMIZATION *diagnostic-config* - -If you need more customization over the way diagnostics are displayed than the -built-in configuration options provide, you can override the display handler -explicitly. For example, use the following to only show a sign for the highest -severity diagnostic on a given line: > - - -- Disable the default signs handler - vim.diagnostic.config({signs = false}) - - -- Create a namespace. This won't be used to add any diagnostics, - -- only to display them. - local ns = vim.api.nvim_create_namespace("my_namespace") - - -- Create a reference to the original function - local orig_show = vim.diagnostic.show - - local function set_signs(bufnr) - -- Get all diagnostics from the current buffer - local diagnostics = vim.diagnostic.get(bufnr) - - -- Find the "worst" diagnostic per line - local max_severity_per_line = {} - for _, d in pairs(diagnostics) do - local m = max_severity_per_line[d.lnum] - if not m or d.severity < m.severity then - max_severity_per_line[d.lnum] = d - end - end - - -- Show the filtered diagnostics using the custom namespace. Use the - -- reference to the original function to avoid a loop. - local filtered_diagnostics = vim.tbl_values(max_severity_per_line) - orig_show(ns, bufnr, filtered_diagnostics, { - virtual_text=false, - underline=false, - signs=true - }) - end - - function vim.diagnostic.show(namespace, bufnr, ...) - orig_show(namespace, bufnr, ...) - set_signs(bufnr) - end -< ============================================================================== Lua module: vim.diagnostic *diagnostic-api* @@ -262,12 +311,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()* For example, if a user enables virtual text globally with > - vim.diagnostic.config({virt_text = true}) + vim.diagnostic.config({virtual_text = true}) < and a diagnostic producer sets diagnostics with > - vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false}) + vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) < then virtual text will not be enabled for those diagnostics. @@ -302,7 +351,7 @@ config({opts}, {namespace}) *vim.diagnostic.config()* • format: (function) A function that takes a diagnostic as input and returns a string. The return value is the text used - to display the diagnostic. Example:> + to display the diagnostic. Example: > function(diagnostic) if diagnostic.severity == vim.diagnostic.severity.ERROR then @@ -324,6 +373,18 @@ config({opts}, {namespace}) *vim.diagnostic.config()* Otherwise, all signs use the same priority. + • float: Options for floating windows: + • severity: See |diagnostic-severity|. + • show_header: (boolean, default true) Show + "Diagnostics:" header + • source: (string) Include the diagnostic + source in the message. One of "always" or + "if_many". + • format: (function) A function that takes + a diagnostic as input and returns a + string. The return value is the text used + to display the diagnostic. + • update_in_insert: (default false) Update diagnostics in Insert mode (if false, diagnostics are updated on InsertLeave) @@ -382,6 +443,15 @@ get({bufnr}, {opts}) *vim.diagnostic.get()* Return: ~ table A list of diagnostic items |diagnostic-structure|. +get_namespace({namespace}) *vim.diagnostic.get_namespace()* + Get namespace metadata. + + Parameters: ~ + {ns} number Diagnostic namespace + + Return: ~ + table Namespace metadata + get_namespaces() *vim.diagnostic.get_namespaces()* Get current diagnostic namespaces. @@ -442,12 +512,10 @@ goto_next({opts}) *vim.diagnostic.goto_next()* • wrap: (boolean, default true) Whether to loop around file or not. Similar to 'wrapscan'. • severity: See |diagnostic-severity|. - • enable_popup: (boolean, default true) Call - |vim.diagnostic.show_line_diagnostics()| on - jump. - • popup_opts: (table) Table to pass as {opts} - parameter to - |vim.diagnostic.show_line_diagnostics()| + • float: (boolean or table, default true) If + "true", call |vim.diagnostic.open_float()| after + moving. If a table, pass the table as the {opts} + parameter to |vim.diagnostic.open_float()|. • win_id: (number, default 0) Window ID goto_prev({opts}) *vim.diagnostic.goto_prev()* @@ -508,6 +576,46 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults}) diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. +open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* + Show diagnostics in a floating window. + + Parameters: ~ + {bufnr} number|nil Buffer number. Defaults to the current + buffer. + {opts} table|nil Configuration table with the same keys + as |vim.lsp.util.open_floating_preview()| in + addition to the following: + • namespace: (number) Limit diagnostics to the + given namespace + • scope: (string, default "buffer") Show + diagnostics from the whole buffer ("buffer"), + the current cursor line ("line"), or the + current cursor position ("cursor"). + • pos: (number or table) If {scope} is "line" or + "cursor", use this position rather than the + cursor position. If a number, interpreted as a + line number; otherwise, a (row, col) tuple. + • severity_sort: (default false) Sort diagnostics + by severity. Overrides the setting from + |vim.diagnostic.config()|. + • severity: See |diagnostic-severity|. Overrides + the setting from |vim.diagnostic.config()|. + • show_header: (boolean, default true) Show + "Diagnostics:" header. Overrides the setting + from |vim.diagnostic.config()|. + • source: (string) Include the diagnostic source + in the message. One of "always" or "if_many". + Overrides the setting from + |vim.diagnostic.config()|. + • format: (function) A function that takes a + diagnostic as input and returns a string. The + return value is the text used to display the + diagnostic. Overrides the setting from + |vim.diagnostic.config()|. + + Return: ~ + tuple ({float_bufnr}, {win_id}) + reset({namespace}, {bufnr}) *vim.diagnostic.reset()* Remove all diagnostics from the given namespace. @@ -569,7 +677,7 @@ show({namespace}, {bufnr}, {diagnostics}, {opts}) Display diagnostics for the given namespace and buffer. Parameters: ~ - {namespace} number Diagnostic namespace + {namespace} number Diagnostic namespace. {bufnr} number|nil Buffer number. Defaults to the current buffer. {diagnostics} table|nil The diagnostics to display. When @@ -581,51 +689,6 @@ show({namespace}, {bufnr}, {diagnostics}, {opts}) {opts} table|nil Display options. See |vim.diagnostic.config()|. - *vim.diagnostic.show_line_diagnostics()* -show_line_diagnostics({opts}, {bufnr}, {lnum}) - Open a floating window with the diagnostics from the given - line. - - Parameters: ~ - {opts} table Configuration table. See - |vim.diagnostic.show_position_diagnostics()|. - {bufnr} number|nil Buffer number. Defaults to the current - buffer. - {lnum} number|nil Line number. Defaults to line number - of cursor. - - Return: ~ - tuple ({popup_bufnr}, {win_id}) - - *vim.diagnostic.show_position_diagnostics()* -show_position_diagnostics({opts}, {bufnr}, {position}) - Open a floating window with the diagnostics at the given - position. - - Parameters: ~ - {opts} table|nil Configuration table with the same - keys as |vim.lsp.util.open_floating_preview()| - in addition to the following: - • namespace: (number) Limit diagnostics to the - given namespace - • severity: See |diagnostic-severity|. - • show_header: (boolean, default true) Show - "Diagnostics:" header - • source: (string) Include the diagnostic - source in the message. One of "always" or - "if_many". - • format: (function) A function that takes a - diagnostic as input and returns a string. - The return value is the text used to display - the diagnostic. - {bufnr} number|nil Buffer number. Defaults to the - current buffer. - {position} table|nil The (0,0)-indexed position. Defaults - to the current cursor position. - - Return: ~ - tuple ({popup_bufnr}, {win_id}) - toqflist({diagnostics}) *vim.diagnostic.toqflist()* Convert a list of diagnostics to a list of quickfix items that can be passed to |setqflist()| or |setloclist()|. diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index efba9020f9..4e3173cfa9 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1331,11 +1331,12 @@ window the current directory is changed to the last specified local current directory. If none was specified, the global or tab-local directory is used. When changing tabs the same behaviour applies. If the current tab has no -local working directory the global working directory is used. When a |:cd| -command is used, the current window and tab will lose their local current -directories and will use the global current directory from now on. When -a |:tcd| command is used, only the current window will lose its local working -directory. +local working directory the global working directory is used. + +When a |:cd| command is used, the current window and tab will lose their local +current directories and will use the global current directory from now on. +When a |:tcd| command is used, only the current window will lose its local +working directory. After using |:cd| the full path name will be used for reading and writing files. On some networked file systems this may cause problems. The result of diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4467afaa16..6549d0b5f3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2461,8 +2461,8 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) has({feature}) Number |TRUE| if feature {feature} supported has_key({dict}, {key}) Number |TRUE| if {dict} has entry {key} haslocaldir([{winnr} [, {tabnr}]]) - Number |TRUE| if current window executed |:lcd| - or |:tcd| + Number |TRUE| if the window executed |:lcd| or + the tab executed |:tcd| hasmapto({what} [, {mode} [, {abbr}]]) Number |TRUE| if mapping to {what} exists histadd({history}, {item}) String add an item to a history @@ -5012,6 +5012,7 @@ getcwd([{winnr}[, {tabnr}]]) *getcwd()* {winnr} can be the window number or the |window-ID|. If both {winnr} and {tabnr} are -1 the global working directory is returned. + Throw error if the arguments are invalid. |E5000| |E5001| |E5002| Can also be used as a |method|: > GetWinnr()->getcwd() @@ -5774,8 +5775,9 @@ has_key({dict}, {key}) *has_key()* mydict->has_key(key) haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* - The result is a Number, which is 1 when the tabpage or window - has set a local path via |:tcd| or |:lcd|, otherwise 0. + The result is a Number, which is 1 when the window has set a + local path via |:lcd| or when {winnr} is -1 and the tabpage + has set a local path via |:tcd|, otherwise 0. Tabs and windows are identified by their respective numbers, 0 means current tab or window. Missing argument implies 0. @@ -5787,6 +5789,7 @@ haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* With {winnr} and {tabnr} use the window in that tabpage. {winnr} can be the window number or the |window-ID|. If {winnr} is -1 it is ignored, only the tab is resolved. + Throw error if the arguments are invalid. |E5000| |E5001| |E5002| Can also be used as a |method|: > GetWinnr()->haslocaldir() diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index ac797de92d..80ac762792 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -61,6 +61,10 @@ Example config (in init.vim): > -- See `:help omnifunc` and `:help ins-completion` for more information. vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.vim.lsp.omnifunc') + -- Use LSP as the handler for formatexpr. + -- See `:help formatexpr` for more information. + vim.api.nvim_buf_set_option(0, 'formatexpr', 'v:lua.vim.lsp.formatexpr()') + -- For plugins with an `on_attach` callback, call them here. For example: -- require('completion').on_attach() end @@ -452,6 +456,22 @@ LspSignatureActiveParameter |vim.lsp.handlers.signature_help()|. ============================================================================== +EVENTS *lsp-events* + +LspProgressUpdate *LspProgressUpdate* + Upon receipt of a progress notification from the server. See + |vim.lsp.util.get_progress_messages()|. + +LspRequest *LspRequest* + After a change to the active set of pending LSP requests. See {requests} + in |vim.lsp.client|. + +Example: > + autocmd User LspProgressUpdate redrawstatus + autocmd User LspRequest redrawstatus +< + +============================================================================== Lua module: vim.lsp *lsp-core* buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* @@ -608,6 +628,11 @@ client() *vim.lsp.client* server. • {handlers} (table): The handlers used by the client as described in |lsp-handler|. + • {requests} (table): The current pending requests in flight + to the server. Entries are key-value pairs with the key + being the request ID while the value is a table with `type`, + `bufnr`, and `method` key-value pairs. `type` is either "pending" + for an active request, or "cancel" for a cancel request. • {config} (table): copy of the table that was passed by the user to |vim.lsp.start_client()|. • {server_capabilities} (table): Response from the server @@ -628,6 +653,10 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* flush({client}) *vim.lsp.flush()* TODO: Documentation + *vim.lsp.for_each_buffer_client()* +for_each_buffer_client({bufnr}, {fn}) + TODO: Documentation + get_active_clients() *vim.lsp.get_active_clients()* Gets all active clients. @@ -666,7 +695,7 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* {base} If findstart=0, text to match against Return: ~ - (number) Decided by`findstart`: + (number) Decided by {findstart}: • findstart=0: column where the completion starts, or -2 or -3 • findstart=1: list of matches (actually just calls diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index a6ccf9f35c..1e058874bd 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -393,6 +393,14 @@ where the args are converted to Lua values. The expression > is equivalent to the Lua chunk > return somemod.func(...) +In addition, functions of packages can be accessed like > + v:lua.require'mypack'.func(arg1, arg2) + v:lua.require'mypack.submod'.func(arg1, arg2) +Note: only single quote form without parens is allowed. Using +`require"mypack"` or `require('mypack')` as prefixes do NOT work (the latter +is still valid as a function call of itself, in case require returns a useful +value). + The `v:lua` prefix may be used to call Lua functions as |method|s. For example: > arg1->v:lua.somemod.func(arg2) @@ -409,7 +417,8 @@ For example consider the following Lua omnifunc handler: > end vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc') -Note: the module ("mymod" in the above example) must be a Lua global. +Note: the module ("mymod" in the above example) must either be a Lua global, +or use the require syntax as specified above to access it from a package. Note: `v:lua` without a call is not allowed in a Vimscript expression: |Funcref|s cannot represent Lua functions. The following are errors: > @@ -689,15 +698,14 @@ vim.diff({a}, {b}, {opts}) *vim.diff()* ------------------------------------------------------------------------------ VIM.MPACK *lua-mpack* -The *vim.mpack* module provides packing and unpacking of lua objects to -msgpack encoded strings. |vim.NIL| and |vim.empty_dict()| are supported. +The *vim.mpack* module provides encoding and decoding of Lua objects to and +from msgpack-encoded strings. Supports |vim.NIL| and |vim.empty_dict()|. -vim.mpack.pack({obj}) *vim.mpack.pack* - Packs a lua object {obj} and returns the msgpack representation as - a string +vim.mpack.encode({obj}) *vim.mpack.encode* + Encodes (or "packs") Lua object {obj} as msgpack in a Lua string. -vim.mpack.unpack({str}) *vim.mpack.unpack* - Unpacks the msgpack encoded {str} and returns a lua object +vim.mpack.decode({str}) *vim.mpack.decode* + Decodes (or "unpacks") the msgpack-encoded {str} to a Lua object. ------------------------------------------------------------------------------ VIM *lua-builtin* @@ -928,6 +936,7 @@ Example: > vim.g.foo = 5 -- Set the g:foo Vimscript variable. print(vim.g.foo) -- Get and print the g:foo Vimscript variable. vim.g.foo = nil -- Delete (:unlet) the Vimscript variable. + vim.b[2].foo = 6 -- Set b:foo for buffer 2 vim.g *vim.g* Global (|g:|) editor variables. @@ -935,15 +944,18 @@ vim.g *vim.g* vim.b *vim.b* Buffer-scoped (|b:|) variables for the current buffer. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific buffer. vim.w *vim.w* Window-scoped (|w:|) variables for the current window. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific window. vim.t *vim.t* Tabpage-scoped (|t:|) variables for the current tabpage. - Invalid or unset key returns `nil`. + Invalid or unset key returns `nil`. Can be indexed with + an integer to access variables for a specific tabpage. vim.v *vim.v* |v:| variables. @@ -1518,7 +1530,7 @@ tbl_flatten({t}) *vim.tbl_flatten()* Flattened copy of the given list-like table. See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + From https://github.com/premake/premake-core/blob/master/src/base/table.lua tbl_isempty({t}) *vim.tbl_isempty()* Checks if a table is empty. @@ -1554,7 +1566,7 @@ tbl_keys({t}) *vim.tbl_keys()* list of keys See also: ~ - Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua + From https://github.com/premake/premake-core/blob/master/src/base/table.lua tbl_map({func}, {t}) *vim.tbl_map()* Apply a function to all values of a table. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 64c0d96aed..90d4c4de93 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -877,6 +877,9 @@ Also note that the 'clipboard' option is temporarily emptied to avoid clobbering the `"*` or `"+` registers, if its value contains the item `unnamed` or `unnamedplus`. +The `mode()` function will return the state as it will be after applying the +operator. + ============================================================================== 2. Abbreviations *abbreviations* *Abbreviations* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 3520d40591..be397117b2 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1159,9 +1159,9 @@ A jump table for the options with a short description can be found at |Q_op|. 'cdpath' 'cd' string (default: equivalent to $CDPATH or ",,") global This is a list of directories which will be searched when using the - |:cd| and |:lcd| commands, provided that the directory being searched - for has a relative path, not an absolute part starting with "/", "./" - or "../", the 'cdpath' option is not used then. + |:cd|, |:tcd| and |:lcd| commands, provided that the directory being + searched for has a relative path, not an absolute part starting with + "/", "./" or "../", the 'cdpath' option is not used then. The 'cdpath' option's value has the same form and semantics as |'path'|. Also see |file-searching|. The default value is taken from $CDPATH, with a "," prepended to look diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 7e7bb7d62f..77bf1d29eb 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -180,7 +180,6 @@ Commands: |:match| can be invoked before highlight group is defined Events: - |DirChanged| can be triggered when switching to another window |Signal| |TabNewEntered| |TermClose| @@ -197,10 +196,6 @@ Functions: |stdpath()| |system()|, |systemlist()| can run {cmd} directly (without 'shell') |matchadd()| can be called before highlight group is defined - |getcwd()| and |haslocaldir()| may throw errors. *E5000* *E5001* *E5002* - |haslocaldir()|'s only possible return values are 0 and 1, it never returns 2. - `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global - working directory. Use `getcwd(-1, -1)` to get the global working directory. Highlight groups: |highlight-blend| controls blend level for a highlight group @@ -253,6 +248,8 @@ Variables: Nvim always builds with all features, in contrast to Vim which may have certain features removed/added at compile-time. |feature-compile| +Some Vim features were changed in Nvim, and vice versa. + If a Python interpreter is available on your `$PATH`, |:python| and |:python3| are always available and may be used simultaneously. See |provider-python|. @@ -428,6 +425,15 @@ Vimscript compatibility: `shell_error` does not alias to |v:shell_error| `this_session` does not alias to |v:this_session| +Working directory (Vim implemented some of these later than Nvim): +- |DirChanged| can be triggered when switching to another window. +- |getcwd()| and |haslocaldir()| may throw errors if the tab page or window + cannot be found. *E5000* *E5001* *E5002* +- |haslocaldir()| only checks for tab-local directory when -1 is passed as + window number, and its only possible returns values are 0 and 1. +- `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global + working directory. Use `getcwd(-1, -1)` to get the global working directory. + ============================================================================== 5. Missing legacy features *nvim-features-missing* diff --git a/runtime/ftplugin/changelog.vim b/runtime/ftplugin/changelog.vim index 257e9cd9d4..e9df63f8c9 100644 --- a/runtime/ftplugin/changelog.vim +++ b/runtime/ftplugin/changelog.vim @@ -2,7 +2,7 @@ " Language: generic Changelog file " Maintainer: Martin Florian <marfl@posteo.de> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2015-10-25 +" Latest Revision: 2021-10-17 " Variables: " g:changelog_timeformat (deprecated: use g:changelog_dateformat instead) - " description: the timeformat used in ChangeLog entries. @@ -55,7 +55,7 @@ if &filetype == 'changelog' elseif $EMAIL_ADDRESS != "" return $EMAIL_ADDRESS endif - + let login = s:login() return printf('%s <%s@%s>', s:name(login), login, s:hostname()) endfunction @@ -223,12 +223,6 @@ if &filetype == 'changelog' let &paste = save_paste endfunction - if exists(":NewChangelogEntry") != 2 - nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> - xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> - command! -nargs=0 NewChangelogEntry call s:new_changelog_entry('') - endif - let b:undo_ftplugin = "setl com< fo< et< ai<" setlocal comments= @@ -241,14 +235,26 @@ if &filetype == 'changelog' let b:undo_ftplugin .= " tw<" endif + if !exists("no_plugin_maps") && !exists("no_changelog_maps") && exists(":NewChangelogEntry") != 2 + nnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> + xnoremap <buffer> <silent> <Leader>o :<C-u>call <SID>new_changelog_entry('')<CR> + command! -buffer -nargs=0 NewChangelogEntry call s:new_changelog_entry('') + let b:undo_ftplugin .= " | sil! exe 'nunmap <buffer> <Leader>o'" . + \ " | sil! exe 'vunmap <buffer> <Leader>o'" . + \ " | sil! delc NewChangelogEntry" + endif + let &cpo = s:cpo_save unlet s:cpo_save else let s:cpo_save = &cpo set cpo&vim - " Add the Changelog opening mapping - nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR> + if !exists("no_plugin_maps") && !exists("no_changelog_maps") + " Add the Changelog opening mapping + nnoremap <silent> <Leader>o :call <SID>open_changelog()<CR> + let b:undo_ftplugin .= " | silent! exe 'nunmap <buffer> <Leader>o" + endif function! s:open_changelog() let path = expand('%:p:h') diff --git a/runtime/ftplugin/nsis.vim b/runtime/ftplugin/nsis.vim index 1a35127c86..3dc0d6318a 100644 --- a/runtime/ftplugin/nsis.vim +++ b/runtime/ftplugin/nsis.vim @@ -3,7 +3,7 @@ " Maintainer: Ken Takata " URL: https://github.com/k-takata/vim-nsis " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Last Change: 2018-01-26 +" Last Change: 2021-10-18 if exists("b:did_ftplugin") finish @@ -15,7 +15,6 @@ set cpo&vim let b:did_ftplugin = 1 let b:undo_ftplugin = "setl com< cms< fo< def< inc<" - \ " | unlet! b:match_ignorecase b:match_words" setlocal comments=s1:/*,mb:*,ex:*/,b:#,:; commentstring=;\ %s setlocal formatoptions-=t formatoptions+=croql @@ -37,6 +36,7 @@ if exists("loaded_matchit") \ '\${MementoSection}:\${MementoSectionEnd},' . \ '!if\%(\%(macro\)\?n\?def\)\?\>:!else\>:!endif\>,' . \ '!macro\>:!macroend\>' + let b:undo_ftplugin .= " | unlet! b:match_ignorecase b:match_words" endif let &cpo = s:cpo_save diff --git a/runtime/indent/dosbatch.vim b/runtime/indent/dosbatch.vim index aea2a184d4..d24b139242 100644 --- a/runtime/indent/dosbatch.vim +++ b/runtime/indent/dosbatch.vim @@ -2,7 +2,7 @@ " Language: MSDOS batch file (with NT command extensions) " Maintainer: Ken Takata " URL: https://github.com/k-takata/vim-dosbatch-indent -" Last Change: 2017 May 10 +" Last Change: 2021-10-18 " Filenames: *.bat " License: VIM License @@ -17,6 +17,8 @@ setlocal indentexpr=GetDosBatchIndent(v:lnum) setlocal indentkeys=!^F,o,O setlocal indentkeys+=0=) +let b:undo_indent = "setl ai< inde< indk< si<" + if exists("*GetDosBatchIndent") finish endif diff --git a/runtime/indent/nsis.vim b/runtime/indent/nsis.vim index 223f4fa28e..5d3decca37 100644 --- a/runtime/indent/nsis.vim +++ b/runtime/indent/nsis.vim @@ -2,7 +2,7 @@ " Language: NSIS script " Maintainer: Ken Takata " URL: https://github.com/k-takata/vim-nsis -" Last Change: 2018-01-21 +" Last Change: 2021-10-18 " Filenames: *.nsi " License: VIM License @@ -17,6 +17,8 @@ setlocal indentexpr=GetNsisIndent(v:lnum) setlocal indentkeys=!^F,o,O setlocal indentkeys+==~${Else,=~${EndIf,=~${EndUnless,=~${AndIf,=~${AndUnless,=~${OrIf,=~${OrUnless,=~${Case,=~${Default,=~${EndSelect,=~${EndSwith,=~${Loop,=~${Next,=~${MementoSectionEnd,=~FunctionEnd,=~SectionEnd,=~SectionGroupEnd,=~PageExEnd,0=~!macroend,0=~!if,0=~!else,0=~!endif +let b:undo_indent = "setl ai< inde< indk< si<" + if exists("*GetNsisIndent") finish endif diff --git a/runtime/indent/teraterm.vim b/runtime/indent/teraterm.vim index 35d7354290..181c9a343f 100644 --- a/runtime/indent/teraterm.vim +++ b/runtime/indent/teraterm.vim @@ -3,7 +3,7 @@ " Based on Tera Term Version 4.100 " Maintainer: Ken Takata " URL: https://github.com/k-takata/vim-teraterm -" Last Change: 2018-08-31 +" Last Change: 2021-10-18 " Filenames: *.ttl " License: VIM License @@ -18,6 +18,8 @@ setlocal indentexpr=GetTeraTermIndent(v:lnum) setlocal indentkeys=!^F,o,O,e setlocal indentkeys+==elseif,=endif,=loop,=next,=enduntil,=endwhile +let b:undo_indent = "setl ai< inde< indk< si<" + if exists("*GetTeraTermIndent") finish endif diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 326932d982..19cad3cec8 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -19,10 +19,21 @@ local global_diagnostic_options = { signs = true, underline = true, virtual_text = true, + float = true, update_in_insert = false, severity_sort = false, } +M.handlers = setmetatable({}, { + __newindex = function(t, name, handler) + vim.validate { handler = {handler, "t" } } + rawset(t, name, handler) + if not global_diagnostic_options[name] then + global_diagnostic_options[name] = true + end + end, +}) + -- Local functions {{{ ---@private @@ -96,31 +107,9 @@ end local all_namespaces = {} ---@private -local function get_namespace(ns) - if not all_namespaces[ns] then - local name - for k, v in pairs(vim.api.nvim_get_namespaces()) do - if ns == v then - name = k - break - end - end - - assert(name, "namespace does not exist or is anonymous") - - all_namespaces[ns] = { - name = name, - sign_group = string.format("vim.diagnostic.%s", name), - opts = {} - } - end - return all_namespaces[ns] -end - ----@private local function enabled_value(option, namespace) - local ns = get_namespace(namespace) - if type(ns.opts[option]) == "table" then + local ns = namespace and M.get_namespace(namespace) or {} + if ns.opts and type(ns.opts[option]) == "table" then return ns.opts[option] end @@ -153,8 +142,9 @@ end ---@private local function get_resolved_options(opts, namespace, bufnr) - local ns = get_namespace(namespace) - local resolved = vim.tbl_extend('keep', opts or {}, ns.opts, global_diagnostic_options) + local ns = namespace and M.get_namespace(namespace) or {} + -- Do not use tbl_deep_extend so that an empty table can be used to reset to default values + local resolved = vim.tbl_extend('keep', opts or {}, ns.opts or {}, global_diagnostic_options) for k in pairs(global_diagnostic_options) do if resolved[k] ~= nil then resolved[k] = resolve_optional_value(k, resolved[k], namespace, bufnr) @@ -341,7 +331,7 @@ local registered_autocmds = {} ---@private local function make_augroup_key(namespace, bufnr) - local ns = get_namespace(namespace) + local ns = M.get_namespace(namespace) return string.format("DiagnosticInsertLeave:%s:%s", bufnr, ns.name) end @@ -354,19 +344,15 @@ local function schedule_display(namespace, bufnr, args) local key = make_augroup_key(namespace, bufnr) if not registered_autocmds[key] then - vim.cmd(string.format("augroup %s", key)) - vim.cmd(" au!") - vim.cmd( - string.format( - [[autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s)]], - table.concat(insert_leave_auto_cmds, ","), - bufnr, - namespace, - bufnr - ) - ) - vim.cmd("augroup END") - + vim.cmd(string.format([[augroup %s + au! + autocmd %s <buffer=%s> lua vim.diagnostic._execute_scheduled_display(%s, %s) + augroup END]], + key, + table.concat(insert_leave_auto_cmds, ","), + bufnr, + namespace, + bufnr)) registered_autocmds[key] = true end end @@ -376,77 +362,14 @@ local function clear_scheduled_display(namespace, bufnr) local key = make_augroup_key(namespace, bufnr) if registered_autocmds[key] then - vim.cmd(string.format("augroup %s", key)) - vim.cmd(" au!") - vim.cmd("augroup END") - + vim.cmd(string.format([[augroup %s + au! + augroup END]], key)) registered_autocmds[key] = nil end end ---@private ---- Open a floating window with the provided diagnostics ----@param opts table Configuration table ---- - show_header (boolean, default true): Show "Diagnostics:" header ---- - all opts for |vim.util.open_floating_preview()| can be used here ----@param diagnostics table: The diagnostics to display ----@return table {popup_bufnr, win_id} -local function show_diagnostics(opts, diagnostics) - if not diagnostics or vim.tbl_isempty(diagnostics) then - return - end - local lines = {} - local highlights = {} - local show_header = vim.F.if_nil(opts.show_header, true) - if show_header then - table.insert(lines, "Diagnostics:") - table.insert(highlights, {0, "Bold"}) - end - - if opts.format then - diagnostics = reformat_diagnostics(opts.format, diagnostics) - end - - if opts.source then - diagnostics = prefix_source(opts.source, diagnostics) - end - - -- Use global setting for severity_sort since 'show_diagnostics' is namespace - -- independent - local severity_sort = global_diagnostic_options.severity_sort - if severity_sort then - if type(severity_sort) == "table" and severity_sort.reverse then - table.sort(diagnostics, function(a, b) return a.severity > b.severity end) - else - table.sort(diagnostics, function(a, b) return a.severity < b.severity end) - end - end - - for i, diagnostic in ipairs(diagnostics) do - local prefix = string.format("%d. ", i) - local hiname = floating_highlight_map[diagnostic.severity] - assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity)) - - local message_lines = vim.split(diagnostic.message, '\n', true) - table.insert(lines, prefix..message_lines[1]) - table.insert(highlights, {#prefix, hiname}) - for j = 2, #message_lines do - table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) - table.insert(highlights, {0, hiname}) - end - end - - local popup_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) - for i, hi in ipairs(highlights) do - local prefixlen, hiname = unpack(hi) - -- Start highlight after the prefix - vim.api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1) - end - - return popup_bufnr, winnr -end - ----@private local function set_list(loclist, opts) opts = opts or {} local open = vim.F.if_nil(opts.open, true) @@ -469,6 +392,7 @@ local function set_list(loclist, opts) end ---@private +--- To (slightly) improve performance, modifies diagnostics in place. local function clamp_line_numbers(bufnr, diagnostics) local buf_line_count = vim.api.nvim_buf_line_count(bufnr) if buf_line_count == 0 then @@ -526,7 +450,7 @@ end local function diagnostic_move_pos(opts, pos) opts = opts or {} - local enable_popup = vim.F.if_nil(opts.enable_popup, true) + local float = vim.F.if_nil(opts.float, true) local win_id = opts.win_id or vim.api.nvim_get_current_win() if not pos then @@ -539,10 +463,13 @@ local function diagnostic_move_pos(opts, pos) vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) - if enable_popup then - -- This is a bit weird... I'm surprised that we need to wait til the next tick to do this. + if float then + local float_opts = type(float) == "table" and float or {} vim.schedule(function() - M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) + M.open_float( + vim.api.nvim_win_get_buf(win_id), + vim.tbl_extend("keep", float_opts, {scope="cursor"}) + ) end) end end @@ -561,12 +488,12 @@ end --- --- For example, if a user enables virtual text globally with --- <pre> ---- vim.diagnostic.config({virt_text = true}) +--- vim.diagnostic.config({virtual_text = true}) --- </pre> --- --- and a diagnostic producer sets diagnostics with --- <pre> ---- vim.diagnostic.set(ns, 0, diagnostics, {virt_text = false}) +--- vim.diagnostic.set(ns, 0, diagnostics, {virtual_text = false}) --- </pre> --- --- then virtual text will not be enabled for those diagnostics. @@ -603,6 +530,13 @@ end --- * priority: (number, default 10) Base priority to use for signs. When --- {severity_sort} is used, the priority of a sign is adjusted based on --- its severity. Otherwise, all signs use the same priority. +--- - float: Options for floating windows: +--- * severity: See |diagnostic-severity|. +--- * show_header: (boolean, default true) Show "Diagnostics:" header +--- * source: (string) Include the diagnostic source in +--- the message. One of "always" or "if_many". +--- * format: (function) A function that takes a diagnostic as input and returns a +--- string. The return value is the text used to display the diagnostic. --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in @@ -620,7 +554,7 @@ function M.config(opts, namespace) local t if namespace then - local ns = get_namespace(namespace) + local ns = M.get_namespace(namespace) t = ns.opts else t = global_diagnostic_options @@ -687,6 +621,32 @@ function M.set(namespace, bufnr, diagnostics, opts) vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged") end +--- Get namespace metadata. +--- +---@param ns number Diagnostic namespace +---@return table Namespace metadata +function M.get_namespace(namespace) + vim.validate { namespace = { namespace, 'n' } } + if not all_namespaces[namespace] then + local name + for k, v in pairs(vim.api.nvim_get_namespaces()) do + if namespace == v then + name = k + break + end + end + + assert(name, "namespace does not exist or is anonymous") + + all_namespaces[namespace] = { + name = name, + opts = {}, + user_data = {}, + } + end + return all_namespaces[namespace] +end + --- Get current diagnostic namespaces. --- ---@return table A list of active diagnostic namespaces |vim.diagnostic|. @@ -825,10 +785,9 @@ end --- |nvim_win_get_cursor()|. Defaults to the current cursor position. --- - wrap: (boolean, default true) Whether to loop around file or not. Similar to 'wrapscan'. --- - severity: See |diagnostic-severity|. ---- - enable_popup: (boolean, default true) Call |vim.diagnostic.show_line_diagnostics()| ---- on jump. ---- - popup_opts: (table) Table to pass as {opts} parameter to ---- |vim.diagnostic.show_line_diagnostics()| +--- - float: (boolean or table, default true) If "true", call |vim.diagnostic.open_float()| +--- after moving. If a table, pass the table as the {opts} parameter to +--- |vim.diagnostic.open_float()|. --- - win_id: (number, default 0) Window ID function M.goto_next(opts) return diagnostic_move_pos( @@ -837,156 +796,167 @@ function M.goto_next(opts) ) end --- Diagnostic Setters {{{ - ---- Set signs for given diagnostics. ---- ----@param namespace number The diagnostic namespace ----@param bufnr number Buffer number ----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the ---- current diagnostics in the given buffer are used. ----@param opts table Configuration table with the following keys: ---- - priority: Set the priority of the signs |sign-priority|. ----@private -function M._set_signs(namespace, bufnr, diagnostics, opts) - vim.validate { - namespace = {namespace, 'n'}, - bufnr = {bufnr, 'n'}, - diagnostics = {diagnostics, 't'}, - opts = {opts, 't', true}, - } - - bufnr = get_bufnr(bufnr) - opts = get_resolved_options({ signs = opts }, namespace, bufnr) +M.handlers.signs = { + show = function(namespace, bufnr, diagnostics, opts) + vim.validate { + namespace = {namespace, 'n'}, + bufnr = {bufnr, 'n'}, + diagnostics = {diagnostics, 't'}, + opts = {opts, 't', true}, + } - if opts.signs and opts.signs.severity then - diagnostics = filter_by_severity(opts.signs.severity, diagnostics) - end + bufnr = get_bufnr(bufnr) - local ns = get_namespace(namespace) + if opts.signs and opts.signs.severity then + diagnostics = filter_by_severity(opts.signs.severity, diagnostics) + end - define_default_signs() + define_default_signs() - -- 10 is the default sign priority when none is explicitly specified - local priority = opts.signs and opts.signs.priority or 10 - local get_priority - if opts.severity_sort then - if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then - get_priority = function(severity) - return priority + (severity - vim.diagnostic.severity.ERROR) + -- 10 is the default sign priority when none is explicitly specified + local priority = opts.signs and opts.signs.priority or 10 + local get_priority + if opts.severity_sort then + if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then + get_priority = function(severity) + return priority + (severity - vim.diagnostic.severity.ERROR) + end + else + get_priority = function(severity) + return priority + (vim.diagnostic.severity.HINT - severity) + end end else - get_priority = function(severity) - return priority + (vim.diagnostic.severity.HINT - severity) + get_priority = function() + return priority end end - else - get_priority = function() - return priority - end - end - for _, diagnostic in ipairs(diagnostics) do - vim.fn.sign_place( - 0, - ns.sign_group, - sign_highlight_map[diagnostic.severity], - bufnr, - { - priority = get_priority(diagnostic.severity), - lnum = diagnostic.lnum + 1 - } - ) - end -end - ---- Set underline for given diagnostics. ---- ----@param namespace number The diagnostic namespace ----@param bufnr number Buffer number ----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the ---- current diagnostics in the given buffer are used. ----@param opts table Configuration table. Currently unused. ----@private -function M._set_underline(namespace, bufnr, diagnostics, opts) - vim.validate { - namespace = {namespace, 'n'}, - bufnr = {bufnr, 'n'}, - diagnostics = {diagnostics, 't'}, - opts = {opts, 't', true}, - } + local ns = M.get_namespace(namespace) + if not ns.user_data.sign_group then + ns.user_data.sign_group = string.format("vim.diagnostic.%s", ns.name) + end - bufnr = get_bufnr(bufnr) - opts = get_resolved_options({ underline = opts }, namespace, bufnr).underline + local sign_group = ns.user_data.sign_group + for _, diagnostic in ipairs(diagnostics) do + vim.fn.sign_place( + 0, + sign_group, + sign_highlight_map[diagnostic.severity], + bufnr, + { + priority = get_priority(diagnostic.severity), + lnum = diagnostic.lnum + 1 + } + ) + end + end, + hide = function(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.user_data.sign_group then + vim.fn.sign_unplace(ns.user_data.sign_group, {buffer=bufnr}) + end + end, +} - if opts and opts.severity then - diagnostics = filter_by_severity(opts.severity, diagnostics) - end +M.handlers.underline = { + show = function(namespace, bufnr, diagnostics, opts) + vim.validate { + namespace = {namespace, 'n'}, + bufnr = {bufnr, 'n'}, + diagnostics = {diagnostics, 't'}, + opts = {opts, 't', true}, + } - for _, diagnostic in ipairs(diagnostics) do - local higroup = underline_highlight_map[diagnostic.severity] + bufnr = get_bufnr(bufnr) - if higroup == nil then - -- Default to error if we don't have a highlight associated - higroup = underline_highlight_map.Error + if opts.underline and opts.underline.severity then + diagnostics = filter_by_severity(opts.underline.severity, diagnostics) end - vim.highlight.range( - bufnr, - namespace, - higroup, - { diagnostic.lnum, diagnostic.col }, - { diagnostic.end_lnum, diagnostic.end_col } - ) - end -end + local ns = M.get_namespace(namespace) + if not ns.user_data.underline_ns then + ns.user_data.underline_ns = vim.api.nvim_create_namespace("") + end ---- Set virtual text for given diagnostics. ---- ----@param namespace number The diagnostic namespace ----@param bufnr number Buffer number ----@param diagnostics table A list of diagnostic items |diagnostic-structure|. When omitted the ---- current diagnostics in the given buffer are used. ----@param opts table|nil Configuration table with the following keys: ---- - prefix: (string) Prefix to display before virtual text on line. ---- - spacing: (number) Number of spaces to insert before virtual text. ---- - source: (string) Include the diagnostic source in virtual text. One of "always" or ---- "if_many". ----@private -function M._set_virtual_text(namespace, bufnr, diagnostics, opts) - vim.validate { - namespace = {namespace, 'n'}, - bufnr = {bufnr, 'n'}, - diagnostics = {diagnostics, 't'}, - opts = {opts, 't', true}, - } + local underline_ns = ns.user_data.underline_ns + for _, diagnostic in ipairs(diagnostics) do + local higroup = underline_highlight_map[diagnostic.severity] - bufnr = get_bufnr(bufnr) - opts = get_resolved_options({ virtual_text = opts }, namespace, bufnr).virtual_text + if higroup == nil then + -- Default to error if we don't have a highlight associated + higroup = underline_highlight_map.Error + end - if opts and opts.format then - diagnostics = reformat_diagnostics(opts.format, diagnostics) + vim.highlight.range( + bufnr, + underline_ns, + higroup, + { diagnostic.lnum, diagnostic.col }, + { diagnostic.end_lnum, diagnostic.end_col } + ) + end + end, + hide = function(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.user_data.underline_ns then + vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.underline_ns, 0, -1) + end end +} - if opts and opts.source then - diagnostics = prefix_source(opts.source, diagnostics) - end +M.handlers.virtual_text = { + show = function(namespace, bufnr, diagnostics, opts) + vim.validate { + namespace = {namespace, 'n'}, + bufnr = {bufnr, 'n'}, + diagnostics = {diagnostics, 't'}, + opts = {opts, 't', true}, + } + + bufnr = get_bufnr(bufnr) - local buffer_line_diagnostics = diagnostic_lines(diagnostics) - for line, line_diagnostics in pairs(buffer_line_diagnostics) do - if opts and opts.severity then - line_diagnostics = filter_by_severity(opts.severity, line_diagnostics) + local severity + if opts.virtual_text then + if opts.virtual_text.format then + diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) + end + if opts.virtual_text.source then + diagnostics = prefix_source(opts.virtual_text.source, diagnostics) + end + if opts.virtual_text.severity then + severity = opts.virtual_text.severity + end end - local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) - if virt_texts then - vim.api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { - hl_mode = "combine", - virt_text = virt_texts, - }) + local ns = M.get_namespace(namespace) + if not ns.user_data.virt_text_ns then + ns.user_data.virt_text_ns = vim.api.nvim_create_namespace("") end - end -end + + local virt_text_ns = ns.user_data.virt_text_ns + local buffer_line_diagnostics = diagnostic_lines(diagnostics) + for line, line_diagnostics in pairs(buffer_line_diagnostics) do + if severity then + line_diagnostics = filter_by_severity(severity, line_diagnostics) + end + local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text) + + if virt_texts then + vim.api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, { + hl_mode = "combine", + virt_text = virt_texts, + }) + end + end + end, + hide = function(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.user_data.virt_text_ns then + vim.api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) + end + end, +} --- Get virtual text chunks to display using |nvim_buf_set_extmark()|. --- @@ -1066,19 +1036,16 @@ function M.hide(namespace, bufnr) bufnr = get_bufnr(bufnr) diagnostic_cache_extmarks[bufnr][namespace] = {} - local ns = get_namespace(namespace) - - -- clear sign group - vim.fn.sign_unplace(ns.sign_group, {buffer=bufnr}) - - -- clear virtual text namespace - vim.api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + for _, handler in pairs(M.handlers) do + if handler.hide then + handler.hide(namespace, bufnr) + end + end end - --- Display diagnostics for the given namespace and buffer. --- ----@param namespace number Diagnostic namespace +---@param namespace number Diagnostic namespace. ---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param diagnostics table|nil The diagnostics to display. When omitted, use the --- saved diagnostics for the given namespace and @@ -1129,83 +1096,145 @@ function M.show(namespace, bufnr, diagnostics, opts) clamp_line_numbers(bufnr, diagnostics) - if opts.underline then - M._set_underline(namespace, bufnr, diagnostics, opts.underline) - end - - if opts.virtual_text then - M._set_virtual_text(namespace, bufnr, diagnostics, opts.virtual_text) - end - - if opts.signs then - M._set_signs(namespace, bufnr, diagnostics, opts.signs) + for handler_name, handler in pairs(M.handlers) do + if handler.show and opts[handler_name] then + handler.show(namespace, bufnr, diagnostics, opts) + end end save_extmarks(namespace, bufnr) end ---- Open a floating window with the diagnostics at the given position. +--- Show diagnostics in a floating window. --- +---@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param opts table|nil Configuration table with the same keys as --- |vim.lsp.util.open_floating_preview()| in addition to the following: --- - namespace: (number) Limit diagnostics to the given namespace ---- - severity: See |diagnostic-severity|. ---- - show_header: (boolean, default true) Show "Diagnostics:" header ---- - source: (string) Include the diagnostic source in ---- the message. One of "always" or "if_many". +--- - scope: (string, default "buffer") Show diagnostics from the whole buffer ("buffer"), +--- the current cursor line ("line"), or the current cursor position ("cursor"). +--- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather +--- than the cursor position. If a number, interpreted as a line number; +--- otherwise, a (row, col) tuple. +--- - severity_sort: (default false) Sort diagnostics by severity. Overrides the setting +--- from |vim.diagnostic.config()|. +--- - severity: See |diagnostic-severity|. Overrides the setting from +--- |vim.diagnostic.config()|. +--- - show_header: (boolean, default true) Show "Diagnostics:" header. Overrides the +--- setting from |vim.diagnostic.config()|. +--- - source: (string) Include the diagnostic source in the message. One of "always" or +--- "if_many". Overrides the setting from |vim.diagnostic.config()|. --- - format: (function) A function that takes a diagnostic as input and returns a --- string. The return value is the text used to display the diagnostic. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ----@param position table|nil The (0,0)-indexed position. Defaults to the current cursor position. ----@return tuple ({popup_bufnr}, {win_id}) -function M.show_position_diagnostics(opts, bufnr, position) +--- Overrides the setting from |vim.diagnostic.config()|. +---@return tuple ({float_bufnr}, {win_id}) +function M.open_float(bufnr, opts) vim.validate { - opts = { opts, 't', true }, bufnr = { bufnr, 'n', true }, - position = { position, 't', true }, + opts = { opts, 't', true }, } opts = opts or {} - - opts.focus_id = "position_diagnostics" bufnr = get_bufnr(bufnr) - if not position then - local curr_position = vim.api.nvim_win_get_cursor(0) - curr_position[1] = curr_position[1] - 1 - position = curr_position - end - local match_position_predicate = function(diag) - return position[1] == diag.lnum and - position[2] >= diag.col and - (position[2] <= diag.end_col or position[1] < diag.end_lnum) + local scope = opts.scope or "buffer" + local lnum, col + if scope == "line" or scope == "cursor" then + if not opts.pos then + local pos = vim.api.nvim_win_get_cursor(0) + lnum = pos[1] - 1 + col = pos[2] + elseif type(opts.pos) == "number" then + lnum = opts.pos + elseif type(opts.pos) == "table" then + lnum, col = unpack(opts.pos) + else + error("Invalid value for option 'pos'") + end + elseif scope ~= "buffer" then + error("Invalid value for option 'scope'") end + local diagnostics = M.get(bufnr, opts) clamp_line_numbers(bufnr, diagnostics) - local position_diagnostics = vim.tbl_filter(match_position_predicate, diagnostics) - return show_diagnostics(opts, position_diagnostics) -end ---- Open a floating window with the diagnostics from the given line. ---- ----@param opts table Configuration table. See |vim.diagnostic.show_position_diagnostics()|. ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ----@param lnum number|nil Line number. Defaults to line number of cursor. ----@return tuple ({popup_bufnr}, {win_id}) -function M.show_line_diagnostics(opts, bufnr, lnum) - vim.validate { - opts = { opts, 't', true }, - bufnr = { bufnr, 'n', true }, - lnum = { lnum, 'n', true }, - } + if scope == "line" then + diagnostics = vim.tbl_filter(function(d) + return d.lnum == lnum + end, diagnostics) + elseif scope == "cursor" then + -- LSP servers can send diagnostics with `end_col` past the length of the line + local line_length = #vim.api.nvim_buf_get_lines(bufnr, lnum, lnum + 1, true)[1] + diagnostics = vim.tbl_filter(function(d) + return d.lnum == lnum + and math.min(d.col, line_length - 1) <= col + and (d.end_col >= col or d.end_lnum > lnum) + end, diagnostics) + end - opts = opts or {} - opts.focus_id = "line_diagnostics" - bufnr = get_bufnr(bufnr) - local diagnostics = M.get(bufnr, opts) - clamp_line_numbers(bufnr, diagnostics) - lnum = lnum or (vim.api.nvim_win_get_cursor(0)[1] - 1) - local line_diagnostics = diagnostic_lines(diagnostics)[lnum] - return show_diagnostics(opts, line_diagnostics) + if vim.tbl_isempty(diagnostics) then + return + end + + local severity_sort = vim.F.if_nil(opts.severity_sort, global_diagnostic_options.severity_sort) + if severity_sort then + if type(severity_sort) == "table" and severity_sort.reverse then + table.sort(diagnostics, function(a, b) return a.severity > b.severity end) + else + table.sort(diagnostics, function(a, b) return a.severity < b.severity end) + end + end + + do + -- Resolve options with user settings from vim.diagnostic.config + -- Unlike the other decoration functions (e.g. set_virtual_text, set_signs, etc.) `open_float` + -- does not have a dedicated table for configuration options; instead, the options are mixed in + -- with its `opts` table which also includes "keyword" parameters. So we create a dedicated + -- options table that inherits missing keys from the global configuration before resolving. + local t = global_diagnostic_options.float + local float_opts = vim.tbl_extend("keep", opts, type(t) == "table" and t or {}) + opts = get_resolved_options({ float = float_opts }, nil, bufnr).float + end + + local lines = {} + local highlights = {} + local show_header = vim.F.if_nil(opts.show_header, true) + if show_header then + table.insert(lines, "Diagnostics:") + table.insert(highlights, {0, "Bold"}) + end + + if opts.format then + diagnostics = reformat_diagnostics(opts.format, diagnostics) + end + + if opts.source then + diagnostics = prefix_source(opts.source, diagnostics) + end + + for i, diagnostic in ipairs(diagnostics) do + local prefix = string.format("%d. ", i) + local hiname = floating_highlight_map[diagnostic.severity] + local message_lines = vim.split(diagnostic.message, '\n') + table.insert(lines, prefix..message_lines[1]) + table.insert(highlights, {#prefix, hiname}) + for j = 2, #message_lines do + table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) + table.insert(highlights, {0, hiname}) + end + end + + -- Used by open_floating_preview to allow the float to be focused + if not opts.focus_id then + opts.focus_id = scope + end + local float_bufnr, winnr = require('vim.lsp.util').open_floating_preview(lines, 'plaintext', opts) + for i, hi in ipairs(highlights) do + local prefixlen, hiname = unpack(hi) + -- Start highlight after the prefix + vim.api.nvim_buf_add_highlight(float_bufnr, -1, hiname, i-1, prefixlen, -1) + end + + return float_bufnr, winnr end --- Remove all diagnostics from the given namespace. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 9c35351608..fb4718c1bb 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -122,9 +122,6 @@ local active_clients = {} local all_buffer_active_clients = {} local uninitialized_clients = {} --- Tracks all buffers attached to a client. -local all_client_active_buffers = {} - ---@private --- Invokes a function for each LSP client attached to the buffer {bufnr}. --- @@ -242,6 +239,7 @@ local function validate_client_config(config) on_exit = { config.on_exit, "f", true }; on_init = { config.on_init, "f", true }; settings = { config.settings, "t", true }; + commands = { config.commands, 't', true }; before_init = { config.before_init, "f", true }; offset_encoding = { config.offset_encoding, "s", true }; flags = { config.flags, "t", true }; @@ -593,6 +591,11 @@ end --- returned to the language server if requested via `workspace/configuration`. --- Keys are case-sensitive. --- +---@param commands table Table that maps string of clientside commands to user-defined functions. +--- Commands passed to start_client take precedence over the global command registry. Each key +--- must be a unique comand name, and the value is a function which is called if any LSP action +--- (code action, code lenses, ...) triggers the command. +--- ---@param init_options Values to pass in the initialization request --- as `initializationOptions`. See `initialize` in the LSP spec. --- @@ -647,7 +650,9 @@ end --- - debounce_text_changes (number, default nil): Debounce didChange --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil ---- +--- - exit_timeout (number, default 500): Milliseconds to wait for server to +-- exit cleanly after sending the 'shutdown' request before sending kill -15. +-- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. @@ -742,7 +747,6 @@ function lsp.start_client(config) lsp.diagnostic.reset(client_id, all_buffer_active_clients) changetracking.reset(client_id) - all_client_active_buffers[client_id] = nil for _, client_ids in pairs(all_buffer_active_clients) do client_ids[client_id] = nil end @@ -771,10 +775,14 @@ function lsp.start_client(config) rpc = rpc; offset_encoding = offset_encoding; config = config; + attached_buffers = {}; handlers = handlers; + commands = config.commands or {}; + + requests = {}; -- for $/progress report - messages = { name = name, messages = {}, progress = {}, status = {} } + messages = { name = name, messages = {}, progress = {}, status = {} }; } -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. @@ -907,11 +915,21 @@ function lsp.start_client(config) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client) - + bufnr = resolve_bufnr(bufnr) local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr) - return rpc.request(method, params, function(err, result) + local success, request_id = rpc.request(method, params, function(err, result) handler(err, result, {method=method, client_id=client_id, bufnr=bufnr, params=params}) + end, function(request_id) + client.requests[request_id] = nil + nvim_command("doautocmd <nomodeline> User LspRequest") end) + + if success then + client.requests[request_id] = { type='pending', bufnr=bufnr, method=method } + nvim_command("doautocmd <nomodeline> User LspRequest") + end + + return success, request_id end ---@private @@ -971,6 +989,11 @@ function lsp.start_client(config) ---@see |vim.lsp.client.notify()| function client.cancel_request(id) validate{id = {id, 'n'}} + local request = client.requests[id] + if request and request.type == 'pending' then + request.type = 'cancel' + nvim_command("doautocmd <nomodeline> User LspRequest") + end return rpc.notify("$/cancelRequest", { id = id }) end @@ -989,7 +1012,6 @@ function lsp.start_client(config) lsp.diagnostic.reset(client_id, all_buffer_active_clients) changetracking.reset(client_id) - all_client_active_buffers[client_id] = nil for _, client_ids in pairs(all_buffer_active_clients) do client_ids[client_id] = nil end @@ -1032,6 +1054,7 @@ function lsp.start_client(config) -- TODO(ashkan) handle errors. pcall(config.on_attach, client, bufnr) end + client.attached_buffers[bufnr] = true end initialize() @@ -1142,12 +1165,6 @@ function lsp.buf_attach_client(bufnr, client_id) }) end - if not all_client_active_buffers[client_id] then - all_client_active_buffers[client_id] = {} - end - - table.insert(all_client_active_buffers[client_id], bufnr) - if buffer_client_ids[client_id] then return end -- This is our first time attaching this client to this buffer. buffer_client_ids[client_id] = true @@ -1172,7 +1189,7 @@ end --- Gets a client by id, or nil if the id is invalid. --- The returned client may not yet be fully initialized. -- ----@param client_id client id number +---@param client_id number client id --- ---@returns |vim.lsp.client| object, or nil function lsp.get_client_by_id(client_id) @@ -1181,15 +1198,11 @@ end --- Returns list of buffers attached to client_id. -- ----@param client_id client id +---@param client_id number client id ---@returns list of buffer ids function lsp.get_buffers_by_client_id(client_id) - local active_client_buffers = all_client_active_buffers[client_id] - if active_client_buffers then - return active_client_buffers - else - return {} - end + local client = lsp.get_client_by_id(client_id) + return client and vim.tbl_keys(client.attached_buffers) or {} end --- Stops a client(s). @@ -1239,9 +1252,41 @@ function lsp._vim_exit_handler() client.stop() end - if not vim.wait(500, function() return tbl_isempty(active_clients) end, 50) then - for _, client in pairs(active_clients) do - client.stop(true) + local timeouts = {} + local max_timeout = 0 + local send_kill = false + + for client_id, client in pairs(active_clients) do + local timeout = if_nil(client.config.flags.exit_timeout, 500) + if timeout then + send_kill = true + timeouts[client_id] = timeout + max_timeout = math.max(timeout, max_timeout) + end + end + + local poll_time = 50 + + local function check_clients_closed() + for client_id, timeout in pairs(timeouts) do + timeouts[client_id] = timeout - poll_time + end + + for client_id, _ in pairs(active_clients) do + if timeouts[client_id] ~= nil and timeouts[client_id] > 0 then + return false + end + end + return true + end + + if send_kill then + if not vim.wait(max_timeout, check_clients_closed, poll_time) then + for client_id, client in pairs(active_clients) do + if timeouts[client_id] ~= nil then + client.stop(true) + end + end end end end @@ -1282,7 +1327,7 @@ function lsp.buf_request(bufnr, method, params, handler) if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR) vim.api.nvim_command("redraw") - return + return {}, function() end end local client_request_ids = {} @@ -1430,7 +1475,7 @@ end ---@param findstart 0 or 1, decides behavior ---@param base If findstart=0, text to match against --- ----@returns (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) @@ -1494,6 +1539,52 @@ function lsp.omnifunc(findstart, base) return -2 end +--- Provides an interface between the built-in client and a `formatexpr` function. +--- +--- Currently only supports a single client. This can be set via +--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` but will typically or in `on_attach` +--- via `vim.api.nvim_buf_set_option(bufnr, 'formatexpr', 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})')`. +--- +---@param opts table options for customizing the formatting expression which takes the +--- following optional keys: +--- * timeout_ms (default 500ms). The timeout period for the formatting request. +function lsp.formatexpr(opts) + opts = opts or {} + local timeout_ms = opts.timeout_ms or 500 + + if vim.tbl_contains({'i', 'R', 'ic', 'ix'}, vim.fn.mode()) then + -- `formatexpr` is also called when exceeding `textwidth` in insert mode + -- fall back to internal formatting + return 1 + end + + local start_line = vim.v.lnum + local end_line = start_line + vim.v.count - 1 + + if start_line > 0 and end_line > 0 then + local params = { + textDocument = util.make_text_document_params(); + range = { + start = { line = start_line - 1; character = 0; }; + ["end"] = { line = end_line - 1; character = 0; }; + }; + }; + params.options = util.make_formatting_params().options + local client_results = vim.lsp.buf_request_sync(0, "textDocument/rangeFormatting", params, timeout_ms) + + -- Apply the text edits from one and only one of the clients. + for _, response in pairs(client_results) do + if response.result then + vim.lsp.util.apply_text_edits(response.result, 0) + return 0 + end + end + end + + -- do not run builtin formatter. + return 0 +end + ---Checks whether a client is stopped. --- ---@param client_id (Number) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 245f29943e..128f0b01ad 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -480,11 +480,11 @@ local function on_code_action_results(results, ctx) end if action.command then local command = type(action.command) == 'table' and action.command or action - local fn = vim.lsp.commands[command.command] + local fn = client.commands[command.command] or vim.lsp.commands[command.command] if fn then local enriched_ctx = vim.deepcopy(ctx) enriched_ctx.client_id = client.id - fn(command, ctx) + fn(command, enriched_ctx) else M.execute_command(command) end @@ -529,6 +529,7 @@ local function on_code_action_results(results, ctx) vim.ui.select(action_tuples, { prompt = 'Code actions:', + kind = 'codeaction', format_item = function(action_tuple) local title = action_tuple[2].title:gsub('\r\n', '\\r\\n') return title:gsub('\n', '\\n') diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 20b203fe99..9eb64c9a2e 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -31,15 +31,15 @@ local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + local client = vim.lsp.get_client_by_id(client_id) + assert(client, 'Client is required to execute lens, client_id=' .. client_id) local command = lens.command - local fn = vim.lsp.commands[command.command] + local fn = client.commands[command.command] or vim.lsp.commands[command.command] if fn then fn(command, { bufnr = bufnr, client_id = client_id }) return end -- Need to use the client that returned the lens → must not use buf_request - local client = vim.lsp.get_client_by_id(client_id) - assert(client, 'Client is required to execute lens, client_id=' .. client_id) local command_provider = client.server_capabilities.executeCommandProvider local commands = type(command_provider) == 'table' and command_provider.commands or {} if not vim.tbl_contains(commands, command.command) then @@ -91,16 +91,16 @@ function M.run() local option = options[1] execute_lens(option.lens, bufnr, option.client) else - local options_strings = {"Code lenses:"} - for i, option in ipairs(options) do - table.insert(options_strings, string.format('%d. %s', i, option.lens.command.title)) - end - local choice = vim.fn.inputlist(options_strings) - if choice < 1 or choice > #options then - return - end - local option = options[choice] - execute_lens(option.lens, bufnr, option.client) + vim.ui.select(options, { + prompt = 'Code lenses:', + format_item = function(option) + return option.lens.command.title + end, + }, function(option) + if option then + execute_lens(option.lens, bufnr, option.client) + end + end) end end @@ -138,7 +138,8 @@ function M.display(lenses, bufnr, client_id) end end if #chunks > 0 then - api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks }) + api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks, + hl_mode="combine" }) end end end @@ -199,7 +200,8 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) ns, lens.range.start.line, 0, - { virt_text = {{ lens.command.title, 'LspCodeLens' }} } + { virt_text = {{ lens.command.title, 'LspCodeLens' }}, + hl_mode="combine" } ) end countdown() diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index c6c08a15d3..6b856a52a5 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -146,7 +146,8 @@ local _client_namespaces = {} function M.get_namespace(client_id) vim.validate { client_id = { client_id, 'n' } } if not _client_namespaces[client_id] then - local name = string.format("vim.lsp.client-%d", client_id) + local client = vim.lsp.get_client_by_id(client_id) + local name = string.format("vim.lsp.%s.%d", client.name, client_id) _client_namespaces[client_id] = vim.api.nvim_create_namespace(name) end return _client_namespaces[client_id] @@ -211,9 +212,14 @@ function M.on_publish_diagnostics(_, result, ctx, config) end end end + + -- Persist configuration to ensure buffer reloads use the same + -- configuration. To make lsp.with configuration work (See :help + -- lsp-handler-configuration) + vim.diagnostic.config(config, namespace) end - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id), config) + vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) -- Keep old autocmd for back compat. This should eventually be removed. vim.api.nvim_command("doautocmd <nomodeline> User LspDiagnosticsChanged") @@ -546,14 +552,15 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) - if opts then - if opts.severity then - opts.severity = severity_lsp_to_vim(opts.severity) - elseif opts.severity_limit then - opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} - end + opts = opts or {} + opts.scope = "cursor" + opts.pos = position + if opts.severity then + opts.severity = severity_lsp_to_vim(opts.severity) + elseif opts.severity_limit then + opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} end - return vim.diagnostic.show_position_diagnostics(opts, buf_nr, position) + return vim.diagnostic.open_float(buf_nr, opts) end --- Open a floating window with the diagnostics from {line_nr} @@ -568,11 +575,13 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) + opts = opts or {} + opts.scope = "line" + opts.pos = line_nr if client_id then - opts = opts or {} opts.namespace = M.get_namespace(client_id) end - return vim.diagnostic.show_line_diagnostics(opts, buf_nr, line_nr) + return vim.diagnostic.open_float(buf_nr, opts) end --- Redraw diagnostics for the given buffer and client diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index eff27807be..01d7102e8f 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -185,7 +185,7 @@ local function response_to_list(map_result, entity) title = 'Language Server'; items = map_result(result, ctx.bufnr); }) - api.nvim_command("copen") + api.nvim_command("botright copen") end end end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index d9a684a738..bce1e9f35d 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -297,6 +297,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local message_index = 0 local message_callbacks = {} + local notify_reply_callbacks = {} local handle, pid do @@ -309,8 +310,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) stdout:close() stderr:close() handle:close() - -- Make sure that message_callbacks can be gc'd. + -- Make sure that message_callbacks/notify_reply_callbacks can be gc'd. message_callbacks = nil + notify_reply_callbacks = nil dispatchers.on_exit(code, signal) end local spawn_params = { @@ -375,10 +377,12 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@param method (string) The invoked LSP method ---@param params (table) Parameters for the invoked LSP method ---@param callback (function) Callback to invoke + ---@param notify_reply_callback (function) Callback to invoke as soon as a request is no longer pending ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not - local function request(method, params, callback) + local function request(method, params, callback, notify_reply_callback) validate { callback = { callback, 'f' }; + notify_reply_callback = { notify_reply_callback, 'f', true }; } message_index = message_index + 1 local message_id = message_index @@ -388,8 +392,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) method = method; params = params; } - if result and message_callbacks then - message_callbacks[message_id] = schedule_wrap(callback) + if result then + if message_callbacks then + message_callbacks[message_id] = schedule_wrap(callback) + else + return false + end + if notify_reply_callback and notify_reply_callbacks then + notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback) + end return result, message_id else return false @@ -466,6 +477,16 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- We sent a number, so we expect a number. local result_id = tonumber(decoded.id) + -- Notify the user that a response was received for the request + local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] + if notify_reply_callback then + validate { + notify_reply_callback = { notify_reply_callback, 'f' }; + } + notify_reply_callback(result_id) + notify_reply_callbacks[result_id] = nil + end + -- Do not surface RequestCancelled to users, it is RPC-internal. if decoded.error then local mute_error = false diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 3751f94caf..a5bf0efcb1 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -152,6 +152,7 @@ end --- Returns a zero-indexed column, since set_lines() does the conversion to --- 1-indexed local function get_line_byte_from_position(bufnr, position) + -- TODO handle offset_encoding -- LSP's line and characters are 0-indexed -- Vim's line and columns are 1-indexed local col = position.character @@ -165,7 +166,7 @@ local function get_line_byte_from_position(bufnr, position) local line = position.line local lines = api.nvim_buf_get_lines(bufnr, line, line + 1, false) if #lines > 0 then - local ok, result = pcall(vim.str_byteindex, lines[1], col) + local ok, result = pcall(vim.str_byteindex, lines[1], col, true) if ok then return result @@ -226,9 +227,10 @@ function M.get_progress_messages() table.remove(client.messages, item.idx) end - for _, item in ipairs(progress_remove) do - client.messages.progress[item.token] = nil - end + end + + for _, item in ipairs(progress_remove) do + item.client.messages.progress[item.token] = nil end return new_messages @@ -275,7 +277,8 @@ function M.apply_text_edits(text_edits, bufnr) -- Some LSP servers may return +1 range of the buffer content but nvim_buf_set_text can't accept it so we should fix it here. local has_eol_text_edit = false local max = vim.api.nvim_buf_line_count(bufnr) - local len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') + -- TODO handle offset_encoding + local _, len = vim.str_utfindex(vim.api.nvim_buf_get_lines(bufnr, -2, -1, false)[1] or '') text_edits = vim.tbl_map(function(text_edit) if max <= text_edit.range.start.line then text_edit.range.start.line = max - 1 @@ -335,10 +338,12 @@ function M.apply_text_edits(text_edits, bufnr) end if is_cursor_fixed then - vim.api.nvim_win_set_cursor(0, { - cursor.row + 1, - math.min(cursor.col, #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or '')) - }) + local is_valid_cursor = true + is_valid_cursor = is_valid_cursor and cursor.row < vim.api.nvim_buf_line_count(bufnr) + is_valid_cursor = is_valid_cursor and cursor.col <= #(vim.api.nvim_buf_get_lines(bufnr, cursor.row, cursor.row + 1, false)[1] or '') + if is_valid_cursor then + vim.api.nvim_win_set_cursor(0, { cursor.row + 1, cursor.col }) + end end -- Remove final line if needed @@ -1717,7 +1722,9 @@ function M.symbols_to_items(symbols, bufnr) }) if symbol.children then for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do - vim.list_extend(_items, v) + for _, s in ipairs(v) do + table.insert(_items, s) + end end end end @@ -1785,7 +1792,9 @@ local function make_position_param() if not line then return { line = 0; character = 0; } end - col = str_utfindex(line, col) + -- TODO handle offset_encoding + local _ + _, col = str_utfindex(line, col) return { line = row; character = col; } end @@ -1835,11 +1844,14 @@ function M.make_given_range_params(start_pos, end_pos) A[1] = A[1] - 1 B[1] = B[1] - 1 -- account for encoding. + -- TODO handle offset_encoding if A[2] > 0 then - A = {A[1], M.character_offset(0, A[1], A[2])} + local _, char = M.character_offset(0, A[1], A[2]) + A = {A[1], char} end if B[2] > 0 then - B = {B[1], M.character_offset(0, B[1], B[2])} + local _, char = M.character_offset(0, B[1], B[2]) + B = {B[1], char} end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index 5eab20fc54..adc1e16759 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -9,6 +9,11 @@ local M = {} --- - format_item (function item -> text) --- Function to format an --- individual item from `items`. Defaults to `tostring`. +--- - kind (string|nil) +--- Arbitrary hint string indicating the item shape. +--- Plugins reimplementing `vim.ui.select` may wish to +--- use this to infer the structure or semantics of +--- `items`, or the context in which select() was called. ---@param on_choice function ((item|nil, idx|nil) -> ()) --- Called once the user made a choice. --- `idx` is the 1-based index of `item` within `item`. diff --git a/runtime/syntax/arduino.vim b/runtime/syntax/arduino.vim index 4a4ef82072..1c9618ff0f 100644 --- a/runtime/syntax/arduino.vim +++ b/runtime/syntax/arduino.vim @@ -1,50 +1,79 @@ " Vim syntax file " Language: Arduino " Maintainer: Johannes Hoff <johannes@johanneshoff.com> -" Last Change: 2011 June 3 +" Last Change: 21 October 2021 " License: VIM license (:help license, replace vim by arduino.vim) " Syntax highlighting like in the Arduino IDE -" Keywords extracted from <arduino>/build/shared/lib/keywords.txt (arduino -" version 0021) +" Automatically generated by the script available at +" https://bitbucket.org/johannes/arduino-vim-syntax +" Using keywords from <arduino>/build/shared/lib/keywords.txt +" From version: 1.8.16 -" Thanks to Rik, Erik Nomitch, Adam Obeng and Graeme Cross for helpful feedback! +" Thanks to Rik, Erik Nomitch, Adam Obeng, Graeme Cross and Niall Parker +" for helpful feedback! -" quit when a syntax file was already loaded -if exists("b:current_syntax") +" For version 5.x: Clear all syntax items +" For version 6.x: Quit when a syntax file was already loaded +if version < 600 + syntax clear +elseif exists("b:current_syntax") finish endif " Read the C syntax to start with -runtime! syntax/cpp.vim - -syn keyword arduinoConstant HIGH LOW INPUT OUTPUT -syn keyword arduinoConstant DEC BIN HEX OCT BYTE -syn keyword arduinoConstant PI HALF_PI TWO_PI -syn keyword arduinoConstant LSBFIRST MSBFIRST -syn keyword arduinoConstant CHANGE FALLING RISING -syn keyword arduinoConstant SERIAL DISPLAY -syn keyword arduinoConstant DEFAULT EXTERNAL INTERNAL INTERNAL1V1 INTERNAL2V56 - -syn keyword arduinoStdFunc abs acos asin atan atan2 ceil constrain -syn keyword arduinoStdFunc cos degrees exp floor log -syn keyword arduinoStdFunc map max min pow radians -syn keyword arduinoStdFunc round sin sq sqrt tan -syn keyword arduinoStdFunc randomSeed random - -syn keyword arduinoFunc analogReference analogRead analogWrite -syn keyword arduinoFunc attachInterrupt detachInterrupt interrupts noInterrupts -syn keyword arduinoFunc lowByte highByte bitRead bitWrite bitSet bitClear -syn keyword arduinoFunc millis micros delay delayMicroseconds -syn keyword arduinoFunc pinMode digitalWrite digitalRead -syn keyword arduinoFunc tone noTone pulseIn shiftOut - -syn keyword arduinoMethod setup loop -syn keyword arduinoMethod begin end available read flush print println write peek - -syn keyword arduinoType boolean byte word String - -syn keyword arduinoModule Serial Serial1 Serial2 Serial3 +if version < 600 + so <sfile>:p:h/cpp.vim +else + runtime! syntax/cpp.vim +endif + +syn keyword arduinoConstant BIN CHANGE DEC DEFAULT EXTERNAL FALLING HALF_PI HEX +syn keyword arduinoConstant HIGH INPUT INPUT_PULLUP INTERNAL INTERNAL1V1 +syn keyword arduinoConstant INTERNAL2V56 LED_BUILTIN LED_BUILTIN_RX +syn keyword arduinoConstant LED_BUILTIN_TX LOW LSBFIRST MSBFIRST OCT OUTPUT PI +syn keyword arduinoConstant RISING TWO_PI + +syn keyword arduinoFunc analogRead analogReadResolution analogReference +syn keyword arduinoFunc analogWrite analogWriteResolution attachInterrupt +syn keyword arduinoFunc bit bitClear bitRead bitSet bitWrite delay +syn keyword arduinoFunc delayMicroseconds detachInterrupt +syn keyword arduinoFunc digitalPinToInterrupt digitalRead digitalWrite +syn keyword arduinoFunc highByte interrupts lowByte micros millis +syn keyword arduinoFunc noInterrupts noTone pinMode pulseIn pulseInLong +syn keyword arduinoFunc shiftIn shiftOut tone yield + +syn keyword arduinoMethod available availableForWrite begin charAt compareTo +syn keyword arduinoMethod concat end endsWith equals equalsIgnoreCase export +syn keyword arduinoMethod final find findUntil flush getBytes indexOf +syn keyword arduinoMethod lastIndexOf length loop override parseFloat +syn keyword arduinoMethod parseInt peek print println read readBytes +syn keyword arduinoMethod readBytesUntil readString readStringUntil replace +syn keyword arduinoMethod setCharAt setTimeout setup startsWith Stream +syn keyword arduinoMethod substring toCharArray toInt toLowerCase toUpperCase +syn keyword arduinoMethod trim + +syn keyword arduinoModule Keyboard Mouse Serial Serial1 Serial2 Serial3 +syn keyword arduinoModule SerialUSB + +syn keyword arduinoStdFunc abs accept acos acosf asin asinf atan atan2 atan2f +syn keyword arduinoStdFunc atanf cbrt cbrtf ceil ceilf click constrain +syn keyword arduinoStdFunc copysign copysignf cos cosf cosh coshf degrees exp +syn keyword arduinoStdFunc expf fabs fabsf fdim fdimf floor floorf fma fmaf +syn keyword arduinoStdFunc fmax fmaxf fmin fminf fmod fmodf hypot hypotf +syn keyword arduinoStdFunc isfinite isinf isnan isPressed ldexp ldexpf log +syn keyword arduinoStdFunc log10 log10f logf lrint lrintf lround lroundf map +syn keyword arduinoStdFunc max min move pow powf press radians random +syn keyword arduinoStdFunc randomSeed release releaseAll round roundf signbit +syn keyword arduinoStdFunc sin sinf sinh sinhf sq sqrt sqrtf tan tanf tanh +syn keyword arduinoStdFunc tanhf trunc truncf + +syn keyword arduinoType _Bool _Complex _Imaginary array atomic_bool +syn keyword arduinoType atomic_char atomic_int atomic_llong atomic_long +syn keyword arduinoType atomic_schar atomic_short atomic_uchar atomic_uint +syn keyword arduinoType atomic_ullong atomic_ulong atomic_ushort boolean +syn keyword arduinoType byte char16_t char32_t complex NULL null PROGMEM +syn keyword arduinoType String word hi def link arduinoType Type hi def link arduinoConstant Constant diff --git a/runtime/syntax/debchangelog.vim b/runtime/syntax/debchangelog.vim index 93b03ae06d..79352c0827 100644 --- a/runtime/syntax/debchangelog.vim +++ b/runtime/syntax/debchangelog.vim @@ -3,7 +3,7 @@ " Maintainer: Debian Vim Maintainers " Former Maintainers: Gerfried Fuchs <alfie@ist.org> " Wichert Akkerman <wakkerma@debian.org> -" Last Change: 2021 Aug 03 +" Last Change: 2021 Oct 19 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debchangelog.vim " Standard syntax initialization @@ -24,7 +24,8 @@ let s:supported = [ \ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm', \ 'trixie', 'sid', 'rc-buggy', \ - \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'devel' + \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy', + \ 'devel' \ ] let s:unsupported = [ \ 'frozen', 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', diff --git a/runtime/syntax/debsources.vim b/runtime/syntax/debsources.vim index 8aa96fcb58..4b4c497f18 100644 --- a/runtime/syntax/debsources.vim +++ b/runtime/syntax/debsources.vim @@ -2,7 +2,7 @@ " Language: Debian sources.list " Maintainer: Debian Vim Maintainers " Former Maintainer: Matthijs Mohlmann <matthijs@cacholong.nl> -" Last Change: 2021 Aug 03 +" Last Change: 2021 Oct 19 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/master/syntax/debsources.vim " Standard syntax initialization @@ -26,7 +26,8 @@ let s:supported = [ \ 'jessie', 'stretch', 'buster', 'bullseye', 'bookworm', \ 'trixie', 'sid', 'rc-buggy', \ - \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'devel' + \ 'trusty', 'xenial', 'bionic', 'focal', 'hirsute', 'impish', 'jammy', + \ 'devel' \ ] let s:unsupported = [ \ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', diff --git a/runtime/syntax/nsis.vim b/runtime/syntax/nsis.vim index 3389771b78..3a73fe0989 100644 --- a/runtime/syntax/nsis.vim +++ b/runtime/syntax/nsis.vim @@ -1,9 +1,9 @@ " Vim syntax file -" Language: NSIS script, for version of NSIS 3.03 and later +" Language: NSIS script, for version of NSIS 3.08 and later " Maintainer: Ken Takata " URL: https://github.com/k-takata/vim-nsis " Previous Maintainer: Alex Jakushev <Alex.Jakushev@kemek.lt> -" Last Change: 2018-10-02 +" Last Change: 2020-10-18 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -97,6 +97,8 @@ syn match nsisSysVar "$RESOURCES_LOCALIZED" syn match nsisSysVar "$CDBURN_AREA" syn match nsisSysVar "$HWNDPARENT" syn match nsisSysVar "$PLUGINSDIR" +syn match nsisSysVar "$\%(USERTEMPLATES\|USERSTARTMENU\|USERSMPROGRAMS\|USERDESKTOP\)" +syn match nsisSysVar "$\%(COMMONTEMPLATES\|COMMONSTARTMENU\|COMMONSMPROGRAMS\|COMMONDESKTOP\|COMMONPROGRAMDATA\)" syn match nsisSysVar "$\\r" syn match nsisSysVar "$\\n" syn match nsisSysVar "$\\t" @@ -149,7 +151,7 @@ syn keyword nsisStatement contained Section nextgroup=nsisSectionOpt skipwhite syn region nsisSectionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSectionKwd syn match nsisSectionKwd contained "/o\>" -syn keyword nsisStatement contained SectionIn nextgroup=nsisSectionInOpt skipwhite +syn keyword nsisStatement contained SectionInstType SectionIn nextgroup=nsisSectionInOpt skipwhite syn region nsisSectionInOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSectionInKwd syn keyword nsisSectionInKwd contained RO @@ -269,10 +271,22 @@ syn keyword nsisAttribute contained ManifestDPIAware nextgroup=nsisManifestDPIAw syn region nsisManifestDPIAwareOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestDPIAwareKwd syn keyword nsisManifestDPIAwareKwd contained notset true false +syn keyword nsisAttribute contained ManifestLongPathAware nextgroup=nsisManifestLongPathAwareOpt skipwhite +syn region nsisManifestLongPathAwareOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestLongPathAwareKwd +syn match nsisManifestLongPathAwareKwd contained "\<\%(notset\|true\|false\)\>" + syn keyword nsisAttribute contained ManifestSupportedOS nextgroup=nsisManifestSupportedOSOpt skipwhite syn region nsisManifestSupportedOSOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisManifestSupportedOSKwd syn match nsisManifestSupportedOSKwd contained "\<\%(none\|all\|WinVista\|Win7\|Win8\|Win8\.1\|Win10\)\>" +syn keyword nsisAttribute contained PEAddResource nextgroup=nsisPEAddResourceOpt skipwhite +syn region nsisPEAddResourceOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPEAddResourceKwd +syn match nsisPEAddResourceKwd contained "/\%(OVERWRITE\|REPLACE\)\>" + +syn keyword nsisAttribute contained PERemoveResource nextgroup=nsisPERemoveResourceOpt skipwhite +syn region nsisPERemoveResourceOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisPERemoveResourceKwd +syn match nsisPERemoveResourceKwd contained "/NOERRORS\>" + syn keyword nsisAttribute contained RequestExecutionLevel nextgroup=nsisRequestExecutionLevelOpt skipwhite syn region nsisRequestExecutionLevelOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisRequestExecutionLevelKwd syn keyword nsisRequestExecutionLevelKwd contained none user highest admin @@ -353,7 +367,7 @@ syn keyword nsisInstruction contained ExpandEnvStrings ReadEnvStr syn keyword nsisInstruction contained DeleteRegKey nextgroup=nsisDeleteRegKeyOpt skipwhite syn region nsisDeleteRegKeyOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisDeleteRegKeyKwd,nsisRegistry -syn match nsisDeleteRegKeyKwd contained "/ifempty\>" +syn match nsisDeleteRegKeyKwd contained "/\%(ifempty\|ifnosubkeys\|ifnovalues\)\>" syn keyword nsisInstruction contained nextgroup=nsisRegistryOpt skipwhite \ DeleteRegValue EnumRegKey EnumRegValue ReadRegDWORD ReadRegStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr @@ -368,8 +382,8 @@ syn region nsisSetRegViewOpt contained start="" end="$" transparent keepend cont syn keyword nsisSetRegViewKwd contained default lastused "FUNCTIONS - general purpose (4.9.3) -syn keyword nsisInstruction contained CallInstDLL CreateDirectory GetDLLVersion -syn keyword nsisInstruction contained GetDLLVersionLocal GetFileTime GetFileTimeLocal +syn keyword nsisInstruction contained CallInstDLL CreateDirectory GetWinVer +syn keyword nsisInstruction contained GetFileTime GetFileTimeLocal GetKnownFolderPath syn keyword nsisInstruction contained GetTempFileName SearchPath RegDLL UnRegDLL syn keyword nsisInstruction contained CopyFiles nextgroup=nsisCopyFilesOpt skipwhite @@ -380,6 +394,10 @@ syn keyword nsisInstruction contained CreateShortcut nextgroup=nsisCreateShortcu syn region nsisCreateShortcutOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisCreateShortcutKwd syn match nsisCreateShortcutKwd contained "/NoWorkingDir\>" +syn keyword nsisInstruction contained GetDLLVersion GetDLLVersionLocal nextgroup=nsisGetDLLVersionOpt skipwhite +syn region nsisGetDLLVersionOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetDLLVersionKwd +syn match nsisGetDLLVersionKwd contained "/ProductVersion\>" + syn keyword nsisInstruction contained GetFullPathName nextgroup=nsisGetFullPathNameOpt skipwhite syn region nsisGetFullPathNameOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisGetFullPathNameKwd syn match nsisGetFullPathNameKwd contained "/SHORT\>" @@ -395,6 +413,7 @@ syn keyword nsisFileAttrib contained FILE_ATTRIBUTE_TEMPORARY syn keyword nsisInstruction contained Abort Call ClearErrors GetCurrentAddress syn keyword nsisInstruction contained GetFunctionAddress GetLabelAddress Goto syn keyword nsisInstruction contained IfAbort IfErrors IfFileExists IfRebootFlag IfSilent +syn keyword nsisInstruction contained IfShellVarContextAll IfRtlLanguage syn keyword nsisInstruction contained IntCmp IntCmpU Int64Cmp Int64CmpU IntPtrCmp IntPtrCmpU syn keyword nsisInstruction contained Return Quit SetErrors StrCmp StrCmpS @@ -460,6 +479,10 @@ syn keyword nsisInstruction contained CreateFont nextgroup=nsisFontOpt skipwhite syn keyword nsisInstruction contained nextgroup=nsisBooleanOpt skipwhite \ LockWindow SetAutoClose +syn keyword nsisInstruction contained LoadAndSetImage nextgroup=nsisLoadAndSetImageOpt skipwhite +syn region nsisLoadAndSetImageOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisLoadAndSetImageKwd +syn match nsisLoadAndSetImageKwd contained "/\%(EXERESOURCE\|STRINGID\|RESIZETOFIT\%(WIDTH\|HEIGHT\)\)\>" + syn keyword nsisInstruction contained SendMessage nextgroup=nsisSendMessageOpt skipwhite syn region nsisSendMessageOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisSendMessageKwd syn match nsisSendMessageKwd contained "/TIMEOUT\>" @@ -556,7 +579,7 @@ syn keyword nsisVerboseKwd contained push pop "PREPROCESSOR (5.4) syn match nsisDefine contained "!define\>" nextgroup=nsisDefineOpt skipwhite syn region nsisDefineOpt contained start="" end="$" transparent keepend contains=@nsisAnyOpt,nsisDefineKwd -syn match nsisDefineKwd contained "/\%(ifndef\|redef\|date\|utcdate\|math\|file\)\>" +syn match nsisDefineKwd contained "/\%(ifndef\|redef\|date\|utcdate\|file\|intfmt\|math\)\>" syn match nsisDefine contained "!undef\>" syn match nsisPreCondit contained "!ifdef\>" @@ -615,7 +638,10 @@ hi def link nsisInstTypeKwd Constant hi def link nsisLicenseBkColorKwd Constant hi def link nsisLicenseForceSelectionKwd Constant hi def link nsisManifestDPIAwareKwd Constant +hi def link nsisManifestLongPathAwareKwd Constant hi def link nsisManifestSupportedOSKwd Constant +hi def link nsisPEAddResourceKwd Constant +hi def link nsisPERemoveResourceKwd Constant hi def link nsisRequestExecutionLevelKwd Constant hi def link nsisShowInstDetailsKwd Constant hi def link nsisSilentInstallKwd Constant @@ -633,11 +659,13 @@ hi def link nsisWriteRegMultiStrKwd Constant hi def link nsisSetRegViewKwd Constant hi def link nsisCopyFilesKwd Constant hi def link nsisCreateShortcutKwd Constant +hi def link nsisGetDLLVersionKwd Constant hi def link nsisGetFullPathNameKwd Constant hi def link nsisFileAttrib Constant hi def link nsisMessageBox Constant hi def link nsisFileWriteUTF16LEKwd Constant hi def link nsisSetShellVarContextKwd Constant +hi def link nsisLoadAndSetImageKwd Constant hi def link nsisSendMessageKwd Constant hi def link nsisSetBrandingImageKwd Constant hi def link nsisSetDetailsViewKwd Constant |