From 3b2666f6a1bdee2dea83c6d2a4177a0eb796617b Mon Sep 17 00:00:00 2001 From: Justin Clareburt Date: Thu, 16 Feb 2023 21:45:50 +0100 Subject: [PATCH] New feature: DuplicateLinesUp (CopyLinesUp) (#92) Update to v4.2.0 - First implementation of DuplicateLinesUp (CopyLinesUp) Bug exists with multi-caret if selection ends at start or end of line. * Added fix to CopyLinesUp - only for single selection --- HotCommands/Commands/DuplicateSelection.cs | 46 +++++++++++++++---- HotCommands/Constants.cs | 3 +- HotCommands/Hot Commands Shortcuts.vssettings | 3 +- HotCommands/HotCommandsCommandFilter.cs | 8 ++-- HotCommands/HotCommandsPackage.vsct | 31 +++++++++---- HotCommands/Packaging/ReleaseNotes.txt | 1 + HotCommands/source.extension.vsixmanifest | 2 +- 7 files changed, 72 insertions(+), 22 deletions(-) diff --git a/HotCommands/Commands/DuplicateSelection.cs b/HotCommands/Commands/DuplicateSelection.cs index 483328a..58400b2 100644 --- a/HotCommands/Commands/DuplicateSelection.cs +++ b/HotCommands/Commands/DuplicateSelection.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Windows; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; @@ -35,9 +36,9 @@ private DuplicateSelection(Package package) _package = package; } - public static int HandleCommand_DuplicateLine(IWpfTextView textView, IClassifier classifier, + public static int HandleCommand_DuplicateLines(IWpfTextView textView, IClassifier classifier, IOleCommandTarget commandTarget, IEditorOperations editorOperations, - ITextBufferUndoManagerProvider undoManagerProvider) + ITextBufferUndoManagerProvider undoManagerProvider, bool isCopyUp) { // Use cases: // Single or Multiple carets @@ -65,6 +66,7 @@ public static int HandleCommand_DuplicateLine(IWpfTextView textView, IClassifier SnapshotPoint endOfLastLine = endPoint.GetContainingLine().End; // Don't include the last line if the end point is at the very beginning! bool endsAtLineStart = span.Length > 0 && (endPoint.GetContainingLine().Start.Position == endPoint.Position); + bool endsAtLineEnd = span.Length > 0 && (endPoint.GetContainingLine().End.Position == endPoint.Position); if (endsAtLineStart) { // Return the text up to the actual endpoint, not the end of its line. @@ -76,14 +78,40 @@ public static int HandleCommand_DuplicateLine(IWpfTextView textView, IClassifier SnapshotSpan linesToCopy = new SnapshotSpan(startOfFirstLine, endOfLastLine); string text = linesToCopy.GetText(); - // Always end with a new line (CR/LF) - if (!endsAtLineStart) + // Copy Lines Up? or Copy Lines Down? + if (isCopyUp) // ie. CopyLinesUp { - text += Environment.NewLine; // Note: This does not detect the line endings of the current file. + // Always start with a new line (CR/LF) + if (!endsAtLineStart) + { + text = Environment.NewLine + text; // Note: This does not detect the line endings of the current file. + } + + // Insert the text on a new line after the last line - TODO (CopyLinesUp) + int insertPosn = endOfLastLine.Position; + textView.TextBuffer.Insert(insertPosn, text); // (CopyLinesUp) + + // Hack: Fix the selection, if the selection ended at the end of a line or start of new line. + if (endsAtLineStart || endsAtLineEnd) + { + // Hack: Only works for single-selection. TODO: Fix for multi-selection. + if (spans.Count < 2) + { + editorOperations.ExtendSelection(endPoint); + } + } } + else // ie. CopyLinesDown + { + // Always end with a new line (CR/LF) + if (!endsAtLineStart) + { + text += Environment.NewLine; // Note: This does not detect the line endings of the current file. + } - // Insert the text at the start of the first line - textView.TextBuffer.Insert(startOfFirstLine.Position, text); + // Insert the text at the start of the first line + textView.TextBuffer.Insert(startOfFirstLine.Position, text); // (CopyLinesDown) + } } // Complete the transaction @@ -96,7 +124,7 @@ public static int HandleCommand_DuplicateLine(IWpfTextView textView, IClassifier public static int HandleCommand(IWpfTextView textView, IClassifier classifier, IOleCommandTarget commandTarget, IEditorOperations editorOperations, bool shiftPressed = false) { //Guid cmdGroup = VSConstants.VSStd2K; - var selectedText = editorOperations.SelectedText; + string selectedText = editorOperations.SelectedText; ITrackingPoint trackingPoint = null; if (selectedText.Length == 0) { @@ -149,6 +177,7 @@ public static int HandleCommand(IWpfTextView textView, IClassifier classifier, I } + // Case where there is just one selection: if (list.Count < 2) { var offset = 0; @@ -181,6 +210,7 @@ public static int HandleCommand(IWpfTextView textView, IClassifier classifier, I editorOperations.SelectAndMoveCaret(virtualSnapshotPoint1, virtualSnapshotPoint2, TextSelectionMode.Stream); } } + // Handle Multi-selections: else { var trackingPointOffsetList = new List>(); diff --git a/HotCommands/Constants.cs b/HotCommands/Constants.cs index bcd1f2b..b068a0c 100644 --- a/HotCommands/Constants.cs +++ b/HotCommands/Constants.cs @@ -5,7 +5,8 @@ namespace HotCommands public class Constants { public static readonly Guid HotCommandsGuid = new Guid("1023dc3d-550c-46b8-a3ec-c6b03431642c"); - public const uint DuplicateLineCmdId = 0x1018; + public const uint DuplicateLinesUpCmdId = 0x1017; + public const uint DuplicateLinesDownCmdId = 0x1018; public const uint DuplicateSelectionCmdId = 0x1019; public const uint DuplicateSelectionReverseCmdId = 0x1020; public const uint ToggleCommentCmdId = 0x1021; diff --git a/HotCommands/Hot Commands Shortcuts.vssettings b/HotCommands/Hot Commands Shortcuts.vssettings index f28b1a2..c4c5a85 100644 --- a/HotCommands/Hot Commands Shortcuts.vssettings +++ b/HotCommands/Hot Commands Shortcuts.vssettings @@ -18,7 +18,8 @@ Ctrl+Shift+Bkspce Ctrl+D - Ctrl+Shift+D + Ctrl+Shift+U + Ctrl+Shift+D Ctrl+D diff --git a/HotCommands/HotCommandsCommandFilter.cs b/HotCommands/HotCommandsCommandFilter.cs index dda3bd7..c01b4ef 100644 --- a/HotCommands/HotCommandsCommandFilter.cs +++ b/HotCommands/HotCommandsCommandFilter.cs @@ -55,8 +55,10 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv return ExpandSelection.Instance.HandleCommand(textView, false); case Constants.FormatCodeCmdId: return FormatCode.Instance.HandleCommand(textView, GetShellCommandDispatcher()); - case Constants.DuplicateLineCmdId: - return DuplicateSelection.HandleCommand_DuplicateLine(textView, classifier, GetShellCommandDispatcher(), editorOperations, undoManagerProvider); + case Constants.DuplicateLinesUpCmdId: + return DuplicateSelection.HandleCommand_DuplicateLines(textView, classifier, GetShellCommandDispatcher(), editorOperations, undoManagerProvider, true); + case Constants.DuplicateLinesDownCmdId: + return DuplicateSelection.HandleCommand_DuplicateLines(textView, classifier, GetShellCommandDispatcher(), editorOperations, undoManagerProvider, false); case Constants.DuplicateSelectionCmdId: return DuplicateSelection.HandleCommand(textView, classifier, GetShellCommandDispatcher(), editorOperations); case Constants.DuplicateSelectionReverseCmdId: @@ -103,7 +105,7 @@ public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, Int case Constants.ToggleCommentCmdId: case Constants.ExpandSelectionCmdId: case Constants.FormatCodeCmdId: - case Constants.DuplicateLineCmdId: + case Constants.DuplicateLinesDownCmdId: case Constants.DuplicateSelectionCmdId: case Constants.DuplicateSelectionReverseCmdId: case Constants.MoveMemberUpCmdId: diff --git a/HotCommands/HotCommandsPackage.vsct b/HotCommands/HotCommandsPackage.vsct index acd9494..72bea00 100644 --- a/HotCommands/HotCommandsPackage.vsct +++ b/HotCommands/HotCommandsPackage.vsct @@ -69,13 +69,22 @@ - +