Browse Source

CodeManipulation (Alt+Arrow code moving, Ctrl+W selection extension) now works again.

pull/45/merge
Andreas Weizel 12 years ago
parent
commit
95109a4af2
  1. 2
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
  2. 122
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs

2
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj

@ -67,6 +67,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Configuration\AssemblyInfo.cs" /> <Compile Include="Configuration\AssemblyInfo.cs" />
<Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\Completion\CSharpCompletionContext.cs" /> <Compile Include="Src\Completion\CSharpCompletionContext.cs" />
<Compile Include="Src\Completion\CSharpInsightItem.cs" /> <Compile Include="Src\Completion\CSharpInsightItem.cs" />
<Compile Include="Src\Completion\CSharpMethodInsight.cs" /> <Compile Include="Src\Completion\CSharpMethodInsight.cs" />
@ -104,7 +105,6 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<EmbeddedResource Include="Resources\MyNamespaceSupportForCSharp.cs" /> <EmbeddedResource Include="Resources\MyNamespaceSupportForCSharp.cs" />
<Compile Include="Src\CodeManipulation.cs" />
<Compile Include="Src\Completion\CompletionData.cs" /> <Compile Include="Src\Completion\CompletionData.cs" />
<Compile Include="Src\Completion\CSharpCompletionBinding.cs" /> <Compile Include="Src\Completion\CSharpCompletionBinding.cs" />
<Compile Include="Src\Completion\CSharpCompletionDataFactory.cs" /> <Compile Include="Src\Completion\CSharpCompletionDataFactory.cs" />

122
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs

