ラベル emacs の投稿を表示しています。 すべての投稿を表示
ラベル emacs の投稿を表示しています。 すべての投稿を表示

2025年9月19日金曜日

emacs の copilot-chat.el で MCP サーバと連携する

emacs の copilot-chat.el で MCP サーバと連携する

概要

まだテスト段階ですが使えるようなので使ってみました
モデルは gpt4-1 にしています

emacs はバージョン30以上が必要です

環境

  • Ubuntu 24.04
  • emacs 30.1
    • copilot-chat.el 20250909.1505
    • mcp.el 20250829.1241

設定

copilo-chat 起動後に設定が読み込まれその後 mcp-hub-start-all-server を実行すると mcp サーバが起動するようにします

MCP にアクセスを許可するディレクトリ (/home/devops) はシンボリックリンクだとうまく動作しないので実フォルダを指定しましょう

(use-package
 mcp
 :ensure t
 :after copilot-chat
 :custom
 (mcp-hub-servers
  '(("filesystem" .
     (:command
      "npx"
      :args
      ("-y"
       "@modelcontextprotocol/server-filesystem"
       "/home/devops")))))
 :config (require 'mcp-hub)
 :hook (after-init . mcp-hub-start-all-server))

MCPサーバ起動

  • copilot-chat
  • mcp-hub-all-start-server
Started server filesystem (/1/1)

となれば起動完了です
ps aux | grep npx などでプロセスを確認しても OK です

copilot-chat.el に MCP サーバ登録

  • copilot-chat-set-mcp-servers

上記を実行するとまずは追加する MCP サーバを選択するミニバッファが起動します
今回は1つしかないですが複数ある場合はその分を数字を教えて有効にします
All でも OK です

emacs 上にまだ copilot-chat インスタンスが起動していない場合は新規で作成するか既存のインスタンスを選択するミニバッファが表示されます

自分が試してみた感じ新規でインスタンスを作成しないと MCP サーバを使ってくれなかったので新規で作成しましょう

動作確認

copilot-chat のバッファで指示してみましょう   ちゃんと結果が返ってくることを確認します

MCP 初回実行時はツールを実行する確認があるので yes で許可します

最後に

emacs + copilot-chat.el + mcp を試してみました
ちゃんと Copilot Chat バッファで命令した際に MCP を使うことが確認できました
公式だとモデルは Claude のほうが良いようなのですが gpt4-1 でもちゃんと動作しました

今回は copilot-chat 起動 -> MCP サーバ起動 -> MCP サーバを copilot-chat を連携という感じの3段階で設定したので面倒な感じはしますが .emacs に自動起動する設定を書けばすべて一発で起動することもできます

MCP と関係はないですがまだ vscode の Github Copilot Chat の拡張にあるエージェントモードには対応していないようです (参考サイトの issues 参照)

参考サイト

2025年4月15日火曜日

emacs の copilot-chat で選択した部分をコンテキストに追加して質問する方法

emacs の copilot-chat で選択した部分をコンテキストに追加して質問する方法

概要

前回選択中のバッファをコンテキストに追加する方法を紹介しました
今回は選択した部分をコンテキストとして追加し質問する方法を紹介します

環境

  • macOS 15.4
  • emacs 30.1

copilot-chat-custom-prompt-selection

  • 質問したい部分を選択
  • M-x copilot-chat-custom-prompt-selection
  • ミニバッファで質問する

すると Copilot chat 用のバッファで質問が返ってくるので確認します
ちゃんと選択した部分が質問のコンテキストに追加されていることを確認しましょう

* *[16:28:35]* You                 :you:
Create another Message instance and call the say method.

#+BEGIN_SRC python
from my_libs.util import Message


def main():
    # Create an instance of the Message class
    msg = Message()
    
    # Call the say method with keyword arguments
    try:
        msg.say(text="Hello, World!", sender="user@example.com", recipient="recipient@example.com")
    except ValueError:
        print("An error occurred while executing the say method.")

if __name__ == "__main__":
    main()

#+END_SRC


** *[16:28:39]* Copilot(gpt-4o)                 :copilot:
*** Create another Message instance and call the ~say~ method
To create another instance of the ~Message~ class and call the ~say~ method, you can follow the example below:

#+BEGIN_SRC python
from my_libs.util import Message


def main():
    # Create the first instance of the Message class
    msg1 = Message()
    
    # Call the say method for the first instance
    try:
        msg1.say(text="Hello, World!", sender="user@example.com", recipient="recipient@example.com")
    except ValueError:
        print("An error occurred while executing the say method for msg1.")
    
    # Create another instance of the Message class
    msg2 = Message()
    
    # Call the say method for the second instance
    try:
        msg2.say(text="Goodbye, World!", sender="admin@example.com", recipient="user@example.com")
    except ValueError:
        print("An error occurred while executing the say method for msg2.")

if __name__ == "__main__":
    main()
#+END_SRC

*** Explanation
+ The code creates two separate instances of the ~Message~ class: ~msg1~ and ~msg2~.
+ The ~say~ method is called on each instance with different arguments.
+ Error handling is included using ~try~ and ~except~ blocks to catch any ~ValueError~ exceptions that may occur during the execution of the ~say~ method.


* ╭──── Chat Input ────╮ :copilotChatInput:

最後に

コンテキストをうまく追加して自分のコードを LLM 側に認識させましょう

2025年4月13日日曜日

