Skip to content

Commit 59d4285

Browse files
committed
fix(search): preserve infinite range overlap semantics
1 parent cdeeb43 commit 59d4285

2 files changed

Lines changed: 39 additions & 13 deletions

File tree

apps/sim/lib/workflows/search-replace/resources/resolvers.test.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,17 @@ describe('workflowSearchMatchMatchesQuery', () => {
218218
// the oracle has to defend inverted, empty and non-finite ends as well.
219219
const degenerate = random()
220220
const end =
221-
degenerate > 0.97
221+
degenerate > 0.98
222222
? Number.NaN
223-
: degenerate > 0.94
224-
? start - 1 - Math.floor(random() * 3)
225-
: degenerate > 0.91
226-
? start
227-
: start + 1 + Math.floor(random() * 8)
223+
: degenerate > 0.96
224+
? Number.POSITIVE_INFINITY
225+
: degenerate > 0.94
226+
? Number.NEGATIVE_INFINITY
227+
: degenerate > 0.91
228+
? start - 1 - Math.floor(random() * 3)
229+
: degenerate > 0.88
230+
? start
231+
: start + 1 + Math.floor(random() * 8)
228232
const isSubBlockTarget = random() > 0.15
229233
return createMatch({
230234
id: `m-${index}`,
@@ -296,6 +300,28 @@ describe('workflowSearchMatchMatchesQuery', () => {
296300
expect(dedupeOverlappingWorkflowSearchMatches(matches)).toHaveLength(1)
297301
})
298302

303+
it('agrees when a positive-infinity range contains a later range', () => {
304+
const matches = [
305+
createMatch({
306+
id: 'unbounded',
307+
kind: 'workflow-reference',
308+
range: { start: 0, end: Number.POSITIVE_INFINITY },
309+
}),
310+
createMatch({
311+
id: 'inside',
312+
kind: 'text',
313+
range: { start: 1, end: 2 },
314+
}),
315+
]
316+
317+
expect(dedupeOverlappingWorkflowSearchMatches(matches).map((match) => match.id)).toEqual(
318+
referenceDedupe(matches).map((match) => match.id)
319+
)
320+
expect(dedupeOverlappingWorkflowSearchMatches(matches).map((match) => match.id)).toEqual([
321+
'inside',
322+
])
323+
})
324+
299325
it.each([0, 1])('agrees on a %i-element input', (count) => {
300326
const matches = randomMatches(5, count)
301327

apps/sim/lib/workflows/search-replace/resources/resolvers.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ interface RangeMatchScopeBucket {
153153
}
154154

155155
function widenScopeBucket(bucket: RangeMatchScopeBucket, end: number): void {
156-
if (Number.isFinite(end)) bucket.maxEnd = Math.max(bucket.maxEnd, end)
156+
if (!Number.isNaN(end)) bucket.maxEnd = Math.max(bucket.maxEnd, end)
157157
}
158158

159159
/**
@@ -190,11 +190,11 @@ function widenScopeBucket(bucket: RangeMatchScopeBucket, end: number): void {
190190
* can still end further right than the one it evicts. Leaving `maxEnd` stale
191191
* there let the short-circuit skip genuine overlaps and leak duplicates.
192192
*
193-
* Only finite ends widen it. `Math.max` with a non-finite end would pin
194-
* `maxEnd` at `NaN`, and since every comparison against `NaN` is false that
195-
* would silently switch dedupe off for the rest of the scope. A non-finite
196-
* range cannot overlap anything anyway - `rangesOverlap` is false for it - so
197-
* skipping the widening matches what the unbucketed scan did.
193+
* Only `NaN` ends are ignored. `Math.max` with `NaN` would pin `maxEnd` at
194+
* `NaN`, and since every comparison against `NaN` is false that would silently
195+
* switch dedupe off for the rest of the scope. A `NaN`-ended range cannot
196+
* overlap anything anyway, while positive infinity is an unbounded end that
197+
* can overlap later ranges and therefore must widen the high-water mark.
198198
*/
199199
export function dedupeOverlappingWorkflowSearchMatches<T extends WorkflowSearchMatch>(
200200
matches: T[]
@@ -226,7 +226,7 @@ export function dedupeOverlappingWorkflowSearchMatches<T extends WorkflowSearchM
226226
} else {
227227
bucketsByScopeKey.set(scopeKey, {
228228
indices: [deduped.length],
229-
maxEnd: Number.isFinite(matchRange.end) ? matchRange.end : Number.NEGATIVE_INFINITY,
229+
maxEnd: Number.isNaN(matchRange.end) ? Number.NEGATIVE_INFINITY : matchRange.end,
230230
})
231231
}
232232
}

0 commit comments

Comments
 (0)