diff --git a/Codist/Helpers/TextEditorHelper.cs b/Codist/Helpers/TextEditorHelper.cs index 8eb9de05..a3df69a7 100644 --- a/Codist/Helpers/TextEditorHelper.cs +++ b/Codist/Helpers/TextEditorHelper.cs @@ -79,10 +79,49 @@ public static StyleBase GetStyle(string classificationType) { } } - public static void ExpandSelectionToLine(this IWpfTextView view) { + /// + /// Begins an edit operation to the . + /// + /// The type of the view. + /// The to be edited. + /// The edit operation. + /// Returns a new if returns , otherwise, returns . + public static ITextSnapshot Edit(this TView view, Action action) + where TView : ITextView { + using (var edit = view.TextSnapshot.TextBuffer.CreateEdit()) { + action(view, edit); + if (edit.HasEffectiveChanges) { + edit.Apply(); + return edit.Snapshot; + } + return null; + } + } + /// + /// Performs edit operation to each selected spans in the . + /// + /// The type of the view. + /// The to be edited. + /// The edit operation against each selected span. + /// Returns a new if returns , otherwise, returns . + public static ITextSnapshot EditSelection(this TView view, Action action) + where TView : ITextView { + using (var edit = view.TextSnapshot.TextBuffer.CreateEdit()) { + foreach (var item in view.Selection.SelectedSpans) { + action(view, edit, item); + } + if (edit.HasEffectiveChanges) { + edit.Apply(); + return edit.Snapshot; + } + return null; + } + } + + public static void ExpandSelectionToLine(this ITextView view) { view.ExpandSelectionToLine(true); } - public static void ExpandSelectionToLine(this IWpfTextView view, bool includeLineBreak) { + public static void ExpandSelectionToLine(this ITextView view, bool includeLineBreak) { var start = view.TextSnapshot.GetLineFromPosition(view.Selection.Start.Position).Start; var end = view.Selection.End.Position; var endLine = view.TextSnapshot.GetLineFromPosition(end); @@ -92,6 +131,12 @@ public static void ExpandSelectionToLine(this IWpfTextView view, bool includeLin } view.Selection.Select(new SnapshotSpan(start, end), false); } + public static string GetFirstSelectionText(this ITextSelection selection) { + return selection.IsEmpty ? String.Empty : selection.SelectedSpans[0].GetText(); + } + public static string GetFirstSelectionText(this ITextView view) { + return view.TryGetFirstSelectionSpan(out var span) ? span.GetText() : String.Empty; + } public static bool TryGetFirstSelectionSpan(this ITextView view, out SnapshotSpan span) { if (view.Selection.IsEmpty || view.Selection.SelectedSpans.Count < 1) { span = new SnapshotSpan(); @@ -139,15 +184,15 @@ public static TokenType GetSelectedTokenType(this ITextView view) { return t; } - public static SnapshotPoint GetCaretPosition(this IWpfTextView textView) { + public static SnapshotPoint GetCaretPosition(this ITextView textView) { return textView.Caret.Position.BufferPosition; } - public static bool IsCaretInReadOnlyRegion(this IWpfTextView textView) { + public static bool IsCaretInReadOnlyRegion(this ITextView textView) { return textView.TextBuffer.IsReadOnly(textView.Caret.Position.BufferPosition); } - public static bool IsMultilineSelected(this IWpfTextView textView) { + public static bool IsMultilineSelected(this ITextView textView) { var s = textView.Selection; if (s.IsEmpty || s.SelectedSpans.Count < 1) { return false; @@ -155,7 +200,7 @@ public static bool IsMultilineSelected(this IWpfTextView textView) { return textView.GetTextViewLineContainingBufferPosition(s.Start.Position) != textView.GetTextViewLineContainingBufferPosition(s.End.Position); } - public static void SelectNode(this IWpfTextView view, Microsoft.CodeAnalysis.SyntaxNode node, bool includeTrivia) { + public static void SelectNode(this ITextView view, Microsoft.CodeAnalysis.SyntaxNode node, bool includeTrivia) { var span = includeTrivia ? node.FullSpan : node.Span; if (view.TextSnapshot.Length > span.End) { SnapshotSpan ss; @@ -178,7 +223,7 @@ public static void SelectNode(this IWpfTextView view, Microsoft.CodeAnalysis.Syn } } - public static void SelectSpan(this IWpfTextView view, SnapshotSpan span) { + public static void SelectSpan(this ITextView view, SnapshotSpan span) { view.ViewScroller.EnsureSpanVisible(span, EnsureSpanVisibleOptions.ShowStart); view.Selection.Select(span, false); view.Caret.MoveTo(span.End); diff --git a/Codist/SmartBars/CSharpSmartBar.cs b/Codist/SmartBars/CSharpSmartBar.cs index ab316d3f..a0b07fa2 100644 --- a/Codist/SmartBars/CSharpSmartBar.cs +++ b/Codist/SmartBars/CSharpSmartBar.cs @@ -166,7 +166,7 @@ void AddContextualCommands(CancellationToken cancellationToken) { } } if (isDesignMode == false) { - AddCommands(MyToolBar, KnownImageIds.BreakpointEnabled, "Debugger...\nLeft click: Toggle breakpoint\nRight click: Debugger menu...", ctx => TextEditorHelper.ExecuteEditorCommand("Debug.ToggleBreakpoint"), GetDebugCommands); + AddCommands(MyToolBar, KnownImageIds.BreakpointEnabled, "Debugger...\nLeft click: Toggle breakpoint\nRight click: Debugger menu...", ctx => TextEditorHelper.ExecuteEditorCommand("Debug.ToggleBreakpoint"), ctx => DebugCommands); } AddCommands(MyToolBar, KnownImageIds.SelectFrame, "Expand selection...\nRight click: Duplicate...\nCtrl click item: Copy\nShift click item: Exclude whitespaces and comments", null, GetExpandSelectionCommands); } @@ -178,30 +178,28 @@ void AddXmlDocCommands() { AddCommand(MyToolBar, KnownImageIds.GoToNext, "Tag XML Doc with or ", ctx => { // updates the semantic model before executing the command, // for it could be modified by external editor commands or duplicated document windows - if (UpdateSemanticModel()) { - using (var edit = ctx.View.TextSnapshot.TextBuffer.CreateEdit()) { - foreach (var item in View.Selection.SelectedSpans) { - var t = item.GetText(); - var d = _Context.GetNode(item.Start, false, false).GetAncestorOrSelfDeclaration(); - if (d != null) { - var mp = (d as BaseMethodDeclarationSyntax).FindParameter(t); - if (mp != null) { - edit.Replace(item, ""); - continue; - } - var tp = d.FindTypeParameter(t); - if (tp != null) { - edit.Replace(item, ""); - continue; - } + if (UpdateSemanticModel() == false) { + return; + } + ctx.View.Edit((view, edit) => { + foreach (var item in view.Selection.SelectedSpans) { + var t = item.GetText(); + var d = _Context.GetNode(item.Start, false, false).GetAncestorOrSelfDeclaration(); + if (d != null) { + var mp = (d as BaseMethodDeclarationSyntax).FindParameter(t); + if (mp != null) { + edit.Replace(item, ""); + continue; + } + var tp = d.FindTypeParameter(t); + if (tp != null) { + edit.Replace(item, ""); + continue; } - edit.Replace(item, (SyntaxFacts.GetKeywordKind(t) != SyntaxKind.None ? ""); - } - if (edit.HasEffectiveChanges) { - edit.Apply(); } + edit.Replace(item, (SyntaxFacts.GetKeywordKind(t) != SyntaxKind.None ? ""); } - } + }); }); AddCommand(MyToolBar, KnownImageIds.ParagraphHardReturn, "Tag XML Doc with ", ctx => { SurroundWith(ctx, "", "", false); @@ -461,14 +459,6 @@ void FindSymbolWithName(CommandContext ctx, MenuItem menuItem, ISymbol source) { SortAndGroupSymbolByClass(menuItem, new List(result)); } - CommandItem[] GetDebugCommands(CommandContext ctx) { - return new CommandItem[] { - new CommandItem(KnownImageIds.Watch, "Add Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddWatch")), - new CommandItem(KnownImageIds.Watch, "Add Parallel Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddParallelWatch")), - new CommandItem(KnownImageIds.DeleteBreakpoint, "Delete All Breakpoints", c => TextEditorHelper.ExecuteEditorCommand("Debug.DeleteAllBreakpoints")) - }; - } - List GetExpandSelectionCommands(CommandContext ctx) { var r = new List(); var duplicate = ctx.RightClick; diff --git a/Codist/SmartBars/CppSmartBar.cs b/Codist/SmartBars/CppSmartBar.cs index ab6a43e2..70d3779e 100644 --- a/Codist/SmartBars/CppSmartBar.cs +++ b/Codist/SmartBars/CppSmartBar.cs @@ -38,18 +38,10 @@ protected override void AddCommands(CancellationToken cancellationToken) { AddEditorCommand(MyToolBar, KnownImageIds.UncommentCode, "Edit.UncommentSelection", "Uncomment selection"); } else if (mode != DebuggerStatus.Design) { - AddCommands(MyToolBar, KnownImageIds.BreakpointEnabled, "Debugger...\nLeft click: Toggle breakpoint\nRight click: Debugger menu...", ctx => TextEditorHelper.ExecuteEditorCommand("Debug.ToggleBreakpoint"), GetDebugCommands); + AddCommands(MyToolBar, KnownImageIds.BreakpointEnabled, "Debugger...\nLeft click: Toggle breakpoint\nRight click: Debugger menu...", ctx => TextEditorHelper.ExecuteEditorCommand("Debug.ToggleBreakpoint"), ctx => DebugCommands); } } - CommandItem[] GetDebugCommands(CommandContext ctx) { - return new CommandItem[] { - new CommandItem(KnownImageIds.Watch, "Add Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddWatch")), - new CommandItem(KnownImageIds.Watch, "Add Parallel Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddParallelWatch")), - new CommandItem(KnownImageIds.DeleteBreakpoint, "Delete All Breakpoints", c => TextEditorHelper.ExecuteEditorCommand("Debug.DeleteAllBreakpoints")) - }; - } - string GetCurrentWord(ITextView view) { return _TextStructureNavigator.GetExtentOfWord(view.Selection.Start.Position).Span.GetText(); } diff --git a/Codist/SmartBars/SmartBar.CommonEdit.cs b/Codist/SmartBars/SmartBar.CommonEdit.cs index 68484191..42b8eac0 100644 --- a/Codist/SmartBars/SmartBar.CommonEdit.cs +++ b/Codist/SmartBars/SmartBar.CommonEdit.cs @@ -18,23 +18,22 @@ partial class SmartBar static readonly CommandItem[] __CaseCommands = GetCaseCommands(); static readonly CommandItem[] __SurroundingCommands = GetSurroundingCommands(); static readonly CommandItem[] __FormatCommands = GetFormatCommands(); + static readonly CommandItem[] __DebugCommands = GetDebugCommands(); - static void ExecuteAndFind(CommandContext ctx, string command) { - ThreadHelper.ThrowIfNotOnUIThread(); + protected static IEnumerable DebugCommands => __DebugCommands; + + static void ExecuteAndFind(CommandContext ctx, string command, string text) { if (ctx.RightClick) { ctx.View.ExpandSelectionToLine(false); } - string t = null; - if (Keyboard.Modifiers == ModifierKeys.Control && ctx.View.Selection.IsEmpty == false) { - t = ctx.View.TextSnapshot.GetText(ctx.View.Selection.SelectedSpans[0]); - } ctx.KeepToolBar(false); TextEditorHelper.ExecuteEditorCommand(command); - FindNext(ctx, t); + if (Keyboard.Modifiers == ModifierKeys.Control) { + FindNext(ctx, text); + } } protected static void FindNext(CommandContext ctx, string t) { - ThreadHelper.ThrowIfNotOnUIThread(); if (t != null) { var r = ctx.TextSearchService.Find(ctx.View.Selection.StreamSelectionSpan.End.Position, t, FindOptions.MatchCase); if (r.HasValue) { @@ -47,31 +46,30 @@ protected static void FindNext(CommandContext ctx, string t) { } protected SnapshotSpan Replace(CommandContext ctx, Func replaceHandler, bool selectModified) { + ctx.KeepToolBar(false); var firstModified = new SnapshotSpan(); int length = 0; - string t = null; - ctx.KeepToolBar(false); - using (var edit = ctx.View.TextSnapshot.TextBuffer.CreateEdit()) { - foreach (var item in View.Selection.SelectedSpans) { - t = item.GetText(); - var replacement = replaceHandler(t); - if (replacement != null - && edit.Replace(item, replacement) - && firstModified.Snapshot == null) { - firstModified = item; - length = replacement.Length; - } + string t = ctx.View.GetFirstSelectionText(); + if (t.Length == 0) { + return firstModified; + } + var edited = ctx.View.EditSelection((view, edit, item) => { + var replacement = replaceHandler(item.GetText()); + if (replacement != null + && edit.Replace(item, replacement) + && firstModified.Snapshot == null) { + firstModified = item; + length = replacement.Length; } - if (edit.HasEffectiveChanges) { - var snapsnot = edit.Apply(); - firstModified = new SnapshotSpan(snapsnot, firstModified.Start, length); - if (t != null - && Keyboard.Modifiers == ModifierKeys.Control) { - FindNext(ctx, t); - } - else if (selectModified) { - View.SelectSpan(firstModified); - } + }); + if (edited != null) { + firstModified = new SnapshotSpan(edited, firstModified.Start, length); + if (t != null + && Keyboard.Modifiers == ModifierKeys.Control) { + FindNext(ctx, t); + } + else if (selectModified) { + ctx.View.SelectSpan(firstModified); } } return firstModified; @@ -137,7 +135,10 @@ void AddCutCommand() { void AddDeleteCommand() { AddCommand(ToolBar, KnownImageIds.Cancel, "Delete selected text\nRight click: Delete line\nCtrl click: Delete and select next", ctx => { var s = View.Selection; - if (s.Mode == TextSelectionMode.Stream && ctx.RightClick == false && Keyboard.Modifiers != ModifierKeys.Control && s.IsEmpty == false) { + var t = s.GetFirstSelectionText(); + if (s.Mode == TextSelectionMode.Stream + && ctx.RightClick == false + && s.IsEmpty == false) { var end = s.End.Position; // remove a trailing space if (end < View.TextSnapshot.Length - 1) { @@ -147,7 +148,7 @@ void AddDeleteCommand() { } } } - ExecuteAndFind(ctx, "Edit.Delete"); + ExecuteAndFind(ctx, "Edit.Delete", t); }); } @@ -164,10 +165,8 @@ void AddDuplicateCommand() { void AddFindAndReplaceCommands() { AddCommands(ToolBar, KnownImageIds.FindNext, "Find next selected text\nCtrl click: Find match case\nRight click: Find and replace...", ctx => { ThreadHelper.ThrowIfNotOnUIThread(); - string t = ctx.View.Selection.IsEmpty == false - ? ctx.View.TextSnapshot.GetText(ctx.View.Selection.SelectedSpans[0]) - : null; - if (t == null) { + string t = ctx.View.GetFirstSelectionText(); + if (t.Length == 0) { return; } ctx.KeepToolBar(false); @@ -184,7 +183,7 @@ void AddFindAndReplaceCommands() { void AddPasteCommand() { if (Clipboard.ContainsText()) { - AddCommand(ToolBar, KnownImageIds.Paste, "Paste text from clipboard\nRight click: Paste over line\nCtrl click: Paste and select next", ctx => ExecuteAndFind(ctx, "Edit.Paste")); + AddCommand(ToolBar, KnownImageIds.Paste, "Paste text from clipboard\nRight click: Paste over line\nCtrl click: Paste and select next", ctx => ExecuteAndFind(ctx, "Edit.Paste", ctx.View.GetFirstSelectionText())); } } @@ -195,16 +194,17 @@ void AddSpecialFormatCommand() { break; case TokenType.Digit: AddCommand(ToolBar, KnownImageIds.Counter, "Increment number", ctx => { - var span = ctx.View.Selection.SelectedSpans[0]; - var t = span.GetText(); - long l; - if (long.TryParse(t, out l)) { - using (var ed = ctx.View.TextBuffer.CreateEdit()) { - t = (++l).ToString(System.Globalization.CultureInfo.InvariantCulture); - if (ed.Replace(span.Span, t)) { - ed.Apply(); - ctx.View.Selection.Select(new SnapshotSpan(ctx.View.TextSnapshot, span.Start, t.Length), false); - ctx.KeepToolBar(false); + if (ctx.View.TryGetFirstSelectionSpan(out var span)) { + var t = span.GetText(); + long l; + if (long.TryParse(t, out l)) { + using (var ed = ctx.View.TextBuffer.CreateEdit()) { + t = (++l).ToString(System.Globalization.CultureInfo.InvariantCulture); + if (ed.Replace(span.Span, t)) { + ed.Apply(); + ctx.View.Selection.Select(new SnapshotSpan(ctx.View.TextSnapshot, span.Start, t.Length), false); + ctx.KeepToolBar(false); + } } } } @@ -213,13 +213,14 @@ void AddSpecialFormatCommand() { case TokenType.Guid: case TokenType.GuidPlaceHolder: AddCommand(ToolBar, KnownImageIds.NewNamedSet, "New GUID\nHint: To create a new GUID, type 'guid' (without quotes) and select it", ctx => { - var span = ctx.View.Selection.SelectedSpans[0]; - using (var ed = ctx.View.TextBuffer.CreateEdit()) { - var t = Guid.NewGuid().ToString(span.Length == 36 || span.Length == 4 ? "D" : span.GetText()[0] == '(' ? "P" : "B").ToUpperInvariant(); - if (ed.Replace(span, t)) { - ed.Apply(); - ctx.View.Selection.Select(new SnapshotSpan(ctx.View.TextSnapshot, span.Start, t.Length), false); - ctx.KeepToolBar(false); + if (ctx.View.TryGetFirstSelectionSpan(out var span)) { + using (var ed = ctx.View.TextBuffer.CreateEdit()) { + var t = Guid.NewGuid().ToString(span.Length == 36 || span.Length == 4 ? "D" : span.GetText()[0] == '(' ? "P" : "B").ToUpperInvariant(); + if (ed.Replace(span, t)) { + ed.Apply(); + ctx.View.Selection.Select(new SnapshotSpan(ctx.View.TextSnapshot, span.Start, t.Length), false); + ctx.KeepToolBar(false); + } } } }); @@ -238,9 +239,10 @@ List GetFormatItems(CommandContext arg) { r.AddRange(__SurroundingCommands); r.AddRange(__FormatCommands); if (View.IsMultilineSelected()) { - r.Add(new CommandItem(KnownImageIds.Join, "Join lines", _ => { - var span = View.Selection.SelectedSpans[0]; - View.TextBuffer.Replace(span, System.Text.RegularExpressions.Regex.Replace(span.GetText(), @"[ \t]*\r?\n[ \t]*", " ")); + r.Add(new CommandItem(KnownImageIds.Join, "Join lines", ctx => { + if (ctx.View.TryGetFirstSelectionSpan(out var span)) { + ctx.View.TextBuffer.Replace(span, System.Text.RegularExpressions.Regex.Replace(span.GetText(), @"[ \t]*\r?\n[ \t]*", " ")); + } })); } var t = View.GetTextViewLineContainingBufferPosition(selection.Start.Position).Extent.GetText(); @@ -259,7 +261,6 @@ List GetFormatItems(CommandContext arg) { return r; } - static CommandItem[] GetCaseCommands() { return new CommandItem[] { new CommandItem(KnownImageIds.Font, "Capitalize", ctx => { @@ -276,6 +277,14 @@ static CommandItem[] GetCaseCommands() { }), }; } + static CommandItem[] GetDebugCommands() { + return new CommandItem[] { + new CommandItem(KnownImageIds.Watch, "Add Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddWatch")), + new CommandItem(KnownImageIds.Watch, "Add Parallel Watch", c => TextEditorHelper.ExecuteEditorCommand("Debug.AddParallelWatch")), + new CommandItem(KnownImageIds.DeleteBreakpoint, "Delete All Breakpoints", c => TextEditorHelper.ExecuteEditorCommand("Debug.DeleteAllBreakpoints")) + }; + } + static CommandItem[] GetFormatCommands() { return new CommandItem[] { new CommandItem(KnownImageIds.FormatSelection, "Format Selection", _ => TextEditorHelper.ExecuteEditorCommand("Edit.FormatSelection")), @@ -296,7 +305,9 @@ static CommandItem[] GetSurroundingCommands() { TextEditorHelper.ExecuteEditorCommand("Edit.SurroundWith"); }), new CommandItem(KnownImageIds.MaskedTextBox, "Toggle Parentheses", ctx => { - var span = ctx.View.Selection.SelectedSpans[0]; + if (ctx.View.TryGetFirstSelectionSpan(out var span) == false) { + return; + } using (var ed = ctx.View.TextBuffer.CreateEdit()) { var t = span.GetText(); if (t.Length > 1 diff --git a/Codist/SmartBars/SmartBar.cs b/Codist/SmartBars/SmartBar.cs index da4eee79..3876bfc0 100644 --- a/Codist/SmartBars/SmartBar.cs +++ b/Codist/SmartBars/SmartBar.cs @@ -285,16 +285,17 @@ void KeepToolbar() { void SetToolBarPosition() { // keep tool bar position when the selection is restored and the tool bar reappears after executing command - if (DateTime.Now > _LastExecute.AddSeconds(1)) { - var pos = Mouse.GetPosition(View.VisualElement); - var rs = _ToolBarTray.RenderSize; - var x = pos.X - 35; - var y = pos.Y - rs.Height - 10; - Canvas.SetLeft(_ToolBarTray, x < View.ViewportLeft ? View.ViewportLeft - : x + rs.Width < View.ViewportRight ? x - : View.ViewportRight - rs.Width); - Canvas.SetTop(_ToolBarTray, (y < 0 || x < View.ViewportLeft && View.Selection.IsReversed == false ? y + rs.Height + 30 : y) + View.ViewportTop); + if (DateTime.Now < _LastExecute.AddSeconds(1)) { + return; } + var pos = Mouse.GetPosition(View.VisualElement); + var rs = _ToolBarTray.RenderSize; + var x = pos.X - 35; + var y = pos.Y - rs.Height - 10; + Canvas.SetLeft(_ToolBarTray, x < View.ViewportLeft ? View.ViewportLeft + : x + rs.Width < View.ViewportRight ? x + : View.ViewportRight - rs.Width); + Canvas.SetTop(_ToolBarTray, (y < 0 || x < View.ViewportLeft && View.Selection.IsReversed == false ? y + rs.Height + 30 : y) + View.ViewportTop); } #region Event handlers