Allow any quoted string to be multiline?

# J Decker (6 years ago)

If any string were allowed to be multi-line it would not break any existing code, and it would simplify parsing strings.

'a multi- line string'

Maybe this is a question... why not allow line spanning with single and double quoted strings?

# Isiah Meadows (6 years ago)

There are ES5 newline escapes. However, it'd be nice if I didn't have to resort to templates just for multiline strings. Note: it wouldn't actually simplify parsing by much - either you're handling the case and remapping CR/CRLF to NL, or you're handling the case and throwing an error.

// ES5 escaped newlines
'a multi-\
line string'

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# kdex (6 years ago)

Sorry to be pedantic here, but:

"this\
right\
here"

aren't escaped newlines; that would be "\n". Those up there are line continuations. :)

# kai zhu (6 years ago)

On Friday, December 15, 2017 2:48:00 PM CET Isiah Meadows wrote:

There are ES5 newline escapes. However, it'd be nice if I didn't have to resort to templates just for multiline strings. Note: it wouldn't actually simplify parsing by much - either you're handling the case and remapping CR/CRLF to NL, or you're handling the case and throwing an error.

es5 multiline escapes (with newline) are perfectly adequate. here’s are several real-world examples and screenshots of multi-line templates using es5 escapes

kaizhu256/node-utility2/blob/2017.9.15/lib.utility2.js#L1114, kaizhu256/node-utility2/blob/2017.9.15/lib.utility2.js#L1114

kaizhu256/node-swgg/blob/2017.7.24/lib.swgg.js#L1323, kaizhu256/node-swgg/blob/2017.7.24/lib.swgg.js#L1323

kaizhu256/node-istanbul-lite/blob/2017.5.27/lib.istanbul.js#L1996, kaizhu256/node-istanbul-lite/blob/2017.5.27/lib.istanbul.js#L1996

// init lib istanbul.report.templates.foot
/* jslint-ignore-begin */
...

// init lib istanbul.report.templates.head
// 2014-07-04T07:47:53Z
// https://github.com/gotwarlost/istanbul/blob/v0.2.16/lib/report/templates/head.txt <https://github.com/gotwarlost/istanbul/blob/v0.2.16/lib/report/templates/head.txt>

