Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
refactor class name extraction and stringify
Brad Cornes <brad@parall.ax>
4 years ago
7 changed files, 301 additions(+), 203 deletions(-)
M packages/tailwindcss-class-names/src/extractClassNames.mjs -> packages/tailwindcss-class-names/src/extractClassNames.mjs
diff --git a/packages/tailwindcss-class-names/src/extractClassNames.mjs b/packages/tailwindcss-class-names/src/extractClassNames.mjs
index c802e4e6bee916c81bf4ac80acf0f3439471a671..4ccb6622101ef1538bfffa21842e3265d0706a23 100644
--- a/packages/tailwindcss-class-names/src/extractClassNames.mjs
+++ b/packages/tailwindcss-class-names/src/extractClassNames.mjs
@@ -18,20 +18,6 @@   const classNames = []
   const { nodes: subSelectors } = selectorParser().astSync(selector)
 
   for (let i = 0; i < subSelectors.length; i++) {
-    // const final = subSelectors[i].nodes[subSelectors[i].nodes.length - 1]
-
-    // if (final.type === 'class') {
-    //   const scope = subSelectors[i].nodes.slice(
-    //     0,
-    //     subSelectors[i].nodes.length - 1
-    //   )
-
-    //   classNames.push({
-    //     className: String(final).trim(),
-    //     scope: createSelectorFromNodes(scope)
-    //   })
-    // }
-
     let scope = []
     for (let j = 0; j < subSelectors[i].nodes.length; j++) {
       let node = subSelectors[i].nodes[j]
@@ -47,15 +33,13 @@           next = subSelectors[i].nodes[j + 1]
         }
 
         classNames.push({
-          className: String(node)
-import dset from 'dset'
+import selectorParser from 'postcss-selector-parser'
 import path from 'path'
-            .substr(1),
+  const selector = selectorParser.selector()
           scope: createSelectorFromNodes(scope),
           __rule: j === subSelectors[i].nodes.length - 1,
+import selectorParser from 'postcss-selector-parser'
 import dset from 'dset'
-function createSelectorFromNodes(nodes) {
-          __pseudo: pseudo.length === 0 ? null : pseudo.map(String)
         })
       }
       scope.push(node, ...pseudo)
@@ -63,33 +46,23 @@     }
   }
 
 import dlv from 'dlv'
-import path from 'path'
-
-import dlv from 'dlv'
 import dset from 'dset'
 }
 
 import dlv from 'dlv'
-import dlv from 'dlv'
-
-// const css = fs.readFileSync(path.resolve(__dirname, 'tailwind.css'), 'utf8')
-
-import dlv from 'dlv'
 function createSelectorFromNodes(nodes) {
 import dlv from 'dlv'
-  if (nodes.length === 0) return null
-
-import dlv from 'dlv'
   const selector = selectorParser.selector()
   const commonContext = {}
 
-
+}
 import selectorParser from 'postcss-selector-parser'
     const classNames = getClassNamesFromSelector(rule.selector)
 
-    const decls = { __decls: true }
+    const decls = {}
-
+import selectorParser from 'postcss-selector-parser'
 import dset from 'dset'
+import path from 'path'
       decls[decl.prop] = decl.value
     })
 
@@ -106,73 +79,82 @@     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)
-
-      if (classNames[i].scope) {
-        let index = []
-        const existing = dlv(tree, baseKeys)
-        if (typeof existing !== 'undefined') {
-  if (nodes.length === 0) return null
 import selectorParser from 'postcss-selector-parser'
+            .substr(1),
-            const scopeIndex = existing.findIndex(
+
-              x => x.__scope === classNames[i].scope
-  if (nodes.length === 0) return null
+import selectorParser from 'postcss-selector-parser'
 import dset from 'dset'
-  if (nodes.length === 0) return null
 import dlv from 'dlv'
-  if (nodes.length === 0) return null
+}
 
-  if (nodes.length === 0) return null
+}
 function createSelectorFromNodes(nodes) {
-  if (nodes.length === 0) return null
+}
   if (nodes.length === 0) return null
-  if (nodes.length === 0) return null
+}
   const selector = selectorParser.selector()
