VSCode の Emacs keymaps 拡張についてのメモ

概要

Visual Studio CodeEmacs 風の操作を提供する拡張をいくつか試したが細かいところで違和感があったので、その覚え書きをメモしておく。

自分が本当に望む Emacs 風の操作を実現するためには、やはり自分で拡張を作成しなければならないようだ。NotKyon さんの Emacs Friendly Keybindings - Visual Studio Marketplace から fork するか、Pull Request を投げるのが一番目的に近いように思われる。

tuttieee さんが、かなり理想に近い Awesome Emacs Keymap - Visual Studio Marketplace を作成してくださったので、これを使っていこうと思います!!! (2019/1/11追記)

Keymap 調査

marketplace.visualstudio.com

Emacs 風にする拡張はぱっと検索しただけで11個も見つかる。はたしてどれが良いのだろうか?

  • なお、現在使用している Emacs Friendly Keymap 以外は短時間しか使用していない
  • 最終更新日が2017年以前のものは、開発停止ということにしておく

hiro-sun 系

Emacs 風拡張のうち、最も古いと思われるものが hiro-sun のものであり、その派生物も多く存在する。派生物はだいたい共通の欠点を持ち、自分のニーズに合っていない部分もよく似ている。

  • 全体的な特徴
    • pros:
      • 自分が想定している Emacs 的な操作をほぼ網羅している
    • cons:
      • よく使う C-x 2 (split-window-below) が存在しないか、Emacs と分割方向が異なる
      • たまに使う C-o (open-line) が未実装
      • たまに使う C-x C-c (save-buffers-kill-terminal) が未実装
      • Multi Cursor 機能と相性が悪い
        • Multi Cursor + 選択中に、Emacs 的な操作で選択範囲の拡縮ができない
        • (そもそも Emacs には Multi Cursor 機能がないので、仕方がない)
  • Emacs Keymap - Visual Studio Marketplace (2016/2/16 ~ 11/10) 開発停止
    • hiro-sun
  • Emacs Keymap Improved - Visual Studio Marketplace (2017/7/23 ~ 8/25) 開発停止
    • rkwan94, Robert Kwan (forked from hiro-sun)
    • cons:
      • マウスで範囲選択後に M-w しても、範囲選択状態が継続してしまう
  • Emacs HJKL - Visual Studio Marketplace (2017/6/7 ~ 22) 開発停止
    • dotWaldemr, Waldemar D. (forked from hiro-sun)
    • cons:
      • 移動が HJKL
      • 明示的に fork していないが、実際には hiro-sun のものからの fork だと思われる
  • Emacs Mininum Keymap - Visual Studio Marketplace (2018/6/19)
    • jamesyang999, futurist (forked from hiro-sun)
    • cons:
      • 移動系のコマンドのみを実装
  • Emacs Keybindings with S-exp - Visual Studio Marketplace (2018/8/22 ~ 23)
    • haruhi-s (forked from hiro-sun)

SebastianZaha 系 (forked from hiro-sun)

他の拡張と同時に使用するもの (2019/1/4追記)

そのほか

macOS 専用 (2019/1/12追記)

  • vscode-emacs-ergomac - Visual Studio Marketplace (2018/3/15) (2019/1/4追記)
    • RumataEstor, Dmitry Belyaev (forked from SebastianZaha (forked from hiro-sun))
    • SebastianZaha のものから C- を ⌘- に置き換えたもの。
  • nemacs - Visual Studio Marketplace (2016/7/1 ~ 15) 開発停止 (2019/1/4追記)
  • Tiny Emacs - Visual Studio Marketplace (2018/7/8 ~ 24)
    • aki77
    • Emacs Keymap (hiro-sun) と同時に使用する (ドキュメントに書かれていないので推測)
    • いままで誤解していたのだが、この拡張はおそらく macOS 専用。VSCodemacOS で使用する場合は、デフォルトのカーソル移動キーが Emacs っぽいバインディングになっているらしいのでそのあたりはわざわざ定義していないと思われる (2019/1/12追記)
    • pros:
      • Multi Cursor 機能と相性が良いように作られている
        • Multi Cursor で複数の選択範囲を有している際に、C-f で選択範囲を拡大したり C-b で選択範囲を縮小したりできる
      • C-x 2 (split-window-below), C-x 3 (split-window-right) の画面分割が Emacs とほぼ同じ
    • cons:
      • デフォルト機能と被るキーは定義されていない (C-f など)

