Руководство по интеграции Nim IDE
«Да, я создатель» — Арак, 2013-07-26 19:28:32.
Примечание: этот документ в основном устарел, см. вместо него nimsuggest
Nim отличается от многих других компиляторов тем, что он очень быстрый, а высокая скорость делает его подходящим для предоставления внешних запросов к текстовым редакторам о коде источника, который в данный момент пишется. С помощью команды idetools компилятора компилятора любая IDE может запросить файл .nim источника и получить полезную информацию, такую как определение символов или предложения для автозаполнения.
Этот документ поможет вам разобраться с доступными вариантами. Если вы хотите посмотреть практические примеры поддержки idetools, вы можете посмотреть тестовые файлы, находящиеся в Наборе тестов или различных интеграциях с редакторами.
Вызов idetools
Указание расположения запроса
Все доступные команды idetools требуют указания расположения запроса с помощью ключей --track или --trackDirty. Общие вызовы idetools:
nim idetools --track:FILE,LINE,COL <switches> proj.nim
Или:
nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
proj.nim- Это имя файла основного проекта. Большинство времени вы будете передавать то же значение, что и в FILE, но для больших проектов это файл, используемый в качестве основной точки входа в программу, тот, который пользователи компилируют для генерации конечного двоичного файла.
<switches>- Это могут быть любые другие доступные параметры idetools, такие как
--defили--suggest, описанные в следующих разделах. COL- Целое число с колонкой, которую вы собираетесь запросить. В компиляторе колонки начинаются с нуля, поэтому первая колонка будет 0, а последняя в терминале с 80 колонками — 79.
LINE- Целое число с строкой, которую вы собираетесь запросить. В компиляторе строки начинаются с 1.
FILE- Файл, для которого вы хотите выполнить запрос. Обычно вы передаёте то же значение, что и proj.nim.
DIRTY_FILE-
Параметр FILE достаточно для статического анализа, но IDE, как правило, имеют несохранённые буферы, где пользователь может всё ещё находиться в процессе ввода строки. В таких ситуациях IDE может сохранить текущее содержимое в временный файл и затем использовать ключ
--trackDirty.Несохранённые файлы, скорее всего, содержат ошибки, и они обычно компилируются лишь частично до той степени, которая необходима для обслуживания запроса idetool. Компилятор различает их, чтобы гарантировать, что а) они не будут кэшированы и б) они не повлияют на кэш содержимого исходного модуля.
Другая причина заключается в том, что несохранённый файл может появиться где угодно на диске (например, в tmpfs), но он должен обрабатываться как имеющий путь, соответствующий исходному модулю, когда дело доходит до использования относительных путей и т. д. Однако запросы будут ссылаться на имя несохранённого модуля в своих ответах вместо обычного имени файла.
Определения
Переключатель --def idetools выполняет запрос об определении конкретного символа. Если доступно, idetools ответит типом, файлом источника, информацией о строке/столбце и другими дополнительными данными, если они есть, такими как строка документации. С этой информацией IDE может предоставить типичный переход к определению, где пользователь помещает курсор на символ или использует мышь для его выбора и перенаправляется к месту расположения символа.
Поскольку Nim реализован на Nim, одним из приятных аспектов этой функции является то, что любой пользователь с IDE, поддерживающей её, может быстро перемещаться по реализации стандартной библиотеки и видеть, что делает конкретная процедура, изучая язык и видя реальные примеры написания/реализации конкретных функций.
Idetools всегда отвечает одним определением или не отвечает, если не может найти подходящий символ, соответствующий позиции запроса.
Предложения
Переключатель --suggest idetools выполняет запрос о возможных символах завершения в какой-то момент файла. IDE могут легко предоставить функцию автозаполнения, где IDE сканирует текущий файл (и связанные файлы, если известно о редактируемом языке и следуют включениям/импортам), и когда пользователь начинает печатать что-то, появляется поле автозаполнения с различными вариантами.
Однако такие функции не зависят от контекста и работают просто по совпадению строк, что может быть проблематично в Nim, особенно из-за отсутствия чувствительности к регистру языка (плюс нижние подчёркивания как разделители!).
Типичный сценарий использования этого параметра заключается в его вызове после того, как пользователь набрал точку для синтаксиса вызова объектов. Idetools попытается вернуть предложения, отсортированные сначала по области действия (от внутренней к внешней), а затем по имени элемента.
Контекст вызова
Переключатель --context idetools очень похож на переключатель предложений, но вместо использования после того, как пользователь набрал точку, этот используется после ввода открывающей фигурной скобки для начала ввода параметров.
Использование символов
Переключатель --usages idetools перечисляет все использования символа в позиции. IDE могут использовать это для поиска всех мест в файле, где используется символ, и предложить пользователю переименовать его во всех местах одновременно. Опять же, поиск и замена, основанный на чистом совпадении строк, может выхватывать символы за пределами области действия функции/цикла.
Для этого типа запроса IDE, скорее всего, проигнорирует всю информацию о типе/подписи, предоставленную idetools, и сосредоточится на имени файла, строке и столбце позиции нескольких возвращаемых ответов.
Вычисление выражений
Эта функция всё ещё находится в стадии разработки. В будущем она позволит IDE вычислять выражение в контексте текущего выполняемого/отлаживаемого проекта пользователя.
Компилятор как служба (CAAS)
Случайное использование idetools приемлемо для таких задач, как определения, где пользователь помещает курсор на символ или дважды нажимает на него, и через секунду или две IDE отображает, где этот символ определён. Такие задержки были бы ужасными для функций, таких как предложения символов, плюс зачем ждать, если можно этого избежать?
Команда idetools может выполняться как служба компилятора (CAAS), где вы сначала запускаете компилятор, и он остаётся активным как сервер, принимающий запросы в стиле telnet. Преимущество поддержания подключения заключается в том, что для многих запросов компилятор может кэшировать результаты компиляции, а последующие запросы должны выполняться быстро, в пределах миллисекунд, тем самым обеспечивая достаточную реакцию для IDE.
Если вы хотите запустить сервер, используя stdin/stdout в качестве канала связи, вам нужно ввести:
nim serve --server.type:stdin proj.nim
Если вы хотите запустить сервер, используя tcp и порт, вам нужно ввести:
nim serve --server.type:tcp --server.port:6000 \ --server.address:hostname proj.nim
В обоих случаях сервер запустится и будет ожидать дальнейших команд. Синтаксис команд, которые вы теперь можете отправлять на сервер, практически идентичен запуску компилятора nim в командной строке, вам нужно только удалить имя компилятора, так как вы уже общаетесь с ним. Сервер ответит стольком строк текста, сколько посчитает необходимым, плюс пустую строку, чтобы указать конец ответа.
Примеры обмена данными между клиентом и сервером вы найдёте в тестах idetools, расположенных в Наборе тестов.
Парсинг вывода idetools
Вывод idetools всегда возвращается в строках, разделённых табуляцией (\t). Значения каждой колонки:
- Три символа, указывающие тип возвращаемого ответа (например, def для определения,
sugдля предложения и т. д.). - Тип символа. Это может быть
skProc,skLet, и практически любой из перечислений, определённых в модулеcompiler/ast.nim. - Полный квалифицированный путь символа. Если вы запрашиваете символ, определённый в файле
proj.nim, это будет иметь видproj.symbolName. - Тип/подпись. Для переменных и перечислений это будет содержать тип символа, для процедур, методов и шаблонов это будет содержать полную уникальную подпись (например,
proc (File)). - Полный путь к файлу, содержащему символ.
- Строка, в которой находится символ в файле. Нумерация строк начинается с 1.
- Столбец, в котором находится символ в файле. Нумерация столбцов начинается с 0.
-
Строка документации для символа, если она доступна, или пустая строка. Чтобы отличить строку документации от конца ответа в режиме сервера, строка документации всегда предоставляется заключённой в двойные кавычки, и если строка документации занимает несколько строк, все последующие строки документации начинаются с пробела, чтобы визуально выровняться с открывающей кавычкой.
Также вы не найдёте необработанных
\nсимволов, нарушающих формат одной строки ответа. Вместо этого вам нужно будет проанализировать последовательности вида\xHH, где HH — шестнадцатеричное значение (например, новые строки генерируют последовательность\x0A).
В следующих разделах определён ожидаемый вывод для каждого типа символа, для которого idetools возвращает допустимый вывод.
skConst
Третья колонка: модуль + [n вложенность области] + имя константы.
Четвёртая колонка: тип значения константы.
Строка документации: всегда пустая строка.
const SOME_SEQUENCE = @[1, 2]
--> col 2: $MODULE.SOME_SEQUENCE
col 3: seq[int]
col 7: "" skEnumField
Третья колонка: модуль + [n вложенность области] + тип перечисления + имя поля перечисления.
Четвёртая колонка: группировка других полей перечисления по типу перечисления.
Строка документации: всегда пустая строка.
Open(filename, fmWrite)
--> col 2: system.FileMode.fmWrite
col 3: FileMode
col 7: "" skForVar
Третья колонка: модуль + [n вложенность области] + имя переменной.
Четвёртая колонка: тип переменной.
Строка документации: всегда пустая строка.
proc looper(filename = "tests.nim") =
for letter in filename:
echo letter
--> col 2: $MODULE.looper.letter
col 3: char
col 7: "" skIterator, skClosureIterator
Четвёртая колонка будет пустой строкой, если итератор определяется, поскольку на этом этапе в файле парсер ещё не обработает полную строку. Подпись будет возвращена полностью в последующих случаях итератора.
Третья колонка: модуль + [n вложенность области] + имя итератора.
Четвёртая колонка: подпись итератора, включая возвращаемый тип.
Строка документации: строка документации, если она доступна.
let
text = "some text"
letters = toSeq(runes(text))
--> col 2: unicode.runes
col 3: iterator (string): Rune
col 7: "iterates over any unicode character of the string `s`." skLabel
Третья колонка: модуль + [n вложенность области] + имя.
Четвёртая колонка: всегда пустая строка.
Строка документации: всегда пустая строка.
proc test(text: string) =
var found = -1
block loops:
--> col 2: $MODULE.test.loops
col 3: ""
col 7: "" skLet
Третий столбец: модуль + [вложенность области] + имя переменной.
Четвертый столбец: тип переменной.
Строка документации: всегда пустая строка.
let
text = "some text"
--> col 2: $MODULE.text
col 3: TaintedString
col 7: "" skMacro
Четвертый столбец будет пустой строкой, если макрос определяется, так как на этом этапе обработки файла парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих случаях вызова макроса.
Третий столбец: модуль + [вложенность области] + имя макроса.
Четвертый столбец: подпись макроса, включая тип возвращаемого значения.
Строка документации: строка документации, если доступна.
proc testMacro() =
expect(EArithmetic):
--> col 2: idetools_api.expect
col 3: proc (varargs[expr], stmt): stmt
col 7: "" skMethod
Четвертый столбец будет пустой строкой, если метод определяется, так как на этом этапе обработки файла парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих случаях вызова метода.
Методы предполагают динамическую диспатчинг, а idetools выполняет статический анализ кода. По этой причине idetools может не вернуть определение правильного метода, который вы запрашиваете, потому что это может быть невозможно узнать до выполнения кода. Он будет пытаться вернуть метод, который охватывает как можно больше случаев (т. е. для вариантов различных классов в иерархии он будет отдавать предпочтение методам, использующим базовый класс).
Хотя на уровне языка метод отличается от других по параметрам и возвращаемому значению, подпись метода, возвращаемая idetools, также возвращает прагмы для метода.
Обратите внимание, что в настоящее время слово proc возвращается для подписи найденного метода вместо ожидаемого method. Это может измениться в будущем.
Третий столбец: модуль + [вложенность области] + имя метода.
Четвертый столбец: подпись метода, включая тип возвращаемого значения.
Строка документации: строка документации, если доступна.
method eval(e: PExpr): int = quit "to override!"
method eval(e: PLiteral): int = e.x
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
--> col 2: $MODULE.eval
col 3: proc (PPlusExpr): int
col 7: "" skParam
Третий столбец: модуль + [вложенность области] + имя параметра.
Четвертый столбец: тип параметра.
Строка документации: всегда пустая строка.
proc reader(filename = "tests.nim") =
let text = readFile(filename)
--> col 2: $MODULE.reader.filename
col 3: string
col 7: "" skProc
Четвертый столбец будет пустой строкой, если процедура определяется, так как на этом этапе обработки файла парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих случаях вызова процедуры.
Хотя на уровне языка процедура отличается от других по параметрам и возвращаемому значению, подпись процедуры, возвращаемая idetools, также возвращает прагмы для процедуры.
Третий столбец: модуль + [вложенность области] + имя процедуры.
Четвертый столбец: подпись процедуры, включая тип возвращаемого значения.
Строка документации: строка документации, если доступна.
open(filename, fmWrite)
--> col 2: system.Open
col 3: proc (var File, string, FileMode, int): bool
col 7:
"Opens a file named `filename` with given `mode`.
Default mode is readonly. Returns true iff the file could be opened.
This throws no exception if the file could not be opened." skResult
Третий столбец: модуль + [вложенность области] + результат.
Четвертый столбец: тип результата.
Строка документации: всегда пустая строка.
proc getRandomValue() : int =
return 4
--> col 2: $MODULE.getRandomValue.result
col 3: int
col 7: "" skTemplate
Четвертый столбец будет пустой строкой, если шаблон определяется, так как на этом этапе обработки файла парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих случаях вызова шаблона.
Третий столбец: модуль + [вложенность области] + имя шаблона.
Четвертый столбец: подпись шаблона, включая тип возвращаемого значения.
Строка документации: строка документации, если доступна.
let
text = "some text"
letters = toSeq(runes(text))
--> col 2: sequtils.toSeq
col 3: proc (expr): expr
col 7:
"Transforms any iterator into a sequence.
Example:
.. code-block:: nim
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
if x mod 2 == 1:
result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]" skType
Третий столбец: модуль + [вложенность области] + имя типа.
Четвертый столбец: тип.
Строка документации: всегда пустая строка.
proc writeTempFile() =
var output: File
--> col 2: system.File
col 3: File
col 7: "" skVar
Третий столбец: модуль + [вложенность области] + имя переменной.
Четвертый столбец: тип переменной.
Строка документации: всегда пустая строка.
proc writeTempFile() =
var output: File
output.open("/tmp/somefile", fmWrite)
output.write("test")
--> col 2: $MODULE.writeTempFile.output
col 3: File
col 7: "" Набор тестов
Для проверки правильности работы idetools в каталоге tests/caas/ есть файлы, которые обеспечивают модульное тестирование. Если вы обнаружите странное поведение idetools и сможете его воспроизвести, вы можете сообщить об этом как об ошибке и добавить тест в набор тестов, чтобы избежать будущих регрессий.
Запуск набора тестов
В настоящее время поддержка idetools всё ещё находится в стадии разработки, поэтому набор тестов не интегрирован в основной набор тестов, и вам нужно запустить его вручную. Сначала необходимо скомпилировать тестовую программу:
$ cd my/nim/checkout/tests $ nim c testament/caasdriver.nim
Запуск caasdriver без параметров попытается обработать все тестовые случаи во всех трёх режимах работы. Если тест пройдёт успешно, ничего не будет выведено, и процесс завершится с кодом 0. Если какой-либо тест завершится с ошибкой, конкретная строка теста, предшествующая ошибке, и сама ошибка будут выведены в стандартный вывод, а также будет указано конечное состояние успеха и режим работы. Вы можете передать параметр verbose , чтобы форсировать вывод даже при успешных тестах.
Нормальный режим работы называется ProcRun и включает запуск процесса для каждой команды или запроса, аналогично ручному запуску компилятора Nim из командной строки. Режим CaasRun запускает процесс сервера для обработки всех запросов. Режим SymbolProcRun используется разработчиками компилятора. Это означает, что запуск всех тестов включает обработку всех файлов *.txt трижды, что может занять много времени.
Если вы не хотите запускать все тестовые файлы, вы можете передать любую подстроку в качестве параметра к caasdriver. Будут запущены только файлы, соответствующие переданной подстроке. Фильтрация не использует символы подстановки, это просто совпадение. Например, для запуска только *-compile*.txt тестов в подробном режиме:
./caasdriver verbose -compile
Формат файла тестового случая
Все файлы tests/caas/*.txt кодируют сеанс работы с компилятором:
- Первая строка указывает основной файл проекта.
- Строки, начинающиеся с
>, указывают команду, которая должна быть отправлена компилятору, и следующие за командой строки включают проверки ожидаемого или запрещённого вывода (!для запрещённого). - Если строка начинается с
#, она будет полностью проигнорирована, так что вы можете использовать её для комментариев. - Поскольку некоторые случаи относятся к режиму
ProcRunилиCaasRun, вы можете добавить префикс режима к строке, и эта строка будет обработана только в этом режиме. - Остальная часть строки обрабатывается как регулярное выражение, поэтому будьте осторожны с экранированием специальных символов, таких как скобки.
Перед обработкой строки как регулярного выражения ищутся и заменяются некоторые базовые переменные в тестах. Переменные, которые будут заменены:
- $TESTNIM: имя файла, указанное в первой строке скрипта.
- $MODULE: как $TESTNIM, но без расширения, полезно для ожидаемого вывода.
При добавлении тестового случая в набор тестов рекомендуется написать несколько комментариев о том, что должен проверять тест.
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/idetools.html