mjbvz / vscode-lit-html

Adds syntax highlighting for html inside of JavaScript and TypeScript tagged template strings
https://marketplace.visualstudio.com/items?itemName=bierner.lit-html
MIT License
318 stars 75 forks source link

Can this work with standard template literals (non-tagged)? #14

Open benfrain opened 6 years ago

benfrain commented 6 years ago

Should this be working with non-tagged template literals?

For example, running insiders 1.20 and a ts file including the following I don't get any syntax highlighting:

return `
    <div class="mb-Summary" data-mb-controls="${idx}" aria-selected="${data.summarySelected}">
        <a class="mb-Summary_TitleAndStatus">
                <h2 class="mb-Summary_Title">${data.Stake} ${data.Type}</h2>
        </a>
    </div>
`

Should that work?

mjbvz commented 6 years ago

No, this is not supported

lancetipton commented 6 years ago

If you open up ~/ .vscode/extensions/bierner.lit-html-1.2.0/syntaxes/lit-html.json and change line 18 to be this: "begin": "(`\\s\<!--.-->)", Then you can do things like this

return `<!--html-->
    <div class="mb-Summary" data-mb-controls="${idx}" aria-selected="${data.summarySelected}">
        <a class="mb-Summary_TitleAndStatus">
                <h2 class="mb-Summary_Title">${data.Stake} ${data.Type}</h2>
        </a>
    </div>
`

You can also change \ to something like \ so it will be more specific. You will have to make that change each time the plugin updates, but it works well for me.

BBlackwo commented 6 years ago

@lancetipton04 I changed my json file slightly different from yours to get it working for me:

// Original
"begin": "(?x)(\\s+?(\\w+\\.)?(?:html|raw)\\s*)(`)",

// My change
"begin": "(?x)(\\s+?(`)(?:<!--.*-->)\\s*)",
WebReflection commented 6 years ago

any reason why the following wouldn't work for any template literal that contains HTML?

"begin": "(?x)(\\s+?(`)(?:[\\n\\s]*<[a-zA-Z!]))"
WebReflection commented 6 years ago

FWIW, I've landed a PR to relax this parser: https://github.com/mjbvz/vscode-lit-html/pull/21

BBlackwo commented 6 years ago

@WebReflection the code in your PR #21 works great to allow any html template string! Thanks.

"begin": "(?x)(\\s+?(\\w+\\.)?(?:html|raw|render)\\s*)?(`)(?=$|\\s*<[a-zA-Z!])",
WebReflection commented 6 years ago

@BBlackwo watch out that RegExp is wrong. My ideal scenario is to use the followed by but that RegExp assumes any template literal that starts with a new line is HTML, which is not the case.

The latest version in literally-html is better and it's been tested with both hyperHTML and lit-html.

neilrackett commented 6 years ago

This enables syntax highlighting for almost any template literal containing one or more HTML tag, regardless of whether it's single or multi-line:

"begin": "(`)(?=$|[\\s\\S]*<[a-zA-Z][\\s\\S]*>[\\s\\S]*)",

WebReflection commented 6 years ago

@neilrackett I think that has exact same issue with new lines, handling as HTML even regular text

neilrackett commented 6 years ago

Everything we've tried so far works without any problems, with the exception of multi-line template literals that have text on the first line that doesn't contain a tag, e.g.

let foo = `this
    doesn't
    <b>work</b>
`;

but

let foo = `
    but
    this
    <b>works just fine</b>
`;
WebReflection commented 6 years ago

Everything we've tried so far works without any problems

the problem is that you are switching to HTML highlight even regular multiline text ... if that's your cup of tea, good for you.

nicolasparada commented 6 years ago

Please, make this work with innerHTML:

const template = document.createElement('template')
template.innerHTML = `
  <pre>Highlight here</pre>
`

Even GitHub highlights it 🙂

fidelepalouki commented 6 years ago

innerHTML and also outerHTML please :)

nicolasparada commented 6 years ago

I've succeeded to highlight html, but without intellisense. I suppose that typescript-lit-html-plugin just works for tagged template literals.

neilrackett commented 6 years ago

To enable HTML highlighting for all template literals, you could use something like this in lit-html.json:

"begin": "(`)"
daKmoR commented 5 years ago

