fuzzyReplaceLines handles fuzzy matching passes (2 and 3) for fuzzyReplace. When replaceAll is false and there are multiple matches, an error is returned. When replaceAll is true, all non-overlapping matches are replaced. Returns (result, true, nil) on success, ("", false, nil) when searchLines don
( contentLines, searchLines []string, replace string, eq func(a, b string) bool, replaceAll bool, forcedEnding string, )
| 1487 | // |
| 1488 | //nolint:revive // replaceAll is a direct pass-through of the user's flag, not a control coupling. |
| 1489 | func fuzzyReplaceLines( |
| 1490 | contentLines, searchLines []string, |
| 1491 | replace string, |
| 1492 | eq func(a, b string) bool, |
| 1493 | replaceAll bool, |
| 1494 | forcedEnding string, |
| 1495 | ) (string, bool, error) { |
| 1496 | start, end, ok := seekLines(contentLines, searchLines, eq) |
| 1497 | if !ok { |
| 1498 | return "", false, nil |
| 1499 | } |
| 1500 | |
| 1501 | if !replaceAll { |
| 1502 | if count := countLineMatches(contentLines, searchLines, eq); count > 1 { |
| 1503 | return "", true, xerrors.Errorf("search string matches %d occurrences "+ |
| 1504 | "(expected exactly 1). Include more surrounding "+ |
| 1505 | "context to make the match unique, or set "+ |
| 1506 | "replace_all to true", count) |
| 1507 | } |
| 1508 | var b strings.Builder |
| 1509 | for _, l := range contentLines[:start] { |
| 1510 | _, _ = b.WriteString(l) |
| 1511 | } |
| 1512 | _, _ = b.WriteString(buildReplacementLines(contentLines[start:end], searchLines, replace, forcedEnding, atNoNewlineEOF(contentLines, end))) |
| 1513 | for _, l := range contentLines[end:] { |
| 1514 | _, _ = b.WriteString(l) |
| 1515 | } |
| 1516 | return b.String(), true, nil |
| 1517 | } |
| 1518 | |
| 1519 | // Replace all: collect all match positions, then emit the |
| 1520 | // output forward, interleaving unmatched spans with spliced |
| 1521 | // replacements. Each match runs through the same per-position |
| 1522 | // splice as single-replace, using its own matched content |
| 1523 | // slice as the reference. |
| 1524 | type lineMatch struct{ start, end int } |
| 1525 | var matches []lineMatch |
| 1526 | for i := 0; i <= len(contentLines)-len(searchLines); { |
| 1527 | found := true |
| 1528 | for j, sLine := range searchLines { |
| 1529 | if !eq(contentLines[i+j], sLine) { |
| 1530 | found = false |
| 1531 | break |
| 1532 | } |
| 1533 | } |
| 1534 | if found { |
| 1535 | matches = append(matches, lineMatch{i, i + len(searchLines)}) |
| 1536 | i += len(searchLines) // skip past this match |
| 1537 | } else { |
| 1538 | i++ |
| 1539 | } |
| 1540 | } |
| 1541 | |
| 1542 | var b strings.Builder |
| 1543 | prev := 0 |
| 1544 | for _, m := range matches { |
| 1545 | for _, l := range contentLines[prev:m.start] { |
| 1546 | _, _ = b.WriteString(l) |
no test coverage detected