開発ツール」カテゴリーアーカイブ

vimで保存時に不要な行末スペースを削除する

概要

.vimrcに以下のコマンドを追加すると、ファイル保存時に、不要な行末スペースを自動で削除してくれる。

autocmd BufWritePre * :%s/\s\+$//ge

解説

autocmdは、vimで特定のタイミングで何らかのコマンドを実行させるコマンド。

第一引数に指定している”BufWritePre”は、コマンド実行のタイミング指定で、ファイルを保存する直前を意味する。他にもファイル保存直後や、新規ファイル作成時などのトリガがある。

第二引数”“は、対象となるファイル名のパターンを表す。例えばtxtファイルでのみ適用したい場合、.txtと指定すればよい。今回は全てのファイルにおいて動作させたいので”*”を指定する。

第三引数には、実行するコマンドを記述する。

:%s/\s\+$//ge

を紐解くと、ファイル内の全ての/\s+$/を空文字で置換するという記述で、行末の手前にあるスペースを削除するという意味になる。

以上より、

autocmd BufWritePre * :%s/\s\+$//ge

とvimrcに記述することで、全てのファイル保存時に、ファイル内の不要な行末スペースを削除するという機能をvimに追加することができる。

参考

[vim] 挿入モードで検索ハイライトを無効にする

概要

検索時のハイライトはありがたいけど、挿入する頃には鬱陶しいので消したい。
:nohを適当にマッピングしてもいいがそれも面倒くさい。

挿入モード遷移時に:nohを実行する

autocmdのInsertEnterを使えば、挿入モード遷移時に任意のコマンドを実行させられるので、ここでnohを叩かせる。

autocmd InsertEnter * :noh

が、ダメ。動作しない。

Why

ヘルプを叩いてみた。

:noh[lsearch]           Stop the highlighting for the 'hlsearch' option.  It
                        is automatically turned back on when using a search
                        command, or setting the 'hlsearch' option.
                        This command doesn't work in an autocommand, because
                        the highlighting state is saved and restored when
                        executing autocommands |autocmd-searchpat|.
                        Same thing for when invoking a user function.

autocommandでは機能しないってハッキリ書いてあった。

代替策

しかたないので、挿入モード移行時にはハイライト機能を無効にし、挿入モードを終了したら有効に戻すというアプローチを取る。

autocmd InsertEnter * set nohlsearch
autocmd InsertLeave * set hlsearch

いい感じに動いた。

vimで矢印キーを使う癖を矯正(強制)する

vimのカーソル移動についつい矢印キーを使ってしまう人に、vimで矢印キーは絶対使っちゃイケないんだと思い込ませるための設定。

nnoremap <Up>    :q!<CR>
nnoremap <Down>  :q!<CR>
nnoremap <Left>  :q!<CR>
nnoremap <Right> :q!<CR>
inoremap <Up>    <ESC>:q!<CR>
inoremap <Down>  <ESC>:q!<CR>
inoremap <Left>  <ESC>:q!<CR>
inoremap <Right> <ESC>:q!<CR>

一応解説すると、ノーマルモード挿入モード問わず、矢印キーを叩いてしまうとファイルを保存せずに閉じるので、矢印キーを使わないカーソル操作を強制できる。恐怖で支配する的なアレ。

GoogleChrome拡張でチャットワークを使いやすくする

概要

業務でもプライベートでも、GoogleChromeでチャットワークを使用しているが、微妙に使いづらい部分があったり、使わない機能のUIが気になったりしていたので、Chromeの拡張機能でどうにか改善できないかと調査し、改善したお話。

標準の問題点

  • 何か全体的にUIの主張がデカイ
  • メッセージ記法の入力が面倒というか忘れる
  • タスク機能はメモレベルでしか使ってないので不要なUIが多い

Chrome拡張の導入

Chatwork Extension

