internal EnvDTE.CodeAttribute AddAttribute(SyntaxNode containerNode, string name, string value, object position, string?target = null) { containerNode = CodeModelService.GetNodeWithAttributes(containerNode); var attributeNode = CodeModelService.CreateAttributeNode(CodeModelService.GetUnescapedName(name), value, target); var insertionIndex = CodeModelService.PositionVariantToAttributeInsertionIndex(position, containerNode, fileCodeModel: this); var newNode = InsertAttribute(containerNode, attributeNode, insertionIndex); return((EnvDTE.CodeAttribute)CodeModelService.CreateInternalCodeElement(this.State, fileCodeModel: this, node: newNode)); }
private EnvDTE.CodeElement CreateInternalCodeMember(CodeModelState state, FileCodeModel fileCodeModel, SyntaxNode node) { var element = CodeModelService.CreateInternalCodeElement(state, fileCodeModel, node); if (IsBatchOpen) { var codeElement = ComAggregate.TryGetManagedObject <AbstractKeyedCodeElement>(element); if (codeElement != null) { _batchElements.Add(codeElement); } } return(element); }
internal T CreateCodeElement <T>(SyntaxNode node) { var nodeKey = CodeModelService.TryGetNodeKey(node); if (!nodeKey.IsEmpty) { // Check if the node exists in the parse tree. // Note that in designer spew the nodes don't get created right away so we skip this check. if (!IsBatchOpen && CodeModelService.LookupNode(nodeKey, GetSyntaxTree()) == null) { throw Exceptions.ThrowEFail(); } // See if the element exists. var previousElement = _elementTable.TryGetValue(nodeKey); // Here's our element... possibly. It must be valid -- if it isn't, // we need to remove it. if (previousElement != null) { var previousElementImpl = ComAggregate.TryGetManagedObject <AbstractCodeElement>(previousElement); if (previousElementImpl.IsValidNode()) { if (previousElement is T) { return((T)previousElement); } else { Debug.Fail("Called asked for the wrong type!"); throw new InvalidOperationException(); } } else { // This guy is no longer valid, so yank it out. No sense // continuing to look for a match, either. RemoveElement(nodeKey); } } } return((T)CodeModelService.CreateInternalCodeElement(this.State, this, node)); }
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.TryGetManagedObject <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 EnvDTE.CodeElement CodeElementFromPosition(int position, EnvDTE.vsCMElement scope) { var root = GetSyntaxRoot(); var leftToken = SyntaxFactsService.FindTokenOnLeftOfPosition(root, position); var rightToken = SyntaxFactsService.FindTokenOnRightOfPosition(root, position); // We apply a set of heuristics to determine which member we pick to start searching. var token = leftToken; if (leftToken != rightToken) { if (leftToken.Span.End == position && rightToken.SpanStart == position) { // If both tokens are touching, we prefer identifiers and keywords to // separators. Note that the language doesn't allow both tokens to be a // keyword or identifier. if (SyntaxFactsService.IsKeyword(rightToken) || SyntaxFactsService.IsIdentifier(rightToken)) { token = rightToken; } } else if (leftToken.Span.End < position && rightToken.SpanStart <= position) { // If only the right token is touching, we have to use it. token = rightToken; } } // If we ended up using the left token but the position is after that token, // walk up to the first node who's last token is not the leftToken. By doing this, we // ensure that we don't find members when the position is actually between them. // In that case, we should find the enclosing type or namespace. var parent = token.Parent; if (token == leftToken && position > token.Span.End) { while (parent != null) { if (parent.GetLastToken() == token) { parent = parent.Parent; } else { break; } } } var node = parent != null ? parent.AncestorsAndSelf().FirstOrDefault(n => CodeModelService.MatchesScope(n, scope)) : null; if (node == null) { return(null); } if (scope == EnvDTE.vsCMElement.vsCMElementAttribute || scope == EnvDTE.vsCMElement.vsCMElementImportStmt || scope == EnvDTE.vsCMElement.vsCMElementParameter || scope == EnvDTE.vsCMElement.vsCMElementOptionStmt || scope == EnvDTE.vsCMElement.vsCMElementInheritsStmt || scope == EnvDTE.vsCMElement.vsCMElementImplementsStmt || (scope == EnvDTE.vsCMElement.vsCMElementFunction && CodeModelService.IsAccessorNode(node))) { // Attributes, imports, parameters, Option, Inherts and Implements // don't have node keys of their own and won't be included in our // collection of elements. Delegate to the service to create these. return(CodeModelService.CreateInternalCodeElement(State, this, node)); } return(CreateCodeElement <EnvDTE.CodeElement>(node)); }