/// <summary> /// Determines if a code element is within a specific tree node. /// </summary> /// <param name="treeNode">The source CodeElementWrapper object.</param> /// <param name="tp">The TextPoint cursor position in the source file.</param> /// <returns>The CodeElementWrapper object that contains the text point.</returns> private CodeElementWrapper FindBestMatch(CodeElementWrapper treeNode, TextPoint tp) { CodeElementWrapper cewBestMatch = null; // Is the tp between the start and end point of this CodeElement. if ((treeNode.StartPoint.EqualTo(tp)) || (treeNode.StartPoint.LessThan(tp) && treeNode.EndPoint.GreaterThan(tp))) { cewBestMatch = treeNode; CodeElementWrapper closestCEWInChildren = FindBestMatchInCollection(treeNode.Nodes, tp); if (closestCEWInChildren != null) { cewBestMatch = closestCEWInChildren; } } return cewBestMatch; }
/// <summary> /// Locates a specific element in the flat view by ID. /// </summary> /// <param name="codeElement">The CodeElementWrapper object to locate.</param> /// <returns>The CodeElementWrapper object containing the element.</returns> private CodeElementWrapper FindElementInFilterView(CodeElementWrapper codeElementWrapper) { CodeElementWrapper codeElementWrapperOut = null; string strElementID = codeElementWrapper.UniqueElementId; // Use sequential search. foreach (CodeElementWrapper node in FilterView.Nodes) { if (strElementID == node.UniqueElementId) { codeElementWrapperOut = node; break; } } return codeElementWrapperOut; }
/// <summary> /// Locates an element by name within a set of nodes. /// </summary> /// <param name="codeElementWrapperParent">The CodeElementWrapper object to search.</param> /// <param name="codeElement">The CodeElement object to locate.</param> /// <param name="listCodeElementWrapperOut">The List of elements found.</param> private static void FindChildrenByName(CodeElementWrapper codeElementWrapperParent, CodeElement codeElement, out System.Collections.Generic.List<CodeElementWrapper> listCodeElementWrapperOut) { listCodeElementWrapperOut = new System.Collections.Generic.List<CodeElementWrapper>(); string strElementName = codeElement.Name; int matches = 0; foreach (CodeElementWrapper node in codeElementWrapperParent.Nodes) { if (node.ElementName == strElementName) { listCodeElementWrapperOut.Add(node); matches++; } } }
/// <summary> /// Adds a new element to the tree views. /// </summary> /// <param name="codeElementWrapper">The new element as a CodeElementWrapper object.</param> private void AddNodeToInternalStructures(CodeElementWrapper codeElementWrapper) { try { // Add the element to the tree view map. _mapElementIDToTreeViewCodeElementWrapper.Add(codeElementWrapper.UniqueElementId, codeElementWrapper); } catch (System.ArgumentException e) { Utils.DisplayMessage(Resources.ErrorPrefix, "Adding duplicate to _mapElementIDToTreeViewCodeElementWrapper: " + e.Message); } // Add to filtered (flat) tree. CodeElementWrapper codeElementWrapperFilterView = new CodeElementWrapper(codeElementWrapper.CodeElement); FilterView.Nodes.Add(codeElementWrapperFilterView); try { // Add the element to the filter view map. _mapElementIDToFilterViewCodeElementWrapper.Add(codeElementWrapper.UniqueElementId, codeElementWrapperFilterView); } catch (System.ArgumentException e) { Utils.DisplayMessage(Resources.ErrorPrefix, "Adding duplicate to _mapElementIDToFilterViewCodeElementWrapper: " + e.Message); } // Add the node to the array. CodeElementWrapper codeElementWrapperClone = new CodeElementWrapper(codeElementWrapper.CodeElement); _codeElementWrapperArray.AddCodeElementWrapper(codeElementWrapperClone); }
/// <summary> /// Removes a node and all of its children. /// </summary> /// <param name="codeElementWrapperToDelete">The CodeElementWrapper to delete.</param> /// <param name="codeElementWrapperParent">The CodeElementWrapper parent of the object to delete.</param> private void RemoveElementRecursive(CodeElementWrapper codeElementWrapperToDelete, CodeElementWrapper codeElementWrapperParent) { CodeElementWrapper codeElementWrapper; // Remove the children before the node, deleting upwards from last to first. for (int i = codeElementWrapperToDelete.Nodes.Count - 1; i >= 0; i--) { CodeElementWrapper node = codeElementWrapperToDelete.Nodes[i] as CodeElementWrapper; RemoveElementRecursive(node, codeElementWrapperToDelete); } // Remove the element from the outline view. RemoveElementFromTree(codeElementWrapperToDelete, codeElementWrapperParent); // Remove the element from the filter view. codeElementWrapper = FindElementInFilterView(codeElementWrapperToDelete); if (codeElementWrapper != null) { RemoveElementFromFilterView(codeElementWrapper); } // Remove the element from the array. codeElementWrapper = FindElementInWrapperArray(codeElementWrapperToDelete); if (codeElementWrapper != null) { RemoveElementFromWrapperArray(codeElementWrapper); } }
/// <summary> /// Occurs when a new code element is added to the text window, /// and adds the new element to the appropriate place in the outline. /// </summary> /// <param name="newElement">The new code element.</param> public void OnCodeModelElementAdd(CodeElement newElement) { if ((newElement == null) || !CodeModelHelpers.IsInterestingKind(newElement.Kind)) { return; } try { int line = newElement.StartPoint.Line; } catch { // An exception can be thrown here when an element is being edited, so ignore it. return; } // Update the tree. if (newElement.Kind == vsCMElement.vsCMElementParameter) { FindBestMatchCodeElementToRefreshFrom(newElement); return; } // Get the start point from the wrapper object and not from the CodeElement directly. CodeElementWrapper temp = new CodeElementWrapper(newElement); TextPoint tp = temp.StartPoint; CodeElementWrapper cew = GetClosestCodeElementInTreeView(TreeView, tp); if (cew == null) { // Nothing found, this must be the first element drawn. ReadCodeModelElementsRecursive(newElement, TreeView.Nodes, false); } else { CodeElementWrapper newNode = new CodeElementWrapper(newElement); // Note that the add could result from a paste, and the editor only informs // regarding the outer element that was added if the language is C# or VB; // the C++ language raises an event for each element. AddNodeToInternalStructures(newNode); ReadCodeModelChildrenElementsRecursive(newNode, false); newNode.ExpandAll(); int index = 0; // Insert this element in the correct place. foreach (CodeElementWrapper n in cew.Nodes) { if (n.Location > newNode.Location) { // Insert prior to n. cew.Nodes.Insert(index, newNode); newNode = null; break; } else { index++; } } // If it was not inserted, append it. if (newNode != null) { cew.Nodes.Add(newNode); } } // Update the filter view. _codeElementWrapperIndexTable = new CodeElementWrapperIndexTable(_codeElementWrapperArray); _currentResults = _codeElementWrapperIndexTable.GenerateResultsTable(_searchCriteria); this.FilterText = _currentFilterText; }
/// <summary> /// Removes an element from the outline tree view by ID. /// </summary> /// <param name="codeElementWrapperToDelete">The CodeElementWrapper to delete.</param> /// <param name="codeElementWrapperParent">The CodeElementWrapper parent of the object to delete.</param> private void RemoveElementFromTree(CodeElementWrapper codeElementWrapperToDelete, CodeElementWrapper codeElementWrapperParent) { // Remove the element from the tree view. codeElementWrapperParent.Nodes.Remove(codeElementWrapperToDelete); // Remove the element from the map. _mapElementIDToTreeViewCodeElementWrapper.Remove(codeElementWrapperToDelete.UniqueElementId); }
/// <summary> /// Removes an element from the wrapper array. /// </summary> /// <param name="codeElementWrapperToDelete">The CodeElementWrapper to remove.</param> private void RemoveElementFromWrapperArray(CodeElementWrapper codeElementWrapperToDelete) { // Remove the element from the array. _codeElementWrapperArray.Remove(codeElementWrapperToDelete); }
/// <summary> /// Refreshes a specific tree node with a specific element. /// </summary> /// <param name="codeElementWrapper">The CodeElementWrapper to refresh.</param> /// <param name="codeElement">The CodeElement to refresh with.</param> private void RefreshCodeModelForElement(ref CodeElementWrapper codeElementWrapper, CodeElement codeElement) { TreeNode treeNodeFilterView = null; TreeNode treeNodeTreeView = null; try { treeNodeFilterView = FilterView.SelectedNode; treeNodeTreeView = TreeView.SelectedNode; TreeView.BeginUpdate(); FilterView.BeginUpdate(); BeginTransaction(); // Update the element itself. codeElementWrapper.CodeElement = codeElement; // Remove elements from the parent in reverse order. for (int i = codeElementWrapper.Nodes.Count - 1; i >= 0; i--) { CodeElementWrapper node = codeElementWrapper.Nodes[i] as CodeElementWrapper; RemoveElementRecursive(node, codeElementWrapper); } codeElementWrapper.Nodes.Clear(); ReadCodeModelChildrenElementsRecursive(codeElementWrapper, false); // Update the copy of the codeElementWrapper in the wrapper array. CodeElementWrapper codeElementWrapperClone = FindElementInWrapperArray(codeElementWrapper); if (codeElementWrapperClone != null) { RemoveElementFromWrapperArray(codeElementWrapperClone); } codeElementWrapperClone = new CodeElementWrapper(codeElementWrapper.CodeElement); _codeElementWrapperArray.AddCodeElementWrapper(codeElementWrapperClone); } finally { EndTransaction(); TreeView.ExpandAll(); FilterView.EndUpdate(); TreeView.EndUpdate(); FilterView.SelectedNode = treeNodeFilterView; TreeView.SelectedNode = treeNodeTreeView; } }
/// <summary> /// Removes an element from the flat view by ID. /// </summary> /// <param name="codeElementWrapperToDelete">The CodeElementWrapper to delete.</param> private void RemoveElementFromFilterView(CodeElementWrapper codeElementWrapperToDelete) { // Remove the element from the filter view. FilterView.Nodes.Remove(codeElementWrapperToDelete); // Remove the element from the map. _mapElementIDToFilterViewCodeElementWrapper.Remove(codeElementWrapperToDelete.UniqueElementId); }
/// <summary> /// Iterates through the code model starting at a specified element /// and adds the discovered elements to a tree. /// </summary> /// <param name="codeElement">The starting CodeElement.</param> /// <param name="nodes">The TreeNodeCollection to add the element to.</param> /// <param name="interruptible">true if this method can be interrupted (is background), otherwise false.</param> /// <returns>true if the method completed, otherwise false.</returns> private bool ReadCodeModelElementsRecursive(EnvDTE.CodeElement codeElement, TreeNodeCollection nodes, bool interruptible) { if (interruptible) { yieldCount0++; if (yieldCount0 >= 5) { yieldCount0 = 0; NemerlePackage pkg = _sourceOutlinerToolWindow.Package as NemerlePackage; if ((pkg != null) && (!pkg.CanContinueIdle)) return false; } } if (codeElement == null) { return true; } // Test whether this is a code element of interest. if (!CodeModelHelpers.IsInterestingKind(codeElement.Kind)) { return true; } if (codeElement.InfoLocation != EnvDTE.vsCMInfoLocation.vsCMInfoLocationProject) { return true; } CodeElementWrapper treenode = null; string uniqueID = CodeModelHelpers.GetUniqueElementId(codeElement); // See if this node has finished processing. _mapElementIDToTreeViewCodeElementWrapper.TryGetValue(uniqueID, out treenode); if (treenode != null) { if (treenode.FinishedLoading) return true; } else { treenode = new CodeElementWrapper(codeElement); nodes.Add(treenode); AddNodeToInternalStructures(treenode); } treenode.FinishedLoading = ReadCodeModelChildrenElementsRecursive(treenode, interruptible); return treenode.FinishedLoading; }
/// <summary> /// Iterates through the children of a code model element. /// </summary> /// <param name="codeElementWrapper">The CodeElementWrapper to process.</param> /// <param name="interruptible">true if this method can be interrupted (is background), otherwise false.</param> /// <returns>true if the method completed, otherwise false.</returns> private bool ReadCodeModelChildrenElementsRecursive(CodeElementWrapper codeElementWrapper, bool interruptible) { if (interruptible) { yieldCount1++; if (yieldCount1 >= 5) { yieldCount1 = 0; NemerlePackage pkg = _sourceOutlinerToolWindow.Package as NemerlePackage; if ((pkg != null) && (!pkg.CanContinueIdle)) return false; } } bool childrenRead; CodeElements members = CodeModelHelpers.GetMembersOf(codeElementWrapper.CodeElement); if (members != null) { foreach (CodeElement codeElementMember in members) { string uniqueID = CodeModelHelpers.GetUniqueElementId(codeElementMember); CodeElementWrapper node = null; _mapElementIDToTreeViewCodeElementWrapper.TryGetValue(uniqueID, out node); if (node != null) { if (node.FinishedLoading) continue; } // Prevent a recursive call if the code element is not of interest. if (CodeModelHelpers.IsInterestingKind(codeElementMember.Kind)) { childrenRead = ReadCodeModelElementsRecursive(codeElementMember, codeElementWrapper.Nodes, interruptible); if (!childrenRead) { // Children failed to be read, so leave. return false; } } } } return true; }
/// <summary> /// Locates an element in the wrapper array. /// </summary> /// <param name="codeElementWrapper">The CodeElementWrapper object to locate.</param> /// <returns>The CodeElementWrapper node that was located in the array.</returns> private CodeElementWrapper FindElementInWrapperArray(CodeElementWrapper codeElementWrapper) { CodeElementWrapper codeElementWrapperOut = null; try { codeElementWrapperOut = _codeElementWrapperArray.FindCodeElementWrapper(codeElementWrapper); } catch (Exception) { // If not found in the map, use a sequential search. string strElementID = codeElementWrapper.UniqueElementId; foreach (CodeElementWrapper node in _codeElementWrapperArray) { if (strElementID == node.UniqueElementId) { codeElementWrapperOut = node; break; } } } return codeElementWrapperOut; }
/// <summary> /// Locates a specific element in a tree node by ID, recursively. /// </summary> /// <param name="codeElement">The CodeElement object to locate.</param> /// <param name="codeElementWrapperNode">The CodeElementWrapper node to search.</param> /// <returns>The CodeElementWrapper object containing the element.</returns> private CodeElementWrapper FindElementInTreeRecursive(CodeElement codeElement, CodeElementWrapper codeElementWrapperNode) { string strCodeElementID = CodeModelHelpers.GetUniqueElementId(codeElement); if (strCodeElementID == codeElementWrapperNode.UniqueElementId) { return codeElementWrapperNode; } // If this is not it, check the children. CodeElementWrapper codeElementWrapper = null; foreach (CodeElementWrapper node in codeElementWrapperNode.Nodes) { codeElementWrapper = FindElementInTreeRecursive(codeElement, node); if (codeElementWrapper != null) { break; } } return codeElementWrapper; }
/// <summary> /// This method is used to determine on which argument position the user is. It's able to determine if the user is inside the INPUT/OUTPUT/IN-OUT argument section. /// </summary> /// <param name="procedure">The detected procedure</param> /// <param name="wrappedCodeElement">One codeElement containing arrangedTokens concerning a procedure CALL</param> /// <param name="position">Cursors position in the document</param> /// <returns></returns> public static int?SignatureHelperParameterSelecter(FunctionDeclaration procedure, CodeElementWrapper wrappedCodeElement, Position position) { int activeParameter = 0; var closestTokenToCursor = wrappedCodeElement.ArrangedConsumedTokens.Where( t => (t.Line == position.line + 1 && t.StartIndex <= position.character && t.StopIndex + 1 > position.character)) .OrderBy(t => Math.Abs(position.character - t.StopIndex + 1)) //Allows to get the token closest to the cursor and ignoring the one where the cursor is .FirstOrDefault(); //Get the last significant token before cursor (INPUT / OUTPUT / IN-OUT) var closestSignificantTokenToCursor = wrappedCodeElement.ArrangedConsumedTokens.LastOrDefault( t => (t.TokenType == TokenType.INPUT || t.TokenType == TokenType.OUTPUT || t.TokenType == TokenType.IN_OUT) && t.Line <= position.line + 1); if (closestSignificantTokenToCursor == null) { return(null);//No argument have to be selected for now we will wait until the user typed (input/output/in-out) } var alreadyGivenTokens = wrappedCodeElement.ArrangedConsumedTokens .SkipWhile(t => t != closestSignificantTokenToCursor).Skip(1) .TakeWhile(t => t.TokenType != TokenType.OUTPUT && t.TokenType != TokenType.IN_OUT) .Where(t => (t.StartIndex < position.character && t.Line == position.line + 1) || t.Line < position.line + 1); int alreadyGivenParametersCount = 0; TokenType?previousTokenType = null; //Loop that allows to ignore qualified name parameters. foreach (var givenToken in alreadyGivenTokens) { if (givenToken.TokenType == TokenType.UserDefinedWord && (previousTokenType == null || previousTokenType.Value == TokenType.UserDefinedWord)) { alreadyGivenParametersCount++; } previousTokenType = givenToken.TokenType; } if (closestSignificantTokenToCursor.TokenType == TokenType.INPUT) { activeParameter = alreadyGivenParametersCount; } else if (closestSignificantTokenToCursor.TokenType == TokenType.IN_OUT) { //Count total input parameters //add alreadyGivenParametersCount activeParameter = procedure.Profile.InputParameters.Count + alreadyGivenParametersCount; } else if (closestSignificantTokenToCursor.TokenType == TokenType.OUTPUT) { //Count total input & inout parameters //add alreadyGivenParametersCount activeParameter = procedure.Profile.InputParameters.Count + procedure.Profile.InoutParameters.Count + alreadyGivenParametersCount; } if (closestTokenToCursor != null && (closestTokenToCursor.TokenType == TokenType.UserDefinedWord || closestTokenToCursor.TokenType == TokenType.QualifiedNameSeparator) && position.character <= closestTokenToCursor.StopIndex + 1 && activeParameter > 0) { activeParameter--; //The cursor is still on this argument so the user is not willing the next argument. } return(activeParameter); }