おもしろく生きたいね

「Tech」カテゴリの記事一覧

WEB+DB PRESS Vol.46WEB+DB PRESS Vol.46で取り上げたGNU diff(1)のラッパー、「di」をGitHubに置いた。GNU diffには多彩なオプションが用意されているが、POSIXなどの規格の縛りもありデフォルト設定が日常利用には不便なものになっている。いちいちdiff -Nrpu dir1 dir2なんて面倒くさい。以下の特長を持つラッパーdiで楽をしよう。

  • -U3 -N -p -dがデフォルト。
  • 出力が色付き。ページャが起動される。(Gitのまね)
  • バックアップファイルやオブジェクトファイル、.git, .hg, .svn, CVSなどのディレクトリを無視する。
  • RCS/CVSキーワードタグの違いを無視する。
  • ディレクトリを与えれば自動で再帰比較。
  • 上記を含むデフォルトのオプション設定は環境変数で変更可能。
  • GNU diffがサポートしないオプションの組み合わせもサポート。

alias di="diff -ru"でいいんじゃないの?」という人は、理不尽におこられた経験がないのかもしれない。context diffがほしくなってdi -c FILE1 FILE2とすると、展開されてこうなる。

% diff -ru -c FILE1 FILE2
diff: conflicting output style options
diff: Try `diff --help' for more information.

黙って後から指定した方を採用してくれればいいのに、まったく余計なお世話だ。(FreeBSDではこのエラーは抑止されている)
そして、diff FILE1 DIRは動くのになぜかdiff FILE1 FILE2 DIRはエラーになるのも不便だ。

% diff FILE1 FILE2 DIR
diff: extra operand `DIR'
diff: Try `diff --help' for more information.

diだとちゃんと動く。ほかにも、-r-D NAMEが組み合わせられない問題も自力でディレクトリを辿ることで回避している。
インストールは、単にbin/diをパスの通ったところに置くか、またはgem install diでOK。動作にはRubyが必要。
弱点は大きなツリーの比較が遅いこと。GNU diffは-x/--excludeを使ってもファイル名でしか除外できず、二つのファイルリストを渡して比較するということもサポートしていないため、自力でディレクトリ探索を行う場合はディレクトリごとにdiff(1)を複数回起動しなければならない。しかし、並列起動で高速化する余地はあるので今後の課題。
もう一つ、--ignore-file-name-caseをサポートしていないが、これは面倒な割には使いたいと思ったことがないため。それだけなので、気が向けば実装するかも。あるいは切実にほしいあなたが実装するかも。


Emacsでカーソル移動、削除、マクロ実行などを繰り返したいときに手軽なのがC-uC-uの後に数字を入力すればその回数になるが、単にC-uだと4回、C-u C-uと2度押すと16回、3度押すと64回、という具合に、n度押すと4n回繰り返される。数字キーに手を伸ばさなくていいのですばやく操作できる。
しかし。4の次が16って飛びすぎじゃね?4回というのは「目の届く」「小さい範囲の」繰り返し回数として適度だが、16回となるといきなり「だいたいこのあたりぜんぶ」「もっとたくさん」といった鷹揚なニュアンスである。昔から「いち、にい、たくさん」と言うではないか。ナベアツもイチ、ニ、サン!で初めてアホになる。
というわけで、4, 8, 16, 64, 256, …のように8を挟むようにしてみた。

(defadvice universal-argument-more
  (before stop-by-eight-before-sixteen (arg) activate)
  "Stop by eight before sixteen."
  (let ((num (car arg)))
    (if (> 16 num) (ad-set-arg 0 (list (/ num 2))))))

使ってみると、これがけっこうしっくり来るんだな。C-u C-uだと多すぎだからC-u+コマンドを2回、というシーンはけっこうあるので、4から16と急加速する前に8を。スペースを8個削るとか、8行分コピーするとか、なかなか手頃なサイズです。
このなかなか表面化しないフラストレーションを言葉にしてくれた同僚のK君に感謝。


そろそろ自分でいろいろプラグインを書くのが面倒になってきたので、日記のシステムをWordPressに移行した。tDiaryにはまともな(ちゃんとtDiaryのフレームワークを使った)MTエクスポーターが見当たらず、書く気力も湧かずに放置していたのだが、ちょうど仕事のプロジェクトの成果が流用できそうなのでやってみたらほどなくできた。(笑)

記事の整形(ERBテンプレートの展開)方法がなかなか分からなかったのでtDiaryのsqueezeプラグインの出力をスクレイピングして使ったが、eval_rhtmlはどうやって呼ぶのが正解なんだろう。まあ、タグの閉じ忘れ等ではまったけど、直せたのでよしとしよう。

MT形式ファイルの出力はできたが、permalinkの移行で引っ掛かった。WordPressのMTインポーターがBASENAMEを見てくれなくてしょんぼり(´・ω・`)。手を入れて移行し、WordPressのTracにチケットを submitしておいた。これで、外部からのリンク用にRewriteRuleは書いてあるけど、内部(記事間リンク)ではもうすべて移行後のURLになっています。

