Creating VS Code plugins with F# and Fable

Creating VS Code plugins with F# and Fable

Introduction

EDIT 22.03.2016 Thanks to Alfonso’s help I was able to remove postbuild step fixing JS.

VS Code is new text editor (or rather lightweight IDE) created by Microsoft. Because it is product based on Electron - cross platform engine allowing developers to write desktop applications using web technologies - its plugin system supports JavaScript (and TypeScript). Unfortunately both those languages are not nice choice for someone using statically typed functional programming languages like F#. Up to this moment in my VS Code extensions I was using F# library called FunScript which compiles F# code to JavaScript. Whereas it sounds nice, library has some problems which makes writing code using it not nice experience. Fortunately recently, Alfonso Garcia-Caro, one of contributors to FunScript, has decided to create new project compiling F# to JS (with Babel as middle step) called Fable which hopefully will solve some of the FunScript’s problems. I have decided to investigate how this new library can be used to create VS Code plugins… using VS Code to code and compile those plugins.

Requirements

You need to have F# 4 and node.js installed. Node has to be included in your $PATH.

Initial steps

The simplest way to start building own VS Code plugin is using Yeoman to scaffold plugin project. Install Yeoman and VS Code Plugin Generator using following commands:

npm install -g yo
npm install -g generator-code

The Yeoman generator will walk you through the steps required to create your customization or extension prompting for the required information. To launch the generator type the following in a command prompt:

yo code

We pick New Extension (JavaScript) option, go through all questions, and let Yeoman do its magic. After process is finished we enter newly created folder.Here we have to remove some unnecessary things generated by Yeoman - extension.js file, and typing and tests folders (who need tests anyway ;) ). We add out/ entry to .gitignore files - all output files will be generated as part of our build process and shouldn’t be commited to GitHub. Final step is creating empty src folder - it will contain our F# source files.

Installing Fable and setting project.

Installing Fable and VS Code bindings for it is easy - all things are published as npm modules. So we can just run following commands to add those tools to our project

npm install --save-dev fable-compiler fable-import-vscode
npm install --save fable-core

Next step is updating our package.json file to include changes we have done and to create build targets which will compile F# code to JS.First of all we update main entry - it defines where is our entry file of plugin.

{
...  
"main": "./out/extension",
}

Second, we update scripts part - here we define possible build targets which can be used for our project.

{
...
"scripts": {
    "build": "fable src/extension.fsx --outDir ../out -s -m commonjs"
  },
}

For more details about Fable and compiler options please check Fable documentation

build target runs fable compiler to generate JS from our F# file.

Writing F# Code

For more details about VS Code extension API please visit extension documentation

At last we can write some F# code. Our sample extension will be simple - it will be just Hello World.To start in src folder we create F# script file called extension.fs. First step is referencing Fable core library and VS Code bindings

#r "../node_modules/fable-core/Fable.Core.dll"
 
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import
open Fable.Import.vscode

Entry point of any VS Code plugin is activate function placed in file defined as entry point in package.json.

open Fable.Import.vscode.commands.Globals

let activate (context : vscode.ExtensionContext) = 
  registerCommand("extension.sayHello", fun _ ->
    showInformationMessage "Hello world!" |> unbox )
  |> context.subscriptions.Add    

As we can see we can use both standard F# construct like printfn function (which is mapped to JS console.log) and functions defined in VS Code bindings. Here we print “Hello world” to console and register command which will display Hello World information in the popup. Now from console, we can run npm build and compile our F# script to JavaScript. We shall see result in out directory.

Integration with VS Code

Now, when we have set up project and can compile it, we need to integrate our solution with VS Code - first we define Task to run our build script from inside editor. In .vscode folder we create tasks.json file and put there:

{
  "version": "0.1.0",
  "command": "npm",
  "isShellCommand": true,
  "tasks": [
    {
     "taskName": "run",
      "isBuildCommand": true,
      "args": [ "build" ]
    }
  ]
}

It’s just simple task running npm run build - it is defined as build command which means it can be run using Ctrl+Shift+B command. Next step is setting this target as task we want to invoke before we start debugging. To do this, we open launch.json file and append "preLaunchTask": "run" to Lanunch Extension configuration (we can remove Launch test entry, we do not need tests anyway ;) ). Pressing F5 will run our extension - we can try it out by running Hello World command in Command Palette.

Summary

In this post we have shortly moved all steps necessary to create VS Code extension using F# and Fable. From my short experience with this tool it looks like it is much nicer option than FunScript, hopefully I will be able to port my other extensions to it! Whole source code of this sample application is on GitHub

Krzysztof Cieslak's Picture

About Krzysztof Cieslak

Krzysztof is a F# developer and consultant, open source contributor, conference speaker

Lodz, Poland http://kcieslak.io