Browse Source

Basic implementation of Move statement up / down.

pull/15/head
mkonicek 15 years ago
parent
commit
85341978e6
  1. 8
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
  2. 79
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs
  3. 11
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs

8
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs

@ -395,6 +395,14 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) {
CodeManipulation.ExtendSelection(this.Adapter); CodeManipulation.ExtendSelection(this.Adapter);
} }
if (e.SystemKey == Key.Up && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) {
// Left Alt + Up (probably will have different shortcut)
CodeManipulation.MoveStatementUp(this.Adapter);
}
if (e.SystemKey == Key.Down && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) {
// Left Alt + Down (probably will have different shortcut)
CodeManipulation.MoveStatementDown(this.Adapter);
}
} }
#endregion #endregion

79
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs

@ -24,38 +24,92 @@ namespace ICSharpCode.AvalonEdit.AddIn
public static void MoveStatementUp(ITextEditor editor) public static void MoveStatementUp(ITextEditor editor)
{ {
MoveStatement(editor, MoveStatementDirection.Up); MoveStatement(editor, MoveStatementDirection.Up);
editor.ClearSelection();
} }
public static void MoveStatementDown(ITextEditor editor) public static void MoveStatementDown(ITextEditor editor)
{ {
MoveStatement(editor, MoveStatementDirection.Down); MoveStatement(editor, MoveStatementDirection.Down);
editor.ClearSelection();
} }
// move selection - find outermost node in selection, swap selection with closest child of its parent to the selection
static void MoveStatement(ITextEditor editor, MoveStatementDirection direction) static void MoveStatement(ITextEditor editor, MoveStatementDirection direction)
{ {
// Find the Statement or Definition containing caret -> Extend selection to Statement or Definition // Find the Statement or Definition containing caret -> Extend selection to Statement or Definition
INode currentStatement = ExtendSelection(editor, new Type[] { typeof(Statement) });
if (currentStatement == null)
return;
// Take its sibling // Take its sibling
if (currentStatement.Parent == null)
return;
var siblings = currentStatement.Parent.Children;
int currentStatementPos = siblings.IndexOf(currentStatement);
int swapIndex = currentStatementPos + (direction == MoveStatementDirection.Down ? 1 : -1);
if (swapIndex < 0 || swapIndex >= siblings.Count)
return;
INode swapSibling = siblings[swapIndex];
// Swap them // Swap them
string currentNodeText = editor.Document.GetText(currentStatement.StartLocation, currentStatement.EndLocation);
SwapText(editor.Document, currentStatement.StartLocation, currentStatement.EndLocation, swapSibling.StartLocation, swapSibling.EndLocation);
// Move caret to the start of moved statement
Location upperLocation = new Location[] {currentStatement.StartLocation, swapSibling.StartLocation}.Min();
if (direction == MoveStatementDirection.Up)
editor.Caret.Position = upperLocation;
else {
// look where current statement ended because it is hard to calculate it correctly
int currentMovedOffset = editor.Document.Text.IndexOf(currentNodeText, editor.Document.PositionToOffset(upperLocation));
editor.Caret.Offset = currentMovedOffset;
}
}
static void SwapText(IDocument document, Location start1, Location end1, Location start2, Location end2)
{
if (start1 > start2) {
Location sw;
sw = start1; start1 = start2; start2 = sw;
sw = end1; end1 = end2; end2 = sw;
}
if (end1 >= start2)
throw new InvalidOperationException("Cannot swap overlaping segments");
int offset1 = document.PositionToOffset(start1);
int len1 = document.PositionToOffset(end1) - offset1;
int offset2 = document.PositionToOffset(start2);
int len2 = document.PositionToOffset(end2) - offset2;
string text1 = document.GetText(offset1, len1);
string text2 = document.GetText(offset2, len2);
using (var undoGroup = document.OpenUndoGroup()) {
document.Replace(offset2, len2, text1);
document.Replace(offset1, len1, text2);
}
} }
public static void ExtendSelection(ITextEditor editor) public static void ExtendSelection(ITextEditor editor)
{ {
ExtendSelection(editor, new Type[] { typeof(Statement) }); // any node type ExtendSelection(editor, new Type[] { typeof(INode) }); // any node type
} }
// could work to extend selection to set of adjacent statements - e.g. select 3 lines // could work to extend selection to set of adjacent statements - e.g. select 3 lines
static void ExtendSelection(ITextEditor editor, Type[] interestingNodeTypes) static INode ExtendSelection(ITextEditor editor, Type[] interestingNodeTypes)
{ {
IList<ISpecial> commentsBlankLines; IList<ISpecial> commentsBlankLines;
var parsedCU = ParseDocument(editor, out commentsBlankLines); var parsedCU = ParseDocument(editor, out commentsBlankLines);
if (parsedCU == null) return null;
var selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart); var selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart);
var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength); var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength);
Ast.INode currentNode = parsedCU.Children.Select( Ast.INode currentNode = parsedCU.Children.Select(
n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).Where(n => n != null).FirstOrDefault(); n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).Where(n => n != null).FirstOrDefault();
if (currentNode == null) return; if (currentNode == null) return null;
if (!IsNodeTypeInteresting(currentNode, interestingNodeTypes)) {
// ignore uninteresting nodes in the AST
currentNode = GetInterestingParent(currentNode, interestingNodeTypes);
}
if (currentNode == null) return null;
var selectedResultNode = currentNode;
// whole node already selected -> expand selection // whole node already selected -> expand selection
if (currentNode.StartLocation == selectionStart && currentNode.EndLocation == selectionEnd) { if (currentNode.StartLocation == selectionStart && currentNode.EndLocation == selectionEnd) {
@ -67,9 +121,13 @@ namespace ICSharpCode.AvalonEdit.AddIn
while (commentIndex >= 0 && comments[commentIndex].EndPosition.Line == selectionStart.Line) while (commentIndex >= 0 && comments[commentIndex].EndPosition.Line == selectionStart.Line)
{ {
var comment = comments[commentIndex]; var comment = comments[commentIndex];
// Move selection start to include comment
selectionStart = comment.StartPosition; selectionStart = comment.StartPosition;
if (comment.CommentStartsLine) if (comment.CommentStartsLine)
selectionStart.Column = 1; selectionStart.Column = 1;
// selected node stays the same
selectedResultNode = currentNode;
commentIndex--; commentIndex--;
} }
} }
@ -81,9 +139,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
parent = GetInterestingParent(parent, interestingNodeTypes); parent = GetInterestingParent(parent, interestingNodeTypes);
} }
if (parent == null) if (parent == null)
return; return currentNode;
// Select the parent
var extendedSelectionStart = parent.StartLocation; var extendedSelectionStart = parent.StartLocation;
var extendedLocationEnd = parent.EndLocation; var extendedLocationEnd = parent.EndLocation;
selectedResultNode = parent;
// if the extended selection would contain blank lines, extend the selection only to the blank lines/comments on both sides (use siblings) // if the extended selection would contain blank lines, extend the selection only to the blank lines/comments on both sides (use siblings)
// if the selection contains blank lines or comments on both sides, dont do this // if the selection contains blank lines or comments on both sides, dont do this
@ -100,15 +160,17 @@ namespace ICSharpCode.AvalonEdit.AddIn
// select current node // select current node
selectionStart = currentNode.StartLocation; selectionStart = currentNode.StartLocation;
selectionEnd = currentNode.EndLocation; selectionEnd = currentNode.EndLocation;
selectedResultNode = currentNode;
} }
int startOffset, endOffset; int startOffset, endOffset;
try { try {
startOffset = editor.Document.PositionToOffset(selectionStart); startOffset = editor.Document.PositionToOffset(selectionStart);
endOffset = editor.Document.PositionToOffset(selectionEnd); endOffset = editor.Document.PositionToOffset(selectionEnd);
} catch(ArgumentOutOfRangeException) { } catch(ArgumentOutOfRangeException) {
return; return null;
} }
editor.Select(startOffset, endOffset - startOffset); editor.Select(startOffset, endOffset - startOffset);
return selectedResultNode;
} }
/// <summary> /// <summary>
@ -118,15 +180,16 @@ namespace ICSharpCode.AvalonEdit.AddIn
{ {
var parent = node.Parent; var parent = node.Parent;
while(parent != null) { while(parent != null) {
if (IsNodeTypeInteresting(parent.GetType(), interestingNodeTypes)) if (IsNodeTypeInteresting(parent, interestingNodeTypes))
return parent; return parent;
parent = parent.Parent; parent = parent.Parent;
} }
return null; return null;
} }
static bool IsNodeTypeInteresting(Type nodeType, Type[] interestingNodeTypes) static bool IsNodeTypeInteresting(INode node, Type[] interestingNodeTypes)
{ {
Type nodeType = node.GetType();
return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType)); return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType));
} }

11
src/Main/Base/Project/Src/Util/ExtensionMethods.cs

@ -595,6 +595,17 @@ namespace ICSharpCode.SharpDevelop
{ {
return document.PositionToOffset(location.Line, location.Column); return document.PositionToOffset(location.Line, location.Column);
} }
public static string GetText(this IDocument document, Location startPos, Location endPos)
{
int startOffset = document.PositionToOffset(startPos);
return document.GetText(startOffset, document.PositionToOffset(endPos) - startOffset);
}
public static void ClearSelection(this ITextEditor editor)
{
editor.Select(editor.Document.PositionToOffset(editor.Caret.Position), 0);
}
#endregion #endregion
} }
} }

Loading…
Cancel
Save