Skip to content

Commit 11cf822

Browse files
committed
fix#140: ordering of linting errors by source location
1 parent faec85d commit 11cf822

File tree

4 files changed

+108
-12
lines changed

4 files changed

+108
-12
lines changed

test/vscode/extension.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ suite('Extension Test Suite', () => {
198198
const vscodeJsonDiagnostics = diagnostics.filter(diagnostic =>
199199
diagnostic.source === 'json' || diagnostic.source === 'JSON');
200200

201-
assert.strictEqual(vscodeJsonDiagnostics.length, 0,
201+
assert.strictEqual(vscodeJsonDiagnostics.length, 0,
202202
'VS Code built-in JSON validation should be disabled');
203203

204204
const sourcemetaDiagnostics = diagnostics.filter(diagnostic =>
@@ -207,4 +207,51 @@ suite('Extension Test Suite', () => {
207207
assert.ok(sourcemetaDiagnostics.length > 0,
208208
'Sourcemeta Studio should still report metaschema errors');
209209
});
210+
211+
test("Lint diagnostics should be ordered by line number", async function () {
212+
this.timeout(15000);
213+
214+
const extension = vscode.extensions.getExtension(
215+
"sourcemeta.sourcemeta-studio"
216+
);
217+
if (extension && !extension.isActive) {
218+
await extension.activate();
219+
}
220+
const fixtureDir = path.join(
221+
__dirname,
222+
"..",
223+
"..",
224+
"..",
225+
"test",
226+
"vscode",
227+
"fixtures"
228+
);
229+
const schemaPath = path.join(fixtureDir, "lint-order.schema.json");
230+
231+
const document = await vscode.workspace.openTextDocument(
232+
vscode.Uri.file(schemaPath)
233+
);
234+
await vscode.window.showTextDocument(document);
235+
236+
await vscode.commands.executeCommand("sourcemeta-studio.openPanel");
237+
238+
// wait for lint + diagnostics
239+
await new Promise((resolve) => setTimeout(resolve, 5000));
240+
241+
const diagnostics = vscode.languages
242+
.getDiagnostics(document.uri)
243+
.filter((d) => d.source === "Sourcemeta Studio (Lint)");
244+
245+
assert.ok(diagnostics.length > 1, "Expected multiple lint diagnostics");
246+
247+
const lineNumbers = diagnostics.map((d) => d.range.start.line);
248+
249+
const sorted = [...lineNumbers].sort((a, b) => a - b);
250+
251+
assert.deepStrictEqual(
252+
lineNumbers,
253+
sorted,
254+
"Lint diagnostics should be sorted by line number"
255+
);
256+
});
210257
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
4+
"type": "object",
5+
6+
"properties": {
7+
"role": {
8+
"enum": ["admin", "user"],
9+
"type": "string"
10+
},
11+
12+
"preferences": {
13+
"type": "object",
14+
"properties": {
15+
"theme": {
16+
"enum": ["dark", "light"],
17+
"type": "string"
18+
}
19+
}
20+
}
21+
},
22+
23+
"required": ["id", "role"]
24+
}

test/vscode/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.

vscode/src/extension.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { PanelManager } from './panel/PanelManager';
55
import { CommandExecutor } from './commands/CommandExecutor';
66
import { DiagnosticManager } from './diagnostics/DiagnosticManager';
77
import { getFileInfo, parseLintResult, parseMetaschemaResult, errorPositionToRange, parseCliError, hasJsonParseErrors } from './utils/fileUtils';
8-
import { PanelState, WebviewToExtensionMessage } from '../../protocol/types';
8+
import { LintError, PanelState, WebviewToExtensionMessage } from '../../protocol/types';
99
import { DiagnosticType } from './types';
1010

1111
let panelManager: PanelManager;
@@ -35,8 +35,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
3535
// Only disable validation if a workspace is open to avoid changing global user settings
3636
if (vscode.workspace.workspaceFolders) {
3737
await vscode.workspace.getConfiguration('json').update(
38-
'validate.enable',
39-
false,
38+
'validate.enable',
39+
false,
4040
vscode.ConfigurationTarget.Workspace
4141
);
4242
}
@@ -105,15 +105,15 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
105105
}
106106
vscode.window.showTextDocument(lastActiveTextEditor.document, showOptions).then((editor) => {
107107
editor.selection = new vscode.Selection(range.start, range.end);
108-
108+
109109
editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
110110
});
111111
} else if (message.command === 'openExternal' && message.url) {
112112
vscode.env.openExternal(vscode.Uri.parse(message.url));
113113
} else if (message.command === 'formatSchema' && lastActiveTextEditor) {
114114
const filePath = lastActiveTextEditor.document.uri.fsPath;
115115
const fileInfo = getFileInfo(filePath);
116-
116+
117117
if (!fileInfo || !panelManager.exists() || !currentPanelState) {
118118
return;
119119
}
@@ -138,14 +138,14 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
138138
if (lastActiveTextEditor) {
139139
await vscode.window.showTextDocument(lastActiveTextEditor.document, lastActiveTextEditor.viewColumn);
140140
}
141-
141+
142142
// Wait for Huge schemas to reload after formatting
143143
await new Promise(resolve => setTimeout(resolve, 300));
144144

145145
await updatePanelContent();
146146
}).catch((error) => {
147147
let errorMessage = error.message;
148-
148+
149149
// Try to parse JSON error from CLI
150150
const cliError = parseCliError(error.message);
151151
if (cliError) {
@@ -157,7 +157,7 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
157157
}
158158
}
159159
}
160-
160+
161161
vscode.window.showErrorMessage(`Format failed: ${errorMessage}`);
162162
if (currentPanelState) {
163163
const updatedState = {
@@ -217,7 +217,7 @@ function handleActiveEditorChange(editor: vscode.TextEditor | undefined): void {
217217
* Handle document save events
218218
*/
219219
function handleDocumentSave(document: vscode.TextDocument): void {
220-
if (panelManager.exists() && lastActiveTextEditor &&
220+
if (panelManager.exists() && lastActiveTextEditor &&
221221
document.uri.fsPath === lastActiveTextEditor.document.uri.fsPath) {
222222
const fileInfo = getFileInfo(document.uri.fsPath);
223223
// Only refresh if it's a JSON/YAML file
@@ -227,6 +227,26 @@ function handleDocumentSave(document: vscode.TextDocument): void {
227227
}
228228
}
229229

230+
/**
231+
* Sort the Linting Errors by line location
232+
*/
233+
function sortLintErrorsByLocation(errors: LintError[]): LintError[] {
234+
return [...errors].sort((a, b) => {
235+
if (!a.position && !b.position) return 0;
236+
if (!a.position) return 1;
237+
if (!b.position) return -1;
238+
239+
const [aLine, aColumn] = a.position;
240+
const [bLine, bColumn] = b.position;
241+
242+
if (aLine !== bLine) {
243+
return aLine - bLine;
244+
}
245+
246+
return aColumn - bColumn;
247+
});
248+
}
249+
230250
/**
231251
* Update the panel content with current file analysis
232252
*/
@@ -313,6 +333,11 @@ async function updatePanelContent(): Promise<void> {
313333

314334
const lintResult = parseLintResult(lintOutput);
315335

336+
if (lintResult.errors && lintResult.errors.length > 0) {
337+
// TODO: Consider moving lint diagnostic ordering to the jsonschema CLI
338+
lintResult.errors = sortLintErrorsByLocation(lintResult.errors);
339+
}
340+
316341
const parseErrors = hasJsonParseErrors(lintResult, metaschemaResult);
317342

318343
const finalState: PanelState = {

0 commit comments

Comments
 (0)