ChatGPTの力を借りてエディタ用の小さいプラグイン的なスクリプトを書く

## はじめに

エディタを使ってプログラミングしていると、ちょっとした困りごとを解決したいと思うことがよくあります。

ただこのような手順を踏めばエディタ上で解決できそうかなと思っても、その実装をしようとすると重い腰があがらない、というケースもよくあります。

そんな状況が長く続いてたのですが、最近はChatGPTなどAIツールのおかげで、エディタ用の小さなプラグイン的なスクリプトを書くハードルが下がった感じがあります。

この記事では簡単な例をまじえつつ、AIの力を借りてどんな感じに小さいプラグイン的なスクリプトを書くか紹介します。

## 前提：利用しているエディタ、出力されるスクリプト

自分はneovimをつかっていますが、恥ずかしながら現状まだエディタの設定はvimscriptで運用しているため、例に出てくるスクリプトはvimscriptになります。

AIに「VSCode用」などとエディタを指定すればそのエディタ用に出力してくれる気がするので、出力されたコードは参考程度にご覧ください。

## スクリプトのお題

以下になります。

> - エディタ上で開いたrspecのspecファイルを、`describe/context/it` が含まれる行のみgrepで抽出してクリップボードにコピーする
> - 上記の処理をエディタ上のショートカットキーで実行できるようにする

後々社内の方に共有いただいた下記の記事のようにrspecのdry-runオプションを使って一覧出力する方法や、他のエディタのプラグインなどで似たようなことができるかもしれませんが、当時困ってたこちらを例としてお題にしました。(当時そこまで調べていなかったという事情もあります)

https://qiita.com/jnchito/items/855d9cffc1f3ca048af2

直近携わっているお仕事で、大規模なrspecファイルにテストを追加していく作業がありました。

作って間もないrspecならそこまで苦労しないのですが、修正対象のrspecファイルは1ファイル数千行くらいあったので、全体のspecの構造を掴むのが難しいという課題がありました。

そこで簡易的にspecの全体構造を把握したいなということで、上記のようなことをしたいなと思いました。

ただ手数は最小限にしたいので、エディタ上で抽出→クリップボードコピーまでをショートカットキーで完結させたいなという気持ちがありました。

## 作成の流れ

ChatGPTなどの使い方は皆さん馴染みがあるかもしれないので説明を割愛します。

基本的にはそこまで難しいことはやっておらず、以下のサイクルを繰り返して精緻化していくイメージです。

- プロンプト作成
- プロンプト実行
- エディタ上でテスト
- 修正内容検討

## 作成したプロンプトと出力されたスクリプト

プロンプトにはやりたいことのプロセスをなるべく具体的に、箇条書きにして書くようにしています。

以下は最終的にChatGPTで利用したプロンプトと出力されたスクリプトです。スクリプトに余計な処理や誤動作する部分があればもちろん修正していきます。

```md
以下を満たすようなvimscriptを実装して

## 概要

neovimのvimscriptで、rspecファイルのdescribe/context/itの行を抽出してクリップボードにコピーしたい

## 処理概要

- エディタ上で開いたrspecのspecファイルを対象とする
- rspecファイル内の、`describe/context/it` が含まれる行をOSコマンドのgrepで抽出
- 抽出結果をクリップボードにコピーする。コマンドはpbcopyコマンドを利用する
- 上記を関数化し、ショートカットキー（バックスラッシュ + g）で実行できるようにする
```

```vimscript
" RSpecのdescribe/context/itを抽出してクリップボードにコピーする関数
function! CopyRspecLinesToClipboard()
  " 現在のバッファのファイルがRSpecファイルかどうか確認
  if !expand('%:e') =~ 'rb$'
    echo "This is not a Ruby file."
    return
  endif

  " describe, context, itの行をgrepで抽出
  let l:cmd = 'grep -E "^\s*(describe|context|it)\s" ' . shellescape(expand('%'))

  " コマンドを実行して、その結果をpbcopyに渡す
  let l:output = system(l:cmd)

  " 出力が空でない場合はクリップボードにコピー
  if v:shell_error == 0 && !empty(l:output)
    call system('echo ' . shellescape(l:output) . ' | pbcopy')
    echo "RSpec lines copied to clipboard."
  else
    echo "No matching lines found."
  endif
endfunction

" バックスラッシュ + g で関数を呼び出せるように設定
nnoremap <silent> \g :call CopyRspecLinesToClipboard()<CR>
```

出力されたコードから、エディタ上からOSコマンドの実行ができることや、ショートカット指定のイメージもざっくりわかって勉強になったりします。

結果のサンプルはこんな感じになります。
例としてActiveJobのバックエンドの一種であるgood_jobのあるspecファイルで実行した結果です。

https://github.com/bensheldon/good_job/blob/main/spec/integration/batch_spec.rb

```ruby
  describe 'simple batching' do
    it 'assigns a batch_id to all jobs in the batch' do
    context 'when all jobs complete successfully' do
      it 'has success status' do
    context 'when a job is discarded' do
      it "has a failure status" do
    context 'when there is a callback' do
      it 'calls the callback with a batch' do
  describe 'complex batching' do
    it 'can be used as instance object' do
  context 'when running inline' do
    it 'does not unintentionally add sub-enqueued job to the batch' do
  describe 'complex recursive batching' do
    it 'can enqueue multiple jobs' do
  describe 'all callbacks are called and retryable' do
    it 'calls discard callbacks' do
    it 'calls success callbacks' do
  describe 'aggressive async batching' do
    it 'can execute multiple jobs' do
  describe 'job that errors once then succeeds' do
    it 'calls the success callbacks' do
  describe 'adding to an existing batch' do
    it 'adds jobs to the existing batch' do
  describe 'retrying a discarded batch' do
    it 'retries all discarded jobs in the batch' do
  describe 'batch deletion' do
    it 'deletes batches only after their callback jobs have completed' do
```

## 所感

AIの力を借りつつ、プラグイン的なスクリプトを書くハードルを下げることができるのは個人的には良いなと感じています。

もちろんゼロから自分でプラグイン実装ができたほうが良いとは思っていますが、主要なお仕事に時間を割かれがちでプラグイン実装にそこまで時間をかけられず何も改善できない、では悲しい感じがします。

その代替手段としてAIの力を借りつつ少しずつ開発体験を改善させる方法は始めやすいですし、継続もしやすい気がしています。

またプロンプトを書くにあたり、結局自然言語で処理の流れを書くという手間はあります。ただ頭の整理になりますし、書いたプロンプトがある種設計メモというかドキュメントにもなるため、後々似たような処理をしたいときにも役立つ感じがしています。

## おわりに

今回自分の使用例を紹介しましたが、猛者の方々はもっと効率的でアウトプットの質の良いプロンプトを研究されてるのではと思われます…。あくまで一例として見てくださるとありがたいです。

これからもちょこちょこ改善できると良いなと思っています。