[vim/vim] manpager.vim plugin fails for certain manpages (#2323)

117 views
Skip to first unread message

Ryan Lue

unread,
Nov 12, 2017, 11:01:51 AM11/12/17
to vim/vim, Subscribed

Note

This issue was created to capture/continue discussion on PR #2296 that was unrelated to the content of the PR itself.


I don't really know much about man, but my understanding is that the challenge of manpager.vim is to infer, from the text of a manpage, its precise location in the man db (/usr/share/man or whatever else it might be on other systems). I don't know whether there is a standard for how such a location should be encoded in the first line of the file, but it seems:

  • the general rule for most built-in and commonly-used GNU utilities is manpage headers in uppercase and filenames in lowercase, e.g., LS(1) for /usr/share/man/man1/ls.1

  • an unfortunate and not-uncommon edge case is filenames and manpage headers matching case (e.g., Xorg(1) for /usr/share/man/man1/Xorg.1)

  • another unfortunate edge case, according to @lcd047, is where, e.g., zzz and ZZZ have separate manpages, but may both use uppercased manpage headers (at least, it appears that way for zzz).

  • another (hopefully one-off) edge case I've encountered is with GNU stow (installed via Homebrew on my Mac):

    IO::FILE=IO(0X81D9F0)(1)					User Contributed Perl Documentation					  IO::FILE=IO(0X81D9F0)(1)
    

    (and it turns out this manpage isn't even in section 1 of the manpages — it's in section 8!)

I don't know of any other edge cases (though I'm sure there are at least a few).

Proposal

What would the maintainers think of the following procedure?

  1. Check for both the case-sensitive and downcased versions of the manpage. If only one is found, use that. If both are found, then...
  2. Use the numeric section from the manpage header, and the name from the first word of the first line of the NAME section of the manpage (case-sensitive).
  3. If the manpage header can't be parsed according to the standard format, don't use any numeric section and simply call :Man with the first word of the first line of the NAME section of the manpage (case-sensitive).

I believe this should adequately handle all edge cases without going out of its way to explicitly support the quirks of one platform or another.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub

LCD 047

unread,
Nov 13, 2017, 2:52:49 AM11/13/17
to vim/vim, Subscribed

my understanding is that the challenge of manpager.vim is to infer, from the text of a manpage, its precise location in the man db (/usr/share/man or whatever else it might be on other systems).

It doesn't care about the location, it just tries to infer the name and section of the page being displayed.

Running Vim from MANPAGER is a hack, and a really bad one at that. It's inefficient (it ends up running man at least 3 times), ugly, and broken beyond repair (because the name of the man page can't be reliably inferred from the formatted file). This is the root of the problem, and IMO this is what needs to be addressed.

As I said on vim_dev, a cleaner approach would be to drop MANPAGER altogether, and just write a script (as man(1) replacement) that passes the right options to Vim. The problem with writing such a script is that not all men, err, I mean man(1)s, are born equal, so the script has to be OS-dependent.

Ryan Lue

unread,
Nov 13, 2017, 3:19:34 AM11/13/17
to vim/vim, Subscribed

Forgive my ignorance on the subject, but I've been wondering — why does manpager.vim determine the section/page and reload via :Man in the first place? Why can't vim parse/syntax-highlight/enable-hyperlinks-for the output of man directly?

Kazunobu Kuriyama

unread,
Nov 13, 2017, 4:58:33 AM11/13/17
to vim/vim, Subscribed

I actually have had a similar view about the plugin that @lcd047 expressed in the second paragraph of his comment above, and hence thought file name inference based on heuristics using regex is a last resort to save the plugin somehow if we have to go along with it. Now that I saw the idea not appealing even to the author of the plugin, I'm now inclined to an idea of a new implementation instead of making a further effort to try having the current one work better.

@Konfekt Is there any chance of re-implementing the plugin so that it will handle command-line arguments directly instead of processing output of man to infer them in order to address the remaining issues all at once?

Konfekt

unread,
Nov 13, 2017, 10:38:51 AM11/13/17
to vim/vim, Subscribed

Of course, plugin/manpager.vim is a lazy hack that piggybacks on ftplugin/man.vim that displays man pages OS-independently. When I wrote it, I hoped it to be sufficient thanks to $MAN_PN but later was told that it is only defined on Linux, and even there are exceptions.

As we see know, the work-around by inferring the value of $MAN_PN through the man page itself falls short on Mac OS for man pages containing an upper case letter.

In this case, replacing manpager.vim by an OS-dependent shell script, or even $MANPAGER definition, is the more reliable approach.

Konfekt

unread,
Nov 13, 2017, 11:10:28 AM11/13/17
to vim/vim, Subscribed

