diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index cf1ff4e9c25ca6894a49fea0c4431daff8636931..cdfc599466f623e722d7223a2ed8eff06a9399be 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -126,6 +126,9 @@ + + + diff --git a/vsintegration/src/FSharp.Editor/Hints/HintService.fs b/vsintegration/src/FSharp.Editor/Hints/HintService.fs new file mode 100644 index 0000000000000000000000000000000000000000..13edb5448d8918444e09b213ab48df84d343d71b --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/HintService.fs @@ -0,0 +1,79 @@ +// 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.CodeAnalysis +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Symbols +open FSharp.Compiler.Text + +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) = + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as mfvSymbol + when isValidForHint parseResults mfvSymbol symbolUse -> + + [ { + Range = symbolUse.Range + Parts = getHintParts mfvSymbol symbolUse + } ] + + // we'll be adding other stuff gradually here + | _ -> + [] + + let getHintsForDocument (document: Document) userOpName cancellationToken = + async { + if isSignatureFile document.FilePath + then + return [] + else + let! parseResults, checkResults = + document.GetFSharpParseAndCheckResultsAsync userOpName + + return + checkResults.GetAllUsesOfAllSymbolsInFile cancellationToken + |> Seq.toList + |> List.collect (getHintsForSymbol parseResults) + } diff --git a/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs new file mode 100644 index 0000000000000000000000000000000000000000..9396d7d9256e088ac4da9327d5afaa4fc666c7ad --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/NativeToRoslynHintConverter.fs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open System.Collections.Immutable +open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.Text +open HintService + +module NativeToRoslynHintConverter = + + let rangeToSpan range sourceText = + let symbolSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) + let overshadowLength = 0 // anything >0 means overlaying the code + TextSpan(symbolSpan.End, overshadowLength) + + let nativeToRoslynText (taggedText: TaggedText) = + let tag = RoslynHelpers.roslynTag taggedText.Tag + let text = taggedText.Text + RoslynTaggedText(tag, text) + + let convert sourceText hint = + let span = rangeToSpan hint.Range sourceText + let displayParts = hint.Parts |> Seq.map nativeToRoslynText + FSharpInlineHint(span, displayParts.ToImmutableArray()) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs new file mode 100644 index 0000000000000000000000000000000000000000..01fde061024a6185f0e059d24973c28cb9e0ef44 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Hints/RoslynAdapter.fs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor.Hints + +open System.Collections.Immutable +open System.ComponentModel.Composition +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.InlineHints +open Microsoft.VisualStudio.FSharp.Editor + +// So the Roslyn interface is called IFSharpInlineHintsService +// but our implementation is called just HintsService. +// That's because we'll likely use this API for things other than inline hints, +// e.g. signature hints above the line, pipeline hints on the side and so on. + +[)>] +type internal RoslynAdapter + [] + (settings: EditorOptions) = + + static let userOpName = "Hints" + + 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 roslynHints = + nativeHints + |> Seq.map (NativeToRoslynHintConverter.convert sourceText) + + 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 e6c4b202d8eaf29462cfbf293cacb0f79556a58c..e9a05b99c7acc0d9d8e5f8a76481880b52d5d70b 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -85,10 +85,12 @@ type LensOptions = [] type AdvancedOptions = { IsBlockStructureEnabled: bool - IsOutliningEnabled: bool } + IsOutliningEnabled: bool + IsInlineHintsEnabled: bool } static member Default = { IsBlockStructureEnabled = true - IsOutliningEnabled = true } + IsOutliningEnabled = true + IsInlineHintsEnabled = false } [] type FormattingOptions = diff --git a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml index 7126d6efb8bbad8743f20f0aee4d4347f5b60f30..4e80e56c6f484d2a70a9c281f0871f5f734445ce 100644 --- a/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml +++ b/vsintegration/src/FSharp.UIResources/AdvancedOptionsControl.xaml @@ -24,6 +24,10 @@ + + + diff --git a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs index d7e8ec6a86c1cd2943861f458f79511772b63a53..23f3b7232049ef1fc486c89bfd23a2f40c92d6f2 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.Designer.cs +++ b/vsintegration/src/FSharp.UIResources/Strings.Designer.cs @@ -204,6 +204,15 @@ public class Strings { } } + /// + /// Looks up a localized string similar to Inline Hints. + /// + public static string Inline_Hints { + get { + return ResourceManager.GetString("Inline_Hints", resourceCulture); + } + } + /// /// Looks up a localized string similar to IntelliSense Performance Options. /// @@ -330,6 +339,15 @@ public class Strings { } } + /// + /// Looks up a localized string similar to Display inline type hints (experimental). + /// + public static string Show_Inline_Hints { + get { + return ResourceManager.GetString("Show_Inline_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 a55acfe02906a503a5f04c726415266c7144abd4..e6f9fccd9dac505ac1f23ae5de8d9f0896191a3e 100644 --- a/vsintegration/src/FSharp.UIResources/Strings.resx +++ b/vsintegration/src/FSharp.UIResources/Strings.resx @@ -189,6 +189,12 @@ Show outlining and collapsible nodes for F# code + + Inline Hints + + + Display inline type hints (experimental) + Time until stale results are used (in milliseconds) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf index 465126331dd46c4f591ea01d9de9a7795daa1ad0..2c2fe934b7c532a76ec40bfd9a73d3faa36826f9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.cs.xlf @@ -7,6 +7,11 @@ Vždy umístit otevřené příkazy na nejvyšší úroveň + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Zobrazit s_ymboly v neotevřených oborech názvů diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf index 19dbdecc59179a825fd0425a4fe9d48c53aa29cd..b946628c022a2cbd1ef5911a2cb34269f7fc38dd 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.de.xlf @@ -7,6 +7,11 @@ open-Anweisungen immer an oberster Ebene platzieren + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces S_ymbole in nicht geöffneten Namespaces anzeigen diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf index 593de70499a8a40ae0d33e6dd7f0a978e3194729..9212a40cb0b9357dded724f1112359035240f66f 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.es.xlf @@ -7,6 +7,11 @@ Colocar siempre las instrucciones open en el nivel superior + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Mostrar sím_bolos en espacios de nombres sin abrir diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf index ac9837fd02a81cfe8a8fb917eeb91659516f4d7b..5fa8f1caea20b50cc229d94426a443848ec8c7a2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.fr.xlf @@ -7,6 +7,11 @@ Placer toujours les instructions open au niveau supérieur + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Afficher les sym_boles dans les espaces de noms non ouverts diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf index b1cbcdc1cda6e5b14018958191d15505a1c4a09d..a5732c33ea345eae84d90cc7564bc5340a681bb0 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.it.xlf @@ -7,6 +7,11 @@ Inserisci sempre le istruzioni OPEN al primo livello + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Mostra si_mboli in spazi dei nomi non aperti diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf index 1d40d97e0e7e93f4196663f325f5cc3f4ec7cf07..30a7d45df9f034bc250bb00a54d4be2a4b1fd03e 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ja.xlf @@ -7,6 +7,11 @@ Open ステートメントを常に最上位に配置する + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces 開かれていない名前空間の記号を表示する(_Y) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf index 42d4f8d5b2f2b17a22699576024acc3b3176346d..ef59262a118ebcff9308db50ebc2b32dbc8dbaad 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ko.xlf @@ -7,6 +7,11 @@ 항상 최상위에 open 문 배치 + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces 열려 있지 않은 네임스페이스에 기호 표시(_Y) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf index 80c575eb707871a726f9b3df46a353e6a989ea62..b388b74d48e63cba0d45eb9060341b521d74cfd2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pl.xlf @@ -7,6 +7,11 @@ Zawsze umieszczaj otwarte instrukcje na najwyższym poziomie + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Pokaż s_ymbole w nieotwartych przestrzeniach nazw diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf index ed7bd886e925151f1ac72a30d39799b645ae4009..43082d7a4680c28758550d179e1f466cd597df3d 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.pt-BR.xlf @@ -7,6 +7,11 @@ Sempre coloque as instruções abertas no nível superior + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Mostrar s_ímbolos em namespaces não abertos diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf index 2b61ff39466d301806d83706e6ec208074c433e8..dfe465c0259f41fcb00f1af688f44228a8ab86a2 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.ru.xlf @@ -7,6 +7,11 @@ Всегда располагайте открытые операторы на верхнем уровне + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces По_казать символы в неоткрытых пространствах имен diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf index 031215b1fdcc0e34732cff5976a3bb5ab469e27e..7717e353c223c280992bd7f3718dc8957f9599a9 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.tr.xlf @@ -7,6 +7,11 @@ Açık deyimleri her zaman en üst düzeye yerleştir + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces Açılmamış ad alanlarında s_embolleri göster diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf index f78d543557e048685a83a2360ad2209b738107ac..7e5b5761e6e0207b6463e59b2009e389a074974a 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hans.xlf @@ -7,6 +7,11 @@ 始终在顶层放置 open 语句 + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces 显示未打开的命名空间中的符号(_Y) diff --git a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf index 71eac224dc4fa149735d4fe32dc3189954bcf814..c24a02fb051d7a0d94e240d01119a4162f228098 100644 --- a/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf +++ b/vsintegration/src/FSharp.UIResources/xlf/Strings.zh-Hant.xlf @@ -7,6 +7,11 @@ 一律將 open 陳述式放在最上層 + + Inline Hints + Inline Hints + + Lens Lens @@ -77,6 +82,11 @@ Parallelization (requires restart) + + Display inline type hints (experimental) + Display inline type hints (experimental) + + Show s_ymbols in unopened namespaces 顯示未開啟之命名空間中的符號(_Y) diff --git a/vsintegration/tests/UnitTests/HintServiceTests.fs b/vsintegration/tests/UnitTests/HintServiceTests.fs new file mode 100644 index 0000000000000000000000000000000000000000..f94954df4c9a0f3e55cf006a5a4bf5347d37dcd5 --- /dev/null +++ b/vsintegration/tests/UnitTests/HintServiceTests.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 HintServiceTests = + +[] +let ``Type 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 = getHints fsiDocument + + Assert.IsEmpty(result) + +[] +let ``Type hints are not shown for let-bound functions yet`` () = + let code = """ +let setConsoleOut = System.Console.SetOut +""" + let document = getFsDocument code + + let result = getHints document + + Assert.IsEmpty(result) + +[] +let ``Type 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 = getHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Type 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 = getHints document + + Assert.IsEmpty(result) + +[] +let ``Type 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 = getHints document + + Assert.AreEqual(expected, actual) + +[] +let ``Type 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 = getHints document + + Assert.IsEmpty(result) + +[] // here we don't want a hint after "this" +let ``Type hint is not shown for type self-identifiers`` () = + let code = """ +type Song() = + member this.GetName() = "Porcelain" +""" + let document = getFsDocument code + + let result = getHints document + + Assert.IsEmpty(result) + +[] // here we don't want a hint after "x" +let ``Type hint is not shown for type aliases`` () = + let code = """ +type Song() as x = + member this.Name = "Porcelain" +""" + let document = getFsDocument code + + let result = getHints document + + Assert.IsEmpty(result) diff --git a/vsintegration/tests/UnitTests/HintTestFramework.fs b/vsintegration/tests/UnitTests/HintTestFramework.fs new file mode 100644 index 0000000000000000000000000000000000000000..87d4438abc36d43b9be1bac5cd83e6a3917df005 --- /dev/null +++ b/vsintegration/tests/UnitTests/HintTestFramework.fs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +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 + +module HintTestFramework = + +// another representation for extra convenience +type TestHint = { + Content: string + Location: int * int +} + +let private convert hint = + let content = + hint.Parts + |> Seq.map (fun hintPart -> hintPart.Text) + |> String.concat "" + + // that's about different coordinate systems + // in tests, the most convenient is the one used in editor, + // hence this conversion + let location = (hint.Range.StartLine - 1, hint.Range.EndColumn + 1) + + { Content = content + Location = location } + +let getFsDocument code = + use project = SingleFileProject code + let fileName = fst project.Files.Head + let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code) + document + +let getFsiAndFsDocuments (fsiCode: string) (fsCode: string) = + RoslynTestHelpers.CreateTwoDocumentSolution( + "test.fsi", + SourceText.From fsiCode, + "test.fs", + SourceText.From fsCode) + +let getHints document = + async { + let! hints = HintService.getHintsForDocument document "test" CancellationToken.None + return hints |> Seq.map convert + } + |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index f7de265b87b1fa5d3ccb80818a591d9c102ef8bb..da0451837a64d10c26b930dc3cc322e4e0f4b8ba 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -273,7 +273,59 @@ type RoslynTestHelpers private () = document + static member CreateTwoDocumentSolution (filePath1, text1: SourceText, filePath2, text2: SourceText) = + let workspace = new AdhocWorkspace(TestHostServices()) + + let projId = ProjectId.CreateNewId() + let docId1 = DocumentId.CreateNewId(projId) + let docId2 = DocumentId.CreateNewId(projId) + + let docInfo1 = + DocumentInfo.Create( + docId1, + filePath1, + loader=TextLoader.From(text1.Container, VersionStamp.Create(DateTime.UtcNow)), + filePath=filePath1, + sourceCodeKind= SourceCodeKind.Regular) + + let docInfo2 = + DocumentInfo.Create( + docId2, + filePath2, + loader=TextLoader.From(text2.Container, VersionStamp.Create(DateTime.UtcNow)), + filePath=filePath2, + sourceCodeKind= SourceCodeKind.Regular) + + let projFilePath = "C:\\test.fsproj" + let projInfo = + ProjectInfo.Create( + projId, + VersionStamp.Create(DateTime.UtcNow), + projFilePath, + "test.dll", + LanguageNames.FSharp, + documents = [docInfo1;docInfo2], + filePath = projFilePath + ) + + let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo]) + let solution = workspace.AddSolution(solutionInfo) + + workspace + .Services + .GetService() + .FSharpProjectOptionsManager + .SetCommandLineOptions(projId, [|filePath1;filePath2|], ImmutableArray.Empty) + + let document1 = solution.GetProject(projId).GetDocument(docId1) + let document2 = solution.GetProject(projId).GetDocument(docId2) + document1, document2 + static member CreateSingleDocumentSolution (filePath, code: string, ?options) = let text = SourceText.From(code) RoslynTestHelpers.CreateSingleDocumentSolution(filePath, text, ?options = options), text + static member CreateTwoDocumentSolution (filePath1, code1: string, filePath2, code2: string) = + let text1 = SourceText.From code1 + let text2 = SourceText.From code2 + RoslynTestHelpers.CreateTwoDocumentSolution(filePath1, text1, filePath2, text2) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 542ec626ebd18f06e5c38b24da8beffa06d27395..15b068503d80dbdc1ac88101308d10acf715f562 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -102,6 +102,12 @@ Editor\QuickInfoTests.fs + + Editor\Hints\HintTestFramework.fs + + + Editor\Hints\HintServiceTests.fs + Editor\GoToDefinitionServiceTests.fs