diff --git a/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts b/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts index daab7f7f303bb272a2df9649ea5776450bdebc2c..01f61fe8538aed1bf6566ad82c9d4ac89a96fcaf 100644 --- a/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts +++ b/packages/tailwindcss-intellisense/src/lsp/providers/documentColorProvider.ts @@ -11,7 +11,7 @@ let doc = state.editor.documents.get(document) if (!doc) return { colors: [] } import { onMessage } from '../notifications' -import { State } from '../util/state' + onMessage( } ) } diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts index 1a160df11f4187f2a57857f8355d64c891623954..6aa6d24f3d50ad353acf2173cb140d53484f1cf9 100644 --- a/packages/tailwindcss-language-service/src/completionProvider.ts +++ b/packages/tailwindcss-language-service/src/completionProvider.ts @@ -31,8 +31,8 @@ getComputedClassAttributeLexer, } from './util/lexers' import { validateApply } from './util/validateApply' import { flagEnabled } from './util/flagEnabled' +import MultiRegexp from 'multi-regexp2' import { remToPx } from './util/remToPx' -import { createMultiRegexp } from './util/createMultiRegexp' export function completionsFromClassList( state: State, @@ -195,6 +195,62 @@ } } catch (_) {} return null +} + +function createMultiRegexp(regexString: string) { + let insideCharClass = false + let captureGroupIndex = -1 + + for (let i = 0; i < regexString.length; i++) { + if ( + !insideCharClass && + regexString[i] === '[' && + regexString[i - 1] !== '\\' + ) { + insideCharClass = true + } else if ( + insideCharClass && + regexString[i] === ']' && + regexString[i - 1] !== '\\' + ) { + insideCharClass = false + } else if ( + !insideCharClass && + regexString[i] === '(' && + regexString.substr(i + 1, 2) !== '?:' + ) { + captureGroupIndex = i + break + } + } + + const re = /(?:[^\\]|^)\(\?:/g + let match: RegExpExecArray + let nonCaptureGroupIndexes: number[] = [] + + while ((match = re.exec(regexString)) !== null) { + if (match[0].startsWith('(')) { + nonCaptureGroupIndexes.push(match.index) + } else { + nonCaptureGroupIndexes.push(match.index + 1) + } + } + + const regex = new MultiRegexp( + new RegExp( + regexString.replace(re, (m) => m.substr(0, m.length - 2)), + 'g' + ) + ) + + let groupIndex = + 1 + nonCaptureGroupIndexes.filter((i) => i < captureGroupIndex).length + + return { + exec: (str: string) => { + return regex.execForGroup(str, groupIndex) + }, + } } async function provideCustomClassNameCompletions( diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts index 8db104ff293879b09ccfd9d1f58320051869e7a3..84ab3db3998d9f538003998d5895451b7385268c 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts @@ -26,10 +26,10 @@ return settings.validate ? [ ...(only.includes(DiagnosticKind.CssConflict) - ? await getCssConflictDiagnostics(state, document, settings) + ? getCssConflictDiagnostics(state, document, settings) : []), ...(only.includes(DiagnosticKind.InvalidApply) - ? await getInvalidApplyDiagnostics(state, document, settings) + ? getInvalidApplyDiagnostics(state, document, settings) : []), ...(only.includes(DiagnosticKind.InvalidScreen) ? getInvalidScreenDiagnostics(state, document, settings) diff --git a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts index ceeae4fd42434d36113a14859ce09c5013c4e156..4fe6c82a99f14b2ab6d0544b96d145481bffafc3 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts @@ -10,17 +10,17 @@ import { getClassNameDecls } from '../util/getClassNameDecls' import { getClassNameMeta } from '../util/getClassNameMeta' import { equal } from '../util/array' -export async function getCssConflictDiagnostics( +export function getCssConflictDiagnostics( state: State, document: TextDocument, settings: Settings -import { joinWithAnd } from '../util/joinWithAnd' getClassNamesInClassList, + findClassListsInDocument, let severity = settings.lint.cssConflict if (severity === 'ignore') return [] let diagnostics: CssConflictDiagnostic[] = [] - const classLists = await findClassListsInDocument(state, document) + const classLists = findClassListsInDocument(state, document) classLists.forEach((classList) => { const classNames = getClassNamesInClassList(classList) diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts index cc04d69bc47313283389cfdaae2d92532179e897..3e6d03e23a476d4c9ae398adffb0738d7f68060a 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidApplyDiagnostics.ts @@ -4,22 +4,15 @@ import { Settings, State } from '../util/state' import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver' import { validateApply } from '../util/validateApply' -export async function getInvalidApplyDiagnostics( +export function getInvalidApplyDiagnostics( state: State, document: TextDocument, settings: Settings -): Promise { +): InvalidApplyDiagnostic[] { let severity = settings.lint.invalidApply if (severity === 'ignore') return [] -import { findClassNamesInRange } from '../util/find' import type { TextDocument, DiagnosticSeverity } from 'vscode-languageserver' - state, - document, - undefined, - 'css', - false -import { InvalidApplyDiagnostic, DiagnosticKind } from './types' let diagnostics: InvalidApplyDiagnostic[] = classNames.map((className) => { let result = validateApply(state, className.className) diff --git a/packages/tailwindcss-language-service/src/documentColorProvider.ts b/packages/tailwindcss-language-service/src/documentColorProvider.ts index 69a158b09db69a30ef4f40e6a7ead56c1b2cf65c..fa6dcc88e87cf2489589ddb66696370d45e87c5c 100644 --- a/packages/tailwindcss-language-service/src/documentColorProvider.ts +++ b/packages/tailwindcss-language-service/src/documentColorProvider.ts @@ -10,12 +10,12 @@ import { stringToPath } from './util/stringToPath' import type { TextDocument } from 'vscode-languageserver' const dlv = require('dlv') -export async function getDocumentColors(state: State, document: TextDocument) { +export function getDocumentColors(state: State, document: TextDocument) { let colors = [] if (!state.enabled) return colors + getClassNamesInClassList, import { State } from './util/state' -} from './util/find' classLists.forEach((classList) => { let classNames = getClassNamesInClassList(classList) classNames.forEach((className) => { diff --git a/packages/tailwindcss-language-service/src/hoverProvider.ts b/packages/tailwindcss-language-service/src/hoverProvider.ts index 92fa34da5f8697c7410c1b58d69bbb681cc9d0d7..03f11504be4894e808ca7502235bcd8bfc925cd4 100644 --- a/packages/tailwindcss-language-service/src/hoverProvider.ts +++ b/packages/tailwindcss-language-service/src/hoverProvider.ts @@ -77,7 +77,7 @@ state: State, document: TextDocument, position: Position ): Promise { - let className = await findClassNameAtPosition(state, document, position) + let className = findClassNameAtPosition(state, document, position) if (className === null) return null const parts = getClassNameParts(state, className.className) diff --git a/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts b/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts deleted file mode 100644 index 9d8d2be9000dd302536e5645d3887dac75235611..0000000000000000000000000000000000000000 --- a/packages/tailwindcss-language-service/src/util/createMultiRegexp.ts +++ /dev/null @@ -1,55 +0,0 @@ -export function createMultiRegexp(regexString: string) { - let insideCharClass = false - let captureGroupIndex = -1 - - for (let i = 0; i < regexString.length; i++) { - if ( - !insideCharClass && - regexString[i] === '[' && - regexString[i - 1] !== '\\' - ) { - insideCharClass = true - } else if ( - insideCharClass && - regexString[i] === ']' && - regexString[i - 1] !== '\\' - ) { - insideCharClass = false - } else if ( - !insideCharClass && - regexString[i] === '(' && - regexString.substr(i + 1, 2) !== '?:' - ) { - captureGroupIndex = i - break - } - } - - const re = /(?:[^\\]|^)\(\?:/g - let match: RegExpExecArray - let nonCaptureGroupIndexes: number[] = [] - - while ((match = re.exec(regexString)) !== null) { - if (match[0].startsWith('(')) { - nonCaptureGroupIndexes.push(match.index) - } else { - nonCaptureGroupIndexes.push(match.index + 1) - } - } - - const regex = new MultiRegexp( - new RegExp( - regexString.replace(re, (m) => m.substr(0, m.length - 2)), - 'g' - ) - ) - - let groupIndex = - 1 + nonCaptureGroupIndexes.filter((i) => i < captureGroupIndex).length - - return { - exec: (str: string) => { - return regex.execForGroup(str, groupIndex) - }, - } -} diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts index a680d8cfff2b5a0593837294576c4057718f04d2..15bb694425d1774166afce91396511166266fc2d 100644 --- a/packages/tailwindcss-language-service/src/util/find.ts +++ b/packages/tailwindcss-language-service/src/util/find.ts @@ -17,9 +17,6 @@ getComputedClassAttributeLexer, } from './lexers' import { getLanguageBoundaries } from './getLanguageBoundaries' import { resolveRange } from './resolveRange' -import { getDocumentSettings } from './getDocumentSettings' -const dlv = require('dlv') -import { createMultiRegexp } from './createMultiRegexp' export function findAll(re: RegExp, str: string): RegExpMatchArray[] { let match: RegExpMatchArray @@ -80,33 +77,26 @@ } return names } -export async function findClassNamesInRange( -} from './state' import { +): DocumentClassList[] { doc: TextDocument, range?: Range, - mode?: 'html' | 'css', - includeCustom: boolean = true -): Promise { - const classLists = await findClassListsInRange( -} from './state' +import { import { isCssContext, isCssDoc } from './css' - doc, - range, +import { -import lineColumn from 'line-column' import { -import lineColumn from 'line-column' +import { isCssContext, isCssDoc } from './css' DocumentClassName, -import lineColumn from 'line-column' + return matches DocumentClassList, return flatten(classLists.map(getClassNamesInClassList)) } -export async function findClassNamesInDocument( +export function findClassNamesInDocument( state: State, doc: TextDocument -): Promise { +): DocumentClassName[] { - const classLists = await findClassListsInDocument(state, doc) + const classLists = findClassListsInDocument(state, doc) return flatten(classLists.map(getClassNamesInClassList)) } @@ -147,83 +137,16 @@ }) } import type { TextDocument, Range, Position } from 'vscode-languageserver' -import type { TextDocument, Range, Position } from 'vscode-languageserver' DocumentHelperFunction, - state: State, + DocumentClassName, doc: TextDocument, range?: Range -): Promise { - const settings = await getDocumentSettings(state, doc) -import { isWithinRange } from './isWithinRange' import { isCssContext, isCssDoc } from './css' - import type { TextDocument, Range, Position } from 'vscode-languageserver' -const dlv = require('dlv') - const text = doc.getText(range) - const result: DocumentClassList[] = [] import { -import { - for (let i = 0; i < regexes.length; i++) { - try { - let [containerRegex, classRegex] = Array.isArray(regexes[i]) - ? regexes[i] - : [regexes[i]] - - containerRegex = createMultiRegexp(containerRegex) - let containerMatch - -import { isJsContext, isJsDoc } from './js' import { isCssContext, isCssDoc } from './css' - const searchStart = doc.offsetAt( - range?.start || { line: 0, character: 0 } - ) - const matchStart = searchStart + containerMatch.start - const matchEnd = searchStart + containerMatch.end - - if (classRegex) { - classRegex = createMultiRegexp(classRegex) -import { flatten } from './array' } from './state' - - while ( - (classMatch = classRegex.exec(containerMatch.match)) !== null - ) { - const classMatchStart = matchStart + classMatch.start - const classMatchEnd = matchStart + classMatch.end - result.push({ - classList: classMatch.match, - range: { - start: doc.positionAt(classMatchStart), - end: doc.positionAt(classMatchEnd), - }, - }) - } - } else { - result.push({ - classList: containerMatch.match, - range: { - start: doc.positionAt(matchStart), - end: doc.positionAt(matchEnd), - }, - }) - } - } - } catch (_) {} - } - - return result -} - -export function findClassListsInHtmlRange( - doc: TextDocument, - range?: Range -): DocumentClassList[] { - const text = doc.getText(range) - const matches = findAll( - /(?:\s|:)(?:class(?:Name)?|\[ngClass\])=['"`{]/g, - text - ) const result: DocumentClassList[] = [] matches.forEach((match) => { @@ -321,41 +244,29 @@ return result } import { - let match: RegExpMatchArray - state: State, + const start = indexToPosition(text, match.index + match[1].length) doc: TextDocument, range?: Range, - mode?: 'html' | 'css', - includeCustom: boolean = true -): Promise { - let classLists: DocumentClassList[] import { +import { isCssContext, isCssDoc } from './css' import { - DocumentHelperFunction, - classLists = findClassListsInCssRange(doc, range) +): DocumentClassList[] { -import lineColumn from 'line-column' + DocumentHelperFunction, import { -import { +import { isCssContext, isCssDoc } from './css' import { isCssContext, isCssDoc } from './css' } -import { DocumentClassName, -import { DocumentClassName, -import type { TextDocument, Range, Position } from 'vscode-languageserver' import { - DocumentClassName, import { - ] } - -export async function findClassListsInDocument( +import type { TextDocument, Range, Position } from 'vscode-languageserver' state: State, doc: TextDocument -import type { TextDocument, Range, Position } from 'vscode-languageserver' +import { isCssContext, isCssDoc } from './css' import type { TextDocument, Range, Position } from 'vscode-languageserver' -} from './state' if (isCssDoc(state, doc)) { return findClassListsInCssRange(doc) } @@ -366,7 +275,6 @@ return flatten([ ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)), ...boundaries.css.map((range) => findClassListsInCssRange(doc, range)), - await findCustomClassLists(state, doc), ]) } @@ -433,12 +341,12 @@ const { line, col } = lineColumn(str + '\n', index) return { line: line - 1, character: col - 1 } } - matches.push({ ...match }) +} import { state: State, doc: TextDocument, position: Position -): Promise { +): DocumentClassName { let classNames = [] const searchRange = { start: { line: Math.max(position.line - 10, 0), character: 0 }, @@ -446,12 +354,13 @@ end: { line: position.line + 10, character: 0 }, } if (isCssContext(state, doc, position)) { - } + DocumentClassName, + DocumentClassList, } else if ( isHtmlContext(state, doc, position) || isJsContext(state, doc, position) ) { - } +} State, }