opencode-gbk-tools 0.1.2 → 0.1.4
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/dist/opencode-tools/gbk_edit.js +131 -11
- package/dist/opencode-tools/gbk_read.js +11 -4
- package/dist/plugin/index.js +121 -15
- package/dist/release-manifest.json +3 -3
- package/package.json +1 -1
|
@@ -16315,11 +16315,32 @@ function assertPositiveInteger(value, name) {
|
|
|
16315
16315
|
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
|
|
16316
16316
|
}
|
|
16317
16317
|
}
|
|
16318
|
+
function normalizeOptionalPositiveInteger(value, name) {
|
|
16319
|
+
if (value === void 0 || value === -1) {
|
|
16320
|
+
return void 0;
|
|
16321
|
+
}
|
|
16322
|
+
assertPositiveInteger(value, name);
|
|
16323
|
+
return Number(value);
|
|
16324
|
+
}
|
|
16318
16325
|
function assertNotBinary(buffer) {
|
|
16319
16326
|
if (buffer.includes(0)) {
|
|
16320
16327
|
throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
|
|
16321
16328
|
}
|
|
16322
16329
|
}
|
|
16330
|
+
function detectNewlineStyle(text) {
|
|
16331
|
+
const crlfCount = (text.match(/\r\n/g) ?? []).length;
|
|
16332
|
+
const lfCount = (text.match(/(^|[^\r])\n/g) ?? []).length;
|
|
16333
|
+
if (crlfCount > 0 && lfCount === 0) {
|
|
16334
|
+
return "crlf";
|
|
16335
|
+
}
|
|
16336
|
+
if (lfCount > 0 && crlfCount === 0) {
|
|
16337
|
+
return "lf";
|
|
16338
|
+
}
|
|
16339
|
+
if (crlfCount > 0 && lfCount > 0) {
|
|
16340
|
+
return "mixed";
|
|
16341
|
+
}
|
|
16342
|
+
return "none";
|
|
16343
|
+
}
|
|
16323
16344
|
function applyLineRange(text, startLine, endLine) {
|
|
16324
16345
|
if (startLine === void 0 && endLine === void 0) {
|
|
16325
16346
|
return {
|
|
@@ -16401,6 +16422,84 @@ function resolveEditScope(text, input) {
|
|
|
16401
16422
|
rangeEnd: anchored.rangeStart + lineRanged.rangeEnd
|
|
16402
16423
|
};
|
|
16403
16424
|
}
|
|
16425
|
+
function normalizeNewlines(text) {
|
|
16426
|
+
return text.replace(/\r\n/g, "\n");
|
|
16427
|
+
}
|
|
16428
|
+
function trimRightSpaces(text) {
|
|
16429
|
+
return text.replace(/[ \t]+$/g, "");
|
|
16430
|
+
}
|
|
16431
|
+
function splitNormalizedLines(text) {
|
|
16432
|
+
return normalizeNewlines(text).split("\n");
|
|
16433
|
+
}
|
|
16434
|
+
function trimTrailingEmptyLines(lines) {
|
|
16435
|
+
const result = [...lines];
|
|
16436
|
+
while (result.length > 0 && trimRightSpaces(result[result.length - 1]) === "") {
|
|
16437
|
+
result.pop();
|
|
16438
|
+
}
|
|
16439
|
+
return result;
|
|
16440
|
+
}
|
|
16441
|
+
function getNearestContext(content, oldString) {
|
|
16442
|
+
const lines = content.split(/\r?\n/);
|
|
16443
|
+
const oldLines = normalizeNewlines(oldString).split("\n").filter(Boolean);
|
|
16444
|
+
const firstToken = oldLines[0] || oldString.trim();
|
|
16445
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
16446
|
+
if (lines[index].includes(firstToken)) {
|
|
16447
|
+
return lines.slice(index, index + 4).join("\n");
|
|
16448
|
+
}
|
|
16449
|
+
}
|
|
16450
|
+
return lines.slice(0, 4).join("\n");
|
|
16451
|
+
}
|
|
16452
|
+
function buildNoMatchMessage(content, oldString) {
|
|
16453
|
+
return [
|
|
16454
|
+
"\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
|
|
16455
|
+
"oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\u884C/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\u3002",
|
|
16456
|
+
"\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFF1A",
|
|
16457
|
+
getNearestContext(content, oldString)
|
|
16458
|
+
].join("\n");
|
|
16459
|
+
}
|
|
16460
|
+
function tryLooseBlockReplace(content, oldString, newString) {
|
|
16461
|
+
const normalizedContent = normalizeNewlines(content);
|
|
16462
|
+
const contentLines = splitNormalizedLines(content);
|
|
16463
|
+
const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
|
|
16464
|
+
const newLines = splitNormalizedLines(newString);
|
|
16465
|
+
if (oldLines.length === 0) {
|
|
16466
|
+
return null;
|
|
16467
|
+
}
|
|
16468
|
+
for (let start = 0; start < contentLines.length; start += 1) {
|
|
16469
|
+
let contentIndex = start;
|
|
16470
|
+
let oldIndex = 0;
|
|
16471
|
+
while (oldIndex < oldLines.length && contentIndex < contentLines.length) {
|
|
16472
|
+
const expected = trimRightSpaces(oldLines[oldIndex]);
|
|
16473
|
+
const actual = trimRightSpaces(contentLines[contentIndex]);
|
|
16474
|
+
if (expected === actual) {
|
|
16475
|
+
oldIndex += 1;
|
|
16476
|
+
contentIndex += 1;
|
|
16477
|
+
continue;
|
|
16478
|
+
}
|
|
16479
|
+
if (actual === "") {
|
|
16480
|
+
contentIndex += 1;
|
|
16481
|
+
continue;
|
|
16482
|
+
}
|
|
16483
|
+
if (expected === "") {
|
|
16484
|
+
oldIndex += 1;
|
|
16485
|
+
continue;
|
|
16486
|
+
}
|
|
16487
|
+
break;
|
|
16488
|
+
}
|
|
16489
|
+
if (oldIndex === oldLines.length) {
|
|
16490
|
+
const replacedNormalized = [
|
|
16491
|
+
...contentLines.slice(0, start),
|
|
16492
|
+
...newLines,
|
|
16493
|
+
...contentLines.slice(contentIndex)
|
|
16494
|
+
].join("\n");
|
|
16495
|
+
return {
|
|
16496
|
+
occurrencesBefore: 1,
|
|
16497
|
+
content: detectNewlineStyle(content) === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
|
|
16498
|
+
};
|
|
16499
|
+
}
|
|
16500
|
+
}
|
|
16501
|
+
return null;
|
|
16502
|
+
}
|
|
16404
16503
|
function countOccurrences(text, target) {
|
|
16405
16504
|
if (target.length === 0) {
|
|
16406
16505
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -16558,12 +16657,17 @@ async function replaceLargeGbkFileText(input) {
|
|
|
16558
16657
|
}
|
|
16559
16658
|
}
|
|
16560
16659
|
async function replaceGbkFileText(input) {
|
|
16660
|
+
const normalizedInput = {
|
|
16661
|
+
...input,
|
|
16662
|
+
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
16663
|
+
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
16664
|
+
};
|
|
16561
16665
|
if (input.oldString.length === 0) {
|
|
16562
16666
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16563
16667
|
}
|
|
16564
|
-
const replaceAll =
|
|
16565
|
-
const resolved = await resolveReadableGbkFile(
|
|
16566
|
-
const hasScopedRange =
|
|
16668
|
+
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
16669
|
+
const resolved = await resolveReadableGbkFile(normalizedInput);
|
|
16670
|
+
const hasScopedRange = normalizedInput.startLine !== void 0 || normalizedInput.endLine !== void 0 || normalizedInput.startAnchor !== void 0 || normalizedInput.endAnchor !== void 0;
|
|
16567
16671
|
if (resolved.stat.size >= STREAMING_FILE_SIZE_THRESHOLD_BYTES && !hasScopedRange) {
|
|
16568
16672
|
return await replaceLargeGbkFileText({
|
|
16569
16673
|
...resolved,
|
|
@@ -16573,18 +16677,34 @@ async function replaceGbkFileText(input) {
|
|
|
16573
16677
|
});
|
|
16574
16678
|
}
|
|
16575
16679
|
const current = await readWholeGbkTextFile(resolved);
|
|
16576
|
-
const scope = resolveEditScope(current.content,
|
|
16577
|
-
const occurrencesBefore = countOccurrences(scope.selectedText,
|
|
16680
|
+
const scope = resolveEditScope(current.content, normalizedInput);
|
|
16681
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, normalizedInput.oldString);
|
|
16682
|
+
if (!replaceAll && occurrencesBefore === 0) {
|
|
16683
|
+
const loose = tryLooseBlockReplace(scope.selectedText, normalizedInput.oldString, normalizedInput.newString);
|
|
16684
|
+
if (loose !== null) {
|
|
16685
|
+
const outputText2 = `${current.content.slice(0, scope.rangeStart)}${loose.content}${current.content.slice(scope.rangeEnd)}`;
|
|
16686
|
+
const buffer2 = import_iconv_lite.default.encode(outputText2, current.encoding);
|
|
16687
|
+
await fs2.writeFile(current.filePath, buffer2);
|
|
16688
|
+
return {
|
|
16689
|
+
filePath: current.filePath,
|
|
16690
|
+
encoding: current.encoding,
|
|
16691
|
+
replacements: 1,
|
|
16692
|
+
occurrencesBefore: loose.occurrencesBefore,
|
|
16693
|
+
bytesRead: current.bytesRead,
|
|
16694
|
+
bytesWritten: buffer2.byteLength
|
|
16695
|
+
};
|
|
16696
|
+
}
|
|
16697
|
+
}
|
|
16578
16698
|
if (replaceAll) {
|
|
16579
16699
|
if (occurrencesBefore === 0) {
|
|
16580
|
-
throw createGbkError("GBK_NO_MATCH",
|
|
16700
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
|
|
16581
16701
|
}
|
|
16582
16702
|
} else if (occurrencesBefore === 0) {
|
|
16583
|
-
throw createGbkError("GBK_NO_MATCH",
|
|
16703
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
|
|
16584
16704
|
} else if (occurrencesBefore > 1) {
|
|
16585
|
-
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${
|
|
16705
|
+
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${normalizedInput.oldString}`);
|
|
16586
16706
|
}
|
|
16587
|
-
const replaced = replaceAll ? scope.selectedText.split(
|
|
16707
|
+
const replaced = replaceAll ? scope.selectedText.split(normalizedInput.oldString).join(normalizedInput.newString) : scope.selectedText.replace(normalizedInput.oldString, normalizedInput.newString);
|
|
16588
16708
|
const outputText = `${current.content.slice(0, scope.rangeStart)}${replaced}${current.content.slice(scope.rangeEnd)}`;
|
|
16589
16709
|
const buffer = import_iconv_lite.default.encode(outputText, current.encoding);
|
|
16590
16710
|
await fs2.writeFile(current.filePath, buffer);
|
|
@@ -16606,8 +16726,8 @@ var gbk_edit_default = tool({
|
|
|
16606
16726
|
oldString: tool.schema.string().describe("Text to replace"),
|
|
16607
16727
|
newString: tool.schema.string().describe("Replacement text"),
|
|
16608
16728
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
|
|
16609
|
-
startLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based start line"),
|
|
16610
|
-
endLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based end line"),
|
|
16729
|
+
startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based start line"),
|
|
16730
|
+
endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based end line"),
|
|
16611
16731
|
startAnchor: tool.schema.string().optional().describe("Restrict edit to content after this anchor"),
|
|
16612
16732
|
endAnchor: tool.schema.string().optional().describe("Restrict edit to content before this anchor"),
|
|
16613
16733
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
|
|
@@ -16315,6 +16315,13 @@ function assertPositiveInteger(value, name) {
|
|
|
16315
16315
|
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
|
|
16316
16316
|
}
|
|
16317
16317
|
}
|
|
16318
|
+
function normalizeOptionalPositiveInteger(value, name) {
|
|
16319
|
+
if (value === void 0 || value === -1) {
|
|
16320
|
+
return void 0;
|
|
16321
|
+
}
|
|
16322
|
+
assertPositiveInteger(value, name);
|
|
16323
|
+
return Number(value);
|
|
16324
|
+
}
|
|
16318
16325
|
function assertNotBinary(buffer) {
|
|
16319
16326
|
if (buffer.includes(0)) {
|
|
16320
16327
|
throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
|
|
@@ -16556,8 +16563,8 @@ function createTailCollector(limit) {
|
|
|
16556
16563
|
};
|
|
16557
16564
|
}
|
|
16558
16565
|
async function readGbkFile(input) {
|
|
16559
|
-
const offset = input.offset ?? 1;
|
|
16560
|
-
const limit = input.limit ?? 2e3;
|
|
16566
|
+
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
16567
|
+
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
16561
16568
|
const tail = input.tail ?? false;
|
|
16562
16569
|
const resolved = await resolveReadableGbkFile(input);
|
|
16563
16570
|
if (resolved.stat.size < STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
|
|
@@ -16600,8 +16607,8 @@ var gbk_read_default = tool({
|
|
|
16600
16607
|
description: "Read GBK encoded text files",
|
|
16601
16608
|
args: {
|
|
16602
16609
|
filePath: tool.schema.string().describe("Target file path"),
|
|
16603
|
-
offset: tool.schema.number().int().positive().optional().describe("1-based start line"),
|
|
16604
|
-
limit: tool.schema.number().int().positive().optional().describe("Number of lines to read"),
|
|
16610
|
+
offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line"),
|
|
16611
|
+
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read"),
|
|
16605
16612
|
tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
|
|
16606
16613
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
|
|
16607
16614
|
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
package/dist/plugin/index.js
CHANGED
|
@@ -16315,6 +16315,13 @@ function assertPositiveInteger(value, name) {
|
|
|
16315
16315
|
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
|
|
16316
16316
|
}
|
|
16317
16317
|
}
|
|
16318
|
+
function normalizeOptionalPositiveInteger(value, name) {
|
|
16319
|
+
if (value === void 0 || value === -1) {
|
|
16320
|
+
return void 0;
|
|
16321
|
+
}
|
|
16322
|
+
assertPositiveInteger(value, name);
|
|
16323
|
+
return Number(value);
|
|
16324
|
+
}
|
|
16318
16325
|
function assertNotBinary(buffer) {
|
|
16319
16326
|
if (buffer.includes(0)) {
|
|
16320
16327
|
throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
|
|
@@ -16466,6 +16473,84 @@ function resolveEditScope(text, input) {
|
|
|
16466
16473
|
rangeEnd: anchored.rangeStart + lineRanged.rangeEnd
|
|
16467
16474
|
};
|
|
16468
16475
|
}
|
|
16476
|
+
function normalizeNewlines(text) {
|
|
16477
|
+
return text.replace(/\r\n/g, "\n");
|
|
16478
|
+
}
|
|
16479
|
+
function trimRightSpaces(text) {
|
|
16480
|
+
return text.replace(/[ \t]+$/g, "");
|
|
16481
|
+
}
|
|
16482
|
+
function splitNormalizedLines(text) {
|
|
16483
|
+
return normalizeNewlines(text).split("\n");
|
|
16484
|
+
}
|
|
16485
|
+
function trimTrailingEmptyLines(lines) {
|
|
16486
|
+
const result = [...lines];
|
|
16487
|
+
while (result.length > 0 && trimRightSpaces(result[result.length - 1]) === "") {
|
|
16488
|
+
result.pop();
|
|
16489
|
+
}
|
|
16490
|
+
return result;
|
|
16491
|
+
}
|
|
16492
|
+
function getNearestContext(content, oldString) {
|
|
16493
|
+
const lines = content.split(/\r?\n/);
|
|
16494
|
+
const oldLines = normalizeNewlines(oldString).split("\n").filter(Boolean);
|
|
16495
|
+
const firstToken = oldLines[0] || oldString.trim();
|
|
16496
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
16497
|
+
if (lines[index].includes(firstToken)) {
|
|
16498
|
+
return lines.slice(index, index + 4).join("\n");
|
|
16499
|
+
}
|
|
16500
|
+
}
|
|
16501
|
+
return lines.slice(0, 4).join("\n");
|
|
16502
|
+
}
|
|
16503
|
+
function buildNoMatchMessage(content, oldString) {
|
|
16504
|
+
return [
|
|
16505
|
+
"\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
|
|
16506
|
+
"oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\u884C/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\u3002",
|
|
16507
|
+
"\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFF1A",
|
|
16508
|
+
getNearestContext(content, oldString)
|
|
16509
|
+
].join("\n");
|
|
16510
|
+
}
|
|
16511
|
+
function tryLooseBlockReplace(content, oldString, newString) {
|
|
16512
|
+
const normalizedContent = normalizeNewlines(content);
|
|
16513
|
+
const contentLines = splitNormalizedLines(content);
|
|
16514
|
+
const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
|
|
16515
|
+
const newLines = splitNormalizedLines(newString);
|
|
16516
|
+
if (oldLines.length === 0) {
|
|
16517
|
+
return null;
|
|
16518
|
+
}
|
|
16519
|
+
for (let start = 0; start < contentLines.length; start += 1) {
|
|
16520
|
+
let contentIndex = start;
|
|
16521
|
+
let oldIndex = 0;
|
|
16522
|
+
while (oldIndex < oldLines.length && contentIndex < contentLines.length) {
|
|
16523
|
+
const expected = trimRightSpaces(oldLines[oldIndex]);
|
|
16524
|
+
const actual = trimRightSpaces(contentLines[contentIndex]);
|
|
16525
|
+
if (expected === actual) {
|
|
16526
|
+
oldIndex += 1;
|
|
16527
|
+
contentIndex += 1;
|
|
16528
|
+
continue;
|
|
16529
|
+
}
|
|
16530
|
+
if (actual === "") {
|
|
16531
|
+
contentIndex += 1;
|
|
16532
|
+
continue;
|
|
16533
|
+
}
|
|
16534
|
+
if (expected === "") {
|
|
16535
|
+
oldIndex += 1;
|
|
16536
|
+
continue;
|
|
16537
|
+
}
|
|
16538
|
+
break;
|
|
16539
|
+
}
|
|
16540
|
+
if (oldIndex === oldLines.length) {
|
|
16541
|
+
const replacedNormalized = [
|
|
16542
|
+
...contentLines.slice(0, start),
|
|
16543
|
+
...newLines,
|
|
16544
|
+
...contentLines.slice(contentIndex)
|
|
16545
|
+
].join("\n");
|
|
16546
|
+
return {
|
|
16547
|
+
occurrencesBefore: 1,
|
|
16548
|
+
content: detectNewlineStyle(content) === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
|
|
16549
|
+
};
|
|
16550
|
+
}
|
|
16551
|
+
}
|
|
16552
|
+
return null;
|
|
16553
|
+
}
|
|
16469
16554
|
function countOccurrences(text, target) {
|
|
16470
16555
|
if (target.length === 0) {
|
|
16471
16556
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -16727,8 +16812,8 @@ async function replaceLargeGbkFileText(input) {
|
|
|
16727
16812
|
}
|
|
16728
16813
|
}
|
|
16729
16814
|
async function readGbkFile(input) {
|
|
16730
|
-
const offset = input.offset ?? 1;
|
|
16731
|
-
const limit = input.limit ?? 2e3;
|
|
16815
|
+
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
16816
|
+
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
16732
16817
|
const tail = input.tail ?? false;
|
|
16733
16818
|
const resolved = await resolveReadableGbkFile(input);
|
|
16734
16819
|
if (resolved.stat.size < STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
|
|
@@ -16766,12 +16851,17 @@ async function readGbkFile(input) {
|
|
|
16766
16851
|
};
|
|
16767
16852
|
}
|
|
16768
16853
|
async function replaceGbkFileText(input) {
|
|
16854
|
+
const normalizedInput = {
|
|
16855
|
+
...input,
|
|
16856
|
+
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
16857
|
+
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
16858
|
+
};
|
|
16769
16859
|
if (input.oldString.length === 0) {
|
|
16770
16860
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16771
16861
|
}
|
|
16772
|
-
const replaceAll =
|
|
16773
|
-
const resolved = await resolveReadableGbkFile(
|
|
16774
|
-
const hasScopedRange =
|
|
16862
|
+
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
16863
|
+
const resolved = await resolveReadableGbkFile(normalizedInput);
|
|
16864
|
+
const hasScopedRange = normalizedInput.startLine !== void 0 || normalizedInput.endLine !== void 0 || normalizedInput.startAnchor !== void 0 || normalizedInput.endAnchor !== void 0;
|
|
16775
16865
|
if (resolved.stat.size >= STREAMING_FILE_SIZE_THRESHOLD_BYTES && !hasScopedRange) {
|
|
16776
16866
|
return await replaceLargeGbkFileText({
|
|
16777
16867
|
...resolved,
|
|
@@ -16781,18 +16871,34 @@ async function replaceGbkFileText(input) {
|
|
|
16781
16871
|
});
|
|
16782
16872
|
}
|
|
16783
16873
|
const current = await readWholeGbkTextFile(resolved);
|
|
16784
|
-
const scope = resolveEditScope(current.content,
|
|
16785
|
-
const occurrencesBefore = countOccurrences(scope.selectedText,
|
|
16874
|
+
const scope = resolveEditScope(current.content, normalizedInput);
|
|
16875
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, normalizedInput.oldString);
|
|
16876
|
+
if (!replaceAll && occurrencesBefore === 0) {
|
|
16877
|
+
const loose = tryLooseBlockReplace(scope.selectedText, normalizedInput.oldString, normalizedInput.newString);
|
|
16878
|
+
if (loose !== null) {
|
|
16879
|
+
const outputText2 = `${current.content.slice(0, scope.rangeStart)}${loose.content}${current.content.slice(scope.rangeEnd)}`;
|
|
16880
|
+
const buffer2 = import_iconv_lite.default.encode(outputText2, current.encoding);
|
|
16881
|
+
await fs2.writeFile(current.filePath, buffer2);
|
|
16882
|
+
return {
|
|
16883
|
+
filePath: current.filePath,
|
|
16884
|
+
encoding: current.encoding,
|
|
16885
|
+
replacements: 1,
|
|
16886
|
+
occurrencesBefore: loose.occurrencesBefore,
|
|
16887
|
+
bytesRead: current.bytesRead,
|
|
16888
|
+
bytesWritten: buffer2.byteLength
|
|
16889
|
+
};
|
|
16890
|
+
}
|
|
16891
|
+
}
|
|
16786
16892
|
if (replaceAll) {
|
|
16787
16893
|
if (occurrencesBefore === 0) {
|
|
16788
|
-
throw createGbkError("GBK_NO_MATCH",
|
|
16894
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
|
|
16789
16895
|
}
|
|
16790
16896
|
} else if (occurrencesBefore === 0) {
|
|
16791
|
-
throw createGbkError("GBK_NO_MATCH",
|
|
16897
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
|
|
16792
16898
|
} else if (occurrencesBefore > 1) {
|
|
16793
|
-
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${
|
|
16899
|
+
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${normalizedInput.oldString}`);
|
|
16794
16900
|
}
|
|
16795
|
-
const replaced = replaceAll ? scope.selectedText.split(
|
|
16901
|
+
const replaced = replaceAll ? scope.selectedText.split(normalizedInput.oldString).join(normalizedInput.newString) : scope.selectedText.replace(normalizedInput.oldString, normalizedInput.newString);
|
|
16796
16902
|
const outputText = `${current.content.slice(0, scope.rangeStart)}${replaced}${current.content.slice(scope.rangeEnd)}`;
|
|
16797
16903
|
const buffer = import_iconv_lite.default.encode(outputText, current.encoding);
|
|
16798
16904
|
await fs2.writeFile(current.filePath, buffer);
|
|
@@ -16871,8 +16977,8 @@ var gbk_edit_default = tool({
|
|
|
16871
16977
|
oldString: tool.schema.string().describe("Text to replace"),
|
|
16872
16978
|
newString: tool.schema.string().describe("Replacement text"),
|
|
16873
16979
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
|
|
16874
|
-
startLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based start line"),
|
|
16875
|
-
endLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based end line"),
|
|
16980
|
+
startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based start line"),
|
|
16981
|
+
endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based end line"),
|
|
16876
16982
|
startAnchor: tool.schema.string().optional().describe("Restrict edit to content after this anchor"),
|
|
16877
16983
|
endAnchor: tool.schema.string().optional().describe("Restrict edit to content before this anchor"),
|
|
16878
16984
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
|
|
@@ -16889,8 +16995,8 @@ var gbk_read_default = tool({
|
|
|
16889
16995
|
description: "Read GBK encoded text files",
|
|
16890
16996
|
args: {
|
|
16891
16997
|
filePath: tool.schema.string().describe("Target file path"),
|
|
16892
|
-
offset: tool.schema.number().int().positive().optional().describe("1-based start line"),
|
|
16893
|
-
limit: tool.schema.number().int().positive().optional().describe("Number of lines to read"),
|
|
16998
|
+
offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line"),
|
|
16999
|
+
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read"),
|
|
16894
17000
|
tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
|
|
16895
17001
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
|
|
16896
17002
|
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
3
|
"packageName": "opencode-gbk-tools",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.4",
|
|
5
5
|
"artifacts": [
|
|
6
6
|
{
|
|
7
7
|
"relativePath": "tools/gbk_edit.js",
|
|
8
8
|
"kind": "tool",
|
|
9
|
-
"expectedHash": "
|
|
9
|
+
"expectedHash": "6a8a26850a21e47a9ececc2b8606452491a654d5fc79ef908cfbcea9e6c5b7e3",
|
|
10
10
|
"hashAlgorithm": "sha256"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"relativePath": "tools/gbk_read.js",
|
|
14
14
|
"kind": "tool",
|
|
15
|
-
"expectedHash": "
|
|
15
|
+
"expectedHash": "c61e5ee95e05e96965c76e5dc2f18b3943bc3c2a76567b415839442358024d46",
|
|
16
16
|
"hashAlgorithm": "sha256"
|
|
17
17
|
},
|
|
18
18
|
{
|