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): resolve config by file path #1101

Merged
merged 8 commits into from Jun 18, 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
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