Add message passing support
* eat.el (eat-message-handler-alist): New user option. * eat.el (eat--handle-message): New function. * eat.el (eat--handle-uic, eat--eshell-handle-uic): Handle message passing sequence. * eat.texi (Message Passing): New section in chapter "Shell Integration". * integration/bash (__eat_enable_integration): Remove the unnecessary complex code to update PROMPT_COMMAND. * integration/bash (_eat_msg): * integration/zsh (_eat_msg): New function.
This commit is contained in:
parent
9e129f33a2
commit
b2ad1be411
4 changed files with 100 additions and 13 deletions
46
eat.el
46
eat.el
|
@ -296,6 +296,20 @@ the history of commands like `eat', `shell-command' and
|
||||||
:group 'eat-ui
|
:group 'eat-ui
|
||||||
:group 'eat-eshell)
|
:group 'eat-eshell)
|
||||||
|
|
||||||
|
(defcustom eat-message-handler-alist nil
|
||||||
|
"Alist of message handler name and its handler function.
|
||||||
|
|
||||||
|
The keys are the names of message handlers, and the values are their
|
||||||
|
respective handler functions.
|
||||||
|
|
||||||
|
Shells can send Eat messages, as defined in this user option. If an
|
||||||
|
appropiate message handler is defined, it's called with the other
|
||||||
|
arguments, otherwise it's ignored."
|
||||||
|
:type '(alist :key-type string
|
||||||
|
:value-type function)
|
||||||
|
:group 'eat-ui
|
||||||
|
:group 'eat-eshell)
|
||||||
|
|
||||||
(defcustom eat-enable-native-shell-prompt-editing nil
|
(defcustom eat-enable-native-shell-prompt-editing nil
|
||||||
"Non-nil means allowing editing shell prompt using Emacs commands.
|
"Non-nil means allowing editing shell prompt using Emacs commands.
|
||||||
|
|
||||||
|
@ -5215,6 +5229,22 @@ BUFFER is the terminal buffer."
|
||||||
locale-coding-system))
|
locale-coding-system))
|
||||||
format))))
|
format))))
|
||||||
|
|
||||||
|
(defun eat--handle-message (name &rest args)
|
||||||
|
"Handle message with handler name NAME and ARGS."
|
||||||
|
(when-let* ((name (ignore-errors (decode-coding-string
|
||||||
|
(base64-decode-string name)
|
||||||
|
locale-coding-system)))
|
||||||
|
(handler (assoc name eat-message-handler-alist)))
|
||||||
|
(save-restriction
|
||||||
|
(widen)
|
||||||
|
(save-excursion
|
||||||
|
(apply (cdr handler)
|
||||||
|
(mapcar (lambda (arg)
|
||||||
|
(ignore-errors (decode-coding-string
|
||||||
|
(base64-decode-string arg)
|
||||||
|
locale-coding-system)))
|
||||||
|
args))))))
|
||||||
|
|
||||||
(defun eat--handle-uic (_ cmd)
|
(defun eat--handle-uic (_ cmd)
|
||||||
"Handle UI Command sequence CMD."
|
"Handle UI Command sequence CMD."
|
||||||
(pcase cmd
|
(pcase cmd
|
||||||
|
@ -5257,20 +5287,27 @@ BUFFER is the terminal buffer."
|
||||||
(let status (one-or-more digit))
|
(let status (one-or-more digit))
|
||||||
string-end)
|
string-end)
|
||||||
(eat--set-cmd-status (string-to-number status)))
|
(eat--set-cmd-status (string-to-number status)))
|
||||||
;; UIC e ; I ; <n> ST.
|
;; UIC e ; I ; 0 ; <t> ; <t> ; <t> ST.
|
||||||
((rx string-start "e;I;0;"
|
((rx string-start "e;I;0;"
|
||||||
(let format (zero-or-more (not ?\;)))
|
(let format (zero-or-more (not ?\;)))
|
||||||
?\; (let host (zero-or-more (not ?\;)))
|
?\; (let host (zero-or-more (not ?\;)))
|
||||||
?\; (let path (zero-or-more anything))
|
?\; (let path (zero-or-more anything))
|
||||||
string-end)
|
string-end)
|
||||||
(eat--get-shell-history (cons host path) format))
|
(eat--get-shell-history (cons host path) format))
|
||||||
|
;; UIC e ; I ; 1 ; <t> ; <t> ST.
|
||||||
((rx string-start "e;I;1;"
|
((rx string-start "e;I;1;"
|
||||||
(let format (zero-or-more (not ?\;)))
|
(let format (zero-or-more (not ?\;)))
|
||||||
?\; (let hist (zero-or-more anything))
|
?\; (let hist (zero-or-more anything))
|
||||||
string-end)
|
string-end)
|
||||||
(eat--get-shell-history hist format))
|
(eat--get-shell-history hist format))
|
||||||
|
;; UIC e ; J ST.
|
||||||
("e;J"
|
("e;J"
|
||||||
(eat--before-new-prompt))))
|
(eat--before-new-prompt))
|
||||||
|
;; UIC e ; M ; ... ST.
|
||||||
|
((rx string-start "e;M;"
|
||||||
|
(let msg (zero-or-more anything))
|
||||||
|
string-end)
|
||||||
|
(apply #'eat--handle-message (string-split msg ";")))))
|
||||||
|
|
||||||
(defun eat-previous-shell-prompt (&optional arg)
|
(defun eat-previous-shell-prompt (&optional arg)
|
||||||
"Go to the previous shell prompt.
|
"Go to the previous shell prompt.
|
||||||
|
@ -6894,6 +6931,11 @@ PROGRAM can be a shell command."
|
||||||
;; UIC e ; I ; 0 ; <t> ST.
|
;; UIC e ; I ; 0 ; <t> ST.
|
||||||
((rx string-start "e;I;0;" (zero-or-more anything) string-end)
|
((rx string-start "e;I;0;" (zero-or-more anything) string-end)
|
||||||
(eat-term-send-string eat--terminal "\e]51;e;I;0\e\\"))
|
(eat-term-send-string eat--terminal "\e]51;e;I;0\e\\"))
|
||||||
|
;; UIC e ; M ; ... ST.
|
||||||
|
((rx string-start "e;M;"
|
||||||
|
(let msg (zero-or-more anything))
|
||||||
|
string-end)
|
||||||
|
(apply #'eat--handle-message (string-split msg ";")))
|
||||||
;; Other sequences are ignored.
|
;; Other sequences are ignored.
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
35
eat.texi
35
eat.texi
|
@ -479,7 +479,6 @@ When set to @code{right-margin}, Eat uses the right margin.
|
||||||
@vindex eat-shell-prompt-annotation-success
|
@vindex eat-shell-prompt-annotation-success
|
||||||
@vindex eat-shell-prompt-annotation-failure-margin-indicator
|
@vindex eat-shell-prompt-annotation-failure-margin-indicator
|
||||||
@vindex eat-shell-prompt-annotation-failure
|
@vindex eat-shell-prompt-annotation-failure
|
||||||
|
|
||||||
Eat uses the strings ``-'', ``0'' and ``X'' respectively to indicate
|
Eat uses the strings ``-'', ``0'' and ``X'' respectively to indicate
|
||||||
the command is running, the command has succeeded and the command has
|
the command is running, the command has succeeded and the command has
|
||||||
failed. You can also customize the them. The user option
|
failed. You can also customize the them. The user option
|
||||||
|
@ -495,6 +494,40 @@ face @code{eat-shell-prompt-annotation-failure} control the indicator
|
||||||
used to indicate the command has exited unsuccessfully with non-zero
|
used to indicate the command has exited unsuccessfully with non-zero
|
||||||
exit status.
|
exit status.
|
||||||
|
|
||||||
|
@anchor{Message Passing}
|
||||||
|
@section Message Passing
|
||||||
|
|
||||||
|
After enabling shell integration, you can send messages to Emacs from
|
||||||
|
your shell. Then you can handle the message on Emacs side using usual
|
||||||
|
Emacs Lisp function.
|
||||||
|
|
||||||
|
When shell integration script is loaded, a function named
|
||||||
|
@command{_eat_msg} is defined in your shell. You can use this to send
|
||||||
|
any message to Emacs. (The @samp{_} in the beginning of the function
|
||||||
|
name is intentional to prevent shadowing any actual command.)
|
||||||
|
|
||||||
|
@deffn Command _eat_msg @var{handler-name} @var{message}...
|
||||||
|
Send message @var{message}, handled by the handler named
|
||||||
|
@var{handler-name} in Emacs.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
The messages are handled with the handlers defined in
|
||||||
|
@code{eat-message-handler-alist}.
|
||||||
|
|
||||||
|
@vindex eat-message-handler-alist
|
||||||
|
@defopt eat-message-handler-alist
|
||||||
|
Alist of message handler name and its handler function. The keys are
|
||||||
|
the names of message handlers (i.e. the @var{handler-name} argument of
|
||||||
|
@command{_eat_msg}), and the values are their respective handler
|
||||||
|
functions. The handler function is called with the @var{message}
|
||||||
|
arguments of @command{_eat_msg}. Messages with undefined handlers are
|
||||||
|
ignored. To disable message passing, set this to nil.
|
||||||
|
@end defopt
|
||||||
|
|
||||||
|
Beware, messages can be sent by malicious and/or buggy programs
|
||||||
|
running in the shell, therefore you should always verify the message
|
||||||
|
before doing anywhere.
|
||||||
|
|
||||||
@anchor{Native Shell Prompt Editing}
|
@anchor{Native Shell Prompt Editing}
|
||||||
@cindex native shell prompt editing
|
@cindex native shell prompt editing
|
||||||
@cindex shell prompt native editing
|
@cindex shell prompt native editing
|
||||||
|
|
|
@ -89,21 +89,13 @@ __eat_enable_integration ()
|
||||||
PROMPT_COMMAND+=(__eat_prompt_command)
|
PROMPT_COMMAND+=(__eat_prompt_command)
|
||||||
trap '__eat_before_exec' DEBUG
|
trap '__eat_before_exec' DEBUG
|
||||||
# Wrap 'PROMPT_COMMAND' to avoid it getting trapped in 'DEBUG' trap.
|
# Wrap 'PROMPT_COMMAND' to avoid it getting trapped in 'DEBUG' trap.
|
||||||
# Step 1: Append to PROMPT_COMMAND.
|
|
||||||
PROMPT_COMMAND+=(__eat_after_prompt_command)
|
|
||||||
# Step 2: Prepend to PROMPT_COMMAND.
|
|
||||||
# Step 2.1: Move all elements to make the first index free.
|
|
||||||
# Fun fact: Microsoft doesn't still about know this simple trick.
|
# Fun fact: Microsoft doesn't still about know this simple trick.
|
||||||
# They ended up using something as silly and pityful as
|
# They ended up using something as silly and pityful as
|
||||||
# 'VAR=$PROMPT_COMMAND' to copy a Bash array in VSCode Bash
|
# 'VAR=$PROMPT_COMMAND' to copy a Bash array in VSCode Bash
|
||||||
# integration script, which simply won't work ever, and then
|
# integration script, which simply won't work ever, and then
|
||||||
# complain about Bash in the comments! xD
|
# complain about Bash in the comments! xD
|
||||||
for i in $(eval "echo {${#PROMPT_COMMAND[*]}..1..-1}")
|
PROMPT_COMMAND+=(__eat_after_prompt_command)
|
||||||
do
|
PROMPT_COMMAND=(__eat_before_prompt_command "${PROMPT_COMMAND[@]}")
|
||||||
PROMPT_COMMAND[$i]=${PROMPT_COMMAND[$((i-1))]}
|
|
||||||
done
|
|
||||||
# Step 2.2: Assign the first element.
|
|
||||||
PROMPT_COMMAND[0]=__eat_before_prompt_command
|
|
||||||
# Send the history, for native shell prompt.
|
# Send the history, for native shell prompt.
|
||||||
printf '\e]51;e;I;0;bash;%s;%s\e\\' \
|
printf '\e]51;e;I;0;bash;%s;%s\e\\' \
|
||||||
"$(printf "%s" "$HOSTNAME" | base64)" \
|
"$(printf "%s" "$HOSTNAME" | base64)" \
|
||||||
|
@ -117,6 +109,16 @@ __eat_enable_integration ()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_eat_msg () {
|
||||||
|
local msg=$'\e]51;e;M'
|
||||||
|
for _ in $(eval "echo {1..$#}")
|
||||||
|
do
|
||||||
|
msg="$msg;$(printf "%s" "$1" | base64)"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
printf "%s\e\\" "$msg"
|
||||||
|
}
|
||||||
|
|
||||||
# Enable.
|
# Enable.
|
||||||
if test -z "$__eat_integration_enabled" && \
|
if test -z "$__eat_integration_enabled" && \
|
||||||
test "${TERM:0:4}" = "eat-"
|
test "${TERM:0:4}" = "eat-"
|
||||||
|
|
|
@ -80,6 +80,16 @@ __eat_enable_integration ()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_eat_msg () {
|
||||||
|
local msg=$'\e]51;e;M'
|
||||||
|
for _ in $(eval "echo {1..$#}")
|
||||||
|
do
|
||||||
|
msg="$msg;$(printf "%s" "$1" | base64)"
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
printf "%s\e\\" "$msg"
|
||||||
|
}
|
||||||
|
|
||||||
# Enable.
|
# Enable.
|
||||||
if test -z "$__eat_integration_enabled" && \
|
if test -z "$__eat_integration_enabled" && \
|
||||||
test "${TERM:0:4}" = "eat-"
|
test "${TERM:0:4}" = "eat-"
|
||||||
|
|
Loading…
Add table
Reference in a new issue