ラベル emacs の投稿を表示しています。 すべての投稿を表示
ラベル emacs の投稿を表示しています。 すべての投稿を表示

2013年6月3日月曜日

html5でも正しくインデントしてくれるweb-modeを導入してみた

以前にhtml5を書くためにnxml-modeを導入したが、これはどちらかというとxhtml5を書くためのものでinputタグ等は最後にスラッシュを書いてタグを閉じておかないとインデントが残念なことになってしまっていた。

残念なインデント

最近はhtml5で書いた(しかも、微妙にxhtmlの記述が混ざっていたりする)ファイルを扱う事が多くなってくると、インデントがうまく処理できないnxml-modeだと使いにくいので、web-modeをインストールして使用してみた。

インストール

web-mode.elのページからweb-mode.eをダウンロードして適当なディレクトリに置く。

.emacsの設定

.emacsに下記の記述を追加

(setq auto-mode-alist
      (append '(
                ("\\.\\(html\\|xhtml\\|shtml\\|tpl\\)\\'" . web-mode)
                ("\\.php\\'" . php-mode)
                )
              auto-mode-alist))

;;==========================================================
;;         web-modeの設定
;;==========================================================
(require 'web-mode)
(defun web-mode-hook ()
  "Hooks for Web mode."
  ;; 変更日時の自動修正
  (setq time-stamp-line-limit -200)
  (if (not (memq 'time-stamp write-file-hooks))
      (setq write-file-hooks
            (cons 'time-stamp write-file-hooks)))
  (setq time-stamp-format " %3a %3b %02d %02H:%02M:%02S %:y %Z")
  (setq time-stamp-start "Last modified:")
  (setq time-stamp-end "$")
  ;; web-modeの設定
  (setq web-mode-markup-indent-offset 2) ;; html indent
  (setq web-mode-css-indent-offset 2)    ;; css indent
  (setq web-mode-code-indent-offset 2)   ;; script indent(js,php,etc..)
  ;; htmlの内容をインデント
  ;; TEXTAREA等の中身をインデントすると副作用が起こったりするので
  ;; デフォルトではインデントしない
  ;;(setq web-mode-indent-style 2)
  ;; コメントのスタイル
  ;;   1:htmlのコメントスタイル(default)
  ;;   2:テンプレートエンジンのコメントスタイル
  ;;      (Ex. {# django comment #},{* smarty comment *},{{-- blade comment --}})
  (setq web-mode-comment-style 2)
  ;; 終了タグの自動補完をしない
  ;;(setq web-mode-disable-auto-pairing t)
  ;; color:#ff0000;等とした場合に指定した色をbgに表示しない
  ;;(setq web-mode-disable-css-colorization t)
  ;;css,js,php,etc..の範囲をbg色で表示
  ;; (setq web-mode-enable-block-faces t)
  ;; (custom-set-faces
  ;;  '(web-mode-server-face
  ;;    ((t (:background "grey"))))                  ; template Blockの背景色
  ;;  '(web-mode-css-face
  ;;    ((t (:background "grey18"))))                ; CSS Blockの背景色
  ;;  '(web-mode-javascript-face
  ;;    ((t (:background "grey36"))))                ; javascript Blockの背景色
  ;;  )
  ;;(setq web-mode-enable-heredoc-fontification t)
)
(add-hook 'web-mode-hook  'web-mode-hook)
;; 色の設定
(custom-set-faces
 '(web-mode-doctype-face
   ((t (:foreground "#82AE46"))))                          ; doctype
 '(web-mode-html-tag-face
   ((t (:foreground "#E6B422" :weight bold))))             ; 要素名
 '(web-mode-html-attr-name-face
   ((t (:foreground "#C97586"))))                          ; 属性名など
 '(web-mode-html-attr-value-face
   ((t (:foreground "#82AE46"))))                          ; 属性値
 '(web-mode-comment-face
   ((t (:foreground "#D9333F"))))                          ; コメント
 '(web-mode-server-comment-face
   ((t (:foreground "#D9333F"))))                          ; コメント
 '(web-mode-css-rule-face
   ((t (:foreground "#A0D8EF"))))                          ; cssのタグ
 '(web-mode-css-pseudo-class-face
   ((t (:foreground "#FF7F00"))))                          ; css 疑似クラス
 '(web-mode-css-at-rule-face
   ((t (:foreground "#FF7F00"))))                          ; cssのタグ
)

