TrickLogo

Trick Simulation Toolkit for VS Code

Contents


Purpose

The purpose of this document is to explain how to build, install, and use the Trick Simulation Toolkit extension for Visual Studio Code.


Introduction

The vscode-trick extension, included in Trick's source tree under vscode-trick/, brings Trick-aware editing, IntelliSense, build and run support, and live views of running sims to Visual Studio Code, so working on a sim doesn't mean bouncing between an editor that treats S_define/input.py as opaque text, a separate terminal, and the Java Sim Sniffer, Trick View, and Sim Control Panel tools.

It is not currently published to the VS Code Marketplace, so it's installed from a .vsix package — either built locally or downloaded from this repo's Releases page (see Installing the Extension).


Features

Each of these is covered in more detail, with usage examples, in its own section below.


Prerequisite Knowledge

One should:


Building the Extension

Every Trick release has a prebuilt vscode-trick-<version>.vsix attached as a release asset, built and tested by CI — if you just want to install the extension, grab that and skip straight to Installing the Extension.

To build it yourself instead (for example, to try local changes), from a Trick source checkout:

cd vscode-trick
npm install
npm run package

This produces vscode-trick-<version>.vsix in that directory.


Installing the Extension

From a terminal:

code --install-extension vscode-trick-<version>.vsix

Or from VS Code's UI: open the Extensions view, click the ... menu in its top corner, choose Install from VSIX..., and select the .vsix file.

After installing, reload the window and open an S_define file - it should be auto-detected as "Trick".


Editing S_define/.sm Files

Opening an S_define or .sm file gets you Trick-aware syntax highlighting on top of embedded C++ highlighting: ##include (model headers) is colored differently from #include (CP-parsed includes), %header{ }/%{ } raw code blocks are recognized, and job declarations (e.g. C1 {tag} P2 (0.01, "scheduled") obj.method();) have their class/phase/cycle-time/job-tag pieces highlighted individually.

Snippets (type the prefix, then press Tab/Enter to expand): simobject, job-default_data, job-initialization, job-scheduled, job-derivative, job-integration, job-dynamic_event, job-shutdown, IntegLoop, integrate, collect, job_class_order, create_connections, plus #include/##include/ %header/%{ block skeletons and a header snippet for the Trick comment-header fields (PURPOSE:, LIBRARY DEPENDENCIES:, ...).

Ctrl+click (or Cmd+click on macOS) on an #include/##include target jumps to that header, resolved against the owning sim's TRICK_CFLAGS/TRICK_CXXFLAGS/ TRICK_SFLAGS (from its S_overrides.mk) and Trick's own system include paths. If a target can't be resolved anywhere, it's underlined as an unresolved-include diagnostic instead of silently failing.

