
Здравствуйте, господа!
Я был достаточно приятно удивлен, когда узнал, что проект Ajax.org, именуемый Ace почти ни разу не упоминался на хабре. Ace — это веб-редактор исходного кода нового поколения. Он поддерживает уйму различных функций, среди которых: наличие режимов подсветки синтаксиса для более чем 60 языков программирования, поддержка сумашедшего количества цветовых схем из различных популярных IDE, функций, среди которых: широкая кастомизация, проверка синтаксиса для нескольких скриптовых языков. Так уж получилось, что эта статья скорее всего будет разносторонняя, т.е. содержать материал для веб-разработчиков и С++ программистов, которых я так успешно агитирую в сторону Qt. Итак, что вы можете прочитать в этой статье?
- Быстрый старт с Ace. Где я могу использовать Ace? Почему?
- Портирование Ace на Qt и обертка его API: How-To для создателей Qt5-based библиотек (таких как мой порт Ace — Novile
Сейчас позволю себе показать пару скриншотов example-приложения, которое использует мою библиотеку Novile:


Что такое Ace?
Ace (англ. Ajax.org Cloud9 Editor) — онлайн-редактор исходного кода с подсветкой синтаксиса, темами, и горячими клавишами, написанный на Javascript, распространяющийся по лицензиям MPL/LGPL/GPL и легко встраиваемый в любую веб-страницу. Ace разработан в качестве основного редактора Cloud9 IDE является продолжением проекта Mozilla Skywriter (Bespin). Сейчас проект хостится на GitHub и активно развивается.
Ace может заинтересовать того, кому необходимо установить красивый и главное — удобный редактор исходного кода на сайт или встроить в свое приложение. Из преимуществ редактора можно выделить:
- Подсветка для более чем 60 языков (есть импорт файлов .tmlanguage)
- Есть более 20 тем оформления (.tmtheme также импортируются)
- Проверка синтаксиса в режиме реального времени (JavaScript/PHP/CSS/etc.)
- Работает на больших документах (говорят, тянет 4 000 000 строчках кода)
- Сворачивание кода, при наличии его языка программирования
- Легко настраиваемые «горячие» клавиши и комбинации
- Несколько курсоров и выделений
- Отображение непечатных символов (что очень немаловажно)
- Автоматическое выравнивание кода
- Поиск и замена на регулярках
- Режимы soft и hard табуляций
Это и делает Ace очень удобным и легко встраиваемым редактором!

API и документации
Присутствие документации определяет ее отсутствие. Короче говоря, нормальной документации у Ace'a нету. На сайте таковое есть, но назвать его API Reference — это слишком громко сказать. Сейчас, давайте как-то интеративненько настроим этот редактор. Step-by-step, так сказать.
Шаг 1. Элементарная настройка
Начнем с создания предполагаемой страницы с редактором. Создадим шаблон:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ace Editor Demo</title>
<style type="text/css">
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
</head>
<body>
<div id="editor"></div>
<script src="http://d1n0x3qji82z53.cloudfront.net/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor"); // теперь обращаться к редактору будем через editor
// Далее весь экшон будет проходить тут!
</script>
</body>
</html>
Отлично! Теперь у нас есть простая страничка с простым редактором кода. Это уже что-то. Кстати, можно использовать другие источники ace.js. Не говоря о том, что вам понадобятся другие файлы, лучше вообще собирать его самостоятельно (node-ом) или собирать по-кусочкам из built-файлов с различным функционалом.
Шаг 2. Больше красок!
Сейчас настало время добавить красок нашему предполагаемому JavaScript коду. Это можно легко сделать путем нескольких нехитрых call-ов. Ну что, давайте допишем пару строчек:
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
Что же мы сделали? Тема редактора задается строкой, содержащей путь формата ace/theme/<theme_name>. Важно отметить, что тема — это именно атрибут Editor. Далее мы задаем режим подсветки, подсветка — атрибут EditSession. Она задается аналогично теме, за исключением того, что задается не редактору на прямую, а его сессии. Теперь, давайте что-то помалякаем. Например, можете поупражняться в написании навороченного jquery-based AJAX-запроса :)
Шаг 3. Управляем текстом на API-level
Писать код — хорошо, но еще лучше уметь что-то с ним делать на программном уровне. На этом шаге я постараюсь разобраться в основных функциях редактора и их API. Как сказал Гагарин, Поехали!
Получаем и задаем содержимое:
editor.setValue("<source code>"); // задаем
editor.getValue(); // -> String (получаем)
Немного работы с выделением:
var selectionRange = editor.getSelectionRange(); // Range - название говорит само за себя
var selectionText = editor.getSession().getTextRange(selectionRange); // -> String (текст выделения)
А теперь немного поиграемся с курсором:
var pos = editor.getCursorPosition(); // Object {row: N, column: M}
editor.insert("I know how to insert text"); // вставляем в точку, где находится курсор
editor.gotoLine(lineNumber); // переходим на линию #lineNumber (нумерация с нуля)
Что насчет расширенного управления кодингом?
var lines = editor.session.getLength(); // количество строчек в документе
editor.getSession().setUseSoftTabs(true); // использования "мягкого" выравнивания Tab-ами.
document.getElementById('editor').style.fontSize='12px'; // достаточно только поменять размер шрифта #editor
editor.getSession().setUseWrapMode(true); // включаем text wrapping
editor.setShowPrintMargin(false); // такая полоска-граница (40, 80 или свободно число символов, считая слева)
editor.setReadOnly(true); // нельзя редактировать, false - можно
Кстати, если вдруг вам захочется поменять размер div#editor, не меняя размеров самого окна, то нужно выполнить
editor.resize()
Шаг 4. Поиск и события (events)
К сожалению, о поиске я скажу только пару слов, так как почему-то нет желания сейчас здесь описывать его работу. Вкратце, поддерживает регулярные выражения, чувствительность к регистру, и много других параметров. API сводится к
editor.find('needle', { // ищем текст "needle"
backwards: false, // только спереди курсора
wrap: false, // циклируем по документу
caseSensitive: false, // все равно на регистр
wholeWord: false, // только слово целиком
regExp: false // без регулярок
});
editor.findNext();
editor.findPrevious();
editor.replaceAll('pub');
Слушаем различные события:
// Что-то изменилось в сессии
editor.getSession().on('change', function(e) {
// e.type, etc
});
// Поменялось выделение
editor.getSession().selection.on('changeSelection', function(e) {
// e.type, etc
});
// Смена локации курсора
editor.getSession().selection.on('changeCursor', function(e) {
// e.type, etc
});
А теперь очень классное — «горячие» клавиши и сочетания:
editor.commands.addCommand({
name: 'myCommand', // название команды
bindKey: {win: 'Ctrl-M', mac: 'Command-M'}, // вызов на PC и Mac
exec: function(editor) { // судный час
// editor.*
},
readOnly: true // false, если мы не хотим чтобы в readOnly работало
});
В теории, обладая этими знаниями (+ специфика из полного api reference) можно написать редактор, аналогичный редактору Cloud9 IDE. Конечно нужно очень сильно его расширять, но оно того стоит.
Novile Component for Qt
Сейчас речь пойдет о проекте Novile, проекте над которым я работал последнюю неделю. Смелые продолжат читать, а те, кому не особо оно-то и колышется, могут пролистать к Заключению. А джедаев я попрошу остаться. Я собираюсь рассказать о том, как использовать Novile, зачем он нужен, и главное, расскажу,
Кому оно надо?
Когда я начинал работу над Novile, я в первую очередь думал о том, какое она сможет найти применение. По отзывам знакомых программистов, я решил, что использовать Novile можно в различных десктопных (и не только) приложениях, где нужно редактировать код или как минимум, конфигурационные файлы. Таких приложений есть достаточно много, а с Novile их должно стать больше.
Novile, в частности, created with Qt, created for Qt. Кто не знает, Qt — это фреймворк для разработки кросс-платформенного ПО на C++ (еще можно на Python, Java, но это не тот случай). Погуглив, можно понять что Qt — это нечто, что Qt — это Linux Mac Window Symbian Android iOS Embedded. Novile — это мост между низкоуровневым API редактора на JavaScript к высокоуровневому коду на С++ через QtWebKit.
Сборочная система
Несмотря на то, что Qt очень активно пропагандирует QMake, а уже скоро начнется QBS-мания, я считаю, что для библиотек самый лучший вариант, однозначно CMake. Он дает тот уровень кастомизации сборки, который действительно нужен.
Сейчас для сборки и установки Novile с документацией, debug output и примером нужно выполнить (Linux):
cd path/to/novile
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=path -DVERBOSE_OUTPUT=Yes -DBUILD_DOCS=Yes -DBUILD_EXAMPLE=Yes ..
make # можно пропустить
make install
Удобно, не так-ли? Сегодня я попробую научить вас делать такие библиотеки (в данном случае для Qt 5). Кстати, я не буду рассказывать, как работает СMake, и как с ним работать, так как на хабре уже есть такая статья. Она отнимет у вас буквально пару минут. А сейчас, если вам неинтересен CMake, вы можете пролистать следующую статью.
Qt 5 в CMake 2.8.8+
Как же заставить CMake собирать Qt 5 проекты? Да очень просто. Читаем код и комментарии (кстати, приятно, что на хабре есть хайлайтер для CMake):
# .h файлы будут автоматически подвержены мета-объектному компилятору
set(CMAKE_AUTOMOC ON)
# будет добавлять текущую папку в инклюды автоматически
# (не нужно будет везде писать include_directories(.)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# ищем QtWebKitWidgets и находим (я надеюсь)
# кстати, чтобы оно его нашло, cmake-файлы Qt должны быть в $PATH
find_package(Qt5WebKitWidgets REQUIRED)
# я люблю записывать все .cpp файлы в такой манер
set(NOVILE_SOURCES # .cpp
editor.cpp
)
# аналогично
set(NOVILE_HEADERS # .h
editor.h
novile_export.h
novile_debug.h
editor_p.h
)
# QRC - файлы ресурсов Qt. Утилита uic собирает .qrc файлы в qrc_*.cpp файлы, которые
# можно использовать для ресурсов. NOVUILE_RCC_SRC - готовые qrc_*.cpp файлы
qt5_add_resources(NOVILE_RCC_SRC ../data/shared.qrc) # скармливаем файл ресурсов и получаем qrc_*.cpp файл
add_library(novile SHARED ${NOVILE_SOURCES} # создаем shared-библиотеку
${NOVILE_HEADERS}
${NOVILE_RCC_SRC}
../data/shared.qrc # только чтобы этот файл отображался в дереве проекта IDE (.cbp файл)
)
qt5_use_modules(novile WebKitWidgets) # linkуем к таргету модуль Qt 5
set_target_properties(novile PROPERTIES # свойства библиотеки
DEFINE_SYMBOL NOVILE_MAKEDLL # define для свитчера EXPORT/IMPORT
PUBLIC_HEADER "${NOVILE_HEADERS}" # хедеры для установки
)
install(TARGETS novile
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}/include
)
Если вы создаете свою библиотеку, то этого вам не избежать. Еще вам может понадобится собирать .ui файлы пользовательского интерфейса. Для этого есть специальный макрос:
# EXAMPLE_UIC_HDR - переменная со сгенерированными ui_*.h
qt5_wrap_ui(EXAMPLE_UIC_HDR mainwindow.ui)
Import/Export свитчеры
При работе с библиотекой, некоторые символы должны уйти в экспорт, а потом, на этапе использования либы, быть импортированными из нее, поэтому удобно использовать такую конструкцию:
#ifndef NOVILE_EXPORT
# if defined(NOVILE_MAKEDLL)
# define NOVILE_EXPORT Q_DECL_EXPORT // кьютовый макрос экспорта
# else
# define NOVILE_EXPORT Q_DECL_IMPORT // импорта
# endif
#endif
class NOVILE_EXPORT MyClass
{
Q_OBJECT
// ...
Итак, теперь, во время сборки, нужно объявить -DNOVILE_MAKEDLL (помните, выше мы его установили в cmake). При использовании библиотеки, макроса не будет, и будет вызван экспорт.
Как пользоваться Novile?
Пример использования Novile классно описан в проекте из папки example, который хорошо собирается с ключом конфигурации -DBUILD_EXAMPLE=Yes, который сделает все за вас.
Но тут я готов привести маленький пример того, как же удобно это все делается из плюсов (С++11 + Qt 5)
#include <Novile/Editor>
using namespace std;
// somewhere in the code...
Editor *editor = new Editor;
editor->setHighlightMode(Editor::ModePython);
editor->setTheme(Editor::ThemeMonokai);
editor->setFontSize(13);
editor->setPrintMarginShown(false);
connect(editor, &Editor::textChanged, [=]() { // код будет выполнен асинхронно на событии
const QString &text = editor->text();
doSmth(text);
});
editor->show(); // показать его миру
C++11, мощный Qt и дописанная Novile позволяют построить быструю и кросс-платформенную IDE со всем необходимым функционалом.
Заключение
Я надеюсь что эта статья действительно помогла и веб-разработчикам, и программистам. Представить не могу, насколько удачно я сагитировал народ на Qt, но я старался. Честно. Хотелось бы сказать пару слов в сторону Novile. Признаться, сейчас Novile не совсем хороший продукт, чтобы я про него не говорил. Сейчас она покрывает от силы 50% функционала Ace. Если есть кто-то заинтересованный, вы всегда можете помочь проекту на GitHub. Даже самый маленький pull request будет кстати.
Ссылочки
Спасибо за внимание,
namespace
Only registered users can participate in poll. Log in, please.
Побудила ли вас эта статья к Qt?
9.77% Да81
52.59% Нет436
6.39% Слышал раньше, но сейчас заинтересовался53
31.24% А мне как-то все равно259
829 users voted. 328 users abstained.
Only registered users can participate in poll. Log in, please.
Как вам Ace?
38.06% Классная штука, много где пролезет333
42.74% Круто, но применения почти нету374
19.2% Полный шлак — пустотень168
875 users voted. 371 users abstained.
Only registered users can participate in poll. Log in, please.
Нужен ли Novile?
29.88% Да, поможет разработчикам разных редакторов и IDE193
70.12% Умрет, так как никому толком не пригодится453
646 users voted. 450 users abstained.