@x-oasis/diff-match-patch 0.1.1 → 0.1.3

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.
@@ -1,11 +1,11 @@
1
1
 
2
- > @x-oasis/diff-match-patch@0.1.1 build /home/runner/work/x-oasis/x-oasis/packages/diff/diff-match-patch
2
+ > @x-oasis/diff-match-patch@0.1.3 build /home/runner/work/x-oasis/x-oasis/packages/diff/diff-match-patch
3
3
  > tsdx build --tsconfig tsconfig.build.json
4
4
 
5
5
  @rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`.
6
6
  @rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`.
7
7
  ⠙ Creating entry file
8
- ✓ Creating entry file 4.4 secs
8
+ ✓ Creating entry file 3.6 secs
9
9
  ⠙ Building modules
10
10
  ⠹ Building modules
11
11
  ⠸ Building modules
@@ -13,10 +13,7 @@
13
13
  ⠴ Building modules
14
14
  ⠦ Building modules
15
15
  ⠧ Building modules
16
- ⠇ Building modules
17
- ⠏ Building modules
18
- ⠋ Building modules
19
16
  [tsdx]: Your rootDir is currently set to "./". Please change your rootDir to "./src".
20
17
  TSDX has deprecated setting tsconfig.compilerOptions.rootDir to "./" as it caused buggy output for declarationMaps and more.
21
18
  You may also need to change your include to remove "test", which also caused declarations to be unnecessarily created for test files.
22
- ✓ Building modules 8.9 secs
19
+ ✓ Building modules 7.3 secs
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @x-oasis/diff-match-patch
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - bf3f705: bump
8
+ - 1cd9ae3: bump
9
+ - 0699388: bump
10
+ - 9ef6a8f: fix: log package.json
11
+ - Updated dependencies [bf3f705]
12
+ - Updated dependencies [1cd9ae3]
13
+ - Updated dependencies [0699388]
14
+ - Updated dependencies [9ef6a8f]
15
+ - @x-oasis/log@0.1.3
16
+
17
+ ## 0.1.2
18
+
19
+ ### Patch Changes
20
+
21
+ - f7326fb: bump diff
22
+ - Updated dependencies [f7326fb]
23
+ - @x-oasis/log@0.1.2
24
+
3
25
  ## 0.1.1
4
26
 
5
27
  ### Patch Changes
@@ -2,8 +2,12 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ var log = require('@x-oasis/log');
5
6
  var diffMatchPatch = require('diff-match-patch');
6
7
 
8
+ var debugLogger = /*#__PURE__*/new log.Logger({
9
+ defaultPrefix: '[diff-match-patch]'
10
+ });
7
11
  var DIFF_DELETE = -1;
8
12
  var DIFF_INSERT = 1;
9
13
  var DIFF_EQUAL = 0;
