From 42e9606c23eab6542678d2786e7e5819fd704e92 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Mon, 14 Mar 2016 05:05:28 -0400 Subject: [PATCH 1/2] man.vim: rewrite - Smart autocomplete. It's automatically sorted, filtered for duplicates and even formats the candidates based on what is needed. For example, `:Man 1 printf` will show the pages that are in section 1m as 'page(sect)' to let you know they are in a more specific section. - Instead of trying to unset $MANPAGER we use the -P flag to set the pager to cat - Always use the section arg '-s', it makes the code much simpler (see comment in s:man-args). - A manpage name starting with '-' is invalid. It's fine for sections because of the use of '-s'. - The tagstack is an actual stack now, makes it much simpler. - By using v:count and v:count1, the plugin can explicitly check whether the user set a count, instead of relying on a default value (0) that is actually a real manpage section. - Extraction of a manpage reference is much more simple. No giant long complicated regexes. Now, the plugin lets `man` handle the actual validation. We merely extract the section and page. Syntax regexes are a bit more specific though to prevent highlighting everything. - Multilingual support in the syntax file. Removed the cruft that was only relevent to vim. Also simplified and improved many of the regexes. - Using shellescape when sending the page and sect as arguments - In general, the code flow is much more obvious. - man#get_page has been split up into smaller functions with explicit responsibilties - ':help' behavior in opening splits and manpages - Comments explaining anything that needs explaining and isn't immediately obvious. - If a manpage has already been loaded but if it were to reloaded at the current width which is the same as the width at which it was loaded at previously, it is not reloaded. - Use substitute to remove the backspaced instead of `col -b`, as the latter doesn't work with other languages. - Open paths to manpages - It uses cWORD instead of cword to get the manpage under the cursor, this helps with files that do not have (,) in iskeyword. It also means the plugin does not set iskeyword locally anymore. - (Man) mapping for easy remapping - Switched to single quotes wherever possible. - Updated docs in $VIMRUNTIME/doc/filetype.txt (still need to update user-manual) - Always call tolower on section name. See comment in s:extract_page_and_sect_fpage - Formatting/consistency cleanup - Automatically map q to ':q' when invoked as $MANPAGER - It also fully supports being used as $MANPAGER. Setting the name and stuff automatically. - Split up the setlocals into multiple lines for easier readability - Better detection of errors by redirecting stderr to /dev/null. If an error occured, stdout will be empty. - Functions return [sect, page] not [page, sect]. Makes more sense with how man takes the arguments as sect and then page. - Pretty prints errors on a single line. - If no section is given, automatically finds the correct section for the buffer name. It also gets the correct page. See the comment in s:get_page - If $MANWIDTH is not set, do not assign directly to $MANWIDTH because then $MANWIDTH will always stay set to the same value as we only use winwidth(0) when the global $MANWIDTH is empty. Instead we set it locally for the command. - Maintainer notes on all files. --- runtime/autoload/man.vim | 364 ++++++++++++++++++++++++++++----------- runtime/doc/filetype.txt | 57 +++--- runtime/ftplugin/man.vim | 70 +++++--- runtime/plugin/man.vim | 8 +- runtime/syntax/man.vim | 90 ++++------ 5 files changed, 374 insertions(+), 215 deletions(-) diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 0dfcc424e2..6f50cce9dc 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -1,137 +1,291 @@ -let s:man_tag_depth = 0 -let s:man_sect_arg = '' -let s:man_find_arg = '-w' +" Maintainer: Anmol Sethi +" Ensure Vim is not recursively invoked (man-db does this) +" by forcing man to use cat as the pager. +" More info here http://comments.gmane.org/gmane.editors.vim.devel/29085 +if &shell =~# 'fish$' + let s:man_cmd = 'man -P cat ^/dev/null' +else + let s:man_cmd = 'man -P cat 2>/dev/null' +endif + +let s:man_find_arg = "-w" + +" TODO(nhooyr) I do not think completion will work on SunOS because I'm not sure if `man -l` +" displays the list of directories that are searched by man for manpages. +" I also do not think Solaris supports the '-P' flag used above and uses only $PAGER. try - if !has('win32') && $OSTYPE !~? 'cygwin\|linux' && system('uname -s') =~? 'SunOS' && system('uname -r') =~? '^5' - let s:man_sect_arg = '-s' + if !has('win32') && $OSTYPE !~? 'cygwin\|linux' && system('uname -s') =~? 'SunOS' && system('uname -r') =~# '^5' let s:man_find_arg = '-l' endif catch /E145:/ " Ignore the error in restricted mode endtry -" Load man page {page} from {section} -" call man#get_page([{section}, ]{page}) -function man#get_page(...) abort - let invoked_from_man = (&filetype ==# 'man') - - if a:0 == 0 - echoerr 'argument required' +function! man#open_page_command(...) abort + if a:0 > 2 + call s:error('too many arguments') return - elseif a:0 > 2 - echoerr 'too many arguments' + elseif a:0 ==# 0 + call s:error('what manual page do you want?') + return + elseif a:0 ==# 1 + let ref = a:000[0] + else + " We combine the name and sect into a manpage reference so that all + " verification/extraction can be kept in a single function. + " If a:000[1] is a reference as well, that is fine because it is the only + " reference that will match. + let ref = a:000[1].'('.a:000[0].')' + endif + try + let [sect, name] = s:extract_sect_and_name_ref(ref) + let [sect, name] = s:verify_exists(sect, name) + catch + call s:error(v:exception) + return + endtry + call s:open_page(sect, name) +endfunction + +" We need count and count1 to ensure the section was explicitly set +" by the user. count defaults to 0 which is a valid section and +" count1 defaults to 1 which is also a valid section. Only when they +" are equal was the count explicitly set. +function! man#open_page_mapping(count, count1, ref) abort + if empty(a:ref) + call s:error('what manual page do you want?') return endif - - let sect = get(a:000, 0) - let page = get(a:000, 1, sect) - - let [page, sect] = s:parse_page_and_section(sect, page) - - if !empty(sect) && s:find_page(sect, page) == 0 - let sect = '' - endif - - if s:find_page(sect, page) == 0 - echo 'No manual entry for '.page - return - endif - - exec 'let s:man_tag_buf_'.s:man_tag_depth.' = '.bufnr('%') - exec 'let s:man_tag_lin_'.s:man_tag_depth.' = '.line('.') - exec 'let s:man_tag_col_'.s:man_tag_depth.' = '.col('.') - let s:man_tag_depth = s:man_tag_depth + 1 - - let editcmd = 'edit' - " Use an existing 'man' window, else open a new one. - if &filetype !=# 'man' - let thiswin = winnr() - wincmd b - if winnr() > 1 - exec thiswin . 'wincmd w' - while 1 - if &filetype ==# 'man' - break - endif - wincmd w - if thiswin == winnr() - break - endif - endwhile + try + let [sect, name] = s:extract_sect_and_name_ref(a:ref) + if a:count ==# a:count1 + " user explicitly set a count + let sect = string(a:count) endif + let [sect, name] = s:verify_exists(sect, name) + catch + call s:error(v:exception) + return + endtry + call s:open_page(sect, name) +endfunction - if &filetype !=# 'man' - let editcmd = 'tabnew' +" attempt to extract the name and sect out of 'name(sect)' +" otherwise just return the largest string of valid characters in ref +function! s:extract_sect_and_name_ref(ref) abort + if a:ref[0] ==# '-' " try ':Man -pandoc' with this disabled. + throw 'manpage name starts with ''-''' + endif + let ref = matchstr(a:ref, '[^()]\+([^()]\+)') + if empty(ref) + let name = matchstr(a:ref, '[^()]\+') + if empty(name) + throw 'manpage reference contains only parantheses' + endif + return ['', name] + endif + let left = split(ref, '(') + " see ':Man 3X curses' on why tolower. + " TODO(nhooyr) Not sure if this is portable across OSs + " but I have not seen a single uppercase section. + return [tolower(split(left[1], ')')[0]), left[0]] +endfunction + +function! s:verify_exists(sect, name) abort + let path = system(s:man_cmd.' '.s:man_find_arg.' '.s:man_args(a:sect, a:name)) + if path !~# '^\/' + if empty(a:sect) + throw 'no manual entry for '.a:name + endif + let path = system(s:man_cmd.' '.s:man_find_arg.' '.shellescape(a:name)) + if path !~# '^\/' + throw 'no manual entry for '.a:name.'('.a:sect.') or '.a:name endif endif + call s:push_tag() + if a:name =~# '\/' + " We do not need to extract the section/name from the path if the name is + " just a path. + return ['', a:name] + endif + " We need to extract the section from the path because sometimes + " the actual section of the manpage is more specific than the section + " we provided to `man`. Try ':Man 3 App::CLI'. + " Also on linux, it seems that the name is case insensitive. So if one does + " ':Man PRIntf', we still want the name of the buffer to be 'printf' or + " whatever the correct capitilization is. + return s:extract_sect_and_name_path(path[:len(path)-2]) +endfunction - silent exec editcmd.' man://'.page.(empty(sect)?'':'('.sect.')') +let s:tag_stack = [] - setlocal modifiable - silent keepjumps norm! 1G"_dG +function! s:push_tag() abort + let s:tag_stack += [{ + \ 'buf': bufnr('%'), + \ 'lnum': line('.'), + \ 'col': col('.'), + \ }] +endfunction + +function! man#pop_tag() abort + if !empty(s:tag_stack) + let tag = remove(s:tag_stack, -1) + execute tag['buf'].'b' + call cursor(tag['lnum'], tag['col']) + endif +endfunction + +" extracts the name and sect out of 'path/name.sect' +function! s:extract_sect_and_name_path(path) abort + let tail = fnamemodify(a:path, ':t') + if a:path =~# '\.\%([glx]z\|bz2\|lzma\|Z\)$' " valid extensions + let tail = fnamemodify(tail, ':r') + endif + let sect = matchstr(tail, '\.\zs[^.]\+$') + let name = matchstr(tail, '^.\+\ze\.[^.]\+$') + return [sect, name] +endfunction + +function! s:open_page(sect, name) + let bufname = 'man://'.a:name.(empty(a:sect)?'':'('.a:sect.')') + let found_man = s:find_man() + " The reason for respecting $MANWIDTH even if it is wider/smaller than the + " current window is that the current window might only be temporarily + " narrow/wide. Since we don't reflow, we should just assume the + " user knows what they're doing and respect $MANWIDTH. if empty($MANWIDTH) - let $MANWIDTH = winwidth(0) + " If $MANWIDTH is not set, we do not assign directly to $MANWIDTH because + " then $MANWIDTH will always stay the same value as we only use + " winwidth(0) when $MANWIDTH is empty. Instead we set it locally for the command. + let manwidth = winwidth(0) + else + let manwidth = $MANWIDTH endif - silent exec 'r!/usr/bin/man '.s:cmd(sect, page).' | col -b' - " Remove blank lines from top and bottom. + if getbufvar(bufname, 'manwidth') ==# manwidth + if found_man + silent execute 'buf '.bufnr(bufname) + else + execute 'split '.bufname + endif + keepjumps 1 + return + endif + let already_opened = bufexists(bufname) + if found_man + execute 'edit '.bufname + else + execute 'split '.bufname + endif + if already_opened + setlocal modifiable + setlocal noreadonly + keepjumps %delete _ + endif + silent execute 'read!env MANWIDTH='.manwidth.' '.s:man_cmd.' '.s:man_args(a:sect, a:name) + let b:manwidth = manwidth + " remove all the backspaced text + silent keeppatterns keepjumps %substitute,.\b,,ge while getline(1) =~# '^\s*$' silent keepjumps 1delete _ endwhile - while getline('$') =~# '^\s*$' - silent keepjumps $delete _ - endwhile - setlocal nomodified setlocal filetype=man +endfunction - if invoked_from_man || editcmd ==# 'tabnew' - call s:set_window_local_options() +function! s:find_man() abort + if &filetype ==# 'man' + return 1 endif -endfunction - -function s:set_window_local_options() abort - setlocal colorcolumn=0 foldcolumn=0 nonumber - setlocal nolist norelativenumber nofoldenable -endfunction - -function man#pop_page() abort - if s:man_tag_depth > 0 - let s:man_tag_depth = s:man_tag_depth - 1 - exec "let s:man_tag_buf=s:man_tag_buf_".s:man_tag_depth - exec "let s:man_tag_lin=s:man_tag_lin_".s:man_tag_depth - exec "let s:man_tag_col=s:man_tag_col_".s:man_tag_depth - exec s:man_tag_buf."b" - exec s:man_tag_lin - exec "norm! ".s:man_tag_col."|" - exec "unlet s:man_tag_buf_".s:man_tag_depth - exec "unlet s:man_tag_lin_".s:man_tag_depth - exec "unlet s:man_tag_col_".s:man_tag_depth - unlet s:man_tag_buf s:man_tag_lin s:man_tag_col - endif -endfunction - -" Expects a string like 'access' or 'access(2)'. -function s:parse_page_and_section(sect, str) abort - try - let [page, sect] = matchlist(a:str, '\v\C([-.[:alnum:]_]+)%(\(([-.[:alnum:]_]+)\))?')[1:2] - if empty(sect) - let sect = a:sect + let thiswin = winnr() + while 1 + wincmd w + if &filetype ==# 'man' + return 1 + elseif thiswin ==# winnr() + return 0 endif - catch - echoerr 'man.vim: failed to parse: "'.a:str.'"' - endtry - - return [page, sect] + endwhile endfunction -function s:cmd(sect, page) abort - if !empty(a:sect) - return s:man_sect_arg.' '.a:sect.' '.a:page +function! s:man_args(sect, name) abort + if empty(a:sect) + return shellescape(a:name) endif - return a:page + " The '-s' flag is very useful. + " We do not need to worry about stuff like 'printf(echo)' + " (two manpages would be interpreted by man without -s) + " We do not need to check if the sect starts with '-' + " Lastly, the 3pcap section on macOS doesn't work without -s + return '-s '.shellescape(a:sect).' '.shellescape(a:name) endfunction -function s:find_page(sect, page) abort - let where = system('/usr/bin/man '.s:man_find_arg.' '.s:cmd(a:sect, a:page)) - return (where =~# '^ */') +function! s:error(msg) abort + redraw + echon 'man.vim: ' + echohl ErrorMsg + echon a:msg + echohl None +endfunction + +let s:mandirs = join(split(system(s:man_cmd.' '.s:man_find_arg), ':\|\n'), ',') + +" see s:extract_sect_and_name_ref on why tolower(sect) +function! man#complete(arg_lead, cmd_line, cursor_pos) abort + let args = split(a:cmd_line) + let l = len(args) + if l > 3 + return + elseif l ==# 1 + let name = '' + let sect = '' + elseif a:arg_lead =~# '^[^()]\+([^()]*$' + " cursor (|) is at ':Man printf(|' or ':Man 1 printf(|' + " The later is is allowed because of ':Man pri'. + " It will offer 'priclass.d(1m)' even though section is specified as 1. + let tmp = split(a:arg_lead, '(') + let name = tmp[0] + let sect = tolower(get(tmp, 1, '')) + elseif args[1] !~# '^[^()]\+$' + " cursor (|) is at ':Man 3() |' or ':Man (3|' or ':Man 3() pri|' + " or ':Man 3() pri |' + return + elseif l ==# 2 + if empty(a:arg_lead) + " cursor (|) is at ':Man 1 |' + let name = '' + let sect = tolower(args[1]) + else + " cursor (|) is at ':Man pri|' + if a:arg_lead =~# '\/' + " if the name is a path, complete files + " TODO(nhooyr) why does this complete the last one automatically + return glob(a:arg_lead.'*', 0, 1) + endif + let name = a:arg_lead + let sect = '' + endif + elseif a:arg_lead !~# '^[^()]\+$' + " cursor (|) is at ':Man 3 printf |' or ':Man 3 (pr)i|' + return + else + " cursor (|) is at ':Man 3 pri|' + let name = a:arg_lead + let sect = tolower(args[1]) + endif + " We remove duplicates incase the same manpage in different languages was found. + return uniq(sort(map(globpath(s:mandirs,'man?/'.name.'*.'.sect.'*', 0, 1), 's:format_candidate(v:val, sect)'), 'i')) +endfunction + +function! s:format_candidate(c, sect) abort + if a:c =~# '\.\%(pdf\|in\)$' " invalid extensions + return + endif + let [sect, name] = s:extract_sect_and_name_path(a:c) + if sect ==# a:sect + return name + elseif sect =~# a:sect.'[^.]\+$' + " We include the section if the user provided section is a prefix + " of the actual section. + return name.'('.sect.')' + endif endfunction diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 76aa3a50ce..644f7c23e1 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -500,7 +500,7 @@ Options: avoid that a Subject line with "Vim:" in it will cause an error message. 'textwidth' is set to 72. This is often recommended for e-mail. -'formatoptions' is set to break text lines and to repeat the comment leader +'formatoptions' is set to break text lines and to repeat the comment leader in new lines, so that a leading ">" for quotes is repeated. You can also format quoted text with |gq|. @@ -512,37 +512,48 @@ Local mappings: MAN *ft-man-plugin* *:Man* *man.vim* -Displays a manual page in a nice way. Also see the user manual -|find-manpage|. +View manpages from the comfort of vim. Features include syntax highlighting, +smart autocompletion, multilingual support, and manpage jumping. +Also see |find-manpage|. -To start using the ":Man" command before any manual page was loaded, source -this script from your startup vimrc file: > +You can use neovim as a manpager with > - runtime ftplugin/man.vim - -Options: -'iskeyword' the '.' character is added to be able to use CTRL-] on the - manual page name. + export MANPAGER="nvim -c 'set ft=man' -" Commands: -Man {name} Display the manual page for {name} in a window. -Man {number} {name} - Display the manual page for {name} in a section {number}. +Man {name} Display the manpage for {name} in a new tab. +Man {sect} {name} Same as above except specify the section. +Man {name}({sect}) Alternate syntax to above. Exists so that you can see + the section of the manpage you are completing. +Man {sect} {name}({sect}) This is used during completion to show the real section + of a manpage when the provided section is a prefix. + E.g. section 1m vs 1. -Global mapping: -K Displays the manual page for the word under the cursor. +Man {path} Open the manpage specified by path, if it's in the current + directory, specify the leading ./ +Global Mappings: +(Man) Jump to the manpage for the under the + cursor in a new tab. Takes a count as the manpage + section. Local mappings: -CTRL-] Jump to the manual page for the word under the cursor. -CTRL-T Jump back to the previous manual page. -q Same as ":quit" +CTRL-] Jump to the manpage for the under the cursor. Takes a count + as the manpage section. +K Same as CTRL-]. +CTRL-T Jump back to the previous manpage. +q Close the window. + +Variables: +g:no_man_maps + If set, no mappings are created in man buffers. + +g:ft_man_folding_enable + If set to 1, manpages are folded with foldmethod=indent and + foldnestmax=1. -To enable folding use this: > - let g:ft_man_folding_enable = 1 If you do not like the default folding, use an autocommand to add your desired -folding style instead. For example: > - autocmd FileType man setlocal foldmethod=indent foldenable - +folding style instead. For example: > + :autocmd FileType man setlocal foldmethod=indent foldenable PDF *ft-pdf-plugin* diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 04ab539fb1..a45a380eb2 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -1,41 +1,61 @@ -" Vim filetype plugin file -" Language: man -" Maintainer: SungHyun Nam +" Maintainer: Anmol Sethi +" Previous Maintainer: SungHyun Nam -if has('vim_starting') && &filetype !=# 'man' - finish -endif - -" Only do this when not done yet for this buffer if exists('b:did_ftplugin') finish endif let b:did_ftplugin = 1 -" Ensure Vim is not recursively invoked (man-db does this) -" when doing ctrl-[ on a man page reference. -if exists('$MANPAGER') - let $MANPAGER = '' +let s:pager = 0 + +if has('vim_starting') + let s:pager = 1 + " remove all those backspaces + silent keeppatterns keepjumps %substitute,.\b,,ge + if getline(1) =~# '^\s*$' + silent keepjumps 1delete _ + else + keepjumps 1 + endif + " This is not perfect.See `man glDrawArraysInstanced`. Since the title is + " all caps it is impossible to tell what the original capitilization was. + execute 'file '.'man://'.tolower(matchstr(getline(1), '^\S\+')) endif -setlocal iskeyword+=\.,-,(,) +setlocal buftype=nofile +setlocal noswapfile +setlocal bufhidden=hide +setlocal nobuflisted +setlocal nomodified +setlocal readonly +setlocal nomodifiable +setlocal noexpandtab +setlocal tabstop=8 +setlocal softtabstop=8 +setlocal shiftwidth=8 -setlocal buftype=nofile noswapfile -setlocal nomodifiable readonly bufhidden=hide nobuflisted tabstop=8 +setlocal nonumber +setlocal norelativenumber +setlocal foldcolumn=0 +setlocal colorcolumn=0 +setlocal nolist +setlocal nofoldenable -if !exists("g:no_plugin_maps") && !exists("g:no_man_maps") - nnoremap :call man#get_page(v:count, expand('')) - nnoremap :call man#pop_page() - nnoremap q c - if &keywordprg !=# ':Man' - nnoremap K :call man#get_page(v:count, expand('')) +if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') + nmap (Man) + nmap K (Man) + if s:pager + nnoremap q :q + else + nnoremap q c endif endif -if exists('g:ft_man_folding_enable') && (g:ft_man_folding_enable == 1) - setlocal foldmethod=indent foldnestmax=1 foldenable +if get(g:, 'ft_man_folding_enable', 0) + setlocal foldenable + setlocal foldmethod=indent + setlocal foldnestmax=1 endif -let b:undo_ftplugin = 'setlocal iskeyword<' - +let b:undo_ftplugin = '' " vim: set sw=2: diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index 8e5062a209..c47459a551 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -1,6 +1,10 @@ -if get(g:, 'loaded_man', 0) +" Maintainer: Anmol Sethi + +if exists('g:loaded_man') finish endif let g:loaded_man = 1 -command! -count=0 -nargs=+ Man call man#get_page(, ) +command! -complete=customlist,man#complete -nargs=* Man call man#open_page_command() + +nnoremap (Man) :call man#open_page_mapping(v:count, v:count1, expand('')) diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index fbc1847e6e..aeba0b1e3c 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -1,67 +1,37 @@ -" Vim syntax file -" Language: Man page -" Maintainer: SungHyun Nam -" Previous Maintainer: Gautam H. Mudunuri -" Version Info: -" Last Change: 2015 Nov 24 +" Maintainer: Anmol Sethi +" Previous Maintainer: SungHyun Nam -" Additional highlighting by Johannes Tanzler : -" * manSubHeading -" * manSynopsis (only for sections 2 and 3) - -" For version 5.x: Clear all syntax items -" For version 6.x: Quit when a syntax file was already loaded -if version < 600 - syntax clear -elseif exists("b:current_syntax") +if exists('b:current_syntax') finish endif -" Get the CTRL-H syntax to handle backspaced text -if version >= 600 - runtime! syntax/ctrlh.vim -else - source :p:h/ctrlh.vim +syntax case ignore +syntax match manReference '[^()[:space:]]\+([0-9nx][a-z]*)' +syntax match manSectionHeading '^\%(\S.*\)\=\S$' +syntax match manTitle '^\%1l.*$' +syntax match manSubHeading '^\s\{3\}\S.*$' +syntax match manOptionDesc '^\s\+\%(+\|--\=\)\S\+' + +highlight default link manTitle Title +highlight default link manSectionHeading Statement +highlight default link manOptionDesc Constant +highlight default link manReference PreProc +highlight default link manSubHeading Function + +if getline(1) =~# '^[^()[:space:]]\+([23].*' + syntax include @cCode $VIMRUNTIME/syntax/c.vim + syntax match manCFuncDefinition display '\<\h\w*\>\s*('me=e-1 contained + syntax region manSynopsis start='\V\^\%( + \SYNOPSIS\| + \SYNTAX\| + \SINTASSI\| + \SKŁADNIA\| + \СИНТАКСИС\| + \書式\)\$'hs=s+8 end='^\%(\S.*\)\=\S$'me=e-12 keepend contains=manSectionHeading,@cCode,manCFuncDefinition + highlight default link manCFuncDefinition Function endif -syn case ignore -syn match manReference "\f\+([1-9][a-z]\=)" -syn match manTitle "^\f\+([0-9]\+[a-z]\=).*" -syn match manSectionHeading "^[a-z][a-z -]*[a-z]$" -syn match manSubHeading "^\s\{3\}[a-z][a-z -]*[a-z]$" -syn match manOptionDesc "^\s*[+-][a-z0-9]\S*" -syn match manLongOptionDesc "^\s*--[a-z0-9-]\S*" -" syn match manHistory "^[a-z].*last change.*$" +" Prevent everything else from matching the last line +execute 'syntax match manFooter "^\%'.line('$').'l.*$"' -if getline(1) =~ '^[a-zA-Z_]\+([23])' - syntax include @cCode :p:h/c.vim - syn match manCFuncDefinition display "\<\h\w*\>\s*("me=e-1 contained - syn region manSynopsis start="^SYNOPSIS"hs=s+8 end="^\u\+\s*$"me=e-12 keepend contains=manSectionHeading,@cCode,manCFuncDefinition -endif - - -" Define the default highlighting. -" For version 5.7 and earlier: only when not done already -" For version 5.8 and later: only when an item doesn't have highlighting yet -if version >= 508 || !exists("did_man_syn_inits") - if version < 508 - let did_man_syn_inits = 1 - command -nargs=+ HiLink hi link - else - command -nargs=+ HiLink hi def link - endif - - HiLink manTitle Title - HiLink manSectionHeading Statement - HiLink manOptionDesc Constant - HiLink manLongOptionDesc Constant - HiLink manReference PreProc - HiLink manSubHeading Function - HiLink manCFuncDefinition Function - - delcommand HiLink -endif - -let b:current_syntax = "man" - -" vim:ts=8 sts=2 sw=2: +let b:current_syntax = 'man' From 66ceb5a487eff0b856142b41ce0a95d3b1ea85e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 4 Aug 2016 23:22:51 -0400 Subject: [PATCH 2/2] man.vim: doc, UX tweaks s:error: Convention is to highlight the entire message, so stick to that. --- runtime/autoload/man.vim | 5 ++--- runtime/doc/filetype.txt | 40 ++++++++++++++++------------------------ runtime/doc/usr_12.txt | 19 ++++--------------- runtime/doc/vim_diff.txt | 1 + 4 files changed, 23 insertions(+), 42 deletions(-) diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 6f50cce9dc..79931b61b1 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -27,7 +27,7 @@ function! man#open_page_command(...) abort call s:error('too many arguments') return elseif a:0 ==# 0 - call s:error('what manual page do you want?') + call s:error('missing argument') return elseif a:0 ==# 1 let ref = a:000[0] @@ -221,9 +221,8 @@ endfunction function! s:error(msg) abort redraw - echon 'man.vim: ' echohl ErrorMsg - echon a:msg + echon 'man.vim: ' a:msg echohl None endfunction diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 644f7c23e1..92ed9de369 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -512,44 +512,36 @@ Local mappings: MAN *ft-man-plugin* *:Man* *man.vim* -View manpages from the comfort of vim. Features include syntax highlighting, -smart autocompletion, multilingual support, and manpage jumping. -Also see |find-manpage|. +View manpages in Nvim. Supports highlighting, autocompletion, locales, and +navigation. See also |find-manpage|. -You can use neovim as a manpager with > +To use Nvim as a manpager: export MANPAGER="nvim -c 'set ft=man' -" Commands: -Man {name} Display the manpage for {name} in a new tab. -Man {sect} {name} Same as above except specify the section. -Man {name}({sect}) Alternate syntax to above. Exists so that you can see - the section of the manpage you are completing. -Man {sect} {name}({sect}) This is used during completion to show the real section - of a manpage when the provided section is a prefix. - E.g. section 1m vs 1. +Man {name} Display the manpage for {name} in a window. +Man {sect} {name} Display the manpage for {name} and section {sect}. +Man {name}({sect}) Alternate syntax which auto-completes the section. +Man {sect} {name}({sect}) Used during completion to show the real section of + when the provided section is a prefix, e.g. 1m vs 1. +Man {path} Open the manpage specified by path. Use "./" if it + is in the current directory. -Man {path} Open the manpage specified by path, if it's in the current - directory, specify the leading ./ Global Mappings: (Man) Jump to the manpage for the under the - cursor in a new tab. Takes a count as the manpage - section. + cursor in a new tab. Takes a count for the section. Local mappings: -CTRL-] Jump to the manpage for the under the cursor. Takes a count - as the manpage section. -K Same as CTRL-]. +K +CTRL-] Jump to the manpage for the under the + cursor. Takes a count for the section. CTRL-T Jump back to the previous manpage. q Close the window. Variables: -g:no_man_maps - If set, no mappings are created in man buffers. - -g:ft_man_folding_enable - If set to 1, manpages are folded with foldmethod=indent and - foldnestmax=1. +g:no_man_maps Do not create mappings in manpage buffers. +g:ft_man_folding_enable Fold manpages with foldmethod=indent foldnestmax=1. If you do not like the default folding, use an autocommand to add your desired folding style instead. For example: > diff --git a/runtime/doc/usr_12.txt b/runtime/doc/usr_12.txt index 237abae55f..169d886e78 100644 --- a/runtime/doc/usr_12.txt +++ b/runtime/doc/usr_12.txt @@ -237,19 +237,8 @@ simple way: Move the cursor to the word you want to find help on and press > K -Vim will run the external "man" program on the word. If the man page is -found, it is displayed. This uses the normal pager to scroll through the text -(mostly the "more" program). When you get to the end pressing will -get you back into Vim. - -A disadvantage is that you can't see the man page and the text you are working -on at the same time. There is a trick to make the man page appear in a Vim -window. First, load the man filetype plugin: > - - :runtime! ftplugin/man.vim - -Put this command in your vimrc file if you intend to do this often. Now you -can use the ":Man" command to open a window on a man page: > +Nvim will run |:Man| on the word. If the man page is found, it is displayed. +You can also use the |:Man| command to open a window on a man page: > :Man csh @@ -267,7 +256,7 @@ window. To display a man page for the word under the cursor, use this: > - \K + K (If you redefined the , use it instead of the backslash). For example, you want to know the return value of "strstr()" while editing @@ -275,7 +264,7 @@ this line: if ( strstr (input, "aap") == ) ~ -Move the cursor to somewhere on "strstr" and type "\K". A window will open +Move the cursor to somewhere on "strstr" and type "K". A window will open to display the man page for strstr(). ============================================================================== diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 937ed9e8ba..47380428b0 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -96,6 +96,7 @@ Options: Commands: |:CheckHealth| + |:Man| has many improvements, including auto-completion Functions: |execute()| works with |:redir|