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 @@
<Compile Include="Src\CodeEditorView.cs" /> <Compile Include="Src\CodeEditorView.cs" />
<Compile Include="Src\ContextActionsRenderer.cs" /> <Compile Include="Src\ContextActionsRenderer.cs" />
<Compile Include="Src\ExpressionHighlightRenderer.cs" /> <Compile Include="Src\ExpressionHighlightRenderer.cs" />
<Compile Include="Src\ExtendSelection.cs" /> <Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\NewLineConsistencyCheck.cs" /> <Compile Include="Src\NewLineConsistencyCheck.cs" />
<Compile Include="Src\SharpDevelopTextEditor.cs" /> <Compile Include="Src\SharpDevelopTextEditor.cs" />
<Compile Include="Src\Commands\FoldingCommands.cs" /> <Compile Include="Src\Commands\FoldingCommands.cs" />

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

@ -393,7 +393,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
base.OnKeyUp(e); base.OnKeyUp(e);
if (e.Handled) return; if (e.Handled) return;
if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) {
ExtendSelection.Run(this.Adapter); CodeManipulation.ExtendSelection(this.Adapter);
} }
} }
#endregion #endregion

120
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ExtendSelection.cs → 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) // 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) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory; using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring; using ICSharpCode.SharpDevelop.Refactoring;
@ -15,36 +17,52 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// <summary> /// <summary>
/// Description of ExtendSelection. /// Description of ExtendSelection.
/// </summary> /// </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); MoveStatement(editor, MoveStatementDirection.Up);
if (editorLang == null) return; }
var parser = ParserFactory.CreateParser(editorLang.Value, editor.Document.CreateReader());
if (parser == null) return; public static void MoveStatementDown(ITextEditor editor)
parser.ParseMethodBodies = true; {
parser.Lexer.SkipAllComments = false; MoveStatement(editor, MoveStatementDirection.Down);
parser.Parse(); }
var parsedCU = parser.CompilationUnit;
if (parsedCU == null) return; 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 selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart);
var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength); 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( 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;
// 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) {
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 // 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(); 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)) { if (commentIndex >= 0 && IsWhitespaceBetween(editor.Document, comments[commentIndex].EndPosition, selectionStart)) {
while (commentIndex >= 0 && comments[commentIndex].EndPosition.Line == selectionStart.Line) while (commentIndex >= 0 && comments[commentIndex].EndPosition.Line == selectionStart.Line)
{ {
@ -56,20 +74,27 @@ namespace ICSharpCode.AvalonEdit.AddIn
} }
} }
else { else {
// if the extended selection would contain blank lines, extend the selection only to the blank lines/comments on both sides (use siblings) var parent = GetInterestingParent(currentNode, interestingNodeTypes);
// if the selection contains blank lines or comments on both sides, dont do this
var parent = currentNode.Parent;
// it can happen that parent region exactly matches child region - in this case we need to advance even to the next parent // 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 // bc otherwise the selection would never move
while (parent != null && parent.StartLocation == selectionStart && parent.EndLocation == selectionEnd) { while (parent != null && parent.StartLocation == selectionStart && parent.EndLocation == selectionEnd) {
parent = parent.Parent; parent = GetInterestingParent(parent, interestingNodeTypes);
} }
if (parent == null) if (parent == null)
return; return;
selectionStart = parent.StartLocation; var extendedSelectionStart = parent.StartLocation;
selectionEnd = parent.EndLocation; 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 { } else {
// select current node // select current node
@ -86,11 +111,56 @@ namespace ICSharpCode.AvalonEdit.AddIn
editor.Select(startOffset, endOffset - startOffset); 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) static bool IsWhitespaceBetween(IDocument document, Location startPos, Location endPos)
{ {
int startOffset = document.PositionToOffset(startPos); int startOffset = document.PositionToOffset(startPos);
int endOffset = document.PositionToOffset(endPos); int endOffset = document.PositionToOffset(endPos);
return string.IsNullOrWhiteSpace(document.GetText(startOffset, endOffset - startOffset)); 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