From 02683ece850414283986044abc6c540e6acfaea4 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Sun, 24 May 2020 18:32:25 +0100 Subject: [PATCH] add class list finder helper and fix ranges --- src/lsp/util/css.ts | 2 +- src/lsp/util/find.ts | 104 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/lsp/util/css.ts b/src/lsp/util/css.ts index 848d6bb..e6dbd09 100644 --- a/src/lsp/util/css.ts +++ b/src/lsp/util/css.ts @@ -12,7 +12,7 @@ export const CSS_LANGUAGES = [ 'sugarss', ] -function isCssDoc(state: State, doc: TextDocument): boolean { +export function isCssDoc(state: State, doc: TextDocument): boolean { const userCssLanguages = Object.keys( state.editor.userLanguages ).filter((lang) => CSS_LANGUAGES.includes(state.editor.userLanguages[lang])) diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts index 2fa3895..6b1bfca 100644 --- a/src/lsp/util/find.ts +++ b/src/lsp/util/find.ts @@ -1,10 +1,10 @@ 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' +import { isCssContext, isCssDoc } from './css' +import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html' import { isWithinRange } from './isWithinRange' -import { isJsContext } from './js' +import { isJsContext, isJsDoc } from './js' import { getClassAttributeLexer } from './lexers' export function findAll(re: RegExp, str: string): RegExpMatchArray[] { @@ -26,8 +26,8 @@ export function findLast(re: RegExp, str: string): RegExpMatchArray { export function findClassNamesInRange( doc: TextDocument, - range: Range, - mode: 'html' | 'css' + range?: Range, + mode?: 'html' | 'css' ): DocumentClassName[] { const classLists = findClassListsInRange(doc, range, mode) return [].concat.apply( @@ -66,10 +66,11 @@ export function findClassNamesInRange( export function findClassListsInCssRange( doc: TextDocument, - range: Range + range?: Range ): DocumentClassList[] { const text = doc.getText(range) const matches = findAll(/(@apply\s+)(?[^;}]+)[;}]/g, text) + const globalStart: Position = range ? range.start : { line: 0, character: 0 } return matches.map((match) => { const start = indexToPosition(text, match.index + match[1].length) @@ -81,12 +82,14 @@ export function findClassListsInCssRange( classList: match.groups.classList, range: { start: { - line: range.start.line + start.line, - character: range.start.character + start.character, + line: globalStart.line + start.line, + character: + (end.line === 0 ? globalStart.character : 0) + start.character, }, end: { - line: range.start.line + end.line, - character: range.start.character + end.character, + line: globalStart.line + end.line, + character: + (end.line === 0 ? globalStart.character : 0) + end.character, }, }, } @@ -172,11 +175,14 @@ export function findClassListsInHtmlRange( range: { start: { line: range.start.line + start.line, - character: range.start.character + start.character, + character: + (end.line === 0 ? range.start.character : 0) + + start.character, }, end: { line: range.start.line + end.line, - character: range.start.character + end.character, + character: + (end.line === 0 ? range.start.character : 0) + end.character, }, }, } @@ -199,6 +205,80 @@ export function findClassListsInRange( return findClassListsInHtmlRange(doc, range) } +export function findClassListsInDocument( + state: State, + doc: TextDocument +): DocumentClassList[] { + if (isCssDoc(state, doc)) { + return findClassListsInCssRange(doc) + } + + if (isVueDoc(doc)) { + let text = doc.getText() + let blocks = findAll( + /<(?template|style|script)\b[^>]*>.*?(<\/\k>|$)/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)), + ] + ) + } + + if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) { + let text = doc.getText() + let styleBlocks = findAll(/]*>|>).*?(<\/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 [] +} + function indexToPosition(str: string, index: number): Position { const { line, col } = lineColumn(str + '\n', index) return { line: line - 1, character: col - 1 }