Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
add initial class name conflict diagnostics
Brad Cornes <bradlc41@gmail.com>
4 years ago
2 changed files, 116 additions(+), 64 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 e76bf49249f86a753313ebc8aecd65c29403d89a..9f483ac9f69be7fbc5deeae70607194fafacf66b 100644
--- a/src/lsp/providers/diagnosticsProvider.ts
+++ b/src/lsp/providers/diagnosticsProvider.ts
@@ -5,10 +5,16 @@   DiagnosticSeverity,
 } from 'vscode-languageserver'
 import { State } from '../util/state'
 import { isCssDoc } from '../util/css'
-import { findClassNamesInRange } from '../util/find'
+import {
+  findClassNamesInRange,
+  findClassListsInDocument,
+  getClassNamesInClassList,
+} from '../util/find'
 import { getClassNameMeta } from '../util/getClassNameMeta'
+import { getClassNameDecls } from '../util/getClassNameDecls'
+import { equal } from '../../util/array'
 
-function provideCssDiagnostics(state: State, document: TextDocument): void {
+function getCssDiagnostics(state: State, document: TextDocument): Diagnostic[] {
   const classNames = findClassNamesInRange(document, undefined, 'css')
 
   let diagnostics: Diagnostic[] = classNames
@@ -46,38 +52,73 @@       return {
         severity: DiagnosticSeverity.Error,
         range,
         message,
-        // source: 'ex',
       }
     })
     .filter(Boolean)
 
-  // if (state.editor.capabilities.diagnosticRelatedInformation) {
-  //   diagnostic.relatedInformation = [
-  //     {
-  //       location: {
-  //         uri: document.uri,
-  //         range: Object.assign({}, diagnostic.range),
-  //       },
-  //       message: '',
-  //     },
-  //     {
-  //       location: {
-  //         uri: document.uri,
-  //         range: Object.assign({}, diagnostic.range),
-  //       },
-  //       message: '',
-  //     },
-  //   ]
-  // }
+  return diagnostics
+}
+
+function getConflictDiagnostics(
+  state: State,
+  document: TextDocument
+): Diagnostic[] {
+  let diagnostics: Diagnostic[] = []
+  const classLists = findClassListsInDocument(state, document)
+
+  classLists.forEach((classList) => {
+    const classNames = getClassNamesInClassList(classList)
+
+    classNames.forEach((className, index) => {
+      let otherClassNames = classNames.filter((_className, i) => i !== index)
+      otherClassNames.forEach((otherClassName) => {
+        let decls = getClassNameDecls(state, className.className)
+        if (!decls) return
+
+        let otherDecls = getClassNameDecls(state, otherClassName.className)
+        if (!otherDecls) return
+
+        let meta = getClassNameMeta(state, className.className)
+        let otherMeta = getClassNameMeta(state, otherClassName.className)
+
+        if (
+          equal(Object.keys(decls), Object.keys(otherDecls)) &&
+          !Array.isArray(meta) &&
+          !Array.isArray(otherMeta) &&
+          equal(meta.context, otherMeta.context) &&
+          equal(meta.pseudo, otherMeta.pseudo)
+        ) {
+          diagnostics.push({
+            range: className.range,
+            severity: DiagnosticSeverity.Warning,
+            message: `You can’t use \`${className.className}\` and \`${otherClassName.className}\` together`,
+            relatedInformation: [
+              {
+                message: otherClassName.className,
+                location: {
+                  uri: document.uri,
+                  range: otherClassName.range,
+                },
+              },
+            ],
+          })
+        }
+      })
+    })
+  })
 
-  state.editor.connection.sendDiagnostics({ uri: document.uri, diagnostics })
+  return diagnostics
 }
 
 export async function provideDiagnostics(
   state: State,
   document: TextDocument
 ): Promise<void> {
-  if (isCssDoc(state, document)) {
-    return provideCssDiagnostics(state, document)
-  }
+  state.editor.connection.sendDiagnostics({
+    uri: document.uri,
+    diagnostics: [
+      ...getConflictDiagnostics(state, document),
+      ...(isCssDoc(state, document) ? getCssDiagnostics(state, document) : []),
+    ],
+  })
 }
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 6b1bfca7fbce3737b86c7529b4e7141715d17f58..9a04ac4de58771e60379719acd0b50a5e1ebcdf2 100644
--- a/src/lsp/util/find.ts
+++ b/src/lsp/util/find.ts
@@ -6,6 +6,7 @@ import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
 import { isWithinRange } from './isWithinRange'
 import { isJsContext, isJsDoc } from './js'
 import { getClassAttributeLexer } from './lexers'
