Skip to content

Commit 75cb5d4

Browse files
committed
Suggest completions for source(…)
1 parent 59d4b90 commit 75cb5d4

File tree

4 files changed

+124
-9
lines changed

4 files changed

+124
-9
lines changed

packages/tailwindcss-language-server/tests/completions/at-config.test.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,90 @@ withFixture('v4/dependencies', (c) => {
296296
],
297297
})
298298
})
299+
300+
test.concurrent('@import "…" source(…)', async ({ expect }) => {
301+
let result = await completion({
302+
text: '@import "tailwindcss" source("',
303+
lang: 'css',
304+
position: {
305+
line: 0,
306+
character: 30,
307+
},
308+
})
309+
310+
expect(result).toEqual({
311+
isIncomplete: false,
312+
items: [
313+
{
314+
label: 'sub-dir/',
315+
kind: 19,
316+
command: { command: 'editor.action.triggerSuggest', title: '' },
317+
data: expect.anything(),
318+
textEdit: {
319+
newText: 'sub-dir/',
320+
range: { start: { line: 0, character: 30 }, end: { line: 0, character: 30 } },
321+
},
322+
},
323+
],
324+
})
325+
})
326+
327+
test.concurrent('@tailwind utilities source(…)', async ({ expect }) => {
328+
let result = await completion({
329+
text: '@tailwind utilities source("',
330+
lang: 'css',
331+
position: {
332+
line: 0,
333+
character: 28,
334+
},
335+
})
336+
337+
expect(result).toEqual({
338+
isIncomplete: false,
339+
items: [
340+
{
341+
label: 'sub-dir/',
342+
kind: 19,
343+
command: { command: 'editor.action.triggerSuggest', title: '' },
344+
data: expect.anything(),
345+
textEdit: {
346+
newText: 'sub-dir/',
347+
range: { start: { line: 0, character: 28 }, end: { line: 0, character: 28 } },
348+
},
349+
},
350+
],
351+
})
352+
})
353+
354+
test.concurrent('@import "…" source(…) directory', async ({ expect }) => {
355+
let result = await completion({
356+
text: '@import "tailwindcss" source("sub-dir/',
357+
lang: 'css',
358+
position: {
359+
line: 0,
360+
character: 38,
361+
},
362+
})
363+
364+
expect(result).toEqual({
365+
isIncomplete: false,
366+
items: [],
367+
})
368+
})
369+
370+
test.concurrent('@tailwind utilities source(…) directory', async ({ expect }) => {
371+
let result = await completion({
372+
text: '@tailwind utilities source("sub-dir/',
373+
lang: 'css',
374+
position: {
375+
line: 0,
376+
character: 36,
377+
},
378+
})
379+
380+
expect(result).toEqual({
381+
isIncomplete: false,
382+
items: [],
383+
})
384+
})
299385
})

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,9 @@ async function provideFileDirectiveCompletions(
16261626

16271627
if (suggest === 'source') return IS_TEMPLATE_SOURCE.test(name)
16281628

1629+
// Files are not allowed but directories are
1630+
if (suggest === 'directory') return false
1631+
16291632
return false
16301633
}
16311634

@@ -1638,16 +1641,16 @@ async function provideFileDirectiveCompletions(
16381641
return type.isDirectory || isAllowedFile(name)
16391642
})
16401643

1644+
let items: CompletionItem[] = entries.map(([name, type]) => ({
1645+
label: type.isDirectory ? name + '/' : name,
1646+
kind: type.isDirectory ? 19 : 17,
1647+
command: type.isDirectory ? { command: 'editor.action.triggerSuggest', title: '' } : undefined,
1648+
}))
1649+
16411650
return withDefaults(
16421651
{
16431652
isIncomplete: false,
1644-
items: entries.map(([name, type]) => ({
1645-
label: type.isDirectory ? name + '/' : name,
1646-
kind: type.isDirectory ? 19 : 17,
1647-
command: type.isDirectory
1648-
? { command: 'editor.action.triggerSuggest', title: '' }
1649-
: undefined,
1650-
})),
1653+
items,
16511654
},
16521655
{
16531656
data: {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { expect, test } from 'vitest'
2+
import { findFileDirective } from './file-paths'
3+
4+
let findV3 = (text: string) => findFileDirective({ enabled: true, v4: false }, text)
5+
let findV4 = (text: string) => findFileDirective({ enabled: true, v4: true }, text)
6+
7+
test('…', async () => {
8+
await expect(findV4('@import "tailwindcss" source("./')).resolves.toEqual({
9+
directive: 'import',
10+
partial: './',
11+
suggest: 'directory',
12+
})
13+
})

packages/tailwindcss-language-service/src/completions/file-paths.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ import type { State } from '../util/state'
44
const PATTERN_CUSTOM_V4 = /@(?<directive>config|plugin|source)\s*(?<partial>'[^']*|"[^"]*)$/
55
const PATTERN_CUSTOM_V3 = /@(?<directive>config)\s*(?<partial>'[^']*|"[^"]*)$/
66

7+
// @import … source('…')
8+
// @tailwind utilities source('…')
9+
const PATTERN_IMPORT_SOURCE = /@(?<directive>import)\s*(?<path>'[^']*'|"[^"]*")\s*source\((?<partial>'[^']*|"[^"]*)$/
10+
const PATTERN_UTIL_SOURCE = /@(?<directive>tailwind)\s+utilities\s+source\((?<partial>'[^']*|"[^"]*)?$/
11+
712
export type FileDirective = {
813
directive: string
914
partial: string
10-
suggest: 'script' | 'source'
15+
suggest: 'script' | 'source' | 'directory'
1116
}
1217

1318
export async function findFileDirective(state: State, text: string): Promise<FileDirective | null> {
1419
if (state.v4) {
1520
let match = text.match(PATTERN_CUSTOM_V4)
21+
?? text.match(PATTERN_IMPORT_SOURCE)
22+
?? text.match(PATTERN_UTIL_SOURCE)
1623

1724
if (!match) return null
1825

1926
let directive = match.groups.directive
20-
let partial = match.groups.partial.slice(1) // remove leading quote
27+
let partial = match.groups.partial?.slice(1) ?? "" // remove leading quote
2128

2229
// Most suggestions are for JS files so we'll default to that
2330
let suggest: FileDirective['suggest'] = 'script'
@@ -27,6 +34,12 @@ export async function findFileDirective(state: State, text: string): Promise<Fil
2734
suggest = 'source'
2835
}
2936

37+
// If we're looking at @import … source('…') or @tailwind … source('…') then
38+
// we want to list directories instead of files
39+
else if (directive === 'import' || directive === 'tailwind') {
40+
suggest = 'directory'
41+
}
42+
3043
return { directive, partial, suggest }
3144
}
3245

0 commit comments

Comments
 (0)