Skip to content

Commit 6874bb0

Browse files
authored
Monaco Vue support (codesandbox#191)
* Implement monaco-vue * Create .vue files with default template * Fix linter * Fix new template errors * Add style to template
1 parent 0a5640f commit 6874bb0

File tree

8 files changed

+186
-348
lines changed

8 files changed

+186
-348
lines changed

config/webpack.common.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ module.exports = {
240240
: 'node_modules/monaco-editor/min/vs',
241241
to: 'public/vs',
242242
},
243+
{
244+
from: 'node_modules/monaco-vue/release/min',
245+
to: 'public/vs/language/vue',
246+
},
243247
{
244248
from: 'static',
245249
to: 'static',

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"match-sorter": "^1.8.1",
122122
"moment": "^2.18.1",
123123
"monaco-editor": "CompuIves/codesandbox-monaco-editor",
124+
"monaco-vue": "^0.1.0",
124125
"normalize.css": "^5.0.0",
125126
"normalizr": "^3.2.3",
126127
"postcss": "^6.0.9",

src/app/components/sandbox/CodeEditor/Monaco.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ const CodeContainer = styled.div`
125125
}
126126
`;
127127

128+
const requireAMDModule = paths =>
129+
new Promise(resolve => window.require(paths, () => resolve()));
130+
128131
const handleError = (
129132
monaco,
130133
editor,
@@ -211,7 +214,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
211214
});
212215
};
213216

