Skip to content

Commit 2f02de6

Browse files
committed
fix #6830 -- links to stack trace files
1 parent 3637d8d commit 2f02de6

File tree

6 files changed

+74
-15
lines changed

6 files changed

+74
-15
lines changed

src/packages/frontend/client/project.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { ipywidgetsGetBufferUrl } from "@cocalc/frontend/jupyter/server-urls";
3838
import type { ApiKey } from "@cocalc/util/db-schema/api-keys";
3939
import computeServers from "@cocalc/frontend/compute/manager";
4040
import type { ExecOpts, ExecOutput } from "@cocalc/util/db-schema/projects";
41+
import { HOME_ROOT } from "@cocalc/util/consts/files";
4142

4243
export class ProjectClient {
4344
private client: WebappClient;
@@ -79,7 +80,7 @@ export class ProjectClient {
7980
const base_path = appBasePath;
8081
if (opts.path[0] === "/") {
8182
// absolute path to the root
82-
opts.path = ".smc/root" + opts.path; // use root symlink, which is created by start_smc
83+
opts.path = HOME_ROOT + opts.path; // use root symlink, which is created by start_smc
8384
}
8485
return encode_path(join(base_path, `${opts.project_id}/raw/${opts.path}`));
8586
}

src/packages/frontend/jupyter/output-messages/ansi.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export function is_ansi(s: any): boolean {
2424
);
2525
}
2626

27+
export function toText(s: string): string {
28+
return Anser.ansiToText(s);
29+
}
30+
2731
// Extract a plain-text representation of a given cell
2832
export function cellOutputToText(cell): string {
2933
const raw = cell.get("output");

src/packages/frontend/jupyter/output-messages/traceback.tsx

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
import React from "react";
77
import { Map } from "immutable";
8-
import { endswith } from "@cocalc/util/misc";
9-
import { Ansi } from "./ansi";
8+
import { Ansi, toText } from "./ansi";
109
import { TRACEBACK_STYLE } from "./style";
10+
import { useFileContext } from "@cocalc/frontend/lib/file-context";
11+
import { HOME_ROOT } from "@cocalc/util/consts/files";
1112

1213
interface TracebackProps {
1314
message: Map<string, any>;
@@ -24,23 +25,65 @@ export const Traceback: React.FC<TracebackProps> = React.memo(
2425
const v: JSX.Element[] = [];
2526

2627
const tb = message.get("traceback");
28+
const { AnchorTagComponent } = useFileContext();
2729

30+
let lines: string[];
2831
if (typeof tb == "string") {
29-
v.push(<Ansi>{tb}</Ansi>);
32+
lines = tb.split("\n");
33+
} else if (typeof tb.forEach == "function" || Array.isArray(tb)) {
34+
// forEach detects an immutable.js object
35+
lines = [];
36+
for (const x of tb) {
37+
lines.push(x);
38+
}
39+
} else {
40+
lines = [JSON.stringify(tb)];
3041
}
31-
// forEach detects an immutable object
32-
else if (typeof tb.forEach == "function" || Array.isArray(tb)) {
33-
let n: number = 0;
34-
for (let x of tb) {
35-
if (!endswith(x, "\n")) {
36-
x += "\n";
37-
}
42+
43+
let n: number = 0;
44+
for (let x of lines) {
45+
if (!x.endsWith("\n")) {
46+
x += "\n";
47+
}
48+
if (AnchorTagComponent != null && x.startsWith("File ")) {
49+
const { file, target, rest, line } = parseFile(x);
50+
v.push(
51+
<div key={n}>
52+
File{" "}
53+
<AnchorTagComponent href={`${target}#line=${line}`}>
54+
{file}:{line}
55+
</AnchorTagComponent>
56+
<Ansi>{rest}</Ansi>
57+
</div>,
58+
);
59+
} else {
3860
v.push(<Ansi key={n}>{x}</Ansi>);
39-
n += 1;
4061
}
62+
n += 1;
4163
}
4264

4365
return <div style={TRACEBACK_STYLE}>{v}</div>;
4466
},
45-
should_memoize
67+
should_memoize,
4668
);
69+
70+
function parseFile(x: string): {
71+
file: string;
72+
rest: string;
73+
target: string;
74+
line: number;
75+
} {
76+
const i = x.indexOf(", ");
77+
const a = toText(x.slice(0, i).trim()).slice(5).trim();
78+
const rest = x.slice(i);
79+
const v = a.split(":");
80+
const file = v[0];
81+
let target = file;
82+
if (target[0] === "/") {
83+
// absolute path to the root
84+
target = '~/' + HOME_ROOT + target; // use root symlink
85+
}
86+
87+
const line = parseInt(v[1]);
88+
return { rest, file, target, line };
89+
}

src/packages/frontend/project/open-file.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ export async function open_file(
163163
if (!actions.open_files) return; // closed
164164
alert_message({
165165
type: "info",
166-
message: `Opening realpath "${realpath}" instead, since filesystem links are not fully supported.`,
167-
timeout: 15,
166+
message: `Opening normalized real path "${realpath}"`,
167+
timeout: 10,
168168
});
169169
actions.open_files.delete(opts.path);
170170
opts.path = realpath;

src/packages/frontend/project/utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,16 @@ export function editor_id(project_id: string, path: string): string {
236236
}
237237

238238
// Normalize path as in node, except '' is the home dir, not '.'.
239+
// Also, if ~/ is somewhere in the path, start over at home.
239240
export function normalize(path: string): string {
241+
while (true) {
242+
const i = path.indexOf("~/");
243+
if (i == -1) {
244+
break;
245+
}
246+
path = path.slice(i + 2);
247+
}
248+
240249
path = os_path.normalize(path);
241250
if (path === ".") {
242251
return "";

src/packages/util/consts/files.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const HOME_ROOT = ".smc/root";
2+

0 commit comments

Comments
 (0)