tailwind-ctp-intellisense @master -
refs -
log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
update "invalid helper key" error conditions and messages
4 changed files, 103 additions(+), 13 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 91c1aca81ac946c1fd36e4fd59904914710f8008..06b59a27dd6b110985a9c4f584b84b9935ae2a83 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -4591,6 +4591,12 @@ }
}
}
},
+ "js-levenshtein": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
+ "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
+ "dev": true
+ },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index a1634692f7d62a226b5372b12fbb0f85b322ed11..2386c68b559746bed1a67f6dca26378ff6b9423f 100755
--- a/package.json
+++ b/package.json
@@ -173,6 +173,7 @@ "globalyzer": "^0.1.4",
"globrex": "^0.1.2",
"import-from": "^3.0.0",
"jest": "^25.5.4",
+ "js-levenshtein": "^1.1.6",
"line-column": "^1.0.2",
"mitt": "^1.2.0",
"mkdirp": "^1.0.3",
diff --git a/src/lsp/providers/diagnosticsProvider.ts b/src/lsp/providers/diagnosticsProvider.ts
index 79f462ecee02d56ab76b780cdae993e5b66bca31..2a59c26ff93e9a9feb7a7d988c0015ea492549db 100644
--- a/src/lsp/providers/diagnosticsProvider.ts
+++ b/src/lsp/providers/diagnosticsProvider.ts
@@ -15,12 +15,15 @@ indexToPosition,
} from '../util/find'
import { getClassNameMeta } from '../util/getClassNameMeta'
import { getClassNameDecls } from '../util/getClassNameDecls'
-import { equal, flatten } from '../../util/array'
+import { equal } from '../../util/array'
import { getDocumentSettings } from '../util/getDocumentSettings'
const dlv = require('dlv')
import semver from 'semver'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { absoluteRange } from '../util/absoluteRange'
+import { isObject } from '../../class-names/isObject'
+import levenshtein from 'js-levenshtein'
+import { stringToPath } from '../util/stringToPath'
function getUnsupportedApplyDiagnostics(
state: State,
@@ -276,16 +279,84 @@ )
matches.forEach((match) => {
let base = match.groups.helper === 'theme' ? ['theme'] : []
- let keys = match.groups.key.split(/[.\[\]]/).filter(Boolean)
+ let keys = stringToPath(match.groups.key)
let value = dlv(state.config, [...base, ...keys])
- if (
- typeof value === 'string' ||
- typeof value === 'number' ||
- value instanceof String ||
- value instanceof Number ||
- Array.isArray(value)
- ) {
+ const isValid = (val: unknown): boolean =>
+ typeof val === 'string' ||
+ typeof val === 'number' ||
+ val instanceof String ||
+ val instanceof Number ||
+ Array.isArray(val)
+
+ const stitch = (keys: string[]): string =>
+ keys.reduce((acc, cur, i) => {
+ if (i === 0) return cur
+ if (cur.includes('.')) return `${acc}[${cur}]`
+ return `${acc}.${cur}`
+ }, '')
+
+ let message: string
+
+ if (isValid(value)) {
+ // 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')
+ // this will resolve to 'h', which is probably not intentional, so we
+ // check that all of the keys are object or array keys (i.e. not string
+ // indexes)
+ let valid = true
+ for (let i = keys.length - 1; i >= 0; i--) {
+ let key = keys[i]
+ let parentValue = dlv(state.config, [...base, ...keys.slice(0, i)])
+ if (/^[0-9]+$/.test(key)) {
+ if (!isObject(parentValue) && !Array.isArray(parentValue)) {
+ valid = false
+ break
+ }
+ } else if (!isObject(parentValue)) {
+ valid = false
+ break
+ }
+ }
+ if (!valid) {
+ message = `'${match.groups.key}' does not exist in your theme config.`
+ }
+ } else if (typeof value === 'undefined') {
+ message = `'${match.groups.key}' does not exist in your theme config.`
+ let parentValue = dlv(state.config, [
+ ...base,
+ ...keys.slice(0, keys.length - 1),
+ ])
+ if (isObject(parentValue)) {
+ let validKeys = Object.keys(parentValue)
+ .filter((key) => isValid(parentValue[key]))
+ .sort(
+ (a, b) =>
+ levenshtein(keys[keys.length - 1], a) -
+ levenshtein(keys[keys.length - 1], b)
+ )
+ if (validKeys.length) {
+ message += ` Did you mean '${stitch([
+ ...keys.slice(0, keys.length - 1),
+ validKeys[0],
+ ])}'?`
+ }
+ }
+ } else {
+ message = `'${match.groups.key}' was found but does not resolve to a string.`
+
+ if (isObject(value)) {
+ let firstValidKey = Object.keys(value).find((key) =>
+ isValid(value[key])
+ )
+ if (firstValidKey) {
+ message += ` Did you mean '${stitch([...keys, firstValidKey])}'?`
+ }
+ }
+ }
+
+ if (!message) {
return null
}
@@ -308,10 +379,7 @@ severity:
severity === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
- message:
- typeof value === 'undefined'
- ? `'${match.groups.key}' does not exist in your theme config.`
- : `'${match.groups.key}' was found but does not resolve to a string.`,
+ message,
})
})
})
diff --git a/src/lsp/util/stringToPath.ts b/src/lsp/util/stringToPath.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b06e1532e1903abb99361f1df64c01ad8bf408cf
--- /dev/null
+++ b/src/lsp/util/stringToPath.ts
@@ -0,0 +1,15 @@
+// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L6735-L6744
+let rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g
+let reEscapeChar = /\\(\\)?/g
+
+export function stringToPath(string: string): string[] {
+ let result: string[] = []
+ if (string.charCodeAt(0) === 46 /* . */) {
+ result.push('')
+ }
+ // @ts-ignore
+ string.replace(rePropName, (match, number, quote, subString) => {
+ result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match)
+ })
+ return result
+}