Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions ai-code-backends-infra-ghostel.el
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

(defvar ai-code-backends-infra--session-terminal-backend)
(defvar ghostel--copy-mode-active nil)
(defvar ghostel--input-mode nil)
(defvar ghostel-kill-buffer-on-exit nil)
Comment thread
tninja marked this conversation as resolved.
Outdated
(defvar ghostel-set-title-function nil)

(defun ai-code-backends-infra-ghostel-ensure-backend ()
Expand All @@ -43,7 +45,8 @@

(defun ai-code-backends-infra-ghostel-navigation-mode-p ()
"Return non-nil when the current Ghostel buffer is in copy mode."
(bound-and-true-p ghostel--copy-mode-active))
(or (bound-and-true-p ghostel--copy-mode-active)
(eq ghostel--input-mode 'copy)))

(defun ai-code-backends-infra-ghostel-install-navigation-cursor-sync ()
"Install cursor synchronization for Ghostel navigation mode."
Expand Down Expand Up @@ -73,6 +76,7 @@
(defun ai-code-backends-infra--configure-ghostel-buffer ()
"Configure the current Ghostel buffer for AI Code sessions."
(setq-local ghostel-set-title-function nil)
(setq-local ghostel-kill-buffer-on-exit nil)
(ai-code-backends-infra--configure-session-input-shortcuts)
(ai-code-backends-infra--install-navigation-cursor-sync))

Expand All @@ -86,7 +90,10 @@
(cond
((not program) nil)
((fboundp 'ghostel-exec)
(ghostel-exec buffer program args))
(let ((proc (ghostel-exec buffer program args)))
;; `ghostel-exec' enters `ghostel-mode', which resets local state.
(ai-code-backends-infra--configure-ghostel-buffer)
proc))
(t
(user-error
"Ghostel backend requires a Ghostel version that provides `ghostel-exec`"))))))
Expand All @@ -107,6 +114,11 @@ variables for the terminal process."
(when (processp proc)
(ignore-errors
(set-process-query-on-exit-flag proc nil))
(when-let ((sentinel (ignore-errors (process-sentinel proc))))
(ignore-errors
(process-put proc
'ai-code-backends-infra--ghostel-sentinel
sentinel)))
(let ((orig-filter (process-filter proc)))
(set-process-filter
proc
Expand Down
70 changes: 46 additions & 24 deletions ai-code-backends-infra.el
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ being sent for the response completion.")
This validates the textual shape, but not UUID version or variant bits.")

(defun ai-code-backends-infra--selected-session-id ()
"Return the active region text when it contains a UUID session id."
"Return active region text if it has a UUID session id."
(when (use-region-p)
(let ((candidate
(string-trim
Expand Down Expand Up @@ -197,11 +197,12 @@ Set to t to debug scrollback-preservation transformations.")

(defvar-local ai-code-backends-infra--last-scrollback-inject-time 0
"Timestamp of the last scrollback-preservation injection.
Used to throttle injections per `ai-code-backends-infra-scrollback-inject-interval'.")
Used to throttle injections per
`ai-code-backends-infra-scrollback-inject-interval'.")

(defvar-local ai-code-backends-infra--sync-redraw-scrollback nil
"When non-nil, inject scrollback-preserving newlines before
synchronized-update frame redraws (\\e[?2026h\\e[1;1H).
"Non-nil means inject scrollback-preserving newlines before redraws.
This applies to synchronized-update frame redraws (\\e[?2026h\\e[1;1H).
Backends that hardcode alternate screen buffer should set this
to t via their post-start-fn.")

Expand Down Expand Up @@ -304,7 +305,7 @@ the current buffer is an AI session buffer, apply these transformations:
(declare-function ai-code-notifications-response-ready "ai-code-notifications" (&optional backend-name))

(defun ai-code-backends-infra--output-meaningful-p (output)
"Return non-nil when OUTPUT contains meaningful printable content."
"Return non-nil if OUTPUT has meaningful printable content."
(let* ((str (or output ""))
;; Strip OSC sequences (ESC ] ... BEL or ESC ] ... ESC \).
(str (replace-regexp-in-string "\x1b\\][^\x07\x1b]*\\(?:\x07\\|\x1b\\\\\\)" "" str))
Expand Down Expand Up @@ -501,6 +502,7 @@ MULTILINE-INPUT-SEQUENCE configures `S-<return>' and `C-<return>' when non-nil."

(defun ai-code-backends-infra--terminal-reflow-filter (original-fn &rest args)
"Filter terminal reflows to prevent height-only resize triggers.
ORIGINAL-FN is the terminal resize function and ARGS are its arguments.
Suppress reflow when terminal width is unchanged or when the session
buffer is in scroll/copy mode, working around bug #1422."
(let* ((base-result (apply original-fn args))
Expand Down Expand Up @@ -594,9 +596,14 @@ from the window where it was initially created."
(when (and buffer window (buffer-live-p buffer) (window-live-p window))
(with-current-buffer buffer
(when-let ((proc (get-buffer-process buffer)))
(let ((height (window-body-height window))
(width (window-body-width window)))
(set-process-window-size proc height width))))))
(if (eq (ai-code-backends-infra--current-terminal-backend) 'ghostel)
(when-let ((size (funcall (ai-code-backends-infra-ghostel-resize-handler)
proc
(list window))))
(set-process-window-size proc (cdr size) (car size)))
(let ((height (window-body-height window))
(width (window-body-width window)))
(set-process-window-size proc height width)))))))

;;; Session Helpers

Expand Down Expand Up @@ -639,6 +646,7 @@ from the window where it was initially created."

(defun ai-code-backends-infra--attached-file-session (prefix source-buffer working-dir)
"Return attached session state for PREFIX and SOURCE-BUFFER.
WORKING-DIR is the directory used to validate compatible sessions.
Return a cons of (BUFFER . MISSING-P)."
(let ((key (ai-code-backends-infra--file-session-map-key prefix source-buffer)))
(if (null key)
Expand Down Expand Up @@ -809,6 +817,7 @@ Return a cons of (base-name . instance-name) or nil."

(defun ai-code-backends-infra--select-session-buffer (prefix directory &optional force-prompt)
"Select a session buffer for PREFIX in DIRECTORY.
FORCE-PROMPT means always prompt even if a session was remembered.
Returns the selected buffer or nil if none exist."
(let* ((remembered (gethash (ai-code-backends-infra--session-map-key prefix directory)
ai-code-backends-infra--directory-buffer-map))
Expand Down Expand Up @@ -880,8 +889,9 @@ DEFAULT-INSTANCE-NAME seeds the minibuffer when prompting."
"default"))

(defun ai-code-backends-infra--resolve-start-command (program switches arg &optional prompt-label)
"Build command string for PROGRAM and SWITCHES.
When ARG is non-nil, prompt for CLI args using SWITCHES as default input.
"Build command string for PROGRAM.
SWITCHES is the default command-line argument list.
Use it as default input when ARG is non-nil and CLI args are prompted.
PROMPT-LABEL is used in the minibuffer prompt.
When resuming and the active region contains a UUID, prompt as though ARG
were non-nil and append that UUID to the default CLI args."
Expand Down Expand Up @@ -913,6 +923,7 @@ were non-nil and append that UUID to the default CLI args."
(defun ai-code-backends-infra--cleanup-session (directory buffer-name process-table
&optional instance-name prefix event)
"Clean up a session for DIRECTORY using BUFFER-NAME and PROCESS-TABLE.
INSTANCE-NAME and PREFIX identify the session map entry to remove.
EVENT is the process sentinel event string. When EVENT is non-nil and does
not start with \"finished\", the buffer is preserved so the user can inspect
any error output left behind by the CLI."
Expand Down Expand Up @@ -998,6 +1009,7 @@ Return a plist with target information plus the current buffer and process."
(defun ai-code-backends-infra--reuse-session-window (buffer working-dir
prefix multiline-input-sequence)
"Toggle visibility for an existing session BUFFER.
WORKING-DIR, PREFIX, and MULTILINE-INPUT-SEQUENCE refresh session state.
When BUFFER is already visible, close its window.
Otherwise refresh session-local state and display it."
(if (get-buffer-window buffer)
Expand All @@ -1014,19 +1026,28 @@ Otherwise refresh session-local state and display it."
prefix escape-fn cleanup-fn
multiline-input-sequence
post-start-fn)
"Finalize a successfully started session BUFFER and PROCESS."
(set-process-sentinel
process
(lambda (_proc event)
(ai-code-backends-infra--cleanup-session
working-dir
buffer-name
process-table
resolved-instance
prefix
event)
(when cleanup-fn
(funcall cleanup-fn))))
"Finalize a successfully started session BUFFER and PROCESS.
WORKING-DIR, BUFFER-NAME, PROCESS-TABLE, RESOLVED-INSTANCE, and PREFIX
identify the session for cleanup and reuse. ESCAPE-FN, CLEANUP-FN,
MULTILINE-INPUT-SEQUENCE, and POST-START-FN install session behavior."
(let ((previous-sentinel
(ignore-errors
(process-get process 'ai-code-backends-infra--ghostel-sentinel))))
(set-process-sentinel
process
(lambda (proc event)
(when previous-sentinel
(ignore-errors
(funcall previous-sentinel proc event)))
(ai-code-backends-infra--cleanup-session
working-dir
buffer-name
process-table
resolved-instance
prefix
event)
(when cleanup-fn
(funcall cleanup-fn)))))
(ai-code-backends-infra--configure-session-buffer
buffer escape-fn multiline-input-sequence)
(when post-start-fn
Expand All @@ -1043,7 +1064,7 @@ Otherwise refresh session-local state and display it."
(ai-code-backends-infra--display-buffer-in-side-window buffer))

(defun ai-code-backends-infra--handle-session-start-failure (buffer session-key process-table)
"Handle startup failure for BUFFER and SESSION-KEY."
"Handle startup failure for BUFFER and SESSION-KEY in PROCESS-TABLE."
(remhash session-key process-table)
(if (buffer-live-p buffer)
(progn
Expand Down Expand Up @@ -1129,6 +1150,7 @@ session starts successfully."
(defun ai-code-backends-infra--switch-to-session-buffer (buffer-name missing-message
&optional prefix working-dir force-prompt)
"Switch to BUFFER-NAME or signal MISSING-MESSAGE.
FORCE-PROMPT means always prompt when PREFIX and WORKING-DIR are provided.
When PREFIX and WORKING-DIR are provided, select from multiple sessions."
(let* ((source-buffer (current-buffer))
(buffer (ai-code-backends-infra--resolve-session-buffer
Expand Down
Loading
Loading