Gopls: Реализация
Последнее крупное обновление: 16 января 2024
В данном документе представлена высокоуровневая структура gopls, чтобы помочь новым участникам ориентироваться в проекте. Он не предназначен для полного описания реализации, а также даже ключевых компонентов; для этого лучше обратиться к документации пакетов (ссылки ниже) и другим комментариям внутри кода.
Диаграмма ниже показывает выбранные компоненты модуля gopls и их взаимосвязи согласно графу импортов Go. Тесты и инфраструктура тестов не показаны, а также вспомогательные пакеты и пакеты из модуля x/tools. Для краткости пакеты обозначены последним сегментом их названия, который обычно является однозначным.
Высота каждого блока соответствует его технической глубине. Некоторые блоки широкие и мелкие, такие как protocol, который определяет стандартный протокол; он в основном генерируется механически из схемы, предоставленной Microsoft.
Самый важный тип — DocumentURI, который представляет file: URL, идентифицирующий документ в клиентском редакторе. Также предоставляется Mapper, который отображает между различными системами координат, используемыми для позиций в исходном коде: UTF-8, UTF-16 и token.Pos.
Далее идут важные и широко используемые структуры данных:
-
Пакет file определяет основные абстракции клиента файла: его
Identity(URI и хэш содержимого), а такжеHandle(который дополнительно предоставляет версию и содержимое конкретного снимка файла). -
Пакет parsego определяет
File— разобранную форму Go-файла, включая его содержимое, синтаксическое дерево и отображения координат (Mapper и token.File). Пакет выполняет различные виды восстановления дерева, чтобы компенсировать недостатки обработки ошибок парсера Go. -
Пакет metadata определяет
Package— абстракцию метаданных Go-пакета, аналогичную выводуgo list -json. Метаданные создаются из go/packages, который заботится о вызовеgo list. (Пользователи сообщают, что он работает в какой-то степени с GOPACKAGESDRIVER для Bazel, хотя мы не поддерживаем тесты для этой сценарии.)Пакет также предоставляет
Graph— полный граф импортов для рабочей области; каждый узел графа — этоPackage.
Слой settings определяет структуру данных (по сути, большой деревянный объект) для параметров конфигурации gopls, а также его JSON-кодирование.
Слой cache является самым большим и сложным компонентом gopls. Он отвечает за управление состоянием, анализ зависимостей и инвалидацию:
Session — сеанс связи с клиентом;
Folder — папки, открытые клиентом;
View — представление конкретного дерева рабочей области с определёнными параметрами сборки;
Snapshot — состояние всех файлов в рабочей области после определённой операции редактирования;
содержимое всех файлов, будь то сохранённые на диск (DiskFile) или отредактированные и несохранённые (Overlay);
Cache — кэш вычислений в памяти с мемоизацией,
например, разбор go.mod файлов или построение индекса символов;
и Package, который содержит результаты проверки типов пакета из синтаксиса Go.
Слой кэша зависит от различных вспомогательных пакетов, включая:
-
Пакет filecache, который управляет постоянным, транзакционным, key/value хранилищем на основе файлов gopls.
-
Пакеты xrefs, methodsets и typerefs определяют алгоритмы для построения индексов информации, полученной при проверке типов, а также для кодирования и декодирования этих сериализуемых индексов в файловом кэше.
Вместе эти пакеты обеспечивают быстрый запуск, снижение потребления памяти и синергию между процессами, которые были реализованы при переосмыслении v0.12 и описаны в «Scaling gopls for the growing Go ecosystem».
Кэш также определяет драйвер go/analysis gopls, который выполняет модульный анализ (похожий на go vet) по всей рабочей области.
gopls также включает несколько анализов, которые не являются частью go vet.
Следующий слой определяет четыре пакета, каждый из которых обрабатывает файлы определённого языка:
mod для go.mod файлов;
work для go.work файлов;
template для файлов в синтаксисе text/template; и
golang, для файлов на самом Go.
Этот пакет, безусловно, самый большой, предоставляет основные функции gopls:
навигацию, анализ и рефакторинг Go-кода.
Как представляют себе большинство пользователей, этот пакет и есть gopls.
Пакет server определяет реализацию службы LSP, при этом для каждого типа запроса LSP предусмотрен свой метод-обработчик. Каждый обработчик переключается по типу файла и передаёт управление одному из четырёх языковых специфичных пакетов.
Пакет lsprpc обеспечивает связь между интерфейсом службы и нашим сервером jsonrpc2.
Следует учитывать, что диаграмма представляет собой граф зависимостей — «статическую» точку зрения на структуру программы. Более динамическая точка зрения упорядочила бы пакеты по последовательности их обнаружения во время обработки конкретного запроса; в таком представлении нижний уровень представлял бы «канал» (протокол и команда), следующий уровень включал бы пакеты, связанные с RPC (lsprpc и server), а функциональные возможности (например, golang, mod, work, template) находились бы на верхнем уровне.
Пакет cmd определяет интерфейс командной строки команды gopls, вокруг которого основной пакет gopls является всего лишь тривиальной обёрткой. Обычно команда запускается без аргументов, что приводит к запуску сервера и его постоянному ожиданию подключений.
Также она предоставляет ряд субкоманд, которые запускают сервер, выполняют один запрос к нему и завершаются, обеспечивая традиционный пакетный доступ к функциональности сервера. Эти субкоманды в основном предоставляются в качестве средства отладки; однако см. https://go.dev/issue/63693.
Исходные файлы этой документации можно найти в golang.org/x/tools/gopls/doc.