import { hasConflictMarkers, splitLines } from "../git/merge3";
import type { ConflictHunk } from "../git/merge3";
import { parseConflictText } from "~/lib/conflict-blocks";
import { assembleResolution } from "./candidates";
/** A candidate may reorder and lightly edit both sides, but not balloon past them. */
const GROWTH_FACTOR = 2;
const GROWTH_SLACK = 4;
const MIN_SIGNIFICANT_CHARS = 4;
const BRACE_LANGUAGES = /\.(go|ts|tsx|js|jsx|mjs|cjs|java|kt|kts|c|h|cc|cpp|hpp|cs|swift|scala|php|json|jsonc)$/i;
function bare(line: string): string {
return line.replace(/\r?\n$/, "").trim();
}
/** Lines that carry enough content that repeating them elsewhere is not a coincidence. */
function significant(line: string): boolean {
const text = bare(line);
return text.length >= MIN_SIGNIFICANT_CHARS && /[\p{L}\p{N}]/u.test(text);
}
function countLines(text: string): number {
return splitLines(text).length;
}
/** Text outside every conflict block, i.e. what the resolution is spliced into. */
function outsideLines(content: string): Set<string> {
const parsed = parseConflictText(content);
if (!parsed) return new Set();
return new Set(parsed.segments.flatMap((segment) => ("text" in segment ? splitLines(segment.text).map(bare) : [])));
}
/**
* Bracket balance with strings, runes, raw strings and comments skipped. Not a parser, just enough to spot
* a candidate that pasted half a declaration into the hunk.
*/
export function bracketsBalanced(text: string): boolean {
const stack: string[] = [];
const closer: Record<string, string> = { "(": ")", "[": "]", "{": "}" };
let index = 0;