5

In Vim you can hit Shift-k and open a manual for a string under the cursor.

Is it possible to configure Bash this way as well (when using set -o vi)?

For example:

# '|' represents the position of a cursor.
$ |
# Write a command.
$ grep things *|
# Hit 'esc' to enter normal mode.
# Hit '3b' to move to 'grep'.
$ |grep things *
# Now I would like to hit 'Shift-k' to open 'man grep'.
  • 1
    If you can have multiple terminals, do so. What I cannot have multiple terminals is (1) insert a # at the beginning of the command line and press enter, then (2) use man and finally (3) restore the commented-out command line from history. – AlexP Nov 14 '16 at 15:15

3 Answers3

1

You can bind a bash function to a key with bind -x. In this function, you can access the current content of the input buffer through the variables READLINE_LINE and READLINE_POINT.

run_man () {
  declare prefix="${READLINE_LINE:0:$READLINE_POINT}" suffix="${READLINE_LINE:$READLINE_POINT}"
  declare word="${prefix##*[!-+.0-9A-Z_a-z]}${suffix%%[!-+.0-9A-Z_a-z]*}"
  man "$word"
}
bind -m vi -x '"K": run_man'

It might be more useful to open the man page for the word in command position than for the word under the cursor, but this requires more complex parsing. The bash-completion code may help for that. Or you may settle for the first word on the line, which requires less parsing than getting the current word.

To detect bash builtins and show bash documentation instead of a man page, see universal help/man command: help builtin partial matches

P.S.

It'd be nice to be able to see man without deleting the whole command from the prompt.

I do this often in zsh. I expect that it's possible in bash too, but more complicated to set up.

1

Just use history expansion to reference the command name of the last command given.

$ grep something
$ man !:0

Since history expansion is done before alias expansion, if you want to use an alias you should do it like so:

alias k='man "$(history -p \!:0)"'

Then just type k to look at the man page of the last executed command.

Wildcard
  • 36,499
  • 1
    Thanks but I was looking for a way to do it on the fly with Shift-k. man !:0 is cool but it does not utilise the Shift-k shortcut. Gilles' solution is perfect. – Mateusz Piotrowski Nov 15 '16 at 00:30
0

tl;dr

It is not a standard operation but you can add it pretty easily. See this answer (link).

If you're still willing to hack on the source code...

I've read the source code of readline and it looks like it is pretty doable to add the desired functionality. readline already supports v option which allows you to enter the editing mode and open your $EDITOR. If one analyse the logic of how readline opens $EDITOR then it should be pretty easy to open man with the word under the cursor as an argument.

Here are some interesting greps:

  • grep -RI EDITOR *

    doc/hsuser.texi:is used: @code{$@{FCEDIT:-$@{EDITOR:-vi@}@}}.  This says to use the
    doc/hsuser.texi:@env{EDITOR} variable if that is set, or @code{vi} if neither is set.
    doc/rluser.texi:@code{$VISUAL}, @code{$EDITOR}, and @code{emacs}
    examples/rlfe/ChangeLog:    * line options; use EDITOR/VISUAL to set vi/emacs preference.
    examples/rlfe/README:but if the the environment variable EDITOR is set to "vi" that
    examples/rlfe/rlfe.c: * line options; use EDITOR/VISUAL to set vi/emacs preference.
    examples/rlfe/rlfe.c:      if (getenv ("EDITOR") != 0)
    examples/rlfe/rlfe.c:   vi |= strcmp (getenv ("EDITOR"), "vi") == 0;
    
  • grep -RI -C 5 editing-mode *.c

    bind.c-  { "bell-style",        V_STRING,       sv_bell_style },
    bind.c-  { "comment-begin",     V_STRING,       sv_combegin },
    bind.c-  { "completion-display-width", V_INT,   sv_compwidth },
    bind.c-  { "completion-prefix-display-length", V_INT,   sv_dispprefix },
    bind.c-  { "completion-query-items", V_INT,     sv_compquery },
    bind.c:  { "editing-mode",      V_STRING,       sv_editmode },
    bind.c-  { "emacs-mode-string", V_STRING,       sv_emacs_modestr },  
    bind.c-  { "history-size",      V_INT,          sv_histsize },
    bind.c-  { "isearch-terminators", V_STRING,     sv_isrchterm },
    bind.c-  { "keymap",            V_STRING,       sv_keymap },
    bind.c-  { "keyseq-timeout",    V_INT,          sv_seqtimeout },
    --
    --
    bind.c-  else if (_rl_stricmp (name, "completion-query-items") == 0)
    bind.c-    {
    bind.c-      sprintf (numbuf, "%d", rl_completion_query_items);
    bind.c-      return (numbuf);
    bind.c-    }
    bind.c:  else if (_rl_stricmp (name, "editing-mode") == 0)
    bind.c-    return (rl_get_keymap_name_from_edit_mode ());
    bind.c-  else if (_rl_stricmp (name, "history-size") == 0)
    bind.c-    {
    bind.c-      sprintf (numbuf, "%d", history_is_stifled() ? history_max_entries : 0);
    bind.c-      return (numbuf);
    --
    --
    funmap.c-  { "do-lowercase-version", rl_do_lowercase_version },
    funmap.c-  { "downcase-word", rl_downcase_word },
    funmap.c-  { "dump-functions", rl_dump_functions },
    funmap.c-  { "dump-macros", rl_dump_macros },
    funmap.c-  { "dump-variables", rl_dump_variables },
    funmap.c:  { "emacs-editing-mode", rl_emacs_editing_mode },
    funmap.c-  { "end-kbd-macro", rl_end_kbd_macro },
    funmap.c-  { "end-of-history", rl_end_of_history },
    funmap.c-  { "end-of-line", rl_end_of_line },
    funmap.c-  { "exchange-point-and-mark", rl_exchange_point_and_mark },
    funmap.c-  { "forward-backward-delete-char", rl_rubout_or_delete },
    --
    --
    funmap.c-  { "vi-column", rl_vi_column },
    funmap.c-  { "vi-complete", rl_vi_complete },
    funmap.c-  { "vi-delete", rl_vi_delete },
    funmap.c-  { "vi-delete-to", rl_vi_delete_to },
    funmap.c-  { "vi-eWord", rl_vi_eWord },
    funmap.c:  { "vi-editing-mode", rl_vi_editing_mode },
    funmap.c-  { "vi-end-bigword", rl_vi_eWord },
    funmap.c-  { "vi-end-word", rl_vi_end_word },
    funmap.c-  { "vi-eof-maybe", rl_vi_eof_maybe },
    funmap.c-  { "vi-eword", rl_vi_eword },
    funmap.c-  { "vi-fWord", rl_vi_fWord },