diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 702822c2f6..db074cdfad 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -44,10 +44,16 @@ 3.0 + + 3.0 + + + 4.0 + @@ -58,6 +64,7 @@ + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs index 69597d0443..98153b951c 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpLanguageBinding.cs @@ -2,7 +2,9 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.ComponentModel.Design; using CSharpBinding.FormattingStrategy; +using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Editor; @@ -26,9 +28,16 @@ namespace CSharpBinding get { return new CSharpBracketSearcher(); } } + CodeManipulation codeManipulation; + public override void Attach(ITextEditor editor) { - //CSharpBackgroundCompiler.Init(); + codeManipulation = new CodeManipulation(editor); + } + + public override void Detach() + { + codeManipulation.Dispose(); } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs similarity index 70% rename from src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs rename to src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs index 335c89cee9..c26d91968b 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeManipulation.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs @@ -2,9 +2,12 @@ // 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 System.Windows.Input; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.Core; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.Ast; using ICSharpCode.SharpDevelop; @@ -12,35 +15,125 @@ using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Refactoring; using Ast = ICSharpCode.NRefactory.Ast; -namespace ICSharpCode.AvalonEdit.AddIn +namespace CSharpBinding { /// /// Description of ExtendSelection. /// - public class CodeManipulation + public class CodeManipulation : IDisposable { enum MoveStatementDirection { Up, Down }; + ITextEditor editor; + + public static readonly RoutedCommand ExtendSelectionCommand = new RoutedCommand( + "ExtendSelection", typeof(CodeManipulation), + new InputGestureCollection { new KeyGesture(Key.W, ModifierKeys.Control) } + ); + + public static readonly RoutedCommand ShrinkSelectionCommand = new RoutedCommand( + "ShrinkSelection", typeof(CodeManipulation), + new InputGestureCollection { new KeyGesture(Key.W, ModifierKeys.Control | ModifierKeys.Shift) } + ); + + public static readonly RoutedCommand MoveStatementUpCommand = new RoutedCommand( + "MoveStatementUp", typeof(CodeManipulation), + new InputGestureCollection { new KeyGesture(Key.Up, ModifierKeys.Alt) } + ); + + public static readonly RoutedCommand MoveStatementDownCommand = new RoutedCommand( + "MoveStatementDown", typeof(CodeManipulation), + new InputGestureCollection { new KeyGesture(Key.Down, ModifierKeys.Alt) } + ); + + IEnumerable bindings; + + public CodeManipulation(ITextEditor editor) + { + this.editor = editor; + this.editor.SelectionChanged += CodeManipulationSelectionChanged; + + TextArea area = (TextArea)editor.GetService(typeof(TextArea)); + + bindings = new List { + new CommandBinding(ExtendSelectionCommand, ExtendSelectionExecuted), + new CommandBinding(ShrinkSelectionCommand, ShrinkSelectionExecuted), + new CommandBinding(MoveStatementUpCommand, MoveStatementUpExecuted), + new CommandBinding(MoveStatementDownCommand, MoveStatementDownExecuted) + }; + + area.DefaultInputHandler.CommandBindings.AddRange(bindings); + } + + bool internalSelectionChange; + + void ExtendSelectionExecuted(object sender, ExecutedRoutedEventArgs e) + { + internalSelectionChange = true; + try { + ExtendSelection(); + } finally { + internalSelectionChange = false; + } + } + + void ShrinkSelectionExecuted(object sender, ExecutedRoutedEventArgs e) + { + internalSelectionChange = true; + try { + ShrinkSelection(); + } finally { + internalSelectionChange = false; + } + } + + void MoveStatementUpExecuted(object sender, ExecutedRoutedEventArgs e) + { + MoveStatementUp(); + } + + void MoveStatementDownExecuted(object sender, ExecutedRoutedEventArgs e) + { + MoveStatementDown(); + } + + readonly Stack previousSelections = new Stack(); + class Selection { public Location Start { get; set; } public Location End { get; set; } } - public static void MoveStatementUp(ITextEditor editor) + void CodeManipulationSelectionChanged(object sender, EventArgs e) + { + if (!internalSelectionChange) + previousSelections.Clear(); + } + + public void Dispose() + { + this.editor.SelectionChanged -= CodeManipulationSelectionChanged; + TextArea area = (TextArea)editor.GetService(typeof(TextArea)); + + foreach (var b in bindings) + area.DefaultInputHandler.CommandBindings.Remove(b); + } + + public void MoveStatementUp() { MoveStatement(editor, MoveStatementDirection.Up); editor.ClearSelection(); } - public static void MoveStatementDown(ITextEditor editor) + public void MoveStatementDown() { 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) + void MoveStatement(ITextEditor editor, MoveStatementDirection direction) { IList commentsBlankLines; var parsedCU = ParseDocument(editor, out commentsBlankLines); @@ -81,7 +174,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - static Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines) + Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines) { var extendedToComments = ExtendSelectionToComments(document, selection, commentsBlankLines); if (extendedToComments != null) @@ -89,20 +182,37 @@ namespace ICSharpCode.AvalonEdit.AddIn return selection; } - public static void ExtendSelection(ITextEditor editor) + public void ExtendSelection() { INode selectedNode = null; IList commentsBlankLines; var parsedCU = ParseDocument(editor, out commentsBlankLines); if (parsedCU == null) return; + 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 SelectText(extendedSelection, editor); + + if (previousSelections.Count == 0 || !(previousSelections.Peek().Offset == oldSelection.Offset && previousSelections.Peek().EndOffset == oldSelection.EndOffset)) { + previousSelections.Push(oldSelection); + LoggingService.Debug("pushed: " + oldSelection); + } else { + LoggingService.Debug("not accepted: " + oldSelection); + } + } + + public void ShrinkSelection() + { + if (previousSelections.Count < 1) + return; + var selection = previousSelections.Pop(); + editor.Select(selection.Offset, selection.Length); + LoggingService.Debug("popped: " + selection); } // could work to extend selection to set of adjacent statements separated by blank lines - static Selection ExtendSelection(ITextEditor editor, CompilationUnit parsedCU, IList commentsBlankLines, out INode selectedResultNode, Type[] interestingNodeTypes) + Selection ExtendSelection(ITextEditor editor, CompilationUnit parsedCU, IList commentsBlankLines, out INode selectedResultNode, Type[] interestingNodeTypes) { selectedResultNode = null; @@ -168,7 +278,7 @@ namespace ICSharpCode.AvalonEdit.AddIn return new Selection { Start = selectionStart, End = selectionEnd }; } - static Selection ExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines) + Selection ExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines) { if (selection == null) throw new ArgumentNullException("selection"); @@ -178,7 +288,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// /// If there is a comment block immediately before selection, or behind selection on the same line, add it to selection. /// - static Selection ExtendSelectionToComments(IDocument document, Location selectionStart, Location selectionEnd, IList commentsBlankLines) + Selection ExtendSelectionToComments(IDocument document, Location selectionStart, Location selectionEnd, IList commentsBlankLines) { var comments = commentsBlankLines.Where(s => s is Comment).Cast(); // add "var i = 5; // comments" comments @@ -194,7 +304,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// /// If there is a comment block behind selection on the same line ("var i = 5; // comment"), add it to selection. /// - static Selection ExtendSelectionToEndOfLineComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines) + Selection ExtendSelectionToEndOfLineComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines) { var lineComment = commentsBlankLines.Where(c => c.StartPosition.Line == selectionEnd.Line && c.StartPosition >= selectionEnd).FirstOrDefault(); if (lineComment == null) { @@ -213,7 +323,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// /// If there is a comment block immediately before selection, add it to selection. /// - static Selection ExtendSelectionToSeparateComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines) + Selection ExtendSelectionToSeparateComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines) { var comments = commentsBlankLines.Where(c => c.CommentStartsLine).ToList(); int commentIndex = comments.FindIndex(c => c.EndPosition <= selectionStart && IsWhitespaceBetween(document, c.EndPosition, selectionStart)); @@ -234,7 +344,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// /// Searches for parent of interesting type. Skips uninteresting parents. /// - static INode GetInterestingParent(INode node, Type[] interestingNodeTypes) + INode GetInterestingParent(INode node, Type[] interestingNodeTypes) { var parent = node.Parent; while(parent != null) { @@ -245,18 +355,18 @@ namespace ICSharpCode.AvalonEdit.AddIn return null; } - static bool IsNodeTypeInteresting(INode node, Type[] interestingNodeTypes) + bool IsNodeTypeInteresting(INode node, Type[] interestingNodeTypes) { Type nodeType = node.GetType(); return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType)); } - static bool SelectionContainsBlankLines(Location selectionStart, Location selectionEnd, List blankLines) + 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) + bool IsWhitespaceBetween(IDocument document, Location startPos, Location endPos) { int startOffset = document.PositionToOffset(startPos); int endOffset = document.PositionToOffset(endPos); @@ -270,7 +380,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// If the text at startPos is preceded by any of the prefixes, moves the start backwards to include one prefix /// (the rightmost one). /// - static Location ExtendLeft(Location startPos, IDocument document, params String[] prefixes) + Location ExtendLeft(Location startPos, IDocument document, params String[] prefixes) { int startOffset = document.PositionToOffset(startPos); foreach (string prefix in prefixes) { @@ -285,7 +395,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } // could depend just on IDocument - static CompilationUnit ParseDocument(ITextEditor editor, out IList parsedSpecials) + CompilationUnit ParseDocument(ITextEditor editor, out IList parsedSpecials) { parsedSpecials = null; var editorLang = EditorContext.GetEditorLanguage(editor); @@ -308,7 +418,7 @@ namespace ICSharpCode.AvalonEdit.AddIn /// /// Swaps 2 ranges of text in a document. /// - static void SwapText(IDocument document, Location start1, Location end1, Location start2, Location end2) + void SwapText(IDocument document, Location start1, Location end1, Location start2, Location end2) { if (start1 > start2) { Location sw; @@ -331,7 +441,7 @@ namespace ICSharpCode.AvalonEdit.AddIn } } - static void SelectText(Selection selection, ITextEditor editor) + void SelectText(Selection selection, ITextEditor editor) { if (selection == null) return; diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj index 68af13281c..1c6ef5f091 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj @@ -101,7 +101,6 @@ Code - HiddenDefinitionControl.xaml diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs index 52437a186e..403efc1ece 100755 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs @@ -490,25 +490,6 @@ namespace ICSharpCode.AvalonEdit.AddIn } #endregion - #region CTRL+W extend selection - protected override void OnKeyUp(KeyEventArgs e) - { - base.OnKeyUp(e); - if (e.Handled) return; - if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) { - CodeManipulation.ExtendSelection(this.Adapter); - } - if (e.SystemKey == Key.Up && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { - // Left Alt + Up (probably will have different shortcut) - CodeManipulation.MoveStatementUp(this.Adapter); - } - if (e.SystemKey == Key.Down && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) { - // Left Alt + Down (probably will have different shortcut) - CodeManipulation.MoveStatementDown(this.Adapter); - } - } - #endregion - public void JumpTo(int line, int column) { // closes Debugger popup on debugger step