Add automatic support for multi-config workspaces, including `@config` resolution (#633)

* wip

* wip

* Boot client if a CSS file contains `@config`

* wip

* Check document exists

* wip

* Fix duplicate document selector

* wip

* Use enum for document selector priorities

* Delete unused functions

* Remove unused state type

* Share glob patterns

* Update config file glob

* fix logs

* Fix filename checks on Windows

* Don't show error popups

* wip

* handle negated content paths

* Handle non-tailwind dependency installs

* add package root to document selectors

* tidy

* wip

* dedupe document selectors

* Fix `@config` regex

* Fix document selectors when using `experimental.configFile`

* Remove log
master
Brad Cornes 2022-10-18 20:35:02 +01:00 committed by GitHub
parent 68750d859b
commit b26e122fac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 915 additions and 352 deletions

View File

@ -0,0 +1,3 @@
export const CONFIG_GLOB = '{tailwind,tailwind.config,tailwind.*.config,tailwind.config.*}.{js,cjs}'
export const PACKAGE_LOCK_GLOB = '{package-lock.json,yarn.lock,pnpm-lock.yaml}'
export const CSS_GLOB = '*.{css,scss,sass,less,pcss}'

View File

@ -20,15 +20,3 @@ export function clearDiagnostics(state: State, document: TextDocument): void {
diagnostics: [], diagnostics: [],
}) })
} }
export function clearAllDiagnostics(state: State): void {
state.editor?.documents.all().forEach((document) => {
clearDiagnostics(state, document)
})
}
export function updateAllDiagnostics(state: State): void {
state.editor?.documents.all().forEach((document) => {
provideDiagnostics(state, document)
})
}

File diff suppressed because it is too large Load Diff

View File

