The fsintellisense project in the samples/compiler directory implements a command line tool that can be used for providing F# IntelliSense for various editors in a simple way that should be usable from most of the source code editors on all platforms supported by F# (.NET on Windows, Mono on Linux and Mac). The command line tool can be called by sending source code and commands through standard input. The resulting information (e.g. tool tips, completion and error information) are reported back to through standard console output. In this document, we'll look at a basic sample that demonstrates some of the features of the command line tool. This should explain how you could call it from a editor macro when implementing IntelliSense support for F#.

Interacting with the tool

Loading script

When the tool starts, we first need to load a script file (currently, it supports only standalone script files and not complete multi-file projects). This can be done using the script command. The command takes a file name as the argument. Note that the file is not actually loaded from that location (it doesn't need to exist at all) - this is only used to resolve references in the script file (for example if it contains #load "other.fs"):

script /home/fsharp/test.fsx
type Hello(who) =
  member x.Say() =
    printfn "Hello %s!" who

let hi = Hello("world")
hi.S
<<EOF>>

When accepting multi-line input, we need to add a special command <<EOF>> to the last line to end the input. This initializes F# in the background and it starts loading the input file. When the file is processed, it will print the following (until then, the tool won't accept any commands):

DONE: Script loaded 

Getting IntelliSense information

Now we can use tip and completion commands to get some information for the IntelliSense. The former command can be used to get tool tip information (e.g. inferred type and other information about declared values). The latter command can be used to get completion list for the specified position (e.g. you may want to invoke it after the user types "."). Let's start by getting tool tip above the e letter in the Hello identifier on the first line. Note that the column and line numbers are zero-based:

tip 0 6 

The command line tool prints the tool tip information. This can be multi-line output, so it will be ended with the special command <<EOF>> (so that your macro knows when to stop reading the output):

type Hello =
  class
    new : who:string -> Hello
    member Say : unit -> unit
  end

Full name: Test.Hello
<<EOF>>

The following command (and printed output) demonstrate another tool tip information - this time for the who value declared on the first line (the first line was entered on the standard input, the rest of the listing is output generated by the tool):

tip 0 12
val who : string

  type: string
  implements: System.IComparable
  implements: System.ICloneable
  implements: System.IConvertible
  implements: System.IComparable<string>
  implements: seq<char>
  implements: System.Collections.IEnumerable
  implements: System.IEquatable<string>
<<EOF>>

Next, we'll use the completion command to get a list of members (or functions) that are available at the specified location. The following example uses the command to get a list of members of the hi value on the last line of the script input:

completion 5 4

In this case, the generated output is quite short as it only includes standard members of all .NET objects and the Say member:

Equals
GetHashCode
GetType
Say
ToString
<<EOF>>

It is worth noting that the list may be quite large. For example completion 0 0 would give you a list of all top-level functions and namespaces.

Updating source and getting error information

A text editor should generally call the command line tool when the source code changes (though maybe not with every change - you may want to play with the tool to find the right ballance). This can be done using the parse command, which is similar to script, but doesn't rebuild the script context (e.g. doesn't re-load all libraries referenced by default) and it doesn't take any arguments. The command should be followed by the script content:

parse
type Hello(who) =
  member x.Say() =
    printfn "Hello %s!" who

let hi = Hello("world")
hi.Say(10)
<<EOF>>

The tool starts processing the update in background (we can enter additional commands almost immediately after entering the parse command, but we won't get replies before the processing completes). The tool reports that the processing has started:

DONE: Background parsing started

As you may notice, the above script body contains an error. When calling the Say member, we give it the value 10 as an argument even though it doesn't have any parameters. You can use the errors command to get information about errors that occurred during the last background compilation of the script. The command doesn't have any parameters:

errors

The tool prints errors in the following format. There is one error message per line (in the output below, it is split into multiple lines to make the text on the wiki readable), starting with the specification of the range and ending with the <<EOF>> token:

[0:5-10:5] ERROR The member or object constructor 'Say' takes 0 argument(s)
but is here given 1. The required signature is 'member Hello.Say : unit -> unit'.
<<EOF>>

Last edited Nov 15, 2010 at 3:39 PM by tomasp, version 6