214-
updateDecorations = (classifications: Array<Object>) => {
217+
updateDecorations = async (classifications: Array<Object>) => {
215218
const decorations = classifications.map(classification => ({
216219
range: new this.monaco.Range(
217220
classification.startLine,
@@ -224,7 +227,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
224227
},
225228
}));
226229

227-
const modelInfo = this.getModelById(this.props.id);
230+
const modelInfo = await this.getModelById(this.props.id);
228231

229232
modelInfo.decorations = this.editor.deltaDecorations(
230233
modelInfo.decorations || [],
@@ -270,16 +273,16 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
270273
modelCache[module.id].model.dispose();
271274
delete modelCache[module.id];
272275

273-
const newModel = this.createModel(
276+
this.createModel(
274277
module,
275278
nextProps.modules,
276279
nextProps.directories
277-
);
278-
279-
if (isCurrentlyOpened) {
280-
// Open it again if it was open
281-
this.editor.setModel(newModel);
282-
}
280+
).then(newModel => {
281+
if (isCurrentlyOpened) {
282+
// Open it again if it was open
283+
this.editor.setModel(newModel);
284+
}
285+
});
283286
}
284287
}
285288
});
@@ -337,12 +340,12 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
337340
nextCode: ?string,
338341
nextTitle: string,
339342
}) => {
340-
if (nextId !== currentId) {
343+
if (nextId !== currentId && this.editor) {
341344
const pos = this.editor.getPosition();
342345
if (modelCache[currentId]) {
343346
modelCache[currentId].cursorPos = pos;
344347
}
345-
this.openNewModel(nextId, nextTitle);
348+
await this.openNewModel(nextId, nextTitle);
346349
this.editor.focus();
347350
}
348351
};
@@ -369,8 +372,9 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
369372
// Do in setTimeout, since disposeModules is async
370373
setTimeout(() => {
371374
// Initialize new models
372-
this.initializeModules(nextProps.modules);
373-
this.openNewModel(nextId, nextTitle);
375+
this.initializeModules(nextProps.modules).then(() => {
376+
this.openNewModel(nextId, nextTitle);
377+
});
374378
});
375379
} else {
376380
this.swapDocuments({
@@ -411,7 +415,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
411415
this.editor.setPosition(pos);
412416
}
413417

414-
getMode = (title: string) => {
418+
getMode = async (title: string) => {
415419
if (title == null) return 'javascript';
416420

417421
const kind = title.match(/\.([^.]*)$/);
@@ -425,7 +429,10 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
425429
} else if (kind[1] === 'html') {
426430
return 'html';
427431
} else if (kind[1] === 'vue') {
428-
return 'html';
432+
if (!this.monaco.languages.getLanguages().find(l => l.id === 'vue')) {
433+
await requireAMDModule(['vs/language/vue/monaco.contribution']);
434+
}
435+
return 'vue';
429436
} else if (kind[1] === 'less') {
430437
return 'less';
431438
} else if (kind[1] === 'md') {
@@ -440,8 +447,8 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
440447
return 'typescript';
441448
};
442449

443-
syntaxHighlight = (code: string, title: string, version: string) => {
444-
const mode = this.getMode(title);
450+
syntaxHighlight = async (code: string, title: string, version: string) => {
451+
const mode = await this.getMode(title);
445452
if (mode === 'typescript' || mode === 'javascript') {
446453
this.syntaxWorker.postMessage({
447454
code,
@@ -451,8 +458,9 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
451458
}
452459
};
453460

454-
lint = (code: string, title: string, version: string) => {
455-
if (this.getMode(title) === 'javascript') {
461+
lint = async (code: string, title: string, version: string) => {
462+
const mode = await this.getMode(title);
463+
if (mode === 'javascript') {
456464
this.lintWorker.postMessage({
457465
code,
458466
title,
@@ -461,7 +469,8 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
461469
}
462470
};
463471

464-
handleChange = (newCode: string) => {
472+
handleChange = () => {
473+
const newCode = this.editor.getModel().getValue();
465474
this.props.changeCode(this.props.id, newCode);
466475

467476
this.syntaxHighlight(
@@ -484,7 +493,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
484493
});
485494
};
486495

487-
configureEditor = (editor, monaco) => {
496+
configureEditor = async (editor, monaco) => {
488497
this.editor = editor;
489498
this.monaco = monaco;
490499

@@ -524,8 +533,8 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
524533
noSyntaxValidation: true,
525534
});
526535

527-
this.initializeModules();
528-
this.openNewModel(this.props.id, this.props.title);
536+
await this.initializeModules();
537+
await this.openNewModel(this.props.id, this.props.title);
529538

530539
this.addKeyCommands();
531540
enableEmmet(editor, monaco, {});
@@ -565,6 +574,10 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
565574
});
566575
},
567576
});
577+
578+
editor.onDidChangeModelContent(() => {
579+
this.handleChange();
580+
});
568581
};
569582

570583
closeFuzzySearch = () => {
@@ -601,11 +614,8 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
601614
modelCache = {};
602615
};
603616

604-
initializeModules = (modules = this.props.modules) => {
605-
modules.forEach(module => {
606-
this.createModel(module, modules);
607-
});
608-
};
617+
initializeModules = (modules = this.props.modules) =>
618+
Promise.all(modules.map(module => this.createModel(module, modules)));
609619

610620
resizeEditor = () => {
611621
this.editor.layout();
@@ -623,15 +633,15 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
623633
clearTimeout(this.sizeProbeInterval);
624634
}
625635

626-
createModel = (
636+
createModel = async (
627637
module: Module,
628638
modules: Array<Module> = this.props.modules,
629639
directories: Array<Directory> = this.props.directories
630640
) => {
631641
// Remove the first slash, as this will otherwise create errors in monaco
632642
const path = getModulePath(modules, directories, module.id);
633643

634-
const mode = this.getMode(module.title);
644+
const mode = await this.getMode(module.title);
635645
const model = this.monaco.editor.createModel(
636646
module.code,
637647
mode === 'javascript' ? 'typescript' : mode,
@@ -650,19 +660,19 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
650660
return model;
651661
};
652662

653-
getModelById = (id: string) => {
663+
getModelById = async (id: string) => {
654664
const { modules } = this.props;
655665
if (!modelCache[id]) {
656666
const module = modules.find(m => m.id === id);
657667

658-
this.createModel(module);
668+
await this.createModel(module);
659669
}
660670

661671
return modelCache[id];
662672
};
663673

664-
openNewModel = (id: string, title: string) => {
665-
const modelInfo = this.getModelById(id);
674+
openNewModel = async (id: string, title: string) => {
675+
const modelInfo = await this.getModelById(id);
666676
this.editor.setModel(modelInfo.model);
667677

668678
if (modelInfo.cursorPos) {
@@ -789,12 +799,6 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
789799

790800
const options = this.getEditorOptions();
791801

792-
const requireConfig = {
793-
url: '/public/vs/loader.js',
794-
paths: {
795-
vs: '/public/vs',
796-
},
797-
};
798802
return (
799803
<Container>
800804
<Header
@@ -815,13 +819,10 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
815819
<MonacoEditor
816820
width="100%"
817821
height="100%"
818-
language="typescript"
819822
theme="CodeSandbox"
820823
options={options}
821-
requireConfig={requireConfig}
822824
editorDidMount={this.configureEditor}
823825
editorWillMount={this.editorWillMount}
824-
onChange={this.handleChange}
825826
openReference={this.openReference}
826827
/>
827828
</CodeContainer>

0 commit comments

Comments
 (0)