Keymap ではないものの調査 (2019/1/4追記)

以下のものは、Keymap を構成せず、コマンドだけを追加する。そのため、自分で Keyboard Shortcuts (keybindings.json) を設定しなければならない。 (試用していないので、実際に期待通りの動作をするのかどうか不明)

C# の double の %

C# 5.0 の double の % がなんか仕様とは異なる値を返す気がするという話。

Download C# Language Specification 5.0 from Official Microsoft Download Center C# 5.0 の言語仕様によれば、

7.8.3 Remainder operator

  • Floating-point remainder:
double operator %(double x, double y);

z is the result of x % y and is computed as x - n * y,
where n is the largest possible integer that is less than or equal to x / y.

とのことなので、n = Floor(x / y) のハズ...。でも実際は Truncate っぽい動作をする。

static void ReminderOperatorDoubleFloor()
{
    var x = -5.2;
    var y = 2.0;
    var n = System.Math.Floor(x / y);
    var z = x - n * y;
    Console.Out.WriteLine("FLOOR: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => FLOOR: x = -5.2, y = 2, n = -3, z = 0.8, x % y = -1.2
}

static void ReminderOperatorDoubleTruncate()
{
    var x = -5.2;
    var y = 2.0;
    var n = System.Math.Truncate(x / y);
    var z = x - n * y;
    Console.Out.WriteLine("TRUNCATE: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => TRUNCATE: x = -5.2, y = 2, n = -2, z = -1.2, x % y = -1.2
}

static void ReminderOperatorInt()
{
    var x = -52;
    var y = 20;
    var n = x / y;
    var z = x - (x / y) * y;
    Console.Out.WriteLine("INT: x = {0}, y = {1}, n = {2}, z = {3}, x % y = {4}", x, y, n, z, x % y);
    // => INT: x = -52, y = 20, n = -2, z = -12, x % y = -12
}

で、実際どんな式が使われてるの、というと Math.IEEERemainder(Double, Double) Method (System) | Microsoft Docs の Remarks に書かれているが、Abs を使った上で Floor して符号は後から追加している。でも仕様には Abs 使うなんて書いてないじゃん!*1

# 蛇足ですが C++03 では double の % 演算は定義されておらずコンパイルできない。

*1:7.8.2 Division operator の整数の / 演算子のところには "absolute value" うんぬんとの記述がみられるので、やはり % のほうの記述は書き忘れであるように思う

C++11 の正規表現ライブラリの曖昧さ

libstdc++ の regex が実装されたそうです。

そこで、仕様の曖昧な点(2点)を調査して纏めました。

GitHub Pages 機能を使ったことが無かったので、試しに使ってみようということで上記のところで公開してあります。

gh-pages ブランチの中で symlink を作っても、GitHub Pages で公開されたページでは symlink は辿ってくれないようでした。そのため、ちょっとだけ修正が必要でした。

正規表現で [a-[:alpha:]] の意味って?

C++11 の仕様に対する疑問

C++11 の正規表現 (std::regex_constants::ECMAScript 時) の仕様は ECMA-262 の文法を次のように微妙に変更したものとなっています。


ClassAtom ::
-
ClassAtomNoDash
ClassAtomExClass
ClassAtomCollatingElement
ClassAtomEquivalence
ClassAtomExClass ::
[: ClassName :]
ClassAtomCollatingElement ::
[. ClassName .]
ClassAtomEquivalence ::
[= ClassName =]
ClassName ::
ClassNameCharacter
ClassNameCharacter ClassName
ClassNameCharacter ::
SourceCharacter but not one of "." "=" ":"
(ドラフト n3337.pdf より)

そして ECMA-262 の正規表現の文字クラス周りの文法はだいたい以下のような感じになっています。


CharacterClass ::
[ [lookahead ∉ {^}] ClassRanges ]
[ ^ ClassRanges ]
ClassRanges ::
[empty]
NonemptyClassRanges
NonemptyClassRanges ::
ClassAtom
ClassAtom NonemptyClassRangesNoDash
ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
ClassAtom
ClassAtomNoDash NonemptyClassRangesNoDash
ClassAtomNoDash - ClassAtom ClassRanges
ClassAtom :: (ここは C++11 の仕様で変更される)
-
ClassAtomNoDash
ClassAtomNoDash ::
SourceCharacter but not one of \ or ] or -
\ ClassEscape
ClassEscape ::
DecimalEscape
b
CharacterEscape
CharacterClassEscape
(Standard ECMA-262 より)

ここで、強調した部分、つまり以下の部分


ClassAtom ::
ClassAtomExClass
NonemptyClassRanges ::
ClassAtom - ClassAtom ClassRanges
NonemptyClassRangesNoDash ::
ClassAtomNoDash - ClassAtom ClassRanges

を素直に解釈すると、例えば次のような正規表現は文法上正しいということになります。

[a-[:alpha:]]
[[:alpha:]-a]
[[:alpha:]-[:alpha:]]

文法上は正しいといっても、何を表現しているのか意味がよくわかりません。

現実の実装では?

例えば、Boost C++ Libraries ではテストケース (test_sets.cpp) 内で、以下のようなテストをしており、

   TEST_INVALID_REGEX("[a-[:alpha:]]", boost::regex::extended);

問題となる正規表現を記述できないことを確かめています。

また、OS X Mountain Lion の clang-425.0.28 では、

#include <regex>
#include <iostream>

int main() {
	std::locale::global(std::locale("C"));
	std::regex re("[a-[:alpha:]]");
	std::cout << std::boolalpha << regex_match("a", re) << std::endl;
	std::cout << std::boolalpha << regex_match("b", re) << std::endl;
	return 0;
}

上記のソースを -std=c++11 -stdlib=libc++ でコンパイルして実行すると


true
false
が出力されます。なにが起こったのかよくわかりません。

また、Visual Studio 2012 Express では上記のソースを実行すると std::regex のコンストラクタ内で std::regex_error 例外が .code() == std::regex_constants::error_range で throw されます。

まとめ

というわけで、この問題は C++11 の仕様の間違いとして解釈するのが良いと思います。

zshのデフォルトの補完の定義を使わないことにした

zshで以下のように設定しておくと

autoload -Uz compinit
compinit

様々な補完がデフォルトで定義されますが、ほとんどの定義は自分には不要であることが最近分かってきました。

むしろ

make ファイル名

と実行したいのにファイル名を補完できないなど、不便な点のほうが目立ちます。

compinit を行わなければデフォルトの補完も定義されないので上記のような不便はないのですが、その代わり補完候補に色が付かなくなってしまうのでそれはそれで不便です。

そこで、compinit を実行した上で不要な定義をばっさり削除することにしました。

autoload -Uz compinit
compinit

# ほとんどの補完の定義を削除する。
# special contexts の定義のみ残す (see: man zshcompsys)
fix_comp_assoc() {
	local var=$1
	shift
	for key in "$argv[@]"; do
		case $key in
		-redirect-,\<,*) unset "${var}[$key]";;
		-redirect-,\>,*) unset "${var}[$key]";;
		-value-,-*) ;;
		-value-,*) unset "${var}[$key]";;
		-*) ;;
		*) unset "${var}[$key]";;
		esac
	done
}
fix_comp_assoc _comps        "${(k)_comps[@]}"
fix_comp_assoc _services     "${(k)_services[@]}"
fix_comp_assoc _patcomps     "${(k)_patcomps[@]}"
fix_comp_assoc _postpatcomps "${(k)_postpatcomps[@]}"

