v-code-diff 1.1.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "v-code-diff",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "template component for vue-demi, can dev and build",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.es.js",
@@ -23,9 +23,11 @@
23
23
  "build:2.7": "vue-demi-switch 2.7 vue2 && pnpm --filter vue2.7-playground build",
24
24
  "build:3": "vue-demi-switch 3 vue3 && pnpm --filter vue3-playground build",
25
25
  "clean": "rimraf ./dist",
26
+ "deploy:demo": "pnpm --filter demo gh-pages",
26
27
  "dev:2": "vue-demi-switch 2 vue2 && pnpm --filter vue2-playground dev",
27
28
  "dev:2.7": "vue-demi-switch 2.7 vue2 && pnpm --filter vue2.7-playground dev",
28
29
  "dev:3": "vue-demi-switch 3 vue3 && pnpm --filter vue3-playground dev",
30
+ "dev:demo": "vue-demi-switch 3 vue3 && pnpm --filter demo dev",
29
31
  "lint:fix": "eslint . --ext .vue,.js,.jsx,.ts,.tsx --fix --ignore-path .gitignore",
30
32
  "postinstall": "node scripts/postinstall.js",
31
33
  "prepublishOnly": "npm run build",
@@ -51,12 +53,14 @@
51
53
  },
52
54
  "dependencies": {
53
55
  "diff": "^5.0.0",
56
+ "diff-match-patch": "^1.0.5",
54
57
  "highlight.js": "^11.6.0",
55
58
  "vue-demi": "^0.13.11"
56
59
  },
