imdone-core 1.54.9 → 2.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.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/lib/cli.js +114 -0
  3. package/lib/index.js +23 -0
  4. package/package.json +48 -66
  5. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
  6. package/.github/workflows/ci.yml +0 -24
  7. package/.imdoneignore +0 -12
  8. package/.mocharc.json +0 -10
  9. package/.prettierrc +0 -6
  10. package/CHANGELOG.md +0 -160
  11. package/_navbar.md +0 -1
  12. package/cli.js +0 -74
  13. package/index-markdown.js +0 -55
  14. package/index.html +0 -184
  15. package/index.js +0 -1
  16. package/lib/ProjectContext.js +0 -42
  17. package/lib/adapters/date-time.js +0 -20
  18. package/lib/adapters/file-gateway.js +0 -127
  19. package/lib/adapters/git-download.js +0 -35
  20. package/lib/adapters/markdown.js +0 -168
  21. package/lib/adapters/parsers/DateLanguageParser.js +0 -10
  22. package/lib/adapters/parsers/content-transformer.js +0 -111
  23. package/lib/adapters/parsers/file/LocalFileParserFactory.js +0 -37
  24. package/lib/adapters/parsers/file/ReadStreamCodeParser.js +0 -126
  25. package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +0 -17
  26. package/lib/adapters/parsers/file/ReadStreamTaskParser.js +0 -104
  27. package/lib/adapters/parsers/function-parser.js +0 -29
  28. package/lib/adapters/parsers/task/CardContentParser.js +0 -376
  29. package/lib/adapters/parsers/task/CheckStyleTaskParser.js +0 -28
  30. package/lib/adapters/parsers/task/CodeStyleTaskParser.js +0 -24
  31. package/lib/adapters/parsers/task/HashStyleTaskParser.js +0 -27
  32. package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +0 -24
  33. package/lib/adapters/parsers/task/TaskParser.js +0 -38
  34. package/lib/adapters/readers/FileSystemProjectReader.js +0 -45
  35. package/lib/adapters/storage/config.js +0 -49
  36. package/lib/adapters/storage/tags.js +0 -30
  37. package/lib/adapters/yaml.js +0 -6
  38. package/lib/card.js +0 -300
  39. package/lib/config.js +0 -214
  40. package/lib/constants.js +0 -97
  41. package/lib/context/ApplicationContext.js +0 -32
  42. package/lib/context/StreamingApplicationContext.js +0 -31
  43. package/lib/controlers/SaveViewController.js +0 -10
  44. package/lib/default-settings.js +0 -37
  45. package/lib/domain/entities/FileProjectContext.js +0 -10
  46. package/lib/domain/entities/View.js +0 -30
  47. package/lib/domain/usecases/GetViewsUsecase.js +0 -50
  48. package/lib/domain/usecases/OpenProjectUsecase.js +0 -13
  49. package/lib/domain/usecases/SaveViewUsecase.js +0 -57
  50. package/lib/file.js +0 -1395
  51. package/lib/languages.js +0 -114
  52. package/lib/list.js +0 -105
  53. package/lib/migrate-config.js +0 -165
  54. package/lib/mixins/repo-fs-store.js +0 -424
  55. package/lib/mixins/repo-watched-fs-store.js +0 -195
  56. package/lib/plugins/archive-plugin.js +0 -70
  57. package/lib/plugins/default-board-actions-plugin.js +0 -56
  58. package/lib/plugins/default-board-properties-plugin.js +0 -101
  59. package/lib/plugins/epic-plugin.js +0 -176
  60. package/lib/plugins/extension-plugin.js +0 -87
  61. package/lib/plugins/persist-tags-plugin.js +0 -13
  62. package/lib/plugins/plugin-manager.js +0 -421
  63. package/lib/plugins/plugin-registry.js +0 -61
  64. package/lib/project-factory.js +0 -45
  65. package/lib/project.js +0 -911
  66. package/lib/repository.js +0 -1746
  67. package/lib/task.js +0 -798
  68. package/lib/tools.js +0 -250
  69. package/lib/usecases/get-imdone-url.js +0 -6
  70. package/lib/usecases/get-project-tags.js +0 -16
  71. package/lib/usecases/get-tasks-in-file.js +0 -29
