neovimでGitHub Copilotを触ってみた その2

## はじめに
前回の続き、Chatプラグインを追加してみる。

https://qiita.com/migihizao/items/8ec9078b13210362694b

## 環境設定
### lazy.nvimの導入
参考記事の多くがlazy.nvimベースで書かれているため、プラグインマネージャーをlazy.nvimに変更する。
1. 既存設定の退避
    ```zsh
    cp -r ~/.config/nvim ~/.config/nvim_bk
    rm ~/.config/nvim/init.vim
    ```
1. lazy.nvimのインストール
    ```zsh
    git clone https://github.com/folke/lazy.nvim.git ~/.local/share/nvim/lazy/lazy.nvim
    ```
1. init.luaの作成
    ```zsh
    nvim ~/.config/nvim/init.lua
    ```
    ```~/.config/nvim/init.lua
    -- lazy.nvimのパスを設定
    local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
    if not vim.loop.fs_stat(lazypath) then
        vim.fn.system({
            "git",
            "clone",
            "--filter=blob:none",
            "https://github.com/folke/lazy.nvim.git",
            "--branch=stable", -- 最新の安定バージョンを使用
            lazypath,
        })
    end
    vim.opt.rtp:prepend(lazypath)

    -- lazy.nvimの設定をロード
    require('settings')
    require('lazy').setup({
      spec = {
        { import = 'plugins' },
      },
      install = { colorscheme = {'habamax'} },
      checker = { enabled = true },
    })
    ```
1. init.vimの設定情報を`~/.config/nvim/lua/settings.lua`に移設
1. init.vimのプラグイン情報を`~/.config/nvim/lua/plugins/*.lua`に移設
1. プラグインをインストール
    Neovimを起動し以下のコマンドを実行
    ```zsh
    :Lazy sync
    ```
### Copilot Chatの導入
1. CopilotChat.nvimプラグイン定義ファイルを作成
    ```zsh
    vi ~/.config/nvim/lua/plugins/copilot-chat.lua
    ```
    ```~/.config/nvim/lua/plugins/copilot-chat.lua
 	return {
	  {
	    "CopilotC-Nvim/CopilotChat.nvim",
	    branch = "canary",
	    dependencies = {
	      { "zbirenbaum/copilot.lua" }, -- or github/copilot.vim
	      { "nvim-lua/plenary.nvim" }, -- for curl, log wrapper
	    },
	    build = "make tiktoken", -- Only on MacOS or Linux
	    opts = {
	      debug = true, -- Enable debugging
	      -- See Configuration section for rest
	    },
	    -- See Commands section for default commands if you want to lazy load on them
	  },
	}   
    ```
    <br/>
