Skip to content

Commit

Permalink
feat(vscode): resolve config by file path (#1101)
Browse files Browse the repository at this point in the history
  • Loading branch information
QiroNT committed Jun 18, 2022
1 parent 3d403bf commit 08a173f
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 83 deletions.
4 changes: 2 additions & 2 deletions packages/autocomplete/src/create.ts
Expand Up @@ -2,10 +2,10 @@ import type { AutoCompleteExtractorResult, AutoCompleteFunction, AutoCompleteTem
import { escapeRegExp, toArray, uniq } from '@unocss/core'
import LRU from 'lru-cache'
import { parseAutocomplete } from './parse'
import type { ParsedAutocompleteTemplate } from './types'
import type { ParsedAutocompleteTemplate, UnocssAutocomplete } from './types'
import { searchUsageBoundary } from './utils'

export function createAutocomplete(uno: UnoGenerator) {
export function createAutocomplete(uno: UnoGenerator): UnocssAutocomplete {
const templateCache = new Map<string, ParsedAutocompleteTemplate>()
const cache = new LRU<string, string[]>({ max: 5000 })

Expand Down
12 changes: 12 additions & 0 deletions packages/autocomplete/src/types.ts
@@ -1,3 +1,6 @@
import type { AutoCompleteFunction, SuggestResult } from '@unocss/core'
import type LRU from 'lru-cache'

export type AutocompleteTemplatePart = AutocompleteTemplateStatic | AutocompleteTemplateGroup | AutocompleteTemplateTheme

export interface AutocompleteTemplateStatic {
Expand All @@ -19,3 +22,12 @@ export interface ParsedAutocompleteTemplate {
parts: AutocompleteTemplatePart[]
suggest(input: string): string[] | undefined
}

export interface UnocssAutocomplete {
suggest: (input: string) => Promise<string[]>
suggestInFile: (content: string, cursor: number) => Promise<SuggestResult>
templates: (string | AutoCompleteFunction)[]
cache: LRU<string, string[]>
reset: () => void
enumerate: () => Promise<Set<string>>
}
1 change: 1 addition & 0 deletions packages/core/src/types.ts
Expand Up @@ -466,6 +466,7 @@ export interface UnocssPluginContext<Config extends UserConfig = UserConfig> {

reloadConfig: () => Promise<LoadConfigResult<Config>>
getConfig: () => Promise<Config>
onReload: (fn: () => void) => void

invalidate: () => void
onInvalidate: (fn: () => void) => void
Expand Down
9 changes: 9 additions & 0 deletions packages/shared-integration/src/context.ts
Expand Up @@ -18,6 +18,7 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
let rollupFilter = createFilter(defaultInclude, defaultExclude)

const invalidations: Array<() => void> = []
const reloadListeners: Array<() => void> = []

const modules = new BetterMap<string, string>()
const tokens = new Set<string>()
Expand All @@ -38,6 +39,7 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
tokens.clear()
await Promise.all(modules.map((code, id) => uno.applyExtractors(code, id, tokens)))
invalidate()
dispatchReload()

// check preset duplication
const presets = new Set<string>()
Expand Down Expand Up @@ -65,6 +67,10 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
invalidations.forEach(cb => cb())
}

function dispatchReload() {
reloadListeners.forEach(cb => cb())
}

async function extract(code: string, id?: string) {
if (id)
modules.set(id, code)
Expand Down Expand Up @@ -97,6 +103,9 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
},
filter,
reloadConfig,
onReload(fn: () => void) {
reloadListeners.push(fn)
},
uno,
extract,
getConfig,
Expand Down
8 changes: 7 additions & 1 deletion packages/vscode/package.json
Expand Up @@ -28,7 +28,13 @@
"workspaceContains:**/package.json"
],
"contributes": {
"commands": [],
"commands": [
{
"command": "unocss.reload",
"title": "Reload UnoCSS",
"category": "UnoCSS"
}
],
"configuration": {
"type": "object",
"title": "UnoCSS",
Expand Down
33 changes: 22 additions & 11 deletions packages/vscode/src/annonation.ts
@@ -1,19 +1,17 @@
import { relative } from 'path'
import path from 'path'
import type { DecorationOptions, ExtensionContext, StatusBarItem } from 'vscode'
import { DecorationRangeBehavior, MarkdownString, Range, window, workspace } from 'vscode'
import type { UnocssPluginContext } from '@unocss/core'
import { INCLUDE_COMMENT_IDE, getMatchedPositions } from './integration'
import { log } from './log'
import { getPrettiedMarkdown, isCssId, throttle } from './utils'
import type { ContextLoader } from './contextLoader'

export async function registerAnnonations(
cwd: string,
context: UnocssPluginContext,
contextLoader: ContextLoader,
status: StatusBarItem,
ext: ExtensionContext,
) {
const { sources } = await context.ready
const { uno, filter } = context
let underline: boolean = workspace.getConfiguration().get('unocss.underline') ?? true
ext.subscriptions.push(workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration('unocss.underline')) {
Expand All @@ -23,10 +21,14 @@ export async function registerAnnonations(
}))

workspace.onDidSaveTextDocument(async (doc) => {
if (sources.includes(doc.uri.fsPath)) {
const id = doc.uri.fsPath
const dir = path.dirname(id)

if (contextLoader.contexts.has(dir)) {
const ctx = contextLoader.contexts.get(dir)!
try {
await context.reloadConfig()
log.appendLine(`Config reloaded by ${relative(cwd, doc.uri.fsPath)}`)
await ctx.reloadConfig()
log.appendLine(`Config reloaded by ${path.relative(cwd, doc.uri.fsPath)}`)
}
catch (e) {
log.appendLine('Error on loading config')
Expand Down Expand Up @@ -54,17 +56,23 @@ export async function registerAnnonations(
const code = doc.getText()
const id = doc.uri.fsPath

if (!code || (!code.includes(INCLUDE_COMMENT_IDE) && !isCssId(id) && !filter(code, id)))
if (!code)
return reset()

const result = await uno.generate(code, { id, preflights: false, minify: true })
let ctx = await contextLoader.resolveContext(code, id)
if (!ctx && (code.includes(INCLUDE_COMMENT_IDE) || isCssId(id)))
ctx = await contextLoader.resolveCloestContext(code, id)
else if (!ctx?.filter(code, id))
return null

const result = await ctx.uno.generate(code, { id, preflights: false, minify: true })

const ranges: DecorationOptions[] = (
await Promise.all(
getMatchedPositions(code, Array.from(result.matched))
.map(async (i): Promise<DecorationOptions> => {
try {
const md = await getPrettiedMarkdown(uno, i[2])
const md = await getPrettiedMarkdown(ctx!.uno, i[2])
return {
range: new Range(doc.positionAt(i[0]), doc.positionAt(i[1])),
get hoverMessage() {
Expand Down Expand Up @@ -113,6 +121,9 @@ export async function registerAnnonations(
if (e.document === window.activeTextEditor?.document)
throttledUpdateAnnotation()
})
contextLoader.events.on('reload', async () => {
await updateAnnotation()
})

await updateAnnotation()
}
58 changes: 46 additions & 12 deletions packages/vscode/src/autocomplete.ts
@@ -1,9 +1,11 @@
import type { UnocssPluginContext } from '@unocss/core'
import type { UnocssAutocomplete } from '@unocss/autocomplete'
import { createAutocomplete } from '@unocss/autocomplete'
import type { CompletionItemProvider, ExtensionContext, Position, TextDocument } from 'vscode'
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 } from './utils'
import { log } from './log'
import type { ContextLoader } from './contextLoader'

const languageIds = [
'erb',
Expand All @@ -24,27 +26,59 @@ const languageIds = [
]
const delimiters = ['-', ':']

class UnoCompletionItem extends CompletionItem {
uno: UnoGenerator

constructor(label: string, kind: CompletionItemKind, uno: UnoGenerator) {
super(label, kind)
this.uno = uno
}
}

export async function registerAutoComplete(
context: UnocssPluginContext,
contextLoader: ContextLoader,
ext: ExtensionContext,
) {
const { uno, filter } = context
const autoCompletes = new Map<UnocssPluginContext, UnocssAutocomplete>()
contextLoader.events.on('contextReload', (ctx) => {
autoCompletes.delete(ctx)
})
contextLoader.events.on('contextUnload', (ctx) => {
autoCompletes.delete(ctx)
})

function getAutocomplete(ctx: UnocssPluginContext) {
const cached = autoCompletes.get(ctx)
if (cached)
return cached

const autoComplete = createAutocomplete(uno)
const autocomplete = createAutocomplete(ctx.uno)

async function getMarkdown(util: string) {
autoCompletes.set(ctx, autocomplete)
return autocomplete
}

async function getMarkdown(uno: UnoGenerator, util: string) {
return new MarkdownString(await getPrettiedMarkdown(uno, util))
}

const provider: CompletionItemProvider = {
async provideCompletionItems(doc: TextDocument, position: Position) {
const provider: CompletionItemProvider<UnoCompletionItem> = {
async provideCompletionItems(doc, position) {
const code = doc.getText()
const id = doc.uri.fsPath

if (!code || (!isCssId(id) && !filter(code, id)))
if (!code)
return null

let ctx = await contextLoader.resolveContext(code, id)
if (!ctx && isCssId(id))
ctx = await contextLoader.resolveCloestContext(code, id)
else if (!ctx?.filter(code, id))
return null

try {
const autoComplete = getAutocomplete(ctx)

const result = await autoComplete.suggestInFile(code, doc.offsetAt(position))

log.appendLine(`[autocomplete] ${id} | ${result.suggestions.slice(0, 10).map(v => `[${v[0]}, ${v[1]}]`).join(', ')}`)
Expand All @@ -54,7 +88,7 @@ export async function registerAutoComplete(

return new CompletionList(result.suggestions.map(([value, label]) => {
const resolved = result.resolveReplacement(value)
const item = new CompletionItem(label, CompletionItemKind.EnumMember)
const item = new UnoCompletionItem(label, CompletionItemKind.EnumMember, ctx!.uno)
item.insertText = resolved.replacement
item.range = new Range(doc.positionAt(resolved.start), doc.positionAt(resolved.end))
return item
Expand All @@ -66,10 +100,10 @@ export async function registerAutoComplete(
}
},

async resolveCompletionItem(item: CompletionItem) {
async resolveCompletionItem(item) {
return {
...item,
documentation: await getMarkdown(item.label as string),
documentation: await getMarkdown(item.uno, item.label as string),
}
},
}
Expand Down

0 comments on commit 08a173f

Please sign in to comment.