@optique/core 1.0.15-dev.2835 → 1.0.15-dev.2849
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/completion.cjs +84 -16
- package/dist/completion.js +84 -16
- package/package.json +1 -1
package/dist/completion.cjs
CHANGED
|
@@ -815,10 +815,13 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
815
815
|
local -a matches
|
|
816
816
|
local -i i
|
|
817
817
|
for (( i=1; i <= \${#completions[@]}; i++ )); do
|
|
818
|
+
# _describe treats colons as separators and backslashes as escapes.
|
|
819
|
+
value="\${completions[i]//\\\\/\\\\\\\\}"
|
|
820
|
+
value="\${value//:/\\\\:}"
|
|
818
821
|
if [[ -n "\${descriptions[i]}" ]]; then
|
|
819
|
-
matches+=("\${
|
|
822
|
+
matches+=("\${value}:\${descriptions[i]}")
|
|
820
823
|
else
|
|
821
|
-
matches+=("\${
|
|
824
|
+
matches+=("\${value}")
|
|
822
825
|
fi
|
|
823
826
|
done
|
|
824
827
|
_describe 'commands' matches
|
|
@@ -1730,12 +1733,33 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1730
1733
|
# Extract arguments from AST (handles quoted strings properly)
|
|
1731
1734
|
\$arguments = @()
|
|
1732
1735
|
\$commandElements = \$commandAst.CommandElements
|
|
1736
|
+
\$valuePrefix = \$wordToComplete
|
|
1737
|
+
\$quoteType = \$null
|
|
1733
1738
|
|
|
1734
1739
|
# Keep only complete arguments before the cursor. The current word
|
|
1735
1740
|
# (including a token ending exactly at the cursor) is appended below.
|
|
1736
1741
|
for (\$i = 1; \$i -lt \$commandElements.Count; \$i++) {
|
|
1737
1742
|
\$element = \$commandElements[\$i]
|
|
1738
|
-
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1743
|
+
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1744
|
+
# wordToComplete has synthesized delimiters and decoded escapes.
|
|
1745
|
+
# Only the AST retains the literal value and original quote type.
|
|
1746
|
+
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst] -and
|
|
1747
|
+
\$element.Extent.EndOffset -eq \$cursorPosition) {
|
|
1748
|
+
if (\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')) {
|
|
1749
|
+
\$valuePrefix = \$element.Value
|
|
1750
|
+
\$quoteType = \$element.StringConstantType
|
|
1751
|
+
} elseif (\$element.Extent.Text -match "^[^=]+=['\\u2018-\\u201b]") {
|
|
1752
|
+
# Joined values are BareWord constants despite their quotes.
|
|
1753
|
+
# Quote the entire replacement to keep it one argument.
|
|
1754
|
+
\$valuePrefix = \$element.Value
|
|
1755
|
+
\$quoteType = 'SingleQuoted'
|
|
1756
|
+
} elseif (\$element.Extent.Text -match '^[^=]+=["\\u201c-\\u201e]') {
|
|
1757
|
+
\$valuePrefix = \$element.Value
|
|
1758
|
+
\$quoteType = 'DoubleQuoted'
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
break
|
|
1762
|
+
}
|
|
1739
1763
|
|
|
1740
1764
|
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
|
|
1741
1765
|
\$arguments += \$element.Value
|
|
@@ -1744,21 +1768,37 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1744
1768
|
}
|
|
1745
1769
|
}
|
|
1746
1770
|
|
|
1771
|
+
# CompletionText replaces the whole token, including its opening quote.
|
|
1772
|
+
# Escape literals without evaluating variables or expressions.
|
|
1773
|
+
\$quoteCompletion = {
|
|
1774
|
+
param(\$text)
|
|
1775
|
+
if (\$quoteType -eq 'SingleQuoted') {
|
|
1776
|
+
"'" + (\$text -replace "['\\u2018-\\u201b]", '\$0\$0') + "'"
|
|
1777
|
+
} elseif (\$quoteType -eq 'DoubleQuoted') {
|
|
1778
|
+
'"' + (\$text -replace '[\\x60\$"\\u201c-\\u201e]', '\`\$0') + '"'
|
|
1779
|
+
} else {
|
|
1780
|
+
\$text
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1747
1784
|
# Build arguments array for completion command
|
|
1748
1785
|
\$completionArgs = @()
|
|
1749
1786
|
${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
1750
1787
|
` : ""} \$completionArgs += \$arguments
|
|
1751
|
-
\$completionArgs += \$wordToComplete
|
|
1752
1788
|
|
|
1753
1789
|
# Call completion command and capture output
|
|
1754
1790
|
try {
|
|
1755
|
-
|
|
1791
|
+
# Quote the prefix separately: array arguments expand a literal tilde
|
|
1792
|
+
# when passed to native programs on Unix.
|
|
1793
|
+
\$output = & ${programName} \$completionArgs "\$valuePrefix" 2>\$null
|
|
1756
1794
|
if (-not \$output) { return }
|
|
1757
1795
|
|
|
1758
1796
|
# Parse tab-separated output and create CompletionResult objects
|
|
1759
1797
|
\$output -split "\`n" | ForEach-Object {
|
|
1760
1798
|
\$literal = \$_.StartsWith('__LITERAL__:', [System.StringComparison]::Ordinal)
|
|
1761
|
-
|
|
1799
|
+
# Quoted values preserve whitespace; only remove the CR of CRLF.
|
|
1800
|
+
# Keep the existing trimming for unquoted completions.
|
|
1801
|
+
\$line = if (\$null -ne \$quoteType) { \$_.TrimEnd([char]13) } else { \$_.Trim() }
|
|
1762
1802
|
if (-not \$line) { return }
|
|
1763
1803
|
if (\$literal) { \$line = \$line.Substring(12) }
|
|
1764
1804
|
|
|
@@ -1778,7 +1818,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1778
1818
|
# comparing so that Windows backslashes match forward slashes
|
|
1779
1819
|
# in the transported pattern.
|
|
1780
1820
|
\$normalizedPattern = if (\$pattern) { \$pattern.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1781
|
-
\$normalizedWord = if (\$
|
|
1821
|
+
\$normalizedWord = if (\$valuePrefix) { \$valuePrefix.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1782
1822
|
\$comparison = if (\$IsWindows) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
1783
1823
|
# For wildcard patterns, compare only the directory prefix
|
|
1784
1824
|
\$comparePattern = \$normalizedPattern
|
|
@@ -1790,12 +1830,25 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1790
1830
|
\$comparePattern = ''
|
|
1791
1831
|
}
|
|
1792
1832
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1833
|
+
# A quoted home-pattern directory inserts an absolute path.
|
|
1834
|
+
# Compare subsequent input against the same expanded prefix.
|
|
1835
|
+
# Detect home syntax before removing ./, which can target a
|
|
1836
|
+
# literal local tilde directory rather than the home directory.
|
|
1837
|
+
\$homePattern = \$pattern -and (\$pattern -eq '~' -or \$pattern.Replace('\\', '/').StartsWith('~/'))
|
|
1838
|
+
if (\$homePattern) {
|
|
1839
|
+
# Preserve the home root when the pattern is exactly ~.
|
|
1840
|
+
\$expandedPattern = if (\$comparePattern -eq '~') { \$HOME } else { \$HOME.Replace('\\', '/').TrimEnd('/') + \$comparePattern.Substring(1) }
|
|
1841
|
+
\$expandedPattern = [IO.Path]::GetFullPath(\$expandedPattern).Replace('\\', '/')
|
|
1842
|
+
if (\$normalizedWord.StartsWith(\$expandedPattern, \$comparison)) {
|
|
1843
|
+
\$comparePattern = \$expandedPattern
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$valuePrefix -ne \$pattern) {
|
|
1847
|
+
\$valuePrefix
|
|
1795
1848
|
} elseif (\$pattern) {
|
|
1796
1849
|
\$pattern
|
|
1797
|
-
} elseif (\$
|
|
1798
|
-
\$
|
|
1850
|
+
} elseif (\$valuePrefix) {
|
|
1851
|
+
\$valuePrefix
|
|
1799
1852
|
} else { '' }
|
|
1800
1853
|
|
|
1801
1854
|
# Use -Force to include hidden files when requested, or when
|
|
@@ -1803,15 +1856,22 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1803
1856
|
\$prefixBasename = if (\$prefix) { Split-Path -Leaf \$prefix } else { '' }
|
|
1804
1857
|
\$forceParam = if (\$hidden -or (\$prefixBasename -and \$prefixBasename.StartsWith('.'))) { @{Force = \$true} } else { @{} }
|
|
1805
1858
|
|
|
1859
|
+
# A quoted tilde is literal when passed to a native command.
|
|
1860
|
+
# Prevent PowerShell's file provider from expanding it to home.
|
|
1861
|
+
# Parser-provided home patterns still need home expansion.
|
|
1862
|
+
\$prefixFromInput = \$valuePrefix -and \$prefix -ceq \$valuePrefix
|
|
1863
|
+
\$lookupPrefix = if (\$prefixFromInput -and \$null -ne \$quoteType -and \$prefix.StartsWith('~')) { './' + \$prefix } else { \$prefix }
|
|
1864
|
+
|
|
1806
1865
|
# If prefix is a directory without trailing slash, append
|
|
1807
1866
|
# one so Get-ChildItem lists its contents
|
|
1808
|
-
if (\$prefix -and (Test-Path -Path \$
|
|
1867
|
+
if (\$prefix -and (Test-Path -Path \$lookupPrefix -PathType Container) -and -not \$prefix.EndsWith('/') -and -not \$prefix.EndsWith('\\')) {
|
|
1809
1868
|
\$prefix = \$prefix + '/'
|
|
1869
|
+
\$lookupPrefix = \$lookupPrefix + '/'
|
|
1810
1870
|
}
|
|
1811
1871
|
|
|
1812
1872
|
# Build the glob path — when the prefix contains wildcard
|
|
1813
1873
|
# characters, use it as-is; otherwise append *
|
|
1814
|
-
\$globPath = if (\$
|
|
1874
|
+
\$globPath = if (\$lookupPrefix -match '[\*\?]') { \$lookupPrefix } else { "\${lookupPrefix}*" }
|
|
1815
1875
|
|
|
1816
1876
|
# Get file system items based on type
|
|
1817
1877
|
\$items = @()
|
|
@@ -1848,7 +1908,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1848
1908
|
# For file/any modes with glob patterns, also add directories
|
|
1849
1909
|
# from the base directory for navigation
|
|
1850
1910
|
if (\$prefix -match '[\*\?]' -and \$type -ne 'directory') {
|
|
1851
|
-
\$globDir = Split-Path -Parent \$
|
|
1911
|
+
\$globDir = Split-Path -Parent \$lookupPrefix
|
|
1852
1912
|
if (-not \$globDir) { \$globDir = '.' }
|
|
1853
1913
|
\$extraDirs = Get-ChildItem @forceParam -Directory -Path "\$globDir/*" -ErrorAction SilentlyContinue
|
|
1854
1914
|
if (\$extraDirs) { \$items = @(\$items) + @(\$extraDirs) | Select-Object -Unique }
|
|
@@ -1869,8 +1929,13 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1869
1929
|
\$items | ForEach-Object {
|
|
1870
1930
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1871
1931
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
1932
|
+
# Quoting disables native tilde expansion. A home pattern
|
|
1933
|
+
# supplied by the parser must insert the discovered path.
|
|
1934
|
+
\$replacementText = if (\$null -ne \$quoteType -and -not \$prefixFromInput -and \$homePattern) {
|
|
1935
|
+
if (\$_.PSIsContainer) { \$_.FullName + '/' } else { \$_.FullName }
|
|
1936
|
+
} else { \$completionText }
|
|
1872
1937
|
[System.Management.Automation.CompletionResult]::new(
|
|
1873
|
-
\$
|
|
1938
|
+
(& \$quoteCompletion \$replacementText),
|
|
1874
1939
|
\$completionText,
|
|
1875
1940
|
'ParameterValue',
|
|
1876
1941
|
\$itemType
|
|
@@ -1880,11 +1945,14 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1880
1945
|
# Parse literal completion: text\\tlistItemText\\tdescription
|
|
1881
1946
|
\$parts = \$line -split "\`t", 3
|
|
1882
1947
|
\$completionText = \$parts[0]
|
|
1948
|
+
# Empty display text is invalid for CompletionResult. Keep
|
|
1949
|
+
# ignoring empty suggestions without dropping later records.
|
|
1950
|
+
if (-not \$completionText) { return }
|
|
1883
1951
|
\$listItemText = if (\$parts.Length -gt 1 -and \$parts[1]) { \$parts[1] } else { \$completionText }
|
|
1884
1952
|
\$toolTip = if (\$parts.Length -gt 2 -and \$parts[2]) { \$parts[2] } else { \$completionText }
|
|
1885
1953
|
|
|
1886
1954
|
[System.Management.Automation.CompletionResult]::new(
|
|
1887
|
-
\$completionText,
|
|
1955
|
+
(& \$quoteCompletion \$completionText),
|
|
1888
1956
|
\$listItemText,
|
|
1889
1957
|
'ParameterValue',
|
|
1890
1958
|
\$toolTip
|
package/dist/completion.js
CHANGED
|
@@ -815,10 +815,13 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
815
815
|
local -a matches
|
|
816
816
|
local -i i
|
|
817
817
|
for (( i=1; i <= \${#completions[@]}; i++ )); do
|
|
818
|
+
# _describe treats colons as separators and backslashes as escapes.
|
|
819
|
+
value="\${completions[i]//\\\\/\\\\\\\\}"
|
|
820
|
+
value="\${value//:/\\\\:}"
|
|
818
821
|
if [[ -n "\${descriptions[i]}" ]]; then
|
|
819
|
-
matches+=("\${
|
|
822
|
+
matches+=("\${value}:\${descriptions[i]}")
|
|
820
823
|
else
|
|
821
|
-
matches+=("\${
|
|
824
|
+
matches+=("\${value}")
|
|
822
825
|
fi
|
|
823
826
|
done
|
|
824
827
|
_describe 'commands' matches
|
|
@@ -1730,12 +1733,33 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1730
1733
|
# Extract arguments from AST (handles quoted strings properly)
|
|
1731
1734
|
\$arguments = @()
|
|
1732
1735
|
\$commandElements = \$commandAst.CommandElements
|
|
1736
|
+
\$valuePrefix = \$wordToComplete
|
|
1737
|
+
\$quoteType = \$null
|
|
1733
1738
|
|
|
1734
1739
|
# Keep only complete arguments before the cursor. The current word
|
|
1735
1740
|
# (including a token ending exactly at the cursor) is appended below.
|
|
1736
1741
|
for (\$i = 1; \$i -lt \$commandElements.Count; \$i++) {
|
|
1737
1742
|
\$element = \$commandElements[\$i]
|
|
1738
|
-
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1743
|
+
if (\$element.Extent.EndOffset -ge \$cursorPosition) {
|
|
1744
|
+
# wordToComplete has synthesized delimiters and decoded escapes.
|
|
1745
|
+
# Only the AST retains the literal value and original quote type.
|
|
1746
|
+
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst] -and
|
|
1747
|
+
\$element.Extent.EndOffset -eq \$cursorPosition) {
|
|
1748
|
+
if (\$element.StringConstantType -in @('SingleQuoted', 'DoubleQuoted')) {
|
|
1749
|
+
\$valuePrefix = \$element.Value
|
|
1750
|
+
\$quoteType = \$element.StringConstantType
|
|
1751
|
+
} elseif (\$element.Extent.Text -match "^[^=]+=['\\u2018-\\u201b]") {
|
|
1752
|
+
# Joined values are BareWord constants despite their quotes.
|
|
1753
|
+
# Quote the entire replacement to keep it one argument.
|
|
1754
|
+
\$valuePrefix = \$element.Value
|
|
1755
|
+
\$quoteType = 'SingleQuoted'
|
|
1756
|
+
} elseif (\$element.Extent.Text -match '^[^=]+=["\\u201c-\\u201e]') {
|
|
1757
|
+
\$valuePrefix = \$element.Value
|
|
1758
|
+
\$quoteType = 'DoubleQuoted'
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
break
|
|
1762
|
+
}
|
|
1739
1763
|
|
|
1740
1764
|
if (\$element -is [System.Management.Automation.Language.StringConstantExpressionAst]) {
|
|
1741
1765
|
\$arguments += \$element.Value
|
|
@@ -1744,21 +1768,37 @@ Register-ArgumentCompleter -Native -CommandName ${programName} -ScriptBlock {
|
|
|
1744
1768
|
}
|
|
1745
1769
|
}
|
|
1746
1770
|
|
|
1771
|
+
# CompletionText replaces the whole token, including its opening quote.
|
|
1772
|
+
# Escape literals without evaluating variables or expressions.
|
|
1773
|
+
\$quoteCompletion = {
|
|
1774
|
+
param(\$text)
|
|
1775
|
+
if (\$quoteType -eq 'SingleQuoted') {
|
|
1776
|
+
"'" + (\$text -replace "['\\u2018-\\u201b]", '\$0\$0') + "'"
|
|
1777
|
+
} elseif (\$quoteType -eq 'DoubleQuoted') {
|
|
1778
|
+
'"' + (\$text -replace '[\\x60\$"\\u201c-\\u201e]', '\`\$0') + '"'
|
|
1779
|
+
} else {
|
|
1780
|
+
\$text
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1747
1784
|
# Build arguments array for completion command
|
|
1748
1785
|
\$completionArgs = @()
|
|
1749
1786
|
${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
1750
1787
|
` : ""} \$completionArgs += \$arguments
|
|
1751
|
-
\$completionArgs += \$wordToComplete
|
|
1752
1788
|
|
|
1753
1789
|
# Call completion command and capture output
|
|
1754
1790
|
try {
|
|
1755
|
-
|
|
1791
|
+
# Quote the prefix separately: array arguments expand a literal tilde
|
|
1792
|
+
# when passed to native programs on Unix.
|
|
1793
|
+
\$output = & ${programName} \$completionArgs "\$valuePrefix" 2>\$null
|
|
1756
1794
|
if (-not \$output) { return }
|
|
1757
1795
|
|
|
1758
1796
|
# Parse tab-separated output and create CompletionResult objects
|
|
1759
1797
|
\$output -split "\`n" | ForEach-Object {
|
|
1760
1798
|
\$literal = \$_.StartsWith('__LITERAL__:', [System.StringComparison]::Ordinal)
|
|
1761
|
-
|
|
1799
|
+
# Quoted values preserve whitespace; only remove the CR of CRLF.
|
|
1800
|
+
# Keep the existing trimming for unquoted completions.
|
|
1801
|
+
\$line = if (\$null -ne \$quoteType) { \$_.TrimEnd([char]13) } else { \$_.Trim() }
|
|
1762
1802
|
if (-not \$line) { return }
|
|
1763
1803
|
if (\$literal) { \$line = \$line.Substring(12) }
|
|
1764
1804
|
|
|
@@ -1778,7 +1818,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1778
1818
|
# comparing so that Windows backslashes match forward slashes
|
|
1779
1819
|
# in the transported pattern.
|
|
1780
1820
|
\$normalizedPattern = if (\$pattern) { \$pattern.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1781
|
-
\$normalizedWord = if (\$
|
|
1821
|
+
\$normalizedWord = if (\$valuePrefix) { \$valuePrefix.Replace('\\', '/') -replace '^\\./','' } else { '' }
|
|
1782
1822
|
\$comparison = if (\$IsWindows) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
1783
1823
|
# For wildcard patterns, compare only the directory prefix
|
|
1784
1824
|
\$comparePattern = \$normalizedPattern
|
|
@@ -1790,12 +1830,25 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1790
1830
|
\$comparePattern = ''
|
|
1791
1831
|
}
|
|
1792
1832
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1833
|
+
# A quoted home-pattern directory inserts an absolute path.
|
|
1834
|
+
# Compare subsequent input against the same expanded prefix.
|
|
1835
|
+
# Detect home syntax before removing ./, which can target a
|
|
1836
|
+
# literal local tilde directory rather than the home directory.
|
|
1837
|
+
\$homePattern = \$pattern -and (\$pattern -eq '~' -or \$pattern.Replace('\\', '/').StartsWith('~/'))
|
|
1838
|
+
if (\$homePattern) {
|
|
1839
|
+
# Preserve the home root when the pattern is exactly ~.
|
|
1840
|
+
\$expandedPattern = if (\$comparePattern -eq '~') { \$HOME } else { \$HOME.Replace('\\', '/').TrimEnd('/') + \$comparePattern.Substring(1) }
|
|
1841
|
+
\$expandedPattern = [IO.Path]::GetFullPath(\$expandedPattern).Replace('\\', '/')
|
|
1842
|
+
if (\$normalizedWord.StartsWith(\$expandedPattern, \$comparison)) {
|
|
1843
|
+
\$comparePattern = \$expandedPattern
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
\$prefix = if ((\$comparePattern -or \$normalizedWord) -and \$normalizedWord -and \$normalizedWord.StartsWith(\$comparePattern, \$comparison) -and \$normalizedWord.Length -ge \$comparePattern.Length -and \$valuePrefix -ne \$pattern) {
|
|
1847
|
+
\$valuePrefix
|
|
1795
1848
|
} elseif (\$pattern) {
|
|
1796
1849
|
\$pattern
|
|
1797
|
-
} elseif (\$
|
|
1798
|
-
\$
|
|
1850
|
+
} elseif (\$valuePrefix) {
|
|
1851
|
+
\$valuePrefix
|
|
1799
1852
|
} else { '' }
|
|
1800
1853
|
|
|
1801
1854
|
# Use -Force to include hidden files when requested, or when
|
|
@@ -1803,15 +1856,22 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1803
1856
|
\$prefixBasename = if (\$prefix) { Split-Path -Leaf \$prefix } else { '' }
|
|
1804
1857
|
\$forceParam = if (\$hidden -or (\$prefixBasename -and \$prefixBasename.StartsWith('.'))) { @{Force = \$true} } else { @{} }
|
|
1805
1858
|
|
|
1859
|
+
# A quoted tilde is literal when passed to a native command.
|
|
1860
|
+
# Prevent PowerShell's file provider from expanding it to home.
|
|
1861
|
+
# Parser-provided home patterns still need home expansion.
|
|
1862
|
+
\$prefixFromInput = \$valuePrefix -and \$prefix -ceq \$valuePrefix
|
|
1863
|
+
\$lookupPrefix = if (\$prefixFromInput -and \$null -ne \$quoteType -and \$prefix.StartsWith('~')) { './' + \$prefix } else { \$prefix }
|
|
1864
|
+
|
|
1806
1865
|
# If prefix is a directory without trailing slash, append
|
|
1807
1866
|
# one so Get-ChildItem lists its contents
|
|
1808
|
-
if (\$prefix -and (Test-Path -Path \$
|
|
1867
|
+
if (\$prefix -and (Test-Path -Path \$lookupPrefix -PathType Container) -and -not \$prefix.EndsWith('/') -and -not \$prefix.EndsWith('\\')) {
|
|
1809
1868
|
\$prefix = \$prefix + '/'
|
|
1869
|
+
\$lookupPrefix = \$lookupPrefix + '/'
|
|
1810
1870
|
}
|
|
1811
1871
|
|
|
1812
1872
|
# Build the glob path — when the prefix contains wildcard
|
|
1813
1873
|
# characters, use it as-is; otherwise append *
|
|
1814
|
-
\$globPath = if (\$
|
|
1874
|
+
\$globPath = if (\$lookupPrefix -match '[\*\?]') { \$lookupPrefix } else { "\${lookupPrefix}*" }
|
|
1815
1875
|
|
|
1816
1876
|
# Get file system items based on type
|
|
1817
1877
|
\$items = @()
|
|
@@ -1848,7 +1908,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1848
1908
|
# For file/any modes with glob patterns, also add directories
|
|
1849
1909
|
# from the base directory for navigation
|
|
1850
1910
|
if (\$prefix -match '[\*\?]' -and \$type -ne 'directory') {
|
|
1851
|
-
\$globDir = Split-Path -Parent \$
|
|
1911
|
+
\$globDir = Split-Path -Parent \$lookupPrefix
|
|
1852
1912
|
if (-not \$globDir) { \$globDir = '.' }
|
|
1853
1913
|
\$extraDirs = Get-ChildItem @forceParam -Directory -Path "\$globDir/*" -ErrorAction SilentlyContinue
|
|
1854
1914
|
if (\$extraDirs) { \$items = @(\$items) + @(\$extraDirs) | Select-Object -Unique }
|
|
@@ -1869,8 +1929,13 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1869
1929
|
\$items | ForEach-Object {
|
|
1870
1930
|
\$completionText = if (\$_.PSIsContainer) { "\$dirPrefix\$(\$_.Name)/" } else { "\$dirPrefix\$(\$_.Name)" }
|
|
1871
1931
|
\$itemType = if (\$_.PSIsContainer) { 'Directory' } else { 'File' }
|
|
1932
|
+
# Quoting disables native tilde expansion. A home pattern
|
|
1933
|
+
# supplied by the parser must insert the discovered path.
|
|
1934
|
+
\$replacementText = if (\$null -ne \$quoteType -and -not \$prefixFromInput -and \$homePattern) {
|
|
1935
|
+
if (\$_.PSIsContainer) { \$_.FullName + '/' } else { \$_.FullName }
|
|
1936
|
+
} else { \$completionText }
|
|
1872
1937
|
[System.Management.Automation.CompletionResult]::new(
|
|
1873
|
-
\$
|
|
1938
|
+
(& \$quoteCompletion \$replacementText),
|
|
1874
1939
|
\$completionText,
|
|
1875
1940
|
'ParameterValue',
|
|
1876
1941
|
\$itemType
|
|
@@ -1880,11 +1945,14 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
1880
1945
|
# Parse literal completion: text\\tlistItemText\\tdescription
|
|
1881
1946
|
\$parts = \$line -split "\`t", 3
|
|
1882
1947
|
\$completionText = \$parts[0]
|
|
1948
|
+
# Empty display text is invalid for CompletionResult. Keep
|
|
1949
|
+
# ignoring empty suggestions without dropping later records.
|
|
1950
|
+
if (-not \$completionText) { return }
|
|
1883
1951
|
\$listItemText = if (\$parts.Length -gt 1 -and \$parts[1]) { \$parts[1] } else { \$completionText }
|
|
1884
1952
|
\$toolTip = if (\$parts.Length -gt 2 -and \$parts[2]) { \$parts[2] } else { \$completionText }
|
|
1885
1953
|
|
|
1886
1954
|
[System.Management.Automation.CompletionResult]::new(
|
|
1887
|
-
\$completionText,
|
|
1955
|
+
(& \$quoteCompletion \$completionText),
|
|
1888
1956
|
\$listItemText,
|
|
1889
1957
|
'ParameterValue',
|
|
1890
1958
|
\$toolTip
|