DDSKKの日本語入力でC-iをまともに使いたい

DDSKK を使用していて困っていたのが,「かなモード」や「カナモード」のときに transient-mark-mode が有効なときの C-i が indent-resion にならないことだ.TAB キーでは上手くいくのに,C-i だとなぜかリージョン範囲が削除される.

define-key などで調べるとタブが insert 扱いになっているせいだとわかった.アルファベットキーで試せばわかるように,insert は transient-mark-mode 有効時にリージョンを削除して文字を挿入する.ということで,特定のキーに強制的にコマンドを割り当てる方法 - (rubikitch loves (Emacs Ruby CUI Books)) で強制的に割り当てようとしたが,上手くいかない.どうやら skk-j-mode-map の方が overriding-minor-mode より優先度が高いらしい.

そこで .emacsskk-j-mode-map にキーを定義してみたが,なんと skk-j-mode-on するたびに勝手にキーマップがリセットされる.

仕方無く skk.el を読んでみたところ,関数 skk-setup-j-mode-map-options で skk-try-completion-char (初期設定では "\t") が skk-insert に設定されていることが,C-i が insert となる原因だと突き止めた.TAB キーはその後に skk-comp-wrapper が割り当てられているため,C-i と挙動が異なっているというわけだ.

この問題を解決するには,C-i にも skk-comp-wrapper を割り当てる.以下のコードを .emacs に書けばOK.

