# Quarkdown Language Server Protocol Implementation Architecture: A Deep Dive into the LSP-4J Design

> Explore the Quarkdown Language Server Protocol implementation architecture. Discover its modular supplier-driven design built on LSP-4J and how it aggregates results efficiently.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: architecture
- Published: 2026-04-29

---

**Quarkdown's Language Server Protocol implementation is built on LSP-4J using a modular supplier-driven architecture where the `QuarkdownLanguageServer` core delegates all requests to specialized sub-services that aggregate results from composable supplier objects.**

The `iamgio/quarkdown` repository ships a fully-featured Language Server that provides intelligent editor features—including completion, hover, diagnostics, semantic token highlighting, and on-type formatting—for the Quarkdown markup language. Understanding this architecture reveals how the server handles concurrent document edits, caches function catalogues, and stays extensible without modifying core plumbing.

## Core Architecture Components

The implementation organizes functionality around three distinct conceptual layers that separate concerns between connection management, document state, and feature logic.

### Server Core

[`QuarkdownLanguageServer.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownLanguageServer.kt) serves as the central entry point that manages the LSP client connection and advertises server capabilities during initialization. This class holds a reference to the `CacheableFunctionCatalogue`, which parses the `docs/` directory once at startup to index all native Quarkdown functions for fast lookup.

### Document Service

[`QuarkdownTextDocumentService.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownTextDocumentService.kt) implements the LSP4J `TextDocumentService` interface and acts as the traffic controller for all document-related operations. It maintains an in-memory `MutableMap<String, TextDocument>` that caches open files, normalizes line endings on ingestion, and spawns background threads to run diagnostics asynchronously.

### Sub-services and Suppliers

Individual features are implemented as small, composable units in the `subservices/` directory. Each sub-service (e.g., [`CompletionSubservice.kt`](https://github.com/iamgio/quarkdown/blob/main/CompletionSubservice.kt), [`HoverSubservice.kt`](https://github.com/iamgio/quarkdown/blob/main/HoverSubservice.kt)) receives a prioritized list of **supplier** objects. When processing a request, the sub-service iterates through its suppliers and returns the first non-empty result, enabling rich fallbacks without complex conditional logic.

## Server Initialization and Capability Registration

The launcher in [`QuarkdownLanguageServerLauncher.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownLanguageServerLauncher.kt) wires the server to the LSP client and starts the JSON-RPC connection. During the `initialize` handshake, the server announces its supported features through `ServerCapabilities`:

```kotlin
val legend = SemanticTokensLegend(TokenType.legend, emptyList())
val serverCaps = ServerCapabilities().apply {
    textDocumentSync = Either.forLeft(TextDocumentSyncKind.Full)
    completionProvider = CompletionOptions(true, completionTriggers)
    hoverProvider = Either.forLeft(true)
    semanticTokensProvider = SemanticTokensWithRegistrationOptions(legend, true, null)
    documentOnTypeFormattingProvider = onTypeFormattingOptions
}

```

**Completion triggers** are derived from `QuarkdownPatterns.FunctionCall` regular expressions, ensuring the client requests completions after specific characters like `.` or `:` that indicate function-call chains.

## Document Lifecycle Management

`QuarkdownTextDocumentService` handles the full document lifecycle through LSP notifications. When `didOpen` or `didChange` fires, the service calls `putDocument` to update its internal map with a normalized `TextDocument` instance.

The `didClose` notification triggers removal from the document map to free memory. All subsequent LSP methods—`completion`, `hover`, `semanticTokensFull`, `onTypeFormatting`—follow a consistent three-step pattern:

1. Retrieve the `TextDocument` from the URI-keyed map
2. Delegate processing to the relevant sub-service (`completionService`, `hoverService`, etc.)
3. Return a `CompletableFuture` that the LSP client awaits

This design ensures thread safety while allowing long-running operations like diagnostics to execute in background threads without blocking the main request loop.

## Supplier-Driven Feature Implementation

The supplier pattern is the architectural backbone that makes the server modular and testable. Each feature area uses a factory class to assemble its suppliers.

### Completion Architecture

`CompletionSubservice` iterates over suppliers created by `CompletionSuppliersFactory.default(this)`. Standard suppliers include `FunctionNameCompletionSupplier` and `FunctionParameterNameCompletionSupplier`, which leverage the cached function catalogue and [`FunctionCallTokenizer.kt`](https://github.com/iamgio/quarkdown/blob/main/FunctionCallTokenizer.kt) to parse raw text into lightweight AST nodes for accurate suggestions.

### Hover and Diagnostics

`HoverSubservice` and `DiagnosticsSubservice` operate identically, aggregating results from factories (`HoverSuppliersFactory`, `DiagnosticsSuppliersFactory`) that mix standard-library documentation with custom logic. Diagnostics run asynchronously through background threads spawned in the document service, publishing results back to the client via the server core.

### Semantic Tokens and Formatting

`SemanticTokensSubservice` encodes highlighted ranges using `SemanticTokensEncoder`, while `OnTypeFormattingSubservice` applies edit suppliers like `TrailingSpacesRemoverOnTypeFormattingEditSupplier` to auto-correct formatting as the user types.

## Document Model and Utilities

[`TextDocument.kt`](https://github.com/iamgio/quarkdown/blob/main/TextDocument.kt) provides an immutable representation of editor content, exposing helper methods like `positionToOffset` that sub-services use to translate LSP line/character positions into string indices. The class also hosts a lazy `cache` holder for per-document computation results, ensuring expensive parsing operations happen only once per edit cycle.

Supporting utilities include [`QuarkdownPatterns.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownPatterns.kt), which defines regex patterns for detecting function calls, arguments, and chain separators, and [`ParsingUtils.kt`](https://github.com/iamgio/quarkdown/blob/main/ParsingUtils.kt), which provides shared AST manipulation logic used across completion and hover suppliers.

## Extending the Server

Adding new capabilities requires only implementing a supplier interface and registering it in the appropriate factory. For example, creating a custom hover provider involves:

```kotlin
// MyHoverSupplier.kt
class MyHoverSupplier : HoverSupplier {
    override fun getHover(params: HoverParams, document: TextDocument): Hover? {
        val pos = params.position
        val line = document.text.lines()[pos.line]
        val word = line.substringBefore(' ', pos.character).substringAfterLast(' ', "")
        return Hover(MarkedString("markdown", "Documentation for: $word"))
    }
}

// Register in HoverSuppliersFactory.kt
object HoverSuppliersFactory {
    fun default(server: QuarkdownLanguageServer): List<HoverSupplier> =
        listOf(MyHoverSupplier(), FunctionDocumentationSupplier())
}

```

Once registered, the server automatically wires the new supplier into `HoverSubservice` without modifying the core server code or document service logic.

## Summary

- **LSP-4J Foundation**: The server builds on Eclipse's LSP4J library using standard JSON-RPC communication.
- **Three-Layer Design**: Separation between `QuarkdownLanguageServer` (core), `QuarkdownTextDocumentService` (documents), and sub-service/supplier layers (features).
- **Supplier Pattern**: Composable supplier objects enable extensible completion, hover, diagnostics, and formatting without core code changes.
- **Immutable Document Model**: `TextDocument` caches normalized content and provides position-to-offset mapping for all sub-services.
- **Async Diagnostics**: Background threading keeps the editor responsive while semantic analysis runs.

## Frequently Asked Questions

### What is the entry point for starting the Quarkdown Language Server?

The [`QuarkdownLanguageServerLauncher.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownLanguageServerLauncher.kt) file in the `quarkdown-lsp` module serves as the CLI entry point. It instantiates `QuarkdownLanguageServer`, connects the client reference, and starts the LSP4J `LSPLauncher` to handle stdin/stdout communication with the editor.

### How does the server provide autocomplete suggestions?

When the client sends a `textDocument/completion` request, `CompletionSubservice` queries each `CompletionSupplier` in order. Suppliers like `FunctionNameCompletionSupplier` use `FunctionCallTokenizer` and `CacheableFunctionCatalogue` to analyze the document AST and return relevant `CompletionItem` objects.

### Can I add custom diagnostics to the Quarkdown Language Server?

Yes. Implement the `DiagnosticsSupplier` interface to return `Diagnostic` objects for specific issues, then register your implementation in `DiagnosticsSuppliersFactory`. The `DiagnosticsSubservice` automatically aggregates results from all registered suppliers during the background diagnostic pass triggered by document changes.

### What triggers semantic highlighting updates in Quarkdown?

The server registers semantic token capabilities during initialization with `SemanticTokensLegend`. When the client requests `textDocument/semanticTokens/full`, `SemanticTokensSubservice` encodes token data using the `SemanticTokensEncoder`, highlighting function calls and other Quarkdown-specific syntax based on patterns from [`QuarkdownPatterns.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownPatterns.kt).