From 16336c486ecb5a60e85a870904316308c7d7fc3f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Sep 2022 02:20:47 +0200 Subject: [PATCH] feat(gen_help_html.lua): adapt to new parser - adapt to parser changes from https://github.com/vigoux/tree-sitter-vimdoc/pull/16 - numerous other generator improvements --- runtime/doc/api.txt | 53 +-- runtime/doc/develop.txt | 17 + runtime/doc/eval.txt | 2 +- runtime/doc/filetype.txt | 2 +- runtime/doc/helphelp.txt | 7 +- runtime/doc/luaref.txt | 110 ++--- runtime/doc/quickref.txt | 45 ++- runtime/doc/ui.txt | 201 +++++----- runtime/lua/vim/lsp/util.lua | 4 +- scripts/gen_help_html.lua | 465 ++++++++++++++-------- test/functional/core/startup_spec.lua | 4 +- test/functional/helpers.lua | 13 +- test/functional/lua/help_spec.lua | 31 +- test/functional/options/defaults_spec.lua | 58 +-- 14 files changed, 600 insertions(+), 412 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index f0145344a4..59d1d36be9 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -83,6 +83,7 @@ and |rpcnotify()|: let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true}) echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"') call jobstop(nvim) +< ============================================================================== API Definitions *api-definitions* @@ -92,7 +93,7 @@ The Nvim C API defines custom types for all function parameters. Some are just typedefs around C99 standard types, others are Nvim-defined data structures. Basic types ~ - +> API Type C type ------------------------------------------------------------------------ Nil @@ -103,7 +104,7 @@ Basic types ~ Array Dictionary (msgpack: map) Object - +< Note: empty Array is accepted as a valid argument for Dictionary parameter. Special types (msgpack EXT) ~ @@ -115,13 +116,13 @@ Special types (msgpack EXT) ~ The EXT object data is the (integer) object handle. The EXT type codes given in the |api-metadata| `types` key are stable: they will not change and are thus forward-compatible. - +> EXT Type C type Data ------------------------------------------------------------------------ Buffer enum value kObjectTypeBuffer |bufnr()| Window enum value kObjectTypeWindow |window-ID| Tabpage enum value kObjectTypeTabpage internal handle - +< *api-indexing* Most of the API uses 0-based indices, and ranges are end-exclusive. For the @@ -130,19 +131,19 @@ end of a range, -1 denotes the last line/column. Exception: the following API functions use "mark-like" indexing (1-based lines, 0-based columns): - |nvim_get_mark()| - |nvim_buf_get_mark()| - |nvim_buf_set_mark()| - |nvim_win_get_cursor()| - |nvim_win_set_cursor()| +- |nvim_get_mark()| +- |nvim_buf_get_mark()| +- |nvim_buf_set_mark()| +- |nvim_win_get_cursor()| +- |nvim_win_set_cursor()| Exception: the following API functions use |extmarks| indexing (0-based indices, end-inclusive): - |nvim_buf_del_extmark()| - |nvim_buf_get_extmark_by_id()| - |nvim_buf_get_extmarks()| - |nvim_buf_set_extmark()| +- |nvim_buf_del_extmark()| +- |nvim_buf_get_extmark_by_id()| +- |nvim_buf_get_extmarks()| +- |nvim_buf_set_extmark()| *api-fast* Most API functions are "deferred": they are queued on the main loop and @@ -162,19 +163,19 @@ and return values. Nvim exposes its API metadata as a Dictionary with these items: -version Nvim version, API level/compatibility -version.api_level API version integer *api-level* -version.api_compatible API is backwards-compatible with this level -version.api_prerelease Declares the API as unstable/unreleased > - (version.api_prerelease && fn.since == version.api_level) -functions API function signatures, containing |api-types| info - describing the return value and parameters. -ui_events |UI| event signatures -ui_options Supported |ui-option|s -{fn}.since API level where function {fn} was introduced -{fn}.deprecated_since API level where function {fn} was deprecated -types Custom handle types defined by Nvim -error_types Possible error types returned by API functions +- version Nvim version, API level/compatibility +- version.api_level API version integer *api-level* +- version.api_compatible API is backwards-compatible with this level +- version.api_prerelease Declares the API as unstable/unreleased + `(version.api_prerelease && fn.since == version.api_level)` +- functions API function signatures, containing |api-types| info + describing the return value and parameters. +- ui_events |UI| event signatures +- ui_options Supported |ui-option|s +- {fn}.since API level where function {fn} was introduced +- {fn}.deprecated_since API level where function {fn} was deprecated +- types Custom handle types defined by Nvim +- error_types Possible error types returned by API functions About the `functions` map: diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 4f17e7d34a..ebb0dfeb4e 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -151,6 +151,23 @@ DOCUMENTATION *dev-doc* /// @param dirname The path fragment before `pend` < +Documentation format ~ + +For Nvim-owned docs, use the following strict subset of "vimdoc" to ensure +the help doc renders nicely in other formats (such as HTML: +https://neovim.io/doc/user ). + +Strict "vimdoc" subset: + +- Use lists (like this!) prefixed with "-", "*", or "•", for adjacent lines + that you don't want auto-wrapped. Lists are always rendered with "flow" + (soft-wrapped) layout instead of preformatted (hard-wrapped) layout common + in legacy :help docs. +- Separate blocks (paragraphs) of content by a blank line(s). +- Do not use indentation in random places—that prevents the page from using + "flow" layout. If you need a preformatted section, put it in + a |help-codeblock| starting with ">". + C docstrings ~ Nvim API documentation lives in the source code, as docstrings (Doxygen diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ae28977556..6a9fb6d03c 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4119,7 +4119,7 @@ This example sorts lines with a specific compare function. > As a one-liner: > :call setline(1, sort(getline(1, '$'), function("Strcmp"))) - +< scanf() replacement ~ *sscanf* diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 9f8ef248f8..ac54a6b6ca 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -178,7 +178,7 @@ If a file type that you want to use is not detected yet, there are a few ways to add it. In any way, it's better not to modify the $VIMRUNTIME/filetype.lua or $VIMRUNTIME/filetype.vim files. They will be overwritten when installing a new version of Nvim. The following explains the legacy Vim mechanism (enabled -if |do_legacy_filetype| is set). For Nvim's default mechanism, see +if |g:do_legacy_filetype| is set). For Nvim's default mechanism, see |vim.filetype.add()|. A. If you want to overrule all default file type checks. diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 569995d319..4758cd37c6 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -212,12 +212,6 @@ This is done when viewing the file in Vim, the file itself is not changed. It is done by going through all help files and obtaining the first line of each file. The files in $VIMRUNTIME/doc are skipped. - *help-xterm-window* -If you want to have the help in another xterm window, you could use this -command: > - :!xterm -e vim +help & -< - *:helpt* *:helptags* *E150* *E151* *E152* *E153* *E154* *E670* *E856* :helpt[ags] [++t] {dir} @@ -372,6 +366,7 @@ To separate sections in a help file, place a series of '=' characters in a line starting from the first column. The section separator line is highlighted differently. + *help-codeblock* To quote a block of ex-commands verbatim, place a greater than (>) character at the end of the line before the block and a less than (<) character as the first non-blank on a line following the block. Any line starting in column 1 diff --git a/runtime/doc/luaref.txt b/runtime/doc/luaref.txt index ecb92d4bb6..0b04005e1a 100644 --- a/runtime/doc/luaref.txt +++ b/runtime/doc/luaref.txt @@ -55,7 +55,6 @@ Lua means "moon" in Portuguese and is pronounced LOO-ah. ============================================================================== 2 THE LANGUAGE *luaref-language* -============================================================================== This section describes the lexis, the syntax, and the semantics of Lua. In other words, this section describes which tokens are valid, how they can be @@ -450,21 +449,22 @@ through an arithmetic progression. It has the following syntax: < The `block` is repeated for `name` starting at the value of the first `exp`, until it passes the second `exp` by steps of the third `exp`. More precisely, -a `for` statement like +a `for` statement like > - `for var =` `e1, e2, e3` `do` `block` `end` + for var = e1, e2, e3 do block end -is equivalent to the code: +< is equivalent to the code: > - `do` - `local` `var, limit, step` `= tonumber(e1), tonumber(e2), tonumber(e3)` - `if not (` `var` `and` `limit` `and` `step` `) then error() end` - `while (` `step` `>0 and` `var` `<=` `limit` `)` - `or (` `step` `<=0 and` `var` `>=` `limit` `) do` - `block` - `var` `=` `var` `+` `step` - `end` - `end` + do + local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3) + if not ( var and limit and step ) then error() end + while ( step >0 and var <= limit ) + or ( step <=0 and var >= limit ) do + block + var = var + step + end + end +< Note the following: @@ -490,18 +490,18 @@ A `for` statement like `for` `var1, ..., varn` `in` `explist` `do` `block` `end` -is equivalent to the code: - - `do` - `local` `f, s, var` `=` `explist` - `while true do` - `local` `var1, ..., varn` `=` `f(s, var)` - `var` `=` `var1` - `if` `var` `== nil then break end` - `block` - `end` - `end` +is equivalent to the code: > + do + local f, s, var = explist + while true do + local var1, ..., varn = f(s, var) + var = var1 + if var == nil then break end + block + end + end +< Note the following: - `explist` is evaluated only once. Its results are an iterator function, @@ -1871,25 +1871,25 @@ lua_gc *lua_gc()* This function performs several tasks, according to the value of the parameter `what`: - `LUA_GCSTOP` stops the garbage collector. - `LUA_GCRESTART` restarts the garbage collector. - `LUA_GCCOLLECT` performs a full garbage-collection cycle. - `LUA_GCCOUNT` returns the current amount of memory (in Kbytes) in + - `LUA_GCSTOP` stops the garbage collector. + - `LUA_GCRESTART` restarts the garbage collector. + - `LUA_GCCOLLECT` performs a full garbage-collection cycle. + - `LUA_GCCOUNT` returns the current amount of memory (in Kbytes) in use by Lua. - `LUA_GCCOUNTB` returns the remainder of dividing the current + - `LUA_GCCOUNTB` returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024. - `LUA_GCSTEP` performs an incremental step of garbage collection. + - `LUA_GCSTEP` performs an incremental step of garbage collection. The step "size" is controlled by `data` (larger values mean more steps) in a non-specified way. If you want to control the step size you must experimentally tune the value of `data`. The function returns 1 if the step finished a garbage-collection cycle. - `LUA_GCSETPAUSE` sets `data` /100 as the new value for the + - `LUA_GCSETPAUSE` sets `data` /100 as the new value for the `pause` of the collector (see |luaref-langGC|). The function returns the previous value of the pause. - `LUA_GCSETSTEPMUL` sets `data` /100 as the new value for the + - `LUA_GCSETSTEPMUL`sets `data` /100 as the new value for the `step` `multiplier` of the collector (see |luaref-langGC|). The function returns the previous value of the step multiplier. @@ -2717,20 +2717,22 @@ need "inside information" from the interpreter. lua_Debug *lua_Debug()* - `typedef struct lua_Debug {` - `int event;` - `const char *name; /* (n) */` - `const char *namewhat; /* (n) */` - `const char *what; /* (S) */` - `const char *source; /* (S) */` - `int currentline; /* (l) */` - `int nups; /* (u) number of upvalues */` - `int linedefined; /* (S) */` - `int lastlinedefined; /* (S) */` - `char short_src[LUA_IDSIZE]; /* (S) */` - `/* private part */` - `other fields` - `} lua_Debug;` +> + typedef struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) */ + const char *what; /* (S) */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + other fields + } lua_Debug; +< A structure used to carry different pieces of information about an active function. `lua_getstack` (see |lua_getstack()|) fills only the private part @@ -2739,28 +2741,28 @@ useful information, call `lua_getinfo` (see |lua_getinfo()|). The fields of `lua_Debug` have the following meaning: - `source` If the function was defined in a string, then `source` is +- `source` If the function was defined in a string, then `source` is that string. If the function was defined in a file, then `source` starts with a `@` followed by the file name. - `short_src` a "printable" version of `source`, to be used in error messages. - `linedefined` the line number where the definition of the function starts. - `lastlinedefined` the line number where the definition of the function ends. - `what` the string `"Lua"` if the function is a Lua function, +- `short_src` a "printable" version of `source`, to be used in error messages. +- `linedefined` the line number where the definition of the function starts. +- `lastlinedefined` the line number where the definition of the function ends. +- `what` the string `"Lua"` if the function is a Lua function, `"C"` if it is a C function, `"main"` if it is the main part of a chunk, and `"tail"` if it was a function that did a tail call. In the latter case, Lua has no other information about the function. - `currentline` the current line where the given function is executing. +- `currentline` the current line where the given function is executing. When no line information is available, `currentline` is set to -1. - `name` a reasonable name for the given function. Because +- `name` a reasonable name for the given function. Because functions in Lua are first-class values, they do not have a fixed name: some functions may be the value of multiple global variables, while others may be stored only in a table field. The `lua_getinfo` function checks how the function was called to find a suitable name. If it cannot find a name, then `name` is set to `NULL`. - `namewhat` explains the `name` field. The value of `namewhat` can be +- `namewhat` explains the `name` field. The value of `namewhat` can be `"global"`, `"local"`, `"method"`, `"field"`, `"upvalue"`, or `""` (the empty string), according to how the function was called. (Lua uses the empty string when diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 23ce16a40a..bdb0f7447c 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -3,7 +3,8 @@ VIM REFERENCE MANUAL by Bram Moolenaar - Quick reference guide +============================================================================== +Quick reference guide *quickref* *Contents* tag subject tag subject ~ @@ -29,11 +30,11 @@ |Q_to| Text objects |Q_gu| GUI commands |Q_fo| Folding ------------------------------------------------------------------------------- -N is used to indicate an optional count that can be given before the command. ------------------------------------------------------------------------------ *Q_lr* Left-right motions +N is used to indicate an optional count that can be given before the command. + |h| N h left (also: CTRL-H, , or key) |l| N l right (also: or key) |0| 0 to first character in the line (also: key) @@ -56,6 +57,7 @@ N is used to indicate an optional count that can be given before the command. |;| N ; repeat the last "f", "F", "t", or "T" N times |,| N , repeat the last "f", "F", "t", or "T" N times in opposite direction + ------------------------------------------------------------------------------ *Q_ud* Up-down motions @@ -73,6 +75,7 @@ N is used to indicate an optional count that can be given before the command. given, otherwise it is the |%| command |gk| N gk up N screen lines (differs from "k" when line wraps) |gj| N gj down N screen lines (differs from "j" when line wraps) + ------------------------------------------------------------------------------ *Q_tm* Text object motions @@ -105,6 +108,7 @@ N is used to indicate an optional count that can be given before the command. |]#| N ]# N times forward to unclosed "#else" or "#endif" |[star| N [* N times back to start of comment "/*" |]star| N ]* N times forward to end of comment "*/" + ------------------------------------------------------------------------------ *Q_pa* Pattern searches @@ -168,6 +172,7 @@ N is used to indicate an optional count that can be given before the command. b[+num] [num] identical to s[+num] above (mnemonic: begin) b[-num] [num] identical to s[-num] above (mnemonic: begin) ;{search-command} execute {search-command} next + ------------------------------------------------------------------------------ *Q_ma* Marks and motions @@ -188,6 +193,7 @@ N is used to indicate an optional count that can be given before the command. |CTRL-O| N CTRL-O go to Nth older position in jump list |CTRL-I| N CTRL-I go to Nth newer position in jump list |:ju| :ju[mps] print the jump list + ------------------------------------------------------------------------------ *Q_vm* Various motions @@ -202,6 +208,7 @@ N is used to indicate an optional count that can be given before the command. |go| N go go to Nth byte in the buffer |:go| :[range]go[to] [off] go to [off] byte in the buffer + ------------------------------------------------------------------------------ *Q_ta* Using tags @@ -229,6 +236,7 @@ N is used to indicate an optional count that can be given before the command. |:ptjump| :ptj[ump] like ":tjump" but show tag in preview window |:pclose| :pc[lose] close tag preview window |CTRL-W_z| CTRL-W z close tag preview window + ------------------------------------------------------------------------------ *Q_sc* Scrolling @@ -247,6 +255,7 @@ These only work when 'wrap' is off: |zl| N zl scroll screen N characters to the left |zH| N zH scroll screen half a screenwidth to the right |zL| N zL scroll screen half a screenwidth to the left + ------------------------------------------------------------------------------ *Q_in* Inserting text @@ -263,6 +272,7 @@ These only work when 'wrap' is off: in Visual block mode: |v_b_I| I insert the same text in front of all the selected lines |v_b_A| A append the same text after all the selected lines + ------------------------------------------------------------------------------ *Q_ai* Insert mode keys @@ -279,6 +289,7 @@ moving around: |i_| shift-up/down one screenful backward/forward |i_| cursor after last character in the line |i_| cursor to first character in the line + ------------------------------------------------------------------------------ *Q_ss* Special keys in Insert mode @@ -313,6 +324,7 @@ moving around: |i_0_CTRL-D| 0 CTRL-D delete all indent in the current line |i_^_CTRL-D| ^ CTRL-D delete all indent in the current line, restore indent in next line + ------------------------------------------------------------------------------ *Q_di* Digraphs @@ -325,12 +337,14 @@ In Insert or Command-line mode: enter digraph |i_digraph| {char1} {char2} enter digraph if 'digraph' option set + ------------------------------------------------------------------------------ *Q_si* Special inserts |:r| :r [file] insert the contents of [file] below the cursor |:r!| :r! {command} insert the standard output of {command} below the cursor + ------------------------------------------------------------------------------ *Q_de* Deleting text @@ -346,6 +360,7 @@ In Insert or Command-line mode: |gJ| N gJ like "J", but without inserting spaces |v_gJ| {visual}gJ like "{visual}J", but without inserting spaces |:d| :[range]d [x] delete [range] lines [into register x] + ------------------------------------------------------------------------------ *Q_cm* Copying and moving text @@ -363,6 +378,7 @@ In Insert or Command-line mode: |[p| N [p like P, but adjust indent to current line |gp| N gp like p, but leave cursor after the new text |gP| N gP like P, but leave cursor after the new text + ------------------------------------------------------------------------------ *Q_ch* Changing text @@ -418,6 +434,7 @@ In Insert or Command-line mode: left-align the lines in [range] (with [indent]) |:ri| :[range]ri[ght] [width] right-align the lines in [range] + ------------------------------------------------------------------------------ *Q_co* Complex changes @@ -444,6 +461,7 @@ In Insert or Command-line mode: |:ret| :[range]ret[ab][!] [tabstop] set 'tabstop' to new value and adjust white space accordingly + ------------------------------------------------------------------------------ *Q_vi* Visual mode @@ -457,6 +475,7 @@ In Insert or Command-line mode: |v_v| v highlight characters or stop highlighting |v_V| V highlight linewise or stop highlighting |v_CTRL-V| CTRL-V highlight blockwise or stop highlighting + ------------------------------------------------------------------------------ *Q_to* Text objects (only in Visual mode or after an operator) @@ -509,6 +528,7 @@ In Insert or Command-line mode: |:sl| :sl[eep] [sec] don't do anything for [sec] seconds |gs| N gs goto Sleep for N seconds + ------------------------------------------------------------------------------ *Q_km* Key mapping @@ -556,6 +576,7 @@ In Insert or Command-line mode: like ":mkvimrc", but store current files, windows, etc. too, to be able to continue this session later + ------------------------------------------------------------------------------ *Q_ab* Abbreviations @@ -570,6 +591,7 @@ In Insert or Command-line mode: |:abclear| :abc[lear] remove all abbreviations |:cabclear| :cabc[lear] remove all abbr's for Cmdline mode |:iabclear| :iabc[lear] remove all abbr's for Insert mode + ------------------------------------------------------------------------------ *Q_op* Options @@ -940,18 +962,21 @@ Short explanation of each option: *option-list* 'writeany' 'wa' write to file with no need for "!" override 'writebackup' 'wb' make a backup before overwriting a file 'writedelay' 'wd' delay this many msec for each char (for debug) + ------------------------------------------------------------------------------ *Q_ur* Undo/Redo commands |u| N u undo last N changes |CTRL-R| N CTRL-R redo last N undone changes |U| U restore last changed line + ------------------------------------------------------------------------------ *Q_et* External commands |:!| :!{command} execute {command} with a shell |K| K lookup keyword under the cursor with 'keywordprg' program (default: "man") + ------------------------------------------------------------------------------ *Q_qf* Quickfix commands @@ -975,6 +1000,7 @@ Short explanation of each option: *option-list* error |:grep| :gr[ep] [args] execute 'grepprg' to find matches and jump to the first one + ------------------------------------------------------------------------------ *Q_vc* Various commands @@ -1000,6 +1026,7 @@ Short explanation of each option: *option-list* unsaved changes or read-only files |:browse| :browse {command} open/read/write file, using a file selection dialog + ------------------------------------------------------------------------------ *Q_ce* Command-line editing @@ -1046,6 +1073,7 @@ Context-sensitive completion on the command-line: to next match |c_CTRL-P| CTRL-P after 'wildchar' with multiple matches: go to previous match + ------------------------------------------------------------------------------ *Q_ra* Ex ranges @@ -1066,6 +1094,7 @@ Context-sensitive completion on the command-line: (default: 1) |:range| -[num] subtract [num] from the preceding line number (default: 1) + ------------------------------------------------------------------------------ *Q_ex* Special Ex characters @@ -1098,6 +1127,7 @@ Context-sensitive completion on the command-line: |::r| :r root (extension removed) |::e| :e extension |::s| :s/{pat}/{repl}/ substitute {pat} with {repl} + ------------------------------------------------------------------------------ *Q_st* Starting Vim @@ -1134,6 +1164,7 @@ Context-sensitive completion on the command-line: |--help| --help show list of arguments and exit |--version| --version show version info and exit |--| - read file from stdin + ------------------------------------------------------------------------------ *Q_ed* Editing a file @@ -1153,6 +1184,7 @@ Context-sensitive completion on the command-line: position |:file| :f[ile] {name} set the current file name to {name} |:files| :files show alternate file names + ------------------------------------------------------------------------------ *Q_fl* Using the argument list |argument-list| @@ -1173,6 +1205,7 @@ Context-sensitive completion on the command-line: |:Next| :N[ext] :sN[ext] edit previous file |:first| :fir[st] :sfir[st] edit first file |:last| :la[st] :sla[st] edit last file + ------------------------------------------------------------------------------ *Q_wq* Writing and quitting @@ -1210,6 +1243,7 @@ Context-sensitive completion on the command-line: |:stop| :st[op][!] suspend Vim or start new shell; if 'aw' option is set and [!] not given write the buffer |CTRL-Z| CTRL-Z same as ":stop" + ------------------------------------------------------------------------------ *Q_ac* Automatic Commands @@ -1241,6 +1275,7 @@ Context-sensitive completion on the command-line: with {pat} |:autocmd| :au! {event} {pat} {cmd} remove all autocommands for {event} with {pat} and enter new one + ------------------------------------------------------------------------------ *Q_wi* Multi-window commands @@ -1286,6 +1321,7 @@ Context-sensitive completion on the command-line: |CTRL-W_>| CTRL-W > increase current window width |CTRL-W_bar| CTRL-W | set current window width (default: widest possible) + ------------------------------------------------------------------------------ *Q_bu* Buffer list commands @@ -1307,6 +1343,7 @@ Context-sensitive completion on the command-line: |:bfirst| :bfirst :sbfirst to first arg/buf |:blast| :blast :sblast to last arg/buf |:bmodified| :[N]bmod [N] :[N]sbmod [N] to Nth modified buf + ------------------------------------------------------------------------------ *Q_sy* Syntax Highlighting @@ -1333,6 +1370,7 @@ Context-sensitive completion on the command-line: |:filetype| :filetype plugin indent on switch on file type detection, with automatic indenting and settings + ------------------------------------------------------------------------------ *Q_gu* GUI commands @@ -1345,6 +1383,7 @@ Context-sensitive completion on the command-line: add toolbar item, giving {rhs} |:tmenu| :tmenu {mpath} {text} add tooltip to menu {mpath} |:unmenu| :unmenu {mpath} remove menu {mpath} + ------------------------------------------------------------------------------ *Q_fo* Folding diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 2d0c8d3b75..7ce6cd251f 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -23,40 +23,37 @@ screen grid with a size of width × height cells. This is typically done by an embedder at startup (see |ui-startup|), but UIs can also connect to a running Nvim instance and invoke nvim_ui_attach(). The `options` parameter is a map with these (optional) keys: + *ui-rgb* - `rgb` Decides the color format. - true: (default) 24-bit RGB colors - false: Terminal colors (8-bit, max 256) +- `rgb` Decides the color format. + - true: (default) 24-bit RGB colors + - false: Terminal colors (8-bit, max 256) *ui-override* - `override` Decides how UI capabilities are resolved. - true: Enable requested UI capabilities, even - if not supported by all connected UIs - (including |TUI|). - false: (default) Disable UI capabilities not - supported by all connected UIs - (including TUI). +- `override` Decides how UI capabilities are resolved. + - true: Enable requested UI capabilities, even if not + supported by all connected UIs (including |TUI|). + - false: (default) Disable UI capabilities not + supported by all connected UIs (including TUI). *ui-ext-options* - `ext_cmdline` Externalize the cmdline. |ui-cmdline| - `ext_hlstate` Detailed highlight state. |ui-hlstate| - Sets `ext_linegrid` implicitly. - `ext_linegrid` Line-based grid events. |ui-linegrid| - Deactivates |ui-grid-old| implicitly. - `ext_messages` Externalize messages. |ui-messages| - Sets `ext_linegrid` and `ext_cmdline` implicitly. - `ext_multigrid` Per-window grid events. |ui-multigrid| - Sets `ext_linegrid` implicitly. - `ext_popupmenu` Externalize |popupmenu-completion| and - 'wildmenu'. |ui-popupmenu| - `ext_tabline` Externalize the tabline. |ui-tabline| - `ext_termcolors` Use external default colors. - `term_name` Sets the name of the terminal 'term'. - `term_colors` Sets the number of supported colors 't_Co'. - `term_background` Sets the default value of 'background'. - `stdin_fd` Read buffer from `fd` as if it was a stdin pipe - This option can only used by |--embed| ui, - see |ui-startup-stdin|. - - +- `ext_cmdline` Externalize the cmdline. |ui-cmdline| +- `ext_hlstate` Detailed highlight state. |ui-hlstate| + Sets `ext_linegrid` implicitly. +- `ext_linegrid` Line-based grid events. |ui-linegrid| + Deactivates |ui-grid-old| implicitly. +- `ext_messages` Externalize messages. |ui-messages| + Sets `ext_linegrid` and `ext_cmdline` implicitly. +- `ext_multigrid` Per-window grid events. |ui-multigrid| + Sets `ext_linegrid` implicitly. +- `ext_popupmenu` Externalize |popupmenu-completion| and + 'wildmenu'. |ui-popupmenu| +- `ext_tabline` Externalize the tabline. |ui-tabline| +- `ext_termcolors` Use external default colors. +- `term_name` Sets the name of the terminal 'term'. +- `term_colors` Sets the number of supported colors 't_Co'. +- `term_background` Sets the default value of 'background'. +- `stdin_fd` Read buffer from `fd` as if it was a stdin pipe + This option can only used by |--embed| ui, + see |ui-startup-stdin|. Specifying an unknown option is an error; UIs can check the |api-metadata| `ui_options` key for supported options. @@ -164,13 +161,13 @@ Global Events *ui-global* The following UI events are always emitted, and describe global state of the editor. -["set_title", title] -["set_icon", icon] +["set_title", title] ~ +["set_icon", icon] ~ Set the window title, and icon (minimized) window title, respectively. In windowing systems not distinguishing between the two, "set_icon" can be ignored. -["mode_info_set", cursor_style_enabled, mode_info] +["mode_info_set", cursor_style_enabled, mode_info] ~ `cursor_style_enabled` is a boolean indicating if the UI should set the cursor style. `mode_info` is a list of mode property maps. The current mode is given by the `mode_idx` field of the `mode_change` @@ -197,21 +194,21 @@ the editor. `hl_id`: Use `attr_id` instead. `hl_lm`: Use `attr_id_lm` instead. -["option_set", name, value] +["option_set", name, value] ~ UI-related option changed, where `name` is one of: - 'arabicshape' - 'ambiwidth' - 'emoji' - 'guifont' - 'guifontwide' - 'linespace' - 'mousefocus' - 'mousemoveevent' - 'pumblend' - 'showtabline' - 'termguicolors' - "ext_*" (all |ui-ext-options|) + - 'arabicshape' + - 'ambiwidth' + - 'emoji' + - 'guifont' + - 'guifontwide' + - 'linespace' + - 'mousefocus' + - 'mousemoveevent' + - 'pumblend' + - 'showtabline' + - 'termguicolors' + - "ext_*" (all |ui-ext-options|) Triggered when the UI first connects to Nvim, and whenever an option is changed by the user or a plugin. @@ -224,7 +221,7 @@ the editor. however a UI might still use such options when rendering raw text sent from Nvim, like for |ui-cmdline|. -["mode_change", mode, mode_idx] +["mode_change", mode, mode_idx] ~ Editor mode changed. The `mode` parameter is a string representing the current mode. `mode_idx` is an index into the array emitted in the `mode_info_set` event. UIs should change the cursor style @@ -233,30 +230,30 @@ the editor. instance more submodes and temporary states might be represented as separate modes. -["mouse_on"] -["mouse_off"] +["mouse_on"] ~ +["mouse_off"] ~ 'mouse' was enabled/disabled in the current editor mode. Useful for a terminal UI, or embedding into an application where Nvim mouse would conflict with other usages of the mouse. Other UI:s may ignore this event. -["busy_start"] -["busy_stop"] +["busy_start"] ~ +["busy_stop"] ~ Indicates to the UI that it must stop rendering the cursor. This event is misnamed and does not actually have anything to do with busyness. -["suspend"] +["suspend"] ~ |:suspend| command or |CTRL-Z| mapping is used. A terminal client (or another client where it makes sense) could suspend itself. Other clients can safely ignore it. -["update_menu"] +["update_menu"] ~ The menu mappings changed. -["bell"] -["visual_bell"] +["bell"] ~ +["visual_bell"] ~ Notify the user with an audible or visual bell, respectively. -["flush"] +["flush"] ~ Nvim is done redrawing the screen. For an implementation that renders to an internal buffer, this is the time to display the redrawn parts to the user. @@ -279,11 +276,11 @@ be created; to enable per-window grids, activate |ui-multigrid|. Highlight attribute groups are predefined. UIs should maintain a table to map numerical highlight ids to the actual attributes. -["grid_resize", grid, width, height] +["grid_resize", grid, width, height] ~ Resize a `grid`. If `grid` wasn't seen by the client before, a new grid is being created with this size. -["default_colors_set", rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg] +["default_colors_set", rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg] ~ The first three arguments set the default foreground, background and special colors respectively. `cterm_fg` and `cterm_bg` specifies the default color codes to use in a 256-color terminal. @@ -300,7 +297,7 @@ numerical highlight ids to the actual attributes. screen with changed background color itself. *ui-event-hl_attr_define* -["hl_attr_define", id, rgb_attr, cterm_attr, info] +["hl_attr_define", id, rgb_attr, cterm_attr, info] ~ Add a highlight with `id` to the highlight table, with the attributes specified by the `rgb_attr` and `cterm_attr` dicts, with the following (all optional) keys. @@ -346,7 +343,7 @@ numerical highlight ids to the actual attributes. `info` is an empty array by default, and will be used by the |ui-hlstate| extension explained below. -["hl_group_set", name, hl_id] +["hl_group_set", name, hl_id] ~ The bulitin highlight group `name` was set to use the attributes `hl_id` defined by a previous `hl_attr_define` call. This event is not needed to render the grids which use attribute ids directly, but is useful @@ -355,7 +352,7 @@ numerical highlight ids to the actual attributes. use the |hl-Pmenu| family of builtin highlights. *ui-event-grid_line* -["grid_line", grid, row, col_start, cells] +["grid_line", grid, row, col_start, cells] ~ Redraw a continuous part of a `row` on a `grid`, starting at the column `col_start`. `cells` is an array of arrays each with 1 to 3 items: `[text(, hl_id, repeat)]` . `text` is the UTF-8 text that should be put in @@ -374,19 +371,19 @@ numerical highlight ids to the actual attributes. enough to cover the remaining line, will be sent when the rest of the line should be cleared. -["grid_clear", grid] +["grid_clear", grid] ~ Clear a `grid`. -["grid_destroy", grid] +["grid_destroy", grid] ~ `grid` will not be used anymore and the UI can free any data associated with it. -["grid_cursor_goto", grid, row, column] +["grid_cursor_goto", grid, row, column] ~ Makes `grid` the current grid and `row, column` the cursor position on this grid. This event will be sent at most once in a `redraw` batch and indicates the visible cursor position. -["grid_scroll", grid, top, bot, left, right, rows, cols] +["grid_scroll", grid, top, bot, left, right, rows, cols] ~ Scroll a region of `grid`. This is semantically unrelated to editor |scrolling|, rather this is an optimized way to say "copy these screen cells". @@ -439,30 +436,30 @@ Grid Events (cell-based) *ui-grid-old* This is the legacy representation of the screen grid, emitted if |ui-linegrid| is not active. New UIs should implement |ui-linegrid| instead. -["resize", width, height] +["resize", width, height] ~ The grid is resized to `width` and `height` cells. -["clear"] +["clear"] ~ Clear the grid. -["eol_clear"] +["eol_clear"] ~ Clear from the cursor position to the end of the current line. -["cursor_goto", row, col] +["cursor_goto", row, col] ~ Move the cursor to position (row, col). Currently, the same cursor is used to define the position for text insertion and the visible cursor. However, only the last cursor position, after processing the entire array in the "redraw" event, is intended to be a visible cursor position. -["update_fg", color] -["update_bg", color] -["update_sp", color] +["update_fg", color] ~ +["update_bg", color] ~ +["update_sp", color] ~ Set the default foreground, background and special colors respectively. *ui-event-highlight_set* -["highlight_set", attrs] +["highlight_set", attrs] ~ Set the attributes that the next text put on the grid will have. `attrs` is a dict with the keys below. Any absent key is reset to its default value. Color defaults are set by the `update_fg` etc @@ -482,18 +479,18 @@ is not active. New UIs should implement |ui-linegrid| instead. `underdotted`: underdotted text. The dots have `special` color. `underdashed`: underdashed text. The dashes have `special` color. -["put", text] +["put", text] ~ The (utf-8 encoded) string `text` is put at the cursor position (and the cursor is advanced), with the highlights as set by the last `highlight_set` update. -["set_scroll_region", top, bot, left, right] +["set_scroll_region", top, bot, left, right] ~ Define the scroll region used by `scroll` below. Note: ranges are end-inclusive, which is inconsistent with API conventions. -["scroll", count] +["scroll", count] ~ Scroll the text in the scroll region. The diagrams below illustrate what will happen, depending on the scroll direction. "=" is used to represent the SR(scroll region) boundaries and "-" the moved rectangles. @@ -588,29 +585,29 @@ A window can be hidden and redisplayed without its grid being deallocated. This can happen multiple times for the same window, for instance when switching tabs. -["win_pos", grid, win, start_row, start_col, width, height] +["win_pos", grid, win, start_row, start_col, width, height] ~ Set the position and size of the grid in Nvim (i.e. the outer grid size). If the window was previously hidden, it should now be shown again. -["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable] +["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable] ~ Display or reconfigure floating window `win`. The window should be displayed above another grid `anchor_grid` at the specified position `anchor_row` and `anchor_col`. For the meaning of `anchor` and more details of positioning, see |nvim_open_win()|. -["win_external_pos", grid, win] +["win_external_pos", grid, win] ~ Display or reconfigure external window `win`. The window should be displayed as a separate top-level window in the desktop environment, or something similar. -["win_hide", grid] +["win_hide", grid] ~ Stop displaying the window. The window can be shown again later. -["win_close", grid] +["win_close", grid] ~ Close the window. -["msg_set_pos", grid, row, scrolled, sep_char] +["msg_set_pos", grid, row, scrolled, sep_char] ~ Display messages on `grid`. The grid will be displayed at `row` on the default grid (grid=1), covering the full column width. `scrolled` indicates whether the message area has been scrolled to cover other @@ -621,13 +618,13 @@ tabs. When |ui-messages| is active, no message grid is used, and this event will not be sent. -["win_viewport", grid, win, topline, botline, curline, curcol] +["win_viewport", grid, win, topline, botline, curline, curcol] ~ Indicates the range of buffer text displayed in the window, as well as the cursor position in the buffer. All positions are zero-based. `botline` is set to one more than the line count of the buffer, if there are filler lines past the end. -["win_extmark", grid, win, ns_id, mark_id, row, col] +["win_extmark", grid, win, ns_id, mark_id, row, col] ~ Updates the position of an extmark which is currently visible in a window. Only emitted if the mark has the `ui_watched` attribute. @@ -639,7 +636,7 @@ Activated by the `ext_popupmenu` |ui-option|. This UI extension delegates presentation of the |popupmenu-completion| and command-line 'wildmenu'. -["popupmenu_show", items, selected, row, col, grid] +["popupmenu_show", items, selected, row, col, grid] ~ Show |popupmenu-completion|. `items` is an array of completion items to show; each item is an array of the form [word, kind, menu, info] as defined at |complete-items|, except that `word` is replaced by `abbr` @@ -651,12 +648,12 @@ command-line 'wildmenu'. set to -1 to indicate the popupmenu should be anchored to the external cmdline. Then `col` will be a byte position in the cmdline text. -["popupmenu_select", selected] +["popupmenu_select", selected] ~ Select an item in the current popupmenu. `selected` is a zero-based index into the array of items from the last popupmenu_show event, or -1 if no item is selected. -["popupmenu_hide"] +["popupmenu_hide"] ~ Hide the popupmenu. ============================================================================== @@ -664,7 +661,7 @@ Tabline Events *ui-tabline* Activated by the `ext_tabline` |ui-option|. -["tabline_update", curtab, tabs, curbuf, buffers] +["tabline_update", curtab, tabs, curbuf, buffers] ~ Tabline was updated. UIs should present this data in a custom tabline widget. Note: options `curbuf` + `buffers` were added in API7. curtab: Current Tabpage @@ -680,7 +677,7 @@ Activated by the `ext_cmdline` |ui-option|. This UI extension delegates presentation of the |cmdline| (except 'wildmenu'). For command-line 'wildmenu' UI events, activate |ui-popupmenu|. -["cmdline_show", content, pos, firstc, prompt, indent, level] +["cmdline_show", content, pos, firstc, prompt, indent, level] ~ content: List of [attrs, string] [[{}, "t"], [attrs, "est"], ...] @@ -703,10 +700,10 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|. prompt has level 2. A command line invoked from the |cmdline-window| has a higher level than than the edited command line. -["cmdline_pos", pos, level] +["cmdline_pos", pos, level] ~ Change the cursor position in the cmdline. -["cmdline_special_char", c, shift, level] +["cmdline_special_char", c, shift, level] ~ Display a special char in the cmdline at the cursor position. This is typically used to indicate a pending state, e.g. after |c_CTRL-V|. If `shift` is true the text after the cursor should be shifted, otherwise @@ -714,10 +711,10 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|. Should be hidden at next cmdline_show. -["cmdline_hide"] +["cmdline_hide"] ~ Hide the cmdline. -["cmdline_block_show", lines] +["cmdline_block_show", lines] ~ Show a block of context to the current command line. For example if the user defines a |:function| interactively: > :function Foo() @@ -727,10 +724,10 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|. `lines` is a list of lines of highlighted chunks, in the same form as the "cmdline_show" `contents` parameter. -["cmdline_block_append", line] +["cmdline_block_append", line] ~ Append a line at the end of the currently shown block. -["cmdline_block_hide"] +["cmdline_block_hide"] ~ Hide the block. ============================================================================== @@ -747,7 +744,7 @@ Nvim will not allocate screen space for the cmdline or messages, and 'cmdheight' will be forced zero. Cmdline state is emitted as |ui-cmdline| events, which the UI must handle. -["msg_show", kind, content, replace_last] +["msg_show", kind, content, replace_last] ~ Display a message to the user. kind @@ -781,25 +778,25 @@ events, which the UI must handle. true: Replace the message in the most-recent `msg_show` call, but any other visible message should still remain. -["msg_clear"] +["msg_clear"] ~ Clear all messages currently displayed by "msg_show". (Messages sent by other "msg_" events below will not be affected). -["msg_showmode", content] +["msg_showmode", content] ~ Shows 'showmode' and |recording| messages. `content` has the same format as in "msg_show". This event is sent with empty `content` to hide the last message. -["msg_showcmd", content] +["msg_showcmd", content] ~ Shows 'showcmd' messages. `content` has the same format as in "msg_show". This event is sent with empty `content` to hide the last message. -["msg_ruler", content] +["msg_ruler", content] ~ Used to display 'ruler' when there is no space for the ruler in a statusline. `content` has the same format as in "msg_show". This event is sent with empty `content` to hide the last message. -["msg_history_show", entries] +["msg_history_show", entries] ~ Sent when |:messages| command is invoked. History is sent as a list of entries, where each entry is a `[kind, content]` tuple. diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index dbc18963f9..aea2a27f9e 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -907,8 +907,8 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers return end --The active signature. If omitted or the value lies outside the range of - --`signatures` the value defaults to zero or is ignored if `signatures.length - --=== 0`. Whenever possible implementors should make an active decision about + --`signatures` the value defaults to zero or is ignored if `signatures.length == 0`. + --Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} local active_hl diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua index 6e297814b8..fdbf5f605a 100644 --- a/scripts/gen_help_html.lua +++ b/scripts/gen_help_html.lua @@ -1,5 +1,7 @@ -- Converts Vim :help files to HTML. Validates |tag| links and document syntax (parser errors). -- +-- NOTE: :helptags checks for duplicate tags, whereas this script checks _links_ (to tags). +-- -- USAGE (GENERATE HTML): -- 1. Run `make helptags` first; this script depends on vim.fn.taglist(). -- 2. nvim -V1 -es --clean +"lua require('scripts.gen_help_html').gen('./build/runtime/doc/', 'target/dir/')" @@ -20,37 +22,19 @@ -- * visit_node() is the core function used by gen() to traverse the document tree and produce HTML. -- * visit_validate() is the core function used by validate(). -- * Files in `new_layout` will be generated with a "flow" layout instead of preformatted/fixed-width layout. --- --- parser bugs: --- * Should NOT be code_block: --- tab:xy The 'x' is always used, then 'y' as many times as will --- fit. Thus "tab:>-" displays: --- > --- >- --- >-- --- etc. --- --- tab:xyz The 'z' is always used, then 'x' is prepended, and --- then 'y' is used as many times as will fit. Thus --- "tab:<->" displays: --- > --- <> --- <-> --- <--> --- etc. --- * Should NOT be a "headline". Perhaps a "table" (or just "line"). --- expr5 and expr6 *expr5* *expr6* --- --------------- --- expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+* --- expr6 - expr6 Number subtraction *expr--* --- expr6 . expr6 String concatenation *expr-.* --- expr6 .. expr6 String concatenation *expr-..* local tagmap = nil local helpfiles = nil -local invalid_tags = {} +local invalid_links = {} +local invalid_urls = {} +local invalid_spelling = {} +local spell_dict = { + Neovim = 'Nvim', + NeoVim = 'Nvim', + neovim = 'Nvim', + lua = 'Lua', +} -local api = vim.api local M = {} -- These files are generated with "flow" layout (non fixed-width, wrapped text paragraphs). @@ -59,16 +43,31 @@ local new_layout = { ['api.txt'] = true, ['channel.txt'] = true, ['develop.txt'] = true, + ['luaref.txt'] = true, ['nvim.txt'] = true, ['pi_health.txt'] = true, ['provider.txt'] = true, ['ui.txt'] = true, } --- TODO: treesitter gets stuck on these files... -local exclude = { - ['filetype.txt'] = true, - ['usr_24.txt'] = true, +-- TODO: These known invalid |links| require an update to the relevant docs. +local exclude_invalid = { + ["'previewpopup'"] = "quickref.txt", + ["'pvp'"] = "quickref.txt", + ["'string'"] = "eval.txt", + Query = "treesitter.txt", + ["eq?"] = "treesitter.txt", + ["lsp-request"] = "lsp.txt", + matchit = "vim_diff.txt", + ["matchit.txt"] = "help.txt", + ["set!"] = "treesitter.txt", + ["v:_null_blob"] = "builtin.txt", + ["v:_null_dict"] = "builtin.txt", + ["v:_null_list"] = "builtin.txt", + ["v:_null_string"] = "builtin.txt", + ["vim.lsp.buf_request()"] = "lsp.txt", + ["vim.lsp.util.get_progress_messages()"] = "lsp.txt", + ["vim.treesitter.start()"] = "treesitter.txt" } local function tofile(fname, text) @@ -82,10 +81,6 @@ local function tofile(fname, text) end local function html_esc(s) - if s:find(']local%-additions[*<]') + ) then + -- table.insert(stats.noise_lines, getbuflinestr(root, opt.buf, 0)) + table.insert(noise_lines or {}, line) + return true + end + return false end --- Creates a github issue URL at vigoux/tree-sitter-vimdoc with prefilled content. +-- Creates a github issue URL at neovim/tree-sitter-vimdoc with prefilled content. local function get_bug_url_vimdoc(fname, to_fname, sample_text) local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname)) - local bug_url = ('https://github.com/vigoux/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+' + local bug_url = ('https://github.com/neovim/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+' ..vim.fs.basename(fname) ..'+&body=Found+%60tree-sitter-vimdoc%60+parse+error+at%3A+' ..this_url @@ -237,24 +236,46 @@ local function getbuflinestr(node, bufnr, offset) return table.concat(lines, '\n') end --- Gets the whitespace just before `node` from the raw buffer text. --- Needed for preformatted `old` lines. -local function getws(node, bufnr) - local line1, c1, line2, _ = node:range() - local raw = vim.fn.getbufline(bufnr, line1 + 1, line2 + 1)[1] - local text_before = raw:sub(1, c1) - local leading_ws = text_before:match('%s+$') or '' - return leading_ws +local function get_tagname(node, bufnr) + local node_text = vim.treesitter.get_node_text(node, bufnr) + local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink') and ("'%s'"):format(node_text) or node_text + local helpfile = vim.fs.basename(tagmap[tag]) or nil -- "api.txt" + local helppage = get_helppage(helpfile) -- "api.html" + return helppage, tag end -local function get_tagname(node, bufnr, link) - local node_name = (node.named and node:named()) and node:type() or nil - local node_text = vim.treesitter.get_node_text(node, bufnr) - local tag = ((node_name == 'option' and node_text) - or (link and node_text:gsub('^|', ''):gsub('|$', '') or node_text:gsub('^%*', ''):gsub('%*$', ''))) - local helpfile = tag and vim.fs.basename(tagmap[tag]) or nil -- "api.txt" - local helppage = get_helppage(helpfile) -- "api.html" - return helppage, tag +-- Returns true if the given invalid tagname is a false positive. +local function ignore_invalid(s) + -- Strings like |~/====| appear in various places and the parser thinks they are links, but they + -- are just table borders. + return not not (s:find('===') or exclude_invalid[s]) +end + +local function ignore_parse_error(s) + -- Ignore parse errors for unclosed codespan/optionlink/tag. + -- This is common in vimdocs and is treated as plaintext by :help. + return s:find("^[`'|*]") +end + +local function has_ancestor(node, ancestor_name) + local p = node + while true do + p = p:parent() + if not p or p:type() == 'help_file' then + break + elseif p:type() == ancestor_name then + return true + end + end + return false +end + +local function validate_link(node, bufnr, fname) + local helppage, tagname = get_tagname(node:child(1), bufnr, true) + if not has_ancestor(node, 'column_heading') and not node:has_error() and not tagmap[tagname] and not ignore_invalid(tagname) then + invalid_links[tagname] = vim.fs.basename(fname) + end + return helppage, tagname end -- Traverses the tree at `root` and checks that |tag| links point to valid helptags. @@ -262,6 +283,10 @@ local function visit_validate(root, level, lang_tree, opt, stats) level = level or 0 local node_name = (root.named and root:named()) and root:type() or nil local toplevel = level < 1 + local function node_text(node) + return vim.treesitter.get_node_text(node or root, opt.buf) + end + local text = trim(node_text()) if root:child_count() > 0 then for node, _ in root:iter_children() do @@ -272,14 +297,26 @@ local function visit_validate(root, level, lang_tree, opt, stats) end if node_name == 'ERROR' then + if ignore_parse_error(text) then + return + end -- Store the raw text to give context to the bug report. local sample_text = not toplevel and getbuflinestr(root, opt.buf, 3) or '[top level!]' table.insert(stats.parse_errors, sample_text) - elseif node_name == 'hotlink' or node_name == 'option' then - local _, tagname = get_tagname(root, opt.buf, true) - if not root:has_error() and not tagmap[tagname] then - invalid_tags[tagname] = vim.fs.basename(opt.fname) + elseif node_name == 'word' or node_name == 'uppercase_name' then + if spell_dict[text] then + if not invalid_spelling[text] then + invalid_spelling[text] = { vim.fs.basename(opt.fname) } + else + table.insert(invalid_spelling[text], vim.fs.basename(opt.fname)) + end end + elseif node_name == 'url' then + if text:find('http%:') then + invalid_urls[text] = vim.fs.basename(opt.fname) + end + elseif node_name == 'taglink' or node_name == 'optionlink' then + local _, _ = validate_link(root, opt.buf, opt.fname) end end @@ -296,19 +333,18 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) local parent = root:parent() and root:parent():type() or nil local text = '' local toplevel = level < 1 - local function node_text() - return vim.treesitter.get_node_text(root, opt.buf) + local function node_text(node) + return vim.treesitter.get_node_text(node or root, opt.buf) end - if root:child_count() == 0 then + if root:child_count() == 0 or node_name == 'ERROR' then text = node_text() else -- Process children and join them with whitespace. for node, _ in root:iter_children() do if node:named() then local r = visit_node(node, level + 1, lang_tree, headings, opt, stats) - local ws = r == '' and '' or ((opt.old and (node:type() == 'word' or not node:named())) and getws(node, opt.buf) or ' ') - text = string.format('%s%s%s', text, ws, r) + text = string.format('%s%s', text, r) end end end @@ -316,82 +352,112 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) if node_name == 'help_file' then -- root node return text + elseif node_name == 'url' then + return ('%s'):format(trimmed, trimmed) elseif node_name == 'word' or node_name == 'uppercase_name' then - if parent == 'headline' then - -- Start a new heading item, or update the current one. - local n = (prev == nil or #headings == 0) and #headings + 1 or #headings - headings[n] = string.format('%s%s', headings[n] and headings[n]..' ' or '', text) - end - return html_esc(text) - elseif node_name == 'headline' then - return ('

