debian-mirror-gitlab/app/assets/javascripts/ide/lib/editor.js

224 lines
5.5 KiB
JavaScript
Raw Normal View History

2018-05-09 12:01:36 +05:30
import _ from 'underscore';
2018-11-08 19:23:39 +05:30
import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
2018-10-15 14:42:47 +05:30
import store from '../stores';
2018-05-09 12:01:36 +05:30
import DecorationsController from './decorations/controller';
import DirtyDiffController from './diff/controller';
import Disposable from './common/disposable';
import ModelManager from './common/model_manager';
import editorOptions, { defaultEditorOptions } from './editor_options';
import gitlabTheme from './themes/gl_theme';
2018-10-15 14:42:47 +05:30
import keymap from './keymap.json';
2018-05-09 12:01:36 +05:30
2018-11-08 19:23:39 +05:30
function setupMonacoTheme() {
monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
monacoEditor.setTheme('gitlab');
}
2018-05-09 12:01:36 +05:30
export const clearDomElement = el => {
if (!el || !el.firstChild) return;
while (el.firstChild) {
el.removeChild(el.firstChild);
}
};
export default class Editor {
2018-11-08 19:23:39 +05:30
static create() {
if (!this.editorInstance) {
this.editorInstance = new Editor();
}
2018-05-09 12:01:36 +05:30
return this.editorInstance;
}
2018-11-08 19:23:39 +05:30
constructor() {
2018-05-09 12:01:36 +05:30
this.currentModel = null;
this.instance = null;
this.dirtyDiffController = null;
this.disposable = new Disposable();
2018-11-08 19:23:39 +05:30
this.modelManager = new ModelManager();
2018-05-09 12:01:36 +05:30
this.decorationsController = new DecorationsController(this);
2018-11-08 19:23:39 +05:30
setupMonacoTheme();
2018-05-09 12:01:36 +05:30
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
}, 200);
}
createInstance(domElement) {
if (!this.instance) {
clearDomElement(domElement);
this.disposable.add(
2018-11-08 19:23:39 +05:30
(this.instance = monacoEditor.create(domElement, {
2018-05-09 12:01:36 +05:30
...defaultEditorOptions,
})),
(this.dirtyDiffController = new DirtyDiffController(
this.modelManager,
this.decorationsController,
)),
);
2018-10-15 14:42:47 +05:30
this.addCommands();
2018-05-09 12:01:36 +05:30
window.addEventListener('resize', this.debouncedUpdate, false);
}
}
2018-10-15 14:42:47 +05:30
createDiffInstance(domElement, readOnly = true) {
2018-05-09 12:01:36 +05:30
if (!this.instance) {
clearDomElement(domElement);
this.disposable.add(
2018-11-08 19:23:39 +05:30
(this.instance = monacoEditor.createDiffEditor(domElement, {
2018-05-09 12:01:36 +05:30
...defaultEditorOptions,
quickSuggestions: false,
occurrencesHighlight: false,
renderSideBySide: Editor.renderSideBySide(domElement),
2018-10-15 14:42:47 +05:30
readOnly,
renderLineHighlight: readOnly ? 'all' : 'none',
hideCursorInOverviewRuler: !readOnly,
2018-05-09 12:01:36 +05:30
})),
);
2018-10-15 14:42:47 +05:30
this.addCommands();
2018-05-09 12:01:36 +05:30
window.addEventListener('resize', this.debouncedUpdate, false);
}
}
2018-10-15 14:42:47 +05:30
createModel(file, head = null) {
return this.modelManager.addModel(file, head);
2018-05-09 12:01:36 +05:30
}
attachModel(model) {
if (this.isDiffEditorType) {
this.instance.setModel({
original: model.getOriginalModel(),
modified: model.getModel(),
});
return;
}
this.instance.setModel(model.getModel());
if (this.dirtyDiffController) this.dirtyDiffController.attachModel(model);
this.currentModel = model;
this.instance.updateOptions(
editorOptions.reduce((acc, obj) => {
Object.keys(obj).forEach(key => {
Object.assign(acc, {
[key]: obj[key](model),
});
});
return acc;
}, {}),
);
if (this.dirtyDiffController) this.dirtyDiffController.reDecorate(model);
}
attachMergeRequestModel(model) {
this.instance.setModel({
original: model.getBaseModel(),
modified: model.getModel(),
});
2018-11-08 19:23:39 +05:30
monacoEditor.createDiffNavigator(this.instance, {
2018-05-09 12:01:36 +05:30
alwaysRevealFirst: true,
});
}
clearEditor() {
if (this.instance) {
this.instance.setModel(null);
}
}
dispose() {
window.removeEventListener('resize', this.debouncedUpdate);
// catch any potential errors with disposing the error
// this is mainly for tests caused by elements not existing
try {
this.disposable.dispose();
this.instance = null;
} catch (e) {
this.instance = null;
if (process.env.NODE_ENV !== 'test') {
// eslint-disable-next-line no-console
console.error(e);
}
}
}
updateDimensions() {
this.instance.layout();
this.updateDiffView();
}
setPosition({ lineNumber, column }) {
this.instance.revealPositionInCenter({
lineNumber,
column,
});
this.instance.setPosition({
lineNumber,
column,
});
}
onPositionChange(cb) {
if (!this.instance.onDidChangeCursorPosition) return;
this.disposable.add(this.instance.onDidChangeCursorPosition(e => cb(this.instance, e)));
}
updateDiffView() {
if (!this.isDiffEditorType) return;
this.instance.updateOptions({
renderSideBySide: Editor.renderSideBySide(this.instance.getDomNode()),
});
}
get isDiffEditorType() {
return this.instance.getEditorType() === 'vs.editor.IDiffEditor';
}
static renderSideBySide(domElement) {
return domElement.offsetWidth >= 700;
}
2018-10-15 14:42:47 +05:30
addCommands() {
const getKeyCode = key => {
const monacoKeyMod = key.indexOf('KEY_') === 0;
2018-11-08 19:23:39 +05:30
return monacoKeyMod ? KeyCode[key] : KeyMod[key];
2018-10-15 14:42:47 +05:30
};
keymap.forEach(command => {
const keybindings = command.bindings.map(binding => {
const keys = binding.split('+');
// eslint-disable-next-line no-bitwise
return keys.length > 1 ? getKeyCode(keys[0]) | getKeyCode(keys[1]) : getKeyCode(keys[0]);
});
this.instance.addAction({
id: command.id,
label: command.label,
keybindings,
run() {
store.dispatch(command.action.name, command.action.params);
return null;
},
});
});
}
2018-05-09 12:01:36 +05:30
}