Anmerkung des Herausgebers
Im
letzten Artikel haben wir über die Veröffentlichung des Voximplant-Kontrollfelds gesprochen, nicht zu vergessen die aktualisierte IDE. Heute widmen wir diesem Tool einen eigenen
Longride - unser Kollege
Geloosa hat sowohl den Prozess der Auswahl einer Technologie als auch die Implementierung mit Registerkarten, automatischer Vervollständigung und benutzerdefinierten Stilen sorgfältig beschrieben. Setzen Sie sich bequemer hin, legen Sie den Rest Ihrer Angelegenheiten beiseite und gehen Sie zum Gerät, wo die Eingeweide von Monaco auf die Neugierigen warten - rutschen Sie nicht aus, es gibt viele davon :) Viel Spaß beim Lesen.
Welche Bibliothek soll für den Code-Editor ausgewählt werden?
Npm erzeugt mehr als 400 Ergebnisse für den Code-Editor. Zum größten Teil sind dies UI-Wrapper einiger der beliebtesten Bibliotheken, die für ein bestimmtes Framework oder Projekt erstellt wurden, Plugins für dieselben Bibliotheken oder deren Gabeln mit Änderungen für sich selbst und auch nicht zum Bearbeiten von Code im Browser, der einfach über Schlüsselwörter in die Suchergebnisse gelangt ist. Zum Glück ist die Auswahl also viel enger. Noch ein paar
Bibliotheken - a la
CodeFlask , leichtgewichtig, aber nicht sehr funktional, entwickelt für kleine Schnipsel und interaktive Beispiele, aber nicht für eine vollwertige Web-IDE mit der Funktionalität, die wir in Desktop-Editoren gewohnt sind.
Am Ende haben wir 3 Bibliotheken zur Auswahl:
Ace ,
CodeMirror und
Monaco Editor . Das früheste davon, CodeMirror, war eine private Initiative des Berliner
Marijn Haverbeke , der in seinem Online-Tutorial
Eloquent JavaScript einen Übungscode-Editor benötigte. Die erste Version des Editors wurde 2007 veröffentlicht. 2010 wurde die erste Version von Ace auf der JSConf.eu im selben Berlin vorgestellt, die Ajax.org dann für seine Cloud-IDE Cloud9 entwickelte (tatsächlich steht Ace für Ajax.org Cloud9 Editor). Im Jahr 2016 wurde Cloud9 von Amazon gekauft und ist jetzt Teil von AWS. Der neueste Monaco Editor ist Bestandteil von VS Code und wurde Ende 2015 von Microsoft veröffentlicht.
Jeder Editor hat seine eigenen Stärken und Schwächen, jeder wird in mehr als einem großen Projekt verwendet. Beispielsweise wird CodeMirror in den Chrome- und Firefox-Entwicklertools, einer IDE in Bitbucket, in RunKit in npm verwendet. Ass - an der Codecademy, Khan Academy, MODX; Monaco - in der GitLab IDE und CodeSandbox. Das folgende Vergleichsdiagramm kann Ihnen bei der Auswahl der für Ihr Projekt am besten geeigneten Bibliothek helfen.
| Bibliotheken |
| Ass | CodeMirror | Monaco |
Entwickler | Cloud9 IDE (Ajax.org), jetzt Teil von AmazonMozilla | Marijn Haverbeke | Microsoft |
Browser-Unterstützung | Firefox ^ 3.5 Chrome Safari ^ 4.0 IE ^ 8.0 Oper ^ 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 |
Sprachunterstützung (Syntaxhervorhebung) | > 120 | > 100 | > 20 |
Anzahl der Zeichen in neueste Versionen auf cndjs.com | 366 608 (v1.4.3) | 394,269 (v5.44.0) | 2,064,949 (v0,16,2) |
Das Gewicht der neuesten Versionen, gzip | 2,147 KB | 1,411 KB | 10.898 KB |
Rendern | Dom | Dom | DOM und teilweise <canvas> (zum Scrollen und Minikarten) |
Die Dokumentation | 7 von 10: keine Suche, nicht immer klar dass Methoden zurückkehren, gibt es Zweifel in Vollständigkeit und Relevanz (Nicht alle Links funktionieren im Dock) | 6 von 10: zusammengeführt mit Benutzerhandbuch, Suche mit Strg + F, Es bestehen Zweifel an der Vollständigkeit | 9 von 10: schön, mit Suche und Querverweis -1 Punkt mangels Erklärung zu einigen Flags, deren Anwendung nicht ganz offensichtlich aus dem Namen |
Schnellstart-Demos | How-to - Textdokumente mit Codebeispielen, separat gibt es demos mit codebeispielen (wahr, sie sind auf verschiedenen Seiten verstreut, nicht jeder arbeitet und sie werden am einfachsten über Google gesucht), Es gibt eine Demo, in der Sie verschiedene Funktionen berühren können. Es wird jedoch vorgeschlagen, sie über UI-Steuerelemente zu verwalten. das heißt, dann müssen wir immer noch separat nach Methoden suchen um sie zu verbinden | How-to sind wirklich arm Im Grunde ist alles auf Github verstreut und Stackoverflow, aber es gibt Demos von Funktionen mit Beispielen Code für ihre Implementierung | Kombiniert im Format eines Spielplatzes: Code mit Kommentaren und einer Reihe von Demos können Sie sofort versuchen und bewerten viele Möglichkeiten |
Gemeinschaftsaktivität | Durchschnitt | Hoch | Durchschnitt |
Entwickleraktivität | Durchschnitt | Durchschnitt | Hoch |
Es macht keinen Sinn, Bibliotheken nach Größe zu vergleichen, da alles davon abhängt, was und wie eine Verbindung für ein bestimmtes Projekt hergestellt werden soll: Laden Sie die fertige Datei mit einem der Builds (die ebenfalls variieren) oder führen Sie das npm-Paket über eine Art Kollektor aus. Und das Wichtigste ist, wie oft der Editor verwendet wird: ob alle Stile und Themen geladen sind, wie viele und welche Add-Ons und Plug-Ins verwendet werden. In CodeMirror sind beispielsweise die meisten Funktionen, die in Monaco und Ace sofort verfügbar sind, nur mit Add-Ons verfügbar. Die Tabelle zeigt die Anzahl der Zeichen in den letzten Versionen auf dem CDN und das Gewicht ihrer komprimierten Dateien, um eine allgemeine Vorstellung davon zu erhalten, um welche Bestellungen es sich handelt.
Alle Bibliotheken verfügen über ungefähr die gleichen Grundfunktionen: automatische Formatierung von Code, Falten von Linien, Ausschneiden / Kopieren / Einfügen, Hotkeys, die Möglichkeit, neue Syntaxen zum Hervorheben und Ordnen hinzuzufügen, Syntaxprüfung (in CodeMirror nur über Add-Ons, in Ace bisher nur für JavaScript / CoffeeScript / CSS / XQuery), QuickInfos und automatische Vervollständigung (in CodeMirror - über Add-Ons), erweiterte Suche nach Code (in CodeMirror - über Add-Ons), Methoden zum Implementieren von Registerkarten und Split-Modus, Diff-Modus und ein Zusammenführungs-Tool (in CodeMirror) - entweder mit Vor- und Nachteilen in einem Fenster oder mit zwei Feldern durch ein Addon, Ace - Separate Lieb). Aufgrund seines Alters wurden viele Add-Ons für CodeMirror geschrieben, deren Anzahl sich jedoch sowohl auf das Gewicht als auch auf die Geschwindigkeit des Editors auswirkt. Monaco kann viele Dinge sofort erledigen und meiner Meinung nach besser und in größerem Umfang als Ace und CodeMirror.
Wir waren aus mehreren Gründen in Monaco:
- Die am weitesten entwickelten Tools, die wir für unser Projekt als kritisch erachtet haben:
- IntelliSense - Tipps und automatische Vervollständigung;
- Smart Code Navigation im Kontextmenü und durch Minikarte;
- Zwei-Panel-Diff-Modus sofort einsatzbereit.
- Geschrieben in TypeScript. Unser Control Panel ist in Vue + Typescript geschrieben, daher war die TS-Unterstützung wichtig. Übrigens unterstützt Ace kürzlich auch TS, aber es wurde ursprünglich in JS geschrieben. Für CodeMirror gibt es Typen in DefinitelyTyped .
- Es wird am aktivsten darin entwickelt (möglicherweise weil es vor nicht allzu langer Zeit veröffentlicht wurde), Fehler werden schneller behoben und Pool-Anfragen werden bekämpft. Zum Vergleich hatten wir mit CodeMirror eine traurige Erfahrung, als Fehler jahrelang nicht behoben wurden und wir eine Krücke auf eine Krücke legten und eine Krücke fuhren.
- Praktische automatisch generierte Dokumentation (die Hoffnung auf Vollständigkeit gibt) mit Querverweisen zwischen Schnittstellen und Methoden.
- Nach unserem Geschmack die schönste Benutzeroberfläche (wahrscheinlich auch im Zusammenhang mit der Erstellungszeit) und eine übersichtliche API.
- Nachdem Ace und CodeMirror Freunde der Entwickler gefragt hatten, welcher der Editoren die meisten Kopfschmerzen verursachte, waren sie führend.
Separat sollte über die Geschwindigkeit der Arbeit gesagt werden. Das kostspielige Parsen findet in einem parallelen Worker-Thread statt. Außerdem sind alle Berechnungen durch die Größe des Ansichtsfensters begrenzt (alle Typen, Farben und Renderings werden nur für die sichtbaren Linien berechnet). Es beginnt erst zu bremsen, wenn der Code 100.000 Zeilen enthält - Eingabeaufforderungen können für einige Sekunden berechnet werden. Ace, das auch Worker für Heavy Computing einsetzt, erwies sich als schneller: Bei Code gleicher Länge werden Eingabeaufforderungen fast sofort angezeigt und es werden schnell 200.000 Zeilen verarbeitet (auf der offiziellen Website wird angegeben, dass selbst 4 Millionen Zeilen kein Problem darstellen sollten Die Schrauben wurden beschleunigt, die Eingabe wurde langsamer und die Eingabeaufforderungen verschwanden nach der ersten Million. CodeMirror, bei dem es keine parallelen Berechnungen gibt, kann solche Volumes kaum abrufen: Es kann sowohl Text- als auch Syntaxhervorhebungen flackern. Da 100.000 Zeilen in einer Datei in der realen Welt selten sind, haben wir dies ignoriert. Selbst mit 40-50 Tausend Zeilen leistet Monaco hervorragende Arbeit.
Anschließen von Monaco und Verwenden grundlegender Funktionen (z. B. Integration in Vue)
Verbindung
Hier werde ich Codebeispiele aus vue-Komponenten geben und die entsprechende Terminologie verwenden. All dies lässt sich jedoch problemlos auf jedes andere Framework oder reine JS portieren.
Der Quellcode von Monaco kann von der offiziellen Website heruntergeladen und in Ihr Projekt eingefügt werden. Sie können ihn von CDN abholen und über npm eine Verbindung zum Projekt herstellen. Ich werde über die dritte Option sprechen und mit Webpack bauen.
Wir haben einen Monaco-Editor und ein Plug-In für die Montage bereitgestellt:
npm i -S monaco-editor npm i -D monaco-editor-webpack-plugin
Fügen Sie in der Webpack-Konfiguration Folgendes hinzu:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = {
Wenn Sie zum Erstellen Vue und vue-cli-service verwenden, fügen Sie vue.config.js hinzu:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = {
Wenn Sie nicht alle Sprachen und Funktionen von Monaco benötigen, um die Größe des Bundles zu verringern, können Sie
MonacoWebpackPlugin
Objekt mit den Einstellungen an
MonacoWebpackPlugin
:
new MonacoWebpackPlugin({ output: '',
Eine vollständige Liste der Funktionen und Sprachen für das Plugin finden Sie
hier .
Erstellen und Anpassen eines Editors
Wir importieren den
editor
und rufen
editor.create(el: HTMLElement, config?: IEditorConstructionOptions)
, wobei wir das DOM-Element übergeben, in dem wir den Editor als erstes Argument erstellen möchten.
In der Editor-Komponente:
<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>
Der Container für den Editor muss unbedingt die Höhe so einstellen, dass er nicht Null wird. Wenn Sie den Editor in einem leeren Div (mit einer Höhe von Null - Ihrem K.O.) erstellen, schreibt Monaco dieselbe Höhe in einem Inline-Stil im Editorfenster.
Das zweite optionale Argument für
editor.create
ist die Editor-Konfiguration. Es gibt mehr als hundert Optionen. Eine vollständige Beschreibung der
IEditorConstructionOptions- Schnittstelle finden Sie in der Dokumentation.
In einem Beispiel legen wir die Sprache, das Thema und den Anfangstext fest und aktivieren den Zeilenumbruch (standardmäßig werden sie nicht umbrochen):
const config = { value: `function hello() { alert('Hello world!'); }`, language: 'javascript', theme: 'vs-dark', wordWrap: 'on' }; this.editor = editor.create(this.$refs.editor, config);
Die Funktion
editor.create
gibt ein Objekt mit der
IStandaloneCodeEditor- Schnittstelle zurück. Über diese Funktion können Sie jetzt alles steuern, was im Editor geschieht, einschließlich der Änderung der Anfangseinstellungen:
Nun zum Schmerz:
updateOptions
akzeptiert ein Objekt mit der
IEditorOptions- Schnittstelle, nicht IEditorConstructionOptions. Sie unterscheiden sich geringfügig: IEditorConstructionOptions ist breiter und enthält die Eigenschaften dieser Editorinstanz sowie einige globale.
updateOptions
werden über
updateOptions
global geändert - über die Methoden des globalen
editor
. Dementsprechend ändern sich diejenigen, die sich global ändern, für alle Instanzen. Unter diesen Optionen ist
theme
. Erstellen Sie 2 Instanzen mit unterschiedlichen Themen. y von beiden wird das letzte sein (hier dunkel). Die globale Methode
editor.setTheme('vs')
ändert auch das Thema für beide. Dies wirkt sich auch auf die Fenster aus, die sich auf einer anderen Seite Ihres SPA befinden. Es gibt nur wenige solcher Orte, aber Sie müssen ihnen folgen.
<template> <div ref='editor1' class='editor'></div> <div ref='editor2' class='editor'></div> </template> <script> </script>
Editor löschen
Wenn Sie ein Monaco-Fenster zerstören, müssen Sie die
dispose
Methode aufrufen. Andernfalls werden nicht alle Listener gelöscht und die danach erstellten Fenster funktionieren möglicherweise nicht richtig und reagieren mehrmals auf einige Ereignisse:
beforeDestroy() { this.editor && this.editor.dispose(); }
Tabs
Die im Datei-Editor geöffneten Registerkarten verwenden dasselbe Monaco-Fenster. Um zwischen ihnen zu wechseln, werden IStandaloneCodeEditor-Methoden verwendet:
getModel
zum Speichern und
setModel
zum Aktualisieren des Editor-Modells. Das Modell speichert Text, Cursorposition und Aktionsverlauf zum Rückgängigmachen. Um ein Modell einer neuen Datei zu erstellen, wird die globale Methode
editor.createModel(text: string, language: string)
verwendet. Wenn die Datei leer ist, können Sie kein Modell erstellen und
null
an
setModel
:
Code anzeigen <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) { </script>
Diff-Modus
Für den Diff-Modus müssen Sie beim Erstellen des Editorfensters eine andere
editor
Methode verwenden -
createDiffEditor
:
<template> <div ref='diffEditor' class='editor'></div> </template> // ... mounted() { this.diffEditor = editor.createDiffEditor(this.$refs.diffEditor, config); } // ...
Es werden dieselben Parameter wie
editor.create
, aber die Konfiguration sollte über eine
IDiffEditorConstructionOptions- Schnittstelle verfügen, die sich geringfügig von der regulären Editor-Konfiguration unterscheidet. Insbesondere hat sie keinen
value
. Vergleichstexte werden nach dem Erstellen des zurückgegebenen
IStandaloneDiffEditor über
setModel festgelegt :
this.diffEditor.setModel({ original: editor.createModel('const a = 1;', 'javascript'), modified: editor.createModel('const a = 2;', 'javascript') });
Kontextmenü, Befehlspalette und Tastenkombinationen
Monaco verwendet sein Nicht-Browser-Kontextmenü, in dem es eine intelligente Navigation, einen Multi-Cursor zum Ändern aller Vorkommen und eine Befehlspalette wie in VS Code (Befehlspalette) mit einer Reihe nützlicher Befehle und Verknüpfungen gibt, die das Schreiben von Code beschleunigen:
Monaco Kontextmenü
Monaco Befehlspalette
Das Kontextmenü wird durch die
addAction
Methode erweitert (sie ist sowohl in
IStandaloneCodeEditor
als auch in
IStandaloneCodeEditor
IStandaloneDiffEditor
), die ein
IActionDescriptor- Objekt akzeptiert:
Um eine Verknüpfung nur an eine Aktion zu binden, ohne sie im Kontextmenü anzuzeigen, wird dieselbe Methode verwendet, nur die
contextMenuGroupId
der Aktion wird nicht angegeben:
Die Befehlspalette enthält alle hinzugefügten Aktionen.
Tipps und automatische Vervollständigung
Für diese Zwecke verwendet Monaco
IntelliSense , was cool ist. Sie können auf den Screenshots den Link lesen und sehen, wie viele nützliche Informationen er anzeigen kann. Wenn Ihre Sprache noch keine automatische Vervollständigung hat, können Sie diese über
registerCompletionItemProvider hinzufügen. Und für JS und TS gibt es bereits eine
addExtraLib
Methode, mit der Sie TypeScript-Definitionen für
addExtraLib
laden und automatisch vervollständigen können:
Im ersten Parameter übergibt die Zeile die Definitionen, im zweiten optional den Namen der Bibliothek.
Benutzerdefinierte Sprachen und Themen
Monaco verfügt über ein
Monarch- Modul zur Bestimmung der Syntax seiner Sprachen. Die Syntax wird ganz normal beschrieben: Die Entsprechung zwischen den für diese Sprache charakteristischen Stammgästen und Token wird festgelegt.
Sie können auch ein Thema für Ihre Token erstellen - ein Objekt mit der
IStandaloneThemeData- Oberfläche - und es im globalen
editor
installieren:
Nun sieht der Text in der beschriebenen Sprache folgendermaßen aus:
Sie können diese Funktion so weit anwenden, wie Sie es sich vorstellen. Zum Beispiel haben wir in unserem Panel einen Anrufprotokoll-Viewer für Entwickler erstellt. Protokolle sind oft lang und unverständlich. Wenn sie jedoch mit Syntaxhervorhebung, intelligenter Suche, Falten / Erweitern von Zeilen, den erforderlichen Befehlen (z. B. Prettify-Parameter) angezeigt werden, alle Anrufleitungen anhand ihrer ID hervorgehoben werden oder die Zeit im Protokoll in eine andere Zeitzone übersetzt wird, graben Sie dann es wird in ihnen viel einfacher (der Screenshot ist anklickbar):
Fazit
Zusammenfassend werde ich sagen, dass Monaco Feuer ist. Nach monatelanger Arbeit mit ihm habe ich außergewöhnlich angenehme Erinnerungen. Wenn Sie einen Editor für den Code auswählen, gehen Sie auf dessen
Spielplatz und spielen Sie mit dem Code herum. Sehen Sie, was er sonst noch tun kann. Vielleicht ist das genau das, wonach Sie suchen.