Skip to main content

Interactive REPL (BIM Laboratory)

The Paracore REPL (Read-Eval-Print Loop) is a persistent C# scratchpad with direct, real-time access to the Revit API and Paracore's high-level automation helpers. It is designed for iterative API discovery and rapid data exploration.

Interactive Modes

The REPL is located in the Console Tab and provides two mutually exclusive entry modes designed to toggle based on your active task:

  1. Single-Line REPL: A fast command-line at the footer for quick one-off commands and API queries. Entries here are saved in a Command Cache; use the Up and Down arrows to cycle through your history.
  2. Multi-Line Workspace: A dedicated scratchpad for longer snippets and logic prototyping. It features a standalone Run button and supports Ctrl + Enter for execution.

[!NOTE] While the Multi-Line Workspace supports the same "magic" syntax as the single-line input, entries written here are not saved in the arrow-key Command Cache. This is because the workspace provides full Load, Save, and Save As file operations, allowing you to persist your logic as pure .cs files instead of relying on a volatile command history.

Console Full View

File Management

Unlike Modular Projects, scripts for the multi-line REPL do not require a specific folder structure. You can Save and Load pure .cs files from any folder on your machine, making the REPL the ideal environment for rough drafts and scratchpad logic.

Multi-Line Editor Controls

Mode Switching

  • Expanding: Clicking the expand icon transitions the UI to the Multi-Line Workspace, removing the single-line input to maximize the editor area.
  • Collapsing: Collapsing the workspace removes the larger editor and restores the Single-Line REPL at the footer, providing a streamlined view with maximum Console History visibility.
Single-Line REPL

The Interactive Workflow

The REPL operates on a "Read-Eval-Print" cycle. You write pure C# code (no classes or namespaces required) and execute it instantly in the active Revit session.

1. Instant Execution

  • Single-Line: Type your command and press Enter.
  • Multi-Line: Click the Run button or press Ctrl + Enter to execute the entire workspace.

2. Syntax & Discovery

Both modes are designed for speed, but follow specific rules to ensure successful execution:

  • Semicolons: Mandatory for variable assignments (var x = 10;), but optional for one-liners and queries (Selection.Count).
  • Count vs Count(): Use the .Count property for immediate lists (e.g., Selection.Count, GetMagicNames().Count) and the .Count() method for dynamic enumerable queries (e.g., GetElements<Wall>().Where(w => ...).Count()).
  • Magic Discovery: Use the Magic Accessors for direct and simplified property access.

3. Shared Context & Multi-Turn Interaction

Both modes share the same persistent execution environment. This means you can initialize complex data in the Multi-Line Workspace and surgically query it later from the Single-Line REPL.

Because the environment is persistent, you can define a variable in one turn and reference it in the next:

// Multi-Line Workspace (Turn 1)
var rooms = GetElements<Room>();
Println($"Found {rooms.Count} rooms");
// Single-Line REPL (Turn 2)
rooms.Table().Select(r => new { r.Name, r.Area })
REPL Session History

Global Reference

These objects and helpers are globally injected into every REPL turn.

ObjectTypeDescription
DocDocumentThe active Revit database Document.
UIDocUIDocumentThe Revit UI Document (active window).
SelectionList<Element>Elements currently selected in the Revit viewport.
Println(msg)voidFormatted console output with string interpolation support.

Element Accessors (Extension Methods)

Paracore extends standard Revit elements with "Magic" accessors that handle unit conversion and identity resolution automatically.

Reading Data

MethodReturnsNotes
GetStr(name)stringSmart resolution: returns the Name if the parameter is an ID (e.g., Level).
GetNum(name)doubleReturns raw internal units (feet/sqft).
GetNum(name, unit)doubleConverts internal units to requested unit (e.g., "mm", "m2").
GetVal(name)stringReturns the formatted string exactly as seen in the Revit Properties palette.

Writing Data

MethodDescription
SetVal(name, value)The Smart Setter. Automatically parses units, IDs, and strings.
SetNum(name, num, unit)Explicit unit-aware numeric setter.

Magic Discovery

Use these high-level accessors to instantly find elements by name, category, or class.

// Single-Line REPL examples
var doors = GetElements("Doors"); // All door instances
var types = GetElements<FamilySymbol>("Doors"); // All door types
GetElement("Level 1").Select() // Chain with Select()
GetMagicNames().Count // Total number of magic names
GetElements<Element>() // Universal Accessor: Target everything

// Use the new Fluent extension
Println(Selection[0].FamilyName())
Master Tip: Target Everything

Since Element is the base class for all Revit objects, calling GetElements<Element>() is the most powerful way to perform bulk audits or search for data across the entire model.


Visualization API

Pipe your data directly into the Analytics Tab for interactive analysis.

Command / MethodTypeDescription
Table(data)GlobalRenders a grid. Returns void (not chainable).
.Table()ExtensionRenders a grid and returns the collection. Chainable.
.PieChart() / PieGraph(data)BothRenders a high-contrast pie chart (requires name and value).
.BarChart() / BarGraph(data)BothRenders a high-contrast bar chart.
.Select() / .Zoom()ExtensionChain these to any collection to select or focus elements.
Analytics Visualization
// Multi-Line Snippet (Workshop)
// Create the "Walls per Level" bar graph shown above
var data = GetElements<Wall>()
.GroupBy(w => w.GetStr("Base Constraint")) // Use the standard Revit parameter name
.Select(g => new { name = g.Key, value = g.Count() });

BarGraph(data);

Example Recipes

1. Element Audit

Find elements more than 1000m from the origin (likely misplaced or CAD import artifacts).

var limit = 1000.InputUnit("m");
var lost = GetElements<Element>().Where(e => {
var bb = e.get_BoundingBox(null);
if (bb == null) return false;
var center = (bb.Min + bb.Max) / 2.0;
// Semantically clear: Distance from one point to another
return center.DistanceTo(XYZ.Zero) > limit;
});
if (lost.Any()) {
lost.Select().Zoom()
Selection[0].Peek()
}

2. Room Data Sync

Copy the Room Name to the "Comments" of every door hosted within that room.

// Multi-Line Snippet (Workshop)
Transact("Sync Room Data", () => {
foreach (var door in GetElements<FamilyInstance>("Doors")) {
var room = door.Room ?? door.FromRoom ?? door.ToRoom;
if (room != null) door.SetVal("Comments", $"In: {room.Name}");
}
});

3. Quick Area Chart

Visualize the distribution of Room areas by Level.

// Multi-Line Snippet (Workshop)
var stats = GetElements<Room>().Where(r => r.Area > 0)
.GroupBy(r => r.GetStr("Level"))
.Select(g => new { name = g.Key, value = g.Sum(r => r.GetNum("Area", "m2")) });

PieGraph(stats);

📖 Full API Reference: For an exhaustive list of every available helper, parameter accessor, and unit conversion method, see the Paracore Extension Methods.