all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Augusto Stoffel <arstoffel@gmail.com>
To: emacs-devel@gnu.org
Subject: OSC control sequences in comint
Date: Sun, 22 Aug 2021 19:31:15 +0200	[thread overview]
Message-ID: <87bl5pgzdo.fsf@gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1412 bytes --]

I would like to suggest adding some (optional) support for "Operating
System Commands" [1] to comint.  (This is unrelated to having Emacs
talk OSC codes with the terminal inside of which its running.)

This class of control sequences serves various random purposes, for
instance adding clickable hyperlinks in terminal applications.  As an
example, compare `ls --hyperlink' in the Emacs shell versus some other
"modern" terminal emulator.

Now, I don't particularly care about those hyperlinks or any other
established use of OSC sequences, and I guess a well-made program will
avoid outputting them to Emacs, since it advertises itself as a dumb
terminal.  But OSCs seem to be good a way to create ad-hoc communication
protocols between Emacs and other programs, say to display images in a
REPL buffer (see [3] for a precedent).

Thus, concretely, the suggestion is to add the
`comint-osc-process-output' function attached below to comint.el or some
other appropriate place.  It would not be added to
`comint-output-filter-functions' by default.  The hyperlink handler is
added as an example, it might be kept or removed.

Anyway, as an illustration, after evaluating the attached file and calling

  (add-hook 'comint-output-filter-functions 'comint-osc-process-output nil t)

in a shell buffer, `ls --hyperlink' should produce clickable
hyperlinks, as it does in other capable terminals.

What do you think?


[-- Attachment #2: comint-osc.el --]
[-- Type: text/plain, Size: 2322 bytes --]

;; -*- lexical-binding: t -*-

(defvar-local comint-osc-handlers '(("8" . comint-osc-8-handler))
  "Alist of handlers for OSC escape sequences.
See `comint-osc-process-output' for details.")

(defvar-local comint-osc--marker nil)

(defun comint-osc-process-output (_)
  "Interpret OSC control sequences in comint output.

This function is intended to be added to
`comint-output-filter-functions' and interprets control sequences
of the forms

    `\\e]<name>;<text>\\a'
    `\\e]<name>;<text>\\e\\'

Specifically, every occurrence of such control sequences is
removed from the buffer.  Then, if the alist
`comint-osc-handlers' contains an entry <name>, the corresponding
value, which should be a function, is called with <name> and
<text> as arguments, with point where the escape sequence was
located."
  (let ((bound (process-mark (get-buffer-process (current-buffer)))))
    (save-excursion
      (goto-char (or comint-osc--marker
                     (and (markerp comint-last-output-start)
			  (eq (marker-buffer comint-last-output-start)
			      (current-buffer))
			  comint-last-output-start)
                     (point-min)))
      (when (eq (char-before) ?\e) (backward-char))
      (while (re-search-forward "\e]" bound t)
        (let ((pos0 (match-beginning 0))
              (code (and (re-search-forward "\\=\\([0-9A-Za-z]*\\);" bound t)
                         (match-string 1)))
              (pos1 (point)))
          (if (re-search-forward "\a\\|\e\\\\" bound t)
              (let ((text (buffer-substring-no-properties pos1 (match-beginning 0))))
                (setq comint-osc--marker nil)
                (delete-region pos0 (point))
                (when-let ((fun (cdr (assoc-string code comint-osc-handlers))))
                  (funcall fun code text)))
            (put-text-property pos0 bound 'invisible t)
            (setq comint-osc--marker (copy-marker pos0))))))))

;;; Hyperlink handling

(defvar-local comint-osc-8--state nil)

(defun comint-osc-8-handler (_ text)
  (when comint-osc-8--state
    (let ((start (car comint-osc-8--state))
          (uri (cdr comint-osc-8--state)))
      (require 'shr)
      (shr-urlify start uri)))
  (setq comint-osc-8--state
        (and (string-match ";\\(.+\\)" text)
             (cons (point-marker) (match-string-no-properties 1 text)))))

[-- Attachment #3: Type: text/plain, Size: 213 bytes --]


[1]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
[2]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
[3]: https://iterm2.com/documentation-images.html

             reply	other threads:[~2021-08-22 17:31 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-22 17:31 Augusto Stoffel [this message]
2021-08-22 22:38 ` OSC control sequences in comint Lars Ingebrigtsen
2021-08-23 16:46   ` Augusto Stoffel
2021-08-25 10:32     ` Lars Ingebrigtsen
2021-08-26  7:44       ` Augusto Stoffel
2021-08-26 14:06         ` Lars Ingebrigtsen
2021-08-28 15:15           ` [PATCH] OSC 7 handler (alternative to shell-dirtrack-mode) Augusto Stoffel
2021-08-29  8:06             ` Michael Albinus
2021-08-29 18:56             ` Lars Ingebrigtsen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87bl5pgzdo.fsf@gmail.com \
    --to=arstoffel@gmail.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.