Поделиться через


Необработанный строковый литерал

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификаций, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих собраниях по проектированию языка (LDM).

Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .

Сводка

Разрешить новую форму строкового литерала, который начинается с как минимум трех символов """ (без ограничения сверху), после которых опционально следует new_line, затем содержимое строки и заканчивается таким же числом кавычек, с каким начинался литерал. Например:

var xml = """
          <element attr="content"/>
          """;

Поскольку само вложенное содержимое может захотеть использовать """, начальные и конечные разделители могут быть более длинными.

var xml = """"
          Ok to use """ here
          """";

Чтобы упростить чтение текста и разрешить отступы, которые нравятся разработчикам в коде, эти строковые литералы будут естественно удалять отступы, указанные в последней строке, при создании окончательного значения литерала. Например, литерал следующей формы:

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
          """;

Будет содержать следующие элементы.

<element attr="content">
  <body>
  </body>
</element>

Это позволяет коду выглядеть естественно, создавая нужные литералы и избегая затрат во время выполнения, если это требует использования специализированных процедур обработки строк.

Если поведение отступа не требуется, его легко отключить следующим образом.

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
""";

Также поддерживается форма в одну строку. Она начинается с не менее чем трех символов """ (максимум не ограничен), содержимого строки (которая не может содержать какие-либо символы new_line), а затем заканчивается тем же количеством кавычек, с которых начался литерал. Например:

var xml = """<summary><element attr="content"/></summary>""";

Интерполированные необработанные строки также поддерживаются. В этом случае строка указывает количество фигурных скобок, необходимых для начала интерполяции (определяется числом знаков доллара, присутствующих в начале литерала). Любая последовательность фигурных скобок с меньшим количеством скобок, чем это, рассматривается как содержимое. Например:

var json = $$"""
             {
                "summary": "text",
                "length" : {{value.Length}},
             };
             """;

Мотивация

C# не имеет общего способа создания простых строковых литералов, которые могут содержать фактически любой произвольный текст. Все строковые литеральные формы C# сегодня нуждаются в некоторой форме экранирования, если содержимое использует некоторый специальный символ (всегда если используется разделитель). Это предотвращает лёгкое создание литералов, содержащих другие языки (например, литерал XML, HTML или JSON).

Все текущие подходы к формированию этих литералов в C# сегодня всегда вынуждают пользователя вручную экранировать содержимое. Редактирование на данном этапе может быть очень раздражающим, так как экраннирование невозможно избежать и с этим нужно справляться всякий раз, когда оно возникает внутри содержимого. Это особенно больно для регулярных выражений, особенно если они содержат кавычки или обратные слэши. Даже с дословной строкой (@"") кавычки должны быть экранированы, что приводит к чередованию C# и вкрапления regex. { и } являются одинаково раздражающими в интерполированных ($"") строках.

Проблема заключается в том, что все наши строки имеют фиксированный разделитель начала и конца. До тех пор, пока это так, нам всегда придется иметь механизм экранирования, так как содержимое строки может потребовать указания конечного разделителя в себя. Это особенно проблематично, так как этот разделитель " чрезвычайно распространен во многих языках.

Для решения этой проблемы это предложение позволяет использовать гибкие начальные и конечные разделители, так, чтобы они не конфликтовали с содержимым строки.

Цели

  1. Предоставьте механизм, который позволит все строковые значения, предоставляемые пользователем без необходимости каких-либо escape-последовательностей. Так как все строки должны представляться без escape-последовательностей, пользователю всегда необходимо указать разделители, которые гарантированно не будут сталкиваться с любым текстовым содержимым.
  2. Поддерживайте интерполяции таким же образом. Как описано выше, так как все строки должны представляться без экранирования, пользователь всегда должен иметь возможность указать interpolation разделитель, который гарантированно не будет пересекаться с любым текстовым содержимым. Важно отметить, что языки, использующие наши символы-разделители для интерполяции ({ и }), должны быть высокого качества и удобными в использовании.
  3. Многострочные строковые литералы должны выглядеть аккуратно в коде и не должны искажать отступы в блоке кода. Важно отметить, что литеральные значения, которые сами по себе не имеют отступов, не должны быть принудительно размещены в первом столбце файла, так как это может нарушить поток кода и будет выглядеть несоответствующим остальному коду вокруг.
    • Это поведение должно быть легко изменяемым, чтобы литералы оставались ясными и простыми для чтения.
  4. Для всех строк, которые не содержат сам new_line, или не начинаются и не заканчиваются символом кавычки ("), можно представить сам строковый литерал в одной строке.
    • При необходимости, с дополнительной сложностью мы могли бы уточнить это, чтобы сказать, что для всех строк, которые сами не содержат new_line (но могут начинаться или заканчиваться символом кавычки "), должно быть возможно представить саму строку в одной строке. Дополнительные сведения см. в расширенном предложении в разделе Drawbacks.

Подробное проектирование (случай без интерполяции)

Мы добавим новое string_literal производство в следующем формате:

string_literal
    : regular_string_literal
    | verbatim_string_literal
    | raw_string_literal
    ;

raw_string_literal
    : single_line_raw_string_literal
    | multi_line_raw_string_literal
    ;

raw_string_literal_delimiter
    : """
    | """"
    | """""
    | etc.
    ;

raw_content
    : not_new_line+
    ;

single_line_raw_string_literal
    : raw_string_literal_delimiter raw_content raw_string_literal_delimiter
    ;

multi_line_raw_string_literal
    : raw_string_literal_delimiter whitespace* new_line (raw_content | new_line)* new_line whitespace* raw_string_literal_delimiter
    ;

not_new_line
    : <any unicode character that is not new_line>
    ;

Конечный разделитель raw_string_literal должен соответствовать начальному разделителю. Поэтому если начальный разделитель """"" конечный разделитель должен быть таким же.

Приведенная выше грамматика для raw_string_literal должна быть интерпретирована следующим образом:

  1. Он начинается по крайней мере с трех кавычек (но нет ограничения на количество кавычек).
  2. Затем он продолжается с содержимым в той же строке, что и начальные кавычки. Это содержимое в той же строке может быть пустым или непустым. "пустой" является синонимом "полностью пробела".
  3. Если содержимое той же строки не является пустым, то дальнейшее содержимое не может следовать. Другими словами, литерал должен заканчиваться тем же количеством кавычек в той же строке.
  4. Если содержимое той же строки пусто, то литерал может продолжаться с new_line и ряд последующих строк содержимого и new_lines.
    • Строка содержимого — это любой текст, кроме new_line.
    • Затем он заканчивается new_line некоторым числом (возможно, ноль) whitespace и таким же количеством кавычек, с какого начался литерал.

Необработанное строковое литеральное значение

Части между начальной и конечной raw_string_literal_delimiter используются для формирования значения raw_string_literal следующим образом:

  • В случае с single_line_raw_string_literal значение литерала будет именно тем содержимым, что находится между начальным и конечным raw_string_literal_delimiter.
  • В случае multi_line_raw_string_literal начальный whitespace* new_line и конечный new_line whitespace* не являются частью значения строки. Однако последняя часть whitespace* перед конечным символом raw_string_literal_delimiter считается "пробелом отступа" и повлияет на то, как будут интерпретироваться другие строки.
  • Чтобы получить окончательное значение, проходит последовательность (raw_content | new_line)* и выполняется следующее:
    • Если оно new_line содержимое new_line добавляется в окончательное строковое значение.
    • Если он не является пустым raw_content (т. е. not_new_line+ содержит символ, отличный отwhitespace):
      • Пробелы в отступах должны быть префиксом raw_content. В противном случае это ошибка.
      • Пробелы, используемые для отступов, удаляются с начала raw_content, а оставшаяся часть добавляется в итоговое строковое значение.
    • Если это "пустой" raw_content (т. е. not_new_line+ полностью whitespace):
      • Пробелы в отступах должны быть префиксом raw_content или raw_content должны быть префиксом пробела отступа. В противном случае это ошибка.
      • так как большая часть пробела отступа отрезается от начала raw_content, а остальная часть добавляется в окончательное строковое значение.

Разъяснения:

  1. single_line_raw_string_literal не может представлять строку со значением new_line в нем. single_line_raw_string_literal не участвует в удалении пробелов отступов. Его значение всегда совпадает с точными символами между начинающими и завершающими разделителями.

  2. Так как multi_line_raw_string_literal игнорирует последний new_line последней строки содержимого, следующая строка представляет собой строку без начальной new_line и без завершающего new_line

var v1 = """
         This is the entire content of the string.
         """;

Это поддерживает симметрию с тем, как начальный new_line игнорируется, и также предоставляет универсальный способ всегда настраивать отступ пробелов. Чтобы представить строку с терминалом new_line необходимо указать дополнительную строку следующим образом:

var v1 = """
         This string ends with a new line.

         """;
  1. single_line_raw_string_literal не может представлять строковое значение, которое начинается или заканчивается кавычками ("), хотя расширение этого предложения представлено в разделе Drawbacks, где показано, как это может поддерживаться.

  2. multi_line_raw_string_literal начинается с whitespace* new_line после первоначального raw_string_literal_delimiter. Это содержимое после того, как разделитель полностью игнорируется и не используется каким-либо образом при определении значения строки. Это позволяет механизму указать raw_string_literal, содержимое которого начинается с самого " символа. Например:

var v1 = """
         "The content of this string starts with a quote
         """;
  1. raw_string_literal также может представлять содержимое, заканчивающееся цитатой ("). Это поддерживается, так как завершающий разделитель должен находиться в собственной строке. Например:
var v1 = """
         "The content of this string starts and ends with a quote"
         """;
var v1 = """
         ""The content of this string starts and ends with two quotes""
         """;
  1. Требование, что 'пустой' raw_content должен быть либо префиксом 'пробелов отступа', либо сами 'пробелы отступа' должны быть его префиксом, помогает гарантировать, что не возникнет запутанных сценариев со смешанными пробелами, особенно потому, что было бы непонятно, что должно случиться с этой строкой. Например, следующий случай является незаконным:
var v1 = """
         Start
<tab>
         End
         """;
  1. Здесь "отступ пробела" составляет девять пробелов, но raw_content "пустой" не начинается с префикса этого. Нет четкого ответа на вопрос, каким образом следует обращаться с этой линией <tab>. Следует ли игнорировать его? Должно ли это быть так же, как .........<tab>? Объявление его незаконным является наиболее ясным способом предотвращения путаницы.

  2. Следующие случаи являются законными, хотя и представляют ту же строку:

var v1 = """
         Start
<four spaces>
         End
         """;
var v1 = """
         Start
<nine spaces>
         End
         """;

В обоих случаях пробелы отступа будут состоять из девяти пробелов. И в обоих случаях мы удалим как можно больше этого префикса, чтобы в каждом случае raw_content оказался пустым (за исключением каждого new_line). Это позволяет пользователям не видеть и не беспокоиться о пробелах в этих строках при их копировании и вставке или редактировании.

  1. Однако в таком случае:
var v1 = """
         Start
<ten spaces>
         End
         """;

Пробелы отступа по-прежнему будут девятью пробелами. Однако мы удалим столько пробелов в отступах, сколько это возможно, и raw_content "пустой" будет вносить один пробел в окончательное содержимое. Это позволяет предусмотреть случаи, когда в содержимом нужны пробелы на этих строках, которые следует сохранить.

  1. Следующее технически не является законным:
var v1 = """
         """;

Это связано с тем, что начало необработанной строки должно иметь new_line (что оно имеет), но конец должен иметь new_line (чего оно не имеет). Минимальный юридический raw_string_literal:

var v1 = """

         """;

Однако эта строка считается неинтересной, так как она эквивалентна "".

Примеры отступов

Алгоритм «отступа и пробелов» можно визуализировать на нескольких входных данных следующим образом. В следующих примерах используется символ вертикальной полосы | для иллюстрации первого столбца в результирующей необработанной строке:

Пример 1. Стандартный вариант

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
          """;

интерпретируется как

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |</element>
           """;

Пример 2. Конечный разделитель в той же строке, что и содержимое.

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>""";

Это незаконно. Последняя строка содержимого должна заканчиваться new_line.

Пример 3 - Конечный разделитель перед начальным разделителем

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
""";

интерпретируется как

var xml = """
|          <element attr="content">
|            <body>
|            </body>
|          </element>
""";

Пример 4 - Конечный разделитель после начального разделителя

var xml = """
          <element attr="content">
            <body>
            </body>
          </element>
              """;

Это незаконно. Строки содержимого должны начинаться с "пробела отступа"

Пример 5. Пустая пустая строка

var xml = """
          <element attr="content">
            <body>
            </body>

          </element>
          """;

интерпретируется как

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |
          |</element>
           """;

Пример 6. Пустая строка с меньшим пробелом, чем префикс (точки представляют пробелы)

var xml = """
          <element attr="content">
            <body>
            </body>
....
          </element>
          """;

интерпретируется как

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |
          |</element>
           """;

Пример 7. Пустая строка с большим пространством пробелов, чем префикс (точки представляют пробелы)

var xml = """
          <element attr="content">
            <body>
            </body>
..............
          </element>
          """;

интерпретируется как

var xml = """
          |<element attr="content">
          |  <body>
          |  </body>
          |....
          |</element>
           """;

Подробное проектирование (случай интерполяции)

Интерполяции в обычных интерполированных строках (например, $"...") поддерживаются сегодня с помощью символа { для запуска interpolation и использования escape-последовательности {{ для вставки фактического открытого символа фигурной скобки. Использование этого же механизма будет нарушать цели "1" и "2" этого предложения. Для языков, имеющих { в качестве основного символа (например, JavaScript, JSON, Regex и даже встроенный C#), теперь потребуется экранирование, что сводит на нет цель использования необработанных строковых литералов.

Для поддержки интерполяций мы представляем их по-другому, чем обычные $" интерполированные строки. В частности, interpolated_raw_string_literal начинается с некоторого количества символов $. Количество этих символов указывает, сколько символов {}) необходимо в содержимом литерального значения, чтобы разграничивать interpolation. Важно отметить, что способ выхода из фигурных скобок по-прежнему отсутствует. Точно так же, как и с кавычками (") сам литерал всегда может гарантировать, что он указывает разделители для интерполяций, которые не могут пересекаться с остальной частью содержимого строки. Например, можно записать литерал JSON, содержащий отверстия интерполяции, как показано ниже.

var v1 = $$"""
         {
            "orders": 
            [
                { "number": {{order_number}} }
            ]
         }
         """

Здесь {{...}} соответствует требуемому количеству двух фигурных скобок, заданных префиксом $$ разделителя. В случае одного $ это означает, что интерполяция указывается так же, как {...}, как в обычных интерполированных строковых литералах. Важно отметить, что интерполированный литерал с N$ символами может иметь последовательность 2*N-1 фигурных скобок (одного типа в строке). Последние N фигурные скобки начнут (или завершат) процесс интерполяции, а остальные N-1 фигурные скобки будут просто содержимым. Например:

var v1 = $$"""X{{{1+1}}}Z""";

В этом случае внутренние два {{ и }} фигурные скобки принадлежат интерполяции, а внешние одиночные фигурные скобки являются содержимым. Таким образом, указанная выше строка эквивалентна содержимому X{2}Z. Наличие 2*N (или более) фигурных скобок всегда является ошибкой. Чтобы иметь длинную последовательность фигурных скобок в качестве содержимого, необходимо соответствующим образом увеличить количество символов $.

Интерполированные необработанные строковые литералы определяются следующим образом:

interpolated_raw_string_literal
    : single_line_interpolated_raw_string_literal
    | multi_line_interpolated_raw_string_literal
    ;

interpolated_raw_string_start
    : $
    | $$
    | $$$
    | etc.
    ;

interpolated_raw_string_literal_delimiter
    : interpolated_raw_string_start raw_string_literal_delimiter
    ;

single_line_interpolated_raw_string_literal
    : interpolated_raw_string_literal_delimiter interpolated_raw_content raw_string_literal_delimiter
    ;

multi_line_interpolated_raw_string_literal
    : interpolated_raw_string_literal_delimiter whitespace* new_line (interpolated_raw_content | new_line)* new_line whitespace* raw_string_literal_delimiter
    ;

interpolated_raw_content
    : (not_new_line | raw_interpolation)+
    ;

raw_interpolation
    : raw_interpolation_start interpolation raw_interpolation_end
    ;

raw_interpolation_start
    : {
    | {{
    | {{{
    | etc.
    ;

raw_interpolation_end
    : }
    | }}
    | }}}
    | etc.
    ;

Выше описано определение raw_string_literal, но с некоторыми важными различиями. interpolated_raw_string_literal следует интерпретировать следующим образом:

  1. Он начинается по крайней мере с одного знака доллара (но без верхней границы), а затем три кавычки (также без верхней границы).
  2. Затем текст продолжается на той же строке, что и начальная кавычка. Это содержимое в той же строке может быть пустым или непустым. "пустой" является синонимом "полностью пробела".
  3. Если содержимое в той же строке не является пустым, то дальнейшее содержимое не может следовать. Другими словами, литерал должен заканчиваться тем же количеством кавычек в той же строке.
  4. Если содержимое той же строки пусто, то литерал может продолжаться с new_line и ряд последующих строк содержимого и new_lines.
    • Строка содержимого — это любой текст, кроме new_line.
    • Строка содержимого может содержать несколько вхождений raw_interpolation в любой из позиций. raw_interpolation должен начинаться с равного числа открытых фигурных скобок ({), что и число знаков доллара в начале литерала.
    • Если "отступ пробела" не пуст, raw_interpolation не может непосредственно следовать за new_line.
    • raw_interpolation будет следовать обычным правилам, указанным в §12.8.3. Любая raw_interpolation должна заканчиваться таким же количеством закрывающихся фигурных скобок (}), как и знаков доллара и открытых фигурных скобок.
    • Любая interpolation может содержать новые строки таким же образом, как и interpolation в обычной verbatim_string_literal (@"").
    • Затем он заканчивается new_line некоторым числом (возможно, ноль) whitespace и таким же количеством кавычек, с какого начался литерал.

Вычисление интерполированного строкового значения соответствует тем же правилам, что и обычный raw_string_literal, за исключением обновленных для обработки строк, содержащих raw_interpolations. Построение строкового значения происходит аналогичным образом, только при этом отверстия интерполяции заменяются значениями, которые эти выражения создают во время выполнения. Если interpolated_raw_string_literal преобразуется в FormattableString, значения интерполяций передаются в соответствующем порядке массиву arguments в FormattableString.Create. Остальная часть содержимого interpolated_raw_string_literalпосле пробела отступа будет использоваться для создания строки format, передаваемой в FormattableString.Create, за исключением соответствующего нумерованного содержимого {N} в каждом расположении, где произошло raw_interpolation (или {N,constant} в случае, если его interpolation имеет форму expression ',' constant_expression).

В приведенной выше спецификации существует неоднозначность. В частности, когда раздел { в тексте и { интерполяции соприкасаются. Например:

var v1 = $$"""
         {{{order_number}}}
         """

Это может быть интерпретировано как {{ {order_number } }} или { {{order_number}} }. Однако, поскольку прежнее является незаконным (ни одно выражение C#не может начинаться с {) было бы бессмысленно интерпретировать этот способ. Поэтому мы интерпретируем подобным образом, где самые внутренние { и } фигурные скобки образуют интерполяцию, а все внешние образуют текст. В будущем это может быть проблема, если язык когда-либо поддерживает любые выражения, окруженные фигурными скобками. Однако в этом случае следует написать рекомендацию следующим образом: {{({some_new_expression_form})}}. Здесь круглые скобки помогут отделить часть выражения от остальной части литерала или интерполяции. Это уже имело прецедент в том, как тернарные условные выражения должны быть обёрнуты, чтобы не конфликтовать со спецификатором форматирования или выравнивания при интерполяции (например, {(x ? y : z)}).

Недостатки

Необработанные строковые литералы добавляют большую сложность к языку. У нас уже есть много строковых литеральных форм уже для многочисленных целей. Строки "", строки @"" и строки $"" уже обладают большой мощностью и гибкостью. Но все они не имеют способа обеспечить необработанное содержимое, которое никогда не нуждается в побеге.

Приведенные выше правила не поддерживают случай 4.a:

  1. ...
    • При необходимости, с дополнительной сложностью мы могли бы уточнить это, чтобы сказать, что для всех строк, которые сами не содержат new_line (но могут начинаться или заканчиваться символом кавычки "), должно быть возможно представить саму строку в одной строке.

Это связано с тем, что у нас нет средств знать, что начальные или конечные кавычки (") принадлежат содержимому, а не разделителю. Если это важный сценарий, который мы хотим поддержать, мы можем добавить конструкцию ''' параллельно, чтобы дополнить форму """. С помощью этой параллельной конструкции одна строка, которая начинается и заканчивается ", можно легко записать как '''"This string starts and ends with quotes"''' вместе с параллельной конструкцией """'This string starts and ends with apostrophes'""". Это также может быть желательно, чтобы помочь визуально разделить символы кавычек, что может оказаться полезным при внедрении языков, которые используют один символ кавычки гораздо чаще, чем другой.

Альтернативы

https://github.com/dotnet/csharplang/discussions/89 охватывает множество вариантов здесь. Альтернативные варианты многочисленны, но я считаю, что они становятся слишком сложными и обладают плохой эргономикой. Этот подход выбирает простоту, когда вы просто продолжаете увеличивать длину начальной или конечной кавычки, пока не возникает никаких проблем с конфликтом с содержимым строки. Он также позволяет писать код, чтобы он выглядел хорошо форматированным, одновременно создавая литерал без отступов, что необходимо для большинства кода.

Одним из самых интересных возможных вариантов является использование ограждений ` (или ```) для этих необработанных строковых литералов. Это может иметь несколько преимуществ:

  1. Это позволит избежать всех проблем со строками, начинающимися или заканчивающимися кавычками.
  2. Он будет напоминать Markdown. Хотя это потенциально не хорошо, так как пользователи могут ожидать интерпретацию markdown.
  3. Необработанный строковый литерал должен начинаться и заканчиваться только одним символом в большинстве случаев, и потребуется несколько символов в гораздо более редких случаях, когда содержимое содержит обратные апострофы.
  4. Будет естественно в будущем расширить его с ```xml, снова как в markdown. Конечно, это также верно для формы """.

В целом, однако чистое преимущество здесь кажется небольшим. В соответствии с историей C# я думаю, что " следует продолжать быть разделителем string literal, так же, как и для @"" и $"".

Дизайнерские встречи

Открытые проблемы для обсуждения Устраненные проблемы:

  • [x] должна ли у нас быть форма в одну строку? Мы фактически могли бы обойтись без этого. Но это означает, что простые строки, не содержащие новую строку, всегда будут принимать по крайней мере три строки. Я думаю, что мы должны очень тяжеловес, чтобы заставить однострочные конструкции быть тремя линиями, чтобы избежать убегания.

Решение по дизайну: Да, у нас будет однострочная форма.

  • [x] следует требовать, чтобы многостроочное должно начинаться с новой линии? Я думаю, что мы должны. Это также дает нам возможность поддерживать такие вещи, как """xml в будущем.

Решение по дизайну: Да, мы будем требовать, чтобы многострочный текст начинался с новой строки.

  • [x] нужно ли вообще выполнять автоматическое дедентирование? Я думаю, что мы должны. Это делает код намного более приятным.

Решение по проектированию: Да, будет выполнено автоматическое уменьшение отступов.

  • [x] следует ограничить общее пробелы от смешивания типов пробелов? Я не думаю, что мы должны. Действительно, существует общая стратегия отступа под названием «табуляция для отступов, пробел для выравнивания». Это было бы очень естественно для выравнивания конечного разделителя с начальным разделителем в случае, когда начальный разделитель не запускается на остановке табуляции.

Решение о проектировании: У нас не будет никаких ограничений на сочетание пробелов.

  • [x] стоит ли нам использовать что-нибудь другое для заборов? ` будет соответствовать синтаксису Markdown и означает, что нам не нужно всегда запускать эти строки с тремя кавычками. Просто одного будет достаточно для классического случая.

Решение по проектированию: мы будем использовать """

  • [x] должны иметь требование, чтобы разделитель имел больше кавычек, чем самая длинная последовательность кавычек в строковом значении? Технически это не обязательно. Например:
var v = """
        contents"""""
        """

Это строка с """ в качестве разделителя. Несколько членов сообщества заявили, что это вызывает путаницу, и мы должны требовать, чтобы в таких случаях разделитель всегда содержал больше символов. Тогда это будет:

var v = """"""
        contents"""""
        """"""

Решение о проектировании: да, разделитель должен быть длиннее, чем любая последовательность кавычки в самой строке.