Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

Emacs Zshなどを設定した話

先月中途で入社した卜部です。

中途入社したタイミングでちょうどよいので環境設定を見直していました。 たぶん全部捨てて書き直したのは8年ぶりくらいでした。 今回の感想を忘れないうちに書いておきます。  

Emacs

Emacsを使い続けている理由はauto-save-buffersです。これと同じような機能を提供しているエディタは(RubyMineとかなくもないが)とても少なく、移行先として選択肢があまりありません。Atomのautosaveはどうやら求めているものと違うようですし。

auto-save-buffersで救われたファイルは数知れず、というか保存とかいう前世紀の遺物を計算資源に下請けさせることで考えることが減るので、本当は他のエディタにも是非あるべき機能です。下にzshでpredict-onの話も書いてありますが、自分は0クリック革命をわりと実践している派だと思います。

パッケージシステムを使うことにした

さて以前はEmacsの設定ファイルはもちろん手書きで、文字コードはEUC-JP、パッケージシステムなどもなく、すでに使わなくなったnavi2chの設定が残っていたり、最古のコードには"fj.editor.mule"と書いてあったりして、端的に言ってだいぶ雑然としていました。今回はこれを作り直すにあたり、「モダンEmacs」とか「Emacs modernization」でいろいろ検索したりして、やはりelispファイルをローカルにをコピペして運用する時代はとっくに終わっていたということと、「とりあえずparadoxが使えると良さそうだ」という結論になったので、まずはparadoxが使えるようにセットアップしました。

