diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts index 98f5d612..4273e9e1 100644 --- a/packages/tailwindcss-language-service/src/completionProvider.ts +++ b/packages/tailwindcss-language-service/src/completionProvider.ts @@ -13,7 +13,7 @@ import type { TextDocument } from 'vscode-languageserver-textdocument' import dlv from 'dlv' import removeMeta from './util/removeMeta' import { formatColor, getColor, getColorFromValue } from './util/color' -import { isHtmlContext, isHtmlDoc, isVueDoc } from './util/html' +import { isHtmlContext, isHtmlDoc } from './util/html' import { isCssContext } from './util/css' import { findLast, matchClassAttributes, matchClassFunctions } from './util/find' import { stringifyConfigValue, stringifyCss } from './util/stringify' diff --git a/packages/tailwindcss-language-service/src/util/css.ts b/packages/tailwindcss-language-service/src/util/css.ts index f43219aa..33f51230 100644 --- a/packages/tailwindcss-language-service/src/util/css.ts +++ b/packages/tailwindcss-language-service/src/util/css.ts @@ -1,6 +1,6 @@ import type { Position } from 'vscode-languageserver' import type { TextDocument } from 'vscode-languageserver-textdocument' -import { isVueDoc, isSvelteDoc, isHtmlDoc } from './html' +import { isVueDoc, isSvelteDoc, isHtmlDoc, isAstroDoc } from './html' import { isJsDoc } from './js' import type { State } from './state' import { cssLanguages } from './languages' @@ -27,7 +27,13 @@ export function isCssContext(state: State, doc: TextDocument, position: Position return true } - if (isHtmlDoc(state, doc) || isVueDoc(doc) || isSvelteDoc(doc) || isJsDoc(state, doc)) { + if ( + isHtmlDoc(state, doc) || + isVueDoc(doc) || + isSvelteDoc(doc) || + isJsDoc(state, doc) || + isAstroDoc(doc) + ) { let str = doc.getText({ start: { line: 0, character: 0 }, end: position, diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts index 11f5946f..b200ad7d 100644 --- a/packages/tailwindcss-language-service/src/util/find.ts +++ b/packages/tailwindcss-language-service/src/util/find.ts @@ -3,7 +3,7 @@ import type { TextDocument } from 'vscode-languageserver-textdocument' import type { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' import lineColumn from 'line-column' import { isCssContext, isCssDoc } from './css' -import { isHtmlContext, isVueDoc } from './html' +import { isAstroDoc, isHtmlContext, isVueDoc } from './html' import { isWithinRange } from './isWithinRange' import { isJsContext } from './js' import { dedupeByRange, flatten } from './array' @@ -617,6 +617,26 @@ export async function findClassNameAtPosition( }), ) + classNames = dedupeByRange(flatten(groups)).flatMap((classList) => + getClassNamesInClassList(classList, state.blocklist), + ) + } else if (isAstroDoc(doc)) { + let boundaries = getLanguageBoundaries(state, doc) + + let groups = await Promise.all( + boundaries.map(async ({ type, range }) => { + if (type === 'html') { + return await findClassListsInRange(state, doc, range, 'html') + } + + if (type === 'js' || type === 'jsx') { + return await findClassListsInRange(state, doc, range, 'jsx') + } + + return [] + }), + ) + classNames = dedupeByRange(flatten(groups)).flatMap((classList) => getClassNamesInClassList(classList, state.blocklist), ) diff --git a/packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts b/packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts index 786cc2f7..fe1e734e 100644 --- a/packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts +++ b/packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts @@ -1,6 +1,6 @@ import type { Range } from 'vscode-languageserver' import type { TextDocument } from 'vscode-languageserver-textdocument' -import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html' +import { isVueDoc, isHtmlDoc, isSvelteDoc, isAstroDoc } from './html' import type { State } from './state' import { indexToPosition } from './find' import { isJsDoc } from './js' @@ -134,8 +134,25 @@ let vueStates = { }, } +let astroStates = { + ...states, + main: { + htmlBlockStart: { push: 'htmlBlock' }, + ...states.main, + jsBlockStart: { match: '---', push: 'script' }, + }, + script: { + htmlBlockStart: { match: '---', push: 'html' }, + ...text, + }, + html: { + ...text, + }, +} + let defaultLexer = moo.states(states) let vueLexer = moo.states(vueStates) +let astroLexer = moo.states(astroStates) let cache = new Cache({ max: 25, maxAge: 1000 }) @@ -157,13 +174,15 @@ export function getLanguageBoundaries( let isJs = isJsDoc(state, doc) - let defaultType = isVueDoc(doc) - ? 'none' - : isHtmlDoc(state, doc) || isSvelteDoc(doc) - ? 'html' - : isJs - ? 'jsx' - : null + let defaultType: string | null = null + + if (isVueDoc(doc) || isAstroDoc(doc)) { + defaultType = 'none' + } else if (isHtmlDoc(state, doc) || isSvelteDoc(doc)) { + defaultType = 'html' + } else if (isJs) { + defaultType = 'jsx' + } if (defaultType === null) { cache.set(cacheKey, null) @@ -172,17 +191,28 @@ export function getLanguageBoundaries( text = getTextWithoutComments(text, isJs ? 'js' : 'html') - let lexer = defaultType === 'none' ? vueLexer : defaultLexer + let lexer = defaultLexer + + if (defaultType === 'none') { + if (isVueDoc(doc)) { + lexer = vueLexer + } else if (isAstroDoc(doc)) { + lexer = astroLexer + } + } + lexer.reset(text) let type = defaultType let boundaries: LanguageBoundary[] = [ { type: defaultType, range: { start: { line: 0, character: 0 }, end: undefined } }, ] + let offset = 0 try { for (let token of lexer) { + console.log({ token }) if (!token.type.startsWith('nested')) { if (token.type.endsWith('BlockStart')) { let position = indexToPosition(text, offset) diff --git a/packages/tailwindcss-language-service/src/util/html.ts b/packages/tailwindcss-language-service/src/util/html.ts index 666869bc..9464c452 100644 --- a/packages/tailwindcss-language-service/src/util/html.ts +++ b/packages/tailwindcss-language-service/src/util/html.ts @@ -20,6 +20,10 @@ export function isSvelteDoc(doc: TextDocument): boolean { return doc.languageId === 'svelte' } +export function isAstroDoc(doc: TextDocument): boolean { + return doc.languageId === 'astro' +} + export function isHtmlContext(state: State, doc: TextDocument, position: Position): boolean { let str = doc.getText({ start: { line: 0, character: 0 }, diff --git a/packages/tailwindcss-language-service/src/util/language-boundaries.test.ts b/packages/tailwindcss-language-service/src/util/language-boundaries.test.ts index 483a4ead..c334c50c 100644 --- a/packages/tailwindcss-language-service/src/util/language-boundaries.test.ts +++ b/packages/tailwindcss-language-service/src/util/language-boundaries.test.ts @@ -1,6 +1,6 @@ import { test } from 'vitest' import { getLanguageBoundaries } from './getLanguageBoundaries' -import { jsx, createDocument, html } from './test-utils' +import { jsx, createDocument, html, astro } from './test-utils' test('regex literals are ignored when determining language boundaries', ({ expect }) => { let file = createDocument({ @@ -189,3 +189,48 @@ test('Vue files detect