Add named keys, input-line reporting and append guard to terminal_type

This commit is contained in:
Ian Gulliver
2026-10-20 12:20:11 -07:00
parent cfd06d5582
commit 181482e5d0
3 changed files with 160 additions and 20 deletions
+76 -1
View File
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
@@ -22,8 +23,12 @@ const (
clientBuf = 256
MaxSize = 1 << 15
promptMark = "\x1b]133;D;"
lineMark = "\x1b]7701;"
carryMax = 64 << 10
)
var lineMarks = [][]byte{[]byte(promptMark), []byte(lineMark)}
const zshenv = `if [[ -n $BYRE_ZDOTDIR ]]; then
ZDOTDIR=$BYRE_ZDOTDIR
else
@@ -35,6 +40,13 @@ if [[ -o interactive ]]; then
unsetopt PROMPT_SP
_byre_precmd() { print -n "\e]133;D;$?\a" }
precmd_functions+=(_byre_precmd)
_byre_line() { local line=$PREBUFFER$BUFFER; print -rn -- $'\e]7701;L'"${line//[[:cntrl:]]/ }"$'\a' }
_byre_line_end() { print -rn -- $'\e]7701;E\a' }
zmodload zsh/zle
autoload -Uz add-zle-hook-widget
add-zle-hook-widget line-init _byre_line
add-zle-hook-widget line-pre-redraw _byre_line
add-zle-hook-widget line-finish _byre_line_end
fi
`
@@ -58,6 +70,9 @@ type Terminal struct {
started time.Time
lastOutput time.Time
wake *wake
carry []byte
line string
atPrompt bool
}
type wake struct {
@@ -188,6 +203,9 @@ func (term *Terminal) output(b []byte) {
if term.wake != nil {
term.checkWake(b)
}
if term.Prompts {
term.trackLine(b)
}
term.backlog = append(term.backlog, b...)
if len(term.backlog) > 2*backlogSize {
term.backlog = bytes.Clone(term.backlog[len(term.backlog)-backlogSize:])
@@ -222,10 +240,67 @@ func (term *Terminal) checkWake(b []byte) {
go w.fn(string(w.out[start+len(promptMark):start+end]), w.out[:start])
}
func (term *Terminal) WakeOnPrompt(fn func(exit string, out []byte)) {
func (term *Terminal) trackLine(b []byte) {
s := append(term.carry, b...)
term.carry = nil
for {
i := bytes.Index(s, []byte("\x1b]"))
if i < 0 {
if len(s) > 0 && s[len(s)-1] == 0x1b {
term.carry = []byte{0x1b}
}
return
}
rest := s[i:]
var prefix []byte
for _, m := range lineMarks {
if bytes.HasPrefix(rest, m) {
prefix = m
break
}
if len(rest) < len(m) && bytes.HasPrefix(m, rest) {
term.carry = bytes.Clone(rest)
return
}
}
if prefix == nil {
s = rest[2:]
continue
}
end := bytes.IndexByte(rest, '\a')
if end < 0 {
if len(rest) < carryMax {
term.carry = bytes.Clone(rest)
}
return
}
body := string(rest[len(prefix):end])
switch {
case string(prefix) == promptMark:
term.atPrompt, term.line = true, ""
case body == "E":
term.atPrompt, term.line = false, ""
case strings.HasPrefix(body, "L"):
term.atPrompt, term.line = true, body[1:]
}
s = rest[end+1:]
}
}
func (term *Terminal) InputLine() (string, bool) {
term.mu.Lock()
defer term.mu.Unlock()
return term.line, term.atPrompt
}
func (term *Terminal) WakeOnPrompt(fn func(exit string, out []byte), mayRun bool) bool {
term.mu.Lock()
defer term.mu.Unlock()
if term.atPrompt && term.line == "" || !term.atPrompt && !mayRun {
return false
}
term.wake = &wake{fn: fn}
return true
}
func (term *Terminal) CancelWake() {