Emmet как платформа для инструментов
Сергей Чикуёнок | http://emmet.io
Что такое Emmet?
• Инструмент для ускорения работы с HTML и CSS
• Плагин для множества популярных редакторов кода• Написан на чистом JavaScript, легко встраивается
Что такое Emmet?
Развитие инструментов для фронт-энд разработчиков
1990-е: Microsoft FrontPage
2000-е: Ъ-кодеры пишут в «Блокноте»
2010-е: Sublime Text, WebIDE
Sublime Text WebIDE
• быстрый• красивый• классный API
• удобный автокомплит• кроссплатформенный
• богатый функционал• много хорошо интегрированных инструментов
• удобная отладка
С точки зрения фронт-энд разработчика:
Sublime Text WebIDE
• только раскрашивает HTML и CSS
• API работает с обычным текстом• Java
• медленная• API позволяет работать с AST, но это очень сложно
• разработчики акцентируют внимание на других вещах
Код по-прежнему долго писать и тяжело редактировать
Выход?
Препроцессоры!
Преимущества:
• Короткая нотация, быстрее писать• Читабельность• Синтаксический сахар• Шаблонизация, связь с внешними данными
Недостатки:
• Сложная отладка кода• Не решают проблему редактирования существующего кода
• Не всегда можно использовать на клиенте
Чего хочется:
• писать меньше кода• работать в любимом редакторе
• разрабатывать инструменты на JavaScript
• работать с HTML и CSS как с DOM
Emmet – платформа для инструментов веб-разработчика
Emmet как платформа
• модульная архитектура• написан на JavaScript
• поддержка большого количества редакторов• поддержка расширений
Расширения Emmet
• Обычные .js и .json-файлы в специальной папке
• Загружаются автоматически• Могут дополнять или переопределять основной функционал
• Одинаково работают в разных редакторах
Основные модули
• разбор и преобразование аббревиатур• поиск пар тэгов в коде• работа с HTML-тэгами и CSS-правилами через DOM-подобный интерфейс
Как из аббревиатур получается HTML-код
Разбор аббревиатуры в дерево
Нормализация дерева
Формирование текстового результата
Готовый код
Как из аббревиатур получается HTML-код
Разбор аббревиатуры в дерево
Нормализация дерева
Формирование текстового результата
Готовый код
препроцессоры
постпроцессоры
фильтры
emmet.exec(function(require, _) { require('filters').add('my_filter', function process(tree, profile) { _.each(tree.children, function(item) { // пример для HTML item.start = '<' + item.name() + '>'; item.end = '</' + item.name() + '>';
// пример для Jade item.start = item.name() + '\n'; item.padding = '\t';
// преобразуем уже существующий результат item.start = item.start.replace(/</g, '<').replace(/>/g, '>');
// рекурсивное преобразование всего дерева process(item, profile); }); });});
Пример фильтраdiv>em|my_filter
var tag = emmet.require('htmlMatcher').tag( 'hello <em>world</em>', // текст, где искать тэг 12 // позиция, с которой начать поиск);
tag.open = { // открывающий тэг name: 'em', selfClose: false, // является ли тэг самозакрывающимся range: new Range() // {start: 6, end: 10}};
tag.close = {...}; // закрывающий тэг
tag.innerRange = new Range(); // {start: 10, end: 15}tag.innerContent = function(){}; // worldtag.outerRange = new Range(); // {start: 6, end: 20}tag.outerContent = function(){}; // <em>world</em>tag.range = innerRange || outerRange;tag.content = innerContent || outerContent;
Получение HTML-тэга
emmet.exec(function(require, _) { require('actions').add('rename_tag', function(editor) { var tag = require('htmlMatcher').tag(editor.getContent(), editor.getCaretPos()); if (tag) { var newName = editor.prompt('Введите новое имя тэга'); if (tag.close) { editor.replaceContent( '</' + newName + '>', tag.close.range.start, tag.close.range.end); }
editor.replaceContent( '<' + newName, tag.open.range.start, tag.open.range.start + tag.open.name.length + 1); } });});
Пример: переименование тэга
Интерфейс для редактирования HTML и CSS
(EditTree)
• Интерфейс для высокоуровнего редактирования HTML-тэгов и CSS-правил
• Похож на DOM
• Предоставляет доступ ко всем атрибутам/свойствам контейнера, а также их диапазонам
•Учитывает форматирование кода
var tree = emmet.require('xmlEditTree') .parse('<tag attr1="val1" attr2="val2">');
tree.value('attr1'); // val1tree.get('attr1').range(); // {start: 5, end: 17}tree.value('attr1', 'Hello world');
tree.remove('attr2');tree.add('a', 'b', 0);
tree.source; // <tag a="b" attr1="Hello world">
Редактирование HTML (xmlEditTree)
var tree = emmet.require('cssEditTree').parse('div {color: red}');// можно использовать метод parseFromPosition(content, pos)// для получения правила из документа tree.value('color', 'black');tree.value('position', 'relative');
tree.source; // div {color: black;position: relative;}
Редактирование CSS (cssEditTree)
Только вы сами можете создавать лучшие инструменты