beta/code-blocks #1
4 changed files with 74 additions and 69 deletions
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -38,8 +38,10 @@ const elements = [
|
||||||
'card', 'badge', 'alert', 'tooltip', 'input', 'textarea', 'select',
|
'card', 'badge', 'alert', 'tooltip', 'input', 'textarea', 'select',
|
||||||
'checkbox', 'radio', 'switch', 'list', 'table', 'progress', 'slider'
|
'checkbox', 'radio', 'switch', 'list', 'table', 'progress', 'slider'
|
||||||
];
|
];
|
||||||
// Script block types
|
// Script blocks
|
||||||
const scriptBlocks = ['client', 'server'];
|
const scriptBlocks = [
|
||||||
|
'client', 'server'
|
||||||
|
];
|
||||||
// Single instance elements
|
// Single instance elements
|
||||||
const singleElements = ['page', 'navbar'];
|
const singleElements = ['page', 'navbar'];
|
||||||
// Blueprint properties
|
// Blueprint properties
|
||||||
|
@ -54,6 +56,8 @@ const properties = [
|
||||||
];
|
];
|
||||||
// Page configuration properties
|
// Page configuration properties
|
||||||
const pageProperties = ['title', 'description', 'keywords', 'author'];
|
const pageProperties = ['title', 'description', 'keywords', 'author'];
|
||||||
|
// ID attribute suggestion - using underscore format
|
||||||
|
const idAttributeTemplate = 'id:$1_$2';
|
||||||
// Container elements that can have children
|
// Container elements that can have children
|
||||||
const containerElements = [
|
const containerElements = [
|
||||||
'horizontal', 'vertical', 'section', 'grid', 'navbar',
|
'horizontal', 'vertical', 'section', 'grid', 'navbar',
|
||||||
|
@ -87,6 +91,18 @@ connection.onCompletion((textDocumentPosition) => {
|
||||||
const position = textDocumentPosition.position;
|
const position = textDocumentPosition.position;
|
||||||
const line = lines[position.line];
|
const line = lines[position.line];
|
||||||
const linePrefix = line.slice(0, position.character);
|
const linePrefix = line.slice(0, position.character);
|
||||||
|
// Suggest script blocks after @ symbol
|
||||||
|
if (linePrefix.trim().endsWith('@')) {
|
||||||
|
return scriptBlocks.map(block => ({
|
||||||
|
label: `@${block}`,
|
||||||
|
kind: node_1.CompletionItemKind.Snippet,
|
||||||
|
insertText: `@${block} {\n $1\n}`,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: block === 'client' ?
|
||||||
|
'Create a client-side JavaScript block that runs when the element is clicked. The "e" event object is available.' :
|
||||||
|
'Create a server-side JavaScript block that runs on the server.'
|
||||||
|
}));
|
||||||
|
}
|
||||||
// Check if this is a template completion trigger
|
// Check if this is a template completion trigger
|
||||||
if (linePrefix.trim() === '!') {
|
if (linePrefix.trim() === '!') {
|
||||||
return [{
|
return [{
|
||||||
|
@ -106,65 +122,6 @@ connection.onCompletion((textDocumentPosition) => {
|
||||||
}]
|
}]
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
// Check for @client or @server block completion
|
|
||||||
if (linePrefix.trim() === '@' || linePrefix.trim().startsWith('@')) {
|
|
||||||
return scriptBlocks.map(blockType => ({
|
|
||||||
label: `@${blockType}`,
|
|
||||||
kind: node_1.CompletionItemKind.Snippet,
|
|
||||||
insertText: `@${blockType} {\n $1\n}`,
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: `Create a ${blockType} script block`
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// After an @client or @server block opening brace, suggest JS snippets
|
|
||||||
const scriptBlockMatch = /@(client|server)\s*{\s*$/.exec(linePrefix);
|
|
||||||
if (scriptBlockMatch) {
|
|
||||||
const blockType = scriptBlockMatch[1];
|
|
||||||
const jsSnippets = [];
|
|
||||||
if (blockType === 'client') {
|
|
||||||
jsSnippets.push({
|
|
||||||
label: 'element.set',
|
|
||||||
kind: node_1.CompletionItemKind.Method,
|
|
||||||
insertText: '${1:elementId}.set("${2:new value}");',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Set the content of an element'
|
|
||||||
}, {
|
|
||||||
label: 'console.log',
|
|
||||||
kind: node_1.CompletionItemKind.Method,
|
|
||||||
insertText: 'console.log("${1:message}");',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Log a message to the console'
|
|
||||||
}, {
|
|
||||||
label: 'DOM event handling',
|
|
||||||
kind: node_1.CompletionItemKind.Snippet,
|
|
||||||
insertText: 'e.preventDefault();\n${1}',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Prevent default action of event'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (blockType === 'server') {
|
|
||||||
jsSnippets.push({
|
|
||||||
label: 'element.set',
|
|
||||||
kind: node_1.CompletionItemKind.Method,
|
|
||||||
insertText: '${1:elementId}.set(${2:newValue});',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Update element value from server'
|
|
||||||
}, {
|
|
||||||
label: 'element.value',
|
|
||||||
kind: node_1.CompletionItemKind.Property,
|
|
||||||
insertText: 'const value = ${1:elementId}.value;',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Get the current value of an element'
|
|
||||||
}, {
|
|
||||||
label: 'fetch data',
|
|
||||||
kind: node_1.CompletionItemKind.Snippet,
|
|
||||||
insertText: 'const response = await fetch("${1:url}");\nconst data = await response.json();\n${2}',
|
|
||||||
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
|
||||||
documentation: 'Fetch data from an API'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return jsSnippets;
|
|
||||||
}
|
|
||||||
// Inside page block
|
// Inside page block
|
||||||
if (text.includes('page {') && !text.includes('}')) {
|
if (text.includes('page {') && !text.includes('}')) {
|
||||||
return pageProperties.map(prop => ({
|
return pageProperties.map(prop => ({
|
||||||
|
@ -175,13 +132,22 @@ connection.onCompletion((textDocumentPosition) => {
|
||||||
documentation: `Add ${prop} to the page configuration`
|
documentation: `Add ${prop} to the page configuration`
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// After an opening parenthesis, suggest properties
|
// After an opening parenthesis, suggest properties including ID with underscore format
|
||||||
if (linePrefix.trim().endsWith('(')) {
|
if (linePrefix.trim().endsWith('(')) {
|
||||||
return properties.map(prop => ({
|
return [
|
||||||
|
...properties.map(prop => ({
|
||||||
label: prop,
|
label: prop,
|
||||||
kind: node_1.CompletionItemKind.Property,
|
kind: node_1.CompletionItemKind.Property,
|
||||||
documentation: `Apply ${prop} property`
|
documentation: `Apply ${prop} property`
|
||||||
}));
|
})),
|
||||||
|
{
|
||||||
|
label: 'id',
|
||||||
|
kind: node_1.CompletionItemKind.Property,
|
||||||
|
insertText: idAttributeTemplate,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: 'Add an ID to the element (use underscores instead of hyphens for JavaScript compatibility)'
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
// After a container element's opening brace, suggest child elements
|
// After a container element's opening brace, suggest child elements
|
||||||
const containerMatch = /\b(horizontal|vertical|section|grid|navbar|links|card)\s*{\s*$/.exec(linePrefix);
|
const containerMatch = /\b(horizontal|vertical|section|grid|navbar|links|card)\s*{\s*$/.exec(linePrefix);
|
||||||
|
@ -200,6 +166,25 @@ connection.onCompletion((textDocumentPosition) => {
|
||||||
suggestedElements = ['title', 'text', 'button', 'image'];
|
suggestedElements = ['title', 'text', 'button', 'image'];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Include client/server block suggestions for interactive elements
|
||||||
|
if (['button', 'button-light', 'button-secondary', 'button-compact'].includes(parentElement)) {
|
||||||
|
return [
|
||||||
|
...suggestedElements.map(element => ({
|
||||||
|
label: element,
|
||||||
|
kind: node_1.CompletionItemKind.Class,
|
||||||
|
insertText: `${element} {\n $1\n}`,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: `Create a ${element} block inside ${parentElement}`
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
label: '@client',
|
||||||
|
kind: node_1.CompletionItemKind.Snippet,
|
||||||
|
insertText: `@client {\n $1\n}`,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: 'Create a client-side JavaScript block that runs when the element is clicked. The "e" event object is available.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
return suggestedElements.map(element => ({
|
return suggestedElements.map(element => ({
|
||||||
label: element,
|
label: element,
|
||||||
kind: node_1.CompletionItemKind.Class,
|
kind: node_1.CompletionItemKind.Class,
|
||||||
|
@ -208,6 +193,26 @@ connection.onCompletion((textDocumentPosition) => {
|
||||||
documentation: `Create a ${element} block inside ${parentElement}`
|
documentation: `Create a ${element} block inside ${parentElement}`
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
// Inside interactive elements, suggest @client blocks
|
||||||
|
const interactiveElementMatch = /\b(button|button-light|button-secondary|button-compact|input|textarea|select|checkbox|radio|switch)\s*(?:\([^)]*\))?\s*{\s*$/.exec(linePrefix);
|
||||||
|
if (interactiveElementMatch) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '@client',
|
||||||
|
kind: node_1.CompletionItemKind.Snippet,
|
||||||
|
insertText: `@client {\n $1\n}`,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: 'Create a client-side JavaScript block that runs when the element is clicked. The "e" event object is available.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'text',
|
||||||
|
kind: node_1.CompletionItemKind.Class,
|
||||||
|
insertText: `"$1"`,
|
||||||
|
insertTextFormat: node_1.InsertTextFormat.Snippet,
|
||||||
|
documentation: 'Add text content to the element'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
// Get available single instance elements
|
// Get available single instance elements
|
||||||
const availableSingleElements = singleElements.filter(element => !elementExists(text, element));
|
const availableSingleElements = singleElements.filter(element => !elementExists(text, element));
|
||||||
// Combine regular elements with available single instance elements
|
// Combine regular elements with available single instance elements
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue