@@ -267,4 +267,248 @@ console.log('test');]]></replace></change>`
267267 expect ( result . suggestions . hasSuggestions ( ) ) . toBe ( true )
268268 } )
269269 } )
270+
271+ describe ( "onDidChangeTextDocument filtering" , ( ) => {
272+ it ( "should process user typing (small changes near cursor)" , async ( ) => {
273+ const initialContent = `console.log('test');`
274+ const { testUri, mockDocument } = await setupTestDocument ( "typing.js" , initialContent )
275+
276+ // Mock active editor with cursor at line 0
277+ ; ( vscode . window as any ) . activeTextEditor = {
278+ document : mockDocument ,
279+ selection : {
280+ active : new vscode . Position ( 0 , 10 ) ,
281+ } ,
282+ }
283+
284+ // Simulate user typing - small change near cursor
285+ const event = {
286+ document : mockDocument ,
287+ contentChanges : [
288+ {
289+ range : new vscode . Range ( new vscode . Position ( 0 , 10 ) , new vscode . Position ( 0 , 10 ) ) ,
290+ rangeLength : 0 ,
291+ text : "a" ,
292+ } ,
293+ ] ,
294+ reason : undefined , // User typing has no reason
295+ }
296+
297+ // This should be processed (not filtered out)
298+ // We can't directly test the private method, but we can verify the logic
299+ expect ( event . reason ) . toBeUndefined ( )
300+ expect ( event . contentChanges . length ) . toBeGreaterThan ( 0 )
301+ expect ( event . contentChanges [ 0 ] . text . length ) . toBeLessThanOrEqual ( 100 )
302+ } )
303+
304+ it ( "should filter out undo operations" , async ( ) => {
305+ const initialContent = `console.log('test');`
306+ const { mockDocument } = await setupTestDocument ( "undo.js" , initialContent )
307+
308+ const event = {
309+ document : mockDocument ,
310+ contentChanges : [
311+ {
312+ range : new vscode . Range ( new vscode . Position ( 0 , 0 ) , new vscode . Position ( 0 , 10 ) ) ,
313+ rangeLength : 10 ,
314+ text : "" ,
315+ } ,
316+ ] ,
317+ reason : 1 , // Undo
318+ }
319+
320+ // Should be filtered out
321+ expect ( event . reason ) . toBe ( 1 )
322+ } )
323+
324+ it ( "should filter out redo operations" , async ( ) => {
325+ const initialContent = `console.log('test');`
326+ const { mockDocument } = await setupTestDocument ( "redo.js" , initialContent )
327+
328+ const event = {
329+ document : mockDocument ,
330+ contentChanges : [
331+ {
332+ range : new vscode . Range ( new vscode . Position ( 0 , 0 ) , new vscode . Position ( 0 , 10 ) ) ,
333+ rangeLength : 0 ,
334+ text : "console.log" ,
335+ } ,
336+ ] ,
337+ reason : 2 , // Redo
338+ }
339+
340+ // Should be filtered out
341+ expect ( event . reason ) . toBe ( 2 )
342+ } )
343+
344+ it ( "should filter out bulk changes (git operations)" , async ( ) => {
345+ const initialContent = `console.log('test');`
346+ const { mockDocument } = await setupTestDocument ( "bulk.js" , initialContent )
347+
348+ // Simulate git checkout - large text replacement
349+ const largeText = "a" . repeat ( 150 )
350+ const event = {
351+ document : mockDocument ,
352+ contentChanges : [
353+ {
354+ range : new vscode . Range ( new vscode . Position ( 0 , 0 ) , new vscode . Position ( 0 , 20 ) ) ,
355+ rangeLength : 20 ,
356+ text : largeText ,
357+ } ,
358+ ] ,
359+ reason : undefined ,
360+ }
361+
362+ // Should be filtered out due to bulk change
363+ const isBulkChange = event . contentChanges . some (
364+ ( change ) => change . rangeLength > 100 || change . text . length > 100 ,
365+ )
366+ expect ( isBulkChange ) . toBe ( true )
367+ } )
368+
369+ it ( "should filter out changes far from cursor (LLM edits)" , async ( ) => {
370+ const initialContent = `line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8`
371+ const { mockDocument } = await setupTestDocument ( "far.js" , initialContent )
372+
373+ // Mock active editor with cursor at line 0
374+ ; ( vscode . window as any ) . activeTextEditor = {
375+ document : mockDocument ,
376+ selection : {
377+ active : new vscode . Position ( 0 , 0 ) ,
378+ } ,
379+ }
380+
381+ // Simulate change at line 7 (far from cursor at line 0)
382+ const event = {
383+ document : mockDocument ,
384+ contentChanges : [
385+ {
386+ range : new vscode . Range ( new vscode . Position ( 7 , 0 ) , new vscode . Position ( 7 , 6 ) ) ,
387+ rangeLength : 6 ,
388+ text : "modified" ,
389+ } ,
390+ ] ,
391+ reason : undefined ,
392+ }
393+
394+ // Should be filtered out - change is more than 2 lines away
395+ const cursorPos = ( vscode . window as any ) . activeTextEditor . selection . active
396+ const isNearCursor = event . contentChanges . some ( ( change ) => {
397+ const distance = Math . abs ( cursorPos . line - change . range . start . line )
398+ return distance <= 2
399+ } )
400+ expect ( isNearCursor ) . toBe ( false )
401+ } )
402+
403+ it ( "should allow changes within 2 lines of cursor" , async ( ) => {
404+ const initialContent = `line 1\nline 2\nline 3\nline 4\nline 5`
405+ const { mockDocument } = await setupTestDocument ( "near.js" , initialContent )
406+
407+ // Mock active editor with cursor at line 2
408+ ; ( vscode . window as any ) . activeTextEditor = {
409+ document : mockDocument ,
410+ selection : {
411+ active : new vscode . Position ( 2 , 0 ) ,
412+ } ,
413+ }
414+
415+ // Simulate change at line 4 (2 lines away from cursor)
416+ const event = {
417+ document : mockDocument ,
418+ contentChanges : [
419+ {
420+ range : new vscode . Range ( new vscode . Position ( 4 , 0 ) , new vscode . Position ( 4 , 6 ) ) ,
421+ rangeLength : 6 ,
422+ text : "modified" ,
423+ } ,
424+ ] ,
425+ reason : undefined ,
426+ }
427+
428+ // Should NOT be filtered out - change is within 2 lines
429+ const cursorPos = ( vscode . window as any ) . activeTextEditor . selection . active
430+ const isNearCursor = event . contentChanges . some ( ( change ) => {
431+ const distance = Math . abs ( cursorPos . line - change . range . start . line )
432+ return distance <= 2
433+ } )
434+ expect ( isNearCursor ) . toBe ( true )
435+ } )
436+
437+ it ( "should filter out changes to non-active documents" , async ( ) => {
438+ const initialContent = `console.log('test');`
439+ const { mockDocument } = await setupTestDocument ( "inactive.js" , initialContent )
440+
441+ // Create a different document for the active editor
442+ const activeContent = `console.log('active');`
443+ const activeUri = vscode . Uri . parse ( "file://active.js" )
444+ mockWorkspace . addDocument ( activeUri , activeContent )
445+ const activeDocument = await mockWorkspace . openTextDocument ( activeUri )
446+
447+ // Mock active editor with a different document
448+ ; ( vscode . window as any ) . activeTextEditor = {
449+ document : activeDocument ,
450+ selection : {
451+ active : new vscode . Position ( 0 , 10 ) ,
452+ } ,
453+ }
454+
455+ // Simulate change to the non-active document
456+ const event = {
457+ document : mockDocument ,
458+ contentChanges : [
459+ {
460+ range : new vscode . Range ( new vscode . Position ( 0 , 10 ) , new vscode . Position ( 0 , 10 ) ) ,
461+ rangeLength : 0 ,
462+ text : "a" ,
463+ } ,
464+ ] ,
465+ reason : undefined ,
466+ }
467+
468+ // Should be filtered out - document doesn't match active editor
469+ const editor = ( vscode . window as any ) . activeTextEditor
470+ const shouldProcess = editor && editor . document === event . document
471+ expect ( shouldProcess ) . toBe ( false )
472+ } )
473+
474+ it ( "should allow small paste operations near cursor" , async ( ) => {
475+ const initialContent = `console.log('test');`
476+ const { mockDocument } = await setupTestDocument ( "paste.js" , initialContent )
477+
478+ // Mock active editor with cursor at line 0
479+ ; ( vscode . window as any ) . activeTextEditor = {
480+ document : mockDocument ,
481+ selection : {
482+ active : new vscode . Position ( 0 , 10 ) ,
483+ } ,
484+ }
485+
486+ // Simulate paste of 50 characters (under threshold)
487+ const pastedText = "a" . repeat ( 50 )
488+ const event = {
489+ document : mockDocument ,
490+ contentChanges : [
491+ {
492+ range : new vscode . Range ( new vscode . Position ( 0 , 10 ) , new vscode . Position ( 0 , 10 ) ) ,
493+ rangeLength : 0 ,
494+ text : pastedText ,
495+ } ,
496+ ] ,
497+ reason : undefined ,
498+ }
499+
500+ // Should NOT be filtered out - paste is small and near cursor
501+ const isBulkChange = event . contentChanges . some (
502+ ( change ) => change . rangeLength > 100 || change . text . length > 100 ,
503+ )
504+ expect ( isBulkChange ) . toBe ( false )
505+
506+ const cursorPos = ( vscode . window as any ) . activeTextEditor . selection . active
507+ const isNearCursor = event . contentChanges . some ( ( change ) => {
508+ const distance = Math . abs ( cursorPos . line - change . range . start . line )
509+ return distance <= 2
510+ } )
511+ expect ( isNearCursor ) . toBe ( true )
512+ } )
513+ } )
270514} )
0 commit comments