Commit 9d285f1f authored by Tim Zallmann's avatar Tim Zallmann

Merge branch '44331-upgrade-monaco-editor-and-remove-copywebpackplugin' into 'master'

Resolve "Upgrade Monaco editor and remove CopyWebpackPlugin"

Closes #44331

See merge request gitlab-org/gitlab-ce!17813
parents 5652b3aa eeafa8ac
<script>
/* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
......@@ -50,7 +48,7 @@ export default {
// Compare key to allow for files opened in review mode to be cached differently
if (oldVal.key !== this.file.key) {
this.initMonaco();
this.initEditor();
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
......@@ -84,15 +82,10 @@ export default {
this.editor.dispose();
},
mounted() {
if (this.editor && monaco) {
this.initMonaco();
} else {
monacoLoader(['vs/editor/editor.main'], () => {
this.editor = Editor.create(monaco);
this.initMonaco();
});
if (!this.editor) {
this.editor = Editor.create();
}
this.initEditor();
},
methods: {
...mapActions([
......@@ -105,7 +98,7 @@ export default {
'updateViewer',
'removePendingTab',
]),
initMonaco() {
initEditor() {
if (this.shouldHideEditor) return;
this.editor.clearEditor();
......@@ -118,7 +111,7 @@ export default {
this.createEditorInstance();
})
.catch(err => {
flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
flash('Error setting up editor. Please try again.', 'alert', document, null, false, true);
throw err;
});
},
......
import { editor as monacoEditor, Uri } from 'monaco-editor';
import Disposable from './disposable';
import eventHub from '../../eventhub';
export default class Model {
constructor(monaco, file, head = null) {
this.monaco = monaco;
constructor(file, head = null) {
this.disposable = new Disposable();
this.file = file;
this.head = head;
this.content = file.content !== '' ? file.content : file.raw;
this.disposable.add(
(this.originalModel = this.monaco.editor.createModel(
(this.originalModel = monacoEditor.createModel(
head ? head.content : this.file.raw,
undefined,
new this.monaco.Uri(null, null, `original/${this.path}`),
new Uri(false, false, `original/${this.path}`),
)),
(this.model = this.monaco.editor.createModel(
(this.model = monacoEditor.createModel(
this.content,
undefined,
new this.monaco.Uri(null, null, this.path),
new Uri(false, false, this.path),
)),
);
if (this.file.mrChange) {
this.disposable.add(
(this.baseModel = this.monaco.editor.createModel(
(this.baseModel = monacoEditor.createModel(
this.file.baseRaw,
undefined,
new this.monaco.Uri(null, null, `target/${this.path}`),
new Uri(false, false, `target/${this.path}`),
)),
);
}
......
......@@ -3,8 +3,7 @@ import Disposable from './disposable';
import Model from './model';
export default class ModelManager {
constructor(monaco) {
this.monaco = monaco;
constructor() {
this.disposable = new Disposable();
this.models = new Map();
}
......@@ -22,7 +21,7 @@ export default class ModelManager {
return this.getModel(file.key);
}
const model = new Model(this.monaco, file, head);
const model = new Model(file, head);
this.models.set(model.path, model);
this.disposable.add(model);
......
/* global monaco */
import { Range } from 'monaco-editor';
import { throttle } from 'underscore';
import DirtyDiffWorker from './diff_worker';
import Disposable from '../common/disposable';
......@@ -16,7 +16,7 @@ export const getDiffChangeType = change => {
};
export const getDecorator = change => ({
range: new monaco.Range(change.lineNumber, 1, change.endLineNumber, 1),
range: new Range(change.lineNumber, 1, change.endLineNumber, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`,
......
import _ from 'underscore';
import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
import store from '../stores';
import DecorationsController from './decorations/controller';
import DirtyDiffController from './diff/controller';
......@@ -8,6 +9,11 @@ import editorOptions, { defaultEditorOptions } from './editor_options';
import gitlabTheme from './themes/gl_theme';
import keymap from './keymap.json';
function setupMonacoTheme() {
monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
monacoEditor.setTheme('gitlab');
}
export const clearDomElement = el => {
if (!el || !el.firstChild) return;
......@@ -17,24 +23,22 @@ export const clearDomElement = el => {
};
export default class Editor {
static create(monaco) {
if (this.editorInstance) return this.editorInstance;
this.editorInstance = new Editor(monaco);
static create() {
if (!this.editorInstance) {
this.editorInstance = new Editor();
}
return this.editorInstance;
}
constructor(monaco) {
this.monaco = monaco;
constructor() {
this.currentModel = null;
this.instance = null;
this.dirtyDiffController = null;
this.disposable = new Disposable();
this.modelManager = new ModelManager(this.monaco);
this.modelManager = new ModelManager();
this.decorationsController = new DecorationsController(this);
this.setupMonacoTheme();
setupMonacoTheme();
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
......@@ -46,7 +50,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
(this.instance = this.monaco.editor.create(domElement, {
(this.instance = monacoEditor.create(domElement, {
...defaultEditorOptions,
})),
(this.dirtyDiffController = new DirtyDiffController(
......@@ -66,7 +70,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
(this.instance = this.monaco.editor.createDiffEditor(domElement, {
(this.instance = monacoEditor.createDiffEditor(domElement, {
...defaultEditorOptions,
quickSuggestions: false,
occurrencesHighlight: false,
......@@ -122,17 +126,11 @@ export default class Editor {
modified: model.getModel(),
});
this.monaco.editor.createDiffNavigator(this.instance, {
monacoEditor.createDiffNavigator(this.instance, {
alwaysRevealFirst: true,
});
}
setupMonacoTheme() {
this.monaco.editor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
this.monaco.editor.setTheme('gitlab');
}
clearEditor() {
if (this.instance) {
this.instance.setModel(null);
......@@ -200,7 +198,7 @@ export default class Editor {
const getKeyCode = key => {
const monacoKeyMod = key.indexOf('KEY_') === 0;
return monacoKeyMod ? this.monaco.KeyCode[key] : this.monaco.KeyMod[key];
return monacoKeyMod ? KeyCode[key] : KeyMod[key];
};
keymap.forEach(command => {
......
import monacoContext from 'monaco-editor/dev/vs/loader';
monacoContext.require.config({
paths: {
vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
},
});
// ignore CDN config and use local assets path for service worker which cannot be cross-domain
const relativeRootPath = (gon && gon.relative_url_root) || '';
const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
// eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext;
export default monacoContext.require;
......@@ -534,3 +534,9 @@
:why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
:versions: []
:when: 2018-02-20 22:20:25.958123000 Z
- - :approve
- "@webassemblyjs/ieee754"
- :who: Mike Greiling
:why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
:versions: []
:when: 2018-06-08 05:30:56.764116000 Z
......@@ -4,8 +4,8 @@ const glob = require('glob');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ROOT_PATH = path.resolve(__dirname, '..');
......@@ -168,15 +168,7 @@ module.exports = {
name: '[name].[hash:8].[ext]',
},
},
{
test: /monaco-editor\/\w+\/vs\/loader\.js$/,
use: [
{ loader: 'exports-loader', options: 'l.global' },
{ loader: 'imports-loader', options: 'l=>{},this=>l,AMDLoader=>this,module=>undefined' },
],
},
],
noParse: [/monaco-editor\/\w+\/vs\//],
},
optimization: {
......@@ -226,6 +218,9 @@ module.exports = {
// enable vue-loader to use existing loader rules for other module types
new VueLoaderPlugin(),
// automatically configure monaco editor web workers
new MonacoWebpackPlugin(),
// prevent pikaday from including moment.js
new webpack.IgnorePlugin(/moment/, /pikaday/),
......@@ -235,29 +230,6 @@ module.exports = {
jQuery: 'jquery',
}),
// copy pre-compiled vendor libraries verbatim
new CopyWebpackPlugin([
{
from: path.join(
ROOT_PATH,
`node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`
),
to: 'monaco-editor/vs',
transform: function(content, path) {
if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
return (
'(function(){\n' +
'var define = this.define, require = this.require;\n' +
'window.define = define; window.require = require;\n' +
content +
'\n}.call(window.__monaco_context__ || (window.__monaco_context__ = {})));'
);
}
return content;
},
},
]),
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
......
......@@ -3,7 +3,6 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
import monacoLoader from '~/ide/monaco_loader';
import Editor from '~/ide/lib/editor';
import { activityBarViews } from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
......@@ -25,13 +24,10 @@ describe('RepoEditor', () => {
f.tempFile = true;
vm.$store.state.openFiles.push(f);
Vue.set(vm.$store.state.entries, f.path, f);
vm.monaco = true;
vm.$mount();
monacoLoader(['vs/editor/editor.main'], () => {
setTimeout(done, 0);
});
Vue.nextTick(() => setTimeout(done));
});
afterEach(() => {
......
/* global monaco */
import eventHub from '~/ide/eventhub';
import monacoLoader from '~/ide/monaco_loader';
import ModelManager from '~/ide/lib/common/model_manager';
import { file } from '../../helpers';
describe('Multi-file editor library model manager', () => {
let instance;
beforeEach(done => {
monacoLoader(['vs/editor/editor.main'], () => {
instance = new ModelManager(monaco);
done();
});
beforeEach(() => {
instance = new ModelManager();
});
afterEach(() => {
......
/* global monaco */
import eventHub from '~/ide/eventhub';
import monacoLoader from '~/ide/monaco_loader';
import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
describe('Multi-file editor library model', () => {
let model;
beforeEach(done => {
beforeEach(() => {
spyOn(eventHub, '$on').and.callThrough();
monacoLoader(['vs/editor/editor.main'], () => {
const f = file('path');
f.mrChange = { diff: 'ABC' };
f.baseRaw = 'test';
model = new Model(monaco, f);
done();
});
const f = file('path');
f.mrChange = { diff: 'ABC' };
f.baseRaw = 'test';
model = new Model(f);
});
afterEach(() => {
......@@ -38,7 +32,7 @@ describe('Multi-file editor library model', () => {
const f = file('path');
model.dispose();
model = new Model(monaco, f, {
model = new Model(f, {
...f,
content: '123 testing',
});
......
/* global monaco */
import monacoLoader from '~/ide/monaco_loader';
import editor from '~/ide/lib/editor';
import Editor from '~/ide/lib/editor';
import DecorationsController from '~/ide/lib/decorations/controller';
import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
......@@ -10,16 +8,12 @@ describe('Multi-file editor library decorations controller', () => {
let controller;
let model;
beforeEach(done => {
monacoLoader(['vs/editor/editor.main'], () => {
editorInstance = editor.create(monaco);
editorInstance.createInstance(document.createElement('div'));
beforeEach(() => {
editorInstance = Editor.create();
editorInstance.createInstance(document.createElement('div'));
controller = new DecorationsController(editorInstance);
model = new Model(monaco, file('path'));
done();
});
controller = new DecorationsController(editorInstance);
model = new Model(file('path'));
});
afterEach(() => {
......
/* global monaco */
import monacoLoader from '~/ide/monaco_loader';
import editor from '~/ide/lib/editor';
import { Range } from 'monaco-editor';
import Editor from '~/ide/lib/editor';
import ModelManager from '~/ide/lib/common/model_manager';
import DecorationsController from '~/ide/lib/decorations/controller';
import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller';
......@@ -14,20 +13,16 @@ describe('Multi-file editor library dirty diff controller', () => {
let decorationsController;
let model;
beforeEach(done => {
monacoLoader(['vs/editor/editor.main'], () => {
editorInstance = editor.create(monaco);
editorInstance.createInstance(document.createElement('div'));
beforeEach(() => {
editorInstance = Editor.create();
editorInstance.createInstance(document.createElement('div'));
modelManager = new ModelManager(monaco);
decorationsController = new DecorationsController(editorInstance);
modelManager = new ModelManager();
decorationsController = new DecorationsController(editorInstance);
model = modelManager.addModel(file('path'));
model = modelManager.addModel(file('path'));
controller = new DirtyDiffController(modelManager, decorationsController);
done();
});
controller = new DirtyDiffController(modelManager, decorationsController);
});
afterEach(() => {
......@@ -170,7 +165,7 @@ describe('Multi-file editor library dirty diff controller', () => {
[],
[
{
range: new monaco.Range(1, 1, 1, 1),
range: new Range(1, 1, 1, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: 'dirty-diff dirty-diff-modified',
......
/* global monaco */
import monacoLoader from '~/ide/monaco_loader';
import editor from '~/ide/lib/editor';
import { editor as monacoEditor } from 'monaco-editor';
import Editor from '~/ide/lib/editor';
import { file } from '../helpers';
describe('Multi-file editor library', () => {
......@@ -8,18 +7,14 @@ describe('Multi-file editor library', () => {
let el;
let holder;
beforeEach(done => {
beforeEach(() => {
el = document.createElement('div');
holder = document.createElement('div');
el.appendChild(holder);
document.body.appendChild(el);
monacoLoader(['vs/editor/editor.main'], () => {
instance = editor.create(monaco);
done();
});
instance = Editor.create();
});
afterEach(() => {
......@@ -29,20 +24,20 @@ describe('Multi-file editor library', () => {
});
it('creates instance of editor', () => {
expect(editor.editorInstance).not.toBeNull();
expect(Editor.editorInstance).not.toBeNull();
});
it('creates instance returns cached instance', () => {
expect(editor.create(monaco)).toEqual(instance);
expect(Editor.create()).toEqual(instance);
});
describe('createInstance', () => {
it('creates editor instance', () => {
spyOn(instance.monaco.editor, 'create').and.callThrough();
spyOn(monacoEditor, 'create').and.callThrough();
instance.createInstance(holder);
expect(instance.monaco.editor.create).toHaveBeenCalled();
expect(monacoEditor.create).toHaveBeenCalled();
});
it('creates dirty diff controller', () => {
......@@ -60,11 +55,11 @@ describe('Multi-file editor library', () => {
describe('createDiffInstance', () => {
it('creates editor instance', () => {
spyOn(instance.monaco.editor, 'createDiffEditor').and.callThrough();
spyOn(monacoEditor, 'createDiffEditor').and.callThrough();
instance.createDiffInstance(holder);
expect(instance.monaco.editor.createDiffEditor).toHaveBeenCalledWith(holder, {
expect(monacoEditor.createDiffEditor).toHaveBeenCalledWith(holder, {
model: null,
contextmenu: true,
minimap: {
......
import monacoContext from 'monaco-editor/dev/vs/loader';
import monacoLoader from '~/ide/monaco_loader';
describe('MonacoLoader', () => {
it('calls require.config and exports require', () => {
expect(monacoContext.require.getConfig()).toEqual(
jasmine.objectContaining({
paths: {
vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
},
}),
);
expect(monacoLoader).toBe(monacoContext.require);
});
});
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment