mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
bug fix orphaned models and graphical tray glitch
- ensure models are disposed correctly - clean up any orphaned models left after editor:close - improve concurrent multiple instances javascript models - fix graphical glitch
This commit is contained in:
parent
dedf5c52d9
commit
ffbd140a97
@ -99,6 +99,20 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
|
|
||||||
const modulesCache = {};
|
const modulesCache = {};
|
||||||
|
|
||||||
|
RED.events.on("editor:close",function() {
|
||||||
|
//catch all - when editor is closed, ensure lod models that are not explicitly destroyed by a call to .destroy() are dumped
|
||||||
|
let models = monaco.editor.getModels();
|
||||||
|
if(models && models.length) {
|
||||||
|
console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().")
|
||||||
|
for (let index = 0; index < models.length; index++) {
|
||||||
|
const model = models[index];
|
||||||
|
if(!model.isDisposed()) {
|
||||||
|
model.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to load/reload types.
|
* Helper function to load/reload types.
|
||||||
* @param {string} mod - type lib to load. Only known libs are currently supported.
|
* @param {string} mod - type lib to load. Only known libs are currently supported.
|
||||||
@ -681,7 +695,7 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
|
|
||||||
var editorSettings = RED.editor.codeEditor.settings || {};
|
var editorSettings = RED.editor.codeEditor.settings || {};
|
||||||
var loadedLibs = {JS:{}, TS:{}};//for tracking and later disposing of loaded type libs
|
var loadedLibs = {JS:{}, TS:{}};//for tracking and later disposing of loaded type libs
|
||||||
var watchTimer, elVisible, elVisibleMem;
|
var watchTimer;
|
||||||
var createThemeMenuOption = function (theme, keybinding) {
|
var createThemeMenuOption = function (theme, keybinding) {
|
||||||
return {
|
return {
|
||||||
// An unique identifier of the contributed action.
|
// An unique identifier of the contributed action.
|
||||||
@ -708,7 +722,7 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
if (mode) {
|
if (mode) {
|
||||||
mode = mode.replace("ace/mode/", "");
|
mode = mode.replace("ace/mode/", "");
|
||||||
} else {
|
} else {
|
||||||
mode = "javascript";
|
mode = "text";
|
||||||
}
|
}
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "nrjavascript":
|
case "nrjavascript":
|
||||||
@ -764,6 +778,7 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
if (editorOptions.lineNumbers === false) { editorOptions.lineNumbers = false; }
|
if (editorOptions.lineNumbers === false) { editorOptions.lineNumbers = false; }
|
||||||
if (editorOptions.theme == null) { editorOptions.theme = monacoThemes[0]; }
|
if (editorOptions.theme == null) { editorOptions.theme = monacoThemes[0]; }
|
||||||
if (editorOptions.mode == null) { editorOptions.mode = convertAceModeToMonacoLang(options.mode); }
|
if (editorOptions.mode == null) { editorOptions.mode = convertAceModeToMonacoLang(options.mode); }
|
||||||
|
if (editorOptions.automaticLayout == null) { editorOptions.automaticLayout = true; }
|
||||||
|
|
||||||
if (options.foldStyle) {
|
if (options.foldStyle) {
|
||||||
switch (options.foldStyle) {
|
switch (options.foldStyle) {
|
||||||
@ -917,13 +932,8 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
ed.session = ed;
|
ed.session = ed;
|
||||||
ed.renderer = {};
|
ed.renderer = {};
|
||||||
|
|
||||||
ed.setMode = function(mode, cb) {
|
ed.setMode = function(mode, cb, resize) {
|
||||||
if (mode && typeof mode === "object") {
|
if(resize==null) { resize = true; }
|
||||||
var options = mode;
|
|
||||||
mode = options.path;
|
|
||||||
} else {
|
|
||||||
mode = mode || "text";
|
|
||||||
}
|
|
||||||
mode = convertAceModeToMonacoLang(mode);
|
mode = convertAceModeToMonacoLang(mode);
|
||||||
var oldModel = ed.getModel();
|
var oldModel = ed.getModel();
|
||||||
var oldValue = ed.getValue();
|
var oldValue = ed.getValue();
|
||||||
@ -935,9 +945,10 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
var oldSelections = ed.getSelections();
|
var oldSelections = ed.getSelections();
|
||||||
var oldPosition = ed.getPosition();
|
var oldPosition = ed.getPosition();
|
||||||
oldValue = oldModel.getValue() || "";
|
oldValue = oldModel.getValue() || "";
|
||||||
|
if(!oldModel.isDisposed()) { oldModel.dispose(); }
|
||||||
|
ed.setModel(null);
|
||||||
newModel = monaco.editor.createModel((oldValue || ""), mode);
|
newModel = monaco.editor.createModel((oldValue || ""), mode);
|
||||||
ed.setModel(newModel);
|
ed.setModel(newModel);
|
||||||
oldModel.dispose();
|
|
||||||
ed.setScrollTop(oldScrollTop, 1/* immediate */);
|
ed.setScrollTop(oldScrollTop, 1/* immediate */);
|
||||||
ed.setScrollLeft(oldScrollLeft, 1/* immediate */);
|
ed.setScrollLeft(oldScrollLeft, 1/* immediate */);
|
||||||
ed.setPosition(oldPosition);
|
ed.setPosition(oldPosition);
|
||||||
@ -949,8 +960,10 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
if (cb && typeof cb == "function") {
|
if (cb && typeof cb == "function") {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
if(resize) {
|
||||||
this.resize(); //cause a call to layout()
|
this.resize(); //cause a call to layout()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ed.getRange = function getRange(){
|
ed.getRange = function getRange(){
|
||||||
var r = ed.getSelection();
|
var r = ed.getSelection();
|
||||||
@ -1032,7 +1045,9 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
try {
|
try {
|
||||||
var m = this.getModel();
|
var m = this.getModel();
|
||||||
|
if(m && !m.isDisposed()) {
|
||||||
m.dispose();
|
m.dispose();
|
||||||
|
}
|
||||||
this.setModel(null);
|
this.setModel(null);
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
|
|
||||||
@ -1182,41 +1197,19 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
|
|
||||||
//as models are signleton, consts and let are avialable to other javascript instances
|
//as models are signleton, consts and let are avialable to other javascript instances
|
||||||
//so when not focused, set editor mode to text temporarily to avoid multiple defs
|
//so when not focused, set editor mode to text temporarily to avoid multiple defs
|
||||||
|
|
||||||
|
|
||||||
ed.onDidBlurEditorWidget(function() {
|
ed.onDidBlurEditorWidget(function() {
|
||||||
if(isVisible(el) == false) {
|
if(isVisible(el) == false) {
|
||||||
if(ed._mode == "javascript") {
|
onVisibilityChange(false);
|
||||||
ed.setMode('text');
|
|
||||||
ed._tempMode = "text";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ed.onDidFocusEditorWidget(function() {
|
ed.onDidFocusEditorWidget(function() {
|
||||||
if(ed._mode == "javascript" && ed._tempMode == "text") {
|
onVisibilityChange(true, 10);
|
||||||
ed._tempMode = "";
|
|
||||||
setTimeout(function() {
|
|
||||||
ed.setMode('javascript');
|
|
||||||
}, 5);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watchVisibility(el, function(visible, element) {
|
function visibilityWatcher(element, callback) {
|
||||||
if(visible) {
|
|
||||||
if(ed._mode == "javascript" && ed._tempMode == "text") {
|
|
||||||
ed._tempMode = "";
|
|
||||||
setTimeout(function() {
|
|
||||||
ed.setMode('javascript');
|
|
||||||
}, 5);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(ed._mode == "javascript") {
|
|
||||||
ed.setMode('text');
|
|
||||||
ed._tempMode = "text";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function watchVisibility(element, callback) {
|
|
||||||
try {
|
try {
|
||||||
var options = {
|
var options = {
|
||||||
root: $(element).closest("div.red-ui-tray-content")[0] || document,
|
root: $(element).closest("div.red-ui-tray-content")[0] || document,
|
||||||
@ -1225,32 +1218,51 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
};
|
};
|
||||||
var observer = new IntersectionObserver(function(entries, observer) {
|
var observer = new IntersectionObserver(function(entries, observer) {
|
||||||
entries.forEach(function(entry) {
|
entries.forEach(function(entry) {
|
||||||
callback(entry.intersectionRatio > 0, entry);
|
callback(entry.intersectionRatio > 0, 5, entry);
|
||||||
});
|
});
|
||||||
}, options);
|
}, options);
|
||||||
observer.observe(element);
|
observer.observe(element);
|
||||||
} catch (e1) {
|
} catch (e1) {
|
||||||
//browser not supporting IntersectionObserver?
|
//browser not supporting IntersectionObserver? then fall back to polling!
|
||||||
//fall back to polling
|
|
||||||
try {
|
try {
|
||||||
|
let elVisibleMem = isVisible(el)
|
||||||
watchTimer = setInterval(function() {
|
watchTimer = setInterval(function() {
|
||||||
elVisible = isVisible(el);
|
let elVisible = isVisible(el);
|
||||||
if(elVisible != elVisibleMem) {
|
if(elVisible != elVisibleMem) {
|
||||||
callback(elVisible, element);
|
callback(elVisible, 100, element);
|
||||||
}
|
}
|
||||||
elVisible = elVisibleMem;
|
elVisibleMem = elVisible;
|
||||||
}, 200);
|
}, 100);
|
||||||
} catch (e2) { }
|
} catch (e2) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onVisibilityChange(visible, delay, element) {
|
||||||
|
if(visible) {
|
||||||
|
if(ed._mode == "javascript" && ed._tempMode == "text") {
|
||||||
|
ed._tempMode = "";
|
||||||
|
setTimeout(function() {
|
||||||
|
if(el.parentElement) { //ensure el is still in DOM
|
||||||
|
ed.setMode('javascript', undefined, false);
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
} else if(ed._mode == "javascript") {
|
||||||
|
if(el.parentElement) { //ensure el is still in DOM
|
||||||
|
ed.setMode('text', undefined, false);
|
||||||
|
ed._tempMode = "text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visibilityWatcher(el, onVisibilityChange);
|
||||||
|
|
||||||
//by default, set javascript editors to text mode.
|
//by default, set javascript editors to text mode.
|
||||||
//when elemt becomes visible, it will be (re) set to javascript mode
|
//when elemt becomes visible, it will be (re) set to javascript mode
|
||||||
//this is to ensure multiple editors sharing the model dont presnet its
|
//this is to ensure multiple editors sharing the model dont presnet its
|
||||||
//consts & lets to each other
|
//consts & lets to each other
|
||||||
if(ed._mode == "javascript") {
|
if(ed._mode == "javascript") {
|
||||||
ed.setMode('text');
|
ed.setMode('text', undefined, false);
|
||||||
ed._tempMode = "text";
|
ed._tempMode = "text";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user