@@ -38,6 +38,11 @@ vi.mock('../gui/VDateInputPrompt/VDateInputPrompt', () => ({
3838 } ,
3939} ) ) ;
4040
41+ vi . mock ( '../utils/errorUtils' , ( ) => ( {
42+ __esModule : true ,
43+ reportError : vi . fn ( ) ,
44+ } ) ) ;
45+
4146vi . mock ( '../gui/MathModal' , ( ) => ( {
4247 __esModule : true ,
4348 MathModal : {
@@ -98,6 +103,7 @@ vi.mock('../main', () => ({
98103} ) ) ;
99104
100105import { CaptureChoiceFormatter } from './captureChoiceFormatter' ;
106+ import { reportError } from '../utils/errorUtils' ;
101107
102108const createChoice = ( overrides : Partial < ICaptureChoice > = { } ) : ICaptureChoice => ( {
103109 id : 'test' ,
@@ -111,7 +117,7 @@ const createChoice = (overrides: Partial<ICaptureChoice> = {}): ICaptureChoice =
111117 prepend : false ,
112118 appendLink : false ,
113119 task : false ,
114- insertAfter : { enabled : false , after : '' , insertAtEnd : false , considerSubsections : false , createIfNotFound : false , createIfNotFoundLocation : '' , blankLineAfterMatchMode : 'auto' } ,
120+ insertAfter : { enabled : false , after : '' , insertAtEnd : false , considerSubsections : false , createIfNotFound : false , createIfNotFoundLocation : '' , inline : false , replaceExisting : false , blankLineAfterMatchMode : 'auto' } ,
115121 newLineCapture : { enabled : false , direction : 'below' } ,
116122 openFile : false ,
117123 fileOpening : { location : 'tab' , direction : 'vertical' , mode : 'default' , focus : true } ,
@@ -356,3 +362,175 @@ describe('CaptureChoiceFormatter insert after blank lines', () => {
356362 expect ( result ) . toBe ( [ '# H' , 'X' , '' , 'A' ] . join ( '\n' ) ) ;
357363 } ) ;
358364} ) ;
365+
366+ describe ( 'CaptureChoiceFormatter insert after inline' , ( ) => {
367+ beforeEach ( ( ) => {
368+ vi . resetAllMocks ( ) ;
369+ ( global as any ) . navigator = {
370+ clipboard : {
371+ readText : vi . fn ( ) . mockResolvedValue ( '' ) ,
372+ } ,
373+ } ;
374+ } ) ;
375+
376+ const createFormatter = ( ) => {
377+ const app = createMockApp ( ) ;
378+ const plugin = {
379+ settings : {
380+ enableTemplatePropertyTypes : false ,
381+ globalVariables : { } ,
382+ showCaptureNotification : false ,
383+ showInputCancellationNotification : true ,
384+ } ,
385+ } as any ;
386+ const formatter = new CaptureChoiceFormatter ( app , plugin ) ;
387+ const file = createTFile ( 'Inline.md' ) ;
388+
389+ return { formatter, file } ;
390+ } ;
391+
392+ const createInlineChoice = (
393+ after : string ,
394+ overrides : Partial < ICaptureChoice [ 'insertAfter' ] > = { } ,
395+ ) : ICaptureChoice =>
396+ createChoice ( {
397+ insertAfter : {
398+ enabled : true ,
399+ after,
400+ insertAtEnd : false ,
401+ considerSubsections : false ,
402+ createIfNotFound : false ,
403+ createIfNotFoundLocation : 'top' ,
404+ inline : true ,
405+ replaceExisting : false ,
406+ blankLineAfterMatchMode : 'auto' ,
407+ ...overrides ,
408+ } ,
409+ } ) ;
410+
411+ it ( 'inserts inline at match end and preserves suffix' , async ( ) => {
412+ const { formatter, file } = createFormatter ( ) ;
413+ const choice = createInlineChoice ( 'Status:' , { replaceExisting : false } ) ;
414+ const fileContent = 'Status: pending' ;
415+
416+ const result = await formatter . formatContentWithFile (
417+ ' done' ,
418+ choice ,
419+ fileContent ,
420+ file ,
421+ ) ;
422+
423+ expect ( result ) . toBe ( 'Status: done pending' ) ;
424+ } ) ;
425+
426+ it ( 'replaces to end-of-line when enabled, preserving newline' , async ( ) => {
427+ const { formatter, file } = createFormatter ( ) ;
428+ const choice = createInlineChoice ( 'Status: ' , { replaceExisting : true } ) ;
429+ const fileContent = [ 'Status: pending' , 'Next' ] . join ( '\n' ) ;
430+
431+ const result = await formatter . formatContentWithFile (
432+ 'done' ,
433+ choice ,
434+ fileContent ,
435+ file ,
436+ ) ;
437+
438+ expect ( result ) . toBe ( [ 'Status: done' , 'Next' ] . join ( '\n' ) ) ;
439+ } ) ;
440+
441+ it ( 'replace mode behaves like append when target is at end-of-line' , async ( ) => {
442+ const { formatter, file } = createFormatter ( ) ;
443+ const choice = createInlineChoice ( 'pending' , { replaceExisting : true } ) ;
444+ const fileContent = 'Status: pending' ;
445+
446+ const result = await formatter . formatContentWithFile (
447+ '!' ,
448+ choice ,
449+ fileContent ,
450+ file ,
451+ ) ;
452+
453+ expect ( result ) . toBe ( 'Status: pending!' ) ;
454+ } ) ;
455+
456+ it ( 'creates a single inline line when target is not found' , async ( ) => {
457+ const { formatter, file } = createFormatter ( ) ;
458+ const choice = createInlineChoice ( 'Status: ' , {
459+ createIfNotFound : true ,
460+ createIfNotFoundLocation : 'top' ,
461+ } ) ;
462+ const fileContent = '# Header' ;
463+
464+ const result = await formatter . formatContentWithFile (
465+ 'done' ,
466+ choice ,
467+ fileContent ,
468+ file ,
469+ ) ;
470+
471+ expect ( result ) . toBe ( [ 'Status: done' , '# Header' ] . join ( '\n' ) ) ;
472+ } ) ;
473+
474+ it ( 'does not modify the file when target is missing and create-if-not-found is off' , async ( ) => {
475+ const { formatter, file } = createFormatter ( ) ;
476+ const choice = createInlineChoice ( 'Missing: ' , { createIfNotFound : false } ) ;
477+ const fileContent = 'Status: pending' ;
478+
479+ const result = await formatter . formatContentWithFile (
480+ 'done' ,
481+ choice ,
482+ fileContent ,
483+ file ,
484+ ) ;
485+
486+ expect ( result ) . toBe ( fileContent ) ;
487+ expect ( reportError ) . toHaveBeenCalled ( ) ;
488+ } ) ;
489+
490+ it ( 'updates only the first match' , async ( ) => {
491+ const { formatter, file } = createFormatter ( ) ;
492+ const choice = createInlineChoice ( 'Tag: ' , { replaceExisting : true } ) ;
493+ const fileContent = [ 'Tag: a' , 'Tag: b' ] . join ( '\n' ) ;
494+
495+ const result = await formatter . formatContentWithFile (
496+ 'X' ,
497+ choice ,
498+ fileContent ,
499+ file ,
500+ ) ;
501+
502+ expect ( result ) . toBe ( [ 'Tag: X' , 'Tag: b' ] . join ( '\n' ) ) ;
503+ } ) ;
504+
505+ it ( 'works with capture to active file enabled' , async ( ) => {
506+ const { formatter, file } = createFormatter ( ) ;
507+ const choice = createInlineChoice ( 'Status: ' , { replaceExisting : true } ) ;
508+ choice . captureToActiveFile = true ;
509+ const fileContent = 'Status: pending' ;
510+
511+ const result = await formatter . formatContentWithFile (
512+ 'done' ,
513+ choice ,
514+ fileContent ,
515+ file ,
516+ ) ;
517+
518+ expect ( result ) . toBe ( 'Status: done' ) ;
519+ } ) ;
520+
521+ it ( 'reports an error and leaves content unchanged when target contains a newline' , async ( ) => {
522+ const { formatter, file } = createFormatter ( ) ;
523+ const choice = createInlineChoice ( 'Status:\n' , { replaceExisting : true } ) ;
524+ const fileContent = 'Status:\npending' ;
525+
526+ const result = await formatter . formatContentWithFile (
527+ 'done' ,
528+ choice ,
529+ fileContent ,
530+ file ,
531+ ) ;
532+
533+ expect ( result ) . toBe ( fileContent ) ;
534+ expect ( reportError ) . toHaveBeenCalled ( ) ;
535+ } ) ;
536+ } ) ;
0 commit comments