2023年12月10日日曜日

EMP版 emacs29.1 で漢字変換時に「あ」とかを表示をする elisp

少し前に、 自宅のiMac に emacs 29.1 (macOS EMP版) をインストールし、 いい感じで日本語入力ができ満足していたのだが、 漢字変換している時の IME の状態を表示する機能がないのが唯一の不満だった。
そこで、よくありがちな
こんな感じの、モードライン上にIMEの状態を表示する機能を elisp で作ってみた。

ソースコード:  myemp-aux.el

いちおう、自分の環境(mojava + kotoeri) では、そこそこうまく動作することを確認したが、 他の環境でどうなるかは不明だ。

myemp-aux.el の使い方

(ここの説明では emacs の 初期化ファイルを ~/.emacs.el と仮定している)
  1. myemp-aux.el から、ソースファイルをダウンロード
  2. ダウンロードした myemp-aux.el を load-path ディレクトリーに移動
  3. load-path の設定は、例えば、$HOME/elisp に置く場合は、~/.emacs.el に
    (add-to-list 'load-path (file-truename "~/elisp"))
    と書いておけばいい。
  4. ~/.emacs.el に以下を設定
  5. (when (fboundp 'mac-input-source)
      ;; (mac-auto-ascii-mode 1) ;; C-xなどで自動で ASCII 入力
      ;; (setq default-input-method nil) ;; disable leim ??
      (require 'myemp-aux)
      ;; 必要なら `my-ime-title-alist' の設定
      ;; (setq my-ime-title-alist '((インプットソースID名 . "あ")))
      (my-ime-enable)
      )
    
  6. emacs を再起動
  7. ⌘-SPACE などで入力ソースを切り替えて、モードラインの左端あたりに、」が表示されればとりあえずは成功。

あれ? 使えないよ、の場合

myemp-aux.el は、少々古めの環境で申し訳ないが、macOS-mojave + kotoeri で しか動作確認を行っていない。
運が良ければ、他の環境でもそのままで動作するかもしれないが、 そうでない場合は、次項の説明に従って my-ime-title-alist 変数をカスタマイズすれば、何とか使えるようになるかもしれない。

プログラムの仕組みと my-ime-title-alist 変数について

EMP版 emacs29 では、mac-input-source という関数で、現在の IME 状態を知ることができ、 mac-selected-keyboard-input-source-change-hook 変数で、IMEの状態が変化した時のコールバック関数を定義することができる。

myemp-aux.el ではこの二つを利用して、 IMEの状態が変化した時のモードライン表示を制御しているのだが、 フローの概略は、
  1. 現在のインプットソースIDを取得
  2. (mac-input-source) の返り値が、現在のインプットソースIDを教えてくれる。
  3. インプットソースIDを 「」 などのインプットソースタイトルに変換する
  4. この時、あらかじめ定義してある、my-ime-title-alist 変数を参照する。
    この変数は、インプットソースIDとインプットソースタイトルの対応を表す連想リストで、
    ( (インプットソースID . インプットソースタイトル) ... ) 
    の書式で定義する。
  5. モードドラインに、変換したインプットソースタイトルを表示する
  6. isearchの場合には、ミニバッファープロンプトに表示
つまり、myemp-aux.el がうまく動作しないケース、 特に、インプットソースを変えてもモードラインが変化しない場合は、 my-ime-title-alist 変数を、 IME 環境に合わせてカスタマイズする必要がある。
テスト用のコマンド my--ime-toggle-checkId を使えば、実際に、 どのようなインプットソースIDがあり、 それがどんな場合に有効になっているかを調べることができる。
具体的には、M-x my--ime-toggle-checkId を実行して、 キーボードからインプットソースを切り替えれば、 その都度 *mylog* というバッファーに ID のログが表示される筈だ。 そのID値を覚えておいて、my-ime-title-alist 要素の左辺(car部)を決めればいい。

上図は、kotoeri で、「ひらがなモード」と「カタカナモード」を有効にした時の、 *mylog* の一例である。
各々のモードとその時のインプットソースIDは
入力モードインプットソース ID
ひらがな com.apple.inputmethod.Kotoeri.Japanese
カタカナ com.apple.inputmethod.Kotoeri.Japanese.Katakana
IME OFF com.apple.inputmethod.Kotoeri.Roman
になっていると推測出来るので、
(setq my-ime-title-alist
      '(("com.apple.inputmethod.Kotoeri.Japanese" . "あ")
	("com.apple.inputmethod.Kotoeri.Japanese.Katakana" . "ア")
))
これを ~/.emacs.el に記述すればいい。

Note:

myemp-aux.el スクリプトは、あくまで、emacs29.1 のソースに山本光晴さんのパッチを当てた、通称 EMPバージョンの emacs に対するもので、他の emacs に対しては無効です。

2023年11月18日土曜日

emacs29.1 (macOS EMP版) をソースからインストールしてみた

今更ながらだけど、Mac に、emacs29.1 の EMP版を、ソースからインストールした時のメモ。

きっかけ

今まで emacs26 の ns バージョンを使っていて特に大きな不満はなかったが、 日本語で文章を書いていると、たまに変換候補のポップアップが消えなかったり、IME がらみの不具合に見舞われる時があった。
ネットをいろいろ探っていると、IMEパッチについては、 ns 版とは別の EMP版というのがあり、かなり評判がいいようなので、 試しにインストールしてみようと思ったのが直接のきっかけ。

さらに、emacs28 あたりから、elisp のネイティブコンパイルとか、webkit 利用のフルブラウザー機能など、いろいろと斬新な機能が emacs 自体にも加えられているということ。ネイティブコンパイルについては、 僕の技量の範囲を超えているのでパスするとして、フルブラウザーについては結構なインパクトだ。 emacs のバッファーで、youtube が見れるのはうれしいかも。

インストール作業

インストールを行った環境は、古過ぎて馬鹿にされそうだが、Mac はインテル版の iMacで、OSは mojave。

ソースコードを置いたり、いろんなコマンドを実行する作業ディレクトリーは、
    $ mkdir ~/emacs-29
    $ workdir=$HOME/emacs-29
make における仮のインストール場所は、
    $ mkdir ~/emacs-29/build
    $ build="$HOME/emacs-29/build"

に設定した。

$biuid については、make が正常に終了した後、 あらためて ~/Applications にコピーする。

  1. ソースの取得
  2. 山本光晴さんの git ページ から clone で取得。
        $ cd $workdir
        $ git clone https://bitbucket.org/mituharu/emacs-mac.git
    これで、$workdir/emacs-mac/ にソースコードが配備される。

  3. sexy icon をダウンロード
  4. しばらくは、今回の emacs29と、古い emacs26 とを両方使うことになり、 アイコンを標準とは別のものにしたかったので、 Emacs is sexy から、macos 用をダウンロードした。

    なんか、とってもかっこいいアイコン!

  5. sexy アイコンを resource ディレクトリーへコピー
  6.     $ cd $workdir
        $ cp -p ~/Downloads/Emacs.icns emacs-mac/mac/Emacs.app/Contents/Resources/

  7. configure options の検討
    • --with-mac
    • EMP版の場合は、--with-ns ではなく、--with-mac とするらしい。

    • --with-xwidgets
    • webkit 利用のフルブラウザー機能を有効にする。

    • --prefix
    • --enable-mac-app
    • --enable-mac-self-contained
    • これらを指定しないと、Emacs.app 内で全てが完結する状態にならなかった。
      (つまり、loda-pathC-h で表示するさまざまな情報が、 $workdir/emacs-mac/lisp や $workdir/emacs-mac/info を参照している状態になってしまう)

    • --with-native-compilation
    • elisp のネイティブコンパイルが有効になるオプション。
      かなり魅力的な機能なんだが、へたれな僕としてはパス。

  8. make
  9.   $ cd $workdir/emacs-mac
      $ make clean
      $ ./configure  --with-mac  --without-x  --enable-mac-app=$build \
          --prefix=$build  --enable-mac-self-contained  --with-xwidgets
      $ echo $? #=> 0
      $ make bootstrap # (bootstrap なしで、単に make だけでいいのかも?)
      $ echo $? #=> 0
      $ make install
      $ echo $? #=> 0
    
      # AppTranslocation 対策
      # (この作業は、必要なかったもしれない?)
      $ cd $build
      $ xattr -d com.apple.quarantine Emacs.app
      $ xattr -dr com.apple.quarantine Emacs.app/
      $ cd ..

  10. 端末から実行して動作確認
  11.   $ open $build/Emacs.app --args -q
    初期設定ファイルなしのこの状態で、kotoeri からの日本語入力ができているのに感動……

  12. $build 環境を、$HOME/Applications にコピー
  13. App 資源を、仮のインストール場所 $build/Emacs.app から、~/Applications にコピーする。
      $ cp -Rp $build/Emacs.app  ~/Applications/
    追加で、C言語で書かれたソースコードも C-h で見れるように、 C のソースファイル を Emacs.app/ の下にコピーしておく。
      $ cp -Rp $workdir/emacs-mac/src \
           ~/Applications/Emacs.app/Contents/Resources/
    これで、.emacs.el などの初期設定ファイルに、

    (setq source-directory (file-truename 
          "~/Applications/Emacs.app/Contents/Resources"))

    と書いておけば、C-h からのソースファイルや INFO ドキュメントの参照が、 ~/Applications/Emacs.app の下を見るようになり、もう、$workdir からはおさらばになる筈だ。

.emacs.el の設定

  • 必要最低限の設定
  • 少なくとも、以下を設定しておけばいいと思う。
    ;;; -*- coding: utf-8; lexical-binding: t; -*-
    (setq source-directory
          (file-truename
           "~/Applications/Emacs.app/Contents/Resources"))
    (set-language-environment "Japanese")
    (prefer-coding-system  'utf-8-unix)
    (when (fboundp 'mac-input-source) ;; when emacs EMP version
      ;; (setq default-input-method nil) ;; disable leim and ns-xxx ???
      ;; C-xなどのプレフィックスが押されたら自動的に ASCII入力へ移行
      (mac-auto-ascii-mode 1) 
      )
  • 個人的に必要だった設定
  • 僕だけかもしれないが、org-mode の設定にいろいろと手間取った。
    ;; () <> [] {} などの対応装飾が煩わしい...
    (setq show-paren-mode nil)
    ;; --- org-mode 関連 ------
    (require 'org-tempo) ;; <s TAB で source block を書く
    (setq org-adapt-indentation t) ;; header の直下で indent
    ;; たまに起こる、C-c C-o での、Visit tags table の prompt を抑制する W/A
    ;; See: https://emacs.stackexchange.com/questions/76351/how-to-follow-an-internal-link-in-recent-org-mode
    (with-eval-after-load 'org-ctags
      (setq org-open-link-functions
            (cl-delete-if
             (lambda (sym)
               (string-match-p "^org-ctags-" (symbol-name sym)))
             org-open-link-functions)) )
    ;; TeX 出力において、#+OPTIONS: \n:t かつ 図形がある場合の対策??
    (setq org-latex-line-break-safe "\\\\")

emacs29.1 EMP版 の感想

約1ヶ月間 emacs29.1 を使ってみての個人的感想。
  • 何も設定せずに、Kotoeri を使って普通に日本語文章が打てた。でも……
  • IMEはかなり安定して使えるようだ。 ただし、漢字の変換中にモードライン上に「あ」とかの表示は出ない。

    調べてみると、 mac-input-source という関数で現状の IME 状態を知ることができ、 mac-selected-keyboard-input-source-change-hook 変数で、IMEの出入りをフックできるみたいなので、いろいろと対策は打てそう。

    追記 (2023-12-10): 何とか対策した! → EMP版 emacs29.1 で漢字変換時に「あ」とかを表示

  • 辞書サービスが使える
  • 他の mac アプリケーションと同様に、 マウス + M-C-d で辞書を引けるようになっていた。
    もう、dict:///検索語 を使わなくてもいいんだ。

  • youtube が emacs 内で観れる
  • M-x xwidget-webkit-browse-url で、https://www.youtube.com/ を指定すればいいだけだ。

    わざわざ emacs で youtube を見なくても、ブラウザーで見れば済むことなのだが、 もしかしたら自作の javascript と elisp との連携ができるのかも? と考えると夢は広がる……

2013年11月23日土曜日

zip とか tar の中身を grep するツール

zipやtar などのアーカイブの中身を、いちいち展開することなくそのまま検索でき、 かつ、ShiftJis とか EUC など日本語コードの違いも、よしなに扱う grepライクな python スクリプトを作ってみた。
(ググって探せば、先人の作ったもっといいものが見つかるような気もするけど)

jzgrep.py

スクリプトのソースは ここ
動かすには、python2.6 以上が必要。でも、python3 では動かない。
動作確認は、linux と os-x10.6 でしか行なっていない。

使い方は、$ python jzgrep.py --help | less を参照。
$ python jzgrep.py --help
Usage: jzgrep.py [OPTIONS] PATTERN [FILE ...]
  PATTERN  -- 正規表現パターン (Python の `re' モジュールを使っています)
  FILE...  -- PATTERN で検索するファイル。省略時は stdin。
  OPTIONS
       --help -- このメッセージを表示して終了する
       -n     -- 行番号を表示
       -l     -- PATTERN に一致する行を含むファイル名だけを出力
       -H     -- 出力する行の先頭にファイル名を付ける。
                 (検索するファイルが複数の場合は、これがデフォルト)
       -h     -- 出力する行の先頭にファイル名を付けない
                 (検索するファイルが一つの場合は、これがデフォルト)
       -i     -- 大文字小文字を区別しない (re.IGNORECASE)
       -s     -- 警告メッセージを出さない (e.g [Warning]: Cannot decode ...)

対応しているファイル・タイプは、
  • 普通のテキストファイル
  • gzip, bzip2
  • 暗号化していない zip 類 (たぶん epub とか jar も)
  • tar とその圧縮ファイル( tar、 tar.gz 及び tar.bz2)
また、tar や zip の中の tar や zip 、さらにその中の tar や zip、 さらに… も再帰的に検索できる筈。

日本語エンコーディングは、今のところ
utf-8, cp932, euc-jp, iso2022_jp
の4種類だけに対応している。
(他の文字コードについては、ソース中の _encodings 変数や matchtext 関数を、 少しだけ書き換えれば対応できるかもしれない)

但し、jzgrep.py はとても遅い!
たぶん、異なる日本語コードの照合処理について、こんな感じで、
for enc in ("iso2022_jp", "utf-8", "euc-jp", "cp932")
  try:
    m = re.search(PATTERN, TEXTLINE.decode(enc))
    if m: return m
  except UnicodeError: pass
return None
総当りでチェックしているからだと思う。
それに、ほんのたまにだけど、 間違って余計なものにヒットしちゃうこともある。かな?

2013年11月4日月曜日

青空文庫の複数テキストを合本して kindle-paperwhite で読む

AozoraEpub3 という素晴らしいソフトのおかげで、青空文庫の小説を、kindle で自由に読めるようになった。
青空からテキストデータの zip をダウンロードして、それを、 AozoraEpub3で epub3 に変換し、さらに、 kindlegen で mobi に変換。あとは、usb ケーブルで kindle に転送すれだけでいい。
AozoraEpub3の作者の方には本当に感謝致します。

ただ、欲を言を言わせて貰うと、 短編やシリーズ物など、複数の小説データを合本する機能が欲しい。
まあ、TODOには優先度高として挙げられているので、 AozoraEpub3の将来のバージョンで合本の機能が追加されるのを待てばいいわけだが、 何でもかんでも人任せというのも情けない。

というわけで、 青空文庫の複数のテキストファイルを結合するスクリプトを作ってみた。
(本当は、epub レベルで結合するのが理想なんだけど、 epub3の仕様は、僕には難しすぎて無理…)

ソースコード (bindao.py)

ソースは ここ。 実行するには、python2.7 以上が必要。でも、python3 では動きません。
os-x 10.6.8 + kindle-pw でしか試していないけど、 たぶん、linux でも動作すると思う。

合本するロジックは、 「青空文庫の作品を合本して Epub 化する試み」 の方法を参考にさせて頂き、プラス、
  • 目次を階層化するために、個別作品の見出しレベルを変更
    合本上では、個別の構成作品の表題を大見出しに設定するので、 個別作品自体に大見出しがあれば、重複を避けるために、それぞれ、 大見出しを中見出しに、中見出しを小見出しに変換する。ただし、 個別作品に小見出しがあればこの変換はしない。

  • Zip のまま処理する。
    個別作品のzipアーカイブをいちいち展開する手間を省く。

  • 合本ファイルとしての、出力も zip 形式にする。
    個別作品のzip内に含まれる画像データ等を、 出力のzipファイルにも自動で埋め込ませる。

の3つの機能を施した。

使い方

ターミナルアプリなどから、shell上で、
$ python bindao.py 結合する青空形式のzipファイル....
を実行すると、
Top>
というプロンプトが出る。help と打つと、各コマンドの説明が表示されるが、 その中でも、よく使うコマンドは、
  • title 合本のタイトル
  • author 合本の著者名
  • bindzip
の3つ。
titleコマンドで合本の表題を、authorコマンドで著者名を設定し、最後に、 bindzip と打てば、bind.zip というファイルが作成される。
より詳しくは、$ python bindao.py -h を参照。

実際の例

青空文庫の小栗虫太郎の短編4本を合本してみた時のログ。
  1. まずは、青空文庫から zip ファイルをダウンロードする。
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/4841_ruby_16998.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/666_ruby_2185.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/45230_ruby_26898.zip
     $ curl -O http://www.aozora.gr.jp/cards/000125/files/667_ruby_441.zip
    
  2. ダウンロードした zip を、bindao.py で結合する。
    (この場合は、-noauthor というオプションを指定しないと、 個別作品の表題の次にも著者名が書かれてしまう)
     $ python bindao.py -noauthor 4841_ruby_16998.zip 666_ruby_2185.zip \
                                   45230_ruby_26898.zip 667_ruby_441.zip
       Top> title 小栗虫太郎短編集 1
       Top> author 小栗虫太郎
       Top> show all
         合本の表題: 小栗虫太郎短編集 1
         合本の著者: 小栗虫太郎
         合本の副題: -未設定-
         構成ファイル(1): 4841_ruby_16998.zip 内の goko_satsujinjiken.txt
         ................................................................
         ................................................................
       Top> bindzip
         OK: bind.zip を出力しました
       Top> quit
      
  3. bind.zip の中身を確認
     $ unzip -l bind.zip 
        ------  ----     ----    ----
        258407  10-30-13 15:21   bind.txt
          9286  03-04-12 18:40   fig666_02.png
           674  03-04-12 18:39   fig666_01.png
           296  07-24-07 13:52   fig45230_01.png
           255  07-24-07 13:52   fig45230_02.png
          9834  02-27-06 05:33   fig45230_03.png
         12804  02-24-11 17:54   fig667_01.png
     $
    
  4. AozoraEpub3 を使って、bind.zip を epub ファイルに変換する
    $ java -jar AozoraEpub3.jar
    • 端末設定 で kindle-PW を選択
    • 表題: 本文内 「表題→著者名」を選択し、「先頭が発行者」のチェックを外す。
    • 目次設定の「目次(NCX)階層化」のチェックをいれる
    • 他はデフォルトのままで bind.zipをドラッグアンドドロップ
    • 「変換」をクリック
  5. kindlegen で mobi に変換
      $ kindlegen '[小栗虫太郎] 小栗虫太郎短編集 1.epub' -o oguri.mobi
    
  6. kindle を usb 接続して mobi ファイルをコピーする。
    osx の場合は、/Volumes/Kindle にマウントされるので、
    $ cp oguri.mobi /Volumes/Kindle/documents/
最後に、ファインダーなどで kindle device をアンマウントすることを忘れないように。

ありゃ、部分的に辞書が引けない場合がある!

AozoraEpub3 + bindao.py を使って、 小栗の他にも、久生十蘭、甲賀三郎、浜尾四郎など、 古き良き時代の探偵小説をかたっぱしから合本して、秋の読書を堪能してたんだけど、 なんだか部分的にkindleの辞書が引けない本があることに気づいた。
調べたい単語をロングタッチして、デジタル大辞泉を引いても、
「お探しの言葉が見つかりませんでした」
と出るばかり。でも、同じ言葉を同じ作品の別の段落で引くと、 ちゃんと辞書が引けたりする。
bindao.py のバグかとも思ったのだが、 AozoraEpub3単独で変換しても直らない。かといって、 この症状をググってみても全然引っかからない。 同じような問題で悩んでいる人は誰もいないようだ。もしかしたら、 僕の kindle-pw だけで起こる、固有の問題なのかもしれない。

で、いろいろ調べてみた。
なんとなく、文章量の長い段落でこの現象が起きているみたいなので、 epubファイル内本文のxhtmlファイルを開き、 問題の生じる段落で適当に句点の後(。の後)に改行をいれてみると、 辞書が引けるようになった。
でもこれだと、kindleで見た時に、改行が半角空白で表示されてしまう。
改行の代わりに、&zwnj; という特殊シンボル(自分でも意味がわかっていない)を使っても駄目。
その他いろいろ試行錯誤の結果、 <span /> という、たぶん、意味のない空タグを、 句読点の後に入れてやると直ることがわかった。余分な半角空白も入らず、 かつ、どこでも辞書引きがOKになる。ただ mobi ファイルが太ってしまうが…

想像だけど、空白も改行もタグも途中に入らない日本語の長い段落というのは、 xhtml上では、長いワードだと認識されるのではないだろうか。 その異常に長いワードをkindleが解析する時に、 僕の環境に固有の何か他の事象が重なると、このような不具合が起きるのではないかと思う。

で、これも、 python のスクリプトでまとめてみた。
ソースコードは ここ
使い方は、AozoraEpub3 で epub を作成し、kindlegen を実行する前に、 shellからこのスクリプトを実行すればいい。
例えば、AozoraEpub3 が出力するファイル名を source.epub とすると、
 $ python aoepub3_hack.py source.epub dist.epub 
 $ kindlegen dist.epub
これで、空の span タグだらけの、変な dist.mobi が出来上がる。
(他のオプションなどは、 $ python aoepub3_hack.py -h を参照)

しばらく使ってみて、今のところ特に問題はない。
ただ、こんな解決方法はどう考えても醜い。本によっては、 <span /> タグを入れている事が原因で、 何か変な副作用が起きるかもしれない。
こんなことやらなくても、何か、css 関連の設定ですっきりと解決、 なんてことになればいいのだけど。