Pull to refresh

ACE: самый функциональный редактор кода

Reading time8 min
Views88K


Здравствуйте, господа!

Я был достаточно приятно удивлен, когда узнал, что проект 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 будет кстати.

Ссылочки


  1. ace.ajax.org
  2. github.com/tucnak/novile
  3. qt-project.org


Спасибо за внимание,
namespace
Only registered users can participate in poll. Log in, please.
Побудила ли вас эта статья к Qt?
9.78% Да81
52.54% Нет435
6.4% Слышал раньше, но сейчас заинтересовался53
31.28% А мне как-то все равно259
828 users voted. 328 users abstained.
Only registered users can participate in poll. Log in, please.
Как вам Ace?
37.99% Классная штука, много где пролезет332
42.79% Круто, но применения почти нету374
19.22% Полный шлак — пустотень168
874 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.
Tags:
Hubs:
+47
Comments44

Articles

Change theme settings