Skip to content

Commit 89102ab

Browse files
committed
Don't underline the entire schema for top-level errors/warnings
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 8e8daa3 commit 89102ab

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed

test/vscode/extension.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,97 @@ suite('Extension Test Suite', () => {
214214
'Sourcemeta Studio should still report metaschema errors');
215215
});
216216

217+
test('Should clamp root-level lint diagnostics to first token', async function() {
218+
this.timeout(15000);
219+
220+
const extension = vscode.extensions.getExtension('sourcemeta.sourcemeta-studio');
221+
if (extension && !extension.isActive) {
222+
await extension.activate();
223+
}
224+
225+
const fixtureDir = path.join(__dirname, '..', '..', '..', 'test', 'vscode', 'fixtures');
226+
const schemaPath = path.join(fixtureDir, 'root-only-lint-schema.json');
227+
228+
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(schemaPath));
229+
await vscode.window.showTextDocument(document);
230+
231+
await vscode.commands.executeCommand('sourcemeta-studio.openPanel');
232+
233+
await new Promise(resolve => setTimeout(resolve, 5000));
234+
235+
const diagnostics = vscode.languages.getDiagnostics(document.uri);
236+
237+
const lintDiagnostics = diagnostics.filter(diagnostic =>
238+
diagnostic.source === 'Sourcemeta Studio (Lint)');
239+
240+
assert.ok(lintDiagnostics.length > 0,
241+
'Root-level lint issues should still produce diagnostics');
242+
243+
for (const diagnostic of lintDiagnostics) {
244+
assert.strictEqual(diagnostic.range.start.line, 0,
245+
'Root-level diagnostic should start at line 0');
246+
assert.strictEqual(diagnostic.range.start.character, 0,
247+
'Root-level diagnostic should start at character 0');
248+
assert.strictEqual(diagnostic.range.end.line, 0,
249+
'Root-level diagnostic should not extend beyond line 0');
250+
assert.strictEqual(diagnostic.range.end.character, 0,
251+
'Root-level diagnostic should have zero-width range');
252+
}
253+
});
254+
255+
test('Should clamp root-level metaschema diagnostics to first token', async function() {
256+
this.timeout(15000);
257+
258+
const extension = vscode.extensions.getExtension('sourcemeta.sourcemeta-studio');
259+
if (extension && !extension.isActive) {
260+
await extension.activate();
261+
}
262+
263+
const fixtureDir = path.join(__dirname, '..', '..', '..', 'test', 'vscode', 'fixtures');
264+
const schemaPath = path.join(fixtureDir, 'invalid-metaschema.json');
265+
266+
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(schemaPath));
267+
await vscode.window.showTextDocument(document);
268+
269+
await vscode.commands.executeCommand('sourcemeta-studio.openPanel');
270+
271+
await new Promise(resolve => setTimeout(resolve, 5000));
272+
273+
const diagnostics = vscode.languages.getDiagnostics(document.uri);
274+
275+
const metaschemaDiagnostics = diagnostics.filter(diagnostic =>
276+
diagnostic.source === 'Sourcemeta Studio (Metaschema)');
277+
278+
assert.ok(metaschemaDiagnostics.length > 0,
279+
'Metaschema errors should produce diagnostics');
280+
281+
const rootDiagnostics = metaschemaDiagnostics.filter(diagnostic =>
282+
diagnostic.range.start.line === 0);
283+
284+
assert.ok(rootDiagnostics.length > 0,
285+
'Should have root-level metaschema diagnostics');
286+
287+
for (const diagnostic of rootDiagnostics) {
288+
assert.strictEqual(diagnostic.range.start.character, 0,
289+
'Root-level metaschema diagnostic should start at character 0');
290+
assert.strictEqual(diagnostic.range.end.line, 0,
291+
'Root-level metaschema diagnostic should not extend beyond line 0');
292+
assert.strictEqual(diagnostic.range.end.character, 0,
293+
'Root-level metaschema diagnostic should have zero-width range');
294+
}
295+
296+
const nonRootDiagnostics = metaschemaDiagnostics.filter(diagnostic =>
297+
diagnostic.range.start.line > 0);
298+
299+
assert.ok(nonRootDiagnostics.length > 0,
300+
'Should also have non-root metaschema diagnostics');
301+
302+
for (const diagnostic of nonRootDiagnostics) {
303+
assert.ok(diagnostic.range.end.character > diagnostic.range.start.character,
304+
'Non-root metaschema diagnostic should retain a non-zero-width range');
305+
}
306+
});
307+
217308
test('Should run linter even when metaschema validation fails', async function() {
218309
this.timeout(15000);
219310

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"type": "object"
4+
}

vscode/src/utils/fileUtils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,24 @@ export function arrayToPosition(arr: [number, number]): vscode.Position {
262262
/**
263263
* Convert error position array to VS Code range
264264
* Position array is 1-based and inclusive, VS Code is 0-based and end-exclusive
265+
*
266+
* When a diagnostic applies to the root of the document (position spanning
267+
* from line 1, column 1 across multiple lines), we collapse the range to
268+
* a zero-width range at (0,0). VS Code renders this by expanding the
269+
* squiggle to the first word, matching what ESLint and Pylint do. VS Code
270+
* does not support file-level diagnostics without a range:
271+
* https://github.com/microsoft/vscode/issues/238608
265272
*/
266273
export function errorPositionToRange(position: Position): vscode.Range {
267274
const [lineStart, columnStart, lineEnd, columnEnd] = position;
275+
276+
if (lineStart === 1 && columnStart === 1 && lineEnd > lineStart) {
277+
return new vscode.Range(
278+
new vscode.Position(0, 0),
279+
new vscode.Position(0, 0)
280+
);
281+
}
282+
268283
return new vscode.Range(
269284
new vscode.Position(lineStart - 1, columnStart - 1),
270285
new vscode.Position(lineEnd - 1, columnEnd)

0 commit comments

Comments
 (0)