Spec-Zone.ru › Nim

Руководство по интеграции Nim IDE

Исходный кодРедактировать

"да, я создатель" -- Araq, 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), но при использовании относительных путей и т. д. он должен обрабатываться как имеющий путь, соответствующий исходному модулю. Однако запросы будут ссылаться на имя несохраненного модуля в своих ответах вместо обычного имени файла.

Определения

Переключатель idetools --def выполняет запрос об определении конкретного символа. Если доступно, idetools ответит типом, файлом, строкой/колонкой и другой дополнительной информацией, если доступна, например, строкой документации. С этой информацией IDE может предоставить типичную функцию Переход к определению, где пользователь помещает курсор на символ или выбирает его мышкой и перенаправляется в место, где находится символ.

Поскольку Nim написан на Nim, одним из приятных аспектов этой функции является то, что любой пользователь с IDE, поддерживающей ее, может быстро переходить по реализации стандартной библиотеки и точно видеть, что делает proc, изучая язык и рассматривая реальные примеры написания/реализации определенных функций.

Idetools всегда отвечает одним определением или ничего, если не может найти соответствующий символ в заданном положении.

Предложения

Переключатель idetools --suggest выполняет запрос о возможных символах для автодополнения в определенной точке файла. IDE легко могут предоставить функцию автодополнения, где IDE сканирует текущий файл (и связанные с ним, если известно, какой язык редактируется, и учитываются include/import) и когда пользователь начинает вводить что-то, появляется окно автодополнения с различными вариантами.

Однако такие функции не учитывают контекст и работают просто на сопоставлении строк, что может быть проблематично в Nim, особенно из-за регистронезависимости языка (плюс подчеркивание в качестве разделителей!).

Типичный сценарий использования этого варианта — вызов его после того, как пользователь ввёл точку для синтаксиса вызова методов объектов. Idetools постарается вернуть предложения, отсортированные сначала по области видимости (от внутренней к внешней), а затем по имени элемента.

Контекст вызова

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

Использование символов

Переключатель idetools --usages перечисляет все места использования символа в определённой позиции. 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). Значения каждого столбца:

  1. Три символа, указывающие тип возвращаемого ответа (например, def для определения, sug для предложения и т. д.).
  2. Тип символа. Это может быть skProc, skLet, и практически любой из перечислений, определенных в модуле compiler/ast.nim.
  3. Полное квалифицированное имя символа. Если вы запрашиваете символ, определённый в файле proj.nim, это будет иметь вид proj.symbolName.
  4. Тип/подпись. Для переменных и перечислений это будет содержать тип символа, для процедур, методов и шаблонов это будет содержать полную уникальную подпись (например, proc (File)).
  5. Полный путь к файлу, содержащему символ.
  6. Строка, в которой находится символ в файле. Нумерация строк начинается с 1.
  7. Столбец, в котором находится символ в файле. Нумерация столбцов начинается с 0.
  8. Строка документации для символа, если доступна, или пустая строка. Чтобы отличить строку документации от конца ответа в режиме сервера, строка документации всегда предоставляется в двойных кавычках, а если строка документации занимает несколько строк, все последующие строки документации будут начинаться с пробела для визуального выравнивания со стартовой кавычкой.

    Также вы не найдёте символов \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

Третий столбец: модуль + [n вложенности области] + имя let.
Четвертый столбец: тип переменной let.

Строка документации: всегда пустая строка.

let
  text = "some text"
--> col 2: $MODULE.text
    col 3: string
    col 7: ""

skMacro

Четвертый столбец будет пустой строкой, если макрос определяется, так как на этом этапе в файле парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих экземплярах макроса.

Третий столбец: модуль + [n вложенности области] + имя макроса.
Четвертый столбец: подпись макроса, включая тип возвращаемого значения.

Строка документации: строка документации, если она доступна.

proc testMacro() =
  expect(EArithmetic):
--> col 2: idetools_api.expect
    col 3: proc (varargs[expr], stmt): stmt
    col 7: ""

skMethod

Четвертый столбец будет пустой строкой, если метод определяется, так как на этом этапе в файле парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих экземплярах метода.

Методы подразумевают динамическую диспетчеризацию, а idetools выполняет статический анализ кода. По этой причине idetools может не вернуть определение правильного метода, который вы запрашиваете, потому что это может быть невозможно узнать до тех пор, пока код не будет выполнен. Он будет пытаться вернуть метод, охватывающий наибольшее количество возможных случаев (то есть для вариантов разных классов в иерархии он будет отдавать предпочтение методам, использующим базовый класс).

Хотя на уровне языка метод отличается от других по параметрам и возвращаемому значению, подпись метода, возвращаемая idetools, также возвращает прагмы для метода.

Обратите внимание, что в настоящее время для подписи найденного метода возвращается слово proc, а не ожидаемое method. Это может измениться в будущем.

Третий столбец: модуль + [n вложенности области] + имя метода.
Четвертый столбец: подпись метода, включая тип возвращаемого значения.

