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