Skip to content

Perform a soft reload of some project state when editing theme files #918

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

Merged
merged 2 commits into from
Mar 5, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions packages/tailwindcss-language-server/src/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export interface ProjectService {
onCodeAction(params: CodeActionParams): Promise<CodeAction[]>
onDocumentLinks(params: DocumentLinkParams): DocumentLink[]
sortClassLists(classLists: string[]): string[]

reload(): Promise<void>
}

export enum DocumentSelectorPriority {
Expand Down Expand Up @@ -975,6 +977,42 @@ export async function createProjectService(
enable() {
enabled = true
},

async reload() {
if (!state.v4) return

console.log('---- RELOADING DESIGN SYSTEM ----')
let css = await readCssFile(state.configPath)
let designSystem = await loadDesignSystem(
state.modules.tailwindcss.module,
state.configPath,
css,
)

// TODO: This is weird and should be changed
// We use Object.create so no global state is mutated until necessary
let pseudoState = Object.create(state, {
designSystem: {
value: designSystem,
},
})

let classList = designSystem.getClassList().map((className) => {
return [
className[0],
{
...className[1],
color: getColor(pseudoState, className[0]),
},
]
})

state.designSystem = designSystem
state.classList = classList as any

console.log('---- RELOADED ----')
},

state,
documentSelector() {
return documentSelector
Expand Down
92 changes: 63 additions & 29 deletions packages/tailwindcss-language-server/src/tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class TW {
isDefaultVersion: false,
},
}
},
}
)
} else {
console.log("Searching for Tailwind CSS projects in the workspace's folders.")
Expand Down Expand Up @@ -224,9 +224,10 @@ export class TW {
console.log(`[Global] Creating projects: ${JSON.stringify(workspaceDescription)}`)

const onDidChangeWatchedFiles = async (
changes: Array<{ file: string; type: FileChangeType }>,
changes: Array<{ file: string; type: FileChangeType }>
): Promise<void> => {
let needsRestart = false
let needsSoftRestart = false

changeLoop: for (let change of changes) {
let normalizedFilename = normalizePath(change.file)
Expand All @@ -242,12 +243,10 @@ export class TW {
for (let [, project] of this.projects) {
let twVersion = require('tailwindcss/package.json').version
try {
let v = require(
resolveFrom(
path.dirname(project.projectConfig.configPath),
'tailwindcss/package.json',
),
).version
let v = require(resolveFrom(
path.dirname(project.projectConfig.configPath),
'tailwindcss/package.json'
)).version
if (typeof v === 'string') {
twVersion = v
}
Expand All @@ -259,6 +258,20 @@ export class TW {
}
}

for (let [, project] of this.projects) {
if (!project.state.v4) continue

let reloadableFiles = [
project.projectConfig.configPath,
...project.projectConfig.config.entries.map((entry) => entry.path),
]

if (!changeAffectsFile(normalizedFilename, reloadableFiles)) continue

needsSoftRestart = true
break changeLoop
}

let isCssFile = minimatch(normalizedFilename, `**/${CSS_GLOB}`, {
dot: true,
})
Expand Down Expand Up @@ -300,6 +313,15 @@ export class TW {
return
}

if (needsSoftRestart) {
try {
await this.softRestart()
} catch {
this.restart()
}
return
}

for (let [, project] of this.projects) {
project.onFileEvents(changes)
}
Expand All @@ -316,11 +338,11 @@ export class TW {
.filter(
(change, changeIndex, changes) =>
changes.findIndex((c) => c.file === change.file && c.type === change.type) ===
changeIndex,
changeIndex
)

await onDidChangeWatchedFiles(normalizedChanges)
}),
})
)

let disposable = await this.connection.client.register(
Expand All @@ -331,7 +353,7 @@ export class TW {
{ globPattern: `**/${PACKAGE_LOCK_GLOB}` },
{ globPattern: `**/${CSS_GLOB}` },
],
},
}
)

this.disposables.push(disposable)
Expand Down Expand Up @@ -360,14 +382,14 @@ export class TW {
base,
(err, events) => {
onDidChangeWatchedFiles(
events.map((event) => ({ file: event.path, type: typeMap[event.type] })),
events.map((event) => ({ file: event.path, type: typeMap[event.type] }))
)
},
{
ignore: ignore.map((ignorePattern) =>
path.resolve(base, ignorePattern.replace(/^[*/]+/, '').replace(/[*/]+$/, '')),
path.resolve(base, ignorePattern.replace(/^[*/]+/, '').replace(/[*/]+$/, ''))
),
},
}
)

this.disposables.push({
Expand All @@ -388,7 +410,7 @@ export class TW {
stabilityThreshold: 100,
pollInterval: 20,
},
},
}
)

await new Promise<void>((resolve) => {
Expand All @@ -399,17 +421,17 @@ export class TW {
.on('add', (file) =>
onDidChangeWatchedFiles([
{ file: path.resolve(base, file), type: FileChangeType.Created },
]),
])
)
.on('change', (file) =>
onDidChangeWatchedFiles([
{ file: path.resolve(base, file), type: FileChangeType.Changed },
]),
])
)
.on('unlink', (file) =>
onDidChangeWatchedFiles([
{ file: path.resolve(base, file), type: FileChangeType.Deleted },
]),
])
)

this.disposables.push({
Expand All @@ -433,9 +455,9 @@ export class TW {
projectConfig,
this.initializeParams,
this.watchPatterns,
configTailwindVersionMap.get(projectConfig.configPath),
),
),
configTailwindVersionMap.get(projectConfig.configPath)
)
)
)

// init projects for documents that are _already_ open
Expand Down Expand Up @@ -465,19 +487,19 @@ export class TW {
for (let [, project] of this.projects) {
project.onUpdateSettings(settings)
}
}),
})
)

this.disposables.push(
this.connection.onShutdown(() => {
this.dispose()
}),
})
)

this.disposables.push(
this.documentService.onDidChangeContent((change) => {
this.getProject(change.document)?.provideDiagnostics(change.document)
}),
})
)

this.disposables.push(
Expand All @@ -487,7 +509,7 @@ export class TW {
project.enable()
project.tryInit()
}
}),
})
)
}

Expand All @@ -501,7 +523,7 @@ export class TW {
projectConfig: ProjectConfig,
params: InitializeParams,
watchPatterns: (patterns: string[]) => void,
tailwindVersion: string,
tailwindVersion: string
): Promise<void> {
let key = String(this.projectCounter++)
const project = await createProjectService(
Expand All @@ -524,7 +546,7 @@ export class TW {
() => this.refreshDiagnostics(),
(patterns: string[]) => watchPatterns(patterns),
tailwindVersion,
this.settingsCache.get,
this.settingsCache.get
)
this.projects.set(key, project)

Expand Down Expand Up @@ -571,11 +593,11 @@ export class TW {

private onRequest(
method: '@/tailwindCSS/sortSelection',
params: { uri: string; classLists: string[] },
params: { uri: string; classLists: string[] }
): { error: string } | { classLists: string[] }
private onRequest(
method: '@/tailwindCSS/getProject',
params: { uri: string },
params: { uri: string }
): { version: string } | null
private onRequest(method: string, params: any): any {
if (method === '@/tailwindCSS/sortSelection') {
Expand Down Expand Up @@ -776,6 +798,18 @@ export class TW {
this.initPromise = undefined
this.init()
}

async softRestart(): Promise<void> {
// Tell each v4 project to reload it's design system
for (let [, project] of this.projects) {
if (!project.state.v4) continue

// "soft"-reload the project
try {
await project.reload()
} catch {}
}
}
}

function supportsDynamicRegistration(params: InitializeParams): boolean {
Expand Down