markdown-actions 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/markdown-actions.esm.js +146 -39
- package/dist/markdown-actions.esm.js.map +4 -4
- package/dist/markdown-actions.js +146 -39
- package/dist/markdown-actions.js.map +4 -4
- package/dist/markdown-actions.min.js +22 -22
- package/package.json +12 -3
- package/src/core/detection.js +15 -19
- package/src/core/insertion.js +2 -0
- package/src/core/markers.js +34 -0
- package/src/index.js +20 -7
- package/src/operations/inline.js +101 -0
- package/src/operations/list.js +7 -7
package/dist/markdown-actions.js
CHANGED
|
@@ -36,6 +36,7 @@ var markdownActions = (() => {
|
|
|
36
36
|
__export(src_exports, {
|
|
37
37
|
applyCustomFormat: () => applyCustomFormat,
|
|
38
38
|
default: () => src_default,
|
|
39
|
+
detectMarkers: () => detectMarkers,
|
|
39
40
|
expandSelection: () => expandSelection2,
|
|
40
41
|
getActiveFormats: () => getActiveFormats2,
|
|
41
42
|
getDebugMode: () => getDebugMode,
|
|
@@ -194,8 +195,12 @@ var markdownActions = (() => {
|
|
|
194
195
|
textarea.contentEditable = "true";
|
|
195
196
|
try {
|
|
196
197
|
canInsertText = document.execCommand("insertText", false, text);
|
|
198
|
+
if (debugMode2)
|
|
199
|
+
console.log("execCommand returned:", canInsertText, "for text with", text.split("\n").length, "lines");
|
|
197
200
|
} catch (error) {
|
|
198
201
|
canInsertText = false;
|
|
202
|
+
if (debugMode2)
|
|
203
|
+
console.log("execCommand threw error:", error);
|
|
199
204
|
}
|
|
200
205
|
textarea.contentEditable = "false";
|
|
201
206
|
}
|
|
@@ -503,20 +508,20 @@ ${blockSuffix}` : suffix;
|
|
|
503
508
|
}
|
|
504
509
|
function undoUnorderedListStyle(text) {
|
|
505
510
|
const lines = text.split("\n");
|
|
506
|
-
const
|
|
507
|
-
const shouldUndoUnorderedList = lines.every((line) =>
|
|
511
|
+
const unorderedListRegex = /^[-*+] /;
|
|
512
|
+
const shouldUndoUnorderedList = lines.every((line) => unorderedListRegex.test(line));
|
|
508
513
|
let result = lines;
|
|
509
514
|
if (shouldUndoUnorderedList) {
|
|
510
|
-
result = lines.map((line) => line.
|
|
515
|
+
result = lines.map((line) => line.replace(unorderedListRegex, ""));
|
|
511
516
|
}
|
|
512
517
|
return {
|
|
513
518
|
text: result.join("\n"),
|
|
514
519
|
processed: shouldUndoUnorderedList
|
|
515
520
|
};
|
|
516
521
|
}
|
|
517
|
-
function makePrefix(index, unorderedList) {
|
|
522
|
+
function makePrefix(index, unorderedList, bulletMarker = "-") {
|
|
518
523
|
if (unorderedList) {
|
|
519
|
-
return
|
|
524
|
+
return `${bulletMarker} `;
|
|
520
525
|
} else {
|
|
521
526
|
return `${index + 1}. `;
|
|
522
527
|
}
|
|
@@ -544,7 +549,7 @@ ${blockSuffix}` : suffix;
|
|
|
544
549
|
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
545
550
|
const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
|
|
546
551
|
const prefixedLines = pristineText.split("\n").map((value, index) => {
|
|
547
|
-
return `${makePrefix(index, style.unorderedList)}${value}`;
|
|
552
|
+
return `${makePrefix(index, style.unorderedList, style.bulletMarker)}${value}`;
|
|
548
553
|
});
|
|
549
554
|
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
550
555
|
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
|
|
@@ -587,7 +592,7 @@ ${blockSuffix}` : suffix;
|
|
|
587
592
|
adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
|
|
588
593
|
const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
|
|
589
594
|
const orderedListRegex = /^\d+\.\s+/;
|
|
590
|
-
const unorderedListRegex =
|
|
595
|
+
const unorderedListRegex = /^[-*+] /;
|
|
591
596
|
const hasOrderedList = orderedListRegex.test(currentLine);
|
|
592
597
|
const hasUnorderedList = unorderedListRegex.test(currentLine);
|
|
593
598
|
const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
|
|
@@ -662,8 +667,8 @@ ${blockSuffix}` : suffix;
|
|
|
662
667
|
}
|
|
663
668
|
lineStart += line.length + 1;
|
|
664
669
|
}
|
|
665
|
-
if (currentLine
|
|
666
|
-
if (
|
|
670
|
+
if (/^[-*+] /.test(currentLine)) {
|
|
671
|
+
if (/^[-*+] \[[ xX]\] /.test(currentLine)) {
|
|
667
672
|
formats.push("task-list");
|
|
668
673
|
} else {
|
|
669
674
|
formats.push("bullet-list");
|
|
@@ -684,36 +689,30 @@ ${blockSuffix}` : suffix;
|
|
|
684
689
|
const lookBehind = Math.max(0, selectionStart - 10);
|
|
685
690
|
const lookAhead = Math.min(value.length, selectionEnd + 10);
|
|
686
691
|
const surrounding = value.slice(lookBehind, lookAhead);
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const nextCloseBold = afterCursor.indexOf("**");
|
|
692
|
-
if (lastOpenBold !== -1 && nextCloseBold !== -1) {
|
|
693
|
-
formats.push("bold");
|
|
694
|
-
}
|
|
692
|
+
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
693
|
+
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
694
|
+
if (["**", "__"].some((marker) => surrounding.includes(marker) && beforeCursor.lastIndexOf(marker) !== -1 && afterCursor.indexOf(marker) !== -1)) {
|
|
695
|
+
formats.push("bold");
|
|
695
696
|
}
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
formats.push("italic");
|
|
703
|
-
}
|
|
697
|
+
const singleMarker = (ch) => {
|
|
698
|
+
const c = ch === "*" ? "\\*" : ch;
|
|
699
|
+
return new RegExp(`(^|[^${c}])${c}[^${c}]*$`).test(beforeCursor) && new RegExp(`^[^${c}]*${c}(?!${c})`).test(afterCursor);
|
|
700
|
+
};
|
|
701
|
+
if (surrounding.includes("_") && singleMarker("_") || surrounding.includes("*") && singleMarker("*")) {
|
|
702
|
+
formats.push("italic");
|
|
704
703
|
}
|
|
705
704
|
if (surrounding.includes("`")) {
|
|
706
|
-
const
|
|
707
|
-
const
|
|
708
|
-
if (
|
|
705
|
+
const beforeCursor2 = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
706
|
+
const afterCursor2 = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
707
|
+
if (beforeCursor2.includes("`") && afterCursor2.includes("`")) {
|
|
709
708
|
formats.push("code");
|
|
710
709
|
}
|
|
711
710
|
}
|
|
712
711
|
if (surrounding.includes("[") && surrounding.includes("]")) {
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
const lastOpenBracket =
|
|
716
|
-
const nextCloseBracket =
|
|
712
|
+
const beforeCursor2 = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
713
|
+
const afterCursor2 = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
714
|
+
const lastOpenBracket = beforeCursor2.lastIndexOf("[");
|
|
715
|
+
const nextCloseBracket = afterCursor2.indexOf("]");
|
|
717
716
|
if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
|
|
718
717
|
const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
|
|
719
718
|
if (afterBracket.startsWith("(")) {
|
|
@@ -761,23 +760,129 @@ ${blockSuffix}` : suffix;
|
|
|
761
760
|
}
|
|
762
761
|
}
|
|
763
762
|
|
|
763
|
+
// src/operations/inline.js
|
|
764
|
+
var isEmphasis = (ch) => ch === "*" || ch === "_";
|
|
765
|
+
var OPENING_PUNCTUATION = /[(\[{"'“‘]/;
|
|
766
|
+
var CLOSING_PUNCTUATION = /[.,;:!?)\]}"'”’]/;
|
|
767
|
+
function carries(kind, n) {
|
|
768
|
+
return kind === "bold" ? n >= 2 : n % 2 === 1;
|
|
769
|
+
}
|
|
770
|
+
function runLength(text, ch, fromEnd) {
|
|
771
|
+
let n = 0;
|
|
772
|
+
while (n < text.length && text[fromEnd ? text.length - 1 - n : n] === ch)
|
|
773
|
+
n++;
|
|
774
|
+
return n;
|
|
775
|
+
}
|
|
776
|
+
function findWrap(value, start, end, kind) {
|
|
777
|
+
const size = kind === "bold" ? 2 : 1;
|
|
778
|
+
let open = start;
|
|
779
|
+
while (open > 0 && isEmphasis(value[open - 1]))
|
|
780
|
+
open--;
|
|
781
|
+
let close = end;
|
|
782
|
+
while (close < value.length && isEmphasis(value[close]))
|
|
783
|
+
close++;
|
|
784
|
+
const before = value.slice(open, start);
|
|
785
|
+
const after = value.slice(end, close);
|
|
786
|
+
for (const ch of ["*", "_"]) {
|
|
787
|
+
if (carries(kind, runLength(before, ch, true)) && carries(kind, runLength(after, ch, false))) {
|
|
788
|
+
return { open: start - size, close: end, size };
|
|
789
|
+
}
|
|
790
|
+
const outerBefore = runLength(before, ch, false);
|
|
791
|
+
if (outerBefore < before.length && carries(kind, outerBefore) && carries(kind, runLength(after, ch, true))) {
|
|
792
|
+
return { open, close: close - size, size };
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
function targetRange(value, selectionStart, selectionEnd) {
|
|
798
|
+
let start = selectionStart;
|
|
799
|
+
let end = selectionEnd;
|
|
800
|
+
if (start === end) {
|
|
801
|
+
start = wordSelectionStart(value, start);
|
|
802
|
+
end = wordSelectionEnd(value, end, false);
|
|
803
|
+
while (start < end && OPENING_PUNCTUATION.test(value[start]))
|
|
804
|
+
start++;
|
|
805
|
+
while (end > start && CLOSING_PUNCTUATION.test(value[end - 1]))
|
|
806
|
+
end--;
|
|
807
|
+
}
|
|
808
|
+
while (start < end && /\s/.test(value[start]))
|
|
809
|
+
start++;
|
|
810
|
+
while (end > start && /\s/.test(value[end - 1]))
|
|
811
|
+
end--;
|
|
812
|
+
while (start < end && isEmphasis(value[start]))
|
|
813
|
+
start++;
|
|
814
|
+
while (end > start && isEmphasis(value[end - 1]))
|
|
815
|
+
end--;
|
|
816
|
+
return { start, end };
|
|
817
|
+
}
|
|
818
|
+
var clamp = (n, low, high) => Math.min(Math.max(n, low), high);
|
|
819
|
+
function inlineStyle(textarea, kind, marker) {
|
|
820
|
+
const value = textarea.value;
|
|
821
|
+
const caretStart = textarea.selectionStart;
|
|
822
|
+
const caretEnd = textarea.selectionEnd;
|
|
823
|
+
const hadSelection = caretStart !== caretEnd;
|
|
824
|
+
const { start, end } = targetRange(value, caretStart, caretEnd);
|
|
825
|
+
const wrap = start < end ? findWrap(value, start, end, kind) : null;
|
|
826
|
+
if (wrap) {
|
|
827
|
+
textarea.selectionStart = wrap.open;
|
|
828
|
+
textarea.selectionEnd = wrap.close + wrap.size;
|
|
829
|
+
const newStart = start - wrap.size;
|
|
830
|
+
const newEnd = end - wrap.size;
|
|
831
|
+
const caret2 = clamp(caretStart - wrap.size, newStart, newEnd);
|
|
832
|
+
return {
|
|
833
|
+
text: value.slice(wrap.open + wrap.size, wrap.close),
|
|
834
|
+
selectionStart: hadSelection ? newStart : caret2,
|
|
835
|
+
selectionEnd: hadSelection ? newEnd : caret2
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
textarea.selectionStart = start;
|
|
839
|
+
textarea.selectionEnd = end;
|
|
840
|
+
const caret = clamp(caretStart, start, end) + marker.length;
|
|
841
|
+
return {
|
|
842
|
+
text: marker + value.slice(start, end) + marker,
|
|
843
|
+
selectionStart: hadSelection ? start + marker.length : caret,
|
|
844
|
+
selectionEnd: hadSelection ? end + marker.length : caret
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/core/markers.js
|
|
849
|
+
var MARKERS = {
|
|
850
|
+
bold: ["**", "__"],
|
|
851
|
+
italic: ["_", "*"],
|
|
852
|
+
bullet: ["-", "*", "+"]
|
|
853
|
+
};
|
|
854
|
+
function resolveMarker(kind, marker) {
|
|
855
|
+
return MARKERS[kind].indexOf(marker) !== -1 ? marker : MARKERS[kind][0];
|
|
856
|
+
}
|
|
857
|
+
var DETECT = {
|
|
858
|
+
bullet: /^[ \t]*([-*+])[ \t]+\S/m,
|
|
859
|
+
bold: /(\*\*|__)(?=[^\s*_])[^\n]*?[^\s*_]\1/,
|
|
860
|
+
italic: /(?:^|[^*_\w])([*_])(?=[^\s*_])[^\n*_]*?[^\s*_]\1(?![*_\w])/
|
|
861
|
+
};
|
|
862
|
+
function detectMarkers(text) {
|
|
863
|
+
const found = {};
|
|
864
|
+
Object.keys(DETECT).forEach((kind) => {
|
|
865
|
+
const match = DETECT[kind].exec(text || "");
|
|
866
|
+
found[kind] = match ? match[1] : null;
|
|
867
|
+
});
|
|
868
|
+
return found;
|
|
869
|
+
}
|
|
870
|
+
|
|
764
871
|
// src/index.js
|
|
765
|
-
function toggleBold(textarea) {
|
|
872
|
+
function toggleBold(textarea, options = {}) {
|
|
766
873
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
767
874
|
return;
|
|
768
875
|
debugLog("toggleBold", "Starting");
|
|
769
876
|
debugSelection(textarea, "Before");
|
|
770
|
-
const
|
|
771
|
-
const result = blockStyle(textarea, style);
|
|
877
|
+
const result = inlineStyle(textarea, "bold", resolveMarker("bold", options.marker));
|
|
772
878
|
debugResult(result);
|
|
773
879
|
insertText(textarea, result);
|
|
774
880
|
debugSelection(textarea, "After");
|
|
775
881
|
}
|
|
776
|
-
function toggleItalic(textarea) {
|
|
882
|
+
function toggleItalic(textarea, options = {}) {
|
|
777
883
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
778
884
|
return;
|
|
779
|
-
const
|
|
780
|
-
const result = blockStyle(textarea, style);
|
|
885
|
+
const result = inlineStyle(textarea, "italic", resolveMarker("italic", options.marker));
|
|
781
886
|
insertText(textarea, result);
|
|
782
887
|
}
|
|
783
888
|
function toggleCode(textarea) {
|
|
@@ -809,10 +914,11 @@ ${blockSuffix}` : suffix;
|
|
|
809
914
|
const result = blockStyle(textarea, style);
|
|
810
915
|
insertText(textarea, result);
|
|
811
916
|
}
|
|
812
|
-
function toggleBulletList(textarea) {
|
|
917
|
+
function toggleBulletList(textarea, options = {}) {
|
|
813
918
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
814
919
|
return;
|
|
815
920
|
const style = mergeWithDefaults(FORMATS.bulletList);
|
|
921
|
+
style.bulletMarker = resolveMarker("bullet", options.marker);
|
|
816
922
|
applyListStyle(textarea, style);
|
|
817
923
|
}
|
|
818
924
|
function toggleNumberedList(textarea) {
|
|
@@ -995,6 +1101,7 @@ ${blockSuffix}` : suffix;
|
|
|
995
1101
|
hasFormat: hasFormat2,
|
|
996
1102
|
expandSelection: expandSelection2,
|
|
997
1103
|
applyCustomFormat,
|
|
1104
|
+
detectMarkers,
|
|
998
1105
|
preserveSelection,
|
|
999
1106
|
setUndoMethod,
|
|
1000
1107
|
setDebugMode,
|