Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 47 additions & 1 deletion test/vscode/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ suite('Extension Test Suite', () => {
const vscodeJsonDiagnostics = diagnostics.filter(diagnostic =>
diagnostic.source === 'json' || diagnostic.source === 'JSON');

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

const sourcemetaDiagnostics = diagnostics.filter(diagnostic =>
Expand All @@ -207,4 +207,50 @@ suite('Extension Test Suite', () => {
assert.ok(sourcemetaDiagnostics.length > 0,
'Sourcemeta Studio should still report metaschema errors');
});

test("Lint diagnostics should be ordered by line number", async function () {
this.timeout(15000);

const extension = vscode.extensions.getExtension(
"sourcemeta.sourcemeta-studio"
);
if (extension && !extension.isActive) {
await extension.activate();
}
const fixtureDir = path.join(
__dirname,
"..",
"..",
"..",
"test",
"vscode",
"fixtures"
);
const schemaPath = path.join(fixtureDir, "lint-order.schema.json");

const document = await vscode.workspace.openTextDocument(
vscode.Uri.file(schemaPath)
);
await vscode.window.showTextDocument(document);

await vscode.commands.executeCommand("sourcemeta-studio.openPanel");

await new Promise((resolve) => setTimeout(resolve, 5000));

const diagnostics = vscode.languages
.getDiagnostics(document.uri)
.filter((d) => d.source === "Sourcemeta Studio (Lint)");

assert.ok(diagnostics.length > 1, "Expected multiple lint diagnostics");

const lineNumbers = diagnostics.map((d) => d.range.start.line);

const sorted = [...lineNumbers].sort((a, b) => a - b);

assert.deepStrictEqual(
lineNumbers,
sorted,
"Lint diagnostics should be sorted by line number"
);
});
});
24 changes: 24 additions & 0 deletions test/vscode/fixtures/lint-order.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",

"type": "object",

"properties": {
"role": {
"enum": ["admin", "user"],
"type": "string"
},

"preferences": {
"type": "object",
"properties": {
"theme": {
"enum": ["dark", "light"],
"type": "string"
}
}
}
},

"required": ["id", "role"]
}
4 changes: 2 additions & 2 deletions test/vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 34 additions & 9 deletions vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PanelManager } from './panel/PanelManager';
import { CommandExecutor } from './commands/CommandExecutor';
import { DiagnosticManager } from './diagnostics/DiagnosticManager';
import { getFileInfo, parseLintResult, parseMetaschemaResult, errorPositionToRange, parseCliError, hasJsonParseErrors } from './utils/fileUtils';
import { PanelState, WebviewToExtensionMessage } from '../../protocol/types';
import { LintError, PanelState, WebviewToExtensionMessage } from '../../protocol/types';
import { DiagnosticType } from './types';

let panelManager: PanelManager;
Expand Down Expand Up @@ -35,8 +35,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
// Only disable validation if a workspace is open to avoid changing global user settings
if (vscode.workspace.workspaceFolders) {
await vscode.workspace.getConfiguration('json').update(
'validate.enable',
false,
'validate.enable',
false,
vscode.ConfigurationTarget.Workspace
);
}
Expand Down Expand Up @@ -105,15 +105,15 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
}
vscode.window.showTextDocument(lastActiveTextEditor.document, showOptions).then((editor) => {
editor.selection = new vscode.Selection(range.start, range.end);

editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
});
} else if (message.command === 'openExternal' && message.url) {
vscode.env.openExternal(vscode.Uri.parse(message.url));
} else if (message.command === 'formatSchema' && lastActiveTextEditor) {
const filePath = lastActiveTextEditor.document.uri.fsPath;
const fileInfo = getFileInfo(filePath);

if (!fileInfo || !panelManager.exists() || !currentPanelState) {
return;
}
Expand All @@ -138,14 +138,14 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
if (lastActiveTextEditor) {
await vscode.window.showTextDocument(lastActiveTextEditor.document, lastActiveTextEditor.viewColumn);
}

// Wait for Huge schemas to reload after formatting
await new Promise(resolve => setTimeout(resolve, 300));

await updatePanelContent();
}).catch((error) => {
let errorMessage = error.message;

// Try to parse JSON error from CLI
const cliError = parseCliError(error.message);
if (cliError) {
Expand All @@ -157,7 +157,7 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
}
}
}

vscode.window.showErrorMessage(`Format failed: ${errorMessage}`);
if (currentPanelState) {
const updatedState = {
Expand Down Expand Up @@ -217,7 +217,7 @@ function handleActiveEditorChange(editor: vscode.TextEditor | undefined): void {
* Handle document save events
*/
function handleDocumentSave(document: vscode.TextDocument): void {
if (panelManager.exists() && lastActiveTextEditor &&
if (panelManager.exists() && lastActiveTextEditor &&
document.uri.fsPath === lastActiveTextEditor.document.uri.fsPath) {
const fileInfo = getFileInfo(document.uri.fsPath);
// Only refresh if it's a JSON/YAML file
Expand All @@ -227,6 +227,26 @@ function handleDocumentSave(document: vscode.TextDocument): void {
}
}

/**
* Sort the Linting Errors by line location
*/
function sortLintErrorsByLocation(errors: LintError[]): LintError[] {
return [...errors].sort((a, b) => {
if (!a.position && !b.position) return 0;
if (!a.position) return 1;
if (!b.position) return -1;

const [aLine, aColumn] = a.position;
const [bLine, bColumn] = b.position;

if (aLine !== bLine) {
return aLine - bLine;
}

return aColumn - bColumn;
});
}

/**
* Update the panel content with current file analysis
*/
Expand Down Expand Up @@ -313,6 +333,11 @@ async function updatePanelContent(): Promise<void> {

const lintResult = parseLintResult(lintOutput);

if (lintResult.errors && lintResult.errors.length > 0) {
// TODO: Consider moving lint diagnostic ordering to the jsonschema CLI
lintResult.errors = sortLintErrorsByLocation(lintResult.errors);
}

const parseErrors = hasJsonParseErrors(lintResult, metaschemaResult);

const finalState: PanelState = {
Expand Down