スニペット管理cgi書いた。

スニペットをどのように管理しているだろうか?
スニペットは個人的なものである。
セキュリティ的な事柄も書く。
なのでweb上でみんなで共有とかには向いていない。
私はスニペットをファイルで管理している。
テキスト形式で何かを書いてフォルダに突っ込んでいくのである。
原始的である。

$HOME/code/ruby/file
$HOME/code/ruby/dir
$HOME/code/ruby/fiber

こんなふうに。


原始的な方法を使うのは特定の仕組みに依存して痛い目に会いたくないからだ。
開発終了に翻弄されるのがいやだからだ。
clmemoはドコかに行ってしまった。紙copiもmacでは扱いづらい。
EvernoteもDropboxもいつ終わるか"わからない。"
Windowsも無くなるかもしれない。Appleは古いものをどんどん捨てていく
xxxはクソ便利!!とか吠えてもその仕組が使えなくなったら難民になってしまう。

だけれども

原始的な方法はやはり不便である。
私の方法においてはともかくgrepを掛けないといけない。
grepは絞り込みとかコマンドラインとかはめんどくさい。
勢い、作成したスニペットを検索することなくググッてしまう。
googleで新鮮な情報を得るのは大切ではある。
だが、シェルコマンドやmountのパラメータなど、いちいちググっていくのは正直ウザい。
なにか自分に進歩がない感覚もある。
「あー、まえもこのページみたなぁ」となるとがっかりする。
なので、この状況を打破すべくできるだけシンプルに書いた。

こうなった。

rubyを使ってフォルダのファイルをhtmlに起こす
rubyはglobしてreadして書きだすだけである。shellスクリプトで代用できそうだ。
htmlをjavascriptを使って検索する。
htmlの内容を正規表現で力づくで検索する。
ブラウザ搭載の検索でも十分なのでおまけだ。
2ファイルなので簡単だ。

#! /Users/seijiro/.rvm/rubies/ruby-1.9.2-p290/bin/ruby
# -*- coding:utf-8 -*-
require 'cgi/util'

config = {
  :dir => "/xxxxx/xxxx/code/ruby/*",
  :coding => "utf-8",
  :kind => ""
}
filedatas = { }
files = Dir.glob(config[:dir])
files.each do |f|
  next unless File.file?(f)
  File.open(f,"r:#{config[:coding]}") do |io|
    data = io.read
    filedatas[f] = data
  end
end

html = <<EOF
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Snippet Tool</title>
    <link rel="icon" href="http://ja.gravatar.com/userimage/14611836/d5caef2a5366cf647fc8fba3430e5854.png" type="image/png">
    <!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <!-- use prettify very slow!! 
    <link rel="stylesheet" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" type="text/css" media="screen" />
    <script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script>
-->
    <style>
    * { margin:0;padding:0; }
    h2 { margin-left:1em;
    }
    body {
      font-size:12px;
      line-height:12px;
    }
    pre.prettyprint {
      font-size:1.5em;
      line-height:1.3em;
      border-radius: 10px;
      -webkit-border-radius: 10px;
      -moz-border-radius: 10px;
      padding:1.0em;
      margin:1.5em;
      overflow:auto;
      background:darkblue;
      color:white;
      border:1px solid darkgreen;
    }
    header {
      left: 0;
      position: fixed;
      top: 0;
      width: 100%;
      z-index: 998;
      background:#bbb;
    }
    div .snippet:first {
      margin-top:2em;
    }
    </style>
    <script src="snippet.js"></script>
  </head>
  <body _onload="prettyPrint();">
<header>
<div style="float:left">
<h1 style="margin:0;padding:10px 10px 0px 10px">
#{config[:kind]} Snippet Tool
<span id="echoarea">(No Security!!!) regexp split by space.</span>
</h1>
</div>
<div style="float:right;margin:0;padding:10px 10px 10px 10px">
<form onsubmit="run();return false;">
<input type="text" id="query" value="">
<input type="button"  onclick="run();return false;" value="Search">
</form>
</div>
</header>
<div id="wrapper" style="margin-top:70px;">
EOF

filedatas.each do |k,v|
html << "<div class='snippet'>\n<h2>#{CGI.escapeHTML k}</h2>\n"
html << "<pre class='prettyprint lang-#{config[:kind]}'>#{CGI.escapeHTML v}</pre>\n</div>"
end
puts html
puts "</div></body></html>"
var divs     = [];//snippetを格納
var now_divs = [];//現在表示中のsnippetを格納
var prev_patterns_string = "";//検索語

// divを格納する
function pool(jqueryE){
    divs.push( jqueryE  );
}

// 正規表現検索
function is_div_contain_pattern(pattern,div){
    try{
        var re = new RegExp(pattern,"im");
        return re.test(div.innerHTML);//ハイライト等しないのでtestでよし
    } catch (x) {
        console.log(x);
        return false;
    }
}

// 検索実行
function search(pattern,trydivs){
    var ret_divs = [];
    for(var i=0;i < trydivs.length;i++){
        if(is_div_contain_pattern(pattern,trydivs[i])){
            ret_divs.push(trydivs[i]);
        }
    }
    return ret_divs;
}

// 全てのdivを表示
function display_all(){
    for(var i=0;i<divs.length;i++){
        divs[i].style.display = "block";
    }
}

// 検索をやり直すべきか否か
function detect_divs_reset(patterns_string){
    //かなりアホな管理だ
    if(patterns_string.length < prev_patterns_string.length
       || patterns_string.length != prev_patterns_string.length
      ){
          display_all();
          now_divs = divs;
      }
    prev_patterns_string = patterns_string;
    console.log("detect:"+ prev_patterns_string);
}

// 検索を管理する
function search_handler(patterns_string){
    detect_divs_reset(patterns_string);

    var patterns = patterns_string.split(' ');

    var recursive = function(patterns,divs){
        if(patterns.length < 1){
            return divs;
        }else{
            var pattern = patterns.shift();
            var ret_divs = search(pattern,divs);
            return recursive(patterns,ret_divs);
        }
    };

    var n_divs = recursive(patterns,now_divs);

    for(var i=0;i<now_divs.length;i++){
        now_divs[i].style.display = "none";
    }
    for(var i=0;i<n_divs.length;i++){
        n_divs[i].style.display = "block";
    }

    now_divs = n_divs;
    echo(now_divs.length);
}
// 検索件数を表示
function echo(string){ $('#echoarea').html("match:"+string);}

// formタグからからキックされる
function run(){
    var query_string = $('#query').val();
    search_handler(query_string);
}

// 初期化
$(function(){
      $('.snippet').each(function(){ pool(this); });
      now_divs = divs;
      //search_handler('概要 条件'); //test
  });

結果


案外良い感じだ。(macのフォントは素晴らしいなぁ!)
javascript検索機能が現状ともかく重い。現代のPCならなんとかなるレベル。
とてもじゃないがonkeyupにバインドしてインクリメンタルに検索することなどできないレベル。
下記にyasnippetのスニッペットファイルを使った稼働サンプルへのリンクを貼るので試してみてほしい。
気に入ったら使ってみてほしい。自己管理のwebサーバーがない人(そんな人いるのか?)はcronで適当な間隔でruby snippet.rb > snippet.htmlなどすれば良いと思う。読んで書くだけのrubyスクリプトなので負担は掛からないと思う。
今後、検索部分を最適化したりすれば気持ちよくなれるのではないだろうか?

http://modeverv.a.lisonal.com/snippet_tool/snippet.rb