Chatwork Extensionは、Chatwork全体をいい感じに拡張してくれるが、拡張内容のオン/オフの切替や、任意のスクリプト/スタイルの適用を行えるので実質ユーザの望むままのカスタマイズを行うことができる。

導入後のデフォルト設定だけで、主に以下のような拡張が行われる

ルーム一覧のスマート表示

ルーム一覧の縦幅が狭まりスマートに表示され、検索窓を用いてルームを検索することができる。

コードのシンタックスハイライト化

コードにシンプルな装飾をしてくれるので、チャットワークでコードを共有するレベルでなら充分。

@でToの入力をアシスト

Twitterに近い感覚で、@を入力すると対象ユーザの一覧が表示され、To指定を容易に行えるようになるので、マウスレスな宛先指定ができるようになる。

その他の機能

その他の機能についてはこちらを参照

カスタムスタイル

Chatwork Extensionでは、拡張のオプションから、任意のスタイルを設定することができる。

現状、チャットワークのタスク機能はメモ程度にしか使っていないので、タスク機能周りUIを始めとした、不要な要素を片っ端から非表示にするスタイルを適用した。適用したスタイルは以下の通り

/* チャットルームごとのタスク数を非表示 */
li.roomListBadges__taskBadge {
  display: none !important;
}

/* ヘッダーメニューのタスク一覧ボタンを非表示 */
li#_openTaskWindow {
  display: none !important;
}

/* ヘッダーメニューのチャットワークアシスタントボタンを非表示 */
div.globalHeaderAssistant {
  display: none !important;
}

/* チャット一覧のメニューを非表示 */
div#_chatFilterMenu {
  height: 25px !important;
}
div#_chatFilterMenu > div {
  display: none !important;
}
div#_chatFilterMenu > ul {
  display: none !important;
}
div#_chatFilterMenu > input  {
  width: 97% !important;
  position: static !important;
}

/*タスクの期限を非表示に*/
ul#_subRoomTaskList .taskListItem__meta {
  display: none !important;
}

/* 自分のタスクのみ表示 を非表示 */
div#_subRoomMyTaskList {
  display: none !important;
}

適用した結果、チャット一覧はよりスマートに

タスク一覧はただのメモとして表示されるようになった

ChatWork Quick Info Input

Chatwork Extensionとは別にもう一種、ChatWork Quick Info Inputも導入した。

こちらは地味ながらかなり大きな役割をもっており、入力ウィンドウに、title/info/codeのメッセージ記法を入力するためのUIを追加してくれる。

所感

  • チャットワーク用のChrome拡張は本記事で紹介した2種類の他にも様々ある。個々人のチャットワークの使い方や拘りに応じて柔軟にカスタマイズすると良さそう
  • Chatwork Extensionは、任意のスクリプトを仕込むこともできるので、やりようによっては凄いことできそう
  • Slackはもっとカスタマイズがしやすいとの噂が気になる

CentOS7にphpenv/php-buildを入れてPHPのバージョンを自在に切り替える

前提

以下の環境で動作確認

要素 バージョン
CentOS 7.3.1611
git 1.8.3.1
phpenv 1.1.1-2

概要

Dockerで構築したほぼ空っぽのCentOS7に対し、各種パッケージをインストール後、phpenv/php-buildをインストールし、PHPのバージョンを自在に切り替えたお話。PHPのバージョンを上げたり下げたりすることを容易に行うことが目的。

本記事ではほぼ空っぽのCentOS7を利用しているので、色々な基本となるパッケージの導入も行ってるが、多くの環境では既に入っているものが殆どなので必要に応じてインストールする形で問題ない。

phpenv/php-buildとは

phpenv: PHPの複数のバージョンを一元管理し、バージョンの切り替えを容易に行うためのツール
php-build: phpenvで使用するPHPのインストールツール

下準備

ツールはGithubで公開されてるのでgitが必要。ファイルの書き換えなどでvimを使う。パッケージのダウンロードにwgetを使う。

