Skip to content

Actually support multi-line julia> prompts (follow-up to #285) - #301

Merged
pfitzseb merged 2 commits into
JuliaEditorSupport:masterfrom
Pangoraw:fix/multiline-console-begin-end
Sep 7, 2026
Merged

pfitzseb merged 2 commits into
JuliaEditorSupport:masterfrom
Pangoraw:fix/multiline-console-begin-end

Conversation

@Pangoraw

Copy link
Copy Markdown
Member

Follow-up to #285, which does not actually work.

The multi-line regex is dead code

#285 changed the julia> pattern to a single match rule containing a newline:

^(julia>)(\s+.*(?:\n\s{6}\s+.*)*)

TextMate grammars are tokenized one line at a time. vscode-textmate — what VS Code and GitHub both use — calls tokenizeLine() per line, and the string it passes never contains a newline, so (?:\n\s{6}\s+.*)* can only ever match zero times. Only begin/end rules carry state across lines.

Tokenizing the current grammar line by line, the way a real consumer does:

"julia>"           => punctuation.separator.prompt.julia.console
" begin"           => source.julia.console      <- capture 2, no Julia scopes
"           x = 1" => source.julia.console      <- nothing
"       end"       => source.julia.console      <- nothing

So multi-line REPL input has been unhighlighted on GitHub this whole time, even though the change shipped in Linguist v9.2.0.

Why CI went green

tokenize() in test/test.js passed the entire multi-line source to grammar.tokenizeLine() as one line, handing oniguruma a string that really does contain newlines. It is the only consumer that does this. The tell-tale sign is in the expected tokens of the multi-line test — { value: '\ntrue\n\n' }. A token whose value contains a newline is impossible under real tokenization.

Commit 1 — the fix

A begin/end rule that stays open while lines are indented to the REPL's 7-column continuation alignment:

{
  "begin": "^(julia>)",
  "beginCaptures": { "1": { "name": "..." } },
  "end": "^(?!\\s{7})",
  "patterns": [{ "include": "source.julia" }]
}

Verified on a realistic transcript — continuation lines get Julia scopes, output does not:

"julia>" => punctuation.separator.prompt.julia.console
"function" => keyword.other.julia
"f" => entity.name.function.julia
"           " / "return" => keyword.control.julia      <- continuation, highlighted
"       end" => keyword.control.end.julia              <- highlighted
"f (generic function with 1 method)" => (plain)        <- output, correctly excluded

It also fixes begin/end keyword pairing on the prompt line, which needed cross-line state as well.

tokenize() now splits on newlines and threads the rule stack, so a match rule spanning lines can no longer pass a test. That surfaced 14 expectations asserting \n-containing token values, split into per-line tokens here. One, in the PyCall test, also needed meta.embedded.inline.python added — real line-based tokenization does apply the contentName to the string body, consistent with the neighbouring cxx and js tests.

The existing "should highlight multi-line expressions" test is now a genuine regression test: reverting just the grammar makes it fail. I also added a test over the transcript above.

Known limitation, unchanged from #285's assumptions: output indented by 7+ columns (stacktraces, some show methods) gets scoped as code. Requiring \s{7}\S doesn't help, since nested code is indented further. Happy to take suggestions if you'd prefer a different heuristic.

Commit 2 — prompt color on GitHub (separable, take it or leave it)

punctuation.separator.prompt.julia.console has no rule in primer/github-vscode-theme, so the prompt renders in the default foreground on GitHub. Renaming the capture to entity.name.tag.prompt.julia.console makes it green (#116329 light / #7ee787 dark, the .pl-ent class).

This is an admitted abuse of the scope name — entity.name.tag means "XML/HTML tag name" — and since this grammar also ships in the VS Code extension it changes the prompt color in every editor and theme, not only GitHub. Please evaluate this commit independently of the first; I'll drop it without complaint.

The cleaner fix is upstream of this grammar: Primer's theme styles no punctuation.separator.prompt scope at all, which is also why ShellSession $ prompts render plain on GitHub. Adding it there would fix every console grammar at once. I'm glad to open that instead if you'd rather not carry commit 2.


npm test: 131 passing.

PR JuliaEditorSupport#285 tried to support multi-line REPL input with a single `match`
rule containing `\n`:

    ^(julia>)(\s+.*(?:\n\s{6}\s+.*)*)

That can never fire. TextMate grammars are tokenized one line at a
time -- vscode-textmate (what VS Code and GitHub use) calls
`tokenizeLine()` per line, and the input string never contains a
newline. Only begin/end rules carry state across lines. The regex has
therefore been dead code since it was merged, and multi-line input on
GitHub is still unhighlighted.

Replace it with a begin/end rule that stays open while lines are
indented to the REPL's 7-column continuation alignment. As a bonus
this also fixes `begin`/`end` keyword pairing on the prompt line,
which needed cross-line state too.

The reason CI went green is that `tokenize()` passed the whole
multi-line source to `grammar.tokenizeLine()` as a single line, which
hands oniguruma a string that really does contain newlines. It is the
only consumer in the world that does this. Tokenize line by line
instead, threading the rule stack, so a `match` rule spanning lines
can no longer pass a test.

That change surfaced 14 expectations asserting token values that
contain `\n` -- impossible under real tokenization -- which are split
into per-line tokens here. One of them, in the PyCall test, also
needed `meta.embedded.inline.python` added: line-based tokenization
does apply the `contentName` to the string body, matching the
neighbouring cxx and js tests.

Also add a test over a realistic transcript asserting that
continuation lines get Julia scopes while REPL *output* does not.

Known limitation, unchanged from JuliaEditorSupport#285's assumptions: output indented
by 7 or more columns (stacktraces, some `show` methods) is scoped as
code. Requiring `\s{7}\S` does not help, since nested code is
indented further.
`punctuation.separator.prompt.julia.console` has no rule in
primer/github-vscode-theme, so on GitHub the prompt falls through to
the default foreground and is indistinguishable from surrounding
output. Resolving the theme through vscode-textmate:

    punctuation.separator.prompt.julia.console  ->  #1F2328 (default)
    entity.name.tag.prompt.julia.console        ->  #116329 (green)

`entity.name.tag` is one of the few scopes that theme paints green
(alongside `support.class.component`, `markup.inserted`,
`punctuation.definition.inserted` and `markup.quote`). Keeping the
`.prompt.julia.console` tail means theme prefix matching still finds
the `entity.name.tag` rule -- and its three components outrank the
`entity.name` -> orange rule -- while a Julia-specific theme can still
target the full scope. On github.com this lands in the `.pl-ent`
class.

This is an admitted abuse of the scope name: `entity.name.tag` means
"XML/HTML tag name", and because this grammar also ships in the VS
Code Julia extension the prompt changes color in every editor and
every theme, not just GitHub. Please take or leave this commit
independently of the first one.

The cleaner fix lives upstream of this grammar: Primer's theme styles
no `punctuation.separator.prompt` scope at all, which is also why
ShellSession `$` prompts render plain on GitHub. Adding that scope
there would fix every console grammar at once without distorting
anyone's semantics. Happy to drop this commit and pursue that instead.

`shell>` and `help?>` keep their `punctuation.separator` scopes here,
since this only concerns the julia> prompt.
@Pangoraw
Pangoraw requested a review from pfitzseb September 4, 2026 14:58
@pfitzseb
pfitzseb merged commit bc6cbc3 into JuliaEditorSupport:master Sep 7, 2026
1 check passed
@Pangoraw
Pangoraw deleted the fix/multiline-console-begin-end branch September 8, 2026 07:59
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