From eb8a609519fe45b13aa945e36637381741313aed Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 21 Nov 2017 14:22:33 +0000 Subject: [PATCH] added disposable manager added model manager [ci skip] --- .../repo/components/repo_editor.vue | 14 ++-- .../javascripts/repo/lib/common/disposable.js | 14 ++++ .../javascripts/repo/lib/common/model.js | 66 ++++++++++++++----- .../repo/lib/common/model_manager.js | 31 +++++++++ .../repo/lib/decorations/controller.js | 2 +- .../javascripts/repo/lib/diff/controller.js | 25 +++---- .../javascripts/repo/lib/diff/worker.js | 39 ++++++----- app/assets/javascripts/repo/lib/editor.js | 41 ++++++------ 8 files changed, 152 insertions(+), 80 deletions(-) create mode 100644 app/assets/javascripts/repo/lib/common/disposable.js create mode 100644 app/assets/javascripts/repo/lib/common/model_manager.js diff --git a/app/assets/javascripts/repo/components/repo_editor.vue b/app/assets/javascripts/repo/components/repo_editor.vue index e0ce8a97665..8772b45669f 100644 --- a/app/assets/javascripts/repo/components/repo_editor.vue +++ b/app/assets/javascripts/repo/components/repo_editor.vue @@ -3,13 +3,15 @@ import { mapGetters, mapActions } from 'vuex'; import flash from '../../flash'; import monacoLoader from '../monaco_loader'; -import editor from '../lib/editor'; +import Editor from '../lib/editor'; export default { destroyed() { - editor.dispose(); + this.editor.dispose(); }, mounted() { + this.editor = Editor.create(); + if (this.monaco) { this.initMonaco(); } else { @@ -26,11 +28,11 @@ export default { initMonaco() { if (this.shouldHideEditor) return; - editor.clearEditor(); + this.editor.clearEditor(); this.getRawFileData(this.activeFile) .then(() => { - editor.createInstance(this.$el); + this.editor.createInstance(this.$el); }) .then(() => this.setupEditor()) .catch(() => flash('Error setting up monaco. Please try again.')); @@ -38,9 +40,9 @@ export default { setupEditor() { if (!this.activeFile) return; - const model = editor.createModel(this.activeFile); + const model = this.editor.createModel(this.activeFile); - editor.attachModel(model); + this.editor.attachModel(model); model.onChange((m) => { this.changeFileContent({ file: this.activeFile, diff --git a/app/assets/javascripts/repo/lib/common/disposable.js b/app/assets/javascripts/repo/lib/common/disposable.js new file mode 100644 index 00000000000..84b29bdb600 --- /dev/null +++ b/app/assets/javascripts/repo/lib/common/disposable.js @@ -0,0 +1,14 @@ +export default class Disposable { + constructor() { + this.disposers = new Set(); + } + + add(...disposers) { + disposers.forEach(disposer => this.disposers.add(disposer)); + } + + dispose() { + this.disposers.forEach(disposer => disposer.dispose()); + this.disposers.clear(); + } +} diff --git a/app/assets/javascripts/repo/lib/common/model.js b/app/assets/javascripts/repo/lib/common/model.js index 77d36b4f52c..75828bfb09d 100644 --- a/app/assets/javascripts/repo/lib/common/model.js +++ b/app/assets/javascripts/repo/lib/common/model.js @@ -1,26 +1,59 @@ /* global monaco */ +import Disposable from './disposable'; export default class Model { constructor(file) { + this.disposable = new Disposable(); this.file = file; this.content = file.content !== '' ? file.content : file.raw; - this.originalModel = monaco.editor.createModel( - this.content, - undefined, - new monaco.Uri(null, null, `original/${this.file.path}`), - ); - this.model = monaco.editor.createModel( - this.content, - undefined, - new monaco.Uri(null, null, this.file.path), + + this.disposable.add( + this.originalModel = monaco.editor.createModel( + this.content, + undefined, + new monaco.Uri(null, null, `original/${this.file.path}`), + ), + this.model = monaco.editor.createModel( + this.content, + undefined, + new monaco.Uri(null, null, this.file.path), + ), ); - this.disposers = new Map(); + + this.attachedToWorker = false; + this.events = new Map(); } get url() { return this.model.uri.toString(); } + get originalUrl() { + return this.originalModel.uri.toString(); + } + + get path() { + return this.file.path; + } + + get diffModel() { + return { + url: this.model.uri.toString(), + versionId: this.model.getVersionId(), + lines: this.model.getLinesContent(), + EOL: '\n', + }; + } + + get originalDiffModel() { + return { + url: this.originalModel.uri.toString(), + versionId: this.originalModel.getVersionId(), + lines: this.originalModel.getLinesContent(), + EOL: '\n', + }; + } + getModel() { return this.model; } @@ -29,18 +62,21 @@ export default class Model { return this.originalModel; } + setAttachedToWorker(val) { + this.attachedToWorker = val; + } + onChange(cb) { - this.disposers.set( + this.events.set( this.file.path, this.model.onDidChangeContent(e => cb(this.model, e)), ); } dispose() { - this.model.dispose(); - this.originalModel.dispose(); + this.disposable.dispose(); - this.disposers.forEach(disposer => disposer.dispose()); - this.disposers.clear(); + this.events.forEach(disposer => disposer.dispose()); + this.events.clear(); } } diff --git a/app/assets/javascripts/repo/lib/common/model_manager.js b/app/assets/javascripts/repo/lib/common/model_manager.js new file mode 100644 index 00000000000..fdb1e148681 --- /dev/null +++ b/app/assets/javascripts/repo/lib/common/model_manager.js @@ -0,0 +1,31 @@ +import Disposable from './disposable'; +import Model from './model'; + +export default class ModelManager { + constructor() { + this.disposable = new Disposable(); + this.models = new Map(); + } + + hasCachedModel(path) { + return this.models.has(path); + } + + addModel(file) { + if (this.hasCachedModel(file.path)) { + return this.models.get(file.path); + } + + const model = new Model(file); + this.models.set(model.path, model); + this.disposable.add(model); + + return model; + } + + dispose() { + // dispose of all the models + this.disposable.dispose(); + this.models.clear(); + } +} diff --git a/app/assets/javascripts/repo/lib/decorations/controller.js b/app/assets/javascripts/repo/lib/decorations/controller.js index 0ab74f1ebdf..eb369dbbfe1 100644 --- a/app/assets/javascripts/repo/lib/decorations/controller.js +++ b/app/assets/javascripts/repo/lib/decorations/controller.js @@ -33,7 +33,7 @@ class DecorationsController { this.editorDecorations.set( model.url, - editor.instance.deltaDecorations(oldDecorations, decorations), + editor.editorInstance.instance.deltaDecorations(oldDecorations, decorations), ); } diff --git a/app/assets/javascripts/repo/lib/diff/controller.js b/app/assets/javascripts/repo/lib/diff/controller.js index 77d07b730cf..39c6cb92e0f 100644 --- a/app/assets/javascripts/repo/lib/diff/controller.js +++ b/app/assets/javascripts/repo/lib/diff/controller.js @@ -1,4 +1,5 @@ /* global monaco */ +import Disposable from '../common/disposable'; import DirtyDiffWorker from './worker'; import decorationsController from '../decorations/controller'; @@ -32,27 +33,21 @@ export const decorate = (model, changes) => { }; export default class DirtyDiffController { - constructor() { + constructor(modelManager) { + this.disposable = new Disposable(); this.editorSimpleWorker = null; - this.models = new Map(); - this.worker = new DirtyDiffWorker(); + this.modelManager = modelManager; + this.disposable.add(this.worker = new DirtyDiffWorker()); } attachModel(model) { - if (this.models.has(model.getModel().uri.toString())) return; + if (model.attachedToWorker) return; - [model.getModel(), model.getOriginalModel()].forEach((iModel) => { - this.worker.attachModel({ - url: iModel.uri.toString(), - versionId: iModel.getVersionId(), - lines: iModel.getLinesContent(), - EOL: '\n', - }); + [model.getModel(), model.getOriginalModel()].forEach(() => { + this.worker.attachModel(model); }); model.onChange((_, e) => this.computeDiff(model, e)); - - this.models.set(model.getModel().uri.toString(), model); } computeDiff(model, e) { @@ -66,8 +61,6 @@ export default class DirtyDiffController { } dispose() { - this.models.clear(); - this.worker.dispose(); - decorationsController.dispose(); + this.disposable.dispose(); } } diff --git a/app/assets/javascripts/repo/lib/diff/worker.js b/app/assets/javascripts/repo/lib/diff/worker.js index 93d94f8d138..39047d85507 100644 --- a/app/assets/javascripts/repo/lib/diff/worker.js +++ b/app/assets/javascripts/repo/lib/diff/worker.js @@ -1,15 +1,17 @@ /* global monaco */ +import Disposable from '../common/disposable'; + export default class DirtyDiffWorker { constructor() { this.editorSimpleWorker = null; - this.models = new Map(); + this.disposable = new Disposable(); this.actions = new Set(); // eslint-disable-next-line promise/catch-or-return monaco.editor.createWebWorker({ moduleId: 'vs/editor/common/services/editorSimpleWorker', }).getProxy().then((editorSimpleWorker) => { - this.editorSimpleWorker = editorSimpleWorker; + this.disposable.add(this.editorSimpleWorker = editorSimpleWorker); this.ready(); }); } @@ -26,10 +28,11 @@ export default class DirtyDiffWorker { } attachModel(model) { - if (this.editorSimpleWorker && !this.models.has(model.url)) { - this.editorSimpleWorker.acceptNewModel(model); + if (this.editorSimpleWorker && !model.attachedToWorker) { + this.editorSimpleWorker.acceptNewModel(model.diffModel); + this.editorSimpleWorker.acceptNewModel(model.originalDiffModel); - this.models.set(model.url, model); + model.setAttachedToWorker(true); } else if (!this.editorSimpleWorker) { this.actions.add({ attachModel: [model], @@ -40,7 +43,7 @@ export default class DirtyDiffWorker { modelChanged(model, e) { if (this.editorSimpleWorker) { this.editorSimpleWorker.acceptModelChanged( - model.getModel().uri.toString(), + model.url, e, ); } else { @@ -52,27 +55,23 @@ export default class DirtyDiffWorker { compute(model, cb) { if (this.editorSimpleWorker) { - // eslint-disable-next-line promise/catch-or-return - this.editorSimpleWorker.computeDiff( - model.getOriginalModel().uri.toString(), - model.getModel().uri.toString(), + return this.editorSimpleWorker.computeDiff( + model.originalUrl, + model.url, ).then(cb); - } else { - this.actions.add({ - compute: [model, cb], - }); } + + this.actions.add({ + compute: [model, cb], + }); + + return null; } dispose() { - this.models.forEach(model => - this.editorSimpleWorker.acceptRemovedModel(model.url), - ); - this.models.clear(); - this.actions.clear(); - this.editorSimpleWorker.dispose(); + this.disposable.dispose(); this.editorSimpleWorker = null; } } diff --git a/app/assets/javascripts/repo/lib/editor.js b/app/assets/javascripts/repo/lib/editor.js index de1c6d61b10..173eacbd65c 100644 --- a/app/assets/javascripts/repo/lib/editor.js +++ b/app/assets/javascripts/repo/lib/editor.js @@ -1,14 +1,24 @@ /* global monaco */ import DirtyDiffController from './diff/controller'; -import Model from './common/model'; +import Disposable from './common/disposable'; +import ModelManager from './common/model_manager'; + +export default class Editor { + static create() { + this.editorInstance = new Editor(); + + return this.editorInstance; + } -class Editor { constructor() { - this.models = new Map(); this.diffComputers = new Map(); this.currentModel = null; this.instance = null; this.dirtyDiffController = null; + this.modelManager = new ModelManager(); + this.disposable = new Disposable(); + + this.disposable.add(this.modelManager); } createInstance(domElement) { @@ -20,19 +30,14 @@ class Editor { scrollBeyondLastLine: false, }); - this.dirtyDiffController = new DirtyDiffController(); + this.dirtyDiffController = new DirtyDiffController(this.modelManager); + + this.disposable.add(this.dirtyDiffController, this.instance); } } createModel(file) { - if (this.models.has(file.path)) { - return this.models.get(file.path); - } - - const model = new Model(file); - this.models.set(file.path, model); - - return model; + return this.modelManager.addModel(file); } attachModel(model) { @@ -51,19 +56,11 @@ class Editor { } dispose() { + this.disposable.dispose(); + // dispose main monaco instance if (this.instance) { - this.instance.dispose(); this.instance = null; } - - // dispose of all the models - this.models.forEach(model => model.dispose()); - this.models.clear(); - - this.dirtyDiffController.dispose(); - this.dirtyDiffController = null; } } - -export default new Editor(); -- GitLab