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

feat(vscode): selection style #1840

Merged
merged 7 commits into from Nov 9, 2022
Merged
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() {}
90 changes: 90 additions & 0 deletions 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 = `<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>()
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))
}
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