flang компилятор доказывает, что программа не зациклится 0.7.29 GitHub English

Настройка редактора

Языковой сервер входит в компилятор — ставить отдельно нечего:

flang lsp --stdio

Расширений у программы четыре, и все четыре равноправны: .flang — основное и то, что стоит во всех командах и в CI; .fp — короткое; .фп — «функциональная программа», чтобы имя файла по-русски не приходилось писать транслитом; .фланг — имя языка кириллицей целиком (решения записаны в docs/adr/0016-three-file-extensions.md и docs/adr/0018-file-extensions-are-one-list.md). Подхватывают все четыре и Vim, и Neovim, и VS Code.

Подсветка есть для Vim 8/9, Neovim и VS Code; для остальных редакторов её нет.

Что работает сегодня

ЧтоСостояние
Подсветка в Vim 8/9 и Neovimработает, ставится одной ссылкой
Подсветка в VS Codeработает, расширение собирается из дерева языка
Подсветка в Emacsнет
Сервер на закрытом вводе (проверка одной командой)отвечает
Сервер при открытом вводе, как в редактореотвечает на каждое сообщение по мере прихода

Сервер отвечает на каждое сообщение, как только прочёл его, и не ждёт закрытия ввода: initialize получает ответ, а textDocument/didOpen — textDocument/publishDiagnostics, пока ввод остаётся открытым.

Что лежит в дереве языка

docs/editors/
  vim/         подсветка и языковой сервер для Vim 8/9 и Neovim
  vscode/      расширение VS Code: подсветка и языковой сервер
  flang-lsp/   указатель на эту страницу: сервер входит в компилятор, отдельного пакета нет
  linguist/    заявка в github-linguist на язык .flang

Подсветка не написана руками ни в одном редакторе, и это замер, а не намерение. В docs/editors/vim/syntax/flang.vim — 47 строк, в docs/editors/vscode/syntaxes/flang.tmLanguage.json — 66 строк, и обе печатаются из таблицы ключевых слов языка программами на самом flang (scripts/editors/vim-highlighting.fscript, scripts/editors/vscode-highlighting.fscript). Список слов, набранный отдельно, — это второе описание языка, и оно расходится с первым в первый же день. И то и другое проверяется настоящим редактором, запущенным без окна и человека: flang io scripts/editors/vim-highlight-check.fscript, flang io scripts/editors/lsp-check.fscript. Вторая проверка открывает файл в nvim --headless и спрашивает четыре вещи: дошла ли диагностика до буфера с местом и кодом, привёл ли переход к объявлению туда, где оно стоит, показало ли наведение подпись — и отвечает ли сервер, пока ввод открыт. В Neovim на все четыре ответ «да»; прогон Vim 8/9 пропускается, если FLANG_VIM_LSP не указывает на vim-lsp и async.vim.

Vim и Neovim делят один каталог, но настраиваются по-разному: у Neovim клиент протокола встроенный, Vim 8/9 берёт сторонний vim-lsp. Ищут сервер оба одинаково — одной функцией на VimScript, которую Lua зовёт через vim.fn.

Расширение VS Code написано на JavaScript (docs/editors/vscode/extension.js), и ни одна его строка ничего не знает о языке: точка входа расширения VS Code — модуль, который редактор загружает в свой процесс Node, и другого способа подключиться у него нет. Файл только запускает сервер и передаёт ему слова протокола; всё остальное делает языковой сервер, написанный на самом flang.

Чего здесь нет. Остальные редакторы, а также tree-sitter и Chroma (ими красят код на страницах и на GitHub), по-прежнему без подсветки, и это неоплаченный долг. Работа кажется большой — несколько грамматик на один язык, — но оценка обманчива: печатью она стоит одной программы на набор правил, и это уже дважды измерено числами выше.

Что умеет сервер

УмеетМетод протокола
Диагностика той же дорогой, что flang check: разбор, связывание, типы, завершаемостьtextDocument/publishDiagnostics
Дополнение: ключевые слова всех четырёх поверхностей записи (из таблицы лексера, flang/self/lexer.flang), имена функций и типов модуля и ввезённых модулей, поля записи после точки, варианты суммы после случайtextDocument/completion
Подпись при наведении: что принимает, что возвращает, доказано ли завершениеtextDocument/hover
Переход к объявлению, в том числе в другой модульtextDocument/definition

Дополнение вызывается двумя знаками: « и .. Текст документа передаётся целиком, а не приращениями. Позиции считаются в UTF-16.

