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

Conversation

LaurynasGr
Copy link
Contributor

@LaurynasGr LaurynasGr commented Mar 7, 2025

This PR adds tailwindCSS.classFunctions option to the settings to add simple and performant class completions, hover previews, linting etc. for such cases:

const classes = cn(
  'pointer-events-auto relative flex bg-red-500',
  'items-center justify-between overflow-hidden',
  'md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full',
  'data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)]',
  Date.now() > 15000 ? 'text-red-200' : 'text-red-700',
  'data-[swipe=move]:transition-none',
)

image

const variants = cva(
  cn(
    'pointer-events-auto relative flex bg-green-500',
    'md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full',
    'md:h-[calc(100%-2rem)]',
    'bg-red-700',
  ),
  {
    variants: {
      mobile: {
        default: 'bottom-0 left-0',
        fullScreen: `
          inset-0
          md:h-[calc(100%-2rem)]
          rounded-none
          bg-blue-700
        `,
      },
    },
    defaultVariants: {
      mobile: 'default',
    },
  },
)

image

const tagged = cn`
  pointer-events-auto relative flex bg-red-500
  items-center justify-between overflow-hidden
  md:min-w-[20rem] md:max-w-[37.5rem] md:py-sm pl-md py-xs pr-xs gap-sm w-full
  data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)]
  md:h-[calc(100%-2rem)] text-green-700
  data-[swipe=move]:transition-none
`

image

@thecrypticace
Copy link
Contributor

Hah. This is fantastic! I was thinking about adding exactly this feature last week while working on a larger refactor of stuff which is intended to make features like this simpler to implement and hopefully cleanup code a ton too: Document Scopes. It's a massive WIP and nowhere near done though.

Some notes:

  • I absolutely want to add this feature
  • I'd prefer to implement this in a way that makes it feels pretty solid internally so I'd name the setting tailwindCSS.classFunctions
  • I don't want to introduce more regex-ish features which match against the document as a whole. Users inevitably write regexes that have catastrophic backtracking problems (… its happened a lot 😭).
  • Regex matching of fn names themselves are fine but I'd rather match against a set of extracted function names rather than something that could technically match against the entire document.
  • The rexes works for a lot of languages but not all of them. This is fine but I'd love to make it easier to add support for new languages.
  • I also want to support tagged template literals with this feature so there is parity with the prettier plugin.

The Document Scopes feature I've been working on is pretty much designed to make every since one of the above goals easier to achieve but it's a long way off from being done.

I'm going to think on this some to see if there's an interim solution that hits most of those points (the primary one being the whole document regex one tho).

@LaurynasGr
Copy link
Contributor Author

@thecrypticace thanks for the notes!

  • I'd prefer to implement this in a way that makes it feels pretty solid internally so I'd name the setting tailwindCSS.classFunctions

Moved it outside experimental so it's simply tailwindCSS.classFunctions

  • I don't want to introduce more regex-ish features which match against the document as a whole. Users inevitably write regexes that have catastrophic backtracking problems (… its happened a lot 😭).

Yea, I know.. Prior to taking this on, I tried to write logic to achieve this with tailwindCSS.experimental.classRegex, but some regex combinations were to aggressive, consuming every single string in the document after it sees a cn or cva, others were too weak and broke as soon as it encountered a ( in the class list (e.g. translate-x-[var(--radix-toast-swipe-end-x)]) and yet others were just to unstable, breaking the extension in VS Code as soon as you have an unclosed ( 😅

  • I also want to support tagged template literals with this feature so there is parity with the prettier plugin.

Added support for tagged template literals with the same option (See dc441bc)

image

@LaurynasGr
Copy link
Contributor Author

LaurynasGr commented Mar 7, 2025

  • I don't want to introduce more regex-ish features which match against the document as a whole. Users inevitably write regexes that have catastrophic backtracking problems (… its happened a lot 😭).
  • Regex matching of fn names themselves are fine but I'd rather match against a set of extracted function names rather than something that could technically match against the entire document.

We could possibly look into an AST approach (e.g. for JS/JSX @babel/template) - Parse the document into an AST and then use the AST to generate DocumentClassList[]

Edit:
Or even better to use something like acorn (and acorn-jsx for JSX files) for the AST generation as that exposes code locations nicely and we need these to generate DocumentClassList[]. Then using the AST in combination with tailwindCSS options, DocumentClassList[] could be generated

@LaurynasGr LaurynasGr changed the title Add experimental.classFunctions option to the settings Add tailwindCSS.classFunctions option to the settings Mar 7, 2025
@thecrypticace
Copy link
Contributor

I can probably write a specialized parser that's "good enough" for this case. We definitely do not need the full power of acorn or babel here.

@thecrypticace thecrypticace changed the title Add tailwindCSS.classFunctions option to the settings Add tailwindCSS.classFunctions setting Mar 13, 2025
@@ -64,7 +65,7 @@ export type TailwindCssSettings = {
recommendedVariantOrder: DiagnosticSeveritySetting
}
experimental: {
classRegex: string[]
classRegex: string[] | [string, string][]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ngl seeing that this was wrong is a bit embarrassing 😂

@thecrypticace
Copy link
Contributor

Alright, I've made a handful of changes to the implementation and tests. Still some typescript things that I want to cleanup but I'm not gonna do that in this PR (tbh there's a LOT of low hanging fruit there — I'm very, very slowly working towards migrating everything to strict mode).

Gonna have some other folks take a look at this and hopefully get it in a release maybe next week.

@LaurynasGr
Copy link
Contributor Author

Perfect! I'll look over your changes during the weekend as well - thanks for taking this on!

@LaurynasGr LaurynasGr force-pushed the feat/add-experimental-classFunctions-for-intellisense branch from f048e51 to 8706e3a Compare March 16, 2025 15:23
Laurynas Grigutis and others added 5 commits March 16, 2025 20:06
Copy link
Member

@RobinMalfait RobinMalfait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add this to the readme so people know that this is available and how they can configure it.

@RobinMalfait
Copy link
Member

Only small thing we noticed is that this also triggers it:
image

But this can be addressed in a follow up PR by @thecrypticace!

@thecrypticace
Copy link
Contributor

I went ahead and addressed it in this one

@thecrypticace
Copy link
Contributor

Also realized I need completion tests for this but I'm gonna add those after merging a couple other PRs b/c conflicts

@thecrypticace thecrypticace merged commit b4b3a2a into tailwindlabs:main Mar 19, 2025
@thecrypticace
Copy link
Contributor

Thanks @LaurynasGr! Such a nice feature to have 💯

I'm gonna try getting a release out with this today or tomorrow

@thecrypticace
Copy link
Contributor

Just published v0.14.10 with this 🔥

@LaurynasGr
Copy link
Contributor Author

Awesome! Thanks @thecrypticace for accepting this and helping to get it through. I'm certain this will help lots of people struggling to achieve this behaviour with classRegex

@thecrypticace
Copy link
Contributor

just found a bug — if you've got cva & cn in your list, the following breaks:

let x = cva("  no completions here   ");

export function Button() {
  return <Comp className={cn(" or here   ")} />;
}

export function Button2() {
  return <Comp className={cn(" or here   ")} />;
}

let x = cva("  but I do get them here   ");

The hovers are fine. Looking at the class attribute completion code in general it seems a bit weird so I think I'll need to rework it.

😱

@thecrypticace
Copy link
Contributor

Figured it out #1278

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants