Skip to content
Draft
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
72 changes: 29 additions & 43 deletions apps/lsp/src/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,44 +55,8 @@ export async function registerDiagnostics(

const subs: Disposable[] = [];



// baseline diagnostics sent on save (and cleared on change)
const saveDiagnosticsSources: Array<(doc: Document) => Promise<Diagnostic[]>> = [];
saveDiagnosticsSources.push((doc: Document) => {
return mdLs.computeOnSaveDiagnostics(doc);
});
// diagnostics on open and save (clear on doc modified)
subs.push(
documents.onDidOpen(async (e) => {
sendDiagnostics(e.document, await computeDiagnostics(e.document));
})
);
subs.push(
documents.onDidSave(async (e) => {
sendDiagnostics(e.document, await computeDiagnostics(e.document));
})
);
subs.push(
documents.onDidChangeContent(async (e) => {
sendDiagnostics(e.document, []);
})
);
const computeDiagnostics = async (
doc: Document
): Promise<Diagnostic[]> => {
return (await Promise.all(saveDiagnosticsSources.map(src => src(doc)))).flat();
};
const sendDiagnostics = (doc: Document, diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({
uri: doc.uri,
version: doc.version,
diagnostics,
});
};


// if we can watch files then register a pull source for markdown
// if we can watch files then register a pull source (diagnostics are
// computed as the user types)
if (isWorkspaceWithFileWatching(workspace)) {
let diagnosticOptions: DiagnosticOptions = kDefaultDiagnosticOptions;
const updateDiagnosticsSetting = (): void => {
Expand Down Expand Up @@ -169,14 +133,36 @@ export async function registerDiagnostics(
})
);
} else {
// run diagnostics on save (and clear on edit)
saveDiagnosticsSources.push((doc: Document) => {
return mdLs?.computeDiagnostics(
// no file watching, so run diagnostics on open and save (and clear on edit)
const computeDiagnostics = (doc: Document): Promise<Diagnostic[]> => {
return mdLs.computeDiagnostics(
doc,
getDiagnosticsOptions(configManager),
CancellationToken.None
)
});
);
};
const sendDiagnostics = (doc: Document, diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({
uri: doc.uri,
version: doc.version,
diagnostics,
});
};
subs.push(
documents.onDidOpen(async (e) => {
sendDiagnostics(e.document, await computeDiagnostics(e.document));
})
);
subs.push(
documents.onDidSave(async (e) => {
sendDiagnostics(e.document, await computeDiagnostics(e.document));
})
);
subs.push(
documents.onDidChangeContent(async (e) => {
sendDiagnostics(e.document, []);
})
);
}