主なキーアサイン

個人的には、C-c C-fでタグブロックを開閉できるので、複雑なHTMLを編集するときは便利だと思う。

Generalなキーアサイン
キー機能
C-c C-;コメント/アンコメント
C-c C-e閉じていないタグを見つける
C-c C-f指定したタグのブロックを開閉する
C-c C-i現在開いているバッファをインデントする
C-c C-mマークする(マークする場所によって選択範囲が変わります)
C-c C-n開始・終了タグまでジャンプ
C-c C-rHTML entitiesをリプレースする
C-c C-sスニペットを挿入
C-c C-wスペースを表示・非表示
HTML element系のキーアサイン
キー機能
C-c /閉じタグを挿入(エレメントを閉じる)
C-c ebエレメントの最初へ移動
C-c edエレメントを削除
C-c eeエレメントの最後へ移動
C-c eeエレメントを複製
C-c en次のエレメントへ移動
C-c ep前のエレメントへ移動
C-c eu親エレメントへ移動
C-c erエレメントをリネーム
C-c esエレメント全体を選択
C-c eiエレメントのコンテンツを選択
HTML tag系のキーアサイン
キー機能
C-c tbタグの先頭へ移動(エレメントの先頭では無くタグの先頭です。
</div>で実行した場合は</div>タグの先頭(<)に移動します)
C-c teタグの後尾へ移動
C-c tmマッチするタグへ移動
C-c tsタグを選択
C-c tp前のタグに移動
C-c tn次のタグに移動

インデント

C-c C-iでhtml5でソースをインデントさせてみたところ、インデントの崩れは起きませんでした。

coolなインデント

2013年4月12日金曜日

Markdownを使う環境を整えてみた

素のhtmlを使って書くより、Markdownを使用した方がblogやドキュメントが書きやすそうなので、Markdownを使用する環境を整えてみました。

Markdownのインストール

Markdownはオリジナルの表記を拡張した、派生バージョンがあるらしいけれど、まずはオリジナルであるMarkdown.plインストールしてみました。

Markdown.plをインストールする

最初にMarkdownのサイトよりMarkdown.plをダウンロードし、インストールします。

# wget http://daringfireball.net/projects/downloads/Markdown_1.0.1.zip
# unzip  Markdown_1.0.1.zip
# cd Markdown_1.0.1
# chmod 755 Markdown.pl
# mv Markdown.pl /usr/local/bin/Markdown
#

試しにmarkdown.plを使用していくつかの文章を書いてみましたが、ちょっと凝った構造の文章を書こうとすると、HTMLを使わないと表現できなかったりするので、ちょっと貧弱かなと思いました。

kramdown コマンドのインストール

もう少し表現力高いものが欲しいので、シンタクスを拡張したバージョンを使ってみようかと色々と調べてみると、kramdownが機能が豊富で使いやすそうなので、これをインストールしてみました。

$ sudo gem install kramdown
Password:
Successfully installed kramdown-0.14.2
1 gem installed
Installing ri documentation for kramdown-0.14.2...
Installing RDoc documentation for kramdown-0.14.2...
$

これで、/usr/bin/kramdownにインストールされます。

kramdownのドキュメントを見ると、かなり拡張されていて表現力もオリジナルよりアップしています。これならばHTMLタグに頼らなくても大体の文章は書けるのでは無いかと思います。

MarsEditでkramdownを使いプレビューさせる設定

MarsEditはデフォルトでmarkdown表記のプレビューが可能ですが、これはMarkdown.plを使用してプレビューを行っています。このため、kramdownの拡張シンタックスを使うとうまく表示できません。

そこで、markdownのプレビューにkramdownを使うように設定します。

{$HOME}/Library/Application Support/MarsEdit/TextFilters/kramdownkramdownディレクトリを作成し、そこにShell Script ファイルkramdown.shを作成します。

