Perforce Command プラグインを作ってみた話 - p4cmd.vim

# はじめに

作ったのは結構前だけど、アドカレネタとして紹介しておくとともに、これからやりたいこともまとめようと思ったので！

# 当プラグインはこれ

https://github.com/takavfx/p4cmd.vim

Perforce コマンドの Add や Edit、Revert、Submit などを Vim/NeoVim 上のコマンドから実行できるようにしたもの。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51857/f2bcb23b-600f-512d-151a-b434f92daf8a.png)

# 中のしくみ

[denops](https://github.com/vim-denops/denops.vim) を裏に敷いており、TypeScript で書いている。
denops で書いていると、Vim と NeoVim 両方に対応できるというのも良い。

以前、dailynote.vim というプラグインを作ったものを上げたことがあったが、そこから少し変更があったので、そこをメモ程度に残しておく。

## コア部分の実装

実際のところは、P4CLI のコマンドを TypeScript 上から、`denops.cmd` で叩いてるだけ。
でもこれができるってことは無限では？:relaxed:

```typescript:denops/p4cmd/perforce.ts
import { Denops } from "./deps.ts";

export async function info(denops: Denops): Promise<void> {
  let response = await denops.cmd("!p4 info");
}

export async function add(denops: Denops): Promise<void> {
  let currentFile = await denops.call("expand", "%:p");
  let response = await denops.cmd(`!p4 add ${currentFile}`);
}

export async function edit(denops: Denops): Promise<void> {
  let currentFile = await denops.call("expand", "%:p");
  let response = await denops.cmd(`!p4 edit ${currentFile}`);
}

export async function revert(denops: Denops): Promise<void> {
  let currentFile = await denops.call("expand", "%:p");
  let response = await denops.cmd(`!p4 revert ${currentFile}`);
}
```

## コマンド登録部分

さらにコマンドを Vim/NeoVim 側に認識できるように登録するところは次のように宣言している。
あえて内部で二重にラップしているが、一応追加実装があった際に、差し込めるようにしているため。
現時点ではそこまで必要性を感じてないので、消してもいいかもしれない。

```typescript:denops/p4cmd/main.ts
import { batch, Denops } from "./deps.ts";
import { add, edit, info, revert } from "./perforce.ts";

export function main(denops: Denops): void {
  denops.dispatcher = {
    async init() {
      await batch.batch(denops, async (denops) => {
        // P4Info
        await denops.cmd(
          `command! -nargs=0 P4Info call denops#request('${denops.name}', 'info', [])`,
        );

        // P4Add
        await denops.cmd(
          `command! -nargs=0 P4Add call denops#request('${denops.name}', 'add', [])`,
        );

        // P4Edit
        await denops.cmd(
          `command! -nargs=0 P4Edit call denops#request('${denops.name}', 'edit', [])`,
        );

        // P4Revert
        await denops.cmd(
          `command! -nargs=0 P4Revert call denops#request('${denops.name}', 'revert', [])`,
        );
      });
    },

    async info(args) {
      info(denops);
    },

    async add(args) {
      add(denops);
    },

    async edit(args) {
      edit(denops);
    },

    async revert(args) {
      revert(denops);
    },
  };
}
```

プラグインつくるって実はこれだけでもできちゃう。

# 昔の構成からちょっとだけ違うやつ

Perforce コマンド類などが複数回コールするのを防ぐためのもの。
とりあえずドキュメントに倣って追加しているが…はて :thinking: 

```vimscript:plugins/p4cmd.vim
if exists('g:loaded_p4cmd')
  finish
endif
let g:loaded_p4cmd = 1

augroup denops=p4cmd
  autocmd!
  autocmd User DenopsPluginPost:p4cmd call denops#notify('p4cmd', 'init', [])
augroup END
```


# 今後の展望

今後は、LazyGit のようなクライアントの Perforce 版が欲しいなと思っているので、そういたものを作りたいと考えている。
[ddu](https://github.com/Shougo/ddu.vim) 系のプラグインとして作れないかな？

まぁ、この denops では厳しいものとしては、[lualine]() のプラグインとして、Perforce のステータスを見られるようにしたいというのがある。

来年になったらいずれも作ってしまおうという展望。
（どちらかは今年までの宿題にしたいけども笑

# さいごに

denops は、TypeScript と Vim の操作としくみををある程度知ってれば、ほんとサクッとやりたいことを実装できてとてもいい。
実は仕事上で表に出せないところでも自分の毎日の自動化のためにプラグインを書いていたりする。
この調子で来年もいろいろ自動化していきたい :v: