Google Code Prettify

2010年5月26日

Cocoa Emacs のフレームをSpaces のワークスペースの上下左右に飛ばす。

個人的な使い方の問題だろうが、emacs に凝っていた頃は仮想ディスクトップ毎にフレームを置いた使い方をしていた。

この使い方の要は、elisp からフレームを任意の位置、任意のワークスペースに移動できる事であった。

既に MacOSX では仮想ディスクトップが Spaces としてサポートされている。 位置移動はできるようだが、ワークスペースの移動はどうもわからない。。。

マウスでチマチマ動かすのは、ちょっといただけない。。。

どうしたもんだろう?

Quartz のなんか

MacOSX の仮想ディスクトップは Leopard で初めてお目見えしたが、 結構前から Window Server (Quartz Compositor/Core Graphics Services/Quartz Window Services...一体いくつの名称があるんだか。。。)には実装されていたそうである。 Tiger の時代から仮想ディスクトップがサードパーティのものがあったのはそういう訳らしい。

で、コイツのAPIのほとんどが今でも非公開になっている。。。断片的には、スクリーンキャプチャ用途レベルのものが公開されている。。。なんとも厄介。

先人が調べ尽くした結果として、最後のリンクの先の内容が、 特定のウィンドウを任意のワークスペースに移動する方法らしい。

模式コードは次のようになる。(CGSPrivate.hは一番目のリンク先にあるもの)

#import <CGSPrivate.h>
...
NSWindow *win = ...;
CGSConnection cid = _CGSDefaultConnection();
CGSWindow wid = [win windowNumber]; // ?
CGSMoveWorkspaceWindowList(cid, &wid, 1, workspace);

CGSを解析した作られた CGSPrivate.h でのCGSWindow と Appleから出ているドキュメントに出てくる CGWindowID とは window server 内で割り振られた番号で同じ実体と思われるが、 NSWindow の windowNumber のドキュメントには、それとは違うと書かれている。 混ぜても動いたから多分良いかと思われるが、、、ここいらはぼかしたい内容なのかなぁ。。。分からんなぁ林檎は。

まぁ、ここまで分かれば、出来たも同然〜。

で、適当にコマンドを作ってみた。

cgsutil.m

Emacs.appの改造

(frame-parameters)には window-id があるから、Cocoa Emacs では多分コイツがフレームウィンドウの CGWindowID のはずで、コイツと飛ばしたい workspace 番号を適当に作ったコマンドに渡して、お仕舞いかなぁ。

わぁーい。らくちん。

はぅ。。。

Cocoa Emacs では window-id の値は、1から単調増になるように割り振った全く意味の無い番号でした。

仕様が無いので、次の追加機能を付けるパッチ(emacs-23-cgs-workspace.diff) を作ってみた。

  • フレームパラメータns-window-idに、CGWindowIDと思われる値
  • フレームパラメータns-workspace-idに、フレームのworkspace番号
  • フレームを指定のworkspaceに移動する関数 (set-frame-ns-workspace FRAME WORKSPACE)

まぁ、最初の一つだけで十分だが、後二つは組込まなくてもいいかも。

まとめ

上のパッチを適用した Emacs.app に適当な elispを書くと、1コマンドで複数のフレームを特定のワークスペース/特定の位置に開く事が出来るようになった。

screenshot-2010-05-26 17.54.51

うん〜〜。快適じゃ。

はぅ。。。

Cocoa Emacs で8フレームも開くと、キー入力が引っ掛かるようになり、Emacsプロセス自体のCPU利用率もイマイチ高い。イベント処理方法にまだ難があるのかなぁ。

4フレームくらいで抑えないとスムーズに使えないかなぁ。。。

追記 (2010/06/16)

複数のフレームを開いたときCPU利用率が高くなるのは、本パッチ内のframe-paramaters にワークスペース番号を追加するのが原因だった。。。frame-parameters の取得は不随に起きるらしくのでどうも軽くなくては行けない。

なので、workspace番号は取得関数から得るように変更し、workspace 絡みの部分を分けたパッチにしてみた。

  1. emacs-23-cg-window-id.diff
    • フレームパラメータns-window-idに、CGWindowIDと思われる値
  2. emacs-23-cgs-workspace.diff
    • 指定フレームのworkspaceを取得する関数 (frame-ns-workspace FRAME)
    • フレームを指定のworkspaceに移動する関数 (set-frame-ns-workspace FRAME WORKSPACE)