Dear @rlue, I am actually a bit surprised that with, say,

export MANPAGER="/bin/sh -c \"col -b | ${vim} -c 'set ft=man ro nomod nolist' -\""

the mappings such as <c-]> do not work, even though nmap <c-]> reveals that they are correctly set.

LCD 047

unread,
Nov 13, 2017, 1:04:35 PM11/13/17
to vim/vim, Subscribed

@Konfekt Actually I think this should work, and it's actually a lot better than a script, with three amendments:

  1. There is no need for col -b, it can be replaced by Vim commands:
    silent keepj %s/\m_\b\ze.//ge
    silent keepj %s/\v(.)\b\ze\1//ge
    The same is true for col -b in man.vim.
  2. On OpenBSD the MANPAGER file is passed as a filename, not on stdin.
  3. For c-] to work the tags must be calculated, which means the pieces of code in man.vim involving s:man_tag_depth have to be moved out of GetPage().

Ryan Lue

unread,
Nov 13, 2017, 9:01:20 PM11/13/17
to vim/vim, Subscribed

@Konfekt, that definitely works (at least on macOS).

@lcd047, I've seen some manpages with bulleted lists formatted like so:

       +^Ho Lorem ipsum dolor...

Of course, it would be trivial to add another substitute command to handle this, but I wonder how many remaining edge cases would need to be tracked down to completely substitute col -b.

Kazunobu Kuriyama

unread,
Nov 13, 2017, 11:01:56 PM11/13/17
to vim/vim, Subscribed

For me, @Konfekt 's

export MANPAGER="/bin/sh -c \"col -b | ${vim} -c 'set ft=man ro nomod nolist' -\""

doesn't work due to the unusual environment variable ${vim}.

But a slightly modified version:

export MANPAGER="/bin/sh -c \"col -b | vim -c 'runtime ftplugin/man.vim | set ft=man ro nomod nolist iskeyword+=:' -\""

works well for me with CTRL-], CTRL-T for tag jump and q for quit.

To enable tag jump to man pages like DBD::DBM(3), I needed to add iskeyword+=:.

Ryan Lue

unread,
Nov 13, 2017, 11:24:09 PM11/13/17
to vim/vim, Subscribed

Ah, should have clarified that I replaced ${vim} with vim, too. Not clear on the intent of the expansion there.

Kazunobu Kuriyama

unread,
Nov 13, 2017, 11:39:29 PM11/13/17
to vim/vim, Subscribed

Yeah, I suspect that might be the reason why @Konfekt couldn't make tag jump work. For the case where a user disables man.vim or plugins in general, I added runtime ftplugin/man.vim. A little bit paranoia.

Kazunobu Kuriyama

unread,
Nov 14, 2017, 2:11:51 AM11/14/17
to vim/vim, Subscribed

Using a slightly modified version of @lcd047 's sequence of substitutions to fix the +^H issue, col(1) can be eliminated from the export expression like this:

export MANPAGER="vim -c 'runtime ftplugin/man.vim' -c 'silent keepj %s/\m_\b\ze.//ge | silent keepj %s/\v(.)\b\ze\1//ge | silent keepj %s/.\b//ge | set ft=man iskeyword+=: ro nomod nolist | norm 1G' -"

Yet another manpager.vim out of this?

Konfekt

unread,
Nov 14, 2017, 3:10:10 AM11/14/17
to vim/vim, Subscribed

I am sorry for the confusion about ${vim}, an oversight. It is given by

vim=$(type 2>/dev/null -a vim | tail -n 1)
vim=${vim#* * }

However, even with /usr/bin/vim instead of ${vim} the mapping <c-]> only throws a cannot find .... instead of jumping to the man pager. This is apparently only due to my Vim setup though.

Even

export MANPAGER="vim -c 'runtime ftplugin/man.vim' -c 'silent keepj %s/\m_\b\ze.//ge | silent keepj %s/\v(.)\b\ze\1//ge | silent keepj %s/.\b//ge | set ft=man iskeyword+=: ro nomod nolist | norm 1G' -"

works fine on every other platform, we can go for it.

Konfekt

unread,
Nov 14, 2017, 3:16:20 AM11/14/17
to vim/vim, Subscribed

As an addendum, even with

export MANPAGER="vim -i NONE -u $XDG_CONFIG_HOME/.vim/viminrc -c 'runtime ftplugin/man.vim' -c 'silent keepj %s/\m_\b\ze.//ge | silent keepj %s/\v(.)\b\ze\1//ge | silent keepj %s/.\b//ge | set ft=man iskeyword+=: ro nomod nolist | norm 1G' -"

and viminrc reading