$ yum install -y git vim wget

CentOS7の標準リポジトリだけだと足りないので、EPELを追加する。

$ yum install epel-release

EPELリポジトリを有効にする(デフォルトで有効になってる?)

$ vim /etc/yum.repos.d/epel.repo
enabled=1

PHPに依存するパッケージ、コンパイルに必要パッケージなどなどを纏めてドン(元々入ってるものが多数、不要なものもあるかも)
PHPのバージョンによっては依存モジュールが異なる場合がある模様。その都度エラーメッセージが出るのでインストールすればよい。

$ yum install -y yum install gcc bison libxml2 libxml2-devel openssl-devel libcurl-devel libjpeg-turbo-devel libpng-devel libmcrypt-devel readline-devel libtidy-devel libxslt-devel bzip2 bzip2-devel libicu-devel  gcc-c++ tidyp make re2c file libtool-ltdl-devel autoconf automake patch mysql-devel

phpenvのインストール

Githubから本体を落とす。/usr/srcあたりに置くのが定石なのでそこでclone

$ cd /usr/src
$ git clone https://github.com/CHH/phpenv.git

本体の中にインストールスクリプトが含まれているのでそれを実行する

$ cd phpenv/bin/
$ ./phpenv-install.sh
Installing phpenv in /root/.phpenv
remote: Counting objects: 2620, done.
remote: Total 2620 (delta 0), reused 0 (delta 0), pack-reused 2620
Receiving objects: 100% (2620/2620), 491.71 KiB | 368.00 KiB/s, done.
Resolving deltas: 100% (1640/1640), done.
Success.

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

Add above line at the end of your ~/.bashrc and restart your shell to use phpenv.

これでphpenvは使えるようになったが、コマンドですぐに利用できるようにパスを通す。
以下をbashrcなりに追記する

export PATH="/root/.phpenv/bin:$PATH"
eval "$(phpenv init -)"

bashrcを再読込

$ source ~/.bashrc

phpenvが読めることを確認

$ phpenv --version
rbenv 1.1.1-2-g615f844

php-buildのインストール

PHPをインストールするためのphp-buildを取得し、phpenvのプラグインディレクトリに配置する。こちらもGithubから取得できるので以下の手順でCone

$ git clone https://github.com/CHH/php-build.git ~/.phpenv/plugins/php-build

以下コマンドでインストール可能なPHPの一覧が出てくるので、これが出ればOK

$ phpenv install --list
(中略)
  7.1.0
  7.1.1
  7.1.2
  7.1.3
  7.1.4
  7.1.5
  7.1.6
  7.1.7
  7.1.8
  7.1snapshot
  7.2.0beta1
  7.2.0beta2
  7.2snapshot
  master

最新のPHPをインストールする

前項でインストール可能なPHPの一覧を確認した所、開発段階の7.2までインストール可能になっているが、今回は安定版の最新版である7.1.8をインストールする。

インストール時間が長い。その上インストールログがあんまり出ないので固まったとも思ってしまうが、気長に待てば終わるか、エラーメッセージがちゃんと出るので待機する。(本環境では11分程度)

$ phpenv install 7.1.8
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 7.1.8 into /root/.phpenv/versions/7.1.8
[Downloading]: https://secure.php.net/distributions/php-7.1.8.tar.bz2
[Preparing]: /tmp/php-build/source/7.1.8
[Compiling]: /tmp/php-build/source/7.1.8
[xdebug]: Installing version 2.5.5
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.5.5
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/7.1.8/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: Enabling Opcache...
[Info]: Done
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.7.1.8.20170815015550.log
[Success]: Built 7.1.8 successfully.

PHP7.1.8がインストールできたので、この環境で利用するデフォルトのPHPを7.1.8に設定する

$ phpenv global 7.1.8

PHP7.1.8が利用できる状態になったことが確認できる

