Movatterモバイル変換


[0]ホーム

URL:


Header menu logoF# Compiler Guide

Compiler Services: Editor services

This tutorial demonstrates how to use the editor services provided by the F# compiler.This API is used to provide auto-complete, tool-tips, parameter info help, matching ofbrackets and other functions in F# editors including Visual Studio, Xamarin Studio and Emacs(seefsharpbindings project for more information).Similarly tothe tutorial on using untyped AST, we start bygetting theInteractiveChecker object.

NOTE: The FSharp.Compiler.Service API is subject to change when later versions of the nuget package are published

Type checking sample source code

As in theprevious tutorial (using untyped AST), we start by referencingFSharp.Compiler.Service.dll, opening the relevant namespace and creating an instanceofInteractiveChecker:

// Reference F# compiler API#r"FSharp.Compiler.Service.dll"openFSharp.Compiler.CodeAnalysisopenFSharp.Compiler.EditorServicesopenFSharp.Compiler.TextopenFSharp.Compiler.Tokenization// Create an interactive checker instanceletchecker=FSharpChecker.Create()

Aspreviously, we useGetProjectOptionsFromScriptRoot to get a contextwhere the specified input is the only file passed to the compiler (and it is treated as ascript file or stand-alone F# source code).

// Sample input as a multi-line stringletinput="""  open System  let foo() =    let msg = String.Concat("Hello"," ","world")    if true then      printfn "%s" msg.  """// Split the input & define file nameletinputLines=input.Split('\n')letfile="/home/user/Test.fsx"letprojOptions,_diagnostics=checker.GetProjectOptionsFromScript(file,SourceText.ofStringinput,assumeDotNetFramework=false)|>Async.RunSynchronouslyletparsingOptions,_diagnostics2=checker.GetParsingOptionsFromProjectOptions(projOptions)

To perform type checking, we first need to parse the input usingParseFile, which gives us access to theuntyped AST. However,then we need to callCheckFileInProject to perform the full type checking. This functionalso requires the result ofParseFileInProject, so the two functions are often calledtogether.

// Perform parsingletparseFileResults=checker.ParseFile(file,SourceText.ofStringinput,parsingOptions)|>Async.RunSynchronously

Before we look at the interesting operations provided byTypeCheckResults, weneed to run the type checker on a sample input. On F# code with errors, you would get some type checkingresult (but it may contain incorrectly "guessed" results).

// Perform type checkingletcheckFileAnswer=checker.CheckFileInProject(parseFileResults,file,0,SourceText.ofStringinput,projOptions)|>Async.RunSynchronously

Alternatively you can useParseAndCheckFileInProject to check both in one step:

letparseResults2,checkFileAnswer2=checker.ParseAndCheckFileInProject(file,0,SourceText.ofStringinput,projOptions)|>Async.RunSynchronously

The function returns both the untyped parse result (which we do not use in thistutorial), but also aCheckFileAnswer value, which gives us access to allthe interesting functionality...

letcheckFileResults=matchcheckFileAnswerwith|FSharpCheckFileAnswer.Succeeded(res)->res|res->failwithf"Parsing did not finish... (%A)"res

Here, we type check a simple function that (conditionally) prints "Hello world".On the last line, we leave an additional dot inmsg. so that we can get thecompletion list on themsg value (we expect to see various methods on the stringtype there).

Using type checking results

Let's now look at some of the API that is exposed by theTypeCheckResults type. In general,this is the type that lets you implement most of the interesting F# source code editor services.

Getting a tool tip

To get a tool tip, you can use theGetToolTip method. The method takes a line number and characteroffset. Both of the numbers are zero-based. In the sample code, we want to get a tooltip for thefoofunction that is defined on line 3 (line 0 is blank) and the letterf starts at index 7 (the tooltipwould work anywhere inside the identifier).

In addition, the method takes a tag of token which is typicallyIDENT, when getting a tooltip for anidentifier (the other option lets you get a tooltip with full assembly location when using#r "...").

// Get tag of the IDENT token to be used as the last argumentletidentToken=FSharpTokenTag.Identifier// Get tool tip at the specified locationlettip=checkFileResults.GetToolTip(4,7,inputLines.[1],["foo"],identToken)printfn"%A"tip

Aside from the location and token kind, the function also requires the current contents of the line(useful when the source code changes) and aNames value, which is a list of strings representingthe current long name. For example, to get a tooltip for theRandom identifier in a long nameSystem.Random, you would use a location somewhere in the stringRandom and you would pass["System"; "Random"] as theNames value.

The returned value is of typeToolTipText which contains a discriminated unionToolTipElement.The union represents different kinds of tool tips that you can get from the compiler.

Getting auto-complete lists

The next method exposed byTypeCheckResults lets us perform auto-complete on a given location.This can be called on any identifier or in any scope (in which case you get a list of names visiblein the scope) or immediately after. to get a list of members of some object. Here, we get alist of members of the string valuemsg.

To do this, we callGetDeclarationListInfo with the location of the. symbol on the last line(ending withprintfn "%s" msg.). The offsets are one-based, so the location is7, 23.We also need to specify a function that says that the text has not changed and the current identifierwhere we need to perform the completion.

// Get declarations (autocomplete) for a locationletdecls=checkFileResults.GetDeclarationListInfo(SomeparseFileResults,7,inputLines.[6],PartialLongName.Empty23,(fun()->[]))// Print the names of available itemsforitemindecls.Itemsdoprintfn" -%s"item.NameInList

NOTE:v is an alternative name for the oldGetDeclarations. The oldGetDeclarations wasdeprecated because it accepted zero-based line numbers. At some point it will be removed, andGetDeclarationListInfo will be renamed back toGetDeclarations.

When you run the code, you should get a list containing the usual string methods such asSubstring,ToUpper,ToLower etc. The fourth argument ofGetDeclarations, here([], "msg"),specifies the context for the auto-completion. Here, we want a completion on a complete namemsg, but you could for example use(["System"; "Collections"], "Generic") to get a completion listfor a fully qualified namespace.

Getting parameter information

The next common feature of editors is to provide information about overloads of a method. In oursample code, we useString.Concat which has a number of overloads. We can get the list usingGetMethods operation. As previously, this takes the zero-indexed offset of the location that we areinterested in (here, right at the end of theString.Concat identifier) and we also need to providethe identifier again (so that the compiler can provide up-to-date information when the source codechanges):

// Get overloads of the String.Concat methodletmethods=checkFileResults.GetMethods(5,27,inputLines.[4],Some["String";"Concat"])// Print concatenated parameter listsformiinmethods.Methodsdo[forpinmi.Parametersdoforttinp.Displaydoyieldtt.Text]|>String.concat", "|>printfn"%s(%s)"methods.MethodName

The code uses theDisplay property to get the annotation for each parameter. This returns informationsuch asarg0: obj orparams args: obj[] orstr0: string, str1: string. We concatenate the parametersand print a type annotation with the method name.

Asynchronous and immediate operations

You may have noticed thatCheckFileInProject is an asynchronous operation.This indicates that type checking of F# code can take some time.The F# compiler performs the work in the background (automatically) and whenwe call theCheckFileInProject method, it returns an asynchronous operation.

There is also theCheckFileInProjectIfReady method. This returns immediately if thetype checking operation can't be started immediately, e.g. if other files in the projectare not yet type-checked. In this case, a background worker might choose to do otherwork in the meantime, or give up on type checking the file until theFileTypeCheckStateIsDirty eventis raised.

Thefsharpbinding project has a more advancedexample of handling the background work where all requests are sent through an F# agent.This may be more appropriate for implementing editor support.

Summary

TheCheckFileAnswer object contains other useful methods that were not covered in this tutorial. Youcan use it to get location of a declaration for a given identifier, additional colorization information(the F# 3.1 colorizes computation builder identifiers & query operators) and others.

Using the FSharpChecker component in multi-project, incremental and interactive editing situations may involveknowledge of theFSharpChecker operations queue and theFSharpChecker caches.

Finally, if you are implementing an editor support for an editor that cannot directly call the .NET API,you can call many of the methods discussed here via a command line interface that is available in theFSharp.AutoComplete project.

Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Compiler
namespace FSharp.Compiler.CodeAnalysis
namespace FSharp.Compiler.EditorServices
namespace FSharp.Compiler.Text
namespace FSharp.Compiler.Tokenization
val checker: FSharpChecker
type FSharpChecker = member CheckFileInProject: parseResults: FSharpParseFileResults * fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpCheckFileAnswer> member ClearCache: options: FSharpProjectOptions seq * ?userOpName: string -> unit + 1 overload member ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients: unit -> unit member Compile: argv: string array * ?userOpName: string -> Async<FSharpDiagnostic array * exn option> member FindBackgroundReferencesInFile: fileName: string * options: FSharpProjectOptions * symbol: FSharpSymbol * ?canInvalidateProject: bool * [<Experimental ("This FCS API is experimental and subject to change.")>] ?fastCheck: bool * ?userOpName: string -> Async<range seq> + 1 overload member GetBackgroundCheckResultsForFileInProject: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileResults> member GetBackgroundParseResultsForFileInProject: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults> member GetBackgroundSemanticClassificationForFile: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<SemanticClassificationView option> + 1 overload member GetParsingOptionsFromCommandLineArgs: sourceFiles: string list * argv: string list * ?isInteractive: bool * ?isEditing: bool -> FSharpParsingOptions * FSharpDiagnostic list + 1 overload member GetParsingOptionsFromProjectOptions: options: FSharpProjectOptions -> FSharpParsingOptions * FSharpDiagnostic list ...
<summary> Used to parse and check F# source code.</summary>
static member FSharpChecker.Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: LegacyReferenceResolver * ?tryGetMetadataSnapshot: FSharp.Compiler.AbstractIL.ILBinaryReader.ILReaderTryGetMetadataSnapshot * ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enablePartialTypeChecking: bool * ?parallelReferenceResolution: bool * ?captureIdentifiersWhenParsing: bool * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?documentSource: DocumentSource * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?useTransparentCompiler: bool * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?transparentCompilerCacheSizes: TransparentCompiler.CacheSizes -> FSharpChecker
val input: string
val inputLines: string array
System.String.Split(separator: System.ReadOnlySpan<char>) : string array
   (+0 other overloads)
System.String.Split([<System.ParamArray>] separator: char array) : string array
   (+0 other overloads)
System.String.Split(separator: string array, options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: string, ?options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: char array, options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: char array, count: int) : string array
   (+0 other overloads)
System.String.Split(separator: char, ?options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: string array, count: int, options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: string, count: int, ?options: System.StringSplitOptions) : string array
   (+0 other overloads)
System.String.Split(separator: char array, count: int, options: System.StringSplitOptions) : string array
   (+0 other overloads)
val file: string
val projOptions: FSharpProjectOptions
val _diagnostics: FSharp.Compiler.Diagnostics.FSharpDiagnostic list
member FSharpChecker.GetProjectOptionsFromScript: fileName: string * source: ISourceText * ?caret: Position * ?previewEnabled: bool * ?loadedTimeStamp: System.DateTime * ?otherFlags: string array * ?useFsiAuxLib: bool * ?useSdkRefs: bool * ?assumeDotNetFramework: bool * ?sdkDirOverride: string * ?optionsStamp: int64 * ?userOpName: string -> Async<FSharpProjectOptions * FSharp.Compiler.Diagnostics.FSharpDiagnostic list>
module SourceTextfrom FSharp.Compiler.Text
<summary> Functions related to ISourceText objects</summary>
val ofString: string -> ISourceText
<summary> Creates an ISourceText object from the given string</summary>
Multiple items
type Async = static member AsBeginEnd: computation: ('Arg -> Async<'T>) -> ('Arg * AsyncCallback * objnull -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent: event: IEvent<'Del,'T> * ?cancelAction: (unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult: iar: IAsyncResult * ?millisecondsTimeout: int -> Async<bool> static member AwaitTask: task: Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle: waitHandle: WaitHandle * ?millisecondsTimeout: int -> Async<bool> static member CancelDefaultToken: unit -> unit static member Catch: computation: Async<'T> -> Async<Choice<'T,exn>> static member Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * objnull -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
static member Async.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: System.Threading.CancellationToken -> 'T
val parsingOptions: FSharpParsingOptions
val _diagnostics2: FSharp.Compiler.Diagnostics.FSharpDiagnostic list
member FSharpChecker.GetParsingOptionsFromProjectOptions: options: FSharpProjectOptions -> FSharpParsingOptions * FSharp.Compiler.Diagnostics.FSharpDiagnostic list
val parseFileResults: FSharpParseFileResults
member FSharpChecker.ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async<FSharpParseFileResults>
member FSharpChecker.ParseFile: fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async<FSharpParseFileResults>
val checkFileAnswer: FSharpCheckFileAnswer
member FSharpChecker.CheckFileInProject: parseResults: FSharpParseFileResults * fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpCheckFileAnswer>
val parseResults2: FSharpParseFileResults
val checkFileAnswer2: FSharpCheckFileAnswer
member FSharpChecker.ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
member FSharpChecker.ParseAndCheckFileInProject: fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
val checkFileResults: FSharpCheckFileResults
type FSharpCheckFileAnswer = | Aborted | Succeeded of FSharpCheckFileResults
<summary> The result of calling TypeCheckResult including the possibility of abort and background compiler not caught up.</summary>
union case FSharpCheckFileAnswer.Succeeded: FSharpCheckFileResults -> FSharpCheckFileAnswer
<summary> Success</summary>
val res: FSharpCheckFileResults
val res: FSharpCheckFileAnswer
val failwithf: format: Printf.StringFormat<'T,'Result> -> 'T
val identToken: int
module FSharpTokenTagfrom FSharp.Compiler.Tokenization
<summary> Some of the values in the field FSharpTokenInfo.Tag</summary>
val Identifier: int
<summary> Indicates the token is an identifier</summary>
val tip: ToolTipText
member FSharpCheckFileResults.GetToolTip: line: int * colAtEndOfNames: int * lineText: string * names: string list * tokenTag: int * ?width: int -> ToolTipText
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
val decls: DeclarationListInfo
member FSharpCheckFileResults.GetDeclarationListInfo: parsedFileResults: FSharpParseFileResults option * line: int * lineText: string * partialName: PartialLongName * ?getAllEntities: (unit -> AssemblySymbol list) * ?completionContextAtPos: (pos * CompletionContext option) * ?genBodyForOverriddenMeth: bool -> DeclarationListInfo
union case Option.Some: Value: 'T -> Option<'T>
type PartialLongName = { QualifyingIdents: string list PartialIdent: string EndColumn: int LastDotPos: int option } static member Empty: endColumn: int -> PartialLongName
<summary> Qualified long name.</summary>
static member PartialLongName.Empty: endColumn: int -> PartialLongName
val item: DeclarationListItem
property DeclarationListInfo.Items: DeclarationListItem array with get
property DeclarationListItem.NameInList: string with get
<summary> Get the text to display in the declaration list for the declaration. This is a display name without backticks.</summary>
val methods: MethodGroup
member FSharpCheckFileResults.GetMethods: line: int * colAtEndOfNames: int * lineText: string * names: string list option -> MethodGroup
val mi: MethodGroupItem
property MethodGroup.Methods: MethodGroupItem array with get
<summary> The methods (or other items) in the group</summary>
val p: MethodGroupItemParameter
property MethodGroupItem.Parameters: MethodGroupItemParameter array with get
<summary> The parameters of the method in the overload set</summary>
val tt: TaggedText
property MethodGroupItemParameter.Display: TaggedText array with get
<summary> The representation for the parameter including its name, its type and visual indicators of other information such as whether it is optional.</summary>
property TaggedText.Text: string with get
<summary> Gets the text</summary>
module Stringfrom Microsoft.FSharp.Core
val concat: sep: string -> strings: string seq -> string
property MethodGroup.MethodName: string with get
<summary> The shared name of the methods (or other items) in the group</summary>

On this page

Type something to start searching.


[8]ページ先頭

©2009-2025 Movatter.jp