v-code-diff 1.13.2 → 1.14.1
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 +51 -55
- package/dist/v2/index.cjs +28 -19
- package/dist/v2/index.es.js +1609 -1506
- package/dist/v2/index.umd.js +28 -19
- package/dist/v2.7/index.cjs +28 -19
- package/dist/v2.7/index.es.js +1434 -1377
- package/dist/v2.7/index.umd.js +28 -19
- package/dist/v3/index.cjs +28 -19
- package/dist/v3/index.es.js +1634 -1555
- package/dist/v3/index.umd.js +28 -19
- package/package.json +7 -2
- package/src/CodeDiff.vue +4 -4
- package/src/split/SplitViewer.vue +34 -2
- package/src/style.scss +18 -0
- package/src/types.ts +2 -0
- package/src/unified/UnifiedViewer.vue +34 -2
- package/src/utils.ts +153 -55
- package/types/index.d.ts +60 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "v-code-diff",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.14.1",
|
|
5
5
|
"packageManager": "pnpm@11.2.2",
|
|
6
6
|
"description": "A code diff viewer for Vue 2.6, Vue 2.7, and Vue 3",
|
|
7
7
|
"license": "MIT",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"types"
|
|
38
38
|
],
|
|
39
39
|
"scripts": {
|
|
40
|
+
"benchmark": "pnpm build:3 && vitest bench --run && node --expose-gc benchmarks/render.mjs",
|
|
40
41
|
"build": "npm run clean && run-s build:**",
|
|
41
42
|
"build:2": "vue-demi-switch 2 vue2 && pnpm --filter vue2-playground build",
|
|
42
43
|
"build:2:umd": "vue-demi-switch 2 vue2 && format=umd pnpm --filter vue2-playground build",
|
|
@@ -54,7 +55,10 @@
|
|
|
54
55
|
"lint:fix": "eslint . --ext .vue,.js,.jsx,.ts,.tsx,json --fix --ignore-path .gitignore",
|
|
55
56
|
"postinstall": "node scripts/postinstall.cjs",
|
|
56
57
|
"prepublishOnly": "npm run build",
|
|
57
|
-
"release": "bumpp --commit --no-push --tag && npm publish"
|
|
58
|
+
"release": "bumpp --commit --no-push --tag && npm publish",
|
|
59
|
+
"test": "run-s test:unit test:types",
|
|
60
|
+
"test:types": "tsc --noEmit --skipLibCheck --module ESNext --moduleResolution node --target ES2020 tests/types.test-d.ts",
|
|
61
|
+
"test:unit": "vitest run"
|
|
58
62
|
},
|
|
59
63
|
"peerDependencies": {
|
|
60
64
|
"@vue/composition-api": "^1.4.9",
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
"typescript": "~4.7.4",
|
|
88
92
|
"vite": "^5.4.2",
|
|
89
93
|
"vite-plugin-css-injected-by-js": "^2.4.0",
|
|
94
|
+
"vitest": "^2.1.9",
|
|
90
95
|
"vue": "^3.4.38",
|
|
91
96
|
"vue-i18n": "^9.14.0",
|
|
92
97
|
"vue-tsc": "^0.40.13",
|
package/src/CodeDiff.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, watch } from 'vue-demi'
|
|
2
|
+
import { computed, ref, shallowRef, watch } from 'vue-demi'
|
|
3
3
|
import { createSplitDiff, createUnifiedDiff } from './utils'
|
|
4
4
|
import UnifiedViewer from './unified/UnifiedViewer.vue'
|
|
5
5
|
import SplitViewer from './split/SplitViewer.vue'
|
|
@@ -77,7 +77,7 @@ const raw = computed(() =>
|
|
|
77
77
|
? createUnifiedDiff(oldString.value, newString.value, props.language, props.diffStyle, props.forceInlineComparison, props.context, props.ignoreMatchingLines)
|
|
78
78
|
: createSplitDiff(oldString.value, newString.value, props.language, props.diffStyle, props.forceInlineComparison, props.context, props.ignoreMatchingLines),
|
|
79
79
|
)
|
|
80
|
-
const diffChange =
|
|
80
|
+
const diffChange = shallowRef(raw.value)
|
|
81
81
|
const isNotChanged = computed(() => diffChange.value.stat.additionsNum === 0 && diffChange.value.stat.deletionsNum === 0)
|
|
82
82
|
|
|
83
83
|
const currentDiffIndex = ref(-1)
|
|
@@ -169,8 +169,8 @@ watch(() => props, () => {
|
|
|
169
169
|
</span>
|
|
170
170
|
</div>
|
|
171
171
|
</div>
|
|
172
|
-
<UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" />
|
|
173
|
-
<SplitViewer v-else :diff-change="diffChange" />
|
|
172
|
+
<UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" :language="language" />
|
|
173
|
+
<SplitViewer v-else :diff-change="diffChange" :language="language" />
|
|
174
174
|
</div>
|
|
175
175
|
</template>
|
|
176
176
|
|
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, shallowRef, watch } from 'vue-demi'
|
|
2
3
|
import type { SplitLineChange, SplitViewerChange } from '../types'
|
|
4
|
+
import { RENDER_BATCH_SIZE, highlightSplitLine } from '../utils'
|
|
3
5
|
import SplitLine from './SplitLine.vue'
|
|
4
6
|
|
|
5
7
|
const props = defineProps<{
|
|
6
8
|
diffChange: SplitViewerChange
|
|
9
|
+
language: string
|
|
7
10
|
}>()
|
|
8
11
|
|
|
12
|
+
const renderLimit = ref(RENDER_BATCH_SIZE)
|
|
13
|
+
const visibleChanges = shallowRef(props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined))
|
|
14
|
+
const renderedChanges = computed(() => visibleChanges.value.slice(0, renderLimit.value))
|
|
15
|
+
const remainingLines = computed(() => visibleChanges.value.length - renderedChanges.value.length)
|
|
16
|
+
const nextBatchSize = computed(() => Math.min(remainingLines.value, RENDER_BATCH_SIZE))
|
|
17
|
+
|
|
18
|
+
watch(() => props.diffChange, (diffChange) => {
|
|
19
|
+
renderLimit.value = RENDER_BATCH_SIZE
|
|
20
|
+
visibleChanges.value = diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function highlightRenderedChanges() {
|
|
24
|
+
renderedChanges.value.forEach(line => highlightSplitLine(line, props.language))
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
function expandHandler({ hideIndex }: SplitLineChange) {
|
|
10
28
|
if (hideIndex === undefined)
|
|
11
29
|
return
|
|
12
|
-
props.diffChange.collector[hideIndex
|
|
30
|
+
props.diffChange.collector[hideIndex].lines.forEach((line) => {
|
|
13
31
|
line.hide = false
|
|
14
32
|
line.fold = false
|
|
15
33
|
})
|
|
34
|
+
visibleChanges.value = props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined)
|
|
35
|
+
highlightRenderedChanges()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadMore() {
|
|
39
|
+
renderLimit.value += RENDER_BATCH_SIZE
|
|
40
|
+
highlightRenderedChanges()
|
|
16
41
|
}
|
|
17
42
|
</script>
|
|
18
43
|
|
|
@@ -25,7 +50,14 @@ function expandHandler({ hideIndex }: SplitLineChange) {
|
|
|
25
50
|
<col>
|
|
26
51
|
</colgroup>
|
|
27
52
|
<tbody>
|
|
28
|
-
<SplitLine v-for="(item, index) in
|
|
53
|
+
<SplitLine v-for="(item, index) in renderedChanges" :key="index" :split-line="item" @expand="expandHandler" />
|
|
54
|
+
<tr v-if="remainingLines">
|
|
55
|
+
<td class="blob-code blob-code-hunk load-more" colspan="4">
|
|
56
|
+
<button class="load-more-button" type="button" @click="loadMore">
|
|
57
|
+
Show next {{ nextBatchSize }} lines ({{ remainingLines }} remaining)
|
|
58
|
+
</button>
|
|
59
|
+
</td>
|
|
60
|
+
</tr>
|
|
29
61
|
</tbody>
|
|
30
62
|
</table>
|
|
31
63
|
</template>
|
package/src/style.scss
CHANGED
|
@@ -168,6 +168,24 @@
|
|
|
168
168
|
.blob-code-hunk {
|
|
169
169
|
background-color: var(--color-accent-subtle);
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
.load-more {
|
|
173
|
+
padding: 8px;
|
|
174
|
+
text-align: center;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.load-more-button {
|
|
178
|
+
padding: 4px 12px;
|
|
179
|
+
color: var(--color-accent-fg);
|
|
180
|
+
background: transparent;
|
|
181
|
+
border: 1px solid var(--color-border-default);
|
|
182
|
+
border-radius: 4px;
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.load-more-button:hover {
|
|
187
|
+
background-color: var(--color-accent-muted);
|
|
188
|
+
}
|
|
171
189
|
}
|
|
172
190
|
|
|
173
191
|
.file-diff-split {
|
package/src/types.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface DiffLine {
|
|
|
23
23
|
|
|
24
24
|
export interface SplitLineChange {
|
|
25
25
|
fold?: boolean
|
|
26
|
+
highlighted?: boolean
|
|
26
27
|
left: DiffLine
|
|
27
28
|
right: DiffLine
|
|
28
29
|
hide?: boolean
|
|
@@ -31,6 +32,7 @@ export interface SplitLineChange {
|
|
|
31
32
|
|
|
32
33
|
export interface UnifiedLineChange {
|
|
33
34
|
fold?: boolean
|
|
35
|
+
highlighted?: boolean
|
|
34
36
|
type: DiffType
|
|
35
37
|
code: string
|
|
36
38
|
delNum?: number
|
|
@@ -1,25 +1,57 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, shallowRef, watch } from 'vue-demi'
|
|
2
3
|
import type { UnifiedLineChange, UnifiedViewerChange } from '../types'
|
|
4
|
+
import { RENDER_BATCH_SIZE, highlightUnifiedLine } from '../utils'
|
|
3
5
|
import UnifiedLine from './UnifiedLine.vue'
|
|
4
6
|
|
|
5
7
|
const props = defineProps<{
|
|
6
8
|
diffChange: UnifiedViewerChange
|
|
9
|
+
language: string
|
|
7
10
|
}>()
|
|
8
11
|
|
|
12
|
+
const renderLimit = ref(RENDER_BATCH_SIZE)
|
|
13
|
+
const visibleChanges = shallowRef(props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined))
|
|
14
|
+
const renderedChanges = computed(() => visibleChanges.value.slice(0, renderLimit.value))
|
|
15
|
+
const remainingLines = computed(() => visibleChanges.value.length - renderedChanges.value.length)
|
|
16
|
+
const nextBatchSize = computed(() => Math.min(remainingLines.value, RENDER_BATCH_SIZE))
|
|
17
|
+
|
|
18
|
+
watch(() => props.diffChange, (diffChange) => {
|
|
19
|
+
renderLimit.value = RENDER_BATCH_SIZE
|
|
20
|
+
visibleChanges.value = diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function highlightRenderedChanges() {
|
|
24
|
+
renderedChanges.value.forEach(line => highlightUnifiedLine(line, props.language))
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
function expandHandler({ hideIndex }: UnifiedLineChange) {
|
|
10
28
|
if (hideIndex === undefined)
|
|
11
29
|
return
|
|
12
|
-
props.diffChange.collector[hideIndex
|
|
30
|
+
props.diffChange.collector[hideIndex].lines.forEach((line) => {
|
|
13
31
|
line.hide = false
|
|
14
32
|
line.fold = false
|
|
15
33
|
})
|
|
34
|
+
visibleChanges.value = props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined)
|
|
35
|
+
highlightRenderedChanges()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadMore() {
|
|
39
|
+
renderLimit.value += RENDER_BATCH_SIZE
|
|
40
|
+
highlightRenderedChanges()
|
|
16
41
|
}
|
|
17
42
|
</script>
|
|
18
43
|
|
|
19
44
|
<template>
|
|
20
45
|
<table class="diff-table">
|
|
21
46
|
<tbody>
|
|
22
|
-
<UnifiedLine v-for="(item, index) in
|
|
47
|
+
<UnifiedLine v-for="(item, index) in renderedChanges" :key="index" :line="item" @expand="expandHandler" />
|
|
48
|
+
<tr v-if="remainingLines">
|
|
49
|
+
<td class="blob-code blob-code-hunk load-more" colspan="3">
|
|
50
|
+
<button class="load-more-button" type="button" @click="loadMore">
|
|
51
|
+
Show next {{ nextBatchSize }} lines ({{ remainingLines }} remaining)
|
|
52
|
+
</button>
|
|
53
|
+
</td>
|
|
54
|
+
</tr>
|
|
23
55
|
</tbody>
|
|
24
56
|
</table>
|
|
25
57
|
</template>
|
package/src/utils.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type { DiffLine, DiffStat, SplitLineChange, SplitLineUnchanges, SplitView
|
|
|
7
7
|
|
|
8
8
|
const MODIFIED_START_TAG = '<code-diff-modified>'
|
|
9
9
|
const MODIFIED_CLOSE_TAG = '</code-diff-modified>'
|
|
10
|
+
const MAX_INLINE_DIFF_LENGTH = 10_000
|
|
11
|
+
export const RENDER_BATCH_SIZE = 1_000
|
|
10
12
|
|
|
11
13
|
const startEntity = MODIFIED_START_TAG.replace('<', '<').replace('>', '>')
|
|
12
14
|
const closeEntity = MODIFIED_CLOSE_TAG.replace('<', '<').replace('>', '>')
|
|
@@ -32,45 +34,117 @@ function lineType(diff: Diff.Change): DiffType {
|
|
|
32
34
|
return DiffType.EQUAL
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
function
|
|
36
|
-
if (typeof prev === 'undefined')
|
|
37
|
-
return current
|
|
38
|
-
if (
|
|
39
|
-
return prev
|
|
37
|
+
function renderChangedLines(prev?: string, current?: string, diffStyle = 'word', force = false): [string | undefined, string | undefined] {
|
|
38
|
+
if (typeof prev === 'undefined' || typeof current === 'undefined')
|
|
39
|
+
return [prev, current]
|
|
40
|
+
if (!force && prev.length + current.length > MAX_INLINE_DIFF_LENGTH)
|
|
41
|
+
return [prev, current]
|
|
40
42
|
|
|
41
43
|
type RenderFunctionType = (prev: string, old: string) => Change[]
|
|
42
44
|
const func: RenderFunctionType = diffStyle === 'char' ? Diff.diffChars : Diff.diffWords
|
|
43
|
-
|
|
45
|
+
const changes = func(prev, current)
|
|
46
|
+
const oldLine = changes
|
|
47
|
+
.filter(word => lineType(word) !== DiffType.ADD)
|
|
48
|
+
.map(word =>
|
|
49
|
+
lineType(word) === DiffType.DELETE ? `${MODIFIED_START_TAG}${word.value}${MODIFIED_CLOSE_TAG}` : word.value,
|
|
50
|
+
)
|
|
51
|
+
.join('')
|
|
52
|
+
const newLine = changes
|
|
44
53
|
.filter(word => lineType(word) !== DiffType.DELETE)
|
|
45
54
|
.map(word =>
|
|
46
55
|
lineType(word) === DiffType.ADD ? `${MODIFIED_START_TAG}${word.value}${MODIFIED_CLOSE_TAG}` : word.value,
|
|
47
56
|
)
|
|
48
57
|
.join('')
|
|
58
|
+
return [oldLine, newLine]
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
function diffLines(prev: string, current: string) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
if (prev === current) {
|
|
63
|
+
return prev
|
|
64
|
+
? [{ count: prev.replace(/\n$/, '').split('\n').length, value: prev }]
|
|
65
|
+
: []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const prevHasFinalNewline = prev.endsWith('\n')
|
|
69
|
+
const currentHasFinalNewline = current.endsWith('\n')
|
|
70
|
+
if (prevHasFinalNewline !== currentHasFinalNewline) {
|
|
71
|
+
if (prevHasFinalNewline)
|
|
72
|
+
prev = prev.slice(0, -1)
|
|
73
|
+
else
|
|
74
|
+
current = current.slice(0, -1)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (prev)
|
|
78
|
+
prev += '\n'
|
|
79
|
+
if (current)
|
|
80
|
+
current += '\n'
|
|
81
|
+
|
|
82
|
+
let changes: Diff.Change[] | undefined
|
|
83
|
+
if (prev.length + current.length >= 100_000 && prev.includes('\n') && current.includes('\n')) {
|
|
84
|
+
const prevLines = prev ? prev.replace(/\n$/, '').split('\n') : []
|
|
85
|
+
const currentLines = current ? current.replace(/\n$/, '').split('\n') : []
|
|
86
|
+
const prevLineSet = new Set(prevLines)
|
|
87
|
+
const currentLineSet = new Set(currentLines)
|
|
88
|
+
const [smallerSet, largerSet] = prevLineSet.size < currentLineSet.size
|
|
89
|
+
? [prevLineSet, currentLineSet]
|
|
90
|
+
: [currentLineSet, prevLineSet]
|
|
91
|
+
let sharedLines = 0
|
|
92
|
+
for (const line of smallerSet) {
|
|
93
|
+
if (largerSet.has(line))
|
|
94
|
+
sharedLines++
|
|
95
|
+
}
|
|
96
|
+
const overlap = smallerSet.size ? sharedLines / smallerSet.size : 0
|
|
97
|
+
const exceedsDmpLineLimit = prevLineSet.size >= 40_000 || prevLineSet.size + currentLineSet.size - sharedLines >= 65_535
|
|
98
|
+
const changedUniqueLines = prevLineSet.size + currentLineSet.size - sharedLines * 2
|
|
99
|
+
|
|
100
|
+
if (overlap < 0.01 || (exceedsDmpLineLimit && changedUniqueLines > 200)) {
|
|
101
|
+
// ponytail: complex large inputs use whole-file replacements; revisit if detailed move detection becomes necessary.
|
|
102
|
+
changes = []
|
|
103
|
+
if (prev)
|
|
104
|
+
changes.push({ count: prevLines.length, value: prev, removed: true })
|
|
105
|
+
if (current)
|
|
106
|
+
changes.push({ count: currentLines.length, value: current, added: true })
|
|
67
107
|
}
|
|
68
|
-
|
|
69
|
-
|
|
108
|
+
else if (exceedsDmpLineLimit) {
|
|
109
|
+
changes = Diff.diffLines(prev, current)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!changes) {
|
|
114
|
+
const dmp = new DiffMatchPatch()
|
|
115
|
+
const a = dmp.diff_linesToChars_(prev, current)
|
|
116
|
+
const linePrev = a.chars1
|
|
117
|
+
const lineCurrent = a.chars2
|
|
118
|
+
const lineArray = a.lineArray
|
|
119
|
+
const diffs: any[] = dmp.diff_main(linePrev, lineCurrent, false)
|
|
120
|
+
dmp.diff_charsToLines_(diffs, lineArray)
|
|
121
|
+
changes = diffs.map((x) => {
|
|
122
|
+
const [type, text] = x
|
|
123
|
+
const count = text.replace(/\n$/, '').split('\n').length
|
|
124
|
+
const change: Diff.Change = {
|
|
125
|
+
count,
|
|
126
|
+
value: text,
|
|
127
|
+
removed: type === DIFF_DELETE,
|
|
128
|
+
added: type === DIFF_INSERT,
|
|
129
|
+
}
|
|
130
|
+
return change
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (prevHasFinalNewline !== currentHasFinalNewline) {
|
|
135
|
+
changes.push({
|
|
136
|
+
count: 1,
|
|
137
|
+
value: '',
|
|
138
|
+
removed: prevHasFinalNewline,
|
|
139
|
+
added: currentHasFinalNewline,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return changes
|
|
70
144
|
}
|
|
71
145
|
|
|
72
146
|
function getHighlightCode(language: string, code: string) {
|
|
73
|
-
if (typeof document === 'undefined') {
|
|
147
|
+
if (typeof document === 'undefined' || language === 'plaintext') {
|
|
74
148
|
return escapeHtml(code)
|
|
75
149
|
.replace(new RegExp(startEntity, 'g'), '<span class="x">')
|
|
76
150
|
.replace(new RegExp(closeEntity, 'g'), '</span>')
|
|
@@ -152,6 +226,28 @@ function getHighlightCode(language: string, code: string) {
|
|
|
152
226
|
.replace(new RegExp(closeEntity, 'g'), '</span>')
|
|
153
227
|
}
|
|
154
228
|
|
|
229
|
+
export function highlightUnifiedLine(line: UnifiedLineChange, language: string) {
|
|
230
|
+
if (line.highlighted)
|
|
231
|
+
return
|
|
232
|
+
line.code = getHighlightCode(language, line.code)
|
|
233
|
+
line.highlighted = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function highlightSplitLine(line: SplitLineChange, language: string) {
|
|
237
|
+
if (line.highlighted)
|
|
238
|
+
return
|
|
239
|
+
const leftCode = line.left.code
|
|
240
|
+
if (leftCode !== undefined)
|
|
241
|
+
line.left.code = getHighlightCode(language, leftCode)
|
|
242
|
+
|
|
243
|
+
if (line.right.code !== undefined) {
|
|
244
|
+
line.right.code = line.left.type === DiffType.EQUAL && line.right.type === DiffType.EQUAL && line.right.code === leftCode
|
|
245
|
+
? line.left.code
|
|
246
|
+
: getHighlightCode(language, line.right.code)
|
|
247
|
+
}
|
|
248
|
+
line.highlighted = true
|
|
249
|
+
}
|
|
250
|
+
|
|
155
251
|
function calcDiffStat(changes: Change[], ignoreRegex?: RegExp): DiffStat {
|
|
156
252
|
const count = (s: string, c: string) => (s.match(new RegExp(c, 'g')) || []).length
|
|
157
253
|
const ignoreCount = (lines: string[]) => lines.filter(line => ignoreRegex?.test(line)).length
|
|
@@ -162,12 +258,20 @@ function calcDiffStat(changes: Change[], ignoreRegex?: RegExp): DiffStat {
|
|
|
162
258
|
let ignoreDeletionsNum = 0
|
|
163
259
|
for (const change of changes) {
|
|
164
260
|
if (change.added) {
|
|
261
|
+
if (!ignoreRegex) {
|
|
262
|
+
additionsNum += change.count ?? 0
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
165
265
|
const ignoreNum = ignoreCount(change.value.trim().split('\n'))
|
|
166
266
|
additionsNum += count(change.value.trim(), '\n') + 1 - ignoreNum
|
|
167
267
|
ignoreAdditionsNum += ignoreNum
|
|
168
268
|
continue
|
|
169
269
|
}
|
|
170
270
|
if (change.removed) {
|
|
271
|
+
if (!ignoreRegex) {
|
|
272
|
+
deletionsNum += change.count ?? 0
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
171
275
|
const ignoreNum = ignoreCount(change.value.trim().split('\n'))
|
|
172
276
|
deletionsNum += count(change.value.trim(), '\n') + 1 - ignoreNum
|
|
173
277
|
ignoreDeletionsNum += ignoreNum
|
|
@@ -224,25 +328,24 @@ export function createSplitDiff(
|
|
|
224
328
|
let left: DiffLine = newEmptySplitDiff()
|
|
225
329
|
let right: DiffLine = newEmptySplitDiff()
|
|
226
330
|
|
|
227
|
-
const highlightCode = getHighlightCode(language, line)
|
|
228
331
|
if (curType === DiffType.EQUAL) {
|
|
229
332
|
delNum++
|
|
230
333
|
addNum++
|
|
231
334
|
|
|
232
|
-
left = newSplitDiff(DiffType.EQUAL, delNum,
|
|
233
|
-
right = newSplitDiff(DiffType.EQUAL, addNum,
|
|
335
|
+
left = newSplitDiff(DiffType.EQUAL, delNum, line)
|
|
336
|
+
right = newSplitDiff(DiffType.EQUAL, addNum, line)
|
|
234
337
|
}
|
|
235
338
|
if (curType === DiffType.DELETE) {
|
|
236
339
|
delNum++
|
|
237
340
|
|
|
238
|
-
left = newSplitDiff(DiffType.DELETE, delNum,
|
|
341
|
+
left = newSplitDiff(DiffType.DELETE, delNum, line)
|
|
239
342
|
right = newEmptySplitDiff()
|
|
240
343
|
}
|
|
241
344
|
if (curType === DiffType.ADD) {
|
|
242
345
|
addNum++
|
|
243
346
|
|
|
244
347
|
left = newEmptySplitDiff()
|
|
245
|
-
right = newSplitDiff(DiffType.ADD, addNum,
|
|
348
|
+
right = newSplitDiff(DiffType.ADD, addNum, line)
|
|
246
349
|
}
|
|
247
350
|
rawChanges.push({ left, right })
|
|
248
351
|
}
|
|
@@ -256,10 +359,9 @@ export function createSplitDiff(
|
|
|
256
359
|
delNum++
|
|
257
360
|
addNum++
|
|
258
361
|
|
|
259
|
-
const highlightCode = getHighlightCode(language, line)
|
|
260
362
|
rawChanges.push({
|
|
261
|
-
left: newSplitDiff(DiffType.EQUAL, delNum,
|
|
262
|
-
right: newSplitDiff(DiffType.EQUAL, addNum,
|
|
363
|
+
left: newSplitDiff(DiffType.EQUAL, delNum, line),
|
|
364
|
+
right: newSplitDiff(DiffType.EQUAL, addNum, line),
|
|
263
365
|
})
|
|
264
366
|
}
|
|
265
367
|
}
|
|
@@ -272,7 +374,7 @@ export function createSplitDiff(
|
|
|
272
374
|
delNum++
|
|
273
375
|
|
|
274
376
|
rawChanges.push({
|
|
275
|
-
left: newSplitDiff(DiffType.DELETE, delNum,
|
|
377
|
+
left: newSplitDiff(DiffType.DELETE, delNum, line),
|
|
276
378
|
right: newEmptySplitDiff(),
|
|
277
379
|
})
|
|
278
380
|
}
|
|
@@ -289,8 +391,9 @@ export function createSplitDiff(
|
|
|
289
391
|
|
|
290
392
|
const [curLine, nextLine] = [curLines[j], nextLines[j]]
|
|
291
393
|
const shouldRenderWords = forceInlineComparison || curLines.length === nextLines.length
|
|
292
|
-
const leftLine = shouldRenderWords
|
|
293
|
-
|
|
394
|
+
const [leftLine, rightLine] = shouldRenderWords
|
|
395
|
+
? renderChangedLines(curLine, nextLine, diffStyle, forceInlineComparison)
|
|
396
|
+
: [curLine, nextLine]
|
|
294
397
|
|
|
295
398
|
// 忽略匹配的行等价于相等
|
|
296
399
|
const leftDiffType = ignoreRegex?.test(curLine) ? DiffType.EQUAL : DiffType.DELETE
|
|
@@ -298,11 +401,11 @@ export function createSplitDiff(
|
|
|
298
401
|
|
|
299
402
|
const left
|
|
300
403
|
= j < cur.count!
|
|
301
|
-
? newSplitDiff(leftDiffType, delNum,
|
|
404
|
+
? newSplitDiff(leftDiffType, delNum, leftLine!)
|
|
302
405
|
: newEmptySplitDiff()
|
|
303
406
|
const right
|
|
304
407
|
= j < next.count!
|
|
305
|
-
? newSplitDiff(rightDiffType, addNum,
|
|
408
|
+
? newSplitDiff(rightDiffType, addNum, rightLine!)
|
|
306
409
|
: newEmptySplitDiff()
|
|
307
410
|
|
|
308
411
|
rawChanges.push({ left, right })
|
|
@@ -315,7 +418,7 @@ export function createSplitDiff(
|
|
|
315
418
|
addNum++
|
|
316
419
|
rawChanges.push({
|
|
317
420
|
left: newEmptySplitDiff(),
|
|
318
|
-
right: newSplitDiff(DiffType.ADD, addNum,
|
|
421
|
+
right: newSplitDiff(DiffType.ADD, addNum, line),
|
|
319
422
|
})
|
|
320
423
|
}
|
|
321
424
|
}
|
|
@@ -324,6 +427,7 @@ export function createSplitDiff(
|
|
|
324
427
|
if (oldString === newString) {
|
|
325
428
|
for (let i = 0; i < rawChanges.length; i++)
|
|
326
429
|
rawChanges[i].fold = false
|
|
430
|
+
rawChanges.slice(0, RENDER_BATCH_SIZE).forEach(line => highlightSplitLine(line, language))
|
|
327
431
|
|
|
328
432
|
return result
|
|
329
433
|
}
|
|
@@ -370,6 +474,7 @@ export function createSplitDiff(
|
|
|
370
474
|
unchanges = []
|
|
371
475
|
}
|
|
372
476
|
result.changes = processedChanges
|
|
477
|
+
result.changes.filter(line => !line.hide).slice(0, RENDER_BATCH_SIZE).forEach(line => highlightSplitLine(line, language))
|
|
373
478
|
|
|
374
479
|
return result
|
|
375
480
|
}
|
|
@@ -421,11 +526,9 @@ export function createUnifiedDiff(
|
|
|
421
526
|
if (curType === DiffType.ADD)
|
|
422
527
|
addNum++
|
|
423
528
|
|
|
424
|
-
const code = getHighlightCode(language, line)
|
|
425
|
-
|
|
426
529
|
rawChanges.push({
|
|
427
530
|
type: curType,
|
|
428
|
-
code,
|
|
531
|
+
code: line,
|
|
429
532
|
addNum: curType === DiffType.DELETE ? undefined : addNum,
|
|
430
533
|
delNum: curType === DiffType.ADD ? undefined : delNum,
|
|
431
534
|
})
|
|
@@ -439,9 +542,7 @@ export function createUnifiedDiff(
|
|
|
439
542
|
for (const line of curLines) {
|
|
440
543
|
delNum++
|
|
441
544
|
addNum++
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
rawChanges.push({ type: DiffType.EQUAL, code, delNum, addNum })
|
|
545
|
+
rawChanges.push({ type: DiffType.EQUAL, code: line, delNum, addNum })
|
|
445
546
|
}
|
|
446
547
|
}
|
|
447
548
|
|
|
@@ -450,28 +551,26 @@ export function createUnifiedDiff(
|
|
|
450
551
|
if (curType === DiffType.DELETE) {
|
|
451
552
|
// 下一处差异为新增,且删除与新增行数相同时,对每行依次 diff
|
|
452
553
|
if (nextType === DiffType.ADD && (curLines.length === nextLines.length || forceInlineComparison)) {
|
|
554
|
+
const maxCount = Math.max(curLines.length, nextLines.length)
|
|
555
|
+
const renderedLines = Array.from({ length: maxCount }, (_, index) => renderChangedLines(curLines[index], nextLines[index], diffStyle, forceInlineComparison))
|
|
453
556
|
for (let j = 0; j < curLines.length; j++) {
|
|
454
557
|
const curLine = curLines[j]
|
|
455
|
-
const nextLine = nextLines[j]
|
|
456
558
|
delNum++
|
|
457
559
|
|
|
458
|
-
const code = getHighlightCode(language, renderWords(nextLine, curLine, diffStyle))
|
|
459
560
|
rawChanges.push({
|
|
460
561
|
type: ignoreRegex?.test(curLine) ? DiffType.EQUAL : DiffType.DELETE,
|
|
461
|
-
code
|
|
562
|
+
code: renderedLines[j][0]!,
|
|
462
563
|
delNum,
|
|
463
564
|
})
|
|
464
565
|
}
|
|
465
566
|
|
|
466
567
|
for (let j = 0; j < nextLines.length; j++) {
|
|
467
|
-
const curLine = curLines[j]
|
|
468
568
|
const nextLine = nextLines[j]
|
|
469
569
|
addNum++
|
|
470
570
|
|
|
471
|
-
const code = getHighlightCode(language, renderWords(curLine, nextLine, diffStyle))
|
|
472
571
|
rawChanges.push({
|
|
473
572
|
type: ignoreRegex?.test(nextLine) ? DiffType.EQUAL : DiffType.ADD,
|
|
474
|
-
code
|
|
573
|
+
code: renderedLines[j][1]!,
|
|
475
574
|
addNum,
|
|
476
575
|
})
|
|
477
576
|
}
|
|
@@ -483,8 +582,7 @@ export function createUnifiedDiff(
|
|
|
483
582
|
for (const line of curLines) {
|
|
484
583
|
delNum++
|
|
485
584
|
|
|
486
|
-
|
|
487
|
-
rawChanges.push({ type: DiffType.DELETE, code, delNum })
|
|
585
|
+
rawChanges.push({ type: DiffType.DELETE, code: line, delNum })
|
|
488
586
|
}
|
|
489
587
|
}
|
|
490
588
|
}
|
|
@@ -492,9 +590,7 @@ export function createUnifiedDiff(
|
|
|
492
590
|
if (curType === DiffType.ADD) {
|
|
493
591
|
for (const line of curLines) {
|
|
494
592
|
addNum++
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
rawChanges.push({ type: DiffType.ADD, code, addNum })
|
|
593
|
+
rawChanges.push({ type: DiffType.ADD, code: line, addNum })
|
|
498
594
|
}
|
|
499
595
|
}
|
|
500
596
|
}
|
|
@@ -513,6 +609,7 @@ export function createUnifiedDiff(
|
|
|
513
609
|
if (oldString === newString) {
|
|
514
610
|
for (let i = 0; i < rawChanges.length; i++)
|
|
515
611
|
rawChanges[i].fold = false
|
|
612
|
+
rawChanges.slice(0, RENDER_BATCH_SIZE).forEach(line => highlightUnifiedLine(line, language))
|
|
516
613
|
|
|
517
614
|
return result
|
|
518
615
|
}
|
|
@@ -551,6 +648,7 @@ export function createUnifiedDiff(
|
|
|
551
648
|
unchanges = []
|
|
552
649
|
}
|
|
553
650
|
result.changes = processedChanges
|
|
651
|
+
result.changes.filter(line => !line.hide).slice(0, RENDER_BATCH_SIZE).forEach(line => highlightUnifiedLine(line, language))
|
|
554
652
|
|
|
555
653
|
return result
|
|
556
654
|
}
|