• 作成:
  • 更新:

SWI-PrologのEmacs向け開発支援機構Sweepをセットアップ

SWI-Prolog用のLSPサーバがありますが機能が貧弱です

SWI-Prologには、 jamesnvc/lsp_server: Language Server Protocol server for SWI-Prolog というLSPサーバがあり、 lsp-modeにも、 Prolog - LSP Mode - LSP support for Emacs として公式にサポートされています。

swipl -g 'pack_install(lsp_server).'

としてインストールして、

(lsp-register-client
 (make-lsp-client
  :new-connection
  (lsp-stdio-connection (list "swipl"
                              "-g" "use_module(library(lsp_server))."
                              "-g" "lsp_server:main"
                              "-t" "halt"
                              "--" "stdio"))
  :major-modes '(prolog-mode)
  :priority 1
  :multi-root t
  :server-id 'prolog-ls))

のように登録すればスムーズに使えるようになります。

しかし、 VSC-Prolog - Visual Studio Marketplace などの動きを見たあとにこれを見ると、定義されていない述語を報告してくれなかったり、かなり機能が貧弱と言わざるを得ません。

VSC-Prologの説明を見るとSWI-Prologの機能を使っているようなので、 Emacsでも似たようなものを使うことが出来るのではないかと思って調べました。

Sweepがありました

Program Development Tools には"Using GNU-Emacs"の項目があり、 Sweep: SWI-Prolog Embedded in Emacs というEmacs専用の開発支援機能があることが分かりました。

正確には開発支援機構に留まらず、 Emacs LispからSWI-Prologを使ったりすることも出来るようです。

これのセットアップに少々手こずったので、メモをしておきます。日本語文献が当然のようにない。

nongnuにはパッケージがあります

まず公式には手動でgit cloneしろと書いてありますが、流石にやりたくありません。

GitHubに載ってるため最初はel-getを使おうかと思いましたが、 NonGNU ELPAにはパッケージがあります。 NonGNU ELPA - sweeprolog melpaには載っていないようです。

これまでmelpagnuだけでやりくりしてきましたが、ついにnongnuをリポジトリ一覧に追加する日が来たようです。

しかしsweeprologパッケージはEmacs側でインストールするだけでは動かないことがあります。 C言語のモジュールを使ってやり取りをしているからです。

SWI-Prologをソースからビルドしましょう

Gentooだと普通にemerge swi-prologするだけで、多分minimalとか設定されてなければ、 sweep-module.soがインストールされるため余分なことは不要でした。

しかしUbuntuだとaptからインストールしたSWI-Prologではsweep-module.soが入りませんでした。

アップデートへの追随とかを考えると、あまり手動でビルドをしたくないから、なんとかパッケージマネージャを使いたくなるのですが、とりあえず今回は早くSweepを動かしたいので妥協してビルド前提にすることにしました。

SWI-Prolog -- Installation on Linux, *BSD (Unix) を参考にビルドしてインストールします。

ビルドには様々なパッケージが必要です。 Ubuntuの場合は/etc/apt/sources.listdeb-srcのコメントアウトを外して、

apt-get build-dep swi-prolog

を実行すると良いでしょう。

コメントアウトを外してネットワーク通信を多くしたくない人は、素直に大量のapt-get installをしても良いと思います。

root権限を必要とする箇所を不用意に変えたくないので、 /home/以下にインストールする上のビルド方法を使うのですが、そのままだと変な感じにインストールされてしまうため、改変が必要でした。

変えた部分は、

cmake -DCMAKE_INSTALL_PREFIX=$HOME -G Ninja ..

の部分を、

cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local/ -G Ninja ..

に変えてxdg準拠にするだけです。これだと~/.local/bin/を既にPATHに入れているような標準的環境だと、 SWI-Prologの実行ファイルのPATHは新規に通さなくて良いです。

ただしaptで入れた場合と違い、 prologコマンドは無くなってしまうようなので、 EmacsにSWI-Prolog特化しているという設定をするか、シンボリックリンクでも貼っておく必要があるでしょう。

私はSWI-Prologに特化していくことにしました。

(leaf prolog :custom (prolog-system . 'swi))

パスを通す必要があるかもしれません

手動でインストールした場合だとライブラリパスが貼られないので、 sweeprolog-libswipl-pathを設定する必要があるかもしれません。私の場合は設定しなくても実行バイナリからの繋がりなのか接続できましたが、人によっては"~/.local/lib/swipl/lib/x86_64-linux/"のように設定する必要があるかもしれません。

使えるようになりました

これで(sweeprolog-mode)を使えるようになりました。

端的にしか説明してなかったのと、日本語資料がなくてちょっと戸惑ってしまいましたが、方法としては普通でしたね。

本当はflycheckが使いたいけどflymakeを使用します

Sweepはデフォルトではflymakeを使うようですね。普段flycheckを使っているので出来ればflycheckにしたいです。

lsp-modeの普及で基本的にセットアップが不要になったので、 flycheckからflymakeに回帰しても良いのではないかと思うときも度々ありますが。

軽く調べた所Sweepがflycheckを直接サポートしていることは無いようです。

flycheckがflymakeのラッパーをやれば良いのではないかと思って少し調べたのですが、そう簡単な話でもないらしいですね。

仕方がないのでflymakeの設定を以下のようにしておくことにしました。

(leaf flymake
  :bind (:flymake-mode-map
         ("C-z" . flymake-show-buffer-diagnostics)
         ([remap previous-error] . flymake-goto-prev-error)
         ([remap next-error]     . flymake-goto-next-error)))

REPLの起動は(sweeprolog-top-level)

名前とキーバインドがEmacsの風習から少し外れていたので少し気がつくのに時間がかかりましたが、 (sweeprolog-top-level)で普通にREPLは起動できました。

ひとまずそこそこのエラーチェックを手に入れました

これでVSCodeのVSC-Prologと同程度、いやそれ以上かもしれない開発支援を手に入れました。これでインタプリタ開発のためのテストコード記述などが少しは捗ってくれるでしょう。

Emacsのxwidgetsを有効にしていると一定のソースコードでクラッシュします

sweepが一定のPrologソースコードでクラッシュして、クラッシュするソースコードとしないソースコードがあるので不思議に思いました。とりあえずスタックトレースを見るとswiplが関わっているのでGitHubで報告をしました。 Emacs crashes with segv when opening library/clp/bounds.pl in Emacs with sweep enabled · Issue #1188 · SWI-Prolog/swipl-devel

報告したら開発者が調査してくれて詳細はよく分からないんですが、 Emacsのxwidgets機能を有効にしているとクラッシュするらしいです。とりあえずビルドの./configureから--with-xwidgetsを抜いて凌ぐことにします。今の所WebKitの機能そんなに使うことないですしね。

修正されました

SWI-Prologの開発者さんの努力でクラッシュ問題は解決しました。

C言語追いかけるのが面倒なのとSWI-Prologのソースコードがそれなりの大きさであることで完全には把握してないんですが、どうもxwidgetのWebKitとSWI-PrologのGUI機能でシンボルが衝突してクラッシュしてたらしいです。