Skip to content

Commit c800f78

Browse files
authored
Feature: Add validation in visual editor yaml frontmatter (#744)
* Add working prototype of validation in visual editor yaml frontmatter * Add validation on update * Pass diagnostics * Fix build, move LintItem defn, change literal to constant `kCodeViewGetDiagnostics` * Update validation to be and use diagnostics instead * Fix duplicate error icon in tooltip * format: add semicolons, and remove old code and comment * Cleanup a little more * Add changelog entry
1 parent bc9d871 commit c800f78

File tree

14 files changed

+442
-186
lines changed

14 files changed

+442
-186
lines changed

apps/lsp/src/custom.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ import {
2222
EditorServerOptions,
2323
sourceServerMethods,
2424
editorServerDocuments,
25-
} from "editor-server"
25+
} from "editor-server";
2626

2727
import { LspConnection, registerLspServerMethods } from "core-node";
2828
import { userDictionaryDir, Document } from "quarto-core";
2929
import { CompletionList } from "vscode-languageserver-types";
3030
import { Hover, Position, TextDocuments } from "vscode-languageserver";
31-
import { CodeViewCellContext, CodeViewCompletionContext, kCodeViewAssist, kCodeViewGetCompletions } from "editor-types";
31+
import { CodeViewCellContext, CodeViewCompletionContext, kCodeViewAssist, kCodeViewGetDiagnostics, kCodeViewGetCompletions, LintItem } from "editor-types";
3232
import { yamlCompletions } from "./service/providers/completion/completion-yaml";
3333
import { yamlHover } from "./service/providers/hover/hover-yaml";
34-
import { Quarto, codeEditorContext } from "./service/quarto";
34+
import { EditorContext, Quarto, codeEditorContext } from "./service/quarto";
3535

3636
export function registerCustomMethods(
3737
quarto: Quarto,
@@ -63,9 +63,31 @@ export function registerCustomMethods(
6363
// we have the yaml hover and completions here so provide entry points for them
6464
[kCodeViewAssist]: args => codeViewAssist(quarto, args[0]),
6565
[kCodeViewGetCompletions]: args => codeViewCompletions(quarto, args[0]),
66+
[kCodeViewGetDiagnostics]: args => codeViewDiagnostics(quarto, args[0])
6667
});
6768
}
6869

70+
async function codeViewDiagnostics(quarto: Quarto, context: CodeViewCellContext): Promise<LintItem[] | undefined> {
71+
const edContext = codeEditorContext(
72+
context.filepath,
73+
context.language == "yaml" ? "yaml" : "script",
74+
context.code.join("\n"),
75+
Position.create(0, 0),
76+
false
77+
);
78+
79+
return await diagnostics(quarto, edContext) ?? undefined;
80+
81+
}
82+
export async function diagnostics(quarto: Quarto, context: EditorContext): Promise<LintItem[] | null> {
83+
if (!quarto?.getYamlDiagnostics) return null;
84+
85+
try {
86+
return await quarto.getYamlDiagnostics(context);
87+
} catch {
88+
return null;
89+
}
90+
}
6991

7092
async function codeViewAssist(quarto: Quarto, context: CodeViewCellContext): Promise<Hover | undefined> {
7193

@@ -94,5 +116,5 @@ async function codeViewCompletions(quarto: Quarto, context: CodeViewCompletionCo
94116
return {
95117
isIncomplete: false,
96118
items: completions || []
97-
}
119+
};
98120
}

apps/lsp/src/quarto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ import {
3535
CompletionResult,
3636
EditorContext,
3737
HoverResult,
38-
LintItem,
3938
AttrContext,
4039
AttrToken,
4140
kContextDiv,
4241
kContextDivSimple
4342
} from "./service/quarto";
43+
import { LintItem } from "editor-types";
4444

4545
export async function initializeQuarto(context: QuartoContext): Promise<Quarto> {
4646
const quartoModule = await initializeQuartoYamlModule(context.resourcePath) as QuartoYamlModule;

apps/lsp/src/service/providers/diagnostics-yaml.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,10 @@ import {
2424
import { Document } from "quarto-core";
2525

2626
import {
27-
kEndColumn,
28-
kEndRow,
29-
kStartColumn,
30-
kStartRow,
31-
LintItem,
3227
Quarto,
3328
docEditorContext
3429
} from "../quarto";
30+
import { kEndColumn, kEndRow, kStartColumn, kStartRow, LintItem } from "editor-types";
3531

3632
export async function provideYamlDiagnostics(
3733
quarto: Quarto,

apps/lsp/src/service/quarto.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,7 @@ import { CompletionItem, Position } from "vscode-languageserver-types";
1818
import { QuartoContext, Document, filePathForDoc, isQuartoDoc, isQuartoRevealDoc, isQuartoYaml, isQuartoDashboardDoc } from "quarto-core";
1919

2020
import { lines } from "core";
21-
22-
23-
export const kStartRow = "start.row";
24-
export const kStartColumn = "start.column";
25-
export const kEndRow = "end.row";
26-
export const kEndColumn = "end.column";
27-
28-
export interface LintItem {
29-
[kStartRow]: number;
30-
[kStartColumn]: number;
31-
[kEndRow]: number;
32-
[kEndColumn]: number;
33-
text: string;
34-
type: string;
35-
}
21+
import { LintItem } from "editor-types";
3622

3723
export interface CompletionResult {
3824
token: string;

apps/vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 1.124.0 (unreleased)
44

5+
- Add yaml frontmatter validation to visual editor (<https://github.com/quarto-dev/quarto/pull/744>).
56

67
## 1.123.0 (Release on 2025-06-17)
78

apps/vscode/src/providers/editor/codeview.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import {
4343
CodeViewServer,
4444
DiagramState,
4545
kCodeViewGetCompletions,
46+
kCodeViewGetDiagnostics,
47+
LintItem
4648
} from "editor-types";
4749

4850
import { hasHooks } from "../../host/hooks";
@@ -73,6 +75,12 @@ export function vscodeCodeViewServer(_engine: MarkdownEngine, document: TextDocu
7375
break;
7476
}
7577
},
78+
async codeViewDiagnostics(context: CodeViewCellContext): Promise<LintItem[] | undefined> {
79+
// if this is yaml then call the lsp directly
80+
if (context.language === "yaml") {
81+
return lspRequest(kCodeViewGetDiagnostics, [context]);
82+
}
83+
},
7684
async codeViewCompletions(context: CodeViewCompletionContext): Promise<CompletionList> {
7785
// if this is yaml then call the lsp directly
7886
if (context.language === "yaml") {
@@ -205,7 +213,7 @@ export function vsCompletionItemToLsCompletionItem(item: VCompletionItem): Compl
205213

206214
}
207215

208-
export function labelWithDetails(item: VCompletionItem): { label: string, labelWithDetails: CompletionItemLabelDetails } {
216+
export function labelWithDetails(item: VCompletionItem): { label: string, labelWithDetails: CompletionItemLabelDetails; } {
209217
if (typeof (item.label) === "string") {
210218
return {
211219
label: item.label,

0 commit comments

Comments
 (0)