1. 日本語で返答をもらうための設定を追加
    [こちらの記事](https://qiita.com/haw_ohnuma/items/1ec8ef5091b440cbb8bd)を参考に定義ファイルを作成
    ```zsh
    vi ~/.config/nvim/lua/settings-copilot-chat.lua
    ```
    ```~/.config/nvim/lua/settings-copilot-chat.lua
	require("CopilotChat").setup({
	    show_help = "yes",
	    prompts = {
	        Explain = {
	            prompt = "/COPILOT_EXPLAIN コードを日本語で説明してください",
	            mapping = '<leader>ce',
	            description = "コードの説明をお願いする",
	        },
	        Review = {
	            prompt = '/COPILOT_REVIEW コードを日本語でレビューしてください。',
	            mapping = '<leader>cr',
	            description = "コードのレビューをお願いする",
	        },
	        Fix = {
	            prompt = "/COPILOT_FIX このコードには問題があります。バグを修正したコードを表示してください。説明は日本語でお願いします。",
	            mapping = '<leader>cf',
	            description = "コードの修正をお願いする",
	        },
	        Optimize = {
	            prompt = "/COPILOT_REFACTOR 選択したコードを最適化し、パフォーマンスと可読性を向上させてください。説明は日本語でお願いします。",
	            mapping = '<leader>co',
	            description = "コードの最適化をお願いする",
	        },
	        Docs = {
	            prompt = "/COPILOT_GENERATE 選択したコードに関するドキュメントコメントを日本語で生成してください。",
	            mapping = '<leader>cd',
	            description = "コードのドキュメント作成をお願いする",
	        },
	        Tests = {
	            prompt = "/COPILOT_TESTS 選択したコードの詳細なユニットテストを書いてください。説明は日本語でお願いします。",
	            mapping = '<leader>ct',
	            description = "テストコード作成をお願いする",
	        },
	        FixDiagnostic = {
	            prompt = 'コードの診断結果に従って問題を修正してください。修正内容の説明は日本語でお願いします。',
	            mapping = '<leader>cd',
	            description = "コードの修正をお願いする",
	            selection = require('CopilotChat.select').diagnostics,
	        },
	        Commit = {
	            prompt =
	            '実装差分に対するコミットメッセージを日本語で記述してください。',
	            mapping = '<leader>cco',
	            description = "コミットメッセージの作成をお願いする",
	            selection = require('CopilotChat.select').gitdiff,
	        },
	        CommitStaged = {
	            prompt =
	            'ステージ済みの変更に対するコミットメッセージを日本語で記述してください。',
	            mapping = '<leader>cs',
	            description = "ステージ済みのコミットメッセージの作成をお願いする",
	            selection = function(source)
	                return require('CopilotChat.select').gitdiff(source, true)
	            end,
	        },
	    },
	})
	```
    ```~/.config/nvim/init.luaに追加
    require('settings-copilot-chat')
    ```
    <br/>
1. セットアップ
    Neovimを起動し以下のコマンドを実行
    ```zsh
    :Lazy sync
    ```
## 使ってみた
1. サンプルプログラムを作成
    ```zsh
    vi fizzbazz.py
    ```
    ```fizzbazz.py
	def fizzbazz(n):
	    for i in range(1, n+1):
	        if i % 3 == 0 and i % 5 == 0:
	            print("FizzBazz")
	        elif i % 3 == 0:
	            print("Fizz")
	        elif i % 5 == 0:
	            print("Bazz")
	        else:
	            print(i)
    
	if __name__ == "__main__":
	    fizzbazz(100)
    ```
    <br/>
1. レビューしてもらう
    ```fizzbazz.py
    :CopilotChatReview
    ```
    ```copilot:結果
 	## User ───
	
	/COPILOT_REVIEW コードを日本語でレビューしてください。
	
	## Copilot ───
	
	line=1: 関数名 'fizzbazz' は 'fizzbuzz' のタイポの可能性があります。 'fizzbuzz' に修正することを検討してください。
	line=1: 引数 'n' は短すぎて意味が不明瞭です。 'limit' などのより説明的な名前に変更することを検討してください。
	line=3: コメントが不足しています。条件式の意味を説明するコメントを追加してください。
	line=12-13: マジックナンバー '100' を直接使用しています。定数として定義することを検討してください。
    ```
    ほほぅ。コードが簡易すぎもあるとは思うが、対人レビューと大差ないのでは。
    <br/>
1. テストコードを書いてもらう
    ```fizzbazz.py
    :CopilotChatTests
    ```
	```python:結果
	import unittest
	from fizzbazz import fizzbazz
	from io import StringIO
	import sys
	
	class TestFizzBazz(unittest.TestCase):
	    def test_fizzbazz(self):
	        # 標準出力をキャプチャするためにStringIOを使用
	        captured_output = StringIO()
	        sys.stdout = captured_output
	        
	        # fizzbazz関数を実行
	        fizzbazz(15)
	        
	        # 標準出力を元に戻す
	        sys.stdout = sys.__stdout__
	        
	        # 期待される出力結果
	        expected_output = (
	            "1\n"
	            "2\n"
	            "Fizz\n"
	            "4\n"
	            "Bazz\n"
	            "Fizz\n"
	            "7\n"
	            "8\n"
	            "Fizz\n"
	            "Bazz\n"
	            "11\n"
	            "Fizz\n"
	            "13\n"
	            "14\n"
	            "FizzBazz\n"
	        )
	        
	        # キャプチャした出力と期待される出力を比較
	        self.assertEqual(captured_output.getvalue(), expected_output)
	
	if __name__ == '__main__':
	    unittest.main()
	```

## 感想
Copilotとはよくいったもんだ。一人で壁打ちしながらコードが書けるのはかなり心強い。
実際のアプリケーション開発でどの程度の効果を発揮するのか、引き続き実証実験していこう。