Skip to content

Commit 4dd521a

Browse files
Parse Astro code fences as JS (#1386)
Fixes #1385 --------- Co-authored-by: Robin Malfait <[email protected]>
1 parent d0d1e3d commit 4dd521a

File tree

7 files changed

+150
-12
lines changed

7 files changed

+150
-12
lines changed

packages/tailwindcss-language-service/src/completionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { TextDocument } from 'vscode-languageserver-textdocument'
1313
import dlv from 'dlv'
1414
import removeMeta from './util/removeMeta'
1515
import { formatColor, getColor, getColorFromValue } from './util/color'
16-
import { isHtmlContext, isHtmlDoc, isVueDoc } from './util/html'
16+
import { isHtmlContext, isHtmlDoc } from './util/html'
1717
import { isCssContext } from './util/css'
1818
import { findLast, matchClassAttributes, matchClassFunctions } from './util/find'
1919
import { stringifyConfigValue, stringifyCss } from './util/stringify'

packages/tailwindcss-language-service/src/util/find.test.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
findClassNameAtPosition,
55
findHelperFunctionsInDocument,
66
} from './find'
7-
import { js, html, pug, createDocument, css } from './test-utils'
7+
import { js, html, pug, createDocument, css, astro } from './test-utils'
88
import type { Range } from 'vscode-languageserver-textdocument'
99

1010
const range = (startLine: number, startCol: number, endLine: number, endCol: number): Range => ({
@@ -1044,3 +1044,40 @@ test('Can find helper functions in CSS', async ({ expect }) => {
10441044
},
10451045
])
10461046
})
1047+
1048+
test('class functions work inside astro code fences', async ({ expect }) => {
1049+
let file = createDocument({
1050+
name: 'file.astro',
1051+
lang: 'astro',
1052+
settings: {
1053+
tailwindCSS: {
1054+
classFunctions: ['clsx'],
1055+
},
1056+
},
1057+
content: astro`
1058+
---
1059+
let x = clsx("relative flex bg-red-500")
1060+
---
1061+
<div class="relative flex bg-red-500"></div>
1062+
`,
1063+
})
1064+
1065+
let classLists = await findClassListsInHtmlRange(file.state, file.doc, 'html')
1066+
1067+
expect(classLists).toEqual([
1068+
{
1069+
classList: 'relative flex bg-red-500',
1070+
range: {
1071+
start: { line: 3, character: 12 },
1072+
end: { line: 3, character: 36 },
1073+
},
1074+
},
1075+
{
1076+
classList: 'relative flex bg-red-500',
1077+
range: {
1078+
start: { line: 1, character: 14 },
1079+
end: { line: 1, character: 38 },
1080+
},
1081+
},
1082+
])
1083+
})

packages/tailwindcss-language-service/src/util/getLanguageBoundaries.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Range } from 'vscode-languageserver'
22
import type { TextDocument } from 'vscode-languageserver-textdocument'
3-
import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
3+
import { isVueDoc, isHtmlDoc, isSvelteDoc, isAstroDoc } from './html'
44
import type { State } from './state'
55
import { indexToPosition } from './find'
66
import { isJsDoc } from './js'
@@ -134,8 +134,21 @@ let vueStates = {
134134
},
135135
}
136136

137+
let astroStates = {
138+
...states,
139+
main: {
140+
frontmatterBlockStart: { match: /^[\s\n]*---/, push: 'frontmatterScript' },
141+
...states.main,
142+
},
143+
frontmatterScript: {
144+
frontmatterBlockEnd: { match: /\s*---(?=\s)/, pop: 1 },
145+
...text,
146+
},
147+
}
148+
137149
let defaultLexer = moo.states(states)
138150
let vueLexer = moo.states(vueStates)
151+
let astroLexer = moo.states(astroStates)
139152

140153
let cache = new Cache<string, LanguageBoundary[] | null>({ max: 25, maxAge: 1000 })
141154