最後にいちばん大変だったのはコメントの付け替え!日付にぶらさがるtDiaryのコメントをセクションに振り分けるのがしんどかった。「Move Commentsプラグイン」にちょっと手を入れてひたすら手動で選別…。疲れました。

はぁ、懸案が片付いてすっきりした!


 SubversionにあってGitに足りないものはまだいろいろあるが、頻繁にイライラするのがsvn info相当の欠如。git config -lは読みづらいし情報が足りない。ちょくちょくp ../../.git/config(p$PAGERのalias)とかやりながら悶々としているのも嫌になってgit-infoを書いた。見ての通りコードはGitHubに置いたからいいと思ったら使ったりいじったりしてみて。インストールは一般ユーザで簡単。出力はこんな感じです。

~/src/github/git-info% git info
Repository Path: /home/knu/src/github/git-info/.git
Path: /home/knu/src/github/git-info
Remote Repositories:
	origin	git@github.com:knu/git-info.git
Remote Branches:
	  origin/master
Local Branches:
	* master
Repository Configuration:
	[core]
		repositoryformatversion = 0
		filemode = true
		bare = false
		logallrefupdates = true
	[remote "origin"]
		url = git@github.com:knu/git-info.git
		fetch = +refs/heads/*:refs/remotes/origin/*
Last Changed Commit ID: f4cc42ae422a25cc1032506fe248ba8f375ecf22
Last Changed Author: Akinori MUSHA <knu@idaemons.org>
Last Changed Date: Thu Jun 25 23:19:59 2009 +0900
Last Changed Log:
	Add git-pager and git-info.

 KeyRemap4MacBookはOS X用キーボードカスタマイザの決定版ですが、窓使いの憂鬱からの移民としてはとにかくいじりがいがあるので、手元でいろいろ設定を追加しています。その中でも汎用性が高いと思われるやつをひとつ紹介。

 US配列で右Commandキーの右にある「Enter」は、なかなかいい場所にあるのに使用頻度の低いかわいそうな子。KeyRemap4MacBookでも様々なキーへのマッピングが用意されていますが、僕はOptionキーにしています。Optionはメタキーとして欧文文字の入力に使いますが、ギリシャ文字や学術記号等、大して必要のないものも多いので、デスクトップ操作等のグローバルショートカットに活用しています。

 しかし、EnterをOptionにするだけではもったいない。日本語使いがキーボードを使っていて何がまどろっこしいって、入力モードの切り替えの面倒さ。OS XのCommand+Spaceはとてもいい組み合わせだけど、両の親指を使うのが気に食わない。そこで、Enter単独押しでモード切替できたらいいんじゃね?というパッチがこれ。

diff -r f0c59438beaf files/prefpane/sysctl.xml
--- a/files/prefpane/sysctl.xml	Thu Feb 21 11:23:13 2008 +0900
+++ b/files/prefpane/sysctl.xml	Fri Feb 22 16:15:20 2008 +0900
@@ -83,6 +83,12 @@
         <item>
           <name>Enter to Option_L</name>
           <sysctl>keyremap4macbook.remap.enter2optionL</sysctl>
+          <list>
+            <item>
+              <name>Non-modifier Enter to Command_L+Space</name>
+              <sysctl>keyremap4macbook.option.enter2optionL_commandspace</sysctl>
+            </item>
+          </list>
         </item>
         <item>
           <name>Enter to Command_L+Control_L</name>
diff -r f0c59438beaf src/kext/config.cpp
--- a/src/kext/config.cpp	Thu Feb 21 11:23:13 2008 +0900
+++ b/src/kext/config.cpp	Fri Feb 22 16:15:20 2008 +0900
@@ -76,6 +76,9 @@ namespace org_pqrs_KeyRemap4MacBook {
   SYSCTL_INT(_keyremap4macbook_remap, OID_AUTO, enter2optionL, CTLTYPE_INT|CTLFLAG_RW,
              &(config.remap_enter2optionL), 0, "");
 
+  SYSCTL_INT(_keyremap4macbook_option, OID_AUTO, enter2optionL_commandspace, CTLTYPE_INT|CTLFLAG_RW,
+              &(config.option_enter2optionL_commandspace), 0, "");
+ 
   SYSCTL_INT(_keyremap4macbook_remap, OID_AUTO, enter2commandLcontrolL, CTLTYPE_INT|CTLFLAG_RW,
              &(config.remap_enter2commandLcontrolL), 0, "");
 
@@ -370,7 +373,10 @@ namespace org_pqrs_KeyRemap4MacBook {
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2commandL);
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2controlL);
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2fn);
+
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2optionL);
+    sysctl_register_oid(&sysctl__keyremap4macbook_option_enter2optionL_commandspace);
+
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2commandLcontrolL);
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2commandLshiftL);
     sysctl_register_oid(&sysctl__keyremap4macbook_remap_enter2semicolon);
@@ -515,7 +521,10 @@ namespace org_pqrs_KeyRemap4MacBook {
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2commandL);
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2controlL);
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2fn);
+
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2optionL);
+    sysctl_unregister_oid(&sysctl__keyremap4macbook_option_enter2optionL_commandspace);
+
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2commandLcontrolL);
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2commandLshiftL);
     sysctl_unregister_oid(&sysctl__keyremap4macbook_remap_enter2semicolon);
diff -r f0c59438beaf src/kext/config.hpp
--- a/src/kext/config.hpp	Thu Feb 21 11:23:13 2008 +0900
+++ b/src/kext/config.hpp	Fri Feb 22 16:15:20 2008 +0900
@@ -31,7 +31,10 @@ namespace org_pqrs_KeyRemap4MacBook {
     int remap_enter2commandL;
     int remap_enter2controlL;
     int remap_enter2fn;
+
     int remap_enter2optionL;
+    int option_enter2optionL_commandspace;
+
     int remap_enter2commandLcontrolL;
     int remap_enter2commandLshiftL;
     int remap_enter2semicolon;
diff -r f0c59438beaf src/kext/keycode.hpp
--- a/src/kext/keycode.hpp	Thu Feb 21 11:23:13 2008 +0900
+++ b/src/kext/keycode.hpp	Fri Feb 22 16:15:20 2008 +0900
@@ -156,6 +156,7 @@ namespace org_pqrs_KeyRemap4MacBook {
   namespace CharCode {
     enum {
       CONTROL_L = 0,
+      COMMAND_L = 0,
       FN = 0,
       TAB = 9,
       RETURN = 13,
diff -r f0c59438beaf src/kext/remap.cpp
--- a/src/kext/remap.cpp	Thu Feb 21 11:23:13 2008 +0900
+++ b/src/kext/remap.cpp	Fri Feb 22 16:15:20 2008 +0900
@@ -122,7 +122,35 @@ namespace org_pqrs_KeyRemap4MacBook {
   {
     if (! config.remap_enter2optionL) return;
 
-    RemapUtil::keyToModifier(params, RemapUtil::getEnterKeyCode(params), ModifierFlag::OPTION_L);
+    KeyCode::KeyCode enterKeyCode = RemapUtil::getEnterKeyCode(params);
+
+    if (config.option_enter2optionL_commandspace) {
+      static bool useEnterAsOption = false;
+
+      if (params.ex_origKey != enterKeyCode && *(params.eventType) == KeyEvent::DOWN) {
+        useEnterAsOption = true;
+      }
+
+      if (params.ex_origKey == enterKeyCode) {
+        // Enter => OptionL (if type EnterKey only, works as EnterKey)
+        unsigned int origEventType = *(params.eventType);
+        RemapUtil::keyToModifier(params, enterKeyCode, ModifierFlag::OPTION_L);
+
+        if (origEventType == KeyEvent::DOWN) {
+          useEnterAsOption = false;
+
+        } else if (origEventType == KeyEvent::UP) {
+          if (useEnterAsOption == false) {
+            listFireExtraKey.add(FireExtraKey::TYPE_AFTER, KeyEvent::MODIFY, ModifierFlag::COMMAND_L, KeyCode::COMMAND_L, CharCode::COMMAND_L);
+            listFireExtraKey.add(FireExtraKey::TYPE_AFTER, KeyEvent::DOWN,   ModifierFlag::COMMAND_L, KeyCode::SPACE,     CharCode::SPACE);
+            listFireExtraKey.add(FireExtraKey::TYPE_AFTER, KeyEvent::UP,     ModifierFlag::COMMAND_L, KeyCode::SPACE,     CharCode::SPACE);
+            listFireExtraKey.add(FireExtraKey::TYPE_AFTER, KeyEvent::MODIFY, 0,                       KeyCode::COMMAND_L, CharCode::COMMAND_L);
+          }
+        }
+      }
+    } else {
+      RemapUtil::keyToModifier(params, enterKeyCode, ModifierFlag::OPTION_L);
+    }
   }
 
   void

 親指でタン、と叩いて切り替えるのは昔からのお気に入りで、mayuではこうしていました。

    mod alt            -= RAlt
    key *RAlt           = $ToggleIME
    mod alt            += !!RAlt

 Kinesis Contoured Keyboardでは、右CtrlをEnterにマッピングして、上記の設定で使っています。快適快適。


エンコーディング/ロカール判定をまともにしてみた。[2007-11-06改訂]

Index: NSStringITerm.m
===================================================================
RCS file: /cvsroot/iterm/iTerm/NSStringITerm.m,v
retrieving revision 1.8
diff -u -r1.8 NSStringITerm.m
--- NSStringITerm.m	13 Nov 2006 08:01:04 -0000	1.8
+++ NSStringITerm.m	6 Nov 2007 09:24:28 -0000
@@ -294,9 +294,92 @@
return [NSString stringWithFormat:@"%d", num];
}
++ (BOOL)isCJKEncoding:(NSStringEncoding)encoding
+{
+    static NSMutableDictionary *isEncodingCJK = nil; // cache for encoding to isCJK mapping
+    static NSStringEncoding previousEncoding = 1; // ASCII
+    static BOOL isCJK = NO;
+    NSNumber *key, *val;
+    const char *lang;
+
+    if (encoding == previousEncoding) {
+        //NSLog(@"encoding[0x%08lx] is %s, again", encoding, isCJK ? "CJK" : "not CJK");
+        return isCJK;
+    }
+
+    previousEncoding = encoding;
+
+    key = [NSNumber numberWithUnsignedInt:encoding];
+
+    if (isEncodingCJK == nil) {
+        isEncodingCJK = [[NSMutableDictionary alloc] init];
+    }
+    else {
+        val = [isEncodingCJK objectForKey:key];
+
+        if (val != nil) {
+            isCJK = [val boolValue];
+            //NSLog(@"encoding[0x%08lx] is %s, IIRC", encoding, isCJK ? "CJK" : "not CJK");
+            return isCJK;
+        }
+    }
+
+    switch (encoding) {
+      // Simplified Chinese
+      case 0x80000019: // Mac
+      case 0x80000421: // Windows
+      case 0x80000631: // GBK
+      case 0x80000632: // GB 18030
+      case 0x80000930: // EUC
+      // Traditional Chinese
+      case 0x80000002: // Mac
+      case 0x80000423: // Windows
+      case 0x80000931: // EUC
+      case 0x80000A03: // Big5
+      case 0x80000A06: // Big5 HKSCS
+      // Japanese
+      case 0x00000003: // EUC
+      case 0x00000008: // Windows
+      case 0x00000015: // ISO-2022-JP
+      case 0x80000001: // Mac
+      case 0x80000628: // Shift JIS X0213
+      case 0x80000A01: // Shift JIS
+      // Korean
+      case 0x80000003: // Mac
+      case 0x80000422: // Windows
+      case 0x80000840: // ISO-2022-KR
+      case 0x80000940: // EUC
+        isCJK = YES;
+        //NSLog(@"0x%08lx is known to be %s", encoding, isCJK ? "CJK" : "not CJK");
+        break;
+
+      case 0x00000004: // UTF-8
+        isCJK = ((lang = getenv("LC_ALL")) != NULL ||
+                 (lang = getenv("LC_CTYPE")) != NULL ||
+                 (lang = getenv("LANG")) != NULL) &&
+            strlen(lang) >= 3 &&
+            (!strncmp(lang, "ja_", 3) ||
+             !strncmp(lang, "kr_", 3) ||
+             !strncmp(lang, "zh_", 3));
+        //NSLog(@"locale[%s] looks %s", lang, isCJK ? "CJK" : "not CJK");
+        break;
+
+      default:
+        isCJK = NO;
+        //NSLog(@"encoding[0x%08lx] is not known to be CJK", encoding);
+        break;
+    }
+
+    // Store in cache
+    val = [NSNumber numberWithBool:isCJK];
+    [isEncodingCJK setObject:val forKey:key];
+
+    return isCJK;
+}
+
+ (BOOL)isDoubleWidthCharacter:(unichar)unicode encoding:(NSStringEncoding) e
{
-	if (unicode <= 0xa0 || (unicode>0x452 && unicode <0x200f))
+	if (unicode <= 0xa0 || (unicode>0x452 && unicode <0x1100))
return NO;
/*
unicode character width check
@@ -325,11 +408,7 @@
/* Ambiguous ones */
-	if ((e)==0x80000019||(e)==0x80000421||(e)==0x80000631||(e)==0x80000632||(e)==0x80000930 || //GB
-		(e)==0x80000002||(e)==0x80000423||(e)==0x80000931||(e)==0x80000a03||(e)==0x80000a06 || //BIG5
-		(e)==0x80000001||(e)==0x8||(e)==0x15 || //JP
-		(e)==0x80000628||(e)==0x80000a01 || //SJIS
-		(e)==0x80000422||(e)==0x80000003||(e)==0x80000840||(e)==0x80000940) //KR
+	if ([self isCJKEncoding:e])
{
if ((unicode >=0xfe00 && unicode <=0xfe0f) ||
(unicode >=0x2776 && unicode <=0x277f) ||

これならsubmit可能か。ただ、描画部分でASCII/non-ASCIIフォントの選択を誤るのでまだ不完全。また時間が取れたら見ます。

あと、下線が倍幅に対応していない。

Index: PTYTextView.m
===================================================================
RCS file: /cvsroot/iterm/iTerm/PTYTextView.m,v
retrieving revision 1.307
diff -u -r1.307 PTYTextView.m
--- PTYTextView.m	17 Jun 2007 01:56:31 -0000	1.307
+++ PTYTextView.m	6 Nov 2007 02:07:04 -0000
@@ -963,7 +963,7 @@
 					//draw underline
 					if (theLine[j].fg_color & UNDER_MASK && theLine[j].ch) {
 						[[self colorForCode:(fgcode & 0x1ff)] set];
-						NSRectFill(NSMakeRect(curX,curY-2,charWidth,1));
+						NSRectFill(NSMakeRect(curX,curY-2,double_width?charWidth*2:charWidth,1));
 					}
 				}
 			}