set nocompatible
language messages en_US 

filetype plugin indent on
syntax enable

let g:tex_flavor = 'latex'
let g:vimtex_fold_enabled = 1

the mapping <c-]> does not work. This is on

VIM - Vi IMproved 8.0 (2016 Sep 12)
Included patches: 1-627

LCD 047

unread,
Nov 14, 2017, 4:17:07 AM11/14/17
to vim/vim, Subscribed

There might still be a problem with man.vim not knowing the title and section of the current page, but I think this is harmless.

Also, to make this work on OpenBSD you just need to drop the last -.

@nuko8 %s/\m_\b\ze.//ge and %s/\v(.)\b\ze\1//ge are particular cases of %s/.\b//ge. If you use the latter you don't need the other two. But you shouldn't need that.

@Konfect Does it work if you don't load your vimrc though?

Konfekt

unread,
Nov 14, 2017, 4:33:34 AM11/14/17
to vim/vim, Subscribed

Dear @lcd047 , the addendum was about loading the vimrc file $XDG_CONFIG_HOME/.vim/viminrc reading

set nocompatible
language messages en_US 

filetype plugin indent on
syntax enable

by replaing vim in the $MANPAGER setting by

vim -i NONE -u $XDG_CONFIG_HOME/.vim/viminrc

Kazunobu Kuriyama

unread,
Nov 14, 2017, 5:13:09 AM11/14/17
to vim/vim, Subscribed

@lcd047

%s/\m_\b\ze.//ge and %s/\v(.)\b\ze\1//ge are particular cases of %s/.\b//ge.

Right if we compare them in terms of a match in patterns.

If you use the latter you don't need the other two. But you shouldn't need that.

If only the latter is used, a lot of ^H's are left over since it doesn't handle the pattern .\b\{2,}. Even if .\b is replaced with .\b\+, strings like x^Hx and _^Hx are still littered (The first character occupies a character cell and is almost invisible, looks like as if it was a single whitespace). On the other hand, if only the other two are used, short strings like x^Hy are left over this time, where x and y are two different characters. That's why I needed all the three.

LCD 047

unread,
Nov 14, 2017, 5:23:28 AM11/14/17
to vim/vim, Subscribed

@nuko8 Why do you have double backspaces in man output? This is certainly non-standard.

Tony Mechelynck

unread,
Nov 14, 2017, 6:26:16 AM11/14/17
to vim/vim, Subscribed

Sorry for being latye in noticing this thread.

@rlue : It is not uncommon for a single keyword to have several manpages in different sections (e.g. for keyword "locale" on openSUSE Linux version Leap 42.3, I see manpages about (1) the locale utility program, as installed on this system; (5) the structure of locale definition files; (7) how to use multilanguage support in C code; (1p) the POSIX theory about how the locale utility program should behave; and (3pm) how to use multilanguage support in Perl code — in that order). What the version of man installed on my system does then, is:

  • if $MAN_POSIXLY_CORRECT is unset, ask the user (by section ID) which of these manpages (s)he wants to read;
  • If it is set, use whichever manpage would have been listed first if it hadn't been set.

Best regards,
Tony.

LCD 047

unread,
Nov 14, 2017, 11:22:54 AM11/14/17
to vim/vim, Subscribed

Yet another random note: on OpenBSD the MANPAGER program is run without a shell, so it's still better to use a small manpager.vim rather than write a long command export MANPAGER=... line.

Kazunobu Kuriyama

unread,
Nov 15, 2017, 11:23:18 AM11/15/17
to vim/vim, Subscribed

@Konfekt

Our ultimate goal here is to have a better plugin/manpager.vim, isn't it? Then we shouldn't get stuck with your addendum since use of the -u option is rather unusual when we use Vim normally. I'm wondering what benefit could be possible through getting man.vim to work with an unusual initialization implied by -u.

Nonetheless, if you still find it something meaningful, how about this?:

export MANPAGER="vim -N -i NONE -u NONE \
            -c 'setlocal ft=man | runtime ftplugin/man.vim' \
            -c 'setlocal bufhidden=hide noswapfile iskeyword+=:' \
            -c 'silent keepj %s/\m_\b\ze.//ge \
            | silent keepj %s/\v(.)\b\ze\1//ge \
            | silent keepj %s/.\b//ge \
            | exe \"norm! 1G\"' \
            -c 'setlocal list& readonly nomodified' \
            -c 'augroup myman \
            | exe \"au FilterReadPost * set ft=man \
            | runtime ftplugin/man.vim\" \
            | augroup END' \
            -"

This should also work with -u /path/to/viminrc instead of -u NONE.

