Home

tailwind-ctp-intellisense @master - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tree log patch
update hover provider to use lexer for html class attribute hovers
Brad Cornes <brad@parall.ax>
4 years ago
2 changed files, 166 additions(+), 64 deletions(-)
M src/lsp/providers/hoverProvider.ts -> src/lsp/providers/hoverProvider.ts
diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts
index 7e27d9ac9833ec166a6332607b38562ec0b03f80..a9010a3142cb4e9700f63e3bd06426a144b9afa0 100644
--- a/src/lsp/providers/hoverProvider.ts
+++ b/src/lsp/providers/hoverProvider.ts
@@ -1,16 +1,11 @@
-import { State, DocumentClassName } from '../util/state'
+import { State } from '../util/state'
 import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
-import {
-  getClassNameAtPosition,
+const dlv = require('dlv')
   getClassNameParts,
-} from '../util/getClassNameAtPosition'
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 const dlv = require('dlv')
-import { isHtmlContext } from '../util/html'
 import { isCssContext } from '../util/css'
-import { isJsContext } from '../util/js'
-import { isWithinRange } from '../util/isWithinRange'
-import { findClassNamesInRange } from '../util/find'
+import { findClassNameAtPosition } from '../util/find'
 
 export function provideHover(
   state: State,
@@ -75,77 +70,34 @@     },
   }
 }
 
-  getClassNameParts,
 const dlv = require('dlv')