ほかにも、あんまり致命的ではないが特定のケースでカーソル移動によって文字の右半分が欠けたりする。まあ、ソースがあるからいつか捕まえて直せるだろう。Terminal.appもソースを公開してほしいね。


 LeopardのTerminal.appをしばらく使っているうちに、こいつは使えない代物だと分かった。

 まず、「○」とか「△」とかの記号を倍幅文字と認識してくれない。これは再描画しても直らないのでTigerのTerminal.appよりひどい。もしかすると、日本語フォントの記号グリフに沿った文字幅情報を含むエンコーディング定義を追加することできれいに対処できるのかもしれないが、その方法は未調査。ていうか、こんなのデベロッパープレビュー段階で気づかなかったんだろうか。Appleには日本人ハッカーはいないの?

 二番目に、CommandキーをMetaキーとして使えないのが不便。TigerではAPE+Cmd2Optでなんとかなったが、今のところLeopardには対応していない。ていうかCmd2Optの配布物ってWeb Archive等を漁らないと手に入らなかった気がする。なんにせよ、Emacs使いにとってMetaキーが左下のちっこいOptionキーひとつというのは致命的だ。小指をつって死ねと言うに等しい。小指が休職したら、Ctrl+[で亀のようなもたもた人生を遅れというのか。

 あと、ANSIカラーを調整できないので目に優しくない。頼みの綱はTerminalColorsだが、作者のサイトを見るとLeopardのTerminal.appに対応するのは当分先になりそうだ。

 どうにかしようにもソースがない。じゃあソースがあるものをいじろう、ということでiTermに手を入れました。まず、第一の記号問題を解決するパッチがこれ。

Index: NSStringITerm.m
===================================================================
RCS file: /cvsroot/iterm/iTerm/NSStringITerm.m,v
retrieving revision 1.8
diff -u -r1.8 NSStringITerm.m
--- NSStringITerm.m	13 Nov 2006 08:01:04 -0000	1.8
+++ NSStringITerm.m	5 Nov 2007 14:04:20 -0000
@@ -286,6 +286,8 @@
 	0xfffd,
 };
 
