Skip to content

Commit 7abd8f4

Browse files
committed
frontend: prepping websocket exec for async operations
1 parent b594aa8 commit 7abd8f4

21 files changed

+310
-248
lines changed

src/packages/backend/execute-code.test.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ describe("await", () => {
389389

390390
it("returns an error", async () => {
391391
const c = await executeCode({
392-
command: "sleep 1.5; >&2 echo baz; exit 3",
392+
command: "sleep .1; >&2 echo baz; exit 3",
393393
bash: true,
394394
err_on_exit: false,
395395
async_call: true,
@@ -405,13 +405,12 @@ describe("await", () => {
405405
async_get: job_id,
406406
async_stats: true,
407407
});
408-
expect((Date.now() - t0) / 1000).toBeGreaterThan(1);
408+
expect((Date.now() - t0) / 1000).toBeGreaterThan(.05);
409409
expect(s.type).toEqual("async");
410410
if (s.type !== "async") return;
411411
expect(s.stderr).toEqual("baz\n");
412412
expect(s.exit_code).toEqual(3);
413413
expect(s.status).toEqual("completed");
414-
expect(Array.isArray(s.stats)).toBeTruthy();
415414
});
416415
});
417416

src/packages/frontend/client/project.ts

+54-33
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,38 @@
77
Functionality that mainly involves working with a specific project.
88
*/
99

10+
import computeServers from "@cocalc/frontend/compute/manager";
11+
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
12+
import { ipywidgetsGetBufferUrl } from "@cocalc/frontend/jupyter/server-urls";
13+
import { HOME_ROOT } from "@cocalc/util/consts/files";
14+
import type { ApiKey } from "@cocalc/util/db-schema/api-keys";
15+
import type { ExecOpts, ExecOutput } from "@cocalc/util/db-schema/projects";
16+
import * as message from "@cocalc/util/message";
1017
import {
18+
coerce_codomain_to_numbers,
1119
copy_without,
20+
defaults,
1221
encode_path,
1322
is_valid_uuid_string,
1423
required,
15-
defaults,
16-
coerce_codomain_to_numbers,
1724
} from "@cocalc/util/misc";
1825
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
19-
import * as message from "@cocalc/util/message";
2026
import { DirectoryListingEntry } from "@cocalc/util/types";
21-
import { connection_to_project } from "../project/websocket/connect";
22-
import { API } from "../project/websocket/api";
23-
import httpApi from "./api";
27+
import { join } from "path";
2428
import { redux } from "../app-framework";
25-
import { WebappClient } from "./client";
2629
import { allow_project_to_run } from "../project/client-side-throttle";
30+
import { ensure_project_running } from "../project/project-start-warning";
31+
import { API } from "../project/websocket/api";
32+
import { connection_to_project } from "../project/websocket/connect";
2733
import { ProjectInfo, project_info } from "../project/websocket/project-info";
2834
import {
2935
ProjectStatus,
3036
project_status,
3137
} from "../project/websocket/project-status";
3238
import { UsageInfoWS, get_usage_info } from "../project/websocket/usage-info";
33-
import { ensure_project_running } from "../project/project-start-warning";
3439
import { Configuration, ConfigurationAspect } from "../project_configuration";
35-
import { join } from "path";
36-
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
37-
import { ipywidgetsGetBufferUrl } from "@cocalc/frontend/jupyter/server-urls";
38-
import type { ApiKey } from "@cocalc/util/db-schema/api-keys";
39-
import computeServers from "@cocalc/frontend/compute/manager";
40-
import type { ExecOpts, ExecOutput } from "@cocalc/util/db-schema/projects";
41-
import { HOME_ROOT } from "@cocalc/util/consts/files";
40+
import httpApi from "./api";
41+
import { WebappClient } from "./client";
4242

4343
export class ProjectClient {
4444
private client: WebappClient;
@@ -187,22 +187,34 @@ export class ProjectClient {
187187
for operations like "run rst2html on this file whenever it is saved."
188188
*/
189189
public async exec(opts: ExecOpts & { post?: boolean }): Promise<ExecOutput> {
190-
opts = defaults(opts, {
191-
project_id: required,
192-
compute_server_id: undefined,
193-
filesystem: undefined,
194-
path: "",
195-
command: required,
196-
args: [],
197-
timeout: 30,
198-
max_output: undefined,
199-
bash: false,
200-
aggregate: undefined,
201-
err_on_exit: true,
202-
env: undefined,
203-
post: false, // if true, uses the POST api through nextjs instead of the websocket api.
204-
cb: undefined, // if given use a callback interface instead of async
205-
});
190+
if ("async_get" in opts) {
191+
opts = defaults(opts, {
192+
project_id: required,
193+
compute_server_id: undefined,
194+
async_get: required,
195+
async_stats: undefined,
196+
async_await: undefined,
197+
post: false, // if true, uses the POST api through nextjs instead of the websocket api.
198+
cb: undefined,
199+
});
200+
} else {
201+
opts = defaults(opts, {
202+
project_id: required,
203+
compute_server_id: undefined,
204+
filesystem: undefined,
205+
path: "",
206+
command: required,
207+
args: [],
208+
timeout: 30,
209+
max_output: undefined,
210+
bash: false,
211+
aggregate: undefined,
212+
err_on_exit: true,
213+
env: undefined,
214+
post: false, // if true, uses the POST api through nextjs instead of the websocket api.
215+
async_call: undefined, // if given use a callback interface instead of async
216+
});
217+
}
206218

207219
if (
208220
!(await ensure_project_running(
@@ -211,6 +223,7 @@ export class ProjectClient {
211223
))
212224
) {
213225
return {
226+
type: "blocking",
214227
stdout: "",
215228
stderr: "You must start the project first",
216229
exit_code: 1,
@@ -234,7 +247,9 @@ export class ProjectClient {
234247
if (msg.status && msg.status == "error") {
235248
throw new Error(msg.error);
236249
}
237-
delete msg.status;
250+
if (msg.type === "blocking") {
251+
delete msg.status;
252+
}
238253
delete msg.error;
239254
if (opts.cb == null) {
240255
return msg;
@@ -252,7 +267,13 @@ export class ProjectClient {
252267
} else {
253268
opts.cb(err.message);
254269
}
255-
return { stdout: "", stderr: err.message, exit_code: 1, time: 0 }; // should be ignored; this is just to make typescript happy.
270+
return {
271+
type: "blocking",
272+
stdout: "",
273+
stderr: err.message,
274+
exit_code: 1,
275+
time: 0, // should be ignored; this is just to make typescript happy.
276+
};
256277
}
257278
}
258279
}

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

+45-50
Original file line numberDiff line numberDiff line change
@@ -18,72 +18,67 @@ const MINIMAL = `\\documentclass{article}
1818
const HELP_URL = "https://doc.cocalc.com/latex.html";
1919

2020
const VIEWERS = ["pdfjs_canvas", "pdf_embed", "build"] as const;
21+
2122
import { delay } from "awaiting";
2223
import * as CodeMirror from "codemirror";
23-
import { normalize as path_normalize } from "path";
24-
import { debounce, union } from "lodash";
25-
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
2624
import { fromJS, List, Map } from "immutable";
27-
import { once } from "@cocalc/util/async-utils";
28-
import { project_api } from "../generic/client";
25+
import { debounce, union } from "lodash";
26+
import { normalize as path_normalize } from "path";
27+
28+
import { Store } from "@cocalc/frontend/app-framework";
29+
import {
30+
TableOfContentsEntry,
31+
TableOfContentsEntryList,
32+
} from "@cocalc/frontend/components";
2933
import {
3034
Actions as BaseActions,
3135
CodeEditorState,
32-
} from "../code-editor/actions";
36+
} from "@cocalc/frontend/frame-editors/code-editor/actions";
37+
import { print_html } from "@cocalc/frontend/frame-editors/frame-tree/print";
38+
import { FrameTree } from "@cocalc/frontend/frame-editors/frame-tree/types";
39+
import { raw_url } from "@cocalc/frontend/frame-editors/frame-tree/util";
3340
import {
34-
latexmk,
35-
build_command,
36-
Engine,
37-
get_engine_from_config,
38-
} from "./latexmk";
39-
import { sagetex, sagetex_hash, sagetex_errors } from "./sagetex";
40-
import { pythontex, pythontex_errors } from "./pythontex";
41-
import { knitr, patch_synctex, knitr_errors } from "./knitr";
42-
import * as synctex from "./synctex";
43-
import { bibtex } from "./bibtex";
44-
import { count_words } from "./count_words";
45-
import { server_time, ExecOutput } from "../generic/client";
46-
import { clean } from "./clean";
47-
import { LatexParser, IProcessedLatexLog } from "./latex-log-parser";
48-
import { update_gutters } from "./gutters";
49-
import { ensureTargetPathIsCorrect, pdf_path } from "./util";
50-
import { KNITR_EXTS } from "./constants";
51-
import { forgetDocument, url_to_pdf } from "./pdfjs-doc-cache";
52-
import { FrameTree } from "../frame-tree/types";
53-
import { Store } from "../../app-framework";
54-
import { createTypedMap, TypedMap } from "../../app-framework";
55-
import { print_html } from "../frame-tree/print";
56-
import { raw_url } from "../frame-tree/util";
41+
project_api,
42+
server_time,
43+
} from "@cocalc/frontend/frame-editors/generic/client";
44+
import { open_new_tab } from "@cocalc/frontend/misc";
45+
import { once } from "@cocalc/util/async-utils";
5746
import {
47+
change_filename_extension,
5848
path_split,
5949
separate_file_extension,
50+
sha1,
6051
splitlines,
6152
startswith,
62-
change_filename_extension,
63-
sha1,
6453
} from "@cocalc/util/misc";
54+
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
55+
56+
import { bibtex } from "./bibtex";
6557
import { IBuildSpecs } from "./build";
66-
import { open_new_tab } from "@cocalc/frontend/misc";
58+
import { clean } from "./clean";
59+
import { KNITR_EXTS } from "./constants";
60+
import { count_words } from "./count_words";
61+
import { update_gutters } from "./gutters";
62+
import { knitr, knitr_errors, patch_synctex } from "./knitr";
63+
import { IProcessedLatexLog, LatexParser } from "./latex-log-parser";
64+
import {
65+
build_command,
66+
Engine,
67+
get_engine_from_config,
68+
latexmk,
69+
} from "./latexmk";
70+
import { forgetDocument, url_to_pdf } from "./pdfjs-doc-cache";
71+
import { pythontex, pythontex_errors } from "./pythontex";
72+
import { sagetex, sagetex_errors, sagetex_hash } from "./sagetex";
73+
import * as synctex from "./synctex";
6774
import { parseTableOfContents } from "./table-of-contents";
6875
import {
69-
TableOfContentsEntryList,
70-
TableOfContentsEntry,
71-
} from "@cocalc/frontend/components";
72-
73-
export interface BuildLog extends ExecOutput {
74-
parse?: IProcessedLatexLog;
75-
}
76-
77-
export type BuildLogs = Map<string, Map<string, any>>;
78-
79-
interface ScrollIntoViewParams {
80-
page: number;
81-
y: number;
82-
id: string;
83-
}
84-
85-
export const ScrollIntoViewRecord = createTypedMap<ScrollIntoViewParams>();
86-
export type ScrollIntoViewMap = TypedMap<ScrollIntoViewParams>;
76+
BuildLog,
77+
BuildLogs,
78+
ScrollIntoViewMap,
79+
ScrollIntoViewRecord,
80+
} from "./types";
81+
import { ensureTargetPathIsCorrect, pdf_path } from "./util";
8782

8883
interface LatexEditorState extends CodeEditorState {
8984
build_logs: BuildLogs;

src/packages/frontend/frame-editors/latex-editor/bibtex.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@
77
Run BibTex
88
*/
99

10-
import { exec } from "../generic/client";
11-
import { parse_path } from "../frame-tree/util";
10+
import { parse_path } from "@cocalc/frontend/frame-editors/frame-tree/util";
11+
import { exec } from "@cocalc/frontend/frame-editors/generic/client";
1212

1313
// time (ms since epoch) to use for aggregate
1414

1515
export async function bibtex(
1616
project_id: string,
1717
path: string,
1818
time?: number,
19-
output_directory?: string
19+
output_directory?: string,
2020
) {
2121
const { base, directory } = parse_path(path);
2222
return await exec({

src/packages/frontend/frame-editors/latex-editor/build.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ Show the last latex build log, i.e., output from last time we ran the LaTeX buil
88
*/
99

1010
import Ansi from "@cocalc/ansi-to-react";
11+
1112
import { AntdTabItem, Tab, Tabs } from "@cocalc/frontend/antd-bootstrap";
1213
import { React, Rendered, useRedux } from "@cocalc/frontend/app-framework";
1314
import { IconName, Loading } from "@cocalc/frontend/components";
1415
import { path_split } from "@cocalc/util/misc";
1516
import { COLORS } from "@cocalc/util/theme";
16-
import { BuildLogs } from "./actions";
1717
import { BuildCommand } from "./build-command";
1818
import { use_build_logs } from "./hooks";
19+
import { BuildLogs } from "./types";
1920

2021
interface IBuildSpec {
2122
button: boolean;
@@ -83,7 +84,7 @@ const BUILD_SPECS: IBuildSpecs = {
8384
icon: "trash",
8485
tip: "Delete all autogenerated auxiliary files",
8586
},
86-
};
87+
} as const;
8788

8889
interface Props {
8990
name: string;
@@ -103,7 +104,7 @@ export const Build: React.FC<Props> = React.memo((props) => {
103104
useRedux([name, "build_command_hardcoded"]) ?? false;
104105
const knitr: boolean = useRedux([name, "knitr"]);
105106
const [active_tab, set_active_tab] = React.useState<string>(
106-
BUILD_SPECS.latex.label
107+
BUILD_SPECS.latex.label,
107108
);
108109
const [error_tab, set_error_tab] = React.useState(null);
109110
let no_errors = true;
@@ -112,7 +113,7 @@ export const Build: React.FC<Props> = React.memo((props) => {
112113
title: string,
113114
value: string,
114115
error?: boolean,
115-
time_str?: string
116+
time_str?: string,
116117
): AntdTabItem {
117118
const style: React.CSSProperties = {
118119
fontFamily: "monospace",
@@ -152,7 +153,8 @@ export const Build: React.FC<Props> = React.memo((props) => {
152153
const time_str = time ? `(${(time / 1000).toFixed(1)} seconds)` : "";
153154
const title = BUILD_SPECS[stage].label;
154155
// highlights tab, if there is at least one parsed error
155-
const error = (build_logs.getIn([stage, "parse", "errors"]) as any).size > 0;
156+
const error =
157+
(build_logs.getIn([stage, "parse", "errors"]) as any).size > 0;
156158
// also show the problematic log to the user
157159
if (error) {
158160
no_errors = false;

src/packages/frontend/frame-editors/latex-editor/errors-and-warnings.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ import {
1818
useRedux,
1919
} from "@cocalc/frontend/app-framework";
2020
import { Icon, IconName, Loading } from "@cocalc/frontend/components";
21+
import { EditorState } from "@cocalc/frontend/frame-editors/frame-tree/types";
2122
import HelpMeFix from "@cocalc/frontend/frame-editors/llm/help-me-fix";
2223
import { capitalize, is_different, path_split } from "@cocalc/util/misc";
2324
import { COLORS } from "@cocalc/util/theme";
24-
import { EditorState } from "../frame-tree/types";
25-
import { Actions, BuildLogs } from "./actions";
25+
import { Actions } from "./actions";
2626
import { use_build_logs } from "./hooks";
27+
import { BuildLogs } from "./types";
2728

2829
function group_to_level(group: string): string {
2930
switch (group) {

src/packages/frontend/frame-editors/latex-editor/gutters.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@
1111
import { Popover } from "antd";
1212

1313
import { Icon } from "@cocalc/frontend/components";
14+
//import { Actions } from "@cocalc/frontend/frame-editors/code-editor/actions";
1415
import HelpMeFix from "@cocalc/frontend/frame-editors/llm/help-me-fix";
1516
import { capitalize } from "@cocalc/util/misc";
16-
import { Actions } from "../code-editor/actions";
17+
import { Actions } from "./actions";
1718
import { SPEC, SpecItem } from "./errors-and-warnings";
1819
import { Error, IProcessedLatexLog } from "./latex-log-parser";
1920

2021
export function update_gutters(opts: {
2122
log: IProcessedLatexLog;
2223
set_gutter: Function;
23-
actions;
24+
actions: Actions;
2425
}): void {
2526
for (const group of ["typesetting", "warnings", "errors"]) {
2627
// errors last so always shown if multiple issues on a single line!
@@ -86,7 +87,7 @@ function component(
8687
return v + `% this is line number ${line + 1}`;
8788
}}
8889
language={"latex"}
89-
extraFileInfo={actions.languageModelExtraFileInfo(false)}
90+
extraFileInfo={actions.languageModelExtraFileInfo()}
9091
tag={"latex-error-popover"}
9192
prioritize="end"
9293
/>

0 commit comments

Comments
 (0)