opencode-gbk-tools 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -1
- package/dist/opencode-tools/gbk_edit.js +157 -13
- package/dist/opencode-tools/text_edit.js +238 -38
- package/dist/opencode-tools/text_write.js +8 -0
- package/dist/plugin/index.js +391 -51
- package/dist/plugins/opencode-gbk-tools.js +2 -0
- package/dist/release-manifest.json +5 -5
- package/package.json +1 -1
|
@@ -16309,6 +16309,11 @@ import { createReadStream } from "fs";
|
|
|
16309
16309
|
import fs2 from "fs/promises";
|
|
16310
16310
|
import path2 from "path";
|
|
16311
16311
|
var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16312
|
+
function assertStringArgument(value, name) {
|
|
16313
|
+
if (typeof value !== "string") {
|
|
16314
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
16315
|
+
}
|
|
16316
|
+
}
|
|
16312
16317
|
function assertPositiveInteger(value, name) {
|
|
16313
16318
|
if (!Number.isInteger(value) || Number(value) <= 0) {
|
|
16314
16319
|
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
|
|
@@ -16336,6 +16341,8 @@ function detectNewlineStyle(text) {
|
|
|
16336
16341
|
return "none";
|
|
16337
16342
|
}
|
|
16338
16343
|
function countOccurrences(text, target) {
|
|
16344
|
+
assertStringArgument(text, "text");
|
|
16345
|
+
assertStringArgument(target, "oldString");
|
|
16339
16346
|
if (target.length === 0) {
|
|
16340
16347
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16341
16348
|
}
|
|
@@ -16354,6 +16361,25 @@ function countOccurrences(text, target) {
|
|
|
16354
16361
|
// src/lib/text-file.ts
|
|
16355
16362
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16356
16363
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16364
|
+
function assertStringArgument2(value, name) {
|
|
16365
|
+
if (typeof value !== "string") {
|
|
16366
|
+
throw createTextError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
16367
|
+
}
|
|
16368
|
+
}
|
|
16369
|
+
function assertReplaceArguments(input) {
|
|
16370
|
+
assertStringArgument2(input.oldString, "oldString");
|
|
16371
|
+
assertStringArgument2(input.newString, "newString");
|
|
16372
|
+
}
|
|
16373
|
+
function resolveEditMode(mode) {
|
|
16374
|
+
return mode ?? "replace";
|
|
16375
|
+
}
|
|
16376
|
+
function resolveInsertIfExists(value) {
|
|
16377
|
+
return value ?? "skip";
|
|
16378
|
+
}
|
|
16379
|
+
function resolveExplicitTextEncoding(value, fallback) {
|
|
16380
|
+
const requested = value ?? "auto";
|
|
16381
|
+
return requested === "auto" ? fallback : requested;
|
|
16382
|
+
}
|
|
16357
16383
|
function isSupportedEncoding(value) {
|
|
16358
16384
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
16359
16385
|
}
|
|
@@ -16472,6 +16498,8 @@ function assertLikelyTextBuffer(buffer) {
|
|
|
16472
16498
|
throw createTextError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309\u6587\u672C\u5904\u7406");
|
|
16473
16499
|
}
|
|
16474
16500
|
function getNearestContext(content, oldString) {
|
|
16501
|
+
assertStringArgument2(content, "content");
|
|
16502
|
+
assertStringArgument2(oldString, "oldString");
|
|
16475
16503
|
const lines = content.split(/\r?\n/);
|
|
16476
16504
|
const oldLines = oldString.replace(/\r\n/g, "\n").split("\n").filter(Boolean);
|
|
16477
16505
|
const firstToken = oldLines[0] || oldString.trim();
|
|
@@ -16483,6 +16511,8 @@ function getNearestContext(content, oldString) {
|
|
|
16483
16511
|
return lines.slice(0, 4).join("\n");
|
|
16484
16512
|
}
|
|
16485
16513
|
function buildNoMatchMessage(content, oldString) {
|
|
16514
|
+
assertStringArgument2(content, "content");
|
|
16515
|
+
assertStringArgument2(oldString, "oldString");
|
|
16486
16516
|
return [
|
|
16487
16517
|
"\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
|
|
16488
16518
|
"oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\u3002",
|
|
@@ -16490,6 +16520,105 @@ function buildNoMatchMessage(content, oldString) {
|
|
|
16490
16520
|
getNearestContext(content, oldString)
|
|
16491
16521
|
].join("\n");
|
|
16492
16522
|
}
|
|
16523
|
+
function findOccurrenceIndex(text, token, occurrence) {
|
|
16524
|
+
assertStringArgument2(text, "text");
|
|
16525
|
+
assertStringArgument2(token, "anchor");
|
|
16526
|
+
assertPositiveInteger(occurrence, "occurrence");
|
|
16527
|
+
if (token.length === 0) {
|
|
16528
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "anchor \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16529
|
+
}
|
|
16530
|
+
let count = 0;
|
|
16531
|
+
let searchFrom = 0;
|
|
16532
|
+
while (true) {
|
|
16533
|
+
const index = text.indexOf(token, searchFrom);
|
|
16534
|
+
if (index === -1) {
|
|
16535
|
+
break;
|
|
16536
|
+
}
|
|
16537
|
+
count += 1;
|
|
16538
|
+
if (count === occurrence) {
|
|
16539
|
+
return { index, total: countOccurrences(text, token) };
|
|
16540
|
+
}
|
|
16541
|
+
searchFrom = index + token.length;
|
|
16542
|
+
}
|
|
16543
|
+
if (count === 0) {
|
|
16544
|
+
throw createTextError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\u70B9: ${token}`);
|
|
16545
|
+
}
|
|
16546
|
+
throw createTextError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\u5230 ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\u7B2C ${occurrence} \u5904`);
|
|
16547
|
+
}
|
|
16548
|
+
function assertInsertArguments(input) {
|
|
16549
|
+
assertStringArgument2(input.anchor, "anchor");
|
|
16550
|
+
assertStringArgument2(input.content, "content");
|
|
16551
|
+
if (input.content.length === 0) {
|
|
16552
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16553
|
+
}
|
|
16554
|
+
if (input.replaceAll !== void 0) {
|
|
16555
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 replaceAll");
|
|
16556
|
+
}
|
|
16557
|
+
if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
|
|
16558
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
|
|
16559
|
+
}
|
|
16560
|
+
}
|
|
16561
|
+
function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
|
|
16562
|
+
const beforeLines = normalizeNewlines(beforeText).split("\n");
|
|
16563
|
+
const afterLines = normalizeNewlines(afterText).split("\n");
|
|
16564
|
+
const maxLines = Math.max(beforeLines.length, afterLines.length);
|
|
16565
|
+
const lines = [
|
|
16566
|
+
`--- ${path3.basename(filePath)} (${encoding})`,
|
|
16567
|
+
`+++ ${path3.basename(filePath)} (${encoding})`
|
|
16568
|
+
];
|
|
16569
|
+
for (let index = 0; index < maxLines; index += 1) {
|
|
16570
|
+
const before = beforeLines[index];
|
|
16571
|
+
const after = afterLines[index];
|
|
16572
|
+
if (before !== void 0 && after !== void 0 && before === after) {
|
|
16573
|
+
lines.push(` ${before}`);
|
|
16574
|
+
continue;
|
|
16575
|
+
}
|
|
16576
|
+
if (before !== void 0) {
|
|
16577
|
+
lines.push(`-${before}`);
|
|
16578
|
+
}
|
|
16579
|
+
if (after !== void 0) {
|
|
16580
|
+
lines.push(`+${after}`);
|
|
16581
|
+
}
|
|
16582
|
+
}
|
|
16583
|
+
return lines.join("\n");
|
|
16584
|
+
}
|
|
16585
|
+
function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
|
|
16586
|
+
const alignedContent = alignTextToNewlineStyle(content, newlineStyle);
|
|
16587
|
+
const located = findOccurrenceIndex(text, anchor, occurrence);
|
|
16588
|
+
const insertionPoint = mode === "insertAfter" ? located.index + anchor.length : located.index;
|
|
16589
|
+
const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
|
|
16590
|
+
if (alreadyExists) {
|
|
16591
|
+
if (ifExists === "error") {
|
|
16592
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\u5BB9");
|
|
16593
|
+
}
|
|
16594
|
+
if (ifExists === "skip") {
|
|
16595
|
+
return {
|
|
16596
|
+
outputText: text,
|
|
16597
|
+
inserted: false,
|
|
16598
|
+
skipped: true,
|
|
16599
|
+
anchorMatches: located.total,
|
|
16600
|
+
occurrence,
|
|
16601
|
+
anchor,
|
|
16602
|
+
previewBefore: text.slice(Math.max(0, located.index - 80), Math.min(text.length, located.index + anchor.length + 80)),
|
|
16603
|
+
previewAfter: text.slice(Math.max(0, located.index - 80), Math.min(text.length, located.index + anchor.length + 80))
|
|
16604
|
+
};
|
|
16605
|
+
}
|
|
16606
|
+
}
|
|
16607
|
+
const outputText = `${text.slice(0, insertionPoint)}${alignedContent}${text.slice(insertionPoint)}`;
|
|
16608
|
+
const previewStart = Math.max(0, insertionPoint - 80);
|
|
16609
|
+
const previewBeforeEnd = Math.min(text.length, insertionPoint + 80);
|
|
16610
|
+
const previewAfterEnd = Math.min(outputText.length, insertionPoint + alignedContent.length + 80);
|
|
16611
|
+
return {
|
|
16612
|
+
outputText,
|
|
16613
|
+
inserted: true,
|
|
16614
|
+
skipped: false,
|
|
16615
|
+
anchorMatches: located.total,
|
|
16616
|
+
occurrence,
|
|
16617
|
+
anchor,
|
|
16618
|
+
previewBefore: text.slice(previewStart, previewBeforeEnd),
|
|
16619
|
+
previewAfter: outputText.slice(previewStart, previewAfterEnd)
|
|
16620
|
+
};
|
|
16621
|
+
}
|
|
16493
16622
|
function detectTextEncodingFromBuffer(buffer, requestedEncoding = "auto") {
|
|
16494
16623
|
if (requestedEncoding !== "auto") {
|
|
16495
16624
|
assertTextEncodingSupported(requestedEncoding);
|
|
@@ -16665,6 +16794,7 @@ function resolveEditScope(text, input) {
|
|
|
16665
16794
|
};
|
|
16666
16795
|
}
|
|
16667
16796
|
function alignTextToNewlineStyle(text, newlineStyle) {
|
|
16797
|
+
assertStringArgument2(text, "text");
|
|
16668
16798
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
16669
16799
|
if (newlineStyle === "crlf") {
|
|
16670
16800
|
return normalized.replace(/\n/g, "\r\n");
|
|
@@ -16675,6 +16805,7 @@ function alignTextToNewlineStyle(text, newlineStyle) {
|
|
|
16675
16805
|
return text;
|
|
16676
16806
|
}
|
|
16677
16807
|
function ensureLossless(input, encoding, hasBom = encoding === "utf8-bom") {
|
|
16808
|
+
assertStringArgument2(input, "content");
|
|
16678
16809
|
const buffer = encodeText(input, encoding, hasBom);
|
|
16679
16810
|
const roundTrip = decodeText(buffer, encoding);
|
|
16680
16811
|
if (roundTrip !== input) {
|
|
@@ -16682,43 +16813,100 @@ function ensureLossless(input, encoding, hasBom = encoding === "utf8-bom") {
|
|
|
16682
16813
|
}
|
|
16683
16814
|
}
|
|
16684
16815
|
async function replaceTextFileText(input) {
|
|
16816
|
+
const mode = resolveEditMode(input.mode);
|
|
16685
16817
|
const normalizedInput = {
|
|
16686
16818
|
...input,
|
|
16687
16819
|
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
16688
16820
|
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
16689
16821
|
};
|
|
16690
|
-
if (input.oldString.length === 0) {
|
|
16691
|
-
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16692
|
-
}
|
|
16693
16822
|
const loaded = await readWholeTextFile({
|
|
16694
16823
|
filePath: input.filePath,
|
|
16695
16824
|
encoding: input.encoding ?? "auto",
|
|
16696
16825
|
allowExternal: input.allowExternal,
|
|
16697
16826
|
context: input.context
|
|
16698
16827
|
});
|
|
16828
|
+
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
16829
|
+
assertInsertArguments(normalizedInput);
|
|
16830
|
+
const occurrence = normalizeOptionalPositiveInteger(normalizedInput.occurrence, "occurrence") ?? 1;
|
|
16831
|
+
const insertResult = buildInsertOutput(
|
|
16832
|
+
loaded.content,
|
|
16833
|
+
mode,
|
|
16834
|
+
normalizedInput.anchor,
|
|
16835
|
+
normalizedInput.content,
|
|
16836
|
+
occurrence,
|
|
16837
|
+
resolveInsertIfExists(normalizedInput.ifExists),
|
|
16838
|
+
loaded.newlineStyle
|
|
16839
|
+
);
|
|
16840
|
+
if (insertResult.skipped) {
|
|
16841
|
+
return {
|
|
16842
|
+
mode,
|
|
16843
|
+
filePath: loaded.filePath,
|
|
16844
|
+
encoding: loaded.encoding,
|
|
16845
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
16846
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
16847
|
+
confidence: loaded.confidence,
|
|
16848
|
+
hasBom: loaded.hasBom,
|
|
16849
|
+
anchor: insertResult.anchor,
|
|
16850
|
+
occurrence: insertResult.occurrence,
|
|
16851
|
+
anchorMatches: insertResult.anchorMatches,
|
|
16852
|
+
inserted: false,
|
|
16853
|
+
skipped: true,
|
|
16854
|
+
bytesRead: loaded.bytesRead,
|
|
16855
|
+
bytesWritten: 0,
|
|
16856
|
+
newlineStyle: loaded.newlineStyle
|
|
16857
|
+
};
|
|
16858
|
+
}
|
|
16859
|
+
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ? loaded.encoding : resolveExplicitTextEncoding(normalizedInput.encoding, loaded.encoding);
|
|
16860
|
+
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" : loaded.hasBom;
|
|
16861
|
+
ensureLossless(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
16862
|
+
const buffer2 = encodeText(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
16863
|
+
await fs3.writeFile(loaded.filePath, buffer2);
|
|
16864
|
+
return {
|
|
16865
|
+
mode,
|
|
16866
|
+
filePath: loaded.filePath,
|
|
16867
|
+
encoding: targetEncoding2,
|
|
16868
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
16869
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
16870
|
+
confidence: loaded.confidence,
|
|
16871
|
+
hasBom: targetHasBom2,
|
|
16872
|
+
anchor: insertResult.anchor,
|
|
16873
|
+
occurrence: insertResult.occurrence,
|
|
16874
|
+
anchorMatches: insertResult.anchorMatches,
|
|
16875
|
+
inserted: true,
|
|
16876
|
+
skipped: false,
|
|
16877
|
+
bytesRead: loaded.bytesRead,
|
|
16878
|
+
bytesWritten: buffer2.byteLength,
|
|
16879
|
+
newlineStyle: detectNewlineStyle(insertResult.outputText)
|
|
16880
|
+
};
|
|
16881
|
+
}
|
|
16882
|
+
assertReplaceArguments(input);
|
|
16883
|
+
if (input.oldString.length === 0) {
|
|
16884
|
+
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16885
|
+
}
|
|
16699
16886
|
const scope = resolveEditScope(loaded.content, normalizedInput);
|
|
16700
16887
|
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
16701
16888
|
const preserveEncoding = normalizedInput.preserveEncoding ?? true;
|
|
16702
16889
|
const requestedEncoding = normalizedInput.encoding ?? "auto";
|
|
16703
|
-
const occurrencesBefore = countOccurrences(scope.selectedText,
|
|
16890
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, input.oldString);
|
|
16704
16891
|
if (replaceAll) {
|
|
16705
16892
|
if (occurrencesBefore === 0) {
|
|
16706
|
-
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText,
|
|
16893
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, input.oldString));
|
|
16707
16894
|
}
|
|
16708
16895
|
} else if (occurrencesBefore === 0) {
|
|
16709
|
-
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText,
|
|
16896
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, input.oldString));
|
|
16710
16897
|
} else if (occurrencesBefore > 1) {
|
|
16711
|
-
throw createTextError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${
|
|
16898
|
+
throw createTextError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${input.oldString}`);
|
|
16712
16899
|
}
|
|
16713
|
-
const alignedNewString = normalizedInput.preserveNewlineStyle === false ?
|
|
16714
|
-
const replaced = replaceAll ? scope.selectedText.split(
|
|
16900
|
+
const alignedNewString = normalizedInput.preserveNewlineStyle === false ? input.newString : alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
16901
|
+
const replaced = replaceAll ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
16715
16902
|
const outputText = `${loaded.content.slice(0, scope.rangeStart)}${replaced}${loaded.content.slice(scope.rangeEnd)}`;
|
|
16716
|
-
const targetEncoding = preserveEncoding || requestedEncoding === "auto" ? loaded.encoding : requestedEncoding;
|
|
16903
|
+
const targetEncoding = preserveEncoding || requestedEncoding === "auto" ? loaded.encoding : resolveExplicitTextEncoding(requestedEncoding, loaded.encoding);
|
|
16717
16904
|
const targetHasBom = preserveEncoding ? loaded.hasBom : targetEncoding === "utf8-bom" || targetEncoding === "utf16le" || targetEncoding === "utf16be";
|
|
16718
16905
|
ensureLossless(outputText, targetEncoding, targetHasBom);
|
|
16719
16906
|
const buffer = encodeText(outputText, targetEncoding, targetHasBom);
|
|
16720
16907
|
await fs3.writeFile(loaded.filePath, buffer);
|
|
16721
16908
|
return {
|
|
16909
|
+
mode: "replace",
|
|
16722
16910
|
filePath: loaded.filePath,
|
|
16723
16911
|
encoding: targetEncoding,
|
|
16724
16912
|
requestedEncoding: loaded.requestedEncoding,
|
|
@@ -16733,40 +16921,39 @@ async function replaceTextFileText(input) {
|
|
|
16733
16921
|
};
|
|
16734
16922
|
}
|
|
16735
16923
|
async function createTextDiffPreview(input) {
|
|
16924
|
+
const mode = resolveEditMode(input.mode);
|
|
16736
16925
|
const loaded = await readWholeTextFile({
|
|
16737
16926
|
filePath: input.filePath,
|
|
16738
16927
|
encoding: input.encoding ?? "auto",
|
|
16739
16928
|
allowExternal: input.allowExternal,
|
|
16740
16929
|
context: input.context
|
|
16741
16930
|
});
|
|
16931
|
+
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
16932
|
+
assertInsertArguments(input);
|
|
16933
|
+
const occurrence = normalizeOptionalPositiveInteger(input.occurrence, "occurrence") ?? 1;
|
|
16934
|
+
const insertResult = buildInsertOutput(
|
|
16935
|
+
loaded.content,
|
|
16936
|
+
mode,
|
|
16937
|
+
input.anchor,
|
|
16938
|
+
input.content,
|
|
16939
|
+
occurrence,
|
|
16940
|
+
resolveInsertIfExists(input.ifExists),
|
|
16941
|
+
loaded.newlineStyle
|
|
16942
|
+
);
|
|
16943
|
+
return {
|
|
16944
|
+
filePath: loaded.filePath,
|
|
16945
|
+
encoding: loaded.encoding,
|
|
16946
|
+
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
16947
|
+
};
|
|
16948
|
+
}
|
|
16949
|
+
assertReplaceArguments(input);
|
|
16742
16950
|
const scope = resolveEditScope(loaded.content, input);
|
|
16743
16951
|
const alignedNewString = alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
16744
16952
|
const replaced = input.replaceAll ?? false ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
16745
|
-
const beforeLines = normalizeNewlines(scope.selectedText).split("\n");
|
|
16746
|
-
const afterLines = normalizeNewlines(replaced).split("\n");
|
|
16747
|
-
const maxLines = Math.max(beforeLines.length, afterLines.length);
|
|
16748
|
-
const lines = [
|
|
16749
|
-
`--- ${path3.basename(loaded.filePath)} (${loaded.encoding})`,
|
|
16750
|
-
`+++ ${path3.basename(loaded.filePath)} (${loaded.encoding})`
|
|
16751
|
-
];
|
|
16752
|
-
for (let index = 0; index < maxLines; index += 1) {
|
|
16753
|
-
const before = beforeLines[index];
|
|
16754
|
-
const after = afterLines[index];
|
|
16755
|
-
if (before !== void 0 && after !== void 0 && before === after) {
|
|
16756
|
-
lines.push(` ${before}`);
|
|
16757
|
-
continue;
|
|
16758
|
-
}
|
|
16759
|
-
if (before !== void 0) {
|
|
16760
|
-
lines.push(`-${before}`);
|
|
16761
|
-
}
|
|
16762
|
-
if (after !== void 0) {
|
|
16763
|
-
lines.push(`+${after}`);
|
|
16764
|
-
}
|
|
16765
|
-
}
|
|
16766
16953
|
return {
|
|
16767
16954
|
filePath: loaded.filePath,
|
|
16768
16955
|
encoding: loaded.encoding,
|
|
16769
|
-
preview:
|
|
16956
|
+
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, scope.selectedText, replaced)
|
|
16770
16957
|
};
|
|
16771
16958
|
}
|
|
16772
16959
|
|
|
@@ -16776,11 +16963,17 @@ var text_edit_default = tool({
|
|
|
16776
16963
|
|
|
16777
16964
|
- Existing files keep their original encoding and BOM by default.
|
|
16778
16965
|
- Existing newline style is preserved by default.
|
|
16779
|
-
-
|
|
16966
|
+
- Replace mode uses oldString/newString.
|
|
16967
|
+
- Insert mode uses mode=insertAfter or insertBefore with anchor/content.`,
|
|
16780
16968
|
args: {
|
|
16781
16969
|
filePath: tool.schema.string().describe("Target file path"),
|
|
16782
|
-
|
|
16783
|
-
|
|
16970
|
+
mode: tool.schema.enum(["replace", "insertAfter", "insertBefore"]).optional().describe("Edit mode, default replace"),
|
|
16971
|
+
oldString: tool.schema.string().optional().describe("Exact text to replace when mode=replace"),
|
|
16972
|
+
newString: tool.schema.string().optional().describe("Replacement text when mode=replace"),
|
|
16973
|
+
anchor: tool.schema.string().optional().describe("Anchor text used by insertAfter/insertBefore"),
|
|
16974
|
+
content: tool.schema.string().optional().describe("Inserted content used by insertAfter/insertBefore"),
|
|
16975
|
+
occurrence: tool.schema.number().int().positive().optional().describe("1-based anchor occurrence for insert mode"),
|
|
16976
|
+
ifExists: tool.schema.enum(["allow", "skip", "error"]).optional().describe("What to do when inserted content already exists at target position"),
|
|
16784
16977
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
|
|
16785
16978
|
startLine: tool.schema.number().int().positive().optional().describe("Start line for scoped edit"),
|
|
16786
16979
|
endLine: tool.schema.number().int().positive().optional().describe("End line for scoped edit"),
|
|
@@ -16794,16 +16987,23 @@ var text_edit_default = tool({
|
|
|
16794
16987
|
async execute(args, context) {
|
|
16795
16988
|
const result = await replaceTextFileText({ ...args, context });
|
|
16796
16989
|
const preview = await createTextDiffPreview({ ...args, context });
|
|
16990
|
+
const title = result.mode === "replace" ? `\u6587\u672C\u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}` : result.inserted ? `\u6587\u672C\u63D2\u5165 ${result.encoding.toUpperCase()} #${result.occurrence}` : `\u6587\u672C\u8DF3\u8FC7 ${result.encoding.toUpperCase()} #${result.occurrence}`;
|
|
16797
16991
|
context.metadata({
|
|
16798
|
-
title
|
|
16992
|
+
title,
|
|
16799
16993
|
metadata: {
|
|
16800
16994
|
filePath: result.filePath,
|
|
16801
16995
|
encoding: result.encoding,
|
|
16802
16996
|
requestedEncoding: result.requestedEncoding,
|
|
16803
16997
|
confidence: result.confidence,
|
|
16804
16998
|
hasBom: result.hasBom,
|
|
16805
|
-
|
|
16806
|
-
|
|
16999
|
+
mode: result.mode,
|
|
17000
|
+
replacements: result.mode === "replace" ? result.replacements : void 0,
|
|
17001
|
+
occurrencesBefore: result.mode === "replace" ? result.occurrencesBefore : void 0,
|
|
17002
|
+
anchor: result.mode === "replace" ? void 0 : result.anchor,
|
|
17003
|
+
occurrence: result.mode === "replace" ? void 0 : result.occurrence,
|
|
17004
|
+
anchorMatches: result.mode === "replace" ? void 0 : result.anchorMatches,
|
|
17005
|
+
inserted: result.mode === "replace" ? void 0 : result.inserted,
|
|
17006
|
+
skipped: result.mode === "replace" ? void 0 : result.skipped,
|
|
16807
17007
|
newlineStyle: result.newlineStyle,
|
|
16808
17008
|
diffPreview: preview.preview
|
|
16809
17009
|
}
|
|
@@ -16327,6 +16327,11 @@ function detectNewlineStyle(text) {
|
|
|
16327
16327
|
// src/lib/text-file.ts
|
|
16328
16328
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16329
16329
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
16330
|
+
function assertStringArgument(value, name) {
|
|
16331
|
+
if (typeof value !== "string") {
|
|
16332
|
+
throw createTextError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
16333
|
+
}
|
|
16334
|
+
}
|
|
16330
16335
|
function isSupportedEncoding(value) {
|
|
16331
16336
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
16332
16337
|
}
|
|
@@ -16535,6 +16540,7 @@ async function readWholeTextFile(input) {
|
|
|
16535
16540
|
}
|
|
16536
16541
|
}
|
|
16537
16542
|
function alignTextToNewlineStyle(text, newlineStyle) {
|
|
16543
|
+
assertStringArgument(text, "text");
|
|
16538
16544
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
16539
16545
|
if (newlineStyle === "crlf") {
|
|
16540
16546
|
return normalized.replace(/\n/g, "\r\n");
|
|
@@ -16545,6 +16551,7 @@ function alignTextToNewlineStyle(text, newlineStyle) {
|
|
|
16545
16551
|
return text;
|
|
16546
16552
|
}
|
|
16547
16553
|
function ensureLossless(input, encoding, hasBom = encoding === "utf8-bom") {
|
|
16554
|
+
assertStringArgument(input, "content");
|
|
16548
16555
|
const buffer = encodeText(input, encoding, hasBom);
|
|
16549
16556
|
const roundTrip = decodeText(buffer, encoding);
|
|
16550
16557
|
if (roundTrip !== input) {
|
|
@@ -16569,6 +16576,7 @@ async function ensureParentDirectory(parent, createDirectories) {
|
|
|
16569
16576
|
}
|
|
16570
16577
|
}
|
|
16571
16578
|
async function writeTextFile(input) {
|
|
16579
|
+
assertStringArgument(input.content, "content");
|
|
16572
16580
|
const requestedEncoding = input.encoding ?? "auto";
|
|
16573
16581
|
assertTextEncodingSupported(requestedEncoding);
|
|
16574
16582
|
const preserveEncoding = input.preserveEncoding ?? true;
|