@x-oasis/map-diff-range 0.1.0

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.
@@ -0,0 +1,18 @@
1
+
2
+ > @x-oasis/map-diff-range@0.1.0 build /home/runner/work/x-oasis/x-oasis/packages/diff/map-diff-range
3
+ > tsdx build --tsconfig tsconfig.build.json
4
+
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
+ @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
+ ⠙ Creating entry file
8
+ ✓ Creating entry file 2.2 secs
9
+ ⠙ Building modules
10
+ ⠹ Building modules
11
+ ⠸ Building modules
12
+ ⠼ Building modules
13
+ ⠴ Building modules
14
+ ⠦ Building modules
15
+ [tsdx]: Your rootDir is currently set to "./". Please change your rootDir to "./src".
16
+ TSDX has deprecated setting tsconfig.compilerOptions.rootDir to "./" as it caused buggy output for declarationMaps and more.
17
+ You may also need to change your include to remove "test", which also caused declarations to be unnecessarily created for test files.
18
+ ✓ Building modules 3.5 secs
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # @x-oasis/map-diff-range
2
+
3
+ ## 0.1.0
4
+
5
+ ### Initial Release
6
+
7
+ - 实现基于 diff-match-patch 的 range 映射功能
8
+ - 提供 `mapNewerRangeToOlder` 和 `mapOlderRangeToNewer` 函数用于双向 range 映射
9
+ - 提供 `analyzeFragmentChange` 函数用于片段级变更分析
10
+ - 提供 `resolveGroupChangeFragments` 函数用于三路变更解析(originalContent → currentContent → finalContent)
11
+ - 支持变更类型检测(equal、onlyDeletion、onlyInsertion、replacement)
12
+ - 自动生成语义化变更摘要
13
+ - 提供完整的交互式示例(React + Vite)
14
+ - 支持 GitHub Pages 部署
package/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # @x-oasis/map-diff-range
2
+
3
+ 基于 diff-match-patch 的 range 映射与变更分析工具,用于在 currentContent 和 nextContent 之间映射 range,并分析片段级变更。
4
+
5
+ ## 功能
6
+
7
+ - **Range 映射**:在新旧内容之间双向映射 offset range
8
+ - **变更分析**:分析两段片段之间的变更类型(equal、onlyDeletion、onlyInsertion、replacement)
9
+ - **内容映射**:根据 currentContent 和 currentRange,找到 nextContent 对应的 range
10
+ - **语义化描述**:自动生成简短的变更摘要
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ npm install @x-oasis/map-diff-range
16
+ # 或
17
+ pnpm add @x-oasis/map-diff-range
18
+ ```
19
+
20
+ ## 在线示例
21
+
22
+ 查看 [交互式示例](./examples/index.html) 来可视化地了解如何使用这个库。示例包含:
23
+
24
+ - 文件内容输入(currentContent、nextContent)
25
+ - 交互式的 offset range 映射功能
26
+ - 详细的变更分析展示
27
+ - 多个预设的测试场景
28
+
29
+ ## 使用方法
30
+
31
+ ### 基本用法:Range 映射
32
+
33
+ ```typescript
34
+ import { mapNewerRangeToOlder, mapOlderRangeToNewer } from '@x-oasis/map-diff-range';
35
+
36
+ const older = 'Hello World';
37
+ const newer = 'Hello Beautiful World';
38
+
39
+ // 将新内容中的 range 映射到旧内容
40
+ const olderRange = mapNewerRangeToOlder(older, newer, 6, 16);
41
+ console.log(olderRange); // { start: 6, end: 6 }
42
+
43
+ // 将旧内容中的 range 映射到新内容
44
+ const newerRange = mapOlderRangeToNewer(older, newer, 6, 6);
45
+ console.log(newerRange); // { start: 6, end: 16 }
46
+ ```
47
+
48
+ ### 变更分析
49
+
50
+ ```typescript
51
+ import { analyzeFragmentChange } from '@x-oasis/map-diff-range';
52
+
53
+ const original = 'Hello World';
54
+ const final = 'Hello Beautiful World';
55
+
56
+ const analysis = analyzeFragmentChange(original, final);
57
+ console.log(analysis.equal); // false
58
+ console.log(analysis.onlyInsertion); // true
59
+ console.log(analysis.summary); // "新增: Beautiful "
60
+ console.log(analysis.diffs); // [[0, "Hello "], [1, "Beautiful "], [0, "World"]]
61
+ ```
62
+
63
+ ### Range 映射与变更分析
64
+
65
+ ```typescript
66
+ import { resolveGroupChangeFragments } from '@x-oasis/map-diff-range';
67
+
68
+ const current = '<h1 class="title text-xl">Name</h1>';
69
+ const next = '<h1 class="text-xl font-bold">Name</h1>';
70
+
71
+ const result = resolveGroupChangeFragments({
72
+ currentContent: current,
73
+ nextContent: next,
74
+ currentRange: { start: 4, end: 30 },
75
+ });
76
+
77
+ if (result) {
78
+ console.log(result.nextRange); // { start: 4, end: 35 }
79
+ console.log(result.currentFragment); // 'class="title text-xl">Name</h1>'
80
+ console.log(result.nextFragment); // 'class="text-xl font-bold">Name</h1>'
81
+ console.log(result.changeAnalysis.replacement); // true
82
+ console.log(result.changeAnalysis.summary); // "替换: title → font-bold"
83
+ }
84
+ ```
85
+
86
+ ## API
87
+
88
+ ### Range
89
+
90
+ ```typescript
91
+ interface Range {
92
+ start: number; // 起始 offset(含头)
93
+ end: number; // 结束 offset(含尾)
94
+ }
95
+ ```
96
+
97
+ ### FragmentChangeAnalysis
98
+
99
+ ```typescript
100
+ interface FragmentChangeAnalysis {
101
+ originalFragment: string; // 原始片段内容
102
+ finalFragment: string; // 最终片段内容
103
+ equal: boolean; // 是否完全相同
104
+ onlyDeletion: boolean; // 仅删除(无新增)
105
+ onlyInsertion: boolean; // 仅新增(无删除)
106
+ replacement: boolean; // 既有删除又有新增(替换)
107
+ summary: string; // 语义化描述(简短)
108
+ diffs: Array<[number, string]>; // 详细 diff 条目
109
+ }
110
+ ```
111
+
112
+ ### mapNewerRangeToOlder
113
+
114
+ 将「新内容」中的 offset range 映射到「旧内容」中的 offset range。
115
+
116
+ ```typescript
117
+ function mapNewerRangeToOlder(
118
+ olderContent: string,
119
+ newerContent: string,
120
+ startOffset: number,
121
+ endOffset: number
122
+ ): Range
123
+ ```
124
+
125
+ **参数:**
126
+ - `olderContent`: 旧内容(diff 的 text1)
127
+ - `newerContent`: 新内容(diff 的 text2)
128
+ - `startOffset`: 新内容中的 range 起始 offset
129
+ - `endOffset`: 新内容中的 range 结束 offset(含尾)
130
+
131
+ **返回:** 旧内容中对应的 range
132
+
133
+ ### mapOlderRangeToNewer
134
+
135
+ 将「旧内容」中的 offset range 映射到「新内容」中的 offset range。
136
+
137
+ ```typescript
138
+ function mapOlderRangeToNewer(
139
+ olderContent: string,
140
+ newerContent: string,
141
+ startOffset: number,
142
+ endOffset: number
143
+ ): Range
144
+ ```
145
+
146
+ **参数:**
147
+ - `olderContent`: 旧内容(diff 的 text1)
148
+ - `newerContent`: 新内容(diff 的 text2)
149
+ - `startOffset`: 旧内容中的 range 起始 offset
150
+ - `endOffset`: 旧内容中的 range 结束 offset
151
+
152
+ **返回:** 新内容中对应的 range
153
+
154
+ ### analyzeFragmentChange
155
+
156
+ 对比两段片段内容,分析发生的变更类型并生成简短描述。
157
+
158
+ ```typescript
159
+ function analyzeFragmentChange(
160
+ originalFragment: string,
161
+ finalFragment: string
162
+ ): FragmentChangeAnalysis
163
+ ```
164
+
165
+ **参数:**
166
+ - `originalFragment`: 原始片段
167
+ - `finalFragment`: 变更后片段
168
+
169
+ **返回:** 变更分析结果
170
+
171
+ ### resolveGroupChangeFragments
172
+
173
+ 根据当前内容和 range,找到下一个内容对应的 range,并分析二者之间的变更。
174
+
175
+ ```typescript
176
+ function resolveGroupChangeFragments(options: {
177
+ currentContent: string;
178
+ nextContent: string;
179
+ currentRange: Range;
180
+ }): {
181
+ nextRange: Range;
182
+ currentFragment: string;
183
+ nextFragment: string;
184
+ changeAnalysis: FragmentChangeAnalysis;
185
+ } | undefined
186
+ ```
187
+
188
+ **参数:**
189
+ - `options.currentContent`: 当前文件内容
190
+ - `options.nextContent`: 下一个文件内容
191
+ - `options.currentRange`: 当前文件中需要映射的 range
192
+
193
+ **返回:** 映射得到的 nextRange、对应片段、以及片段级变更分析;若无法解析则返回 `undefined`
194
+
195
+ ## 示例场景
196
+
197
+ ### 场景 1: Class 属性变更
198
+
199
+ ```typescript
200
+ const current = '<h1 class="title text-xl">Name</h1>';
201
+ const next = '<h1 class="text-xl font-bold">Name</h1>';
202
+
203
+ const result = resolveGroupChangeFragments({
204
+ currentContent: current,
205
+ nextContent: next,
206
+ currentRange: { start: 4, end: 30 },
207
+ });
208
+
209
+ // result.changeAnalysis.replacement === true
210
+ // result.changeAnalysis.summary === "替换: title → font-bold"
211
+ ```
212
+
213
+ ### 场景 2: 文本替换
214
+
215
+ ```typescript
216
+ const current = 'Hello Beautiful World';
217
+ const next = 'Hello Amazing World';
218
+
219
+ const result = resolveGroupChangeFragments({
220
+ currentContent: current,
221
+ nextContent: next,
222
+ currentRange: { start: 6, end: 15 },
223
+ });
224
+
225
+ // result.changeAnalysis.replacement === true
226
+ // result.changeAnalysis.summary === "替换: Beautiful → Amazing "
227
+ ```
228
+
229
+ ### 场景 3: 仅删除
230
+
231
+ ```typescript
232
+ const current = 'Hello Beautiful World';
233
+ const next = 'Hello World';
234
+
235
+ const result = resolveGroupChangeFragments({
236
+ currentContent: current,
237
+ nextContent: next,
238
+ currentRange: { start: 6, end: 15 },
239
+ });
240
+
241
+ // result.changeAnalysis.onlyDeletion === true
242
+ // result.changeAnalysis.summary === "删除: Beautiful "
243
+ ```
244
+
245
+ ### 场景 4: 仅新增
246
+
247
+ ```typescript
248
+ const current = 'Hello World';
249
+ const next = 'Hello Beautiful World';
250
+
251
+ const result = resolveGroupChangeFragments({
252
+ currentContent: current,
253
+ nextContent: next,
254
+ currentRange: { start: 6, end: 6 },
255
+ });
256
+
257
+ // result.changeAnalysis.onlyInsertion === true
258
+ // result.changeAnalysis.summary === "新增: Beautiful "
259
+ ```
260
+
261
+ ## 错误处理
262
+
263
+ 如果提供的 offset range 无效,`resolveGroupChangeFragments` 会返回 `undefined`:
264
+
265
+ - `startOffset < 0` 或 `endOffset < 0`
266
+ - `startOffset > endOffset`
267
+ - `endOffset > currentContent.length`
268
+
269
+ ## 依赖
270
+
271
+ - [diff-match-patch](https://www.npmjs.com/package/diff-match-patch): Google 的 diff-match-patch 库
272
+
273
+ ## License
274
+
275
+ ISC
@@ -0,0 +1,27 @@
1
+ export interface Range {
2
+ start: number;
3
+ end: number;
4
+ }
5
+ export interface FragmentChangeAnalysis {
6
+ originalFragment: string;
7
+ finalFragment: string;
8
+ equal: boolean;
9
+ onlyDeletion: boolean;
10
+ onlyInsertion: boolean;
11
+ replacement: boolean;
12
+ summary: string;
13
+ diffs: Array<[number, string]>;
14
+ }
15
+ export declare function mapNewerRangeToOlder(olderContent: string, newerContent: string, startOffset: number, endOffset: number): Range;
16
+ export declare function mapOlderRangeToNewer(olderContent: string, newerContent: string, startOffset: number, endOffset: number): Range;
17
+ export declare function analyzeFragmentChange(originalFragment: string, finalFragment: string): FragmentChangeAnalysis;
18
+ export declare function resolveGroupChangeFragments(options: {
19
+ currentContent: string;
20
+ nextContent: string;
21
+ currentRange: Range;
22
+ }): {
23
+ nextRange: Range;
24
+ currentFragment: string;
25
+ nextFragment: string;
26
+ changeAnalysis: FragmentChangeAnalysis;
27
+ } | undefined;
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+
2
+ 'use strict'
3
+
4
+ if (process.env.NODE_ENV === 'production') {
5
+ module.exports = require('./map-diff-range.cjs.production.min.js')
6
+ } else {
7
+ module.exports = require('./map-diff-range.cjs.development.js')
8
+ }
@@ -0,0 +1,220 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var diffMatchPatch = require('diff-match-patch');
6
+
7
+ var DIFF_DELETE = -1;
8
+ var DIFF_INSERT = 1;
9
+ var DIFF_EQUAL = 0;
10
+ function mapNewerRangeToOlder(olderContent, newerContent, startOffset, endOffset) {
11
+ var dmp = new diffMatchPatch.diff_match_patch();
12
+ var diffs = dmp.diff_main(olderContent, newerContent);
13
+ dmp.diff_cleanupSemantic(diffs);
14
+ var newerOffset = 0;
15
+ var olderOffset = 0;
16
+ var olderStart = null;
17
+ var olderEnd = null;
18
+ for (var i = 0; i < diffs.length; i++) {
19
+ var _diffs$i = diffs[i],
20
+ op = _diffs$i[0],
21
+ text = _diffs$i[1];
22
+ var len = text.length;
23
+ var nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;
24
+ if (op === DIFF_EQUAL) {
25
+ var rangeStart = newerOffset;
26
+ var rangeEnd = newerOffset + len;
27
+ if (olderStart === null && startOffset >= rangeStart && startOffset < rangeEnd) {
28
+ olderStart = olderOffset + (startOffset - rangeStart);
29
+ }
30
+ if (olderEnd === null && endOffset > rangeStart && endOffset <= rangeEnd) {
31
+ var offsetInRange = endOffset - rangeStart;
32
+ olderEnd = olderOffset + offsetInRange;
33
+ if (endOffset === rangeEnd && nextDiff && nextDiff[0] === DIFF_DELETE) {
34
+ olderEnd = olderOffset + len + nextDiff[1].length;
35
+ }
36
+ }
37
+ newerOffset += len;
38
+ olderOffset += len;
39
+ } else if (op === DIFF_INSERT) {
40
+ var _rangeStart = newerOffset;
41
+ var _rangeEnd = newerOffset + len;
42
+ if (olderStart === null && startOffset >= _rangeStart && startOffset < _rangeEnd) {
43
+ olderStart = olderOffset;
44
+ }
45
+ if (olderEnd === null && endOffset > _rangeStart && endOffset <= _rangeEnd) {
46
+ olderEnd = olderOffset;
47
+ }
48
+ newerOffset += len;
49
+ } else if (op === DIFF_DELETE) {
50
+ if (olderStart === null && startOffset <= newerOffset) {
51
+ olderStart = olderOffset;
52
+ }
53
+ if (olderEnd === null && endOffset <= newerOffset) {
54
+ olderEnd = endOffset === newerOffset ? olderOffset + len : olderOffset;
55
+ } else if (olderEnd === null && endOffset === newerOffset) {
56
+ olderEnd = olderOffset + len;
57
+ }
58
+ olderOffset += len;
59
+ }
60
+ if (olderStart !== null && olderEnd !== null) break;
61
+ }
62
+ if (olderStart === null) olderStart = olderOffset;
63
+ if (olderEnd === null) olderEnd = olderOffset;
64
+ return {
65
+ start: olderStart,
66
+ end: olderEnd
67
+ };
68
+ }
69
+ function mapOlderRangeToNewer(olderContent, newerContent, startOffset, endOffset) {
70
+ var dmp = new diffMatchPatch.diff_match_patch();
71
+ var diffs = dmp.diff_main(olderContent, newerContent);
72
+ dmp.diff_cleanupSemantic(diffs);
73
+ var olderOffset = 0;
74
+ var newerOffset = 0;
75
+ var newerStart = null;
76
+ var newerEnd = null;
77
+ for (var i = 0; i < diffs.length; i++) {
78
+ var _diffs$i2 = diffs[i],
79
+ op = _diffs$i2[0],
80
+ text = _diffs$i2[1];
81
+ var len = text.length;
82
+ var nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;
83
+ if (op === DIFF_EQUAL) {
84
+ var oldRangeStart = olderOffset;
85
+ var oldRangeEnd = olderOffset + len;
86
+ if (newerStart === null && startOffset >= oldRangeStart && startOffset < oldRangeEnd) {
87
+ newerStart = newerOffset + (startOffset - oldRangeStart);
88
+ }
89
+ if (newerEnd === null && endOffset > oldRangeStart && endOffset <= oldRangeEnd) {
90
+ var offsetInRange = endOffset - oldRangeStart;
91
+ newerEnd = newerOffset + offsetInRange;
92
+ if (endOffset === oldRangeEnd && nextDiff && nextDiff[0] === DIFF_INSERT) {
93
+ newerEnd = newerOffset + len + nextDiff[1].length;
94
+ }
95
+ }
96
+ olderOffset += len;
97
+ newerOffset += len;
98
+ } else if (op === DIFF_INSERT) {
99
+ newerOffset += len;
100
+ } else if (op === DIFF_DELETE) {
101
+ var _oldRangeStart = olderOffset;
102
+ var _oldRangeEnd = olderOffset + len;
103
+ if (newerStart === null && startOffset >= _oldRangeStart && startOffset < _oldRangeEnd) {
104
+ newerStart = newerOffset;
105
+ }
106
+ if (newerEnd === null && endOffset > _oldRangeStart && endOffset <= _oldRangeEnd) {
107
+ newerEnd = newerOffset;
108
+ } else if (newerEnd === null && endOffset <= olderOffset) {
109
+ newerEnd = newerOffset;
110
+ }
111
+ olderOffset += len;
112
+ }
113
+ if (newerStart !== null && newerEnd !== null) break;
114
+ }
115
+ if (newerStart === null) newerStart = newerOffset;
116
+ if (newerEnd === null) newerEnd = newerOffset;
117
+ return {
118
+ start: newerStart,
119
+ end: newerEnd
120
+ };
121
+ }
122
+ function analyzeFragmentChange(originalFragment, finalFragment) {
123
+ var dmp = new diffMatchPatch.diff_match_patch();
124
+ var diffs = dmp.diff_main(originalFragment, finalFragment);
125
+ dmp.diff_cleanupSemantic(diffs);
126
+ var hasDelete = diffs.some(function (_ref) {
127
+ var op = _ref[0];
128
+ return op === DIFF_DELETE;
129
+ });
130
+ var hasInsert = diffs.some(function (_ref2) {
131
+ var op = _ref2[0];
132
+ return op === DIFF_INSERT;
133
+ });
134
+ var equal = !hasDelete && !hasInsert;
135
+ var onlyDeletion = hasDelete && !hasInsert;
136
+ var onlyInsertion = !hasDelete && hasInsert;
137
+ var replacement = hasDelete && hasInsert;
138
+ var summary;
139
+ if (equal) {
140
+ summary = '无变更';
141
+ } else if (onlyDeletion) {
142
+ var deleted = diffs.filter(function (_ref3) {
143
+ var op = _ref3[0];
144
+ return op === DIFF_DELETE;
145
+ }).map(function (_ref4) {
146
+ var text = _ref4[1];
147
+ return text;
148
+ }).join('');
149
+ summary = "\u5220\u9664: " + formatSnippet(deleted);
150
+ } else if (onlyInsertion) {
151
+ var inserted = diffs.filter(function (_ref5) {
152
+ var op = _ref5[0];
153
+ return op === DIFF_INSERT;
154
+ }).map(function (_ref6) {
155
+ var text = _ref6[1];
156
+ return text;
157
+ }).join('');
158
+ summary = "\u65B0\u589E: " + formatSnippet(inserted);
159
+ } else {
160
+ var _deleted = diffs.filter(function (_ref7) {
161
+ var op = _ref7[0];
162
+ return op === DIFF_DELETE;
163
+ }).map(function (_ref8) {
164
+ var text = _ref8[1];
165
+ return text;
166
+ }).join('');
167
+ var _inserted = diffs.filter(function (_ref9) {
168
+ var op = _ref9[0];
169
+ return op === DIFF_INSERT;
170
+ }).map(function (_ref10) {
171
+ var text = _ref10[1];
172
+ return text;
173
+ }).join('');
174
+ summary = "\u66FF\u6362: " + formatSnippet(_deleted) + " \u2192 " + formatSnippet(_inserted);
175
+ }
176
+ return {
177
+ originalFragment: originalFragment,
178
+ finalFragment: finalFragment,
179
+ equal: equal,
180
+ onlyDeletion: onlyDeletion,
181
+ onlyInsertion: onlyInsertion,
182
+ replacement: replacement,
183
+ summary: summary,
184
+ diffs: diffs
185
+ };
186
+ }
187
+ function formatSnippet(s, maxLen) {
188
+ if (maxLen === void 0) {
189
+ maxLen = 40;
190
+ }
191
+ var t = s.replace(/\s+/g, ' ').trim();
192
+ if (t.length <= maxLen) return t;
193
+ return t.slice(0, maxLen) + "\u2026";
194
+ }
195
+ function resolveGroupChangeFragments(options) {
196
+ var currentContent = options.currentContent,
197
+ nextContent = options.nextContent,
198
+ currentRange = options.currentRange;
199
+ var startOffset = currentRange.start,
200
+ endOffset = currentRange.end;
201
+ if (startOffset < 0 || endOffset < 0 || startOffset > endOffset || endOffset > currentContent.length) {
202
+ return undefined;
203
+ }
204
+ var nextRange = mapOlderRangeToNewer(currentContent, nextContent, startOffset, endOffset);
205
+ var currentFragment = currentContent.substring(startOffset, endOffset);
206
+ var nextFragment = nextContent.substring(nextRange.start, nextRange.end);
207
+ var changeAnalysis = analyzeFragmentChange(currentFragment, nextFragment);
208
+ return {
209
+ nextRange: nextRange,
210
+ currentFragment: currentFragment,
211
+ nextFragment: nextFragment,
212
+ changeAnalysis: changeAnalysis
213
+ };
214
+ }
215
+
216
+ exports.analyzeFragmentChange = analyzeFragmentChange;
217
+ exports.mapNewerRangeToOlder = mapNewerRangeToOlder;
218
+ exports.mapOlderRangeToNewer = mapOlderRangeToNewer;
219
+ exports.resolveGroupChangeFragments = resolveGroupChangeFragments;
220
+ //# sourceMappingURL=map-diff-range.cjs.development.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-diff-range.cjs.development.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * 基于 diff-match-patch 的 range 映射与变更分析工具\n * 用于在 originalContent / currentContent / finalContent 之间映射 range,并分析片段级变更\n *\n * 参考: https://github.com/red-armor/x-oasis/blob/main/packages/diff/diff-match-patch/src/index.ts#L123\n */\n\nimport { diff_match_patch } from 'diff-match-patch';\n\n/** diff 操作常量,与 diff-match-patch 一致 */\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\n/** 单端 range,start 含头,end 含尾(substring 语义) */\nexport interface Range {\n start: number;\n end: number;\n}\n\n/** 片段级变更分析结果 */\nexport interface FragmentChangeAnalysis {\n /** 原始片段内容 */\n originalFragment: string;\n /** 最终片段内容 */\n finalFragment: string;\n /** 是否完全相同 */\n equal: boolean;\n /** 仅删除(无新增) */\n onlyDeletion: boolean;\n /** 仅新增(无删除) */\n onlyInsertion: boolean;\n /** 既有删除又有新增(替换) */\n replacement: boolean;\n /** 语义化描述(简短) */\n summary: string;\n /** 详细 diff 条目,便于上层做展示或进一步处理 */\n diffs: Array<[number, string]>;\n}\n\n/**\n * 将「新内容」中的 offset range 映射到「旧内容」中的 offset range\n * 等价于 x-oasis 的 mapCurrentRangeToOriginal:diffs = diff_main(older, newer),给定 newer 上的 [start,end],返回 older 上的 [start,end]\n *\n * @param olderContent 旧内容(diff 的 text1)\n * @param newerContent 新内容(diff 的 text2)\n * @param startOffset 新内容中的 range 起始 offset\n * @param endOffset 新内容中的 range 结束 offset(不含尾,即 [startOffset, endOffset) 或含尾由调用方约定,此处按 substring 含头含尾)\n * @returns 旧内容中对应的 range;若越界或无效则返回 { start: 0, end: 0 }\n */\nexport function mapNewerRangeToOlder(\n olderContent: string,\n newerContent: string,\n startOffset: number,\n endOffset: number\n): Range {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(olderContent, newerContent);\n dmp.diff_cleanupSemantic(diffs);\n\n let newerOffset = 0;\n let olderOffset = 0;\n let olderStart: number | null = null;\n let olderEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [op, text] = diffs[i];\n const len = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (op === DIFF_EQUAL) {\n const rangeStart = newerOffset;\n const rangeEnd = newerOffset + len;\n\n if (\n olderStart === null &&\n startOffset >= rangeStart &&\n startOffset < rangeEnd\n ) {\n olderStart = olderOffset + (startOffset - rangeStart);\n }\n if (\n olderEnd === null &&\n endOffset > rangeStart &&\n endOffset <= rangeEnd\n ) {\n const offsetInRange = endOffset - rangeStart;\n olderEnd = olderOffset + offsetInRange;\n if (endOffset === rangeEnd && nextDiff && nextDiff[0] === DIFF_DELETE) {\n olderEnd = olderOffset + len + nextDiff[1].length;\n }\n }\n\n newerOffset += len;\n olderOffset += len;\n } else if (op === DIFF_INSERT) {\n const rangeStart = newerOffset;\n const rangeEnd = newerOffset + len;\n\n if (\n olderStart === null &&\n startOffset >= rangeStart &&\n startOffset < rangeEnd\n ) {\n olderStart = olderOffset;\n }\n if (\n olderEnd === null &&\n endOffset > rangeStart &&\n endOffset <= rangeEnd\n ) {\n olderEnd = olderOffset;\n }\n\n newerOffset += len;\n } else if (op === DIFF_DELETE) {\n if (olderStart === null && startOffset <= newerOffset) {\n olderStart = olderOffset;\n }\n if (olderEnd === null && endOffset <= newerOffset) {\n olderEnd = endOffset === newerOffset ? olderOffset + len : olderOffset;\n } else if (olderEnd === null && endOffset === newerOffset) {\n olderEnd = olderOffset + len;\n }\n\n olderOffset += len;\n }\n\n if (olderStart !== null && olderEnd !== null) break;\n }\n\n if (olderStart === null) olderStart = olderOffset;\n if (olderEnd === null) olderEnd = olderOffset;\n\n return { start: olderStart, end: olderEnd };\n}\n\n/**\n * 将「旧内容」中的 offset range 映射到「新内容」中的 offset range\n * 与 mapNewerRangeToOlder 对称:diffs = diff_main(older, newer),给定 older 上的 [start,end],返回 newer 上的 [start,end]\n *\n * @param olderContent 旧内容(diff 的 text1)\n * @param newerContent 新内容(diff 的 text2)\n * @param startOffset 旧内容中的 range 起始 offset\n * @param endOffset 旧内容中的 range 结束 offset\n * @returns 新内容中对应的 range\n */\nexport function mapOlderRangeToNewer(\n olderContent: string,\n newerContent: string,\n startOffset: number,\n endOffset: number\n): Range {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(olderContent, newerContent);\n dmp.diff_cleanupSemantic(diffs);\n\n let olderOffset = 0;\n let newerOffset = 0;\n let newerStart: number | null = null;\n let newerEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [op, text] = diffs[i];\n const len = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (op === DIFF_EQUAL) {\n const oldRangeStart = olderOffset;\n const oldRangeEnd = olderOffset + len;\n\n if (\n newerStart === null &&\n startOffset >= oldRangeStart &&\n startOffset < oldRangeEnd\n ) {\n newerStart = newerOffset + (startOffset - oldRangeStart);\n }\n if (\n newerEnd === null &&\n endOffset > oldRangeStart &&\n endOffset <= oldRangeEnd\n ) {\n const offsetInRange = endOffset - oldRangeStart;\n newerEnd = newerOffset + offsetInRange;\n if (\n endOffset === oldRangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_INSERT\n ) {\n newerEnd = newerOffset + len + nextDiff[1].length;\n }\n }\n\n olderOffset += len;\n newerOffset += len;\n } else if (op === DIFF_INSERT) {\n newerOffset += len;\n } else if (op === DIFF_DELETE) {\n const oldRangeStart = olderOffset;\n const oldRangeEnd = olderOffset + len;\n\n if (\n newerStart === null &&\n startOffset >= oldRangeStart &&\n startOffset < oldRangeEnd\n ) {\n newerStart = newerOffset;\n }\n if (\n newerEnd === null &&\n endOffset > oldRangeStart &&\n endOffset <= oldRangeEnd\n ) {\n newerEnd = newerOffset;\n } else if (newerEnd === null && endOffset <= olderOffset) {\n newerEnd = newerOffset;\n }\n\n olderOffset += len;\n }\n\n if (newerStart !== null && newerEnd !== null) break;\n }\n\n if (newerStart === null) newerStart = newerOffset;\n if (newerEnd === null) newerEnd = newerOffset;\n\n return { start: newerStart, end: newerEnd };\n}\n\n/**\n * 对比两段片段内容,分析发生的变更类型并生成简短描述\n *\n * @param originalFragment 原始片段\n * @param finalFragment 变更后片段\n * @returns 变更分析结果\n */\nexport function analyzeFragmentChange(\n originalFragment: string,\n finalFragment: string\n): FragmentChangeAnalysis {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(originalFragment, finalFragment);\n dmp.diff_cleanupSemantic(diffs);\n\n const hasDelete = diffs.some(([op]) => op === DIFF_DELETE);\n const hasInsert = diffs.some(([op]) => op === DIFF_INSERT);\n const equal = !hasDelete && !hasInsert;\n const onlyDeletion = hasDelete && !hasInsert;\n const onlyInsertion = !hasDelete && hasInsert;\n const replacement = hasDelete && hasInsert;\n\n let summary: string;\n if (equal) {\n summary = '无变更';\n } else if (onlyDeletion) {\n const deleted = diffs\n .filter(([op]) => op === DIFF_DELETE)\n .map(([, text]) => text)\n .join('');\n summary = `删除: ${formatSnippet(deleted)}`;\n } else if (onlyInsertion) {\n const inserted = diffs\n .filter(([op]) => op === DIFF_INSERT)\n .map(([, text]) => text)\n .join('');\n summary = `新增: ${formatSnippet(inserted)}`;\n } else {\n const deleted = diffs\n .filter(([op]) => op === DIFF_DELETE)\n .map(([, text]) => text)\n .join('');\n const inserted = diffs\n .filter(([op]) => op === DIFF_INSERT)\n .map(([, text]) => text)\n .join('');\n summary = `替换: ${formatSnippet(deleted)} → ${formatSnippet(inserted)}`;\n }\n\n return {\n originalFragment,\n finalFragment,\n equal,\n onlyDeletion,\n onlyInsertion,\n replacement,\n summary,\n diffs,\n };\n}\n\n/** 片段截断展示,避免过长 */\nfunction formatSnippet(s: string, maxLen = 40): string {\n const t = s.replace(/\\s+/g, ' ').trim();\n if (t.length <= maxLen) return t;\n return `${t.slice(0, maxLen)}…`;\n}\n\n/**\n * 根据当前内容和 range,找到下一个内容对应的 range\n * 用于将 currentContent 中的 range 映射到 nextContent 中对应的 range\n *\n * @param options.currentContent 当前文件内容\n * @param options.nextContent 下一个文件内容\n * @param options.currentRange 当前文件中需要映射的 range\n * @returns 映射得到的 nextRange、对应片段、以及片段级变更分析;若无法解析则返回 undefined\n */\nexport function resolveGroupChangeFragments(options: {\n currentContent: string;\n nextContent: string;\n currentRange: Range;\n}):\n | {\n nextRange: Range;\n currentFragment: string;\n nextFragment: string;\n changeAnalysis: FragmentChangeAnalysis;\n }\n | undefined {\n const { currentContent, nextContent, currentRange } = options;\n\n const { start: startOffset, end: endOffset } = currentRange;\n\n if (\n startOffset < 0 ||\n endOffset < 0 ||\n startOffset > endOffset ||\n endOffset > currentContent.length\n ) {\n return undefined;\n }\n\n const nextRange = mapOlderRangeToNewer(\n currentContent,\n nextContent,\n startOffset,\n endOffset\n );\n\n const currentFragment = currentContent.substring(startOffset, endOffset);\n const nextFragment = nextContent.substring(nextRange.start, nextRange.end);\n\n const changeAnalysis = analyzeFragmentChange(currentFragment, nextFragment);\n\n return {\n nextRange,\n currentFragment,\n nextFragment,\n changeAnalysis,\n };\n}\n"],"names":["DIFF_DELETE","DIFF_INSERT","DIFF_EQUAL","mapNewerRangeToOlder","olderContent","newerContent","startOffset","endOffset","dmp","diff_match_patch","diffs","diff_main","diff_cleanupSemantic","newerOffset","olderOffset","olderStart","olderEnd","i","length","_diffs$i","op","text","len","nextDiff","rangeStart","rangeEnd","offsetInRange","start","end","mapOlderRangeToNewer","newerStart","newerEnd","_diffs$i2","oldRangeStart","oldRangeEnd","analyzeFragmentChange","originalFragment","finalFragment","hasDelete","some","_ref","hasInsert","_ref2","equal","onlyDeletion","onlyInsertion","replacement","summary","deleted","filter","_ref3","map","_ref4","join","formatSnippet","inserted","_ref5","_ref6","_ref7","_ref8","_ref9","_ref10","s","maxLen","t","replace","trim","slice","resolveGroupChangeFragments","options","currentContent","nextContent","currentRange","undefined","nextRange","currentFragment","substring","nextFragment","changeAnalysis"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var n=require("diff-match-patch");function e(e,r,t,l){var u=new n.diff_match_patch,a=u.diff_main(e,r);u.diff_cleanupSemantic(a);for(var i=0,f=0,o=null,c=null,s=0;s<a.length;s++){var m=a[s],g=m[0],p=m[1].length,h=s<a.length-1?a[s+1]:null;if(0===g){var d=i+p;null===o&&t>=i&&t<d&&(o=f+(t-i)),null===c&&l>i&&l<=d&&(c=f+(l-i),l===d&&h&&1===h[0]&&(c=f+p+h[1].length)),i+=p,f+=p}else if(1===g)f+=p;else if(-1===g){var v=i+p;null===o&&t>=i&&t<v&&(o=f),(null===c&&l>i&&l<=v||null===c&&l<=i)&&(c=f),i+=p}if(null!==o&&null!==c)break}return null===o&&(o=f),null===c&&(c=f),{start:o,end:c}}function r(e,r){var l=new n.diff_match_patch,u=l.diff_main(e,r);l.diff_cleanupSemantic(u);var a,i=u.some((function(n){return-1===n[0]})),f=u.some((function(n){return 1===n[0]})),o=!i&&!f,c=i&&!f,s=!i&&f,m=i&&f;if(o)a="无变更";else if(c)a="删除: "+t(u.filter((function(n){return-1===n[0]})).map((function(n){return n[1]})).join(""));else if(s)a="新增: "+t(u.filter((function(n){return 1===n[0]})).map((function(n){return n[1]})).join(""));else{var g=u.filter((function(n){return-1===n[0]})).map((function(n){return n[1]})).join(""),p=u.filter((function(n){return 1===n[0]})).map((function(n){return n[1]})).join("");a="替换: "+t(g)+" → "+t(p)}return{originalFragment:e,finalFragment:r,equal:o,onlyDeletion:c,onlyInsertion:s,replacement:m,summary:a,diffs:u}}function t(n,e){void 0===e&&(e=40);var r=n.replace(/\s+/g," ").trim();return r.length<=e?r:r.slice(0,e)+"…"}exports.analyzeFragmentChange=r,exports.mapNewerRangeToOlder=function(e,r,t,l){var u=new n.diff_match_patch,a=u.diff_main(e,r);u.diff_cleanupSemantic(a);for(var i=0,f=0,o=null,c=null,s=0;s<a.length;s++){var m=a[s],g=m[0],p=m[1].length,h=s<a.length-1?a[s+1]:null;if(0===g){var d=i+p;null===o&&t>=i&&t<d&&(o=f+(t-i)),null===c&&l>i&&l<=d&&(c=f+(l-i),l===d&&h&&-1===h[0]&&(c=f+p+h[1].length)),i+=p,f+=p}else if(1===g){var v=i+p;null===o&&t>=i&&t<v&&(o=f),null===c&&l>i&&l<=v&&(c=f),i+=p}else-1===g&&(null===o&&t<=i&&(o=f),null===c&&l<=i?c=l===i?f+p:f:null===c&&l===i&&(c=f+p),f+=p);if(null!==o&&null!==c)break}return null===o&&(o=f),null===c&&(c=f),{start:o,end:c}},exports.mapOlderRangeToNewer=e,exports.resolveGroupChangeFragments=function(n){var t=n.currentContent,l=n.nextContent,u=n.currentRange,a=u.start,i=u.end;if(!(a<0||i<0||a>i||i>t.length)){var f=e(t,l,a,i),o=t.substring(a,i),c=l.substring(f.start,f.end);return{nextRange:f,currentFragment:o,nextFragment:c,changeAnalysis:r(o,c)}}};
2
+ //# sourceMappingURL=map-diff-range.cjs.production.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-diff-range.cjs.production.min.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * 基于 diff-match-patch 的 range 映射与变更分析工具\n * 用于在 originalContent / currentContent / finalContent 之间映射 range,并分析片段级变更\n *\n * 参考: https://github.com/red-armor/x-oasis/blob/main/packages/diff/diff-match-patch/src/index.ts#L123\n */\n\nimport { diff_match_patch } from 'diff-match-patch';\n\n/** diff 操作常量,与 diff-match-patch 一致 */\nconst DIFF_DELETE = -1;\nconst DIFF_INSERT = 1;\nconst DIFF_EQUAL = 0;\n\n/** 单端 range,start 含头,end 含尾(substring 语义) */\nexport interface Range {\n start: number;\n end: number;\n}\n\n/** 片段级变更分析结果 */\nexport interface FragmentChangeAnalysis {\n /** 原始片段内容 */\n originalFragment: string;\n /** 最终片段内容 */\n finalFragment: string;\n /** 是否完全相同 */\n equal: boolean;\n /** 仅删除(无新增) */\n onlyDeletion: boolean;\n /** 仅新增(无删除) */\n onlyInsertion: boolean;\n /** 既有删除又有新增(替换) */\n replacement: boolean;\n /** 语义化描述(简短) */\n summary: string;\n /** 详细 diff 条目,便于上层做展示或进一步处理 */\n diffs: Array<[number, string]>;\n}\n\n/**\n * 将「新内容」中的 offset range 映射到「旧内容」中的 offset range\n * 等价于 x-oasis 的 mapCurrentRangeToOriginal:diffs = diff_main(older, newer),给定 newer 上的 [start,end],返回 older 上的 [start,end]\n *\n * @param olderContent 旧内容(diff 的 text1)\n * @param newerContent 新内容(diff 的 text2)\n * @param startOffset 新内容中的 range 起始 offset\n * @param endOffset 新内容中的 range 结束 offset(不含尾,即 [startOffset, endOffset) 或含尾由调用方约定,此处按 substring 含头含尾)\n * @returns 旧内容中对应的 range;若越界或无效则返回 { start: 0, end: 0 }\n */\nexport function mapNewerRangeToOlder(\n olderContent: string,\n newerContent: string,\n startOffset: number,\n endOffset: number\n): Range {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(olderContent, newerContent);\n dmp.diff_cleanupSemantic(diffs);\n\n let newerOffset = 0;\n let olderOffset = 0;\n let olderStart: number | null = null;\n let olderEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [op, text] = diffs[i];\n const len = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (op === DIFF_EQUAL) {\n const rangeStart = newerOffset;\n const rangeEnd = newerOffset + len;\n\n if (\n olderStart === null &&\n startOffset >= rangeStart &&\n startOffset < rangeEnd\n ) {\n olderStart = olderOffset + (startOffset - rangeStart);\n }\n if (\n olderEnd === null &&\n endOffset > rangeStart &&\n endOffset <= rangeEnd\n ) {\n const offsetInRange = endOffset - rangeStart;\n olderEnd = olderOffset + offsetInRange;\n if (endOffset === rangeEnd && nextDiff && nextDiff[0] === DIFF_DELETE) {\n olderEnd = olderOffset + len + nextDiff[1].length;\n }\n }\n\n newerOffset += len;\n olderOffset += len;\n } else if (op === DIFF_INSERT) {\n const rangeStart = newerOffset;\n const rangeEnd = newerOffset + len;\n\n if (\n olderStart === null &&\n startOffset >= rangeStart &&\n startOffset < rangeEnd\n ) {\n olderStart = olderOffset;\n }\n if (\n olderEnd === null &&\n endOffset > rangeStart &&\n endOffset <= rangeEnd\n ) {\n olderEnd = olderOffset;\n }\n\n newerOffset += len;\n } else if (op === DIFF_DELETE) {\n if (olderStart === null && startOffset <= newerOffset) {\n olderStart = olderOffset;\n }\n if (olderEnd === null && endOffset <= newerOffset) {\n olderEnd = endOffset === newerOffset ? olderOffset + len : olderOffset;\n } else if (olderEnd === null && endOffset === newerOffset) {\n olderEnd = olderOffset + len;\n }\n\n olderOffset += len;\n }\n\n if (olderStart !== null && olderEnd !== null) break;\n }\n\n if (olderStart === null) olderStart = olderOffset;\n if (olderEnd === null) olderEnd = olderOffset;\n\n return { start: olderStart, end: olderEnd };\n}\n\n/**\n * 将「旧内容」中的 offset range 映射到「新内容」中的 offset range\n * 与 mapNewerRangeToOlder 对称:diffs = diff_main(older, newer),给定 older 上的 [start,end],返回 newer 上的 [start,end]\n *\n * @param olderContent 旧内容(diff 的 text1)\n * @param newerContent 新内容(diff 的 text2)\n * @param startOffset 旧内容中的 range 起始 offset\n * @param endOffset 旧内容中的 range 结束 offset\n * @returns 新内容中对应的 range\n */\nexport function mapOlderRangeToNewer(\n olderContent: string,\n newerContent: string,\n startOffset: number,\n endOffset: number\n): Range {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(olderContent, newerContent);\n dmp.diff_cleanupSemantic(diffs);\n\n let olderOffset = 0;\n let newerOffset = 0;\n let newerStart: number | null = null;\n let newerEnd: number | null = null;\n\n for (let i = 0; i < diffs.length; i++) {\n const [op, text] = diffs[i];\n const len = text.length;\n const nextDiff = i < diffs.length - 1 ? diffs[i + 1] : null;\n\n if (op === DIFF_EQUAL) {\n const oldRangeStart = olderOffset;\n const oldRangeEnd = olderOffset + len;\n\n if (\n newerStart === null &&\n startOffset >= oldRangeStart &&\n startOffset < oldRangeEnd\n ) {\n newerStart = newerOffset + (startOffset - oldRangeStart);\n }\n if (\n newerEnd === null &&\n endOffset > oldRangeStart &&\n endOffset <= oldRangeEnd\n ) {\n const offsetInRange = endOffset - oldRangeStart;\n newerEnd = newerOffset + offsetInRange;\n if (\n endOffset === oldRangeEnd &&\n nextDiff &&\n nextDiff[0] === DIFF_INSERT\n ) {\n newerEnd = newerOffset + len + nextDiff[1].length;\n }\n }\n\n olderOffset += len;\n newerOffset += len;\n } else if (op === DIFF_INSERT) {\n newerOffset += len;\n } else if (op === DIFF_DELETE) {\n const oldRangeStart = olderOffset;\n const oldRangeEnd = olderOffset + len;\n\n if (\n newerStart === null &&\n startOffset >= oldRangeStart &&\n startOffset < oldRangeEnd\n ) {\n newerStart = newerOffset;\n }\n if (\n newerEnd === null &&\n endOffset > oldRangeStart &&\n endOffset <= oldRangeEnd\n ) {\n newerEnd = newerOffset;\n } else if (newerEnd === null && endOffset <= olderOffset) {\n newerEnd = newerOffset;\n }\n\n olderOffset += len;\n }\n\n if (newerStart !== null && newerEnd !== null) break;\n }\n\n if (newerStart === null) newerStart = newerOffset;\n if (newerEnd === null) newerEnd = newerOffset;\n\n return { start: newerStart, end: newerEnd };\n}\n\n/**\n * 对比两段片段内容,分析发生的变更类型并生成简短描述\n *\n * @param originalFragment 原始片段\n * @param finalFragment 变更后片段\n * @returns 变更分析结果\n */\nexport function analyzeFragmentChange(\n originalFragment: string,\n finalFragment: string\n): FragmentChangeAnalysis {\n const dmp = new diff_match_patch();\n const diffs = dmp.diff_main(originalFragment, finalFragment);\n dmp.diff_cleanupSemantic(diffs);\n\n const hasDelete = diffs.some(([op]) => op === DIFF_DELETE);\n const hasInsert = diffs.some(([op]) => op === DIFF_INSERT);\n const equal = !hasDelete && !hasInsert;\n const onlyDeletion = hasDelete && !hasInsert;\n const onlyInsertion = !hasDelete && hasInsert;\n const replacement = hasDelete && hasInsert;\n\n let summary: string;\n if (equal) {\n summary = '无变更';\n } else if (onlyDeletion) {\n const deleted = diffs\n .filter(([op]) => op === DIFF_DELETE)\n .map(([, text]) => text)\n .join('');\n summary = `删除: ${formatSnippet(deleted)}`;\n } else if (onlyInsertion) {\n const inserted = diffs\n .filter(([op]) => op === DIFF_INSERT)\n .map(([, text]) => text)\n .join('');\n summary = `新增: ${formatSnippet(inserted)}`;\n } else {\n const deleted = diffs\n .filter(([op]) => op === DIFF_DELETE)\n .map(([, text]) => text)\n .join('');\n const inserted = diffs\n .filter(([op]) => op === DIFF_INSERT)\n .map(([, text]) => text)\n .join('');\n summary = `替换: ${formatSnippet(deleted)} → ${formatSnippet(inserted)}`;\n }\n\n return {\n originalFragment,\n finalFragment,\n equal,\n onlyDeletion,\n onlyInsertion,\n replacement,\n summary,\n diffs,\n };\n}\n\n/** 片段截断展示,避免过长 */\nfunction formatSnippet(s: string, maxLen = 40): string {\n const t = s.replace(/\\s+/g, ' ').trim();\n if (t.length <= maxLen) return t;\n return `${t.slice(0, maxLen)}…`;\n}\n\n/**\n * 根据当前内容和 range,找到下一个内容对应的 range\n * 用于将 currentContent 中的 range 映射到 nextContent 中对应的 range\n *\n * @param options.currentContent 当前文件内容\n * @param options.nextContent 下一个文件内容\n * @param options.currentRange 当前文件中需要映射的 range\n * @returns 映射得到的 nextRange、对应片段、以及片段级变更分析;若无法解析则返回 undefined\n */\nexport function resolveGroupChangeFragments(options: {\n currentContent: string;\n nextContent: string;\n currentRange: Range;\n}):\n | {\n nextRange: Range;\n currentFragment: string;\n nextFragment: string;\n changeAnalysis: FragmentChangeAnalysis;\n }\n | undefined {\n const { currentContent, nextContent, currentRange } = options;\n\n const { start: startOffset, end: endOffset } = currentRange;\n\n if (\n startOffset < 0 ||\n endOffset < 0 ||\n startOffset > endOffset ||\n endOffset > currentContent.length\n ) {\n return undefined;\n }\n\n const nextRange = mapOlderRangeToNewer(\n currentContent,\n nextContent,\n startOffset,\n endOffset\n );\n\n const currentFragment = currentContent.substring(startOffset, endOffset);\n const nextFragment = nextContent.substring(nextRange.start, nextRange.end);\n\n const changeAnalysis = analyzeFragmentChange(currentFragment, nextFragment);\n\n return {\n nextRange,\n currentFragment,\n nextFragment,\n changeAnalysis,\n };\n}\n"],"names":["mapOlderRangeToNewer","olderContent","newerContent","startOffset","endOffset","dmp","diff_match_patch","diffs","diff_main","diff_cleanupSemantic","olderOffset","newerOffset","newerStart","newerEnd","i","length","_diffs$i2","op","len","nextDiff","oldRangeEnd","start","end","analyzeFragmentChange","originalFragment","finalFragment","summary","hasDelete","some","_ref","hasInsert","_ref2","equal","onlyDeletion","onlyInsertion","replacement","formatSnippet","filter","_ref3","map","_ref4","join","_ref5","_ref6","deleted","_ref7","_ref8","inserted","_ref9","_ref10","s","maxLen","t","replace","trim","slice","olderStart","olderEnd","_diffs$i","rangeEnd","options","currentContent","nextContent","currentRange","nextRange","currentFragment","substring","nextFragment","changeAnalysis"],"mappings":"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"}