fix(docs-html): update parser

- Improve generated HTML by updating parser which includes fixes for
  single "'" and single "|":
  https://github.com/neovim/tree-sitter-vimdoc/pull/31
- Updated parser also fixes the conceal issue for "help" highlight
  queries https://github.com/neovim/tree-sitter-vimdoc/issues/23 by
  NOT including whitespace in nodes.
  - But this means we need to restore the getws() function which scrapes
    leading whitespace from the original input (buffer).

(cherry picked from commit a7a83bc4c2)
This commit is contained in:
Justin M. Keyes 2022-10-09 18:19:43 +02:00 committed by github-actions[bot]
parent f294712d8c
commit 5e88506508
4 changed files with 88 additions and 70 deletions

View File

@ -164,6 +164,9 @@ Strict "vimdoc" subset:
that you don't want auto-wrapped. Lists are always rendered with "flow" that you don't want auto-wrapped. Lists are always rendered with "flow"
(soft-wrapped) layout instead of preformatted (hard-wrapped) layout common (soft-wrapped) layout instead of preformatted (hard-wrapped) layout common
in legacy :help docs. in legacy :help docs.
- Limitation: currently the parser https://github.com/neovim/tree-sitter-vimdoc
does not understand numbered listitems, so use a bullet symbol (- or •)
before numbered items, e.g. "- 1." instead of "1.".
- Separate blocks (paragraphs) of content by a blank line(s). - Separate blocks (paragraphs) of content by a blank line(s).
- Do not use indentation in random places—that prevents the page from using - Do not use indentation in random places—that prevents the page from using
"flow" layout. If you need a preformatted section, put it in "flow" layout. If you need a preformatted section, put it in

View File