ふむ。
8フレームを開いても、キー入力が引っ掛かる症状は無くなった!!
すごく快適〜ん。

パッチを切ったり張ったり入れ替えたりする(1)のに、git rebase -i は極めて便利。git 優秀だなぁ

2010年5月18日

Emacs で文法チェック

Emacs でプログラムを書く人々は、リアルタイムに文法チェックをするために flymake を使うらしい。

以前聞き覚えがあるが、何をする物か分からないのでスルーした奴だが、結構使えるものだったのかぁ。

なので flymake の設定をしてみた。

Quick Start

詳細は info マニュアルに書かれているが、 使い始めの設定は次の通りになる。この状態で、何もせずに Perl/PHP の文法チェックは行なえる(perl/php コマンドがあればの話だが)。

(require 'flymake)

;; GUIの警告は表示しない
(setq flymake-gui-warnings-enabled nil)

;; 全てのファイルで flymakeを有効化
(add-hook 'find-file-hook 'flymake-find-file-hook)

;; M-p/M-n で警告/エラー行の移動
(global-set-key "\M-p" 'flymake-goto-prev-error)
(global-set-key "\M-n" 'flymake-goto-next-error)

;; 警告エラー行の表示
(global-set-key "\C-cd" 'flymake-display-err-menu-for-current-line)

次に C/C++ で文法チェックを可能にするには、 Makefile を使い、次の様な check-syntax ルールを追加すればよい。

PHONY: check-syntax
check-syntax:
    $(CC) -Wall -Wextra -pedantic -fsyntax-only $(CHK_SOURCES)

ここまですると、編集途中にリアルタイムに文法エラー/警告の部分に色が付き、分かり易い。

なるほど、、、これは素晴らしい。

以下の設定は、好きずきだと思う

警告エラー行の表示のカスタマイズ

警告エラー行の表示は、 プラットホーム毎のポップアップメニュー実装が使われるが、 色々検索すれば出てくるが、2通りのカスタマイズが良く知られている。

  1. Minibufに表示する。
  2. popup.el(auto-complete に含まれている)のpopup-tipsで表示する。

;; Minibuf に出力
(defun my-flymake-display-err-minibuf-for-current-line ()
  "Displays the error/warning for the current line in the minibuffer"
  (interactive)
  (let* ((line-no            (flymake-current-line-no))
         (line-err-info-list (nth 0 (flymake-find-err-info flymake-err-info line-no)))
         (count              (length line-err-info-list)))
    (while (> count 0)
      (when line-err-info-list
        (let* ((text       (flymake-ler-text (nth (1- count) line-err-info-list)))
               (line       (flymake-ler-line (nth (1- count) line-err-info-list))))
          (message "[%s] %s" line text)))
      (setq count (1- count)))))

;; popup.el を使って tip として表示
(defun my-flymake-display-err-popup.el-for-current-line ()
  "Display a menu with errors/warnings for current line if it has errors and/or warnings."
  (interactive)
  (let* ((line-no            (flymake-current-line-no))
         (line-err-info-list (nth 0 (flymake-find-err-info flymake-err-info line-no)))
         (menu-data          (flymake-make-err-menu-data line-no line-err-info-list)))
    (if menu-data
      (popup-tip (mapconcat '(lambda (e) (nth 0 e))
                            (nth 1 menu-data)
                            "\n")))
    ))

僕としては popup.el を使うのが結構好み

check-syntaxターゲットルール

先に述べて Makefile の check-syntaxターゲットルールでは一種類の文法チェックプログラムしか使えない。。。 gcc でチェックできるものは全てチェック出来るから、気を回す必要は無いかもしれない。

GNU make を使えば、拡張子毎に文法チェックプログラムを選べるルールの書き方がある。

CHECKSYNTAX.c = $(CC) $(CFLAGS) $(CPPFLAGS) -Wall -Wextra -pedantic -fsyntax-only
CHECKSYNTAX.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -Wall -Wextra -pedantic -fsyntax-only

check-syntax: $(addsuffix -check-syntax,$(CHK_SOURCES))

%.c-check-syntax:
	$(CHECKSYNTAX.c) $*.c

%.cc-check-syntax:
	$(CHECKSYNTAX.cc) $*.cc

まぁ、ここまで書く奴はいないかぁ。。。

Makefileが無くてもC/C++の文法チェック

Makefileが無ければ、直接 gcc で文法チェックをし、 Makefileがあればcheck-syntaxターゲットルールを使いたい場合は、 次の設定をすれば良い。

(defun flymake-simple-generic-init (cmd &optional opts)
  (let* ((temp-file  (flymake-init-create-temp-buffer-copy
                      'flymake-create-temp-inplace))
         (local-file (file-relative-name
                      temp-file
                      (file-name-directory buffer-file-name))))
    (list cmd (append opts (list local-file)))))

;; Makefile が無くてもC/C++のチェック
(defun flymake-simple-make-or-generic-init (cmd &optional opts)
  (if (file-exists-p "Makefile")
      (flymake-simple-make-init)
    (flymake-simple-generic-init cmd opts)))

(defun flymake-c-init ()
  (flymake-simple-make-or-generic-init
   "gcc" '("-Wall" "-Wextra" "-pedantic" "-fsyntax-only")))

(defun flymake-cc-init ()
  (flymake-simple-make-or-generic-init
   "g++" '("-Wall" "-Wextra" "-pedantic" "-fsyntax-only")))

(push '("\\.[cC]\\'" flymake-c-init) flymake-allowed-file-name-masks)
(push '("\\.\\(?:cc\|cpp\|CC\|CPP\\)\\'" flymake-cc-init) flymake-allowed-file-name-masks)

まとめ

取り敢えず、次いでに ruby/bash の文法チェックを追加したのが、 今現在の設定になっている。

45flymake.el

あとは Python/Java/HTML/CSS をチェックするのがあれば良いが、、、 これくらい素のflymakeに含まれていればいいのになぁ。。。

あとSemanticかぁ。。。

追記 (2010/05/24)

暫く使ってみたが、基本動作で2点だけイマイチな挙動を修正しないと、ストレスが溜まりまくる。

  1. ファイルもしくは上位ディレクトリに書き込み権限が無い場合、messageを吐いて開くのを諦めてしまう。。。
  2. 文法チェックプログラムが無い場合 flymake がオフになるが、それでも何かのプロセス?が残って、後々無意味な y の連打をしないといけない。。。
これも、素のflymakeで用意してくれれば良いのに、、、で場当たり的に次の設定を追加した。。。

;; flymake を使えない場合をチェック
(defadvice flymake-can-syntax-check-file
  (after my-flymake-can-syntax-check-file activate)
  (cond
   ((not ad-return-value))
   ;; tramp 経由であれば、無効
   ((and (fboundp 'tramp-list-remote-buffers)
         (memq (current-buffer) (tramp-list-remote-buffers)))
    (setq ad-return-value nil))
   ;; 書き込み不可ならば、flymakeは無効
   ((not (file-writable-p buffer-file-name))
    (setq ad-return-value nil))
   ;; flymake で使われるコマンドが無ければ無効
   ((let ((cmd (nth 0 (prog1
                          (funcall (flymake-get-init-function buffer-file-name))
                        (funcall (flymake-get-cleanup-function buffer-file-name))))))
      (and cmd (not (executable-find cmd))))
    (setq ad-return-value nil))
   ))

追記 (2010/05/25)

上の修正は、ちょっと上手く行かない場合があったので修正っと。

追記 (2010/05/27)

tramp経由の場合も、イマイチ上手く動かないので、除外に加えてみた。

2010年5月13日

Emacs Refcard 日本語版

Emacs のチートシートは、Emacs と一緒に配布されている。 refcard.tex から pdfを作って印刷すれば良い。 少し古いバージョンだが、日本語に翻訳されたものは「Emacs-20/21 Reference Card 」にある。

キー操作として、手に染み付いているものは半分くらいしか無い。。。 大抵 M-x コマンドで済まして場合が多いなぁ。

いかんなぁ。。。

再度覚え直す為に、先の日本語RefcardとEmacs23に入ってるrefcardと若干漏れている部分を補い、 一部語句を修正し、フォント関係を微調節してものを作ってみた。

refcardja.tex

次いでに ptex/dvipdfmx を使って PDF を作って、Google Docs に上げてみた。

refcardja.pdf

まとめ

うん、久しぶりに素のTeXを弄ったが本当にややこしい奴だな。

久しぶりの投稿

かなり期間が空いてしまったが、ブログを再開してみようと思う。 2013年3月が直前の投稿だったが、頻繁に更新していた時期が 2011年11月までなので、8年間ぶりとなる。 8年間なにをしていたのかと言えば、2回転職して未だにIT技術者の職を得ている。 その...