summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/editor/extensions/editor_markdown_ext.js
blob: 2ce003753f79aba1d08584ec40201d3f1c089481 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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);
  }
}