Skip to content

Add tailwindCSS.classFunctions setting #1258

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
39b4a94
Added "tailwindCSS.experimental.classFunctions" option
Mar 6, 2025
baee7b5
Use optional chaining to access classFunctions
Mar 7, 2025
dc5a298
Added tests for "tailwindCSS.experimental.classFunctions"
Mar 7, 2025
0e227e9
Removed settings duplication: Created getDefaultTailwindSettings in @…
Mar 7, 2025
100cad7
Use getDefaultTailwindSettings in find.test.ts
Mar 7, 2025
41cc24a
Fixed findClassListsInHtmlRange state type & removed type casting in …
Mar 7, 2025
3b4e04c
Changed getDefaultTailwindSettings to return const object that satisf…
Mar 7, 2025
7d059da
Moved classFunctions option out of experimental
Mar 7, 2025
dc441bc
Added support for tagged template literals
Mar 7, 2025
43fab13
wip
thecrypticace Mar 12, 2025
9ee8b19
wip
thecrypticace Mar 12, 2025
94f1300
wip
thecrypticace Mar 12, 2025
e29481d
wip
thecrypticace Mar 12, 2025
23ccf9c
Move types
thecrypticace Mar 13, 2025
481d270
wip
thecrypticace Mar 13, 2025
b214092
Rewrite tests
thecrypticace Mar 13, 2025
878b18d
wip
thecrypticace Mar 13, 2025
a1375d8
wip
thecrypticace Mar 13, 2025
15ece2d
wip
thecrypticace Mar 13, 2025
9c9b63a
fix: DeepPartial type issues
Mar 16, 2025
9f2f840
fix: ts config issues in @tailwindcss/language-service
Mar 16, 2025
3baeb5b
fix: matchClassFunctions isClassFn RegExp flags
Mar 16, 2025
0977df1
feat: classFunctions & classProperties should not duplicate matches
Mar 16, 2025
8706e3a
feat: ensure same matches in a different spot are pushed to results i…
Mar 16, 2025
3514bb6
fix: use extended tsconfig instead of find *.test.d.ts -delete to rem…
Mar 16, 2025
86dace3
Remove `resolveRange` fn
thecrypticace Mar 13, 2025
44ba4b9
Rename config file
thecrypticace Mar 16, 2025
d2b0eed
Add test
thecrypticace Mar 16, 2025
a859e4b
Cleanup
thecrypticace Mar 16, 2025
0eb3712
Fix test name
thecrypticace Mar 19, 2025
58df6d9
Update readme
thecrypticace Mar 19, 2025
5902f5d
Limit class function matching to JS language boundaries
thecrypticace Mar 19, 2025
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
41 changes: 5 additions & 36 deletions packages/tailwindcss-language-server/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,16 @@
import merge from 'deepmerge'
import { isObject } from './utils'
import type { Settings } from '@tailwindcss/language-service/src/util/state'
import {
getDefaultTailwindSettings,
type Settings,
} from '@tailwindcss/language-service/src/util/state'
import type { Connection } from 'vscode-languageserver'

export interface SettingsCache {
get(uri?: string): Promise<Settings>
clear(): void
}

