123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- ================================
- Nim IDE Integration Guide
- ================================
- :Author: Britney Spears
- :Version: |nimversion|
- .. contents::
- .. raw:: html
- <blockquote><p>
- "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.
- </p></blockquote>
- Nim differs from many other compilers in that it is really fast,
- and being so fast makes it suited to provide external queries for
- text editors about the source code being written. Through the
- ``idetools`` command of `the compiler <nimc.html>`_, any IDE
- can query a ``.nim`` source file and obtain useful information like
- definition of symbols or suggestions for completion.
- This document will guide you through the available options. If you
- want to look at practical examples of idetools support you can look
- at the test files found in the `Test suite`_ or `various editor
- integrations <https://github.com/Araq/Nim/wiki/Editor-Support>`_
- already available.
- Idetools invocation
- ===================
- Specifying the location of the query
- ------------------------------------
- All of the available idetools commands require you to specify a
- query location through the ``--track`` or ``--trackDirty`` switches.
- The general idetools invocations are::
- nim idetools --track:FILE,LINE,COL <switches> proj.nim
- Or::
- nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
- ``proj.nim``
- This is the main *project* filename. Most of the time you will
- pass in the same as **FILE**, but for bigger projects this is
- the file which is used as main entry point for the program, the
- one which users compile to generate a final binary.
- ``<switches>``
- This would be any of the other idetools available options, like
- ``--def`` or ``--suggest`` explained in the following sections.
- ``COL``
- An integer with the column you are going to query. For the
- compiler columns start at zero, so the first column will be
- **0** and the last in an 80 column terminal will be **79**.
- ``LINE``
- An integer with the line you are going to query. For the compiler
- lines start at **1**.
- ``FILE``
- The file you want to perform the query on. Usually you will
- pass in the same value as **proj.nim**.
- ``DIRTY_FILE``
- The **FILE** paramater is enough for static analysis, but IDEs
- tend to have *unsaved buffers* where the user may still be in
- the middle of typing a line. In such situations the IDE can
- save the current contents to a temporary file and then use the
- ``--trackDirty`` switch.
- Dirty files are likely to contain errors and they are usually
- compiled partially only to the point needed to service the
- idetool request. The compiler discriminates them to ensure that
- **a)** they won't be cached and **b)** they won't invalidate
- the cached contents of the original module.
- The other reason is that the dirty file can appear anywhere on
- disk (e.g. in tmpfs), but it must be treated as having a path
- matching the original module when it comes to usage of relative
- paths, etc. Queries, however, will refer to the dirty module
- name in their answers instead of the normal filename.
- Definitions
- -----------
- The ``--def`` idetools switch performs a query about the definition
- of a specific symbol. If available, idetools will answer with the
- type, source file, line/column information and other accessory data
- if available like a docstring. With this information an IDE can
- provide the typical *Jump to definition* where a user puts the
- cursor on a symbol or uses the mouse to select it and is redirected
- to the place where the symbol is located.
- Since Nim is implemented in Nim, one of the nice things of
- this feature is that any user with an IDE supporting it can quickly
- jump around the standard library implementation and see exactly
- what a proc does, learning about the language and seeing real life
- examples of how to write/implement specific features.
- Idetools will always answer with a single definition or none if it
- can't find any valid symbol matching the position of the query.
- Suggestions
- -----------
- The ``--suggest`` idetools switch performs a query about possible
- completion symbols at some point in the file. IDEs can easily provide
- an autocompletion feature where the IDE scans the current file (and
- related ones, if it knows about the language being edited and follows
- includes/imports) and when the user starts typing something a
- completion box with different options appears.
- However such features are not context sensitive and work simply on
- string matching, which can be problematic in Nim especially due
- to the case insensitiveness of the language (plus underscores as
- separators!).
- The typical usage scenario for this option is to call it after the
- user has typed the dot character for `the object oriented call
- syntax <tut2.html#method-call-syntax>`_. Idetools will try to return
- the suggestions sorted first by scope (from innermost to outermost)
- and then by item name.
- Invocation context
- ------------------
- The ``--context`` idetools switch is very similar to the suggestions
- switch, but instead of being used after the user has typed a dot
- character, this one is meant to be used after the user has typed
- an opening brace to start typing parameters.
- Symbol usages
- -------------
- The ``--usages`` idetools switch lists all usages of the symbol at
- a position. IDEs can use this to find all the places in the file
- where the symbol is used and offer the user to rename it in all
- places at the same time. Again, a pure string based search and
- replace may catch symbols out of the scope of a function/loop.
- For this kind of query the IDE will most likely ignore all the
- type/signature info provided by idetools and concentrate on the
- filename, line and column position of the multiple returned answers.
- Expression evaluation
- ---------------------
- This feature is still under development. In the future it will allow
- an IDE to evaluate an expression in the context of the currently
- running/debugged user project.
- Compiler as a service (CAAS)
- ============================
- The occasional use of idetools is acceptable for things like
- definitions, where the user puts the cursor on a symbol or double
- clicks it and after a second or two the IDE displays where that
- symbol is defined. Such latencies would be terrible for features
- like symbol suggestion, plus why wait at all if we can avoid it?
- The idetools command can be run as a compiler service (CAAS),
- where you first launch the compiler and it will stay online as a
- server, accepting queries in a telnet like fashion. The advantage
- of staying on is that for many queries the compiler can cache the
- results of the compilation, and subsequent queries should be fast
- in the millisecond range, thus being responsive enough for IDEs.
- If you want to start the server using stdin/stdout as communication
- you need to type::
- nim serve --server.type:stdin proj.nim
- If you want to start the server using tcp and a port, you need to type::
- nim serve --server.type:tcp --server.port:6000 \
- --server.address:hostname proj.nim
- In both cases the server will start up and await further commands.
- The syntax of the commands you can now send to the server is
- practically the same as running the nim compiler on the commandline,
- you only need to remove the name of the compiler since you are
- already talking to it. The server will answer with as many lines
- of text it thinks necessary plus an empty line to indicate the end
- of the answer.
- You can find examples of client/server communication in the idetools
- tests found in the `Test suite`_.
- Parsing idetools output
- =======================
- Idetools outputs is always returned on single lines separated by
- tab characters (``\t``). The values of each column are:
- 1. Three characters indicating the type of returned answer (e.g.
- def for definition, ``sug`` for suggestion, etc).
- 2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
- about any of the enums defined in the module ``compiler/ast.nim``.
- 3. Full qualitifed path of the symbol. If you are querying a symbol
- defined in the ``proj.nim`` file, this would have the form
- ``proj.symbolName``.
- 4. Type/signature. For variables and enums this will contain the
- type of the symbol, for procs, methods and templates this will
- contain the full unique signature (e.g. ``proc (File)``).
- 5. Full path to the file containing the symbol.
- 6. Line where the symbol is located in the file. Lines start to
- count at **1**.
- 7. Column where the symbol is located in the file. Columns start
- to count at **0**.
- 8. Docstring for the symbol if available or the empty string. To
- differentiate the docstring from end of answer in server mode,
- the docstring is always provided enclosed in double quotes, and
- if the docstring spans multiple lines, all following lines of the
- docstring will start with a blank space to align visually with
- the starting quote.
- Also, you won't find raw ``\n`` characters breaking the one
- answer per line format. Instead you will need to parse sequences
- in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
- newlines generate the sequence ``\x0A``).
- The following sections define the expected output for each kind of
- symbol for which idetools returns valid output.
- skConst
- -------
- | **Third column**: module + [n scope nesting] + const name.
- | **Fourth column**: the type of the const value.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- const SOME_SEQUENCE = @[1, 2]
- --> col 2: $MODULE.SOME_SEQUENCE
- col 3: seq[int]
- col 7: ""
- skEnumField
- -----------
- | **Third column**: module + [n scope nesting] + enum type + enum field name.
- | **Fourth column**: enum type grouping other enum fields.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- Open(filename, fmWrite)
- --> col 2: system.FileMode.fmWrite
- col 3: FileMode
- col 7: ""
- skForVar
- --------
- | **Third column**: module + [n scope nesting] + var name.
- | **Fourth column**: type of the var.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc looper(filename = "tests.nim") =
- for letter in filename:
- echo letter
- --> col 2: $MODULE.looper.letter
- col 3: char
- col 7: ""
- skIterator, skClosureIterator
- -----------------------------
- The fourth column will be the empty string if the iterator is being
- defined, since at that point in the file the parser hasn't processed
- the full line yet. The signature will be returned complete in
- posterior instances of the iterator.
- | **Third column**: module + [n scope nesting] + iterator name.
- | **Fourth column**: signature of the iterator including return type.
- | **Docstring**: docstring if available.
- .. code-block:: nim
- 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
- -------
- | **Third column**: module + [n scope nesting] + name.
- | **Fourth column**: always the empty string.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc test(text: string) =
- var found = -1
- block loops:
- --> col 2: $MODULE.test.loops
- col 3: ""
- col 7: ""
- skLet
- -----
- | **Third column**: module + [n scope nesting] + let name.
- | **Fourth column**: the type of the let variable.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- let
- text = "some text"
- --> col 2: $MODULE.text
- col 3: TaintedString
- col 7: ""
- skMacro
- -------
- The fourth column will be the empty string if the macro is being
- defined, since at that point in the file the parser hasn't processed
- the full line yet. The signature will be returned complete in
- posterior instances of the macro.
- | **Third column**: module + [n scope nesting] + macro name.
- | **Fourth column**: signature of the macro including return type.
- | **Docstring**: docstring if available.
- .. code-block:: nim
- proc testMacro() =
- expect(EArithmetic):
- --> col 2: idetools_api.expect
- col 3: proc (varargs[expr], stmt): stmt
- col 7: ""
- skMethod
- --------
- The fourth column will be the empty string if the method is being
- defined, since at that point in the file the parser hasn't processed
- the full line yet. The signature will be returned complete in
- posterior instances of the method.
- Methods imply `dynamic dispatch <tut2.html#dynamic-dispatch>`_ and
- idetools performs a static analysis on the code. For this reason
- idetools may not return the definition of the correct method you
- are querying because it may be impossible to know until the code
- is executed. It will try to return the method which covers the most
- possible cases (i.e. for variations of different classes in a
- hierarchy it will prefer methods using the base class).
- While at the language level a method is differentiated from others
- by the parameters and return value, the signature of the method
- returned by idetools returns also the pragmas for the method.
- Note that at the moment the word ``proc`` is returned for the
- signature of the found method instead of the expected ``method``.
- This may change in the future.
- | **Third column**: module + [n scope nesting] + method name.
- | **Fourth column**: signature of the method including return type.
- | **Docstring**: docstring if available.
- .. code-block:: nim
- 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
- -------
- | **Third column**: module + [n scope nesting] + param name.
- | **Fourth column**: the type of the parameter.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc reader(filename = "tests.nim") =
- let text = readFile(filename)
- --> col 2: $MODULE.reader.filename
- col 3: string
- col 7: ""
- skProc
- ------
- The fourth column will be the empty string if the proc is being
- defined, since at that point in the file the parser hasn't processed
- the full line yet. The signature will be returned complete in
- posterior instances of the proc.
- While at the language level a proc is differentiated from others
- by the parameters and return value, the signature of the proc
- returned by idetools returns also the pragmas for the proc.
- | **Third column**: module + [n scope nesting] + proc name.
- | **Fourth column**: signature of the proc including return type.
- | **Docstring**: docstring if available.
- .. code-block:: nim
- 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
- --------
- | **Third column**: module + [n scope nesting] + result.
- | **Fourth column**: the type of the result.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc getRandomValue() : int =
- return 4
- --> col 2: $MODULE.getRandomValue.result
- col 3: int
- col 7: ""
- skTemplate
- ----------
- The fourth column will be the empty string if the template is being
- defined, since at that point in the file the parser hasn't processed
- the full line yet. The signature will be returned complete in
- posterior instances of the template.
- | **Third column**: module + [n scope nesting] + template name.
- | **Fourth column**: signature of the template including return type.
- | **Docstring**: docstring if available.
- .. code-block:: nim
- 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
- ------
- | **Third column**: module + [n scope nesting] + type name.
- | **Fourth column**: the type.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc writeTempFile() =
- var output: File
- --> col 2: system.File
- col 3: File
- col 7: ""
- skVar
- -----
- | **Third column**: module + [n scope nesting] + var name.
- | **Fourth column**: the type of the var.
- | **Docstring**: always the empty string.
- .. code-block:: nim
- proc writeTempFile() =
- var output: File
- output.open("/tmp/somefile", fmWrite)
- output.write("test")
- --> col 2: $MODULE.writeTempFile.output
- col 3: File
- col 7: ""
- Test suite
- ==========
- To verify that idetools is working properly there are files in the
- ``tests/caas/`` directory which provide unit testing. If you find
- odd idetools behaviour and are able to reproduce it, you are welcome
- to report it as a bug and add a test to the suite to avoid future
- regressions.
- Running the test suite
- ----------------------
- At the moment idetools support is still in development so the test
- suite is not integrated with the main test suite and you have to
- run it manually. First you have to compile the tester::
- $ cd my/nim/checkout/tests
- $ nim c testament/caasdriver.nim
- Running the ``caasdriver`` without parameters will attempt to process
- all the test cases in all three operation modes. If a test succeeds
- nothing will be printed and the process will exit with zero. If any
- test fails, the specific line of the test preceding the failure
- and the failure itself will be dumped to stdout, along with a final
- indicator of the success state and operation mode. You can pass the
- parameter ``verbose`` to force all output even on successful tests.
- The normal operation mode is called ``ProcRun`` and it involves
- starting a process for each command or query, similar to running
- manually the Nim compiler from the commandline. The ``CaasRun``
- mode starts a server process to answer all queries. The ``SymbolProcRun``
- mode is used by compiler developers. This means that running all
- tests involves processing all ``*.txt`` files three times, which
- can be quite time consuming.
- If you don't want to run all the test case files you can pass any
- substring as a parameter to ``caasdriver``. Only files matching the
- passed substring will be run. The filtering doesn't use any globbing
- metacharacters, it's a plain match. For example, to run only
- ``*-compile*.txt`` tests in verbose mode::
- ./caasdriver verbose -compile
- Test case file format
- ---------------------
- All the ``tests/caas/*.txt`` files encode a session with the compiler:
- * The first line indicates the main project file.
- * Lines starting with ``>`` indicate a command to be sent to the
- compiler and the lines following a command include checks for
- expected or forbidden output (``!`` for forbidden).
- * If a line starts with ``#`` it will be ignored completely, so you
- can use that for comments.
- * Since some cases are specific to either ``ProcRun`` or ``CaasRun``
- modes, you can prefix a line with the mode and the line will be
- processed only in that mode.
- * The rest of the line is treated as a `regular expression <re.html>`_,
- so be careful escaping metacharacters like parenthesis.
- Before the line is processed as a regular expression, some basic
- variables are searched for and replaced in the tests. The variables
- which will be replaced are:
- * **$TESTNIM**: filename specified in the first line of the script.
- * **$MODULE**: like $TESTNIM but without extension, useful for
- expected output.
- When adding a test case to the suite it is a good idea to write a
- few comments about what the test is meant to verify.
|