Exec / shell panes (:!)¶
gdbforge can open an external PTY session in the focused pane, similar to Vim’s :! but as a persistent console widget (not a one-shot filter).
Companion docs: COMMAND_SYSTEM.md · termforge: UI Architecture · INPUT.md · DEBUGGER_INTEGRATION.md
User commands¶
| Command | Effect |
|---|---|
:!bash |
Start bash on a PTY; show Exec widget in the focused pane |
:!ls |
Same for ls (short-lived; after exit, any key returns to the previous widget) |
:!ssh user@host |
Same for any argv |
:b exec |
Re-show the last Exec widget (if still registered) |
:b gdb / :b about / :b help / :b logger / :b breakpoint / :b threads / :b callstack / :b io / :b output |
Swap other built-in views into the focused pane |
:edit / :edit file.c |
Project source picker, or open a source file (:e = unique prefix) |
:b file.c |
Switch to an already-open file buffer |
<C-o> (normal mode) |
Jump back to the previous widget in this pane (Vim-style jump list) |
Bang may be glued or spaced: :!ls and :! ls both work.
Architecture¶
flowchart LR
Cmd[":!bash"] --> OnRun
OnRun --> Client["execcli.ExecClient · *ptyx.TTY"]
OnRun --> Widget["ExecWidget · CompositeTerminal"]
Client -->|WireTTY| Widget
Widget -->|SendRaw| Client
OnRun --> Jump["push previous widget"]
JumpBack["Ctrl-O JumpBack"] --> Jump
| Layer | Package / type | Role |
|---|---|---|
| Command | LeafRest("!", OnRun) |
Rest-args leaf; remainder of line → argv |
| PTY | *ptyx.TTY (ptyx.Start) |
Process PTY: Subscribe, SendRaw, SetSize |
| Client | termforge/execcli.ExecClient |
Thin embed of *ptyx.TTY |
| Terminal | CompositeTerminal + WireTTY |
xterm emulator in Exec pane |
| Widget | widgets.ExecWidget |
View — WireExec, keys, cursor |
| App | DebuggerApp.OnRun |
Owns ExecClient; swapFocusedWidget; insert mode |
GDB/IO panes use the same WireTTY pattern; exec has no MI parser — plain terminal bytes only.
Nested gdbforge: You can run ./gdbforge from :!bash, but nested full TUIs inside the Exec pane are not supported — use an external terminal or tmux pane for a second gdbforge session.
Rest-args (LeafRest / RestArgs)¶
Normal colon commands walk every token as a tree child (:set equalalways). That cannot parse :!ssh root@host.
A rest-args leaf (RestArgs == true) means: after accepting this node, stop walking the tree and pass remaining tokens to Action.
:!ssh root@host
│ └──────────┘
│ p.args (not Accept()'d as children)
└─ current stays on "!" node → OnRun
Implementation: termforge/commands/dsl.go (CmdRest / LeafRest) and CommandParser.Parse / Sync (including glued :!ls).
Terminal rendering¶
Exec uses CompositeTerminal (xterm via gitpod-io/xterm-go):
- Full ANSI/VT sequences from bash, ssh, etc.
- Keys forwarded as raw bytes (
WireTTYInput) - Inverse cursor when the Exec pane is focused
- Scrollback in the emulator buffer
Copy/paste: mouse selection + clipboard bridge (same as other panes); paste sends bytes to the PTY.
Copy / paste¶
| Action | Behavior |
|---|---|
Mouse drag + Ctrl-C |
Copy selection (ANSI stripped where applicable) |
Ctrl-V |
Paste into cmdline when in command mode; in Exec pane, keys go to PTY |
| Middle-click | Platform PRIMARY paste where supported |
EventClipboard |
In command mode → CmdWidget only; otherwise → focused widget |
Jump list (Ctrl-O)¶
Workspace.swapFocusedWidget (thin DebuggerApp delegate) pushes the outgoing widget before :b / :e / :! swaps.
| API | Role |
|---|---|
pushWidgetJump |
Append (dedupe consecutive, cap 32) — on Workspace |
JumpBack |
Pop and ReplaceFocusedWidget without pushing |
| Binding | <C-o> in internal/app/keybindings.go (normal mode) |
Example: GDB → :b about → <C-o> → GDB again.
Lifecycle notes¶
- Each
:!…restarts the exec session (closes previousExecClient). - When the PTY process exits, the Exec pane shows
[exec] process exited — press any key to return, then any key runsJumpBackto the previous widget and clears theexecbuiltin. - App exit (
:quit/ GDBq→gdb-exit) still closesexecClientif present (DebuggerApp.Close).
Key source files¶
| Path | Responsibility |
|---|---|
internal/app/command_tree.go |
LeafRest("!", a.OnRun) |
internal/app/actions.go |
OnRun, startExecSession |
internal/app/workspace_place.go |
swapFocusedWidget, JumpBack, jump list |
internal/app/keybindings.go |
<C-o> |
termforge/execcli/exec_client.go |
ptyx.Start wrapper |
internal/gdbforge/widgets/exec_widget.go |
Exec terminal view |
termforge/composite_terminal.go |
xterm + WireTTY |
termforge/wire_tty.go |
PTY ↔ terminal bridge |
termforge/ptyx/tty.go |
Unified PTY type |