首页 » SEO优化 » vscodephp语法着色技巧_手把手教你实现在Monaco Editor中运用VSCode主题

vscodephp语法着色技巧_手把手教你实现在Monaco Editor中运用VSCode主题

访客 2024-12-07 0

扫一扫用手机浏览

文章目录 [+]

其余笔者是一个颜控,不管做什么项目,都热衷于配套一些好看的皮肤、主题,以是Moncao Editor仅仅内置了三种主题是远远知足不了笔者需求的,况且还都很丑,于是结合Monaco EditorVSCode的关系就很自然的想到,能不能直接复用VSCode的主题,接下来就给大家先容一下笔者的探索之路。

ps.想直接理解如何实现的可以跳转到【详细实现】小节。

vscodephp语法着色技巧_手把手教你实现在Monaco Editor中运用VSCode主题

基本利用

先看一下Monaco Editor的基本利用,首先安装:

vscodephp语法着色技巧_手把手教你实现在Monaco Editor中运用VSCode主题
(图片来自网络侵删)

npm install monaco-editor

然后引入:

import as monaco from 'monaco-editor'// 创建一个js编辑器const editor = monaco.editor.create(document.getElementById('container'), { value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'), language: 'javascript', theme: 'vs'})

这样就可以在container元素上创建一个js措辞的编辑器,并且利用了内置的vs-dark主题。
如果碰着报错或者语法提示不生效,那么可能须要配置一下worker文件的路径,可以参考官方示例browser-esm-webpack。

自定义主题

Monaco Editor支持自定义主题,方法如下:

// 定义主题monaco.editor.defineTheme(themeName, themeData)// 利用定义的主题monaco.editor.setTheme(themeName)

themeName是要自定义的主题名称,比如OneDarkProthemeData是一个工具,即主题数据,基本构造如下:

{ base: 'vs',// 要继续的根本主题,即内置的三个:vs、vs-dark、hc-black inherit: false,// 是否继续 rules: [// 高亮规则,即给代码里不同token类型的代码设置不同的显示样式 { token: '', foreground: '000000', background: 'fffffe' } ], colors: {// 非代码部分的其他部分的颜色,比如背景、滚动条等 [editorBackground]: '#FFFFFE' }}

rules里面便是用来给代码进行高亮的,常见的tokenstring(字符串)、comment(注释)、keyword(关键词)等等,完全的请移步themes.ts,这些token是怎么确定的呢,Monaco Editor内置了一个语法着色器Monarch,实质是通过正则表达式来匹配,然后给匹配到的内容命名为一个token

可以直接在编辑器中查看代码某块对应的token,按F1或鼠标右键点击Command Palette,然后再找到并点击Developer: Inspect Tokens,接下来鼠标点哪一块代码,就会显示对应的信息,包括token类型,当前运用的颜色等。

踩坑

最开始的想法很大略,直接找到VSCode的主题文件,然后通过自定义主题来利用。

获取VSCode主题文件

有两种方法,如果某个主题已经在你的VSCode里安装并正在利用的话,那么可以按F1Command/Control + Shift + P或鼠标右键点击Command Palette/命令面板,接着找到并点击Developer:Generate Color Theme From Current Setting/开拓职员:利用当前设置天生颜色主题,然后VSCode就会天生一份json数据,保存即可。

如果某个主题没有安装的话,那么可以去vscode主题商店搜索该主题,进入主题详情页面后点击右侧的Download Extension按钮即可下载该主题,下载完成后找到刚才下载的文件,文件该当因此.vsix结尾的,直接把该后缀改成.zip,然后解压缩,末了打开里面的/extension/themes/文件夹,里面的.json文件即主题文件,打开该文件复制json数据即可。

VSCode主题转换成Monaco Editor主题格式

上一步过后你该当可以创造VSCode主题的格式是这样的:

{ "$schema": "vscode://schemas/color-theme", "type": "dark", "colors": { "activityBar.background": "#282c34" }, "tokenColors": [ { "scope": "variable.other.generic-type.haskell", "settings": { "foreground": "#C678DD" } }, { "scope": [ "punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php" ], "settings": { "foreground": "#BE5046" } } ]}

Monaco Editor的主题格式有一点差异,那是不是可以写一个转换方法把它转换成下面这样呢:

{ base: 'vs', inherit: false, rules: [ { token: 'variable.other.generic-type.haskell', foreground: '#C678DD' }, { token: 'punctuation.section.embedded.begin.php', foreground: '#BE5046' }, { token: 'punctuation.section.embedded.end.php', foreground: '#BE5046' } ], colors: { "activityBar.background": "#282c34" }}

当然可以,这也不难,但是末了当你利用这个自定义的主题后会创造,没有效果,为什么呢,去Monarch看一下对应措辞的解析配置后就会创造,压根就没有VSCode主题里定义的这些token,有效果才奇怪,那怎么办呢,自己扩展这个解析的配置吗,笔者最开始便是这么做的,写正则表达式嘛,该当也不是很难,为此,笔者还把Monarch文档完全翻译了一遍Monarch中文,但是当笔者在VSCode里看到如下效果时:

果断放弃,这显然是要进行语义剖析才行,否则谁知道abc是个变量。

其实在VSCode里语法高亮利用的是TextMate,而在Monaco Editor里利用的是Monarch,两者压根不是一个东西,为什么Monaco Editor不该用TextMate,而是要开拓一个新的东西呢,缘故原由是VSCode利用的是vscode-textmate来解析TextMate语法,这个库依赖一个Oniguruma正则表达式库,而这个正则表达式库是利用C措辞开拓的,当然不支持在浏览器上运行。

退而求其次

既然VSCode的主题不能直策应用,那么就只能能用多少用多少,由于Monaco Editor内置的主题token就只有那么多,那么把它所有的token颜色换成VSCode的主题颜色不就行了吗,虽然语义高亮没有,但是总比默认主题好看。
实现也很大略,首先colors部分的基本可以直策应用,而token部分可以通过上面先容的方法Developer: Inspect TokensVSCode里找到对应代码块的颜色,复制到Monaco Editor主题的对应token上即可,比如笔者转换后的OneDarkPro的实际效果如下:

VSCode里的效果如下:

只可粗看,不要细究。

这个事情也有人已经做了,可以参考这个仓库monaco-themes,里面帮你转换了一些常见的主题,可以拿来直策应用。

新的曙光

就在笔者已经放弃在Monaco Editor中直策应用VSCode主题的想法后,无意间创造codesandbox和leetcode两个网站中的编辑器主题效果和VSCode中基本同等,而且可以明显的看到在leetcode中切换主题要求的文件:

基本和VSCode主题格式是一样的,这就解释在Monaco Editor中利用VSCode主题是可以实现的,那么问题就变成了怎么实现。

实现

不得不说,这方面资料真的很少,干系文章基本没有,百度搜索结果里只有一两个干系的链接,不过也足以办理问题了,干系链接详见文章尾部。

紧张利用的是monaco-editor-textmate这个工具(以是除了百度谷歌之外,github也是一个很主要的搜索引擎啊),先安装:

npm i monaco-editor-textmate

npm该当会同时帮你再安装monaco-textmate、onigasm、monaco-editor这几个包,monaco-editor自不必说,我们自己都装了,其他两个可以自行检讨一下,如果没有的话须要自行安装。

工具先容

大略先容一下这几个包。

onigasm

这个库便是用来办理上述浏览器不支持C措辞编写的Oniguruma的问题,办理方法是把Oniguruma编译为WebAssembly,WebAssembly是一种中间格式,可以把非js代码编译成.wasm格式的文件,然后浏览器就可以加载并运行它了,WebAssembly已经是WEB的标准之一了,随着韶光的推移,相信兼容性也不是问题。

monaco-textmate

这个库是在VSCode利用的vscode-textmate库的根本上修正的, 以便让它在浏览器上利用。
紧张浸染是解析TextMate语法,这个库依赖前面的onigasm

monaco-editor-textmate

这个库的紧张浸染是帮我们把monaco-editormonaco-textmate关联起来,内部首先会加载对应措辞的TextMate语法文件,然后调用monaco.languages.setTokensProvider方法来自定义措辞的token解析器。

看一下它的利用示例:

import { loadWASM } from 'onigasm'import { Registry } from 'monaco-textmate'import { wireTmGrammars } from 'monaco-editor-textmate'export async function liftOff() { await loadWASM(`path/to/onigasm.wasm`) const registry = new Registry({ getGrammarDefinition: async (scopeName) => { return { format: 'json', content: await (await fetch(`static/grammars/css.tmGrammar.json`)).text() } } }) const grammars = new Map() grammars.set('css', 'source.css') grammars.set('html', 'text.html.basic') grammars.set('typescript', 'source.ts') monaco.editor.defineTheme('vs-code-theme-converted', {}); var editor = monaco.editor.create(document.getElementById('container'), { value: [ 'html, body {', ' margin: 0;', '}' ].join('\n'), language: 'css', theme: 'vs-code-theme-converted' }) await wireTmGrammars(monaco, registry, grammars, editor)}详细实现

看完前面的利用示例后,接下来我们详细看一下如何利用。

加载onigasm

首先我们要做的是加载onigasmwasm文件,这个文件须要首先被加载,且加载一次就可以了,以是我们在编辑器初始化提高行加载:

import { loadWASM } from 'onigasm'const init = async () => { await loadWASM(`${base}/onigasm/onigasm.wasm`) // 创建编辑器...}init()

onigasm.wasm文件可以在/node_modules/onigasm/lib/目录下找到,然后复制到项目的/public/onigasm/目录下,这样可以通过http进行要求。

创建浸染域映射

接下来创建措辞id到浸染域名称的映射:

const grammars = new Map()grammars.set('css', 'source.css')

其他措辞的浸染域名称可以在各种措辞的语法列表这里找到,比如想知道css的浸染域名称,我们进入css目录,然后打开package.json文件,可以看到个中有一个grammars字段:

"grammars": [ { "language": "css", "scopeName": "source.css", "path": "./syntaxes/css.tmLanguage.json", "tokenTypes": { "meta.function.url string.quoted": "other" } }]

language便是措辞idscopeName便是浸染域名称。
常见的如下:

const scopeNameMap = { html: 'text.html.basic', pug: 'text.pug', css: 'source.css', less: 'source.css.less', scss: 'source.css.scss', typescript: 'source.ts', javascript: 'source.js', javascriptreact: 'source.js.jsx', coffeescript: 'source.coffee'}注册语法映射

再接着注册TextMate的语法映射关系,这样可以通过浸染域名称来加载并创建对应的语法:

import { Registry} from 'monaco-textmate'// 创建一个注册表,可以从浸染域名称来加载对应的语法文件const registry = new Registry({ getGrammarDefinition: async (scopeName) => { return { format: 'json',// 语法文件格式,有json、plist content: await (await fetch(`${base}grammars/css.tmLanguage.json`)).text() } }})

语法文件和前面的浸染域名称一样,也是在各种措辞的语法列表这里找,同样以css措辞为例,还是看它的package.jsongrammars字段:

"grammars": [ { "language": "css", "scopeName": "source.css", "path": "./syntaxes/css.tmLanguage.json", "tokenTypes": { "meta.function.url string.quoted": "other" } }]

path字段便是对应的语法文件的路径,我们把这些json文件复制到项目的/public/grammars/目录下,这样就可以通过fetch来要求到。

定义主题

前面先容过,Monaco Editor的主题格式和VSCode的格式是有点不一样的,以是须要进行转换,转换可以自己实现,也可以直策应用monaco-vscode-textmate-theme-converter这个工具,它可以同时转换多个本地文件:

// convertTheme.jsconst converter = require('monaco-vscode-textmate-theme-converter')const path = require('path')const run = async () => { try { await converter.convertThemeFromDir( path.resolve(__dirname, './vscodeThemes'), path.resolve(__dirname, '../public/themes') ); } catch (error) { console.log(error) }}run()

运行node ./convertTheme.js命令后,就会把你放在vscodeThemes目录下所有VSCode的主题文件转换成Monaco Editor的主题文件并输出到public/themes目录下,然后我们在代码里直接通过fetch来要求主题文件并利用defineTheme方法定义主题即可:

// 要求OneDarkPro主题文件const themeData = await ( await fetch(`${base}themes/OneDarkPro.json`)).json()// 定义主题monaco.editor.defineTheme('OneDarkPro', themeData)设置token解析器

经由前面这些准备事情,末了一步要做的是设置Monaco Editortoken解析器,默认利用的是内置的Monarch,我们要换成TextMate的解析器,也便是monaco-editor-textmate做的事情:

import { wireTmGrammars} from 'monaco-editor-textmate'import as monaco from 'monaco-editor'let editor = monaco.editor.create(document.getElementById('container'), { value: [ 'html, body {', ' margin: 0;', '}' ].join('\n'), language: 'css', theme: 'OneDarkPro'})await wireTmGrammars(monaco, registry, grammars, editor)问题1

上一步后该当可以看到VSCode的主题在Monaco Editor上生效了,但是多试几次可能会创造偶尔会失落效,缘故原由是Monaco Editor内置的措辞是延迟加载的,并且加载完后也会同样注册一个token解析器,以是会把我们的给覆盖掉,详见issue:setTokensProvider unable to override existing tokenizer。

一种办理方法是去除内置的措辞,这可以利用monaco-editor-webpack-plugin。

安装:

npm install monaco-editor-webpack-plugin -D

Vue项目配置如下:

// vue.config.jsconst MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')module.exports = { configureWebpack: { plugins: [ new MonacoWebpackPlugin({ languages: [] }) ] }}

languages选项用来指定要包含的措辞,我们直接设为空,啥也不要。

然后修正Monaco Editor的引入办法为:

import as monaco from 'monaco-editor/esm/vs/editor/editor.api'

末了须要手动注册我们须要的措辞,由于所有内置措辞都被去除了嘛,比如我们要利用js措辞的话:

monaco.languages.register({id: 'javascript'})

这种方法虽然可以完美办理该问题,但是很大的一个副浸染是语法提示不生效了,由于只有包含了内置的htmlcsstypescript时才会去加载对应的worker文件,没有语法提示笔者也是无法接管的,以是末了笔者利用了一种比较lowhack办法:

// 插件配置new MonacoWebpackPlugin({ languages: ['css', 'html', 'javascript', 'less', 'pug', 'scss', 'typescript', 'coffee']})// 注释掉措辞注册语句// monaco.languages.register({id: 'javascript'})// 当worker文件被加载了后再wirelet hasGetAllWorkUrl = falsewindow.MonacoEnvironment = { getWorkerUrl: function (moduleId, label) { hasGetAllWorkUrl = true if (label === 'json') { return './monaco/json.worker.bundle.js' } if (label === 'css' || label === 'scss' || label === 'less') { return './monaco/css.worker.bundle.js' } if (label === 'html' || label === 'handlebars' || label === 'razor') { return './monaco/html.worker.bundle.js' } if (label === 'typescript' || label === 'javascript') { return './monaco/ts.worker.bundle.js' } return './monaco/editor.worker.bundle.js' },}// 循环检测let loop = () => { if (hasGetAllWorkUrl) { Promise.resolve().then(async () => { await wireTmGrammars(monaco, registry, grammars, editor) }) } else { setTimeout(() => { loop() }, 100) }}loop()问题2

笔者碰着的其余一个问题是,转换后有些主题的默认颜色并未设置,以是都是玄色,很丑:

这个问题的办理方法是可以给主题的rules数组添加一个空的token,用来作为没有匹配到的默认token

{ "rules": [ { "foreground": "#abb2bf", "token": "" } ]}

foreground的色值可以取colors选项里的editor.foreground的值,要手动修正每个色值比较麻烦,可以在之前的转换主题的步骤里顺便进行,会不才一个问题里一起办理。

问题3

monaco-vscode-textmate-theme-converter这个包实质算是nodejs环境下的工具,以是想在纯前端环境下利用不太方便,其余它对付非标准json格式的VSCode主题转换时会报错,由于很多主题格式是.jsonc,内容是带有很多注释的,以是都须要自己前辈行检讨并修正,不是很方便,基于这两个问题,笔者fork了它的代码,然后修正并分成了两个包,分别对应nodejs浏览器环境,详见https://github.com/wanglin2/monaco-vscode-textmate-theme-converter。

以是我们可以更换掉monaco-vscode-textmate-theme-converter,改成安装笔者的:

npm i vscode-theme-to-monaco-theme-node -D

利用办法基本是一样的:

// 只要修正引入为笔者的包即可const converter = require('vscode-theme-to-monaco-theme-node')const path = require('path')const run = async () => { try { await converter.convertThemeFromDir( path.resolve(__dirname, './vscodeThemes'), path.resolve(__dirname, '../public/themes') ); } catch (error) { console.log(error) }}run()

现在就可以直接转换.jsonc文件,而且输出统一为.json文件,其余内部会自动添加一个空的token作为没有匹配到的默认token,效果如下:

最佳实践

VSCode主题除了代码主题外,一样平常还包含编辑器其他部分的主题,比如标题栏、状态栏、侧边栏、按钮等等,以是我们也可以在页面运用这些样式,达到全体页面的主题也能随编辑器代码主题一起切换的效果,这样能让页面整体更加折衷,详细的实现上,我们可以利用CSS变量,先把页面所有涉及到的颜色都定义成CSS变量,然后在切换主题时根据主题的colors选项里的指定字段来更新变量即可,详细利用哪个字段来对应页面的哪个部分可以根据实际情形来确定,VSCode主题的所有可配置项可以在theme-color这里找到。
效果如下:

总结

本文完全详细的先容了笔者对付Monaco Editor编辑器主题的探索,希望能给有主题定制需求的小伙伴们一点帮助,完全的代码请参考本项目源码:code-run。

参考链接

文章:monaco利用vscode干系语法高亮在浏览器上显示

文章:codesandbox是如何办理主题的问题

文章:闲谈Monaco Editor-自定义措辞之Monarch

谈论:如何在Monaco Editor中利用VSC主题?

谈论:利用WebAssembly来支持TextMate语法

标签:

相关文章