Rholang Language Server

October 31, 2025 · View on GitHub

Language Server Protocol (LSP) implementation for Rholang, the smart contract language for the RChain blockchain.

Features

  • Go to Definition - Navigate to symbol declarations with cross-file support
  • Find References - Find all usages of a symbol across the workspace
  • Hover Information - View symbol types, signatures, and documentation
  • Semantic Rename - Safely rename symbols with workspace-wide atomic edits
  • Document Symbols - Outline view of contracts, variables, and definitions
  • Document Highlighting - Highlight all occurrences of the symbol under cursor
  • Diagnostics - Syntax error detection with local Tree-Sitter parsing
  • MeTTa Support - Embedded MeTTa language support within Rholang strings
  • Pattern Matching - Contract overload resolution with multi-argument matching
  • Cross-File Navigation - Navigate definitions and references across multiple files

Installation

Package Managers

Debian/Ubuntu

Download the .deb package from Releases:

sudo dpkg -i rholang-language-server_0.1.0_amd64.deb
# If dependencies are missing:
sudo apt-get install -f

RedHat/Fedora/CentOS

Download the .rpm package from Releases:

sudo dnf install rholang-language-server-0.1.0-1.x86_64.rpm
# Or for older systems:
sudo yum install rholang-language-server-0.1.0-1.x86_64.rpm

Arch Linux

Download the package from Releases:

sudo pacman -U rholang-language-server-0.1.0-1-x86_64.pkg.tar.zst

macOS

Download the .dmg from Releases, mount it, and copy to /usr/local/bin:

# After downloading and mounting the DMG:
sudo cp "/Volumes/Rholang Language Server/rholang-language-server" /usr/local/bin/

Binary Download

Download the appropriate archive for your platform from Releases:

# Linux x86_64
wget https://github.com/F1R3FLY-io/rholang-language-server/releases/download/v0.1.0/rholang-language-server-linux-x86_64.tar.gz
tar xzf rholang-language-server-linux-x86_64.tar.gz
chmod +x rholang-language-server
sudo mv rholang-language-server /usr/local/bin/

# macOS (ARM64)
wget https://github.com/F1R3FLY-io/rholang-language-server/releases/download/v0.1.0/rholang-language-server-macos-arm64.tar.gz
tar xzf rholang-language-server-macos-arm64.tar.gz
chmod +x rholang-language-server
sudo mv rholang-language-server /usr/local/bin/

Requirements

For End Users (Binary Installation)

  • Platform: Linux x86_64/ARM64 or macOS x86_64/ARM64

For Building from Source

  • Rust: Nightly toolchain (edition 2024)
  • Protobuf: protoc compiler
  • Node.js/npm: For Tree-Sitter CLI installation
  • Tree-Sitter CLI: For grammar generation (npm install -g tree-sitter-cli)

Editor Integration

VSCode

Install the Rholang extension and add to your settings.json:

{
  "rholang.languageServer.path": "/usr/local/bin/rholang-language-server",
  "rholang.trace.server": "verbose"
}

Neovim

With nvim-lspconfig:

require'lspconfig'.rholang_ls.setup{
  cmd = {"/usr/local/bin/rholang-language-server"},
  filetypes = {"rholang"},
  root_dir = require'lspconfig'.util.root_pattern(".git"),
}

Emacs

With lsp-mode:

