Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MenuComplete: Improve tab completion in Filesystem providers #4038

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions PSReadLine/Completion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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, 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))
{
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);
Expand Down