Browse Source

Extend selection - refactoring.

pull/15/head
mkonicek 15 years ago
parent
commit
3eb705c21f
  1. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  2. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
  3. 120
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -94,7 +94,7 @@ @@ -94,7 +94,7 @@
<Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\ContextActionsRenderer.cs" />
<Compile Include="Src\ExpressionHighlightRenderer.cs" />
<Compile Include="Src\ExtendSelection.cs" />
<Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\NewLineConsistencyCheck.cs" />
<Compile Include="Src\SharpDevelopTextEditor.cs" />
<Compile Include="Src\Commands\FoldingCommands.cs" />

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

@ -393,7 +393,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -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

120
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ExtendSelection.cs → src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs

@ -1,10 +1,12 @@ @@ -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 @@ -15,36 +17,52 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// <summary>
/// Description of ExtendSelection.
/// </summary>
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<ISpecial> 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<Comment>().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 @@ -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<BlankLine>().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 @@ -86,11 +111,56 @@ namespace ICSharpCode.AvalonEdit.AddIn
editor.Select(startOffset, endOffset - startOffset);
}
/// <summary>
/// Searches for parent of interesting type. Skips uninteresting parents.
/// </summary>
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<BlankLine> 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<ISpecial> 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;
}
}
}
Loading…
Cancel
Save