diff --git a/app/assets/javascripts/repo/lib/diff/controller.js b/app/assets/javascripts/repo/lib/diff/controller.js index 39c6cb92e0f..6c97f1cc24d 100644 --- a/app/assets/javascripts/repo/lib/diff/controller.js +++ b/app/assets/javascripts/repo/lib/diff/controller.js @@ -1,24 +1,25 @@ /* global monaco */ -import Disposable from '../common/disposable'; import DirtyDiffWorker from './worker'; +import Disposable from '../common/disposable'; import decorationsController from '../decorations/controller'; export const getDiffChangeType = (change) => { - if (change.originalEndLineNumber === 0) { + if (change.modified) { + return 'modified'; + } else if (change.added) { return 'added'; - } else if (change.modifiedEndLineNumber === 0) { + } else if (change.removed) { return 'removed'; } - return 'modified'; + return ''; }; export const getDecorator = change => ({ range: new monaco.Range( - change.modifiedStartLineNumber, + change.lineNumber, 1, - !change.modifiedEndLineNumber ? - change.modifiedStartLineNumber : change.modifiedEndLineNumber, + change.endLineNumber, 1, ), options: { @@ -37,22 +38,15 @@ export default class DirtyDiffController { this.disposable = new Disposable(); this.editorSimpleWorker = null; this.modelManager = modelManager; - this.disposable.add(this.worker = new DirtyDiffWorker()); + this.dirtyDiffWorker = new DirtyDiffWorker(); } attachModel(model) { - if (model.attachedToWorker) return; - - [model.getModel(), model.getOriginalModel()].forEach(() => { - this.worker.attachModel(model); - }); - - model.onChange((_, e) => this.computeDiff(model, e)); + model.onChange(() => this.computeDiff(model)); } - computeDiff(model, e) { - this.worker.modelChanged(model, e); - this.worker.compute(model, changes => decorate(model, changes)); + computeDiff(model) { + decorate(model, this.dirtyDiffWorker.compute(model)); } // eslint-disable-next-line class-methods-use-this diff --git a/app/assets/javascripts/repo/lib/diff/worker.js b/app/assets/javascripts/repo/lib/diff/worker.js index 39047d85507..0007c7060dc 100644 --- a/app/assets/javascripts/repo/lib/diff/worker.js +++ b/app/assets/javascripts/repo/lib/diff/worker.js @@ -1,77 +1,34 @@ -/* global monaco */ -import Disposable from '../common/disposable'; +import { diffLines } from 'diff'; export default class DirtyDiffWorker { - constructor() { - this.editorSimpleWorker = null; - this.disposable = new Disposable(); - this.actions = new Set(); + // eslint-disable-next-line class-methods-use-this + compute(model) { + const originalContent = model.getOriginalModel().getValue(); + const newContent = model.getModel().getValue(); + const changes = diffLines(originalContent, newContent); - // eslint-disable-next-line promise/catch-or-return - monaco.editor.createWebWorker({ - moduleId: 'vs/editor/common/services/editorSimpleWorker', - }).getProxy().then((editorSimpleWorker) => { - this.disposable.add(this.editorSimpleWorker = editorSimpleWorker); - this.ready(); - }); - } + let lineNumber = 1; + return changes.reduce((acc, change) => { + const findOnLine = acc.find(c => c.lineNumber === lineNumber); - // loop through all the previous cached actions - // this way we don't block the user from editing the file - ready() { - this.actions.forEach((action) => { - const methodName = Object.keys(action)[0]; - this[methodName](...action[methodName]); - }); + if (findOnLine) { + Object.assign(findOnLine, change, { + modified: true, + endLineNumber: change.count > 1 ? lineNumber + change.count : lineNumber, + }); + } else if ('added' in change || 'removed' in change) { + acc.push(Object.assign({}, change, { + lineNumber, + modified: undefined, + endLineNumber: change.count > 1 ? lineNumber + change.count : lineNumber, + })); + } - this.actions.clear(); - } + if (!change.removed) { + lineNumber += change.count; + } - attachModel(model) { - if (this.editorSimpleWorker && !model.attachedToWorker) { - this.editorSimpleWorker.acceptNewModel(model.diffModel); - this.editorSimpleWorker.acceptNewModel(model.originalDiffModel); - - model.setAttachedToWorker(true); - } else if (!this.editorSimpleWorker) { - this.actions.add({ - attachModel: [model], - }); - } - } - - modelChanged(model, e) { - if (this.editorSimpleWorker) { - this.editorSimpleWorker.acceptModelChanged( - model.url, - e, - ); - } else { - this.actions.add({ - modelChanged: [model, e], - }); - } - } - - compute(model, cb) { - if (this.editorSimpleWorker) { - return this.editorSimpleWorker.computeDiff( - model.originalUrl, - model.url, - ).then(cb); - } - - this.actions.add({ - compute: [model, cb], - }); - - return null; - } - - dispose() { - this.actions.clear(); - - this.disposable.dispose(); - this.editorSimpleWorker = null; + return acc; + }, []); } } diff --git a/app/assets/javascripts/repo/lib/editor.js b/app/assets/javascripts/repo/lib/editor.js index 173eacbd65c..ea3bfe16462 100644 --- a/app/assets/javascripts/repo/lib/editor.js +++ b/app/assets/javascripts/repo/lib/editor.js @@ -11,28 +11,27 @@ export default class Editor { } constructor() { - 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); + this.disposable.add( + this.modelManager = new ModelManager(), + ); } createInstance(domElement) { if (!this.instance) { - this.instance = monaco.editor.create(domElement, { - model: null, - readOnly: false, - contextmenu: true, - scrollBeyondLastLine: false, - }); - - this.dirtyDiffController = new DirtyDiffController(this.modelManager); - - this.disposable.add(this.dirtyDiffController, this.instance); + this.disposable.add( + this.instance = monaco.editor.create(domElement, { + model: null, + readOnly: false, + contextmenu: true, + scrollBeyondLastLine: false, + }), + this.dirtyDiffController = new DirtyDiffController(this.modelManager), + ); } } diff --git a/package.json b/package.json index 8c1b2c401ed..5b4676bf4ae 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "css-loader": "^0.28.0", "d3": "^3.5.11", "deckar01-task_list": "^2.0.0", + "diff": "^3.4.0", "document-register-element": "1.3.0", "dropzone": "^4.2.0", "emoji-unicode-version": "^0.2.1", diff --git a/yarn.lock b/yarn.lock index 73cc4f11500..2050f9eeed7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1895,6 +1895,10 @@ di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" +diff@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" + diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"