emacs の copilot-chat で copilot-chat-add-current-buffer を使うと現在のバッファを質問のコンテクストとして加えることができる

emacs の copilot-chat で copilot-chat-add-current-buffer を使うと現在のバッファを質問のコンテクストとして加えることができる

概要

タイトルの通りです
手元のコードを学習させたりコンテキストとして持たせたりするベストプラクティスは何がいいのでしょうか

環境

  • emacs 30.1
  • copilot-chat 2.0.1

チャット履歴

まずはコンテキストを与えないで質問します

* *[11:06:32]* You                 :you:
Please provide a sample python code that uses the Message class.

** *[11:06:34]* Copilot(gpt-4o)                 :copilot:
*** Sample Python Code Using the Message Class

Here is a sample Python code that demonstrates the use of the `Message` class from the `email.message` module. This example creates an email message, sets its headers, and adds content.

#+BEGIN_SRC python
from email.message import EmailMessage

def create_email():
    # Create an instance of the EmailMessage class
    msg = EmailMessage()
    
    # Set the email headers
    msg['From'] = 'sender@example.com'
    msg['To'] = 'recipient@example.com'
    msg['Subject'] = 'Sample Email Subject'
    
    # Add the email content
    msg.set_content('This is the plain text content of the email.')
    
    # Add an HTML version of the content
    msg.add_alternative("""
    <html>
        <body>
            <h1>This is the HTML content of the email.</h1>
            <p>It includes <b>bold</b> and <i>italic</i> text.</p>
        </body>
    </html>
    """, subtype='html')
    
    return msg

# Example usage
if __name__ == "__main__":
    email_message = create_email()
    print(email_message)
#+END_SRC

*** Key Points
+ The `EmailMessage` class is part of Python's built-in `email` module.
+ The `set_content` method is used to add plain text content.
+ The `add_alternative` method is used to add an HTML version of the email content.
+ Headers like ~From~, ~To~, and ~Subject~ are set using dictionary-like syntax.

次に以下のファイルを開いて copilot-chat-add-current-buffer で開いたファイルをコンテキストに追加します

  • vim my_lib/utils.py
import time


class Message:
    def say(self, **kwargs):
        print("Start say")
        print(kwargs)
        time.sleep(10)
        raise ValueError()
        # print("End say")

そして更に質問します
するとコンテキストとして追加したクラスをちゃんと使って修正してくれます

* *[11:07:47]* You                 :you:
Use the Message class in my_libs/utils.py

** *[11:07:48]* Copilot(gpt-4o)                 :copilot:
*** Sample Code Using the ~Message~ Class in ~my_libs/utils.py~

Below is an example of how to use the ~Message~ class from your provided code in ~my_libs/utils.py~.

#+BEGIN_SRC python
from my_libs.utils import Message

def main():
    # Create an instance of the Message class
    msg = Message()
    
    # Call the say method with keyword arguments
    try:
        msg.say(text="Hello, World!", sender="user@example.com", recipient="recipient@example.com")
    except ValueError:
        print("An error occurred while executing the say method.")

if __name__ == "__main__":
    main()
#+END_SRC

*** Explanation
+ The ~Message~ class is imported from ~my_libs/utils.py~.
+ An instance of the ~Message~ class is created using ~msg = Message()~.
+ The ~say~ method is called with keyword arguments like ~text~, ~sender~, and ~recipient~.
+ A ~try-except~ block is used to handle the ~ValueError~ raised by the ~say~ method.

更に Message クラスに機能を追加する依頼をしてみます
ちゃんと元の Message クラスの構成をキープしつつ追加してくれました

* *[11:11:04]* You                 :you:
Add a field to the Message class to manage the message body.


** *[11:11:06]* Copilot(gpt-4o)                 :copilot:
*** Updated ~Message~ Class with a Field for the Message Body

Below is the updated ~Message~ class with a new field to manage the message body.

#+BEGIN_SRC python
import time

class Message:
    def __init__(self, body=""):
        """
        Initialize the Message class with an optional body field.
        """
        self.body = body

    def say(self, **kwargs):
        """
        Print the message body and additional keyword arguments.
        """
        print("Start say")
        print("Message Body:", self.body)
        print("Additional Arguments:", kwargs)
        time.sleep(10)
        raise ValueError()
        # print("End say")
#+END_SRC

*** Explanation of Changes
+ A new field ~body~ is added to the ~Message~ class, initialized in the constructor (~__init__~).
+ The ~say~ method now prints the value of the ~body~ field along with the additional keyword arguments.

*** Example Usage
Here is an example of how to use the updated ~Message~ class:

#+BEGIN_SRC python
from my_libs.utils import Message

def main():
    # Create an instance of the Message class with a custom body
    msg = Message(body="This is the message body.")
    
    # Call the say method with keyword arguments
    try:
        msg.say(sender="user@example.com", recipient="recipient@example.com")
    except ValueError:
        print("An error occurred while executing the say method.")

if __name__ == "__main__":
    main()
#+END_SRC

*** Key Points
+ The ~body~ field allows you to store and manage the message content directly within the ~Message~ class.
+ The ~say~ method now includes the ~body~ field in its output for better context.


* ╭──── Chat Input ────╮ :copilotChatInput:

最後に

emacs の copilot-chat でコンテキストを追加する方法を紹介しました
こんな感じで公開されていないコードや手元にあるライブラリ情報をコンテキストとして追加すればそれを考慮したコードを書いてくれるようです

