diff --git a/.fantomasignore b/.fantomasignore index fabfd3547e1..119fe1d5ca3 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -7,7 +7,8 @@ fcs-samples/ scripts/ setup/ tests/ -vsintegration/ +vsintegration/* +!vsintegration/tests/FSharp.Editor.Tests artifacts/ # Explicitly unformatted implementation files diff --git a/FSharp.Editor.sln b/FSharp.Editor.sln new file mode 100644 index 00000000000..70ffe6bb0ea --- /dev/null +++ b/FSharp.Editor.sln @@ -0,0 +1,76 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32113.165 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Editor.Tests", "vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj", "{321F47BC-8148-4C8D-B340-08B7BF07D31D}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service", "src\Compiler\FSharp.Compiler.Service.fsproj", "{AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Core", "src\FSharp.Core\FSharp.Core.fsproj", "{67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.DependencyManager.Nuget", "src\FSharp.DependencyManager.Nuget\FSharp.DependencyManager.Nuget.fsproj", "{24399E68-9000-4556-BDDD-8D74A9660D28}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Editor", "vsintegration\src\FSharp.Editor\FSharp.Editor.fsproj", "{86E148BE-92C8-47CC-A070-11D769C6D898}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.PatternMatcher", "vsintegration\src\FSharp.PatternMatcher\FSharp.PatternMatcher.csproj", "{4FFA5E03-4128-48C9-8FCD-D7C60729ED74}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.UIResources", "vsintegration\src\FSharp.UIResources\FSharp.UIResources.csproj", "{DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.VS.FSI", "vsintegration\src\FSharp.VS.FSI\FSharp.VS.FSI.fsproj", "{EAC029EB-4A8F-4966-9B38-60D73D8E20D1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Proto|Any CPU = Proto|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {321F47BC-8148-4C8D-B340-08B7BF07D31D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {321F47BC-8148-4C8D-B340-08B7BF07D31D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {321F47BC-8148-4C8D-B340-08B7BF07D31D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {321F47BC-8148-4C8D-B340-08B7BF07D31D}.Release|Any CPU.Build.0 = Release|Any CPU + {321F47BC-8148-4C8D-B340-08B7BF07D31D}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}.Release|Any CPU.Build.0 = Release|Any CPU + {AD603EF2-FAC6-48D1-AAEB-A6CF898062A9}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Release|Any CPU.Build.0 = Release|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Proto|Any CPU.ActiveCfg = Proto|Any CPU + {67DA0BF3-AAD3-47F4-9EC6-AD8EC532B5A9}.Proto|Any CPU.Build.0 = Proto|Any CPU + {24399E68-9000-4556-BDDD-8D74A9660D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24399E68-9000-4556-BDDD-8D74A9660D28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24399E68-9000-4556-BDDD-8D74A9660D28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24399E68-9000-4556-BDDD-8D74A9660D28}.Release|Any CPU.Build.0 = Release|Any CPU + {24399E68-9000-4556-BDDD-8D74A9660D28}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {86E148BE-92C8-47CC-A070-11D769C6D898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86E148BE-92C8-47CC-A070-11D769C6D898}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86E148BE-92C8-47CC-A070-11D769C6D898}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86E148BE-92C8-47CC-A070-11D769C6D898}.Release|Any CPU.Build.0 = Release|Any CPU + {86E148BE-92C8-47CC-A070-11D769C6D898}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {4FFA5E03-4128-48C9-8FCD-D7C60729ED74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FFA5E03-4128-48C9-8FCD-D7C60729ED74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FFA5E03-4128-48C9-8FCD-D7C60729ED74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FFA5E03-4128-48C9-8FCD-D7C60729ED74}.Release|Any CPU.Build.0 = Release|Any CPU + {4FFA5E03-4128-48C9-8FCD-D7C60729ED74}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}.Release|Any CPU.Build.0 = Release|Any CPU + {DA9495E6-BEAA-42A4-AD3B-170D2005AF4B}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {EAC029EB-4A8F-4966-9B38-60D73D8E20D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAC029EB-4A8F-4966-9B38-60D73D8E20D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAC029EB-4A8F-4966-9B38-60D73D8E20D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAC029EB-4A8F-4966-9B38-60D73D8E20D1}.Release|Any CPU.Build.0 = Release|Any CPU + {EAC029EB-4A8F-4966-9B38-60D73D8E20D1}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {68ED1BEB-EB1D-4334-A708-988D54C83B66} + EndGlobalSection +EndGlobal diff --git a/TESTGUIDE.md b/TESTGUIDE.md index 91a28af528a..9eeaa90ead4 100644 --- a/TESTGUIDE.md +++ b/TESTGUIDE.md @@ -106,8 +106,9 @@ The F# tests are split as follows: * [FSharp.Compiler.ComponentTests](tests/FSharp.Compiler.ComponentTests) - Validation of compiler APIs. -* [VisualFSharp.UnitTests](vsintegration/tests/unittests) - Visual F# Tools IDE Unit Test Suite - This suite exercises a wide range of behaviors in the F# Visual Studio project system and language service. +* [VisualFSharp.UnitTests](vsintegration/tests/unittests) - Validation of a wide range of behaviors in the F# Visual Studio project system and language service (including the legacy one). + +* [FSharp.Editor.Tests](vsintegration/tests/FSharp.Editor.Tests) - Visual F# Tools IDE Test Suite. ### FSharp Suite @@ -148,9 +149,9 @@ Tags are in the left column, paths to to corresponding test folders are in the r If you want to re-run a particular test area, the easiest way to do so is to set a temporary tag for that area in test.lst (e.g. "RERUN") and adjust `ttags` [run.fsharpqa.test.fsx script](tests/fsharpqa/run.fsharpqa.test.fsx) and run it. -### FSharp.Compiler.UnitTests, FSharp.Core.UnitTests, VisualFSharp.UnitTests +### FSharp.Compiler.UnitTests, FSharp.Core.UnitTests, VisualFSharp.UnitTests, FSharp.Editor.Tests -These are all NUnit tests. You can execute these tests individually via the Visual Studio NUnit3 runner +These are all currently NUnit tests (we hope to migrate them to xUnit). You can execute these tests individually via the Visual Studio NUnit3 runner extension or the command line via `nunit3-console.exe`. Note that for compatibility reasons, the IDE unit tests should be run in a 32-bit process, diff --git a/VisualFSharp.sln b/VisualFSharp.sln index 60b7ece4d7f..c9bb5ddc220 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -193,6 +193,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FCSBenchmarks", "FCSBenchma EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fsharp.ProfilingStartpointProject", "tests\benchmarks\Fsharp.ProfilingStartpointProject\Fsharp.ProfilingStartpointProject.fsproj", "{FE23BB65-276A-4E41-8CC7-F7752241DEBA}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Editor.Tests", "vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj", "{CBC96CC7-65AB-46EA-A82E-F6A788DABF80}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1019,6 +1021,18 @@ Global {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|Any CPU.Build.0 = Release|Any CPU {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|x86.ActiveCfg = Release|Any CPU {FE23BB65-276A-4E41-8CC7-F7752241DEBA}.Release|x86.Build.0 = Release|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Debug|x86.Build.0 = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Proto|Any CPU.Build.0 = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Proto|x86.ActiveCfg = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Proto|x86.Build.0 = Debug|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Release|x86.ActiveCfg = Release|Any CPU + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1099,6 +1113,7 @@ Global {583182E1-3484-4A8F-AC06-7C0D232C0CA4} = {39CDF34B-FB23-49AE-AB27-0975DA379BB5} {39CDF34B-FB23-49AE-AB27-0975DA379BB5} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} {FE23BB65-276A-4E41-8CC7-F7752241DEBA} = {39CDF34B-FB23-49AE-AB27-0975DA379BB5} + {CBC96CC7-65AB-46EA-A82E-F6A788DABF80} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/eng/Build.ps1 b/eng/Build.ps1 index c2d58455868..98a71be0b07 100644 --- a/eng/Build.ps1 +++ b/eng/Build.ps1 @@ -592,6 +592,7 @@ try { if ($testVs -and -not $noVisualStudio) { TestUsingNUnit -testProject "$RepoRoot\vsintegration\tests\UnitTests\VisualFSharp.UnitTests.fsproj" -targetFramework $desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\VisualFSharp.UnitTests\" + TestUsingNUnit -testProject "$RepoRoot\vsintegration\tests\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj" -targetFramework $desktopTargetFramework -testadapterpath "$ArtifactsDir\bin\FSharp.Editor.Tests\FSharp.Editor.Tests.fsproj" } # verify nupkgs have access to the source code diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index d27151bf5e5..7d75ba27ad8 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -14,6 +14,7 @@ + diff --git a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs similarity index 65% rename from vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs index f0a158b694b..49b852c5d4a 100644 --- a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/BraceMatchingServiceTests.fs @@ -1,35 +1,33 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor -open System -open System.Threading +namespace FSharp.Editor.Tests +open System open NUnit.Framework - -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor open Microsoft.CodeAnalysis.Text open FSharp.Compiler.CodeAnalysis open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.LanguageService -open UnitTests.TestLib.LanguageService -[][] -type BraceMatchingServiceTests() = +[] +type BraceMatchingServiceTests() = + let checker = FSharpChecker.Create() + let fileName = "C:\\test.fs" - let projectOptions: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| fileName |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } + + let projectOptions: FSharpProjectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| fileName |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } member private this.VerifyNoBraceMatch(fileContents: string, marker: string) = let sourceText = SourceText.From(fileContents) @@ -37,10 +35,14 @@ type BraceMatchingServiceTests() = Assert.IsTrue(position >= 0, "Cannot find marker '{0}' in file contents", marker) let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunImmediate with + + match + FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") + |> Async.RunImmediateExceptOnUI + with | None -> () - | Some(left, right) -> Assert.Fail("Found match for brace '{0}'", marker) - + | Some (left, right) -> Assert.Fail("Found match for brace '{0}'", marker) + member private this.VerifyBraceMatch(fileContents: string, startMarker: string, endMarker: string) = let sourceText = SourceText.From(fileContents) let startMarkerPosition = fileContents.IndexOf(startMarker) @@ -48,16 +50,27 @@ type BraceMatchingServiceTests() = Assert.IsTrue(startMarkerPosition >= 0, "Cannot find start marker '{0}' in file contents", startMarkerPosition) Assert.IsTrue(endMarkerPosition >= 0, "Cannot find end marker '{0}' in file contents", endMarkerPosition) - + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, startMarkerPosition, "UnitTest") |> Async.RunImmediate with + + match + FSharpBraceMatchingService.GetBraceMatchingResult( + checker, + sourceText, + fileName, + parsingOptions, + startMarkerPosition, + "UnitTest" + ) + |> Async.RunImmediateExceptOnUI + with | None -> Assert.Fail("Didn't find a match for start brace at position '{0}", startMarkerPosition) - | Some(left, right) -> - let endPositionInRange(range) = + | Some (left, right) -> + let endPositionInRange (range) = let span = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) span.Start <= endMarkerPosition && endMarkerPosition <= span.End - Assert.IsTrue(endPositionInRange(left) || endPositionInRange(right), "Found end match at incorrect position") - + + Assert.IsTrue(endPositionInRange (left) || endPositionInRange (right), "Found end match at incorrect position") // Starting Brace [] @@ -69,8 +82,9 @@ type BraceMatchingServiceTests() = [] [] [] - member this.NestedBrackets(startMarker: string, endMarker: string) = - let code = " + member this.NestedBrackets(startMarker: string, endMarker: string) = + let code = + " (marker1 {marker2 (marker3 @@ -79,91 +93,105 @@ type BraceMatchingServiceTests() = )marker3 }marker2 )marker1" + this.VerifyBraceMatch(code, startMarker, endMarker) [] - member this.BracketInExpression() = + member this.BracketInExpression() = this.VerifyBraceMatch("let x = (3*5)-1", "(3*", ")-1") [] - member this.BraceInInterpolatedStringSimple() = + member this.BraceInInterpolatedStringSimple() = this.VerifyBraceMatch("let x = $\"abc{1}def\"", "{1", "}def") [] - member this.BraceInInterpolatedStringTwoHoles() = + member this.BraceInInterpolatedStringTwoHoles() = this.VerifyBraceMatch("let x = $\"abc{1}def{2+3}hij\"", "{2", "}hij") [] - member this.BraceInInterpolatedStringNestedRecord() = + member this.BraceInInterpolatedStringNestedRecord() = this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "{contents", "}.contents") this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "{ id", "}\"") [] [] - member this.BraceInMultiLineCommentShouldNotBeMatched(startMarker: string) = - let code = " + member this.BraceInMultiLineCommentShouldNotBeMatched(startMarker: string) = + let code = + " let x = 3 (* This [start is a multiline comment ]end *) printf \"%d\" x" + this.VerifyNoBraceMatch(code, startMarker) - + [] - member this.BraceInAttributesMatch() = - let code = " + member this.BraceInAttributesMatch() = + let code = + " [] module internal name" + this.VerifyBraceMatch(code, "[<", ">]") - + [] - member this.BraceEncapsulatingACommentShouldBeMatched() = - let code = " + member this.BraceEncapsulatingACommentShouldBeMatched() = + let code = + " let x = 3 + (start (* this is a comment *) )end" + this.VerifyBraceMatch(code, "(start", ")end") - + [] [] [] [startsInComment")>] - member this.BraceStartingOrEndingInCommentShouldNotBeMatched(startMarker: string) = - let code = " + member this.BraceStartingOrEndingInCommentShouldNotBeMatched(startMarker: string) = + let code = + " let x = 123 + (endsInComment (* )endsInComment startsInComment" + this.VerifyNoBraceMatch(code, startMarker) - + [] [] [] [startsInDisabledCode")>] - member this.BraceStartingOrEndingInDisabledCodeShouldNotBeMatched(startMarker: string) = - let code = " + member this.BraceStartingOrEndingInDisabledCodeShouldNotBeMatched(startMarker: string) = + let code = + " let x = 123 + (endsInDisabledCode #if UNDEFINED )endsInDisabledCode startsInDisabledCode" + this.VerifyNoBraceMatch(code, startMarker) - + [] [] [] [startsInString")>] - member this.BraceStartingOrEndingInStringShouldNotBeMatched(startMarker: string) = - let code = " + member this.BraceStartingOrEndingInStringShouldNotBeMatched(startMarker: string) = + let code = + " let x = \"stringValue\" + (endsInString + \" )endsInString startsInString" + this.VerifyNoBraceMatch(code, startMarker) - + [] - member this.BraceMatchingAtEndOfLine_Bug1597() = + member this.BraceMatchingAtEndOfLine_Bug1597() = // https://github.com/dotnet/fsharp/issues/1597 - let code = """ + let code = + """ [] let main argv = let arg1 = "" @@ -171,21 +199,25 @@ let main argv = let arg3 = "" (printfn "%A '%A' '%A'" (arg1) (arg2) (arg3))endBrace 0 // return an integer exit code""" + this.VerifyBraceMatch(code, "(printfn", ")endBrace") - - [] - [] - [", [|9;10;15|])>] - [", [|9;10;11;15;16|])>] - [] - []\nlet a7 = 70", [|0;1;22|])>] - [] + + [] + [] + [", [| 9; 10; 15 |])>] + [", [| 9; 10; 11; 15; 16 |])>] + [] + []\nlet a7 = 70", [| 0; 1; 22 |])>] + [] member this.DoNotMatchOnInnerSide(fileContents: string, matchingPositions: int[]) = let sourceText = SourceText.From(fileContents) let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - + for position in matchingPositions do - match FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") |> Async.RunSynchronously with + match + FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, fileName, parsingOptions, position, "UnitTest") + |> Async.RunSynchronously + with | Some _ -> () | None -> match position with diff --git a/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs new file mode 100644 index 00000000000..bc5ca99f999 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/BreakpointResolutionServiceTests.fs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open NUnit.Framework +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Editor.Tests.Helpers + +[] +type BreakpointResolutionServiceTests() = + + let fileName = "C:\\test.fs" + + let code = + " +// This is a comment + +type exampleType(parameter: int) = + member this.exampleMember = parameter + +[] +let main argv = + let integerValue = 123456 + let stringValue = \"This is a string\" + let objectValue = exampleType(789) + + printfn \"%d %s %A\" integerValue stringValue objectValue + + let booleanValue = true + match booleanValue with + | true -> printfn \"correct\" + | false -> printfn \"wrong\" + + 0 // return an integer exit code + " + + static member private testCases: Object[][] = + [| + [| "This is a comment"; None |] + [| "123456"; Some("let integerValue = 123456") |] + [| "stringValue"; Some("let stringValue = \"This is a string\"") |] + [| "789"; Some("let objectValue = exampleType(789)") |] + [| "correct"; Some("printfn \"correct\"") |] + [| "wrong"; Some("printfn \"wrong\"") |] + [| "0"; Some("0") |] + |] + + [] + member this.TestBreakpointResolution(searchToken: string, expectedResolution: string option) = + let searchPosition = code.IndexOf(searchToken) + Assert.IsTrue(searchPosition >= 0, "SearchToken '{0}' is not found in code", searchToken) + + let document, sourceText = + RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code) + + let searchSpan = + TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) + + let actualResolutionOption = + FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) + |> Async.RunSynchronously + + match actualResolutionOption with + | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") + | Some (actualResolutionRange) -> + let actualResolution = + sourceText + .GetSubText(RoslynHelpers.FSharpRangeToTextSpan(sourceText, actualResolutionRange)) + .ToString() + + Assert.IsTrue( + expectedResolution.IsSome, + "BreakpointResolutionService resolved a breakpoint while it shouldn't at: {0}", + actualResolution + ) + + Assert.AreEqual(expectedResolution.Value, actualResolution, "Expected and actual resolutions should match") diff --git a/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs new file mode 100644 index 00000000000..3f51bcc694e --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/CompletionProviderTests.fs @@ -0,0 +1,1291 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +module CompletionProviderTests = + + open System + open System.Linq + open NUnit.Framework + open Microsoft.CodeAnalysis + open Microsoft.CodeAnalysis.Completion + open Microsoft.CodeAnalysis.Text + open Microsoft.VisualStudio.FSharp.Editor + open FSharp.Compiler.CodeAnalysis + open FSharp.Editor.Tests.Helpers + + let filePath = "C:\\test.fs" + + let internal projectOptions opts = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = opts + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let formatCompletions (completions: string seq) = + "\n\t" + String.Join("\n\t", completions) + + let VerifyCompletionListWithOptions (fileContents: string, marker: string, expected: string list, unexpected: string list, opts) = + let options = projectOptions opts + let caretPosition = fileContents.IndexOf(marker) + marker.Length + + let document, _ = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) + + let results = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + |> Async.RunSynchronously + |> Option.defaultValue (ResizeArray()) + |> Seq.map (fun result -> result.DisplayText) + + let expectedFound = expected |> List.filter results.Contains + + let expectedNotFound = expected |> List.filter (expectedFound.Contains >> not) + + let unexpectedNotFound = unexpected |> List.filter (results.Contains >> not) + + let unexpectedFound = unexpected |> List.filter (unexpectedNotFound.Contains >> not) + + // If either of these are true, then the test fails. + let hasExpectedNotFound = not (List.isEmpty expectedNotFound) + let hasUnexpectedFound = not (List.isEmpty unexpectedFound) + + if hasExpectedNotFound || hasUnexpectedFound then + let expectedNotFoundMsg = + if hasExpectedNotFound then + sprintf "\nExpected completions not found:%s\n" (formatCompletions expectedNotFound) + else + String.Empty + + let unexpectedFoundMsg = + if hasUnexpectedFound then + sprintf "\nUnexpected completions found:%s\n" (formatCompletions unexpectedFound) + else + String.Empty + + let completionsMsg = sprintf "\nin Completions:%s" (formatCompletions results) + + let msg = sprintf "%s%s%s" expectedNotFoundMsg unexpectedFoundMsg completionsMsg + + Assert.Fail(msg) + + let VerifyCompletionList (fileContents, marker, expected, unexpected) = + VerifyCompletionListWithOptions(fileContents, marker, expected, unexpected, [||]) + + let VerifyCompletionListExactlyWithOptions (fileContents: string, marker: string, expected: string list, opts) = + let options = projectOptions opts + let caretPosition = fileContents.IndexOf(marker) + marker.Length + + let document, _ = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) + + let actual = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + |> Async.RunSynchronously + |> Option.defaultValue (ResizeArray()) + |> Seq.toList + // sort items as Roslyn do - by `SortText` + |> List.sortBy (fun x -> x.SortText) + + let actualNames = actual |> List.map (fun x -> x.DisplayText) + + if actualNames <> expected then + Assert.Fail( + sprintf + "Expected:\n%s,\nbut was:\n%s\nactual with sort text:\n%s" + (String.Join("; ", expected |> List.map (sprintf "\"%s\""))) + (String.Join("; ", actualNames |> List.map (sprintf "\"%s\""))) + (String.Join("\n", actual |> List.map (fun x -> sprintf "%s => %s" x.DisplayText x.SortText))) + ) + + let VerifyCompletionListExactly (fileContents: string, marker: string, expected: string list) = + VerifyCompletionListExactlyWithOptions(fileContents, marker, expected, [||]) + + let VerifyNoCompletionList (fileContents: string, marker: string) = + VerifyCompletionListExactly(fileContents, marker, []) + + let VerifyCompletionListSpan (fileContents: string, marker: string, expected: string) = + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let sourceText = SourceText.From(fileContents) + + let resultSpan = + CompletionUtils.getDefaultCompletionListSpan (sourceText, caretPosition, documentId, filePath, []) + + Assert.AreEqual(expected, sourceText.ToString(resultSpan)) + + [] + let ShouldTriggerCompletionAtCorrectMarkers () = + let testCases = + [ + ("x", true) + ("y", true) + ("1", false) + ("2", false) + ("x +", false) + ("Console.Write", false) + ("System.", true) + ("Console.", true) + ] + + for (marker, shouldBeTriggered) in testCases do + let fileContents = + """ +let x = 1 +let y = 2 +System.Console.WriteLine(x + y) +""" + + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.AreEqual( + shouldBeTriggered, + triggered, + "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result" + ) + + [] + let ShouldNotTriggerCompletionAfterAnyTriggerOtherThanInsertionOrDeletion () = + for triggerKind in [ CompletionTriggerKind.Invoke; CompletionTriggerKind.Snippets ] do + let fileContents = "System.Console.WriteLine(123)" + let caretPosition = fileContents.IndexOf("rite") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + triggerKind, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + + [] + let ShouldNotTriggerCompletionInStringLiterals () = + let fileContents = "let literal = \"System.Console.WriteLine()\"" + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + + [] + let ShouldNotTriggerCompletionInComments () = + let fileContents = + """ +(* +This is a comment +System.Console.WriteLine() +*) +""" + + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + + [] + let ShouldTriggerCompletionInInterpolatedString () = + let fileContents = + """ + +let x = 1 +let y = 2 +let z = $"abc {System.Console.WriteLine(x + y)} def" +""" + + let testCases = + [ + ("x", true) + ("y", true) + ("1", false) + ("2", false) + ("x +", false) + ("Console.Write", false) + ("System.", true) + ("Console.", true) + ] + + for (marker, shouldBeTriggered) in testCases do + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.AreEqual( + shouldBeTriggered, + triggered, + sprintf "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result for marker '%s'" marker + ) + + [] + let ShouldNotTriggerCompletionInExcludedCode () = + let fileContents = + """ +#if UNDEFINED +System.Console.WriteLine() +#endif +""" + + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") + + [] + let ShouldNotTriggerCompletionInOperatorWithDot () = + // Simulate mistyping '|>' as '|.' + let fileContents = + """ +let f() = + 12.0 |. sqrt +""" + + let caretPosition = fileContents.IndexOf("|.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger on operators") + + [] + let ShouldTriggerCompletionInAttribute () = + let fileContents = + """ +[] + let ShouldTriggerCompletionAfterDerefOperator () = + let fileContents = + """ +let foo = ref 12 +printfn "%d" !f +""" + + let marker = "!f" + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows a dereference operator (!).") + + [] + let ShouldTriggerCompletionAfterAddressOfOperator () = + let fileContents = + """ +type Point = { mutable X: int; mutable Y: int } +let pnt = { X = 1; Y = 2 } +use ptr = fixed &p +""" + + let marker = "&p" + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows an addressOf operator (&).") + + [] + let ShouldTriggerCompletionAfterArithmeticOperation () = + let fileContents = + """ +let xVal = 1.0 +let yVal = 2.0 +let zVal + +xVal+y +xVal-y +xVal*y +xVal/y +xVal%y +xVal**y +""" + + let markers = [ "+y"; "-y"; "*y"; "/y"; "%y"; "**y" ] + + for marker in markers do + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows a mathematical operation") + + [] + let ShouldTriggerCompletionAtStartOfFileWithInsertion = + let fileContents = + """ +l""" + + let marker = "l" + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo () = documentId, filePath, [] + + let triggered = + FSharpCompletionProvider.ShouldTriggerCompletionAux( + SourceText.From(fileContents), + caretPosition, + CompletionTriggerKind.Insertion, + getInfo, + IntelliSenseOptions.Default + ) + + Assert.IsTrue( + triggered, + "Completion should trigger after typing an Insertion character at the top of the file, e.g. a function definition in a new script file." + ) + + [] + let ShouldDisplayTypeMembers () = + let fileContents = + """ +type T1() = + member this.M1 = 5 + member this.M2 = "literal" + +[] +let main argv = + let obj = T1() + obj. +""" + + VerifyCompletionList(fileContents, "obj.", [ "M1"; "M2" ], [ "System" ]) + + [] + let ShouldDisplaySystemNamespace () = + let fileContents = + """ +type T1 = + member this.M1 = 5 + member this.M2 = "literal" +System.Console.WriteLine() +""" + + VerifyCompletionList(fileContents, "System.", [ "Console"; "Array"; "String" ], [ "T1"; "M1"; "M2" ]) + + [] + let ShouldDisplaySystemNamespaceInInterpolatedString () = + let fileContents = + """ +type T1 = + member this.M1 = 5 + member this.M2 = "literal" +let x = $"1 not the same as {System.Int32.MaxValue} is it" +""" + + VerifyCompletionList(fileContents, "System.", [ "Console"; "Array"; "String" ], [ "T1"; "M1"; "M2" ]) + + [] + let ``Class instance members are ordered according to their kind and where they are defined (simple case, by a variable)`` () = + let fileContents = + """ +type Base() = + member _.BaseMethod() = 1 + member _.BaseProp = 1 + +type Class() = + inherit Base() + member this.MineMethod() = 1 + member this.MineProp = 1 + +let x = Class() +x. +""" + + let expected = + [ + "MineProp" + "BaseProp" + "MineMethod" + "BaseMethod" + "Equals" + "GetHashCode" + "GetType" + "ToString" + ] + + VerifyCompletionListExactly(fileContents, "x.", expected) + + [] + let ``Class instance members are ordered according to their kind and where they are defined (simple case, by a constructor)`` () = + let fileContents = + """ +type Base() = + member _.BaseMethod() = 1 + member _.BaseProp = 1 + +type Class() = + inherit Base() + member this.MineMethod() = 1 + member this.MineProp = 1 + +let x = Class(). +""" + + let expected = + [ + "MineProp" + "BaseProp" + "MineMethod" + "BaseMethod" + "Equals" + "GetHashCode" + "GetType" + "ToString" + ] + + VerifyCompletionListExactly(fileContents, "let x = Class().", expected) + + [] + let ``Class static members are ordered according to their kind and where they are defined`` () = + let fileContents = + """ +type Base() = + static member BaseStaticMethod() = 1 + static member BaseStaticProp = 1 + +type Class() = + inherit Base() + static member MineStaticMethod() = 1 + static member MineStaticProp = 2 + +Class. +""" + + let expected = + [ "MineStaticProp"; "BaseStaticProp"; "MineStaticMethod"; "BaseStaticMethod" ] + + VerifyCompletionListExactly(fileContents, "Class.", expected) + + [] + let ``Class instance members are ordered according to their kind and where they are defined (complex case)`` () = + let fileContents = + """ +type Base() = + inherit System.Collections.Generic.List + member _.BaseMethod() = 1 + member _.BaseProp = 1 + +type Class() = + inherit Base() + member this.MineMethod() = 1 + member this.MineProp = 1 + +let x = Class() +x. +""" + + let expected = + [ + "MineProp" + "BaseProp" + "Capacity" + "Count" + "Item" + "MineMethod" + "Add" + "AddRange" + "AsReadOnly" + "BaseMethod" + "BinarySearch" + "Clear" + "Contains" + "ConvertAll" + "CopyTo" + "Equals" + "Exists" + "Find" + "FindAll" + "FindIndex" + "FindLast" + "FindLastIndex" + "ForEach" + "GetEnumerator" + "GetHashCode" + "GetRange" + "GetType" + "IndexOf" + "Insert" + "InsertRange" + "LastIndexOf" + "Remove" + "RemoveAll" + "RemoveAt" + "RemoveRange" + "Reverse" + "Sort" + "ToArray" + "ToString" + "TrimExcess" + "TrueForAll" + ] + + VerifyCompletionListExactly(fileContents, "x.", expected) + + [] + let ``Constructing a new class with object initializer syntax`` () = + let fileContents = + """ +type A() = + member val SettableProperty = 1 with get, set + member val AnotherSettableProperty = 1 with get, set + member val NonSettableProperty = 1 + +let _ = new A(Setta) +""" + + let expected = [ "SettableProperty"; "AnotherSettableProperty" ] + let notExpected = [ "NonSettableProperty" ] + VerifyCompletionList(fileContents, "(Setta", expected, notExpected) + + [] + let ``Constructing a new class with object initializer syntax and verifying 'at' character doesn't exist.`` () = + let fileContents = + """ +type A() = + member val SettableProperty = 1 with get, set + member val AnotherSettableProperty = 1 with get, set + member val NonSettableProperty = 1 + +let _ = new A(Setta) +""" + + let expected = [] + + let notExpected = + [ "SettableProperty@"; "AnotherSettableProperty@"; "NonSettableProperty@" ] + + VerifyCompletionList(fileContents, "(Setta", expected, notExpected) + + [] + let ``Constructing a new fully qualified class with object initializer syntax without ending paren`` () = + let fileContents = + """ +module M = + type A() = + member val SettableProperty = 1 with get, set + member val AnotherSettableProperty = 1 with get, set + member val NonSettableProperty = 1 + +let _ = new M.A(Setta +""" + + let expected = [ "SettableProperty"; "AnotherSettableProperty" ] + let notExpected = [ "NonSettableProperty" ] + VerifyCompletionList(fileContents, "(Setta", expected, notExpected) + + [] + let ``Extension methods go after everything else, extension properties are treated as normal ones`` () = + let fileContents = + """ +open System.Collections.Generic + +type List<'a> with + member _.ExtensionProp = 1 + member _.ExtensionMeth() = 1 + +List(). +""" + + let expected = + [ + "Capacity" + "Count" + "Item" + "ExtensionProp" + "Add" + "AddRange" + "AsReadOnly" + "BinarySearch" + "Clear" + "Contains" + "ConvertAll" + "CopyTo" + "Exists" + "Find" + "FindAll" + "FindIndex" + "FindLast" + "FindLastIndex" + "ForEach" + "GetEnumerator" + "GetRange" + "IndexOf" + "Insert" + "InsertRange" + "LastIndexOf" + "Remove" + "RemoveAll" + "RemoveAt" + "RemoveRange" + "Reverse" + "Sort" + "ToArray" + "TrimExcess" + "TrueForAll" + "Equals" + "GetHashCode" + "GetType" + "ToString" + "ExtensionMeth" + ] + + VerifyCompletionListExactly(fileContents, "List().", expected) + + [] + let ``Completion for open contains namespaces and static types`` () = + let fileContents = + """ +open type System.Ma +""" + + let expected = [ "Management"; "Math" ] // both namespace and static type + VerifyCompletionList(fileContents, "System.Ma", expected, []) + + [] + let ``No completion on type name at declaration site`` () = + let fileContents = + """ +type T + +""" + + VerifyNoCompletionList(fileContents, "type T") + + [] + let ``No completion on name of unfinished function declaration`` () = + let fileContents = + """ +let f + +""" + + VerifyNoCompletionList(fileContents, "let f") + + [] + let ``No completion on name of value declaration`` () = + let fileContents = + """ +let xyz = 1 + +""" + + VerifyNoCompletionList(fileContents, "let xy") + + [] + let ``No completion on name of function declaration`` () = + let fileContents = + """ +let foo x = 1 + +""" + + VerifyNoCompletionList(fileContents, "let fo") + + [] + let ``No completion on name of tupled function declaration`` () = + let fileContents = + """ +let foo (x, y) = 1 + +""" + + VerifyNoCompletionList(fileContents, "let fo") + + [] + let ``No completion on member name at declaration site`` () = + let fileContents = + """ +type T() = + member this.M +""" + + VerifyNoCompletionList(fileContents, "member this.M") + + [] + let ``No completion on function first argument name`` () = + let fileContents = + """ +let func (p +""" + + VerifyNoCompletionList(fileContents, "let func (p") + + [] + let ``No completion on function subsequent argument name`` () = + let fileContents = + """ +let func (p, h +""" + + VerifyNoCompletionList(fileContents, "let func (p, h") + + [] + let ``No completion on curried function subsequent argument name`` () = + let fileContents = + """ +let func (p) (h +""" + + VerifyNoCompletionList(fileContents, "let func (p) (h") + + [] + let ``No completion on method first argument name`` () = + let fileContents = + """ +type T() = + member this.M(p) = () +""" + + VerifyNoCompletionList(fileContents, "member this.M(p") + + [] + let ``No completion on method subsequent argument name`` () = + let fileContents = + """ +type T() = + member this.M(p:int, h ) = () +""" + + VerifyNoCompletionList(fileContents, "member this.M(p:int, h") + + [] + let ``Completion list on abstract member type signature contains modules and types but not keywords or functions`` = + let fileContents = + """ +type Interface = + abstract member Eat: l +""" + + VerifyCompletionList(fileContents, "Eat: l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``Provide completion on first function argument type hint`` () = + let fileContents = + """ +let func (p:i +""" + + VerifyCompletionList(fileContents, "let func (p:i", [ "int" ], []) + + [] + let ``Provide completion on subsequent function argument type hint`` () = + let fileContents = + """ +let func (p:int, h:f +""" + + VerifyCompletionList(fileContents, "let func (p:int, h:f", [ "float" ], []) + + [] + let ``Provide completion on local function argument type hint`` () = + let fileContents = + """ +let top () = + let func (p:i +""" + + VerifyCompletionList(fileContents, "let func (p:i", [ "int" ], []) + + [] + let ``No completion on implicit constructor first argument name`` () = + let fileContents = + """ +type T(p) = +""" + + VerifyNoCompletionList(fileContents, "type T(p") + + [] + let ``No completion on implicit constructor subsequent argument name`` () = + let fileContents = + """ +type T(p:int, h) = +""" + + VerifyNoCompletionList(fileContents, "type T(p:int, h") + + [] + let ``Provide completion on implicit constructor argument type hint`` () = + let fileContents = + """ +type T(p:i) = +""" + + VerifyCompletionList(fileContents, "type T(p:i", [ "int" ], []) + + [] + let ``No completion on lambda argument name`` () = + let fileContents = + """ +let _ = fun (p) -> () +""" + + VerifyNoCompletionList(fileContents, "let _ = fun (p") + + [] + let ``No completion on lambda argument name2`` () = + let fileContents = + """ +let _ = fun (p: int) -> () +""" + + VerifyNoCompletionList(fileContents, "let _ = fun (p") + + [] + let ``Completions on lambda argument type hint contain modules and types but not keywords or functions`` () = + let fileContents = + """ +let _ = fun (p:l) -> () +""" + + VerifyCompletionList(fileContents, "let _ = fun (p:l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``Completions in match clause type test contain modules and types but not keywords or functions`` () = + let fileContents = + """ +match box 5 with +| :? l as x -> () +| _ -> () +""" + + VerifyCompletionList(fileContents, ":? l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``Completions in catch clause type test contain modules and types but not keywords or functions`` () = + let fileContents = + """ +try + () +with :? l as x -> + () +""" + + VerifyCompletionList(fileContents, ":? l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``Extensions.Bug5162`` () = + let fileContents = + """ +module Extensions = + type System.Object with + member x.P = 1 +module M2 = + let x = 1 + Ext +""" + + VerifyCompletionList(fileContents, " Ext", [ "Extensions"; "ExtraTopLevelOperators" ], []) + + [] + let ``Custom operations should be at the top of completion list inside computation expression`` () = + let fileContents = + """ +let joinLocal = 1 + +let _ = + query { + for i in 1..10 do + select i + join + } +""" + + VerifyCompletionList(fileContents, " join", [ "groupJoin"; "join"; "leftOuterJoin"; "joinLocal" ], []) + + [] + let ``Byref Extension Methods`` () = + let fileContents = + """ +module Extensions = + open System + open System.Runtime.CompilerServices + + [] + type Message = Message of String + + [] + type MessageExtensions private () = + let (|Message|) (Message message) = message + + [] + static member Print (Message message : Message) = + printfn "%s" message + + [] + static member PrintRef (Message message : inref) = + printfn "%s" message + + let wrappedMessage = Message "Hello World" + + wrappedMessage. +""" + + VerifyCompletionList(fileContents, "wrappedMessage.", [ "PrintRef" ], []) + + [] + let ``Completion list span works with underscore in identifier`` () = + let fileContents = + """ +let x = A.B_C +""" + + VerifyCompletionListSpan(fileContents, "A.B_C", "B_C") + + [] + let ``Completion list span works with digit in identifier`` () = + let fileContents = + """ +let x = A.B1C +""" + + VerifyCompletionListSpan(fileContents, "A.B1C", "B1C") + + [] + let ``Completion list span works with enclosed backtick identifier`` () = + let fileContents = + """ +let x = A.``B C`` +""" + + VerifyCompletionListSpan(fileContents, "A.``B C``", "``B C``") + + [] + let ``Completion list span works with partial backtick identifier`` () = + let fileContents = + """ +let x = A.``B C +""" + + VerifyCompletionListSpan(fileContents, "A.``B C", "``B C") + + [] + let ``Completion list span works with first of multiple enclosed backtick identifiers`` () = + let fileContents = + """ +let x = A.``B C`` + D.``E F`` +""" + + VerifyCompletionListSpan(fileContents, "A.``B C``", "``B C``") + + [] + let ``Completion list span works with last of multiple enclosed backtick identifiers`` () = + let fileContents = + """ +let x = A.``B C`` + D.``E F`` +""" + + VerifyCompletionListSpan(fileContents, "D.``E F``", "``E F``") + + [] + let ``No completion on record field identifier at declaration site`` () = + let fileContents = + """ +type A = { le: string } +""" + + VerifyNoCompletionList(fileContents, "le") + + [] + let ``Completion list on record field type at declaration site contains modules, types and type parameters but not keywords or functions`` + () + = + let fileContents = + """ +type A<'lType> = { Field: l } +""" + + VerifyCompletionList(fileContents, "Field: l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``No completion on record stub with no fields at declaration site`` () = + let fileContents = + """ +type A = { } +""" + + VerifyNoCompletionList(fileContents, "{ ") + + [] + let ``No completion on record outside of all fields at declaration site`` () = + let fileContents = + """ +type A = { Field: string; } +""" + + VerifyNoCompletionList(fileContents, "; ") + + [] + let ``No completion on union case identifier at declaration site`` () = + let fileContents = + """ +type A = + | C of string +""" + + VerifyNoCompletionList(fileContents, "| C") + + [] + let ``No completion on union case field identifier at declaration site`` () = + let fileContents = + """ +type A = + | Case of blah: int * str: int +""" + + VerifyNoCompletionList(fileContents, "str") + + [] + let ``Completion list on union case type at declaration site contains modules, types and type parameters but not keywords or functions`` + () + = + let fileContents = + """ +type A<'lType> = + | Case of blah: int * str: l +""" + + VerifyCompletionList(fileContents, "str: l", [ "LanguagePrimitives"; "List"; "lType" ], [ "let"; "log" ]) + + [] + let ``Completion list on union case type at declaration site contains modules, types and type parameters but not keywords or functions2`` + () + = + let fileContents = + """ +type A<'lType> = + | Case of l +""" + + VerifyCompletionList(fileContents, "of l", [ "LanguagePrimitives"; "List"; "lType" ], [ "let"; "log" ]) + + [] + let ``Completion list on union case type at declaration site contains type parameter`` () = + let fileContents = + """ +type A<'keyType> = + | Case of key +""" + + VerifyCompletionList(fileContents, "of key", [ "keyType" ], []) + + [] + let ``Completion list on type alias contains modules and types but not keywords or functions`` () = + let fileContents = + """ +type A = l +""" + + VerifyCompletionList(fileContents, "= l", [ "LanguagePrimitives"; "List" ], [ "let"; "log" ]) + + [] + let ``No completion on enum case identifier at declaration site`` () = + let fileContents = + """ +type A = + | C = 0 +""" + + VerifyNoCompletionList(fileContents, "| C") + + [] + let ``Completion list in generic function body contains type parameter`` () = + let fileContents = + """ +let Null<'wrappedType> () = + Unchecked.defaultof +""" + + VerifyCompletionList(fileContents, "defaultof] + let ``Completion list in generic method body contains type parameter`` () = + let fileContents = + """ +type A () = + member _.Null<'wrappedType> () = Unchecked.defaultof +""" + + VerifyCompletionList(fileContents, "defaultof] + let ``Completion list in generic class method body contains type parameter`` () = + let fileContents = + """ +type A<'wrappedType> () = + member _.Null () = Unchecked.defaultof +""" + + VerifyCompletionList(fileContents, "defaultof] + let ``Completion list in type application contains modules, types and type parameters but not keywords or functions`` () = + let fileContents = + """ +let emptyMap<'keyType, 'lValueType> () = + Map.empty<'keyType, l> +""" + + VerifyCompletionList(fileContents, ", l", [ "LanguagePrimitives"; "List"; "lValueType" ], [ "let"; "log" ]) + + [] + let ``Completion list for interface with static abstract method type invocation contains static property with residue`` () = + let fileContents = + """ +type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = + static abstract StaticProperty: 'T + +let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = + 'T.StaticProperty +""" + + VerifyCompletionListWithOptions(fileContents, "'T.Stati", [ "StaticProperty" ], [], [| "/langversion:preview" |]) + + [] + let ``Completion list for interface with static abstract method type invocation contains static property after dot`` () = + let fileContents = + """ +type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = + static abstract StaticProperty: 'T + +let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = + 'T.StaticProperty +""" + + VerifyCompletionListWithOptions(fileContents, "'T.", [ "StaticProperty" ], [], [| "/langversion:preview" |]) + + [] + let ``Completion list for SRTP invocation contains static property with residue`` () = + let fileContents = + """ +let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = + 'T.StaticProperty + +""" + + VerifyCompletionListWithOptions(fileContents, "'T.Stati", [ "StaticProperty" ], [], [| "/langversion:preview" |]) + + [] + let ``Completion list for SRTP invocation contains static property after dot`` () = + let fileContents = + """ +let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = + 'T.StaticProperty + +""" + + VerifyCompletionListWithOptions(fileContents, "'T.", [ "StaticProperty" ], [], [| "/langversion:preview" |]) diff --git a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs similarity index 64% rename from vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs index b438c0b96ff..423fbc6e0b3 100644 --- a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/DocumentDiagnosticAnalyzerTests.fs @@ -1,75 +1,64 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor -open System -open System.Threading +namespace FSharp.Editor.Tests open NUnit.Framework - open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Text - open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.LanguageService - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text +open FSharp.Editor.Tests.Helpers -open UnitTests.TestLib.LanguageService - -[][] -type DocumentDiagnosticAnalyzerTests() = +[] +type DocumentDiagnosticAnalyzerTests() = let filePath = "C:\\test.fs" let startMarker = "(*start*)" let endMarker = "(*end*)" - let projectOptions: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - - let getDiagnostics (fileContents: string) = + + let getDiagnostics (fileContents: string) = async { - let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) - let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) - let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) + let document, _ = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) + + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) - } |> Async.RunSynchronously + } + |> Async.RunSynchronously member private this.VerifyNoErrors(fileContents: string, ?additionalFlags: string[]) = - let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let additionalOptions = match additionalFlags with - | None -> projectOptions - | Some(flags) -> {projectOptions with OtherOptions = Array.append projectOptions.OtherOptions flags} - let errors = getDiagnostics fileContents + if not errors.IsEmpty then Assert.Fail("There should be no errors generated", errors) member private this.VerifyErrorAtMarker(fileContents: string, expectedMarker: string, ?expectedMessage: string) = - let errors = getDiagnostics fileContents |> Seq.filter(fun e -> e.Severity = DiagnosticSeverity.Error) |> Seq.toArray + let errors = + getDiagnostics fileContents + |> Seq.filter (fun e -> e.Severity = DiagnosticSeverity.Error) + |> Seq.toArray + Assert.AreEqual(1, errors.Length, "There should be exactly one error generated") let actualError = errors.[0] + if expectedMessage.IsSome then Assert.AreEqual(expectedMessage.Value, actualError.GetMessage(), "Error messages should match") + Assert.AreEqual(DiagnosticSeverity.Error, actualError.Severity) let expectedStart = fileContents.IndexOf(expectedMarker) Assert.AreEqual(expectedStart, actualError.Location.SourceSpan.Start, "Error start positions should match") let expectedEnd = expectedStart + expectedMarker.Length Assert.AreEqual(expectedEnd, actualError.Location.SourceSpan.End, "Error end positions should match") - member private this.VerifyDiagnosticBetweenMarkers(fileContents: string, expectedMessage: string, expectedSeverity: DiagnosticSeverity) = - let errors = getDiagnostics fileContents |> Seq.filter(fun e -> e.Severity = expectedSeverity) |> Seq.toArray + member private this.VerifyDiagnosticBetweenMarkers + ( + fileContents: string, + expectedMessage: string, + expectedSeverity: DiagnosticSeverity + ) = + let errors = + getDiagnostics fileContents + |> Seq.filter (fun e -> e.Severity = expectedSeverity) + |> Seq.toArray + Assert.AreEqual(1, errors.Length, "There should be exactly one error generated") let actualError = errors.[0] Assert.AreEqual(expectedSeverity, actualError.Severity) @@ -78,127 +67,151 @@ type DocumentDiagnosticAnalyzerTests() = Assert.AreEqual(expectedStart, actualError.Location.SourceSpan.Start, "Error start positions should match") let expectedEnd = fileContents.IndexOf(endMarker) Assert.AreEqual(expectedEnd, actualError.Location.SourceSpan.End, "Error end positions should match") - + member private this.VerifyErrorBetweenMarkers(fileContents: string, expectedMessage: string) = this.VerifyDiagnosticBetweenMarkers(fileContents, expectedMessage, DiagnosticSeverity.Error) - + member private this.VerifyWarningBetweenMarkers(fileContents: string, expectedMessage: string) = this.VerifyDiagnosticBetweenMarkers(fileContents, expectedMessage, DiagnosticSeverity.Warning) - [] - member public this.Error_Expression_IllegalIntegerLiteral() = + member public this.Error_Expression_IllegalIntegerLiteral() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let _ = 1 let a = 0.1(*start*).(*end*)0 """, - expectedMessage = "Missing qualification after '.'") - + expectedMessage = "Missing qualification after '.'" + ) + [] - member public this.Error_Expression_IncompleteDefine() = + member public this.Error_Expression_IncompleteDefine() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let a = (*start*);(*end*) """, - expectedMessage = "Unexpected symbol ';' in binding") - + expectedMessage = "Unexpected symbol ';' in binding" + ) + [] - member public this.Error_Expression_KeywordAsValue() = + member public this.Error_Expression_KeywordAsValue() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let b = (*start*)type(*end*) """, - expectedMessage = "Incomplete structured construct at or before this point in binding") - + expectedMessage = "Incomplete structured construct at or before this point in binding" + ) + [] - member public this.Error_Type_WithoutName() = + member public this.Error_Type_WithoutName() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ type (*start*)=(*end*) """, - expectedMessage = "Unexpected symbol '=' in type name") - + expectedMessage = "Unexpected symbol '=' in type name" + ) + [] - member public this.AbstractClasses_Constructors_PositiveTests_1() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_1() = + this.VerifyNoErrors( + """ [] type C(a : int) = new(a : string) = C(int a) new(b) = match b with Some _ -> C(1) | _ -> C("") - """) + """ + ) [] - member public this.AbstractClasses_Constructors_PositiveTests_2() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_2() = + this.VerifyNoErrors( + """ [] type C(a : int) = new(a : string) = new C(int a) new(b) = match b with Some _ -> new C(1) | _ -> new C("") - """) + """ + ) [] - member public this.AbstractClasses_Constructors_PositiveTests_3() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_3() = + this.VerifyNoErrors( + """ [] type O(o : int) = new() = O(1) - """) + """ + ) [] - member public this.AbstractClasses_Constructors_PositiveTests_4() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_4() = + this.VerifyNoErrors( + """ [] type O(o : int) = new() = O() then printfn "A" - """) + """ + ) [] - member public this.AbstractClasses_Constructors_PositiveTests_5() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_5() = + this.VerifyNoErrors( + """ [] type O(o : int) = new() = new O(1) then printfn "A" - """) + """ + ) [] - member public this.AbstractClasses_Constructors_PositiveTests_6() = - this.VerifyNoErrors(""" + member public this.AbstractClasses_Constructors_PositiveTests_6() = + this.VerifyNoErrors( + """ [] type D() = class end [] type E = inherit D new() = { inherit D(); } - """) - + """ + ) + [] - member public this.AbstractClasses_Constructors_NegativeTests_1() = + member public this.AbstractClasses_Constructors_NegativeTests_1() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ [] type D = val d : D new() = {d = D()} """, - expectedMarker = "D()") - + expectedMarker = "D()" + ) + [] - member public this.AbstractClasses_Constructors_NegativeTests_2() = + member public this.AbstractClasses_Constructors_NegativeTests_2() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ [] type Z () = new(a : int) = Z(10) then ignore(Z()) """, - expectedMarker = "Z()") - + expectedMarker = "Z()" + ) + [] - member public this.AbstractClasses_Constructors_NegativeTests_3() = + member public this.AbstractClasses_Constructors_NegativeTests_3() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ [] type X() = member val V : bool = true @@ -207,116 +220,144 @@ type X() = type Y() = member val M : bool = (new X()).V """, - expectedMarker = "new X()") - + expectedMarker = "new X()" + ) + [] - member public this.Waring_Construct_TypeMatchWithoutAnnotation() = + member public this.Waring_Construct_TypeMatchWithoutAnnotation() = this.VerifyWarningBetweenMarkers( - fileContents = """ + fileContents = + """ let f () = let g1 (x:'a) = x let g2 (y:'a) = ((*start*)y(*end*):string) g1 3, g1 "3", g2 "4" """, - expectedMessage = "This construct causes code to be less generic " + - "than indicated by the type annotations. The " + - "type variable 'a has been constrained to be " + - "type 'string'.") + expectedMessage = + "This construct causes code to be less generic " + + "than indicated by the type annotations. The " + + "type variable 'a has been constrained to be " + + "type 'string'." + ) [] - member public this.Error_Identifer_IllegalFloatPointLiteral() = + member public this.Error_Identifer_IllegalFloatPointLiteral() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let x: float = 1.2(*start*).(*end*)3 """, - expectedMessage = "Missing qualification after '.'") - + expectedMessage = "Missing qualification after '.'" + ) + [] - member public this.Error_TypeCheck_ParseError_Bug67133() = + member public this.Error_TypeCheck_ParseError_Bug67133() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let gDateTime (arr: (*start*)DateTime(*end*)[]) = arr.[0] """, - expectedMessage = "The type 'DateTime' is not defined.") - + expectedMessage = "The type 'DateTime' is not defined." + ) + [] - member public this.Error_CyclicalDeclarationDoesNotCrash() = + member public this.Error_CyclicalDeclarationDoesNotCrash() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ type (*start*)A(*end*) = int * A """, - expectedMessage = "This type definition involves an immediate cyclic reference through an abbreviation") - + expectedMessage = "This type definition involves an immediate cyclic reference through an abbreviation" + ) + [] - member public this.Warning_FlagsAndSettings_TargetOptionsRespected() = + member public this.Warning_FlagsAndSettings_TargetOptionsRespected() = this.VerifyWarningBetweenMarkers( - fileContents = """ + fileContents = + """ [] let fn x = 0 let y = (*start*)fn(*end*) 1 """, - expectedMessage = "This construct is deprecated. x") + expectedMessage = "This construct is deprecated. x" + ) [] member public this.Basic_Case() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let x = 3 let y = (*start*)x(*end*) 4 let arr = [| 1; 2; 3 |] """, - expectedMessage = "This value is not a function and cannot be applied.") + expectedMessage = "This value is not a function and cannot be applied." + ) [] member public this.Multiline_Bug5449() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ let f x = x + 1 let r = (*start*)f 3(*end*) 4 """, - expectedMessage = "This value is not a function and cannot be applied.") + expectedMessage = "This value is not a function and cannot be applied." + ) [] member public this.InComputationExpression_Bug6095_A() = this.VerifyWarningBetweenMarkers( - fileContents = """ + fileContents = + """ let a = async { let! (*start*)[| r1; r2 |](*end*) = Async.Parallel [| async.Return(1); async.Return(2) |] let yyyy = 4 return r1,r2 } """, - expectedMessage = "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s).") + expectedMessage = + "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s)." + ) [] member public this.InComputationExpression_Bug6095_B() = this.VerifyWarningBetweenMarkers( - fileContents = """ + fileContents = + """ let f = (*start*)function(*end*) | [| a;b |] -> () """, - expectedMessage = "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s).") + expectedMessage = + "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s)." + ) [] member public this.InComputationExpression_Bug6095_C() = this.VerifyWarningBetweenMarkers( - fileContents = """ + fileContents = + """ for (*start*)[|a;b|](*end*) in [| [|42|] |] do () """, - expectedMessage = "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s). Unmatched elements will be ignored.") - + expectedMessage = + "Incomplete pattern matches on this expression. For example, the value '[|_; _; _|]' may indicate a case not covered by the pattern(s). Unmatched elements will be ignored." + ) + [] member public this.InComputationExpression_Bug914685() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ async { if true then return 1 } |> ignore """, - expectedMarker = "if true then") + expectedMarker = "if true then" + ) [] member public this.ExtraEndif() = this.VerifyErrorBetweenMarkers( - fileContents = """ + fileContents = + """ #if UNDEFINED 1 #else @@ -324,12 +365,14 @@ async { if true then return 1 } |> ignore #endif (*start*)#endif(*end*) """, - expectedMessage = "#endif has no matching #if in implementation file") - + expectedMessage = "#endif has no matching #if in implementation file" + ) + [] member public this.Squiggles_HashNotFirstSymbol_If() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ (*comment*) #if UNDEFINED 1 #else @@ -337,12 +380,14 @@ async { if true then return 1 } |> ignore #endif """, expectedMarker = " #if UNDEFINED", - expectedMessage = "#if directive must appear as the first non-whitespace character on a line") - + expectedMessage = "#if directive must appear as the first non-whitespace character on a line" + ) + [] member public this.Squiggles_HashNotFirstSymbol_Endif() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ #if DEBUG 1 #else @@ -350,32 +395,38 @@ async { if true then return 1 } |> ignore (*comment*) #endif """, expectedMarker = " #endif", - expectedMessage = "#endif directive must appear as the first non-whitespace character on a line") - + expectedMessage = "#endif directive must appear as the first non-whitespace character on a line" + ) + [] member public this.Squiggles_HashIfWithMultilineComment() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ #if DEBUG (*comment*) #endif """, expectedMarker = "(*comment*)", - expectedMessage = "Expected single line comment or end of line") - + expectedMessage = "Expected single line comment or end of line" + ) + [] member public this.Squiggles_HashIfWithUnexpected() = this.VerifyErrorAtMarker( - fileContents = """ + fileContents = + """ #if DEBUG TEST #endif """, expectedMarker = "TEST", - expectedMessage = "Incomplete preprocessor expression") - + expectedMessage = "Incomplete preprocessor expression" + ) + [] member public this.OverloadsAndExtensionMethodsForGenericTypes() = this.VerifyNoErrors( - fileContents = """ + fileContents = + """ open System.Linq type T = @@ -388,14 +439,17 @@ type T = member this.GetEnumerator() : System.Collections.IEnumerator = failwith "not implemented" let g (t : T) = t.Count() - """) - + """ + ) + [] member public this.DocumentDiagnosticsDontReportProjectErrors_Bug1596() = // https://github.com/dotnet/fsharp/issues/1596 this.VerifyNoErrors( - fileContents = """ + fileContents = + """ let x = 3 printf "%d" x """, - additionalFlags = [| "--times" |]) + additionalFlags = [| "--times" |] + ) diff --git a/vsintegration/tests/FSharp.Editor.Tests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/DocumentHighlightsServiceTests.fs new file mode 100644 index 00000000000..c6749195615 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/DocumentHighlightsServiceTests.fs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open FSharp.Editor.Tests.Helpers + +module DocumentHighlightsServiceTests = + + open System + open NUnit.Framework + open Microsoft.CodeAnalysis + open Microsoft.CodeAnalysis.Text + open Microsoft.VisualStudio.FSharp.Editor + open FSharp.Compiler.CodeAnalysis + open FSharp.Compiler.Text + + let filePath = "C:\\test.fs" + + let internal projectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + UnresolvedReferences = None + OriginalLoadReferences = [] + Stamp = None + } + + let private getSpans (sourceText: SourceText) (caretPosition: int) = + let document = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, sourceText) + + FSharpDocumentHighlightsService.GetDocumentHighlights(document, caretPosition) + |> Async.RunSynchronously + |> Option.defaultValue [||] + + let private span sourceText isDefinition (startLine, startCol) (endLine, endCol) = + let range = + Range.mkRange filePath (Position.mkPos startLine startCol) (Position.mkPos endLine endCol) + + { + IsDefinition = isDefinition + TextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) + } + + [] + let ShouldHighlightAllSimpleLocalSymbolReferences () = + let fileContents = + """ + let foo x = + x + x + let y = foo 2 + """ + + let sourceText = SourceText.From(fileContents) + let caretPosition = fileContents.IndexOf("foo") + 1 + let spans = getSpans sourceText caretPosition + + let expected = + [| span sourceText true (2, 8) (2, 11); span sourceText false (4, 12) (4, 15) |] + + Assert.AreEqual(expected, spans) + + [] + let ShouldHighlightAllQualifiedSymbolReferences () = + let fileContents = + """ + let x = System.DateTime.Now + let y = System.DateTime.MaxValue + """ + + let sourceText = SourceText.From(fileContents) + let caretPosition = fileContents.IndexOf("DateTime") + 1 + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + + let spans = getSpans sourceText caretPosition + + let expected = + [| + span sourceText false (2, 19) (2, 27) + span sourceText false (3, 19) (3, 27) + |] + + Assert.AreEqual(expected, spans) + + let caretPosition = fileContents.IndexOf("Now") + 1 + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let spans = getSpans sourceText caretPosition + let expected = [| span sourceText false (2, 28) (2, 31) |] + + Assert.AreEqual(expected, spans) diff --git a/vsintegration/tests/UnitTests/EditorFormattingServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs similarity index 66% rename from vsintegration/tests/UnitTests/EditorFormattingServiceTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs index 739b2600f6e..abf741b972d 100644 --- a/vsintegration/tests/UnitTests/EditorFormattingServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/EditorFormattingServiceTests.fs @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor -open System +namespace FSharp.Editor.Tests +open System open NUnit.Framework - open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor @@ -12,27 +11,29 @@ open FSharp.Compiler.CodeAnalysis open Microsoft.CodeAnalysis.Formatting [] -[] -type EditorFormattingServiceTests() = +type EditorFormattingServiceTests() = let filePath = "C:\\test.fs" - let projectOptions : FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } + + let projectOptions: FSharpProjectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) let indentStyle = FormattingOptions.IndentStyle.Smart - - let indentTemplate = """ + + let indentTemplate = + """ let foo = [ 15 ]marker1 @@ -52,7 +53,8 @@ let def = )marker4 """ - let pasteTemplate = """ + let pasteTemplate = + """ let foo = printfn "Something here" @@ -63,7 +65,7 @@ marker2 marker3 marker4""" - + [] [] [] @@ -74,10 +76,24 @@ marker4""" Assert.IsTrue(position >= 0, "Precondition failed: unable to find marker in template") let sourceText = SourceText.From(indentTemplate) - let lineNumber = sourceText.Lines |> Seq.findIndex (fun line -> line.Span.Contains position) + + let lineNumber = + sourceText.Lines |> Seq.findIndex (fun line -> line.Span.Contains position) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - - let changesOpt = FSharpEditorFormattingService.GetFormattingChanges(documentId, sourceText, filePath, checker, indentStyle, parsingOptions, position) |> Async.RunSynchronously + + let changesOpt = + FSharpEditorFormattingService.GetFormattingChanges( + documentId, + sourceText, + filePath, + checker, + indentStyle, + parsingOptions, + position + ) + |> Async.RunSynchronously + match changesOpt with | None -> Assert.Fail("Expected a text change, but got None") | Some changes -> @@ -92,11 +108,14 @@ marker4""" let checker = FSharpChecker.Create() let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let clipboard = prefix + """[] + let clipboard = + prefix + + """[] type SomeNameHere () = member _.Test ()""" - let start = """ + let start = + """ let foo = printfn "Something here" @@ -105,7 +124,8 @@ let foo = somethingElseHere """ - let expected = """ + let expected = + """ let foo = printfn "Something here" @@ -115,23 +135,32 @@ let foo = somethingElseHere """ - + let sourceText = SourceText.From(start.Replace("$", clipboard)) let span = TextSpan(start.IndexOf '$', clipboard.Length) let formattingOptions = { FormatOnPaste = enabled } let changesOpt = - FSharpEditorFormattingService.GetPasteChanges(documentId, sourceText, filePath, formattingOptions, 4, parsingOptions, clipboard, span) + FSharpEditorFormattingService.GetPasteChanges( + documentId, + sourceText, + filePath, + formattingOptions, + 4, + parsingOptions, + clipboard, + span + ) |> Async.RunSynchronously |> Option.map List.ofSeq - + if enabled then match changesOpt with | Some changes -> let changedText = sourceText.WithChanges(changes).ToString() Assert.AreEqual(expected, changedText) - | _ -> Assert.Fail (sprintf "Expected text changes, but got %+A" changesOpt) + | _ -> Assert.Fail(sprintf "Expected text changes, but got %+A" changesOpt) else Assert.AreEqual(None, changesOpt, "Expected no changes as FormatOnPaste is disabled") @@ -141,11 +170,14 @@ somethingElseHere let checker = FSharpChecker.Create() let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let clipboard = prefix + """[] + let clipboard = + prefix + + """[] type SomeNameHere () = member _.Test ()""" - let start = """ + let start = + """ $ let foo = @@ -154,7 +186,8 @@ let foo = somethingElseHere """ - let expected = """ + let expected = + """ [] type SomeNameHere () = member _.Test () @@ -164,14 +197,23 @@ let foo = somethingElseHere """ - + let sourceText = SourceText.From(start.Replace("$", clipboard)) let span = TextSpan(start.IndexOf '$', clipboard.Length) let formattingOptions = { FormatOnPaste = true } let changesOpt = - FSharpEditorFormattingService.GetPasteChanges(documentId, sourceText, filePath, formattingOptions, 4, parsingOptions, clipboard, span) + FSharpEditorFormattingService.GetPasteChanges( + documentId, + sourceText, + filePath, + formattingOptions, + 4, + parsingOptions, + clipboard, + span + ) |> Async.RunSynchronously |> Option.map List.ofSeq @@ -179,18 +221,20 @@ somethingElseHere | Some changes -> let changedText = sourceText.WithChanges(changes).ToString() Assert.AreEqual(expected, changedText) - | _ -> Assert.Fail (sprintf "Expected a changes, but got %+A" changesOpt) + | _ -> Assert.Fail(sprintf "Expected a changes, but got %+A" changesOpt) [] member this.TestPasteChanges_PastingWithAutoIndentationInPasteSpan() = let checker = FSharpChecker.Create() let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let clipboard = """[] + let clipboard = + """[] type SomeNameHere () = member _.Test ()""" - let start = """ + let start = + """ let foo = printfn "Something here" @@ -199,7 +243,8 @@ let foo = somethingElseHere """ - let expected = """ + let expected = + """ let foo = printfn "Something here" @@ -209,7 +254,7 @@ let foo = somethingElseHere """ - + let sourceText = SourceText.From(start.Replace("$", clipboard)) // If we're pasting on an empty line which has been automatically indented, @@ -220,7 +265,16 @@ somethingElseHere let formattingOptions = { FormatOnPaste = true } let changesOpt = - FSharpEditorFormattingService.GetPasteChanges(documentId, sourceText, filePath, formattingOptions, 4, parsingOptions, clipboard, span) + FSharpEditorFormattingService.GetPasteChanges( + documentId, + sourceText, + filePath, + formattingOptions, + 4, + parsingOptions, + clipboard, + span + ) |> Async.RunSynchronously |> Option.map List.ofSeq @@ -228,4 +282,4 @@ somethingElseHere | Some changes -> let changedText = sourceText.WithChanges(changes).ToString() Assert.AreEqual(expected, changedText) - | _ -> Assert.Fail (sprintf "Expected a changes, but got %+A" changesOpt) + | _ -> Assert.Fail(sprintf "Expected a changes, but got %+A" changesOpt) diff --git a/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj new file mode 100644 index 00000000000..53a86636a80 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/FSharp.Editor.Tests.fsproj @@ -0,0 +1,50 @@ + + + + net472 + nunit + false + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs new file mode 100644 index 00000000000..b06a009c055 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/FsxCompletionProviderTests.fs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open System.Collections.Generic +open NUnit.Framework +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Editor.Tests.Helpers + +// AppDomain helper +type Worker() = + + let filePath = "C:\\test.fsx" + + let projectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = true + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + member _.VerifyCompletionListExactly(fileContents: string, marker: string, expected: List) = + let caretPosition = fileContents.IndexOf(marker) + marker.Length + + let document = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, SourceText.From(fileContents), options = projectOptions) + + let expected = expected |> Seq.toList + + let actual = + let x = + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) + |> Async.RunSynchronously + + x + |> Option.defaultValue (ResizeArray()) + |> Seq.toList + // sort items as Roslyn do - by `SortText` + |> List.sortBy (fun x -> x.SortText) + + let actualNames = actual |> List.map (fun x -> x.DisplayText) + + if actualNames <> expected then + Assert.Fail( + sprintf + "Expected:\n%s,\nbut was:\n%s\nactual with sort text:\n%s" + (String.Join("; ", expected |> List.map (sprintf "\"%s\""))) + (String.Join("; ", actualNames |> List.map (sprintf "\"%s\""))) + (String.Join("\n", actual |> List.map (fun x -> sprintf "%s => %s" x.DisplayText x.SortText))) + ) + +module FsxCompletionProviderTests = + + let getWorker () = Worker() + + [] +#if RELEASE + [] +#endif + let fsiShouldTriggerCompletionInFsxFile () = + let fileContents = + """ + fsi. + """ + + let expected = + List( + [ + "CommandLineArgs" + "EventLoop" + "FloatingPointFormat" + "FormatProvider" + "PrintDepth" + "PrintLength" + "PrintSize" + "PrintWidth" + "ShowDeclarationValues" + "ShowIEnumerable" + "ShowProperties" + "AddPrinter" + "AddPrintTransformer" + "Equals" + "GetHashCode" + "GetType" + "ToString" + ] + ) + + // We execute in a seperate appdomain so that we can set BaseDirectory to a non-existent location + getWorker().VerifyCompletionListExactly(fileContents, "fsi.", expected) diff --git a/vsintegration/tests/FSharp.Editor.Tests/GoToDefinitionServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/GoToDefinitionServiceTests.fs new file mode 100644 index 00000000000..2386e432c4d --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/GoToDefinitionServiceTests.fs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open System.IO +open NUnit.Framework +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.EditorServices +open FSharp.Compiler.Text +open FSharp.Editor.Tests.Helpers + +[] +module GoToDefinitionServiceTests = + + let userOpName = "GoToDefinitionServiceTests" + + let private findDefinition (document: Document, sourceText: SourceText, position: int, defines: string list) : range option = + maybe { + let textLine = sourceText.Lines.GetLineFromPosition position + let textLinePos = sourceText.Lines.GetLinePosition position + let fcsTextLineNumber = Line.fromZ textLinePos.Line + + let! lexerSymbol = + Tokenizer.getSymbolAtPosition ( + document.Id, + sourceText, + position, + document.FilePath, + defines, + SymbolLookupKind.Greedy, + false, + false + ) + + let _, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync(nameof (userOpName)) + |> Async.RunSynchronously + + let declarations = + checkFileResults.GetDeclarationLocation( + fcsTextLineNumber, + lexerSymbol.Ident.idRange.EndColumn, + textLine.ToString(), + lexerSymbol.FullIsland, + false + ) + + match declarations with + | FindDeclResult.DeclFound range -> return range + | _ -> return! None + } + + let makeOptions filePath args = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = args + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let GoToDefinitionTest (fileContents: string, caretMarker: string, expected, opts) = + + let filePath = Path.GetTempFileName() + ".fs" + File.WriteAllText(filePath, fileContents) + let options = makeOptions filePath opts + + let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker + + let document, sourceText = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) + + let actual = + findDefinition (document, sourceText, caretPosition, []) + |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) + + if actual <> expected then + Assert.Fail( + sprintf + "Incorrect information returned for fileContents=<<<%s>>>, caretMarker=<<<%s>>>, expected =<<<%A>>>, actual = <<<%A>>>" + fileContents + caretMarker + expected + actual + ) + + [] + let ``goto definition smoke test`` () = + + let manyTestCases = + [ + // Test1 + (""" +type TestType() = + member this.Member1(par1: int) = + printf "%d" par1 + member this.Member2(par2: string) = + printf "%s" par2 + +[] +let main argv = + let obj = TestType() + obj.Member1(5) + obj.Member2("test")""", + [ + ("printf \"%d\" par1", Some(3, 3, 24, 28)) + ("printf \"%s\" par2", Some(5, 5, 24, 28)) + ("let obj = TestType", Some(2, 2, 5, 13)) + ("let obj", Some(10, 10, 8, 11)) + ("obj.Member1", Some(3, 3, 16, 23)) + ("obj.Member2", Some(5, 5, 16, 23)) + ]) + // Test2 + (""" +module Module1 = + let foo x = x + +let _ = Module1.foo 1 +""", + [ ("let _ = Module", Some(2, 2, 7, 14)) ]) + ] + + for fileContents, testCases in manyTestCases do + for caretMarker, expected in testCases do + + printfn "Test case: caretMarker=<<<%s>>>" caretMarker + GoToDefinitionTest(fileContents, caretMarker, expected, [||]) + + [] + let ``goto definition for string interpolation`` () = + + let fileContents = + """ +let xxxxx = 1 +let yyyy = $"{abc{xxxxx}def}" """ + + let caretMarker = "xxxxx" + let expected = Some(2, 2, 4, 9) + + GoToDefinitionTest(fileContents, caretMarker, expected, [||]) + + [] + let ``goto definition for static abstract method invocation`` () = + + let fileContents = + """ +type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = + static abstract StaticProperty: 'T + +let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = + 'T.StaticProperty +""" + + let caretMarker = "'T.StaticProperty" + let expected = Some(3, 3, 20, 34) + + GoToDefinitionTest(fileContents, caretMarker, expected, [| "/langversion:preview" |]) diff --git a/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs new file mode 100644 index 00000000000..1c02be655bf --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/HelpContextServiceTests.fs @@ -0,0 +1,436 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open System.Threading +open NUnit.Framework +open Microsoft.CodeAnalysis +open FSharp.Compiler.CodeAnalysis +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.IO +open FSharp.Editor.Tests.Helpers + +[] +type HelpContextServiceTests() = + let PathRelativeToTestAssembly p = + Path.Combine(Path.GetDirectoryName(Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) + + let filePath = "C:\\test.fs" + + let makeOptions args = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = args + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let getMarkers (source: string) = + let mutable cnt = 0 + + [ + for i in 0 .. (source.Length - 1) do + if source.[i] = '$' then + yield (i - cnt) + cnt <- cnt + 1 + ] + + let TestF1KeywordsWithOptions (expectedKeywords: string option list, lines: string list, opts: string[]) = + let options = makeOptions opts + + let fileContentsWithMarkers = String.Join("\r\n", lines) + let fileContents = fileContentsWithMarkers.Replace("$", "") + + let document, sourceText = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) + + let markers = getMarkers fileContentsWithMarkers + + let res = + [ + for marker in markers do + let span = Microsoft.CodeAnalysis.Text.TextSpan(marker, 0) + let textLine = sourceText.Lines.GetLineFromPosition(marker) + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + + let classifiedSpans = + Tokenizer.getClassifiedSpans (documentId, sourceText, textLine.Span, Some "test.fs", [], CancellationToken.None) + + FSharpHelpContextService.GetHelpTerm(document, span, classifiedSpans) + |> Async.RunSynchronously + ] + + let equalLength = (expectedKeywords.Length = res.Length) + Assert.True(equalLength) + + for (exp, res) in List.zip expectedKeywords res do + Assert.AreEqual(exp, res) + + let TestF1Keywords (expectedKeywords, lines) = + TestF1KeywordsWithOptions(expectedKeywords, lines, [||]) + + [] +#if RELEASE + [] +#endif + member _.``F1 help keyword NoKeyword.Negative``() = + let file = + [ + "let s = \"System.Con$sole\"" + "let n = 999$99" + "#if UNDEFINED" + " let w = List.re$v []" + "#endif" + ] + + let keywords = [ None; None; None ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Preprocessor``() = + let file = [ "#i$f foobaz"; "#e$ndif" ] + let keywords = [ Some "#if_FS"; Some "#endif_FS" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Regression.DotNetMethod.854364``() = + let file = [ "let i : int = 42"; "i.ToStri$ng()"; "i.ToStri$ng(\"format\")" ] + let keywords = [ Some "System.Int32.ToString"; Some "System.Int32.ToString" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Namespaces``() = + let file = + [ + "open Syst$em.N$et" + "open System.I$O" + "open Microsoft.FSharp.Core" + "" + "System.Cons$ole.WriteLine()" + ] + + let keywords = + [ Some "System"; Some "System.Net"; Some "System.IO"; Some "System.Console" ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Namespaces.BeforeDot``() = + let file = + [ + "open System$.Net$" + "open System$.IO" + "open System$.Collections$.Generic$" + "open Microsoft.FSharp.Core" + "" + "System$.Console$.WriteLine()" + ] + + let keywords = + [ + Some "System" + Some "System.Net" + Some "System" + Some "System" + Some "System.Collections" + Some "System.Collections.Generic" + Some "System" + Some "System.Console" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Namespaces.AfterDot``() = + let file = + [ + "open $System.$Net" + "open $System.IO" + "open $System.$Collections.$Generic" + "open Microsoft.FSharp.Core" + "" + "$System.$Console.$WriteLine()" + ] + + let keywords = + [ + Some "System" + Some "System.Net" + Some "System" + Some "System" + Some "System.Collections" + Some "System.Collections.Generic" + Some "System" + Some "System.Console" + Some "System.Console.WriteLine" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword QuotedIdentifiers``() = + let file = + [ + "let `$`escaped func`` x y = x + y" + "let ``escaped value`$` = 1" + "let x = 1" + "``escaped func`` x$ ``escaped value``" + "``escaped func``$ x ``escaped value``" + "``escaped func`` x $``escaped value``" + "let ``z$`` = 1" + "``$z`` |> printfn \"%d\"" + ] + + let keywords = + [ + Some "Test.escaped func" + Some "Test.escaped value" + Some "Test.x" + Some "Test.escaped func" + Some "Test.escaped value" + Some "Test.z" + Some "Test.z" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Attributes``() = + let file = + [ + "open System.Runtime.InteropServices" + "open System.Runtime.CompilerServices" + "[]" + "type X = " + " []" + " val mutable f : int" + " []" + " member _.Run() = ()" + "[]" + "type Y = class end" + ] + + let keywords = + [ + Some "Microsoft.FSharp.Core.StructAttribute.#ctor" + Some "Microsoft.FSharp.Core.DefaultValueAttribute.#ctor" + Some "System.Runtime.CompilerServices.MethodImplAttribute.#ctor" + Some "System.Runtime.InteropServices.StructLayoutAttribute.Size" + ] + + TestF1Keywords(keywords, file) + + [] +#if RELEASE + [] +#endif + //This test case Verify that when F1 is Hit on TypeProvider namespaces it contain the right keyword + member _.``F1 help keyword TypeProvider.Namespaces``() = + let file = [ "open N$1" ] + let keywords = [ Some "N1" ] + + TestF1KeywordsWithOptions( + keywords, + file, + [| + "-r:" + + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") + |] + ) + + [] +#if RELEASE + [] +#endif + //This test case Verify that when F1 is Hit on TypeProvider Type it contain the right keyword + member _.``F1 help keyword TypeProvider.type``() = + + let file = + [ + //Dummy Type Provider exposes a parametric type (N1.T) that takes 2 static params (string * int) + """let foo = typeof>""" + ] + + let keywords = [ Some "N1.T" ] + + TestF1KeywordsWithOptions( + keywords, + file, + [| + "-r:" + + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") + |] + ) + + [] + member _.``F1 help keyword EndOfLine``() = + let file = [ "open System.Net$"; "open System.IO$" ] + let keywords = [ Some "System.Net"; Some "System.IO" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword EndOfLine2``() = + let file = [ "module M"; "open System.Net$"; "open System.IO$" ] + let keywords = [ Some "System.Net"; Some "System.IO" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Comments``() = + let file = [ "($* co$mment *$)"; "/$/ com$ment" ] + + let keywords = + [ + Some "comment_FS" + Some "comment_FS" + Some "comment_FS" + Some "comment_FS" + Some "comment_FS" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword FSharpEntities``() = + let file = + [ + "let (KeyValu$e(k,v)) = null" + "let w : int lis$t = []" + "let b = w.IsEm$pty" + "let m : map = Map.empty" + "m.A$dd(1,1)" + "let z = Li$st.r$ev w" + "let o = No$ne" + "let o1 = So$me 1" + "let c : System.IO.Str$eam = null" + "c.Async$Read(10)" + "let r = r$ef 0" + "r.conten$ts" + ] + + let keywords = + [ + Some "Microsoft.FSharp.Core.Operators.KeyValuePattern``2" + Some "Microsoft.FSharp.Collections.FSharpList`1" + Some "Microsoft.FSharp.Collections.FSharpList`1.IsEmpty" + Some "Microsoft.FSharp.Collections.FSharpMap`2.Add" + Some "Microsoft.FSharp.Collections.ListModule" + Some "Microsoft.FSharp.Collections.ListModule.Reverse``1" // per F1 keyword spec - one tick for classes, two ticks for members + Some "Microsoft.FSharp.Core.FSharpOption`1.None" + Some "Microsoft.FSharp.Core.FSharpOption`1.Some" + Some "System.IO.Stream" + Some "Microsoft.FSharp.Control.CommonExtensions.AsyncReadBytes" + Some "Microsoft.FSharp.Core.Operators.Ref``1" + Some "Microsoft.FSharp.Core.FSharpRef`1.contents" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Keywords``() = + let file = + [ + "l$et r = ref 0" + "r :$= 1" + "let mut$able foo = 1" + "foo <$- 2" + "let$ z = 1" + ] + + let keywords = + [ Some "let_FS"; Some ":=_FS"; Some "mutable_FS"; Some "<-_FS"; Some "let_FS" ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Regression.NewInstance.854367``() = + let file = [ "let q : System.Runtime.Remoting.TypeE$ntry = null" ] + let keywords = [ Some "System.Runtime.Remoting.TypeEntry" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Regression.NewInstance.854367.2``() = + let file = + [ + "let q1 = new System.Runtime.Remoting.Type$Entry()" // this consutrctor exists but is not accessible (it is protected), but the help entry still goes to the type + ] + + let keywords = [ Some "System.Runtime.Remoting.TypeEntry" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Classes.WebClient``() = + let file = [ "let w : System.Net.Web$Client = new System.Net.Web$Client()" ] + let keywords = [ Some "System.Net.WebClient"; Some "System.Net.WebClient.#ctor" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Classes.Object``() = + let file = [ "let w : System.Ob$ject = new System.Obj$ect()" ] + let keywords = [ Some "System.Object"; Some "System.Object.#ctor" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Classes.Generic``() = + let file = [ "let x : System.Collections.Generic.L$ist = null" ] + let keywords = [ Some "System.Collections.Generic.List`1" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Classes.Abbrev``() = + let file = [ "let z : Resi$zeArray = null" ] + let keywords = [ Some "System.Collections.Generic.List`1" ] + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword Members``() = + let file = + [ + "open System.Linq" + "open System" + "let l = new ResizeArray()" + "let i = l.Cou$nt" + "l.Ad$d(1)" + "let m = new System.IO.MemoryStream()" + "m.BeginRe$ad()" + "l.Se$lect(fun i -> i + 1)" + "let d = new System.DateTi$me()" + "let s = String.Empty" + "s.Equ$als(null)" + "let i = 12" + "i.ToStr$ing()" + ] + + let keywords = + [ + Some "System.Collections.Generic.List`1.Count" + Some "System.Collections.Generic.List`1.Add" + Some "System.IO.Stream.BeginRead" + Some "System.Linq.Enumerable.Select``2" // per F1 keyword spec - one tick for classes, two ticks for members + Some "System.DateTime.#ctor" + Some "System.String.Equals" + Some "System.Int32.ToString" + ] + + TestF1Keywords(keywords, file) + + [] + member _.``F1 help keyword static abstract interface method``() = + let file = + [ + "type IStaticProperty<'T when 'T :> IStaticProperty<'T>> =" + " static abstract StaticProperty: 'T" + "" + "let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) =" + " 'T.StaticProp$erty" + ] + + let keywords = [ Some "Test.IStaticProperty`1.StaticProperty" ] + TestF1Keywords(keywords, file) diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/AssemblyResolver.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/AssemblyResolver.fs new file mode 100644 index 00000000000..f41f10fa138 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/AssemblyResolver.fs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.Helpers + +open System +open System.IO +open System.Reflection + +module AssemblyResolver = + open System.Globalization + + let vsInstallDir = + // use the environment variable to find the VS installdir + let vsvar = + let var = Environment.GetEnvironmentVariable("VS170COMNTOOLS") + + if String.IsNullOrEmpty var then + Environment.GetEnvironmentVariable("VSAPPIDDIR") + else + var + + if String.IsNullOrEmpty vsvar then + failwith "VS170COMNTOOLS and VSAPPIDDIR environment variables not found." + + Path.Combine(vsvar, "..") + + let probingPaths = + [| + Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\Editor") + Path.Combine(vsInstallDir, @"IDE\PublicAssemblies") + Path.Combine(vsInstallDir, @"IDE\PrivateAssemblies") + Path.Combine(vsInstallDir, @"IDE\CommonExtensions\Microsoft\ManagedLanguages\VBCSharp\LanguageServices") + Path.Combine(vsInstallDir, @"IDE\Extensions\Microsoft\CodeSense\Framework") + Path.Combine(vsInstallDir, @"IDE") + |] + + let addResolver () = + AppDomain.CurrentDomain.add_AssemblyResolve (fun h args -> + let found () = + (probingPaths) + |> Seq.tryPick (fun p -> + try + let name = AssemblyName(args.Name) + let codebase = Path.GetFullPath(Path.Combine(p, name.Name) + ".dll") + + if File.Exists(codebase) then + name.CodeBase <- codebase + name.CultureInfo <- Unchecked.defaultof + name.Version <- Unchecked.defaultof + Some(name) + else + None + with _ -> + None) + + match found () with + | None -> Unchecked.defaultof + | Some name -> Assembly.Load(name)) diff --git a/vsintegration/tests/UnitTests/ProjectOptionsBuilder.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/ProjectOptionsBuilder.fs similarity index 74% rename from vsintegration/tests/UnitTests/ProjectOptionsBuilder.fs rename to vsintegration/tests/FSharp.Editor.Tests/Helpers/ProjectOptionsBuilder.fs index f9a20e674d6..395493ae148 100644 --- a/vsintegration/tests/UnitTests/ProjectOptionsBuilder.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/ProjectOptionsBuilder.fs @@ -1,4 +1,6 @@ -namespace VisualFSharp.UnitTests.Editor +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.Helpers open System open System.IO @@ -9,14 +11,14 @@ module FileSystemHelpers = let safeDeleteFile (path: string) = try File.Delete(path) - with - | _ -> () + with _ -> + () let safeDeleteDirectory (path: string) = try Directory.Delete(path) - with - | _ -> () + with _ -> + () type FSharpProject = { @@ -26,24 +28,29 @@ type FSharpProject = } /// Strips cursor information from each file and returns the name and cursor position of the last file to specify it. - member this.GetCaretPosition () = + member this.GetCaretPosition() = let caretSentinel = "$$" let mutable cursorInfo: (string * int) = (null, 0) + this.Files |> List.iter (fun (name, contents) -> // find the '$$' sentinel that represents the cursor location let caretPosition = contents.IndexOf(caretSentinel) + if caretPosition >= 0 then - let newContents = contents.Substring(0, caretPosition) + contents.Substring(caretPosition + caretSentinel.Length) + let newContents = + contents.Substring(0, caretPosition) + + contents.Substring(caretPosition + caretSentinel.Length) + File.WriteAllText(Path.Combine(this.Directory, name), newContents) cursorInfo <- (name, caretPosition)) + cursorInfo + interface IDisposable with member this.Dispose() = // delete each source file - this.Files - |> List.map fst - |> List.iter FileSystemHelpers.safeDeleteFile + this.Files |> List.map fst |> List.iter FileSystemHelpers.safeDeleteFile // delete the directory FileSystemHelpers.safeDeleteDirectory (this.Directory) // project file doesn't really exist, nothing to delete @@ -56,11 +63,17 @@ module internal ProjectOptionsBuilder = let private ProjectName = XName.op_Implicit "Project" let private ReferenceName = XName.op_Implicit "Reference" - let private CreateSingleProjectFromMarkup(markup:XElement) = - if markup.Name.LocalName <> "Project" then failwith "Expected root node to be " + let private CreateSingleProjectFromMarkup (markup: XElement) = + if markup.Name.LocalName <> "Project" then + failwith "Expected root node to be " + let projectRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()) - if Directory.Exists(projectRoot) then Directory.Delete(projectRoot, true) + + if Directory.Exists(projectRoot) then + Directory.Delete(projectRoot, true) + Directory.CreateDirectory(projectRoot) |> ignore + let files = // filename -> fileContents markup.Elements(FileName) |> Seq.map (fun file -> @@ -69,6 +82,7 @@ module internal ProjectOptionsBuilder = File.WriteAllText(fileName, fileContents) (fileName, fileContents)) |> List.ofSeq + let options = { ProjectFileName = Path.Combine(projectRoot, markup.Attribute(NameName).Value) @@ -83,14 +97,17 @@ module internal ProjectOptionsBuilder = UnresolvedReferences = None Stamp = None } + { Directory = projectRoot Options = options Files = files } - let private CreateMultipleProjectsFromMarkup(markup:XElement) = - if markup.Name.LocalName <> "Projects" then failwith "Expected root node to be " + let private CreateMultipleProjectsFromMarkup (markup: XElement) = + if markup.Name.LocalName <> "Projects" then + failwith "Expected root node to be " + let projectsAndXml = markup.Elements(ProjectName) |> Seq.map (fun xml -> (CreateSingleProjectFromMarkup xml, xml)) @@ -102,9 +119,10 @@ module internal ProjectOptionsBuilder = let normalizedProjectName = Path.GetFileName(projectOptions.Options.ProjectFileName) (normalizedProjectName, projectOptions)) |> Map.ofList + let projects = projectsAndXml - |> List.map(fun (projectOptions, xml) -> + |> List.map (fun (projectOptions, xml) -> // bind references to their `FSharpProjectOptions` counterpart let referenceList = xml.Elements(ReferenceName) @@ -117,33 +135,34 @@ module internal ProjectOptionsBuilder = let binaryPath = Path.Combine(project.Directory, "bin", asmName + ".dll") (binaryPath, project.Options)) |> Array.ofList - let binaryRefs = - referenceList - |> Array.map fst - |> Array.map (fun r -> "-r:" + r) + + let binaryRefs = referenceList |> Array.map fst |> Array.map (fun r -> "-r:" + r) let otherOptions = Array.append projectOptions.Options.OtherOptions binaryRefs + { projectOptions with - Options = { projectOptions.Options with - ReferencedProjects = referenceList |> Array.map FSharpReferencedProject.CreateFSharp - OtherOptions = otherOptions - } + Options = + { projectOptions.Options with + ReferencedProjects = referenceList |> Array.map FSharpReferencedProject.CreateFSharp + OtherOptions = otherOptions + } }) + let rootProject = List.head projects rootProject - - let CreateProjectFromMarkup(markup:XElement) = + + let CreateProjectFromMarkup (markup: XElement) = match markup.Name.LocalName with | "Project" -> CreateSingleProjectFromMarkup markup | "Projects" -> CreateMultipleProjectsFromMarkup markup | name -> failwith <| sprintf "Unsupported root node name: %s" name - let CreateProject(markup:string) = - XDocument.Parse(markup).Root - |> CreateProjectFromMarkup + let CreateProject (markup: string) = + XDocument.Parse(markup).Root |> CreateProjectFromMarkup - let SingleFileProject(code:string) = + let SingleFileProject (code: string) = code - |> sprintf @" + |> sprintf + @" diff --git a/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs new file mode 100644 index 00000000000..7e2af345e20 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Helpers/RoslynHelpers.fs @@ -0,0 +1,327 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.Helpers + +open System +open System.IO +open System.Reflection +open System.Linq +open System.Collections.Generic +open System.Collections.Immutable +open Microsoft.CodeAnalysis +open Microsoft.VisualStudio.Composition +open Microsoft.CodeAnalysis.Host +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.CodeAnalysis.Host.Mef +open FSharp.Compiler.CodeAnalysis + +[] +module MefHelpers = + + let getAssemblies () = + let self = Assembly.GetExecutingAssembly() + let here = AppContext.BaseDirectory + + let imports = + [| + "Microsoft.CodeAnalysis.Workspaces.dll" + "Microsoft.VisualStudio.Shell.15.0.dll" + "FSharp.Editor.dll" + |] + + let resolvedImports = imports.Select(fun name -> Path.Combine(here, name)).ToList() + + let missingDlls = + resolvedImports.Where(fun path -> not (File.Exists(path))).ToList() + + if (missingDlls.Any()) then + failwith "Missing imports" + + let loadedImports = resolvedImports.Select(fun p -> Assembly.LoadFrom(p)).ToList() + + let result = + loadedImports.ToDictionary(fun k -> Path.GetFileNameWithoutExtension(k.Location)) + + result.Values + |> Seq.append [| self |] + |> Seq.append MefHostServices.DefaultAssemblies + |> Array.ofSeq + + let createExportProvider () = + let resolver = Resolver.DefaultInstance + + let catalog = + let asms = getAssemblies () + + let partDiscovery = + PartDiscovery.Combine( + new AttributedPartDiscoveryV1(resolver), + new AttributedPartDiscovery(resolver, isNonPublicSupported = true) + ) + + let parts = partDiscovery.CreatePartsAsync(asms).Result + let catalog = ComposableCatalog.Create(resolver) + catalog.AddParts(parts) + + let configuration = + CompositionConfiguration.Create(catalog.WithCompositionService()) + + let runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration) + let exportProviderFactory = runtimeComposition.CreateExportProviderFactory() + exportProviderFactory.CreateExportProvider() + +type TestWorkspaceServiceMetadata(serviceType: string, layer: string) = + + member _.ServiceType = serviceType + member _.Layer = layer + + new(data: IDictionary) = + let serviceType = + match data.TryGetValue("ServiceType") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let layer = + match data.TryGetValue("Layer") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + TestWorkspaceServiceMetadata(serviceType, layer) + + new(serviceType: Type, layer: string) = TestWorkspaceServiceMetadata(serviceType.AssemblyQualifiedName, layer) + +type TestLanguageServiceMetadata(language: string, serviceType: string, layer: string, data: IDictionary) = + + member _.Language = language + member _.ServiceType = serviceType + member _.Layer = layer + member _.Data = data + + new(data: IDictionary) = + let language = + match data.TryGetValue("Language") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let serviceType = + match data.TryGetValue("ServiceType") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let layer = + match data.TryGetValue("Layer") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + TestLanguageServiceMetadata(language, serviceType, layer, data) + +type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language: string, exportProvider: ExportProvider) as this = + inherit HostLanguageServices() + + let services1 = + exportProvider.GetExports() + |> Seq.filter (fun x -> x.Metadata.Language = language) + + let factories1 = + exportProvider.GetExports() + |> Seq.filter (fun x -> x.Metadata.Language = language) + |> Seq.map (fun x -> Lazy<_, _>((fun () -> x.Value.CreateLanguageService(this)), x.Metadata)) + + let otherServices1 = Seq.append factories1 services1 + + let otherServicesMap1 = + otherServices1 + |> Seq.map (fun x -> KeyValuePair(x.Metadata.ServiceType, x)) + |> Seq.distinctBy (fun x -> x.Key) + |> System.Collections.Concurrent.ConcurrentDictionary + + override this.WorkspaceServices = workspaceServices + + override this.Language = language + + override this.GetService<'T when 'T :> ILanguageService>() : 'T = + match otherServicesMap1.TryGetValue(typeof<'T>.AssemblyQualifiedName) with + | true, otherService -> otherService.Value :?> 'T + | _ -> + try + exportProvider.GetExport<'T>().Value + with _ -> + Unchecked.defaultof<'T> + +type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) as this = + inherit HostWorkspaceServices() + + let exportProvider = createExportProvider () + + let services1 = + exportProvider.GetExports() + + let factories1 = + exportProvider.GetExports() + |> Seq.map (fun x -> Lazy<_, _>((fun () -> x.Value.CreateService(this)), x.Metadata)) + + let otherServices1 = Seq.append factories1 services1 + + let otherServicesMap1 = + otherServices1 + |> Seq.map (fun x -> KeyValuePair(x.Metadata.ServiceType, x)) + |> Seq.distinctBy (fun x -> x.Key) + |> System.Collections.Concurrent.ConcurrentDictionary + + let langServices = + TestHostLanguageServices(this, LanguageNames.FSharp, exportProvider) + + override _.Workspace = workspace + + override this.GetService<'T when 'T :> IWorkspaceService>() : 'T = + let ty = typeof<'T> + + match otherServicesMap1.TryGetValue(ty.AssemblyQualifiedName) with + | true, otherService -> otherService.Value :?> 'T + | _ -> + try + exportProvider.GetExport<'T>().Value + with _ -> + Unchecked.defaultof<'T> + + override _.FindLanguageServices(filter) = Seq.empty + + override _.GetLanguageServices(languageName) = + match languageName with + | LanguageNames.FSharp -> langServices :> HostLanguageServices + | _ -> raise (NotSupportedException(sprintf "Language '%s' not supported in FSharp VS tests." languageName)) + + override _.HostServices = hostServices + +type TestHostServices() = + inherit HostServices() + + override this.CreateWorkspaceServices(workspace) = + TestHostWorkspaceServices(this, workspace) :> HostWorkspaceServices + +[] +type RoslynTestHelpers private () = + + static member CreateSingleDocumentSolution(filePath, text: SourceText, ?options: FSharpProjectOptions) = + let isScript = + String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) + + let workspace = new AdhocWorkspace(TestHostServices()) + + let projId = ProjectId.CreateNewId() + let docId = DocumentId.CreateNewId(projId) + + let docInfo = + DocumentInfo.Create( + docId, + filePath, + loader = TextLoader.From(text.Container, VersionStamp.Create(DateTime.UtcNow)), + filePath = filePath, + sourceCodeKind = + if isScript then + SourceCodeKind.Script + else + SourceCodeKind.Regular + ) + + let projFilePath = "C:\\test.fsproj" + + let projInfo = + ProjectInfo.Create( + projId, + VersionStamp.Create(DateTime.UtcNow), + projFilePath, + "test.dll", + LanguageNames.FSharp, + documents = [ docInfo ], + filePath = projFilePath + ) + + let solutionInfo = + SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [ projInfo ]) + + let solution = workspace.AddSolution(solutionInfo) + + let workspaceService = workspace.Services.GetService() + + let document = solution.GetProject(projId).GetDocument(docId) + + match options with + | Some options -> + let options = + { options with + ProjectId = Some(Guid.NewGuid().ToString()) + } + + workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions( + projId, + options.SourceFiles, + options.OtherOptions |> ImmutableArray.CreateRange + ) + + document.SetFSharpProjectOptionsForTesting(options) + | _ -> workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, [| filePath |], ImmutableArray.Empty) + + 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/FSharp.Editor.Tests/Hints/HintTestFramework.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs new file mode 100644 index 00000000000..751d089720c --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/HintTestFramework.fs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.Hints + +open System.Threading +open Microsoft.VisualStudio.FSharp.Editor +open Microsoft.VisualStudio.FSharp.Editor.Hints +open Microsoft.CodeAnalysis.Text +open Hints +open FSharp.Editor.Tests.Helpers + +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 hintKinds = + async { + 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 diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs new file mode 100644 index 00000000000..532ce7adfe1 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineParameterNameHintTests.fs @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.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 shown for method parameters`` () = + let code = + """ +let theAnswer = System.Console.WriteLine 42 +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = "value = " + Location = (1, 42) + } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + [] + let ``Hints are shown for parameters of overloaded and curried methods`` () = + let code = + """ +type C () = + member _.Normal (alone: string) = 1 + member _.Normal (what: string, what2: int) = 1 + member _.Curried (curr1: string, curr2: int) (x: int) = 1 + +let c = C () + +let a = c.Curried ("hmm", 2) 1 +let a = c.Normal ("hmm", 2) +let a = c.Normal "hmm" +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = "curr1 = " + Location = (8, 20) + } + { + Content = "curr2 = " + Location = (8, 27) + } + { Content = "x = "; Location = (8, 30) } + { + Content = "what = " + Location = (9, 19) + } + { + Content = "what2 = " + Location = (9, 26) + } + { + Content = "alone = " + Location = (10, 18) + } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + [] + let ``Hints are shown for constructor parameters`` () = + let code = + """ +type C (blahFirst: int) = + new (blah: int, blah2: string) = C blah + +let a = C (1, "") +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = "blahFirst = " + Location = (2, 40) + } + { + Content = "blah = " + Location = (4, 12) + } + { + Content = "blah2 = " + Location = (4, 15) + } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + [] + let ``Hints are shown for discriminated union case fields with explicit names`` () = + let code = + """ +type Shape = + | Square of side: int + | Rectangle of width: int * height: int + +let a = Square 1 +let b = Rectangle (1, 2) +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = "side = " + Location = (5, 16) + } + { + Content = "width = " + Location = (6, 20) + } + { + Content = "height = " + Location = (6, 23) + } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + [] + let ``Hints for discriminated union case fields are not shown when names are generated`` () = + let code = + """ +type Shape = + | Triangle of side1: int * int * side3: int + | Circle of int + +let c = Triangle (1, 2, 3) +let d = Circle 1 +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = "side1 = " + Location = (5, 19) + } + { + Content = "side3 = " + Location = (5, 25) + } + ] + + let actual = getParameterNameHints document + + Assert.AreEqual(expected, actual) + + [] + let ``Hints for discriminated union case fields are not shown when provided arguments don't match the expected count`` () = + let code = + """ +type Shape = + | Triangle of side1: int * side2: int * side3: int + | Circle of int + +let c = Triangle (1, 2) +""" + + let document = getFsDocument code + + let actual = getParameterNameHints document + + Assert.IsEmpty(actual) + + [] + let ``Hints for discriminated union case fields are not shown for Cons`` () = + let code = + """ +type X = + member _.Test() = 42 :: [42; 42] +""" + + let document = getFsDocument code + + let actual = getParameterNameHints document + + Assert.IsEmpty(actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs new file mode 100644 index 00000000000..f2818954ee2 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/InlineTypeHintTests.fs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.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/FSharp.Editor.Tests/Hints/OptionParserTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/OptionParserTests.fs new file mode 100644 index 00000000000..778911047c8 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/OptionParserTests.fs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.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) diff --git a/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs new file mode 100644 index 00000000000..cd9c4fa7b14 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/Hints/OverallHintExperienceTests.fs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests.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" } + +type Shape = + | Square of side: int + | Rectangle of width: int * height: int + +let a = Square 1 +let b = Rectangle (1, 2) + +type C (blahFirst: int) = + member _.Normal (what: string) = 1 + +let a = C 1 +let cc = a.Normal "hmm" +""" + + let document = getFsDocument code + + let expected = + [ + { + Content = ": Song" + Location = (2, 18) + } + { + Content = "song = " + Location = (4, 23) + } + { + Content = ": string" + Location = (4, 11) + } + { + Content = "side = " + Location = (10, 16) + } + { + Content = ": Shape" + Location = (10, 6) + } + { + Content = "width = " + Location = (11, 20) + } + { + Content = "height = " + Location = (11, 23) + } + { + Content = ": Shape" + Location = (11, 6) + } + { + Content = "blahFirst = " + Location = (16, 11) + } + { Content = ": C"; Location = (16, 6) } + { + Content = "what = " + Location = (17, 19) + } + { + Content = ": int" + Location = (17, 7) + } + ] + + let actual = getAllHints document + + CollectionAssert.AreEquivalent(expected, actual) diff --git a/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs new file mode 100644 index 00000000000..6844b27b985 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/IndentationServiceTests.fs @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open NUnit.Framework +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open Microsoft.CodeAnalysis.Formatting + +[] +type IndentationServiceTests() = + let checker = FSharpChecker.Create() + + let filePath = "C:\\test.fs" + + let projectOptions: FSharpProjectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let tabSize = 4 + let indentStyle = FormattingOptions.IndentStyle.Smart + + let indentComment = + System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") + + let consoleProjectTemplate = + " +// Learn more about F# at https://fsharp.org +// See the 'F# Tutorial' project for more help. + +[] +let main argv = + printfn \"%A\" argv + 0 // return an integer exit code" + + let libraryProjectTemplate = + " +namespace ProjectNamespace + +type Class1() = + member this.X = \"F#\"" + + let nestedTypesTemplate = + " +namespace testspace + type testtype + static member testmember = 1 + +" + + let autoIndentTemplate = + " +let plus x y = + x + y // $Indent: 4$ + +let mutable x = 0 +x <- + 10 * 2 // $Indent: 4$ + +match some 10 with +| None -> 0 +| Some x -> + x + 1 // $Indent: 4$ + +try + failwith \"fail\" // $Indent: 4$ +with + | :? System.Exception -> \"error\" + +if 10 > 0 then + true // $Indent: 4$ +else + false // $Indent: 4$ + +( + 1, // $Indent: 4$ + 2 +) + +[ + 1 // $Indent: 4$ + 2 +] + +[| + 1 // $Indent: 4$ + 2 +|] + +[< + Literal // $Indent: 4$ +>] +let constx = 10 + +let t = seq { // $Indent: 0$ + yield 1 // $Indent: 4$ +} + +let g = + function + | None -> 1 // $Indent: 4$ + | Some _ -> 0 + +module MyModule = begin +end // $Indent: 4$ + +type MyType() = class +end // $Indent: 4$ + +type MyStruct = struct +end // $Indent: 4$ + +while true do + printfn \"never end\" // $Indent: 4$ + +// After line has keyword in comment such as function +// should not be indented $Indent: 0$ + + // Even if the line before only had comment like this +// The follwing line should inherit that indentation too $Indent: 4$ +" + + let testCases = + [| + (None, 0, consoleProjectTemplate) + (None, 1, consoleProjectTemplate) + (Some(0), 2, consoleProjectTemplate) + (Some(0), 3, consoleProjectTemplate) + (Some(0), 4, consoleProjectTemplate) + (Some(0), 5, consoleProjectTemplate) + (Some(4), 6, consoleProjectTemplate) + (Some(4), 7, consoleProjectTemplate) + (Some(4), 8, consoleProjectTemplate) + + (None, 0, libraryProjectTemplate) + (None, 1, libraryProjectTemplate) + (Some(0), 2, libraryProjectTemplate) + (Some(0), 3, libraryProjectTemplate) + (Some(4), 4, libraryProjectTemplate) + (Some(4), 5, libraryProjectTemplate) + + (None, 0, nestedTypesTemplate) + (None, 1, nestedTypesTemplate) + (Some(0), 2, nestedTypesTemplate) + (Some(4), 3, nestedTypesTemplate) + (Some(8), 4, nestedTypesTemplate) + (Some(8), 5, nestedTypesTemplate) + |] + + let autoIndentTestCases = + autoIndentTemplate.Split [| '\n' |] + |> Array.map (fun s -> s.Trim()) + |> Array.indexed + |> Array.choose (fun (line, text) -> + let m = indentComment.Match text + + if m.Success then + Some(line, System.Convert.ToInt32 m.Groups.[1].Value) + else + None) + |> Array.map (fun (lineNumber, expectedIndentation) -> (Some(expectedIndentation), lineNumber, autoIndentTemplate)) + + [] + member this.TestIndentation() = + for (expectedIndentation, lineNumber, template) in testCases do + let sourceText = SourceText.From(template) + + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + + let actualIndentation = + FSharpIndentationService.GetDesiredIndentation( + documentId, + sourceText, + filePath, + lineNumber, + tabSize, + indentStyle, + parsingOptions + ) + + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> + Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) + + [] + member this.TestAutoIndentation() = + for (expectedIndentation, lineNumber, template) in autoIndentTestCases do + + let sourceText = SourceText.From(template) + + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions + + let actualIndentation = + FSharpIndentationService.GetDesiredIndentation( + documentId, + sourceText, + filePath, + lineNumber, + tabSize, + indentStyle, + parsingOptions + ) + + match expectedIndentation with + | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) + | Some indentation -> + Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) diff --git a/vsintegration/tests/UnitTests/LanguageDebugInfoServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/LanguageDebugInfoServiceTests.fs similarity index 51% rename from vsintegration/tests/UnitTests/LanguageDebugInfoServiceTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/LanguageDebugInfoServiceTests.fs index 76dc7a54681..92e8e1e890c 100644 --- a/vsintegration/tests/UnitTests/LanguageDebugInfoServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/LanguageDebugInfoServiceTests.fs @@ -1,23 +1,22 @@ - // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests open System open System.Threading - open NUnit.Framework - open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis - open Microsoft.VisualStudio.FSharp.Editor - open FSharp.Compiler.Text -[][] -type LanguageDebugInfoServiceTests() = +[] +type LanguageDebugInfoServiceTests() = let fileName = "C:\\test.fs" let defines = [] - let code = " + + let code = + " // This is a comment [] @@ -36,32 +35,45 @@ let main argv = 0 // return an integer exit code " - static member private testCases: Object[][] = [| - [| "123456"; None |] // Numeric literals are not interesting - [| "is a string"; Some("\"This is a string\"") |] - [| "objectValue"; Some("objectValue") |] - [| "exampleMember"; Some("objectValue.exampleMember") |] - [| "%s"; Some("\"%d %s %A\"") |] - |] + static member private testCases: Object[][] = + [| + [| "123456"; None |] // Numeric literals are not interesting + [| "is a string"; Some("\"This is a string\"") |] + [| "objectValue"; Some("objectValue") |] + [| "exampleMember"; Some("objectValue.exampleMember") |] + [| "%s"; Some("\"%d %s %A\"") |] + |] [] - member this.TestDebugInfo(searchToken: string, expectedDataTip: string option) = + member this.TestDebugInfo(searchToken: string, expectedDataTip: string option) = let searchPosition = code.IndexOf(searchToken) Assert.IsTrue(searchPosition >= 0, "SearchToken '{0}' is not found in code", searchToken) let sourceText = SourceText.From(code) let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let classifiedSpans = Tokenizer.getClassifiedSpans(documentId, sourceText, TextSpan.FromBounds(0, sourceText.Length), Some(fileName), defines, CancellationToken.None) - let actualDataTipSpanOption = FSharpLanguageDebugInfoService.GetDataTipInformation(sourceText, searchPosition, classifiedSpans) - + + let classifiedSpans = + Tokenizer.getClassifiedSpans ( + documentId, + sourceText, + TextSpan.FromBounds(0, sourceText.Length), + Some(fileName), + defines, + CancellationToken.None + ) + + let actualDataTipSpanOption = + FSharpLanguageDebugInfoService.GetDataTipInformation(sourceText, searchPosition, classifiedSpans) + match actualDataTipSpanOption with | None -> Assert.IsTrue(expectedDataTip.IsNone, "LanguageDebugInfoService failed to produce a data tip") - | Some(actualDataTipSpan) -> + | Some (actualDataTipSpan) -> let actualDataTipText = sourceText.GetSubText(actualDataTipSpan).ToString() - Assert.IsTrue(expectedDataTip.IsSome, "LanguageDebugInfoService produced a data tip while it shouldn't at: {0}", actualDataTipText) - Assert.AreEqual(expectedDataTip.Value, actualDataTipText, "Expected and actual data tips should match") + Assert.IsTrue( + expectedDataTip.IsSome, + "LanguageDebugInfoService produced a data tip while it shouldn't at: {0}", + actualDataTipText + ) - - - \ No newline at end of file + Assert.AreEqual(expectedDataTip.Value, actualDataTipText, "Expected and actual data tips should match") diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs new file mode 100644 index 00000000000..0a2c9e4b946 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -0,0 +1,305 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open NUnit.Framework +open FSharp.Compiler.EditorServices +open FSharp.Compiler.CodeAnalysis +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Editor.Tests.Helpers + +[] +type public AssemblyResolverTestFixture() = + + [] + member public __.Init() = AssemblyResolver.addResolver () + +module QuickInfoProviderTests = + + let filePath = "C:\\test.fs" + + let internal projectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = [||] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let private normalizeLineEnds (s: string) = + s.Replace("\r\n", "\n").Replace("\n\n", "\n") + + let private tooltipTextToRawString (ToolTipText elements) : string = + let rec parseElement = + function + | ToolTipElement.None -> "" + | ToolTipElement.Group (xs) -> + let descriptions = xs |> List.map (fun item -> item.MainDescription) + + let descriptionTexts = + descriptions + |> List.map (fun taggedTexts -> taggedTexts |> Array.map (fun taggedText -> taggedText.Text)) + + let descriptionText = descriptionTexts |> Array.concat |> String.concat "" + + let remarks = xs |> List.choose (fun item -> item.Remarks) + + let remarkTexts = + remarks |> Array.concat |> Array.map (fun taggedText -> taggedText.Text) + + let remarkText = + (match remarks with + | [] -> "" + | _ -> "\n" + String.concat "" remarkTexts) + + let tps = xs |> List.collect (fun item -> item.TypeMapping) + + let tpTexts = + tps |> List.map (fun x -> x |> Array.map (fun y -> y.Text) |> String.concat "") + + let tpText = + (match tps with + | [] -> "" + | _ -> "\n" + String.concat "\n" tpTexts) + + descriptionText + remarkText + tpText + | ToolTipElement.CompositionError (error) -> error + + elements |> List.map parseElement |> String.concat "\n" |> normalizeLineEnds + + let executeQuickInfoTest (programText: string) testCases = + let document, _ = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, programText) + + Assert.Multiple(fun _ -> + for (symbol: string, expected: string option) in testCases do + let expected = + expected + |> Option.map normalizeLineEnds + |> Option.map (fun s -> s.Replace("___", "")) + + let caretPosition = programText.IndexOf(symbol) + symbol.Length - 1 + + let quickInfo = + FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) + |> Async.RunSynchronously + + let actual = + quickInfo |> Option.map (fun qi -> tooltipTextToRawString qi.StructuredText) + + Assert.AreEqual(expected, actual, "Symbol: " + symbol)) + + [] + let ShouldShowQuickInfoAtCorrectPositions () = + let fileContents = + """ +let x = 1 +let y = 2 +System.Console.WriteLine(x + y) + """ + + let testCases = + [ + "let", Some "let___Used to associate, or bind, a name to a value or function." + "x", Some "val x: int\nFull name: Test.x" + "y", Some "val y: int\nFull name: Test.y" + "1", None + "2", None + "x +", + Some + """val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) +Full name: Microsoft.FSharp.Core.Operators.(+) +'T1 is int +'T2 is int +'T3 is int""" + "System", Some "namespace System" + "WriteLine", Some "System.Console.WriteLine(value: int) : unit" + ] + + executeQuickInfoTest fileContents testCases + + [] + let ShouldShowQuickKeywordInfoAtCorrectPositionsForSignatureFiles () = + let fileContents = + """ +namespace TestNs +module internal MyModule = + val MyVal: isDecl:bool -> string + """ + + let testCases = + [ + "namespace", + Some + "namespace___Used to associate a name with a group of related types and modules, to logically separate it from other code." + "module", + Some + "module___Used to associate a name with a group of related types, values, and functions, to logically separate it from other code." + "internal", Some "internal___Used to specify that a member is visible inside an assembly but not outside it." + "val", Some "val___Used in a signature to indicate a value, or in a type to declare a member, in limited situations." + "->", + Some + "->___In function types, delimits arguments and return values. Yields an expression (in sequence expressions); equivalent to the yield keyword. Used in match expressions" + ] + + executeQuickInfoTest fileContents testCases + + [] + let ShouldShowQuickKeywordInfoAtCorrectPositionsWithinComputationExpressions () = + let fileContents = + """ +type MyOptionBuilder() = + member __.Zero() = None + member __.Return(x: 'T) = Some x + member __.Bind(m: 'T option, f) = Option.bind f m + +let myOpt = MyOptionBuilder() +let x = + myOpt{ + let! x = Some 5 + let! y = Some 11 + return x + y + } + """ + + let testCases = + [ + "let!", Some "let!___Used in computation expressions to bind a name to the result of another computation expression." + "return", Some "return___Used to provide a value for the result of the containing computation expression." + ] + + executeQuickInfoTest fileContents testCases + + [] + let ShouldShowQuickInfoForGenericParameters () = + let fileContents = + """ + +type C() = + member x.FSharpGenericMethodExplitTypeParams<'T>(a:'T, y:'T) = (a,y) + + member x.FSharpGenericMethodInferredTypeParams(a, y) = (a,y) + +open System.Linq +let coll = [ for i in 1 .. 100 -> (i, string i) ] +let res1 = coll.GroupBy (fun (a, b) -> a) +let res2 = System.Array.Sort [| 1 |] +let test4 x = C().FSharpGenericMethodExplitTypeParams([x], [x]) +let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams([x], [x]) +let test6 = C().FSharpGenericMethodExplitTypeParams(1, 1) +let test7 x = C().FSharpGenericMethodInferredTypeParams([x], [x]) +let test8 = C().FSharpGenericMethodInferredTypeParams(1, 1) +let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams([x], [x]) +let res3 = [1] |> List.map id +let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s + string x) // note there is a type error here, still cehck quickinfo any way +let res5 = 1 + 2 +let res6 = System.DateTime.Now + System.TimeSpan.Zero +let res7 = sin 5.0 +let res8 = abs 5.0 + """ + + let testCases = + + [ + ("GroupBy", + Some + "(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : System.Collections.Generic.IEnumerable> +'TSource is int * string +'TKey is int"); + ("Sort", + Some + "System.Array.Sort<'T>(array: 'T array) : unit +'T is int"); + ("let test4 x = C().FSharpGenericMethodExplitTypeParams", + Some + "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 +'T is 'a list"); + ("let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams", + Some + "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 +'T is 'U list"); + ("let test6 = C().FSharpGenericMethodExplitTypeParams", + Some + "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 +'T is int"); + ("let test7 x = C().FSharpGenericMethodInferredTypeParams", + Some + "member C.FSharpGenericMethodInferredTypeParams: a: 'a1 * y: 'b2 -> 'a1 * 'b2 +'a is 'a0 list +'b is 'a0 list"); + ("let test8 = C().FSharpGenericMethodInferredTypeParams", + Some + "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 +'a is int +'b is int"); + ("let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams", + Some + "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 +'a is 'U list +'b is 'U list"); + ("let res3 = [1] |>", + Some + "val (|>) : arg: 'T1 -> func: ('T1 -> 'U) -> 'U +Full name: Microsoft.FSharp.Core.Operators.(|>) +'T1 is int list +'U is int list"); + ("let res3 = [1] |> List.map id", + Some + "val id: x: 'T -> 'T +Full name: Microsoft.FSharp.Core.Operators.id +'T is int"); + ("let res4 = (1.0,[1]) ||>", + Some + "val (||>) : arg1: 'T1 * arg2: 'T2 -> func: ('T1 -> 'T2 -> 'U) -> 'U +Full name: Microsoft.FSharp.Core.Operators.(||>) +'T1 is float +'T2 is int list +'U is float"); + ("let res4 = (1.0,[1]) ||> List.fold", + Some + "val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State +Full name: Microsoft.FSharp.Collections.List.fold +'T is int +'State is float"); + ("let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s +", + Some + "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) +Full name: Microsoft.FSharp.Core.Operators.(+) +'T1 is string +'T2 is string +'T3 is float"); + ("let res5 = 1 +", + Some + "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) +Full name: Microsoft.FSharp.Core.Operators.(+) +'T1 is int +'T2 is int +'T3 is int"); + ("let res6 = System.DateTime.Now +", + Some + "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) +Full name: Microsoft.FSharp.Core.Operators.(+) +'T1 is System.DateTime +'T2 is System.TimeSpan +'T3 is System.DateTime"); + ("let res7 = sin", + Some + "val sin: value: 'T -> 'T (requires member Sin) +Full name: Microsoft.FSharp.Core.Operators.sin +'T is float"); + ("let res8 = abs", + Some + "val abs: value: 'T -> 'T (requires member Abs) +Full name: Microsoft.FSharp.Core.Operators.abs +'T is int"); + ] + + executeQuickInfoTest fileContents testCases diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs new file mode 100644 index 00000000000..0d6cac3b5a5 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoTests.fs @@ -0,0 +1,638 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System.IO +open Microsoft.VisualStudio.FSharp.Editor +open NUnit.Framework +open FSharp.Editor.Tests.Helpers + +module QuickInfo = + + let internal GetQuickInfo (project: FSharpProject) (fileName: string) (caretPosition: int) = + async { + let code = File.ReadAllText(fileName) + let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code) + return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) + } + |> Async.RunSynchronously + + let GetQuickInfoText (project: FSharpProject) (fileName: string) (caretPosition: int) = + let sigHelp = GetQuickInfo project fileName caretPosition + + match sigHelp with + | Some (quickInfo) -> + let documentationBuilder = + { new IDocumentationBuilder with + override _.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () + override _.AppendDocumentation(_, _, _, _, _, _, _) = () + } + + let mainDescription, docs = + FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo + + let mainTextItems = mainDescription |> Seq.map (fun x -> x.Text) + let docTextItems = docs |> Seq.map (fun x -> x.Text) + System.String.Join(System.String.Empty, (Seq.concat [ mainTextItems; docTextItems ])) + | _ -> "" + + let GetQuickInfoTextFromCode (code: string) = + use project = SingleFileProject code + let fileName, caretPosition = project.GetCaretPosition() + GetQuickInfoText project fileName caretPosition + + let expectedLines (lines: string list) = System.String.Join("\n", lines) + + [] + let ``Automation.EnumDUInterfacefromFSBrowse.InsideComputationExpression`` () = + let code = + """ +namespace FsTest + +type MyColors = + | Red = 0 + | Green = 1 + | Blue = 2 + +module Test = + let test() = + let x = + seq { + for i in 1..10 do + let f = MyColors.Re$$d + yield f + } + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "MyColors.Red: MyColors = 0" + Assert.AreEqual(expected, quickInfo) + + [] + let ``Automation.EnumDUInterfacefromFSBrowse.InsideMatch`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + +module Test = + let test() = + let myDuList = (fun x -> + match x with + | 0 -> MyDistanc$$e.Kilometers + | 1 -> MyDistance.Miles + | _ -> MyDistance.NauticalMiles + ) + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + + let expected = + expectedLines + [ + "type MyDistance =" + " | Kilometers of float" + " | Miles of float" + " | NauticalMiles of float" + "Full name: FsTest.MyDistance" + ] + + Assert.AreEqual(expected, quickInfo) + + [] + let ``Automation.EnumDUInterfacefromFSBrowse.InsideLambda`` () = + let code = + """ +namespace FsTest + +type IMyInterface = + interface + abstract Represent : unit -> string + end + +type MyTestType() = + [] + val mutable field : int + + interface IMyInterface with + member this.Represent () = "Implement Interface" + +module Test = + let test() = + let s = new MyTestType() + |> fun (x:MyTestType) -> x :> IMyInterface + |> fun (x:IMyInterface) -> x.Represen$$t() + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "abstract IMyInterface.Represent: unit -> string" + Assert.AreEqual(expected, quickInfo) + + [] + let ``Automation.RecordAndInterfaceFromFSProj.InsideComputationExpression`` () = + let code = + """ +namespace FsTest + +type MyEmployee = + { mutable Name : string; + mutable Age : int; + mutable IsFTE : bool } + +module Test = + let test() = + let construct = + seq { + for i in 1..10 do + let a = MyEmploye$$e.MakeDummy() + () + } + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + + let expected = + expectedLines + [ + "type MyEmployee =" + " {" + " mutable Name: string" + " mutable Age: int" + " mutable IsFTE: bool" + " }" + "Full name: FsTest.MyEmployee" + ] + + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.RecordAndInterfaceFromFSProj.InsideQuotation`` () = + let code = + """ +namespace FsTest + +type MyEmployee = + { mutable Name : string; + mutable Age : int; + mutable IsFTE : bool } + +module Test = + let test() = + let aa = { Name: "name"; + Age: 1; + IsFTE: false; } + let b = <@ a$$a.Name @> + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val aa: MyEmployee" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.RecordAndInterfaceFromFSProj.InsideLambda`` () = + let code = + """ +namespace FsTest + +type MyEmployee = + { mutable Name : string; + mutable Age : int; + mutable IsFTE : bool } + +module Test = + let test() = + let aa = { Name: "name"; + Age: 1; + IsFTE: false; } + let b = + [ aa ] + |> List.filter (fun e -> e.IsFT$$E) + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "MyEmployee.IsFTE: bool" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.TupleRecordFromFSBrowse.InsideComputationExpression`` () = + let code = + """ +namespace FsTest + +module Test = + let GenerateTuple = + fun x -> + let tuple = (x, x.ToString(), (float)x, (fun y -> (y.ToString(), y + 1))) + tuple + let test() = + let mySeq = + seq { + for i in 1..9 do + let my$$Tuple = GenerateTuple i + yield myTuple + } + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val myTuple: int * string * float * (int -> string * int)" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.TupleRecordFromFSBrowse.SequenceOfMethods`` () = + let code = + """ +namespace FsTest + +module Test = + let GenerateTuple = + fun x -> + let tuple = (x, x.ToString(), (float)x, (fun y -> (y.ToString(), y + 1))) + tuple + let GetTupleMethod tuple = + let (_intInTuple, _stringInTuple, _floatInTuple, methodInTuple) = tuple + methodInTuple + let test() = + let mySeq = + seq { + for i in 1..9 do + let myTuple = GenerateTuple i + yield myTuple + } + let method$$Seq = Seq.map GetTupleMethod mySeq + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val methodSeq: seq<(int -> string * int)>" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.MatchExpression`` () = + let code = + """ +namespace FsTest + +[] +type MyPoint(x:int, y:int) = + member this.X = x + member this.Y = y + +module Test = + let test() = + let p1 = MyPoint(1, 2) + match p$$1 with + | p3 when p3.X = 1 -> 0 + | _ -> 1 +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val p1: MyPoint" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.MatchPattern`` () = + let code = + """ +namespace FsTest + +[] +type MyPoint(x:int, y:int) = + member this.X = x + member this.Y = y + +module Test = + let test() = + let p1 = MyPoint(1, 2) + match p1 with + | p$$3 when p3.X = 1 -> 0 + | _ -> 1 +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val p3: MyPoint" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.UnionIfPredicate`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + +module Test = + let test() = + let dd = MyDistance.Kilometers 1.0 + if MyDistance.toMiles d$$d > 0 then () + else () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val dd: MyDistance" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.UnionForPattern`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + +module Test = + let test() = + let distances = [ MyDistance.Kilometers 1.0; MyDistance.Miles 1.0 ] + for dist$$ance in distances do + () +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "val distance: MyDistance" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.UnionMethodPatternMatch`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + static member toMiles x = + Miles( + match x with + | Miles x -> x + | Kilometers x -> x / 1.6 + | NauticalMiles x -> x * 1.15 + ) + +module Test = + let test() = + let dd = MyDistance.Kilometers 1.0 + match MyDistance.to$$Miles dd with + | Miles x -> 0 + | _ -> 1 +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "static member MyDistance.toMiles: x: MyDistance -> MyDistance" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.UnionMethodPatternMatchBody`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + member this.IncreaseBy dist = + match this with + | Kilometers x -> Kilometers (x + dist) + | Miles x -> Miles (x + dist) + | NauticalMiles x -> NauticalMiles (x + dist) + +module Test = + let test() = + let dd = MyDistance.Kilometers 1.0 + match dd.toMiles() with + | Miles x -> dd.Increase$$By 1.0 + | _ -> dd +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "member MyDistance.IncreaseBy: dist: float -> MyDistance" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.UnionAndStructFromFSProj.UnionPropertyInComputationExpression`` () = + let code = + """ +namespace FsTest + +type MyDistance = + | Kilometers of float + | Miles of float + | NauticalMiles of float + member this.asNautical = + NauticalMiles( + match this with + | Kilometers x -> x / 1.852 + | Miles x -> x / 1.15 + | NauticalMiles x -> x + ) + +module Test = + let test() = + let dd = MyDistance.Kilometers 1.0 + async { + let r = dd.as$$Nautical + return r + } +""" + + let quickInfo = GetQuickInfoTextFromCode code + let expected = "property MyDistance.asNautical: MyDistance with get" + Assert.AreEqual(expected, quickInfo) + () + + [] + let ``Automation.LetBindings.Module`` () = + let code = + """ +namespace FsTest + +module Test = + let fu$$nc x = () +""" + + let expectedSignature = "val func: x: 'a -> unit" + + let tooltip = GetQuickInfoTextFromCode code + + StringAssert.StartsWith(expectedSignature, tooltip) + () + + [] + let ``Automation.LetBindings.InsideType.Instance`` () = + let code = + """ +namespace FsTest + +module Test = + type T() = + let fu$$nc x = () +""" + + let expectedSignature = "val func: x: 'a -> unit" + + let tooltip = GetQuickInfoTextFromCode code + + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``Automation.LetBindings.InsideType.Static`` () = + let code = + """ +namespace FsTest + +module Test = + type T() = + static let fu$$nc x = () +""" + + let expectedSignature = "val func: x: 'a -> unit" + + let tooltip = GetQuickInfoTextFromCode code + + StringAssert.StartsWith(expectedSignature, tooltip) + () + + [] + let ``Automation.LetBindings`` () = + let code = + """ +namespace FsTest + +module Test = + do + let fu$$nc x = () + () +""" + + let expectedSignature = "val func: x: 'a -> unit" + let tooltip = GetQuickInfoTextFromCode code + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``quick info for IWSAM property get`` () = + let code = + """ +type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = + static abstract StaticProperty: 'T + +let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = + 'T.StaticPr$$operty +""" + + let expectedSignature = "property IStaticProperty.StaticProperty: 'T with get" + let tooltip = GetQuickInfoTextFromCode code + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``quick info for IWSAM method call`` () = + let code = + """ +type IStaticMethod<'T when 'T :> IStaticMethod<'T>> = + static abstract StaticMethod: unit -> 'T + +let f (x: #IStaticMethod<'T>) = + 'T.StaticMe$$thod() +""" + + let expectedSignature = "static abstract IStaticMethod.StaticMethod: unit -> 'T" + let tooltip = GetQuickInfoTextFromCode code + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``quick info for SRTP property get`` () = + let code = + """ + +let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = + 'T.StaticPr$$operty +""" + + let expectedSignature = "'T: (static member StaticProperty: 'T)" + let tooltip = GetQuickInfoTextFromCode code + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``quick info for SRTP method call`` () = + let code = + """ + +let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticMethod: unit -> 'T) >() = + 'T.StaticMe$$thod() +""" + + let expectedSignature = "'T: (static member StaticMethod: unit -> 'T)" + let tooltip = GetQuickInfoTextFromCode code + StringAssert.StartsWith(expectedSignature, tooltip) + + [] + let ``Display names for exceptions with backticks preserve backticks`` () = + let code = + """ +exception SomeError of ``thing wi$$th space``: string +""" + + let expected = "``thing with space``" + + let actual = GetQuickInfoTextFromCode code + StringAssert.Contains(expected, actual) + () + + [] + let ``Display names for anonymous record fields with backticks preserve backticks`` () = + let code = + """ +type R = {| ``thing wi$$th space``: string |} +""" + + let expected = "``thing with space``" + + let actual = GetQuickInfoTextFromCode code + + StringAssert.Contains(expected, actual) + () + + [] + let ``Display names identifiers for active patterns with backticks preserve backticks`` () = + let code = + """ +let (|``Thing with space``|_|) x = if x % 2 = 0 then Some (x/2) else None + +match 4 with +| ``Thing wi$$th space`` _ -> "yes" +| _ -> "no" +""" + + let expected = "``Thing with space``" + + let actual = GetQuickInfoTextFromCode code + + StringAssert.Contains(expected, actual) + () diff --git a/vsintegration/tests/UnitTests/RoslynSourceTextTests.fs b/vsintegration/tests/FSharp.Editor.Tests/RoslynSourceTextTests.fs similarity index 72% rename from vsintegration/tests/UnitTests/RoslynSourceTextTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/RoslynSourceTextTests.fs index 12132dc9acd..e06392e5e5e 100644 --- a/vsintegration/tests/UnitTests/RoslynSourceTextTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/RoslynSourceTextTests.fs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor +namespace FSharp.Editor.Tests open System open NUnit.Framework - open Microsoft.VisualStudio.FSharp.Editor -open FSharp.Compiler.CodeAnalysis open Microsoft.CodeAnalysis.Text [] @@ -17,15 +15,15 @@ module RoslynSourceTextTests = let text = "test\ntest2\r\ntest3\n\ntest4\ntest5\rtest6\n" let sourceText = SourceText.From(text).ToFSharpSourceText() - Assert.AreEqual("test", sourceText.GetLineString(0)) + Assert.AreEqual("test", sourceText.GetLineString(0)) Assert.AreEqual("test2", sourceText.GetLineString(1)) Assert.AreEqual("test3", sourceText.GetLineString(2)) - Assert.AreEqual("", sourceText.GetLineString(3)) + Assert.AreEqual("", sourceText.GetLineString(3)) Assert.AreEqual("test4", sourceText.GetLineString(4)) Assert.AreEqual("test5", sourceText.GetLineString(5)) Assert.AreEqual("test6", sourceText.GetLineString(6)) - Assert.AreEqual("", sourceText.GetLineString(7)) - Assert.AreEqual(8, sourceText.GetLineCount()) + Assert.AreEqual("", sourceText.GetLineString(7)) + Assert.AreEqual(8, sourceText.GetLineCount()) let (count, length) = sourceText.GetLastCharacterPosition() Assert.AreEqual(8, count) @@ -35,11 +33,18 @@ module RoslynSourceTextTests = Assert.True(sourceText.SubTextEquals("test2", 5)) Assert.True(sourceText.SubTextEquals("test3", 12)) - Assert.Throws(fun () -> sourceText.SubTextEquals("test", -1) |> ignore) |> ignore - Assert.Throws(fun () -> sourceText.SubTextEquals("test", text.Length) |> ignore) |> ignore - Assert.Throws(fun () -> sourceText.SubTextEquals("", 0) |> ignore) |> ignore - Assert.Throws(fun () -> sourceText.SubTextEquals(text + text, 0) |> ignore) |> ignore + Assert.Throws(fun () -> sourceText.SubTextEquals("test", -1) |> ignore) + |> ignore + + Assert.Throws(fun () -> sourceText.SubTextEquals("test", text.Length) |> ignore) + |> ignore + + Assert.Throws(fun () -> sourceText.SubTextEquals("", 0) |> ignore) + |> ignore + + Assert.Throws(fun () -> sourceText.SubTextEquals(text + text, 0) |> ignore) + |> ignore Assert.False(sourceText.SubTextEquals("test", 1)) Assert.False(sourceText.SubTextEquals("test", 4)) - Assert.False(sourceText.SubTextEquals("test", 11)) \ No newline at end of file + Assert.False(sourceText.SubTextEquals("test", 11)) diff --git a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/SemanticColorizationServiceTests.fs similarity index 69% rename from vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/SemanticColorizationServiceTests.fs index 7b8d2825abe..3b0a425c325 100644 --- a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/SemanticColorizationServiceTests.fs @@ -1,61 +1,68 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor -open System +namespace FSharp.Editor.Tests + open NUnit.Framework open Microsoft.VisualStudio.FSharp.Editor -open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices open FSharp.Compiler.Text open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Classification +open FSharp.Editor.Tests.Helpers -[] +[] type SemanticClassificationServiceTests() = let filePath = "C:\\test.fs" - let projectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - UnresolvedReferences = None - OriginalLoadReferences = [] - Stamp = None - } - - let checker = FSharpChecker.Create() - let perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false } - let getRanges (source: string) : SemanticClassificationItem list = asyncMaybe { let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, source) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("SemanticClassificationServiceTests") |> liftAsync + + let! _, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync("SemanticClassificationServiceTests") + |> liftAsync + return checkFileResults.GetSemanticClassification(None) - } + } |> Async.RunSynchronously |> Option.toList |> List.collect Array.toList - let verifyClassificationAtEndOfMarker(fileContents: string, marker: string, classificationType: string) = + let verifyClassificationAtEndOfMarker (fileContents: string, marker: string, classificationType: string) = let text = SourceText.From(fileContents) let ranges = getRanges fileContents - let line = text.Lines.GetLinePosition (fileContents.IndexOf(marker) + marker.Length - 1) - let markerPos = Position.mkPos (Line.fromZ line.Line) (line.Character + marker.Length - 1) + + let line = + text.Lines.GetLinePosition(fileContents.IndexOf(marker) + marker.Length - 1) + + let markerPos = + Position.mkPos (Line.fromZ line.Line) (line.Character + marker.Length - 1) + match ranges |> List.tryFind (fun item -> Range.rangeContainsPos item.Range markerPos) with | None -> Assert.Fail("Cannot find colorization data for end of marker") - | Some item -> Assert.AreEqual(classificationType, FSharpClassificationTypes.getClassificationTypeName item.Type, "Classification data doesn't match for end of marker") - - let verifyNoClassificationDataAtEndOfMarker(fileContents: string, marker: string, classificationType: string) = + | Some item -> + Assert.AreEqual( + classificationType, + FSharpClassificationTypes.getClassificationTypeName item.Type, + "Classification data doesn't match for end of marker" + ) + + let verifyNoClassificationDataAtEndOfMarker (fileContents: string, marker: string, classificationType: string) = let text = SourceText.From(fileContents) let ranges = getRanges fileContents - let line = text.Lines.GetLinePosition (fileContents.IndexOf(marker) + marker.Length - 1) - let markerPos = Position.mkPos (Line.fromZ line.Line) (line.Character + marker.Length - 1) - let anyData = ranges |> List.exists (fun item -> Range.rangeContainsPos item.Range markerPos && ((FSharpClassificationTypes.getClassificationTypeName item.Type) = classificationType)) + + let line = + text.Lines.GetLinePosition(fileContents.IndexOf(marker) + marker.Length - 1) + + let markerPos = + Position.mkPos (Line.fromZ line.Line) (line.Character + marker.Length - 1) + + let anyData = + ranges + |> List.exists (fun item -> + Range.rangeContainsPos item.Range markerPos + && ((FSharpClassificationTypes.getClassificationTypeName item.Type) = classificationType)) + Assert.False(anyData, "Classification data was found when it wasn't expected.") [] @@ -66,8 +73,8 @@ type SemanticClassificationServiceTests() = [] [] member _.Measured_Types(marker: string, classificationType: string) = - verifyClassificationAtEndOfMarker( - """#light (*Light*) + verifyClassificationAtEndOfMarker ( + """#light (*Light*) open System [] type (*1*)Guid<[] 'm> = Guid @@ -84,8 +91,9 @@ type SemanticClassificationServiceTests() = let i: (*5*)int = 1 let g: (*6*)Guid = Uom.tag Guid.Empty let s: (*7*)string = Uom.tag "foo" """, - marker, - classificationType) + marker, + classificationType + ) [] [] @@ -100,7 +108,8 @@ type SemanticClassificationServiceTests() = [] [] member _.MutableValues(marker: string, classificationType: string) = - let sourceText =""" + let sourceText = + """ type R1 = { mutable (*1*)Doop: int} let r1 = { (*2*)Doop = 12 } r1.Doop @@ -121,7 +130,8 @@ let r = { (*12*)MutableField = ref 12 } r.MutableField r.MutableField := 3 """ - verifyClassificationAtEndOfMarker(sourceText, marker, classificationType) + + verifyClassificationAtEndOfMarker (sourceText, marker, classificationType) [] [] @@ -131,7 +141,8 @@ r.MutableField := 3 [] [] member _.Disposables(marker: string, classificationType: string) = - let sourceText = """ + let sourceText = + """ open System type (*1*)Disposable() = @@ -146,7 +157,8 @@ let f() = let (*7*)local2 = { new IDisposable with member _.Dispose() = () } () """ - verifyClassificationAtEndOfMarker(sourceText, marker, classificationType) + + verifyClassificationAtEndOfMarker (sourceText, marker, classificationType) [] [] @@ -155,7 +167,8 @@ let f() = [] [] member _.NoInrefsExpected(marker: string, classificationType: string) = - let sourceText = """ + let sourceText = + """ let f (item: (*1*)inref) = printfn "%d" (*2*)item let g() = let x = 1 @@ -165,4 +178,5 @@ let g() = f (*5*)&xRef f (*6*)&yRef """ - verifyNoClassificationDataAtEndOfMarker(sourceText, marker, classificationType) \ No newline at end of file + + verifyNoClassificationDataAtEndOfMarker (sourceText, marker, classificationType) diff --git a/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs new file mode 100644 index 00000000000..855c3346321 --- /dev/null +++ b/vsintegration/tests/FSharp.Editor.Tests/SignatureHelpProviderTests.fs @@ -0,0 +1,644 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Editor.Tests + +open System +open System.IO +open NUnit.Framework +open Microsoft.VisualStudio.FSharp.Editor +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.Text +open Microsoft.CodeAnalysis.Text +open FSharp.Editor.Tests.Helpers + +module SignatureHelpProvider = + let private DefaultDocumentationProvider = + { new IDocumentationBuilder with + override doc.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () + override doc.AppendDocumentation(_, _, _, _, _, _, _) = () + } + + let checker = FSharpChecker.Create() + + let filePath = "C:\\test.fs" + + let PathRelativeToTestAssembly p = + Path.Combine(Path.GetDirectoryName(Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) + + let internal projectOptions = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [||] + OtherOptions = + [| + "-r:" + + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") + |] + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + Stamp = None + } + + let internal parsingOptions = + { FSharpParsingOptions.Default with + SourceFiles = [| filePath |] + } + + let GetSignatureHelp (project: FSharpProject) (fileName: string) (caretPosition: int) = + async { + let triggerChar = None + let fileContents = File.ReadAllText(fileName) + let sourceText = SourceText.From(fileContents) + let textLines = sourceText.Lines + let caretLinePos = textLines.GetLinePosition(caretPosition) + let caretLineColumn = caretLinePos.Character + + let document = + RoslynTestHelpers.CreateSingleDocumentSolution(fileName, sourceText, options = project.Options) + + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync("GetSignatureHelp") + |> Async.RunSynchronously + + let paramInfoLocations = + parseResults + .FindParameterLocations( + Position.fromZ caretLinePos.Line caretLineColumn + ) + .Value + + let triggered = + FSharpSignatureHelpProvider.ProvideMethodsAsyncAux( + caretLinePos, + caretLineColumn, + paramInfoLocations, + checkFileResults, + DefaultDocumentationProvider, + sourceText, + caretPosition, + triggerChar + ) + |> Async.RunSynchronously + + return triggered + } + |> Async.RunSynchronously + + let GetCompletionTypeNames (project: FSharpProject) (fileName: string) (caretPosition: int) = + let sigHelp = GetSignatureHelp project fileName caretPosition + + match sigHelp with + | None -> [||] + | Some data -> + let completionTypeNames = + data.SignatureHelpItems + |> Array.map (fun r -> r.Parameters |> Array.map (fun p -> p.CanonicalTypeTextForSorting)) + + completionTypeNames + + let GetCompletionTypeNamesFromCursorPosition (project: FSharpProject) = + let fileName, caretPosition = project.GetCaretPosition() + let completionNames = GetCompletionTypeNames project fileName caretPosition + completionNames + + let GetCompletionTypeNamesFromXmlString (xml: string) = + use project = CreateProject xml + GetCompletionTypeNamesFromCursorPosition project + + let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (expected: (string * int * int * string option) option) = + let caretPosition = fileContents.IndexOf(marker) + marker.Length + + let triggerChar = + if marker = "," then Some ',' + elif marker = "(" then Some '(' + elif marker = "<" then Some '<' + else None + + let sourceText = SourceText.From(fileContents) + let textLines = sourceText.Lines + let caretLinePos = textLines.GetLinePosition(caretPosition) + let caretLineColumn = caretLinePos.Character + + let document = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, sourceText, options = projectOptions) + + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForMethodCalls") + |> Async.RunSynchronously + + let actual = + let paramInfoLocations = + parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn) + + match paramInfoLocations with + | None -> None + | Some paramInfoLocations -> + let triggered = + FSharpSignatureHelpProvider.ProvideMethodsAsyncAux( + caretLinePos, + caretLineColumn, + paramInfoLocations, + checkFileResults, + DefaultDocumentationProvider, + sourceText, + caretPosition, + triggerChar + ) + |> Async.RunSynchronously + + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + + match triggered with + | None -> None + | Some data -> Some(data.ApplicableSpan.ToString(), data.ArgumentIndex, data.ArgumentCount, data.ArgumentName) + + Assert.AreEqual(expected, actual) + + let assertSignatureHelpForFunctionApplication + (fileContents: string) + (marker: string) + expectedArgumentCount + expectedArgumentIndex + expectedArgumentName + = + let caretPosition = fileContents.LastIndexOf(marker) + marker.Length + + let document, sourceText = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) + + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForFunctionApplication") + |> Async.RunSynchronously + + let adjustedColumnInSource = + let rec loop ch pos = + if Char.IsWhiteSpace(ch) then + loop sourceText.[pos - 1] (pos - 1) + else + pos + + loop sourceText.[caretPosition - 1] (caretPosition - 1) + + let sigHelp = + FSharpSignatureHelpProvider.ProvideParametersAsyncAux( + parseResults, + checkFileResults, + document.Id, + [], + DefaultDocumentationProvider, + sourceText, + caretPosition, + adjustedColumnInSource, + filePath + ) + |> Async.RunSynchronously + + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + + match sigHelp with + | None -> Assert.Fail("Expected signature help") + | Some sigHelp -> + Assert.AreEqual(expectedArgumentCount, sigHelp.ArgumentCount) + Assert.AreEqual(expectedArgumentIndex, sigHelp.ArgumentIndex) + Assert.AreEqual(1, sigHelp.SignatureHelpItems.Length) + Assert.IsTrue(expectedArgumentIndex < sigHelp.SignatureHelpItems[0].Parameters.Length) + Assert.AreEqual(expectedArgumentName, sigHelp.SignatureHelpItems[0].Parameters[expectedArgumentIndex].ParameterName) + +open SignatureHelpProvider + +[] +module ``Gives signature help in method calls`` = + + [] + let ``dot`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "." + assertSignatureHelpForMethodCalls fileContents marker None + + [] + let ``System`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "System" + assertSignatureHelpForMethodCalls fileContents marker None + + [] + let ``WriteLine`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "WriteLine" + assertSignatureHelpForMethodCalls fileContents marker None + + [] + let ``open paren`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "(" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 0, 2, Some "format")) + + [] + let ``named arg`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "format" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 0, 2, Some "format")) + + [] + let ``comma`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "," + assertSignatureHelpForMethodCalls fileContents marker None + + [] + let ``second comma`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + assertSignatureHelpForMethodCalls fileContents """",""" (Some("[7..64)", 1, 2, Some "arg0")) + + [] + let ``second named arg`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "arg0" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 1, 2, Some "arg0")) + + [] + let ``second named arg equals`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "arg0=" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 1, 2, Some "arg0")) + + [] + let ``World`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") +""" + + let marker = "World" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 1, 2, Some "arg0")) + + [] + let ``end paren`` () : unit = + let fileContents = + """ +//1 +System.Console.WriteLine(format="Hello, {0}",arg0="World") + """ + + let marker = ")" + assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 0, 2, Some "format")) + +[] +module ``Signature help with list literals, parens, etc`` = + [] + let ``Open paren`` () : unit = + let fileContents = + """ +//2 +open System +Console.WriteLine([(1,2)]) +""" + + let marker = "WriteLine(" + assertSignatureHelpForMethodCalls fileContents marker (Some("[20..45)", 0, 0, None)) + + [] + let ``comma`` () : unit = + let fileContents = + """ +//2 +open System +Console.WriteLine([(1,2)]) +""" + + let marker = "," + assertSignatureHelpForMethodCalls fileContents marker None + + [] + let ``list and tuple bracket pair start`` () : unit = + let fileContents = + """ +//2 +open System +Console.WriteLine([(1,2)]) +""" + + let marker = "[(" + assertSignatureHelpForMethodCalls fileContents marker (Some("[20..45)", 0, 1, None)) + +[] +module ``Unfinished parentheses`` = + [] + let ``Unfinished parentheses`` () : unit = + let fileContents = + """ +let _ = System.DateTime( +""" + + let marker = "let _ = System.DateTime(" + assertSignatureHelpForMethodCalls fileContents marker (Some("[10..26)", 0, 0, None)) + + [] + let ``Unfinished parentheses with comma`` () : unit = + let fileContents = + """ +let _ = System.DateTime(1L, +""" + + let marker = "let _ = System.DateTime(1L," + assertSignatureHelpForMethodCalls fileContents marker (Some("[10..31)", 1, 2, None)) + + [] +#if RELEASE + [] +#endif + let ``type provider static parameter tests`` () : unit = + // This is old code and I'm too lazy to move it all out. - Phillip Carter + let manyTestCases = + [ + (""" +//3 +type foo = N1.T< +type foo2 = N1.T +type foo2 = N1.T +type foo3 = N1.T +type foo4 = N1.T +type foo5 = N1.T +""", + [ + ("type foo = N1.T<", Some("[18..24)", 0, 0, None)) + ("type foo2 = N1.T<", Some("[40..53)", 0, 0, Some "Param1")) + ("type foo2 = N1.T] +module ``Function argument applications`` = + let private DefaultDocumentationProvider = + { new IDocumentationBuilder with + override doc.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () + override doc.AppendDocumentation(_, _, _, _, _, _, _) = () + } + + [] + let ``single argument function application`` () : unit = + let fileContents = + """ +sqrt + """ + + let marker = "sqrt " + assertSignatureHelpForFunctionApplication fileContents marker 1 0 "value" + + [] + let ``multi-argument function application`` () : unit = + let fileContents = + """ +let add2 x y = x + y +add2 1 + """ + + let marker = "add2 1 " + assertSignatureHelpForFunctionApplication fileContents marker 2 1 "y" + + [] + let ``qualified function application`` () : unit = + let fileContents = + """ +module M = + let f x = x +M.f + """ + + let marker = "M.f " + assertSignatureHelpForFunctionApplication fileContents marker 1 0 "x" + + [] + let ``function application in single pipeline with no additional args`` () : unit = + let fileContents = + """ +[1..10] |> id + """ + + let marker = "id " + let caretPosition = fileContents.IndexOf(marker) + marker.Length + + let document, sourceText = + RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) + + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync("function application in single pipeline with no additional args") + |> Async.RunSynchronously + + let adjustedColumnInSource = + let rec loop ch pos = + if Char.IsWhiteSpace(ch) then + loop sourceText.[pos - 1] (pos - 1) + else + pos + + loop sourceText.[caretPosition - 1] (caretPosition - 1) + + let sigHelp = + FSharpSignatureHelpProvider.ProvideParametersAsyncAux( + parseResults, + checkFileResults, + document.Id, + [], + DefaultDocumentationProvider, + sourceText, + caretPosition, + adjustedColumnInSource, + filePath + ) + |> Async.RunSynchronously + + checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() + + Assert.True(sigHelp.IsNone, "No signature help is expected because there are no additional args to apply.") + + [] + let ``function application in single pipeline with an additional argument`` () : unit = + let fileContents = + """ +[1..10] |> List.map + """ + + let marker = "List.map " + assertSignatureHelpForFunctionApplication fileContents marker 1 0 "mapping" + + [] + let ``function application in middle of pipeline with an additional argument`` () : unit = + let fileContents = + """ +[1..10] +|> List.map +|> List.filer (fun x -> x > 3) + """ + + let marker = "List.map " + assertSignatureHelpForFunctionApplication fileContents marker 1 0 "mapping" + + [] + let ``function application with function as parameter`` () : unit = + let fileContents = + """ +let derp (f: int -> int -> int) x = f x 1 +derp +""" + + let marker = "derp " + assertSignatureHelpForFunctionApplication fileContents marker 2 0 "f" + + [] + let ``function application with function as second parameter 1`` () : unit = + let fileContents = + """ +let derp (f: int -> int -> int) x = f x 1 +let add x y = x + y +derp add +""" + + let marker = "derp add " + assertSignatureHelpForFunctionApplication fileContents marker 2 1 "x" + + [] + let ``function application with function as second parameter 2`` () : unit = + let fileContents = + """ +let f (derp: int -> int) x = derp x +""" + + let marker = "derp " + assertSignatureHelpForFunctionApplication fileContents marker 1 0 "arg0" + + [] + let ``function application with curried function as parameter`` () : unit = + let fileContents = + """ +let f (derp: int -> int -> int) x = derp x +""" + + let marker = "derp x " + assertSignatureHelpForFunctionApplication fileContents marker 2 1 "arg1" + + // migrated from legacy test + [] + let ``Multi.ReferenceToProjectLibrary`` () : unit = + let completionNames = + GetCompletionTypeNamesFromXmlString + @" + + + + HelperLibrary.fsproj + + + + + + + + + + + + + " + + let expected = [| [| "System.Int32"; "System.Int32" |] |] + Assert.AreEqual(expected, completionNames) diff --git a/vsintegration/tests/UnitTests/SyntacticColorizationServiceTests.fs b/vsintegration/tests/FSharp.Editor.Tests/SyntacticColorizationServiceTests.fs similarity index 82% rename from vsintegration/tests/UnitTests/SyntacticColorizationServiceTests.fs rename to vsintegration/tests/FSharp.Editor.Tests/SyntacticColorizationServiceTests.fs index 3bb4245ee4d..a6971835e5f 100644 --- a/vsintegration/tests/UnitTests/SyntacticColorizationServiceTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/SyntacticColorizationServiceTests.fs @@ -1,109 +1,159 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor -open System -open System.Threading +namespace FSharp.Editor.Tests +open System.Threading open NUnit.Framework - open Microsoft.CodeAnalysis.Classification open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor -[][] -type SyntacticClassificationServiceTests() = +[] +type SyntacticClassificationServiceTests() = member private this.ExtractMarkerData(fileContents: string, marker: string, defines: string list, isScriptFile: Option) = let textSpan = TextSpan(0, fileContents.Length) - let fileName = if isScriptFile.IsSome && isScriptFile.Value then "test.fsx" else "test.fs" + + let fileName = + if isScriptFile.IsSome && isScriptFile.Value then + "test.fsx" + else + "test.fs" + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let tokens = Tokenizer.getClassifiedSpans(documentId, SourceText.From(fileContents), textSpan, Some(fileName), defines, CancellationToken.None) + + let tokens = + Tokenizer.getClassifiedSpans ( + documentId, + SourceText.From(fileContents), + textSpan, + Some(fileName), + defines, + CancellationToken.None + ) + let markerPosition = fileContents.IndexOf(marker) Assert.IsTrue(markerPosition >= 0, "Cannot find marker '{0}' in file contents", marker) (tokens, markerPosition) - - member private this.VerifyColorizerAtStartOfMarker(fileContents: string, marker: string, defines: string list, classificationType: string, ?isScriptFile: bool) = - let (tokens, markerPosition) = this.ExtractMarkerData(fileContents, marker, defines, isScriptFile) - match tokens |> Seq.tryFind(fun token -> token.TextSpan.Contains(markerPosition)) with + + member private this.VerifyColorizerAtStartOfMarker + ( + fileContents: string, + marker: string, + defines: string list, + classificationType: string, + ?isScriptFile: bool + ) = + let (tokens, markerPosition) = + this.ExtractMarkerData(fileContents, marker, defines, isScriptFile) + + match tokens |> Seq.tryFind (fun token -> token.TextSpan.Contains(markerPosition)) with | None -> Assert.Fail("Cannot find colorization data for start of marker") - | Some(classifiedSpan) -> Assert.AreEqual(classificationType, classifiedSpan.ClassificationType, "Classification data doesn't match for start of marker") - - member private this.VerifyColorizerAtEndOfMarker(fileContents : string, marker: string, defines: string list, classificationType: string, ?isScriptFile: bool) = - let (tokens, markerPosition) = this.ExtractMarkerData(fileContents, marker, defines, isScriptFile) - match tokens |> Seq.tryFind(fun token -> token.TextSpan.Contains(markerPosition + marker.Length - 1)) with + | Some (classifiedSpan) -> + Assert.AreEqual(classificationType, classifiedSpan.ClassificationType, "Classification data doesn't match for start of marker") + + member private this.VerifyColorizerAtEndOfMarker + ( + fileContents: string, + marker: string, + defines: string list, + classificationType: string, + ?isScriptFile: bool + ) = + let (tokens, markerPosition) = + this.ExtractMarkerData(fileContents, marker, defines, isScriptFile) + + match + tokens + |> Seq.tryFind (fun token -> token.TextSpan.Contains(markerPosition + marker.Length - 1)) + with | None -> Assert.Fail("Cannot find colorization data for end of marker") - | Some(classifiedSpan) -> Assert.AreEqual(classificationType, classifiedSpan.ClassificationType, "Classification data doesn't match for end of marker") + | Some (classifiedSpan) -> + Assert.AreEqual(classificationType, classifiedSpan.ClassificationType, "Classification data doesn't match for end of marker") [] - member this.Comment_SingleLine() = + member this.Comment_SingleLine() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let simplefunction x y = x + y // Test1SimpleComment""", - marker = "// Test1", + marker = "// Test1", defines = [], - classificationType = ClassificationTypeNames.Comment) - + classificationType = ClassificationTypeNames.Comment + ) + [] - member this.Conment_SingleLine_MultiConments() = + member this.Conment_SingleLine_MultiConments() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let x = // Test2SimpleComment // 1""", - marker = "// Test2", + marker = "// Test2", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] - member this.Comment_MultiLine_AfterAnExpression() = + member this.Comment_MultiLine_AfterAnExpression() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let mutliLine x = 5(* Test1MultiLine Test2MultiLine <@@asdf@@> Test3MultiLine*) + 1(*Test4*)""", marker = "Test1", defines = [], - classificationType = ClassificationTypeNames.Comment) - + classificationType = ClassificationTypeNames.Comment + ) + [] - member this.Comment_MultiLine_WithLineBreakAndATab() = + member this.Comment_MultiLine_WithLineBreakAndATab() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let mutliLine x = 5(* Test1MultiLine Test2MultiLine <@@asdf@@> Test3MultiLine*) + 1(*Test4*) """, marker = "Test2", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] - member this.Comment_MultiLine_WithLineBreakAfterQuotExp() = + member this.Comment_MultiLine_WithLineBreakAfterQuotExp() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let mutliLine x = 5(* Test1MultiLine Test2MultiLine <@@asdf@@> Test3MultiLine*) + 1(*Test4*) """, marker = "Test3", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] member this.Comment_MultiLine_AfterANumber() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let mutliLine x = 5(* Test1MultiLine Test2MultiLine <@@asdf@@> Test3MultiLine*) + 1(*Test4*) """, marker = "1(*Test4*)", defines = [], - classificationType = ClassificationTypeNames.NumericLiteral) + classificationType = ClassificationTypeNames.NumericLiteral + ) [] member this.Comment_Nested_Nested01() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ (* L1Nesting (* L2Nesting (* L3 Nesting @@ -117,12 +167,14 @@ type SyntacticClassificationServiceTests() = """, marker = "let l3", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] - member this.Comment_Nested_Nested02() = + member this.Comment_Nested_Nested02() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ (* L1Nesting (* L2Nesting (* L3 Nesting @@ -136,12 +188,14 @@ type SyntacticClassificationServiceTests() = """, marker = "let l2", defines = [], - classificationType = ClassificationTypeNames.Comment) - + classificationType = ClassificationTypeNames.Comment + ) + [] - member this.Comment_Nested_Nested03() = + member this.Comment_Nested_Nested03() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ (* L1Nesting (* L2Nesting (* L3 Nesting @@ -155,12 +209,14 @@ type SyntacticClassificationServiceTests() = """, marker = "let l1", defines = [], - classificationType = ClassificationTypeNames.Comment) - + classificationType = ClassificationTypeNames.Comment + ) + [] - member this.Comment_Nested_IdentAfterNestedComments() = + member this.Comment_Nested_IdentAfterNestedComments() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ (* L1Nesting (* L2Nesting (* L3 Nesting @@ -174,33 +230,39 @@ type SyntacticClassificationServiceTests() = """, marker = "let l0", defines = [], - classificationType = ClassificationTypeNames.Identifier) + classificationType = ClassificationTypeNames.Identifier + ) [] - member this.Comment_CommentInString() = + member this.Comment_CommentInString() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ let commentsInString = "...(*test1_comment_in_string_literal*)..." )""", marker = "test1", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + [] - member this.Comment_StringInComment() = + member this.Comment_StringInComment() = this.VerifyColorizerAtEndOfMarker( - fileContents = """ + fileContents = + """ (* let commentsInString2 = "...*)test2_stringliteral_in_comment(*..." *)""", marker = "test2", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] - member this.Comment_Unterminated_KeywordBeforeComment() = + member this.Comment_Unterminated_KeywordBeforeComment() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ type IPeekPoke = interface(*ML Comment Start abstract member Peek: unit -> int abstract member Poke: int -> unit @@ -208,12 +270,14 @@ type SyntacticClassificationServiceTests() = """, marker = "face(*ML Comment Start", defines = [], - classificationType = ClassificationTypeNames.Keyword) - + classificationType = ClassificationTypeNames.Keyword + ) + [] - member this.Comment_Unterminated_KeywordInComment() = + member this.Comment_Unterminated_KeywordInComment() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ type IPeekPoke = interface(*ML Comment Start abstract member Peek: unit -> int abstract member Poke: int -> unit @@ -228,12 +292,14 @@ type SyntacticClassificationServiceTests() = end(*Few Lines Later3*)""", marker = "with(*Few Lines Later2*)", defines = [], - classificationType = ClassificationTypeNames.Comment) - + classificationType = ClassificationTypeNames.Comment + ) + [] - member this.Comment_Unterminated_NestedComments() = + member this.Comment_Unterminated_NestedComments() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ type IPeekPoke = interface(*ML Comment Start abstract member Peek: unit -> int abstract member Poke: int -> unit @@ -250,73 +316,88 @@ type SyntacticClassificationServiceTests() = end(*Few Lines Later3*)""", marker = "nd(*Few Lines Later3*)", defines = [], - classificationType = ClassificationTypeNames.Comment) + classificationType = ClassificationTypeNames.Comment + ) [] member this.String_AtEnd() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ - let stringone = "simple string test"(*Simple String*) """, + fileContents = + """ + let stringone = "simple string test"(*Simple String*) """, marker = """est"(*Simple String*)""", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) + classificationType = ClassificationTypeNames.StringLiteral + ) [] member this.String_MultiLines() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let stringtwo = "simple test(*MultiLine - First*) string test"(*MultiLine - Second*)""", marker = "st(*MultiLine - First*)", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + [] member this.String_MultiLines_LineBreak() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let stringtwo = "simple test(*MultiLine - First*) string test"(*MultiLine - Second*) """, - marker = "\"(*MultiLine - Second*) ", + marker = "\"(*MultiLine - Second*) ", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + [] member this.String_Literal() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let stringthree = @"literal test"(*Literal String*)""", marker = """st"(*Literal String*)""", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + [] member this.ByteString_AtEnd() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ - let bytestringone = "abcdefg"B(*Byte String*)""", + fileContents = + """ + let bytestringone = "abcdefg"B(*Byte String*)""", marker = "B(*Byte String*)", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) + classificationType = ClassificationTypeNames.StringLiteral + ) [] member this.ByteString_MultiLines() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let bytestringtwo = "simple(*MultiLineB - First*) string"B(*MultiLineB - Second*)""", - marker = """ing"B(*MultiLineB - Second*)""", + marker = """ing"B(*MultiLineB - Second*)""", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + [] member this.ByteString_Literal() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let bytestringthree = @"literal"B(*Literal Byte*)""", marker = """al"B(*Literal Byte*)""", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) + classificationType = ClassificationTypeNames.StringLiteral + ) [] member this.EscapedIdentifier_word() = @@ -324,7 +405,8 @@ type SyntacticClassificationServiceTests() = fileContents = """let ``this is an escaped identifier 123ASDF@#$"`` = 4""", marker = "this", defines = [], - classificationType = ClassificationTypeNames.Identifier) + classificationType = ClassificationTypeNames.Identifier + ) [] member this.EscapedIdentifier_SpecialChar() = @@ -332,7 +414,8 @@ type SyntacticClassificationServiceTests() = fileContents = """let ``this is an escaped identifier 123ASDF@#$"`` = 4""", marker = "3ASDF@#", defines = [], - classificationType = ClassificationTypeNames.Identifier) + classificationType = ClassificationTypeNames.Identifier + ) [] member this.EscapedIdentifier_EscapeChar() = @@ -340,45 +423,53 @@ type SyntacticClassificationServiceTests() = fileContents = """let ``this is an escaped identifier 123ASDF@#$"`` = 4""", marker = "\"``", defines = [], - classificationType = ClassificationTypeNames.Identifier) + classificationType = ClassificationTypeNames.Identifier + ) /// Regression for 3609 - Colorizer: __SOURCE__ and others colorized as a string [] - member this.PredefinedIdentifier_SOURCE_DIRECTORY() = + member this.PredefinedIdentifier_SOURCE_DIRECTORY() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let x = __SOURCE_DIRECTORY__(*Test1*)""", marker = "__(*Test1*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] - member this.PredefinedIdentifier_SOURCE_FILE() = + member this.PredefinedIdentifier_SOURCE_FILE() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let y = __SOURCE_FILE__(*Test2*))""", marker = "__(*Test2*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] - member this.PredefinedIdentifier_LINE() = + member this.PredefinedIdentifier_LINE() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let z = __LINE__(*Test3*)""", marker = "__(*Test3*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) - // Regression Test for FSB 3566, F# colorizer does not respect numbers + // Regression Test for FSB 3566, F# colorizer does not respect numbers [] member this.Number_InAnExpression() = this.VerifyColorizerAtStartOfMarker( fileContents = """let f x = x + 9""", marker = "9", defines = [], - classificationType = ClassificationTypeNames.NumericLiteral) - + classificationType = ClassificationTypeNames.NumericLiteral + ) + // Regression Test for FSB 1778 - Colorization seems to be confused after parsing a comment that contains a verbatim string that contains a \ [] member this.Number_AfterCommentWithBackSlash() = @@ -386,13 +477,15 @@ type SyntacticClassificationServiceTests() = fileContents = """let f (* @"\\" *)x = x + 19(*Marker1*)""", marker = "9(*Marker1*)", defines = [], - classificationType = ClassificationTypeNames.NumericLiteral) - + classificationType = ClassificationTypeNames.NumericLiteral + ) + // Regression Test for FSharp1.0:2539 -- lexing @"" strings inside (* *) comments? [] member this.Keyword_AfterCommentWithLexingStrings() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ (* let x = @"\\" *) @@ -401,13 +494,15 @@ type SyntacticClassificationServiceTests() = """, marker = "t(*Marker1*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) - + classificationType = ClassificationTypeNames.Keyword + ) + // Regression Test for FSB 1380 - Language Service colorizes anything followed by a bang as a keyword [] member this.Keyword_LetBang() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let seqExpr = seq { let! x = [1 .. 10](*Marker1*) @@ -416,12 +511,14 @@ type SyntacticClassificationServiceTests() = }""", marker = "! x = [1 .. 10](*Marker1*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_Yield() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let seqExpr = seq { let! x = [1 .. 10](*Marker1*) @@ -430,12 +527,14 @@ type SyntacticClassificationServiceTests() = }""", marker = "! x(*Marker2*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_Do() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let seqExpr = seq { let! x = [1 .. 10](*Marker1*) @@ -444,37 +543,41 @@ type SyntacticClassificationServiceTests() = }""", marker = "! - = ()(*Marker3*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) - + classificationType = ClassificationTypeNames.Keyword + ) + [] member this.Keyword_Invalid_Bang() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let seqExpr = seq { foo! = true(*Marker1*) }""", marker = "! = true(*Marker1*)", defines = [], - classificationType = ClassificationTypeNames.Identifier) - + classificationType = ClassificationTypeNames.Identifier + ) + [] - [] - [] //This test case Verify that the color of const is the keyword color - member this.TypeProvider_StaticParameters_Keyword_const() = + member this.TypeProvider_StaticParameters_Keyword_const() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ type foo = N1.T< const(*Marker1*) "Hello World",2>""", marker = "t(*Marker1*)", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) // Regression test for FSB 3696 - Colorization doesn't treat #if/else/endif correctly when embedded in a string literal [] member this.PreProcessor_InStringLiteral01() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED let x = "#elseMarker1" let y = "#endifMarker2" @@ -484,12 +587,14 @@ type SyntacticClassificationServiceTests() = #endif""", marker = "eMarker1", defines = [], - classificationType = ClassificationTypeNames.ExcludedCode) + classificationType = ClassificationTypeNames.ExcludedCode + ) [] member this.PreProcessor_InStringLiteral02() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED let x = "#elseMarker1" let y = "#endifMarker2" @@ -499,12 +604,14 @@ type SyntacticClassificationServiceTests() = #endif""", marker = "fMarker2", defines = [], - classificationType = ClassificationTypeNames.ExcludedCode) - + classificationType = ClassificationTypeNames.ExcludedCode + ) + [] member this.PreProcessor_ElseKeyword() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED let x = "#elseMarker1" let y = "#endifMarker2" @@ -514,12 +621,14 @@ type SyntacticClassificationServiceTests() = #endif""", marker = "e//Marker3", defines = [], - classificationType = ClassificationTypeNames.PreprocessorKeyword) - + classificationType = ClassificationTypeNames.PreprocessorKeyword + ) + [] member this.PreProcessor_InStringLiteral03() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED let x = "#elseMarker1" let y = "#endifMarker2" @@ -529,12 +638,14 @@ type SyntacticClassificationServiceTests() = #endif""", marker = "eMarker4", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) + classificationType = ClassificationTypeNames.StringLiteral + ) [] member this.PreProcessor_InStringLiteral04() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED let x = "#elseMarker1" let y = "#endifMarker2" @@ -544,104 +655,121 @@ type SyntacticClassificationServiceTests() = #endif""", marker = "fMarker5", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) + // Regression test for FSHARP1.0:4279 [] member this.Keyword_OCaml_asr() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(asr, b) -> () |_ -> ()""", marker = "asr", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_land() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(land, b) -> () |_ -> ()""", marker = "land", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_lor() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(lor, b) -> () |_ -> ()""", marker = "lor", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_lsl() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(lsl, b) -> () |_ -> ()""", marker = "lsl", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_lsr() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(lsr, b) -> () |_ -> ()""", marker = "lsr", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_lxor() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(lxor, b) -> () |_ -> ()""", marker = "lxor", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_mod() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(mod, b) -> () |_ -> ()""", marker = "mod", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] member this.Keyword_OCaml_sig() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ let foo a = match a with | Some(sig, b) -> () |_ -> ()""", marker = "sig", defines = [], - classificationType = ClassificationTypeNames.Keyword) + classificationType = ClassificationTypeNames.Keyword + ) [] [] @@ -658,7 +786,8 @@ type SyntacticClassificationServiceTests() = [] [] member this.InactiveCode(marker: string, classificationType: string) = - let fileContents = """ + let fileContents = + """ #if UNDEFINED (*Inactive Code1*)let notLegit1 x = x #else @@ -697,31 +826,32 @@ type SyntacticClassificationServiceTests() = (*Inactive Code6*)let notLegit6 x = x #endif """ - - this.VerifyColorizerAtEndOfMarker(fileContents, marker, ["DEFINED"], classificationType) + this.VerifyColorizerAtEndOfMarker(fileContents, marker, [ "DEFINED" ], classificationType) [] member public this.Colorizer_AtString() = - this.VerifyColorizerAtEndOfMarker("let s = @\"Bob\"", + this.VerifyColorizerAtEndOfMarker( + "let s = @\"Bob\"", marker = "let s = @\"B", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) [] [] [] - member public this.Regression_Bug4860(marker: string, classificationType: string) = + member public this.Regression_Bug4860(marker: string, classificationType: string) = this.VerifyColorizerAtStartOfMarker( - fileContents = " + fileContents = + " let x = __SOURCE_DIRECTORY__(*Test1*) let y = __SOURCE_FILE__(*Test2*) let z = __LINE__(*Test3*)", marker = marker, defines = [], - classificationType = classificationType) - + classificationType = classificationType + ) [] [] @@ -749,7 +879,7 @@ type SyntacticClassificationServiceTests() = [] member public this.Number_Regression_Bug3566(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "let n = 123 let l = [12..15] let l2 = [12 .. 15] @@ -759,14 +889,14 @@ type SyntacticClassificationServiceTests() = let other = 0x4, 0b0100, 4L, 4UL, 4u, 4s, 4us, 4y, 4uy, 4.0, 4.0f, 4N, 4I, 1M, 123", marker = marker, defines = [], - classificationType = classificationType) - - - /// FEATURE: Hash commands in .fsx files are colorized in PreprocessorKeyword color + classificationType = classificationType + ) + + /// FEATURE: Hash commands in .fsx files are colorized in PreprocessorKeyword color [] member public this.Preprocessor_InFsxFile_StartOfMarker(marker: string, classificationType: string) = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#reference @\"\" #load @\"\" #I <--hash I @@ -774,16 +904,16 @@ type SyntacticClassificationServiceTests() = marker = marker, defines = [], classificationType = classificationType, - isScriptFile = true) - - - /// FEATURE: Hash commands in .fsx files are colorized in PreprocessorKeyword color + isScriptFile = true + ) + + /// FEATURE: Hash commands in .fsx files are colorized in PreprocessorKeyword color [] [] [] member public this.Preprocessor_InFsxFile_EndOfMarker(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "#reference @\"\" #load @\"\" #I <--hash I @@ -791,37 +921,38 @@ type SyntacticClassificationServiceTests() = marker = marker, defines = [], classificationType = classificationType, - isScriptFile = true) + isScriptFile = true + ) - - /// FEATURE: Script-specific hash commands do not show up in blue in .fs files. + /// FEATURE: Script-specific hash commands do not show up in blue in .fs files. [] member public this.Preprocessor_InFsFile_StartOfMarker(marker: string, classificationType: string) = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#reference @\"\" #load @\"\" #I <--hash I #time @\"\"", marker = marker, defines = [], - classificationType = classificationType) - - - /// FEATURE: Script-specific hash commands do not show up in blue in .fs files. + classificationType = classificationType + ) + + /// FEATURE: Script-specific hash commands do not show up in blue in .fs files. [] [] [] member public this.Preprocessor_InFsFile_EndOfMarker(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "#reference @\"\" #load @\"\" #I <--hash I #time @\"\"", marker = marker, defines = [], - classificationType = classificationType) + classificationType = classificationType + ) /// FEATURE: Nested (* *) comments are allowed and will be colorized with CommentColor. Only the final *) causes the comment to close. [] @@ -829,7 +960,7 @@ type SyntacticClassificationServiceTests() = [] member public this.Comment_AfterCommentBlock(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "(*Bob*)type Bob() = class end (* (* @@ -839,19 +970,19 @@ type SyntacticClassificationServiceTests() = (*Charles*)type Charles() = class end", marker = marker, defines = [], - classificationType = classificationType) + classificationType = classificationType + ) - /// BUG: The comment used to be colored in black. [] member public this.Regression_Bug1596() = this.VerifyColorizerAtEndOfMarker( - fileContents = " let 2d (* Identifiers cannot start with numbers *)", - marker = "let 2d (* Ide", + fileContents = " let 2d (* Identifiers cannot start with numbers *)", + marker = "let 2d (* Ide", defines = [], - classificationType = ClassificationTypeNames.Comment) - - + classificationType = ClassificationTypeNames.Comment + ) + /// FEATURE: Code inside #if\#else\#endif blocks is colored with InactiveCodeColor depending on defines. This works for nested #if blocks as well. [] [] @@ -861,7 +992,7 @@ type SyntacticClassificationServiceTests() = [] member public this.Preprocessor_AfterPreprocessorBlock(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "(*Bob*)type Bob() = class end #if UNDEFINED #if UNDEFINED @@ -877,27 +1008,27 @@ type SyntacticClassificationServiceTests() = #endif #endif (*Charles*)type Charles() = class end", - marker = marker, + marker = marker, defines = [], - classificationType = classificationType) - + classificationType = classificationType + ) // Wrong "#else" in "#if" should be ignored [] [] member public this.Preprocessor_InvalidElseDirectiveIgnored(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "#if UNDEFINED (*Alice*)type Alice() = class end (**) #else (*Larry*)type Larry() = class end #endif", - marker = marker, + marker = marker, defines = [], - classificationType = classificationType) + classificationType = classificationType + ) - /// FEATURE: Code inside #if\#else\#endif blocks is colored with InactiveCodeColor depending on defines. This works for nested #if blocks as well. [] [] @@ -907,7 +1038,7 @@ type SyntacticClassificationServiceTests() = [] member public this.Preprocessor_AfterPreprocessorBlockWithDefines(marker: string, classificationType: string) = this.VerifyColorizerAtEndOfMarker( - fileContents = + fileContents = "(*Bob*)type Bob() = class end #if UNDEFINED #if UNDEFINED @@ -923,10 +1054,10 @@ type SyntacticClassificationServiceTests() = #endif #endif (*Charles*)type Charles() = class end", - marker = marker, + marker = marker, defines = [], - classificationType = classificationType) - + classificationType = classificationType + ) /// FEATURE: Preprocessor keywords #light\#if\#else\#endif are colored with the PreprocessorKeyword color. /// FEATURE: All code in the inactive side of #if\#else\#endif is colored with with InactiveCode color. @@ -942,24 +1073,25 @@ type SyntacticClassificationServiceTests() = [] member public this.Preprocessor_Keywords(marker: string, classificationType: string) = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#light (*Light*) #if UNDEFINED //(*If*) let x = 1(*Inactive*) #else //(*Else*) let(*Active*) x = 1 #endif //(*Endif*)", - marker = marker, + marker = marker, defines = [], - classificationType = classificationType) - + classificationType = classificationType + ) /// FEATURE: Preprocessor extended grammar basic check. /// FEATURE: More extensive grammar test is done in compiler unit tests [] member public this.Preprocesso_ExtendedIfGrammar_Basic01() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED || !UNDEFINED // Extended #if let x = "activeCode" #else @@ -968,13 +1100,14 @@ type SyntacticClassificationServiceTests() = """, marker = "activeCode", defines = [], - classificationType = ClassificationTypeNames.StringLiteral) - + classificationType = ClassificationTypeNames.StringLiteral + ) [] member public this.Preprocessor_ExtendedIfGrammar_Basic02() = this.VerifyColorizerAtStartOfMarker( - fileContents = """ + fileContents = + """ #if UNDEFINED || !UNDEFINED // Extended #if let x = "activeCode" #else @@ -983,14 +1116,14 @@ type SyntacticClassificationServiceTests() = """, marker = "inactiveCode", defines = [], - classificationType = ClassificationTypeNames.ExcludedCode) - + classificationType = ClassificationTypeNames.ExcludedCode + ) /// #else / #endif in multiline strings is ignored [] member public this.Preprocessor_DirectivesInString() = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#light #if DEFINED @@ -1000,17 +1133,17 @@ type SyntacticClassificationServiceTests() = let testme = 1 #endif", marker = "let testme", - defines = ["DEFINED"], - classificationType = ClassificationTypeNames.Keyword) + defines = [ "DEFINED" ], + classificationType = ClassificationTypeNames.Keyword + ) - /// Bug 2076 - String literals were causing the endif stack information to be discarded [] [] [] member public this.Preprocessor_KeywordsWithStrings(marker: string, classificationType: string) = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#light (*Light*) let x1 = \"string1\" #if UNDEFINED //(*If*) @@ -1019,32 +1152,32 @@ type SyntacticClassificationServiceTests() = let x3 = \"string3\" #endif //(*Endif*) let x4 = \"string4\"", - marker = marker, + marker = marker, defines = [], - classificationType = classificationType) - + classificationType = classificationType + ) [] member public this.Comment_VerbatimStringInComment_Bug1778() = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#light (* @\"\\\" *) let a = 0", marker = "le", defines = [], - classificationType = ClassificationTypeNames.Keyword) - + classificationType = ClassificationTypeNames.Keyword + ) [] member public this.Preprocessor_KeywordsWrongIf_Bug1577() = this.VerifyColorizerAtStartOfMarker( - fileContents = + fileContents = "#if !!!!!!!!!!!!!!!COMPILED #endif", marker = "!!COMPILED", defines = [], - classificationType = ClassificationTypeNames.Identifier) - + classificationType = ClassificationTypeNames.Identifier + ) // This was an off-by-one bug in the replacement Colorizer [] @@ -1053,4 +1186,5 @@ type SyntacticClassificationServiceTests() = fileContents = "(*Bob*)type Bob() = int", marker = "(*Bob*)typ", defines = [], - classificationType = ClassificationTypeNames.Keyword) \ No newline at end of file + classificationType = ClassificationTypeNames.Keyword + ) diff --git a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs b/vsintegration/tests/UnitTests/BreakpointResolutionService.fs deleted file mode 100644 index 1081471994e..00000000000 --- a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor - -open System -open System.Threading - -open NUnit.Framework - -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Text - -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.LanguageService - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text - -open UnitTests.TestLib.LanguageService - -[][] -type BreakpointResolutionServiceTests() = - - let fileName = "C:\\test.fs" - let projectOptions: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| fileName |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - let code = " -// This is a comment - -type exampleType(parameter: int) = - member this.exampleMember = parameter - -[] -let main argv = - let integerValue = 123456 - let stringValue = \"This is a string\" - let objectValue = exampleType(789) - - printfn \"%d %s %A\" integerValue stringValue objectValue - - let booleanValue = true - match booleanValue with - | true -> printfn \"correct\" - | false -> printfn \"wrong\" - - 0 // return an integer exit code - " - - static member private testCases: Object[][] = [| - [| "This is a comment"; None |] - [| "123456"; Some("let integerValue = 123456") |] - [| "stringValue"; Some("let stringValue = \"This is a string\"") |] - [| "789"; Some("let objectValue = exampleType(789)") |] - [| "correct"; Some("printfn \"correct\"") |] - [| "wrong"; Some("printfn \"wrong\"") |] - [| "0"; Some("0") |] - |] - - [] - member this.TestBreakpointResolution(searchToken: string, expectedResolution: string option) = - let searchPosition = code.IndexOf(searchToken) - Assert.IsTrue(searchPosition >= 0, "SearchToken '{0}' is not found in code", searchToken) - - let document, sourceText = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code) - let searchSpan = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) - - let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) |> Async.RunSynchronously - - match actualResolutionOption with - | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") - | Some(actualResolutionRange) -> - let actualResolution = sourceText.GetSubText(RoslynHelpers.FSharpRangeToTextSpan(sourceText, actualResolutionRange)).ToString() - Assert.IsTrue(expectedResolution.IsSome, "BreakpointResolutionService resolved a breakpoint while it shouldn't at: {0}", actualResolution) - Assert.AreEqual(expectedResolution.Value, actualResolution, "Expected and actual resolutions should match") - \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/CompletionProviderTests.fs b/vsintegration/tests/UnitTests/CompletionProviderTests.fs deleted file mode 100644 index 2281e51ad08..00000000000 --- a/vsintegration/tests/UnitTests/CompletionProviderTests.fs +++ /dev/null @@ -1,917 +0,0 @@ - -// To run the tests in this file: Compile VisualFSharp.UnitTests.dll and run it as a set of unit tests - -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -module VisualFSharp.UnitTests.Editor.CompletionProviderTests - -open System -open System.Linq - -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Completion -open Microsoft.CodeAnalysis.Text -open Microsoft.VisualStudio.FSharp.Editor - -open FSharp.Compiler.CodeAnalysis -open UnitTests.TestLib.LanguageService - -let filePath = "C:\\test.fs" -let internal projectOptions opts = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = opts - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None -} - -let formatCompletions(completions : string seq) = - "\n\t" + String.Join("\n\t", completions) - -let VerifyCompletionListWithOptions(fileContents: string, marker: string, expected: string list, unexpected: string list, opts) = - let options = projectOptions opts - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) - let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously - |> Option.defaultValue (ResizeArray()) - |> Seq.map(fun result -> result.DisplayText) - - let expectedFound = - expected - |> List.filter results.Contains - - let expectedNotFound = - expected - |> List.filter (expectedFound.Contains >> not) - - let unexpectedNotFound = - unexpected - |> List.filter (results.Contains >> not) - - let unexpectedFound = - unexpected - |> List.filter (unexpectedNotFound.Contains >> not) - - // If either of these are true, then the test fails. - let hasExpectedNotFound = not (List.isEmpty expectedNotFound) - let hasUnexpectedFound = not (List.isEmpty unexpectedFound) - - if hasExpectedNotFound || hasUnexpectedFound then - let expectedNotFoundMsg = - if hasExpectedNotFound then - sprintf "\nExpected completions not found:%s\n" (formatCompletions expectedNotFound) - else - String.Empty - - let unexpectedFoundMsg = - if hasUnexpectedFound then - sprintf "\nUnexpected completions found:%s\n" (formatCompletions unexpectedFound) - else - String.Empty - - let completionsMsg = sprintf "\nin Completions:%s" (formatCompletions results) - - let msg = sprintf "%s%s%s" expectedNotFoundMsg unexpectedFoundMsg completionsMsg - - Assert.Fail(msg) - -let VerifyCompletionList(fileContents, marker, expected, unexpected) = - VerifyCompletionListWithOptions(fileContents, marker, expected, unexpected, [| |]) - - -let VerifyCompletionListExactlyWithOptions(fileContents: string, marker: string, expected: string list, opts) = - let options = projectOptions opts - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) - let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously - |> Option.defaultValue (ResizeArray()) - |> Seq.toList - // sort items as Roslyn do - by `SortText` - |> List.sortBy (fun x -> x.SortText) - - let actualNames = actual |> List.map (fun x -> x.DisplayText) - - if actualNames <> expected then - Assert.Fail(sprintf "Expected:\n%s,\nbut was:\n%s\nactual with sort text:\n%s" - (String.Join("; ", expected |> List.map (sprintf "\"%s\""))) - (String.Join("; ", actualNames |> List.map (sprintf "\"%s\""))) - (String.Join("\n", actual |> List.map (fun x -> sprintf "%s => %s" x.DisplayText x.SortText)))) - -let VerifyCompletionListExactly(fileContents: string, marker: string, expected: string list) = - VerifyCompletionListExactlyWithOptions(fileContents, marker, expected, [| |]) - -let VerifyNoCompletionList(fileContents: string, marker: string) = - VerifyCompletionListExactly(fileContents, marker, []) - -let VerifyCompletionListSpan(fileContents: string, marker: string, expected: string) = - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let sourceText = SourceText.From(fileContents) - let resultSpan = CompletionUtils.getDefaultCompletionListSpan(sourceText, caretPosition, documentId, filePath, []) - Assert.AreEqual(expected, sourceText.ToString(resultSpan)) - -[] -let ShouldTriggerCompletionAtCorrectMarkers() = - let testCases = - [("x", true) - ("y", true) - ("1", false) - ("2", false) - ("x +", false) - ("Console.Write", false) - ("System.", true) - ("Console.", true) ] - - for (marker, shouldBeTriggered) in testCases do - let fileContents = """ -let x = 1 -let y = 2 -System.Console.WriteLine(x + y) -""" - - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.AreEqual(shouldBeTriggered, triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result") - -[] -let ShouldNotTriggerCompletionAfterAnyTriggerOtherThanInsertionOrDeletion() = - for triggerKind in [ CompletionTriggerKind.Invoke; CompletionTriggerKind.Snippets ] do - let fileContents = "System.Console.WriteLine(123)" - let caretPosition = fileContents.IndexOf("rite") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, triggerKind, getInfo, IntelliSenseOptions.Default) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - -[] -let ShouldNotTriggerCompletionInStringLiterals() = - let fileContents = "let literal = \"System.Console.WriteLine()\"" - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - -[] -let ShouldNotTriggerCompletionInComments() = - let fileContents = """ -(* -This is a comment -System.Console.WriteLine() -*) -""" - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - -[] -let ShouldTriggerCompletionInInterpolatedString() = - let fileContents = """ - -let x = 1 -let y = 2 -let z = $"abc {System.Console.WriteLine(x + y)} def" -""" - let testCases = - [ - ("x", true) - ("y", true) - ("1", false) - ("2", false) - ("x +", false) - ("Console.Write", false) - ("System.", true) - ("Console.", true) ] - - for (marker, shouldBeTriggered) in testCases do - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.AreEqual(shouldBeTriggered, triggered, sprintf "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result for marker '%s'" marker) - -[] -let ShouldNotTriggerCompletionInExcludedCode() = - let fileContents = """ -#if UNDEFINED -System.Console.WriteLine() -#endif -""" - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") - -[] -let ShouldNotTriggerCompletionInOperatorWithDot() = - // Simulate mistyping '|>' as '|.' - let fileContents = """ -let f() = - 12.0 |. sqrt -""" - let caretPosition = fileContents.IndexOf("|.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger on operators") - -[] -let ShouldTriggerCompletionInAttribute() = - let fileContents = """ -[] -let ShouldTriggerCompletionAfterDerefOperator() = - let fileContents = """ -let foo = ref 12 -printfn "%d" !f -""" - let marker = "!f" - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows a dereference operator (!).") - -[] -let ShouldTriggerCompletionAfterAddressOfOperator() = - let fileContents = """ -type Point = { mutable X: int; mutable Y: int } -let pnt = { X = 1; Y = 2 } -use ptr = fixed &p -""" - let marker = "&p" - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows an addressOf operator (&).") - -[] -let ShouldTriggerCompletionAfterArithmeticOperation() = - let fileContents = """ -let xVal = 1.0 -let yVal = 2.0 -let zVal - -xVal+y -xVal-y -xVal*y -xVal/y -xVal%y -xVal**y -""" - - let markers = [ "+y"; "-y"; "*y"; "/y"; "%y"; "**y"] - - for marker in markers do - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsTrue(triggered, "Completion should trigger after typing an identifier that follows a mathematical operation") - -[] -let ShouldTriggerCompletionAtStartOfFileWithInsertion = - let fileContents = """ -l""" - - let marker = "l" - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsTrue(triggered, "Completion should trigger after typing an Insertion character at the top of the file, e.g. a function definition in a new script file.") - -[] -let ShouldDisplayTypeMembers() = - let fileContents = """ -type T1() = - member this.M1 = 5 - member this.M2 = "literal" - -[] -let main argv = - let obj = T1() - obj. -""" - VerifyCompletionList(fileContents, "obj.", ["M1"; "M2"], ["System"]) - -[] -let ShouldDisplaySystemNamespace() = - let fileContents = """ -type T1 = - member this.M1 = 5 - member this.M2 = "literal" -System.Console.WriteLine() -""" - VerifyCompletionList(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"]) - -[] -let ShouldDisplaySystemNamespaceInInterpolatedString() = - let fileContents = """ -type T1 = - member this.M1 = 5 - member this.M2 = "literal" -let x = $"1 not the same as {System.Int32.MaxValue} is it" -""" - VerifyCompletionList(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"]) - -[] -let ``Class instance members are ordered according to their kind and where they are defined (simple case, by a variable)``() = - let fileContents = """ -type Base() = - member _.BaseMethod() = 1 - member _.BaseProp = 1 - -type Class() = - inherit Base() - member this.MineMethod() = 1 - member this.MineProp = 1 - -let x = Class() -x. -""" - let expected = ["MineProp"; "BaseProp"; "MineMethod"; "BaseMethod"; "Equals"; "GetHashCode"; "GetType"; "ToString"] - VerifyCompletionListExactly(fileContents, "x.", expected) - -[] -let ``Class instance members are ordered according to their kind and where they are defined (simple case, by a constructor)``() = - let fileContents = """ -type Base() = - member _.BaseMethod() = 1 - member _.BaseProp = 1 - -type Class() = - inherit Base() - member this.MineMethod() = 1 - member this.MineProp = 1 - -let x = Class(). -""" - let expected = ["MineProp"; "BaseProp"; "MineMethod"; "BaseMethod"; "Equals"; "GetHashCode"; "GetType"; "ToString"] - VerifyCompletionListExactly(fileContents, "let x = Class().", expected) - - -[] -let ``Class static members are ordered according to their kind and where they are defined``() = - let fileContents = """ -type Base() = - static member BaseStaticMethod() = 1 - static member BaseStaticProp = 1 - -type Class() = - inherit Base() - static member MineStaticMethod() = 1 - static member MineStaticProp = 2 - -Class. -""" - let expected = ["MineStaticProp"; "BaseStaticProp"; "MineStaticMethod"; "BaseStaticMethod"] - VerifyCompletionListExactly(fileContents, "Class.", expected) - -[] -let ``Class instance members are ordered according to their kind and where they are defined (complex case)``() = - let fileContents = """ -type Base() = - inherit System.Collections.Generic.List - member _.BaseMethod() = 1 - member _.BaseProp = 1 - -type Class() = - inherit Base() - member this.MineMethod() = 1 - member this.MineProp = 1 - -let x = Class() -x. -""" - let expected = ["MineProp"; "BaseProp"; "Capacity"; "Count"; "Item"; "MineMethod"; "Add"; "AddRange"; "AsReadOnly"; "BaseMethod"; "BinarySearch"; "Clear"; "Contains" - "ConvertAll"; "CopyTo"; "Equals"; "Exists"; "Find"; "FindAll"; "FindIndex"; "FindLast"; "FindLastIndex"; "ForEach"; "GetEnumerator"; "GetHashCode" - "GetRange"; "GetType"; "IndexOf"; "Insert"; "InsertRange"; "LastIndexOf"; "Remove"; "RemoveAll"; "RemoveAt"; "RemoveRange"; "Reverse"; "Sort" - "ToArray"; "ToString"; "TrimExcess"; "TrueForAll"] - VerifyCompletionListExactly(fileContents, "x.", expected) - -[] -let ``Constructing a new class with object initializer syntax``() = - let fileContents = """ -type A() = - member val SettableProperty = 1 with get, set - member val AnotherSettableProperty = 1 with get, set - member val NonSettableProperty = 1 - -let _ = new A(Setta) -""" - - let expected = ["SettableProperty"; "AnotherSettableProperty"] - let notExpected = ["NonSettableProperty"] - VerifyCompletionList(fileContents, "(Setta", expected, notExpected) - -[] -let ``Constructing a new class with object initializer syntax and verifying 'at' character doesn't exist.``() = - let fileContents = """ -type A() = - member val SettableProperty = 1 with get, set - member val AnotherSettableProperty = 1 with get, set - member val NonSettableProperty = 1 - -let _ = new A(Setta) -""" - - let expected = [] - let notExpected = ["SettableProperty@"; "AnotherSettableProperty@"; "NonSettableProperty@"] - VerifyCompletionList(fileContents, "(Setta", expected, notExpected) - -[] -let ``Constructing a new fully qualified class with object initializer syntax without ending paren``() = - let fileContents = """ -module M = - type A() = - member val SettableProperty = 1 with get, set - member val AnotherSettableProperty = 1 with get, set - member val NonSettableProperty = 1 - -let _ = new M.A(Setta -""" - - let expected = ["SettableProperty"; "AnotherSettableProperty"] - let notExpected = ["NonSettableProperty"] - VerifyCompletionList(fileContents, "(Setta", expected, notExpected) - -[] -let ``Extension methods go after everything else, extension properties are treated as normal ones``() = - let fileContents = """ -open System.Collections.Generic - -type List<'a> with - member _.ExtensionProp = 1 - member _.ExtensionMeth() = 1 - -List(). -""" - let expected = ["Capacity"; "Count"; "Item"; "ExtensionProp"; "Add"; "AddRange"; "AsReadOnly"; "BinarySearch"; "Clear"; "Contains"; "ConvertAll"; "CopyTo"; "Exists" - "Find"; "FindAll"; "FindIndex"; "FindLast"; "FindLastIndex"; "ForEach"; "GetEnumerator"; "GetRange"; "IndexOf"; "Insert"; "InsertRange"; "LastIndexOf" - "Remove"; "RemoveAll"; "RemoveAt"; "RemoveRange"; "Reverse"; "Sort"; "ToArray"; "TrimExcess"; "TrueForAll"; "Equals"; "GetHashCode"; "GetType"; "ToString" - "ExtensionMeth"] - VerifyCompletionListExactly(fileContents, "List().", expected) - -[] -let ``Completion for open contains namespaces and static types``() = - let fileContents = """ -open type System.Ma -""" - let expected = ["Management"; "Math"] // both namespace and static type - VerifyCompletionList(fileContents, "System.Ma", expected, []) - -[] -let ``No completion on type name at declaration site``() = - let fileContents = """ -type T - -""" - VerifyNoCompletionList(fileContents, "type T") - -[] -let ``No completion on name of unfinished function declaration``() = - let fileContents = """ -let f - -""" - VerifyNoCompletionList(fileContents, "let f") - -[] -let ``No completion on name of value declaration``() = - let fileContents = """ -let xyz = 1 - -""" - VerifyNoCompletionList(fileContents, "let xy") - -[] -let ``No completion on name of function declaration``() = - let fileContents = """ -let foo x = 1 - -""" - VerifyNoCompletionList(fileContents, "let fo") - -[] -let ``No completion on name of tupled function declaration``() = - let fileContents = """ -let foo (x, y) = 1 - -""" - VerifyNoCompletionList(fileContents, "let fo") - -[] -let ``No completion on member name at declaration site``() = - let fileContents = """ -type T() = - member this.M -""" - VerifyNoCompletionList(fileContents, "member this.M") - -[] -let ``No completion on function first argument name``() = - let fileContents = """ -let func (p -""" - VerifyNoCompletionList(fileContents, "let func (p") - -[] -let ``No completion on function subsequent argument name``() = - let fileContents = """ -let func (p, h -""" - VerifyNoCompletionList(fileContents, "let func (p, h") - -[] -let ``No completion on curried function subsequent argument name``() = - let fileContents = """ -let func (p) (h -""" - VerifyNoCompletionList(fileContents, "let func (p) (h") - -[] -let ``No completion on method first argument name``() = - let fileContents = """ -type T() = - member this.M(p) = () -""" - VerifyNoCompletionList(fileContents, "member this.M(p") - -[] -let ``No completion on method subsequent argument name``() = - let fileContents = """ -type T() = - member this.M(p:int, h ) = () -""" - VerifyNoCompletionList(fileContents, "member this.M(p:int, h") - -[] -let ``Completion list on abstract member type signature contains modules and types but not keywords or functions`` = - let fileContents = """ -type Interface = - abstract member Eat: l -""" - VerifyCompletionList(fileContents, "Eat: l", ["LanguagePrimitives"; "List" ], ["let"; "log"]) - -[] -let ``Provide completion on first function argument type hint``() = - let fileContents = """ -let func (p:i -""" - VerifyCompletionList(fileContents, "let func (p:i", ["int"], []) - -[] -let ``Provide completion on subsequent function argument type hint``() = - let fileContents = """ -let func (p:int, h:f -""" - VerifyCompletionList(fileContents, "let func (p:int, h:f", ["float"], []) - -[] -let ``Provide completion on local function argument type hint``() = - let fileContents = """ -let top () = - let func (p:i -""" - VerifyCompletionList(fileContents, "let func (p:i", ["int"], []) - -[] -let ``No completion on implicit constructor first argument name``() = - let fileContents = """ -type T(p) = -""" - VerifyNoCompletionList(fileContents, "type T(p") - -[] -let ``No completion on implicit constructor subsequent argument name``() = - let fileContents = """ -type T(p:int, h) = -""" - VerifyNoCompletionList(fileContents, "type T(p:int, h") - -[] -let ``Provide completion on implicit constructor argument type hint``() = - let fileContents = """ -type T(p:i) = -""" - VerifyCompletionList(fileContents, "type T(p:i", ["int"], []) - -[] -let ``No completion on lambda argument name``() = - let fileContents = """ -let _ = fun (p) -> () -""" - VerifyNoCompletionList(fileContents, "let _ = fun (p") - -[] -let ``No completion on lambda argument name2``() = - let fileContents = """ -let _ = fun (p: int) -> () -""" - VerifyNoCompletionList(fileContents, "let _ = fun (p") - -[] -let ``Completions on lambda argument type hint contain modules and types but not keywords or functions``() = - let fileContents = """ -let _ = fun (p:l) -> () -""" - VerifyCompletionList(fileContents, "let _ = fun (p:l", ["LanguagePrimitives"; "List"], ["let"; "log"]) - -[] -let ``Completions in match clause type test contain modules and types but not keywords or functions``() = - let fileContents = """ -match box 5 with -| :? l as x -> () -| _ -> () -""" - VerifyCompletionList(fileContents, ":? l", ["LanguagePrimitives"; "List"], ["let"; "log"]) - -[] -let ``Completions in catch clause type test contain modules and types but not keywords or functions``() = - let fileContents = """ -try - () -with :? l as x -> - () -""" - VerifyCompletionList(fileContents, ":? l", ["LanguagePrimitives"; "List"], ["let"; "log"]) - -[] -let ``Extensions.Bug5162``() = - let fileContents = """ -module Extensions = - type System.Object with - member x.P = 1 -module M2 = - let x = 1 - Ext -""" - VerifyCompletionList(fileContents, " Ext", ["Extensions"; "ExtraTopLevelOperators"], []) - -[] -let ``Custom operations should be at the top of completion list inside computation expression``() = - let fileContents = """ -let joinLocal = 1 - -let _ = - query { - for i in 1..10 do - select i - join - } -""" - VerifyCompletionList(fileContents, " join", ["groupJoin"; "join"; "leftOuterJoin"; "joinLocal"], []) - -[] -let ``Byref Extension Methods`` () = - let fileContents = """ -module Extensions = - open System - open System.Runtime.CompilerServices - - [] - type Message = Message of String - - [] - type MessageExtensions private () = - let (|Message|) (Message message) = message - - [] - static member Print (Message message : Message) = - printfn "%s" message - - [] - static member PrintRef (Message message : inref) = - printfn "%s" message - - let wrappedMessage = Message "Hello World" - - wrappedMessage. -""" - VerifyCompletionList(fileContents, "wrappedMessage.", ["PrintRef"], []) - -[] -let ``Completion list span works with underscore in identifier``() = - let fileContents = """ -let x = A.B_C -""" - VerifyCompletionListSpan(fileContents, "A.B_C", "B_C") - -[] -let ``Completion list span works with digit in identifier``() = - let fileContents = """ -let x = A.B1C -""" - VerifyCompletionListSpan(fileContents, "A.B1C", "B1C") - -[] -let ``Completion list span works with enclosed backtick identifier``() = - let fileContents = """ -let x = A.``B C`` -""" - VerifyCompletionListSpan(fileContents, "A.``B C``", "``B C``") - -[] -let ``Completion list span works with partial backtick identifier``() = - let fileContents = """ -let x = A.``B C -""" - VerifyCompletionListSpan(fileContents, "A.``B C", "``B C") - -[] -let ``Completion list span works with first of multiple enclosed backtick identifiers``() = - let fileContents = """ -let x = A.``B C`` + D.``E F`` -""" - VerifyCompletionListSpan(fileContents, "A.``B C``", "``B C``") - -[] -let ``Completion list span works with last of multiple enclosed backtick identifiers``() = - let fileContents = """ -let x = A.``B C`` + D.``E F`` -""" - VerifyCompletionListSpan(fileContents, "D.``E F``", "``E F``") - -[] -let ``No completion on record field identifier at declaration site``() = - let fileContents = """ -type A = { le: string } -""" - VerifyNoCompletionList(fileContents, "le") - -[] -let ``Completion list on record field type at declaration site contains modules, types and type parameters but not keywords or functions``() = - let fileContents = """ -type A<'lType> = { Field: l } -""" - VerifyCompletionList(fileContents, "Field: l", ["LanguagePrimitives"; "List"], ["let"; "log"]) - -[] -let ``No completion on record stub with no fields at declaration site``() = - let fileContents = """ -type A = { } -""" - VerifyNoCompletionList(fileContents, "{ ") - -[] -let ``No completion on record outside of all fields at declaration site``() = - let fileContents = """ -type A = { Field: string; } -""" - VerifyNoCompletionList(fileContents, "; ") - -[] -let ``No completion on union case identifier at declaration site``() = - let fileContents = """ -type A = - | C of string -""" - VerifyNoCompletionList(fileContents, "| C") - -[] -let ``No completion on union case field identifier at declaration site``() = - let fileContents = """ -type A = - | Case of blah: int * str: int -""" - VerifyNoCompletionList(fileContents, "str") - -[] -let ``Completion list on union case type at declaration site contains modules, types and type parameters but not keywords or functions``() = - let fileContents = """ -type A<'lType> = - | Case of blah: int * str: l -""" - VerifyCompletionList(fileContents, "str: l", ["LanguagePrimitives"; "List"; "lType"], ["let"; "log"]) - -[] -let ``Completion list on union case type at declaration site contains modules, types and type parameters but not keywords or functions2``() = - let fileContents = """ -type A<'lType> = - | Case of l -""" - VerifyCompletionList(fileContents, "of l", ["LanguagePrimitives"; "List"; "lType"], ["let"; "log"]) - -[] -let ``Completion list on union case type at declaration site contains type parameter``() = - let fileContents = """ -type A<'keyType> = - | Case of key -""" - VerifyCompletionList(fileContents, "of key", ["keyType"], []) - -[] -let ``Completion list on type alias contains modules and types but not keywords or functions``() = - let fileContents = """ -type A = l -""" - VerifyCompletionList(fileContents, "= l", ["LanguagePrimitives"; "List"], ["let"; "log"]) - -[] -let ``No completion on enum case identifier at declaration site``() = - let fileContents = """ -type A = - | C = 0 -""" - VerifyNoCompletionList(fileContents, "| C") - -[] -let ``Completion list in generic function body contains type parameter``() = - let fileContents = """ -let Null<'wrappedType> () = - Unchecked.defaultof -""" - VerifyCompletionList(fileContents, "defaultof] -let ``Completion list in generic method body contains type parameter``() = - let fileContents = """ -type A () = - member _.Null<'wrappedType> () = Unchecked.defaultof -""" - VerifyCompletionList(fileContents, "defaultof] -let ``Completion list in generic class method body contains type parameter``() = - let fileContents = """ -type A<'wrappedType> () = - member _.Null () = Unchecked.defaultof -""" - VerifyCompletionList(fileContents, "defaultof] -let ``Completion list in type application contains modules, types and type parameters but not keywords or functions``() = - let fileContents = """ -let emptyMap<'keyType, 'lValueType> () = - Map.empty<'keyType, l> -""" - VerifyCompletionList(fileContents, ", l", ["LanguagePrimitives"; "List"; "lValueType"], ["let"; "log"]) - -[] -let ``Completion list for interface with static abstract method type invocation contains static property with residue``() = - let fileContents = """ -type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = - static abstract StaticProperty: 'T - -let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = - 'T.StaticProperty -""" - VerifyCompletionListWithOptions(fileContents, "'T.Stati", ["StaticProperty"], [], [| "/langversion:preview" |]) - -[] -let ``Completion list for interface with static abstract method type invocation contains static property after dot``() = - let fileContents = """ -type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = - static abstract StaticProperty: 'T - -let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = - 'T.StaticProperty -""" - VerifyCompletionListWithOptions(fileContents, "'T.", ["StaticProperty"], [], [| "/langversion:preview" |]) - - -[] -let ``Completion list for SRTP invocation contains static property with residue``() = - let fileContents = """ -let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = - 'T.StaticProperty - -""" - VerifyCompletionListWithOptions(fileContents, "'T.Stati", ["StaticProperty"], [], [| "/langversion:preview" |]) - -[] -let ``Completion list for SRTP invocation contains static property after dot``() = - let fileContents = """ -let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = - 'T.StaticProperty - -""" - VerifyCompletionListWithOptions(fileContents, "'T.", ["StaticProperty"], [], [| "/langversion:preview" |]) - diff --git a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs deleted file mode 100644 index 322d3f37eed..00000000000 --- a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs +++ /dev/null @@ -1,89 +0,0 @@ - -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// -// To run the tests in this file: Compile VisualFSharp.UnitTests.dll and run it as a set of unit tests - -[] -module VisualFSharp.UnitTests.Editor.DocumentHighlightsServiceTests - -open System -open System.Threading - -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text -open Microsoft.VisualStudio.FSharp.Editor - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text -open UnitTests.TestLib.LanguageService - -let filePath = "C:\\test.fs" - -let internal projectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - UnresolvedReferences = None - OriginalLoadReferences = [] - Stamp = None -} - -let private getSpans (sourceText: SourceText) (caretPosition: int) = - let document = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, sourceText) - FSharpDocumentHighlightsService.GetDocumentHighlights(document, caretPosition) - |> Async.RunSynchronously - |> Option.defaultValue [||] - -let private span sourceText isDefinition (startLine, startCol) (endLine, endCol) = - let range = Range.mkRange filePath (Position.mkPos startLine startCol) (Position.mkPos endLine endCol) - { IsDefinition = isDefinition - TextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) } - -[] -let ShouldHighlightAllSimpleLocalSymbolReferences() = - let fileContents = """ - let foo x = - x + x - let y = foo 2 - """ - let sourceText = SourceText.From(fileContents) - let caretPosition = fileContents.IndexOf("foo") + 1 - let spans = getSpans sourceText caretPosition - - let expected = - [| span sourceText true (2, 8) (2, 11) - span sourceText false (4, 12) (4, 15) |] - - Assert.AreEqual(expected, spans) - -[] -let ShouldHighlightAllQualifiedSymbolReferences() = - let fileContents = """ - let x = System.DateTime.Now - let y = System.DateTime.MaxValue - """ - let sourceText = SourceText.From(fileContents) - let caretPosition = fileContents.IndexOf("DateTime") + 1 - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - - let spans = getSpans sourceText caretPosition - - let expected = - [| span sourceText false (2, 19) (2, 27) - span sourceText false (3, 19) (3, 27) |] - - Assert.AreEqual(expected, spans) - - let caretPosition = fileContents.IndexOf("Now") + 1 - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let spans = getSpans sourceText caretPosition - let expected = [| span sourceText false (2, 28) (2, 31) |] - - Assert.AreEqual(expected, spans) \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs deleted file mode 100644 index 45a415ea187..00000000000 --- a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// -// To run the tests in this file: Compile VisualFSharp.UnitTests.dll and run it as a set of unit tests - -namespace VisualFSharp.UnitTests.Editor - -open System -open System.Collections.Generic -open System.IO -open System.Linq -open System.Reflection - -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Completion -open Microsoft.CodeAnalysis.Text -open Microsoft.VisualStudio.FSharp.Editor - -open FSharp.Compiler.CodeAnalysis -open UnitTests.TestLib.LanguageService - -// AppDomain helper -type Worker () = - - let filePath = "C:\\test.fsx" - let projectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = true - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - - member _.VerifyCompletionListExactly(fileContents: string, marker: string, expected: List) = - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let document = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, SourceText.From(fileContents), options = projectOptions) - let expected = expected |> Seq.toList - let actual = - let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> [])) - |> Async.RunSynchronously - x |> Option.defaultValue (ResizeArray()) - |> Seq.toList - // sort items as Roslyn do - by `SortText` - |> List.sortBy (fun x -> x.SortText) - - let actualNames = actual |> List.map (fun x -> x.DisplayText) - - if actualNames <> expected then - Assert.Fail(sprintf "Expected:\n%s,\nbut was:\n%s\nactual with sort text:\n%s" - (String.Join("; ", expected |> List.map (sprintf "\"%s\""))) - (String.Join("; ", actualNames |> List.map (sprintf "\"%s\""))) - (String.Join("\n", actual |> List.map (fun x -> sprintf "%s => %s" x.DisplayText x.SortText)))) - -module FsxCompletionProviderTests = - - let getWorker () = Worker() - - [] - let fsiShouldTriggerCompletionInFsxFile() = - let fileContents = """ - fsi. - """ - let expected = List([ - "CommandLineArgs"; "EventLoop"; "FloatingPointFormat"; "FormatProvider"; "PrintDepth"; - "PrintLength"; "PrintSize"; "PrintWidth"; "ShowDeclarationValues"; "ShowIEnumerable"; - "ShowProperties"; "AddPrinter"; "AddPrintTransformer"; "Equals"; "GetHashCode"; - "GetType"; "ToString"; ]) - - // We execute in a seperate appdomain so that we can set BaseDirectory to a non-existent location - getWorker().VerifyCompletionListExactly(fileContents, "fsi.", expected) - diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs deleted file mode 100644 index fa9e0ca2937..00000000000 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// -// To run the tests in this file: Compile VisualFSharp.UnitTests.dll and run it as a set of unit tests - -namespace VisualFSharp.UnitTests.Editor - -open System -open System.IO -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text -open Microsoft.VisualStudio.FSharp.Editor -open FSharp.Compiler -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Text -open UnitTests.TestLib.LanguageService - -[] -module GoToDefinitionServiceTests = - - let userOpName = "GoToDefinitionServiceTests" - - let private findDefinition - ( - document: Document, - sourceText: SourceText, - position: int, - defines: string list - ) : range option = - maybe { - let textLine = sourceText.Lines.GetLineFromPosition position - let textLinePos = sourceText.Lines.GetLinePosition position - let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(userOpName)) |> Async.RunSynchronously - - let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) - - match declarations with - | FindDeclResult.DeclFound range -> return range - | _ -> return! None - } - - let makeOptions filePath args = - { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = args - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - - let GoToDefinitionTest (fileContents: string, caretMarker: string, expected, opts) = - - let filePath = Path.GetTempFileName() + ".fs" - File.WriteAllText(filePath, fileContents) - let options = makeOptions filePath opts - - let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker - let document, sourceText = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) - let actual = - findDefinition(document, sourceText, caretPosition, []) - |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) - - if actual <> expected then - Assert.Fail(sprintf "Incorrect information returned for fileContents=<<<%s>>>, caretMarker=<<<%s>>>, expected =<<<%A>>>, actual = <<<%A>>>" fileContents caretMarker expected actual) - - [] - let ``goto definition smoke test``() = - - let manyTestCases = - [ -// Test1 - (""" -type TestType() = - member this.Member1(par1: int) = - printf "%d" par1 - member this.Member2(par2: string) = - printf "%s" par2 - -[] -let main argv = - let obj = TestType() - obj.Member1(5) - obj.Member2("test")""", - [ ("printf \"%d\" par1", Some(3, 3, 24, 28)); - ("printf \"%s\" par2", Some(5, 5, 24, 28)); - ("let obj = TestType", Some(2, 2, 5, 13)); - ("let obj", Some(10, 10, 8, 11)); - ("obj.Member1", Some(3, 3, 16, 23)); - ("obj.Member2", Some(5, 5, 16, 23)); ]); -// Test2 - (""" -module Module1 = - let foo x = x - -let _ = Module1.foo 1 -""", - [ ("let _ = Module", Some (2, 2, 7, 14)) ]) - ] - - for fileContents, testCases in manyTestCases do - for caretMarker, expected in testCases do - - printfn "Test case: caretMarker=<<<%s>>>" caretMarker - GoToDefinitionTest (fileContents, caretMarker, expected, [| |]) - - [] - let ``goto definition for string interpolation``() = - - let fileContents = """ -let xxxxx = 1 -let yyyy = $"{abc{xxxxx}def}" """ - let caretMarker = "xxxxx" - let expected = Some(2, 2, 4, 9) - - GoToDefinitionTest (fileContents, caretMarker, expected, [| |]) - - [] - let ``goto definition for static abstract method invocation``() = - - let fileContents = """ -type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = - static abstract StaticProperty: 'T - -let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = - 'T.StaticProperty -""" - let caretMarker = "'T.StaticProperty" - let expected = Some(3, 3, 20, 34) - - GoToDefinitionTest (fileContents, caretMarker, expected, [| "/langversion:preview" |]) diff --git a/vsintegration/tests/UnitTests/HelpContextServiceTests.fs b/vsintegration/tests/UnitTests/HelpContextServiceTests.fs deleted file mode 100644 index 248535527f3..00000000000 --- a/vsintegration/tests/UnitTests/HelpContextServiceTests.fs +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor - -open System -open System.Threading - -open NUnit.Framework - -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Text -open Microsoft.CodeAnalysis -open FSharp.Compiler.CodeAnalysis -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.LanguageService -open UnitTests.TestLib.Utils -open UnitTests.TestLib.LanguageService - -[][] -type HelpContextServiceTests() = - - let filePath = "C:\\test.fs" - let makeOptions args = - { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = args - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - - let getMarkers (source:string) = - let mutable cnt = 0 - [ for i in 0 .. (source.Length - 1) do - if source.[i] = '$' then - yield (i - cnt) - cnt <- cnt + 1 - ] - - let TestF1KeywordsWithOptions(expectedKeywords: string option list, lines : string list, opts : string[]) = - let options = makeOptions opts - - let fileContentsWithMarkers = String.Join("\r\n", lines) - let fileContents = fileContentsWithMarkers.Replace("$", "") - let document, sourceText = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents, options = options) - - let markers = getMarkers fileContentsWithMarkers - let res = - [ for marker in markers do - let span = Microsoft.CodeAnalysis.Text.TextSpan(marker, 0) - let textLine = sourceText.Lines.GetLineFromPosition(marker) - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let classifiedSpans = Tokenizer.getClassifiedSpans(documentId, sourceText, textLine.Span, Some "test.fs", [], CancellationToken.None) - - FSharpHelpContextService.GetHelpTerm(document, span, classifiedSpans) |> Async.RunSynchronously - ] - let equalLength = (expectedKeywords.Length = res.Length) - Assert.True(equalLength) - - for (exp, res) in List.zip expectedKeywords res do - Assert.AreEqual(exp, res) - - let TestF1Keywords(expectedKeywords, lines) = - TestF1KeywordsWithOptions(expectedKeywords, lines, [| |]) - - [] - member _.``F1 help keyword NoKeyword.Negative`` () = - let file = - [ "let s = \"System.Con$sole\"" - "let n = 999$99" - "#if UNDEFINED" - " let w = List.re$v []" - "#endif" - ] - let keywords = [ None; None; None ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Preprocessor`` () = - let file = - [ "#i$f foobaz" - "#e$ndif" - ] - let keywords = [ Some "#if_FS"; Some "#endif_FS" ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Regression.DotNetMethod.854364``() = - let file = - [ "let i : int = 42" - "i.ToStri$ng()" - "i.ToStri$ng(\"format\")" - ] - let keywords = - [ Some "System.Int32.ToString" - Some "System.Int32.ToString" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Namespaces`` () = - let file = - [ "open Syst$em.N$et" - "open System.I$O" - "open Microsoft.FSharp.Core" - "" - "System.Cons$ole.WriteLine()" - ] - let keywords = - [ Some "System" - Some "System.Net" - Some "System.IO" - Some "System.Console" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Namespaces.BeforeDot`` () = - let file = - [ "open System$.Net$" - "open System$.IO" - "open System$.Collections$.Generic$" - "open Microsoft.FSharp.Core" - "" - "System$.Console$.WriteLine()" - ] - let keywords = - [ Some "System" - Some "System.Net" - Some "System" - Some "System" - Some "System.Collections" - Some "System.Collections.Generic" - Some "System" - Some "System.Console" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Namespaces.AfterDot`` () = - let file = - [ "open $System.$Net" - "open $System.IO" - "open $System.$Collections.$Generic" - "open Microsoft.FSharp.Core" - "" - "$System.$Console.$WriteLine()" - ] - let keywords = - [ Some "System" - Some "System.Net" - Some "System" - Some "System" - Some "System.Collections" - Some "System.Collections.Generic" - Some "System" - Some "System.Console" - Some "System.Console.WriteLine" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword QuotedIdentifiers``() = - let file = - [ - "let `$`escaped func`` x y = x + y" - "let ``escaped value`$` = 1" - "let x = 1" - "``escaped func`` x$ ``escaped value``" - "``escaped func``$ x ``escaped value``" - "``escaped func`` x $``escaped value``" - "let ``z$`` = 1" - "``$z`` |> printfn \"%d\"" - ] - let keywords = - [ - Some "Test.escaped func" - Some "Test.escaped value" - Some "Test.x" - Some "Test.escaped func" - Some "Test.escaped value" - Some "Test.z" - Some "Test.z" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Attributes`` () = - let file = - [ - "open System.Runtime.InteropServices" - "open System.Runtime.CompilerServices" - "[]" - "type X = " - " []" - " val mutable f : int" - " []" - " member _.Run() = ()" - "[]" - "type Y = class end" - ] - let keywords = - [ - Some "Microsoft.FSharp.Core.StructAttribute.#ctor" - Some "Microsoft.FSharp.Core.DefaultValueAttribute.#ctor" - Some "System.Runtime.CompilerServices.MethodImplAttribute.#ctor" - Some "System.Runtime.InteropServices.StructLayoutAttribute.Size" - ] - TestF1Keywords(keywords, file) - - - [] - [] - [] - //This test case Verify that when F1 is Hit on TypeProvider namespaces it contain the right keyword - member _.``F1 help keyword TypeProvider.Namespaces`` () = - let file = - [ - "open N$1" - ] - let keywords = - [ - Some "N1" - ] - TestF1KeywordsWithOptions(keywords, file, [| "-r:" + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") |]) - - [] - [] - [] - //This test case Verify that when F1 is Hit on TypeProvider Type it contain the right keyword - member _.``F1 help keyword TypeProvider.type`` () = - - let file = - [ - //Dummy Type Provider exposes a parametric type (N1.T) that takes 2 static params (string * int) - """let foo = typeof>""" - ] - let keywords = - [ - Some "N1.T" - ] - TestF1KeywordsWithOptions(keywords, file, [| "-r:"+PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") |]) - - [] - member _.``F1 help keyword EndOfLine``() = - let file = - [ "open System.Net$" - "open System.IO$" - ] - let keywords = - [ Some "System.Net" - Some "System.IO" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword EndOfLine2``() = - let file = - [ "module M" - "open System.Net$" - "open System.IO$" - ] - let keywords = - [ Some "System.Net" - Some "System.IO" - ] - TestF1Keywords(keywords, file) - - - [] - member _.``F1 help keyword Comments``() = - let file = - [ "($* co$mment *$)" - "/$/ com$ment" - ] - let keywords = - [ Some "comment_FS"; Some "comment_FS"; Some "comment_FS"; Some "comment_FS"; Some "comment_FS"; ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword FSharpEntities`` () = - let file = - [ "let (KeyValu$e(k,v)) = null" - "let w : int lis$t = []" - "let b = w.IsEm$pty" - "let m : map = Map.empty" - "m.A$dd(1,1)" - "let z = Li$st.r$ev w" - "let o = No$ne" - "let o1 = So$me 1" - "let c : System.IO.Str$eam = null" - "c.Async$Read(10)" - "let r = r$ef 0" - "r.conten$ts" - ] - let keywords = - [ Some "Microsoft.FSharp.Core.Operators.KeyValuePattern``2" - Some "Microsoft.FSharp.Collections.FSharpList`1" - Some "Microsoft.FSharp.Collections.FSharpList`1.IsEmpty" - Some "Microsoft.FSharp.Collections.FSharpMap`2.Add" - Some "Microsoft.FSharp.Collections.ListModule" - Some "Microsoft.FSharp.Collections.ListModule.Reverse``1" // per F1 keyword spec - one tick for classes, two ticks for members - Some "Microsoft.FSharp.Core.FSharpOption`1.None" - Some "Microsoft.FSharp.Core.FSharpOption`1.Some" - Some "System.IO.Stream" - Some "Microsoft.FSharp.Control.CommonExtensions.AsyncReadBytes" - Some "Microsoft.FSharp.Core.Operators.Ref``1" - Some "Microsoft.FSharp.Core.FSharpRef`1.contents" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Keywords`` () = - let file = - [ "l$et r = ref 0" - "r :$= 1" - "let mut$able foo = 1" - "foo <$- 2" - "let$ z = 1" - ] - let keywords = - [ Some "let_FS" - Some ":=_FS" - Some "mutable_FS" - Some "<-_FS" - Some "let_FS" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Regression.NewInstance.854367`` () = - let file = - [ "let q : System.Runtime.Remoting.TypeE$ntry = null" ] - let keywords = - [ Some "System.Runtime.Remoting.TypeEntry" ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Regression.NewInstance.854367.2`` () = - let file = - [ "let q1 = new System.Runtime.Remoting.Type$Entry()" // this consutrctor exists but is not accessible (it is protected), but the help entry still goes to the type - ] - let keywords = - [ Some "System.Runtime.Remoting.TypeEntry" ] - TestF1Keywords(keywords, file) - - - [] - member _.``F1 help keyword Classes.WebClient`` () = - let file = - [ "let w : System.Net.Web$Client = new System.Net.Web$Client()" ] - let keywords = - [ Some "System.Net.WebClient" - Some "System.Net.WebClient.#ctor" - ] - TestF1Keywords(keywords, file) - - - [] - member _.``F1 help keyword Classes.Object`` () = - let file = - [ "let w : System.Ob$ject = new System.Obj$ect()" ] - let keywords = - [ Some "System.Object" - Some "System.Object.#ctor" - ] - TestF1Keywords(keywords, file) - - - [] - member _.``F1 help keyword Classes.Generic`` () = - let file = - [ "let x : System.Collections.Generic.L$ist = null" ] - let keywords = - [ Some "System.Collections.Generic.List`1" ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Classes.Abbrev`` () = - let file = - [ "let z : Resi$zeArray = null" ] - let keywords = - [ Some "System.Collections.Generic.List`1" ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword Members`` () = - let file = - [ "open System.Linq" - "open System" - "let l = new ResizeArray()" - "let i = l.Cou$nt" - "l.Ad$d(1)" - "let m = new System.IO.MemoryStream()" - "m.BeginRe$ad()" - "l.Se$lect(fun i -> i + 1)" - "let d = new System.DateTi$me()" - "let s = String.Empty" - "s.Equ$als(null)" - "let i = 12" - "i.ToStr$ing()" - ] - let keywords = - [ Some "System.Collections.Generic.List`1.Count" - Some "System.Collections.Generic.List`1.Add" - Some "System.IO.Stream.BeginRead" - Some "System.Linq.Enumerable.Select``2" // per F1 keyword spec - one tick for classes, two ticks for members - Some "System.DateTime.#ctor" - Some "System.String.Equals" - Some "System.Int32.ToString" - ] - TestF1Keywords(keywords, file) - - [] - member _.``F1 help keyword static abstract interface method`` () = - let file = - ["type IStaticProperty<'T when 'T :> IStaticProperty<'T>> =" - " static abstract StaticProperty: 'T" - "" - "let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) =" - " 'T.StaticProp$erty" ] - let keywords = - [ Some "Test.IStaticProperty`1.StaticProperty" ] - TestF1Keywords(keywords, file) - diff --git a/vsintegration/tests/UnitTests/HintServiceTests.fs b/vsintegration/tests/UnitTests/HintServiceTests.fs deleted file mode 100644 index f94954df4c9..00000000000 --- a/vsintegration/tests/UnitTests/HintServiceTests.fs +++ /dev/null @@ -1,115 +0,0 @@ -// 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 deleted file mode 100644 index 135f2721be1..00000000000 --- a/vsintegration/tests/UnitTests/HintTestFramework.fs +++ /dev/null @@ -1,66 +0,0 @@ -// 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 VisualFSharp.UnitTests.Editor -open Microsoft.CodeAnalysis.Text -open Hints - -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 hintKinds = - async { - 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/IndentationServiceTests.fs b/vsintegration/tests/UnitTests/IndentationServiceTests.fs deleted file mode 100644 index 477fa4b2e93..00000000000 --- a/vsintegration/tests/UnitTests/IndentationServiceTests.fs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace VisualFSharp.UnitTests.Editor - -open System -open System.Threading - -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Text -open Microsoft.VisualStudio.FSharp.Editor -open FSharp.Compiler.CodeAnalysis -open Microsoft.CodeAnalysis.Formatting - -open UnitTests.TestLib.LanguageService - -[][] -type IndentationServiceTests() = - let filePath = "C:\\test.fs" - let projectOptions: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None - } - - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let tabSize = 4 - let indentStyle = FormattingOptions.IndentStyle.Smart - - let indentComment = System.Text.RegularExpressions.Regex(@"\$\s*Indent:\s*(\d+)\s*\$") - - let consoleProjectTemplate = " -// Learn more about F# at https://fsharp.org -// See the 'F# Tutorial' project for more help. - -[] -let main argv = - printfn \"%A\" argv - 0 // return an integer exit code" - - let libraryProjectTemplate = " -namespace ProjectNamespace - -type Class1() = - member this.X = \"F#\"" - - let nestedTypesTemplate = " -namespace testspace - type testtype - static member testmember = 1 - -" - - let autoIndentTemplate = " -let plus x y = - x + y // $Indent: 4$ - -let mutable x = 0 -x <- - 10 * 2 // $Indent: 4$ - -match some 10 with -| None -> 0 -| Some x -> - x + 1 // $Indent: 4$ - -try - failwith \"fail\" // $Indent: 4$ -with - | :? System.Exception -> \"error\" - -if 10 > 0 then - true // $Indent: 4$ -else - false // $Indent: 4$ - -( - 1, // $Indent: 4$ - 2 -) - -[ - 1 // $Indent: 4$ - 2 -] - -[| - 1 // $Indent: 4$ - 2 -|] - -[< - Literal // $Indent: 4$ ->] -let constx = 10 - -let t = seq { // $Indent: 0$ - yield 1 // $Indent: 4$ -} - -let g = - function - | None -> 1 // $Indent: 4$ - | Some _ -> 0 - -module MyModule = begin -end // $Indent: 4$ - -type MyType() = class -end // $Indent: 4$ - -type MyStruct = struct -end // $Indent: 4$ - -while true do - printfn \"never end\" // $Indent: 4$ - -// After line has keyword in comment such as function -// should not be indented $Indent: 0$ - - // Even if the line before only had comment like this -// The follwing line should inherit that indentation too $Indent: 4$ -" - - let testCases = [| - ( None, 0, consoleProjectTemplate ) - ( None, 1, consoleProjectTemplate ) - ( Some(0), 2, consoleProjectTemplate ) - ( Some(0), 3, consoleProjectTemplate ) - ( Some(0), 4, consoleProjectTemplate ) - ( Some(0), 5, consoleProjectTemplate ) - ( Some(4), 6, consoleProjectTemplate ) - ( Some(4), 7, consoleProjectTemplate ) - ( Some(4), 8, consoleProjectTemplate ) - - ( None, 0, libraryProjectTemplate ) - ( None, 1, libraryProjectTemplate ) - ( Some(0), 2, libraryProjectTemplate ) - ( Some(0), 3, libraryProjectTemplate ) - ( Some(4), 4, libraryProjectTemplate ) - ( Some(4), 5, libraryProjectTemplate ) - - ( None, 0, nestedTypesTemplate ) - ( None, 1, nestedTypesTemplate ) - ( Some(0), 2, nestedTypesTemplate ) - ( Some(4), 3, nestedTypesTemplate ) - ( Some(8), 4, nestedTypesTemplate ) - ( Some(8), 5, nestedTypesTemplate ) - |] - - let autoIndentTestCases = - autoIndentTemplate.Split [|'\n'|] - |> Array.map (fun s -> s.Trim()) - |> Array.indexed - |> Array.choose (fun (line, text) -> - let m = indentComment.Match text - if m.Success then Some (line, System.Convert.ToInt32 m.Groups.[1].Value) - else None ) - |> Array.map (fun (lineNumber, expectedIndentation) -> - ( Some(expectedIndentation), lineNumber, autoIndentTemplate )) - - [] - member this.TestIndentation() = - for (expectedIndentation, lineNumber, template) in testCases do - let sourceText = SourceText.From(template) - - let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, parsingOptions) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) - - [] - member this.TestAutoIndentation() = - for (expectedIndentation, lineNumber, template) in autoIndentTestCases do - - - let sourceText = SourceText.From(template) - - let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let actualIndentation = FSharpIndentationService.GetDesiredIndentation(documentId, sourceText, filePath, lineNumber, tabSize, indentStyle, parsingOptions) - match expectedIndentation with - | None -> Assert.IsTrue(actualIndentation.IsNone, "No indentation was expected at line {0}", lineNumber) - | Some indentation -> Assert.AreEqual(expectedIndentation.Value, actualIndentation.Value, "Indentation on line {0} doesn't match", lineNumber) diff --git a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs b/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs deleted file mode 100644 index 8333f5ca689..00000000000 --- a/vsintegration/tests/UnitTests/InlineParameterNameHintTests.fs +++ /dev/null @@ -1,269 +0,0 @@ -// 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 shown for method parameters`` () = - let code = """ -let theAnswer = System.Console.WriteLine 42 -""" - let document = getFsDocument code - - let expected = [ - { Content = "value = "; Location = (1, 42) } - ] - - let actual = getParameterNameHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints are shown for parameters of overloaded and curried methods`` () = - let code = """ -type C () = - member _.Normal (alone: string) = 1 - member _.Normal (what: string, what2: int) = 1 - member _.Curried (curr1: string, curr2: int) (x: int) = 1 - -let c = C () - -let a = c.Curried ("hmm", 2) 1 -let a = c.Normal ("hmm", 2) -let a = c.Normal "hmm" -""" - let document = getFsDocument code - - let expected = [ - { Content = "curr1 = "; Location = (8, 20) } - { Content = "curr2 = "; Location = (8, 27) } - { Content = "x = "; Location = (8, 30) } - { Content = "what = "; Location = (9, 19) } - { Content = "what2 = "; Location = (9, 26) } - { Content = "alone = "; Location = (10, 18) } - ] - - let actual = getParameterNameHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints are shown for constructor parameters`` () = - let code = """ -type C (blahFirst: int) = - new (blah: int, blah2: string) = C blah - -let a = C (1, "") -""" - let document = getFsDocument code - - let expected = [ - { Content = "blahFirst = "; Location = (2, 40) } - { Content = "blah = "; Location = (4, 12) } - { Content = "blah2 = "; Location = (4, 15) } - ] - - let actual = getParameterNameHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints are shown for discriminated union case fields with explicit names`` () = - let code = """ -type Shape = - | Square of side: int - | Rectangle of width: int * height: int - -let a = Square 1 -let b = Rectangle (1, 2) -""" - let document = getFsDocument code - - let expected = [ - { Content = "side = "; Location = (5, 16) } - { Content = "width = "; Location = (6, 20) } - { Content = "height = "; Location = (6, 23) } - ] - - let actual = getParameterNameHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints for discriminated union case fields are not shown when names are generated`` () = - let code = """ -type Shape = - | Triangle of side1: int * int * side3: int - | Circle of int - -let c = Triangle (1, 2, 3) -let d = Circle 1 -""" - let document = getFsDocument code - - let expected = [ - { Content = "side1 = "; Location = (5, 19) } - { Content = "side3 = "; Location = (5, 25) } - ] - - let actual = getParameterNameHints document - - Assert.AreEqual(expected, actual) - -[] -let ``Hints for discriminated union case fields are not shown when provided arguments don't match the expected count`` () = - let code = """ -type Shape = - | Triangle of side1: int * side2: int * side3: int - | Circle of int - -let c = Triangle (1, 2) -""" - let document = getFsDocument code - - let actual = getParameterNameHints document - - Assert.IsEmpty(actual) - -[] -let ``Hints for discriminated union case fields are not shown for Cons`` () = - let code = """ -type X = - member _.Test() = 42 :: [42; 42] -""" - let document = getFsDocument code - - let actual = getParameterNameHints document - - Assert.IsEmpty(actual) - diff --git a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs b/vsintegration/tests/UnitTests/InlineTypeHintTests.fs deleted file mode 100644 index 36aabb4c8a0..00000000000 --- a/vsintegration/tests/UnitTests/InlineTypeHintTests.fs +++ /dev/null @@ -1,115 +0,0 @@ -// 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 deleted file mode 100644 index 365bf1ef7c7..00000000000 --- a/vsintegration/tests/UnitTests/OptionParserTests.fs +++ /dev/null @@ -1,59 +0,0 @@ -// 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 deleted file mode 100644 index e28720622a2..00000000000 --- a/vsintegration/tests/UnitTests/OverallHintExperienceTests.fs +++ /dev/null @@ -1,50 +0,0 @@ -// 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" } - -type Shape = - | Square of side: int - | Rectangle of width: int * height: int - -let a = Square 1 -let b = Rectangle (1, 2) - -type C (blahFirst: int) = - member _.Normal (what: string) = 1 - -let a = C 1 -let cc = a.Normal "hmm" -""" - let document = getFsDocument code - let expected = [ - { Content = ": Song"; Location = (2, 18) } - { Content = "song = "; Location = (4, 23) } - { Content = ": string"; Location = (4, 11) } - { Content = "side = "; Location = (10, 16) } - { Content = ": Shape"; Location = (10, 6) } - { Content = "width = "; Location = (11, 20) } - { Content = "height = "; Location = (11, 23) } - { Content = ": Shape"; Location = (11, 6) } - { Content = "blahFirst = "; Location = (16, 11) } - { Content = ": C"; Location = (16, 6) } - { Content = "what = "; Location = (17, 19) } - { Content = ": int"; Location = (17, 7) } - ] - - let actual = getAllHints document - - CollectionAssert.AreEquivalent(expected, actual) diff --git a/vsintegration/tests/UnitTests/QuickInfoProviderTests.fs b/vsintegration/tests/UnitTests/QuickInfoProviderTests.fs deleted file mode 100644 index 9575d2b1e06..00000000000 --- a/vsintegration/tests/UnitTests/QuickInfoProviderTests.fs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -// ------------------------------------------------------------------------------------------------------------------------ -// -// To run the tests in this file: Compile VisualFSharp.UnitTests.dll and run it as a set of unit tests -// ------------------------------------------------------------------------------------------------------------------------ - - -namespace VisualFSharp.UnitTests.Editor - -open System -open NUnit.Framework -open Microsoft.VisualStudio.FSharp -open FSharp.Compiler.EditorServices -open FSharp.Compiler.CodeAnalysis -open Microsoft.CodeAnalysis -open Microsoft.VisualStudio.FSharp.Editor - -[] -type public AssemblyResolverTestFixture () = - - [] - member public __.Init () = AssemblyResolver.addResolver () - -[] -module QuickInfoProviderTests = - -let filePath = "C:\\test.fs" - -let internal projectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None -} - -let private normalizeLineEnds (s: string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n") - -let private tooltipTextToRawString (ToolTipText elements) : string = - let rec parseElement = function - | ToolTipElement.None -> "" - | ToolTipElement.Group(xs) -> - let descriptions = xs |> List.map (fun item -> item.MainDescription) - let descriptionTexts = descriptions |> List.map (fun taggedTexts -> taggedTexts |> Array.map (fun taggedText -> taggedText.Text)) - let descriptionText = descriptionTexts |> Array.concat |> String.concat "" - - let remarks = xs |> List.choose (fun item -> item.Remarks) - let remarkTexts = remarks |> Array.concat |> Array.map (fun taggedText -> taggedText.Text) - let remarkText = (match remarks with [] -> "" | _ -> "\n" + String.concat "" remarkTexts) - - let tps = xs |> List.collect (fun item -> item.TypeMapping) - let tpTexts = tps |> List.map (fun x -> x |> Array.map (fun y -> y.Text) |> String.concat "") - let tpText = (match tps with [] -> "" | _ -> "\n" + String.concat "\n" tpTexts) - - descriptionText + remarkText + tpText - | ToolTipElement.CompositionError(error) -> error - elements |> List.map parseElement |> String.concat "\n" |> normalizeLineEnds - -let executeQuickInfoTest (programText:string) testCases = - let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, programText) - Assert.Multiple(fun _ -> - for (symbol: string, expected: string option) in testCases do - let expected = expected |> Option.map normalizeLineEnds |> Option.map (fun s -> s.Replace("___","")) - let caretPosition = programText.IndexOf(symbol) + symbol.Length - 1 - - let quickInfo = - FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) - |> Async.RunSynchronously - - let actual = quickInfo |> Option.map (fun qi -> tooltipTextToRawString qi.StructuredText) - Assert.AreEqual(expected, actual,"Symbol: " + symbol) - ) - -[] -let ShouldShowQuickInfoAtCorrectPositions() = - let fileContents = """ -let x = 1 -let y = 2 -System.Console.WriteLine(x + y) - """ - - let testCases = - [ "let", Some "let___Used to associate, or bind, a name to a value or function." - "x", Some "val x: int\nFull name: Test.x" - "y", Some "val y: int\nFull name: Test.y" - "1", None - "2", None - "x +", Some """val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) -Full name: Microsoft.FSharp.Core.Operators.(+) -'T1 is int -'T2 is int -'T3 is int""" - "System", Some "namespace System" - "WriteLine", Some "System.Console.WriteLine(value: int) : unit" - ] - - executeQuickInfoTest fileContents testCases - - -[] -let ShouldShowQuickKeywordInfoAtCorrectPositionsForSignatureFiles() = - let fileContents = """ -namespace TestNs -module internal MyModule = - val MyVal: isDecl:bool -> string - """ - let testCases = - [ "namespace", Some "namespace___Used to associate a name with a group of related types and modules, to logically separate it from other code." - "module", Some "module___Used to associate a name with a group of related types, values, and functions, to logically separate it from other code." - "internal", Some "internal___Used to specify that a member is visible inside an assembly but not outside it." - "val", Some "val___Used in a signature to indicate a value, or in a type to declare a member, in limited situations." - "->", Some "->___In function types, delimits arguments and return values. Yields an expression (in sequence expressions); equivalent to the yield keyword. Used in match expressions" - ] - executeQuickInfoTest fileContents testCases - -[] -let ShouldShowQuickKeywordInfoAtCorrectPositionsWithinComputationExpressions() = - let fileContents = """ -type MyOptionBuilder() = - member __.Zero() = None - member __.Return(x: 'T) = Some x - member __.Bind(m: 'T option, f) = Option.bind f m - -let myOpt = MyOptionBuilder() -let x = - myOpt{ - let! x = Some 5 - let! y = Some 11 - return x + y - } - """ - - let testCases = - [ "let!", Some "let!___Used in computation expressions to bind a name to the result of another computation expression." - "return", Some "return___Used to provide a value for the result of the containing computation expression." - ] - - executeQuickInfoTest fileContents testCases - -[] -let ShouldShowQuickInfoForGenericParameters() = - let fileContents = """ - -type C() = - member x.FSharpGenericMethodExplitTypeParams<'T>(a:'T, y:'T) = (a,y) - - member x.FSharpGenericMethodInferredTypeParams(a, y) = (a,y) - -open System.Linq -let coll = [ for i in 1 .. 100 -> (i, string i) ] -let res1 = coll.GroupBy (fun (a, b) -> a) -let res2 = System.Array.Sort [| 1 |] -let test4 x = C().FSharpGenericMethodExplitTypeParams([x], [x]) -let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams([x], [x]) -let test6 = C().FSharpGenericMethodExplitTypeParams(1, 1) -let test7 x = C().FSharpGenericMethodInferredTypeParams([x], [x]) -let test8 = C().FSharpGenericMethodInferredTypeParams(1, 1) -let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams([x], [x]) -let res3 = [1] |> List.map id -let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s + string x) // note there is a type error here, still cehck quickinfo any way -let res5 = 1 + 2 -let res6 = System.DateTime.Now + System.TimeSpan.Zero -let res7 = sin 5.0 -let res8 = abs 5.0 - """ - - let testCases = - - [("GroupBy", - Some - "(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : System.Collections.Generic.IEnumerable> -'TSource is int * string -'TKey is int"); - ("Sort", Some "System.Array.Sort<'T>(array: 'T array) : unit -'T is int"); - ("let test4 x = C().FSharpGenericMethodExplitTypeParams", - Some - "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 -'T is 'a list"); - ("let test5<'U> (x: 'U) = C().FSharpGenericMethodExplitTypeParams", - Some - "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 -'T is 'U list"); - ("let test6 = C().FSharpGenericMethodExplitTypeParams", - Some - "member C.FSharpGenericMethodExplitTypeParams: a: 'T0 * y: 'T0 -> 'T0 * 'T0 -'T is int"); - ("let test7 x = C().FSharpGenericMethodInferredTypeParams", - Some - "member C.FSharpGenericMethodInferredTypeParams: a: 'a1 * y: 'b2 -> 'a1 * 'b2 -'a is 'a0 list -'b is 'a0 list"); - ("let test8 = C().FSharpGenericMethodInferredTypeParams", - Some - "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 -'a is int -'b is int"); - ("let test9<'U> (x: 'U) = C().FSharpGenericMethodInferredTypeParams", - Some - "member C.FSharpGenericMethodInferredTypeParams: a: 'a0 * y: 'b1 -> 'a0 * 'b1 -'a is 'U list -'b is 'U list"); - ("let res3 = [1] |>", - Some - "val (|>) : arg: 'T1 -> func: ('T1 -> 'U) -> 'U -Full name: Microsoft.FSharp.Core.Operators.(|>) -'T1 is int list -'U is int list"); - ("let res3 = [1] |> List.map id", - Some - "val id: x: 'T -> 'T -Full name: Microsoft.FSharp.Core.Operators.id -'T is int"); - ("let res4 = (1.0,[1]) ||>", - Some - "val (||>) : arg1: 'T1 * arg2: 'T2 -> func: ('T1 -> 'T2 -> 'U) -> 'U -Full name: Microsoft.FSharp.Core.Operators.(||>) -'T1 is float -'T2 is int list -'U is float"); - ("let res4 = (1.0,[1]) ||> List.fold", - Some - "val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State -Full name: Microsoft.FSharp.Collections.List.fold -'T is int -'State is float"); - ("let res4 = (1.0,[1]) ||> List.fold (fun s x -> string s +", - Some - "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) -Full name: Microsoft.FSharp.Core.Operators.(+) -'T1 is string -'T2 is string -'T3 is float"); - ("let res5 = 1 +", - Some - "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) -Full name: Microsoft.FSharp.Core.Operators.(+) -'T1 is int -'T2 is int -'T3 is int"); - ("let res6 = System.DateTime.Now +", - Some - "val (+) : x: 'T1 -> y: 'T2 -> 'T3 (requires member (+)) -Full name: Microsoft.FSharp.Core.Operators.(+) -'T1 is System.DateTime -'T2 is System.TimeSpan -'T3 is System.DateTime"); - ("let res7 = sin", - Some - "val sin: value: 'T -> 'T (requires member Sin) -Full name: Microsoft.FSharp.Core.Operators.sin -'T is float"); - ("let res8 = abs", - Some - "val abs: value: 'T -> 'T (requires member Abs) -Full name: Microsoft.FSharp.Core.Operators.abs -'T is int")] - - executeQuickInfoTest fileContents testCases \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/QuickInfoTests.fs b/vsintegration/tests/UnitTests/QuickInfoTests.fs deleted file mode 100644 index fe45fead284..00000000000 --- a/vsintegration/tests/UnitTests/QuickInfoTests.fs +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace VisualFSharp.UnitTests.Editor - -open System.IO -open Microsoft.VisualStudio.FSharp.Editor -open NUnit.Framework -open VisualFSharp.UnitTests.Editor - -[] -module QuickInfo = - -let internal GetQuickInfo (project:FSharpProject) (fileName:string) (caretPosition:int) = - async { - let code = File.ReadAllText(fileName) - let document, _ = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, code) - return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) - } |> Async.RunSynchronously - -let GetQuickInfoText (project:FSharpProject) (fileName:string) (caretPosition:int) = - let sigHelp = GetQuickInfo project fileName caretPosition - match sigHelp with - | Some (quickInfo) -> - let documentationBuilder = - { new IDocumentationBuilder with - override _.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () - override _.AppendDocumentation(_, _, _, _, _, _, _) = () - } - let mainDescription, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo - let mainTextItems = - mainDescription - |> Seq.map (fun x -> x.Text) - let docTextItems = - docs - |> Seq.map (fun x -> x.Text) - System.String.Join(System.String.Empty, (Seq.concat [mainTextItems; docTextItems])) - | _ -> "" - -let GetQuickInfoTextFromCode (code:string) = - use project = SingleFileProject code - let fileName, caretPosition = project.GetCaretPosition() - GetQuickInfoText project fileName caretPosition - -let expectedLines (lines:string list) = System.String.Join("\n", lines) - -[] -let ``Automation.EnumDUInterfacefromFSBrowse.InsideComputationExpression`` () = - let code = """ -namespace FsTest - -type MyColors = - | Red = 0 - | Green = 1 - | Blue = 2 - -module Test = - let test() = - let x = - seq { - for i in 1..10 do - let f = MyColors.Re$$d - yield f - } - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "MyColors.Red: MyColors = 0" - Assert.AreEqual(expected, quickInfo) - -[] -let ``Automation.EnumDUInterfacefromFSBrowse.InsideMatch`` () = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - -module Test = - let test() = - let myDuList = (fun x -> - match x with - | 0 -> MyDistanc$$e.Kilometers - | 1 -> MyDistance.Miles - | _ -> MyDistance.NauticalMiles - ) - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = - expectedLines [ "type MyDistance =" - " | Kilometers of float" - " | Miles of float" - " | NauticalMiles of float" - "Full name: FsTest.MyDistance" ] - Assert.AreEqual(expected, quickInfo) - -[] -let ``Automation.EnumDUInterfacefromFSBrowse.InsideLambda`` () = - let code = """ -namespace FsTest - -type IMyInterface = - interface - abstract Represent : unit -> string - end - -type MyTestType() = - [] - val mutable field : int - - interface IMyInterface with - member this.Represent () = "Implement Interface" - -module Test = - let test() = - let s = new MyTestType() - |> fun (x:MyTestType) -> x :> IMyInterface - |> fun (x:IMyInterface) -> x.Represen$$t() - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "abstract IMyInterface.Represent: unit -> string" - Assert.AreEqual(expected, quickInfo) - -[] -let ``Automation.RecordAndInterfaceFromFSProj.InsideComputationExpression``() = - let code = """ -namespace FsTest - -type MyEmployee = - { mutable Name : string; - mutable Age : int; - mutable IsFTE : bool } - -module Test = - let test() = - let construct = - seq { - for i in 1..10 do - let a = MyEmploye$$e.MakeDummy() - () - } - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = - expectedLines [ "type MyEmployee =" - " {" - " mutable Name: string" - " mutable Age: int" - " mutable IsFTE: bool" - " }" - "Full name: FsTest.MyEmployee" ] - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.RecordAndInterfaceFromFSProj.InsideQuotation``() = - let code = """ -namespace FsTest - -type MyEmployee = - { mutable Name : string; - mutable Age : int; - mutable IsFTE : bool } - -module Test = - let test() = - let aa = { Name: "name"; - Age: 1; - IsFTE: false; } - let b = <@ a$$a.Name @> - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val aa: MyEmployee" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.RecordAndInterfaceFromFSProj.InsideLambda``() = - let code = """ -namespace FsTest - -type MyEmployee = - { mutable Name : string; - mutable Age : int; - mutable IsFTE : bool } - -module Test = - let test() = - let aa = { Name: "name"; - Age: 1; - IsFTE: false; } - let b = - [ aa ] - |> List.filter (fun e -> e.IsFT$$E) - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "MyEmployee.IsFTE: bool" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.TupleRecordFromFSBrowse.InsideComputationExpression``() = - let code = """ -namespace FsTest - -module Test = - let GenerateTuple = - fun x -> - let tuple = (x, x.ToString(), (float)x, (fun y -> (y.ToString(), y + 1))) - tuple - let test() = - let mySeq = - seq { - for i in 1..9 do - let my$$Tuple = GenerateTuple i - yield myTuple - } - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val myTuple: int * string * float * (int -> string * int)" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.TupleRecordFromFSBrowse.SequenceOfMethods``() = - let code = """ -namespace FsTest - -module Test = - let GenerateTuple = - fun x -> - let tuple = (x, x.ToString(), (float)x, (fun y -> (y.ToString(), y + 1))) - tuple - let GetTupleMethod tuple = - let (_intInTuple, _stringInTuple, _floatInTuple, methodInTuple) = tuple - methodInTuple - let test() = - let mySeq = - seq { - for i in 1..9 do - let myTuple = GenerateTuple i - yield myTuple - } - let method$$Seq = Seq.map GetTupleMethod mySeq - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val methodSeq: seq<(int -> string * int)>" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.MatchExpression``() = - let code = """ -namespace FsTest - -[] -type MyPoint(x:int, y:int) = - member this.X = x - member this.Y = y - -module Test = - let test() = - let p1 = MyPoint(1, 2) - match p$$1 with - | p3 when p3.X = 1 -> 0 - | _ -> 1 -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val p1: MyPoint" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.MatchPattern``() = - let code = """ -namespace FsTest - -[] -type MyPoint(x:int, y:int) = - member this.X = x - member this.Y = y - -module Test = - let test() = - let p1 = MyPoint(1, 2) - match p1 with - | p$$3 when p3.X = 1 -> 0 - | _ -> 1 -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val p3: MyPoint" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.UnionIfPredicate``() = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - -module Test = - let test() = - let dd = MyDistance.Kilometers 1.0 - if MyDistance.toMiles d$$d > 0 then () - else () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val dd: MyDistance" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.UnionForPattern``() = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - -module Test = - let test() = - let distances = [ MyDistance.Kilometers 1.0; MyDistance.Miles 1.0 ] - for dist$$ance in distances do - () -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "val distance: MyDistance" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.UnionMethodPatternMatch``() = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - static member toMiles x = - Miles( - match x with - | Miles x -> x - | Kilometers x -> x / 1.6 - | NauticalMiles x -> x * 1.15 - ) - -module Test = - let test() = - let dd = MyDistance.Kilometers 1.0 - match MyDistance.to$$Miles dd with - | Miles x -> 0 - | _ -> 1 -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "static member MyDistance.toMiles: x: MyDistance -> MyDistance" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.UnionMethodPatternMatchBody``() = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - member this.IncreaseBy dist = - match this with - | Kilometers x -> Kilometers (x + dist) - | Miles x -> Miles (x + dist) - | NauticalMiles x -> NauticalMiles (x + dist) - -module Test = - let test() = - let dd = MyDistance.Kilometers 1.0 - match dd.toMiles() with - | Miles x -> dd.Increase$$By 1.0 - | _ -> dd -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "member MyDistance.IncreaseBy: dist: float -> MyDistance" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.UnionAndStructFromFSProj.UnionPropertyInComputationExpression``() = - let code = """ -namespace FsTest - -type MyDistance = - | Kilometers of float - | Miles of float - | NauticalMiles of float - member this.asNautical = - NauticalMiles( - match this with - | Kilometers x -> x / 1.852 - | Miles x -> x / 1.15 - | NauticalMiles x -> x - ) - -module Test = - let test() = - let dd = MyDistance.Kilometers 1.0 - async { - let r = dd.as$$Nautical - return r - } -""" - let quickInfo = GetQuickInfoTextFromCode code - let expected = "property MyDistance.asNautical: MyDistance with get" - Assert.AreEqual(expected, quickInfo) - () - -[] -let ``Automation.LetBindings.Module``() = - let code = """ -namespace FsTest - -module Test = - let fu$$nc x = () -""" - let expectedSignature = "val func: x: 'a -> unit" - - let tooltip = GetQuickInfoTextFromCode code - - StringAssert.StartsWith(expectedSignature, tooltip) - () - -[] -let ``Automation.LetBindings.InsideType.Instance``() = - let code = """ -namespace FsTest - -module Test = - type T() = - let fu$$nc x = () -""" - let expectedSignature = "val func: x: 'a -> unit" - - let tooltip = GetQuickInfoTextFromCode code - - StringAssert.StartsWith(expectedSignature, tooltip) - -[] -let ``Automation.LetBindings.InsideType.Static``() = - let code = """ -namespace FsTest - -module Test = - type T() = - static let fu$$nc x = () -""" - let expectedSignature = "val func: x: 'a -> unit" - - let tooltip = GetQuickInfoTextFromCode code - - StringAssert.StartsWith(expectedSignature, tooltip) - () - -[] -let ``Automation.LetBindings``() = - let code = """ -namespace FsTest - -module Test = - do - let fu$$nc x = () - () -""" - let expectedSignature = "val func: x: 'a -> unit" - let tooltip = GetQuickInfoTextFromCode code - StringAssert.StartsWith(expectedSignature, tooltip) - -[] -let ``quick info for IWSAM property get``() = - let code = """ -type IStaticProperty<'T when 'T :> IStaticProperty<'T>> = - static abstract StaticProperty: 'T - -let f_IWSAM_flex_StaticProperty(x: #IStaticProperty<'T>) = - 'T.StaticPr$$operty -""" - - let expectedSignature = "property IStaticProperty.StaticProperty: 'T with get" - let tooltip = GetQuickInfoTextFromCode code - StringAssert.StartsWith(expectedSignature, tooltip) - -[] -let ``quick info for IWSAM method call``() = - let code = """ -type IStaticMethod<'T when 'T :> IStaticMethod<'T>> = - static abstract StaticMethod: unit -> 'T - -let f (x: #IStaticMethod<'T>) = - 'T.StaticMe$$thod() -""" - - let expectedSignature = "static abstract IStaticMethod.StaticMethod: unit -> 'T" - let tooltip = GetQuickInfoTextFromCode code - StringAssert.StartsWith(expectedSignature, tooltip) - -[] -let ``quick info for SRTP property get``() = - let code = """ - -let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticProperty: 'T) >() = - 'T.StaticPr$$operty -""" - - let expectedSignature = "'T: (static member StaticProperty: 'T)" - let tooltip = GetQuickInfoTextFromCode code - StringAssert.StartsWith(expectedSignature, tooltip) - -[] -let ``quick info for SRTP method call``() = - let code = """ - -let inline f_StaticProperty_SRTP<'T when 'T : (static member StaticMethod: unit -> 'T) >() = - 'T.StaticMe$$thod() -""" - - let expectedSignature = "'T: (static member StaticMethod: unit -> 'T)" - let tooltip = GetQuickInfoTextFromCode code - StringAssert.StartsWith(expectedSignature, tooltip) - - -[] -let ``Display names for exceptions with backticks preserve backticks``() = - let code = """ -exception SomeError of ``thing wi$$th space``: string -""" - let expected = "``thing with space``" - - let actual = GetQuickInfoTextFromCode code - StringAssert.Contains(expected, actual) - () - -[] -let ``Display names for anonymous record fields with backticks preserve backticks``() = - let code = """ -type R = {| ``thing wi$$th space``: string |} -""" - let expected = "``thing with space``" - - let actual = GetQuickInfoTextFromCode code - - StringAssert.Contains(expected, actual) - () - -[] -let ``Display names identifiers for active patterns with backticks preserve backticks``() = - let code = """ -let (|``Thing with space``|_|) x = if x % 2 = 0 then Some (x/2) else None - -match 4 with -| ``Thing wi$$th space`` _ -> "yes" -| _ -> "no" -""" - let expected = "``Thing with space``" - - let actual = GetQuickInfoTextFromCode code - - StringAssert.Contains(expected, actual) - () diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs deleted file mode 100644 index 805cd41c477..00000000000 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ /dev/null @@ -1,535 +0,0 @@ -[] -module VisualFSharp.UnitTests.Editor.SignatureHelpProvider - -open System -open System.IO - -open NUnit.Framework - -open Microsoft.VisualStudio.FSharp.Editor - -open VisualFSharp.UnitTests.Editor - -open UnitTests.TestLib.LanguageService - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.Text - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text - -let filePath = "C:\\test.fs" - -let PathRelativeToTestAssembly p = Path.Combine(Path.GetDirectoryName(Uri( System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath), p) - -let internal projectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| "-r:" + PathRelativeToTestAssembly(@"DummyProviderForLanguageServiceTesting.dll") |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - Stamp = None -} - -let internal parsingOptions = - { FSharpParsingOptions.Default with SourceFiles = [| filePath |] } - -let private DefaultDocumentationProvider = - { new IDocumentationBuilder with - override doc.AppendDocumentationFromProcessedXML(_, _, _, _, _, _) = () - override doc.AppendDocumentation(_, _, _, _, _, _, _) = () - } - -let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:int) = - async { - let triggerChar = None - let fileContents = File.ReadAllText(fileName) - let sourceText = SourceText.From(fileContents) - let textLines = sourceText.Lines - let caretLinePos = textLines.GetLinePosition(caretPosition) - let caretLineColumn = caretLinePos.Character - - let document = RoslynTestHelpers.CreateSingleDocumentSolution(fileName, sourceText, options = project.Options) - let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync("GetSignatureHelp") - |> Async.RunSynchronously - - let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn).Value - let triggered = - FSharpSignatureHelpProvider.ProvideMethodsAsyncAux( - caretLinePos, - caretLineColumn, - paramInfoLocations, - checkFileResults, - DefaultDocumentationProvider, - sourceText, - caretPosition, - triggerChar) - |> Async.RunSynchronously - return triggered - } |> Async.RunSynchronously - -let GetCompletionTypeNames (project:FSharpProject) (fileName:string) (caretPosition:int) = - let sigHelp = GetSignatureHelp project fileName caretPosition - match sigHelp with - | None -> [||] - | Some data -> - let completionTypeNames = - data.SignatureHelpItems - |> Array.map (fun r -> r.Parameters |> Array.map (fun p -> p.CanonicalTypeTextForSorting)) - completionTypeNames - -let GetCompletionTypeNamesFromCursorPosition (project:FSharpProject) = - let fileName, caretPosition = project.GetCaretPosition() - let completionNames = GetCompletionTypeNames project fileName caretPosition - completionNames - -let GetCompletionTypeNamesFromXmlString (xml:string) = - use project = CreateProject xml - GetCompletionTypeNamesFromCursorPosition project - -let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (expected: (string * int * int * string option) option) = - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let triggerChar = if marker ="," then Some ',' elif marker = "(" then Some '(' elif marker = "<" then Some '<' else None - let sourceText = SourceText.From(fileContents) - let textLines = sourceText.Lines - let caretLinePos = textLines.GetLinePosition(caretPosition) - let caretLineColumn = caretLinePos.Character - - let document = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, sourceText, options = projectOptions) - let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForMethodCalls") - |> Async.RunSynchronously - - let actual = - let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn) - match paramInfoLocations with - | None -> None - | Some paramInfoLocations -> - let triggered = - FSharpSignatureHelpProvider.ProvideMethodsAsyncAux( - caretLinePos, - caretLineColumn, - paramInfoLocations, - checkFileResults, - DefaultDocumentationProvider, - sourceText, - caretPosition, - triggerChar) - |> Async.RunSynchronously - - checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - match triggered with - | None -> None - | Some data -> Some (data.ApplicableSpan.ToString(),data.ArgumentIndex,data.ArgumentCount,data.ArgumentName) - - Assert.AreEqual(expected, actual) - -let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: string) expectedArgumentCount expectedArgumentIndex expectedArgumentName = - let caretPosition = fileContents.LastIndexOf(marker) + marker.Length - let document, sourceText = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) - - let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForFunctionApplication") - |> Async.RunSynchronously - - let adjustedColumnInSource = - let rec loop ch pos = - if Char.IsWhiteSpace(ch) then - loop sourceText.[pos - 1] (pos - 1) - else - pos - loop sourceText.[caretPosition - 1] (caretPosition - 1) - - let sigHelp = - FSharpSignatureHelpProvider.ProvideParametersAsyncAux( - parseResults, - checkFileResults, - document.Id, - [], - DefaultDocumentationProvider, - sourceText, - caretPosition, - adjustedColumnInSource, - filePath) - |> Async.RunSynchronously - - checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - - match sigHelp with - | None -> Assert.Fail("Expected signature help") - | Some sigHelp -> - Assert.AreEqual(expectedArgumentCount, sigHelp.ArgumentCount) - Assert.AreEqual(expectedArgumentIndex, sigHelp.ArgumentIndex) - Assert.AreEqual(1, sigHelp.SignatureHelpItems.Length) - Assert.IsTrue(expectedArgumentIndex < sigHelp.SignatureHelpItems[0].Parameters.Length) - Assert.AreEqual(expectedArgumentName, sigHelp.SignatureHelpItems[0].Parameters[expectedArgumentIndex].ParameterName) - -[] -module ``Gives signature help in method calls`` = - - [] - let ``dot``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "." - assertSignatureHelpForMethodCalls fileContents marker None - - [] - let ``System``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "System" - assertSignatureHelpForMethodCalls fileContents marker None - - [] - let ``WriteLine``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "WriteLine" - assertSignatureHelpForMethodCalls fileContents marker None - - [] - let ``open paren``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "(" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[7..64)", 0, 2, Some "format")) - - [] - let ``named arg``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "format" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[7..64)", 0, 2, Some "format")) - - [] - let ``comma``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "," - assertSignatureHelpForMethodCalls fileContents marker None - - [] - let ``second comma``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - assertSignatureHelpForMethodCalls fileContents """",""" (Some ("[7..64)", 1, 2, Some "arg0")) - - [] - let ``second named arg``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "arg0" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[7..64)", 1, 2, Some "arg0")) - - [] - let ``second named arg equals``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "arg0=" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[7..64)", 1, 2, Some "arg0")) - - [] - let ``World``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") -""" - let marker = "World" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[7..64)", 1, 2, Some "arg0")) - - [] - let ``end paren``() : unit = - let fileContents = """ -//1 -System.Console.WriteLine(format="Hello, {0}",arg0="World") - """ - let marker = ")" - assertSignatureHelpForMethodCalls fileContents marker (Some("[7..64)", 0, 2, Some "format")) - -[] -module ``Signature help with list literals, parens, etc`` = - [] - let ``Open paren``() : unit = - let fileContents = """ -//2 -open System -Console.WriteLine([(1,2)]) -""" - let marker = "WriteLine(" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[20..45)", 0, 0, None)) - - [] - let ``comma``() : unit = - let fileContents = """ -//2 -open System -Console.WriteLine([(1,2)]) -""" - let marker = "," - assertSignatureHelpForMethodCalls fileContents marker None - - [] - let ``list and tuple bracket pair start``() : unit = - let fileContents = """ -//2 -open System -Console.WriteLine([(1,2)]) -""" - let marker = "[(" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[20..45)", 0, 1, None)) - -[] -module ``Unfinished parentheses`` = - [] - let ``Unfinished parentheses``() : unit = - let fileContents = """ -let _ = System.DateTime( -""" - let marker = "let _ = System.DateTime(" - assertSignatureHelpForMethodCalls fileContents marker (Some ("[10..26)", 0, 0, None)) - - [] - let ``Unfinished parentheses with comma``() : unit = - let fileContents = """ -let _ = System.DateTime(1L, -""" - let marker = "let _ = System.DateTime(1L," - assertSignatureHelpForMethodCalls fileContents marker (Some ("[10..31)", 1, 2, None )) - -[] -let ``type provider static parameter tests``() : unit = - // This is old code and I'm too lazy to move it all out. - Phillip Carter - let manyTestCases = - [ - ( """ -//3 -type foo = N1.T< -type foo2 = N1.T -type foo2 = N1.T -type foo3 = N1.T -type foo4 = N1.T -type foo5 = N1.T -""", - [("type foo = N1.T<", Some ("[18..24)", 0, 0, None)); - ("type foo2 = N1.T<", Some ("[40..53)", 0, 0, Some "Param1")); - ("type foo2 = N1.T] -module ``Function argument applications`` = - [] - let ``single argument function application``() : unit = - let fileContents = """ -sqrt - """ - let marker = "sqrt " - assertSignatureHelpForFunctionApplication fileContents marker 1 0 "value" - - [] - let ``multi-argument function application``() : unit = - let fileContents = """ -let add2 x y = x + y -add2 1 - """ - let marker = "add2 1 " - assertSignatureHelpForFunctionApplication fileContents marker 2 1 "y" - - [] - let ``qualified function application``() : unit = - let fileContents = """ -module M = - let f x = x -M.f - """ - let marker = "M.f " - assertSignatureHelpForFunctionApplication fileContents marker 1 0 "x" - - [] - let ``function application in single pipeline with no additional args``() : unit = - let fileContents = """ -[1..10] |> id - """ - let marker = "id " - let caretPosition = fileContents.IndexOf(marker) + marker.Length - - let document, sourceText = RoslynTestHelpers.CreateSingleDocumentSolution(filePath, fileContents) - - let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync("function application in single pipeline with no additional args") - |> Async.RunSynchronously - - let adjustedColumnInSource = - let rec loop ch pos = - if Char.IsWhiteSpace(ch) then - loop sourceText.[pos - 1] (pos - 1) - else - pos - loop sourceText.[caretPosition - 1] (caretPosition - 1) - - let sigHelp = - FSharpSignatureHelpProvider.ProvideParametersAsyncAux( - parseResults, - checkFileResults, - document.Id, - [], - DefaultDocumentationProvider, - sourceText, - caretPosition, - adjustedColumnInSource, - filePath) - |> Async.RunSynchronously - - checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - - Assert.True(sigHelp.IsNone, "No signature help is expected because there are no additional args to apply.") - - [] - let ``function application in single pipeline with an additional argument``() : unit = - let fileContents = """ -[1..10] |> List.map - """ - let marker = "List.map " - assertSignatureHelpForFunctionApplication fileContents marker 1 0 "mapping" - - [] - let ``function application in middle of pipeline with an additional argument``() : unit = - let fileContents = """ -[1..10] -|> List.map -|> List.filer (fun x -> x > 3) - """ - let marker = "List.map " - assertSignatureHelpForFunctionApplication fileContents marker 1 0 "mapping" - - [] - let ``function application with function as parameter``() : unit = - let fileContents = """ -let derp (f: int -> int -> int) x = f x 1 -derp -""" - let marker = "derp " - assertSignatureHelpForFunctionApplication fileContents marker 2 0 "f" - - [] - let ``function application with function as second parameter 1``() : unit = - let fileContents = """ -let derp (f: int -> int -> int) x = f x 1 -let add x y = x + y -derp add -""" - let marker = "derp add " - assertSignatureHelpForFunctionApplication fileContents marker 2 1 "x" - - [] - let ``function application with function as second parameter 2``() : unit = - let fileContents = """ -let f (derp: int -> int) x = derp x -""" - let marker = "derp " - assertSignatureHelpForFunctionApplication fileContents marker 1 0 "arg0" - - [] - let ``function application with curried function as parameter``() : unit = - let fileContents = """ -let f (derp: int -> int -> int) x = derp x -""" - let marker = "derp x " - assertSignatureHelpForFunctionApplication fileContents marker 2 1 "arg1" - -// migrated from legacy test -[] -let ``Multi.ReferenceToProjectLibrary``() : unit = - let completionNames = GetCompletionTypeNamesFromXmlString @" - - - - HelperLibrary.fsproj - - - - - - - - - - - - -" - let expected = [| - [|"System.Int32"; "System.Int32"|] - |] - Assert.AreEqual(expected, completionNames) diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index da0451837a6..25ba0ce68dc 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -5,18 +5,12 @@ open System.IO open System.Text open System.Reflection open System.Linq -open System.Composition.Hosting open System.Collections.Generic -open System.Collections.Immutable open Microsoft.VisualStudio.Composition open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Host -open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis.Host.Mef -open Microsoft.VisualStudio.LanguageServices -open Microsoft.VisualStudio.Shell -open FSharp.Compiler.CodeAnalysis [] module MefHelpers = @@ -227,105 +221,3 @@ type RoslynTestHelpers private () = filePath = projFilePath ) - static member CreateSingleDocumentSolution (filePath, text: SourceText, ?options: FSharpProjectOptions) = - let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) - - let workspace = new AdhocWorkspace(TestHostServices()) - - let projId = ProjectId.CreateNewId() - let docId = DocumentId.CreateNewId(projId) - - let docInfo = - DocumentInfo.Create( - docId, - filePath, - loader=TextLoader.From(text.Container, VersionStamp.Create(DateTime.UtcNow)), - filePath=filePath, - sourceCodeKind= if isScript then SourceCodeKind.Script else SourceCodeKind.Regular) - - let projFilePath = "C:\\test.fsproj" - let projInfo = - ProjectInfo.Create( - projId, - VersionStamp.Create(DateTime.UtcNow), - projFilePath, - "test.dll", - LanguageNames.FSharp, - documents = [docInfo], - filePath = projFilePath - ) - - let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo]) - - let solution = workspace.AddSolution(solutionInfo) - - let workspaceService = workspace.Services.GetService() - - let document = solution.GetProject(projId).GetDocument(docId) - - match options with - | Some options -> - let options = { options with ProjectId = Some(Guid.NewGuid().ToString()) } - workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, options.SourceFiles, options.OtherOptions |> ImmutableArray.CreateRange) - document.SetFSharpProjectOptionsForTesting(options) - | _ -> - workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, [|filePath|], ImmutableArray.Empty) - - 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 6889c747714..8bfcdc25ff1 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -60,75 +60,6 @@ CompilerService\UnusedOpensTests.fs - - Editor\ProjectOptionsBuilder.fs - - - Editor\SyntacticColorizationServiceTests.fs - - - Editor\SemanticColorizationServiceTests.fs - - - Editor\BraceMatchingServiceTests.fs - - - Editor\EditorFormattingServiceTests.fs - - - Editor\RoslynSourceTextTests.fs - - - Editor\IndentationServiceTests.fs - - - Editor\BreakpointResolutionService.fs - - - Editor\LanguageDebugInfoServiceTests.fs - - - Editor\DocumentDiagnosticAnalyzerTests.fs - - - Editor\CompletionProviderTests.fs - - - Editor\FsxCompletionProviderTests.fs - - - Editor\SignatureHelpProviderTests.fs - - - Editor\QuickInfoTests.fs - - - Editor\Hints\HintTestFramework.fs - - - Editor\Hints\InlineParameterNameHintTests.fs - - - Editor\Hints\InlineTypeHintTests.fs - - - Editor\Hints\OptionParserTests.fs - - - Editor\Hints\OverallHintExperienceTests.fs - - - Editor\GoToDefinitionServiceTests.fs - - - Editor\QuickInfoProviderTests.fs - - - Editor\HelpContextServiceTests.fs - - - Editor\DocumentHighlightsServiceTests.fs - {{FSCoreVersion}} $(FSCoreVersion)