以前項目是用ace編輯器的,但是總有些不敬人意的地方。前端事件看見的VS Code編輯器Monaco Editor准備更換下,下面介紹一些使用中遇到的一點問題。代碼提示
1.項目引用
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
項目中引用了editor.api.js,但是這個文件不包含一些默認的語言和插件,所以在使用的時候,還需要我們自己import
import 'monaco-editor/esm/vs/basic-languages/mysql/mysql.contribution'; import 'monaco-editor/esm/vs/editor/contrib/suggest/suggestController.js'; import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js'; import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js';
如果嫌麻煩我們也可以使用editor.all.js
2.react組件封裝
可以使用github項目,https://github.com/superRaytin/react-monaco-editor,也可以拿來項目里面改下,比較簡單
其實在componentDidUpdate的方法中,使用這個方法this.editor.setValue,undo不能撤回上次的value,
如果項目中有別的需求可以使用
var model = this.editor.getModel(); model.pushEditOperations( [], [ { range: model.getFullModelRange(), text: prevProps.value } ] );
onDidChangeModelContent,方法產生的監聽需要在組件銷毀的時候dispose下
3.編輯器參數配置
查看以下代碼文件 monaco-editor/esm/vs/editor/common/config/commonEditorConfig.js
minimap,scrollBeyondLastLine,Suggestion,snippet相關的
4.在光標處插入文本
跟ace使用差別比較大
var position = editor.getPosition(); editor.executeEdits('', [ { range: { startLineNumber: position.lineNumber, startColumn: position.column, endLineNumber: position.lineNumber, endColumn: position.column }, text: 'test' } ]);
5.代碼自動完成
下面代碼是引用了monaco-editor自帶的mysql的語法高亮里面的定義,設置的代碼提示
import { language as mysqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js'; monaco.languages.registerCompletionItemProvider('mysql', { provideCompletionItems: function(model, position) { // get editor content before the pointer var textUntilPosition = model.getValueInRange({ startLineNumber: position.lineNumber, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column }); var match = textUntilPosition.match(/(\S+)$/); if (!match) return []; match = match[0].toUpperCase(); var suggestions = []; mysqlLanguage.keywords.forEach(item => { if (item.indexOf(match) !== -1) { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Keyword, insertText: item }); } }); mysqlLanguage.operators.forEach(item => { if (item.indexOf(match) !== -1) { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Operator, insertText: item }); } }); mysqlLanguage.builtinFunctions.forEach(item => { if (item.indexOf(match) !== -1) { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Function, insertText: item }); } }); return { suggestions }; } });
如果你想增加代碼片段,也可以直接添加下面的信息
{ label: 'ifelse', kind: monaco.languages.CompletionItemKind.Snippet, insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'), insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'If-Else Statement' }
使用中發現,編輯器自帶的默認的變量提示,在提供了自定義的registerCompletionItemProvider后會失效,暫時沒找到什么好方法
默認的變量提示 monaco-editor/esm/vs/editor/common/services/editorWorkerServiceImpl.js
_this._register(modes.CompletionProviderRegistry.register('*', new WordBasedCompletionItemProvider(_this._workerManager, configurationService, _this._modelService)));
monaco-editor/esm/vs/editor/contrib/suggest/suggest.js
會根據注冊的Provider的分組,語言默認的優先級不高,沒有分到一組,找到了結果就不再會執行下面的組
6.語法高亮
官網的文檔蠻詳細的,和ace的差別不是很大,雖然我也沒怎么看懂
https://microsoft.github.io/monaco-editor/monarch.html
可以在項目中找個相似的語言抄抄改改,結合着看官方文檔
monaco-editor/esm/vs/basic-languages
高亮的樣式需要在定義主題來對應,不是完全通過css處理的
monaco.editor.defineTheme('ace', { base: 'vs', inherit: true, rules: [ { token: '', foreground: '5c6773' }, { token: 'invalid', foreground: 'ff3333' }, { token: 'emphasis', fontStyle: 'italic' }, { token: 'strong', fontStyle: 'bold' }, { token: 'variable', foreground: '5c6773' }, { token: 'variable.predefined', foreground: '5c6773' }, { token: 'constant', foreground: 'f08c36' }, ] });
7.語法檢測
語法檢測,就需要編寫對應的語法編譯邏輯,這個不同語言不同的寫法,一些也有現成的。這邊主要介紹下怎么和編輯器交互,顯示錯誤信息
錯誤信息hover顯示需要插件 import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js';
//清楚mark monaco.editor.setModelMarkers(model, 'eslint', []); //添加mark monaco.editor.setModelMarkers(model, 'eslint', [ { startLineNumber: 2, endLineNumber: 2, startColumn: 2, endColumn: 4, message: 'Syntax error', severity: 3, source: 'ESLint', code: 'asdasdas' } ]);
對於上面的一些配置信息會對提示框有什么影響可以看下面的源碼
monaco-editor/esm/vs/editor/common/services/modelServiceImpl.js ModelMarkerHandler._createDecorationOption

ModelMarkerHandler._createDecorationOption = function (marker) { var className; var color = undefined; var zIndex; var inlineClassName = undefined; switch (marker.severity) { case MarkerSeverity.Hint: if (marker.tags && marker.tags.indexOf(1 /* Unnecessary */) >= 0) { className = "squiggly-unnecessary" /* EditorUnnecessaryDecoration */; } else { className = "squiggly-hint" /* EditorHintDecoration */; } zIndex = 0; break; case MarkerSeverity.Warning: className = "squiggly-warning" /* EditorWarningDecoration */; color = themeColorFromId(overviewRulerWarning); zIndex = 20; break; case MarkerSeverity.Info: className = "squiggly-info" /* EditorInfoDecoration */; color = themeColorFromId(overviewRulerInfo); zIndex = 10; break; case MarkerSeverity.Error: default: className = "squiggly-error" /* EditorErrorDecoration */; color = themeColorFromId(overviewRulerError); zIndex = 30; break; } if (marker.tags) { if (marker.tags.indexOf(1 /* Unnecessary */) !== -1) { inlineClassName = "squiggly-inline-unnecessary" /* EditorUnnecessaryInlineDecoration */; } } var hoverMessage = null; var message = marker.message, source = marker.source, relatedInformation = marker.relatedInformation, code = marker.code; if (typeof message === 'string') { message = message.trim(); if (source) { if (/\n/g.test(message)) { if (code) { message = nls.localize('diagAndSourceAndCodeMultiline', "[{0}]\n{1} [{2}]", source, message, code); } else { message = nls.localize('diagAndSourceMultiline', "[{0}]\n{1}", source, message); } } else { if (code) { message = nls.localize('diagAndSourceAndCode', "[{0}] {1} [{2}]", source, message, code); } else { message = nls.localize('diagAndSource', "[{0}] {1}", source, message); } } } hoverMessage = new MarkdownString().appendCodeblock('_', message); if (!isFalsyOrEmpty(relatedInformation)) { hoverMessage.appendMarkdown('\n'); for (var _i = 0, _a = relatedInformation; _i < _a.length; _i++) { var _b = _a[_i], message_1 = _b.message, resource = _b.resource, startLineNumber = _b.startLineNumber, startColumn = _b.startColumn; hoverMessage.appendMarkdown("* [" + basename(resource.path) + "(" + startLineNumber + ", " + startColumn + ")](" + resource.toString(false) + "#" + startLineNumber + "," + startColumn + "): "); hoverMessage.appendText("" + message_1); hoverMessage.appendMarkdown('\n'); } hoverMessage.appendMarkdown('\n'); } }