Skip to content

Commit 4055a58

Browse files
committed
opening files on compute servers: issue where file is blank for a moment when opened for the first time
1 parent 8ee0dcb commit 4055a58

File tree

5 files changed

+59
-23
lines changed

5 files changed

+59
-23
lines changed

src/packages/frontend/compute/manager.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ export class ComputeServersManager extends EventEmitter {
4242
this.sync_db.close();
4343
};
4444

45+
// save the current state to the backend. This is critical to do, e.g., before
46+
// opening a file and after calling connectComputeServerToPath, since otherwise
47+
// the project doesn't even know that the file should open on the compute server
48+
// until after it has opened it, which is disconcerting and not efficient (but
49+
// does mostly work, though it is intentionally making things really hard on ourselves).
50+
save = async () => {
51+
await this.sync_db.save();
52+
};
53+
4554
getComputeServers = () => {
4655
const servers = {};
4756
const cursors = this.sync_db.get_cursors().toJS();

src/packages/frontend/frame-editors/code-editor/actions.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ export class Actions<
258258
// Init setting of value whenever syncstring changes -- only used in derived classes
259259
protected _init_syncstring_value(): void {
260260
this._syncstring.on("change", () => {
261-
if (!this._syncstring || this._syncstring.versions().length == 0) {
261+
if (!this._syncstring) {
262262
// edge case where actions closed but this event was still triggered, OR
263263
// the syncstring changed, but has not actually loaded a version from
264264
// disk yet, which happens with compute servers for a second.
@@ -1509,7 +1509,9 @@ export class Actions<
15091509
syncstring_commit(): void {
15101510
// We also skip if the syncstring hasn't yet been initialized.
15111511
// This happens in some cases.
1512-
if (this._state === "closed" || this._syncstring.get_state() != "ready") return;
1512+
if (this._state === "closed" || this._syncstring.get_state() != "ready") {
1513+
return;
1514+
}
15131515
if (this._syncstring != null) {
15141516
// we pass true since here we want any UI for this or any derived
15151517
// editor to immediately react when we commit. This is particularly
@@ -1547,7 +1549,9 @@ export class Actions<
15471549
// note -- we don't try to set the syncstring if actions are closed
15481550
// or the syncstring isn't initialized yet. The latter case happens when
15491551
// switching the file that is being edited in a frame, e.g., for latex.
1550-
if (this._state === "closed" || this._syncstring.get_state() != "ready") return;
1552+
if (this._state === "closed" || this._syncstring.get_state() != "ready") {
1553+
return;
1554+
}
15511555
if (!do_not_exit_undo_mode) {
15521556
// If we are NOT doing an undo operation, then setting the
15531557
// syncstring due to any

src/packages/frontend/frame-editors/terminal-editor/connected-terminal.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ declare const $: any;
4747
const SCROLLBACK = 5000;
4848
const MAX_HISTORY_LENGTH = 100 * SCROLLBACK;
4949

50+
// I think this may be flaky in some cases? Not sure. Not sure it is worth it. I can
51+
// never tell the difference. ALSO, we do so much to throttle the terminal to avoid
52+
// messing up the browser and/or using excessive bandwidth when people do dumb things,
53+
// that we can't really leverage this speed (or rather network is more of a limit).
54+
const ENABLE_WEBGL = false;
55+
5056
interface Path {
5157
file?: string;
5258
directory?: string;
@@ -143,25 +149,27 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
143149
throw Error("terminal.element must be defined");
144150
}
145151

146-
const webglAddon = new WebglAddon();
147-
try {
148-
this.terminal.loadAddon(webglAddon);
149-
webglAddon.onContextLoss(() => {
150-
// This really does work and properly switches back to canvas. To convince yourself
151-
// of this, open a single terminal, then open another tab with another terminal and
152-
// split it about 20+ times. In the console, you'll see that the oldest webGL contexts
153-
// go away. That triggers calling this function, and indeed the terminal then falls
154-
// back seamlessly to canvas rendering. Very impressive, xterm.js.
155-
webglAddon.dispose();
156-
});
157-
} catch (err) {
158-
// We have to disable the dispose when it doesn't get used, since it breaks
159-
// on cleanup, and the xtermjs api has no way of removing an addon, and
160-
// only catching the error on dispose later would mean leaving other things
161-
// potentially not cleaned up properly. I read the code of webglAddon.dispose
162-
// and it doesn't do anything if the addon wasn't initialized.
163-
webglAddon.dispose = () => {};
164-
console.log(`WebGL Terminal not available (using fallback). -- ${err}`);
152+
if (ENABLE_WEBGL) {
153+
const webglAddon = new WebglAddon();
154+
try {
155+
this.terminal.loadAddon(webglAddon);
156+
webglAddon.onContextLoss(() => {
157+
// This really does work and properly switches back to canvas. To convince yourself
158+
// of this, open a single terminal, then open another tab with another terminal and
159+
// split it about 20+ times. In the console, you'll see that the oldest webGL contexts
160+
// go away. That triggers calling this function, and indeed the terminal then falls
161+
// back seamlessly to canvas rendering. Very impressive, xterm.js.
162+
webglAddon.dispose();
163+
});
164+
} catch (err) {
165+
// We have to disable the dispose when it doesn't get used, since it breaks
166+
// on cleanup, and the xtermjs api has no way of removing an addon, and
167+
// only catching the error on dispose later would mean leaving other things
168+
// potentially not cleaned up properly. I read the code of webglAddon.dispose
169+
// and it doesn't do anything if the addon wasn't initialized.
170+
webglAddon.dispose = () => {};
171+
console.log(`WebGL Terminal not available (using fallback). -- ${err}`);
172+
}
165173
}
166174

167175
this.element = this.terminal.element;
@@ -887,6 +895,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
887895
id,
888896
path: this.term_path,
889897
});
898+
await computeServerAssociations.save();
890899
}
891900
};
892901
}

src/packages/frontend/project_actions.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,7 @@ export class ProjectActions extends Actions<ProjectStoreState> {
13751375
id,
13761376
path: sidePath,
13771377
});
1378+
await computeServerAssociations.save();
13781379
};
13791380

13801381
// getComputeServerIdForFile = async ({
@@ -1426,6 +1427,15 @@ export class ProjectActions extends Actions<ProjectStoreState> {
14261427
id: selectedComputeServerId,
14271428
path,
14281429
});
1430+
// Now we save: why?
1431+
// Because we need to be sure the backend actually knows we want to use the compute
1432+
// server for the file before opening it; otherwise, it'll first get opened
1433+
// in the project, then later on the compute server, which is potentially VERY
1434+
// disconcerting and annoying, especially if the file doesn't exist. It does
1435+
// work without doing this (because our design is robust to switching compute servers
1436+
// at any time), but it ends up with a blank file for a moment, and lots of empty files
1437+
// being created.
1438+
await computeServerAssociations.save();
14291439
return true;
14301440
};
14311441

src/packages/sync/editor/generic/sync-doc.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,10 @@ export class SyncDoc extends EventEmitter {
13561356
// large or is not readable by the user. They are informed to
13571357
// fix the problem... and once they do (and wait up to 10s),
13581358
// this will finish.
1359+
// if (!this.client.is_browser() && !this.client.is_project()) {
1360+
// // FAKE DELAY!!! Just to simulate flakiness / slow network!!!!
1361+
// await delay(10000);
1362+
// }
13591363
await retry_until_success({
13601364
f: this.init_load_from_disk.bind(this),
13611365
max_delay: 10000,
@@ -1599,7 +1603,7 @@ export class SyncDoc extends EventEmitter {
15991603
size = await this.load_from_disk();
16001604
if (firstLoad) {
16011605
dbg("emitting first-load event");
1602-
// this event is emittd the first time the document is ever loaded from disk.
1606+
// this event is emited the first time the document is ever loaded from disk.
16031607
this.emit("first-load");
16041608
}
16051609
dbg("loaded");

0 commit comments

Comments
 (0)