Skip to content

Commit f56db7c

Browse files
committed
Make Cells Display Lazily When Diffing Unchanged Cells
Motivation: During diffing Unchanged cells are displayed, which is not necessary. This is a performance issue when there are many unchanged cells. Related Issues: - #635 Open Questions: - I am struggling with getting the svg images to build properly. - How can this be tested in JupyterLab? Changes: - Alter Notebooks Widget to only display changed cells by using new linked list of cells. - Add new linked list of cells and lazy version of linked list of cells.
1 parent 4bc7ae2 commit f56db7c

File tree

10 files changed

+610
-181
lines changed

10 files changed

+610
-181
lines changed

nbdime/webapp/templates/diff.html

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,49 @@
1-
{% extends "nbdimepage.html" %}
1+
{% extends "nbdimepage.html" %} {% block nbdimeheader %}
22

3-
{% block nbdimeheader %}
3+
<div id="nbdime-header" class="nbdime-Diff">
4+
<h3>Notebook Diff</h3>
5+
Enter notebook filenames or URLs in the form below to get started.
46

5-
<div id="nbdime-header" class="nbdime-Diff">
6-
<h3>Notebook Diff</h3>
7-
Enter notebook filenames or URLs in the form below to get started.
7+
<form id="nbdime-diff-form" class="nbdime-forms">
8+
<fieldset>
9+
<legend>Please input filenames/URLs of notebooks to diff:</legend>
10+
<label>Base:</label>
11+
<input
12+
id="diff-base"
13+
type="text"
14+
name="base"
15+
value="{{ config_data['base']|e }}"
16+
/>
17+
<label>Remote:</label>
18+
<input
19+
id="diff-remote"
20+
type="text"
21+
name="remote"
22+
value="{{ config_data['remote']|e }}"
23+
/>
24+
<input
25+
id="nbdime-run-diff"
26+
type="submit"
27+
name="diff"
28+
value="Diff files"
29+
/>
30+
</fieldset>
31+
</form>
32+
<!-- nbdime-forms -->
33+
<div id="nbdime-header-buttonrow">
34+
<button id="nbdime-trust" style="display: none">Trust outputs</button>
35+
<button id="nbdime-close" type="checkbox" style="display: none">
36+
Close tool
37+
</button>
38+
<button id="nbdime-export" type="checkbox" style="display: none">
39+
Export diff
40+
</button>
41+
</div>
42+
<div id="nbdime-header-banner">
43+
<span id="nbdime-header-base">Base</span>
44+
<span id="nbdime-header-remote">Remote</span>
45+
</div>
46+
</div>
47+
<!-- ndime-header -->
848

