PowerShellを書くことが多くなってきたので、補完やREPLが欲しくなって整えた。
tree-sitter
をつかうのがイマドキな感じだが、PowerShellのはうまく動かなかった。
しかたがないので、メジャーモードでやる。
powershell.elを使う。
MelpaからとれるのでM-x package-install powershell
でインストールできる。
NTEmacsのshellはcmd
なので、PowerShellのスクリプトを実行するには、$ powershell -File foo.ps1
のように呼ばないといけない。これは面倒。
PowerShellのプロンプト環境をEmacsから使えれば、$ foo.ps1
で呼び出せる。
powershell.elにはそれがある。M-x powershell
でM-x shell
のようにPowerShellのプロンプト環境が立ち上がる。
前項で、スクリプト実行をEmacsの中でできるようになったが、
次に欲しくなるのが、スクリプトの一部をshellに送って実行するREPL環境。
残念ながら、powershell.elにはその機能はない。
なので自作する。
以下のように関数を定義して、それをキーにバインドすればいい。
;;; powershell-shell.el --- powershell shell sender
;;; Commentary:
;;; Code:
(require 'powershell)
;;; interactive
;;;###autoload
(defun my-powershell-shell-send-line ()
"Send powershell shell pointd line."
(interactive)
(let ((string (replace-regexp-in-string
"^[ \t]+"
""
(thing-at-point 'line t)))
(process (get-buffer-process "*PowerShell*")))
(my-powershell-shell-send-string string process)))
;;;###autoload
(defun my-powershell-shell-send-region (s e)
"Send powershell shell region.
S region start point
E region end point"
(interactive "r")
(let ((region-string (buffer-substring-no-properties s e))
(process (get-buffer-process "*PowerShell*")))
(mapc (lambda (string)
(my-powershell-shell-send-string string process)
;; Wait for powershell side standard output.
(sleep-for 0.01))
(split-string region-string "[\n\r]"))))
(defun my-powershell-shell-send-string (string process)
"Send string to the process.
STRING substring up to newline is sent.
PROCESS process object"
(with-current-buffer (process-buffer process)
(goto-char (point-max))
(insert (car (split-string string "[\n\r]")))
(comint-send-input)
(goto-char (point-max))))
(provide 'powershell-shell)
;;; powershell-shell.el ends here
キーバインドの設定例。
(leaf *powershell-mode
:doc "powershell用設定"
:hook (powershell-mode-hook . my-powershell-mode-hook)
:preface
(defun my-powershell-mode-hook ()
(local-set-key (kbd "<C-return>") 'my-powershell-shell-send-line)
(local-set-key (kbd "C-x C-e") 'my-powershell-shell-send-region)
(lsp))
:config
(leaf powershell
:ensure t))
仕組みを解説する。
(get-buffer-process "*PowerShell*")
でPowerShellのshellバッファを探し、そこで起動されているプロセスを取得(process-buffer process)
でPowerShellのshellバッファを探し、カレントバッファを一時的に切り替える(comint-send-input)
で実際のプロセスに送る(Enterキー押したのと同じ)shellバッファの探し方で1回プロセスを経由するのはPythonのときのコードを流用したからで、
Pythonのときはスクリプトに結びついたrun-pythonのプロセスを取得する関数があったため。
powershell.elにはその機能はないので、バッファ名で直接指定。
lsp
を使えばよい。Language Serverはpwsh-ls
。M-x lsp-install-server
でpwsh-ls
を選べばOK。
company
とflycheck
使えるようにしてれば、補完とLintできるはず。
ただし、lsp-mode
が古いとうまく起動しなかった。
自分の環境だとlsp-mode
が8.0.0
でダメだったので、最新20230401.434
だとうまくいった。
原因は、pwsh-ls
を起動するためのスクリプトのパスがlsp-mode
のlsp-pwsh
で定義されているが、間違っている。
どのバージョンで正しくなったかまでは追ってないが、とりあえず最近のにすれば大丈夫そう。
M-x lsp-format-buffer
で整形してくれる。
まだ大きいスクリプトで試してないので、しばらくは手打ちで様子見。
問題なければ、before-save-hook
に追加するつもり。
マイナーな言語なので、lspとかshellとかないだろうと思ってたが、どちらも用意されていて助かった。
PowerShellのいいとこが最近わかってきて積極的に使いたいと思っている。