GitHub Copilot LabsのBRUSHES機能がすごかった

# はじめに

VSCodeには、GitHub Copilot Labsという拡張機能があります。

https://githubnext.com/projects/copilot-labs

その中に、`BRUSHES`という機能があります。
`BRUSHES`を使えば、コードを読みやすくしたり各行ごとにコメントを追加してくれたりします。

![名称未設定のデザイン (77).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577119/880d9667-d845-4743-0d73-7f772b723ec0.png)

実は、標準で提供してくれている機能で満足してない場合、`CUSTOM`という機能を使うことで、ChatGPTにお願いするようなことをGitHub Copilotに対して指示することができます。

# リファクタリング

例えば、次のようなメソッドを書いていたとします。

```ts
...
  deserialize(json: string): void {
    // should return when json is empty
    if (!json) { return; }
    this.elements.clear();
    const tree = JSON.parse(json) as TreeElement[];

    tree.forEach((element) => {
      this.elements.set(element.data.id, element);

      if (!element.parentId) {
        this.rootId = element.data.id;
      }
    });
  }
...
```

そして、この`deserialize()`に対して次のようなテストを書いていたとします。
このテストでは、JSONとして正しくない値を渡した場合、`set()`が呼ばれないことを確認するためのテストです。

```ts
// Write a test case for when value is not empty string and is not valid JSON
test("when value is not empty string and is not valid JSON", () => {
  // register vscode.ExtensionContext as a dependency using tsyringe
  container.register("ExtensionContext", {
    useValue: {
      globalState: {
        get: () => {
          return "not valid JSON";
        }
      }
    }
  });
  // create a fake ChatGPTEditorStorageImpl instance
  const storage = new ChatGPTEditorStorageImpl();
  // stub this.elements.set() method
  const setStub = sinon.stub(storage.elements, "set");
  // call deserialize method
  storage.deserialize("not valid JSON");
  // assert this.elements.set() method is called with empty array
  assert.ok(setStub.notCalled);
});
```

このテストを実行すると、もちろんエラーが出てしまいます。

```
deserialize
    when value is not empty string and is not valid JSON:
SyntaxError: Unexpected token o in JSON at position 1
```

これは、`JSON.parse()`にJSONでない値を渡してしまっているため、エラーが発生しているのです。

GitHub Copilotに`deserialize()`をリファクタリングしてもらい、エラーが出ないようにしてみましょう。

まず、リファクタリングしたいコードのブロックを選択します。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577119/38eaf31b-1002-62d0-1d45-e4b33bd06941.png)

GitHub Copilot Labsの`Brushes`から`CUSTOM`を選択します。

テキストフィールドに、`refactor not to throw error when json is not valid`と入力します。

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577119/144644cd-37e4-3c17-47e9-afec4821012c.png)

じゃじゃーん！！！これでコードのリファクタリングが完了しました！
めちゃくちゃ簡単ですよね。

```ts
  deserialize(json: string): void {
    // should return when json is empty
    if (!json) { return; }
    this.elements.clear();
    let tree: TreeElement[];
    try {
      tree = JSON.parse(json) as TreeElement[];
    } catch (error) {
      return;
    }

    tree.forEach((element) => {
      this.elements.set(element.data.id, element);

      if (!element.parentId) {
        this.rootId = element.data.id;
      }
    });
  }
```

もちろんこの程度のリファクタリングなら、自分で書いた方が早いかもしれませんが、この程度のリファクタリングさえもAIに任しちゃった方が設計などの本質的な部分に集中できる気がしています。

それでは先ほどのテストをもう一度実行してみましょう。

今度はちゃんと成功しました。

```
✔ when value is not empty string and is not valid JSON
```

# ChatGPTを使ってリファクタリング

今度は、ChatGPTを使ってリファクタリングをしてみたいと思います。

https://marketplace.visualstudio.com/items?itemName=takagimeow.chatgpt-editor

先ほど、`CUSTOM`に入力したメッセージをコメントとして記述し、ChatGPTに渡してみたいと思います。

```ts
// refactor not to throw error when json is not valid
deserialize(json: string): void {
    // should return when json is empty
    if (!json) { return; }
    this.elements.clear();
    const tree = JSON.parse(json) as TreeElement[];
    
    tree.forEach((element) => {
      this.elements.set(element.data.id, element);
    
      if (!element.parentId) {
        this.rootId = element.data.id;
      }
    });
}
```

実行した結果、テストもちゃんと通るコードにリファクタリングされました！

ChatGPTは`let`を使わず、ブロック自体を`try-catch`で囲むという選択をしました。

```ts
// refactor to handle invalid JSON gracefully
deserialize(json: string): void {
    try {
      const tree = JSON.parse(json) as TreeElement[];
      this.elements.clear();

      tree.forEach((element) => {
        this.elements.set(element.data.id, element);

        if (!element.parentId) {
          this.rootId = element.data.id;
        }
      });
    } catch (error) {
      console.error(`Error deserializing JSON: ${error}`);
    }
}
```

ChatGPTに渡されたコンテキストは、GitHub Copilotに渡したものよりも少ないにもかかわらず、推論を行い正しい結果を出してくれていることにかなり驚愕しました。

# まとめ

GitHub Copilot Labsは初見だとどの機能がどのように使えるのかよくわからなかったりしますが、どれもすごい機能ばかりなので是非トライアンドエラーの精神で使ってみることをおすすめします。