@@ -157,13 +170,15 @@ export function getLanguageBoundaries(
157170

158171
let isJs = isJsDoc(state, doc)
159172

160-
let defaultType = isVueDoc(doc)
161-
? 'none'
162-
: isHtmlDoc(state, doc) || isSvelteDoc(doc)
163-
? 'html'
164-
: isJs
165-
? 'jsx'
166-
: null
173+
let defaultType: string | null = null
174+
175+
if (isVueDoc(doc)) {
176+
defaultType = 'none'
177+
} else if (isHtmlDoc(state, doc) || isSvelteDoc(doc) || isAstroDoc(doc)) {
178+
defaultType = 'html'
179+
} else if (isJs) {
180+
defaultType = 'jsx'
181+
}
167182

168183
if (defaultType === null) {
169184
cache.set(cacheKey, null)
@@ -172,7 +187,18 @@ export function getLanguageBoundaries(
172187

173188
text = getTextWithoutComments(text, isJs ? 'js' : 'html')
174189

175-
let lexer = defaultType === 'none' ? vueLexer : defaultLexer
190+
let lexer = defaultLexer
191+
192+
if (defaultType === 'none') {
193+
if (isVueDoc(doc)) {
194+
lexer = vueLexer
195+
}
196+
} else if (defaultType === 'html') {
197+
if (isAstroDoc(doc)) {
198+
lexer = astroLexer
199+
}
200+
}
201+
176202
lexer.reset(text)
177203

178204
let type = defaultType
@@ -190,6 +216,11 @@ export function getLanguageBoundaries(
190216
boundaries[boundaries.length - 1].range.end = position
191217
}
192218
type = token.type.replace(/BlockStart$/, '')
219+
220+
if (lexer === astroLexer && type === 'frontmatter') {
221+
type = 'js'
222+
}
223+
193224
boundaries.push({ type, range: { start: position, end: undefined } })
194225
} else if (token.type.endsWith('BlockEnd')) {
195226
let position = indexToPosition(text, offset)

packages/tailwindcss-language-service/src/util/html.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ export function isSvelteDoc(doc: TextDocument): boolean {
2020
return doc.languageId === 'svelte'
2121
}
2222

23+
export function isAstroDoc(doc: TextDocument): boolean {
24+
return doc.languageId === 'astro'
25+
}
26+
2327
export function isHtmlContext(state: State, doc: TextDocument, position: Position): boolean {
2428
let str = doc.getText({
2529
start: { line: 0, character: 0 },

packages/tailwindcss-language-service/src/util/language-boundaries.test.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { test } from 'vitest'
22
import { getLanguageBoundaries } from './getLanguageBoundaries'
3-
import { jsx, createDocument, html } from './test-utils'
3+
import { jsx, createDocument, html, astro } from './test-utils'
44

55
test('regex literals are ignored when determining language boundaries', ({ expect }) => {
66
let file = createDocument({
@@ -189,3 +189,67 @@ test('Vue files detect <template>, <script>, and <style> as separate boundaries'
189189
},
190190
])
191191
})
192+
193+
test('Astro files default to HTML', ({ expect }) => {
194+
let file = createDocument({
195+
name: 'file.astro',
196+
lang: 'astro',
197+
content: html`<div class="border-gray-200"></div>`,
198+
})
199+
200+
let boundaries = getLanguageBoundaries(file.state, file.doc)
201+
202+
expect(boundaries).toEqual([
203+
{
204+
type: 'html',
205+
range: {
206+
start: { line: 0, character: 0 },
207+
end: { line: 0, character: 35 },
208+
},
209+
},
210+
])
211+
})
212+
213+
test('Astro files front matter is parsed as JS', ({ expect }) => {
214+
let file = createDocument({
215+
name: 'file.astro',
216+
lang: 'astro',
217+
content: astro`
218+
---
219+
console.log('test')
220+
---
221+
<div class="border-gray-200"></div>
222+
`,
223+
})
224+
225+
let boundaries = getLanguageBoundaries(file.state, file.doc)
226+
227+
expect(boundaries).toEqual([
228+
// This block just shouldn't be here
229+
{
230+
type: 'html',
231+
range: {
232+
start: { line: 0, character: 0 },
233+
end: { line: 0, character: 0 },
234+
},
235+
},
236+
{
237+
type: 'js',
238+
range: {
239+
// This should probably be 0:3 instead of 0:0
240+
start: { line: 0, character: 0 },
241+
242+
// This should probably be 2:0 instead of 1:19
243+
end: { line: 1, character: 19 },
244+
},
245+
},
246+
{
247+
type: 'html',
248+
range: {
249+
// This should probably be 2:3 instead of 1:19
250+
start: { line: 1, character: 19 },
251+
end: { line: 3, character: 35 },
252+
},
253+
},
254+
])
255+
})

packages/tailwindcss-language-service/src/util/test-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const ts: Dedent = dedent
99
export const tsx: Dedent = dedent
1010
export const css: Dedent = dedent
1111
export const html: Dedent = dedent
12+
export const astro: Dedent = dedent
1213
export const pug: Dedent = dedent
1314

1415
export function createDocument({

packages/vscode-tailwindcss/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Highlight CSS variables correctly inside `@theme` ([#1409](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1409))
66
- Highlight comments inside `@theme` ([#1409](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1409))
77
- Highlight at-rules inside `@theme` ([#1409](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1409))
8+
- Detect class functions and class attributes inside Astro code fences ([#1386](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1386))
89

910
## 0.14.22
1011

0 commit comments

Comments
 (0)