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): color preview in suggestions #1202

Merged
merged 1 commit into from Jul 1, 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
23 changes: 17 additions & 6 deletions packages/vscode/src/autocomplete.ts
Expand Up @@ -3,7 +3,7 @@ import { createAutocomplete } from '@unocss/autocomplete'
import type { CompletionItemProvider, ExtensionContext } from 'vscode'
import { CompletionItem, CompletionItemKind, CompletionList, MarkdownString, Range, languages } from 'vscode'
import type { UnoGenerator, UnocssPluginContext } from '@unocss/core'
import { getPrettiedMarkdown, isCssId, isSubdir } from './utils'
import { body2ColorValue, getPrettiedCSS, getPrettiedMarkdown, isCssId, isSubdir } from './utils'
import { log } from './log'
import type { ContextLoader } from './contextLoader'

Expand Down Expand Up @@ -90,11 +90,21 @@ export async function registerAutoComplete(
if (!result.suggestions.length)
return

const theme = ctx?.uno.config.theme
return new CompletionList(result.suggestions.map(([value, label]) => {
const colorValue = theme ? body2ColorValue(value, theme) : null
const itemKind = colorValue?.color ? CompletionItemKind.Color : CompletionItemKind.EnumMember

const resolved = result.resolveReplacement(value)
const item = new UnoCompletionItem(label, CompletionItemKind.EnumMember, ctx!.uno)
const item = new UnoCompletionItem(label, itemKind, ctx!.uno)
item.insertText = resolved.replacement
item.range = new Range(doc.positionAt(resolved.start), doc.positionAt(resolved.end))

if (colorValue?.color) {
item.documentation = colorValue?.color
item.sortText = /-\d$/.test(label) ? '1' : '2' // reorder color completions
}

return item
}), true)
}
Expand All @@ -105,10 +115,11 @@ export async function registerAutoComplete(
},

async resolveCompletionItem(item) {
return {
...item,
documentation: await getMarkdown(item.uno, item.label as string),
}
if (item.kind === CompletionItemKind.Color)
item.detail = await (await getPrettiedCSS(item.uno, item.label as string)).prettified
else
item.documentation = await getMarkdown(item.uno, item.label as string)
return item
},
}

Expand Down
37 changes: 22 additions & 15 deletions packages/vscode/src/utils.ts
Expand Up @@ -40,12 +40,26 @@ export async function getPrettiedMarkdown(uno: UnoGenerator, util: string) {
return `\`\`\`css\n${(await getPrettiedCSS(uno, util)).prettified}\n\`\`\``
}

export function body2ColorValue(body: string, theme: Theme) {
const themeColorNames = Object.keys(theme.colors ?? {})
const colorNames = themeColorNames.concat(themeColorNames.map(colorName => colorName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()))

for (const colorName of colorNames) {
const nameIndex = body.indexOf(colorName)
if (nameIndex > -1) {
const parsedResult = parseColor(body.substring(nameIndex), theme)
if (parsedResult?.cssColor)
return parsedResult
}
}

return null
}

const matchedAttributifyRE = /(?<=^\[.+~?=").*(?="\]$)/
const _colorsMapCache = new Map<string, string>()
export function getColorsMap(uno: UnoGenerator, result: GenerateResult) {
const theme = uno.config.theme as Theme
const themeColorNames = Object.keys(theme.colors ?? {})
const colorNames = themeColorNames.concat(themeColorNames.map(colorName => colorName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()))
const colorsMap = new Map<string, string>()

for (const i of result.matched) {
Expand All @@ -56,20 +70,13 @@ export function getColorsMap(uno: UnoGenerator, result: GenerateResult) {
}

const matchedAttr = i.match(matchedAttributifyRE)
const body = matchedAttr ? matchedAttr[0].split(':').at(-1) ?? '' : i // remove prefix e.g. `dark:` `hover:`

for (const colorName of colorNames) {
const nameIndex = body.indexOf(colorName)
if (nameIndex > -1) {
const parsedResult = parseColor(body.substring(nameIndex), theme)
if (parsedResult?.cssColor) {
const color = colorToString(parsedResult.cssColor, parsedResult.alpha)
colorsMap.set(_i, color)
_colorsMapCache.set(_i, color)
}
const body = matchedAttr?.[0] ?? i // remove prefix e.g. `dark:` `hover:`

break
}
const colorValue = body2ColorValue(body, theme)
if (colorValue) {
const colorString = colorToString(colorValue.cssColor!, colorValue.alpha)
colorsMap.set(_i, colorString)
_colorsMapCache.set(_i, colorString)
}
}

Expand Down