-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Add single/double quote support for Join-String Argument Completer
#25283
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 14 commits
99b1ff5
c22bcdd
d4a5a7d
401ac49
51d9437
20ccaa7
ffae7ef
8d556b4
163a6ec
09818bb
e8fe981
d57f8c8
33ca0a7
3258b66
f159a33
99f2e3a
5a2ab03
de63820
38bc0a7
231f7bc
8066ef5
cd0b123
f3ec9f9
f4bfa8e
139a778
bcc9af4
e7153fa
0022a79
89da820
7ea1680
6156af3
0a853cf
babbde7
002f46a
542a28a
7125ec9
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 |
|---|---|---|
|
|
@@ -21,36 +21,66 @@ internal static class CompletionHelpers | |
| /// <param name="wordToComplete">The word to complete.</param> | ||
| /// <param name="possibleCompletionValues">The possible completion values to iterate.</param> | ||
| /// <param name="toolTipMapping">The optional tool tip mapping delegate.</param> | ||
| /// <param name="listItemTextMapping">The optional list item text mapping delegate.</param> | ||
| /// <param name="resultType">The optional completion result type. Default is Text.</param> | ||
| /// <returns></returns> | ||
| /// <param name="escapeStrategy"> | ||
| /// A delegate that specifies how to escape the word to complete. If null, no escaping is applied. | ||
| /// </param> | ||
| /// <returns>List of matching completion results.</returns> | ||
| internal static IEnumerable<CompletionResult> GetMatchingResults( | ||
| string wordToComplete, | ||
| IEnumerable<string> possibleCompletionValues, | ||
| Func<string, string> toolTipMapping = null, | ||
| CompletionResultType resultType = CompletionResultType.Text) | ||
| Func<string, string> listItemTextMapping = null, | ||
|
Collaborator
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. Perhaps convert these
Contributor
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. Yep agree it should all be consolidated to delegates. |
||
| CompletionResultType resultType = CompletionResultType.Text, | ||
| Func<string, string> escapeStrategy = null) | ||
ArmaanMcleod marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| string quote = HandleDoubleAndSingleQuote(ref wordToComplete); | ||
| var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); | ||
|
|
||
| foreach (string value in possibleCompletionValues) | ||
| { | ||
| if (pattern.IsMatch(value)) | ||
| if (IsMatch(value, wordToComplete, escapeStrategy)) | ||
| { | ||
| string completionText = QuoteCompletionText(value, quote); | ||
| string toolTip = toolTipMapping?.Invoke(value) ?? value; | ||
| string listItemText = listItemTextMapping?.Invoke(value) ?? value; | ||
|
Collaborator
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. We should do the both mappings in one method. If this requires many changes in other completers it is better to implement this in follow PR.
Contributor
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. Agree. I am actually thinking of just changing the type from
Collaborator
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. It could be
Contributor
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. Yep sounds good. Will postpone this. I'd like to just get IsMatch and ListItemTextMapping in interface in next PR.
Collaborator
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.
|
||
|
|
||
| string listItemText = value; | ||
|
|
||
| yield return new CompletionResult( | ||
| completionText, | ||
| listItemText, | ||
| resultType, | ||
| toolTip: toolTipMapping is null | ||
| ? listItemText | ||
| : toolTipMapping(value)); | ||
| yield return new CompletionResult(completionText, listItemText, resultType, toolTip); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Represents a method that checks whether a value matches a word or pattern. | ||
| /// </summary> | ||
| /// <param name="value">The input string to evaluate for a match.</param> | ||
| /// <param name="wordToComplete">The word or pattern to compare against.</param> | ||
| /// <param name="escapeStrategy"> | ||
| /// A delegate that specifies how to escape the word to complete. If null, no escaping is applied. | ||
| /// </param> | ||
| /// <returns> | ||
| /// <c>true</c> if the value matches the word (case-insensitively) or the wildcard pattern; otherwise, <c>false</c>. | ||
| /// </returns> | ||
| internal delegate bool MatchDelegate(string value, string wordToComplete, Func<string, string> escapeStrategy = null); | ||
|
|
||
| internal static readonly MatchDelegate IsMatch = (value, wordToComplete, escapeStrategy) => | ||
| { | ||
| string normalizedWord = WildcardPattern.Unescape(wordToComplete.ReplaceLineEndings("`")); | ||
|
|
||
| bool match = value.StartsWith(normalizedWord, StringComparison.OrdinalIgnoreCase); | ||
|
|
||
| if (!match) | ||
| { | ||
| string escapedWord = escapeStrategy?.Invoke(wordToComplete) ?? wordToComplete; | ||
|
|
||
| match = WildcardPattern | ||
| .Get(escapedWord + "*", WildcardOptions.IgnoreCase) | ||
| .IsMatch(value); | ||
| } | ||
ArmaanMcleod marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return match; | ||
| }; | ||
|
|
||
| /// <summary> | ||
| /// Removes wrapping quotes from a string and returns the quote used, if present. | ||
| /// </summary> | ||
|
|
@@ -105,7 +135,7 @@ internal static string HandleDoubleAndSingleQuote(ref string wordToComplete) | |
| return quoteInUse; | ||
| } | ||
|
|
||
| bool hasFrontQuoteAndNoBackQuote = | ||
| bool hasFrontQuoteAndNoBackQuote = | ||
| (hasFrontSingleQuote || hasFrontDoubleQuote) && !hasBackSingleQuote && !hasBackDoubleQuote; | ||
|
|
||
| if (hasFrontQuoteAndNoBackQuote) | ||
|
|
@@ -124,6 +154,7 @@ internal static string HandleDoubleAndSingleQuote(ref string wordToComplete) | |
| /// <item><description>There are parsing errors in the input string.</description></item> | ||
| /// <item><description>The parsed token count is not exactly two (the input token + EOF).</description></item> | ||
| /// <item><description>The first token is a string or a PowerShell keyword containing special characters.</description></item> | ||
| /// <item><description>The first token is a semi colon or comma token.</description></item> | ||
| /// </list> | ||
| /// </summary> | ||
| /// <param name="completion">The input string to analyze for quoting requirements.</param> | ||
|
|
@@ -139,15 +170,25 @@ internal static bool CompletionRequiresQuotes(string completion) | |
| Token firstToken = tokens[0]; | ||
| bool isStringToken = firstToken is StringToken; | ||
| bool isKeywordToken = (firstToken.TokenFlags & TokenFlags.Keyword) != 0; | ||
| bool isSemiToken = firstToken.Kind == TokenKind.Semi; | ||
| bool isCommaToken = firstToken.Kind == TokenKind.Comma; | ||
|
|
||
| if ((!requireQuote && isStringToken) || (isExpectedTokenCount && isKeywordToken)) | ||
| { | ||
| requireQuote = ContainsCharsToCheck(firstToken.Text); | ||
| } | ||
|
|
||
| else if (isExpectedTokenCount && (isSemiToken || isCommaToken)) | ||
| { | ||
| requireQuote = true; | ||
| } | ||
|
|
||
| return requireQuote; | ||
| } | ||
|
|
||
| private static bool ContainsEscapedNewlineString(string text) | ||
| => text.Contains("`n", StringComparison.Ordinal); | ||
ArmaanMcleod marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private static bool ContainsCharsToCheck(ReadOnlySpan<char> text) | ||
| => text.ContainsAny(s_defaultCharsToCheck); | ||
|
|
||
|
|
@@ -165,6 +206,12 @@ private static bool ContainsCharsToCheck(ReadOnlySpan<char> text) | |
| /// </returns> | ||
| internal static string QuoteCompletionText(string completionText, string quote) | ||
| { | ||
| // Escaped newlines e.g. `r`n need be surrounded with double quotes | ||
| if (ContainsEscapedNewlineString(completionText)) | ||
| { | ||
| return "\"" + completionText + "\""; | ||
| } | ||
|
|
||
| if (!CompletionRequiresQuotes(completionText)) | ||
| { | ||
| return quote + completionText + quote; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.