%s

\n'):format(to_heading_tag(headings[#headings]), text) + elseif node_name == 'h1' or node_name == 'h2' or node_name == 'h3' then + if is_noise(text, stats.noise_lines) then + return '' -- Discard common "noise" lines. + end + -- Remove "===" and tags from ToC text. + local hname = (node_text():gsub('%-%-%-%-+', ''):gsub('%=%=%=%=+', ''):gsub('%*.*%*', '')) + if node_name == 'h1' or #headings == 0 then + table.insert(headings, { name = hname, subheadings = {}, }) + else + table.insert(headings[#headings].subheadings, { name = hname, subheadings = {}, }) + end + local el = node_name == 'h1' and 'h2' or 'h3' + return ('<%s class="help-heading">%s\n'):format(to_heading_tag(hname), el, text, el) elseif node_name == 'column_heading' or node_name == 'column_name' then - return ('

%s

\n'):format(trimmed) - elseif node_name == 'line' then - -- TODO: remove these "sibling inspection" hacks once the parser provides structured info - -- about paragraphs and listitems: https://github.com/vigoux/tree-sitter-vimdoc/issues/12 - local next_text = root:next_sibling() and vim.treesitter.get_node_text(root:next_sibling(), opt.buf) or '' - local li = startswith_bullet(text) -- Listitem? - local next_li = startswith_bullet(next_text) -- Next is listitem? - -- Close the paragraph/listitem if the next sibling is not a line. - local close = (next_ ~= 'line' or next_li or is_blank(next_text)) and '\n' or '' - - -- HACK: discard common "noise" lines. - if is_noise(text) then - table.insert(stats.noise_lines, getbuflinestr(root, opt.buf, 0)) - return (opt.old or prev ~= 'line') and '' or close + if root:has_error() then + return text + end + return ('
%s
'):format(trimmed) + elseif node_name == 'block' then + if is_blank(text) then + return '' end - if opt.old then - -- XXX: Treat old docs as preformatted. Until those docs are "fixed" or we get better info - -- from tree-sitter-vimdoc, this avoids broken layout for legacy docs. - return ('
%s
\n'):format(text) + -- XXX: Treat old docs as preformatted; random indentation is used for layout there. + return ('
%s
\n'):format(text) + end + return string.format('
\n%s\n
\n', text) + elseif node_name == 'line' then + local sib = root:prev_sibling() + local sib_last = sib and sib:named_child(sib:named_child_count() - 1) + local in_li = false + + -- XXX: parser bug: (codeblock) without terminating "<" consumes first char of the next (line). Recover it here. + local recovered = (sib_last and sib_last:type() == 'codeblock') and node_text(root:prev_sibling()):sub(-1) or '' + recovered = recovered == '<' and '' or html_esc(recovered) + + -- XXX: see if we are currently "in" a listitem. + while sib ~= nil and not in_li do + in_li = (sib:type() == 'line_li') + sib = sib:prev_sibling() end - if li then - return string.format('
%s%s', trim_bullet(expandtabs(text)), close) - end - if prev ~= 'line' then -- Start a new paragraph. - return string.format('
%s%s', expandtabs(text), close) + -- Close the current listitem. + local close = (in_li and next_ ~= 'line') and '
' or '' + + if is_blank(text) or is_noise(text, stats.noise_lines) then + return close -- Discard common "noise" lines. end - -- Continue in the current paragraph/listitem. - return string.format('%s%s', expandtabs(text), close) - elseif node_name == 'hotlink' or node_name == 'option' then - local helppage, tagname = get_tagname(root, opt.buf, true) - if not root:has_error() and not tagmap[tagname] then - invalid_tags[tagname] = vim.fs.basename(opt.fname) + local div = (root:child(0) and root:child(0):type() == 'column_heading') or close ~= '' + return string.format('%s%s%s%s', recovered, div and trim(text) or text, div and '' or '\n', close) + elseif node_name == 'line_li' then + -- Close the listitem immediately if the next sibling is not a line. + local close = (next_ ~= 'line') and '
' or '' + return string.format('
%s %s', trim_bullet(text), close) + elseif node_name == 'taglink' or node_name == 'optionlink' then + if root:has_error() then + return text end - return ('%s'):format(helppage, url_encode(tagname), html_esc(tagname)) - elseif node_name == 'backtick' then - return ('%s'):format(html_esc(text)) + local helppage, tagname = validate_link(root, opt.buf, opt.fname) + return (' %s'):format(helppage, url_encode(tagname), html_esc(tagname)) + elseif node_name == 'codespan' then + if root:has_error() then + return text + end + return (' %s'):format(text) elseif node_name == 'argument' then - return ('{%s}'):format(html_esc(trimmed)) - elseif node_name == 'code_block' then - return ('
\n%s
\n'):format(html_esc(trim_indent(trim_gt_lt(text)))) + return (' {%s}'):format(trimmed) + elseif node_name == 'codeblock' then + return ('
%s
'):format(html_esc(trim_indent(trim_gt_lt(text)))) elseif node_name == 'tag' then -- anchor - local _, tagname = get_tagname(root, opt.buf, false) - local s = ('%s'):format(url_encode(tagname), trimmed) - if parent == 'headline' and prev ~= 'tag' then + if root:has_error() then + return text + end + local in_heading = (parent == 'h1' or parent == 'h2') + local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right' or 'help-tag' + local tagname = node_text(root:child(1)) + if vim.tbl_count(stats.first_tags) < 2 then + -- First 2 tags in the doc will be anchored at the main heading. + table.insert(stats.first_tags, tagname) + return '' + end + local s = (' %s'):format(url_encode(tagname), cssclass, trimmed) + if in_heading and prev ~= 'tag' then -- Start the container for tags in a heading. -- This makes "justify-content:space-between" right-align the tags. --