$ php -v
PHP 7.1.8 (cli) (built: Aug 15 2017 02:06:59) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.8, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans

古いPHPをインストールし、切り替える

開発内容によっては意図的に古いPHPを使用せざるを得ない場面もある。そんな場合にphpenvは効果的。ここではPHP5.2.17(デフォルトでインストールできる最も古いPHP)をインストールする。

$ phpenv install 5.2.17
[Info]: Loaded extension plugin
[Info]: Loaded apc Plugin.
[Info]: Loaded composer Plugin.
[Info]: Loaded github Plugin.
[Info]: Loaded uprofiler Plugin.
[Info]: Loaded xdebug Plugin.
[Info]: Loaded xhprof Plugin.
[Info]: Loaded zendopcache Plugin.
[Info]: php.ini-production gets used as php.ini
[Info]: Building 5.2.17 into /root/.phpenv/versions/5.2.17
[Downloading]: https://git.php.net/?p=web/php-distributions.git;a=blob;f=php-5.2.17.tar.bz2;h=90d654f7b9c60320f6228194ed1d2f12976a3a59;hb=bed93a30bbc2104f6e88e6ef142891a4c289502f
[Info]: Applying patches: /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/gmp.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/xp_ssl.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/zip_direct.c.patch /root/.phpenv/plugins/php-build/bin/../share/php-build/patches/php-5.4.6-libxml2-2.9.patch
[Preparing]: /tmp/php-build/source/5.2.17
[Compiling]: /tmp/php-build/source/5.2.17
[xdebug]: Installing version 2.2.7
[xdebug]: Compiling xdebug in /tmp/php-build/source/xdebug-2.2.7
[xdebug]: Installing xdebug configuration in /root/.phpenv/versions/5.2.17/etc/conf.d/xdebug.ini
[xdebug]: Cleaning up.
[Info]: The Log File is not empty, but the Build did not fail. Maybe just warnings got logged. You can review the log in /tmp/php-build.5.2.17.20170815021852.log
[Success]: Built 5.2.17 successfully.

versionsコマンドを叩くと、以下のようにインストール済みのPHP一覧と、デフォルトに設定されたバージョン(7.1.8)が確認できる

$ phpenv versions
  5.2.17
* 7.1.8 (set by /root/.phpenv/version)

globalコマンドでバージョンを切り替えると

$ phpenv global 5.2.17

デフォルトが変わり

$ phpenv versions
* 5.2.17 (set by /root/.phpenv/version)
  7.1.8

5.2.17が利用できる状態になる

$ php --version
PHP 5.2.17 (cli) (built: Aug 15 2017 02:23:16)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.2.7, Copyright (c) 2002-2015, by Derick Rethans

備考

phpenv installで、以下のようなエラーが出て

-----------------
|  BUILD ERROR  |
-----------------

Here are the last 10 lines from the log:

何らかの対応を行った後は、中途半端にデータが残っている可能性のある下記ディレクトリを削除しておいたほうが無難な模様

$ rm /tmp/php-build -rf

所感

  • これまでrbenv(ruby),pyenv(python),nodenv(node)を使ってきたが、それ比べて導入に一番苦労した。依存が多すぎる。
  • 同様に一番インストールにも時間がかかる。コンパイルが長すぎる
  • いくつかのバージョンを一度入れてしまえば後は他の言語と同様なので容易
  • apacheとか関連モジュールは結局バージョンに依存してしまうからあんまり効果的でないのかも
  • 他言語含めて、anyenvというのを使えばまとめて出来たりするらしい。今度調べたい。

はじめてのvimscriptとvimプラグインの作成

前提

以下の環境で作業を行った

要素 バージョン
debian 8.6
vim 7.4
neobundle 7.2.051
git 2.11.0

概要

vimは長らく使ってきたけどvimscriptを書いたことがない私が、vimscriptの基本を勉強し、簡単なvimプラグインを作成、NeoBundleでインストールできる状態にしたお話。