return {
Expand Down
17 changes: 3 additions & 14 deletions apps/lsp/src/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { URI } from 'vscode-uri';
import { Document, Parser } from "quarto-core";
import { LsConfiguration } from './config';
import { MdDefinitionProvider } from './providers/definitions';
import { DiagnosticComputer, DiagnosticOnSaveComputer, DiagnosticOptions, DiagnosticsManager, IPullDiagnosticsManager } from './providers/diagnostics';
import { DiagnosticComputer, DiagnosticOptions, DiagnosticsManager, IPullDiagnosticsManager } from './providers/diagnostics';
import { MdDocumentHighlightProvider } from './providers/document-highlights';
import { createWorkspaceLinkCache, MdLinkProvider, ResolvedDocumentLinkTarget } from './providers/document-links';
import { MdDocumentSymbolProvider } from './providers/document-symbols';
Expand Down Expand Up @@ -148,13 +148,6 @@ export interface IMdLanguageService {
*/
getDocumentHighlights(document: Document, position: lsp.Position, token: CancellationToken): Promise<lsp.DocumentHighlight[]>;

/**
* Compute save diagnostics for a given file
*
* Compute diagnostics that should be scanned for on save (and cleared on edit)
*/
computeOnSaveDiagnostics(doc: Document): Promise<lsp.Diagnostic[]>;

/**
* Compute diagnostics for a given file.
*
Expand Down Expand Up @@ -206,8 +199,7 @@ export function createLanguageService(init: LanguageServiceInitialization): IMdL
const linkCache = createWorkspaceLinkCache(init.parser, init.workspace);
const referencesProvider = new MdReferencesProvider(config, init.parser, init.workspace, tocProvider, linkCache, logger);
const definitionsProvider = new MdDefinitionProvider(config, init.workspace, tocProvider, linkCache);
const diagnosticOnSaveComputer = new DiagnosticOnSaveComputer(init.quarto);
const diagnosticsComputer = new DiagnosticComputer(config, init.workspace, linkProvider, tocProvider, logger);
const diagnosticsComputer = new DiagnosticComputer(config, init.workspace, linkProvider, tocProvider, logger, init.quarto);
const docSymbolProvider = new MdDocumentSymbolProvider(config, tocProvider, linkProvider, logger);
const workspaceSymbolProvider = new MdWorkspaceSymbolProvider(init.workspace, init.config, docSymbolProvider);
const documentHighlightProvider = new MdDocumentHighlightProvider(config, tocProvider, linkProvider);
Expand Down Expand Up @@ -237,17 +229,14 @@ export function createLanguageService(init: LanguageServiceInitialization): IMdL
getDocumentHighlights: (document: Document, position: lsp.Position, token: CancellationToken): Promise<lsp.DocumentHighlight[]> => {
return documentHighlightProvider.getDocumentHighlights(document, position, token);
},
computeOnSaveDiagnostics: async (doc: Document) => {
return (await diagnosticOnSaveComputer.compute(doc));
},
computeDiagnostics: async (doc: Document, options: DiagnosticOptions, token: CancellationToken): Promise<lsp.Diagnostic[]> => {
return (await diagnosticsComputer.compute(doc, options, token))?.diagnostics;
},
createPullDiagnosticsManager: () => {
if (!isWorkspaceWithFileWatching(init.workspace)) {
throw new Error(`Workspace does not support file watching. Diagnostics manager not supported`);
}
return new DiagnosticsManager(config, init.workspace, linkProvider, tocProvider, logger);
return new DiagnosticsManager(config, init.workspace, linkProvider, tocProvider, logger, init.quarto);
}
});
}
20 changes: 11 additions & 9 deletions apps/lsp/src/service/providers/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,34 +171,29 @@ class FileLinkMap {
}
}

export class DiagnosticOnSaveComputer {
constructor(private readonly quarto_: Quarto) { }

public async compute(doc: Document): Promise<lsp.Diagnostic[]> {
return provideYamlDiagnostics(this.quarto_, doc);
}
}

export class DiagnosticComputer {

readonly #configuration: LsConfiguration;
readonly #workspace: IWorkspace;
readonly #linkProvider: MdLinkProvider;
readonly #tocProvider: MdTableOfContentsProvider;
readonly #logger: ILogger;
readonly #quarto: Quarto;

constructor(
configuration: LsConfiguration,
workspace: IWorkspace,
linkProvider: MdLinkProvider,
tocProvider: MdTableOfContentsProvider,
logger: ILogger,
quarto: Quarto,
) {
this.#configuration = configuration;
this.#workspace = workspace;
this.#linkProvider = linkProvider;
this.#tocProvider = tocProvider;
this.#logger = logger;
this.#quarto = quarto;
}

public async compute(
Expand All @@ -212,6 +207,10 @@ export class DiagnosticComputer {
}> {
this.#logger.logDebug('DiagnosticComputer.compute', { document: doc.uri, version: doc.version });

// yaml diagnostics (frontmatter and cell options) -- kicked off
// concurrently with link resolution below
const yamlDiagnostics = provideYamlDiagnostics(this.#quarto, doc);

const { links, definitions } = await this.#linkProvider.getLinks(doc);
const statCache = new ResourceMap<{ readonly exists: boolean; }>();
if (token.isCancellationRequested) {
Expand All @@ -235,6 +234,8 @@ export class DiagnosticComputer {
])).flat());
}

diagnostics.push(...(await yamlDiagnostics));

this.#logger.logTrace('DiagnosticComputer.compute finished', { document: doc.uri, version: doc.version, diagnostics });

return {
Expand Down Expand Up @@ -643,6 +644,7 @@ export class DiagnosticsManager extends Disposable implements IPullDiagnosticsMa
linkProvider: MdLinkProvider,
tocProvider: MdTableOfContentsProvider,
logger: ILogger,
quarto: Quarto,
) {
super();

Expand Down Expand Up @@ -679,7 +681,7 @@ export class DiagnosticsManager extends Disposable implements IPullDiagnosticsMa
},
});

this.#computer = new DiagnosticComputer(configuration, stateCachedWorkspace, linkProvider, tocProvider, logger);
this.#computer = new DiagnosticComputer(configuration, stateCachedWorkspace, linkProvider, tocProvider, logger, quarto);

this._register(workspace.onDidDeleteMarkdownDocument(uri => {
this.#linkWatcher.deleteDocument(uri);
Expand Down
46 changes: 45 additions & 1 deletion apps/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,49 @@
"path": "./languages/mermaid/mermaid.tmLanguage.json"
}
],
"semanticTokenTypes": [
{
"id": "quartoYamlKey",
"description": "YAML key in a Quarto cell option (#|) comment"
},
{
"id": "quartoYamlString",
"description": "YAML string value in a Quarto cell option (#|) comment"
},
{
"id": "quartoYamlNumber",
"description": "YAML number value in a Quarto cell option (#|) comment"
},
{
"id": "quartoYamlBoolean",
"description": "YAML boolean value in a Quarto cell option (#|) comment"
},
{
"id": "quartoYamlNull",
"description": "YAML null value in a Quarto cell option (#|) comment"
}
],
"semanticTokenScopes": [
{
"scopes": {
"quartoYamlKey": [
"entity.name.tag.yaml"
],
"quartoYamlString": [
"string.unquoted.plain.out.yaml"
],
"quartoYamlNumber": [
"constant.numeric.yaml"
],
"quartoYamlBoolean": [
"constant.language.boolean.yaml"
],
"quartoYamlNull": [
"constant.language.null.yaml"
]
}
}
],
"snippets": [
{
"language": "quarto",
Expand Down Expand Up @@ -1529,7 +1572,8 @@
"vscode-languageclient": "^8.1.0",
"vscode-languageserver-types": "^3.17.3",
"vscode-nls": "^5.2.0",
"which": "^3.0.0"
"which": "^3.0.0",
"yaml": "^2.8.1"
},
"devDependencies": {
"@types/axios": "^0.14.0",
Expand Down
4 changes: 4 additions & 0 deletions apps/vscode/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { activateDiagram } from "./providers/diagram/diagram";
import { activateCodeFormatting } from "./providers/format";
import { activateOptionEnterProvider } from "./providers/option";
import { activateBackgroundHighlighter } from "./providers/background";
import { activateHashPipeYamlHighlighter } from "./providers/hash-pipe-yaml";
import { activateYamlLinks } from "./providers/yaml-links";
import { activateYamlFilepathCompletions } from "./providers/yaml-filepath-completions";
import { activateContextKeySetter } from "./providers/context-keys";
Expand Down Expand Up @@ -231,6 +232,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<Quarto
// background highlighter
activateBackgroundHighlighter(context, engine);

// yaml highlighting for cell options (#| comments)
activateHashPipeYamlHighlighter(context, engine);

// yaml document links
activateYamlLinks(context);

Expand Down
Loading
Loading