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 Session objects that cache diagnostics and compilation results
  • Background compilation – A dedicated thread that runs the compiler without blocking the LSP main loop
  • Token mapping – The TokenMap that 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:

  1. Initialize workspacestate.get_or_init_sync_workspace creates a temporary workspace mirroring the project
  2. Retrieve sessionstate.uri_and_session_from_workspace gets or creates a Session to store diagnostics
  3. Track documentstate.documents.handle_open_file records the file for token mapping
  4. Queue compilationsend_new_compilation_request builds a CompilationContext and pushes it to the background thread
  5. Await parsingstate.wait_for_parsing() blocks until compilation completes
  6. Publish diagnosticsstate.publish_diagnostics forwards results to the client
pub async fn handle_did_open_text_document(
    state: &ServerState,
    params: DidOpenTextDocumentParams,
) -> Result<(), LanguageServerError> {
    let file_uri = &params.text_document.uri;
    let sync_workspace = state.get_or_init_sync_workspace(file_uri).await?;
    let (uri, session) = state.uri_and_session_from_workspace(&params.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:

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:

  1. Apply changes to the temporary workspace
  2. Queue a new compilation request (dropping stale requests if the channel is full)
  3. Wait for the background thread to finish
  4. 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_document in notification.rs, which initializes workspaces and queues compilation requests.
  • Background compilation in server_state.rs runs the Sway compiler and traverses ASTs to build a TokenMap used for precise error positioning and IDE features.
  • The get_diagnostics function in diagnostic.rs converts compiler errors, warnings, and info messages into LSP Diagnostic objects with severity levels and additional data for code actions.
  • publish_diagnostics sends 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →