fix(parameterization): word-boundary check prevents over-eager value match
ParameterizationPreview.tokenize() matched highlight values via raw seg.text.startsWith(value, cursor) with no word-boundary check and no minimum length. A param value like "D" (e.g. a drive letter) lit up every capital D in the script body — Get-ADUser, Add-Type, Disable- all rendered as proposed-parameter pills. Add a word-boundary guard: a candidate match is only accepted if either side of the match either falls at start/end of the segment, OR the adjacent character is non-alphanumeric. The guard is conditional on whether the value itself starts/ends with a word char, so values that begin or end in punctuation (e.g. "D:\\Folder") still match cleanly when they sit next to whitespace or punctuation. Surfaced 2026-05-01 while testing the suggested-fix flow with a real PowerShell script. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -80,10 +80,27 @@ function tokenize(body: string, highlightValues: Record<string, string> | undefi
|
|||||||
while (cursor < seg.text.length) {
|
while (cursor < seg.text.length) {
|
||||||
let matched: { key: string; value: string } | null = null
|
let matched: { key: string; value: string } | null = null
|
||||||
for (const [key, value] of valueEntries) {
|
for (const [key, value] of valueEntries) {
|
||||||
if (seg.text.startsWith(value, cursor)) {
|
if (!seg.text.startsWith(value, cursor)) continue
|
||||||
matched = { key, value }
|
// Word-boundary guard: a single-char value like "D" (drive letter)
|
||||||
break
|
// would otherwise light up every capital D in identifiers like
|
||||||
}
|
// `Get-ADUser`. We only require a boundary on a side of the value
|
||||||
|
// that itself starts/ends with a word char, so values that begin or
|
||||||
|
// end in punctuation (e.g. "D:\\Folder") still match cleanly.
|
||||||
|
const valueStartsWithWordChar = /^\w/.test(value)
|
||||||
|
const valueEndsWithWordChar = /\w$/.test(value)
|
||||||
|
const before = cursor > 0 ? seg.text[cursor - 1] : undefined
|
||||||
|
const after = cursor + value.length < seg.text.length
|
||||||
|
? seg.text[cursor + value.length]
|
||||||
|
: undefined
|
||||||
|
const startBounded = !valueStartsWithWordChar
|
||||||
|
|| before === undefined
|
||||||
|
|| !/\w/.test(before)
|
||||||
|
const endBounded = !valueEndsWithWordChar
|
||||||
|
|| after === undefined
|
||||||
|
|| !/\w/.test(after)
|
||||||
|
if (!startBounded || !endBounded) continue
|
||||||
|
matched = { key, value }
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (matched) {
|
if (matched) {
|
||||||
flushPending()
|
flushPending()
|
||||||
|
|||||||
Reference in New Issue
Block a user