@shawnstack/quickforge 1.3.6 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +348 -348
- package/dist/assets/{anthropic-BtDSt6Co.js → anthropic-67kzbrvt.js} +1 -1
- package/dist/assets/{azure-openai-responses-BiE5KBJr.js → azure-openai-responses-D2XquyCv.js} +1 -1
- package/dist/assets/{google-yYxzgw4O.js → google-CPViRPcU.js} +1 -1
- package/dist/assets/{google-gemini-cli-B8OJAX-P.js → google-gemini-cli-CNT4rz9y.js} +1 -1
- package/dist/assets/{google-vertex-Dmz1T75D.js → google-vertex-DOnrgFCc.js} +1 -1
- package/dist/assets/{index-BcbxYyM-.css → index-B0wkRg7T.css} +1 -1
- package/dist/assets/{index-Bc4Ghgsv.js → index-D3njc0Th.js} +454 -445
- package/dist/assets/{mistral-BJiBvfjD.js → mistral-Cz-xIri6.js} +1 -1
- package/dist/assets/{openai-codex-responses-rJJhq5x0.js → openai-codex-responses-CyA4Z3WI.js} +1 -1
- package/dist/assets/{openai-completions-CaCNPWQP.js → openai-completions-DSDeg9P2.js} +1 -1
- package/dist/assets/{openai-responses-D-zeSioM.js → openai-responses-jFa5zzTQ.js} +1 -1
- package/dist/assets/{openai-responses-shared-Bqg-VsV2.js → openai-responses-shared-BDx4vPct.js} +1 -1
- package/dist/index.html +2 -2
- package/node_modules/@aws-sdk/client-bedrock-runtime/dist-cjs/schemas/schemas_0.js +7 -4
- package/node_modules/@aws-sdk/client-bedrock-runtime/dist-es/schemas/schemas_0.js +7 -4
- package/node_modules/@aws-sdk/client-bedrock-runtime/package.json +2 -2
- package/node_modules/@aws-sdk/token-providers/package.json +1 -1
- package/package.json +1 -1
- package/server/utils/text-diff.mjs +215 -215
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
const DEFAULT_CONTEXT_LINES = 3
|
|
2
|
-
const DEFAULT_MAX_DIFF_CHARS = 60000
|
|
3
|
-
const DEFAULT_MAX_DIFF_LINES = 1200
|
|
4
|
-
const MAX_LCS_CELLS = 2_000_000
|
|
5
|
-
|
|
6
|
-
function splitTextLines(text) {
|
|
7
|
-
if (!text) return []
|
|
8
|
-
const normalized = String(text).replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
|
9
|
-
const lines = normalized.split('\n')
|
|
10
|
-
if (lines.at(-1) === '') lines.pop()
|
|
11
|
-
return lines
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function countCommonPrefix(oldLines, newLines) {
|
|
15
|
-
const limit = Math.min(oldLines.length, newLines.length)
|
|
16
|
-
let index = 0
|
|
17
|
-
while (index < limit && oldLines[index] === newLines[index]) index++
|
|
18
|
-
return index
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function countCommonSuffix(oldLines, newLines, prefixLength) {
|
|
22
|
-
const oldRemaining = oldLines.length - prefixLength
|
|
23
|
-
const newRemaining = newLines.length - prefixLength
|
|
24
|
-
const limit = Math.min(oldRemaining, newRemaining)
|
|
25
|
-
let count = 0
|
|
26
|
-
while (count < limit && oldLines[oldLines.length - 1 - count] === newLines[newLines.length - 1 - count]) count++
|
|
27
|
-
return count
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function diffMiddleLines(oldLines, newLines) {
|
|
31
|
-
const oldCount = oldLines.length
|
|
32
|
-
const newCount = newLines.length
|
|
33
|
-
|
|
34
|
-
if (oldCount === 0) return newLines.map((line) => ({ type: 'insert', line }))
|
|
35
|
-
if (newCount === 0) return oldLines.map((line) => ({ type: 'delete', line }))
|
|
36
|
-
|
|
37
|
-
if (oldCount * newCount > MAX_LCS_CELLS) {
|
|
38
|
-
return [
|
|
39
|
-
...oldLines.map((line) => ({ type: 'delete', line })),
|
|
40
|
-
...newLines.map((line) => ({ type: 'insert', line })),
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const dp = Array.from({ length: oldCount + 1 }, () => new Uint32Array(newCount + 1))
|
|
45
|
-
for (let oldIndex = oldCount - 1; oldIndex >= 0; oldIndex--) {
|
|
46
|
-
for (let newIndex = newCount - 1; newIndex >= 0; newIndex--) {
|
|
47
|
-
dp[oldIndex][newIndex] = oldLines[oldIndex] === newLines[newIndex]
|
|
48
|
-
? dp[oldIndex + 1][newIndex + 1] + 1
|
|
49
|
-
: Math.max(dp[oldIndex + 1][newIndex], dp[oldIndex][newIndex + 1])
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const operations = []
|
|
54
|
-
let oldIndex = 0
|
|
55
|
-
let newIndex = 0
|
|
56
|
-
while (oldIndex < oldCount && newIndex < newCount) {
|
|
57
|
-
if (oldLines[oldIndex] === newLines[newIndex]) {
|
|
58
|
-
operations.push({ type: 'equal', line: oldLines[oldIndex] })
|
|
59
|
-
oldIndex++
|
|
60
|
-
newIndex++
|
|
61
|
-
} else if (dp[oldIndex + 1][newIndex] >= dp[oldIndex][newIndex + 1]) {
|
|
62
|
-
operations.push({ type: 'delete', line: oldLines[oldIndex] })
|
|
63
|
-
oldIndex++
|
|
64
|
-
} else {
|
|
65
|
-
operations.push({ type: 'insert', line: newLines[newIndex] })
|
|
66
|
-
newIndex++
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
while (oldIndex < oldCount) {
|
|
71
|
-
operations.push({ type: 'delete', line: oldLines[oldIndex] })
|
|
72
|
-
oldIndex++
|
|
73
|
-
}
|
|
74
|
-
while (newIndex < newCount) {
|
|
75
|
-
operations.push({ type: 'insert', line: newLines[newIndex] })
|
|
76
|
-
newIndex++
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return operations
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function diffLineOperations(oldLines, newLines) {
|
|
83
|
-
const prefixLength = countCommonPrefix(oldLines, newLines)
|
|
84
|
-
const suffixLength = countCommonSuffix(oldLines, newLines, prefixLength)
|
|
85
|
-
const oldMiddleEnd = oldLines.length - suffixLength
|
|
86
|
-
const newMiddleEnd = newLines.length - suffixLength
|
|
87
|
-
|
|
88
|
-
const operations = [
|
|
89
|
-
...oldLines.slice(0, prefixLength).map((line) => ({ type: 'equal', line })),
|
|
90
|
-
...diffMiddleLines(oldLines.slice(prefixLength, oldMiddleEnd), newLines.slice(prefixLength, newMiddleEnd)),
|
|
91
|
-
...oldLines.slice(oldMiddleEnd).map((line) => ({ type: 'equal', line })),
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
let oldLine = 1
|
|
95
|
-
let newLine = 1
|
|
96
|
-
for (const operation of operations) {
|
|
97
|
-
if (operation.type !== 'insert') {
|
|
98
|
-
operation.oldLine = oldLine
|
|
99
|
-
oldLine++
|
|
100
|
-
} else {
|
|
101
|
-
operation.oldLine = oldLine
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (operation.type !== 'delete') {
|
|
105
|
-
operation.newLine = newLine
|
|
106
|
-
newLine++
|
|
107
|
-
} else {
|
|
108
|
-
operation.newLine = newLine
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return operations
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function changedOperationRanges(operations, contextLines) {
|
|
116
|
-
const ranges = []
|
|
117
|
-
let index = 0
|
|
118
|
-
|
|
119
|
-
while (index < operations.length) {
|
|
120
|
-
while (index < operations.length && operations[index].type === 'equal') index++
|
|
121
|
-
if (index >= operations.length) break
|
|
122
|
-
|
|
123
|
-
const changeStart = index
|
|
124
|
-
while (index < operations.length && operations[index].type !== 'equal') index++
|
|
125
|
-
const changeEnd = index - 1
|
|
126
|
-
const start = Math.max(0, changeStart - contextLines)
|
|
127
|
-
const end = Math.min(operations.length, changeEnd + contextLines + 1)
|
|
128
|
-
|
|
129
|
-
const previous = ranges.at(-1)
|
|
130
|
-
if (previous && start <= previous.end) {
|
|
131
|
-
previous.end = end
|
|
132
|
-
} else {
|
|
133
|
-
ranges.push({ start, end })
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return ranges
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function formatRange(start, count) {
|
|
141
|
-
if (count === 0) return `${start},0`
|
|
142
|
-
if (count === 1) return String(start)
|
|
143
|
-
return `${start},${count}`
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function hunkHeader(operations, oldLineCount) {
|
|
147
|
-
const oldOperations = operations.filter((operation) => operation.type !== 'insert')
|
|
148
|
-
const newOperations = operations.filter((operation) => operation.type !== 'delete')
|
|
149
|
-
const firstOperation = operations[0]
|
|
150
|
-
const oldStart = oldOperations[0]?.oldLine ?? (oldLineCount === 0 ? 0 : firstOperation?.oldLine ?? 1)
|
|
151
|
-
const newStart = newOperations[0]?.newLine ?? (firstOperation?.newLine ?? 1)
|
|
152
|
-
|
|
153
|
-
return `@@ -${formatRange(oldStart, oldOperations.length)} +${formatRange(newStart, newOperations.length)} @@`
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function formatOperation(operation) {
|
|
157
|
-
if (operation.type === 'insert') return `+${operation.line}`
|
|
158
|
-
if (operation.type === 'delete') return `-${operation.line}`
|
|
159
|
-
return ` ${operation.line}`
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function truncateDiffText(text, maxChars, maxLines) {
|
|
163
|
-
let truncated = false
|
|
164
|
-
let output = text
|
|
165
|
-
|
|
166
|
-
const lines = output.split('\n')
|
|
167
|
-
if (lines.length > maxLines) {
|
|
168
|
-
output = lines.slice(0, maxLines).join('\n')
|
|
169
|
-
truncated = true
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (output.length > maxChars) {
|
|
173
|
-
output = output.slice(0, maxChars)
|
|
174
|
-
truncated = true
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (truncated) output = `${output}\n\n[diff truncated]`
|
|
178
|
-
return { text: output, truncated }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function createTextDiff(oldText, newText, relativePath, options = {}) {
|
|
182
|
-
const oldLines = splitTextLines(oldText)
|
|
183
|
-
const newLines = splitTextLines(newText)
|
|
184
|
-
const operations = diffLineOperations(oldLines, newLines)
|
|
185
|
-
const addedLines = operations.filter((operation) => operation.type === 'insert').length
|
|
186
|
-
const removedLines = operations.filter((operation) => operation.type === 'delete').length
|
|
187
|
-
const contextLines = Math.max(0, Number(options.contextLines ?? DEFAULT_CONTEXT_LINES))
|
|
188
|
-
const ranges = changedOperationRanges(operations, contextLines)
|
|
189
|
-
const oldLabel = options.oldExists === false ? '/dev/null' : `a/${relativePath}`
|
|
190
|
-
const newLabel = `b/${relativePath}`
|
|
191
|
-
|
|
192
|
-
const diffLines = ranges.length > 0 ? [`--- ${oldLabel}`, `+++ ${newLabel}`] : []
|
|
193
|
-
for (const range of ranges) {
|
|
194
|
-
const hunkOperations = operations.slice(range.start, range.end)
|
|
195
|
-
diffLines.push(hunkHeader(hunkOperations, oldLines.length))
|
|
196
|
-
diffLines.push(...hunkOperations.map(formatOperation))
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const truncated = truncateDiffText(
|
|
200
|
-
diffLines.join('\n'),
|
|
201
|
-
Number(options.maxChars ?? DEFAULT_MAX_DIFF_CHARS),
|
|
202
|
-
Number(options.maxLines ?? DEFAULT_MAX_DIFF_LINES),
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
format: 'unified',
|
|
207
|
-
path: relativePath,
|
|
208
|
-
addedLines,
|
|
209
|
-
removedLines,
|
|
210
|
-
oldLineCount: oldLines.length,
|
|
211
|
-
newLineCount: newLines.length,
|
|
212
|
-
truncated: truncated.truncated,
|
|
213
|
-
text: truncated.text,
|
|
214
|
-
}
|
|
215
|
-
}
|
|
1
|
+
const DEFAULT_CONTEXT_LINES = 3
|
|
2
|
+
const DEFAULT_MAX_DIFF_CHARS = 60000
|
|
3
|
+
const DEFAULT_MAX_DIFF_LINES = 1200
|
|
4
|
+
const MAX_LCS_CELLS = 2_000_000
|
|
5
|
+
|
|
6
|
+
function splitTextLines(text) {
|
|
7
|
+
if (!text) return []
|
|
8
|
+
const normalized = String(text).replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
|
9
|
+
const lines = normalized.split('\n')
|
|
10
|
+
if (lines.at(-1) === '') lines.pop()
|
|
11
|
+
return lines
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function countCommonPrefix(oldLines, newLines) {
|
|
15
|
+
const limit = Math.min(oldLines.length, newLines.length)
|
|
16
|
+
let index = 0
|
|
17
|
+
while (index < limit && oldLines[index] === newLines[index]) index++
|
|
18
|
+
return index
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function countCommonSuffix(oldLines, newLines, prefixLength) {
|
|
22
|
+
const oldRemaining = oldLines.length - prefixLength
|
|
23
|
+
const newRemaining = newLines.length - prefixLength
|
|
24
|
+
const limit = Math.min(oldRemaining, newRemaining)
|
|
25
|
+
let count = 0
|
|
26
|
+
while (count < limit && oldLines[oldLines.length - 1 - count] === newLines[newLines.length - 1 - count]) count++
|
|
27
|
+
return count
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function diffMiddleLines(oldLines, newLines) {
|
|
31
|
+
const oldCount = oldLines.length
|
|
32
|
+
const newCount = newLines.length
|
|
33
|
+
|
|
34
|
+
if (oldCount === 0) return newLines.map((line) => ({ type: 'insert', line }))
|
|
35
|
+
if (newCount === 0) return oldLines.map((line) => ({ type: 'delete', line }))
|
|
36
|
+
|
|
37
|
+
if (oldCount * newCount > MAX_LCS_CELLS) {
|
|
38
|
+
return [
|
|
39
|
+
...oldLines.map((line) => ({ type: 'delete', line })),
|
|
40
|
+
...newLines.map((line) => ({ type: 'insert', line })),
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const dp = Array.from({ length: oldCount + 1 }, () => new Uint32Array(newCount + 1))
|
|
45
|
+
for (let oldIndex = oldCount - 1; oldIndex >= 0; oldIndex--) {
|
|
46
|
+
for (let newIndex = newCount - 1; newIndex >= 0; newIndex--) {
|
|
47
|
+
dp[oldIndex][newIndex] = oldLines[oldIndex] === newLines[newIndex]
|
|
48
|
+
? dp[oldIndex + 1][newIndex + 1] + 1
|
|
49
|
+
: Math.max(dp[oldIndex + 1][newIndex], dp[oldIndex][newIndex + 1])
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const operations = []
|
|
54
|
+
let oldIndex = 0
|
|
55
|
+
let newIndex = 0
|
|
56
|
+
while (oldIndex < oldCount && newIndex < newCount) {
|
|
57
|
+
if (oldLines[oldIndex] === newLines[newIndex]) {
|
|
58
|
+
operations.push({ type: 'equal', line: oldLines[oldIndex] })
|
|
59
|
+
oldIndex++
|
|
60
|
+
newIndex++
|
|
61
|
+
} else if (dp[oldIndex + 1][newIndex] >= dp[oldIndex][newIndex + 1]) {
|
|
62
|
+
operations.push({ type: 'delete', line: oldLines[oldIndex] })
|
|
63
|
+
oldIndex++
|
|
64
|
+
} else {
|
|
65
|
+
operations.push({ type: 'insert', line: newLines[newIndex] })
|
|
66
|
+
newIndex++
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
while (oldIndex < oldCount) {
|
|
71
|
+
operations.push({ type: 'delete', line: oldLines[oldIndex] })
|
|
72
|
+
oldIndex++
|
|
73
|
+
}
|
|
74
|
+
while (newIndex < newCount) {
|
|
75
|
+
operations.push({ type: 'insert', line: newLines[newIndex] })
|
|
76
|
+
newIndex++
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return operations
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function diffLineOperations(oldLines, newLines) {
|
|
83
|
+
const prefixLength = countCommonPrefix(oldLines, newLines)
|
|
84
|
+
const suffixLength = countCommonSuffix(oldLines, newLines, prefixLength)
|
|
85
|
+
const oldMiddleEnd = oldLines.length - suffixLength
|
|
86
|
+
const newMiddleEnd = newLines.length - suffixLength
|
|
87
|
+
|
|
88
|
+
const operations = [
|
|
89
|
+
...oldLines.slice(0, prefixLength).map((line) => ({ type: 'equal', line })),
|
|
90
|
+
...diffMiddleLines(oldLines.slice(prefixLength, oldMiddleEnd), newLines.slice(prefixLength, newMiddleEnd)),
|
|
91
|
+
...oldLines.slice(oldMiddleEnd).map((line) => ({ type: 'equal', line })),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
let oldLine = 1
|
|
95
|
+
let newLine = 1
|
|
96
|
+
for (const operation of operations) {
|
|
97
|
+
if (operation.type !== 'insert') {
|
|
98
|
+
operation.oldLine = oldLine
|
|
99
|
+
oldLine++
|
|
100
|
+
} else {
|
|
101
|
+
operation.oldLine = oldLine
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (operation.type !== 'delete') {
|
|
105
|
+
operation.newLine = newLine
|
|
106
|
+
newLine++
|
|
107
|
+
} else {
|
|
108
|
+
operation.newLine = newLine
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return operations
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function changedOperationRanges(operations, contextLines) {
|
|
116
|
+
const ranges = []
|
|
117
|
+
let index = 0
|
|
118
|
+
|
|
119
|
+
while (index < operations.length) {
|
|
120
|
+
while (index < operations.length && operations[index].type === 'equal') index++
|
|
121
|
+
if (index >= operations.length) break
|
|
122
|
+
|
|
123
|
+
const changeStart = index
|
|
124
|
+
while (index < operations.length && operations[index].type !== 'equal') index++
|
|
125
|
+
const changeEnd = index - 1
|
|
126
|
+
const start = Math.max(0, changeStart - contextLines)
|
|
127
|
+
const end = Math.min(operations.length, changeEnd + contextLines + 1)
|
|
128
|
+
|
|
129
|
+
const previous = ranges.at(-1)
|
|
130
|
+
if (previous && start <= previous.end) {
|
|
131
|
+
previous.end = end
|
|
132
|
+
} else {
|
|
133
|
+
ranges.push({ start, end })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return ranges
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formatRange(start, count) {
|
|
141
|
+
if (count === 0) return `${start},0`
|
|
142
|
+
if (count === 1) return String(start)
|
|
143
|
+
return `${start},${count}`
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function hunkHeader(operations, oldLineCount) {
|
|
147
|
+
const oldOperations = operations.filter((operation) => operation.type !== 'insert')
|
|
148
|
+
const newOperations = operations.filter((operation) => operation.type !== 'delete')
|
|
149
|
+
const firstOperation = operations[0]
|
|
150
|
+
const oldStart = oldOperations[0]?.oldLine ?? (oldLineCount === 0 ? 0 : firstOperation?.oldLine ?? 1)
|
|
151
|
+
const newStart = newOperations[0]?.newLine ?? (firstOperation?.newLine ?? 1)
|
|
152
|
+
|
|
153
|
+
return `@@ -${formatRange(oldStart, oldOperations.length)} +${formatRange(newStart, newOperations.length)} @@`
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function formatOperation(operation) {
|
|
157
|
+
if (operation.type === 'insert') return `+${operation.line}`
|
|
158
|
+
if (operation.type === 'delete') return `-${operation.line}`
|
|
159
|
+
return ` ${operation.line}`
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function truncateDiffText(text, maxChars, maxLines) {
|
|
163
|
+
let truncated = false
|
|
164
|
+
let output = text
|
|
165
|
+
|
|
166
|
+
const lines = output.split('\n')
|
|
167
|
+
if (lines.length > maxLines) {
|
|
168
|
+
output = lines.slice(0, maxLines).join('\n')
|
|
169
|
+
truncated = true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (output.length > maxChars) {
|
|
173
|
+
output = output.slice(0, maxChars)
|
|
174
|
+
truncated = true
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (truncated) output = `${output}\n\n[diff truncated]`
|
|
178
|
+
return { text: output, truncated }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function createTextDiff(oldText, newText, relativePath, options = {}) {
|
|
182
|
+
const oldLines = splitTextLines(oldText)
|
|
183
|
+
const newLines = splitTextLines(newText)
|
|
184
|
+
const operations = diffLineOperations(oldLines, newLines)
|
|
185
|
+
const addedLines = operations.filter((operation) => operation.type === 'insert').length
|
|
186
|
+
const removedLines = operations.filter((operation) => operation.type === 'delete').length
|
|
187
|
+
const contextLines = Math.max(0, Number(options.contextLines ?? DEFAULT_CONTEXT_LINES))
|
|
188
|
+
const ranges = changedOperationRanges(operations, contextLines)
|
|
189
|
+
const oldLabel = options.oldExists === false ? '/dev/null' : `a/${relativePath}`
|
|
190
|
+
const newLabel = `b/${relativePath}`
|
|
191
|
+
|
|
192
|
+
const diffLines = ranges.length > 0 ? [`--- ${oldLabel}`, `+++ ${newLabel}`] : []
|
|
193
|
+
for (const range of ranges) {
|
|
194
|
+
const hunkOperations = operations.slice(range.start, range.end)
|
|
195
|
+
diffLines.push(hunkHeader(hunkOperations, oldLines.length))
|
|
196
|
+
diffLines.push(...hunkOperations.map(formatOperation))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const truncated = truncateDiffText(
|
|
200
|
+
diffLines.join('\n'),
|
|
201
|
+
Number(options.maxChars ?? DEFAULT_MAX_DIFF_CHARS),
|
|
202
|
+
Number(options.maxLines ?? DEFAULT_MAX_DIFF_LINES),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
format: 'unified',
|
|
207
|
+
path: relativePath,
|
|
208
|
+
addedLines,
|
|
209
|
+
removedLines,
|
|
210
|
+
oldLineCount: oldLines.length,
|
|
211
|
+
newLineCount: newLines.length,
|
|
212
|
+
truncated: truncated.truncated,
|
|
213
|
+
text: truncated.text,
|
|
214
|
+
}
|
|
215
|
+
}
|