66 lines
1.9 KiB
JavaScript
66 lines
1.9 KiB
JavaScript
/**
|
|
* Unique search/replace (Grok-shaped). No Bare imports.
|
|
*/
|
|
|
|
function countOccurrences(hay, needle) {
|
|
if (!needle) return 0;
|
|
const h = String(hay || '');
|
|
const n = String(needle);
|
|
let count = 0;
|
|
let i = 0;
|
|
while (i < h.length) {
|
|
const at = h.indexOf(n, i);
|
|
if (at < 0) break;
|
|
count += 1;
|
|
i = at + Math.max(1, n.length);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
function applySearchReplace(cur, oldString, newString, replaceAll) {
|
|
const old = oldString == null ? '' : String(oldString);
|
|
const neu = newString == null ? '' : String(newString);
|
|
const text = cur == null ? '' : String(cur);
|
|
if (!old) {
|
|
if (text.trim()) {
|
|
throw new Error(
|
|
'old_string is empty but the file is not empty; refuse overwrite. Use write_file to replace the whole file, or pass a unique old_string.'
|
|
);
|
|
}
|
|
return { text: neu, created: true, replacements: 1 };
|
|
}
|
|
const n = countOccurrences(text, old);
|
|
if (n === 0) throw new Error('old_string not found');
|
|
if (n > 1 && !replaceAll) {
|
|
throw new Error(
|
|
'old_string matched ' + n + ' times; add surrounding lines to make it unique, or set replace_all to true.'
|
|
);
|
|
}
|
|
const next = replaceAll ? text.split(old).join(neu) : text.replace(old, neu);
|
|
return { text: next, created: false, replacements: replaceAll ? n : 1 };
|
|
}
|
|
|
|
function contextSnippet(text, needle, radius) {
|
|
const lines = String(text || '').split('\n');
|
|
const r = radius == null ? 3 : radius;
|
|
const want = String(needle || '');
|
|
let idx = -1;
|
|
if (want) {
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (lines[i].indexOf(want) >= 0) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (idx < 0) idx = 0;
|
|
const start = Math.max(0, idx - r);
|
|
const end = Math.min(lines.length, idx + r + 1);
|
|
return lines
|
|
.slice(start, end)
|
|
.map((l, i) => String(start + i + 1).padStart(6) + '| ' + l)
|
|
.join('\n');
|
|
}
|
|
|
|
module.exports = { countOccurrences, applySearchReplace, contextSnippet };
|