-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Make TabCompletion case-insensitive for file names on Unix #4699
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4061,10 +4061,8 @@ internal static IEnumerable<CompletionResult> CompleteFilename(CompletionContext | |
| var relativePaths = context.GetOption("RelativePaths", @default: defaultRelative); | ||
| var useLiteralPath = context.GetOption("LiteralPaths", @default: false); | ||
|
|
||
| if (useLiteralPath && LocationGlobber.StringContainsGlobCharacters(wordToComplete)) | ||
| { | ||
| wordToComplete = WildcardPattern.Escape(wordToComplete, Utils.Separators.StarOrQuestion); | ||
| } | ||
| // We should always escape '[' and ']' if present. | ||
| wordToComplete = WildcardPattern.Escape(wordToComplete, Utils.Separators.StarOrQuestion); | ||
|
|
||
| if (!defaultRelative && wordToComplete.Length >= 2 && wordToComplete[1] == ':' && char.IsLetter(wordToComplete[0]) && executionContext != null) | ||
| { | ||
|
|
@@ -4073,6 +4071,43 @@ internal static IEnumerable<CompletionResult> CompleteFilename(CompletionContext | |
| executionContext.SessionState.Drive.GetAtScope(wordToComplete.Substring(0, 1), "global"); | ||
| } | ||
|
|
||
| #if UNIX | ||
| // We use globbing to get completions. | ||
| // Globbing is based on system functions which is case-sensitive on Unix. | ||
| // To make globbing on Unix case-insensitive we transform every char from the input string: | ||
| // "alias:dir" -> "alias:[dD][iI][rR]" | ||
| if (context.GetOption("UnixCaseInsensitiveGlobbing", @default: true) == true) | ||
| { | ||
| StringBuilder sb = new StringBuilder(wordToComplete.Length * 4); | ||
|
|
||
| //wordToComplete = WildcardPattern.Escape(wordToComplete, Utils.Separators.Brackets); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove, but it might be worth a comment saying we assume
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. Previous I removed there
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The word to complete is usually a wildcard pattern (unless the filename specifies So I think it's probably best to not escape anything. It is probably uncommon to use tab completion and square brackets together, but I'm sure I've done it before, so it might feel broken. This does mean you need to detect letters inside square brackets and ignore those.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we leave as is now and add escape StarOrQuestion for useLiteralPath when we'll need?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, except I missed the code above that already does that, except you changed it. In reply to: 136893930 [](ancestors = 136893930) |
||
| // Copy a provider name "as is" without transformation. | ||
| var providerPrefixIndex = wordToComplete.IndexOf(':') + 1; | ||
| int i; | ||
| for (i=0; i < providerPrefixIndex; i++) | ||
| { | ||
| sb.Append(wordToComplete[i]); | ||
| } | ||
|
|
||
| // Transform rest of input string after a provider prefix. | ||
| for (var j=i; j < wordToComplete.Length; j++) | ||
| { | ||
| var lch = Char.ToLower(wordToComplete[j]); | ||
| var uch = Char.ToUpper(wordToComplete[j]); | ||
| if (lch != uch) | ||
| { | ||
| sb.Append('[').Append(lch).Append(uch).Append(']'); | ||
| } | ||
| else | ||
| { | ||
| sb.Append(wordToComplete[j]); | ||
| } | ||
| } | ||
|
|
||
| wordToComplete = sb.ToString(); | ||
| } | ||
| #endif | ||
| var powerShellExecutionHelper = context.Helper; | ||
| powerShellExecutionHelper | ||
| .AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Resolve-Path") | ||
|
|
@@ -4215,7 +4250,7 @@ internal static IEnumerable<CompletionResult> CompleteFilename(CompletionContext | |
| } | ||
|
|
||
| // Sorting the results by the path | ||
| var sortedPsobjs = psobjs.OrderBy(a => a, new ItemPathComparer()); | ||
| var sortedPsobjs = psobjs.OrderBy(a => a, new ItemPathComparer(wordToComplete)); | ||
|
|
||
| foreach (PSObject psobj in sortedPsobjs) | ||
| { | ||
|
|
@@ -6637,6 +6672,13 @@ internal static bool IsAmpersandNeeded(CompletionContext context, bool defaultCh | |
|
|
||
| private class ItemPathComparer : IComparer<PSObject> | ||
| { | ||
| private String _baseWord; | ||
|
|
||
| public ItemPathComparer(String baseWord) | ||
| { | ||
| _baseWord = baseWord + ".*"; | ||
| } | ||
|
|
||
| public int Compare(PSObject x, PSObject y) | ||
| { | ||
| var xPathInfo = PSObject.Base(x) as PathInfo; | ||
|
|
@@ -6666,7 +6708,21 @@ public int Compare(PSObject x, PSObject y) | |
| if (string.IsNullOrEmpty(xPath) || string.IsNullOrEmpty(yPath)) | ||
| Diagnostics.Assert(false, "Base object of item PSObject should be either PathInfo or FileSystemInfo"); | ||
|
|
||
| return String.Compare(xPath, yPath, StringComparison.CurrentCultureIgnoreCase); | ||
| var result = String.Compare(xPath, yPath, StringComparison.CurrentCultureIgnoreCase); | ||
| #if UNIX | ||
| if (result == 0) | ||
| { | ||
| if (Regex.IsMatch(xPath, _baseWord)) | ||
| { | ||
| return -1; | ||
| } | ||
| else | ||
| { | ||
| return 1; | ||
| } | ||
| } | ||
| #endif | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the code was correct before - only when we know we have -LiteralPath.