batコマンド解剖

# はじめに

batコマンドという高級なcatコマンドのようなものを普段使っているのですが
オプションについて変更したい点がいくつかあったので、オプション部分の実装を見ると同時に変更を加えてみました。

# batとは
batとはcatコマンドにsyntax highlighting機能やlessコマンドに備わっているpaging機能を追加したコマンド
です。
https://github.com/sharkdp/bat

```bash
bat src/index.html
```
![alt text](https://camo.githubusercontent.com/43e40bf9c20d5ceda8fa67f1d95b5c66548b2f6f8dca8403e08129991cc32966/68747470733a2f2f692e696d6775722e636f6d2f326c53573452452e706e67)

# 変更した箇所

batコマンドはファイルが大きい場合、pagingというファイルの一部だけを表示し、jで下へ、kで上へスクロー ルするモードに入ります。
ですが私としては、ファイルが大きい場合でもpagingモードに入らずに全てを表示してほしいです。
batコマンドでは-Pオプションを付与することでそれを行えます。
ですが、デフォルトでpagingをオフにし、-Pオプションを指定した時にだけpagingモードに入るようにしたいです。
ということでソースコードを変更し、それを実現させていきます。

# コード解析および変更

オプションの定義は```src/bat/bin/clap_app.rs```にあります。
このファイルの319~346行にpagingオプションの定義があります。
```rust
        .arg(
            Arg::new("paging")
                .long("paging")
                .overrides_with("paging")
                .overrides_with("no-paging")
                .value_name("when")
                .value_parser(["auto", "never", "always"])
                .default_value("auto")
                .hide_default_value(true)
                .help("Specify when to use the pager, or use `-P` to disable (*auto*, never, always).")
                .long_help(
                    "Specify when to use the pager. To disable the pager, use \
                    --paging=never' or its alias,'-P'. To disable the pager permanently, \
                    set BAT_PAGING to 'never'. To control which pager is used, see the \
                    '--pager' option. Possible values: *auto*, never, always."
                ),
        )
        .arg(
            Arg::new("no-paging")
                .short('P')
                .long("no-paging")
                .alias("no-pager")
                .action(ArgAction::SetTrue)
                .overrides_with("no-paging")
                .hide(true)
                .hide_short_help(true)
                .help("Alias for '--paging=never'")
            )
```
これを見るとpagingにはauto、always、neverの3種類がある用です。
そしてデフォルトではautoになっています。
autoということはファイルの大きさでpagingモードに入るか入らないかを決めているのだと推測できます。    
default_valueをneverにすればオプションを指定しなければpagingモードに入ることができないようにできそうです。
次にno-pagingですが、これはこのオプションを指定した際に```--paging=never```としてくれるオプションの 用です。
これを少し改良し、```--paging=always```とすることで、オプションを指定した時にのみpagingモードに入れ るようにできます。
これには```src/bin/bat/app.rs```の方も変更する必要があるのですが、一旦オプション定義の方だけ以下のように変更します。
```rust
        .arg(
            Arg::new("paging")
                .long("paging")
                .overrides_with("paging")
                .overrides_with("allow-paging")
                .value_name("when")
                .value_parser(["auto", "never", "always"])
                .default_value("never")
                .hide_default_value(true)
                .help("Specify when to use the pager, or use `-P` to disable (*auto*, never, always).")
                .long_help(
                    "Specify when to use the pager. To disable the pager, use \
                    --paging=never' or its alias,'-P'. To disable the pager permanently, \
                    set BAT_PAGING to 'never'. To control which pager is used, see the \
                    '--pager' option. Possible values: *auto*, never, always."
                ),
        )
        .arg(
            Arg::new("allow-paging")
                .short('P')
                .long("allow-paging")
                .alias("allow-pager")
                .action(ArgAction::SetTrue)
                .overrides_with("allow-paging")
                .hide(true)
                .hide_short_help(true)
                .help("Alias for '--paging=never'")
            )
```
no-pagingに関してはallow-pagingに名前を変更しています。
ですが、名前を変更しただけでは--paging=neverと指定するという機能を変更できないので、次は先ほど言ったapp.rsに変更を加えます。
```rust
            Some("never") => {
                PagingMode::Never
            }
            Some("auto") | None => {
                if extra_plain || self.matches.get_flag("no-paging") {
                    PagingMode::Never
                } else if inputs.iter().any(Input::is_stdin) {
                 */
                if inputs.iter().any(Input::is_stdin) {
                    ...
```
98行目のconfig関数でオプションを基にpagingモードをonかoffどちらにするかを決めています。
--paging=autoだと--no-pagingオプションが指定されている場合はpagingモードがoffになります。
ここを以下のように変更します。
```rust
            Some("never") => {
                if self.matches.get_flag("allow-paging") {
                    PagingMode::Always
                } else {
                    PagingMode::Never
                }
            }
            Some("auto") | None => {
                // If we have -pp as an option when in auto mode, the pager should be disabled.     
                /*
                if extra_plain || self.matches.get_flag("no-paging") {
                    PagingMode::Never
                } else if inputs.iter().any(Input::is_stdin) {
                 */
                if inputs.iter().any(Input::is_stdin) {
                    ...
```
no-pagingオプションはもう無いので、そこの処理は消します。
その代わり--paging=neverとなっていた場合の処理に、--allow-pagingオプションが指定されていた場合はpagingをonにする処理を追加します。

# ビルド

上記の変更を行った上で以下のコマンドでビルドします。
README.mdが置かれているディレクトリで実行します。
ビルドされた実行ファイルは$HOME/.cargo/binに置かれます。
```bash
cargo install --path . --locked
```

# 検証

適当なファイルに対し以下のコマンドを実行し、変更が反映されているか確認してみてください。
1つ目のコマンドではファイルが大きいにも関わらずpagingモードがonになりません。
2つ目のコマンドでは-Pオプションを指定することにより手動でpagingモードに入れます。
```bash
bat src/bat/bin/app.rs
```
```bash
bat -P src/bat/bin/app.rs
```

# さいごに

今回改造したものは私のgithubレポジトリに公開しておきます。
気になった方は上記のコマンドでビルドして使ってみてください。
https://github.com/sota70/bat