Skip to content

Commit 3836cbf

Browse files
author
Brad Cornes
committed
add includeLanguages setting and remove default language client
1 parent 6af6797 commit 3836cbf

File tree

10 files changed

+175
-97
lines changed

10 files changed

+175
-97
lines changed

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
"type": "boolean",
5656
"default": false,
5757
"description": ""
58+
},
59+
"tailwindCSS.includeLanguages": {
60+
"type": "object",
61+
"default": {}
5862
}
5963
}
6064
}

src/extension.ts

Lines changed: 100 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ import {
1818
TransportKind,
1919
} from 'vscode-languageclient'
2020
import { registerConfigErrorHandler } from './lib/registerConfigErrorHandler'
21-
import { LANGUAGES } from './lib/languages'
21+
import { DEFAULT_LANGUAGES } from './lib/languages'
22+
import isObject from './util/isObject'
23+
import { dedupe, equal } from './util/array'
24+
25+
const CLIENT_ID = 'tailwindcss-intellisense'
26+
const CLIENT_NAME = 'Tailwind CSS IntelliSense'
2227

23-
let defaultClient: LanguageClient
2428
let clients: Map<string, LanguageClient> = new Map()
29+
let languages: Map<string, string[]> = new Map()
2530

2631
let _sortedWorkspaceFolders: string[] | undefined
2732
function sortedWorkspaceFolders(): string[] {
@@ -60,48 +65,102 @@ function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
6065
return folder
6166
}
6267

