2018年10月1日月曜日

peco/fzfを使って軽量ランチャーを作ってみた

常駐なしでコマンドをpeco/fzfで部分一致検索できる軽量ランチャーを作ってみた。

これまでAlbertランチャーを使っていたのだけど常駐する必要もない気がしてきて、何となくランチャーを作ってみたら一瞬で作れてしまった。 似たようなコンセプトでRofiという良い感じのランチャーもあるのだけど、作ってしまったものは仕方ないので紹介してみる。

peco/fzfとRofiそのものの最大の違いは執筆時点では複数選択できるかどうかと思うけど、ランチャーでは複数選択する必要性があまりないので、正直Rofiでも良かったかも知れない。 まあRofiよりWindowsに応用しやすい点は利点かなあ。


作り方: まず以下のコードを ~/bin/launch などの実行しやすいディレクトリに配置する。 launchコマンドを実行するとpeco/fzfを使ってコマンドを検索、実行できる。
特に複雑な事は何もしていないけど、コマンドのリストをキャッシュして高速化している。 apt-getなどでコマンドをインストールしてキャッシュを更新したい場合は launch update で更新できる。 何もしないでも1日間隔でキャッシュは更新する。
#!/usr/bin/env ruby

update_freq = 24 * 60 * 60  # 1day
cache = ENV['HOME'] + '/bin/launch.lst'

def update(filepath)
  t = Time.now
  open(filepath, 'w') {|f|
    f.write `bash -ic "compgen -ac"`
  }
  puts "cache are updated! (#{Time.now - t}sec.)"
end


if ARGV[0] == 'update'
  update(cache)
  exit
elsif !File.exist?(cache)
  update(cache)
elsif File.ctime(cache) + update_freq < Time.now()
  update(cache)
end

# app = `cat #{cache} | peco --select-1`
app = `cat #{cache} | fzf --select-1`
`lnch #{app}`

最終行のlnchコマンドは存在しないため、次にこのコマンドを ~/bin/lnch などのパスが通ったディレクトリに登録する必要がある。 lnchコマンドは外部コマンドを実行する時に子プロセスを停止しないような実行を行う。 RubyやPythonではたぶんこれができない。 なので仕方なく以下のようなコードをGo言語で書いてビルドする必要がある。 実はこのテクニック、このサイトで紹介されていたもの。この方もfzfをランチャー化したいという事で、よく似た事をやっている。
package main
import (
  "fmt"
  "log"
  "os"
  "os/exec"
  "syscall"
)

func main() {
  if len(os.Args) <= 1 {
    fmt.Println("Usage: lnch  ")
    os.Exit(1)
  }
  cmd := exec.Command(os.Args[1], os.Args[2:]...)

  // this is the important part to avoid killing the child process instantly
  cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

  err := cmd.Start()
  if err != nil {
    log.Fatal(err)
  }
}

あとは何らかの方法でlaunchコマンドをGUI呼び出しすればランチャーになる。 実はこれ、xfce4-terminalを使うと秒速で完成する。 xfce4-terminalには様々なウィンドウオプションがあるので簡単に格好良い表示ができる。 例えば以下のような感じで呼び出す。
xfce4-terminal --drop-down \
--hide-menubar --hide-borders --hide-toolbar --hide-scrollbar \
-x launch

あとはこれを何らかの方法でキーボード呼び出しすれば、キーボードランチャーの完成となります。 Lubuntuなら ~/.config/openbox/lubuntu-rc.xml でキーバインドを設定できるので、 以下をkeybindタグがある辺りに追加すると、Win+Spaceでランチャーを起動できる。 設定は openbox --reconfigure で反映できます。
<keybind key="W-space">
  <action name="Execute">
    <command>xfce4-terminal --drop-down --hide-menubar --hide-borders --hide-toolbar --hide-scrollbar -e "bash -ic launch"</command>
  </action>
</keybind>

実行画面は以下のような感じ。キャッシュを利用すれば起動時のラグはほぼゼロでストレスもない。



現時点で作り込みの甘さからRofiに劣っているところは、過去の起動履歴から検索候補順を並び替えたりしていないところ。でもそれくらいかなあ。たった数十行でそれくらいしか課題がないなら結構凄いかなと。 xfce4-terminalを応用すると誰でも簡単にドロップダウンメニューを作れるので、ランチャー以外にも応用できそう。

0 件のコメント: