IDE orang normal atau mengapa kami memilih Monako

Catatan Editor


Pada artikel terakhir, kami berbicara tentang rilis panel kontrol Voximplant, tidak lupa menyebutkan IDE yang diperbarui. Hari ini kami menyediakan longride terpisah untuk alat ini - kolega kami Geloosa dengan hati-hati menjelaskan proses pemilihan teknologi dan penerapannya dengan tab, lengkapi-otomatis, dan gaya khusus. Duduk lebih nyaman, sisihkan sisa urusan Anda dan pergi ke mengatasi, di mana nyali Monaco menunggu yang penasaran - jangan terpeleset, ada banyak dari mereka :) Selamat membaca.


Pustaka mana yang akan dipilih untuk editor kode?


Npm menghasilkan 400+ hasil untuk editor kode. Untuk sebagian besar, ini adalah pembungkus UI dari beberapa lib paling populer yang dibuat untuk kerangka kerja atau proyek tertentu, plug-in untuk lib yang sama atau garpu mereka dengan modifikasi untuk diri mereka sendiri, serta tidak untuk mengedit kode di browser, mereka hanya masuk ke output dengan kata kunci. Jadi, untungnya, pilihannya jauh lebih sempit. Beberapa lib lagi - ala CodeFlask , ringan, tetapi tidak terlalu fungsional, dirancang untuk cuplikan kecil dan contoh interaktif, tetapi tidak untuk IDE web lengkap dengan fungsi yang biasa kita gunakan dalam editor desktop.

Pada akhirnya, kami memiliki 3 perpustakaan untuk dipilih: Ace , CodeMirror dan Editor Monaco . Yang paling awal, CodeMirror, adalah inisiatif pribadi oleh Berliner Marijn Haverbeke , yang membutuhkan editor kode latihan dalam tutorial online-nya, Eloquent JavaScript . Versi pertama editor dirilis pada 2007. Pada tahun 2010, versi pertama Ace dipresentasikan di JSConf.eu di Berlin yang sama, yang kemudian dikembangkan oleh Ajax.org untuk cloud-nya IDE Cloud9 (pada kenyataannya, Ace adalah singkatan dari Ajax.org Cloud9 Editor). Pada 2016, Cloud9 dibeli oleh Amazon dan sekarang menjadi bagian dari AWS. Yang terbaru, Monaco Editor, adalah komponen VS Code dan diterbitkan oleh Microsoft pada akhir 2015.

Setiap editor memiliki kelebihan dan kekurangan masing-masing, masing-masing digunakan di lebih dari satu proyek besar. Misalnya, CodeMirror digunakan di alat pengembang Chrome dan Firefox, sebuah IDE di Bitbucket, di RunKit di npm; Ace - di Codecademy, Khan Academy, MODX; Monako - di GitLab IDE dan CodeSandbox. Berikut ini adalah bagan perbandingan yang dapat membantu Anda memilih perpustakaan yang paling cocok untuk proyek Anda.

Perpustakaan
AceCodeMirrorMonako
PengembangCloud9 IDE (Ajax.org),
sekarang bagian dari AmazonMozilla
Marijn haverbekeMicrosoft
Dukungan browserFirefox ^ 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
Dukungan bahasa
(penyorotan sintaksis)
> 120> 100> 20
Jumlah karakter dalam
versi terbaru aktif
cndjs.com
366 608 (v1.4.3)394.269 (v5.44.0)2.064.949 (v0.16.2)
Berat versi terbaru,
gzip
2.147 KB1.411 KB10.898 KB
RenderingDomDomDOM dan sebagian <canvas>
(untuk scrolling dan minimap)
Dokumentasi7 dari 10: tidak ada pencarian, tidak selalu jelas
Metode yang kembali, ada keraguan
dalam kelengkapan dan relevansi
(tidak semua tautan berfungsi di dok)
6 dari 10: digabung dengan buku petunjuk,
cari dengan Ctrl + F,
ada keraguan tentang kelengkapan
9 dari 10: cantik, dengan pencarian dan
referensi silang
-1 poin karena tidak ada penjelasan
ke beberapa bendera yang aplikasi
tidak cukup jelas dari namanya
Demo mulai cepatBagaimana-ke - teks dokumen dengan contoh kode,
secara terpisah ada demo dengan contoh kode
(Benar, mereka tersebar di halaman yang berbeda,
tidak semua orang berfungsi dan mereka paling mudah dicari melalui Google),
ada demo di mana Anda dapat menyentuh berbagai fitur,
tetapi diusulkan untuk mengelolanya melalui kontrol UI,
yaitu, maka kita masih harus mencari metode secara terpisah
untuk menghubungkan mereka
Bagaimana-benar-benar miskin
pada dasarnya semuanya tersebar di github
dan stackoverflow, tetapi ada demo fitur dengan contoh
kode untuk implementasi mereka
Dikombinasikan dalam format taman bermain:
kode dengan komentar dan sejumlah demo, Anda bisa
segera coba dan evaluasi
banyak kemungkinan
Kegiatan komunitasRata-rataTinggiRata-rata
Aktivitas PengembangRata-rataRata-rataTinggi

Tidak masuk akal untuk membandingkan pustaka berdasarkan ukuran, karena itu semua tergantung pada apa dan bagaimana menghubungkan untuk proyek tertentu: memuat file yang sudah selesai dengan salah satu build (yang juga bervariasi) atau menjalankan paket npm melalui semacam kolektor. Dan yang paling penting adalah seberapa banyak editor yang digunakan: apakah semua gaya dan tema dimuat, berapa banyak dan apa add-on dan plug-in yang digunakan. Misalnya, di CodeMirror, sebagian besar fungsi yang bekerja di Monaco dan Ace di luar kotak hanya tersedia dengan add-on. Tabel menunjukkan jumlah karakter dalam versi terbaru pada CDN dan berat file terkompresi mereka untuk ide umum tentang pesanan apa yang terlibat.

Semua perpustakaan memiliki sekumpulan fitur dasar yang kira-kira sama: autoformatting kode, garis lipat, potong / salin / tempel, tombol pintas, kemampuan untuk menambahkan sintaks baru untuk penyorotan dan pemesanan, pemeriksaan sintaks (dalam CodeMirror hanya melalui add-ons, di Ace sejauh ini hanya untuk JavaScript / CoffeeScript / CSS / XQuery), tooltips dan pelengkapan otomatis (dalam CodeMirror - melalui add-ons), pencarian lanjutan dengan kode (dalam CodeMirror - melalui add-ons), metode untuk menerapkan tab dan mode-split, mode diff dan alat gabungan (dalam CodeMirror - Baik dengan plus dan minus dalam satu jendela, atau dua panel melalui addon, Ace - Lieb terpisah). Karena usianya, banyak add-on telah ditulis untuk CodeMirror, tetapi jumlah mereka akan memengaruhi berat dan kecepatan editor. Monako dapat melakukan banyak hal di luar kotak, dan, menurut pendapat saya, lebih baik dan dalam volume yang lebih besar daripada Ace dan CodeMirror.

Kami tinggal di Monako karena beberapa alasan:

  1. Alat paling berkembang yang kami anggap penting untuk proyek kami:
    • IntelliSense - kiat dan pelengkapan otomatis;
    • navigasi kode pintar dalam menu konteks dan melalui minimap;
    • mode diff dua-panel keluar dari kotak.

  2. Ditulis dalam TypeScript. Panel kontrol kami ditulis dalam skrip Vue +, sehingga dukungan TS penting. Omong-omong, Ace baru-baru ini juga mendukung TS, tetapi pada awalnya ditulis dalam JS. Untuk CodeMirror, ada beberapa tipe di DefinitelyTyped .
  3. Ini paling aktif dikembangkan di dalamnya (mungkin karena dirilis belum lama ini), bug diperbaiki lebih cepat dan permintaan kolam diperjuangkan. Sebagai perbandingan, dengan CodeMirror kami memiliki pengalaman yang menyedihkan, ketika bug tidak diperbaiki selama bertahun-tahun dan kami menaruh kruk di atas kruk dan mengendarai kruk.
  4. Dokumentasi hasil-otomatis yang nyaman (yang memberikan harapan untuk kelengkapannya) dengan referensi silang antara antarmuka dan metode.
  5. Menurut selera kami, UI terindah (mungkin juga terkait dengan waktu pembuatan) dan API ringkas.
  6. Setelah bertanya kepada teman-teman pengembang editor mana yang paling menyebabkan sakit kepala, Ace dan CodeMirror adalah pemimpinnya.

Secara terpisah, harus dikatakan tentang kecepatan kerja. Penguraian mahal dilakukan dalam utas pekerja paralel. Plus, semua perhitungan dibatasi oleh ukuran viewport (semua jenis, warna, rendering dihitung hanya untuk garis-garis yang terlihat). Itu mulai mengerem hanya jika kode berisi 100.000 baris - prompt dapat dihitung selama beberapa detik. Ace, yang juga menggunakan pekerja untuk komputasi berat, ternyata lebih cepat: dalam kode dengan panjang yang sama, prompt muncul hampir secara instan, dan dengan cepat mengatasi 200.000 baris (di situs web resmi dinyatakan bahwa bahkan 4 juta baris seharusnya tidak menjadi masalah, meskipun sekrup dipercepat, input mulai melambat dan konfirmasi menghilang setelah 1 juta). CodeMirror, di mana tidak ada perhitungan paralel, hampir tidak dapat menarik volume seperti itu: ia dapat berkedip teks dan penyorotan sintaksis. Karena 100.000 baris dalam file jarang terjadi di dunia nyata, kami menutup mata terhadap hal ini. Bahkan dengan 40-50 ribu baris Monako melakukan pekerjaan yang sangat baik.