By the way, what is meant by $XDG_CONFIG_HOME/.vim/viminrc? It looks quite ridiculous because, according to the specifications, if $XDG_CONFIG_HOME is either not set or empty, its recommended fallback value is $HOME/.config (I believe people usually use that fallback). Then the path would be expanded to ~/.config/.vim/viminrc. A hidden directory in another hidden directory? Looks like a typo to ~/.config/vim/viminrc. Or, if it's actually not a typo and if /.vim/viminrc is valid, then $XDG_CONFIG_HOME looks like an alias of $HOME. Non-standard and confusing. Either way, this particular usage of the env var only helps people have a feeling that things cannot be worked out well. So, let's avoid it and keep things simple.

Konfekt

unread,
Nov 16, 2017, 4:27:50 AM11/16/17
to vim/vim, Subscribed

Dear @nuko8, the effect of vim -i NONE -u ~/.vim/viminrc with

set nocompatible
language messages en_US 

filetype plugin indent on
syntax enable

was to start Vim with a minimal vimrc, to avoid interference from personal configuration, when testing the issue about the non-working <c-]> mapping. I thought calling Vim with these parameters to be equivalent to replacing ~/.vim/vimrc with ~/.vim/viminrc. This gives, in comparison to vim -u NONE, the additional comfort of ensuring English error messages and syntax highlighting.

With your proposal

export MANPAGER="vim -N -i NONE -u NONE \
            -c 'setlocal ft=man | runtime ftplugin/man.vim' \
            -c 'setlocal bufhidden=hide noswapfile iskeyword+=:' \
            -c 'silent keepj %s/\m_\b\ze.//ge \
            | silent keepj %s/\v(.)\b\ze\1//ge \
            | silent keepj %s/.\b//ge \
            | exe \"norm! 1G\"' \
            -c 'setlocal list& readonly nomodified' \
            -c 'augroup myman \
            | exe \"au FilterReadPost * set ft=man \
            | runtime ftplugin/man.vim\" \
            | augroup END' \
            -"

there appears

--- ~ » man git
stty: 'entrée standard': Ioctl() inappropré pour un périphérique
stty: 'entrée standard': Ioctl() inappropré pour un périphérique
Vim : Lecture de stdin...

stty: 'entrée standard': Ioctl() inappropré pour un périphérique
man: commande terminée avec 1 comme code de retour : LESS=-ix8RmPm Manual page git(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$PM Manual page git(1) ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):?pB %pB\%.. (press h for help or q to quit)$-M -I -R MAN_PN=git(1) vim -N -i NONE -u NONE -c setlocal ft=man | runtime ftplugin/man.vim -c setlocal bufhidden=hide noswapfile iskeyword+=: -c silent keepj %s/\m_\b\ze.//ge   | silent keepj %s/\v(.)\b\ze\1//ge   | silent keepj %s/.\b//ge   | exe "norm! 1G" -c setlocal list& readonly nomodified -c augroup myman   | exe "au FilterReadPost * set ft=man   | runtime ftplugin/man.vim"   | augroup END -

Kazunobu Kuriyama

unread,
Nov 16, 2017, 7:20:00 AM11/16/17
to vim/vim, Subscribed

@Konfekt

Although I never claim the MANPAGER is free from bugs, even judging from my miserably poor knowledge about French (Blame me if I'm wrong :), your issue regarding stty appears to be identical with #2343. So, it seems to me that the failure is apparently unrelated to the MANPAGER. What do you think?

Konfekt

unread,
Nov 16, 2017, 11:00:52 AM11/16/17
to vim/vim, Subscribed

Dear @nuko8 ,

Sorry for the noise. The issue was that the vim executable was a script invoking /usr/bin/vim. Using

export MANPAGER="/usr/bin/vim -N -i NONE -u NONE \
  -c 'setlocal ft=man | runtime ftplugin/man.vim' \
  -c 'setlocal bufhidden=hide noswapfile iskeyword+=:' \
  -c 'silent keepj %s/\m_\b\ze.//ge \
  | silent keepj %s/\v(.)\b\ze\1//ge \
  | silent keepj %s/.\b//ge \
  | exe \"norm! 1G\"' \
  -c 'setlocal list& readonly nomodified' \
  -c 'augroup myman \
  | exe \"au FilterReadPost * set ft=man \
  | runtime ftplugin/man.vim\" \
  | augroup END' \
  -"

the issue about the wrong stty is gone. However, the issue about <c-]> not working persists.

Kazunobu Kuriyama

unread,
Nov 16, 2017, 11:20:03 AM11/16/17
to vim/vim, Subscribed

