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/issues/show/utils.js')
-rw-r--r--app/assets/javascripts/issues/show/utils.js135
1 files changed, 135 insertions, 0 deletions
diff --git a/app/assets/javascripts/issues/show/utils.js b/app/assets/javascripts/issues/show/utils.js
index 05b06586362..7742a015836 100644
--- a/app/assets/javascripts/issues/show/utils.js
+++ b/app/assets/javascripts/issues/show/utils.js
@@ -1,4 +1,6 @@
+import { TITLE_LENGTH_MAX } from '~/issues/constants';
import { COLON, HYPHEN, NEWLINE } from '~/lib/utils/text_utility';
+import { __ } from '~/locale';
/**
* Returns the start and end `sourcepos` rows, converted to zero-based numbering.
@@ -93,3 +95,136 @@ export const convertDescriptionWithNewSort = (description, list) => {
return descriptionLines.join(NEWLINE);
};
+
+const bulletTaskListItemRegex = /^\s*[-*]\s+\[.]\s+/;
+const numericalTaskListItemRegex = /^\s*[0-9]\.\s+\[.]\s+/;
+const codeMarkdownRegex = /^\s*`.*`\s*$/;
+const imageOrLinkMarkdownRegex = /^\s*!?\[.*\)\s*$/;
+
+/**
+ * Checks whether the line of markdown contains a task list item,
+ * i.e. `- [ ]`, `* [ ]`, or `1. [ ]`.
+ *
+ * @param {String} line A line of markdown
+ * @returns {boolean} `true` if the line contains a task list item, otherwise `false`
+ */
+const containsTaskListItem = (line) =>
+ bulletTaskListItemRegex.test(line) || numericalTaskListItemRegex.test(line);
+
+/**
+ * Deletes a task list item from the description.
+ *
+ * Starting from the task list item, it deletes each line until it hits a nested
+ * task list item and reduces the indentation of each line from this line onwards.
+ *
+ * For example, for a given description like:
+ *
+ * <pre>
+ * 1. [ ] item 1
+ *
+ * paragraph text
+ *
+ * 1. [ ] item 2
+ *
+ * paragraph text
+ *
+ * 1. [ ] item 3
+ * </pre>
+ *
+ * Then when prompted to delete item 1, this function will return:
+ *
+ * <pre>
+ * 1. [ ] item 2
+ *
+ * paragraph text
+ *
+ * 1. [ ] item 3
+ * </pre>
+ *
+ * @param {String} description Description in markdown format
+ * @param {String} sourcepos Source position in format `23:3-23:14`
+ * @returns {{newDescription: String, taskDescription: String, taskTitle: String}} Object with:
+ *
+ * - `newDescription` property that contains markdown with the deleted task list item omitted
+ * - `taskDescription` property that contains the description of the deleted task list item
+ * - `taskTitle` property that contains the title of the deleted task list item
+ */
+export const deleteTaskListItem = (description, sourcepos) => {
+ const descriptionLines = description.split(NEWLINE);
+ const [startIndex, endIndex] = getSourceposRows(sourcepos);
+
+ const firstLine = descriptionLines[startIndex];
+ const firstLineIndentation = firstLine.length - firstLine.trimStart().length;
+
+ const taskTitle = firstLine
+ .replace(bulletTaskListItemRegex, '')
+ .replace(numericalTaskListItemRegex, '');
+ const taskDescription = [];
+
+ let indentation = 0;
+ let linesToDelete = 1;
+ let reduceIndentation = false;
+
+ for (let i = startIndex + 1; i <= endIndex; i += 1) {
+ if (reduceIndentation) {
+ descriptionLines[i] = descriptionLines[i].slice(indentation);
+ } else if (containsTaskListItem(descriptionLines[i])) {
+ reduceIndentation = true;
+ const currentLine = descriptionLines[i];
+ const currentLineIndentation = currentLine.length - currentLine.trimStart().length;
+ indentation = currentLineIndentation - firstLineIndentation;
+ descriptionLines[i] = descriptionLines[i].slice(indentation);
+ } else {
+ taskDescription.push(descriptionLines[i].trimStart());
+ linesToDelete += 1;
+ }
+ }
+
+ descriptionLines.splice(startIndex, linesToDelete);
+
+ return {
+ newDescription: descriptionLines.join(NEWLINE),
+ taskDescription: taskDescription.join(NEWLINE) || undefined,
+ taskTitle,
+ };
+};
+
+/**
+ * Given a title and description for a task:
+ *
+ * - Moves characters beyond the 255 character limit from the title to the description
+ * - Moves a pure markdown title to the description and gives the title the value `Untitled`
+ *
+ * @param {String} taskTitle The task title
+ * @param {String} taskDescription The task description
+ * @returns {{description: String, title: String}} An object with the formatted task title and description
+ */
+export const extractTaskTitleAndDescription = (taskTitle, taskDescription) => {
+ const isTitleOnlyMarkdown =
+ codeMarkdownRegex.test(taskTitle) || imageOrLinkMarkdownRegex.test(taskTitle);
+
+ if (isTitleOnlyMarkdown) {
+ return {
+ title: __('Untitled'),
+ description: taskDescription
+ ? taskTitle.concat(NEWLINE, NEWLINE, taskDescription)
+ : taskTitle,
+ };
+ }
+
+ const isTitleTooLong = taskTitle.length > TITLE_LENGTH_MAX;
+
+ if (isTitleTooLong) {
+ return {
+ title: taskTitle.slice(0, TITLE_LENGTH_MAX),
+ description: taskDescription
+ ? taskTitle.slice(TITLE_LENGTH_MAX).concat(NEWLINE, NEWLINE, taskDescription)
+ : taskTitle.slice(TITLE_LENGTH_MAX),
+ };
+ }
+
+ return {
+ title: taskTitle,
+ description: taskDescription,
+ };
+};