w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.38k stars 641 forks source link

CSS syntax highlighting proposal #9247

Open dragoncoder047 opened 10 months ago

dragoncoder047 commented 10 months ago

This would be similar to the existing CSS Painting API.

Basically any text-color-affecting property (idk the proper term) would be able to use a new syntax() function text-syntax property to define which syntax grammar for the text that gets highlighted, and the individual tokens could be styled using the ::token() pseudo-element.

So for example:

::token(comment) { color: gray }
::token(tag-name) { color: red } /* these are really bad colors */
::token(attr-name) { color: blue }
::token(attr-value) { color: green }
textarea.language-html { text-syntax: html }

and bam, instant syntax-highlited textarea.

Browsers would have built-in html, css, and js grammars, obviously (utilizing the existing parsers -- side effect is the grammar evolves with the language standard!), and for everything else there could be a plugin-based approach like the CSS Paint API. Something like CSS.syntaxHighlighting.addModule("path/to/worker.js"). Haven't bothered to think about how the worker would be passed the string to highlight (differential/incremental edits? highlight the whole string every time?), provide tokens to the browser (state machine? something else?), etc.

I don't think defining a grammar as opposed to a worker would be a good idea because that would be prone to bias towards a particular grammar definition, because there are tons of different kinds (PrismJS, highlight.js, Rainbow, Pygments, GeSHi, Tree-sitter, TextMate, even Emacs modes, just to name a few...)

One other thing I think would be useful would be a way for the CSS engine to signal it doesn't have a particular grammar registered, and prompt auto-loading via Javascript:

CSS.syntaxHighlighting.addEventListener("language-not-found", e => {
    CSS.syntaxHighlighting.addModule(`/path/to/grammars/${e.language}.js`);
});

@LeaVerou would probably be a good person to comment on this proposal (for obvious reasons).

EDIT: a function() was a silly idea, I fixed it...

LeaVerou commented 10 months ago

I don't think this is the appropriate level of abstraction for CSS. Do note that there is the CSS Custom Highlight API, which is very similar to what you're describing here, but covers more broad use cases than syntax highlighting. In fact, we (Prism) are considering using once it gets Baseline browser support, though not sure we'd use it as a default.

Though do note that something like this does limit what you can do with syntax highlighting compared to the current approach. E.g. these Prism plugins would not be possible if we used the CSS Custom Highlight API:

Which might be an acceptable tradeoff, but something to have in mind.

dragoncoder047 commented 10 months ago

I don't think this is not the appropriate level of abstraction for CSS.

Do you support my proposal or not? Double-negatives are really confusing...

It might be possible to implement something like those using ::before pseudo elements.

And honestly, I did expect there to be holes in this proposal with regards to interactive content (i.e. beyond simple :active or :hover styles).

One other use I did think of for this would be a textbook website could include some sort of worker that could apply token styles to all of the terms in the glossary, so they would appear bold (::token(glossary) { font-weight: bold }) and then the publisher doesn't have to rebuild the entire book when they update the glossary. Of course, actually liking to the glossary would not be possible with CSS alone.

LeaVerou commented 10 months ago

Do you support my proposal or not? Double-negatives are really confusing...

Typo :) Fixed it now.

One other use I did think of for this …

I think it would be useful to define your proposal in terms of delta from the Custom Highlight API. What does it offer that is better, and could these use cases be covered by an extension to this existing API, rather than a whole new one.

dragoncoder047 commented 10 months ago

I think it would be useful to define your proposal in terms of delta from the Custom Highlight API. What does it offer that is better, and could these use cases be covered by an extension to this existing API, rather than a whole new one.

The biggest thing is that the CSS Custom Highlight API doesn't work on textareas. See this jsfiddle -- the code tries to highlight the "print" in both a textarea and a pre, and it only works on the pre.

<textarea cols=40>if True: print("hello")</textarea>
<pre>if False: print("bye")</pre>
var area = document.querySelector("textarea");
var pre = document.querySelector("pre");
var areatext = area.childNodes[0];
var pretext = pre.childNodes[0];
var print1 = new Range();
print1.setStart(areatext, 9);
print1.setEnd(areatext, 14);
var print2 = new Range();
print2.setStart(pretext, 10);
print2.setEnd(pretext, 15);
CSS.highlights.set('function', new Highlight(print1, print2));
console.log("textarea has child node: ", areatext.toString());
console.log("pre has child node: ", pretext.toString());
::highlight(function) { color: red }

The console.log()'s at the end both print [object Text] at the end for the childNodes[0] of both the textarea and the pre, and the range doesn't balk that it wasn't passed a valid node, but the highlight doesn't show up.

So we need some way to highlight text in textareas. Seriously, all of the major code editors out there only use a (hidden) textarea for capturing input events, and they re-implement all of the hard work of rendering the text onscreen in javascript.

tabatkins commented 10 months ago

Then yeah, I suspect pushing on that part (being able to style ranges in a textarea and other inputs) might be the place to start, before we step to a higher level and build in some parsing libraries. Outside of textareas, it looks like Custom Highlights does indeed solve the issue decently well, at least at a quick glance.

LeaVerou commented 10 months ago

Yeah, that sounds like a bug with the Highlight API that we should fix; definitely not a reason to design a whole other API.

schenney-chromium commented 10 months ago

It's not obvious if it's a browser bug or an API bug. If you can create a range you should be able to set a highlight on it, as I understand the spec. Does anyone happen to know if you can create the desired range for the textarea?

As reference, we can certainly draw spelling and grammar markers in textarea.

dragoncoder047 commented 10 months ago

As reference, we can certainly draw spelling and grammar markers in textarea.

You can?? I changed the CSS in the above jsfiddle to this:

::highlight(function) { text-decoration-line: underline;
  text-decoration-style: wavy;
  text-decoration-color: red; }

and it drew the red squiggle only on the pre, but NOT on the textarea.