$ midir  $HOME/Library/Application\ Support/MarsEdit/TextFilters/kramdown/
$ cd $HOME/Library/Application\ Support/MarsEdit/TextFilters/kramdown/
$ vi kramdown.sh

kramdown.shの内容は下記のようにします。

#! /bin/sh
CMD=/usr/bin/kramdown
CMDARGS="--auto-ids"
$CMD $CMDARGS $1

CMDARGSに設定するオプションはコマンドラインでkramdown --helpとすると出てきますので、お好みで指定します。

ファイルの作成が完了したならば、プレビュー画面左下にあるPreview Text Filterプルダウンをkarmdownにすれば、kramdownの拡張シンタックスが正常にレンダリングされます。

karmdown でプレビュー

emacsの設定

emacsからもmarkdownを扱えるようにmarkdown-mode.elをインストール・設定をします。

markdown-mode.elのインストール

Emacs Markdown Modeのサ イトよりmarkdown-mode.elをダウンロードしてelispのディレクトリに置きま す。

$ cd ~/.elisp
$ wget http://jblevins.org/projects/markdown-mode/markdown-mode.el
$

.emacs.elの編集

.emacs.elに下記のコードを追加します。

;;==========================================================
;;	   Markdown モードの設定
;;==========================================================
(autoload 'markdown-mode "markdown-mode"
   "Major mode for editing Markdown files" t)
(add-to-list 'auto-mode-alist '("\\.\\(text\\|md\\|mdwn\\|mdt\\)\\'" . markdown-mode))
(defun markdown-custom ()
  "markdown-mode-hook"
  (auto-fill-mode -1)
  (setq markdown-command "/usr/bin/kramdown"))  ;; コマンドパスの設定
(add-hook 'markdown-mode-hook '(lambda() (markdown-custom)))

2012年11月6日火曜日

emacs24にしたらマウスでコピペの挙動が変わった

emacs24にしたらマウスでコピペの挙動が変わってしまった。以前はマウスで選択するとクリップボードにコピーされ、マウスの真ん中ボタンでペーストされていたのだが出来なくなってしまった。

どうやらその辺のデフォルト設定が変更になったらしいので、以前の動作に戻すため下記の設定を追加した。

(setq x-select-enable-clipboard nil)
(setq x-select-enable-primary t)
(setq select-active-regions t)
(setq mouse-drag-copy-region t)
(global-set-key [mouse-2] 'mouse-yank-at-click)

2012年6月16日土曜日

Emacs nxml-modeの色設定

Emacsを23.4にしたら、nxml-modeのカラー表示設定が有効にならなくなってしまったので再設定しなおしてみた。

(custom-set-faces
 ;; xml-validate
 '(rng-error
   ((t (:foreground "red" :weight bold))))       ;; エラー部分

 '(nxml-name
   ((t (:foreground "#AACF53"))))                ;; 
 '(nxml-text
   ((t (:foreground "#AFAFB0"))))                ;; テキスト
 '(nxml-ref
   ((t (:foreground "#FF6A6A" :weight bold))))   ;; 参照文字の親定義
 '(nxml-delimiter
   ((t (:foreground "#FFFAFA"))))                ;; delimiterの親定義
 '(nxml-delimited-data
   ((t (:foreground "#9B30FF"))))                ;; delimiter内データの親定義

 ;; xml宣言タグ
 '(nxml-processing-instruction-target
   ((t (:foreground "#C7DC68"))))                ;; xml宣言
 '(nxml-processing-instruction-delimiter
   ((t (:foreground "#E9DFE5"))))                ;; xmlタグのdelimiter(<? ?>)
 '(nxml-processing-instruction-content
   ((t (:foreground "#9B30FF"))))                ;; ???

 ;; DOCTYPEタグ
 '(nxml-prolog-literal-delimiter
   ((t (:foreground "#AFAFB0"))))                ;; DOCTYPE内のdelimiter(")
 '(nxml-prolog-literal-content
   ((t (:foreground "#82AE46"))))                ;; DOCTYPEの内容
 '(nxml-prolog-keyword
   ((t (:foreground "#C1E4E9"))))                ;; DOCTYPE内のkeyword
 '(nxml-markup-declaration-delimiter
   ((t (:foreground "#E9DFE5"))))                ;; DOCTYPEタグのdelimiter(<! >)

 ;; htmlタグ
 '(nxml-namespace-attribute-xmlns
   ((t (:foreground "#FF8247"))))                ;; xmlns属性
 '(nxml-namespace-attribute-prefix
   ((t (:foreground "#FF8247"))))                ;; xmlns属性のprefix(xmlns:math)