local['head.txt'] = '\
<!doctype html>\n\
<html lang="en">\n\
<head>\n\
    <title>Code coverage report for {{entity}}</title>\n\
    <meta charset="utf-8">\n\
    <style>\n\
        body, html {\n\
            margin:0; padding: 0;\n\
        }\n\
        body {\n\
            font-family: Arial, Helvetica;\n\
            font-size: 10pt;\n\
        }\n\
        div.header, div.footer {\n\
            background: #eee;\n\
            padding: 1em;\n\
        }\n\
        div.header {\n\
            height: 160px;\n\
            padding: 0 1em 0 1em;\n\
            z-index: 100;\n\
            position: fixed;\n\
            top: 0;\n\
            border-bottom: 1px solid #666;\n\
            width: 100%;\n\
        }\n\
        div.footer {\n\
            border-top: 1px solid #666;\n\
        }\n\
        div.body {\n\
            margin-top: 170px;\n\
        }\n\
        div.meta {\n\
            font-size: 90%;\n\
            text-align: center;\n\
        }\n\
        h1, h2, h3 {\n\
            font-weight: normal;\n\
        }\n\
        h1 {\n\
            font-size: 12pt;\n\
        }\n\
        h2 {\n\
            font-size: 10pt;\n\
        }\n\
        pre {\n\
            font-family: Menlo, Monaco, Consolas, Courier New, monospace;\n\
            margin: 0;\n\
            padding: 0;\n\
            font-size: 14px;\n\
            tab-size: 2;\n\
        }\n\
\n\
        div.path { font-size: 110%; }\n\
        div.path a:link, div.path a:visited { color: #000; }\n\
        table.coverage { border-collapse: collapse; margin:0; padding: 0 }\n\
\n\
        table.coverage td {\n\
            margin: 0;\n\
            padding: 0;\n\
            color: #111;\n\
            vertical-align: top;\n\
        }\n\
        table.coverage td.line-count {\n\
            width: 50px;\n\
            text-align: right;\n\
            padding-right: 5px;\n\
        }\n\
        table.coverage td.line-coverage {\n\
            color: #777 !important;\n\
            text-align: right;\n\
            border-left: 1px solid #666;\n\
            border-right: 1px solid #666;\n\
        }\n\
\n\
        table.coverage td.text {\n\
        }\n\
\n\
        table.coverage td span.cline-any {\n\
            display: inline-block;\n\
            padding: 0 5px;\n\
            width: 40px;\n\
        }\n\
        table.coverage td span.cline-neutral {\n\
            background: #eee;\n\
        }\n\
        table.coverage td span.cline-yes {\n\
            background: #b5d592;\n\
            color: #999;\n\
        }\n\
        table.coverage td span.cline-no {\n\
            background: #fc8c84;\n\
        }\n\
\n\
        .cstat-yes { color: #111; }\n\
        .cstat-no { background: #fc8c84; color: #111; }\n\
        .fstat-no { background: #ffc520; color: #111 !important; }\n\
        .cbranch-no { background:  yellow !important; color: #111; }\n\
\n\
        .cstat-skip { background: #ddd; color: #111; }\n\
        .fstat-skip { background: #ddd; color: #111 !important; }\n\
        .cbranch-skip { background: #ddd !important; color: #111; }\n\
\n\
        .missing-if-branch {\n\
            display: inline-block;\n\
            margin-right: 10px;\n\
            position: relative;\n\
            padding: 0 4px;\n\
            background: black;\n\
            color: yellow;\n\
        }\n\
\n\
        .skip-if-branch {\n\
            display: none;\n\
            margin-right: 10px;\n\
            position: relative;\n\
            padding: 0 4px;\n\
            background: #ccc;\n\
            color: white;\n\
        }\n\
\n\
        .missing-if-branch .typ, .skip-if-branch .typ {\n\
            color: inherit !important;\n\
        }\n\
\n\
        .entity, .metric { font-weight: bold; }\n\
        .metric { display: inline-block; border: 1px solid #333; padding: 0.3em; background: white; }\n\
        .metric small { font-size: 80%; font-weight: normal; color: #666; }\n\
\n\
        div.coverage-summary table { border-collapse: collapse; margin: 3em; font-size: 110%; }\n\
        div.coverage-summary td, div.coverage-summary table  th { margin: 0; padding: 0.25em 1em; border-top: 1px solid #666; border-bottom: 1px solid #666; }\n\
        div.coverage-summary th { text-align: left; border: 1px solid #666; background: #eee; font-weight: normal; }\n\
        div.coverage-summary th.file { border-right: none !important; }\n\
        div.coverage-summary th.pic { border-left: none !important; text-align: right; }\n\
        div.coverage-summary th.pct { border-right: none !important; }\n\
        div.coverage-summary th.abs { border-left: none !important; text-align: right; }\n\
        div.coverage-summary td.pct { text-align: right; border-left: 1px solid #666; }\n\
        div.coverage-summary td.abs { text-align: right; font-size: 90%; color: #444; border-right: 1px solid #666; }\n\
        div.coverage-summary td.file { text-align: right; border-left: 1px solid #666; white-space: nowrap;  }\n\
        div.coverage-summary td.pic { min-width: 120px !important;  }\n\
        div.coverage-summary a:link { color: #000; }\n\
        div.coverage-summary a:visited { color: #333; }\n\
        div.coverage-summary tfoot td { border-top: 1px solid #666; }\n\
\n\
        div.coverage-summary .yui3-datatable-sort-indicator, div.coverage-summary .dummy-sort-indicator {\n\
            height: 10px;\n\
            width: 7px;\n\
            display: inline-block;\n\
            margin-left: 0.5em;\n\
        }\n\
        div.coverage-summary .yui3-datatable-sort-indicator {\n\
            background: no-repeat scroll 0 0 transparent;\n\
        }\n\
        div.coverage-summary .yui3-datatable-sorted .yui3-datatable-sort-indicator {\n\
            background-position: 0 -20px;\n\
        }\n\
        div.coverage-summary .yui3-datatable-sorted-desc .yui3-datatable-sort-indicator {\n\
            background-position: 0 -10px;\n\
        }\n\
\n\
        .high { background: #b5d592 !important; }\n\
        .medium { background: #ffe87c !important; }\n\
        .low { background: #fc8c84 !important; }\n\
\n\
        span.cover-fill, span.cover-empty {\n\
            display:inline-block;\n\
            border:1px solid #444;\n\
            background: white;\n\
            height: 12px;\n\
        }\n\
        span.cover-fill {\n\
            background: #ccc;\n\
            border-right: 1px solid #444;\n\
        }\n\
        span.cover-empty {\n\
            background: white;\n\
            border-left: none;\n\
        }\n\
        span.cover-full {\n\
            border-right: none !important;\n\
        }\n\
        pre.prettyprint {\n\
            border: none !important;\n\
            padding: 0 !important;\n\
            margin: 0 !important;\n\
        }\n\
        .com { color: #999 !important; }\n\
        .ignore-none { color: #999; font-weight: normal; }\n\
\n\
    </style>\n\
</head>\n\
<body>\n\
<div class="header {{reportClass}}">\n\
    <h1 style="font-weight: bold;">\n\
        <a href="{{env.npm_package_homepage}}">{{env.npm_package_name}} (v{{env.npm_package_version}})</a>\n\
    </h1>\n\
    <h1>Code coverage report for <span class="entity">{{entity}}</span></h1>\n\
    <h2>\n\
        {{#with metrics.statements}}\n\
        Statements: <span class="metric">{{pct}}% <small>({{covered}} / {{total}})</small></span>     \n\
        {{/with}}\n\
        {{#with metrics.branches}}\n\
        Branches: <span class="metric">{{pct}}% <small>({{covered}} / {{total}})</small></span>     \n\
        {{/with}}\n\
        {{#with metrics.functions}}\n\
        Functions: <span class="metric">{{pct}}% <small>({{covered}} / {{total}})</small></span>     \n\
        {{/with}}\n\
        {{#with metrics.lines}}\n\
        Lines: <span class="metric">{{pct}}% <small>({{covered}} / {{total}})</small></span>     \n\
        {{/with}}\n\
        Ignored: <span class="metric">{{#show_ignores metrics}}{{/show_ignores}}</span>     \n\
    </h2>\n\
    {{{pathHtml}}}\n\
</div>\n\
<div class="body">\n\
';
/* jslint-ignore-end */

