parserlib: reuse global keywords

This commit is contained in:
tophf 2021-08-20 13:01:56 +03:00
parent 9d6542a39d
commit 8e36e0277f
3 changed files with 30 additions and 28 deletions

View File

@ -16,17 +16,11 @@
const rxVAR = /(^|[^-.\w\u0080-\uFFFF])var\(/iyu; const rxVAR = /(^|[^-.\w\u0080-\uFFFF])var\(/iyu;
const rxCONSUME = /([-\w]*\s*:\s?)?/yu; const rxCONSUME = /([-\w]*\s*:\s?)?/yu;
const cssMime = CodeMirror.mimeModes['text/css']; const cssMime = CodeMirror.mimeModes['text/css'];
const cssGlobalValues = [
'inherit',
'initial',
'revert',
'unset',
];
const docFuncs = addSuffix(cssMime.documentTypes, '('); const docFuncs = addSuffix(cssMime.documentTypes, '(');
const {tokenHooks} = cssMime; const {tokenHooks} = cssMime;
const originalCommentHook = tokenHooks['/']; const originalCommentHook = tokenHooks['/'];
const originalHelper = CodeMirror.hint.css || (() => {}); const originalHelper = CodeMirror.hint.css || (() => {});
let cssMedia, cssProps, cssPropsValues; let cssMedia, cssProps, cssValues;
const AOT_ID = 'autocompleteOnTyping'; const AOT_ID = 'autocompleteOnTyping';
const AOT_PREF_ID = 'editor.' + AOT_ID; const AOT_PREF_ID = 'editor.' + AOT_ID;
@ -148,8 +142,8 @@
leftLC = leftLC.replace(/^[^\w\s]\s*/, ''); leftLC = leftLC.replace(/^[^\w\s]\s*/, '');
} }
if (prop.startsWith('--')) prop = 'color'; // assuming 90% of variables are colors if (prop.startsWith('--')) prop = 'color'; // assuming 90% of variables are colors
if (!cssPropsValues) cssPropsValues = await linterMan.worker.getCssPropsValues(); if (!cssValues) cssValues = await linterMan.worker.getCssPropsValues();
list = [...new Set([...cssPropsValues[prop] || [], ...cssGlobalValues])]; list = [...new Set([...cssValues.own[prop] || [], ...cssValues.global])];
end = prev + execAt(/(\s*[-a-z(]+)?/y, prev, text)[0].length; end = prev + execAt(/(\s*[-a-z(]+)?/y, prev, text)[0].length;
} }
} }

View File

