@@ -48,11 +48,25 @@
48 48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Copy reads from r (typically a PTY fd) and also writes to w (typically
49 49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// os.Stdout for the interactive terminal). Lines are emitted via onLine.
50 50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Blocks until r returns EOF or error.
51 51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func (m *PTYMirror) Copy(r io.Reader, w io.Writer) error {
52 52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
buf := make([]byte, 4096)
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- var lineBuf bytes.Buffer
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ lineCh := make(chan []byte, 64) // buffered channel for async line processing
54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ done := make(chan struct{})
55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Process lines in a separate goroutine so terminal is never blocked.
57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ go func() {
58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ defer close(done)
59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ var lineBuf bytes.Buffer
60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for chunk := range lineCh {
61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ lineBuf.Write(chunk)
62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m.emitLines(&lineBuf)
63 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
64 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if lineBuf.Len() > 0 {
65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ m.emitLine(lineBuf.String())
66 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
67 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }()
54 68 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
55 69 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
for {
56 70 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
n, err := r.Read(buf)
57 71 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if n > 0 {
58 72 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
// Detect busy signals for interrupt logic.
@@ -60,23 +74,26 @@
60 74 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
lower := strings.ToLower(string(buf[:n]))
61 75 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if strings.Contains(lower, "esc to interrupt") || strings.Contains(lower, "working...") {
62 76 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
m.BusyCallback(time.Now())
63 77 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
64 78 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // Pass through to terminal.
79 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Pass through to terminal — ALWAYS immediate, never blocked.
66 80 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if w != nil {
67 81 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
_, _ = w.Write(buf[:n])
68 82 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
69 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // Buffer and emit lines.
70 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- lineBuf.Write(buf[:n])
71 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- m.emitLines(&lineBuf)
83 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Send to line processor (non-blocking with buffered channel).
84 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ chunk := make([]byte, n)
85 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ copy(chunk, buf[:n])
86 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ select {
87 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ case lineCh <- chunk:
88 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ default:
89 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // Channel full — drop this chunk rather than block terminal.
90 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
72 91 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
73 92 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if err != nil {
74 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- // Flush remaining buffer.
75 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- if lineBuf.Len() > 0 {
76 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- m.emitLine(lineBuf.String())
77 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
- }
93 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ close(lineCh)
94 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ <-done
78 95 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if err == io.EOF {
79 96 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return nil
80 97 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
81 98 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
return err
82 99 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
83 100 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!