Menghubungkan Monako dan menggunakan fitur-fitur dasar (misalnya, integrasi dengan Vue)


Koneksi


Di sini saya akan memberikan contoh kode dari komponen vue dan menggunakan terminologi yang sesuai. Tapi semua ini dengan mudah dipindahkan ke kerangka kerja lain atau JS murni.

Kode sumber Monaco dapat diunduh di situs web resmi dan dimasukkan ke dalam proyek Anda, Anda dapat mengambilnya dari CDN, Anda dapat terhubung ke proyek melalui npm. Saya akan berbicara tentang opsi ketiga dan membangun menggunakan webpack.

Kami menempatkan monaco-editor dan plug-in untuk perakitan:

npm i -S monaco-editor npm i -D monaco-editor-webpack-plugin 

Di konfigurasi webpack, tambahkan:

 const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = { // ... plugins: [ // ... new MonacoWebpackPlugin() ] }; 

Jika Anda menggunakan Vue dan vue-cli-service untuk membangun, tambahkan ke vue.config.js:

 const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = { // ... configureWebpack: (config) => { // ... config.plugins.push(new MonacoWebpackPlugin()); } }; 

Jika Anda tidak memerlukan semua bahasa dan fitur Monaco, untuk mengurangi ukuran bundel, Anda dapat mentransfer objek dengan pengaturan ke MonacoWebpackPlugin :

 new MonacoWebpackPlugin({ output: '', // ,     languages: ['markdown'], //     ,     features: ['format', 'contextmenu'] //      }) 

Daftar lengkap fitur dan bahasa untuk plugin ada di sini .

Buat dan sesuaikan editor


Kami mengimpor editor dan memanggil editor.create(el: HTMLElement, config?: IEditorConstructionOptions) , melewati elemen DOM di mana kami ingin membuat editor sebagai argumen pertama.

Dalam komponen 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> 

Wadah untuk editor harus mengatur ketinggian agar tidak menjadi nol. Jika Anda membuat editor dalam div kosong (dengan tinggi nol - K.O Anda), Monako akan menulis ketinggian yang sama dengan gaya inline di jendela editor.

Argumen opsional kedua untuk editor.create adalah konfigurasi editor. Ada lebih dari seratus opsi di dalamnya, deskripsi lengkap antarmuka IEditorConstructionOptions ada dalam dokumentasi.

Sebagai contoh, kami akan mengatur bahasa, tema dan teks awal dan memungkinkan pembungkus baris (secara default tidak ada pembungkus):

 const config = { value: `function hello() { alert('Hello world!'); }`, language: 'javascript', theme: 'vs-dark', wordWrap: 'on' }; this.editor = editor.create(this.$refs.editor, config); 

Fungsi editor.create mengembalikan objek dengan antarmuka IStandaloneCodeEditor . Melalui itu, Anda sekarang dapat mengontrol semua yang terjadi di editor, termasuk mengubah pengaturan awal:

 //        read-only  this.editor.updateOptions({wordWrap: 'off', readOnly: true}); 

Sekarang untuk rasa sakit: updateOptions menerima objek dengan antarmuka IEditorOptions , bukan IEditorConstructionOptions. Mereka sedikit berbeda: IEditorConstructionOptions lebih luas, termasuk properti instance editor ini dan beberapa yang global. Properti updateOptions diubah melalui updateOptions , global - melalui metode editor global. Dan karenanya, mereka yang berubah secara global berubah untuk semua hal. Di antara opsi-opsi ini adalah theme . Buat 2 instance dengan tema yang berbeda; y dari keduanya akan menjadi yang diberikan dalam yang terakhir (gelap di sini). Metode global editor.setTheme('vs') juga akan mengubah topik pembicaraan untuk keduanya. Ini bahkan akan memengaruhi jendela-jendela yang ada di halaman lain SPA Anda. Ada beberapa tempat seperti itu, tetapi Anda harus mengikuti mereka.

 <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> 


Hapus Editor


Saat Anda menghancurkan jendela Monako, Anda harus memanggil metode dispose , jika tidak semua pendengar tidak akan dihapus dan jendela yang dibuat setelah ini mungkin tidak berfungsi dengan benar, bereaksi terhadap beberapa peristiwa beberapa kali:

 beforeDestroy() { this.editor && this.editor.dispose(); } 

Tab


Tab yang terbuka di editor file menggunakan jendela Monaco yang sama. Untuk beralih di antara mereka, metode IStandaloneCodeEditor digunakan: getModel untuk menyimpan dan setModel untuk memperbarui model editor. Model menyimpan teks, posisi kursor, riwayat tindakan untuk undo-redo. Untuk membuat model file baru, metode editor.createModel(text: string, language: string) digunakan. Jika file kosong, Anda tidak dapat membuat model dan meneruskan null ke setModel :

Lihat kode
 <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> 


Mode Diff


Untuk mode diff, Anda perlu menggunakan metode editor lain saat membuat jendela editor - createDiffEditor :

 <template> <div ref='diffEditor' class='editor'></div> </template> // ... mounted() { this.diffEditor = editor.createDiffEditor(this.$refs.diffEditor, config); } // ... 

Dibutuhkan parameter yang sama dengan editor.create , tetapi konfigurasi harus memiliki antarmuka IDiffEditorConstructionOptions , yang sedikit berbeda dari konfigurasi editor biasa, khususnya, tidak memiliki value . Teks untuk perbandingan ditetapkan setelah membuat IStandaloneDiffEditor yang dikembalikan melalui setModel :

 this.diffEditor.setModel({ original: editor.createModel('const a = 1;', 'javascript'), modified: editor.createModel('const a = 2;', 'javascript') }); 


Menu konteks, palet perintah dan tombol pintas


Monaco menggunakan sendiri, non-browser, menu konteks, di mana ada navigasi yang cerdas, multi-kursor untuk mengubah semua kejadian, dan palet perintah seperti dalam VS Code (Command palette) dengan sekelompok perintah yang berguna dan cara pintas yang mempercepat penulisan kode:

  Menu konteks Monaco 


  Palet perintah Monaco 


Menu konteks diperluas melalui metode addAction (ada di IStandaloneCodeEditor dan IStandaloneDiffEditor ), yang menerima objek IActionDescriptor :

Lihat kode
 // ... <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; } 


Untuk hanya mengikat pintasan ke tindakan tanpa menunjukkannya di menu konteks, metode yang sama digunakan, hanya contextMenuGroupId tindakan tidak ditentukan:

Lihat kode
 // ... //   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); //     } 


Palet perintah akan mencakup semua tindakan yang ditambahkan.

Kiat dan lengkapi otomatis


Untuk tujuan ini, Monako menggunakan IntelliSense , yang keren. Anda dapat membaca dan melihat pada tangkapan layar tautan berapa banyak informasi berguna yang dapat dia perlihatkan. Jika bahasa Anda belum memiliki pelengkapan otomatis, Anda dapat menambahkannya melalui registerCompletionItemProvider . Dan untuk JS dan TS, sudah ada metode addExtraLib yang memungkinkan Anda memuat definisi TypeScript untuk tooltips dan pelengkapan otomatis:

 // ... 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(); } 

Di parameter pertama, baris melewati definisi, di kedua, opsional, nama lib.

Bahasa dan Tema Kustom


Monako memiliki modul Monarch untuk menentukan sintaks bahasanya. Sintaksnya diuraikan cukup standar: korespondensi antara pelanggan tetap dan token yang merupakan karakteristik untuk bahasa ini ditetapkan.

Lihat kode
 // ... //  ,    : 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); // ... } 


Anda juga dapat membuat tema untuk token Anda - sebuah objek dengan antarmuka IStandaloneThemeData - dan menginstalnya di editor global:

 // ... 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); // ... } 

Sekarang teks dalam bahasa yang dijelaskan akan terlihat seperti ini:


Anda dapat menerapkan fitur ini, selama Anda memiliki imajinasi yang cukup. Misalnya, kami membuat penampil log panggilan di panel kami untuk pengembang. Log seringkali panjang dan tidak dapat dipahami, tetapi ketika mereka ditampilkan dengan penyorotan sintaks, pencarian cerdas, pelipatan / perluasan jalur, perintah yang diperlukan (misalnya, parett Prettify), menyorot semua saluran panggilan dengan id atau menerjemahkan waktu dalam log ke zona waktu yang berbeda, kemudian menggali menjadi lebih mudah di dalamnya (tangkapan layar dapat diklik):


Kesimpulan


Singkatnya, saya akan mengatakan bahwa Monaco adalah api. Setelah berbulan-bulan bekerja dengannya, saya memiliki kenangan yang sangat menyenangkan. Jika Anda memilih editor untuk kode, pastikan untuk pergi ke Playground -nya dan bermain-main dengan kode, lihat apa lagi yang bisa dilakukan. Mungkin inilah yang Anda cari.

Source: https://habr.com/ru/post/id445390/


All Articles