imdone-core 1.38.0 → 1.40.1
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/lib/adapters/parsers/task/CardContentParser.js +256 -0
- package/lib/card.js +4 -2
- package/lib/file.js +96 -173
- package/lib/repository.js +2 -5
- package/lib/task.js +9 -8
- package/package.json +1 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
const eol = require('eol')
|
|
2
|
+
const Task = require('../../../task')
|
|
3
|
+
const XRegExp = require('xregexp')
|
|
4
|
+
|
|
5
|
+
const FILE_TYPES = {
|
|
6
|
+
MARKDOWN: 'markdown',
|
|
7
|
+
CODE: 'code',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const START_TAG = '<card>'
|
|
11
|
+
const END_TAG = '</card>'
|
|
12
|
+
const CODE_STYLE_PATTERN = '([A-Z]+[A-Z-_]+?)((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
|
|
13
|
+
const LIST_NAME_PATTERN = '[\\p{L}0-9-_]{2,}' // [a-zA-Z-_]+?
|
|
14
|
+
const HASH_STYLE_REGEX = new XRegExp(
|
|
15
|
+
`#(${LIST_NAME_PATTERN})(:(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$`,
|
|
16
|
+
'gm'
|
|
17
|
+
)
|
|
18
|
+
const HASH_STYLE_META_ORDER_REGEX = new XRegExp(
|
|
19
|
+
`#(${LIST_NAME_PATTERN})[ \\t]+(.+)$`, 'gm'
|
|
20
|
+
)
|
|
21
|
+
const LINK_STYLE_REGEX = new XRegExp(
|
|
22
|
+
`(\\[.*\\]\\s?)?(\\[(.+)\\]\\(#(${LIST_NAME_PATTERN})(:)(-?[\\d.]+(?:e-?\\d+)?)?\\))`,
|
|
23
|
+
'gm'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
function getContentLines(content) {
|
|
27
|
+
let foundContent = false
|
|
28
|
+
return eol.split(content).filter((line) => {
|
|
29
|
+
const hasContent = line.trim()
|
|
30
|
+
if (!foundContent && hasContent) foundContent = true
|
|
31
|
+
return foundContent || hasContent
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isCheckBoxTask (config, line, beforeText) {
|
|
36
|
+
if (!config.isAddCheckBoxTasks()) return
|
|
37
|
+
|
|
38
|
+
const beforeTextCheckData = Task.getCheckedData(beforeText)
|
|
39
|
+
if (!beforeTextCheckData) return
|
|
40
|
+
|
|
41
|
+
const lineCheckData = Task.getCheckedData(line)
|
|
42
|
+
if (!lineCheckData) return
|
|
43
|
+
|
|
44
|
+
return lineCheckData.pad <= beforeTextCheckData.pad
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasTaskInText(config, text, isCodeFile) {
|
|
48
|
+
return (
|
|
49
|
+
(isCodeFile && hasCodeStyleTask(config, text)) ||
|
|
50
|
+
((new XRegExp(HASH_STYLE_REGEX).test(text) ||
|
|
51
|
+
new XRegExp(HASH_STYLE_META_ORDER_REGEX).test(text) ||
|
|
52
|
+
new XRegExp(LINK_STYLE_REGEX).test(text)) &&
|
|
53
|
+
config.lists.find((list) => text.includes(`#${list.name}`)))
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hasCodeStyleTask (config, text) {
|
|
58
|
+
let result = new RegExp(CODE_STYLE_PATTERN).exec(text)
|
|
59
|
+
if (!result) return false
|
|
60
|
+
return config.includeList(result[1])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getCardTagsTaskContent ({
|
|
64
|
+
content,
|
|
65
|
+
fileType
|
|
66
|
+
}) {
|
|
67
|
+
const isCodeFile = fileType === FILE_TYPES.CODE
|
|
68
|
+
if (isCodeFile) return
|
|
69
|
+
|
|
70
|
+
const isMarkDownFile = fileType === FILE_TYPES.MARKDOWN
|
|
71
|
+
let lines = getContentLines(content)
|
|
72
|
+
const hasCardStartTag =
|
|
73
|
+
isMarkDownFile && lines.length > 1 && lines[1].trim() === START_TAG
|
|
74
|
+
if (!hasCardStartTag) return
|
|
75
|
+
|
|
76
|
+
let openBlockCommentTokens = 0
|
|
77
|
+
let closeBlockCommentTokens = 0
|
|
78
|
+
let hasCardEndTag = false
|
|
79
|
+
const rawTaskContentLines = []
|
|
80
|
+
const taskContentLines = []
|
|
81
|
+
for (let i = 1; i < lines.length; i++) {
|
|
82
|
+
let line = lines[i]
|
|
83
|
+
// Check for matching block comments in markdown files
|
|
84
|
+
if (isMarkDownFile && line.trim().indexOf('<!--') > -1)
|
|
85
|
+
openBlockCommentTokens++
|
|
86
|
+
if (isMarkDownFile && line.trim().indexOf('-->') > -1)
|
|
87
|
+
closeBlockCommentTokens++
|
|
88
|
+
|
|
89
|
+
rawTaskContentLines.push(line)
|
|
90
|
+
|
|
91
|
+
if (line.trim() === START_TAG) continue
|
|
92
|
+
if (line.trim() === END_TAG) {
|
|
93
|
+
hasCardEndTag = true
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
taskContentLines.push(line)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Remove extra block comment tokens
|
|
100
|
+
if (
|
|
101
|
+
isMarkDownFile &&
|
|
102
|
+
rawTaskContentLines.length &&
|
|
103
|
+
rawTaskContentLines[rawTaskContentLines.length - 1].trim() === '-->' &&
|
|
104
|
+
openBlockCommentTokens !== closeBlockCommentTokens
|
|
105
|
+
)
|
|
106
|
+
rawTaskContentLines.pop()
|
|
107
|
+
|
|
108
|
+
const isWrappedWithCardTag = hasCardStartTag && hasCardEndTag
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
rawTaskContentLines,
|
|
112
|
+
taskContentLines,
|
|
113
|
+
isWrappedWithCardTag,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getCodeFileTaskContent ({
|
|
118
|
+
config,
|
|
119
|
+
content,
|
|
120
|
+
inBlockComment,
|
|
121
|
+
fileType,
|
|
122
|
+
lang
|
|
123
|
+
}) {
|
|
124
|
+
const isCodeFile = fileType === FILE_TYPES.CODE
|
|
125
|
+
if (!isCodeFile) return
|
|
126
|
+
|
|
127
|
+
const rawTaskContentLines = []
|
|
128
|
+
let lines = getContentLines(content)
|
|
129
|
+
for (let i = 1; i < lines.length; i++) {
|
|
130
|
+
let line = lines[i]
|
|
131
|
+
// Another task ends a task
|
|
132
|
+
if (hasTaskInText(config, line, isCodeFile)) break
|
|
133
|
+
if (
|
|
134
|
+
!inBlockComment &&
|
|
135
|
+
(line.indexOf(lang.symbol) < 0 ||
|
|
136
|
+
line.trim().indexOf(lang.symbol) > 0 ||
|
|
137
|
+
line.trim() === lang.symbol)
|
|
138
|
+
)
|
|
139
|
+
break
|
|
140
|
+
if (lang.block && line.trim() === lang.block.end)
|
|
141
|
+
break
|
|
142
|
+
if (lang.block && line.trim() === lang.block.ignore)
|
|
143
|
+
break
|
|
144
|
+
if (line && lang && lang.block) line = line.replace(lang.block.end, '')
|
|
145
|
+
|
|
146
|
+
rawTaskContentLines.push(line)
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
rawTaskContentLines,
|
|
150
|
+
taskContentLines: rawTaskContentLines,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getTextFileTaskContent ({
|
|
155
|
+
config,
|
|
156
|
+
content,
|
|
157
|
+
beforeText,
|
|
158
|
+
fileType,
|
|
159
|
+
}) {
|
|
160
|
+
const isCodeFile = fileType === FILE_TYPES.CODE
|
|
161
|
+
if (isCodeFile) return
|
|
162
|
+
|
|
163
|
+
let lines = getContentLines(content)
|
|
164
|
+
const hasCardStartTag = lines.length > 1 && lines[1].trim() === START_TAG
|
|
165
|
+
if (hasCardStartTag) return
|
|
166
|
+
|
|
167
|
+
const isMarkDownFile = fileType === FILE_TYPES.MARKDOWN
|
|
168
|
+
let openBlockCommentTokens = 0
|
|
169
|
+
let closeBlockCommentTokens = 0
|
|
170
|
+
let lastLineBlank = false
|
|
171
|
+
let endsWithDoubleBlank = false
|
|
172
|
+
let trailingBlankLines = 0
|
|
173
|
+
const rawTaskContentLines = []
|
|
174
|
+
|
|
175
|
+
for (let i = 1; i < lines.length; i++) {
|
|
176
|
+
let line = lines[i]
|
|
177
|
+
// Check for end of task
|
|
178
|
+
// Another task ends a task
|
|
179
|
+
if (hasTaskInText(config, line, isCodeFile)) break
|
|
180
|
+
if (isCheckBoxTask(config, line, beforeText)) break
|
|
181
|
+
|
|
182
|
+
// TODO Break this out into a function with an accurate name
|
|
183
|
+
// Check if task is a list item in markdown and end it if content is not part of a nested list
|
|
184
|
+
// #urgent
|
|
185
|
+
// <!-- order:-65 -->
|
|
186
|
+
if (
|
|
187
|
+
isMarkDownFile &&
|
|
188
|
+
beforeText &&
|
|
189
|
+
/^\s*(\*|-|\d+\.|[a-zA-Z]\.)\s$/.test(beforeText) &&
|
|
190
|
+
line.search(/\S/) <= beforeText.search(/\S/)
|
|
191
|
+
) break
|
|
192
|
+
|
|
193
|
+
rawTaskContentLines.push(line)
|
|
194
|
+
|
|
195
|
+
// Check for matching block comments in markdown files
|
|
196
|
+
if (isMarkDownFile && line.trim().indexOf('<!--') > -1)
|
|
197
|
+
openBlockCommentTokens++
|
|
198
|
+
if (isMarkDownFile && line.trim().indexOf('-->') > -1)
|
|
199
|
+
closeBlockCommentTokens++
|
|
200
|
+
|
|
201
|
+
const thisLineBlank = line.trim() === ''
|
|
202
|
+
|
|
203
|
+
if (thisLineBlank && lastLineBlank) {
|
|
204
|
+
endsWithDoubleBlank = true
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
lastLineBlank = thisLineBlank
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (endsWithDoubleBlank) trailingBlankLines = 2
|
|
212
|
+
else if (lastLineBlank) trailingBlankLines = 1
|
|
213
|
+
|
|
214
|
+
// Remove extra block comment tokens
|
|
215
|
+
if (
|
|
216
|
+
isMarkDownFile &&
|
|
217
|
+
rawTaskContentLines.length &&
|
|
218
|
+
rawTaskContentLines[rawTaskContentLines.length - 1].trim() === '-->' &&
|
|
219
|
+
openBlockCommentTokens !== closeBlockCommentTokens
|
|
220
|
+
)
|
|
221
|
+
rawTaskContentLines.pop()
|
|
222
|
+
|
|
223
|
+
const taskContentLines = trailingBlankLines
|
|
224
|
+
? rawTaskContentLines.slice(0, -trailingBlankLines)
|
|
225
|
+
: rawTaskContentLines;
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
rawTaskContentLines,
|
|
229
|
+
taskContentLines,
|
|
230
|
+
trailingBlankLines
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getTaskContent ({
|
|
235
|
+
config,
|
|
236
|
+
content,
|
|
237
|
+
inBlockComment,
|
|
238
|
+
beforeText,
|
|
239
|
+
fileType,
|
|
240
|
+
lang
|
|
241
|
+
}) {
|
|
242
|
+
return getCardTagsTaskContent({ content, fileType }) ||
|
|
243
|
+
getCodeFileTaskContent({ config, content, inBlockComment, fileType, lang }) ||
|
|
244
|
+
getTextFileTaskContent({ config, content, beforeText, fileType })
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = {
|
|
248
|
+
FILE_TYPES,
|
|
249
|
+
getTaskContent,
|
|
250
|
+
START_TAG,
|
|
251
|
+
END_TAG,
|
|
252
|
+
LIST_NAME_PATTERN,
|
|
253
|
+
HASH_STYLE_REGEX,
|
|
254
|
+
HASH_STYLE_META_ORDER_REGEX,
|
|
255
|
+
LINK_STYLE_REGEX
|
|
256
|
+
}
|
package/lib/card.js
CHANGED
|
@@ -6,6 +6,7 @@ const _isFunction = require('lodash.isfunction')
|
|
|
6
6
|
const _isEmpty = require('lodash.isempty')
|
|
7
7
|
const _get = require('lodash.get')
|
|
8
8
|
const eol = require('eol')
|
|
9
|
+
const moment = require('moment')
|
|
9
10
|
const allEmoji = require('markdown-it-emoji/lib/data/full.json')
|
|
10
11
|
|
|
11
12
|
const { removeMD, renderMarkdown } = require('./adapters/markdown')
|
|
@@ -115,6 +116,7 @@ module.exports = function newCard(task, _project, dontParse) {
|
|
|
115
116
|
|
|
116
117
|
get defaultData() {
|
|
117
118
|
return {
|
|
119
|
+
timestamp: moment().format(),
|
|
118
120
|
..._pick(
|
|
119
121
|
this,
|
|
120
122
|
...['beforeText',
|
|
@@ -186,10 +188,10 @@ module.exports = function newCard(task, _project, dontParse) {
|
|
|
186
188
|
? { ...pluginManager.getCardProperties(defaultData) }
|
|
187
189
|
: {}
|
|
188
190
|
const data = {
|
|
189
|
-
...defaultData,
|
|
190
191
|
...pluginData,
|
|
191
192
|
...props,
|
|
192
|
-
...computed
|
|
193
|
+
...computed,
|
|
194
|
+
...defaultData
|
|
193
195
|
}
|
|
194
196
|
|
|
195
197
|
this.data = { ...data }
|
package/lib/file.js
CHANGED
|
@@ -19,7 +19,16 @@ var _clone = require('lodash.clone'),
|
|
|
19
19
|
Task = require('./task'),
|
|
20
20
|
newCard = require('./card'),
|
|
21
21
|
fastSort = require('fast-sort/dist/sort'),
|
|
22
|
-
XRegExp = require('xregexp')
|
|
22
|
+
XRegExp = require('xregexp'),
|
|
23
|
+
moment = require('moment'),
|
|
24
|
+
{
|
|
25
|
+
FILE_TYPES,
|
|
26
|
+
getTaskContent,
|
|
27
|
+
LIST_NAME_PATTERN,
|
|
28
|
+
HASH_STYLE_REGEX,
|
|
29
|
+
HASH_STYLE_META_ORDER_REGEX,
|
|
30
|
+
LINK_STYLE_REGEX,
|
|
31
|
+
} = require('./adapters/parsers/task/CardContentParser')
|
|
23
32
|
|
|
24
33
|
const appContext = require('./context/ApplicationContext')
|
|
25
34
|
const { isNumber } = require('./task')
|
|
@@ -68,8 +77,6 @@ function File(opts) {
|
|
|
68
77
|
}
|
|
69
78
|
}
|
|
70
79
|
util.inherits(File, events.EventEmitter)
|
|
71
|
-
const START_TAG = (File.START_TAG = '<card>')
|
|
72
|
-
const END_TAG = (File.END_TAG = '</card>')
|
|
73
80
|
const CODE_BLOCK_REGEX = (File.CODE_BLOCK_REGEX = /`{3}[\s\S]*?`{3}/gm)
|
|
74
81
|
const INLINE_CODE_REGEX = (File.INLINE_CODE_REGEX = /`[\s\S]*?`/g)
|
|
75
82
|
const CODE_STYLE_END = '((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
|
|
@@ -77,28 +84,12 @@ const CODE_STYLE_END = '((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
|
|
|
77
84
|
const CODE_STYLE_PATTERN = (File.CODE_STYLE_PATTERN =
|
|
78
85
|
'([A-Z]+[A-Z-_]+?)' + CODE_STYLE_END)
|
|
79
86
|
|
|
80
|
-
const LIST_NAME_PATTERN = '[\\p{L}0-9-_]{2,}' // [a-zA-Z-_]+?
|
|
81
|
-
File.LIST_NAME_PATTERN = new XRegExp(LIST_NAME_PATTERN)
|
|
82
87
|
File.isListNameValid = function(listName) {
|
|
83
88
|
return new XRegExp(LIST_NAME_PATTERN).test(listName)
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
const HASH_STYLE_PATTERN = (File.HASH_STYLE_PATTERN = new XRegExp(
|
|
87
|
-
`#(${LIST_NAME_PATTERN})(:(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$`,
|
|
88
|
-
'gm'
|
|
89
|
-
))
|
|
90
|
-
|
|
91
|
-
const HASH_STYLE_META_ORDER_PATTERN = (File.HASH_STYLE_META_ORDER_PATTERN =
|
|
92
|
-
new XRegExp(`#(${LIST_NAME_PATTERN})[ \\t]+(.+)$`, 'gm'))
|
|
93
|
-
|
|
94
|
-
const LINK_STYLE_PATTERN = (File.LINK_STYLE_PATTERN = new XRegExp(
|
|
95
|
-
`(\\[.*\\]\\s?)?(\\[(.+)\\]\\(#(${LIST_NAME_PATTERN})(:)(-?[\\d.]+(?:e-?\\d+)?)?\\))`,
|
|
96
|
-
'gm'
|
|
97
|
-
))
|
|
98
|
-
|
|
99
91
|
const CHECK_STYLE_PATTERN = (File.CHECK_STYLE_PATTERN =
|
|
100
92
|
/^(\s*- \[([x ])\]\s)(.+$)/gm)
|
|
101
|
-
// var LINK_STYLE_PATTERN_META_ORDER = File.LINK_STYLE_PATTERN_META_ORDER = /(\[.*\]\s?)?(\[(.+)\]\(#([a-zA-Z-_]+?)\))/gm;
|
|
102
93
|
|
|
103
94
|
var getTaskId = (File.getTaskId = function (path, line, text) {
|
|
104
95
|
var shasum = crypto.createHash('sha1')
|
|
@@ -158,7 +149,7 @@ File.addCompleted = function ({ task, content, config }) {
|
|
|
158
149
|
if (task.list !== doneList) return content
|
|
159
150
|
if (task.completed || task.meta.completed) return content
|
|
160
151
|
if (content.includes(`completed${config.getMetaSep()}`)) return content
|
|
161
|
-
const now =
|
|
152
|
+
const now = moment().format()
|
|
162
153
|
content = task.addToLastCommentInContent(
|
|
163
154
|
content,
|
|
164
155
|
`completed${config.getMetaSep()}${now}`,
|
|
@@ -175,7 +166,7 @@ File.parseDueDate = function (config, text) {
|
|
|
175
166
|
if (dueString) return dueMatch
|
|
176
167
|
try {
|
|
177
168
|
const due = chrono.parseDate(dueMatch, new Date(), {forwardDate: true})
|
|
178
|
-
dueString = due.
|
|
169
|
+
dueString = moment(due).format()
|
|
179
170
|
return `${p1.toLowerCase()}${config.getMetaSep()}${dueString}`
|
|
180
171
|
} catch (e) {
|
|
181
172
|
// console.log(`Unable to parse due date for ${text}`)
|
|
@@ -292,9 +283,9 @@ File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
|
292
283
|
var self = this
|
|
293
284
|
var codeStyleRegex = new RegExp(CODE_STYLE_PATTERN, 'gm')
|
|
294
285
|
var result
|
|
295
|
-
const
|
|
286
|
+
const inBlockComment = this.isInBlockComment(content)
|
|
296
287
|
const singleLineBlockComment =
|
|
297
|
-
|
|
288
|
+
inBlockComment && this.isSingleLineBlockComment(content)
|
|
298
289
|
while ((result = codeStyleRegex.exec(content)) !== null) {
|
|
299
290
|
var posInContent = pos + result.index
|
|
300
291
|
var line = this.getLineNumber(posInContent)
|
|
@@ -315,10 +306,10 @@ File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
|
315
306
|
const charsInFront = matchCharsInFront ? matchCharsInFront[0].length : 0
|
|
316
307
|
var hasColon =
|
|
317
308
|
result[3] !== undefined
|
|
318
|
-
const taskStartOnLine =
|
|
309
|
+
const taskStartOnLine = inBlockComment
|
|
319
310
|
? posInContent - charsInFront - linePos
|
|
320
311
|
: pos - linePos
|
|
321
|
-
var task = self.
|
|
312
|
+
var task = self.extractTaskWithDescription({
|
|
322
313
|
taskStartOnLine,
|
|
323
314
|
rawTask,
|
|
324
315
|
config,
|
|
@@ -328,7 +319,7 @@ File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
|
328
319
|
type: Task.Types.CODE,
|
|
329
320
|
hasColon,
|
|
330
321
|
content: self.getContent().substring(linePos),
|
|
331
|
-
|
|
322
|
+
inBlockComment,
|
|
332
323
|
singleLineBlockComment,
|
|
333
324
|
})
|
|
334
325
|
self.addTask(task)
|
|
@@ -338,7 +329,7 @@ File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
|
338
329
|
}
|
|
339
330
|
|
|
340
331
|
File.prototype.extractHashStyleTasks = function (config, pos, content) {
|
|
341
|
-
var hashStyleRegex = new XRegExp(
|
|
332
|
+
var hashStyleRegex = new XRegExp(HASH_STYLE_REGEX)
|
|
342
333
|
var lang = this.getLang()
|
|
343
334
|
var result
|
|
344
335
|
while ((result = hashStyleRegex.exec(content)) !== null) {
|
|
@@ -355,7 +346,7 @@ File.prototype.extractHashStyleTasks = function (config, pos, content) {
|
|
|
355
346
|
const blockEndPos = text.indexOf(lang.block.end)
|
|
356
347
|
if (blockEndPos > -1) text = text.substring(0, blockEndPos)
|
|
357
348
|
}
|
|
358
|
-
var task = this.
|
|
349
|
+
var task = this.extractTaskWithDescription({
|
|
359
350
|
config,
|
|
360
351
|
list,
|
|
361
352
|
text,
|
|
@@ -375,7 +366,7 @@ File.prototype.extractHashStyleTasks = function (config, pos, content) {
|
|
|
375
366
|
|
|
376
367
|
File.prototype.extractLinkStyleTasks = function (config, pos, content) {
|
|
377
368
|
var self = this
|
|
378
|
-
var linkStyleRegex = new XRegExp(
|
|
369
|
+
var linkStyleRegex = new XRegExp(LINK_STYLE_REGEX)
|
|
379
370
|
var result
|
|
380
371
|
while ((result = linkStyleRegex.exec(content)) !== null) {
|
|
381
372
|
let [match, before, rawTask, text, list, colon, order] = result
|
|
@@ -390,7 +381,7 @@ File.prototype.extractLinkStyleTasks = function (config, pos, content) {
|
|
|
390
381
|
log(result)
|
|
391
382
|
log('list:%s text:%s order:%d line:%d', list, text, order, line)
|
|
392
383
|
|
|
393
|
-
var task = self.
|
|
384
|
+
var task = self.extractTaskWithDescription({
|
|
394
385
|
config,
|
|
395
386
|
list,
|
|
396
387
|
text,
|
|
@@ -431,7 +422,7 @@ File.prototype.extractCheckboxTasks = function (config, pos, content) {
|
|
|
431
422
|
const posInContent = result.index + pos + beforeLength
|
|
432
423
|
var line = this.getLineNumber(posInContent)
|
|
433
424
|
if (hasTaskAtLine(line)) continue
|
|
434
|
-
var task = this.
|
|
425
|
+
var task = this.extractTaskWithDescription({
|
|
435
426
|
config,
|
|
436
427
|
list,
|
|
437
428
|
text,
|
|
@@ -620,7 +611,7 @@ File.prototype.deleteTask = function (task, config) {
|
|
|
620
611
|
config
|
|
621
612
|
)
|
|
622
613
|
} else if (task.type === Task.Types.HASHTAG) {
|
|
623
|
-
this.deleteCodeOrHashTask(
|
|
614
|
+
this.deleteCodeOrHashTask(HASH_STYLE_REGEX, task, config)
|
|
624
615
|
}
|
|
625
616
|
} else {
|
|
626
617
|
const fileContent = this.getContentLines()
|
|
@@ -661,22 +652,7 @@ File.prototype.modifyTask = function (task, config, noEmit) {
|
|
|
661
652
|
} else {
|
|
662
653
|
task.updateContent()
|
|
663
654
|
const fileContent = this.getContentLines()
|
|
664
|
-
let description = task.description.slice()
|
|
665
|
-
task.preserveBlankLines = hasBlankLines(description.join(eol.auto))
|
|
666
|
-
if (task.preserveBlankLines) {
|
|
667
|
-
description.unshift('<card>')
|
|
668
|
-
description.push('</card>')
|
|
669
|
-
}
|
|
670
655
|
let text = File.TASK_TYPE_TEMPLATES[task.type](task, config)
|
|
671
|
-
description = Task.padDescription(description, task.beforeText)
|
|
672
|
-
if (
|
|
673
|
-
description &&
|
|
674
|
-
description.length > 0 &&
|
|
675
|
-
description[description.length - 1].trim() === ''
|
|
676
|
-
) {
|
|
677
|
-
description.pop()
|
|
678
|
-
}
|
|
679
|
-
|
|
680
656
|
const linesBefore = fileContent.slice(0, task.line - 1)
|
|
681
657
|
if (this.isCodeFile()) {
|
|
682
658
|
const linesBeforeLength = linesBefore.join(lineEnd).length
|
|
@@ -686,19 +662,19 @@ File.prototype.modifyTask = function (task, config, noEmit) {
|
|
|
686
662
|
}
|
|
687
663
|
}
|
|
688
664
|
|
|
689
|
-
const linesAfter = fileContent.slice(task.
|
|
690
|
-
|
|
665
|
+
const linesAfter = fileContent.slice(task.line)
|
|
691
666
|
const newContent = [
|
|
692
667
|
...linesBefore,
|
|
693
668
|
text,
|
|
694
|
-
...description,
|
|
695
669
|
...linesAfter,
|
|
696
670
|
].join(lineEnd)
|
|
697
671
|
|
|
698
|
-
|
|
672
|
+
const contentBeforeUpdate = this.getContent()
|
|
699
673
|
this.setContent(newContent)
|
|
674
|
+
this.modifyDescription(task, config)
|
|
675
|
+
if (contentBeforeUpdate.trim() == this.getContent().trim()) return
|
|
676
|
+
|
|
700
677
|
this.setModified(true)
|
|
701
|
-
task.lastLine = task.line + description.length
|
|
702
678
|
if (!noEmit) {
|
|
703
679
|
this.emit('task.modified', task)
|
|
704
680
|
this.emit('file.modified', this)
|
|
@@ -737,7 +713,7 @@ File.prototype.modifyCodeOrHashTask = function (re, task, config, noEmit) {
|
|
|
737
713
|
if (index < 0) index = 0
|
|
738
714
|
var beforeContent = this.getContent().substring(0, pos)
|
|
739
715
|
var afterContent = this.getContent().substring(re.lastIndex)
|
|
740
|
-
if (task.
|
|
716
|
+
if (task.inBlockComment) {
|
|
741
717
|
var blockEnd = text.indexOf(lang.block.end)
|
|
742
718
|
if (blockEnd > -1) {
|
|
743
719
|
const desc =
|
|
@@ -797,25 +773,44 @@ File.prototype.modifyTaskFromHtml = function (task, html) {
|
|
|
797
773
|
}
|
|
798
774
|
|
|
799
775
|
File.prototype.modifyTaskFromContent = function (task, content, config) {
|
|
776
|
+
content = trimBlankLines(content, this.isCodeFile())
|
|
800
777
|
task.updateFromContent(content)
|
|
801
778
|
this.modifyTask(task, config)
|
|
802
779
|
}
|
|
803
780
|
|
|
781
|
+
const trimBlankLines = File.trimBlankLines = function (content, isCodeFile) {
|
|
782
|
+
if (isCodeFile) {
|
|
783
|
+
// replace all occurrences of two empty lines in a row with a single empty line
|
|
784
|
+
return content.replace(/\n\s*\n/g, '\n')
|
|
785
|
+
}
|
|
786
|
+
return content.replace(/\n\s*\n\s*\n/g, '\n\n')
|
|
787
|
+
}
|
|
788
|
+
|
|
804
789
|
File.prototype.modifyDescription = function (task, config) {
|
|
805
|
-
//
|
|
806
|
-
|
|
790
|
+
// DOING This should be split out by type (code, markdown, etc)
|
|
791
|
+
// #urgent
|
|
792
|
+
// <!-- order:-10 -->
|
|
807
793
|
const taskStart = this.getLinePos(task.line)
|
|
794
|
+
const descStart = this.getLinePos(task.line + 1)
|
|
808
795
|
const contentWithTask = this.getContent().substring(taskStart)
|
|
809
|
-
let {
|
|
810
|
-
|
|
796
|
+
let {
|
|
797
|
+
rawTaskContentLines,
|
|
798
|
+
taskContentLines,
|
|
799
|
+
isWrappedWithCardTag,
|
|
800
|
+
trailingBlankLines
|
|
801
|
+
} =
|
|
802
|
+
this.getTaskContent({
|
|
811
803
|
config,
|
|
812
804
|
content: contentWithTask,
|
|
813
|
-
|
|
805
|
+
inBlockComment: task.inBlockComment,
|
|
814
806
|
beforeText: task.beforeText,
|
|
815
807
|
})
|
|
808
|
+
|
|
816
809
|
let beforeDescContent = this.getContent().substring(0, descStart)
|
|
810
|
+
|
|
817
811
|
if (descStart === this.getContent().length && !beforeDescContent.endsWith(lf))
|
|
818
812
|
beforeDescContent += lineEnd
|
|
813
|
+
|
|
819
814
|
let content = this.getContent().substring(descStart)
|
|
820
815
|
let spaces = ''
|
|
821
816
|
if (this.isCodeFile() && !this.isMarkDownFile()) {
|
|
@@ -830,32 +825,34 @@ File.prototype.modifyDescription = function (task, config) {
|
|
|
830
825
|
)
|
|
831
826
|
// BACKLOG:-30 Allow this to pad for code files as well
|
|
832
827
|
description = File.padDescription(description, task.beforeText).join(lineEnd)
|
|
833
|
-
if (
|
|
828
|
+
if (taskContentLines.length === 0 && description.length > 0) {
|
|
834
829
|
beforeDescContent += description + lineEnd
|
|
835
830
|
} else {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (hasCardEndTag) rawDescription.push(END_TAG)
|
|
839
|
-
}
|
|
840
|
-
rawDescription = rawDescription.join(lineEnd)
|
|
831
|
+
let rawTaskContent = rawTaskContentLines.join(lineEnd)
|
|
832
|
+
// Add card terminator to description
|
|
841
833
|
if (
|
|
842
834
|
this.isMarkDownFile() &&
|
|
843
835
|
description.length > 0 &&
|
|
844
|
-
(
|
|
836
|
+
(hasBlankLines(description) || isWrappedWithCardTag || trailingBlankLines)
|
|
845
837
|
) {
|
|
846
|
-
task.
|
|
847
|
-
|
|
838
|
+
task.isWrappedWithCardTag = isWrappedWithCardTag // DOING Find out where this is used and stop using it
|
|
839
|
+
// <!-- order:-30 -->
|
|
840
|
+
const blankLinesToAdd = isWrappedWithCardTag ? 2 : trailingBlankLines
|
|
841
|
+
description = `${description}${lineEnd.repeat(blankLinesToAdd)}`
|
|
848
842
|
}
|
|
843
|
+
|
|
844
|
+
// Handle code file tasks
|
|
849
845
|
if (
|
|
850
846
|
task.singleLineBlockComment &&
|
|
851
|
-
|
|
847
|
+
rawTaskContent.length > 0 &&
|
|
852
848
|
description.length === 0
|
|
853
849
|
) {
|
|
854
|
-
|
|
850
|
+
description = lineEnd + description
|
|
855
851
|
}
|
|
856
|
-
content = content.replace(
|
|
852
|
+
content = content.replace(rawTaskContent, description)
|
|
857
853
|
}
|
|
858
854
|
|
|
855
|
+
task.lastLine = task.line + description.split(lineEnd).length
|
|
859
856
|
this.setContent(beforeDescContent + content)
|
|
860
857
|
}
|
|
861
858
|
|
|
@@ -863,7 +860,7 @@ File.padDescription = function (description = [], beforeText = '') {
|
|
|
863
860
|
return Task.padDescription(description, beforeText)
|
|
864
861
|
}
|
|
865
862
|
|
|
866
|
-
File.prototype.
|
|
863
|
+
File.prototype.extractTaskWithDescription = function ({
|
|
867
864
|
taskStartOnLine,
|
|
868
865
|
rawTask,
|
|
869
866
|
config,
|
|
@@ -874,7 +871,7 @@ File.prototype.newTask = function ({
|
|
|
874
871
|
type,
|
|
875
872
|
hasColon,
|
|
876
873
|
content,
|
|
877
|
-
|
|
874
|
+
inBlockComment,
|
|
878
875
|
singleLineBlockComment,
|
|
879
876
|
pos,
|
|
880
877
|
}) {
|
|
@@ -883,20 +880,20 @@ File.prototype.newTask = function ({
|
|
|
883
880
|
let description = []
|
|
884
881
|
// BACKLOG:-230 ## add beforeText for code files
|
|
885
882
|
const beforeText = this.getBeforeText(line, pos)
|
|
886
|
-
let {
|
|
883
|
+
let { rawTaskContentLines, taskContentLines, isWrappedWithCardTag } = this.getTaskContent({
|
|
887
884
|
config,
|
|
888
885
|
content,
|
|
889
|
-
|
|
886
|
+
inBlockComment,
|
|
890
887
|
beforeText,
|
|
891
888
|
})
|
|
892
889
|
|
|
893
890
|
if (!singleLineBlockComment) {
|
|
894
891
|
description = this.isCodeFile()
|
|
895
|
-
?
|
|
896
|
-
: _clone(
|
|
892
|
+
? taskContentLines.map((line) => this.trimCommentChars(line))
|
|
893
|
+
: _clone(taskContentLines)
|
|
897
894
|
}
|
|
898
895
|
description = Task.trimDescription(description, beforeText)
|
|
899
|
-
const descriptionStartsWith = this.getDescriptionChars(
|
|
896
|
+
const descriptionStartsWith = this.getDescriptionChars(inBlockComment)
|
|
900
897
|
const frontMatter = this.frontMatter
|
|
901
898
|
text = this.trimCommentBlockEnd(text)
|
|
902
899
|
let commentStartOnLine =
|
|
@@ -908,12 +905,12 @@ File.prototype.newTask = function ({
|
|
|
908
905
|
{
|
|
909
906
|
pos,
|
|
910
907
|
frontMatter,
|
|
911
|
-
|
|
908
|
+
inBlockComment,
|
|
912
909
|
singleLineBlockComment,
|
|
913
910
|
rawTask,
|
|
914
911
|
text,
|
|
915
912
|
list,
|
|
916
|
-
|
|
913
|
+
rawTaskContentLines,
|
|
917
914
|
description,
|
|
918
915
|
descriptionStartsWith,
|
|
919
916
|
taskStartOnLine,
|
|
@@ -926,7 +923,7 @@ File.prototype.newTask = function ({
|
|
|
926
923
|
source: self.getSource(),
|
|
927
924
|
type,
|
|
928
925
|
beforeText,
|
|
929
|
-
|
|
926
|
+
isWrappedWithCardTag,
|
|
930
927
|
},
|
|
931
928
|
this.project
|
|
932
929
|
)
|
|
@@ -1098,8 +1095,8 @@ File.prototype.ignoreCodeBlocks = function () {
|
|
|
1098
1095
|
// BACKLOG:-100 This is not used, but we need a way to ignore tasks in code blocks. Maybe save position ranges
|
|
1099
1096
|
var cleanContent = this.content
|
|
1100
1097
|
var replacer = function (block) {
|
|
1101
|
-
block = block.replace(new XRegExp(
|
|
1102
|
-
block = block.replace(new XRegExp(
|
|
1098
|
+
block = block.replace(new XRegExp(LINK_STYLE_REGEX), '**TASK**')
|
|
1099
|
+
block = block.replace(new XRegExp(HASH_STYLE_REGEX), '**TASK**')
|
|
1103
1100
|
return block
|
|
1104
1101
|
}
|
|
1105
1102
|
|
|
@@ -1135,105 +1132,31 @@ File.prototype.getBeforeText = function (line, pos) {
|
|
|
1135
1132
|
: this.content.substring(this.getLinePos(line), pos)
|
|
1136
1133
|
}
|
|
1137
1134
|
|
|
1138
|
-
File.prototype.getDescriptionChars = function (
|
|
1135
|
+
File.prototype.getDescriptionChars = function (inBlockComment) {
|
|
1139
1136
|
if (!this.isCodeFile()) return ''
|
|
1140
1137
|
const lang = this.getLang()
|
|
1141
|
-
if (
|
|
1138
|
+
if (inBlockComment && lang.block && lang.block.ignore) return lang.block.ignore
|
|
1142
1139
|
return lang.symbol
|
|
1143
1140
|
}
|
|
1144
1141
|
|
|
1145
|
-
File.prototype.getRawDescription = function ({
|
|
1146
|
-
config,
|
|
1147
|
-
content,
|
|
1148
|
-
inCodeBlock,
|
|
1149
|
-
beforeText,
|
|
1150
|
-
}) {
|
|
1151
|
-
const { rawDescription, preserveBlankLines, hasCardEndTag } =
|
|
1152
|
-
this.getTaskContent({ config, content, inCodeBlock, beforeText })
|
|
1153
|
-
const blockEnd = this.getLang().block && this.getLang().block.end
|
|
1154
|
-
if (
|
|
1155
|
-
this.isCodeFile() &&
|
|
1156
|
-
blockEnd &&
|
|
1157
|
-
rawDescription.length > 0 &&
|
|
1158
|
-
rawDescription[rawDescription.length - 1].indexOf(blockEnd) > -1
|
|
1159
|
-
)
|
|
1160
|
-
rawDescription.pop()
|
|
1161
|
-
return { rawDescription, preserveBlankLines, hasCardEndTag }
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
File.getContentLines = function (content) {
|
|
1165
|
-
let foundContent = false
|
|
1166
|
-
return eol.split(content).filter((line) => {
|
|
1167
|
-
const hasContent = line.trim()
|
|
1168
|
-
if (!foundContent && hasContent) foundContent = true
|
|
1169
|
-
return foundContent || hasContent
|
|
1170
|
-
})
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
1142
|
File.prototype.getTaskContent = function ({
|
|
1174
1143
|
config,
|
|
1175
1144
|
content,
|
|
1176
|
-
|
|
1145
|
+
inBlockComment,
|
|
1177
1146
|
beforeText,
|
|
1178
1147
|
}) {
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
const preserveBlankLines =
|
|
1182
|
-
this.isMarkDownFile() && lines.length > 1 && lines[1].trim() === START_TAG
|
|
1148
|
+
const isMarkDownFile = this.isMarkDownFile()
|
|
1149
|
+
const isCodeFile = this.isCodeFile()
|
|
1183
1150
|
const lang = this.getLang()
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
if (this.isMarkDownFile() && line.trim().indexOf('-->') > -1)
|
|
1194
|
-
closeBlockTokens++
|
|
1195
|
-
|
|
1196
|
-
if (preserveBlankLines) {
|
|
1197
|
-
if (line.trim() === END_TAG) {
|
|
1198
|
-
hasCardEndTag = true
|
|
1199
|
-
break
|
|
1200
|
-
}
|
|
1201
|
-
} else {
|
|
1202
|
-
if (
|
|
1203
|
-
this.isMarkDownFile() &&
|
|
1204
|
-
beforeText &&
|
|
1205
|
-
/^\s*(\*|-|\d+\.|[a-zA-Z]\.)\s$/.test(beforeText) &&
|
|
1206
|
-
line.search(/\S/) <= beforeText.search(/\S/)
|
|
1207
|
-
)
|
|
1208
|
-
break
|
|
1209
|
-
if (
|
|
1210
|
-
(this.isCodeFile() &&
|
|
1211
|
-
!inCodeBlock &&
|
|
1212
|
-
(line.indexOf(lang.symbol) < 0 ||
|
|
1213
|
-
line.trim().indexOf(lang.symbol) > 0)) ||
|
|
1214
|
-
line.trim() === lang.symbol
|
|
1215
|
-
)
|
|
1216
|
-
break
|
|
1217
|
-
if (this.isCodeFile() && lang.block && line.trim() === lang.block.end)
|
|
1218
|
-
break
|
|
1219
|
-
if (this.isCodeFile() && lang.block && line.trim() === lang.block.ignore)
|
|
1220
|
-
break
|
|
1221
|
-
if (!this.isCodeFile() && line.trim() === '') break
|
|
1222
|
-
}
|
|
1223
|
-
if (this.hasTaskInText(config, line)) break
|
|
1224
|
-
|
|
1225
|
-
if (line && lang.block) line = line.replace(lang.block.end, '')
|
|
1226
|
-
|
|
1227
|
-
if (preserveBlankLines || line.trim() !== '') commentLines.push(line)
|
|
1228
|
-
}
|
|
1229
|
-
if (
|
|
1230
|
-
this.isMarkDownFile() &&
|
|
1231
|
-
commentLines.length &&
|
|
1232
|
-
commentLines[commentLines.length - 1].trim() === '-->' &&
|
|
1233
|
-
openBlockTokens !== closeBlockTokens
|
|
1234
|
-
)
|
|
1235
|
-
commentLines.pop()
|
|
1236
|
-
return { rawDescription: commentLines, preserveBlankLines, hasCardEndTag }
|
|
1151
|
+
const fileType = isMarkDownFile ? FILE_TYPES.MARKDOWN : isCodeFile ? FILE_TYPES.CODE : undefined
|
|
1152
|
+
return getTaskContent({
|
|
1153
|
+
config,
|
|
1154
|
+
content,
|
|
1155
|
+
inBlockComment,
|
|
1156
|
+
beforeText,
|
|
1157
|
+
lang,
|
|
1158
|
+
fileType,
|
|
1159
|
+
})
|
|
1237
1160
|
}
|
|
1238
1161
|
|
|
1239
1162
|
File.prototype.isCheckBoxTask = function (config, line, beforeText) {
|
|
@@ -1322,9 +1245,9 @@ File.prototype.hasCodeStyleTask = function (config, text) {
|
|
|
1322
1245
|
File.prototype.hasTaskInText = function (config, text) {
|
|
1323
1246
|
return (
|
|
1324
1247
|
this.hasCodeStyleTask(config, text) ||
|
|
1325
|
-
((new XRegExp(
|
|
1326
|
-
new XRegExp(
|
|
1327
|
-
new XRegExp(
|
|
1248
|
+
((new XRegExp(HASH_STYLE_REGEX).test(text) ||
|
|
1249
|
+
new XRegExp(HASH_STYLE_META_ORDER_REGEX).test(text) ||
|
|
1250
|
+
new XRegExp(LINK_STYLE_REGEX).test(text)) &&
|
|
1328
1251
|
config.lists.find((list) => text.includes(`#${list.name}`)))
|
|
1329
1252
|
)
|
|
1330
1253
|
}
|
package/lib/repository.js
CHANGED
|
@@ -1048,10 +1048,6 @@ Repository.prototype.appendTask = function({file, content, list}, cb) {
|
|
|
1048
1048
|
}).trimEnd()
|
|
1049
1049
|
const lines = eol.split(content)
|
|
1050
1050
|
const text = lines[0]
|
|
1051
|
-
if (tools.hasBlankLines(lines.join(eol.auto))) {
|
|
1052
|
-
lines.splice(1, 0, File.START_TAG)
|
|
1053
|
-
lines.push(File.END_TAG)
|
|
1054
|
-
}
|
|
1055
1051
|
const taskSyntax = config.getNewCardSyntax()
|
|
1056
1052
|
const taskPrefix = interpretedTaskPrefix ? `${interpretedTaskPrefix} ` : ''
|
|
1057
1053
|
let fileContent = file.getContent()
|
|
@@ -1092,7 +1088,8 @@ Repository.prototype.appendTask = function({file, content, list}, cb) {
|
|
|
1092
1088
|
const inLineOrder = config.orderMeta ? '' : `:${orderString}`
|
|
1093
1089
|
appendContent = `${taskPrefix}#${list}${inLineOrder} ${text}${eol.lf}${taskContent}`
|
|
1094
1090
|
}
|
|
1095
|
-
|
|
1091
|
+
appendContent = File.trimBlankLines(appendContent)
|
|
1092
|
+
file.setContent(`${fileContent}${sep}${appendContent}${"\n".repeat(2)}`)
|
|
1096
1093
|
this.writeAndExtract(file, true, cb)
|
|
1097
1094
|
}
|
|
1098
1095
|
|
package/lib/task.js
CHANGED
|
@@ -45,15 +45,15 @@ class Task {
|
|
|
45
45
|
this.context = obj.context || []
|
|
46
46
|
this.meta = obj.meta || {}
|
|
47
47
|
this.metaIndex = obj.metaIndex || {}
|
|
48
|
-
this.
|
|
48
|
+
this.inBlockComment = obj.inBlockComment
|
|
49
49
|
this.singleLineBlockComment = obj.singleLineBlockComment
|
|
50
50
|
this.description = obj.description || []
|
|
51
|
-
this.
|
|
51
|
+
this.rawTaskContentLines = obj.rawTaskContentLines || []
|
|
52
52
|
this.taskStartOnLine = obj.taskStartOnLine || 0
|
|
53
53
|
this.commentStartOnLine = obj.commentStartOnLine || 0
|
|
54
54
|
this.descriptionStartsWith = obj.descriptionStartsWith
|
|
55
55
|
this.filteredListName = obj.filteredListName
|
|
56
|
-
this.
|
|
56
|
+
this.isWrappedWithCardTag = obj.isWrappedWithCardTag
|
|
57
57
|
this.config = config
|
|
58
58
|
this.allMeta = obj.allMeta || {}
|
|
59
59
|
this.allContext = obj.allContext || []
|
|
@@ -80,8 +80,7 @@ class Task {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
getLastLine() {
|
|
83
|
-
let lastLine = this.line + this.
|
|
84
|
-
if (this.preserveBlankLines) lastLine += 2
|
|
83
|
+
let lastLine = this.line + this.rawTaskContentLines.length
|
|
85
84
|
return lastLine
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -178,6 +177,8 @@ class Task {
|
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
updateFromContent(content) {
|
|
180
|
+
// replace all occurrences of two empty lines in a row with a single empty line
|
|
181
|
+
content = content.replace(/\n\s*\n\s*\n/g, '\n\n')
|
|
181
182
|
let lines = eol.split(content)
|
|
182
183
|
this.text = lines.shift().trim()
|
|
183
184
|
if (lines && lines.length > 1 && lines[lines.length - 1].trim() === '') lines.pop()
|
|
@@ -223,16 +224,16 @@ Task.padDescription = function (description = [], beforeText = '') {
|
|
|
223
224
|
return description
|
|
224
225
|
}
|
|
225
226
|
|
|
226
|
-
Task.trimDescription = function (
|
|
227
|
+
Task.trimDescription = function (rawTaskContentLines, beforeText) {
|
|
227
228
|
const checkedData = Task.getCheckedData(beforeText)
|
|
228
229
|
if (checkedData) {
|
|
229
|
-
|
|
230
|
+
rawTaskContentLines = rawTaskContentLines.map((line) => {
|
|
230
231
|
const lineLength = line.length
|
|
231
232
|
line = line.trimStart()
|
|
232
233
|
return line.padStart(lineLength - checkedData.pad)
|
|
233
234
|
})
|
|
234
235
|
}
|
|
235
|
-
return
|
|
236
|
+
return rawTaskContentLines
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
Task.prototype.getCheckedData = function () {
|