@@ -314,6 +314,228 @@ describe("GhostInlineCompletionProvider", () => {
314314
315315 expect ( result [ 0 ] . insertText ) . toBe ( "console.log('test')" )
316316 } )
317+
318+ describe ( "partial typing support" , ( ) => {
319+ it ( "should return remaining suggestion when user has partially typed the suggestion" , ( ) => {
320+ // Set up a suggestion
321+ const suggestions = new GhostSuggestionsState ( )
322+ suggestions . setFillInAtCursor ( {
323+ text : "console.log('Hello, World!');" ,
324+ prefix : "const x = 1" ,
325+ suffix : "\nconst y = 2" ,
326+ } )
327+ provider . updateSuggestions ( suggestions )
328+
329+ // Simulate user typing "cons" after the prefix
330+ const partialDocument = new MockTextDocument (
331+ vscode . Uri . file ( "/test.ts" ) ,
332+ "const x = 1cons\nconst y = 2" ,
333+ )
334+ const partialPosition = new vscode . Position ( 0 , 15 ) // After "const x = 1cons"
335+
336+ const result = provider . provideInlineCompletionItems (
337+ partialDocument ,
338+ partialPosition ,
339+ mockContext ,
340+ mockToken ,
341+ ) as vscode . InlineCompletionItem [ ]
342+
343+ expect ( result ) . toHaveLength ( 1 )
344+ // Should return the remaining part after "cons"
345+ expect ( result [ 0 ] . insertText ) . toBe ( "ole.log('Hello, World!');" )
346+ } )
347+
348+ it ( "should return full suggestion when user has typed nothing after prefix" , ( ) => {
349+ const suggestions = new GhostSuggestionsState ( )
350+ suggestions . setFillInAtCursor ( {
351+ text : "console.log('test');" ,
352+ prefix : "const x = 1" ,
353+ suffix : "\nconst y = 2" ,
354+ } )
355+ provider . updateSuggestions ( suggestions )
356+
357+ // User is at exact prefix position (no partial typing)
358+ const result = provider . provideInlineCompletionItems (
359+ mockDocument ,
360+ mockPosition ,
361+ mockContext ,
362+ mockToken ,
363+ ) as vscode . InlineCompletionItem [ ]
364+
365+ expect ( result ) . toHaveLength ( 1 )
366+ expect ( result [ 0 ] . insertText ) . toBe ( "console.log('test');" )
367+ } )
368+
369+ it ( "should return empty when partially typed content does not match suggestion" , ( ) => {
370+ const suggestions = new GhostSuggestionsState ( )
371+ suggestions . setFillInAtCursor ( {
372+ text : "console.log('test');" ,
373+ prefix : "const x = 1" ,
374+ suffix : "\nconst y = 2" ,
375+ } )
376+ provider . updateSuggestions ( suggestions )
377+
378+ // User typed "xyz" which doesn't match the suggestion
379+ const mismatchDocument = new MockTextDocument (
380+ vscode . Uri . file ( "/test.ts" ) ,
381+ "const x = 1xyz\nconst y = 2" ,
382+ )
383+ const mismatchPosition = new vscode . Position ( 0 , 14 )
384+
385+ const result = provider . provideInlineCompletionItems (
386+ mismatchDocument ,
387+ mismatchPosition ,
388+ mockContext ,
389+ mockToken ,
390+ )
391+
392+ expect ( result ) . toEqual ( [ ] )
393+ } )
394+
395+ it ( "should return empty string when user has typed entire suggestion" , ( ) => {
396+ const suggestions = new GhostSuggestionsState ( )
397+ suggestions . setFillInAtCursor ( {
398+ text : "console.log('test');" ,
399+ prefix : "const x = 1" ,
400+ suffix : "\nconst y = 2" ,
401+ } )
402+ provider . updateSuggestions ( suggestions )
403+
404+ // User has typed the entire suggestion - cursor is at the end of typed text
405+ // Position 31 is right after the semicolon, before the newline
406+ const completeDocument = new MockTextDocument (
407+ vscode . Uri . file ( "/test.ts" ) ,
408+ "const x = 1console.log('test');\nconst y = 2" ,
409+ )
410+ const completePosition = new vscode . Position ( 0 , 31 ) // After the semicolon, before newline
411+
412+ const result = provider . provideInlineCompletionItems (
413+ completeDocument ,
414+ completePosition ,
415+ mockContext ,
416+ mockToken ,
417+ ) as vscode . InlineCompletionItem [ ]
418+
419+ expect ( result ) . toHaveLength ( 1 )
420+ // Should return empty string since everything is typed
421+ expect ( result [ 0 ] . insertText ) . toBe ( "" )
422+ } )
423+
424+ it ( "should not match when suffix has changed" , ( ) => {
425+ const suggestions = new GhostSuggestionsState ( )
426+ suggestions . setFillInAtCursor ( {
427+ text : "console.log('test');" ,
428+ prefix : "const x = 1" ,
429+ suffix : "\nconst y = 2" ,
430+ } )
431+ provider . updateSuggestions ( suggestions )
432+
433+ // User typed partial content but suffix changed
434+ const changedSuffixDocument = new MockTextDocument (
435+ vscode . Uri . file ( "/test.ts" ) ,
436+ "const x = 1cons\nconst y = 3" ,
437+ )
438+ const changedSuffixPosition = new vscode . Position ( 0 , 15 )
439+
440+ const result = provider . provideInlineCompletionItems (
441+ changedSuffixDocument ,
442+ changedSuffixPosition ,
443+ mockContext ,
444+ mockToken ,
445+ )
446+
447+ expect ( result ) . toEqual ( [ ] )
448+ } )
449+
450+ it ( "should prefer exact match over partial match" , ( ) => {
451+ // Add a suggestion that would match partially
452+ const suggestions1 = new GhostSuggestionsState ( )
453+ suggestions1 . setFillInAtCursor ( {
454+ text : "console.log('partial');" ,
455+ prefix : "const x = 1" ,
456+ suffix : "\nconst y = 2" ,
457+ } )
458+ provider . updateSuggestions ( suggestions1 )
459+
460+ // Add a suggestion with exact match (more recent)
461+ const suggestions2 = new GhostSuggestionsState ( )
462+ suggestions2 . setFillInAtCursor ( {
463+ text : "exact match" ,
464+ prefix : "const x = 1cons" ,
465+ suffix : "\nconst y = 2" ,
466+ } )
467+ provider . updateSuggestions ( suggestions2 )
468+
469+ // User is at position that matches exact prefix of second suggestion
470+ const document = new MockTextDocument ( vscode . Uri . file ( "/test.ts" ) , "const x = 1cons\nconst y = 2" )
471+ const position = new vscode . Position ( 0 , 15 )
472+
473+ const result = provider . provideInlineCompletionItems (
474+ document ,
475+ position ,
476+ mockContext ,
477+ mockToken ,
478+ ) as vscode . InlineCompletionItem [ ]
479+
480+ expect ( result ) . toHaveLength ( 1 )
481+ // Should return exact match (most recent), not partial
482+ expect ( result [ 0 ] . insertText ) . toBe ( "exact match" )
483+ } )
484+
485+ it ( "should handle multi-character partial typing" , ( ) => {
486+ const suggestions = new GhostSuggestionsState ( )
487+ suggestions . setFillInAtCursor ( {
488+ text : "function test() { return 42; }" ,
489+ prefix : "const x = 1" ,
490+ suffix : "\nconst y = 2" ,
491+ } )
492+ provider . updateSuggestions ( suggestions )
493+
494+ // User typed "function te"
495+ const partialDocument = new MockTextDocument (
496+ vscode . Uri . file ( "/test.ts" ) ,
497+ "const x = 1function te\nconst y = 2" ,
498+ )
499+ const partialPosition = new vscode . Position ( 0 , 22 )
500+
501+ const result = provider . provideInlineCompletionItems (
502+ partialDocument ,
503+ partialPosition ,
504+ mockContext ,
505+ mockToken ,
506+ ) as vscode . InlineCompletionItem [ ]
507+
508+ expect ( result ) . toHaveLength ( 1 )
509+ expect ( result [ 0 ] . insertText ) . toBe ( "st() { return 42; }" )
510+ } )
511+
512+ it ( "should handle case-sensitive partial matching" , ( ) => {
513+ const suggestions = new GhostSuggestionsState ( )
514+ suggestions . setFillInAtCursor ( {
515+ text : "Console.log('test');" ,
516+ prefix : "const x = 1" ,
517+ suffix : "\nconst y = 2" ,
518+ } )
519+ provider . updateSuggestions ( suggestions )
520+
521+ // User typed "cons" (lowercase) but suggestion starts with "Console" (uppercase)
522+ const partialDocument = new MockTextDocument (
523+ vscode . Uri . file ( "/test.ts" ) ,
524+ "const x = 1cons\nconst y = 2" ,
525+ )
526+ const partialPosition = new vscode . Position ( 0 , 15 )
527+
528+ const result = provider . provideInlineCompletionItems (
529+ partialDocument ,
530+ partialPosition ,
531+ mockContext ,
532+ mockToken ,
533+ )
534+
535+ // Should not match due to case difference
536+ expect ( result ) . toEqual ( [ ] )
537+ } )
538+ } )
317539 } )
318540
319541 describe ( "updateSuggestions" , ( ) => {
0 commit comments