もう少し自由に歩く。

もっと自由にカーソルを移動したり、いくつかの場所をマークして同時に編集するモードを考えているのだけど、完成するまでは不便なので、もっと簡単に実装できるものを取り敢えず作ってみた。
とはいっても、前に作ったものの改良だけど。
関数の説明を表示してくれるモード*1を見つけたので、それに合わせて説明を入れたのと、universal-argumentの使い方が何となく分かったので、それを使うようにした。
forward-lineなどの移動量を指定できても使い道がないので、別の数値を使う移動を割り当ててある。
これでgoto-charとかgoto-lineに割り当てるキーを節約できた。

コードは畳んでおいた。

(defun walk-up (&optional N)
  "上にループ動作で移動。オプションでgoto-lineする。"
  (interactive "p")
  (or (if N (universal-walk N #'goto-line (buffer-lines)))
	  (if (< (get-window-line) (/ (window-lines) 2))
		  (scroll-window -1))
	  (previous-virtual-line 1)
	  (goto-line (buffer-lines))))

(defun walk-down (&optional N)
  "下にループ動作で移動。オプションでgoto-virtual-lineする。"
  (interactive "p")
  (let ((goal (goal-column))
		(test (save-window-excursion (scroll-window (window-lines)))))
	(set-goal-column goal)
	(or (if N (universal-walk N #'goto-virtual-line (save-excursion (goto-char (buffer-size)) (current-virtual-line-number))))
		(and test
			 (> (get-window-line) (/ (window-lines) 2))
			 (scroll-window 1))
		(next-virtual-line 1)
		(goto-line 0))))


(defun walk-left (&optional N)
  "左にループ動作で移動。オプションでgoto-charする。"
  (interactive "p")
  (if N (universal-walk N #'goto-char (buffer-size))
	(if (backward-char 1) nil (goto-char (buffer-size))))
  (let ((goal (goal-column))
		(here (current-column)))
	(set-goal-column
	 (cond
	  ((= here (save-excursion (goto-eol) (current-column)))
	   (window-columns))
	  ((zerop here) 0)
	  (t goal)))))

(defun walk-right (&optional N)
  "右にループ動作で移動。オプションでgoto-columnする。"
  (interactive "p")
  (if N (universal-walk N #'goto-column #1=(save-excursion (goto-eol) (current-column)))
	(if (forward-char 1) nil (goto-char 0)))
  (let ((goal (goal-column))
		(here (current-column)))
	(set-goal-column
	 (cond
	  ((zerop here) 0)
	  ((= here #1#)
	   (window-columns))
	  (t goal)))))

(defun universal-walk (N func limit)
  (funcall func (mod N (1+ limit))))

*1:shiroのdescribe-bindings-mode