@ -25,11 +25,11 @@ export function showError(
message: string = 'Tailwind CSS' message: string = 'Tailwind CSS'
): void { ): void {
console.error(formatError(message, err)) console.error(formatError(message, err))
if (!(err instanceof SilentError)) { // if (!(err instanceof SilentError)) {
connection.sendNotification('@/tailwindCSS/error', { // connection.sendNotification('@/tailwindCSS/error', {
message: formatError(message, err, false), // message: formatError(message, err, false),
}) // })
} // }
} }
export function SilentError(message: string) { export function SilentError(message: string) {

View File

@ -24,6 +24,7 @@ async function getDiagnosticsFromCodeActionParams(
only?: DiagnosticKind[] only?: DiagnosticKind[]
): Promise<AugmentedDiagnostic[]> { ): Promise<AugmentedDiagnostic[]> {
let document = state.editor.documents.get(params.textDocument.uri) let document = state.editor.documents.get(params.textDocument.uri)
if (!document) return []
let diagnostics = await doValidate(state, document, only) let diagnostics = await doValidate(state, document, only)
return params.context.diagnostics return params.context.diagnostics
@ -40,6 +41,10 @@ async function getDiagnosticsFromCodeActionParams(
} }
export async function doCodeActions(state: State, params: CodeActionParams): Promise<CodeAction[]> { export async function doCodeActions(state: State, params: CodeActionParams): Promise<CodeAction[]> {
if (!state.enabled) {
return []
}
let diagnostics = await getDiagnosticsFromCodeActionParams( let diagnostics = await getDiagnosticsFromCodeActionParams(
state, state,
params, params,

View File

@ -25,6 +25,7 @@ export async function provideInvalidApplyCodeActions(
diagnostic: InvalidApplyDiagnostic diagnostic: InvalidApplyDiagnostic
): Promise<CodeAction[]> { ): Promise<CodeAction[]> {
let document = state.editor.documents.get(params.textDocument.uri) let document = state.editor.documents.get(params.textDocument.uri)
if (!document) return []
let documentText = getTextWithoutComments(document, 'css') let documentText = getTextWithoutComments(document, 'css')
let cssRange: Range let cssRange: Range
let cssText = documentText let cssText = documentText

View File

@ -50,29 +50,3 @@ export async function doValidate(
] ]
: [] : []
} }
export async function provideDiagnostics(state: State, document: TextDocument) {
// state.editor.connection.sendDiagnostics({
// uri: document.uri,
// diagnostics: await doValidate(state, document),
// })
}
export function clearDiagnostics(state: State, document: TextDocument): void {
// state.editor.connection.sendDiagnostics({
// uri: document.uri,
// diagnostics: [],
// })
}
export function clearAllDiagnostics(state: State): void {
state.editor.documents.all().forEach((document) => {
clearDiagnostics(state, document)
})
}
export function updateAllDiagnostics(state: State): void {
state.editor.documents.all().forEach((document) => {
provideDiagnostics(state, document)
})
}

View File

@ -21,7 +21,6 @@ export type EditorState = {
connection: Connection connection: Connection
folder: string folder: string
documents: TextDocuments<TextDocument> documents: TextDocuments<TextDocument>
globalSettings: Settings
userLanguages: Record<string, string> userLanguages: Record<string, string>
capabilities: { capabilities: {
configuration: boolean configuration: boolean

View File

@ -42,14 +42,13 @@ import isObject from 'tailwindcss-language-service/src/util/isObject'
import { dedupe, equal } from 'tailwindcss-language-service/src/util/array' import { dedupe, equal } from 'tailwindcss-language-service/src/util/array'
import namedColors from 'color-name' import namedColors from 'color-name'
import minimatch from 'minimatch' import minimatch from 'minimatch'
import { CONFIG_GLOB, CSS_GLOB } from 'tailwindcss-language-server/src/lib/constants'
const colorNames = Object.keys(namedColors) const colorNames = Object.keys(namedColors)
const CLIENT_ID = 'tailwindcss-intellisense' const CLIENT_ID = 'tailwindcss-intellisense'
const CLIENT_NAME = 'Tailwind CSS IntelliSense' const CLIENT_NAME = 'Tailwind CSS IntelliSense'
const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
let clients: Map<string, LanguageClient> = new Map() let clients: Map<string, LanguageClient> = new Map()
let languages: Map<string, string[]> = new Map() let languages: Map<string, string[]> = new Map()
let searchedFolders: Set<string> = new Set() let searchedFolders: Set<string> = new Set()
@ -131,6 +130,11 @@ function mergeExcludes(settings: WorkspaceConfiguration, scope: ConfigurationSco
} }
} }
async function fileContainsAtConfig(uri: Uri) {
let contents = (await Workspace.fs.readFile(uri)).toString()
return /@config\s*['"]/.test(contents)
}
export async function activate(context: ExtensionContext) { export async function activate(context: ExtensionContext) {
let module = context.asAbsolutePath(path.join('dist', 'server.js')) let module = context.asAbsolutePath(path.join('dist', 'server.js'))
let prod = path.join('dist', 'tailwindServer.js') let prod = path.join('dist', 'tailwindServer.js')
@ -206,20 +210,36 @@ export async function activate(context: ExtensionContext) {
// ) // )
// ) // )
let watcher = Workspace.createFileSystemWatcher(`**/${CONFIG_FILE_GLOB}`, false, true, true) let configWatcher = Workspace.createFileSystemWatcher(`**/${CONFIG_GLOB}`, false, true, true)
watcher.onDidCreate((uri) => { configWatcher.onDidCreate((uri) => {
let folder = Workspace.getWorkspaceFolder(uri) let folder = Workspace.getWorkspaceFolder(uri)
if (!folder) { if (!folder || isExcluded(uri.fsPath, folder)) {
return return
} }
if (!isExcluded(uri.fsPath, folder)) { folder = getOuterMostWorkspaceFolder(folder)
bootWorkspaceClient(folder)
})
context.subscriptions.push(configWatcher)
let cssWatcher = Workspace.createFileSystemWatcher(`**/${CSS_GLOB}`, false, false, true)
async function bootClientIfCssFileContainsAtConfig(uri: Uri) {
let folder = Workspace.getWorkspaceFolder(uri)
if (!folder || isExcluded(uri.fsPath, folder)) {
return
}
if (await fileContainsAtConfig(uri)) {
folder = getOuterMostWorkspaceFolder(folder) folder = getOuterMostWorkspaceFolder(folder)
bootWorkspaceClient(folder) bootWorkspaceClient(folder)
} }
}) }
context.subscriptions.push(watcher) cssWatcher.onDidCreate(bootClientIfCssFileContainsAtConfig)
cssWatcher.onDidChange(bootClientIfCssFileContainsAtConfig)
context.subscriptions.push(cssWatcher)
// TODO: check if the actual language MAPPING changed // TODO: check if the actual language MAPPING changed
// not just the language IDs // not just the language IDs
@ -607,16 +627,27 @@ export async function activate(context: ExtensionContext) {
searchedFolders.add(folder.uri.toString()) searchedFolders.add(folder.uri.toString())
let [configFile] = await Workspace.findFiles( let [configFile] = await Workspace.findFiles(
new RelativePattern(folder, `**/${CONFIG_FILE_GLOB}`), new RelativePattern(folder, `**/${CONFIG_GLOB}`),
`{${getExcludePatterns(folder).join(',')}}`, `{${getExcludePatterns(folder).join(',')}}`,
1 1
) )
if (!configFile) { if (configFile) {
bootWorkspaceClient(folder)
return return
} }
bootWorkspaceClient(folder) let cssFiles = await Workspace.findFiles(
new RelativePattern(folder, `**/${CSS_GLOB}`),
`{${getExcludePatterns(folder).join(',')}}`
)
for (let cssFile of cssFiles) {
if (await fileContainsAtConfig(cssFile)) {
bootWorkspaceClient(folder)
return
}
}
} }
context.subscriptions.push(Workspace.onDidOpenTextDocument(didOpenTextDocument)) context.subscriptions.push(Workspace.onDidOpenTextDocument(didOpenTextDocument))