10

シェルコマンドを呼び出して特定の文字列を処理し、結果の文字列を返すEmacsLisp関数を作成しました。trこれは、テキストを大文字に変換するために呼び出すだけの単純化された例です。

(defun test-shell-command (str)
  "Apply tr to STR to convert lowercase letters to uppercase."
  (let ((buffer (generate-new-buffer "*temp*")))
    (with-current-buffer buffer
      (insert str)
      (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'")
      (buffer-string))))

この関数は、一時バッファーを作成し、テキストを挿入し、呼び出し tr、テキストを結果に置き換え、結果を返します。

上記の関数は期待どおりに機能しますが、この関数のラッパーを記述してコマンドをリージョンに適用すると、元に戻る履歴に2つのステップが追加されます。別の例を次に示します。

(defun test-shell-command-region (begin end)
  "Apply tr to region from BEGIN to END."
  (interactive "*r")
  (insert (test-shell-command (delete-and-extract-region begin end))))

を呼び出すと、領域は大文字のテキストに置き換えられますが、 ( )M-x test-shell-command-on-regionを押すと、元に戻る履歴の最初のステップは、テキストが削除された状態です。2ステップ前に戻ると、元のテキストが復元されます。C-_undo

私の質問は、中間ステップが元に戻る履歴に追加されないようにするにはどうすればよいですか?私はundoに関するEmacsのドキュメントを読みましたが、私が知る限り、これに対処していないようです。

これは、前と同じように、組み込みのEmacs関数を呼び出すことによって同じことを実現する関数ですupcase。結果は次の delete-and-extract-regionように渡されます insert

(defun test-upcase-region (begin end)
  "Apply upcase to region from BEGIN to END."
  (interactive "*r")
  (insert (upcase (delete-and-extract-region begin end))))

を呼び出すときM-x test-upcase-region、予想どおり、取り消し履歴には1つのステップしかありません。そのため、呼び出すと test-shell-command元に戻る境界が作成される場合のようです。それはどういうわけか避けることができますか?

4

3 に答える 3

7

キーはバッファ名です。元に戻すの維持を参照してください:

新しく作成されたバッファへの取り消し情報の記録は、通常、最初から有効になっています。ただし、バッファ名がスペースで始まる場合、元に戻す記録は最初は無効になっています。次の2つの機能を使用するか、自分でbuffer-undo-listを設定することにより、undorecordingを明示的に有効または無効にできます。

with-temp-buffer␣*temp*(先頭の空白に注意してください)という名前のバッファを作成しますが、関数はを使用し*temp*ます。

コード内のUNDO境界を削除するには、先頭にスペースが付いたバッファー名を使用するか、一時バッファーでのUNDO再コーディングを明示的に無効にしbuffer-disable-undoます。

しかし、一般的にはwith-temp-buffer、実際に使用します。これはEmacsでのそのようなことの標準的な方法であり、コードを読む人にあなたの意図を明確にします。また、with-temp-buffer一時バッファを適切にクリーンアップしようとします。


一時バッファで元に戻すが現在のバッファに元に戻る境界を作成する理由について:前の変更が元にでき、他のバッファ(この場合は一時バッファ)で行われた場合、暗黙の境界が作成されます。差出人undo-boundary

以前の取り消し可能な変更他のバッファで行われた場合は常に、すべてのバッファ変更によって境界が追加されます。これは、各コマンドが変更を行う各バッファーで境界を確実に作成するためです。

したがって、一時バッファーで元に戻るを禁止すると、現在のバッファーの元に戻る境界も削除されます。以前の変更は単に元に戻せなくなるため、暗黙の境界は作成されません。

于 2013-02-27T11:36:31.160 に答える
4

一時バッファの使用が実用的でない状況は数多くあります。たとえば、何が起こっているのかをデバッグするのは難しいです。

これらの場合undo-inhibit-record-point、Emacs が境界を置く場所を決定するのを防ぐために let-bind できます:

(let ((undo-inhibit-record-point t))
  ;; Then record boundaries manually
  (undo-boundary)
  (do-lots-of-stuff)
  (undo-boundary))
于 2015-09-10T08:57:05.500 に答える
2

この場合の解決策は、を使用して一時出力バッファーをwith-temp-buffer明示的に作成するのではなく、 を使用して一時出力バッファーを作成することでしたgenerate-new-buffer。最初の関数の次の代替バージョンは、元に戻る境界を作成しません。

(defun test-shell-command (str)
  "Apply tr to STR to convert lowercase letters to uppercase."
  (with-temp-buffer
    (insert str)
    (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'")
    (buffer-string)))

generate-new-buffer本当に元に戻る境界を作成している かどうかを判断できませんでしたが、これで問題は解決しました。generate-new-bufferを呼び出しますget-buffer-create。これはCソースコードで定義されていますが、元に戻る履歴の観点から何が起こっているのかをすばやく判断できませんでした。

この問題は、 EmacsLispのマニュアルエントリundo-boundaryの次の箇所に関連している可能性があります。

以前の取り消し可能な変更が他のバッファーで行われた場合は常に、すべてのバッファー変更によって境界が追加されます。これは、各コマンドが変更を行う各バッファーで境界を確実に作成するためです。

with-temp-bufferマクロは元の関数と同じように呼び出しますが、generate-new-buffer のドキュメントに with-temp-bufferは、元に戻す情報は保存されないと記載されています(Emacs Lispソースにはこれが当てはまると示唆するものは何もありませんが)。

デフォルトでは、undo(Undoを参照)はこのマクロによって作成されたバッファーに記録されません(ただし、必要に応じて、bodyで有効にできます)。

于 2013-02-26T19:52:49.947 に答える