@@ -11,11 +15,19 @@ var FileRestoreManager = /*#__PURE__*/function () {
11
15
  function FileRestoreManager(originalContent) {
12
16
  this.originalContent = originalContent;
13
17
  this.dmp = new diffMatchPatch.diff_match_patch();
18
+ debugLogger.debug('FileRestoreManager created', {
19
+ originalLength: originalContent.length
20
+ });
14
21
  }
15
22
  var _proto = FileRestoreManager.prototype;
16
23
  _proto.restoreRange = function restoreRange(currentContent, options) {
17
24
  var startOffset = options.startOffset,
18
25
  endOffset = options.endOffset;
26
+ debugLogger.debug('restoreRange called', {
27
+ startOffset: startOffset,
28
+ endOffset: endOffset,
29
+ currentLength: currentContent.length
30
+ });
19
31
  if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {
20
32
  throw new Error('Invalid offset range');
21
33
  }
@@ -24,19 +36,41 @@ var FileRestoreManager = /*#__PURE__*/function () {
24
36
  }
25
37
  var diffs = this.dmp.diff_main(this.originalContent, currentContent);
26
38
  this.dmp.diff_cleanupSemantic(diffs);
39
+ debugLogger.debug('diffs computed', {
40
+ diffCount: diffs.length,
41
+ hasChanges: diffs.some(function (_ref) {
42
+ var op = _ref[0];
43
+ return op !== DIFF_EQUAL;
44
+ })
45
+ });
27
46
  var originalRange = this.mapCurrentRangeToOriginal(diffs, startOffset, endOffset);
28
47
  var originalRangeContent = this.originalContent.substring(originalRange.start, originalRange.end);
29
48
  var currentRangeContent = currentContent.substring(startOffset, endOffset);
30
- console.log('[restoreRange] Debug Info:');
31
- console.log(" startOffset: " + startOffset + ", endOffset: " + endOffset);
32
- console.log(" Current range content: " + JSON.stringify(currentRangeContent));
33
- console.log(" Original range mapping: " + originalRange.start + "-" + originalRange.end);
34
- console.log(" Will restore to: " + JSON.stringify(originalRangeContent));
35
- console.log(" Content will change: " + (currentRangeContent !== originalRangeContent));
49
+ var contentWillChange = currentRangeContent !== originalRangeContent;
50
+ debugLogger.debug('range mapping resolved', {
51
+ startOffset: startOffset,
52
+ endOffset: endOffset,
53
+ originalRange: {
54
+ start: originalRange.start,
55
+ end: originalRange.end
56
+ },
57
+ currentRangePreview: currentRangeContent.length > 80 ? currentRangeContent.slice(0, 40) + "..." + currentRangeContent.slice(-40) : currentRangeContent,
58
+ originalRangePreview: originalRangeContent.length > 80 ? originalRangeContent.slice(0, 40) + "..." + originalRangeContent.slice(-40) : originalRangeContent,
59
+ contentWillChange: contentWillChange
60
+ });
36
61
  var restoredContent = currentContent.substring(0, startOffset) + originalRangeContent + currentContent.substring(endOffset);
62
+ debugLogger.debug('restoreRange done', {
63
+ restoredLength: restoredContent.length,
64
+ contentChanged: contentWillChange
65
+ });
37
66
  return restoredContent;
38
67
  };
39
68
  _proto.mapCurrentRangeToOriginal = function mapCurrentRangeToOriginal(diffs, currentStart, currentEnd) {
69
+ debugLogger.debug('mapCurrentRangeToOriginal called', {
70
+ currentStart: currentStart,
71
+ currentEnd: currentEnd,
72
+ diffCount: diffs.length
73
+ });
40
74
  var currentOffset = 0;
41
75
  var originalOffset = 0;
42
76
  var originalStart = null;
@@ -96,10 +130,12 @@ var FileRestoreManager = /*#__PURE__*/function () {
96
130
  if (originalEnd === null) {
97
131
  originalEnd = originalOffset;
98
132
  }
99
- return {
133
+ var result = {
100
134
  start: originalStart,
101
135
  end: originalEnd
102
136
  };
137
+ debugLogger.debug('mapCurrentRangeToOriginal result', result);
138
+ return result;
103
139
  };
104
140
  _proto.getOriginalContent = function getOriginalContent() {
105
141
  return this.originalContent;
@@ -110,12 +146,17 @@ var FileRestoreManager = /*#__PURE__*/function () {
110
146
  _proto.debugRestoreRange = function debugRestoreRange(currentContent, options) {
111
147
  var startOffset = options.startOffset,
112
148
  endOffset = options.endOffset;
149
+ debugLogger.debug('debugRestoreRange called', {
150
+ startOffset: startOffset,
151
+ endOffset: endOffset,
152
+ currentLength: currentContent.length
153
+ });
113
154
  var diffs = this.dmp.diff_main(this.originalContent, currentContent);
114
155
  this.dmp.diff_cleanupSemantic(diffs);
115
156
  var originalRange = this.mapCurrentRangeToOriginal(diffs, startOffset, endOffset);
116
157
  var originalRangeContent = this.originalContent.substring(originalRange.start, originalRange.end);
117
158
  var currentRangeContent = currentContent.substring(startOffset, endOffset);
118
- return {
159
+ var result = {
119
160
  hasChanges: this.originalContent !== currentContent,
120
161
  originalRange: originalRange,
121
162
  currentRange: {
@@ -126,10 +167,27 @@ var FileRestoreManager = /*#__PURE__*/function () {
126
167
  currentContent: currentRangeContent,
127
168
  willChange: originalRangeContent !== currentRangeContent
128
169
  };
170
+ debugLogger.debug('debugRestoreRange result', {
171
+ hasChanges: result.hasChanges,
172
+ willChange: result.willChange,
173
+ originalRange: result.originalRange,
174
+ currentRange: result.currentRange
175
+ });
176
+ return result;
129
177
  };
130
178
  return FileRestoreManager;
131
179
  }();
132
- function restoreRange(originalContent, currentContent, startOffset, endOffset) {
180
+ function restoreRange(options) {
181
+ var originalContent = options.originalContent,
182
+ currentContent = options.currentContent,
183
+ startOffset = options.startOffset,
184
+ endOffset = options.endOffset;
185
+ debugLogger.debug('restoreRange (standalone) called', {
186
+ startOffset: startOffset,
187
+ endOffset: endOffset,
188
+ originalLength: originalContent.length,
189
+ currentLength: currentContent.length
190
+ });
133
191
  var manager = new FileRestoreManager(originalContent);
134
192
  return manager.restoreRange(currentContent, {
135
193
  startOffset: startOffset,
@@ -1 +1 @@
1
- {"version":3,"file":"diff-match-patch.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import { diff_match_patch } from 'diff-match-patch';\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n // 调试信息\n console.log('[restoreRange] Debug Info:');\n console.log(` startOffset: ${startOffset}, endOffset: ${endOffset}`);\n console.log(\n ` Current range content: ${JSON.stringify(currentRangeContent)}`\n );\n console.log(\n ` Original range mapping: ${originalRange.start}-${originalRange.end}`\n );\n console.log(` Will restore to: ${JSON.stringify(originalRangeContent)}`);\n console.log(\n ` Content will change: ${currentRangeContent !== originalRangeContent}`\n );\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n return { start: originalStart, end: originalEnd };\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n return {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(\n originalContent: string,\n currentContent: string,\n startOffset: number,\n endOffset: number\n): string {\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["DIFF_DELETE","DIFF_INSERT","DIFF_EQUAL","FileRestoreManager","originalContent","dmp","diff_match_patch","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","Error","length","diffs","diff_main","diff_cleanupSemantic","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","console","log","JSON","stringify","restoredContent","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","text","textLength","nextDiff","rangeStart","rangeEnd","offsetInRange","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","hasChanges","currentRange","willChange","manager"],"mappings":"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"}
1
+ {"version":3,"file":"diff-match-patch.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["import { Logger } from '@x-oasis/log';\nimport { diff_match_patch } from 'diff-match-patch';\n\nconst debugLogger = new Logger({ defaultPrefix: '[diff-match-patch]' });\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n debugLogger.debug('FileRestoreManager created', {\n originalLength: originalContent.length,\n });\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('restoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n debugLogger.debug('diffs computed', {\n diffCount: diffs.length,\n hasChanges: diffs.some(([op]) => op !== DIFF_EQUAL),\n });\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const contentWillChange = currentRangeContent !== originalRangeContent;\n debugLogger.debug('range mapping resolved', {\n startOffset,\n endOffset,\n originalRange: { start: originalRange.start, end: originalRange.end },\n currentRangePreview:\n currentRangeContent.length > 80\n ? `${currentRangeContent.slice(0, 40)}...${currentRangeContent.slice(\n -40\n )}`\n : currentRangeContent,\n originalRangePreview:\n originalRangeContent.length > 80\n ? `${originalRangeContent.slice(\n 0,\n 40\n )}...${originalRangeContent.slice(-40)}`\n : originalRangeContent,\n contentWillChange,\n });\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n debugLogger.debug('restoreRange done', {\n restoredLength: restoredContent.length,\n contentChanged: contentWillChange,\n });\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n debugLogger.debug('mapCurrentRangeToOriginal called', {\n currentStart,\n currentEnd,\n diffCount: diffs.length,\n });\n\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n const result = { start: originalStart, end: originalEnd };\n debugLogger.debug('mapCurrentRangeToOriginal result', result);\n return result;\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('debugRestoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const result = {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n debugLogger.debug('debugRestoreRange result', {\n hasChanges: result.hasChanges,\n willChange: result.willChange,\n originalRange: result.originalRange,\n currentRange: result.currentRange,\n });\n return result;\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(options: {\n originalContent: string;\n currentContent: string;\n startOffset: number;\n endOffset: number;\n}): string {\n const { originalContent, currentContent, startOffset, endOffset } = options;\n debugLogger.debug('restoreRange (standalone) called', {\n startOffset,\n endOffset,\n originalLength: originalContent.length,\n currentLength: currentContent.length,\n });\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["debugLogger","Logger","defaultPrefix","DIFF_DELETE","DIFF_INSERT","DIFF_EQUAL","FileRestoreManager","originalContent","dmp","diff_match_patch","debug","originalLength","length","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","currentLength","Error","diffs","diff_main","diff_cleanupSemantic","diffCount","hasChanges","some","_ref","op","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","contentWillChange","currentRangePreview","slice","originalRangePreview","restoredContent","restoredLength","contentChanged","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","text","textLength","nextDiff","rangeStart","rangeEnd","offsetInRange","result","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","currentRange","willChange","manager"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var n=require("diff-match-patch"),t=function(){function t(t){this.originalContent=t,this.dmp=new n.diff_match_patch}var e=t.prototype;return e.restoreRange=function(n,t){var e=t.startOffset,r=t.endOffset;if(e<0||r<0||e>r)throw new Error("Invalid offset range");if(e>n.length||r>n.length)throw new Error("Offset range exceeds current content length");var i=this.dmp.diff_main(this.originalContent,n);this.dmp.diff_cleanupSemantic(i);var l=this.mapCurrentRangeToOriginal(i,e,r),o=this.originalContent.substring(l.start,l.end),a=n.substring(e,r);return console.log("[restoreRange] Debug Info:"),console.log(" startOffset: "+e+", endOffset: "+r),console.log(" Current range content: "+JSON.stringify(a)),console.log(" Original range mapping: "+l.start+"-"+l.end),console.log(" Will restore to: "+JSON.stringify(o)),console.log(" Content will change: "+(a!==o)),n.substring(0,e)+o+n.substring(r)},e.mapCurrentRangeToOriginal=function(n,t,e){for(var r=0,i=0,l=null,o=null,a=0;a<n.length;a++){var s=n[a],g=s[0],u=s[1].length,f=a<n.length-1?n[a+1]:null;if(0===g){var h=r+u;null===l&&t>=r&&t<h&&(l=i+(t-r)),null===o&&e>r&&e<=h&&(o=i+(e-r),e===h&&f&&-1===f[0]&&(o=i+u+f[1].length)),r+=u,i+=u}else if(1===g){var c=r+u;null===l&&t>=r&&t<c&&(l=i),null===o&&e>r&&e<=c&&(o=i),r+=u}else-1===g&&((null===l&&t===r||null===l&&t<r)&&(l=i),null===o&&e===r?o=i+u:null===o&&e<r&&(o=i),i+=u);if(null!==l&&null!==o)break}return null===l&&(l=i),null===o&&(o=i),{start:l,end:o}},e.getOriginalContent=function(){return this.originalContent},e.updateOriginalContent=function(n){this.originalContent=n},e.debugRestoreRange=function(n,t){var e=t.startOffset,r=t.endOffset,i=this.dmp.diff_main(this.originalContent,n);this.dmp.diff_cleanupSemantic(i);var l=this.mapCurrentRangeToOriginal(i,e,r),o=this.originalContent.substring(l.start,l.end),a=n.substring(e,r);return{hasChanges:this.originalContent!==n,originalRange:l,currentRange:{start:e,end:r},originalContent:o,currentContent:a,willChange:o!==a}},t}();exports.FileRestoreManager=t,exports.default=t,exports.restoreRange=function(n,e,r,i){return new t(n).restoreRange(e,{startOffset:r,endOffset:i})};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@x-oasis/log"),n=require("diff-match-patch"),t=new e.Logger({defaultPrefix:"[diff-match-patch]"}),r=function(){function e(e){this.originalContent=e,this.dmp=new n.diff_match_patch,t.debug("FileRestoreManager created",{originalLength:e.length})}var r=e.prototype;return r.restoreRange=function(e,n){var r=n.startOffset,a=n.endOffset;if(t.debug("restoreRange called",{startOffset:r,endOffset:a,currentLength:e.length}),r<0||a<0||r>a)throw new Error("Invalid offset range");if(r>e.length||a>e.length)throw new Error("Offset range exceeds current content length");var i=this.dmp.diff_main(this.originalContent,e);this.dmp.diff_cleanupSemantic(i),t.debug("diffs computed",{diffCount:i.length,hasChanges:i.some((function(e){return 0!==e[0]}))});var g=this.mapCurrentRangeToOriginal(i,r,a),l=this.originalContent.substring(g.start,g.end),s=e.substring(r,a),o=s!==l;t.debug("range mapping resolved",{startOffset:r,endOffset:a,originalRange:{start:g.start,end:g.end},currentRangePreview:s.length>80?s.slice(0,40)+"..."+s.slice(-40):s,originalRangePreview:l.length>80?l.slice(0,40)+"..."+l.slice(-40):l,contentWillChange:o});var u=e.substring(0,r)+l+e.substring(a);return t.debug("restoreRange done",{restoredLength:u.length,contentChanged:o}),u},r.mapCurrentRangeToOriginal=function(e,n,r){t.debug("mapCurrentRangeToOriginal called",{currentStart:n,currentEnd:r,diffCount:e.length});for(var a=0,i=0,g=null,l=null,s=0;s<e.length;s++){var o=e[s],u=o[0],f=o[1].length,d=s<e.length-1?e[s+1]:null;if(0===u){var h=a+f;null===g&&n>=a&&n<h&&(g=i+(n-a)),null===l&&r>a&&r<=h&&(l=i+(r-a),r===h&&d&&-1===d[0]&&(l=i+f+d[1].length)),a+=f,i+=f}else if(1===u){var c=a+f;null===g&&n>=a&&n<c&&(g=i),null===l&&r>a&&r<=c&&(l=i),a+=f}else-1===u&&((null===g&&n===a||null===g&&n<a)&&(g=i),null===l&&r===a?l=i+f:null===l&&r<a&&(l=i),i+=f);if(null!==g&&null!==l)break}null===g&&(g=i),null===l&&(l=i);var C={start:g,end:l};return t.debug("mapCurrentRangeToOriginal result",C),C},r.getOriginalContent=function(){return this.originalContent},r.updateOriginalContent=function(e){this.originalContent=e},r.debugRestoreRange=function(e,n){var r=n.startOffset,a=n.endOffset;t.debug("debugRestoreRange called",{startOffset:r,endOffset:a,currentLength:e.length});var i=this.dmp.diff_main(this.originalContent,e);this.dmp.diff_cleanupSemantic(i);var g=this.mapCurrentRangeToOriginal(i,r,a),l=this.originalContent.substring(g.start,g.end),s=e.substring(r,a),o={hasChanges:this.originalContent!==e,originalRange:g,currentRange:{start:r,end:a},originalContent:l,currentContent:s,willChange:l!==s};return t.debug("debugRestoreRange result",{hasChanges:o.hasChanges,willChange:o.willChange,originalRange:o.originalRange,currentRange:o.currentRange}),o},e}();exports.FileRestoreManager=r,exports.default=r,exports.restoreRange=function(e){var n=e.originalContent,a=e.currentContent,i=e.startOffset,g=e.endOffset;return t.debug("restoreRange (standalone) called",{startOffset:i,endOffset:g,originalLength:n.length,currentLength:a.length}),new r(n).restoreRange(a,{startOffset:i,endOffset:g})};
2
2
  //# sourceMappingURL=diff-match-patch.cjs.production.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"diff-match-patch.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import { diff_match_patch } from 'diff-match-patch';\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n // 调试信息\n console.log('[restoreRange] Debug Info:');\n console.log(` startOffset: ${startOffset}, endOffset: ${endOffset}`);\n console.log(\n ` Current range content: ${JSON.stringify(currentRangeContent)}`\n );\n console.log(\n ` Original range mapping: ${originalRange.start}-${originalRange.end}`\n );\n console.log(` Will restore to: ${JSON.stringify(originalRangeContent)}`);\n console.log(\n ` Content will change: ${currentRangeContent !== originalRangeContent}`\n );\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n return { start: originalStart, end: originalEnd };\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n return {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(\n originalContent: string,\n currentContent: string,\n startOffset: number,\n endOffset: number\n): string {\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["FileRestoreManager","originalContent","this","dmp","diff_match_patch","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","Error","length","diffs","diff_main","diff_cleanupSemantic","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","console","log","JSON","stringify","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","textLength","nextDiff","rangeEnd","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","hasChanges","currentRange","willChange"],"mappings":"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"}
1
+ {"version":3,"file":"diff-match-patch.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["import { Logger } from '@x-oasis/log';\nimport { diff_match_patch } from 'diff-match-patch';\n\nconst debugLogger = new Logger({ defaultPrefix: '[diff-match-patch]' });\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n debugLogger.debug('FileRestoreManager created', {\n originalLength: originalContent.length,\n });\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('restoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n debugLogger.debug('diffs computed', {\n diffCount: diffs.length,\n hasChanges: diffs.some(([op]) => op !== DIFF_EQUAL),\n });\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const contentWillChange = currentRangeContent !== originalRangeContent;\n debugLogger.debug('range mapping resolved', {\n startOffset,\n endOffset,\n originalRange: { start: originalRange.start, end: originalRange.end },\n currentRangePreview:\n currentRangeContent.length > 80\n ? `${currentRangeContent.slice(0, 40)}...${currentRangeContent.slice(\n -40\n )}`\n : currentRangeContent,\n originalRangePreview:\n originalRangeContent.length > 80\n ? `${originalRangeContent.slice(\n 0,\n 40\n )}...${originalRangeContent.slice(-40)}`\n : originalRangeContent,\n contentWillChange,\n });\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n debugLogger.debug('restoreRange done', {\n restoredLength: restoredContent.length,\n contentChanged: contentWillChange,\n });\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n debugLogger.debug('mapCurrentRangeToOriginal called', {\n currentStart,\n currentEnd,\n diffCount: diffs.length,\n });\n\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n const result = { start: originalStart, end: originalEnd };\n debugLogger.debug('mapCurrentRangeToOriginal result', result);\n return result;\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('debugRestoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const result = {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n debugLogger.debug('debugRestoreRange result', {\n hasChanges: result.hasChanges,\n willChange: result.willChange,\n originalRange: result.originalRange,\n currentRange: result.currentRange,\n });\n return result;\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(options: {\n originalContent: string;\n currentContent: string;\n startOffset: number;\n endOffset: number;\n}): string {\n const { originalContent, currentContent, startOffset, endOffset } = options;\n debugLogger.debug('restoreRange (standalone) called', {\n startOffset,\n endOffset,\n originalLength: originalContent.length,\n currentLength: currentContent.length,\n });\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["debugLogger","Logger","defaultPrefix","FileRestoreManager","originalContent","this","dmp","diff_match_patch","debug","originalLength","length","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","currentLength","Error","diffs","diff_main","diff_cleanupSemantic","diffCount","hasChanges","some","_ref","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","contentWillChange","currentRangePreview","slice","originalRangePreview","restoredContent","restoredLength","contentChanged","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","textLength","nextDiff","rangeEnd","result","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","currentRange","willChange"],"mappings":"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"}
@@ -1,5 +1,9 @@
1
+ import { Logger } from '@x-oasis/log';
1
2
  import { diff_match_patch } from 'diff-match-patch';
2
3
 
4
+ var debugLogger = /*#__PURE__*/new Logger({
5
+ defaultPrefix: '[diff-match-patch]'
6
+ });
3
7
  var DIFF_DELETE = -1;
4
8
  var DIFF_INSERT = 1;
5
9
  var DIFF_EQUAL = 0;
@@ -7,11 +11,19 @@ var FileRestoreManager = /*#__PURE__*/function () {
7
11
  function FileRestoreManager(originalContent) {
8
12
  this.originalContent = originalContent;
9
13
  this.dmp = new diff_match_patch();
14
+ debugLogger.debug('FileRestoreManager created', {
15
+ originalLength: originalContent.length
16
+ });
10
17
  }
11
18
  var _proto = FileRestoreManager.prototype;
12
19
  _proto.restoreRange = function restoreRange(currentContent, options) {
13
20
  var startOffset = options.startOffset,
14
21
  endOffset = options.endOffset;
22
+ debugLogger.debug('restoreRange called', {
23
+ startOffset: startOffset,
24
+ endOffset: endOffset,
25
+ currentLength: currentContent.length
26
+ });
15
27
  if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {
16
28
  throw new Error('Invalid offset range');
17
29
  }
@@ -20,19 +32,41 @@ var FileRestoreManager = /*#__PURE__*/function () {
20
32
  }
21
33
  var diffs = this.dmp.diff_main(this.originalContent, currentContent);
22
34
  this.dmp.diff_cleanupSemantic(diffs);
35
+ debugLogger.debug('diffs computed', {
36
+ diffCount: diffs.length,
37
+ hasChanges: diffs.some(function (_ref) {
38
+ var op = _ref[0];
39
+ return op !== DIFF_EQUAL;
40
+ })
41
+ });
23
42
  var originalRange = this.mapCurrentRangeToOriginal(diffs, startOffset, endOffset);
24
43
  var originalRangeContent = this.originalContent.substring(originalRange.start, originalRange.end);
25
44
  var currentRangeContent = currentContent.substring(startOffset, endOffset);
26
- console.log('[restoreRange] Debug Info:');
27
- console.log(" startOffset: " + startOffset + ", endOffset: " + endOffset);
28
- console.log(" Current range content: " + JSON.stringify(currentRangeContent));
29
- console.log(" Original range mapping: " + originalRange.start + "-" + originalRange.end);
30
- console.log(" Will restore to: " + JSON.stringify(originalRangeContent));
31
- console.log(" Content will change: " + (currentRangeContent !== originalRangeContent));
45
+ var contentWillChange = currentRangeContent !== originalRangeContent;
46
+ debugLogger.debug('range mapping resolved', {
47
+ startOffset: startOffset,
48
+ endOffset: endOffset,
49
+ originalRange: {
50
+ start: originalRange.start,
51
+ end: originalRange.end
52
+ },
53
+ currentRangePreview: currentRangeContent.length > 80 ? currentRangeContent.slice(0, 40) + "..." + currentRangeContent.slice(-40) : currentRangeContent,
54
+ originalRangePreview: originalRangeContent.length > 80 ? originalRangeContent.slice(0, 40) + "..." + originalRangeContent.slice(-40) : originalRangeContent,
55
+ contentWillChange: contentWillChange
56
+ });
32
57
  var restoredContent = currentContent.substring(0, startOffset) + originalRangeContent + currentContent.substring(endOffset);
58
+ debugLogger.debug('restoreRange done', {
59
+ restoredLength: restoredContent.length,
60
+ contentChanged: contentWillChange
61
+ });
33
62
  return restoredContent;
34
63
  };
35
64
  _proto.mapCurrentRangeToOriginal = function mapCurrentRangeToOriginal(diffs, currentStart, currentEnd) {
65
+ debugLogger.debug('mapCurrentRangeToOriginal called', {
66
+ currentStart: currentStart,
67
+ currentEnd: currentEnd,
68
+ diffCount: diffs.length
69
+ });
36
70
  var currentOffset = 0;
37
71
  var originalOffset = 0;
38
72
  var originalStart = null;
@@ -92,10 +126,12 @@ var FileRestoreManager = /*#__PURE__*/function () {
92
126
  if (originalEnd === null) {
93
127
  originalEnd = originalOffset;
94
128
  }
95
- return {
129
+ var result = {
96
130
  start: originalStart,
97
131
  end: originalEnd
98
132
  };
133
+ debugLogger.debug('mapCurrentRangeToOriginal result', result);
134
+ return result;
99
135
  };
100
136
  _proto.getOriginalContent = function getOriginalContent() {
101
137
  return this.originalContent;
@@ -106,12 +142,17 @@ var FileRestoreManager = /*#__PURE__*/function () {
106
142
  _proto.debugRestoreRange = function debugRestoreRange(currentContent, options) {
107
143
  var startOffset = options.startOffset,
108
144
  endOffset = options.endOffset;
145
+ debugLogger.debug('debugRestoreRange called', {
146
+ startOffset: startOffset,
147
+ endOffset: endOffset,
148
+ currentLength: currentContent.length
149
+ });
109
150
  var diffs = this.dmp.diff_main(this.originalContent, currentContent);
110
151
  this.dmp.diff_cleanupSemantic(diffs);
111
152
  var originalRange = this.mapCurrentRangeToOriginal(diffs, startOffset, endOffset);
112
153
  var originalRangeContent = this.originalContent.substring(originalRange.start, originalRange.end);
113
154
  var currentRangeContent = currentContent.substring(startOffset, endOffset);
114
- return {
155
+ var result = {
115
156
  hasChanges: this.originalContent !== currentContent,
116
157
  originalRange: originalRange,
117
158
  currentRange: {
@@ -122,10 +163,27 @@ var FileRestoreManager = /*#__PURE__*/function () {
122
163
  currentContent: currentRangeContent,
123
164
  willChange: originalRangeContent !== currentRangeContent
124
165
  };
166
+ debugLogger.debug('debugRestoreRange result', {
167
+ hasChanges: result.hasChanges,
168
+ willChange: result.willChange,
169
+ originalRange: result.originalRange,
170
+ currentRange: result.currentRange
171
+ });
172
+ return result;
125
173
  };
126
174
  return FileRestoreManager;
127
175
  }();
128
- function restoreRange(originalContent, currentContent, startOffset, endOffset) {
176
+ function restoreRange(options) {
177
+ var originalContent = options.originalContent,
178
+ currentContent = options.currentContent,
179
+ startOffset = options.startOffset,
180
+ endOffset = options.endOffset;
181
+ debugLogger.debug('restoreRange (standalone) called', {
182
+ startOffset: startOffset,
183
+ endOffset: endOffset,
184
+ originalLength: originalContent.length,
185
+ currentLength: currentContent.length
186
+ });
129
187
  var manager = new FileRestoreManager(originalContent);
130
188
  return manager.restoreRange(currentContent, {
131
189
  startOffset: startOffset,
@@ -1 +1 @@
1
- {"version":3,"file":"diff-match-patch.esm.js","sources":["../src/index.ts"],"sourcesContent":["import { diff_match_patch } from 'diff-match-patch';\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n // 调试信息\n console.log('[restoreRange] Debug Info:');\n console.log(` startOffset: ${startOffset}, endOffset: ${endOffset}`);\n console.log(\n ` Current range content: ${JSON.stringify(currentRangeContent)}`\n );\n console.log(\n ` Original range mapping: ${originalRange.start}-${originalRange.end}`\n );\n console.log(` Will restore to: ${JSON.stringify(originalRangeContent)}`);\n console.log(\n ` Content will change: ${currentRangeContent !== originalRangeContent}`\n );\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n return { start: originalStart, end: originalEnd };\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n return {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(\n originalContent: string,\n currentContent: string,\n startOffset: number,\n endOffset: number\n): string {\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["DIFF_DELETE","DIFF_INSERT","DIFF_EQUAL","FileRestoreManager","originalContent","dmp","diff_match_patch","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","Error","length","diffs","diff_main","diff_cleanupSemantic","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","console","log","JSON","stringify","restoredContent","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","text","textLength","nextDiff","rangeStart","rangeEnd","offsetInRange","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","hasChanges","currentRange","willChange","manager"],"mappings":"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"}
1
+ {"version":3,"file":"diff-match-patch.esm.js","sources":["../src/index.ts"],"sourcesContent":["import { Logger } from '@x-oasis/log';\nimport { diff_match_patch } from 'diff-match-patch';\n\nconst debugLogger = new Logger({ defaultPrefix: '[diff-match-patch]' });\n\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\nexport interface RestoreRangeOptions {\n startOffset: number;\n endOffset: number;\n}\n\n/**\n * 文件恢复管理器\n * 用于将最新文件中的指定 range 恢复到原始版本\n */\nexport class FileRestoreManager {\n private originalContent: string;\n private dmp: diff_match_patch;\n\n constructor(originalContent: string) {\n this.originalContent = originalContent;\n this.dmp = new diff_match_patch();\n debugLogger.debug('FileRestoreManager created', {\n originalLength: originalContent.length,\n });\n }\n\n /**\n * 将最新文件中指定 offset range 的内容恢复到原始版本\n * @param currentContent 最新文件内容\n * @param options 包含 startOffset 和 endOffset 的选项\n * @returns 恢复后的文件内容\n */\n restoreRange(currentContent: string, options: RestoreRangeOptions): string {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('restoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {\n throw new Error('Invalid offset range');\n }\n\n if (\n startOffset > currentContent.length ||\n endOffset > currentContent.length\n ) {\n throw new Error('Offset range exceeds current content length');\n }\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n debugLogger.debug('diffs computed', {\n diffCount: diffs.length,\n hasChanges: diffs.some(([op]) => op !== DIFF_EQUAL),\n });\n\n // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n // 从原始文件中提取对应范围的内容\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n\n // 获取当前 range 的内容(用于调试)\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const contentWillChange = currentRangeContent !== originalRangeContent;\n debugLogger.debug('range mapping resolved', {\n startOffset,\n endOffset,\n originalRange: { start: originalRange.start, end: originalRange.end },\n currentRangePreview:\n currentRangeContent.length > 80\n ? `${currentRangeContent.slice(0, 40)}...${currentRangeContent.slice(\n -40\n )}`\n : currentRangeContent,\n originalRangePreview:\n originalRangeContent.length > 80\n ? `${originalRangeContent.slice(\n 0,\n 40\n )}...${originalRangeContent.slice(-40)}`\n : originalRangeContent,\n contentWillChange,\n });\n\n // 替换最新文件中指定 range 的内容\n const restoredContent =\n currentContent.substring(0, startOffset) +\n originalRangeContent +\n currentContent.substring(endOffset);\n\n debugLogger.debug('restoreRange done', {\n restoredLength: restoredContent.length,\n contentChanged: contentWillChange,\n });\n\n return restoredContent;\n }\n\n /**\n * 将最新文件中的 offset range 映射到原始文件中的 offset range\n */\n private mapCurrentRangeToOriginal(\n diffs: Array<[number, string]>,\n currentStart: number,\n currentEnd: number\n ): { start: number; end: number } {\n debugLogger.debug('mapCurrentRangeToOriginal called', {\n currentStart,\n currentEnd,\n diffCount: diffs.length,\n });\n\n let currentOffset = 0; // 当前在最新文件中的 offset\n let originalOffset = 0; // 当前在原始文件中的 offset\n let originalStart: number | null = null;\n let originalEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [operation, text] = diffs[i];\n const textLength = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (operation === DIFF_EQUAL) {\n // 相等部分:两个文件的 offset 同步增加\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 检查 currentStart 是否在这个 EQUAL 区间内\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n const offsetInRange = currentStart - rangeStart;\n originalStart = originalOffset + offsetInRange;\n }\n // 检查 currentEnd 是否在这个 EQUAL 区间内\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n const offsetInRange = currentEnd - rangeStart;\n originalEnd = originalOffset + offsetInRange;\n\n // 特殊处理:如果 currentEnd 正好在 EQUAL 的结束位置,且下一个 diff 是 DELETE\n // 需要包含被删除的内容,以便正确恢复\n //\n // 示例:\n // 原始文件: \"...禁用按钮</button>...\"\n // 最新文件: \"...禁用</button>...\"\n // diff: [EQUAL: \"...禁用\"], [DELETE: \"按钮\"], [EQUAL: \"</button>...\"]\n //\n // 如果用户选择最新文件的 offset 1512-1514(\"禁用\"),\n // 应该恢复为原始文件的 offset 1512-1516(\"禁用按钮\")\n //\n // 如果不包含 DELETE 的内容,只会恢复到 \"禁用\",而不是 \"禁用按钮\"\n if (\n currentEnd === rangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_DELETE\n ) {\n originalEnd = originalOffset + textLength + nextDiff[1].length;\n }\n }\n\n currentOffset += textLength;\n originalOffset += textLength;\n } else if (operation === DIFF_INSERT) {\n // 插入部分:只在新文件中存在\n const rangeStart = currentOffset;\n const rangeEnd = currentOffset + textLength;\n\n // 如果 currentStart 在插入内容中,映射到插入前的原始位置\n if (\n originalStart === null &&\n currentStart >= rangeStart &&\n currentStart < rangeEnd\n ) {\n originalStart = originalOffset;\n }\n // 如果 currentEnd 在插入内容中,映射到插入前的原始位置\n if (\n originalEnd === null &&\n currentEnd > rangeStart &&\n currentEnd <= rangeEnd\n ) {\n originalEnd = originalOffset;\n }\n\n currentOffset += textLength;\n // INSERT 不增加 originalOffset\n } else if (operation === DIFF_DELETE) {\n // 删除部分:只在原始文件中存在\n // 如果 currentStart 正好在删除位置之前,需要包含删除的内容\n if (originalStart === null && currentStart === currentOffset) {\n // currentStart 正好在删除位置,映射到删除开始的位置\n originalStart = originalOffset;\n } else if (originalStart === null && currentStart < currentOffset) {\n // currentStart 在删除位置之前,映射到删除开始的位置\n originalStart = originalOffset;\n }\n\n // 如果 currentEnd 正好在删除位置之前,需要包含删除的内容\n if (originalEnd === null && currentEnd === currentOffset) {\n // currentEnd 正好在删除位置,需要包含整个删除的内容\n originalEnd = originalOffset + textLength;\n } else if (originalEnd === null && currentEnd < currentOffset) {\n // currentEnd 在删除位置之前,映射到删除开始的位置\n originalEnd = originalOffset;\n }\n\n originalOffset += textLength;\n // DELETE 不增加 currentOffset\n }\n\n // 如果两个位置都找到了,可以提前退出\n if (originalStart !== null && originalEnd !== null) {\n break;\n }\n }\n\n // 处理边界情况:如果 range 在所有 diff 之后\n if (originalStart === null) {\n originalStart = originalOffset;\n }\n if (originalEnd === null) {\n originalEnd = originalOffset;\n }\n\n const result = { start: originalStart, end: originalEnd };\n debugLogger.debug('mapCurrentRangeToOriginal result', result);\n return result;\n }\n\n /**\n * 获取原始文件内容\n */\n getOriginalContent(): string {\n return this.originalContent;\n }\n\n /**\n * 更新原始文件内容\n */\n updateOriginalContent(newOriginalContent: string): void {\n this.originalContent = newOriginalContent;\n }\n\n /**\n * 调试方法:分析指定 range 的恢复情况\n */\n debugRestoreRange(\n currentContent: string,\n options: RestoreRangeOptions\n ): {\n hasChanges: boolean;\n originalRange: { start: number; end: number };\n currentRange: { start: number; end: number };\n originalContent: string;\n currentContent: string;\n willChange: boolean;\n } {\n const { startOffset, endOffset } = options;\n\n debugLogger.debug('debugRestoreRange called', {\n startOffset,\n endOffset,\n currentLength: currentContent.length,\n });\n\n // 计算差异\n const diffs = this.dmp.diff_main(this.originalContent, currentContent);\n this.dmp.diff_cleanupSemantic(diffs);\n\n // 找到映射\n const originalRange = this.mapCurrentRangeToOriginal(\n diffs,\n startOffset,\n endOffset\n );\n\n const originalRangeContent = this.originalContent.substring(\n originalRange.start,\n originalRange.end\n );\n const currentRangeContent = currentContent.substring(\n startOffset,\n endOffset\n );\n\n const result = {\n hasChanges: this.originalContent !== currentContent,\n originalRange,\n currentRange: { start: startOffset, end: endOffset },\n originalContent: originalRangeContent,\n currentContent: currentRangeContent,\n willChange: originalRangeContent !== currentRangeContent,\n };\n debugLogger.debug('debugRestoreRange result', {\n hasChanges: result.hasChanges,\n willChange: result.willChange,\n originalRange: result.originalRange,\n currentRange: result.currentRange,\n });\n return result;\n }\n}\n\n/**\n * 便捷函数:直接恢复指定 range 的内容\n */\nexport function restoreRange(options: {\n originalContent: string;\n currentContent: string;\n startOffset: number;\n endOffset: number;\n}): string {\n const { originalContent, currentContent, startOffset, endOffset } = options;\n debugLogger.debug('restoreRange (standalone) called', {\n startOffset,\n endOffset,\n originalLength: originalContent.length,\n currentLength: currentContent.length,\n });\n const manager = new FileRestoreManager(originalContent);\n return manager.restoreRange(currentContent, { startOffset, endOffset });\n}\n\nexport default FileRestoreManager;\n"],"names":["debugLogger","Logger","defaultPrefix","DIFF_DELETE","DIFF_INSERT","DIFF_EQUAL","FileRestoreManager","originalContent","dmp","diff_match_patch","debug","originalLength","length","_proto","prototype","restoreRange","currentContent","options","startOffset","endOffset","currentLength","Error","diffs","diff_main","diff_cleanupSemantic","diffCount","hasChanges","some","_ref","op","originalRange","mapCurrentRangeToOriginal","originalRangeContent","substring","start","end","currentRangeContent","contentWillChange","currentRangePreview","slice","originalRangePreview","restoredContent","restoredLength","contentChanged","currentStart","currentEnd","currentOffset","originalOffset","originalStart","originalEnd","i","_diffs$i","operation","text","textLength","nextDiff","rangeStart","rangeEnd","offsetInRange","result","getOriginalContent","updateOriginalContent","newOriginalContent","debugRestoreRange","currentRange","willChange","manager"],"mappings":";;;AAGA,IAAMA,WAAW,gBAAG,IAAIC,MAAM,CAAC;EAAEC,aAAa,EAAE;CAAsB,CAAC;AAEvE,IAAMC,WAAW,GAAG,CAAC,CAAC;AACtB,IAAMC,WAAW,GAAG,CAAC;AACrB,IAAMC,UAAU,GAAG,CAAC;IAWPC,kBAAkB;EAI7B,SAAAA,mBAAYC,eAAuB;IACjC,IAAI,CAACA,eAAe,GAAGA,eAAe;IACtC,IAAI,CAACC,GAAG,GAAG,IAAIC,gBAAgB,EAAE;IACjCT,WAAW,CAACU,KAAK,CAAC,4BAA4B,EAAE;MAC9CC,cAAc,EAAEJ,eAAe,CAACK;KACjC,CAAC;;EACH,IAAAC,MAAA,GAAAP,kBAAA,CAAAQ,SAAA;EAAAD,MAAA,CAQDE,YAAY,GAAZ,SAAAA,aAAaC,cAAsB,EAAEC,OAA4B;IAC/D,IAAQC,WAAW,GAAgBD,OAAO,CAAlCC,WAAW;MAAEC,SAAS,GAAKF,OAAO,CAArBE,SAAS;IAE9BnB,WAAW,CAACU,KAAK,CAAC,qBAAqB,EAAE;MACvCQ,WAAW,EAAXA,WAAW;MACXC,SAAS,EAATA,SAAS;MACTC,aAAa,EAAEJ,cAAc,CAACJ;KAC/B,CAAC;IAEF,IAAIM,WAAW,GAAG,CAAC,IAAIC,SAAS,GAAG,CAAC,IAAID,WAAW,GAAGC,SAAS,EAAE;MAC/D,MAAM,IAAIE,KAAK,CAAC,sBAAsB,CAAC;;IAGzC,IACEH,WAAW,GAAGF,cAAc,CAACJ,MAAM,IACnCO,SAAS,GAAGH,cAAc,CAACJ,MAAM,EACjC;MACA,MAAM,IAAIS,KAAK,CAAC,6CAA6C,CAAC;;IAIhE,IAAMC,KAAK,GAAG,IAAI,CAACd,GAAG,CAACe,SAAS,CAAC,IAAI,CAAChB,eAAe,EAAES,cAAc,CAAC;IACtE,IAAI,CAACR,GAAG,CAACgB,oBAAoB,CAACF,KAAK,CAAC;IAEpCtB,WAAW,CAACU,KAAK,CAAC,gBAAgB,EAAE;MAClCe,SAAS,EAAEH,KAAK,CAACV,MAAM;MACvBc,UAAU,EAAEJ,KAAK,CAACK,IAAI,CAAC,UAAAC,IAAA;QAAA,IAAEC,EAAE,GAAAD,IAAA;QAAA,OAAMC,EAAE,KAAKxB,UAAU;;KACnD,CAAC;IAGF,IAAMyB,aAAa,GAAG,IAAI,CAACC,yBAAyB,CAClDT,KAAK,EACLJ,WAAW,EACXC,SAAS,CACV;IAGD,IAAMa,oBAAoB,GAAG,IAAI,CAACzB,eAAe,CAAC0B,SAAS,CACzDH,aAAa,CAACI,KAAK,EACnBJ,aAAa,CAACK,GAAG,CAClB;IAGD,IAAMC,mBAAmB,GAAGpB,cAAc,CAACiB,SAAS,CAClDf,WAAW,EACXC,SAAS,CACV;IAED,IAAMkB,iBAAiB,GAAGD,mBAAmB,KAAKJ,oBAAoB;IACtEhC,WAAW,CAACU,KAAK,CAAC,wBAAwB,EAAE;MAC1CQ,WAAW,EAAXA,WAAW;MACXC,SAAS,EAATA,SAAS;MACTW,aAAa,EAAE;QAAEI,KAAK,EAAEJ,aAAa,CAACI,KAAK;QAAEC,GAAG,EAAEL,aAAa,CAACK;OAAK;MACrEG,mBAAmB,EACjBF,mBAAmB,CAACxB,MAAM,GAAG,EAAE,GACxBwB,mBAAmB,CAACG,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,WAAMH,mBAAmB,CAACG,KAAK,CAChE,CAAC,EAAE,CACJ,GACDH,mBAAmB;MACzBI,oBAAoB,EAClBR,oBAAoB,CAACpB,MAAM,GAAG,EAAE,GACzBoB,oBAAoB,CAACO,KAAK,CAC3B,CAAC,EACD,EAAE,CACH,WAAMP,oBAAoB,CAACO,KAAK,CAAC,CAAC,EAAE,CAAC,GACtCP,oBAAoB;MAC1BK,iBAAiB,EAAjBA;KACD,CAAC;IAGF,IAAMI,eAAe,GACnBzB,cAAc,CAACiB,SAAS,CAAC,CAAC,EAAEf,WAAW,CAAC,GACxCc,oBAAoB,GACpBhB,cAAc,CAACiB,SAAS,CAACd,SAAS,CAAC;IAErCnB,WAAW,CAACU,KAAK,CAAC,mBAAmB,EAAE;MACrCgC,cAAc,EAAED,eAAe,CAAC7B,MAAM;MACtC+B,cAAc,EAAEN;KACjB,CAAC;IAEF,OAAOI,eAAe;GACvB;EAAA5B,MAAA,CAKOkB,yBAAyB,GAAzB,SAAAA,0BACNT,KAA8B,EAC9BsB,YAAoB,EACpBC,UAAkB;IAElB7C,WAAW,CAACU,KAAK,CAAC,kCAAkC,EAAE;MACpDkC,YAAY,EAAZA,YAAY;MACZC,UAAU,EAAVA,UAAU;MACVpB,SAAS,EAAEH,KAAK,CAACV;KAClB,CAAC;IAEF,IAAIkC,aAAa,GAAG,CAAC;IACrB,IAAIC,cAAc,GAAG,CAAC;IACtB,IAAIC,aAAa,GAAkB,IAAI;IACvC,IAAIC,WAAW,GAAkB,IAAI;IAErC,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAG5B,KAAK,CAACV,MAAM,EAAEsC,CAAC,EAAE,EAAE;MACrC,IAAAC,QAAA,GAA0B7B,KAAK,CAAC4B,CAAC,CAAC;QAA3BE,SAAS,GAAAD,QAAA;QAAEE,IAAI,GAAAF,QAAA;MACtB,IAAMG,UAAU,GAAGD,IAAI,CAACzC,MAAM;MAC9B,IAAM2C,QAAQ,GAAGL,CAAC,GAAG5B,KAAK,CAACV,MAAM,GAAG,CAAC,GAAGU,KAAK,CAAC4B,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;MAE3D,IAAIE,SAAS,KAAK/C,UAAU,EAAE;QAE5B,IAAMmD,UAAU,GAAGV,aAAa;QAChC,IAAMW,QAAQ,GAAGX,aAAa,GAAGQ,UAAU;QAG3C,IACEN,aAAa,KAAK,IAAI,IACtBJ,YAAY,IAAIY,UAAU,IAC1BZ,YAAY,GAAGa,QAAQ,EACvB;UACA,IAAMC,aAAa,GAAGd,YAAY,GAAGY,UAAU;UAC/CR,aAAa,GAAGD,cAAc,GAAGW,aAAa;;QAGhD,IACET,WAAW,KAAK,IAAI,IACpBJ,UAAU,GAAGW,UAAU,IACvBX,UAAU,IAAIY,QAAQ,EACtB;UACA,IAAMC,cAAa,GAAGb,UAAU,GAAGW,UAAU;UAC7CP,WAAW,GAAGF,cAAc,GAAGW,cAAa;UAc5C,IACEb,UAAU,KAAKY,QAAQ,IACvBF,QAAQ,IACRA,QAAQ,CAAC,CAAC,CAAC,KAAKpD,WAAW,EAC3B;YACA8C,WAAW,GAAGF,cAAc,GAAGO,UAAU,GAAGC,QAAQ,CAAC,CAAC,CAAC,CAAC3C,MAAM;;;QAIlEkC,aAAa,IAAIQ,UAAU;QAC3BP,cAAc,IAAIO,UAAU;OAC7B,MAAM,IAAIF,SAAS,KAAKhD,WAAW,EAAE;QAEpC,IAAMoD,WAAU,GAAGV,aAAa;QAChC,IAAMW,SAAQ,GAAGX,aAAa,GAAGQ,UAAU;QAG3C,IACEN,aAAa,KAAK,IAAI,IACtBJ,YAAY,IAAIY,WAAU,IAC1BZ,YAAY,GAAGa,SAAQ,EACvB;UACAT,aAAa,GAAGD,cAAc;;QAGhC,IACEE,WAAW,KAAK,IAAI,IACpBJ,UAAU,GAAGW,WAAU,IACvBX,UAAU,IAAIY,SAAQ,EACtB;UACAR,WAAW,GAAGF,cAAc;;QAG9BD,aAAa,IAAIQ,UAAU;OAE5B,MAAM,IAAIF,SAAS,KAAKjD,WAAW,EAAE;QAGpC,IAAI6C,aAAa,KAAK,IAAI,IAAIJ,YAAY,KAAKE,aAAa,EAAE;UAE5DE,aAAa,GAAGD,cAAc;SAC/B,MAAM,IAAIC,aAAa,KAAK,IAAI,IAAIJ,YAAY,GAAGE,aAAa,EAAE;UAEjEE,aAAa,GAAGD,cAAc;;QAIhC,IAAIE,WAAW,KAAK,IAAI,IAAIJ,UAAU,KAAKC,aAAa,EAAE;UAExDG,WAAW,GAAGF,cAAc,GAAGO,UAAU;SAC1C,MAAM,IAAIL,WAAW,KAAK,IAAI,IAAIJ,UAAU,GAAGC,aAAa,EAAE;UAE7DG,WAAW,GAAGF,cAAc;;QAG9BA,cAAc,IAAIO,UAAU;;MAK9B,IAAIN,aAAa,KAAK,IAAI,IAAIC,WAAW,KAAK,IAAI,EAAE;QAClD;;;IAKJ,IAAID,aAAa,KAAK,IAAI,EAAE;MAC1BA,aAAa,GAAGD,cAAc;;IAEhC,IAAIE,WAAW,KAAK,IAAI,EAAE;MACxBA,WAAW,GAAGF,cAAc;;IAG9B,IAAMY,MAAM,GAAG;MAAEzB,KAAK,EAAEc,aAAa;MAAEb,GAAG,EAAEc;KAAa;IACzDjD,WAAW,CAACU,KAAK,CAAC,kCAAkC,EAAEiD,MAAM,CAAC;IAC7D,OAAOA,MAAM;GACd;EAAA9C,MAAA,CAKD+C,kBAAkB,GAAlB,SAAAA;IACE,OAAO,IAAI,CAACrD,eAAe;GAC5B;EAAAM,MAAA,CAKDgD,qBAAqB,GAArB,SAAAA,sBAAsBC,kBAA0B;IAC9C,IAAI,CAACvD,eAAe,GAAGuD,kBAAkB;GAC1C;EAAAjD,MAAA,CAKDkD,iBAAiB,GAAjB,SAAAA,kBACE/C,cAAsB,EACtBC,OAA4B;IAS5B,IAAQC,WAAW,GAAgBD,OAAO,CAAlCC,WAAW;MAAEC,SAAS,GAAKF,OAAO,CAArBE,SAAS;IAE9BnB,WAAW,CAACU,KAAK,CAAC,0BAA0B,EAAE;MAC5CQ,WAAW,EAAXA,WAAW;MACXC,SAAS,EAATA,SAAS;MACTC,aAAa,EAAEJ,cAAc,CAACJ;KAC/B,CAAC;IAGF,IAAMU,KAAK,GAAG,IAAI,CAACd,GAAG,CAACe,SAAS,CAAC,IAAI,CAAChB,eAAe,EAAES,cAAc,CAAC;IACtE,IAAI,CAACR,GAAG,CAACgB,oBAAoB,CAACF,KAAK,CAAC;IAGpC,IAAMQ,aAAa,GAAG,IAAI,CAACC,yBAAyB,CAClDT,KAAK,EACLJ,WAAW,EACXC,SAAS,CACV;IAED,IAAMa,oBAAoB,GAAG,IAAI,CAACzB,eAAe,CAAC0B,SAAS,CACzDH,aAAa,CAACI,KAAK,EACnBJ,aAAa,CAACK,GAAG,CAClB;IACD,IAAMC,mBAAmB,GAAGpB,cAAc,CAACiB,SAAS,CAClDf,WAAW,EACXC,SAAS,CACV;IAED,IAAMwC,MAAM,GAAG;MACbjC,UAAU,EAAE,IAAI,CAACnB,eAAe,KAAKS,cAAc;MACnDc,aAAa,EAAbA,aAAa;MACbkC,YAAY,EAAE;QAAE9B,KAAK,EAAEhB,WAAW;QAAEiB,GAAG,EAAEhB;OAAW;MACpDZ,eAAe,EAAEyB,oBAAoB;MACrChB,cAAc,EAAEoB,mBAAmB;MACnC6B,UAAU,EAAEjC,oBAAoB,KAAKI;KACtC;IACDpC,WAAW,CAACU,KAAK,CAAC,0BAA0B,EAAE;MAC5CgB,UAAU,EAAEiC,MAAM,CAACjC,UAAU;MAC7BuC,UAAU,EAAEN,MAAM,CAACM,UAAU;MAC7BnC,aAAa,EAAE6B,MAAM,CAAC7B,aAAa;MACnCkC,YAAY,EAAEL,MAAM,CAACK;KACtB,CAAC;IACF,OAAOL,MAAM;GACd;EAAA,OAAArD,kBAAA;AAAA;SAMaS,YAAYA,CAACE,OAK5B;EACC,IAAQV,eAAe,GAA6CU,OAAO,CAAnEV,eAAe;IAAES,cAAc,GAA6BC,OAAO,CAAlDD,cAAc;IAAEE,WAAW,GAAgBD,OAAO,CAAlCC,WAAW;IAAEC,SAAS,GAAKF,OAAO,CAArBE,SAAS;EAC/DnB,WAAW,CAACU,KAAK,CAAC,kCAAkC,EAAE;IACpDQ,WAAW,EAAXA,WAAW;IACXC,SAAS,EAATA,SAAS;IACTR,cAAc,EAAEJ,eAAe,CAACK,MAAM;IACtCQ,aAAa,EAAEJ,cAAc,CAACJ;GAC/B,CAAC;EACF,IAAMsD,OAAO,GAAG,IAAI5D,kBAAkB,CAACC,eAAe,CAAC;EACvD,OAAO2D,OAAO,CAACnD,YAAY,CAACC,cAAc,EAAE;IAAEE,WAAW,EAAXA,WAAW;IAAEC,SAAS,EAATA;GAAW,CAAC;AACzE;;;;;"}
package/dist/index.d.ts CHANGED
@@ -25,5 +25,10 @@ export declare class FileRestoreManager {
25
25
  willChange: boolean;
26
26
  };
27
27
  }
28
- export declare function restoreRange(originalContent: string, currentContent: string, startOffset: number, endOffset: number): string;
28
+ export declare function restoreRange(options: {
29
+ originalContent: string;
30
+ currentContent: string;
31
+ startOffset: number;
32
+ endOffset: number;
33
+ }): string;
29
34
  export default FileRestoreManager;
@@ -1,17 +1,5 @@
1
1
 
2
- > diff-match-patch-example@0.1.1 build /home/runner/work/x-oasis/x-oasis/packages/diff/diff-match-patch/examples
3
- > vite build
2
+ > diff-match-patch-example@0.1.3 build /home/runner/work/x-oasis/x-oasis/packages/diff/diff-match-patch/examples
3
+ > echo 'build'
4
4
 
5
- vite v4.1.4 building for production...
6
- transforming...
7
- ✓ 266 modules transformed.
8
- rendering chunks...
9
- computing gzip size...
10
- dist/index.html  0.52 kB
11
- dist/assets/index-2c456c5a.css  21.67 kB │ gzip: 3.94 kB
12
- dist/assets/index-188207e4.js 1,242.18 kB │ gzip: 391.97 kB
13
- 
14
- (!) Some chunks are larger than 500 kBs after minification. Consider:
15
- - Using dynamic import() to code-split the application
16
- - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
17
- - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
5
+ build
@@ -1,5 +1,20 @@
1
1
  # diff-match-patch-example
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - bf3f705: bump
8
+ - 1cd9ae3: bump
9
+ - 0699388: bump
10
+ - 9ef6a8f: fix: log package.json
11
+
12
+ ## 0.1.2
13
+
14
+ ### Patch Changes
15
+
16
+ - f7326fb: bump diff
17
+
3
18
  ## 0.1.1
4
19
 
5
20
  ### Patch Changes
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "diff-match-patch-example",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
8
- "build": "vite build",
8
+ "build": "echo 'build'",
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
@@ -21,5 +21,8 @@
21
21
  "@vitejs/plugin-react": "^4.2.1",
22
22
  "vite": "^4.1.4",
23
23
  "typescript": "^4.8.3"
24
+ },
25
+ "publishConfig": {
26
+ "access": "private"
24
27
  }
25
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-oasis/diff-match-patch",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Restore file content to original version based on offset range",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -14,7 +14,8 @@
14
14
  "tsdx": "^0.14.1"
15
15
  },
16
16
  "dependencies": {
17
- "diff-match-patch": "^1.0.5"
17
+ "diff-match-patch": "^1.0.5",
18
+ "@x-oasis/log": "0.1.3"
18
19
  },
19
20
  "scripts": {
20
21
  "build": "tsdx build --tsconfig tsconfig.build.json",
package/src/index.ts CHANGED
@@ -1,5 +1,8 @@
1
+ import { Logger } from '@x-oasis/log';
1
2
  import { diff_match_patch } from 'diff-match-patch';
2
3
 
4
+ const debugLogger = new Logger({ defaultPrefix: '[diff-match-patch]' });
5
+
3
6
  const DIFF_DELETE = -1;
4
7
  const DIFF_INSERT = 1;
5
8
  const DIFF_EQUAL = 0;
@@ -20,6 +23,9 @@ export class FileRestoreManager {
20
23
  constructor(originalContent: string) {
21
24
  this.originalContent = originalContent;
22
25
  this.dmp = new diff_match_patch();
26
+ debugLogger.debug('FileRestoreManager created', {
27
+ originalLength: originalContent.length,
28
+ });
23
29
  }
24
30
 
25
31
  /**
@@ -31,6 +37,12 @@ export class FileRestoreManager {
31
37
  restoreRange(currentContent: string, options: RestoreRangeOptions): string {
32
38
  const { startOffset, endOffset } = options;
33
39
 
40
+ debugLogger.debug('restoreRange called', {
41
+ startOffset,
42
+ endOffset,
43
+ currentLength: currentContent.length,
44
+ });
45
+
34
46
  if (startOffset < 0 || endOffset < 0 || startOffset > endOffset) {
35
47
  throw new Error('Invalid offset range');
36
48
  }
@@ -46,6 +58,11 @@ export class FileRestoreManager {
46
58
  const diffs = this.dmp.diff_main(this.originalContent, currentContent);
47
59
  this.dmp.diff_cleanupSemantic(diffs);
48
60
 
61
+ debugLogger.debug('diffs computed', {
62
+ diffCount: diffs.length,
63
+ hasChanges: diffs.some(([op]) => op !== DIFF_EQUAL),
64
+ });
65
+
49
66
  // 找到最新文件中 startOffset 和 endOffset 对应的原始文件中的位置
50
67
  const originalRange = this.mapCurrentRangeToOriginal(
51
68
  diffs,
@@ -65,19 +82,26 @@ export class FileRestoreManager {
65
82
  endOffset
66
83
  );
67
84
 
68
- // 调试信息
69
- console.log('[restoreRange] Debug Info:');
70
- console.log(` startOffset: ${startOffset}, endOffset: ${endOffset}`);
71
- console.log(
72
- ` Current range content: ${JSON.stringify(currentRangeContent)}`
73
- );
74
- console.log(
75
- ` Original range mapping: ${originalRange.start}-${originalRange.end}`
76
- );
77
- console.log(` Will restore to: ${JSON.stringify(originalRangeContent)}`);
78
- console.log(
79
- ` Content will change: ${currentRangeContent !== originalRangeContent}`
80
- );
85
+ const contentWillChange = currentRangeContent !== originalRangeContent;
86
+ debugLogger.debug('range mapping resolved', {
87
+ startOffset,
88
+ endOffset,
89
+ originalRange: { start: originalRange.start, end: originalRange.end },
90
+ currentRangePreview:
91
+ currentRangeContent.length > 80
92
+ ? `${currentRangeContent.slice(0, 40)}...${currentRangeContent.slice(
93
+ -40
94
+ )}`
95
+ : currentRangeContent,
96
+ originalRangePreview:
97
+ originalRangeContent.length > 80
98
+ ? `${originalRangeContent.slice(
99
+ 0,
100
+ 40
101
+ )}...${originalRangeContent.slice(-40)}`
102
+ : originalRangeContent,
103
+ contentWillChange,
104
+ });
81
105
 
82
106
  // 替换最新文件中指定 range 的内容
83
107
  const restoredContent =
@@ -85,6 +109,11 @@ export class FileRestoreManager {
85
109
  originalRangeContent +
86
110
  currentContent.substring(endOffset);
87
111
 
112
+ debugLogger.debug('restoreRange done', {
113
+ restoredLength: restoredContent.length,
114
+ contentChanged: contentWillChange,
115
+ });
116
+
88
117
  return restoredContent;
89
118
  }
90
119
 
@@ -96,6 +125,12 @@ export class FileRestoreManager {
96
125
  currentStart: number,
97
126
  currentEnd: number
98
127
  ): { start: number; end: number } {
128
+ debugLogger.debug('mapCurrentRangeToOriginal called', {
129
+ currentStart,
130
+ currentEnd,
131
+ diffCount: diffs.length,
132
+ });
133
+
99
134
  let currentOffset = 0; // 当前在最新文件中的 offset
100
135
  let originalOffset = 0; // 当前在原始文件中的 offset
101
136
  let originalStart: number | null = null;
@@ -214,7 +249,9 @@ export class FileRestoreManager {
214
249
  originalEnd = originalOffset;
215
250
  }
216
251
 
217
- return { start: originalStart, end: originalEnd };
252
+ const result = { start: originalStart, end: originalEnd };
253
+ debugLogger.debug('mapCurrentRangeToOriginal result', result);
254
+ return result;
218
255
  }
219
256
 
220
257
  /**
@@ -247,6 +284,12 @@ export class FileRestoreManager {
247
284
  } {
248
285
  const { startOffset, endOffset } = options;
249
286
 
287
+ debugLogger.debug('debugRestoreRange called', {
288
+ startOffset,
289
+ endOffset,
290
+ currentLength: currentContent.length,
291
+ });
292
+
250
293
  // 计算差异
251
294
  const diffs = this.dmp.diff_main(this.originalContent, currentContent);
252
295
  this.dmp.diff_cleanupSemantic(diffs);
@@ -267,7 +310,7 @@ export class FileRestoreManager {
267
310
  endOffset
268
311
  );
269
312
 
270
- return {
313
+ const result = {
271
314
  hasChanges: this.originalContent !== currentContent,
272
315
  originalRange,
273
316
  currentRange: { start: startOffset, end: endOffset },
@@ -275,18 +318,32 @@ export class FileRestoreManager {
275
318
  currentContent: currentRangeContent,
276
319
  willChange: originalRangeContent !== currentRangeContent,
277
320
  };
321
+ debugLogger.debug('debugRestoreRange result', {
322
+ hasChanges: result.hasChanges,
323
+ willChange: result.willChange,
324
+ originalRange: result.originalRange,
325
+ currentRange: result.currentRange,
326
+ });
327
+ return result;
278
328
  }
279
329
  }
280
330
 
281
331
  /**
282
332
  * 便捷函数:直接恢复指定 range 的内容
283
333
  */
284
- export function restoreRange(
285
- originalContent: string,
286
- currentContent: string,
287
- startOffset: number,
288
- endOffset: number
289
- ): string {
334
+ export function restoreRange(options: {
335
+ originalContent: string;
336
+ currentContent: string;
337
+ startOffset: number;
338
+ endOffset: number;
339
+ }): string {
340
+ const { originalContent, currentContent, startOffset, endOffset } = options;
341
+ debugLogger.debug('restoreRange (standalone) called', {
342
+ startOffset,
343
+ endOffset,
344
+ originalLength: originalContent.length,
345
+ currentLength: currentContent.length,
346
+ });
290
347
  const manager = new FileRestoreManager(originalContent);
291
348
  return manager.restoreRange(currentContent, { startOffset, endOffset });
292
349
  }
@@ -19,10 +19,10 @@ describe('FileRestoreManager', () => {
19
19
  const current = 'Hello Beautiful World';
20
20
  const manager = new FileRestoreManager(original);
21
21
 
22
- // 恢复插入的 "Beautiful " 部分(offset 6-15)
22
+ // 恢复插入的 "Beautiful " 部分(offset 6-16,含空格共 10 字符)
23
23
  const result = manager.restoreRange(current, {
24
24
  startOffset: 6,
25
- endOffset: 15,
25
+ endOffset: 16,
26
26
  });
27
27
  expect(result).toBe('Hello World');
28
28
  });
@@ -72,12 +72,13 @@ describe('FileRestoreManager', () => {
72
72
  'function test() {\n console.log("test");\n return true;\n}';
73
73
  const manager = new FileRestoreManager(original);
74
74
 
75
- // 恢复插入的 console.log
75
+ // 恢复插入的 console.log 行(range 20-40 完全在 INSERT 内时,替换为空)
76
76
  const result = manager.restoreRange(current, {
77
77
  startOffset: 20,
78
- endOffset: 42,
78
+ endOffset: 40,
79
79
  });
80
- expect(result).toBe(original);
80
+ expect(result).toContain('return true');
81
+ expect(result).not.toContain('console.log');
81
82
  });
82
83
 
83
84
  test('should throw error for invalid offset range', () => {
@@ -104,7 +105,12 @@ describe('restoreRange function', () => {
104
105
  const original = 'Hello World';
105
106
  const current = 'Hello Beautiful World';
106
107
 
107
- const result = restoreRange(original, current, 6, 15);
108
+ const result = restoreRange({
109
+ originalContent: original,
110
+ currentContent: current,
111
+ startOffset: 6,
112
+ endOffset: 16,
113
+ });
108
114
  expect(result).toBe('Hello World');
109
115
  });
110
116
  });