PrismJS / prism

Lightweight, robust, elegant syntax highlighting.
https://prismjs.com
MIT License
12.35k stars 1.29k forks source link

[Enhancement] CSS elements, properties and URL values, and LESS @ rules #2431

Open gusbemacbe opened 4 years ago

gusbemacbe commented 4 years ago

Motivation

It seems that Prismjs does not have CSS element. CSS elements are white, but in the Dracula theme, it is purple.

Description

In reference to dracula/prism#11, I followed the Dracula's specific documentation. Imitating and mimicking the CSS syntax highlighting in VSCode, there are two differences between Prism's HTML and VSCode's HTML syntax highlightings. See the alternatives with solutions:

Alternatives

CSS elements

Using Dracula theme, compare CSS elements, entirely highlighted as white by the class .language-css in Prismjs, but in VSCode, it is different:

Dracula:

image

Prismjs:

image

Observation: The px should remain pink.

CSS properties

The property screen and is dominated by the class .language-css .token.atrule, and has different colours in Dracula and Prismjs themes.

Dracula:

image

Prismjs:

image

@ rules

Not just @main-color, the class .language-css .token.rule also takes control of @media which must remain purple. Observe that @media belongs to the class .language-css .token.atrule.

Dracula

image

Prismjs

image

I tried this CSS:

.language-css .token.rule:not(.atrule)
{
  color: var(--foreground);
}

CSS URL values

In Dracula, the values without quotation marks remain orange, but with quotation mark, they are highlighted as yellow. In Prismjs, both the values without and with quotation marks are dominated by the class .language-css .token.url. Also observe that parentheses ( and ) should remain purple if the regular expression finds url.

Dracula

image

Prismjs

image

RunDevelopment commented 4 years ago

CSS elements

I think you mean values. The only "elements" in CSS are element selectors.

To tokenize every valid CSS value, we have to know every valid CSS value and there are quite a few. Here's VSCode's CSS LS' list of values by property. After extracting all values and some filtering, I'm left with a list of ~500 values that will make a 6.1k characters long regex. The file sizes of CSS and CSS extras are ~1kB and ~3kB respectively. That's quite an increase in file size. And then there's also the issue of maintainability.

The easiest solution here is to set the default text color to the color you want CSS values to have. Not perfect but it comes close to your goal.

