Example #1
0
        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));
        }
Example #2
0
            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);
            }
Example #3
0
            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);
            }
Example #4
0
            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);
            }
Example #5
0
        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();
        }
Example #6
0
        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);
        }