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/lean/server.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
LEAN server
8
*/
9
10
const lean_files = {};
11
12
import { lean_server, Lean } from "./lean";
13
import { init_global_packages } from "./global-packages";
14
import { isEqual } from "lodash";
15
16
let the_lean_server: Lean | undefined = undefined;
17
18
function init_lean_server(client: any, logger: any): void {
19
the_lean_server = lean_server(client);
20
the_lean_server.on("tasks", function (path: string, tasks: object[]) {
21
logger.debug("lean_server:websocket:tasks -- ", path, tasks);
22
const lean_file = lean_files[`lean:${path}`];
23
if (lean_file !== undefined && !isEqual(lean_file.tasks, tasks)) {
24
lean_file.tasks = tasks;
25
lean_file.channel.write({ tasks });
26
}
27
});
28
29
the_lean_server.on("sync", function (path: string, hash: number) {
30
logger.debug("lean_server:websocket:sync -- ", path, hash);
31
const lean_file = lean_files[`lean:${path}`];
32
if (lean_file !== undefined && !isEqual(lean_file.sync, hash)) {
33
const sync = { hash: hash, time: Date.now() };
34
lean_file.sync = sync;
35
lean_file.channel.write({ sync });
36
}
37
});
38
39
the_lean_server.on("messages", function (path: string, messages: object) {
40
logger.debug("lean_server:websocket:messages -- ", path, messages);
41
const lean_file = lean_files[`lean:${path}`];
42
if (lean_file !== undefined && !isEqual(lean_file.messages, messages)) {
43
lean_file.messages = messages;
44
lean_file.channel.write({ messages });
45
}
46
});
47
}
48
49
export async function lean_channel(
50
client: any,
51
primus: any,
52
logger: any,
53
path: string
54
): Promise<string> {
55
if (the_lean_server === undefined) {
56
await init_global_packages();
57
init_lean_server(client, logger);
58
if (the_lean_server === undefined) {
59
// just to satisfy typescript.
60
throw Error("lean server not defined");
61
}
62
}
63
the_lean_server.register(path);
64
65
// TODO: delete lean_files[name] under some condition.
66
const name = `lean:${path}`;
67
if (lean_files[name] !== undefined) {
68
return name;
69
}
70
const channel = primus.channel(name);
71
lean_files[name] = {
72
channel,
73
messages: [],
74
};
75
76
channel.on("connection", function (spark: any): void {
77
// make sure lean server cares:
78
if (the_lean_server === undefined) {
79
// just to satisfy typescript.
80
throw Error("lean server not defined");
81
}
82
the_lean_server.register(path);
83
84
const lean_file = lean_files[name];
85
if (lean_file === undefined) {
86
return;
87
}
88
// Now handle the connection
89
logger.debug(
90
"lean channel",
91
`new connection from ${spark.address.ip} -- ${spark.id}`
92
);
93
spark.write({
94
messages: lean_file.messages,
95
sync: lean_file.sync,
96
tasks: lean_file.tasks,
97
});
98
spark.on("end", function () {});
99
});
100
101
return name;
102
}
103
104
function assert_type(name: string, x: any, type: string): void {
105
if (typeof x != type) {
106
throw Error(`${name} must have type ${type}`);
107
}
108
}
109
110
export async function lean(
111
client: any,
112
_: any,
113
logger: any,
114
opts: any
115
): Promise<any> {
116
if (the_lean_server === undefined) {
117
init_lean_server(client, logger);
118
if (the_lean_server === undefined) {
119
// just to satisfy typescript.
120
throw Error("lean server not defined");
121
}
122
}
123
if (opts == null || typeof opts.cmd != "string") {
124
throw Error("opts must be an object with cmd field a string");
125
}
126
// control message
127
logger.debug("lean command", JSON.stringify(opts));
128
switch (opts.cmd) {
129
case "info":
130
assert_type("path", opts.path, "string");
131
assert_type("line", opts.line, "number");
132
assert_type("column", opts.column, "number");
133
const r = (await the_lean_server.info(opts.path, opts.line, opts.column))
134
.record;
135
return r ? r : {};
136
137
// get server version
138
case "version":
139
return await the_lean_server.version();
140
141
// kill the LEAN server.
142
// this can help with, e.g., updating the LEAN_PATH
143
case "kill":
144
return the_lean_server.kill();
145
146
case "restart":
147
return await the_lean_server.restart();
148
149
case "complete":
150
assert_type("path", opts.path, "string");
151
assert_type("line", opts.line, "number");
152
assert_type("column", opts.column, "number");
153
const complete = await the_lean_server.complete(
154
opts.path,
155
opts.line,
156
opts.column,
157
opts.skipCompletions
158
);
159
if (complete == null || complete.completions == null) {
160
return [];
161
}
162
// delete the source fields -- they are LARGE and not used at all in the UI.
163
for (const c of complete.completions) {
164
delete (c as any).source; // cast because of mistake in upstream type def. sigh.
165
}
166
/*
167
complete.completions.sort(function(a, b): number {
168
if (a.text == null || b.text == null) {
169
// satisfy typescript null checks; shouldn't happen.
170
return 0;
171
}
172
return cmp(a.text.toLowerCase(), b.text.toLowerCase());
173
});
174
*/
175
return complete.completions;
176
177
default:
178
throw Error(`unknown cmd ${opts.cmd}`);
179
}
180
}
181
182