(add-to-list 'lsp-language-id-configuration '(rholang-mode . "rholang"))
(lsp-register-client
 (make-lsp-client :new-connection (lsp-stdio-connection "/usr/local/bin/rholang-language-server")
                  :major-modes '(rholang-mode)
                  :server-id 'rholang-ls))

Other LSP Clients

The language server communicates via standard LSP protocol over stdio. Configure your LSP client to launch:

/usr/local/bin/rholang-language-server

Building from Source

Prerequisites

Install required build tools:

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y protobuf-compiler npm

# macOS
brew install protobuf npm

# Install Tree-Sitter CLI (all platforms)
npm install -g tree-sitter-cli

# Install Rust nightly
rustup install nightly
rustup default nightly

Build Instructions

Clone and build the language server:

git clone https://github.com/F1R3FLY-io/rholang-language-server.git
cd rholang-language-server

# Build in debug mode
cargo build

# Or build in release mode
cargo build --release

# The binary will be at:
# - Debug: target/debug/rholang-language-server
# - Release: target/release/rholang-language-server

Note: The build process will automatically clone git dependencies. For local development with modified dependencies, see the Development section below.

Development

Git Hooks Setup

⚠️ Important: Due to MORK transitive dependencies, local path overrides are currently required for building. The git hooks will block commits with uncommented [patch] sections, which conflicts with this requirement.

Options:

  1. Don't install hooks during this transition period (simplest)
  2. Install but uninstall when committing:
    # Install hooks
    ./scripts/install-git-hooks.sh
    
    # When ready to commit, uninstall temporarily
    ./scripts/uninstall-git-hooks.sh
    git commit -m "Your changes"
    
    # Reinstall for future work
    ./scripts/install-git-hooks.sh
    
  3. Use --no-verify flag: git commit --no-verify -m "Your changes"

What the hooks do (when MORK dependencies are resolved):

  • Prevent committing uncommented [patch] sections in Cargo.toml
  • Prevent committing local path dependencies that should use git sources
  • Ensure repository hygiene for dependency management

See .githooks/README.md for more information.

Working with Local Dependencies

⚠️ Current Requirement: The [patch] sections in Cargo.toml are currently uncommented by default and must remain uncommented to build the project. This is due to MORK transitive dependencies that are not yet published or accessible via git.

Current workflow (temporary, until MORK dependencies are resolved):

  1. Clone all required dependencies with correct branches:

    cd ..
    git clone --depth=1 --branch main https://github.com/trueagi-io/MORK.git
    git clone --depth=1 --branch master https://github.com/Adam-Vandervorst/PathMap.git
    git clone --depth=1 --branch dylon/rholang-language-server https://github.com/F1R3FLY-io/MeTTa-Compiler.git
    git clone --depth=1 --branch dylon/named-comment-nodes https://github.com/F1R3FLY-io/rholang-rs.git
    git clone --depth=1 --branch dylon/mettatron https://github.com/F1R3FLY-io/f1r3node.git
    cd rholang-language-server
    
  2. The [patch] sections are already uncommented in Cargo.toml - leave them as-is

  3. Install Tree-Sitter CLI (if not already installed):

    npm install -g tree-sitter-cli
    
  4. Build the project:

    cargo build
    

Future workflow (when MORK dependencies are resolved):

  1. Clone only the dependency you want to modify
  2. Uncomment the appropriate [patch] section in Cargo.toml
  3. Make your changes and test locally
  4. Before committing, comment out the [patch] section
  5. The pre-commit hook will verify this is done correctly

Available Local Overrides

The following dependencies can be overridden locally via [patch] sections (see end of Cargo.toml):

  • MORK (mork, mork-expr, mork-frontend) - https://github.com/trueagi-io/MORK.git (branch: main)
  • MeTTa-Compiler (mettatron, tree-sitter-metta) - https://github.com/F1R3FLY-io/MeTTa-Compiler.git (branch: dylon/rholang-language-server)
  • PathMap (pathmap) - https://github.com/Adam-Vandervorst/PathMap.git (branch: master)
  • rholang-rs (rholang-parser, rholang-tree-sitter) - https://github.com/F1R3FLY-io/rholang-rs.git (branch: dylon/named-comment-nodes)
  • f1r3node (rholang) - https://github.com/F1R3FLY-io/f1r3node.git (branch: dylon/mettatron)

Dependency Management Philosophy

⚠️ Temporary State: Due to MORK transitive dependencies, the [patch] sections are currently uncommented and required for building. This is a temporary situation until MORK publishes its dependencies properly.

Target State (when MORK dependencies are resolved):

Primary dependencies (in [dependencies] section) will use git repositories. This ensures:

  • ✅ CI builds work without local clones
  • ✅ New contributors can build immediately after cloning
  • ✅ Reproducible builds across environments
  • ✅ No accidental commits of local paths

Local overrides (in [patch] sections, commented by default) will enable:

  • 🔧 Local development with modified dependencies
  • 🔧 Testing changes before upstreaming
  • 🔧 Debugging dependency issues

Git hooks will ensure local overrides never get committed accidentally.

Testing

Run the test suite:

cargo test --verbose

The tests include:

  • Unit tests - Core functionality and IR transformations
  • Integration tests - LSP protocol features (goto-definition, references, rename, etc.)
  • Tree-Sitter tests - Parser grammar validation

All tests run without external dependencies and complete in seconds.

Performance

The language server has been extensively optimized through profiling-driven development:

Phase 1: Lock-Free Concurrent Access

  • Replaced Arc<RwLock<WorkspaceState>> with lock-free DashMap structures
  • Result: 2-5x throughput improvement for concurrent LSP requests
  • Zero read contention for workspace state access

Phase 2: Data-Driven Optimizations

Based on profiling analysis with perf (39GB profiling data):

Key Optimizations:

  1. Parse Tree Caching (src/parsers/parse_cache.rs)

    • Lock-free cache using DashMap with hash collision detection
    • 1,000-10,000x speedup on cache hits (20-30ns vs 37-263µs parsing)
    • Capacity: 1000 entries (~60-110MB memory)
  2. Adaptive Parallelization (src/language_regions/async_detection.rs)

    • Dynamically chooses sequential vs parallel processing based on workload
    • Eliminates 45-50% Rayon overhead for small batches
    • Threshold: 5+ documents AND 100+µs estimated work
  3. FxHash Integration (src/ir/symbol_table.rs)

    • ~2x faster hashing for internal symbol tables
    • ~1% overall CPU savings
  4. Incremental Parsing (src/lsp/document.rs)

    • Tree-Sitter incremental updates on document changes
    • 7-50x faster than full re-parsing for typical edits

Benchmark Results (Phase 2 vs Phase 1):

  • Virtual document detection (simple): -45.1% (2.2x faster)
  • Virtual document detection (complex): -54.0% (2.2x faster)
  • Sequential processing (cache hits): -50.9% (2.0x faster)
  • Symbol resolution: -15.7% (1.2x faster)

Combined Impact:

  • Initial workspace indexing: ~3-11x faster
  • Document editing (incremental): ~10-100x faster
  • Symbol navigation (concurrent): ~2-3x faster
  • Undo/redo operations: ~100-1000x faster (parse cache)

Overall: Language server is approximately 4-8x faster for typical LSP workflows.

Performance Monitoring

The language server includes built-in performance metrics collection (src/metrics.rs):

  • Parse cache hit rate tracking
  • LSP request latency histograms (p50, p95, p99)
  • Workspace indexing statistics
  • Error counters

Metrics are collected with minimal overhead (~10-20ns per operation) using atomic counters.

Benchmarking

Run benchmarks with:

cargo bench --bench lsp_operations_benchmark  # LSP features
cargo bench --bench real_world_benchmark       # Real-world file sizes
cargo bench --bench detection_worker_benchmark # Virtual document detection

Detailed optimization documentation:

  • docs/PHASE2_OPTIMIZATION_PLAN.md - Profiling analysis and strategy
  • docs/PHASE2_RESULTS.md - Comprehensive benchmark results
  • docs/PERFORMANCE_PROFILING_GUIDE.md - Profiling methodology

Intermediate Representation (IR) Design

The Rholang Language Server employs an Intermediate Representation (IR) to represent parsed Rholang code, designed with immutability and persistence as core properties:

  • Immutability:

    • Once created, the IR tree cannot be modified. This ensures thread safety by eliminating data races in concurrent operations and maintains consistency across transformations, as original nodes remain unchanged.
    • Why it matters: Simplifies reasoning about code transformations (e.g., optimizations), making the system more predictable and debuggable.
  • Persistence:

    • Utilizes structural sharing to allow new IR versions to reuse unchanged subtrees, reducing memory usage.
    • Enables versioning for features like undo/redo or transformation history with minimal overhead, and enhances efficiency by avoiding duplication of large tree segments.
    • Why it matters: Supports efficient handling of large codebases and facilitates backtracking or analysis without performance penalties.

Symbol Table and Inverted Index

The language server now includes a hierarchical symbol table and inverted index, built as part of the IR pipeline:

  • Symbol Table: Manages scoping for new, let, contract, input, case, and branch nodes. Symbols are stored with their type, declaration, and definition locations, accessible via node metadata.
  • Inverted Index: Tracks all usage locations of symbols, enabling features like semantic renaming.
  • Usage: Use SymbolTableBuilder in the pipeline to build these structures. Query them with find_node_at_position to access symbol information at any source position.

Example usage in the pipeline:

let mut pipeline = Pipeline::new();
pipeline.add_transform(Transform {
    id: "symbol_table".to_string(),
    dependencies: vec![],
    visitor: Arc::new(SymbolTableBuilder::new(ir.clone())),
});
let (transformed, inverted_index) = builder.build();

See src/ir/transforms/symbol_table_builder.rs for details.

Workspace Indexing and Symbol Management

The Rholang Language Server now supports:

  • Dynamic Metadata: The metadata field in IR nodes now uses a HashMap for flexible storage of version, symbol tables, and more.
  • Workspace Indexing: On initialization, all .rho files in the workspace are indexed, with parsed IR, symbol tables, and inverted indices cached.
  • File Watching: Changes to .rho files trigger reindexing, keeping caches current across platforms.
  • Cross-File Linking: Symbols are linked across files, updating inverted indices for cross-references.
  • Explicit Document Handling: Opened documents override on-disk versions, with Tree-Sitter enabling incremental updates.

Usage

  • Initialization: Provide a rootUri in InitializeParams to trigger workspace indexing.
  • Cache Access: Access cached data via RholangBackend::workspace.
  • Debugging: Enable RUST_LOG=debug for indexing and linking logs.

See src/backend.rs for implementation details.

Benefits

  • Thread Safety: Safe concurrent parsing and transformation.
  • Consistency: Predictable transformation outcomes.
  • Versioning: Track changes or revert transformations easily.
  • Efficiency: Memory and performance optimization via structural sharing.
  • Facilitates Operations: Ideal for optimization, analysis, and formatting tasks, as transformations produce new trees without altering originals.

For example, transforming not not true to true creates a new IR tree, preserving the original for reference or rollback, with shared subtrees minimizing resource use.

Additional Considerations

  • Performance: The rholang-parser leverages Tree-Sitter, maintaining consistent performance. Local parsing is lightweight compared to RNode communication.
  • IR Integration: The parse_to_ir function in src/tree_sitter.rs uses Tree-Sitter directly:
    pub fn parse_to_ir<'a>(tree: &'a Tree, source_code: &'a str) -> Arc<Node<'a>> {
        debug!("Parsing Tree-Sitter tree into IR for source: {}", source_code);
        convert_ts_node_to_ir(tree.root_node(), source_code)
    }
    
    Modification is optional unless additional parser features (e.g., custom error handling) are needed.
  • Logging: Debug-level logging is optional and controlled via RUST_LOG=debug, aiding troubleshooting without overwhelming output.

Conclusion

This integration enhances the Rholang Language Server with local syntax validation via rholang-parser, improving responsiveness and error reporting. The immutable, persistent IR design ensures robust, efficient transformations, maintaining readability and maintainability through modular design and concise logging.