live web-demo of Istanbul coverage-report rendered in browser from es5 multiline templates kaizhu256.github.io/node-istanbul-lite/build..beta..travis-ci.org/app, kaizhu256.github.io/node-istanbul-lite/build..beta..travis-ci.org/app

various html input-elements rendered from es5 multiline escape templates @ kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/app/#!swgg_id_x_test_parametersDefault_1, kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/app/#!swgg_id_x_test_parametersDefault_1

# Isiah Meadows (6 years ago)

I'm aware. I typically use "newline" and "line continuation" interchangeably, even though they technically aren't. (So feel free to substitute "line continuation" for "newline", and that should be accurate to what I meant. ;-))

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# kdex (6 years ago)

IIRC, Douglas Crockford used to give the following example to illustrate why multi-line strings without template literals are cumbersome and error-prone:

One of these snippets is okay, the other one won't even compile. Can you see which is which?

Snippet 1:

const x = "foo\
bar";

Snippet 2:

const x = "foo\ 
bar";

If you haven't already guessed it, snippet 2 contains whitespace after the backslash invalidating the line continuation and hence making the string literal invalid. That's an invisible syntax error.

Significant whitespace has never been the most popular idea in programming, and trailing whitespace is particularly problematic, as most editors don't even bother to show whitespace characters by default.

There is more than one way to treat the following case:

const x = "foo
bar";

What should x contain? Should it be "foobar" (implicit line continuation) or "foo\nbar" (implicit EOL)?

@Kai Zhu: As has been done in the past, I, too, would like to kindly ask if you could please refrain from putting gigantic walls of code into your e- mails. Your last e-mail contained 225 (!) lines of escaped HTML and two screenshots that appeared to be unrelated to this discussion. Would it not have been enough to give one instance (as opposed to 225) of a "real-world example" for escaping, and to spare us the attachments?

