Темная сторона QScintilla

Недавно на Хабре начали появляться статьи (1, 2) про замечательный компонент QScintilla.
Я один из разработчиков проекта Monkey Studio, и, последние полтора года, mksv3. Мы пользуемся QScintilla уже 5 лет. Периодически делаем для него баг репорты и патчи. Мы решили от него отказаться. И нам мучительно больно за потраченные на него годы.
Пишу этот пост, чтобы у других не случилось так же.

Я делюсь своим опытом. Мы разрабатываем свободный универсальный кроссплатформенный текстовый редактор. То, что критично для нас, может быть не важно для вас. И наоборот. Анализируйте.
И так, наш опыт. Что плохо в QScintilla…

Бинарные лексеры

Лексер в терминологии Scintilla — класс, разбирающий, подсвечивающий, выравнивающий и т.д. определенный язык программирования.
Для большинства компонентов, позволяющих редактировать код, можно создавать текстовые файлы с описанием синтаксиса нового языка. Например, для katepart. Для QScintilla лексер — это только C++ код. Соответственно:
  • Поддерживается мало языков и типов файлов (по сравнению с emacs, vim, kate). Не много желающих писать свой лексер.
  • Для большинства редакторов пользователь может скачать текстовый файлик из интернета и получить подсветку нового языка без перекомпиляции редактора. (Вот, например, поддержка go для vim). Вот только для QScintilla таких файликов вы не найдете.


Настройки лексеров

QScintilla позволяет настраивать лексер. Вы можете выбрать, какие структуры будут сворачиваться (code folding), а какие нет в C++, и будет ли лексер подсвечивать неконсистентное выравнивание кода в Python. Но, у каждого лексера свой набор параметров. И код и для для настройки лексера приходится писать уникальный.
Если программисту захочется узнать, является ли строка файла Y символ X комментарием (например, чтобы проверять правописание в комментариях, но не в коде) — снова придется писать уникальную проверку для каждого из лексеров.
Еще больше проблема обостряется тем, что набор лексеров и их настроек меняется с каждым релизом. О том, что настройки изменились вам будут периодически напоминать баг репортами «У меня не собирается проект на QScintilla версии x.x.x». И вам очень пригодится конструкция
#if QSCINTILLA_VERSION >= ...

Цветовые схемы

Люди разные, вкусы у них тоже разные. Кому-то нравится темный текст на светлом фоне, кому-то светлый текст на темном фоне. QScintilla позволяет настроить шрифт и фон для каждого из синтаксических элементов (числа, строки, ключевые слова...)
Вот, например, диалог настройки из Monkey Studio (кликните картинку для полного размера):

Но, настроить можно только конкретный лексер. На скриншоте — страничка настройки C++. Если пользователю захочется создать свою цветовую схему, придется настраивать по очереди каждый из используемых языков.
Если программист захочет создать цветовую схему для пользователей, вариант тот-же — придется настроить каждый из нескольких десятков языков, и не забывать обновлять тему с появлением новых.
Очень трудоемко. Kate и некоторые другие редакторы позволяют настраивать все языки одновременно. Так проще.

Плохой рендеринг

QScintilla не использует QTextEdit + QSyntaxHighlighter, а рисует все самостоятельно на QAbstractScrollArea. Велосипед местами получился кривой.
  • Ширина monospace шрифта на некоторых платформах зависит от того, является ли он жирным.
  • Шрифты на некоторых платформах не сглаживаются и смотрятся убого.
  • Если строка длинная и не вмещается в ширину экрана, иногда она рисуется не правильно. Редактирование происходит не там, где отображается курсор. Приходится переоткрывать файл, чтобы его возможно было редактировать. Пока снова не начнется...


Собственный синтаксис регулярных выражений

QScintilla поддерживает поиск в тексте. Он работает, даже можно использовать регулярные выражения. Но вот синтаксис регулярных выражений местами отличается от общепринятого (такого, как, например, у QRegExp). И пользователям эти мелкие отличия не понравятся. Для прототипа — сгодится, для стабильного проекта — нет.
Мы сделали поиск на QScintilla, потом пришлось его выбросить и написать собственный.

