757 lines
18 KiB
JavaScript
757 lines
18 KiB
JavaScript
const vscode = require("vscode");
|
|
|
|
const AT_RULES = new Set([
|
|
"each",
|
|
"for",
|
|
"from",
|
|
"through",
|
|
"include",
|
|
"responsive",
|
|
"media",
|
|
"supports",
|
|
"concentric",
|
|
"view-transition",
|
|
"predict",
|
|
"measure",
|
|
"property",
|
|
"mixin",
|
|
"stcss",
|
|
"import",
|
|
"charset",
|
|
"document",
|
|
"font-face",
|
|
"keyframes",
|
|
"layer",
|
|
"namespace",
|
|
"page",
|
|
"scope",
|
|
"starting-style",
|
|
"container",
|
|
]);
|
|
|
|
const STATE_WORDS = new Set([
|
|
"active",
|
|
"clicked",
|
|
"focus",
|
|
"hover",
|
|
"open",
|
|
"closed",
|
|
"selected",
|
|
"loading",
|
|
"visible",
|
|
"hidden",
|
|
]);
|
|
|
|
const AT_RULE_DOCS = new Map([
|
|
["mixin", "Declare a reusable STCSS mixin: `@mixin name($param) { … }`"],
|
|
["include", "Expand a mixin: `@include name(10%);`"],
|
|
["each", "Repeat rules over values: `@each $item in a b c { … }`"],
|
|
["for", "Count a range: `@for $i from 1 through 5 { … }`"],
|
|
["responsive", "Wrap rules in a named breakpoint from the config."],
|
|
["media", "Standard `@media` query wrapper."],
|
|
["supports", "Standard `@supports` feature wrapper."],
|
|
["concentric", "Emit concentric nested radii: `@concentric 0.75rem { … }`"],
|
|
["view-transition", "Emit `view-transition-name` on the attached rule."],
|
|
[
|
|
"predict",
|
|
"Declare which properties transition when prediction prefill changes (emits a smooth transition rule).",
|
|
],
|
|
[
|
|
"measure",
|
|
'Mark the attached rule (or listed selectors / `@anchor`s) as a prediction target. Prerender measures the elements and writes geometry. Bare `@anchor` arguments compile to `[data-muddle-anchor="anchor"]`.',
|
|
],
|
|
]);
|
|
|
|
const REFERENCE_DOC =
|
|
'Reads pre-computed geometry recorded from a `@sample` anchor: compiles to `var(--muddle-relate-…)`.\n\nMetrics: `left`, `top`, `width`, `height`. `@anchor` compiles to `[data-muddle-anchor="anchor"]`.';
|
|
|
|
const RELATE_DOC =
|
|
"Legacy relation value of an anchor (deprecated in favour of `$refer[metric](@anchor)`).";
|
|
|
|
const CSS_PROPERTIES = [
|
|
"background",
|
|
"background-color",
|
|
"background-image",
|
|
"border",
|
|
"border-color",
|
|
"border-radius",
|
|
"border-width",
|
|
"bottom",
|
|
"box-shadow",
|
|
"box-sizing",
|
|
"color",
|
|
"cursor",
|
|
"display",
|
|
"flex",
|
|
"flex-basis",
|
|
"flex-direction",
|
|
"flex-shrink",
|
|
"flex-wrap",
|
|
"font-family",
|
|
"font-size",
|
|
"font-weight",
|
|
"gap",
|
|
"grid-area",
|
|
"height",
|
|
"inset",
|
|
"justify-content",
|
|
"left",
|
|
"letter-spacing",
|
|
"line-height",
|
|
"margin",
|
|
"max-height",
|
|
"max-width",
|
|
"min-height",
|
|
"min-width",
|
|
"opacity",
|
|
"overflow",
|
|
"padding",
|
|
"pointer-events",
|
|
"position",
|
|
"right",
|
|
"text-decoration",
|
|
"text-transform",
|
|
"top",
|
|
"transform",
|
|
"transition",
|
|
"vertical-align",
|
|
"visibility",
|
|
"white-space",
|
|
"width",
|
|
"z-index",
|
|
];
|
|
|
|
const HTML_ATTRIBUTES = [
|
|
"alt",
|
|
"aria-controls",
|
|
"aria-current",
|
|
"aria-description",
|
|
"aria-expanded",
|
|
"aria-hidden",
|
|
"aria-label",
|
|
"aria-live",
|
|
"autocomplete",
|
|
"class",
|
|
"content",
|
|
"href",
|
|
"id",
|
|
"placeholder",
|
|
"rel",
|
|
"role",
|
|
"src",
|
|
"style",
|
|
"tabindex",
|
|
"target",
|
|
"title",
|
|
"type",
|
|
];
|
|
|
|
const INDEX = {
|
|
anchors: new Map(), // name -> [{ uri, line, kind }]
|
|
variables: new Map(), // $name -> [{ uri, line }]
|
|
spugFiles: [],
|
|
styleFiles: [],
|
|
};
|
|
|
|
let indexToken = 0;
|
|
|
|
function relativeToWorkspace(uri) {
|
|
const folder = vscode.workspace.getWorkspaceFolder(uri);
|
|
if (!folder) return uri.fsPath.split(/[\\/]/).pop();
|
|
return uri.fsPath.slice(folder.uri.fsPath.length + 1);
|
|
}
|
|
|
|
function debounce(callback, wait) {
|
|
let timer;
|
|
return (...args) => {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(() => callback(...args), wait);
|
|
};
|
|
}
|
|
|
|
async function readText(uri) {
|
|
try {
|
|
const bytes = await vscode.workspace.fs.readFile(uri);
|
|
return new TextDecoder().decode(bytes);
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function posFromOffset(text, offset) {
|
|
const before = text.slice(0, offset);
|
|
const newline = before.lastIndexOf("\n");
|
|
return new vscode.Position(
|
|
before.split("\n").length - 1,
|
|
offset - newline - 1,
|
|
);
|
|
}
|
|
|
|
function rangeOf(text, offset, length) {
|
|
return new vscode.Range(
|
|
posFromOffset(text, offset),
|
|
posFromOffset(text, offset + Math.max(length, 1)),
|
|
);
|
|
}
|
|
|
|
function isStcss(document) {
|
|
return document.languageId === "stcss";
|
|
}
|
|
|
|
function rememberAnchor(name, uri, line, kind) {
|
|
const list = INDEX.anchors.get(name) ?? [];
|
|
list.push({ uri, line, kind });
|
|
INDEX.anchors.set(name, list);
|
|
}
|
|
|
|
function indexSpugDocument(uri, text) {
|
|
const lines = text.split("\n");
|
|
const anchorRegex = /@([A-Za-z_][\w-]*)(?:\(([^(){}]*)\))?/g;
|
|
lines.forEach((line, index) => {
|
|
let match;
|
|
anchorRegex.lastIndex = 0;
|
|
while ((match = anchorRegex.exec(line))) {
|
|
if (AT_RULES.has(match[1]) || match[1] === "sample") continue;
|
|
rememberAnchor(match[1], uri, index + 1, "declared in SPUG");
|
|
}
|
|
});
|
|
}
|
|
|
|
function indexStcssDocument(uri, text) {
|
|
const variableRegex = /(\$[\w-]+)\s*:/g;
|
|
let match;
|
|
while ((match = variableRegex.exec(text))) {
|
|
const position = posFromOffset(text, match.index);
|
|
const list = INDEX.variables.get(match[1]) ?? [];
|
|
list.push({ uri, line: position.line + 1 });
|
|
INDEX.variables.set(match[1], list);
|
|
}
|
|
|
|
const anchorRegex = /@([A-Za-z_][\w-]*)/g;
|
|
while ((match = anchorRegex.exec(text))) {
|
|
if (AT_RULES.has(match[1])) continue;
|
|
rememberAnchor(
|
|
match[1],
|
|
uri,
|
|
posFromOffset(text, match.index).line + 1,
|
|
"referenced in STCSS",
|
|
);
|
|
}
|
|
}
|
|
|
|
async function indexWorkspace() {
|
|
const token = ++indexToken;
|
|
if (!vscode.workspace.workspaceFolders) return;
|
|
const [spugFiles, styleFiles] = await Promise.all([
|
|
vscode.workspace.findFiles("**/*.spug", "**/node_modules/**"),
|
|
vscode.workspace.findFiles("**/*.stcss", "**/node_modules/**"),
|
|
]);
|
|
if (token !== indexToken) return;
|
|
INDEX.anchors = new Map();
|
|
INDEX.variables = new Map();
|
|
INDEX.spugFiles = spugFiles;
|
|
INDEX.styleFiles = styleFiles;
|
|
await Promise.all([
|
|
...spugFiles.map(async (uri) =>
|
|
indexSpugDocument(uri, await readText(uri)),
|
|
),
|
|
...styleFiles.map(async (uri) =>
|
|
indexStcssDocument(uri, await readText(uri)),
|
|
),
|
|
]);
|
|
}
|
|
|
|
function anchorCompletions() {
|
|
return [...INDEX.anchors.keys()].map((name) => {
|
|
const item = new vscode.CompletionItem(
|
|
`@${name}`,
|
|
vscode.CompletionItemKind.Keyword,
|
|
);
|
|
item.detail = "relation anchor";
|
|
const first = INDEX.anchors.get(name)[0];
|
|
if (first)
|
|
item.documentation = new vscode.MarkdownString(
|
|
`${first.kind}: \`${relativeToWorkspace(first.uri)}:${first.line}\``,
|
|
);
|
|
return item;
|
|
});
|
|
}
|
|
|
|
function stcssCompletions() {
|
|
const atRules = [...AT_RULE_DOCS.keys()].map((name) => {
|
|
const item = new vscode.CompletionItem(
|
|
`@${name}`,
|
|
vscode.CompletionItemKind.Struct,
|
|
);
|
|
item.detail = "stcss at-rule";
|
|
item.insertText = new vscode.SnippetString(
|
|
name === "concentric" || name === "media"
|
|
? `${name} $1 {\n\t$0\n}`
|
|
: `${name};`,
|
|
);
|
|
item.documentation = new vscode.MarkdownString(AT_RULE_DOCS.get(name));
|
|
return item;
|
|
});
|
|
const metrics = ["left", "top", "width", "height"].map((metric) => {
|
|
const item = new vscode.CompletionItem(
|
|
`$refer[${metric}](@)`,
|
|
vscode.CompletionItemKind.Function,
|
|
);
|
|
item.insertText = new vscode.SnippetString(
|
|
`$refer[${metric}](@\${1:anchor})`,
|
|
);
|
|
item.documentation = new vscode.MarkdownString(REFERENCE_DOC);
|
|
return item;
|
|
});
|
|
const relate = new vscode.CompletionItem(
|
|
"$relate(@)",
|
|
vscode.CompletionItemKind.Function,
|
|
);
|
|
relate.insertText = new vscode.SnippetString("$relate(@${1:anchor})");
|
|
relate.documentation = new vscode.MarkdownString(RELATE_DOC);
|
|
const states = [...STATE_WORDS].map((state) => {
|
|
const item = new vscode.CompletionItem(
|
|
state,
|
|
vscode.CompletionItemKind.EnumMember,
|
|
);
|
|
item.detail = "anchor state";
|
|
return item;
|
|
});
|
|
const properties = CSS_PROPERTIES.map((name) => {
|
|
const item = new vscode.CompletionItem(
|
|
name,
|
|
vscode.CompletionItemKind.Property,
|
|
);
|
|
item.insertText = new vscode.SnippetString(`${name}: $0;`);
|
|
return item;
|
|
});
|
|
const variables = [...INDEX.variables.keys()].map((name) => {
|
|
const item = new vscode.CompletionItem(
|
|
name,
|
|
vscode.CompletionItemKind.Variable,
|
|
);
|
|
item.detail = "stcss variable";
|
|
return item;
|
|
});
|
|
return [
|
|
...atRules,
|
|
...metrics,
|
|
relate,
|
|
...states,
|
|
...properties,
|
|
...variables,
|
|
];
|
|
}
|
|
|
|
function spugCompletions() {
|
|
const keywords = [
|
|
"extends",
|
|
"include",
|
|
"block",
|
|
"append",
|
|
"prepend",
|
|
"mixin",
|
|
"slot",
|
|
"yield",
|
|
"island",
|
|
"component",
|
|
"render",
|
|
"doctype",
|
|
].map((word) => {
|
|
const item = new vscode.CompletionItem(
|
|
word,
|
|
vscode.CompletionItemKind.Keyword,
|
|
);
|
|
return item;
|
|
});
|
|
const javascript = new vscode.CompletionItem(
|
|
"javascript!",
|
|
vscode.CompletionItemKind.Keyword,
|
|
);
|
|
javascript.insertText = new vscode.SnippetString("javascript!\n\t$0\nend");
|
|
const control = [
|
|
"if",
|
|
"else",
|
|
"unless",
|
|
"each",
|
|
"while",
|
|
"case",
|
|
"when",
|
|
"default",
|
|
"props",
|
|
].map(
|
|
(word) =>
|
|
new vscode.CompletionItem(word, vscode.CompletionItemKind.Keyword),
|
|
);
|
|
const attributes = HTML_ATTRIBUTES.map((name) => {
|
|
const item = new vscode.CompletionItem(
|
|
name,
|
|
vscode.CompletionItemKind.Property,
|
|
);
|
|
item.insertText = new vscode.SnippetString(`${name}=$0`);
|
|
return item;
|
|
});
|
|
return [
|
|
...keywords,
|
|
javascript,
|
|
...control,
|
|
...attributes,
|
|
...anchorCompletions(),
|
|
];
|
|
}
|
|
|
|
function stcssHover(document, position) {
|
|
const line = document.lineAt(position.line).text;
|
|
const preceding = line.slice(0, position.character);
|
|
const anchor = /(?:^|[^\w$])@([A-Za-z_][\w-]*)$/.exec(preceding)?.[1];
|
|
if (anchor) return hoverAnchor(anchor);
|
|
const variable = /\$[\w-]+$/.exec(preceding)?.[0];
|
|
if (variable) {
|
|
const hits = INDEX.variables.get(variable) ?? [];
|
|
return new vscode.Hover(
|
|
new vscode.MarkdownString(
|
|
hits.length
|
|
? `**${variable}**: defined in ${hits.map((h) => `${relativeToWorkspace(h.uri)}:${h.line}`).join(", ")}`
|
|
: `**${variable}**`,
|
|
),
|
|
);
|
|
}
|
|
const refer = /(?:\$\w+(?:\[[\w-]+\])?)$/.exec(preceding)?.[0];
|
|
if (refer) {
|
|
if (refer.startsWith("$refer"))
|
|
return new vscode.Hover(new vscode.MarkdownString(REFERENCE_DOC));
|
|
if (refer.startsWith("$relate"))
|
|
return new vscode.Hover(new vscode.MarkdownString(RELATE_DOC));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function hoverAnchor(name) {
|
|
const hits = INDEX.anchors.get(name) ?? [];
|
|
if (!hits.length) return null;
|
|
const body = hits
|
|
.slice(0, 8)
|
|
.map(
|
|
(hit) => `- ${hit.kind}: \`${relativeToWorkspace(hit.uri)}:${hit.line}\``,
|
|
)
|
|
.join("\n");
|
|
return new vscode.Hover(
|
|
new vscode.MarkdownString(`**@${name}**: relation anchor\n\n${body}`),
|
|
);
|
|
}
|
|
|
|
function spugHover(document, position) {
|
|
const range = document.getWordRangeAtPosition(position, /@?[A-Za-z_][\w-]*/);
|
|
if (!range) return null;
|
|
const word = document.getText(range);
|
|
if (
|
|
word.startsWith("@") ||
|
|
document.getText(
|
|
new vscode.Range(
|
|
range.start.translate({ characterDelta: -1 }),
|
|
range.start,
|
|
),
|
|
) === "@"
|
|
) {
|
|
return hoverAnchor(word.replace(/^@/, ""));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function anchorDefinition(name) {
|
|
return (INDEX.anchors.get(name) ?? [])
|
|
.filter((hit) => hit.kind === "declared in SPUG")
|
|
.map(
|
|
(hit) =>
|
|
new vscode.Location(hit.uri, new vscode.Position(hit.line - 1, 0)),
|
|
);
|
|
}
|
|
|
|
function stcssDefinition(document, position) {
|
|
const line = document
|
|
.lineAt(position.line)
|
|
.text.slice(0, position.character + 1);
|
|
const anchor = /@([A-Za-z_][\w-]*)[^\w]?$/.exec(line)?.[1];
|
|
if (anchor) return anchorDefinition(anchor);
|
|
return null;
|
|
}
|
|
|
|
function spugDefinition(document, position) {
|
|
const before = document
|
|
.lineAt(position.line)
|
|
.text.slice(0, position.character + 1);
|
|
const anchor = /@([A-Za-z_][\w-]*)[^\w]?$/.exec(before)?.[1];
|
|
if (anchor) {
|
|
// jump from anchor to its STCSS references
|
|
const refs = (INDEX.anchors.get(anchor) ?? []).filter(
|
|
(hit) => hit.kind === "referenced in STCSS",
|
|
);
|
|
return refs.map(
|
|
(hit) =>
|
|
new vscode.Location(
|
|
hit.uri,
|
|
new vscode.Range(hit.line - 1, 0, hit.line - 1, 200),
|
|
),
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function diagnosticCollection() {
|
|
return vscode.languages.createDiagnosticCollection("muddle");
|
|
}
|
|
|
|
function lintDocument(document, collection) {
|
|
if (document.languageId !== "stcss" && document.languageId !== "spug")
|
|
return [];
|
|
const text = document.getText();
|
|
const issues = [];
|
|
|
|
// balanced braces in both languages
|
|
let depth = 0;
|
|
let lastOpen = 0;
|
|
for (let index = 0; index < text.length && issues.length < 40; index += 1) {
|
|
const char = text[index];
|
|
if (char === "{") {
|
|
depth += 1;
|
|
lastOpen = index;
|
|
} else if (char === "}") {
|
|
depth -= 1;
|
|
if (depth < 0) {
|
|
issues.push(
|
|
new vscode.Diagnostic(
|
|
rangeOf(text, index, 1),
|
|
"Unexpected `}`.",
|
|
vscode.DiagnosticSeverity.Error,
|
|
),
|
|
);
|
|
depth = 0;
|
|
}
|
|
}
|
|
}
|
|
if (depth > 0) {
|
|
issues.push(
|
|
new vscode.Diagnostic(
|
|
rangeOf(text, lastOpen, 1),
|
|
"Unclosed `{` block.",
|
|
vscode.DiagnosticSeverity.Error,
|
|
),
|
|
);
|
|
}
|
|
|
|
if (document.languageId === "stcss") {
|
|
const anchorRegex = /@([A-Za-z_][\w-]*)/g;
|
|
let match;
|
|
while ((match = anchorRegex.exec(text))) {
|
|
const name = match[1];
|
|
const offset = match.index;
|
|
const previous = text[offset - 1];
|
|
const dynamic = text
|
|
.slice(
|
|
offset,
|
|
text.indexOf("\n", offset) === -1
|
|
? text.length
|
|
: text.indexOf("\n", offset),
|
|
)
|
|
.includes("(");
|
|
if (AT_RULES.has(name) || previous === "$" || dynamic) continue;
|
|
if (!INDEX.anchors.has(name)) {
|
|
issues.push(
|
|
new vscode.Diagnostic(
|
|
rangeOf(text, offset, match[0].length),
|
|
`No SPUG file declares relation anchor \`@${name}\`.`,
|
|
vscode.DiagnosticSeverity.Warning,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
collection.set(document.uri, issues);
|
|
return issues;
|
|
}
|
|
|
|
function lintVisible() {
|
|
for (const editor of vscode.window.visibleTextEditors)
|
|
lintDocument(editor.document, diagnostics);
|
|
}
|
|
|
|
function symbolKindFor(document) {
|
|
return isStcss(document) ? vscode.SymbolKind.Class : vscode.SymbolKind.Module;
|
|
}
|
|
|
|
function symbolsOf(document) {
|
|
const flat = [];
|
|
for (let line = 0; line < document.lineCount; line += 1) {
|
|
const text = document.lineAt(line).text;
|
|
const match = /^(\s*)(@?[A-Za-z][^\n{;]*?|@[\w-]+)\s*\{?\s*$/.exec(text);
|
|
if (!match || !text.trim()) continue;
|
|
if (isStcss(document) && !text.includes("{")) continue;
|
|
if (!isStcss(document) && text.includes("{")) continue;
|
|
const label = match[2].trim() || "root";
|
|
const symbol = new vscode.DocumentSymbol(
|
|
label,
|
|
"",
|
|
symbolKindFor(document),
|
|
new vscode.Range(line, 0, line, text.length),
|
|
new vscode.Range(
|
|
line,
|
|
match[1].length,
|
|
line,
|
|
match[1].length + label.length,
|
|
),
|
|
);
|
|
flat.push({ symbol, indent: match[1].length });
|
|
}
|
|
const roots = [];
|
|
const stack = [];
|
|
for (const entry of flat) {
|
|
while (stack.length && entry.indent <= stack[stack.length - 1].indent)
|
|
stack.pop();
|
|
if (stack.length)
|
|
stack[stack.length - 1].symbol.children.push(entry.symbol);
|
|
else roots.push(entry.symbol);
|
|
stack.push(entry);
|
|
}
|
|
return roots;
|
|
}
|
|
|
|
function foldingOf(document) {
|
|
const ranges = [];
|
|
const stack = [];
|
|
for (let line = 0; line < document.lineCount; line += 1) {
|
|
const text = document.lineAt(line).text;
|
|
if (isStcss(document)) {
|
|
const opens = (text.match(/\{/g) ?? []).length;
|
|
const closes = (text.match(/\}/g) ?? []).length;
|
|
for (let i = 0; i < opens; i += 1) stack.push(line);
|
|
for (let i = 0; i < closes && stack.length; i += 1) {
|
|
const start = stack.pop();
|
|
if (line > start) ranges.push(new vscode.FoldingRange(start, line));
|
|
}
|
|
} else {
|
|
const indent = text.match(/^(\s*)/)[1].length;
|
|
if (!text.trim()) continue;
|
|
while (stack.length && indent <= stack[stack.length - 1].indent) {
|
|
const start = stack.pop();
|
|
if (line - 1 > start)
|
|
ranges.push(new vscode.FoldingRange(start, line - 1));
|
|
}
|
|
stack.push({ indent, line });
|
|
}
|
|
}
|
|
while (stack.length) {
|
|
const start =
|
|
typeof stack[stack.length - 1] === "number"
|
|
? stack.pop()
|
|
: stack.pop().line;
|
|
if (document.lineCount - 1 > start)
|
|
ranges.push(new vscode.FoldingRange(start, document.lineCount - 1));
|
|
}
|
|
return ranges;
|
|
}
|
|
|
|
let diagnostics;
|
|
|
|
function completionsFor(document, position) {
|
|
const line = document.lineAt(position.line);
|
|
const before = line.text.slice(0, position.character);
|
|
if (line.isEmptyOrWhitespace) return [];
|
|
const prefix = before.trim();
|
|
if (prefix.length === 0 && position.character > 0) return [];
|
|
// completions popped while just typing whitespace mid-line are noise
|
|
if (
|
|
before.length > 0 &&
|
|
/\s$/.test(before) &&
|
|
!/[$@:(\[=,{]\s*$/.test(before)
|
|
)
|
|
return [];
|
|
return isStcss(document) ? stcssCompletions(before) : spugCompletions(before);
|
|
}
|
|
|
|
function activate(context) {
|
|
diagnostics = diagnosticCollection();
|
|
|
|
const refresh = debounce(async () => {
|
|
await indexWorkspace();
|
|
lintVisible();
|
|
}, 500);
|
|
|
|
Promise.resolve(refresh());
|
|
|
|
context.subscriptions.push(
|
|
diagnostics,
|
|
|
|
vscode.languages.registerCompletionItemProvider(
|
|
["stcss", "spug"],
|
|
{
|
|
provideCompletionItems(document, position) {
|
|
return completionsFor(document, position);
|
|
},
|
|
},
|
|
"@",
|
|
"$",
|
|
".",
|
|
"-",
|
|
":",
|
|
"=",
|
|
),
|
|
|
|
vscode.languages.registerHoverProvider(["stcss", "spug"], {
|
|
provideHover(document, position) {
|
|
const word = document.getWordRangeAtPosition(position, /[@$]?[\w-]+/);
|
|
if (!word) return null;
|
|
try {
|
|
return isStcss(document)
|
|
? stcssHover(document, position)
|
|
: spugHover(document, position);
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
}),
|
|
|
|
vscode.languages.registerDefinitionProvider(["stcss", "spug"], {
|
|
provideDefinition(document, position) {
|
|
try {
|
|
return isStcss(document)
|
|
? stcssDefinition(document, position)
|
|
: spugDefinition(document, position);
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
}),
|
|
|
|
vscode.languages.registerDocumentSymbolProvider(["stcss", "spug"], {
|
|
provideDocumentSymbols(document) {
|
|
return symbolsOf(document);
|
|
},
|
|
}),
|
|
|
|
vscode.languages.registerFoldingRangeProvider(["stcss", "spug"], {
|
|
provideFoldingRanges(document) {
|
|
return foldingOf(document);
|
|
},
|
|
}),
|
|
|
|
vscode.workspace.onDidOpenTextDocument((document) =>
|
|
lintDocument(document, diagnostics),
|
|
),
|
|
vscode.workspace.onDidChangeTextDocument((event) => {
|
|
lintDocument(event.document, diagnostics);
|
|
refresh();
|
|
}),
|
|
vscode.workspace.onDidSaveTextDocument(refresh),
|
|
vscode.workspace.onDidCreateFiles(refresh),
|
|
vscode.workspace.onDidDeleteFiles(refresh),
|
|
);
|
|
|
|
lintVisible();
|
|
}
|
|
|
|
function deactivate() {
|
|
return undefined;
|
|
}
|
|
|
|
module.exports = { activate, deactivate };
|