From 53192cefcf3eef4ea77f8cc984d77a7dc57b4896 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Thu, 4 Jan 2024 14:50:42 +0530 Subject: [PATCH] refactor: improvements to completions --- language-configuration.json | 2 +- src/regexes.ts | 6 ++-- src/template_indexer.ts | 10 +++++- src/types.ts | 11 +++++++ src/vscode/builtin_tags_completion.ts | 12 +++++++ src/vscode/providers/edge_completions.ts | 42 +++++++++++++++++------- src/vscode/wrappers.ts | 6 +++- 7 files changed, 72 insertions(+), 17 deletions(-) diff --git a/language-configuration.json b/language-configuration.json index 9775909..91c2643 100644 --- a/language-configuration.json +++ b/language-configuration.json @@ -19,7 +19,7 @@ ], "folding": { "markers": { - "start": "^\\s*(@((?!let|eval|assign|include|includeIf|inject|newError)[a-zA-Z._]+)(\\s{0,2}))(\\()", + "start": "^\\s*(@((?!let|eval|assign|include|includeIf|inject|newError|svg)[a-zA-Z._]+)(\\s{0,2}))(\\()", "end": "^\\s*@end([a-zA-Z._]?)\\b" } }, diff --git a/src/regexes.ts b/src/regexes.ts index 8c0a178..03cf4fd 100644 --- a/src/regexes.ts +++ b/src/regexes.ts @@ -1,13 +1,13 @@ /** * Find all the views that are being used inside an Edge template */ -export const edgeRegex = /(@include|@layout|@!?component)\(['"]([^'"]+)['"]/g +export const edgeRegex = /(@include|@!?component)\(['"]([^'"]+)['"]/g /** * Find all components as tags inside an Edge template */ export const edgeComponentsAsTagsRegex = - /^[ \t]*(@!?(?!include|set|can|unless|let|eval|inject|!component|if|elseif|vite|entryPointScripts|entryPointStyles|each|click|section|layout|component|slot|!section)(.+?))\(.*/gm + /^[ \t]*(@!?(?!include|includeIf|set|can|unless|svg|let|eval|inject|!component|if|elseif|else|vite|entryPointScripts|entryPointStyles|each|assign|debugger|component|slot|newError)(.+?))\(.*/gm /** * Find all the views that are being used inside a TS/Js file @@ -24,7 +24,7 @@ export const viewsCompletionRegex = /(?<=@include\(['"]|@layout\(['"]|@!componen * to suggest completions */ export const edgeComponentsAsTagsCompletionRegex = - /@!?(?!include|set|unless|let|eval|inject|!component|if|elseif|vite|entryPointScripts|entryPointStyles|each|click|section|layout|component|slot|!section)(.+)?\(?/g + /@!?(?!include|includeIf|set|can|unless|svg|let|eval|inject|!component|if|elseif|else|vite|entryPointScripts|entryPointStyles|each|assign|debugger|component|slot|newError)(.+)?\(?/g /** * Check if we are currently inside a view link and capture the user input to suggest completions diff --git a/src/template_indexer.ts b/src/template_indexer.ts index 8b56a18..405d453 100644 --- a/src/template_indexer.ts +++ b/src/template_indexer.ts @@ -137,7 +137,15 @@ export class TemplateIndexer { const name = disk === 'default' ? filename : `${disk}::${filename}` const componentName = this.#componentFileNameToTagName(diskPath, path, disk) - return { path, name, disk, isComponent: !!componentName, componentName } + return { + path, + name, + disk, + isComponent: !!componentName, + componentName, + selfClosedInsertText: `${componentName}(\${1})`, + insertText: `${componentName}(\${1}) \n\t$0\n@end`, + } }) return this.#templates diff --git a/src/types.ts b/src/types.ts index 62f2581..4f2ada6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -32,6 +32,17 @@ export interface Template { */ disk: string + /** + * Text to insert on completion + */ + insertText: string + + /** + * Text to insert on completion for a self + * closed usage + */ + selfClosedInsertText: string + /** * Whether the template is a component or not ( located in a components/ directory ) */ diff --git a/src/vscode/builtin_tags_completion.ts b/src/vscode/builtin_tags_completion.ts index 042fdb9..63bb366 100644 --- a/src/vscode/builtin_tags_completion.ts +++ b/src/vscode/builtin_tags_completion.ts @@ -69,6 +69,16 @@ componentItem.documentation = new MarkdownString( ) componentItem.detail = 'Render a component' +/** + * Self closing `@component` + */ +const selfClosingComponentItem = new CompletionItem('component', CompletionItemKind.Keyword) +selfClosingComponentItem.insertText = new SnippetString("component('${1:componentName}', { $2 })") +selfClosingComponentItem.documentation = new MarkdownString( + 'Insert an @component statement\n\nhttps://edgejs.dev/docs/components/introduction' +) +selfClosingComponentItem.detail = 'Render a self closing component' + /** * `@slot` */ @@ -211,3 +221,5 @@ export const builtinTags = [ layoutItem, setItem, ] + +export const builtinSelfClosingTags = [selfClosingComponentItem] diff --git a/src/vscode/providers/edge_completions.ts b/src/vscode/providers/edge_completions.ts index b4495ee..c809816 100644 --- a/src/vscode/providers/edge_completions.ts +++ b/src/vscode/providers/edge_completions.ts @@ -1,8 +1,8 @@ import { CompletionItemKind, Position, Range } from 'vscode' +import { SuperCompletionItem } from '../wrappers' import { IndexerManager } from '../indexer_manager' import { edgeComponentsAsTagsCompletionRegex, viewsCompletionRegex } from '../../regexes' -import { SuperCompletionItem } from '../wrappers' -import { builtinTags } from '../builtin_tags_completion' +import { builtinSelfClosingTags, builtinTags } from '../builtin_tags_completion' import type { CompletionItem, CompletionItemProvider, TextDocument } from 'vscode' /** @@ -28,9 +28,15 @@ export class EdgeCompletionProvider implements CompletionItemProvider { * Check if we are within a tag completion context and return builtin tag suggestions if so */ const range = new Range(new Position(pos.line, 0), pos) - const tagMatch = doc.getText(range).match(/\s*@(\w*)$/) + const text = doc.getText(range) + const tagMatch = text.match(/\s*@(\w*)$/) + const selfClosingMatch = text.match(/\s*@!(\w*)$/) if (tagMatch) { completionItems = builtinTags.filter((tag) => tag.label.toString().startsWith(tagMatch[1]!)) + } else if (selfClosingMatch) { + completionItems = builtinSelfClosingTags.filter((tag) => + tag.label.toString().startsWith(selfClosingMatch[1]!) + ) } /** @@ -39,15 +45,29 @@ export class EdgeCompletionProvider implements CompletionItemProvider { const componentAsTagRange = doc.getWordRangeAtPosition(pos, edgeComponentsAsTagsCompletionRegex) if (componentAsTagRange) { const text = doc.getText(componentAsTagRange) - const items = indexer?.searchComponent(text)?.map(({ componentName, disk }) => { - return new SuperCompletionItem({ - label: componentName!, - detail: disk, - kind: CompletionItemKind.Constructor, + indexer + ?.searchComponent(text) + ?.forEach(({ componentName, selfClosedInsertText, insertText, disk }) => { + if (selfClosingMatch) { + completionItems.push( + new SuperCompletionItem({ + label: `!${componentName!}`, + detail: disk, + insertText: selfClosedInsertText, + kind: CompletionItemKind.Function, + }) + ) + } else { + completionItems.push( + new SuperCompletionItem({ + label: componentName!, + detail: disk, + insertText, + kind: CompletionItemKind.Function, + }) + ) + } }) - }) - - completionItems.push(...(items ?? [])) } return completionItems diff --git a/src/vscode/wrappers.ts b/src/vscode/wrappers.ts index c7a9120..3671690 100644 --- a/src/vscode/wrappers.ts +++ b/src/vscode/wrappers.ts @@ -1,9 +1,10 @@ -import { CompletionItem, MarkdownString } from 'vscode' +import { CompletionItem, MarkdownString, SnippetString } from 'vscode' import type { CompletionItemKind, Range } from 'vscode' export class SuperCompletionItem extends CompletionItem { constructor(options: { label: string + insertText?: string documentation?: string detail?: string kind?: CompletionItemKind @@ -11,6 +12,9 @@ export class SuperCompletionItem extends CompletionItem { }) { super(options.label, options.kind) this.detail = options.detail + if (options.insertText) { + this.insertText = new SnippetString(options.insertText) + } this.documentation = new MarkdownString(options.documentation as string) this.range = options.range }