;; '(nxml-namespace-attribute-colon
;;   ((t (:foreground "#FFFAFA"))))              ;; namespace属性のcolon
 '(nxml-namespace-attribute-value
   ((t (:foreground "#AFAFB0"))))                ;; namespace属性値
 '(nxml-namespace-attribute-value-delimiter
   ((t (:foreground "#AFAFB0"))))                ;; namespace属性のdelimiter(")

 ;; CDATA
 '(nxml-cdata-section-delimiter
   ((t (:foreground "#D9333F"))))                ;; <![xxxx[  ]]>
 '(nxml-cdata-section-CDATA
   ((t (:foreground "#D9333F"))))                ;; CDATAの文字
 '(nxml-cdata-section-content
   ((t (:foreground "#AFAFB0"))))                ;; CDATAの内容

 
 '(nxml-tag-delimiter
   ((t (:foreground "#E9DFE5"))))                ;; tagのdelimiter(< >)
 '(nxml-tag-slash
   ((t (:foreground "#E9DFE5"))))                ;; tagのスラッシュ
 
 ;;'(nxml-element-prefix
 ;;  ((t (:foreground "#884898"))))
 ;;'(nxml-element-colon
 ;;  ((t (:foreground "#884898"))))
 '(nxml-element-local-name
   ((t (:foreground "#E6B422" :weight bold))))   ; 要素名
 
 '(nxml-attribute-prefix
  ((t (:foreground "#C97586"))))                 ;; 属性のprefix(xml:lang)
;; '(nxml-attribute-colon
;;   ((t (:foregroud "#9B30FF"))))               ;; 属性のコロン
 '(nxml-attribute-local-name
   ((t (:foreground "#C97586"))))                ;; 属性名など
 '(nxml-attribute-value
   ((t (:foreground "#82AE46"))))                ;; 属性値
 '(nxml-attribute-value-delimiter
   ((t (:foreground "#AFAFB0"))))                ;; 属性値のdelimiter(")

 '(nxml-comment-delimiter
   ((t (:foreground "#D9333F"))))                ; コメントのdelimiter()
 '(nxml-comment-content
   ((t (:foreground "#D9333F"))))                ;; コメント内容
 
 ;; 参照文字
 ;; '(nxml-entity-ref-name
 ;;   ((t (:foreground "#FF6A6A"))))          ;; entity名 (<)
 ;; '(nxml-entity-ref-delimiter
 ;;   ((t (:foreground "#FF6A6A"))))          ;; entityのdelimiter(&xx;)
 ;; '(nxml-char-ref-number
 ;;   ((t (:foreground "#FF6A6A"))))          ;; 文字実体参照の番号 (&#160;)
 ;; '(nxml-char-ref-delimiter
 ;;   ((t (:foreground "#FF6A6A"))))          ;; 文字実体参照のdelimiter(&#xxx;)

 '(nxml-hash
   ((t (:foreground "#9B30FF"))))
 '(nxml-glyph
   ((t (:foreground "#9B30FF"))))
)

下記の様な配色にしていみました。

配色

2011年10月27日木曜日

Emacs nxml-modeの設定

最近、スマフォ関係でhtml5で書くことが多くなってきました。今まで使用していたpsgml-modeはhtml5と相性が悪く、インデントやタグの補完がうまく動かないので、nxml-modeに変更してみることにしました。

nxml-modeはCarbon Emacsやemacs23以降には標準搭載されているので、インストールは特に不要ですが、別途hober/html5-elをインストールする必要があります。

nxml-modeの設定

最初にnxml-modeの設定を行って、正常に動作するかチェックします。下記のように設定しました。

(setq auto-mode-alist
      (append '(
                ;;("\\.\\(html\\|xhtml\\|shtml\\|tpl\\)\\'" . xml-mode)
                ("\\.\\(html\\|xhtml\\|shtml\\|tpl\\)\\'" . nxml-mode)
                ("\\.php\\'" . php-mode)
                )
              auto-mode-alist))

(load "rng-auto.el" 't)
(add-hook 'nxml-mode-hook
          (lambda ()
            ;; 更新タイムスタンプの自動挿入
            (setq time-stamp-line-limit 10000)
            (if (not (memq 'time-stamp write-file-hooks))
                (setq write-file-hooks
                      (cons 'time-stamp write-file-hooks)))
            (setq time-stamp-format "%3a %3b %02d %02H:%02M:%02S %:y %Z")
            (setq time-stamp-start "Last modified:[ \t]")
            (setq time-stamp-end "$")
            ;;
            (setq auto-fill-mode -1)
            (setq nxml-slash-auto-complete-flag t)      ; スラッシュの入力で終了タグを自動補完
            (setq nxml-child-indent 2)                  ; タグのインデント幅
            (setq nxml-attribute-indent 4)              ; 属性のインデント幅
            (setq indent-tabs-mode t)
            (setq nxml-bind-meta-tab-to-complete-flag t) 
            (setq nxml-slash-auto-complete-flag t)      ; </の入力で閉じタグを補完する
            (setq nxml-sexp-element-flag t)             ; C-M-kで下位を含む要素全体をkillする
            (setq nxml-char-ref-display-glyph-flag nil) ; グリフは非表示
            (setq tab-width 4)))

custom-set-faces
 '(nxml-comment-content-face
   ((t (:foreground "red"))))                            ; コメント
 '(nxml-comment-delimiter-face
   ((t (:foreground "red"))))                            ; <!-- -->
 '(nxml-delimited-data-face
   ((t (:foreground "DarkViolet"))))                     ; 属性値やDTD引数値など
 '(nxml-delimiter-face
   ((t (:foreground "blue"))))                           ; <> <? ?> ""
 '(nxml-element-local-name-face
   ((t (:inherit nxml-name-face :foreground "blue"))))   ; 要素名
 '(nxml-name-face
   ((t (:foreground "dark green"))))                     ; 属性名など
 '(nxml-element-colon-face
   ((t (:foreground "LightSteelBlue"))))                 ; :(xsl:paramなど)
 '(nxml-ref-face
   ((t (:foreground "DarkGoldenrod"))))                  ; &lt;など
 '(nxml-tag-slash-face
   ((t (:inherit nxml-name-face :foreground "blue")))))  ; /(終了タグ)

設定後htmlファイルを開くと、モードラインにnXMLと表示されます。そしてHTML構文に誤りが無ければValidと表示されます。

nxml-mode

また、"C-c C-s C-w"で現在どのスキーマを使用しているか表示してみると'/Applications/Emacs.app/Contents/Resources/site-lisp/nxml-mode/schema/xhtml.rnc'が使われているのがわかります。

hober/html5-elのインストール

次にDownloads for hober's html5-elのページからファイルをダウンロードして適当なディレクトリに展開します。

$ cd ~/.elisp
$ tarxvfz ~/hober-html5-el-934944b.tar.gz
$ cd html5-el
$ make relaxng
$ make webapps
$ 

インストールが完了したら、README.markdownに従って設定を行います。

(add-to-list 'load-path "~/.elisp/html5-el/")
(eval-after-load "rng-loc"
   '(add-to-list 'rng-schema-locating-files "~/.elisp/html5-el/schemas.xml"))
