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:
protoccompiler - 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:
- Don't install hooks during this transition period (simplest)
- 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 - Use
--no-verifyflag:git commit --no-verify -m "Your changes"
What the hooks do (when MORK dependencies are resolved):
- Prevent committing uncommented
[patch]sections inCargo.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):
-
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 -
The
[patch]sections are already uncommented inCargo.toml- leave them as-is -
Install Tree-Sitter CLI (if not already installed):
npm install -g tree-sitter-cli -
Build the project:
cargo build
Future workflow (when MORK dependencies are resolved):
- Clone only the dependency you want to modify
- Uncomment the appropriate
[patch]section inCargo.toml - Make your changes and test locally
- Before committing, comment out the
[patch]section - 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-freeDashMapstructures - 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:
-
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)
-
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
-
FxHash Integration (
src/ir/symbol_table.rs)- ~2x faster hashing for internal symbol tables
- ~1% overall CPU savings
-
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 strategydocs/PHASE2_RESULTS.md- Comprehensive benchmark resultsdocs/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, andbranchnodes. 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
SymbolTableBuilderin the pipeline to build these structures. Query them withfind_node_at_positionto 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
metadatafield in IR nodes now uses aHashMapfor flexible storage of version, symbol tables, and more. - Workspace Indexing: On initialization, all
.rhofiles in the workspace are indexed, with parsed IR, symbol tables, and inverted indices cached. - File Watching: Changes to
.rhofiles 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
rootUriinInitializeParamsto trigger workspace indexing. - Cache Access: Access cached data via
RholangBackend::workspace. - Debugging: Enable
RUST_LOG=debugfor 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-parserleverages Tree-Sitter, maintaining consistent performance. Local parsing is lightweight compared to RNode communication. - IR Integration: The
parse_to_irfunction insrc/tree_sitter.rsuses Tree-Sitter directly:
Modification is optional unless additional parser features (e.g., custom error handling) are needed.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) } - 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.