From d87e0f7fbd26b83fd90d383c9dfb47892ffed8aa Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 23 Aug 2017 09:01:50 -0700 Subject: [PATCH] Fixes #1632 - track textviews in the document tracker I've stripped out some of the dead code and complexity from the document tracker in an attempt to simplify it. I will bring this back as part of the multi-targeting work. --- .../RazorDocumentTracker.cs | 21 -- .../BufferGraphExtensions.cs | 22 ++ .../DefaultTextViewRazorDocumentTracker.cs | 267 ------------------ ...aultTextViewRazorDocumentTrackerService.cs | 177 ------------ .../Editor/DefaultTextBufferProjectService.cs | 74 +++++ .../DefaultVisualStudioDocumentTracker.cs | 108 +++++++ ...faultVisualStudioDocumentTrackerFactory.cs | 139 +++++++++ .../Editor/TextBufferProjectService.cs | 15 + .../Editor/VisualStudioDocumentTracker.cs | 26 ++ .../VisualStudioDocumentTrackerFactory.cs | 12 + .../Editor/VisualStudioRazorParser.cs | 3 +- ...VisualStudio.LanguageServices.Razor.csproj | 1 + .../TextBufferExtensions.cs | 21 ++ .../TextBufferProjectService.cs | 15 + .../TextViewRazorDocumentTrackerService.cs | 13 - ...tVisualStudioDocumentTrackerFactoryTest.cs | 258 +++++++++++++++++ ...lStudio.LanguageServices.Razor.Test.csproj | 6 + .../RazorDocumentInfoViewModel.cs | 10 +- .../DocumentInfo/RazorDocumentInfoWindow.cs | 10 +- .../RazorDocumentInfoWindowControl.xaml | 25 +- ...crosoft.VisualStudio.RazorExtension.csproj | 1 + 21 files changed, 711 insertions(+), 513 deletions(-) delete mode 100644 src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/BufferGraphExtensions.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTrackerService.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTracker.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTrackerFactory.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferExtensions.cs create mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs delete mode 100644 src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs create mode 100644 test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerFactoryTest.cs diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs deleted file mode 100644 index 1d6e60b698..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Razor -{ - public abstract class RazorDocumentTracker - { - public abstract event EventHandler ContextChanged; - - public abstract bool IsSupportedDocument { get; } - - public abstract ProjectId ProjectId { get; } - - public abstract SourceTextContainer TextContainer { get; } - - public abstract Workspace Workspace { get; } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/BufferGraphExtensions.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/BufferGraphExtensions.cs new file mode 100644 index 0000000000..1d16e0f082 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/BufferGraphExtensions.cs @@ -0,0 +1,22 @@ +// 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.Collections.ObjectModel; +using Microsoft.VisualStudio.Text.Projection; + +namespace Microsoft.VisualStudio.Text +{ + internal static class BufferGraphExtensions + { + public static Collection GetRazorBuffers(this IBufferGraph bufferGraph) + { + if (bufferGraph == null) + { + throw new ArgumentNullException(nameof(bufferGraph)); + } + + return bufferGraph.GetTextBuffers(TextBufferExtensions.IsRazorBuffer); + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs deleted file mode 100644 index 3e50057051..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs +++ /dev/null @@ -1,267 +0,0 @@ -// 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.Diagnostics; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Projection; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - internal class DefaultTextViewRazorDocumentTracker : RazorDocumentTracker - { - private readonly DefaultTextViewRazorDocumentTrackerService _service; - private readonly ITextView _textView; - - private DocumentId _documentId; - private ITextBuffer _razorBuffer; - private bool _isSupportedProject; - private ITextBuffer _cSharpBuffer; - private SourceTextContainer _textContainer; - private Workspace _workspace; - private WorkspaceRegistration _workspaceRegistration; - - public override event EventHandler ContextChanged; - - public DefaultTextViewRazorDocumentTracker(DefaultTextViewRazorDocumentTrackerService service, ITextView textView) - { - if (service == null) - { - throw new ArgumentNullException(nameof(service)); - } - - if (textView == null) - { - throw new ArgumentNullException(nameof(textView)); - } - - _service = service; - _textView = textView; - - Update(); - - _textView.Closed += TextView_Closed; - - _textView.BufferGraph.GraphBufferContentTypeChanged += BufferGraph_GraphBufferContentTypeChanged; - _textView.BufferGraph.GraphBuffersChanged += BufferGraph_GraphBuffersChanged; - } - - public override bool IsSupportedDocument => _isSupportedProject && _textContainer != null; - - public override ProjectId ProjectId => _documentId?.ProjectId; - - public override SourceTextContainer TextContainer => _textContainer; - - public override Workspace Workspace => _workspace; - - private bool Update() - { - // Update is called when the state of any of our surrounding systems changes. Here we want to examine the - // state of the world and then update properties as necessary. - // - // Fundamentally we have a Razor half of the world as as soon as the document is open - and then later - // the C# half of the world will be initialized. This code is in general pretty tolerant of - // unexpected /impossible states. - // - // We also want to successfully shut down when the buffer is renamed to something other .cshtml. - - // If there's no Razor buffer, then there's really nothing to do. This isn't a Razor file. - var razorBuffer = _textView.BufferGraph.GetTextBuffers(IsRazorBuffer).FirstOrDefault(); - - // The source container is tied to the Razor buffer since that's what we want to watch for changes. - SourceTextContainer textContainer = null; - if (razorBuffer != null) - { - textContainer = razorBuffer.AsTextContainer(); - } - - bool isSupportedProject = false; - if (razorBuffer != null) - { - // The project can be null when the document is in the process of closing. - var project = _service.GetProject(razorBuffer); - - if (project != null) - { - isSupportedProject = _service.IsSupportedProject(project); - } - } - - // The C# buffer is the other buffer in the graph that is C# (only if we already found a Razor buffer). - // We're not currently validating that it's a child of the Razor buffer even though we expect that. - ITextBuffer cSharpBuffer = null; - if (razorBuffer != null) - { - cSharpBuffer = _textView.BufferGraph.GetTextBuffers(IsCSharpBuffer).FirstOrDefault(); - } - - // Now if we have a C# buffer we want to watch for it be attached to a workspace. - SourceTextContainer cSharpTextContainer = null; - WorkspaceRegistration workspaceRegistration = null; - if (cSharpBuffer != null) - { - cSharpTextContainer = cSharpBuffer.AsTextContainer(); - Debug.Assert(cSharpTextContainer != null); - - workspaceRegistration = Workspace.GetWorkspaceRegistration(cSharpTextContainer); - } - - // Now finally we can see if we have a workspace - Workspace workspace = null; - if (workspaceRegistration != null) - { - workspace = workspaceRegistration.Workspace; - } - - // Now we know if the Roslyn state for this document has been initialized, let's look for a project. - DocumentId documentId = null; - if (cSharpTextContainer != null && workspace != null) - { - documentId = workspace.GetDocumentIdInCurrentContext(cSharpTextContainer); - } - - // As a special case, we want to default to the VisualStudioWorkspace until we find out otherwise - // This lets us start working before a project gets initialized. - if (isSupportedProject && workspace == null) - { - workspace = _service.Workspace; - } - - var changed = false; - changed |= razorBuffer == _razorBuffer; - changed |= textContainer == _textContainer; - changed |= isSupportedProject == _isSupportedProject; - changed |= cSharpBuffer == _cSharpBuffer; - changed |= workspaceRegistration == _workspaceRegistration; - changed |= workspace == _workspace; - changed |= documentId == _documentId; - - // Now if nothing has changed we're all done! - if (!changed) - { - return false; - } - - // OK, so something did change, let's commit the changes. - // - // These are all of the straightforward ones. - _razorBuffer = razorBuffer; - _textContainer = textContainer; - _isSupportedProject = isSupportedProject; - _cSharpBuffer = cSharpBuffer; - _documentId = documentId; - - // Now these ones we subscribe to events so it's a little tricky. - if (_workspaceRegistration != null) - { - _workspaceRegistration.WorkspaceChanged -= WorkspaceRegistration_WorkspaceChanged; - } - if (workspaceRegistration != null) - { - workspaceRegistration.WorkspaceChanged += WorkspaceRegistration_WorkspaceChanged; - } - - if (_workspace != null) - { - _workspace.DocumentActiveContextChanged -= Workspace_DocumentActiveContextChanged; - } - if (workspace != null) - { - workspace.DocumentActiveContextChanged += Workspace_DocumentActiveContextChanged; - } - - _workspaceRegistration = workspaceRegistration; - _workspace = workspace; - - return true; - } - - private static bool IsCSharpBuffer(ITextBuffer textBuffer) - { - return textBuffer.ContentType.IsOfType("CSharp"); - } - - private static bool IsRazorBuffer(ITextBuffer textBuffer) - { - return textBuffer.ContentType.IsOfType(RazorLanguage.ContentType); - } - - private void BufferGraph_GraphBuffersChanged(object sender, GraphBuffersChangedEventArgs e) - { - if (Update()) - { - OnContextChanged(); - } - } - - private void BufferGraph_GraphBufferContentTypeChanged(object sender, GraphBufferContentTypeChangedEventArgs e) - { - if (Update()) - { - OnContextChanged(); - } - } - - private void WorkspaceRegistration_WorkspaceChanged(object sender, EventArgs e) - { - if (Update()) - { - OnContextChanged(); - } - } - - private void Workspace_DocumentActiveContextChanged(object sender, DocumentActiveContextChangedEventArgs e) - { - var textBuffer = e.SourceTextContainer.GetTextBuffer(); - if (textBuffer != null && (textBuffer == _cSharpBuffer || textBuffer == _razorBuffer)) - { - if (Update()) - { - OnContextChanged(); - } - } - } - - private void TextView_Closed(object sender, EventArgs e) - { - _textView.BufferGraph.GraphBufferContentTypeChanged -= BufferGraph_GraphBufferContentTypeChanged; - _textView.BufferGraph.GraphBuffersChanged -= BufferGraph_GraphBuffersChanged; - - if (_workspaceRegistration != null) - { - _workspaceRegistration.WorkspaceChanged -= WorkspaceRegistration_WorkspaceChanged; - } - - if (_workspace != null) - { - _workspace.DocumentActiveContextChanged -= Workspace_DocumentActiveContextChanged; - } - - _textView.Closed -= TextView_Closed; - - _razorBuffer = null; - _textContainer = null; - _isSupportedProject = false; - _cSharpBuffer = null; - _workspaceRegistration = null; - _workspace = null; - _documentId = null; - - OnContextChanged(); - } - - private void OnContextChanged() - { - var handler = ContextChanged; - if (handler != null) - { - handler(this, EventArgs.Empty); - } - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTrackerService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTrackerService.cs deleted file mode 100644 index 1d175017d1..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTrackerService.cs +++ /dev/null @@ -1,177 +0,0 @@ -// 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.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel.Composition; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - [ContentType(RazorLanguage.ContentType)] - [TextViewRole(PredefinedTextViewRoles.Editable)] - [Export(typeof(IWpfTextViewConnectionListener))] - [Export(typeof(TextViewRazorDocumentTrackerService))] - internal class DefaultTextViewRazorDocumentTrackerService : TextViewRazorDocumentTrackerService, IWpfTextViewConnectionListener - { - private readonly ITextDocumentFactoryService _documentFactory; - private readonly IServiceProvider _services; - private readonly Workspace _workspace; - - private RunningDocumentTable _runningDocumentTable; - private IVsSolution _solution; - - [ImportingConstructor] - public DefaultTextViewRazorDocumentTrackerService( - [Import(typeof(SVsServiceProvider))] IServiceProvider services, - ITextDocumentFactoryService documentFactory, - [Import(typeof(VisualStudioWorkspace))] Workspace workspace) - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (documentFactory == null) - { - throw new ArgumentNullException(nameof(documentFactory)); - } - - if (workspace == null) - { - throw new ArgumentNullException(nameof(workspace)); - } - - _services = services; - _documentFactory = documentFactory; - _workspace = workspace; - } - - // Lazy to avoid needing this in unit tests. - private RunningDocumentTable RunningDocumentTable - { - get - { - if (_runningDocumentTable == null) - { - _runningDocumentTable = new RunningDocumentTable(_services); - } - - return _runningDocumentTable; - } - } - - // Lazy to avoid needing this in unit tests. - private IVsSolution Solution - { - get - { - if (_solution == null) - { - _solution = (IVsSolution)_services.GetService(typeof(SVsSolution)); - } - - return _solution; - } - } - - public Workspace Workspace => _workspace; - - public override RazorDocumentTracker CreateTracker(ITextView textView) - { - if (textView == null) - { - throw new ArgumentNullException(nameof(textView)); - } - - if (!textView.Properties.TryGetProperty(typeof(RazorDocumentTracker), out var tracker)) - { - tracker = new DefaultTextViewRazorDocumentTracker(this, textView); - textView.Properties.AddProperty(typeof(RazorDocumentTracker), tracker); - } - - return tracker; - } - - public void SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) - { - if (textView == null) - { - throw new ArgumentException(nameof(textView)); - } - - if (subjectBuffers == null) - { - throw new ArgumentNullException(nameof(subjectBuffers)); - } - - // This means a Razor buffer has been attached to this ITextView or the ITextView is just opening with Razor content. - // - // Call CreateTracker just for the side effect. The tracker will do all of the real work after it's initialized. - CreateTracker(textView); - } - - public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) - { - if (textView == null) - { - throw new ArgumentException(nameof(textView)); - } - - if (subjectBuffers == null) - { - throw new ArgumentNullException(nameof(subjectBuffers)); - } - - // This means a Razor buffer has be detached from this ITextView or the ITextView is closing. - // - // Do nothing, the tracker will update itself. - } - - public virtual IVsHierarchy GetProject(ITextBuffer textBuffer) - { - if (textBuffer == null) - { - throw new ArgumentNullException(nameof(textBuffer)); - } - - // If there's no document we can't find the FileName, or look for a matching hierarchy. - if (!_documentFactory.TryGetTextDocument(textBuffer, out var textDocument)) - { - return null; - } - - RunningDocumentTable.FindDocument(textDocument.FilePath, out var hierarchy, out uint itemId, out uint cookie); - - // We don't currently try to look a Roslyn ProjectId at this point, we just want to know some - // basic things. - // See https://github.com/dotnet/roslyn/blob/4e3db2b7a0732d45a720e9ed00c00cd22ab67a14/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs#L47 - // for a more complete implementation. - return hierarchy; - } - - public virtual bool IsSupportedProject(IVsHierarchy project) - { - if (project == null) - { - throw new ArgumentNullException(nameof(project)); - } - - return project.IsCapabilityMatch("DotNetCoreWeb"); - } - - public static IEnumerable GetTextViews(ITextBuffer textBuffer) - { - // TODO: Extract text views from buffer - - return new[] { (ITextView)null }; - } - } -} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs new file mode 100644 index 0000000000..462ef99d97 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs @@ -0,0 +1,74 @@ +// 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.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + /// + /// Infrastructure methods to find project information from an . + /// + [Export(typeof(TextBufferProjectService))] + internal class DefaultTextBufferProjectService : TextBufferProjectService + { + private readonly RunningDocumentTable _documentTable; + private readonly ITextDocumentFactoryService _documentFactory; + + [ImportingConstructor] + public DefaultTextBufferProjectService( + [Import(typeof(SVsServiceProvider))] IServiceProvider services, + ITextDocumentFactoryService documentFactory, + [Import(typeof(VisualStudioWorkspace))] Workspace workspace) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (documentFactory == null) + { + throw new ArgumentNullException(nameof(documentFactory)); + } + + _documentFactory = documentFactory; + _documentTable = new RunningDocumentTable(services); + } + + public override IVsHierarchy GetHierarchy(ITextBuffer textBuffer) + { + if (textBuffer == null) + { + throw new ArgumentNullException(nameof(textBuffer)); + } + + // If there's no document we can't find the FileName, or look for a matching hierarchy. + if (!_documentFactory.TryGetTextDocument(textBuffer, out var textDocument)) + { + return null; + } + + _documentTable.FindDocument(textDocument.FilePath, out var hierarchy, out uint itemId, out uint cookie); + + // We don't currently try to look a Roslyn ProjectId at this point, we just want to know some + // basic things. + // See https://github.com/dotnet/roslyn/blob/4e3db2b7a0732d45a720e9ed00c00cd22ab67a14/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs#L47 + // for a more complete implementation. + return hierarchy; + } + + public override bool IsSupportedProject(IVsHierarchy hierarchy) + { + if (hierarchy == null) + { + throw new ArgumentNullException(nameof(hierarchy)); + } + + return hierarchy.IsCapabilityMatch("DotNetCoreWeb"); + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs new file mode 100644 index 0000000000..2e673956e5 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs @@ -0,0 +1,108 @@ +// 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.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + internal class DefaultVisualStudioDocumentTracker : VisualStudioDocumentTracker + { + private readonly TextBufferProjectService _projectService; + private readonly ITextBuffer _textBuffer; + private readonly List _textViews; + + private bool _isSupportedProject; + private Workspace _workspace; + + public override event EventHandler ContextChanged; + + public DefaultVisualStudioDocumentTracker(TextBufferProjectService projectService, Workspace workspace, ITextBuffer textBuffer) + { + if (projectService == null) + { + throw new ArgumentNullException(nameof(projectService)); + } + + if (workspace == null) + { + throw new ArgumentNullException(nameof(workspace)); + } + + if (textBuffer == null) + { + throw new ArgumentNullException(nameof(textBuffer)); + } + + _projectService = projectService; + _textBuffer = textBuffer; + _workspace = workspace; + + _textViews = new List(); + + Update(); + } + + public override bool IsSupportedProject => _isSupportedProject; + + public override ProjectId ProjectId => null; + + public override ITextBuffer TextBuffer => _textBuffer; + + public override IReadOnlyList TextViews => _textViews; + + public IList TextViewsInternal => _textViews; + + public override Workspace Workspace => _workspace; + + private bool Update() + { + // Update is called when the state of any of our surrounding systems changes. Here we want to examine the + // state of the world and then update properties as necessary. + // + // Fundamentally we have a Razor half of the world as as soon as the document is open - and then later + // the C# half of the world will be initialized. This code is in general pretty tolerant of + // unexpected /impossible states. + // + // We also want to successfully shut down when the buffer is renamed to something other .cshtml. + IVsHierarchy project = null; + var isSupportedProject = false; + if (_textBuffer.ContentType.IsOfType(RazorLanguage.ContentType) && + (project = _projectService.GetHierarchy(_textBuffer)) != null) + { + // We expect the document to have a hierarchy even if it's not a real 'project'. + // However the hierarchy can be null when the document is in the process of closing. + isSupportedProject = _projectService.IsSupportedProject(project); + } + + // For now we temporarily assume that the workspace is the default VS workspace. + var workspace = _workspace; + + var changed = false; + changed |= isSupportedProject == _isSupportedProject; + changed |= workspace == _workspace; + + if (changed) + { + _isSupportedProject = isSupportedProject; + _workspace = workspace; + } + + return changed; + } + + private void OnContextChanged() + { + var handler = ContextChanged; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs new file mode 100644 index 0000000000..68bce7a1c2 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs @@ -0,0 +1,139 @@ +// 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.Collections.ObjectModel; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Projection; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + [ContentType(RazorLanguage.ContentType)] + [TextViewRole(PredefinedTextViewRoles.Document)] + [Export(typeof(IWpfTextViewConnectionListener))] + [Export(typeof(VisualStudioDocumentTrackerFactory))] + internal class DefaultVisualStudioDocumentTrackerFactory : VisualStudioDocumentTrackerFactory, IWpfTextViewConnectionListener + { + private readonly TextBufferProjectService _projectService; + private readonly Workspace _workspace; + + [ImportingConstructor] + public DefaultVisualStudioDocumentTrackerFactory( + TextBufferProjectService projectService, + [Import(typeof(VisualStudioWorkspace))] Workspace workspace) + { + if (projectService == null) + { + throw new ArgumentNullException(nameof(projectService)); + } + + if (workspace == null) + { + throw new ArgumentNullException(nameof(workspace)); + } + + _projectService = projectService; + _workspace = workspace; + } + + public Workspace Workspace => _workspace; + + public override VisualStudioDocumentTracker GetTracker(ITextView textView) + { + if (textView == null) + { + throw new ArgumentNullException(nameof(textView)); + } + + // While it's definitely possible to have multiple Razor text buffers attached to the same text view, there's + // no real scenario for it. This method always returns the tracker for the first Razor text buffer, but the + // other functionality for this class will maintain state correctly for the other buffers. + var textBuffer = textView.BufferGraph.GetRazorBuffers().FirstOrDefault(); + if (textBuffer == null) + { + // No Razor buffer, nothing to track. + return null; + } + + // A little bit of hardening here, to make sure our assumptions are correct. + DefaultVisualStudioDocumentTracker tracker; + if (!textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out tracker)) + { + Debug.Fail("The document tracker should be initialized"); + } + + Debug.Assert(tracker.TextViewsInternal.Contains(textView)); + return tracker; + } + + public void SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) + { + if (textView == null) + { + throw new ArgumentException(nameof(textView)); + } + + if (subjectBuffers == null) + { + throw new ArgumentNullException(nameof(subjectBuffers)); + } + + for (var i = 0; i < subjectBuffers.Count; i++) + { + var textBuffer = subjectBuffers[i]; + if (!textBuffer.IsRazorBuffer()) + { + continue; + } + + DefaultVisualStudioDocumentTracker tracker; + if (!textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out tracker)) + { + tracker = new DefaultVisualStudioDocumentTracker(_projectService, _workspace, textBuffer); + textBuffer.Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + } + + if (!tracker.TextViewsInternal.Contains(textView)) + { + tracker.TextViewsInternal.Add(textView); + } + } + } + + public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) + { + if (textView == null) + { + throw new ArgumentException(nameof(textView)); + } + + if (subjectBuffers == null) + { + throw new ArgumentNullException(nameof(subjectBuffers)); + } + + // This means a Razor buffer has be detached from this ITextView or the ITextView is closing. Since we keep a + // list of all of the open text views for each text buffer, we need to update the tracker. + // + // Notice that this method is called *after* changes are applied to the text buffer(s). We need to check every + // one of them for a tracker because the content type could have changed. + for (var i = 0; i < subjectBuffers.Count; i++) + { + var textBuffer = subjectBuffers[i]; + + DefaultVisualStudioDocumentTracker tracker; + if (textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out tracker)) + { + tracker.TextViewsInternal.Remove(textView); + } + } + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs new file mode 100644 index 0000000000..e85298a376 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs @@ -0,0 +1,15 @@ +// 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.Shell.Interop; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + internal abstract class TextBufferProjectService + { + public abstract IVsHierarchy GetHierarchy(ITextBuffer textBuffer); + + public abstract bool IsSupportedProject(IVsHierarchy hierarchy); + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTracker.cs new file mode 100644 index 0000000000..97ab37285a --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTracker.cs @@ -0,0 +1,26 @@ +// 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.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + public abstract class VisualStudioDocumentTracker + { + public abstract event EventHandler ContextChanged; + + public abstract bool IsSupportedProject { get; } + + public abstract ProjectId ProjectId { get; } + + public abstract Workspace Workspace { get; } + + public abstract ITextBuffer TextBuffer { get; } + + public abstract IReadOnlyList TextViews { get; } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTrackerFactory.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTrackerFactory.cs new file mode 100644 index 0000000000..7e6fcb01d7 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioDocumentTrackerFactory.cs @@ -0,0 +1,12 @@ +// 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.Text.Editor; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + public abstract class VisualStudioDocumentTrackerFactory + { + public abstract VisualStudioDocumentTracker GetTracker(ITextView textView); + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioRazorParser.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioRazorParser.cs index 94f7935395..ce92f7f227 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioRazorParser.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/VisualStudioRazorParser.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Legacy; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; using ITextBuffer = Microsoft.VisualStudio.Text.ITextBuffer; using Timer = System.Timers.Timer; @@ -145,7 +146,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor { _foregroundThreadAffinitizedObject.AssertIsBackground(); - var textViews = DefaultTextViewRazorDocumentTrackerService.GetTextViews(_textBuffer); + var textViews = Array.Empty(); foreach (var textView in textViews) { diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj b/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj index 174f6490f8..f0ac323f47 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferExtensions.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferExtensions.cs new file mode 100644 index 0000000000..4fff0bea0f --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferExtensions.cs @@ -0,0 +1,21 @@ +// 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 Microsoft.CodeAnalysis.Razor; + +namespace Microsoft.VisualStudio.Text +{ + internal static class TextBufferExtensions + { + public static bool IsRazorBuffer(this ITextBuffer textBuffer) + { + if (textBuffer == null) + { + throw new ArgumentNullException(nameof(textBuffer)); + } + + return textBuffer.ContentType.IsOfType(RazorLanguage.ContentType); + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs new file mode 100644 index 0000000000..902b8b4a68 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs @@ -0,0 +1,15 @@ +// 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.Shell.Interop; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.LanguageServices.Razor +{ + internal abstract class TextBufferProjectService + { + public abstract IVsHierarchy GetHierarchy(ITextBuffer textBuffer); + + public abstract bool IsSupportedProject(IVsHierarchy hierarchy); + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs deleted file mode 100644 index f0b944161a..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs +++ /dev/null @@ -1,13 +0,0 @@ -// 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.CodeAnalysis.Razor; -using Microsoft.VisualStudio.Text.Editor; - -namespace Microsoft.VisualStudio.LanguageServices.Razor -{ - public abstract class TextViewRazorDocumentTrackerService - { - public abstract RazorDocumentTracker CreateTracker(ITextView textView); - } -} diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerFactoryTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerFactoryTest.cs new file mode 100644 index 0000000000..c7d4ada5af --- /dev/null +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerFactoryTest.cs @@ -0,0 +1,258 @@ +// 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.Collections.ObjectModel; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Projection; +using Microsoft.VisualStudio.Utilities; +using Moq; +using Xunit; + +namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +{ + public class DefaultVisualStudioDocumentTrackerFactoryTest + { + private TextBufferProjectService ProjectService { get; } = Mock.Of( + s => s.GetHierarchy(It.IsAny()) == Mock.Of() && + s.IsSupportedProject(It.IsAny()) == true); + + private Workspace Workspace { get; } = new AdhocWorkspace(); + + private IContentType RazorContentType { get; } = Mock.Of(c => c.IsOfType(RazorLanguage.ContentType) == true); + + private IContentType NonRazorContentType { get; } = Mock.Of(c => c.IsOfType(It.IsAny()) == false); + + [Fact] + public void SubjectBuffersConnected_ForNonRazorTextBuffer_DoesNothing() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); + + // Assert + Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + } + + [Fact] + public void SubjectBuffersConnected_ForRazorTextBufferWithoutTracker_CreatesTrackerAndTracksTextView() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); + + // Assert + var tracker = buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView)); + Assert.Equal(buffers[0], tracker.TextBuffer); + } + + [Fact] + public void SubjectBuffersConnected_ForRazorTextBufferWithoutTracker_CreatesTrackerAndTracksTextView_ForMultipleBuffers() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); + + // Assert + var tracker = buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView)); + Assert.Equal(buffers[0], tracker.TextBuffer); + + Assert.False(buffers[1].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + + tracker = buffers[2].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView)); + Assert.Equal(buffers[2], tracker.TextBuffer); + } + + [Fact] + public void SubjectBuffersConnected_ForRazorTextBufferWithTracker_DoesNotAddDuplicateTextViewEntry() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. + var tracker = new DefaultVisualStudioDocumentTracker(ProjectService, Workspace, buffers[0]); + tracker.TextViewsInternal.Add(textView); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + + // Act + factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); + + // Assert + Assert.Same(tracker, buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker))); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView)); + } + + [Fact] + public void SubjectBuffersConnected_ForRazorTextBufferWithTracker_AddsEntryForADifferentTextView() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView1 = Mock.Of(); + var textView2 = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. + var tracker = new DefaultVisualStudioDocumentTracker(ProjectService, Workspace, buffers[0]); + tracker.TextViewsInternal.Add(textView1); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + + // Act + factory.SubjectBuffersConnected(textView2, ConnectionReason.BufferGraphChange, buffers); + + // Assert + Assert.Same(tracker, buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker))); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1), v => Assert.Same(v, textView2)); + } + + [Fact] + public void SubjectBuffersDisconnected_ForAnyTextBufferWithTracker_RemovesTextView() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView1 = Mock.Of(); + var textView2 = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + + // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. + var tracker = new DefaultVisualStudioDocumentTracker(ProjectService, Workspace, buffers[0]); + tracker.TextViewsInternal.Add(textView1); + tracker.TextViewsInternal.Add(textView2); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + + tracker = new DefaultVisualStudioDocumentTracker(ProjectService, Workspace, buffers[1]); + tracker.TextViewsInternal.Add(textView1); + tracker.TextViewsInternal.Add(textView2); + buffers[1].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + + // Act + factory.SubjectBuffersDisconnected(textView2, ConnectionReason.BufferGraphChange, buffers); + + // Assert + tracker = buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1)); + + tracker = buffers[1].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1)); + } + + [Fact] + public void SubjectBuffersDisconnected_ForAnyTextBufferWithoutTracker_DoesNothing() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var textView = Mock.Of(); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + factory.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers); + + // Assert + Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + } + + [Fact] + public void GetTracker_ForRazorTextBufferWithTracker_ReturnsTheFirstTracker() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + var bufferGraph = Mock.Of(g => g.GetTextBuffers(It.IsAny>()) == buffers); + + var textView = Mock.Of(v => v.BufferGraph == bufferGraph); + + // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. + var tracker = new DefaultVisualStudioDocumentTracker(ProjectService, Workspace, buffers[0]); + tracker.TextViewsInternal.Add(textView); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); + + // Act + var result = factory.GetTracker(textView); + + // Assert + Assert.Same(tracker, result); + } + + [Fact] + public void GetTracker_WithoutRazorBuffer_ReturnsNull() + { + // Arrange + var factory = new DefaultVisualStudioDocumentTrackerFactory(ProjectService, Workspace); + + var buffers = new Collection(); + + var bufferGraph = Mock.Of(g => g.GetTextBuffers(It.IsAny>()) == buffers); + + var textView = Mock.Of(v => v.BufferGraph == bufferGraph); + + // Act + var result = factory.GetTracker(textView); + + // Assert + Assert.Null(result); + } + } +} diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj index b980d10aed..7d3f1f9c1b 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj @@ -3,6 +3,10 @@ net461 true + + + true + true @@ -22,6 +26,8 @@ + + diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs index 7dd5e8e755..7027cbeca8 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs @@ -7,15 +7,15 @@ using System; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Razor; -using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.LanguageServices.Razor.Editor; namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo { public class RazorDocumentInfoViewModel : NotifyPropertyChanged { - private readonly RazorDocumentTracker _documentTracker; + private readonly VisualStudioDocumentTracker _documentTracker; - public RazorDocumentInfoViewModel(RazorDocumentTracker documentTracker) + public RazorDocumentInfoViewModel(VisualStudioDocumentTracker documentTracker) { if (documentTracker == null) { @@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo _documentTracker = documentTracker; } - public bool IsSupportedDocument => _documentTracker.IsSupportedDocument; + public bool IsSupportedDocument => _documentTracker.IsSupportedProject; public Project Project { @@ -42,8 +42,6 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo public ProjectId ProjectId => _documentTracker.ProjectId; - public SourceTextContainer TextContainer => _documentTracker.TextContainer; - public Workspace Workspace => _documentTracker.Workspace; public HostLanguageServices RazorLanguageServices => Workspace?.Services.GetLanguageServices(RazorLanguage.Name); diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs index 9d51d88ad0..d93732c655 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs @@ -9,7 +9,7 @@ using System.Windows; using Microsoft.CodeAnalysis.Razor; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; -using Microsoft.VisualStudio.LanguageServices.Razor; +using Microsoft.VisualStudio.LanguageServices.Razor.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Editor; @@ -21,13 +21,13 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo internal class RazorDocumentInfoWindow : ToolWindowPane { private IVsEditorAdaptersFactoryService _adapterFactory; - private TextViewRazorDocumentTrackerService _documentTrackerService; + private VisualStudioDocumentTrackerFactory _documentTrackerService; private IVsTextManager _textManager; private IVsRunningDocumentTable _rdt; private uint _cookie; private ITextView _textView; - private RazorDocumentTracker _documentTracker; + private VisualStudioDocumentTracker _documentTracker; public RazorDocumentInfoWindow() : base(null) @@ -43,7 +43,7 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo var component = (IComponentModel)GetService(typeof(SComponentModel)); _adapterFactory = component.GetService(); - _documentTrackerService = component.GetService(); + _documentTrackerService = component.GetService(); _textManager = (IVsTextManager)GetService(typeof(SVsTextManager)); _rdt = (IVsRunningDocumentTable)GetService(typeof(SVsRunningDocumentTable)); @@ -78,7 +78,7 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo _documentTracker.ContextChanged -= DocumentTracker_ContextChanged; } - _documentTracker = _documentTrackerService.CreateTracker(textView); + _documentTracker = _documentTrackerService.GetTracker(textView); _documentTracker.ContextChanged += DocumentTracker_ContextChanged; ((FrameworkElement)Content).DataContext = new RazorDocumentInfoViewModel(_documentTracker); diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml index c18ae0c7fb..123118bd34 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml @@ -19,34 +19,13 @@ - - - - - - -