(require 'whattf-dt)

設定後、同じファイルを開いて使用されてるスキーマを調べると'~/.elisp/html5-el/relaxng/xhtml5.rnc'となりhtml5(xhtml5?)対応になります。

nxml-mode html5

上記の場合、xhtml1をxhtml5として解釈しているため、Invalidとなってしまっています。こういうときは"C-c C-s C-t"と打って明示的にDocument Typeを指定する事が出来ます。指定したDocument Typeはrng-schema-locating-filesに保存されるので、次回からは指定しなくてもセットされます。

Document Type

rng-schema-locating-filesを修正

まだ、xhtmlで書くことの方が多いから、デフォルトはxhtmlでhtml5を編集する場合は"C-c C-s C-t"で明示的に指定したいと言うときは、rng-schema-locating-filesに定義したファイルを修正すれば出来ると思います。rng-schema-locating-filesはhober/html5-elをインストールしたときに"~/.elisp/html5-el/schemas.xml"に設定しました。

(eval-after-load "rng-loc"
   '(add-to-list 'rng-schema-locating-files "~/.elisp/html5-el/schemas.xml"))

このファイルの中身をみると下記のように記述されています。

<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
  <uri pattern="*.html" typeId="XHTML5"/>
  <uri pattern="*.xhtml" typeId="XHTML5"/>
  <namespace ns="http://www.w3.org/1999/xhtml" typeId="XHTML5"/>
  <documentElement localName="html" typeId="XHTML5"/>
  <typeId id="XHTML5" uri="relaxng/xhtml5.rnc"/>
</locatingRules>

これを次のように記述すれば良いと思います。

<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
  <uri pattern="*.html" typeId="XHTML"/>
  <uri pattern="*.xhtml" typeId="XHTML"/>
  <namespace ns="http://www.w3.org/1999/xhtml" typeId="XHTML5"/>
  <documentElement localName="html" typeId="XHTML5"/>
  <typeId id="XHTML5" uri="relaxng/xhtml5.rnc"/>
</locatingRules>

使い方

最初使い方がわからなかったのですが、youtubeにチュートリアルな動画がありましたので、それを見て大体の使い方は理解出来ました。



主なキーアサイン

主なキーアサインをまとめておきました。

