2022-11-05 15:13:40 +08:00
|
|
|
|
<template>
|
2023-12-07 23:57:11 +08:00
|
|
|
|
<div class="monaco-editor" style="border: 1px solid var(--el-border-color-light, #ebeef5); height: 100%">
|
2023-04-16 00:50:36 +08:00
|
|
|
|
<div class="monaco-editor-content" ref="monacoTextarea" :style="{ height: height }"></div>
|
2022-11-05 15:13:40 +08:00
|
|
|
|
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage">
|
2023-05-24 12:32:17 +08:00
|
|
|
|
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
2022-11-05 15:13:40 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2022-11-05 21:08:01 +08:00
|
|
|
|
import { ref, watch, toRefs, reactive, onMounted, onBeforeUnmount } from 'vue';
|
2023-09-11 17:34:24 +08:00
|
|
|
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|
|
|
|
|
// 相关语言
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/html/html.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/css/css.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/java/java.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/language/json/monaco.contribution';
|
|
|
|
|
|
// 右键菜单
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/caretOperations/browser/caretOperations.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/clipboard//browser/clipboard.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/format//browser/formatActions.js';
|
|
|
|
|
|
// 提示
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js';
|
|
|
|
|
|
import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestInlineCompletions.js';
|
|
|
|
|
|
|
2022-11-25 18:59:23 +08:00
|
|
|
|
import { editor, languages } from 'monaco-editor';
|
2023-09-11 17:34:24 +08:00
|
|
|
|
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
|
2023-07-06 20:59:22 +08:00
|
|
|
|
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
|
2022-11-22 00:34:42 +08:00
|
|
|
|
// 主题仓库 https://github.com/brijeshb42/monaco-themes
|
2022-11-05 15:13:40 +08:00
|
|
|
|
// 主题例子 https://editor.bitwiser.in/
|
|
|
|
|
|
// import Monokai from 'monaco-themes/themes/Monokai.json'
|
|
|
|
|
|
// import Active4D from 'monaco-themes/themes/Active4D.json'
|
|
|
|
|
|
// import ahe from 'monaco-themes/themes/All Hallows Eve.json'
|
|
|
|
|
|
// import bop from 'monaco-themes/themes/Birds of Paradise.json'
|
|
|
|
|
|
// import krTheme from 'monaco-themes/themes/krTheme.json'
|
|
|
|
|
|
// import Dracula from 'monaco-themes/themes/Dracula.json'
|
2023-07-06 20:59:22 +08:00
|
|
|
|
import SolarizedLight from 'monaco-themes/themes/Solarized-light.json';
|
2022-11-05 21:08:01 +08:00
|
|
|
|
import { language as shellLan } from 'monaco-editor/esm/vs/basic-languages/shell/shell.js';
|
2022-11-05 15:13:40 +08:00
|
|
|
|
import { ElOption, ElSelect } from 'element-plus';
|
|
|
|
|
|
|
2023-09-13 23:57:28 +08:00
|
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
|
|
import { useThemeConfig } from '@/store/themeConfig';
|
|
|
|
|
|
|
|
|
|
|
|
const { themeConfig } = storeToRefs(useThemeConfig());
|
|
|
|
|
|
|
2022-11-05 15:13:40 +08:00
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
modelValue: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
},
|
|
|
|
|
|
language: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
height: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: '500px',
|
|
|
|
|
|
},
|
|
|
|
|
|
width: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: 'auto',
|
|
|
|
|
|
},
|
|
|
|
|
|
canChangeMode: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false,
|
|
|
|
|
|
},
|
|
|
|
|
|
options: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: null,
|
|
|
|
|
|
},
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
|
|
|
|
|
//定义事件
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
2023-05-24 12:32:17 +08:00
|
|
|
|
const languageArr = [
|
2022-11-05 15:13:40 +08:00
|
|
|
|
{
|
|
|
|
|
|
value: 'shell',
|
|
|
|
|
|
label: 'Shell',
|
|
|
|
|
|
},
|
2022-11-25 18:59:23 +08:00
|
|
|
|
{
|
|
|
|
|
|
value: 'json',
|
|
|
|
|
|
label: 'JSON',
|
|
|
|
|
|
},
|
2022-11-05 15:13:40 +08:00
|
|
|
|
{
|
|
|
|
|
|
value: 'yaml',
|
|
|
|
|
|
label: 'Yaml',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'dockerfile',
|
|
|
|
|
|
label: 'Dockerfile',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'html',
|
|
|
|
|
|
label: 'XML/HTML',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'python',
|
|
|
|
|
|
label: 'Python',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'sql',
|
|
|
|
|
|
label: 'SQL',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'css',
|
|
|
|
|
|
label: 'CSS',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'javascript',
|
|
|
|
|
|
label: 'Javascript',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'java',
|
|
|
|
|
|
label: 'Java',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'markdown',
|
|
|
|
|
|
label: 'Markdown',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
value: 'text',
|
|
|
|
|
|
label: 'text',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2023-10-31 12:36:04 +08:00
|
|
|
|
const defaultOptions = {
|
2022-11-05 21:08:01 +08:00
|
|
|
|
language: 'shell',
|
|
|
|
|
|
theme: 'SolarizedLight',
|
|
|
|
|
|
automaticLayout: true, //自适应宽高布局
|
2023-07-06 20:59:22 +08:00
|
|
|
|
foldingStrategy: 'indentation', //代码可分小段折叠
|
2022-11-05 21:08:01 +08:00
|
|
|
|
roundedSelection: false, // 禁用选择文本背景的圆角
|
|
|
|
|
|
matchBrackets: 'near',
|
|
|
|
|
|
linkedEditing: true,
|
2023-07-06 20:59:22 +08:00
|
|
|
|
cursorBlinking: 'smooth', // 光标闪烁样式
|
2022-11-05 21:08:01 +08:00
|
|
|
|
mouseWheelZoom: true, // 在按住Ctrl键的同时使用鼠标滚轮时,在编辑器中缩放字体
|
|
|
|
|
|
overviewRulerBorder: false, // 不要滚动条的边框
|
2022-11-14 18:07:27 +08:00
|
|
|
|
tabSize: 4, // tab 缩进长度
|
2022-11-22 00:34:42 +08:00
|
|
|
|
// fontFamily: 'JetBrainsMono', // 字体 暂时不要设置,否则光标容易错位
|
2022-11-05 21:08:01 +08:00
|
|
|
|
fontWeight: 'bold',
|
|
|
|
|
|
// fontSize: 12,
|
|
|
|
|
|
// letterSpacing: 1, 字符间距
|
|
|
|
|
|
// quickSuggestions:false, // 禁用代码提示
|
|
|
|
|
|
minimap: {
|
|
|
|
|
|
enabled: false, // 不要小地图
|
|
|
|
|
|
},
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-11-05 21:08:01 +08:00
|
|
|
|
|
2023-09-19 23:00:32 +08:00
|
|
|
|
const monacoTextarea: any = ref();
|
|
|
|
|
|
|
|
|
|
|
|
let monacoEditorIns: editor.IStandaloneCodeEditor = null as any;
|
|
|
|
|
|
let completionItemProvider: any = null;
|
|
|
|
|
|
|
|
|
|
|
|
self.MonacoEnvironment = {
|
|
|
|
|
|
getWorker(_: any, label: string) {
|
|
|
|
|
|
if (label === 'json') {
|
|
|
|
|
|
return new JsonWorker();
|
|
|
|
|
|
}
|
|
|
|
|
|
return new EditorWorker();
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2022-11-05 15:13:40 +08:00
|
|
|
|
const state = reactive({
|
|
|
|
|
|
languageMode: 'shell',
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const { languageMode } = toRefs(state);
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
2022-11-05 21:08:01 +08:00
|
|
|
|
state.languageMode = props.language;
|
2022-11-05 22:18:59 +08:00
|
|
|
|
initMonacoEditorIns();
|
2022-11-05 21:08:01 +08:00
|
|
|
|
setEditorValue(props.modelValue);
|
|
|
|
|
|
registerCompletionItemProvider();
|
2022-11-05 15:13:40 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2022-11-05 21:08:01 +08:00
|
|
|
|
onBeforeUnmount(() => {
|
2022-11-05 22:18:59 +08:00
|
|
|
|
if (monacoEditorIns) {
|
|
|
|
|
|
monacoEditorIns.dispose();
|
2022-11-05 21:08:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (completionItemProvider) {
|
2023-09-19 23:00:32 +08:00
|
|
|
|
console.log('unmount=> dispose completion item provider');
|
2022-11-05 21:08:01 +08:00
|
|
|
|
completionItemProvider.dispose();
|
|
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 21:08:01 +08:00
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
|
(newValue: any) => {
|
|
|
|
|
|
if (!monacoEditorIns.hasTextFocus()) {
|
|
|
|
|
|
state.languageMode = props.language;
|
|
|
|
|
|
monacoEditorIns?.setValue(newValue);
|
|
|
|
|
|
}
|
2022-11-05 21:08:01 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
);
|
2022-11-10 18:55:10 +08:00
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => props.language,
|
|
|
|
|
|
(newValue: any) => {
|
|
|
|
|
|
changeLanguage(newValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2022-11-05 21:08:01 +08:00
|
|
|
|
|
2023-09-13 23:57:28 +08:00
|
|
|
|
// 监听 themeConfig editorTheme配置文件的变化
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => themeConfig.value.editorTheme,
|
|
|
|
|
|
(val) => {
|
|
|
|
|
|
console.log('monaco editor theme change: ', val);
|
|
|
|
|
|
monaco?.editor?.setTheme(val);
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2022-11-05 22:18:59 +08:00
|
|
|
|
const initMonacoEditorIns = () => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
console.log('初始化monaco编辑器');
|
2022-11-05 15:13:40 +08:00
|
|
|
|
// options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language
|
|
|
|
|
|
// 初始化一些主题
|
|
|
|
|
|
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
|
2023-10-31 12:36:04 +08:00
|
|
|
|
defaultOptions.language = state.languageMode;
|
|
|
|
|
|
defaultOptions.theme = themeConfig.value.editorTheme;
|
|
|
|
|
|
monacoEditorIns = monaco.editor.create(monacoTextarea.value, Object.assign(defaultOptions, props.options as any));
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
|
|
|
|
|
// 监听内容改变,双向绑定
|
2022-11-05 22:18:59 +08:00
|
|
|
|
monacoEditorIns.onDidChangeModelContent(() => {
|
2022-11-25 18:59:23 +08:00
|
|
|
|
emit('update:modelValue', monacoEditorIns.getModel()?.getValue());
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 15:13:40 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const changeLanguage = (value: any) => {
|
|
|
|
|
|
console.log('change lan');
|
|
|
|
|
|
// 获取当前的文档模型
|
2023-07-06 20:59:22 +08:00
|
|
|
|
let oldModel = monacoEditorIns.getModel();
|
2022-11-25 18:59:23 +08:00
|
|
|
|
if (!oldModel) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-11-05 15:13:40 +08:00
|
|
|
|
// 创建一个新的文档模型
|
2023-07-06 20:59:22 +08:00
|
|
|
|
let newModel = monaco.editor.createModel(oldModel.getValue(), value);
|
2022-11-05 15:13:40 +08:00
|
|
|
|
// 设置成新的
|
2023-07-06 20:59:22 +08:00
|
|
|
|
monacoEditorIns.setModel(newModel);
|
2022-11-05 15:13:40 +08:00
|
|
|
|
// 销毁旧的模型
|
|
|
|
|
|
if (oldModel) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
oldModel.dispose();
|
2022-11-05 15:13:40 +08:00
|
|
|
|
}
|
2022-11-05 21:08:01 +08:00
|
|
|
|
|
|
|
|
|
|
registerCompletionItemProvider();
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
|
|
|
|
|
const setEditorValue = (value: any) => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
monacoEditorIns.getModel()?.setValue(value);
|
|
|
|
|
|
};
|
2022-11-05 15:13:40 +08:00
|
|
|
|
|
2022-11-05 21:08:01 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 注册联想补全提示
|
|
|
|
|
|
*/
|
|
|
|
|
|
const registerCompletionItemProvider = () => {
|
|
|
|
|
|
if (completionItemProvider) {
|
2023-09-19 23:00:32 +08:00
|
|
|
|
console.log('exist competion item provider, dispose now');
|
2022-11-05 21:08:01 +08:00
|
|
|
|
completionItemProvider.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (state.languageMode == 'shell') {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
registeShell();
|
2022-11-05 21:08:01 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-11-05 21:08:01 +08:00
|
|
|
|
|
|
|
|
|
|
const registeShell = () => {
|
|
|
|
|
|
completionItemProvider = monaco.languages.registerCompletionItemProvider('shell', {
|
|
|
|
|
|
provideCompletionItems: async () => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
let suggestions: languages.CompletionItem[] = [];
|
2022-11-05 21:08:01 +08:00
|
|
|
|
shellLan.keywords.forEach((item: any) => {
|
|
|
|
|
|
suggestions.push({
|
|
|
|
|
|
label: item,
|
|
|
|
|
|
kind: monaco.languages.CompletionItemKind.Keyword,
|
|
|
|
|
|
insertText: item,
|
2022-11-25 18:59:23 +08:00
|
|
|
|
} as any);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 21:08:01 +08:00
|
|
|
|
shellLan.builtins.forEach((item: any) => {
|
|
|
|
|
|
suggestions.push({
|
|
|
|
|
|
label: item,
|
|
|
|
|
|
kind: monaco.languages.CompletionItemKind.Property,
|
|
|
|
|
|
insertText: item,
|
2022-11-25 18:59:23 +08:00
|
|
|
|
} as any);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2022-11-05 21:08:01 +08:00
|
|
|
|
return {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
suggestions: suggestions,
|
2022-11-05 21:08:01 +08:00
|
|
|
|
};
|
2023-07-06 20:59:22 +08:00
|
|
|
|
},
|
|
|
|
|
|
});
|
2022-11-05 21:08:01 +08:00
|
|
|
|
};
|
2022-11-25 18:59:23 +08:00
|
|
|
|
|
|
|
|
|
|
const format = () => {
|
|
|
|
|
|
/*
|
|
|
|
|
|
触发自动格式化;
|
|
|
|
|
|
*/
|
2023-07-06 20:59:22 +08:00
|
|
|
|
monacoEditorIns.trigger('', 'editor.action.formatDocument', '');
|
|
|
|
|
|
};
|
2022-11-25 18:59:23 +08:00
|
|
|
|
|
2023-09-19 23:00:32 +08:00
|
|
|
|
const getEditor = () => {
|
|
|
|
|
|
return monacoEditorIns;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({ getEditor, format });
|
2022-11-05 15:13:40 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
|
.monaco-editor {
|
|
|
|
|
|
.code-mode-select {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
right: 10px;
|
|
|
|
|
|
top: 10px;
|
|
|
|
|
|
max-width: 130px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-11-22 00:34:42 +08:00
|
|
|
|
</style>
|