【问题标题】:Multiline strings that don't break indentation不破坏缩进的多行字符串
【发布时间】:2014-11-13 11:11:39
【问题描述】:

根据this esdiscuss discussion,可以在ECMAScript 6 中定义多行字符串,而不必将字符串的后续行放在行首。

Allen Wirfs-Brock’s post 包含一个代码示例:

var a = dontIndent
        `This is a template string.
         Even though each line is indented to keep the
         code neat and tidy, the white space used to indent
         is not in the resulting string`;

有人能解释一下这是如何实现的吗?如何定义这个dontIndent 东西以删除用于缩进的空格?

【问题讨论】:

标签: javascript ecmascript-6 template-literals


【解决方案1】:

这个特性是通过定义一个自定义函数,然后将其用作标签来实现的(上面的dontIndent)。代码打击来自Zenparsing's gist

function dedent(callSite, ...args) {

    function format(str) {

        let size = -1;

        return str.replace(/\n(\s+)/g, (m, m1) => {

            if (size < 0)
                size = m1.replace(/\t/g, "    ").length;

            return "\n" + m1.slice(Math.min(m1.length, size));
        });
    }

    if (typeof callSite === "string")
        return format(callSite);

    if (typeof callSite === "function")
        return (...args) => format(callSite(...args));

    let output = callSite
        .slice(0, args.length + 1)
        .map((text, i) => (i === 0 ? "" : args[i - 1]) + text)
        .join("");

    return format(output);
}

我已经在 Firefox Nightly 中成功测试了它:

【讨论】:

  • 如果这对某些人来说似乎令人困惑,您基本上可以通过编写其名称后跟 `` (Tagged templates) 来调用 js 函数。然后该函数可以访问模板文字的内容。
  • 请注意,此函数仅去除第一行带有缩进的缩进量。如果一行在第一行之后包含更多缩进,则保留该缩进。
  • 这个解决方案似乎有空行问题
  • @peter.bartos I created a Babel plugin 不需要在模板文字之前放置任何标签(函数名),保留换行符但在编译时去掉前导空格。它也保留空行。也许它会更好地满足您的需求? More details are on my answer.
【解决方案2】:

2020 年回答:尽管TC39 has discussed adding a new template literal that handles indentation,但 JS 标准库中仍然没有内置任何内容来处理去凹痕。您目前有 2 个选择:

  1. endentdedent-js 包将处理此问题。请注意,dedent-js 包实际上适用于制表符和空格,dedent 是一个单独的包,在制表符上失败:
    var dedent = require('dedent-js');
    var text = dedent(`
      <div>
        <span>OK</span>
        <div>
          <div></div>
        </div>
      </div>
    `);

将删除每行的后续空格和前导回车符。它还拥有更多用户、问题跟踪器,并且比从 Stack Overflow 复制粘贴更容易更新!

  1. 不要缩进长行,而是使用将长行显示为缩进的编辑器。例如,vsCode - 您可以简单地使用长行,而不是缩进任何内容,并在长字符串中包含回车。 vsCode 将显示它们缩进。下面的字符串没有缩进 - 第二行 The empty export... 紧跟在回车之后,但显示为缩进。