@ -4,7 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
/*
using System.Windows.Input; using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
@ -13,11 +13,8 @@ using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.PatternMatching;
//using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Refactoring;
//using Ast = ICSharpCode.NRefactory.Ast;
namespace CSharpBinding namespace CSharpBinding
{ {
@ -139,46 +136,43 @@ namespace CSharpBinding
// move selection - find outermost node in selection, swap selection with closest child of its parent to the selection // move selection - find outermost node in selection, swap selection with closest child of its parent to the selection
void MoveStatement(ITextEditor editor, MoveStatementDirection direction) void MoveStatement(ITextEditor editor, MoveStatementDirection direction)
{ {
IList<ISpecial> commentsBlankLines; IList<AstNode> commentsBlankLines;
var parsedCU = ParseDocument(editor, out commentsBlankLines); var parsedCU = ParseDocument(editor, out commentsBlankLines);
if (parsedCU == null) return; if (parsedCU == null) return;
// 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; AstNode currentStatement;
Selection statementSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out currentStatement, new Type[] { Selection statementSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out currentStatement, new Type[] {
typeof(Statement), typeof(Statement),
typeof(MemberNode), typeof(EntityDeclaration) });
typeof(FieldDeclaration),
typeof(ConstructorDeclaration),
typeof(DestructorDeclaration) });
if (currentStatement == null) if (currentStatement == null)
return; return;
statementSelection = TryExtendSelectionToComments(editor.Document, statementSelection, commentsBlankLines); statementSelection = TryExtendSelectionToComments(editor.Document, statementSelection, commentsBlankLines);
// Take its sibling // Take its sibling
if (currentStatement.Parent == null) if (currentStatement.Parent == null)
return; return;
var siblings = currentStatement.Parent.Children; var siblings = currentStatement.Parent.Children.Where(c => (c.Role.GetType() == currentStatement.Role.GetType())).ToList();
int currentStatementPos = siblings.IndexOf(currentStatement); int currentStatementPos = siblings.IndexOf(currentStatement);
int swapIndex = currentStatementPos + (direction == MoveStatementDirection.Down ? 1 : -1); int swapIndex = currentStatementPos + (direction == MoveStatementDirection.Down ? 1 : -1);
if (swapIndex < 0 || swapIndex >= siblings.Count) if (swapIndex < 0 || swapIndex >= siblings.Count)
return; return;
INode swapSibling = siblings[swapIndex]; AstNode swapSibling = siblings[swapIndex];
// Swap them // Swap them
string currentNodeText = editor.Document.GetText(statementSelection.Start, statementSelection.End); string currentNodeText = editor.Document.GetText(statementSelection.Start, statementSelection.End);
SwapText(editor.Document, statementSelection.Start, statementSelection.End, swapSibling.StartLocation, swapSibling.EndLocation); SwapText(editor.Document, statementSelection.Start, statementSelection.End, swapSibling.StartLocation, swapSibling.EndLocation);
// Move caret to the start of moved statement // Move caret to the start of moved statement
TextLocation upperLocation = new TextLocation[] {statementSelection.Start, swapSibling.StartLocation}.Min(); TextLocation upperLocation = new TextLocation[] {statementSelection.Start, swapSibling.StartLocation}.Min();
if (direction == MoveStatementDirection.Up) if (direction == MoveStatementDirection.Up)
editor.Caret.Position = upperLocation; editor.Caret.Location = upperLocation;
else { else {
// look where current statement ended up because it is hard to calculate it correctly // look where current statement ended up because it is hard to calculate it correctly
int currentMovedOffset = editor.Document.Text.IndexOf(currentNodeText, editor.Document.PositionToOffset(upperLocation)); int currentMovedOffset = editor.Document.Text.IndexOf(currentNodeText, editor.Document.GetOffset(upperLocation));
editor.Caret.Offset = currentMovedOffset; editor.Caret.Offset = currentMovedOffset;
} }
} }
Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList<ISpecial> commentsBlankLines) Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList<AstNode> commentsBlankLines)
{ {
var extendedToComments = ExtendSelectionToComments(document, selection, commentsBlankLines); var extendedToComments = ExtendSelectionToComments(document, selection, commentsBlankLines);
if (extendedToComments != null) if (extendedToComments != null)
@ -188,13 +182,13 @@ namespace CSharpBinding
public void ExtendSelection() public void ExtendSelection()
{ {
INode selectedNode = null; AstNode selectedNode = null;
IList<ISpecial> commentsBlankLines; IList<AstNode> commentsBlankLines;
var parsedCU = ParseDocument(editor, out commentsBlankLines); var parsedCU = ParseDocument(editor, out commentsBlankLines);
if (parsedCU == null) return; if (parsedCU == null) return;
ISegment oldSelection = new TextSegment { StartOffset = editor.SelectionStart, Length = editor.SelectionLength }; ISegment oldSelection = new TextSegment { StartOffset = editor.SelectionStart, Length = editor.SelectionLength };
Selection extendedSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out selectedNode, new Type[] { typeof(INode) }); // any node type Selection extendedSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out selectedNode, new Type[] { typeof(AstNode) }); // any node type
SelectText(extendedSelection, editor); SelectText(extendedSelection, editor);
@ -216,14 +210,14 @@ namespace CSharpBinding
} }
// could work to extend selection to set of adjacent statements separated by blank lines // could work to extend selection to set of adjacent statements separated by blank lines
Selection ExtendSelection(ITextEditor editor, SyntaxTree parsedCU, IList<ISpecial> commentsBlankLines, out INode selectedResultNode, Type[] interestingNodeTypes) Selection ExtendSelection(ITextEditor editor, SyntaxTree parsedCU, IList<AstNode> commentsBlankLines, out AstNode selectedResultNode, Type[] interestingNodeTypes)
{ {
selectedResultNode = null; selectedResultNode = null;
var selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart); var selectionStart = editor.Document.GetLocation(editor.SelectionStart);
var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength); var selectionEnd = editor.Document.GetLocation(editor.SelectionStart + editor.SelectionLength);
Ast.INode currentNode = parsedCU.Children.Select(n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).FirstOrDefault(n => n != null); AstNode currentNode = parsedCU.GetNodeContaining(selectionStart, selectionEnd);
if (currentNode == null) return null; if (currentNode == null) return null;
if (!IsNodeTypeInteresting(currentNode, interestingNodeTypes)) { if (!IsNodeTypeInteresting(currentNode, interestingNodeTypes)) {
// ignore uninteresting nodes in the AST // ignore uninteresting nodes in the AST
@ -263,7 +257,7 @@ namespace CSharpBinding
// 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
var blankLines = commentsBlankLines.Where(s => s is BlankLine).Cast<BlankLine>().ToList(); // var blankLines = commentsBlankLines.Where(s => s is BlankLine).Cast<BlankLine>().ToList();
//if (SelectionContainsBlankLines(extendedSelectionStart, extendedLocationEnd, blankLines)) { //if (SelectionContainsBlankLines(extendedSelectionStart, extendedLocationEnd, blankLines)) {
if (false) { // blank line separators - implement later if (false) { // blank line separators - implement later
@ -281,7 +275,7 @@ namespace CSharpBinding
return new Selection { Start = selectionStart, End = selectionEnd }; return new Selection { Start = selectionStart, End = selectionEnd };
} }
Selection ExtendSelectionToComments(IDocument document, Selection selection, IList<ISpecial> commentsBlankLines) Selection ExtendSelectionToComments(IDocument document, Selection selection, IList<AstNode> commentsBlankLines)
{ {
if (selection == null) if (selection == null)
throw new ArgumentNullException("selection"); throw new ArgumentNullException("selection");
@ -291,7 +285,7 @@ namespace CSharpBinding
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
Selection ExtendSelectionToComments(IDocument document, Location selectionStart, Location selectionEnd, IList<ISpecial> commentsBlankLines) Selection ExtendSelectionToComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IList<AstNode> commentsBlankLines)
{ {
var comments = commentsBlankLines.Where(s => s is Comment).Cast<Comment>(); var comments = commentsBlankLines.Where(s => s is Comment).Cast<Comment>();
// add "var i = 5; // comments" comments // add "var i = 5; // comments" comments
@ -307,38 +301,37 @@ namespace CSharpBinding
/// <summary> /// <summary>
/// If there is a comment block behind selection on the same line ("var i = 5; // comment"), add it to selection. /// If there is a comment block behind selection on the same line ("var i = 5; // comment"), add it to selection.
/// </summary> /// </summary>
Selection ExtendSelectionToEndOfLineComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable<Comment> commentsBlankLines) Selection ExtendSelectionToEndOfLineComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IEnumerable<AstNode> commentsBlankLines)
{ {
var lineComment = commentsBlankLines.FirstOrDefault(c => c.StartPosition.Line == selectionEnd.Line && c.StartPosition >= selectionEnd); var lineComment = commentsBlankLines.FirstOrDefault(c => c.StartLocation.Line == selectionEnd.Line && c.StartLocation >= selectionEnd);
if (lineComment == null) { if (lineComment == null) {
return null; return null;
} }
bool isWholeLineSelected = IsWhitespaceBetween(document, new Location(1, selectionStart.Line), selectionStart); bool isWholeLineSelected = IsWhitespaceBetween(document, new TextLocation(selectionStart.Line, 1), selectionStart);
if (!isWholeLineSelected) { if (!isWholeLineSelected) {
// whole line must be selected before we add the comment // whole line must be selected before we add the comment
return null; return null;
} }
// fix the end of comment set to next line incorrectly by the parser int endPos = document.GetOffset(lineComment.EndLocation);
int fixEndPos = document.PositionToOffset(lineComment.EndPosition) - 1; return new Selection { Start = selectionStart, End = document.GetLocation(endPos) };
return new Selection { Start = selectionStart, End = document.OffsetToPosition(fixEndPos) };
} }
/// <summary> /// <summary>
/// If there is a comment block immediately before selection, add it to selection. /// If there is a comment block immediately before selection, add it to selection.
/// </summary> /// </summary>
Selection ExtendSelectionToSeparateComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable<Comment> commentsBlankLines) Selection ExtendSelectionToSeparateComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IEnumerable<AstNode> commentsBlankLines)
{ {
var comments = commentsBlankLines.Where(c => c.CommentStartsLine).ToList(); var comments = commentsBlankLines.Where(c => (c is Comment) && ((Comment) c).StartsLine).ToList();
int commentIndex = comments.FindIndex(c => c.EndPosition <= selectionStart && IsWhitespaceBetween(document, c.EndPosition, selectionStart)); int commentIndex = comments.FindIndex(c => c.EndLocation <= selectionStart && IsWhitespaceBetween(document, c.EndLocation, selectionStart));
if (commentIndex < 0) { if (commentIndex < 0) {
return null; return null;
} }
var extendedSelection = new Selection { Start = selectionStart, End = selectionEnd }; var extendedSelection = new Selection { Start = selectionStart, End = selectionEnd };
// start at the selection and keep adding comments upwards as long as they are separated only by whitespace // start at the selection and keep adding comments upwards as long as they are separated only by whitespace
while (commentIndex >= 0 && IsWhitespaceBetween(document, comments[commentIndex].EndPosition, extendedSelection.Start)) { while (commentIndex >= 0 && IsWhitespaceBetween(document, comments[commentIndex].EndLocation, extendedSelection.Start)) {
var comment = comments[commentIndex]; var comment = comments[commentIndex];
// Include the "//, /*, ///" since they are not included by the parser // Include the "//, /*, ///" since they are not included by the parser
extendedSelection.Start = ExtendLeft(comment.StartPosition, document, "///", "/*", "//") ; extendedSelection.Start = ExtendLeft(comment.StartLocation, document, "///", "/*", "//") ;
commentIndex--; commentIndex--;
} }
return extendedSelection; return extendedSelection;
@ -347,7 +340,7 @@ namespace CSharpBinding
/// <summary> /// <summary>
/// Searches for parent of interesting type. Skips uninteresting parents. /// Searches for parent of interesting type. Skips uninteresting parents.
/// </summary> /// </summary>
INode GetInterestingParent(INode node, Type[] interestingNodeTypes) AstNode GetInterestingParent(AstNode node, Type[] interestingNodeTypes)
{ {
var parent = node.Parent; var parent = node.Parent;
while(parent != null) { while(parent != null) {
@ -364,15 +357,15 @@ namespace CSharpBinding
return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType)); return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType));
} }
bool SelectionContainsBlankLines(Location selectionStart, Location selectionEnd, List<BlankLine> blankLines) bool SelectionContainsBlankLines(TextLocation selectionStart, TextLocation selectionEnd, List<AstNode> blankLines)
{ {
return blankLines.Exists(b => b.StartPosition >= selectionStart && b.EndPosition <= selectionEnd); return blankLines.Exists(b => b.StartLocation >= selectionStart && b.EndLocation <= selectionEnd);
} }
bool IsWhitespaceBetween(IDocument document, Location startPos, Location endPos) bool IsWhitespaceBetween(IDocument document, TextLocation startPos, TextLocation endPos)
{ {
int startOffset = document.PositionToOffset(startPos); int startOffset = document.GetOffset(startPos);
int endOffset = document.PositionToOffset(endPos); int endOffset = document.GetOffset(endPos);
if (startOffset > endOffset) { if (startOffset > endOffset) {
throw new ArgumentException("Invalid range for (startPos, endPos)"); throw new ArgumentException("Invalid range for (startPos, endPos)");
} }
@ -383,14 +376,14 @@ namespace CSharpBinding
/// If the text at startPos is preceded by any of the prefixes, moves the start backwards to include one prefix /// If the text at startPos is preceded by any of the prefixes, moves the start backwards to include one prefix
/// (the rightmost one). /// (the rightmost one).
/// </summary> /// </summary>
Location ExtendLeft(Location startPos, IDocument document, params String[] prefixes) TextLocation ExtendLeft(TextLocation startPos, IDocument document, params String[] prefixes)
{ {
int startOffset = document.PositionToOffset(startPos); int startOffset = document.GetOffset(startPos);
foreach (string prefix in prefixes) { foreach (string prefix in prefixes) {
if (startOffset < prefix.Length) continue; if (startOffset < prefix.Length) continue;
string realPrefix = document.GetText(startOffset - prefix.Length, prefix.Length); string realPrefix = document.GetText(startOffset - prefix.Length, prefix.Length);
if (realPrefix == prefix) { if (realPrefix == prefix) {
return document.OffsetToPosition(startOffset - prefix.Length); return document.GetLocation(startOffset - prefix.Length);
} }
} }
// no prefixes -> do not extend // no prefixes -> do not extend
@ -398,42 +391,34 @@ namespace CSharpBinding
} }
// could depend just on IDocument // could depend just on IDocument
SyntaxTree ParseDocument(ITextEditor editor, out IList<ISpecial> parsedSpecials) SyntaxTree ParseDocument(ITextEditor editor, out IList<AstNode> parsedSpecials)
{ {
parsedSpecials = null; parsedSpecials = null;
var editorLang = EditorContext.GetEditorLanguage(editor); CompilerSettings compilerSettings = new CompilerSettings();
if (editorLang == null) return null; var parser = new CSharpParser();
var parser = ParserFactory.CreateParser(editorLang.Value, editor.Document.CreateReader());
if (parser == null) return null; if (parser == null) return null;
parser.ParseMethodBodies = true; var syntaxTree = parser.Parse(editor.Document.CreateReader());
parser.Lexer.SkipAllComments = false; if (syntaxTree == null) return null;
parser.Parse(); parsedSpecials = new List<AstNode>(syntaxTree.Descendants.OfType<Comment>());
var parsedCU = parser.SyntaxTree; return syntaxTree;
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;
} }
/// <summary> /// <summary>
/// Swaps 2 ranges of text in a document. /// Swaps 2 ranges of text in a document.
/// </summary> /// </summary>
void SwapText(IDocument document, Location start1, Location end1, Location start2, Location end2) void SwapText(IDocument document, TextLocation start1, TextLocation end1, TextLocation start2, TextLocation end2)
{ {
if (start1 > start2) { if (start1 > start2) {
Location sw; TextLocation sw;
sw = start1; start1 = start2; start2 = sw; sw = start1; start1 = start2; start2 = sw;
sw = end1; end1 = end2; end2 = sw; sw = end1; end1 = end2; end2 = sw;
} }
if (end1 >= start2) if (end1 >= start2)
throw new InvalidOperationException("Cannot swap overlaping segments"); throw new InvalidOperationException("Cannot swap overlaping segments");
int offset1 = document.PositionToOffset(start1); int offset1 = document.GetOffset(start1);
int len1 = document.PositionToOffset(end1) - offset1; int len1 = document.GetOffset(end1) - offset1;
int offset2 = document.PositionToOffset(start2); int offset2 = document.GetOffset(start2);
int len2 = document.PositionToOffset(end2) - offset2; int len2 = document.GetOffset(end2) - offset2;
string text1 = document.GetText(offset1, len1); string text1 = document.GetText(offset1, len1);
string text2 = document.GetText(offset2, len2); string text2 = document.GetText(offset2, len2);
@ -450,8 +435,8 @@ namespace CSharpBinding
return; return;
int startOffset, endOffset; int startOffset, endOffset;
try { try {
startOffset = editor.Document.PositionToOffset(selection.Start); startOffset = editor.Document.GetOffset(selection.Start);
endOffset = editor.Document.PositionToOffset(selection.End); endOffset = editor.Document.GetOffset(selection.End);
} catch (ArgumentOutOfRangeException) { } catch (ArgumentOutOfRangeException) {
return; return;
} }
@ -459,4 +444,3 @@ namespace CSharpBinding
} }
} }
} }
*/
Loading…
Cancel
Save