Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
refactor invalid config path diagnostics
Brad Cornes <bradlc41@gmail.com>
4 years ago
3 changed files, 201 additions(+), 58 deletions(-)
M src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts -> src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts
diff --git a/src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts b/src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts
index 95293341d536810ead06332a6125e96c6a77a776..ccf52fcb1aa3b88bd1b73aa9963e0e7729b29cca 100644
--- a/src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts
+++ b/src/lsp/providers/diagnostics/getInvalidConfigPathDiagnostics.ts
@@ -9,153 +9,297 @@ import isObject from '../../../util/isObject'
 import { closest } from '../../util/closest'
 import { absoluteRange } from '../../util/absoluteRange'
 import { State, Settings } from '../../util/state'
-import { State, Settings } from '../../util/state'
-import { State, Settings } from '../../util/state'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+import { closest } from '../../util/closest'
 import { State, Settings } from '../../util/state'
-import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { State, Settings } from '../../util/state'
-import { isCssDoc } from '../../util/css'
 import { State, Settings } from '../../util/state'
-import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { State, Settings } from '../../util/state'
-import { findAll, indexToPosition } from '../../util/find'
+    let text = document.getText(range)
 import { State, Settings } from '../../util/state'
-import { stringToPath } from '../../util/stringToPath'
+import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { State, Settings } from '../../util/state'
-import isObject from '../../../util/isObject'
 import { State, Settings } from '../../util/state'
