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>>