2020-06-19 08:09:07 -04:00
|
|
|
import { editor as monacoEditor, languages as monacoLanguages, Position, Uri } from 'monaco-editor';
|
2020-03-06 06:28:26 -05:00
|
|
|
import { DEFAULT_THEME, themes } from '~/ide/lib/themes';
|
2020-05-07 11:09:29 -04:00
|
|
|
import languages from '~/ide/lib/languages';
|
2020-01-29 13:08:47 -05:00
|
|
|
import { defaultEditorOptions } from '~/ide/lib/editor_options';
|
2020-05-07 11:09:29 -04:00
|
|
|
import { registerLanguages } from '~/ide/utils';
|
2020-08-11 23:10:17 -04:00
|
|
|
import { joinPaths } from '~/lib/utils/url_utility';
|
2020-01-29 13:08:47 -05:00
|
|
|
import { clearDomElement } from './utils';
|
2020-08-26 05:10:16 -04:00
|
|
|
import { EDITOR_LITE_INSTANCE_ERROR_NO_EL, URI_PREFIX } from './constants';
|
2020-01-29 13:08:47 -05:00
|
|
|
|
|
|
|
export default class Editor {
|
|
|
|
constructor(options = {}) {
|
|
|
|
this.editorEl = null;
|
|
|
|
this.blobContent = '';
|
|
|
|
this.blobPath = '';
|
2020-08-26 05:10:16 -04:00
|
|
|
this.instances = [];
|
2020-01-29 13:08:47 -05:00
|
|
|
this.model = null;
|
|
|
|
this.options = {
|
2020-03-10 17:09:21 -04:00
|
|
|
extraEditorClassName: 'gl-editor-lite',
|
2020-01-29 13:08:47 -05:00
|
|
|
...defaultEditorOptions,
|
|
|
|
...options,
|
|
|
|
};
|
|
|
|
|
|
|
|
Editor.setupMonacoTheme();
|
2020-05-07 11:09:29 -04:00
|
|
|
|
|
|
|
registerLanguages(...languages);
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static setupMonacoTheme() {
|
2020-03-06 06:28:26 -05:00
|
|
|
const themeName = window.gon?.user_color_scheme || DEFAULT_THEME;
|
|
|
|
const theme = themes.find(t => t.name === themeName);
|
|
|
|
if (theme) monacoEditor.defineTheme(themeName, theme.data);
|
|
|
|
monacoEditor.setTheme(theme ? themeName : DEFAULT_THEME);
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|
|
|
|
|
2020-08-11 23:10:17 -04:00
|
|
|
/**
|
|
|
|
* Creates a monaco instance with the given options.
|
|
|
|
*
|
|
|
|
* @param {Object} options Options used to initialize monaco.
|
|
|
|
* @param {Element} options.el The element which will be used to create the monacoEditor.
|
|
|
|
* @param {string} options.blobPath The path used as the URI of the model. Monaco uses the extension of this path to determine the language.
|
|
|
|
* @param {string} options.blobContent The content to initialize the monacoEditor.
|
|
|
|
* @param {string} options.blobGlobalId This is used to help globally identify monaco instances that are created with the same blobPath.
|
|
|
|
*/
|
2020-08-26 05:10:16 -04:00
|
|
|
createInstance({
|
|
|
|
el = undefined,
|
|
|
|
blobPath = '',
|
|
|
|
blobContent = '',
|
|
|
|
blobGlobalId = '',
|
|
|
|
...instanceOptions
|
|
|
|
} = {}) {
|
|
|
|
if (!el) {
|
|
|
|
throw new Error(EDITOR_LITE_INSTANCE_ERROR_NO_EL);
|
|
|
|
}
|
2020-01-29 13:08:47 -05:00
|
|
|
this.editorEl = el;
|
|
|
|
this.blobContent = blobContent;
|
|
|
|
this.blobPath = blobPath;
|
|
|
|
|
|
|
|
clearDomElement(this.editorEl);
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
const uriFilePath = joinPaths(URI_PREFIX, blobGlobalId, blobPath);
|
2020-08-11 23:10:17 -04:00
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
const model = monacoEditor.createModel(this.blobContent, undefined, Uri.file(uriFilePath));
|
2020-01-29 13:08:47 -05:00
|
|
|
|
|
|
|
monacoEditor.onDidCreateEditor(this.renderEditor.bind(this));
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
const instance = monacoEditor.create(this.editorEl, {
|
|
|
|
...this.options,
|
|
|
|
...instanceOptions,
|
|
|
|
});
|
|
|
|
instance.setModel(model);
|
|
|
|
instance.onDidDispose(() => {
|
|
|
|
const index = this.instances.findIndex(inst => inst === instance);
|
|
|
|
this.instances.splice(index, 1);
|
|
|
|
model.dispose();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Reference to the model on the editor level will go away in
|
|
|
|
// https://gitlab.com/gitlab-org/gitlab/-/issues/241023
|
|
|
|
// After that, the references to the model will be routed through
|
|
|
|
// instance exclusively
|
|
|
|
this.model = model;
|
|
|
|
|
|
|
|
this.instances.push(instance);
|
|
|
|
return instance;
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
dispose() {
|
2020-08-26 05:10:16 -04:00
|
|
|
this.instances.forEach(instance => instance.dispose());
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
renderEditor() {
|
|
|
|
delete this.editorEl.dataset.editorLoading;
|
|
|
|
}
|
|
|
|
|
2020-08-11 23:10:17 -04:00
|
|
|
onChangeContent(fn) {
|
|
|
|
return this.model.onDidChangeContent(fn);
|
|
|
|
}
|
|
|
|
|
2020-01-29 13:08:47 -05:00
|
|
|
updateModelLanguage(path) {
|
|
|
|
if (path === this.blobPath) return;
|
|
|
|
this.blobPath = path;
|
|
|
|
const ext = `.${path.split('.').pop()}`;
|
|
|
|
const language = monacoLanguages
|
|
|
|
.getLanguages()
|
|
|
|
.find(lang => lang.extensions.indexOf(ext) !== -1);
|
|
|
|
const id = language ? language.id : 'plaintext';
|
|
|
|
monacoEditor.setModelLanguage(this.model, id);
|
|
|
|
}
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
/**
|
|
|
|
* @deprecated do not use .getValue() directly on the editor.
|
|
|
|
* This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025
|
|
|
|
* Rather use it on the exact instance
|
|
|
|
*/
|
2020-01-29 13:08:47 -05:00
|
|
|
getValue() {
|
2020-08-26 05:10:16 -04:00
|
|
|
return this.instances[0].getValue();
|
2020-06-19 08:09:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
/**
|
|
|
|
* @deprecated do not use .setValue() directly on the editor.
|
|
|
|
* This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025
|
|
|
|
* Rather use it on the exact instance
|
|
|
|
*/
|
2020-06-19 08:09:07 -04:00
|
|
|
setValue(val) {
|
2020-08-26 05:10:16 -04:00
|
|
|
this.instances[0].setValue(val);
|
2020-06-19 08:09:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
/**
|
|
|
|
* @deprecated do not use .focus() directly on the editor.
|
|
|
|
* This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025
|
|
|
|
* Rather use it on the exact instance
|
|
|
|
*/
|
2020-06-19 08:09:07 -04:00
|
|
|
focus() {
|
2020-08-26 05:10:16 -04:00
|
|
|
this.instances[0].focus();
|
2020-06-19 08:09:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
/**
|
|
|
|
* @deprecated do not use .updateOptions() directly on the editor.
|
|
|
|
* This proxy-method will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/241025
|
|
|
|
* Rather use it on the exact instance
|
|
|
|
*/
|
|
|
|
updateOptions(options = {}) {
|
|
|
|
this.instances[0].updateOptions(options);
|
2020-06-19 08:09:07 -04:00
|
|
|
}
|
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
navigateFileStart() {
|
|
|
|
this.instances[0].setPosition(new Position(1, 1));
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|
2020-06-22 08:08:47 -04:00
|
|
|
|
2020-08-26 05:10:16 -04:00
|
|
|
use(exts = [], instance = null) {
|
2020-06-22 08:08:47 -04:00
|
|
|
const extensions = Array.isArray(exts) ? exts : [exts];
|
2020-08-26 05:10:16 -04:00
|
|
|
if (instance) {
|
|
|
|
Object.assign(instance, ...extensions);
|
|
|
|
} else {
|
|
|
|
this.instances.forEach(inst => Object.assign(inst, ...extensions));
|
|
|
|
}
|
2020-06-22 08:08:47 -04:00
|
|
|
}
|
2020-01-29 13:08:47 -05:00
|
|
|
}
|