Skip to content

Commit abb0f9f

Browse files
Merge user languages from initialization options and include languages (#1014)
* Merge user languages from initialization options and include languages * Cleanup code * Clear language boundary cache during testing * Add language mappings test
1 parent c91a63e commit abb0f9f

File tree

5 files changed

+199
-13
lines changed

5 files changed

+199
-13
lines changed

packages/tailwindcss-language-server/src/tw.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -169,18 +169,26 @@ export class TW {
169169
}
170170

171171
private async _initFolder(baseUri: URI): Promise<void> {
172+
let initUserLanguages = this.initializeParams.initializationOptions?.userLanguages ?? {}
173+
174+
if (Object.keys(initUserLanguages).length > 0) {
175+
console.warn(
176+
'Language mappings are currently set via initialization options (`userLanguages`). This is deprecated and will be removed in a future release. Please use the `tailwindCSS.includeLanguages` setting instead.',
177+
)
178+
}
179+
172180
let base = baseUri.fsPath
173181
let workspaceFolders: Array<ProjectConfig> = []
174182
let globalSettings = await this.settingsCache.get()
175183
let ignore = globalSettings.tailwindCSS.files.exclude
176184

177185
// Get user languages for the given workspace folder
178186
let folderSettings = await this.settingsCache.get(baseUri.toString())
179-
let userLanguages = folderSettings.tailwindCSS.includeLanguages
180187

181-
// Fall back to settings defined in `initializationOptions` if invalid
182-
if (!isObject(userLanguages)) {
183-
userLanguages = this.initializeParams.initializationOptions?.userLanguages ?? {}
188+
// Merge the languages from the global settings with the languages from the workspace folder
189+
let userLanguages = {
190+
...initUserLanguages,
191+
...(folderSettings.tailwindCSS.includeLanguages ?? {}),
184192
}
185193

186194
let cssFileConfigMap: Map<string, string> = new Map()

packages/tailwindcss-language-server/tests/common.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from 'vscode-languageserver-protocol'
1616
import type { ClientCapabilities, ProtocolConnection } from 'vscode-languageclient'
1717
import type { Feature } from '@tailwindcss/language-service/src/features'
18+
import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/util/getLanguageBoundaries'
1819
import { CacheMap } from '../src/cache-map'
1920

2021
type Settings = any
@@ -42,7 +43,10 @@ interface FixtureContext
4243
}
4344
}
4445

45-
async function init(fixture: string | string[]): Promise<FixtureContext> {
46+
export async function init(
47+
fixture: string | string[],
48+
options: Record<string, any> = {},
49+
): Promise<FixtureContext> {
4650
let settings = {}
4751
let docSettings = new Map<string, Settings>()
4852

@@ -158,6 +162,7 @@ async function init(fixture: string | string[]): Promise<FixtureContext> {
158162
workspaceFolders,
159163
initializationOptions: {
160164
testMode: true,
165+
...options,
161166
},
162167
} as InitializeParams)
163168

@@ -200,6 +205,9 @@ async function init(fixture: string | string[]): Promise<FixtureContext> {
200205
openingDocuments.get(params.uri)?.resolve()
201206
})
202207

208+
// This is a global cache that must be reset between tests for accurate results
209+
clearLanguageBoundariesCache()
210+
203211
let counter = 0
204212

205213
return {

packages/tailwindcss-language-server/tests/connection.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import type { ProtocolConnection } from 'vscode-languageclient/node'
55
import { Duplex } from 'node:stream'
66
import { TW } from '../src/tw'
77

8-
export async function connect() {
9-
class TestStream extends Duplex {
10-
_write(chunk: string, _encoding: string, done: () => void) {
11-
this.emit('data', chunk)
12-
done()
13-
}
14-
15-
_read(_size: number) {}
8+
class TestStream extends Duplex {
9+
_write(chunk: string, _encoding: string, done: () => void) {
10+
this.emit('data', chunk)
11+
done()
1612
}
1713

14+
_read(_size: number) {}
15+
}
16+
17+
export async function connect() {
1818
let input = new TestStream()
1919
let output = new TestStream()
2020

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import { test } from 'vitest'
2+
import { init } from '../common'
3+
import { HoverRequest } from 'vscode-languageserver'
4+
5+
test('Unknown languages do not provide completions', async ({ expect }) => {
6+
let c = await init('basic')
7+
8+
let textDocument = await c.openDocument({
9+
lang: 'some-lang',
10+
text: '<div class="bg-[#000]">',
11+
})
12+
13+
let res = await c.sendRequest(HoverRequest.type, {
14+
textDocument,
15+
position: { line: 0, character: 13 },
16+
})
17+
18+
expect(res).toEqual(null)
19+
})
20+
21+
test('Custom languages may be specified via init options (deprecated)', async ({ expect }) => {
22+
let c = await init('basic', {
23+
userLanguages: {
24+
'some-lang': 'html',
25+
},
26+
})
27+
28+
let textDocument = await c.openDocument({
29+
lang: 'some-lang',
30+
text: '<div class="bg-[#000]">',
31+
})
32+
33+
let res = await c.sendRequest(HoverRequest.type, {
34+
textDocument,
35+
position: { line: 0, character: 13 },
36+
})
37+
38+
expect(res).toEqual({
39+
contents: {
40+
language: 'css',
41+
value:
42+
'.bg-\\[\\#000\\] {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity)) /* #000000 */;\n}',
43+
},
44+
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } },
45+
})
46+
})
47+
48+
test('Custom languages may be specified via settings', async ({ expect }) => {
49+
let c = await init('basic')
50+
51+
await c.updateSettings({
52+
tailwindCSS: {
53+
includeLanguages: {
54+
'some-lang': 'html',
55+
},
56+
},
57+
})
58+
59+
let textDocument = await c.openDocument({
60+
lang: 'some-lang',
61+
text: '<div class="bg-[#000]">',
62+
})
63+
64+
let res = await c.sendRequest(HoverRequest.type, {
65+
textDocument,
66+
position: { line: 0, character: 13 },
67+
})
68+
69+
expect(res).toEqual({
70+
contents: {
71+
language: 'css',
72+
value:
73+
'.bg-\\[\\#000\\] {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity)) /* #000000 */;\n}',
74+
},
75+
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } },
76+
})
77+
})
78+
79+
test('Custom languages are merged from init options and settings', async ({ expect }) => {
80+
let c = await init('basic', {
81+
userLanguages: {
82+
'some-lang': 'html',
83+
},
84+
})
85+
86+
await c.updateSettings({
87+
tailwindCSS: {
88+
includeLanguages: {
89+
'other-lang': 'html',
90+
},
91+
},
92+
})
93+
94+
let textDocument = await c.openDocument({
95+
lang: 'some-lang',
96+
text: '<div class="bg-[#000]">',
97+
})
98+
99+
let res = await c.sendRequest(HoverRequest.type, {
100+
textDocument,
101+
position: { line: 0, character: 13 },
102+
})
103+
104+
textDocument = await c.openDocument({
105+
lang: 'other-lang',
106+
text: '<div class="bg-[#000]">',
107+
})
108+
109+
let res2 = await c.sendRequest(HoverRequest.type, {
110+
textDocument,
111+
position: { line: 0, character: 13 },
112+
})
113+
114+
expect(res).toEqual({
115+
contents: {
116+
language: 'css',
117+
value:
118+
'.bg-\\[\\#000\\] {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity)) /* #000000 */;\n}',
119+
},
120+
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } },
121+
})
122+
123+
expect(res2).toEqual({
124+
contents: {
125+
language: 'css',
126+
value:
127+
'.bg-\\[\\#000\\] {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity)) /* #000000 */;\n}',
128+
},
129+
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } },
130+
})
131+
})
132+
133+
test('Language mappings from settings take precedence', async ({ expect }) => {
134+
let c = await init('basic', {
135+
userLanguages: {
136+
'some-lang': 'css',
137+
},
138+
})
139+
140+
await c.updateSettings({
141+
tailwindCSS: {
142+
includeLanguages: {
143+
'some-lang': 'html',
144+
},
145+
},
146+
})
147+
148+
let textDocument = await c.openDocument({
149+
lang: 'some-lang',
150+
text: '<div class="bg-[#000]">',
151+
})
152+
153+
let res = await c.sendRequest(HoverRequest.type, {
154+
textDocument,
155+
position: { line: 0, character: 13 },
156+
})
157+
158+
expect(res).toEqual({
159+
contents: {
160+
language: 'css',
161+
value:
162+
'.bg-\\[\\#000\\] {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity)) /* #000000 */;\n}',
163+
},
164+
range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } },
165+
})
166+
})

packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts

+4
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ let vueLexer = moo.states(vueStates)
139139

140140
let cache = new Cache<string, LanguageBoundary[] | null>({ max: 25, maxAge: 1000 })
141141

142+
export function clearLanguageBoundariesCache() {
143+
cache.clear()
144+
}
145+
142146
export function getLanguageBoundaries(
143147
state: State,
144148
doc: TextDocument,

0 commit comments

Comments
 (0)