CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/sync/compute-server-open-file-tracking.ts
Views: 687
1
/*
2
Manage the state of open files in the compute servers syncdb sync'd file.
3
4
TODO: terminals aren't handled at all here, since they don't have a syncdoc.
5
*/
6
7
import type { SyncDocs } from "./sync-doc";
8
import type { SyncDB } from "@cocalc/sync/editor/db/sync";
9
import { once } from "@cocalc/util/async-utils";
10
import { meta_file, auxFileToOriginal } from "@cocalc/util/misc";
11
import { terminalTracker } from "@cocalc/terminal";
12
import { getLogger } from "@cocalc/backend/logger";
13
14
const log = getLogger("project:sync:compute-file-tracker").debug;
15
16
export default async function computeServerOpenFileTracking(
17
syncDocs: SyncDocs,
18
compute: SyncDB,
19
) {
20
log("initialize");
21
if (compute.get_state() != "ready") {
22
log("wait for compute server syncdoc to be ready...");
23
await once(compute, "ready");
24
}
25
26
const getOpenPaths = () => {
27
const v = syncDocs.getOpenPaths().concat(terminalTracker.getOpenPaths());
28
log("getOpenPaths", v);
29
return new Set(v);
30
};
31
const isOpen = (path: string): boolean => {
32
return syncDocs.isOpen(path) || terminalTracker.isOpen(path);
33
};
34
35
// Initialize -- get all open paths and update syncdb state to reflect this correctly.
36
const openPaths = getOpenPaths();
37
for (const { path, open } of compute.get().toJS()) {
38
const syncdocPath = computePathToSyncDocPath(path);
39
const isOpen = openPaths.has(syncdocPath);
40
log("init ", { path, open, syncdocPath, isOpen });
41
if (open != isOpen) {
42
log("init ", "changing state of ", { path });
43
compute.set({ path, open: isOpen });
44
}
45
}
46
compute.commit();
47
48
// Watch for files being opened or closed or paths being added/removed from syncdb
49
const handleOpen = (path: string) => {
50
log("handleOpen", { path });
51
if (compute.get_state() == "closed") {
52
syncDocs.removeListener("open", handleOpen);
53
return;
54
}
55
// A path was opened. If it is in the syncdb, then mark it as opened there.
56
const x = compute.get_one({ path: syncDocPathToComputePath(path) });
57
if (x != null) {
58
compute.set({ path: syncDocPathToComputePath(path), open: true });
59
compute.commit();
60
}
61
};
62
syncDocs.on("open", handleOpen);
63
terminalTracker.on("open", handleOpen);
64
65
const handleClose = (path: string) => {
66
log("handleClose", { path });
67
if (compute.get_state() == "closed") {
68
syncDocs.removeListener("open", handleClose);
69
return;
70
}
71
// A path was closed. If it is in the syncdb, then mark it as closed there.
72
const x = compute.get_one({ path: syncDocPathToComputePath(path) });
73
if (x != null) {
74
compute.set({ path: syncDocPathToComputePath(path), open: false });
75
compute.commit();
76
}
77
};
78
79
syncDocs.on("close", handleClose);
80
// terminals currently don't get closed, but we include this anyways so
81
// it will "just work" when we do implement that.
82
terminalTracker.on("close", handleClose);
83
84
// keys is an immutablejs Set of {path} objects
85
const handleComputeChange = (keys) => {
86
// The compute server table that tracks where things should run changed.
87
// If any path was added to that tabl, make sure its open state is correct.
88
const keyList = keys.toJS();
89
log("handleComputeChange", { keyList });
90
let n = 0;
91
for (const { path } of keyList) {
92
const x = compute.get_one({ path });
93
if (x == null) {
94
// path was REMOVED
95
log("handleComputeChange: removed", { path });
96
continue;
97
}
98
// path was added or changed in some way -- make sure it agrees
99
const open = isOpen(computePathToSyncDocPath(path));
100
if (x.get("open") != open) {
101
log("handleComputeChange -- making change:", { path, open });
102
compute.set({ path, open });
103
n += 1;
104
}
105
}
106
if (n > 0) {
107
compute.commit();
108
}
109
};
110
111
compute.on("change", handleComputeChange);
112
}
113
114
function syncDocPathToComputePath(path: string): string {
115
if (path.endsWith(".sage-jupyter2")) {
116
return auxFileToOriginal(path);
117
}
118
return path;
119
}
120
121
function computePathToSyncDocPath(path: string): string {
122
if (path.endsWith(".ipynb")) {
123
return meta_file(path, "jupyter2");
124
}
125
return path;
126
}
127
128