dotfiles 振り返り2022

thumbnail

まだまだ 2022 年の振り返りが終わらないぜということで今日は dotfiles の振り返り。dotfiles はその変遷を見ると面白いので、毎年やろうと思い早速やっていきたい。

ちょっと前に M2 の MBA 買って、dotfiles を一新した。 これが今の dotfiles だ。

github

https://github.com/sadnessOjisan/dotfiles

コンセプト

  • 自分は Mac しか使わない
  • が、WSL 環境も持ってるのでシェル周りの環境は移せるように作っておく(原神しかしないけど・・・)
  • make all だけでセットアップが完結する
  • 手作業はしない
  • なるべく標準に準拠し、プラグインやライブラリへの依存を減らす。入れる場合も単体で剥がせるものを選ぶ。
  • シンボリックリンクを貼って、dotfiles の変更が即時に反映されるようにする
  • .config など XDG に準拠する

ツール選定

NeoVim

Vim での作業を当初は考えていたので入れてある。だが設定にハマると大変なので使うのは LSP と括弧挿入とファイラくらいだ。

ちなみにファイラの話をするとブリザラとかサンダラの話をする人がいるのだが、俺は好き。

Zsh

もともと fish ユーザーだったが、POSIX 準拠じゃない点で色々困ったことがあった。script であれば互換実行があるのでいいのだが、例えば人から Slack でもらったスクリプトの実行などで困ったりしていた。そこで zsh に戻すことにした。そして zsh で fish の操作感を出すためにあれやこれやをしていく。

starship

starship lp

シェル用の最小限の、非常に高速で、無限にカスタマイズ可能なプロンプトです!

https://starship.rs/ja-jp/guide/

プロンプトに Git の状態や Node のバージョンを出したくて入れた。Git の状態くらいであれば shell script 書けばすぐできるのだが、言語のバージョンはそうはいかなかった。Node くらいであれば自作してもいいかもと思ったが、最近は Node, Rust, Scala, Go, Python などといろんな言語を書いているのでそのセットアップは非常にめんどくさかった。なのでその辺をワンストップで設定してくれる starship を使うことにした。まあ結局バージョンを頻繁に切り替えるのは Node だけなんだけど。

alacritty

alacritty lp

ターミナルの文字とかの設定を、手作業を廃したくて入れた。その辺を yaml で設定できる。そして alacritty がなくても普通の zsh でも同じ zsh 設定が動くのでいざ剥がす時も剥がしやすいと思ってこれに選定した。

tmux

alacritty が標準ターミナルと違って cmd + t でタブを増やせなかったので、タブとかを増やしたくて tmux 入れた。

セットアップ

make のエントリ

make all で全てが完結するようにしている。

.PHONY: all
all: pre starship starship_conf nvim lang zsh brew alacritty tmux_conf

サブコマンドを呼び出すのでそれを見ていこう。

pre で usr/local/bin のセット

環境によっては usr/local/bin がない。そして starship はこの dir を要求するので事前に作っておく。

.PHONY: pre
pre:
	if [ ! -d /usr/local/bin ]; then sudo mkdir -p /usr/local/bin; fi

starship

.PHONY: starship
starship:
	curl -sS https://starship.rs/install.sh | sh

.PHONY: starship_conf
starship_conf:
	if [ ! -d ${HOME}/.config ]; then mkdir -p ${HOME}/.config; fi
	ln -s -f ${PWD}/starship/starship.toml ${HOME}/.config/starship.toml

starship の install と設定へのシンボリックリンクを設定する。こんな設定にしてある。

format = """
$username\
$hostname\
$directory\
$git_branch\
$git_state\
$git_status\
$cmd_duration\
$nodejs\
$line_break\
$python\
$character"""

[directory]
style = "blue"

[character]
success_symbol = "[❯](purple)"
error_symbol = "[❯](red)"
vicmd_symbol = "[❮](green)"

[git_branch]
format = "[$branch]($style)"
style = "bright-black"

[git_status]
format = "[[(*$conflicted$untracked$modified$staged$renamed$deleted)](218) ($ahead_behind$stashed)]($style)"
style = "cyan"
conflicted = ""
untracked = ""
modified = ""
staged = ""
renamed = ""
deleted = ""
stashed = "≡"

[git_state]
format = '\([$state( $progress_current/$progress_total)]($style)\) '
style = "bright-black"

[cmd_duration]
format = "[$duration]($style) "
style = "yellow"

[python]
format = "[$virtualenv]($style) "
style = "bright-black"

[nodejs]
format = "[🤖 $version](bold green) "

nvim

