change code editor to codemirror.

This commit is contained in:
2023-04-07 10:00:29 +08:00
parent 007de440d6
commit 0862b0ff91
14 changed files with 335 additions and 2744 deletions

View File

@@ -16,7 +16,7 @@ import {
uploadTestcases,
} from "../api"
const Monaco = defineAsyncComponent(() => import("~/shared/Monaco.vue"))
const CodeEditor = defineAsyncComponent(() => import("~/shared/CodeEditor.vue"))
interface Props {
problemID?: string
@@ -429,7 +429,7 @@ watch([fromExistingTags, newTags], (tags) => {
:key="index"
:name="lang"
>
<Monaco
<CodeEditor
v-model:value="template[lang]"
:language="lang"
:font-size="16"

View File

@@ -2,7 +2,7 @@
import { isDesktop } from "~/shared/composables/breakpoints"
import { code } from "~/shared/composables/learn"
const Monaco = defineAsyncComponent(() => import("~/shared/Monaco.vue"))
const CodeEditor = defineAsyncComponent(() => import("~/shared/CodeEditor.vue"))
const route = useRoute()
const router = useRouter()
@@ -57,7 +57,7 @@ function next() {
</n-scrollbar>
</n-gi>
<n-gi :span="14">
<Monaco v-model:value="code" />
<CodeEditor v-model:value="code" />
</n-gi>
</n-grid>
<div v-else>
@@ -72,7 +72,7 @@ function next() {
</n-button>
</n-space>
</n-scrollbar>
<Monaco v-model:value="code" height="calc(50vh - 42px)" />
<CodeEditor v-model:value="code" height="calc(50vh - 42px)" />
</div>
</template>

View File

@@ -5,7 +5,7 @@ import { code } from "oj/composables/code"
import { isDesktop } from "~/shared/composables/breakpoints"
import Form from "./Form.vue"
const Monaco = defineAsyncComponent(() => import("~/shared/Monaco.vue"))
const CodeEditor = defineAsyncComponent(() => import("~/shared/CodeEditor.vue"))
interface Props {
problem: Problem
@@ -23,16 +23,11 @@ const editorHeight = computed(() =>
<template>
<Form :problem="props.problem" />
<Monaco
class="editor"
<CodeEditor
v-model:value="code.value"
:language="code.language"
:height="editorHeight"
/>
</template>
<style scoped>
.editor {
min-height: 200px;
}
</style>
<style scoped></style>

View File

@@ -2,7 +2,7 @@
import { code } from "oj/composables/code"
import party from "party-js"
import { Ref } from "vue"
import { SOURCES, JUDGE_STATUS, SubmissionStatus } from "utils/constants"
import { JUDGE_STATUS, SubmissionStatus } from "utils/constants"
import { submissionMemoryFormat, submissionTimeFormat } from "utils/functions"
import { Problem, Submission, SubmitCodePayload } from "utils/types"
import { getSubmission, submitCode } from "oj/api"
@@ -65,12 +65,7 @@ const submitDisabled = computed(() => {
if (!userStore.isAuthed) {
return true
}
const value = code.value
if (
value.trim() === "" ||
value === problem!.value.template[code.language] ||
value === SOURCES[code.language]
) {
if (code.value.trim() === "") {
return true
}
if (judging.value || pending.value || submitting.value) {

55
src/shared/CodeEditor.vue Normal file
View File

@@ -0,0 +1,55 @@
<script lang="ts" setup>
import { Codemirror } from "vue-codemirror"
import { cpp } from "@codemirror/lang-cpp"
import { python } from "@codemirror/lang-python"
import { java } from "@codemirror/lang-java"
import { javascript } from "@codemirror/lang-javascript"
import { LANGUAGE } from "~/utils/types"
import { EditorView } from "@codemirror/view"
const styleTheme = EditorView.baseTheme({
"&.cm-editor.cm-focused": {
outline: "none",
},
})
interface Props {
value: string
language?: LANGUAGE
fontSize?: number
height?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
fontSize: 20,
height: "100%",
})
const code = ref(props.value)
const emit = defineEmits(["update:value"])
const lang = computed(() => {
if (props.language === "C" || props.language === "C++") return cpp()
if (props.language === "Java") return java()
if (props.language === "JavaScript") return javascript()
return python()
})
function onChange(v: string) {
emit("update:value", v)
}
</script>
<template>
<Codemirror
v-model="code"
:extensions="[styleTheme, lang]"
indentWithTab
:tabSize="4"
@change="onChange"
:style="{
height: props.height,
fontSize: props.fontSize + 'px',
}"
/>
</template>

View File

@@ -35,7 +35,6 @@ onMounted(() => {
const menus = computed<MenuOption[]>(() => [
{
show: false,
label: () =>
h(RouterLink, { to: "/learn/step-1" }, { default: () => "自学" }),
key: "learn",

View File

@@ -1,107 +0,0 @@
<script setup lang="ts">
import type * as Monaco from "monaco-editor"
import { LANGUAGE_FORMAT_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types"
import { isMobile } from "~/shared/composables/breakpoints"
import { isDark } from "./composables/dark"
import { init, monaco } from "./composables/monaco"
interface Props {
value: string
language?: LANGUAGE
height?: string
fontSize?: number
class?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
height: "calc(100vh - 92px)",
fontSize: 20,
class: "",
})
const emit = defineEmits<{
(e: "update:value", value: string): void
}>()
const monacoEditorRef = ref()
let editor: Monaco.editor.IStandaloneCodeEditor
let model: Monaco.editor.ITextModel
onMounted(async () => {
if (!monaco.value) await init()
model = monaco.value!.editor.createModel(
props.value,
LANGUAGE_FORMAT_VALUE[props.language]
)
editor = monaco.value!.editor.create(monacoEditorRef.value, {
model,
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: {
enabled: false,
},
lineNumbersMinChars: 2,
automaticLayout: true, // 自适应布局
tabSize: 4,
fontSize: props.fontSize || (isMobile.value ? 20 : 22), // 字体大小
scrollBeyondLastLine: false,
lineDecorationsWidth: 0,
scrollBeyondLastColumn: 0,
glyphMargin: false,
scrollbar: {
useShadows: false,
vertical: "hidden",
horizontal: "hidden",
},
overviewRulerLanes: 0,
})
model.onDidChangeContent(() => {
const value = model.getValue().toString()
emit("update:value", value)
})
editor.onKeyDown((e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
e.preventDefault()
}
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
e.preventDefault()
}
})
watchEffect(() => {
if (!monaco.value) return
monaco.value.editor.setModelLanguage(
model,
LANGUAGE_FORMAT_VALUE[props.language]
)
})
watchEffect(() => {
if (props.value !== model.getValue()) {
model.setValue(props.value)
}
})
watchEffect(() => {
if (!monaco.value) return
monaco.value.editor.setTheme(isDark.value ? "dark" : "light")
})
})
onUnmounted(() => {
editor && editor.dispose()
})
</script>
<template>
<div
v-if="monaco"
ref="monacoEditorRef"
:class="props.class"
:style="{ height: props.height }"
></div>
<div v-else :style="{ height: props.height }"></div>
</template>
<style scoped></style>

View File

@@ -1,25 +0,0 @@
import loader, { Monaco } from "@monaco-editor/loader"
import * as monaco0301 from "monaco-editor"
import { isLowVersion } from "~/utils/functions"
export const monaco = ref<Monaco>()
export async function init() {
if (isLowVersion) {
loader.config({ monaco: monaco0301 })
} else {
loader.config({
paths: { vs: "https://cdn.staticfile.org/monaco-editor/0.36.1/min/vs" },
"vs/nls": { availableLanguages: { "*": "zh-cn" } },
})
}
const [m, light, dark] = await Promise.all([
loader.init(),
fetch("/light.json").then((t) => t.json()),
fetch("/dark.json").then((t) => t.json()),
])
monaco.value = m
monaco.value.editor.defineTheme("light", light)
monaco.value.editor.defineTheme("dark", dark)
}