Нет открытого репозитория

Из каких-то соображений Phil Thomson не открывает репозиторий QScintilla в публичный доступ, а только выкладывает архивы.
Из-за этого:
  • Неудобно проверять, что изменилось в версии X по сравнению с версией Y.
  • Очень сложно делать патчи для старых версий QScintilla на основе исправлений в новой.
    Необходимость возникает, например, если нужно сделать патч для версии, которая используется в Debian stable. Политика дистрибутива не позволяет просто обновить пакет.


Заключение

Надеюсь никто не подумал, что я не люблю QScintilla, или считаю его плохим компонентом. У него очень много полезной функциональности, он поддерживает большое количество языков, в большинстве случаев автор очень оперативно и адекватно реагирует на патчи и баг репорты. Для большинства проектов на Qt, которым нужен редактор кода, QScintilla — оптимальное решение. Тем более, что альтернатив не много. И очень здорово, что namespace пишет про QScintilla полезные статьи.
Просто про положительные стороны уже написано. А я рассказал про найденные нами недостатки, которые не очевидны на начальных стадиях проекта. Надеюсь, это поможет составить полную картину и объективно оценить компонент.

Список недостатков — наш опыт. Вожможно мы что-то не поняли и делаем не правильно. Комментируйте. Давайте искать истину.

Если решите использовать QScintilla, полезные примеры можно найти в Eric, JuffEd и моих проектах, ссылки на которые есть в начале.

Авторам новых IDE

И еще, если вы дочитали до конца, с некоторой долей вероятности вы планируете или уже начали сделать свой собственный текстовый редактор или IDE. И, может быть, на этом этапе вас еще не поздно остановить.
За пять лет я видел не менее десятка проектов, которые умерли через пару лет после старта. Запал автора иссякает, а функционал оказывается не намного больше того, что было в первые пару месяцев. Печально.
Иногда новые проекты начинать стоит. Но, только если есть действительно серьезные причины. Сначала просмотрите существующие и четко сформулируйте для себя, почему нельзя влиться в их комманду и продолжить работу вместе. Лучше один хороший проект, чем 10 плохих. Ссылки на несколько стоящих проектов на Qt, и в т.ч. на QScintilla есть здесь.