how about something like this

return /* html */`
    <h2>foo</h2>
  `;

that is at least how it works in es6-string-html

nicolasparada commented 5 years ago

how about something like this

return /* html */`
    <h2>foo</h2>
  `;

that is at least how it works in es6-string-html

Matt has already an extension that does that bierner.comment-tagged-templates. No intellisense tho.

chase-moskal commented 5 years ago

as a workaround, for now, we can fool the highlighter by using a tagged-template function named "html" that actually doesn't do anything at all

const html = (strings, ...values) => {
  let result = ""
  strings.forEach((string, index) => {
    result += string + (values[index] || "");
  })
  return result
}

thus we can

return html`<div awesome>cool</div>`

edit: see later incarnation of template-noop.ts below

WebReflection commented 5 years ago

@chase-moskal you can use i18n-dummy if you need a no-op for template literals.

const html = require('i18n-dummy');

that's it

edit

actually, I've just published dummy-tag for this very unique purpose (since it's more common that I've thought)

iansan5653 commented 5 years ago

how about something like this

return /* html */`
    <h2>foo</h2>
  `;

that is at least how it works in es6-string-html

This is how it works now, so this issue should be closed.

diminutivesloop commented 4 years ago

Some kind of comment tag would be a good solution although supporting custom patterns would be even better. This would enable support for something like inline Angular templates by adding a custom pattern to match against template: `.

falco467 commented 4 years ago

@iansan5653

This is how it works now, so this issue should be closed.

It does not work for me like this - how did you get it to work with a comment? Any more settings required?

iansan5653 commented 4 years ago

@falco467 I'm not sure anymore - I haven't used this in a long time and it doesn't seem to work now. It's possible that I may have been using a different extension; I'm not sure.

chase-moskal commented 4 years ago

@WebReflection, @benfrain, @mjbvz, @nicolasparada, @falco467 — hey all you silly geese!

isn't it obvious enough that a template noop is proper the answer? :rofl:

template-noop.ts

export function noop(strings: TemplateStringsArray, ...keys: any[]) {
    const lastIndex = strings.length - 1
    return strings
        .slice(0, lastIndex)
        .reduce((a, b, c) => a + b + keys[c], "")
            + strings[lastIndex]
}

whatever.ts

import {noop as html} from "./template-noop.js"

document.body.innerHTML = html`
  <h1>tada!</h1>
`

i've been doing this a long while, works like a charm, this should be the official answer

all the comment-based regex hacking madness above is making my tummy grumble!

  :tumbler_glass: /thread

edit: ahahah, i already posted this technique but nobody noticed it, self included... or maybe the way i didn't explain it before was too tricky to get your grippers on XD

WebReflection commented 4 years ago

@chase-moskal that's dummy-tag which I've linked already

chase-moskal commented 4 years ago

i know, but you and i are the only ones who noticed that this issue is resolved and has a good answer :)

it can be closed, but a note in the readme would be good, if there isn't one already

falco467 commented 4 years ago

@chase-moskal I saw your answer and I've been using this Work-Around for a while. But it's a silly function/dependency I have to include and document in every project - and the doc of the function says "this noop dummy function is a necessary Work-Around for a particular vs-code extension, see bug *link to this issue"

I agree this is a workaround, but a real solution would either work with a comment / html / or by recognizing the variable name or even peeking the content of the literal - without needing changes in the code.

An additional noop function is irritating to other developers and a potential source of errors. And a single extension used by some developers on a single IDE should not require code changes in a big project.

chase-moskal commented 4 years ago

@falco467, @diminutivesloop -- noop is more elegant, i no longer consider it a workaround. it's the correct answer, and it's great, and i have a concrete argument for why you should reconsider

it's refactorable. you see, the noop buys us an ability to more cleanly bring in new features without even having to refactor every line we had used the templates. it's a slight thing, but if you keep looking at this from different angles, the conceptual elegance of the noop starts to shine through. stay with me here

it's actually an interesting and desirable pattern, to use noops wherever this principal makes sense. we should use template noop's anywhere we're embedding any structured string information, like little dsl's that we could identify and enhance later. here's a great example

import {noop as csv} from "../toolbox/template-noop.js"

whatever.lol = csv`
  name,value,occupation
  jim,1x,frontend
  chase,10x,fullstack
