manをもっと便利に使おう！(ビルトインコマンドに対応 + 色付きで表示)


はじめに
========

よく使うコマンドの筆頭に`man`コマンドがありますが(僕調べ)、builtinコマンドや、"if"などの予約語についても同じように調べたいことがあるかと思います。

また、manページをlessで表示するときに、デフォルトでは白黒となっていますが、より見やすくするために色付きで表示するようにします。

使用するシェルはzshで、**それ以外のシェルでは動作しません**。

ビルトインコマンド、エイリアス、予約語に対する扱い
==================================================

```shell-session
$ whence -wa cd
```

を実行すると、

```shell-session
cd: alias
cd: builtin
```

のように、`cd`がシェルにどのように解釈されるかを表示することができます。

(上の例のように複数の対象が存在する場合には`fzf`などで選択すればよいでしょう。)

": "の右側には"builtin","reserved","alias","function","command","hash","none"がくるので、この値に応じて異なる動作をするように新たに`man`コマンドを定義し直すことにします。

> 参考になるもの:
>
> * [Can I get individual man pages for the bash builtin commands? - Unix & Linux Stack Exchange](http://unix.stackexchange.com/questions/18087/can-i-get-individual-man-pages-for-the-bash-builtin-commands)
> * [manpage - How to make man work for shell builtin commands and keywords? - Ask Ubuntu](http://askubuntu.com/questions/439410/how-to-make-man-work-for-shell-builtin-commands-and-keywords)
>

ただし、上の場合にはbashのビルトインコマンドに関しての話なので、今回はzsh用にしてみたいと思います。

for builtin commands
--------------------

`man zshbuiltins`を実行し、そのあと`/cd`のように実行されるようにします。

```shell-session
$ man_indent=7
$ _space="$(printf '%*s' "${man_indent}" '')"
$ /usr/bin/man --pager="less -p'^${_space}cd '" zshbuiltins
```

(TODO: コマンドの字下げ幅は環境依存でないか?)

**注意**

Ubuntuなどでzshbuiltinsがない場合には、手動でインストールする必要があります(バージョンに注意)。

```shell-session
$ mkdir -p ~/Downloads/zsh-doc; cd $_
$ wget http://downloads.sourceforge.net/project/zsh/zsh/5.0.2/zsh-5.0.2.tar.bz2
$ tar -xvf zsh-5.0.2.tar.bz2
$ sudo cp Doc/*.1 /usr/local/share/man/man1
```

### 例

```shell-session
man cd
```

結果:
![screen_002.png](https://qiita-image-store.s3.amazonaws.com/0/54869/e6d12692-3d66-e752-4c00-65d9305deef8.png)


for reserved words
------------------

`man zshall`の"*COMPLEX COMMANDS*"と"*ALTERNATE FORMS FOR COMPLEX COMMANDS*"のセクションに、具体的な使い方が書かれているので、

```shell-session
$ /usr/bin/man --pager="less -p'^COMPLEX COMMANDS$'" zshall
```

が実行されるようにします。

(builtinの時と同じようにしてもよかったのですが、`esac`などがマッチしないので。)

### 例

```shell-session
man esac
```

結果:
![screen_003.png](https://qiita-image-store.s3.amazonaws.com/0/54869/27f99bc2-c7ad-8302-8656-99dd9ae685ce.png)


for aliases
-----------

`whence`コマンドのcshフォーマットでの出力をみるようにします。

### 例

```shell-session
$ whence -c dot
dot: aliased to dot_main
```

(`dot_main`は関数)

結果:
![screen_004.png](https://qiita-image-store.s3.amazonaws.com/0/54869/d80ca3c2-9234-52b1-a96f-b3118e199695.png)

for functions
-------------

`whence -f dot_main`とすれば、その関数`dot_main`の中身を表示します。
`pygmentize`コマンドなどが使えれば、カラーでハイライトして表示できるので、それを使い`$MANPAGER`で表示します。
(後述するように`less -R`を指定していればカラー表示出来るようになります。)

```zsh
if hash pygmentize 2>/dev/null; then
  whence -f "$1" \
    | pygmentize -l sh \
    | ${MANPAGER:-${PAGER:-less}}
else
  whence -f "$1" | ${MANPAGER:-${PAGER:-less}}
fi
```

### 例

```shell-session
man man
```

結果:
![screen_004.png](https://qiita-image-store.s3.amazonaws.com/0/54869/a375c8b9-046d-f980-a84d-dc01d5643d73.png)


その他の場合
------------

通常の`man hoge`が実行されるようにします。

以上をまとめると
----------------

* [zshfiles/functions/man.zsh - ssh0/dotfiles](https://github.com/ssh0/dotfiles/tree/master/zshfiles/functions/man.zsh)

```zsh:man.zsh
# vim: set ft=zsh
#=#=#=
# man (available for buitlin commands)
#
# **Features:**
#
# * for zsh builtin commands
# * for reserved word
# * for alias
# * for zsh function
# * for (natural) command
# * if the same name exists, choose one by fzf
# * with color
#
# >* [Can I get individual man pages for the bash builtin commands? - Unix & Linux Stack Exchange](http://unix.stackexchange.com/questions/18087/can-i-get-individual-man-pages-for-the-bash-builtin-commands)
# >* [manpage - How to make `man` work for shell builtin commands and keywords? - Ask Ubuntu](http://askubuntu.com/questions/439410/how-to-make-man-work-for-shell-builtin-commands-and-keywords)
#
# But I want to know about zsh builtins, so I wrote this.
#
# Install zsh manuals to enable `man zshbuiltins`:
#
# ```
# $ mkdir -p ~/Downloads/zsh-doc; cd $_
# $ wget http://downloads.sourceforge.net/project/zsh/zsh/5.0.2/zsh-5.0.2.tar.bz2
# $ tar -xvf zsh-5.0.2.tar.bz2
# $ sudo cp zsh-5.0.2/Doc/*.1 /usr/local/share/man/man1
# ```
#
# **Require:**
#
# * fzf
# * "pygmentize" or "highlight" for highlighting scripts
# * LESS="R" option for ansi color in "less" command
#=#=

function man() {
  # Stock current LANG
  _LANG=${LANG}

  # set language (but if MANLANG is already set, use that)
  export LANG=${MANLANG:-${_LANG}}

  function restore_lang() {
    # restore LANG and clean up name space
    export LANG=${_LANG}
    unset _LANG
    unset -f $0
  }

  trap restore_lang 1 2 3 EXIT

  if [ ! -n "$1" ]; then
    echo "What manual page do you want?"
    return 1
  fi

  # get man type (using fzf but you can replace it with peco or percol or zaw)
  word="$(whence -wa -- $1 | uniq | fzf -1 | sed 's/: / /' | cut -d' ' -f2)"

  # if escaped, do nothing
  if [ ! -n "${word}" ]; then
    return 0
  fi

  # switch operation by word
  case ${word} in
    builtin) # built-in command
      local man_indent _space
      # TODO: get how many spaces before the commands
      man_indent=7
      _space="$(printf '%*s' "$man_indent" '')"
      /usr/bin/man --pager="less -p'^${_space}\\$1 '" zshbuiltins
      ;;
    reserved) # reserved words
      /usr/bin/man --pager="less -p'^COMPLEX COMMANDS$'" zshall
      ;;
    alias) # alias
      whence -c $1
      ;;
    function) # function
      if hash pygmentize 2>/dev/null; then
        whence -f "$1" \
          | pygmentize -l sh
      elif hash highlight 2>/dev/null; then
        whence -f "$1" \
          | highlight --out-format=ansi --src-lang=Bash
      else
        whence -f "$1"
      fi
      ;;
    *) # try using man
      /usr/bin/man "$@"
      ;;
  esac
}
```

manページを色付きで表示する
===========================

上にあげた画像を見てもらえれば分かるように、色付きでmanページが見られるといろいろと捗ります。

> 参考:
>
> * http://unix.stackexchange.com/questions/119/colors-in-man-pages

`~/.zshrc`で

```zsh
export PAGER=less
```

としておけば、manコマンド実行時にはlessコマンドを使って表示します。

lessコマンドのオプションは環境変数`LESS`に指定することができるので(See `man less`)、

```zsh
export LESS='-iMRj.5'
```

と指定しておきます。

| オプション | 意味                       |
|---         |---                         |
| `-i`       | ignore-case                |
| `-M`       | long-prompt                |
| `-R`       | ANSIカラー表示             |
| `-j.5`     | ターゲット行を画面の中心に |

また参考リンクのように`~/.zshrc`に以下のように書いておけば、太字や下線、ハイライト時の色を指定することができます。

```zsh
export LESS_TERMCAP_mb=$(tput bold)                # begin blinking
export LESS_TERMCAP_md=$(tput bold; tput setaf 4)  # begin bold (blue)
export LESS_TERMCAP_me=$(tput sgr0)                # end mode
export LESS_TERMCAP_se=$(tput sgr0)                # end standout-mode
export LESS_TERMCAP_so=$(tput bold; tput setaf 3)  # begin standout-mode (yellow)
export LESS_TERMCAP_ue=$(tput rmul; tput sgr0)     # end underline
export LESS_TERMCAP_us=$(tput smul; tput setaf 2)  # begin underline (green)
```

僕はtputを使って色などを指定していますが、エスケープ文字を使って直接指定することもできるので、細かく調節したい人はこちらを使うといいと思います。

まとめると:

* [zshfiles/functions/less.zsh - ssh0/dotfiles](https://github.com/ssh0/dotfiles/tree/master/zshfiles/functions/less.zsh)

終わりに
========

manコマンドを活用するために、ビルトインコマンド等も同じ`man`で参照できるようにしました。また、色付きで表示されると見やすくなると思いますので、色付き表示の仕方もご紹介しました。
何かしら役に立てば幸いです。