編集系のキーアサイン
キー 関数 機能
C-c C-x nxml-insert-xml-declaration xml宣言文を挿入
C-return nxml-complete タグ・属性の補完
/ nxml-electric-slash 直前の「開始タグ」に対応する「終了タグ」で補完する
C-c tab nxml-balanced-close-start-tag-inline 直前の「開始タグ」に対応する「終了タグ」を同じ行に挿入する
C-c C-b nxml-balanced-close-start-tag-block 直前の「開始タグ」に対応する「終了タグ」を改行して挿入する
C-c C-u nxml-insert-named-char HTML Character Entityを挿入する
C-c C-d nxml-dynamic-markup-word 選択文字列をカレントバッファから探し、同じタグで補完する
移動系のキーアサイン
キー 関数 機能
ESC h nxml-mark-paragraph 現在の段落を選択する
ESC } nxml-forward-paragraph 上の段落に移動する
ESC { nxml-backward-paragraph 下の段落に移動する
ESC C-p nxml-backward-element 1要素後ろに移動する
ESC C-n nxml-forward-element 1要素前に移動する
ESC C-d nxml-down-element 下の階層の要素に移動する。(下の階層があれば)
ESC C-u nxml-backward-up-element 上の階層の要素に移動する。(上の階層があれば)
検証系のキーアサイン
キー 関数 機能
C-c C-v rng-validate-mode 検証モードの切り替え
C-c C-n rng-next-error エラーが発生した箇所に移動する
C-c C-s C-w rng-what-schema 現在どのスキーマを使用しているか表示する
C-c C-s C-t rng-set-document-type-and-validate 現在開いている文章のDocument Typeをセットし検証を行う
セットしたDocument Typeはスキーマファイルに保存される
C-c C-s C-f rng-set-schema-file-and-validate 指定したスキーマファイルを読込み検証を行う
C-c C-s C-a rng-auto-set-schema-and-validate 現在開いている文章に最適なスキーマを読込み検証を行う
C-c C-s C-l rng-save-schema-location 現在使用しているスキーマファイルを保存する

2011年7月20日水曜日

64bit版emacsのwnn7eggがbackend timeoutになる件を調査してみた

emacsの64bit化を阻むもの

自宅サーバ(NetBSD 64bit)には、32bit Linux emulationでオムロンソフトウェアのWnn7が動作しています。そして、これを自宅サーバ(NetBSD)・開発マシン(Mac mini)・サブマシン(Macbook air)上のemacsから使用しています。この構成だと学習した情報をサーバー側で一元管理できるので、学習データを共有するための同期処理をどうしようかと悩む必要がなくて重宝しているのですが、以前、各マシンのemacsを64bit化しようと目論んだのだけれども、64bit化するとwnn7eggがbackend timeoutというエラーを吐いてしまい断念してしまいました。

デバッグ

まず64bit版 Emacsを"--debug"を付けて立ち上げてbackend timeoutするときのBacktraceを取って、不具合のありそうな関数に当たりをつけて32bit番の動きと違いがないか一個ずつ調べてみました。Elispのデバッグはしたことがかなったので、凄く効率が悪く面倒臭いデバッグ方法になってしまいました。

上記のやり方で調査していくと、 wnn7rpc-get-autolearning-dic関数で違いが出ることがわかりました。

  (defun wnn7rpc-get-autolearning-dic (env type)
  "Get id of auto learning dictionary on the server.
Return dictionary id + 1 on success, 0 on no dictionary, negate-encoded
error code on faiulure."
  (wnn7rpc-call-with-environment env (result)
    (comm-format (u u u) (wnn-const JS_GET_AUTOLEARNING_DIC)
                 env-id type)
    (wnn7rpc-get-result
      (comm-unpack (u) result)
      (print result)
      (1+ result))))

上記のソースのように(print result)を付加してresultの値を表示すると32bitと64bitで違いが発生します

;; 32bit Emacsで正常の場合
Wnn: connecting to jserver at foo.rfc2606.invalid.jp(22273)...done
ホスト foo.rfc2606.invalid.jp の Wnn を起動しました
Loading /Users/bar/.eggrc-wnn7...

-1

32

-1

34

Loading /Users/bar/.eggrc-wnn7...done

;; 64bit Emacsでbackend timeoutの場合
Wnn: connecting to jserver at foo.rfc2606.invalid.jp(22273)...done
ホスト foo.rfc2606.invalid.jp の Wnn を起動しました
Loading /Users/bar/.eggrc-wnn7...

4294967295

4294967295

もしかして変数のbit幅の問題?

32bit Emacsでは-1を取り、64bit Emacsでは4294967295を取る。これを16進数に直すと興味深い値が出てきます。

;; 32bit Emacs場合
(format "%x" -1)
1fffffff

(format "%x" 4294967295)
1fffffff

;; 64bit Emacs場合
(format "%x" -1)
3fffffffffffffff

(format "%x" 4294967295)
ffffffff

え〜っ!? 32bit Emacsの変数のbit幅って、29bitだったんですか! 長年Emacsを使用していたけれどしらなかった。しかも64bit Emacsは62bit? なんでこんなに中途半端なbit数なんでしょうね

上記の結果より処理結果が536870911(#x1fffffff)以上の値をとると32bit Emacsと64bit Emacsで評価内容が変わってしまう事がわかりました。

comm-unpack関数って何しているの?

次に、32bitと64bitで異なった値を返してくるcomm-unpack関数を調べてみました。comm-unpack自体は引数の値によって適切な関数を呼び出すディスパッチ処理を行い、呼び出した関数の値を返すだけの簡単な仕事しかしていませんでした。

(comm-unpack (u) result)のように引数に"u"をつけるとcomm-unpack-u32関数が呼ばれます。

comm-unpack-u32関数って何しているの?

EmacsはJServerと直接通信をしてコマンド/レスポンスのやりとりをしている訳ですが、受信したデータは*Wnn7*バッファに入ってきます。

Wnn7 バッファ

これを適切なbit長で読み出してコマンドを再構築するのが、comm-unpack関数で受信したレスポンスを32bitにアンパックしてする場合にcomm-unpack-u32関数が呼ばれます。

(defun comm-unpack-u32 ()
  (progn
    (comm-require-process-output 4)
    (+ (lsh (comm-following+forward-char) 24)
       (lsh (comm-following+forward-char) 16)
       (lsh (comm-following+forward-char) 8)
       (comm-following+forward-char))))

この時に、0xffffffffというデータを受信していると32bit Emacsと64bit Emacsで結果が変わってしまいます。

では、どんな値が*Wnn7*バッファ入ってきているのでしょう?ダンプしてみました

Wnn7 バッファ

はい、見事に0xffffffffを受信しています。ありがとうございました。

原因

  1. 32bit Emacsの整数bit幅は29bit
  2. 62bit Emacsの整数bit幅は62bit
  3. jserverから0xffffffffを受信すると
  4. 32bit Emacsは-1と評価する。
  5. 62bit Emacsは4294967295と評価する。
  6. 62bit Emacsの場合、不明なレスポンスしかこないので、backend timeoutで落ちる。

対策

原因が判明したので、comm-unpack-u32関数を修正して整数が29bit幅の以外の時は、変数の値を62bit幅に拡張する処理を入れてあげればよいと思われる。整数のbit幅が他の値をとることはあるのかな?考えないことにしておく。

wnn7egg-edep.el

Emacsの依存性があるコードはここに書かれるようなので、下記のコードを追加

(defconst interger_width_29bit (logxor (lsh (lsh 1 29) -29) 1))

interger_width_29bitは整数が29bit幅ならば1、29bit幅以上ならば0を取ります。

wnn7egg-com.el

comm-unpack-u32関数を下記のように修正。

(defun comm-unpack-u32 ()
  (progn
    (comm-require-process-output 4)
    (if (= interger_width_29bit 1)
        (+ (lsh (comm-following+forward-char) 24)
           (lsh (comm-following+forward-char) 16)
           (lsh (comm-following+forward-char) 8)
           (comm-following+forward-char))
      ;; for 62bit width
      (ash (lsh (+ (lsh (comm-following+forward-char) 24)
                   (lsh (comm-following+forward-char) 16)
                   (lsh (comm-following+forward-char) 8)
                   (comm-following+forward-char)) 30) -30))))

変数の値が62bit幅の場合は、31bit目のデータを符号ビットの場所までシフトしてから、算出シフトで元の位置まで戻してあげます。(ソースでは30bitシフトしていますが、33bitシフトの方がよいかもしれません。)

ソースとパッチ

ソースとパッチをおいておきます。ご自由にお使いください。

  1. wnn7egg-edep.el
  2. wnn7egg-com.el
  3. wnn7-egg-backend-timeout.patch

2011年6月10日金曜日

emacsのフレームサイズを画面解像度によって動的に変更する方法

メインの開発環境はmac mini+FlexScanL885なのだが、外出先などではMacbook air 11インチを使用している。emacsのデフォルト フレーム サイズをFlexScanL885にあわせるとMacbook airでは、はみ出てしまう。逆にMacbook airにあわせると、FlexScanL885では狭すぎて開くたびにリサイズするなんてことになる。

じゃあ解像度に合わせて、動的に開くフレームサイズを決めれば良いんじゃねぇ。ということで下記の設定を書いてみた。

;; 画面の解像度によりフレームサイズを変化させる
(when window-system
  (if (>= (x-display-pixel-width) 800) (setq width-gain 0.45) (setq width-gain 0.8))
  (if (>= (x-display-pixel-height) 1000) (setq height-gain 0.7) (setq height-gain 0.87))
  (set-frame-size (selected-frame)
                  (floor (/ (* (x-display-pixel-width) width-gain) (frame-char-width)))
                  (floor (/ (* (x-display-pixel-height) height-gain) (frame-char-height))))

横の解像度が800px以下の場合は、横のフレームサイズを ((横解像度*0.8)/1文字の横ピクセル数)、横の解像度が800px以上の場合は ((横解像度*0.45)/1文字の横ピクセル数) としている。

また、縦の解像度が1000px以下の場合は、縦のフレームサイズを ((縦解像度*0.87)/1文字の縦ピクセル数)、縦の解像度が1000px以上の場合は ((縦解像度*0.7)/1文字の縦ピクセル数) としている。