- v1.0.1
- VS Code 1.85+
- 12 commands
- 39 settings
- reads Python, never runs it
- no runtime dependencies
Reference.
Everything it does, and what each part needs from your project.
The overview makes the argument. This page is the inventory: every capability, stated with its real limits, plus the prerequisites that decide whether half of this extension does anything at all in your workspace. Where the behaviour is narrower than the name suggests, it says so here rather than letting you find out.
Prose that lives next to the step it describes
A [Documentation] block puts all the explanation at the top of a test, while the
steps it describes are fifty lines below. The two drift apart, and the block becomes the thing
nobody updates. The alternative here is a comment marker: #> at the start of a
comment makes it documentation, and it can sit directly above the step it is about.
Guest Checkout Completes
#> ## Guest checkout
#> A shopper without an account completes an order.
#>> The basket is prepared first, so the totals are known
#>> before payment is attempted.
${basket}= Add Item To Basket SKU-4417
#> -> the shop must accept the card and return a confirmation
${order}= Place Order ${basket} shipping=EXPRESS
- Nesting.
#>>and#>>>indent two spaces per level in the rendered output. The depth is simply the number of>characters. - Both forms merge. Inline markers and a classic
[Documentation]block belonging to the same test render as one document, so adopting markers does not mean rewriting what you already have. - Source wrapping disappears. A sentence broken across several comment lines to
respect your line length joins back into flowing text. An explicit
<br>survives as a real break, and fenced code blocks are left exactly as written. - Arrow notes. Lines beginning
->or=>render with the marker in a fixed column and the text flowing beside it, so a wrapped expectation hangs correctly instead of sliding back under the arrow. Repeated arrows nest a level each. - Scope. Only
*** Test Cases ***,*** Tasks ***and*** Keywords ***are scanned. Settings and variables sections are ignored.
A rendered document that is also a table of contents
The sidebar renders the block your cursor is in as Markdown. The part that makes it a tool rather than a viewer is that the rendered text is the navigation: every line clicks back to the exact source line it came from, so you read the test as prose and jump into it at the point you care about.
That binding is not positional. Earlier versions matched rendered lines to source lines by order, which broke whenever the Markdown renderer regrouped the output and later links drifted onto the wrong lines. Each item now carries an explicit marker embedded in the rendered Markdown, so the binding survives whatever the renderer does with the shape.
- Variables, listed. Every local
VARandSet Variablein the block appears in aVariablessection, each one linking to the line that defines it. Variables assigned from keyword calls get their own collapsibleReturned Variablessection. - Values substituted into the prose. Documentation reading "the total is
${Total}" renders with the actual value — but only when it is unambiguous. A variable with several distinct assignments, or one that depends on a branch, stays as the literal token rather than picking a value and being confidently wrong. - An overview of the file. Every documented block listed, with a Show keywords filter that only appears when the file actually contains documented keywords.
- A CodeLens above each documentation block opens the preview. On by default,
and switched off with
enableCodeLens. - An optional hover preview of the whole block. This is the one feature that is deliberately off by default — it is a large hover, and it should be something you ask for.
Marking a line as a warning, not just writing "warning"
Documentation supports a small set of tags, in the live preview and in both export formats. They are semantic first, with plain colours available when you just want a colour.
Eight colour shortcuts — <red>, <orange>,
<yellow>, <green>, <blue>,
<pink>, <purple>, <gray> — sit
alongside them, and <color value="#0f766e"> takes an arbitrary value.
Anything else is text. An unsupported colour, an unexpected attribute, or arbitrary HTML is escaped and rendered as plain characters rather than interpreted. That is a deliberate guarantee and it is pinned by a test that feeds the renderer markup designed to execute and asserts it comes out as visible text.
The documentation was always shareable
A rendered test description is useful to people who will never open your editor — someone approving a release, someone asking what a suite actually covers. Four commands export the current block, or a multi-selection of blocks, with variables substituted and page breaks between test cases.
- Markdown writes a real file, through a save dialog — nothing is written anywhere until you choose a path. The author-facing colour tags are preserved, so the exported file stays editable in the same vocabulary you wrote.
- Print-ready HTML is what the "PDF" commands actually produce. The extension has no PDF writer: it renders a print-styled, fixed-light-theme page, writes it to a temporary file, and opens it in your browser, where you print to PDF. There is also a secondary button that tries VS Code's own print. It is worth knowing this is the mechanism — the result is a PDF, but your browser makes it.
The question that costs you three open files
A keyword returns an object. Its fields are declared in a Python class, which inherits most of them from a base class in another module, which is imported under an alias. Answering "what can I read off this" by hand means opening all of them. The Return Explorer answers it beside the cursor, in the only form that is actually useful — a string you can paste.
${order.confirmation_number}
${order.status}
${order.shipping.method}
${order.items[0].name}
${order.items[0].line_total}
The first version of this panel showed a type tree. Within two days it was rewritten to show access paths instead, because a test author does not want to browse a type — they want the expression. The tree survives underneath as Technical Details, for when you do want it.
- Inheritance. Fields declared on base classes are resolved, including two base
classes with the same name in different modules, followed through
import … as …aliases. A base is only followed when it looks like a structured type; inheriting from a plain mixin is skipped. - Generics.
list[T],Wrapper[T]andOptional[list[T]]resolve to the element type, under a policy you can set to always, never, or an explicit allow/deny list of containers. - Indexed access. A collection-typed field emits
${order.items[0].name}, with the index landing on the right segment at any depth — because the path is wrong otherwise, and a wrong path is worse than none. - Properties.
@propertygetters are indexed as members alongside plain fields. - Parsing is textual, not an AST. Attributes assigned only inside
__init__, or injected dynamically, are invisible. This is a deliberate trade: nothing in your workspace is ever imported or executed to answer these questions.
Two spellings of the same field. Some projects give their model classes a base that
makes every snake_case attribute readable in camelCase as well. Where that is
true, returnFieldNameStyle chooses which spelling the access paths use —
camelCase, raw, or both. It cannot be inferred, so it is declared:
camelCaseAliasBaseNames lists the base class names that mark a type as
converting, matched as a substring against the whole inheritance chain, ignoring case,
underscores and hyphens. It defaults to ["camelcase"], so a base called
CamelCaseBase works untouched; add your own marker class to the list, or empty it
to switch aliasing off. Types with no such base always render exactly the attribute names
written in Python.
What this argument accepts, and which value is flowing in
Hovering a named argument shows the members of the enum it accepts, with the value currently being passed marked and linked to the line that assigned it. That second half is the useful half: the question you have when a test fails is rarely "what is allowed" and usually "what did it actually get, and where did that come from".
- Provenance is labelled. A match is marked
direct,argument-fallback, orannotation-only, with a confidence note on the looser two. The loose name-based fallback exists but is off by default — it was switched off one version after it was added, because a confident wrong answer is worse than no answer. - Duplicates collapse. The same enum defined in several modules with identical members renders once, with a note saying how many were folded together.
- Hints travel through wrappers. A
.resourcekeyword that forwards an argument to another keyword that eventually reaches a typed Python keyword inherits the hint, up to twelve links of forwarding. Suites built as thin Robot layers over Python get argument intelligence they never annotated themselves. - Docstrings render. Google-style
Args,ReturnsandRaisesare normalised. When a docstring will not parse, you get a warning banner and the content — refusing to render is worse than rendering imperfectly. Insertwrites the argument for you. AnArgsentry for an argument missing from your call offers a link that adds... name=as an aligned continuation line, puts the caret after the=, and opens suggestions. It respects the documented argument order, and clicking an argument that is already present focuses it instead of adding a duplicate.
The one part that needs no Python at all
Hovering a variable shows its value, resolved from Set Variable or a Robot
Framework 7 VAR, including typed names like ${name: date}. Alias
chains are followed. A variable embedded in a longer string resolves to whichever token your
cursor is actually inside.
The interesting case is a variable assigned in more than one branch:
IF ${express}
VAR ${carrier} OVERNIGHT
ELSE
VAR ${carrier} STANDARD
END
Hovering ${carrier} shows both candidates, because the branches are
mutually exclusive and collapsing them to one would be a confident lie. Each candidate is
individually clickable back to its assignment.
Limit worth knowing. Only IF / ELSE IF / ELSE
create branches. Assignments inside FOR, WHILE or TRY
bodies are not branch-disambiguated — those blocks are tracked only so a stray
END does not confuse the parser.
Three kinds, on eight trigger characters
- Enum values after
argname=. A setting chooses whether they insert the member name, the literal value, or both. Skipped when what you have typed already looks like a variable reference. - Type-matched local variables. Typing
orderData=offers the variables in this test that were assigned from a keyword whose return type matches. Eventually consistent by design: on a cold cache the first trigger returns without them and schedules the work, so they appear on the next one. - Members of a return variable after
${order., to a configurable depth, handling the auto-closed}. Collection fields insert[0]for you. Prefix matching accepts either spelling — typingstatus_still matchesstatusCode.
Fold to the shape of the documentation
Documentation markers act as section boundaries, so a long test collapses to its own outline.
A heading owns everything until the next heading of equal or higher level; a #>
peer closes at the next peer of the same depth; nested markers fold independently. Two
commands — Fold to Headlines and Fold to Steps — plus Unfold, available
from the palette and from the preview.
One command changes your settings. Use Robot Companion as Default Folding
Provider writes editor.foldingStrategy and
editor.defaultFoldingRangeProvider into your [robotframework]
language override — workspace scope when a workspace is open, otherwise global. It exists
because competing folding providers otherwise merge into something neither of them
intended.
What it needs from your project
Sections 05, 06 and 08 all read the same Python index. If your project does not match these assumptions, those features do not fail loudly — they quietly find nothing. This is the part most worth reading before deciding the extension does not work.
| It needs | Or else |
|---|---|
Python keywords decorated with @keyword(...), with parentheses |
A plain method that Robot Framework happily exposes as a keyword is invisible here.
Every Python-derived feature — argument hints, annotations, return types, docstrings,
Insert — is built from the decorated list only. |
Keyword .robot files under a path matching *keywords* |
They are skipped. .resource files are indexed from anywhere, so this
only bites projects that define keywords in .robot files outside such a
folder. |
indexExcludeFolderPatterns reviewed |
It excludes tests by default — a very common home for Robot suites and
their resources. |
| Return types that are structured classes | A keyword returning a dict or an untyped object has nothing to resolve. |
Narrowing indexImportFolderPatterns to the folders that actually hold your keyword
libraries — ["libs/**"] rather than ["**"] — is the single biggest
lever if indexing feels slow in a large repository.
Staying out of the way while you type
Return resolution is real work — walking types through imports and inheritance — and doing it on the editor's main thread makes typing feel bad. It runs in a worker thread that has no filesystem access at all; it receives a snapshot and computes. Results are cached in memory and persisted per workspace on disk.
- One result serves every variable of a type. Access paths are computed as a variable-agnostic template and rebound to your actual variable name when read.
- Saving a Python file updates only that file's contribution, with dependent cache entries evicted. It used to rescan the workspace.
- Background prewarming yields to you. Every hover and completion marks you as active; prewarm aborts mid-run and waits for a pause.
- Hovers read the cache first, then allow a 45 ms grace window before doing the expensive thing — so a hover you have already moved away from never pays for itself.
- During a Robot debug session the passive features stay live — hover,
completions, folding ranges, both panels, and
Insert. Only things that move text under a running debugger are suspended. An earlier blanket pause made the tool useless exactly when it was most wanted.
If return resolution stops answering, the worker has exited: it does not restart, and it
stays quiet for the rest of the session. Invalidate All Caches and a window reload
bring it back, and the Robot Companion output channel will say what
happened.
Twelve, all under one category
| Command | Does |
|---|---|
| Focus Return Explorer | Reveals the return panel. |
| Open Current Documentation Block | Opens the preview at the block under the cursor. |
| Export Current Documentation as Markdown | Save dialog, one block. |
| Export Selected Documentation as Markdown | Save dialog, a chosen set of blocks. |
| Export Current Documentation as PDF | Print-ready page, opened in your browser. |
| Export Selected Documentation as PDF | The same, for several blocks, page-broken. |
| Fold Documentation To Headlines | Collapse to the outline. |
| Fold Documentation To Steps | Collapse to the documented steps. |
| Unfold Documentation | Expand again. |
| Use Robot Companion as Default Folding Provider | Writes two editor settings. See 09. |
| Invalidate All Caches | Parser, index, worker memory, and the persisted disk cache. |
| Show Output | Opens the diagnostic channel. |
Thirty-nine, and the ten that matter
All under the robotCompanion. prefix, with full descriptions in the Settings UI.
These are the ones that change what you see rather than how fast you see it.
| Setting | Default | Why touch it |
|---|---|---|
indexImportFolderPatterns | ["**"] | Narrow it to your library folders if indexing is slow. |
indexExcludeFolderPatterns | includes tests | Remove tests if your suites live there. |
returnFieldNameStyle | camelcase | Alias, raw, or both spellings of return members. See 05. |
umlautDecoratorNames | ["convert_umlaut_kwargs"] | Name of the decorator mapping ASCII argument spellings onto diacritic ones. See 06. |
camelCaseAliasBaseNames | ["camelcase"] | Which base classes mark a type as offering camelCase aliases. See 05. |
enumCompletionDisplayMode | name | Insert the member name, its value, or both. |
enableEnumArgumentFallback | false | Looser enum matching. Off because it can be confidently wrong. |
enableDocumentationHoverPreview | false | The whole block on hover, if you want it. |
enableCodeLens | true | Turn off if the lens above every block is noise. |
logLevel | warn | trace exists mainly for folding diagnostics. |
Where it stops
- It is additive, with two exceptions. It registers no formatter and no
diagnostics, so it coexists with other Robot tooling. The two things that do write are
Insert, which edits your test, and the default-folding-provider command, which edits your settings. Export writes only where you point a save dialog. - Python is read, never run. Types are recovered by reading source text. Nothing is imported, so nothing in your workspace executes — and dynamically constructed attributes are therefore invisible.
- The technical tree has fixed ceilings that your settings do not raise: fifteen enum members per type, two nested types per field, five inherited bases.
- A few field names are always hidden from the simple access list — container-plumbing names that are never what you meant to assert on.
- Keyword-doc rendering is best-effort. Ambiguous matches show the best one plus the alternatives; unparseable docstrings warn and render anyway.
- It is a local extension. It works over Remote SSH and Dev Containers, but every file it reads crosses the remote bridge, so indexing a large remote workspace is slower.
How much of this is tested. npm run test:all runs twenty-three Node test
groups plus three suites that drive a real VS Code Extension Development Host — clicking every
rendered preview target through the actual webview bridge and asserting where the cursor lands,
and asserting the exact resulting line text and caret position after an argument insertion.
The folding work earned that: parser-level tests kept passing while the feature was visibly
broken in a real window.