Строка документации: строка документации, если она доступна.

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

Третий столбец: модуль + [n вложенности области] + имя параметра.
Четвертый столбец: тип параметра.

Строка документации: всегда пустая строка.

proc reader(filename = "tests.nim") =
  let text = readFile(filename)
--> col 2: $MODULE.reader.filename
    col 3: string
    col 7: ""

skProc

Четвертый столбец будет пустой строкой, если proc определяется, так как на этом этапе в файле парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих экземплярах proc.

Хотя на уровне языка proc отличается от других по параметрам и возвращаемому значению, подпись proc, возвращаемая idetools, также возвращает прагмы для proc.

Третий столбец: модуль + [n вложенности области] + имя proc.
Четвертый столбец: подпись proc, включая тип возвращаемого значения.

Строка документации: строка документации, если она доступна.

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

Третий столбец: модуль + [n вложенности области] + результат.
Четвертый столбец: тип результата.

Строка документации: всегда пустая строка.

proc getRandomValue() : int =
  return 4
--> col 2: $MODULE.getRandomValue.result
    col 3: int
    col 7: ""

skTemplate

Четвертый столбец будет пустой строкой, если шаблон определяется, так как на этом этапе в файле парсер ещё не обработает всю строку. Подпись будет возвращена полностью в последующих экземплярах шаблона.

Третий столбец: модуль + [n вложенности области] + имя шаблона.
Четвертый столбец: подпись шаблона, включая тип возвращаемого значения.

Строка документации: строка документации, если она доступна.

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:
     
     ```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

Третий столбец: модуль + [n вложенности области] + имя типа.
Четвертый столбец: тип.

Строка документации: всегда пустая строка.

proc writeTempFile() =
  var output: File
--> col 2: system.File
    col 3: File
    col 7: ""

skVar

Третий столбец: модуль + [n вложенности области] + имя переменной.
Четвертый столбец: тип переменной.

Строка документации: всегда пустая строка.

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/ есть файлы, которые предоставляют unit-тестирование. Если вы обнаружите странное поведение idetools и сможете его воспроизвести, вы можете сообщить об этом как об ошибке и добавить тест в набор, чтобы избежать будущих регрессий.

Запуск набора тестов

В настоящее время поддержка idetools всё ещё находится в стадии разработки, поэтому набор тестов не интегрирован с основным набором тестов, и вам нужно запустить его вручную. Сначала вам нужно скомпилировать тестер:

$ cd my/nim/checkout/tests
$ nim c testament/caasdriver.nim

Запуск caasdriver без параметров попытается обработать все тестовые случаи во всех трёх режимах работы. Если тест пройдёт успешно, ничего не будет выведено, и процесс завершится с нулём. Если какой-либо тест завершится неудачно, в стандартный вывод будет выведена соответствующая строка теста, предшествующая ошибке, и сама ошибка, а также финальный индикатор состояния успеха и режима работы. Вы можете передать параметр verbose для принудительного вывода всего, даже при успешных тестах.

Нормальный режим работы называется ProcRun и включает запуск процесса для каждой команды или запроса, аналогично ручному запуску Nim-компилятора из командной строки. Режим CaasRun запускает серверный процесс для ответа на все запросы. Режим SymbolProcRun используется разработчиками компилятора. Это означает, что запуск всех тестов включает обработку всех *.txt файлов три раза, что может быть довольно длительным.

Если вы не хотите запускать все файлы тестовых случаев, вы можете передать любую подстроку в качестве параметра для caasdriver. Будут запущены только файлы, соответствующие переданной подстроке. Фильтрация не использует никаких метасимволов оболочки, это просто совпадение. Например, для запуска только *-compile*.txt тестов в подробном режиме:

./caasdriver verbose -compile

Формат файла тестового случая

Все файлы tests/caas/*.txt кодируют сеанс с компилятором:

  • Первая строка указывает основной файл проекта.
  • Строки, начинающиеся с >, указывают команду, которая должна быть отправлена компилятору, а строки, следующие за командой, включают проверки ожидаемого или запрещённого вывода (! для запрещённого).
  • Если строка начинается с #, она будет полностью проигнорирована, поэтому вы можете использовать её для комментариев.
  • Поскольку некоторые случаи специфичны для режимов ProcRun или CaasRun, вы можете добавлять префикс режима к строке, и она будет обработана только в этом режиме.
  • Остальная часть строки обрабатывается как регулярное выражение, поэтому будьте внимательны при экранировании метасимволов, таких как скобки.

Перед обработкой строки как регулярного выражения ищутся и заменяются некоторые базовые переменные в тестах. Переменные, которые будут заменены:

  • $TESTNIM: имя файла, указанного в первой строке сценария.
  • $MODULE: как $TESTNIM, но без расширения, полезно для ожидаемого вывода.

При добавлении тестового случая в набор рекомендуется написать несколько комментариев о том, что проверяется тестом.

© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/idetools.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API