tailwind-ctp-intellisense @master -
refs -
log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
add includeLanguages setting and remove default language client
10 changed files, 175 additions(+), 97 deletions(-)
diff --git a/package.json b/package.json
index 1e563a3008546894cbba855f1937c547abe85504..916beadc59c8d1402921342a378c73e4831b8d06 100755
--- a/package.json
+++ b/package.json
@@ -55,6 +55,10 @@ "tailwindCSS.emmetCompletions": {
"type": "boolean",
"default": false,
"description": ""
+ },
+ "tailwindCSS.includeLanguages": {
+ "type": "object",
+ "default": {}
}
}
}
diff --git a/src/extension.ts b/src/extension.ts
index 0adfcd1d8374b5cf8dd5dde3b92b091e3caffdf0..2497372df113002f18b7bc1ec6a65a46d054c62e 100755
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -18,10 +18,15 @@ LanguageClientOptions,
TransportKind,
} from 'vscode-languageclient'
import { registerConfigErrorHandler } from './lib/registerConfigErrorHandler'
-import { LANGUAGES } from './lib/languages'
+import { DEFAULT_LANGUAGES } from './lib/languages'
+import isObject from './util/isObject'
+import { dedupe, equal } from './util/array'
+
+const CLIENT_ID = 'tailwindcss-intellisense'
+const CLIENT_NAME = 'Tailwind CSS IntelliSense'
-let defaultClient: LanguageClient
let clients: Map<string, LanguageClient> = new Map()
+let languages: Map<string, string[]> = new Map()
let _sortedWorkspaceFolders: string[] | undefined
function sortedWorkspaceFolders(): string[] {
@@ -60,48 +65,102 @@ }
return folder
}
+function getUserLanguages(folder?: WorkspaceFolder): Record<string, string> {
+ const langs = Workspace.getConfiguration('tailwindCSS', folder)
+ .includeLanguages
+ return isObject(langs) ? langs : {}
+}
+
export function activate(context: ExtensionContext) {
- let module = context.asAbsolutePath(
- path.join('dist', 'server', 'index.js')
- )
- let outputChannel: OutputChannel = Window.createOutputChannel(
- 'lsp-multi-server-example'
- )
+ let module = context.asAbsolutePath(path.join('dist', 'server', 'index.js'))
+ let outputChannel: OutputChannel = Window.createOutputChannel(CLIENT_ID)
+
+ // TODO: check if the actual language MAPPING changed
+ // not just the language IDs
+ // e.g. "plaintext" already exists but you change it from "html" to "css"
+ Workspace.onDidChangeConfiguration((event) => {
+ clients.forEach((client, key) => {
+ const folder = Workspace.getWorkspaceFolder(Uri.parse(key))
+
+ if (event.affectsConfiguration('tailwindCSS', folder)) {
+ const userLanguages = getUserLanguages(folder)
+ if (userLanguages) {
+ const userLanguageIds = Object.keys(userLanguages)
+ const newLanguages = dedupe([
+ ...DEFAULT_LANGUAGES,
+ ...userLanguageIds,
+ ])
+ if (!equal(newLanguages, languages.get(folder.uri.toString()))) {
+ languages.set(folder.uri.toString(), newLanguages)
+
+ if (client) {
+ clients.delete(folder.uri.toString())
+ client.stop()
+ bootWorkspaceClient(folder)
+ }
+ }
+ }
+ }
+ })
+ })
- function didOpenTextDocument(document: TextDocument): void {
- // We are only interested in language mode text
- if (
- LANGUAGES.indexOf(document.languageId) === -1 ||
- (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled')
- ) {
+ function bootWorkspaceClient(folder: WorkspaceFolder) {
+ if (clients.has(folder.uri.toString())) {
return
}
- let uri = document.uri
- // Untitled files go to a default client.
- if (uri.scheme === 'untitled' && !defaultClient) {
- let debugOptions = { execArgv: ['--nolazy', '--inspect=6010'] }
- let serverOptions = {
- run: { module, transport: TransportKind.ipc },
- debug: { module, transport: TransportKind.ipc, options: debugOptions },
- }
- let clientOptions: LanguageClientOptions = {
- documentSelector: LANGUAGES.map((language) => ({
- scheme: 'untitled',
+ // placeholder so we don't boot another server before this one is ready
+ clients.set(folder.uri.toString(), null)
+
+ let debugOptions = {
+ execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`],
+ }
+ let serverOptions = {
+ run: { module, transport: TransportKind.ipc },
+ debug: {
+ module,
+ transport: TransportKind.ipc,
+ options: debugOptions,
+ },
+ }
+ let clientOptions: LanguageClientOptions = {
+ documentSelector: languages
+ .get(folder.uri.toString())
+ .map((language) => ({
+ scheme: 'file',
language,
+ pattern: `${folder.uri.fsPath}/**/*`,
})),
- diagnosticCollectionName: 'lsp-multi-server-example',
- outputChannel: outputChannel,
- }
- defaultClient = new LanguageClient(
- 'lsp-multi-server-example',
- 'LSP Multi Server Example',
- serverOptions,
- clientOptions
- )
- defaultClient.start()
+ diagnosticCollectionName: CLIENT_ID,
+ workspaceFolder: folder,
+ outputChannel: outputChannel,
+ middleware: {},
+ initializationOptions: {
+ userLanguages: getUserLanguages(folder),
+ },
+ }
+ let client = new LanguageClient(
+ CLIENT_ID,
+ CLIENT_NAME,
+ serverOptions,
+ clientOptions
+ )
+
+ client.onReady().then(() => {
+ registerConfigErrorHandler(client)
+ })
+
+ client.start()
+ clients.set(folder.uri.toString(), client)
+ }
+
+ function didOpenTextDocument(document: TextDocument): void {
+ // We are only interested in language mode text
+ if (document.uri.scheme !== 'file') {
return
}
+
+ let uri = document.uri
let folder = Workspace.getWorkspaceFolder(uri)
// Files outside a folder can't be handled. This might depend on the language.
// Single file languages like JSON might handle files outside the workspace folders.
@@ -111,39 +170,14 @@ }
// If we have nested workspace folders we only start a server on the outer most workspace folder.
folder = getOuterMostWorkspaceFolder(folder)
- if (!clients.has(folder.uri.toString())) {
- let debugOptions = {
- execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`],
- }
- let serverOptions = {
- run: { module, transport: TransportKind.ipc },
- debug: { module, transport: TransportKind.ipc, options: debugOptions },
- }
- let clientOptions: LanguageClientOptions = {
- documentSelector: LANGUAGES.map((language) => ({
- scheme: 'file',
- language,
- pattern: `${folder.uri.fsPath}/**/*`,
- })),
- diagnosticCollectionName: 'lsp-multi-server-example',
- workspaceFolder: folder,
- outputChannel: outputChannel,
- middleware: {},
- }
- let client = new LanguageClient(
- 'lsp-multi-server-example',
- 'LSP Multi Server Example',
- serverOptions,
- clientOptions
+ if (!languages.has(folder.uri.toString())) {
+ languages.set(
+ folder.uri.toString(),
+ dedupe([...DEFAULT_LANGUAGES, ...Object.keys(getUserLanguages())])
)
+ }
- client.onReady().then(() => {
- registerConfigErrorHandler(client)
- })
-
- client.start()
- clients.set(folder.uri.toString(), client)
- }
+ bootWorkspaceClient(folder)
}
Workspace.onDidOpenTextDocument(didOpenTextDocument)
@@ -161,9 +195,6 @@ }
export function deactivate(): Thenable<void> {
let promises: Thenable<void>[] = []
- if (defaultClient) {
- promises.push(defaultClient.stop())
- }
for (let client of clients.values()) {
promises.push(client.stop())
}
diff --git a/src/lib/languages.ts b/src/lib/languages.ts
index 8e1b859d9d8ed6f99e6bdc74ff43d75551faed42..b9238edde46be72d6b7a5db044b063e57666efff 100644
--- a/src/lib/languages.ts
+++ b/src/lib/languages.ts
@@ -1,4 +1,4 @@
-export const LANGUAGES = [
+export const DEFAULT_LANGUAGES = [
// html
'aspnetcorerazor',
'blade',
diff --git a/src/lsp/providers/completionProvider.ts b/src/lsp/providers/completionProvider.ts
index f1e9130abb7d978a454c163c3d289daa4184f067..4dc374518e5e6b8e9830031c19a26afb1cbb5cb6 100644
--- a/src/lsp/providers/completionProvider.ts
+++ b/src/lsp/providers/completionProvider.ts
@@ -191,13 +191,13 @@ ): CompletionList {
let doc = state.editor.documents.get(params.textDocument.uri)
if (
- isHtmlContext(doc, params.position) ||
- isJsContext(doc, params.position)
+ isHtmlContext(state, doc, params.position) ||
+ isJsContext(state, doc, params.position)
) {
return provideClassAttributeCompletions(state, params)
}
- if (isCssContext(doc, params.position)) {
+ if (isCssContext(state, doc, params.position)) {
return provideAtApplyCompletions(state, params)
}
@@ -210,7 +210,7 @@ { position, textDocument }: CompletionParams
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) {
+ if (!isCssContext(state, doc, position)) {
return null
}
@@ -318,7 +318,7 @@ { position, textDocument }: CompletionParams
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) {
+ if (!isCssContext(state, doc, position)) {
return null
}
@@ -409,7 +409,7 @@ { position, textDocument }: CompletionParams
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) {
+ if (!isCssContext(state, doc, position)) {
return null
}
@@ -457,7 +457,7 @@ { position, textDocument }: CompletionParams
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) {
+ if (!isCssContext(state, doc, position)) {
return null
}
@@ -505,7 +505,7 @@ { position, textDocument }: CompletionParams
): CompletionList {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) {
+ if (!isCssContext(state, doc, position)) {
return null
}
@@ -600,9 +600,9 @@ if (settings.emmetCompletions !== true) return null
let doc = state.editor.documents.get(textDocument.uri)
- const syntax = isHtmlContext(doc, position)
+ const syntax = isHtmlContext(state, doc, position)
? 'html'
- : isJsContext(doc, position)
+ : isJsContext(state, doc, position)
? 'jsx'
: null
diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts
index ce213758a18f24ae45a5f72ea00346e33b91d888..7e27d9ac9833ec166a6332607b38562ec0b03f80 100644
--- a/src/lsp/providers/hoverProvider.ts
+++ b/src/lsp/providers/hoverProvider.ts
@@ -27,7 +27,7 @@ { textDocument, position }: TextDocumentPositionParams
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) return null
+ if (!isCssContext(state, doc, position)) return null
const line = doc.getText({
start: { line: position.line, character: 0 },
@@ -81,7 +81,11 @@ { textDocument, position }: TextDocumentPositionParams
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isHtmlContext(doc, position) && !isJsContext(doc, position)) return null
+ if (
+ !isHtmlContext(state, doc, position) &&
+ !isJsContext(state, doc, position)
+ )
+ return null
let hovered = getClassNameAtPosition(doc, position)
if (!hovered) return null
@@ -111,7 +115,7 @@ { textDocument, position }: TextDocumentPositionParams
): Hover {
let doc = state.editor.documents.get(textDocument.uri)
- if (!isCssContext(doc, position)) return null
+ if (!isCssContext(state, doc, position)) return null
const classNames = findClassNamesInRange(doc, {
start: { line: Math.max(position.line - 10, 0), character: 0 },
diff --git a/src/lsp/server.ts b/src/lsp/server.ts
index 3315853a892e72680b786a48e45987b70dc13f58..b28b1bad9d226222f5d9e24472c9d8d698df6187 100644
--- a/src/lsp/server.ts
+++ b/src/lsp/server.ts
@@ -32,7 +32,10 @@ let connection = createConnection(ProposedFeatures.all)
let documents = new TextDocuments()
let workspaceFolder: string | null
-const defaultSettings: Settings = { emmetCompletions: false }
+const defaultSettings: Settings = {
+ emmetCompletions: false,
+ includeLanguages: {},
+}
let globalSettings: Settings = defaultSettings
let documentSettings: Map<string, Settings> = new Map()
@@ -53,6 +56,11 @@ connection,
documents,
documentSettings,
globalSettings,
+ userLanguages:
+ params.initializationOptions &&
+ params.initializationOptions.userLanguages
+ ? params.initializationOptions.userLanguages
+ : {},
capabilities: {
configuration:
capabilities.workspace && !!capabilities.workspace.configuration,
diff --git a/src/lsp/util/css.ts b/src/lsp/util/css.ts
index fb4f90a86a88412678b90e801df0d47764dbe4b1..84b788889c876707b4201ba3d2093b8bb472dd2f 100644
--- a/src/lsp/util/css.ts
+++ b/src/lsp/util/css.ts
@@ -1,5 +1,6 @@
import { TextDocument, Position } from 'vscode-languageserver'
import { isInsideTag, isVueDoc, isSvelteDoc } from './html'
+import { State } from './state'
export const CSS_LANGUAGES = [
'css',
@@ -10,12 +11,20 @@ 'scss',
'stylus',
]
-function isCssDoc(doc: TextDocument): boolean {
- return CSS_LANGUAGES.indexOf(doc.languageId) !== -1
+function isCssDoc(state: State, doc: TextDocument): boolean {
+ const userCssLanguages = Object.keys(
+ state.editor.userLanguages
+ ).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang]))
+
+ return [...CSS_LANGUAGES, ...userCssLanguages].indexOf(doc.languageId) !== -1
}
-export function isCssContext(doc: TextDocument, position: Position): boolean {
- if (isCssDoc(doc)) {
+export function isCssContext(
+ state: State,
+ doc: TextDocument,
+ position: Position
+): boolean {
+ if (isCssDoc(state, doc)) {
return true
}
diff --git a/src/lsp/util/html.ts b/src/lsp/util/html.ts
index d0f5743546bb04975746961bc9419d7fbf35e130..8808141906f9545b6ad1c62f838acf6f52f0f88a 100644
--- a/src/lsp/util/html.ts
+++ b/src/lsp/util/html.ts
@@ -1,4 +1,5 @@
import { TextDocument, Position } from 'vscode-languageserver'
+import { State } from './state'
export const HTML_LANGUAGES = [
'aspnetcorerazor',
@@ -27,8 +28,14 @@ 'slim',
'twig',
]
-export function isHtmlDoc(doc: TextDocument): boolean {
- return HTML_LANGUAGES.indexOf(doc.languageId) !== -1
+export function isHtmlDoc(state: State, doc: TextDocument): boolean {
+ const userHtmlLanguages = Object.keys(
+ state.editor.userLanguages
+ ).filter((lang) => HTML_LANGUAGES.includes(state.editor.userLanguages[lang]))
+
+ return (
+ [...HTML_LANGUAGES, ...userHtmlLanguages].indexOf(doc.languageId) !== -1
+ )
}
export function isVueDoc(doc: TextDocument): boolean {
@@ -39,13 +46,17 @@ export function isSvelteDoc(doc: TextDocument): boolean {
return doc.languageId === 'svelte'
}
-export function isHtmlContext(doc: TextDocument, position: Position): boolean {
+export function isHtmlContext(
+ state: State,
+ doc: TextDocument,
+ position: Position
+): boolean {
let str = doc.getText({
start: { line: 0, character: 0 },
end: position,
})
- if (isHtmlDoc(doc) && !isInsideTag(str, ['script', 'style'])) {
+ if (isHtmlDoc(state, doc) && !isInsideTag(str, ['script', 'style'])) {
return true
}
diff --git a/src/lsp/util/js.ts b/src/lsp/util/js.ts
index 48107f079ef9569635bf1f87a246175c41bbad14..8a62a5ff69c8ff4869e8541c6d0eac40a9b1744e 100644
--- a/src/lsp/util/js.ts
+++ b/src/lsp/util/js.ts
@@ -1,5 +1,6 @@
import { TextDocument, Position } from 'vscode-languageserver'
import { isHtmlDoc, isInsideTag, isVueDoc, isSvelteDoc } from './html'
+import { State } from './state'
export const JS_LANGUAGES = [
'javascript',
@@ -8,12 +9,20 @@ 'reason',
'typescriptreact',
]
-export function isJsDoc(doc: TextDocument): boolean {
- return JS_LANGUAGES.indexOf(doc.languageId) !== -1
+export function isJsDoc(state: State, doc: TextDocument): boolean {
+ const userJsLanguages = Object.keys(
+ state.editor.userLanguages
+ ).filter((lang) => JS_LANGUAGES.includes(state.editor.userLanguages[lang]))
+
+ return [...JS_LANGUAGES, ...userJsLanguages].indexOf(doc.languageId) !== -1
}
-export function isJsContext(doc: TextDocument, position: Position): boolean {
- if (isJsDoc(doc)) {
+export function isJsContext(
+ state: State,
+ doc: TextDocument,
+ position: Position
+): boolean {
+ if (isJsDoc(state, doc)) {
return true
}
@@ -22,7 +31,7 @@ start: { line: 0, character: 0 },
end: position,
})
- if (isHtmlDoc(doc) && isInsideTag(str, ['script'])) {
+ if (isHtmlDoc(state, doc) && isInsideTag(str, ['script'])) {
return true
}
diff --git a/src/lsp/util/state.ts b/src/lsp/util/state.ts
index 401cedfeb2252fe7752dc2768aa7f880f44c21fa..daaa0501c05f20e0e88dc85c4357533d26e4a74d 100644
--- a/src/lsp/util/state.ts
+++ b/src/lsp/util/state.ts
@@ -18,6 +18,7 @@ connection: Connection
documents: TextDocuments
documentSettings: Map<string, Settings>
globalSettings: Settings
+ userLanguages: Record<string, string>
capabilities: {
configuration: boolean
}
@@ -25,6 +26,7 @@ }
export type Settings = {
emmetCompletions: boolean
+ includeLanguages: Record<string, string>
}
export type State = null | {