Два сужения дополнения. Поля после точки предлагаются по типу параметра функции, в теле которой стоит курсор; если тип не выведен, предлагаются поля всех записей программы, и каждое подписано своей записью. Имена берутся из последнего удачного разбора файла: пока строка не дописана, текст не разбирается, и сервер дополняет по прежней программе; если файл не разобрался ни разу, остаются одни ключевые слова. Диагностика при этом всегда от текущего текста.

Чего сервер не делает: переименование, правки, форматирование, поиск ссылок, список символов в файле. На такой запрос он отвечает отказом:

{"code":-32601,"message":"метод не поддержан: textDocument/rename"}

Запрос до initialize получает отказ -32002 «сервер ещё не инициализирован».

JSON с экранированным не-ASCII (\uXXXX) сервер читает так же, как UTF-8 без экранирования.

Диагностика ввезённого модуля не подчёркивается ни в его буфере, ни в открытом: файл у диагностики назван только тогда, когда исходник один, а беда чужого модуля уходит в журнал редактора (window/logMessage). Туда же уходит диагностика, у которой нет ни строки, ни файла, — вместо подчёркивания наугад первой строки. Это же сказано в flang lsp --help.

Проверьте, что сервер отвечает

printf 'Content-Length: 75\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}' | flang lsp --stdio

Печатает рамку и ответ, код возврата 0:

Content-Length: 311