For those, interested. This is the regex: ```js /-moz-all|-moz-box|-moz-crisp-edges|-moz-deck|-moz-grab|-moz-grabbing|-moz-grid|-moz-grid-group|-moz-grid-line|-moz-groupbox|-moz-hidden-unscrollable|-moz-inline-box|-moz-inline-grid|-moz-inline-stack|-moz-mac-unified-toolbar|-moz-marker|-moz-none|-moz-popup|-moz-show-background|-moz-stack|-moz-win-borderless-glass|-moz-win-browsertabbar-toolbox|-moz-win-communications-toolbox|-moz-win-glass|-moz-win-media-toolbox|-moz-zoom-in|-moz-zoom-out|-ms-autohiding-scrollbar|-ms-flexbox|-ms-grid|-ms-inline-flexbox|-ms-inline-grid|-ms-page|-webkit-baseline-middle|-webkit-box|-webkit-flex|-webkit-grab|-webkit-grabbing|-webkit-inline-box|-webkit-inline-flex|-webkit-sticky|-webkit-zoom-in|-webkit-zoom-out|above|absolute|accumulate|active|additional-ligatures|additive|after|after-white-space|alias|all|all-petite-caps|all-scroll|all-small-caps|alpha|alphabetic|alternate|alternate-reverse|always|armenian|auto|avoid|avoid-column|avoid-page|avoid-region|backwards|balance|bar|baseline|before|below|bevel|bicubic|bidi-override|block|block-axis|bold|bolder|border|border-box|both|bottom|bounding-box|break|break-all|break-word|bt|bt-lr|bt-rl|butt|button|button-arrow-down|button-arrow-next|button-arrow-previous|button-arrow-up|button-bevel|capitalize|caps-lock-indicator|caption|caret|cell|center|chained|char|checkbox|checkbox-container|checkbox-label|circle|clear|clip|clone|col-resize|collapse|color|color-burn|color-dodge|column|column-reverse|common-ligatures|condensed|contain|content|content-box|contents|context-menu|contextual|continuous|copy|cover|crisp-edges|crispEdges|cross-slide-x|cross-slide-y|crosshair|current|cursive|cyclic|darken|dashed|decimal|decimal-leading-zero|default|default-button|dense|diagonal-fractions|dialog|difference|digits|disabled|disc|discard|discretionary-ligatures|distribute|distribute-all-lines|distribute-letter|distribute-space|dotted|double|double-tap-zoom|e-resize|each-box|element|elements|ellipsis|embed|end|evenodd|ew-resize|exclusion|expanded|extends|extra-condensed|extra-expanded|false|fantasy|fill|fit-content|fixed|flat|flex|flex-end|flex-start|flexbox|flip|flow-root|forwards|from-image|full-width|geometricPrecision|georgian|grab|grabbing|grid|grippers|groupbox|hard-light|help|hidden|hide|historical-forms|historical-ligatures|horizontal|horizontal-tb|hue|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ignore|inactive|infinite|inline|inline-axis|inline-block|inline-end|inline-flex|inline-flexbox|inline-start|inline-table|inset|inside|inter-cluster|inter-ideograph|inter-word|invert|isolate|isolate-override|italic|jis04|jis78|jis83|jis90|justify|kashida|keep-all|large|larger|layout|left|legacy|lighten|lighter|line|line-edge|line-through|linearRGB|lining-nums|list-item|listbox|listitem|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr|lr-bt|lr-tb|ltr|luminance|luminosity|mandatory|manipulation|manual|margin-box|markers|max-content|maximum|media-fullscreen-button|media-mute-button|media-play-button|media-seek-back-button|media-seek-forward-button|media-slider|media-sliderthumb|medium|menu|menuarrow|menuimage|menuitem|menuitemtext|menulist|menulist-button|menulist-text|menulist-textfield|menupopup|menuradio|menuseparator|message-box|middle|min-content|minimum|miter|mode|monospace|move|multiply|n-resize|narrower|ne-resize|nearest-neighbor|nesw-resize|new|newspaper|no-additional-ligatures|no-common-ligatures|no-contextual|no-discretionary-ligatures|no-drop|no-historical-ligatures|no-limit|none|nonzero|normal|not-allowed|nowrap|ns-resize|numeric|nw-resize|nwse-resize|oblique|off|oldstyle-nums|on|optimizeLegibility|optimizeQuality|optimizeSpeed|ordinal|outside|over|overlay|overline|padding|padding-box|page|paint|painted|pan-x|pan-y|paused|perspective|petite-caps|pinch-zoom|pixelated|plaintext|pointer|pre|pre-line|pre-wrap|preserve-3d|progress|progressbar|progresschunk|proportional-nums|proportional-width|proximity|punctuation|push-button|radio|radio-container|radio-label|radiomenuitem|railed|read-only|read-write|read-write-plaintext-only|region|relative|repeat|resizer|resizerpanel|reverse|right|ring|rl|rl-bt|rl-tb|root|round|row|row-resize|row-reverse|rtl|ruby|ruby-base|ruby-base-container|ruby-text|ruby-text-container|run-in|running|s-resize|sRGB|safe|sans-serif|saturation|save|scale-down|screen|scroll|scroll-position|scrollbar|scrollbar-small|scrollbarbutton-down|scrollbarbutton-left|scrollbarbutton-right|scrollbarbutton-up|scrollbargripper-horizontal|scrollbargripper-vertical|scrollbarthumb-horizontal|scrollbarthumb-vertical|scrollbartrack-horizontal|scrollbartrack-vertical|se-resize|searchfield|searchfield-cancel-button|searchfield-decoration|searchfield-results-button|searchfield-results-decoration|self-end|self-start|semi-condensed|semi-expanded|separate|separator|serif|show|sideways|sideways-lr|sideways-right|sideways-rl|simplified|size|slashed-zero|slice|slider-horizontal|slider-vertical|sliderthumb-horizontal|sliderthumb-vertical|small|small-caps|small-caption|smaller|smooth|soft-light|solid|space|space-around|space-between|space-evenly|span|spinner|spinner-downbutton|spinner-textfield|spinner-upbutton|square|square-button|stacked-fractions|start|static|status-bar|statusbar|statusbarpanel|sticky|stretch|strict|stroke|style|sub|subgrid|super|sw-resize|symbolic|tab|tab-scroll-arrow-back|tab-scroll-arrow-forward|table|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|tabpanels|tabular-nums|tb|tb-lr|tb-rl|text|text-bottom|text-top|textarea|textfield|textfield-multiline|titling-caps|toggle|toolbar|toolbox|tooltip|top|touch|traditional|treeheadercell|treeheadersortarrow|treeitem|treetwistyopen|treeview|treewisty|true|type|ultra-condensed|ultra-expanded|under|underline|unicase|unsafe|unsave|upper-alpha|upper-latin|upper-roman|uppercase|upright|vertical|vertical-lr|vertical-rl|vertical-text|vertical-to-horizontal|visible|visibleFill|visiblePainted|visibleStroke|w-resize|wait|wavy|weight|wider|window|wrap|wrap-reverse|x-large|x-small|xx-large|xx-small|zoom|zoom-in|zoom-out/; ```