(defadvice skk-setup-j-mode-map-options (after c-i-comp-wrapper2 activate)
  (define-key skk-j-mode-map (kbd "C-i") #'skk-comp-wrapper))

C-i に skk-comp-wrapper を割り当てるコードを,第1引数が after の defadvice によって関数 skk-setup-j-mode-map-options の末尾に追加するコードとなる.

とりあえずこれで DDSKK 使用時の大きな問題が1つ解決した.しかし,DDSKK は様々な重要キーを奪っていくのを本当にどうにかしてほしいものだ.C-j で return-indent できなかったり,ReadOnly ファイルで C-p を入力してもカーソルを移動できなかったり…….DDSKK 常用の道は険しい.

anything-for-filesにrecentfを全て表示したい

recentf-max-saved-items を変更しても,anything-for-files で表示される recentf が増えないのが嫌だったので anything-c-source-recentf を書き換えた.やることは anything-for-filesでlocateを有効にすると表示直後に移動したカーソルが一番上に戻される問題 - 勉強日記 とほぼ同じ.

;;; recentf の表示数を 100 まで拡張
(setq recentf-max-saved-items 100)

(setq anything-c-source-recentf
      `((name . "Recentf")
        (init . (lambda ()
                  (require 'recentf)
                  (or recentf-mode (recentf-mode 1))))
        (disable-shortcuts)
        (candidates . recentf-list)
        (keymap . ,anything-generic-files-map)
        (help-message . anything-generic-file-help-message)
        (candidate-number-limit . ,recentf-max-saved-items) ; 標準定義にこれを追加した
        (mode-line . anything-generic-file-mode-line-string)
        (match anything-c-match-on-basename)
        (type . file)))

注意することは,recentf-max-saved-items への代入を先に行うことと,recentf-max-saved-items の前にカンマを置くこと.クォートの代わりにバッククォートを使用すると,カンマ直後の変数を展開してくれる.

anything-for-filesでlocateを有効にすると表示直後に移動したカーソルが一番上に戻される問題

locate の表示数が多すぎるのがよくないらしい.ということで .emacs に以下のコードを追加して anything-c-locate で表示される項目数を限定.

;;; anything-locate の表示数を50に限定
(setq anything-c-source-locate
      `((name . "Locate")
        (candidates . anything-c-locate-init)
        (type . file)
        (requires-pattern . 3)
        (keymap . ,anything-generic-files-map)
        (help-message . anything-generic-file-help-message)
        (candidate-number-limit . 50)   ; ここで表示数を決定
        (mode-line . anything-generic-file-mode-line-string)
        (delayed)))

できるだけデフォルトの anything-config.el を書き換えたくないので,anything-c-source-locate を上書きして無理矢理解決した.これを書くと anything-locate も 50 個しか表示されなくなってしまう.

もっと良い解決方法があるのかもしれないけど,まあ応急処置ということで.

anything-for-filesでlocateが動かない問題について

anything インストール済み Emacs を起動してすぐに anything-for-files を起動しても,locate が動かない.anything-for-files には anything-c-source-locate が含まれているはずなのに…….

原因は,anything-c-locate-command が初期状態では nil になっているからのようだ.そのため,これに適切な代入を行う anything-locate を実行後は anything-for-files で locate が有効になる.

anything-locate 実行時に anything-c-locate-command には以下のような代入が行われているようだ.

(setq anything-c-locate-command
      (case system-type
        ('gnu/linux "locate -i -r %s")
        ('berkeley-unix "locate -i %s")
        ('windows-nt "es -i -r %s")
        (t "locate %s")))

.emacs にこれを書けば解決する.マシンが決まっているのであれば以下で十分(私のUbuntu環境の例).

(setq anything-c-locate-command "locate -i -r %s")

Emacsの単語をブラウザで検索

Emacs でコードとか読んでると Google で検索したくなることはよくあるので,方法をいろいろ調べてみた.

anything-c-source-surfraw

1つが anything-c-source-surfraw.anything をインストールしている Emacs なら,sudo apt-get install surfraw で surfraw をインストールして,M-x anything-surfraw で使える.

試してみたが,複雑な検索を簡単に行うことはできても,検索までにいろいろ入力が必要で,いつも Google で検索する私にはちょい面倒.
あと,別のサーチエンジンを追加するには surfraw 側を弄る必要があるらしいのだが,そこがさっぱりわからなかったので断念.

anything-c-source-surfraw の詳細を知りたければ,以下が詳しいので参考に.

emacs からサーチエンジンの検索結果を呼び出す anything-c-source-surfraw の紹介 - わからん

search-web.el

もう1つ,search-web.el を見つけた.こちらは手軽に検索できて,サーチエンジンの追加も簡単.

Emacs ですぐに単語の検索をしたい欲望を叶える Elisp。 - 日々、とんは語る。

search-web.el ではリージョン検索と付近の単語検索で関数を分けているが,transient-mark-mode 時にはリージョンを,そうでないときは付近の単語を検索する方が便利.
あと,現在のバージョンの search-web.el では,キーバインド一発検索が設定できない.

ということで,.emacsに以下を追加.

(require 'search-web)

(define-prefix-command 'search-web-map)
(global-set-key (kbd "M-i") 'search-web-map)
(global-set-key (kbd "M-i g") (lambda () (interactive) (search-web-dwim "google")))
(global-set-key (kbd "M-i M-i") (lambda () (interactive) (search-web-dwim)))

(defun search-web-dwim (&optional arg-engine)
  "transient-mark-mode がオンの時はリージョンを,
オフの時はカーソル位置の単語を検索する."
  (interactive)
  (cond
   ((transient-region-active-p)
    (search-web-region arg-engine))
   (t
    (search-web-at-point arg-engine))))

さらに,search-web.el の search-web-at-point と search-web-region を以下に書き換え.

(defun search-web-at-point (&optional arg-engine)
  (interactive)
  (message "sw: %s" arg-engine)
  (let* ((completion-ignore-case t)
         (search-word (substring-no-properties (thing-at-point 'word)))
         (engine (or arg-engine (completing-read (format "[%s] Search Engine: " search-word)
                                   (make-search-engine-name-list) nil t))))
  (search-web engine search-word)))

(defun search-web-region (&optional arg-engine)
  (interactive)
  (let* ((completion-ignore-case t)
         (beg (mark))
         (end (point))
         (search-word (buffer-substring-no-properties beg end))
         (engine (or arg-engine (completing-read (format "[%s] Search Engine: " search-word)
                                   (make-search-engine-name-list) nil t))))
    (search-web engine search-word)))

M-i g で Google 検索,M-i M-i でサーチエンジンを選択して検索できる.

O'ReillyのiPhoneアプリからepubを作成するRubyプログラム

404 Blog Not Found:perl - O'ReillyのiPhoneアプリ本からepubをぶっこぬくRubyで書いた.ziprubyが必要.Ubuntuで使用したが,ziprubyがあればたぶんWindowsでも動く.

akisute3/ipa2epub · GitHub

# -*- coding: utf-8 -*-
#/usr/bin/env ruby
#
# O'Reilly の iPhone App を epub ファイルに変換し,
# 入力と同じディレクトリに出力する.
# ziprubyが必要 (gem install zipruby).
#
# $ ruby epub_extractor.rb {iPhone App}

require "fileutils"
require 'zipruby'
require 'pathname'
require 'tmpdir'
require 'find'
require 'readline'

class Ipa2Epub
  def self.create_epub(ipa)
    # 作成予定ファイルが既に存在していないかチェック
    output_path = Pathname(ipa).sub_ext(".epub")
    if File.exist?(output_path)
      while buf = Readline.readline("Overwrite #{output_path}? (y/n): ")
        if (buf == "y")
          break
        elsif (buf == "n")
          puts "exit."
          exit
        end
      end
    end

    Dir.mktmpdir {|tmpdir|
      # ipaをtmpdir下に解凍
      Zip::Archive.open(ipa) {|archives|
        archives.each do |a|
          d = File.dirname(a.name)
          FileUtils.mkdir_p("#{tmpdir}/#{d}")
          unless a.directory?
            open("#{tmpdir}/#{a.name}", "w") {|f| f.puts a.read }
          end
        end
      }

      # epubに必要なファイルをepubに追加
      puts "#{ipa} -> #{output_path.to_s} "
      Zip::Archive.open(output_path.to_s, Zip::CREATE | Zip::TRUNC) {|output|
        book_path = Pathname(Dir.glob("#{tmpdir}/Payload/*.app/book")[0])
        Find.find(book_path) do |f|
          next if f == book_path
          relative_path = Pathname.new(f).relative_path_from(book_path)
          if File.directory?(f)
            output.add_dir(relative_path.to_s)
          else
            output.add_file(relative_path.to_s, f)
          end
        end
      }
    }
  end
end


if __FILE__ == $0
  ARGV.each do |ipa|
    Ipa2Epub.create_epub(ipa)
  end
  puts "complete."
end

elispでprintデバッグ

こんな関数を定義してみるのはどうだろう.

;;; printデバッグ用関数
(defun p (FORMAT-STRING &rest ARGS)
  (prog1
      (apply 'message FORMAT-STRING ARGS)
    (sit-for 3)))

;;; 例
(setq num 10)
(p "num: %d" num)                       ; => "num: 10"
(message "num: %d" num)                 ; => "num: 10"

message関数と使い方は同じ.messageと違って表示後に一時停止するので,関数の途中に挟んでも変数の値を見る時間がある.

一文字の関数で入力しやすいというのも利点.もちろんRubyのパクり.グローバルなelispでこんな名前の関数を定義するのは危険すぎる気もするが…….