9-
<form id="nbdime-diff-form" class="nbdime-forms">
10-
<fieldset>
11-
<legend>Please input filenames/URLs of notebooks to diff:</legend>
12-
<label>Base:</label>
13-
<input id="diff-base" type="text" name="base" value="{{ config_data['base']|e }}" />
14-
<label>Remote:</label>
15-
<input id="diff-remote" type="text" name="remote" value="{{ config_data['remote']|e }}" />
16-
<input id="nbdime-run-diff" type="submit" name="diff" value="Diff files" />
17-
</fieldset>
18-
</form> <!-- nbdime-forms -->
19-
<div id="nbdime-header-buttonrow">
20-
<input id="nbdime-hide-unchanged" type="checkbox"><label for="cbox1">Hide unchanged cells</label></input>
21-
<button id="nbdime-trust" style="display: none">Trust outputs</button>
22-
<button id="nbdime-close" type="checkbox" style="display: none">Close tool</button>
23-
<button id="nbdime-export" type="checkbox" style="display: none">Export diff</button>
24-
</div>
25-
<div id=nbdime-header-banner>
26-
<span id="nbdime-header-base">Base</span>
27-
<span id="nbdime-header-remote">Remote</span>
28-
</div>
29-
</div> <!-- ndime-header -->
30-
31-
{% endblock %}
49+
{% endblock %}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import { Panel, Widget } from "@lumino/widgets";
2+
import type { CellDiffWidget } from "./";
3+
4+
import foldDown from "./fold-down.svg";
5+
import foldUp from "./fold-up.svg";
6+
import fold from "./fold.svg";
7+
8+
class LinkedListCell extends Panel {
9+
_next: LinkedListCell | LazyDisplayLinkedListCell | null;
10+
_prev: LinkedListCell | LazyDisplayLinkedListCell | null;
11+
renderFunc: () => CellDiffWidget;
12+
_displayed: boolean;
13+
lazy: boolean;
14+
15+
constructor(renderFunc: () => CellDiffWidget) {
16+
super();
17+
this._next = null;
18+
this._prev = null;
19+
this.renderFunc = renderFunc;
20+
this._displayed = true;
21+
this.renderCell();
22+
this.addClass("linked-cell");
23+
this.lazy = false;
24+
}
25+
26+
protected renderCell() {
27+
this.addWidget(this.renderFunc());
28+
this._displayed = true;
29+
}
30+
31+
get next(): LinkedListCell | LazyDisplayLinkedListCell | null {
32+
return this._next;
33+
}
34+
35+
set next(nextCell: LinkedListCell | LazyDisplayLinkedListCell | null) {
36+
this._next = nextCell;
37+
if (nextCell === null) {
38+
return;
39+
}
40+
41+
if (nextCell.lazy) {
42+
console.log("call expandDown");
43+
nextCell.expandDown();
44+
}
45+
}
46+
47+
get prev(): LinkedListCell | LazyDisplayLinkedListCell | null {
48+
return this._prev;
49+
}
50+
51+
set prev(prevCell: LinkedListCell | LazyDisplayLinkedListCell | null) {
52+
this._prev = prevCell;
53+
if (prevCell === null) {
54+
return;
55+
}
56+
prevCell.next = this;
57+
if (prevCell.lazy) {
58+
prevCell.expandUp();
59+
}
60+
}
61+
62+
get displayed(): boolean {
63+
return this._displayed;
64+
}
65+
66+
expandUp(): void {
67+
return;
68+
}
69+
70+
expandDown(): void {
71+
return;
72+
}
73+
}
74+
75+
class LazyDisplayLinkedListCell extends LinkedListCell {
76+
expandButton: HTMLDivElement;
77+
expandButtonDisplayed: boolean;
78+
79+
// Path: packages/nbdime/src/diff/widget/wrapper_cells.ts
80+
constructor(renderFunc: () => CellDiffWidget) {
81+
super(renderFunc);
82+
this.expandButton = document.createElement("div");
83+
this.expandButton.className = "jp-expand-output-wrapper";
84+
this.expandButtonDisplayed = false;
85+
this.addClass("lazy-linked-cell");
86+
this.lazy = true;
87+
}
88+
89+
set prev(prevCell: LinkedListCell | LazyDisplayLinkedListCell) {
90+
this._prev = prevCell;
91+
prevCell.next = this;
92+
}
93+
94+
set next(nextCell: LinkedListCell | LazyDisplayLinkedListCell) {
95+
this._next = nextCell;
96+
}
97+
98+
protected renderCell() {
99+
this._displayed = false;
100+
}
101+
102+
expandUp(): void {
103+
if (this._displayed) {
104+
return;
105+
}
106+
if (this.expandButtonDisplayed) {
107+
this._setupFoldButton();
108+
} else {
109+
this._setupExpandUpButton();
110+
}
111+
}
112+
113+
expandDown(): void {
114+
if (this._displayed) {
115+
return;
116+
}
117+
if (this.expandButtonDisplayed) {
118+
this._setupFoldButton();
119+
} else {
120+
this._setupExpandDownButton();
121+
}
122+
}
123+
124+
_setupFoldButton() {
125+
this.expandButton.innerHTML = "";
126+
const button = this.createButton("Fold");
127+
button.onclick = (e) => {
128+
e.preventDefault();
129+
this.showLazyCellUp();
130+
};
131+
this.expandButton.appendChild(button);
132+
const widget = new Widget({ node: this.expandButton });
133+
this.addWidget(widget);
134+
}
135+
136+
_setupExpandUpButton() {
137+
const button = this.createButton("Up");
138+
button.onclick = (e) => {
139+
e.preventDefault();
140+
this.showLazyCellUp();
141+
};
142+
this.expandButton.appendChild(button);
143+
const widget = new Widget({ node: this.expandButton });
144+
this.addWidget(widget);
145+
}
146+
147+
_setupExpandDownButton() {
148+
const button = this.createButton("Down");
149+
button.onclick = (e) => {
150+
e.preventDefault();
151+
this.showLazyCellDown();
152+
};
153+
this.expandButton.appendChild(button);
154+
const widget = new Widget({ node: this.expandButton });
155+
this.addWidget(widget);
156+
}
157+
158+
createButton(direction: "Up" | "Down" | "Fold"): HTMLAnchorElement {
159+
this.expandButton.innerHTML = "";
160+
const button = document.createElement("a");
161+
button.title = `Expand ${direction}`;
162+
button.setAttribute("aria-label", `Expand ${direction}`);
163+
button.innerHTML = this.buttonSvg(direction);
164+
if (direction === "Up") {
165+
button.innerHTML = foldUp;
166+
} else if (direction === "Down") {
167+
button.innerHTML = foldDown;
168+
} else {
169+
button.innerHTML = fold;
170+
}
171+
this.expandButtonDisplayed = true;
172+
return button;
173+
}
174+
175+
buttonSvg(direction: "Up" | "Down" | "Fold"): string {
176+
if (direction === "Up") {
177+
return foldUp;
178+
} else if (direction === "Down") {
179+
return foldDown;
180+
} else {
181+
return fold;
182+
}
183+
}
184+
185+
showLazyCellUp() {
186+
this.showLazyCell();
187+
this._prev?.expandUp();
188+
}
189+
190+
showLazyCellDown() {
191+
this.showLazyCell();
192+
this._next?.expandDown();
193+
}
194+
195+
showLazyCell() {
196+
this.addWidget(this.renderFunc());
197+
this._displayed = true;
198+
this.expandButton.remove();
199+
}
200+
}
201+
202+
export { LinkedListCell, LazyDisplayLinkedListCell };

0 commit comments

Comments
 (0)