From 950b54f10ad594fe2698a8034bf5a199a08f27ca Mon Sep 17 00:00:00 2001 From: blaisemGH <81759796+blaisemGH@users.noreply.github.com> Date: Thu, 13 Jun 2024 01:49:27 +0200 Subject: [PATCH 1/2] Feature/improve menu complete tab behavior in filesystems (#2) * Address 3 issues: 1. pressing space inside quotes autocompletes the current word, even if there are multiple ambiguous choices available 2. Pressing Tab to open a menu and then typing out a directory completely locks tab from autocompleting. It stops before the slash and requires an enter instead. 3. Align tab completion behavior between no menu and menu scenarios when a single item is involved and that item is a container in a filesystem provider. --- PSReadLine/Completion.cs | 53 +++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/PSReadLine/Completion.cs b/PSReadLine/Completion.cs index c6654f92..65a7b9ce 100644 --- a/PSReadLine/Completion.cs +++ b/PSReadLine/Completion.cs @@ -771,7 +771,7 @@ private int FindUserCompletionTextPosition(CompletionResult match, string userCo private bool IsDoneWithCompletions(CompletionResult currentCompletion, PSKeyInfo nextKey) { - return nextKey == Keys.Space + return (nextKey == Keys.Space && ! currentCompletion.CompletionText.Contains(' ')) || nextKey == Keys.Enter || KeysEndingCompletion.TryGetValue(currentCompletion.ResultType, out var doneKeys) && doneKeys.Contains(nextKey); @@ -949,21 +949,46 @@ private void MenuCompleteImpl(Menu menu, CommandCompletion completions) else if (nextKey == Keys.Tab) { // Search for possible unambiguous common prefix. - string unAmbiguousText = GetUnambiguousPrefix(menu.MenuItems, out ambiguous); - int userComplPos = unAmbiguousText.IndexOf(userCompletionText, StringComparison.OrdinalIgnoreCase); - - // ... If found - advance IncrementalCompletion ... - if (unAmbiguousText.Length > 0 && userComplPos >= 0 && - unAmbiguousText.Length > (userComplPos + userCompletionText.Length)) + string unambiguousText = GetUnambiguousPrefix(menu.MenuItems, out ambiguous); + int userComplPos = unambiguousText.IndexOf(userCompletionText, StringComparison.OrdinalIgnoreCase); + + // Obtain all the menu items beginning with unambigousText, so we can count them. + var unambiguousMenuItems = menu.MenuItems.Where(item => + item.CompletionText + .Trim('\'') // handles comparisons with items that have spaces in them (these auto receive quote wraps) + .StartsWith(unambiguousText, StringComparison.OrdinalIgnoreCase) + ); + int countUnambiguousItems = Enumerable.Count(unambiguousMenuItems); + + // If there is only 1 item, autoaccept it + if (unambiguousText.Length > 0 && userComplPos >= 0 && countUnambiguousItems == 1 ) { - userCompletionText = unAmbiguousText.Substring(userComplPos); - _current = completions.ReplacementIndex + - FindUserCompletionTextPosition(menu.MenuItems[menu.CurrentSelection], userCompletionText) + - userCompletionText.Length; - Render(); - Ding(); + processingKeys = false; + int cursorAdjustment = 0; + + var onlyCompletionResult = unambiguousMenuItems.First(); + userCompletionText = onlyCompletionResult.CompletionText; + + _current = userCompletionText.Length; + // Append a slash if it's a filesystem container + DoReplacementForCompletion(onlyCompletionResult, completions); + _current -= cursorAdjustment; + + // Autoaccepts the single available option (otherwise need to press rightarrow/tab a second time manually) + PrependQueuedKeys(Keys.RightArrow); + } + // For multiple items which are shorter length than the unambiguous text, autocomplete through the unambiguous text. + else if (unambiguousText.Length > 0 && userComplPos >= 0 && + unambiguousText.Length > (userComplPos + userCompletionText.Length)) + { + userCompletionText = unambiguousText.Substring(userComplPos); + _current = completions.ReplacementIndex + + FindUserCompletionTextPosition(menu.MenuItems[menu.CurrentSelection], userCompletionText) + + userCompletionText.Length; + Render(); + Ding(); } - // ... if no - usual Tab behaviour + // For multiple items and only ambiguous text remaining, tab will cycle through the available choices. else { menu.MoveN(1); From 10d9bfd9f557dfecca0d8b824d71a4829b684535 Mon Sep 17 00:00:00 2001 From: blaisemGH <81759796+blaisemGH@users.noreply.github.com> Date: Thu, 13 Jun 2024 02:26:43 +0200 Subject: [PATCH 2/2] rebased and clarified 1 comment --- PSReadLine/Completion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSReadLine/Completion.cs b/PSReadLine/Completion.cs index 65a7b9ce..f01edc92 100644 --- a/PSReadLine/Completion.cs +++ b/PSReadLine/Completion.cs @@ -977,7 +977,7 @@ private void MenuCompleteImpl(Menu menu, CommandCompletion completions) // Autoaccepts the single available option (otherwise need to press rightarrow/tab a second time manually) PrependQueuedKeys(Keys.RightArrow); } - // For multiple items which are shorter length than the unambiguous text, autocomplete through the unambiguous text. + // For multiple items, when your typed length is shorter length than the unambiguous text, autocomplete through the unambiguous text. else if (unambiguousText.Length > 0 && userComplPos >= 0 && unambiguousText.Length > (userComplPos + userCompletionText.Length)) {