Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix selection style #1860

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/vscode/package.json
Expand Up @@ -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"
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/vscode/src/index.ts
Expand Up @@ -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`)
Expand Down Expand Up @@ -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() {}
113 changes: 113 additions & 0 deletions packages/vscode/src/selectionStyle.ts
@@ -0,0 +1,113 @@
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 = `<div ${code}`
if (!code.endsWith('>'))
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<string, string>()
for (const [start, end, className] of result)
uniqMap.set(`${start}-${end}`, className)

const classNamePlaceholder = '___'
const sheetMap = new Map<string, string>()
const mediaSheetMap = new Map<string, Map<string, string>>()
await Promise.all(Array.from(uniqMap.values())
.map(async (name) => {
const tokens = await ctx.uno.parseToken(name, classNamePlaceholder) || []
tokens.forEach(([, className, cssText, media]) => {
if (className && cssText) {
const key = className
.replace(`.${classNamePlaceholder}`, '&')
.replace(regexScopePlaceholder, ' ')
.trim()

let activeSheetMap = sheetMap
if (media) {
if (!mediaSheetMap.has(media))
mediaSheetMap.set(media, new Map<string, string>())

activeSheetMap = mediaSheetMap.get(media)!
}

activeSheetMap.set(key, (activeSheetMap.get(key) || '') + cssText)
}
})
}),
)

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, {
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))
}
6 changes: 3 additions & 3 deletions packages/vscode/src/utils.ts
Expand Up @@ -9,15 +9,15 @@ import { colorToString } from '@unocss/preset-mini/utils'
export function throttle<T extends ((...args: any) => 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
}
Expand Down