仕事ですぐ役立つ Vim&Emacsエキスパート活用術 を買った

本屋に立ち寄ったら見かけたので購入。すっかり買うのを忘れていたけど、こういうことを思い出させてくれるのでリアル店舗は必要。

Emacs + evil-mode を使っている自分には夢のような本だと思った。

まだ読み始めたばかりだけどさっそく結城浩さんのコラムが役立った。最近秀丸エディタからVimに乗り換えてVimの挿入モードにEmacsキーバインドを組み込んだそうな。がっつり文字を打ち込むときは挿入モードでEmacsキーを使ってカーソル移動、文章を俯瞰して編集したいときはVimキーバインドでまとめて操作するらしい。

???あれ、それってまんまEmacs + evil-mode じゃないかと気がついた。今Vimに矯正中なので編集が終わったときは必ずEscを押してノーマルモードに戻っていたけどしばらく文字入力に集中したいときはそのままEmacsを使ってもいいんだなーと。(evil-modeではCtrl-zEmacs(<E>)とVim(<N>)の入力方式を簡単に切り替えできる)

そういえば昔Emacsでもview-modeのときだけvi風の操作系にするカスタマイズが流行ったりしてたなー。私自身もvim-regionというプラグインを作ったりしてた。

結城さんのコラムは他にも環境切り替えのときに頑張った様々な工夫の話が書いてあって面白かったです。evil-modeの新しい使い方を(何故かVimユーザーの方に)気づかせてもらいすでに買った甲斐がありました。おすすめです。

仕事の進め方

仕事というか、趣味で作っているソフトウェアやブログの更新、英語の勉強の話なのだけど。

最近ちょっと更新が止まっているので習慣行動を整理した方がいいなぁと思った。

反省として、何をするのかちゃんと明確にしてから始めようとする傾向が強くなりすぎたかなというのがある。やるときはちゃんと予定通りに進むのだけど反動でエネルギーに余裕のあるときしかできなくなっていた。で余った時間でスマホサーフィンしてしまう悪循環。(なんとなく本来作る予定だった時間に漫画読んだりゲームするのは気がひけるらしい、よくない)

改善方法として「とりあえず時間をとって何かする」という形にしてみようと思う。この記事もスマホアプリから段落構成も決めず書いている。疲れているときときはパフォーマンスが悪いことを自覚してちょっとだけやる。

とりあえず1日に30分〜1時間を目標にがんばってみよう。

はてなブックマークのTOP5をスマホがしゃべりはじめるPictRuby botを書いた

チェレンコフさんの書いたエビ中とLINEしてる気分になれるChatBotが面白かったので真似してはてなブックマーク版を作りました。はてなブックマークじゃなくてもRSSさえあればなんでもしゃべります。

使い方

総合とか世の中とか言うと各カテゴリ最新の5件を表示してくれます。

f:id:tuto0621:20160417174451p:plain:w462

最新の5件を表示したあと、1から5までの数字を入力すると記事の概要をしゃべり始めます。

f:id:tuto0621:20160417174508p:plain:w462

相槌を打つと続きをどんどんしゃべります。

f:id:tuto0621:20160417174535p:plain:w462

最後にそのURLが表示されるのでクリックするとそのページが表示されます。

f:id:tuto0621:20160417174549p:plain:w462

bmと入力することでそのページをはてなブックマークアプリで開いてくれます。(概要の紹介途中でもOK)

f:id:tuto0621:20160417174600g:plain:w462

カテゴリと入力するとことで入力可能なカテゴリ一覧が表示されます。

f:id:tuto0621:20160417174628p:plain:w462

文字だけでサクサク見れるのが快適です。

インストール

以下のソースコードPictRubyに貼り付けてください。get_sampleからja/hatena_bookmark_botでもインストールできます。

# Define "main" function or "Chat" class

class Chat
  def initialize
  end
  
  def welcome
    "カテゴリを入力 (例: 総合, 世の中)"
  end
  
  def call(input)
    url = RSS[input]
    
    if input == "カテゴリ"
      return RSS.keys.join("\n")
    end
    
    # argv = input.split(/\s+/)
    argv = input.split(" ")
    
    if argv[0] == "bm"
      url = argv[1] || @manuscript.last
      bookmark url
      return "#{url} をブックマーク"
    end

    if url
      feed = FeedReader.new url
      @items = feed.data['query']['results']['item']
      
      return sitemap
    end
    
    if (1..5).include? input.to_i
      @item = @items[input.to_i - 1]
      
      @manuscript = []
      @manuscript.push @item['title']
      
      description = @item['description']
      if description
        @manuscript.concat description.split("").map { |e| e + "" }
      end
      @manuscript.push @item['link']
      @i = 0
    
      txt = @manuscript[@i]
      @i += 1
      return txt
    end
    
    if @manuscript && @manuscript[@i]
      txt = @manuscript[@i]
      @i += 1
      return txt
    end
    
    "?"
  end
  
  private
  
  def sitemap
    i = 0

    @items.map do |e|
      i += 1
      "#{i} #{e['title']}"
    end.join("\n")
  end
  
  def bookmark(url)
    Browser.open "hatenabookmark:/entry?url=#{url}"
  end
end

