Модуль предоставляет множество языковых функций, таких как дополнение кода, навигация по коду или поиск ссылок на основе протокола языкового сервера.
Как только модуль будет активирован на странице модулей, в диалоге настройки Kate появится новый раздел под названием «Клиент LSP».
Далее, в описании пунктов меню, также приводятся соответствующие команды (если таковые имеются). В документации этих команд доступны дополнительная информация и интерпретация (они могут варьироваться в зависимости от используемого языка программирования). Фраза «текущий символ» относится к символу, соответствующему текущей позиции курсора, как это определено языком и реализацией сервера.
- →
[textDocument/definition] Перейти к определению текущего символа.
- →
[textDocument/declaration] Перейти к объявлению текущего символа.
- →
[textDocument/typeDefinition] Перейти к определению типа текущего символа.
- →
[textDocument/references] Найти ссылки на текущий символ.
- →
[textDocument/implementation] Найти реализации текущего символа.
- →
[textDocument/documentHighlight] Подсветить ссылки на текущий символ в текущем документе.
- →
[textDocument/hover] Информация о текущем символе при наведении.
- →
[textDocument/formatting] [textDocument/rangeFormatting] Форматировать текущий документ или текущий выделенный фрагмент.
- →
[textDocument/rename] Переименовать текущий символ.
- →
[textDocument/codeAction, workspace/executeCommand] Рассчитывает и применяет быстрое исправление для диагностики в текущей позиции (или строке).
- →
Показывать документацию по выделенному объекту в списке автодополнения.
- →
Также показывать справку сигнатуры в списке автодополнения.
- →
Запрашивать включение объявления символа при запросе ссылок.
- →
Автоматически добавлять пару скобок после автоматического дополнения названия функции.
- →
Показывать информацию при наведении (указателя мыши). Независимо от значения этого параметра, запрос этих данных всегда возможно инициировать вручную.
- →
[document/onTypeFormatting] Форматировать части документа при наборе определённых символов пользователем. Например, при наборе символа переноса строки возможно создать отступ или выполнить любое другое действие, определяемое сервером LSP. Сценарии расстановки отступов в редакторе могут пытаться сделать то же самое (в зависимости от режима расстановки), поэтому не рекомендуется включать оба соответствующих параметра одновременно.
- →
Отправлять для обработки сервером отдельные части документа во время редактирования, а не весь документ (если на сервере предусмотрена поддержка такого режима обработки).
- →
Предоставить временный визуальный сигнал после выполнения перехода (к определению, объявлению и так далее).
- →
[textDocument/publishDiagnostics] Обрабатывать и показывать уведомления о диагностике, отправляемые сервером.
- →
Добавить подсветку текста для диапазонов, указанных в диагностике.
- →
Отмечать в документе строки, указанные в диагностике.
- →
Перейти на вкладку диагностики в панели модуля.
- →
Закрыть все вкладки, не относящиеся к диагностике (например, ссылки), на панели модуля.
- →
Перезапустить сервер LSP текущего документа.
- →
Остановить все серверы LSP и затем перезапустить их.
Клиент LSP Client позволяет перейти к любому идентификатору в проекте или текущем файле. Чтобы перейти к какому-либо идентификатору в файле, воспользуйтесь панелью «Клиент LSP: структура идентификаторов», расположенной в правой части окна Kate. В этой панели приводится список всех идентификаторов, обнаруженных сервером в текущем документе.
По умолчанию все идентификаторы упорядочены по порядку в документе, но также возможно выполнить сортировку по алфавиту. Для этого следует щёлкнуть по панели правой кнопкой мыши и выбрать пункт «Сортировать по алфавиту».
По умолчанию панель отображает идентификаторы в виде дерева, но с помощью контекстного меню также возможно представить их в виде списка.
Чтобы перейти к любому идентификатору в проекте, следует открыть диалог перехода с помощью комбинации клавиш Ctrl+Alt+p. При открытии диалог пуст, но по мере набора текста пользователем будут появляться соответствующие идентификаторы. Качество совпадений, а также возможности фильтрации зависят от используемого сервера. Например, clangd поддерживает неточную фильтрацию, а какой-либо другой сервер может не поддерживать эту возможность.
Поддерживается команда переключения заголовка источника clangd. Чтобы переключить заголовок источника в проекте C или C++ следует либо выбрать пункт контекстного меню «Переключить заголовок источника», либо нажать клавишу F12.
Для быстрого перехода к идентификатору возможно навести указатель мыши на символ и затем использовать комбинацию Ctrl + левая кнопка мыши.
На странице настройки модуля возможно установить стандартные значения некоторых перечисленных выше элементов меню. На этой странице также имеется поле, в котором возможно указать путь к файлу конфигурации сервера. Это файл JSON, который позволяет указать сервер LSP для запуска (и последующего обмена данными с помощью stdin/stdout). Для удобства включена конфигурация по умолчанию (она доступна на странице настройки модуля). Чтобы сделать дальнейшие пояснения доступнее, здесь приводится фрагмент этой конфигурации:
{
"servers": {
"bibtex": {
"use": "latex",
"highlightingModeRegex": "^BibTeX$"
},
"c": {
"command": ["clangd", "-log=error", "--background-index"],
"commandDebug": ["clangd", "-log=verbose", "--background-index"],
"url": "https://clang.llvm.org/extra/clangd/",
"highlightingModeRegex": "^(C|ANSI C89|Objective-C)$"
},
"cpp": {
"use": "c",
"highlightingModeRegex": "^(C\\+\\+|ISO C\\+\\+|Objective-C\\+\\+)$"
},
"d": {
"command": ["dls", "--stdio"],
"url": "https://github.com/d-language-server/dls",
"highlightingModeRegex": "^D$"
},
"fortran": {
"command": ["fortls"],
"rootIndicationFileNames": [".fortls"],
"url": "https://github.com/hansec/fortran-language-server",
"highlightingModeRegex": "^Fortran.*$"
},
"javascript": {
"command": ["typescript-language-server", "--stdio"],
"rootIndicationFileNames": ["package.json", "package-lock.json"],
"url": "https://github.com/theia-ide/typescript-language-server",
"highlightingModeRegex": "^JavaScript.*$",
"documentLanguageId": false
},
"latex": {
"command": ["texlab"],
"url": "https://texlab.netlify.com/",
"highlightingModeRegex": "^LaTeX$"
},
"go": {
"command": ["go-langserver"],
"commandDebug": ["go-langserver", "-trace"],
"url": "https://github.com/sourcegraph/go-langserver",
"highlightingModeRegex": "^Go$"
},
"python": {
"command": ["python3", "-m", "pyls", "--check-parent-process"],
"url": "https://github.com/palantir/python-language-server",
"highlightingModeRegex": "^Python$"
},
"rust": {
"command": ["rls"],
"path": ["%{ENV:HOME}/.cargo/bin", "%{ENV:USERPROFILE}/.cargo/bin"],
"rootIndicationFileNames": ["Cargo.lock", "Cargo.toml"],
"url": "https://github.com/rust-lang/rls",
"highlightingModeRegex": "^Rust$"
},
"ocaml": {
"command": ["ocamlmerlin-lsp"],
"url": "https://github.com/ocaml/merlin",
"highlightingModeRegex": "^Objective Caml.*$"
}
}
}
Обратите внимание, что каждая запись «command» может быть массивом или строкой (в этом случае она будет поделена на записи массива). Кроме того, также учитывается запись верхнего уровня «global» (рядом с записью «server»); подробнее об этом далее. Поиск указанного исполняемого файла выполняется обычным способом, то есть с помощью переменной PATH. Если исполняемый файл установлен в каком-либо нестандартном расположении, может потребоваться развернуть соответствующий путь. В расположении, которое хранится в обычной переменной PATH, можно воспользоваться (символической)ссылкой или сценарием-обёрткой. Как показано выше, также можно указать «path» (расположение, в котором будет выполнен поиск после стандартных мест).
Во всех записях «command», «root» и «path» будет выполняться расширение переменных.
Инструкцией «highlightingModeRegex» можно воспользоваться для привязки режима подсветки, который используется Kate, к идентификатору языка сервера. Если регулярное выражение не указано, используется сам идентификатор языка. Если запись «documentLanguageId» установлена в значение «false», идентификатор языка не предоставляется серверу при открытии документа. Это может подойти для серверов, которые определяют тип документа точнее, чем способ на основе режима Kate.
Кроме того, каждый объект записи сервера может также иметь запись «initializationOptions», данные которой передаются серверу как часть метода «initialize». Если такая запись присутствует, данные записи «settings» будут переданы серверу с помощью уведомления «workspace/didChangeConfiguration». Как «completionTriggerCharacters», так и «signatureTriggerCharacters» можно указать как объект JSON со строковыми участниками «exclude» и/или «include». Они будут использоваться, соответственно, для исключения или добавления символов в соответствующий набор переключателей, который предоставляется сервером.
Применяются различные этапы переопределения и слияния.
Пользовательская конфигурация (загруженная из файла) переопределяет (внутреннюю) конфигурацию по умолчанию
Запись «lspclient» в конфигурации проекта
.kateprojectпереопределяет предыдущие записиПолученная в результате обработки запись «global» используется как дополнение (не переопределение) любой из записей сервера
Для каждой комбинации (root, servertype) используется один экземпляр сервера. Если значение «root» указано как абсолютный путь, оно используется без обработки. В противном случае оно считается указанным относительно «projectBase» (как определено в модуле проектов), если это применимо, или иным образом связанным с каталогом документа. Если значение не указано и «rootIndicationFileNames» представляет собой массив имён файлов, то выбирается родительский каталог текущего документа, содержащий такой файл. Альтернативный вариант (если не указано значение «root» и «rootIndicationFilePatterns» представляет собой массив шаблонов имён файлов) — выбирается родительский каталог текущего документа, соответствующий шаблону имени файла. Последний резервный вариант для «root» — домашний каталог пользователя. Полученное в результате такой обработки значение «root» определяет, требуется ли для документа отдельный экземпляр сервера. Если такой экземпляр нужен, значение «root» будет передано как rootUri/rootPath.
Рекомендуется не указывать значение root, так как оно не так важно для сервера (хотя ситуации могут быть разными). Если экземпляров сервера будет немного, они будут работать эффективнее и обеспечивать «более широкое» поле зрения, чем в случае использования многих отдельных экземпляров сервера.
Как упоминалось ранее, в некоторых записях выполняется расширение переменных. Использование этой возможности в сочетании со «сценарием-обёрткой» позволяет гибко подстроиться к самым разным обстоятельствам. Например, рассмотрим сценарий разработки Python, состоящий из нескольких проектов (например, репозиториев git), каждый из которых имеет свою виртуальную среду. При использовании стандартной конфигурации у сервера языка Python не будет информации о виртуальной среде. Но это можно исправить следующим образом. Сначала следует ввести следующий текст в поле «Параметры сервера пользователя» модуля клиента LSP:
{
"servers":
{
"python":
{
"command": ["pylsp_in_env", "%{Project:NativePath}"],
"root": "."
}
}
}
Корневая запись выше относится к каталогу проекта и обеспечивает запуск отдельного языкового сервера для каждого проекта (что необходимо в этом случае, так как каждый проект имеет собственную уникальную виртуальную среду).
pylsp_in_env — это небольшой «сценарий-обёртка», который следует поместить в PATH со следующим (требует редактирования) содержимым:
#!/bin/bash cd $1 # запустить сервер (python-lsp-server) в виртуальной среде # (т.е. с заданными переменными виртуальной среды) # поэтому источником будет виртуальная среда source XYZ # сервер и аргументы могут быть разными exec myserver
Это только один из примеров более общего шаблона, с которым может быть немного удобнее работать (описание доступно в разделе Среда выполнения ниже).
Каждый сервер LSP имеет свой собственный способ настройки; для конфигурации могут использоваться средства, специфичные для языка или инструмента, например, tox.ini (в том числе для python), .clang-format для форматирования в стиле C++. Такая конфигурация затем может также использоваться другими (отличными от LSP) инструментами (такими как tox или clang-format). Кроме того, некоторые серверы LSP загружают конфигурацию из пользовательских файлов (например, .ccls). Более того, настраиваемая конфигурация сервера также может быть передана с помощью LSP (протокол): смотрите вышеупомянутые записи «initializationOptions» и «settings» в конфигурации сервера.
Поскольку применяются различные уровни переопределения и слияния, в приведённом далее примере определённые пользователем параметры конфигурации клиента корректируют некоторые из параметров конфигурации python-language-server.
{
"servers": {
"python": {
"settings": {
"pyls": {
"plugins": {
"pylint": {
"enable": true
}
}
}
}
}
}
}
К сожалению, конфигурация сервера LSP зачастую не очень хорошо документирована, поэтому только изучение исходного кода позволяет найти способы настройки и набор доступных параметров конфигурации. В частности, сервер из приведённого выше примера поддерживает намного больше параметров в «settings». Ознакомьтесь с документацией другого клиента LSP, содержащей примеры серверов других языков и соответствующие параметры настройки, которые возможно легко и просто преобразовать в конфигурацию JSON, которая описана в этом руководстве.
Можно включить «форматирование при сохранении» в диалоге настройки параметров LSP.
Иногда диагностические данные могут оказаться не слишком полезны. Диагностика может быть лишней, особенно если диагностических сообщений слишком много (часто однотипных). В некоторых случаях поток сообщений можно скорректировать по языку программирования (на сервере). Например, механизм настройки clangd позволяет корректировать некоторые аспекты диагностики. Тем не менее, способ корректировки не всегда очевиден, а также желаемая корректировка может быть невозможна из-за ограничений сервера или внутренней ошибки.
Поэтому в модуле предусмотрена поддержка подавления диагностических сообщений (она похожа, например, на подавление valgrind). Самую тонкую настройку возможно выполнить с помощью ключа «suppressions» в (объединённой) конфигурации JSON.
{
"servers": {
"c": {
"suppressions": {
"rulename": ["filename", "foo"],
"clang_pointer": ["", "clang-tidy", "clear_pointer"],
}
}
}
}
Каждое (корректное) правило имеет произвольное имя и определяется массивом длины 2 или 3, в котором указано регулярное выражение для установления соответствия (полного) имени файла, регулярное выражение для установления соответствия диагностического текста и необязательное регулярное выражение для установления соответствия (диапазона исходного кода) текста, к которому применяется диагностика.
Помимо вышеупомянутой тонкой настройки, в контекстном меню на вкладке диагностики также предусмотрено добавление и удаление подавлений, которые точно соответствуют определённому диагностическому тексту (либо глобально (любой файл), либо локально (конкретный рассматриваемый файл)). Эти подавления хранятся и загружаются из конфигурации сеанса.
Одно дело описать настройку (нестандартного) сервера LSP для какого-либо языка программирования, другое же — сделать так, чтобы сервер работал без каких-либо проблем. К счастью, обычно сервер работает именно так. Впрочем, иногда могут возникать сложности из-за случайно допущенных неточностей в параметрах настройки или более серьёзных проблем с самим сервером. Второй вариант может проявлять себя в виде сообщения о неудачных попытках запустить сервер, которое появляется на вкладке вывода данных Kate. Следует учитывать, что такие сообщения представляют собой лишь сообщения верхнего уровня или хода обработки, а не подробные диагностические сообщения, а также могут не содержать данных о том, что фактически является другим процессом (сервер LSP).
Обычный способ диагностики заключается в том, чтобы добавить к команде запуска (языкового сервера) один или несколько флагов; это включает (дополнительное) журналирование (в какой-либо файл или стандартный поток ошибок), если оно не выполняется по умолчанию. Если затем запустить Kate из командной строки, возможно, получится узнать о проблеме больше.
Информацию также можно получить, изучив обмен данными между клиентом LSP Kate и сервером LSP. Опять же, на последнем обычно есть средства трассировки. Клиент LSP также предоставляет дополнительную трассировку отладки (в stderr) при вызове Kate с должным образом экспортированной переменной среды LSPCLIENT_DEBUG=1.
Приведённый выше пример виртуальной среды python — лишь один из вариантов «среды выполнения», работающей отличным и отдельным от обычной среды основной системы образом. Этого можно достичь с помощью различных параметров переменных (например, virtualenv) или настройки (s)chroot (переключения на другой каталог в качестве нового корня), контейнера (например, podman, docker) или ssh-сеанса к другой основной системе. В каждом из этих случаев «другая среда» определяется «префиксом выполнения». Это означает, что в этой другой среде программу можно вызвать/запустить с помощью «префикса» (программа и аргументы), добавленного к предполагаемому вызову. Например, podman exec -i containername или ssh user@host.
В частности, как было сделано в приведённом ранее примере virtualenv, можно запустить сервер LSP в такой отдельной среде (например, контейнер со всеми необходимыми проекту зависимостями). «Ручной» подход, описание которого приведено выше, имеет следующий недостаток: выполняется замена стандартной командной строки LSP, её потом необходимо указать и продублировать ещё раз в сценарии-обёртке. Кроме того, в некоторых других вышеупомянутых примерах «путь к пространству имён» основной системы (как он виден редактору) может отличаться от соответствующего для среды. Чтобы решить эти вопросы более системно (а не просто с помощью «индивидуального» подхода), можно дополнить конфигурацию.
Например, в конфигурации .kateproject можно указать следующее. Очевидно, что «фиктивные» комментарии включать не следует.
{
// это также может быть массив объектов
"exec": {
"hostname": "foobar"
// команда также может быть массивом строк
"prefix": "podman exec -i foobarcontainer",
"mapRemoteRoot": true,
"pathMappings": [
// возможна любая из следующих форм
// также существует более автоматизированная альтернатива, подробнее о ней далее/ниже
[ "/dir/on/host", "/mounted/in/container" ]
{ "localRoot": "/local/dir", "remoteRoot": "/remote/dir" }
]
},
"lspclient": {
"servers": {
"python": {
// это устанавливает соответствие или выполняет объединение для объекта выше
"exec": { "hostname": "foobar" },
// ограничивает этот сервер корневым каталогом этого проекта,
// чтобы он не использовался для других проектов, которые могут быть открыты
// (для других серверов уже могут использоваться определённые корневые каталоги, но для python это обычно не так)
"root": "."
},
"c": {
// как выше
"exec": { "hostname": "foobar" },
"root": "."
}
}
}
}
Как работает приведённый выше код? Как уже говорилось, часть с lspclient из этого кода добавляется в глобальную конфигурацию, поэтому будет найден раздел exec (для указанных языков). Поиск другого объекта (который указывает соответствующий hostname) выполняется либо в разделе exec, либо в разделе lspclient, и соответствующий объект служит основой для объединения. В результате командная строка сервера LSP (для C и python) будет добавлена к указанному (с подстановкой переменных) префиксу и, следовательно, он будет запускаться в указанном контейнере. Конечно, контейнер уже должен быть создан, корректно запущен и снабжён надлежащими серверами LSP. Может возникнуть соблазн использовать раздел global (внутри lspclient.) Это также может сработать, но тогда все серверы LSP будут запускаться с этим префиксом, включая серверы для, например, Markdown, Bash-сценариев или JSON. Более вероятно, что в основной системе всё это есть (если используется). Поэтому, как всегда, всё зависит от конкретных параметров настройки.
Тем не менее, сервер LSP теперь может отслеживать другие (сетевые) пути, которые отличаются от локальных путей, отображаемых и используемых редактором. Указанные pathMappings используются для преобразования туда-обратно в рамках обмена данными с сервером LSP. Конечно, это делается по возможности. Очевидно, что не у всех локальных путей есть сетевые аналоги, но отсутствующие пути не будут видимы (серверу) и не создадут проблем. Тем не менее, верно и обратное: (сетевой) сервер теперь может видеть и делать ссылки в «сетевом корневом каталоге», которые не представлены в локальной системе простым/явным образом. При включении mapRemoteRoot сетевой корневой каталог неявно привязывается к «локальному URL-адресу» exec://foobar/, который затем обрабатывается простым и понятным протоколом KIO (протокол системы ввода-вывода среды KDE). Последний, по сути, использует (например) podman exec -i foobarcontainer cat somefile" для копирования с сетевого компьютера на локальный (и другие подобные варианты с использованием утилит из набора coreutils). Достаточно сказать, что это решение не предназначается для использования в качестве основного и не претендует на производительность, но позволяет быстро получить файл по ссылке и с лёгкостью загрузить его в редактор.
Теперь очевидно, что упрощённый вариант приведённой выше конфигурации (без hostname или pathMappings) можно использовать для обработки virtualenv без дублирования командной строки сервера (в сценарии-обёртке).
Следующее может быть не слишком очевидно, поэтому здесь приводится прямое описание.
Как
hostname, так и фактическийprefixопределяют среду выполнения (первый — по имени, второй — по содержимому). В экземпляре процесса редактора тот же самыйhostnameне должен быть связан с другимprefix, так как это приводит к неопределённому поведению (без необходимости в диагностике).Оба значения,
prefixиpathMappings, подлежат расширению переменных (в редакторе) (но с учётом предыдущей записи).Во время выполнения также задаются некоторые переменные среды, с помощью чего можно (тонко) настроить поведение средства запуска с префиксом (но ещё раз обратите внимание на первый пункт). В частности,
KATE_EXEC_PLUGINустанавливается в значениеlspclient, аKATE_EXEC_SERVER— в значение идентификатора сервера (например,python).В частности,
KATE_EXEC_INSPECTтакже имеет значение1. Это сообщает «средству запуска с префиксом», что получатель (модуль Kate) принимает некоторые данные вне диапазона/протокола. Таким образом, средство запуска может использовать некоторые возможности для определения привязок путей (например,podman inspect) и передать эту информацию в подходящем формате. Затем это будет удалено из потока, который в остальном соответствует протоколу LSP, и использовано для расширения заданной привязки путей. Это может быть альтернативой явной привязке ресурсов (снова дублирование определения контейнера). Конкретно, вместо этого затем можно было бы использовать вышеприведённый фрагмент;{ // ... "exec": { "hostname": "foobar" // нежелательный повтор имени, но вспомогательный сценарий понятен и прост "prefix": "exec_inspect.sh foobarcontainer podman exec -i foobarcontainer", "mapRemoteRoot": true, "pathMappings": [] } // ... }
И последнее по порядку, но не по значимости: что, если подход с «резервным KIO» не считается подходящим? На практике зачастую можно «подключить» сетевой корневой каталог к локальной файловой системе и затем указать последнюю в pathMappings. Для начала, широко известная файловая система sshfs (fuse) может подключить (действительно) сетевую систему к локальной. Если используется контейнер (podman/docker), большинство конфигураций (драйвера файловой системы) поддерживают следующий трюк;
$ rootdir=/proc/`podman inspect --format '{{.State.Pid}}' containername`/root
# некоторые символьные ссылки могут приводить к проблемам, ниже приводится альтернатива
$ sudo mount --bind $rootdir /somewhere/containername
Если это не сработает, можно воспользоваться sshfs или его вариантом для подключения корневого каталога сетевого ресурса или контейнера;
# смотрите соответствующие man-страницы; для обычного (незашифрованного) протокола обработки файлов достаточно sftp-server $ socat 'exec:podman exec -i containername /usr/lib/openssh/sftp-server' 'exec:sshfs -o transform_symlinks -o passive \:/ /somewhere/containername' # ... $ fusermount -u /somewhere/containername
Обратите внимание, что такое подключение сетевого корневого каталога к основной файловой системе, скорее всего, потребуется указывать вручную и в явном виде в разделе pathMappings (за исключением случаев, когда используется интеллектуальное средство запуска с префиксом, которое организовывает всё это автоматически).