Skip to content

feat(parsing): opt-out of shell-style backslash escaping (EscapeLiteral) - #89

Merged
maxlandon merged 1 commit into
mainfrom
dev
Jul 18, 2026
Merged

feat(parsing): opt-out of shell-style backslash escaping (EscapeLiteral)#89
maxlandon merged 1 commit into
mainfrom
dev

Conversation

@maxlandon

Copy link
Copy Markdown
Member

Closes #88.

Problem

The console splits every input line with POSIX shell semantics, so an unquoted backslash escapes the next character. When the console is used as a general Cobra frontend rather than a shell, this is surprising:

  • ls C:\Windows\Temp reaches the command as ["ls", "C:WindowsTemp"] — the backslashes are eaten.
  • ls C:\Windows\Temp\ is treated as an incomplete line (trailing backslash = continuation) and blocks waiting for more input.

Users currently have to quote the whole value ('C:\Windows\Temp') or double every backslash. As @h3zh1 noted in #88, that's cumbersome for a Cobra-frontend use case.

I confirmed empirically that the mvdan.cc/sh comment-stripping stage is innocent — it round-trips backslashes untouched. The mangling comes entirely from the shellquote-derived word splitters.

Solution

A console-wide EscapeMode, default unchanged:

app := console.New("myapp")
app.SetEscapeMode(console.EscapeLiteral)
Mode Behavior
EscapeShell (default) POSIX escape rules — byte-for-byte unchanged
EscapeLiteral backslash is an ordinary char; quotes still group words; C:\Windows\Temp passes through verbatim; a trailing backslash no longer requests another line

Implementation: each of the three shellquote-derived splitters gained an EscapeMode parameter; in literal mode the case char == EscapeChar branch is simply skipped, so backslash falls through as a plain rune. Because literal mode never enters the escape state, ErrUnterminatedEscape can't fire — the trailing-backslash multiline bug is fixed for free, while unterminated quotes still correctly request more input.

Per the design discussion, the mode is threaded through all three call sites — command execution (line.Parse), multiline detection (AcceptMultiline), and completion/highlighting — so a C:\ path also completes and highlights consistently. Menu.RunCommandLine honors it too.

Tests

  • All pre-existing EscapeShell tests pass unmodified (only the new mode arg threaded through).
  • New literal-mode unit tests in internal/line and internal/completion.
  • New end-to-end TestRunCommandLineEscapeMode proving the mode reaches the actual arg vector a cobra command receives — including a case pinning the current shell-mode C:\Windows\TempC:WindowsTemp behavior for contrast.

go build, go vet, go test ./... all green.

🤖 Generated with Claude Code

…scaping

By default the console splits input with POSIX shell semantics, so an
unquoted backslash escapes the next character. This mangles values that
carry literal backslashes (e.g. Windows paths: `C:\Windows\Temp` becomes
`C:WindowsTemp`) and makes a trailing backslash request a continuation
line, which is surprising when the console is used as a general Cobra
frontend rather than a shell.

Introduce a console-wide EscapeMode with two values:

  - EscapeShell   (default): unchanged POSIX escape behavior.
  - EscapeLiteral: backslashes are ordinary characters; quotes still
    group words, `C:\Windows\Temp` is passed through verbatim, and a
    trailing backslash no longer requests another line.

Select it with Console.SetEscapeMode(console.EscapeLiteral). The mode is
threaded through all three shell-word splitters so command execution,
multiline-continuation detection, and completion/highlighting stay
consistent. The EscapeShell path is byte-for-byte unchanged.

Closes #88.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 27.32%. Comparing base (f44dc1d) to head (397edc2).

Files with missing lines Patch % Lines
internal/completion/line.go 75.00% 2 Missing and 1 partial ⚠️
console.go 80.00% 1 Missing and 1 partial ⚠️
internal/line/line.go 84.61% 0 Missing and 2 partials ⚠️
run.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #89      +/-   ##
==========================================
+ Coverage   26.75%   27.32%   +0.57%     
==========================================
  Files          28       28              
  Lines        2119     2137      +18     
==========================================
+ Hits          567      584      +17     
+ Misses       1505     1503       -2     
- Partials       47       50       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@maxlandon
maxlandon merged commit 738e5de into main Jul 18, 2026
8 checks passed
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.

Allow applications to opt out of shell-style backslash escaping

1 participant