Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
Merge branch 'next' into diagnostics
Brad Cornes <brad@parall.ax>
4 years ago
4 changed files, 132 additions(+), 97 deletions(-)
M src/class-names/extractClassNames.js -> src/class-names/extractClassNames.js
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.js -> src/class-names/index.js
diff --git a/src/class-names/index.js b/src/class-names/index.js
index 1e2d3e5fd54fe7bf1452d8893f155532686fa118..62d478d3f212678d9eaba2d53ffcaabcc38261f7 100644
--- a/src/class-names/index.js
+++ b/src/class-names/index.js
@@ -83,12 +83,16 @@       throw new TailwindConfigError(error)
     }
     hook.unwatch()
 
-    const ast = await postcss([tailwindcss(configPath)]).process(
-      `
-        @tailwind components;
-        @tailwind utilities;
-      `,
-      { from: undefined }
+    const [base, components, utilities] = await Promise.all(
+      [
+        semver.gte(version, '0.99.0') ? 'base' : 'preflight',
+        'components',
+        'utilities',
+      ].map((group) =>
+        postcss([tailwindcss(configPath)]).process(`@tailwind ${group};`, {
+          from: undefined,
+        })
+      )
     )
 
     hook.unhook()
@@ -111,7 +115,11 @@       version,
       configPath,
       config: resolvedConfig,
       separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
-      classNames: await extractClassNames(ast),
+      classNames: await extractClassNames([
+        { root: base.root, source: 'base' },
+        { 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.ts -> src/lsp/providers/completionProvider.ts
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(
M src/lsp/util/removeMeta.ts -> src/lsp/util/removeMeta.ts
diff --git a/src/lsp/util/removeMeta.ts b/src/lsp/util/removeMeta.ts
index 7e379a81a580ee60a4462230a61dd134afe32e9e..dfe58d336a4d4f355d1e634175694e44d8d4e756 100644
--- a/src/lsp/util/removeMeta.ts
+++ b/src/lsp/util/removeMeta.ts
@@ -3,9 +3,10 @@
 export default function removeMeta(obj: any): any {
   let result = {}
   for (let key in obj) {
+    if (key.substr(0, 2) === '__') continue
     if (isObject(obj[key])) {
       result[key] = removeMeta(obj[key])
-    } else if (key.substr(0, 2) !== '__') {
+    } else {
       result[key] = obj[key]
     }
   }