@ -144,7 +144,7 @@ documentation at https://www.lua.org/manual/5.1/manual.html#pdf-require.
For example, if 'runtimepath' is `foo,bar` and |package.cpath| was For example, if 'runtimepath' is `foo,bar` and |package.cpath| was
`./?.so;./?.dll` at startup, `require('mod')` searches these paths in order `./?.so;./?.dll` at startup, `require('mod')` searches these paths in order
and loads the first module found ("first wins"): and loads the first module found ("first wins"): >
foo/lua/mod.lua foo/lua/mod.lua
foo/lua/mod/init.lua foo/lua/mod/init.lua
bar/lua/mod.lua bar/lua/mod.lua
@ -153,7 +153,7 @@ and loads the first module found ("first wins"):
foo/lua/mod.dll foo/lua/mod.dll
bar/lua/mod.so bar/lua/mod.so
bar/lua/mod.dll bar/lua/mod.dll
<
*lua-package-path* *lua-package-path*
Nvim automatically adjusts |package.path| and |package.cpath| according to the Nvim automatically adjusts |package.path| and |package.cpath| according to the
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
@ -166,37 +166,33 @@ added to |package.cpath|. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of `/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing |package.cpath| are used. Example: the existing |package.cpath| are used. Example:
1. Given that - 1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`; - 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial (defined at compile-time or derived from - initial |package.cpath| (defined at compile-time or derived from
`$LUA_CPATH`/`$LUA_INIT`) |package.cpath| contains `$LUA_CPATH` / `$LUA_INIT`) contains `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`. - 2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in order: parts of the path starting from the first path component containing
order: parts of the path starting from the first path component containing question mark and preceding path separator.
question mark and preceding path separator. - 3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same as the suffix of the first path from |package.path| (i.e. `./?.so`). Which
as the suffix of the first path from |package.path| (i.e. `./?.so`). Which leaves `/?.so` and `/a?d/j/g.elf`, in this order.
leaves `/?.so` and `/a?d/j/g.elf`, in this order. - 4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The second one contains a semicolon which is a paths separator so it is out,
second one contains a semicolon which is a paths separator so it is out, leaving only `/foo/bar` and `/abc`, in order.
leaving only `/foo/bar` and `/abc`, in order. - 5. The cartesian product of paths from 4. and suffixes from 3. is taken,
5. The cartesian product of paths from 4. and suffixes from 3. is taken, giving four variants. In each variant a `/lua` path segment is inserted
giving four variants. In each variant, a `/lua` path segment is inserted between path and suffix, leaving:
between path and suffix, leaving: - `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
- 6. New paths are prepended to the original |package.cpath|.
- `/foo/bar/lua/?.so` The result will look like this: >
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
6. New paths are prepended to the original |package.cpath|. /foo/bar,/xxx;yyy/baz,/abc ('runtimepath')
× ./?.so;/def/ghi/a?d/j/g.elf;/def/?.so (package.cpath)
The result will look like this: = /foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
Note: Note:
@ -278,7 +274,7 @@ arguments separated by " " (space) instead of "\t" (tab).
:lua require"lpeg" :lua require"lpeg"
:lua -- balanced parenthesis grammar: :lua -- balanced parenthesis grammar:
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" } :lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
:luado if bp:match(line) then return "-->\t" .. line end :luado if bp:match(line) then return "=>\t" .. line end
< <
*:luafile* *:luafile*
:luafile {file} :luafile {file}
@ -595,12 +591,12 @@ vim.highlight.range({bufnr}, {ns}, {hlgroup}, {start}, {finish}, {opts})
Apply highlight group to range of text. Apply highlight group to range of text.
Parameters: ~ Parameters: ~
{bufnr} buffer number {bufnr} buffer number
{ns} namespace for highlights {ns} namespace for highlights
{hlgroup} highlight group name {hlgroup} highlight group name
{start} starting position (tuple {line,col}) {start} starting position (tuple {line,col})
{finish} finish position (tuple {line,col}) {finish} finish position (tuple {line,col})
{opts} optional parameters: {opts} optional parameters:
• `regtype`: type of range (characterwise, linewise, • `regtype`: type of range (characterwise, linewise,
or blockwise, see |setreg()|), default `'v'` or blockwise, see |setreg()|), default `'v'`
• `inclusive`: range includes end position, • `inclusive`: range includes end position,
@ -653,22 +649,22 @@ vim.diff({a}, {b}, {opts}) *vim.diff()*
Examples: > Examples: >
vim.diff('a\n', 'b\nc\n') vim.diff('a\n', 'b\nc\n')
--> =>
@@ -1 +1,2 @@ @@ -1 +1,2 @@
-a -a
+b +b
+c +c
vim.diff('a\n', 'b\nc\n', {result_type = 'indices'}) vim.diff('a\n', 'b\nc\n', {result_type = 'indices'})
--> =>
{ {
{1, 1, 1, 2} {1, 1, 1, 2}
} }
< <
Parameters: ~ Parameters: ~
{a} First string to compare {a} First string to compare
{b} Second string to compare {b} Second string to compare
{opts} Optional parameters: {opts} Optional parameters:
• `on_hunk` (callback): • `on_hunk` (callback):
Invoked for each hunk in the diff. Return a negative number Invoked for each hunk in the diff. Return a negative number
to cancel the callback for any remaining hunks. to cancel the callback for any remaining hunks.
@ -734,13 +730,13 @@ vim.spell.check({str}) *vim.spell.check()*
Example: > Example: >
vim.spell.check("the quik brown fox") vim.spell.check("the quik brown fox")
--> =>
{ {
{'quik', 'bad', 4} {'quik', 'bad', 4}
} }
< <
Parameters: ~ Parameters: ~
{str} String to spell check. {str} String to spell check.
Return: ~ Return: ~
List of tuples with three items: List of tuples with three items:
@ -829,9 +825,9 @@ vim.iconv({str}, {from}, {to}[, {opts}]) *vim.iconv()*
can accept, see ":Man 3 iconv". can accept, see ":Man 3 iconv".
Parameters: ~ Parameters: ~
{str} (string) Text to convert {str} (string) Text to convert
{from} (string) Encoding of {str} {from} (string) Encoding of {str}
{to} (string) Target encoding {to} (string) Target encoding
Returns: ~ Returns: ~
Converted string if conversion succeeds, `nil` otherwise. Converted string if conversion succeeds, `nil` otherwise.
@ -849,8 +845,8 @@ vim.defer_fn({fn}, {timeout}) *vim.defer_fn*
safe to call. safe to call.
Parameters: ~ Parameters: ~
{fn} Callback to call once {timeout} expires {fn} Callback to call once {timeout} expires
{timeout} Time in ms to wait before calling {fn} {timeout} Time in ms to wait before calling {fn}
Returns: ~ Returns: ~
|vim.loop|.new_timer() object |vim.loop|.new_timer() object
@ -863,10 +859,10 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()*
this time. this time.
Parameters: ~ Parameters: ~
{time} Number of milliseconds to wait {time} Number of milliseconds to wait
{callback} Optional callback. Waits until {callback} returns true {callback} Optional callback. Waits until {callback} returns true
{interval} (Approximate) number of milliseconds to wait between polls {interval} (Approximate) number of milliseconds to wait between polls
{fast_only} If true, only |api-fast| events will be processed. {fast_only} If true, only |api-fast| events will be processed.
If called from while in an |api-fast| event, will If called from while in an |api-fast| event, will
automatically be set to `true`. automatically be set to `true`.
@ -1254,7 +1250,7 @@ Option:get()
-- { space = "_", tab = ">~", } -- { space = "_", tab = ">~", }
for char, representation in pairs(vim.opt.listchars:get()) do for char, representation in pairs(vim.opt.listchars:get()) do
print(char, "->", representation) print(char, "=>", representation)
end end
< <
For values that are lists of flags, a set will be returned with the flags For values that are lists of flags, a set will be returned with the flags
@ -1634,10 +1630,10 @@ split({s}, {sep}, {kwargs}) *vim.split()*
Examples: > Examples: >
split(":aa::b:", ":") --> {'','aa','','b',''} split(":aa::b:", ":") => {'','aa','','b',''}
split("axaby", "ab?") --> {'','x','y'} split("axaby", "ab?") => {'','x','y'}
split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'} split("x*yz*o", "*", {plain=true}) => {'x','yz','o'}
split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'} split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
< <
Parameters: ~ Parameters: ~

View File

@ -99,10 +99,10 @@ end
--- ---
--- Examples: --- Examples:
--- <pre> --- <pre>
--- split(":aa::b:", ":") --> {'','aa','','b',''} --- split(":aa::b:", ":") => {'','aa','','b',''}
--- split("axaby", "ab?") --> {'','x','y'} --- split("axaby", "ab?") => {'','x','y'}
--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'} --- split("x*yz*o", "*", {plain=true}) => {'x','yz','o'}
--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'} --- split("|x|y|z|", "|", {trimempty=true}) => {'x', 'y', 'z'}
--- </pre> --- </pre>
--- ---
---@see |vim.gsplit()| ---@see |vim.gsplit()|

View File

@ -248,6 +248,16 @@ local function getbuflinestr(node, bufnr, offset)
return table.concat(lines, '\n') return table.concat(lines, '\n')
end 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
end
local function get_tagname(node, bufnr) local function get_tagname(node, bufnr)
local text = vim.treesitter.get_node_text(node, bufnr) local text = vim.treesitter.get_node_text(node, bufnr)
local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink') and ("'%s'"):format(text) or text local tag = (node:type() == 'optionlink' or node:parent():type() == 'optionlink') and ("'%s'"):format(text) or text
@ -353,12 +363,21 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
local parent = root:parent() and root:parent():type() or nil local parent = root:parent() and root:parent():type() or nil
local text = '' local text = ''
local trimmed local trimmed
local function node_text(node) -- Gets leading whitespace of `node`.
return vim.treesitter.get_node_text(node or root, opt.buf) local function ws(node)
node = node or root
local ws_ = getws(node, opt.buf)
-- XXX: first node of a (line) includes whitespace, even after
-- https://github.com/neovim/tree-sitter-vimdoc/pull/31 ?
if ws_ == '' then
ws_ = vim.treesitter.get_node_text(node, opt.buf):match('^%s+') or ''
end
return ws_
end end
-- Gets leading whitespace of the current node. local function node_text(node, ws_)
local function ws() node = node or root
return node_text():match('^%s+') or '' ws_ = (ws_ == nil or ws_ == true) and getws(node, opt.buf) or ''
return string.format('%s%s', ws_, vim.treesitter.get_node_text(node, opt.buf))
end end
if root:child_count() == 0 or node_name == 'ERROR' then if root:child_count() == 0 or node_name == 'ERROR' then
@ -400,7 +419,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
if root:has_error() then if root:has_error() then
return text return text
end end
return ('<div class="help-column_heading">%s%s</div>'):format(ws(), trimmed) return ('<div class="help-column_heading">%s</div>'):format(text)
elseif node_name == 'block' then elseif node_name == 'block' then
if is_blank(text) then if is_blank(text) then
return '' return ''
@ -461,9 +480,9 @@ local function visit_node(root, level, lang_tree, headings, opt, stats)
end end
local in_heading = vim.tbl_contains({'h1', 'h2', 'h3'}, parent) local in_heading = vim.tbl_contains({'h1', 'h2', 'h3'}, parent)
local cssclass = (not in_heading and get_indent(node_text()) > 8) and 'help-tag-right' or 'help-tag' 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)) local tagname = node_text(root:child(1), false)
if vim.tbl_count(stats.first_tags) < 2 then if vim.tbl_count(stats.first_tags) < 2 then
-- First 2 tags in the doc will be anchored at the main heading. -- Force the first 2 tags in the doc to be anchored at the main heading.
table.insert(stats.first_tags, tagname) table.insert(stats.first_tags, tagname)
return '' return ''
end end