CodeEditor.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <template>
  2. <div ref="dom" class="code-editor"></div>
  3. </template>
  4. <script lang="ts" setup>
  5. import { onMounted, onUnmounted, ref, watch } from 'vue';
  6. //按需引入
  7. import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
  8. import 'monaco-editor/esm/vs/editor/browser/widget/codeEditorWidget.js';
  9. import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/browser/bracketMatching.js';
  10. import 'monaco-editor/esm/vs/editor/contrib/caretOperations/browser/caretOperations.js';
  11. import 'monaco-editor/esm/vs/editor/contrib/caretOperations/browser/transpose.js';
  12. import 'monaco-editor/esm/vs/editor/contrib/clipboard/browser/clipboard.js';
  13. import 'monaco-editor/esm/vs/editor/contrib/codeAction/browser/codeActionContributions.js';
  14. import 'monaco-editor/esm/vs/editor/contrib/copyPaste/browser/copyPasteContribution.js';
  15. import 'monaco-editor/esm/vs/editor/contrib/comment/browser/comment.js';
  16. import 'monaco-editor/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js';
  17. import 'monaco-editor/esm/vs/editor/contrib/cursorUndo/browser/cursorUndo.js';
  18. import 'monaco-editor/esm/vs/editor/contrib/find/browser/findController.js';
  19. import 'monaco-editor/esm/vs/editor/contrib/folding/browser/folding.js';
  20. import 'monaco-editor/esm/vs/editor/contrib/format/browser/formatActions.js';
  21. import 'monaco-editor/esm/vs/editor/contrib/documentSymbols/browser/documentSymbols.js';
  22. import 'monaco-editor/esm/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.js';
  23. import 'monaco-editor/esm/vs/editor/contrib/hover/browser/hover.js';
  24. import 'monaco-editor/esm/vs/editor/contrib/indentation/browser/indentation.js';
  25. import 'monaco-editor/esm/vs/editor/contrib/inlayHints/browser/inlayHintsContribution.js';
  26. import 'monaco-editor/esm/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.js';
  27. import 'monaco-editor/esm/vs/editor/contrib/lineSelection/browser/lineSelection.js';
  28. import 'monaco-editor/esm/vs/editor/contrib/linesOperations/browser/linesOperations.js';
  29. import 'monaco-editor/esm/vs/editor/contrib/linkedEditing/browser/linkedEditing.js';
  30. import 'monaco-editor/esm/vs/editor/contrib/links/browser/links.js';
  31. import 'monaco-editor/esm/vs/editor/contrib/longLinesHelper/browser/longLinesHelper.js';
  32. import 'monaco-editor/esm/vs/editor/contrib/multicursor/browser/multicursor.js';
  33. import 'monaco-editor/esm/vs/editor/contrib/parameterHints/browser/parameterHints.js';
  34. import 'monaco-editor/esm/vs/editor/contrib/rename/browser/rename.js';
  35. import 'monaco-editor/esm/vs/editor/contrib/semanticTokens/browser/documentSemanticTokens.js';
  36. import 'monaco-editor/esm/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.js';
  37. import 'monaco-editor/esm/vs/editor/contrib/smartSelect/browser/smartSelect.js';
  38. import 'monaco-editor/esm/vs/editor/contrib/snippet/browser/snippetController2.js';
  39. import 'monaco-editor/esm/vs/editor/contrib/stickyScroll/browser/stickyScrollContribution.js';
  40. import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js';
  41. import 'monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestInlineCompletions.js';
  42. import 'monaco-editor/esm/vs/editor/contrib/tokenization/browser/tokenization.js';
  43. import 'monaco-editor/esm/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.js';
  44. import 'monaco-editor/esm/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.js';
  45. import 'monaco-editor/esm/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.js';
  46. import 'monaco-editor/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js';
  47. import 'monaco-editor/esm/vs/editor/contrib/wordOperations/browser/wordOperations.js';
  48. import 'monaco-editor/esm/vs/editor/contrib/wordPartOperations/browser/wordPartOperations.js';
  49. import 'monaco-editor/esm/vs/language/typescript/monaco.contribution'
  50. // import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
  51. // import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
  52. // import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
  53. import 'monaco-editor/esm/vs/language/json/monaco.contribution';
  54. import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution';
  55. import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution';
  56. import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution';
  57. import {MonacoTypeManager} from "@/services/utils";
  58. const dom = ref<any>();
  59. const { modelValue, json, language, options, hints } = defineProps<{
  60. modelValue: any;
  61. json?: boolean;
  62. language?: string;
  63. hints?: any; /** 代码提示,传入参考:[{
  64. obj:'data',
  65. properties:[{
  66. label: 'xxxx', // 显示的文本
  67. kind: 9, // 图标类型
  68. insertText: 'insertXXX', // 插入的文本
  69. detail: 'detail'
  70. }],
  71. trigger:'[' 触发条件
  72. },{
  73. obj:"ctx", // 对象
  74. type:'Pen', // 类型
  75. typeFilePath:'pen.d.ts', //从public目录加载的类型文件
  76. }]
  77. */
  78. options?: any;
  79. }>();
  80. const emit = defineEmits(['update:modelValue', 'change']);
  81. // self.MonacoEnvironment = {
  82. // getWorker(_: any, label: string) {
  83. // if (label === 'json') {
  84. // return new jsonWorker();
  85. // }
  86. // if (label === 'typescript' || label === 'javascript') {
  87. // return new tsWorker();
  88. // }
  89. // return new editorWorker();
  90. // },
  91. // };
  92. let editor: monaco.editor.IStandaloneCodeEditor;
  93. const typeManager = new MonacoTypeManager(monaco);
  94. typeManager.clearTypeFiles()
  95. onMounted(() => {
  96. let text = '';
  97. if (json) {
  98. if (modelValue) {
  99. text = JSON.stringify(modelValue);
  100. }
  101. } else {
  102. text = modelValue;
  103. }
  104. let theme = localStorage.getItem('le-theme')==='light'?'vs':'vs-dark';
  105. editor = monaco.editor.create(dom.value, {
  106. value: text,
  107. automaticLayout: true,
  108. minimap: { enabled: false },
  109. language: language || 'javascript',
  110. theme,
  111. lineNumbersMinChars:1,
  112. lineDecorationsWidth:0,
  113. ...options,
  114. });
  115. editor.onDidChangeModelContent(() => {
  116. const currenValue = editor.getValue();
  117. if (json) {
  118. let obj: any;
  119. try {
  120. obj = JSON.parse(currenValue);
  121. } catch {}
  122. emit('update:modelValue', obj);
  123. emit('change', obj);
  124. } else {
  125. emit('update:modelValue', currenValue);
  126. emit('change', currenValue);
  127. }
  128. });
  129. setTimeout(() => {
  130. editor.getAction('editor.action.formatDocument').run();
  131. }, 300);
  132. });
  133. let completionDisposables: monaco.IDisposable[] = [];
  134. watch(() => hints, (newValue) => {
  135. if (!hints) return;
  136. completionDisposables.forEach(disposable => disposable.dispose());
  137. newValue.forEach((hint: any) => {
  138. if (hint.type && !hint.typeFilePath) {
  139. typeManager.injectVariableType(hint.obj,hint.type)
  140. }else if(hint.type && hint.typeFilePath){
  141. typeManager.loadTypeFile(hint.typeFilePath,()=>{
  142. typeManager.injectVariableType(hint.obj,hint.type)
  143. })
  144. }
  145. const instance = monaco.languages.registerCompletionItemProvider('javascript', {
  146. triggerCharacters: [hint.trigger],
  147. provideCompletionItems: (model, position) => {
  148. const textUntilPosition = model.getValueInRange({
  149. startLineNumber: position.lineNumber,
  150. startColumn: 1,
  151. endLineNumber: position.lineNumber,
  152. endColumn: position.column
  153. });
  154. const match = textUntilPosition.match(new RegExp(`(\\b(${hint.obj})\\${hint.trigger})$`));
  155. if (!match) return { suggestions: [] };
  156. const properties = hint.properties;
  157. if (properties) {
  158. return {
  159. suggestions: properties.map(prop => ({
  160. label: String(prop.label),
  161. kind: prop.kind,
  162. insertText: prop.insertText,
  163. detail: prop.detail,
  164. })),
  165. dispose() {
  166. if(!hint.replace)return
  167. const line = position.lineNumber
  168. const column = position.column
  169. editor.executeEdits("", [
  170. {
  171. range: new monaco.Range(line, column - hint.obj.length - 1, line, column),
  172. text: null
  173. }
  174. ])
  175. }
  176. };
  177. }
  178. }
  179. });
  180. completionDisposables.push(instance);
  181. });
  182. }, { immediate: true });
  183. // watch(
  184. // () => modelValue,
  185. // (newValue) => {
  186. // if (editor) {
  187. // const value = editor.getValue();
  188. // newValue = json ? JSON.stringify(newValue) : newValue;
  189. // if (newValue !== value) {
  190. // editor.setValue(newValue);
  191. // }
  192. // }
  193. // }
  194. // );
  195. watch(
  196. () => options,
  197. (newValue) => {
  198. editor.updateOptions(newValue);
  199. },
  200. { deep: true }
  201. );
  202. watch(
  203. () => language,
  204. (newValue) => {
  205. monaco.editor.setModelLanguage(editor.getModel()!, newValue);
  206. }
  207. );
  208. onUnmounted(() => {
  209. completionDisposables.forEach(disposable=>{disposable.dispose()})
  210. editor?.dispose();
  211. });
  212. </script>
  213. <style lang="postcss" scoped>
  214. .code-editor {
  215. border: 1px solid var(--color-sub-border);
  216. width: 100%;
  217. min-height: 160px;
  218. :deep(.monaco-editor) {
  219. --vscode-editorGutter-background: var(--color-background-editor);
  220. --vscode-editor-background: var(--color-background-editor);
  221. .view-overlays .current-line {
  222. border: 1px solid var(--color-border-editor);
  223. }
  224. }
  225. }
  226. </style>