linuxにおけるtmuxの設定(クリップボード, ステータスライン)

## 概要
- powerlineを使わずにpowerlineみたいな表示にしてます。そのため特殊文字を使います。
![2018-01-18_10_10_39.png](https://qiita-image-store.s3.amazonaws.com/0/163210/f2012d36-d6be-9b93-9e58-46d1e4e4ebea.png)
![2018-01-28_11_52_55.png](https://qiita-image-store.s3.amazonaws.com/0/163210/92c128b8-73a6-2434-90e9-a93bfda24495.png)
![2018-01-28_11_57_19.png](https://qiita-image-store.s3.amazonaws.com/0/163210/b747a841-014e-3171-128e-5537ca11ba6d.png)

- ステータスラインの表示をシェルスクリプトで行ってます。メモリ消費量、ロードアベレージ、信号強度、音量、電池残量と時間を表示しています。
- ステータスラインがウィンドウを狭めると完全に消えるのが悲しいので、画面幅によって表示を変えています。画面幅は`tput cols`というコマンドでわかります。
- Xのクリップボードとtmuxのbufferを連携させています。
- 音量をステータスラインに表示しています。ですが、tmuxのステータスラインはn秒で更新するので、それにより音量を変えたときの表示がもっさりします。解決策として、音量を変えるキーバインドにステータスラインを更新するコマンドを&&で繋いで書きます。`&& tmux refresh -S`
- 左側のステータスラインはtmuxのセッションとzshのモードを表示しています。その処理は.zshrcに書いてます。

## 動作環境
- Arch Linux(4.19.8-arch1-1-ARCH)
- tmux 2.8
- [True Colorをサポートしているターミナル。](https://gist.github.com/XVilka/8346728) alacritty, termite, gnome-terminal, mlterm, ...
- 特殊文字(三角形を90度傾けたやつなど)をサポートしているフォント。Hack, FiraCode, ...
- pulseaudioがインストールされていること。サウンドに関するパッケージ。大抵の場合は入っています。音量を表示するときに使用します。
- xselがインストールされていること。Xのクリップボードと連携する際に使用します。

## コマンド
set == set-option
setw == set-window-option
bind == bind-key -T prefix
prefix-keyを押さずにbindとした処理が走る。
bind -n == bind-key -T root

## tmuxの設定
```conf:.tmux.conf
# prefix-keyの指定
set -g prefix C-g
# prefix-keyの解除
unbind-key C-b
# 設定ファイルをリロード
bind-key -T prefix r source-file ~/.tmux.conf \; display-message 'Reload'
# True Colorを使用する
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc"
# ESCの反応を早くする
set -g escape-time 1
# 添字を1から始める
set -g base-index 1
setw -g pane-base-index 1
# マウスを有効
setw -g mouse on
# ステータスラインの更新間隔を1秒にする
set -g status-interval 1
# ウィンドウを閉じた際に番号を詰める
set -g renumber-windows on
# prefix-keyの有効時間をミリ秒で指定
# bind-key -rで指定しているkeyはprefix-keyを再度押さなくとも連続で効く
set -g repeat-time 400
# display-message
set -g display-time 1500
# focus events
set -g focus-events on

# ----------------------------------------
# Key bindings
# ----------------------------------------
# コマンドプロンプト
bind -n M-: command-prompt -p '>>>'
#ペイン間の移動
bind -n M-h select-pane -L
bind -n M-j select-pane -D
bind -n M-k select-pane -U
bind -n M-l select-pane -R
#新しいウィンドウ(current-directoryを引き継ぐ)
bind -n M-w new-window -c '#{pane_current_path}'
#ウィンドウ間の移動
bind -n M-u select-window -t :-
bind -n M-i select-window -t :+
bind -n M-1 select-window -t 1
bind -n M-2 select-window -t 2
bind -n M-3 select-window -t 3
bind -n M-4 select-window -t 4
bind -n M-5 select-window -t 5
bind -n M-6 select-window -t 6
bind -n M-7 select-window -t 7
bind -n M-8 select-window -t 8
bind -n M-9 select-window -t 9
# new-session
bind -n M-s  command-prompt -p \
  'new-session :' "new-session -s %% -c '#{pane_current_path}'"
# switch-client
bind c display-message 'switch-session' \; run-shell \
  'tmux choose-tree -s -f "##{!=:##{session_name},#{session_name}}"'
# kill-session
bind x display-message 'kill-session' \; run-shell \
  'tmux choose-tree -s -f "##{!=:##{session_name},#{session_name}}" \
    "kill-session -t %%"'
#ペインの大きさを調整
bind -r < resize-pane -L 5
bind -r > resize-pane -R 5
bind -r - resize-pane -D 5
bind -r + resize-pane -U 5
#ペインを区切る(current-directoryを引き継ぐ)
bind v split-window -h -c "#{pane_current_path}"
bind s split-window -v -c "#{pane_current_path}"
#ペインの移動(右回り)
bind -r N swap-pane -D
#ペインの移動(左回り)
bind -r P swap-pane -U
#ウィンドウの削除
bind X confirm-before -p 'kill-window #I? (y/n)' kill-window
#ペインを指定したウィンドウに移動
bind j command-prompt -1 'join-pane -ht :%%'

# ----------------------------------------
# Aliases
# ----------------------------------------
set -s command-alias[0] exit='kill-server'
set -s command-alias[1] only='kill-session -a'
set -s command-alias[2] d='detach-client'

# ----------------------------------------
# Status
# ----------------------------------------
set -g status on
set -g status-keys vi
#window-statusの位置を中央にする
set -g status-justify centre
setw -g window-status-format ' #I.#W:#{b:pane_current_path} '
setw -g window-status-current-format ' #I.<-> '

# 左右のステータスラインの最大長を指定
set -g status-left-length 40
set -g status-right-length 80

# 右のステータスライン
# 画面の横幅で表示項目を変える。149以下で項目を減らす。tput colsで現在の画面幅がわかる。
set -g status-right \
  "#{?#{==:1,#([[ $(echo #{client_width}) -ge 150 ]] && echo 1 || echo 0)},#(${DOTFILES}/tmux/right_status.sh),#(${DOTFILES}/tmux/right_status.sh short)}"

# ----------------------------------------
# Color
# ----------------------------------------
# statusの色
set -g status-style fg=default,bg=default
# message表示時のstatus lineの色。dispaly,confirm-before -p などのコマンド出力の色
set -g message-style fg=green,reverse,bg=default
# command-promptでviのノーマルモード時のstatus lineの色。
set -g message-command-style fg=default,bg=default
# コピーモードのときに、右上に出てくる表示の色
set -g mode-style fg=black,bg=blue

# paneを区切っている線の色
set -g pane-active-border-style fg=green,bg=default
set -g pane-border-style fg=default,bg=default

# setw -g window-status-current-format で指定した文字列の色
setw -g window-status-current-style fg='#8fa1b3',bold,bg=default
# setw -g window-status-format で指定した文字列の色
setw -g window-status-style fg='#535b66',bold,bg=default

# window自体の色。terminalの設定ファイルではなく、ここで色を設定する。
setw -g window-active-style fg='#c0c5ce',bg='#2b303b'
setw -g window-style fg='#c0c5ce',bg='#27292d'

# ----------------------------------------
# Copy Mode
# ----------------------------------------
#キーバインドをviにする
setw -g mode-keys vi
bind-key -T copy-mode-vi v send-keys -X begin-selection

# tmuxのbufferとxのclipboardを連携させる
if-shell 'type xsel' \
  "bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'xsel -ib'"
if-shell 'type xsel' \
  "bind-key -T copy-mode-vi Y send-keys -X copy-pipe 'xsel -ib'"
if-shell 'type xsel' \
  "bind ] run-shell \
    'xsel -ob | xargs -0 -I{} tmux set-buffer -- {} && tmux paste-buffer'"
```

## tmuxの右側のステータスライン用のスクリプト
```bash:tmux_right_status.sh
#!/usr/bin/env bash

set -euCo pipefail

function separator() {
  [[ $# -lt 1 ]] && return 1
  output --color-name $1 --string ''
}

function output() {
  [[ $# -lt 2 ]] && return 1

  local -rA colors=(
    ['black']='#[fg=black,bg=blue]'
    ['blue']='#[fg=blue,bg=black]'
    ['default']='#[default]'
  )

  for option in $@; do
    case ${option} in
      '-n'|'--color-name' )
        [[ -z $2 || $2 =~ ^-+ ]] && return 1
        local color=${colors[$2]}
        shift 2
      ;;
      '-c'|'--color-code' )
        [[ -z $2 || $2 =~ ^-+ ]] && return 1
        local color=$2
        shift 2
      ;;
      '-s'|'--string' )
        # -30dBm
        [[ -z $2 ]] && return 1
        local string=$2
        shift 2
      ;;
    esac
  done

  echo "${color} ${string}${colors['default']}"
}

# メモリ使用量
function memory() {
  local src
  src=$(free -h | sed '/^Mem:/!d;s/  */ /g' | cut -d' ' -f3)
  echo "$(separator 'blue')$(output -n 'black' -s ${src})"
}

# ロードアベレージ
function load_average() {
  local src cpus
  src=$(uptime | sed -E 's/.*load average: ([0-9]\.[0-9][0-9]).*/\1/g')
  cpus=$(grep 'processor' /proc/cpuinfo | wc -l)
  echo "$(separator 'black')$(output -n 'blue' -s ${src}/${cpus})"
}

# 電波強度
function network_level() {
  local -r interface='wlp4s0'

  [[ -n $(ip link show up dev ${interface}) ]] \
    && local -r signal=$(cat /proc/net/wireless \
      | tail -1 | tr -s ' ' | cut -d' ' -f4 | sed 's/\./dBm/') \
    || local -r signal='---'

  echo "$(separator 'blue')$(output -n 'black' -s ${signal})"
}

# 音量
function sound() {
  function get_volume() {
    local volume
    volume="$(pactl list sinks \
      | grep 'Volume' | grep -o '[0-9]*%' | head -1 | tr -d '%')"
    [[ ${volume} -gt 100 ]] && echo 100 || echo "${volume}"
  }

  function get_muted() {
    pactl list sinks \
      | grep 'Mute' | sed 's/[[:space:]]//g' | cut -d: -f2 | head -1
  }

  function to_blocks() {
    seq -f '%02g' -s '' 1 5 $1 | sed 's/.\{2\}/■/g'
  }

  function to_spaces() {
    seq -s '_' $1 5 100 | tr -d '[:digit:]'
  }

  function to_meters() {
    echo "[$(to_blocks $1)$(to_spaces $1)]"
  }

  type pactl &> /dev/null \
    || { echo "$(sep 'black')$(value 'blue' '×') "; return 1; }

  local -rA colors=(
    ['yes']='#[fg=colour237,bg=black]'
    ['no']='blue'
  )
  local -rA options=(
    ['yes']='--color-code'
    ['no']='--color-name'
  )
  local muted
  muted=$(get_muted)

  echo "$(separator 'black')$(output \
      ${options[${muted}]} ${colors[${muted}]} \
      -s $(to_meters $(get_volume)))" \
      | sed 's/_/ /g'
}

# 時刻
function hours_minutes() {
  echo "$(separator 'blue')$(output -n 'black' -s $(date +%H:%M))"
}

# バッテリー残量
function battery() {
  function online() {
    [[ $(cat /sys/class/power_supply/ADP1/online) != '1' ]] \
      && return
    local -ar icons=('' '' '' '' '')
    local index
    index=$(expr $(date +%S) % ${#icons[@]})
    output -n 'blue' -s ${icons[${index}]}
  }

  [[ -e '/sys/class/power_supply/BAT1' ]] \
    || { echo "$(separator 'black')$(online)"; return; }

  local charge
  charge=$(< /sys/class/power_supply/BAT1/capacity)

  if [[ ${charge} -gt 79 ]];then
    local -r color='#[fg=#08d137,bg=black]'
  elif [[ ${charge} -gt 20 ]];then
    local -r color='#[fg=#509de0,bg=black]'
  else
    local -r color='#[fg=#f73525,bg=black]'
  fi

  echo "$(separator 'black')$(online) $(output -c ${color} -s ${charge}%)"
}

function main() {
  if [[ $1 == 'short' ]];then
    echo -n "$(memory)$(load_average)"
  else
    echo -n "$(memory)$(load_average)$(network_level)$(sound)$(hours_minutes)$(battery)"
  fi
  echo '  '
}

main ${1:-long}
```

## tmuxの左側のステータスライン用のスクリプト
```zsh:.zshrc
function __tmux_status__() {
  # tmuxのSession番号を表示。commandがzshのときにはmodeも表示。

  [[ -z ${TMUX} ]] && return 1
  local -r separator=''
  [[ ${KEYMAP} == 'vicmd' ]] \
    && local -r \
      mode="#[fg=black,bg=green]#{?#{==:#{pane_current_command},zsh}, -- NORM -- #[default]#[fg=green]#[bg=blue]#{?client_prefix,#[bg=yellow],}${separator},}" \
    || local -r \
      mode="#[fg=blue,bg=black]#{?#{==:#{pane_current_command},zsh}, -- INS -- #[default]#[fg=black]#[bg=blue]#{?client_prefix,#[bg=yellow],}${separator},}"

  tmux set -g status-left "${mode}#[fg=black,bg=blue]#{?client_prefix,#[bg=yellow],} S/#S #[default]#[fg=blue]#{?client_prefix,#[fg=yellow],}${separator}"
}
zle -N zle-line-init __tmux_status__
zle -N zle-keymap-select __tmux_status__
```