Xyzzy/MovingByWord

Last-modified: 2008-04-10 (木) 22:05:14

2008.04.10
WordStar風キーバインディングを実装しようとしていて、大体はとっくにできてたんだけど、「単語」単位の移動(^F, ^A)だけずっと放っておいた(関数を書くにも面倒なのが判っていたから)。この度漸くやっつけたので挙げておく。

もともとXyzzyは単語を認識してくれて単語単位の移動も提供してくれている(next-word, previous-word)。放っておいたのはそれを使えばよかったからでもあるけれど、どうもEmacs系の単語ってのはなんだか賢すぎて肌に合わない。

エディタのプリミティブを書くなら、一文字ずつ「単語」か「単語」でないか判定しながらバッファを走査する……という風でいいだろう。でもエディタ記述言語の上で拡張機能を書くなら、正規表現のお世話になるに限る。

調べると、Xyzzyの正規表現に"\<"とか"\>"というのがある。それぞれ単語の先頭と終了に一致するらしい。これを使ってみた(コメントアウト箇所は試行錯誤中の試行錯誤)。

最初期バージョン
(defun wss-next-word (&optional dir)
  (interactive "p")
  ; (skip-chars-forward " \t\n ")
  ; (re-search-forward "\\b")
  ; (re-search-forward "[ \t\n]+[^ \t]")
  (if (looking-at "\\(\\<\\|^\\|$\\)")
      (forward-char))
  (re-search-forward "\\(\\<\\|^\\|$\\)")
  )

おお。なんとそれらしく動くではないか。でも、Xyzzyデフォルトの動きとなんら変わらない(そりゃそうだ)。やはり、自分の肌に合う「単語」を定義してやらないといけないらしい。

ご本家(?)WordStarやVzなんかの「単語」がどういう定義だったか憶えていないけれど、自分にはもっと「素朴」で充分だし、その方がよい。試行錯誤的に差し当たり以下の綴り(一連の文字列)としておく。

  • 空白文字(半角スペース、タブ、改行、全角スペース)の綴り
  • 半角英数字の綴り
  • 半角記号の綴り
  • 全角英数字の綴り
  • ひらがなの綴り
  • カタカナの綴り
  • 漢字の綴り

で、出来上がったのがこれ。(機種依存文字を含んでいます)

(setq wss-word-re "\\([A-Za-z0-9]+\\|[!-/:-@[-`{-~]+\\|[ \t\n ]+\\|
  [、-◯]+\\|[0-9a-zA-Z]+\\|[ぁ-んー]+\\|[ァ-ヶー]+\\|[亜-熙]+\\|
  [Α-╂]+\\|[①-∪]+\\|[-]+\\)")
; wss-next-word
; 今いる位置が「単語」の中だったら、いつまでも「単語」に一致し
; 続けてしまうので、「単語」の末尾にまで移動する(looking-atと
; forward-char)。
; いわゆる"wrap-around"(ホントはハイフンなし)はしない。
; 「文字の移動(カーソル移動)」と同様の振舞いにするべき、
; というのはちょっと後付で、prev-wordでwarparoundするのが大変そう
; だったから。
(defun wss-next-word (&optional dir)
  (interactive "p")
  (if (looking-at wss-word-re)
      (let ((nchar (length (match-string 0))))
        (if (= nchar 0)
            (setq nchar 1))
        (forward-char nchar)))
  (if (< (point) (point-max))
      (re-search-forward wss-word-re)))
; wss-prev-word
; 後方(行頭方向、文書先頭方向)への文型照合は難事業。
; ちょっと考えて、「Xyzzy Wiki 質問箱/175」に載ってたコードを
; そのまま戴いた。感謝。
; ただしsave-excursionは外した。
(defun wss-prev-word (&optional dir)
  (interactive "p")
  (save-restriction
    (narrow-to-region (point-min) (point))
    (goto-char (point-min))
    (scan-buffer (format nil "~A\\'" wss-word-re)
                 :regexp t)))

「Xyzzy Wiki 質問箱/175」はこちら

wss-word-reが「単語」の文型。二行に分かれているが勿論本当は一行にする。

[Α-╂]+以降は最初は入れていなかったが、prev-wordでうまくないことが判明。バッファ先頭から探索を開始するため、一致する文型がないとカーソルが文頭に行ってしまう。罫線文字より後は機種依存文字なんであまり気持のいいものじゃない。別の文型としておいて、前の単語を探す時だけくっつける方がいいだろう。面倒だから書き直さないけど。

今のところまあまあ違和感のない(素直に言えばまあまあ満足できる)動きをしているので、初版としてはこんなもんで。

ここを参照しているページ

#related: relatedプラグインは廃止されました。