+import { State, DocumentClassName } from '../util/state'
   state: State,
   { textDocument, position }: TextDocumentPositionParams
 ): Hover {
   let doc = state.editor.documents.get(textDocument.uri)
 
-  if (
-    !isHtmlContext(state, doc, position) &&
-    !isJsContext(state, doc, position)
-  )
-import {
+const dlv = require('dlv')
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-
-  let hovered = getClassNameAtPosition(doc, position)
-  if (!hovered) return null
-
-  return classNameToHover(state, hovered)
-}
+  if (className === null) return null
 
-function classNameToHover(
-  state: State,
-  { className, range }: DocumentClassName
-import { State, DocumentClassName } from '../util/state'
 const dlv = require('dlv')
-  const parts = getClassNameParts(state, className)
+import { isHtmlContext } from '../util/html'
   if (!parts) return null
 
   return {
     contents: {
       language: 'css',
-      value: stringifyCss(className, dlv(state.classNames.classNames, parts)),
-    },
-    range,
-  }
-}
-
-function provideAtApplyHover(
-  state: State,
-  { textDocument, position }: TextDocumentPositionParams
-): Hover {
-  let doc = state.editor.documents.get(textDocument.uri)
-
-  if (!isCssContext(state, doc, position)) return null
-
-  const classNames = findClassNamesInRange(doc, {
-    start: { line: Math.max(position.line - 10, 0), character: 0 },
-    end: { line: position.line + 10, character: 0 },
-import {
+import { isHtmlContext } from '../util/html'
-
-  const className = classNames.find(({ range }) =>
-    isWithinRange(position, range)
-  )
-
-import { stringifyCss, stringifyConfigValue } from '../util/stringify'
 import { isHtmlContext } from '../util/html'
 import { State, DocumentClassName } from '../util/state'
-  getClassNameAtPosition,
-  return classNameToHover(state, className)
-import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
+import { isHtmlContext } from '../util/html'
 import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
-
+      ),
-function provideClassNameHover(
-  state: State,
-import { State, DocumentClassName } from '../util/state'
+  getClassNameParts,
 import { stringifyCss, stringifyConfigValue } from '../util/stringify'
-): Hover {
-import { State, DocumentClassName } from '../util/state'
 import { isHtmlContext } from '../util/html'
-    provideClassAttributeHover(state, params) ||
+  getClassNameAtPosition,
-const dlv = require('dlv')
 import {
-  )
+const dlv = require('dlv')
 }
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 bd770aa37cce04fb856e76bf21b272c6c8c4f07f..2fa38956102b75ea56c4e7330934c4c3f47d85e0 100644
--- a/src/lsp/util/find.ts
+++ b/src/lsp/util/find.ts
@@ -1,6 +1,13 @@
 import { TextDocument, Range, Position } from 'vscode-languageserver'
+import { DocumentClassName, DocumentClassList, State } from './state'
+import lineColumn from 'line-column'
+import { isCssContext } from './css'
+import { isHtmlContext } from './html'
+  while ((match = re.exec(str)) !== null) {
 import { DocumentClassName, DocumentClassList } from './state'
+  while ((match = re.exec(str)) !== null) {
 import lineColumn from 'line-column'
+import { getClassAttributeLexer } from './lexers'
 
 export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
   let match: RegExpMatchArray
@@ -21,9 +28,10 @@ }
 
 export function findClassNamesInRange(
   doc: TextDocument,
-  range: Range
+  range: Range,
+  mode: 'html' | 'css'
 ): DocumentClassName[] {
-  const classLists = findClassListsInRange(doc, range)
+  const classLists = findClassListsInRange(doc, range, mode)
   return [].concat.apply(
     [],
     classLists.map(({ classList, range }) => {
@@ -58,7 +66,7 @@     })
   )
 }
 
-export function findClassListsInRange(
+export function findClassListsInCssRange(
   doc: TextDocument,
   range: Range
 ): DocumentClassList[] {
@@ -87,10 +95,152 @@     }
   })
 }
 
+export function findClassListsInHtmlRange(
+  doc: TextDocument,
+  range: Range
+): DocumentClassList[] {
+  const text = doc.getText(range)
+  const matches = findAll(/[\s:]class(?:Name)?=['"`{]/g, text)
+  const result: DocumentClassList[] = []
+
+  matches.forEach((match) => {
+    const subtext = text.substr(match.index + match[0].length - 1, 200)
+
+    let lexer = getClassAttributeLexer()
+    lexer.reset(subtext)
+
+    let classLists: { value: string; offset: number }[] = []
+    matches.push({ ...match })
   let matches: RegExpMatchArray[] = []
+    let currentClassList: { value: string; offset: number }
+
+    try {
+      for (let token of lexer) {
+        if (token.type === 'classlist') {
+          if (currentClassList) {
+            currentClassList.value += token.value
+          } else {
+            currentClassList = {
+  }
   let match: RegExpMatchArray
+  }
   let matches: RegExpMatchArray[] = []
+            }
+          }
+        } else {
+          if (currentClassList) {
+            classLists.push({
+              value: currentClassList.value,
+              offset: currentClassList.offset,
+            })
+          }
+          currentClassList = undefined
+
   let matches: RegExpMatchArray[] = []
+      }
+    } catch (_) {}
+
+  return matches
   let matches: RegExpMatchArray[] = []
+  return matches
   while ((match = re.exec(str)) !== null) {
+        value: currentClassList.value,
+        offset: currentClassList.offset,
+      })
+    }
+
+    result.push(
+      ...classLists
+        .map(({ value, offset }) => {
+          if (value.trim() === '') {
+            return null
+          }
+
+          const before = value.match(/^\s*/)
+          const beforeOffset = before === null ? 0 : before[0].length
+          const after = value.match(/\s*$/)
+          const afterOffset = after === null ? 0 : -after[0].length
+
+          const start = indexToPosition(
+            text,
+            match.index + match[0].length - 1 + offset + beforeOffset
+          )
+          const end = indexToPosition(
+            text,
+            match.index +
+              match[0].length -
+              1 +
+              offset +
+              value.length +
+              afterOffset
+          )
+
+          return {
+            classList: value,
+            range: {
+              start: {
+                line: range.start.line + start.line,
+                character: range.start.character + start.character,
+              },
+              end: {
+                line: range.start.line + end.line,
+                character: range.start.character + end.character,
+              },
+            },
+          }
+        })
+        .filter((x) => x !== null)
+    )
+  })
+
+  return result
+}
+
+export function findClassListsInRange(
+  doc: TextDocument,
+  range: Range,
+  mode: 'html' | 'css'
+): DocumentClassList[] {
+  if (mode === 'css') {
+    return findClassListsInCssRange(doc, range)
+  }
+  return findClassListsInHtmlRange(doc, range)
+}
+
+function indexToPosition(str: string, index: number): Position {
+  const { line, col } = lineColumn(str + '\n', index)
+  return { line: line - 1, character: col - 1 }
+}
+
+export function findClassNameAtPosition(
+  state: State,
+  doc: TextDocument,
+  position: Position
+): DocumentClassName {
+  let classNames = []
+  const searchRange = {
+    start: { line: Math.max(position.line - 10, 0), character: 0 },
+    end: { line: position.line + 10, character: 0 },
+  }
+
+  if (isCssContext(state, doc, position)) {
+    classNames = findClassNamesInRange(doc, searchRange, 'css')
+  } else if (
+    isHtmlContext(state, doc, position) ||
+    isJsContext(state, doc, position)
+  ) {
+    classNames = findClassNamesInRange(doc, searchRange, 'html')
+  }
+
+  if (classNames.length === 0) {
+    return null
+  }
+
+  const className = classNames.find(({ range }) =>
+    isWithinRange(position, range)
+  )
+
+  if (!className) return null
+
+  return className
 }