Skip to content

Commit 0afa277

Browse files
committed
feat: clickable diagnostic codes, .explcheckrc awareness, remote support (v0.2.1)
- Diagnostic codes link to the explcheck rule docs (phase-specific markdown, chosen by the rule's leading digit — explcheck has no per-rule anchors) - Run explcheck from the workspace root so project-level .explcheckrc applies to files in subdirectories (explcheck searches cwd only, no upward walk) - extensionKind: [workspace] — diagnostics need the local explcheck binary, so the extension must run workspace-side under Remote-SSH / Dev Containers - Settings use markdownDescription; README gains a diagnostics screenshot (rendered from real explcheck output) and an as-you-type / .explcheckrc note
1 parent f5c3403 commit 0afa277

9 files changed

Lines changed: 208 additions & 13 deletions

File tree

.vscodeignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ node_modules/**
66
src/**
77
tests/**
88
images/before-after.*
9+
images/diagnostics.*
910
tsconfig.json
1011
package-lock.json
1112
**/*.ts

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## [0.2.1] - 2026-07-06
4+
5+
### Added
6+
7+
- Diagnostic codes are now clickable and link to the relevant `explcheck`
8+
documentation (the phase-specific rule reference).
9+
- `.explcheckrc` project config is respected: checks run from the workspace
10+
root, so a root-level config applies to files in subdirectories.
11+
12+
### Changed
13+
14+
- Declared `extensionKind: ["workspace"]` so diagnostics work correctly in
15+
Remote-SSH / Dev Containers (explcheck runs on the workspace side).
16+
- Settings use `markdownDescription` for richer links and formatting.
17+
- README gains a diagnostics screenshot and an as-you-type / `.explcheckrc` note.
18+
319
## [0.2.0] - 2026-07-06
420

521
### Added

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ looks native in Dark+, Light+, Solarized, and everything else.
2929
If [`explcheck`](https://ctan.org/pkg/expltools) is available, the extension runs it
3030
on your expl3 files and shows the results as squiggles, mapped by severity:
3131

32+
![explcheck diagnostics](https://raw.githubusercontent.com/CTeX-org/expl3-vscode/main/images/diagnostics.png)
33+
3234
| explcheck prefix | meaning | VS Code severity |
3335
|------------------|---------|------------------|
3436
| `e`, `t` | errors / type errors | Error |
@@ -58,6 +60,10 @@ keeps working regardless.
5860

5961
Command **expl3: Run explcheck on the active file** triggers a check on demand.
6062

63+
Project-level [`.explcheckrc`](https://ctan.org/pkg/expltools) config is respected:
64+
checks run from the workspace root, so a `.explcheckrc` at the project root applies
65+
even to files in subdirectories.
66+
6167
#### Live (as-you-type) checking
6268

6369
By default checks run on save (`onSave`). To lint **as you type**, set:

images/diagnostics.png

102 KB
Loading

images/diagnostics.svg

Lines changed: 4 additions & 0 deletions
Loading

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "expl3-vscode",
33
"displayName": "expl3 (LaTeX3) Language Support",
44
"description": "Syntax highlighting and explcheck diagnostics for expl3 (LaTeX3) code.",
5-
"version": "0.2.0",
5+
"version": "0.2.1",
66
"publisher": "CTeX-org",
77
"license": "MIT",
88
"icon": "images/icon.png",
@@ -36,6 +36,9 @@
3636
},
3737
"homepage": "https://github.com/CTeX-org/expl3-vscode#readme",
3838
"main": "./dist/extension.js",
39+
"extensionKind": [
40+
"workspace"
41+
],
3942
"activationEvents": [
4043
"onLanguage:latex",
4144
"onLanguage:latex-expl3",
@@ -78,12 +81,12 @@
7881
"expl3.check.enable": {
7982
"type": "boolean",
8083
"default": true,
81-
"description": "Enable explcheck static analysis diagnostics."
84+
"markdownDescription": "Enable [`explcheck`](https://ctan.org/pkg/expltools) static-analysis diagnostics."
8285
},
8386
"expl3.check.path": {
8487
"type": "string",
8588
"default": "explcheck",
86-
"description": "Path to the explcheck executable. Defaults to `explcheck` on PATH (provided by the TeX Live `expltools` package)."
89+
"markdownDescription": "Path to the `explcheck` executable. Defaults to `explcheck` on `PATH` (provided by the TeX Live [`expltools`](https://ctan.org/pkg/expltools) package)."
8790
},
8891
"expl3.check.run": {
8992
"type": "string",
@@ -98,31 +101,31 @@
98101
"Only run explcheck via the 'expl3: Run explcheck' command."
99102
],
100103
"default": "onSave",
101-
"description": "When to run explcheck diagnostics. Use 'onType' for live as-you-type checking."
104+
"markdownDescription": "When to run explcheck diagnostics. Use `onType` for live as-you-type checking."
102105
},
103106
"expl3.check.debounce": {
104107
"type": "number",
105108
"default": 400,
106109
"minimum": 100,
107-
"description": "Debounce delay in milliseconds for `onType` checking."
110+
"markdownDescription": "Debounce delay in milliseconds for `onType` checking."
108111
},
109112
"expl3.check.maxLineLength": {
110113
"type": "number",
111114
"default": 0,
112-
"description": "Max line length before the S103 (line too long) warning. 0 uses explcheck's default (80)."
115+
"markdownDescription": "Max line length before the `s103` (line too long) warning. `0` uses explcheck's default (80)."
113116
},
114117
"expl3.check.ignoredIssues": {
115118
"type": "array",
116119
"items": {
117120
"type": "string"
118121
},
119122
"default": [],
120-
"description": "Issue identifiers or prefixes to suppress (passed to explcheck --ignored-issues), e.g. [\"s103\", \"w2\"]."
123+
"markdownDescription": "Issue identifiers or prefixes to suppress (passed to `explcheck --ignored-issues`), e.g. `[\"s103\", \"w2\"]`."
121124
},
122125
"expl3.check.makeAtLetter": {
123126
"type": "boolean",
124127
"default": false,
125-
"description": "Tokenize @ as a letter (catcode 11), like in LaTeX .sty files (explcheck --make-at-letter)."
128+
"markdownDescription": "Tokenize `@` as a letter (catcode 11), like in LaTeX `.sty` files (`explcheck --make-at-letter`)."
126129
}
127130
}
128131
}

src/extension.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,27 @@ function severityOf(t: string): vscode.DiagnosticSeverity {
3535
}
3636
}
3737

