Add QuoteCompletionText method to CompletionHelpers class (#25180)

Unifies how quoting a given completion text works.
This commit is contained in:
Armaan Mcleod
2025-03-22 18:04:54 +05:00
committed by GitHub
parent d76e40df10
commit 94d0361d59
3 changed files with 106 additions and 140 deletions
@@ -524,17 +524,7 @@ namespace System.Management.Automation
+ moduleInfo.ModuleType.ToString() + "\r\nPath: "
+ moduleInfo.Path;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, toolTip));
}
@@ -1844,23 +1834,8 @@ namespace System.Management.Automation
{
string realEntry = entry;
string completionText = entry;
if (quote == string.Empty)
{
if (CompletionHelpers.CompletionRequiresQuotes(entry))
{
realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry);
completionText = "'" + realEntry + "'";
}
}
else
{
if (quote.Equals("'", StringComparison.OrdinalIgnoreCase))
{
realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry);
}
completionText = quote + realEntry + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, entry, CompletionResultType.ParameterValue, entry));
}
@@ -3220,17 +3195,7 @@ namespace System.Management.Automation
var completionText = eventLog.Log.ToString();
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
if (pattern.IsMatch(listItemText))
{
@@ -3311,17 +3276,7 @@ namespace System.Management.Automation
var completionText = psJob.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3386,17 +3341,7 @@ namespace System.Management.Automation
var completionText = psJob.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3526,17 +3471,7 @@ namespace System.Management.Automation
continue;
uniqueSet.Add(completionText);
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
// on macOS, system processes names will be empty if PowerShell isn't run as `sudo`
if (string.IsNullOrEmpty(listItemText))
@@ -3579,17 +3514,7 @@ namespace System.Management.Automation
var completionText = providerInfo.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3627,17 +3552,7 @@ namespace System.Management.Automation
var completionText = driveInfo.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3678,17 +3593,7 @@ namespace System.Management.Automation
var completionText = serviceInfo.DisplayName;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3709,17 +3614,7 @@ namespace System.Management.Automation
var completionText = serviceInfo.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3815,17 +3710,7 @@ namespace System.Management.Automation
var completionText = aliasInfo.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -3878,17 +3763,7 @@ namespace System.Management.Automation
var completionText = trace.Name;
var listItemText = completionText;
if (CompletionHelpers.CompletionRequiresQuotes(completionText))
{
var quoteInUse = quote == string.Empty ? "'" : quote;
if (quoteInUse == "'")
completionText = completionText.Replace("'", "''");
completionText = quoteInUse + completionText + quoteInUse;
}
else
{
completionText = quote + completionText + quote;
}
completionText = CompletionHelpers.QuoteCompletionText(completionText, quote);
result.Add(new CompletionResult(completionText, listItemText, CompletionResultType.ParameterValue, listItemText));
}
@@ -36,9 +36,7 @@ namespace System.Management.Automation
{
if (pattern.IsMatch(value))
{
string completionText = quote == string.Empty
? value
: quote + value + quote;
string completionText = QuoteCompletionText(value, quote);
string listItemText = value;
@@ -140,5 +138,34 @@ namespace System.Management.Automation
private static bool ContainsCharsToCheck(ReadOnlySpan<char> text)
=> text.ContainsAny(s_defaultCharsToCheck);
/// <summary>
/// Quotes a given completion text.
/// </summary>
/// <param name="completionText">
/// The text to be quoted.
/// </param>
/// <param name="quote">
/// The quote character to use for enclosing the text. Defaults to a single quote if not provided.
/// </param>
/// <returns>
/// The quoted <paramref name="completionText"/>.
/// </returns>
internal static string QuoteCompletionText(string completionText, string quote)
{
if (!CompletionRequiresQuotes(completionText))
{
return quote + completionText + quote;
}
string quoteInUse = string.IsNullOrEmpty(quote) ? "'" : quote;
if (quoteInUse == "'")
{
completionText = CodeGeneration.EscapeSingleQuotedStringContent(completionText);
}
return quoteInUse + completionText + quoteInUse;
}
}
}
@@ -8,6 +8,70 @@ namespace PSTests.Parallel
{
public class CompletionHelpersTests
{
[Theory]
[InlineData("", "'", "''")]
[InlineData("", "\"", "\"\"")]
[InlineData("'", "'", "''''")]
[InlineData("", "", "''")]
[InlineData("", null, "''")]
[InlineData("word", "'", "'word'")]
[InlineData("word", "\"", "\"word\"")]
[InlineData("word's", "'", "'word''s'")]
[InlineData("already 'quoted'", "'", "'already ''quoted'''")]
[InlineData("multiple 'quotes' in 'text'", "'", "'multiple ''quotes'' in ''text'''")]
[InlineData("\"word\"", "'", "'\"word\"'")]
[InlineData("'word'", "'", "''word''")]
[InlineData("word", "", "word")]
[InlineData("word", null, "word")]
[InlineData("\"word\"", "\"", "\"\"word\"\"")]
[InlineData("'word'", "\"", "\"'word'\"")]
[InlineData("word with space", "'", "'word with space'")]
[InlineData("word with space", "\"", "\"word with space\"")]
[InlineData("word\"with\"quotes", "'", "'word\"with\"quotes'")]
[InlineData("word'with'quotes", "\"", "\"word'with'quotes\"")]
[InlineData("while", "'", "'while'")]
[InlineData("while", "", "while")]
[InlineData("while", "\"", "\"while\"")]
[InlineData("$variable", "'", "'$variable'")]
[InlineData("$variable", "", "$variable")]
[InlineData("$variable", "\"", "\"$variable\"")]
[InlineData("key$word", "'", "'key$word'")]
[InlineData("key$word", "", "'key$word'")]
[InlineData("key$word", "\"", "\"key$word\"")]
public void TestQuoteCompletionText(
string completionText,
string quote,
string expected)
{
string result = CompletionHelpers.QuoteCompletionText(completionText, quote);
Assert.Equal(expected, result);
}
[Theory]
[InlineData("normaltext", false)]
[InlineData("$variable", false)]
[InlineData("abc def", true)]
[InlineData("while", false)] // PowerShell keyword
[InlineData("key$word", true)]
[InlineData("abc`def", true)]
[InlineData("normal-text", false)]
[InlineData("\"doublequotes\"", false)]
[InlineData("'singlequotes'", false)]
[InlineData("normal 'text'", true)]
[InlineData("normal \"text\"", true)]
[InlineData("text with ' and \"", true)]
[InlineData("text\"with\"quotes", false)]
[InlineData("text'with'quotes", false)]
[InlineData("\"key$", true)]
[InlineData("\"", true)]
[InlineData("'", true)]
[InlineData("", true)]
public void TestCompletionRequiresQuotes(string completion, bool expected)
{
bool result = CompletionHelpers.CompletionRequiresQuotes(completion);
Assert.Equal(expected, result);
}
[Theory]
[InlineData("", "", "")]
[InlineData("\"", "", "\"")]