[REF] runbot: extract diff display to own component

Also modernize code of the diff match patch use.
This commit is contained in:
William Braeckman 2025-03-13 10:02:43 +01:00
parent 9097aa4545
commit cc39949464
4 changed files with 102 additions and 62 deletions

View File

@ -0,0 +1,79 @@
import { Component, onWillRender } from '@odoo/owl';
import { diff_match_patch } from "@runbot/libs/diff_match_patch/diff_match_patch";
export class DiffDisplay extends Component {
static template = 'runbot.DiffDisplay';
static props = {
fromValue: { type: String },
toValue: { type: String },
lineFilter: { type: Function },
}
static defaultProps = {
lineFilter: (line) => line.type !== 'kept',
}
setup() {
onWillRender(() => {
this.lines = this.makeLines(this.props.fromValue, this.props.toValue);
});
}
makeLines(oldValue, newValue) {
const diff = this.makeDiff(oldValue, newValue);
const lines = this.prepareForRendering(diff);
return lines;
}
makeDiff(text1, text2) {
const dmp = new diff_match_patch();
const a = dmp.diff_linesToChars_(text1, text2);
const lineText1 = a.chars1;
const lineText2 = a.chars2;
const lineArray = a.lineArray;
const diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
dmp.diff_cleanupSemantic(diffs);
return diffs;
}
prepareForRendering(diffs) {
let preLineCounter = 0;
let postLineCounter = 0;
return diffs.reduce((lines, {0: diff_type, 1: data}) => {
data.split('\n').forEach(line => {
line = line
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
let type, colOne, colTwo;
switch (diff_type) {
case 0: //kept
type = 'kept'
colOne = ''
colTwo = postLineCounter;
preLineCounter++; postLineCounter++;
break;
case -1: //removed
type = 'removed';
colOne = preLineCounter;
colTwo = '-';
preLineCounter++;
break;
case 1: //added
type = 'added';
colOne = '+';
colTwo = postLineCounter;
postLineCounter++;
break;
default:
console.warn('Unknown diff_type', diff_type)
return;
}
lines.push({type, colOne, colTwo, line});
})
return lines
}, []);
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="runbot.DiffDisplay">
<div class="code_diff">
<table>
<tr t-foreach="lines" t-as="line" t-key="line_index" t-if="props.lineFilter(line)">
<td class="col_number" t-out="line.colOne"/>
<td class="col_number" t-out="line.colTwo"/>
<td class="code" t-att-class="line.type" t-out="line.line"/>
</tr>
</table>
</div>
</t>
</templates>

View File

@ -2,6 +2,11 @@
import { patch } from "@web/core/utils/patch";
import { Message } from "@mail/core/common/message";
import { diff_match_patch } from "@runbot/libs/diff_match_patch/diff_match_patch";
import { DiffDisplay } from './diff_display';
patch(Message, {
components: {...Message.components, DiffDisplay},
});
patch(Message.prototype, {
setup() {
@ -13,9 +18,6 @@ patch(Message.prototype, {
const newValue = trackingValue.newValue.value;
return ((oldValue && typeof oldValue=== 'string' && oldValue.includes('\n')) && (newValue && typeof oldValue=== 'string' && newValue.includes('\n')))
},
formatTracking(trackingType, trackingValue) {
return super.formatTracking(trackingType, trackingValue)
},
toggleKept() {
this.kept = !this.kept;
},
@ -29,52 +31,4 @@ patch(Message.prototype, {
navigator.clipboard.writeText(trackingValue.newValue.value);
};
},
lines(trackingValue) {
const oldValue = trackingValue.oldValue.value;
const newValue = trackingValue.newValue.value;
const diff = this.makeDiff(oldValue, newValue);
const lines = this.prepareForRendering(diff);
return lines;
},
makeDiff(text1, text2) {
var dmp = new diff_match_patch();
var a = dmp.diff_linesToChars_(text1, text2);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
dmp.diff_cleanupSemantic(diffs);
return diffs;
},
prepareForRendering(diffs) {
var lines = [];
var pre_line_counter = 0
var post_line_counter = 0
for (var x = 0; x < diffs.length; x++) {
var diff_type = diffs[x][0];
var data = diffs[x][1];
var data_lines = data.split('\n');
for (var line_index in data_lines) {
var line = data_lines[line_index];
line = line.replace(/&/g, '&amp;');
line = line.replace(/</g, '&lt;');
line = line.replace(/>/g, '&gt;');
//text = text.replace(/\n/g, '<br>');
//text = text.replace(/ /g, '&nbsp&nbsp');
if (diff_type == -1) {
lines.push({type:'removed', pre_line_counter: pre_line_counter, post_line_counter: '-', line: line})
pre_line_counter += 1
} else if (diff_type == 0) {
lines.push({type:'kept', pre_line_counter: '', post_line_counter: post_line_counter, line: line})
pre_line_counter += 1
post_line_counter +=1
} else if (diff_type == 1) {
lines.push({type:'added', pre_line_counter: '+', post_line_counter: post_line_counter, line: line})
post_line_counter +=1
}
}
}
return lines;
},
});

View File

@ -10,17 +10,10 @@
<button class="btn btn-sm btn-outline-primary" t-on-click="copyNewToClipboard(trackingValue)">Copy new value to clipboard</button>
</div>
<div class="o-mail-Message-trackingField ms-1 fst-italic text-muted">(<t t-out="trackingValue.changedField"/>)</div>
<div class="code_diff">
<table>
<t t-foreach="lines(trackingValue)" t-as="line" t-key="line_index">
<tr t-if="kept or line.type!=='kept'">
<td class="col_number" t-out="line.pre_line_counter"/>
<td class="col_number" t-out="line.post_line_counter"/>
<td class="code" t-att-class="line.type" t-out="line.line"/>
</tr>
</t>
</table>
</div>
<DiffDisplay
fromValue="trackingValue.oldValue.value" toValue="trackingValue.newValue.value"
lineFilter="kept ? () => true : undefined"
/>
</t>
<t t-else="">
<span class="o-mail-Message-trackingOld me-1 px-1 text-muted fw-bold" t-out="formatTrackingOrNone(trackingValue.fieldType, trackingValue.oldValue)"/>