added disposable manager

added model manager

[ci skip]
This commit is contained in:
Phil Hughes 2017-11-21 14:22:33 +00:00
parent c90b520d79
commit eb8a609519
No known key found for this signature in database
GPG key ID: 32245528C52E0F9F
8 changed files with 152 additions and 80 deletions

View file

@ -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,

View file

@ -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();
}
}

View file

@ -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.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.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();
}
}

View file

@ -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();
}
}

View file

@ -33,7 +33,7 @@ class DecorationsController {
this.editorDecorations.set(
model.url,
editor.instance.deltaDecorations(oldDecorations, decorations),
editor.editorInstance.instance.deltaDecorations(oldDecorations, decorations),
);
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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();