-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathMonacoEditor.vue
149 lines (111 loc) · 3.58 KB
/
MonacoEditor.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<template>
<div class="editor" ref="boxRef"></div>
</template>
<script setup lang="ts">
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
import { PropType, onMounted, ref, watch } from "vue";
import { TypstCompileResult, TypstPage } from "../pages/typst/interface";
import type { editor as editorType } from "monaco-editor";
import { invoke } from "@tauri-apps/api";
import { throttle, debounce, relativePath } from './../shared/util'
type IModelChangedEvent = editorType.IModelChangedEvent;
type IModelContentChangedEvent = editorType.IModelContentChangedEvent;
type ICodeEditor = editorType.ICodeEditor;
const props = defineProps({
path: String as PropType<string>,
root: String as PropType<string>,
})
const emit = defineEmits<{
(e: 'change', text: string): void
(e: 'compiled', data: TypstCompileResult): void
}>()
const boxRef = ref<HTMLElement>();
let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null;
const updateContent = async (editor: ICodeEditor, path: string) => {
if (!editor) return;
// Prevent further updates and immediately flush pending updates
editor.updateOptions({ readOnly: true });
editor.getModel()?.dispose();
try {
console.log('read file path:', path);
const content = await invoke<string>('fs_read_file_text', { path });
const uri = monaco.Uri.file(path);
console.log('uri:', uri)
let editorModel = monaco.editor.getModel(uri);
if (editorModel) {
// Update existing model. This should only be possible in development mode
// after hot reload.
editorModel.setValue(content);
} else {
editorModel = monaco.editor.createModel(content, undefined, uri);
}
editor.setModel(editorModel);
} catch (err) {
console.warn(err)
} finally {
editor.updateOptions({ readOnly: false });
}
};
const handleCompile = async () => {
const editorModel = monacoEditor?.getModel();
if (editorModel) {
const content = editorModel.getValue()
const path = relativePath(props.root!, props.path!)
const pages = await invoke<TypstCompileResult>('typst_compile_doc', { path, content })
emit('compiled', pages)
}
};
const handleSave = async () => {
const model = monacoEditor?.getModel();
if (model) {
// Removing the preceding slash
const path = relativePath(props.root!, props.path!);
await invoke("fs_write_file_text", { path, content: model.getValue() });
}
};
//@ts-ignore
const handleCompileThrottle = throttle(handleCompile);
const handleSaveDebounce = debounce(handleSave, 200, { maxWait: 5000 });
onMounted(() => {
if (!boxRef.value) {
return;
}
monacoEditor = monaco.editor.create(boxRef.value!, {
language: "typst",
fontSize: 16,
lineHeight: 28,
scrollBeyondLastColumn: 2,
automaticLayout: true,
minimap: { enabled: false },
});
monacoEditor.onDidChangeModel((evt: IModelChangedEvent) => {
handleCompileThrottle();
});
monacoEditor.onDidChangeModelContent((evt: IModelContentChangedEvent) => {
// Compile will update the source file directly in the memory without
// writing to the file system first, this will reduce the preview delay.
handleCompileThrottle();
handleSaveDebounce();
// handleCompile()
});
updateContent(monacoEditor, props.path!)
return () => {
if (monacoEditor) {
monacoEditor.dispose();
}
}
});
watch(() => props.path, () => {
if (monacoEditor && props.path) {
updateContent(monacoEditor, props.path)
}
})
</script>
<style scoped>
.editor {
box-sizing: border-box;
padding: 16px 0px;
height: 100%;
overflow: hidden;
}
</style>