Адаптация ZenCoding к C# — ZenSharp

в 7:55, , рубрики: nemerle, peg, ReSharper, Visual Studio, кодогенерация, мнемоника, формальная грамматика

Многие наверняка знают, что для HTML & CSS существует великолепный инструмент ZenCoding(emmet), который позволяет очень сильно упростить ввод рутинных конструкций языка, определяя специальный язык мнемоник. C# менее многословный язык, чем Html, но тем не менее, ввод его конструкций можно здорово оптимизировать.
Я предлагаю динамическое расширение идеи мнемоник, впервые услышанное мною от Дмитрия Нестерука [1].

proto

sample

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

Небольшое описание

Основная идея, которая заложена в ZenSharp — создать такой язык, в котором сразу можно определить два языка — язык сокращений и соответсвующее ему полное написание.
Весь файл определений сокращений (*.ltg) состоит из двух частей:

  • определения правил (т.е. что и как во что раскрывается),
  • определения зоны действий этих правил.

Простой пример определения правила: если хотим, чтобы по набору ex текст раскрывался в expand text, то стартовое правило необходимо определить так:

start ::= "expand text"="ex"

Такие правила я называю «определяющими сокращение».
Всего существует три типа правил:

  • Определяющий сокращение. Состоит из пары строк, определенных в виде "expand"="short" Т.е. если хочется получить сокращение, чтобы, введя p, получить public, то такое правило объявляется как "public"="p"
  • Строковый. Просто строка, которая всегда будет использоваться при раскрытии правила сокращения. Иначе говоря, просто текст, который не зависит от того, какую букву используем при сокращении. Строковое правило это тоже самое, что определяющее сокращение правило с пустой строкой сокращения.
  • Подстановочный. Специальный тип, который введен специально для того, чтобы поддержать возможность ввода названия переменной/функции в языке сокращения. Можно использовать два правила, написанные по умолчанию для типа и названия переменной.

Синтаксический сахар

В языке описания шаблонов есть немного сахара:

  • скобочки
  • вопросительный знак после скобочек переписывает правило на (x)? => ((x) | "")
  • если сокращение не содержит специальных символов, то его можно определить
    без кавычек, т. е. "some"=s, а не "some"="s"
  • многострочное определение альтернатив. Это сделано для удобной записи стартового правила, чтобы не создавать мусора в VCS.
    start ::= rulea | ruleb
    это тоже самое, что

    start ::=
      | rulea
      | ruleb

Пояснение на примерах

Проще объяснить зачем все это нужно на реальном примере. Возьмем небольшую
часть стандартных правил:

interface ::= access "interface"=i space classBody
class ::= access ("sealed "=s)? ("class"=c | "static class"=C) space classBody
access ::= (internal=i | public=p | private=_ | protected=P) space
classBody ::= identifier "{" cursor "}"
scope "InCSharpTypeAndNamespace" {
  start ::= class | interface
}

Предположим, что мы ввели в студии какую-нибудь абракадабру вида pсSome. Понимать как будет происходить раскрытие этого правила необходимо, отталкиваясь от стартового правила start. Это правило определяет две альтернативы — class или interface. Грамматика работает по принципу PEG (parsing expression grammar), т.е. оператор | является оператором упорядоченного выбора.
Проверяется альтернатива class. Внутри class успешно раскрывается access и забирает первую букву p, раскрывая в ее расширенное написание «public ». Далее идет ленивая часть "sealed"=s. У нашей абракадабры осталась часть cSome, и она не подходит под эту часть, поэтому пропускаем ее, ничего не убираем из
сокращения и ничего не добавляем в раскрытие. Далее идет альтернатива из class или static class. Нам подходит class, поэтому имеем раскрытие «public class» и короткую строку Some.
Специальное правило identifier забирает все буквенные символы из сокращения и без изменений передает их в длинное написание (это сделано для удобства).
В итоге, наша абракадабра pcSome превратилась в public class Some { $END$ }.


Повторюсь, что плагин для ReSharper 8.2 доступен в галерее.
Исходный код на github.com/ulex/ZenSharp
Это первая из двух запланированных статей. Я постарался описать внутренности работы ZenSharp. Во второй статье я расскажу чуть более про стандартные шаблоны. Я сам использую для работы ZenSharp не более месяца, время от времени добавляя удобные правила. Буду рад обратной связи.

Автор: aulitin

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js