xyzzy から標準のファイラのかわりに FreeCommander を起動する

以前にコードエディタを xyzzy から Visual Studio Codexyzzy のファイラから FreeCommander に乗り換えた。

それでもメモ書きに xyzzy を使うことがあり、癖で xyzzy のファイラを起動してしまうことがあるので C-c C-f のキーバインドで FreeCommander を起動できるようにした。

;; FreeCommander XE を起動する
(defun open-freecommander (&optional (dir nil))
  "FreeCommander で開く"
  (interactive)
  (let ((command "\"C:\\Program Files\\FreeCommander XE\\FreeCommander.exe\""))
	(if dir
		(call-process
		 (concat command " /C \"" dir "\" /T"))
	  (call-process
	   (concat command " /C"))
	  )))

;; 標準のファイラがわりに使う
(global-set-key '(#\C-c #\C-f) 'open-freecommander)

引数にディレクトリのパスを渡すと FreeCommander の新しいタブでそのディレクトリを開けるが、今のところ使っていない。

VSCode May 2020 (version 1.46) に Selection Anchor が導入されたので xyzzy 風のリージョン操作ができるようにした

VSCode が May 2020 (version 1.46) にアップデートされる前は拡張機能 Emacs Friendly Keymap を導入して Emacs 風のキーバインドを使っていたが、次の点が不満だった。

  • C-SPC でマークをセットしたあと、リージョンの終わりに検索でカーソルを移動できない
  • 別の拡張機能 Clipboard History に kill-ring の内容が反映されない

そこで今回搭載されたキーボードでの選択を補助するための次の機能を使ってこれを改善した。また、クリップボード履歴の拡張機能を Multiple Clipboards for VSCode に変更した。

  • Set Selection Anchor (Ctrl+K Ctrl+B)
  • Select From Anchor to Cursor (Ctrl+K Ctrl+K)
  • Cancel Selection Anchor (Escape)
  • Go to Selection Anchor

この動作をするためには拡張機能 multi-command が必要。これは一つのキーバインドで複数のコマンドを実行するためのものだ。

設定内容

  • C-Space (set-mark-command) 相当
    • カーソル位置にアンカーを設定する
  • M-w (copy-region-as-kill) 相当
    • アンカーからカーソル位置までを選択し、クリップボードにコピーする
    • コピー後は選択を解除する
    • アンカーを設定せずにこのコマンドを実行すると、カーソル位置の行をコピーする(editor.emptySelectionClipboard がオンの場合)
  • C-w (kill-region) 相当
    • アンカーからカーソル位置までを選択し、クリップボードにカットする
    • アンカーを設定せずにこのコマンドを実行すると、カーソル位置の行をカットする(editor.emptySelectionClipboard がオンの場合)
  • C-y (yank) 相当
  • C-c C-y
    • Clipboard History から貼り付け

settings.json

    "multiCommand.commands": [
        {
            "command": "emacs.copy-region",
            "sequence": [
                "editor.action.selectFromAnchorToCursor",
                "multiclip.copy",
                "cancelSelection"
            ]
        },
        {
            "command": "emacs.kill-region",
            "sequence": [
                "editor.action.selectFromAnchorToCursor",
                "multiclip.cut"
            ]
        },
        {
            "command": "emacs.paste",
            "sequence": ["editor.action.clipboardPasteAction"]
        },
        {
            "command": "emacs.expand-selection",
            "sequence": [
                "editor.action.cancelSelectionAnchor",
                "editor.action.smartSelect.expand"
            ]
        }
    ],

keybindings.json

	{
		"key": "ctrl+space",
		"command": "editor.action.setSelectionAnchor",
		"when": "editorTextFocus"
	},
	{
		"key": "alt+w",
		"command": "emacs.copy-region",
		"when": "editorTextFocus"
	},
	{
		"key": "ctrl+w",
		"command": "emacs.kill-region",
		"when": "editorTextFocus"
	},
	{
		"key": "ctrl+y",
		"command": "emacs.paste",
		"when": "editorTextFocus && !editorReadonly"
	},
	{
		"key": "ctrl+c ctrl+y",
		"command": "multiclip.list",
		"when": "editorTextFocus && !editorReadonly"
	},

Glyphs で背景レイヤーに描いた骨格から線幅を持ったパスを自動生成する

フォント作成ソフト Glyphs でグリフに対する操作を自動化してみた。

