public SnapshotSpan GetSpanOfEnclosing(SnapshotSpan activeSpan) { if (!XmlBackgroundParser.TryGetParser(activeSpan.Snapshot.TextBuffer, out var parser)) { return(codeNavigator.GetSpanOfEnclosing(activeSpan)); } // use last parse if it's up to date, which is most likely will be // else use a spine from the end of the selection and update as needed var lastParse = parser.LastOutput; List <XObject> nodePath; XmlSpineParser spine = null; if (lastParse != null && lastParse.TextSnapshot.Version.VersionNumber == activeSpan.Snapshot.Version.VersionNumber) { var n = lastParse.XDocument.FindAtOrBeforeOffset(activeSpan.Start.Position); nodePath = n.GetPath(); } else { spine = parser.GetSpineParser(activeSpan.Start); nodePath = spine.AdvanceToNodeEndAndGetNodePath(activeSpan.Snapshot); } // this is a little odd because it was ported from MonoDevelop, where it has to maintain its own stack of state // for contract selection. it describes the current semantic selection as a node path, the index of the node in that path // that's selected, and the kind of selection that node has. int selectedNodeIndex = nodePath.Count; SelectionLevel selectionLevel = default; // keep on expanding the selection until we find one that contains the current selection but is a little bigger while (ExpandSelection(nodePath, spine, activeSpan, ref selectedNodeIndex, ref selectionLevel)) { var selectionSpan = GetSelectionSpan(activeSpan.Snapshot, nodePath, ref selectedNodeIndex, ref selectionLevel); if (selectionSpan is TextSpan s && s.Start <= activeSpan.Start && s.End >= activeSpan.End && s.Length > activeSpan.Length) { var selectionSnapshotSpan = new SnapshotSpan(activeSpan.Snapshot, s.Start, s.Length); // if we're in content, the code navigator may be able to make a useful smaller expansion if (selectionLevel == SelectionLevel.Content) { var codeNavigatorSpan = codeNavigator.GetSpanOfEnclosing(activeSpan); if (selectionSnapshotSpan.Contains(codeNavigatorSpan)) { return(codeNavigatorSpan); } } return(selectionSnapshotSpan); } } return(codeNavigator.GetSpanOfEnclosing(activeSpan)); }
public DocumentRegion?Shrink() { // if we have expansion state, pop it if (expansions.Count > 0) { var last = expansions.Pop(); Index = last.Item1; Level = last.Item2; return(GetCurrent()); } return(null); }
public DocumentRegion?Shrink() { // if we have expansion state, pop it if (!expansions.IsEmpty) { expansions = expansions.Pop(out var last); Index = last.Item1; Level = last.Item2; return(GetCurrent()); } return(null); }
bool GrowStateInternal() { if (Index + 1 == NodePath.Count) { return(false); } //if an index is selected, we may need to transition level rather than transitioning index if (Index >= 0) { var current = NodePath [Index]; if (current is XElement element) { switch (Level) { case SelectionLevel.Self: if (!element.IsSelfClosing) { Level = SelectionLevel.WithClosing; return(true); } break; case SelectionLevel.Content: Level = SelectionLevel.WithClosing; return(true); case SelectionLevel.Name: Level = SelectionLevel.Self; return(true); case SelectionLevel.Attributes: Level = SelectionLevel.Self; return(true); } } else if (current is XAttribute att) { switch (Level) { case SelectionLevel.Name: case SelectionLevel.Content: Level = SelectionLevel.Self; return(true); } } else if (Level == SelectionLevel.Name) { Level = SelectionLevel.Self; return(true); } else if (Level == SelectionLevel.Document) { return(false); } } //advance up the node path Index++; var newNode = NodePath [Index]; //determine the starting selection level for the new node if (newNode is XDocument) { Level = SelectionLevel.Document; return(true); } AdvanceUntilClosed(newNode, parser, document); if (newNode.Region.ContainsOuter(editor.CaretLocation)) { var nr = newNode.TryGetNameRegion(); if (nr != null && nr.Value.ContainsOuter(editor.CaretLocation)) { Level = SelectionLevel.Name; return(true); } if (newNode is XAttribute attribute) { var valRegion = attribute.GetAttributeValueRegion(document); if (valRegion.ContainsOuter(editor.CaretLocation)) { Level = SelectionLevel.Content; return(true); } } if (newNode is XElement xElement && xElement.Attributes.Count > 1) { var attsRegion = xElement.GetAttributesRegion(); if (attsRegion.ContainsOuter(editor.CaretLocation)) { Level = SelectionLevel.Attributes; return(true); } } Level = SelectionLevel.Self; return(true); } if (newNode is XElement el) { if (el.IsSelfClosing) { Level = SelectionLevel.Self; return(true); } if (el.ClosingTag.Region.ContainsOuter(editor.CaretLocation)) { Level = SelectionLevel.WithClosing; return(true); } Level = SelectionLevel.Content; return(true); } Level = SelectionLevel.Self; return(true); }
TextSpan?GetSelectionSpan(ITextSnapshot snapshot, List <XObject> nodePath, ref int index, ref SelectionLevel level) { if (index < 0 || index >= nodePath.Count) { return(null); } var current = nodePath[index]; switch (level) { case SelectionLevel.Self: return(current.Span); case SelectionLevel.OuterElement: var element = (XElement)current; return(element.OuterSpan); case SelectionLevel.Name: return((current as INamedXObject)?.NameSpan); case SelectionLevel.Content: if (current is XElement el) { return(el.InnerSpan); } if (current is XAttribute att) { return(att.ValueSpan); } if (current is XText text) { return(text.Span); } return(null); case SelectionLevel.Document: return(new TextSpan(0, snapshot.Length)); case SelectionLevel.Attributes: return((current as IAttributedXObject)?.GetAttributesSpan()); } throw new InvalidOperationException(); }
bool ExpandSelection(List <XObject> nodePath, XmlSpineParser spine, SnapshotSpan activeSpan, ref int index, ref SelectionLevel level) { if (index - 1 < 0) { return(false); } //if an index is selected, we may need to transition level rather than transitioning index if (index < nodePath.Count) { var current = nodePath[index]; if (current is XElement element) { switch (level) { case SelectionLevel.Self: if (spine != null && !spine.AdvanceUntilClosed(element, activeSpan.Snapshot, 5000)) { return(false); } if (!element.IsSelfClosing) { level = SelectionLevel.OuterElement; return(true); } break; case SelectionLevel.Content: level = SelectionLevel.OuterElement; return(true); case SelectionLevel.Name: level = SelectionLevel.Self; return(true); case SelectionLevel.Attributes: level = SelectionLevel.Self; return(true); } } else if (current is XAttribute) { switch (level) { case SelectionLevel.Name: case SelectionLevel.Content: level = SelectionLevel.Self; return(true); } } else if (level == SelectionLevel.Name) { level = SelectionLevel.Self; return(true); } else if (level == SelectionLevel.Document) { return(false); } } //advance up the node path index--; var newNode = nodePath[index]; //determine the starting selection level for the new node if (newNode is XDocument) { level = SelectionLevel.Document; return(true); } if (spine != null && !spine.AdvanceUntilEnded(newNode, activeSpan.Snapshot, 5000)) { return(false); } bool ContainsSelection(TextSpan span) => activeSpan.Start >= span.Start && activeSpan.End <= span.End; if (ContainsSelection(newNode.Span)) { if ((newNode as INamedXObject)?.NameSpan is TextSpan nr && ContainsSelection(nr)) { level = SelectionLevel.Name; return(true); } if (newNode is XAttribute attribute) { var valRegion = attribute.ValueSpan; if (ContainsSelection(valRegion)) { level = SelectionLevel.Content; return(true); } } if (newNode is XText) { level = SelectionLevel.Content; return(true); } if (newNode is XElement xElement && xElement.Attributes.Count > 1) { if (xElement.GetAttributesSpan() is TextSpan attsSpan && ContainsSelection(attsSpan)) { level = SelectionLevel.Attributes; return(true); } } level = SelectionLevel.Self; return(true); } if (spine != null && !spine.AdvanceUntilClosed(newNode, activeSpan.Snapshot, 5000)) { return(false); } if (newNode is XElement el && el.ClosingTag != null) { if (el.IsSelfClosing) { level = SelectionLevel.Self; return(true); } if (ContainsSelection((TextSpan)el.InnerSpan)) { level = SelectionLevel.Content; return(true); } level = SelectionLevel.OuterElement; return(true); } level = SelectionLevel.Self; return(true); }