emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 630a13a: Add support for OSC escape codes in comint


From: Lars Ingebrigtsen
Subject: master 630a13a: Add support for OSC escape codes in comint
Date: Wed, 25 Aug 2021 06:29:32 -0400 (EDT)

branch: master
commit 630a13ac4631866889bab1177e06ca1d693708c1
Author: Augusto Stoffel <arstoffel@gmail.com>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add support for OSC escape codes in comint
    
    * doc/emacs/misc.texi (Shell Mode): Document it.
    
    * lisp/comint.el (comint-osc-handlers, comint-osc--marker): New
    variables.
    (comint-osc-process-output): New function.
    (comint-osc-hyperlink-map): New map.
    (comint-osc-hyperlink-handler): New function.
---
 doc/emacs/misc.texi    | 13 ++++++++
 etc/NEWS               | 19 ++++++++---
 lisp/comint.el         | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lisp/net/browse-url.el |  1 +
 4 files changed, 114 insertions(+), 4 deletions(-)

diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 528cfa9..47e3e11 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1113,6 +1113,19 @@ subshell:
 @end example
 @end table
 
+By default, Shell mode handles common @acronym{ANSI} escape codes (for
+instance, for changing the color of text).  Emacs also optionally
+supports some extend escape codes, like some of the @acronym{OSC}
+(Operating System Codes) if you put the following in your init file:
+
+@lisp
+(add-hook 'comint-output-filter-functions 'comint-osc-process-output)
+@end lisp
+
+With this enabled, the output from, for instance, @code{ls
+--hyperlink} will be made into clickable buttons in the Shell mode
+buffer.
+
 @cindex Comint mode
 @cindex mode, Comint
   Shell mode is a derivative of Comint mode, a general-purpose mode for
diff --git a/etc/NEWS b/etc/NEWS
index 07a7821..bfc1d3e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1561,10 +1561,6 @@ If non-nil, 'shell-mode' handles implicit "cd" commands, 
changing the
 directory if the command is a directory.  Useful for shells like "zsh"
 that has this feature.
 
-+++
-*** 'comint-delete-output' can now save deleted text in the kill-ring.
-Interactively, 'C-u C-c C-o' triggers this new optional behavior.
-
 ** Eshell
 
 ---
@@ -3026,6 +3022,21 @@ default are unaffected.)
 states to be maintained if 'so-long-mode' replaces the original major
 mode.  By default, these new options support 'view-mode'.
 
+** Comint
+
++++
+*** Support for OSC escape sequences.
+Adding the new 'comint-osc-process-output' to
+'comint-output-filter-functions' enables the interpretation of OSC
+("Operating System Command") escape sequences in comint buffers.  By
+default, only OSC 8, for hyperlinks, is acted upon.  Adding more
+entries to `comint-osc-handlers' allows a customized treatment of
+further escape sequences.
+
++++
+*** 'comint-delete-output' can now save deleted text in the kill-ring.
+Interactively, 'C-u C-c C-o' triggers this new optional behavior.
+
 
 * New Modes and Packages in Emacs 28.1
 
diff --git a/lisp/comint.el b/lisp/comint.el
index 7af8e8f..b2557dd 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -3887,6 +3887,91 @@ REGEXP-GROUP is the regular expression group in REGEXP 
to use."
           ;; don't advance, so ensure forward progress.
          (forward-line 1)))
       (nreverse results))))
+
+
+;;; OSC escape sequences (Operating System Commands)
+;;============================================================================
+;; Adding `comint-osc-process-output' to `comint-output-filter-functions'
+;; enables the interpretation of OSC escape sequences.  By default, only
+;; OSC 8, for hyperlinks, is acted upon.  Adding more entries to
+;; `comint-osc-handlers' allows a customized treatment of further sequences.
+
+(defvar-local comint-osc-handlers '(("8" . comint-osc-hyperlink-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 escape sequences in comint output.
+This function is intended to be added to
+`comint-output-filter-functions' in order to interpret escape
+sequences of the forms
+
+    ESC ] command ; text BEL
+    ESC ] command ; text ESC \\
+
+Specifically, every occurrence of such escape sequences is
+removed from the buffer.  Then, if `command' is a key of the
+`comint-osc-handlers' alist, the corresponding value, which
+should be a function, is called with `command' 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 (OSC 8)
+
+(defvar comint-osc-hyperlink-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\r" 'browse-url-button-open)
+    (define-key map [mouse-2] 'browse-url-button-open)
+    (define-key map [follow-link] 'mouse-face)
+    map)
+  "Keymap used by OSC 8 hyperlink buttons.")
+
+(define-button-type 'comint-osc-hyperlink
+  'keymap comint-osc-hyperlink-map
+  'help-echo (lambda (_ buffer pos)
+               (when-let ((url (get-text-property pos 'browse-url-data 
buffer)))
+                 (format "mouse-2, C-c RET: Open %s" url))))
+
+(defvar-local comint-osc-hyperlink--state nil)
+
+(defun comint-osc-hyperlink-handler (_ text)
+  "Create a hyperlink from an OSC 8 escape sequence.
+This function is intended to be included as an entry of
+`comint-osc-handlers'."
+  (when comint-osc-hyperlink--state
+    (let ((start (car comint-osc-hyperlink--state))
+          (url (cdr comint-osc-hyperlink--state)))
+      (make-text-button start (point)
+                        'type 'comint-osc-hyperlink
+                        'browse-url-data url)))
+  (setq comint-osc-hyperlink--state
+        (and (string-match ";\\(.+\\)" text)
+             (cons (point-marker) (match-string-no-properties 1 text)))))
+
 
 ;;; Converting process modes to use comint mode
 ;;============================================================================
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index f739cd7..c8ca70c 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -1780,6 +1780,7 @@ clickable and will use `browse-url' to open the URLs in 
question."
                                          category browse-url
                                          browse-url-data ,(match-string 
0)))))))
 
+;;;###autoload
 (defun browse-url-button-open (&optional external mouse-event)
   "Follow the link under point using `browse-url'.
 If EXTERNAL (the prefix if used interactively), open with the



reply via email to

[Prev in Thread] Current Thread [Next in Thread]