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 @@ -10,7 +10,7 @@ async ({ document }) => { let doc = state.editor.documents.get(document) if (!doc) return { colors: [] } - return { colors: await getDocumentColors(state, doc) } + return { colors: getDocumentColors(state, doc) } } ) } 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,16 +10,16 @@ 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 -): Promise { +): CssConflictDiagnostic[] { 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,21 +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 [] - const classNames = await findClassNamesInRange( - state, - document, - undefined, - 'css', - false - ) + const classNames = findClassNamesInRange(document, undefined, 'css') 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,11 +10,11 @@ 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 - let classLists = await findClassListsInDocument(state, document) + let classLists = findClassListsInDocument(state, document) 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,28 +77,20 @@ } return names } -export async function findClassNamesInRange( - state: State, +export function findClassNamesInRange( doc: TextDocument, range?: Range, - mode?: 'html' | 'css', - includeCustom: boolean = true -): Promise { - const classLists = await findClassListsInRange( - state, - doc, - range, - mode, - includeCustom - ) + mode?: 'html' | 'css' +): DocumentClassName[] { + const classLists = findClassListsInRange(doc, range, mode) return flatten(classLists.map(getClassNamesInClassList)) } -export async function findClassNamesInDocument( +export function findClassNamesInDocument( state: State, doc: TextDocument -): Promise { - const classLists = await findClassListsInDocument(state, doc) +): DocumentClassName[] { + const classLists = findClassListsInDocument(state, doc) return flatten(classLists.map(getClassNamesInClassList)) } @@ -141,77 +130,12 @@ } }) } -async function findCustomClassLists( - state: State, - doc: TextDocument, - range?: Range -): Promise { - const settings = await getDocumentSettings(state, doc) - const regexes = dlv(settings, 'experimental.classRegex', []) - - if (!Array.isArray(regexes) || regexes.length === 0) return [] - - const text = doc.getText(range) - const result: DocumentClassList[] = [] - - 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 - - while ((containerMatch = containerRegex.exec(text)) !== null) { - 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) - let classMatch - - 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 matches = findAll(/(?:\s|:)(?:class(?:Name)?|\[ngClass\])=['"`{]/g, text) const result: DocumentClassList[] = [] matches.forEach((match) => { @@ -308,29 +232,21 @@ return result } -export async function findClassListsInRange( - state: State, +export function findClassListsInRange( doc: TextDocument, range?: Range, - mode?: 'html' | 'css', - includeCustom: boolean = true -): Promise { - let classLists: DocumentClassList[] + mode?: 'html' | 'css' +): DocumentClassList[] { if (mode === 'css') { - classLists = findClassListsInCssRange(doc, range) - } else { - classLists = findClassListsInHtmlRange(doc, range) + return findClassListsInCssRange(doc, range) } - return [ - ...classLists, - ...(includeCustom ? await findCustomClassLists(state, doc, range) : []), - ] + return findClassListsInHtmlRange(doc, range) } -export async function findClassListsInDocument( +export function findClassListsInDocument( state: State, doc: TextDocument -): Promise { +): DocumentClassList[] { if (isCssDoc(state, doc)) { return findClassListsInCssRange(doc) } @@ -341,7 +257,6 @@ return flatten([ ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)), ...boundaries.css.map((range) => findClassListsInCssRange(doc, range)), - await findCustomClassLists(state, doc), ]) } @@ -408,11 +323,11 @@ const { line, col } = lineColumn(str + '\n', index) return { line: line - 1, character: col - 1 } } -export async function findClassNameAtPosition( +export function findClassNameAtPosition( state: State, doc: TextDocument, position: Position -): Promise { +): DocumentClassName { let classNames = [] const searchRange = { start: { line: Math.max(position.line - 10, 0), character: 0 }, @@ -420,12 +335,12 @@ end: { line: position.line + 10, character: 0 }, } if (isCssContext(state, doc, position)) { - classNames = await findClassNamesInRange(state, doc, searchRange, 'css') + classNames = findClassNamesInRange(doc, searchRange, 'css') } else if ( isHtmlContext(state, doc, position) || isJsContext(state, doc, position) ) { - classNames = await findClassNamesInRange(state, doc, searchRange, 'html') + classNames = findClassNamesInRange(doc, searchRange, 'html') } if (classNames.length === 0) {