Add `experimental.configFile` setting (#541)
* Add experimental `configFile` setting * Fix initial capability registration * Update readme * Add setting default and description * Remove unused variable * Be more defensive when reading setting * Fix type * Fix typemaster
parent
68682298b9
commit
78a20f4daf
|
@ -26,6 +26,7 @@ import {
|
||||||
DidChangeWatchedFilesNotification,
|
DidChangeWatchedFilesNotification,
|
||||||
FileChangeType,
|
FileChangeType,
|
||||||
Disposable,
|
Disposable,
|
||||||
|
TextDocumentIdentifier,
|
||||||
} from 'vscode-languageserver/node'
|
} from 'vscode-languageserver/node'
|
||||||
import { TextDocument } from 'vscode-languageserver-textdocument'
|
import { TextDocument } from 'vscode-languageserver-textdocument'
|
||||||
import { URI } from 'vscode-uri'
|
import { URI } from 'vscode-uri'
|
||||||
|
@ -177,6 +178,7 @@ interface ProjectService {
|
||||||
tryInit: () => Promise<void>
|
tryInit: () => Promise<void>
|
||||||
dispose: () => void
|
dispose: () => void
|
||||||
onUpdateSettings: (settings: any) => void
|
onUpdateSettings: (settings: any) => void
|
||||||
|
onFileEvents: (changes: Array<{ file: string; type: FileChangeType }>) => void
|
||||||
onHover(params: TextDocumentPositionParams): Promise<Hover>
|
onHover(params: TextDocumentPositionParams): Promise<Hover>
|
||||||
onCompletion(params: CompletionParams): Promise<CompletionList>
|
onCompletion(params: CompletionParams): Promise<CompletionList>
|
||||||
onCompletionResolve(item: CompletionItem): Promise<CompletionItem>
|
onCompletionResolve(item: CompletionItem): Promise<CompletionItem>
|
||||||
|
@ -186,6 +188,8 @@ interface ProjectService {
|
||||||
onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
|
onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProjectConfig = { folder: string; configPath?: string; documentSelector?: string[] }
|
||||||
|
|
||||||
function getMode(config: any): unknown {
|
function getMode(config: any): unknown {
|
||||||
if (typeof config.mode !== 'undefined') {
|
if (typeof config.mode !== 'undefined') {
|
||||||
return config.mode
|
return config.mode
|
||||||
|
@ -210,11 +214,13 @@ function deleteMode(config: any): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createProjectService(
|
async function createProjectService(
|
||||||
folder: string,
|
projectConfig: ProjectConfig,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
params: InitializeParams,
|
params: InitializeParams,
|
||||||
documentService: DocumentService
|
documentService: DocumentService,
|
||||||
|
updateCapabilities: () => void
|
||||||
): Promise<ProjectService> {
|
): Promise<ProjectService> {
|
||||||
|
const folder = projectConfig.folder
|
||||||
const disposables: Disposable[] = []
|
const disposables: Disposable[] = []
|
||||||
const documentSettingsCache: Map<string, Settings> = new Map()
|
const documentSettingsCache: Map<string, Settings> = new Map()
|
||||||
|
|
||||||
|
@ -259,8 +265,6 @@ async function createProjectService(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let registrations: Promise<BulkUnregistration>
|
|
||||||
|
|
||||||
let chokidarWatcher: chokidar.FSWatcher
|
let chokidarWatcher: chokidar.FSWatcher
|
||||||
let ignore = state.editor.globalSettings.tailwindCSS.files?.exclude ?? DEFAULT_FILES_EXCLUDE
|
let ignore = state.editor.globalSettings.tailwindCSS.files?.exclude ?? DEFAULT_FILES_EXCLUDE
|
||||||
|
|
||||||
|
@ -311,15 +315,6 @@ async function createProjectService(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
|
if (params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
|
||||||
connection.onDidChangeWatchedFiles(({ changes }) => {
|
|
||||||
onFileEvents(
|
|
||||||
changes.map(({ uri, type }) => ({
|
|
||||||
file: URI.parse(uri).fsPath,
|
|
||||||
type,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
connection.client.register(DidChangeWatchedFilesNotification.type, {
|
connection.client.register(DidChangeWatchedFilesNotification.type, {
|
||||||
watchers: [{ globPattern: `**/${CONFIG_FILE_GLOB}` }, { globPattern: `**/${PACKAGE_GLOB}` }],
|
watchers: [{ globPattern: `**/${CONFIG_FILE_GLOB}` }, { globPattern: `**/${PACKAGE_GLOB}` }],
|
||||||
})
|
})
|
||||||
|
@ -376,38 +371,6 @@ async function createProjectService(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerCapabilities(watchFiles: string[] = []): void {
|
|
||||||
if (supportsDynamicRegistration(connection, params)) {
|
|
||||||
if (registrations) {
|
|
||||||
registrations.then((r) => r.dispose())
|
|
||||||
}
|
|
||||||
|
|
||||||
let capabilities = BulkRegistration.create()
|
|
||||||
|
|
||||||
capabilities.add(HoverRequest.type, {
|
|
||||||
documentSelector: null,
|
|
||||||
})
|
|
||||||
capabilities.add(DocumentColorRequest.type, {
|
|
||||||
documentSelector: null,
|
|
||||||
})
|
|
||||||
capabilities.add(CodeActionRequest.type, {
|
|
||||||
documentSelector: null,
|
|
||||||
})
|
|
||||||
capabilities.add(CompletionRequest.type, {
|
|
||||||
documentSelector: null,
|
|
||||||
resolveProvider: true,
|
|
||||||
triggerCharacters: [...TRIGGER_CHARACTERS, state.separator].filter(Boolean),
|
|
||||||
})
|
|
||||||
if (watchFiles.length > 0) {
|
|
||||||
capabilities.add(DidChangeWatchedFilesNotification.type, {
|
|
||||||
watchers: watchFiles.map((file) => ({ globPattern: file })),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
registrations = connection.client.register(capabilities)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetState(): void {
|
function resetState(): void {
|
||||||
clearAllDiagnostics(state)
|
clearAllDiagnostics(state)
|
||||||
Object.keys(state).forEach((key) => {
|
Object.keys(state).forEach((key) => {
|
||||||
|
@ -417,7 +380,7 @@ async function createProjectService(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.enabled = false
|
state.enabled = false
|
||||||
registerCapabilities(state.dependencies)
|
updateCapabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tryInit() {
|
async function tryInit() {
|
||||||
|
@ -452,19 +415,23 @@ async function createProjectService(
|
||||||
async function init() {
|
async function init() {
|
||||||
clearRequireCache()
|
clearRequireCache()
|
||||||
|
|
||||||
let [configPath] = (
|
let configPath = projectConfig.configPath
|
||||||
await glob([`**/${CONFIG_FILE_GLOB}`], {
|
|
||||||
cwd: folder,
|
if (!configPath) {
|
||||||
ignore: state.editor.globalSettings.tailwindCSS.files?.exclude ?? DEFAULT_FILES_EXCLUDE,
|
configPath = (
|
||||||
onlyFiles: true,
|
await glob([`**/${CONFIG_FILE_GLOB}`], {
|
||||||
absolute: true,
|
cwd: folder,
|
||||||
suppressErrors: true,
|
ignore: state.editor.globalSettings.tailwindCSS.files?.exclude ?? DEFAULT_FILES_EXCLUDE,
|
||||||
dot: true,
|
onlyFiles: true,
|
||||||
concurrency: Math.max(os.cpus().length, 1),
|
absolute: true,
|
||||||
})
|
suppressErrors: true,
|
||||||
)
|
dot: true,
|
||||||
.sort((a: string, b: string) => a.split('/').length - b.split('/').length)
|
concurrency: Math.max(os.cpus().length, 1),
|
||||||
.map(path.normalize)
|
})
|
||||||
|
)
|
||||||
|
.sort((a: string, b: string) => a.split('/').length - b.split('/').length)
|
||||||
|
.map(path.normalize)[0]
|
||||||
|
}
|
||||||
|
|
||||||
if (!configPath) {
|
if (!configPath) {
|
||||||
throw new SilentError('No config file found.')
|
throw new SilentError('No config file found.')
|
||||||
|
@ -957,7 +924,7 @@ async function createProjectService(
|
||||||
|
|
||||||
updateAllDiagnostics(state)
|
updateAllDiagnostics(state)
|
||||||
|
|
||||||
registerCapabilities(state.dependencies)
|
updateCapabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -980,12 +947,13 @@ async function createProjectService(
|
||||||
updateAllDiagnostics(state)
|
updateAllDiagnostics(state)
|
||||||
}
|
}
|
||||||
if (settings.editor.colorDecorators) {
|
if (settings.editor.colorDecorators) {
|
||||||
registerCapabilities(state.dependencies)
|
updateCapabilities()
|
||||||
} else {
|
} else {
|
||||||
connection.sendNotification('@/tailwindCSS/clearColors')
|
connection.sendNotification('@/tailwindCSS/clearColors')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onFileEvents,
|
||||||
async onHover(params: TextDocumentPositionParams): Promise<Hover> {
|
async onHover(params: TextDocumentPositionParams): Promise<Hover> {
|
||||||
if (!state.enabled) return null
|
if (!state.enabled) return null
|
||||||
let document = documentService.getDocument(params.textDocument.uri)
|
let document = documentService.getDocument(params.textDocument.uri)
|
||||||
|
@ -1002,11 +970,19 @@ async function createProjectService(
|
||||||
let settings = await state.editor.getConfiguration(document.uri)
|
let settings = await state.editor.getConfiguration(document.uri)
|
||||||
if (!settings.tailwindCSS.suggestions) return null
|
if (!settings.tailwindCSS.suggestions) return null
|
||||||
if (await isExcluded(state, document)) return null
|
if (await isExcluded(state, document)) return null
|
||||||
return doComplete(state, document, params.position, params.context)
|
let result = await doComplete(state, document, params.position, params.context)
|
||||||
|
if (!result) return result
|
||||||
|
return {
|
||||||
|
isIncomplete: result.isIncomplete,
|
||||||
|
items: result.items.map((item) => ({
|
||||||
|
...item,
|
||||||
|
data: { projectKey: JSON.stringify(projectConfig), originalData: item.data },
|
||||||
|
})),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
|
onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
|
||||||
if (!state.enabled) return null
|
if (!state.enabled) return null
|
||||||
return resolveCompletionItem(state, item)
|
return resolveCompletionItem(state, { ...item, data: item.data?.originalData })
|
||||||
},
|
},
|
||||||
async onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
|
async onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
|
||||||
if (!state.enabled) return null
|
if (!state.enabled) return null
|
||||||
|
@ -1345,6 +1321,7 @@ class TW {
|
||||||
private projects: Map<string, ProjectService>
|
private projects: Map<string, ProjectService>
|
||||||
private documentService: DocumentService
|
private documentService: DocumentService
|
||||||
public initializeParams: InitializeParams
|
public initializeParams: InitializeParams
|
||||||
|
private registrations: Promise<BulkUnregistration>
|
||||||
|
|
||||||
constructor(private connection: Connection) {
|
constructor(private connection: Connection) {
|
||||||
this.documentService = new DocumentService(this.connection)
|
this.documentService = new DocumentService(this.connection)
|
||||||
|
@ -1358,16 +1335,15 @@ class TW {
|
||||||
this.initialized = true
|
this.initialized = true
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
const workspaceFolders =
|
let workspaceFolders: Array<ProjectConfig> =
|
||||||
false &&
|
false &&
|
||||||
Array.isArray(this.initializeParams.workspaceFolders) &&
|
Array.isArray(this.initializeParams.workspaceFolders) &&
|
||||||
this.initializeParams.capabilities.workspace?.workspaceFolders
|
this.initializeParams.capabilities.workspace?.workspaceFolders
|
||||||
? this.initializeParams.workspaceFolders.map((el) => ({
|
? this.initializeParams.workspaceFolders.map((el) => ({
|
||||||
name: el.name,
|
folder: getFileFsPath(el.uri),
|
||||||
fsPath: getFileFsPath(el.uri),
|
|
||||||
}))
|
}))
|
||||||
: this.initializeParams.rootPath
|
: this.initializeParams.rootPath
|
||||||
? [{ name: '', fsPath: normalizeFileNameToFsPath(this.initializeParams.rootPath) }]
|
? [{ folder: normalizeFileNameToFsPath(this.initializeParams.rootPath) }]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
if (workspaceFolders.length === 0) {
|
if (workspaceFolders.length === 0) {
|
||||||
|
@ -1375,14 +1351,65 @@ class TW {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let configFileOrFiles = dlv(
|
||||||
|
await connection.workspace.getConfiguration('tailwindCSS'),
|
||||||
|
'experimental.configFile',
|
||||||
|
null
|
||||||
|
) as Settings['tailwindCSS']['experimental']['configFile']
|
||||||
|
|
||||||
|
if (configFileOrFiles) {
|
||||||
|
let base = workspaceFolders[0].folder
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof configFileOrFiles !== 'string' &&
|
||||||
|
(!isObject(configFileOrFiles) ||
|
||||||
|
!Object.entries(configFileOrFiles).every(([key, value]) => {
|
||||||
|
if (typeof key !== 'string') return false
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.every((item) => typeof item === 'string')
|
||||||
|
}
|
||||||
|
return typeof value === 'string'
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
console.error('Invalid `experimental.configFile` configuration, not initializing.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let configFiles =
|
||||||
|
typeof configFileOrFiles === 'string' ? { [configFileOrFiles]: '**' } : configFileOrFiles
|
||||||
|
|
||||||
|
workspaceFolders = Object.entries(configFiles).map(
|
||||||
|
([relativeConfigPath, relativeDocumentSelectorOrSelectors]) => {
|
||||||
|
return {
|
||||||
|
folder: base,
|
||||||
|
configPath: path.join(base, relativeConfigPath),
|
||||||
|
documentSelector: []
|
||||||
|
.concat(relativeDocumentSelectorOrSelectors)
|
||||||
|
.map((selector) => path.join(base, selector)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
workspaceFolders.map(async (folder) => {
|
workspaceFolders.map((projectConfig) => this.addProject(projectConfig, this.initializeParams))
|
||||||
return this.addProject(folder.fsPath, this.initializeParams)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
this.setupLSPHandlers()
|
this.setupLSPHandlers()
|
||||||
|
|
||||||
|
if (this.initializeParams.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration) {
|
||||||
|
this.connection.onDidChangeWatchedFiles(({ changes }) => {
|
||||||
|
for (let [, project] of this.projects) {
|
||||||
|
project.onFileEvents(
|
||||||
|
changes.map(({ uri, type }) => ({
|
||||||
|
file: URI.parse(uri).fsPath,
|
||||||
|
type,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.connection.onDidChangeConfiguration(async ({ settings }) => {
|
this.connection.onDidChangeConfiguration(async ({ settings }) => {
|
||||||
for (let [, project] of this.projects) {
|
for (let [, project] of this.projects) {
|
||||||
project.onUpdateSettings(settings)
|
project.onUpdateSettings(settings)
|
||||||
|
@ -1394,24 +1421,24 @@ class TW {
|
||||||
})
|
})
|
||||||
|
|
||||||
this.documentService.onDidChangeContent((change) => {
|
this.documentService.onDidChangeContent((change) => {
|
||||||
// TODO
|
this.getProject(change.document)?.provideDiagnostics(change.document)
|
||||||
const project = Array.from(this.projects.values())[0]
|
|
||||||
project?.provideDiagnostics(change.document)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addProject(folder: string, params: InitializeParams): Promise<void> {
|
private async addProject(projectConfig: ProjectConfig, params: InitializeParams): Promise<void> {
|
||||||
if (this.projects.has(folder)) {
|
let key = JSON.stringify(projectConfig)
|
||||||
await this.projects.get(folder).tryInit()
|
if (this.projects.has(key)) {
|
||||||
|
await this.projects.get(key).tryInit()
|
||||||
} else {
|
} else {
|
||||||
const project = await createProjectService(
|
const project = await createProjectService(
|
||||||
folder,
|
projectConfig,
|
||||||
this.connection,
|
this.connection,
|
||||||
params,
|
params,
|
||||||
this.documentService
|
this.documentService,
|
||||||
|
() => this.updateCapabilities()
|
||||||
)
|
)
|
||||||
|
this.projects.set(key, project)
|
||||||
await project.tryInit()
|
await project.tryInit()
|
||||||
this.projects.set(folder, project)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1424,38 +1451,78 @@ class TW {
|
||||||
this.connection.onCodeAction(this.onCodeAction.bind(this))
|
this.connection.onCodeAction(this.onCodeAction.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateCapabilities() {
|
||||||
|
if (this.registrations) {
|
||||||
|
this.registrations.then((r) => r.dispose())
|
||||||
|
}
|
||||||
|
|
||||||
|
let projects = Array.from(this.projects.values())
|
||||||
|
|
||||||
|
let capabilities = BulkRegistration.create()
|
||||||
|
|
||||||
|
capabilities.add(HoverRequest.type, { documentSelector: null })
|
||||||
|
capabilities.add(DocumentColorRequest.type, { documentSelector: null })
|
||||||
|
capabilities.add(CodeActionRequest.type, { documentSelector: null })
|
||||||
|
|
||||||
|
capabilities.add(CompletionRequest.type, {
|
||||||
|
documentSelector: null,
|
||||||
|
resolveProvider: true,
|
||||||
|
triggerCharacters: [
|
||||||
|
...TRIGGER_CHARACTERS,
|
||||||
|
...projects.map((project) => project.state.separator).filter(Boolean),
|
||||||
|
].filter(Boolean),
|
||||||
|
})
|
||||||
|
|
||||||
|
capabilities.add(DidChangeWatchedFilesNotification.type, {
|
||||||
|
watchers: projects.flatMap((project) =>
|
||||||
|
project.state.dependencies.map((file) => ({ globPattern: file }))
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
this.registrations = this.connection.client.register(capabilities)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProject(document: TextDocumentIdentifier): ProjectService {
|
||||||
|
let fallbackProject: ProjectService
|
||||||
|
for (let [key, project] of this.projects) {
|
||||||
|
let projectConfig = JSON.parse(key) as ProjectConfig
|
||||||
|
if (projectConfig.configPath) {
|
||||||
|
for (let selector of projectConfig.documentSelector) {
|
||||||
|
if (minimatch(URI.parse(document.uri).fsPath, selector)) {
|
||||||
|
return project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!fallbackProject) {
|
||||||
|
fallbackProject = project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallbackProject
|
||||||
|
}
|
||||||
|
|
||||||
async onDocumentColor(params: DocumentColorParams): Promise<ColorInformation[]> {
|
async onDocumentColor(params: DocumentColorParams): Promise<ColorInformation[]> {
|
||||||
const project = Array.from(this.projects.values())[0]
|
return this.getProject(params.textDocument)?.onDocumentColor(params) ?? []
|
||||||
return project?.onDocumentColor(params) ?? []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onColorPresentation(params: ColorPresentationParams): Promise<ColorPresentation[]> {
|
async onColorPresentation(params: ColorPresentationParams): Promise<ColorPresentation[]> {
|
||||||
const project = Array.from(this.projects.values())[0]
|
return this.getProject(params.textDocument)?.onColorPresentation(params) ?? []
|
||||||
return project?.onColorPresentation(params) ?? []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onHover(params: TextDocumentPositionParams): Promise<Hover> {
|
async onHover(params: TextDocumentPositionParams): Promise<Hover> {
|
||||||
// TODO
|
return this.getProject(params.textDocument)?.onHover(params) ?? null
|
||||||
const project = Array.from(this.projects.values())[0]
|
|
||||||
return project?.onHover(params) ?? null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onCompletion(params: CompletionParams): Promise<CompletionList> {
|
async onCompletion(params: CompletionParams): Promise<CompletionList> {
|
||||||
// TODO
|
return this.getProject(params.textDocument)?.onCompletion(params) ?? null
|
||||||
const project = Array.from(this.projects.values())[0]
|
|
||||||
return project?.onCompletion(params) ?? null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
|
async onCompletionResolve(item: CompletionItem): Promise<CompletionItem> {
|
||||||
// TODO
|
return this.projects.get(item.data.projectKey)?.onCompletionResolve(item) ?? null
|
||||||
const project = Array.from(this.projects.values())[0]
|
|
||||||
return project?.onCompletionResolve(item) ?? null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
|
onCodeAction(params: CodeActionParams): Promise<CodeAction[]> {
|
||||||
// TODO
|
return this.getProject(params.textDocument)?.onCodeAction(params) ?? null
|
||||||
const project = Array.from(this.projects.values())[0]
|
|
||||||
return project?.onCodeAction(params) ?? null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listen() {
|
listen() {
|
||||||
|
|
|
@ -59,6 +59,7 @@ export type Settings = {
|
||||||
}
|
}
|
||||||
experimental: {
|
experimental: {
|
||||||
classRegex: string[]
|
classRegex: string[]
|
||||||
|
configFile: string | Record<string, string | string[]>
|
||||||
}
|
}
|
||||||
files: {
|
files: {
|
||||||
exclude: string[]
|
exclude: string[]
|
||||||
|
|
|
@ -146,6 +146,31 @@ Class variants not in the recommended order (applies in [JIT mode](https://tailw
|
||||||
|
|
||||||
Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`**
|
Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`**
|
||||||
|
|
||||||
|
## Experimental Extension Settings
|
||||||
|
|
||||||
|
**_Experimental settings may be changed or removed at any time._**
|
||||||
|
|
||||||
|
### `tailwindCSS.experimental.configFile`
|
||||||
|
|
||||||
|
**Default: `null`**
|
||||||
|
|
||||||
|
By default the extension will automatically use the first `tailwind.config.js` or `tailwind.config.cjs` file that it can find to provide Tailwind CSS IntelliSense. Use this setting to manually specify the config file(s) yourself instead.
|
||||||
|
|
||||||
|
If your project contains a single Tailwind config file you can specify a string value:
|
||||||
|
|
||||||
|
```
|
||||||
|
"tailwindCSS.experimental.configFile": ".config/tailwind.config.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
For projects with multiple config files use an object where each key is a config file path and each value is a glob pattern (or array of glob patterns) representing the set of files that the config file applies to:
|
||||||
|
|
||||||
|
```
|
||||||
|
"tailwindCSS.experimental.configFile": {
|
||||||
|
"themes/simple/tailwind.config.js": "themes/simple/**",
|
||||||
|
"themes/neon/tailwind.config.js": "themes/neon/**"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
If you’re having issues getting the IntelliSense features to activate, there are a few things you can check:
|
If you’re having issues getting the IntelliSense features to activate, there are a few things you can check:
|
||||||
|
|
|
@ -277,6 +277,15 @@
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"scope": "language-overridable"
|
"scope": "language-overridable"
|
||||||
},
|
},
|
||||||
|
"tailwindCSS.experimental.configFile": {
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string",
|
||||||
|
"object"
|
||||||
|
],
|
||||||
|
"default": null,
|
||||||
|
"markdownDescription": "Manually specify the Tailwind config file or files that should be read to provide IntelliSense features. Can either be a single string value, or an object where each key is a config file path and each value is a glob or array of globs representing the set of files that the config file applies to."
|
||||||
|
},
|
||||||
"tailwindCSS.showPixelEquivalents": {
|
"tailwindCSS.showPixelEquivalents": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
|
|
|
@ -169,25 +169,31 @@ export async function activate(context: ExtensionContext) {
|
||||||
// e.g. "plaintext" already exists but you change it from "html" to "css"
|
// e.g. "plaintext" already exists but you change it from "html" to "css"
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
Workspace.onDidChangeConfiguration((event) => {
|
Workspace.onDidChangeConfiguration((event) => {
|
||||||
clients.forEach((client, key) => {
|
;[...clients].forEach(([key, client]) => {
|
||||||
const folder = Workspace.getWorkspaceFolder(Uri.parse(key))
|
const folder = Workspace.getWorkspaceFolder(Uri.parse(key))
|
||||||
|
let reboot = false
|
||||||
|
|
||||||
if (event.affectsConfiguration('tailwindCSS', folder)) {
|
if (event.affectsConfiguration('tailwindCSS.includeLanguages', folder)) {
|
||||||
const userLanguages = getUserLanguages(folder)
|
const userLanguages = getUserLanguages(folder)
|
||||||
if (userLanguages) {
|
if (userLanguages) {
|
||||||
const userLanguageIds = Object.keys(userLanguages)
|
const userLanguageIds = Object.keys(userLanguages)
|
||||||
const newLanguages = dedupe([...defaultLanguages, ...userLanguageIds])
|
const newLanguages = dedupe([...defaultLanguages, ...userLanguageIds])
|
||||||
if (!equal(newLanguages, languages.get(folder.uri.toString()))) {
|
if (!equal(newLanguages, languages.get(folder.uri.toString()))) {
|
||||||
languages.set(folder.uri.toString(), newLanguages)
|
languages.set(folder.uri.toString(), newLanguages)
|
||||||
|
reboot = true
|
||||||
if (client) {
|
|
||||||
clients.delete(folder.uri.toString())
|
|
||||||
client.stop()
|
|
||||||
bootWorkspaceClient(folder)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.affectsConfiguration('tailwindCSS.experimental.configFile', folder)) {
|
||||||
|
reboot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reboot && client) {
|
||||||
|
clients.delete(folder.uri.toString())
|
||||||
|
client.stop()
|
||||||
|
bootWorkspaceClient(folder)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue