Skip to content

Feature: Add validation in visual editor yaml frontmatter #744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 9, 2025
30 changes: 26 additions & 4 deletions apps/lsp/src/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ import {
EditorServerOptions,
sourceServerMethods,
editorServerDocuments,
} from "editor-server"
} from "editor-server";

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

export function registerCustomMethods(
quarto: Quarto,
Expand Down Expand Up @@ -63,9 +63,31 @@ export function registerCustomMethods(
// we have the yaml hover and completions here so provide entry points for them
[kCodeViewAssist]: args => codeViewAssist(quarto, args[0]),
[kCodeViewGetCompletions]: args => codeViewCompletions(quarto, args[0]),
[kCodeViewGetDiagnostics]: args => codeViewDiagnostics(quarto, args[0])
});
}

async function codeViewDiagnostics(quarto: Quarto, context: CodeViewCellContext): Promise<LintItem[] | undefined> {
const edContext = codeEditorContext(
context.filepath,
context.language == "yaml" ? "yaml" : "script",
context.code.join("\n"),
Position.create(0, 0),
false
);

return await diagnostics(quarto, edContext) ?? undefined;

}
export async function diagnostics(quarto: Quarto, context: EditorContext): Promise<LintItem[] | null> {
if (!quarto?.getYamlDiagnostics) return null;

try {
return await quarto.getYamlDiagnostics(context);
} catch {
return null;
}
}

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

Expand Down Expand Up @@ -94,5 +116,5 @@ async function codeViewCompletions(quarto: Quarto, context: CodeViewCompletionCo
return {
isIncomplete: false,
items: completions || []
}
};
}
2 changes: 1 addition & 1 deletion apps/lsp/src/quarto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ import {
CompletionResult,
EditorContext,
HoverResult,
LintItem,
AttrContext,
AttrToken,
kContextDiv,
kContextDivSimple
} from "./service/quarto";
import { LintItem } from "editor-types";

export async function initializeQuarto(context: QuartoContext): Promise<Quarto> {
const quartoModule = await initializeQuartoYamlModule(context.resourcePath) as QuartoYamlModule;
Expand Down
6 changes: 1 addition & 5 deletions apps/lsp/src/service/providers/diagnostics-yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,10 @@ import {
import { Document } from "quarto-core";

import {
kEndColumn,
kEndRow,
kStartColumn,
kStartRow,
LintItem,
Quarto,
docEditorContext
} from "../quarto";
import { kEndColumn, kEndRow, kStartColumn, kStartRow, LintItem } from "editor-types";

export async function provideYamlDiagnostics(
quarto: Quarto,
Expand Down
16 changes: 1 addition & 15 deletions apps/lsp/src/service/quarto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,7 @@ import { CompletionItem, Position } from "vscode-languageserver-types";
import { QuartoContext, Document, filePathForDoc, isQuartoDoc, isQuartoRevealDoc, isQuartoYaml, isQuartoDashboardDoc } from "quarto-core";

import { lines } from "core";


export const kStartRow = "start.row";
export const kStartColumn = "start.column";
export const kEndRow = "end.row";
export const kEndColumn = "end.column";

export interface LintItem {
[kStartRow]: number;
[kStartColumn]: number;
[kEndRow]: number;
[kEndColumn]: number;
text: string;
type: string;
}
import { LintItem } from "editor-types";

export interface CompletionResult {
token: string;
Expand Down
1 change: 1 addition & 0 deletions apps/vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 1.124.0 (unreleased)

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

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

Expand Down
10 changes: 9 additions & 1 deletion apps/vscode/src/providers/editor/codeview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
CodeViewServer,
DiagramState,
kCodeViewGetCompletions,
kCodeViewGetDiagnostics,
LintItem
} from "editor-types";

import { hasHooks } from "../../host/hooks";
Expand Down Expand Up @@ -73,6 +75,12 @@ export function vscodeCodeViewServer(_engine: MarkdownEngine, document: TextDocu
break;
}
},
async codeViewDiagnostics(context: CodeViewCellContext): Promise<LintItem[] | undefined> {
// if this is yaml then call the lsp directly
if (context.language === "yaml") {
return lspRequest(kCodeViewGetDiagnostics, [context]);
}
},
async codeViewCompletions(context: CodeViewCompletionContext): Promise<CompletionList> {
// if this is yaml then call the lsp directly
if (context.language === "yaml") {
Expand Down Expand Up @@ -205,7 +213,7 @@ export function vsCompletionItemToLsCompletionItem(item: VCompletionItem): Compl

}

export function labelWithDetails(item: VCompletionItem): { label: string, labelWithDetails: CompletionItemLabelDetails } {
export function labelWithDetails(item: VCompletionItem): { label: string, labelWithDetails: CompletionItemLabelDetails; } {
if (typeof (item.label) === "string") {
return {
label: item.label,
Expand Down
Loading