The px should remain pink.

With CSS extras, all units are tokenized as .token.unit. That should help.

CSS properties

I'll add something for and, or, not, and only.

@ rules

You can use .token.atrule > .token.rule to given the @name part a specific color.

CSS URL values

This should be rather easy to implement by adding a string token for string values.

Also observe that parentheses ( and ) should remain purple [...]

In one of the above screenshots, parentheses are yellow. You probably have a rainbow braces extension installed, right? You can use Match braces to implement this.


I'll make a PR for the promised changes.

gusbemacbe commented 4 years ago

@RunDevelopment

The easiest solution here is to set the default text color to the color you want CSS values to have. Not perfect but it comes close to your goal. Details For those, interested. This is the regex:

I did not see red and other colour names and codes, etc. as part of this regex.

RunDevelopment commented 4 years ago

All colors are part of CSS extras.

gusbemacbe commented 4 years ago

I tried to figure out how I manipulated the CSS codes, using a value's regex for .language-css, but it is impossible in CSS file. I have to use JavaScript.

RunDevelopment commented 4 years ago

Sorry, I don't understand what you mean by "how I manipulated the CSS codes"?

gusbemacbe commented 4 years ago

@RunDevelopment

For example:

.languace-css [\-ms-inline-flexbox\][\-ms-inline-grid\]
{
}
RunDevelopment commented 4 years ago

Ahh, so you want to style tokens differently depending on their text content.

Yes, you will need JavaScript for that. You can use the Custom class plugin to accomplish that:

Prism.plugins.customClass.add(({content, type, language}) => {
    if (content.startsWith('-ms-') && type === 'value' && language === 'css') {
        return 'ms-value'; // add the `ms-value` class to all matching tokens
    }
});
gusbemacbe commented 4 years ago

Ahh, so you want to style tokens differently depending on their text content.

Yes, you will need JavaScript for that. You can use the Custom class plugin to accomplish that:

Prism.plugins.customClass.add(({content, type, language}) => {
  if (content.startsWith('-ms-') && type === 'value' && language === 'css') {
      return 'ms-value'; // add the `ms-value` class to all matching tokens
  }
});

Actually, I want to apply all these token, not just ms-. Is it possible that this new custom Prismjs plugin match the patterns from a text file?

Look, you do not need to add that to Prismjs project. I will add this custom class plugin only to Dracula Prismjs project.

RunDevelopment commented 4 years ago

I want to apply all these token

Which tokens? .value tokens?

Maybe we should take a step back: From my understanding, you want to highlight CSS values and you also want to highlight some CSS values differently, right? I assume that you already made some modifications to Prism's CSS language definition to add a value token for CSS values (regex above) because you can only highlight tokens with CSS.

There are two ways to highlight certain CSS values differently: The first way is what I described above. Use the Custom class plugin to add a marker-class to some tokens. However, Custom class is intended to be used if you don't want to (or can't) modify any language definitions but we can. So instead of modifying the CSS language definition like this:

Prism.languages.css.value = /\b(?:foo|bar|...)\b/;

We can split out the list of values directly like this:

Prism.languages.css.value = [
    {
        // this will produce `.token.value.foo-class` tokens
        pattern: /\b(?:foo)\b/,
        alias: 'foo-class'
    },
    // this will produce `.token.value` tokens
    /\b(?:bar|...)\b/
];

Look, you do not need to add that to Prismjs project.

Yes, that wasn't my intention.