-import { closest } from '../../util/closest'
-import { State, Settings } from '../../util/state'
+import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-  let diagnostics: InvalidConfigPathDiagnostic[] = []
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { State, Settings } from '../../util/state'
+      text
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-  if (isCssDoc(state, document)) {
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { isCssDoc } from '../../util/css'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+export function getInvalidConfigPathDiagnostics(
 import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+export function getInvalidConfigPathDiagnostics(
 import { findAll, indexToPosition } from '../../util/find'
-    ranges.push(...boundaries.css)
+import { State, Settings } from '../../util/state'
   }
 
-  ranges.forEach((range) => {
+import { State, Settings } from '../../util/state'
 import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
+import { stringToPath } from '../../util/stringToPath'
-import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { State, Settings } from '../../util/state'
-      /(?<prefix>\s|^)(?<helper>config|theme)\((?<quote>['"])(?<key>[^)]+)\k<quote>\)/g,
-      text
-import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { isCssDoc } from '../../util/css'
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-    matches.forEach((match) => {
-      let base = match.groups.helper === 'theme' ? ['theme'] : []
-      let keys = stringToPath(match.groups.key)
       let value = dlv(state.config, [...base, ...keys])
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
       const isValid = (val: unknown): boolean =>
+import { State, Settings } from '../../util/state'
         typeof val === 'string' ||
+import { State, Settings } from '../../util/state'
         typeof val === 'number' ||
+import { State, Settings } from '../../util/state'
         val instanceof String ||
+import { State, Settings } from '../../util/state'
         val instanceof Number ||
+import { State, Settings } from '../../util/state'
         Array.isArray(val)
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
       const stitch = (keys: string[]): string =>
+
+import { State, Settings } from '../../util/state'
         keys.reduce((acc, cur, i) => {
+import { State, Settings } from '../../util/state'
           if (i === 0) return cur
+import { State, Settings } from '../../util/state'
           if (cur.includes('.')) return `${acc}[${cur}]`
+    )
+
+import { State, Settings } from '../../util/state'
           return `${acc}.${cur}`
+import { State, Settings } from '../../util/state'
         }, '')
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
       let message: string
+import { State, Settings } from '../../util/state'
       let suggestions: string[] = []
 import { State, Settings } from '../../util/state'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
       if (isValid(value)) {
+import { State, Settings } from '../../util/state'
         // The value resolves successfully, but we need to check that there
+import { State, Settings } from '../../util/state'
         // wasn't any funny business. If you have a theme object:
+import { State, Settings } from '../../util/state'
         // { msg: 'hello' } and do theme('msg.0')
+import { State, Settings } from '../../util/state'
         // this will resolve to 'h', which is probably not intentional, so we
+import { State, Settings } from '../../util/state'
         // check that all of the keys are object or array keys (i.e. not string
+      })
+import { State, Settings } from '../../util/state'
         // indexes)
+
+import { State, Settings } from '../../util/state'
         let valid = true
+import { State, Settings } from '../../util/state'
         for (let i = keys.length - 1; i >= 0; i--) {
+    )
+  }
+
+import { State, Settings } from '../../util/state'
           let key = keys[i]
+import { State, Settings } from '../../util/state'
           let parentValue = dlv(state.config, [...base, ...keys.slice(0, i)])
+import { State, Settings } from '../../util/state'
           if (/^[0-9]+$/.test(key)) {
+import { State, Settings } from '../../util/state'
             if (!isObject(parentValue) && !Array.isArray(parentValue)) {
+
+import { State, Settings } from '../../util/state'
               valid = false
+import { State, Settings } from '../../util/state'
               break
+import { State, Settings } from '../../util/state'
             }
+import { State, Settings } from '../../util/state'
           } else if (!isObject(parentValue)) {
+import { State, Settings } from '../../util/state'
             valid = false
+import { State, Settings } from '../../util/state'
             break
+import { State, Settings } from '../../util/state'
           }
+import { State, Settings } from '../../util/state'
         }
+import { State, Settings } from '../../util/state'
         if (!valid) {
+import { State, Settings } from '../../util/state'
           message = `'${match.groups.key}' does not exist in your theme config.`
+import { State, Settings } from '../../util/state'
 import { stringToPath } from '../../util/stringToPath'
-import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
+import { State, Settings } from '../../util/state'
+import { State, Settings } from '../../util/state'
       } else if (typeof value === 'undefined') {
+      }
+import { State, Settings } from '../../util/state'
         message = `'${match.groups.key}' does not exist in your theme config.`
+import { State, Settings } from '../../util/state'
         let parentValue = dlv(state.config, [
+import { State, Settings } from '../../util/state'
           ...base,
+import { State, Settings } from '../../util/state'
           ...keys.slice(0, keys.length - 1),
+import { State, Settings } from '../../util/state'
         ])
+import { State, Settings } from '../../util/state'
         if (isObject(parentValue)) {
+import { State, Settings } from '../../util/state'
           let closestValidKey = closest(
+        }
+      }
+import { State, Settings } from '../../util/state'
             keys[keys.length - 1],
+
+import { State, Settings } from '../../util/state'
             Object.keys(parentValue).filter((key) => isValid(parentValue[key]))
+import { State, Settings } from '../../util/state'
           )
+import { State, Settings } from '../../util/state'
           if (closestValidKey) {
+import { State, Settings } from '../../util/state'
             suggestions.push(
+    }
+  }
+
+import { State, Settings } from '../../util/state'
               stitch([...keys.slice(0, keys.length - 1), closestValidKey])
+import { State, Settings } from '../../util/state'
             )
+import { State, Settings } from '../../util/state'
             message += ` Did you mean '${suggestions[0]}'?`
+      typeof value === 'number' ||
+      value instanceof String ||
+      value instanceof Number ||
+      Array.isArray(value)
+    )
+  ) {
+  if (severity === 'ignore') return []
 import { stringToPath } from '../../util/stringToPath'
+      path
+    )}' was found but does not resolve to a string.`
+import { State, Settings } from '../../util/state'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+    if (isObject(value)) {
+      let validKeys = Object.keys(value).filter(
+        (key) => validateConfigPath(state, [...keys, key], base).isValid
+import { State, Settings } from '../../util/state'
 import { stringToPath } from '../../util/stringToPath'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+  let diagnostics: InvalidConfigPathDiagnostic[] = []
 import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
-import { closest } from '../../util/closest'
+        suggestions.push(
+          ...validKeys.map((validKey) => pathToString([...keys, validKey]))
+        )
+        reason += ` Did you mean something like '${suggestions[0]}'?`
+      }
+    }
+    return {
+      isValid: false,
+      reason,
+      suggestions,
+    }
+  }
+import { State, Settings } from '../../util/state'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+  // The value resolves successfully, but we need to check that there
+  // wasn't any funny business. If you have a theme object:
+  // { msg: 'hello' } and do theme('msg.0')
+  let diagnostics: InvalidConfigPathDiagnostic[] = []
 import { closest } from '../../util/closest'
+  // check that all of the keys are object or array keys (i.e. not string
+  // indexes)
+  let isValid = true
+  let ranges: Range[] = []
 import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { State, Settings } from '../../util/state'
