v-code-diff 1.13.1 → 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 +23 -5
- package/dist/v2/index.cjs +26 -0
- package/dist/v2/index.es.js +1727 -1604
- package/dist/v2/index.umd.js +26 -19
- package/dist/v2.7/index.cjs +26 -0
- package/dist/v2.7/index.es.js +1487 -1420
- package/dist/v2.7/index.umd.js +26 -19
- package/dist/v3/index.cjs +26 -0
- package/dist/v3/index.es.js +1905 -1808
- package/dist/v3/index.umd.js +26 -19
- package/package.json +25 -14
- package/scripts/perf-test.ts +40 -0
- package/scripts/postinstall.cjs +15 -18
- package/scripts/utils.cjs +4 -19
- package/src/CodeDiff.vue +11 -9
- package/src/split/SplitLine.vue +9 -5
- package/src/split/SplitViewer.vue +34 -2
- package/src/style.scss +18 -0
- package/src/types.ts +2 -0
- package/src/unified/UnifiedLine.vue +1 -1
- package/src/unified/UnifiedViewer.vue +34 -2
- package/src/utils.ts +164 -54
- package/dist/v2/index.cjs.js +0 -19
- package/dist/v2.7/index.cjs.js +0 -19
- package/dist/v3/index.cjs.js +0 -19
package/package.json
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "v-code-diff",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"
|
|
4
|
+
"version": "1.14.0",
|
|
5
|
+
"packageManager": "pnpm@11.2.2",
|
|
6
|
+
"description": "A code diff viewer for Vue 2.6, Vue 2.7, and Vue 3",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"types": "./types/index.d.ts",
|
|
10
11
|
"import": "./dist/index.es.js",
|
|
11
|
-
"require": "./dist/index.cjs
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./vue2": {
|
|
15
|
+
"types": "./types/index.d.ts",
|
|
16
|
+
"import": "./dist/v2/index.es.js",
|
|
17
|
+
"require": "./dist/v2/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./vue2.7": {
|
|
20
|
+
"types": "./types/index.d.ts",
|
|
21
|
+
"import": "./dist/v2.7/index.es.js",
|
|
22
|
+
"require": "./dist/v2.7/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./vue3": {
|
|
25
|
+
"types": "./types/index.d.ts",
|
|
26
|
+
"import": "./dist/v3/index.es.js",
|
|
27
|
+
"require": "./dist/v3/index.cjs"
|
|
12
28
|
}
|
|
13
29
|
},
|
|
14
30
|
"main": "dist/index.cjs",
|
|
@@ -21,6 +37,7 @@
|
|
|
21
37
|
"types"
|
|
22
38
|
],
|
|
23
39
|
"scripts": {
|
|
40
|
+
"benchmark": "pnpm build:3 && vitest bench --run && node --expose-gc benchmarks/render.mjs",
|
|
24
41
|
"build": "npm run clean && run-s build:**",
|
|
25
42
|
"build:2": "vue-demi-switch 2 vue2 && pnpm --filter vue2-playground build",
|
|
26
43
|
"build:2:umd": "vue-demi-switch 2 vue2 && format=umd pnpm --filter vue2-playground build",
|
|
@@ -38,7 +55,8 @@
|
|
|
38
55
|
"lint:fix": "eslint . --ext .vue,.js,.jsx,.ts,.tsx,json --fix --ignore-path .gitignore",
|
|
39
56
|
"postinstall": "node scripts/postinstall.cjs",
|
|
40
57
|
"prepublishOnly": "npm run build",
|
|
41
|
-
"release": "bumpp --commit --no-push --tag && npm publish"
|
|
58
|
+
"release": "bumpp --commit --no-push --tag && npm publish",
|
|
59
|
+
"test": "vitest run"
|
|
42
60
|
},
|
|
43
61
|
"peerDependencies": {
|
|
44
62
|
"@vue/composition-api": "^1.4.9",
|
|
@@ -50,7 +68,7 @@
|
|
|
50
68
|
}
|
|
51
69
|
},
|
|
52
70
|
"dependencies": {
|
|
53
|
-
"diff": "^5.2.
|
|
71
|
+
"diff": "^5.2.2",
|
|
54
72
|
"diff-match-patch": "^1.0.5",
|
|
55
73
|
"highlight.js": "^11.10.0",
|
|
56
74
|
"vue-demi": "^0.14.6"
|
|
@@ -67,22 +85,15 @@
|
|
|
67
85
|
"npm-run-all": "^4.1.5",
|
|
68
86
|
"rimraf": "^3.0.2",
|
|
69
87
|
"sass": "^1.69.5",
|
|
88
|
+
"tsx": "^4.19.4",
|
|
70
89
|
"typescript": "~4.7.4",
|
|
71
90
|
"vite": "^5.4.2",
|
|
72
91
|
"vite-plugin-css-injected-by-js": "^2.4.0",
|
|
92
|
+
"vitest": "^2.1.9",
|
|
73
93
|
"vue": "^3.4.38",
|
|
74
94
|
"vue-i18n": "^9.14.0",
|
|
75
95
|
"vue-tsc": "^0.40.13",
|
|
76
96
|
"vue2": "npm:vue@2",
|
|
77
97
|
"vue3": "npm:vue@3"
|
|
78
|
-
},
|
|
79
|
-
"pnpm": {
|
|
80
|
-
"packageExtensions": {
|
|
81
|
-
"vue-template-compiler": {
|
|
82
|
-
"peerDependencies": {
|
|
83
|
-
"vue": "~2.6.14"
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
98
|
}
|
|
88
99
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { createSplitDiff } from '../src/utils'
|
|
5
|
+
|
|
6
|
+
// 获取当前文件的 URL 和目录路径
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = path.dirname(__filename)
|
|
9
|
+
|
|
10
|
+
// 构建文件的绝对路径
|
|
11
|
+
const oldTextPath = path.join(__dirname, '../demo/text/old-long-text.txt')
|
|
12
|
+
const newTextPath = path.join(__dirname, '../demo/text/new-long-text.txt')
|
|
13
|
+
|
|
14
|
+
// 同步读取文件内容
|
|
15
|
+
const oldLongText = fs.readFileSync(oldTextPath, 'utf-8')
|
|
16
|
+
const newLongText = fs.readFileSync(newTextPath, 'utf-8')
|
|
17
|
+
|
|
18
|
+
console.log('开始执行性能测试...') // 更新日志消息以匹配函数名
|
|
19
|
+
|
|
20
|
+
const label = 'createSplitDiff execution time' // 更新标签以匹配函数名
|
|
21
|
+
|
|
22
|
+
// 开始计时
|
|
23
|
+
console.time(label)
|
|
24
|
+
|
|
25
|
+
// 执行函数
|
|
26
|
+
try {
|
|
27
|
+
const _result = createSplitDiff(oldLongText, newLongText) // 将 result 重命名为 _result 以消除未使用变量的警告
|
|
28
|
+
console.log(_result.changes.length)
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('执行 createSplitDiff 时出错:', error) // 更新错误消息
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 结束计时并打印时间
|
|
35
|
+
console.timeEnd(label)
|
|
36
|
+
|
|
37
|
+
console.log('性能测试完成.')
|
|
38
|
+
|
|
39
|
+
// 你可能还需要一个空的 export {} 来让 TypeScript 将其视为一个模块(如果需要)
|
|
40
|
+
// export {};
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const { switchVersion
|
|
1
|
+
const fs = require('node:fs')
|
|
2
|
+
const { switchVersion } = require('./utils.cjs')
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (fs.existsSync('.local'))
|
|
4
|
+
if (fs.existsSync('.local')) {
|
|
7
5
|
console.log('Currently, it is the local development environment, not doing anything.')
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
const { version } = require('vue')
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
else
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
else if (Vue.version.startsWith('3.'))
|
|
19
|
-
switchVersion('3')
|
|
20
|
-
|
|
21
|
-
else console.warn(`[v-code-diff] Vue version v${Vue.version} is not suppported.`)
|
|
10
|
+
if (version.startsWith('2.7.'))
|
|
11
|
+
switchVersion('2.7')
|
|
12
|
+
else if (version.startsWith('2.'))
|
|
13
|
+
switchVersion('2')
|
|
14
|
+
else if (version.startsWith('3.'))
|
|
15
|
+
switchVersion('3')
|
|
16
|
+
else
|
|
17
|
+
throw new Error(`[v-code-diff] Vue version v${version} is not supported.`)
|
|
18
|
+
}
|
package/scripts/utils.cjs
CHANGED
|
@@ -1,36 +1,21 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
1
|
+
const fs = require('node:fs')
|
|
2
|
+
const path = require('node:path')
|
|
3
3
|
|
|
4
4
|
const dir = path.resolve(__dirname, '..', 'dist')
|
|
5
5
|
|
|
6
|
-
function loadModule(name) {
|
|
7
|
-
try {
|
|
8
|
-
return require(name)
|
|
9
|
-
}
|
|
10
|
-
catch (e) {
|
|
11
|
-
return undefined
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
6
|
function copy(name, version, vue) {
|
|
16
7
|
vue = vue || 'vue'
|
|
17
8
|
const src = path.join(dir, `v${version}`, name)
|
|
18
9
|
const dest = path.join(dir, name)
|
|
19
10
|
let content = fs.readFileSync(src, 'utf-8')
|
|
20
11
|
content = content.replace(/'vue'/g, `'${vue}'`)
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
fs.unlinkSync(dest)
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
}
|
|
12
|
+
fs.rmSync(dest, { force: true })
|
|
27
13
|
fs.writeFileSync(dest, content, 'utf-8')
|
|
28
14
|
}
|
|
29
15
|
|
|
30
16
|
function switchVersion(version, vue) {
|
|
31
17
|
copy('index.es.js', version, vue)
|
|
32
|
-
copy('index.cjs
|
|
18
|
+
copy('index.cjs', version, vue)
|
|
33
19
|
}
|
|
34
20
|
|
|
35
|
-
module.exports.loadModule = loadModule
|
|
36
21
|
module.exports.switchVersion = switchVersion
|
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,13 +77,14 @@ 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)
|
|
84
|
+
const root = ref<HTMLElement>()
|
|
84
85
|
|
|
85
86
|
function goToNextDiff() {
|
|
86
|
-
const diffs =
|
|
87
|
+
const diffs = root.value!.querySelectorAll('[data-diff-change]')
|
|
87
88
|
if (currentDiffIndex.value < diffs.length - 1) {
|
|
88
89
|
currentDiffIndex.value++
|
|
89
90
|
updateCurrentDiffHighlight(diffs)
|
|
@@ -91,7 +92,7 @@ function goToNextDiff() {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function goToPrevDiff() {
|
|
94
|
-
const diffs =
|
|
95
|
+
const diffs = root.value!.querySelectorAll('[data-diff-change]')
|
|
95
96
|
if (currentDiffIndex.value > 0) {
|
|
96
97
|
currentDiffIndex.value--
|
|
97
98
|
updateCurrentDiffHighlight(diffs)
|
|
@@ -99,17 +100,18 @@ function goToPrevDiff() {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
function updateCurrentDiffHighlight(diffs: NodeListOf<Element>) {
|
|
102
|
-
|
|
103
|
+
root.value!.querySelectorAll('.current-diff').forEach(diff => diff.classList.remove('current-diff'))
|
|
103
104
|
|
|
104
105
|
const currentDiff = diffs[currentDiffIndex.value]
|
|
105
106
|
|
|
106
107
|
if (currentDiff) {
|
|
107
|
-
currentDiff.classList.add('current-diff')
|
|
108
|
+
currentDiff.querySelectorAll('.blob-code').forEach(diff => diff.classList.add('current-diff'))
|
|
108
109
|
currentDiff.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
watch(() => props, () => {
|
|
114
|
+
currentDiffIndex.value = -1
|
|
113
115
|
diffChange.value = raw.value
|
|
114
116
|
emits('diff', {
|
|
115
117
|
stat: {
|
|
@@ -122,7 +124,7 @@ watch(() => props, () => {
|
|
|
122
124
|
</script>
|
|
123
125
|
|
|
124
126
|
<template>
|
|
125
|
-
<div class="code-diff-view" :theme="theme" :style="{ maxHeight }">
|
|
127
|
+
<div ref="root" class="code-diff-view" :theme="theme" :style="{ maxHeight }">
|
|
126
128
|
<div v-if="!hideHeader" class="file-header">
|
|
127
129
|
<!-- line by line -->
|
|
128
130
|
<div v-if="isUnifiedViewer" class="file-info">
|
|
@@ -167,8 +169,8 @@ watch(() => props, () => {
|
|
|
167
169
|
</span>
|
|
168
170
|
</div>
|
|
169
171
|
</div>
|
|
170
|
-
<UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" />
|
|
171
|
-
<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" />
|
|
172
174
|
</div>
|
|
173
175
|
</template>
|
|
174
176
|
|
package/src/split/SplitLine.vue
CHANGED
|
@@ -16,9 +16,10 @@ function getCodeMarker(type: DiffType) {
|
|
|
16
16
|
return ''
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function onSplitLineMousedown(side: 'left' | 'right') {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
19
|
+
function onSplitLineMousedown(event: MouseEvent, side: 'left' | 'right') {
|
|
20
|
+
const table = (event.currentTarget as HTMLElement).closest('.file-diff-split')!
|
|
21
|
+
const leftElements = table.querySelectorAll('.split-side-left')
|
|
22
|
+
const rightElements = table.querySelectorAll('.split-side-right')
|
|
22
23
|
|
|
23
24
|
for (const el of rightElements)
|
|
24
25
|
el.classList.toggle('no-select', side === 'left')
|
|
@@ -37,7 +38,10 @@ function onSplitLineMousedown(side: 'left' | 'right') {
|
|
|
37
38
|
⋯
|
|
38
39
|
</td>
|
|
39
40
|
</tr>
|
|
40
|
-
<tr
|
|
41
|
+
<tr
|
|
42
|
+
v-else-if="!splitLine.hide"
|
|
43
|
+
:data-diff-change="splitLine.left.type === DiffType.DELETE || splitLine.right.type === DiffType.ADD ? '' : undefined"
|
|
44
|
+
>
|
|
41
45
|
<template v-for="(line, index) in [splitLine.left, splitLine.right]">
|
|
42
46
|
<!-- eslint-disable -->
|
|
43
47
|
<template v-if="line.type === DiffType.EMPTY">
|
|
@@ -66,7 +70,7 @@ function onSplitLineMousedown(side: 'left' | 'right') {
|
|
|
66
70
|
'split-side-left': index === 0,
|
|
67
71
|
'split-side-right': index === 1,
|
|
68
72
|
}"
|
|
69
|
-
@mousedown="onSplitLineMousedown(index === 0 ? 'left' : 'right')"
|
|
73
|
+
@mousedown="onSplitLineMousedown($event, index === 0 ? 'left' : 'right')"
|
|
70
74
|
>
|
|
71
75
|
<span
|
|
72
76
|
class="blob-code-inner blob-code-marker" :data-code-marker="getCodeMarker(line.type)"
|
|
@@ -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>
|