Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3567144
Initial skeleton + dependencies updates
cartermp Oct 16, 2020
8b4b8cb
Initial proof of concept
cartermp Oct 21, 2020
6b36be5
Cleanup, tag interfaces correctly, surface area
cartermp Oct 21, 2020
b67ebdb
Basic, buggy support for parameter names
cartermp Oct 22, 2020
7f09a76
Preliminary support for not adding hints to already-annotated values
cartermp Oct 23, 2020
e129f58
don't show for typed value decls
cartermp Oct 23, 2020
fcc4be6
No sig files, handle more typed cases
cartermp Oct 23, 2020
626f7ff
Be precise about type annotations when looking for them in the syntax…
cartermp Oct 23, 2020
1c9afe6
More annotations fixity
cartermp Oct 23, 2020
57f8246
Hints show only the return type for functions
cartermp Oct 24, 2020
7dfc1b8
Cleanup and surface tests
cartermp Oct 24, 2020
47ed27a
Match names
cartermp Oct 24, 2020
51dcd6f
Basic tests for arg names
cartermp Oct 24, 2020
d2502db
Fix issues with nested functions + more tests
cartermp Oct 26, 2020
a964414
More testing and fix a bug with annotating return types for methods
cartermp Oct 27, 2020
3169656
Add failing test for infix exprs
cartermp Oct 27, 2020
b565539
Tests and fixes for exprs in infix operators
cartermp Oct 27, 2020
a8e289b
QuickInfo is scoped out + surface area
cartermp Oct 27, 2020
16c0b93
Add IsMethod and change parameter name hints to be more like names pa…
cartermp Oct 28, 2020
7e9eded
Only show type hints for lambdas + tests + surface
cartermp Oct 28, 2020
0ce8403
Preliminary support for labels for methods
cartermp Oct 29, 2020
50e7d9d
Cleanup, handle method params properly, constructors
cartermp Oct 30, 2020
258aa23
Feedback
cartermp Nov 2, 2020
86c25b9
Param names
cartermp Nov 4, 2020
2fef548
Update with latest
cartermp Nov 6, 2020
feaacf7
Update src/fsharp/symbols/Symbols.fsi
cartermp Nov 16, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Initial proof of concept
  • Loading branch information
cartermp committed Nov 6, 2020
commit 8b4b8cb392046b07fba3459de13759c23eb8f0d1
13 changes: 13 additions & 0 deletions src/fsharp/service/FSharpCheckerResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,19 @@ type FSharpCheckFileResults
FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range)
})

member __.GetAllUsesOfAllSymbolsInFileWithinRange(outerRange: range, ?cancellationToken: CancellationToken) =
threadSafeOp
(fun () -> [| |])
(fun scope ->
let cenv = scope.SymbolEnv
[|
for symbolUseChunk in scope.ScopeSymbolUses.AllUsesOfSymbols do
for symbolUse in symbolUseChunk do
cancellationToken |> Option.iter (fun ct -> ct.ThrowIfCancellationRequested())
if rangeContainsRange outerRange symbolUse.Range && symbolUse.ItemOccurence <> ItemOccurence.RelatedText then
let symbol = FSharpSymbol.Create(cenv, symbolUse.Item)
FSharpSymbolUse(scope.TcGlobals, symbolUse.DisplayEnv, symbol, symbolUse.ItemOccurence, symbolUse.Range) |])

member __.GetUsesOfSymbolInFile(symbol:FSharpSymbol, ?cancellationToken: CancellationToken) =
threadSafeOp
(fun () -> [| |])
Expand Down
4 changes: 4 additions & 0 deletions src/fsharp/service/FSharpCheckerResults.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ type public FSharpCheckFileResults =
/// Get all textual usages of all symbols throughout the file
member GetAllUsesOfAllSymbolsInFile : ?cancellationToken: CancellationToken -> seq<FSharpSymbolUse>

/// <summary>Get all textual usages of all symbols throughout a typechecked file that fall within a given range.</summary>
/// <param name="outerRange">The range in the typechecked document that all symbols must by defined within</param>
member GetAllUsesOfAllSymbolsInFileWithinRange : outerRange: range -> Async<FSharpSymbolUse[]>

/// Get the textual usages that resolved to the given symbol throughout the file
member GetUsesOfSymbolInFile : symbol:FSharpSymbol * ?cancellationToken: CancellationToken -> FSharpSymbolUse[]

Expand Down
2 changes: 1 addition & 1 deletion vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
<Compile Include="LanguageService\LanguageService.fs" />
<Compile Include="LanguageService\AssemblyContentProvider.fs" />
<Compile Include="LanguageService\SymbolHelpers.fs" />
<Compile Include="InlineHints\InlineHints.fs" />
<Compile Include="Classification\ClassificationDefinitions.fs" />
<Compile Include="Classification\ClassificationService.fs" />
<Compile Include="Formatting\BraceMatchingService.fs" />
Expand Down Expand Up @@ -85,6 +84,7 @@
<Compile Include="QuickInfo\Navigation.fs" />
<Compile Include="QuickInfo\Views.fs" />
<Compile Include="QuickInfo\QuickInfoProvider.fs" />
<Compile Include="InlineHints\InlineHints.fs" />
<Compile Include="Structure\BlockStructureService.fs" />
<Compile Include="Commands\HelpContextService.fs" />
<Compile Include="Commands\FsiCommandService.fs" />
Expand Down
118 changes: 94 additions & 24 deletions vsintegration/src/FSharp.Editor/InlineHints/InlineHints.fs
Original file line number Diff line number Diff line change
@@ -1,47 +1,117 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
namespace Microsoft.VisualStudio.FSharp.Editor