One of my intentions of writing a -u NONE version was, by making your Vim free from the user settings, to get a clue of the reason why <C-]> didn't work for you. I thought, by source man.vim explicitly, it would work well for you.

What do you get from :nmap after running man with the MANPAGER? That should be at least

n  <C-T>       *@:call <SNR>1_PopPage()<CR>
n  <C-]>       *@:call <SNR>1_PreGetPage(v:count)<CR>

to get page jump to be possible.

Konfekt

unread,
Nov 16, 2017, 11:48:22 AM11/16/17
to vim/vim, Subscribed

Yes, it is

n  <C-]>       *@:call <SNR>1_PreGetPage(v:count)<CR>

as it should be.

Kazunobu Kuriyama

unread,
Nov 16, 2017, 12:04:43 PM11/16/17
to vim/vim, Subscribed

Then, using the MANPAGER, open any man page having the SEE ALSO section and jump to it, place the cursor over one of the man pages there, and do

:call feedkeys("\<C-]>")

What happens?

Konfekt

unread,
Nov 16, 2017, 12:37:11 PM11/16/17
to vim/vim, Subscribed

For example, in man git, there appears Cannot find a 'gittutorial'.

Kazunobu Kuriyama

unread,
Nov 16, 2017, 12:48:58 PM11/16/17
to vim/vim, Subscribed

That's man(1)'s responsibility to find man pages, neither Vim's nor the MANPAGER's. Anyway, that shows that Vim actually understands the mapping correctly. IOW, no issue with both.

Fix your system configuration so that Vim can receive <C-]> from the keyboard, and man can find man pages, please.

Konfekt

unread,
Nov 16, 2017, 1:31:38 PM11/16/17
to vim/vim, Subscribed

Yes, it should read

export MANPAGER="env MANPATH=${MANPATH} vim ...

to make it work.
Not sure why the MANPATH has to be redefined.

Tony Mechelynck

unread,
Nov 16, 2017, 1:42:06 PM11/16/17
to vim/vim, Subscribed

@Konfekt : Maybe $MANPATH isn't «export»ed by the shell to daughter processes' environments? Assuming a bash shell, does export -p |less (at the shell prompt, not in Vim) have a line for MANPATH? (On my openSUSE 42.3 system it does.)

Best regards,
Tony.

LCD 047

unread,
Nov 16, 2017, 1:46:06 PM11/16/17
to vim/vim, Subscribed

Or maybe man runs MANPAGER in a different shell than the one where you do the export.

Kazunobu Kuriyama

unread,
Nov 16, 2017, 10:50:37 PM11/16/17
to vim/vim, Subscribed

Not sure why the MANPATH has to be redefined.

When a shell creates a new process, it calls fork(2)to create it and then exec(3) to execute what is specified by the command-line which triggered the shell to do it. When fork(2) is done, the child process is an exact copy of the parent process. When exec(3) is called, the child implicitly takes the external environ variable of the parent process except for the case where execle(3) is used.

Summing up, the newly created process always shares the same environment list with the parent process unless setenv(3) is called to change it in the new process, or unless execle(3) is invoked with an environment list different from the parent's.

And, there's neither reason nor place for Vim and the MANPAGER to change $MANPATH.

Accordingly, who is to be blamed is neither Vim nor the MANPAGER.

Konfekt

unread,
Nov 17, 2017, 10:24:38 AM11/17/17
to vim/vim, Subscribed

Dear @tonymec, thanks for your help. Yes, export -p, has a line containing MANPATH on OpenSUSE 42.3.

Kazunobu Kuriyama

unread,
Nov 18, 2017, 5:09:05 AM11/18/17
to vim/vim, Subscribed

Then, as explained in terms of the system call and the library function, MANPATH should be available to the MANPAGER without env MANPATH=${MANPATH}, unless MANPATH is empty from the outset.

For the missing MANPATH, there could be several possible scenarios:

  1. unset, declare -x, or env -u is called against MANPATH to make it null and void, or
  2. exec -c is called to an executable to run it with an empty environment list

at some point in the process of command pipeline or subprocess creation.

Plus, having read @lcd047 's latest comment, I read man zsh and found the following paragraph in the section COMPATIBILITY:

In sh and ksh compatibility modes the following parameters are not spe-
cial  and  not  initialized  by the shell: ARGC, argv, cdpath, fignore,
fpath, HISTCHARS, mailpath, MANPATH,  manpath,  path,  prompt,  PROMPT,
PROMPT2, PROMPT3, PROMPT4, psvar, status, watch.

Naturally, things similar to this could happen to other shells.

Indeed, there are a number of possible scenarios to that failure.

