Автозакрытие скобок в Geany

    Если вы пользовались редактором исходного кода Geany, то наверняка сталкивались с плохо предсказуемым поведением стандартного автозакрытия скобок и кавычек. Если не пользовались — рекомендую попробовать.

    Geany замечательный редактор, но на протяжении всей его истории автозакрытие работало так:

    func())
    

    или так:

    func()
    {
        }}
    

    Настало время поставить крест на ручной расстановке скобочек. Встречайте, новый режим автозакрытия в Geany:

    image

    Преамбула


    В Geany есть «нативный» режим автодополнения и автозакрытия (чтобы не путаться: автодополнение — это для функций и методов, а автозакрытие — для парных скобок и кавычек). Казалось бы, суть алгоритма проста: отслеживаем нажатие одного символа и вставляем соответствующий ему парный. Однако существует целая груда подводных камней, нырнув в которую можно набить много шишек.

    Проблема 1: люди привыкли к блокноту. Многие машинально сразу ставят и открывающую скобку и закрывающую, результат не заставляет себя ждать:

    f())
    f();)
    f() {}}
    f("abc"");
    

    Решили удалить скобочку при помощи бекспейса? Получайте:

    f()) -> f))
    

    Такой режим автозакрытия только оказывает медвежью услугу, поэтому его проще сразу выключить.

    Проблема 2: в разных языках скобки и кавычки имеют свой смысл. Например, в C++ фигурные скобки обычно определяют границы блока, а в Bash это может просто быть частью переменной. Geany является многоязыковым редактором, и здесь не мешало бы учитывать особенности каждого языка в отдельности.

    Проблема 3: во многих редакторах при выделении куска текста и нажатии на скобку/кавычку выделенный текст «оборачивается» в этот символ. В Geany этот текст просто удаляется.

    Проблема 4: возможно вы этого никогда не подозревали, но в Geany при зажатии Shift выделение переходит в блочный режим (Multiline Selection). Стоит ли говорить, что автозакрытие в этом режиме не поддерживается никак. И его довольно сложно реализовать, так как блочное выделение захватывает «виртуальные» пробелы:


    А ещё в редакторе есть буфер обмена, буфер отмены/повтора действия, горячие клавиши и всё это как-то должно вместе работать…

    Решение


    Поскольку такие проблемы совсем не к лицу блокноту 21-го века, было решено реализовать автозакрытие в виде дополнения к Geany. Благо, API позволяет легко это сделать. Сказано — сделано, волевым решением плагин был включён в проект geany-plugins и будет доступен в качестве альтернативного режима автозкарытия в следующей версии Geany (для этого достаточно будет поставить галочку в меню плагинов).

    Что он умеет на данный момент? А умеет довольно много:

    • разумеется, автозавершение символов { }, [ ], ( ), " ", ' ', < >, ` `
    • выключать/отключать автозавершение внутри строк и комментариев
    • умное завершение: парная скобка удаляется автоматически, подавляется двойное закрытие
    • помещает выделенный текст в скобки, сохраняя выделение
    • автоматически завершаются функции: sin(|); и структуры/классы: struct {|};
    • по нажатию Tab курсор прыгнет к парному символу (как в Eclipse)
    • по нажатию Shift+BackSpace удаляется парная скобка, а для фигурных скобок — убирается отступ (как на скриншоте в начале статьи)

    Помимо всего прочего учитываются особенности разных языков: для си-подобных, например, достаточно выделить кусок текста и нажать {, чтобы появился новый блок; для Bash включается автозакрытие обратной кавычки; для HTML завершаются символы <> и т. п. Также для си-подобных языков сильно улучшены авто-отступы.

    Как оно работает?


    Geany основан на Scintilla, которая предоставляет довольно разнообразный API в части работы с буфером текста. Идея как обычно проста: отслеживаем нажатия клавиш и в зависимости от обстоятельство реагируем на окружающую среду. Но не тут-то было: в Scintilla был фатальный недостаток, из-за которого API позволяет отслеживать все нажатия клавиш, кроме бекспейса. Мелочь? А как прикажете отслеживать событие удаления символа?

    В итоге пришлось плюнуть и соорудить мегакостыль обработки событий, минуя Scintilla API и используя чистый GLib. Сложность этого костыля в том, что нужно корректно обрабатывать открытие и закрытие документа (а вкладок может быть мнооого), чтобы ненароком не прицепить несколько обработчиков на одно событие или вообще прицепить не туда. Например, типичный баг в плагине Addons — обработчик прицеплен к главному окну. Теперь события плагина срабатывают везде — даже в терминале. В общем, если когда-нибудь вам придётся писать плагин для Geany, а стандартного Scintilla API будет не хватать, вы всегда сможете найти стабильную реализацию костыля в плагине Autoclose.

    Остальное — скучная череда свичей и условий. Из интересного — проверка на валидность указателя на документ — doc. На этом нюансе много плагинов полегло: разработчики по незнанию проверяют аргумент функции как все нормальные люди:

    if(NULL == doc)
        return; //error!
    

    Проблема Geany в том, что doc строго говоря может быть и не NULL, но при попытке к нему обратиться будет сегфолт. Как так? Оказывается, внутри Geany активно практикует повторное использование указателей. Если звёзды сложатся неудачно и в момент проверки документ будет закрыт, его указатель может стать некорректным или вообще указывать на совершенно другой документ. Чтобы избежать коллизий нужно проверять документ макросом DOC_VALID.

    Нужно отметить, что сам Geany наступает на свои грабли: загадочный сегфолт, гадание на ассемблерном дампе и результат — проверка doc на NULL в самой ключевой функции загрузки документов.

    Как установить?


    На данный момент дополнение находится в git head, то есть оно ещё не было выпущено вместе с релизом Geany. Плагин появится только в версии 1.24, но его можно использовать уже сейчас одним из следующих способов:
    — скачать экспериментальный, ещё не выпущенный в релиз Geany 1.24 из так называемых «ночных билдов» — там уже всё есть: nightly.geany.org
    — скачать плагин отдельно и попытаться его собрать под старую версию
    — попытаться взять библиотеку autoclose из ночных билдов и подсунуть её в 1.23, но гарантий никаких

    Внимание! Второй способ только для экстремалов.
    — нужно скачать исходники geany-plugins из гита: github.com/scriptum/geany-plugins
    — скачать исходники geany-plugins для версии 1.23: www.geany.org/Download/Releases
    — скопировать плагин autoclose в версию 1.23 и попытаться собрать плагин внутри исходинков 1.23

    Сразу не соберётся, так как нужно прописать плагин во все autotools-скрипты, которые разбросаны не очень очевидным образом.

    Пусть вас не пугает использование ночной сборки: все правки в Geany тщательно тестируют и проверяют, более того, в 1.24 исправлено большое количество багов, утечек, добавлено много полезных фич, я бы сказал, что она намного стабильнее «стабильной» 1.23. После установки нужно будет включить плагин Autoclose в меню плагинов и в бой.

    Что дальше?


    Плагин пока решено оставить плагином. После боевого крещения релизом он перекочует в Geany (Autoclose останется плагином, но будет частью самого Geany, а не проекта geany-plugins), после чего скорее всего будет удалена старая функциональность непосредственно из редактора. Проблему автодополнения он не решает, это тема для отдельной (и довольно большой) работы, в IRC ходят слухи, что к Geany пытаются прикрутить clang, но тссс...:)

    А пока, в период предрелизного томления, предлагаю читателям сообщать о багах или высказывать пожелания. В частности, поддержки языков, о которых я имею весьма смутные представления, там нет.
    • +15
    • 5,5k
    • 9
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 9

      –1
      Сколько пользуюсь — проблем с автозакрытием скобок/кавычек не возникало, всегда устраивало как они работают.
      Хотя может кому то и будет полезен данный плагин.
        0
        P.S. Да и вообще в принципе никогда не понимал людей которым не удобно использовать автозакрытие кавычек/скобок. А потом сидят и втыкают где пропустили закрывающую кавычку/скобку.
        0
        Это комментарий про vim, который ты, читающий эти строки, хотел написать.
          0
          > В итоге пришлось плюнуть и соорудить мегакостыль обработки событий, минуя Scintilla API и используя чистый GLib

          А пытались ли Вы связаться с авторами Scuntilla и обсудить этот вопрос (или выслать им патч выполняющий подобный функционал)?
            0
            Нет, не пытался даже. Я планировал отслеживать нажатия определённых клавиш, включая Shift и Backspace, а в API предусмотрена лишь передача кодов введенных символов, т.е. ни Shift, ни Backspace тут уже не вписываются, так как это клавиши, а не символы. Там предусмотрено событие DELETE, то есть удаление символа, но оно опять же не позволяет узнать какое это удаление — то ли Delete, то ли Backspace что в случае этого плагина было важно. То есть требования очень специфические, но ради небольшого плагина переделывать API движка, который используется в десятках редакторов — я думаю это перебор:)
              0
              Я поэтому и спросил. Т.к Scintilla это все же центральный компонент для немалого количества редакторов для программистов, полагаю, большинство авторов данных редакторов сталкивались с подобной проблемой. Быть может и какое-либо решение для этого уже предлагалось. ИМХО, обращение, в любом случае, было бы не лишним.

              P.S По поводу пожеланий. Лично мне в Geany сильно не нравится то, что горизонтальный скроллинг не учитывает реальное состояние страницы и включен всегда, даже когда это не нужно. Авторы утверждают, что это особенность Scuntilla и отказываются это исправлять до тех пор, пока это не появится в самом Scintilla.
                0
                Возможно и не сталкивались — большинство редакторов на Scintilla (тот же Notepad++, о котором я могу более-менее судить) не претендуют на функциональность IDE и автодополнения символов а-ля Eclipse там нет. Единственный блокнот, где есть нормальное автодополнение — Sublime, но там похоже всё самописное. Учитывая примитивную реализацию вряд ли разработчики столкнулись с проблемами (считал введеный символ, записал парный — такой плагин в строк 15 уместится). Я могу гарантировать что патч, затрагивающий код Scintilla, не примут, а пропихивание сразу двух патчей в два не связанных друг с другом проекта в мои временные лимиты просто не укладывается. В Geany и так слишком консервативная разработка и даже багфиксы ждут своего часа годами.

                > сильно не нравится то, что горизонтальный скроллинг не учитывает реальное состояние
                Как там сказано, это for performance reasons. Я обычно всегда использую перенос строк и этот скроллбар не так уж и мешает. Но я не разработчик Scintilla и даже не контрибьютор, разбираться с компонентом редактирования желания не было — как раз производительность Scintilla даёт изюминку Geany:) В Geany ML были поползновения в сторону другого компонента редактирования — GtkSourceview, но на мой взгляд его производительность значительно отстаёт, хотя он и более OpenSource-вейный.

                Отказываются исправлять опять же не просто так: Geany зависит от кодовой базы Scintilla и использует её непосредственно внутри себя. Т.е. если разработчики начнут ковырять движок Scintilla, им придётся потом заниматься его поддержкой. Учитывая что автор Geany перестал им заниматься, у текущей команды из двух с половиной человек нет просто на это ресурсов.
            0
            Учитывая что автор Geany перестал им заниматься, у текущей команды из двух с половиной человек нет просто на это ресурсов


            Понятно, спасибо. Если мне не изменяет память, все три основных разработчика (Энрико, Френк и Ник) еще два года назад решили отказаться от разработки «Geany» из-за потери интереса к проекту. Да и у Коломбана (нового лидера) не было больших планов по развитию. Что-то изменилось с того времени? Каковы планы текущей группы разработчиков?

              0
              Текущая команда пока не планирует особо сильно менять Geany, потихоньку исправляют ошибки, не более того. Единственное, что возможно действительно серьёзное появится — это интеграция с clang, но вот устранять архитектурные проблемы никто не будет. Основные изменения в настоящий момент вносятся внешними разработчиками.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое