<2015-10-01 Thu>スクリプトバグ修正・機能追加。

  • printfを使っていたため、入力を受け取りきれなかったバグを修正
  • 端末エミュレータを使っていても標準エラー出力を表示するように改良
  • 入力ファイルがある場合はfifoを使わないように改良
  • pecoのオプション解析をより厳密化するよう改良
  • --xterm オプションを追加

<2015-09-30 Wed>
peco 0.3.4でhit-a-hintが実装されたのでスクリプトを更新。
旧pecoはhttp://rubikitch.com/f/peco.old に保存。

今のEmacsならば定番となっているhelm(カテゴリ)
絞り込みにより候補選択を行う素晴しいインターフェースです。

それを端末の世界で行うのがpeco です。

percol というPythonによる同種のプログラムをGoに移植したものです。

https://github.com/peco/peco/releases/
からダウンロードしてください。

さすが並列処理が意識されたコンパイラ言語であるGoだけに、動作は高速です。

しかし、emacs上のshell/eshellを使っている人にとってはそのままでは使えません。

pecoは画面指向のプログラムなのでバッファによるシェルでは正しく動かないからです。

eshellでは画面指向のプログラムはM-x termで動かすよう設定できますが
pecoは標準入出力も使うのでそれではいけません。

そこで、Emacsのシェルのように本物の端末以外から起動する場合には
新しく端末エミュレータを立ち上げてその上でpecoを動かすラッパースクリプトを書きました。

その判別は環境変数TERMを見て判断しています。

スクリプトは同名なので、pecoバイナリよりもPATH優先度の高いディレクトリに
放り込んで実行可能属性をつければ、本物のpecoの代わりとして端末以外からも使えます。

pecoではなくてxpecoという名前で保存してもよいです。

標準入力を使う場合はmkfifoコマンドを使っているので、
Unix系OSでしか動かないと思います。

<2015-10-01 Thu> --xterm オプションを追加することで、
端末エミュレータのコマンドラインを細かく指定できるようにしました。

使用例

$ peco --xterm='urxvt -fn xft:Ricty-80' /proc/meminfo

これまでeshellからhelmをなんとか呼び出そうと画策してきましたが、
限界を感じたのでこれからはpecoを活用していこうと思います。

他にもページをスクロールした後に絞り込み結果が出てこないバグを報告したところ、
@lestrrat さんはすぐに修正してくれました。

素晴しいプログラム、そして素早い対応に心から感謝しています。

コード peco(以下のコードと同一)

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
# pecoのdumb端末対応化
## カスタマイズ定数
# pecoバイナリ
PECO = File.expand_path "~/gocode/bin/peco"
#PECO = File.expand_path "~/bin/peco040"
#PECO = File.expand_path "~/bin/peco036"
# 端末エミュレータ
XTERM = "urxvt"                 # or mlterm/xterm etc
# 端末エミュレータを使うTERM環境変数
TERMS_USE_XTERM = %w[dumb linux]

require 'tempfile'
require 'shellwords'
require 'optparse'

# 新しいコマンドライン
def peco_cmdline
  if input_file
    "#{PECO} #{ARGV.shelljoin}"
  else
    "#{PECO} #{ARGV.shelljoin} < #{new_input_file}"
  end
end

# xterm上でpecoを起動させる
def xterm_peco(xterm)
  out = Tempfile.new "peco-out"
  err = Tempfile.new "peco-err"
  system "#{xterm} -e sh -c '#{peco_cmdline} > #{out.path} 2> #{err.path}'"
  print out.read
  $stderr.print err.read
end

def input_file(argv=ARGV)
  argv = argv.clone
  until argv.empty?
    case argv.first
    when '-h', '--help', '--version', '--null'
      argv.shift
    when /^-/
      argv.shift
      argv.shift
    else
      return argv.first
    end
  end
  nil
end

# 入力をfifoに変換する
def new_input_file
  fifo = "/tmp/peco#$$.fifo"
  system "mkfifo", fifo
  at_exit { File.unlink fifo }
  Thread.start do
    open(fifo, "w") do |fifo|
      fifo.print while $stdin.gets
    end
  end
  fifo
end

conf = {:xterm => XTERM}
ARGV.each_with_index do |x, i|
  case x
  when "--xterm"
    conf[:xterm] = ARGV[i+1]
    ARGV[i..i+1] = nil
  when /^--xterm=(.+)$/
    conf[:xterm] = $1
    ARGV[i] = nil
  end
end
ARGV.compact!

if TERMS_USE_XTERM.include? ENV['TERM']
  xterm_peco conf[:xterm]
else
  exec PECO, *ARGV
end

本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。