runtime(helptoc): the helptoc package can be improved

Adds the following changes:
- New Maintainer: Pete Kenny
- New filetypes supported (asciidoc, html, tex, vim, xhtml)
- improved Markdown support
- Sanitised ToCs and popup presentation
- Configuration improvements and options
- Add helptoc.txt help file

closes: #17255

Signed-off-by: Peter Kenny <github.com@k1w1.cyou>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Peter Kenny
2025-05-05 20:15:39 +02:00
committed by Christian Brabandt
parent adfeb4ad95
commit ba0062b0c7
6 changed files with 803 additions and 109 deletions

1
.github/MAINTAINERS vendored
View File

@ -431,6 +431,7 @@ runtime/lang/menu_ru_ru.koi8-r.vim @RestorerZ
runtime/lang/menu_ru_ru.utf-8.vim @RestorerZ
runtime/pack/dist/opt/cfilter/plugin/cfilter.vim @yegappan
runtime/pack/dist/opt/comment/ @habamax
runtime/pack/dist/opt/helptoc/ @kennypete
runtime/pack/dist/opt/matchit/ @chrisbra
runtime/pack/dist/opt/nohlsearch/ @habamax
runtime/plugin/manpager.vim @Konfekt

View File

@ -808,6 +808,8 @@ RT_ALL = \
runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim \
runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim \
runtime/pack/dist/opt/helptoc/autoload/helptoc.vim \
runtime/pack/dist/opt/helptoc/doc/helptoc.txt \
runtime/pack/dist/opt/helptoc/doc/tags \
runtime/pack/dist/opt/helptoc/plugin/helptoc.vim \
runtime/pack/dist/opt/hlyank/plugin/hlyank.vim \
runtime/pack/dist/opt/justify/plugin/justify.vim \

View File

@ -1,4 +1,4 @@
*helphelp.txt* For Vim version 9.1. Last change: 2025 Apr 21
*helphelp.txt* For Vim version 9.1. Last change: 2025 May 04
VIM REFERENCE MANUAL by Bram Moolenaar
@ -287,9 +287,11 @@ The latter supports the following normal commands: >
<Home> | select first entry
<End> | select last entry
The plugin can also provide a table of contents in man pages, markdown files,
and terminal buffers. In the latter, the entries will be the past executed
shell commands. To find those, the following pattern is used: >
The plugin can also provide a table of contents in buffers of the following
filetypes: asciidoc, html, man, markdown, tex, vim, and xhtml. In addition
it also provide a table of contents for a terminal buffer, which produces
entries that are the past executed shell commands. To find those, by default,
the following pattern is used: >
^\w\+@\w\+:\f\+\$\s
@ -303,6 +305,9 @@ Tip: After inserting a pattern to look for with the `/` command, if you press
<Esc> instead of <CR>, you can then get more context for each remaining entry
by pressing `J` or `K`.
Refer |helptoc.vim| for more details about helptoc, particularly about using
it with filetypes other than help, and configuring its options.
==============================================================================
2. Translated help files *help-translated*

View File

