あれ、どうやったっけ

(たぶん)テキストサイト風blog。文が安定するまで書き直しあるからメンゴ。

これで終わりにしたい、emacs + company + powershell-mode + powershell(shellのほう)でだいたい補完

うまく動いたらこんな感じになるかしら。長い闘いだった……elisp初心者には。

f:id:osaka_zumai:20171123200606g:plain

スクショのcompany-powershell-modeっておかしいな、company-powershell-backendのほうが適切。めんどいからいいやもう

これでだいたいpowershell-modeでも、powershell(shell-modeのほう)でも補完入るはず@emacs25.2.1 on 窓10。Melpaでcompanyとpowershellをいれよう。

お前ら、ISEはもういりません!(本当か? 嘘いうなや 例:クラスのメソッド引けない)

shell-mode(powershellでshell-mode入っている奴)の場合dabbrev抜き、capf入れ、ほかのシェル使ってるときはpowershell補完しないよう起動コマンド見るフック。powershell-mode(ps1編集時)はdabbrevマシマシのフック。

ps1編集時にxxxx.exe要らんとかなら -CommandType の引数に「Application」を抜くとかで。その場合shell-mode(powershellの)では company-capf がコマンド補完してくれるでしょう……たぶん。

デフォルトでは ~/.emacs.d/ps-cache ってのができます。補完の初回時にキャッシュ作りますので一回目だけお時間いただきますぼくんちトランセンドSSD + A8-7600 + 24GBメモリなマシン構成で)4秒か5秒くらいかなぁ。

キャッシュはGet-Commandの出力ベタ書きしたものです。Cmdletとか、あたらしいexeとかインストールしたら消してキャッシュ作り直してください。キャッシュ作り直し手順とは company-powershell-tags-generate -> company-powershell-tags-reset した後、もう一回補完したらOK。

(autoload 'powershell "powershell-mode" nil t)

;; Editing ps1
(add-hook 'powershell-mode-hook
          '(lambda ()
             (set (make-local-variable 'company-backends)
                  (append '((company-files company-powershell company-dabbrev-code))
                          company-backends))))

;; running shell(as powershell)
(add-hook 'shell-mode-hook
          '(lambda ()
             ;; command = cmdproxy.exe or plink.exe, candidates do not need.
             (when (string-match "powershell"
                                 (car (process-command (get-buffer-process (buffer-name)))))
               (set (make-local-variable 'company-backends)
                    (append '((company-files company-powershell company-capf)) company-backends)))))

;; cache file save path
(setq-default ps-cache-save-path "~/.emacs.d/ps-cache")

;; memory cache
(setq company-powershell-tags nil)

;; Tagging target. See Get-Command -CommandType parameter.
;; https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-command?view=powershell-5.1
(setq-default ps-command-types "Alias,Function,Filter,CmdLet,Application")

(defun company-powershell-tags-generate ()
  "generate powershell commmand cache, cache will used by company-powershell."
  (interactive)
  (let ((ps-out ""))
    (setq ps-out (shell-command-to-string
                  (concat "powershell -Nologo -Command \"Get-Command -Name * "
                          " -CommandType " ps-command-types
                          " | Format-Wide -Property Name -Column 1\"")))
    (setq ps-out (string-trim ps-out))
    (with-temp-buffer
      (insert ps-out)
      (write-region (point-min) (point-max) ps-cache-save-path))))

(defun company-powershell-tags-reset ()
  "clear current powershell command candidates cache."
  (interactive)
  (setq company-powershell-tags nil))

(defun company-powershell (command &optional arg &rest ignored)
  (interactive (list 'interactive))
  (pcase command
    (`prefix (when (or (eq major-mode 'shell-mode)
                       (eq major-mode 'powershell-mode))
               (company-grab-symbol)))
    (`candidates
     (let ((ps-out "") (completion-ignore-case t))
       (when (not (file-exists-p ps-cache-save-path))
         (company-powershell-tags-generate))
       (when (eq company-powershell-tags nil)        
         (with-temp-buffer
           (insert-file-contents ps-cache-save-path)
           (setq ps-out (buffer-substring-no-properties (point-min) (point-max))))
         (setq company-powershell-tags (split-string ps-out)))
       (all-completions arg company-powershell-tags)))))

なお前記事で書いたprefixについては「補完できへんで次行って」がnil。 「後続のバックエンドに候補聞くな、聞いたらコロス」って伝えたい場合'stop、候補あるなら文字列返しやがれ、だそうです(よく見てないけどcompany-grab-symbolでよさげ)。俺に'stopは必要ない……というかcompany-modeの勉強したいんやないって。

後の問題は、クラスの時のメソッド名、electric絡み、flycheckとこれパッケージにでもしたほうが良いのかあたり。 すごくどうでもよくなってきた……。

elisp童貞やからelisp玄人の人あと頼むね、↑ のは好きにしていいから! (public domain的な意味で)