+import { isCssDoc } from '../../util/css'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+  document: TextDocument,
+    if (/^[0-9]+$/.test(key)) {
+      if (!isObject(parentValue) && !Array.isArray(parentValue)) {
+        isValid = false
+  let ranges: Range[] = []
 import { closest } from '../../util/closest'
+      }
+    } else if (!isObject(parentValue)) {
+      isValid = false
+      break
+  let severity = settings.lint.invalidConfigPath
 import { isCssDoc } from '../../util/css'
-import { closest } from '../../util/closest'
+  }
+  if (!isValid) {
+    let reason = `'${pathToString(path)}' does not exist in your theme config.`
+
+    let altPath = findAlternativePath()
+  if (isCssDoc(state, document)) {
 import { getLanguageBoundaries } from '../../util/getLanguageBoundaries'
+      return {
+        isValid: false,
+        reason: `${reason} Did you mean '${pathToString(altPath)}'?`,
+  if (isCssDoc(state, document)) {
 import { closest } from '../../util/closest'
-import { findAll, indexToPosition } from '../../util/find'
+      }
+    }
+
+    return {
+import { State, Settings } from '../../util/state'
           )
-import { closest } from '../../util/closest'
+  let severity = settings.lint.invalidConfigPath
 import { stringToPath } from '../../util/stringToPath'
+      suggestions: [],
+import { State, Settings } from '../../util/state'
 import isObject from '../../../util/isObject'
+import { isCssDoc } from '../../util/css'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import isObject from '../../../util/isObject'
-import { closest } from '../../util/closest'
+
+  return {
+    isValid: true,
+    value,
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import isObject from '../../../util/isObject'
+}
+
+export function getInvalidConfigPathDiagnostics(
+  state: State,
+  document: TextDocument,
+  settings: Settings
+): InvalidConfigPathDiagnostic[] {
+  let severity = settings.lint.invalidConfigPath
+import { State, Settings } from '../../util/state'
 import { closest } from '../../util/closest'
+
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-            message += ` Did you mean something like '${suggestions[0]}'?`
+  let ranges: Range[] = []
-import { stringToPath } from '../../util/stringToPath'
+
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-import { stringToPath } from '../../util/stringToPath'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
 import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.css)
+  }
 import { State, Settings } from '../../util/state'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+  ranges.forEach((range) => {
+import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
+import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { State, Settings } from '../../util/state'
+import { InvalidConfigPathDiagnostic, DiagnosticKind } from './types'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+      text
+    )
 import { State, Settings } from '../../util/state'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+    matches.forEach((match) => {
+      let base = match.groups.helper === 'theme' ? ['theme'] : []
+      let result = validateConfigPath(state, match.groups.key, base)
 import { State, Settings } from '../../util/state'
+import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+      if (result.isValid === true) {
         return null
       }
 
@@ -176,11 +323,10 @@         severity:
           severity === 'error'
             ? DiagnosticSeverity.Error
             : DiagnosticSeverity.Warning,
-import { State, Settings } from '../../util/state'
 import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
-import { TextDocument, Range, DiagnosticSeverity } from 'vscode-languageserver'
+      let base = match.groups.helper === 'theme' ? ['theme'] : []
-import { State, Settings } from '../../util/state'
     ranges.push(undefined)
+import { stringToPath } from '../../util/stringToPath'
       })
     })
   })
I src/lsp/util/combinations.ts
diff --git a/src/lsp/util/combinations.ts b/src/lsp/util/combinations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c9868b207b01b60cc0789b800058285257e187a
--- /dev/null
+++ b/src/lsp/util/combinations.ts
@@ -0,0 +1,13 @@
+export function combinations(str: string): string[] {
+  let fn = function (active: string, rest: string, a: string[]) {
+    if (!active && !rest) return
+    if (!rest) {
+      a.push(active)
+    } else {
+      fn(active + rest[0], rest.slice(1), a)
+      fn(active, rest.slice(1), a)
+    }
+    return a
+  }
+  return fn('', str, [])
+}
M src/lsp/util/getClassNameAtPosition.ts -> src/lsp/util/getClassNameAtPosition.ts
diff --git a/src/lsp/util/getClassNameAtPosition.ts b/src/lsp/util/getClassNameAtPosition.ts
index 083832ca5e8b2707a1405fdbcd83f6c163e18f65..7418b2f157723967380453978a082e42fdb9ce49 100644
--- a/src/lsp/util/getClassNameAtPosition.ts
+++ b/src/lsp/util/getClassNameAtPosition.ts
@@ -1,4 +1,5 @@
 import { State } from './state'
+import { combinations } from './combinations'
 const dlv = require('dlv')
 
 export function getClassNameParts(state: State, className: string): string[] {
@@ -41,17 +42,3 @@     }
     return false
   })
 }
-
-function combinations(str: string): string[] {
-  let fn = function (active: string, rest: string, a: string[]) {
-    if (!active && !rest) return
-    if (!rest) {
-      a.push(active)
-    } else {
-      fn(active + rest[0], rest.slice(1), a)
-      fn(active, rest.slice(1), a)
-    }
-    return a
-  }
-  return fn('', str, [])
-}