semicons 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/extension.js +827 -0
- package/dist/extension.js.map +7 -0
- package/esbuild.js +26 -0
- package/package.json +75 -0
- package/src/extension.ts +78 -0
- package/src/features/commands.ts +149 -0
- package/src/features/completion.ts +105 -0
- package/src/features/diagnostics.ts +105 -0
- package/src/features/hover.ts +90 -0
- package/src/registry/cache.ts +71 -0
- package/src/registry/loader.ts +26 -0
- package/src/registry/types.ts +38 -0
- package/src/registry/watcher.ts +67 -0
- package/src/utils/config.ts +17 -0
- package/src/utils/path.ts +49 -0
- package/src/utils/tokenParse.ts +79 -0
- package/src/views/previewWebview.ts +253 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/extension.ts", "../src/registry/watcher.ts", "../src/registry/cache.ts", "../src/registry/loader.ts", "../src/features/completion.ts", "../src/utils/config.ts", "../src/features/hover.ts", "../src/utils/tokenParse.ts", "../src/features/diagnostics.ts", "../src/features/commands.ts", "../src/views/previewWebview.ts", "../src/utils/path.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n window,\n workspace,\n commands,\n type ExtensionContext,\n type Disposable,\n} from 'vscode';\nimport { setupRegistryWatcher, refreshAllRegistries } from './registry/watcher';\nimport { getAllRegistries } from './registry/cache';\nimport { registerCompletionProvider } from './features/completion';\nimport { registerHoverProvider } from './features/hover';\nimport { registerDiagnosticsProvider, DiagnosticsManager } from './features/diagnostics';\nimport { registerCommands } from './features/commands';\n\nlet diagnosticsManager: DiagnosticsManager | null = null;\nconst disposables: Disposable[] = [];\n\nexport async function activate(context: ExtensionContext): Promise<void> {\n // Initial registry load\n await refreshAllRegistries();\n \n // Setup file watcher\n setupRegistryWatcher();\n \n // Register features\n disposables.push(registerCompletionProvider());\n disposables.push(registerHoverProvider());\n \n diagnosticsManager = new DiagnosticsManager();\n disposables.push(registerDiagnosticsProvider(diagnosticsManager));\n disposables.push(registerCommands());\n \n // Show welcome message\n const registries = await getAllRegistries();\n if (registries.length > 0) {\n window.setStatusBarMessage('Semicons loaded', 3000);\n } else {\n window.showWarningMessage(\n 'Semicons: No registry found. Run \"pnpm semicons generate\" to create registry.json',\n 'Generate Now'\n ).then((action) => {\n if (action === 'Generate Now') {\n commands.executeCommand('workbench.action.terminal.runSelectedText');\n }\n });\n }\n \n // Subscribe to document changes for diagnostics\n workspace.onDidChangeTextDocument((event) => {\n if (diagnosticsManager) {\n diagnosticsManager.updateDocument(event.document);\n }\n });\n \n // Subscribe to active editor changes\n window.onDidChangeActiveTextEditor((editor) => {\n if (editor && diagnosticsManager) {\n diagnosticsManager.updateDocument(editor.document);\n }\n });\n \n context.subscriptions.push({\n dispose: () => {\n for (const d of disposables) {\n d.dispose();\n }\n disposables.length = 0;\n if (diagnosticsManager) {\n diagnosticsManager.dispose();\n diagnosticsManager = null;\n }\n },\n });\n}\n\nexport function deactivate(): void {\n // Cleanup handled by subscriptions\n}\n", "import { workspace, Uri, Disposable } from 'vscode';\nimport { clearCache, setRegistry } from './cache';\nimport { loadRegistry, resolveRegistryPath } from './loader';\n\nconst watchers: Map<string, Disposable> = new Map();\n\nexport function setupRegistryWatcher(): void {\n const folders = workspace.workspaceFolders;\n if (!folders) {\n return;\n }\n \n for (const folder of folders) {\n const registryUri = resolveRegistryPath(folder.uri);\n const watcherKey = folder.uri.toString();\n \n if (watchers.has(watcherKey)) {\n continue;\n }\n \n const watcher = workspace.createFileSystemWatcher(\n registryUri.fsPath,\n false,\n false,\n false\n );\n \n watcher.onDidChange(async () => {\n const registry = await loadRegistry(folder.uri);\n setRegistry(folder.uri, registry);\n });\n \n watcher.onDidCreate(async () => {\n const registry = await loadRegistry(folder.uri);\n setRegistry(folder.uri, registry);\n });\n \n watcher.onDidDelete(() => {\n setRegistry(folder.uri, null);\n });\n \n watchers.set(watcherKey, watcher);\n }\n}\n\nexport function disposeWatchers(): void {\n for (const disposable of watchers.values()) {\n disposable.dispose();\n }\n watchers.clear();\n}\n\nexport function refreshAllRegistries(): Promise<void> {\n clearCache();\n \n const folders = workspace.workspaceFolders;\n if (!folders) {\n return Promise.resolve();\n }\n \n const promises = folders.map(async (folder) => {\n const registry = await loadRegistry(folder.uri);\n setRegistry(folder.uri, registry);\n });\n \n return Promise.all(promises).then(() => undefined);\n}\n", "import { Uri, workspace, type Uri as VSCodeUri } from 'vscode';\nimport type { Registry } from './types';\nimport { loadRegistry } from './loader';\n\ninterface RegistryCache {\n [folderUri: string]: Registry | null | Promise<Registry | null>;\n}\n\nconst cache: RegistryCache = {};\n\nexport async function getRegistryForFile(fileUri: VSCodeUri): Promise<Registry | null> {\n const folder = workspace.getWorkspaceFolder(fileUri);\n \n if (folder) {\n const folderKey = folder.uri.toString();\n \n if (!cache[folderKey]) {\n cache[folderKey] = await loadRegistry(folder.uri);\n }\n \n return cache[folderKey];\n }\n \n // Fallback: use first available registry\n const folders = workspace.workspaceFolders;\n if (folders && folders.length > 0) {\n const firstFolder = folders[0];\n const firstKey = firstFolder.uri.toString();\n \n if (!cache[firstKey]) {\n cache[firstKey] = await loadRegistry(firstFolder.uri);\n }\n \n return cache[firstKey];\n }\n \n return null;\n}\n\nexport async function getRegistryForFolder(folderUri: VSCodeUri): Promise<Registry | null> {\n const folderKey = folderUri.toString();\n\n if (!cache[folderKey]) {\n cache[folderKey] = await loadRegistry(folderUri);\n }\n\n return cache[folderKey] as Promise<Registry | null> | Registry | null;\n}\n\nexport function setRegistry(folderUri: VSCodeUri, registry: Registry | null): void {\n const folderKey = folderUri.toString();\n cache[folderKey] = registry;\n}\n\nexport function clearCache(): void {\n for (const key of Object.keys(cache)) {\n delete cache[key];\n }\n}\n\nexport async function getAllRegistries(): Promise<Registry[]> {\n const registries: Registry[] = [];\n\n for (const value of Object.values(cache)) {\n if (value && !(value instanceof Promise)) {\n registries.push(value);\n }\n }\n\n return registries;\n}\n", "import { Uri, workspace } from 'vscode';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { Registry } from './types';\n\nexport async function loadRegistry(folderUri: Uri): Promise<Registry | null> {\n const config = workspace.getConfiguration('semicons', folderUri);\n const registryPath = config.get<string>('registryPath', 'src/icons.generated/registry.json');\n \n const registryUri = Uri.joinPath(folderUri, registryPath);\n \n try {\n const document = await workspace.openTextDocument(registryUri);\n const content = document.getText();\n const registry = JSON.parse(content) as Registry;\n return registry;\n } catch (error) {\n return null;\n }\n}\n\nexport function resolveRegistryPath(folderUri: Uri): Uri {\n const config = workspace.getConfiguration('semicons', folderUri);\n const registryPath = config.get<string>('registryPath', 'src/icons.generated/registry.json');\n return Uri.joinPath(folderUri, registryPath);\n}\n", "import {\n languages,\n CompletionItem,\n CompletionItemKind,\n MarkdownString,\n Disposable,\n type TextDocument,\n type Position,\n type CancellationToken,\n type ProviderResult,\n} from 'vscode';\nimport { getRegistryForFile } from '../registry/cache';\nimport { getSemiconsConfig } from '../utils/config';\n\nexport function registerCompletionProvider(): Disposable {\n const provider = languages.registerCompletionItemProvider(\n [\n { language: 'typescriptreact' },\n { language: 'javascriptreact' },\n { language: 'vue' },\n { language: 'astro' },\n ],\n {\n async provideCompletionItems(\n document: TextDocument,\n position: Position,\n token: CancellationToken\n ): Promise<CompletionItem[]> {\n const registry = await getRegistryForFile(document.uri);\n\n if (!registry || !registry.tokens) {\n return [];\n }\n\n const config = getSemiconsConfig();\n const iconName = config.iconComponentName;\n\n // Check if we're inside an Icon component\n const line = document.lineAt(position.line).text;\n const linePrefix = line.substring(0, position.character);\n\n // Match patterns like:\n // - <Icon name=\"\n // - <Icon name={\" or <Icon name={\n // - :name=\" (Vue)\n const iconPattern = new RegExp(`<${iconName}\\\\s+[^>]*name\\\\s*=\\\\s*[\"']`, 'g');\n const vuePattern = new RegExp(`:${iconName}\\\\s*:\\\\s*name\\\\s*=\\\\s*[\"']`, 'g');\n\n const isInIconName = iconPattern.test(linePrefix) || vuePattern.test(linePrefix);\n\n if (!isInIconName) {\n return [];\n }\n\n const items: CompletionItem[] = [];\n\n for (const token of registry.tokens) {\n const deprecated = token.meta.deprecated;\n const category = token.name.split(':')[0];\n\n const item = new CompletionItem(\n token.name,\n CompletionItemKind.EnumMember\n );\n\n // Detail shows category and deprecated status\n let detail = category;\n if (deprecated) {\n detail += ' (deprecated)';\n }\n item.detail = detail;\n\n // Documentation\n const docLines: string[] = [];\n\n if (token.a11y?.label) {\n docLines.push(`a11y: \"${token.a11y.label}\"`);\n }\n if (token.meta.description) {\n docLines.push(token.meta.description);\n }\n if (deprecated) {\n docLines.push(`\u26A0\uFE0F Deprecated: ${typeof deprecated === 'string' ? deprecated : 'This icon is deprecated'}`);\n }\n\n const doc = new MarkdownString();\n doc.value = docLines.join('\\n\\n');\n item.documentation = doc;\n\n // Insert text\n item.insertText = token.name;\n item.range = undefined; // Let VS Code auto-detect range\n\n items.push(item);\n }\n\n return items;\n },\n },\n '\"', // Trigger on quote\n \"'\" // Trigger on single quote\n );\n\n return provider;\n}\n", "import { Uri, workspace } from 'vscode';\n\nexport interface SemiconsConfig {\n registryPath: string;\n localIconDir: string;\n iconComponentName: string;\n}\n\nexport function getSemiconsConfig(folder?: Uri): SemiconsConfig {\n const config = workspace.getConfiguration('semicons', folder);\n \n return {\n registryPath: config.get<string>('registryPath', 'src/icons.generated/registry.json'),\n localIconDir: config.get<string>('localIconDir', 'icons/local'),\n iconComponentName: config.get<string>('iconComponentName', 'Icon'),\n };\n}\n", "import {\n languages,\n MarkdownString,\n window,\n Hover,\n Disposable,\n type TextDocument,\n type Position,\n type CancellationToken,\n type ProviderResult,\n} from 'vscode';\nimport { getRegistryForFile } from '../registry/cache';\nimport { getSemiconsConfig } from '../utils/config';\nimport { extractTokenNameAtPosition } from '../utils/tokenParse';\n\nexport function registerHoverProvider(): Disposable {\n const provider = languages.registerHoverProvider(\n [\n { language: 'typescriptreact' },\n { language: 'javascriptreact' },\n { language: 'vue' },\n { language: 'astro' },\n ],\n {\n async provideHover(\n document: TextDocument,\n position: Position,\n cancellationToken: CancellationToken\n ): Promise<Hover | null | undefined> {\n const registry = await getRegistryForFile(document.uri);\n\n if (!registry || !registry.tokens) {\n return null;\n }\n\n const tokenName = extractTokenNameAtPosition(document, position);\n\n if (!tokenName) {\n return null;\n }\n\n const foundToken = registry.tokens.find((t: { name: string }) => t.name === tokenName);\n\n if (!foundToken) {\n return null;\n }\n\n const markdown = new MarkdownString();\n markdown.isTrusted = true;\n\n const deprecated = foundToken.meta.deprecated;\n const theme = registry.defaultTheme;\n const assetRef = foundToken.themes[theme] || Object.values(foundToken.themes)[0];\n\n // Build hover content\n const lines: string[] = [];\n\n // Token name with deprecated marker\n if (deprecated) {\n lines.push(`~~${foundToken.name}~~ \u26A0\uFE0F`);\n } else {\n lines.push(`**${foundToken.name}**`);\n }\n\n // Asset ref\n lines.push(`\\`${assetRef}\\``);\n\n // A11y label\n if (foundToken.a11y?.label) {\n lines.push(`a11y: \"${foundToken.a11y.label}\"`);\n }\n\n // Tags\n if (foundToken.meta.tags && foundToken.meta.tags.length > 0) {\n lines.push(`tags: ${foundToken.meta.tags.join(', ')}`);\n }\n\n // Preview command\n const previewCommand = `[Preview icon](command:semicons.previewIcon?${encodeURIComponent(JSON.stringify([foundToken.name]))})`;\n lines.push('', previewCommand);\n\n markdown.value = lines.join('\\n\\n');\n\n return new Hover(markdown);\n },\n }\n );\n\n return provider;\n}\n", "import { Position, Range, type TextDocument } from 'vscode';\n\nexport interface TokenMatch {\n name: string;\n range: Range;\n isAttributeValue: boolean;\n}\n\nconst ICON_TAG_REGEX = /<Icon\\b/gi;\nconst NAME_ATTR_REGEX = /\\s+name\\s*=\\s*[\"']([^\"']+)[\"']/g;\nconst VUE_NAME_ATTR_REGEX = /\\s+:name\\s*=\\s*[\"']([^\"']+)[\"']/g;\n\nexport function findIconTokens(document: TextDocument): TokenMatch[] {\n const text = document.getText();\n const matches: TokenMatch[] = [];\n \n let match;\n \n // Match <Icon name=\"...\"> patterns\n while ((match = ICON_TAG_REGEX.exec(text)) !== null) {\n const tagStart = match.index;\n const tagEnd = tagStart + match[0].length;\n \n // Look for name attribute after the tag\n const remainingText = text.substring(tagEnd);\n \n // Check for name=\"value\"\n let nameMatch;\n const nameRegex = new RegExp(NAME_ATTR_REGEX);\n while ((nameMatch = nameRegex.exec(remainingText)) !== null) {\n const nameStart = tagEnd + nameMatch.index;\n const nameEnd = nameStart + nameMatch[0].length;\n const nameValue = nameMatch[1];\n \n const startPos = document.positionAt(nameStart);\n const endPos = document.positionAt(nameEnd);\n \n matches.push({\n name: nameValue,\n range: new Range(startPos, endPos),\n isAttributeValue: true,\n });\n }\n \n // Check for Vue :name=\"'value'\"\n const vueNameRegex = new RegExp(VUE_NAME_ATTR_REGEX);\n while ((nameMatch = vueNameRegex.exec(remainingText)) !== null) {\n const nameStart = tagEnd + nameMatch.index;\n const nameEnd = nameStart + nameMatch[0].length;\n const nameValue = nameMatch[1];\n \n const startPos = document.positionAt(nameStart);\n const endPos = document.positionAt(nameEnd);\n \n matches.push({\n name: nameValue,\n range: new Range(startPos, endPos),\n isAttributeValue: true,\n });\n }\n }\n \n return matches;\n}\n\nexport function extractTokenNameAtPosition(\n document: TextDocument,\n position: Position\n): string | null {\n const matches = findIconTokens(document);\n \n for (const match of matches) {\n if (match.range.contains(position)) {\n return match.name;\n }\n }\n \n return null;\n}\n", "import {\n languages,\n Diagnostic,\n DiagnosticSeverity,\n Disposable,\n TextDocument,\n workspace,\n} from 'vscode';\nimport { getRegistryForFile } from '../registry/cache';\nimport { findIconTokens, type TokenMatch } from '../utils/tokenParse';\n\nconst TOKEN_PATTERN = /^[a-z][a-z0-9-]*:[a-zA-Z0-9][a-zA-Z0-9._/-]*$/;\n\nexport class DiagnosticsManager {\n private collection: ReturnType<typeof languages.createDiagnosticCollection>;\n private disposables: Disposable[] = [];\n\n constructor() {\n this.collection = languages.createDiagnosticCollection('semicons');\n }\n\n async updateDocument(document: TextDocument): Promise<void> {\n const registry = await getRegistryForFile(document.uri);\n\n const diagnostics: Diagnostic[] = [];\n const matches = findIconTokens(document);\n\n for (const match of matches) {\n const { name, range } = match;\n\n // Check if matches token pattern\n if (!TOKEN_PATTERN.test(name)) {\n diagnostics.push(new Diagnostic(\n range,\n `Invalid token format. Expected \"category:name\" (e.g., \"navigation:menu\")`,\n DiagnosticSeverity.Error\n ));\n continue;\n }\n\n // Check if token exists in registry\n if (!registry || !registry.tokens) {\n continue;\n }\n\n const token = registry.tokens.find((t: { name: string }) => t.name === name);\n\n if (!token) {\n diagnostics.push(new Diagnostic(\n range,\n `Icon token \"${name}\" not found in registry`,\n DiagnosticSeverity.Error\n ));\n } else if (token.meta.deprecated) {\n const deprecationMessage = typeof token.meta.deprecated === 'string'\n ? `Deprecated: ${token.meta.deprecated}`\n : 'This icon is deprecated';\n\n diagnostics.push(new Diagnostic(\n range,\n `\u26A0\uFE0F ${deprecationMessage}`,\n DiagnosticSeverity.Warning\n ));\n }\n }\n\n this.collection.set(document.uri, diagnostics);\n }\n\n clear(): void {\n this.collection.clear();\n }\n\n dispose(): void {\n this.collection.dispose();\n for (const d of this.disposables) {\n d.dispose();\n }\n }\n}\n\nexport function registerDiagnosticsProvider(\n manager: DiagnosticsManager\n): Disposable {\n const disposables: Disposable[] = [];\n\n const languagesToRegister = [\n 'typescriptreact',\n 'javascriptreact',\n 'vue',\n 'astro',\n ];\n\n for (const language of languagesToRegister) {\n const pattern = `**/*.${language === 'typescriptreact' || language === 'javascriptreact' ? 'tsx' : language === 'vue' ? 'vue' : 'astro'}`;\n const watcher = workspace.createFileSystemWatcher(pattern);\n watcher.onDidChange(async (uri) => {\n const document = await workspace.openTextDocument(uri);\n await manager.updateDocument(document);\n });\n disposables.push(watcher);\n }\n\n return Disposable.from(...disposables);\n}\n", "import {\n commands,\n window,\n workspace,\n Uri,\n QuickPickItem,\n Disposable,\n} from 'vscode';\nimport { getRegistryForFile, getAllRegistries } from '../registry/cache';\nimport { refreshAllRegistries } from '../registry/watcher';\nimport { PreviewWebview } from '../views/previewWebview';\n\nconst previewWebview = new PreviewWebview();\n\nexport function registerCommands(): Disposable {\n const disposables: Disposable[] = [];\n \n // Refresh Registry\n disposables.push(commands.registerCommand('semicons.refreshRegistry', async () => {\n await refreshAllRegistries();\n window.showInformationMessage('Semicons registry refreshed');\n }));\n \n // Search Token (QuickPick)\n disposables.push(commands.registerCommand('semicons.searchToken', async () => {\n const registries = await getAllRegistries();\n if (registries.length === 0) {\n window.showWarningMessage('No registry found. Run pnpm semicons generate first.');\n return;\n }\n \n // Collect all tokens from all registries\n const tokens: Array<{ name: string; registry: typeof registries[0]; folder?: Uri }> = [];\n \n for (const registry of registries) {\n if (registry && registry.tokens) {\n for (const token of registry.tokens) {\n tokens.push({ name: token.name, registry });\n }\n }\n }\n \n // Create QuickPick items\n const items: QuickPickItem[] = tokens.map((t) => ({\n label: t.name,\n detail: t.name.split(':')[0],\n description: t.registry.tokens.find((tok: { name: string }) => tok.name === t.name)?.meta.deprecated ? '(deprecated)' : undefined,\n }));\n \n // Show QuickPick\n const selected = await window.showQuickPick(items, {\n placeHolder: 'Search for an icon token...',\n matchOnDescription: true,\n matchOnDetail: true,\n });\n \n if (!selected) {\n return;\n }\n \n const tokenName = selected.label;\n \n // Find the registry and folder for this token\n let foundRegistry = null;\n let foundFolder: Uri | undefined;\n \n for (const folder of workspace.workspaceFolders || []) {\n const registry = await getRegistryForFile(folder.uri);\n if (registry?.tokens.some((t: { name: string }) => t.name === tokenName)) {\n foundRegistry = registry;\n foundFolder = folder.uri;\n break;\n }\n }\n \n if (!foundRegistry || !foundFolder) {\n window.showErrorMessage('Token registry not found');\n return;\n }\n \n const token = foundRegistry.tokens.find((t: { name: string }) => t.name === tokenName);\n if (!token) {\n return;\n }\n \n // Insert into active editor\n const editor = window.activeTextEditor;\n if (editor) {\n const insertText = `name=\"${tokenName}\"`;\n editor.edit((editBuilder) => {\n editBuilder.insert(editor.selection.active, insertText);\n });\n } else {\n // Copy to clipboard as fallback\n await commands.executeCommand('editor.action.clipboardCopyAction');\n window.showInformationMessage(`Token \"${tokenName}\" copied to clipboard`);\n }\n }));\n \n // Preview Icon\n disposables.push(commands.registerCommand('semicons.previewIcon', async (tokenName: string) => {\n if (!tokenName) {\n const editor = window.activeTextEditor;\n if (editor) {\n const token = await getTokenAtCursor(editor.document, editor.selection.active);\n if (token) {\n tokenName = token;\n }\n }\n }\n \n if (!tokenName) {\n window.showWarningMessage('No token found at cursor');\n return;\n }\n \n // Find the token in registries\n const registries = await getAllRegistries();\n\n for (const registry of registries) {\n if (registry?.tokens) {\n const token = registry.tokens.find((t: { name: string }) => t.name === tokenName);\n if (token) {\n // Find the folder\n const folder = workspace.workspaceFolders?.find(async (f) => {\n const allRegs = await getAllRegistries();\n const reg = allRegs.find((r: any) => r === registry);\n return reg !== undefined;\n });\n \n if (folder) {\n await previewWebview.show(token, registry, folder.uri);\n return;\n }\n }\n }\n }\n \n window.showErrorMessage(`Token \"${tokenName}\" not found in registry`);\n }));\n \n return Disposable.from(...disposables);\n}\n\nasync function getTokenAtCursor(document: { getText: (range?: any) => string }, position: any): Promise<string | null> {\n const line = document.getText().split('\\n')[position.line];\n const match = line.match(/name\\s*=\\s*[\"']([^\"']+)[\"']/);\n return match ? match[1] : null;\n}\n", "import { window, ViewColumn, Uri, type WebviewPanel } from 'vscode';\nimport type { Token, Registry } from '../registry/types';\nimport { resolveAssetToFileUri, readSvgContent } from '../utils/path';\n\ninterface PreviewState {\n token: Token;\n registry: Registry;\n folderUri: Uri;\n}\n\nexport class PreviewWebview {\n private panel: WebviewPanel | null = null;\n private state: PreviewState | null = null;\n\n async show(token: Token, registry: Registry, folderUri: Uri): Promise<void> {\n this.state = { token, registry, folderUri };\n \n if (this.panel) {\n this.panel.reveal(ViewColumn.Beside);\n this.updatePanel();\n } else {\n this.panel = window.createWebviewPanel(\n 'semiconsPreview',\n `Preview: ${token.name}`,\n ViewColumn.Beside,\n {\n enableScripts: true,\n localResourceRoots: [folderUri],\n }\n );\n \n this.panel.onDidDispose(() => {\n this.panel = null;\n this.state = null;\n });\n \n this.updatePanel();\n }\n }\n\n private async updatePanel(): Promise<void> {\n if (!this.panel || !this.state) {\n return;\n }\n\n const { token, registry, folderUri } = this.state;\n const theme = registry.defaultTheme;\n const assetRef = token.themes[theme] || Object.values(token.themes)[0];\n \n // Try to get real SVG content\n const fileUri = await resolveAssetToFileUri(assetRef, folderUri);\n let svgContent: string | null = null;\n let svgExists = false;\n \n if (fileUri) {\n svgContent = await readSvgContent(fileUri);\n svgExists = svgContent !== null;\n }\n \n // Build preview HTML\n const previewHtml = svgExists && svgContent\n ? this.wrapSvg(svgContent)\n : this.createPlaceholder(token.name, assetRef, svgExists);\n \n const html = this.buildHtml(token, registry, assetRef, previewHtml);\n \n this.panel.webview.html = html;\n }\n\n private wrapSvg(svg: string): string {\n // Remove existing width/height/viewBox for consistent sizing\n return svg\n .replace(/\\s+width=\"[^\"]*\"/g, '')\n .replace(/\\s+height=\"[^\"]*\"/g, '')\n .replace(/\\s+viewBox=\"[^\"]*\"/g, '')\n .replace(/^<svg/, '<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 24 24\"');\n }\n\n private createPlaceholder(name: string, assetRef: string, fileExists: boolean): string {\n return `\n <div class=\"placeholder\">\n <div class=\"placeholder-icon\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n <line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/>\n </svg>\n </div>\n <p class=\"placeholder-text\">${fileExists ? 'Preview unavailable' : 'File not found'}</p>\n <p class=\"placeholder-ref\">${assetRef}</p>\n ${!fileExists ? `<p class=\"placeholder-hint\">Expected: ${name.replace('local:', 'icons/local/') + '.svg'}</p>` : ''}\n </div>\n `;\n }\n\n private buildHtml(token: Token, registry: Registry, assetRef: string, previewHtml: string): string {\n const deprecated = token.meta.deprecated;\n \n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${token.name}</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n margin: 0;\n padding: 16px;\n background: #1e1e2e;\n color: #cdd6f4;\n }\n .header {\n margin-bottom: 16px;\n }\n .token-name {\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n }\n .token-name.deprecated {\n text-decoration: line-through;\n opacity: 0.7;\n }\n .token-ref {\n font-family: 'Fira Code', monospace;\n font-size: 12px;\n color: #a6adc8;\n background: #313244;\n padding: 2px 8px;\n border-radius: 4px;\n }\n .section {\n margin-bottom: 16px;\n }\n .section-title {\n font-size: 12px;\n text-transform: uppercase;\n color: #6c7086;\n margin-bottom: 8px;\n }\n .preview-container {\n background: #181825;\n border-radius: 8px;\n padding: 24px;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 120px;\n }\n .preview-container svg {\n max-width: 100%;\n max-height: 100px;\n }\n .placeholder {\n text-align: center;\n padding: 24px;\n }\n .placeholder-icon {\n opacity: 0.3;\n margin-bottom: 8px;\n }\n .placeholder-text {\n font-size: 14px;\n color: #a6adc8;\n margin: 0 0 4px 0;\n }\n .placeholder-ref {\n font-family: 'Fira Code', monospace;\n font-size: 11px;\n color: #6c7086;\n margin: 0 0 8px 0;\n }\n .placeholder-hint {\n font-size: 11px;\n color: #f38ba8;\n margin: 0;\n }\n .meta-row {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n .tag {\n font-size: 11px;\n background: #313244;\n padding: 2px 8px;\n border-radius: 4px;\n }\n .warning {\n background: #f9e2af;\n color: #1e1e2e;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n margin-top: 8px;\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1 class=\"token-name ${deprecated ? 'deprecated' : ''}\">${token.name}</h1>\n <span class=\"token-ref\">${assetRef}</span>\n </div>\n \n ${deprecated ? `<div class=\"warning\">\u26A0\uFE0F Deprecated: ${typeof deprecated === 'string' ? deprecated : 'This icon is deprecated'}</div>` : ''}\n \n <div class=\"section\">\n <div class=\"section-title\">Preview</div>\n <div class=\"preview-container\">\n ${previewHtml}\n </div>\n </div>\n \n ${token.a11y?.label ? `\n <div class=\"section\">\n <div class=\"section-title\">Accessibility</div>\n <div>aria-label: \"${token.a11y.label}\"</div>\n </div>\n ` : ''}\n \n ${token.meta.description ? `\n <div class=\"section\">\n <div class=\"section-title\">Description</div>\n <div>${token.meta.description}</div>\n </div>\n ` : ''}\n \n ${token.meta.tags && token.meta.tags.length > 0 ? `\n <div class=\"section\">\n <div class=\"section-title\">Tags</div>\n <div class=\"meta-row\">\n ${token.meta.tags.map(tag => `<span class=\"tag\">${tag}</span>`).join('')}\n </div>\n </div>\n ` : ''}\n \n <div class=\"section\">\n <div class=\"section-title\">Usage</div>\n <pre style=\"background: #313244; padding: 12px; border-radius: 6px; overflow-x: auto;\"><code><Icon name=\"${token.name}\" /></code></pre>\n </div>\n</body>\n</html>`;\n }\n\n dispose(): void {\n if (this.panel) {\n this.panel.dispose();\n this.panel = null;\n }\n this.state = null;\n }\n}\n", "import { Uri, workspace } from 'vscode';\nimport type { AssetRef } from '../registry/types';\nimport { getSemiconsConfig } from './config';\n\nexport function parseAssetRef(assetRef: AssetRef): { namespace: string; id: string } | null {\n const match = assetRef.match(/^([^:]+):(.+)$/);\n if (!match) {\n return null;\n }\n return { namespace: match[1], id: match[2] };\n}\n\nexport async function resolveAssetToFileUri(\n assetRef: AssetRef,\n folder: Uri\n): Promise<Uri | null> {\n const parsed = parseAssetRef(assetRef);\n if (!parsed) {\n return null;\n }\n \n const { namespace, id } = parsed;\n \n if (namespace === 'local') {\n const config = getSemiconsConfig(folder);\n const filePath = `${config.localIconDir}/${id}.svg`;\n const fileUri = Uri.joinPath(folder, filePath);\n \n try {\n // Check if file exists\n await workspace.fs.stat(fileUri);\n return fileUri;\n } catch {\n return null;\n }\n }\n \n // For other namespaces (lucide, etc.), we can't resolve to local file\n return null;\n}\n\nexport async function readSvgContent(fileUri: Uri): Promise<string | null> {\n try {\n const bytes = await workspace.fs.readFile(fileUri);\n return new TextDecoder().decode(bytes);\n } catch {\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_vscode", "import_vscode", "import_vscode", "import_vscode", "import_vscode", "token", "import_vscode", "import_vscode", "import_vscode", "disposables", "import_vscode", "import_vscode", "import_vscode", "disposables", "token"]
|
|
7
|
+
}
|
package/esbuild.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const esbuild = require('esbuild');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
async function build() {
|
|
5
|
+
const production = process.argv.includes('--production');
|
|
6
|
+
|
|
7
|
+
await esbuild.build({
|
|
8
|
+
entryPoints: [path.resolve(__dirname, 'src/extension.ts')],
|
|
9
|
+
bundle: true,
|
|
10
|
+
outfile: path.resolve(__dirname, 'dist/extension.js'),
|
|
11
|
+
platform: 'node',
|
|
12
|
+
target: 'node20',
|
|
13
|
+
format: 'cjs',
|
|
14
|
+
minify: production,
|
|
15
|
+
sourcemap: !production,
|
|
16
|
+
external: ['vscode'],
|
|
17
|
+
logLevel: 'info',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log('Build completed:', production ? 'production' : 'development');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
build().catch((err) => {
|
|
24
|
+
console.error(err);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "semicons",
|
|
3
|
+
"displayName": "Semicons",
|
|
4
|
+
"description": "Semantic icon token system for VS Code - completion, hover, diagnostics, and preview",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"publisher": "semicons",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.90.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": [
|
|
11
|
+
"Programming Languages",
|
|
12
|
+
"Other"
|
|
13
|
+
],
|
|
14
|
+
"activationEvents": [
|
|
15
|
+
"onLanguage:typescriptreact",
|
|
16
|
+
"onLanguage:javascriptreact",
|
|
17
|
+
"onLanguage:vue",
|
|
18
|
+
"onLanguage:astro",
|
|
19
|
+
"onCommand:semicons.refreshRegistry",
|
|
20
|
+
"onCommand:semicons.searchToken",
|
|
21
|
+
"onCommand:semicons.previewIcon",
|
|
22
|
+
"workspaceContains:**/src/icons.generated/registry.json"
|
|
23
|
+
],
|
|
24
|
+
"main": "./dist/extension.js",
|
|
25
|
+
"contributes": {
|
|
26
|
+
"commands": [
|
|
27
|
+
{
|
|
28
|
+
"command": "semicons.refreshRegistry",
|
|
29
|
+
"title": "Semicons: Refresh Registry",
|
|
30
|
+
"category": "Semicons"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"command": "semicons.searchToken",
|
|
34
|
+
"title": "Semicons: Search Token",
|
|
35
|
+
"category": "Semicons"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"command": "semicons.previewIcon",
|
|
39
|
+
"title": "Semicons: Preview Icon",
|
|
40
|
+
"category": "Semicons"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"configuration": [
|
|
44
|
+
{
|
|
45
|
+
"title": "Semicons",
|
|
46
|
+
"properties": {
|
|
47
|
+
"semicons.registryPath": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"default": "src/icons.generated/registry.json",
|
|
50
|
+
"description": "Path to registry.json relative to workspace folder"
|
|
51
|
+
},
|
|
52
|
+
"semicons.localIconDir": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"default": "icons/local",
|
|
55
|
+
"description": "Directory for local icons (relative to workspace root)"
|
|
56
|
+
},
|
|
57
|
+
"semicons.iconComponentName": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"default": "Icon",
|
|
60
|
+
"description": "Icon component name to match in code"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@types/vscode": "^1.90.0",
|
|
68
|
+
"esbuild": "^0.20.0",
|
|
69
|
+
"typescript": "^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "node esbuild.js",
|
|
73
|
+
"watch": "node esbuild.js --watch"
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/extension.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
window,
|
|
3
|
+
workspace,
|
|
4
|
+
commands,
|
|
5
|
+
type ExtensionContext,
|
|
6
|
+
type Disposable,
|
|
7
|
+
} from 'vscode';
|
|
8
|
+
import { setupRegistryWatcher, refreshAllRegistries } from './registry/watcher';
|
|
9
|
+
import { getAllRegistries } from './registry/cache';
|
|
10
|
+
import { registerCompletionProvider } from './features/completion';
|
|
11
|
+
import { registerHoverProvider } from './features/hover';
|
|
12
|
+
import { registerDiagnosticsProvider, DiagnosticsManager } from './features/diagnostics';
|
|
13
|
+
import { registerCommands } from './features/commands';
|
|
14
|
+
|
|
15
|
+
let diagnosticsManager: DiagnosticsManager | null = null;
|
|
16
|
+
const disposables: Disposable[] = [];
|
|
17
|
+
|
|
18
|
+
export async function activate(context: ExtensionContext): Promise<void> {
|
|
19
|
+
// Initial registry load
|
|
20
|
+
await refreshAllRegistries();
|
|
21
|
+
|
|
22
|
+
// Setup file watcher
|
|
23
|
+
setupRegistryWatcher();
|
|
24
|
+
|
|
25
|
+
// Register features
|
|
26
|
+
disposables.push(registerCompletionProvider());
|
|
27
|
+
disposables.push(registerHoverProvider());
|
|
28
|
+
|
|
29
|
+
diagnosticsManager = new DiagnosticsManager();
|
|
30
|
+
disposables.push(registerDiagnosticsProvider(diagnosticsManager));
|
|
31
|
+
disposables.push(registerCommands());
|
|
32
|
+
|
|
33
|
+
// Show welcome message
|
|
34
|
+
const registries = await getAllRegistries();
|
|
35
|
+
if (registries.length > 0) {
|
|
36
|
+
window.setStatusBarMessage('Semicons loaded', 3000);
|
|
37
|
+
} else {
|
|
38
|
+
window.showWarningMessage(
|
|
39
|
+
'Semicons: No registry found. Run "pnpm semicons generate" to create registry.json',
|
|
40
|
+
'Generate Now'
|
|
41
|
+
).then((action) => {
|
|
42
|
+
if (action === 'Generate Now') {
|
|
43
|
+
commands.executeCommand('workbench.action.terminal.runSelectedText');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Subscribe to document changes for diagnostics
|
|
49
|
+
workspace.onDidChangeTextDocument((event) => {
|
|
50
|
+
if (diagnosticsManager) {
|
|
51
|
+
diagnosticsManager.updateDocument(event.document);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Subscribe to active editor changes
|
|
56
|
+
window.onDidChangeActiveTextEditor((editor) => {
|
|
57
|
+
if (editor && diagnosticsManager) {
|
|
58
|
+
diagnosticsManager.updateDocument(editor.document);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
context.subscriptions.push({
|
|
63
|
+
dispose: () => {
|
|
64
|
+
for (const d of disposables) {
|
|
65
|
+
d.dispose();
|
|
66
|
+
}
|
|
67
|
+
disposables.length = 0;
|
|
68
|
+
if (diagnosticsManager) {
|
|
69
|
+
diagnosticsManager.dispose();
|
|
70
|
+
diagnosticsManager = null;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function deactivate(): void {
|
|
77
|
+
// Cleanup handled by subscriptions
|
|
78
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
commands,
|
|
3
|
+
window,
|
|
4
|
+
workspace,
|
|
5
|
+
Uri,
|
|
6
|
+
QuickPickItem,
|
|
7
|
+
Disposable,
|
|
8
|
+
} from 'vscode';
|
|
9
|
+
import { getRegistryForFile, getAllRegistries } from '../registry/cache';
|
|
10
|
+
import { refreshAllRegistries } from '../registry/watcher';
|
|
11
|
+
import { PreviewWebview } from '../views/previewWebview';
|
|
12
|
+
|
|
13
|
+
const previewWebview = new PreviewWebview();
|
|
14
|
+
|
|
15
|
+
export function registerCommands(): Disposable {
|
|
16
|
+
const disposables: Disposable[] = [];
|
|
17
|
+
|
|
18
|
+
// Refresh Registry
|
|
19
|
+
disposables.push(commands.registerCommand('semicons.refreshRegistry', async () => {
|
|
20
|
+
await refreshAllRegistries();
|
|
21
|
+
window.showInformationMessage('Semicons registry refreshed');
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Search Token (QuickPick)
|
|
25
|
+
disposables.push(commands.registerCommand('semicons.searchToken', async () => {
|
|
26
|
+
const registries = await getAllRegistries();
|
|
27
|
+
if (registries.length === 0) {
|
|
28
|
+
window.showWarningMessage('No registry found. Run pnpm semicons generate first.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Collect all tokens from all registries
|
|
33
|
+
const tokens: Array<{ name: string; registry: typeof registries[0]; folder?: Uri }> = [];
|
|
34
|
+
|
|
35
|
+
for (const registry of registries) {
|
|
36
|
+
if (registry && registry.tokens) {
|
|
37
|
+
for (const token of registry.tokens) {
|
|
38
|
+
tokens.push({ name: token.name, registry });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create QuickPick items
|
|
44
|
+
const items: QuickPickItem[] = tokens.map((t) => ({
|
|
45
|
+
label: t.name,
|
|
46
|
+
detail: t.name.split(':')[0],
|
|
47
|
+
description: t.registry.tokens.find((tok: { name: string }) => tok.name === t.name)?.meta.deprecated ? '(deprecated)' : undefined,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
// Show QuickPick
|
|
51
|
+
const selected = await window.showQuickPick(items, {
|
|
52
|
+
placeHolder: 'Search for an icon token...',
|
|
53
|
+
matchOnDescription: true,
|
|
54
|
+
matchOnDetail: true,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!selected) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tokenName = selected.label;
|
|
62
|
+
|
|
63
|
+
// Find the registry and folder for this token
|
|
64
|
+
let foundRegistry = null;
|
|
65
|
+
let foundFolder: Uri | undefined;
|
|
66
|
+
|
|
67
|
+
for (const folder of workspace.workspaceFolders || []) {
|
|
68
|
+
const registry = await getRegistryForFile(folder.uri);
|
|
69
|
+
if (registry?.tokens.some((t: { name: string }) => t.name === tokenName)) {
|
|
70
|
+
foundRegistry = registry;
|
|
71
|
+
foundFolder = folder.uri;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!foundRegistry || !foundFolder) {
|
|
77
|
+
window.showErrorMessage('Token registry not found');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const token = foundRegistry.tokens.find((t: { name: string }) => t.name === tokenName);
|
|
82
|
+
if (!token) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Insert into active editor
|
|
87
|
+
const editor = window.activeTextEditor;
|
|
88
|
+
if (editor) {
|
|
89
|
+
const insertText = `name="${tokenName}"`;
|
|
90
|
+
editor.edit((editBuilder) => {
|
|
91
|
+
editBuilder.insert(editor.selection.active, insertText);
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
// Copy to clipboard as fallback
|
|
95
|
+
await commands.executeCommand('editor.action.clipboardCopyAction');
|
|
96
|
+
window.showInformationMessage(`Token "${tokenName}" copied to clipboard`);
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
// Preview Icon
|
|
101
|
+
disposables.push(commands.registerCommand('semicons.previewIcon', async (tokenName: string) => {
|
|
102
|
+
if (!tokenName) {
|
|
103
|
+
const editor = window.activeTextEditor;
|
|
104
|
+
if (editor) {
|
|
105
|
+
const token = await getTokenAtCursor(editor.document, editor.selection.active);
|
|
106
|
+
if (token) {
|
|
107
|
+
tokenName = token;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!tokenName) {
|
|
113
|
+
window.showWarningMessage('No token found at cursor');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Find the token in registries
|
|
118
|
+
const registries = await getAllRegistries();
|
|
119
|
+
|
|
120
|
+
for (const registry of registries) {
|
|
121
|
+
if (registry?.tokens) {
|
|
122
|
+
const token = registry.tokens.find((t: { name: string }) => t.name === tokenName);
|
|
123
|
+
if (token) {
|
|
124
|
+
// Find the folder
|
|
125
|
+
const folder = workspace.workspaceFolders?.find(async (f) => {
|
|
126
|
+
const allRegs = await getAllRegistries();
|
|
127
|
+
const reg = allRegs.find((r: any) => r === registry);
|
|
128
|
+
return reg !== undefined;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (folder) {
|
|
132
|
+
await previewWebview.show(token, registry, folder.uri);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
window.showErrorMessage(`Token "${tokenName}" not found in registry`);
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
return Disposable.from(...disposables);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function getTokenAtCursor(document: { getText: (range?: any) => string }, position: any): Promise<string | null> {
|
|
146
|
+
const line = document.getText().split('\n')[position.line];
|
|
147
|
+
const match = line.match(/name\s*=\s*["']([^"']+)["']/);
|
|
148
|
+
return match ? match[1] : null;
|
|
149
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
languages,
|
|
3
|
+
CompletionItem,
|
|
4
|
+
CompletionItemKind,
|
|
5
|
+
MarkdownString,
|
|
6
|
+
Disposable,
|
|
7
|
+
type TextDocument,
|
|
8
|
+
type Position,
|
|
9
|
+
type CancellationToken,
|
|
10
|
+
type ProviderResult,
|
|
11
|
+
} from 'vscode';
|
|
12
|
+
import { getRegistryForFile } from '../registry/cache';
|
|
13
|
+
import { getSemiconsConfig } from '../utils/config';
|
|
14
|
+
|
|
15
|
+
export function registerCompletionProvider(): Disposable {
|
|
16
|
+
const provider = languages.registerCompletionItemProvider(
|
|
17
|
+
[
|
|
18
|
+
{ language: 'typescriptreact' },
|
|
19
|
+
{ language: 'javascriptreact' },
|
|
20
|
+
{ language: 'vue' },
|
|
21
|
+
{ language: 'astro' },
|
|
22
|
+
],
|
|
23
|
+
{
|
|
24
|
+
async provideCompletionItems(
|
|
25
|
+
document: TextDocument,
|
|
26
|
+
position: Position,
|
|
27
|
+
token: CancellationToken
|
|
28
|
+
): Promise<CompletionItem[]> {
|
|
29
|
+
const registry = await getRegistryForFile(document.uri);
|
|
30
|
+
|
|
31
|
+
if (!registry || !registry.tokens) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const config = getSemiconsConfig();
|
|
36
|
+
const iconName = config.iconComponentName;
|
|
37
|
+
|
|
38
|
+
// Check if we're inside an Icon component
|
|
39
|
+
const line = document.lineAt(position.line).text;
|
|
40
|
+
const linePrefix = line.substring(0, position.character);
|
|
41
|
+
|
|
42
|
+
// Match patterns like:
|
|
43
|
+
// - <Icon name="
|
|
44
|
+
// - <Icon name={" or <Icon name={
|
|
45
|
+
// - :name=" (Vue)
|
|
46
|
+
const iconPattern = new RegExp(`<${iconName}\\s+[^>]*name\\s*=\\s*["']`, 'g');
|
|
47
|
+
const vuePattern = new RegExp(`:${iconName}\\s*:\\s*name\\s*=\\s*["']`, 'g');
|
|
48
|
+
|
|
49
|
+
const isInIconName = iconPattern.test(linePrefix) || vuePattern.test(linePrefix);
|
|
50
|
+
|
|
51
|
+
if (!isInIconName) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const items: CompletionItem[] = [];
|
|
56
|
+
|
|
57
|
+
for (const token of registry.tokens) {
|
|
58
|
+
const deprecated = token.meta.deprecated;
|
|
59
|
+
const category = token.name.split(':')[0];
|
|
60
|
+
|
|
61
|
+
const item = new CompletionItem(
|
|
62
|
+
token.name,
|
|
63
|
+
CompletionItemKind.EnumMember
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Detail shows category and deprecated status
|
|
67
|
+
let detail = category;
|
|
68
|
+
if (deprecated) {
|
|
69
|
+
detail += ' (deprecated)';
|
|
70
|
+
}
|
|
71
|
+
item.detail = detail;
|
|
72
|
+
|
|
73
|
+
// Documentation
|
|
74
|
+
const docLines: string[] = [];
|
|
75
|
+
|
|
76
|
+
if (token.a11y?.label) {
|
|
77
|
+
docLines.push(`a11y: "${token.a11y.label}"`);
|
|
78
|
+
}
|
|
79
|
+
if (token.meta.description) {
|
|
80
|
+
docLines.push(token.meta.description);
|
|
81
|
+
}
|
|
82
|
+
if (deprecated) {
|
|
83
|
+
docLines.push(`⚠️ Deprecated: ${typeof deprecated === 'string' ? deprecated : 'This icon is deprecated'}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const doc = new MarkdownString();
|
|
87
|
+
doc.value = docLines.join('\n\n');
|
|
88
|
+
item.documentation = doc;
|
|
89
|
+
|
|
90
|
+
// Insert text
|
|
91
|
+
item.insertText = token.name;
|
|
92
|
+
item.range = undefined; // Let VS Code auto-detect range
|
|
93
|
+
|
|
94
|
+
items.push(item);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return items;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
'"', // Trigger on quote
|
|
101
|
+
"'" // Trigger on single quote
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return provider;
|
|
105
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
languages,
|
|
3
|
+
Diagnostic,
|
|
4
|
+
DiagnosticSeverity,
|
|
5
|
+
Disposable,
|
|
6
|
+
TextDocument,
|
|
7
|
+
workspace,
|
|
8
|
+
} from 'vscode';
|
|
9
|
+
import { getRegistryForFile } from '../registry/cache';
|
|
10
|
+
import { findIconTokens, type TokenMatch } from '../utils/tokenParse';
|
|
11
|
+
|
|
12
|
+
const TOKEN_PATTERN = /^[a-z][a-z0-9-]*:[a-zA-Z0-9][a-zA-Z0-9._/-]*$/;
|
|
13
|
+
|
|
14
|
+
export class DiagnosticsManager {
|
|
15
|
+
private collection: ReturnType<typeof languages.createDiagnosticCollection>;
|
|
16
|
+
private disposables: Disposable[] = [];
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this.collection = languages.createDiagnosticCollection('semicons');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async updateDocument(document: TextDocument): Promise<void> {
|
|
23
|
+
const registry = await getRegistryForFile(document.uri);
|
|
24
|
+
|
|
25
|
+
const diagnostics: Diagnostic[] = [];
|
|
26
|
+
const matches = findIconTokens(document);
|
|
27
|
+
|
|
28
|
+
for (const match of matches) {
|
|
29
|
+
const { name, range } = match;
|
|
30
|
+
|
|
31
|
+
// Check if matches token pattern
|
|
32
|
+
if (!TOKEN_PATTERN.test(name)) {
|
|
33
|
+
diagnostics.push(new Diagnostic(
|
|
34
|
+
range,
|
|
35
|
+
`Invalid token format. Expected "category:name" (e.g., "navigation:menu")`,
|
|
36
|
+
DiagnosticSeverity.Error
|
|
37
|
+
));
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if token exists in registry
|
|
42
|
+
if (!registry || !registry.tokens) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const token = registry.tokens.find((t: { name: string }) => t.name === name);
|
|
47
|
+
|
|
48
|
+
if (!token) {
|
|
49
|
+
diagnostics.push(new Diagnostic(
|
|
50
|
+
range,
|
|
51
|
+
`Icon token "${name}" not found in registry`,
|
|
52
|
+
DiagnosticSeverity.Error
|
|
53
|
+
));
|
|
54
|
+
} else if (token.meta.deprecated) {
|
|
55
|
+
const deprecationMessage = typeof token.meta.deprecated === 'string'
|
|
56
|
+
? `Deprecated: ${token.meta.deprecated}`
|
|
57
|
+
: 'This icon is deprecated';
|
|
58
|
+
|
|
59
|
+
diagnostics.push(new Diagnostic(
|
|
60
|
+
range,
|
|
61
|
+
`⚠️ ${deprecationMessage}`,
|
|
62
|
+
DiagnosticSeverity.Warning
|
|
63
|
+
));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.collection.set(document.uri, diagnostics);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
clear(): void {
|
|
71
|
+
this.collection.clear();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
dispose(): void {
|
|
75
|
+
this.collection.dispose();
|
|
76
|
+
for (const d of this.disposables) {
|
|
77
|
+
d.dispose();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function registerDiagnosticsProvider(
|
|
83
|
+
manager: DiagnosticsManager
|
|
84
|
+
): Disposable {
|
|
85
|
+
const disposables: Disposable[] = [];
|
|
86
|
+
|
|
87
|
+
const languagesToRegister = [
|
|
88
|
+
'typescriptreact',
|
|
89
|
+
'javascriptreact',
|
|
90
|
+
'vue',
|
|
91
|
+
'astro',
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
for (const language of languagesToRegister) {
|
|
95
|
+
const pattern = `**/*.${language === 'typescriptreact' || language === 'javascriptreact' ? 'tsx' : language === 'vue' ? 'vue' : 'astro'}`;
|
|
96
|
+
const watcher = workspace.createFileSystemWatcher(pattern);
|
|
97
|
+
watcher.onDidChange(async (uri) => {
|
|
98
|
+
const document = await workspace.openTextDocument(uri);
|
|
99
|
+
await manager.updateDocument(document);
|
|
100
|
+
});
|
|
101
|
+
disposables.push(watcher);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Disposable.from(...disposables);
|
|
105
|
+
}
|