@ -18,7 +18,10 @@
getCssPropsValues() { getCssPropsValues() {
require(['/js/csslint/parserlib']); /* global parserlib */ require(['/js/csslint/parserlib']); /* global parserlib */
const {css: {Colors, Properties}, util: {describeProp}} = parserlib; const {
css: {Colors, GlobalKeywords, Properties},
util: {describeProp},
} = parserlib;
const namedColors = Object.keys(Colors); const namedColors = Object.keys(Colors);
const rxNonWord = /(?:<.+?>|[^-\w<(]+\d*)+/g; const rxNonWord = /(?:<.+?>|[^-\w<(]+\d*)+/g;
const res = {}; const res = {};
@ -41,7 +44,7 @@
if (uniq.length) res[k] = uniq; if (uniq.length) res[k] = uniq;
} }
} }
return res; return {own: res, global: GlobalKeywords};
}, },
getRules(linter) { getRules(linter) {

View File

@ -30,12 +30,18 @@ self.parserlib = (() => {
//#region Properties //#region Properties
// Global keywords that can be set for any property are conveniently listed in `all` prop:
// https://drafts.csswg.org/css-cascade/#all-shorthand
const GlobalKeywords = ['initial', 'inherit', 'revert', 'unset'];
const isGlobalKeyword = RegExp.prototype.test.bind(
new RegExp(`^(${GlobalKeywords.join('|')})$`, 'i'));
const Properties = { const Properties = {
'accent-color': 'auto | <color>', 'accent-color': 'auto | <color>',
'align-items': 'normal | stretch | <baseline-position> | [ <overflow-position>? <self-position> ]', 'align-items': 'normal | stretch | <baseline-position> | [ <overflow-position>? <self-position> ]',
'align-content': '<align-content>', 'align-content': '<align-content>',
'align-self': '<align-self>', 'align-self': '<align-self>',
'all': 'initial | inherit | revert | unset', 'all': GlobalKeywords.join(' | '),
'alignment-adjust': 'auto | baseline | before-edge | text-before-edge | middle | central | ' + 'alignment-adjust': 'auto | baseline | before-edge | text-before-edge | middle | central | ' +
'after-edge | text-after-edge | ideographic | alphabetic | hanging | ' + 'after-edge | text-after-edge | ideographic | alphabetic | hanging | ' +
'mathematical | <length-pct>', 'mathematical | <length-pct>',
@ -702,8 +708,8 @@ self.parserlib = (() => {
const VTSimple = { const VTSimple = {
'<absolute-size>': 'xx-small | x-small | small | medium | large | x-large | xx-large', '<absolute-size>': 'xx-small | x-small | small | medium | large | x-large | xx-large',
'<animateable-feature>': 'scroll-position | contents | <animateable-feature-name>', '<animateable-feature>': 'scroll-position | contents | <animateable-feature-name>',
'<animateable-feature-name>': p => vtIsIdent(p) && '<animateable-feature-name>': p => vtIsIdent(p) && !isGlobalKeyword(p) &&
!/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(p), !/^(will-change|auto|scroll-position|contents)$/i.test(p),
'<angle>': p => p.type === 'angle' || p.isCalc, '<angle>': p => p.type === 'angle' || p.isCalc,
'<angle-or-0>': p => p.text === '0' || p.type === 'angle' || p.isCalc, '<angle-or-0>': p => p.text === '0' || p.type === 'angle' || p.isCalc,
'<attr>': vtIsAttr, '<attr>': vtIsAttr,
@ -752,8 +758,8 @@ self.parserlib = (() => {
'<hex-color>': p => p.tokenType === Tokens.HASH, //eslint-disable-line no-use-before-define '<hex-color>': p => p.tokenType === Tokens.HASH, //eslint-disable-line no-use-before-define
'<icccolor>': 'cielab() | cielch() | cielchab() | icc-color() | icc-named-color()', '<icccolor>': 'cielab() | cielch() | cielchab() | icc-color() | icc-named-color()',
'<ident>': vtIsIdent, '<ident>': vtIsIdent,
'<ident-for-grid>': p => vtIsIdent(p) && '<ident-for-grid>': p => vtIsIdent(p) && !isGlobalKeyword(p.value) &&
!/^(span|auto|initial|inherit|unset|default)$/i.test(p.value), !/^(span|auto|default)$/i.test(p.value),
'<ident-not-generic-family>': p => vtIsIdent(p) && !VTSimple['<generic-family>'](p), '<ident-not-generic-family>': p => vtIsIdent(p) && !VTSimple['<generic-family>'](p),
'<ident-not-none>': p => vtIsIdent(p) && !lowerCmp(p.value, 'none'), '<ident-not-none>': p => vtIsIdent(p) && !lowerCmp(p.value, 'none'),
'<image>': '<uri> | <gradient> | cross-fade()', '<image>': '<uri> | <gradient> | cross-fade()',
@ -793,8 +799,8 @@ self.parserlib = (() => {
'<shape-box>': '<box> | margin-box', '<shape-box>': '<box> | margin-box',
'<single-animation-direction>': 'normal | reverse | alternate | alternate-reverse', '<single-animation-direction>': 'normal | reverse | alternate | alternate-reverse',
'<single-animation-fill-mode>': 'none | forwards | backwards | both', '<single-animation-fill-mode>': 'none | forwards | backwards | both',
'<single-animation-name>': p => vtIsIdent(p) && '<single-animation-name>': p => vtIsIdent(p) && !isGlobalKeyword(p) &&
/^(?!(none|unset|initial|inherit)$)-?[a-z_][-a-z0-9_]+$/i.test(p), /^-?[a-z_][-a-z0-9_]+$/i.test(p),
'<string>': p => p.type === 'string', '<string>': p => p.type === 'string',
'<text-align>': 'start | end | left | right | center | justify | match-parent', '<text-align>': 'start | end | left | right | center | justify | match-parent',
'<text-decoration-style>': 'solid | double | dotted | dashed | wavy', '<text-decoration-style>': 'solid | double | dotted | dashed | wavy',
@ -2395,10 +2401,8 @@ self.parserlib = (() => {
const validationCache = new Map(); const validationCache = new Map();
function validateProperty(property, value) { function validateProperty(name, property, value) {
// Global keywords that can be set for any property are conveniently listed in `all` prop: if (isGlobalKeyword(value.parts[0])) {
// https://drafts.csswg.org/css-cascade/#all-shorthand
if (/^(inherit|initial|unset|revert)$/i.test(value.parts[0])) {
if (value.parts.length > 1) { if (value.parts.length > 1) {
throwEndExpected(value.parts[1], true); throwEndExpected(value.parts[1], true);
} }
@ -2407,7 +2411,7 @@ self.parserlib = (() => {
if (hasVarParts(value)) { if (hasVarParts(value)) {
return; return;
} }
const prop = lower(property); const prop = lower(name);
let known = validationCache.get(prop); let known = validationCache.get(prop);
if (known && known.has(value.text)) { if (known && known.has(value.text)) {
return; return;
@ -2417,7 +2421,7 @@ self.parserlib = (() => {
return; return;
} }
if (!spec) { if (!spec) {
throw new ValidationError(`Unknown property '${property}'.`, value); throw new ValidationError(`Unknown property '${name}'.`, value);
} }
// Property-specific validation. // Property-specific validation.
const expr = new PropertyValueIterator(value); const expr = new PropertyValueIterator(value);
@ -4066,7 +4070,7 @@ self.parserlib = (() => {
this.options.underscoreHack && property.hack === '_' this.options.underscoreHack && property.hack === '_'
? property.text ? property.text
: property.toString(); : property.toString();
validateProperty(name, value); validateProperty(name, property, value);
} catch (ex) { } catch (ex) {
if (!(ex instanceof ValidationError)) { if (!(ex instanceof ValidationError)) {
ex.message = ex.stack; ex.message = ex.stack;
@ -4650,14 +4654,15 @@ self.parserlib = (() => {
css: { css: {
Colors, Colors,
Combinator, Combinator,
GlobalKeywords,
Matcher,
MediaFeature,
MediaQuery,
Parser, Parser,
Properties, Properties,
PropertyName, PropertyName,
PropertyValue, PropertyValue,
PropertyValuePart, PropertyValuePart,
Matcher,
MediaFeature,
MediaQuery,
Selector, Selector,
SelectorPart, SelectorPart,
SelectorSubPart, SelectorSubPart,