{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":1,"save":{"includeText":false}},"completionProvider":{"triggerCharacters":["«","."]},"hoverProvider":true,"definitionProvider":true},"serverInfo":{"name":"flang-lsp","version":"0.1.0"}}}

Ответ не ждёт закрытия ввода: при открытом вводе он приходит так же.

VS Code

Расширение лежит в дереве языка — docs/editors/vscode/. В Marketplace его нет: собирается и ставится оно локально.

cd docs/editors/vscode
npm install
npx vsce package
code --install-extension flang-0.1.0.vsix

Оно даёт подсветку всех четырёх расширений имени файла и запускает языковой сервер. Настроек три:

КлючПо умолчаниюЧто делает
flang.server.commandflang-lspчем запускать сервер
flang.server.args["--stdio"]доводы запуска
flang.server.enabledtrueвыключите, если нужна только подсветка

Отдельной команды flang-lsp в дереве нет — сервер зовётся как flang lsp. Если в PATH нет и её, поставьте в flang.server.command команду flang (или полный путь до неё), а в flang.server.args — ["lsp", "--stdio"].

Подробнее — docs/editors/vscode/README.md: как поставить прямо из дерева без сборки, как перепечатать подсветку и что понадобится, чтобы выложить расширение в Marketplace.

Другого клиента для VS Code нет: без расширения редактор сторонний языковой сервер не запускает.

Vim 8/9

Подсветка и настройки буфера ставятся штатным механизмом пакетов. Плагин живёт в подкаталоге docs/editors/vim дерева языка, а подкаталог этот механизм не умеет — поэтому на него кладётся ссылка:

git clone https://github.com/digitable-lol/flang.git ~/.local/share/flang
mkdir -p ~/.vim/pack/flang/start
ln -s ~/.local/share/flang/editors/vim ~/.vim/pack/flang/start/flang

В ~/.vimrc нужны две строки. Без них Vim не прочитает ни подсветку, ни настройки типа файла, и файл откроется серым:

filetype plugin indent on
syntax on

У vim-plug подкаталог задаётся ключом: Plug 'digitable-lol/flang', { 'rtp': 'docs/editors/vim' }.

Проверить, что встало: откройте любой .flang, .fp, .фп или .фланг и спросите редактор.

:set filetype?

Ответ filetype=flang значит, что расширение подхвачено. Текст при этом раскрашен по пяти видам — ключевое слово, имя в ёлочках, строка, число, комментарий, — и раскрашен одинаково на всех четырёх поверхностях записи. Отступ развёрнут в пробелы по два: в языке отступ значим, и смешение табуляций с пробелами меняло бы смысл программы, а не только вид. По « и » редактор ходит парой и подставляет закрывающую.

Сервер: своего клиента протокола у Vim 8/9 нет, ставится сторонний vim-lsp. Регистрировать сервер в нём руками не нужно — плагин языка делает это сам:

Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'digitable-lol/flang', { 'rtp': 'docs/editors/vim' }

Клиент выбран один и по одному доводу: он написан на чистом VimScript, работает с Vim 8.0.1453 и внешних зависимостей у него нет вовсе. coc.nvim требует Node — а вся затея в том, чтобы редактору Node не понадобился; yegappan/lsp требует Vim 9.0 и тем отрезает весь Vim 8.

Нет vim-lsp — не беда: подсветка работает и без сервера, а плагин молчит вместо того, чтобы делать вид, что подсказки будут. Не нашёлся сам сервер — Vim скажет об этом один раз за сеанс и перечислит, где искал.

Neovim

Тот же плагин и тот же подкаталог, только каталог пакетов свой:

git clone https://github.com/digitable-lol/flang.git ~/.local/share/flang
mkdir -p ~/.config/nvim/pack/flang/start
ln -s ~/.local/share/flang/editors/vim ~/.config/nvim/pack/flang/start/flang

Менеджером плагинов подкаталог задаётся ключом:

{
  "digitable-lol/flang",
  config = function(плагин)
    vim.opt.runtimepath:prepend(плагин.dir .. "/editors/vim")
    vim.cmd("runtime! ftdetect/*.vim")
    require("flang").setup()
  end,
}

У vim-plug и packer — Plug 'digitable-lol/flang', { 'rtp': 'docs/editors/vim' } и use { 'digitable-lol/flang', rtp = 'docs/editors/vim' }.

Настраивать сервер отдельно не нужно и не следует: плагин сам назначает тип файла всем четырём расширениям и сам поднимает сервер через встроенный vim.lsp. Второй клиент на тот же буфер дал бы две одинаковые диагностики на каждую строку.

Выключить сервер — одним из двух способов, в init.lua до загрузки плагина:

vim.g.flang_ne_nastraivat = 1        -- не поднимать сервер вовсе
require("flang").setup({ lsp = false })  -- то же, но тип файла назначить

Оба касаются только сервера: подсветка и настройки буфера остаются, потому что их даёт не плагин, а тип файла.

Дополнение — <C-x><C-o>, наведение — K, переход к объявлению — gd.

Без плагина

Один сервер без подсветки и настроек буфера подключается штатным клиентом Neovim 0.11 и новее — назвать тип файла и команду:

vim.filetype.add({ extension = { flang = "flang", fp = "flang", ["фп"] = "flang", ["фланг"] = "flang" } })

vim.lsp.config.flang = {
  cmd = { "flang", "lsp", "--stdio" },
  filetypes = { "flang" },
  root_markers = { ".git" },
}
vim.lsp.enable("flang")

На Neovim 0.10 и старше — тот же сервер через vim.lsp.start в автокоманде FileType для flang. Вместе с плагином этого делать не нужно: второй клиент на тот же буфер удвоил бы диагностику.

Emacs

Через eglot (входит в Emacs 29 и новее). Своего режима у .flang нет — заведите производный от prog-mode:

(define-derived-mode flang-mode prog-mode "flang"
  (setq-local comment-start "// ")
  (setq-local comment-end ""))

(add-to-list 'auto-mode-alist '("\\.flang\\'" . flang-mode))

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '(flang-mode . ("flang" "lsp" "--stdio"))))

(add-hook 'flang-mode-hook #'eglot-ensure)

Переход к объявлению — M-., подпись — M-x eldoc, дополнение — M-x completion-at-point.

Подсветка на GitHub: заявка в github-linguist

GitHub раскрашивает исходники и считает языки репозитория по файлу «languages.yml» проекта github-linguist/linguist. Заготовка записи для flang лежит в docs/editors/linguist/languages-flang.yml: тип, цвет, четыре расширения (первым .flang — у linguist порядок в списке есть порядок предпочтения), область «source.flang» — та же, что у грамматики расширения VS Code.

Заявка не подана, и чего для неё нет — названо:

language_id в записи не заполнен намеренно: его присваивают сопровождающие linguist при приёме, и после этого он не меняется. Запись вставляется в «languages.yml» по алфавиту. Расширения .фп и .фланг вне ASCII; если запись примут без них, компилятор и редакторы их читать не перестанут — GitHub лишь не покрасит такой файл.

Без сервера: проверка на клавише

Диагностику даёт та же дорога, что и у сервера, только командой:

flang check путь/к/файлу.flang

В Vim и Neovim: :setlocal makeprg=flang\ check\ % и дальше :make — замечания лягут в список ошибок, и по нему ходят :cnext и :cprevious. В VS Code — задача в .vscode/tasks.json с "command": "flang check ${file}". Сообщения идут с кодом беды и местом — тем же текстом, что показал бы редактор.

Дальше: Устранение неполадок — что делать, когда проверка не отвечает или зеленеет не к месту.