Emacs:カーソルのトレーニング

なぜこれらのジャンプ?

 オスタップベンダー 


エントリー



実際、「Clojureでマクロを操作するいくつかの機能について」という短い記事を書きたかったのです。 しかし、途中で、彼はついにEmacsをより完全に理解することにしました。



もちろん、私はLispとはまったく同じ年齢ではありませんが、20年前からすでに知られています。理論と実践の両方で、この素晴らしい言語(さらには哲学)の可能性を完全に想像しています。 それは独自の実装を書くことの問題でした(実際の使用よりもLispインタプリタの動作メカニズムのより良い理解のため)。 ただし、Emacsはほとんど使用しませんでした。 古代では、Lispでのかなりの作業は、私のバージョンの組み込みエディターによってかなりバイパスされていました(もちろん、エディターもそれ自体に書かれていました)。 それから、私は「他の」ツールを使用しなければなりませんでした。最近では、まったく異なる分野で作業する必要がありました。 さあ、ここで少しだけ「魂のために」...



実際、Emacsの「イマージョン」は非常に快適でした-私は(まだ何らかの理由で)Unixoidではありませんが、コンソールコマンドを扱い、一般にキーボードを理解しながら作業します。 設定管理と紳士の「プラグイン」のセットも問題を引き起こしませんでした。 SBCL、Clojure、Scalaのねじ込みを少し工夫する必要がありましたが、すべての欠点はバージョンの不一致および/またはそれらの(バージョン)固有の問題でした。



ただし、「ジャンプカーソル」シンドローム(現在の行よりも短い場合、次の行または前の行に移動するときに行の末尾に移動する)は、わずかな特異性を引き起こします。 Emacsに関するものでない場合は、問題を解決できない場合によく行われるように、そのようなアプローチで「概念主義」を探して用語に目を向ける必要があるでしょう。 しかし、私たちは編集者のデザイナーを扱っているので、この問題は挑戦と解釈されました(今言うと流行になったので)。



それで、カーソルを「訓練」する必要性の問題を無視しましょう-私にとって個人的には「50 x 50」の利便性であり、ちょうど良い物理学は Emacsプログラミングの概念に慣れるための良い練習です。 おそらく誰かが興味を持って役に立つかもしれません。少なくとも既成のソリューションは見つかりませんでした(「どうやってやるの?..」という質問だけです)。



対象読者



Emacs Lispに精通している人にとってこれは30時間の作業であるとすぐに言わなければならないので、この記事は主にEmacsプログラミングの実用的な可能性に精通し始めた人に向けられています。 プレゼンテーションスタイルには、Emacs Lispの基本とEmacsの基本概念の知識が含まれます。



次に、代替ソリューションに関する専門家からのコメントを聞きたいと思いますが、今のところ、Emacsのアーキテクチャについては表面的な知識が十分にあるため、おそらく私は見なかったでしょう。



解決策



実際、私の解決策は非常に明白です:



カーソルを目的の位置に移動するために追加されたスペースを正確に追跡するというアイデアがありましたが、これは特定の利点をもたらすことなく決定を複雑にしますので、行末のすべてのスペースを削除します(厳密には次の非スペース文字から行末まで) 。 このようなしっくいの除去の瞬間については、簡単にするために、モードオフにし てバッファー保存するイベントに限定します



それでは、「wpers」と呼ばれるマイナーモード(以下、単にモード )の作成を始めましょう。 GitHubから簡単なインストールガイドで完全なパッケージを入手できます 。 ここでは、すべてのコードについて詳しくコメントします。



概念的には次の行、前の行、右の文字、および行末移動コマンドをインターセプトするだけです。 最初の3つは単にスペースを追加する必要があります。必要に応じて、後者は行末の「余分な」スペースを自動的に削除します。 インターセプトはローカルモードの キーマップ内の標準のリマップツールによって実行されます



可能であれば、コードから可能なすべての定数を抽出します。

