3 releases
| 0.1.2 | Oct 13, 2025 |
|---|---|
| 0.1.1 | Oct 12, 2025 |
| 0.1.0 | Oct 11, 2025 |
#717 in Database interfaces
160KB
3.5K
SLoC
DBML Language Server
A high-performance, standalone Language Server Protocol (LSP) implementation for DBML (Database Markup Language), written in Rust.
Features
- 🔍 Real-time Error Diagnostics - Instant syntax and semantic validation with detailed error messages
- 🎨 Semantic Syntax Highlighting - Context-aware highlighting for tables, columns, enums, and relationships
- 🔗 Go to Definition - Navigate to table and column definitions with a single click
- ✏️ Rename Refactoring - Safely rename symbols across your entire schema
- 🛡️ Robust Error Recovery - Continue getting language features even with syntax errors
- ⚡ High Performance - Asynchronous architecture built on Tokio for responsive editing
- 🔌 Editor Agnostic - Works with any LSP-compatible editor (VS Code, Neovim, Vim, Emacs, Sublime Text, etc.)
Installation
From crates.io
cargo install dbml-lsp
From Source
git clone https://github.com/your-username/dbml-lsp.git
cd dbml-lsp
cargo install --path .
Pre-built Binaries
Download pre-built binaries from the releases page.
Quick Start
1. Install the Server
cargo install dbml-lsp
2. Configure Your Editor
Visual Studio Code
Install a generic LSP extension or create a minimal one:
Using settings.json:
{
"dbml.lsp.path": "dbml-lsp"
}
Or use the VS Code extension (coming soon).
Neovim
Add to your init.lua:
vim.api.nvim_create_autocmd('FileType', {
pattern = 'dbml',
callback = function()
vim.lsp.start({
name = 'dbml-lsp',
cmd = {'dbml-lsp'},
root_dir = vim.fs.dirname(vim.fs.find({'.git'}, { upward = true })[1]),
})
end,
})
-- Set filetype for .dbml files
vim.filetype.add({
extension = { dbml = 'dbml' },
})
Vim (vim-lsp)
Add to your .vimrc:
if executable('dbml-lsp')
au User lsp_setup call lsp#register_server({
\ 'name': 'dbml-lsp',
\ 'cmd': {server_info->['dbml-lsp']},
\ 'allowlist': ['dbml'],
\ })
endif
autocmd BufNewFile,BufRead *.dbml set filetype=dbml
Emacs (lsp-mode)
Add to your Emacs config:
(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(dbml-mode . "dbml"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection "dbml-lsp")
:major-modes '(dbml-mode)
:server-id 'dbml-lsp))
(add-to-list 'auto-mode-alist '("\\.dbml\\'" . dbml-mode))
(add-hook 'dbml-mode-hook #'lsp)
Sublime Text
Install the LSP package, then add to LSP settings:
{
"clients": {
"dbml-lsp": {
"enabled": true,
"command": ["dbml-lsp"],
"selector": "source.dbml"
}
}
}
3. Test It Out
Create a file example.dbml:
Table users {
id int [pk, increment]
username varchar(255) [unique, not null]
email varchar(255) [unique, not null]
created_at timestamp [default: `now()`]
Indexes {
(email) [unique]
(created_at)
}
}
Table posts {
id int [pk, increment]
user_id int [not null, ref: > users.id]
title varchar(500) [not null]
content text
status post_status [default: 'draft']
created_at timestamp [default: `now()`]
}
Enum post_status {
draft
published
archived
}
Ref: posts.user_id > users.id
You should now see:
- ✅ Syntax highlighting
- ✅ Error diagnostics for invalid syntax
- ✅ Go to definition (click on table/column references)
- ✅ Rename refactoring (F2 on symbols)
Supported DBML Features
| Feature | Supported | Notes |
|---|---|---|
| Tables | ✅ | With columns, aliases, and settings |
| Columns | ✅ | All data types and settings |
| Relationships | ✅ | All types: ->, <, >, <> |
| Inline Refs | ✅ | [ref: > table.column] |
| Composite Keys | ✅ | Multi-column foreign keys |
| Enums | ✅ | With members and notes |
| Indexes | ✅ | Single, composite, and expression-based |
| Projects | ✅ | Project metadata |
| Comments | ✅ | Single-line // and multi-line /* */ |
| Notes | ✅ | Table and column notes |
Architecture
Technology Stack
- tower-lsp - Async LSP framework built on Tower and Tokio
- chumsky - Parser combinator library with excellent error recovery
- tokio - Async runtime for concurrent request handling
- dashmap - Thread-safe concurrent HashMap for document caching
Pipeline
DBML Source Code
↓
Lexer (Tokenization)
↓
Parser (AST Construction)
↓
Semantic Analyzer (Symbol Resolution)
↓
LSP Features (Diagnostics, Navigation, etc.)
The language server uses a multi-stage analysis pipeline:
- Lexical Analysis - Tokenizes DBML source with comment handling
- Syntactic Analysis - Builds an Abstract Syntax Tree with error recovery
- Semantic Analysis - Creates symbol tables and validates relationships
This architecture ensures robust error handling and allows features to work even when the document contains syntax errors.
Development
Building
# Debug build
cargo build
# Release build (optimized)
cargo build --release
# The binary will be at:
# target/release/dbml-lsp
Testing
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_parse_simple_table
# Run with logging
RUST_LOG=debug cargo test
Project Structure
dbml-lsp/
├── Cargo.toml # Dependencies and project metadata
├── src/
│ ├── main.rs # Entry point
│ ├── server.rs # LSP server implementation
│ ├── state.rs # Document state management
│ ├── ast.rs # Abstract Syntax Tree definitions
│ └── analysis/ # Parsing and analysis pipeline
│ ├── mod.rs
│ ├── token.rs # Token definitions
│ ├── lexer.rs # Lexical analyzer
│ ├── parser.rs # Parser implementation
│ └── semantic.rs # Semantic analyzer
└── tests/
└── integration_tests.rs
Troubleshooting
Server Won't Start
# Check if the binary is executable
chmod +x $(which dbml-lsp)
# Verify it runs
dbml-lsp --version
Enable Debug Logging
Set the RUST_LOG environment variable:
# In your editor config or terminal
RUST_LOG=debug dbml-lsp
VS Code: Check the Output panel → "DBML Language Server"
Neovim: :LspLog
Features Not Working
- Ensure the file has a
.dbmlextension - Check that the LSP client is configured correctly
- Look for errors in your editor's LSP logs
- Verify the server is running: check your editor's LSP status
Performance
The server is designed for high performance:
- Asynchronous: Non-blocking request handling
- Concurrent: Multiple documents analyzed in parallel
- Efficient: In-memory caching with automatic cleanup
- Scalable: Handles large schemas (10,000+ lines)
Typical performance metrics:
- Parse time: ~5ms for 1000-line file
- Memory usage: ~50MB for 10 open documents
- Startup time: ~100ms
Roadmap
Planned Features
- Code Completion - Auto-complete for table names, columns, and keywords
- Hover Information - Show column types and relationship info on hover
- Document Symbols - Outline view of tables, enums, and relationships
- Workspace Symbols - Project-wide symbol search
- Code Actions - Quick fixes for common errors
- Formatting - Auto-format DBML files
- Incremental Parsing - Faster updates for large files
- Signature Help - Parameter hints for settings
- Folding Ranges - Collapse/expand table definitions
Future Enhancements
- Multi-file project support
- Import/export to SQL
- Database introspection
- Diagram generation
- Schema validation rules
- Custom lint rules
Contributing
Contributions are welcome! Here's how you can help:
- Report Bugs - Open an issue with reproduction steps
- Suggest Features - Describe your use case
- Submit PRs - Fork, create a feature branch, and submit a PR
- Improve Docs - Help make the documentation clearer
- Write Tests - Increase test coverage
Development Setup
# Clone the repository
git clone https://github.com/your-username/dbml-lsp.git
cd dbml-lsp
# Install dependencies
cargo build
# Run tests
cargo test
# Format code
cargo fmt
# Lint
cargo clippy
Guidelines
- Follow Rust conventions and idioms
- Write tests for new features
- Update documentation
- Keep commits focused and descriptive
- Ensure
cargo testpasses before submitting
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built with tower-lsp by Eyal Kalderon
- Parsing powered by chumsky by Joshua Barretto
- Inspired by the DBML specification by Holistics
- Special thanks to the Rust LSP community for resources and examples
Related Projects
- dbdiagram.io - Official DBML database designer
- rust-analyzer - Inspiration for LSP architecture
- tree-sitter-dbml - Tree-sitter grammar for DBML
Dependencies
~13–20MB
~329K SLoC