Skip to content

Commit 4c35889

Browse files
authored
Do not block lint / format on metaschema failures (#152)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 0826c2f commit 4c35889

File tree

8 files changed

+53
-99
lines changed

8 files changed

+53
-99
lines changed

protocol/types.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export interface PanelState {
4545
isLoading?: boolean;
4646
formatLoading?: boolean;
4747
hasParseErrors?: boolean;
48-
blockedByMetaschema?: boolean;
4948
noFileSelected?: boolean;
5049
}
5150

test/vscode/extension.test.ts

Lines changed: 34 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,37 @@ suite('Extension Test Suite', () => {
207207
assert.ok(sourcemetaDiagnostics.length > 0,
208208
'Sourcemeta Studio should still report metaschema errors');
209209
});
210+
211+
test('Should run linter even when metaschema validation fails', async function() {
212+
this.timeout(15000);
213+
214+
const extension = vscode.extensions.getExtension('sourcemeta.sourcemeta-studio');
215+
if (extension && !extension.isActive) {
216+
await extension.activate();
217+
}
218+
219+
const fixtureDir = path.join(__dirname, '..', '..', '..', 'test', 'vscode', 'fixtures');
220+
const schemaPath = path.join(fixtureDir, 'invalid-metaschema.json');
221+
222+
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(schemaPath));
223+
await vscode.window.showTextDocument(document);
224+
225+
await vscode.commands.executeCommand('sourcemeta-studio.openPanel');
226+
227+
await new Promise(resolve => setTimeout(resolve, 5000));
228+
229+
const diagnostics = vscode.languages.getDiagnostics(document.uri);
230+
231+
const metaschemaDiagnostics = diagnostics.filter(diagnostic =>
232+
diagnostic.source === 'Sourcemeta Studio (Metaschema)');
233+
234+
const lintDiagnostics = diagnostics.filter(diagnostic =>
235+
diagnostic.source === 'Sourcemeta Studio (Lint)');
236+
237+
assert.ok(metaschemaDiagnostics.length > 0,
238+
'Should have metaschema errors for invalid schema');
239+
240+
assert.ok(lintDiagnostics.length > 0,
241+
'Should still have lint diagnostics even when metaschema fails');
242+
});
210243
});

vscode/src/extension.ts

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,6 @@ function handleWebviewMessage(message: WebviewToExtensionMessage): void {
123123
return;
124124
}
125125

