Современные редакторы кода стремятся стать универсальными рабочими средами, способными поддерживать огромное количество языков программирования и расширять свои функциональные возможности. Zed — это новейший редактор кода, построенный на технологиях Rust и WebAssembly, который предлагает уникальный подход к расширяемости за счет использования Wasm-модулей и стандарта Language Server Protocol (LSP). Если вы задумываетесь о создании расширения для нового, возможно даже вымышленного языка программирования, Zed предлагает отличную платформу для реализации подобных проектов. В этом материале мы познакомимся с фундаментальными принципами создания такого расширения, углубимся в специфику взаимодействия Wasm и LSP, а также рассмотрим практические сложности и решения, сопровождающие разработку. Понимание окружения и возможностей Zed Zed является редактором, который работает с расширениями, реализованными как вебассемблерные модули, запускаемые в изолированной среде.
Такой подход позволяет разработчикам писать расширения на Rust, используя zed_extension_api, компилировать их в wasm32-wasip1 и упаковывать вместе с конфигурацией и грамматиками Tree-sitter в tar.gz архив. При запуске этот архив загружается редактором, а WebAssembly модуль выполняется в песочнице Wasmtime. Таким образом, расширение не имеет доступа к ядру редактора и не способно его повредить, обеспечивая безопасность и стабильность работы. Кроме того, Zed содержит в себе поддержку Language Server Protocol — интероперабельного стандарта для взаимодействия редакторов и языковых серверов.
Вся интеллектуальная обработка кода — автодополнение, переход к определению, диагностика и т. п. — реализуется на стороне языкового сервера, который запускается отдельно. Расширение Zed отвечает за скачивание такого сервера, его запуск и взаимодействие с ним, а также за подгрузку грамматик языка по формату Tree-sitter, необходимых для подсветки синтаксиса. Структура расширения и роль ключевых компонентов При создании расширения для вымышленного языка первоочередно необходимо реализовать базовое расширение на Rust, имплементирующее трейты из zed_extension_api.
Особое внимание уделяется методу language_server_command, который указывает редактору, как именно запускать LSP для данного языка. Грамматика Tree-sitter играет важную роль, поскольку отвечает за анализ и визуальное оформление кода. Создание грамматики — задача, зачастую требующая изучения синтаксиса (даже условного) и написания запросов в формате .scm. Эта часть может оказаться самой сложной, но и самой важной, поскольку она обеспечивает базовую читаемость и навигацию в коде.
Языковой сервер запускается отдельно от Wasm-модуля расширения. Он является нативным исполняемым файлом, который редактор загружает и запускает. Коммуникация между редактором и сервером происходит через стандартные протоколы, что позволяет использовать любой язык реализации LSP. В случае вымышленного языка возможно встраивание в сервер всей логики интерпретатора, что открывает возможности для отладки и отображения результатов работы непосредственно в редакторе. Использование WebAssembly в двойном исполнении Особенностью подхода, используемого в Zed, является двойное применение Wasm.
С одной стороны, расширение написано на Rust и компилируется в Wasm для безопасного исполнения в редакторе. С другой стороны, внутри самого языкового сервера, особенно если он поддерживает интегрированное исполнение кода, может использоваться отдельный Wasm-движок (тот, что встроен в браузер), где выполняется runtime вымышленного языка. Это обеспечивает изоляцию, переносимость и хорошую производительность. Такой подход обеспечивает сильный уровень безопасности за счет sandboxing, в то время как гибкость и возможность расширения остаются на высоком уровне. Например, для языка Baml, который задумывался как язык для построения AI-ориентированных workflows с поддержкой отладки и живого исполнения, описанный метод позволил создать «песочницу» прямо внутри расширения и LSP.
Сложности интеграции и пути их решения Создавая расширение для вымышленного языка, разработчик сталкивается с рядом проблем. Одна из них — версия языка и его быстрая эволюция. Зачастую плотно связанные к конкретному проекту версии синтаксиса требуют от расширения гибкой системы загрузки грамматик и серверов в зависимости от контекста. Zed, несмотря на свои возможности, пока не полностью поддерживает такой продвинутый функционал. Одним из подходов к управлению версиями может стать разработка минимального менеджера загрузки версий LSP и грамматик на стороне расширения или самого языкового сервера.
Альтернативой является наличие внешнего глобального менеджера версий, подобного rustup или pyenv, но он ухудшает удобство и портативность, лишая расширение «одного клика» установки. Еще одна не до конца реализованная задача — поддержка расширенного функционала LSP в Zed, например, code lens, который мог бы служить для интеграции элементов UI, таких как кнопки «открыть playground» над функциями кода. В настоящее время обходным путем служат кодовые действия, посылающие команды на языковой сервер. Безопасность запуска исполняемого кода и доверие к расширению Поскольку языковой сервер запускается нативным процессом и выполняет код, он представляет угрозу безопасности пользователя, особенно если язык поддерживает динамическое выполнение кода, макросы и плагины. Механизмы защиты, реализованные в Zed для Wasm расширений, не распространяются на LSP, поэтому важно доверять используемому серверу и его исходному коду.
Открытый исходный код языковых серверов, как в случае Baml и rust-analyzer, снижает риски, но конечный пользователь должен осознавать потенциальную опасность. Некоторые редакторы требуют подтверждения доверия к проекту для запуска подобных сервисов, и этот механизм постепенно развивается. Практические советы для разработки расширения своего языка Для тех, кто готов пойти по пути создания расширения для Zed под собственный язык, важно помнить о предпочтительной архитектуре: минимальный Wasm-расширение для взаимодействия с редактором, скачивания и запуска LSP, а затем сам LSP, реализующий полную логику языка, службу подсказок, диагностики и даже исполнения. Рекомендуется тщательно изучить примеры работающих расширений, например, для Docker, Baml и других языков, так как API Zed минимален и требует понимания сопутствующих технологий. Очень полезно изучение и написание грамматик Tree-sitter, что дает базу для высококачественного синтаксического анализа и подсветки.