Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
enable diagnostics in multi-language documents
Brad Cornes <bradlc41@gmail.com>
4 years ago
4 changed files, 252 additions(+), 157 deletions(-)
M src/lsp/providers/diagnosticsProvider.ts -> src/lsp/providers/diagnosticsProvider.ts
diff --git a/src/lsp/providers/diagnosticsProvider.ts b/src/lsp/providers/diagnosticsProvider.ts
index 866c07d56dabc00b1759e3d154f43154c872931c..bb59ff571ca903793d4eb34a45d4775e9e9beaca 100644
--- a/src/lsp/providers/diagnosticsProvider.ts
+++ b/src/lsp/providers/diagnosticsProvider.ts
@@ -2,6 +2,7 @@ import {
   TextDocument,
   Diagnostic,
   DiagnosticSeverity,
+  Range,
 } from 'vscode-languageserver'
 import { State, Settings } from '../util/state'
 import { isCssDoc } from '../util/css'
@@ -18,6 +19,8 @@ import { equal, flatten } 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'
 
 function getUnsupportedApplyDiagnostics(
   state: State,
@@ -140,35 +143,51 @@ ): Diagnostic[] {
   let severity = settings.lint.unknownScreen
   if (severity === 'ignore') return []
 
-  let text = document.getText()
-  let matches = findAll(/(?:\s|^)@screen\s+(?<screen>[^\s{]+)/g, text)
+  let diagnostics: Diagnostic[] = []
+  let ranges: Range[] = []
 
-  let screens = Object.keys(
-    dlv(state.config, 'theme.screens', dlv(state.config, 'screens', {}))
-  )
+  if (isCssDoc(state, document)) {
+    ranges.push(undefined)
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.css)
+  }
 
-  return matches
-    .map((match) => {
+  ranges.forEach((range) => {
+    let text = document.getText(range)
+    let matches = findAll(/(?:\s|^)@screen\s+(?<screen>[^\s{]+)/g, text)
+
+    let screens = Object.keys(
+      dlv(state.config, 'theme.screens', dlv(state.config, 'screens', {}))
+    )
+
+    matches.forEach((match) => {
       if (screens.includes(match.groups.screen)) {
         return null
       }
 
-      return {
-        range: {
-          start: indexToPosition(
-            text,
-            match.index + match[0].length - match.groups.screen.length
-          ),
-          end: indexToPosition(text, match.index + match[0].length),
-        },
+      diagnostics.push({
+        range: absoluteRange(
+          {
+            start: indexToPosition(
+              text,
+              match.index + match[0].length - match.groups.screen.length
+            ),
+            end: indexToPosition(text, match.index + match[0].length),
+          },
+          range
+        ),
         severity:
           severity === 'error'
             ? DiagnosticSeverity.Error
             : DiagnosticSeverity.Warning,
         message: 'Unknown screen',
-      }
+      })
     })
-    .filter(Boolean)
+  })
+
+  return diagnostics
 }
 
 function getUnknownVariantDiagnostics(
@@ -179,43 +198,54 @@ ): Diagnostic[] {
   let severity = settings.lint.unknownVariant
   if (severity === 'ignore') return []
 
-  let text = document.getText()
-  let matches = findAll(/(?:\s|^)@variants\s+(?<variants>[^{]+)/g, text)
+  let diagnostics: Diagnostic[] = []
+  let ranges: Range[] = []
+
+  if (isCssDoc(state, document)) {
+    ranges.push(undefined)
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.css)
+  }
 
-  return flatten(
-    matches
-      .map((match) => {
-        let diagnostics: Diagnostic[] = []
-        let variants = match.groups.variants.split(/(\s*,\s*)/)
-        let listStartIndex =
-          match.index + match[0].length - match.groups.variants.length
+  ranges.forEach((range) => {
+    let text = document.getText(range)
+    let matches = findAll(/(?:\s|^)@variants\s+(?<variants>[^{]+)/g, text)
+
+    matches.forEach((match) => {
+      let variants = match.groups.variants.split(/(\s*,\s*)/)
+      let listStartIndex =
+        match.index + match[0].length - match.groups.variants.length
 
-        for (let i = 0; i < variants.length; i += 2) {
-          let variant = variants[i].trim()
-          if (state.variants.includes(variant)) {
-            continue
-          }
+      for (let i = 0; i < variants.length; i += 2) {
+        let variant = variants[i].trim()
+        if (state.variants.includes(variant)) {
+          continue
+        }
 
-          let variantStartIndex =
-            listStartIndex + variants.slice(0, i).join('').length
+        let variantStartIndex =
+          listStartIndex + variants.slice(0, i).join('').length
 
-          diagnostics.push({
-            range: {
+        diagnostics.push({
+          range: absoluteRange(
+            {
               start: indexToPosition(text, variantStartIndex),
               end: indexToPosition(text, variantStartIndex + variant.length),
             },
-            severity:
-              severity === 'error'
-                ? DiagnosticSeverity.Error
-                : DiagnosticSeverity.Warning,
-            message: `Unknown variant: ${variant}`,
-          })
-        }
+            range
+          ),
+          severity:
+            severity === 'error'
+              ? DiagnosticSeverity.Error
+              : DiagnosticSeverity.Warning,
+          message: `Unknown variant: ${variant}`,
+        })
+      }
+    })
+  })
 
-        return diagnostics
-      })
-      .filter(Boolean)
-  )
+  return diagnostics
 }
 
 function getUnknownConfigKeyDiagnostics(
@@ -226,14 +256,25 @@ ): Diagnostic[] {
   let severity = settings.lint.unknownConfigKey
   if (severity === 'ignore') return []
 
-  let text = document.getText()
-  let matches = findAll(
-    /(?<prefix>\s|^)(?<helper>config|theme)\((?<quote>['"])(?<key>[^)]+)\k<quote>\)/g,
-    text
-  )
+  let diagnostics: Diagnostic[] = []
+  let ranges: Range[] = []
+
+  if (isCssDoc(state, document)) {
+    ranges.push(undefined)
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.css)
+  }
+
+  ranges.forEach((range) => {
+    let text = document.getText(range)
+    let matches = findAll(
+      /(?<prefix>\s|^)(?<helper>config|theme)\((?<quote>['"])(?<key>[^)]+)\k<quote>\)/g,
+      text
+    )
 
-  return matches
-    .map((match) => {
+    matches.forEach((match) => {
       let base = match.groups.helper === 'theme' ? ['theme'] : []
       let keys = match.groups.key.split(/[.\[\]]/).filter(Boolean)
       let value = dlv(state.config, [...base, ...keys])
@@ -251,19 +292,24 @@         match.groups.helper.length +
         1 + // open paren
         match.groups.quote.length
 
-      return {
-        range: {
-          start: indexToPosition(text, startIndex),
-          end: indexToPosition(text, startIndex + match.groups.key.length),
-        },
+      diagnostics.push({
+        range: absoluteRange(
+          {
+            start: indexToPosition(text, startIndex),
+            end: indexToPosition(text, startIndex + match.groups.key.length),
+          },
+          range
+        ),
         severity:
           severity === 'error'
             ? DiagnosticSeverity.Error
             : DiagnosticSeverity.Warning,
         message: `Unknown ${match.groups.helper} key: ${match.groups.key}`,
-      }
+      })
     })
-    .filter(Boolean)
+  })
+
+  return diagnostics
 }
 
 function getUnsupportedTailwindDirectiveDiagnostics(
@@ -274,30 +320,44 @@ ): Diagnostic[] {
   let severity = settings.lint.unsupportedTailwindDirective
   if (severity === 'ignore') return []
 
-  let text = document.getText()
-  let matches = findAll(/(?:\s|^)@tailwind\s+(?<value>[^;]+)/g, text)
+  let diagnostics: Diagnostic[] = []
+  let ranges: Range[] = []
+
+  if (isCssDoc(state, document)) {
+    ranges.push(undefined)
+  } else {
+    let boundaries = getLanguageBoundaries(state, document)
+    if (!boundaries) return []
+    ranges.push(...boundaries.css)
+  }
+
+  ranges.forEach((range) => {
+    let text = document.getText(range)
+    let matches = findAll(/(?:\s|^)@tailwind\s+(?<value>[^;]+)/g, text)
 
-  let allowed = [
-    'utilities',
-    'components',
-    'screens',
-    semver.gte(state.version, '1.0.0-beta.1') ? 'base' : 'preflight',
-  ]
+    let valid = [
+      'utilities',
+      'components',
+      'screens',
+      semver.gte(state.version, '1.0.0-beta.1') ? 'base' : 'preflight',
+    ]
 
-  return matches
-    .map((match) => {
-      if (allowed.includes(match.groups.value)) {
+    matches.forEach((match) => {
+      if (valid.includes(match.groups.value)) {
         return null
       }
 
-      return {
-        range: {
-          start: indexToPosition(
-            text,
-            match.index + match[0].length - match.groups.value.length
-          ),
-          end: indexToPosition(text, match.index + match[0].length),
-        },
+      diagnostics.push({
+        range: absoluteRange(
+          {
+            start: indexToPosition(
+              text,
+              match.index + match[0].length - match.groups.value.length
+            ),
+            end: indexToPosition(text, match.index + match[0].length),
+          },
+          range
+        ),
         severity:
           severity === 'error'
             ? DiagnosticSeverity.Error
@@ -305,9 +365,11 @@             : DiagnosticSeverity.Warning,
         message: `Unsupported value: ${match.groups.value}${
           match.groups.value === 'preflight' ? '. Use base instead.' : ''
         }`,
-      }
+      })
     })
-    .filter(Boolean)
+  })
+
+  return diagnostics
 }
 
 export async function provideDiagnostics(
@@ -319,19 +381,15 @@
   const diagnostics: Diagnostic[] = settings.validate
     ? [
         ...getUtilityConflictDiagnostics(state, document, settings),
-        ...(isCssDoc(state, document)
-          ? [
-              ...getUnsupportedApplyDiagnostics(state, document, settings),
-              ...getUnknownScreenDiagnostics(state, document, settings),
-              ...getUnknownVariantDiagnostics(state, document, settings),
-              ...getUnknownConfigKeyDiagnostics(state, document, settings),
-              ...getUnsupportedTailwindDirectiveDiagnostics(
-                state,
-                document,
-                settings
-              ),
-            ]
-          : []),
+        ...getUnsupportedApplyDiagnostics(state, document, settings),
+        ...getUnknownScreenDiagnostics(state, document, settings),
+        ...getUnknownVariantDiagnostics(state, document, settings),
+        ...getUnknownConfigKeyDiagnostics(state, document, settings),
+        ...getUnsupportedTailwindDirectiveDiagnostics(
+          state,
+          document,
+          settings
+        ),
       ]
     : []
 
I src/lsp/util/absoluteRange.ts
diff --git a/src/lsp/util/absoluteRange.ts b/src/lsp/util/absoluteRange.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9250e4fb9ec9b13ff3124072808f2013ef5009a0
--- /dev/null
+++ b/src/lsp/util/absoluteRange.ts
@@ -0,0 +1,18 @@
+import { Range } from 'vscode-languageserver'
+
+export function absoluteRange(range: Range, reference?: Range) {
+  return {
+    start: {
+      line: (reference?.start.line || 0) + range.start.line,
+      character:
+        (range.end.line === 0 ? reference?.start.character || 0 : 0) +
+        range.start.character,
+    },
+    end: {
+      line: (reference?.start.line || 0) + range.end.line,
+      character:
+        (range.end.line === 0 ? reference?.start.character || 0 : 0) +
+        range.end.character,
+    },
+  }
+}
M src/lsp/util/find.ts -> src/lsp/util/find.ts
diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts
index 0f996a43f914e393b546ae08a4410908687126b3..609de6207d066862bb645e4d9ca199c915010b61 100644
--- a/src/lsp/util/find.ts
+++ b/src/lsp/util/find.ts
@@ -10,6 +10,7 @@ import {
   getClassAttributeLexer,
   getComputedClassAttributeLexer,
 } from './lexers'
+import { getLanguageBoundaries } from './getLanguageBoundaries'
 
 export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
   let match: RegExpMatchArray
@@ -230,70 +231,13 @@   if (isCssDoc(state, doc)) {
     return findClassListsInCssRange(doc)
   }
 
-  if (isVueDoc(doc)) {
-    let text = doc.getText()
-    let blocks = findAll(
-      /<(?<type>template|style|script)\b[^>]*>.*?(<\/\k<type>>|$)/gis,
-      text
-    )
-    let htmlRanges: Range[] = []
-    let cssRanges: Range[] = []
-    for (let i = 0; i < blocks.length; i++) {
-      let range = {
-        start: indexToPosition(text, blocks[i].index),
-        end: indexToPosition(text, blocks[i].index + blocks[i][0].length),
-      }
-      if (blocks[i].groups.type === 'style') {
-        cssRanges.push(range)
-      } else {
-        htmlRanges.push(range)
-      }
-    }
-    return [].concat.apply(
-      [],
-      [
-        ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)),
-        ...cssRanges.map((range) => findClassListsInCssRange(doc, range)),
-      ]
-    )
-  }
+  let boundaries = getLanguageBoundaries(state, doc)
+  if (!boundaries) return []
 
-  if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) {
-    let text = doc.getText()
-    let styleBlocks = findAll(/<style(?:\s[^>]*>|>).*?(<\/style>|$)/gis, text)
-    let htmlRanges: Range[] = []
-    let cssRanges: Range[] = []
-    let currentIndex = 0
-
-    for (let i = 0; i < styleBlocks.length; i++) {
-      htmlRanges.push({
-        start: indexToPosition(text, currentIndex),
-        end: indexToPosition(text, styleBlocks[i].index),
-      })
-      cssRanges.push({
-        start: indexToPosition(text, styleBlocks[i].index),
-        end: indexToPosition(
-          text,
-          styleBlocks[i].index + styleBlocks[i][0].length
-        ),
-      })
-      currentIndex = styleBlocks[i].index + styleBlocks[i][0].length
-    }
-    htmlRanges.push({
-      start: indexToPosition(text, currentIndex),
-      end: indexToPosition(text, text.length),
-    })
-
-    return [].concat.apply(
-      [],
-      [
-        ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)),
-        ...cssRanges.map((range) => findClassListsInCssRange(doc, range)),
-      ]
-    )
-  }
-
-  return []
+  return flatten([
+    ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)),
+    ...boundaries.css.map((range) => findClassListsInCssRange(doc, range)),
+  ])
 }
 
 export function indexToPosition(str: string, index: number): Position {
I src/lsp/util/getLanguageBoundaries.ts
diff --git a/src/lsp/util/getLanguageBoundaries.ts b/src/lsp/util/getLanguageBoundaries.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dfef2300d3a9052cb3f60970538858b81d8af39b
--- /dev/null
+++ b/src/lsp/util/getLanguageBoundaries.ts
@@ -0,0 +1,75 @@
+import { TextDocument, Range } from 'vscode-languageserver'
+import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html'
+import { State } from './state'
+import { findAll, indexToPosition } from './find'
+import { isJsDoc } from './js'
+
+export interface LanguageBoundaries {
+  html: Range[]
+  css: Range[]
+}
+
+export function getLanguageBoundaries(
+  state: State,
+  doc: TextDocument
+): LanguageBoundaries | null {
+  if (isVueDoc(doc)) {
+    let text = doc.getText()
+    let blocks = findAll(
+      /<(?<type>template|style|script)\b[^>]*>.*?(<\/\k<type>>|$)/gis,
+      text
+    )
+    let htmlRanges: Range[] = []
+    let cssRanges: Range[] = []
+    for (let i = 0; i < blocks.length; i++) {
+      let range = {
+        start: indexToPosition(text, blocks[i].index),
+        end: indexToPosition(text, blocks[i].index + blocks[i][0].length),
+      }
+      if (blocks[i].groups.type === 'style') {
+        cssRanges.push(range)
+      } else {
+        htmlRanges.push(range)
+      }
+    }
+
+    return {
+      html: htmlRanges,
+      css: cssRanges,
+    }
+  }
+
+  if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) {
+    let text = doc.getText()
+    let styleBlocks = findAll(/<style(?:\s[^>]*>|>).*?(<\/style>|$)/gis, text)
+    let htmlRanges: Range[] = []
+    let cssRanges: Range[] = []
+    let currentIndex = 0
+
+    for (let i = 0; i < styleBlocks.length; i++) {
+      htmlRanges.push({
+        start: indexToPosition(text, currentIndex),
+        end: indexToPosition(text, styleBlocks[i].index),
+      })
+      cssRanges.push({
+        start: indexToPosition(text, styleBlocks[i].index),
+        end: indexToPosition(
+          text,
+          styleBlocks[i].index + styleBlocks[i][0].length
+        ),
+      })
+      currentIndex = styleBlocks[i].index + styleBlocks[i][0].length
+    }
+    htmlRanges.push({
+      start: indexToPosition(text, currentIndex),
+      end: indexToPosition(text, text.length),
+    })
+
+    return {
+      html: htmlRanges,
+      css: cssRanges,
+    }
+  }
+
+  return null
+}