223 lines
9.3 KiB
C#
Raw Normal View History

/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Parsing;
using ErrorHandler = Microsoft.VisualStudio.ErrorHandler;
using VSConstants = Microsoft.VisualStudio.VSConstants;
namespace Microsoft.VisualStudio.Navigation {
/// <summary>
/// This is a specialized version of the LibraryNode that handles the dynamic languages
/// items. The main difference from the generic one is that it supports navigation
/// to the location inside the source code where the element is defined.
/// </summary>
public abstract class CommonLibraryNode : LibraryNode {
private readonly IVsHierarchy _ownerHierarchy;
private readonly uint _fileId;
private readonly TextSpan _sourceSpan;
private readonly IScopeNode _scope;
private string _fileMoniker;
protected CommonLibraryNode(IScopeNode scope, string namePrefix, IVsHierarchy hierarchy, uint itemId) :
base(GetLibraryNodeName(scope, namePrefix), namePrefix + scope.Name, scope.NodeType) {
_ownerHierarchy = hierarchy;
_fileId = itemId;
// Now check if we have all the information to navigate to the source location.
if ((null != _ownerHierarchy) && (VSConstants.VSITEMID_NIL != _fileId)) {
if ((SourceLocation.Invalid != scope.Start) && (SourceLocation.Invalid != scope.End)) {
_sourceSpan = new TextSpan();
_sourceSpan.iStartIndex = scope.Start.Column - 1;
if (scope.Start.Line > 0) {
_sourceSpan.iStartLine = scope.Start.Line - 1;
}
_sourceSpan.iEndIndex = scope.End.Column;
if (scope.End.Line > 0) {
_sourceSpan.iEndLine = scope.End.Line - 1;
}
CanGoToSource = true;
}
}
_scope = scope;
}
public IScopeNode ScopeNode {
get {
return _scope;
}
}
public TextSpan SourceSpan {
get {
return _sourceSpan;
}
}
private static string GetLibraryNodeName(IScopeNode node, string namePrefix) {
namePrefix = namePrefix.Substring(namePrefix.LastIndexOf(':') + 1); // remove filename prefix
return node.NodeType == LibraryNodeType.Members ? node.Name : string.Format(CultureInfo.InvariantCulture, "{0}{1}", namePrefix, node.Name);
}
protected CommonLibraryNode(CommonLibraryNode node) :
base(node) {
_fileId = node._fileId;
_ownerHierarchy = node._ownerHierarchy;
_fileMoniker = node._fileMoniker;
_sourceSpan = node._sourceSpan;
}
protected CommonLibraryNode(CommonLibraryNode node, string newFullName) :
base(node, newFullName) {
_scope = node._scope;
_fileId = node._fileId;
_ownerHierarchy = node._ownerHierarchy;
_fileMoniker = node._fileMoniker;
_sourceSpan = node._sourceSpan;
}
public override uint CategoryField(LIB_CATEGORY category) {
switch (category) {
case (LIB_CATEGORY)_LIB_CATEGORY2.LC_MEMBERINHERITANCE:
if (NodeType == LibraryNodeType.Members || NodeType == LibraryNodeType.Definitions) {
return (uint)_LIBCAT_MEMBERINHERITANCE.LCMI_IMMEDIATE;
}
break;
}
return base.CategoryField(category);
}
public override void GotoSource(VSOBJGOTOSRCTYPE gotoType) {
// We do not support the "Goto Reference"
if (VSOBJGOTOSRCTYPE.GS_REFERENCE == gotoType) {
return;
}
// There is no difference between definition and declaration, so here we
// don't check for the other flags.
IVsWindowFrame frame = null;
IntPtr documentData = FindDocDataFromRDT();
try {
// Now we can try to open the editor. We assume that the owner hierarchy is
// a project and we want to use its OpenItem method.
IVsProject3 project = _ownerHierarchy as IVsProject3;
if (null == project) {
return;
}
Guid viewGuid = VSConstants.LOGVIEWID_Code;
ErrorHandler.ThrowOnFailure(project.OpenItem(_fileId, ref viewGuid, documentData, out frame));
} finally {
if (IntPtr.Zero != documentData) {
Marshal.Release(documentData);
documentData = IntPtr.Zero;
}
}
// Make sure that the document window is visible.
ErrorHandler.ThrowOnFailure(frame.Show());
// Get the code window from the window frame.
object docView;
ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out docView));
IVsCodeWindow codeWindow = docView as IVsCodeWindow;
if (null == codeWindow) {
object docData;
ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData));
codeWindow = docData as IVsCodeWindow;
if (null == codeWindow) {
return;
}
}
// Get the primary view from the code window.
IVsTextView textView;
ErrorHandler.ThrowOnFailure(codeWindow.GetPrimaryView(out textView));
// Set the cursor at the beginning of the declaration.
ErrorHandler.ThrowOnFailure(textView.SetCaretPos(_sourceSpan.iStartLine, _sourceSpan.iStartIndex));
// Make sure that the text is visible.
TextSpan visibleSpan = new TextSpan();
visibleSpan.iStartLine = _sourceSpan.iStartLine;
visibleSpan.iStartIndex = _sourceSpan.iStartIndex;
visibleSpan.iEndLine = _sourceSpan.iStartLine;
visibleSpan.iEndIndex = _sourceSpan.iStartIndex + 1;
ErrorHandler.ThrowOnFailure(textView.EnsureSpanVisible(visibleSpan));
}
public override void SourceItems(out IVsHierarchy hierarchy, out uint itemId, out uint itemsCount) {
hierarchy = _ownerHierarchy;
itemId = _fileId;
itemsCount = 1;
}
public override string UniqueName {
get {
if (string.IsNullOrEmpty(_fileMoniker)) {
ErrorHandler.ThrowOnFailure(_ownerHierarchy.GetCanonicalName(_fileId, out _fileMoniker));
}
return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", _fileMoniker, Name);
}
}
private IntPtr FindDocDataFromRDT() {
// Get a reference to the RDT.
IVsRunningDocumentTable rdt = Package.GetGlobalService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
if (null == rdt) {
return IntPtr.Zero;
}
// Get the enumeration of the running documents.
IEnumRunningDocuments documents;
ErrorHandler.ThrowOnFailure(rdt.GetRunningDocumentsEnum(out documents));
IntPtr documentData = IntPtr.Zero;
uint[] docCookie = new uint[1];
uint fetched;
while ((VSConstants.S_OK == documents.Next(1, docCookie, out fetched)) && (1 == fetched)) {
uint flags;
uint editLocks;
uint readLocks;
string moniker;
IVsHierarchy docHierarchy;
uint docId;
IntPtr docData = IntPtr.Zero;
try {
ErrorHandler.ThrowOnFailure(
rdt.GetDocumentInfo(docCookie[0], out flags, out readLocks, out editLocks, out moniker, out docHierarchy, out docId, out docData));
// Check if this document is the one we are looking for.
if ((docId == _fileId) && (_ownerHierarchy.Equals(docHierarchy))) {
documentData = docData;
docData = IntPtr.Zero;
break;
}
} finally {
if (IntPtr.Zero != docData) {
Marshal.Release(docData);
}
}
}
return documentData;
}
}
}