1
- import * as Diff from 'diff'
1
+ import { diff_match_patch as DiffMatchPatch } from 'diff-match-patch '
2
2
import hljs from './highlight'
3
3
4
4
import type { Ref } from 'vue'
5
- import type { Change } from 'diff'
5
+ import type { Diff } from 'diff-match-patch '
6
6
7
7
type Mode = 'split' | 'unified'
8
8
type Theme = 'dark' | 'light' | 'custom'
9
9
type Role = 'prev' | 'current' | 'unified'
10
10
11
+ enum Type {
12
+ removed = - 1 ,
13
+ equal = 0 ,
14
+ added = 1 ,
15
+ disabled = 2
16
+ }
17
+
11
18
interface Line {
12
- type : 'added' | 'removed' | 'equal' | 'disabled' ;
19
+ type : string ;
13
20
lineNum ?: number ;
14
21
value ?: string ;
15
22
chkWords ?: boolean ;
16
23
}
17
24
18
25
type Lines = Array < Line >
19
- type Diffs = Array < Change >
26
+ type Diffs = Array < Diff >
20
27
21
28
const MODIFIED_START_TAG = '<vue-diff-modified>'
22
29
const MODIFIED_CLOSE_TAG = '</vue-diff-modified>'
@@ -25,9 +32,9 @@ const MODIFIED_CLOSE_TAG = '</vue-diff-modified>'
25
32
* Get diff type
26
33
* @param diff
27
34
*/
28
- const getDiffType = ( diff : Change ) => {
29
- if ( ! diff . count ) return 'disabled'
30
- return diff . added ? 'added' : diff . removed ? 'removed' : 'equal'
35
+ const getDiffType = ( type : Type ) => {
36
+ if ( ! Type [ type ] ) return 'disabled'
37
+ return Type [ type ]
31
38
}
32
39
33
40
/**
@@ -42,28 +49,28 @@ const getSplitLines = (diffsMap: Array<Diffs>): Array<Lines> => {
42
49
}
43
50
44
51
diffsMap . map ( ( diffs ) => {
45
- const prevLines = diffs [ 0 ] . value . replace ( / \n $ / , '' ) . split ( '\n' )
46
- const currentLines = diffs [ 1 ] . value . replace ( / \n $ / , '' ) . split ( '\n' )
52
+ const prevLines = diffs [ 0 ] [ 1 ] . replace ( / \n $ / , '' ) . split ( '\n' )
53
+ const currentLines = diffs [ 1 ] [ 1 ] . replace ( / \n $ / , '' ) . split ( '\n' )
47
54
const loopCount = Math . max ( prevLines . length , currentLines . length )
48
55
49
56
for ( let i = 0 ; i < loopCount ; i ++ ) {
50
- const hasPrevLine = getDiffType ( diffs [ 0 ] ) !== 'disabled'
51
- const hasCurrentLine = getDiffType ( diffs [ 1 ] ) !== 'disabled'
57
+ const hasPrevLine = getDiffType ( diffs [ 0 ] [ 0 ] ) !== 'disabled' && typeof prevLines [ i ] !== 'undefined '
58
+ const hasCurrentLine = getDiffType ( diffs [ 1 ] [ 0 ] ) !== 'disabled' && typeof currentLines [ i ] !== 'undefined '
52
59
53
60
if ( hasPrevLine ) lineNum . prev = lineNum . prev + 1
54
61
if ( hasCurrentLine ) lineNum . current = lineNum . current + 1
55
62
56
- const chkWords = Boolean ( diffs [ 0 ] . count === diffs [ 1 ] . count && getDiffType ( diffs [ 0 ] ) . match ( / a d d e d | r e m o v e d / ) && getDiffType ( diffs [ 1 ] ) . match ( / a d d e d | r e m o v e d / ) )
63
+ const chkWords = Boolean ( getDiffType ( diffs [ 0 ] [ 0 ] ) . match ( / a d d e d | r e m o v e d / ) && getDiffType ( diffs [ 1 ] [ 0 ] ) . match ( / a d d e d | r e m o v e d / ) )
57
64
58
65
result . push ( [
59
66
{
60
- type : getDiffType ( diffs [ 0 ] ) ,
67
+ type : hasPrevLine ? getDiffType ( diffs [ 0 ] [ 0 ] ) : 'disabled' ,
61
68
lineNum : hasPrevLine ? lineNum . prev : undefined ,
62
69
value : hasPrevLine ? prevLines [ i ] : undefined ,
63
70
chkWords
64
71
} ,
65
72
{
66
- type : getDiffType ( diffs [ 1 ] ) ,
73
+ type : hasCurrentLine ? getDiffType ( diffs [ 1 ] [ 0 ] ) : 'disabled' ,
67
74
lineNum : hasCurrentLine ? lineNum . current : undefined ,
68
75
value : hasCurrentLine ? currentLines [ i ] : undefined ,
69
76
chkWords
@@ -84,33 +91,33 @@ const getUnifiedLines = (diffsMap: Array<Diffs>): Array<Lines> => {
84
91
let lineNum = 0
85
92
86
93
diffsMap . map ( ( diffs ) => {
87
- const prevLines = diffs [ 0 ] . value . replace ( / \n $ / , '' ) . split ( '\n' )
88
- const currentLines = diffs [ 1 ] . value . replace ( / \n $ / , '' ) . split ( '\n' )
94
+ const prevLines = diffs [ 0 ] [ 1 ] . replace ( / \n $ / , '' ) . split ( '\n' )
95
+ const currentLines = diffs [ 1 ] [ 1 ] . replace ( / \n $ / , '' ) . split ( '\n' )
89
96
90
97
prevLines . map ( value => {
91
- const type = getDiffType ( diffs [ 0 ] )
98
+ const type = getDiffType ( diffs [ 0 ] [ 0 ] )
92
99
93
100
if ( type !== 'removed' ) return
94
101
95
102
result . push ( [
96
103
{
97
- type : getDiffType ( diffs [ 0 ] ) ,
104
+ type : getDiffType ( diffs [ 0 ] [ 0 ] ) ,
98
105
lineNum : undefined ,
99
106
value : value
100
107
}
101
108
] )
102
109
} )
103
110
104
111
currentLines . map ( value => {
105
- const type = getDiffType ( diffs [ 1 ] )
112
+ const type = getDiffType ( diffs [ 1 ] [ 0 ] )
106
113
107
114
if ( type === 'disabled' ) return
108
115
109
116
lineNum = lineNum + 1
110
117
111
118
result . push ( [
112
119
{
113
- type : getDiffType ( diffs [ 1 ] ) ,
120
+ type : getDiffType ( diffs [ 1 ] [ 0 ] ) ,
114
121
lineNum,
115
122
value : value
116
123
}
@@ -128,11 +135,22 @@ const getUnifiedLines = (diffsMap: Array<Diffs>): Array<Lines> => {
128
135
* @param current
129
136
*/
130
137
const renderLines = ( mode : Mode , prev : string , current : string ) : Array < Lines > => {
138
+ function diffLines ( prev : string , current : string ) {
139
+ const dmp = new DiffMatchPatch ( )
140
+ const a = dmp . diff_linesToChars_ ( prev , current )
141
+ const linePrev = a . chars1
142
+ const lineCurrent = a . chars2
143
+ const lineArray = a . lineArray
144
+ const diffs = dmp . diff_main ( linePrev , lineCurrent , false )
145
+ dmp . diff_charsToLines_ ( diffs , lineArray )
146
+ return diffs
147
+ }
148
+
131
149
/**
132
150
* stacked prev, current data
133
151
*/
134
- const diffsMap = Diff . diffLines ( prev , current ) . reduce ( ( acc : Array < Diffs > , curr ) => {
135
- const type = getDiffType ( curr )
152
+ const diffsMap = diffLines ( prev , current ) . reduce ( ( acc : Array < Diffs > , curr ) => {
153
+ const type = getDiffType ( curr [ 0 ] )
136
154
137
155
if ( type === 'equal' ) {
138
156
acc . push ( [ curr ] ) // Push index 0
@@ -143,7 +161,8 @@ const renderLines = (mode: Mode, prev: string, current: string): Array<Lines> =>
143
161
}
144
162
145
163
if ( type === 'added' ) {
146
- if ( acc . length && acc [ acc . length - 1 ] [ 0 ] && acc [ acc . length - 1 ] [ 0 ] . removed ) {
164
+ const prev = acc . length && acc [ acc . length - 1 ] [ 0 ] ? acc [ acc . length - 1 ] [ 0 ] : null
165
+ if ( prev && getDiffType ( prev [ 0 ] ) === 'removed' ) {
147
166
acc [ acc . length - 1 ] . push ( curr ) // Push index 1 if index 0 has removed data in last array
148
167
} else {
149
168
acc . push ( [ curr ] ) // Push index 0
@@ -159,14 +178,14 @@ const renderLines = (mode: Mode, prev: string, current: string): Array<Lines> =>
159
178
diffsMap . map ( ( diffs ) => {
160
179
if ( diffs . length > 1 ) return // Return if has index 0, 1
161
180
162
- const type = getDiffType ( diffs [ 0 ] )
181
+ const type = getDiffType ( diffs [ 0 ] [ 0 ] )
163
182
164
183
if ( type === 'added' ) {
165
- diffs . unshift ( { value : '' } ) // Set empty data
184
+ diffs . unshift ( [ 2 , '' ] ) // Set empty data
166
185
} else if ( type === 'removed' ) {
167
- diffs . push ( { value : '' } ) // Set empty data
186
+ diffs . push ( [ 2 , '' ] ) // Set empty data
168
187
} else if ( type === 'equal' ) {
169
- diffs . push ( { ...diffs [ 0 ] } ) // Set same data
188
+ diffs . push ( [ ...diffs [ 0 ] ] ) // Set same data
170
189
}
171
190
} )
172
191
@@ -191,8 +210,11 @@ const renderWords = (prev: string, current: string) => {
191
210
/**
192
211
* Set modified tags in changed words (removed -> added)
193
212
*/
194
- return Diff . diffWords ( prev , current ) . filter ( word => getDiffType ( word ) !== 'removed' ) . map ( word => {
195
- return getDiffType ( word ) === 'added' ? `${ MODIFIED_START_TAG } ${ word . value } ${ MODIFIED_CLOSE_TAG } ` : word . value
213
+ const dmp = new DiffMatchPatch ( )
214
+ const diff = dmp . diff_main ( prev , current )
215
+ dmp . diff_cleanupSemantic ( diff )
216
+ return diff . filter ( result => getDiffType ( result [ 0 ] ) !== 'removed' ) . map ( result => {
217
+ return getDiffType ( result [ 0 ] ) === 'added' ? `${ MODIFIED_START_TAG } ${ result [ 1 ] } ${ MODIFIED_CLOSE_TAG } ` : result [ 1 ]
196
218
} ) . join ( '' )
197
219
}
198
220
@@ -269,4 +291,4 @@ const setHighlightCode = ({ highlightCode, language, code }: { highlightCode: Re
269
291
}
270
292
271
293
export { MODIFIED_START_TAG , MODIFIED_CLOSE_TAG , getDiffType , getSplitLines , getUnifiedLines , renderLines , renderWords , setHighlightCode }
272
- export type { Mode , Theme , Role , Change , Lines , Line }
294
+ export type { Mode , Theme , Role , Lines , Line }
0 commit comments