|
|
@ -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)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|