diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e29238876e9..9e8a716978b 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -363,9 +363,15 @@ stages:
displayName: Setup VS Hive
condition: or(eq(variables['_testKind'], 'testVs'), eq(variables['_testKind'], 'testIntegration'))
+ # yes, this is miserable, but - https://github.com/dotnet/arcade/issues/13239
- script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind)
displayName: Build / Test
- continueOnError: ${{ eq(variables['_testKind'], 'testIntegration') }}
+ condition: ne(variables['_testKind'], 'testIntegration')
+ - script: eng\CIBuild.cmd -compressallmetadata -configuration $(_configuration) -$(_testKind)
+ displayName: Build / Integration Test
+ continueOnError: true
+ condition: eq(variables['_testKind'], 'testIntegration')
+
- task: PublishTestResults@2
displayName: Publish Test Results
inputs:
diff --git a/eng/Versions.props b/eng/Versions.props
index 8cb0ebaf00a..9a57897d2ad 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -160,7 +160,7 @@
$(VisualStudioEditorPackagesVersion)
$(VisualStudioEditorPackagesVersion)
5.6.0
- 0.1.149-beta
+ 0.1.169-beta
$(MicrosoftVisualStudioExtensibilityTestingVersion)
$(MicrosoftVisualStudioExtensibilityTestingVersion)
diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/GoToDefinitionTests.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/GoToDefinitionTests.cs
index debbdf12029..66bb33eccd9 100644
--- a/vsintegration/tests/FSharp.Editor.IntegrationTests/GoToDefinitionTests.cs
+++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/GoToDefinitionTests.cs
@@ -35,4 +35,60 @@ module Test
Assert.Contains(expectedText, actualText);
}
+
+ [IdeFact]
+ public async Task FsiAndFsFilesGoToCorrespondentDefinitions()
+ {
+ var template = WellKnownProjectTemplates.FSharpNetCoreClassLibrary;
+
+ var fsi = """
+module Module
+
+type SomeType =
+| Number of int
+| Letter of char
+
+val id: t: SomeType -> SomeType
+""";
+ var fs = """
+module Module
+
+type SomeType =
+ | Number of int
+ | Letter of char
+
+let id (t: SomeType) = t
+""";
+
+ await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken);
+ await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken);
+
+ // hack: when asked to add a file, VS API seems to insert it in the alphabetical order
+ // so adding Module.fsi and Module.fs we'll end up having signature file below the code file
+ // and this won't work. But it's possible to achieve having the right file order via their renaming
+ await SolutionExplorer.AddFileAsync("Library", "AModule.fsi", fsi, TestToken);
+ await SolutionExplorer.AddFileAsync("Library", "Module.fs", fs, TestToken);
+ await SolutionExplorer.RenameFileAsync("Library", "AModule.fsi", "Module.fsi", TestToken);
+ await SolutionExplorer.BuildSolutionAsync(TestToken);
+
+ await SolutionExplorer.OpenFileAsync("Library", "Module.fsi", TestToken);
+ await Editor.PlaceCaretAsync("SomeType ->", TestToken);
+ await Shell.ExecuteCommandAsync(VSStd97CmdID.GotoDefn, TestToken);
+ var expectedText = "type SomeType =";
+ var expectedWindow = "Module.fsi";
+ var actualText = await Editor.GetCurrentLineTextAsync(TestToken);
+ var actualWindow = await Shell.GetActiveWindowCaptionAsync(TestToken);
+ Assert.Equal(expectedText, actualText);
+ Assert.Equal(expectedWindow, actualWindow);
+
+ await SolutionExplorer.OpenFileAsync("Library", "Module.fs", TestToken);
+ await Editor.PlaceCaretAsync("SomeType)", TestToken);
+ await Shell.ExecuteCommandAsync(VSStd97CmdID.GotoDefn, TestToken);
+ expectedText = "type SomeType =";
+ expectedWindow = "Module.fs";
+ actualText = await Editor.GetCurrentLineTextAsync(TestToken);
+ actualWindow = await Shell.GetActiveWindowCaptionAsync(TestToken);
+ Assert.Equal(expectedText, actualText);
+ Assert.Equal(expectedWindow, actualWindow);
+ }
}
\ No newline at end of file
diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
index 4597c3bffb7..e1836606f58 100644
--- a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
+++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs
@@ -35,25 +35,6 @@ public async Task CreateSolutionAsync(string solutionName, CancellationToken can
var solutionPath = CreateTemporaryPath();
await CreateSolutionAsync(solutionPath, solutionName, cancellationToken);
}
-
- public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken)
- {
- await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
-
- var filePath = await GetAbsolutePathForProjectRelativeFilePathAsync(projectName, relativeFilePath, cancellationToken);
- if (!File.Exists(filePath))
- {
- throw new FileNotFoundException(filePath);
- }
-
- VsShellUtilities.OpenDocument(ServiceProvider.GlobalProvider, filePath, VSConstants.LOGVIEWID.Code_guid, out _, out _, out _, out var view);
-
- // Reliably set focus using NavigateToLineAndColumn
- var textManager = await GetRequiredGlobalServiceAsync(cancellationToken);
- ErrorHandler.ThrowOnFailure(view.GetBuffer(out var textLines));
- ErrorHandler.ThrowOnFailure(view.GetCaretPos(out var line, out var column));
- ErrorHandler.ThrowOnFailure(textManager.NavigateToLineAndColumn(textLines, VSConstants.LOGVIEWID.Code_guid, line, column, line, column));
- }
private async Task CreateSolutionAsync(string solutionPath, string solutionName, CancellationToken cancellationToken)
{
@@ -207,19 +188,6 @@ private string CreateTemporaryPath()
return Path.Combine(Path.GetTempPath(), "fsharp-test", Path.GetRandomFileName());
}
- private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken)
- {
- await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
-
- var dte = await GetRequiredGlobalServiceAsync(cancellationToken);
- var solution = dte.Solution;
- Assumes.Present(solution);
-
- var project = solution.Projects.Cast().First(x => x.Name == projectName);
- var projectPath = Path.GetDirectoryName(project.FullName);
- return Path.Combine(projectPath, relativeFilePath);
- }
-
private async Task GetProjectAsync(string nameOrFileName, CancellationToken cancellationToken)
{
await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess_Files.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess_Files.cs
new file mode 100644
index 00000000000..f639decdb82
--- /dev/null
+++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess_Files.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.TextManager.Interop;
+using Microsoft.VisualStudio.Threading;
+using Task = System.Threading.Tasks.Task;
+
+namespace Microsoft.VisualStudio.Extensibility.Testing;
+
+internal partial class SolutionExplorerInProcess
+{
+ public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+
+ var filePath = await GetAbsolutePathForProjectRelativeFilePathAsync(projectName, relativeFilePath, cancellationToken);
+ if (!File.Exists(filePath))
+ {
+ throw new FileNotFoundException(filePath);
+ }
+
+ VsShellUtilities.OpenDocument(ServiceProvider.GlobalProvider, filePath, VSConstants.LOGVIEWID.Code_guid, out _, out _, out _, out var view);
+
+ // Reliably set focus using NavigateToLineAndColumn
+ var textManager = await GetRequiredGlobalServiceAsync(cancellationToken);
+ ErrorHandler.ThrowOnFailure(view.GetBuffer(out var textLines));
+ ErrorHandler.ThrowOnFailure(view.GetCaretPos(out var line, out var column));
+ ErrorHandler.ThrowOnFailure(textManager.NavigateToLineAndColumn(textLines, VSConstants.LOGVIEWID.Code_guid, line, column, line, column));
+ }
+
+ public async Task AddFileAsync(string projectName, string fileName, string contents, CancellationToken cancellationToken)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+
+ var project = await GetProjectAsync(projectName, cancellationToken);
+ var projectDirectory = Path.GetDirectoryName(project.FullName);
+ var filePath = Path.Combine(projectDirectory, fileName);
+ var directoryPath = Path.GetDirectoryName(filePath);
+
+ Directory.CreateDirectory(directoryPath);
+ File.WriteAllText(filePath, contents);
+
+ _ = project.ProjectItems.AddFromFile(filePath);
+ }
+
+ public async Task RenameFileAsync(string projectName, string oldFileName, string newFileName, CancellationToken cancellationToken)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+ var projectItem = await GetProjectItemAsync(projectName, oldFileName, cancellationToken);
+
+ projectItem.Name = newFileName;
+ }
+
+ private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+
+ var dte = await GetRequiredGlobalServiceAsync(cancellationToken);
+ var solution = dte.Solution;
+
+ var project = solution.Projects.Cast().First(x => x.Name == projectName);
+ var projectPath = Path.GetDirectoryName(project.FullName);
+ return Path.Combine(projectPath, relativeFilePath);
+ }
+
+ private async Task GetProjectItemAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken)
+ {
+ await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+
+ var solution = (await GetRequiredGlobalServiceAsync(cancellationToken)).Solution;
+ var projects = solution.Projects.Cast();
+ var project = projects.FirstOrDefault(x => x.Name == projectName);
+ var projectPath = Path.GetDirectoryName(project.FullName);
+ var fullFilePath = Path.Combine(projectPath, relativeFilePath);
+ var projectItems = project.ProjectItems.Cast();
+ var document = projectItems.FirstOrDefault(d => d.get_FileNames(1).Equals(fullFilePath));
+
+ return document;
+ }
+}