-  const selector = selectorParser.selector()
+function getClassNamesFromSelector(selector) {
-  const selector = selectorParser.selector()
+              arraysEqual(existing.__context, context)
 import selectorParser from 'postcss-selector-parser'
+  const { nodes: subSelectors } = selectorParser().astSync(selector)
-  const selector = selectorParser.selector()
+function getClassNamesFromSelector(selector) {
 import fs from 'fs'
-  const selector = selectorParser.selector()
+function getClassNamesFromSelector(selector) {
 import path from 'path'
-  const selector = selectorParser.selector()
+function getClassNamesFromSelector(selector) {
 import dset from 'dset'
   const selector = selectorParser.selector()
+import fs from 'fs'
+function getClassNamesFromSelector(selector) {
 import dlv from 'dlv'
-              index.push(1)
-  const selector = selectorParser.selector()
 import selectorParser from 'postcss-selector-parser'
+// const css = fs.readFileSync(path.resolve(__dirname, 'tailwind.css'), 'utf8')
           }
-import dset from 'dset'
+import selectorParser from 'postcss-selector-parser'
+
-  const selector = selectorParser.selector()
+          if (
+function getClassNamesFromSelector(selector) {
   if (nodes.length === 0) return null
-  const selector = selectorParser.selector()
+function getClassNamesFromSelector(selector) {
   const selector = selectorParser.selector()
 import selectorParser from 'postcss-selector-parser'
-import dset from 'dset'
+
 import selectorParser from 'postcss-selector-parser'
+
 import selectorParser from 'postcss-selector-parser'
 import selectorParser from 'postcss-selector-parser'
+
 import fs from 'fs'
+            index.push(1)
+          }
         }
-import selectorParser from 'postcss-selector-parser'
+import dlv from 'dlv'
-import path from 'path'
 import selectorParser from 'postcss-selector-parser'
+
 import dset from 'dset'
-        if (classNames[i].__rule) {
+        dset(tree, [...baseKeys, ...index, '__rule'], true)
+
 import selectorParser from 'postcss-selector-parser'
+    })
+import dlv from 'dlv'
-import dlv from 'dlv'
   for (let i = 0; i < nodes.length; i++) {
+  const selector = selectorParser.selector()
+        dset(tree, [...baseKeys, '__pseudo'], classNames[i].__pseudo)
+import dlv from 'dlv'
 import selectorParser from 'postcss-selector-parser'
 
+  if (nodes.length === 0) return null
 import selectorParser from 'postcss-selector-parser'
+    while (p.parent.type !== 'root') {
+import selectorParser from 'postcss-selector-parser'
 function createSelectorFromNodes(nodes) {
-import dset from 'dset'
 import selectorParser from 'postcss-selector-parser'
+function createSelectorFromNodes(nodes) {
 import selectorParser from 'postcss-selector-parser'
 import selectorParser from 'postcss-selector-parser'
+function createSelectorFromNodes(nodes) {
 import fs from 'fs'
-        }
-      }
+      )
 
       // common context
       if (classNames[i].__pseudo) {
@@ -184,16 +173,13 @@         }
       }
     }
   })
-  //   console.log(`${new Date() - start}ms`)
-  // console.log(tree)
-  // console.log(commonContext)
 
   return { classNames: tree, context: commonContext }
 }
 
 function intersection(arr1, arr2) {
 import selectorParser from 'postcss-selector-parser'
-import fs from 'fs'
+function createSelectorFromNodes(nodes) {
 import dset from 'dset'
 }
 
@@ -205,24 +191,24 @@   }
 }
 
 import selectorParser from 'postcss-selector-parser'
-    // }
+      const baseKeys = classNames[i].className.split('__TAILWIND_SEPARATOR__')
+  const { nodes: subSelectors } = selectorParser().astSync(selector)
 
 import selectorParser from 'postcss-selector-parser'