Does one or do some of them apply to your case? Since you seem to like doing things with a chain of shell/script invocations, it seems to be very tough for people (at least for me) to guess and pinpoint the exact cause. Please consider simple usage to avoid inadvertent side effects, so that we'll come back to the Vim proper issue.

Konfekt

unread,
Nov 21, 2017, 7:50:37 AM11/21/17
to vim/vim, Subscribed

Hello, thanks for your help. The reason was that a path $path was prepended to the $MANPATH by MANPATH=$path:$MANPATH, but the distribution already added a colon : at the beginning of $MANPATH. Therefore $MANPATH contained a double colon :: which caused the issue.

Kazunobu Kuriyama

unread,
Nov 21, 2017, 8:15:13 AM11/21/17
to vim/vim, Subscribed

Thank you for the explanation. Obviously I failed to catch the case :) Then...what has become of <C-]>? Was that issue resolved?

Konfekt

unread,
Nov 21, 2017, 8:20:50 AM11/21/17
to vim/vim, Subscribed

Yes, now with

export MANPAGER="/usr/bin/vim -N -i NONE -u NONE \
  -c 'setlocal ft=man | runtime ftplugin/man.vim' \
  -c 'setlocal bufhidden=hide noswapfile iskeyword+=:' \
  -c 'silent keepj %s/\m_\b\ze.//ge \
  | silent keepj %s/\v(.)\b\ze\1//ge \
  | silent keepj %s/.\b//ge \
  | exe \"norm! 1G\"' \
  -c 'setlocal list& readonly nomodified' \
  -c 'augroup myman \
  | exe \"au FilterReadPost * set ft=man \
  | runtime ftplugin/man.vim\" \
  | augroup END' \
  -"

the mapping <c-]> works flawlessly.

Kazunobu Kuriyama

unread,
Nov 21, 2017, 8:57:05 AM11/21/17
to vim/vim, Subscribed

Great. Now that the MANPAGER is written only with Vim's command line options and ex commands, and that it works fine for us, I guess it should not be hard to make a new manpager.vim out of it.

By the way, my current MANPAGER is

        export MANPAGER="vim -N -i NONE --not-a-term \
            -c 'setlocal ft=man | runtime ftplugin/man.vim' \
            -c 'setlocal bufhidden=hide noswapfile list& iskeyword+=:' \
            -c 'silent keepj %s/\m_\b\ze.//ge \
            | silent keepj %s/\v(.)\b\ze\1//ge \
            | silent keepj %s/.\b//ge \
            | call cursor(1, 1) | let n = search(\".*(.*)\", \"c\") \
            | if n > 1 | exe \"1,\" . n-1 . \"d\" | endif \
            | syntax on' \
            -c 'setlocal readonly nomodified laststatus&' \
            -"

which handles and resolves the issue that macOS's man sometimes gives one or more empty lines above the header, which prevents syntax highlight for C functions in Sections 2 and 3 (See syntax/man.vim:29--33).

Also, thanks to Patch 8.0.1308, we can use --not-a-term to suppress the "Reading from stdin" warning.

To handle that increasing complexity easier, I think the script deserves it being converted into a plugin. I'd be more than happy if you would try that.

Konfekt

unread,
Nov 22, 2017, 5:18:28 AM11/22/17
to vim/vim, Subscribed

Ok, please go ahead.

Kazunobu Kuriyama

unread,
Nov 22, 2017, 6:21:18 AM11/22/17
to vim/vim, Subscribed

Seems like there's a misunderstanding :) Let's make this clear: As we all know, It's you and only you who has the privilege of maintaining and updating plugin/manpager.vim and asking Bram for its inclusion when you think it necessary. And inclusion or update will never happen without your decision and action. So, what I actually meant was, "Please exercise your privilege for us." :)

Konfekt

unread,
Nov 22, 2017, 7:25:04 AM11/22/17
to vim/vim, Subscribed

I abdicate this privilege and would like to transfer it to you readily, also because I do not know what I am exactly supposed to do now. As I see it, there is now the configuration line

            -c 'setlocal ft=man | runtime ftplugin/man.vim' \
            -c 'setlocal bufhidden=hide noswapfile list& iskeyword+=:' \
            -c 'silent keepj %s/\m_\b\ze.//ge \
            | silent keepj %s/\v(.)\b\ze\1//ge \
            | silent keepj %s/.\b//ge \
            | call cursor(1, 1) | let n = search(\".*(.*)\", \"c\") \
            | if n > 1 | exe \"1,\" . n-1 . \"d\" | endif \
            | syntax on' \
            -c 'setlocal readonly nomodified laststatus&' \
            -"

