diff --git a/PSReadLine/BasicEditing.cs b/PSReadLine/BasicEditing.cs index e7236a21..33405a88 100644 --- a/PSReadLine/BasicEditing.cs +++ b/PSReadLine/BasicEditing.cs @@ -97,16 +97,36 @@ public static void CancelLine(ConsoleKeyInfo? key = null, object arg = null) } /// - /// Like ForwardKillLine - deletes text from the point to the end of the line, + /// Like KillLine - deletes text from the point to the end of the input, + /// but does not put the deleted text in the kill ring. + /// + public static void ForwardDeleteInput(ConsoleKeyInfo? key = null, object arg = null) + { + ForwardDeleteImpl(_singleton._buffer.Length); + } + + /// + /// Deletes text from the point to the end of the current logical line, /// but does not put the deleted text in the kill ring. /// public static void ForwardDeleteLine(ConsoleKeyInfo? key = null, object arg = null) + { + ForwardDeleteImpl(GetEndOfLogicalLinePos(_singleton._current) + 1); + } + + /// + /// Deletes text from the cursor position to the specified end position + /// but does not put the deleted text in the kill ring. + /// + /// 0-based offset to one character past the end of the text. + private static void ForwardDeleteImpl(int endPosition) { var current = _singleton._current; var buffer = _singleton._buffer; - if (buffer.Length > 0 && current < buffer.Length) + + if (buffer.Length > 0 && current < endPosition) { - int length = buffer.Length - current; + int length = endPosition - current; var str = buffer.ToString(current, length); _singleton.SaveEditItem(EditItemDelete.Create(str, current)); buffer.Remove(current, length); @@ -115,17 +135,33 @@ public static void ForwardDeleteLine(ConsoleKeyInfo? key = null, object arg = nu } /// - /// Like BackwardKillLine - deletes text from the point to the start of the line, + /// Like BackwardKillInput - deletes text from the point to the start of the input, + /// but does not put the deleted text in the kill ring. + public static void BackwardDeleteInput(ConsoleKeyInfo? key = null, object arg = null) + { + BackwardDeleteSubstring(0, BackwardDeleteInput); + } + + /// + /// Like BackwardKillLine - deletes text from the point to the start of the logical line, /// but does not put the deleted text in the kill ring. /// public static void BackwardDeleteLine(ConsoleKeyInfo? key = null, object arg = null) { - if (_singleton._current > 0) + var position = GetBeginningOfLinePos(_singleton._current); + BackwardDeleteSubstring(position, BackwardDeleteLine); + } + + private static void BackwardDeleteSubstring(int position, Action instigator = null) + { + if (_singleton._current > position) { - _clipboard.Record(_singleton._buffer, 0, _singleton._current); - _singleton.SaveEditItem(EditItemDelete.Create(_clipboard, 0)); - _singleton._buffer.Remove(0, _singleton._current); - _singleton._current = 0; + var count = _singleton._current - position; + + _clipboard.Record(_singleton._buffer, position, count); + _singleton.SaveEditItem(EditItemDelete.Create(_clipboard, position, instigator)); + _singleton._buffer.Remove(position, count); + _singleton._current = position; _singleton.Render(); } } diff --git a/PSReadLine/KeyBindings.cs b/PSReadLine/KeyBindings.cs index edd049a5..b9691d2b 100644 --- a/PSReadLine/KeyBindings.cs +++ b/PSReadLine/KeyBindings.cs @@ -209,7 +209,7 @@ void SetDefaultWindowsBindings() { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") }, { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") }, { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") }, - { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") }, + { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteInput, "BackwardDeleteInput") }, { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") }, { Keys.CtrlAltQuestion, MakeKeyHandler(ShowKeyBindings, "ShowKeyBindings") }, { Keys.AltPeriod, MakeKeyHandler(YankLastArg, "YankLastArg") }, @@ -242,7 +242,7 @@ void SetDefaultWindowsBindings() _dispatchTable.Add(Keys.CtrlSpace, MakeKeyHandler(MenuComplete, "MenuComplete")); _dispatchTable.Add(Keys.AltF7, MakeKeyHandler(ClearHistory, "ClearHistory")); _dispatchTable.Add(Keys.CtrlDelete, MakeKeyHandler(KillWord, "KillWord")); - _dispatchTable.Add(Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine")); + _dispatchTable.Add(Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteInput, "ForwardDeleteInput")); _dispatchTable.Add(Keys.CtrlH, MakeKeyHandler(BackwardDeleteChar,"BackwardDeleteChar")); // PageUp/PageDown and CtrlPageUp/CtrlPageDown bindings are supported on Windows only because they depend on the @@ -294,7 +294,7 @@ void SetDefaultEmacsBindings() { Keys.CtrlR, MakeKeyHandler(ReverseSearchHistory, "ReverseSearchHistory") }, { Keys.CtrlS, MakeKeyHandler(ForwardSearchHistory, "ForwardSearchHistory") }, { Keys.CtrlT, MakeKeyHandler(SwapCharacters, "SwapCharacters") }, - { Keys.CtrlU, MakeKeyHandler(BackwardKillLine, "BackwardKillLine") }, + { Keys.CtrlU, MakeKeyHandler(BackwardKillInput, "BackwardKillInput") }, { Keys.CtrlX, MakeKeyHandler(Chord, "ChordFirstKey") }, { Keys.CtrlW, MakeKeyHandler(UnixWordRubout, "UnixWordRubout") }, { Keys.CtrlY, MakeKeyHandler(Yank, "Yank") }, @@ -369,7 +369,7 @@ void SetDefaultEmacsBindings() // Ctrl+X, table [Keys.CtrlX] = new Dictionary { - { Keys.Backspace, MakeKeyHandler(BackwardKillLine, "BackwardKillLine") }, + { Keys.Backspace, MakeKeyHandler(BackwardKillInput, "BackwardKillInput") }, { Keys.CtrlE, MakeKeyHandler(ViEditVisually, "ViEditVisually") }, { Keys.CtrlU, MakeKeyHandler(Undo, "Undo") }, { Keys.CtrlX, MakeKeyHandler(ExchangePointAndMark, "ExchangePointAndMark") }, @@ -388,9 +388,11 @@ public static KeyHandlerGroup GetDisplayGrouping(string function) case nameof(AcceptAndGetNext): case nameof(AcceptLine): case nameof(AddLine): + case nameof(BackwardDeleteInput): case nameof(BackwardDeleteChar): case nameof(BackwardDeleteLine): case nameof(BackwardDeleteWord): + case nameof(BackwardKillInput): case nameof(BackwardKillLine): case nameof(BackwardKillWord): case nameof(CancelLine): @@ -408,6 +410,7 @@ public static KeyHandlerGroup GetDisplayGrouping(string function) case nameof(DeletePreviousLines): case nameof(DeleteToEnd): case nameof(DeleteWord): + case nameof(ForwardDeleteInput): case nameof(ForwardDeleteLine): case nameof(InsertLineAbove): case nameof(InsertLineBelow): diff --git a/PSReadLine/KeyBindings.vi.cs b/PSReadLine/KeyBindings.vi.cs index e7772955..8f4beb07 100644 --- a/PSReadLine/KeyBindings.vi.cs +++ b/PSReadLine/KeyBindings.vi.cs @@ -72,7 +72,7 @@ private void SetDefaultViBindings() { Keys.CtrlSpace, MakeKeyHandler(PossibleCompletions, "PossibleCompletions") }, { Keys.Tab, MakeKeyHandler(ViTabCompleteNext, "ViTabCompleteNext") }, { Keys.ShiftTab, MakeKeyHandler(ViTabCompletePrevious, "ViTabCompletePrevious") }, - { Keys.CtrlU, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") }, + { Keys.CtrlU, MakeKeyHandler(BackwardDeleteInput, "BackwardDeleteInput") }, { Keys.CtrlV, MakeKeyHandler(Paste, "Paste") }, { Keys.CtrlC, MakeKeyHandler(CancelLine, "CancelLine") }, { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") }, @@ -80,8 +80,8 @@ private void SetDefaultViBindings() { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") }, { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") }, { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") }, - { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine") }, - { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") }, + { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteInput, "ForwardDeleteInput") }, + { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteInput, "BackwardDeleteInput") }, { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") }, { Keys.F3, MakeKeyHandler(CharacterSearch, "CharacterSearch") }, { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward,"CharacterSearchBackward") }, @@ -120,13 +120,13 @@ private void SetDefaultViBindings() { Keys.CtrlC, MakeKeyHandler(CancelLine, "CancelLine") }, { Keys.CtrlL, MakeKeyHandler(ClearScreen, "ClearScreen") }, { Keys.CtrlT, MakeKeyHandler(SwapCharacters, "SwapCharacters") }, - { Keys.CtrlU, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") }, + { Keys.CtrlU, MakeKeyHandler(BackwardDeleteInput, "BackwardDeleteInput") }, { Keys.CtrlW, MakeKeyHandler(BackwardDeleteWord, "BackwardDeleteWord") }, { Keys.CtrlY, MakeKeyHandler(Redo, "Redo") }, { Keys.CtrlZ, MakeKeyHandler(Undo, "Undo") }, { Keys.CtrlBackspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord") }, - { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteLine, "ForwardDeleteLine") }, - { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteLine, "BackwardDeleteLine") }, + { Keys.CtrlEnd, MakeKeyHandler(ForwardDeleteInput, "ForwardDeleteInput") }, + { Keys.CtrlHome, MakeKeyHandler(BackwardDeleteInput, "BackwardDeleteInput") }, { Keys.CtrlRBracket, MakeKeyHandler(GotoBrace, "GotoBrace") }, { Keys.F3, MakeKeyHandler(CharacterSearch, "CharacterSearch") }, { Keys.ShiftF3, MakeKeyHandler(CharacterSearchBackward, "CharacterSearchBackward") }, diff --git a/PSReadLine/KillYank.cs b/PSReadLine/KillYank.cs index aa20e178..179bb360 100644 --- a/PSReadLine/KillYank.cs +++ b/PSReadLine/KillYank.cs @@ -117,11 +117,21 @@ public static void KillLine(ConsoleKeyInfo? key = null, object arg = null) /// Clear the input from the start of the input to the cursor. The cleared text is placed /// in the kill ring. /// - public static void BackwardKillLine(ConsoleKeyInfo? key = null, object arg = null) + public static void BackwardKillInput(ConsoleKeyInfo? key = null, object arg = null) { _singleton.Kill(0, _singleton._current, true); } + /// + /// Clear the input from the start of the current logical line to the cursor. The cleared text is placed + /// in the kill ring. + /// + public static void BackwardKillLine(ConsoleKeyInfo? key = null, object arg = null) + { + var start = GetBeginningOfLinePos(_singleton._current); + _singleton.Kill(start, _singleton._current, true); + } + /// /// Clear the input from the cursor to the end of the current word. If the cursor /// is between words, the input is cleared from the cursor to the end of the next word. diff --git a/PSReadLine/PSReadLineResources.Designer.cs b/PSReadLine/PSReadLineResources.Designer.cs index d26a5e4b..ac7ce268 100644 --- a/PSReadLine/PSReadLineResources.Designer.cs +++ b/PSReadLine/PSReadLineResources.Designer.cs @@ -115,7 +115,16 @@ internal static string BackwardDeleteCharDescription { } /// - /// Looks up a localized string similar to Delete text from the cursor to the start of the line. + /// Looks up a localized string similar to Delete text from the cursor to the start of the input. + /// + internal static string BackwardDeleteInputDescription { + get { + return ResourceManager.GetString("BackwardDeleteInputDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete text from the cursor to the start of the current logical line. /// internal static string BackwardDeleteLineDescription { get { @@ -135,6 +144,15 @@ internal static string BackwardDeleteWordDescription { /// /// Looks up a localized string similar to Move the text from the cursor to the beginning of the line to the kill ring. /// + internal static string BackwardKillInputDescription { + get { + return ResourceManager.GetString("BackwardKillInputDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move the text from the start of the current logical line to the cursor to the kill ring. + /// internal static string BackwardKillLineDescription { get { return ResourceManager.GetString("BackwardKillLineDescription", resourceCulture); @@ -504,6 +522,15 @@ internal static string ForwardCharDescription { /// /// Looks up a localized string similar to Delete text from the cursor to the end of the line. /// + internal static string ForwardDeleteInputDescription { + get { + return ResourceManager.GetString("ForwardDeleteInputDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete text from the cursor to the end of the current logical line. + /// internal static string ForwardDeleteLineDescription { get { return ResourceManager.GetString("ForwardDeleteLineDescription", resourceCulture); diff --git a/PSReadLine/PSReadLineResources.resx b/PSReadLine/PSReadLineResources.resx index 76f70996..fbc6ae0b 100644 --- a/PSReadLine/PSReadLineResources.resx +++ b/PSReadLine/PSReadLineResources.resx @@ -129,11 +129,17 @@ Delete the character before the cursor + + Delete text from the cursor to the start of the input + - Delete text from the cursor to the start of the line + Delete text from the cursor to the start of the current logical line + + + Move the text from the cursor to the beginning of the input to the kill ring - Move the text from the cursor to the beginning of the line to the kill ring + Move the text from the start of the current logical line to the cursor to the kill ring Move to the first item in the history @@ -180,8 +186,11 @@ Move the cursor forward one character + + Delete text from the cursor to the end of the input + - Delete text from the cursor to the end of the line + Delete text from the cursor to the end of the current logical line Move the cursor to the beginning of the next token or end of line diff --git a/test/BasicEditingTest.VI.cs b/test/BasicEditingTest.VI.cs index 42b66b74..c725ee49 100644 --- a/test/BasicEditingTest.VI.cs +++ b/test/BasicEditingTest.VI.cs @@ -570,6 +570,26 @@ public void ViDeleteToEnd() )); } + [SkippableFact] + public void ViBackwardDeleteLine() + { + TestSetup(KeyMode.Vi); + + int continuationPrefixLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length; + + Test("\"\nsome words\n\"", Keys( + + _.DQuote, _.Enter, + " this is a line with some words", _.Enter, + _.DQuote, _.Escape, + "k6W", + CheckThat(() => AssertCursorLeftIs(continuationPrefixLength + 23)), + // delete from first non blank of line + "d0", + CheckThat(() => AssertCursorLeftIs(continuationPrefixLength)) + )); + } + [SkippableFact] public void ViDeleteLineToFirstChar() { diff --git a/test/BasicEditingTest.cs b/test/BasicEditingTest.cs index de9fd7f2..6710181c 100644 --- a/test/BasicEditingTest.cs +++ b/test/BasicEditingTest.cs @@ -73,7 +73,7 @@ public void CancelLine() } [SkippableFact] - public void ForwardDeleteLine() + public void ForwardDeleteInput() { ConsoleKeyInfo deleteToEnd; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -98,7 +98,22 @@ public void ForwardDeleteLine() } [SkippableFact] - public void BackwardDeleteLine() + public void ForwardDeleteLine() + { + TestSetup(KeyMode.Emacs); + + PSConsoleReadLine.SetKeyHandler(new[] { "Shift+Tab" }, PSConsoleReadLine.ForwardDeleteLine, "", ""); + + Test("\"H\nWorld\"", Keys( + _.DQuote, "Hello", _.Enter, + "World", _.DQuote, + _.Home, _.Home, _.RightArrow, _.RightArrow, + _.Shift_Tab // delete to the end of the current logical line + )); + } + + [SkippableFact] + public void BackwardDeleteInput() { TestSetup(KeyMode.Cmd); diff --git a/test/KillYankTest.cs b/test/KillYankTest.cs index 72677d51..1612e7ce 100644 --- a/test/KillYankTest.cs +++ b/test/KillYankTest.cs @@ -116,6 +116,16 @@ public void BackwardKillLine() { TestSetup(KeyMode.Emacs); + PSConsoleReadLine.SetKeyHandler(new[] { "Shift+Tab" }, PSConsoleReadLine.BackwardKillLine, "", ""); + + Test("", Keys("dir", _.Shift_Tab)); + } + + [SkippableFact] + public void BackwardKillInput() + { + TestSetup(KeyMode.Emacs); + // Kill whole line // Check killed text by yanking Test("ls", Keys("dir", _.Ctrl_u, "ls"));