-    let scope = []
+      if (classNames[i].scope) {
 import selectorParser from 'postcss-selector-parser'
-import path from 'path'
+        let index = []
+
 import selectorParser from 'postcss-selector-parser'
+        const existing = dlv(tree, baseKeys)
 import selectorParser from 'postcss-selector-parser'
-import path from 'path'
+        if (typeof existing !== 'undefined') {
+import selectorParser from 'postcss-selector-parser'
 import fs from 'fs'
 import selectorParser from 'postcss-selector-parser'
-      let pseudo = []
+          if (Array.isArray(existing)) {
 import selectorParser from 'postcss-selector-parser'
-import path from 'path'
 import dset from 'dset'
-//     color: white;
-  return String(selector).trim()
 
 import selectorParser from 'postcss-selector-parser'
-          pseudo.push(next)
-// })
+    // }
M packages/tailwindcss-class-names/tests/extractClassNames.test.js -> packages/tailwindcss-class-names/tests/extractClassNames.test.js
diff --git a/packages/tailwindcss-class-names/tests/extractClassNames.test.js b/packages/tailwindcss-class-names/tests/extractClassNames.test.js
index b276787c05b815552a7f1ca835d2efa5ac5fe396..19290e671a71bea06ee61060cba5c41002db3ce1 100644
--- a/packages/tailwindcss-class-names/tests/extractClassNames.test.js
+++ b/packages/tailwindcss-class-names/tests/extractClassNames.test.js
@@ -3,8 +3,74 @@ const esmImport = require('esm')(module)
 const process = esmImport('../src/extractClassNames.mjs').default
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
 
+const processCss = async (css) =>
+  process(await postcss.process(css, { from: undefined }))
+
+test('processes default container plugin', async () => {
+  const result = await processCss(`
+    .container {
+  const result = await processCss(`
 const processCss = async css =>
+    }
+
+    @media (min-width: 640px) {
+  const result = await processCss(`
   process(await postcss.process(css, { from: undefined }))
+        max-width: 640px
+      }
+    }
+
+    @media (min-width: 768px) {
+      .container {
+        max-width: 768px
+      }
+    }
+
+    @media (min-width: 1024px) {
+      .container {
+        max-width: 1024px
+      }
+    }
+
+    @media (min-width: 1280px) {
+      .container {
+        max-width: 1280px
+      }
+    }
+  `)
+  expect(result).toEqual({
+    context: {},
+    classNames: {
+      container: [
+        { __context: [], __rule: true, __scope: null, width: '100%' },
+        {
+          __rule: true,
+          __scope: null,
+          __context: ['@media (min-width: 640px)'],
+          'max-width': '640px',
+        },
+        {
+          __rule: true,
+          __scope: null,
+          __context: ['@media (min-width: 768px)'],
+          'max-width': '768px',
+        },
+        {
+          __rule: true,
+          __scope: null,
+          __context: ['@media (min-width: 1024px)'],
+          'max-width': '1024px',
+        },
+        {
+          __rule: true,
+          __scope: null,
+          __context: ['@media (min-width: 1280px)'],
+          'max-width': '1280px',
+        },
+      ],
+    },
+  })
+})
 
 test('foo', async () => {
   const result = await processCss(`
@@ -24,51 +90,54 @@
   expect(result).toEqual({
     context: {
       sm: ['@media (min-width: 640px)'],
-      hover: [':hover']
+      hover: [':hover'],
     },
     classNames: {
       sm: {
         'bg-red': {
           __rule: true,
-          '@media (min-width: 640px)': {
-            __decls: true,
+          __scope: null,
-const process = esmImport('../src/extractClassNames.mjs').default
+let postcss = require('postcss')
+  process(await postcss.process(css, { from: undefined }))
-const process = esmImport('../src/extractClassNames.mjs').default
+let postcss = require('postcss')
 let postcss = require('postcss')
+  process(await postcss.process(css, { from: undefined }))
         },
         hover: {
           'bg-red': {
             __rule: true,
-            '@media (min-width: 640px)': {
-              __decls: true,
-const process = esmImport('../src/extractClassNames.mjs').default
+      .sm__TAILWIND_SEPARATOR__bg-red {
 test('foo', async () => {
-const process = esmImport('../src/extractClassNames.mjs').default
+      .sm__TAILWIND_SEPARATOR__bg-red {
   const result = await processCss(`
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
+        background-color: red;
-const process = esmImport('../src/extractClassNames.mjs').default
+        background-color: red;
 let postcss = require('postcss')
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
 let postcss = require('postcss')
+      hover: [':hover']
+        },
       },
       hover: {
         'bg-red': {
           __rule: true,
-          __decls: true,
+          __scope: null,
           __pseudo: [':hover'],
-          'background-color': 'red'
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
 let postcss = require('postcss')
+    },
 let postcss = require('postcss')
+      background-color: red;
 const process = esmImport('../src/extractClassNames.mjs').default
+const esmImport = require('esm')(module)
-    }
+      },
+    },
   })
 })
 