that goes into .bashrc or .zshrc on MacOS and Linux. This could go into the documentation, and replace the current manpager.vim plug-in. However, there might be some testing necessary, if this line works for everyone. In my .vimrc without having traced it down, there appears, if I do not add -u NONE to the $MANPAGER definition, the warning

Erreur détectée en traitant command line :
E21: Impossible de modifier, 'modifiable' est désactivé

which comes from the %s commands passed to Vim. That is, something in my .vimrc is trying to edit the buffer as well.

Kazunobu Kuriyama

unread,
Nov 22, 2017, 10:28:15 AM11/22/17
to vim/vim, Subscribed

...because I do not know what I am exactly supposed to do now.

(I think you'll find below that what I've been asking you for is much simpler than what you may have thought of.)

Now, our current problem is this:

If we put the following Vim script in ~/.vim/plugin/:

command! -nargs=0 ManPager call s:ManPager() | delcommand ManPager

function! s:ManPager()
  set nocompatible
  set viminfofile=NONE
  set noswapfile 

  setlocal ft=man
  runtime ftplugin/man.vim
  setlocal bufhidden=hide
  setlocal list& iskeyword+=:

  " Emulate 'col -b'
  silent keepj %s/\m_\b\ze.//ge
  silent keepj %s/\v(.)\b\ze\1//ge
  silent keepj %s/.\b//ge

  " Remove empty lines above the header
  call cursor(1, 1)
  let n = search(".*(.*)", "c")
  if n > 1
    exe "1," . n-1 . "d"
  endif

  setlocal nomodified readonly

  setlocal laststatus&
  syntax on
endfunction

and if we set MANPAGER like this:

$ export MANPAGER="vim --not-a-term +ManPager -"

then, does this MANPAGER resolve the issue of this thread while it keeps the functionality the current manapager.vim has?

As far as I can tell, it works on macOS.

I think my intention is now clear: By turning it into a plugin, people can easily discuss its pros and cons in terms of Vim script, or they can even modify it in accordance with their needs to ask changes.

Konfekt

unread,
Nov 23, 2017, 8:06:25 AM11/23/17
to vim/vim, Subscribed

Ok, perfect. The parameter --not-a-term does not cause any hick-ups for Vim versions < 8.0.1308 ?

Konfekt

unread,
Nov 23, 2017, 8:10:25 AM11/23/17
to vim/vim, Subscribed

The same for set viminfofile=NONE. It is not defined here (under Vim 8.0.627).

And

  " Emulate 'col -b'
  silent keepj %s/\m_\b\ze.//ge
  silent keepj %s/\v(.)\b\ze\1//ge
  silent keepj %s/.\b//ge

keeps throwing error E21, the file is non-modifiable.

Christian Brabandt

unread,
Nov 23, 2017, 8:16:59 AM11/23/17
to vim/vim, Subscribed

--no-a-term is available since commit 49c39ff (tag: v7.4.1419)
So it not yet be available on all Vim versions.

'viminfofile' is available since commit c4da113 (tag: v8.0.0716)
so check the version before using it.

  " Emulate 'col -b'
  silent keepj %s/\m_\b\ze.//ge
  silent keepj %s/\v(.)\b\ze\1//ge
  silent keepj %s/.\b//ge

keeps throwing error E21, the file is non-modifiable.

This is for modifying a scratch buffer, right? So you can simply make it modifiable :set ma noro, and make it nomodifiable afterwards. If not you should only perform the substitution for modifiable buffers.

Konfekt

unread,
Nov 23, 2017, 8:21:58 AM11/23/17
to vim/vim, Subscribed

Therefore, how about the following amendments?

command! -nargs=0 MANPAGER call s:ManPager() | delcommand MANPAGER

function! s:ManPager()
  set nocompatible
  if exists('+viminfofile')
    set viminfofile=NONE
  endif
  set noswapfile 

  setlocal ft=man
  runtime ftplugin/man.vim
  setlocal bufhidden=hide
  setlocal list& iskeyword+=:

  " Emulate 'col -b'
  let old_modifiable = &l:modifiable
  setlocal modifiable
  silent keepj %s/\m_\b\ze.//ge
  silent keepj %s/\v(.)\b\ze\1//ge
  silent keepj %s/.\b/
/ge
  let &l:modifiable = old_modifiable

  " Remove empty lines above the header
  call cursor(1, 1)
  let n = search(".*(.*)", "c")
  if n > 1
    exe "1," . n-1 . "d"
  endif

  setlocal nomodified readonly

  setlocal laststatus&
  syntax on
endfunction

I kept the MANPAGER command label for backward compatibility.

Christian Brabandt

unread,
Nov 23, 2017, 9:04:10 AM11/23/17
to vim/vim, Subscribed