foo bartag1 tag2

return string.format('%s', s) - elseif parent == 'headline' and next_ == nil then + elseif in_heading and next_ == nil then -- End the container for tags in a heading. return string.format('%s', s) end return s elseif node_name == 'ERROR' then + if ignore_parse_error(trimmed) then + return text + end + -- Store the raw text to give context to the bug report. local sample_text = not toplevel and getbuflinestr(root, opt.buf, 3) or '[top level!]' table.insert(stats.parse_errors, sample_text) - if prev == 'ERROR' then - -- Avoid trashing the text with cascading errors. - return trimmed, ('parse-error:"%s"'):format(node_text()) - end - return ('%s'):format( + return ('%s'):format( get_bug_url_vimdoc(opt.fname, opt.to_fname, sample_text), trimmed) else -- Unknown token. local sample_text = not toplevel and getbuflinestr(root, opt.buf, 3) or '[top level!]' @@ -406,8 +472,7 @@ local function get_helpfiles(include) for f, type in vim.fs.dir(dir) do if (vim.endswith(f, '.txt') and type == 'file' - and (not include or vim.tbl_contains(include, f)) - and (not exclude[f])) then + and (not include or vim.tbl_contains(include, f))) then local fullpath = vim.fn.fnamemodify(('%s/%s'):format(dir, f), ':p') table.insert(rv, fullpath) end @@ -430,6 +495,13 @@ local function get_helptags(help_dir) return m end +-- Use the help.so parser defined in the build, not whatever happens to be installed on the system. +local function ensure_runtimepath() + if not vim.o.runtimepath:find('build/lib/nvim/') then + vim.cmd[[set runtimepath^=./build/lib/nvim/]] + end +end + -- Opens `fname` in a buffer and gets a treesitter parser for the buffer contents. -- -- @returns lang_tree, bufnr @@ -437,7 +509,7 @@ local function parse_buf(fname) local buf if type(fname) == 'string' then vim.cmd('split '..vim.fn.fnameescape(fname)) -- Filename. - buf = api.nvim_get_current_buf() + buf = vim.api.nvim_get_current_buf() else buf = fname vim.cmd('sbuffer '..tostring(fname)) -- Buffer number. @@ -451,10 +523,9 @@ end -- - checks that |tag| links point to valid helptags. -- - recursively counts parse errors ("ERROR" nodes) -- --- @returns { invalid_tags: number, parse_errors: number } +-- @returns { invalid_links: number, parse_errors: number } local function validate_one(fname) local stats = { - invalid_tags = {}, parse_errors = {}, } local lang_tree, buf = parse_buf(fname) @@ -463,10 +534,7 @@ local function validate_one(fname) end lang_tree:destroy() vim.cmd.close() - return { - invalid_tags = invalid_tags, - parse_errors = stats.parse_errors, - } + return stats end -- Generates HTML from one :help file `fname` and writes the result to `to_fname`. @@ -480,9 +548,10 @@ local function gen_one(fname, to_fname, old, commit) local stats = { noise_lines = {}, parse_errors = {}, + first_tags = {}, -- Track the first few tags in doc. } local lang_tree, buf = parse_buf(fname) - local headings = {} -- Headings (for ToC). + local headings = {} -- Headings (for ToC). 2-dimensional: h1 contains h2/h3. local title = to_titlecase(basename_noext(fname)) local html = ([[ @@ -555,7 +624,12 @@ local function gen_one(fname, to_fname, old, commit) ]] - local main = ([[ + local main = '' + for _, tree in ipairs(lang_tree:trees()) do + main = main .. (visit_node(tree:root(), 0, tree, headings, { buf = buf, old = old, fname = fname, to_fname = to_fname }, stats)) + end + + main = ([[