+import { flatten } from '../../util/array'
 
 export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
   let match: RegExpMatchArray
@@ -24,44 +25,53 @@   }
   return matches[matches.length - 1]
 }
 
+export function getClassNamesInClassList({
+  classList,
+  range,
+}: DocumentClassList): DocumentClassName[] {
+  const parts = classList.split(/(\s+)/)
+  const names: DocumentClassName[] = []
+  let index = 0
+  for (let i = 0; i < parts.length; i++) {
+    if (i % 2 === 0) {
+      const start = indexToPosition(classList, index)
+      const end = indexToPosition(classList, index + parts[i].length)
+      names.push({
+        className: parts[i],
+        range: {
+          start: {
+            line: range.start.line + start.line,
+            character:
+              (end.line === 0 ? range.start.character : 0) + start.character,
+          },
+          end: {
+            line: range.start.line + end.line,
+            character:
+              (end.line === 0 ? range.start.character : 0) + end.character,
+          },
+        },
+      })
+    }
+    index += parts[i].length
+  }
+  return names
+}
+
 export function findClassNamesInRange(
   doc: TextDocument,
   range?: Range,
   mode?: 'html' | 'css'
 ): DocumentClassName[] {
   const classLists = findClassListsInRange(doc, range, mode)
-  return [].concat.apply(
-    [],
-    classLists.map(({ classList, range }) => {
-      const parts = classList.split(/(\s+)/)
-      const names: DocumentClassName[] = []
-      let index = 0
-      for (let i = 0; i < parts.length; i++) {
-        if (i % 2 === 0) {
-          const start = indexToPosition(classList, index)
-          const end = indexToPosition(classList, index + parts[i].length)
-          names.push({
-            className: parts[i],
-            range: {
-              start: {
-                line: range.start.line + start.line,
-                character:
-                  (end.line === 0 ? range.start.character : 0) +
-                  start.character,
-              },
-              end: {
-                line: range.start.line + end.line,
-                character:
-                  (end.line === 0 ? range.start.character : 0) + end.character,
-              },
-            },
-          })
-        }
-        index += parts[i].length
-      }
-      return names
-    })
-  )
+  return flatten(classLists.map(getClassNamesInClassList))
+}
+
+export function findClassNamesInDocument(
+  state: State,
+  doc: TextDocument
+): DocumentClassName[] {
+  const classLists = findClassListsInDocument(state, doc)
+  return flatten(classLists.map(getClassNamesInClassList))
 }
 
 export function findClassListsInCssRange(
@@ -98,7 +108,7 @@ }
 
 export function findClassListsInHtmlRange(
   doc: TextDocument,
-  range: Range
+  range?: Range
 ): DocumentClassList[] {
   const text = doc.getText(range)
   const matches = findAll(/[\s:]class(?:Name)?=['"`{]/g, text)
@@ -174,15 +184,16 @@           return {
             classList: value,
             range: {
               start: {
-                line: range.start.line + start.line,
+                line: (range?.start.line || 0) + start.line,
                 character:
-                  (end.line === 0 ? range.start.character : 0) +
+                  (end.line === 0 ? range?.start.character || 0 : 0) +
                   start.character,
               },
               end: {
-                line: range.start.line + end.line,
+                line: (range?.start.line || 0) + end.line,
                 character:
-                  (end.line === 0 ? range.start.character : 0) + end.character,
+                  (end.line === 0 ? range?.start.character || 0 : 0) +
+                  end.character,
               },
             },
           }
@@ -196,8 +207,8 @@ }
 
 export function findClassListsInRange(
   doc: TextDocument,
-  range: Range,
-  mode: 'html' | 'css'
+  range?: Range,
+  mode?: 'html' | 'css'
 ): DocumentClassList[] {
   if (mode === 'css') {
     return findClassListsInCssRange(doc, range)