Nvim-gdb запитує місця зупинки у налагоджувача через сторонній канал. Для цього створюється локальне гніздо, яке з’єднується зі стороннім каналом. І виконувалося це через сторонній процес Пітону. Однак я нещодавно зрозумів, що в Neovim є вбудований інтерпретатор повноцінної мови програмування Lua. Чому було б не спробувати? Виявилося, що зусилля були не марними і вилилися у цілковиту переробку програми розширення.

Огляд

По-перше, з Lua легко почати. Навіть якщо в її стаднартна бібліотека не справляє враження, все ж її можливості легко розширити з допомогою Luarocks. Так у проекту з’явився власний сценарій розгортання install.sh. Він встановлює спочатку власне luarocks у місцеву теку, потім кілька модулів, які знадобляться для роботи з регулярними виразами і POSIX. Нарешті, готує компілятор Moonscript.

По-друге, особисто мені не до вподоби багатослівність Lua, тому й Moonscript. Він не потребує постійного друкування ключових слів, зате код виходить компактний і виразний водночас. Більше того, він набагато зручніший, коли справа доходить до об’єктно-орієнтованого програмування.

Тож я почав поступово замінювати скрипт за скриптом Vim модулями Lua, написаними мовою Moonscript. Зусилля не виявилися марними, і тепер більшість коду перенесено.

Статистика мови

Деталі

Читальність коду

Moonscript — це звичайна і виразна мова програмування. Порівняймо один і той самий шмат коду до та після перенесення. Можна відразу помітити, що більше немає безглуздих let, щоб присвоїти змінну, call, щоб викликати функцію, ніякого більше сміття при визначенні функції function! і endfunction тощо.

Vim script у Moonscript

Доступ до API

Деякі обхідні шляхи зараз зовсім не потрібні завдяки Neovim API. Наприклад, є спосіб ідентифікувати вікна, вкладки і буфери. Їхні ідентифікатори можна запам’ятовувати, і більше не стрибати туди-сюди аби зорієнтуватися (це я про прибраний шматок у попередній ілюстрації).

Об’єктна орієнтовність

Moonscript дозволяє скористатися перевагами інкапсуляції, успадкування і поліморфізму. Наприклад, специфіки різних налагоджувачів успадковуються від базового. Обробники станів спільні, переходи між станами автомату у кожного відмінні.

class PdbScm extends BaseScm
    new: (...) =>
        super select(2, ...)
        @addTrans(@paused, r([[(?<!-)> ([^(]+)\((\d+)\)[^(]+\(\)]]), m, @jump)
        @addTrans(@paused, r([[^\(Pdb\) ]]),                         m, @query)
        @state = @paused

Розділення коду

Перероблення коду — це хороша нагода переглянути взаємодію об’єктів. Щоб переконатися, що вони взаємодіють чисто і прямолінійно. В якості ілюстрації, розгляньмо, як запускається сеанс налагодження. Самостійні об’єкти @client, @cursor ініціюються першими, потім ті, які їх потребують для функціонування, такі як @breakpoint і @win. Залежні класи використовують свої залежності тільки через відкриті інтерфейси контрольованим чином.

class App
    new: (backendStr, proxyCmd, clientCmd) =>
        -- Створити нову вкладку для налагодження і розбити горизонтально
        V.exe "tabnew | sp"

        -- Перерахувати вікна, що вийшли.
        wins = V.list_wins!
        table.sort wins
        wcli, wjump = unpack(wins)

        @backend = require "gdb.backend." .. backendStr

        -- Перейти до іншого вікна і запустити клієнт налагоджувача
        @client = Client(wcli, proxyCmd, clientCmd)

        -- Підготувати слідкування за активним рядком
        @cursor = Cursor()

        -- Підготувати слідкування за місцями зупинки
        @breakpoint = Breakpoint(@client\getProxyAddr!)

        -- Підготувати віконну підсистему
        @win = Win(wjump, @client, @cursor, @breakpoint)

        -- Підготувати автомат
        @scm = @backend\initScm(@cursor, @win)

        -- Автомат вже має бути готовий, запустити налагоджувач!
        @client\start!

Набір тестів

Саме перенесення було можливе завдяки набору тестів. Разом з цим, так само покращилися й тести: час виконання зменшився удвічі, надійність збільшилася, коли прибралися кілька видів гонок.

Історія тестування у Travis-CI

Висновки і плани

  • Взятися за переробляння — хороша нагода вивчити і покращити базу коду.

  • З оновленим розділеним кодом має бути тепер легше і підтримувати і додавати нові можливості, наприклад, delve.

  • Теоретично можливо обійтися без окремого інтерпретатора lua5.1 і використати сам Neovim як інтерпретатор Lua для початкового розгортання.