Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/editor/extensions')
-rw-r--r--app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js38
-rw-r--r--app/assets/javascripts/editor/extensions/editor_file_template_ext.js8
-rw-r--r--app/assets/javascripts/editor/extensions/editor_lite_extension_base.js11
-rw-r--r--app/assets/javascripts/editor/extensions/editor_markdown_ext.js97
4 files changed, 154 insertions, 0 deletions
diff --git a/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
new file mode 100644
index 00000000000..eb47c20912e
--- /dev/null
+++ b/app/assets/javascripts/editor/extensions/editor_ci_schema_ext.js
@@ -0,0 +1,38 @@
+import Api from '~/api';
+import { registerSchema } from '~/ide/utils';
+import { EditorLiteExtension } from './editor_lite_extension_base';
+import { EXTENSION_CI_SCHEMA_FILE_NAME_MATCH } from '../constants';
+
+export class CiSchemaExtension extends EditorLiteExtension {
+ /**
+ * Registers a syntax schema to the editor based on project
+ * identifier and commit.
+ *
+ * The schema is added to the file that is currently edited
+ * in the editor.
+ *
+ * @param {Object} opts
+ * @param {String} opts.projectNamespace
+ * @param {String} opts.projectPath
+ * @param {String?} opts.ref - Current ref. Defaults to master
+ */
+ registerCiSchema({ projectNamespace, projectPath, ref = 'master' } = {}) {
+ const ciSchemaPath = Api.buildUrl(Api.projectFileSchemaPath)
+ .replace(':namespace_path', projectNamespace)
+ .replace(':project_path', projectPath)
+ .replace(':ref', ref)
+ .replace(':filename', EXTENSION_CI_SCHEMA_FILE_NAME_MATCH);
+ // In order for workers loaded from `data://` as the
+ // ones loaded by monaco editor, we use absolute URLs
+ // to fetch schema files, hence the `gon.gitlab_url`
+ // reference. This prevents error:
+ // "Failed to execute 'fetch' on 'WorkerGlobalScope'"
+ const absoluteSchemaUrl = gon.gitlab_url + ciSchemaPath;
+ const modelFileName = this.getModel().uri.path.split('/').pop();
+
+ registerSchema({
+ uri: absoluteSchemaUrl,
+ fileMatch: [modelFileName],
+ });
+ }
+}
diff --git a/app/assets/javascripts/editor/extensions/editor_file_template_ext.js b/app/assets/javascripts/editor/extensions/editor_file_template_ext.js
new file mode 100644
index 00000000000..f5474318447
--- /dev/null
+++ b/app/assets/javascripts/editor/extensions/editor_file_template_ext.js
@@ -0,0 +1,8 @@
+import { Position } from 'monaco-editor';
+import { EditorLiteExtension } from './editor_lite_extension_base';
+
+export class FileTemplateExtension extends EditorLiteExtension {
+ navigateFileStart() {
+ this.setPosition(new Position(1, 1));
+ }
+}
diff --git a/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js b/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js
new file mode 100644
index 00000000000..8d350068973
--- /dev/null
+++ b/app/assets/javascripts/editor/extensions/editor_lite_extension_base.js
@@ -0,0 +1,11 @@
+import { ERROR_INSTANCE_REQUIRED_FOR_EXTENSION } from '../constants';
+
+export class EditorLiteExtension {
+ constructor({ instance, ...options } = {}) {
+ if (instance) {
+ Object.assign(instance, options);
+ } else if (Object.entries(options).length) {
+ throw new Error(ERROR_INSTANCE_REQUIRED_FOR_EXTENSION);
+ }
+ }
+}
diff --git a/app/assets/javascripts/editor/extensions/editor_markdown_ext.js b/app/assets/javascripts/editor/extensions/editor_markdown_ext.js
new file mode 100644
index 00000000000..2ce003753f7
--- /dev/null
+++ b/app/assets/javascripts/editor/extensions/editor_markdown_ext.js
@@ -0,0 +1,97 @@
+import { EditorLiteExtension } from './editor_lite_extension_base';
+
+export class EditorMarkdownExtension extends EditorLiteExtension {
+ getSelectedText(selection = this.getSelection()) {
+ const { startLineNumber, endLineNumber, startColumn, endColumn } = selection;
+ const valArray = this.getValue().split('\n');
+ let text = '';
+ if (startLineNumber === endLineNumber) {
+ text = valArray[startLineNumber - 1].slice(startColumn - 1, endColumn - 1);
+ } else {
+ const startLineText = valArray[startLineNumber - 1].slice(startColumn - 1);
+ const endLineText = valArray[endLineNumber - 1].slice(0, endColumn - 1);
+
+ for (let i = startLineNumber, k = endLineNumber - 1; i < k; i += 1) {
+ text += `${valArray[i]}`;
+ if (i !== k - 1) text += `\n`;
+ }
+ text = text
+ ? [startLineText, text, endLineText].join('\n')
+ : [startLineText, endLineText].join('\n');
+ }
+ return text;
+ }
+
+ replaceSelectedText(text, select = undefined) {
+ const forceMoveMarkers = !select;
+ this.executeEdits('', [{ range: this.getSelection(), text, forceMoveMarkers }]);
+ }
+
+ moveCursor(dx = 0, dy = 0) {
+ const pos = this.getPosition();
+ pos.column += dx;
+ pos.lineNumber += dy;
+ this.setPosition(pos);
+ }
+
+ /**
+ * Adjust existing selection to select text within the original selection.
+ * - If `selectedText` is not supplied, we fetch selected text with
+ *
+ * ALGORITHM:
+ *
+ * MULTI-LINE SELECTION
+ * 1. Find line that contains `toSelect` text.
+ * 2. Using the index of this line and the position of `toSelect` text in it,
+ * construct:
+ * * newStartLineNumber
+ * * newStartColumn
+ *
+ * SINGLE-LINE SELECTION
+ * 1. Use `startLineNumber` from the current selection as `newStartLineNumber`
+ * 2. Find the position of `toSelect` text in it to get `newStartColumn`
+ *
+ * 3. `newEndLineNumber` — Since this method is supposed to be used with
+ * markdown decorators that are pretty short, the `newEndLineNumber` is
+ * suggested to be assumed the same as the startLine.
+ * 4. `newEndColumn` — pretty obvious
+ * 5. Adjust the start and end positions of the current selection
+ * 6. Re-set selection on the instance
+ *
+ * @param {string} toSelect - New text to select within current selection.
+ * @param {string} selectedText - Currently selected text. It's just a
+ * shortcut: If it's not supplied, we fetch selected text from the instance
+ */
+ selectWithinSelection(toSelect, selectedText) {
+ const currentSelection = this.getSelection();
+ if (currentSelection.isEmpty() || !toSelect) {
+ return;
+ }
+ const text = selectedText || this.getSelectedText(currentSelection);
+ let lineShift;
+ let newStartLineNumber;
+ let newStartColumn;
+
+ const textLines = text.split('\n');
+
+ if (textLines.length > 1) {
+ // Multi-line selection
+ lineShift = textLines.findIndex((line) => line.indexOf(toSelect) !== -1);
+ newStartLineNumber = currentSelection.startLineNumber + lineShift;
+ newStartColumn = textLines[lineShift].indexOf(toSelect) + 1;
+ } else {
+ // Single-line selection
+ newStartLineNumber = currentSelection.startLineNumber;
+ newStartColumn = currentSelection.startColumn + text.indexOf(toSelect);
+ }
+
+ const newEndLineNumber = newStartLineNumber;
+ const newEndColumn = newStartColumn + toSelect.length;
+
+ const newSelection = currentSelection
+ .setStartPosition(newStartLineNumber, newStartColumn)
+ .setEndPosition(newEndLineNumber, newEndColumn);
+
+ this.setSelection(newSelection);
+ }
+}