In the future, if there really is the need to show longer code, I would like to ask if you (and everybody else) could instead use a gist for any example that exceeds ≈20 LoC. Making a gist [1] on GitHub is really simple and introduces far less noise on this list. You don't even need an account. Thanks!

[1] gist.github.com

# kai zhu (6 years ago)

inline

If you haven't already guessed it, snippet 2 contains whitespace after the backslash invalidating the line continuation and hence making the string literal invalid. That's an invisible syntax error.

there are 2 simple solutions to that:

a) configure linter to complain about trailing whitespaces (which it should anyways).

b) configure editor/ide to auto-remove trailing whitespace like this real-world vim one-liner @ kaizhu256/node-utility2/blob/2017.9.15/.vimrc#L6, kaizhu256/node-utility2/blob/2017.9.15/.vimrc#L6

"auto remove trailing whitespace"
autocmd BufRead,BufWrite * if ! &bin | silent! %s/\s\+$//e | endif

not every problem in javascript demands a tc39 language-proposal-hammer. use some common-sense.

@Kai Zhu: As has been done in the past, I, too, would like to kindly ask if you could please refrain from putting gigantic walls of code into your e- mails. Your last e-mail contained 225 (!) lines of escaped HTML and two screenshots that appeared to be unrelated to this discussion. Would it not have been enough to give one instance (as opposed to 225) of a "real-world example" for escaping, and to spare us the attachments?

it may not be relevant to some who can afford to wait a few years for tc39 to come up with something. but it is relevant to engineers and pm’s with hard contract deadlines, and need practical solutions now with credibility they’ve been used in production (and not just some random gist).

# kdex (6 years ago)

not every problem in javascript demands a tc39 language-proposal-hammer. use

some common-sense.

First and foremost, you should change your tone. There is no need for condescending comments. This list is intended to discuss the language and explore how we can further improve it; ideas are always welcome, and their cost, feasibility and usability are constructively discussed.

Implying the lack of common sense is highly disgraceful and shows that you're not willing to have a meaningful discussion with anyone on this list. If all you intend to do on this list is reply "-1" to everyone, make patronizing comments, and flood everyone's inbox with images, I don't think anyone's interested in what you have to say. If you can't be civil and constructive, you are not welcome here.

there are 2 simple solutions to that:

a) configure linter to complain about trailing whitespaces (which it should

anyways).

b) configure editor/ide to auto-remove trailing whitespace

Configuring a third-party software to rewrite your code just so that it doesn't cause syntax errors is not a solution, but an unsafe hack, which should remind you of the path that Java's bloated IDEs went down. I would agree that linters are nice to have, but this isn't addressing the problem at hand. The problem at hand can be found in the "Subject" header of this e-mail.

