1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-05-06 02:25:19 +02:00

add monaco firt draft. fixes #275

This commit is contained in:
Kushagra Gour 2018-11-22 19:01:57 +05:30
parent d9d1643b22
commit 0d97564138
14 changed files with 4202 additions and 236 deletions

View File

@ -0,0 +1,255 @@
import { h, Component } from 'preact';
import CodeMirror from '../CodeMirror';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/addon/comment/comment.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/xml-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/javascript-hint.js';
import 'codemirror/addon/hint/xml-hint.js';
import 'codemirror/addon/hint/html-hint.js';
import 'codemirror/addon/hint/css-hint.js';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/addon/search/searchcursor.js';
import 'codemirror/addon/search/search.js';
import 'codemirror/addon/dialog/dialog.js';
import 'codemirror/addon/search/jump-to-line.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/mode/css/css.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/htmlmixed/htmlmixed.js';
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'code-blast-codemirror/code-blast.js';
import emmet from '@emmetio/codemirror-plugin';
import { prettify } from '../utils';
import '../lib/monaco/monaco.bundle';
import '../lib/monaco/monaco.css';
emmet(CodeMirror);
window.MonacoEnvironment = {
getWorkerUrl(moduleId, label) {
let MonacoWorker;
switch (label) {
case 'html':
return 'lib/monaco/workers/html.worker.bundle.js';
case 'json':
return 'lib/monaco/workers/json.worker.bundle.js';
case 'css':
return 'lib/monaco/workers/css.worker.bundle.js';
case 'typescript':
case 'javascript':
return 'lib/monaco/workers/ts.worker.bundle.js';
default:
return 'lib/monaco/workers/editor.worker.bundle.js';
}
}
};
export default class CodeEditor extends Component {
componentDidMount() {
this.initEditor();
}
shouldComponentUpdate(nextProps) {
if (nextProps.prefs !== this.props.prefs) {
const { prefs } = nextProps;
this.instance.setOption('indentWithTabs', prefs.indentWith !== 'spaces');
this.instance.setOption(
'blastCode',
prefs.isCodeBlastOn ? { effect: 2, shake: false } : false
);
this.instance.setOption('theme', prefs.editorTheme);
this.instance.setOption('indentUnit', +prefs.indentSize);
this.instance.setOption('tabSize', +prefs.indentSize);
this.instance.setOption('keyMap', prefs.keymap);
this.instance.setOption('lineWrapping', prefs.lineWrap);
this.instance.setOption('lineWrapping', prefs.autoCloseTags);
this.instance.refresh();
}
return false;
}
setModel(model) {
this.instance.swapDoc
? this.instance.swapDoc(model)
: this.instance.setModel(model);
}
setValue(value) {
this.instance.setValue
? this.instance.setValue(value)
: this.instance.setModel(model);
}
getValue() {
return this.instance.getValue();
}
saveViewState() {
if (this.props.mode === 'monaco') {
return this.instance.saveViewState();
}
}
restoreViewState(state) {
if (this.props.mode === 'monaco') {
this.instance.restoreViewState(state);
}
}
refresh() {
this.instance.refresh ? this.instance.refresh() : this.instance.layout();
}
focus() {
this.instance.focus();
}
initEditor() {
const { options, prefs } = this.props;
if (this.props.mode === 'monaco') {
this.instance = monaco.editor.create(this.textarea, {
language: 'javascript',
roundedSelection: false,
scrollBeyondLastLine: false,
theme: 'vs-dark',
fontSize: prefs.fontSize,
minimap: {
enabled: false
},
wordWrap: 'on',
fontLigatures: true,
automaticLayout: true
});
window.monacoInstance = this.instance;
this.instance.onDidChangeModelContent(this.props.onChange);
setTimeout(() => {
// this.instance.layout();
}, 1000);
} else {
this.instance = CodeMirror.fromTextArea(
this.textarea.querySelector('textarea'),
{
mode: options.mode,
lineNumbers: true,
lineWrapping: !!prefs.lineWrap,
autofocus: options.autofocus || false,
autoCloseBrackets: true,
autoCloseTags: !!prefs.autoCloseTags,
matchBrackets: true,
matchTags: options.matchTags || false,
tabMode: 'indent',
keyMap: prefs.keyMap || 'sublime',
theme: prefs.editorTheme || 'monokai',
lint: !!options.lint,
tabSize: +prefs.indentSize || 2,
indentWithTabs: prefs.indentWith !== 'spaces',
indentUnit: +prefs.indentSize,
foldGutter: true,
styleActiveLine: true,
gutters: options.gutters || [],
// cursorScrollMargin: '20', has issue with scrolling
profile: options.profile || '',
extraKeys: {
Up: function(editor) {
// Stop up/down keys default behavior when saveditempane is open
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineUp(editor);
},
Down: function(editor) {
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineDown(editor);
},
'Shift-Tab': function(editor) {
CodeMirror.commands.indentAuto(editor);
},
'Shift-Ctrl-F': function(editor) {
if (options.prettier) {
prettify({
content: editor.getValue(),
type: options.prettierParser
}).then(formattedCode => editor.setValue(formattedCode));
}
},
Tab: function(editor) {
if (options.emmet) {
const didEmmetWork = editor.execCommand(
'emmetExpandAbbreviation'
);
if (didEmmetWork === true) {
return;
}
const input = $('[data-setting=indentWith]:checked');
if (
!editor.somethingSelected() &&
(!prefs.indentWith || prefs.indentWith === 'spaces')
) {
// softtabs adds spaces. This is required because by default tab key will put tab, but we want
// to indent with spaces if `spaces` is preferred mode of indentation.
// `somethingSelected` needs to be checked otherwise, all selected code is replaced with softtab.
CodeMirror.commands.insertSoftTab(editor);
} else {
CodeMirror.commands.defaultTab(editor);
}
}
},
Enter: 'emmetInsertLineBreak'
}
}
);
this.instance.on('focus', editor => {
if (typeof this.props.onFocus === 'function')
this.props.onFocus(editor);
});
this.instance.on('change', this.props.onChange);
this.instance.addKeyMap({
'Ctrl-Space': 'autocomplete'
});
this.instance.on('inputRead', (editor, input) => {
// Process further If this has autocompletition on and also the global
// autocomplete setting is on.
if (
!this.props.options.noAutocomplete &&
this.props.prefs.autoComplete
) {
if (
input.origin !== '+input' ||
input.text[0] === ';' ||
input.text[0] === ',' ||
input.text[0] === ' '
) {
return;
}
CodeMirror.commands.autocomplete(editor, null, {
completeSingle: false
});
}
});
}
// this.props.onCreation(this.instance);
}
render() {
return (
<div ref={el => (this.textarea = el)} style="width:100%;height:100%;">
{this.props.mode === 'monaco' ? null : <textarea />}
</div>
);
}
}