`

then in the future, we might replace the csv noop with a real implementation which performs validation for the csv format. this is a great pattern because it can make refactoring easy. it's hot-swappable, it's a stand-in. great pattern

and so we have a similar attitude for the html noop. it's a stand-in for whatever future functionality we might want to refactor in. this extension can hitch a ride on this convention to apply highlighting. it's great!

i know you're bummed about an extra module. maybe template noop should have been a new javascript builtin and you'd feel better about it. but let's face it, we always have a little toolbox of tiny functions like these anyways. you certainly can't avoid 'em

honestly friend, gross comment-hacks to influence syntax highlighting? magic variable names? think about it some more through this lens, i really think it's a show-stopper gotchya, and you'll have to reluctantly agree:

and now another important point,

And a single extension used by some developers on a single IDE should not require code changes in a big project.

oh dear, friend, i see you have it exactly backwards.

you see, it's in fact the noop which best disappears into regular-looking javascript syntax. your coworkers are likely to not even notice anything funny about the noop. html is a conventional name for a block of html markup, and the implementation is ready for swapping

on the other hand: comment-hacking or magic variables? now THAT is putting an ugly imposition onto your coworkers. if they aren't using the extension, will honestly say "what the hell is this gross comment?" and likely just delete it. i mean just think about it

// from the perspective of somebody without the extension and no knowledge of it

// you think this is going to fly on a real team?
// it looks identical to one of those ridiculous comments that must be deleted
// it is seriously indistinguishable from a mistake that should be cleaned up

return /* html */ `
  <div></div>
`

// this makes sense. natural template syntax. it's even functionally valuable

return html`
  <div></div>
`

my teams have always had a policy against "silly" comments that don't really explain anything. i don't know about your policy, but these comment hacks are indistinguishable from those silly comments that we regularly seek and destroy

mull it over and make the right choice. fall on the right side of history here, and your coworkers will thank you :tumbler_glass:

chase-moskal commented 4 years ago

i'll also point out that somehow or another, github is syntax highlighting this the correct way. awesome! things are on the right track right now. this is a good convention, and as i explained, using noops actually have legitimate functional qualities for refactorability

slick and cool

const lol = html`<div github="supports">the highligting</div>`

gross and bunk

const lol = /* html */ `<div github="supports">the highligting</div>`

praise the lord :raised_hands:

/thread

WebReflection commented 4 years ago

FWIW, I kinda agree a noop is the solution, and it's not more workaround than a comment that cannot be removed. It's actually declarative, and it works in every situation.

import css from 'dummy-tag';
import html from 'dummy-tag';

const style = css`
  body {
    color: green;
  }
`;

const node = html`
  <div>hello world</div>
`;

The current dummy-tag is 97 bytes minified (but not gzipped), which is so irrelevant, that once gzipped it weights more (107).

The only caveat is Babel targeting IE, which would create some bloat that, however, will not be a big deal once minified and gzipped.

WebReflection commented 4 years ago

P.S. I'm proposing a String.tag function in ES Discuss, but they pointed out that String.raw might be already a good compromise, although it's footgun ish because of the implicit escape.

const html = String.raw;
const css = String.raw;

const style = css`
  body {
    color: green;
  }
`;

const node = html`
  <div>hello world</div>
`;

This requires zero dependencies, except, eventually, a String.raw polyfill.

My main concern about being a footgun, is transpiled code, or parts with \n chars, where these would be part of the output too.

Dare I say const tag = (raw, ...values) => String.raw({raw}, ...values); might be it, but having it native would be nice.

felipecrs commented 3 years ago

This extension could support cu

how about something like this

return /* html */`
    <h2>foo</h2>
  `;

that is at least how it works in es6-string-html

This still would be the best solution for this. The question is, how to make it work for intellisense?

This syntax highlight can be achieved by something like

"begin": "(/\\*html\\*/\\ `)",
ghuser commented 3 years ago

I think this is the most elegant solution for template-noop. Copying it here:

// tricking String.raw into thinking that the escape-interpreted string is the raw version.
function doNothingTag() {
  arguments[0] = { raw: arguments[0] };
  return String.raw(...arguments);
}