38+
// explcheck has no per-rule HTML page (docs are PDF + pandoc markdown, and
39+
// GitHub does not honour the pandoc {label=...} anchors). The best stable link
40+
// is the phase-specific markdown source, chosen by the rule's leading digit.
41+
// Users land on the right document and can search for the code. Falls back to
42+
// the intro for anything unrecognised.
43+
const DOC_BASE =
44+
"https://github.com/witiko/expltools/blob/main/explcheck/doc/";
45+
const PHASE_DOC: Record<string, string> = {
46+
"1": "warnings-and-errors-01-preprocessing.md",
47+
"2": "warnings-and-errors-02-lexical-analysis.md",
48+
"3": "warnings-and-errors-03-syntactic-analysis.md",
49+
"4": "warnings-and-errors-04-semantic-analysis.md",
50+
"5": "warnings-and-errors-05-flow-analysis.md",
51+
};
52+
function ruleDocUri(code: string): vscode.Uri {
53+
const digit = code.replace(/^[a-z]/i, "").charAt(0);
54+
const file = PHASE_DOC[digit] ?? "warnings-and-errors-00-introduction.md";
55+
return vscode.Uri.parse(DOC_BASE + file);
56+
}
57+
58+
3859
function config() {
3960
return vscode.workspace.getConfiguration("expl3");
4061
}
@@ -47,6 +68,15 @@ function explcheckPath(): string {
4768
return config().get<string>("check.path", "explcheck") || "explcheck";
4869
}
4970

71+
// explcheck looks for `.explcheckrc` in its cwd only (no upward search).
72+
// Run it from the workspace root so a project-level config is picked up
73+
// regardless of where the checked file sits. Falls back to the file's dir.
74+
function execCwd(docUri: vscode.Uri): string {
75+
const folder = vscode.workspace.getWorkspaceFolder(docUri);
76+
if (folder) return folder.uri.fsPath;
77+
return path.dirname(docUri.fsPath);
78+
}
79+
5080
// Should we lint this document? Skip .dtx by extension even if the language id
5181
// happens to resolve to latex, since explcheck rejects .dtx outright.
5282
function isLintable(doc: vscode.TextDocument): boolean {
@@ -83,7 +113,7 @@ function runExplcheck(
83113
cp.execFile(
84114
exe,
85115
buildArgs(target),
86-
{ maxBuffer: 8 * 1024 * 1024 },
116+
{ maxBuffer: 8 * 1024 * 1024, cwd: execCwd(docUri) },
87117
(err, stdout, stderr) => {
88118
// ENOENT => explcheck not installed / not on PATH.
89119
if (err && (err as NodeJS.ErrnoException).code === "ENOENT") {
@@ -120,7 +150,8 @@ function parseOutput(stdout: string, target: string): vscode.Diagnostic[] {
120150
severityOf(m.groups.t),
121151
);
122152
diag.source = "explcheck";
123-
diag.code = code;
153+
// Clickable code that links to the rule's documentation.
154+
diag.code = { value: code, target: ruleDocUri(code) };
124155
diags.push(diag);
125156
}
126157
return diags;
@@ -161,7 +192,7 @@ function runExplcheckTemp(
161192
cp.execFile(
162193
exe,
163194
buildArgs(tmp),
164-
{ maxBuffer: 8 * 1024 * 1024 },
195+
{ maxBuffer: 8 * 1024 * 1024, cwd: execCwd(docUri) },
165196
(err, stdout) => {
166197
fs.unlink(tmp, () => {});
167198
if (err && (err as NodeJS.ErrnoException).code === "ENOENT") {

0 commit comments

Comments
 (0)