68+
function getUserLanguages(folder?: WorkspaceFolder): Record<string, string> {
69+
const langs = Workspace.getConfiguration('tailwindCSS', folder)
70+
.includeLanguages
71+
return isObject(langs) ? langs : {}
72+
}
73+
6374
export function activate(context: ExtensionContext) {
64-
let module = context.asAbsolutePath(
65-
path.join('dist', 'server', 'index.js')
66-
)
67-
let outputChannel: OutputChannel = Window.createOutputChannel(
68-
'lsp-multi-server-example'
69-
)
75+
let module = context.asAbsolutePath(path.join('dist', 'server', 'index.js'))
76+
let outputChannel: OutputChannel = Window.createOutputChannel(CLIENT_ID)
7077

71-
function didOpenTextDocument(document: TextDocument): void {
72-
// We are only interested in language mode text
73-
if (
74-
LANGUAGES.indexOf(document.languageId) === -1 ||
75-
(document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled')
76-
) {
78+
// TODO: check if the actual language MAPPING changed
79+
// not just the language IDs
80+
// e.g. "plaintext" already exists but you change it from "html" to "css"
81+
Workspace.onDidChangeConfiguration((event) => {
82+
clients.forEach((client, key) => {
83+
const folder = Workspace.getWorkspaceFolder(Uri.parse(key))
84+
85+
if (event.affectsConfiguration('tailwindCSS', folder)) {
86+
const userLanguages = getUserLanguages(folder)
87+
if (userLanguages) {
88+
const userLanguageIds = Object.keys(userLanguages)
89+
const newLanguages = dedupe([
90+
...DEFAULT_LANGUAGES,
91+
...userLanguageIds,
92+
])
93+
if (!equal(newLanguages, languages.get(folder.uri.toString()))) {
94+
languages.set(folder.uri.toString(), newLanguages)
95+
96+
if (client) {
97+
clients.delete(folder.uri.toString())
98+
client.stop()
99+
bootWorkspaceClient(folder)
100+
}
101+
}
102+
}
103+
}
104+
})
105+
})
106+
107+
function bootWorkspaceClient(folder: WorkspaceFolder) {
108+
if (clients.has(folder.uri.toString())) {
77109
return
78110
}
79111

80-
let uri = document.uri
81-
// Untitled files go to a default client.
82-
if (uri.scheme === 'untitled' && !defaultClient) {
83-
let debugOptions = { execArgv: ['--nolazy', '--inspect=6010'] }
84-
let serverOptions = {
85-
run: { module, transport: TransportKind.ipc },
86-
debug: { module, transport: TransportKind.ipc, options: debugOptions },
87-
}
88-
let clientOptions: LanguageClientOptions = {
89-
documentSelector: LANGUAGES.map((language) => ({
90-
scheme: 'untitled',
112+
// placeholder so we don't boot another server before this one is ready
113+
clients.set(folder.uri.toString(), null)
114+
115+
let debugOptions = {
116+
execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`],
117+
}
118+
let serverOptions = {
119+
run: { module, transport: TransportKind.ipc },
120+
debug: {
121+
module,
122+
transport: TransportKind.ipc,
123+
options: debugOptions,
124+
},
125+
}
126+
let clientOptions: LanguageClientOptions = {
127+
documentSelector: languages
128+
.get(folder.uri.toString())
129+
.map((language) => ({
130+
scheme: 'file',
91131
language,
132+
pattern: `${folder.uri.fsPath}/**/*`,
92133
})),
93-
diagnosticCollectionName: 'lsp-multi-server-example',
94-
outputChannel: outputChannel,
95-
}
96-
defaultClient = new LanguageClient(
97-
'lsp-multi-server-example',
98-
'LSP Multi Server Example',
99-
serverOptions,
100-
clientOptions
101-
)
102-
defaultClient.start()
134+
diagnosticCollectionName: CLIENT_ID,
135+
workspaceFolder: folder,
136+
outputChannel: outputChannel,
137+
middleware: {},
138+
initializationOptions: {
139+
userLanguages: getUserLanguages(folder),
140+
},
141+
}
142+
let client = new LanguageClient(
143+
CLIENT_ID,
144+
CLIENT_NAME,
145+
serverOptions,
146+
clientOptions
147+
)
148+
149+
client.onReady().then(() => {
150+
registerConfigErrorHandler(client)
151+
})
152+
153+
client.start()
154+
clients.set(folder.uri.toString(), client)
155+
}
156+
157+
function didOpenTextDocument(document: TextDocument): void {
158+
// We are only interested in language mode text
159+
if (document.uri.scheme !== 'file') {
103160
return
104161
}
162+
163+
let uri = document.uri
105164
let folder = Workspace.getWorkspaceFolder(uri)
106165
// Files outside a folder can't be handled. This might depend on the language.
107166
// Single file languages like JSON might handle files outside the workspace folders.
@@ -111,39 +170,14 @@ export function activate(context: ExtensionContext) {
111170
// If we have nested workspace folders we only start a server on the outer most workspace folder.
112171
folder = getOuterMostWorkspaceFolder(folder)
113172

114-
if (!clients.has(folder.uri.toString())) {
115-
let debugOptions = {
116-
execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`],
117-
}
118-
let serverOptions = {
119-
run: { module, transport: TransportKind.ipc },
120-
debug: { module, transport: TransportKind.ipc, options: debugOptions },
121-
}
122-
let clientOptions: LanguageClientOptions = {
123-
documentSelector: LANGUAGES.map((language) => ({
124-
scheme: 'file',
125-
language,
126-
pattern: `${folder.uri.fsPath}/**/*`,
127-
})),
128-
diagnosticCollectionName: 'lsp-multi-server-example',
129-
workspaceFolder: folder,
130-
outputChannel: outputChannel,
131-
middleware: {},
132-
}
133-
let client = new LanguageClient(
134-
'lsp-multi-server-example',
135-
'LSP Multi Server Example',
136-
serverOptions,
137-
clientOptions
173+
if (!languages.has(folder.uri.toString())) {
174+
languages.set(
175+
folder.uri.toString(),
176+
dedupe([...DEFAULT_LANGUAGES, ...Object.keys(getUserLanguages())])
138177
)
139-
140-
client.onReady().then(() => {
141-
registerConfigErrorHandler(client)
142-
})
143-
144-
client.start()
145-
clients.set(folder.uri.toString(), client)
146178
}
179+
180+
bootWorkspaceClient(folder)
147181
}
148182

149183
Workspace.onDidOpenTextDocument(didOpenTextDocument)
@@ -161,9 +195,6 @@ export function activate(context: ExtensionContext) {
161195

162196
export function deactivate(): Thenable<void> {
163197
let promises: Thenable<void>[] = []
164-
if (defaultClient) {
165-
promises.push(defaultClient.stop())
166-
}
167198
for (let client of clients.values()) {
168199
promises.push(client.stop())
169200
}

src/lib/languages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const LANGUAGES = [
1+
export const DEFAULT_LANGUAGES = [
22
// html
33
'aspnetcorerazor',
44
'blade',

src/lsp/providers/completionProvider.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,13 @@ function provideClassNameCompletions(
191191
let doc = state.editor.documents.get(params.textDocument.uri)
192192

193193
if (
194-
isHtmlContext(doc, params.position) ||
195-
isJsContext(doc, params.position)
194+
isHtmlContext(state, doc, params.position) ||
195+
isJsContext(state, doc, params.position)
196196
) {
197197
return provideClassAttributeCompletions(state, params)
198198
}
199199

200-
if (isCssContext(doc, params.position)) {
200+
if (isCssContext(state, doc, params.position)) {
201201
return provideAtApplyCompletions(state, params)
202202
}
203203

@@ -210,7 +210,7 @@ function provideCssHelperCompletions(
210210
): CompletionList {
211211
let doc = state.editor.documents.get(textDocument.uri)
212212

213-
if (!isCssContext(doc, position)) {
213+
if (!isCssContext(state, doc, position)) {
214214
return null
215215
}
216216

@@ -318,7 +318,7 @@ function provideTailwindDirectiveCompletions(
318318
): CompletionList {
319319
let doc = state.editor.documents.get(textDocument.uri)
320320

321-
if (!isCssContext(doc, position)) {
321+
if (!isCssContext(state, doc, position)) {
322322
return null
323323
}
324324

@@ -409,7 +409,7 @@ function provideVariantsDirectiveCompletions(
409409
): CompletionList {
410410
let doc = state.editor.documents.get(textDocument.uri)
411411

412-
if (!isCssContext(doc, position)) {
412+
if (!isCssContext(state, doc, position)) {
413413
return null
414414
}
415415

@@ -457,7 +457,7 @@ function provideScreenDirectiveCompletions(
457457
): CompletionList {
458458
let doc = state.editor.documents.get(textDocument.uri)
459459

460-
if (!isCssContext(doc, position)) {
460+
if (!isCssContext(state, doc, position)) {
461461
return null
462462
}
463463

@@ -505,7 +505,7 @@ function provideCssDirectiveCompletions(
505505
): CompletionList {
506506
let doc = state.editor.documents.get(textDocument.uri)
507507

508-
if (!isCssContext(doc, position)) {
508+
if (!isCssContext(state, doc, position)) {
509509
return null
510510
}
511511

@@ -600,9 +600,9 @@ async function provideEmmetCompletions(
600600

601601
let doc = state.editor.documents.get(textDocument.uri)
602602

603-
const syntax = isHtmlContext(doc, position)
603+
const syntax = isHtmlContext(state, doc, position)
604604
? 'html'
605-
: isJsContext(doc, position)
605+
: isJsContext(state, doc, position)
606606
? 'jsx'
607607
: null
608608

src/lsp/providers/hoverProvider.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function provideCssHelperHover(
2727
): Hover {
2828
let doc = state.editor.documents.get(textDocument.uri)
2929

30-
if (!isCssContext(doc, position)) return null
30+
if (!isCssContext(state, doc, position)) return null
3131

3232
const line = doc.getText({
3333
start: { line: position.line, character: 0 },
@@ -81,7 +81,11 @@ function provideClassAttributeHover(
8181
): Hover {
8282
let doc = state.editor.documents.get(textDocument.uri)
8383

84-
if (!isHtmlContext(doc, position) && !isJsContext(doc, position)) return null
84+
if (
85+
!isHtmlContext(state, doc, position) &&
86+
!isJsContext(state, doc, position)
87+
)
88+
return null
8589

8690
let hovered = getClassNameAtPosition(doc, position)
8791
if (!hovered) return null
@@ -111,7 +115,7 @@ function provideAtApplyHover(
111115
): Hover {
112116
let doc = state.editor.documents.get(textDocument.uri)
113117

114-
if (!isCssContext(doc, position)) return null
118+
if (!isCssContext(state, doc, position)) return null
115119

116120
const classNames = findClassNamesInRange(doc, {
117121
start: { line: Math.max(position.line - 10, 0), character: 0 },

src/lsp/server.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ let connection = createConnection(ProposedFeatures.all)
3232
let documents = new TextDocuments()
3333
let workspaceFolder: string | null
3434

35-
const defaultSettings: Settings = { emmetCompletions: false }
35+
const defaultSettings: Settings = {
36+
emmetCompletions: false,
37+
includeLanguages: {},
38+
}
3639
let globalSettings: Settings = defaultSettings
3740
let documentSettings: Map<string, Settings> = new Map()
3841

@@ -53,6 +56,11 @@ connection.onInitialize(
5356
documents,
5457
documentSettings,
5558
globalSettings,
59+
userLanguages:
60+
params.initializationOptions &&
61+
params.initializationOptions.userLanguages
62+
? params.initializationOptions.userLanguages
63+
: {},
5664
capabilities: {
5765
configuration:
5866
capabilities.workspace && !!capabilities.workspace.configuration,

src/lsp/util/css.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TextDocument, Position } from 'vscode-languageserver'
22
import { isInsideTag, isVueDoc, isSvelteDoc } from './html'
3+
import { State } from './state'
34

45
export const CSS_LANGUAGES = [
56
'css',
@@ -10,12 +11,20 @@ export const CSS_LANGUAGES = [
1011
'stylus',
1112
]
1213

13-
function isCssDoc(doc: TextDocument): boolean {
14-
return CSS_LANGUAGES.indexOf(doc.languageId) !== -1
14+
function isCssDoc(state: State, doc: TextDocument): boolean {
15+
const userCssLanguages = Object.keys(
16+
state.editor.userLanguages
17+
).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang]))
18+
19+
return [...CSS_LANGUAGES, ...userCssLanguages].indexOf(doc.languageId) !== -1
1520
}
1621

17-
export function isCssContext(doc: TextDocument, position: Position): boolean {
18-
if (isCssDoc(doc)) {
22+
export function isCssContext(
23+
state: State,
24+
doc: TextDocument,
25+
position: Position
26+
): boolean {
27+
if (isCssDoc(state, doc)) {
1928
return true
2029
}
2130

0 commit comments

Comments
 (0)