- PVSM.RU - https://www.pvsm.ru -

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

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

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

Я был достаточно приятно удивлен, когда узнал, что проект Ajax.org [1], именуемый Ace [2] почти ни разу не упоминался на хабре. Ace — это веб-редактор исходного кода нового поколения. Он поддерживает уйму различных функций, среди которых: наличие режимов подсветки синтаксиса для более чем 60 языков программирования, сумасшедщего количества цветовых схем из различных поопулярный IDE, широкой кастомизации вида и функционала, syntax checkers для нескольких скриптовых языков. Так уж получилось, что эта статья скорее всего будет разносторонняя, т.е. содержать материал для веб-разработчиков и С++ программистов, которых я так успешно агитирую в сторону Qt. Итак, что вы можете прочитать в этой статье?

  • Быстрый старт с Ace. Где я могу использовать Ace? Почему?
  • Портирование Ace на Qt и обертка его API: How-To для создателей Qt5-based [3] библиотек

Сейчас позволю себе показать пару скриншотов example-приложения, которое использует мою библиотеку Novile:
ACE: самый функциональный редактор кода
ACE: самый функциональный редактор кода

Что такое Ace?

Ace (англ. Ajax.org Cloud9 Editor) — онлайн-редактор исходного кода с подсветкой синтаксиса, темами, и горячими клавишами, написанный на Javascript, распространяющийся по лицензиям MPL/LGPL/GPL и легко встраиваемый в любую веб-страницу. Ace разработан в качестве основного редактора Cloud9 IDE является продолжением проекта Mozilla Skywriter (Bespin). Сейчас проек хостится [4] на GitHub и активно развивается.

Ace может заинтересовать того, кому необходимо установить красивый, и главное — удобный редактор исходного кода на сайт или встроить в свое приложение. Из приемуществ редактора можно выделить такие:

  • Подсветка для более чем 60 языков (есть импорт файлов .tmlanguage)
  • Есть более 20 тем оформления (.tmtheme также импортируются)
  • Проверка синтаксиса в режиме реального времени (JavaScript/PHP/CSS/etc.)
  • Работает на больших документах (говорят, тянет 4 000 000 строчках кода)
  • Сворачивание кода, при наличии его языка программирования
  • Легко настраиваемые «горячие» клавиши и комбинации
  • Несколько курсоров и выделений
  • Отображение непечатных символов (что очень не маловажно)
  • Автоматическое выравнивание кода
  • Поиск и замена на регулярках
  • Режимы soft и hard табуляций

Это и делает Ace очень удобным и легко встраиваемым редактором!

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

API и документации

Присутствие документации определяет ее отсутсвие. Короче говоря, нормальной документации у Ace'a нету. На сайте таковое есть, но назвать его API Reference — это слишком громко сказать. Сейчас, давайте как-то интеративненько настроим этот редактор. Stey-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. Конечно нужно очень сильно его extend-ить, но оно того стоит.

Novile Component for Qt

Сейчас речь пойдет о проекте Novile [5], проекте над которым я работал последнюю неделю. Смелые продолжат читать, а те, кому не особо оно-то и колышется, могут пролистать к Заключению. А джедаев я попрошу остаться. Я собираюсь рассказать о том, как использовать 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, и как с ним работать, так как на хабре уже есть такая статья [6]. Она отнимет у вас буквально пару минут. А сейчас, если вам неинтересен 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 [5]. Даже самый маленький pull request будет кстати.

Ссылочки

  1. ace.ajax.org [2]
  2. github.com/tucnak/novile [5]
  3. qt-project.org/ [7]

Спасибо за внимание,
namespace [8]

Автор: namespace

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/30982

Ссылки в тексте:

[1] Ajax.org: http://ajax.org

[2] Ace: http://ace.ajax.org

[3] Qt5-based: http://qt-project.org

[4] хостится: https://www.reg.ru/?rlink=reflink-717

[5] Novile: https://github.com/tucnak/novile

[6] статья: http://habrahabr.ru/post/155467/

[7] qt-project.org/: http://qt-project.org/

[8] namespace: http://habrahabr.ru/users/namespace/

[9] Источник: http://habrahabr.ru/post/174987/