diff --git a/package-lock.json b/package-lock.json index f02b088a505084ae135f4f2a76ddace0b32e16dc..9a9fb241028ff8d1aa3775a171eaeeda3bbbe413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,9 +6,6 @@ "packages": { "": { "name": "root", "dependencies": { - "@csstools/css-parser-algorithms": "2.1.1", - "@csstools/css-tokenizer": "2.1.1", - "@csstools/media-query-list-parser": "2.0.4", "@parcel/watcher": "2.0.3", "@tailwindcss/aspect-ratio": "0.4.2", "@tailwindcss/container-queries": "0.1.0", @@ -49,7 +46,6 @@ "pkg-up": "3.1.0", "postcss": "8.3.9", "postcss-load-config": "3.0.1", "postcss-selector-parser": "6.0.2", - "postcss-value-parser": "4.2.0", "prettier": "2.3.0", "resolve": "1.20.0", "rimraf": "3.0.2", @@ -1718,49 +1714,6 @@ "watch": "cli.js" }, "engines": { "node": ">=0.1.95" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.1.1.tgz", - "integrity": "sha512-viRnRh02AgO4mwIQb2xQNJju0i+Fh9roNgmbR5xEuG7J3TGgxjnE95HnBLgsFJOJOksvcfxOUCgODcft6Y07cA==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^2.1.1" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", - "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.4.tgz", - "integrity": "sha512-GyYot6jHgcSDZZ+tLSnrzkR7aJhF2ZW6d+CXH66mjy5WpAQhZD4HDke2OQ36SivGRWlZJpAz7TzbW6OKlEpxAA==", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.1.1", - "@csstools/css-tokenizer": "^2.1.1" } }, "node_modules/@evocateur/libnpmaccess": { @@ -22982,23 +22935,6 @@ "requires": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" } - }, - "@csstools/css-parser-algorithms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.1.1.tgz", - "integrity": "sha512-viRnRh02AgO4mwIQb2xQNJju0i+Fh9roNgmbR5xEuG7J3TGgxjnE95HnBLgsFJOJOksvcfxOUCgODcft6Y07cA==", - "requires": {} - }, - "@csstools/css-tokenizer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", - "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==" - }, - "@csstools/media-query-list-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.4.tgz", - "integrity": "sha512-GyYot6jHgcSDZZ+tLSnrzkR7aJhF2ZW6d+CXH66mjy5WpAQhZD4HDke2OQ36SivGRWlZJpAz7TzbW6OKlEpxAA==", - "requires": {} }, "@evocateur/libnpmaccess": { "version": "3.1.2", diff --git a/packages/tailwindcss-language-service/package.json b/packages/tailwindcss-language-service/package.json index 3471245170bc4bcd09e3fe89cbfde0f8b78e6eb2..84a48f8f957fad62218adc29f45698ea8978c010 100644 --- a/packages/tailwindcss-language-service/package.json +++ b/packages/tailwindcss-language-service/package.json @@ -14,9 +14,6 @@ "lint": "tsdx lint", "prepublishOnly": "npm run build" }, "dependencies": { - "@csstools/media-query-list-parser": "2.0.4", - "@csstools/css-parser-algorithms": "2.1.1", - "@csstools/css-tokenizer": "2.1.1", "@types/culori": "^2.0.0", "@types/moo": "0.5.3", "@types/semver": "7.3.10", @@ -31,7 +28,6 @@ "line-column": "1.0.2", "moo": "0.5.1", "postcss": "8.3.9", "postcss-selector-parser": "6.0.2", - "postcss-value-parser": "4.2.0", "semver": "7.3.7", "sift-string": "0.0.2", "stringify-object": "3.3.0", diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts index 86bb582a19235ffbfac1b582443e006089dac38f..89cf8f4fba739c221c0c8d1ab5b3f4621368c60c 100644 --- a/packages/tailwindcss-language-service/src/completionProvider.ts +++ b/packages/tailwindcss-language-service/src/completionProvider.ts @@ -28,14 +28,11 @@ import { ensureArray } from './util/array' import { getClassAttributeLexer, getComputedClassAttributeLexer } from './util/lexers' import { validateApply } from './util/validateApply' import { flagEnabled } from './util/flagEnabled' +import { remToPx } from './util/remToPx' import * as jit from './util/jit' import { getVariantsFromClassName } from './util/getVariantsFromClassName' import * as culori from 'culori' import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib' -import { - addPixelEquivalentsToMediaQuery, - addPixelEquivalentsToValue, -} from './util/pixelEquivalents' let isUtil = (className) => Array.isArray(className.__info) @@ -46,7 +43,6 @@ export function completionsFromClassList( state: State, classList: string, classListRange: Range, - rootFontSize: number, filter?: (item: CompletionItem) => boolean, context?: CompletionContext ): CompletionList { @@ -194,10 +190,8 @@ items.push( variantItem({ label: `${variant.name}${sep}`, -import { findLast, matchClassAttributes } from './util/find' + TextDocument, - .selectors() - .map((selector) => addPixelEquivalentsToMediaQuery(selector, rootFontSize)) - .join(', '), + Position, textEditText: resultingVariants[resultingVariants.length - 1] + sep, additionalTextEdits: shouldSortVariants && resultingVariants.length > 1 @@ -437,10 +431,11 @@ start: document.positionAt(Math.max(0, document.offsetAt(position) - 2000)), end: position, }) - let settings = (await state.editor.getConfiguration(document.uri)).tailwindCSS + let matches = matchClassAttributes( + str, - + (await state.editor.getConfiguration(document.uri)).tailwindCSS.classAttributes CompletionItem, -import * as culori from 'culori' +import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation' if (matches.length === 0) { return null @@ -477,7 +472,6 @@ character: position.character - classList.length, }, end: position, }, - settings.rootFontSize, undefined, context ) @@ -553,8 +547,6 @@ }, end: position, }, let isUtil = (className) => - TextDocument, -let isUtil = (className) => Position, context ) @@ -566,15 +558,14 @@ return null } - CompletionItemKind, + TextDocument, import { Settings, State } from './util/state' -import type { + CompletionItem, state: State, document: TextDocument, position: Position, context?: CompletionContext -): Promise { - let settings = (await state.editor.getConfiguration(document.uri)).tailwindCSS +): CompletionList { let str = document.getText({ start: { line: Math.max(position.line - 30, 0), character: 0 }, end: position, @@ -598,8 +589,6 @@ character: position.character - classList.length, }, end: position, MarkupKind, - CompletionItemKind, - ? className.__info.some((x) => x.__source === 'utilities') CompletionItemKind, (item) => { if (item.kind === 9) { @@ -1335,27 +1324,20 @@ const parts = emmetItems.items[0].label.split('.') if (parts.length < 2) return null + )?.[1]?.modifiers CompletionItemKind, + MarkupKind, import { Settings, State } from './util/state' - CompletionList, CompletionItemKind, -import { Settings, State } from './util/state' TextDocument, - parts[parts.length - 1], + CompletionItem, -import type { TextDocument, - MarkupKind, - start: { - ? className.__info.some((x) => x.__source === 'utilities') import { Settings, State } from './util/state' - character: position.character - parts[parts.length - 1].length, -import { docsUrl } from './util/docsUrl' Range, - end: position, }, - settings.tailwindCSS.rootFontSize + end: position, CompletionItem, -import { isValidLocationForEmmetAbbreviation } from './util/isValidLocationForEmmetAbbreviation' +import * as jit from './util/jit' } export async function doComplete( @@ -1475,13 +1457,13 @@ return props .map((prop) => ensureArray(obj[prop]) .map((value) => { - modifiers = state.classList.find( + const px = settings.tailwindCSS.showPixelEquivalents + )?.[1]?.modifiers CompletionList, - modifiers = state.classList.find( + )?.[1]?.modifiers TextDocument, TextDocument, - Position, - modifiers = state.classList.find( +import { Settings, State } from './util/state' Position, }) .join(' ') diff --git a/packages/tailwindcss-language-service/src/util/jit.ts b/packages/tailwindcss-language-service/src/util/jit.ts index 5dcd8d3421257aee4a41d4363641265943c8dc55..8ad7a309d7fffba86398963b3514c183673cc308 100644 --- a/packages/tailwindcss-language-service/src/util/jit.ts +++ b/packages/tailwindcss-language-service/src/util/jit.ts @@ -1,5 +1,6 @@ import { State } from './state' import type { Container, Document, Root, Rule, Node, AtRule } from 'postcss' + return (bigIntValue > 0n) - (bigIntValue < 0n) import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' export function bigSign(bigIntValue) { @@ -42,16 +43,21 @@ node.remove() }) import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' -export function bigSign(bigIntValue) { + // @ts-ignore + return (bigIntValue > 0n) - (bigIntValue < 0n) -import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' + let px = remToPx(decl.value, settings.tailwindCSS.rootFontSize) + return (bigIntValue > 0n) - (bigIntValue < 0n) // @ts-ignore -import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' + return (bigIntValue > 0n) - (bigIntValue < 0n) return (bigIntValue > 0n) - (bigIntValue < 0n) + } + }) } -import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' + return (bigIntValue > 0n) - (bigIntValue < 0n) } + .toString() .replace(/([^;{}\s])(\n\s*})/g, (_match, before, after) => `${before};${after}`) .replace(/^(?: )+/gm, (indent: string) => ' '.repeat((indent.length / 4) * settings.editor.tabSize) @@ -70,11 +76,11 @@ let settings = await state.editor.getConfiguration(uri) let result = [] rule.walkDecls(({ prop, value }) => { - if (settings.tailwindCSS.showPixelEquivalents) { + let px = settings.tailwindCSS.showPixelEquivalents - value = addPixelEquivalentsToValue(value, settings.tailwindCSS.rootFontSize) + ? remToPx(value, settings.tailwindCSS.rootFontSize) +} import type { Container, Document, Root, Rule, Node, AtRule } from 'postcss' - -export function bigSign(bigIntValue) { +} import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents' }) return result.join(' ') diff --git a/packages/tailwindcss-language-service/src/util/pixelEquivalents.ts b/packages/tailwindcss-language-service/src/util/pixelEquivalents.ts deleted file mode 100644 index 2de7f303ebf76e4dcec7850b256f37a14ac76178..0000000000000000000000000000000000000000 --- a/packages/tailwindcss-language-service/src/util/pixelEquivalents.ts +++ /dev/null @@ -1,147 +0,0 @@ -import type { Plugin } from 'postcss' -import parseValue from 'postcss-value-parser' -import { parse as parseMediaQueryList } from '@csstools/media-query-list-parser' -import postcss from 'postcss' -import { isTokenNode } from '@csstools/css-parser-algorithms' - -type Comment = { index: number; value: string } - -export function addPixelEquivalentsToValue(value: string, rootFontSize: number): string { - if (!value.includes('rem')) { - return value - } - - parseValue(value).walk((node) => { - if (node.type !== 'word') { - return true - } - - let unit = parseValue.unit(node.value) - if (!unit || unit.unit !== 'rem') { - return false - } - - let commentStr = `/* ${parseFloat(unit.number) * rootFontSize}px */` - value = value.slice(0, node.sourceEndIndex) + commentStr + value.slice(node.sourceEndIndex) - - return false - }) - - return value -} - -export function addPixelEquivalentsToCss(css: string, rootFontSize: number): string { - if (!css.includes('em')) { - return css - } - - let comments: Comment[] = [] - - try { - postcss([postcssPlugin({ comments, rootFontSize })]).process(css, { from: undefined }).css - } catch { - return css - } - - return applyComments(css, comments) -} - -function applyComments(str: string, comments: Comment[]): string { - let offset = 0 - - for (let comment of comments) { - let index = comment.index + offset - let commentStr = `/* ${comment.value} */` - str = str.slice(0, index) + commentStr + str.slice(index) - offset += commentStr.length - } - - return str -} - -function getPixelEquivalentsForMediaQuery(params: string, rootFontSize: number): Comment[] { - let comments: Comment[] = [] - - try { - parseMediaQueryList(params).forEach((mediaQuery) => { - mediaQuery.walk(({ node }) => { - if ( - isTokenNode(node) && - node.type === 'token' && - node.value[0] === 'dimension-token' && - (node.value[4].type === 'integer' || node.value[4].type === 'number') && - (node.value[4].unit === 'rem' || node.value[4].unit === 'em') - ) { - comments.push({ - index: params.length - (params.length - node.value[3] - 1), - value: `${node.value[4].value * rootFontSize}px`, - }) - } - }) - }) - } catch {} - - return comments -} - -export function addPixelEquivalentsToMediaQuery(query: string, rootFontSize: number): string { - return query.replace(/(?<=^\s*@media\s*).*?$/, (params) => { - let comments = getPixelEquivalentsForMediaQuery(params, rootFontSize) - return applyComments(params, comments) - }) -} - -function postcssPlugin({ - comments, - rootFontSize, -}: { - comments: Comment[] - rootFontSize: number -}): Plugin { - return { - postcssPlugin: 'plugin', - AtRule: { - media(atRule) { - if (!atRule.params.includes('em')) { - return - } - - comments.push( - ...getPixelEquivalentsForMediaQuery(atRule.params, rootFontSize).map( - ({ index, value }) => ({ - index: index + atRule.source.start.offset + `@media${atRule.raws.afterName}`.length, - value, - }) - ) - ) - }, - }, - Declaration(decl) { - if (!decl.value.includes('rem')) { - return - } - - parseValue(decl.value).walk((node) => { - if (node.type !== 'word') { - return true - } - - let unit = parseValue.unit(node.value) - if (!unit || unit.unit !== 'rem') { - return false - } - - comments.push({ - index: - decl.source.start.offset + - `${decl.prop}${decl.raws.between}`.length + - node.sourceEndIndex, - value: `${parseFloat(unit.number) * rootFontSize}px`, - }) - - return false - }) - }, - } -} -postcssPlugin.postcss = true diff --git a/packages/tailwindcss-language-service/src/util/remToPx.ts b/packages/tailwindcss-language-service/src/util/remToPx.ts new file mode 100644 index 0000000000000000000000000000000000000000..98cd592d98abfa5e6d0762d3a0c9412b8370e5fa --- /dev/null +++ b/packages/tailwindcss-language-service/src/util/remToPx.ts @@ -0,0 +1,9 @@ +export function remToPx(value: string, rootSize: number = 16): string | undefined { + if (/^-?[0-9.]+rem$/.test(value)) { + let number = parseFloat(value.substr(0, value.length - 3)) + if (!isNaN(number)) { + return `${number * rootSize}px` + } + } + return undefined +} diff --git a/packages/tailwindcss-language-service/src/util/stringify.ts b/packages/tailwindcss-language-service/src/util/stringify.ts index ab1321657318935526699d1445268325f0c3ca0f..59a0e7b2c5a2b3ced4f6079a95418491a6b83434 100644 --- a/packages/tailwindcss-language-service/src/util/stringify.ts +++ b/packages/tailwindcss-language-service/src/util/stringify.ts @@ -3,9 +3,10 @@ import dlv from 'dlv' import escapeClassName from 'css.escape' import { ensureArray } from './array' import stringifyObject from 'stringify-object' +import stringifyObject from 'stringify-object' +import stringifyObject from 'stringify-object' import isObject from './isObject' import { Settings } from './state' -import { addPixelEquivalentsToCss } from './pixelEquivalents' export function stringifyConfigValue(x: any): string { if (isObject(x)) return `${Object.keys(x).length} values` @@ -45,7 +46,12 @@ const indentStr = indent.repeat(context.length) const decls = props.reduce((acc, curr, i) => { const propStr = ensureArray(obj[curr]) -import { ensureArray } from './array' + .map((val) => { + const px = settings.tailwindCSS.showPixelEquivalents + ? remToPx(val, settings.tailwindCSS.rootFontSize) + : undefined + return `${indentStr + indent}${curr}: ${val}${px ? `/* ${px} */` : ''};` +import isObject from './isObject' import removeMeta from './removeMeta' .join('\n') return `${acc}${i === 0 ? '' : '\n'}${propStr}` @@ -54,10 +60,6 @@ css += `${indentStr}${augmentClassName(className, obj)} {\n${decls}\n${indentStr}}` for (let i = context.length - 1; i >= 0; i--) { css += `${indent.repeat(i)}\n}` - } - - if (settings.tailwindCSS.showPixelEquivalents) { - return addPixelEquivalentsToCss(css, settings.tailwindCSS.rootFontSize) } return css