Skip to content

Run a project inside VS Code: a real terminal by default, output channel optional - #11

Merged
valentin-baron merged 1 commit into
valentin-baron:mainfrom
ramedw:feat/run-in-terminal
Sep 7, 2026
Merged

Run a project inside VS Code: a real terminal by default, output channel optional#11
valentin-baron merged 1 commit into
valentin-baron:mainfrom
ramedw:feat/run-in-terminal

Conversation

@ramedw

@ramedw ramedw commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What

Run / Run Selected Project (F9) launched the project's executable detached with its output discarded — a console application's output was gone before anyone could read it. It now runs inside the editor. ddk.projects.runIn picks where:

mode what you get
terminal (default) the executable is the process of its own DDK Run: <project> terminal (run parameters applied, cwd = the exe's directory). A real console, so the program's own colors work, the view follows output, and a program reading stdin can be typed into. VS Code reports the exit code. A re-run replaces that project's terminal.
output stdout/stderr piped into a new DDK Run output channel: header with the exact command line, footer with exit code (or signal) and elapsed time, concurrent runs labelled by project, cleared per run, revealed only once a program actually prints (ddk.projects.runRevealOutput).
detached the previous behavior.

ddk run (CLI/MCP) is untouched and still launches detached.

Why terminal is the default

A program coloring its output through SetConsoleTextAttribute — DUnitX's test runner, for instance — writes no color information into stdout. The color is an attribute of the console screen buffer's cells, set by a side-channel API call that fails outright once the handle is a pipe. In output mode those colors are therefore unrecoverable by any reader, and the Output panel renders no ANSI escape sequences either. Only a real console produces them, and ConPTY re-encodes them into sequences the terminal draws — which is what a VS Code terminal is.

Consequences for output mode, all documented in the settings and README: plain text (ANSI sequences, carriage-return overwrites and other control characters are stripped rather than shown as ←[32m litter), no keyboard input (a program reading stdin sees EOF), the process is a child of VS Code so closing VS Code breaks its pipe, and VS Code's "smart scroll" stops the panel from following output once the cursor leaves the last line.

Encoding (output mode only)

ddk.projects.runOutputEncoding defaults to ansi — Windows' own default charset for non-Unicode text (GetACP, read from HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage\ACP), which is what a program writing to a redirected handle normally produces. Also available: oem (the console codepage chcp reports), utf8 (what a current Delphi RTL emits while redirected), fixed codepages, and auto (per line UTF-8, falling back to ANSI). CP437/850/852 are decoded from built-in tables, since TextDecoder supports no DOS codepage; the tables were generated from and verified byte-exact against .NET's encodings. A terminal decodes its own output, so the setting does not apply there.

Verification

Driven from a console-less parent process, the way the extension host runs, against probes compiled with dcc32 (a console application and a VCL GUI application):

  • Output capture end to end: umlauts, en dash, stderr, arguments, exit code 3 — all correct in the channel.
  • Encoding matrix: ansi (1252 bytes), oem (CP850 bytes), utf8, auto both ways, explicit cp850; codepage detection returns ACP 1252 / console 850 on the test machine.
  • Sanitizing: SGR/256-color/erase/cursor sequences, OSC titles (BEL- and ST-terminated), charset escapes, CR overwrites, BEL, tab preserved — 15 cases.
  • Line handling: CRLF and LF, a line straddling two chunks, a multi-byte character split across chunks, unterminated lines flushed when the program goes quiet, blank lines, per-line labels.
  • Failure path: a missing executable produces exactly one ✖ … ENOENT footer (error and close both fire on a failed spawn).
  • windowsHide suppresses the empty console window a captured console application would otherwise pop up, and does not hide a VCL main form (the VCL ignores the hidden show-command).

npm run check-types, npm run lint (only the 4 warnings that predate this branch, in untouched files) and node esbuild.js are clean.

The version stays [Unreleased] in the changelog — bumping it is a separate release commit, as in this repo's history.

🤖 Generated with Claude Code

Run / Run Selected Project (F9) launched the executable detached with its
output discarded, so a console application's output was gone before it could
be read. It now runs inside the editor, with ddk.projects.runIn choosing
where:

- terminal (default): the executable runs in its own "DDK Run: <project>"
  terminal, as a task, started with the project's run parameters in the
  executable's directory. A terminal is a real console (ConPTY), so the
  program keeps everything a console gives it: its own colors — including
  those a program paints through the Windows console API, such as DUnitX's
  test runner — output the view follows, and keyboard input. The terminal
  stays open on the finished output until it is closed or the next run of that
  project reuses and clears it. A task rather than a plain terminal for
  exactly that reason: a terminal whose own process is the executable is
  disposed the moment that process ends, taking a short-lived program's output
  with it. ProcessExecution keeps a shell out of the way, so arguments reach
  the program as given.
- output: stdout/stderr are piped into a new "DDK Run" output channel,
  framed by a header with the exact command line and a footer with the exit
  code (or terminating signal) and elapsed time; concurrent runs are labelled
  by project. The channel is cleared per run unless another run is still
  writing, and for a silent GUI application it is only revealed once output
  appears (ddk.projects.runRevealOutput). Output is decoded with Windows'
  system ANSI codepage by default (GetACP, read from the registry), with
  ddk.projects.runOutputEncoding offering oem, utf8, fixed codepages
  (cp437/cp850/cp852 come from built-in tables, since TextDecoder has no DOS
  codepages) and auto (per line UTF-8, falling back to ANSI). ANSI escape
  sequences, carriage-return overwrites and other control characters are
  stripped, because the Output panel renders none of them.
- detached: the previous behavior.

Colors are the reason terminal is the default: SetConsoleTextAttribute writes
no color information into stdout at all — it sets an attribute on the console
screen buffer's cells, and once the handle is a pipe the call fails — so in
output mode those colors cannot be recovered by any reader. Only a real
console produces them, and ConPTY re-encodes them for the terminal.

Verified against Delphi-built probes (console and VCL GUI) driven from a
console-less parent, the way the extension host runs: output capture,
codepage decoding, ANSI/CR sanitizing, line splitting across chunks, the
missing-executable path, and that windowsHide suppresses the empty console
window of a captured console application without hiding a VCL form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ramedw
ramedw force-pushed the feat/run-in-terminal branch from f438853 to 0278f3e Compare August 5, 2026 13:04
@valentin-baron
valentin-baron merged commit c3909bd into valentin-baron:main Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants