markdown-actions 1.0.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/LICENSE +33 -0
- package/README.md +379 -0
- package/dist/markdown-actions.esm.js +625 -0
- package/dist/markdown-actions.esm.js.map +7 -0
- package/dist/markdown-actions.js +646 -0
- package/dist/markdown-actions.js.map +7 -0
- package/dist/markdown-actions.min.js +19 -0
- package/package.json +45 -0
- package/src/core/detection.js +169 -0
- package/src/core/formats.js +76 -0
- package/src/core/insertion-new.js +66 -0
- package/src/core/insertion.js +79 -0
- package/src/core/selection-new.js +137 -0
- package/src/core/selection.js +117 -0
- package/src/core/style-args.js +33 -0
- package/src/core/style-text.js +30 -0
- package/src/index-new.js +260 -0
- package/src/index.js +229 -0
- package/src/operations/block-new.js +109 -0
- package/src/operations/block.js +139 -0
- package/src/operations/list-new.js +137 -0
- package/src/operations/list.js +152 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List operations for bullet and numbered lists
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { expandSelectionToLine, newlinesToSurroundSelectedText } from '../core/selection.js'
|
|
6
|
+
import { insertText } from '../core/insertion.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Undo ordered list formatting
|
|
10
|
+
*/
|
|
11
|
+
function undoOrderedListStyle(text) {
|
|
12
|
+
const lines = text.split('\n')
|
|
13
|
+
const orderedListRegex = /^\d+\.\s+/
|
|
14
|
+
const shouldUndoOrderedList = lines.every(line => orderedListRegex.test(line))
|
|
15
|
+
let result = lines
|
|
16
|
+
if (shouldUndoOrderedList) {
|
|
17
|
+
result = lines.map(line => line.replace(orderedListRegex, ''))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
text: result.join('\n'),
|
|
22
|
+
processed: shouldUndoOrderedList
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Undo unordered list formatting
|
|
28
|
+
*/
|
|
29
|
+
function undoUnorderedListStyle(text) {
|
|
30
|
+
const lines = text.split('\n')
|
|
31
|
+
const unorderedListPrefix = '- '
|
|
32
|
+
const shouldUndoUnorderedList = lines.every(line => line.startsWith(unorderedListPrefix))
|
|
33
|
+
let result = lines
|
|
34
|
+
if (shouldUndoUnorderedList) {
|
|
35
|
+
result = lines.map(line => line.slice(unorderedListPrefix.length))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
text: result.join('\n'),
|
|
40
|
+
processed: shouldUndoUnorderedList
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Make prefix for list item
|
|
46
|
+
*/
|
|
47
|
+
function makePrefix(index, unorderedList) {
|
|
48
|
+
if (unorderedList) {
|
|
49
|
+
return '- '
|
|
50
|
+
} else {
|
|
51
|
+
return `${index + 1}. `
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clear existing list style
|
|
57
|
+
*/
|
|
58
|
+
function clearExistingListStyle(style, selectedText) {
|
|
59
|
+
let undoResult
|
|
60
|
+
let undoResultOppositeList
|
|
61
|
+
let pristineText
|
|
62
|
+
|
|
63
|
+
if (style.orderedList) {
|
|
64
|
+
undoResult = undoOrderedListStyle(selectedText)
|
|
65
|
+
undoResultOppositeList = undoUnorderedListStyle(undoResult.text)
|
|
66
|
+
pristineText = undoResultOppositeList.text
|
|
67
|
+
} else {
|
|
68
|
+
undoResult = undoUnorderedListStyle(selectedText)
|
|
69
|
+
undoResultOppositeList = undoOrderedListStyle(undoResult.text)
|
|
70
|
+
pristineText = undoResultOppositeList.text
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return [undoResult, undoResultOppositeList, pristineText]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Apply list styling to selected text
|
|
78
|
+
*/
|
|
79
|
+
export function listStyle(textarea, style) {
|
|
80
|
+
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd
|
|
81
|
+
let selectionStart = textarea.selectionStart
|
|
82
|
+
let selectionEnd = textarea.selectionEnd
|
|
83
|
+
|
|
84
|
+
// Select whole line
|
|
85
|
+
expandSelectionToLine(textarea)
|
|
86
|
+
|
|
87
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd)
|
|
88
|
+
|
|
89
|
+
// Clear any existing list formatting
|
|
90
|
+
const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText)
|
|
91
|
+
|
|
92
|
+
const prefixedLines = pristineText.split('\n').map((value, index) => {
|
|
93
|
+
return `${makePrefix(index, style.unorderedList)}${value}`
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
97
|
+
return previousValue + makePrefix(currentIndex, style.unorderedList).length
|
|
98
|
+
}, 0)
|
|
99
|
+
|
|
100
|
+
const totalPrefixLengthOppositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
101
|
+
return previousValue + makePrefix(currentIndex, !style.unorderedList).length
|
|
102
|
+
}, 0)
|
|
103
|
+
|
|
104
|
+
// If we're undoing the same list type, just return the pristine text
|
|
105
|
+
if (undoResult.processed) {
|
|
106
|
+
if (noInitialSelection) {
|
|
107
|
+
selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0)
|
|
108
|
+
selectionEnd = selectionStart
|
|
109
|
+
} else {
|
|
110
|
+
selectionStart = textarea.selectionStart
|
|
111
|
+
selectionEnd = textarea.selectionEnd - totalPrefixLength
|
|
112
|
+
}
|
|
113
|
+
return { text: pristineText, selectionStart, selectionEnd }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Apply new list formatting
|
|
117
|
+
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea)
|
|
118
|
+
const text = newlinesToAppend + prefixedLines.join('\n') + newlinesToPrepend
|
|
119
|
+
|
|
120
|
+
if (noInitialSelection) {
|
|
121
|
+
selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0)
|
|
122
|
+
selectionEnd = selectionStart
|
|
123
|
+
} else {
|
|
124
|
+
if (undoResultOppositeList.processed) {
|
|
125
|
+
// Converting from one list type to another
|
|
126
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0)
|
|
127
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOppositeList
|
|
128
|
+
} else {
|
|
129
|
+
// Adding list formatting to plain text
|
|
130
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0)
|
|
131
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { text, selectionStart, selectionEnd }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Apply list style to textarea
|
|
140
|
+
*/
|
|
141
|
+
export function applyListStyle(textarea, style) {
|
|
142
|
+
// Save the original selection boundaries before expansion
|
|
143
|
+
const originalStart = textarea.selectionStart
|
|
144
|
+
const originalEnd = textarea.selectionEnd
|
|
145
|
+
|
|
146
|
+
// Get the result which includes expandSelectionToLine
|
|
147
|
+
const result = listStyle(textarea, style)
|
|
148
|
+
|
|
149
|
+
// At this point, textarea selection has been expanded to full line(s)
|
|
150
|
+
// insertText will use the current (expanded) selection to know what to replace
|
|
151
|
+
insertText(textarea, result)
|
|
152
|
+
}
|