public static MSBuildResolveResult Resolve( XmlSpineParser spineParser, ITextSource textSource, MSBuildDocument context, IFunctionTypeProvider functionTypeProvider, CancellationToken cancellationToken = default) { int offset = spineParser.Position; var nodePath = spineParser.AdvanceToNodeEndAndGetNodePath(textSource); nodePath.ConnectParents(); //need to look up element by walking how the path, since at each level, if the parent has special children, //then that gives us information to identify the type of its children MSBuildElementSyntax languageElement = null; MSBuildAttributeSyntax languageAttribute = null; XElement el = null; XAttribute att = null; foreach (var node in nodePath) { if (node is XAttribute xatt && xatt.Name.Prefix == null) { att = xatt; languageAttribute = languageElement?.GetAttribute(att.Name.Name); break; } //if children of parent is known to be arbitrary data, don't go into it if (languageElement != null && languageElement.ValueKind == MSBuildValueKind.Data) { break; } //code completion is forgiving, all we care about best guess resolve for deepest child if (node is XElement xel && xel.Name.Prefix == null) { el = xel; languageElement = MSBuildElementSyntax.Get(el.Name.Name, languageElement); if (languageElement != null) { continue; } } if (node is XText) { continue; } if (node is XClosingTag ct && ct == el.ClosingTag) { continue; } languageElement = null; } if (languageElement == null) { return(null); } var rr = new MSBuildResolveResult { ElementSyntax = languageElement, AttributeSyntax = languageAttribute, Element = el, Attribute = att }; var rv = new MSBuildResolveVisitor(offset, rr, functionTypeProvider); try { rv.Run(el, languageElement, textSource, context, token: cancellationToken); } catch (OperationCanceledException) when(cancellationToken.IsCancellationRequested) { // callers always have to handle the possibility this returns null // so this means callers don't need to handle cancellation exceptions explciitly return(null); } return(rr); }
public static MSBuildResolveResult Resolve( XmlParser parser, ITextSource textSource, MSBuildDocument context, IFunctionTypeProvider functionTypeProvider) { int offset = parser.Position; //clones and connects nodes to their parents parser = parser.GetTreeParser(); var nodePath = parser.Nodes.ToList(); nodePath.Reverse(); //capture incomplete names, attributes and element values int i = offset; if (parser.CurrentState is XmlRootState && parser.Nodes.Peek() is XElement unclosedEl) { while (i < textSource.Length && InRootOrClosingTagState() && !unclosedEl.IsClosed) { parser.Push(textSource.GetCharAt(i++)); } } else { while (i < textSource.Length && InNameOrAttributeState()) { parser.Push(textSource.GetCharAt(i++)); } } //if nodes are incomplete, they won't get connected //HACK: the only way to reconnect them is reflection if (nodePath.Count > 1) { for (int idx = 1; idx < nodePath.Count; idx++) { var node = nodePath[idx]; if (node.Parent == null) { var parent = nodePath[idx - 1]; ParentProp.SetValue(node, parent); } } } //need to look up element by walking how the path, since at each level, if the parent has special children, //then that gives us information to identify the type of its children MSBuildLanguageElement languageElement = null; MSBuildLanguageAttribute languageAttribute = null; XElement el = null; XAttribute att = null; foreach (var node in nodePath) { if (node is XAttribute xatt && xatt.Name.Prefix == null) { att = xatt; languageAttribute = languageElement?.GetAttribute(att.Name.Name); break; } //if children of parent is known to be arbitrary data, don't go into it if (languageElement != null && languageElement.ValueKind == MSBuildValueKind.Data) { break; } //code completion is forgiving, all we care about best guess resolve for deepest child if (node is XElement xel && xel.Name.Prefix == null) { el = xel; languageElement = MSBuildLanguageElement.Get(el.Name.Name, languageElement); if (languageElement != null) { continue; } } languageElement = null; } if (languageElement == null) { return(null); } var rr = new MSBuildResolveResult { LanguageElement = languageElement, LanguageAttribute = languageAttribute, XElement = el, XAttribute = att }; var rv = new MSBuildResolveVisitor(offset, rr, functionTypeProvider); rv.Run(el, languageElement, textSource, context); return(rr); bool InNameOrAttributeState() => parser.CurrentState is XmlNameState || parser.CurrentState is XmlAttributeState || parser.CurrentState is XmlAttributeValueState; bool InRootOrClosingTagState() => parser.CurrentState is XmlRootState || parser.CurrentState is XmlNameState || parser.CurrentState is XmlClosingTagState; }