Skip to content

Commit 6808c18

Browse files
authored
Add support for upcasing, downcasing, and capitalizing word (#3365)
1 parent bbbe43b commit 6808c18

File tree

5 files changed

+150
-2
lines changed

5 files changed

+150
-2
lines changed

PSReadLine/BasicEditing.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,72 @@ public static void DeleteCharOrExit(ConsoleKeyInfo? key = null, object arg = nul
246246
_singleton.DeleteCharImpl(1, orExit: true);
247247
}
248248

249+
/// <summary>
250+
/// A helper function to change the case of the current word.
251+
/// </summary>
252+
private static void UpdateWordCase(bool toUpper)
253+
{
254+
if (_singleton._current >= _singleton._buffer.Length)
255+
{
256+
Ding();
257+
return;
258+
}
259+
260+
int endOfWord = _singleton.FindForwardWordPoint(_singleton.Options.WordDelimiters);
261+
int wordlen = endOfWord - _singleton._current;
262+
263+
string word = _singleton._buffer.ToString(_singleton._current, wordlen);
264+
word = toUpper ? word.ToUpper() : word.ToLower();
265+
266+
Replace(_singleton._current, wordlen, word);
267+
268+
_singleton.MoveCursor(endOfWord);
269+
_singleton.Render();
270+
}
271+
272+
/// <summary>
273+
/// Upcase the current word and move to the next one.
274+
/// </summary>
275+
public static void UpcaseWord(ConsoleKeyInfo? key = null, object arg = null)
276+
{
277+
UpdateWordCase(toUpper: true);
278+
}
279+
280+
/// <summary>
281+
/// Downcase the current word and move to the next one.
282+
/// </summary>
283+
public static void DowncaseWord(ConsoleKeyInfo? key = null, object arg = null)
284+
{
285+
UpdateWordCase(toUpper: false);
286+
}
287+
288+
/// <summary>
289+
/// Capitalize the current word and move to the next one.
290+
/// </summary>
291+
public static void CapitalizeWord(ConsoleKeyInfo? key = null, object arg = null)
292+
{
293+
if (_singleton._current >= _singleton._buffer.Length)
294+
{
295+
Ding();
296+
return;
297+
}
298+
299+
int endOfWord = _singleton.FindForwardWordPoint(_singleton.Options.WordDelimiters);
300+
int wordlen = endOfWord - _singleton._current;
301+
302+
char[] word = _singleton._buffer.ToString(_singleton._current, wordlen).ToLower().ToCharArray();
303+
int firstLetterIdx = Array.FindIndex(word, static x => char.IsLetter(x));
304+
305+
if (firstLetterIdx >= 0)
306+
{
307+
word[firstLetterIdx] = char.ToUpper(word[firstLetterIdx]);
308+
Replace(_singleton._current, wordlen, new string(word));
309+
}
310+
311+
_singleton.MoveCursor(endOfWord);
312+
_singleton.Render();
313+
}
314+
249315
private bool AcceptLineImpl(bool validate)
250316
{
251317
using var _ = _prediction.DisableScoped();

PSReadLine/KeyBindings.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ void SetDefaultEmacsBindings()
339339
{ Keys.AltH, MakeKeyHandler(ShowParameterHelp, "ShowParameterHelp") },
340340
{ Keys.F1, MakeKeyHandler(ShowCommandHelp, "ShowCommandHelp") },
341341
{ Keys.F2, MakeKeyHandler(SwitchPredictionView, "SwitchPredictionView") },
342+
{ Keys.AltU, MakeKeyHandler(UpcaseWord, "UpcaseWord") },
343+
{ Keys.AltL, MakeKeyHandler(DowncaseWord, "DowncaseWord") },
344+
{ Keys.AltC, MakeKeyHandler(CapitalizeWord, "CapitalizeWord") },
342345
};
343346

344347
// Some bindings are not available on certain platforms
@@ -371,6 +374,9 @@ void SetDefaultEmacsBindings()
371374
{ Keys.F, MakeKeyHandler(ForwardWord, "ForwardWord")},
372375
{ Keys.R, MakeKeyHandler(RevertLine, "RevertLine")},
373376
{ Keys.Y, MakeKeyHandler(YankPop, "YankPop")},
377+
{ Keys.U, MakeKeyHandler(UpcaseWord, "UpcaseWord") },
378+
{ Keys.L, MakeKeyHandler(DowncaseWord, "DowncaseWord") },
379+
{ Keys.C, MakeKeyHandler(CapitalizeWord, "CapitalizeWord") },
374380
{ Keys.CtrlY, MakeKeyHandler(YankNthArg, "YankNthArg")},
375381
{ Keys.Backspace, MakeKeyHandler(BackwardKillWord, "BackwardKillWord")},
376382
{ Keys.Period, MakeKeyHandler(YankLastArg, "YankLastArg")},
@@ -399,28 +405,30 @@ public static KeyHandlerGroup GetDisplayGrouping(string function)
399405
case nameof(AcceptAndGetNext):
400406
case nameof(AcceptLine):
401407
case nameof(AddLine):
402-
case nameof(BackwardDeleteInput):
403408
case nameof(BackwardDeleteChar):
409+
case nameof(BackwardDeleteInput):
404410
case nameof(BackwardDeleteLine):
405411
case nameof(BackwardDeleteWord):
406412
case nameof(BackwardKillInput):
407413
case nameof(BackwardKillLine):
408414
case nameof(BackwardKillWord):
409415
case nameof(CancelLine):
416+
case nameof(CapitalizeWord):
410417
case nameof(Copy):
411418
case nameof(CopyOrCancelLine):
412419
case nameof(Cut):
413420
case nameof(DeleteChar):
414421
case nameof(DeleteCharOrExit):
415422
case nameof(DeleteEndOfBuffer):
416423
case nameof(DeleteEndOfWord):
417-
case nameof(DeleteRelativeLines):
418424
case nameof(DeleteLine):
419425
case nameof(DeleteLineToFirstChar):
420426
case nameof(DeleteNextLines):
421427
case nameof(DeletePreviousLines):
428+
case nameof(DeleteRelativeLines):
422429
case nameof(DeleteToEnd):
423430
case nameof(DeleteWord):
431+
case nameof(DowncaseWord):
424432
case nameof(ForwardDeleteInput):
425433
case nameof(ForwardDeleteLine):
426434
case nameof(InsertLineAbove):
@@ -442,6 +450,7 @@ public static KeyHandlerGroup GetDisplayGrouping(string function)
442450
case nameof(Undo):
443451
case nameof(UndoAll):
444452
case nameof(UnixWordRubout):
453+
case nameof(UpcaseWord):
445454
case nameof(ValidateAndAcceptLine):
446455
case nameof(ViAcceptLine):
447456
case nameof(ViAcceptLineOrExit):

PSReadLine/PSReadLineResources.Designer.cs

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PSReadLine/PSReadLineResources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,4 +858,13 @@ Or not saving history with:
858858
<data name="FailedToConvertPointToRenderDataOffset" xml:space="preserve">
859859
<value>Cannot locate the offset in the rendered text that was pointed by the original cursor. Initial Coord: ({0}, {1}) Buffer: ({2}, {3}) Cursor: ({4}, {5})</value>
860860
</data>
861+
<data name="CapitalizeWordDescription" xml:space="preserve">
862+
<value>Find the next word starting from the current position and then make it Pascal case.</value>
863+
</data>
864+
<data name="DowncaseWordDescription" xml:space="preserve">
865+
<value>Find the next word starting from the current position and then make it lower case.</value>
866+
</data>
867+
<data name="UpcaseWordDescription" xml:space="preserve">
868+
<value>Find the next word starting from the current position and then make it upper case.</value>
869+
</data>
861870
</root>

test/BasicEditingTest.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,37 @@ public void DeleteCharOrExit()
200200
Test("exit", Keys("foo", _.Home, Enumerable.Repeat(_.Ctrl_d, 4), InputAcceptedNow));
201201
}
202202

203+
[SkippableFact]
204+
public void UpcaseWord()
205+
{
206+
TestSetup(KeyMode.Emacs);
207+
Test("FOO", Keys("foo", _.Alt_b, _.Alt_u));
208+
Test("FOO bar", Keys("foo bar", _.Home, _.Alt_u));
209+
Test("foo BAR", Keys("foo bar", _.Alt_b, _.Alt_u));
210+
Test("FOO BAR", Keys("foo bar", _.Home, Enumerable.Repeat(_.Alt_u, 2)));
211+
}
212+
213+
[SkippableFact]
214+
public void DowncaseWord()
215+
{
216+
TestSetup(KeyMode.Emacs);
217+
Test("foo", Keys("fOO", _.Alt_b, _.Alt_l));
218+
Test("FOO bar", Keys("FOO BAR", _.Alt_b, _.Alt_l));
219+
Test("foo BAR", Keys("FOO BAR", _.Home, _.Alt_l));
220+
Test("foo bar", Keys("FOO BAR", _.Home, Enumerable.Repeat(_.Alt_l, 2)));
221+
}
222+
223+
[SkippableFact]
224+
public void CapitalizeWord()
225+
{
226+
TestSetup(KeyMode.Emacs);
227+
Test("Foo", Keys("fOO", _.Alt_b, _.Alt_c));
228+
Test("fOO Bar", Keys("fOO bAR", _.Alt_b, _.Alt_c));
229+
Test("Foo BAR", Keys("fOO BAR", _.Home, _.Alt_c));
230+
Test("Foo Bar", Keys("fOO BAR", _.Home, Enumerable.Repeat(_.Alt_c, 2)));
231+
Test("Foo ^&*() Bar", Keys("Foo ^&*() bar", _.Home, Enumerable.Repeat(_.Alt_c, 2)));
232+
}
233+
203234
[SkippableFact]
204235
public void SelectAndDelete()
205236
{

0 commit comments

Comments
 (0)