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