Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit ce2fa28

Browse files
committed
chore: development
1 parent 3cf101e commit ce2fa28

File tree

4 files changed

+146
-214
lines changed

4 files changed

+146
-214
lines changed

src/Code.vue

Lines changed: 80 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,86 @@
11
<template>
22
<pre :class="`language-${language}`"><code
33
:class="`language-${language}`"
4-
v-html="code"
4+
v-html="highlightCode"
55
></code></pre>
66
</template>
77

88
<script lang="ts">
99
import hljs from 'highlight.js'
10-
import { defineComponent, ref, onMounted, watch } from 'vue'
10+
import { defineComponent, ref, onMounted, watch, Ref } from 'vue'
1111
import { MODIFIED_START_TAG, MODIFIED_CLOSE_TAG } from './utils'
1212
import 'highlight.js/styles/monokai.css'
1313
14-
hljs.configure({
15-
noHighlightRe: new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g')
16-
})
14+
/**
15+
* Set hightlight code
16+
* This function must calling at client only (use DOM)
17+
*/
18+
const setHighlightCode = ({ highlightCode, language, code }: { highlightCode: Ref; language: string; code: string }) => {
19+
const hasModifiedTags = code.match(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g'))
20+
21+
if (!hasModifiedTags) {
22+
highlightCode.value = hljs.highlight(language, code).value
23+
return
24+
}
25+
26+
/**
27+
* Explore highlight DOM extracted from pure code and compare the text with the original code code to generate the highlight code
28+
*/
29+
let originalCode = code // original code with modified tags
30+
const pureCode = code.replace(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g'), '') // Without modified tags
31+
let pureElement = document.createElement('div')
32+
pureElement.innerHTML = hljs.highlight(language, pureCode).value // Highlight DOM without modified tags
33+
34+
const diffElements = (node: HTMLElement) => {
35+
node.childNodes.forEach(child => {
36+
if (child.nodeType === 1) {
37+
diffElements(child as HTMLElement)
38+
}
39+
40+
// Compare text nodes and check changed text
41+
if (child.nodeType === 3) {
42+
if (!child.textContent) return
43+
44+
let oldContent = child.textContent
45+
let newContent = ''
46+
47+
while (oldContent.length) {
48+
if (originalCode.startsWith(MODIFIED_START_TAG)) { // Add modified start tag
49+
originalCode = originalCode.slice(MODIFIED_START_TAG.length)
50+
newContent = newContent + MODIFIED_START_TAG
51+
continue
52+
}
53+
if (originalCode.startsWith(MODIFIED_CLOSE_TAG)) { // Add modified close tag
54+
originalCode = originalCode.slice(MODIFIED_CLOSE_TAG.length)
55+
newContent = newContent + MODIFIED_CLOSE_TAG
56+
continue
57+
}
58+
59+
// Add words before modified tag
60+
const hasModifiedTag = originalCode.match(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`))
61+
const originalCodeDiffLength = hasModifiedTag && hasModifiedTag.index ? hasModifiedTag.index : originalCode.length
62+
const nextDiffsLength = Math.min(originalCodeDiffLength, oldContent.length)
63+
64+
newContent = newContent + originalCode.substring(0, nextDiffsLength)
65+
originalCode = originalCode.slice(nextDiffsLength)
66+
oldContent = oldContent.slice(nextDiffsLength)
67+
}
68+
69+
child.textContent = newContent // put as entity code because change textContent
70+
}
71+
})
72+
}
73+
diffElements(pureElement)
1774
18-
function escapeHTML (value: string) {
19-
return value
20-
// .replace(/&/g, '&amp;')
21-
// .replace(/</g, '&lt;')
22-
// .replace(/>/g, '&gt;')
23-
// .replace(/"/g, '&quot;')
24-
// .replace(/'/g, '&#x27;')
75+
const startEntity = MODIFIED_START_TAG.replace('<', '&lt;').replace('>', '&gt;')
76+
const closeEntity = MODIFIED_CLOSE_TAG.replace('<', '&lt;').replace('>', '&gt;')
77+
78+
highlightCode.value = pureElement.innerHTML
79+
.replace(new RegExp(startEntity, 'g'), '<span class="modified">')
80+
.replace(new RegExp(closeEntity, 'g'), '</span>')
81+
82+
// @ts-ignore
83+
pureElement = null
2584
}
2685
2786
export default defineComponent({
@@ -30,80 +89,26 @@ export default defineComponent({
3089
type: String,
3190
required: true
3291
},
33-
value: {
92+
code: {
3493
type: String,
3594
required: true
3695
}
3796
},
3897
setup (props) {
39-
const code = ref('')
98+
const highlightCode = ref('')
4099
41100
onMounted(() => {
42-
watch(() => props.value, () => {
43-
const hasModifyTags = props.value.match(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g'))
44-
45-
if (!hasModifyTags) {
46-
const { value: highlightCode } = hljs.highlight(props.language, props.value)
47-
code.value = highlightCode
48-
return
49-
}
50-
51-
let highlightCode = ''
52-
const pureValue = props.value.replace(new RegExp(`(${MODIFIED_START_TAG}|${MODIFIED_CLOSE_TAG})`, 'g'), '') // without modify tag
53-
54-
const pureHighlightCode = hljs.highlight(props.language, pureValue).value
55-
let plainCode = escapeHTML(props.value)
56-
highlightCode = plainCode
57-
58-
const pureEl = document.createElement('pre')
59-
pureEl.innerHTML = pureHighlightCode
60-
const plainEl = document.createElement('div')
61-
plainEl.innerHTML = plainCode
62-
63-
function diff (node: HTMLElement) {
64-
node.childNodes.forEach(child => {
65-
if (child.nodeType === 1) {
66-
diff(child as HTMLElement)
67-
}
68-
if (child.nodeType === 3) {
69-
if (!child.textContent) return
70-
71-
let text = escapeHTML(child.textContent)
72-
73-
let code = ''
74-
75-
while (text.length) {
76-
if (plainCode.startsWith(MODIFIED_START_TAG)) {
77-
plainCode = plainCode.slice(MODIFIED_START_TAG.length)
78-
code = code + MODIFIED_START_TAG
79-
continue
80-
}
81-
if (plainCode.startsWith(MODIFIED_CLOSE_TAG)) {
82-
plainCode = plainCode.slice(MODIFIED_CLOSE_TAG.length)
83-
code = code + MODIFIED_CLOSE_TAG
84-
continue
85-
}
86-
87-
code = code + text[0]
88-
plainCode = plainCode.slice(1)
89-
text = text.slice(1)
90-
}
91-
92-
child.textContent = code
93-
}
94-
})
95-
}
96-
diff(pureEl)
97-
98-
const startEntity = MODIFIED_START_TAG.replace('<', '&lt;').replace('>', '&gt;')
99-
const closeEntity = MODIFIED_CLOSE_TAG.replace('<', '&lt;').replace('>', '&gt;')
100-
101-
code.value = pureEl.innerHTML.replace(new RegExp(startEntity, 'g'), '<span class="modified">').replace(new RegExp(closeEntity, 'g'), '</span>')
101+
watch(() => props.code, () => {
102+
setHighlightCode({
103+
highlightCode,
104+
language: props.language,
105+
code: props.code
106+
})
102107
}, { immediate: true })
103108
})
104109
105110
return {
106-
code
111+
highlightCode
107112
}
108113
}
109114
})

src/Line.vue

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<template>
2+
<!-- split view -->
23
<tr v-if="mode === 'split'" :class="`vue-diff-row-${mode}`">
34
<template :key="index" v-for="(line, index) in data">
45
<td class="lineNum" :class="`vue-diff-cell-${line.type}`">
@@ -7,11 +8,13 @@
78
<td class="code" :class="`vue-diff-cell-${line.type}`">
89
<Code
910
:language="language"
10-
:value="getValue(line, data, index)"
11+
:code="setCode(line, data, index)"
1112
/>
1213
</td>
1314
</template>
1415
</tr>
16+
<!-- // split view -->
17+
<!-- unified view -->
1518
<template v-if="mode === 'unified'">
1619
<tr :class="`vue-diff-row-${mode}`">
1720
<td class="lineNum" :class="`vue-diff-cell-${data[0].type}`">
@@ -20,11 +23,12 @@
2023
<td class="code" :class="`vue-diff-cell-${data[0].type}`">
2124
<Code
2225
:language="language"
23-
:value="getValue(line)"
26+
:code="setCode(line)"
2427
/>
2528
</td>
2629
</tr>
2730
</template>
31+
<!-- // unified view -->
2832
</template>
2933

3034
<script lang="ts">
@@ -53,21 +57,22 @@ export default defineComponent({
5357
}
5458
},
5559
setup () {
56-
const getValue = (line: Line, data?: Lines, index?: number) => {
60+
const setCode = (line: Line, data?: Lines, index?: number) => {
5761
if (!line.value) return '\n'
5862
59-
if (!data || !line.type.match(/added|removed/)) return line.value
63+
// Compare lines when data, index properties exist and has chkWords value in line property
64+
if (typeof data === 'undefined' || typeof index === 'undefined' || !line.chkWords) {
65+
return line.value
66+
}
6067
61-
// return diff words code
62-
const diffIndex = index === 0 ? 1 : 0
63-
const diffValue = data[diffIndex].value
68+
const differ = data[index === 0 ? 1 : 0]
6469
65-
if (!diffValue) return line.value
70+
if (!differ.value) return line.value
6671
67-
return renderWords(diffValue, line.value)
72+
return renderWords(differ.value, line.value) // render with modified tags
6873
}
6974
70-
return { getValue }
75+
return { setCode }
7176
}
7277
})
7378
</script>

src/Viewer.vue

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)