+static int isJapaneseEnvironment = -1;
+
 
 @implementation NSString (iTerm)
 
@@ -296,8 +298,22 @@
 
 + (BOOL)isDoubleWidthCharacter:(unichar)unicode encoding:(NSStringEncoding) e
 {
-	if (unicode <= 0xa0 || (unicode>0x452 && unicode <0x200f))
-		return NO;
+    if (unicode <= 0xa0 || (unicode>0x452 && unicode <0x200f))
+        return NO;
+
+    if (isJapaneseEnvironment < 0) {
+        const char ja_JP[] = "ja_JP.";
+        const char *lang = getenv("LANG");
+
+        if (lang != NULL && strlen(lang) > sizeof(ja_JP) - 1 && !strncmp(lang, ja_JP, sizeof(ja_JP) - 1))
+            isJapaneseEnvironment = YES;
+        else
+            isJapaneseEnvironment = NO;
+    }
+
+    if (isJapaneseEnvironment && unicode >= 0x2000)
+        return YES;
+
     /*
      unicode character width check
      see. http://www.unicode.org

かなり手抜きながら、LANGja_JPっぽいときはU+2000以上の文字は倍幅とみなすものだ。

 次に第二のCommandキーをMetaキーにするパッチがこれ。

Index: iTermApplication.m
===================================================================
RCS file: /cvsroot/iterm/iTerm/iTermApplication.m,v
retrieving revision 1.10
diff -u -r1.10 iTermApplication.m
--- iTermApplication.m	7 Nov 2006 08:03:08 -0000	1.10
+++ iTermApplication.m	5 Nov 2007 13:51:06 -0000
@@ -43,6 +43,10 @@
 	id aWindow;
 	PseudoTerminal *currentTerminal;
 	PTYSession *currentSession;
+	unsigned int modflag;
+	unichar unicode;
+	NSString *keystr;
+	NSEvent *newEvent;
 
 
 	if([anEvent type] == NSKeyDown)
@@ -57,16 +61,42 @@
 			currentSession = [currentTerminal currentSession];
 
 			if([currentSession hasKeyMappingForEvent: anEvent highPriority: YES])
+			{
 				[currentSession keyDown: anEvent];
-			else
-				[super sendEvent: anEvent];
-		}
-		else
-		   [super sendEvent: anEvent];
+				return;
+			}
+
+			modflag = [anEvent modifierFlags];
+
+			if ((modflag & NSCommandKeyMask)) {
+				keystr  = [anEvent characters];
+				unicode = [keystr length] > 0 ? [keystr characterAtIndex : 0] : 0;
 
+				switch (unicode)
+				{
+					case 0x20:						// Switch Input Source
+					case NSCarriageReturnCharacter:	// Enter Full Screen
+						break;
+
+					default:
+						newEvent = [NSEvent keyEventWithType: [anEvent type]
+											location: [anEvent locationInWindow]
+											modifierFlags: ((modflag - NSCommandKeyMask) | NSAlternateKeyMask)
+											timestamp: [anEvent timestamp]
+											windowNumber: [anEvent windowNumber]
+											context: [anEvent context]
+											characters: keystr
+											charactersIgnoringModifiers: [anEvent charactersIgnoringModifiers]
+											isARepeat: [anEvent isARepeat]
+											keyCode: [anEvent keyCode]];
+						[currentSession keyDown: newEvent];
+						return;
+				}
+			}
+		}
 	}
-	else
-		[super sendEvent: anEvent];
+
+	[super sendEvent: anEvent];
 }
 
 @end

これは、一部の組合せを除き、CommandキーをOptionキーと同一視させる修正。もちろん、Keyboard Profileの「Option Key as」でOptionキーをMetaキーにしないと無意味だ。ふつうは日本語を直接入力したいだろうから、そこの設定は「Meta」ではなく「+Esc」にするだろう。

 なお、第三の色問題はiTermには存在しない。Display Profileでいじれるから。

 iTermのサイトからCVSでソースを取ってきて上のパッチを当て、好みに応じていじってビルドしてください。特にどのCommandキーショートカットを生かすかの部分は人それぞれのはず。ビルド方法は、iTerm.xcodeprojを開いてビルドボタンを押すだけ。Xcodeは最新版(Tigerなら2.5, Leopardなら3.0)にしておいた方がいい。Tiger 10.4.10 + Xcode 2.4だとうまくビルドできなかった。

 何か情報があれば寄せてください。みんな我慢しているのか、他にいい選択肢があって俺が知らないだけなのか、いつも不思議なんだよね。

 次に続く。


 家のMBPはLeopardにアップグレードしたが、職場の方はまだ。Leopardでよくなったことは多いが、困ることもまだいろいろある。以下が、まだLeopardで使えないもののリスト。

  • TerminalColors

    SIMBL本体はもう対応しているんだけどね。

  • APE / Cmd2Opt

    Terminal.appはすごくいいんだけど、MetaキーとしてOptionキーしか割り当てられないのが困る。Commandを使わせてくれよ。メニュー項目のショートカットなんて要らない。ちなみに、Keyboard Maestroは動く。

  • MagiCal

    Preferencesを開くと落ちる。

  • rpnCalc (Dashboard widget)

    表示がめちゃくちゃ。

  • Last.fm

    LastFMHelperが暴走する。

  • Speed Download

    4.1.16でLeopardに対応したと言っているが、2つ3つファイルをダウンロードするとすぐに落ちる。

    →4.1.17で直った。(11/3)

  • Witch

    待っていると動く気もするが、反応しないことが多い。何かショートカットがぶつかっているんだろうか。

    →再設定したら動くようになった。(11/3)


 vc-svk.elはEmacs VC用のSVKドライバだが、こいつをロードするとどんなファイルを開くときでも待たされる。今まで必要に応じてSVK専用のEmacsセッションを上げたりしていたが、業を煮やして中を見ることにした。(リビジョンは2299)

 どうも、あるファイルがSVKの管理下にあるかをチェックする部分がおかしい。ファイルのパスをチェックアウトディレクトリ一覧と照合するのだが、その一覧のキャッシュが利いておらず、毎回svk checkout --listを実行している。

 こいつは~/.svk/configのタイムスタンプを頼りにしているのだが、どうやら、SVKはいつごろからかsvk checkout --listを実行しただけで同ファイルが更新されるようになったらしい。SVKの方を直したい気もするが、とりあえずコマンド実行後のタイムスタンプを覚えるようにした。

 ところがやっぱりキャッシュが利かない。なんでだろうと思ったら、キャッシュを使う条件が「現在のタイムスタンプ」<「覚えているタイムスタンプ」になっている。ウオォォォム!

 ということで修正はこう。

Index: vc-svk.el
===================================================================
--- vc-svk.el	(revision 2557)
+++ vc-svk.el	(working copy)
@@ -768,17 +768,20 @@
   (let ((config "~/.svk/config")
         mtime)
     (when (file-readable-p config)
-      (setq mtime (nth 5 (file-attributes "~/.svk/config")))
+      (setq mtime (nth 5 (file-attributes config)))
       (unless (and vc-svk-co-paths           ; has not it been loaded?
-                   (vc-svk-time-less-p mtime ; is it unmodified since?
-                                       (car vc-svk-co-paths)))
+                   (not                      ; is it unmodified since?
+                    (vc-svk-time-less-p (car vc-svk-co-paths) mtime)))
         ;; (re)load
-        (setq vc-svk-co-paths (list mtime))
         (with-temp-buffer
           (vc-svk-command t 0 nil "checkout" "--list")
+          ;; `svk checkout --list' modifies ~/.svk/config somehow, so
+          ;; you have to stat it again.
+          (setq mtime (nth 5 (file-attributes config)))
+          (setq vc-svk-co-paths (list mtime))
           (goto-char (point-min))
           (when (search-forward "==========\n" nil t)
-            (while (re-search-forward "^ +\\(.+\\) *\t\\(.+\\)$" nil t)
+            (while (re-search-forward "^ +\\(.+?\\) *\t\\(.+\\)$" nil t)
               (add-to-list 'vc-svk-co-paths
                            (list (match-string-no-properties 2)
                                  (match-string-no-properties 1))))))

 いまどきVCなんて誰も使ってないのかな。ファイルひとつだけ修正するときはPCL系より手軽でいいんだけど。


 ATOK 2007は飛ばすつもりだったんだけど、本数限定のATOK 2007 for Mac + Windowsなるパッケージが安く売られているので、思わず購入してしまった。既存ユーザ向けの優待版はさらに安い。すぐに使えるダウンロード版がないのがいまいちだけどね。

 で、Macに入れてみたわけです。そしたら、一発ではうまく行かない。ドキュメントや公式サイトにもあるように、インストールしても言語環境の一覧に出てきてくれなかった。削除してまた入れる、を繰り返したらうまく行ったが、同音異義語ウィンドウが出なかったりしてどこかおかしい。

 そこで、指示にあるように手動コピーというのをやってみた。そして再起動させてみると、Finderからターミナルから何から、起動するアプリケーションがことごとく死亡するというとんでもない事態に陥ってしまった。Command+Sを押しながら起動してシングルユーザモードに落ち、手動削除したら直ったが、そういう術を持たないユーザだったら絶望してしまうに違いない。

 そして、もう一度インストーラからインストールしたら、今度はうまく行った。いったい何なんだ。

 ところで、ATOKの設定ファイルはリソースフォーク必須なので、マシン間で移行するときに注意が必要だ。rsyncscpには-Eを忘れずに。