diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.cs new file mode 100644 index 0000000000..1d6e60b698 --- /dev/null +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/RazorDocumentTracker.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.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.CodeAnalysis.Razor/RazorLanguage.cs b/src/Microsoft.CodeAnalysis.Razor/RazorLanguage.cs index 5c993368cc..35562c2907 100644 --- a/src/Microsoft.CodeAnalysis.Razor/RazorLanguage.cs +++ b/src/Microsoft.CodeAnalysis.Razor/RazorLanguage.cs @@ -6,5 +6,7 @@ namespace Microsoft.CodeAnalysis.Razor public static class RazorLanguage { public const string Name = "Razor"; + + public const string ContentType = "RazorCSharp"; } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs new file mode 100644 index 0000000000..3e50057051 --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTracker.cs @@ -0,0 +1,267 @@ +// 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 new file mode 100644 index 0000000000..21e7a481eb --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTextViewRazorDocumentTrackerService.cs @@ -0,0 +1,170 @@ +// 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 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))] - we're not exporting this interface because we only want it to be hooked up + // in developer mode. + [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"); + } + } +} 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 774de1e3ff..de6e58223a 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj @@ -23,6 +23,7 @@ + @@ -38,6 +39,7 @@ + diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs new file mode 100644 index 0000000000..f0b944161a --- /dev/null +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TextViewRazorDocumentTrackerService.cs @@ -0,0 +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 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/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Microsoft.VisualStudio.LanguageServices.Razor.Test.csproj index 8646f856a9..8ac755713c 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 @@ -30,6 +30,7 @@ + diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs new file mode 100644 index 0000000000..ed6790f278 --- /dev/null +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoViewModel.cs @@ -0,0 +1,50 @@ +// 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. + +#if RAZOR_EXTENSION_DEVELOPER_MODE + +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo +{ + public class RazorDocumentInfoViewModel : NotifyPropertyChanged + { + private readonly RazorDocumentTracker _documentTracker; + + public RazorDocumentInfoViewModel(RazorDocumentTracker documentTracker) + { + if (documentTracker == null) + { + throw new ArgumentNullException(nameof(documentTracker)); + } + + _documentTracker = documentTracker; + } + + public bool IsSupportedDocument => _documentTracker.IsSupportedDocument; + + public Project Project + { + get + { + if (Workspace != null && ProjectId != null) + { + return Workspace.CurrentSolution.GetProject(ProjectId); + } + + return null; + } + } + + public ProjectId ProjectId => _documentTracker.ProjectId; + + public SourceTextContainer TextContainer => _documentTracker.TextContainer; + + public Workspace Workspace => _documentTracker.Workspace; + } +} + +#endif \ No newline at end of file diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs new file mode 100644 index 0000000000..d1ee82030b --- /dev/null +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindow.cs @@ -0,0 +1,135 @@ +// 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. + +#if RAZOR_EXTENSION_DEVELOPER_MODE + +using System; +using System.Runtime.InteropServices; +using System.Windows; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.LanguageServices.Razor; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo +{ + [Guid("d8d83218-309c-4c8f-9c9f-38a6fead8dca")] + internal class RazorDocumentInfoWindow : ToolWindowPane + { + private IVsEditorAdaptersFactoryService _adapterFactory; + private TextViewRazorDocumentTrackerService _documentTrackerService; + private IVsTextManager _textManager; + private IVsRunningDocumentTable _rdt; + + private uint _cookie; + private ITextView _textView; + private RazorDocumentTracker _documentTracker; + + public RazorDocumentInfoWindow() + : base(null) + { + Caption = "Razor Document Info"; + + Content = new RazorDocumentInfoWindowControl(); + } + + protected override void Initialize() + { + base.Initialize(); + + var component = (IComponentModel)GetService(typeof(SComponentModel)); + _adapterFactory = component.GetService(); + _documentTrackerService = component.GetService(); + + _textManager = (IVsTextManager)GetService(typeof(SVsTextManager)); + _rdt = (IVsRunningDocumentTable)GetService(typeof(SVsRunningDocumentTable)); + + var hr = _rdt.AdviseRunningDocTableEvents(new RdtEvents(this), out uint _cookie); + ErrorHandler.ThrowOnFailure(hr); + } + + protected override void OnClose() + { + _rdt.UnadviseRunningDocTableEvents(_cookie); + _cookie = 0u; + + base.OnClose(); + } + + private void OnBeforeDocumentWindowShow(IVsWindowFrame frame) + { + var vsTextView = VsShellUtilities.GetTextView(frame); + + var textView = _adapterFactory.GetWpfTextView(vsTextView); + if (textView != null && textView != _textView) + { + _textView = textView; + + if (_documentTracker != null) + { + _documentTracker.ContextChanged -= DocumentTracker_ContextChanged; + } + + _documentTracker = _documentTrackerService.CreateTracker(textView); + _documentTracker.ContextChanged += DocumentTracker_ContextChanged; + + ((FrameworkElement)Content).DataContext = new RazorDocumentInfoViewModel(_documentTracker); + } + } + + private void OnAfterDocumentWindowHide(IVsWindowFrame frame) + { + var vsTextView = VsShellUtilities.GetTextView(frame); + + var textView = _adapterFactory.GetWpfTextView(vsTextView); + if (textView == _textView) + { + ((FrameworkElement)Content).DataContext = null; + _documentTracker.ContextChanged -= DocumentTracker_ContextChanged; + + _textView = null; + _documentTracker = null; + } + } + + private void DocumentTracker_ContextChanged(object sender, EventArgs e) + { + ((FrameworkElement)Content).DataContext = new RazorDocumentInfoViewModel(_documentTracker); + } + + private class RdtEvents : IVsRunningDocTableEvents + { + private readonly RazorDocumentInfoWindow _window; + + public RdtEvents(RazorDocumentInfoWindow window) + { + _window = window; + } + + public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => VSConstants.S_OK; + + public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => VSConstants.S_OK; + + public int OnAfterSave(uint docCookie) => VSConstants.S_OK; + + public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) => VSConstants.S_OK; + + public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) + { + _window.OnBeforeDocumentWindowShow(pFrame); + return VSConstants.S_OK; + } + + public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) + { + _window.OnAfterDocumentWindowHide(pFrame); + return VSConstants.S_OK; + } + } + } +} +#endif diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowCommand.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowCommand.cs new file mode 100644 index 0000000000..91b3bc7e29 --- /dev/null +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowCommand.cs @@ -0,0 +1,107 @@ +// 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. + +#if RAZOR_EXTENSION_DEVELOPER_MODE + +using System; +using System.ComponentModel.Design; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo +{ + /// + /// Command handler + /// + internal sealed class RazorDocumentInfoWindowCommand + { + /// + /// Command ID. + /// + public const int CommandId = 0x101; + + /// + /// Command menu group (command set GUID). + /// + public static readonly Guid CommandSet = new Guid("eaaf8ee4-d120-4a4d-8b58-b138b7f00b96"); + + /// + /// VS Package that provides this command, not null. + /// + private readonly Package package; + + /// + /// Initializes a new instance of the class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// + /// Owner package, not null. + private RazorDocumentInfoWindowCommand(Package package) + { + if (package == null) + { + throw new ArgumentNullException("package"); + } + + this.package = package; + + OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (commandService != null) + { + var menuCommandID = new CommandID(CommandSet, CommandId); + var menuItem = new MenuCommand(this.ShowToolWindow, menuCommandID); + commandService.AddCommand(menuItem); + } + } + + /// + /// Gets the instance of the command. + /// + public static RazorDocumentInfoWindowCommand Instance + { + get; + private set; + } + + /// + /// Gets the service provider from the owner package. + /// + private IServiceProvider ServiceProvider + { + get + { + return this.package; + } + } + + /// + /// Initializes the singleton instance of the command. + /// + /// Owner package, not null. + public static void Initialize(Package package) + { + Instance = new RazorDocumentInfoWindowCommand(package); + } + + /// + /// Shows the tool window when the menu item is clicked. + /// + /// The event sender. + /// The event args. + private void ShowToolWindow(object sender, EventArgs e) + { + // Get the instance number 0 of this tool window. This window is single instance so this instance + // is actually the only one. + // The last flag is set to true so that if the tool window does not exists it will be created. + ToolWindowPane window = this.package.FindToolWindow(typeof(RazorDocumentInfoWindow), 0, true); + if ((null == window) || (null == window.Frame)) + { + throw new NotSupportedException("Cannot create tool window"); + } + + IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); + } + } +} + +#endif \ No newline at end of file diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml new file mode 100644 index 0000000000..831f3cd015 --- /dev/null +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml.cs b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml.cs new file mode 100644 index 0000000000..01662fb063 --- /dev/null +++ b/tooling/Microsoft.VisualStudio.RazorExtension/DocumentInfo/RazorDocumentInfoWindowControl.xaml.cs @@ -0,0 +1,19 @@ +// 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. + +#if RAZOR_EXTENSION_DEVELOPER_MODE + +using System.Windows.Controls; + +namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo +{ + public partial class RazorDocumentInfoWindowControl : UserControl + { + public RazorDocumentInfoWindowControl() + { + this.InitializeComponent(); + } + } +} + +#endif \ No newline at end of file diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj b/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj index 066ae39f2e..70c06c9ed8 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj +++ b/tooling/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj @@ -63,6 +63,12 @@ 4 + + + + + RazorDocumentInfoWindowControl.xaml + @@ -116,15 +122,30 @@ ..\..\packages\Microsoft.CodeAnalysis.CSharp.2.3.0-beta1-61624-03\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll + + ..\..\packages\Microsoft.CodeAnalysis.EditorFeatures.2.3.0-beta1-61624-03\lib\net46\Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll + ..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.2.3.0-beta1-61624-03\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.Workspaces.dll + + ..\..\packages\Microsoft.CodeAnalysis.EditorFeatures.2.3.0-beta1-61624-03\lib\net46\Microsoft.CodeAnalysis.EditorFeatures.dll + + + ..\..\packages\Microsoft.CodeAnalysis.EditorFeatures.Text.2.3.0-beta1-61624-03\lib\net46\Microsoft.CodeAnalysis.EditorFeatures.Text.dll + ..\..\packages\Microsoft.CodeAnalysis.Elfie.0.10.6-rc2\lib\net46\Microsoft.CodeAnalysis.Elfie.dll + + ..\..\packages\Microsoft.CodeAnalysis.Features.2.3.0-beta1-61624-03\lib\netstandard1.3\Microsoft.CodeAnalysis.Features.dll + ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.2.3.0-beta1-61624-03\lib\netstandard1.3\Microsoft.CodeAnalysis.VisualBasic.dll + + ..\..\packages\Microsoft.CodeAnalysis.EditorFeatures.2.3.0-beta1-61624-03\lib\net46\Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.dll + ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.Workspaces.2.3.0-beta1-61624-03\lib\netstandard1.3\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll @@ -146,6 +167,10 @@ ..\..\packages\Microsoft.VisualStudio.CoreUtility.15.0.26201\lib\net45\Microsoft.VisualStudio.CoreUtility.dll True + + ..\..\packages\Microsoft.VisualStudio.Editor.15.0.26201\lib\net45\Microsoft.VisualStudio.Editor.dll + True + ..\..\packages\Microsoft.VisualStudio.Imaging.15.0.26201\lib\net45\Microsoft.VisualStudio.Imaging.dll True @@ -196,6 +221,18 @@ ..\..\packages\Microsoft.VisualStudio.Text.Data.15.0.26201\lib\net45\Microsoft.VisualStudio.Text.Data.dll True + + ..\..\packages\Microsoft.VisualStudio.Text.Logic.15.0.26201\lib\net45\Microsoft.VisualStudio.Text.Logic.dll + True + + + ..\..\packages\Microsoft.VisualStudio.Text.UI.15.0.26201\lib\net45\Microsoft.VisualStudio.Text.UI.dll + True + + + ..\..\packages\Microsoft.VisualStudio.Text.UI.Wpf.15.0.26201\lib\net45\Microsoft.VisualStudio.Text.UI.Wpf.dll + True + ..\..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll True @@ -331,6 +368,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + RazorInfoToolWindowControl.xaml diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs b/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs index 49bb67a812..dd5fc52bca 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs +++ b/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; @@ -12,10 +11,10 @@ namespace Microsoft.VisualStudio.RazorExtension [PackageRegistration(UseManagedResourcesOnly = true)] [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] [Guid(RazorPackage.PackageGuidString)] - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] [ProvideMenuResource("Menus.ctmenu", 1)] #if RAZOR_EXTENSION_DEVELOPER_MODE [ProvideToolWindow(typeof(Microsoft.VisualStudio.RazorExtension.RazorInfo.RazorInfoToolWindow))] + [ProvideToolWindow(typeof(Microsoft.VisualStudio.RazorExtension.DocumentInfo.RazorDocumentInfoWindow))] #endif public sealed class RazorPackage : Package { @@ -41,6 +40,7 @@ namespace Microsoft.VisualStudio.RazorExtension #if RAZOR_EXTENSION_DEVELOPER_MODE RazorInfo.RazorInfoToolWindowCommand.Initialize(this); + DocumentInfo.RazorDocumentInfoWindowCommand.Initialize(this); #endif } } diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.vsct b/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.vsct index a658811e39..e421b6691e 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.vsct +++ b/tooling/Microsoft.VisualStudio.RazorExtension/RazorPackage.vsct @@ -2,24 +2,31 @@ - + - + - + - + @@ -27,10 +34,11 @@ - + + - + @@ -38,5 +46,6 @@ + diff --git a/tooling/Microsoft.VisualStudio.RazorExtension/packages.config b/tooling/Microsoft.VisualStudio.RazorExtension/packages.config index 5cbde216f6..a349f28fa7 100644 --- a/tooling/Microsoft.VisualStudio.RazorExtension/packages.config +++ b/tooling/Microsoft.VisualStudio.RazorExtension/packages.config @@ -6,13 +6,17 @@ + + + + @@ -25,6 +29,9 @@ + + +