compinit では、補完する時にどのようなコンテキストでどのコマンドを利用するという一覧は _comps, _services, _patcomps, _postpatcomps という連想配列に格納されるようです*1。そこで、それらの定義の中から special contexts の定義以外をばっさり削るようにしました。

これで

make ファイル名

も実行可能になり、快適zshライフを送ることができそうです。

# しかし、もっと良いやりかたがありそうなものですが…

*1:これらの変数のデフォルトの内容は ~/.zcompdump へダンプされているのでそこで確認するのが楽です

Storyboardsを使っている時のUITableViewのdequeueReusableCellWithIdentifier:の挙動

この情報に行き当たるのに苦労したのでメモ

https://developer.apple.com/library/ios/#releasenotes/Miscellaneous/RN-AdoptingStoryboards/_index.html#//apple_ref/doc/uid/TP40011297-CH1-DontLinkElementID_5

はてなブックマーク/COOKPAD画像表示(Chrome拡張)

はてなブックマーク/COOKPAD画像表示 - Chrome Web Store

はてなブックマークCOOKPADのレシピをブックマークしている場合に、レシピの写真を一緒に表示してくれる拡張です。先日ふと思い立って作ってみました。

Greasemonkeyでも書ける内容の拡張ですが、Chrome拡張として作りました。なんとなく。