public bool MoveSelectedLinesDown()

in src/Editor/Text/Impl/EditorOperations/EditorOperations.cs [339:550]


        public bool MoveSelectedLinesDown()
        {
            Func<bool> action = () =>
            {
                bool success = false;

                // find line start
                ITextViewLine startViewLine = GetLineStart(_textView, _textView.Selection.Start.Position);
                SnapshotPoint start = startViewLine.Start;
                ITextSnapshotLine startLine = start.GetContainingLine();

                // find the last line view
                ITextViewLine endViewLine = GetLineEnd(_textView, _textView.Selection.End.Position);
                ITextSnapshotLine endLine = endViewLine.End.GetContainingLine();

                ITextSnapshot snapshot = endLine.Snapshot;

                // Handle the case where multiple lines are selected and the caret is sitting just after the line break on the next line.
                // Shortening the selection here handles the case where the last line is a collapsed region. Using endLine.End will give
                // a line within the collapsed region instead of skipping it all together. 
                if (GetLineEnd(_textView, startViewLine.Start) != endViewLine
                    && _textView.Selection.End.Position == GetLineStart(_textView, _textView.Selection.End.Position).Start
                    && !_textView.Selection.End.IsInVirtualSpace)
                {
                    endLine = snapshot.GetLineFromLineNumber(endLine.LineNumber - 1);
                    endViewLine = _textView.GetTextViewLineContainingBufferPosition(_textView.Selection.End.Position - 1);
                }

                #region Initial Asserts

                Debug.Assert(_textView.Selection.Start.Position.Snapshot == _textView.TextSnapshot, "Selection is out of sync with view.");

                Debug.Assert(_textView.TextSnapshot == _textView.TextBuffer.CurrentSnapshot, "View is out of sync with text buffer.");

                Debug.Assert(_textView.TextSnapshot == snapshot, "Text view lines are out of sync with the view");

                #endregion

                // check if we are at the end of the file
                if ((endLine.LineNumber + 1) >= snapshot.LineCount)
                {
                    // noop
                    success = true;
                }
                else
                {
                    // nextLineExtent is different from prevLine.Extent and avoids issues around collapsed regions
                    ITextViewLine lastNextLine = GetLineEnd(_textView, endViewLine.EndIncludingLineBreak);
                    SnapshotSpan nextLineExtent = new SnapshotSpan(endViewLine.EndIncludingLineBreak, lastNextLine.End);
                    SnapshotSpan nextLineExtentIncludingLineBreak = new SnapshotSpan(endViewLine.EndIncludingLineBreak, lastNextLine.EndIncludingLineBreak);

                    using (ITextEdit edit = _textView.TextBuffer.CreateEdit())
                    {
                        SnapshotSpan curLineExtent = new SnapshotSpan(startViewLine.Start, endViewLine.End);
                        SnapshotSpan curLineExtentIncLineBreak = new SnapshotSpan(startViewLine.Start, endViewLine.EndIncludingLineBreak);
                        string curLineText = curLineExtentIncLineBreak.GetText();

                        string nextLineText = nextLineExtentIncludingLineBreak.GetText();

                        if (nextLineText.Length == 0)
                        {
                            // end of file - noop
                            success = true;
                        }
                        else
                        {
                            List<Tuple<Span, IOutliningRegionTag>> collapsedSpansInCurLine = null;
                            bool hasCollapsedRegions = false;

                            IOutliningManager outliningManager = (_factory.OutliningManagerService != null)
                                                                    ? _factory.OutliningManagerService.GetOutliningManager(_textView)
                                                                    : null;

                            if (outliningManager != null)
                            {
                                collapsedSpansInCurLine = outliningManager.GetCollapsedRegions(new NormalizedSnapshotSpanCollection(curLineExtent))
                                    .Select(collapsed => Tuple.Create(collapsed.Extent.GetSpan(curLineExtent.Snapshot).Span, collapsed.Tag)).ToList();

                                hasCollapsedRegions = collapsedSpansInCurLine.Count > 0;

                                // check if we have collapsed spans in the selection and add the undo primitive if so
                                if (hasCollapsedRegions)
                                {
                                    using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesDown))
                                    {
                                        BeforeCollapsedMoveUndoPrimitive undoPrim = new BeforeCollapsedMoveUndoPrimitive(outliningManager, _textView, collapsedSpansInCurLine);
                                        undoTransaction.AddUndo(undoPrim);
                                        undoTransaction.Complete();
                                    }
                                }
                            }


                            int offset = nextLineText.Length;

                            // a line without a line break
                            if (nextLineExtent == nextLineExtentIncludingLineBreak)
                            {
                                string lineBreakText = new SnapshotSpan(startLine.End, startLine.EndIncludingLineBreak).GetText();

                                offset += lineBreakText.Length;

                                curLineText = lineBreakText + curLineText.Substring(0, curLineText.Length - lineBreakText.Length);
                            }


                            edit.Delete(curLineExtentIncLineBreak);
                            edit.Insert(nextLineExtentIncludingLineBreak.End, curLineText);

                            if (edit.HasFailedChanges)
                            {
                                success = false;
                            }
                            else
                            {
                                int anchorPos = _textView.Selection.AnchorPoint.Position.Position;
                                int anchorVirtualSpace = _textView.Selection.AnchorPoint.VirtualSpaces;
                                int activePos = _textView.Selection.ActivePoint.Position.Position;
                                int activeVirtualSpace = _textView.Selection.ActivePoint.VirtualSpaces;
                                var selectionMode = _textView.Selection.Mode;

                                ITextSnapshot newSnapshot = edit.Apply();
                                if (newSnapshot == snapshot)
                                {
                                    success = false;
                                }
                                else
                                {
                                    // Update the selection and caret position after the move
                                    ITextSnapshot currentSnapshot = snapshot.TextBuffer.CurrentSnapshot;
                                    VirtualSnapshotPoint desiredAnchor = new VirtualSnapshotPoint(new SnapshotPoint(newSnapshot, Math.Min(anchorPos + offset, newSnapshot.Length)),
                                        anchorVirtualSpace).TranslateTo(currentSnapshot, PointTrackingMode.Negative);
                                    VirtualSnapshotPoint desiredActive = new VirtualSnapshotPoint(new SnapshotPoint(newSnapshot, Math.Min(activePos + offset, newSnapshot.Length)),
                                        activeVirtualSpace).TranslateTo(currentSnapshot, PointTrackingMode.Negative);

                                    // keep the caret position and selection after the move
                                    SelectAndMoveCaret(desiredAnchor, desiredActive, selectionMode, EnsureSpanVisibleOptions.None);

                                    // Recollapse the spans
                                    if (outliningManager != null && hasCollapsedRegions)
                                    {
                                        // This comes from adhocoutliner.cs in env\editor\pkg\impl\outlining and will not be available outside of VS
                                        SimpleTagger<IOutliningRegionTag> simpleTagger =
                                            _textView.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(() => new SimpleTagger<IOutliningRegionTag>(_textView.TextBuffer));

                                        if (simpleTagger != null)
                                        {
                                            if (hasCollapsedRegions)
                                            {
                                                List<Tuple<ITrackingSpan, IOutliningRegionTag>> addedSpans = collapsedSpansInCurLine.Select(tuple => Tuple.Create(newSnapshot.CreateTrackingSpan(tuple.Item1.Start + offset,
                                                    tuple.Item1.Length, SpanTrackingMode.EdgeExclusive), tuple.Item2)).ToList();

                                                if (addedSpans.Count > 0)
                                                {
                                                    List<Tuple<Span, IOutliningRegionTag>> spansForUndo = new List<Tuple<Span, IOutliningRegionTag>>();

                                                    // add spans to tracking
                                                    foreach (var addedSpan in addedSpans)
                                                    {
                                                        simpleTagger.CreateTagSpan(addedSpan.Item1, addedSpan.Item2);
                                                        spansForUndo.Add(new Tuple<Span, IOutliningRegionTag>(addedSpan.Item1.GetSpan(newSnapshot), addedSpan.Item2));
                                                    }

                                                    SnapshotSpan changedSpan = new SnapshotSpan(addedSpans.Select(tuple => tuple.Item1.GetSpan(newSnapshot).Start).Min(),
                                                        addedSpans.Select(tuple => tuple.Item1.GetSpan(newSnapshot).End).Max());

                                                    List<SnapshotSpan> addedSnapshotSpans = addedSpans.Select(tuple => tuple.Item1.GetSpan(newSnapshot)).ToList();

                                                    bool disableOutliningUndo = _editorOptions.IsOutliningUndoEnabled();

                                                    // Recollapse the span
                                                    // We need to disable the OutliningUndoManager for this operation otherwise an undo will expand it
                                                    try
                                                    {
                                                        if (disableOutliningUndo)
                                                        {
                                                            _textView.Options.SetOptionValue(DefaultTextViewOptions.OutliningUndoOptionId, false);
                                                        }

                                                        outliningManager.CollapseAll(changedSpan, collapsible => addedSnapshotSpans.Contains(collapsible.Extent.GetSpan(newSnapshot)));
                                                    }
                                                    finally
                                                    {
                                                        if (disableOutliningUndo)
                                                        {
                                                            _textView.Options.SetOptionValue(DefaultTextViewOptions.OutliningUndoOptionId, true);
                                                        }
                                                    }

                                                    // we need to recollapse after a redo
                                                    using (ITextUndoTransaction undoTransaction = _undoHistory.CreateTransaction(Strings.MoveSelLinesDown))
                                                    {
                                                        AfterCollapsedMoveUndoPrimitive undoPrim = new AfterCollapsedMoveUndoPrimitive(outliningManager, _textView, spansForUndo);
                                                        undoTransaction.AddUndo(undoPrim);
                                                        undoTransaction.Complete();
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    success = true;
                                }
                            }
                        }
                    }
                }
                return success;
            };

            return ExecuteAction(Strings.MoveSelLinesDown, action, SelectionUpdate.Ignore, true);
        }