mirror of
https://github.com/vim/vim
synced 2025-07-15 16:51:57 +00:00
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:
committed by
Christian Brabandt
parent
adfeb4ad95
commit
ba0062b0c7
1
.github/MAINTAINERS
vendored
1
.github/MAINTAINERS
vendored
@ -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
|
||||
|
2
Filelist
2
Filelist
@ -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 \
|
||||
|
@ -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*
|
||||
|
||||
|
531
runtime/pack/dist/opt/helptoc/autoload/helptoc.vim
vendored
531
runtime/pack/dist/opt/helptoc/autoload/helptoc.vim
vendored
@ -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., § to §
|
||||
# Hexadecimal character references: e.g., § to §
|
||||
# XML predefined entities to chars: e.g., < 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\�*([1-9]\d{0,6});',
|
||||
'\=nr2char(str2nr(submatch(1), 10), 1)', 'g')
|
||||
->substitute('\c\v\�*([1-9a-f][[:xdigit:]]{1,5});',
|
||||
'\=nr2char(str2nr(submatch(1), 16), 1)', 'g')
|
||||
->substitute('\C&', '\="\u0026"', 'g')
|
||||
->substitute('\C'', "\u0027", 'g')
|
||||
->substitute('\C>', "\u003E", 'g')
|
||||
->substitute('\C<', "\u003C", 'g')
|
||||
->substitute('\C"', "\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. [](\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][\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:
|
||||
|
349
runtime/pack/dist/opt/helptoc/doc/helptoc.txt
vendored
Normal file
349
runtime/pack/dist/opt/helptoc/doc/helptoc.txt
vendored
Normal 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
16
runtime/pack/dist/opt/helptoc/doc/tags
vendored
Normal 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*
|
Reference in New Issue
Block a user