| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- import * as codemirror from 'codemirror';
- /**
- * Utility for markdown list
- */
- class MarkdownListUtil {
- constructor() {
- // https://github.com/codemirror/CodeMirror/blob/c7853a989c77bb9f520c9c530cbe1497856e96fc/addon/edit/continuelist.js#L14
- // https://regex101.com/r/7BN2fR/5
- this.indentAndMarkRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/;
- this.indentAndMarkOnlyRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/;
- this.pasteText = this.pasteText.bind(this);
- this.getBol = this.getBol.bind(this);
- this.getEol = this.getEol.bind(this);
- this.getStrFromBol = this.getStrFromBol.bind(this);
- this.getStrToEol = this.getStrToEol.bind(this);
- this.newlineWithoutIndent = this.newlineWithoutIndent.bind(this);
- }
- /**
- * paste text
- * @param {any} editor An editor instance of CodeMirror
- * @param {any} event
- * @param {string} text
- */
- pasteText(editor, event, text) {
- // get strings from BOL(beginning of line) to current position
- const strFromBol = this.getStrFromBol(editor);
- const matched = strFromBol.match(this.indentAndMarkRE);
- // when match indentAndMarkOnlyRE
- // (this means the current position is the beginning of the list item)
- if (this.indentAndMarkOnlyRE.test(strFromBol)) {
- const adjusted = this.adjustPastedData(strFromBol, text);
- // replace
- if (adjusted != null) {
- event.preventDefault();
- editor.getDoc().replaceRange(adjusted, this.getBol(editor), editor.getCursor());
- }
- }
- }
- /**
- * return adjusted pasted data by indentAndMark
- *
- * @param {string} indentAndMark
- * @param {string} text
- * @returns adjusted pasted data
- * returns null when adjustment is not necessary
- */
- adjustPastedData(indentAndMark, text) {
- let adjusted = null;
- // list data (starts with indent and mark)
- if (text.match(this.indentAndMarkRE)) {
- const indent = indentAndMark.match(this.indentAndMarkRE)[1];
- // splice to an array of line
- const lines = text.match(/[^\r\n]+/g);
- // indent
- const replacedLines = lines.map((line) => {
- return indent + line;
- });
- adjusted = replacedLines.join('\n');
- }
- // listful data
- else if (this.isListfulData(text)) {
- // do nothing (return null)
- }
- // not listful data
- else {
- // append `indentAndMark` at the beginning of all lines (except the first line)
- const replacedText = text.replace(/(\r\n|\r|\n)/g, '$1' + indentAndMark);
- // append `indentAndMark` to the first line
- adjusted = indentAndMark + replacedText;
- }
- return adjusted;
- }
- /**
- * evaluate whether `text` is list like data or not
- * @param {string} text
- */
- isListfulData(text) {
- // return false if includes at least one blank line
- // see https://stackoverflow.com/a/16369725
- if (text.match(/^\s*[\r\n]/m) != null) {
- return false;
- }
- const lines = text.match(/[^\r\n]+/g);
- // count lines that starts with indent and mark
- let isListful = false;
- let count = 0;
- lines.forEach((line) => {
- if (line.match(this.indentAndMarkRE)) {
- count++;
- }
- // ensure to be true if it is 50% or more
- if (count >= lines.length / 2) {
- isListful = true;
- return;
- }
- });
- return isListful;
- }
- /**
- * return the postion of the BOL(beginning of line)
- */
- getBol(editor) {
- const curPos = editor.getCursor();
- return { line: curPos.line, ch: 0 };
- }
- /**
- * return the postion of the EOL(end of line)
- */
- getEol(editor) {
- const curPos = editor.getCursor();
- const lineLength = editor.getDoc().getLine(curPos.line).length;
- return { line: curPos.line, ch: lineLength };
- }
- /**
- * return strings from BOL(beginning of line) to current position
- */
- getStrFromBol(editor) {
- const curPos = editor.getCursor();
- return editor.getDoc().getRange(this.getBol(editor), curPos);
- }
- /**
- * return strings from current position to EOL(end of line)
- */
- getStrToEol(editor) {
- const curPos = editor.getCursor();
- return editor.getDoc().getRange(curPos, this.getEol(editor));
- }
- /**
- * insert newline without indent
- */
- newlineWithoutIndent(editor, strToEol) {
- codemirror.commands.newlineAndIndent(editor);
- // replace the line with strToEol (abort auto indent)
- editor.getDoc().replaceRange(strToEol, this.getBol(editor), this.getEol(editor));
- }
- }
- // singleton pattern
- const instance = new MarkdownListUtil();
- Object.freeze(instance);
- export default instance;
|