package/lib/file.js DELETED
@@ -1,1395 +0,0 @@
1
- 'use strict'
2
-
3
- var _clone = require('lodash.clone'),
4
- _isObject = require('lodash.isobject'),
5
- _assign = require('lodash.assign'),
6
- _omit = require('lodash.omit'),
7
- _get = require('lodash.get'),
8
- crypto = require('crypto'),
9
- events = require('events'),
10
- util = require('util'),
11
- path = require('path'),
12
- matter = require('gray-matter'),
13
- eol = require('eol'),
14
- lf = String(eol.lf),
15
- lineEnd = String(eol.auto),
16
- tools = require('./tools'),
17
- log = require('debug')('imdone-core:File'),
18
- chrono = require('chrono-node'),
19
- Task = require('./task'),
20
- newCard = require('./card'),
21
- fastSort = require('fast-sort/dist/sort'),
22
- XRegExp = require('xregexp'),
23
- { getIsoDateWithOffset } = require('./adapters/date-time'),
24
- {
25
- FILE_TYPES,
26
- getTaskContent,
27
- getRawTask,
28
- getHashStyleRegex,
29
- hasTaskInText,
30
- LIST_NAME_PATTERN,
31
- LINK_STYLE_REGEX,
32
- } = require('./adapters/parsers/task/CardContentParser')
33
-
34
- const appContext = require('./context/ApplicationContext')
35
- const { hasBlankLines } = require('./tools')
36
-
37
- const ERRORS = {
38
- NOT_A_TASK: 'task must be of type Task',
39
- }
40
- const escapeRegExp = tools.escapeRegExp
41
-
42
- /**
43
- * Description
44
- * @method File
45
- * @param {} repoId
46
- * @param {} filePath
47
- * @param {} content
48
- * @param {} modifiedTime
49
- * @return
50
- *
51
- * */
52
- function File(opts) {
53
- events.EventEmitter.call(this)
54
- this.project = opts.project
55
- this.projectContext = appContext().projectContext
56
- if (_isObject(opts.file) && _isObject(opts.config)) {
57
- _assign(this, opts.file)
58
- this.tasks.forEach((task) => {
59
- task = newCard(task, this.project)
60
- })
61
- } else {
62
- this.repoId = opts.repoId
63
- this.path = opts.filePath
64
- this.content = opts.content
65
- this.modifiedTime = opts.modifiedTime
66
- this.createdTime = opts.createdTime
67
- this.languages = opts.languages || require('./languages')
68
- this.modified = false
69
- this.frontMatter = {
70
- tags: [],
71
- context: [],
72
- meta: {},
73
- }
74
- this.tasks = []
75
- this.isDir = false
76
- this.lineCount = 0
77
- this.deleted = false
78
- }
79
- }
80
- util.inherits(File, events.EventEmitter)
81
-
82
- const CODE_STYLE_END = '((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
83
- const CODE_STYLE_PATTERN = (File.CODE_STYLE_PATTERN =
84
- '([A-Z]+[A-Z-_]+?)' + CODE_STYLE_END)
85
-
86
- File.isListNameValid = function(listName) {
87
- return new XRegExp(LIST_NAME_PATTERN).test(listName)
88
- }
89
-
90
- const CHECK_STYLE_PATTERN = (File.CHECK_STYLE_PATTERN =
91
- /^(\s*- \[([x ])\]\s)(.+$)/gm)
92
-
93
- var getTaskId = (File.getTaskId = function (path, line, text) {
94
- var shasum = crypto.createHash('sha1')
95
- const taskElements = { path, line, text: text.trim() }
96
- shasum.update(JSON.stringify(taskElements))
97
- return shasum.digest('hex')
98
- })
99
-
100
- function getDoneList(config) {
101
- return config.getDoneList()
102
- }
103
-
104
- function getDoingList(config) {
105
- return config.getDoingList()
106
- }
107
-
108
- function getFrontMeta(meta) {
109
- const metadata = {}
110
- Object.entries(meta).forEach(([prop, val]) => {
111
- if (Array.isArray(val)) {
112
- val.forEach((item) => {
113
- if (_isObject(item) || Array.isArray(item)) return
114
- if (!metadata[prop]) metadata[prop] = []
115
- metadata[prop].push(`${item}`)
116
- })
117
- }
118
- })
119
- return metadata
120
- }
121
-
122
- File.getUnixContent = function (content) {
123
- return content.replace(/\r\n|\r/g, '\n')
124
- }
125
-
126
- File.isMetaNewLine = function (config) {
127
- return config.isMetaNewLine()
128
- }
129
-
130
- File.isAddCompletedMeta = function (config) {
131
- return config.isAddCompletedMeta()
132
- }
133
-
134
- File.isFile = function (file) {
135
- return file instanceof File
136
- }
137
-
138
- File.isMarkdown = function (relPath) {
139
- return /\.md$/i.test(relPath) || path.extname(relPath) === ''
140
- }
141
-
142
- File.prototype.removeCompleted = function ({ task, content, config }) {
143
- const doneList = getDoneList(config)
144
- if (doneList === task.list) return content
145
- if (
146
- task.meta.completed &&
147
- task.meta.completed.length > 0
148
- ) {
149
- content = this.removeMetaData(
150
- content,
151
- 'completed',
152
- task.meta.completed[0]
153
- )
154
- }
155
- return content
156
- }
157
-
158
- File.addStarted = function ({ task, content, config }) {
159
- if (!task || !config || !config.settings) return content
160
- if (!config.settings.cards.addStartedMeta) return content
161
- if (task.meta.started) return content
162
- if (task.list !== getDoingList(config)) return content
163
- const now = getIsoDateWithOffset()
164
- content = task.addToLastCommentInContent(
165
- content,
166
- `started${config.getMetaSep()}${now}`,
167
- File.isMetaNewLine(config)
168
- )
169
- const lines = eol.split(content)
170
- return lines.join(lineEnd)
171
- }
172
-
173
- File.addCompleted = function ({ task, content, config }) {
174
- const doneList = getDoneList(config)
175
- if (
176
- !task ||
177
- !config ||
178
- !config.settings ||
179
- !doneList ||
180
- !this.isAddCompletedMeta(config)
181
- )
182
- return content
183
- if (task.list !== doneList) return content
184
- if (task.completed || task.meta.completed) return content
185
- if (content.includes(`completed${config.getMetaSep()}`)) return content
186
- const now = getIsoDateWithOffset()
187
- content = task.addToLastCommentInContent(
188
- content,
189
- `completed${config.getMetaSep()}${now}`,
190
- File.isMetaNewLine(config)
191
- )
192
- const lines = eol.split(content)
193
- return lines.join(lineEnd)
194
- }
195
-
196
- File.parseDueDate = function (config, text) {
197
- return File.parseDate(config, text, 'due')
198
- }
199
-
200
- File.parseRemindDate = function (config, text) {
201
- return File.parseDate(config, text, 'remind')
202
- }
203
-
204
- File.parseDeferDate = function (config, text) {
205
- return File.parseDate(config, text, 'defer')
206
- }
207
-
208
- File.parseDate = function (config, text, dateType) {
209
- // TODO Add the ability to set multiple reminders
210
- // #imdone-1.54.0
211
- // <!--
212
- // order:-150
213
- // -->
214
- if (text.includes(`${dateType}${config.getMetaSep()}`)) return text
215
- let dateString
216
- return text.replace(new RegExp(`(\\s${dateType})\\s.*?\\.`,'gi'), (match, p1) => {
217
- if (dateString) return match
218
- try {
219
- const date = chrono.parseDate(match, new Date(), {forwardDate: true})
220
- if (!date) return match
221
- dateString = getIsoDateWithOffset(date)
222
- return `${p1.toLowerCase()}${config.getMetaSep()}${dateString}`
223
- } catch (e) {
224
- console.log(`Unable to parse ${dateType} date for ${text}`)
225
- return match
226
- }
227
- })
228
- }
229
-
230
- function trackMarkdownChecklistChanges({task, config, modify}) {
231
- const doneList = config.getDoneList()
232
- let beforeTextModified = false
233
- if (task.beforeText.trim().startsWith('- [ ]')) {
234
- if (task.list === doneList) {
235
- if (modify) {
236
- task.beforeText = task.beforeText.replace('[ ]', '[x]')
237
- beforeTextModified = true
238
- } else {
239
- task.list = config.getDefaultList()
240
- }
241
- }
242
- } else if (task.beforeText.trim().startsWith('- [x]')) {
243
- if (task.list !== doneList) {
244
- if (modify) {
245
- task.beforeText = task.beforeText.replace('[x]', '[ ]')
246
- beforeTextModified = true
247
- } else {
248
- task.list = doneList
249
- }
250
- }
251
- }
252
-
253
- return beforeTextModified
254
- }
255
-
256
- File.prototype.trackListChanges = function ({ task, content, config, modify }) {
257
- const lists = config.lists
258
- .filter((list) => !list.filter)
259
- .map((list) => list.name)
260
-
261
- let beforeTextModified = false
262
- if (this.isMarkDownFile()) {
263
- beforeTextModified = trackMarkdownChecklistChanges({task, config, modify})
264
-
265
- if (
266
- _get(config, 'settings.cards.trackChanges') &&
267
- task.hasListChanged(lists)
268
- ) {
269
- const list = task.list
270
- const now = new Date().toISOString()
271
- content = task.addToLastCommentInContent(
272
- content,
273
- `${list}${config.getMetaSep()}${now}`,
274
- File.isMetaNewLine(config)
275
- )
276
- if (!task.meta[list]) task.meta[list] = []
277
- task.meta[list].push(now)
278
- }
279
- }
280
-
281
- return { content, beforeTextModified }
282
- }
283
-
284
- File.prototype.removeMetaData = function (content, key, value) {
285
- return Task.removeMetaData({
286
- config: this.projectContext.config,
287
- content,
288
- key,
289
- value,
290
- })
291
- }
292
-
293
- File.prototype.transformTask = function ({config, modify, task}) {
294
- let content = task.content
295
-
296
- const trackListChanges = this.trackListChanges({
297
- task,
298
- content,
299
- config,
300
- modify,
301
- })
302
- content = trackListChanges.content
303
- content = File.parseDueDate(config, content)
304
- content = File.parseRemindDate(config, content)
305
- content = File.parseDeferDate(config, content)
306
- content = File.addStarted({ task, content, config })
307
- content = File.addCompleted({ task, content, config })
308
- content = this.removeCompleted({ task, content, config })
309
- content = task.format(content)
310
- if (
311
- content.trim() !== task.content.trim()
312
- || trackListChanges.beforeTextModified
313
- || task.orderModified
314
- ) {
315
- this.modifyTaskFromContent(task, content, config)
316
- }
317
- }
318
-
319
- File.prototype.transformTasks = function (config, modify) {
320
- fastSort(this.getTasks())
321
- .desc(u => u.line)
322
- .forEach(task => this.transformTask({config, modify, task}))
323
- }
324
-
325
- // TODO Blank lines should be allowed in code tasks
326
- // <!--
327
- // #imdone-1.54.0
328
- // #feature
329
- // order:-105
330
- // -->
331
- File.prototype.extractCodeStyleTasks = function (config, pos, content) {
332
- var self = this
333
- var codeStyleRegex = new RegExp(CODE_STYLE_PATTERN, 'gm')
334
- var result
335
- const inBlockComment = this.isInBlockComment(content)
336
- const singleLineBlockComment =
337
- inBlockComment && this.isSingleLineBlockComment(content)
338
- while ((result = codeStyleRegex.exec(content)) !== null) {
339
- var posInContent = pos + result.index
340
- var line = this.getLineNumber(posInContent)
341
- if (self.getTaskAtLine(line)) continue
342
- var charBeforeList = this.getContent().substring(
343
- posInContent - 1,
344
- posInContent
345
- )
346
- if (this.startsWithCommentOrSpace(posInContent) && charBeforeList !== '#') {
347
- var list = result[1]
348
- if (!config.includeList(list)) continue
349
- var rawTask = result[0]
350
- var text = result[5]
351
- const linePos = self.getLinePos(line)
352
- const matchCharsInFront = this.getContent()
353
- .substring(linePos, posInContent)
354
- .match(/\S+\s*/)
355
- const charsInFront = matchCharsInFront ? matchCharsInFront[0].length : 0
356
- var hasColon =
357
- result[3] !== undefined
358
- const taskStartOnLine = inBlockComment
359
- ? posInContent - charsInFront - linePos
360
- : pos - linePos
361
- var task = self.extractTaskWithDescription({
362
- taskStartOnLine,
363
- rawTask,
364
- config,
365
- list,
366
- text,
367
- line,
368
- type: Task.Types.CODE,
369
- hasColon,
370
- content: self.getContent().substring(linePos),
371
- inBlockComment,
372
- singleLineBlockComment,
373
- })
374
- self.addTask(task)
375
- self.emit('task.found', task)
376
- }
377
- }
378
- }
379
-
380
- File.prototype.extractHashStyleTasks = function (config, pos, content) {
381
- var hashStyleRegex = getHashStyleRegex(config.tokenPrefix)
382
- var lang = this.getLang()
383
- const codePositions = this.getMarkdownCodePositions(content);
384
- var result
385
- while ((result = hashStyleRegex.exec(content)) !== null) {
386
- if (this.isResultInMarkdownCode(codePositions, result.index)) continue;
387
- var [rawTask, list, orderGroup, order, text] = result
388
- if (!config.listExists(list)) continue
389
-
390
- const posInContent = result.index + pos
391
- if (this.isWithinCodeSpanOrBlock(result.index, content)) continue
392
-
393
- const line = this.getLineNumber(posInContent)
394
- if (this.getTaskAtLine(line)) continue
395
-
396
- if (lang.block) {
397
- const blockEndPos = text.indexOf(lang.block.end)
398
- if (blockEndPos > -1) text = text.substring(0, blockEndPos)
399
- }
400
- var task = this.extractTaskWithDescription({
401
- config,
402
- list,
403
- text,
404
- order,
405
- line,
406
- type: Task.Types.HASHTAG,
407
- hasColon: orderGroup && orderGroup.startsWith(':'),
408
- content: content.substring(result.index),
409
- pos: posInContent,
410
- })
411
- task.taskStartOnLine = pos - this.getLinePos(line)
412
- task.rawTask = rawTask
413
- this.addTask(task)
414
- this.emit('task.found', task)
415
- }
416
- };
417
-
418
- File.prototype.extractLinkStyleTasks = function (config, pos, content) {
419
- var self = this
420
- var linkStyleRegex = new XRegExp(LINK_STYLE_REGEX)
421
- const codePositions = this.getMarkdownCodePositions(content);
422
- var result
423
- while ((result = linkStyleRegex.exec(content)) !== null) {
424
- if (this.isResultInMarkdownCode(codePositions, result.index)) continue;
425
- let [match, before, rawTask, text, list, colon, order] = result
426
- const beforeLength = before ? before.length : 0
427
- const posInContent = result.index + pos + beforeLength
428
- if (this.isWithinCodeSpanOrBlock(result.index, content)) continue
429
- var line = this.getLineNumber(posInContent)
430
- if (self.getTaskAtLine(line)) continue
431
- if (!config.listExists(list)) continue
432
- log = require('debug')('extractLinkStyleTasks')
433
- log('*********************************************************')
434
- log(result)
435
- log('list:%s text:%s order:%d line:%d', list, text, order, line)
436
-
437
- var task = self.extractTaskWithDescription({
438
- config,
439
- list,
440
- text,
441
- order,
442
- line,
443
- type: Task.Types.MARKDOWN,
444
- hasColon: !!colon,
445
- content: content.substring(result.index),
446
- pos: posInContent,
447
- })
448
- task.rawTask = rawTask
449
- self.addTask(task)
450
- self.emit('task.found', task)
451
- }
452
- }
453
-
454
- File.prototype.extractCheckboxTasks = function (config, pos, content) {
455
- if (!this.isMarkDownFile()) return
456
- if (!config.isAddCheckBoxTasks()) return
457
-
458
- const codePositions = this.getMarkdownCodePositions(content);
459
- const type = Task.Types[config.getNewCardSyntax()]
460
- const checkStyleRegex = new RegExp(CHECK_STYLE_PATTERN)
461
- let result
462
- let tasks = []
463
- const hasTaskAtLine = (line) => {
464
- return (
465
- this.getTaskAtLine(line) ||
466
- tasks.find((task) => {
467
- return line >= task.line && line <= task.lastLine
468
- })
469
- )
470
- }
471
- while ((result = checkStyleRegex.exec(content)) !== null) {
472
- if (this.isResultInMarkdownCode(codePositions, result.index)) continue;
473
- let [match, before, checked, text] = result
474
- const list =
475
- checked.trim() === 'x' ? config.getDoneList() : config.getDefaultList()
476
- const beforeLength = before.length
477
- const posInContent = result.index + pos + beforeLength
478
- var line = this.getLineNumber(posInContent)
479
- if (hasTaskAtLine(line)) continue
480
- var task = this.extractTaskWithDescription({
481
- config,
482
- list,
483
- text,
484
- line,
485
- type,
486
- hasColon: true,
487
- content: content.substring(result.index),
488
- pos: posInContent,
489
- })
490
- task.rawTask = getRawTask({tokenPrefix: config.tokenPrefix, orderMeta: config.orderMeta, list, text, type})
491
- tasks.push(task)
492
- }
493
- if (tasks) {
494
- const hasTaskAtLine = (line) => {
495
- return (
496
- this.getTaskAtLine(line) ||
497
- tasks.find((task) => {
498
- return line > task.line && line <= task.lastLine
499
- })
500
- )
501
- }
502
- tasks = tasks.filter((task) => {
503
- return !hasTaskAtLine(task.line)
504
- })
505
- if (!tasks) return
506
- let newContent = content
507
- tasks.reverse().forEach((task) => {
508
- this.addTask(task)
509
- const beforeTaskContent = content.substring(0, task.pos)
510
- const afterTaskContent = newContent.substring(task.pos + task.text.length)
511
- newContent = beforeTaskContent + task.rawTask + afterTaskContent
512
- this.emit('task.found', task)
513
- })
514
-
515
- if (this.content === newContent || !newContent) return
516
-
517
- this.content = newContent
518
-
519
- this.setModified(true)
520
- console.log('extractCheckboxTasks setModified true for file:', this.path)
521
-
522
- }
523
- }
524
-
525
- File.prototype.extractTasks = function (config) {
526
- this.tasks = []
527
- if (this.isMarkDownFile()) {
528
- const data = this.parseFrontMatter(config)
529
- if (data.imdone_ignore || data['kanban-plugin']) return this
530
- }
531
- if (this.isCodeFile()) {
532
- this.extractTasksInCodeFile(config)
533
- } else {
534
- this.extractTasksInNonCodeFile(config)
535
- }
536
- return this
537
- }
538
-
539
- File.prototype.extractAndTransformTasks = function (config) {
540
- this.extractTasks(config)
541
- this.transformTasks(config)
542
- }
543
-
544
- // TODO Extract functions should be moved to another file
545
- // Only pass the needed config values to the functions
546
- // #imdone-1.54.0
547
- File.prototype.extractTasksInCodeFile = function (config) {
548
- var self = this
549
- var commentRegex = this.getCodeCommentRegex()
550
- var result
551
- while ((result = commentRegex.exec(self.getContent())) !== null) {
552
- var comment = result[0]
553
- var commentStart = result.index
554
- self.extractCodeStyleTasks(config, commentStart, comment)
555
- }
556
- }
557
-
558
- File.prototype.extractTasksInNonCodeFile = function (config) {
559
- this.extractHashStyleTasks(config, 0, this.getContent())
560
- this.extractLinkStyleTasks(config, 0, this.getContent())
561
- this.extractCheckboxTasks(config, 0, this.getContent())
562
- }
563
-
564
- File.prototype.deleteBlankLine = function (lineNo) {
565
- var startOfLine = this.getLinePos(lineNo)
566
- if (this.isMarkDownFile()) startOfLine = startOfLine - 1
567
- var startOfNextLine = this.getLinePos(lineNo + 1)
568
- if (startOfNextLine < 0) return
569
- var content = this.content
570
- var lineContent = content.substring(startOfLine, startOfNextLine)
571
- const trimmedLine = lineContent.trim()
572
- if (
573
- trimmedLine === '' ||
574
- (this.isMarkDownFile() && /^(-|\*+|\#+)$/.test(trimmedLine))
575
- ) {
576
- var start = content.substring(0, startOfLine)
577
- var end = content.substring(startOfNextLine)
578
- this.setContent(start + end)
579
- }
580
- }
581
-
582
- File.prototype.deleteTaskContent = function (beforeContent, afterContent) {
583
- const lang = this.getLang()
584
- if (this.isCodeFile() && lang.block) {
585
- const beforeContentTrim = beforeContent.trim()
586
- const blockStartTrimPos = beforeContentTrim.lastIndexOf(lang.block.start)
587
- if (
588
- blockStartTrimPos > -1 &&
589
- blockStartTrimPos === beforeContentTrim.length - lang.block.start.length
590
- ) {
591
- const blockStartPos = beforeContent.lastIndexOf(lang.block.start)
592
- const blockEndPos =
593
- afterContent.indexOf(lang.block.end) + lang.block.end.length
594
- beforeContent = beforeContent.substring(0, blockStartPos)
595
- afterContent = afterContent.substring(blockEndPos)
596
- }
597
- }
598
-
599
- this.setContent(beforeContent + afterContent)
600
- }
601
-
602
- File.prototype.deleteDescription = function (task, config) {
603
- if (task.singleLineBlockComment) return
604
- task.description = []
605
- this.modifyDescription(task, config)
606
- }
607
-
608
- // TODO This isn't deleting all the newlines at the end of each line in code files
609
- // <!--
610
- // #bug
611
- // #imdone-1.54.0
612
- // order:-165
613
- // -->
614
- File.prototype.deleteCodeOrHashTask = function (re, task, config) {
615
- var log = require('debug')('delete-task:deleteCodeOrHashTask')
616
- var self = this
617
- var line = task.getLine()
618
- re = new XRegExp(re)
619
- re.lastIndex = this.getLinePos(line)
620
- var result = re.exec(this.getContent())
621
- if (result) {
622
- log('Got result: %j', result)
623
- var lang = self.getLang()
624
- var text = result[task.type === Task.Types.HASHTAG ? 4 : 5]
625
- var taskText = this.trimCommentBlockEnd(text)
626
- if (this.tasksMatch(config, task, line, taskText)) {
627
- var index = result.index
628
- var afterStart = re.lastIndex
629
- if (index < 0) index = 0
630
- if (self.isCodeFile()) {
631
- var commentStart = this.getLinePos(line) + task.taskStartOnLine
632
- var commentPrefix = this.getContent().substring(commentStart, index)
633
- var symbolRe = new RegExp(escapeRegExp(lang.symbol) + '\\s*')
634
- if (symbolRe.test(commentPrefix)) index = commentStart
635
- else if (lang && lang.block && lang.block.end) {
636
- var blockEndRe = new RegExp(escapeRegExp(lang.block.end) + '\\s*$')
637
- var match = blockEndRe.exec(task.rawTask)
638
- if (match) afterStart -= match[0].length
639
- }
640
- }
641
- var beforeContent = this.getContent().substring(0, index)
642
- var afterContent = this.getContent().substring(afterStart)
643
- this.deleteTaskContent(beforeContent, afterContent)
644
- this.deleteBlankLine(line)
645
- this.setModified(true)
646
- // task.type = Task.Types.HASHTAG;
647
- this.emit('task.deleted', task)
648
- this.emit('file.modified', self)
649
- }
650
- }
651
-
652
- return this
653
- }
654
-
655
- File.prototype.deleteTask = function (task, config) {
656
- var self = this
657
- if (this.isCodeFile()) {
658
- this.deleteDescription(task, config)
659
- if (task.type === Task.Types.CODE) {
660
- this.deleteCodeOrHashTask(
661
- new RegExp(CODE_STYLE_PATTERN, 'gm'),
662
- task,
663
- config
664
- )
665
- } else if (task.type === Task.Types.HASHTAG) {
666
- this.deleteCodeOrHashTask(getHashStyleRegex(config.tokenPrefix), task, config)
667
- }
668
- } else {
669
- const fileContent = this.getContentLines()
670
- const newContent = [
671
- ...fileContent.slice(0, task.line - 1),
672
- ...fileContent.slice(task.lastLine),
673
- ]
674
- this.setContent(newContent.join(lineEnd))
675
- this.setModified(true)
676
- this.emit('task.deleted', task)
677
- this.emit('file.modified', self)
678
- }
679
- }
680
-
681
- File.prototype.modifyTask = function (task, config, noEmit) {
682
- task.orderModified = true
683
- task.updateOrderMeta(config)
684
- if (task.type === Task.Types.CODE) {
685
- this.modifyCodeOrHashTask(
686
- new RegExp(CODE_STYLE_PATTERN, 'gm'),
687
- task,
688
- config,
689
- noEmit
690
- )
691
- } else {
692
- task.updateContent()
693
- const fileContent = this.getContentLines()
694
- let text = getRawTask({
695
- tokenPrefix: config.tokenPrefix,
696
- orderMeta: config.orderMeta,
697
- beforeText: task.beforeText,
698
- hasColon: task.hasColon,
699
- list: task.list,
700
- order: task.order,
701
- text: task.text,
702
- type: task.type,
703
- })
704
- const linesBefore = fileContent.slice(0, task.line - 1)
705
- if (this.isCodeFile()) {
706
- const linesBeforeLength = linesBefore.join(lineEnd).length
707
- const charsBefore = task.pos - (linesBeforeLength + 1)
708
- if (charsBefore > 0) {
709
- text = fileContent[task.line -1].substring(0,charsBefore) + text
710
- }
711
- }
712
-
713
- const linesAfter = fileContent.slice(task.line)
714
- const newContent = [
715
- ...linesBefore,
716
- text,
717
- ...linesAfter,
718
- ].join(lineEnd)
719
-
720
- const contentBeforeUpdate = this.getContent()
721
- this.setContent(newContent)
722
- this.modifyDescription(task, config)
723
- if (contentBeforeUpdate.trim() == this.getContent().trim()) return
724
-
725
- this.setModified(true)
726
- if (!noEmit) {
727
- this.emit('task.modified', task)
728
- this.emit('file.modified', this)
729
- }
730
- }
731
- }
732
-
733
- File.prototype.modifyCodeOrHashTask = function (re, task, config, noEmit) {
734
- log('In modifyCodeOrHashTask:%j', task)
735
- log('--------------------------------')
736
- log(`modifying task: ${task.rawTask}`)
737
- log(`line: ${task.line}`)
738
- var self = this
739
- var line = task.getLine()
740
- re = new RegExp(re)
741
- var lang = this.getLang()
742
- var linePos = (re.lastIndex = this.getLinePos(line))
743
- var nextLinePos = this.getLinePos(line + 1)
744
- var lineContent = this.getContent().substring(linePos, nextLinePos)
745
- if (lineContent.indexOf(lang.symbol) > -1) {
746
- re.lastIndex = this.getContent().indexOf(lang.symbol, linePos)
747
- }
748
- if (lang.block && lineContent.indexOf(lang.block.start) > -1) {
749
- re.lastIndex = this.getContent().indexOf(lang.block.start, linePos)
750
- }
751
-
752
- var result
753
- while ((result = re.exec(this.getContent())) !== null) {
754
- log('Got result: %j', result)
755
- var text = result[task.type === Task.Types.HASHTAG ? 3 : 5]
756
- var taskText = this.trimCommentBlockEnd(text)
757
- if (this.tasksMatch(config, task, line, taskText)) {
758
- if (task.updatedText) task.text = task.updatedText
759
- var index = result.index
760
- const pos = this.isCodeFile() ? index : this.getLinePos(line)
761
- if (index < 0) index = 0
762
- var beforeContent = this.getContent().substring(0, pos)
763
- var afterContent = this.getContent().substring(re.lastIndex)
764
- if (task.inBlockComment) {
765
- var blockEnd = text.indexOf(lang.block.end)
766
- if (blockEnd > -1) {
767
- const desc =
768
- task.description.length > 0
769
- ? `${lineEnd}${task.description.join(lineEnd)}`
770
- : ''
771
- text = task.text + desc + text.substring(blockEnd)
772
- } else text = task.text
773
- } else text = task.text
774
- if (/[a-z]/.test(task.list)) task.type = Task.Types.HASHTAG
775
- const tokenPrefix = task.type === Task.Types.HASHTAG ? config.tokenPrefix : ''
776
- var taskContent = getRawTask({tokenPrefix, orderMeta: config.orderMeta, list: task.list, order: task.order, text, type: task.type})
777
- task.line = this.getLineNumber(index)
778
- task.id = getTaskId(self.getPath(), task.line, task.text)
779
- this.setContent(beforeContent + taskContent + afterContent)
780
- if (!task.singleLineBlockComment) {
781
- this.modifyDescription(task, config)
782
- }
783
- this.setModified(true)
784
- // console.log('modifyCodeOrHashTask setModified true for file:', this.path)
785
- if (!noEmit) {
786
- this.emit('task.modified', task)
787
- this.emit('file.modified', self)
788
- }
789
- return this
790
- }
791
- }
792
-
793
- return this
794
- }
795
-
796
- File.prototype.modifyTaskFromHtml = function (task, html) {
797
- const checks = task.getChecksFromHtml(html)
798
- const re = /- \[[\sx]{1}\]/g
799
- const descStart = this.getLinePos(task.line + 1)
800
- const descEnd = this.getLinePos(task.line + 1 + task.description.length)
801
- let descContent =
802
- descEnd > 0
803
- ? this.getContent().substring(descStart, descEnd)
804
- : this.getContent().substring(descStart)
805
- const beforeDescContent = this.getContent().substring(0, descStart)
806
- const afterDescContent =
807
- descEnd > 0 ? this.getContent().substring(descEnd) : ''
808
- let i = 0
809
- descContent = descContent.replace(re, (match) => {
810
- const check = checks[i]
811
- i++
812
- if (check === undefined) return match
813
- let char = check ? 'x' : ' '
814
- return `- [${char}]`
815
- })
816
-
817
- this.setContent(beforeDescContent + descContent + afterDescContent)
818
- this.setModified(true)
819
- console.log('modifyTaskFromHtml setModified true for file:', this.path)
820
- }
821
-
822
- File.prototype.modifyTaskFromContent = function (task, content, config) {
823
- content = trimBlankLines(content, this.isCodeFile())
824
- task.updateFromContent(content)
825
- this.modifyTask(task, config)
826
- }
827
-
828
- const trimBlankLines = File.trimBlankLines = function (content, isCodeFile) {
829
- if (isCodeFile) {
830
- // replace all occurrences of two empty lines in a row with a single empty line
831
- return content.replace(/\n\s*\n/g, '\n')
832
- }
833
- return content.replace(/\n\s*\n\s*\n/g, '\n\n')
834
- }
835
-
836
- File.prototype.modifyDescription = function (task, config) {
837
- // TODO This should be split out by type (code, markdown, etc)
838
- // #urgent
839
- // <!-- order:-70 -->
840
- const taskStart = this.getLinePos(task.line)
841
- const descStart = this.getLinePos(task.line + 1)
842
- const contentWithTask = this.getContent().substring(taskStart)
843
- let {
844
- rawTaskContentLines,
845
- taskContentLines,
846
- isWrappedWithCardTag,
847
- trailingBlankLines
848
- } =
849
- this.getTaskContent({
850
- config,
851
- content: contentWithTask,
852
- inBlockComment: task.inBlockComment,
853
- beforeText: task.beforeText,
854
- })
855
-
856
- let beforeDescContent = this.getContent().substring(0, descStart)
857
-
858
- if (descStart === this.getContent().length && !beforeDescContent.endsWith(lf))
859
- beforeDescContent += lineEnd
860
-
861
- let content = this.getContent().substring(descStart)
862
- let spaces = ''
863
- if (this.isCodeFile() && !this.isMarkDownFile()) {
864
- spaces = contentWithTask.substring(0, task.commentStartOnLine)
865
- spaces = spaces.replace(/\S/g, ' ')
866
- }
867
- const descriptionStartsWith = task.descriptionStartsWith
868
- ? `${task.descriptionStartsWith} `
869
- : ''
870
- let description = task.description.map(
871
- (desc) => `${spaces}${descriptionStartsWith}${desc}`
872
- )
873
- // BACKLOG Allow this to pad for code files as well
874
- // <!--
875
- // order:-620
876
- // -->
877
- description = File.padDescription(description, task.beforeText).join(lineEnd)
878
- if (taskContentLines.length === 0 && description.length > 0) {
879
- beforeDescContent += description + lineEnd
880
- } else {
881
- let rawTaskContent = rawTaskContentLines.join(lineEnd)
882
- // Add card terminator to description
883
- if (
884
- this.isMarkDownFile() &&
885
- description.length > 0 &&
886
- (hasBlankLines(description) || isWrappedWithCardTag || trailingBlankLines)
887
- ) {
888
- task.isWrappedWithCardTag = isWrappedWithCardTag // FIXME Find out where this is used and stop using it
889
- // <!-- order:-30 -->
890
- const blankLinesToAdd = isWrappedWithCardTag ? 2 : trailingBlankLines
891
- description = `${description}${lineEnd.repeat(blankLinesToAdd)}`
892
- }
893
-
894
- // Handle code file tasks
895
- if (
896
- task.singleLineBlockComment &&
897
- rawTaskContent.length > 0 &&
898
- description.length === 0
899
- ) {
900
- description = lineEnd + description
901
- }
902
- content = content.replace(rawTaskContent, description)
903
- }
904
-
905
- task.lastLine = task.line + description.split(lineEnd).length
906
- this.setContent(beforeDescContent + content)
907
- }
908
-
909
- File.padDescription = function (description = [], beforeText = '') {
910
- return Task.padDescription(description, beforeText)
911
- }
912
-
913
- File.prototype.extractTaskWithDescription = function ({
914
- taskStartOnLine,
915
- rawTask,
916
- config,
917
- list,
918
- text,
919
- order,
920
- line,
921
- type,
922
- hasColon,
923
- content,
924
- inBlockComment,
925
- singleLineBlockComment,
926
- pos,
927
- }) {
928
- var self = this
929
- const lang = this.getLang()
930
- let description = []
931
- // BACKLOG ## add beforeText for code files
932
- // <!--
933
- // order:-630
934
- // -->
935
- const beforeText = this.getBeforeText(line, pos)
936
- let { rawTaskContentLines, taskContentLines, isWrappedWithCardTag } = this.getTaskContent({
937
- config,
938
- content,
939
- inBlockComment,
940
- beforeText,
941
- })
942
-
943
- if (!singleLineBlockComment) {
944
- description = this.isCodeFile()
945
- ? taskContentLines.map((line) => this.trimCommentChars(line))
946
- : _clone(taskContentLines)
947
- }
948
- description = Task.trimDescription(description, beforeText)
949
- const descriptionStartsWith = this.getDescriptionChars(inBlockComment)
950
- const frontMatter = this.frontMatter
951
- text = this.trimCommentBlockEnd(text)
952
- let commentStartOnLine =
953
- content.search(/\w/) - (descriptionStartsWith.length + 1)
954
- if (descriptionStartsWith === lang.symbol)
955
- commentStartOnLine = taskStartOnLine
956
-
957
- var task = newCard(
958
- {
959
- pos,
960
- frontMatter,
961
- inBlockComment,
962
- singleLineBlockComment,
963
- rawTask,
964
- text,
965
- list,
966
- rawTaskContentLines,
967
- description,
968
- descriptionStartsWith,
969
- taskStartOnLine,
970
- commentStartOnLine,
971
- hasColon,
972
- order: self.projectContext.getOrder(list, order),
973
- line,
974
- id: getTaskId(self.getPath(), line, text),
975
- repoId: self.getRepoId(),
976
- source: self.getSource(),
977
- type,
978
- beforeText,
979
- isWrappedWithCardTag,
980
- },
981
- this.project
982
- )
983
-
984
- task.init()
985
- if (!config.keepEmptyPriority) task.updateOrderMeta(config, task.format(task.descriptionString))
986
- task.orderModified = task.order + "" !== order + "" && !bothAreUndefNull(task.order, order)
987
-
988
- return task
989
- }
990
-
991
- function bothAreUndefNull(a, b) {
992
- return isUndefNull(a) && isUndefNull(b)
993
- }
994
-
995
- function isUndefNull(val) {
996
- return val === null || val === undefined
997
- }
998
-
999
- File.prototype.tasksMatch = function (config, task, line, taskText) {
1000
- return (
1001
- task.id == getTaskId(this.getPath(), line, taskText) ||
1002
- (task.meta.id &&
1003
- Task.getMetaData(config, taskText).id &&
1004
- task.meta.id[0] === Task.getMetaData(config, taskText).id[0])
1005
- )
1006
- }
1007
-
1008
- File.prototype.getTaskAtLine = function (line) {
1009
- return this.getTasks().find((task) => {
1010
- return line >= task.line && line <= task.lastLine
1011
- })
1012
- }
1013
-
1014
- File.prototype.getLinePos = function (lineNo) {
1015
- return Task.getLinePos(this.content, lineNo)
1016
- }
1017
-
1018
- File.prototype.getLineNumber = function (pos, content = this.content) {
1019
- return Task.getLineNumber(content, pos)
1020
- }
1021
-
1022
- File.prototype.toJSON = function () {
1023
- return _omit(this, ['domain', '_events', '_maxListeners'])
1024
- }
1025
-
1026
- File.prototype.getRepoId = function () {
1027
- return this.repoId
1028
- }
1029
-
1030
- File.prototype.getPath = function () {
1031
- return this.path
1032
- }
1033
-
1034
- File.prototype.getFullPath = function () {
1035
- return this.repoId + this.path
1036
- }
1037
-
1038
- File.prototype.getId = function () {
1039
- return this.getPath()
1040
- }
1041
-
1042
- File.prototype.reset = function () {
1043
- this.previousContent = this.content
1044
- this.content = null
1045
- return this
1046
- }
1047
-
1048
- File.prototype.rollback = function () {
1049
- this.content = this.previousContent
1050
- return this
1051
- }
1052
-
1053
- File.prototype.setContent = function (content) {
1054
- this.content = content
1055
- return this
1056
- }
1057
-
1058
- File.prototype.setContentFromFile = function (content) {
1059
- this.content = eol.auto(content || '')
1060
- return this
1061
- }
1062
-
1063
- File.prototype.getContent = function () {
1064
- return this.content
1065
- }
1066
-
1067
- File.prototype.getContentForFile = function () {
1068
- return eol.auto(this.content || '')
1069
- }
1070
-
1071
- File.prototype.getContentLines = function () {
1072
- return eol.split(this.getContentForFile())
1073
- }
1074
-
1075
- File.prototype.setModifiedTime = function (modifiedTime) {
1076
- this.modifiedTime = modifiedTime
1077
- return this
1078
- }
1079
-
1080
- File.prototype.setCreatedTime = function (createdTime) {
1081
- this.createdTime = createdTime
1082
- return this
1083
- }
1084
-
1085
- File.prototype.getCreatedTime = function () {
1086
- return this.createdTime
1087
- }
1088
-
1089
- File.prototype.getModifiedTime = function () {
1090
- return this.modifiedTime
1091
- }
1092
-
1093
- File.prototype.setModified = function (modified) {
1094
- this.modified = modified
1095
- return this
1096
- }
1097
-
1098
- File.prototype.isModified = function () {
1099
- return this.modified
1100
- }
1101
-
1102
- File.prototype.getType = function () {
1103
- return this.constructor.name
1104
- }
1105
-
1106
- File.prototype.getTasks = function () {
1107
- return this.tasks
1108
- }
1109
-
1110
- File.prototype.getTask = function (id) {
1111
- return (
1112
- this.getTasks().find((task) => id === task.id) ||
1113
- this.getTasks().find(
1114
- (task) => task.meta && task.meta.id && task.meta.id[0] === id.toString()
1115
- )
1116
- )
1117
- }
1118
-
1119
- File.prototype.addTask = function (task) {
1120
- if (!(task instanceof Task)) throw new Error(ERRORS.NOT_A_TASK)
1121
- if (!Array.isArray(this.tasks)) this.tasks = []
1122
- var index = this.tasks.findIndex(({ id }) => task.id === id)
1123
- log(
1124
- 'Adding task with text:%s in list:%s with order:%d at index %d',
1125
- task.text,
1126
- task.list,
1127
- task.order,
1128
- index
1129
- )
1130
- if (index > -1) {
1131
- this.tasks[index] = task
1132
- } else {
1133
- this.tasks.push(task)
1134
- }
1135
- return this
1136
- }
1137
-
1138
- File.prototype.removeTask = function (task) {
1139
- if (!(task instanceof Task)) throw new Error(ERRORS.NOT_A_TASK)
1140
- if (!Array.isArray(this.tasks)) this.tasks = []
1141
- var index = this.tasks.findIndex(({ id }) => task.id === id)
1142
- if (index > -1) {
1143
- this.tasks.splice(index, 1)
1144
- }
1145
- }
1146
-
1147
- File.prototype.isMarkDownFile = function () {
1148
- return /^md|markdown$/i.test(this.getExt())
1149
- }
1150
-
1151
- File.prototype.getLang = function () {
1152
- var lang = this.languages[path.extname(this.path)]
1153
- return lang || { name: 'text', symbol: '' }
1154
- }
1155
-
1156
- File.prototype.getExt = function () {
1157
- return path.extname(this.path).substring(1).toLowerCase()
1158
- }
1159
-
1160
- File.prototype.isCodeFile = function () {
1161
- var symbol = this.getLang().symbol
1162
- return symbol && symbol !== ''
1163
- }
1164
-
1165
- File.prototype.getBeforeText = function (line, pos) {
1166
- return this.isCodeFile()
1167
- ? null
1168
- : this.content.substring(this.getLinePos(line), pos)
1169
- }
1170
-
1171
- File.prototype.getDescriptionChars = function (inBlockComment) {
1172
- if (!this.isCodeFile()) return ''
1173
- const lang = this.getLang()
1174
- if (inBlockComment && lang.block && lang.block.ignore) return lang.block.ignore
1175
- return lang.symbol
1176
- }
1177
-
1178
- File.prototype.getTaskContent = function ({
1179
- config,
1180
- content,
1181
- inBlockComment,
1182
- beforeText,
1183
- }) {
1184
- const isMarkDownFile = this.isMarkDownFile()
1185
- const isCodeFile = this.isCodeFile()
1186
- const lang = this.getLang()
1187
- const fileType = isMarkDownFile ? FILE_TYPES.MARKDOWN : isCodeFile ? FILE_TYPES.CODE : undefined
1188
- return getTaskContent({
1189
- config,
1190
- content,
1191
- inBlockComment,
1192
- beforeText,
1193
- lang,
1194
- fileType,
1195
- })
1196
- }
1197
-
1198
- File.prototype.isCheckBoxTask = function (config, line, beforeText) {
1199
- if (!config.isAddCheckBoxTasks()) return
1200
-
1201
- const beforeTextCheckData = Task.getCheckedData(beforeText)
1202
- if (!beforeTextCheckData) return
1203
-
1204
- const lineCheckData = Task.getCheckedData(line)
1205
- if (!lineCheckData) return
1206
-
1207
- return lineCheckData.pad <= beforeTextCheckData.pad
1208
- }
1209
-
1210
- File.prototype.getCodeCommentRegex = function () {
1211
- // #BACKLOG:-460 Allow languages to have multiple block comment styles, like html gh:13 id:5
1212
- var lang = this.getLang()
1213
- var symbol = lang.symbol
1214
- var reString = '(?<!["\'`])' + escapeRegExp(symbol) + '[^{].*$'
1215
-
1216
- if (lang.block) {
1217
- var start = escapeRegExp(lang.block.start)
1218
- var end = escapeRegExp(lang.block.end)
1219
- //
1220
- reString = reString + '|(?<!["\'`])' + start + '(.|[\\r\\n])*?' + end
1221
- }
1222
-
1223
- return new RegExp(reString, 'gmi')
1224
- }
1225
-
1226
- File.prototype.trimCommentStart = function (text) {
1227
- if (this.isCodeFile() && this.getLang().symbol) {
1228
- var start = escapeRegExp(this.getLang().symbol)
1229
- var startPattern = `^\\s*${start}\\s?`
1230
- return text.replace(new RegExp(startPattern), '')
1231
- }
1232
- return text
1233
- }
1234
-
1235
- File.prototype.trimCommentBlockEnd = function (text) {
1236
- if (this.isCodeFile() && this.getLang().block) {
1237
- var blockEnd = escapeRegExp(this.getLang().block.end)
1238
- var endPattern = `\\s?${blockEnd}.*$`
1239
- return text.replace(new RegExp(endPattern), '')
1240
- }
1241
- return text
1242
- }
1243
-
1244
- File.prototype.trimCommentBlockStart = function (text) {
1245
- if (this.isCodeFile() && this.getLang().block) {
1246
- var blockStart = escapeRegExp(this.getLang().block.start)
1247
- var startPattern = `^\\s*${blockStart}\\s?`
1248
- return text.replace(new RegExp(startPattern), '')
1249
- }
1250
- return text
1251
- }
1252
-
1253
- File.prototype.trimCommentBlockIgnore = function (text) {
1254
- if (
1255
- this.isCodeFile() &&
1256
- this.getLang().block &&
1257
- this.getLang().block.ignore
1258
- ) {
1259
- var blockIgnore = escapeRegExp(this.getLang().block.ignore)
1260
- var ignorePattern = `^\\s*${blockIgnore}\\s?`
1261
- return text.replace(new RegExp(ignorePattern), '')
1262
- }
1263
- return text
1264
- }
1265
-
1266
- File.prototype.trimCommentChars = function (text) {
1267
- let newText = this.trimCommentStart(text)
1268
- if (text === newText) newText = this.trimCommentBlockEnd(text)
1269
- if (text === newText) newText = this.trimCommentBlockStart(text)
1270
- if (text === newText) newText = this.trimCommentBlockIgnore(text)
1271
- return newText
1272
- }
1273
-
1274
- File.prototype.hasCodeStyleTask = function (config, text) {
1275
- if (!this.isCodeFile()) return false
1276
- let result = new RegExp(CODE_STYLE_PATTERN).exec(text)
1277
- if (!result) return false
1278
- return config.includeList(result[1])
1279
- }
1280
-
1281
- File.prototype.hasTaskInText = function (config, text) {
1282
- return hasTaskInText(config, text, this.isCodeFile())
1283
- }
1284
-
1285
- File.prototype.isInBlockComment = function (content) {
1286
- if (!this.isCodeFile()) return false
1287
- const lang = this.getLang()
1288
- return (
1289
- lang.block &&
1290
- lang.block.start &&
1291
- content.trim().startsWith(lang.block.start)
1292
- )
1293
- }
1294
-
1295
- File.prototype.startsWithCommentOrSpace = function (pos) {
1296
- var lang = this.getLang()
1297
- var symbol = lang.symbol
1298
- var blockStart = lang.block && lang.block.start
1299
- if (symbol === this.getContent().substring(pos - symbol.length, pos))
1300
- return true
1301
- if (
1302
- blockStart &&
1303
- blockStart === this.getContent().substring(pos - blockStart.length, pos)
1304
- )
1305
- return true
1306
- if (this.getContent().substring(pos - 1, pos) === ' ') return true
1307
- return false
1308
- }
1309
-
1310
- File.prototype.isSingleLineBlockComment = function (content) {
1311
- return eol.split(content).length === 1
1312
- }
1313
-
1314
- File.prototype.parseFrontMatter = function (config) {
1315
- this.frontMatter = {
1316
- tags: [],
1317
- context: [],
1318
- meta: {},
1319
- }
1320
-
1321
- if (config.ignoreFrontMatter) return {}
1322
- try {
1323
- const { data, isEmpty } = matter(this.getContent())
1324
- if (!isEmpty) {
1325
- this.frontMatter = { props: {}, ...data, ...this.frontMatter }
1326
- if (data.meta) this.frontMatter.meta = getFrontMeta(data.meta)
1327
- if (data.context) {
1328
- this.frontMatter.context = Array.isArray(data.context)
1329
- ? data.context.map((context) => context.trim())
1330
- : data.context
1331
- .toString()
1332
- .split(',')
1333
- .map((context) => context.trim())
1334
- }
1335
- if (data.tags && !config.ignoreFrontMatterTags) {
1336
- this.frontMatter.tags = Array.isArray(data.tags)
1337
- ? data.tags.map((tag) => tag.trim())
1338
- : data.tags
1339
- .toString()
1340
- .split(',')
1341
- .map((tag) => tag.trim())
1342
- }
1343
- return data
1344
- }
1345
- } catch (err) {
1346
- console.error(`Error processing front-matter in:${this.path}`, err)
1347
- }
1348
- return {}
1349
- }
1350
-
1351
- File.prototype.getSource = function () {
1352
- var self = this
1353
- return {
1354
- path: self.getPath(),
1355
- id: self.getId(),
1356
- repoId: self.getRepoId(),
1357
- type: self.getType(),
1358
- ext: self.getExt(),
1359
- lang: self.getLang().name,
1360
- modified: self.isModified(),
1361
- modifiedTime: self.getModifiedTime(),
1362
- createdTime: self.getCreatedTime(),
1363
- }
1364
- }
1365
-
1366
- File.prototype.isWithinCodeSpanOrBlock = function(pos, content) {
1367
- const positions = Task.getMarkdownCodePositions(content)
1368
- return Task.isResultInMarkdownCode(positions, pos)
1369
- }
1370
-
1371
- File.prototype.getMarkdownCodePositions = function (text) {
1372
- const positions = [];
1373
- const codeBlockRe = /```[\s\S]*?```/g;
1374
- const inlineCodeRe = /`[^`]*`/g;
1375
-
1376
- let match;
1377
-
1378
- // Find code blocks
1379
- while ((match = codeBlockRe.exec(text)) !== null) {
1380
- positions.push({ start: match.index, end: match.index + match[0].length });
1381
- }
1382
-
1383
- // Find inline code spans
1384
- while ((match = inlineCodeRe.exec(text)) !== null) {
1385
- positions.push({ start: match.index, end: match.index + match[0].length });
1386
- }
1387
-
1388
- return positions;
1389
- };
1390
-
1391
- File.prototype.isResultInMarkdownCode = function (positions, index) {
1392
- return positions.some(pos => index >= pos.start && index < pos.end);
1393
- };
1394
-
1395
- module.exports = File