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: [],
})
}
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'
): void {
console.error(formatError(message, err))
if (!(err instanceof SilentError)) {
connection.sendNotification('@/tailwindCSS/error', {
message: formatError(message, err, false),
})
}
// if (!(err instanceof SilentError)) {
// connection.sendNotification('@/tailwindCSS/error', {
// message: formatError(message, err, false),
// })
// }
}
export function SilentError(message: string) {

View File

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

View File

@ -25,6 +25,7 @@ export async function provideInvalidApplyCodeActions(
diagnostic: InvalidApplyDiagnostic
): Promise<CodeAction[]> {
let document = state.editor.documents.get(params.textDocument.uri)
if (!document) return []
let documentText = getTextWithoutComments(document, 'css')
let cssRange: Range
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
folder: string
documents: TextDocuments<TextDocument>
globalSettings: Settings
userLanguages: Record<string, string>
capabilities: {
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 namedColors from 'color-name'
import minimatch from 'minimatch'
import { CONFIG_GLOB, CSS_GLOB } from 'tailwindcss-language-server/src/lib/constants'
const colorNames = Object.keys(namedColors)
const CLIENT_ID = 'tailwindcss-intellisense'
const CLIENT_NAME = 'Tailwind CSS IntelliSense'
const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
let clients: Map<string, LanguageClient> = new Map()
let languages: Map<string, string[]> = new Map()
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) {
let module = context.asAbsolutePath(path.join('dist', 'server.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)
if (!folder) {
if (!folder || isExcluded(uri.fsPath, folder)) {
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)
bootWorkspaceClient(folder)
}
})
}
context.subscriptions.push(watcher)
cssWatcher.onDidCreate(bootClientIfCssFileContainsAtConfig)
cssWatcher.onDidChange(bootClientIfCssFileContainsAtConfig)
context.subscriptions.push(cssWatcher)
// TODO: check if the actual language MAPPING changed
// not just the language IDs
@ -607,16 +627,27 @@ export async function activate(context: ExtensionContext) {
searchedFolders.add(folder.uri.toString())
let [configFile] = await Workspace.findFiles(
new RelativePattern(folder, `**/${CONFIG_FILE_GLOB}`),
new RelativePattern(folder, `**/${CONFIG_GLOB}`),
`{${getExcludePatterns(folder).join(',')}}`,
1
)
if (!configFile) {
if (configFile) {
bootWorkspaceClient(folder)
return
}
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))