5

Since command outputs are easily mutable, I have to be careful when I work with eshell and REPL buffers.

Is there a way to protect them?

  • could you let know if Tobias' answer statisied you? – ReneFroger Jan 02 '16 at 00:16
  • 1
    I was waiting for a more complete answer, but his answer works for eshell. Since no other answer was coming, I chose his answer. –  Jan 02 '16 at 00:32
  • Do you mean the lisp-scratch buffer by `repl` buffer? – Tobias Jan 02 '16 at 00:54
  • @Tobias In my case, it is REPL buffers launched by `cider-jack-in` and `nodejs-repl`. But, I want everything except the current input line on all REPL buffers and all shell buffers to be immutable against keyboard inputs. –  Jan 02 '16 at 01:53
  • `nodejs-repl` is derived from `comint`-mode -- **as it should be**. Therefore, the added section on `comint` in the already existing answer should work. It looks like this is not the case for `cider` because `cider` is not based on `comint` but they do their own stuff for process communication. – Tobias Jan 02 '16 at 11:25

1 Answers1

5

Modes that communicate with external shells should inherit from comint mode. eshell is an exception w.r.t this principle since it emulates almost all commands on elisp-base. First, setting output read-only is described for eshell afterwards for comint-based modes.

eshell

The following lisp snippet demonstrates the usage of eshell-output-filter-functions for setting the output read-only. It is not very sophisticated and not thoroughly tested.

(defun eshell-interactive-output-readonly ()
  "Make output of interactive commands in eshell read-only.
This should be the last entry in eshell-output-filter-functions!"
  (let ((end eshell-last-output-end))
    (save-excursion
      (goto-char end)
      (end-of-line 0)
      (setq end (point)))
    (when (< eshell-last-output-block-begin end)
      (put-text-property eshell-last-output-block-begin end 'read-only "Read-only interactive eshell output"))))

(defun eshell-make-interactive-output-readonly ()
  (add-hook 'eshell-output-filter-functions 'eshell-interactive-output-readonly 'append))

(add-hook 'eshell-mode-hook 'eshell-make-interactive-output-readonly)

comint based modes:

It looks like one cannot use comint-output-filter-functions for setting the command output read-only in comint-based modes. The reason is that comint-output-filter sets unconditionally the rear-nonsticky text property to t. Therefore you can insert text into the output despite of the read-only-property even if already existing output characters cannot be modified.

The only chance to protect the output is an after-advice for comint-output-filter. The following code-snippet demonstrates the principle.

(defadvice comint-output-filter (after output-readonly activate)
  "Set last process output read-only."
  (add-text-properties comint-last-output-start (line-end-position 0)
               '(read-only "Process output is read-only."
                   rear-nonsticky (inhibit-line-move-field-capture))))
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • Notably, a lot of these modes fail to take advantage of the [fields](https://www.gnu.org/software/emacs/manual/html_node/elisp/Fields.html) feature of Emacs. For example, if I was writing eshell, I would propertize the prompt with `'field 'prompt`, the user-typed input with `'field 'input`, and command output with `'field 'output`. This wouldn't be perfect in the edge cases (an interactive external command which prompts for its own input wouldn't get detected by eshell, for example), but it's far better than no fields at all. Even something as little as tagging the prompt with a field is huge. – mtraceur Apr 13 '23 at 19:54
  • The relevant tie-in to this answer is that if these modes actually annotated the different pieces of text, then the solution to marking pieces read-only becomes more elegant - more universally reusable and composable - because then we could scan the text for the relevant field properties, reducing the mode-specific logic. And incidentally, for example in `eshell` at least, we *can* inject a `field` property to the prompt and add `field` to its `rear-nonsticky` property, by overriding `eshell-prompt-function` (and disabling the default prompt highlighting) or advising `eshell-emit-prompt`. – mtraceur Apr 13 '23 at 20:04