/// <summary> /// Perform the search using the given criteria /// </summary> /// <param name="listType">The list type to return (classes or members)</param> /// <param name="criteria">The criteria to use for the search</param> /// <returns>An enumerable list of <see cref="SearchResult"/> instances that contain information about /// each potential match.</returns> private IEnumerable <SearchResult> PerformSearch(_LIB_LISTTYPE listType, VSOBSEARCHCRITERIA2 criteria) { ThreadHelper.ThrowIfNotOnUIThread(); IVsObjectList2 list; uint count; int pfOK; int result = library.GetList2((uint)listType, (uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER, new[] { criteria }, out list); if (result == VSConstants.S_OK && list != null) { result = list.GetItemCount(out count); if (result == VSConstants.S_OK && count != 0) { for (uint idx = 0; idx < count; idx++) { result = list.CanGoToSource(idx, VSOBJGOTOSRCTYPE.GS_DEFINITION, out pfOK); // Ignore anything for which we can't go to the source if (result == VSConstants.S_OK && pfOK == 1) { yield return(new SearchResult(list, idx)); } } } } }
public NavInfo( NavInfoFactory factory, string libraryName, string referenceOwnerName = null, string namespaceName = null, string className = null, string memberName = null ) { _factory = factory; _libraryName = libraryName; _referenceOwnerName = referenceOwnerName; _namespaceName = namespaceName; _className = className; _memberName = memberName; _baseCanonicalNodes = CreateNodes(expandDottedNames: true); _basePresentationNodes = CreateNodes(expandDottedNames: false); _symbolType = _basePresentationNodes.Length > 0 ? _basePresentationNodes[_basePresentationNodes.Length - 1].ListType : 0; }
public static void Add(this ImmutableArray<NavInfoNode>.Builder builder, string name, _LIB_LISTTYPE type, bool expandDottedNames) { if (name == null) { return; } if (expandDottedNames) { const char separator = '.'; var start = 0; var separatorPos = name.IndexOf(separator, start); while (separatorPos >= 0) { builder.Add(name.Substring(start, separatorPos - start), type); start = separatorPos + 1; separatorPos = name.IndexOf(separator, start); } if (start < name.Length) { builder.Add(name.Substring(start), type); } } else { builder.Add(name, type); } }
public static void Add(this ImmutableArray<NavInfoNode>.Builder builder, string name, _LIB_LISTTYPE type) { if (string.IsNullOrEmpty(name)) { return; } builder.Add(new NavInfoNode(name, type)); }
public static void Add( this ImmutableArray <NavInfoNode> .Builder builder, string name, _LIB_LISTTYPE type ) { if (string.IsNullOrEmpty(name)) { return; } builder.Add(new NavInfoNode(name, type)); }
/// <summary> /// Creates a new NavInfo object that implements <see cref="IVsNavInfo"/> and <see cref="IVsNavInfo2"/>. /// </summary> /// <param name="factory">The <see cref="NavInfoFactory"/> that created this NavInfo.</param> /// <param name="libraryName">The name of the library (project or assembly) to use for navigation.</param> /// <param name="referenceOwnerName">If this NavInfo is inside of an assembly or project reference, this is the name of the project /// that owns the reference. In general, this is only set when the NavInfo is constructed from the Class View window, where references /// are parented inside of projects.</param> /// <param name="namespaceName">The name of the namespace used for navigation.</param> /// <param name="className">The name of the class used for navigation (should be contained by <paramref name="namespaceName"/>).</param> /// <param name="memberName">The name of the member used for navigation (should be contained by <paramref name="memberName"/>).</param> public NavInfo( NavInfoFactory factory, string libraryName, string referenceOwnerName = null, string namespaceName = null, string className = null, string memberName = null) { _factory = factory; _libraryName = libraryName; _referenceOwnerName = referenceOwnerName; _namespaceName = namespaceName; _className = className; _memberName = memberName; _baseCanonicalNodes = CreateNodes(expandDottedNames: true); _basePresentationNodes = CreateNodes(expandDottedNames: false); _symbolType = _basePresentationNodes.Length > 0 ? _basePresentationNodes[_basePresentationNodes.Length - 1].ListType : 0; }
public static void Add( this ImmutableArray <NavInfoNode> .Builder builder, string name, _LIB_LISTTYPE type, bool expandDottedNames ) { if (name == null) { return; } if (expandDottedNames) { const char separator = '.'; var start = 0; var separatorPos = name.IndexOf(separator, start); while (separatorPos >= 0) { builder.Add(name.Substring(start, separatorPos - start), type); start = separatorPos + 1; separatorPos = name.IndexOf(separator, start); } if (start < name.Length) { builder.Add(name.Substring(start), type); } } else { builder.Add(name, type); } }
/// <summary> /// Determine the search criteria by parsing the member ID as best we can /// </summary> /// <param name="memberId">The member ID to parse</param> /// <returns>True if parsed successfully, false if not</returns> private bool DetermineSearchCriteria(string memberId) { string[] parts; string searchText, paramText; searchFlags = _LIB_LISTTYPE.LLT_CLASSES | _LIB_LISTTYPE.LLT_MEMBERS; searchText = memberId.Trim().Replace("{", "<").Replace("}", ">"); typeParameters.Clear(); methodParameters.Clear(); searchClassCandidates.Clear(); searchMemberCandidates.Clear(); alternateCompareText = null; if (searchText.IsCodeEntityReference()) { // We can't search for namespaces and namespace groups since they don't exist as something we can // go to in the code. As such, convert them to NamespaceDoc and NamespaceGroupDoc type searches. if (searchText[0] == 'N' || searchText[0] == 'G') { searchFlags = _LIB_LISTTYPE.LLT_CLASSES; searchText = searchText.Substring(2) + ((searchText[0] == 'N') ? ".NamespaceDoc" : ".NamespaceGroupDoc"); } else { // Limit to classes or members for the results. For member searches, there doesn't appear to // be a way to further qualify it to limit it to specific member types such as just fields, // events, properties, or methods so we may get all member types in the results. if (searchText[0] == 'T') { searchFlags = _LIB_LISTTYPE.LLT_CLASSES; } else { searchFlags = _LIB_LISTTYPE.LLT_MEMBERS; } searchText = searchText.Substring(2); } } // If parentheses are found, only search for members. Drop the parameters because we can't guarantee // their format in the search string. We will keep them for the match method though and compare them // as best we can. int pos = searchText.IndexOf('('); if (pos != -1) { searchFlags = _LIB_LISTTYPE.LLT_MEMBERS; paramText = searchText.Substring(pos + 1); searchText = searchText.Substring(0, pos); // For operators, the parameters are followed by a return type so the parenthesis may not be the // last character in the string. pos = paramText.IndexOf(')'); if (pos != -1) { paramText = paramText.Substring(0, pos); } methodParameters.AddRange(this.ParseParameters(paramText)); } // If type parameters are present, strip them off too but keep them for the match method where we'll // compare them as best we can. However, don't strip them if they are followed by a member name. // We'll have to rely on them matching the base type's type parameter names. pos = searchText.LastIndexOf('.'); if (pos == -1) { pos = searchText.IndexOf('<'); } else { pos = searchText.IndexOf('<', pos); } if (pos != -1) { paramText = searchText.Substring(pos + 1); if (paramText.Length != 0 && paramText[paramText.Length - 1] == '>') { paramText = paramText.Substring(0, paramText.Length - 1); } searchText = searchText.Substring(0, pos); typeParameters.AddRange(this.ParseParameters(paramText)); } if ((searchFlags & _LIB_LISTTYPE.LLT_MEMBERS) != 0) { // Convert constructor references to their type name if (searchText.EndsWith("#ctor", StringComparison.Ordinal) || searchText.EndsWith("#cctor", StringComparison.Ordinal)) { parts = searchText.Split('.'); if (parts.Length > 1) { searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + parts[parts.Length - 2]; } else { searchText = parts[0]; } } // Convert finalizers to their type name with prefix if ((searchText == "Finalize" || searchText.EndsWith(".Finalize", StringComparison.Ordinal)) && methodParameters.Count == 0) { parts = searchText.Split('.'); if (parts.Length > 1) { searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + "~" + parts[parts.Length - 2]; } else { searchText = "~" + parts[0]; } } // Explicit implementation class name prefix? This one's a bit tricky. We need to search for the // name with the EII prefix replacing "#" with "." but compare the results without the prefix. if (searchText.IndexOf('#') != -1) { parts = searchText.Split('#'); alternateCompareText = parts[parts.Length - 1]; parts = parts[0].Split('.'); if (parts.Length > 1) { alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + "." + alternateCompareText; } searchText = searchText.Replace("#", "."); } // Convert internal operator names to their code equivalent if (searchText.StartsWith("op_", StringComparison.Ordinal) || searchText.IndexOf(".op_", StringComparison.Ordinal) != -1) { parts = searchText.Split('.'); paramText = parts[parts.Length - 1].ToCodeOperator() ?? "operator ????"; if (parts.Length < 2) { searchText = paramText; } else { searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + paramText; } // The names for implicit and explicit operators are reversed in the search text if (searchText.EndsWith("implicit", StringComparison.Ordinal)) { if (parts.Length < 2) { alternateCompareText = "implicit operator"; } else { alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + ".implicit operator"; } } if (searchText.EndsWith("explicit", StringComparison.Ordinal)) { if (parts.Length < 2) { alternateCompareText = "explicit operator"; } else { alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + ".explicit operator"; } } // Another odd quirk. The "*" used for the multiply operator appears to be treated as a // wildcard but only when specified by itself. Any other search including "*" returns // nothing as does search for "operator" with a class prefix. "*" by itself returns // everything. The only thing that seems to work is to search for "operator" by itself and // sort it out when comparing results. if (searchText.EndsWith("operator *", StringComparison.Ordinal)) { alternateCompareText = searchText; searchText = "operator"; } } } #pragma warning disable VSTHRD010 this.DetermineSearchCandidates(searchText); #pragma warning restore VSTHRD010 // Add the given search text as the last resort search if ((searchFlags & _LIB_LISTTYPE.LLT_CLASSES) != 0) { searchClassCandidates.Add(searchText); } if ((searchFlags & _LIB_LISTTYPE.LLT_MEMBERS) != 0) { searchMemberCandidates.Add(searchText); } return(true); }
public NavInfoNode(string name, _LIB_LISTTYPE listType) { Name = name; ListType = listType; }
public NavInfoNode(string name, uint listType) { Name = name; ListType = (_LIB_LISTTYPE)listType; }
private void AddSymbolToLibrary(_LIB_LISTTYPE symbolType, string symbolText, ref IVsLibrary2 csLib) { IVsObjectList2 list; var success = ErrorHandler.Succeeded(csLib.GetList2( (uint)symbolType, (uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER, new[] { new VSOBSEARCHCRITERIA2 { eSrchType = VSOBSEARCHTYPE.SO_SUBSTRING, grfOptions = (uint) _VSOBSEARCHOPTIONS.VSOBSO_NONE, szName = symbolText } }, out list)); if (success && list != null) { uint count = 0; list.GetItemCount(out count); if (count == 0) MessageBox.Show(String.Format("Error obtaining native symbol for class '{0}'", symbolText)); else // Merge our symbols with the ones obtained from native lib library.AddExternalReference(symbolText, list); } }
private void AddNested(IVsLibrary2 lib, TreeViewItem libRoot, _LIB_LISTTYPE listType) { var timestamp = DateTime.Now; IVsObjectList2 objects; ErrorHandler.Succeeded(lib.GetList2( (uint)listType, (uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER, new[] { new VSOBSEARCHCRITERIA2 { eSrchType = VSOBSEARCHTYPE.SO_PRESTRING, grfOptions = (uint) _VSOBSEARCHOPTIONS.VSOBSO_CASESENSITIVE, szName = "*" } }, out objects )); if (objects == null) return; var root = new TreeViewItem { Header = listType }; uint libFlags; ErrorHandler.Succeeded(objects.GetCapabilities2(out libFlags)); var flags = ""; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_ALLOWDELETE) != 0) flags += "|LLC_ALLOWDELETE"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_ALLOWDRAGDROP) != 0) flags += "|LLC_ALLOWDRAGDROP"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_ALLOWRENAME) != 0) flags += "|LLC_ALLOWRENAME"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_ALLOWSCCOPS) != 0) flags += "|LLC_ALLOWSCCOPS"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_HASBROWSEOBJ) != 0) flags += "|LLC_HASBROWSEOBJ"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_HASCOMMANDS) != 0) flags += "|LLC_HASCOMMANDS"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_HASDESCPANE) != 0) flags += "|LLC_HASDESCPANE"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES.LLC_NONE) != 0) flags += "|LLC_NONE"; if ((libFlags & (uint)_LIB_LISTCAPABILITIES2.LLC_ALLOWELEMENTSEARCH) != 0) flags += "|LLC_ALLOWELEMENTSEARCH"; if (flags != "") { flags = flags.Substring(1); root.Items.Add("flags = " + flags); } uint count; ErrorHandler.Succeeded(objects.GetItemCount(out count)); root.Items.Add("Items count: " + count); for (var i = (uint)0; i < count; i++) { object propValue; string text; Type objType; //objects.GetText(i, VSTREETEXTOPTIONS.TTO_BASETEXT, out text); objType = objects.GetType(); ErrorHandler.Succeeded(objects.GetProperty(i, (int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_LEAFNAME, out propValue)); var item = new TreeViewItem { Header = (string)propValue }; //var item = new TreeViewItem { Header = "TExt" }; item.Items.Add("Type: " + objType); ErrorHandler.Succeeded(objects.GetProperty(i, (int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_FULLNAME, out propValue)); item.Items.Add("Full Name " + (string)propValue); ErrorHandler.Succeeded(objects.GetProperty(i, (int)_VSOBJLISTELEMPROPID.VSOBJLISTELEMPROPID_COMPONENTPATH, out propValue)); item.Items.Add("Path " + (string)propValue); IVsObjectList2 nestedObjects; ErrorHandler.Succeeded(objects.GetList2( i, (uint)(_LIB_LISTTYPE.LLT_CLASSES), (uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER, new[] { new VSOBSEARCHCRITERIA2 { eSrchType = VSOBSEARCHTYPE.SO_PRESTRING, grfOptions = (uint) _VSOBSEARCHOPTIONS.VSOBSO_CASESENSITIVE, szName = "*" } }, out nestedObjects )); AddNested(item, nestedObjects); root.Items.Add(item); } root.Items.Insert(0, "Elapsed " + (DateTime.Now - timestamp).TotalMilliseconds); libRoot.Items.Add(root); }
/// <summary> /// Perform the search using the given criteria /// </summary> /// <param name="listType">The list type to return (classes or members)</param> /// <param name="criteria">The criteria to use for the search</param> /// <returns>An enumerable list of <see cref="SearchResult"/> instances that contain information about /// each potential match.</returns> private IEnumerable<SearchResult> PerformSearch(_LIB_LISTTYPE listType, VSOBSEARCHCRITERIA2 criteria) { IVsObjectList2 list; uint count; int pfOK; int result = library.GetList2((uint)listType, (uint)_LIB_LISTFLAGS.LLF_USESEARCHFILTER, new[] { criteria }, out list); if(result == VSConstants.S_OK && list != null) { result = list.GetItemCount(out count); if(result == VSConstants.S_OK && count != 0) for(uint idx = 0; idx < count; idx++) { result = list.CanGoToSource(idx, VSOBJGOTOSRCTYPE.GS_DEFINITION, out pfOK); // Ignore anything for which we can't go to the source if(result == VSConstants.S_OK && pfOK == 1) yield return new SearchResult(list, idx); } } }
/// <summary> /// Determine the search criteria by parsing the member ID as best we can /// </summary> /// <param name="memberId">The member ID to parse</param> /// <returns>True if parsed successfully, false if not</returns> private bool DetermineSearchCriteria(string memberId) { string[] parts; string searchText, paramText; searchFlags = _LIB_LISTTYPE.LLT_CLASSES | _LIB_LISTTYPE.LLT_MEMBERS; searchText = memberId.Trim().Replace("{", "<").Replace("}", ">"); typeParameters.Clear(); methodParameters.Clear(); searchClassCandidates.Clear(); searchMemberCandidates.Clear(); alternateCompareText = null; if(searchText.IsCodeEntityReference()) { // We can't search for namespaces and namespace groups since they don't exist as something we can // go to in the code. As such, convert them to NamespaceDoc and NamespaceGroupDoc type searches. if(searchText[0] == 'N' || searchText[0] == 'G') { searchFlags = _LIB_LISTTYPE.LLT_CLASSES; searchText = searchText.Substring(2) + ((searchText[0] == 'N') ? ".NamespaceDoc" : ".NamespaceGroupDoc"); } else { // Limit to classes or members for the results. For member searches, there doesn't appear to // be a way to further qualify it to limit it to specific member types such as just fields, // events, properties, or methods so we may get all member types in the results. if(searchText[0] == 'T') searchFlags = _LIB_LISTTYPE.LLT_CLASSES; else searchFlags = _LIB_LISTTYPE.LLT_MEMBERS; searchText = searchText.Substring(2); } } // If parentheses are found, only search for members. Drop the parameters because we can't guarantee // their format in the search string. We will keep them for the match method though and compare them // as best we can. int pos = searchText.IndexOf('('); if(pos != -1) { searchFlags = _LIB_LISTTYPE.LLT_MEMBERS; paramText = searchText.Substring(pos + 1); searchText = searchText.Substring(0, pos); // For operators, the parameters are followed by a return type so the parenthesis may not be the // last character in the string. pos = paramText.IndexOf(')'); if(pos != -1) paramText = paramText.Substring(0, pos); methodParameters.AddRange(this.ParseParameters(paramText)); } // If type parameters are present, strip them off too but keep them for the match method where we'll // compare them as best we can. However, don't strip them if they are followed by a member name. // We'll have to rely on them matching the base type's type parameter names. pos = searchText.LastIndexOf('.'); if(pos == -1) pos = searchText.IndexOf('<'); else pos = searchText.IndexOf('<', pos); if(pos != -1) { paramText = searchText.Substring(pos + 1); if(paramText.Length != 0 && paramText[paramText.Length - 1] == '>') paramText = paramText.Substring(0, paramText.Length - 1); searchText = searchText.Substring(0, pos); typeParameters.AddRange(this.ParseParameters(paramText)); } if((searchFlags & _LIB_LISTTYPE.LLT_MEMBERS) != 0) { // Convert constructor references to their type name if(searchText.EndsWith("#ctor", StringComparison.Ordinal) || searchText.EndsWith("#cctor", StringComparison.Ordinal)) { parts = searchText.Split('.'); if(parts.Length > 1) searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + parts[parts.Length - 2]; else searchText = parts[0]; } // Convert finalizers to their type name with prefix if((searchText == "Finalize" || searchText.EndsWith(".Finalize", StringComparison.Ordinal)) && methodParameters.Count == 0) { parts = searchText.Split('.'); if(parts.Length > 1) searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + "~" + parts[parts.Length - 2]; else searchText = "~" + parts[0]; } // Explicit implementation class name prefix? This one's a bit tricky. We need to search for the // name with the EII prefix replacing "#" with "." but compare the results without the prefix. if(searchText.IndexOf('#') != -1) { parts = searchText.Split('#'); alternateCompareText = parts[parts.Length - 1]; parts = parts[0].Split('.'); if(parts.Length > 1) alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + "." + alternateCompareText; searchText = searchText.Replace("#", "."); } // Convert internal operator names to their code equivalent if(searchText.StartsWith("op_", StringComparison.Ordinal) || searchText.IndexOf(".op_", StringComparison.Ordinal) != -1) { parts = searchText.Split('.'); paramText = parts[parts.Length - 1].ToCodeOperator() ?? "operator ????"; if(parts.Length < 2) searchText = paramText; else searchText = String.Join(".", parts, 0, parts.Length - 1) + "." + paramText; // The names for implicit and explicit operators are reversed in the search text if(searchText.EndsWith("implicit", StringComparison.Ordinal)) { if(parts.Length < 2) alternateCompareText = "implicit operator"; else alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + ".implicit operator"; } if(searchText.EndsWith("explicit", StringComparison.Ordinal)) { if(parts.Length < 2) alternateCompareText = "explicit operator"; else alternateCompareText = String.Join(".", parts, 0, parts.Length - 1) + ".explicit operator"; } // Another odd quirk. The "*" used for the multiply operator appears to be treated as a // wildcard but only when specified by itself. Any other search including "*" returns // nothing as does search for "operator" with a class prefix. "*" by itself returns // everything. The only thing that seems to work is to search for "operator" by itself and // sort it out when comparing results. if(searchText.EndsWith("operator *", StringComparison.Ordinal)) { alternateCompareText = searchText; searchText = "operator"; } } } this.DetermineSearchCandidates(searchText); // Add the given search text as the last resort search if((searchFlags & _LIB_LISTTYPE.LLT_CLASSES) != 0) searchClassCandidates.Add(searchText); if((searchFlags & _LIB_LISTTYPE.LLT_MEMBERS) != 0) searchMemberCandidates.Add(searchText); return true; }