diff options
35 files changed, 770 insertions, 544 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 6ffb514487..4b0da06971 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -3873,8 +3873,7 @@ json_decode({expr}) *json_decode()* Vim value. In the following cases it will output |msgpack-special-dict|: 1. Dictionary contains duplicate key. - 2. Dictionary contains empty key. - 3. String contains NUL byte. Two special dictionaries: for + 2. String contains NUL byte. Two special dictionaries: for dictionary and for string will be emitted in case string with NUL byte was a dictionary key. @@ -4954,7 +4953,6 @@ msgpackparse({data}) *msgpackparse()* are binary strings). 2. String with NUL byte inside. 3. Duplicate key. - 4. Empty key. ext |List| with two values: first is a signed integer representing extension type. Second is |readfile()|-style list of strings. diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index ed21dc1c37..115fc18cc5 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -530,8 +530,9 @@ FORTRAN *ft-fortran-plugin* Options: 'expandtab' is switched on to avoid tabs as required by the Fortran standards unless the user has set fortran_have_tabs in vimrc. -'textwidth' is set to 72 for fixed source format as required by the - Fortran standards and to 80 for free source format. +'textwidth' is set to 80 for fixed source format whereas it is set to 132 + for free source format. Setting the fortran_extended_line_length + variable increases the width to 132 for fixed source format. 'formatoptions' is set to break code and comment lines and to preserve long lines. You can format comments with |gq|. For further discussion of fortran_have_tabs and the method used for the diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index 853facdaa0..1cd5c7b5f5 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -711,26 +711,25 @@ FORTRAN *ft-fortran-indent* Block if, select case, where, and forall constructs are indented. So are type, interface, associate, block, and enum constructs. The indenting of subroutines, functions, modules, and program blocks is optional. Comments, -labelled statements and continuation lines are indented if the Fortran is in +labeled statements, and continuation lines are indented if the Fortran is in free source form, whereas they are not indented if the Fortran is in fixed source form because of the left margin requirements. Hence manual indent -corrections will be necessary for labelled statements and continuation lines +corrections will be necessary for labeled statements and continuation lines when fixed source form is being used. For further discussion of the method used for the detection of source format see |ft-fortran-syntax|. Do loops ~ All do loops are left unindented by default. Do loops can be unstructured in -Fortran with (possibly multiple) loops ending on a labelled executable +Fortran with (possibly multiple) loops ending on a labeled executable statement of almost arbitrary type. Correct indentation requires -compiler-quality parsing. Old code with do loops ending on labelled statements -of arbitrary type can be indented with elaborate programs such as Tidy -(https://www.unb.ca/chem/ajit/f_tidy.htm). Structured do/continue loops are -also left unindented because continue statements are also used for purposes -other than ending a do loop. Programs such as Tidy can convert structured -do/continue loops to the do/enddo form. Do loops of the do/enddo variety can -be indented. If you use only structured loops of the do/enddo form, you should -declare this by setting the fortran_do_enddo variable in your vimrc as -follows > +compiler-quality parsing. Old code with do loops ending on labeled statements +of arbitrary type can be indented with elaborate programs such as Tidy. +Structured do/continue loops are also left unindented because continue +statements are used for purposes other than ending a do loop. Programs such +as Tidy can convert structured do/continue loops to the do/enddo form. Do +loops of the do/enddo variety can be indented. If you use only structured +loops of the do/enddo form, you should declare this by setting the +fortran_do_enddo variable in your vimrc as follows > let fortran_do_enddo=1 @@ -744,15 +743,14 @@ to get do loops indented in .f90 files and left alone in Fortran files with other extensions such as .for. Program units ~ -The indenting of program units (subroutines, functions, modules, and program -blocks) is enabled by default but can be suppressed if a lighter, screen-width -preserving indent style is desired. To suppress the indenting of program -units for all fortran files set the global fortran_indent_less variable in -your vimrc as follows > +Indenting of program units (subroutines, functions, modules, and program +blocks) can be increased by setting the variable fortran_indent_more and can +be decreased by setting the variable fortran_indent_less. These variables +can be set for all fortran files in your vimrc as follows > let fortran_indent_less=1 -A finer level of suppression can be achieved by setting the corresponding +A finer level of control can be achieved by setting the corresponding buffer-local variable as follows > let b:fortran_indent_less=1 diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index e38707fa76..3006287e62 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -98,6 +98,9 @@ The following changes may require adaptations in user config or plugins. • Default color scheme has been updated to be "Neovim branded" and accessible. Use `:colorscheme vim` to revert to the old legacy color scheme. +• 'termguicolors' is enabled by default when Nvim is able to determine that + the host terminal emulator supports 24-bit color. + ============================================================================== BREAKING CHANGES IN HEAD *news-breaking-dev* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index f47093782c..355c8cc99a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6495,6 +6495,10 @@ A jump table for the options with a short description can be found at |Q_op|. attributes instead of "cterm" attributes. |guifg| Requires an ISO-8613-3 compatible terminal. + Nvim will automatically attempt to determine if the host terminal + supports 24-bit color and will enable this option if it does + (unless explicitly disabled by the user). + *'termpastefilter'* *'tpf'* 'termpastefilter' 'tpf' string (default "BS,HT,ESC,DEL") global diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index e1053b54f1..1d02e49071 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1554,7 +1554,15 @@ FORTRAN *fortran.vim* *ft-fortran-syntax* Default highlighting and dialect ~ Highlighting appropriate for Fortran 2008 is used by default. This choice should be appropriate for most users most of the time because Fortran 2008 is -almost a superset of previous versions (Fortran 2003, 95, 90, and 77). +almost a superset of previous versions (Fortran 2003, 95, 90, and 77). A +small number of features new to Fortran 2018 and Fortran 2023 are supported +and the complete transition to Fortran 2023 will be completed in the future. +A few legacy constructs deleted or declared obsolescent in recent Fortran +standards are highlighted as todo items. + +The syntax script no longer supports Fortran dialects. The variable +fortran_dialect is now silently ignored. Since computers are much faster now, +the variable fortran_more_precise is no longer needed and is silently ignored. Fortran source code form ~ Fortran code can be in either fixed or free source form. Note that the @@ -1563,10 +1571,8 @@ syntax highlighting will not be correct if the form is incorrectly set. When you create a new fortran file, the syntax script assumes fixed source form. If you always use free source form, then > :let fortran_free_source=1 -in your vimrc prior to the :syntax on command. If you always use fixed -source form, then > +If you always use fixed source form, then > :let fortran_fixed_source=1 -in your vimrc prior to the :syntax on command. If the form of the source code depends, in a non-standard way, upon the file extension, then it is most convenient to set fortran_free_source in a ftplugin @@ -1582,14 +1588,33 @@ neither of these variables have been set, the syntax script attempts to determine which source form has been used by examining the file extension using conventions common to the ifort, gfortran, Cray, NAG, and PathScale compilers (.f, .for, .f77 for fixed-source, .f90, .f95, .f03, .f08 for -free-source). If none of this works, then the script examines the first five -columns of the first 500 lines of your file. If no signs of free source form -are detected, then the file is assumed to be in fixed source form. The -algorithm should work in the vast majority of cases. In some cases, such as a -file that begins with 500 or more full-line comments, the script may -incorrectly decide that the fortran code is in fixed form. If that happens, -just add a non-comment statement beginning anywhere in the first five columns -of the first twenty-five lines, save (:w) and then reload (:e!) the file. +free-source). No default is used for the .fpp and .ftn file extensions because +different compilers treat them differently. If none of this works, then the +script examines the first five columns of the first 500 lines of your file. If +no signs of free source form are detected, then the file is assumed to be in +fixed source form. The algorithm should work in the vast majority of cases. +In some cases, such as a file that begins with 500 or more full-line comments, +the script may incorrectly decide that the code is in fixed form. If that +happens, just add a non-comment statement beginning anywhere in the first five +columns of the first twenty-five lines, save (:w), and then reload (:e!) the +file. + +Vendor extensions ~ +Fixed-form Fortran requires a maximum line length of 72 characters but the +script allows a maximum line length of 80 characters as do all compilers +created in the last three decades. An even longer line length of 132 +characters is allowed if you set the variable fortran_extended_line_length +with a command such as > + :let fortran_extended_line_length=1 + +If you want additional highlighting of the CUDA Fortran extensions, you should +set the variable fortran_CUDA with a command such as > + :let fortran_CUDA=1 + +To activate recognition of some common, non-standard, vendor-supplied +intrinsics, you should set the variable fortran_vendor_intrinsics with a +command such as > + :let fortran_vendor_intrinsics=1 Tabs in fortran files ~ Tabs are not recognized by the Fortran standards. Tabs are not a good idea in @@ -1598,8 +1623,8 @@ Therefore, tabs are marked as errors. Nevertheless, some programmers like using tabs. If your fortran files contain tabs, then you should set the variable fortran_have_tabs in your vimrc with a command such as > :let fortran_have_tabs=1 -placed prior to the :syntax on command. Unfortunately, the use of tabs will -mean that the syntax file will not be able to detect incorrect margins. +Unfortunately, the use of tabs will mean that the syntax file will not be able +to detect incorrect margins. Syntax folding of fortran files ~ If you wish to use foldmethod=syntax, then you must first set the variable @@ -1611,8 +1636,8 @@ subprograms, block data subprograms, interface blocks, and modules. If you also set the variable fortran_fold_conditionals with a command such as > :let fortran_fold_conditionals=1 then fold regions will also be defined for do loops, if blocks, and select -case constructs. If you also set the variable -fortran_fold_multilinecomments with a command such as > +case constructs. If you also set the variable fortran_fold_multilinecomments +with a command such as > :let fortran_fold_multilinecomments=1 then fold regions will also be defined for three or more consecutive comment lines. Note that defining fold regions can be slow for large files. @@ -1623,58 +1648,6 @@ you set foldmethod=syntax. Comments or blank lines placed between two program units are not folded because they are seen as not belonging to any program unit. -More precise fortran syntax ~ -If you set the variable fortran_more_precise with a command such as > - :let fortran_more_precise=1 -then the syntax coloring will be more precise but slower. In particular, -statement labels used in do, goto and arithmetic if statements will be -recognized, as will construct names at the end of a do, if, select or forall -construct. - -Non-default fortran dialects ~ -The syntax script supports two Fortran dialects: f08 and F. You will probably -find the default highlighting (f08) satisfactory. A few legacy constructs -deleted or declared obsolescent in the 2008 standard are highlighted as todo -items. - -If you use F, the advantage of setting the dialect appropriately is that -other legacy features excluded from F will be highlighted as todo items and -that free source form will be assumed. - -The dialect can be selected in various ways. If all your fortran files use -the same dialect, set the global variable fortran_dialect in your vimrc prior -to your syntax on statement. The case-sensitive, permissible values of -fortran_dialect are "f08" or "F". Invalid values of fortran_dialect are -ignored. - -If the dialect depends upon the file extension, then it is most convenient to -set a buffer-local variable in a ftplugin file. For more information on -ftplugin files, see |ftplugin|. For example, if all your fortran files with -an .f90 extension are written in the F subset, your ftplugin file should -contain the code > - let s:extfname = expand("%:e") - if s:extfname ==? "f90" - let b:fortran_dialect="F" - else - unlet! b:fortran_dialect - endif -Note that this will work only if the "filetype plugin indent on" command -precedes the "syntax on" command in your vimrc file. - -Finer control is necessary if the file extension does not uniquely identify -the dialect. You can override the default dialect, on a file-by-file basis, -by including a comment with the directive "fortran_dialect=xx" (where xx=F or -f08) in one of the first three lines in your file. For example, your older .f -files may be legacy code but your newer ones may be F codes, and you would -identify the latter by including in the first three lines of those files a -Fortran comment of the form > - ! fortran_dialect=F - -For previous versions of the syntax, you may have set fortran_dialect to the -now-obsolete values "f77", "f90", "f95", or "elf". Such settings will be -silently handled as "f08". Users of "elf" may wish to experiment with "F" -instead. - The syntax/fortran.vim script contains embedded comments that tell you how to comment and/or uncomment some lines to (a) activate recognition of some non-standard, vendor-supplied intrinsics and (b) to prevent features deleted diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index f6dfe3b14a..220505f573 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -75,6 +75,8 @@ Defaults *nvim-defaults* - 'switchbuf' defaults to "uselast" - 'tabpagemax' defaults to 50 - 'tags' defaults to "./tags;,tags" +- 'termguicolors' is enabled by default if Nvim can detect support from the + host terminal - 'ttimeoutlen' defaults to 50 - 'ttyfast' is always set - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created diff --git a/runtime/ftplugin/fortran.vim b/runtime/ftplugin/fortran.vim index 8f5b243b82..a057db9a4b 100644 --- a/runtime/ftplugin/fortran.vim +++ b/runtime/ftplugin/fortran.vim @@ -1,7 +1,8 @@ " Vim settings file " Language: Fortran 2008 (and older: Fortran 2003, 95, 90, 77, 66) -" Version: (v53) 2021 April 06 (updated 2022 May 22) -" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/> +" Version: (v54) 2023 December 5 +" Maintainers: Ajit J. Thakkar <ajit@unb.ca>; <https://ajit.ext.unb.ca/> +" Joshua Hollett <j.hollett@uwinnipeg.ca> " Usage: For instructions, do :help fortran-plugin from Vim " Credits: " Version 0.1 was created in September 2000 by Ajit Thakkar. @@ -39,7 +40,7 @@ if !exists("b:fortran_fixed_source") " Fixed-form file extension defaults let b:fortran_fixed_source = 1 else - " Modern fortran still allows both fixed and free source form + " Modern fortran compilers still allow both fixed and free source form " Assume fixed source form unless signs of free source form " are detected in the first five columns of the first s:lmax lines. " Detection becomes more accurate and time-consuming if more lines @@ -70,14 +71,14 @@ if (b:fortran_fixed_source == 1) " but some vendor extensions allow longer lines if exists("fortran_extended_line_length") setlocal tw=132 - elseif exists("fortran_cardimage_line_length") - setlocal tw=80 else - setlocal tw=72 + " The use of columns 73-80 for sequence numbers is obsolete + " so almost all compilers allow a textwidth of 80 + setlocal tw=80 " If you need to add "&" on continued lines so that the code is " compatible with both free and fixed format, then you should do so - " in column 73 and uncomment the next line - " setlocal tw=73 + " in column 81 and uncomment the next line + " setlocal tw=81 endif else setlocal comments=:! diff --git a/runtime/indent/fortran.vim b/runtime/indent/fortran.vim index 9623014818..7130658f59 100644 --- a/runtime/indent/fortran.vim +++ b/runtime/indent/fortran.vim @@ -1,7 +1,8 @@ " Vim indent file -" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, and 77) -" Version: (v49) 2022 May 14 -" Maintainer: Ajit J. Thakkar <thakkar.ajit@gmail.com>; <http://www2.unb.ca/~ajit/> +" Language: Fortran 2008 (and Fortran 2003, 95, 90, and 77) +" Version: (v49) 2023 December 1 +" Maintainers: Ajit J. Thakkar <ajit@unb.ca>; <https://ajit.ext.unb.ca/> +" Joshua Hollett <j.hollett@uwinnipeg.ca> " Usage: For instructions, do :help fortran-indent from Vim " Credits: " Version 0.1 was created in September 2000 by Ajit Thakkar. diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index c3bb36fc36..b73681be04 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -165,91 +165,92 @@ do }) end ---- Guess value of 'background' based on terminal color. ---- ---- We write Operating System Command (OSC) 11 to the terminal to request the ---- terminal's background color. We then wait for a response. If the response ---- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then ---- compute the luminance[1] of the RGB color and classify it as light/dark ---- accordingly. Note that the color components may have anywhere from one to ---- four hex digits, and require scaling accordingly as values out of 4, 8, 12, ---- or 16 bits. Also note the A(lpha) component is optional, and is parsed but ---- ignored in the calculations. ---- ---- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 -do - --- Parse a string of hex characters as a color. - --- - --- The string can contain 1 to 4 hex characters. The returned value is - --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. - --- - --- For instance, if only a single hex char "a" is used, then this function - --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / - --- 256). - --- - --- @param c string Color as a string of hex chars - --- @return number? Intensity of the color - local function parsecolor(c) - if #c == 0 or #c > 4 then - return nil - end - - local val = tonumber(c, 16) - if not val then - return nil - end - - local max = tonumber(string.rep('f', #c), 16) - return val / max +-- Only do the following when the TUI is attached +local tty = nil +for _, ui in ipairs(vim.api.nvim_list_uis()) do + if ui.chan == 1 and ui.stdout_tty then + tty = ui + break end +end - --- Parse an OSC 11 response - --- - --- Either of the two formats below are accepted: - --- - --- OSC 11 ; rgb:<red>/<green>/<blue> - --- - --- or - --- - --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> +if tty then + --- Guess value of 'background' based on terminal color. --- - --- where + --- We write Operating System Command (OSC) 11 to the terminal to request the + --- terminal's background color. We then wait for a response. If the response + --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then + --- compute the luminance[1] of the RGB color and classify it as light/dark + --- accordingly. Note that the color components may have anywhere from one to + --- four hex digits, and require scaling accordingly as values out of 4, 8, 12, + --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but + --- ignored in the calculations. --- - --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh - --- - --- The alpha component is ignored, if present. - --- - --- @param resp string OSC 11 response - --- @return string? Red component - --- @return string? Green component - --- @return string? Blue component - local function parseosc11(resp) - local r, g, b - r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') - if not r and not g and not b then - local a - r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') - if not a or #a > 4 then - return nil, nil, nil + --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 + do + --- Parse a string of hex characters as a color. + --- + --- The string can contain 1 to 4 hex characters. The returned value is + --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. + --- + --- For instance, if only a single hex char "a" is used, then this function + --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / + --- 256). + --- + --- @param c string Color as a string of hex chars + --- @return number? Intensity of the color + local function parsecolor(c) + if #c == 0 or #c > 4 then + return nil end - end - if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then - return r, g, b + local val = tonumber(c, 16) + if not val then + return nil + end + + local max = tonumber(string.rep('f', #c), 16) + return val / max end - return nil, nil, nil - end + --- Parse an OSC 11 response + --- + --- Either of the two formats below are accepted: + --- + --- OSC 11 ; rgb:<red>/<green>/<blue> + --- + --- or + --- + --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> + --- + --- where + --- + --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh + --- + --- The alpha component is ignored, if present. + --- + --- @param resp string OSC 11 response + --- @return string? Red component + --- @return string? Green component + --- @return string? Blue component + local function parseosc11(resp) + local r, g, b + r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') + if not r and not g and not b then + local a + r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') + if not a or #a > 4 then + return nil, nil, nil + end + end + + if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then + return r, g, b + end - local tty = false - for _, ui in ipairs(vim.api.nvim_list_uis()) do - if ui.chan == 1 and ui.stdout_tty then - tty = true - break + return nil, nil, nil end - end - if tty then local timer = assert(vim.uv.new_timer()) ---@param bg string New value of the 'background' option @@ -300,7 +301,7 @@ do io.stdout:write('\027]11;?\007') timer:start(1000, 0, function() - -- No response received. Delete the autocommand + -- Delete the autocommand if no response was received vim.schedule(function() -- Suppress error if autocommand has already been deleted pcall(vim.api.nvim_del_autocmd, id) @@ -311,4 +312,108 @@ do end end) end + + --- If the TUI (term_has_truecolor) was able to determine that the host + --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the + --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's + --- response indicates that it does support truecolor enable 'termguicolors', + --- but only if the user has not already disabled it. + do + if tty.rgb then + -- The TUI was able to determine truecolor support + vim.o.termguicolors = true + else + --- Enable 'termguicolors', but only if it was not already set by the user. + local function settgc() + if not vim.api.nvim_get_option_info2('termguicolors', {}).was_set then + vim.o.termguicolors = true + end + end + + local caps = {} ---@type table<string, boolean> + require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found) + if not found then + return + end + + caps[cap] = true + if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then + settgc() + end + end) + + local timer = assert(vim.uv.new_timer()) + + -- Arbitrary colors to set in the SGR sequence + local r = 1 + local g = 2 + local b = 3 + + local id = vim.api.nvim_create_autocmd('TermResponse', { + nested = true, + callback = function(args) + local resp = args.data ---@type string + local decrqss = resp:match('^\027P1%$r([%d;:]+)m$') + + if decrqss then + -- The DECRQSS SGR response first contains attributes separated by + -- semicolons, followed by the SGR itself with parameters separated + -- by colons. Some terminals include "0" in the attribute list + -- unconditionally; others do not. Our SGR sequence did not set any + -- attributes, so there should be no attributes in the list. + local attrs = vim.split(decrqss, ';') + if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then + return true + end + + -- The returned SGR sequence should begin with 48:2 + local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$') + if not sgr then + return true + end + + -- The remaining elements of the SGR sequence should be the 3 colors + -- we set. Some terminals also include an additional parameter + -- (which can even be empty!), so handle those cases as well + local params = vim.split(sgr, ':') + if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then + return true + end + + if + tonumber(params[#params - 2]) == r + and tonumber(params[#params - 1]) == g + and tonumber(params[#params]) == b + then + settgc() + end + + return true + end + end, + }) + + -- Write SGR followed by DECRQSS. This sets the background color then + -- immediately asks the terminal what the background color is. If the + -- terminal responds to the DECRQSS with the same SGR sequence that we + -- sent then the terminal supports truecolor. + local decrqss = '\027P$qm\027\\' + if os.getenv('TMUX') then + decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027')) + end + io.stdout:write(string.format('\027[48;2;%d;%d;%dm%s', r, g, b, decrqss)) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + end) + + if not timer:is_closing() then + timer:close() + end + end) + end + end end diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index e3b99f6b3d..bb9ed722e2 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -20,6 +20,7 @@ vim.lsp = require('vim.lsp') vim.re = require('vim.re') vim.secure = require('vim.secure') vim.snippet = require('vim.snippet') +vim.text = require('vim.text') vim.treesitter = require('vim.treesitter') vim.ui = require('vim.ui') vim.version = require('vim.version') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index d2bdab4d28..5e65ca6b1b 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -6941,6 +6941,10 @@ vim.go.tbidi = vim.go.termbidi --- attributes instead of "cterm" attributes. `guifg` --- Requires an ISO-8613-3 compatible terminal. --- +--- Nvim will automatically attempt to determine if the host terminal +--- supports 24-bit color and will enable this option if it does +--- (unless explicitly disabled by the user). +--- --- @type boolean vim.o.termguicolors = false vim.o.tgc = vim.o.termguicolors diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 05e5b2b871..7234b813b6 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -4678,8 +4678,7 @@ function vim.fn.join(list, sep) end --- Vim value. In the following cases it will output --- |msgpack-special-dict|: --- 1. Dictionary contains duplicate key. ---- 2. Dictionary contains empty key. ---- 3. String contains NUL byte. Two special dictionaries: for +--- 2. String contains NUL byte. Two special dictionaries: for --- dictionary and for string will be emitted in case string --- with NUL byte was a dictionary key. --- @@ -5922,7 +5921,6 @@ function vim.fn.msgpackdump(list, type) end --- are binary strings). --- 2. String with NUL byte inside. --- 3. Duplicate key. ---- 4. Empty key. --- ext |List| with two values: first is a signed integer --- representing extension type. Second is --- |readfile()|-style list of strings. diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua index 862cc52149..b88d9ac9ba 100644 --- a/runtime/lua/vim/termcap.lua +++ b/runtime/lua/vim/termcap.lua @@ -12,7 +12,10 @@ local M = {} --- emulator supports the XTGETTCAP sequence. --- --- @param caps string|table A terminal capability or list of capabilities to query ---- @param cb function(cap:string, seq:string) Function to call when a response is received +--- @param cb function(cap:string, found:bool, seq:string?) Callback function which is called for +--- each capability in {caps}. {found} is set to true if the capability was found or false +--- otherwise. {seq} is the control sequence for the capability if found, or nil for +--- boolean capabilities. function M.query(caps, cb) vim.validate({ caps = { caps, { 'string', 'table' } }, @@ -23,21 +26,33 @@ function M.query(caps, cb) caps = { caps } end - local count = #caps + local pending = {} ---@type table<string, boolean> + for _, v in ipairs(caps) do + pending[v] = true + end + + local timer = assert(vim.uv.new_timer()) - vim.api.nvim_create_autocmd('TermResponse', { + local id = vim.api.nvim_create_autocmd('TermResponse', { callback = function(args) local resp = args.data ---@type string - local k, v = resp:match('^\027P1%+r(%x+)=(%x+)$') - if k and v then + local k, rest = resp:match('^\027P1%+r(%x+)(.*)$') + if k and rest then local cap = vim.text.hexdecode(k) - local seq = - vim.text.hexdecode(v):gsub('\\E', '\027'):gsub('%%p%d', ''):gsub('\\(%d+)', string.char) + local seq ---@type string? + if rest:match('^=%x+$') then + seq = vim.text + .hexdecode(rest:sub(2)) + :gsub('\\E', '\027') + :gsub('%%p%d', '') + :gsub('\\(%d+)', string.char) + end + + cb(cap, true, seq) - cb(cap, seq) + pending[cap] = nil - count = count - 1 - if count == 0 then + if next(pending) == nil then return true end end @@ -57,6 +72,23 @@ function M.query(caps, cb) end io.stdout:write(query) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + + -- Call the callback for all capabilities that were not found + for k in pairs(pending) do + cb(k, false, nil) + end + end) + + if not timer:is_closing() then + timer:close() + end + end) end return M diff --git a/runtime/plugin/osc52.lua b/runtime/plugin/osc52.lua index 374b70066f..7a90518966 100644 --- a/runtime/plugin/osc52.lua +++ b/runtime/plugin/osc52.lua @@ -6,7 +6,11 @@ if not tty or vim.g.clipboard ~= nil or vim.o.clipboard ~= '' or not os.getenv(' return end -require('vim.termcap').query('Ms', function(cap, seq) +require('vim.termcap').query('Ms', function(cap, found, seq) + if not found then + return + end + assert(cap == 'Ms') -- Check 'clipboard' and g:clipboard again to avoid a race condition @@ -16,7 +20,7 @@ require('vim.termcap').query('Ms', function(cap, seq) -- If the terminal reports a sequence other than OSC 52 for the Ms capability -- then ignore it. We only support OSC 52 (for now) - if not seq:match('^\027%]52') then + if not seq or not seq:match('^\027%]52') then return end diff --git a/runtime/syntax/fortran.vim b/runtime/syntax/fortran.vim index fc6c82b480..971a2b633d 100644 --- a/runtime/syntax/fortran.vim +++ b/runtime/syntax/fortran.vim @@ -1,7 +1,8 @@ " Vim syntax file -" Language: Fortran 2008 (and older: Fortran 2003, 95, 90, and 77) -" Version: (v105) 2023 August 14 -" Maintainer: Ajit J. Thakkar <ajit@unb.ca>; <http://www2.unb.ca/~ajit/> +" Language: Fortran 2008 (and Fortran 2003, 95, 90, and 77) +" Version: (v106) 2023 December 4 +" Maintainers: Ajit J. Thakkar <ajit@unb.ca>; <https://ajit.ext.unb.ca/> +" Joshua Hollett <j.hollett@uwinnipeg.ca> " Usage: For instructions, do :help fortran-syntax from Vim " Credits: " Version 0.1 for Fortran 95 was created in April 2000 by Ajit Thakkar from an @@ -11,7 +12,8 @@ " Walter Dieudonne, Alexander Wagner, Roman Bertle, Charles Rendleman, " Andrew Griffiths, Joe Krahn, Hendrik Merx, Matt Thompson, Jan Hermann, " Stefano Zaghi, Vishnu V. Krishnan, Judicael Grasset, Takuma Yoshida, -" Eisuke Kawashima, Andre Chalella, Fritz Reese, and Karl D. Hammond. +" Eisuke Kawashima, Andre Chalella, Fritz Reese, Karl D. Hammond, +" and Michele Esposito Marzino. if exists("b:current_syntax") finish @@ -20,34 +22,9 @@ endif let s:cpo_save = &cpo set cpo&vim -" Choose fortran_dialect using the priority: -" source file directive > buffer-local value > global value > file extension -" first try using directive in first three lines of file -let b:fortran_retype = getline(1)." ".getline(2)." ".getline(3) -if b:fortran_retype =~? '\<fortran_dialect\s*=\s*F\>' - let b:fortran_dialect = "F" -elseif b:fortran_retype =~? '\<fortran_dialect\s*=\s*f08\>' - let b:fortran_dialect = "f08" -elseif !exists("b:fortran_dialect") - if exists("g:fortran_dialect") && g:fortran_dialect =~# '\<F\|f08\>' - " try global variable - let b:fortran_dialect = g:fortran_dialect - else " nothing found, so use default - let b:fortran_dialect = "f08" - endif -endif -unlet! b:fortran_retype -" make sure buffer-local value is not invalid -if b:fortran_dialect !~# '\<F\|f08\>' - let b:fortran_dialect = "f08" -endif - " Choose between fixed and free source form if this hasn't been done yet if !exists("b:fortran_fixed_source") - if b:fortran_dialect == "F" - " F requires free source form - let b:fortran_fixed_source = 0 - elseif exists("fortran_free_source") + if exists("fortran_free_source") " User guarantees free source form for all fortran files let b:fortran_fixed_source = 0 elseif exists("fortran_fixed_source") @@ -60,7 +37,7 @@ if !exists("b:fortran_fixed_source") " Fixed-form file extension defaults let b:fortran_fixed_source = 1 else - " Modern fortran still allows both free and fixed source form. + " Modern fortran compilers still allow both free and fixed source form. " Assume fixed source form unless signs of free source form " are detected in the first five columns of the first s:lmax lines. " Detection becomes more accurate and time-consuming if more lines @@ -91,32 +68,29 @@ if b:fortran_fixed_source == 1 else syn match fortranConstructName "^\s*\zs\a\w*\ze\s*:" endif -if exists("fortran_more_precise") - syn match fortranConstructName "\(\<end\s*do\s\+\)\@11<=\a\w*" - syn match fortranConstructName "\(\<end\s*if\s\+\)\@11<=\a\w*" - syn match fortranConstructName "\(\<end\s*select\s\+\)\@15<=\a\w*" - syn match fortranConstructName "\(\<\%(exit\|cycle\)\s\+\)\@11<=\a\w*" -endif +syn match fortranConstructName "\(\<end\s*do\s\+\)\@11<=\a\w*" +syn match fortranConstructName "\(\<end\s*if\s\+\)\@11<=\a\w*" +syn match fortranConstructName "\(\<end\s*select\s\+\)\@15<=\a\w*" +syn match fortranConstructName "\(\<\%(exit\|cycle\)\s\+\)\@11<=\a\w*" + +syn match fortranUserUnit "\(\(end\s*\)\?\(subroutine\|function\|module\|program\|submodule\|block\)\s\+\)\@12<=\a\w\+" -syn match fortranUnitHeader "\<end\>" syn match fortranType "\<character\((\s*kind\s*=\w\+)\)\?\>" syn match fortranType "\<complex\((\s*kind\s*=\w\+)\)\?\>" syn keyword fortranType intrinsic -syn match fortranType "\<implicit\>\s\+\(none\)\?" +syn match fortranType "\<implicit\>\s\+\(none\(\s\+\<external\|type\>\)\?\)\?" syn keyword fortranStructure dimension syn keyword fortranStorageClass parameter save -syn match fortranUnitHeader "\<subroutine\>" -syn keyword fortranCall call -syn match fortranUnitHeader "\<function\>" -syn match fortranUnitHeader "\<program\>" -syn match fortranUnitHeader "\<block\>" -syn keyword fortranKeyword return stop +syn match fortranCall "call\s\+\a\w*" +syn match fortranUnitHeader "\(end\s*\)\?\(subroutine\|function\|module\|program\|submodule\|block\)" +syn keyword fortranKeyword return +syn match fortranKeyword "\(error\s\+\)\=stop" syn keyword fortranConditional else then syn match fortranConditional "\<if\>" -syn match fortranConditionalOb "\<if\s*(.*)\s*\d\+\s*,\s*\d\+\s*,\s*\d\+\s*$" +syn match fortranConditionalDel "\<if\s*(.*)\s*\d\+\s*,\s*\d\+\s*,\s*\d\+\s*$" syn match fortranRepeat "\<do\>" -syn keyword fortranTodo contained todo fixme +syn keyword fortranTodo contained bug note debug todo fixme "Catch errors caused by too many right parentheses syn region fortranParen transparent start="(" end=")" contains=ALLBUT,fortranParenError,@fortranCommentGroup,cIncluded,@spell @@ -141,8 +115,6 @@ endif syn match fortranIO '\%(\((\|,\|, *&\n\)\s*\)\@<=\(access\|blank\|direct\|exist\|file\|fmt\|form\|formatted\|iostat\|name\|named\|nextrec\|number\|opened\|rec\|recl\|sequential\|status\|unformatted\|unit\)\ze\s*=' -syn keyword fortranIntrinsicR alog alog10 amax0 amax1 amin0 amin1 amod cabs ccos cexp clog csin csqrt dabs dacos dasin datan datan2 dcos dcosh ddim dexp dint dlog dlog10 dmax1 dmin1 dmod dnint dsign dsin dsinh dsqrt dtan dtanh float iabs idim idint idnint ifix isign max0 max1 min0 min1 sngl - " Intrinsics provided by some vendors syn keyword fortranExtraIntrinsic algama cdabs cdcos cdexp cdlog cdsin cdsqrt cqabs cqcos cqexp cqlog cqsin cqsqrt dcmplx dconjg derf derfc dfloat dgamma dimag dlgama iqint qabs qacos qasin qatan qatan2 qcmplx qconjg qcos qcosh qdim qerf qerfc qexp qgamma qimag qlgama qlog qlog10 qmax1 qmin1 qmod qnint qsign qsin qsinh qsqrt qtan qtanh @@ -150,6 +122,11 @@ syn keyword fortranIntrinsic abs acos aimag aint anint asin atan atan2 char cmpl syn match fortranIntrinsic "\<len\s*[(,]"me=s+3 syn match fortranIntrinsic "\<real\s*("me=s+4 syn match fortranIntrinsic "\<logical\s*("me=s+7 +" F2018 and F2023 +syn keyword fortranIntrinsic out_of_range reduce random_init lcobound ucobound this_image coshape selected_logical_kind +syn keyword fortranIntrinsic ieee_max ieee_max_mag ieee_min ieee_min_mag split tokenize +syn keyword fortranIntrinsic acosd asind atand atan2d cosd sind tand acospi asinpi atanpi atan2pi cospi sinpi tanpi + syn match fortranType "\<type\>\(\s\+is\>\)\?" syn match fortranType "^\s*\(type\s\+\(is\)\? \)\?\s*\(real\|integer\|logical\|complex\|character\)\>" syn match fortranType "^\s*\(implicit \)\?\s*\(real\|integer\|logical\|complex\|character\)\>" @@ -188,32 +165,28 @@ syn match fortranLabelNumber display "^ \d\{1,3}\s"ms=s+2,me=e-1 syn match fortranLabelNumber display "^ \d\d\=\s"ms=s+3,me=e-1 syn match fortranLabelNumber display "^ \d\s"ms=s+4,me=e-1 -if exists("fortran_more_precise") - " Numbers as targets - syn match fortranTarget display "\(\<if\s*(.\+)\s*\)\@<=\(\d\+\s*,\s*\)\{2}\d\+\>" - syn match fortranTarget display "\(\<do\s\+\)\@11<=\d\+\>" - syn match fortranTarget display "\(\<go\s*to\s*(\=\)\@11<=\(\d\+\s*,\s*\)*\d\+\>" -endif +" Numbers as targets +syn match fortranTarget display "\(\<if\s*(.\+)\s*\)\@<=\(\d\+\s*,\s*\)\{2}\d\+\>" +syn match fortranTarget display "\(\<do\s\+\)\@11<=\d\+\>" +syn match fortranTarget display "\(\<go\s*to\s*(\=\)\@11<=\(\d\+\s*,\s*\)*\d\+\>" syn keyword fortranTypeR external syn keyword fortranIOR format -syn match fortranKeywordR "\<continue\>" +syn match fortranKeyword "\<continue\>" syn match fortranKeyword "^\s*\d\+\s\+continue\>" syn match fortranKeyword "\<go\s*to\>" syn match fortranKeywordDel "\<go\s*to\ze\s\+.*,\s*(.*$" syn match fortranKeywordOb "\<go\s*to\ze\s*(\d\+.*$" -syn region fortranStringR start=+'+ end=+'+ contains=fortranContinueMark,fortranLeftMargin,fortranSerialNumber -syn keyword fortranIntrinsicR dim lge lgt lle llt mod +syn region fortranString start=+'+ end=+'+ contains=fortranContinueMark,fortranLeftMargin,fortranSerialNumber syn keyword fortranKeywordDel assign pause syn keyword fortranStructure private public intent optional syn keyword fortranStructure pointer target allocatable syn keyword fortranStorageClass in out +syn match fortranStorageClass "\<in\s*out\>" syn match fortranStorageClass "\<kind\s*="me=s+4 syn match fortranStorageClass "\<len\s*="me=s+3 -syn match fortranUnitHeader "\<module\>" -syn match fortranUnitHeader "\<submodule\>" syn keyword fortranUnitHeader use only contains syn keyword fortranUnitHeader result operator assignment syn match fortranUnitHeader "\<interface\>" @@ -223,7 +196,7 @@ syn match fortranConditional "\<case\s\+default\>" syn keyword fortranConditional where elsewhere syn match fortranOperator "\(\(>\|<\)=\=\|==\|/=\|=\)" -syn match fortranOperator "=>" +syn match fortranOperator "\(%\|?\|=>\)" syn region fortranString start=+"+ end=+"+ contains=fortranLeftMargin,fortranContinueMark,fortranSerialNumber syn keyword fortranIO pad position action delim readwrite @@ -233,25 +206,18 @@ syn match fortranIntrinsic '\<\(adjustl\|adjustr\|all\|allocated\|any\|associate syn match fortranIntrinsic "\<not\>\(\s*\.\)\@!"me=s+3 syn match fortranIntrinsic "\<kind\>\s*[(,]"me=s+4 -syn match fortranUnitHeader "\<end\s*function" syn match fortranUnitHeader "\<end\s*interface" -syn match fortranUnitHeader "\<end\s*module" -syn match fortranUnitHeader "\<end\s*submodule" -syn match fortranUnitHeader "\<end\s*program" -syn match fortranUnitHeader "\<end\s*subroutine" -syn match fortranUnitHeader "\<end\s*block" syn match fortranRepeat "\<end\s*do" syn match fortranConditional "\<end\s*where" syn match fortranConditional "\<select\s*case" syn match fortranConditional "\<end\s*select" syn match fortranType "\<end\s*type" -syn match fortranType "\<in\s*out" -syn keyword fortranType procedure -syn match fortranType "\<module\ze\s\+procedure\>" +syn match fortranType "\(end\s\+\)\?\(\<module\s\+\)\?procedure\>" syn keyword fortranIOR namelist syn keyword fortranConditionalR while -syn keyword fortranIntrinsicR achar iachar transfer +syn keyword fortranIntrinsicR achar iachar transfer dble dprod dim lge lgt lle llt mod +syn keyword fortranIntrinsicOb alog alog10 amax0 amax1 amin0 amin1 amod cabs ccos cexp clog csin csqrt dabs dacos dasin datan datan2 dcos dcosh ddim dexp dint dlog dlog10 dmax1 dmin1 dmod dnint dsign dsin dsinh dsqrt dtan dtanh float iabs idim idint idnint ifix isign max0 max1 min0 min1 sngl syn keyword fortranInclude include syn keyword fortranStorageClassR sequence @@ -263,63 +229,62 @@ syn match fortranConditional "\<else\s*if" syn keyword fortranUnitHeaderOb entry syn match fortranTypeR display "double\s\+precision" syn match fortranTypeR display "double\s\+complex" -syn match fortranUnitHeaderR display "block\s\+data" -syn keyword fortranStorageClassR common equivalence data -syn keyword fortranIntrinsicR dble dprod +syn match fortranUnitHeaderOb display "block\s\+data" +syn match fortranStorageClass "^\s*data\>\(\s\+\a\w*\s*/\)\@=" +syn match fortranStorageClassOb "^\s*common\>\(\s*/\)\@=" +syn keyword fortranStorageClassOb equivalence syn match fortranOperatorR "\.\s*[gl][et]\s*\." syn match fortranOperatorR "\.\s*\(eq\|ne\)\s*\." -syn keyword fortranRepeat forall -syn match fortranRepeat "\<end\s*forall" -syn keyword fortranIntrinsic null cpu_time +syn keyword fortranRepeatOb forall +syn match fortranRepeatOb "\<end\s*forall" +syn keyword fortranIntrinsic null cpu_time syn match fortranType "\<elemental\>" syn match fortranType "\<pure\>" syn match fortranType "\<impure\>" syn match fortranType "\<recursive\>" -if exists("fortran_more_precise") - syn match fortranConstructName "\(\<end\s*forall\s\+\)\@15<=\a\w*\>" -endif - -if b:fortran_dialect == "f08" - " F2003 - syn keyword fortranIntrinsic command_argument_count get_command get_command_argument get_environment_variable is_iostat_end is_iostat_eor move_alloc new_line selected_char_kind same_type_as extends_type_of - " ISO_C_binding - syn keyword fortranConstant c_null_char c_alert c_backspace c_form_feed c_new_line c_carriage_return c_horizontal_tab c_vertical_tab - syn keyword fortranConstant c_int c_short c_long c_long_long c_signed_char c_size_t c_int8_t c_int16_t c_int32_t c_int64_t c_int_least8_t c_int_least16_t c_int_least32_t c_int_least64_t c_int_fast8_t c_int_fast16_t c_int_fast32_t c_int_fast64_t c_intmax_t C_intptr_t c_float c_double c_long_double c_float_complex c_double_complex c_long_double_complex c_bool c_char c_null_ptr c_null_funptr - syn keyword fortranIntrinsic iso_c_binding c_loc c_funloc c_associated c_f_pointer c_f_procpointer - syn keyword fortranType c_ptr c_funptr - " ISO_Fortran_env - syn keyword fortranConstant iso_fortran_env character_storage_size error_unit file_storage_size input_unit iostat_end iostat_eor numeric_storage_size output_unit - " IEEE_arithmetic - syn keyword fortranIntrinsic ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode - - syn keyword fortranReadWrite flush wait - syn keyword fortranIO decimal round iomsg - syn keyword fortranType asynchronous nopass non_overridable pass protected volatile extends import - syn keyword fortranType non_intrinsic value bind deferred generic final enumerator - syn match fortranType "\<abstract\>" - syn match fortranType "\<class\>" - syn match fortranType "\<associate\>" - syn match fortranType "\<end\s*associate" - syn match fortranType "\<enum\s*,\s*bind\s*(\s*c\s*)" - syn match fortranType "\<end\s*enum" - syn match fortranConditional "\<select\s*type" - syn match fortranConditional "\<class\s*is\>" - syn match fortranConditional "\<class\s*default\>" - syn match fortranUnitHeader "\<abstract\s*interface\>" - syn match fortranOperator "\([\|]\)" - - " F2008 - syn keyword fortranIntrinsic acosh asinh atanh bessel_j0 bessel_j1 bessel_jn bessel_y0 bessel_y1 bessel_yn erf erfc erfc_scaled gamma log_gamma hypot norm2 - syn keyword fortranIntrinsic atomic_define atomic_ref execute_command_line leadz trailz storage_size merge_bits - syn keyword fortranIntrinsic bge bgt ble blt dshiftl dshiftr findloc iall iany iparity image_index lcobound ucobound maskl maskr num_images parity popcnt poppar shifta shiftl shiftr this_image - syn keyword fortranIO newunit - syn keyword fortranType contiguous - syn keyword fortranRepeat concurrent +syn match fortranConstructNameOb "\(\<end\s*forall\s\+\)\@15<=\a\w*\>" + +" F2003 +syn keyword fortranIntrinsic command_argument_count get_command get_command_argument get_environment_variable is_iostat_end is_iostat_eor move_alloc new_line selected_char_kind same_type_as extends_type_of +" ISO_C_binding +syn keyword fortranConstant c_null_char c_alert c_backspace c_form_feed c_new_line c_carriage_return c_horizontal_tab c_vertical_tab +syn keyword fortranConstant c_int c_short c_long c_long_long c_signed_char c_size_t c_int8_t c_int16_t c_int32_t c_int64_t c_int_least8_t c_int_least16_t c_int_least32_t c_int_least64_t c_int_fast8_t c_int_fast16_t c_int_fast32_t c_int_fast64_t c_intmax_t C_intptr_t c_float c_double c_long_double c_float_complex c_double_complex c_long_double_complex c_bool c_char c_null_ptr c_null_funptr +syn keyword fortranIntrinsic iso_c_binding c_loc c_funloc c_associated c_f_pointer c_f_procpointer +syn keyword fortranType c_ptr c_funptr +" ISO_Fortran_env +syn keyword fortranConstant iso_fortran_env character_storage_size error_unit file_storage_size input_unit iostat_end iostat_eor numeric_storage_size output_unit +" IEEE_arithmetic +syn keyword fortranIntrinsic ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode + +syn keyword fortranReadWrite flush wait +syn keyword fortranIO decimal round iomsg +syn keyword fortranType asynchronous nopass non_overridable pass protected volatile extends import +syn keyword fortranType non_intrinsic value bind deferred generic final enumerator +syn match fortranType "\<abstract\>" +syn match fortranType "\<class\>" +syn match fortranType "\<associate\>" +syn match fortranType "\<end\s*associate" +syn match fortranType "\<enum\s*,\s*bind\s*(\s*c\s*)" +syn match fortranType "\<end\s*enum" +syn match fortranConditional "\<select\s*type" +syn match fortranConditional "\<class\s*is\>" +syn match fortranConditional "\<class\s*default\>" +syn match fortranUnitHeader "\<abstract\s*interface\>" +syn match fortranOperator "\([\|]\)" + +" F2008 +syn keyword fortranIntrinsic acosh asinh atanh bessel_j0 bessel_j1 bessel_jn bessel_y0 bessel_y1 bessel_yn erf erfc erfc_scaled gamma log_gamma hypot norm2 +syn keyword fortranIntrinsic atomic_define atomic_ref execute_command_line leadz trailz storage_size merge_bits +syn keyword fortranIntrinsic bge bgt ble blt dshiftl dshiftr findloc iall iany iparity image_index lcobound ucobound maskl maskr num_images parity popcnt poppar shifta shiftl shiftr this_image +syn keyword fortranIO newunit +syn keyword fortranType contiguous +syn keyword fortranRepeat concurrent " CUDA fortran +if exists("fortran_CUDA") syn match fortranTypeCUDA "\<attributes\>" - syn keyword fortranTypeCUDA host global device value + syn keyword fortranTypeCUDA host global device syn keyword fortranTypeCUDA shared constant pinned texture syn keyword fortranTypeCUDA dim1 dim2 dim3 dim4 syn keyword fortranTypeCUDA cudadeviceprop cuda_count_kind cuda_stream_kind @@ -349,10 +314,10 @@ if b:fortran_dialect == "f08" syn keyword fortranIntrinsicCUDA cudaHostAlloc cudaHostGetDevicePointer cudaHostGetFlags cudaHostRegister cudaHostUnregister cudaMalloc cudaMallocArray cudaMallocHost cudaMallocPitch cudaMalloc3D cudaMalloc3DArray syn keyword fortranIntrinsicCUDA cudaMemcpy cudaMemcpyArraytoArray cudaMemcpyAsync cudaMemcpyFromArray cudaMemcpyFromSymbol cudaMemcpyFromSymbolAsync cudaMemcpyPeer cudaMemcpyPeerAsync cudaMemcpyToArray cudaMemcpyToSymbol cudaMemcpyToSymbolAsync cudaMemcpy2D cudaMemcpy2DArrayToArray cudaMemcpy2DAsync cudaMemcpy2DFromArray cudaMemcpy2DToArray cudaMemcpy3D cudaMemcpy3DAsync syn keyword fortranIntrinsicCUDA cudaMemGetInfo cudaMemset cudaMemset2D cudaMemset3D cudaDeviceCanAccessPeer cudaDeviceDisablePeerAccess cudaDeviceEnablePeerAccess cudaPointerGetAttributes cudaDriverGetVersion cudaRuntimeGetVersion - - syn region none matchgroup=fortranType start="<<<" end=">>>" contains=ALLBUT,none endif +syn region none matchgroup=fortranType start="<<<" end=">>>" contains=ALLBUT,none + syn cluster fortranCommentGroup contains=fortranTodo if (b:fortran_fixed_source == 1) @@ -391,7 +356,7 @@ syn match cIncluded contained "<[^>]*>" syn match cInclude "^\s*#\s*include\>\s*["<]" contains=cIncluded "Synchronising limits assume that comment and continuation lines are not mixed -if exists("fortran_fold") || exists("fortran_more_precise") +if exists("fortran_fold") syn sync fromstart elseif (b:fortran_fixed_source == 0) syn sync linecont "&" minlines=30 @@ -447,79 +412,71 @@ if exists("fortran_fold") endif " Define the default highlighting. -" The default highlighting differs for each dialect. " Transparent groups: " fortranParen, fortranLeftMargin " fortranProgram, fortranModule, fortranSubroutine, fortranFunction, " fortranBlockData " fortran77Loop, fortran90Loop, fortranIfBlock, fortranCase " fortranMultiCommentLines -hi def link fortranKeyword Keyword +hi def link fortranKeyword Keyword hi def link fortranConstructName Identifier -hi def link fortranConditional Conditional -hi def link fortranRepeat Repeat -hi def link fortranTodo Todo -hi def link fortranContinueMark Special -hi def link fortranString String -hi def link fortranNumber Number -hi def link fortranBinary Number -hi def link fortranOctal Number -hi def link fortranHex Number -hi def link fortranOperator Operator -hi def link fortranBoolean Boolean -hi def link fortranLabelError Error -hi def link fortranObsolete Todo -hi def link fortranType Type -hi def link fortranStructure Type -hi def link fortranStorageClass StorageClass -hi def link fortranCall Function -hi def link fortranUnitHeader fortranPreCondit -hi def link fortranReadWrite Keyword -hi def link fortranIO Keyword -hi def link fortranIntrinsic Function -hi def link fortranConstant Constant - -" To stop deleted & obsolescent features being highlighted as Todo items, -" comment out the next 5 lines and uncomment the 5 lines after that -hi def link fortranUnitHeaderOb fortranObsolete -hi def link fortranKeywordOb fortranObsolete -hi def link fortranConditionalOb fortranObsolete -hi def link fortranTypeOb fortranObsolete -hi def link fortranKeywordDel fortranObsolete -"hi def link fortranUnitHeaderOb fortranUnitHeader -"hi def link fortranKeywordOb fortranKeyword -"hi def link fortranConditionalOb fortranConditional -"hi def link fortranTypeOb fortranType -"hi def link fortranKeywordDel fortranKeyword - -if b:fortran_dialect == "F" - hi! def link fortranIntrinsicR fortranObsolete - hi! def link fortranUnitHeaderR fortranObsolete - hi! def link fortranTypeR fortranObsolete - hi! def link fortranStorageClassR fortranObsolete - hi! def link fortranOperatorR fortranObsolete - hi! def link fortranInclude fortranObsolete - hi! def link fortranLabelNumber fortranObsolete - hi! def link fortranTarget fortranObsolete - hi! def link fortranFloatIll fortranObsolete - hi! def link fortranIOR fortranObsolete - hi! def link fortranKeywordR fortranObsolete - hi! def link fortranStringR fortranObsolete - hi! def link fortranConditionalR fortranObsolete +hi def link fortranConditional Conditional +hi def link fortranRepeat Repeat +hi def link fortranTodo Todo +hi def link fortranContinueMark Special +hi def link fortranString String +hi def link fortranNumber Number +hi def link fortranBinary Number +hi def link fortranOctal Number +hi def link fortranHex Number +hi def link fortranOperator Operator +hi def link fortranBoolean Boolean +hi def link fortranLabelError Error +hi def link fortranObsolete Todo +hi def link fortranType Type +hi def link fortranStructure Type + +hi def link fortranStorageClass StorageClass +hi def link fortranIntrinsic Function +hi def link fortranCall Function +hi def link fortranUserUnit Function +hi def link fortranUnitHeader fortranPreCondit +hi def link fortranReadWrite Keyword +hi def link fortranIO Keyword +hi def link fortranConstant Constant + +hi def link fortranInclude Include +hi def link fortranLabelNumber Special +hi def link fortranTarget Special +hi def link fortranFloatIll fortranFloat + +" Redundant features which may one day become obsolescent +hi def link fortranIntrinsicR fortranIntrinsic +hi def link fortranTypeR fortranType +hi def link fortranStorageClassR fortranStorageClass +hi def link fortranOperatorR fortranOperator +hi def link fortranIOR fortranIO +hi def link fortranConditionalR fortranConditional + +" features deleted or declared obsolescent in Fortran 2023 +if (b:fortran_fixed_source == 1) + hi def link fortranUnitHeaderOb fortranUnitHeader + hi def link fortranKeywordOb fortranKeyword + hi def link fortranKeywordDel fortranKeyword + hi def link fortranConditionalDel fortranConditional + hi def link fortranTypeOb fortranType + hi def link fortranStorageClassOb fortranStorageClass + hi def link fortranRepeatOb fortranRepeat + hi def link fortranIntrinsicOb fortranIntrinsic else - hi! def link fortranIntrinsicR fortranIntrinsic - hi! def link fortranUnitHeaderR fortranPreCondit - hi! def link fortranTypeR fortranType - hi! def link fortranStorageClassR fortranStorageClass - hi! def link fortranOperatorR fortranOperator - hi! def link fortranInclude Include - hi! def link fortranLabelNumber Special - hi! def link fortranTarget Special - hi! def link fortranFloatIll fortranFloat - hi! def link fortranIOR fortranIO - hi! def link fortranKeywordR fortranKeyword - hi! def link fortranStringR fortranString - hi! def link fortranConditionalR fortranConditional + hi def link fortranUnitHeaderOb fortranObsolete + hi def link fortranKeywordOb fortranObsolete + hi def link fortranKeywordDel fortranObsolete + hi def link fortranConditionalDel fortranObsolete + hi def link fortranTypeOb fortranObsolete + hi def link fortranStorageClassOb fortranObsolete + hi def link fortranRepeatOb fortranObsolete + hi def link fortranIntrinsicOb fortranObsolete endif hi def link fortranFormatSpec Identifier diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index fcd50bccd2..b7ecd917ed 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -200,7 +200,7 @@ syn keyword vimAugroupKey contained aug[roup] " Operators: {{{2 " ========= syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimType,vimRegister,vimContinue,vim9Comment,vimVar -syn match vimOper "||\|&&\|[-+.!]" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "||\|&&\|[-+*/%.!]" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=vimoperStar,@vimOperGroup diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 59423808be..51cf7bb0ea 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5735,8 +5735,7 @@ M.funcs = { Vim value. In the following cases it will output |msgpack-special-dict|: 1. Dictionary contains duplicate key. - 2. Dictionary contains empty key. - 3. String contains NUL byte. Two special dictionaries: for + 2. String contains NUL byte. Two special dictionaries: for dictionary and for string will be emitted in case string with NUL byte was a dictionary key. @@ -7155,7 +7154,6 @@ M.funcs = { are binary strings). 2. String with NUL byte inside. 3. Duplicate key. - 4. Empty key. ext |List| with two values: first is a signed integer representing extension type. Second is |readfile()|-style list of strings. diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index a6407693d7..64b65b42a5 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -141,9 +141,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack ValuesStackItem key = kv_pop(*stack); if (last_container.special_val == NULL) { // These cases should have already been handled. - assert(!(key.is_special_string - || key.val.vval.v_string == NULL - || *key.val.vval.v_string == NUL)); + assert(!(key.is_special_string || key.val.vval.v_string == NULL)); dictitem_T *const obj_di = tv_dict_item_alloc(key.val.vval.v_string); tv_clear(&key.val); if (tv_dict_add(last_container.container.vval.v_dict, obj_di) @@ -170,11 +168,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack tv_clear(&obj.val); return FAIL; } - // Handle empty key and key represented as special dictionary + // Handle special dictionaries if (last_container.special_val == NULL && (obj.is_special_string || obj.val.vval.v_string == NULL - || *obj.val.vval.v_string == NUL || tv_dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { tv_clear(&obj.val); @@ -404,13 +401,6 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, semsg(_("E474: Expected string end: %.*s"), (int)buf_len, buf); goto parse_json_string_fail; } - if (len == 0) { - POP(((typval_T) { - .v_type = VAR_STRING, - .vval = { .v_string = NULL }, - }), false); - goto parse_json_string_ret; - } char *str = xmalloc(len + 1); int fst_in_pair = 0; char *str_end = str; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index daaf73d241..50371b8bf3 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -8764,6 +8764,10 @@ return { Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight| attributes instead of "cterm" attributes. |guifg| Requires an ISO-8613-3 compatible terminal. + + Nvim will automatically attempt to determine if the host terminal + supports 24-bit color and will enable this option if it does + (unless explicitly disabled by the user). ]=], full_name = 'termguicolors', redraw = { 'ui_option' }, diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index c71eb633e9..d625c22c76 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -148,7 +148,8 @@ static bool cursor_style_enabled = false; # include "tui/tui.c.generated.h" #endif -void tui_start(TUIData **tui_p, int *width, int *height, char **term) +void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) + FUNC_ATTR_NONNULL_ALL { TUIData *tui = xcalloc(1, sizeof(TUIData)); tui->is_starting = true; @@ -177,6 +178,7 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term) *width = tui->width; *height = tui->height; *term = tui->term; + *rgb = tui->rgb; } void tui_set_key_encoding(TUIData *tui) @@ -334,6 +336,9 @@ static void terminfo_start(TUIData *tui) int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) : (konsole ? 1 : 0); + // truecolor support must be checked before patching/augmenting terminfo + tui->rgb = term_has_truecolor(tui, colorterm); + patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm); augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm); tui->can_change_scroll_region = @@ -1439,7 +1444,7 @@ void tui_suspend(TUIData *tui) tui_mouse_on(tui); } stream_set_blocking(tui->input.in_fd, false); // libuv expects this - ui_client_attach(tui->width, tui->height, tui->term); + ui_client_attach(tui->width, tui->height, tui->term, tui->rgb); #endif } @@ -1752,6 +1757,44 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) return -1; } +/// Determine if the terminal supports truecolor or not: +/// +/// 1. If $COLORTERM is "24bit" or "truecolor", return true +/// 2. Else, check terminfo for Tc, RGB, setrgbf, or setrgbb capabilities. If +/// found, return true +/// 3. Else, return false +static bool term_has_truecolor(TUIData *tui, const char *colorterm) +{ + // Check $COLORTERM + if (strequal(colorterm, "truecolor") || strequal(colorterm, "24bit")) { + return true; + } + + // Check for Tc and RGB + for (size_t i = 0; i < unibi_count_ext_bool(tui->ut); i++) { + const char *n = unibi_get_ext_bool_name(tui->ut, i); + if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) { + return true; + } + } + + // Check for setrgbf and setrgbb + bool setrgbf = false; + bool setrgbb = false; + for (size_t i = 0; i < unibi_count_ext_str(tui->ut) && (!setrgbf || !setrgbb); i++) { + const char *n = unibi_get_ext_str_name(tui->ut, i); + if (n) { + if (!setrgbf && !strcmp(n, "setrgbf")) { + setrgbf = true; + } else if (!setrgbb && !strcmp(n, "setrgbb")) { + setrgbb = true; + } + } + } + + return setrgbf && setrgbb; +} + /// Patches the terminfo records after loading from system or built-in db. /// Several entries in terminfo are known to be deficient or outright wrong; /// and several terminal emulators falsely announce incorrect terminal types. diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index eb32c16881..d744560a86 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -70,14 +70,14 @@ uint64_t ui_client_start_server(int argc, char **argv) return channel->id; } -void ui_client_attach(int width, int height, char *term) +void ui_client_attach(int width, int height, char *term, bool rgb) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); MAXSIZE_TEMP_DICT(opts, 9); - PUT_C(opts, "rgb", BOOLEAN_OBJ(true)); + PUT_C(opts, "rgb", BOOLEAN_OBJ(rgb)); PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { @@ -111,9 +111,10 @@ void ui_client_run(bool remote_ui) ui_client_is_remote = remote_ui; int width, height; char *term; - tui_start(&tui, &width, &height, &term); + bool rgb; + tui_start(&tui, &width, &height, &term, &rgb); - ui_client_attach(width, height, term); + ui_client_attach(width, height, term, rgb); // os_exit() will be invoked when the client channel detaches while (true) { diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua index c72842f14b..204bfcaa12 100644 --- a/test/functional/autocmd/focus_spec.lua +++ b/test/functional/autocmd/focus_spec.lua @@ -2,7 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local luv = require('luv') local clear = helpers.clear -local nvim_prog = helpers.nvim_prog local feed_command = helpers.feed_command local feed_data = thelpers.feed_data @@ -14,10 +13,12 @@ describe('autoread TUI FocusGained/FocusLost', function() before_each(function() clear() - screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "set noswapfile noshowcmd noruler"]' - ) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + }) end) teardown(function() diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index c347b51dd3..456650be6a 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -30,6 +30,8 @@ local feed_command = helpers.feed_command local skip = helpers.skip local is_os = helpers.is_os local is_ci = helpers.is_ci +local spawn = helpers.spawn +local set_session = helpers.set_session describe('fileio', function() before_each(function() @@ -49,6 +51,23 @@ describe('fileio', function() rmdir('Xtest_backupdir with spaces') end) + local args = { nvim_prog, '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir', } + --- Starts a new nvim session and returns an attached screen. + local function startup(extra_args) + extra_args = extra_args or {} + local argv = vim.tbl_flatten({args, '--embed', extra_args}) + local screen_nvim = spawn(argv) + set_session(screen_nvim) + local screen = Screen.new(70, 10) + screen:attach() + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.NvimDarkGrey4}; + [2] = {background = Screen.colors.NvimDarkGrey1, foreground = Screen.colors.NvimLightGrey3}; + [3] = {foreground = Screen.colors.NvimLightCyan}; + }) + return screen + end + it("fsync() with 'nofsync' #8304", function() clear({ args={ '--cmd', 'set nofsync directory=Xtest_startup_swapdir', } }) @@ -70,27 +89,36 @@ describe('fileio', function() -- 2. Explicit :preserve command. command('preserve') - -- TODO: should be exactly 2; figure out where the extra fsync() is coming from. #26404 - ok(request('nvim__stats').fsync >= 2) + -- TODO: should be exactly 2; where is the extra fsync() is coming from? #26404 + ok(request('nvim__stats').fsync == 2 or request('nvim__stats').fsync == 3) -- 3. Enable 'fsync' option, write file. command('set fsync') feed('Abaz<esc>h') command('write') - eq(4, request('nvim__stats').fsync) + -- TODO: should be exactly 4; where is the extra fsync() is coming from? #26404 + ok(request('nvim__stats').fsync == 4 or request('nvim__stats').fsync == 5) eq('foozubbaz', trim(read_file('Xtest_startup_file1'))) -- 4. Exit caused by deadly signal (+ 'swapfile'). - local j = funcs.jobstart({ nvim_prog, '-u', 'NONE', '--headless', - '--cmd', 'set nofsync directory=Xtest_startup_swapdir', - '-c', 'set swapfile', - '-c', 'write Xtest_startup_file2', - '-c', 'put =localtime()', }) - sleep(10) -- Let Nvim start. + local j = funcs.jobstart(vim.tbl_flatten({args, '--embed'}), {rpc=true}) + funcs.rpcrequest(j, 'nvim_exec2', [[ + set nofsync directory=Xtest_startup_swapdir + edit Xtest_startup_file2 + write + put ='fsyncd text' + ]], {}) + eq('Xtest_startup_swapdir', funcs.rpcrequest(j, 'nvim_eval', '&directory')) funcs.jobstop(j) -- Send deadly signal. + local screen = startup() + feed(':recover Xtest_startup_file2<cr>') + screen:expect({any = [[Using swap file "Xtest_startup_swapdir[/\]Xtest_startup_file2%.swp"]]}) + feed('<cr>') + screen:expect({any = 'fsyncd text'}) + -- 5. SIGPWR signal. - -- ?? + -- oldtest: Test_signal_PWR() end) it('backup #9709', function() diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index d705d56575..c8d800f89a 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -62,7 +62,7 @@ describe('command-line option', function() screen:attach() local args = { nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', - '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix"', + '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix notermguicolors"', '-s', '-' } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 2bff1d16f8..5780d0a978 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -30,7 +30,8 @@ module.nvim_prog = ( module.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent startofline' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid') + ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid' + ..' notermguicolors') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', -- XXX: find treesitter parsers. @@ -438,7 +439,7 @@ function module.connect(file_or_address) return Session.new(stream) end --- Starts a new global Nvim session. +-- Starts (and returns) a new global Nvim session. -- -- Parameters are interpreted as startup args, OR a map with these keys: -- args: List: Args appended to the default `nvim_argv` set. @@ -452,6 +453,7 @@ end -- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}} function module.clear(...) module.set_session(module.spawn_argv(false, ...)) + return module.get_session() end -- same params as clear, but does returns the session instead @@ -943,7 +945,7 @@ function module.add_builddir_to_rtp() module.command(string.format([[set rtp+=%s/runtime]], module.test_build_dir)) end --- Kill process with given pid +-- Kill (reap) a process by PID. function module.os_kill(pid) return os.execute((is_os('win') and 'taskkill /f /t /pid '..pid..' > nul' diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua index 25fdb48eea..705bfcf2f9 100644 --- a/test/functional/lua/json_spec.lua +++ b/test/functional/lua/json_spec.lua @@ -101,6 +101,8 @@ describe('vim.json.decode()', function() eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]])) eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]])) + -- Empty string is a valid key. #20757 + eq({['']=42}, exec_lua([[return vim.json.decode('{"": 42}')]])) end) it('parses strings properly', function() @@ -161,6 +163,8 @@ describe('vim.json.encode()', function() it('dumps dictionaries', function() eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) + -- Empty string is a valid key. #20757 + eq('{"":42}', exec_lua([[return vim.json.encode({['']=42})]])) end) it('dumps vim.NIL', function() diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index c278b2ad0e..68082ba4fa 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -11,8 +11,12 @@ describe('api', function() before_each(function() helpers.clear() os.remove(socket_name) - screen = child_session.screen_setup(0, '["'..helpers.nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "'..helpers.nvim_set..'"]') + screen = child_session.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', helpers.nvim_set, + }) end) after_each(function() os.remove(socket_name) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 423e7bdf21..b92f1b1592 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -17,7 +17,6 @@ local sleep = helpers.sleep local funcs = helpers.funcs local is_os = helpers.is_os local skip = helpers.skip -local nvim_prog = helpers.nvim_prog describe(':terminal buffer', function() local screen @@ -446,10 +445,13 @@ end) describe('terminal input', function() it('sends various special keys with modifiers', function() clear() - local screen = thelpers.screen_setup( - 0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--cmd", 'colorscheme vim', "--cmd", "startinsert"]]=], nvim_prog) - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set notermguicolors', + '--cmd', 'startinsert', + }) screen:expect{grid=[[ {1: } | {4:~ }| diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 95081b7189..37bb0ee817 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -3,7 +3,6 @@ local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local testprg, command = helpers.testprg, helpers.command -local nvim_prog = helpers.nvim_prog local eq, eval = helpers.eq, helpers.eval local matches = helpers.matches local poke_eventloop = helpers.poke_eventloop @@ -184,10 +183,18 @@ describe('buffer cursor position is correct in terminal without number column', local screen local function setup_ex_register(str) - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '-E', + '--cmd', string.format('let @r = "%s"', str), -- <Left> and <Right> don't always work - ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + '--cmd', 'cnoremap <C-X> <Left>', + '--cmd', 'cnoremap <C-O> <Right>', + '--cmd', 'set notermguicolors', + }, { + cols = 70, + }) screen:set_default_attr_ids({ [1] = {foreground = 253, background = 11}; [3] = {bold = true}, @@ -570,10 +577,18 @@ describe('buffer cursor position is correct in terminal with number column', fun local screen local function setup_ex_register(str) - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '-E', + '--cmd', string.format('let @r = "%s"', str), -- <Left> and <Right> don't always work - ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + '--cmd', 'cnoremap <C-X> <Left>', + '--cmd', 'cnoremap <C-O> <Right>', + '--cmd', 'set notermguicolors', + }, { + cols = 70, + }) screen:set_default_attr_ids({ [1] = {foreground = 253, background = 11}; [3] = {bold = true}, diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 62d3dd67a3..4ae054daa8 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -6,6 +6,7 @@ local Screen = require('test.functional.ui.screen') local testprg = helpers.testprg local exec_lua = helpers.exec_lua local nvim = helpers.nvim +local nvim_prog = helpers.nvim_prog local function feed_data(data) if type(data) == 'table' then @@ -122,6 +123,26 @@ local function screen_setup(extra_rows, command, cols, opts) return screen end +local function setup_child_nvim(args, opts) + opts = opts or {} + + local argv = { nvim_prog, unpack(args) } + local cmd = string.format('[%s]', vim.iter(argv):map(function(s) + return string.format('\'%s\'', s) + end):join(', ')) + + if opts.env then + local s = {} + for k, v in pairs(opts.env) do + table.insert(s, string.format('%s: \'%s\'', k, v)) + end + + cmd = string.format('%s, #{env: #{%s}}', cmd, table.concat(s, ', ')) + end + + return screen_setup(0, cmd, opts.cols) +end + return { feed_data = feed_data, feed_termcode = feed_termcode, @@ -141,5 +162,6 @@ return { clear_attrs = clear_attrs, enable_mouse = enable_mouse, disable_mouse = disable_mouse, - screen_setup = screen_setup + screen_setup = screen_setup, + setup_child_nvim = setup_child_nvim, } diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3748f820d4..0c984069b3 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -39,13 +39,13 @@ describe('TUI', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.screen_setup(0, - string.format( - [=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark", "--cmd", "colorscheme vim"]]=], - nvim_prog, - child_server, - nvim_set - )) + screen = thelpers.setup_child_nvim({ + '--listen', child_server, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + '--cmd', 'colorscheme vim' + }) screen:expect([[ {1: } | {4:~ }| @@ -1830,7 +1830,7 @@ describe('TUI', function() }) screen:attach() exec([[ - call termopen([v:progpath, '--clean', '--cmd', 'colorscheme vim', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) + call termopen([v:progpath, '--clean', '--cmd', 'set notermguicolors', '--cmd', 'colorscheme vim', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) sleep 500m vs new ]]) @@ -1856,30 +1856,29 @@ describe('TUI', function() write_file(script_file, [=[ local ffi = require('ffi') ffi.cdef([[int execl(const char *, const char *, ...);]]) - ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean', '--cmd', 'colorscheme vim') + ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean') ]=]) finally(function() os.remove(script_file) end) - local screen = thelpers.screen_setup(0, string.format([=[["%s", "--clean", "-l", "%s"]]=], - nvim_prog, script_file)) + local screen = thelpers.setup_child_nvim({'--clean', '-l', script_file}) screen:expect{grid=[[ {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| + ~ | + ~ | + ~ | + [No Name] 0,0-1 All| | {3:-- TERMINAL --} | ]]} feed_data(':put =v:argv + [v:progname]\n') screen:expect{grid=[[ + Xargv0nvim | + --embed | --clean | - --cmd | - colorscheme vim | {1:X}argv0nvim | - {5:[No Name] [+] 7,1 Bot}| - 6 more lines | + [No Name] [+] 5,1 Bot| + 4 more lines | {3:-- TERMINAL --} | ]]} end) @@ -1903,11 +1902,13 @@ describe('TUI', function() end) it('<C-h> #10134', function() - local screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ]] - ..[["--cmd", "set noruler", "--cmd", ':nnoremap <C-h> :echomsg "\<C-h\>"<CR>']]..']' - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noruler notermguicolors', + '--cmd', ':nnoremap <C-h> :echomsg "\\<C-h\\>"<CR>', + }) screen:expect{grid=[[ {1: } | {4:~ }| @@ -1931,12 +1932,13 @@ describe('TUI', function() end) it('draws line with many trailing spaces correctly #24955', function() - local screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim"]] - ..[[, "--cmd", "call setline(1, ['1st line' .. repeat(' ', 153), '2nd line'])"]]..']', - 80 - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'set notermguicolors', + '--cmd', 'colorscheme vim', + '--cmd', 'call setline(1, ["1st line" .. repeat(" ", 153), "2nd line"])', + }, { cols = 80 }) screen:expect{grid=[[ {1:1}st line | | @@ -1962,16 +1964,16 @@ end) describe('TUI UIEnter/UILeave', function() it('fires exactly once, after VimEnter', function() clear() - local screen = thelpers.screen_setup(0, - '["'..nvim_prog..'", "-u", "NONE", "-i", "NONE"' - ..[[, "--cmd", "colorscheme vim"]] - ..[[, "--cmd", "set noswapfile noshowcmd noruler"]] - ..[[, "--cmd", "let g:evs = []"]] - ..[[, "--cmd", "autocmd UIEnter * :call add(g:evs, 'UIEnter')"]] - ..[[, "--cmd", "autocmd UILeave * :call add(g:evs, 'UILeave')"]] - ..[[, "--cmd", "autocmd VimEnter * :call add(g:evs, 'VimEnter')"]] - ..']' - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + '--cmd', 'let g:evs = []', + '--cmd', 'autocmd UIEnter * :call add(g:evs, "UIEnter")', + '--cmd', 'autocmd UILeave * :call add(g:evs, "UILeave")', + '--cmd', 'autocmd VimEnter * :call add(g:evs, "VimEnter")', + }) screen:expect{grid=[[ {1: } | {4:~ }| @@ -2001,11 +2003,14 @@ describe('TUI FocusGained/FocusLost', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "set noswapfile noshowcmd noruler"]', - nvim_prog, child_server)) + screen = thelpers.setup_child_nvim({ + '--listen', child_server, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + }) + screen:expect([[ {1: } | {4:~ }| @@ -2208,14 +2213,18 @@ describe("TUI 't_Co' (terminal colors)", function() local function assert_term_colors(term, colorterm, maxcolors) clear({env={TERM=term}, args={}}) - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - screen = thelpers.screen_setup(0, string.format( - [=[['sh', '-c', 'LANG=C TERM=%s %s %s -u NONE -i NONE --cmd "colorscheme vim" --cmd "%s"']]=], - term or "", - (colorterm ~= nil and "COLORTERM="..colorterm or ""), - nvim_prog, - nvim_set)) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', nvim_set, + }, { + env = { + LANG = 'C', + TERM = term or '', + COLORTERM = colorterm or '', + }, + }) local tline if maxcolors == 8 then @@ -2483,14 +2492,16 @@ describe("TUI 'term' option", function() local function assert_term(term_envvar, term_expected) clear() - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - local cmd = string.format( - [=[['sh', '-c', 'LANG=C TERM=%s %s -u NONE -i NONE --cmd "%s"']]=], - term_envvar or "", - nvim_prog, - nvim_set) - screen = thelpers.screen_setup(0, cmd) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', nvim_set, + }, { + env = { + LANG = 'C', + TERM = term_envvar or '', + }, + }) local full_timeout = screen.timeout screen.timeout = 250 -- We want screen:expect() to fail quickly. @@ -2540,14 +2551,17 @@ describe("TUI", function() -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI. local function nvim_tui(extra_args) clear() - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - local cmd = string.format( - [=[['sh', '-c', 'LANG=C %s -u NONE -i NONE %s --cmd "colorscheme vim" --cmd "%s"']]=], - nvim_prog, - extra_args or "", - nvim_set) - screen = thelpers.screen_setup(0, cmd) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', nvim_set, + extra_args, + }, { + env = { + LANG = 'C', + }, + }) end it('-V3log logs terminfo values', function() @@ -2604,9 +2618,18 @@ describe('TUI bg color', function() local function setup_bg_test() clear() - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "set noswapfile", ' - ..'"-c", "autocmd OptionSet background echo \\"did OptionSet, yay!\\""]') + screen = thelpers.setup_child_nvim({ + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + 'colorscheme vim', + '--cmd', + 'set noswapfile notermguicolors', + '-c', + 'autocmd OptionSet background echo "did OptionSet, yay!"', + }) end before_each(setup_bg_test) @@ -2725,14 +2748,13 @@ describe("TUI as a client", function() set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "%s laststatus=2 background=dark"]', - nvim_prog, - server_pipe, - nvim_set - )) + local screen_server = thelpers.setup_child_nvim({ + '--listen', server_pipe, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + }) feed_data("iHello, World") screen_server:expect{grid=[[ @@ -2756,9 +2778,10 @@ describe("TUI as a client", function() ]]} set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Hello, Worl{1:d} | @@ -2798,9 +2821,10 @@ describe("TUI as a client", function() server:request('nvim_input', 'iHalloj!<Esc>') set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Halloj{1:!} | @@ -2836,9 +2860,10 @@ describe("TUI as a client", function() it("throws error when no server exists", function() clear() - local screen = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "127.0.0.1:2436546", "--remote-ui"]]=], - nvim_prog), 60) + local screen = thelpers.setup_child_nvim({ + '--server', '127.0.0.1:2436546', + '--remote-ui', + }, { cols = 60 }) screen:expect([[ Remote ui failed to start: {MATCH:.*}| @@ -2857,14 +2882,13 @@ describe("TUI as a client", function() set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "%s laststatus=2 background=dark"]', - nvim_prog, - server_pipe, - nvim_set - )) + local screen_server = thelpers.setup_child_nvim({ + '--listen', server_pipe, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + }) screen_server:expect{grid=[[ {1: } | {4:~ }| @@ -2897,9 +2921,10 @@ describe("TUI as a client", function() ]]} set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Hello, Worl{1:d} | diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 1dbbe76bac..719634aa2f 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -21,8 +21,12 @@ describe("shell command :!", function() local screen before_each(function() clear() - screen = child_session.screen_setup(0, '["'..helpers.nvim_prog.. - '", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "'..helpers.nvim_set..'"]') + screen = child_session.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', helpers.nvim_set, + }) screen:expect([[ {1: } | {4:~ }| diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 4ef7565ec5..0406cc9771 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -12,7 +12,7 @@ describe('screen', function() helpers.nvim_prog, '-u', 'NONE', '-i', 'NONE', - '-N', + '-n', '--cmd', 'set shortmess+=I background=light noswapfile belloff= noshowcmd noruler', '--cmd', 'colorscheme vim', '--embed', diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua index a9dab8431c..53899085e0 100644 --- a/test/functional/vimscript/json_functions_spec.lua +++ b/test/functional/vimscript/json_functions_spec.lua @@ -467,19 +467,18 @@ describe('json_decode() function', function() '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}}, '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]') - end) - - it('parses dictionaries with empty keys to special maps', function() - sp_decode_eq({_TYPE='map', _VAL={{'', 4}}}, - '{"": 4}') - sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, - '{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}') sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}, '{"": 3, "a": 1, "c": 4, "d": 2, "": 4}') sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}}, '[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]') end) + it('parses dictionaries with empty keys', function() + eq({[""] = 4}, funcs.json_decode('{"": 4}')) + eq({b = 3, a = 1, c = 4, d = 2, [""] = 4}, + funcs.json_decode('{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}')) + end) + it('parses dictionaries with keys with NUL bytes to special maps', function() sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}}, '{"a\\u0000\\nb": 4}') @@ -577,6 +576,8 @@ describe('json_encode() function', function() eq('{}', eval('json_encode({})')) eq('{"d": []}', funcs.json_encode({d={}})) eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}})) + -- Empty keys not allowed (yet?) in object_to_vim() (since 7c01d5ff9286). #25564 + -- eq('{"": []}', funcs.json_encode({['']={}})) end) it('cannot dump generic mapping with generic mapping keys and values', |