Ctrl+click / Go to Definition also works on sim object types (e.g. IHM::SimObject ihm;). A SimObject class can only be defined in S_define itself or a .sm file, so it's resolved directly: the currently open file first, then S_define, then .sm files under the sim root and its TRICK_SFLAGS include paths (from S_overrides.mk, including anything a makefile it includes adds). This finds the real class definition even when the C/C++ extension's own index would only see the generated S_source.hh/S_source_py.i (it doesn't parse .sm files at all).

Job target methods (e.g. ihm.update();) aren't confined to S_define/.sm, so those still use the C/C++ extension's own workspace symbol index — which requires it to have indexed the relevant header/source files first.


C/C++ IntelliSense for Model Code

Opening a model .c/.cpp/.h file under a sim gets real IntelliSense (completions, hover, Go to Definition/Implementation, no false "cannot open source file" squiggles) because the extension registers as a Custom Configuration Provider for the Microsoft C/C++ extension: for any file, it walks up to find the owning SIM_* directory (the one containing S_define), then resolves that sim's actual TRICK_CFLAGS/ TRICK_CXXFLAGS/TRICK_SFLAGS — by default by actually invoking make against a shipped helper makefile that includes Trick's real Makefile.common plus that sim's S_overrides.mk, so ${TRICK_HOME} and any custom make rules resolve exactly as trick-CP would see them. This means sims with different include paths in the same workspace each get the right IntelliSense, instead of one guessed-at c_cpp_properties.json for everything.

Use Trick: Show Resolved Include Paths (see Commands) to see exactly what was resolved for the sim containing the active file, and whether it came from make or the regex fallback.


Editing input.py and .dr Files

.dr files (plain Python pulled into input.py via exec(open(...).read())) are recognized as Python, same as input.py itself (VS Code already treats .py files as Python; .dr needed an explicit association since it isn't a standard Python extension).

Snippets (all prefixed trick- so they don't clutter non-Trick Python files): trick-realtime (real-time + frame + itimer setup), trick-varserver, trick-terminate, trick-add_read, trick-drgroup (a DRAscii/DRBinary/DRHDF5 data record group skeleton), trick-exec-dr (exec(open("Modified_data/....dr").read())), trick-simcontrol, trick-trickview, and trick-integrator (<loop>.getIntegrator(trick.<Runge_Kutta_4|Euler|...>, <state size>)).

trick.* completions and hover: Trick's input processor pre-imports trick (along with os, sys, struct, binascii) and binds every sim object name (including IntegLoops, e.g. armIntegLoop) into global scope before input.py runs, with no import statement anywhere in the input file. Pylance has no way to know that on its own, so the extension generates, per workspace folder that contains an S_define:

These are written to .vscode/trick-python/ and __builtins__.pyi in each workspace folder, excluded from git via .git/info/exclude (never a tracked .gitignore), and regenerated automatically when a sim's S_define changes. Use Trick: Regenerate Python Stubs (see Commands) to force a refresh — most commonly after building Trick for the first time, since that's when sim_services.py becomes available to scrape for full trick.* coverage.

Sim variable completion and hover (e.g. ball.state.input.mass) work the same way, but need a built sim, not just a built Trick: every trick-CP/make build regenerates <sim>/S_sie.resource, an XML description of that sim's entire object/variable tree (classes, members, units, descriptions, enumerations). The extension parses it and generates one typed Python class per reachable class, so each sim object in __builtins__.pyi points at a real type instead of a bare Any — giving completions, hover text (member units/description), and unknown-attribute diagnostics at every nesting depth, not just on the top-level object name. This refreshes automatically a moment after each build (watching S_sie.resource directly, separately from the S_define watcher above); an unbuilt sim still gets a plain, untyped object — no false "undefined" warnings, just no completions past the top level until it's built. If two sims in the same workspace folder declare the same object name with different types, that name falls back to Any and the conflict is logged to the "Trick" output channel rather than guessed at.

Ctrl+click on a sim variable or a trick.* name goes to the C++ declaration it was generated from, not the generated stub those names actually resolve to under the hood — the stub just re-declares the name with no body, so landing there isn't useful. For example, Ctrl+click on mass in ball.state.input.mass jumps to double mass ; in the model header that declares it, Ctrl+click on ball jumps to its declaration in S_define, and Ctrl+click on exec_set_terminate_time in trick.exec_set_terminate_time(...) jumps to its prototype in include/trick/exec_proto.h. This also works inside .dr files' variable-path strings (e.g. drg0.add_variable("ball.state.output.position[0]")), which Pylance can't resolve at all since to the Python parser that's just a string literal. Since VS Code can't suppress Pylance's own stub-pointing result, the extension also writes a one-time [python]-scoped editor.gotoLocation.multipleDefinitions: "goto" to the workspace folder's settings (only if nothing there already set a value) so Ctrl+click jumps straight to the C++ source instead of opening a picker between the two; the stub is still one click away via Go to Declaration/Peek Definition.

Ctrl+click on an open("...") target (e.g. exec(open("Modified_data/Rocket.dr").read())) jumps to that file, resolved against the sim root first — because that's where Trick's own working directory is when it runs a sim, not the RUN_* directory input.py lives in — then against the file's own directory as a fallback.

import statements that only resolve via TRICK_PYTHON_PATH: Trick's input processor (IPPython.cpp) puts the sim root, <simRoot>/Modified_data, $TRICK_HOME/share/trick/pymods, and each directory in S_overrides.mk's TRICK_PYTHON_PATH onto sys.path before running input.py. Pylance doesn't know about any of that on its own, so a real import that only resolves through one of those paths (e.g. import ParseJson as pj) shows up as unresolved. The extension resolves <simRoot>/Modified_data, pymods, and TRICK_PYTHON_PATH's entries, and keeps them in python.analysis.extraPaths for each workspace folder, added alongside the generated stub path and refreshed whenever a sim's S_overrides.mk changes; anything you've added to extraPaths yourself is left alone. The sim root itself is deliberately not added, even though Trick puts it on sys.path too: unlike those other directories, a sim root can contain build output, every RUN_* directory, and other large generated trees, and Pylance's extraPaths indexer tries to crawl everything under an entry in full (it doesn't honor .gitignore the way normal workspace indexing does) — adding a whole sim root this way can hang Pylance indexing indefinitely on a large sim. The practical effect is that an import written relative to the sim root itself (e.g. from Modified_data.utils.logger import ... from a RUN_* directory, rather than from Modified_data reached via TRICK_PYTHON_PATH) still shows up as unresolved. Because extraPaths is one flat list per workspace folder rather than per sim, two sims in the same folder that both have a same-named module under Modified_data will also collide — Pylance resolves against whichever sim's path happens to come first.


Building a Sim from VS Code

The extension wraps trick-CP — the actual build command Trick sims use, which generates a makefile and execs make — as a VS Code task, scoped to whichever sim contains the active file:

Both trick-ICG (Trick's code generator, which parses your model headers) and the C++ compiler report errors in the same plain-text Clang/GCC diagnostic format (file:line:column: error: message), since neither tool offers a machine-readable output mode. The extension's problem matcher understands that format, so build errors and warnings — from either tool — show up as clickable entries in the Problems panel (Ctrl+Shift+M/Cmd+Shift+M) instead of as raw terminal text.

This requires TRICK_HOME to be resolvable; see Configuration.


Running a Sim from VS Code

Once a sim is built, Trick: Run Current Sim (see Commands) launches its executable directly — equivalent to cd-ing into the SIM_* directory and running ./S_main_*.exe RUN_<name>/<input>.py by hand — in a VS Code task terminal, with the working directory set to the sim root (same as running it from a shell):

Picking an input file. A sim can have more than one RUN_* directory, and a RUN_* directory can itself hold more than one .py file (for example a RUN_test/unit_test.py alongside its input.py). The input file is chosen like this:

Building first. If the sim hasn't been built yet (no S_main_*.exe present), a prompt offers Build and Run — this runs the same build as Trick: Build Current Sim, and only launches the sim if that build succeeds.

A sim launched this way shows up automatically in the Trick Sims View below, like any other running sim, since that view discovers sims the same way regardless of how they were started.


Trick Sims View

Click the Trick icon in the activity bar to open two stacked views, Sims and Connected. Together they replace three separate Java tools — Sim Sniffer, Trick View, and Sim Control Panel — with trees embedded in the VS Code window rather than separate pop-up windows. The two views share a resizable divider — drag it to give more room to whichever one you're using, or collapse either one entirely.

Discovery. A running sim's variable server broadcasts its host, port, and run directory over UDP multicast every couple of seconds, and the Sims view picks those up automatically — a sim should appear within a few seconds of launch, and disappear a few seconds after it exits (unless it's connected — see below). This is on by default everywhere except macOS, where it's commonly blocked; a firewall or VPN can also block it elsewhere. If a sim doesn't show up on its own, use Connect to Sim (host:port)… (the + button in either view's title bar) instead — the port is printed to the sim's console at startup, or can be read from trick.var_server_get_port() in its input file.

Connecting. Click the plug icon next to a discovered-but-disconnected sim in Sims to connect. Connecting doesn't change what the sim is doing — it just opens a variable server connection so the view can show live status and run the control buttons. The moment you connect, the sim moves out of Sims and into Connected, and stays there — even if it stops broadcasting, or other sims come and go in Sims — so you don't have to chase it around a re-sorting list. If the sim process ends while you're connected, it stays in Connected showing Ended until you click Disconnect.

Running, freezing, stopping. In Connected, a sim shows its current mode (e.g. Frozen, Running) and simulation time, with inline buttons for whichever actions make sense for that mode: Run (sends exec_run()), Freeze (sends exec_freeze()), and Stop (sends trick.stop(), after a confirmation, since it ends the run). A sim that calls trick.exec_set_freeze_command(True) in its input file starts out frozen and waits for Run before it does anything — this is where that button matters most.

Watching variables. Click Add Watch on a sim in Connected to pick a variable. If the sim's S_sie.resource is reachable on your filesystem (true for any sim running locally), you get a step-by-step picker through its sim objects, drilling into each one's members — otherwise you can type a path directly. Watched variables show up as children of the sim in Connected (which expands automatically when you add one), with their live value (and units, where Trick reports any) updating a few times a second; an unknown or mistyped path shows BAD_REF. Remove a watch with the x button next to it. Watch lists are remembered per sim directory, so they come back if you reconnect later.

You can also right-click a variable reference in input.py/.dr (e.g. ball.state.output.position[0]) and choose Watch in Trick Sims View to add it without retyping it, as long as you're connected to the sim it belongs to.


Commands

The rest of the Sims/Connected views' actions (Connect, Run, Freeze, Stop, Add Watch, Remove Watch, Disconnect) are inline buttons and context-menu items on each sim or watched variable in the view itself, rather than Command Palette entries.


Settings

Setting Default Description
trick.home "" Explicit path to TRICK_HOME. Leave empty to auto-detect.
trick.useMakeForFlags true Resolve flags by invoking make against Trick's own makefiles (exact). Disable for a best-effort regex parse of S_overrides.mk only.
trick.cppStandard "" Override the C++ standard reported to the C/C++ extension. Leave empty to infer from -std= flags.
trick.python.generateStubs true Generate a trick.* stub and __builtins__.pyi per workspace folder for Pylance. Disable if you don't want files written into the workspace.

Configuration

The extension needs to know TRICK_HOME to resolve Trick's own system include paths and to run trick-CP. It is discovered automatically, in this order:

  1. the trick.home VS Code setting
  2. the TRICK_HOME environment variable VS Code was launched with
  3. walking up from an open file to find share/trick/makefiles/Makefile.common
  4. a .../trick/bin directory found on PATH

If none of these apply to your setup (for example, a sim developed outside the Trick source tree against an installed Trick with VS Code launched from a shell that doesn't export TRICK_HOME), set the trick.home setting explicitly.

See the extension's own README for implementation details.