@ -1,24 +1,71 @@
vim9script noclear
# Config {{{1
# g:helptoc {{{2
# Create the g:helptoc dict (used to specify the shell_prompt and other
# options) when it does not exist
g:helptoc = exists('g:helptoc') ? g:helptoc : {}
var SHELL_PROMPT: string = ''
# Set the initial shell_prompt pattern matching a default bash prompt
g:helptoc.shell_prompt = get(g:helptoc, 'shell_prompt', '^\w\+@\w\+:\f\+\$\s')
# Track the prior prompt (used to reset b:toc if 'shell_prompt' changes)
g:helptoc.prior_shell_prompt = g:helptoc.shell_prompt
def UpdateUserSettings() #{{{2
var new_prompt: string = g:
->get('helptoc', {})
->get('shell_prompt', '^\w\+@\w\+:\f\+\$\s')
if new_prompt != SHELL_PROMPT
SHELL_PROMPT = new_prompt
if g:helptoc.shell_prompt != g:helptoc.prior_shell_prompt
# invalidate cache: user config has changed
unlet! b:toc
# reset the prior prompt to the new prompt
g:helptoc.prior_shell_prompt = g:helptoc.shell_prompt
endif
# helptoc popup presentation options{{{
# Enable users to choose whether, in toc and help text popups, to have:
# - border (default [], which is a border, so is usually wanted)
# - borderchars (default single box drawing; use [] for Vim's defaults)
# - borderhighlight (default [], but a user may prefer something else)
# - close (default 'none'; mouse users may prefer 'button')
# - drag (default true, which is a popup_menu's default)
# - scrollbar (default false; for long tocs/HELP_TEXT true may be better)
# For example, in a Vim9 script .vimrc, these settings will produce tocs
# with borders that have the same highlight group as the inactive
# statusline, a scrollbar, and an 'X' close button:
# g:helptoc.popup_borderchars = get(g:helptoc, 'popup_borderchars', [' '])
# g:helptoc.popup_borderhighlight = get(g:helptoc,
# 'popup_borderhighlight', ['StatusLineNC'])
# g:helptoc.popup_close = get(g:helptoc, 'popup_close', 'button')
# g:helptoc.popup_scrollbar = get(g:helptoc, 'popup_scrollbar', true)
# }}}
g:helptoc.popup_border = get(g:helptoc, 'popup_border', [])
g:helptoc.popup_borderchars = get(g:helptoc, 'popup_borderchars',
['─', '│', '─', '│', '┌', '┐', '┘', '└'])
g:helptoc.popup_borderhighlight = get(g:helptoc, 'popup_borderhighlight',
[])
g:helptoc.popup_drag = get(g:helptoc, 'popup_drag', true)
g:helptoc.popup_close = get(g:helptoc, 'popup_close', 'none')
g:helptoc.popup_scrollbar = get(g:helptoc, 'popup_scrollbar', false)
# For sanitized tocs, allow the user to specify the level indicator
g:helptoc.level_indicator = get(g:helptoc, 'level_indicator', '| ')
enddef
UpdateUserSettings()
# Init {{{1
# Syntax {{{1
# Used by sanitized tocs (asciidoc, html, markdown, tex, vim, and xhtml)
def SanitizedTocSyntax(): void
silent execute "syntax match helptocLevel _^\\(" ..
g:helptoc.level_indicator .. "\\)*_ contained"
silent execute "syntax region helptocText start=_^\\(" ..
g:helptoc.level_indicator .. "\\)*_ end=_$_ contains=helptocLevel"
highlight link helptocText Normal
highlight link helptocLevel NonText
enddef
# Init {{{1
# Constants {{{2
# HELP_TEXT {{{3
const HELP_TEXT: list<string> =<< trim END
normal commands in help window
──────────────────────────────
@ -73,38 +120,108 @@ const HELP_TEXT: list<string> =<< trim END
more context for each remaining entry by pressing J or K
END
# UPTOINC_H {{{3
const UPTOINC_H: string = '\v\c^%(%([<][^h][^>]*[>])|\s)*[<]h'
# MATCH_ENTRY {{{3
const MATCH_ENTRY: dict<dict<func: bool>> = {
help: {},
man: {
1: (line: string, _): bool => line =~ '^\S',
2: (line: string, _): bool => line =~ '^\%( \{3\}\)\=\S',
3: (line: string, _): bool => line =~ '^\s\+\(\%(+\|-\)\S\+,\s\+\)*\%(+\|-\)\S\+',
# For asciidoc, these patterns should match:
# https://docs.asciidoctor.org/asciidoc/latest/sections/titles-and-levels/
asciidoc: {
1: (l: string, _): bool => l =~ '\v^%(\=|#)\s',
2: (l: string, _): bool => l =~ '\v^%(\={2}|#{2})\s',
3: (l: string, _): bool => l =~ '\v^%(\={3}|#{3})\s',
4: (l: string, _): bool => l =~ '\v^%(\={4}|#{4})\s',
5: (l: string, _): bool => l =~ '\v^%(\={5}|#{5})\s',
6: (l: string, _): bool => l =~ '\v^%(\={6}|#{6})\s',
},
html: {
1: (l: string, _): bool => l =~ $"{UPTOINC_H}1",
2: (l: string, _): bool => l =~ $"{UPTOINC_H}2",
3: (l: string, _): bool => l =~ $"{UPTOINC_H}3",
4: (l: string, _): bool => l =~ $"{UPTOINC_H}4",
5: (l: string, _): bool => l =~ $"{UPTOINC_H}5",
6: (l: string, _): bool => l =~ $"{UPTOINC_H}6",
},
man: {
1: (l: string, _): bool => l =~ '^\S',
2: (l: string, _): bool => l =~ '\v^%( {3})=\S',
3: (l: string, _): bool => l =~ '\v^\s+%(%(\+|-)\S+,\s+)*(\+|-)\S+'
},
# For markdown, these patterns should match:
# https://spec.commonmark.org/0.31.2/#atx-headings and
# https://spec.commonmark.org/0.31.2/#setext-headings
markdown: {
1: (line: string, nextline: string): bool =>
(line =~ '^#[^#]' || nextline =~ '^=\+$') && line =~ '\w',
2: (line: string, nextline: string): bool =>
(line =~ '^##[^#]' || nextline =~ '^-\+$') && line =~ '\w',
3: (line: string, _): bool => line =~ '^###[^#]',
4: (line: string, _): bool => line =~ '^####[^#]',
5: (line: string, _): bool => line =~ '^#####[^#]',
6: (line: string, _): bool => line =~ '^######[^#]',
1: (l: string, nextline: string): bool =>
(l =~ '\v^ {0,3}#%(\s|$)' || nextline =~ '\v^ {0,3}\=+$') &&
l =~ '\S',
2: (l: string, nextline: string): bool =>
(l =~ '\v^ {0,3}##%(\s|$)' || nextline =~ '\v^ {0,3}-+$') &&
l =~ '\S',
3: (l: string, _): bool => l =~ '\v {0,3}#{3}%(\s|$)',
4: (l: string, _): bool => l =~ '\v {0,3}#{4}%(\s|$)',
5: (l: string, _): bool => l =~ '\v {0,3}#{5}%(\s|$)',
6: (l: string, _): bool => l =~ '\v {0,3}#{6}%(\s|$)',
},
terminal: {
1: (line: string, _): bool => line =~ SHELL_PROMPT,
1: (l: string, _): bool => l =~ g:helptoc.shell_prompt
},
# For LaTeX, this should meet
# https://mirrors.rit.edu/CTAN/info/latex2e-help-texinfo/latex2e.pdf
# including:
# para 6.3:
# \section{Heading}
# \section[Alternative ToC Heading]{Heading}
# para 25.1.2:
# \section*{Not for the TOC heading}
# \addcontentsline{toc}{section}{Alternative ToC Heading}
tex: {
1: (l: string, _): bool => l =~ '^[\\]\(\%(part\|chapter\)' ..
'\%([\u005B{]\)\|addcontentsline{toc}{\%(part\|chapter\)\)',
2: (l: string, _): bool => l =~ '^[\\]\%(section' ..
'\%([\u005B{]\)\|addcontentsline{toc}{section}\)',
3: (l: string, _): bool => l =~ '^[\\]\%(subsection' ..
'\%([\u005B{]\)\|addcontentsline{toc}{subsection}\)',
4: (l: string, _): bool => l =~ '^[\\]\%(subsubsection' ..
'\%([\u005B{]\)\|addcontentsline{toc}{subsubsection}\)',
},
vim: {
1: (l: string, _): bool => l =~ '\v\{{3}1',
2: (l: string, _): bool => l =~ '\v\{{3}2',
3: (l: string, _): bool => l =~ '\v\{{3}3',
4: (l: string, _): bool => l =~ '\v\{{3}4',
5: (l: string, _): bool => l =~ '\v\{{3}5',
6: (l: string, _): bool => l =~ '\v\{{3}6',
},
xhtml: {
1: (l: string, _): bool => l =~ $"{UPTOINC_H}1",
2: (l: string, _): bool => l =~ $"{UPTOINC_H}2",
3: (l: string, _): bool => l =~ $"{UPTOINC_H}3",
4: (l: string, _): bool => l =~ $"{UPTOINC_H}4",
5: (l: string, _): bool => l =~ $"{UPTOINC_H}5",
6: (l: string, _): bool => l =~ $"{UPTOINC_H}6",
}
}
# HELP_RULERS {{{3
const HELP_RULERS: dict<string> = {
'=': '^=\{40,}$',
'-': '^-\{40,}',
}
const HELP_RULER: string = HELP_RULERS->values()->join('\|')
# the regex is copied from the help syntax plugin
# HELP_TAG {{{3
# The regex is copied from the help syntax plugin
const HELP_TAG: string = '\*[#-)!+-~]\+\*\%(\s\|$\)\@='
# Adapted from `$VIMRUNTIME/syntax/help.vim`.{{{
@ -113,13 +230,15 @@ const HELP_TAG: string = '\*[#-)!+-~]\+\*\%(\s\|$\)\@='
#
# ^[-A-Z .][-A-Z0-9 .()_]*\ze\(\s\+\*\|$\)
#
# Allowing a space or a hyphen at the start can give false positives, and is
# Allowing a space or a hyphen at the start can give false positives, and is
# useless, so we don't allow them.
#}}}
# HELP_HEADLINE {{{3
const HELP_HEADLINE: string = '^\C[A-Z.][-A-Z0-9 .()_]*\%(\s\+\*+\@!\|$\)'
# ^--^
# To prevent some false positives under `:help feature-list`.
# Others {{{2
var lvls: dict<number>
def InitHelpLvls()
lvls = {
@ -133,7 +252,6 @@ def InitHelpLvls()
}
enddef
const AUGROUP: string = 'HelpToc'
var fuzzy_entries: list<dict<any>>
var help_winid: number
var print_entry: bool
@ -141,11 +259,11 @@ var selected_entry_match: number
# Interface {{{1
export def Open() #{{{2
var type: string = GetType()
if !MATCH_ENTRY->has_key(type)
g:helptoc.type = GetType()
if !MATCH_ENTRY->has_key(g:helptoc.type)
return
endif
if type == 'terminal' && win_gettype() == 'popup'
if g:helptoc.type == 'terminal' && win_gettype() == 'popup'
# trying to deal with a popup menu on top of a popup terminal seems
# too tricky for now
echomsg 'does not work in a popup window; only in a regular window'
@ -158,7 +276,7 @@ export def Open() #{{{2
if exists('b:toc') && &filetype != 'man'
if b:toc.changedtick != b:changedtick
# in a terminal buffer, `b:changedtick` does not change
|| type == 'terminal' && line('$') > b:toc.linecount
|| g:helptoc.type == 'terminal' && line('$') > b:toc.linecount
unlet! b:toc
endif
endif
@ -187,66 +305,257 @@ export def Open() #{{{2
line: winpos[0],
col: winpos[1] + width - 1,
pos: 'topright',
scrollbar: false,
highlight: type == 'terminal' ? 'Terminal' : 'Normal',
border: [],
borderchars: ['─', '│', '─', '│', '┌', '┐', '┘', '└'],
highlight: g:helptoc.type == 'terminal' ? 'Terminal' : 'Normal',
minheight: height,
maxheight: height,
minwidth: b:toc.width,
maxwidth: b:toc.width,
filter: Filter,
callback: Callback,
border: g:helptoc.popup_border,
borderchars: g:helptoc.popup_borderchars,
borderhighlight: g:helptoc.popup_borderhighlight,
close: g:helptoc.popup_close,
drag: g:helptoc.popup_drag,
scrollbar: g:helptoc.popup_scrollbar,
})
Win_execute(winid, [$'ownsyntax {&filetype}', '&l:conceallevel = 3'])
# Specify filetypes using sanitized toc syntax{{{
# Those filetypes have a normalized toc structure. The top level is
# unprefixed and levels 2 to 6 are prefixed, by default, with a vertical
# line and space for each level below 1:
# Level 1
# | Level 2
# ...
# | | | | | Level 6 }}}
final SanitizedTocSyntaxTypes: list<string> =
['asciidoc', 'html', 'markdown', 'tex', 'vim', 'xhtml']
if index(SanitizedTocSyntaxTypes, g:helptoc.type) != -1
# Specified types' toc popups use a common syntax
Win_execute(winid, 'SanitizedTocSyntax()')
else
# Other types' toc popups use the same syntax as the buffer itself
Win_execute(winid, [$'ownsyntax {&filetype}', '&l:conceallevel = 3'])
endif
# In a help file, we might reduce some noisy tags to a trailing asterisk.
# Hide those.
if type == 'help'
if g:helptoc.type == 'help'
matchadd('Conceal', '\*$', 0, -1, {window: winid})
endif
SelectNearestEntryFromCursor(winid)
# can't set the title before jumping to the relevant line, otherwise the
# Can't set the title before jumping to the relevant line, otherwise the
# indicator in the title might be wrong
SetTitle(winid)
enddef
#}}}1
# Core {{{1
def SetToc() #{{{2
var toc: dict<any> = {entries: []}
var type: string = GetType()
# Lambdas:
# CHARACTER_REFERENCES_TO_CHARACTERS {{{3
# These are used for AsciiDoc, Markdown, and [X]HTML, all of which allow
# for decimal, hexadecimal, and XML predefined entities.
# Decimal character references: e.g., &#167; to §
# Hexadecimal character references: e.g., &#xA7; to §
# XML predefined entities to chars: e.g., &lt; to <
# All HTML5 named character references could be handled, though is that
# warranted for the few that may appear in a toc entry, especially when
# they are often mnemonic? Future: A common Vim dict/enum could be useful?
const CHARACTER_REFERENCES_TO_CHARACTERS = (text: string): string =>
text->substitute('\v\&#0*([1-9]\d{0,6});',
'\=nr2char(str2nr(submatch(1), 10), 1)', 'g')
->substitute('\c\v\&#x0*([1-9a-f][[:xdigit:]]{1,5});',
'\=nr2char(str2nr(submatch(1), 16), 1)', 'g')
->substitute('\C&amp;', '\="\u0026"', 'g')
->substitute('\C&apos;', "\u0027", 'g')
->substitute('\C&gt;', "\u003E", 'g')
->substitute('\C&lt;', "\u003C", 'g')
->substitute('\C&quot;', "\u0022", 'g')
# SANITIZE_ASCIIDOC {{{3
# 1 - Substitute the = or # heading markup with the level indicator
# 2 - Substitute XML predefined, dec, and hex char refs in the entry
# AsciiDoc recommends only using named char refs defined in XML:
# https://docs.asciidoctor.org/asciidoc/latest/subs/replacements/
const SANITIZE_ASCIIDOC = (text: string): string =>
text->substitute('\v^(\={1,6}|#{1,6})\s+',
'\=repeat(g:helptoc.level_indicator, len(submatch(1)) - 1)', '')
->CHARACTER_REFERENCES_TO_CHARACTERS()
# SANITIZE_HTML {{{3
# 1 - Remove any leading spaces or tabs
# 2 - Remove any <!--HTML comments-->
# 3 - Remove any <?processing_instructions?>
# 4 - Remove any leading tags (and any blanks) other than <h1 to <h6
# 5 - Remove any persisting leading blanks
# 6 - Handle empty XHTML headings, e.g., <h6 />
# 7 - Remove trailing content following the </h[1-6]>
# 8 - Remove the <h1
# 9 - Substitute the h2 to h6 heading tags with level indicator/level
# 10 - Remove intra-heading tags like <em>, </em>, <strong>, etc.
# 11 - Substitute XML predefined, dec and hex character references
const SANITIZE_HTML = (text: string): string =>
text->substitute('^\s*', '', '')
->substitute('[<]!--.\{-}--[>]', '', 'g')
->substitute('[<]?[^?]\+?[>]', '', 'g')
->substitute('\v%([<][^Hh][^1-6]?[^>][>])*\s*', '', '')
->substitute('^\s\+', '', '')
->substitute('\v[<][Hh]([1-6])\s*[/][>].*',
'\=repeat(g:helptoc.level_indicator, ' ..
'str2nr(submatch(1)) - 1) ' ..
'.. "[Empty heading " .. submatch(1) .. "]"', '')
->substitute('[<][/][Hh][1-6][>].*$', '', '')
->substitute('[<][Hh]1[^>]*[>]', '', '')
->substitute('\v[<][Hh]([2-6])[^>]*[>]',
'\=repeat(g:helptoc.level_indicator, ' ..
'str2nr(submatch(1)) - 1)', '')
->substitute('[<][/]\?[[:alpha:]][^>]*[>]', '', 'g')
->CHARACTER_REFERENCES_TO_CHARACTERS()
# SANITIZE_MARKDOWN #{{{3
# 1 - Hyperlink incl image, e.g. [![Vim The editor](xxx)](\uri), to Vim...
# 2 - Hyperlink [text](/uri) to text
# 3 - Substitute the # ATX heading markup with the level indicator/level
# The omitted markup reflects CommonMark Spec:
# https://spec.commonmark.org/0.31.2/#atx-headings
# 4 - Substitute decimal, hexadecimal, and XML predefined char refs
const SANITIZE_MARKDOWN = (text: string): string =>
text->substitute('\v[\u005B]![\u005B]([^\u005D]+)[\u005D]'
.. '[(][^)]+[)][\u005D][(][^)]+[)]', '\1', '')
->substitute('\v[\u005B]([^\u005D]+)[\u005D][(][^)]+[)]',
'\1', '')
->substitute('\v^ {0,3}(#{1,6})\s*',
'\=repeat(g:helptoc.level_indicator, len(submatch(1)) - 1)',
'')
->CHARACTER_REFERENCES_TO_CHARACTERS()
# SANITIZE_TERMINAL {{{3
# Omit the prompt, which may be very long and otherwise just adds clutter
const SANITIZE_TERMINAL = (text: string): string =>
text->substitute('^' .. g:helptoc.shell_prompt, '', '')
# SANITIZE_TEX #{{{3
# 1 - Use any [toc-title] overrides to move its content into the
# {heading} instead of the (non-ToC) heading's text
# 2 - Replace \part{ or \addcontentsline{toc}{part} with '[PART] '
# 3 - Omit \chapter{ or \addcontentsline{toc}{chapter}
# 4 - Omit \section{ or \addcontentsline{toc}{section}
# 5 - Omit \subsection{ or \addcontentsline{toc}{subsection}
# 6 - Omit \subsubsection{ or \addcontentsline{toc}{subsubsection}
# 7 - Omit the trailing }
# 8 - Unescape common escaped characters &%$_#{}~^\
const SANITIZE_TEX = (text: string): string =>
text->substitute('\v^[\\](part|chapter|%(sub){0,2}section)' ..
'[\u005B]([^\u005D]+).*', '\\\1{\2}', '')
->substitute('^[\\]\(part\|addcontentsline{toc}{part}\){',
'[PART] ', '')
->substitute('^[\\]\(chapter\|addcontentsline{toc}{chapter}\){',
'', '')
->substitute('^[\\]\(section\|addcontentsline{toc}{section}\){',
'\=g:helptoc.level_indicator', '')
->substitute('^[\\]\(subsection\|' ..
'addcontentsline{toc}{subsection}\){',
'\=repeat(g:helptoc.level_indicator, 2)', '')
->substitute('^[\\]\(subsubsection\|' ..
'addcontentsline{toc}{subsubsection}\){',
'\=repeat(g:helptoc.level_indicator, 3)', '')
->substitute('}[^}]*$', '', '')
->substitute('\\\([&%$_#{}~\\^]\)', '\1', 'g')
# SANITIZE_VIM {{{3
# #1 - Omit leading Vim9 script # or vimscript " markers and blanks
# #2 - Omit numbered 3x { markers
const SANITIZE_VIM = (text: string): string =>
text->substitute('\v^[#[:blank:]"]*(.+)\ze[{]{3}([1-6])',
'\=submatch(2) == "1" ? submatch(1) : ' ..
'repeat(g:helptoc.level_indicator, str2nr(submatch(2)) - 1)' ..
' .. submatch(1)', 'g')
->substitute('[#[:blank:]"]*{\{3}[1-6]', '', '')
#}}}3
final toc: dict<any> = {entries: []}
toc.changedtick = b:changedtick
if !toc->has_key('width')
toc.width = 0
endif
# We cache the toc in `b:toc` to get better performance.{{{
#
# Without caching, when we press `H`, `L`, `H`, `L`, ... quickly for a few
# Without caching, when we press `H`, `L`, `H`, `L`, ... quickly for a few
# seconds, there is some lag if we then try to move with `j` and `k`.
# This can only be perceived in big man pages like with `:Man ffmpeg-all`.
#}}}
b:toc = toc
if type == 'help'
if g:helptoc.type == 'help'
SetTocHelp()
return
endif
if type == 'terminal'
if g:helptoc.type == 'terminal'
b:toc.linecount = line('$')
endif
var curline: string = getline(1)
var nextline: string
var lvl_and_test: list<list<any>> = MATCH_ENTRY
->get(type, {})
->get(g:helptoc.type, {})
->items()
->sort((l: list<any>, ll: list<any>): number => l[0]->str2nr() - ll[0]->str2nr())
->sort((l: list<any>, ll: list<any>): number =>
l[0]->str2nr() - ll[0]->str2nr())
var skip_next: bool = false
# Non-help headings processing
for lnum: number in range(1, line('$'))
if skip_next
skip_next = false
curline = nextline
continue
endif
nextline = getline(lnum + 1)
# Special handling for markdown filetype using setext headings
if g:helptoc.type == 'markdown'
# Check for setext formatted headings (= or - underlined)
if nextline =~ '^\s\{0,3}=\+$' && curline =~ '\S'
# Level 1 heading (one or more =, up to three spaces preceding)
b:toc.entries->add({
lnum: lnum,
lvl: 1,
text: SANITIZE_MARKDOWN('# ' .. trim(curline)),
})
skip_next = true
curline = nextline
continue
elseif nextline =~ '^\s\{0,3}-\+$' && curline =~ '\S'
# Level 2 heading (one or more -, up to three spaces preceding)
b:toc.entries->add({
lnum: lnum,
lvl: 2,
text: SANITIZE_MARKDOWN('## ' .. trim(curline)),
})
skip_next = true
curline = nextline
continue
endif
endif
# Regular processing for markdown ATX-style headings + other filetypes
for [lvl: string, IsEntry: func: bool] in lvl_and_test
if IsEntry(curline, nextline)
if g:helptoc.type == 'asciidoc'
curline = curline->SANITIZE_ASCIIDOC()
elseif g:helptoc.type == 'html' || g:helptoc.type == 'xhtml'
curline = curline->SANITIZE_HTML()
elseif g:helptoc.type == 'markdown'
curline = curline->SANITIZE_MARKDOWN()
elseif g:helptoc.type == 'terminal'
curline = curline->SANITIZE_TERMINAL()
elseif g:helptoc.type == 'tex'
curline = curline->SANITIZE_TEX()
elseif g:helptoc.type == 'vim'
curline = curline->SANITIZE_VIM()
endif
b:toc.entries->add({
lnum: lnum,
lvl: lvl->str2nr(),
@ -281,9 +590,9 @@ def SetTocHelp() #{{{2
if main_ruler != '' && curline =~ main_ruler
last_numbered_entry = 0
# The information gathered in `lvls` might not be applicable to all
# the main sections of a help file. Let's reset it whenever we find
# a ruler.
# The information gathered in `lvls` might not be applicable to
# all the main sections of a help file. Let's reset it whenever
# we find a ruler.
InitHelpLvls()
endif
@ -304,7 +613,7 @@ def SetTocHelp() #{{{2
# 1.
if prevline =~ '^\d\+\.\s'
# let's assume that the start of a main entry is always followed by an
# Let's assume that the start of a main entry is always followed by an
# empty line, or a line starting with a tag
&& (curline =~ '^>\=\s*$' || curline =~ $'^\s*{HELP_TAG}')
# ignore a numbered line in a list
@ -337,7 +646,8 @@ def SetTocHelp() #{{{2
if curline =~ HELP_HEADLINE
&& curline !~ '^CTRL-'
&& prevline->IsSpecialHelpLine()
&& (nextline->IsSpecialHelpLine() || nextline =~ '^\s*(\|^\t\|^N[oO][tT][eE]:')
&& (nextline ->IsSpecialHelpLine()
|| nextline =~ '^\s*(\|^\t\|^N[oO][tT][eE]:')
AddEntryInTocHelp('HEADLINE', lnum, curline)
endif
@ -411,7 +721,8 @@ def SetTocHelp() #{{{2
->min()
for entry: dict<any> in b:toc.entries
entry.text = entry.text
->substitute('^\s*', () => repeat(' ', (entry.lvl - min_lvl) * 3), '')
->substitute('^\s*', () =>
repeat(' ', (entry.lvl - min_lvl) * 3), '')
endfor
enddef
@ -455,29 +766,30 @@ def AddEntryInTocHelp(type: string, lnum: number, line: string) #{{{2
# Ignore noisy tags.{{{
#
# 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
# ^----------------------------------------^
# ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\zs\*.*
# 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
# ^----------------------------------------^
# ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\zs\*.*
# ---
#
# We don't use conceal because then, `matchfuzzypos()` could match concealed
# characters, which would be confusing.
# We don't use conceal because then, `matchfuzzypos()` could match
# concealed characters, which would be confusing.
#}}}
# MAKING YOUR OWN SYNTAX FILES *mysyntaxfile*
# ^------------^
# ^\s*[A-Z].\{-}\*\zs.*
# MAKING YOUR OWN SYNTAX FILES *mysyntaxfile*
# ^------------^
# ^\s*[A-Z].\{-}\*\zs.*
#
var after_HEADLINE: string = '^\s*[A-Z].\{-}\*\zs.*'
# 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
# ^----------------------------------------^
# ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\*\zs.*
# 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413*
# ^----------------------------------------^
# ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\*\zs.*
var after_numbered: string = '^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\*\zs.*'
# 01.3 Using the Vim tutor *tutor* *vimtutor*
# ^----------------^
# 01.3 Using the Vim tutor *tutor* *vimtutor*
# ^----------------^
var after_numbered_tutor: string = '^\*\d\+\.\%(\d\+\.\=\)*.\{-}\t\*\zs.*'
var noisy_tags: string = $'{after_HEADLINE}\|{after_numbered}\|{after_numbered_tutor}'
var noisy_tags: string =
$'{after_HEADLINE}\|{after_numbered}\|{after_numbered_tutor}'
text = text->substitute(noisy_tags, '', '')
# We don't remove the trailing asterisk, because the help syntax plugin
# We don't remove the trailing asterisk, because the help syntax plugin
# might need it to highlight some headlines.
b:toc.entries->add({
@ -498,7 +810,7 @@ enddef
def Popup_settext(winid: number, entries: list<dict<any>>) #{{{2
var text: list<any>
# When we fuzzy search the toc, the dictionaries in `entries` contain a
# When we fuzzy search the toc, the dictionaries in `entries` contain a
# `props` key, to highlight each matched character individually.
# We don't want to process those dictionaries further.
# The processing should already have been done by the caller.
@ -544,7 +856,8 @@ def SelectNearestEntryFromCursor(winid: number) #{{{2
var lnum: number = line('.')
var firstline: number = b:toc.entries
->copy()
->filter((_, line: dict<any>): bool => line.lvl <= b:toc.curlvl && line.lnum <= lnum)
->filter((_, line: dict<any>): bool =>
line.lvl <= b:toc.curlvl && line.lnum <= lnum)
->len()
if firstline == 0
return
@ -599,8 +912,8 @@ def Filter(winid: number, key: string): bool #{{{2
if key == 'J' || key == 'K'
var lnum: number = GetBufLnum(winid)
execute $'normal! 0{lnum}zt'
# install a match in the regular buffer to highlight the position of
# the entry in the latter
# Install a match in the regular buffer to highlight the position
# of the entry in the latter
MatchDelete()
selected_entry_match = matchaddpos('IncSearch', [lnum], 0, -1)
endif
@ -665,41 +978,44 @@ def Filter(winid: number, key: string): bool #{{{2
return true
elseif key == '/'
# This is probably what the user expect if they've started a first fuzzy
# search, press Escape, then start a new one.
# This is probably what the user expects if they've started a first
# fuzzy search, press Escape, then start a new one.
DisplayNonFuzzyToc(winid)
[{
group: AUGROUP,
group: 'HelpToc',
event: 'CmdlineChanged',
pattern: '@',
cmd: $'FuzzySearch({winid})',
replace: true,
}, {
group: AUGROUP,
group: 'HelpToc',
event: 'CmdlineLeave',
pattern: '@',
cmd: 'TearDown()',
replace: true,
}]->autocmd_add()
# Need to evaluate `winid` right now with an `eval`'ed and `execute()`'ed heredoc because:{{{
# Need to evaluate `winid` right now{{{
# with an `eval`'ed and `execute()`'ed heredoc because:
#
# - the mappings can only access the script-local namespace
# - `winid` is in the function namespace; not in the script-local one
# - the mappings can only access the script-local namespace
# - `winid` is in the function namespace; not in the script-local one
#}}}
var input_mappings: list<string> =<< trim eval END
cnoremap <buffer><nowait> <Down> <ScriptCmd>Filter({winid}, 'j')<CR>
cnoremap <buffer><nowait> <Up> <ScriptCmd>Filter({winid}, 'k')<CR>
cnoremap <buffer><nowait> <C-N> <ScriptCmd>Filter({winid}, 'j')<CR>
cnoremap <buffer><nowait> <C-P> <ScriptCmd>Filter({winid}, 'k')<CR>
cnoremap <buffer><nowait> <Down> <ScriptCmd>Filter({winid}, 'j')<CR>
cnoremap <buffer><nowait> <Up> <ScriptCmd>Filter({winid}, 'k')<CR>
cnoremap <buffer><nowait> <C-N> <ScriptCmd>Filter({winid}, 'j')<CR>
cnoremap <buffer><nowait> <C-P> <ScriptCmd>Filter({winid}, 'k')<CR>
END
input_mappings->execute()
var look_for: string
try
popup_setoptions(winid, {mapping: true})
look_for = input('look for: ', '', $'custom,{Complete->string()}') | redraw | echo ''
look_for = input('look for: ', '', $'custom,{Complete->string()}')
| redraw
| echo ''
catch /Vim:Interrupt/
TearDown()
finally
@ -718,9 +1034,9 @@ def FuzzySearch(winid: number) #{{{2
return
endif
# We match against *all* entries; not just the currently visible ones.
# Rationale: If we use a (fuzzy) search, we're probably lost. We don't know
# where the info is.
# We match against *all* entries; not just the currently visible ones.
# Rationale: If we use a (fuzzy) search, we're probably lost. We don't
# know where the info is.
var matches: list<list<any>> = b:toc.entries
->copy()
->matchfuzzypos(look_for, {key: 'text'})
@ -764,7 +1080,7 @@ def PrintEntry(winid: number) #{{{2
enddef
def CollapseOrExpand(winid: number, key: string) #{{{2
# Must be saved before we reset the popup contents, so we can
# Must be saved before we reset the popup contents, so we can
# automatically select the least unexpected entry in the updated popup.
var buf_lnum: number = GetBufLnum(winid)
@ -798,11 +1114,11 @@ def CollapseOrExpand(winid: number, key: string) #{{{2
endwhile
endif
# update the popup contents
# Update the popup contents
var toc_entries: list<dict<any>> = GetTocEntries()
Popup_settext(winid, toc_entries)
# Try to select the same entry; if it's no longer visible, select its
# Try to select the same entry; if it's no longer visible, select its
# direct parent.
var toc_lnum: number = 0
for entry: dict<any> in toc_entries
@ -834,6 +1150,8 @@ def Callback(winid: number, choice: number) #{{{2
if choice == -1
fuzzy_entries = null_list
return
elseif choice == -2 # Button X is clicked (when close: 'button')
return
endif
var lnum: number = GetTocEntries()
@ -851,36 +1169,38 @@ def Callback(winid: number, choice: number) #{{{2
enddef
def ToggleHelp(menu_winid: number) #{{{2
# Show/hide HELP_TEXT in a second popup when '?' is typed{{{
# (when a helptoc popup is open). A scrollbar on this popup makes sense
# because it is very long and, even if it's not used for scrolling, works
# well as an indicator of how far through the HELP_TEXT popup you are. }}}
if help_winid == 0
var height: number = [HELP_TEXT->len(), winheight(0) * 2 / 3]->min()
var longest_line: number = HELP_TEXT
->copy()
->map((_, line: string) => line->strcharlen())
->max()
var width: number = [longest_line, winwidth(0) * 2 / 3]->min()
var pos: dict<number> = popup_getpos(menu_winid)
var [line: number, col: number] = [pos.line, pos.col]
--col
var width: number = [longest_line, winwidth(0) - 4]->min()
var zindex: number = popup_getoptions(menu_winid).zindex
++zindex
help_winid = HELP_TEXT->popup_create({
line: line,
col: col,
pos: 'topright',
pos: 'center',
minheight: height,
maxheight: height,
minwidth: width,
maxwidth: width,
border: [],
borderchars: ['─', '│', '─', '│', '┌', '┐', '┘', '└'],
highlight: &buftype == 'terminal' ? 'Terminal' : 'Normal',
scrollbar: false,
zindex: zindex,
border: g:helptoc.popup_border,
borderchars: g:helptoc.popup_borderchars,
borderhighlight: g:helptoc.popup_borderhighlight,
close: g:helptoc.popup_close,
scrollbar: true,
})
setwinvar(help_winid, '&cursorline', true)
setwinvar(help_winid, '&linebreak', true)
matchadd('Special', '^<\S\+\|^\S\{,2} \@=', 0, -1, {window: help_winid})
matchadd('Special', '^<\S\+\|^\S\{,2} \@=', 0, -1,
{window: help_winid})
matchadd('Number', '\d\+', 0, -1, {window: help_winid})
for lnum: number in HELP_TEXT->len()->range()
if HELP_TEXT[lnum] =~ '^─\+$'
@ -898,23 +1218,22 @@ def ToggleHelp(menu_winid: number) #{{{2
enddef
def Win_execute(winid: number, cmd: any) #{{{2
# wrapper around `win_execute()` to enforce a redraw, which might be necessary
# wrapper around `win_execute()` to enforce a redraw, which might be necessary
# whenever we change the cursor position
win_execute(winid, cmd)
redraw
enddef
def TearDown() #{{{2
autocmd_delete([{group: AUGROUP}])
autocmd_delete([{group: 'HelpToc'}])
cunmap <buffer> <Down>
cunmap <buffer> <Up>
cunmap <buffer> <C-N>
cunmap <buffer> <C-P>
enddef
#}}}1
# Util {{{1
def GetType(): string #{{{2
return &buftype == 'terminal' ? 'terminal' : &filetype
return &buftype == 'terminal' ? 'terminal' : &filetype
enddef
def GetTocEntries(): list<dict<any>> #{{{2
@ -944,10 +1263,12 @@ enddef
def Complete(..._): string #{{{2
return b:toc.entries
->copy()
->map((_, entry: dict<any>) => entry.text->trim(' ~')->substitute('*', '', 'g'))
->map((_, entry: dict<any>) =>
entry.text->trim(' ~')->substitute('*', '', 'g'))
->filter((_, text: string): bool => text =~ '^[-a-zA-Z0-9_() ]\+$')
->sort()
->uniq()
->join("\n")
enddef
enddef #}}}2
#}}}1
# vim:et:ft=vim:fdm=marker:

View File

@ -0,0 +1,349 @@
*helptoc.txt* For Vim version 9.1. Last change: 2025 May 04
VIM REFERENCE MANUAL
Interactive table of contents for help buffers and several other filetypes
==============================================================================
1. OVERVIEW *HelpToc-overview*
The helptoc.vim plugin provides one command, :HelpToc, which generates a
hierarchical table of contents in a popup window, which is based on the
structure of a Vim buffer. It was designed initially for help buffers,
but it also works with buffers of the following types:
- asciidoc
- html
- man
- markdown
- terminal
- tex
- vim Note: only with numbered fold markers, e.g. {{{1
- xhtml
1.1 The :HelpToc command *HelpToc-:HelpToc*
The :HelpToc command takes no arguments and it cannot be executed from an
unsupported filetype. Also, it cannot be used to generate a table of contents
for an inactive buffer.
For most buffers of the supported types, :HelpToc may be entered directly
in Vim's |Command-line-mode|. How to use it from Vim's |Terminal-Job| mode is
explained in |HelpToc-terminal-buftype|.
You may choose to map :HelpToc to keys making it easier to use. These are
examples of what could be used: >
nnoremap <Leader>ht <Cmd>HelpToc<CR>
tnoremap <C-t><C-t> <Cmd>HelpToc<CR>
<
2. DETAILS *HelpToc-details*
When the :HelpToc command is executed from an active buffer of a supported
type, a popup window is produced. The window contains a hierarchical and
interactive table of contents. The entries are based on the "headings" in
the buffer.
Jumping to an entry's position in the buffer can be achieved by pressing
enter on the applicable entry. Navigation, and other commands applicable to
the popup window, such as expanding and contracting levels, fuzzy searching,
and jumping to the previous or next entry (leaving the table of contents
itself displayed, using J and K), is provided at |help-TOC|, so that is not
reproduced in this help file.
3. TYPES *HelpToc-types*
Some filetypes have more predictable structures than others. For example,
markdown and asciidoc make the identification of headings (aside from edge
cases, such as when in quotes) straightforward. Some filetypes do not have
such obvious or reliable headings/levels (particularly help buffers).
Further, in some instances, how to enter the :HelpToc command is not
necessarily obvious, e.g., when in a Vim |terminal-window|. So, the following
headings address specific details regarding the in-scope types.
3.1 asciidoc *HelpToc-asciidoc-filetype*
The heading levels in asciidoc are typically identified by lines starting
with a "=" (up to six, one per level), one or more blank characters, and the
heading text. :HelpToc will generate a table of contents based on either
that form of heading type or, because asciidoc also allows for ATX heading
level syntax (i.e., using the "#" character), headings using that format too.
As asciidoc is very structured, its table of contents entries are presented
in a standardized form - see |HelpToc-standardized-toc|. So, the initial
"=" or "#" characters and the following space from the related heading are
not included in the table of contents' entries.
3.2 html *HelpToc-html-filetype*
HTML provides for six levels of headings, <h1> to <h6>, which may be either
upper or lower case and preceded by all sorts of content like <!--comments-->.
:HelpToc will produce a table of contents based on the six heading levels.
As HTML is very structured, its table of contents entries are presented
in a standardized form - see |HelpToc-standardized-toc|. So, the <h1> to <h6>
tags, any preceding content, and any trailing content after the heading text,
is not included in the table of contents' entries.
3.3 man pages *HelpToc-man-filetype*
Retrieving man pages is typically performed in the terminal. To use :HelpToc
to generate a table of contents, the |man.vim| filetype plugin is a
prerequisite. It is provided with Vim, and may be sourced with: >
:runtime ftplugin/man.vim
<
Once sourced, the |:Man| command will open the applicable man page in a new
buffer (of "man" filetype). For example: >
:Man pwd
<
Once in the man buffer, entering :HelpToc opens the table of contents popup,
with level 1 containing section names like NAME, SYNOPSIS, etc. Levels
below 1 include subsections and options, with the level depending on the
number of spaces preceding the content.
The table of contents for a man buffer's popup window has the same syntax
highlighting as man pages. This reflects that its content is reproduced
as-is, i.e., no preceding tags, level-indicating data, etc., need be omitted
for optimal presentation.
3.4 markdown *HelpToc-markdown-filetype*
The headings and levels in markdown typically are ATX formatted headings
(lines starting with up to three spaces, one to six "#", then (optionally)
one or blank characters and the heading text). The alternate form,
setext, uses up to three spaces, the heading 1 text, followed by a
line of one or more "=". The setext heading 2 is similar, but for one or
more "-" instead of "=". There is no heading 3+ in setext. ATX and setext
headings are supported.
As markdown is very structured, its table of contents entries are presented
in a standardized form - see |HelpToc-standardized-toc|. So, they do not
include any leading spaces, any initial "#" characters and the following blank
character(s) in the table of contents' entries.
3.5 terminal *HelpToc-terminal-buftype*
There are no explicit "headings" for a terminal buffer. However, :HelpToc
displays the history of executed shell commands. Those may be specified
by changing the pattern used to match the Vim terminal's prompt.
See |HelpToc-configuration| for examples.
To access the terminal's table of contents, from the Vim's |Terminal-Job| mode
enter CTRL-W N to go to |Terminal-Normal| mode. From there, enter :HelpToc to
generate the table of contents. If you use the terminal's table of contents
a lot, an appropriate mapping may make it easier than using CTRL-W N - e.g.: >
tnoremap <C-t><C-t> <Cmd>HelpToc<CR>
<
As the terminal has only "level 1", the table of contents is presented in a
standardized form - see |HelpToc-standardized-toc| - including only the history
list of commands. The prompt itself is also omitted since it adds no value
repeating it for every entry.
3.6 tex *HelpToc-tex-filetype*
In LaTeX, a document may be structured hierarchically using part, chapter,
and sectioning commands. Document structure levels are:
\part{}
\chapter{}
\section{}
\subsection{}
\subsubsection{}
To keep things simpler, \part{} is supported, though treated as being at
the same level as chapter. Consequently, there are four levels displayed
for a tex filetype's table of contents, regardless of the \documentclass{},
i.e., part and chapter (at level 1), section (level 2), subsection (level 3),
and subsubsection (level 4).
Also supported are:
- The "*" used to produce unnumbered headings, which are not intended
for reproduction in a table of contents: >
\section*{Unnumbered section heading not produced in the TOC}
< - Manual toc entries using \addcontentsline, for example: >
\addcontentsline{toc}{section}{entry in the TOC only!}
<
The table of contents for a tex filetype is in a standardized form -
see |HelpToc-standardized-toc|. Omitted are: the "\", the part, chapter,
*section, or addcontentsline, and the left and right curly
brackets preceding and following each heading's text.
3.7 vim *HelpToc-vim-filetype*
Vimscript and Vim9 script do not have headings or levels inherently like
markup languages. However, Vim provides for |folds| defined by markers (|{{{|),
which themselves may be succeeded by a number explicitly indicating the fold
level. This is the structure recognized and supported by helptoc.vim.
So, for example, the following would produce three table of contents entries: >
vim9script
# Variables {{{1
var b: bool = true
var s: string = $"Fold markers are great? {b}!"
# Functions {{{1
def MyFunction(): void #{{{2
echo s
enddef
MyFunction()
<
The table of contents for that script would appear like this:
Variables
Functions
| MyFunction(): void
Note: The numbered |{{{| marker structure is the only one supported by
helptoc.vim for the vim filetype.
As the {{{1 to {{{6 markers make the "headings" explicit, the table of
contents is in a standardized form - see |HelpToc-standardized-toc|.
It does not include any leading comment markers (i.e., either # or ") and
omits the markers themselves.
3.8 xhtml *HelpToc-xhtml-filetype*
Although XHTML, being XML, is more strictly structured than HTML/HTML5,
there is no practical difference in treatment required for the xhtml filetype
because, at the heading level, the tags that matter are very similar.
See |HelpToc-html-filetype| for how an xhtml filetype's table of contents is
supported.
4. STANDARDIZED TOC *HelpToc-standardized-toc*
The table of contents for a help buffer, terminal, or man page, make sense
being presented in the form they appear, minus trailing content (such as tags).
The table of contents for a markdown, asciidoc, [x]html, terminal, or tex
buffer have superfluous content if the entire line was to be returned.
For example:
- Markdown has "#" characters before headings when using ATX heading notation.
- Asciidoc will have either those or, more often, "=" characters before its
headings.
- HTML, aside from the "<h" headings, may have additional tags, comments,
and whitespace before its headings.
- The Vim terminal has the shell prompt, which adds nothing if repeated for
every heading (and may be very long).
- LaTeX has "\" level indicators like "\section{" and a trailing "}".
Consequently, standardising these filetypes' tables of contents, removing
the "noise", and indicating the contents level of each entry, makes sense.
HelpToc standardizes the markdown, asciidoc, [x]html, terminal and tex tables
of contents by removing extraneous characters, markup indicators, and tags.
It also applies simple, unobtrusive syntax highlighting to the text and level
indicators. By default, it will appear like the following example (though
any level indicators will be less prominent, using |NonText| highlight group).
Level 1
| Level 2
| | Level 3
| | | Level 4
| | | | Level 5
| | | | | Level 6
Note: The "| " level indicator may be changed - see |HelpToc-configuration|.
5. CONFIGURATION *HelpToc-configuration*
All configuration is achieved utilizing the g:helptoc dictionary. Any of the
following may be adjusted to meet your needs or preferences:
g:helptoc key what it controls
------------- ----------------
shell_prompt The terminal prompt, used for creating a table of contents
for the terminal (history list). The default is,
'^\w\+@\w\+:\f\+\$\s', which should match many users' bash
prompt. To change it, either interactively or in your .vimrc,
use (for example for a bare Bourne shell "$ " prompt): >
vim9 g:helptoc.shell_prompt = '^\$\s'
<level_indicator This key's value controls the level indicator used in
standardized tables of contents. The default is '| '
(i.e., a vertical bar and a space), but may be changed to
whatever you want. For example, for a broken bar and space: >
vim9 g:helptoc.level_indicator = '¦ '
<
popup_border By default, the table of contents border will appear above,
right, below, and left of the popup window. If you prefer
not to have the border on the right and left (for example
only), you can achieve that with: >
vim9 g:helptoc.popup_border = [1, 0, 1, 0]
<popup_borderchars
The default border characters for the table of contents popup
window is the list ['─', '│', '─', '│', '┌', '┐', '┘', '└'].
There's nothing wrong with those box drawing characters,
though, for example, if you wanted a border that only uses
ASCII characters, you could make the border spaces only: >
vim9 g:helptoc.popup_borderchars = [' ']
<popup_borderhighlight
The default border highlight group is Normal. You can change
that, perhaps in combination with popup_borderchars, above,
to create a very clearly prominent border. For example, if
the popup_borderchars are made [' '], like above, the border
could be made a solid colour different to the background
with: >
vim9 g:helptoc.popup_borderhighlight = ['Cursor']
< Note: Choosing a highlight group that persists when
colorschemes change may be a good idea if you
do choose to customize this.
popup_drag By default, table of contents popup windows may be dragged
with a mouse. If you want to prevent that from happening,
for whatever reason, you may deactivate it with: >
vim9 g:helptoc.popup_drag = false
<
popup_close Table of contents popups have "none" as the default setting
for this option. If you use a mouse, you may want either
to have the option to close popup windows by clicking on them
or to have a clickable "X" in the top right corner. For the
former, use "click", and for the latter, use "button", e.g.: >
vim9 g:helptoc.popup_close = "button"
<popup_scrollbar
No scrollbar is provided on helptoc popups by default. If you
do want scrollbars (which may be useful as an indicator of how
far through the table of contents you are, not just for using
with a mouse) you may choose to have them with: >
vim9 g:helptoc.popup_scrollbar = true
<
NOTE: Information about the "popup_*" options, above, relate to popup options,
which are explained at the 'second argument' part of |popup_create-arguments|.
6. LIMITATIONS *HelpToc-limitations*
- The help filetype may have edge case formatting patterns. Those may result
in some "headings" not being identified and/or may impact the heading levels
of entries in the table of contents itself.
- Terminal window table of contents may not be active (insofar as jumping to
entries going to the Vim terminal's related command line). For example, if
Vim's terminal is set to Windows PowerShell Core, the table of contents will
display successfully, though the entries go nowhere when Enter, J, or K are
entered on them.
- The tex filetype may have variable sectioning commands depending on the
document class. Consequently, some compromises are made, though they should
have minimal impact. Specifically:
* In instances where \part{} and \chapter{} appear in the same buffer, they
will both present at the top level in the table of contents. This should
be a minor matter because, in many instances, chapters will be in a
separate document using \include{}.
* An article or beamer \documentclass without a \part{} (or any document
with neither any \part{} nor any \chapter{} command) will have no content
at level 1. Consequently, its table of contents entries will all appear
preceded by at least one "| " (by default) because its headings start at
level 2 (presuming \section{} is present).
- The vim filetype is only supported where numbered fold markers are applied.
This is intentional (including not handling unnumbered markers, which, when
used in combination with numbered ones, may be used for folding comments).
helptoc.vim itself provides an exemplar of how to use numbered fold markers,
not only for folds, but to support generating a useful table of contents
using :HelpToc.
==============================================================================
vim:tw=78:ts=8:fo=tcq2:ft=help:

16
runtime/pack/dist/opt/helptoc/doc/tags vendored Normal file
View File

@ -0,0 +1,16 @@
HelpToc-:HelpToc helptoc.txt /*HelpToc-:HelpToc*
HelpToc-asciidoc-filetype helptoc.txt /*HelpToc-asciidoc-filetype*
HelpToc-configuration helptoc.txt /*HelpToc-configuration*
HelpToc-details helptoc.txt /*HelpToc-details*
HelpToc-html-filetype helptoc.txt /*HelpToc-html-filetype*
HelpToc-limitations helptoc.txt /*HelpToc-limitations*
HelpToc-man-filetype helptoc.txt /*HelpToc-man-filetype*
HelpToc-markdown-filetype helptoc.txt /*HelpToc-markdown-filetype*
HelpToc-overview helptoc.txt /*HelpToc-overview*
HelpToc-standardized-toc helptoc.txt /*HelpToc-standardized-toc*
HelpToc-terminal-buftype helptoc.txt /*HelpToc-terminal-buftype*
HelpToc-tex-filetype helptoc.txt /*HelpToc-tex-filetype*
HelpToc-types helptoc.txt /*HelpToc-types*
HelpToc-vim-filetype helptoc.txt /*HelpToc-vim-filetype*
HelpToc-xhtml-filetype helptoc.txt /*HelpToc-xhtml-filetype*
helptoc.txt helptoc.txt /*helptoc.txt*