Include pixel equivalents in more places (#775)
* Show pixel equivalents for more `rem`/`em` values * Add pixel equivalents to media query variant completionsmaster
parent
5e9cce64ef
commit
460d921ca3
|
@ -6,6 +6,9 @@
|
|||
"": {
|
||||
"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",
|
||||
|
@ -46,6 +49,7 @@
|
|||
"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",
|
||||
|
@ -1716,6 +1720,49 @@
|
|||
"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": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz",
|
||||
|
@ -22936,6 +22983,23 @@
|
|||
"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",
|
||||
"resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz",
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
"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",
|
||||
|
@ -28,6 +31,7 @@
|
|||
"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",
|
||||
|
|
|
@ -28,11 +28,14 @@ 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)
|
||||
|
@ -43,6 +46,7 @@ export function completionsFromClassList(
|
|||
state: State,
|
||||
classList: string,
|
||||
classListRange: Range,
|
||||
rootFontSize: number,
|
||||
filter?: (item: CompletionItem) => boolean,
|
||||
context?: CompletionContext
|
||||
): CompletionList {
|
||||
|
@ -190,7 +194,10 @@ export function completionsFromClassList(
|
|||
items.push(
|
||||
variantItem({
|
||||
label: `${variant.name}${sep}`,
|
||||
detail: variant.selectors().join(', '),
|
||||
detail: variant
|
||||
.selectors()
|
||||
.map((selector) => addPixelEquivalentsToMediaQuery(selector, rootFontSize))
|
||||
.join(', '),
|
||||
textEditText: resultingVariants[resultingVariants.length - 1] + sep,
|
||||
additionalTextEdits:
|
||||
shouldSortVariants && resultingVariants.length > 1
|
||||
|
@ -430,10 +437,9 @@ async function provideClassAttributeCompletions(
|
|||
end: position,
|
||||
})
|
||||
|
||||
let matches = matchClassAttributes(
|
||||
str,
|
||||
(await state.editor.getConfiguration(document.uri)).tailwindCSS.classAttributes
|
||||
)
|
||||
let settings = (await state.editor.getConfiguration(document.uri)).tailwindCSS
|
||||
|
||||
let matches = matchClassAttributes(str, settings.classAttributes)
|
||||
|
||||
if (matches.length === 0) {
|
||||
return null
|
||||
|
@ -470,6 +476,7 @@ async function provideClassAttributeCompletions(
|
|||
},
|
||||
end: position,
|
||||
},
|
||||
settings.rootFontSize,
|
||||
undefined,
|
||||
context
|
||||
)
|
||||
|
@ -544,6 +551,7 @@ async function provideCustomClassNameCompletions(
|
|||
},
|
||||
end: position,
|
||||
},
|
||||
settings.tailwindCSS.rootFontSize,
|
||||
undefined,
|
||||
context
|
||||
)
|
||||
|
@ -555,12 +563,13 @@ async function provideCustomClassNameCompletions(
|
|||
return null
|
||||
}
|
||||
|
||||
function provideAtApplyCompletions(
|
||||
async function provideAtApplyCompletions(
|
||||
state: State,
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
context?: CompletionContext
|
||||
): CompletionList {
|
||||
): Promise<CompletionList> {
|
||||
let settings = (await state.editor.getConfiguration(document.uri)).tailwindCSS
|
||||
let str = document.getText({
|
||||
start: { line: Math.max(position.line - 30, 0), character: 0 },
|
||||
end: position,
|
||||
|
@ -584,6 +593,7 @@ function provideAtApplyCompletions(
|
|||
},
|
||||
end: position,
|
||||
},
|
||||
settings.rootFontSize,
|
||||
(item) => {
|
||||
if (item.kind === 9) {
|
||||
return (
|
||||
|
@ -1318,13 +1328,18 @@ async function provideEmmetCompletions(
|
|||
const parts = emmetItems.items[0].label.split('.')
|
||||
if (parts.length < 2) return null
|
||||
|
||||
return completionsFromClassList(state, parts[parts.length - 1], {
|
||||
start: {
|
||||
line: position.line,
|
||||
character: position.character - parts[parts.length - 1].length,
|
||||
return completionsFromClassList(
|
||||
state,
|
||||
parts[parts.length - 1],
|
||||
{
|
||||
start: {
|
||||
line: position.line,
|
||||
character: position.character - parts[parts.length - 1].length,
|
||||
},
|
||||
end: position,
|
||||
},
|
||||
end: position,
|
||||
})
|
||||
settings.tailwindCSS.rootFontSize
|
||||
)
|
||||
}
|
||||
|
||||
export async function doComplete(
|
||||
|
@ -1444,10 +1459,10 @@ function stringifyDecls(obj: any, settings: Settings): string {
|
|||
.map((prop) =>
|
||||
ensureArray(obj[prop])
|
||||
.map((value) => {
|
||||
const px = settings.tailwindCSS.showPixelEquivalents
|
||||
? remToPx(value, settings.tailwindCSS.rootFontSize)
|
||||
: undefined
|
||||
return `${prop}: ${value}${px ? `/* ${px} */` : ''};`
|
||||
if (settings.tailwindCSS.showPixelEquivalents) {
|
||||
value = addPixelEquivalentsToValue(value, settings.tailwindCSS.rootFontSize)
|
||||
}
|
||||
return `${prop}: ${value};`
|
||||
})
|
||||
.join(' ')
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { State } from './state'
|
||||
import type { Container, Document, Root, Rule, Node, AtRule } from 'postcss'
|
||||
import { remToPx } from './remToPx'
|
||||
import { addPixelEquivalentsToCss, addPixelEquivalentsToValue } from './pixelEquivalents'
|
||||
|
||||
export function bigSign(bigIntValue) {
|
||||
// @ts-ignore
|
||||
|
@ -41,17 +41,13 @@ export async function stringifyRoot(state: State, root: Root, uri?: string): Pro
|
|||
node.remove()
|
||||
})
|
||||
|
||||
let css = clone.toString()
|
||||
|
||||
if (settings.tailwindCSS.showPixelEquivalents) {
|
||||
clone.walkDecls((decl) => {
|
||||
let px = remToPx(decl.value, settings.tailwindCSS.rootFontSize)
|
||||
if (px) {
|
||||
decl.value = `${decl.value}/* ${px} */`
|
||||
}
|
||||
})
|
||||
css = addPixelEquivalentsToCss(css, settings.tailwindCSS.rootFontSize)
|
||||
}
|
||||
|
||||
return clone
|
||||
.toString()
|
||||
return css
|
||||
.replace(/([^;{}\s])(\n\s*})/g, (_match, before, after) => `${before};${after}`)
|
||||
.replace(/^(?: )+/gm, (indent: string) =>
|
||||
' '.repeat((indent.length / 4) * settings.editor.tabSize)
|
||||
|
@ -70,10 +66,10 @@ export async function stringifyDecls(state: State, rule: Rule, uri?: string): Pr
|
|||
|
||||
let result = []
|
||||
rule.walkDecls(({ prop, value }) => {
|
||||
let px = settings.tailwindCSS.showPixelEquivalents
|
||||
? remToPx(value, settings.tailwindCSS.rootFontSize)
|
||||
: undefined
|
||||
result.push(`${prop}: ${value}${px ? `/* ${px} */` : ''};`)
|
||||
if (settings.tailwindCSS.showPixelEquivalents) {
|
||||
value = addPixelEquivalentsToValue(value, settings.tailwindCSS.rootFontSize)
|
||||
}
|
||||
result.push(`${prop}: ${value};`)
|
||||
})
|
||||
return result.join(' ')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
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
|
|
@ -1,9 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -2,10 +2,10 @@ import removeMeta from './removeMeta'
|
|||
import dlv from 'dlv'
|
||||
import escapeClassName from 'css.escape'
|
||||
import { ensureArray } from './array'
|
||||
import { remToPx } from './remToPx'
|
||||
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,12 +45,7 @@ export function stringifyCss(className: string, obj: any, settings: Settings): s
|
|||
const indentStr = indent.repeat(context.length)
|
||||
const decls = props.reduce((acc, curr, i) => {
|
||||
const propStr = ensureArray(obj[curr])
|
||||
.map((val) => {
|
||||
const px = settings.tailwindCSS.showPixelEquivalents
|
||||
? remToPx(val, settings.tailwindCSS.rootFontSize)
|
||||
: undefined
|
||||
return `${indentStr + indent}${curr}: ${val}${px ? `/* ${px} */` : ''};`
|
||||
})
|
||||
.map((val) => `${indentStr + indent}${curr}: ${val};`)
|
||||
.join('\n')
|
||||
return `${acc}${i === 0 ? '' : '\n'}${propStr}`
|
||||
}, '')
|
||||
|
@ -60,6 +55,10 @@ export function stringifyCss(className: string, obj: any, settings: Settings): s
|
|||
css += `${indent.repeat(i)}\n}`
|
||||
}
|
||||
|
||||
if (settings.tailwindCSS.showPixelEquivalents) {
|
||||
return addPixelEquivalentsToCss(css, settings.tailwindCSS.rootFontSize)
|
||||
}
|
||||
|
||||
return css
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue