United Kingdom: +44 (0)208 088 8978


Matt showcases Contextive, a VS Code extension written in F# that uses Fable and the Language Server Protocol.

We're hiring Software Developers

Click here to find out more

Contextive is a Visual Studio Code extension that

supports developers where a complex domain or project specific language is in use by surfacing definitions everywhere specific words are used - code, comments, config or documentation.

Contextive VS Code extension homepage

What will make it particularly interesting to readers of this blog is that it is implemented in F#!

The Contextive README on GitHub provides some more background information:

Contextive is inspired by the concept of the Ubiquitous Language from the practice of Domain Driven Design (DDD) and is intended to support ubiquitous language management practices on DDD projects.

Even if you're not using Domain Driven Design, Contextive should still be very helpful in any software project where it's important that developers are aligned on the meaning of terms.

By defining terms in a central definitions file, Contextive can surface definitions and usage examples in auto-complete suggestions & hover panels wherever the terms are used - in code (of any language across the stack), comments, config, and documentation (e.g. markdown).

The key idea is that you define terms in a YAML file in your repo and use an extension to make your IDE aware of those terms. This gives you features in your editor like completions and hover text. As an example, you can get the following editor support with the below definitions file.

Demo showing completion and hover text

  - name: Cargo
    domainVisionStatement: To manage the routing of cargo through transportation legs
    - CargoDemo
    - name: Cargo
      definition: A unit of transportation that needs moving and delivery to its delivery location.
        - Multiple Customers are involved with a Cargo, each playing a different role.
        - The Cargo delivery goal is specified.
        - unit

Background: the Language Server Protocol

The Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc.

Language Server Protocol homepage

Implementing support for features like autocomplete, goto definition, or documentation on hover for a programming language is a significant effort. Traditionally this work must be repeated for each development tool, as each provides different APIs for implementing the same features.

The idea behind a Language Server is to provide the language-specific smarts inside a server that can communicate with development tooling over a protocol that enables inter-process communication.

The idea behind the Language Server Protocol (LSP) is to standardize the protocol for how tools and servers communicate, so a single Language Server can be re-used in multiple development tools, and tools can support languages with minimal effort.

LSP is a win for both language providers and tooling vendors!

LSP overview

Contextive makes use of a language server that reads definitions of terms in the Ubiquitous Language from a YAML file, and uses those terms to respond with appropriate hover text or completions when prompted.

Leveraging Fable to create a VS Code extension

VS Code extensions are usually written in JavaScript. Thanks to the Fable F# to JavaScript compiler, it's possible to write the extension code in F#.

The extension starts a language server client.

let activate (context: ExtensionContext) =
    promise {
        let client = clientFactory ()

        do! client.start ()

It is configured with some server options.

let private clientFactory () =
        serverOptions = serverOptions,
        clientOptions = clientOptions,
        forceDebug = Option.isSome process.env?CONTEXTIVE_DEBUG

Those server options ultimately launch the language server as an executable. Its behaviour is defined elsewhere in the codebase.

let private runServerOptions =
    executable (fun x ->
        x.command <- "./Contextive.LanguageServer"

Some of the code for working with the language server makes use of Ionide-Helpers

Leveraging .NET libraries to implement a language server

The OmniSharp.Extensions.LanguageServer package, written by the OmniSharp team, is designed to make it easier to write language servers that run on .NET. Of course, F# can target .NET as well as JavaScript, so it's possible to make use of this library to write language servers in F#, and this is exactly what Contextive does. Doing so allows Contextive to, for example, easily hook hover behaviour defined in a custom function into the language server that is eventually created.

let private configureServer (input: Stream) (output: Stream) (opts: LanguageServerOptions) =
    let definitions = Definitions.create ()

        .OnHover(Hover.handler <| Definitions.find definitions <| TextDocument.findToken, Hover.registrationOptions)

Aside: automated semantic releases

I first came across Contextive at a .NET meetup in July, during which Chris Simon (Contextive's author) made and released an improvement to the extension. The F# goodness and a solid test suite, along with some well-defined GitHub actions made it possible for Chris to be confident enough to do this in front of a live audience. In particular, making use of the semantic-release npm package allowed Chris to trigger a release, and have the version number and changelog updated automatically (based on commit messages he'd written following a formalized convention). I definitely think it's a package worth library maintainers exploring.


There are a few things to say at the end of this blog post. The first is that Contextive looks like a really useful extension! If you're using VS Code, definitely check it out. It's also another great demonstration of how blessed we are in the F# community to be able to target .NET and JavaScript from the same language; just like writing SAFE Stack code doesn't require changing languages between client and server, in Contextive there's less context switching when moving between extension code and language server code. Finally, Contextive could be a great project to contribute to if you're looking to learn more about writing VS Code extensions (or even an extension for another editor!) or to better understand language server implementation.

Thanks Chris for the great tool, and for giving me an excuse to write some more about the power of F#!