3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

高速でお手軽なソースコード検索スクリプト

Posted at

巨大なライブラリを利用したプロジェクトで、問題に遭遇したり、ライブラリを把握したいと思った際に、高速なソースコード検索ツールが欲しくなります。

Visual Studio の検索は遅すぎるし、gtags なども使ったみたが、エラー文字列の一部から検索したかったりする場合もあり、なかなか満足するものに出会えなかった。

そんな時に この記事 に出会った。とてもありがたい。

とても良かったが、ファイルを開く前に周辺コードが読みたかったのと、そこから Visual Studio で開いてブレークポイントを張ったりなど、複数のキーバインドを設定したかった。

このアイデアを元に fzf のプレビューとキーバインドを利用して以下のものが実現できた。

利用ツールのインストール

ご利用のパッケージマネージャーなどで、 fzf ripgrep をインストールします。

保守的なディストリビューションでインストールされる fzf だとバージョンが古く一部の機能が使えなかったりします。

スクリプト

スクリプトは全て zsh で書いています。

メイン部分

g
main() {
	exec rg --line-number --ignore-case "$@" . 2>/dev/null | fzf -e --multi --no-sort --exit-0 --reverse --prompt="$* > " \
		--preview 'exec g --preview {}' \
		--preview-window up \
		--bind 'enter:execute(g --open less {})' \
		--bind 'ctrl-o:execute(g --open nvim {})' \
		--bind 'ctrl-v:execute-silent(g --open code {})' \
		--bind 'ctrl-t:execute-silent(g --open vs {})' \
		--bind 'ctrl-y:execute-silent(echo {} | clipboard -i)'
}

ripgrep でファイルを探索・展開し fzf に食わせる。
スクリプトの引数を ripgrep に与えることで対象ファイルのフィルタ等を行う。
e.g.) g -tcpp g -g '*scl.csv'

--preview プレビュー機能
--bind キーバインドを設定
execute-silient は比較的新しいバージョンでないと使えない模様。

clipboard はプラットフォーム毎のクリップボードの扱いを吸収する自前スクリプト。
pbcopy xclip などに置き換えてください。

{}FILEPATH:LINENUMBER:該当行の文字列 に展開される。

プレビュー部分

g
preview() {
	local lines=$(( `tput lines` / 2 - 2 ))
	exec cat <<EOF | python3
import subprocess
window_lines = ${lines}
filename, linenumber, vars = '''$* '''.split(':', 2)
linenumber = int(linenumber) - 1
p = subprocess.run(['src-highlight.sh', filename], stdout=subprocess.PIPE, shell=False)
p.check_returncode()

context = p.stdout.decode('utf-8').replace('\r\n', '\n').split('\n')
contextlines = len(context)
headline = max(0, linenumber - (window_lines - 1) // 2)
tailline = min(contextlines, window_lines - (linenumber - headline) + linenumber)
headline = headline - max(0, min(window_lines, contextlines) - (tailline - headline))

out = context[headline:linenumber]
out.append(u'\u001b[38;5;253;48;5;163;01m' + vars.rstrip())
out[linenumber + 1:] = context[linenumber + 1:tailline]
print ('\n'.join(out).replace('\t', '    '))
EOF
}
  1. ソースハイライトにかける
  2. 該当行の周辺コードを取得
  3. 該当行のスタイルを変更

プレビュー表示を作る部分は高級言語のほうがやりやすいので python を呼び出している。
src-highlight.sh はソースハイライトのスクリプト。
source-highligherpygments などお好みのものをご利用ください。
プレビューウィンドウを上下に分割しているので、ターミナルの行数を2で割ったものを元に周辺コードを拾っている。

ファイル開く部分

g
open() {
	local cmd=$1
	shift

	local args=(`echo $@ | awk -F : '{ print $1 " " $2 }'`)
	local filename=${args[1]}
	local linenumber=${args[2]}

	case "$cmd" in
	vs)
		if [ -d /mnt/c/Windows ]; then
			(cmd.exe /c `wslpath -aw /path/to/openfile.vbs` `wslpath -aw $filename` $linenumber) &
		fi
		;;
	code)
		if [ -d /mnt/c/Windows ]; then
			code --reuse-window --goto "`wslpath -aw $filename`:$linenumber"
		else
			code --reuse-window --goto "$filename:$linenumber"
		fi
		;;
	*)
		exec $cmd "+${linenumber}" $filename
		;;
	esac
}

WSL から Windows プログラムを開く場合、ファイルパスは wslpath コマンドを利用して Windowsファイルシステムのパスに変換して渡す必要がある。
WSL の判定は uname -a | grep Microsoft でもいいかもしれない。

openfile.vbs は Visual Studio でファイルを開くスクリプト。

スクリプト全体

g
# !/usr/bin/env zsh

main() {
	...
}

preview() {
	...
}

open() {
	...
}

case "$1" in
	--preview)
		shift
		preview "$@"
		;;
	--open)
		shift
		open "$@"
		;;
	*)
		main "$@"
		;;
esac

.ignore

ripgrep は .ignore を読んでくれるので、書いておくと無駄な探索が省けて高速化できる。

Happy

ソースコードだけでなく、設定ファイル群など、テキストベースの様々なものが高速に検索できるようになって、大幅に生産性が向上し、いっぱいサボれるようになった。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?