release: WRNexusJS 0.2.27
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
"use strict";
|
||||
|
||||
const vscode = require("vscode");
|
||||
|
||||
const BLOCK_COMPLETIONS = [
|
||||
{
|
||||
label: "page",
|
||||
detail: "WRN page",
|
||||
documentation: "Create a WRN page declaration.",
|
||||
snippet: [
|
||||
"page ${1:PageName} {",
|
||||
' layout = "${2:default}"',
|
||||
"",
|
||||
" view {",
|
||||
" $0",
|
||||
" }",
|
||||
"}",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
label: "component",
|
||||
detail: "WRN component",
|
||||
documentation: "Create a reusable WRN component.",
|
||||
snippet: [
|
||||
"component ${1:ComponentName} {",
|
||||
" props {",
|
||||
' ${2:title} = "${3:Title}"',
|
||||
" }",
|
||||
"",
|
||||
" view {",
|
||||
" $0",
|
||||
" }",
|
||||
"}",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
label: "seo",
|
||||
detail: "SEO metadata block",
|
||||
snippet: [
|
||||
"seo {",
|
||||
' title = "${1:Page title}"',
|
||||
' description = "${2:Page description}"',
|
||||
"}",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
label: "view",
|
||||
detail: "WRN view block",
|
||||
snippet: ["view {", " $0", "}"].join("\n"),
|
||||
},
|
||||
{
|
||||
label: "state",
|
||||
detail: "Reactive state declaration",
|
||||
snippet: 'state ${1:name} = ${2:"value"}',
|
||||
},
|
||||
];
|
||||
|
||||
const ATTRIBUTE_COMPLETIONS = [
|
||||
["@click", "Click event handler", '@click="${1:handler()}"'],
|
||||
["@change", "Change event handler", '@change="${1:handler()}"'],
|
||||
["@input", "Input event handler", '@input="${1:handler()}"'],
|
||||
["@submit", "Submit event handler", '@submit="${1:handler()}"'],
|
||||
["@focus", "Focus event handler", '@focus="${1:handler()}"'],
|
||||
["@blur", "Blur event handler", '@blur="${1:handler()}"'],
|
||||
["data-show", "Conditional visibility", 'data-show="${1:condition}"'],
|
||||
["data-for", "Reactive loop", 'data-for="${1:item} in ${2:items}"'],
|
||||
["class:", "Conditional CSS class", 'class:${1:border-indigo-500}="${2:condition}"'],
|
||||
];
|
||||
|
||||
const CONTEXT_COMPLETIONS = [
|
||||
["ctx.params", "Dynamic route parameters"],
|
||||
["ctx.query", "URL query parameters"],
|
||||
["ctx.request", "Current Request object"],
|
||||
["ctx.user", "Authenticated user"],
|
||||
["ctx.session", "Current session"],
|
||||
["ctx.locals", "Request-local data"],
|
||||
];
|
||||
|
||||
function completionKindFor(label) {
|
||||
if (label.startsWith("@")) {
|
||||
return vscode.CompletionItemKind.Event;
|
||||
}
|
||||
|
||||
if (label.startsWith("class:") || label.startsWith("data-")) {
|
||||
return vscode.CompletionItemKind.Property;
|
||||
}
|
||||
|
||||
return vscode.CompletionItemKind.Keyword;
|
||||
}
|
||||
|
||||
function createCompletion(label, detail, snippet) {
|
||||
const item = new vscode.CompletionItem(label, completionKindFor(label));
|
||||
|
||||
item.detail = detail;
|
||||
item.insertText = new vscode.SnippetString(snippet || label);
|
||||
item.documentation = new vscode.MarkdownString(detail);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function getCurrentOpeningTag(document, position) {
|
||||
const textBeforeCursor = document.getText(
|
||||
new vscode.Range(new vscode.Position(position.line, 0), position),
|
||||
);
|
||||
|
||||
const lastOpen = textBeforeCursor.lastIndexOf("<");
|
||||
const lastClose = textBeforeCursor.lastIndexOf(">");
|
||||
|
||||
if (lastOpen > lastClose) {
|
||||
return textBeforeCursor.slice(lastOpen);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractRouteParams(document) {
|
||||
const fileName = document.fileName.replace(/\\/g, "/");
|
||||
const matches = [...fileName.matchAll(/\[([A-Za-z_$][\w$]*)\]/g)];
|
||||
|
||||
return matches.map((match) => match[1]);
|
||||
}
|
||||
|
||||
function extractStates(document) {
|
||||
const source = document.getText();
|
||||
const matches = [...source.matchAll(/^\s*state\s+([A-Za-z_$][\w$]*)\s*=/gm)];
|
||||
|
||||
return matches.map((match) => match[1]);
|
||||
}
|
||||
|
||||
function provideCompletionItems(document, position) {
|
||||
const items = [];
|
||||
const linePrefix = document.lineAt(position.line).text.slice(0, position.character);
|
||||
|
||||
const openingTag = getCurrentOpeningTag(document, position);
|
||||
|
||||
if (openingTag !== null) {
|
||||
for (const [label, detail, snippet] of ATTRIBUTE_COMPLETIONS) {
|
||||
items.push(createCompletion(label, detail, snippet));
|
||||
}
|
||||
} else {
|
||||
for (const completion of BLOCK_COMPLETIONS) {
|
||||
items.push(createCompletion(completion.label, completion.detail, completion.snippet));
|
||||
}
|
||||
}
|
||||
|
||||
if (linePrefix.includes("ctx.") || linePrefix.includes("ctx.params.")) {
|
||||
for (const [label, detail] of CONTEXT_COMPLETIONS) {
|
||||
items.push(createCompletion(label, detail, label));
|
||||
}
|
||||
}
|
||||
|
||||
for (const param of extractRouteParams(document)) {
|
||||
const item = new vscode.CompletionItem(param, vscode.CompletionItemKind.Variable);
|
||||
|
||||
item.detail = `Route parameter from [${param}].wrn`;
|
||||
item.insertText = param;
|
||||
|
||||
items.push(item);
|
||||
|
||||
const fullItem = new vscode.CompletionItem(
|
||||
`ctx.params.${param}`,
|
||||
vscode.CompletionItemKind.Variable,
|
||||
);
|
||||
|
||||
fullItem.detail = `Route parameter from [${param}].wrn`;
|
||||
fullItem.insertText = `ctx.params.${param}`;
|
||||
|
||||
items.push(fullItem);
|
||||
}
|
||||
|
||||
for (const state of extractStates(document)) {
|
||||
const item = new vscode.CompletionItem(state, vscode.CompletionItemKind.Variable);
|
||||
|
||||
item.detail = "WRN state";
|
||||
item.insertText = state;
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function registerCompletionProvider(context) {
|
||||
const provider = vscode.languages.registerCompletionItemProvider(
|
||||
{ language: "wrn", scheme: "file" },
|
||||
{
|
||||
provideCompletionItems,
|
||||
},
|
||||
"@",
|
||||
":",
|
||||
".",
|
||||
"<",
|
||||
" ",
|
||||
);
|
||||
|
||||
context.subscriptions.push(provider);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractRouteParams,
|
||||
extractStates,
|
||||
registerCompletionProvider,
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
const vscode = require("vscode");
|
||||
const { formatWrn } = require("./formatter");
|
||||
const { registerCompletionProvider } = require("./completion");
|
||||
|
||||
// The .wrn compiler, bundled to CJS by `bun run build:compiler`. Loaded
|
||||
// defensively so the rest of the extension (highlighting, snippets, completion)
|
||||
@@ -67,6 +68,8 @@ const EVENTS = [
|
||||
* @param {vscode.ExtensionContext} context
|
||||
*/
|
||||
function activate(context) {
|
||||
registerCompletionProvider(context);
|
||||
|
||||
const diagnostics = vscode.languages.createDiagnosticCollection("wrn");
|
||||
context.subscriptions.push(diagnostics);
|
||||
|
||||
|
||||
@@ -46,8 +46,7 @@ function findOpeningTagEnd(value) {
|
||||
|
||||
function parseAttributes(value) {
|
||||
const attributes = [];
|
||||
const pattern =
|
||||
/[^\s"'=<>`]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?/g;
|
||||
const pattern = /[^\s"'=<>`]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?/g;
|
||||
|
||||
let match;
|
||||
|
||||
@@ -121,9 +120,7 @@ function formatOpeningTag(value, unit, depth, printWidth = 100) {
|
||||
|
||||
const lines = [
|
||||
`${baseIndent}<${parsed.tagName}`,
|
||||
...parsed.attributes.map(
|
||||
(attribute) => `${attributeIndent}${attribute}`,
|
||||
),
|
||||
...parsed.attributes.map((attribute) => `${attributeIndent}${attribute}`),
|
||||
];
|
||||
|
||||
if (parsed.inlineClosing) {
|
||||
@@ -182,10 +179,7 @@ function isWrnBlockOpening(value) {
|
||||
}
|
||||
|
||||
function formatWrn(source, options = {}) {
|
||||
const unit =
|
||||
options.insertSpaces === false
|
||||
? "\t"
|
||||
: " ".repeat(options.tabSize || 4);
|
||||
const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize || 4);
|
||||
|
||||
const printWidth = options.printWidth || 100;
|
||||
const inputLines = source.replace(/\r\n/g, "\n").split("\n");
|
||||
@@ -251,12 +245,7 @@ function formatWrn(source, options = {}) {
|
||||
!value.startsWith("<!--") &&
|
||||
!isInlineElement(value)
|
||||
) {
|
||||
const formattedTag = formatOpeningTag(
|
||||
value,
|
||||
unit,
|
||||
depth,
|
||||
printWidth,
|
||||
);
|
||||
const formattedTag = formatOpeningTag(value, unit, depth, printWidth);
|
||||
|
||||
output.push(...formattedTag.lines);
|
||||
|
||||
@@ -284,4 +273,4 @@ function formatWrn(source, options = {}) {
|
||||
module.exports = {
|
||||
formatOpeningTag,
|
||||
formatWrn,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user