markdown-actions 1.1.2 → 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 +142 -39
- package/dist/markdown-actions.esm.js.map +4 -4
- package/dist/markdown-actions.js +142 -39
- package/dist/markdown-actions.js.map +4 -4
- package/dist/markdown-actions.min.js +22 -23
- package/package.json +12 -3
- package/src/core/detection.js +15 -19
- 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,
|
|
@@ -507,20 +508,20 @@ ${blockSuffix}` : suffix;
|
|
|
507
508
|
}
|
|
508
509
|
function undoUnorderedListStyle(text) {
|
|
509
510
|
const lines = text.split("\n");
|
|
510
|
-
const
|
|
511
|
-
const shouldUndoUnorderedList = lines.every((line) =>
|
|
511
|
+
const unorderedListRegex = /^[-*+] /;
|
|
512
|
+
const shouldUndoUnorderedList = lines.every((line) => unorderedListRegex.test(line));
|
|
512
513
|
let result = lines;
|
|
513
514
|
if (shouldUndoUnorderedList) {
|
|
514
|
-
result = lines.map((line) => line.
|
|
515
|
+
result = lines.map((line) => line.replace(unorderedListRegex, ""));
|
|
515
516
|
}
|
|
516
517
|
return {
|
|
517
518
|
text: result.join("\n"),
|
|
518
519
|
processed: shouldUndoUnorderedList
|
|
519
520
|
};
|
|
520
521
|
}
|
|
521
|
-
function makePrefix(index, unorderedList) {
|
|
522
|
+
function makePrefix(index, unorderedList, bulletMarker = "-") {
|
|
522
523
|
if (unorderedList) {
|
|
523
|
-
return
|
|
524
|
+
return `${bulletMarker} `;
|
|
524
525
|
} else {
|
|
525
526
|
return `${index + 1}. `;
|
|
526
527
|
}
|
|
@@ -548,7 +549,7 @@ ${blockSuffix}` : suffix;
|
|
|
548
549
|
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
549
550
|
const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
|
|
550
551
|
const prefixedLines = pristineText.split("\n").map((value, index) => {
|
|
551
|
-
return `${makePrefix(index, style.unorderedList)}${value}`;
|
|
552
|
+
return `${makePrefix(index, style.unorderedList, style.bulletMarker)}${value}`;
|
|
552
553
|
});
|
|
553
554
|
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
554
555
|
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
|
|
@@ -591,7 +592,7 @@ ${blockSuffix}` : suffix;
|
|
|
591
592
|
adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
|
|
592
593
|
const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
|
|
593
594
|
const orderedListRegex = /^\d+\.\s+/;
|
|
594
|
-
const unorderedListRegex =
|
|
595
|
+
const unorderedListRegex = /^[-*+] /;
|
|
595
596
|
const hasOrderedList = orderedListRegex.test(currentLine);
|
|
596
597
|
const hasUnorderedList = unorderedListRegex.test(currentLine);
|
|
597
598
|
const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
|
|
@@ -666,8 +667,8 @@ ${blockSuffix}` : suffix;
|
|
|
666
667
|
}
|
|
667
668
|
lineStart += line.length + 1;
|
|
668
669
|
}
|
|
669
|
-
if (currentLine
|
|
670
|
-
if (
|
|
670
|
+
if (/^[-*+] /.test(currentLine)) {
|
|
671
|
+
if (/^[-*+] \[[ xX]\] /.test(currentLine)) {
|
|
671
672
|
formats.push("task-list");
|
|
672
673
|
} else {
|
|
673
674
|
formats.push("bullet-list");
|
|
@@ -688,36 +689,30 @@ ${blockSuffix}` : suffix;
|
|
|
688
689
|
const lookBehind = Math.max(0, selectionStart - 10);
|
|
689
690
|
const lookAhead = Math.min(value.length, selectionEnd + 10);
|
|
690
691
|
const surrounding = value.slice(lookBehind, lookAhead);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const nextCloseBold = afterCursor.indexOf("**");
|
|
696
|
-
if (lastOpenBold !== -1 && nextCloseBold !== -1) {
|
|
697
|
-
formats.push("bold");
|
|
698
|
-
}
|
|
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");
|
|
699
696
|
}
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
formats.push("italic");
|
|
707
|
-
}
|
|
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");
|
|
708
703
|
}
|
|
709
704
|
if (surrounding.includes("`")) {
|
|
710
|
-
const
|
|
711
|
-
const
|
|
712
|
-
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("`")) {
|
|
713
708
|
formats.push("code");
|
|
714
709
|
}
|
|
715
710
|
}
|
|
716
711
|
if (surrounding.includes("[") && surrounding.includes("]")) {
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
const lastOpenBracket =
|
|
720
|
-
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("]");
|
|
721
716
|
if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
|
|
722
717
|
const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
|
|
723
718
|
if (afterBracket.startsWith("(")) {
|
|
@@ -765,23 +760,129 @@ ${blockSuffix}` : suffix;
|
|
|
765
760
|
}
|
|
766
761
|
}
|
|
767
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
|
+
|
|
768
871
|
// src/index.js
|
|
769
|
-
function toggleBold(textarea) {
|
|
872
|
+
function toggleBold(textarea, options = {}) {
|
|
770
873
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
771
874
|
return;
|
|
772
875
|
debugLog("toggleBold", "Starting");
|
|
773
876
|
debugSelection(textarea, "Before");
|
|
774
|
-
const
|
|
775
|
-
const result = blockStyle(textarea, style);
|
|
877
|
+
const result = inlineStyle(textarea, "bold", resolveMarker("bold", options.marker));
|
|
776
878
|
debugResult(result);
|
|
777
879
|
insertText(textarea, result);
|
|
778
880
|
debugSelection(textarea, "After");
|
|
779
881
|
}
|
|
780
|
-
function toggleItalic(textarea) {
|
|
882
|
+
function toggleItalic(textarea, options = {}) {
|
|
781
883
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
782
884
|
return;
|
|
783
|
-
const
|
|
784
|
-
const result = blockStyle(textarea, style);
|
|
885
|
+
const result = inlineStyle(textarea, "italic", resolveMarker("italic", options.marker));
|
|
785
886
|
insertText(textarea, result);
|
|
786
887
|
}
|
|
787
888
|
function toggleCode(textarea) {
|
|
@@ -813,10 +914,11 @@ ${blockSuffix}` : suffix;
|
|
|
813
914
|
const result = blockStyle(textarea, style);
|
|
814
915
|
insertText(textarea, result);
|
|
815
916
|
}
|
|
816
|
-
function toggleBulletList(textarea) {
|
|
917
|
+
function toggleBulletList(textarea, options = {}) {
|
|
817
918
|
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
818
919
|
return;
|
|
819
920
|
const style = mergeWithDefaults(FORMATS.bulletList);
|
|
921
|
+
style.bulletMarker = resolveMarker("bullet", options.marker);
|
|
820
922
|
applyListStyle(textarea, style);
|
|
821
923
|
}
|
|
822
924
|
function toggleNumberedList(textarea) {
|
|
@@ -999,6 +1101,7 @@ ${blockSuffix}` : suffix;
|
|
|
999
1101
|
hasFormat: hasFormat2,
|
|
1000
1102
|
expandSelection: expandSelection2,
|
|
1001
1103
|
applyCustomFormat,
|
|
1104
|
+
detectMarkers,
|
|
1002
1105
|
preserveSelection,
|
|
1003
1106
|
setUndoMethod,
|
|
1004
1107
|
setDebugMode,
|