Vim scriptとは

Vim上で動作するスクリプトのこと。

:set filetype=html

のような、コマンドモードで”:”から始まるコマンドの集合。

最も身近なvimscriptは.vimrcで、これはvim起動時に読み込まれるvimscriptである。
つまり、vimscriptの書き方を知っていれば、より効果的なvimrcを記述することができる。

Vim script入門

vim scriptの入門は、以下を参考にした。
Vim script と vimrc の正しい書き方

本記事は簡単に基本文法を紹介する程度にするので、具体的に学びたい場合は各種参考ページを利用すること。

実行方法

vimscriptは基本的にscript.vimのように、拡張子をvimにしてファイルを作成する。
vimファイルはvimコマンドから実行することもできるが、多分以下のように、スクリプトをvimで編集中にsourceコマンドを叩くのが一番早い。

変数と代入

vim scriptには変数宣言の概念はなく、変数への代入と同時に変数が作成される模様。
let で変数への代入を行う。echoを見てもわかるように、シェルスクリプトに似ていると感じる。

let number = 1
let string = "hoge"
let array  = [1,2,3,4,5]

echo number
echo string
echo array

実行結果

1
hoge
[1, 2, 3, 4, 5]

条件分岐

ifとelseifとendifを使う。これもシェルスクリプトと同じ。

let number = 1
if number == 1
  echo "number is 1"
elseif number == 2
  echo "number is 2"
else
  echo "other"
endif

繰り返し

for文とwhile文が使える。while文の中でletを用いて変数を再宣言しているように見えるが、vim scriptにおけるletは代入のコマンドなので代入する場合は必ず必要。

for i in [1,2,3]
  echo i
endfor

let n = 0
while n < 10
  echo n
  let n += 1
endwhile

関数

関数もシェルスクリプトと同じようだが、気をつけたい所が2点。

まず、function! と”!”をつけることで、同名の関数が既にロード済みの場合上書きすることができる。こうしないと、同じバッファ内で複数回同じスクリプトを読んだ時に、同名の関数が既に定義済みだよとエラーが出るので”!”を付ける必要がある。

次に、関数の引数であるnumberを参照するために”a:”を頭につけている。これは変数のスコープの指定を表し、aとつけることでnumberは関数内にあるnumberであることを表す。aの他に、グローバルスコープ用の接頭辞やスクリプトファイル内スコープ用の接頭辞があるので、必要に応じて書き換える必要がある。

function! Double(number)
  return a:number * 2
endfunction

let result = Double(10)
echo Double(10)

vimプラグインの作成

本記事では、簡単すぎず難しくもないシンプルなvimプラグインを実装する。

今回作成するプラグインは、:RandDateTimeと入力することで、1980年から、2017年までの範囲内のランダムな日付を、Y-M-D H:m:sの形式でカーソル位置に挿入するコマンドを利用できるようにするプラグインで、まったくもって実用性はないが、個人的にもう少し改良して実用的なものにする予定。

ディレクトリ構成

作成するプラグイン名をmy-vim-pluginとした場合、同名のディレクトリを作成し、その中にpluginというディレクトリを作成、さらにその下にvim scriptを記述する。my-vim-plugin直下にはREADMEを入れたりするみたいだが、今回はスクリプトファイル一つで完結させるので、ディレクトリ構成は下記のようになる。

vagrant$ tree my-vim-plugin/
my-vim-plugin/
└── plugin
    └── script.vim

1 directory, 1 file

乱数を生成する関数

まず、ランダムな日付を生成するために、指定した範囲内での数値をランダムに戻す関数を実装する。
残念ながら、vim scriptには乱数を生成するための仕組みは存在しない。とは言え、今回は時刻データを用いて擬似的な乱数を生成することにした。