function getDefaultSettings(): Settings {
return {
editor: { tabSize: 2 },
tailwindCSS: {
inspectPort: null,
emmetCompletions: false,
classAttributes: ['class', 'className', 'ngClass', 'class:list'],
codeActions: true,
hovers: true,
suggestions: true,
validate: true,
colorDecorators: true,
rootFontSize: 16,
lint: {
cssConflict: 'warning',
invalidApply: 'error',
invalidScreen: 'error',
invalidVariant: 'error',
invalidConfigPath: 'error',
invalidTailwindDirective: 'error',
invalidSourceDirective: 'error',
recommendedVariantOrder: 'warning',
},
showPixelEquivalents: true,
includeLanguages: {},
files: { exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'] },
experimental: {
classRegex: [],
configFile: null,
},
},
}
}

export function createSettingsCache(connection: Connection): SettingsCache {
const cache: Map<string, Settings> = new Map()

Expand Down Expand Up @@ -73,7 +42,7 @@ export function createSettingsCache(connection: Connection): SettingsCache {
tailwindCSS = isObject(tailwindCSS) ? tailwindCSS : {}

return merge<Settings>(
getDefaultSettings(),
getDefaultTailwindSettings(),
{ editor, tailwindCSS },
{ arrayMerge: (_destinationArray, sourceArray, _options) => sourceArray },
)
Expand Down
2 changes: 1 addition & 1 deletion packages/tailwindcss-language-server/tests/utils/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { createConfiguration, Configuration } from './configuration'
import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/util/getLanguageBoundaries'
import { DefaultMap } from '../../src/util/default-map'
import { connect, ConnectOptions } from './connection'
import type { DeepPartial } from './types'
import type { DeepPartial } from '@tailwindcss/language-service/src/types'

export interface DocumentDescriptor {
/**
Expand Down
41 changes: 5 additions & 36 deletions packages/tailwindcss-language-server/tests/utils/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { Settings } from '@tailwindcss/language-service/src/util/state'
import {
getDefaultTailwindSettings,
type Settings,
} from '@tailwindcss/language-service/src/util/state'
import { URI } from 'vscode-uri'
import type { DeepPartial } from './types'
import { CacheMap } from '../../src/cache-map'
Expand All @@ -10,41 +13,7 @@ export interface Configuration {
}

export function createConfiguration(): Configuration {
let defaults: Settings = {
editor: {
tabSize: 2,
},
tailwindCSS: {
inspectPort: null,
emmetCompletions: false,
includeLanguages: {},
classAttributes: ['class', 'className', 'ngClass', 'class:list'],
suggestions: true,
hovers: true,
codeActions: true,
validate: true,
showPixelEquivalents: true,
rootFontSize: 16,
colorDecorators: true,
lint: {
cssConflict: 'warning',
invalidApply: 'error',
invalidScreen: 'error',
invalidVariant: 'error',
invalidConfigPath: 'error',
invalidTailwindDirective: 'error',
invalidSourceDirective: 'error',
recommendedVariantOrder: 'warning',
},
experimental: {
classRegex: [],
configFile: {},
},
files: {
exclude: ['**/.git/**', '**/node_modules/**', '**/.hg/**', '**/.svn/**'],
},
},
}
let defaults = getDefaultTailwindSettings()

/**
* Settings per file or directory URI
Expand Down
9 changes: 0 additions & 9 deletions packages/tailwindcss-language-server/tests/utils/types.ts

This file was deleted.

2 changes: 2 additions & 0 deletions packages/tailwindcss-language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@
},
"devDependencies": {
"@types/css.escape": "^1.5.2",
"@types/dedent": "^0.7.2",
"@types/line-column": "^1.0.2",
"@types/node": "^18.19.33",
"@types/stringify-object": "^4.0.5",
"dedent": "^1.5.3",
"esbuild": "^0.25.0",
"esbuild-node-externals": "^1.9.0",
"minimist": "^1.2.8",
Expand Down
2 changes: 1 addition & 1 deletion packages/tailwindcss-language-service/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let build = await esbuild.context({
// Call the tsc command to generate the types
spawnSync(
'tsc',
['--emitDeclarationOnly', '--outDir', path.resolve(__dirname, '../dist')],
['-p', path.resolve(__dirname, './tsconfig.build.json'), '--emitDeclarationOnly', '--outDir', path.resolve(__dirname, '../dist')],
{
stdio: 'inherit',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"exclude": ["../src/**/*.test.ts"]
}
23 changes: 22 additions & 1 deletion packages/tailwindcss-language-service/src/completionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import removeMeta from './util/removeMeta'
import { formatColor, getColor, getColorFromValue } from './util/color'
import { isHtmlContext, isHtmlDoc, isVueDoc } from './util/html'
import { isCssContext } from './util/css'
import { findLast, matchClassAttributes } from './util/find'
import { findLast, matchClassAttributes, matchClassFunctions } from './util/find'
import { stringifyConfigValue, stringifyCss } from './util/stringify'
import { stringifyScreen, Screen } from './util/screens'
import isObject from './util/isObject'
Expand Down Expand Up @@ -45,6 +45,8 @@ import type { ThemeEntry } from './util/v4'
import { segment } from './util/segment'
import { resolveKnownThemeKeys, resolveKnownThemeNamespaces } from './util/v4/theme-keys'
import { SEARCH_RANGE } from './util/constants'
import { getLanguageBoundaries } from './util/getLanguageBoundaries'
import { isWithinRange } from './util/isWithinRange'

let isUtil = (className) =>
Array.isArray(className.__info)
Expand Down Expand Up @@ -747,6 +749,25 @@ async function provideClassAttributeCompletions(

let matches = matchClassAttributes(str, settings.classAttributes)

let boundaries = getLanguageBoundaries(state, document)

for (let boundary of boundaries ?? []) {
let isJsContext = boundary.type === 'js' || boundary.type === 'jsx'
if (!isJsContext) continue
if (!settings.classFunctions?.length) continue
if (!isWithinRange(position, boundary.range)) continue

let str = document.getText(boundary.range)
let offset = document.offsetAt(boundary.range.start)
let fnMatches = matchClassFunctions(str, settings.classFunctions)

fnMatches.forEach((match) => {
if (match.index) match.index += offset
})

matches.push(...fnMatches)
}

if (matches.length === 0) {
return null
}
Expand Down
9 changes: 9 additions & 0 deletions packages/tailwindcss-language-service/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends ((...args: any) => any) | ReadonlyArray<any> | Date
? T[P]
: T[P] extends (infer U)[]
? U[]
: T[P] extends object
? DeepPartial<T[P]>
: T[P]
}
Loading