.PHONY: nvim
nvim:
	sh nvim/install-plugin-manager.sh
	if [ ! -d ${HOME}/.config/nvim ]; then mkdir -p ${HOME}/.config/nvim; fi
	ln -s -f ${PWD}/nvim/init.vim ${HOME}/.config/nvim/init.vim
	ln -s -f ${PWD}/nvim/coc-config.json ${HOME}/.config/nvim/coc-config.json

誘惑に負けて plugin manager を使ってしまった。

#!/bin/sh

sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'

設定はかなり薄くしており、LSP, ファイラ、括弧補完くらいだ。LSP は coc を使うことで nvim-lsp の Lua の設定業を廃した。VSCoder には難しすぎた。

call plug#begin()
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'cocopon/iceberg.vim'
call plug#end()

colorscheme iceberg

:set number
set autoread
inoremap { {}<LEFT>
inoremap [ []<LEFT>
inoremap ( ()<LEFT>
inoremap " ""<LEFT>
inoremap ' ''<LEFT>

" yank to clipboard
set clipboard+=unnamed

" for peco
:set modifiable

" terminal
command! -nargs=* T split | wincmd j | resize 20 | terminal <args>
autocmd TermOpen * startinsert

" 隣接した{}で改行したらインデント
function! AddIndentWhenEnter()
    if getline(".")[col(".")-1] == "}" && getline(".")[col(".")-2] == "{"
        return "\n\t\n\<UP>\<END>"
    else
        return "\n"
    endif
endfunction
" Enterに割り当て
inoremap <silent> <expr> <CR> AddIndentWhenEnter()

set expandtab "タブ入力を複数の空白入力に置き換える
set tabstop=4 "画面上でタブ文字が占める幅
set shiftwidth=4

" netrw
set nocp                    " 'compatible' をオフにする
filetype plugin on          " プラグインを有効にする
let g:netrw_preview=1
let g:netrw_liststyle=3 " tree表示
let g:netrw_keepdir = 0
let g:netrw_banner = 0
"window サイズ
let g:netrw_winsize = 25
let g:netrw_browse_split = 4

"Netrw を toggle する関数を設定
"元処理と異なり Vex を呼び出すことで左 window に表示
let g:NetrwIsOpen=0
function! ToggleNetrw()
    if g:NetrwIsOpen
        let i = bufnr("$")
        while (i >= 1)
            if (getbufvar(i, "&filetype") == "netrw")
                silent exe "bwipeout " . i
            endif
            let i-=1
        endwhile
        let g:NetrwIsOpen=0
    else
        let g:NetrwIsOpen=1
        silent Vex
    endif
endfunction

"ショートカットの設定
"= を 2 回連続押下で toggle
noremap <silent><C-e> :call ToggleNetrw()<CR>

" coc
" Use tab for trigger completion with characters ahead and navigate.
" NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
" other plugin before putting this into your config.
inoremap <silent><expr> <TAB>
      \ coc#pum#visible() ? coc#pum#next(1):
      \ CheckBackspace() ? "\<Tab>" :
      \ coc#refresh()
inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"

" Make <CR> to accept selected completion item or notify coc.nvim to format
" <C-g>u breaks current undo, please make your own choice.
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                              \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)

" GoTo code navigation.
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

" Use K to show documentation in preview window.
nnoremap <silent> K :call ShowDocumentation()<CR>

function! ShowDocumentation()
  if CocAction('hasProvider', 'hover')
    call CocActionAsync('doHover')
  else
    call feedkeys('K', 'in')
  endif
endfunction

まあ設定はしたものの、ほとんど使わない。最近全部 JetBrains 系に移したのでそのことはまたブログにしたい。ただ Vim が立ち上がるときはそれなりにあるので、 Vim の環境を揃えておくのは良いことだ。

言語の設定

どの PC にも入っていて欲しいのは Node と Rust なのでその設定。

.PHONY: lang
lang:
	sh ${PWD}/lang/rust/install.sh
	sh ${PWD}/lang/node/install.sh

rust は rustup を入れるだけ

# install for linux or mac
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

node は nvm 経由で入れる

if [ ! -d ${HOME}/.nvm ]; then mkdir ${HOME}/.nvm; fi

たまに職場によっては nodeenv 前提とかあるのでグヌヌとかになるけど、その時はその時で Hack してる。

zsh

.zshenv に ZDOTDIR を指定して、.zsh にある設定を読み取らせるようにする。

# This .zshenv is different from zsh/.zshenv.
# This is just entrypoint for zsh.
# This role is only configure to setup .zsh.
# Real env is loaded in zsh/.zshenv.

echo "@load env@"

export ZDOTDIR="$HOME/.zsh"

source $ZDOTDIR/.zshenv

そして .zsh/ にあるファイルにシンボリックリンクを make で貼りまくる。

.PHONY: zsh-conf
zsh-conf:
	ln -sf ${PWD}/.zshenv ${HOME}/.zshenv
	mkdir -p ${HOME}/.zsh
	ln -sf ${PWD}/zsh/.zshenv ${HOME}/.zsh/.zshenv
	if [ ! -e ${HOME}/.zsh/.zshenv.local ]; then ln -sf ${PWD}/zsh/.zshenv.local ${HOME}/.zsh/.zshenv.local; fi
	ln -sf ${PWD}/zsh/.zshrc ${HOME}/.zsh/.zshrc
	if [ ! -e ${HOME}/.zsh/.zshrc.local ]; then ln -sf ${PWD}/zsh/.zshrc.local ${HOME}/.zsh/.zshrc.local; fi
	ln -sf ${PWD}/zsh/.zprofile ${HOME}/.zsh/.zprofile
	if [ ! -e ${HOME}/.zsh/.zprofile.local ]; then ln -sf ${PWD}/zsh/.zprofile.local ${HOME}/.zsh/.zprofile.local; fi
	ln -sf ${PWD}/zsh/scripts ${HOME}/.zsh/

.PHONY: zsh
zsh: zsh-plugin zsh-conf

.local は GitHub で管理しないもので、その端末固有の設定だ。日頃から作業によって PC を 4 台ほど切り替えて作業しているのでこのようなローカル設定の余地を残している。

plugin manager をどうするかは必死に考えた結果、何も使わないことにした。昔はなんだかんだいろいろなものを使っていたが、余計なこといろいろされたり遅いイメージしかなかった。じゃあどうしたかというと直にセットするのである。

.PHONY: zsh-plugin
zsh-plugin:
	sh ${PWD}/zsh/install-plugin.sh
#!/bin/zsh

# install zsh-autosuggestions
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline"
git -C ~/.zsh/zsh-autosuggestions pull 2>/dev/null || git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions

https://github.com/zsh-users/zsh-autosuggestions/blob/master/INSTALL.md#manual-git-clone などに書いてあるが、plugin は実行させるだけで install できるので、これを ZDOTDIR で指定したフォルダに clone しておき、.zshrc から source することでインストールできる。

# load plugin
source ${HOME}/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh

brew

make に必要なパーツは brew で入れるので、brew も宣言的な設定をできるようにした。

.PHONY: brew
brew:
	sh brew/install.sh
	brew tap Homebrew/bundle
	brew bundle --file "brew/Brewfile"
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
eval "$(/opt/homebrew/bin/brew shellenv)" # brew コマンドが使えるようになる

絶対に入れたいものは BrewBundle で管理している。

tap "homebrew/bundle"
tap "homebrew/cask"
tap "homebrew/core"
tap "homebrew/cask-fonts"
cask "font-source-code-pro"
cask "alacritty"
brew "svn" # for sourcecodepro
brew "peco"
brew "tig"
brew "neovim"
brew "jq"
brew "gh"
brew "nvm"
brew "neovim"
brew "tmux"

だ lock がんだん古くなるはずなのでそのメンテはちゃんと考えておきたい。

alacritty

brew で入るので設定を移すだけ

.PHONY: alacritty
alacritty:
	if [ ! -d ${HOME}/.config/alacritty ]; then mkdir -p ${HOME}/.config/alacritty; fi
	ln -s -f ${PWD}/alacritty/alacritty.yml ${HOME}/.config/alacritty/alacritty.yml

フォントの設定をしている

# Font
font:
  size: 13
  font:
    normal:
      family: SauceCodePro Nerd Font
      style: Regular
    bold:
      family: SauceCodePro Nerd Font
      style: Bold
    italic:
      family: SauceCodePro Nerd Font
      style: Italic

    # Point size of the font
    size: 11.5

# env
env:
  TERM: alacritty

tmux_conf

これも brew で入るので設定を移すだけ

.PHONY: tmux_conf
tmux_conf:
	ln -sf ${PWD}/tmux/.tmux.conf ${HOME}/.tmux.conf
# prefixキーをC-sに変更する
set -g prefix C-s

# デフォルトのprefixキーC-bを解除する
unbind C-b

setw -g window-status-current-format '#[bg=colour2,fg=colour255]#{?client_prefix,#[bg=colour3],} #I #W '

prefix のキーは悩んだ。押しやすくて、Vim や Emacs と衝突させないためにはどうすればいいんだろう。

起動

make all

で動く。経験上たまに brew 周りでコマンドがないと言われて止まることがあるのだが、そのときは eval "$(/opt/homebrew/bin/brew shellenv) のコマンドだけ手で入れたりすることもあった。

まとめ

この設定で今のところ困っていないが、プロからすると非効率だと思うのでアドバイスもらえると嬉しい。