import React, { Component } from 'react'; import Dialog, { DialogActions, DialogContent, DialogContentText, DialogTitle, } from 'material-ui/Dialog'; import _ from 'lodash' import Drawer from 'material-ui/Drawer'; import './TPIMonaco.css' import TPICodeSetting from '../TPICodeSetting' import * as monaco from 'monaco-editor' import { fromStore, toStore } from 'educoder' import './TPIMonacoConfig' monaco.editor.defineTheme('myCoolTheme', { base: 'vs-dark', inherit: true, rules: [ { token: 'green', background: 'FF0000', foreground: '00FF00', fontStyle: 'italic'}, { token: 'red', foreground: 'FF0000' , fontStyle: 'bold underline'}, { background: '#121c23' }, // { foreground: 'FFFFFF' } ], // editor_monaco._themeService._knownThemes.get('myCoolTheme') colors: { // 'editor.foreground': '#FFFFFF', 'editor.background': '#121c23', // 'editor.selectionHighlightBorder': '#ffffff', // 'input.border': '#ffffff', 'editor.lineHighlightBorder': '#222c34', // .current-line } }); const $ = window.$; function loadMonacoResouce(callback) { let _url_origin = ``; let prefix = 'react/build' if (window.location.port == 3007) { prefix = '' } else { // _url_origin = `https://testeduplus2.educoder.net/`; _url_origin = 'http://pre-newweb.educoder.net'; } const $ = window.$; if (!window['monaco']) { // How to import this library in a create-react-app? // https://github.com/Microsoft/monaco-editor/issues/82 // window.require = { paths: { 'vs': '../node_modules/monaco-editor/min/vs' } }; // window.require = { paths: { 'vs': `${_url_origin}${prefix}/js/monaco/vs` } }; // $('head').append($('<link rel="stylesheet" type="text/css" />') // .attr('href', `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.css`)); // $.ajaxSetup({ // cache: true // }); // $.when( // // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/language/typescript/tsMode.js` ), // // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/javascript/javascript.js` ), // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/python/python.js` ), // // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/language/typescript/tsWorker.js` ), // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/loader.js` ), // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.nls.js` ), // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.js` ), // $.Deferred(function( deferred ){ // console.log('Deferred') // // TODO 暂时放这里 // $( deferred.resolve ); // checkIfLoaded(callback); // }) // ).done(function(){ // debugger; // // TODO 没执行到这里 // console.log('done done done ') // //place your code here, the scripts are all loaded // // callback && callback() // }); // require.config({ paths: { 'vs': `/monaco/vs` }}); // require(['/vs/editor/editor.main'], function() { callback && callback() var editor = monaco.editor.create(document.getElementById('container'), { value: [ 'function x() {', '\tconsole.log("Hello world!");', '}' ].join('\n'), language: 'javascript' }); // }); } else { callback && callback() } } function checkIfLoaded (callback) { if (window.monaco && window.monaco.editor) { $.ajaxSetup({ cache: false }); callback && callback() } else { console.log('check again 2s later..') setTimeout(() => { checkIfLoaded(callback) }, 2000); } } /* language javascript css less scss html typescript java ruby vb r python php perl go cpp csharp sql pgsql mysql swift objective-c shell redis xml yaml markdown 'JFinal': 'text/x-java', 'Java': 'text/x-java', 'Kotlin': 'text/x-kotlin', 'C/C++' : 'text/x-c++src', 'MachineLearning': { name: "python", version: 3, singleLineStringErrors: false }, 'Python2.7': { name: "python", version: 3, singleLineStringErrors: false }, 'Python3.6': { name: "python", version: 3, singleLineStringErrors: false }, */ const mirror2LanguageMap = { 'JFinal': 'java', 'Java': 'java', 'JavaWeb': 'java', 'Kotlin': 'java', 'Html': 'html', 'Css': 'css', 'Javascript': 'javascript', 'JavaScript': 'javascript', 'C/C++': 'cpp', 'MachineLearning': 'python', 'Python2.7': 'python', 'Python3.6': 'python', 'C#': 'csharp', 'R': 'r' } function getLanguageByMirrorName(mirror_name) { let lang = 'javascript' if (mirror_name && mirror_name.length) { // 需要倒着遍历, html、css的实训,主评测语言环境是python,小类别是html或css // TODO http://localhost:3007/tasks/hmcwa3g8typ5?debug=t ["Python3.6", "VNC", "Html"] for (let i = mirror_name.length - 1; i >= 0; i--) { let languageVal = mirror2LanguageMap[mirror_name[i]]; if (languageVal) { lang = languageVal; break; } } } return lang; } let notCallCodeMirrorOnChangeFlag = false; /** props : mirror_name 决定语言 isEditablePath repositoryCode codemirrorDidMount shixun.forbid_copy showSettingDrawer, settingDrawerOpen */ class TPIMonaco extends Component { constructor(props) { super(props) loadMonacoResouce(); this.state = { cmFontSize: fromStore('cmFontSize', 16), autoCompleteSwitch: fromStore('autoCompleteSwitch', true), } } componentDidUpdate(prevProps, prevState, snapshot) { const { mirror_name } = this.props const editor_monaco = this.editor_monaco; if (editor_monaco && !_.isEqual(prevProps.mirror_name, mirror_name)) { // TODO 后期考虑加入,根据文件类型的不同,使用不同的lang const lang = getLanguageByMirrorName(mirror_name); // https://github.com/Microsoft/monaco-editor/issues/539 window.monaco.editor.setModelLanguage(editor_monaco.getModel(), lang) } else if (prevProps.isEditablePath != this.props.isEditablePath) { if (this.props.isEditablePath || this.props.shixun && this.props.shixun.code_edit_permission == true) { editor_monaco.updateOptions({readOnly: false}) } else { editor_monaco.updateOptions({readOnly: true}) } } else if (editor_monaco && prevProps.codeLoading === true && this.props.codeLoading === false ) { if (this.props.repositoryCode != editor_monaco.getValue()) { // newProps.repositoryCode !== this.props.repositoryCode && notCallCodeMirrorOnChangeFlag = true; // 重要:setState(因获取代码、重置代码等接口引起的调用)调用引起的变化才需要setValue editor_monaco.setValue(this.props.repositoryCode) } // 代码没变也需要layout,可能从命令行自动切回了代码tab editor_monaco.layout(); // Clears the editor's undo history. // TODO // extend_editor.clearHistory() } } componentWillUnmount() { this.editor_monaco && this.editor_monaco.dispose() } componentDidMount() { checkIfLoaded(() => { let value = [ 'define([], function() {', '\treturn ({p1, p2}) => {', '\t\treturn Promise.resolve("Hello, World");', '\t};', '});' ].join('\n'); // value = [ // 'function x() {', // '\tconsole.log("Hello world!");', // '}' // ].join('\n'), value = "<h1>Hello</h1>↵<!-- ********* Begin ********* -->↵ ↵ ↵<!-- ********* End ********* -->" value = this.props.repositoryCode const height = $('#games_repository_contents').height() - 40 const width = $('#games_repository_contents').width() $('#codetab_con_1').height(height) const lang = getLanguageByMirrorName(this.props.mirror_name); const editor = window.monaco.editor.create(document.getElementById('extend-challenge-file-edit'), { value: value, readOnly: !this.props.isEditablePath && this.props.shixun && this.props.shixun.code_edit_permission != true, // 属性说明 // http://testeduplus2.educoder.net/react/build/static/node_modules/_monaco-editor@0.15.6@monaco-editor/esm/vs/editor/common/config/commonEditorConfig.js // https://github.com/Microsoft/monaco-editor/issues/29 scrollBeyondLastLine: false, language: lang, // language: 'css', // theme: "vs-dark", theme: "myCoolTheme", fontSize: this.state.cmFontSize }); window.editor_monaco = editor; this.editor_monaco = editor // editor.setPosition({ lineNumber: 2, column: 30 }); // editor.model.onDidChangeContent((event) => { // if (notCallCodeMirrorOnChangeFlag === true) { // notCallCodeMirrorOnChangeFlag = false // return; // } // this.props.onRepositoryCodeUpdate(editor.getValue()) // }); editor.onDidChangeModelContent((event) => { if (notCallCodeMirrorOnChangeFlag === true) { notCallCodeMirrorOnChangeFlag = false return; } this.props.onRepositoryCodeUpdate && this.props.onRepositoryCodeUpdate(editor.getValue()) }) this.props.codemirrorDidMount && this.props.codemirrorDidMount() if(this.props.shixun && this.props.shixun.forbid_copy == true) { // 禁用粘贴 // https://github.com/Microsoft/monaco-editor/issues/100 window.editor_monaco.onDidPaste( (a, b, c) => { window.__pastePosition = a }) window.addEventListener('paste', (a, b, c) => { const selection = window.editor_monaco.getSelection(); const range = new monaco.Range( window.__pastePosition.startLineNumber || selection.endLineNumber, window.__pastePosition.startColumn || selection.endColumn, window.__pastePosition.endLineNumber || selection.endLineNumber, window.__pastePosition.endColumn || selection.endColumn,); window.editor_monaco.executeEdits('', [{range, text: ''}] ) }) // 禁用复制 window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_C, () => null); window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_V, () => null); } setTimeout(() => { editor.layout(); editor.focus(); }, 600) window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, () => { this.props.doFileUpdateRequestOnCodeMirrorBlur(); return false; }); }) // window.document.onkeydown = (e) => { // e = window.event || e; // if(e.keyCode== 83 && e.ctrlKey){ // /*延迟,兼容FF浏览器 */ // // setTimeout(function(){ // // alert('ctrl+s'); // // },1); // this.props.doFileUpdateRequestOnCodeMirrorBlur(); // return false; // } // }; } onFontSizeChange = (value) => { toStore('cmFontSize', value) this.editor_monaco.updateOptions({fontSize: value}) this.setState({ cmFontSize: value }); } onAutoCompleteSwitchChange = () => { } render() { const { repositoryCode, showSettingDrawer, settingDrawerOpen } = this.props; const { cmFontSize } = this.state; return ( <React.Fragment> <Drawer anchor="right" className="settingDrawer" width={260} open={settingDrawerOpen} onClose={() => showSettingDrawer( false )} > <TPICodeSetting {...this.props} {...this.state} onFontSizeChange={this.onFontSizeChange} onAutoCompleteSwitchChange={this.onAutoCompleteSwitchChange} ></TPICodeSetting> </Drawer> <div className = "" id="extend-challenge-file-edit" name="content" style={{ width: '100%', height: '100%', border:'1px solid grey' }}></div> </React.Fragment> ); } } export default ( TPIMonaco ) ; /** // http://testeduplus2.educoder.net/react/build/static/node_modules/ _monaco-editor@0.15.6@monaco-editor/esm/vs/editor/common/config/editorOptions.js export var EDITOR_DEFAULTS = { inDiffEditor: false, wordSeparators: USUAL_WORD_SEPARATORS, lineNumbersMinChars: 5, lineDecorationsWidth: 10, readOnly: false, mouseStyle: 'text', disableLayerHinting: false, automaticLayout: false, wordWrap: 'off', wordWrapColumn: 80, wordWrapMinified: true, wrappingIndent: 1 wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++', wordWrapBreakAfterCharacters: ' \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', wordWrapBreakObtrusiveCharacters: '.', autoClosingBrackets: 'languageDefined', autoClosingQuotes: 'languageDefined', autoSurround: 'languageDefined', autoIndent: true, dragAndDrop: true, emptySelectionClipboard: true, copyWithSyntaxHighlighting: true, useTabStops: true, multiCursorModifier: 'altKey', multiCursorMergeOverlapping: true, accessibilitySupport: 'auto', showUnused: true, viewInfo: { extraEditorClassName: '', disableMonospaceOptimizations: false, rulers: [], ariaLabel: nls.localize('editorViewAccessibleLabel', "Editor content"), renderLineNumbers: 1 // On , renderCustomLineNumbers: null, selectOnLineNumbers: true, glyphMargin: true, revealHorizontalRightPadding: 30, roundedSelection: true, overviewRulerLanes: 2, overviewRulerBorder: true, cursorBlinking: 1 /* Blink , mouseWheelZoom: false, cursorStyle: TextEditorCursorStyle.Line, cursorWidth: 0, hideCursorInOverviewRuler: false, scrollBeyondLastLine: true, scrollBeyondLastColumn: 5, smoothScrolling: false, stopRenderingLineAfter: 10000, renderWhitespace: 'none', renderControlCharacters: false, fontLigatures: false, renderIndentGuides: true, highlightActiveIndentGuide: true, renderLineHighlight: 'line', scrollbar: { vertical: 1 /* Auto , horizontal: 1 /* Auto , arrowSize: 11, useShadows: true, verticalHasArrows: false, horizontalHasArrows: false, horizontalScrollbarSize: 10, horizontalSliderSize: 10, verticalScrollbarSize: 14, verticalSliderSize: 14, handleMouseWheel: true, mouseWheelScrollSensitivity: 1, }, minimap: { enabled: true, side: 'right', showSlider: 'mouseover', renderCharacters: true, maxColumn: 120 }, fixedOverflowWidgets: false, }, contribInfo: { selectionClipboard: true, hover: { enabled: true, delay: 300, sticky: true }, links: true, contextmenu: true, quickSuggestions: { other: true, comments: false, strings: false }, quickSuggestionsDelay: 10, parameterHints: { enabled: true, cycle: false }, iconsInSuggestions: true, formatOnType: false, formatOnPaste: false, suggestOnTriggerCharacters: true, acceptSuggestionOnEnter: 'on', acceptSuggestionOnCommitCharacter: true, wordBasedSuggestions: true, suggestSelection: 'recentlyUsed', suggestFontSize: 0, suggestLineHeight: 0, tabCompletion: 'off', suggest: { filterGraceful: true, snippets: 'inline', snippetsPreventQuickSuggestions: true, localityBonus: false }, selectionHighlight: true, occurrencesHighlight: true, codeLens: true, folding: true, foldingStrategy: 'auto', showFoldingControls: 'mouseover', matchBrackets: true, find: { seedSearchStringFromSelection: true, autoFindInSelection: false, globalFindClipboard: false }, colorDecorators: true, lightbulbEnabled: true, codeActionsOnSave: {}, codeActionsOnSaveTimeout: 750 }, }; */