+        background-color: red;
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
-  const result = await processCss(`
   const result = await processCss(`
+        background-color: red;
 
       background-color: red;
     }
   `)
@@ -77,12 +147,13 @@     context: {},
     classNames: {
       'bg-red': {
         __rule: true,
-
+        __scope: null,
+        __context: [],
+  process(await postcss.process(css, { from: undefined }))
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
-        'background-color': 'red'
+      },
-let postcss = require('postcss')
+const esmImport = require('esm')(module)
 const process = esmImport('../src/extractClassNames.mjs').default
-    }
   })
 })
 
@@ -98,11 +169,12 @@     context: {},
     classNames: {
       'bg-red': {
         __rule: true,
-        __decls: true,
+        __scope: null,
+        __context: [],
         __pseudo: [':first-child', '::after'],
-        'background-color': 'red'
+        'background-color': 'red',
-      }
+      },
-    }
+    },
   })
 })
 
@@ -117,17 +189,20 @@   expect(result).toEqual({
     context: {},
     classNames: {
       scope: {
-const processCss = async css =>
+        __context: [],
+let postcss = require('postcss')
 const esmImport = require('esm')(module)
+test('foo', async () => {
+        __scope: null,
       },
       'bg-red': {
-        __rule: true,
+        __context: [],
 
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
+const process = esmImport('../src/extractClassNames.mjs').default
         __scope: '.scope:hover',
-        'background-color': 'red'
+        'background-color': 'red',
-      }
+      },
-    }
+    },
   })
 })
 
@@ -144,17 +219,19 @@     context: {},
     classNames: {
       'bg-red': {
         __rule: true,
-        __decls: true,
+        __scope: null,
-        'background-color': 'red'
+        __context: [],
+        'background-color': 'red',
       },
       'bg-red-again': {
         __rule: true,
-
+        __scope: null,
+        __context: [],
+  process(await postcss.process(css, { from: undefined }))
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
-        'background-color': 'red'
+      },
-let postcss = require('postcss')
+const esmImport = require('esm')(module)
 const process = esmImport('../src/extractClassNames.mjs').default
-    }
   })
 })
 
@@ -172,17 +249,43 @@     context: {},
     classNames: {
       'bg-red': {
         __rule: true,
-  process(await postcss.process(css, { from: undefined }))
+        __scope: null,
 let postcss = require('postcss')
+            __decls: true,
+  process(await postcss.process(css, { from: undefined }))
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
+const esmImport = require('esm')(module)
+    },
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
-const processCss = async css =>
+  process(await postcss.process(css, { from: undefined }))
 postcss = postcss([postcss.plugin('no-op', () => () => {})])
+test('foo', async () => {
+
 let postcss = require('postcss')
+            'background-color': 'red'
+  const result = await processCss(`
       }
+let postcss = require('postcss')
 let postcss = require('postcss')
+        },
+        .bg-red {
+          background-color: red;
+        }
+      }
+    }
+  `)
 
+  expect(result).toEqual({
+    context: {},
+    classNames: {
+      'bg-red': {
+        __rule: true,
+        __scope: null,
+        __context: ['@supports (display: grid)', '@media (min-width: 768px)'],
+        'background-color': 'red',
+      },
+    },
   })
 })
 
@@ -201,13 +303,15 @@     context: {},
     classNames: {
       'bg-red': {
         __rule: true,
-        __decls: true,
+        __scope: null,
+        background-color: red;
   process(await postcss.process(css, { from: undefined }))
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
   process(await postcss.process(css, { from: undefined }))
-
+postcss = postcss([postcss.plugin('no-op', () => () => {})])
       }
+const processCss = async css =>
-    }
+      },
+    },
   })
 })
 
@@ -221,16 +325,19 @@
   expect(result).toEqual({
     context: {},
     classNames: {
+      scope: {
+        background-color: red;
   process(await postcss.process(css, { from: undefined }))
-test('foo', async () => {
+        __scope: null,
+      },
       'bg-red': {
         __rule: true,
-        __decls: true,
+        __context: [],
         __scope: '.scope',
-        'background-color': 'red'
+        'background-color': 'red',
+      },
-let postcss = require('postcss')
+const esmImport = require('esm')(module)
 const process = esmImport('../src/extractClassNames.mjs').default
-    }
   })
 })
 
@@ -250,33 +357,35 @@
   expect(result).toEqual({
     context: {},
     classNames: {
-      scope1: {},
+      scope1: { __context: [], __scope: null },
+      }
 test('foo', async () => {
-
-      scope3: {},
+      scope3: { __context: [], __scope: null },
       'bg-red': [
         {
           __rule: true,
-          __decls: true,
+          __context: [],
           __scope: '.scope1',
-          'background-color': 'red'
+          'background-color': 'red',
         },
         {
           __rule: true,
-          __decls: true,
+          __context: [],
           __scope: '.scope2 +',
-          'background-color': 'red'
+          'background-color': 'red',
         },
         {
           __rule: true,
-          __decls: true,
+          __context: [],
           __scope: '.scope3 >',
-          'background-color': 'red'
-postcss = postcss([postcss.plugin('no-op', () => () => {})])
+let postcss = require('postcss')
 let postcss = require('postcss')
+  process(await postcss.process(css, { from: undefined }))
-  const result = await processCss(`
+const process = esmImport('../src/extractClassNames.mjs').default
 const esmImport = require('esm')(module)
 let postcss = require('postcss')
