internal EnvDTE.CodeParameter AddParameter(EnvDTE.CodeElement parent, SyntaxNode containerNode, string name, object type, object position) { var typeSymbol = CodeModelService.GetTypeSymbol(type, this.GetSemanticModel(), containerNode.SpanStart); var typeName = typeSymbol.GetEscapedFullName(); var parameterNode = CodeModelService.CreateParameterNode(CodeModelService.GetUnescapedName(name), typeName); var insertionIndex = CodeModelService.PositionVariantToParameterInsertionIndex(position, containerNode, fileCodeModel: this); var newNode = InsertParameter(containerNode, parameterNode, insertionIndex); // Since parameters form part of the NodeKey for functions, delegates, and indexers, // creating a CodeParameter hooked up to the correct parent is a little tricky. After // the call to InsertParameter, the syntax tree has been updated, but not the NodeKey // map or the NodeKey in the parent CodeParameter. If we delegate the creation of the // CodeParameter to CodeModelService.CreateInternalCodeElement, it will attempt to // look up an element in the NodeKey map based on the new syntax tree. This will fail, // causing it to create a new, duplicate element for the parent. Later, when we // reacquire the NodeKeys, the original element will get the proper NodeKey, while the // duplicate will be updated to a meaningless NodeKey. Since the duplicate is the one // being used by the CodeParameter, most operations on it will then fail. // Instead, we need to have the parent passed in to us. var parentObj = ComAggregate.GetManagedObject <AbstractCodeMember>(parent); return(CodeParameter.Create(this.State, parentObj, CodeModelService.GetParameterName(newNode))); }
internal T GetOrCreateCodeElement <T>(SyntaxNode node) { var nodeKey = CodeModelService.TryGetNodeKey(node); if (!nodeKey.IsEmpty) { // Since the node already has a key, check to see if a code element already // exists for it. If so, return that element it it's still valid; otherwise, // remove it from the table. if (_codeElementTable.TryGetValue(nodeKey, out var codeElement)) { if (codeElement != null) { var element = ComAggregate.GetManagedObject <AbstractCodeElement>(codeElement); if (element.IsValidNode()) { if (codeElement is T tcodeElement) { return(tcodeElement); } throw new InvalidOperationException($"Found a valid code element for {nodeKey}, but it is not of type, {typeof(T).ToString()}"); } } } // Go ahead and remove the nodeKey from the table. At this point, we'll be creating a new one. _codeElementTable.Remove(nodeKey); } return((T)CodeModelService.CreateInternalCodeElement(this.State, this, node)); }
internal void UpdateCodeElementNodeKey(AbstractKeyedCodeElement keyedElement, SyntaxNodeKey oldNodeKey, SyntaxNodeKey newNodeKey) { if (!_codeElementTable.TryGetValue(oldNodeKey, out var codeElement)) { throw new InvalidOperationException($"Could not find {oldNodeKey} in Code Model element table."); } _codeElementTable.Remove(oldNodeKey); var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement); if (!object.Equals(managedElement, keyedElement)) { throw new InvalidOperationException($"Unexpected failure in Code Model while updating node keys {oldNodeKey} -> {newNodeKey}"); } // If we're updating this element with the same node key as an element that's already in the table, // just remove the old element. The old element will continue to function (through its node key), but // the new element will replace it in the cache. if (_codeElementTable.ContainsKey(newNodeKey)) { _codeElementTable.Remove(newNodeKey); } _codeElementTable.Add(newNodeKey, codeElement); }
private ImmutableArray <EnvDTE.CodeElement> GetOverloads() { // Retrieving the overloads is potentially very expensive because it can force multiple FileCodeModels to be instantiated. // Here, we cache the result to avoid having to perform these calculations each time GetOverloads() is called. // This *could* be an issue because it means that an OverloadsCollection will not necessarily reflect the // current state of the user's code. However, because a new OverloadsCollection is created every time the Overloads // property is accessed on CodeFunction, consumers would hit this behavior rarely. if (_overloads == null) { var symbol = (IMethodSymbol)ParentElement.LookupSymbol(); // Only methods and constructors can be overloaded. However, all functions // can successfully return a collection of overloaded functions; if not // really overloaded, the collection contains just the original function. if (symbol.MethodKind != MethodKind.Ordinary && symbol.MethodKind != MethodKind.Constructor) { return(ImmutableArray.Create((EnvDTE.CodeElement)Parent)); } var solution = this.Workspace.CurrentSolution; var overloadsBuilder = ArrayBuilder <EnvDTE.CodeElement> .GetInstance(); foreach (var method in symbol.ContainingType.GetMembers(symbol.Name)) { if (method.Kind != SymbolKind.Method) { continue; } var location = method.Locations.FirstOrDefault(l => l.IsInSource); if (location != null) { var document = solution.GetDocument(location.SourceTree); if (document != null) { var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id); if (fileCodeModelObject != null) { var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(fileCodeModelObject); var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, EnvDTE.vsCMElement.vsCMElementFunction); if (element != null) { overloadsBuilder.Add(element); } } } } } _overloads = overloadsBuilder.ToImmutableAndFree(); } return(_overloads); }
private void ResetElementKey(GlobalNodeKey globalNodeKey) { // Failure to find the element is not an error -- it just means the code // element didn't exist... if (_codeElementTable.TryGetValue(globalNodeKey.NodeKey, out var element)) { var keyedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element); keyedElement?.ReacquireNodeKey(globalNodeKey.Path, default); } }
private void ResetElementKey(GlobalNodeKey globalNodeKey) { var element = _elementTable.TryGetValue(globalNodeKey.NodeKey); // Failure to find the element is not an error -- it just means the code // element didn't exist... if (element != null) { ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element).ReaquireNodeKey(globalNodeKey.Path, default(CancellationToken)); } }
protected EnvDTE.CodeElements GetCollection <T>(object parentObject) { object parentInstance = ComAggregate.GetManagedObject <object>(parentObject); Debug.Assert(!Marshal.IsComObject(parentInstance), "We should have a pure managed object!"); if (parentInstance is ICodeElementContainer <T> container) { return(container.GetCollection()); } throw Exceptions.ThrowEFail(); }
internal void UpdateCodeElementNodeKey(AbstractKeyedCodeElement keyedElement, SyntaxNodeKey oldNodeKey, SyntaxNodeKey newNodeKey) { var codeElement = _codeElementTable.Remove(oldNodeKey); var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement); if (!object.Equals(managedElement, keyedElement)) { throw new InvalidOperationException($"Unexpected failure in Code Model while updating node keys {oldNodeKey} -> {newNodeKey}"); } _codeElementTable.Add(newNodeKey, codeElement); }
private void ResetElementKey(GlobalNodeKey globalNodeKey) { // Failure to find the element is not an error -- it just means the code // element didn't exist... EnvDTE.CodeElement element; if (_codeElementTable.TryGetValue(globalNodeKey.NodeKey, out element)) { var keyedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element); if (keyedElement != null) { keyedElement.ReacquireNodeKey(globalNodeKey.Path, default(CancellationToken)); } } }
/// <summary> /// This function re-adds a code element to the table, taking care not to duplicate /// an element (i.e., making sure that no two elements with the same key-ordinal /// appear in the same element chain in the table). To resolve any conflict, each /// node with the given key is examined positionally, and the existing order is /// maintained as closely as possible -- but it is still possible the code element /// references to duplicate elements can get "bumped" by odd edits -- nothing we /// can do about this. /// </summary> internal void ResetElementNodeKey(AbstractKeyedCodeElement element, SyntaxNodeKey nodeKey) { EnvDTE.CodeElement elementInTable; _elementTable.Remove(element.NodeKey, out elementInTable); var abstractElementInTable = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(elementInTable); if (!object.Equals(abstractElementInTable, element)) { Debug.Fail("Found a different element with the same key!"); throw new InvalidOperationException(); } abstractElementInTable.NodeKey = nodeKey; _elementTable.Add(nodeKey, elementInTable); }
private ImmutableArray <EnvDTE.CodeElement> GetParts() { // Retrieving the parts is potentially very expensive because it can force multiple FileCodeModels to be instantiated. // Here, we cache the result to avoid having to perform these calculations each time GetParts() is called. // This *could* be an issue because it means that a PartialTypeCollection will not necessarily reflect the // current state of the user's code. However, because a new PartialTypeCollection is created every time the Parts // property is accessed on CodeClass, CodeStruct or CodeInterface, consumers would hit this behavior rarely. if (_parts == null) { var partsBuilder = ArrayBuilder <EnvDTE.CodeElement> .GetInstance(); var solution = this.Workspace.CurrentSolution; var symbol = ParentType.LookupSymbol(); foreach (var location in symbol.Locations.Where(l => l.IsInSource)) { var document = solution.GetDocument(location.SourceTree); if (document != null) { var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id); if (fileCodeModelObject != null) { var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>( fileCodeModelObject ); var element = fileCodeModel.CodeElementFromPosition( location.SourceSpan.Start, ParentType.Kind ); if (element != null) { partsBuilder.Add(element); } } } } _parts = partsBuilder.ToImmutableAndFree(); } return(_parts); }
internal void OnBeforeCodeElementCreated(SyntaxNode node) { // It's conceivable that a consumer is creating a code element with the same node key as a "dead" element // that hasn't been removed from the cache yet. For example, the element could have been "deleted" by // simply replacing its text in the underlying buffer. To handle this situation, we test to see if the // element is "dead" by checking whether it's underlying node is invalid (that is, it can't be found by // its node key). If the element is "dead", we'll go ahead and remove it from the cache here to avoid a // collision with the new element. var nodeKey = CodeModelService.TryGetNodeKey(node); EnvDTE.CodeElement codeElement; if (!nodeKey.IsEmpty && _codeElementTable.TryGetValue(nodeKey, out codeElement)) { var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement); if (managedElement?.IsValidNode() != true) { _codeElementTable.Remove(nodeKey); } } }
private ImmutableArray<EnvDTE.CodeElement> EnumerateOverloads() { var symbol = (IMethodSymbol)ParentElement.LookupSymbol(); // Only methods and constructors can be overloaded. However, all functions // can successfully return a collection of overloaded functions; if not // really overloaded, the collection contains just the original function. if (symbol.MethodKind != MethodKind.Ordinary && symbol.MethodKind != MethodKind.Constructor) { return ImmutableArray.Create((EnvDTE.CodeElement)Parent); } var overloadsBuilder = ImmutableArray.CreateBuilder<EnvDTE.CodeElement>(); foreach (var method in symbol.ContainingType.GetMembers(symbol.Name)) { if (method.Kind != SymbolKind.Method) { continue; } var location = method.Locations.FirstOrDefault(l => l.IsInSource); if (location != null) { var tree = location.SourceTree; var document = this.Workspace.CurrentSolution.GetDocument(tree); var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id); var fileCodeModel = ComAggregate.GetManagedObject<FileCodeModel>(fileCodeModelObject); var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, EnvDTE.vsCMElement.vsCMElementFunction); if (element != null) { overloadsBuilder.Add(element); } } } return overloadsBuilder.ToImmutable(); }
private ImmutableArray <EnvDTE.CodeElement> GetParts() { var partsBuilder = ImmutableArray.CreateBuilder <EnvDTE.CodeElement>(); var symbol = ParentType.LookupSymbol(); foreach (var location in symbol.Locations.Where(l => l.IsInSource)) { var tree = location.SourceTree; var document = this.Workspace.CurrentSolution.GetDocument(tree); var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id); var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(fileCodeModelObject); var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, ParentType.Kind); if (element != null) { partsBuilder.Add(element); } } return(partsBuilder.ToImmutable()); }
internal override object GetBrowseObject(SymbolListItem symbolListItem) { var compilation = symbolListItem.GetCompilation(this); if (compilation == null) { return(null); } var symbol = symbolListItem.ResolveSymbol(compilation); var sourceLocation = symbol.Locations.Where(l => l.IsInSource).FirstOrDefault(); if (sourceLocation == null) { return(null); } var projectId = symbolListItem.ProjectId; if (projectId == null) { return(null); } var project = this.CurrentSolution.GetProject(projectId); if (project == null) { return(null); } var codeModelService = project.LanguageServices.GetService <ICodeModelService>(); if (codeModelService == null) { return(null); } var tree = sourceLocation.SourceTree; var document = project.GetDocument(tree); var vsFileCodeModel = this.GetFileCodeModel(document.Id); var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(vsFileCodeModel); if (fileCodeModel != null) { var syntaxNode = tree.GetRoot().FindNode(sourceLocation.SourceSpan); while (syntaxNode != null) { if (!codeModelService.TryGetNodeKey(syntaxNode).IsEmpty) { break; } syntaxNode = syntaxNode.Parent; } if (syntaxNode != null) { var codeElement = fileCodeModel.GetOrCreateCodeElement <EnvDTE.CodeElement>(syntaxNode); if (codeElement != null) { return(codeElement); } } } return(null); }
internal override object?GetBrowseObject(SymbolListItem symbolListItem) { var compilation = symbolListItem.GetCompilation(this); if (compilation == null) { return(null); } var symbol = symbolListItem.ResolveSymbol(compilation); var sourceLocation = symbol.Locations.Where(l => l.IsInSource).FirstOrDefault(); if (sourceLocation == null) { return(null); } var projectId = symbolListItem.ProjectId; if (projectId == null) { return(null); } var project = this.CurrentSolution.GetProject(projectId); if (project == null) { return(null); } var codeModelService = project.LanguageServices.GetService <ICodeModelService>(); if (codeModelService == null) { return(null); } var tree = sourceLocation.SourceTree; Contract.ThrowIfNull(tree, "We have a location that was in source, but doesn't have a SourceTree."); var document = project.GetDocument(tree); Contract.ThrowIfNull(document, "We have a symbol coming from a tree, and that tree isn't in the Project it supposedly came from."); var vsFileCodeModel = this.GetFileCodeModel(document.Id); var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(vsFileCodeModel); if (fileCodeModel != null) { SyntaxNode?syntaxNode = tree.GetRoot().FindNode(sourceLocation.SourceSpan); while (syntaxNode != null) { if (!codeModelService.TryGetNodeKey(syntaxNode).IsEmpty) { break; } syntaxNode = syntaxNode.Parent; } if (syntaxNode != null) { var codeElement = fileCodeModel.GetOrCreateCodeElement <EnvDTE.CodeElement>(syntaxNode); if (codeElement != null) { return(codeElement); } } } return(null); }