@@ -4,9 +4,7 @@ import type {
4
4
TextDocument ,
5
5
WorkspaceFolder ,
6
6
ConfigurationScope ,
7
- WorkspaceConfiguration ,
8
7
Selection ,
9
- CancellationToken ,
10
8
} from 'vscode'
11
9
import {
12
10
workspace as Workspace ,
@@ -16,8 +14,6 @@ import {
16
14
SymbolInformation ,
17
15
Position ,
18
16
Range ,
19
- RelativePattern ,
20
- CancellationTokenSource ,
21
17
} from 'vscode'
22
18
import type {
23
19
DocumentFilter ,
@@ -34,11 +30,11 @@ import { languages as defaultLanguages } from '@tailwindcss/language-service/src
34
30
import * as semver from '@tailwindcss/language-service/src/util/semver'
35
31
import isObject from '@tailwindcss/language-service/src/util/isObject'
36
32
import namedColors from 'color-name'
37
- import picomatch from 'picomatch'
38
33
import { CONFIG_GLOB , CSS_GLOB } from '@tailwindcss/language-server/src/lib/constants'
39
- import braces from 'braces'
40
34
import normalizePath from 'normalize-path'
41
35
import * as servers from './servers/index'
36
+ import { isExcluded , mergeExcludes } from './exclusions'
37
+ import { createApi } from './api'
42
38
43
39
const colorNames = Object . keys ( namedColors )
44
40
@@ -52,60 +48,6 @@ function getUserLanguages(folder?: WorkspaceFolder): Record<string, string> {
52
48
return isObject ( langs ) ? langs : { }
53
49
}
54
50
55
- function getGlobalExcludePatterns ( scope : ConfigurationScope | null ) : string [ ] {
56
- return Object . entries ( Workspace . getConfiguration ( 'files' , scope ) ?. get ( 'exclude' ) ?? [ ] )
57
- . filter ( ( [ , value ] ) => value === true )
58
- . map ( ( [ key ] ) => key )
59
- . filter ( Boolean )
60
- }
61
-
62
- function getExcludePatterns ( scope : ConfigurationScope | null ) : string [ ] {
63
- return [
64
- ...getGlobalExcludePatterns ( scope ) ,
65
- ...( < string [ ] > Workspace . getConfiguration ( 'tailwindCSS' , scope ) . get ( 'files.exclude' ) ) . filter (
66
- Boolean ,
67
- ) ,
68
- ]
69
- }
70
-
71
- function isExcluded ( file : string , folder : WorkspaceFolder ) : boolean {
72
- for ( let pattern of getExcludePatterns ( folder ) ) {
73
- let matcher = picomatch ( path . join ( folder . uri . fsPath , pattern ) )
74
-
75
- if ( matcher ( file ) ) {
76
- return true
77
- }
78
- }
79
-
80
- return false
81
- }
82
-
83
- function mergeExcludes ( settings : WorkspaceConfiguration , scope : ConfigurationScope | null ) : any {
84
- return {
85
- ...settings ,
86
- files : {
87
- ...settings . files ,
88
- exclude : getExcludePatterns ( scope ) ,
89
- } ,
90
- }
91
- }
92
-
93
- async function fileMayBeTailwindRelated ( uri : Uri ) {
94
- let contents = ( await Workspace . fs . readFile ( uri ) ) . toString ( )
95
-
96
- let HAS_CONFIG = / @ c o n f i g \s * [ ' " ] /
97
- let HAS_IMPORT = / @ i m p o r t \s * [ ' " ] /
98
- let HAS_TAILWIND = / @ t a i l w i n d \s * [ ^ ; ] + ; /
99
- let HAS_THEME = / @ t h e m e \s * \{ /
100
-
101
- return (
102
- HAS_CONFIG . test ( contents ) ||
103
- HAS_IMPORT . test ( contents ) ||
104
- HAS_TAILWIND . test ( contents ) ||
105
- HAS_THEME . test ( contents )
106
- )
107
- }
108
-
109
51
function selectionsAreEqual (
110
52
aSelections : readonly Selection [ ] ,
111
53
bSelections : readonly Selection [ ] ,
@@ -177,6 +119,12 @@ function resetActiveTextEditorContext(): void {
177
119
178
120
export async function activate ( context : ExtensionContext ) {
179
121
let outputChannel = Window . createOutputChannel ( CLIENT_NAME )
122
+
123
+ let api = await createApi ( {
124
+ context,
125
+ outputChannel,
126
+ } )
127
+
180
128
context . subscriptions . push ( outputChannel )
181
129
context . subscriptions . push (
182
130
commands . registerCommand ( 'tailwindCSS.showOutput' , ( ) => {
@@ -282,7 +230,7 @@ export async function activate(context: ExtensionContext) {
282
230
if ( ! folder || isExcluded ( uri . fsPath , folder ) ) {
283
231
return
284
232
}
285
- if ( await fileMayBeTailwindRelated ( uri ) ) {
233
+ if ( await api . stylesheetNeedsLanguageServer ( uri ) ) {
286
234
await bootWorkspaceClient ( )
287
235
}
288
236
}
@@ -579,87 +527,11 @@ export async function activate(context: ExtensionContext) {
579
527
}
580
528
581
529
async function bootClientIfNeeded ( ) : Promise < void > {
582
- if ( currentClient ) {
583
- return
584
- }
585
-
586
- let source : CancellationTokenSource | null = new CancellationTokenSource ( )
587
- source . token . onCancellationRequested ( ( ) => {
588
- source ?. dispose ( )
589
- source = null
590
- outputChannel . appendLine (
591
- 'Server was not started. Search for Tailwind CSS-related files was taking too long.' ,
592
- )
593
- } )
594
-
595
- // Cancel the search after roughly 15 seconds
596
- setTimeout ( ( ) => source ?. cancel ( ) , 15_000 )
530
+ if ( currentClient ) return
597
531
598
- if ( ! ( await anyFolderNeedsLanguageServer ( Workspace . workspaceFolders ?? [ ] , source ! . token ) ) ) {
599
- source ?. dispose ( )
600
- return
601
- }
602
-
603
- source ?. dispose ( )
604
-
605
- await bootWorkspaceClient ( )
606
- }
607
-
608
- async function anyFolderNeedsLanguageServer (
609
- folders : readonly WorkspaceFolder [ ] ,
610
- token : CancellationToken ,
611
- ) : Promise < boolean > {
612
- for ( let folder of folders ) {
613
- if ( await folderNeedsLanguageServer ( folder , token ) ) {
614
- return true
615
- }
616
- }
617
-
618
- return false
619
- }
620
-
621
- async function folderNeedsLanguageServer (
622
- folder : WorkspaceFolder ,
623
- token : CancellationToken ,
624
- ) : Promise < boolean > {
625
- let settings = Workspace . getConfiguration ( 'tailwindCSS' , folder )
626
- if ( settings . get ( 'experimental.configFile' ) !== null ) {
627
- return true
628
- }
629
-
630
- let exclude = `{${ getExcludePatterns ( folder )
631
- . flatMap ( ( pattern ) => braces . expand ( pattern ) )
632
- . join ( ',' )
633
- . replace ( / { / g, '%7B' )
634
- . replace ( / } / g, '%7D' ) } }`
635
-
636
- let configFiles = await Workspace . findFiles (
637
- new RelativePattern ( folder , `**/${ CONFIG_GLOB } ` ) ,
638
- exclude ,
639
- 1 ,
640
- token ,
641
- )
642
-
643
- for ( let file of configFiles ) {
644
- return true
645
- }
646
-
647
- let cssFiles = await Workspace . findFiles (
648
- new RelativePattern ( folder , `**/${ CSS_GLOB } ` ) ,
649
- exclude ,
650
- undefined ,
651
- token ,
652
- )
653
-
654
- for ( let file of cssFiles ) {
655
- outputChannel . appendLine ( `Checking if ${ file . fsPath } may be Tailwind-related…` )
656
-
657
- if ( await fileMayBeTailwindRelated ( file ) ) {
658
- return true
659
- }
532
+ if ( await api . workspaceNeedsLanguageServer ( ) ) {
533
+ await bootWorkspaceClient ( )
660
534
}
661
-
662
- return false
663
535
}
664
536
665
537
async function didOpenTextDocument ( document : TextDocument ) : Promise < void > {
0 commit comments