feat(languages): Scala, Vue, Svelte, Markdown and Perl built-in scanners, with the SCSS dialect - #13
Merged
Merged
Conversation
…without Debug leaks, run summary, -q/-v, comment preview - core: ALL/as_str/aliases/Display on Language, Dialect, CommentKind, Policy, Layout, Severity; Display for Disposition (serde shapes and Keep reasons frozen, pinned by tests/names.rs) - cli: ValueEnum wrappers (values.rs) so --policy/--layout/--language/--dialect/--keep-kind/--remove-kind list and complete their values; descriptions for every subcommand and option; Policy/Output help headings; EXIT STATUS/FILES/EXAMPLES/SEE ALSO; `man` subcommand via clap_mangen; release_extras.py renders the man page from the binary - output: removable/kept labels use kebab-case kind names everywhere (human, scan, diagnostics, SARIF, GitHub, LSP hover, config explain); end-of-run summary on stderr for human format; `fixed <path>` lines for fix; -q/--quiet and -v/--verbose; directory-walk skips folded into the summary; one-line sanitized comment preview (--no-preview to disable) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…oken pipes, summary wording, man roff sections - `-` reads standard input for check/diff/scan (reported as <stdin>); fix refuses it and points at `ocomment strip` - fix --dry-run prints the patch and "Would remove N comments" without writing - --progress draws a live "scanning n/total files" counter on stderr (auto = TTY), cleared before output - closed stdout/stderr pipes end the run quietly with exit 0; the detection is scoped to the program's own output via an OutputPipeClosed marker so a dying `git hash-object` child during fix --staged still fails with exit 2 - no println!/eprintln! left on output paths; stdout is buffered and every write error is reported - summary: proper pluralization, explicit skips not double counted, "Nothing to check/fix/diff/scan", I/O error count, call to action keyed on count; -v only for human format; diff -q / scan -q keep their stdout product - preview also neutralizes bidi/format controls (U+200E/F, U+202A-E, U+2066-9, U+2028/9, U+061C, U+FEFF) and caps characters - man page renders real EXIT STATUS/FILES/EXAMPLES/SEE ALSO sections Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
… the new CLI surface - every config/git/atomic/plugin/files error says what to do next (version = 1 hint, `ocomment languages`, --staged needs a Git repository, stale index.lock, rollback journal, missing curl/gh/oras/cosign name their purpose and point at `ocomment doctor`) - --policy all without --force-protected reports the kept preamble comments - init: refuse to overwrite without --force, --stdout prints the template, note when a parent .ocomment.toml already applies - fix --dry-run lists skipped paths like fix does - source_guards test pins that every stdout write goes through output::wrote; staged broken-pipe test runs in <1s and resolves git from PATH - README quick start, CHANGELOG, CONTRIBUTING output conventions, man page == `ocomment man` consistency test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…bes external tools Incremental engine (found by the widened proptest and a new cross-edit soundness property): - rescan the whole remaining suffix and stop at the convergence checkpoint (scan_until_checkpoint) instead of scanning a truncated slice, so lookahead across the cut sees the same bytes as a full scan (dropped `unterminated-string` diagnostics) - one RestartRules predicate shared by emit time and reuse time: reused checkpoints are re-validated against the edited bytes (Python encoding declaration on line 2, C/C++ line splices, CRLF pairs), and the reused tail is rejected while the preamble is unsettled (stale Shebang/Encoding kinds) - deterministic regression tests for each case, a per-language checkpoint soundness property, a cross-edit property, and a token-mixing generator so preamble tokens are reachable CLI: - doctor probes git/curl/gh/oras/cosign with their purposes, prints cwd/root/config trace/TTY state, and sanitises probe output - -q help describes what it really suppresses; fix --dry-run reports skips on stderr so stdout stays a patch; pronoun agreement; init note only after a successful create; actionable plugin overwrite message Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…errides, --explain, fix --interactive - bare `ocomment`/`ocomment fix` operate on the current directory (not the repository root) with the normal hidden/size filters; the project root is still used for config discovery and glob matching - [[overrides]].paths, files.include and files.exclude are matched relative to the project root from any working directory (previously cwd-relative, so they silently stopped applying below the root) - `check --explain` / `scan --explain` print why each comment is kept or removed: rule (kind list, keep/remove regex index and pattern, directive name, license marker, preamble, policy) plus where the setting came from ([policy] in the project file, [[overrides]] #N with its paths, [languages.x], or the command line); refused for other commands and machine formats - core: explain_disposition/explain_disposition_with + DispositionExplanation (branch order mirrors disposition(); differential unchanged), directive_name/legal_marker refactor - `fix --interactive`/`-i`: per-comment y/n/a/d/q/x/? prompts with capped context hunks; only accepted edits go through the atomic transaction; refused without a terminal or with --staged/--dry-run/-q/machine formats - doctor/-v/config explain sanitise the paths they print; cross-edit proptest biases edit endpoints to document boundaries Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…ogue; never walk into .git - .pre-commit-hooks.yaml (ocomment-check / ocomment-fix, language: system) with a drift check that regenerates the files: regex from spec/languages.toml - action.yml: downloads the release archive for the runner, verifies SHA256SUMS and (when gh is present) the build attestation, runs check/diff with github or sarif output, optional code-scanning upload, exit-code/fail-on-findings contract; ci.yml action-smoke job on three OSes with the built binary; rulesets updated - SARIF: tool.driver.version/informationUri/rules with ruleIndex, %SRCROOT% uriBaseId, no ./ prefixes (also for GitHub annotations); tools/validate_schemas.py --sarif; release_manifests.py defaults to P4suta/OComment - safety: .git directories/files are excluded from every walk even when named explicitly; displayed paths and invalid regex patterns are sanitised; --explain is an allow-list (implicit, check, scan); interactive summary reports files scanned - docs/ci.md, README hooks/CI section, releasing notes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…yperlink/path hardening - .ocomment.toml (legal + doc kinds kept + tagged rationale comments via keep_regex), lefthook.yml (check --staged, fmt), CONTRIBUTING tag convention (NOTE/INVARIANT/PERF/TODO/FIXME/HACK; SAFETY reserved for unsafe); explanatory comments across rust/, ocaml/, tools/ and the Dockerfile retagged without changing meaning - ci.yml dogfood job: doctor, config explain, bare `ocomment` must exit 0, and a self-strip copy (fix --policy all --force-protected) must still build and test in Rust and OCaml - --staged honours [files] include/exclude with the same root-relative matching as the walk (the repository's own hook caught this) - scanner: Rust multi-line string literals no longer confuse the scanner (Rust + OCaml reference, pinned by a differential fixture); `# syntax=` and `# hadolint` are protected directives - Dockerfile (rust:1.88-alpine builder → scratch, non-root), release.yml publish-container (multi-arch, provenance, SBOM, cosign, attestation), ci.yml docker smoke incl. the prebuilt-binary context, docs/docker.md - hyperlink targets percent-encode every non-unreserved byte; displayed paths keep their spacing and neutralise control characters; regex errors are folded onto one sanitised line; skipped paths are deduplicated; GitHub annotations fold walked skips like the human renderer; SARIF validator agrees with the emitter and self-tests; action.yml Windows path/unzip fallbacks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…mit, directive spec enforcement - editors/vscode: LSP client extension (settings for binary path/extra args/languages, commands fix file/workspace/restart/show output, status bar count, config watchers, missing-binary guidance), esbuild bundle, unit + VS Code integration tests, Marketplace README; ci.yml vscode job, release.yml publish-vscode (vsce/ovsx, signed .vsix), CodeQL javascript-typescript, dependabot npm - lsp: shellscript and cuda-cpp language ids map to Shell and C++/CUDA - --staged honours files.hidden and files.max_size (size asked of the index before reading the blob) and reports too-large skips - tools/check_directives.py proves every protected name in spec/directives.toml is recognised by the binary; hadolint prefix narrowed to the pragma form - SARIF: a relative uri whose first segment reads as a drive letter is emitted as ./c:/…; -q never drops GitHub annotations of named or failing paths; invalid file globs are reported sanitised on one line Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…aries, staged pathspec and directive fixes - docs/: book.toml + SUMMARY with introduction, getting started, installation (every channel), commands (generated from --help), configuration, policies and layouts (generated examples), why-kept (generated directive table + --explain), languages (generated from spec), editors, CI, Docker, library, plugins, verify, FAQ, comparison, releasing; tools/gen_docs.py --check pins the generated pages; docs.yml builds on PRs and deploys to GitHub Pages from main - ocomment-core / ocomment-plugin-sdk: crate-level docs, every public item documented (missing_docs is a workspace lint), doctests on the public API, examples strip/external_spans/incremental/profile, missing re-exports added, docs.rs metadata; CI runs doctests and rustdoc with -D warnings - --staged: paths the caller names bypass hidden/size limits like the walk; unknown-language and binary blobs are counted as skips - hadolint/shellcheck directives match the keyword followed by whitespace only; check_directives.py gains negative controls; spec/directives.toml names the bare marker - configuration parse errors are sanitised; VS Code extension shares one watcher and serialises start/restart Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…nguages table from spec, real compact layout
- spec/fixtures/v1/{builtins,hazards}.json hold every differential case (96) with notes citing the lexical spec and recorded expect blocks; tools/differential.py carries no fixture bytes and offers --record; ocomment-core tests/spec_fixtures.rs runs the corpus without OCaml
- spec/languages.toml is embedded and drives `ocomment languages` (+ --format json); tests/spec_languages.rs pins spec ↔ detector ↔ dialect validation ↔ JSON schemas; omissions fixed
- Layout::Compact now drops lines that held only a removed comment and trims trailing blanks before an end-of-line comment (Rust + OCaml, spec cases, docs, help)
- --staged pathspecs are resolved by git (absolute, wildcard, `.` in a subdirectory); named staged skips are listed; configuration path sanitised; directive negative controls derive from the marker text; mdbook pinned; docs/library.md doctested in CI; README links relative until Pages is live
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…zz-found parity fixes - TOML: # comments, basic/literal/multi-line strings with the 3-of-5 quote rule and backslash continuation, quoted keys, Taplo #:schema / taplo: directives, reserved lock file names - Lua: -- and ---(LDoc) comments, level-matched long comments/strings ([==[ ]==]) via a reusable long-bracket helper, \z and other escapes, first-line #/#! handling, luacheck/selene/stylua/luacov and ---@diagnostic directives, lua/luajit shebangs - both mirrored in the OCaml reference with builtin + hazard spec cases and recorded expectations; detection, schemas, docs, pre-commit pattern, VS Code selector updated - Java: only /// and /** */ are documentation markers (Rust matched the OCaml reference); Python unterminated prefixed strings are anchored at the prefix - SARIF fixes report the compact edit region; strip/config refuse formats they cannot honour; five detector extensions published (cuh, hh, hxx, mlt, shtml); shebang table exposed and pinned against the spec; rustdoc lints the binary crate too Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…osed by fuzzing; YAML value-preservation invariant - YAML: # comments only at line start or after whitespace, quoted scalars, block scalars with owner-tracked content indentation (properties, explicit keys, nested sequences, indentation indicators, document root), chomping indicators, flow collections, %directives; yaml-language-server/yamllint/renovate/checkov/trivy/nosec/kics/@Schema directives. Invariant: removing comments never changes the parsed value under any layout — trail comments under a block scalar are removed line-wise (through the blank run a keep-chomped scalar would absorb), and a trail comment that separates the body from a kept deeper comment is kept with the new reason "structural in a YAML block scalar trail" (mirrored in OCaml and surfaced by --explain). tools/yaml_roundtrip.py (PyYAML) gates this in CI - PHP: HTML/PHP mode switching (<?php, <?=, ?> swallowing one newline, <?xml excluded), // and # ending at ?>, #[attributes], PHPDoc, single/double/backtick strings with brace interpolation, heredoc/nowdoc with indented closers, phpcs/phpstan/psalm directives, php shebang; inline HTML is opaque in v1 - parity: 12 fuzz-found Rust/OCaml divergences fixed on the side the language reference decides against (diagnostic wording, Rust lifetimes vs char literals, JSON5 single quotes, unterminated raw strings, vertical tab, Unicode whitespace before directives, C++ raw-string identifier boundary and d-char set, ECMAScript whitespace, OCaml literal recovery, shell blanks, heredoc delimiter operators, #! inside <script>); tools/fuzz_differential.py on-demand harness reads languages from the spec - UTF-8 BOM before #! and Lua's first-line #; bare #:schema / # shellcheck / # hadolint protected; language counts pinned to Language::ALL; 269 corpus cases with recorded expectations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
- Ruby: # comments, =begin/=end embedded documents, __END__, shebang and magic encoding comments, frozen_string_literal/rubocop/typed/warn_indent/shareable_constant_value/standard directives, "#{}" interpolation lexed as code (nested), single/double/backtick/symbol strings, ?c character literals, $-globals, %q/%Q/%w/%W/%i/%I/%s/%r/%x and bare % literals with nesting delimiters, regex literals vs division, here documents (<<, <<-, <<~, quoted, digit-led labels, several per line, opened inside interpolations and inside other bodies, consumed in physical-line order) — decided by a Begin/Argument/End state machine mirroring Ruby's lex_state; readings pinned against Ruby 3.3.12 Ripper.lex in fixture notes
- OCaml reference mirrors every reading; 316 corpus cases with recorded expectations; fuzz and two Ripper cross-checks report zero divergence
- proptest fragment pools are one shared list (ocomment_core::lexical_pool); corpus floors read from spec/fixtures/v1/floor.txt; explain_comment documented; transform_spans structural keep pinned
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…ess by construction
- Zig: //, /// and //! doc comments (//// plain), no block comments, multiline string lines (\\), quoted identifiers, zig fmt: off/on directives — readings taken from std.zig.Tokenizer on Zig 0.16
- R: # and #' (roxygen) comments, multi-line strings, r"(...)" raw strings with dashes and all bracket forms, backtick identifiers, nolint/styler/nocov directives, Rscript shebang — verified against R 4.3.3 getParseData
- Dart: nested block comments, /// and /** */ docs, single/triple-quoted and raw strings, $id and ${expr} interpolation lexed as code, symbol literals, // @Dart, ignore, dart format and coverage directives, dart shebang — verified against the Dart 3.13 scanner
- incremental engine: every lookahead records the furthest byte it consulted (Reach) and a safe checkpoint can no longer stand where an earlier decision read past it; char-literal detection stops at line terminators; tag searches are class-bounded; Ruby alias/undef %s symbol literals (Fname state); language alias table and proptest pools guarded; 23 built-in languages, 378 corpus cases with recorded expectations
- .gitignore: any rust/target* directory
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSWPMQ9PvtGi3jyBF7bJx
…nst their compilers
Wave 2-b completes the wave: Swift and C# join the scanner table alongside
Scala, each grounded in the toolchain's own lexer — SwiftSyntax 6.3.3,
Roslyn 10.0.400 and scalac 3.8.4 — with the Rust engine and the OCaml
reference agreeing byte for byte across the corpus.
Scala: a block comment nests, and a comment is documentation exactly when
the compiler's comment reader says its text starts with `/**`, so `/**/`
is documentation and `///` is not. An identifier directly before a quote
makes the string interpolate — `s"..."`, `raw"..."`, a custom interpolator
— while a keyword and a number leave it plain; inside an interpolated
string `$$` and `$"` are escapes and `${ ... }` is code. A triple-quoted
string closes on the first three quotes of a run and makes any further
quotes of the run part of its value, so `"""a""""` is the string `a"`; a
backquoted identifier may hold `//`; and an XML literal is opaque text, as
the parser reads it, with `{ ... }` code and `<!-- ... -->` comments inside.
`//> using` is kept because scala-cli reads a directive before the file.
Checkpoint soundness: a checkpoint may no longer stand directly before a
`<` that could open an XML literal, because the literal's trigger reads the
byte behind the `<` and a rescan from the checkpoint has no byte behind it
to read. The W2-a nit — bounding the C++ raw-string delimiter search by the
d-char class — and the fuzz-found Python fix — an unterminated prefixed
string inside an f-string replacement field is reported from its prefix —
are pinned in the corpus with the cases that caught them.
The language table, docs, editors, specs, completions and the OCaml
reference follow the new scanners; 25 built-in languages become 26.
…dialect
Vue and Svelte join the scanner table: a component's `<script>` and
`<style>` bodies are scanned as their own languages, the `lang` attribute
choosing which — `ts` and `tsx` select TypeScript, `jsx` JavaScript with
JSX, `scss` and the indented `sass` the SCSS dialect — and a `lang` this
scanner has no rules for makes the whole block opaque. The template is HTML
with code in it: `<!-- ... -->` is an HTML comment, `{{ ... }}` in Vue or
`{ ... }` in Svelte opens an expression whose comments are comments, and a
Vue `v-pre` element makes its whole content raw text. Ground truth is
`@vue/compiler-sfc` 3.5 and `svelte/compiler` 5.56.
SCSS and the indented Sass syntax join CSS as the `scss` dialect: `//`
opens a silent comment, `#{ ... }` opens an interpolation whose expression
is code, and an unquoted `url( ... )` is read the way dart-sass reads it —
a protocol-relative `url(//cdn/x.png)` is URL text, not a comment. `.scss`
and `.sass` select the dialect.
Checkpoint soundness: a checkpoint may no longer stand where an edit grew a
`<` ... `>` tag across it — the tag's quoted attributes would be read as
ordinary text by a rescan — and the tag lookaheads of the component
scanners report the reads they rewind, as the watermark requires. The Rust
engine and the OCaml reference agree byte for byte across the corpus,
fuzz-verified to zero divergences, and the language table, docs, editors,
specs, completions and the OCaml reference follow the new scanners;
26 built-in languages and 16 dialects become 28 and 17.
HTML comments are comments that `safe` keeps as DOM-observable, a fenced
code block is scanned as the language its info string names — a ```rust
fence as Rust, a `{r}` chunk of an R Markdown document as R — and an
inline code span or an indented code block is opaque, so a `//` or a `/*`
inside one is code text, not a comment. A fence closes only at a run of
its own marker at least as long as its own. Ground truth is CommonMark,
read through the reference `commonmark` implementation.
`.md`, `.markdown` and `.Rmd` select it, and the fenced children of the
component scanners now report the reads they rewind, as the watermark
requires. The Rust engine and the OCaml reference agree byte for byte
across the corpus, fuzz-verified to zero divergences; the language table,
docs, editors, specs, completions and the OCaml reference follow, and
28 built-in languages become 29.
…y stop A `#` runs to the end of its line, a POD block is opaque, and every quote word — the single and double quotes and backticks, the `q`, `qq`, `qw` and `qx` forms, the `m`, `s`, `tr` and `y` operators with delimiters of their own, and the here-documents — hides a `#` written inside it. A `/` directly after a closing parenthesis, bracket or brace is reported as lexically ambiguous: perl reads `f() /a#b/` as a regular expression and `(2) / 2` as a division, and only the parse context tells which, so the file is called invalid and nothing is edited. Ground truth is perl 5.38, read with `perl -c` over the corpus cases. Checkpoint soundness: a checkpoint may no longer stand where an edit grew a `<` ... `>` tag across it, now asked by reading the tag forward from the `<` its walk meets, quotes included; and the incremental engine re-asks the converged tail's checkpoints against the edited document, exactly as it re-asks a candidate restart. The Rust engine and the OCaml reference agree byte for byte across the corpus, fuzz-verified to zero divergences; the language table, docs, editors, specs, completions and the OCaml reference follow, and 29 built-in languages become 30.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
Owner
Author
|
Reopening to re-trigger the CI workflows that failed to start. |
The repository allows only GitHub-owned actions, the SHA-pinned tools on its allowlist (rust-toolchain, setup-ocaml), and local actions, so the docker job's setup-buildx/build-push steps and the docs workflow's install-action step could not resolve at workflow start, failing both runs before a single job ran. - docker: build with plain docker, and the hosted runner's default buildx builder for the release-path build - docs: fetch the pinned mdBook release archive directly instead of installing it through an action
…ound The CI run for PR #13 surfaced three failures: 1. incremental: an edit that closes a tag the earlier scan had read as text left the engine offering checkpoints the full scan refuses. The tag-boundary rule only walked back to the nearest '<', but a tag's attributes can hold '<' of their own (a quote pair in reads the second '<' as text), so a checkpoint could stand inside the outer tag once the edit supplied its '>'. The rule now reads every '<' before the offset and refuses when any of them is still open there; the engine re-asks the same rule when it picks a restart point, so the edited document's checkpoint list agrees with a fresh full scan. Regression test: an_edit_that_closes_a_tag_withdraws_the_checkpoints_inside_it. 2. dogfood: 22 untagged explanatory comments in the docs and the policies sample, tagged per the repository convention (NOTE). 3. vscode: the manifest unit test still pinned the language count at 24; the extension contributes 35 now.
The dogfood job strips a copy of rust/ and builds it, but the crate's spec-fixture tests read the corpus from ../../spec, which the copy never received. Copy spec/ alongside, and exclude the fixtures from the strip: they are the tests' input, not source to rewrite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes Wave 2-b and Wave 3 of the language roadmap: five new built-in
scanners, each grounded in the toolchain's own lexer and fuzz-verified to
zero Rust/OCaml divergence, taking the language table from 25 to 30.
/**documentation,identifier-interpolated strings with
$$/$"escapes, the triple-quoterun rule, backquoted identifiers, and parser-following opaque XML literals.
@vue/compiler-sfc3.5,svelte/compiler5.56):templates are HTML with code in the mustaches/braces,
v-preelements areraw text, and the
<script>/<style>bodies are scanned as their ownlanguages via the
langattribute.//silent comments,#{...}interpolation, and unquoted
url(//...)read as URL text..scssand.sassselect it.their named languages (
```rust,{r}chunks), inline andindented code opaque.
opaque, and a
/the parse context alone settles reported aslexical-ambiguitywith the file marked invalid.Checkpoint soundness across the wave: the Scala XML trigger's backward read,
the tag lookahead rewinds of the component scanners, and the incremental
engine's converged-tail reuse are all now covered by the watermark and the
restart rules. Fuzz-found Rust/OCaml divergences (Python f-string prefix
spans, SFC/SCSS/Markdown/Perl edge cases) are pinned in the corpus.