Skip to content

Commit b124e83

Browse files
committed
Suggest completions for source(…)
1 parent aae216c commit b124e83

File tree

3 files changed

+67
-10
lines changed

3 files changed

+67
-10
lines changed

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

+39-8
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,40 @@ async function provideFileDirectiveCompletions(
16381641
return type.isDirectory || isAllowedFile(name)
16391642
})
16401643

1641-
return withDefaults(
1644+
let items: CompletionItem[] = 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+
}))
1651+
1652+
let sourceStart = suggest === 'directory'
1653+
? text.lastIndexOf('source(') + 7
1654+
: -1
1655+
1656+
if (sourceStart !== -1) {
1657+
items.push({
1658+
filterText: '/',
1659+
label: '(none)',
1660+
kind: 15, // Snippet
1661+
textEdit: {
1662+
range: {
1663+
start: {
1664+
line: position.line,
1665+
character: sourceStart,
1666+
},
1667+
end: position,
1668+
},
1669+
newText: 'none',
1670+
},
1671+
})
1672+
}
1673+
1674+
let wip = withDefaults(
16421675
{
16431676
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-
})),
1677+
items,
16511678
},
16521679
{
16531680
data: {
@@ -1664,6 +1691,10 @@ async function provideFileDirectiveCompletions(
16641691
},
16651692
state.editor.capabilities.itemDefaults,
16661693
)
1694+
1695+
console.log(JSON.stringify(wip))
1696+
1697+
return wip
16671698
}
16681699

16691700
async function provideEmmetCompletions(
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

+15-2
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)