以下懸念点です

  • 複数のバッファをコンテキストとして追加するにはどうすればいいのか
  • 一度コンテキストとして追加したバッファを復元するにはどうすればいいのか
    • chat を開くたびに追加しなければならない

参考サイト

2025年3月28日金曜日

emacs で gptel を使ってローカルで起動した ollama と接続してみる

emacs で gptel を使ってローカルで起動した ollama と接続してみる

概要

前回 Openhermes をローカルに構築しました
今回は emacs の gptel を使って Openhermes と通信してみます
なお今回の記事はすでにローカルで Openhermes で起動している前提で話を進めます

環境

  • macOS 15.3.2
  • ollama 0.6.2
  • Openhermes v2.5
  • emacs 30.1
  • gptel 0.9.8

gptel インストール

  • M-x package-list-packages
  • gptel -> i -> x

gptel 設定ファイル作成

  • vim .emacs.d/site-lisp/llm/init.el
;; OPTIONAL configuration
(setq
 gptel-model 'openhermes:latest
 gptel-backend
 (gptel-make-ollama
  "Ollama"
  :host "localhost:11434"
  :stream t
  :models '(openhermes:latest)))

動作確認

gptel

M-x gptel を実行すると Openhermes とやりとりするバッファを選択するので *Ollama* バッファを選択します

すると ### というプロンプトになるのでここに質問を入力します
質問を入力したら C-c RET で質問を送信できます

gptel-send

gptel-send は現在のカーソル位置より上部のテキストを送信します
また返答も現在のバッファにそのまま書き込まれます

gptel-rewrite

gptel-rewrite はリファクタなどに使えます
現在選択している部分を上書きしてくれる機能です
リファクタする際に一行ずつやリファクタする方法などを追加で送信することもできます

最後に

他にもいろいろ機能はありそうです
Tools モードを使うと特定のパスに設定ファイルを作成してくれるタスクをインタラクティブに生成することもできるようです

copilot-mode と混同しているとキーバインドが同じケースがあるので別の割当てをする必要がありそうです

copilot-mode はデフォルトで auto-complete のように自動で補完してくれます
gptel はデフォルトではそうでなく修正したい箇所や新規でコードを追加してい場合で質問して LLM 側に書いてもらう感じになります

おそらくどちらも同じ機能があるかなと思うので使い分けというよりかはどちらか一方を使い倒すほうがいいかなと思います

copilot は無料だと制限があるので無制限にかつ無料で使いたい場合には必然的に gptel になるかなと思います
ただ gptel は当然ですがローカルに LLM を動かす環境が必要になるのでそれなりのマシンスペックが要求される点に注意です

参考サイト

2025年3月24日月曜日

treemacs の workspace の設定ファイルがあるパス

treemacs の workspace の設定ファイルがあるパス

概要

複数のワークスペースを手動で追加したい場合には直接設定ファイルを編集しちゃうのが簡単です

環境

  • macOS 15.3.2
  • emacs 30.1

パス

~/.emacs.d/.cache/treemacs-persist

直接編集する

先に emacs を停止しましょう

treemacs-persist とバックアップファイルを直接編集します
例えば以下のように特定のディレクトリ配下のプロジェクトを一括で追加できます

echo "* Default" > ~/.emacs.d/.cache/treemacs-persist && for i in `ls | grep -v update_libs.sh`; do echo "** ${i}\n - path :: ~/data/repo/${i}"; done >> ~/.emacs.d/.cache/treemacs-persist
echo "* Default" > ~/.emacs.d/.cache/treemacs-persist\~ && for i in `ls | grep -v update_libs.sh`; do echo "** ${i}\n - path :: ~/data/repo/${i}"; done >> ~/.emacs.d/.cache/treemacs-persist\~

動作確認

これで再度 emacs を開くと treemacs にプロジェクトが追加されています

トラブルシューティング

なぜか .emacs.d/.cache/treemacs-persist が改行されるというなぞの現象になるので修正します
この現象は before-save-hook などで自動フォーマットが設定されている場合に自動的に treemacs-persist もフォーマットしてしまい発生してる可能性が高いです

  • cat ~/.emacs.d/.cache/treemacs-persist-at-last-error
# State when last error occurred on 2025-03-20 10:36:48
# Error was 'First item must be a workspace name' in line '*'

*
Default
**
python-try
-
path
::
~/data/repo/python-try
  • vim ~/.emacs.d/.cache/treemacs-persist
* Default
** python-try
- path :: ~/data/repo/python-try

最後に

面倒な場合は直接キャッシュファイルを編集しちゃいましょう
フォーマットがあるのでそこだけ気をつけてください

2025年3月22日土曜日

Github Actions で elisp の lint をする方法

Github Actions で elisp の lint をする方法

概要

elisp-autofmt を使ってフォーマットした結果 git diff で差分があった場合はエラーにすることで lint チェックします

環境

  • emacs 30.1

format_elisp_files.sh

フォーマットするためのシェルスクリプトです
内部で emacs を使って elisp-autofmt を実行しフォーマットします
ls-files で取得するファイル名は適宜変更してください

for file_name in `git ls-files files | egrep -e 'el|emacs'`
do
emacs --batch $file_name -l ~/.emacs.d/elpa/elisp-autofmt-*/elisp-autofmt.el --eval "
(progn
  (let ((use-stdout nil))
    (dolist (buf (buffer-list))
      (with-current-buffer buf
        (when (buffer-file-name)
          (setq buffer-undo-list t) ;; Disable undo.
          (if use-stdout
              (progn
                (elisp-autofmt-buffer)
                (princ (buffer-substring-no-properties (point-min) (point-max))))
            (princ buffer-file-name)
            (princ \"\n\")
            (elisp-autofmt-buffer-to-file)))))))"
done

.github/workflows/elisp_lint.yml

ame: Emacs Lisp Format Check

on:
  push:
  pull_request:

jobs:
  format-check:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Emacs
        run: sudo apt-get install -y emacs-nox

      - name: Install pyenv
        uses: actions/setup-python@v5
        with:
          python-version: "3.12.9"

      - name: Install elisp-autofmt into emacs
        run: |
          mkdir -p ~/.emacs.d/elpa/elisp-autofmt-latest/
          wget -O ~/.emacs.d/elpa/elisp-autofmt-latest/elisp-autofmt.el https://codeberg.org/ideasman42/emacs-elisp-autofmt/raw/branch/main/elisp-autofmt.el

      - name: Run format_elisp_files.sh
        run: sh ./format_elisp_files.sh

      - name: Check diff
        run: git diff --exit-code

最後に

これで CI 上で elisp の lint ができます

参考サイト

2025年3月21日金曜日

emacs に elisp-autofmt を入れて elisp のフォーマットをしてみる

emacs に elisp-autofmt を入れて elisp のフォーマットをしてみる

概要

emacs の設定ファイルである el ファイルをフォーマットする方法を紹介します

環境

  • macOS 15.3.2
  • emacs 30.1
  • python 3.12.9

インストール

  • package-list-packages
  • elisp-autofmt -> x -> y

フォーマット実行

  • M-x elisp-autofmt-buffer

emacs を実行しているパスで python コマンドが使えないとダメなので注意しましょう

トラブルシューティング

実行時に

elisp-autofmt: error output
Note, built-in variable ‘image-scaling-factor’ not bound

となる場合は再度 elisp-autofmt-buffer を実行すればエラーが解消されます

自動起動

(add-hook 'emacs-lisp-mode-hook 'elisp-autofmt-mode)
(add-hook 'lisp-mode-hook 'elisp-autofmt-mode)

これで el ファイル保存時に自動でフォーマットしてくれます

うまく保存時にフォーマットしてくれない場合は以下でも OK です

(add-hook 'before-save-hook 'elisp-autofmt-buffer)

CLI で実行

emacs が必要ですが CLI からも実行できます
https://codeberg.org/ideasman42/emacs-elisp-autofmt/src/branch/main/elisp-autofmt-cmd.py を使っても OK です

for file_name in `git ls-files files | egrep -e 'el|emacs'`
do
emacs --batch $file_name -l ~/.emacs.d/elpa/elisp-autofmt-*/elisp-autofmt.el --eval "
(progn
  (let ((use-stdout nil))
    (dolist (buf (buffer-list))
      (with-current-buffer buf
        (when (buffer-file-name)
          (setq buffer-undo-list t) ;; Disable undo.
          (if use-stdout
              (progn
                (elisp-autofmt-buffer)
                (princ (buffer-substring-no-properties (point-min) (point-max))))
            (princ buffer-file-name)
            (princ \"\n\")
            (elisp-autofmt-buffer-to-file)))))))"
done

こちらも実行するパスに python が必要になるので注意しましょう

最後に

elisp-autofmt をインストールして elisp ファイルの自動フォーマットをしてみました

次回は CI を組み込んで lint してみたいと思います

参考サイト

2025年3月20日木曜日

emacs で特定のモード起動時にシェルコマンド実行する方法

emacs で特定のモード起動時にシェルコマンド実行する方法

概要

いくつかあるので紹介します

環境

  • macOS 15.3.2
  • emacs 30.1

フォアグラウンドで実行

(add-hook 'python-mode-hook
  (lambda ()
    (shell-command "echo 'Hello, Python!' >> ~/start_python_mode_msg.txt")))

バックグラウンドで実行

(add-hook 'c-mode-hook
  (lambda ()
    (start-process "make-process" nil "make")))

非同期実行

(add-hook 'go-mode-hook
  (lambda ()
    (async-shell-command "go build")))

シェルスクリプトを実行

(add-hook 'js-mode-hook
  (lambda ()
    (shell-command "bash ~/scripts/setup.sh")))

最後に

必要に応じて使い分けましょう

2025年3月2日日曜日

error: Eager macro-expansion failure: (error "Shortdoc f function ‘f-older-p’: bad keyword ‘:noeval’")

error: Eager macro-expansion failure: (error "Shortdoc f function ‘f-older-p’: bad keyword ‘:noeval’")

概要

emacs をアップデートした場合に出ることがあるようです

環境

  • macOS 15.3.1
  • emacs 30.1

対策

package を最新にしましょう

  • M-x package-list-packages
  • 大文字 U ですべてを更新対象にする
  • x を押して更新を実行

最後に

emacs 自体の更新は Homebrew で管理できますが emacs にインストールしているパッケージは自分で定期的に更新する必要があるので注意しましょう

2025年1月23日木曜日

emacs に lsp-java をインストールする方法

emacs に lsp-java をインストールする方法

概要

eclipse なしで Java を開発したい場合に lsp-java は必須かなと思います
ポイントは lsp-java とプロジェクトで動作する Java を同一にする点です

環境

  • macOS 15.2
  • emacs 29.4
  • lsp-java 3.1 (20240524.2207)

lsp-java のインストール

package-list-packages から lsp-java を選択してインストールすれば OK です

init.el の設定

; for java lsp with Eclipse JDT Language Server
(require 'lsp-java)
(add-hook 'java-mode-hook #'lsp)

Java のバージョンを指定したい場合は以下のように設定します

(require 'lsp-java)
(add-hook 'java-mode-hook
  (lambda ()
    (setq lsp-java-java-path "/opt/homebrew/Cellar/openjdk@21/21.0.5/libexec/openjdk.jdk/Contents/Home/bin/java")
    (lsp)))

初回起動: Eclipse JDT Language Server のダウンロード

lsp-java 初回起動時は以下のようなメッセージが表示され自動で Eclipse JDT Language Server がインストールされます
エンターを押せば自動でダウンロードされます

Unable to find installed server supporting this file. The following servers could be installed automatically

lsp-install.sh のバッファにダウンロードログが記録されるので確認しましょう

インストールパスは ~/.emacs.d/.cache/lsp/eclipse.jdt.ls/ でサイズが101MBと結構大きいのでディスク容量に注意しましょう

eclipse.jdt.ls % pwd
/Users/user01/.emacs.d/.cache/lsp/eclipse.jdt.ls
eclipse.jdt.ls % du -h -s
101M    .

動作確認

java ファイルを開いて lsp-java が起動することを確認しましょう
lsp-log のバッファにもエラーログがないことを確認します

completion-at-point などでクラス名などを補完したり M-. で jump できることを確認しましょう

トラブルシューティング

いくつかハマリポイントがあったので紹介します

java のパスとバージョンを確認する

lsp-log のバッファに「Unsupported class file major 67」が出ていて lsp-java でジャンプするときに「No definitions found for」など出る場合は lsp-java は起動していて emacs との接続はできているがうまく動作していません

確認する点は以下です

  • emacs を起動しているターミナルに正しい java コマンドが通っているか
  • jenv など使っている場合に zshrc を再度読み込ませているか
  • emacs を起動するパスがプロジェクトのルートパスになっているか
  • lsp-java が使用している java のバージョンが正しいバージョンになっているか (lsp-java-java-path の設定ミス)

あたりを確認していましょう
java のバージョンが 23 になっていたりすると該当のエラーが発生したりします

Gradle Daemon を再起動する

一度どこかのプロジェクトで gradle build しているバックエンドで Gradle Daemon というプロセス (org.gradle.launcher.daemon.bootstrap.GradleDaemon) が起動してしまいます
これが Java23 で動作していると lsp-java の gradle build も Java23 になってしまいます

  • ./gradlew --stop
  • rm -rf ~/.gradle/daemon

一度プロジェクトで gradle daemon を停止しgradle.properties に以下を追記します

org.gradle.java.home=/opt/homebrew/Cellar/openjdk@21/21.0.5/libexec/openjdk.jdk/Contents/Home
  • ./gradlew build

これで gradle daemon が起動する Java も 21 になり lsp-java が参照する Java も 21 になるはずです

それでもダメな場合はシステムで使用する Java を変更しましょう

  • jenv remove 23 23.0 23.0.1 openjdk64-23.0.1
  • sudo rm /Library/Java/JavaVirtualMachines/openjdk-*.jdk
  • sudo ln -sfn /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
  • jenv add "$(/usr/libexec/java_home)"

Eclipse JDT Language Server のバージョンを固定する

(setq lsp-java-jdt-download-url "https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.37.0/jdt-language-server-1.37.0-202406271335.tar.gz")
Cannot convert string value 'VERSION_21' to an enum value of type 'org.gradle.api.JavaVersion'

というエラーログが出る場合はバージョンを固定することで解決することもあります

最後に

lsp-java を emacs に導入してみました
Java のバージョンや JDT のバージョンで結構苦戦するところが多かったです

これで eclipse などの IDE なしで java の開発ができそうです

参考サイト

2024年12月30日月曜日

emacs で正規表現を使いながら置換する方法

emacs で正規表現を使いながら置換する方法

概要

query-replace-regexp を使います

環境

  • macOS 15.2
  • emacs 29.4

使用例

例えば

999 * leftSidePositionMagnification

を

(999 - leftSidePositionMagnification)

にしたい場合は以下のようにします

\(...\) \* leftSidePositionMagnification
(\1 - leftSidePositionMagnification)

元あった数字を使いまわしたい場合は検索時にカッコで囲い置換時に \1 という感じで数字で参照できます

最後に

元の値を残しつつ置換したい場合に便利です

2024年12月19日木曜日

emacs に copilot.el をインストールする方法

emacs に copilot.el をインストールする方法

概要

copilot が無料になって誰でも使えるようになったので emacs の設定をしました

環境

  • macOS 15.2
  • emacs 29.4
  • node 20.17.0

準備

  • node のインストール
    • nvm でローカルだけ使えるようにしてそこで emacs を起動しても OK
  • editorconfig、jsonrpc のインストール
    • package-list-packages などで melpa からインストールすれば OK

copilot.el のインストール

最初に straight.el をインストールしています
そのあとで copilot.el をインストールしています
初回起動時はいろいろと clone するので少し時間がかかります

; for copilot (with straight)
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(use-package copilot
  :straight (:host github :repo "copilot-emacs/copilot.el" :files ("*.el"))
  :ensure t)

copilot サーバのインストール

M-x copilot-install-server で node が起動しサーバのインストールと起動が始まります

node で書かれておりバックグラウンドで npm が起動し copilot-node-server がインストールされます
執筆時点ではバージョンは 1.27.0 でした

copilot-install-server というバッファに「Comint finished at Thu Dec 19 14:44:17」という感じで表示されれば OK です

copilot へのログイン

M-x copilot-login を実行します

  • ワンタイムコードが発行されるのでコピーします
  • https://github.com/login/device にアクセスします、アクセスするブラウザは copilot を使う Github アカウントでログインしているブラウザを使います
  • コピーしたコードを入力し認証します

下のような画面が表示されればあとは許可すれば OK です

emacs 側には「Authenticated as GitHub user username.」と表示されれば OK です

使ってみる

適当に Python ファイルなどを開いてみましょう
M-x copilot-mode で起動します
自動で copilot-mode にする場合は以下を追加します

(add-to-list 'copilot-major-mode-alist '("python" . "python"))

上記がうまく動作しない場合は以下でも OK です

(add-hook 'python-mode-hook 'copilot-mode)

copilot によって補完された候補を入力するには M-x copilot-accept-completion を実行します
これをショートカットキーに登録しておいてもいいかなと思います

うまく補完してくれない場合には M-x copilot-diagnose を実行して copilot events バッファにエラーログが出ていないか確認しましょう

最後に

emacs + copilot の設定をしてみました
無料だと制限があるのでそのうち制限に引っかかって使えなくなるかなと思います

勝手にガンガン補完してくれるので慣れるまで結構かかるかもしれませんが慣れれば便利なのかもしれません

参考サイト

2023年12月25日月曜日

emacs の lsp-mode で The following clients were selected based on priority エラー

emacs の lsp-mode で The following clients were selected based on priority エラー

概要

emacs + lsp-mode でうまく lsp サーバが起動できない場合の対処方法を紹介します

環境

  • macOS 11.7.10
  • emacs 29.1
  • lsp-mode 20231223.811

状況

emacs で ruby ファイルを起動した際に lsp サーバが見つからずに以下のようなエラーになる場合があります

Command "steep langserver --log-level warn" is not present on the path.                                                                      
Command "stree lsp" is not present on the path.
Command "ruby-lsp" is not present on the path.
Command "srb typecheck --lsp --disable-watchman" is not present on the path.
Command "bundle exec solargraph stdio" is present on the path.
Command "semgrep lsp" is not present on the path.
Command "rubocop --lsp" is not present on the path.
Command "steep langserver --log-level warn" is not present on the path.
Command "stree lsp" is not present on the path.
Command "ruby-lsp" is not present on the path.
Command "srb typecheck --lsp --disable-watchman" is not present on the path.
Command "bundle exec solargraph stdio" is present on the path.
Command "semgrep lsp" is not present on the path.
Command "rubocop --lsp" is not present on the path.
Found the following clients for /Users/hawk/data/repo/ruby-fndb/rank/app.rb: (server-id ruby-ls, priority -1)
The following clients were selected based on priority: (server-id ruby-ls, priority -1)

よくある対処方法としては emacs を起動しているシェルで PATH に solargraph や rubocop などがない場合に emacs から lsp が起動できないケースがありますが今回はしっかり PATH に通っている状態です

ログをよく見るとコマンドのチェックが二回行われています

原因

emacs に追加した lsp セッションパス内に複数の Gemfile があるとその配下全てでコマンドのチェックを行うため親ディレクトリなどに Gemfile があると 2 つの solargraph コマンドが見つかりどちらを起動したらいいのかわからないため該当のエラーになります

対応方法

一旦セッションファイルを作成して再度正しいプロジェクトのパスでセッションを登録しましょう

これで追加する際に親プロジェクトからではなく子プロジェクトを直接登録しましょう
以下の場合はドットを入力します

  • rm ~/.emacs.d/.lsp-session-v1
  • emacs app.rb
app.rb is not part of any project.                                                                                                           
                                                                                                                                             
i ==> Import project root ~/data/repo/ruby-fndb/                                                                                             
I ==> Import project by selecting root directory interactively                                                                               
. ==> Import project at current directory /Users/hawk/data/repo/ruby-fndb/rank/                                                      
d ==> Do not ask again for the current project by adding ~/data/repo/ruby-fndb/ to lsp-session-folders-blocklist                             
D ==> Do not ask again for the current project by selecting ignore path interactively                                                        
n ==> Do nothing: ask again when opening other files from the current project                                                                
                                                                                                                                             
Select action: .

これで再度 lsp が起動されエラーにならないことを確認します

最後に

emacs + lsp で起動しない場合は大抵の場合コマンドがインストールされていなかったり参照できなかったりが多いです
今回は参照はできるが複数の lsp サーバコマンドがある場合のエラーになります

ちなみに lsp-mode の ruby モードだと最優先で実行されるのは現状だと solargraph のようです

2023年12月4日月曜日

global な環境にインストールした gem をすべて削除する方法

global な環境にインストールした gem をすべて削除する方法

概要

Homebrew などでインストールした gem コマンドでインストールするとすべての環境に共通の場所に gem がインストールされます
まちがってグローバルな環境にいろいろな gem をインストールしてしまった場合に一旦すべてを削除する方法を紹介します

環境

  • macOS 14.1.2
  • Ruby 3.2.2

コマンド

  • gem uninstall -I -a -x --user-install --force

rbenv など使っている場合は必ず system になるパスで上記を実行しましょう

トラブルシューティング

アンインストール中に以下のようなエラーになり削除できないケースがあります

ERROR:  While executing gem ... (Gem::InstallError)
    test-unit is not installed in GEM_HOME, try:
        gem uninstall -i /opt/homebrew/Cellar/ruby/3.2.2_1/lib/ruby/gems/3.2.0 test-unit

そんな場合は素直に記載のコマンドを実行してアンインストールしましょう

  • gem uninstall -i /opt/homebrew/Cellar/ruby/3.2.2_1/lib/ruby/gems/3.2.0 test-unit

再度必要なものだけインストールする

  • gem install bundler
  • gem install solargraph
  • gem install rubocop

最後に

アンインストール後に gem list でインストール済みの gem の一覧を見ると結構あるので実は削除されていないものがあるような気もします

参考サイト

2023年10月30日月曜日

Company: frontend company-pseudo-tooltip-unless-just-one-frontend error "Company: backend company-capf error "Symbol’s function definition is void: string-lines" with args (match dataclass)" on command post-command

Company: frontend company-pseudo-tooltip-unless-just-one-frontend error "Company: backend company-capf error "Symbol’s function definition is void: string-lines" with args (match dataclass)" on command post-command

概要

タイトルのエラーが pyright + company で発生したので対応しました

環境

  • Ubuntu 22.04
  • emacs 27.1

対応方法

emacs を 29.1 にバージョンアップする

  • git clone --depth 1 --branch emacs-29.1 git://git.savannah.gnu.org/emacs.git emacs-29.1
  • cd emacs-29.1
  • sudo apt -y install autoconf textinfo libgnutls28-dev
  • ./autogen.sh
  • ./configure.sh
  • sudo make install

lsp-mode をアップグレードする

  • package-list-packages
  • Shift+U
  • x

あとは emacs を起動し直せば OK

最後に

emacs の最新版はソースインストールするしかないのでソースからインストールしましょう

足りないパッケージがあれば apt でインストールしてください

2023年7月7日金曜日

golangのlsp設定方法(emacs編)

golangのlsp設定方法(emacs編)

概要

gopls という公式が出している language server があるのでそれを使います
基本は公式のドキュメントを参考にすれば OK です

環境

  • Ubuntu 18.04
  • golang 1.20.5
  • gopls 0.12.4
  • emacs 27.1
    • lsp-mode 20230625

gopls のインストール

必要に応じて PATH を通しましょう

  • export PATH=$PATH:/home/user01/go/bin/

.emacs の編集

クライアントは lsp-mode を使っています
go-mode をフックして lsp を起動するだけです
golsp に対応しているバージョンを使いましょう

; for golang
(require 'lsp-mode)
(add-hook 'go-mode-hook #'lsp-deferred)

(defun lsp-go-install-save-hooks ()
  (add-hook 'before-save-hook #'lsp-format-buffer t t)
  (add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)

動作確認

念のためシェルを抜けて gopls がちゃんとコマンドとして起動することを確認してから emacs を起動しましょう
あとは go ファイルを開くだけで OK です

参考サイト

2023年6月26日月曜日

emacsでruby-lspを使ってみる

emacsでruby-lspを使ってみる

概要

solargraph に変えて ruby-lsp を使ってみました
emacs のクライアントは lsp-mode を使います

環境

  • macOS 11.7.6
  • emacs 28.2
    • lsp-mode 20230524.1820
  • Ruby 3.2.1
    • ruby-lsp 0.5.1

lsp-mode アップデート or インストール

ruby-lsp に対応しているバージョンをインストールしましょう

  • M-x package-list-packages

ruby-lsp

solargraph のアンインストール

  • gem uninstall solargraph

ruby-lsp のインストール

  • gem install ruby-lsp

ruby-lsp コマンドが実行できることを確認してください
また emacs から ruby-lsp コマンドが見つからず Command "ruby-lsp" is not present on the path. という感じでうまく起動できない場合はターミナルや tmux から一度抜けて再度試してみてください

emacs の設定

(require 'lsp-mode)
; (setq lsp-ruby-lsp-use-bundler t)
(add-hook 'ruby-mode-hook 'lsp)

bundler 配下で使用する場合には Gemfile に

gem 'ruby-lsp'

をし bundle install した後上記コメント部分をアウトしてください

動作確認

トラブルシューティング

solargraph から移行した場合には前のセッションが残っておりうまく動作しないことがあるのでセッションを削除して再度 ruby-lsp を起動してみてください

  • rm ~/.emacs.d/.lsp-session-v1
The connected server(s) does not support method textDocument/definition.
To find out what capabilities support your server use ‘M-x lsp-describe-session’
and expand the capabilities section

となり定義へのジャンプはできませんでした
おそらく https://github.com/Shopify/ruby-lsp/pull/195 にあるように特殊なコメントを記載することでそこに飛ぶことはできるようです
またその場合は lsp-mode 側で lsp-enable-links を有効にする必要がありそうです

最後に

まだまだ開発途中という感じがします
また vscode に特化しているのと記述が Spotify 基準になっているので少し特殊な書き方が必要かもしれません
当面は solargraph + robe という気がします

参考サイト

2023年6月1日木曜日

emacs+typescript-language-server設定方法

emacs+typescript-language-server設定方法

概要

emacs で TypeScript の開発環境を整えてみます
typescript-language-server という lsp があるのでこれを使います

環境

  • macOS 11.7.6
  • emacs 28.2
  • nodejs 19.7.0
    • typescript-language-server 3.3.2
    • typescript 5.0.4

typescript-language-server のインストール

  • npm install -g typescript-language-server typescript

lsp-mode と typescript-mode のインストール

  • M-x package-list-packages

で一覧から「typescript-mode」と「lsp-mode」をインストールします

emacs 設定

(require 'typescript-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . typescript-mode))
(add-hook 'typescript-mode-hook #'lsp-deferred)

参考サイト

2023年5月30日火曜日

emacsでFirefoxの拡張を開発する際にコード補完を設定する方法

emacsでFirefoxの拡張を開発する際にコード補完を設定する方法

概要

前回 emacs で Firefox 拡張を開発する環境を構築し lint やインデントの設定などを行いました

今回はコード補完を行う方法を紹介します

環境

  • macOS 11.7.6
  • emacs 28.2
  • nodejs 19.7.0
    • tern

tern のインストール

  • npm install -g tern

.tern.conf の作成

  • vim ~/.tern-config
{
  "libs": [
    "browser",
    "jquery",
    "ecma5",
    "underscore"
  ],
  "plugins": {
    "angular": {},
    "commonjs":{},
    "node":{},
    "requirejs":{},
    "node_resolve":{}
  }
}

tern-mode のインストール

  • M-x package-list-packages

tern-mode と tern-auto-complete をインストール

emacs の設定

前回の設定 (mode の起動や lint の設定) も記載しています
tern はどうやら company には対応しなくなったようで auto-complete 用の拡張があるのでそれを使うように変更しています

; for firefox extension (via js2-mode)
(require 'tern)
(require 'js2-mode)
(require 'flycheck)

# .js ファイルで js2-mode 起動
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
# js2-mode 起動時に auto-complete-mode 起動
(add-hook 'js2-mode-hook 'auto-complete-mode)
# インデントはスペース2つにする
(add-hook 'js2-mode-hook
  (lambda ()
     (setq my-js-mode-indent-num 2)
     (setq js2-basic-offset my-js-mode-indent-num)
     (setq js-switch-indent-offset my-js-mode-indent-num)
    ))
# js2-mode のデフォルトの linter は無効にする
(setq js2-mode-show-strict-warnings nil)
# js2-mode 起動時に flycheck-mode 起動
(add-hook 'js2-mode-hook #'flycheck-mode)
# flycheck の linter は eslint にする
(setq flycheck-javascript-eslint-executable "eslint")
(with-eval-after-load 'flycheck
  (flycheck-add-mode 'javascript-eslint 'js2-mode))

# js2-mode 起動時に tern-mode 起動
(add-hook 'js2-mode-hook
  (lambda ()
    (tern-mode t)))
# tern-auto-complete を使って連携 
(eval-after-load 'tern
  '(progn
    (require 'tern-auto-complete)
    (tern-ac-setup)))
# 補完用のキーバインドを設定
(define-key tern-mode-keymap (kbd "M-.") 'tern-find-definition)
(define-key tern-mode-keymap (kbd "M-,") 'tern-pop-find-definition)

最後に

js2-mode + flycheck (eslint) + tern-mode はおそらく JavaScript 単体の開発であれば Firefox 以外の環境でも使えると思います

emacsでFirefoxの拡張を開発する環境を構築する

emacsでFirefoxの拡張を開発する環境を構築する

概要

emacs で Firefox 拡張を開発する環境を構築してみます

環境

  • macOS 11.7.6
  • emacs 28.2
  • nodejs 19.7.0
    • eslint 8.41.0

eslint のインストール

  • npm -g install eslint

firefox 拡張のプロジェクト配下で eslint 用の設定ファイルを記載します

  • npm init @eslint/config
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · JavaScript
A config file was generated, but the config file itself may not follow your linting rules.
Successfully created .eslintrc.js file in 
  • cat .eslintrc.js
module.exports = {
  "env": {
    "webextensions": true,
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": "eslint:recommended",
  "overrides": [
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "rules": {
  }
}

js2-mode, company, flycheck のインストール

  • M-x package-list-packages

で js2-mode, company, flycheck を選択しインストールします

emacs の設定

(require 'company)
(require 'js2-mode)
(require 'flycheck)

# .js ファイルで js2-mode 起動
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
# js2-mode 起動時に company-mode 起動
(add-hook 'js2-mode-hook 'company-mode)
# インデントはスペース2つにする
(add-hook 'js2-mode-hook
  (lambda ()
     (setq my-js-mode-indent-num 2)
     (setq js2-basic-offset my-js-mode-indent-num)
     (setq js-switch-indent-offset my-js-mode-indent-num)
    ))
# js2-mode のデフォルトの linter は無効にする
(setq js2-mode-show-strict-warnings nil)
# js2-mode 起動時に flycheck-mode 起動
(add-hook 'js2-mode-hook #'flycheck-mode)
# flycheck の linter は eslint にする
(setq flycheck-javascript-eslint-executable "eslint")
(with-eval-after-load 'flycheck
  (flycheck-add-mode 'javascript-eslint 'js2-mode))

課題

  • 補完をどうするか