;;  ()     (defconst wpers-overloaded-funs [next-line previous-line right-char move-end-of-line] "Functions overloaded by the mode") ;;     ("")  ;;       ("") (defconst wpers-fun-prefix "wpers-" "Prefix for new functions") ;;   -   ""    "" (defconst wpers-funs-alist (mapcar '(lambda (x) (cons x (intern (concat wpers-fun-prefix (symbol-name x))))) wpers-overloaded-funs) "alist (old . new) functions") ;; Key-map  -       (wpers-overloaded-funs) (defconst wpers-mode-map (reduce '(lambda (sx) (define-key s (vector 'remap (car x)) (cdr x)) s) wpers-funs-alist :initial-value (make-sparse-keymap)) "Mode map for `wpers'") ;;   -   - ()    (defconst wreps-hooks-alist '((pre-command-hook . wpers--pre-command-hook) (auto-save-hook . wpers-kill-final-spaces) (before-save-hook . wpers-kill-final-spaces)) "alist (hook-var . hook-function)")
      
      







次に、 モード自体を定義します

 (define-minor-mode wpers-mode "Toggle persistent cursor mode." :init-value nil :lighter " wpers" :group 'wpers :keymap wpers-mode-map (if wpers-mode (progn (message "Wpers enabled") (mapc '(lambda (x) (add-hook (car x) (cdr x) nil t)) wreps-hooks-alist)) ;     (progn (message "Wpers disabled") (wpers-kill-final-spaces) (mapc '(lambda (x) (remove-hook (car x) (cdr x) t)) wreps-hooks-alist)))) ;    
      
      







実際には、 政権の機能の単純な「キッチン」:

 ;;    (form)        () (defmacro wpers-save-vpos (form) "Eval form with saving current cursor's position in the line (column)" (let ((old-col (make-symbol "old-col"))) `(let ((,old-col (current-column)) last-col) ,form (move-to-column ,old-col t)))) ;;;   /      (defun wpers-next-line () "Same as `new-line' but adds the spaces if it's needed for saving cursor's position in the line (column)" (interactive) (wpers-save-vpos (next-line))) (defun wpers-previous-line () "Same as `previous-line' but adds the spaces if it's needed for saving cursor's position in the line (column)" (interactive) (wpers-save-vpos (previous-line))) ;;   , " "   (defun wpers-right-char () "Same as `right-char' but adds the spaces if cursor at end of line (column)" (interactive) (let ((ca (char-after))) (if (or (null ca) (eq ca 10)) (insert 32) (right-char)))) ;;          (defun wpers-move-end-of-line () "Function `move-end-of-line' is called and then removes all trailing spaces" (interactive) (move-end-of-line nil) (while (eq (char-before) 32) (delete-char -1))) ;;        (defun wpers-kill-final-spaces () "Deleting all trailing spaces for all lines in the buffer" (save-excursion (goto-char (point-min)) (while (search-forward-regexp " +$" nil t) (replace-match "")))) ;;    (    )   read-only, visual-line    . (defun wpers--pre-command-hook () "Disabling functionality when buffer is read only, visual-line-mode is non-nil or marking is active" (if (or buffer-read-only this-command-keys-shift-translated mark-active visual-line-mode) (let ((fn-pair (rassoc this-command wpers-funs-alist))) (when fn-pair (setq this-command (car fn-pair))))))
      
      







おわりに



与えられたソリューションは確かに理想的ではなく、多くの制限があります。 たとえば、明らかな理由によるモードは、読み取り専用バッファーでは機能しません。 post-command-hookのように、余分なスペースはホットトラッキングで削除したほうがよい場合があります。 空き時間にこれらの問題やその他の問題を解決するかもしれませんが、現時点では非常に満足しています...「象のよう」ではなく、間違いなく古いリスパーや初心者のemaxerのようです。)



関連資料







UPD: オーバーレイを使用して余分なスペースなしで同じことを行う方法を続編で読んでください。



All Articles