57
60
  "devDependencies": {
58
61
  "@antfu/eslint-config": "^0.34.0",
59
62
  "@types/diff": "^5.0.0",
63
+ "@types/diff-match-patch": "^1.0.32",
60
64
  "@types/node": "^18.11.18",
61
65
  "bumpp": "^8.2.1",
62
66
  "eslint": "^8.31.0",
@@ -3,7 +3,7 @@ const { switchVersion, loadModule } = require('./utils')
3
3
  const Vue = loadModule('vue')
4
4
 
5
5
  if (!Vue || typeof Vue.version !== 'string')
6
- console.warn('[vue-demi-sfc-component-template] Vue is not found. Please run "npm install vue" to install.')
6
+ console.warn('[v-code-diff] Vue is not found. Please run "npm install vue" to install.')
7
7
 
8
8
  else if (Vue.version.startsWith('2.7.'))
9
9
  switchVersion('2.7')
@@ -15,4 +15,4 @@ else if (Vue.version.startsWith('3.'))
15
15
  switchVersion('3')
16
16
 
17
17
  else
18
- console.warn(`[vue-demi-sfc-component-template] Vue version v${Vue.version} is not suppported.`)
18
+ console.warn(`[v-code-diff] Vue version v${Vue.version} is not suppported.`)
package/src/CodeDiff.vue CHANGED
@@ -17,6 +17,7 @@ interface Props {
17
17
  trim?: boolean
18
18
  noDiffLineFeed?: boolean
19
19
  maxHeight?: number
20
+ filename?: string
20
21
  }
21
22
 
22
23
  const props = withDefaults(defineProps<Props>(), {
@@ -27,6 +28,7 @@ const props = withDefaults(defineProps<Props>(), {
27
28
  trim: false,
28
29
  noDiffLineFeed: false,
29
30
  maxHeight: undefined,
31
+ filename: undefined,
30
32
  })
31
33
 
32
34
  const isUnifiedViewer = computed(() => props.outputFormat === 'line-by-line')
@@ -49,8 +51,17 @@ const diffChange = computed(() =>
49
51
 
50
52
  <template>
51
53
  <div class="code-diff-view" :style="{ maxHeight }">
52
- <UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" />
53
- <SplitViewer v-else :diff-change="diffChange" />
54
+ <div class="file-header">
55
+ <div class="file-info">
56
+ <span class="filename">{{ filename }}</span>
57
+ <span class="diff-stat">
58
+ <span class="diff-stat-added">+{{ diffChange.stat.additionsNum }} additions</span>
59
+ <span class="diff-stat-deleted" style="margin-left: 8px;">-{{ diffChange.stat.deletionsNum }} deletions</span>
60
+ </span>
61
+ </div>
62
+ </div>
63
+ <UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange.changes" />
64
+ <SplitViewer v-else :diff-change="diffChange.changes" />
54
65
  </div>
55
66
  </template>
56
67
 
@@ -17,10 +17,10 @@ const getCodeMarker = (type: DiffType) => {
17
17
 
18
18
  <template>
19
19
  <tr v-if="splitLine.fold">
20
- <td class="blob-num blob-num-empty empty-cell" colspan="1">
20
+ <td class="blob-num blob-num-hunk" colspan="1">
21
21
  >
22
22
  </td>
23
- <td class="blob-code blob-code-empty empty-cell" colspan="3" align="left">
23
+ <td class="blob-code blob-code-inner blob-code-hunk" colspan="3" align="left">
24
24
 
25
25
  </td>
26
26
  </tr>
package/src/style.scss CHANGED
@@ -13,6 +13,35 @@
13
13
 
14
14
  overflow-y: auto;
15
15
 
16
+ .file-header {
17
+ background-color: var(--color-canvas-subtle);
18
+ border-bottom: 1px solid var(--color-border-default);
19
+ padding: 8px 16px;
20
+ font-size: 12px;
21
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
22
+
23
+ .file-info {
24
+ display: flex;
25
+ justify-content: space-between;
26
+ align-items: center;
27
+ margin-left: 8px;
28
+ height: 24px;
29
+
30
+ .filename {
31
+ font-size: 13px;
32
+ color: var(--color-fg-default);
33
+ }
34
+ .diff-stat {
35
+ .diff-stat-added {
36
+ color: var(--color-diffstat-addition-bg)
37
+ }
38
+ .diff-stat-deleted {
39
+ color: var(--color-danger-emphasis)
40
+ }
41
+ }
42
+ }
43
+ }
44
+
16
45
  table {
17
46
  border-spacing: 0;
18
47
  }
@@ -95,6 +124,13 @@
95
124
  padding-right: 8px;
96
125
  content: attr(data-code-marker);
97
126
  }
127
+
128
+ .blob-num-hunk {
129
+ background-color: var(--color-diff-blob-hunk-num-bg);
130
+ }
131
+ .blob-code-hunk {
132
+ background-color: var(--color-accent-subtle);
133
+ }
98
134
  }
99
135
 
100
136
  .file-diff-split {
package/src/types.ts CHANGED
@@ -25,5 +25,16 @@ export interface UnifiedLine {
25
25
  addNum?: number
26
26
  }
27
27
 
28
- export type SplitViewerChange = SplitDiffLine[]
29
- export type UnifiedViewerChange = UnifiedLine[]
28
+ export interface DiffStat {
29
+ additionsNum: number
30
+ deletionsNum: number
31
+ }
32
+ export interface SplitViewerChange {
33
+ changes: SplitDiffLine[]
34
+ stat: DiffStat
35
+ }
36
+
37
+ export interface UnifiedViewerChange {
38
+ changes: UnifiedLine[]
39
+ stat: DiffStat
40
+ }
@@ -17,13 +17,13 @@ const getCodeMarker = (type: DiffType) => {
17
17
 
18
18
  <template>
19
19
  <tr v-if="line.fold">
20
- <td class="blob-num blob-num-empty empty-cell">
20
+ <td class="blob-num blob-num-hunk">
21
21
  >
22
22
  </td>
23
- <td class="blob-num blob-num-empty empty-cell">
23
+ <td class="blob-num blob-num-hunk">
24
24
  >
25
25
  </td>
26
- <td class="blob-code blob-code-empty empty-cell" align="left">
26
+ <td class="blob-code blob-code-hunk" align="left">
27
27
 
28
28
  </td>
29
29
  </tr>
package/src/utils.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as Diff from 'diff'
2
2
  import type { Change } from 'diff'
3
+ import { DIFF_DELETE, DIFF_INSERT, diff_match_patch as DiffMatchPatch } from 'diff-match-patch'
3
4
  import hljs from './highlight'
4
5
  import { DiffType } from './types'
5
- import type { DiffLine, SplitViewerChange, UnifiedViewerChange } from './types'
6
+ import type { DiffLine, DiffStat, SplitDiffLine, SplitViewerChange, UnifiedLine, UnifiedViewerChange } from './types'
6
7
 
7
8
  const MODIFIED_START_TAG = '<code-diff-modified>'
8
9
  const MODIFIED_CLOSE_TAG = '</code-diff-modified>'
@@ -36,6 +37,27 @@ const renderWords = (prev?: string, current?: string, diffStyle = 'word'): strin
36
37
  .join('')
37
38
  }
38
39
 
40
+ function diffLines(prev: string, current: string) {
41
+ const dmp = new DiffMatchPatch()
42
+ const a = dmp.diff_linesToChars_(prev, current)
43
+ const linePrev = a.chars1
44
+ const lineCurrent = a.chars2
45
+ const lineArray = a.lineArray
46
+ const diffs: any[] = dmp.diff_main(linePrev, lineCurrent, false)
47
+ dmp.diff_charsToLines_(diffs, lineArray)
48
+ return diffs.map((x) => {
49
+ const [type, text] = x
50
+ const count = text.trim().split('\n').length
51
+ const change: Diff.Change = {
52
+ count,
53
+ value: text,
54
+ removed: type === DIFF_DELETE,
55
+ added: type === DIFF_INSERT,
56
+ }
57
+ return change
58
+ })
59
+ }
60
+
39
61
  const getHighlightCode = (language: string, code: string) => {
40
62
  const hasModifiedTags = code.match(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g'))
41
63
 
@@ -113,23 +135,42 @@ const getHighlightCode = (language: string, code: string) => {
113
135
  .replace(new RegExp(closeEntity, 'g'), '</span>')
114
136
  }
115
137
 
138
+ function calcDiffStat(changes: Change[]): DiffStat {
139
+ const count = (s: string, c: string) => (s.match(new RegExp(c, 'g')) || []).length
140
+
141
+ let additionsNum = 0
142
+ let deletionsNum = 0
143
+ for (const change of changes) {
144
+ if (change.added)
145
+ additionsNum += count(change.value.trim(), '\n') + 1
146
+
147
+ if (change.removed)
148
+ deletionsNum += count(change.value.trim(), '\n') + 1
149
+ }
150
+ return { additionsNum, deletionsNum }
151
+ }
152
+
116
153
  export function createSplitDiff(
117
154
  oldString: string,
118
155
  newString: string,
119
156
  language = 'plaintext',
120
157
  diffStyle = 'word',
121
158
  context = 10,
122
- ) {
159
+ ): SplitViewerChange {
123
160
  const newEmptySplitDiff = (): DiffLine => ({ type: DiffType.EMPTY })
124
161
  const newSplitDiff = (type: DiffType, num: number, code: string): DiffLine => ({ type, num, code })
125
-
126
- const changes = Diff.diffLines(oldString, newString)
162
+ const changes = diffLines(oldString, newString)
127
163
 
128
164
  let delNum = 0
129
165
  let addNum = 0
130
166
  let skip = false
131
167
 
132
- const result: SplitViewerChange = []
168
+ const rawChanges: SplitDiffLine[] = []
169
+ const result: SplitViewerChange = {
170
+ changes: rawChanges,
171
+ stat: calcDiffStat(changes),
172
+ }
173
+
133
174
  for (let i = 0; i < changes.length; i++) {
134
175
  if (skip) {
135
176
  skip = false
@@ -168,7 +209,7 @@ export function createSplitDiff(
168
209
  right = newSplitDiff(DiffType.ADD, addNum, highlightCode)
169
210
  }
170
211
 
171
- result.push({ left, right })
212
+ rawChanges.push({ left, right })
172
213
  }
173
214
  break
174
215
  }
@@ -181,7 +222,7 @@ export function createSplitDiff(
181
222
  addNum++
182
223
 
183
224
  const highlightCode = getHighlightCode(language, line)
184
- result.push({
225
+ rawChanges.push({
185
226
  left: newSplitDiff(DiffType.EQUAL, delNum, highlightCode),
186
227
  right: newSplitDiff(DiffType.EQUAL, addNum, highlightCode),
187
228
  })
@@ -195,7 +236,7 @@ export function createSplitDiff(
195
236
  for (const line of curLines) {
196
237
  delNum++
197
238
 
198
- result.push({
239
+ rawChanges.push({
199
240
  left: newSplitDiff(DiffType.DELETE, delNum, getHighlightCode(language, line)),
200
241
  right: newEmptySplitDiff(),
201
242
  })
@@ -225,7 +266,7 @@ export function createSplitDiff(
225
266
  ? newSplitDiff(DiffType.ADD, addNum, getHighlightCode(language, rightLine))
226
267
  : newEmptySplitDiff()
227
268
 
228
- result.push({ left, right })
269
+ rawChanges.push({ left, right })
229
270
  }
230
271
  }
231
272
  }
@@ -233,7 +274,7 @@ export function createSplitDiff(
233
274
  if (curType === DiffType.ADD) {
234
275
  for (const line of curLines) {
235
276
  addNum++
236
- result.push({
277
+ rawChanges.push({
237
278
  left: newEmptySplitDiff(),
238
279
  right: newSplitDiff(DiffType.ADD, addNum, getHighlightCode(language, line)),
239
280
  })
@@ -242,37 +283,38 @@ export function createSplitDiff(
242
283
  }
243
284
 
244
285
  if (oldString === newString) {
245
- for (let i = 0; i < result.length; i++)
246
- result[i].fold = false
286
+ for (let i = 0; i < rawChanges.length; i++)
287
+ rawChanges[i].fold = false
247
288
 
248
289
  return result
249
290
  }
250
291
 
251
- for (let i = 0; i < result.length; i++) {
252
- const line = result[i]
292
+ for (let i = 0; i < rawChanges.length; i++) {
293
+ const line = rawChanges[i]
253
294
  if (line.left.type === DiffType.DELETE || line.right.type === DiffType.ADD) {
254
- const [start, end] = [Math.max(i - context, 0), Math.min(i + context + 1, result.length)]
295
+ const [start, end] = [Math.max(i - context, 0), Math.min(i + context + 1, rawChanges.length)]
255
296
  for (let j = start; j < end; j++)
256
- result[j].fold = false
297
+ rawChanges[j].fold = false
257
298
  }
258
299
  if (line.fold === undefined)
259
300
  line.fold = true
260
301
  }
261
302
 
262
- const realResult = []
263
- for (let i = 0; i < result.length; i++) {
264
- const line = result[i]
303
+ const processedChanges = []
304
+ for (let i = 0; i < rawChanges.length; i++) {
305
+ const line = rawChanges[i]
265
306
  if (line.fold === false) {
266
- realResult.push(line)
307
+ processedChanges.push(line)
267
308
  continue
268
309
  }
269
310
  if (line.fold === true) {
270
- if (realResult[realResult.length - 1]?.fold !== true)
271
- realResult.push(line)
311
+ if (processedChanges[processedChanges.length - 1]?.fold !== true)
312
+ processedChanges.push(line)
272
313
  }
273
314
  }
315
+ result.changes = processedChanges
274
316
 
275
- return realResult
317
+ return result
276
318
  }
277
319
 
278
320
  export function createUnifiedDiff(
@@ -280,15 +322,20 @@ export function createUnifiedDiff(
280
322
  newString: string,
281
323
  language = 'plaintext',
282
324
  diffStyle = 'word',
283
- context = 2,
284
- ) {
285
- const changes = Diff.diffLines(oldString, newString)
325
+ context = 10,
326
+ ): UnifiedViewerChange {
327
+ const changes = diffLines(oldString, newString)
286
328
 
287
329
  let delNum = 0
288
330
  let addNum = 0
289
331
  let skip = false
290
332
 
291
- const result: UnifiedViewerChange = []
333
+ const rawChanges: UnifiedLine[] = []
334
+ const result: UnifiedViewerChange = {
335
+ changes: rawChanges,
336
+ stat: calcDiffStat(changes),
337
+ }
338
+
292
339
  for (let i = 0; i < changes.length; i++) {
293
340
  if (skip) {
294
341
  skip = false
@@ -315,7 +362,7 @@ export function createUnifiedDiff(
315
362
 
316
363
  const code = getHighlightCode(language, line)
317
364
 
318
- result.push({
365
+ rawChanges.push({
319
366
  type: curType,
320
367
  code,
321
368
  addNum: curType === DiffType.DELETE ? undefined : addNum,
@@ -333,7 +380,7 @@ export function createUnifiedDiff(
333
380
  addNum++
334
381
  const code = getHighlightCode(language, line)
335
382
 
336
- result.push({ type: DiffType.EQUAL, code, delNum, addNum })
383
+ rawChanges.push({ type: DiffType.EQUAL, code, delNum, addNum })
337
384
  }
338
385
  }
339
386
 
@@ -348,7 +395,7 @@ export function createUnifiedDiff(
348
395
  delNum++
349
396
 
350
397
  const code = getHighlightCode(language, renderWords(nextLine, curLine, diffStyle))
351
- result.push({ type: DiffType.DELETE, code, delNum })
398
+ rawChanges.push({ type: DiffType.DELETE, code, delNum })
352
399
  }
353
400
 
354
401
  for (let j = 0; j < nextLines.length; j++) {
@@ -357,7 +404,7 @@ export function createUnifiedDiff(
357
404
  addNum++
358
405
 
359
406
  const code = getHighlightCode(language, renderWords(curLine, nextLine, diffStyle))
360
- result.push({ type: DiffType.ADD, code, addNum })
407
+ rawChanges.push({ type: DiffType.ADD, code, addNum })
361
408
  }
362
409
 
363
410
  skip = true
@@ -368,7 +415,7 @@ export function createUnifiedDiff(
368
415
  delNum++
369
416
 
370
417
  const code = getHighlightCode(language, line)
371
- result.push({ type: DiffType.DELETE, code, delNum })
418
+ rawChanges.push({ type: DiffType.DELETE, code, delNum })
372
419
  }
373
420
  }
374
421
  }
@@ -378,41 +425,42 @@ export function createUnifiedDiff(
378
425
  addNum++
379
426
  const code = getHighlightCode(language, line)
380
427
 
381
- result.push({ type: DiffType.ADD, code, addNum })
428
+ rawChanges.push({ type: DiffType.ADD, code, addNum })
382
429
  }
383
430
  }
384
431
  }
385
432
 
386
- for (let i = 0; i < result.length; i++) {
387
- const line = result[i]
433
+ for (let i = 0; i < rawChanges.length; i++) {
434
+ const line = rawChanges[i]
388
435
  if (line.type === DiffType.DELETE || line.type === DiffType.ADD) {
389
- const [start, end] = [Math.max(i - context, 0), Math.min(i + context + 1, result.length)]
436
+ const [start, end] = [Math.max(i - context, 0), Math.min(i + context + 1, rawChanges.length)]
390
437
  for (let j = start; j < end; j++)
391
- result[j].fold = false
438
+ rawChanges[j].fold = false
392
439
  }
393
440
  if (line.fold === undefined)
394
441
  line.fold = true
395
442
  }
396
443
 
397
444
  if (oldString === newString) {
398
- for (let i = 0; i < result.length; i++)
399
- result[i].fold = false
445
+ for (let i = 0; i < rawChanges.length; i++)
446
+ rawChanges[i].fold = false
400
447
 
401
448
  return result
402
449
  }
403
450
 
404
- const realResult = []
405
- for (let i = 0; i < result.length; i++) {
406
- const line = result[i]
451
+ const processedChanges = []
452
+ for (let i = 0; i < rawChanges.length; i++) {
453
+ const line = rawChanges[i]
407
454
  if (line.fold === false) {
408
- realResult.push(line)
455
+ processedChanges.push(line)
409
456
  continue
410
457
  }
411
458
  if (line.fold === true) {
412
- if (i === 0 || realResult[realResult.length - 1]?.fold !== true)
413
- realResult.push(line)
459
+ if (i === 0 || processedChanges[processedChanges.length - 1]?.fold !== true)
460
+ processedChanges.push(line)
414
461
  }
415
462
  }
463
+ result.changes = processedChanges
416
464
 
417
- return realResult
465
+ return result
418
466
  }