A couple of comments:

  • you don't need to restore the modifiable state, you unconditionally set it later to nomodified anyway :)
  • Why are you resetting laststatus? and list?
  • add set buftype=nofile
  • I think the regular expression for replacing col -b can be simplified to silent keepj %s/\v(.)\b\ze\1?//ge
  • perhaps slightly reorder it

overall I would use something like this:

command! -nargs=0 MANPAGER call s:ManPager() | delcommand MANPAGER

function! s:ManPager()
  set nocompatible
  if exists('+viminfofile')
    set viminfofile=NONE
  endif
  set noswapfile 

  setlocal ft=man
  runtime ftplugin/man.vim
  setlocal buftype=nofile bufhidden=hide iskeyword+=: modifiable

  " Emulate 'col -b'
  silent keepj keepp %s/\v(.)\b\ze\1?//ge

  " Remove empty lines above the header
  call cursor(1, 1)
  let n = search(".*(.*)", "c")
  if n > 1
    exe "1," . n-1 . "d"
  endif
  setlocal nomodified readonly

  syntax on
endfunction

Konfekt

unread,
Nov 23, 2017, 10:03:38 AM11/23/17
to vim/vim, Subscribed

I am completely happy with this new version of the MANPAGER command. @nuko8 might have more precise ideas on the latter points pointed out by @chrisbra , though.

LCD 047

unread,
Nov 24, 2017, 2:42:58 AM11/24/17
to vim/vim, Subscribed

@chrisbra ...\ze\1? in a pattern doesn't really make sense... As in, "try \1 first, but don't insist on it if it doesn't match"?

More importantly, I think there is a missing encoding spec hiding there. As far as I can tell that's the only reason why %s/.\b//ge makes any difference. But I don't see any easy way to find the actual encoding at that point.

Christian Brabandt

unread,
Nov 24, 2017, 5:04:59 AM11/24/17
to vim/vim, Subscribed

...\ze\1? in a pattern doesn't really make sense... As in, "try \1 first, but don't insist on it if it doesn't match"?

True, but the same can be said about (and the second one is just a generalisation of the _\b\ze.

  silent keepj %s/\v(.)\b\ze\1//ge
  silent keepj %s/.\b//ge

Why first check if the character following the backspace is the same as before the backspace when afterwards we remove the character in front of the backspace anyway? Why not always do :%s/.\b//ge ?

LCD 047

unread,
Nov 24, 2017, 5:47:23 AM11/24/17
to vim/vim, Subscribed

@chrisbra The "traditional" sequences for overstrike highlighting are a\ba for bold, and _\ba for underline, which is why I suggested above:

silent keepj %s/\m_\b\ze.//ge
silent keepj %s/\v(.)\b\ze\1//ge

Actually, in the old days a\b_ was also used (synonymous to _\ba), but I haven't seen that in a while.

%s/.\b//ge was added by @nuko citing cases of \b\b, but he didn't explain where these come from. Which led me to suspect an encoding problem, or perhaps a single width / double width mismatch.

On the other hand, there isn't much potential for damage from %s/.\b//ge, either. The only case when it might, actually, hurt the actual text is, again, when Vim doesn't detect the correct encoding. shrug

Kazunobu Kuriyama

unread,
Nov 24, 2017, 7:21:00 AM11/24/17
to vim/vim, Subscribed

I'm quite happy with the improvements done by @chrisbra (Thank you, Christian!), and I've been using the improved version on my Mac since this morning and found no flaw in it so far. So, if its possible flaws or bugs remain hypothetical, I think it very good to give it a go in days.

@Konfekt As to the transfer of your privilege, we haven't seen anyone accepting it yet, which implies that you're still the maintainer of the plugin. If you really want to get away from it at all costs, you should first declare your intention to Bram and ask his thoughts about it, since the commitment was made between you and him. Having said that, I'd like you to keep the position as maintainer because I think you deserve it.

Konfekt

unread,
Nov 24, 2017, 7:30:34 AM11/24/17
to vim/vim, Subscribed

Dear @nuko8, I am fine with updating the manpager plug-in as ultimately resolved (between @chrisbra and @lcd047) in this thread.

Kazunobu Kuriyama

unread,
Nov 24, 2017, 7:31:56 AM11/24/17
to vim/vim, Subscribed

I'm very happy to hear that, absolutely!

Christian Brabandt

unread,
Apr 15, 2019, 1:40:06 PM4/15/19
to vim/vim, Subscribed

I think this has been fixed and updated already.

Christian Brabandt

unread,
Apr 15, 2019, 1:40:11 PM4/15/19
to vim/vim, Subscribed

Closed #2323.

Reply all
Reply to author
Forward
0 new messages