作成した関数は以下の通り。GetRandom関数は、minとmaxを引数にとり、min <= n <= max の範囲のランダムな整数を戻す。
reltime関数は、現在のUNIXTIMEを秒とマイクロ秒の配列で戻す。配列の二番目の要素がマイクロ秒になるので、その数値を使って擬似乱数を生成している。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

ランダムな日付を取得する関数

前項で作成したGetRandom関数を用いて、1980年から2017年までのランダムな日付文字列を戻す、GetRandomDate関数を以下のように実装する。なお、今回は実装の簡略化のため、日は1日から28日までの確実に存在する日付のみを対象とするようにした。

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

ランダムな日付時刻を取得する関数

同様に、前項で作成したGetRandomDate関数を用いて、ランダムな時刻までを含めて戻すGetRandomDateTime関数を以下のように実装する

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

ランダムな日付時刻をカーソル位置に挿入する関数

前項で作成したGetRandomDateTime関数は、ランダム日付時刻を取得するだけなので、新たにInsertRandomDateTime関数を以下のように実装する。
executeコマンドは、以降の任意のコマンドをvim上で実行するためのコマンド。:normalは以降のコマンドをノーマルモードで実行するコマンド。aでカーソルの次の位置から挿入モードを開始、そしてdatetimeを入力するという手はずになる。

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

作成した関数を呼び出すコマンドを定義

ここまででランダム日付時刻文字列を挿入する仕組みができあがったので、それを呼び出すためのコマンドを定義する。コマンドの定義にはcomman関数を用い、コマンド名と実行内容を引数に設定する。(callは、指定した関数を実行するコマンド)

command! RandDateTime call InsertRandomDateTime()

動作確認

以上で、以下のような自作のvimプラグインが完成した。

function! GetRandom(min, max)
  return reltime()[1] % (a:max - a:min + 1) + a:min
endfunction

function! GetRandomDate()
  return GetRandom(1980, 2017) . '-' . GetRandom(1, 12) . '-' . GetRandom(1, 28)
endfunction

function! GetRandomDateTime()
  return GetRandomDate() . ' ' . GetRandom(0, 23) . ':' . GetRandom(0, 59) . ':' . GetRandom(0, 59)
endfunction

function! InsertRandomDateTime()
  let datetime = GetRandomDateTime()
  execute ":normal a" . datetime
endfunction

command! RandDateTime call InsertRandomDateTime()

これをsourceコマンドで読み込むことで、現在開いているバッファ上でRandDateTimeコマンドが利用できるようになる。

NeoBundleでインストールできるようにする

NeoBundleはvimプラグインを管理するためのツールで、決まったディレクトリ構成でGithub上にVimプラグインを公開すれば、誰でも手軽にそれをインストールできるようになる。

今回は既にNeoBundleを想定したディレクトリ構成にしているので、これをこのままGithub上にあげる。
Sa2Knight/my-vim-plugin

この状態で、既にNeoBundleが利用できる状態のvimrcに以下を追加

NeoBundle "Sa2Knight/my-vim-plugin"

vimを起動すると、以下のようにインストールするかの確認が出るので、Yを入力する

Not installed bundles:  ['my-vim-plugin']
Press ENTER or type command to continue
Install bundles now?
(y)es, [N]o:

Github経由で先程作成した自作プラグインがインストールされ、次回起動時から自動で:RandDateTimeコマンドが使えるようになる。

所感

vimscript及びvimプラグインの非常に基礎的な部分にようやく手を付けられたので、今後もっと深い部分を勉強して実用的なプラグインを作りたい。とりあえず今回作ったプラグインには以下のような改良点があるので、それを実装しつつノウハウを見つけていく。

  • ランダムの範囲を外から指定できるようにする
  • 日付時刻文字列のフォーマットを外から指定できるようにする
  • 乱数値が一桁になった場合0で詰めるようにする
  • コマンドを叩かずともキーバインドですぐに実行できるようにする