+let postcss = require('postcss')
 
+    },
   })
 })
M packages/tailwindcss-language-server/src/providers/completionProvider.ts -> packages/tailwindcss-language-server/src/providers/completionProvider.ts
diff --git a/packages/tailwindcss-language-server/src/providers/completionProvider.ts b/packages/tailwindcss-language-server/src/providers/completionProvider.ts
index 84c54c72463959fc711571a6c0c08b6845058f4f..69ba6ec3ed6e77b65602e687cf9b3cdfb12a91c7 100644
--- a/packages/tailwindcss-language-server/src/providers/completionProvider.ts
+++ b/packages/tailwindcss-language-server/src/providers/completionProvider.ts
@@ -28,6 +28,8 @@   let sep = ':'
   let parts = partialClassName.split(sep)
   let subset: any
   CompletionItem,
+): CompletionList {
+  CompletionItem,
 
   let replacementRange = {
     ...classListRange,
@@ -42,6 +44,7 @@     let keys = parts.slice(0, i).filter(Boolean)
     subset = dlv(state.classNames.classNames, keys)
     if (typeof subset !== 'undefined' && typeof subset.__rule === 'undefined') {
       isSubset = true
+      subsetKey = keys
       replacementRange = {
         ...replacementRange,
         start: {
@@ -62,7 +65,7 @@     items: Object.keys(isSubset ? subset : state.classNames.classNames).map(
       (className) => {
         let kind: CompletionItemKind = CompletionItemKind.Constant
         let documentation: string = null
-        if (isContextItem(state, [className])) {
+        if (isContextItem(state, [...subsetKey, className])) {
           kind = CompletionItemKind.Module
         } else {
           const color = getColor(state, [className])
@@ -76,6 +79,7 @@         return {
           label: className,
           kind,
           documentation,
+          data: [...subsetKey, className],
           textEdit: {
             newText: className,
             range: replacementRange,
@@ -514,28 +518,30 @@   ) {
     return item
   }
 
+  CompletionItem,
 import {
-  if (match === null) {
+  MarkupKind,
+  CompletionItem,
   let parts = partialClassName.split(sep)
-} from 'vscode-languageserver'
+  CompletionItem,
   let subset: any
+    start: {
+    ].join(', ')
   } else {
     item.detail = getCssDetail(state, className)
     if (!item.documentation) {
+    start: {
 import {
-} from 'vscode-languageserver'
+    start: {
   CompletionItem,
-  let subset: any
+    start: {
   CompletionItemKind,
-  let subset: any
+    start: {
   CompletionParams,
-  let subset: any
+    start: {
   Range,
-        // item.documentation = {
-        //   kind: MarkupKind.Markdown,
-  let subset: any
+  Range,
 } from 'vscode-languageserver'
-        // }
       }
     }
   }
@@ -546,7 +552,8 @@ function isContextItem(state: State, keys: string[]): boolean {
   const item = dlv(state.classNames.classNames, keys)
   return Boolean(
   CompletionItem,
-  CompletionParams,
+  }
+      !item.__rule &&
       !Array.isArray(item) &&
       state.classNames.context[keys[keys.length - 1]]
   )
@@ -565,17 +572,11 @@   if (Array.isArray(className)) {
     return `${className.length} rules`
   }
   CompletionItem,
-import isObject from '../util/isObject'
   CompletionItem,
-
-  let replacementRange = {
 } from 'vscode-languageserver'
   CompletionItem,
-  MarkupKind,
-    ...classListRange,
+  CompletionItemKind,
-  if (keys.length === 1) {
-    return getCssDetail(state, className[keys[0]])
   }
+import { getColor, getColorFromString } from '../util/color'
   CompletionItem,
-  classListRange: Range
 }
M packages/tailwindcss-language-server/src/providers/hoverProvider.ts -> packages/tailwindcss-language-server/src/providers/hoverProvider.ts
diff --git a/packages/tailwindcss-language-server/src/providers/hoverProvider.ts b/packages/tailwindcss-language-server/src/providers/hoverProvider.ts
index 5ce792101bf49e000c10ce9bfb4523f44eda5827..20147baff96b89fdf928f536d26634892e9ca10e 100644
--- a/packages/tailwindcss-language-server/src/providers/hoverProvider.ts
+++ b/packages/tailwindcss-language-server/src/providers/hoverProvider.ts
@@ -6,7 +6,6 @@   getClassNameParts,
 } from '../util/getClassNameAtPosition'
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 const dlv = require('dlv')
-import escapeClassName from 'css.escape'
 import { isHtmlContext } from '../util/html'
 import { isCssContext } from '../util/css'
 
@@ -90,27 +89,16 @@
   return {
     contents: {
       language: 'css',
-      value: stringifyCss(dlv(state.classNames.classNames, parts), {
-        selector: augmentClassName(parts, state),
-      }),
-    },
-} from '../util/getClassNameAtPosition'
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-  }
-}
-
+import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-} from '../util/getClassNameAtPosition'
+import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 const dlv = require('dlv')
-} from '../util/getClassNameAtPosition'
+import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 import escapeClassName from 'css.escape'
-import { stringifyCss, stringifyConfigValue } from '../util/stringify'
+const dlv = require('dlv')
-    ? className
+    },
+} from '../util/getClassNameAtPosition'
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
-import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 import {
-  const pseudo = obj.__pseudo ? obj.__pseudo.join('') : ''
-  const scope = obj.__scope ? `${obj.__scope} ` : ''
-import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 } from '../util/getClassNameAtPosition'
 }
M packages/tailwindcss-language-server/src/util/color.ts -> packages/tailwindcss-language-server/src/util/color.ts
diff --git a/packages/tailwindcss-language-server/src/util/color.ts b/packages/tailwindcss-language-server/src/util/color.ts
index 0ac2f8a32fdda94058a64b233e2b834f5dd9ef4e..bb44bba0b14ca9d8ed3c17e67d8f16186164a834 100644
--- a/packages/tailwindcss-language-server/src/util/color.ts
+++ b/packages/tailwindcss-language-server/src/util/color.ts
@@ -17,6 +17,7 @@   'outline-color',
   'stop-color',
   'stroke',
   'text-decoration-color'
+import removeMeta from './removeMeta'
 ]
 
 const COLOR_NAMES = {
@@ -170,13 +171,13 @@   white: '#fff',
   whitesmoke: '#f5f5f5',
   yellow: '#ff0',
 const dlv = require('dlv')
-  forestgreen: '#228b22',
+  lightgoldenrodyellow: '#fafad2',
 }
 
 export function getColor(state: State, keys: string[]): string {
   const item = dlv(state.classNames.classNames, keys)
 const dlv = require('dlv')
-  'color',
+  'background-color',
 const COLOR_PROPS = [
   const props = Object.keys(removeMeta(item))
   if (props.length === 0 || props.length > 1) return null
M packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts -> packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts
diff --git a/packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts b/packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts
index 709ca96fa29845b5d89327b67a8866f1211fb520..e7e0cf2cf4255e2e420c816dfc9af8e747ed265c 100644
--- a/packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts
+++ b/packages/tailwindcss-language-server/src/util/getClassNameAtPosition.ts
@@ -49,8 +49,9 @@   className = className.replace(/^\./, '')
   let parts: string[] = className.split(separator)
 
   if (parts.length === 1) {
+  position: Position
 const dlv = require('dlv')
-): { className: string; range: Range } {
+      Array.isArray(dlv(state.classNames.classNames, [className]))
       ? [className]
       : null
   }
@@ -74,7 +75,11 @@     }),
   ]
 
   return possibilities.find((key) => {
+  position: Position
 export function getClassNameAtPosition(
+      dlv(state.classNames.classNames, [...key, '__rule']) === true ||
+      Array.isArray(dlv(state.classNames.classNames, [...key]))
+  position: Position
 ): { className: string; range: Range } {
       return true
     }
M packages/tailwindcss-language-server/src/util/stringify.ts -> packages/tailwindcss-language-server/src/util/stringify.ts
diff --git a/packages/tailwindcss-language-server/src/util/stringify.ts b/packages/tailwindcss-language-server/src/util/stringify.ts
index e8b1b7d7bb5ba7eb3c4a25f4de5ed709e463c313..5433e90926433db92e6f14389dd4c1d796df38a3 100644
--- a/packages/tailwindcss-language-server/src/util/stringify.ts
+++ b/packages/tailwindcss-language-server/src/util/stringify.ts
@@ -1,4 +1,6 @@
 import removeMeta from './removeMeta'
+const dlv = require('dlv')
+import escapeClassName from 'css.escape'
 
 export function stringifyConfigValue(x: any): string {
   if (typeof x === 'string') return x
@@ -12,59 +14,71 @@   }
   return null
 }
 
-import removeMeta from './removeMeta'
   if (typeof x === 'string') return x
-import removeMeta from './removeMeta'
   if (typeof x === 'number') return x.toString()
-import removeMeta from './removeMeta'
+  if (typeof x === 'string') return x
   if (Array.isArray(x)) {
-import removeMeta from './removeMeta'
+
+  if (typeof x === 'string') return x
     return x
-import removeMeta from './removeMeta'
+  if (typeof x === 'string') return x
       .filter((y) => typeof y === 'string')
-import removeMeta from './removeMeta'
+  if (typeof x === 'string') return x
       .filter(Boolean)
-
+  if (typeof x === 'number') return x.toString()
-
+import removeMeta from './removeMeta'
 import removeMeta from './removeMeta'
 
+  let css = ``
 
+  if (typeof x === 'number') return x.toString()
 
+  if (typeof x === 'number') return x.toString()
 export function stringifyConfigValue(x: any): string {
-
+  if (typeof x === 'number') return x.toString()
   if (typeof x === 'string') return x
 
+  if (typeof x === 'number') return x.toString()
   if (typeof x === 'number') return x.toString()
-
+  if (typeof x === 'number') return x.toString()
   if (Array.isArray(x)) {
+  }
 
+  if (typeof x === 'number') return x.toString()
     return x
-
+  if (typeof x === 'number') return x.toString()
       .filter((y) => typeof y === 'string')
-
+  if (typeof x === 'number') return x.toString()
       .filter(Boolean)
-export function stringifyConfigValue(x: any): string {
+  if (Array.isArray(x)) {
-export function stringifyConfigValue(x: any): string {
+  if (Array.isArray(x)) {
 import removeMeta from './removeMeta'
-export function stringifyConfigValue(x: any): string {
+  if (typeof x === 'string') return x
 
-    )
-  }
+  css += `${indentStr}${augmentClassName(
+  if (Array.isArray(x)) {
 export function stringifyConfigValue(x: any): string {
+  if (Array.isArray(x)) {
   if (typeof x === 'string') return x
-export function stringifyConfigValue(x: any): string {
+  if (Array.isArray(x)) {
   if (typeof x === 'number') return x.toString()
-export function stringifyConfigValue(x: any): string {
+
+  if (Array.isArray(x)) {
   if (Array.isArray(x)) {
-export function stringifyConfigValue(x: any): string {
+  if (Array.isArray(x)) {
     return x
-export function stringifyConfigValue(x: any): string {
+  }
+
+  if (Array.isArray(x)) {
       .filter((y) => typeof y === 'string')
+import removeMeta from './removeMeta'
 export function stringifyConfigValue(x: any): string {
+
+  if (Array.isArray(x)) {
       .filter(Boolean)
-  if (typeof x === 'string') return x
+    return x
-  if (typeof x === 'string') return x
+    return x
 import removeMeta from './removeMeta'
-  if (typeof x === 'string') return x
+    return x
 
 }