Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/Hints.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open System.Threading
open Microsoft.CodeAnalysis
open FSharp.Compiler.Text

module Hints =
Expand All @@ -17,6 +19,7 @@ module Hints =
Kind: HintKind
Range: range
Parts: TaggedText list
GetTooltip: Document -> Async<TaggedText list>
}

let serialize kind =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,47 @@ open Hints

type InlineParameterNameHints(parseResults: FSharpParseFileResults) =

let getTooltip (symbol: FSharpSymbol) _ =
async {
// This brings little value as of now. Basically just discerns fields from parameters
// and fills the tooltip bubble which otherwise looks like a visual glitch.
//
// Now, we could add some type information here, like C# does, for example:
// (parameter) int number
//
// This would work for simple cases but can get weird in more complex ones.
// Consider this code:
//
// let rev list = list |> List.rev
// let reversed = rev [ 42 ]
//
// With the trivial implementation, the tooltip for hint before [ 42 ] will look like:
// parameter 'a list list
//
// Arguably, this can look confusing.
// Hence, I wouldn't add type info to the text until we have some coloring plugged in here.
//
// Some alignment with C# also needs to be kept in mind,
// e.g. taking the type in braces would be opposite to what C# does which can be confusing.
let text = symbol.ToString()

return [ TaggedText(TextTag.Text, text) ]
}

let getParameterHint (range: range, parameter: FSharpParameter) =
{
Kind = HintKind.ParameterNameHint
Range = range.StartRange
Parts = [ TaggedText(TextTag.Text, $"{parameter.DisplayName} = ") ]
GetTooltip = getTooltip parameter
}

let getFieldHint (range: range, field: FSharpField) =
{
Kind = HintKind.ParameterNameHint
Range = range.StartRange
Parts = [ TaggedText(TextTag.Text, $"{field.Name} = ") ]
GetTooltip = getTooltip field
}

let parameterNameExists (parameter: FSharpParameter) = parameter.DisplayName <> ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ type InlineReturnTypeHints(parseFileResults: FSharpParseFileResults, symbol: FSh
TaggedText(TextTag.Space, " ")
])

let getTooltip _ =
async {
let typeAsString = symbol.ReturnParameter.Type.TypeDefinition.ToString()
let text = $"type {typeAsString}"
return [ TaggedText(TextTag.Text, text) ]
}

let getHint symbolUse range =
getHintParts symbolUse
|> Option.map (fun parts ->
{
Kind = HintKind.ReturnTypeHint
Range = range
Parts = parts
GetTooltip = getTooltip
})

let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) = symbol.IsFunction
Expand Down
18 changes: 18 additions & 0 deletions vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,29 @@ type InlineTypeHints(parseResults: FSharpParseFileResults, symbol: FSharpMemberO
// not sure when this can happen
| None -> []

let getTooltip _ =
async {
// Done this way because I am not sure if we want to show full-blown types everywhere,
// e.g. Microsoft.FSharp.Core.string instead of string.
// On the other hand, for user types this could be useful.
// Then there should be some smarter algorithm here.
let text =
if symbol.FullType.HasTypeDefinition then
let typeAsString = symbol.FullType.TypeDefinition.ToString()
$"type {typeAsString}"
else
// already includes the word "type"
symbol.FullType.ToString()

return [ TaggedText(TextTag.Text, text) ]
}

let getHint symbol (symbolUse: FSharpSymbolUse) =
{
Kind = HintKind.TypeHint
Range = symbolUse.Range.EndRange
Parts = getHintParts symbol symbolUse
GetTooltip = getTooltip
}

let isSolved (symbol: FSharpMemberOrFunctionOrValue) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

namespace Microsoft.VisualStudio.FSharp.Editor.Hints

open System
open System.Collections.Immutable
open System.Threading
open System.Threading.Tasks
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints
open Microsoft.VisualStudio.FSharp.Editor
open Microsoft.CodeAnalysis
open FSharp.Compiler.Text
open Hints

Expand All @@ -21,7 +25,14 @@ module NativeToRoslynHintConverter =
let text = taggedText.Text
RoslynTaggedText(tag, text)

let nativeToRoslynFunc nativeFunc =
Func<Document, CancellationToken, Task<ImmutableArray<RoslynTaggedText>>>(fun doc ct ->
nativeFunc doc
|> Async.map (List.map nativeToRoslynText >> ImmutableArray.CreateRange)
|> fun comp -> Async.StartAsTask(comp, cancellationToken = ct))

let convert sourceText hint =
let span = rangeToSpan hint.Range sourceText
let displayParts = hint.Parts |> Seq.map nativeToRoslynText
FSharpInlineHint(span, displayParts.ToImmutableArray())
let getDescription = hint.GetTooltip |> nativeToRoslynFunc
FSharpInlineHint(span, displayParts.ToImmutableArray(), getDescription)
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ module HintTestFramework =

// another representation for extra convenience
type TestHint =
{ Content: string; Location: int * int }
{
Content: string
Location: int * int
Tooltip: string
}

let private convert hint =
let private convert (hint, tooltip) =
let content =
hint.Parts |> Seq.map (fun hintPart -> hintPart.Text) |> String.concat ""

Expand All @@ -26,6 +30,7 @@ module HintTestFramework =
{
Content = content
Location = location
Tooltip = tooltip
}

let getFsDocument code =
Expand Down Expand Up @@ -57,9 +62,17 @@ module HintTestFramework =
let getHints (document: Document) hintKinds =
async {
let! ct = Async.CancellationToken

let getTooltip hint =
async {
let! roslynTexts = hint.GetTooltip document
return roslynTexts |> Seq.map (fun roslynText -> roslynText.Text) |> String.concat ""
}

let! sourceText = document.GetTextAsync ct |> Async.AwaitTask
let! hints = HintService.getHintsForDocument sourceText document hintKinds "test" ct
return hints |> Seq.map convert
let! tooltips = hints |> Seq.map getTooltip |> Async.Parallel
return tooltips |> Seq.zip hints |> Seq.map convert
}
|> Async.RunSynchronously

Expand Down
Loading