Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
filter @apply completions
Brad Cornes <brad@parall.ax>
5 years ago
3 changed files, 125 additions(+), 96 deletions(-)
src/class-names/extractClassNames.jssrc/class-names/index.jssrc/lsp/providers/completionProvider.ts
M src/class-names/extractClassNames.jssrc/class-names/extractClassNames.js
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
diff --git a/src/class-names/extractClassNames.js b/src/class-names/extractClassNames.js
index df4559939aba242c977b6be4adea01cfe05adb47..2aa426b22ab4a62489155861c15d87c5a0f3ce69 100644
--- a/src/class-names/extractClassNames.js
+++ b/src/class-names/extractClassNames.js
@@ -46,99 +46,102 @@
   return classNames
 }
 
-async function process(ast) {
+async function process(groups) {
   const tree = {}
   const commonContext = {}
 
-  ast.root.walkRules((rule) => {
-    const classNames = getClassNamesFromSelector(rule.selector)
+  groups.forEach((group) => {
+    group.root.walkRules((rule) => {
+      const classNames = getClassNamesFromSelector(rule.selector)
 
-    const decls = {}
-    rule.walkDecls((decl) => {
-      if (decls[decl.prop]) {
-        decls[decl.prop] = [
-          ...(Array.isArray(decls[decl.prop])
-            ? decls[decl.prop]
-            : [decls[decl.prop]]),
-          decl.value,
-        ]
-      } else {
-        decls[decl.prop] = decl.value
-      }
-    })
+      const decls = {}
+      rule.walkDecls((decl) => {
+        if (decls[decl.prop]) {
+          decls[decl.prop] = [
+            ...(Array.isArray(decls[decl.prop])
+              ? decls[decl.prop]
+              : [decls[decl.prop]]),
+            decl.value,
+          ]
+        } else {
+          decls[decl.prop] = decl.value
+        }
+      })
 
-    let p = rule
-    const keys = []
-    while (p.parent.type !== 'root') {
-      p = p.parent
-      if (p.type === 'atrule') {
-        keys.push(`@${p.name} ${p.params}`)
+      let p = rule
+      const keys = []
+      while (p.parent.type !== 'root') {
+        p = p.parent
+        if (p.type === 'atrule') {
+          keys.push(`@${p.name} ${p.params}`)
+        }
       }
-    }
 
-    for (let i = 0; i < classNames.length; i++) {
-      const context = keys.concat([])
-      const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
-      const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
-      const index = []
+      for (let i = 0; i < classNames.length; i++) {
+        const context = keys.concat([])
+        const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
+        const contextKeys = baseKeys.slice(0, baseKeys.length - 1)
+        const index = []
 
-      const existing = dlv(tree, baseKeys)
-      if (typeof existing !== 'undefined') {
-        if (Array.isArray(existing)) {
-          const scopeIndex = existing.findIndex(
-            (x) =>
-              x.__scope === classNames[i].scope &&
-              arraysEqual(existing.__context, context)
-          )
-          if (scopeIndex > -1) {
-            keys.unshift(scopeIndex)
-            index.push(scopeIndex)
+        const existing = dlv(tree, baseKeys)
+        if (typeof existing !== 'undefined') {
+          if (Array.isArray(existing)) {
+            const scopeIndex = existing.findIndex(
+              (x) =>
+                x.__scope === classNames[i].scope &&
+                arraysEqual(existing.__context, context)
+            )
+            if (scopeIndex > -1) {
+              keys.unshift(scopeIndex)
+              index.push(scopeIndex)
+            } else {
+              keys.unshift(existing.length)
+              index.push(existing.length)
+            }
           } else {
-            keys.unshift(existing.length)
-            index.push(existing.length)
-          }
-        } else {
-          if (
-            existing.__scope !== classNames[i].scope ||
-            !arraysEqual(existing.__context, context)
-          ) {
-            dset(tree, baseKeys, [existing])
-            keys.unshift(1)
-            index.push(1)
+            if (
+              existing.__scope !== classNames[i].scope ||
+              !arraysEqual(existing.__context, context)
+            ) {
+              dset(tree, baseKeys, [existing])
+              keys.unshift(1)
+              index.push(1)
+            }
           }
         }
-      }
-      if (classNames[i].__rule) {
-        dset(tree, [...baseKeys, ...index, '__rule'], true)
+        if (classNames[i].__rule) {
+          dset(tree, [...baseKeys, ...index, '__rule'], true)
+          dset(tree, [...baseKeys, ...index, '__source'], group.source)
 
-        dsetEach(tree, [...baseKeys, ...index], decls)
-      }
-      if (classNames[i].__pseudo) {
-        dset(tree, [...baseKeys, '__pseudo'], classNames[i].__pseudo)
-      }
-      dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
-      dset(
-        tree,
-        [...baseKeys, ...index, '__context'],
-        context.concat([]).reverse()
-      )
+          dsetEach(tree, [...baseKeys, ...index], decls)
+        }
+        if (classNames[i].__pseudo) {
+          dset(tree, [...baseKeys, '__pseudo'], classNames[i].__pseudo)
+        }
+        dset(tree, [...baseKeys, ...index, '__scope'], classNames[i].scope)
+        dset(
+          tree,
+          [...baseKeys, ...index, '__context'],
+          context.concat([]).reverse()
+        )
 
-      // common context
-      if (classNames[i].__pseudo) {
-        context.push(...classNames[i].__pseudo)
-      }
+        // common context
+        if (classNames[i].__pseudo) {
+          context.push(...classNames[i].__pseudo)
+        }
 
-      for (let i = 0; i < contextKeys.length; i++) {
-        if (typeof commonContext[contextKeys[i]] === 'undefined') {
-          commonContext[contextKeys[i]] = context
-        } else {
-          commonContext[contextKeys[i]] = intersection(
-            commonContext[contextKeys[i]],
-            context
-          )
+        for (let i = 0; i < contextKeys.length; i++) {
+          if (typeof commonContext[contextKeys[i]] === 'undefined') {
+            commonContext[contextKeys[i]] = context
+          } else {
+            commonContext[contextKeys[i]] = intersection(
+              commonContext[contextKeys[i]],
+              context
+            )
+          }
         }
       }
-    }
+    })
   })
 
   return { classNames: tree, context: commonContext }
M src/class-names/index.jssrc/class-names/index.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
diff --git a/src/class-names/index.js b/src/class-names/index.js
index 1e2d3e5fd54fe7bf1452d8893f155532686fa118..8aef0b0adf2a433c918501f3fc400b5ef94145d3 100644
--- a/src/class-names/index.js
+++ b/src/class-names/index.js
@@ -83,12 +83,12 @@       throw new TailwindConfigError(error)
     }
     hook.unwatch()
 
-    const ast = await postcss([tailwindcss(configPath)]).process(
-      `
-        @tailwind components;
-        @tailwind utilities;
-      `,
-      { from: undefined }
+    const [components, utilities] = await Promise.all(
+      ['components', 'utilities'].map((group) =>
+        postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
+          from: undefined,
+        })
+      )
     )
 
     hook.unhook()
@@ -111,7 +111,10 @@       version,
       configPath,
       config: resolvedConfig,
       separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
-      classNames: await extractClassNames(ast),
+      classNames: await extractClassNames([
+        { root: components.root, source: 'components' },
+        { root: utilities.root, source: 'utilities' },
+      ]),
       dependencies: hook.deps,
       plugins: getPlugins(config),
       variants: getVariants({ config, version, postcss, browserslist }),
M src/lsp/providers/completionProvider.tssrc/lsp/providers/completionProvider.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
diff --git a/src/lsp/providers/completionProvider.ts b/src/lsp/providers/completionProvider.ts
index d7cbbef0d734dfb224fbe93edcff2f7b23a8f921..8f2c43d8325bc847a7b05a87dc180025a647f2a9 100644
--- a/src/lsp/providers/completionProvider.ts
+++ b/src/lsp/providers/completionProvider.ts
@@ -28,7 +28,8 @@
 function completionsFromClassList(
   state: State,
   classList: string,
-  classListRange: Range
+  classListRange: Range,
+  filter?: (item: CompletionItem) => boolean
 ): CompletionList {
   let classNames = classList.split(/[\s+]/)
   const partialClassName = classNames[classNames.length - 1]
@@ -68,8 +69,8 @@   }
 
   return {
     isIncomplete: false,
-    items: Object.keys(isSubset ? subset : state.classNames.classNames).map(
-      (className, index) => {
+    items: Object.keys(isSubset ? subset : state.classNames.classNames)
+      .map((className, index) => {
         let label = className
         let kind: CompletionItemKind = CompletionItemKind.Constant
         let documentation: string = null
@@ -88,7 +89,7 @@             documentation = color.documentation
           }
         }
 
-        return {
+        const item = {
           label,
           kind,
           documentation,
@@ -100,8 +101,14 @@             newText: label,
             range: replacementRange,
           },
         }
-      }
-    ),
+
+        if (filter && !filter(item)) {
+          return null
+        }
+
+        return item
+      })
+      .filter((item) => item !== null),
   }
 }
 
@@ -175,13 +182,29 @@   }
 
   const classList = match.groups.classList
 
-  return completionsFromClassList(state, classList, {
-    start: {
-      line: position.line,
-      character: position.character - classList.length,
+  return completionsFromClassList(
+    state,
+    classList,
+    {
+      start: {
+        line: position.line,
+        character: position.character - classList.length,
+      },
+      end: position,
     },
-    end: position,
-  })
+    (item) => {
+      // TODO: first line excludes all subtrees but there could _technically_ be
+      // valid apply-able class names in there. Will be correct in 99% of cases
+      if (item.kind === CompletionItemKind.Module) return false
+      let info = dlv(state.classNames.classNames, item.data)
+      return (
+        !Array.isArray(info) &&
+        info.__source === 'utilities' &&
+        (info.__context || []).length === 0 &&
+        (info.__pseudo || []).length === 0
+      )
+    }
+  )
 }
 
 function provideClassNameCompletions(