open System.Composition
open Microsoft.VisualStudio.Shell

open System
open System.Collections.Immutable
open System.Threading
open System.ComponentModel.Composition

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints

open FSharp.Compiler
open FSharp.Compiler.Range
open FSharp.Compiler.SourceCodeServices
open FSharp.Compiler.SyntaxTree

[<Export(typeof<IFSharpInlineHintsService>)>]
type internal FSharpInlineHintsService [<ImportingConstructor>] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) =
type internal FSharpInlineHintsService
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
[<Import(typeof<SVsServiceProvider>)>] serviceProvider: IServiceProvider,
projectInfoManager: FSharpProjectOptionsManager
) =

static let userOpName = "FSharpInlineHints"

interface IFSharpInlineHintsService with
member _.GetInlineHintsAsync(document: Document, _textSpan: TextSpan, cancellationToken: CancellationToken) =
member _.GetInlineHintsAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken) =
asyncMaybe {
let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! textVersion = document.GetTextVersionAsync(cancellationToken)
let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName)
let! sourceText = document.GetTextAsync(cancellationToken)
//let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions
//let _textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start)
//let _textLinePos = sourceText.Lines.GetLinePosition(textSpan.Start)
//let _fcsTextLineNumber = Line.fromZ textLinePos.Line
//let! _symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, textSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false)
let! _parseFileResults, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, userOpName)
let! symbols = checkFileResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync
//let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName)

let hints =
[|
for symbol in symbols do
// let givenRange = RoslynHelpers.TextSpanToFSharpRange
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbol.RangeAlternate)
FSharpInlineHint(TextSpan(symbolSpan.Start, 0), ImmutableArray.Create(TaggedText(TextTags.Text, symbol.Symbol.DisplayName + ":")))
|]

return hints.ToImmutableArray()
let range = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText)
let! symbolUses = checkFileResults.GetAllUsesOfAllSymbolsInFileWithinRange(range) |> liftAsync

let typeHints = ImmutableArray.CreateBuilder()

// todo - get these at some point I guess
// most likely need to work with the parse tree API
// since there is no good way to tell if a symbol is a parameter (declared or used)
let _parameterHints = ImmutableArray.CreateBuilder()

for symbolUse in symbolUses do
if symbolUse.IsFromDefinition then
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as x when x.IsValue && not x.IsMemberThisValue && not x.IsConstructorThisValue ->
let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)

// TODO: look for if 'x' is SynExpr.Typed perhaps?

// This does not correctly "classify" non-F# types
// TODO deal with that, probably via a new API?
let typeLayout = x.FormatLayout symbolUse.DisplayContext

let taggedText = ResizeArray()

Layout.renderL (Layout.taggedTextListR taggedText.Add) typeLayout |> ignore

let displayParts = ImmutableArray.CreateBuilder()
displayParts.Add(TaggedText(TextTags.Text, ": "))

taggedText
|> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text)
|> Seq.map (fun (tag, text) -> TaggedText(tag, text))
|> Seq.iter (fun tt -> displayParts.Add(tt))

// TODO - this is not actually correct
// We need to get QuickInfo for the actual type we pull out, not the value
// This code is correct for parameter name hints though, if that gets done!
let callBack position =
fun _ _ ->
asyncMaybe {
let! quickInfo =
FSharpAsyncQuickInfoSource.ProvideQuickInfo(
checkerProvider.Checker,
document.Id,
sourceText,
document.FilePath,
position,
parsingOptions,
projectOptions,
textVersion.GetHashCode(),
document.FSharpOptions.LanguageServicePerformance)

let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService)
let mainDesc, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo

let descriptionParts = ImmutableArray.CreateBuilder()

mainDesc
|> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text)
|> Seq.map (fun (tag, text) -> TaggedText(tag, text))
|> Seq.iter (fun tt -> descriptionParts.Add(tt))

docs
|> Seq.map (fun tt -> RoslynHelpers.roslynTag tt.Tag, tt.Text)
|> Seq.map (fun (tag, text) -> TaggedText(tag, text))
|> Seq.iter (fun tt -> descriptionParts.Add(tt))

return (descriptionParts.ToImmutableArray())
}
|> Async.map (Option.defaultValue ImmutableArray<_>.Empty)
|> RoslynHelpers.StartAsyncAsTask(cancellationToken)

let getDescriptionAsync position = Func<Document, CancellationToken, _>(callBack position)

let hint = FSharpInlineHint(TextSpan(symbolSpan.End, 0), displayParts.ToImmutableArray(), getDescriptionAsync symbolSpan.Start)
typeHints.Add(hint)
| _ -> ()

return typeHints.ToImmutableArray()
}
|> Async.map (Option.defaultValue ImmutableArray<_>.Empty)
|> RoslynHelpers.StartAsyncAsTask(cancellationToken)
|> RoslynHelpers.StartAsyncAsTask(cancellationToken)