背景
上一篇我们讲解了如何设置编辑器的值,获取编辑器的值,以及监听编辑器的内容修改。这些功能对于基础的单文件修改,一次只修改一个文件的业务场景比较友好。但如果是复杂的场景,比如WEB IDE,同时打开一个项目的多个文件,并切换文件进行编辑,修改。这种应该怎么做那?
如下图
今天这篇我就给大家介绍一下如何使用monaco editor实现多文件编辑。
实现效果如下:
另外再说一下,上一篇,我看到有监听mode修改语言的事件,但没有找到在哪里修改,
这次我找到了
http://localhost:8080/monaco-editor-website/api/modules/monaco.editor.html#setModelLanguage
使用editor.setModelLanguage()
方法 修改model的语言。随着不断的阅读文档,你会被这个项目所折服。
文件
首先我们定义三个文件, file1,file2 file3,使用map来存储他们的内容。
const fileMap = new Map()fileMap.set('file1', { content: 'const blog = "https://fizzz.blog.csdn.net/"', language: 'javascript', model: null, state: null })fileMap.set('file2', { content: 'x = 1 \nprint(x)', language: 'python', model: null, state: null })fileMap.set('file3', { content: 'System.out.println("Hello World");', language: 'java', model: null, state: null })
以文件名为key,以文件的其他内容为value,存放到一个map中。方便存取。
其中 content是文件的内容,
language 为文件的语言,
model是语言模型,
state是编辑器的状态 这个属性等下详细讲解
核心代码及解释
在页面上添加三个按钮
<button onclick="showFile('file1')">文件1</button>
<button onclick="showFile('file2')">文件2</button>
<button onclick="showFile('file3')">文件3</button>
点击按钮触发showFile函数,以下是showFile函数内容
function showFile(fileName) {const fileItem = fileMap.get(fileName)var currentState = editor.saveViewState();var currentModel = editor.getModel();fileMap.forEach((item, key) => {if (currentModel === item.model) {item.state = currentState}})if (fileItem.model) {editor.setModel(fileItem.model);editor.restoreViewState(fileItem.state);} else {const newModel = monaco.editor.createModel(fileItem.content, fileItem.language);editor.setModel(newModel);fileItem.model = newModel}editor.focus();
}
首先要说明的是,monaco editor 不提供文件导航的功能,如果你要实现一个web ide,那么编辑器上的 文件切换tab需要自己实现。就是这一部分
另外,我们需要为每一个打开的文件创建一个model,使用monaco.editor.createModel方法。
然后将创建的model保存到对应的文件内容中 fileItem.model = newModel
这行就是做一步的。
在创建model前,需要先判断文件是否已经有了model,如果已经有了,那么用已经创建的,如果没有,那么新建一个model,然后使用editor.setModel()方法,将语言模型显示到编辑器上。
关于model这一块,这里就不讲解太多,上一篇已经讲解的很详细了。
在来讲解一下这几行代码
// 保存当前编辑器的状态,赋值给currentState
var currentState = editor.saveViewState();
...
// 恢复编辑器的状态
editor.restoreViewState(fileItem.state);
...
// 使编辑器获取焦点
editor.focus();
要引入编辑器状态就要首先搞清楚一点,这个编辑器状态,到底包含哪些状态。之前我也不知道,但我查到了这个
http://localhost:8080/monaco-editor-website/api/interfaces/monaco.editor.ICodeEditorViewState.html
并经过试验得知,
这里的状态包括光标,选中,滚动条。 是的你没看错,monaco editor会记录你在编辑器的所有操作,这些状态用户可以保存起来,并在合适的时机进行恢复。达到一种非常无缝衔接的丝滑交互体验。 真的 Amazing 。
在编辑器中 使用 editor.saveViewState()
方法获取当前编辑器的状态,
使用 editor.restoreViewState()
方法恢复编辑器的状态。
恢复编辑器状态后,还需要使用editor.focus();
让编辑器获取焦点。这样编辑器中选中的内容 就会高亮。
效果图
完整代码
<!DOCTYPE html>
<html><head><title>Hello World Monaco Editor</title><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head><body><h2>Hello World Monaco Editor</h2><button onclick="setValue()">设置值</button><button onclick="getValue()">获取值</button><button onclick="showFile('file1')">文件1</button><button onclick="showFile('file2')">文件2</button><button onclick="showFile('file3')">文件3</button><div id="container" style="width: 800px; height: 600px; border: 1px solid grey"></div><script src="./monaco-editor/package/min/vs/loader.js"></script><!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js"></script> --><script>require.config({ paths: { vs: './monaco-editor/package/min/vs' } });let editorrequire(['vs/editor/editor.main'], function () {editor = monaco.editor.create(document.getElementById('container'), {value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),language: 'javascript'});});function setValue() {// 第一种重新设置值// var currentModel = editor.getModel();// currentModel.setValue(`// 重新设置值// const blog = 'https://fizzz.blog.csdn.net/'// `)// 第二种重新设置值var currentModel = editor.getModel();const model = monaco.editor.createModel(`
// 重新设置值
x = y = z = 1
print(x) #1
`, 'python');editor.setModel(model);model.onDidChangeContent(e => {console.log(e)});if (currentModel) {currentModel.dispose();}}function getValue() {var currentModel = editor.getModel();console.log(currentModel.getValue())}const fileMap = new Map()fileMap.set('file1', { content: 'const blog = "https://fizzz.blog.csdn.net/"', language: 'javascript', model: null, state: null })fileMap.set('file2', { content: 'x = 1 \nprint(x)', language: 'python', model: null, state: null })fileMap.set('file3', { content: 'System.out.println("Hello World");', language: 'java', model: null, state: null })function showFile(fileName) {const fileItem = fileMap.get(fileName)var currentState = editor.saveViewState();var currentModel = editor.getModel();fileMap.forEach((item, key) => {if (currentModel === item.model) {item.state = currentState}})if (fileItem.model) {editor.setModel(fileItem.model);editor.restoreViewState(fileItem.state);} else {const newModel = monaco.editor.createModel(fileItem.content, fileItem.language);editor.setModel(newModel);fileItem.model = newModel}editor.focus();}</script>
</body></html>
总结
多文件切换是一个常用的业务场景,也是编辑器的必备的功能,如果做好该场景的交互优化,用户体验是非常关键的。