A Language Server Protocol (LSP) implementation for Comline, providing intelligent code editing features for .ids schema files.
- Diagnostics - Real-time syntax errors plus
comline-core's validation pass (undefined types, duplicate declarations, ...) β the same checkscomline buildruns. A type from another file of the package used without ausegets a specific error with an "Adduse ..." quick fix - Whole-package analysis - every
.idsfile in a package'ssrc/takes part, open or not, kept current through file watching; each package only sees its own files - Dependency packages - the dependencies
config.idpdeclares are indexed (path dependencies, and git pinscomline checkhas fetched; the editor never fetches), so imports, hover, go-to-definition and completion work into them under the dependency's name. Unresolved imports are errors, worded likecomline check's, with a "did you mean" quick fix; imports of a dependency that isn't fetched yet are marked as not checked, and itsconfig.idpentry says why. A dependency whose@unverifiedprotocols aren't accepted is an error on its entry, with a quick fix that writes theaccept_unverifiedlinecomline acceptwould - Standard library -
use std::β¦resolves against the std embedded incomline-core, like a read-only dependency: hover, go-to-definition (into virtualcomline-std:documents, whose text clients fetch with thecomline/stdSourcerequest), completion, and unresolved-import errors worded likecomline check's (std has no schema matching 'std::htp::Request' - did you mean 'std::http'?) - Module docs - a schema documents itself with
//!lines at its top, a package with the same at the top of itsconfig.idp. Hover any segment of a path (std,validatorsinuse std::validators::StringBounds, or in a qualified type name) for that module's docs, the modules below it and the types it declares;usecompletion shows the same docs beside each module and declaration.stdand each of its modules are documented this way - Document Symbols - Hierarchical outline view of structs, enums, protocols, and constants
- Workspace Symbols - Fuzzy search over every schema in the workspace
- Hover Information - Rich tooltips showing full type definitions and signatures
- Go to Definition / Find References / Rename - Across files, following each file's
usestatements; rename updatesuselines and leavesasaliases alone - Auto-Completion - Context-aware code suggestions (keywords, primitives, user and imported types,
@annotationkeys,usepaths); types from other files that aren't imported yet add theiruseline - Semantic Tokens - Comline syntax highlighting from a shared lexer
- Code Formatting, Signature Help β present but thin
std::imports aren't checked (std isn't part of a build yet either)
The crate is also an analysis library. --no-default-features drops the
server feature (tower-lsp, tokio, the doc store, the comline-lsp bin),
leaving parser + analysis + handlers over lsp-types and comline-core β
which builds for wasm32-unknown-unknown. The Comline playground links it
that way so its browser editor runs the same diagnostics, hover, completion
and highlighting as the LSP.
cd language-server
cargo build --releaseThe LSP server binary will be available at target/release/comline-lsp.
- Install a generic LSP client extension (e.g.,
vscode-languageclient) - Configure the server in your settings:
{
"comline.server": {
"command": "/path/to/comline-lsp",
"args": [],
"transport": "stdio"
}
}Using nvim-lspconfig:
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
-- Define Comline LSP
if not configs.comline_ls then
configs.comline_ls = {
default_config = {
cmd = {'/path/to/comline-lsp'},
filetypes = {'comline'},
root_dir = lspconfig.util.root_pattern('.git', 'config.idp'),
settings = {},
},
}
end
-- Setup Comline LSP
lspconfig.comline_ls.setup{}
-- Associate .ids files with comline filetype
vim.filetype.add({
extension = {
ids = 'comline',
},
})Add to your languages.toml:
[[language]]
name = "comline"
scope = "source.comline"
file-types = ["ids"]
roots = ["config.idp", ".git"]
language-servers = ["comline-ls"]
[language-server.comline-ls]
command = "/path/to/comline-lsp"(add-to-list 'lsp-language-id-configuration '(comline-mode . "comline"))
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection "/path/to/comline-lsp")
:major-modes '(comline-mode)
:server-id 'comline-ls))Once integrated with your editor, the LSP server provides:
Syntax errors are highlighted as you type:
struct User {
name string // β Error: Missing colon
age: i32
}
Navigate your schema using the document symbols panel:
π¦ User (3 fields)
ββ π name
ββ π age
ββ π email
π UserService (2 functions)
ββ β‘ getUser
ββ β‘ createUser
Hover over a struct/enum/protocol/const name to see its definition, a per-field wire-size breakdown prefixed with each field's wire index, and the field/variant/function count:
struct User {
name: string
age: i32
optional email: string
}
wire size: variable
- #0 name: variable
- #1 age: 4 bytes
- #2 email: variable
3 fields
Hover over a field's own name for just that field: its type, wire index,
size, optional, and any annotations or default value it carries.
optional email: string
field #2 of `User` (3 fields)
size: variable
optional: yes
Typing @ suggests the keys that are actually known for wherever the
cursor is β a different list on a field, a function, or a protocol/struct's
own leading annotation, since the grammar permits @key=value in all four
spots but each reads a different set of keys:
struct Message {
@v // β validators
}
protocol Chat {
@t // β timeout_ms, idempotent
function send(msg: string) -> bool;
}
@f // β framing (before `protocol`; a no-op before `struct`)
protocol Chat { ... }
Each suggestion's detail line explains what the key does and where it's
actually consumed (@idempotent is honest that it's advisory-only today β
see Per-call settings).
This is a curated, known-good subset, not validation β @key=value stays
an open namespace; an unrecognised key still parses, freezes, and is
silently ignored by whatever doesn't act on it.
Hover over any key β typed yourself or picked from completion β for the same information at a glance: what it does, its default when absent, the value it expects, and what actually reads it.
@timeout_ms
How long the client waits for the response before timing out.
Request/response calls only β a one-way call has nothing to wait for.
default: no timeout β waits indefinitely
value: an integer, in milliseconds
consumed by: the generated Rust client (`comline-rust`)
A use path completes one segment at a time, resolved the way
comline check resolves it:
use // β the package's namespaces, its dependencies, self / parent / package, std
use shared:: // β what's under the `shared` dependency
use api::common:: // β what `api::common` declares, then `*` and `{β¦}`
use api::common::{Error, // β what `api::common` declares that isn't listed yet
A dependency that isn't fetched yet is offered, marked as such, with nothing
under it until comline check fetches it.
Ctrl/Cmd + Click on any type reference to jump to its definition:
struct User { ... }
struct Request {
user: User // β Click jumps to User definition
}
src/
βββ main.rs # Entry point
βββ backend.rs # LSP protocol implementation
βββ document.rs # Document management
βββ parser.rs # Comline parser integration
βββ util.rs # Utility functions
βββ analysis/ # Semantic analysis
β βββ diagnostics.rs # Error reporting
β βββ symbols.rs # Symbol extraction
β βββ types.rs # Type resolution
β βββ imports.rs # Import resolution
βββ handlers/ # LSP feature handlers
βββ completion.rs
βββ definition.rs
βββ formatting.rs
βββ hover.rs
βββ references.rs
βββ rename.rs
βββ symbols.rs
cargo testAll tests should pass:
- Parser tests
- Diagnostic tests
- Symbol extraction tests
- Handler tests
- Integration tests
Set the RUST_LOG environment variable for debugging:
RUST_LOG=debug /path/to/comline-lspLogs are written to stderr and won't interfere with the LSP protocol communication over stdio.
The server advertises the following capabilities:
- Text Document Sync - Full document synchronization
- Hover Provider - Type information on hover
- Completion Provider - Trigger characters:
.,:,(,>, space,@ - Definition Provider - Go to definition, across files
- References Provider - Find all references, across files
- Document Symbol Provider - Outline view
- Workspace Symbol Provider - Global symbol search
- Document Formatting - Auto-formatting
- Rename Provider - Symbol renaming across files, with prepare-rename
- Code Action Provider - Quick fixes (add a missing
use, apply an unresolved import's "did you mean", accept a dependency's@unverifiedprotocols inconfig.idp) - Semantic Tokens - Enhanced highlighting
- Workspace Folders - Multi-root workspaces;
workspace/didChangeWatchedFileskeeps the index current
- tower-lsp - LSP protocol framework
- comline-core - Comline parser and AST
- rust-sitter - Parser infrastructure
- tokio - Async runtime
- dashmap - Concurrent hashmap for document storage
- tracing - Structured logging
Contributions are welcome! Areas for improvement:
- Signature Help - Function argument hints
- Formatting - A fuller Comline code formatter
- Code Actions - More quick fixes and refactorings
See the main Comline project for licensing information.
Built using: