miscountHint detects search lines that match a file line except for the count of one repeated rune. Emits one hint per (search-line, disagreeing-rune) group, capped at maxMiscountHints total with " and N more" suffix.
(contentLines, searchLines []string)
| 1319 | // (search-line, disagreeing-rune) group, capped at maxMiscountHints |
| 1320 | // total with " and N more" suffix. |
| 1321 | func miscountHint(contentLines, searchLines []string) string { |
| 1322 | const maxMiscountHints = 3 |
| 1323 | var hints []string |
| 1324 | extra := 0 |
| 1325 | for _, sLine := range searchLines { |
| 1326 | sContent, _ := splitEnding(sLine) |
| 1327 | if strings.TrimSpace(sContent) == "" { |
| 1328 | continue |
| 1329 | } |
| 1330 | // One search line can disagree on different runes against |
| 1331 | // different file lines; group by rune so each hint names a |
| 1332 | // single codepoint. |
| 1333 | groups := make(map[rune][]candidate) |
| 1334 | counts := make(map[rune]int) |
| 1335 | order := []rune{} |
| 1336 | for i, cLine := range contentLines { |
| 1337 | cContent, _ := splitEnding(cLine) |
| 1338 | r, sc, cc, ok := singleRuneCountMismatch(sContent, cContent) |
| 1339 | if !ok { |
| 1340 | continue |
| 1341 | } |
| 1342 | if _, seen := groups[r]; !seen { |
| 1343 | order = append(order, r) |
| 1344 | counts[r] = sc |
| 1345 | } |
| 1346 | groups[r] = append(groups[r], candidate{line: i + 1, cCount: cc}) |
| 1347 | } |
| 1348 | for _, r := range order { |
| 1349 | if len(hints) >= maxMiscountHints { |
| 1350 | extra++ |
| 1351 | continue |
| 1352 | } |
| 1353 | hints = append(hints, formatMiscount(counts[r], r, groups[r])) |
| 1354 | } |
| 1355 | } |
| 1356 | if extra > 0 { |
| 1357 | hints = append(hints, fmt.Sprintf("and %d more", extra)) |
| 1358 | } |
| 1359 | return strings.Join(hints, ". ") |
| 1360 | } |
| 1361 | |
| 1362 | // formatMiscount renders one miscount candidate group. |
| 1363 | func formatMiscount(sCount int, r rune, cands []candidate) string { |
no test coverage detected