Skip to content

Commit 935f194

Browse files
committed
Moved stack-frame into multiple files
1 parent 5ba5561 commit 935f194

File tree

5 files changed

+302
-199
lines changed

5 files changed

+302
-199
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script setup lang="ts">
2+
import type { CallLocation } from 'shared/src';
3+
import { useStacktraceManagement } from '../composables/useStacktraceManagement';
4+
5+
const props = defineProps<{
6+
traceFrame: CallLocation
7+
}>();
8+
9+
const emit = defineEmits<{
10+
openFile: [path: string, line: number],
11+
setStackFrameId: [frameId: number]
12+
}>();
13+
14+
const { getRealLineNumber } = useStacktraceManagement();
15+
16+
function openFile() {
17+
const realLineNumber = getRealLineNumber(props.traceFrame, props.traceFrame.locationInCode.startLine);
18+
emit("openFile", props.traceFrame.file, realLineNumber);
19+
}
20+
21+
function switchToStackFrame() {
22+
emit("setStackFrameId", props.traceFrame.frameId);
23+
}
24+
</script>
25+
26+
<template>
27+
<vscode-link style="grid-area: path;" class="title-element" :href="traceFrame.file" @click="openFile">
28+
{{ traceFrame.file }}:{{ getRealLineNumber(traceFrame, traceFrame.locationInCode.startLine) }}
29+
</vscode-link>
30+
<vscode-button style="grid-area: focus" @click="switchToStackFrame">
31+
highlight frame
32+
</vscode-button>
33+
</template>
34+
35+
<style scoped>
36+
.title-element {
37+
align-items: baseline;
38+
white-space: nowrap;
39+
overflow: hidden;
40+
text-overflow: ellipsis;
41+
min-width: 0;
42+
}
43+
</style>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<script setup lang="ts">
2+
import { editor } from 'monaco-editor';
3+
import type { CallLocation, MonacoTheme } from 'shared/src';
4+
import { computed, watch } from 'vue';
5+
import { useMonacoEditor } from '../composables/useMonacoEditor';
6+
import { useStacktraceManagement } from '../composables/useStacktraceManagement';
7+
8+
const props = defineProps<{
9+
traceFrame: CallLocation,
10+
theme?: MonacoTheme,
11+
denseCodeMode?: boolean
12+
}>();
13+
14+
const {
15+
currentEditor,
16+
MONACO_EDITOR_OPTIONS,
17+
setEditor: setMonacoEditor,
18+
setDecorators,
19+
layoutEditor,
20+
setTheme,
21+
setupScrollPrevention,
22+
setupResizeObserver
23+
} = useMonacoEditor();
24+
25+
const { registerStacktraceEditor, processCodeForDenseMode } = useStacktraceManagement();
26+
27+
const code = computed(() => {
28+
return processCodeForDenseMode(
29+
props.traceFrame.code,
30+
props.traceFrame.locationInCode.startLine,
31+
props.denseCodeMode ?? false
32+
);
33+
});
34+
35+
function setEditor(edit: editor.IStandaloneCodeEditor) {
36+
setMonacoEditor(edit);
37+
if (!edit) return;
38+
39+
registerStacktraceEditor(edit, props.traceFrame);
40+
setupScrollPrevention(edit);
41+
setupResizeObserver(edit);
42+
}
43+
44+
function setupEditor() {
45+
const editor = currentEditor.value;
46+
if (!editor || !props.traceFrame) return;
47+
48+
function update() {
49+
if (!editor) return;
50+
51+
editor.revealLineNearTop(props.traceFrame.locationInCode.startLine + 1);
52+
setDecorators(editor, props.traceFrame.locationInCode);
53+
layoutEditor(editor);
54+
55+
if (props.theme) {
56+
setTheme(props.theme);
57+
}
58+
}
59+
60+
const disposable = editor.onDidChangeModelContent(update);
61+
editor.onDidDispose(() => disposable.dispose());
62+
63+
update();
64+
}
65+
66+
// Watchers
67+
watch([currentEditor, code], () => {
68+
try {
69+
setupEditor();
70+
} catch (e) {
71+
console.error("Error while setting up editor", e);
72+
}
73+
});
74+
75+
watch(() => props.theme, (newTheme) => {
76+
if (newTheme) setTheme(newTheme);
77+
});
78+
</script>
79+
80+
<template>
81+
<vue-monaco-editor class="stacktrace-monaco-editor" :value="code" theme="vs-dark" :options="MONACO_EDITOR_OPTIONS"
82+
:language="traceFrame.language" @mount="setEditor" />
83+
</template>
84+
85+
<style scoped>
86+
.stacktrace-monaco-editor {
87+
min-width: 100px;
88+
min-height: 50px;
89+
width: 100%;
90+
}
91+
92+
/* Override Monaco editor internal sizing */
93+
.stacktrace-monaco-editor :deep(.monaco-editor) {
94+
width: 100% !important;
95+
}
96+
</style>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { useMonaco } from '@guolao/vue-monaco-editor';
2+
import { editor, Range } from 'monaco-editor';
3+
import type { MonacoTheme, SerializedRange } from 'shared/src';
4+
import { shallowRef, watch } from 'vue';
5+
6+
export function useMonacoEditor() {
7+
const currentEditor = shallowRef<editor.IStandaloneCodeEditor>();
8+
const monaco = useMonaco();
9+
10+
const MONACO_EDITOR_OPTIONS = {
11+
minimap: { enabled: false },
12+
readOnly: true,
13+
lineNumbers: "on",
14+
scrollbar: { vertical: "hidden", horizontal: "auto" },
15+
scrollBeyondLastLine: false,
16+
stickyScrolling: false,
17+
contextmenu: false,
18+
} as editor.IEditorOptions;
19+
20+
function setEditor(edit: editor.IStandaloneCodeEditor) {
21+
currentEditor.value = edit;
22+
}
23+
24+
function setDecorators(editor: editor.IStandaloneCodeEditor, position: SerializedRange) {
25+
try {
26+
const currentDecorations = editor.getDecorationsInRange(new Range(1, 1, 9999999, 999999));
27+
editor.removeDecorations(currentDecorations?.map(e => e.id) ?? []);
28+
} catch {
29+
/* nop */
30+
}
31+
32+
const { startLine, startCharacter, endLine, endCharacter } = position;
33+
editor.createDecorationsCollection([{
34+
range: new Range(
35+
(startLine ?? 0) + 1,
36+
startCharacter ?? 1,
37+
endLine ?? (startLine ?? 0) + 1,
38+
endCharacter ?? 9999),
39+
40+
options: {
41+
isWholeLine: true,
42+
className: 'highlight'
43+
}
44+
}]);
45+
}
46+
47+
function layoutEditor(editor: editor.IStandaloneCodeEditor) {
48+
const lineCount = editor.getModel()?.getLineCount() ?? 0;
49+
const lineHeight = 18; // Standard line height for Monaco
50+
51+
// Calculate height based on line count, not scroll height
52+
const contentHeight = lineCount * lineHeight;
53+
const targetHeight = Math.min(Math.max(contentHeight + 10, 50), 400); // min 50px, max 400px
54+
55+
// Use CSS-based width instead of calculating container width
56+
editor.layout({ width: 100, height: targetHeight });
57+
}
58+
59+
function setTheme(monacoTheme: MonacoTheme) {
60+
try {
61+
const themeName = 'tmp';
62+
monaco.monacoRef.value?.editor.defineTheme(themeName, monacoTheme as editor.IStandaloneThemeData);
63+
monaco.monacoRef.value?.editor.setTheme(themeName);
64+
} catch (e) {
65+
console.error("error setting theme", e);
66+
}
67+
}
68+
69+
function setupScrollPrevention(editor: editor.IStandaloneCodeEditor) {
70+
const domNode = editor?.getDomNode();
71+
domNode?.addEventListener("scroll", e => e.stopImmediatePropagation(), { capture: true });
72+
domNode?.addEventListener("wheel", e => e.stopImmediatePropagation(), { capture: true });
73+
}
74+
75+
function setupResizeObserver(editor: editor.IStandaloneCodeEditor) {
76+
const domNode = editor?.getDomNode();
77+
if (!domNode) return;
78+
79+
let timeoutId: number;
80+
const resizeObserver = new ResizeObserver(() => {
81+
clearTimeout(timeoutId);
82+
timeoutId = window.setTimeout(() => {
83+
const container = domNode.parentElement;
84+
if (container) {
85+
const containerRect = container.getBoundingClientRect();
86+
const targetWidth = Math.max(containerRect.width - 10, 100);
87+
const currentLayout = editor.getLayoutInfo();
88+
editor.layout({ width: targetWidth, height: currentLayout.height });
89+
}
90+
}, 150);
91+
});
92+
93+
const container = domNode.parentElement;
94+
if (container) {
95+
resizeObserver.observe(container);
96+
}
97+
98+
editor.onDidDispose(() => {
99+
clearTimeout(timeoutId);
100+
resizeObserver.disconnect();
101+
});
102+
}
103+
104+
// Watch for theme changes
105+
watch([monaco], () => {
106+
// Theme watching logic can be added here if needed
107+
});
108+
109+
return {
110+
currentEditor,
111+
monaco,
112+
MONACO_EDITOR_OPTIONS,
113+
setEditor,
114+
setDecorators,
115+
layoutEditor,
116+
setTheme,
117+
setupScrollPrevention,
118+
setupResizeObserver
119+
};
120+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { CallLocation } from 'shared/src';
2+
import { stacktraceMap } from '../main';
3+
4+
export function useStacktraceManagement() {
5+
function getRealLineNumber(trace: CallLocation, lineNumber: number) {
6+
const lineOffset = trace.fileLocationOffset.startLine;
7+
return lineOffset + lineNumber; // WARNING: this is 1-based
8+
}
9+
10+
function registerStacktraceEditor(editor: any, traceFrame: CallLocation) {
11+
const currentId = editor.getModel()?.id;
12+
if (!currentId) {
13+
throw new Error("Model does not have a id!");
14+
}
15+
stacktraceMap.set(currentId, traceFrame);
16+
editor.onDidDispose(() => stacktraceMap.delete(currentId));
17+
}
18+
19+
function processCodeForDenseMode(code: string, startLine: number, denseMode: boolean) {
20+
if (!denseMode) return code;
21+
22+
// todo fix for different newline types
23+
return code.split('\n').slice(0, startLine + 1 + 1).join('\n').trimEnd(); // +1 for the current line +1 as a lookahead
24+
}
25+
26+
return {
27+
getRealLineNumber,
28+
registerStacktraceEditor,
29+
processCodeForDenseMode
30+
};
31+
}

0 commit comments

Comments
 (0)