diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
index 7c07e15741..57b8aa69a1 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
@@ -94,7 +94,7 @@
-
+
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
index 76b4e28da1..6b6ef4dccd 100755
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
@@ -393,7 +393,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
base.OnKeyUp(e);
if (e.Handled) return;
if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) {
- ExtendSelection.Run(this.Adapter);
+ CodeManipulation.ExtendSelection(this.Adapter);
}
}
#endregion
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ExtendSelection.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs
similarity index 55%
rename from src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ExtendSelection.cs
rename to src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs
index 8d98e315bd..631e722ef0 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ExtendSelection.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs
@@ -1,10 +1,12 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.NRefactory;
+using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
@@ -15,36 +17,52 @@ namespace ICSharpCode.AvalonEdit.AddIn
///
/// Description of ExtendSelection.
///
- public class ExtendSelection
+ public class CodeManipulation
{
- public static void Run(ITextEditor editor)
+ enum MoveStatementDirection { Up, Down };
+
+ public static void MoveStatementUp(ITextEditor editor)
{
- var editorLang = EditorContext.GetEditorLanguage(editor);
- if (editorLang == null) return;
- var parser = ParserFactory.CreateParser(editorLang.Value, editor.Document.CreateReader());
- if (parser == null) return;
- parser.ParseMethodBodies = true;
- parser.Lexer.SkipAllComments = false;
- parser.Parse();
- var parsedCU = parser.CompilationUnit;
- if (parsedCU == null) return;
+ MoveStatement(editor, MoveStatementDirection.Up);
+ }
+
+ public static void MoveStatementDown(ITextEditor editor)
+ {
+ MoveStatement(editor, MoveStatementDirection.Down);
+ }
+
+ static void MoveStatement(ITextEditor editor, MoveStatementDirection direction)
+ {
+ // Find the Statement or Definition containing caret -> Extend selection to Statement or Definition
+
+ // Take its sibling
+ // Swap them
+ }
+
+ public static void ExtendSelection(ITextEditor editor)
+ {
+ ExtendSelection(editor, new Type[] { typeof(Statement) }); // any node type
+ }
+
+ // could work to extend selection to set of adjacent statements - e.g. select 3 lines
+ static void ExtendSelection(ITextEditor editor, Type[] interestingNodeTypes)
+ {
+ IList commentsBlankLines;
+ var parsedCU = ParseDocument(editor, out commentsBlankLines);
+
var selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart);
var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength);
- foreach (var node in parsedCU.Children) {
- // fix StartLocation / EndLocation
- node.AcceptVisitor(new ICSharpCode.NRefactory.Visitors.SetRegionInclusionVisitor(), null);
- }
+
Ast.INode currentNode = parsedCU.Children.Select(
n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).Where(n => n != null).FirstOrDefault();
if (currentNode == null) return;
// whole node already selected -> expand selection
if (currentNode.StartLocation == selectionStart && currentNode.EndLocation == selectionEnd) {
- var commentsBlankLines = parser.Lexer.SpecialTracker.CurrentSpecials;
// if there is a comment block immediately before selection, or behind selection on the same line, add it to selection
var comments = commentsBlankLines.Where(s => s is Comment).Cast().ToList();
- int commentIndex = comments.FindIndex(c => c.EndPosition.Line == currentNode.StartLocation.Line);
+ int commentIndex = comments.FindIndex(c => c.EndPosition.Line == selectionStart.Line);
if (commentIndex >= 0 && IsWhitespaceBetween(editor.Document, comments[commentIndex].EndPosition, selectionStart)) {
while (commentIndex >= 0 && comments[commentIndex].EndPosition.Line == selectionStart.Line)
{
@@ -56,20 +74,27 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
}
else {
- // 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
-
-
- var parent = currentNode.Parent;
+ var parent = GetInterestingParent(currentNode, interestingNodeTypes);
// it can happen that parent region exactly matches child region - in this case we need to advance even to the next parent
// bc otherwise the selection would never move
while (parent != null && parent.StartLocation == selectionStart && parent.EndLocation == selectionEnd) {
- parent = parent.Parent;
+ parent = GetInterestingParent(parent, interestingNodeTypes);
}
if (parent == null)
return;
- selectionStart = parent.StartLocation;
- selectionEnd = parent.EndLocation;
+ var extendedSelectionStart = parent.StartLocation;
+ var extendedLocationEnd = parent.EndLocation;
+
+ // 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
+ var blankLines = commentsBlankLines.Where(s => s is BlankLine).Cast().ToList();
+ //if (SelectionContainsBlankLines(extendedSelectionStart, extendedLocationEnd, blankLines)) {
+ if (false) { // implement later
+
+ } else {
+ selectionStart = extendedSelectionStart;
+ selectionEnd = extendedLocationEnd;
+ }
}
} else {
// select current node
@@ -86,11 +111,56 @@ namespace ICSharpCode.AvalonEdit.AddIn
editor.Select(startOffset, endOffset - startOffset);
}
+ ///
+ /// Searches for parent of interesting type. Skips uninteresting parents.
+ ///
+ static INode GetInterestingParent(INode node, Type[] interestingNodeTypes)
+ {
+ var parent = node.Parent;
+ while(parent != null) {
+ if (IsNodeTypeInteresting(parent.GetType(), interestingNodeTypes))
+ return parent;
+ parent = parent.Parent;
+ }
+ return null;
+ }
+
+ static bool IsNodeTypeInteresting(Type nodeType, Type[] interestingNodeTypes)
+ {
+ return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType));
+ }
+
+ static bool SelectionContainsBlankLines(Location selectionStart, Location selectionEnd, List blankLines)
+ {
+ return blankLines.Exists(b => b.StartPosition >= selectionStart && b.EndPosition <= selectionEnd);
+ }
+
static bool IsWhitespaceBetween(IDocument document, Location startPos, Location endPos)
{
int startOffset = document.PositionToOffset(startPos);
int endOffset = document.PositionToOffset(endPos);
return string.IsNullOrWhiteSpace(document.GetText(startOffset, endOffset - startOffset));
}
+
+ // could depend just on IDocument
+ static CompilationUnit ParseDocument(ITextEditor editor, out IList parsedSpecials)
+ {
+ parsedSpecials = null;
+ var editorLang = EditorContext.GetEditorLanguage(editor);
+ if (editorLang == null) return null;
+ var parser = ParserFactory.CreateParser(editorLang.Value, editor.Document.CreateReader());
+ if (parser == null) return null;
+ parser.ParseMethodBodies = true;
+ parser.Lexer.SkipAllComments = false;
+ parser.Parse();
+ var parsedCU = parser.CompilationUnit;
+ if (parsedCU == null) return null;
+ foreach (var node in parsedCU.Children) {
+ // fix StartLocation / EndLocation
+ node.AcceptVisitor(new ICSharpCode.NRefactory.Visitors.SetRegionInclusionVisitor(), null);
+ }
+ parsedSpecials = parser.Lexer.SpecialTracker.CurrentSpecials;
+ return parsedCU;
+ }
}
}