From fff1c01aa06d905fa1e9f36dd5375061fac167ab Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Thu, 1 Feb 2018 12:58:30 -0800 Subject: [PATCH] Add Mac implementation of VisualStudioWorkspaceAccessor new APIs. - The Mac implementation does not have fallback logic like the windows variant because Workspaces in VS mac world are spun up / torn down regularly. We don't want to be tracking down bugs that involve us adding services or Razor logic to Workspaces that have nothing to do with Razor. - Added a test for the `DefaultVisualStudioWorkspaceAccessor`. Could not add other tests due to limitations of MonoDevelop's abstractions. #1989 --- .../DefaultVisualStudioWorkspaceAccessor.cs | 61 +++++++++++++++++-- ...efaultVisualStudioWorkspaceAccessorTest.cs | 33 ++++++++++ 2 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 test/Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs diff --git a/src/Microsoft.VisualStudio.Mac.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs b/src/Microsoft.VisualStudio.Mac.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs index 0fb24bc1bd..70cce87eae 100644 --- a/src/Microsoft.VisualStudio.Mac.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs +++ b/src/Microsoft.VisualStudio.Mac.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs @@ -1,11 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Editor.Razor; using Microsoft.VisualStudio.Text; using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Projects; +using Workspace = Microsoft.CodeAnalysis.Workspace; namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor { @@ -13,16 +15,63 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor [Export(typeof(VisualStudioWorkspaceAccessor))] internal class DefaultVisualStudioWorkspaceAccessor : VisualStudioWorkspaceAccessor { - public DefaultVisualStudioWorkspaceAccessor() + private readonly TextBufferProjectService _projectService; + + [ImportingConstructor] + public DefaultVisualStudioWorkspaceAccessor(TextBufferProjectService projectService) { - Workspace = TypeSystemService.Workspace; + if (projectService == null) + { + throw new ArgumentNullException(nameof(projectService)); + } + + _projectService = projectService; } - public override Workspace Workspace { get; } + public override Workspace Workspace => TypeSystemService.Workspace; public override bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace) { - throw new System.NotImplementedException(); + if (textBuffer == null) + { + throw new ArgumentNullException(nameof(textBuffer)); + } + + // We do a best effort approach in this method to get the workspace that belongs to the TextBuffer. + // Below we try and find the project and then the solution that contains the given text buffer. If + // we're able to find both the project and solution then we use the solution to look up the corresponding + // Workspace using MonoDevelops TypeSystemService. + + var hostProject = (DotNetProject)_projectService.GetHostProject(textBuffer); + if (hostProject == null) + { + // Does not have a host project. + workspace = null; + return false; + } + + var hostSolution = hostProject.ParentSolution; + if (hostSolution == null) + { + // Project does not have a solution + workspace = null; + return false; + } + + workspace = TypeSystemService.GetWorkspace(hostSolution); + + // Workspace cannot be null at this point. If TypeSystemService.GetWorkspace isn't able to find a corresponding + // workspace it returns an empty workspace. Therefore, in order to see if we have a valid workspace we need to + // cross-check it against the list of active non-empty workspaces. + + if (!TypeSystemService.AllWorkspaces.Contains(workspace)) + { + // We were returned the empty workspace which is equivalent to us not finding a valid workspace for our text buffer. + workspace = null; + return false; + } + + return true; } } -} \ No newline at end of file +} diff --git a/test/Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs b/test/Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs new file mode 100644 index 0000000000..551c25c6e8 --- /dev/null +++ b/test/Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.VisualStudio.Editor.Razor; +using Microsoft.VisualStudio.Text; +using Moq; +using Xunit; + +namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor +{ + public class DefaultVisualStudioWorkspaceAccessorTest + { + [Fact] + public void TryGetWorkspace_NoHostProject_ReturnsFalse() + { + // Arrange + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of()); + var textBuffer = Mock.Of(); + + // Act + var result = workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace); + + // Assert + Assert.False(result); + } + + // ------------------------------------------------------------------------------------------- + // Purposefully do not have any more tests here because that would involve mocking MonoDevelop + // types. The default constructors for the Solution / DotNetProject MonoDevelop types change + // static classes (they assume they're being created in an IDE). + // ------------------------------------------------------------------------------------------- + } +}