imdone-core 2.0.0 → 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.
- package/.dependency-cruiser.cjs +412 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/workflows/ci.yml +24 -0
- package/.imdoneignore +13 -0
- package/.mocharc.json +10 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +160 -0
- package/_index.md +7 -0
- package/_navbar.md +1 -0
- package/build.js +57 -0
- package/esm-migration.md +446 -0
- package/index.html +184 -0
- package/lib/FileProjectContext.js +13 -0
- package/lib/ProjectContext.js +43 -0
- package/lib/adapters/date-time.js +15 -0
- package/lib/adapters/environment.js +3 -0
- package/lib/adapters/file-gateway.js +154 -0
- package/lib/adapters/git-download.js +55 -0
- package/lib/adapters/index.js +9 -0
- package/lib/adapters/logger.js +136 -0
- package/lib/adapters/markdown.js +180 -0
- package/lib/adapters/parsers/DateLanguageParser.js +12 -0
- package/lib/adapters/parsers/content-transformer.js +116 -0
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +36 -0
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +126 -0
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +17 -0
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +106 -0
- package/lib/adapters/parsers/file/index.js +4 -0
- package/lib/adapters/parsers/function-parser.js +25 -0
- package/lib/adapters/parsers/index.js +5 -0
- package/lib/adapters/parsers/task/CardContentParser.js +367 -0
- package/lib/adapters/parsers/task/CheckStyleTaskParser.js +28 -0
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +27 -0
- package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +24 -0
- package/lib/adapters/parsers/task/TaskParser.js +38 -0
- package/lib/adapters/parsers/task/index.js +6 -0
- package/lib/adapters/storage/config.js +44 -0
- package/lib/adapters/storage/index.js +2 -0
- package/lib/adapters/storage/tags.js +29 -0
- package/lib/adapters/yaml.js +9 -0
- package/lib/card.js +299 -0
- package/lib/checksum.js +22 -0
- package/lib/config.js +249 -0
- package/lib/constants.js +68 -0
- package/lib/context/ApplicationContext.js +35 -0
- package/lib/context/index.js +1 -0
- package/lib/default-settings.js +37 -0
- package/lib/domain/entities/View.js +23 -0
- package/lib/domain/entities/index.js +1 -0
- package/lib/domain/index.js +2 -0
- package/lib/domain/usecases/GetViewsUsecase.js +44 -0
- package/lib/domain/usecases/SaveViewUsecase.js +51 -0
- package/lib/domain/usecases/index.js +2 -0
- package/lib/file.js +1391 -0
- package/lib/get-tasks-to-modify.js +117 -0
- package/lib/languages.js +113 -0
- package/lib/list.js +56 -0
- package/lib/migrate-config.js +165 -0
- package/lib/mixins/index.js +2 -0
- package/lib/mixins/repo-fs-store.js +359 -0
- package/lib/mixins/repo-watched-fs-store.js +206 -0
- package/lib/plugins/archive-plugin.js +72 -0
- package/lib/plugins/default-board-actions-plugin.js +56 -0
- package/lib/plugins/default-board-properties-plugin.js +101 -0
- package/lib/plugins/default-plugins.js +7 -0
- package/lib/plugins/epic-plugin.js +177 -0
- package/lib/plugins/extension-plugin.js +94 -0
- package/lib/plugins/index.js +9 -0
- package/lib/plugins/persist-tags-plugin.js +14 -0
- package/lib/plugins/plugin-manager.js +458 -0
- package/lib/plugins/plugin-registry.js +57 -0
- package/lib/project-factory.js +42 -0
- package/lib/project.js +864 -0
- package/lib/repository.js +1514 -0
- package/lib/task.js +780 -0
- package/lib/tools.js +176 -0
- package/lib/usecases/actions.js +32 -0
- package/lib/usecases/get-imdone-url.js +6 -0
- package/lib/usecases/get-project-tags.js +7 -0
- package/lib/usecases/get-tasks-in-file.js +26 -0
- package/lib/usecases/index.js +4 -0
- package/package.json +2 -6
- package/scripts/delete-markdown-indexes.js +87 -0
- package/scripts/generate-index.js +35 -0
- package/scripts/index-markdown.js +55 -0
- package/tsconfig.json +15 -0
- package/types/FileProjectContext.d.ts +4 -0
- package/types/ProjectContext.d.ts +9 -0
- package/types/adapters/date-time.d.ts +2 -0
- package/types/adapters/environment.d.ts +1 -0
- package/types/adapters/file-gateway.d.ts +51 -0
- package/types/adapters/git-download.d.ts +1 -0
- package/types/adapters/index.d.ts +9 -0
- package/types/adapters/logger.d.ts +24 -0
- package/types/adapters/markdown.d.ts +13 -0
- package/types/adapters/parsers/DateLanguageParser.d.ts +1 -0
- package/types/adapters/parsers/content-transformer.d.ts +10 -0
- package/types/adapters/parsers/file/LocalFileParserFactory.d.ts +5 -0
- package/types/adapters/parsers/file/ReadStreamCodeParser.d.ts +13 -0
- package/types/adapters/parsers/file/ReadStreamMarkdownParser.d.ts +7 -0
- package/types/adapters/parsers/file/ReadStreamTaskParser.d.ts +19 -0
- package/types/adapters/parsers/file/index.d.ts +4 -0
- package/types/adapters/parsers/function-parser.d.ts +1 -0
- package/types/adapters/parsers/index.d.ts +5 -0
- package/types/adapters/parsers/task/CardContentParser.d.ts +45 -0
- package/types/adapters/parsers/task/CheckStyleTaskParser.d.ts +12 -0
- package/types/adapters/parsers/task/CodeStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/HashStyleTaskParser.d.ts +13 -0
- package/types/adapters/parsers/task/MarkdownStyleTaskParser.d.ts +14 -0
- package/types/adapters/parsers/task/TaskParser.d.ts +7 -0
- package/types/adapters/parsers/task/index.d.ts +6 -0
- package/types/adapters/storage/config.d.ts +6 -0
- package/types/adapters/storage/index.d.ts +2 -0
- package/types/adapters/storage/tags.d.ts +3 -0
- package/types/adapters/yaml.d.ts +2 -0
- package/types/card.d.ts +166 -0
- package/types/checksum.d.ts +8 -0
- package/types/config.d.ts +61 -0
- package/types/constants.d.ts +46 -0
- package/types/context/ApplicationContext.d.ts +24 -0
- package/types/context/index.d.ts +1 -0
- package/types/default-settings.d.ts +2 -0
- package/types/domain/entities/FileProjectContext.d.ts +5 -0
- package/types/domain/entities/View.d.ts +17 -0
- package/types/domain/entities/index.d.ts +1 -0
- package/types/domain/index.d.ts +2 -0
- package/types/domain/usecases/GetViewsUsecase.d.ts +4 -0
- package/types/domain/usecases/SaveViewUsecase.d.ts +8 -0
- package/types/domain/usecases/index.d.ts +2 -0
- package/types/file.d.ts +344 -0
- package/types/get-tasks-to-modify.d.ts +3 -0
- package/types/index.d.ts +23 -0
- package/types/languages.d.ts +649 -0
- package/types/list.d.ts +30 -0
- package/types/migrate-config.d.ts +1 -0
- package/types/mixins/index.d.ts +2 -0
- package/types/mixins/repo-fs-store.d.ts +2 -0
- package/types/mixins/repo-watched-fs-store.d.ts +2 -0
- package/types/plugins/archive-plugin.d.ts +8 -0
- package/types/plugins/default-board-actions-plugin.d.ts +9 -0
- package/types/plugins/default-board-properties-plugin.d.ts +16 -0
- package/types/plugins/default-plugins.d.ts +6 -0
- package/types/plugins/epic-plugin.d.ts +19 -0
- package/types/plugins/extension-plugin.d.ts +15 -0
- package/types/plugins/index.d.ts +9 -0
- package/types/plugins/persist-tags-plugin.d.ts +4 -0
- package/types/plugins/plugin-manager.d.ts +68 -0
- package/types/plugins/plugin-registry.d.ts +1 -0
- package/types/project-factory.d.ts +14 -0
- package/types/project.d.ts +158 -0
- package/types/repository.d.ts +996 -0
- package/types/task.d.ts +202 -0
- package/types/tools.d.ts +29 -0
- package/types/usecases/actions.d.ts +7 -0
- package/types/usecases/get-imdone-url.d.ts +1 -0
- package/types/usecases/get-project-tags.d.ts +1 -0
- package/types/usecases/get-tasks-in-file.d.ts +4 -0
- package/types/usecases/index.d.ts +4 -0
- package/vitest.config.js +13 -0
package/lib/file.js
ADDED
|
@@ -0,0 +1,1391 @@
|
|
|
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,
|
|
20
|
+
getTaskContent,
|
|
21
|
+
getRawTask,
|
|
22
|
+
getHashStyleRegex,
|
|
23
|
+
hasTaskInText,
|
|
24
|
+
LIST_NAME_PATTERN,
|
|
25
|
+
LINK_STYLE_REGEX,
|
|
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
|
|
35
|
+
const ERRORS = {
|
|
36
|
+
NOT_A_TASK: 'task must be of type Task',
|
|
37
|
+
}
|
|
38
|
+
const CODE_STYLE_END = '((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
|
|
39
|
+
const CODE_STYLE_PATTERN = '([A-Z]+[A-Z-_]+?)' + CODE_STYLE_END
|
|
40
|
+
const CHECK_STYLE_PATTERN = /^(\s*- \[([x ])\]\s)(.+$)/gm
|
|
41
|
+
|
|
42
|
+
function getTaskId (path, line, text) {
|
|
43
|
+
var shasum = crypto.createHash('sha1')
|
|
44
|
+
const taskElements = { path, line, text: text.trim() }
|
|
45
|
+
shasum.update(JSON.stringify(taskElements))
|
|
46
|
+
return shasum.digest('hex')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getDoneList(config) {
|
|
50
|
+
return config.getDoneList()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getDoingList(config) {
|
|
54
|
+
return config.getDoingList()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getFrontMeta(meta) {
|
|
58
|
+
const metadata = {}
|
|
59
|
+
Object.entries(meta).forEach(([prop, val]) => {
|
|
60
|
+
if (Array.isArray(val)) {
|
|
61
|
+
val.forEach((item) => {
|
|
62
|
+
if (_isObject(item) || Array.isArray(item)) return
|
|
63
|
+
if (!metadata[prop]) metadata[prop] = []
|
|
64
|
+
metadata[prop].push(`${item}`)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
return metadata
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function trackMarkdownChecklistChanges({task, config, modify}) {
|
|
72
|
+
const doneList = config.getDoneList()
|
|
73
|
+
let beforeTextModified = false
|
|
74
|
+
if (task.beforeText.trim().startsWith('- [ ]')) {
|
|
75
|
+
if (task.list === doneList) {
|
|
76
|
+
if (modify) {
|
|
77
|
+
task.beforeText = task.beforeText.replace('[ ]', '[x]')
|
|
78
|
+
beforeTextModified = true
|
|
79
|
+
} else {
|
|
80
|
+
task.list = config.getDefaultList()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else if (task.beforeText.trim().startsWith('- [x]')) {
|
|
84
|
+
if (task.list !== doneList) {
|
|
85
|
+
if (modify) {
|
|
86
|
+
task.beforeText = task.beforeText.replace('[x]', '[ ]')
|
|
87
|
+
beforeTextModified = true
|
|
88
|
+
} else {
|
|
89
|
+
task.list = doneList
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return beforeTextModified
|
|
95
|
+
}
|
|
96
|
+
|
|
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
|
+
}
|
|
104
|
+
|
|
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)
|
|
200
|
+
if (
|
|
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
|
|
261
|
+
) {
|
|
262
|
+
content = this.removeMetaData(
|
|
263
|
+
content,
|
|
264
|
+
'completed',
|
|
265
|
+
task.meta.completed[0]
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
return content
|
|
269
|
+
}
|
|
270
|
+
|
|
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
|
+
}
|
|
295
|
+
|
|
296
|
+
return { content, beforeTextModified }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
removeMetaData (content, key, value) {
|
|
300
|
+
return Task.removeMetaData({
|
|
301
|
+
config: this.projectContext.config,
|
|
302
|
+
content,
|
|
303
|
+
key,
|
|
304
|
+
value,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
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
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
transformTasks (config, modify) {
|
|
335
|
+
sort(this.getTasks())
|
|
336
|
+
.desc(u => u.line)
|
|
337
|
+
.forEach(task => this.transformTask({config, modify, task}))
|
|
338
|
+
}
|
|
339
|
+
|
|
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({
|
|
416
|
+
config,
|
|
417
|
+
list,
|
|
418
|
+
text,
|
|
419
|
+
order,
|
|
420
|
+
line,
|
|
421
|
+
type: Task.Types.HASHTAG,
|
|
422
|
+
hasColon: orderGroup && orderGroup.startsWith(':'),
|
|
423
|
+
content: content.substring(result.index),
|
|
424
|
+
pos: posInContent,
|
|
425
|
+
})
|
|
426
|
+
task.taskStartOnLine = pos - this.getLinePos(line)
|
|
427
|
+
task.rawTask = rawTask
|
|
428
|
+
this.addTask(task)
|
|
429
|
+
this.emit('task.found', task)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
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)
|
|
451
|
+
|
|
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)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
extractCheckboxTasks (config, pos, content) {
|
|
470
|
+
if (!this.isMarkDownFile()) return
|
|
471
|
+
if (!config.isAddCheckBoxTasks()) return
|
|
472
|
+
|
|
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 = []
|
|
478
|
+
const hasTaskAtLine = (line) => {
|
|
479
|
+
return (
|
|
480
|
+
this.getTaskAtLine(line) ||
|
|
481
|
+
tasks.find((task) => {
|
|
482
|
+
return line >= task.line && line <= task.lastLine
|
|
483
|
+
})
|
|
484
|
+
)
|
|
485
|
+
}
|
|
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
|
+
})
|
|
529
|
+
|
|
530
|
+
if (this.content === newContent || !newContent) return
|
|
531
|
+
|
|
532
|
+
this.content = newContent
|
|
533
|
+
|
|
534
|
+
this.setModified(true)
|
|
535
|
+
logger.log('extractCheckboxTasks setModified true for file:', this.path)
|
|
536
|
+
}
|
|
537
|
+
}
|
|
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
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
extractAndTransformTasks (config) {
|
|
553
|
+
this.extractTasks(config)
|
|
554
|
+
this.transformTasks(config)
|
|
555
|
+
}
|
|
556
|
+
|
|
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
|
+
}
|
|
573
|
+
|
|
574
|
+
extractTasksInNonCodeFile (config) {
|
|
575
|
+
this.extractHashStyleTasks(config, 0, this.getContent())
|
|
576
|
+
this.extractLinkStyleTasks(config, 0, this.getContent())
|
|
577
|
+
this.extractCheckboxTasks(config, 0, this.getContent())
|
|
578
|
+
}
|
|
579
|
+
|
|
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()
|
|
588
|
+
if (
|
|
589
|
+
trimmedLine === '' ||
|
|
590
|
+
(this.isMarkDownFile() && /^(-|\*+|\#+)$/.test(trimmedLine))
|
|
591
|
+
) {
|
|
592
|
+
var start = content.substring(0, startOfLine)
|
|
593
|
+
var end = content.substring(startOfNextLine)
|
|
594
|
+
this.setContent(start + end)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
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
|
+
}
|
|
614
|
+
|
|
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
|
+
}
|
|
623
|
+
|
|
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
|
+
}
|
|
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)
|
|
683
|
+
}
|
|
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))
|
|
691
|
+
this.setModified(true)
|
|
692
|
+
this.emit('task.deleted', task)
|
|
693
|
+
this.emit('file.modified', self)
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
modifyTask (task, config, noEmit) {
|
|
698
|
+
task.orderModified = true
|
|
699
|
+
task.updateOrderMeta(config)
|
|
700
|
+
if (task.type === Task.Types.CODE) {
|
|
701
|
+
this.modifyCodeOrHashTask(
|
|
702
|
+
new RegExp(CODE_STYLE_PATTERN, 'gm'),
|
|
703
|
+
task,
|
|
704
|
+
config,
|
|
705
|
+
noEmit
|
|
706
|
+
)
|
|
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
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const linesAfter = fileContent.slice(task.line)
|
|
730
|
+
const newContent = [
|
|
731
|
+
...linesBefore,
|
|
732
|
+
text,
|
|
733
|
+
...linesAfter,
|
|
734
|
+
].join(lineEnd)
|
|
735
|
+
|
|
736
|
+
const contentBeforeUpdate = this.getContent()
|
|
737
|
+
this.setContent(newContent)
|
|
738
|
+
this.modifyDescription(task, config)
|
|
739
|
+
if (contentBeforeUpdate.trim() == this.getContent().trim()) return
|
|
740
|
+
|
|
741
|
+
this.setModified(true)
|
|
742
|
+
if (!noEmit) {
|
|
743
|
+
this.emit('task.modified', task)
|
|
744
|
+
this.emit('file.modified', this)
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
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
|
+
}
|
|
767
|
+
|
|
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
|
+
}
|
|
808
|
+
|
|
809
|
+
return this
|
|
810
|
+
}
|
|
811
|
+
|
|
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
|
+
})
|
|
832
|
+
|
|
833
|
+
this.setContent(beforeDescContent + descContent + afterDescContent)
|
|
834
|
+
this.setModified(true)
|
|
835
|
+
logger.log('modifyTaskFromHtml setModified true for file:', this.path)
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
modifyTaskFromContent (task, content, config) {
|
|
839
|
+
content = trimBlankLines(content, this.isCodeFile())
|
|
840
|
+
task.updateFromContent(content)
|
|
841
|
+
this.modifyTask(task, config)
|
|
842
|
+
}
|
|
843
|
+
|
|
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
|
+
})
|
|
863
|
+
|
|
864
|
+
let beforeDescContent = this.getContent().substring(0, descStart)
|
|
865
|
+
|
|
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, ' ')
|
|
874
|
+
}
|
|
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
|
+
}
|
|
901
|
+
|
|
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
|
+
}
|
|
912
|
+
|
|
913
|
+
task.lastLine = task.line + description.split(lineEnd).length
|
|
914
|
+
this.setContent(beforeDescContent + content)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
extractTaskWithDescription ({
|
|
918
|
+
taskStartOnLine,
|
|
919
|
+
rawTask,
|
|
920
|
+
config,
|
|
921
|
+
list,
|
|
922
|
+
text,
|
|
923
|
+
order,
|
|
924
|
+
line,
|
|
925
|
+
type,
|
|
926
|
+
hasColon,
|
|
927
|
+
content,
|
|
928
|
+
inBlockComment,
|
|
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,
|
|
943
|
+
inBlockComment,
|
|
944
|
+
beforeText,
|
|
945
|
+
})
|
|
946
|
+
|
|
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
|
+
)
|
|
987
|
+
|
|
988
|
+
task.init()
|
|
989
|
+
if (!config.keepEmptyPriority) task.updateOrderMeta(config, task.format(task.descriptionString))
|
|
990
|
+
task.orderModified = task.order + "" !== order + "" && !bothAreUndefNull(task.order, order)
|
|
991
|
+
|
|
992
|
+
return task
|
|
993
|
+
}
|
|
994
|
+
|
|
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
|
+
}
|
|
1003
|
+
|
|
1004
|
+
getTaskAtLine (line) {
|
|
1005
|
+
return this.getTasks().find((task) => {
|
|
1006
|
+
return line >= task.line && line <= task.lastLine
|
|
1007
|
+
})
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
getLinePos (lineNo) {
|
|
1011
|
+
return Task.getLinePos(this.content, lineNo)
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
getLineNumber (pos, content = this.content) {
|
|
1015
|
+
return Task.getLineNumber(content, pos)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
toJSON () {
|
|
1019
|
+
return _omit(this, ['domain', '_events', '_maxListeners'])
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
getRepoId () {
|
|
1023
|
+
return this.repoId
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
getPath () {
|
|
1027
|
+
return this.path
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
getFullPath () {
|
|
1031
|
+
return path.join(this.repoId, this.path)
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
getId () {
|
|
1035
|
+
return this.getPath()
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
reset () {
|
|
1039
|
+
this.previousContent = this.content
|
|
1040
|
+
this.content = null
|
|
1041
|
+
this.modified = false
|
|
1042
|
+
return this
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
rollback () {
|
|
1046
|
+
this.content = this.previousContent
|
|
1047
|
+
return this
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
setContent (content) {
|
|
1051
|
+
this.content = content
|
|
1052
|
+
return this
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
setContentFromFile (content) {
|
|
1056
|
+
this.content = eol.auto(content || '')
|
|
1057
|
+
return this
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
getContent () {
|
|
1061
|
+
return this.content
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
getContentForFile () {
|
|
1065
|
+
return eol.auto(this.content || '')
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
getContentLines () {
|
|
1069
|
+
return eol.split(this.getContentForFile())
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
setModifiedTime (modifiedTime) {
|
|
1073
|
+
this.modifiedTime = modifiedTime
|
|
1074
|
+
return this
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
setCreatedTime (createdTime) {
|
|
1078
|
+
this.createdTime = createdTime
|
|
1079
|
+
return this
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
getCreatedTime () {
|
|
1083
|
+
return this.createdTime
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
getModifiedTime () {
|
|
1087
|
+
return this.modifiedTime
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
setModified (modified) {
|
|
1091
|
+
this.modified = modified
|
|
1092
|
+
return this
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
isModified () {
|
|
1096
|
+
return this.modified
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
getType () {
|
|
1100
|
+
return this.constructor.name
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
getTasks () {
|
|
1104
|
+
return this.tasks
|
|
1105
|
+
}
|
|
1106
|
+
|
|
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
|
+
)
|
|
1113
|
+
)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
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
|
+
}
|
|
1134
|
+
|
|
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
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
isMarkDownFile () {
|
|
1145
|
+
return /^md|markdown$/i.test(this.getExt())
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
getLang () {
|
|
1149
|
+
var lang = this.languages[path.extname(this.path)]
|
|
1150
|
+
return lang || { name: 'text', symbol: '' }
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
getExt () {
|
|
1154
|
+
return path.extname(this.path).substring(1).toLowerCase()
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
isCodeFile () {
|
|
1158
|
+
var symbol = this.getLang().symbol
|
|
1159
|
+
return symbol && symbol !== ''
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
getBeforeText (line, pos) {
|
|
1163
|
+
return this.isCodeFile()
|
|
1164
|
+
? null
|
|
1165
|
+
: this.content.substring(this.getLinePos(line), pos)
|
|
1166
|
+
}
|
|
1167
|
+
|
|
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
|
+
}
|
|
1174
|
+
|
|
1175
|
+
getTaskContent ({
|
|
1176
|
+
config,
|
|
1177
|
+
content,
|
|
1178
|
+
inBlockComment,
|
|
1179
|
+
beforeText,
|
|
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
|
+
}
|
|
1194
|
+
|
|
1195
|
+
isCheckBoxTask (config, line, beforeText) {
|
|
1196
|
+
if (!config.isAddCheckBoxTasks()) return
|
|
1197
|
+
|
|
1198
|
+
const beforeTextCheckData = Task.getCheckedData(beforeText)
|
|
1199
|
+
if (!beforeTextCheckData) return
|
|
1200
|
+
|
|
1201
|
+
const lineCheckData = Task.getCheckedData(line)
|
|
1202
|
+
if (!lineCheckData) return
|
|
1203
|
+
|
|
1204
|
+
return lineCheckData.pad <= beforeTextCheckData.pad
|
|
1205
|
+
}
|
|
1206
|
+
|
|
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) + '[^{].*$'
|
|
1212
|
+
|
|
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
|
+
}
|
|
1222
|
+
|
|
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
|
+
}
|
|
1231
|
+
|
|
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
|
|
1239
|
+
}
|
|
1240
|
+
|
|
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
|
|
1248
|
+
}
|
|
1249
|
+
|
|
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
|
|
1261
|
+
}
|
|
1262
|
+
|
|
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
|
+
}
|
|
1270
|
+
|
|
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
|
+
}
|
|
1277
|
+
|
|
1278
|
+
hasTaskInText (config, text) {
|
|
1279
|
+
return hasTaskInText(config, text, this.isCodeFile())
|
|
1280
|
+
}
|
|
1281
|
+
|
|
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
|
+
}
|
|
1291
|
+
|
|
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
|
+
}
|
|
1306
|
+
|
|
1307
|
+
isSingleLineBlockComment (content) {
|
|
1308
|
+
return eol.split(content).length === 1
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
parseFrontMatter (config) {
|
|
1312
|
+
this.frontMatter = {
|
|
1313
|
+
tags: [],
|
|
1314
|
+
context: [],
|
|
1315
|
+
meta: {},
|
|
1316
|
+
}
|
|
1317
|
+
|
|
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
|
|
1341
|
+
}
|
|
1342
|
+
} catch (err) {
|
|
1343
|
+
logger.error(`Error processing front-matter in:${this.path}`, err)
|
|
1344
|
+
}
|
|
1345
|
+
return {}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
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
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
isWithinCodeSpanOrBlock(pos, content) {
|
|
1364
|
+
const positions = Task.getMarkdownCodePositions(content)
|
|
1365
|
+
return Task.isResultInMarkdownCode(positions, pos)
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
getMarkdownCodePositions (text) {
|
|
1369
|
+
const positions = [];
|
|
1370
|
+
const codeBlockRe = /```[\s\S]*?```/g;
|
|
1371
|
+
const inlineCodeRe = /`[^`]*`/g;
|
|
1372
|
+
|
|
1373
|
+
let match;
|
|
1374
|
+
|
|
1375
|
+
// Find code blocks
|
|
1376
|
+
while ((match = codeBlockRe.exec(text)) !== null) {
|
|
1377
|
+
positions.push({ start: match.index, end: match.index + match[0].length });
|
|
1378
|
+
}
|
|
1379
|
+
|
|
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
|
+
}
|
|
1384
|
+
|
|
1385
|
+
return positions;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
isResultInMarkdownCode (positions, index) {
|
|
1389
|
+
return positions.some(pos => index >= pos.start && index < pos.end);
|
|
1390
|
+
};
|
|
1391
|
+
}
|