Further, there are some valid cases where you may need trailing whitespace at the end of a line (within a template string, this could be necessary depending on what you're trying to achieve). I'm not saying that I like or advocate trailing whitespace; but instructing some third-party software to remove it, even in places where it might make a difference, is the equivalent of closing your eyes and saying, "I can't see a problem here; everything is fine".

it may not be relevant to some who can afford to wait a few years for tc39

to come up with something. but it is relevant to engineers and pm’s with hard contract deadlines, and need practical solutions now with credibility they’ve been used in production (and not just some random gist).

What benefit would an engineer or project manager have from a screenshot showing off some random, off-topic software? Or 225 lines of code where a single line could have been enough to get the idea? This discussion was supposed to be about syntax, not about your software; and we'd like to keep it that way.

# Isiah Meadows (6 years ago)

I'll note that escaped line terminators (which I brought up initially) have been around since ES5 (which I already mentioned then). I've seen it used before in the wild in longer strings to help break up long lines. I can't recall exact projects, but I've seen it in OSS projects in ES5.

Other common alternatives include [...].join("\n") (which I normally use) and concatenation.

# kdex (6 years ago)

Another place where this is used particularly often is when dynamically constructing the source for WebGL shaders in the same <script> tag as the

one where the shaders are compiled.

# J Decker (6 years ago)

I do see there are work-arounds (similar as using a hammer to put in a screw)

I don't see a reason for why not allow multiline strings for other quote types. I know... because people want more errors and to have their hand held to identify missing close quotes? much like the desire to add types and type checking.

# Claude Pache (6 years ago)

Le 17 déc. 2017 à 22:03, J Decker <d3ck0r at gmail.com> a écrit :

I do see there are work-arounds (similar as using a hammer to put in a screw)

I don't see a reason for why not allow multiline strings for other quote types. I know... because people want more errors and to have their hand held to identify missing close quotes? much like the desire to add types and type checking.

Yes, you gave a valid reason just after having said you didn’t see a reason: transforming bugs (missing quote) into early errors, so that it is easier to debug...

The obvious workaround, i.e. using a template literal without placeholder, doesn’t seems too heavy-weight; on the contrary, it is probably the lightest one can imagine.

# J Decker (6 years ago)

On Mon, Dec 18, 2017 at 3:08 AM, Claude Pache <claude.pache at gmail.com>

wrote:

Le 17 déc. 2017 à 22:03, J Decker <d3ck0r at gmail.com> a écrit :

I do see there are work-arounds (similar as using a hammer to put in a screw)

I don't see a reason for why not allow multiline strings for other quote types. I know... because people want more errors and to have their hand held to identify missing close quotes? much like the desire to add types and type checking.

Yes, you gave a valid reason just after having said you didn’t see a reason: transforming bugs (missing quote) into early errors, so that it is easier to debug...

The obvious workaround, i.e. using a template literal without placeholder, doesn’t seems too heavy-weight; on the contrary, it is probably the lightest one can imagine.

It's not actually :) lightest I can imagine is to allow single and double quoted strings to be multiline also. I was considering this because of an issue on my extended json parser. during development it simplified the code making a single path for gathering a string that only had to check for closing quote or backslash. having to test for 4 more characters was then 3x the work... so I just cut out the extra work, since it break anything. The only thing left was ... that it didn't break anything if a newline was found.

So I figured I'd mention the possibility here and see how much push back I got. I don't actually think that 'protection from bad coding' is a very strong argument. there's lots of single character omissions or insertions that can cause ghosted errors that aren't nearly so obvious. Especially since syntax highlighting would show a overflowing string very quickly (more than missing a + in += or having a + on an = that you didn't mean for instance). It would be a change that would take some time to propagate to a lot of tools; but is a change that's entirely backward compatible and breaks nothing from the past.

# Alexander Jones (6 years ago)

I question why 3 forms of quotation exist in modern codebases. Just use template literals. The only reason not to is resistance to change. ESLint will autofix for you.

# Isiah Meadows (6 years ago)

I'll note that making such an adjustment to JSON itself would interfere with many newline-delimited JSON stream parsers, particularly those who look for the newline before parsing the segment itself. (Most JS-based parsers do that, since it's easier and faster to pass a slice to JSON.parse than it is to parse it incrementally.)


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Naveen Chawla (6 years ago)

"Protection from bad coding" is about the strongest argument for a language feature as there could be. Even features that allow doing "more for less" have the ultimate advantage of reducing the chances of coding mistakes. I'd say it's the basis of every modern programming language and framework development out there. I'd be interested to know of any counter-examples if you disagree.

(small example: arrow functions referencing the outer this: avoids mistakes caused by possible different nested thises. I can go through every feature to show how it reducing the chances of programming mistakes, but it would take too long)

# Michael Rosefield (6 years ago)

There can absolutely be breaking changes from doing that. Take this, for example:

const someObj = { 'some.key': 'some value' };

That throws an error if using template literals, as you need to use computed property syntax (i.e., enclose the template literal in square brackets).

[image: image.png]

# Alexander Jones (6 years ago)

Good point, but generally quoted keys like that are signs that you probably really need a Map!

Aside, seems like it could be a fairly cheap spec change to allow template literals for string property keys.

# Andrey Muzalevsky (6 years ago)

In general

This change looks good, in my eyes. From the other side it doesn't seem like must-have feature, as template literals are covering 99.999...% cases with multiline strings


Yes, you gave a valid reason just after having said you didn’t see a reason: transforming bugs (missing quote) into early errors, so that it is easier to debug...

Seems like this is related to editors without syntax-highlighting only... everybody(I hope) use editors with syntax highlighting today


const someObj = { 'some.key': 'some value' }; That throws an error if using template literals, as you need to use

computed property syntax

(i.e., enclose the template literal in square brackets).

Do you really need new lines in property names?