Skip to content

Commit 803fab3

Browse files
committed
chore(monaco-editor-textmate): add monaco-editor-textmate
1 parent 116056d commit 803fab3

File tree

10 files changed

+256
-5
lines changed

10 files changed

+256
-5
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist/
2+
node_modules/

packages/monaco-editor-textmate/.npmignore

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2018 Neek Sandhu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Wire `monaco-textmate` with `monaco-editor`
2+
3+
## Install
4+
5+
```sh
6+
npm i monaco-editor-textmate
7+
```
8+
9+
Please install peer dependencies if you haven't already
10+
```sh
11+
npm i monaco-textmate monaco-editor onigasm
12+
```
13+
## Usage
14+
15+
```javascript
16+
import { loadWASM } from 'onigasm' // peer dependency of 'monaco-textmate'
17+
import { Registry } from 'monaco-textmate' // peer dependency
18+
import { wireTmGrammars } from 'monaco-editor-textmate'
19+
20+
export async function liftOff() {
21+
await loadWASM(`path/to/onigasm.wasm`) // See https://www.npmjs.com/package/onigasm#light-it-up
22+
23+
const registry = new Registry({
24+
getGrammarDefinition: async (scopeName) => {
25+
return {
26+
format: 'json',
27+
content: await (await fetch(`static/grammars/css.tmGrammar.json`)).text()
28+
}
29+
}
30+
})
31+
32+
// map of monaco "language id's" to TextMate scopeNames
33+
const grammars = new Map()
34+
grammars.set('css', 'source.css')
35+
grammars.set('html', 'text.html.basic')
36+
grammars.set('typescript', 'source.ts')
37+
38+
// monaco's built-in themes aren't powereful enough to handle TM tokens
39+
// https://github.com/Nishkalkashyap/monaco-vscode-textmate-theme-converter#monaco-vscode-textmate-theme-converter
40+
monaco.editor.defineTheme('vs-code-theme-converted', {
41+
// ... use `monaco-vscode-textmate-theme-converter` to convert vs code theme and pass the parsed object here
42+
});
43+
44+
var editor = monaco.editor.create(document.getElementById('container'), {
45+
value: [
46+
'html, body {',
47+
' margin: 0;',
48+
'}'
49+
].join('\n'),
50+
language: 'css', // this won't work out of the box, see below for more info,
51+
theme: 'vs-code-theme-converted' // very important, see comment above
52+
})
53+
54+
await wireTmGrammars(monaco, registry, grammars, editor)
55+
}
56+
```
57+
58+
## Limitation
59+
60+
`monaco-editor` distribution comes with built-in tokenization support for few languages. Because of this `monaco-editor-textmate` [cannot
61+
be used with `monaco-editor`](https://github.com/Microsoft/monaco-editor/issues/884) without some modification, see explanation of this problem [here](https://github.com/Microsoft/monaco-editor/issues/884#issuecomment-389778611).
62+
63+
### Solution
64+
65+
To get `monaco-editor-textmate` working with `monaco-editor`, you're advised to use Webpack with [`monaco-editor-webpack-plugin`](https://www.npmjs.com/package/monaco-editor-webpack-plugin) which allows you to control which of "built-in" languages should `monaco-editor` use/bundle, leaving the rest.
66+
With that control you must *exclude* any/all languages for which you'd like to use TextMate grammars based tokenization instead.
67+
68+
## License
69+
MIT
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "monaco-editor-textmate",
3+
"version": "2.2.2",
4+
"description": "Wire monaco-textmate with monaco-editor",
5+
"main": "dist/index.js",
6+
"typings": "dist/typings/index.d.ts",
7+
"scripts": {
8+
"build": "tsc",
9+
"watch": "tsc -w",
10+
"prepack": "npm run build"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "git+https://github.com/NeekSandhu/monaco-editor-textmate.git"
15+
},
16+
"keywords": [
17+
"monaco",
18+
"editor",
19+
"textmate",
20+
"tm",
21+
"grammar",
22+
"vscode"
23+
],
24+
"author": "Neek Sandhu <neek.sandhu@outlook.com>",
25+
"license": "MIT",
26+
"bugs": {
27+
"url": "https://github.com/NeekSandhu/monaco-editor-textmate/issues"
28+
},
29+
"homepage": "https://github.com/NeekSandhu/monaco-editor-textmate#readme",
30+
"devDependencies": {
31+
"typescript": "^2.8.3"
32+
},
33+
"peerDependencies": {
34+
"monaco-editor": "0.x.x",
35+
"monaco-textmate": "^3.0.0"
36+
}
37+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Registry, StackElement, INITIAL } from 'monaco-textmate'
2+
import * as monacoNsps from 'monaco-editor'
3+
import { TMToMonacoToken } from './tm-to-monaco-token';
4+
5+
class TokenizerState implements monacoNsps.languages.IState {
6+
7+
constructor(
8+
private _ruleStack: StackElement
9+
) { }
10+
11+
public get ruleStack(): StackElement {
12+
return this._ruleStack
13+
}
14+
15+
public clone(): TokenizerState {
16+
return new TokenizerState(this._ruleStack);
17+
}
18+
19+
public equals(other: monacoNsps.languages.IState): boolean {
20+
if (!other ||
21+
!(other instanceof TokenizerState) ||
22+
other !== this ||
23+
other._ruleStack !== this._ruleStack
24+
) {
25+
return false;
26+
}
27+
return true;
28+
}
29+
}
30+
31+
/**
32+
* Wires up monaco-editor with monaco-textmate
33+
*
34+
* @param monaco monaco namespace this operation should apply to (usually the `monaco` global unless you have some other setup)
35+
* @param registry TmGrammar `Registry` this wiring should rely on to provide the grammars
36+
* @param languages `Map` of language ids (string) to TM names (string)
37+
*/
38+
export function wireTmGrammars(monaco: typeof monacoNsps, registry: Registry, languages: Map<string, string>, editor?: monacoNsps.editor.ICodeEditor) {
39+
return Promise.all(
40+
Array.from(languages.keys())
41+
.map(async (languageId) => {
42+
const grammar = await registry.loadGrammar(languages.get(languageId))
43+
monaco.languages.setTokensProvider(languageId, {
44+
getInitialState: () => new TokenizerState(INITIAL),
45+
tokenize: (line: string, state: TokenizerState) => {
46+
const res = grammar.tokenizeLine(line, state.ruleStack)
47+
return {
48+
endState: new TokenizerState(res.ruleStack),
49+
tokens: res.tokens.map(token => ({
50+
...token,
51+
// TODO: At the moment, monaco-editor doesn't seem to accept array of scopes
52+
scopes: editor ? TMToMonacoToken(editor, token.scopes) : token.scopes[token.scopes.length - 1]
53+
})),
54+
}
55+
}
56+
})
57+
})
58+
)
59+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import * as monacoNsps from 'monaco-editor'
2+
3+
// as described in issue: https://github.com/NeekSandhu/monaco-textmate/issues/5
4+
export const TMToMonacoToken = (editor: monacoNsps.editor.ICodeEditor, scopes: string[]) => {
5+
let scopeName = "";
6+
// get the scope name. Example: cpp , java, haskell
7+
for (let i = scopes[0].length - 1; i >= 0; i -= 1) {
8+
const char = scopes[0][i];
9+
if (char === ".") {
10+
break;
11+
}
12+
scopeName = char + scopeName;
13+
}
14+
15+
// iterate through all scopes from last to first
16+
for (let i = scopes.length - 1; i >= 0; i -= 1) {
17+
const scope = scopes[i];
18+
19+
/**
20+
* Try all possible tokens from high specific token to low specific token
21+
*
22+
* Example:
23+
* 0 meta.function.definition.parameters.cpp
24+
* 1 meta.function.definition.parameters
25+
*
26+
* 2 meta.function.definition.cpp
27+
* 3 meta.function.definition
28+
*
29+
* 4 meta.function.cpp
30+
* 5 meta.function
31+
*
32+
* 6 meta.cpp
33+
* 7 meta
34+
*/
35+
for (let i = scope.length - 1; i >= 0; i -= 1) {
36+
const char = scope[i];
37+
if (char === ".") {
38+
const token = scope.slice(0, i);
39+
if (
40+
editor['_themeService'].getTheme()._tokenTheme._match(token + "." + scopeName)._foreground >
41+
1
42+
) {
43+
return token + "." + scopeName;
44+
}
45+
if (editor['_themeService'].getTheme()._tokenTheme._match(token)._foreground > 1) {
46+
return token;
47+
}
48+
}
49+
}
50+
}
51+
52+
return "";
53+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"target": "esnext",
4+
"module": "commonjs",
5+
"outDir": "dist",
6+
"declaration": true,
7+
"declarationDir": "dist/typings",
8+
"sourceMap": true
9+
}
10+
}

packages/rollup-plugin-monaco-editor/package.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6024,6 +6024,11 @@ typedarray@^0.0.6:
60246024
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
60256025
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
60266026

6027+
typescript@^2.8.3:
6028+
version "2.9.2"
6029+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
6030+
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
6031+
60276032
uglify-js@^3.1.4:
60286033
version "3.13.5"
60296034
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.5.tgz#5d71d6dbba64cf441f32929b1efce7365bb4f113"

0 commit comments

Comments
 (0)