diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index cdfc599466..ab04e3f105 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -126,8 +126,12 @@ + + + + diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs index 13edb5448d..c04ccef51f 100644 --- a/vsintegration/src/FSharp.Editor/Hints/HintService.fs +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -6,64 +6,28 @@ open Microsoft.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols -open FSharp.Compiler.Text +open Hints module HintService = - - // Relatively convenient for testing - type NativeHint = { - Range: range - Parts: TaggedText list - } - - let private isValidForHint - (parseFileResults: FSharpParseFileResults) - (symbol: FSharpMemberOrFunctionOrValue) - (symbolUse: FSharpSymbolUse) = - - let isNotAnnotatedManually = - not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) - - let isNotAfterDot = - symbolUse.IsFromDefinition - && not symbol.IsMemberThisValue - - let isNotTypeAlias = - not symbol.IsConstructorThisValue - - symbol.IsValue // we'll be adding other stuff gradually here - && isNotAnnotatedManually - && isNotAfterDot - && isNotTypeAlias - - let private getHintParts - (symbol: FSharpMemberOrFunctionOrValue) - (symbolUse: FSharpSymbolUse) = - - match symbol.GetReturnTypeLayout symbolUse.DisplayContext with - | Some typeInfo -> - let colon = TaggedText(TextTag.Text, ": ") - colon :: (typeInfo |> Array.toList) - - // not sure when this can happen but better safe than sorry - | None -> - [] - - let private getHintsForSymbol parseResults (symbolUse: FSharpSymbolUse) = + let private getHintsForSymbol parseResults hintKinds (symbolUse: FSharpSymbolUse) = match symbolUse.Symbol with - | :? FSharpMemberOrFunctionOrValue as mfvSymbol - when isValidForHint parseResults mfvSymbol symbolUse -> + | :? FSharpMemberOrFunctionOrValue as symbol + when hintKinds |> Set.contains HintKind.TypeHint + && InlineTypeHints.isValidForHint parseResults symbol symbolUse -> - [ { - Range = symbolUse.Range - Parts = getHintParts mfvSymbol symbolUse - } ] + InlineTypeHints.getHints symbol symbolUse + | :? FSharpMemberOrFunctionOrValue as symbol + when hintKinds |> Set.contains HintKind.ParameterNameHint + && InlineParameterNameHints.isValidForHint symbol -> + + InlineParameterNameHints.getHints parseResults symbol symbolUse + // we'll be adding other stuff gradually here | _ -> [] - let getHintsForDocument (document: Document) userOpName cancellationToken = + let getHintsForDocument (document: Document) hintKinds userOpName cancellationToken = async { if isSignatureFile document.FilePath then @@ -75,5 +39,5 @@ module HintService = return checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken |> Seq.toList - |> List.collect (getHintsForSymbol parseResults) + |> List.collect (getHintsForSymbol parseResults hintKinds) } diff --git a/vsintegration/src/FSharp.Editor/Hints/Hints.fs b/vsintegration/src/FSharp.Editor/Hints/Hints.fs new file mode 100644 index 0000000000..9e6bab5c29 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/Hints.fs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open FSharp.Compiler.Text + +module Hints = + + type HintKind = + | TypeHint + | ParameterNameHint + + // Relatively convenient for testing + type NativeHint = { + Kind: HintKind + Range: range + Parts: TaggedText list + } \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs new file mode 100644 index 0000000000..4e4baa5687 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/InlineParameterNameHints.fs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Symbols +open FSharp.Compiler.Text +open Hints + +module InlineParameterNameHints = + + let private getHint (range: range, parameter: FSharpParameter) = + { + Kind = HintKind.ParameterNameHint + Range = range.StartRange + Parts = [ TaggedText(TextTag.Text, $"{parameter.DisplayName} = ") ] + } + + let private doesParameterNameExist (parameter: FSharpParameter) = + parameter.DisplayName <> "" + + let isValidForHint (symbol: FSharpMemberOrFunctionOrValue) = + // is there a better way? + let isNotBuiltInOperator = + symbol.DeclaringEntity + |> Option.exists (fun entity -> entity.CompiledName <> "Operators") + + symbol.IsFunction + && isNotBuiltInOperator // arguably, hints for those would be rather useless + + let getHints + (parseResults: FSharpParseFileResults) + (symbol: FSharpMemberOrFunctionOrValue) + (symbolUse: FSharpSymbolUse) = + + let parameters = symbol.CurriedParameterGroups |> Seq.concat + let ranges = parseResults.GetAllArgumentsForFunctionApplicationAtPosition symbolUse.Range.Start + + match ranges with + | Some ranges -> + parameters + |> Seq.zip ranges + |> Seq.where (snd >> doesParameterNameExist) + |> Seq.map getHint + |> Seq.toList + + // this is the case at least for custom operators + | None -> [] diff --git a/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs new file mode 100644 index 0000000000..09566e0514 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/InlineTypeHints.fs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Symbols +open FSharp.Compiler.Text +open Hints + +module InlineTypeHints = + + let private getHintParts + (symbol: FSharpMemberOrFunctionOrValue) + (symbolUse: FSharpSymbolUse) = + + match symbol.GetReturnTypeLayout symbolUse.DisplayContext with + | Some typeInfo -> + let colon = TaggedText(TextTag.Text, ": ") + colon :: (typeInfo |> Array.toList) + + // not sure when this can happen + | None -> + [] + + let private getHint symbol (symbolUse: FSharpSymbolUse) = + { + Kind = HintKind.TypeHint + Range = symbolUse.Range.EndRange + Parts = getHintParts symbol symbolUse + } + + let isValidForHint + (parseFileResults: FSharpParseFileResults) + (symbol: FSharpMemberOrFunctionOrValue) + (symbolUse: FSharpSymbolUse) = + + let isNotAnnotatedManually = + not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) + + let isNotAfterDot = + symbolUse.IsFromDefinition + && not symbol.IsMemberThisValue + + let isNotTypeAlias = + not symbol.IsConstructorThisValue + + symbol.IsValue // we'll be adding other stuff gradually here + && isNotAnnotatedManually + && isNotAfterDot + && isNotTypeAlias + + let getHints symbol symbolUse = + [ getHint symbol symbolUse ] diff --git a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs index 9396d7d925..8433b1a638 100644 --- a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs @@ -7,7 +7,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints open Microsoft.VisualStudio.FSharp.Editor open FSharp.Compiler.Text -open HintService +open Hints module NativeToRoslynHintConverter = diff --git a/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs b/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs new file mode 100644 index 0000000000..c7602aedb4 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/OptionParser.fs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open Microsoft.VisualStudio.FSharp.Editor +open Hints + +module OptionParser = + + let getHintKinds options = + Set + [ if options.IsInlineTypeHintsEnabled then + HintKind.TypeHint + + if options.IsInlineParameterNameHintsEnabled then + HintKind.ParameterNameHint ] \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs index 01fde06102..cddf8d250f 100644 --- a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -22,17 +22,19 @@ type internal RoslynAdapter interface IFSharpInlineHintsService with member _.GetInlineHintsAsync(document, _, cancellationToken) = async { - if not settings.Advanced.IsInlineHintsEnabled - then return ImmutableArray.Empty - - else - let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask - let! nativeHints = - HintService.getHintsForDocument document userOpName cancellationToken + let! sourceText = document.GetTextAsync cancellationToken |> Async.AwaitTask + let hintKinds = OptionParser.getHintKinds settings.Advanced + + let! nativeHints = + HintService.getHintsForDocument + document + hintKinds + userOpName + cancellationToken - let roslynHints = - nativeHints - |> Seq.map (NativeToRoslynHintConverter.convert sourceText) + let roslynHints = + nativeHints + |> Seq.map (NativeToRoslynHintConverter.convert sourceText) - return roslynHints.ToImmutableArray() + return roslynHints.ToImmutableArray() } |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index e9a05b99c7..4ee2d14273 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -86,11 +86,13 @@ type LensOptions = type AdvancedOptions = { IsBlockStructureEnabled: bool IsOutliningEnabled: bool - IsInlineHintsEnabled: bool } + IsInlineTypeHintsEnabled: bool + IsInlineParameterNameHintsEnabled: bool } static member Default = { IsBlockStructureEnabled = true IsOutliningEnabled = true - IsInlineHintsEnabled = false } + IsInlineTypeHintsEnabled = false + IsInlineParameterNameHintsEnabled = false } [] type FormattingOptions = diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 4e80e56c6f..1a80650b15 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -25,8 +25,12 @@ Content="{x:Static local:Strings.Show_Outlining}"/> - + + + + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index 23f3b72320..c2c44bfb4a 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -212,7 +212,7 @@ public static string Inline_Hints { return ResourceManager.GetString("Inline_Hints", resourceCulture); } } - + /// /// Looks up a localized string similar to IntelliSense Performance Options. /// @@ -339,15 +339,24 @@ public static string Show_guides { } } + /// + /// Looks up a localized string similar to Display inline parameter name hints (experimental). + /// + public static string Show_Inline_Parameter_Name_Hints { + get { + return ResourceManager.GetString("Show_Inline_Parameter_Name_Hints", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display inline type hints (experimental). /// - public static string Show_Inline_Hints { + public static string Show_Inline_Type_Hints { get { - return ResourceManager.GetString("Show_Inline_Hints", resourceCulture); + return ResourceManager.GetString("Show_Inline_Type_Hints", resourceCulture); } } - + /// /// Looks up a localized string similar to S_how navigation links as. /// diff --git a/vsintegration/src/FSharp.UIResources/Strings.resx b/vsintegration/src/FSharp.UIResources/Strings.resx index e6f9fccd9d..f87b2edd98 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -192,7 +192,7 @@ Inline Hints - + Display inline type hints (experimental) @@ -243,4 +243,7 @@ Enable parallel reference resolution + + Display inline parameter name hints (experimental) + \ No newline at end of file diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 2c2fe934b7..d50c7ea6d1 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index b946628c02..d4d347fbc8 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 9212a40cb0..2591eae9f3 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index 5fa8f1caea..8f7821d9ae 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index a5732c33ea..447da25a46 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 30a7d45df9..a2f3594e54 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index ef59262a11..e03949a0ba 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index b388b74d48..71e9dbf228 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index 43082d7a46..96ec77b97f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index dfe465c025..7e45d8e8d2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 7717e353c2..c05d626e70 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index 7e5b5761e6..abe99a445c 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index c24a02fb05..4e5a0bacc5 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -82,7 +82,12 @@ Parallelization (requires restart) - + + Display inline parameter name hints (experimental) + Display inline parameter name hints (experimental) + + + Display inline type hints (experimental) Display inline type hints (experimental) diff --git a/vsintegration/tests/UnitTests/HintTestFramework.fs b/vsintegration/tests/UnitTests/HintTestFramework.fs index 87d4438abc..135f2721be 100644 --- a/vsintegration/tests/UnitTests/HintTestFramework.fs +++ b/vsintegration/tests/UnitTests/HintTestFramework.fs @@ -5,9 +5,9 @@ namespace VisualFSharp.UnitTests.Editor.Hints open System.Threading open Microsoft.VisualStudio.FSharp.Editor open Microsoft.VisualStudio.FSharp.Editor.Hints -open Microsoft.VisualStudio.FSharp.Editor.Hints.HintService open VisualFSharp.UnitTests.Editor open Microsoft.CodeAnalysis.Text +open Hints module HintTestFramework = @@ -44,9 +44,23 @@ let getFsiAndFsDocuments (fsiCode: string) (fsCode: string) = "test.fs", SourceText.From fsCode) -let getHints document = +let getHints document hintKinds = async { - let! hints = HintService.getHintsForDocument document "test" CancellationToken.None + let! hints = HintService.getHintsForDocument document hintKinds "test" CancellationToken.None return hints |> Seq.map convert } |> Async.RunSynchronously + +let getTypeHints document = + getHints document (Set.empty.Add(HintKind.TypeHint)) + +let getParameterNameHints document = + getHints document (Set.empty.Add(HintKind.ParameterNameHint)) + +let getAllHints document = + let hintKinds = + Set.empty + .Add(HintKind.TypeHint) + .Add(HintKind.ParameterNameHint) + + getHints document hintKinds \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs new file mode 100644 index 0000000000..9fbebcd139 --- /dev/null +++ b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace VisualFSharp.UnitTests.Editor.Hints + +open NUnit.Framework +open HintTestFramework + +module InlineParameterNameHintTests = + +[] +let ``Hint is shown for a let binding`` () = + let code = """ +let greet friend = $"hello {friend}" +let greeting = greet "darkness" +""" + let document = getFsDocument code + let expected = [{ Content = "friend = "; Location = (2, 22) }] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are shown for multiple function calls`` () = + let code = """ +let greet friend = $"hello {friend}" +let greeting1 = greet "Noel" +let greeting2 = greet "Liam" +""" + let document = getFsDocument code + let expected = [ + { Content = "friend = "; Location = (2, 23) } + { Content = "friend = "; Location = (3, 23) } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are shown for multiple parameters`` () = + let code = """ +let greet friend1 friend2 = $"hello {friend1} and {friend2}" +let greeting = greet "Liam" "Noel" +""" + let document = getFsDocument code + let expected = [ + { Content = "friend1 = "; Location = (2, 22) } + { Content = "friend2 = "; Location = (2, 29) } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are shown for tuple items`` () = + let code = """ +let greet (friend1, friend2) = $"hello {friend1} and {friend2}" +let greeting = greet ("Liam", "Noel") +""" + let document = getFsDocument code + let expected = [ + { Content = "friend1 = "; Location = (2, 23) } + { Content = "friend2 = "; Location = (2, 31) } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are shown for active patterns`` () = + let code = """ +let (|Even|Odd|) n = + if n % 2 = 0 then Even + else Odd + +let evenOrOdd number = + match number with + | Even -> "even" + | Odd -> "odd" + +let even = evenOrOdd 42 +let odd = evenOrOdd 41 +""" + let document = getFsDocument code + let expected = [ + { Content = "number = "; Location = (10, 22) } + { Content = "number = "; Location = (11, 21) } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + +[] // here we don't want an empty hint before "x" +let ``Hints are not shown for nameless parameters`` () = + let code = """ +let exists predicate option = + match option with + | None -> false + | Some x -> predicate x +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + +[] // here we don't want a useless (?) hint "value = " +let ``Hints are not shown for parameters of built-in operators`` () = + let code = """ +let postTrue = not true +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + +[] +let ``Hints are not shown for parameters of custom operators`` () = + let code = """ +let (===) value1 value2 = value1 = value2 + +let c = "javascript" === "javascript" +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + +[] +let ``Hints are not (yet) shown for method parameters`` () = + let code = """ +let theAnswer = System.Console.WriteLine 42 +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + +[] +let ``Hints are not (yet) shown for constructor parameters`` () = + let code = """ +type WrappedThing(x) = + let unwrapped = x + +let wrapped = WrappedThing 42 +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + +[] +let ``Hints are not (yet) shown for dicrimanted unions`` () = + let code = """ +type Shape = + | Square of side : float + | Circle of radius : float + +let circle = Circle 42 +""" + let document = getFsDocument code + + let result = getParameterNameHints document + + Assert.IsEmpty(result) + diff --git a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs new file mode 100644 index 0000000000..36aabb4c8a --- /dev/null +++ b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace VisualFSharp.UnitTests.Editor.Hints + +open NUnit.Framework +open HintTestFramework + +module InlineTypeHintTests = + +[] +let ``Hint is shown for a let binding`` () = + let code = """ +type Song = { Artist: string; Title: string } + +let s = { Artist = "Moby"; Title = "Porcelain" } +""" + let document = getFsDocument code + let expected = [{ Content = ": Song"; Location = (3, 6) }] + + let actual = getTypeHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hint is shown for a parameter`` () = + let code = """ +type Song = { Artist: string; Title: string } + +let whoSings s = s.Artist +""" + let document = getFsDocument code + let expected = [{ Content = ": Song"; Location = (3, 15) }] + + let actual = getTypeHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Hints are not shown in signature files`` () = + let fsiCode = """ +module Test + +val numbers: int[] +""" + let fsCode = """ +module Test + +let numbers = [|42|] +""" + let fsiDocument, _ = getFsiAndFsDocuments fsiCode fsCode + + let result = getTypeHints fsiDocument + + Assert.IsEmpty(result) + +[] +let ``Hints are not shown for let-bound functions yet`` () = + let code = """ +let setConsoleOut = System.Console.SetOut +""" + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) + +[] +let ``Hint is not shown for a let binding when the type is manually specified`` () = + let code = """ +type Song = { Artist: string; Title: string } + +let s: Song = { Artist = "Moby"; Title = "Porcelain" } +""" + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) + +[] +let ``Hint is not shown for a parameter when the type is manually specified`` () = + let code = """ +type Song = { Artist: string; Title: string } + +let whoSings (s: Song) = s.Artist +""" + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) + +[] // here we don't want a hint after "this" +let ``Hint is not shown for type self-identifiers`` () = + let code = """ +type Song() = + member this.GetName() = "Porcelain" +""" + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) + +[] // here we don't want a hint after "x" +let ``Hint is not shown for type aliases`` () = + let code = """ +type Song() as x = + member this.Name = "Porcelain" +""" + let document = getFsDocument code + + let result = getTypeHints document + + Assert.IsEmpty(result) diff --git a/vsintegration/tests/UnitTests/OptionParserTests.fs b/vsintegration/tests/UnitTests/OptionParserTests.fs new file mode 100644 index 0000000000..365bf1ef7c --- /dev/null +++ b/vsintegration/tests/UnitTests/OptionParserTests.fs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace VisualFSharp.UnitTests.Editor.Hints + +open NUnit.Framework +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.Hints +open Microsoft.VisualStudio.FSharp.Editor.Hints.Hints + +// best tests ever - very scalable +module OptionParserTests = + +[] +let ``Type hints off, parameter name hints off`` () = + let options = { AdvancedOptions.Default with + IsInlineTypeHintsEnabled = false + IsInlineParameterNameHintsEnabled = false } + + let expected = [] + + let actual = OptionParser.getHintKinds options + + Assert.AreEqual(expected, actual) + +[] +let ``Type hints on, parameter name hints off`` () = + let options = { AdvancedOptions.Default with + IsInlineTypeHintsEnabled = true + IsInlineParameterNameHintsEnabled = false } + + let expected = [HintKind.TypeHint] + + let actual = OptionParser.getHintKinds options + + Assert.AreEqual(expected, actual) + +[] +let ``Type hints off, parameter name hints on`` () = + let options = { AdvancedOptions.Default with + IsInlineTypeHintsEnabled = false + IsInlineParameterNameHintsEnabled = true } + + let expected = [HintKind.ParameterNameHint] + + let actual = OptionParser.getHintKinds options + + Assert.AreEqual(expected, actual) + +[] +let ``Type hints on, parameter name hints on`` () = + let options = { AdvancedOptions.Default with + IsInlineTypeHintsEnabled = true + IsInlineParameterNameHintsEnabled = true } + + let expected = [HintKind.TypeHint; HintKind.ParameterNameHint] + + let actual = OptionParser.getHintKinds options + + Assert.AreEqual(expected, actual) \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs new file mode 100644 index 0000000000..fb1dcbe801 --- /dev/null +++ b/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace VisualFSharp.UnitTests.Editor.Hints + +open NUnit.Framework +open HintTestFramework + +// just some kind of higher level testing +module OverallHintExperienceTests = + +[] +let ``Current baseline hints`` () = + let code = """ +type Song = { Artist: string; Title: string } +let whoSings song = song.Artist + +let artist = whoSings { Artist = "Květy"; Title = "Je podzim" } +""" + let document = getFsDocument code + let expected = [ + { Content = ": Song"; Location = (2, 18) } + { Content = ": string"; Location = (4, 11) } + { Content = "song = "; Location = (4, 23) } + ] + + let actual = getAllHints document + + CollectionAssert.AreEquivalent(expected, actual) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 15b068503d..6889c74771 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -105,8 +105,17 @@ Editor\Hints\HintTestFramework.fs - - Editor\Hints\HintServiceTests.fs + + Editor\Hints\InlineParameterNameHintTests.fs + + + Editor\Hints\InlineTypeHintTests.fs + + + Editor\Hints\OptionParserTests.fs + + + Editor\Hints\OverallHintExperienceTests.fs Editor\GoToDefinitionServiceTests.fs