From dd5078865af2ae8444d9abd3bd0ebbded8f07d2e Mon Sep 17 00:00:00 2001 From: cuiluc Date: Thu, 3 Nov 2022 13:36:28 +0800 Subject: [PATCH 1/8] feat: selection-style --- packages/core/src/generator/index.ts | 1 + packages/core/src/types.ts | 1 + packages/vscode/package.json | 5 ++ packages/vscode/src/index.ts | 2 + packages/vscode/src/selectionStyle.ts | 90 +++++++++++++++++++++++++++ packages/vscode/src/utils.ts | 6 +- 6 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 packages/vscode/src/selectionStyle.ts diff --git a/packages/core/src/generator/index.ts b/packages/core/src/generator/index.ts index 8e2d483544..f4a201091e 100644 --- a/packages/core/src/generator/index.ts +++ b/packages/core/src/generator/index.ts @@ -288,6 +288,7 @@ export class UnoGenerator { } return { + sheet, get css() { return getLayers() }, layers, matched, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e99f719ae0..8431ec232b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -635,6 +635,7 @@ RequiredByKey workspace.getConfiguration().get('unocss.selectionStyle') ?? true + + const integrationDecoration = window.createTextEditorDecorationType({ + after: { + textDecoration: 'width:10rem;height:10rem;content: \'😎\';cursor: pointer', + }, + }) + + 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) + if (!code.startsWith('<')) + code = `
')) + code = `${code} >` + const ctx = await contextLoader.resolveContext(code, id) || (await contextLoader.resolveClosestContext(code, id)) + const result = await ctx.uno.generate(code, { id, preflights: false, minify: true }) + if (!result.css) + return reset() + + const list = [] + const pseudoReg = /.+(::\w+)/ + const pseudoMap = new Map() + const sheet = result?.sheet as Array + for (const [key, value] of sheet.entries()) { + const arr = [] + for (const val of value) { + const match = val[1].match(pseudoReg) + if (match) { + if (!pseudoMap.get(match[1])) + pseudoMap.set(match[1], []) + pseudoMap.get(match[1]).push(val[2]) + } + else { + arr.push(val[2]) + } + } + const style = `${key}{${arr.join('')}}` + key ? list.push(style) : list.unshift(style) + } + const pseudoList = [] + for (const [key, value] of pseudoMap.entries()) + pseudoList.push(`${key}{${value.join('')}}`) + + const prettified = prettier.format(`${list.concat(pseudoList).join('')}`, { + parser: 'css', + plugins: [parserCSS], + }) + + editor.textEditor.setDecorations(integrationDecoration, [{ + range, + get hoverMessage() { + return new MarkdownString(`\`\`\`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 1ea1443ca1..dd23e47db1 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 } From aa57fd5d5d095218181ba78038593232127593b7 Mon Sep 17 00:00:00 2001 From: cuiluc Date: Thu, 3 Nov 2022 17:01:23 +0800 Subject: [PATCH 2/8] fix: code preFormat --- packages/vscode/src/selectionStyle.ts | 42 +++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index 29c8bd45f5..bb2932b409 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -5,6 +5,7 @@ import type { TextEditorSelectionChangeEvent } from 'vscode' 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 @@ -36,29 +37,52 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context if (!code.endsWith('>')) code = `${code} >` const ctx = await contextLoader.resolveContext(code, id) || (await contextLoader.resolveClosestContext(code, id)) - const result = await ctx.uno.generate(code, { id, preflights: false, minify: true }) - if (!result.css) + 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 sheetMap = new Map() + for (const [, className] of uniqMap.entries()) { + const result = await ctx.uno.generate(new Set([className]), { preflights: false, safelist: false }) + const [[key, value]] = Array.from(result.sheet) + if (!sheetMap.get(key)) + sheetMap.set(key, value) + else sheetMap.get(key).push(value[0]) + } + const list = [] const pseudoReg = /.+(::\w+)/ const pseudoMap = new Map() - const sheet = result?.sheet as Array - for (const [key, value] of sheet.entries()) { + for (const [key, value] of sheetMap) { const arr = [] + const metaPseudoMap = new Map() for (const val of value) { const match = val[1].match(pseudoReg) + const map = key ? metaPseudoMap : pseudoMap if (match) { - if (!pseudoMap.get(match[1])) - pseudoMap.set(match[1], []) - pseudoMap.get(match[1]).push(val[2]) + if (!map.get(match[1])) + map.set(match[1], []) + map.get(match[1]).push(val[2]) } else { arr.push(val[2]) } } - const style = `${key}{${arr.join('')}}` - key ? list.push(style) : list.unshift(style) + if (!key) { + arr.length && list.unshift(`{${arr.join('')}}`) + } + else { + const pseudoList = [] + for (const [key, value] of metaPseudoMap.entries()) + pseudoList.push(`${key}{${value.join('')}}`) + + pseudoList.length && list.push(`${key}{${pseudoList.join('')}}`) + arr.length && list.push(`${key}{${arr.join('')}}`) + } } const pseudoList = [] for (const [key, value] of pseudoMap.entries()) From e846398784d527b106c3370351c479e85649558f Mon Sep 17 00:00:00 2001 From: cuiluc Date: Fri, 4 Nov 2022 09:12:25 +0800 Subject: [PATCH 3/8] fix: type check --- packages/vscode/src/selectionStyle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index bb2932b409..0db9eca63c 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -48,7 +48,7 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context const sheetMap = new Map() for (const [, className] of uniqMap.entries()) { const result = await ctx.uno.generate(new Set([className]), { preflights: false, safelist: false }) - const [[key, value]] = Array.from(result.sheet) + const [[key, value]] = Array.from(result.sheet) as Array<[string, any[]]> if (!sheetMap.get(key)) sheetMap.set(key, value) else sheetMap.get(key).push(value[0]) From c764e176bf7f44b479a5e8ffa54b9efcb98fca98 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 7 Nov 2022 17:52:24 +0800 Subject: [PATCH 4/8] chore: update --- packages/core/src/types.ts | 2 +- packages/vscode/src/selectionStyle.ts | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 8431ec232b..760932c16a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -635,12 +635,12 @@ RequiredByKey + sheet?: Map } export type VariantMatchedResult = readonly [ diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index 0db9eca63c..ab2d11d0a9 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -2,6 +2,7 @@ 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 type { StringifiedUtil } from '@unocss/core' import { log } from './log' import { throttle } from './utils' import type { ContextLoader } from './contextLoader' @@ -10,11 +11,7 @@ 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({ - after: { - textDecoration: 'width:10rem;height:10rem;content: \'😎\';cursor: pointer', - }, - }) + const integrationDecoration = window.createTextEditorDecorationType({}) async function selectionStyle(editor: TextEditorSelectionChangeEvent) { try { @@ -41,20 +38,21 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context if (!result.length) return reset() - const uniqMap = new Map() + const uniqMap = new Map() for (const [start, end, className] of result) uniqMap.set(`${start}-${end}`, className) - const sheetMap = new Map() + const sheetMap = new Map() for (const [, className] of uniqMap.entries()) { const result = await ctx.uno.generate(new Set([className]), { preflights: false, safelist: false }) - const [[key, value]] = Array.from(result.sheet) as Array<[string, any[]]> - if (!sheetMap.get(key)) + const [[key, value]] = Array.from(result.sheet!.entries()) + if (!sheetMap.has(key)) sheetMap.set(key, value) - else sheetMap.get(key).push(value[0]) + else + sheetMap.get(key)!.push(...value) } - const list = [] + const list: string[] = [] const pseudoReg = /.+(::\w+)/ const pseudoMap = new Map() for (const [key, value] of sheetMap) { @@ -84,7 +82,8 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context arr.length && list.push(`${key}{${arr.join('')}}`) } } - const pseudoList = [] + + const pseudoList: string[] = [] for (const [key, value] of pseudoMap.entries()) pseudoList.push(`${key}{${value.join('')}}`) @@ -96,7 +95,7 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context editor.textEditor.setDecorations(integrationDecoration, [{ range, get hoverMessage() { - return new MarkdownString(`\`\`\`css\n${prettified.trim()}\n\`\`\``) + return new MarkdownString(`UnoCSS utilities in the selection will be equivalent to:\n\`\`\`css\n${prettified.trim()}\n\`\`\``) }, }]) From 4c84d7557fe449bb96554093e4b56fcac473311c Mon Sep 17 00:00:00 2001 From: cuiluc Date: Mon, 7 Nov 2022 18:49:25 +0800 Subject: [PATCH 5/8] fix: resolve type check --- packages/vscode/src/selectionStyle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index ab2d11d0a9..ff5fea6a8c 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -59,7 +59,7 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context const arr = [] const metaPseudoMap = new Map() for (const val of value) { - const match = val[1].match(pseudoReg) + const match = val?.[1]?.match(pseudoReg) const map = key ? metaPseudoMap : pseudoMap if (match) { if (!map.get(match[1])) From c986bf4c9af3ceebd34defa22ef4944b447516ea Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 7 Nov 2022 21:36:00 +0800 Subject: [PATCH 6/8] chore: update --- packages/vscode/src/selectionStyle.ts | 69 +++++++++------------------ 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index ff5fea6a8c..cda9af0f5d 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -2,7 +2,7 @@ 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 type { StringifiedUtil } from '@unocss/core' +import { regexScopePlaceholder } from '@unocss/core' import { log } from './log' import { throttle } from './utils' import type { ContextLoader } from './contextLoader' @@ -28,7 +28,7 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context new Position(selection.start.line, selection.start.character), new Position(selection.end.line, selection.end.character), ) - let code = editor.textEditor.document.getText(range) + let code = editor.textEditor.document.getText(range).trim() if (!code.startsWith('<')) code = `
')) @@ -42,52 +42,29 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context for (const [start, end, className] of result) uniqMap.set(`${start}-${end}`, className) - const sheetMap = new Map() - for (const [, className] of uniqMap.entries()) { - const result = await ctx.uno.generate(new Set([className]), { preflights: false, safelist: false }) - const [[key, value]] = Array.from(result.sheet!.entries()) - if (!sheetMap.has(key)) - sheetMap.set(key, value) - else - sheetMap.get(key)!.push(...value) - } - - const list: string[] = [] - const pseudoReg = /.+(::\w+)/ - const pseudoMap = new Map() - for (const [key, value] of sheetMap) { - const arr = [] - const metaPseudoMap = new Map() - for (const val of value) { - const match = val?.[1]?.match(pseudoReg) - const map = key ? metaPseudoMap : pseudoMap - if (match) { - if (!map.get(match[1])) - map.set(match[1], []) - map.get(match[1]).push(val[2]) - } - else { - arr.push(val[2]) - } - } - if (!key) { - arr.length && list.unshift(`{${arr.join('')}}`) - } - else { - const pseudoList = [] - for (const [key, value] of metaPseudoMap.entries()) - pseudoList.push(`${key}{${value.join('')}}`) - - pseudoList.length && list.push(`${key}{${pseudoList.join('')}}`) - arr.length && list.push(`${key}{${arr.join('')}}`) - } - } + 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 pseudoList: string[] = [] - for (const [key, value] of pseudoMap.entries()) - pseudoList.push(`${key}{${value.join('')}}`) + const css = Array.from(sheetMap.keys()) + .sort() + .map(key => `${key}{${sheetMap.get(key)}}`) + .join('\n') - const prettified = prettier.format(`${list.concat(pseudoList).join('')}`, { + const prettified = prettier.format(css, { parser: 'css', plugins: [parserCSS], }) From 5b61afd685b45b76c3babe8b4cc3bd294e10abb3 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 8 Nov 2022 21:48:14 +0800 Subject: [PATCH 7/8] chore: clean up --- packages/core/src/generator/index.ts | 1 - packages/core/src/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/core/src/generator/index.ts b/packages/core/src/generator/index.ts index f4a201091e..8e2d483544 100644 --- a/packages/core/src/generator/index.ts +++ b/packages/core/src/generator/index.ts @@ -288,7 +288,6 @@ export class UnoGenerator { } return { - sheet, get css() { return getLayers() }, layers, matched, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 760932c16a..e99f719ae0 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -640,7 +640,6 @@ export interface GenerateResult { getLayer(name?: string): string | undefined getLayers(includes?: string[], excludes?: string[]): string matched: Set - sheet?: Map } export type VariantMatchedResult = readonly [ From f15cd7953c499035d415d79c9770626b99826354 Mon Sep 17 00:00:00 2001 From: cuiluc Date: Wed, 9 Nov 2022 11:50:14 +0800 Subject: [PATCH 8/8] fix: selection with media --- packages/vscode/src/selectionStyle.ts | 31 +++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/vscode/src/selectionStyle.ts b/packages/vscode/src/selectionStyle.ts index cda9af0f5d..87d9c318e9 100644 --- a/packages/vscode/src/selectionStyle.ts +++ b/packages/vscode/src/selectionStyle.ts @@ -44,16 +44,26 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context const classNamePlaceholder = '___' const sheetMap = new Map() + const mediaSheetMap = 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] + tokens.forEach(([, className, cssText, media]) => { + if (className && cssText) { + const key = className .replace(`.${classNamePlaceholder}`, '&') .replace(regexScopePlaceholder, ' ') .trim() - sheetMap.set(key, (sheetMap.get(key) || '') + token[2]) + + let activeSheetMap = sheetMap + if (media) { + if (!mediaSheetMap.has(media)) + mediaSheetMap.set(media, new Map()) + + activeSheetMap = mediaSheetMap.get(media)! + } + + activeSheetMap.set(key, (activeSheetMap.get(key) || '') + cssText) } }) }), @@ -62,6 +72,19 @@ export async function registerSelectionStyle(cwd: string, contextLoader: Context const css = Array.from(sheetMap.keys()) .sort() .map(key => `${key}{${sheetMap.get(key)}}`) + .concat( + Array.from(mediaSheetMap.keys()) + .sort() + .map((media) => { + const sheetMap = mediaSheetMap.get(media)! + return `${media}{${ + Array.from(sheetMap!.keys()) + .sort() + .map(key => `${key}{${sheetMap!.get(key)}}`) + .join('\n') + }}` + }), + ) .join('\n') const prettified = prettier.format(css, {