class FeedReader
  attr_reader :data
  
  def initialize(url)
    feed = url
    count = 5
    
    url = "https://query.yahooapis.com/v1/public/yql?" + 
    URI.encode_www_form(
      q: "SELECT title,link,description,encoded FROM rss WHERE url=\"#{feed}\" | truncate(count=#{count})",
      format: "json"
      )

    @data = Browser.json url
  end
end

RSS = {
  "総合" => "http://b.hatena.ne.jp/hotentry.rss",
  "世の中" => "http://b.hatena.ne.jp/hotentry/social.rss",
  "政治と経済" => "http://b.hatena.ne.jp/hotentry/economics.rss",
  "暮らし" => "http://b.hatena.ne.jp/hotentry/life.rss",
  "学び" => "http://b.hatena.ne.jp/hotentry/knowledge.rss",
  "テクノロジー" => "http://b.hatena.ne.jp/hotentry/it.rss",
  "アニメとゲーム" => "http://b.hatena.ne.jp/hotentry/game.rss",
  "エンタメ" => "http://b.hatena.ne.jp/hotentry/entertainment.rss",
  "おもしろ" => "http://b.hatena.ne.jp/hotentry/fun.rss",
  "動画" => "http://b.hatena.ne.jp/video.rss",
}

おまけ

RSSだったらなんでも渡せるのでよく見るブログも追加できます。

RSS = {
  .
  .
  "前園" => "http://lineblog.me/maezonomasakiyo/index.rdf",
  "エビ中" => "http://lineblog.me/ebichu/index.rdf",
}

f:id:tuto0621:20160417174644p:plain:w462

余談

iTunes file sharing を使ってPCからPictRubyのコードを書く

PictRuby 0.5 から iTunes file sharing に対応したのでソースコードiOSとPC間で簡単にコピーできるようになった。

クリップボード共有とか色々試してみたけど今のところiPhoneをケーブルでつないでiTunes経由でやりとりするこの方法が今のところ一番しっくりきている。これで基本的な実装はiOS上で行い、リファクタリングやデータ打ち込みはPCで行う、とかできるようになった。

FireLinkに日付変数を正しく動かすPull Requestを取り込む

日付変数が正しく動作しない by Hi-lo · Pull Request #6 · ongaeshi/firelink

Firefox45で%date%が動かなくなったのに気がついていなかった。パッチありがたい。

Add-on SDKPythonベースのcfxじゃなくてnode.jsベースのjpmになった。たまにパッチを取り込むたびにやり方を忘れる(ので忘れないように今回はログに記録)。cfxの頃に比べるといちいち専用シェルにログイン不要になったので使いやすくなっている。

$ cd ~/Documents/firelink

# 実行テスト
$ jpm run

# xpiの作成
$ jpm xpi

パッチもうまく動き、さて新バージョン申請しようと思ったらなぜかFirefoxアカウントにログインできない。パスワードを忘れたをクリックしても同じように予期しないエラーが出る。

サインイン_Add-ons_の利用を続ける.jpg

ググるとサーバトラブルの可能性もあるっぽいので1日待ってみよう。

mrib.c のソースコードを読む

PictRubyのirbはローカル変数を保持することができない、しかしmruby同梱のmirbはできる。その理由を調べるためにソースコードを読むことにした。

mrubyの中に同梱されているのでgit cloneしてソースコードを効率的に読むための準備をする。(要はMilkodeへの登録とタグファイルの作成)

ファイル構造を読んでいくと、結局のところmrib.cの中に全て同梱されていることがわかった。さらに読んでいくと怪しいのは以下の558行目辺り。

mruby/mirb.c:549

// irb内の無限ループの一部です

        /* pass a proc for evaulation */
        /* evaluate the bytecode */
        result = mrb_vm_run(mrb,
            proc,
            mrb_top_self(mrb),
            stack_keep);                       // ここで前回実行時のstack_keepを渡している
        stack_keep = proc->body.irep->nlocals; // 今回の実行結果をstack_keepに保存
        /* did an exception occur? */

mruby-evalのソースを読むと以下のようになっている

mruby/eval.c:216

static mrb_value
f_eval(mrb_state *mrb, mrb_value self)
{
  char *s;
  mrb_int len;
  mrb_value binding = mrb_nil_value();
  char *file = NULL;
  mrb_int line = 1;
  mrb_value ret;
  struct RProc *proc;

  mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);

  proc = create_proc_from_string(mrb, s, len, binding, file, line);
  ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0); // mrb_vm_runの代わりにmrb_top_runを読んでいる、後はなんとなく似ている?
  if (mrb->exc) {
    mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
  }

  return ret;
}

mrb_top_run()は中でmrb_vm_run()を読んでいるのでまぁ便利関数だろう。またnlocalsは

/* Program data array struct */
typedef struct mrb_irep {
  uint16_t nlocals;        /* Number of local variables */
  uint16_t nregs;          /* Number of register variables */

当たりを引いたのではないだろうか。前回evalしたときのnlocalsを保持して次回実行時に渡すような流れを作ればうまくいきそうな予感。まずは実験してみよう。

iOS上のmrubyで正規表現を使えるようにする

ios-ruby-embedded (のfork)に iij/mruby-regexp-pcre を組み込んだらあっさりビルドが通った、万歳。