View File

@ -1,5 +1,5 @@
import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror.jsx';
import CodeEditor from './CodeEditor.jsx';
import { computeHtml, computeCss, computeJs } from '../computes';
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, writeFile, loadJS, getCompleteHtml } from '../utils';
@ -13,7 +13,7 @@ import { PreviewDimension } from './PreviewDimension.jsx';
const minCodeWrapSize = 33;
/* global htmlCodeEl
*/
*/
export default class ContentWrap extends Component {
constructor(props) {
@ -708,7 +708,7 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
options={{
mode: 'htmlmixed',
profile: 'xhtml',
@ -775,7 +775,7 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
options={{
mode: 'css',
gutters: [
@ -830,7 +830,7 @@ export default class ContentWrap extends Component {
/>
</div>
</div>
<UserCodeMirror
<CodeEditor
options={{
mode: 'javascript',
gutters: [

View File

@ -1,5 +1,5 @@
import { h, Component } from 'preact';
import UserCodeMirror from './UserCodeMirror';
import CodeEditor from './CodeEditor';
import { modes, HtmlModes, CssModes, JsModes } from '../codeModes';
import { log, loadJS } from '../utils';
@ -24,7 +24,7 @@ import { PreviewDimension } from './PreviewDimension';
const minCodeWrapSize = 33;
/* global htmlCodeEl
*/
*/
export default class ContentWrapFiles extends Component {
constructor(props) {
super(props);
@ -86,7 +86,7 @@ export default class ContentWrapFiles extends Component {
this.state.selectedFile &&
this.fileBuffers[this.state.selectedFile.path]
) {
this.fileBuffers[this.state.selectedFile.path].setValue(
this.fileBuffers[this.state.selectedFile.path].model.setValue(
getFileFromPath(
nextProps.currentItem.files,
this.state.selectedFile.path
@ -168,32 +168,43 @@ export default class ContentWrapFiles extends Component {
CodeMirror.autoLoadMode(this.cm, mode);
}
if (mime === 'application/json') {
mime = 'application/ld+json';
mime = this.props.prefs.isMonacoEditorOn ? 'json' : 'application/ld+json';
}
if (!this.props.prefs.isMonacoEditorOn) {
this.fileBuffers[file.path] = {
model: CodeMirror.Doc(
file.content || '',
detectedMode ? mime : 'text/plain'
)
};
} else {
this.fileBuffers[file.path] = {
model: monaco.editor.createModel(
file.content || '',
mime || 'javascript'
)
};
}
this.fileBuffers[file.path] = CodeMirror.Doc(
file.content || '',
detectedMode ? mime : 'text/plain'
);
}
onHtmlCodeChange(editor, change) {
this.cmCodes.html = editor.getValue();
this.cmCodes.html = this.editor.getValue();
this.props.onCodeChange(
this.state.selectedFile,
this.cmCodes.html,
change.origin !== 'setValue'
change ? change.origin !== 'setValue' : true
);
this.onCodeChange(editor, change);
this.onCodeChange(change);
}
onCodeChange(editor, change) {
onCodeChange(change) {
clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(() => {
// This is done so that multiple simultaneous setValue don't trigger too many preview refreshes
// and in turn too many file writes on a single file (eg. preview.html).
if (change.origin !== 'setValue') {
if (change ? change.origin !== 'setValue' : true) {
// Specifically checking for false so that the condition doesn't get true even
// on absent key - possible when the setting key hasn't been fetched yet.
if (this.prefs.autoPreview !== false) {
@ -249,7 +260,9 @@ export default class ContentWrapFiles extends Component {
}
}
cleanupErrors() {
this.cm.clearGutter('error-gutter');
if (!this.props.prefs.isMonacoEditorOn) {
// this.editor.clearGutter('error-gutter');
}
}
showErrors(lang, errors) {
@ -305,9 +318,11 @@ export default class ContentWrapFiles extends Component {
}
refreshEditor() {
if (this.state.selectedFile) {
this.cm.setValue(this.state.selectedFile.content);
this.editor.setValue(this.state.selectedFile.content);
}
if (this.editor) {
this.editor.refresh();
}
this.cm.refresh();
window.cm = this.cm;
this.clearConsole();
@ -401,6 +416,7 @@ export default class ContentWrapFiles extends Component {
}, 1);
}
this.updateSplits();
// this.editor.refresh();
}
mainSplitDragHandler() {
this.previewDimension.update({
@ -498,15 +514,24 @@ export default class ContentWrapFiles extends Component {
this.props.onFolderSelect(file);
return;
}
if (!this.fileBuffers[file.path]) {
this.createEditorDoc(file);
}
const currentState = this.editor.saveViewState();
if (currentState && this.state.selectedFile) {
this.fileBuffers[this.state.selectedFile.path].state = currentState;
}
this.setState({
editorOptions: this.getEditorOptions(file.name),
selectedFile: file
});
if (!this.fileBuffers[file.path]) {
this.createEditorDoc(file);
}
this.cm.swapDoc(this.fileBuffers[file.path]);
this.editor.setModel(this.fileBuffers[file.path].model);
if (this.fileBuffers[file.path].state) {
this.editor.restoreViewState(this.fileBuffers[file.path].state);
}
// var cmMode = 'html';
// if (file.name.match(/\.css$/)) {
// this.updateCssMode('css');
@ -516,7 +541,7 @@ export default class ContentWrapFiles extends Component {
// this.updateCssMode('html');
// }
// this.cm.setValue(file.content || '');
this.cm.focus();
this.editor.focus();
}
onMessageFromConsole() {
@ -637,15 +662,15 @@ export default class ContentWrapFiles extends Component {
/>
</div>
</div>
<UserCodeMirror
mode={'monaco'}
<CodeEditor
mode={this.props.prefs.isMonacoEditorOn ? 'monaco' : 'codemirror'}
value={
this.state.selectedFile ? this.state.selectedFile.content : ''
}
options={this.state.editorOptions}
prefs={this.props.prefs}
onChange={this.onHtmlCodeChange.bind(this)}
onCreation={editor => (this.cm = editor)}
ref={editor => (this.editor = editor)}
onFocus={this.editorFocusHandler.bind(this)}
/>
</div>

View File

@ -151,6 +151,13 @@ export default class Settings extends Component {
<TabPanel label="Editor">
<div>
<div>
<CheckboxSetting
title="Use experimental Monaco editor"
label="Use Monaco Editor"
pref={prefs.isMonacoEditorOn}
onChange={e => this.updateSetting(e, 'isMonacoEditorOn')}
/>
<Divider />
<div class="line">
<span>Default Preprocessors</span>
<div class="flex">
@ -321,7 +328,8 @@ export default class Settings extends Component {
Make the app ready to build some games for{' '}
<a href="https://js13kgames.com/" target="_blank" rel="noopener">
Js13kGames
</a>.
</a>
.
</p>
</TabPanel>
<TabPanel label="Advanced">

View File

@ -1,199 +0,0 @@
import { h, Component } from 'preact';
import CodeMirror from '../CodeMirror';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/edit/matchtags.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/closetag.js';
import 'codemirror/addon/comment/comment.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/xml-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/javascript-hint.js';
import 'codemirror/addon/hint/xml-hint.js';
import 'codemirror/addon/hint/html-hint.js';
import 'codemirror/addon/hint/css-hint.js';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/addon/search/searchcursor.js';
import 'codemirror/addon/search/search.js';
import 'codemirror/addon/dialog/dialog.js';
import 'codemirror/addon/search/jump-to-line.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/mode/css/css.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/htmlmixed/htmlmixed.js';
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'code-blast-codemirror/code-blast.js';
import emmet from '@emmetio/codemirror-plugin';
import { prettify } from '../utils';
emmet(CodeMirror);
export default class UserCodeMirror extends Component {
componentDidMount() {
this.initEditor();
}
shouldComponentUpdate(nextProps) {
if (nextProps.prefs !== this.props.prefs) {
const { prefs } = nextProps;
this.cm.setOption('indentWithTabs', prefs.indentWith !== 'spaces');
this.cm.setOption(
'blastCode',
prefs.isCodeBlastOn ? { effect: 2, shake: false } : false
);
this.cm.setOption('theme', prefs.editorTheme);
this.cm.setOption('indentUnit', +prefs.indentSize);
this.cm.setOption('tabSize', +prefs.indentSize);
this.cm.setOption('keyMap', prefs.keymap);
this.cm.setOption('lineWrapping', prefs.lineWrap);
this.cm.setOption('lineWrapping', prefs.autoCloseTags);
this.cm.refresh();
}
return false;
}
initEditor() {
const { options, prefs } = this.props;
if (this.props.mode === 'monaco') {
this.instance = monaco.editor.create(el, {
language: options.language,
roundedSelection: false,
scrollBeyondLastLine: false,
theme: 'vs-dark',
fontSize: 16,
minimap: {
enabled: false
},
wordWrap: 'on',
fontLigatures: true
});
this.instance.onDidChangeModelContent(onChange);
} else {
this.instance = CodeMirror.fromTextArea(this.textarea, {
mode: options.mode,
lineNumbers: true,
lineWrapping: !!prefs.lineWrap,
autofocus: options.autofocus || false,
autoCloseBrackets: true,
autoCloseTags: !!prefs.autoCloseTags,
matchBrackets: true,
matchTags: options.matchTags || false,
tabMode: 'indent',
keyMap: prefs.keyMap || 'sublime',
theme: prefs.editorTheme || 'monokai',
lint: !!options.lint,
tabSize: +prefs.indentSize || 2,
indentWithTabs: prefs.indentWith !== 'spaces',
indentUnit: +prefs.indentSize,
foldGutter: true,
styleActiveLine: true,
gutters: options.gutters || [],
// cursorScrollMargin: '20', has issue with scrolling
profile: options.profile || '',
extraKeys: {
Up: function(editor) {
// Stop up/down keys default behavior when saveditempane is open
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineUp(editor);
},
Down: function(editor) {
// if (isSavedItemsPaneOpen) {
// return;
// }
CodeMirror.commands.goLineDown(editor);
},
'Shift-Tab': function(editor) {
CodeMirror.commands.indentAuto(editor);
},
'Shift-Ctrl-F': function(editor) {
if (options.prettier) {
prettify({
content: editor.getValue(),
type: options.prettierParser
}).then(formattedCode => editor.setValue(formattedCode));
}
},
Tab: function(editor) {
if (options.emmet) {
const didEmmetWork = editor.execCommand(
'emmetExpandAbbreviation'
);
if (didEmmetWork === true) {
return;
}
const input = $('[data-setting=indentWith]:checked');
if (
!editor.somethingSelected() &&
(!prefs.indentWith || prefs.indentWith === 'spaces')
) {
// softtabs adds spaces. This is required because by default tab key will put tab, but we want
// to indent with spaces if `spaces` is preferred mode of indentation.
// `somethingSelected` needs to be checked otherwise, all selected code is replaced with softtab.
CodeMirror.commands.insertSoftTab(editor);
} else {
CodeMirror.commands.defaultTab(editor);
}
}
},
Enter: 'emmetInsertLineBreak'
}
});
this.instance.on('focus', editor => {
if (typeof this.props.onFocus === 'function')
this.props.onFocus(editor);
});
this.instance.on('change', this.props.onChange);
this.instance.addKeyMap({
'Ctrl-Space': 'autocomplete'
});
this.instance.on('inputRead', (editor, input) => {
// Process further If this has autocompletition on and also the global
// autocomplete setting is on.
if (
!this.props.options.noAutocomplete &&
this.props.prefs.autoComplete
) {
if (
input.origin !== '+input' ||
input.text[0] === ';' ||
input.text[0] === ',' ||
input.text[0] === ' '
) {
return;
}
CodeMirror.commands.autocomplete(editor, null, {
completeSingle: false
});
}
});
}
this.props.onCreation(this.instance);
}
render() {
return (
<textarea
ref={el => (this.textarea = el)}
name=""
id=""
cols="30"
rows="10"
/>
);
}
}

View File

@ -133,7 +133,8 @@ export default class App extends Component {
layoutMode: 2,
isJs13kModeOn: false,
autoCloseTags: true,
lang: 'en'
lang: 'en',
isMonacoEditorOn: false
};
this.prefs = {};
@ -1537,7 +1538,7 @@ export default class App extends Component {
<input
type="hidden"
name="data"
value="{&quot;title&quot;: &quot;New Pen!&quot;, &quot;html&quot;: &quot;<div>Hello, World!</div>&quot;}"
value='{"title": "New Pen!", "html": "<div>Hello, World!</div>"}'
/>
</form>
@ -1674,7 +1675,7 @@ export default class App extends Component {
<input
type="hidden"
name="data"
value="{&quot;title&quot;: &quot;New Pen!&quot;, &quot;html&quot;: &quot;<div>Hello, World!</div>&quot;}"
value='{"title": "New Pen!", "html": "<div>Hello, World!</div>"}'
/>
</form>
</div>

File diff suppressed because one or more lines are too long

3843
src/lib/monaco/monaco.css Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1931,10 +1931,6 @@ while the theme CSS file is loading */
.tabs__tabpanel-wrap {
flex: 1;
}
.monaco-editor {
width: 100%;
height: 300px !important; /* 25px for header */
}
@media screen and (max-width: 600px) {
body {