Libraries | |||
Ace | Codemirror | Monaco | |
Developer | Cloud9 IDE (Ajax.org), now part of AmazonMozilla | Marijn haverbeke | Microsoft |
Browser support | Firefox ^ 3.5 Chrome Safari ^ 4.0 IE ^ 8.0 Opera ^ 11.5 | Firefox ^ 3.0 Chrome Safari ^ 5.2 IE ^ 8.0 Opera ^ 9.2 | Firefox ^ 4.0 Chrome Safari (v -?) IE ^ 11.0 Opera ^ 15.0 |
Language support (syntax highlighting) | > 120 | > 100 | > 20 |
Number of characters in latest versions on cndjs.com | 366 608 (v1.4.3) | 394,269 (v5.44.0) | 2,064,949 (v0.16.2) |
The weight of the latest versions, gzip | 2.147 KB | 1.411 KB | 10.898 KB |
Rendering | Dom | Dom | DOM and partly <canvas> (for scrolling and minimap) |
Documentation | 7 out of 10: no search, not always clear That methods are returned, there are doubts in completeness and relevance (not all links work in the dock) | 6 out of 10: merged with the user, Search Ctrl + F, there are doubts about completeness | 9 out of 10: beautiful, with search and cross-references -1 point for the lack of explanation to some flags whose use not quite obvious from the title |
Quickstart demos | How-to - text documents with code examples, separately there are demos with code examples (although they are scattered across different pages, not everyone works and they are most easily searched through Google), there is a demo where you can feel different features, but it is proposed to control them through UI controls, that is, then you need to separately look for methods to connect them | How-to downright poor basically everything is scattered on github and stackoverflow, but there are feature demos with examples code to implement them | Merged in playground format: code with comments and a number of demos, you can immediately try and evaluate many possibilities |
Community activity | Average | High | Average |
Developer Activity | Average | Average | High |
npm i -S monaco-editor npm i -D monaco-editor-webpack-plugin
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = { // ... plugins: [ // ... new MonacoWebpackPlugin() ] };
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = { // ... configureWebpack: (config) => { // ... config.plugins.push(new MonacoWebpackPlugin()); } };
MonacoWebpackPlugin
object with the settings: new MonacoWebpackPlugin({ output: '', // , languages: ['markdown'], // , features: ['format', 'contextmenu'] // })
editor
and call editor.create(el: HTMLElement, config?: IEditorConstructionOptions)
, passing as the first argument the DOM element in which we want to create an editor. <template> <div ref='editor' class='editor'></div> </template> <script> import {editor} from 'monaco-editor'; import {Component, Prop, Vue} from 'vue-property-decorator'; @Component() export default class Monaco extends Vue { private editor = null; mounted() { this.editor = editor.create(this.$refs.editor); } } </script> <style> .editor { margin: auto; width: 60vw; height: 200px; } </style>
editor.create
is the editor's config. It has more than a hundred options, a full description of the IEditorConstructionOptions interface is in the documentation. const config = { value: `function hello() { alert('Hello world!'); }`, language: 'javascript', theme: 'vs-dark', wordWrap: 'on' }; this.editor = editor.create(this.$refs.editor, config);
editor.create
function returns an object with the IStandaloneCodeEditor interface. Through it, you can now manage everything that happens in the editor, including changing the initial settings: // read-only this.editor.updateOptions({wordWrap: 'off', readOnly: true});
updateOptions
takes an object with the IEditorOptions interface, not IEditorConstructionOptions. They are a little different: IEditorConstructionOptions is wider, it includes properties of this instance of the editor and some global ones. The properties of the instance are changed via updateOptions
, global - through the methods of the global editor
. And accordingly, those that change globally, change for all instances. Among such parameters is the theme
. Create 2 instances with different themes; y both will be the one given in the last one (here it is dark). The global editor.setTheme('vs')
method will also change the subject for both. This will affect even those windows that are on another page of your SPA. There are few such places, but you have to keep an eye on them. <template> <div ref='editor1' class='editor'></div> <div ref='editor2' class='editor'></div> </template> <script> // ... this.editor1 = editor.create(this.$refs.editor1, {theme: 'vs'}); this.editor2 = editor.create(this.$refs.editor2, {theme: 'vs-dark'}); // ... </script>
dispose
method must be called, otherwise all listeners will not be cleared and the windows created after this may not work correctly, reacting to certain events several times: beforeDestroy() { this.editor && this.editor.dispose(); }
getModel
for saving and setModel
for updating the editor model. The model stores text, cursor position, history of actions for undo-redo. To create a new file model, the global method editor.createModel(text: string, language: string)
. If the file is empty, you can not create a model and pass null
to setModel
: <template> <div class='tabs'> <div class='tab' v-for="tab in tabs" :key'tab.id' @click='() => switchTab(tab.id)'> {{tab.name}} </div> </div> <div ref='editor' class='editor'></div> </template> <script> import {editor} from 'monaco-editor'; import {Component, Prop, Vue} from 'vue-property-decorator'; @Component() export default class Monaco extends Vue { private editor = null; private tabs: [ {id: 1, name: 'tab 1', text: 'const tab = 1;', model: null, active: true}, {id: 2, name: 'tab 2', text: 'const tab = 2;', model: null, active: false} ]; mounted() { this.editor = editor.create(this.$refs.editor); } private switchTab(id) { const activeTab = this.tabs.find(tab => tab.id === id); if (!activeTab.active) { // ( ) const model = !activeTab.model && activeTab.text ? editor.createModel(activeTab.text, 'javascript') : activeTab.model; // this.tabs = this.tabs.map(tab => ({ ...tab, model: tab.active ? this.editor.getModel() : tab.model, active: tab.id === id })); // this.editor.setModel(model); } } </script>
editor
method when creating the editor window - createDiffEditor
: <template> <div ref='diffEditor' class='editor'></div> </template> // ... mounted() { this.diffEditor = editor.createDiffEditor(this.$refs.diffEditor, config); } // ...
editor.create
, but the config must have the IDiffEditorConstructionOptions interface, which is somewhat different from the configuration of the regular editor, in particular, it has no value
. Texts for comparison are set after creating the returned IStandaloneDiffEditor via setModel : this.diffEditor.setModel({ original: editor.createModel('const a = 1;', 'javascript'), modified: editor.createModel('const a = 2;', 'javascript') });
Monaco context menu
Monaco command palette
addAction
method (both in IStandaloneCodeEditor
and IStandaloneDiffEditor
), which accepts an IActionDescriptor object: // ... <div ref='diffEditor' :style='{display: isDiffOpened ? "block" : "none"}'></div> // ... // KeyCode KeyMod import {editor, KeyCode, KeyMod} from "monaco-editor"; // ... private editor = null; private diffEditor = null; private isDiffOpened = false; private get activeTab() { return this.tabs.find(tab => tab.active); } mounted() { this.diffEditor = editor.createDiffEditor(this.$refs.diffEditor); this.editor = editor.create(this.$refs.editor); this.editor.addAction({ // , . contextMenuGroupId: '1_modification', // : 1 - 'navigation', 2 - '1_modification', 3 - '9_cutcopypaste'; // contextMenuOrder: 3, // label: 'Show diff', id: 'showDiff', keybindings: [KeyMod.CtrlCmd + KeyMod.Shift + KeyCode.KEY_D], // // , // run: this.showDiffEditor }); } // private showDiffEditor() { this.diffEditor.setModel({ original: this.activeTab.initialText, modified: this.activeTab.editedText }); this.isDiffOpened = true; }
contextMenuGroupId
not specified for the action: // ... // private myActions = [ { contextMenuGroupId: '1_modification', contextMenuOrder: 3, label: <string>this.$t('scenarios.showDiff'), id: 'showDiff', keybindings: [KeyMod.CtrlCmd + KeyMod.Shift + KeyCode.KEY_D], run: this.showDiffEditor }, // , Ctrl + C + L { label: 'Get content length', id: 'getContentLength', keybindings: [KeyMod.CtrlCmd + KeyCode.Key_C + KeyCode.Key_L], run: () => this.editor && alert(this.editor.getValue().length) } ]; mounted() { this.editor = editor.create(this.$refs.editor); this.myActions.forEach(this.editor.addAction); // }
addExtraLib
method that allows you to load definitions into TypeScript for hints and autocomplete: // ... import {languages} from "monaco-editor"; // ... // , private myAddedLib = null; mounted() { // languages Monaco this.myAddedLib = languages.typescript.javascriptDefaults.addExtraLib('interface MyType {prop: string}', 'myLib'); } beforeDestroy() { // , this.myAddedLib && this.myAddedLib.dispose(); }
// ... // , : private myLanguage = { defaultToken: 'text', // , brackets: [{ open: '(', close: ')', token: 'bracket.parenthesis' }], // , , keywords: [ 'autumn', 'winter', 'spring', 'summer' ], // tokenizer: { root: [{ regex: /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/, action: { token: 'date' } }, { regex: /(boy|girl|man|woman|person)(\s[A-Za-z]+)/, action: ['text', 'variable'] } ] } }; mounted() { // languages.register({ id: 'myLanguage' }); // languages.setMonarchTokensProvider('myLanguage', this.myLanguage); // ... }
editor
: // ... private myTheme = { base: 'vs', // , inherit: true, // rules: [ {token: 'date', foreground: '22aacc'}, {token: 'variable', foreground: 'ff6600'}, {token: 'text', foreground: 'd4d4d4'}, {token: 'bracket', foreground: 'd4d4d4'} ] }; mounted() { editor.defineTheme('myTheme', this.myTheme); // ... }
Source: https://habr.com/ru/post/445390/
All Articles