Skip to content

Commit

Permalink
feat: implement document symbols provider
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Jul 18, 2022
1 parent 6906b39 commit 7ebc777
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .changeset/short-rabbits-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"marko-vscode": patch
"@marko/language-server": patch
---

Implement document symbols provider.
11 changes: 11 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ connection.onInitialize(async (params) => {
documentLinkProvider: { resolveProvider: false },
colorProvider: true,
documentHighlightProvider: true,
documentSymbolProvider: true,
completionProvider: {
triggerCharacters: [
".",
Expand Down Expand Up @@ -126,6 +127,16 @@ connection.onDocumentLinks(async (params, cancel) => {
);
});

connection.onDocumentSymbol(async (params, cancel) => {
return (
(await service.findDocumentSymbols(
documents.get(params.textDocument.uri)!,
params,
cancel
)) || null
);
});

connection.onDocumentHighlight(async (params, cancel) => {
return (
(await service.findDocumentHighlights(
Expand Down
18 changes: 18 additions & 0 deletions server/src/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
DocumentHighlight,
DocumentLink,
Location,
SymbolInformation,
WorkspaceEdit,
} from "vscode-languageserver";
import { displayError } from "../utils/messages";
Expand Down Expand Up @@ -93,6 +94,23 @@ const service: Plugin = {

return result;
},
async findDocumentSymbols(doc, params, cancel) {
let result: SymbolInformation[] | undefined;

try {
for (const pending of plugins.map((plugin) =>
plugin.findDocumentSymbols?.(doc, params, cancel)
)) {
const cur = await pending;
if (cancel.isCancellationRequested) return;
if (cur) result = result ? result.concat(cur) : cur;
}
} catch (err) {
displayError(err);
}

return result;
},
async findDocumentLinks(doc, params, cancel) {
let result: DocumentLink[] | undefined;

Expand Down
20 changes: 12 additions & 8 deletions server/src/service/marko/document-links/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,20 @@ export function extractDocumentLinks(
const read = (range: Range) => code.slice(range.start, range.end);
const visit = (node: Node.ChildNode) => {
switch (node.type) {
case NodeType.AttrTag:
if (node.body) {
for (const child of node.body) {
visit(child);
}
}
break;
case NodeType.Tag:
if (node.attrs && node.nameText) {
for (const attr of node.attrs) {
if (isDocumentLinkAttr(doc, node, attr)) {
links.push(
DocumentLink.create(
{
start: parsed.positionAt(attr.value.value.start),
end: parsed.positionAt(attr.value.value.end),
},
parsed.locationAt(attr.value.value),
resolveUrl(read(attr.value.value).slice(1, -1), doc.uri)
)
);
Expand Down Expand Up @@ -71,10 +75,10 @@ export function extractDocumentLinks(
if (fileForTag) {
links.push(
DocumentLink.create(
{
start: parsed.positionAt(item.start + match.index),
end: parsed.positionAt(item.start + match.index + length),
},
parsed.locationAt({
start: item.start + match.index,
end: item.start + match.index + length,
}),
fileForTag
)
);
Expand Down
54 changes: 54 additions & 0 deletions server/src/service/marko/document-symbols/extract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { URI } from "vscode-uri";
import type { TaglibLookup } from "@marko/babel-utils";
import { SymbolInformation, SymbolKind } from "vscode-languageserver";
import type { TextDocument } from "vscode-languageserver-textdocument";
import { type Node, type parse, NodeType } from "../../../utils/parser";

/**
* Iterate over the Marko CST and extract all the symbols (mostly tags) in the document.
*/
export function extractDocumentSymbols(
doc: TextDocument,
parsed: ReturnType<typeof parse>,
lookup: TaglibLookup
): SymbolInformation[] {
if (URI.parse(doc.uri).scheme === "untitled") {
return [];
}

const symbols: SymbolInformation[] = [];
const { program } = parsed;
const visit = (node: Node.ChildNode) => {
switch (node.type) {
case NodeType.Tag:
case NodeType.AttrTag:
symbols.push(
SymbolInformation.create(
(node.type === NodeType.AttrTag
? node.nameText?.slice(node.nameText.indexOf("@"))
: node.nameText) || "<${...}>",
(node.nameText &&
lookup.getTag(node.nameText)?.html &&
SymbolKind.Property) ||
SymbolKind.Class,
parsed.locationAt(node),
doc.uri
)
);

if (node.body) {
for (const child of node.body) {
visit(child);
}
}

break;
}
};

for (const item of program.body) {
visit(item);
}

return symbols;
}
18 changes: 18 additions & 0 deletions server/src/service/marko/document-symbols/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { SymbolInformation } from "vscode-languageserver";
import { getCompilerInfo, parse } from "../../../utils/compiler";
import type { Plugin } from "../../types";
import { extractDocumentSymbols } from "./extract";

const cache = new WeakMap<ReturnType<typeof parse>, SymbolInformation[]>();

export const findDocumentSymbols: Plugin["findDocumentSymbols"] = async (
doc
) => {
const parsed = parse(doc);
let result = cache.get(parsed);
if (!result) {
result = extractDocumentSymbols(doc, parsed, getCompilerInfo(doc).lookup);
cache.set(parsed, result);
}
return result;
};
2 changes: 2 additions & 0 deletions server/src/service/marko/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { doComplete } from "./complete";
import { doValidate } from "./validate";
import { findDefinition } from "./definition";
import { findDocumentLinks } from "./document-links";
import { findDocumentSymbols } from "./document-symbols";
import { format } from "./format";

export default {
doComplete,
doValidate,
findDefinition,
findDocumentLinks,
findDocumentSymbols,
format,
} as Plugin;
7 changes: 7 additions & 0 deletions server/src/service/stylesheet/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export function extractStyleSheets(
};
const visit = (node: Node.ChildNode) => {
switch (node.type) {
case NodeType.AttrTag:
if (node.body) {
for (const child of node.body) {
visit(child);
}
}
break;
case NodeType.Tag:
if (node.nameText === "style" && node.concise && node.attrs) {
const block = node.attrs.at(-1)!;
Expand Down
33 changes: 33 additions & 0 deletions server/src/service/stylesheet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DocumentLink,
InitializeParams,
ColorPresentation,
SymbolInformation,
} from "vscode-languageserver";
import {
getCSSLanguageService,
Expand Down Expand Up @@ -166,6 +167,38 @@ const StyleSheetService: Partial<Plugin> = {
return result.length ? result : undefined;
}
},
findDocumentSymbols(doc) {
const infoByExt = getStyleSheetInfo(doc);
const result: SymbolInformation[] = [];

for (const ext in infoByExt) {
const info = infoByExt[ext];
const { service, virtualDoc } = info;

for (const symbol of service.findDocumentSymbols(
virtualDoc,
info.parsed
)) {
if (symbol.location.uri === doc.uri) {
const range = getSourceRange(doc, info, symbol.location.range);
if (range) {
result.push({
kind: symbol.kind,
name: symbol.name,
tags: symbol.tags,
deprecated: symbol.deprecated,
containerName: symbol.containerName,
location: { uri: doc.uri, range },
});
}
} else {
result.push(symbol);
}
}
}

return result.length ? result : undefined;
},
async findDocumentLinks(doc) {
const infoByExt = getStyleSheetInfo(doc);
const result: DocumentLink[] = [];
Expand Down
3 changes: 3 additions & 0 deletions server/src/service/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import type {
DocumentHighlightParams,
DocumentLink,
DocumentLinkParams,
DocumentSymbolParams,
Hover,
HoverParams,
InitializeParams,
Location,
LocationLink,
ReferenceParams,
RenameParams,
SymbolInformation,
TextEdit,
WorkspaceEdit,
} from "vscode-languageserver";
Expand All @@ -48,6 +50,7 @@ export type Plugin = {
Location | LocationLink | (Location | LocationLink)[]
>;
findReferences: Handler<ReferenceParams, Location[]>;
findDocumentSymbols: Handler<DocumentSymbolParams, SymbolInformation[]>;
findDocumentLinks: Handler<DocumentLinkParams, DocumentLink[]>;
findDocumentHighlights: Handler<DocumentHighlightParams, DocumentHighlight[]>;
findDocumentColors: Handler<DocumentColorParams, ColorInformation[]>;
Expand Down

0 comments on commit 7ebc777

Please sign in to comment.