Удачи!

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 28

    0
    > Ссылки на несколько стоящих проектов на Qt
    Ещё Code::Blocks есть, тоже неплохая. Ну, ещё valide. А из редакторов — gedit и mcedit =)

    За статью спасибо, редко бывают адекватные статьи с критикой.
      0
      Code::Blocks вроде на WxWidgets, а gedit на Gtk+.
        0
        По ссылке, приведённой автором, перечислены различные текстовые редакторы и IDE, которые рассматриваются как конкуренты. И не обязательно на Qt.
          0
          А, ну просто написано

          Ссылки на несколько стоящих проектов на Qt

          И вы тоже этот кусок скопировали
      +1
      Так а не проще тогда кусок ответственный за подсветку синтаксиса выдрать из QtCreator'а?
        0
        Но он же умеет только C++, qmake, js подсвечивать.
          0
          А как же generic highlighter? Он конфигурационные файлы от kate прекрасно понимает.
            0
            Извините. Понял уже. Я не сообразил просто.
            0
            Заглянул в QtSDK. В директории generic-highlighter обнаружено несколько xml-файлов синтаксиса для katepart. Если он действительно хорошо их поддерживает — список языков легко расшитить, скачав полную базу katepart файлов.
            Но я с этим еще не разбирался и не тестил.
              +1
              В современных версиях криейтора их можно скачивать напрямую из настроек:
                +2
                Спасибо за скриншот. Теперь знаю где искать.
                  0
                  Всегда пожалуйста =)
              +1
              Он всё подсвечивает для чего есть правила, а правила в kate есть почти для всего на свете.
            +1
            Спасибо. Я всегда почему-то думал что она наследуется от QTextEdit. Это беда, правда. Спасибо за вашу статью, она как раз пришлась к концу моей трилогии).
              0
              По-моему, основной проблемой QScintill'ы является отсутствие гибкости, ввиду чего добавление простейшего функционала или небольшое изменение поведения превращается в головную боль.
                0
                Если вы про добавление функционала в саму QScintilla — то да.
                Я как-то предлагал автору фичу добавить. Но его политика — я только делаю порт на Qt. Так что сначала придется пропихнуть свой патч в Scintilla, только потом в QScintilla.
                А если вы собираетесь делать программу для дистрибутивов Linux — то еще и долго ждать, пока новая версия войдет в популярные дистрибутивы.
                  0
                  Ну, речь идёт скорее даже о том, как QScintilla использует собственно Scintill'у. Вот как автору вздумалось, так он и написал тот или иной метод, не оставляя возможности определить другое поведение. Вот и приходится либо костыли впиливать, либо исходники править.
                    0
                    Иногда спасает то, что доступен низкоуровневый интерфейс к Scintill'е. (Метод SendScintilla)
                      0
                      Ключевое слово «иногда». :)
                +2
                Прошёл через всё, описанное автором, всё чистая правда, однако некоторые вещи не настолько катастрофичны, как может показаться.

                >QScintilla позволяет настроить шрифт и фон для каждого из синтаксических элементов (числа, строки, ключевые слова...)
                Но, настроить можно только конкретный лексер. На скриншоте — страничка настройки C++. Если пользователю захочется создать свою цветовую схему, придется настраивать по очереди каждый из используемых языков.


                А это уже на усмотрение разработчика редактора. Да, с точки зрения кода нужно задавать шрифт и фон для каждого лексера отдельно, но вот с точки зрения пользователя всё может настраиваться централизовано. Задал в настройках шрифт/цвет фона — и он стал таким у всех лексеров. В JuffEd, например, так и сделано.

                Насчёт общего цвета для чисел/строк/ключевых слов — тут тоже никто не мешает сделать единую настройку для всех лексеров одним махом. Решается простейшим мапом.
                Другой разговор, что лично я предпочитаю, чтобы подсветки разных языков различались визуально (у кого-то может быть другие предпочтения — тут уж кому как нравится), но в целом, наверное, неплохо иметь такую опцию.

                >Мы сделали поиск на QScintilla, потом пришлось его выбросить и написать собственный.

                Я выбросил его сразу же после беглого с ним знакомства :)

                В общем и целом — увидел строку «мы решили отказаться от QScintilla», но не увидел, в пользу чего вы решили это сделать. QSyntaxHighlighter? Ну что ж… искренне желаю удачи. Я с него начинал. Но потом послал к чертям и переключился на QScintilla :)
                  0
                  > Да, с точки зрения кода нужно задавать шрифт и фон для каждого лексера отдельно, но вот с точки зрения пользователя всё может настраиваться централизовано. Задал в настройках шрифт/цвет фона — и он стал таким у всех лексеров. В JuffEd, например, так и сделано.
                  Да, естественно. Мои проекты и Eric делают так же. Я имел в виду именно «настроить цвет для всех чисел».

                  > никто не мешает сделать единую настройку для всех лексеров одним махом. Решается простейшим мапом.
                  С таким подходом мап для всех языков должен делать автор редактора. Я не уверен, что у меня получится сделать действительно качественный мап и получить действительно хорошую подсветку для всех поддерживаемых языков, ведь я их не знаю.
                  Мне намного больше нравится подход katepart, когда автор файла ситнаксиса сам мапит элементы Своего языка на стандартный набор, видит и Понимает, что получилось, если нужно — принимает решение что для этого конкретного элемента стандартные стили не подходят и нужно применить уникальный стиль.

                  > Но потом послал к чертям и переключился на QScintilla :)
                  Таким образом ты получил возможность быстро сделать Хороший редактор. Но упустил возможность сделать Великолепный редактор, часть истории, такой, как, например, Vim и Emacs.
                  А я для себя осознал, что мне Хороший редактор делать не интересно, их и так много существует.
                  Это мое личное скромное мнение. Не хотелось бы, чтобы по этому поводу разгорелся холивар. Во первых, правильное решение по формуле не посчитаешь, во вторых, люди разные и вкусы разные, правильных решений много.

                  По поводу того, в пользу чего мы решили отказаться я не писал сознательно. Эта статья — про QScintilla. Потому что про нее мне уже есть что написать. А про альтернативу писать пока рано. История еще только начинается…
                    0
                    >Таким образом ты получил возможность быстро сделать Хороший редактор. Но упустил возможность сделать Великолепный редактор, часть истории, такой, как, например, Vim и Emacs.

                    Тут всё просто: не было такой цели. Цель была именно «быстро сделать хороший редактор». А раз такую характеристику моему редактору даёт кто-то помимо меня, то наверное этой цели я достиг :)

                    >По поводу того, в пользу чего мы решили отказаться я не писал сознательно. Эта статья — про QScintilla. Потому что про нее мне уже есть что написать. А про альтернативу писать пока рано. История еще только начинается…

                    Тогда с нетерпением ждём продолжения!
                    0
                    Изучаю QScintilla не предмет использования в своем приложении для редактирования fb2-файлов.

                    У меня сложилось впечатление, что QScintilla не способен работать с файлами большого объема. При открытии больших файлов с включенным переносом слов QScintilla надолго «зависает» и очень тормозит при редактировании. Такую же проблему я увидел у текстового редактора JuffEd.

                    Однако другие проекты, использующие движок Scintilla (но не использующие QScintilla) легко справляются с файлами больших объемов, в том числе wxStyledTextCtrl из библиотеки wxWidgets 2.9.3 и конечно текстовый редактор SciTE. Файл открывается быстро, обрабатывается в фоновом режиме, не мешая пользователю его редактировать.

                    Подскажите, существует ли возможность заставить QScintilla, обрабатывать большие файлы в фоновом режиме не подвешивая на несколько секунд пользовательский интерфейс.
                      0
                      SciTE (по крайней мере в дефолтных настройках на Debian stable) не переносит строки.
                      У этого есть недостаток — горизонтальный скроллбар виден всегда, даже если длинных строк нет.
                      Либо переносы и тормоза, либо большие файлы и скроллбар.
                        0
                        Даже если в программе SciTE переносы включены, всё равно он быстро большие файлы открывает. Посмотрите как при этом ведет себя вертикальная полоса прокрутки если переместиться в середину документа. Заметно, что после прокрутки в фоновом режиме происходит пересчет ее позиции. Какое-то время после перемещения она как бы подстраивается, меняя свою позицию и размеры.
                          0
                          Поведение wxStyledTextCtrl можно увидеть собрав пример, поставляемый с библиотекой wxWidgets. Приложение легко открывает большие файлы с включенным переносом слов. Горизонтальный скроллбар при этом отсутствует, вертикальный пересчитывается в фоновом режиме. При изменении размера окна интерфейс не тормозит, а вертикальный скроллбар несколько секунд скачет взад-вперед.

                          wxWidgets-2.9.3/samples/stc
                            0
                            Разобрался. Чтобы перенос слов выполнялся в фоновом режиме нужно создавать наследника от класса Scintilla::Editor и переопределять функцию:

                            virtual bool SetIdle(bool) { return false; }
                            

                            Подобно тому, как это сделано в библиотеке wxWidgets:

                            bool ScintillaWX::SetIdle(bool on) {
                              if (idler.state != on) {
                                // connect or disconnect the EVT_IDLE handler
                                if (on)
                                  stc->Connect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(wxStyledTextCtrl::OnIdle));
                                else
                                  stc->Disconnect(wxID_ANY, wxEVT_IDLE, wxIdleEventHandler(wxStyledTextCtrl::OnIdle));
                                idler.state = on;
                              }
                              return idler.state;
                            }
                            
                            0
                            Соответственно пропатчить нужно класс QsciScintillaQt.

                            Only users with full accounts can post comments. Log in, please.