126-
if (currentPanelState.blockedByMetaschema) {
127-
vscode.window.showErrorMessage('Cannot format schema: Metaschema validation failed. Fix metaschema errors first.');
128-
return;
129-
}
130-
131126
// Send format loading state only, preserve existing lint/metaschema state
132127
panelManager.updateContent({
133128
...currentPanelState,
@@ -273,44 +268,17 @@ async function updatePanelContent(): Promise<void> {
273268
diagnosticManager.clearDiagnostics(lastActiveTextEditor.document.uri);
274269
}
275270

276-
// Run metaschema first, if metaschema reports errors, block other commands
277271
try {
278272
const version = await commandExecutor.getVersion();
279273
cachedCliVersion = version;
280274

281-
const metaschemaRawResult = await commandExecutor.metaschema(fileInfo.absolutePath);
282-
const metaschemaResult = parseMetaschemaResult(metaschemaRawResult.output, metaschemaRawResult.exitCode);
283-
284-
if (metaschemaResult.errors && metaschemaResult.errors.length > 0) {
285-
const blockedState: PanelState = {
286-
fileInfo,
287-
cliVersion: cachedCliVersion,
288-
extensionVersion,
289-
lintResult: { raw: '', health: null, errors: [] },
290-
formatResult: { output: '', exitCode: null },
291-
metaschemaResult,
292-
isLoading: false,
293-
hasParseErrors: hasJsonParseErrors({ raw: '', health: null }, metaschemaResult),
294-
blockedByMetaschema: true
295-
};
296-
currentPanelState = blockedState;
297-
panelManager.updateContent(blockedState);
298-
299-
if (lastActiveTextEditor) {
300-
diagnosticManager.updateMetaschemaDiagnostics(
301-
lastActiveTextEditor.document.uri,
302-
metaschemaResult.errors
303-
);
304-
}
305-
306-
return;
307-
}
308-
309-
const [lintOutput, formatResult] = await Promise.all([
275+
const [metaschemaRawResult, lintOutput, formatResult] = await Promise.all([
276+
commandExecutor.metaschema(fileInfo.absolutePath),
310277
commandExecutor.lint(fileInfo.absolutePath),
311278
commandExecutor.formatCheck(fileInfo.absolutePath)
312279
]);
313280

281+
const metaschemaResult = parseMetaschemaResult(metaschemaRawResult.output, metaschemaRawResult.exitCode);
314282
const lintResult = parseLintResult(lintOutput);
315283

316284
const parseErrors = hasJsonParseErrors(lintResult, metaschemaResult);
@@ -323,8 +291,7 @@ async function updatePanelContent(): Promise<void> {
323291
formatResult,
324292
metaschemaResult,
325293
isLoading: false,
326-
hasParseErrors: parseErrors,
327-
blockedByMetaschema: false
294+
hasParseErrors: parseErrors
328295
};
329296
currentPanelState = finalState;
330297
panelManager.updateContent(finalState);

webview/src/App.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ function App() {
3838
};
3939
}, []);
4040

41-
useEffect(() => {
42-
if (state?.blockedByMetaschema) {
43-
setActiveTab('metaschema');
44-
setActiveTabInState('metaschema');
45-
}
46-
}, [state?.blockedByMetaschema]);
47-
4841
const handleTabChange = (tab: TabType) => {
4942
setActiveTab(tab);
5043
setActiveTabInState(tab);
@@ -61,10 +54,9 @@ function App() {
6154
return (
6255
<div className="flex flex-col h-screen p-5">
6356
<FileInfo fileInfo={state.fileInfo} />
64-
<HealthBar
65-
lintResult={state.lintResult}
66-
isLoading={state.isLoading}
67-
blockedByMetaschema={state.blockedByMetaschema}
57+
<HealthBar
58+
lintResult={state.lintResult}
59+
isLoading={state.isLoading}
6860
noFileSelected={state.noFileSelected}
6961
/>
7062
<Tabs activeTab={activeTab} onTabChange={handleTabChange} state={state} />
@@ -76,8 +68,8 @@ function App() {
7668
<LoadingSpinner fileInfo={state.fileInfo} />
7769
) : (
7870
<>
79-
{activeTab === 'lint' && <LintTab lintResult={state.lintResult} blocked={!!state.blockedByMetaschema} noFileSelected={state.noFileSelected} />}
80-
{activeTab === 'format' && <FormatTab formatResult={state.formatResult} fileInfo={state.fileInfo} hasParseErrors={state.hasParseErrors} blocked={!!state.blockedByMetaschema} noFileSelected={state.noFileSelected} />}
71+
{activeTab === 'lint' && <LintTab lintResult={state.lintResult} noFileSelected={state.noFileSelected} />}
72+
{activeTab === 'format' && <FormatTab formatResult={state.formatResult} fileInfo={state.fileInfo} hasParseErrors={state.hasParseErrors} noFileSelected={state.noFileSelected} />}
8173
{activeTab === 'metaschema' && <MetaschemaTab metaschemaResult={state.metaschemaResult} noFileSelected={state.noFileSelected} />}
8274
</>
8375
)}

webview/src/components/FormatTab.tsx

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ export interface FormatTabProps {
77
formatResult: CommandResult;
88
fileInfo: FileInfo | null;
99
hasParseErrors?: boolean | undefined;
10-
blocked?: boolean | undefined;
1110
noFileSelected?: boolean | undefined;
1211
}
1312

14-
export function FormatTab({ formatResult, fileInfo, hasParseErrors, blocked, noFileSelected }: FormatTabProps) {
13+
export function FormatTab({ formatResult, fileInfo, hasParseErrors, noFileSelected }: FormatTabProps) {
1514
const handleFormatSchema = () => {
1615
formatSchema();
1716
};
@@ -49,22 +48,6 @@ export function FormatTab({ formatResult, fileInfo, hasParseErrors, blocked, noF
4948
);
5049
}
5150

52-
if (blocked) {
53-
return (
54-
<div className="text-center py-10 px-5">
55-
<div className="flex justify-center mb-4">
56-
<AlertCircle size={48} className="text-(--error)" strokeWidth={1.5} />
57-
</div>
58-
<div className="text-lg font-semibold text-(--vscode-fg) mb-2">
59-
Cannot Format Schema
60-
</div>
61-
<div className="text-[13px] text-(--vscode-muted) max-w-md mx-auto">
62-
Metaschema validation failed. Fix the metaschema errors first before attempting to format.
63-
</div>
64-
</div>
65-
);
66-
}
67-
6851
if (isYaml) {
6952
return (
7053
<div className="text-center py-10 px-5">

webview/src/components/HealthBar.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@ import type { LintResult } from '../../../protocol/types';
33
export interface HealthBarProps {
44
lintResult: LintResult;
55
isLoading?: boolean | undefined;
6-
blockedByMetaschema?: boolean | undefined;
76
noFileSelected?: boolean | undefined;
87
}
98

10-
export function HealthBar({ lintResult, isLoading, blockedByMetaschema, noFileSelected }: HealthBarProps) {
9+
export function HealthBar({ lintResult, isLoading, noFileSelected }: HealthBarProps) {
1110
const errorCount = lintResult.errors?.length || 0;
12-
11+
1312
let health: number;
1413

15-
if (noFileSelected || blockedByMetaschema) {
14+
if (noFileSelected) {
1615
health = 0;
1716
} else if (lintResult.health !== null && lintResult.health !== undefined) {
1817
health = lintResult.health;
@@ -21,22 +20,20 @@ export function HealthBar({ lintResult, isLoading, blockedByMetaschema, noFileSe
2120
} else {
2221
health = 0;
2322
}
24-
23+
2524
const getHealthColor = (health: number): string => {
2625
if (health >= 80) return 'var(--success)';
2726
if (health >= 50) return 'var(--warning)';
2827
return 'var(--error)';
2928
};
3029

31-
const showUnknown = !blockedByMetaschema && !noFileSelected && (isLoading || (lintResult.health === null && lintResult.errors === undefined));
30+
const showUnknown = !noFileSelected && (isLoading || (lintResult.health === null && lintResult.errors === undefined));
3231

3332
return (
3433
<div className="mb-5">
3534
<div className="text-(--vscode-fg) text-xs mb-1.5 font-semibold">
3635
Schema Health: {noFileSelected ? (
3736
<span className="text-(--vscode-muted)">N/A</span>
38-
) : blockedByMetaschema ? (
39-
<span className="text-(--vscode-muted)">N/A</span>
4037
) : showUnknown ? (
4138
<span className="text-(--vscode-muted)">?%</span>
4239
) : (

webview/src/components/LintTab.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import type { LintResult, Position } from '../../../protocol/types';
22
import { goToPosition } from '../message';
33
import { RawOutput } from './RawOutput';
4-
import { CheckCircle, AlertCircle, FileQuestion } from 'lucide-react';
4+
import { CheckCircle, FileQuestion } from 'lucide-react';
55

66
export interface LintTabProps {
77
lintResult: LintResult;
8-
blocked?: boolean | undefined;
98
noFileSelected?: boolean | undefined;
109
}
1110

12-
export function LintTab({ lintResult, blocked, noFileSelected }: LintTabProps) {
11+
export function LintTab({ lintResult, noFileSelected }: LintTabProps) {
1312
const handleGoToPosition = (position: Position) => {
1413
goToPosition(position);
1514
};
@@ -30,21 +29,6 @@ export function LintTab({ lintResult, blocked, noFileSelected }: LintTabProps) {
3029
);
3130
}
3231

33-
if (blocked) {
34-
return (
35-
<div className="text-center py-10 px-5">
36-
<div className="flex justify-center mb-4">
37-
<AlertCircle size={48} className="text-(--error)" strokeWidth={1.5} />
38-
</div>
39-
<div className="text-lg font-semibold text-(--vscode-fg) mb-2">Cannot Lint Schema</div>
40-
<div className="text-[13px] text-(--vscode-muted) max-w-md mx-auto">
41-
Metaschema validation failed. Fix the metaschema errors first before running lint.
42-
Check the Metaschema tab for more details.
43-
</div>
44-
</div>
45-
);
46-
}
47-
4832
return (
4933
<div>
5034
{errors.length === 0 ? (

webview/src/components/Tabs.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export function Tabs({ activeTab, onTabChange, state }: TabsProps) {
2020
const lintStatus = calculateLintStatus(state.lintResult.errors?.length || 0, state.lintResult.health, state.isLoading, state.noFileSelected);
2121
const formatStatus = calculateFormatStatus(state.formatResult.exitCode, state.formatLoading, state.fileInfo?.isYaml, state.noFileSelected);
2222
const metaschemaStatus = calculateMetaschemaStatus(state.metaschemaResult.exitCode, state.isLoading, state.noFileSelected);
23-
const lintDisabled = !!state.blockedByMetaschema;
2423

2524
const Tab = ({
2625
id,
@@ -50,8 +49,8 @@ export function Tabs({ activeTab, onTabChange, state }: TabsProps) {
5049

5150
return (
5251
<div className="flex border-b border-(--vscode-border) mb-5">
53-
<Tab id="lint" label="Lint" Icon={lintStatus.Icon} color={lintStatus.color} disabled={lintDisabled} />
54-
<Tab id="format" label="Format" Icon={formatStatus.Icon} color={formatStatus.color} disabled={lintDisabled} />
52+
<Tab id="lint" label="Lint" Icon={lintStatus.Icon} color={lintStatus.color} />
53+
<Tab id="format" label="Format" Icon={formatStatus.Icon} color={formatStatus.color} />
5554
<Tab id="metaschema" label="Metaschema" Icon={metaschemaStatus.Icon} color={metaschemaStatus.color} />
5655
</div>
5756
);

0 commit comments

Comments
 (0)