【讨论】:

  • 赏金即将到期。这个答案有更新吗?
  • @Pureferret 唉,这仍然不在标准库中。我怀疑 TC39 没有 dedent 的原因(查看多行字符串的起始缩进是因为这样做(如 dedent)是编译时和运行时功能的混合。但是我将添加两个上述答案的其他解决方案,
  • 感谢您添加这些内容。我确实在 TC39 上看到了一篇关于正版的帖子:es.discourse.group/t/… 以及 endent 包。如果您想将此添加到您的答案中,请告诉我。
  • 对于第二个选项,您需要在设置中添加"editor.wordWrap": "on"。我知道这很明显,但设置的名称可以帮助一些希望启用它的人
  • @mikemaccana 这可以完全在编译时完成。 I created a Babel plugin 不需要在模板文字之前放置任何标记(函数名称),保留换行符但在编译时去掉前导空格。它也保留空行。也许它会更好地满足您的需求? More details are on my answer.
【解决方案3】:

您也可以只对双空格进行字符串替换(假设您的缩进使用空格,而不是制表符)。显然,实际字符串中的任何双空格都会被删除,但在大多数情况下,这应该没问题。

const MSG = (`Line 1
          line 2
          line 3`).replace(/  +/g, '');
// outputs
/*
Line 1
line 2
line 3
*/

【讨论】:

  • 赏金即将到期。这个答案有更新吗?
【解决方案4】:

正如Šime Vidas 所说,函数可以用作标记,只需将其放在模板字符串前面即可调用。

存在许多 NPM 模块来执行此操作,并且将涵盖许多您自己难以涵盖的边缘情况。主要的两个是:

dedent,每周下载 400 万次,上次更新时间为 4 年前

endent,每周下载 250 次,4 个月前更新

【讨论】:

    【解决方案5】:

    如何定义这个dontIndent 东西以删除用于缩进的空格?

    我想这样的事情对于许多情况(包括 OP)应该就足够了:

    function dontIndent(str){
      return ('' + str).replace(/(\n)\s+/g, '$1');
    }
    

    这个sn-p中的演示代码:

    var a = dontIndent
            `This is a template string.
             Even though each line is indented to keep the
             code neat and tidy, the white space used to indent
             is not in the resulting string`;
    
    console.log(a);
             
    function dontIndent(str){
      return ('' + str).replace(/(\n)\s+/g, '$1');
    }

    说明

    JavaScript template literals 可以用一个标签来调用,在这个例子中是dontIndent。标签被定义为函数,并以模板文字作为参数调用,因此我们定义了一个dontIndent() 函数。模板文字作为数组中的参数传递,因此我们使用表达式('' + str) 将数组内容转换为字符串。然后,我们可以使用像/(\n)\s+/g.replace()这样的正则表达式,所有出现的换行符后跟空白,只有换行符来达到OP的目的。

    【讨论】:

    • 如果我仍然想要除缩进之外的换行符,我该如何更改您的正则表达式?
    • 这正是正则表达式所做的。它保留换行符并丢弃缩进。如果把代码console.log(a);改成console.log(JSON.stringify(a));,可以更清楚的看到代码输出的内容。
    • 但我刚刚尝试了您的代码,它也删除了所有换行符。我想用段落撰写电子邮件内容。我需要在段落之间留一个空行以使其更具可读性。
    • @AntonioOoi 如果要保留空行,请使用 [^\S\r\n] 代替 \s
    • @AntonioOoi 这可以完全在编译时完成。 I created a Babel plugin 不需要在模板文字之前放置任何标记(函数名称),保留换行符但在编译时去掉前导空格。它也保留空行。也许它会更好地满足您的需求? More details are on my answer.
    【解决方案6】:

    所有现有答案的问题在于,它们是运行时解决方案。也就是说,他们采用多行模板文字并在程序执行时通过函数运行它,以消除前导空格。这是这样做的“错误方式”,因为这个操作应该在编译时完成。原因是,这个操作不需要运行时信息,所有需要的信息在编译时都是已知的。

    为了在编译时完成,我写了一个Babel plugin named Dedent Template Literals。基本上它的工作原理如下:

    const httpRFC = `                Hypertext Transfer Protocol -- HTTP/1.1
    
                     Status of this Memo
    
                        This document specifies an Internet standards track protocol for the
                        Internet community, and requests discussion and suggestions for
                        improvements.  Please refer to the current edition of the "Internet
                        Official Protocol Standards" (STD 1) for the standardization state
                        and status of this protocol.  Distribution of this memo is unlimited.
    
                     Copyright Notice
    
                        Copyright (C) The Internet Society (1999).  All Rights Reserved.`;
    
    console.log(httpRFC);
    

    将打印:

                    Hypertext Transfer Protocol -- HTTP/1.1
    
    Status of this Memo
    
       This document specifies an Internet standards track protocol for the
       Internet community, and requests discussion and suggestions for
       improvements.  Please refer to the current edition of the "Internet
       Official Protocol Standards" (STD 1) for the standardization state
       and status of this protocol.  Distribution of this memo is unlimited.
    
    Copyright Notice
    
       Copyright (C) The Internet Society (1999).  All Rights Reserved.
    

    插值也没有任何问题。此外,如果您在模板文字的开始反引号之后的第一列之前开始一行,插件将抛出错误,显示错误的位置。如果以下文件在您的项目下的src/httpRFC.js

    const httpRFC = `                Hypertext Transfer Protocol -- HTTP/1.1
    
                     Status of this Memo
    
                        This document specifies an Internet standards track protocol for the
                        Internet community, and requests discussion and suggestions for
                        improvements.  Please refer to the current edition of the "Internet
                        Official Protocol Standards" (STD 1) for the standardization state
                        and status of this protocol.  Distribution of this memo is unlimited.
    
                    Copyright Notice
    
                        Copyright (C) The Internet Society (1999).  All Rights Reserved.`;
    
    console.log(httpRFC);
    

    转译时会出现如下错误:

    Error: <path to your project>/src/httpRFC.js: LINE: 11, COLUMN: 17. Line must start at least at column 18.
        at PluginPass.dedentTemplateLiteral (<path to your project>/node_modules/babel-plugin-dedent-template-literals/index.js:39:15)
        at newFn (<path to your project>/node_modules/@babel/traverse/lib/visitors.js:175:21)
        at NodePath._call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:55:20)
        at NodePath.call (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:42:17)
        at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:92:31)
        at TraversalContext.visitQueue (<path to your project>/node_modules/@babel/traverse/lib/context.js:116:16)
        at TraversalContext.visitSingle (<path to your project>/node_modules/@babel/traverse/lib/context.js:85:19)
        at TraversalContext.visit (<path to your project>/node_modules/@babel/traverse/lib/context.js:144:19)
        at Function.traverse.node (<path to your project>/node_modules/@babel/traverse/lib/index.js:82:17)
        at NodePath.visit (<path to your project>/node_modules/@babel/traverse/lib/path/context.js:99:18) {
      code: 'BABEL_TRANSFORM_ERROR'
    }
    

    如果您使用制表符(且仅制表符)进行缩进并使用空格(且仅空格)进行对齐,它也适用于制表符。

    可以通过运行npm install --save-dev babel-plugin-dedent-template-literals 来安装它,并将dedent-template-literals 作为plugins 数组的第一个元素放在Babel configuration 中使用。 Further information can be found on the README.

    【讨论】:

      猜你喜欢
      • 2011-10-08
      • 1970-01-01
      • 1970-01-01
      • 2016-02-05
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多