From 32daed08561162080cae3ab0b4d7e0af2348b51d Mon Sep 17 00:00:00 2001 From: lishaobos <46661044+lishaobos@users.noreply.github.com> Date: Wed, 9 Nov 2022 10:53:10 +0800 Subject: [PATCH] feat(vscode): selection style (#1840) Co-authored-by: cuiluc Co-authored-by: Anthony Fu --- packages/vscode/package.json | 5 ++ packages/vscode/src/index.ts | 2 + packages/vscode/src/selectionStyle.ts | 90 +++++++++++++++++++++++++++ packages/vscode/src/utils.ts | 6 +- 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 packages/vscode/src/selectionStyle.ts diff --git a/packages/vscode/package.json b/packages/vscode/package.json index ab53e7b7cc..e16a9f0b6b 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -60,6 +60,11 @@ "type": "boolean", "default": true, "description": "Enable/disable color preview decorations" + }, + "unocss.selectionStyle": { + "type": "boolean", + "default": true, + "description": "Enable/disable selection style decorations" } } } diff --git a/packages/vscode/src/index.ts b/packages/vscode/src/index.ts index 7996b7e530..ff78279ba8 100644 --- a/packages/vscode/src/index.ts +++ b/packages/vscode/src/index.ts @@ -6,6 +6,7 @@ import { log } from './log' import { registerAnnotations } from './annotation' import { registerAutoComplete } from './autocomplete' import { ContextLoader } from './contextLoader' +import { registerSelectionStyle } from './selectionStyle' export async function activate(ext: ExtensionContext) { log.appendLine(`⚪️ UnoCSS for VS Code v${version}\n`) @@ -50,6 +51,7 @@ export async function activate(ext: ExtensionContext) { registerAutoComplete(cwd, contextLoader, ext) registerAnnotations(cwd, contextLoader, status, ext) + registerSelectionStyle(cwd, contextLoader) } export function deactivate() {} diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts new file mode 100644 index 0000000000..cda9af0f5d --- /dev/null +++ b/packages/vscode/src/selectionStyle.ts @@ -0,0 +1,90 @@ +import { MarkdownString, Position, Range, window, workspace } from 'vscode' +import parserCSS from 'prettier/parser-postcss' +import prettier from 'prettier/standalone' +import type { TextEditorSelectionChangeEvent } from 'vscode' +import { regexScopePlaceholder } from '@unocss/core' +import { log } from './log' +import { throttle } from './utils' +import type { ContextLoader } from './contextLoader' +import { getMatchedPositionsFromCode } from './integration' + +export async function registerSelectionStyle(cwd: string, contextLoader: ContextLoader) { + const hasSelectionStyle = (): boolean => workspace.getConfiguration().get('unocss.selectionStyle') ?? true + + const integrationDecoration = window.createTextEditorDecorationType({}) + + async function selectionStyle(editor: TextEditorSelectionChangeEvent) { + try { + if (!hasSelectionStyle()) + return reset() + + const doc = editor.textEditor.document + if (!doc) + return reset() + + const id = doc.uri.fsPath + const selection = editor.textEditor.selection + const range = new Range( + new Position(selection.start.line, selection.start.character), + new Position(selection.end.line, selection.end.character), + ) + let code = editor.textEditor.document.getText(range).trim() + if (!code.startsWith('<')) + code = `
')) + code = `${code} >` + const ctx = await contextLoader.resolveContext(code, id) || (await contextLoader.resolveClosestContext(code, id)) + const result = await getMatchedPositionsFromCode(ctx.uno, code) + if (!result.length) + return reset() + + const uniqMap = new Map() + for (const [start, end, className] of result) + uniqMap.set(`${start}-${end}`, className) + + const classNamePlaceholder = '___' + const sheetMap = new Map() + await Promise.all(Array.from(uniqMap.values()) + .map(async (name) => { + const tokens = await ctx.uno.parseToken(name, classNamePlaceholder) || [] + tokens.forEach((token) => { + if (token[1] && token[2]) { + const key = token[1] + .replace(`.${classNamePlaceholder}`, '&') + .replace(regexScopePlaceholder, ' ') + .trim() + sheetMap.set(key, (sheetMap.get(key) || '') + token[2]) + } + }) + }), + ) + + const css = Array.from(sheetMap.keys()) + .sort() + .map(key => `${key}{${sheetMap.get(key)}}`) + .join('\n') + + const prettified = prettier.format(css, { + parser: 'css', + plugins: [parserCSS], + }) + + editor.textEditor.setDecorations(integrationDecoration, [{ + range, + get hoverMessage() { + return new MarkdownString(`UnoCSS utilities in the selection will be equivalent to:\n\`\`\`css\n${prettified.trim()}\n\`\`\``) + }, + }]) + + function reset() { + editor.textEditor.setDecorations(integrationDecoration, []) + } + } + catch (e) { + log.appendLine('⚠️ Error on selectionStyle') + log.appendLine(String(e)) + } + } + + window.onDidChangeTextEditorSelection(throttle(selectionStyle, 200)) +} diff --git a/packages/vscode/src/utils.ts b/packages/vscode/src/utils.ts index 96449e60ea..429f2cf65a 100644 --- a/packages/vscode/src/utils.ts +++ b/packages/vscode/src/utils.ts @@ -9,15 +9,15 @@ import { colorToString } from '@unocss/preset-mini/utils' export function throttle any)>(func: T, timeFrame: number): T { let lastTime = 0 let timer: any - return function () { + return function (...args) { const now = Date.now() clearTimeout(timer) if (now - lastTime >= timeFrame) { lastTime = now - return func() + return func(...args) } else { - timer = setTimeout(func, timeFrame) + timer = setTimeout(func, timeFrame, ...args) } } as T }