v-code-diff 1.13.2 → 1.14.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.
- package/README.md +3 -0
- package/dist/v2/index.cjs +26 -19
- package/dist/v2/index.es.js +1607 -1506
- package/dist/v2/index.umd.js +26 -19
- package/dist/v2.7/index.cjs +26 -19
- package/dist/v2.7/index.es.js +1432 -1377
- package/dist/v2.7/index.umd.js +26 -19
- package/dist/v3/index.cjs +26 -19
- package/dist/v3/index.es.js +1680 -1603
- package/dist/v3/index.umd.js +26 -19
- package/package.json +5 -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 +148 -55
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.0",
|
|
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,8 @@
|
|
|
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": "vitest run"
|
|
58
60
|
},
|
|
59
61
|
"peerDependencies": {
|
|
60
62
|
"@vue/composition-api": "^1.4.9",
|
|
@@ -87,6 +89,7 @@
|
|
|
87
89
|
"typescript": "~4.7.4",
|
|
88
90
|
"vite": "^5.4.2",
|
|
89
91
|
"vite-plugin-css-injected-by-js": "^2.4.0",
|
|
92
|
+
"vitest": "^2.1.9",
|
|
90
93
|
"vue": "^3.4.38",
|
|
91
94
|
"vue-i18n": "^9.14.0",
|
|
92
95
|
"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,112 @@ 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
|
+
let changes: Diff.Change[] | undefined
|
|
78
|
+
if (prev.length + current.length >= 100_000 && prev.includes('\n') && current.includes('\n')) {
|
|
79
|
+
const prevLines = prev ? prev.replace(/\n$/, '').split('\n') : []
|
|
80
|
+
const currentLines = current ? current.replace(/\n$/, '').split('\n') : []
|
|
81
|
+
const prevLineSet = new Set(prevLines)
|
|
82
|
+
const currentLineSet = new Set(currentLines)
|
|
83
|
+
const [smallerSet, largerSet] = prevLineSet.size < currentLineSet.size
|
|
84
|
+
? [prevLineSet, currentLineSet]
|
|
85
|
+
: [currentLineSet, prevLineSet]
|
|
86
|
+
let sharedLines = 0
|
|
87
|
+
for (const line of smallerSet) {
|
|
88
|
+
if (largerSet.has(line))
|
|
89
|
+
sharedLines++
|
|
90
|
+
}
|
|
91
|
+
const overlap = smallerSet.size ? sharedLines / smallerSet.size : 0
|
|
92
|
+
const exceedsDmpLineLimit = prevLineSet.size >= 40_000 || prevLineSet.size + currentLineSet.size - sharedLines >= 65_535
|
|
93
|
+
const changedUniqueLines = prevLineSet.size + currentLineSet.size - sharedLines * 2
|
|
94
|
+
|
|
95
|
+
if (overlap < 0.01 || (exceedsDmpLineLimit && changedUniqueLines > 200)) {
|
|
96
|
+
// ponytail: complex large inputs use whole-file replacements; revisit if detailed move detection becomes necessary.
|
|
97
|
+
changes = []
|
|
98
|
+
if (prev)
|
|
99
|
+
changes.push({ count: prevLines.length, value: prev, removed: true })
|
|
100
|
+
if (current)
|
|
101
|
+
changes.push({ count: currentLines.length, value: current, added: true })
|
|
67
102
|
}
|
|
68
|
-
|
|
69
|
-
|
|
103
|
+
else if (exceedsDmpLineLimit) {
|
|
104
|
+
changes = Diff.diffLines(prev, current)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!changes) {
|
|
109
|
+
const dmp = new DiffMatchPatch()
|
|
110
|
+
const a = dmp.diff_linesToChars_(prev, current)
|
|
111
|
+
const linePrev = a.chars1
|
|
112
|
+
const lineCurrent = a.chars2
|
|
113
|
+
const lineArray = a.lineArray
|
|
114
|
+
const diffs: any[] = dmp.diff_main(linePrev, lineCurrent, false)
|
|
115
|
+
dmp.diff_charsToLines_(diffs, lineArray)
|
|
116
|
+
changes = diffs.map((x) => {
|
|
117
|
+
const [type, text] = x
|
|
118
|
+
const count = text.replace(/\n$/, '').split('\n').length
|
|
119
|
+
const change: Diff.Change = {
|
|
120
|
+
count,
|
|
121
|
+
value: text,
|
|
122
|
+
removed: type === DIFF_DELETE,
|
|
123
|
+
added: type === DIFF_INSERT,
|
|
124
|
+
}
|
|
125
|
+
return change
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (prevHasFinalNewline !== currentHasFinalNewline) {
|
|
130
|
+
changes.push({
|
|
131
|
+
count: 1,
|
|
132
|
+
value: '',
|
|
133
|
+
removed: prevHasFinalNewline,
|
|
134
|
+
added: currentHasFinalNewline,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return changes
|
|
70
139
|
}
|
|
71
140
|
|
|
72
141
|
function getHighlightCode(language: string, code: string) {
|
|
73
|
-
if (typeof document === 'undefined') {
|
|
142
|
+
if (typeof document === 'undefined' || language === 'plaintext') {
|
|
74
143
|
return escapeHtml(code)
|
|
75
144
|
.replace(new RegExp(startEntity, 'g'), '<span class="x">')
|
|
76
145
|
.replace(new RegExp(closeEntity, 'g'), '</span>')
|
|
@@ -152,6 +221,28 @@ function getHighlightCode(language: string, code: string) {
|
|
|
152
221
|
.replace(new RegExp(closeEntity, 'g'), '</span>')
|
|
153
222
|
}
|
|
154
223
|
|
|
224
|
+
export function highlightUnifiedLine(line: UnifiedLineChange, language: string) {
|
|
225
|
+
if (line.highlighted)
|
|
226
|
+
return
|
|
227
|
+
line.code = getHighlightCode(language, line.code)
|
|
228
|
+
line.highlighted = true
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function highlightSplitLine(line: SplitLineChange, language: string) {
|
|
232
|
+
if (line.highlighted)
|
|
233
|
+
return
|
|
234
|
+
const leftCode = line.left.code
|
|
235
|
+
if (leftCode !== undefined)
|
|
236
|
+
line.left.code = getHighlightCode(language, leftCode)
|
|
237
|
+
|
|
238
|
+
if (line.right.code !== undefined) {
|
|
239
|
+
line.right.code = line.left.type === DiffType.EQUAL && line.right.type === DiffType.EQUAL && line.right.code === leftCode
|
|
240
|
+
? line.left.code
|
|
241
|
+
: getHighlightCode(language, line.right.code)
|
|
242
|
+
}
|
|
243
|
+
line.highlighted = true
|
|
244
|
+
}
|
|
245
|
+
|
|
155
246
|
function calcDiffStat(changes: Change[], ignoreRegex?: RegExp): DiffStat {
|
|
156
247
|
const count = (s: string, c: string) => (s.match(new RegExp(c, 'g')) || []).length
|
|
157
248
|
const ignoreCount = (lines: string[]) => lines.filter(line => ignoreRegex?.test(line)).length
|
|
@@ -162,12 +253,20 @@ function calcDiffStat(changes: Change[], ignoreRegex?: RegExp): DiffStat {
|
|
|
162
253
|
let ignoreDeletionsNum = 0
|
|
163
254
|
for (const change of changes) {
|
|
164
255
|
if (change.added) {
|
|
256
|
+
if (!ignoreRegex) {
|
|
257
|
+
additionsNum += change.count ?? 0
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
165
260
|
const ignoreNum = ignoreCount(change.value.trim().split('\n'))
|
|
166
261
|
additionsNum += count(change.value.trim(), '\n') + 1 - ignoreNum
|
|
167
262
|
ignoreAdditionsNum += ignoreNum
|
|
168
263
|
continue
|
|
169
264
|
}
|
|
170
265
|
if (change.removed) {
|
|
266
|
+
if (!ignoreRegex) {
|
|
267
|
+
deletionsNum += change.count ?? 0
|
|
268
|
+
continue
|
|
269
|
+
}
|
|
171
270
|
const ignoreNum = ignoreCount(change.value.trim().split('\n'))
|
|
172
271
|
deletionsNum += count(change.value.trim(), '\n') + 1 - ignoreNum
|
|
173
272
|
ignoreDeletionsNum += ignoreNum
|
|
@@ -224,25 +323,24 @@ export function createSplitDiff(
|
|
|
224
323
|
let left: DiffLine = newEmptySplitDiff()
|
|
225
324
|
let right: DiffLine = newEmptySplitDiff()
|
|
226
325
|
|
|
227
|
-
const highlightCode = getHighlightCode(language, line)
|
|
228
326
|
if (curType === DiffType.EQUAL) {
|
|
229
327
|
delNum++
|
|
230
328
|
addNum++
|
|
231
329
|
|
|
232
|
-
left = newSplitDiff(DiffType.EQUAL, delNum,
|
|
233
|
-
right = newSplitDiff(DiffType.EQUAL, addNum,
|
|
330
|
+
left = newSplitDiff(DiffType.EQUAL, delNum, line)
|
|
331
|
+
right = newSplitDiff(DiffType.EQUAL, addNum, line)
|
|
234
332
|
}
|
|
235
333
|
if (curType === DiffType.DELETE) {
|
|
236
334
|
delNum++
|
|
237
335
|
|
|
238
|
-
left = newSplitDiff(DiffType.DELETE, delNum,
|
|
336
|
+
left = newSplitDiff(DiffType.DELETE, delNum, line)
|
|
239
337
|
right = newEmptySplitDiff()
|
|
240
338
|
}
|
|
241
339
|
if (curType === DiffType.ADD) {
|
|
242
340
|
addNum++
|
|
243
341
|
|
|
244
342
|
left = newEmptySplitDiff()
|
|
245
|
-
right = newSplitDiff(DiffType.ADD, addNum,
|
|
343
|
+
right = newSplitDiff(DiffType.ADD, addNum, line)
|
|
246
344
|
}
|
|
247
345
|
rawChanges.push({ left, right })
|
|
248
346
|
}
|
|
@@ -256,10 +354,9 @@ export function createSplitDiff(
|
|
|
256
354
|
delNum++
|
|
257
355
|
addNum++
|
|
258
356
|
|
|
259
|
-
const highlightCode = getHighlightCode(language, line)
|
|
260
357
|
rawChanges.push({
|
|
261
|
-
left: newSplitDiff(DiffType.EQUAL, delNum,
|
|
262
|
-
right: newSplitDiff(DiffType.EQUAL, addNum,
|
|
358
|
+
left: newSplitDiff(DiffType.EQUAL, delNum, line),
|
|
359
|
+
right: newSplitDiff(DiffType.EQUAL, addNum, line),
|
|
263
360
|
})
|
|
264
361
|
}
|
|
265
362
|
}
|
|
@@ -272,7 +369,7 @@ export function createSplitDiff(
|
|
|
272
369
|
delNum++
|
|
273
370
|
|
|
274
371
|
rawChanges.push({
|
|
275
|
-
left: newSplitDiff(DiffType.DELETE, delNum,
|
|
372
|
+
left: newSplitDiff(DiffType.DELETE, delNum, line),
|
|
276
373
|
right: newEmptySplitDiff(),
|
|
277
374
|
})
|
|
278
375
|
}
|
|
@@ -289,8 +386,9 @@ export function createSplitDiff(
|
|
|
289
386
|
|
|
290
387
|
const [curLine, nextLine] = [curLines[j], nextLines[j]]
|
|
291
388
|
const shouldRenderWords = forceInlineComparison || curLines.length === nextLines.length
|
|
292
|
-
const leftLine = shouldRenderWords
|
|
293
|
-
|
|
389
|
+
const [leftLine, rightLine] = shouldRenderWords
|
|
390
|
+
? renderChangedLines(curLine, nextLine, diffStyle, forceInlineComparison)
|
|
391
|
+
: [curLine, nextLine]
|
|
294
392
|
|
|
295
393
|
// 忽略匹配的行等价于相等
|
|
296
394
|
const leftDiffType = ignoreRegex?.test(curLine) ? DiffType.EQUAL : DiffType.DELETE
|
|
@@ -298,11 +396,11 @@ export function createSplitDiff(
|
|
|
298
396
|
|
|
299
397
|
const left
|
|
300
398
|
= j < cur.count!
|
|
301
|
-
? newSplitDiff(leftDiffType, delNum,
|
|
399
|
+
? newSplitDiff(leftDiffType, delNum, leftLine!)
|
|
302
400
|
: newEmptySplitDiff()
|
|
303
401
|
const right
|
|
304
402
|
= j < next.count!
|
|
305
|
-
? newSplitDiff(rightDiffType, addNum,
|
|
403
|
+
? newSplitDiff(rightDiffType, addNum, rightLine!)
|
|
306
404
|
: newEmptySplitDiff()
|
|
307
405
|
|
|
308
406
|
rawChanges.push({ left, right })
|
|
@@ -315,7 +413,7 @@ export function createSplitDiff(
|
|
|
315
413
|
addNum++
|
|
316
414
|
rawChanges.push({
|
|
317
415
|
left: newEmptySplitDiff(),
|
|
318
|
-
right: newSplitDiff(DiffType.ADD, addNum,
|
|
416
|
+
right: newSplitDiff(DiffType.ADD, addNum, line),
|
|
319
417
|
})
|
|
320
418
|
}
|
|
321
419
|
}
|
|
@@ -324,6 +422,7 @@ export function createSplitDiff(
|
|
|
324
422
|
if (oldString === newString) {
|
|
325
423
|
for (let i = 0; i < rawChanges.length; i++)
|
|
326
424
|
rawChanges[i].fold = false
|
|
425
|
+
rawChanges.slice(0, RENDER_BATCH_SIZE).forEach(line => highlightSplitLine(line, language))
|
|
327
426
|
|
|
328
427
|
return result
|
|
329
428
|
}
|
|
@@ -370,6 +469,7 @@ export function createSplitDiff(
|
|
|
370
469
|
unchanges = []
|
|
371
470
|
}
|
|
372
471
|
result.changes = processedChanges
|
|
472
|
+
result.changes.filter(line => !line.hide).slice(0, RENDER_BATCH_SIZE).forEach(line => highlightSplitLine(line, language))
|
|
373
473
|
|
|
374
474
|
return result
|
|
375
475
|
}
|
|
@@ -421,11 +521,9 @@ export function createUnifiedDiff(
|
|
|
421
521
|
if (curType === DiffType.ADD)
|
|
422
522
|
addNum++
|
|
423
523
|
|
|
424
|
-
const code = getHighlightCode(language, line)
|
|
425
|
-
|
|
426
524
|
rawChanges.push({
|
|
427
525
|
type: curType,
|
|
428
|
-
code,
|
|
526
|
+
code: line,
|
|
429
527
|
addNum: curType === DiffType.DELETE ? undefined : addNum,
|
|
430
528
|
delNum: curType === DiffType.ADD ? undefined : delNum,
|
|
431
529
|
})
|
|
@@ -439,9 +537,7 @@ export function createUnifiedDiff(
|
|
|
439
537
|
for (const line of curLines) {
|
|
440
538
|
delNum++
|
|
441
539
|
addNum++
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
rawChanges.push({ type: DiffType.EQUAL, code, delNum, addNum })
|
|
540
|
+
rawChanges.push({ type: DiffType.EQUAL, code: line, delNum, addNum })
|
|
445
541
|
}
|
|
446
542
|
}
|
|
447
543
|
|
|
@@ -450,28 +546,26 @@ export function createUnifiedDiff(
|
|
|
450
546
|
if (curType === DiffType.DELETE) {
|
|
451
547
|
// 下一处差异为新增,且删除与新增行数相同时,对每行依次 diff
|
|
452
548
|
if (nextType === DiffType.ADD && (curLines.length === nextLines.length || forceInlineComparison)) {
|
|
549
|
+
const maxCount = Math.max(curLines.length, nextLines.length)
|
|
550
|
+
const renderedLines = Array.from({ length: maxCount }, (_, index) => renderChangedLines(curLines[index], nextLines[index], diffStyle, forceInlineComparison))
|
|
453
551
|
for (let j = 0; j < curLines.length; j++) {
|
|
454
552
|
const curLine = curLines[j]
|
|
455
|
-
const nextLine = nextLines[j]
|
|
456
553
|
delNum++
|
|
457
554
|
|
|
458
|
-
const code = getHighlightCode(language, renderWords(nextLine, curLine, diffStyle))
|
|
459
555
|
rawChanges.push({
|
|
460
556
|
type: ignoreRegex?.test(curLine) ? DiffType.EQUAL : DiffType.DELETE,
|
|
461
|
-
code
|
|
557
|
+
code: renderedLines[j][0]!,
|
|
462
558
|
delNum,
|
|
463
559
|
})
|
|
464
560
|
}
|
|
465
561
|
|
|
466
562
|
for (let j = 0; j < nextLines.length; j++) {
|
|
467
|
-
const curLine = curLines[j]
|
|
468
563
|
const nextLine = nextLines[j]
|
|
469
564
|
addNum++
|
|
470
565
|
|
|
471
|
-
const code = getHighlightCode(language, renderWords(curLine, nextLine, diffStyle))
|
|
472
566
|
rawChanges.push({
|
|
473
567
|
type: ignoreRegex?.test(nextLine) ? DiffType.EQUAL : DiffType.ADD,
|
|
474
|
-
code
|
|
568
|
+
code: renderedLines[j][1]!,
|
|
475
569
|
addNum,
|
|
476
570
|
})
|
|
477
571
|
}
|
|
@@ -483,8 +577,7 @@ export function createUnifiedDiff(
|
|
|
483
577
|
for (const line of curLines) {
|
|
484
578
|
delNum++
|
|
485
579
|
|
|
486
|
-
|
|
487
|
-
rawChanges.push({ type: DiffType.DELETE, code, delNum })
|
|
580
|
+
rawChanges.push({ type: DiffType.DELETE, code: line, delNum })
|
|
488
581
|
}
|
|
489
582
|
}
|
|
490
583
|
}
|
|
@@ -492,9 +585,7 @@ export function createUnifiedDiff(
|
|
|
492
585
|
if (curType === DiffType.ADD) {
|
|
493
586
|
for (const line of curLines) {
|
|
494
587
|
addNum++
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
rawChanges.push({ type: DiffType.ADD, code, addNum })
|
|
588
|
+
rawChanges.push({ type: DiffType.ADD, code: line, addNum })
|
|
498
589
|
}
|
|
499
590
|
}
|
|
500
591
|
}
|
|
@@ -513,6 +604,7 @@ export function createUnifiedDiff(
|
|
|
513
604
|
if (oldString === newString) {
|
|
514
605
|
for (let i = 0; i < rawChanges.length; i++)
|
|
515
606
|
rawChanges[i].fold = false
|
|
607
|
+
rawChanges.slice(0, RENDER_BATCH_SIZE).forEach(line => highlightUnifiedLine(line, language))
|
|
516
608
|
|
|
517
609
|
return result
|
|
518
610
|
}
|
|
@@ -551,6 +643,7 @@ export function createUnifiedDiff(
|
|
|
551
643
|
unchanges = []
|
|
552
644
|
}
|
|
553
645
|
result.changes = processedChanges
|
|
646
|
+
result.changes.filter(line => !line.hide).slice(0, RENDER_BATCH_SIZE).forEach(line => highlightUnifiedLine(line, language))
|
|
554
647
|
|
|
555
648
|
return result
|
|
556
649
|
}
|