(require 'package)

(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
(package-initialize)
(package-install 'paradox)

(require 'paradox)

M-x paradox-list-packagesをぬぬぬと眺めて、多分use-packageというやつが便利の気がしました。

(略)
(package-initialize)
(eval-when-compile
  (require 'use-package))


(package-install 'use-package)
(use-package use-package
  :init
  (progn
    (package-install 'diminish)
    (use-package diminish
      :config
      (progn
        (add-hook 'lisp-interaction-mode-hook (lambda () (setq mode-name "Lisp")))
        (add-hook 'emacs-lisp-mode-hook (lambda () (setq mode-name "elisp")))
        (add-hook 'texinfo-mode-hook (lambda () (setq mode-name "texi")))
        (add-hook 'change-log-mode-hook (lambda () (setq mode-name "CL")))
        (diminish 'isearch-mode)))))

(package-install 'paradox)
(use-package paradox :init (setq paradox-github-token t))

以前はuse-packageが提供しているようなautoloadしてauto-mode-alist設定して…みたいのをやるマクロ書いたりしてましたが、今回は他人のパッケージに乗っかる判断です。

これは実際にやってみると、最初に学習コストがかかるのでやや面倒ではあるものの、慣れないelispの俺マクロをメンテしつづける工数のことを考えると断然軽いし、そもそもuse-packageを使ってみたら起動が高速になりました(従来は無駄な読み込みなどあったのだろうとおもいます)。この判断は正解だったなという気がします。

他のパッケージ

「これを使うと便利」みたいのは、今はまだよくわからんという感じですね。移行したばかりですし。もともと使ってたdiminishとかmic-parenとかwindmoveとかあるけど、それは鉄板すぎて今更だし、逆に今回からhelm使ってみたけど、まだ便利とかいうほど使いこなせてないし。

でも一応何を使ってるかというリストぐらいは出せて

(package-install 'use-package)
(package-install 'diminish)
(package-install 'paradox)
(package-install 'ruby-mode)
(package-install 'yard-mode)
(package-install 'rbenv)
(package-install 'ruby-block)
(package-install 'ruby-electric)
(package-install 'electric-spacing)
(package-install 'perl-mode)
(package-install 'php-mode)
(package-install 'haskell-mode)
(package-install 'coffee-mode)
(package-install 'csharp-mode)
(package-install 'go-mode)
(package-install 'slim-mode)
(package-install 'haml-mode)
(package-install 'yaml-mode)
(package-install 'markdown-mode)
(package-install 'gtags)
(package-install 'w3m)
(package-install 'mic-paren)
(package-install 'helm)
(package-install 'company)
(package-install 'tty-format)
(package-install 'ansi-color)
(package-install 'woman)

こんな感じのようです。major modeが多い。あとruby-electricとかelectric-spacingとか「コマンド叩かなくても勝手になんとかする」系が目に付くけど、やはりそのあたり人間より機械のほうがよほど正確ですね。人間は働かないのが正義。

ところで上のリストには肝心のauto-save-buffersが載ってませんね…? いまのところ動かすこと優先なのでまだコピペされたコードも残っている状態なのです。これはいずれ改善していきたいところです。どのパッケージ使えばいいんでしょうね。real-auto-saveってやつなのかな。  

Zsh

Zshを使い続けている理由はpredict-onです。この機能はなかなか強烈(ソースコードに魔術とか書いてあって笑うレベル)なので、一部の人にとってはライフチェンジングであり、合わない人には合わない機能かと思います。一言で言うと、コマンドプロンプトでブラウザのURL欄みたいに履歴のインタラクティブ検索をやってくれるやつです。履歴が育ってくると一文字二文字打つだけでだいたいの作業が終わるようになってくるが、設定とかとくにぜすとも「勝手に育つ」のが素晴らしい。慣れると後戻りが難しくなります。

なので他人にお勧めするのはややためらわれるのですが、そこまでではなくもう少し穏健な感じで同様の機能としてzsh-autosuggestionsというのもあるので、こちらは割と安心してお勧めできます。試してみるといいです。

パッケージシステムを使うことにした

さてそんなこんなでEmacs同様にZshの設定もプラグインを基本とした他人の尻馬に乗る時代かと思い、プラグインマネージャを使ってみようとしましたが、いろいろあるようですね。有名どころでoh-my-zshなど閲覧しましたが、大きすぎてどこを読んでいいかよくわかりませんでした。というわけで今回はファイル一個で読みやすいし置くだけ簡単設置なのでb4b4r07/zplugを採用しました(あと日本語通じそうというのももちろん地味にポジティブです)。

#!/bin/zsh
typeset -x ZPLUG_HOME=$ZDOTDIR/zplug
typeset -x ZPLUG_EXTERNAL=$ZDOTDIR/plugins.zsh
typeset -x ZPLUG_USE_CACHE=true

source $ZPLUG_HOME/zplug

# zplug update --self
zplug check || zplug install
zplug load

plugins.zshに使うプラグインを書いていきます。こんな

#!/bin/zsh
# -*- mode: zsh -*-
# vim:ft=zsh
#
# *** ZPLUG EXTERNAL FILE ***
# You can register plugins or commands to zplug on the
# command-line. If you use zplug on the command-line,
# it is possible to write more easily its settings
# by grace of the command-line completion.
# In this case, zplug spit out its settings to
# zpluglocal.zsh instead of .zshrc.
# If you launch new zsh process, zplug load command
# automatically search this file and run source command.
#
#
# Example:
# zplug "b4b4r07/enhancd", as:plugin, of:"*.zsh"
# zplug "rupa/z",          as:plugin, of:"*.sh"
#
zplug b4b4r07/zplug
zplug b4b4r07/emoji-cli, if:'[[ whence jq >/dev/null ]]'
zplug zsh-users/zsh-autosuggestions
zplug zsh-users/zsh-completions
zplug zsh-users/zsh-history-substring-search
zplug zsh-users/zsh-syntax-highlighting, nice:10
zplug zsh-users/zaw, nice:10
zplug mrowa44/emojify, as:command, of:emojify
zplug djui/alias-tips
zplug willghatch/zsh-cdr
zplug Tarrasch/zsh-functional
zplug "$ZDOTDIR/functions", from:local

zsh-functional

ところで今回はじめてこのTarrasch/zsh-functionalなるものの存在を知ったのですがこれはなかなか良いですね。mapとかeachとかそういう系のコマンドが提供されているだけですが。これでシェルの(控えめに言っても風変わりな)ループ構文を書かなくて良くなるのでかなりわかりやすくなる気がしました。

# 候補のうち最初に見つかったやつを採用
local pagers=(lv less jless w3m most more)
export PAGER=$(filter 'whence $@' $pagers | head -n1)

以前ならforifで書いていたところです。

プロンプトの話

今回powerlineも試してみました。たしかに見た目はかっこいい。しかしシェルの起動が目に見えて遅くなりました。毎日の作業中にシェルの立ち上がりがキビキビしないのは生産性の面で厳しいものがありますので、残念ながら不採用となりました。

プロンプトに関してはこれまで通り zsh % と出すだけのシンプルなやつで落ち着きました。

XDG Base Directoryの話

時々、$HOME直下ではなく$XDG_CONFIG_HOMEというディレクトリに設定ファイルを置いてもいいアプリがあります。代表的な例としてGitなどがあります。設定ファイルを一箇所にまとめておけるとバージョン管理がすごく簡単になるので、これはぜひ使いたいところです。

とはいえ全ての設定がどこかのディレクトリに移動できるかというとそうでもない。たとえばZshとかBashの設定ファイルは、そもそもそれが起動する瞬間までに$XDG_CONFIG_HOMEを指定する方法がないから、どうがんばっても~/.zshenvとか~/.bashrcとかから読むしかないです。他にもたとえば~/.sshを見に行くSSHクライアント実装がたぶん複数種あるので、それらは一存では場所を変えられないのではと愚考するところです。また、$XDG_CONFIG_HOMEとは別の環境変数を見るというやつもあります。そのような例としてVimがあるようです。

ようするにカオスなのですが、とはいえ設定ファイルの場所がまとまるのは魅力的です。なので「環境変数でまとめれるやつはまとめる、まとめれないのは諦める」というやや現実に負けた感のある折衷案に落ち着きました。このような感じです。

typeset -gx XDG_CONFIG_HOME=~/data/etc
typeset -gx XDG_CACHE_HOME=~/data/cache
typeset -gx XDG_DATA_HOME=~/data/share
typeset -gr ZDOTDIR=~/data/etc/zsh
typeset -gx PERLBREW_HOME=$XDG_CONFIG_HOME/perlbrew
typeset -gx RBENV_ROOT=$XDG_CONFIG_HOME/rbenv
typeset -gx GEM_SPEC_CACHE=$XDG_CACHE_HOME/rubygems/cache
typeset -gx INPUTRC=$XDG_CONFIG_HOME/readline/inputrc
typeset -gx LESSHISTFILE=$XDG_CACHE_HOME/less/history

そのほか

他の設定ファイルも一通り作り直しです。

tmuxを使ってみている

上でzsh上のpowerline諦めた話を書きましたが、とはいえかっこよさには一定の評価があるかと思い、tmuxでpowerlineしてみるかと試しているところです。

これに関しては初心者なので「使ってみてます」以上の話はないかとおもいます。powerline入れた以外はほとんどいじってないですし。

dir_colors

みなさんもlsの出力に色をつけて楽しんだりしていることかと思いますが、この色をつける呪文を生成してくれるdircolors(1)というプログラムがあり、それに入力する別の呪文がdir_colors(5)です。たとえば

*~      00;38;5;244

と書いておくと、~で終わるファイルがグレーアウトして見えるという塩梅。256色に対応しているようです(最後の244というのが色番号)。

以前はlsに渡す環境変数を手書きでメンテナンスしていましたが、さすがにそういう時代は終わったぽいので、今回は素直に他人の尻馬に乗ることにしました。

Git

Gitの設定ファイルはgithub tokenなど、不用意にgithubに公開してしまうと危険な情報を含んでいるため、注意が必要でした。バージョン管理しておくファイルと、意図的に.gitignoreするファイルを分けて、バージョン管理しておくほうからinclude.pathすることにしました

[include]
        path = ~/data/etc/git/local

まとめ

今回は方向性としては

  • パッケージシステムを最大利用して楽をする(設定ファイルで苦労しない)
  • 自動化できることを自動化して楽をする(作業で苦労しない)

という方向でした。おそらくは共感していただける方も多い方針ではと思います。また過去に苦労していた部分が今では他人のプロダクトでさっくりと実現できているところもあって、やはり時代は変わっているのだと実感しました。実際にはたとえばpredict-onなど癖もあるので、今回の自分の設定が他の人すべてにおすすめできるとは思いません。が、参考にしていただける部分はきっとあるのではと思います。

というわけで実際の設定ファイルはshyouhei/dotfilesに置いておきましたのでよろしくご査収ください。

最後に

マネーフォワードでは、エンジニアを絶賛募集しています。 ご応募お待ちしています。

【採用サイト】 ■マネーフォワード採用サイトWantedly | マネーフォワード

【プロダクト一覧】 ■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 iPhone,iPad家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 Androidクラウド型会計ソフト『MFクラウド会計』クラウド型請求書管理ソフト『MFクラウド請求書』クラウド型給与計算ソフト『MFクラウド給与』経費精算システム『MFクラウド経費』消込ソフト・システム『MFクラウド消込』マイナンバー対応『MFクラウドマイナンバー』創業支援トータルサービス『MFクラウド創業支援サービス』お金に関する正しい知識やお得な情報を発信するウェブメディア『マネトク!』