Skip to content

Commit

Permalink
New feature: DuplicateLinesUp (CopyLinesUp) (#92)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
justcla authored Feb 16, 2023
1 parent 0305ad5 commit 3b2666f
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 22 deletions.
46 changes: 38 additions & 8 deletions HotCommands/Commands/DuplicateSelection.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<Tuple<ITrackingPoint, int, int>>();
Expand Down
3 changes: 2 additions & 1 deletion HotCommands/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion HotCommands/Hot Commands Shortcuts.vssettings
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
<Shortcut Command="Edit.GoToLastEditLocation" Scope="Global">Ctrl+Shift+Bkspce</Shortcut> <!-- In VS 15.8+ -->
<!--Duplicate Code-->
<Shortcut Command="Edit.Duplicate" Scope="Text Editor">Ctrl+D</Shortcut> <!-- v15.8+ -->
<Shortcut Command="Edit.DuplicateLines" Scope="Text Editor">Ctrl+Shift+D</Shortcut>
<Shortcut Command="Edit.DuplicateLinesUp" Scope="Text Editor">Ctrl+Shift+U</Shortcut>
<Shortcut Command="Edit.DuplicateLinesDown" Scope="Text Editor">Ctrl+Shift+D</Shortcut>
<Shortcut Command="Edit.DuplicateSelection" Scope="Text Editor">Ctrl+D</Shortcut>
<!--<Shortcut Command="Edit.DuplicateSelectionReversed" Scope="Text Editor">Ctrl+Shift+D</Shortcut>-->
<!--Expand Selection-->
Expand Down
8 changes: 5 additions & 3 deletions HotCommands/HotCommandsCommandFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
31 changes: 23 additions & 8 deletions HotCommands/HotCommandsPackage.vsct
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,22 @@
</Strings>
</Button>

<Button guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLine" priority="0x0000" type="Button">
<Button guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesUp" priority="0x0000" type="Button">
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Duplicate Line</ButtonText>
<MenuText>Duplicate &amp;Line</MenuText>
<ToolTipText>Duplicates the selected text.</ToolTipText>
<ButtonText>Duplicate Lines Up</ButtonText>
<MenuText>Duplicate Lines &amp;Up</MenuText>
<ToolTipText>Duplicates the selected lines up.</ToolTipText>
</Strings>
</Button>
<Button guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesDown" priority="0x0000" type="Button">
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Duplicate Lines Down</ButtonText>
<MenuText>Duplicate &amp;Lines Down</MenuText>
<ToolTipText>Duplicates the selected lines down.</ToolTipText>
</Strings>
</Button>
<Button guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateSelection" priority="0x0000" type="Button">
Expand Down Expand Up @@ -187,7 +196,10 @@
<CommandPlacement guid="guidHotCommandsPackageCmdSet" id="cmdidToggleComment" priority="0x7515">
<Parent guid="guidStdEditor" id="IDG_VS_EDITOR_ADVANCED_CMDS"/>
</CommandPlacement>
<CommandPlacement guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLine" priority="0x7516">
<CommandPlacement guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesUp" priority="0x7516">
<Parent guid="guidStdEditor" id="IDG_VS_EDITOR_ADVANCED_CMDS"/>
</CommandPlacement>
<CommandPlacement guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesDown" priority="0x7516">
<Parent guid="guidStdEditor" id="IDG_VS_EDITOR_ADVANCED_CMDS"/>
</CommandPlacement>
<CommandPlacement guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateSelection" priority="0x7517">
Expand Down Expand Up @@ -222,7 +234,8 @@
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidExpandSelection" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidShrinkSelection" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidFormatCode" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLine" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesUp" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesDown" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateSelection" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateSelectionReverse" context="GUID_TextEditorFactory"/>
<VisibilityItem guid="guidHotCommandsPackageCmdSet" id="cmdidJoinLines" context="GUID_TextEditorFactory"/>
Expand All @@ -242,7 +255,8 @@
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidShrinkSelection" editor="GUID_TextEditorFactory" mod1="Control Shift" key1="W"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidFormatCode" editor="GUID_TextEditorFactory" mod1="Control Alt" key1="F"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateSelection" editor="GUID_TextEditorFactory" mod1="Control" key1="D"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLine" editor="GUID_TextEditorFactory" mod1="Control Shift" key1="D"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesUp" editor="GUID_TextEditorFactory" mod1="Control Shift" key1="U"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidDuplicateLinesDown" editor="GUID_TextEditorFactory" mod1="Control Shift" key1="D"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidJoinLines" editor="GUID_TextEditorFactory" mod1="Control Shift" key1="J"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidGoToPreviousMember" editor="GUID_TextEditorFactory" mod1="Control Alt" key1="VK_UP"/>
<KeyBinding guid="guidHotCommandsPackageCmdSet" id="cmdidGoToNextMember" editor="GUID_TextEditorFactory" mod1="Control Alt" key1="VK_DOWN"/>
Expand All @@ -256,7 +270,8 @@

<!-- This is the guid used to group the menu commands together -->
<GuidSymbol name="guidHotCommandsPackageCmdSet" value="{1023dc3d-550c-46b8-a3ec-c6b03431642c}">
<IDSymbol name="cmdidDuplicateLine" value="0x1018" />
<IDSymbol name="cmdidDuplicateLinesUp" value="0x1017" />
<IDSymbol name="cmdidDuplicateLinesDown" value="0x1018" />
<IDSymbol name="cmdidDuplicateSelection" value="0x1019" />
<IDSymbol name="cmdidDuplicateSelectionReverse" value="0x1020" />
<IDSymbol name="cmdidToggleComment" value="0x1021" />
Expand Down
1 change: 1 addition & 0 deletions HotCommands/Packaging/ReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Hot Commands for Visual Studio
16-Feb-2023: v4.2.0 Added command: DuplicateLinesUp (Issue #93 - Request from Carl Foghammar N�mtak)
07-Aug-2022: v4.1.0 Added command: DuplicateLine (Issue #89 - Fabio Marmitt)
10-Jan-2022: v4.0.0 Upgraded to VS2022. Now only targeting VS2022+.
04-May-2020: v3.0.0 Now only targeting VS2017+. Handles Async loading (Issue #81/PR #82)
Expand Down
2 changes: 1 addition & 1 deletion HotCommands/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="e8175b11-0e09-42b5-9c3c-ba7bfb53a311" Version="4.1.0" Language="en-US" Publisher="Justin Clareburt" />
<Identity Id="e8175b11-0e09-42b5-9c3c-ba7bfb53a311" Version="4.2.0" Language="en-US" Publisher="Justin Clareburt" />
<DisplayName>Hot Commands</DisplayName>
<Description xml:space="preserve">A collection of commands and shortcuts for enhanced productivity in Visual Studio IDE. VS2022+</Description>
<MoreInfo>https://marketplace.visualstudio.com/items?itemName=JustinClareburtMSFT.HotCommandsforVisualStudio</MoreInfo>
Expand Down

0 comments on commit 3b2666f

Please sign in to comment.