From 58da87898e69d3393755717bd60ae32c4176188a Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 29 Apr 2021 17:58:41 +0100 Subject: [PATCH] integrate monaco - refactors createEditor out to own code files - moves ace editor to own code file - adds monaco editor to own code file - add monaco bootstrap - update mst to include monaco asset - update grunt to include new files and integrate --- Gruntfile.js | 20 +- .../editor-client/src/js/ui/editor.js | 101 +- .../src/js/ui/editors/code-editor.js | 107 ++ .../src/js/ui/editors/code-editors/ace.js | 153 +++ .../src/js/ui/editors/code-editors/monaco.js | 1219 +++++++++++++++++ .../src/vendor/monaco/monaco-bootstrap.js | 24 + .../editor-client/templates/index.mst | 6 + 7 files changed, 1538 insertions(+), 92 deletions(-) create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/ace.js create mode 100644 packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js create mode 100644 packages/node_modules/@node-red/editor-client/src/vendor/monaco/monaco-bootstrap.js diff --git a/Gruntfile.js b/Gruntfile.js index 3cecfccd4..742b564da 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -181,6 +181,7 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", "packages/node_modules/@node-red/editor-client/src/js/ui/editor.js", "packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js", "packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js", "packages/node_modules/@node-red/editor-client/src/js/ui/tray.js", "packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js", @@ -285,7 +286,9 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/public/index.html", "packages/node_modules/@node-red/editor-client/public/favicon.ico", "packages/node_modules/@node-red/editor-client/public/icons", - "packages/node_modules/@node-red/editor-client/public/vendor" + "packages/node_modules/@node-red/editor-client/public/vendor", + "packages/node_modules/@node-red/editor-client/public/types/node", + "packages/node_modules/@node-red/editor-client/public/types/node-red", ] }, release: { @@ -375,11 +378,24 @@ module.exports = function(grunt) { src: [ 'ace/**', 'jquery/css/base/**', - 'font-awesome/**' + 'font-awesome/**', + 'monaco/dist/**', + 'monaco/types/extraLibs.js', + 'monaco/style.css', + 'monaco/monaco-bootstrap.js' ], expand: true, dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' }, + { + cwd: 'packages/node_modules/@node-red/editor-client/src', + src: [ + 'types/node/*.ts', + 'types/node-red/*.ts', + ], + expand: true, + dest: 'packages/node_modules/@node-red/editor-client/public/' + }, { cwd: 'packages/node_modules/@node-red/editor-client/src/icons', src: '**', diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 7f7decaa1..98b3dd450 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1525,6 +1525,7 @@ var buildingEditDialog = false; console.log("oneditresize",editing_node.id,editing_node.type,err.toString()); } } + if (nodeInfoEditor) {nodeInfoEditor.resize();} //markdown editor doesnt size up without this - idkw!? }, open: function(tray, done) { if (editing_node.hasOwnProperty('outputs')) { @@ -2759,94 +2760,7 @@ var buildingEditDialog = false; } } - function createEditor(options) { - var el = options.element || $("#"+options.id)[0]; - var toolbarRow = $("
").appendTo(el); - el = $("
").appendTo(el).addClass("red-ui-editor-text-container")[0]; - var editor = ace.edit(el); - editor.setTheme("ace/theme/tomorrow"); - var session = editor.getSession(); - session.on("changeAnnotation", function () { - var annotations = session.getAnnotations() || []; - var i = annotations.length; - var len = annotations.length; - while (i--) { - if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); } - else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); } - } - if (len > annotations.length) { session.setAnnotations(annotations); } - }); - if (options.mode) { - session.setMode(options.mode); - } - if (options.foldStyle) { - session.setFoldStyle(options.foldStyle); - } else { - session.setFoldStyle('markbeginend'); - } - if (options.options) { - editor.setOptions(options.options); - } else { - editor.setOptions({ - enableBasicAutocompletion:true, - enableSnippets:true, - tooltipFollowsMouse: false - }); - } - if (options.readOnly) { - editor.setOption('readOnly',options.readOnly); - editor.container.classList.add("ace_read-only"); - } - if (options.hasOwnProperty('lineNumbers')) { - editor.renderer.setOption('showGutter',options.lineNumbers); - } - editor.$blockScrolling = Infinity; - if (options.value) { - session.setValue(options.value,-1); - } - if (options.globals) { - setTimeout(function() { - if (!!session.$worker) { - session.$worker.send("setOptions", [{globals: options.globals, maxerr:1000}]); - } - },100); - } - if (options.mode === 'ace/mode/markdown') { - $(el).addClass("red-ui-editor-text-container-toolbar"); - editor.toolbar = customEditTypes['_markdown'].buildToolbar(toolbarRow,editor); - if (options.expandable !== false) { - var expandButton = $('').appendTo(editor.toolbar); - RED.popover.tooltip(expandButton, RED._("markdownEditor.expand")); - expandButton.on("click", function(e) { - e.preventDefault(); - var value = editor.getValue(); - RED.editor.editMarkdown({ - value: value, - width: "Infinity", - cursor: editor.getCursorPosition(), - complete: function(v,cursor) { - editor.setValue(v, -1); - editor.gotoLine(cursor.row+1,cursor.column,false); - setTimeout(function() { - editor.focus(); - },300); - } - }) - }); - } - var helpButton = $('').appendTo($(el).parent()); - RED.popover.create({ - target: helpButton, - trigger: 'click', - size: "small", - direction: "left", - content: RED._("markdownEditor.format"), - autoClose: 50 - }); - session.setUseWrapMode(true); - } - return editor; - } + return { init: function() { @@ -2862,6 +2776,8 @@ var buildingEditDialog = false; $("#node-dialog-cancel").trigger("click"); $("#node-config-dialog-cancel").trigger("click"); }); + //console.log("packages/node_modules/@node-red/editor-client/src/js/ui/editor.js ? init()") //TODO: Remove + RED.editor.codeEditor.init(); }, edit: showEditDialog, editConfig: showEditConfigNodeDialog, @@ -2908,9 +2824,14 @@ var buildingEditDialog = false; /** * Create a editor ui component * @param {object} options - the editor options - * @function + * @returs The code editor * @memberof RED.editor */ - createEditor: createEditor + createEditor: function(options) { + return RED.editor.codeEditor.create(options); + }, + get customEditTypes() { + return customEditTypes; + } } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js new file mode 100644 index 000000000..13ed25611 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js @@ -0,0 +1,107 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +/** + * @namespace RED.editor.codeEditor + */ + RED.editor.codeEditor = (function() { + + const MONACO = "monaco"; + const ACE = "ace"; + const defaultEditor = ACE; + const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} }; + var selectedCodeEditor = null; + var initialised = false; + + function init() { + var codeEditorSettings = RED.editor.codeEditor.settings; + var editorChoice = codeEditorSettings.lib === MONACO ? MONACO : ACE; + try { + var browser = RED.utils.getBrowserInfo(); + selectedCodeEditor = RED.editor.codeEditor[editorChoice]; + //fall back to default code editor if there are any issues + if (!selectedCodeEditor || (editorChoice === MONACO && (browser.ie || !window.monaco))) { + selectedCodeEditor = RED.editor.codeEditor[defaultEditor]; + } + initialised = selectedCodeEditor.init(); + } catch (error) { + selectedCodeEditor = null; + console.warn("Problem initialising '" + editorChoice + "' code editor", error); + } + if(!initialised) { + selectedCodeEditor = RED.editor.codeEditor[defaultEditor]; + initialised = selectedCodeEditor.init(); + } + } + + function create(options) { + //TODO: (quandry - for consideration) + // Below, I had to create a hidden element if options.id || options.element is not in the DOM + // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an + // invalid (non existing html element selector) (e.g. node-red-contrib-components does this) + // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre' + // code is thus skipped. + // In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI) + // Because one (or more) contrib nodes have left this bad code in place, how would we handle this? + // For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur. + // IMO, we should warn and exit as it is a coding error by the contrib author. + + if (!options) { + console.warn("createEditor() options are missing"); + options = {}; + } + + if (this.editor.type === MONACO) { + // compatibility (see above note) + if (!options.element && !options.id) { + options.id = 'node-backwards-compatability-dummy-editor'; + } + options.element = options.element || $("#" + options.id)[0]; + if (!options.element) { + console.warn("createEditor() options.element or options.id is not valid", options); + $("#dialog-form").append('