一番分かりやすいドキュメントは Glyphs SDK の ObjectWrapper/GlyphsApp/__init__.py だった。

#MenuTitle: Make Offset
# -*- coding: utf-8 -*-
__doc__="""
Make paths offset from background layer
"""

import copy

# Get the filter
offsetCurveFilter = NSClassFromString("GlyphsFilterOffsetCurve")

for glyph in Glyphs.font.glyphs:
	layer = glyph.layers[0]
	""":type : GSLayer"""

	paths = glyph.layers[0].paths
	""":type : LayerPathsProxy"""

	# Remove current paths
	for path in paths:
		paths.remove(path)
	
	# Copy paths from background
	bgPaths = glyph.layers[0].background.paths
	""":type : LayerPathsProxy"""
	for bgPath in bgPaths:
		paths.append(copy.copy(bgPath))
	
	# Apply filter
	offsetCurveFilter.offsetLayer_offsetX_offsetY_makeStroke_autoStroke_position_error_shadow_(layer, 15, 15, True, 0, 0.5, None, None)
  • Glyphs.font は現在開いているフォント
  • 今はレイヤーが1枚なので glyph.layers[0] で参照できているが、レイヤーが複数になったときその順序がどうなるかまだ試していない
  • Python ではリストの項目をすべて削除するのに del paths[:] のようにできるが、LayerPathsProxy は del paths[:] が使えないので paths.remove(path) で一つ一つ消す
  • オブジェクトの deep copy には copy モジュールを使う

Karabiner-Elements でキーボードのカスタマイズ

先日から導入しているファイルマネージャ ForkLift の操作性向上のため、 macOS のキーボードを徹底的にカスタマイズできる Karabiner-Elements を導入した。

やりたいこと

  • ForkLift のカーソル移動に Emacs キーバインドを使う
  • ファイル操作を xyzzy のファイラとほぼ同じにしたい
    • ただし修飾キーとして shift の代わりに control を使用する
    • C-m が Emacs キーバインドの return にあたるので C-m だけ除外する

実現方法

  1. Complex modifications → Rules → Add Rules を選ぶ
  2. Import more rules from Internet を選ぶ
  3. Emacs で検索し、「Emacs key bindings (rev 12) by @tekezo」を追加
  4. Emacs key bindings [control+keys] (rev 10)」「Emacs key bindings [option+keys] (rev 5)」を有効にする
  5. ~/.config/karabiner/karabiner.json を開き、C-m のキーバインドの部分を次のように変更する
{
  "conditions": [
      {
          "bundle_identifiers": [
              "^com\\.binarynights\\.ForkLift-3$"
          ],
          "type": "frontmost_application_unless"
      }
  ],
  "from": {
      "key_code": "m",
      "modifiers": {
          "mandatory": [
              "control"
          ],
          "optional": [
              "caps_lock",
              "shift",
              "option"
          ]
      }
  },
  "to": [
      {
          "key_code": "return_or_enter"
      }
  ],
  "type": "basic"
},

ForkLift でファイル比較に CompareMerge を使う

先日ファイルマネージャとして ForkLift を導入した。2ペイン型のファイラで、SFTP や SMB に加え各種クラウドストレージもローカルと同じように操作できる。

ファイルの比較には FileMerge, Kaleidoscope, Beyond Compare, Araxis Merge が使えるが、次の理由で CompareMerge を使うことにした。

  • FileMerge は非 ASCII 文字を含むファイルを開くといちいち警告を出してくるため実用的でない
  • Kaleidoscope は試用してみたところ行間が広めの値で固定されており、一度に情報をたくさん表示するのに向いていない

さてここで問題は設定に CompareMerge が用意されていないことだが、 Kaleidoscope の呼び出しがコマンドラインツールから行われているのでこれを利用する。すでに Kaleidoscope のコマンドラインツールを使用している場合は使えないので注意

Kaleidoscope のコマンドラインツールは /usr/local/bin/ksdiff にインストールされる。なのでこれを CompareMerge を起動するスクリプトに置き換えてやればうまくいく。CompareMerge の起動には open コマンドを使うとコマンドラインからファイルを指定することができた。

したがって次の内容で /usr/local/bin/ksdiff を作成し、 ForkLift の Compare Tool の設定を Kaleidoscope にすればよい。

#!/bin/sh
open -a /Applications/CompareMerge.app "$@"

f:id:scientre:20200513161727j:plain