@@ -353,23 +353,87 @@ func getValidCodeFragments(i Inserter) CodeFragmentsMap {
353353 return codeFragments
354354}
355355
356- // filterExistingValues removes the single-line values that already exists
357- // TODO: Add support for multi-line duplicate values
356+ // filterExistingValues removes code fragments that already exist in the content.
358357func filterExistingValues (content string , codeFragmentsMap CodeFragmentsMap ) error {
359- scanner := bufio .NewScanner (strings .NewReader (content ))
360- for scanner .Scan () {
361- line := scanner .Text ()
362- for marker , codeFragments := range codeFragmentsMap {
363- for i , codeFragment := range codeFragments {
364- if strings .TrimSpace (line ) == strings .TrimSpace (codeFragment ) {
365- codeFragmentsMap [marker ] = append (codeFragments [:i ], codeFragments [i + 1 :]... )
366- }
358+ for marker , codeFragments := range codeFragmentsMap {
359+ codeFragmentsOut := codeFragments [:0 ]
360+
361+ for _ , codeFragment := range codeFragments {
362+ exists , err := codeFragmentExists (content , codeFragment )
363+ if err != nil {
364+ return err
367365 }
368- if len ( codeFragmentsMap [ marker ]) == 0 {
369- delete ( codeFragmentsMap , marker )
366+ if ! exists {
367+ codeFragmentsOut = append ( codeFragmentsOut , codeFragment )
370368 }
371369 }
370+
371+ if len (codeFragmentsOut ) == 0 {
372+ delete (codeFragmentsMap , marker )
373+ } else {
374+ codeFragmentsMap [marker ] = codeFragmentsOut
375+ }
376+ }
377+
378+ return nil
379+ }
380+
381+ // codeFragmentExists checks if the codeFragment exists in the content.
382+ func codeFragmentExists (content , codeFragment string ) (bool , error ) {
383+ exists := false
384+ codeFragmentTrimmed := strings .TrimSpace (codeFragment )
385+ scanLines := len (strings .Split (codeFragmentTrimmed , "\n " ))
386+ scanFunc := func (contentGroup string ) bool {
387+ if contentGroup == codeFragmentTrimmed {
388+ exists = true
389+ return false
390+ }
391+ return true
392+ }
393+
394+ if err := scanMultiline (content , scanLines , scanFunc ); err != nil {
395+ return false , err
372396 }
397+
398+ return exists , nil
399+ }
400+
401+ // scanMultiline scans a string while buffering the specified number of scanLines. It calls scanFunc
402+ // for every group of lines. The content passed to scanFunc will have trimmed whitespace. It
403+ // continues scanning the content as long as scanFunc returns true.
404+ func scanMultiline (content string , scanLines int , scanFunc func (contentGroup string ) bool ) error {
405+ scanner := bufio .NewScanner (strings .NewReader (content ))
406+
407+ // Optimized simple case.
408+ if scanLines == 1 {
409+ for scanner .Scan () {
410+ if ! scanFunc (strings .TrimSpace (scanner .Text ())) {
411+ return scanner .Err ()
412+ }
413+ }
414+ return scanner .Err ()
415+ }
416+
417+ // Complex case.
418+ bufferedLines := make ([]string , scanLines )
419+ bufferedLinesIndex := 0
420+ var sb strings.Builder
421+
422+ for scanner .Scan () {
423+ bufferedLines [bufferedLinesIndex ] = scanner .Text ()
424+ bufferedLinesIndex = (bufferedLinesIndex + 1 ) % scanLines
425+
426+ sb .Reset ()
427+ for i := 0 ; i < scanLines ; i ++ {
428+ _ , _ = sb .WriteString (bufferedLines [(bufferedLinesIndex + i )% scanLines ])
429+ _ = sb .WriteByte ('\n' )
430+ }
431+
432+ if ! scanFunc (strings .TrimSpace (sb .String ())) {
433+ return scanner .Err ()
434+ }
435+ }
436+
373437 return scanner .Err ()
374438}
375439
0 commit comments