Что такое Редактор Монако?
У Microsoft до этого был проект под названием Monaco Workbench, который впоследствии стал VSCode, и Monaco Editor (далее monaco) — веб-редактор, выросший из этого проекта.Большая часть их кода (monaco-editor-core)) общий, поэтому monaco и VSCode почти одинаковы в редактировании кода, взаимодействии и пользовательском интерфейсе. Разница в том, что платформы у них разные. Monaco основан на браузерах, а VSCode основан наelectron, поэтому VSCode более надежен по функциям и более эффективен по производительности.
начать использовать
В этой статье используется компиляция веб-пакета, поэтому все нижеследующее основано на веб-пакете.
основные навыки
Во-первых, нам нужно установить monaco
npm install monaco-editor -S
Затем импортируйте monaco в свой собственный файл, вам не нужно импортировать их все сюда, вам нужно импортировать только те функциональные модули, которые вам нужны.
HTML
<div id="monaco">
</div>
JS
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
const monacoInstance=monaco.editor.create(document.getElementById("monaco"),{
value:`console.log("hello,world")`,
language:"javascript"
})
monacoInstance.dispose();//使用完成销毁实例
Мы установили язык на javascript, и интерфейс вышел, но мы обнаружили, что нет подсветки синтаксиса.Когда мы ввели команду, мы обнаружили, что языка javascript вообще нет, только самый простой открытый текст.
Итак, нам нужно определить еще один язык javascript, но определить язык, к счастью, не так-то просто.monaco предоставляет множество встроенных языков, нам просто нужно импортировать его.
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution';
Введение завершено, снова проверьте интерфейс и обнаружите, что подсветка синтаксиса уже есть.
В настоящее время мы можем попробовать использовать monaco, как с помощью VSCode, нажать ctrl+f для выполнения текстового поиска, мы обнаружим, что это не элемент управления поиском monaco, а браузер, поэтому нам нужно ввести элемент управления поиском.
import 'monaco-editor/esm/vs/editor/contrib/find/findController.js';
Попробуйте найти его еще раз, и вылезет поисковое управление монако.
в монако тоже много таких средств контроля, мы можем импортировать то, что используем по мере необходимости.
Но есть более простой способ — напрямую ввести основной файл вместо файла API.
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js';
Открой файл, мы увидим
editor.main.js
import '../language/typescript/monaco.contribution';
import '../language/css/monaco.contribution';
import '../language/json/monaco.contribution';
import '../language/html/monaco.contribution';
import '../basic-languages/monaco.contribution';
export * from './edcore.main';
Если импортировать таким образом, он автоматически принесетВсе встроенные языки и элементы управления, единственный минус это упаковкаслишком большой.
На данный момент мы реализовали веб-редактор, который может вводить, выделять и искать, однако по сравнению с VSCode в нем отсутствуют многие важные функции, такие как завершение кода, подсказки об ошибках и команды быстрого доступа.
Расширенное использование
Во-первых, мы можем представить себя, если бы мы захотели реализовать завершение кода и подсказки об ошибках, что бы мы сделали?
Во-первых, нам нужно проанализировать входной текст, а сейчас нам нужно написать парсер.
Во-вторых, в соответствии с результатом синтаксического анализа Parser вызовите интерфейс аннотации monaco, чтобы аннотировать неправильный код для реализации функции подсказки об ошибке.
В-третьих, в соответствии с результирующей информацией, проанализированной Parser, предоставляются контекстно-зависимые варианты кода для реализации функции завершения кода.
Видно, что это будет очень сложно реализовать, и здесь задействовано много моментов, однако, как и подсветка синтаксиса, monaco также помогает нам реализовать эти функции, и на данный момент поддерживаетhtml, css, тс/js, jsonЧетыре языка, нам просто нужно их представить. Но введение здесь не такое простое, как подсветка синтаксиса.
Реализация Monaco использует рабочий метод, поскольку разбор грамматики занимает много времени, более эффективно использовать рабочие процессы для асинхронной обработки возвращаемых результатов. Нам нужно использовать два шага.
- Предоставьте глобальную переменную, которая определяет рабочий путь
window.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.js';
}
if (label === 'css') {
return './css.worker.js';
}
if (label === 'html') {
return './html.worker.js';
}
if (label === 'typescript' || label === 'javascript') {
return './typescript.worker.js';
}
if(label==="sql"){
return "./sql.worker.js";
}
return './editor.worker.js';
}
}
Выберите соответствующий язык, monaco вызовет getWorkerUrl, чтобы проверить путь воркера, а затем загрузит его. По умолчанию здесь загружается editor.worker.js.Это базовый файл функций, который предоставляет функции, общие для всех языков (например, подсказки завершения кода для определенных констант).Независимо от того, какой язык используется, monaco загрузит его. .
2. Упакуйте работника
Ввести необходимых воркеров в webpack
entry: {
"main": path.resolve(process.cwd(), "src/main.js"),
"editor.worker": 'monaco-editor/esm/vs/editor/editor.worker.js',
"ts.worker": 'monaco-editor/esm/vs/language/typescript/ts.worker',
},
Ну, это начинается с адского режима Монако, и мы столкнемся с множеством проблем.
Вопрос один.Наши выходные данные обычно хэшируются, поэтому выходной рабочий файл также будет иметь соответствующий суффикс хэш-значения, например typescript.worker.a23sf4asfqw.js, тогда конфигурация в getWorkerUrl на первом этапе (typescript.worker.js) неверна. с ним, что приводит к невозможности найти рабочий путь.
Вопрос второй.Рабочий процесс выполняется в отдельном потоке, поэтому переменной окна нет, поэтому нам нужно изменить глобальную переменную веб-пакета на self.
Вопрос 3.Если используется плагин html-webpack-plugin, нам нужно предотвратить прямой импорт воркера в html-файл (поскольку воркер — это тоже отдельная запись), поэтому нам также нужно установить чанки html-webpack- плагин.
....
Я должен сказать, что Монако — очень внимательный редактор, и он также помог нам решить этот ряд проблем. Решение нашей проблемы состоитmonaco-editor-webpack-plugin.
monaco-editor-webpack-plugin
Он также очень прост в использовании
npm install monaco-editor-webpack-plugin -S
конфигурация веб-пакета
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports=function(){
return {
...
plugins:[
new MonacoWebpackPlugin()
]
...
}
}
Плагины помогут нам сделать несколько вещей
- Автоматически вводить глобальную переменную getWorkerUrl
- Обработка конфигурации сборки рабочего
- Автоматически импортируйте элементы управления и языковые пакеты.
В частности, какие элементы управления и языковые пакеты ввести, мы можем настроитьlanguagesиfeaturesконтролировать
new MonacoWebpackPlugin({
languages:["javascript","css","html","json"],
features:["coreCommands","find"]
})
По умолчанию плагин импортируетЯзыковые пакеты и элементы управления по умолчанию(Взгляните, это должны быть все элементы управления и языковые пакеты), вы можете проверить этот адрес для деталейMicrosoft/monaco-editor-webpack-plugin.
Отлично, теперь у нас есть редактор с автодополнением, подсказками об ошибках и подсветкой синтаксиса.
привязка события
Закончив настройку самого редактора, мы можем перейти к следующему шагу,Привязать событие редактирования.
monacoInstance.onDidChangeModelContent((event) => {
const newValue=monacoInstance.getValue();
console.log(newValue)
})
monacoInstanceЯвляетсяcreateЭкземпляр, возвращаемый методом, содержит множество методов для управления экземпляром.eventЯвляетсяIModelContentChangedEventОбъект, который содержит очень подробную информацию об изменении, включая тип операции (отмена, повтор или изменение текста, вызванное ручным вводом), измененное положение текста, измененное текстовое содержимое и т. д. И мы хотим получить последнее значение, нам нужно вызвать
monacoInstance.getValue();
Осторожные друзья тоже должны найти очень странное место, то есть наш метод привязки используетonDidChangeModelContent,EстьModel, это наименование очень конкретное, буквальное значениеИзмените содержимое модели, чтобы вызвать событие,От начала и до конца мы не видели существования Модели, так почему же содержимое Модели изменено, чтобы вызвать событие, мы оперируем Моделью?
Да на самом деле, когда мы редактируем, мы редактируем на Модели.По умолчанию monaco сгенерирует для меня Модель, и мы можем вызватьgetModelраспечатать это
monacoInstance.getModel()
Глядя на API, мы можем обнаружить, что Модель на самом деле является объектом, который сохраняет состояние редактирования, который содержит информацию о языке, текущую текстовую информацию о редактировании, информацию об аннотациях и т. д. Таким образом, мы можем кэшировать объект модели и вызывать его напрямую, когда это необходимо.setModelВы можете переключиться в предыдущее состояние в любое время. Или вы можете установить модель при инициализации экземпляра.
const model=monaco.editor.createModel("hahahaha","javascript");
monacoInstance = monaco.editor.create(this.monacoDom.current, {
model:model
})
И мы можем привязывать наши события прямо к модели
model.onDidChangeContent((event)=>{
...
})
Модель тоже нужно уничтожить в конце.Здесь два случая.Если черезcreateModelСоздал Модель, то нам нужно ее вручную уничтожить, но если она создается monaco по умолчанию, то не нужно.При вызове метода destroy экземпляра созданная по умолчанию Модель кстати будет уничтожена.
model.dispose();
Помимо событий редактирования, в Модели есть много других событий.
Например:
onDidChangeOptions событие изменения конфигурации
onDidChangeLanguageсобытие смены языка
...
В простых сценариях наличие Model может затруднить использование, но в сложных сценариях Model может значительно упростить сложность кода.
Представьте, что у нас есть 5 вкладок, каждая вкладка — это редактор, и у каждого редактора свой язык, содержимое и аннотационная информация.Если модели нет, нам нужно сохранить язык, содержимое и другую информацию каждой вкладки. соответствующую вкладку, инициализируем эту информацию в редакторе, но с Моделью нам не нужно сохранять информацию о редактировании каждой вкладки, нам нужно только сохранить Модель каждой вкладки, а затем передать Модель в редактор для инициализации. .
конец
В этой статье основное внимание уделяется ознакомлению с базовым дизайном и использованием monaco. Детали кода не очень подробные. Если у вас есть какие-либо вопросы, вы можете прочитатьОфициальная документация монако. Спасибо~