Skip to content

Feat: Add scroll diff decorators #5807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 104 additions & 5 deletions src/ext/diff/base_diff_view.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
"use strict";

var oop = require("../../lib/oop");

Check warning on line 3 in src/ext/diff/base_diff_view.js

View workflow job for this annotation

GitHub Actions / build (16.x)

'oop' is assigned a value but never used
var Range = require("../../range").Range;
var dom = require("../../lib/dom");
var config = require("../../config");
var LineWidgets = require("../../line_widgets").LineWidgets;
var ScrollDiffDecorator = require("./scroll_diff_decorator").ScrollDiffDecorator;

// @ts-ignore
var css = require("./styles-css.js").cssText;

var Editor = require("../../editor").Editor;
var Renderer = require("../../virtual_renderer").VirtualRenderer;
var UndoManager = require("../../undomanager").UndoManager;
var Decorator = require("../../layer/decorators").Decorator;

require("../../theme/textmate");
// enable multiselect
require("../../multi_select");
Expand Down Expand Up @@ -124,6 +127,8 @@
diffModel.valueB || "")),
chunks: []
});

this.setupScrollbars();
}

addGutterDecorators() {
Expand All @@ -140,7 +145,6 @@
$setupModel(session, value) {
var editor = new Editor(new Renderer(), session);
editor.session.setUndoManager(new UndoManager());
// editor.renderer.setOption("decoratorType", "diff");
if (value) {
editor.setValue(value, -1);
}
Expand Down Expand Up @@ -268,13 +272,100 @@
this.editorA && this.editorA.renderer.updateBackMarkers();
this.editorB && this.editorB.renderer.updateBackMarkers();

//this.updateScrollBarDecorators();
setTimeout(() => {
this.updateScrollBarDecorators();
}, 0);

if (this.$foldUnchangedOnInput) {
this.foldUnchanged();
}
}

setupScrollbars() {
/**
* @param {Renderer & {$scrollDecorator: ScrollDiffDecorator}} renderer
*/
const setupScrollBar = (renderer) => {
setTimeout(() => {
this.$setScrollBarDecorators(renderer);
this.updateScrollBarDecorators();
}, 0);
};

if (this.inlineDiffEditor) {
setupScrollBar(this.activeEditor.renderer);
}
else {
setupScrollBar(this.editorA.renderer);
setupScrollBar(this.editorB.renderer);
}

}

$setScrollBarDecorators(renderer) {
if (renderer.$scrollDecorator) {
renderer.$scrollDecorator.destroy();
}
renderer.$scrollDecorator = new ScrollDiffDecorator(renderer.scrollBarV, renderer, this.inlineDiffEditor);
renderer.$scrollDecorator.setSessions(this.sessionA, this.sessionB);
renderer.scrollBarV.setVisible(true);
renderer.scrollBarV.element.style.bottom = renderer.scrollBarH.getHeight() + "px";
}

$resetDecorators(renderer) {
if (renderer.$scrollDecorator) {
renderer.$scrollDecorator.destroy();
}
renderer.$scrollDecorator = new Decorator(renderer.scrollBarV, renderer);
}

updateScrollBarDecorators() {
if (this.inlineDiffEditor) {
if (!this.activeEditor) {
return;

Check warning on line 325 in src/ext/diff/base_diff_view.js

View check run for this annotation

Codecov / codecov/patch

src/ext/diff/base_diff_view.js#L325

Added line #L325 was not covered by tests
}
this.activeEditor.renderer.$scrollDecorator.zones = [];
}
else {
if (!this.editorA || !this.editorB) {
return;
}
this.editorA.renderer.$scrollDecorator.zones = [];
this.editorB.renderer.$scrollDecorator.zones = [];
}

/**
* @param {DiffChunk} change
*/
const updateDecorators = (editor, change) => {
if (!editor) {
return;

Check warning on line 342 in src/ext/diff/base_diff_view.js

View check run for this annotation

Codecov / codecov/patch

src/ext/diff/base_diff_view.js#L342

Added line #L342 was not covered by tests
}
if (change.old.start.row != change.old.end.row) {
editor.renderer.$scrollDecorator.addZone(change.old.start.row, change.old.end.row - 1, "delete");
}
if (change.new.start.row != change.new.end.row) {
editor.renderer.$scrollDecorator.addZone(change.new.start.row, change.new.end.row - 1, "insert");
}
};

if (this.inlineDiffEditor) {
this.chunks && this.chunks.forEach((lineChange) => {
updateDecorators(this.activeEditor, lineChange);
});
this.activeEditor.renderer.$scrollDecorator.$updateDecorators(this.activeEditor.renderer.layerConfig);
}
else {
this.chunks && this.chunks.forEach((lineChange) => {
updateDecorators(this.editorA, lineChange);
updateDecorators(this.editorB, lineChange);
});

this.editorA.renderer.$scrollDecorator.$updateDecorators(this.editorA.renderer.layerConfig);
this.editorB.renderer.$scrollDecorator.$updateDecorators(this.editorB.renderer.layerConfig);
}
}

/**
*
* @param {string[]} val1
Expand Down Expand Up @@ -443,8 +534,8 @@
}
}
}
scheduleRealign() {

scheduleRealign() {
if (!this.realignPending) {
this.realignPending = true;
this.editorA.renderer.on("beforeRender", this.realign);
Expand Down Expand Up @@ -475,6 +566,14 @@
this.gutterDecoratorB && this.gutterDecoratorB.dispose();
this.sessionA.selection.clearSelection();
this.sessionB.selection.clearSelection();

if (this.savedOptionsA && this.savedOptionsA.customScrollbar) {
this.$resetDecorators(this.editorA.renderer);
}
if (this.savedOptionsB &&this.savedOptionsB.customScrollbar) {
this.$resetDecorators(this.editorB.renderer);

Check warning on line 574 in src/ext/diff/base_diff_view.js

View check run for this annotation

Codecov / codecov/patch

src/ext/diff/base_diff_view.js#L574

Added line #L574 was not covered by tests
}

this.editorA = this.editorB = null;

}
Expand Down Expand Up @@ -738,7 +837,7 @@
maxDiffs: {
value: 5000,
},
});
});

var emptyGutterRenderer = {
getText: function name(params) {
Expand Down
81 changes: 77 additions & 4 deletions src/ext/diff/diff_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
var ace = require("../../ace");
var Range = require("../../range").Range;
var editorA, editorB, diffView;
const {Decorator} = require("../../layer/decorators");
const {ScrollDiffDecorator} = require("./scroll_diff_decorator");


var DEBUG = false;

Expand All @@ -21,18 +24,18 @@
editor.container.style.position = "absolute";
editor.container.style.outline = "solid";
return editor;
}
}

function getValueA(lines) {
return lines.map(function(v) {
return v[0];
return v[0];
}).filter(function(x) {
return x != null;
}).join("\n");
}
function getValueB(lines) {
return lines.map(function(v) {
return v.length == 2 ? v[1] : v[0];
return v.length == 2 ? v[1] : v[0];
}).filter(function(x) {
return x != null;
}).join("\n");
Expand Down Expand Up @@ -189,7 +192,7 @@

diffView.detach();
checkEventRegistry();

},
"test: diff at ends": function() {
var diffProvider = new DiffProvider();
Expand Down Expand Up @@ -277,6 +280,7 @@

editorA.session.setValue(getValueA(simpleDiff));
editorB.session.setValue(getValueB(simpleDiff));
editorA.setOption("customScrollbar", true);

diffView = new InlineDiffView({
editorA, editorB,
Expand All @@ -302,6 +306,8 @@

assert.ok(!!diffView.editorB.renderer.$gutterLayer.$renderer);

assert.ok(editorA.renderer.$scrollDecorator instanceof ScrollDiffDecorator);

diffView.detach();

assert.equal(editorA.getOption("wrap"), "free");
Expand All @@ -312,9 +318,76 @@
assert.equal(editorB.getOption("fadeFoldWidgets"), false);
assert.equal(editorB.getOption("showFoldWidgets"), true);
assert.ok(!editorB.renderer.$gutterLayer.$renderer);

assert.ok(editorA.renderer.$scrollDecorator instanceof Decorator);
},
"test split diff scroll decorators": function(done) {
editorA.session.setValue(["a", "b", "c"].join("\n"));
editorB.session.setValue(["a", "c", "X"].join("\n"));

diffView = new DiffView({ editorA, editorB });
diffView.setProvider(new DiffProvider());

editorA.renderer.$loop._flush();
editorB.renderer.$loop._flush();

editorA.renderer.once("afterRender", () => {
setTimeout(() => {
assertDecoratorsPlacement(editorA, false);
done();
}, 0);
});
},
"test inline diff scroll decorators": function(done) {
editorA.session.setValue(["a", "b", "c"].join("\n"));
editorB.session.setValue(["a", "c", "X"].join("\n"));

diffView = new InlineDiffView({ editorA, editorB, showSideA: true });
diffView.setProvider(new DiffProvider());

editorA.renderer.$loop._flush();

editorA.renderer.once("afterRender", () => {
setTimeout(() => {
assertDecoratorsPlacement(editorA, true);
done();
}, 0);
});
}
};

function findPointFillStyle(imageData, y) {
const data = imageData.slice(4 * y, 4 * (y + 1));
const a = Math.round(data[3] / 256 * 100);
if (a === 100) return "rgb(" + data.slice(0, 3).join(",") + ")";
return "rgba(" + data.slice(0, 3).join(",") + "," + (a / 100) + ")";

Check warning on line 363 in src/ext/diff/diff_test.js

View check run for this annotation

Codecov / codecov/patch

src/ext/diff/diff_test.js#L363

Added line #L363 was not covered by tests
}

function assertDecoratorsPlacement(editor, inlineDiff) {
const decoA = editor.renderer.$scrollDecorator;
const ctxA = decoA.canvas.getContext("2d");
const delRow = 1;
const offA = decoA.sessionA.documentToScreenRow(delRow, 0) * decoA.lineHeight;
const centerA = offA + decoA.lineHeight / 2;
const yA = Math.round(decoA.heightRatio * centerA);
let imgA = ctxA.getImageData(decoA.oneZoneWidth, 0, 1, decoA.canvasHeight).data;
assert.equal(findPointFillStyle(imgA, yA), decoA.colors.light.delete);

if (inlineDiff) {
//make sure that in inline diff, markers fills the whole line (except error decorators part)
imgA = ctxA.getImageData(decoA.canvasWidth - 1, 0, 1, decoA.canvasHeight).data;
assert.equal(findPointFillStyle(imgA, yA), decoA.colors.light.delete);
}

const xB = decoA.oneZoneWidth * 2;
const imgB = ctxA.getImageData(xB, 0, 1, decoA.canvasHeight).data;

const insRow = 2;
const offB = decoA.sessionB.documentToScreenRow(insRow, 0) * decoA.lineHeight;
const centerB = offB + decoA.lineHeight / 2;
const yB = Math.round(decoA.heightRatio * centerB);
assert.equal(findPointFillStyle(imgB, yB), decoA.colors.light.insert);
}

if (typeof module !== "undefined" && module === require.main) {
require("asyncjs").test.testcase(module.exports).exec();
Expand Down
1 change: 1 addition & 0 deletions src/ext/diff/providers/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -2462,6 +2462,7 @@ var {DiffChunk} = require("../base_diff_view");
/**
* VSCode’s computeDiff provider
*/

class DiffProvider {
compute(originalLines, modifiedLines, opts) {
if (!opts) opts = {};
Expand Down
Loading