How the Sway LSP Provides IDE Support and Error Highlighting: A Deep Dive into the Language Server Protocol Implementation
The Sway LSP transforms the compiler into a real-time IDE backend by continuously compiling workspace files and translating compiler diagnostics into LSP Diagnostic messages that editors display as error highlighting, warnings, and inline hints.
The Sway Language Server Protocol (LSP) implementation in the FuelLabs/sway repository provides comprehensive IDE support and error highlighting by bridging the Sway compiler with editor clients. This architecture enables features like real-time diagnostics, code completion, and semantic highlighting by continuously analyzing source code in the background.
Architecture Overview: From Compiler to LSP Backend
The Sway LSP operates as a JSON-RPC server that exposes compiler functionality through standardized LSP capabilities. The architecture separates capability declaration, state management, and compilation orchestration into distinct modules.
Server Capabilities Declaration in lib.rs
The entry point in sway-lsp/src/lib.rs defines every IDE feature the server supports. The server_capabilities() function returns a ServerCapabilities struct that tells the client which operations are available, including diagnostics, completion, hover, and semantic tokens.
pub fn server_capabilities() -> ServerCapabilities {
ServerCapabilities {
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
..Default::default()
}),
definition_provider: Some(OneOf::Left(true)),
document_highlight_provider: Some(OneOf::Left(true)),
document_symbol_provider: Some(OneOf::Left(true)),
hover_provider: Some(HoverProviderCapability::Simple(true)),
inlay_hint_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Right(RenameOptions { … })),
semantic_tokens_provider: Some(
SemanticTokensOptions {
legend: SemanticTokensLegend {
token_types: capabilities::semantic_tokens::SUPPORTED_TYPES.to_vec(),
token_modifiers: capabilities::semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
},
range: Some(true),
..Default::default()
}
.into(),
),
text_document_sync: Some(TextDocumentSyncCapability::Kind(
TextDocumentSyncKind::INCREMENTAL,
)),
…
}
}
The start() function in the same file wires this service to stdin and stdout, creating the communication channel that editors use to send file contents and receive error highlighting.
State Management and Session Handling in server_state.rs
sway-lsp/src/server_state.rs contains the ServerState struct, which acts as the central coordinator. It manages:
- Workspace synchronization – Temporary workspaces that mirror the user's project structure
- Session storage – Per-project
Sessionobjects that cache diagnostics and compilation results - Background compilation – A dedicated thread that runs the compiler without blocking the LSP main loop
- Token mapping – The
TokenMapthat connects source ranges to semantic information for features like hover and go-to-definition
How Error Highlighting Works in the Sway LSP
Error highlighting in the Sway LSP follows a continuous compilation pipeline. When a developer opens or edits a file, the server triggers a background compilation, converts compiler diagnostics into LSP format, and pushes them to the editor.
Triggering Compilation on File Open in notification.rs
When the client sends a textDocument/didOpen notification, the handler in sway-lsp/src/handlers/notification.rs executes a six-step workflow:
- Initialize workspace –
state.get_or_init_sync_workspacecreates a temporary workspace mirroring the project - Retrieve session –
state.uri_and_session_from_workspacegets or creates aSessionto store diagnostics - Track document –
state.documents.handle_open_filerecords the file for token mapping - Queue compilation –
send_new_compilation_requestbuilds aCompilationContextand pushes it to the background thread - Await parsing –
state.wait_for_parsing()blocks until compilation completes - Publish diagnostics –
state.publish_diagnosticsforwards results to the client
pub async fn handle_did_open_text_document(
state: &ServerState,
params: DidOpenTextDocumentParams,
) -> Result<(), LanguageServerError> {
let file_uri = ¶ms.text_document.uri;
let sync_workspace = state.get_or_init_sync_workspace(file_uri).await?;
let (uri, session) = state.uri_and_session_from_workspace(¶ms.text_document.uri)?;
state.documents.handle_open_file(&uri).await;
send_new_compilation_request(state, session.clone(), &uri, None, false, sync_workspace);
state.is_compiling.store(true, Ordering::SeqCst);
state.wait_for_parsing().await;
state
.publish_diagnostics(uri, params.text_document.uri, session)
.await;
Ok(())
}
Background Compilation and Token Map Construction
The compilation thread in ServerState::spawn_compilation_thread receives the CompilationContext, runs session::compile, and then traverses the resulting ASTs to build a TokenMap.
Three traversal passes populate the map with different levels of semantic information:
- Lexed pass (
traverse/lexed_tree.rs) – Raw lexer tokens including keywords and literals - Parsed pass (
traverse/parsed_tree.rs) – Untyped parser nodes representing syntax structure - Typed pass (
traverse/typed_tree.rs) – Fully typed AST nodes with semantic information
This TokenMap enables precise error positioning and powers features like hover tooltips and go-to-definition.
Converting Compiler Diagnostics to LSP Format in diagnostic.rs
After compilation, sway-lsp/src/capabilities/diagnostic.rs transforms compiler diagnostics into LSP Diagnostic objects. The get_diagnostics function processes three severity levels:
- Errors – Critical compilation failures
- Warnings – Non-fatal issues that should be addressed
- Info – Additional context and suggestions
pub fn get_diagnostics(
infos: &[CompileInfo],
warnings: &[CompileWarning],
errors: &[CompileError],
source_engine: &SourceEngine,
) -> DiagnosticMap {
// … iterate over infos, warnings, errors
// create Diagnostic with range, severity, message, optional data
}
The function also attaches DiagnosticData containing additional context (such as unknown symbol names) that code-action providers use to generate auto-import suggestions.
Publishing Diagnostics to the Editor
ServerState::publish_diagnostics retrieves per-file diagnostics from the session and sends them via the LSP client connection:
pub(crate) async fn publish_diagnostics(
&self,
uri: Url,
workspace_uri: Url,
session: Arc<Session>,
) {
let diagnostics = self.diagnostics(&uri, session.clone());
if let Some(client) = self.client.as_ref() {
client
.publish_diagnostics(workspace_uri.clone(), diagnostics, None)
.await;
}
}
When the client receives this textDocument/publishDiagnostics notification, it renders the familiar IDE experience: red squiggly underlines for errors, yellow for warnings, and informational tooltips on hover.
Incremental Updates and Real-Time Synchronization
The Sway LSP maintains responsiveness through incremental synchronization. When a user types, the client sends textDocument/didChange notifications containing only the modified ranges, not the entire file.
The handlers in notification.rs process these updates through the same pipeline as didOpen:
- Apply changes to the temporary workspace
- Queue a new compilation request (dropping stale requests if the channel is full)
- Wait for the background thread to finish
- Publish updated diagnostics
This ensures that error highlighting updates in real-time as developers write code, without blocking the editor's UI thread.
Powering Other IDE Features with the Token Map
While error highlighting relies on compiler diagnostics, other IDE features leverage the TokenMap built during AST traversal:
- Completion (
capabilities/completion.rs) – Suggests methods and fields based on typed AST nodes - Hover (
capabilities/hover/mod.rs) – Displays type information and documentation by looking up tokens in the map - Go-to-Definition – Uses the token map to resolve identifiers to their declaration sites
- Semantic Tokens (
capabilities/semantic_tokens.rs) – Provides syntax highlighting by iterating over the token map - Inlay Hints – Displays inferred types and parameter names using semantic information from the typed AST
All these features share the same compilation pipeline: the background thread populates the TokenMap, and subsequent LSP requests query this map without recompiling.
Summary
- The Sway LSP exposes IDE support and error highlighting by implementing the Language Server Protocol in
sway-lsp/src/lib.rs, declaring capabilities like diagnostics, completion, and semantic tokens. - Error highlighting triggers on file open and change events via
handle_did_open_text_documentinnotification.rs, which initializes workspaces and queues compilation requests. - Background compilation in
server_state.rsruns the Sway compiler and traverses ASTs to build a TokenMap used for precise error positioning and IDE features. - The
get_diagnosticsfunction indiagnostic.rsconverts compiler errors, warnings, and info messages into LSP Diagnostic objects with severity levels and additional data for code actions. publish_diagnosticssends real-time error highlighting to editors, while incremental sync ensures responsiveness during editing.
Frequently Asked Questions
How does the Sway LSP handle compilation errors in real-time?
The Sway LSP handles real-time error highlighting by maintaining a background compilation thread that continuously analyzes the workspace. When a file opens or changes, the handle_did_open_text_document function in sway-lsp/src/handlers/notification.rs queues a new compilation request, waits for completion via state.wait_for_parsing(), and then calls publish_diagnostics to push errors to the editor. This pipeline ensures diagnostics update as you type without freezing the IDE.
What is the TokenMap and why is it important for IDE support?
The TokenMap is a core data structure built during AST traversal that maps source code ranges to semantic tokens. During background compilation, the server runs three traversal passes—lexed, parsed, and typed—implemented in sway-lsp/src/traverse/lexed_tree.rs, parsed_tree.rs, and typed_tree.rs. The resulting TokenMap powers error positioning, hover tooltips, go-to-definition, completion suggestions, and semantic highlighting by providing fast lookups of type and symbol information without recompiling.
How are compiler diagnostics converted to LSP format?
Compiler diagnostics are transformed into LSP-compatible structures in sway-lsp/src/capabilities/diagnostic.rs. The get_diagnostics function accepts CompileInfo, CompileWarning, and CompileError vectors from the compiler, then constructs LSP Diagnostic objects with appropriate severity levels (Error, Warning, Information). It also attaches DiagnosticData containing metadata like unknown symbol names, which enables code-action providers to suggest auto-imports or fixes.
What happens when I save a file in a Sway project?
When you save a file, the LSP client sends a textDocument/didSave notification handled in sway-lsp/src/handlers/notification.rs. This triggers the same compilation pipeline as opening a file: the server updates the temporary workspace, queues a compilation request on the background thread, waits for parsing to complete, and publishes fresh diagnostics. If the channel already contains pending compilation requests, stale ones are dropped to ensure only the latest saved state gets analyzed, maintaining performance in large projects.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →