imdone-core 1.54.10 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/cli.js +114 -0
- package/lib/index.js +23 -0
- package/package.json +48 -66
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -37
- package/.github/workflows/ci.yml +0 -24
- package/.imdoneignore +0 -12
- package/.mocharc.json +0 -10
- package/.prettierrc +0 -6
- package/CHANGELOG.md +0 -160
- package/_navbar.md +0 -1
- package/cli.js +0 -74
- package/index-markdown.js +0 -55
- package/index.html +0 -184
- package/index.js +0 -1
- package/lib/ProjectContext.js +0 -42
- package/lib/adapters/date-time.js +0 -20
- package/lib/adapters/file-gateway.js +0 -127
- package/lib/adapters/git-download.js +0 -35
- package/lib/adapters/markdown.js +0 -168
- package/lib/adapters/parsers/DateLanguageParser.js +0 -10
- package/lib/adapters/parsers/content-transformer.js +0 -111
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +0 -37
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +0 -126
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +0 -17
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +0 -104
- package/lib/adapters/parsers/function-parser.js +0 -29
- package/lib/adapters/parsers/task/CardContentParser.js +0 -376
- package/lib/adapters/parsers/task/CheckStyleTaskParser.js +0 -28
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +0 -24
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +0 -27
- package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +0 -24
- package/lib/adapters/parsers/task/TaskParser.js +0 -38
- package/lib/adapters/readers/FileSystemProjectReader.js +0 -45
- package/lib/adapters/storage/config.js +0 -49
- package/lib/adapters/storage/tags.js +0 -30
- package/lib/adapters/yaml.js +0 -6
- package/lib/card.js +0 -300
- package/lib/config.js +0 -214
- package/lib/constants.js +0 -97
- package/lib/context/ApplicationContext.js +0 -32
- package/lib/context/StreamingApplicationContext.js +0 -31
- package/lib/controlers/SaveViewController.js +0 -10
- package/lib/default-settings.js +0 -37
- package/lib/domain/entities/FileProjectContext.js +0 -10
- package/lib/domain/entities/View.js +0 -30
- package/lib/domain/usecases/GetViewsUsecase.js +0 -50
- package/lib/domain/usecases/OpenProjectUsecase.js +0 -13
- package/lib/domain/usecases/SaveViewUsecase.js +0 -57
- package/lib/file.js +0 -1395
- package/lib/languages.js +0 -114
- package/lib/list.js +0 -105
- package/lib/migrate-config.js +0 -165
- package/lib/mixins/repo-fs-store.js +0 -424
- package/lib/mixins/repo-watched-fs-store.js +0 -195
- package/lib/plugins/archive-plugin.js +0 -70
- package/lib/plugins/default-board-actions-plugin.js +0 -56
- package/lib/plugins/default-board-properties-plugin.js +0 -101
- package/lib/plugins/epic-plugin.js +0 -176
- package/lib/plugins/extension-plugin.js +0 -87
- package/lib/plugins/persist-tags-plugin.js +0 -13
- package/lib/plugins/plugin-manager.js +0 -421
- package/lib/plugins/plugin-registry.js +0 -61
- package/lib/project-factory.js +0 -45
- package/lib/project.js +0 -911
- package/lib/repository.js +0 -1746
- package/lib/task.js +0 -798
- package/lib/tools.js +0 -250
- package/lib/usecases/get-imdone-url.js +0 -6
- package/lib/usecases/get-project-tags.js +0 -16
- package/lib/usecases/get-tasks-in-file.js +0 -29
package/lib/repository.js
DELETED
|
@@ -1,1746 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var _isFunction = require('lodash.isfunction'),
|
|
4
|
-
_isUndefined = require('lodash.isundefined'),
|
|
5
|
-
_isString = require('lodash.isstring'),
|
|
6
|
-
_omit = require('lodash.omit'),
|
|
7
|
-
_reject = require('lodash.reject'),
|
|
8
|
-
_noop = require('lodash.noop'),
|
|
9
|
-
_some = require('lodash.some'),
|
|
10
|
-
_remove = require('lodash.remove'),
|
|
11
|
-
_groupBy = require('lodash.groupby'),
|
|
12
|
-
_where = require('lodash.where'),
|
|
13
|
-
_template = require('lodash.template'),
|
|
14
|
-
_union = require('lodash.union'),
|
|
15
|
-
events = require('events'),
|
|
16
|
-
languages = require('./languages'),
|
|
17
|
-
util = require('util'),
|
|
18
|
-
async = require('async'),
|
|
19
|
-
path = require('path'),
|
|
20
|
-
ignore = require('ignore'),
|
|
21
|
-
File = require('./file'),
|
|
22
|
-
Config = require('./config'),
|
|
23
|
-
eol = require('eol'),
|
|
24
|
-
tools = require('./tools'),
|
|
25
|
-
inMixinsNoop = tools.inMixinsNoop,
|
|
26
|
-
constants = require('./constants'),
|
|
27
|
-
log = require('debug')('imdone-core:Repository'),
|
|
28
|
-
List = require('./list'),
|
|
29
|
-
monquery = require('monquery'),
|
|
30
|
-
sift = require('sift'),
|
|
31
|
-
fastSort = require('fast-sort/dist/sort.js'),
|
|
32
|
-
JSONfns = require('json-fns'),
|
|
33
|
-
Task = require('./task'),
|
|
34
|
-
newCard = require('./card'),
|
|
35
|
-
{ replaceDateLanguage } = require('./adapters/parsers/DateLanguageParser'),
|
|
36
|
-
{ getRawTask, isNumber, LIST_NAME_PATTERN } = require('./adapters/parsers/task/CardContentParser'),
|
|
37
|
-
XRegExp = require('xregexp'),
|
|
38
|
-
promisify = require('util').promisify
|
|
39
|
-
|
|
40
|
-
const appContext = require('./context/ApplicationContext')
|
|
41
|
-
|
|
42
|
-
var ERRORS = constants.ERRORS,
|
|
43
|
-
DEFAULT_FILE_PATTERN = constants.DEFAULT_FILE_PATTERN,
|
|
44
|
-
ASYNC_LIMIT = constants.ASYNC_LIMIT,
|
|
45
|
-
DEFAULT_SORT = [{ asc: u => u.order }, { asc: u => u.text }]
|
|
46
|
-
|
|
47
|
-
// Emits task.found, list.found, file.update and file.delete, file.processed, files.saved
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* A Repository is a file system directory in which to look for tasks in files. The Repository manages all the files
|
|
51
|
-
* and lists in it's path.
|
|
52
|
-
*
|
|
53
|
-
* @constructor
|
|
54
|
-
* @param {} path
|
|
55
|
-
* @param {} config
|
|
56
|
-
* @return
|
|
57
|
-
*/
|
|
58
|
-
|
|
59
|
-
function Repository(_path, config) {
|
|
60
|
-
var self = this
|
|
61
|
-
|
|
62
|
-
events.EventEmitter.call(this)
|
|
63
|
-
// path is the root path of the repository
|
|
64
|
-
this.config = config
|
|
65
|
-
this.path = _path
|
|
66
|
-
this.files = []
|
|
67
|
-
this.languages = languages
|
|
68
|
-
this.allMeta = {}
|
|
69
|
-
this.metaKeys = new Set()
|
|
70
|
-
this.allTags = new Set()
|
|
71
|
-
this.allTopics = new Set()
|
|
72
|
-
this.allContexts = new Set()
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
util.inherits(Repository, events.EventEmitter)
|
|
76
|
-
|
|
77
|
-
Repository.prototype.init = function (cb) {
|
|
78
|
-
cb(null)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
Repository.prototype.refresh = function (cb) {
|
|
82
|
-
cb = tools.cb(cb)
|
|
83
|
-
this.config.dirty = true
|
|
84
|
-
this.loadConfig((err, config) => {
|
|
85
|
-
if (err) {
|
|
86
|
-
return cb(err)
|
|
87
|
-
}
|
|
88
|
-
this.config = config
|
|
89
|
-
this.files = []
|
|
90
|
-
this.allMeta = {}
|
|
91
|
-
this.metaKeys = new Set()
|
|
92
|
-
this.allTags = new Set()
|
|
93
|
-
this.allTopics = new Set()
|
|
94
|
-
this.allContexts = new Set()
|
|
95
|
-
this.readFiles((err, files) => {
|
|
96
|
-
if (err) {
|
|
97
|
-
this.emit('initialized', { ok: false })
|
|
98
|
-
return cb(err)
|
|
99
|
-
}
|
|
100
|
-
this.emit('initialized', { ok: true, lists: this.getTasksByList() })
|
|
101
|
-
if (cb) cb(err, files)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Description
|
|
108
|
-
* @method destroy
|
|
109
|
-
* @return
|
|
110
|
-
*/
|
|
111
|
-
Repository.prototype.destroy = function () {
|
|
112
|
-
this.destroyed = true
|
|
113
|
-
this.removeAllListeners()
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Description
|
|
118
|
-
* @method getId
|
|
119
|
-
* @return CallExpression
|
|
120
|
-
*/
|
|
121
|
-
Repository.prototype.getId = function () {
|
|
122
|
-
return this.getPath()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
Repository.prototype.getProject = function () {
|
|
126
|
-
return this.project
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
Repository.prototype.getDisplayName = function () {
|
|
130
|
-
return path.basename(this.path)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
Repository.prototype.emitFileUpdate = function (file, force) {
|
|
134
|
-
if (force || this.shouldEmitFileUpdate(file)) this.emit('file.update', file)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
Repository.prototype.shouldEmitFileUpdate = function (file) {
|
|
138
|
-
if (this.moving) return
|
|
139
|
-
if (this.lastMovedFiles) {
|
|
140
|
-
var index = this.lastMovedFiles.indexOf(file)
|
|
141
|
-
if (index > -1) {
|
|
142
|
-
this.lastMovedFiles.splice(index, 1)
|
|
143
|
-
} else {
|
|
144
|
-
if (file && file.updated) return true
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
if (file && file.updated) return true
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
Repository.prototype.emitConfigUpdate = function (data) {
|
|
152
|
-
if (this.savingConfig) return
|
|
153
|
-
process.nextTick(() => {
|
|
154
|
-
this.emit('config.update', data)
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Description
|
|
160
|
-
* @method createListeners
|
|
161
|
-
* @return
|
|
162
|
-
*/
|
|
163
|
-
Repository.prototype.createListeners = function () {
|
|
164
|
-
if (this.taskListener) return
|
|
165
|
-
var self = this
|
|
166
|
-
/**
|
|
167
|
-
* Description
|
|
168
|
-
* @method taskFoundListener
|
|
169
|
-
* @param {} task
|
|
170
|
-
* @return
|
|
171
|
-
*/
|
|
172
|
-
this.taskListener = function (event, task) {
|
|
173
|
-
if (!self.listExists(task.list) && self.config.includeList(task.list)) {
|
|
174
|
-
const list = new List({ name: task.list })
|
|
175
|
-
self.addList(list)
|
|
176
|
-
self.emit('list.found', list)
|
|
177
|
-
self.saveConfig()
|
|
178
|
-
}
|
|
179
|
-
Object.keys(task.allMeta).forEach(key => self.metaKeys.add(key))
|
|
180
|
-
task.topics.forEach(topic => self.allTopics.add(topic))
|
|
181
|
-
task.allTags.forEach(tag => self.allTags.add(tag))
|
|
182
|
-
task.allContext.forEach(context => self.allContexts.add(context))
|
|
183
|
-
self.allMeta = self.addAllMeta(task.allMeta)
|
|
184
|
-
self.emit(event, task)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
this.taskFoundListener = function (task) {
|
|
188
|
-
self.taskListener('task.found', task)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.taskModifiedListener = function (task) {
|
|
192
|
-
self.taskListener('task.modified', task)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
Repository.prototype.addAllMeta = function (meta) {
|
|
197
|
-
const allMeta = Object.assign({}, this.allMeta)
|
|
198
|
-
Object.keys(meta).forEach((key) => {
|
|
199
|
-
if (!allMeta[key]) {
|
|
200
|
-
allMeta[key] = meta[key]
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
allMeta[key] = _union(allMeta[key], meta[key])
|
|
204
|
-
})
|
|
205
|
-
return allMeta
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Description
|
|
210
|
-
* @method addList
|
|
211
|
-
* @param {} list
|
|
212
|
-
* @return
|
|
213
|
-
*/
|
|
214
|
-
Repository.prototype.addList = async function (list, cb) {
|
|
215
|
-
cb = tools.cb(cb);
|
|
216
|
-
if (this.listExists(list.name)) return cb();
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
list = _omit(list, 'tasks');
|
|
220
|
-
this.config.lists.push(new List(list));
|
|
221
|
-
|
|
222
|
-
if (!list.filter && !/[a-z]/.test(list.name)) {
|
|
223
|
-
const codeList = list.name.replace(/\s+/g, '-').toUpperCase();
|
|
224
|
-
if (!this.config.code.include_lists.find((name) => name === codeList)) {
|
|
225
|
-
this.config.code.include_lists.push(codeList);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
await promisify(this.saveConfig).bind(this)();
|
|
230
|
-
this.emit('list.modified', list);
|
|
231
|
-
cb();
|
|
232
|
-
} catch (err) {
|
|
233
|
-
cb(err);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
Repository.prototype.removeList = function (list, cb) {
|
|
238
|
-
return new Promise((resolve, reject) => {
|
|
239
|
-
var fn = (err) => {
|
|
240
|
-
if (err) return cb && cb(err) || reject(err)
|
|
241
|
-
this.emit('list.modified', list)
|
|
242
|
-
resolve()
|
|
243
|
-
}
|
|
244
|
-
if (!this.listExists(list)) return fn()
|
|
245
|
-
|
|
246
|
-
var lists = _reject(this.getLists(), { name: list })
|
|
247
|
-
if (this.config.code && this.config.code.include_lists) {
|
|
248
|
-
this.config.code.include_lists = _reject(this.config.code.include_lists, list)
|
|
249
|
-
}
|
|
250
|
-
this.setLists(lists)
|
|
251
|
-
this.saveConfig(fn)
|
|
252
|
-
})
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Description
|
|
257
|
-
* @method getPath
|
|
258
|
-
* @return MemberExpression
|
|
259
|
-
*/
|
|
260
|
-
Repository.prototype.getPath = function () {
|
|
261
|
-
return this.path
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Description
|
|
266
|
-
* @method getConfig
|
|
267
|
-
* @return MemberExpression
|
|
268
|
-
*/
|
|
269
|
-
Repository.prototype.getConfig = function () {
|
|
270
|
-
return this.config
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Description
|
|
275
|
-
* @method getLists
|
|
276
|
-
* @return MemberExpression
|
|
277
|
-
*/
|
|
278
|
-
Repository.prototype.getLists = function () {
|
|
279
|
-
const config = this.getConfig()
|
|
280
|
-
return config ? config.lists.map(obj => ({...obj})) : []
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
Repository.prototype.getVisibleLists = function () {
|
|
284
|
-
return _reject(this.getLists(), 'hidden')
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
Repository.prototype.isListVisible = function (name) {
|
|
288
|
-
return this.getVisibleLists.find((list) => list.name === name)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Description
|
|
293
|
-
* @method setLists
|
|
294
|
-
* @param {} lists
|
|
295
|
-
* @return ThisExpression
|
|
296
|
-
*/
|
|
297
|
-
Repository.prototype.setLists = function (lists) {
|
|
298
|
-
this.config.lists = lists.map((list) => {
|
|
299
|
-
return new List(list)
|
|
300
|
-
})
|
|
301
|
-
return this
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Description
|
|
306
|
-
* @method listExists
|
|
307
|
-
* @param {} name
|
|
308
|
-
* @return BinaryExpression
|
|
309
|
-
*/
|
|
310
|
-
Repository.prototype.listExists = function (name) {
|
|
311
|
-
return this.getConfig().listExists(name)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Save the config file (Implemented in mixins)
|
|
316
|
-
*
|
|
317
|
-
* @method saveConfig
|
|
318
|
-
* @param {} cb
|
|
319
|
-
* @return
|
|
320
|
-
*/
|
|
321
|
-
Repository.prototype.saveConfig = function (cb) {
|
|
322
|
-
inMixinsNoop(cb)
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Load the config file (Implemented in mixins)
|
|
327
|
-
*
|
|
328
|
-
* @method loadConfig
|
|
329
|
-
* @return MemberExpression
|
|
330
|
-
*/
|
|
331
|
-
Repository.prototype.loadConfig = function (cb) {
|
|
332
|
-
inMixinsNoop(cb)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
Repository.prototype.migrateTasksByConfig = function (
|
|
336
|
-
oldConfig,
|
|
337
|
-
newConfig,
|
|
338
|
-
cb
|
|
339
|
-
) {
|
|
340
|
-
if (!oldConfig || !newConfig) return cb()
|
|
341
|
-
const oldMetaSep = oldConfig.getMetaSep()
|
|
342
|
-
const newMetaSep = newConfig.getMetaSep()
|
|
343
|
-
if (oldMetaSep === newMetaSep) return cb()
|
|
344
|
-
async.eachLimit(
|
|
345
|
-
this.getFiles(),
|
|
346
|
-
ASYNC_LIMIT,
|
|
347
|
-
(file, cb) => {
|
|
348
|
-
const tasks = fastSort(file.tasks).desc(u => u.line)
|
|
349
|
-
async.eachSeries(
|
|
350
|
-
tasks,
|
|
351
|
-
(task, cb) => {
|
|
352
|
-
if (!Task.isTask(task)) return cb()
|
|
353
|
-
task.replaceMetaSep(oldMetaSep, newMetaSep)
|
|
354
|
-
this.modifyTask(task, false, cb)
|
|
355
|
-
},
|
|
356
|
-
(err) => {
|
|
357
|
-
if (err) return cb(err)
|
|
358
|
-
if (!file.isModified() || file.getContent().trim() === '') return cb()
|
|
359
|
-
this.writeFile(file, (err, file) => {
|
|
360
|
-
this.resetFile(file)
|
|
361
|
-
if (err) return cb(err)
|
|
362
|
-
cb(null, file)
|
|
363
|
-
})
|
|
364
|
-
}
|
|
365
|
-
)
|
|
366
|
-
},
|
|
367
|
-
cb
|
|
368
|
-
)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Get the full path from a relative path
|
|
373
|
-
*
|
|
374
|
-
* @method getFullPath
|
|
375
|
-
* @param {} file
|
|
376
|
-
* @return String
|
|
377
|
-
*/
|
|
378
|
-
Repository.prototype.getFullPath = function (file) {
|
|
379
|
-
if (File.isFile(file)) file = file.path
|
|
380
|
-
if (file.indexOf(this.path) === 0) return file
|
|
381
|
-
try {
|
|
382
|
-
var fullPath = path.join(this.path, file)
|
|
383
|
-
return fullPath
|
|
384
|
-
} catch (e) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
util.format(
|
|
387
|
-
'Error getting full path for file:%s and repo path:%s',
|
|
388
|
-
file,
|
|
389
|
-
this.path
|
|
390
|
-
)
|
|
391
|
-
)
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Get the relative path from repository root
|
|
397
|
-
*
|
|
398
|
-
* @method getRelativePath
|
|
399
|
-
* @param {} fullPath
|
|
400
|
-
* @return String
|
|
401
|
-
*/
|
|
402
|
-
Repository.prototype.getRelativePath = function (fullPath) {
|
|
403
|
-
if (path.sep === '\\') {
|
|
404
|
-
fullPath = fullPath.replace(/\//g, path.sep)
|
|
405
|
-
}
|
|
406
|
-
if (!fullPath.startsWith(this.path)) return fullPath
|
|
407
|
-
try {
|
|
408
|
-
var relPath = path.relative(this.path, fullPath)
|
|
409
|
-
return relPath
|
|
410
|
-
} catch (e) {
|
|
411
|
-
throw new Error(
|
|
412
|
-
util.format(
|
|
413
|
-
'Error getting relative path for file:%s and repo path:%s',
|
|
414
|
-
fullPath,
|
|
415
|
-
this.path
|
|
416
|
-
)
|
|
417
|
-
)
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Is this file OK? Implemented in mixins
|
|
423
|
-
*
|
|
424
|
-
* @method fileOK
|
|
425
|
-
* @param {} file
|
|
426
|
-
* @param {} includeDirs
|
|
427
|
-
* @return stat
|
|
428
|
-
*/
|
|
429
|
-
Repository.prototype.fileOK = function (file, includeDirs, cb) {
|
|
430
|
-
if (_isFunction(includeDirs)) cb = includeDirs
|
|
431
|
-
cb(null, true)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
Repository.prototype.setIgnores = function (ignores) {
|
|
435
|
-
// console.info('ignore patterns:', ignores)
|
|
436
|
-
this.ignorePatterns = ignores
|
|
437
|
-
this.ignore = ignore().add(ignores)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Should the relative path be included.
|
|
442
|
-
*
|
|
443
|
-
* @method shouldInclude
|
|
444
|
-
* @param {} relPath
|
|
445
|
-
* @return exclude
|
|
446
|
-
*/
|
|
447
|
-
Repository.prototype.shouldInclude = function (relPath) {
|
|
448
|
-
relPath = this.getRelativePath(relPath)
|
|
449
|
-
let include = true
|
|
450
|
-
if (this.config.markdownOnly && relPath) {
|
|
451
|
-
include = File.isMarkdown(relPath)
|
|
452
|
-
}
|
|
453
|
-
if (this.ignore) {
|
|
454
|
-
try {
|
|
455
|
-
include = include && (relPath === '' || !this.ignore.ignores(relPath))
|
|
456
|
-
} catch (e) {
|
|
457
|
-
console.error(
|
|
458
|
-
`Failed to check ignore status for dir: [${relPath}] in: [${this.path}]. It will be included.`,
|
|
459
|
-
e
|
|
460
|
-
)
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return include
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Add or replace a file in the files reference array
|
|
469
|
-
*
|
|
470
|
-
* @method addFile
|
|
471
|
-
* @param {} file
|
|
472
|
-
* @return MemberExpression
|
|
473
|
-
*/
|
|
474
|
-
Repository.prototype.addFile = function (file, cb) {
|
|
475
|
-
if (this.destroyed) return cb(new Error('destroyed'))
|
|
476
|
-
var self = this
|
|
477
|
-
this.fileOK(file, function (err, ok) {
|
|
478
|
-
if (err) return cb(err)
|
|
479
|
-
if (ok) {
|
|
480
|
-
var index = self.files.findIndex(({ path }) => path === file.path)
|
|
481
|
-
if (index > -1) {
|
|
482
|
-
self.files[index] = file
|
|
483
|
-
} else {
|
|
484
|
-
self.files.push(file)
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
cb(err, self.files)
|
|
488
|
-
})
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Remove a file from the files refrence array
|
|
493
|
-
*
|
|
494
|
-
* @method removeFile
|
|
495
|
-
* @param {} file
|
|
496
|
-
* @return MemberExpression
|
|
497
|
-
*/
|
|
498
|
-
Repository.prototype.removeFile = function (file) {
|
|
499
|
-
if (!File.isFile(file)) throw new Error(ERRORS.NOT_A_FILE)
|
|
500
|
-
_remove(this.files, f => f.path === file.path)
|
|
501
|
-
|
|
502
|
-
return this.files
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Description
|
|
507
|
-
* @method getFile
|
|
508
|
-
* @param {} path
|
|
509
|
-
* @return CallExpression
|
|
510
|
-
*/
|
|
511
|
-
Repository.prototype.getFile = function (path) {
|
|
512
|
-
path = this.getRelativePath(path)
|
|
513
|
-
return this.files.find((file) => file.path === path)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
Repository.prototype.getTask = function (id) {
|
|
517
|
-
if (Task.isTask(id)) {
|
|
518
|
-
let task = id
|
|
519
|
-
if (!task.meta.id) return
|
|
520
|
-
return this.getTasks().find((existingTask) => {
|
|
521
|
-
return existingTask.meta.id && existingTask.meta.id[0] == task.meta.id[0]
|
|
522
|
-
})
|
|
523
|
-
} else {
|
|
524
|
-
let task = this.getTasks().find((task) => task.id === id)
|
|
525
|
-
if (!task) return
|
|
526
|
-
return this.getFile(task.source.path)
|
|
527
|
-
.getTasks()
|
|
528
|
-
.find((task) => task.id === id)
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Description
|
|
534
|
-
* @method getFileForTask
|
|
535
|
-
* @param {} task
|
|
536
|
-
* @return CallExpression
|
|
537
|
-
*/
|
|
538
|
-
Repository.prototype.getFileForTask = function (task) {
|
|
539
|
-
return this.getFile(task.getSource().path)
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Descriptione
|
|
544
|
-
* @method getFiles
|
|
545
|
-
* @param {} paths
|
|
546
|
-
* @return CallExpression
|
|
547
|
-
*/
|
|
548
|
-
Repository.prototype.getFiles = function (paths) {
|
|
549
|
-
if (_isUndefined(paths)) return fastSort(this.files).asc(u => u.path)
|
|
550
|
-
return this.files.filter((file) => {
|
|
551
|
-
return paths.includes(file.path)
|
|
552
|
-
})
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
Repository.prototype.getFilesWithTasks = function () {
|
|
556
|
-
const files = this.files.filter((file) => file.getTasks().length > 0)
|
|
557
|
-
return fastSort(files).asc(u => u.path)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
Repository.prototype.resetFile = function (file) {
|
|
561
|
-
file.reset()
|
|
562
|
-
file.removeListener('task.found', this.taskFoundListener)
|
|
563
|
-
file.removeListener('task.modified', this.taskModifiedListener)
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
Repository.prototype.extractTasks = function (file, cb) {
|
|
567
|
-
cb = tools.cb(cb)
|
|
568
|
-
var self = this
|
|
569
|
-
var extract = (file) => {
|
|
570
|
-
file.on('task.found', self.taskFoundListener)
|
|
571
|
-
file.on('task.modified', self.taskModifiedListener)
|
|
572
|
-
const fileContent = file.content
|
|
573
|
-
file.extractAndTransformTasks(self.getConfig())
|
|
574
|
-
if (!file.isModified() || fileContent === file.content) {
|
|
575
|
-
this.resetFile(file)
|
|
576
|
-
return cb(null, file)
|
|
577
|
-
}
|
|
578
|
-
file.extractTasks(self.getConfig())
|
|
579
|
-
self.writeFile(file, (err, file) => {
|
|
580
|
-
this.resetFile(file)
|
|
581
|
-
if (err) return cb(err)
|
|
582
|
-
cb(null, file)
|
|
583
|
-
})
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (file.content === null) {
|
|
587
|
-
this.readFileContent(file, function (err, file) {
|
|
588
|
-
if (err) return cb(err)
|
|
589
|
-
extract(file)
|
|
590
|
-
})
|
|
591
|
-
} else extract(file)
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* Implemented in mixins
|
|
596
|
-
* @method writeFile
|
|
597
|
-
* @param {} file
|
|
598
|
-
* @param {} cb
|
|
599
|
-
* @return
|
|
600
|
-
*/
|
|
601
|
-
Repository.prototype.writeFile = function (file, cb) {
|
|
602
|
-
inMixinsNoop(cb)
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Implemented in mixins
|
|
607
|
-
* @method getFilesInPath
|
|
608
|
-
* @param {} includeDirs
|
|
609
|
-
* @return CallExpression
|
|
610
|
-
*/
|
|
611
|
-
|
|
612
|
-
Repository.prototype.getFilesInPath = function (includeDirs, cb) {
|
|
613
|
-
inMixinsNoop(cb)
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Implemented in mixins
|
|
618
|
-
* @method readFileContent
|
|
619
|
-
* @param {} file
|
|
620
|
-
* @param {} cb
|
|
621
|
-
* @return
|
|
622
|
-
*/
|
|
623
|
-
Repository.prototype.readFileContent = function (file, cb) {
|
|
624
|
-
inMixinsNoop(cb)
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* Description
|
|
629
|
-
* @method readFile
|
|
630
|
-
* @param {} file
|
|
631
|
-
* @param {} cb
|
|
632
|
-
* @return
|
|
633
|
-
*/
|
|
634
|
-
Repository.prototype.readFile = function (file, cb) {
|
|
635
|
-
const checksum = this.checksum || function () {}
|
|
636
|
-
cb = tools.cb(cb)
|
|
637
|
-
if (!File.isFile(file)) return cb(new Error(ERRORS.NOT_A_FILE))
|
|
638
|
-
if (file.deleted) return cb(null, file)
|
|
639
|
-
var self = this
|
|
640
|
-
|
|
641
|
-
var currentChecksum = file.checksum
|
|
642
|
-
const filePath = file.path
|
|
643
|
-
if (!/\.\.(\/|\\)/.test(filePath)) {
|
|
644
|
-
this.readFileContent(file, (err, file) => {
|
|
645
|
-
if (err) return cb(new Error('Unable to read file:' + filePath))
|
|
646
|
-
// BACKLOG Allow user to assign a module for content transformation
|
|
647
|
-
// <!--
|
|
648
|
-
// order:-800
|
|
649
|
-
// -->
|
|
650
|
-
file.checksum = checksum(file.getContent())
|
|
651
|
-
file.updated = currentChecksum !== file.checksum
|
|
652
|
-
if (file.updated)
|
|
653
|
-
self.extractTasks(file, (err) => {
|
|
654
|
-
if (err) return cb(err)
|
|
655
|
-
self.addFile(file, (err) => {
|
|
656
|
-
if (err) log('error:', err)
|
|
657
|
-
log(`File added. repo: ${self.getPath()} file-path: ${file.path}`)
|
|
658
|
-
if (err) return cb(err)
|
|
659
|
-
cb(null, file)
|
|
660
|
-
})
|
|
661
|
-
})
|
|
662
|
-
else cb(null, file)
|
|
663
|
-
})
|
|
664
|
-
} else return cb(new Error('Unable to read file:' + file))
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Description
|
|
669
|
-
* @method readFiles
|
|
670
|
-
* @param {} files
|
|
671
|
-
* @param {} cb
|
|
672
|
-
* @return
|
|
673
|
-
*/
|
|
674
|
-
Repository.prototype.readFiles = function (files, cb) {
|
|
675
|
-
if (arguments.length === 0) {
|
|
676
|
-
cb = _noop
|
|
677
|
-
files = undefined
|
|
678
|
-
} else if (_isFunction(files)) {
|
|
679
|
-
cb = files
|
|
680
|
-
files = undefined
|
|
681
|
-
} else if (files && !Array.isArray(files))
|
|
682
|
-
return cb(new Error('files must be an array of files or undefined'))
|
|
683
|
-
|
|
684
|
-
cb = tools.cb(cb)
|
|
685
|
-
|
|
686
|
-
var self = this
|
|
687
|
-
|
|
688
|
-
this.allMeta = {}
|
|
689
|
-
this.metaKeys = new Set()
|
|
690
|
-
this.allTags = new Set()
|
|
691
|
-
this.allContexts = new Set()
|
|
692
|
-
|
|
693
|
-
var exec = function (files) {
|
|
694
|
-
var completed = 0
|
|
695
|
-
if (files.length < 1) return cb(null, [])
|
|
696
|
-
async.eachLimit(
|
|
697
|
-
files,
|
|
698
|
-
ASYNC_LIMIT,
|
|
699
|
-
function (file, cb) {
|
|
700
|
-
self.emit('file.reading', { path: file.path })
|
|
701
|
-
self.readFile(file, function (err, file) {
|
|
702
|
-
if (err) return cb(err)
|
|
703
|
-
completed++
|
|
704
|
-
self.emit('file.read', {
|
|
705
|
-
path: file.path,
|
|
706
|
-
completed: completed,
|
|
707
|
-
})
|
|
708
|
-
cb()
|
|
709
|
-
})
|
|
710
|
-
},
|
|
711
|
-
function (err) {
|
|
712
|
-
cb(err, files)
|
|
713
|
-
}
|
|
714
|
-
)
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (files === undefined) {
|
|
718
|
-
if (this.files && this.files.length > 0) {
|
|
719
|
-
exec(this.files)
|
|
720
|
-
} else {
|
|
721
|
-
this.getFilesInPath(false, (err, files) => {
|
|
722
|
-
if (err) return cb(err)
|
|
723
|
-
self.files = files
|
|
724
|
-
const filesToInclude = files.map((file) => file.path)
|
|
725
|
-
const fileStats = {
|
|
726
|
-
count: filesToInclude.length,
|
|
727
|
-
files: filesToInclude,
|
|
728
|
-
}
|
|
729
|
-
this.emit('files.found', fileStats)
|
|
730
|
-
exec(files)
|
|
731
|
-
})
|
|
732
|
-
}
|
|
733
|
-
} else exec(files)
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Implemented in mixins
|
|
738
|
-
* @method deleteFile
|
|
739
|
-
* @param {} path
|
|
740
|
-
* @param {} cb
|
|
741
|
-
* @return
|
|
742
|
-
*/
|
|
743
|
-
Repository.prototype.deleteFile = function (path, cb) {
|
|
744
|
-
inMixinsNoop(cb)
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Description
|
|
749
|
-
* @method hasDefaultFile
|
|
750
|
-
* @return CallExpression
|
|
751
|
-
*/
|
|
752
|
-
Repository.prototype.hasDefaultFile = function () {
|
|
753
|
-
return _some(this.getFiles(), function (file) {
|
|
754
|
-
var regex = new RegExp(DEFAULT_FILE_PATTERN, 'i')
|
|
755
|
-
return regex.test(file.path)
|
|
756
|
-
})
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/**
|
|
760
|
-
* Description
|
|
761
|
-
* @method getDefaultFile
|
|
762
|
-
* @return file
|
|
763
|
-
*/
|
|
764
|
-
Repository.prototype.getDefaultFile = function () {
|
|
765
|
-
var files = fastSort(this.getFiles()).asc(u => u.path)
|
|
766
|
-
var file = files.reverse().find((file) => {
|
|
767
|
-
var regex = new RegExp(DEFAULT_FILE_PATTERN, 'i')
|
|
768
|
-
return regex.test(file.path)
|
|
769
|
-
})
|
|
770
|
-
return file
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
Repository.prototype.getList = function (name) {
|
|
774
|
-
return this.getLists().find((list) => list.name === name)
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
Repository.prototype.getListById = function(id, lists = this.getLists()) {
|
|
779
|
-
return lists.find((list) => list.id === id)
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
Repository.prototype.hideList = function (name, cb) {
|
|
783
|
-
var self = this
|
|
784
|
-
cb = tools.cb(cb)
|
|
785
|
-
var fn = function (err) {
|
|
786
|
-
if (!err) self.emit('list.modified', name)
|
|
787
|
-
cb(err)
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
var list = this.getList(name)
|
|
791
|
-
if (list) {
|
|
792
|
-
list.hidden = true
|
|
793
|
-
this.saveConfig(fn)
|
|
794
|
-
} else cb()
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
Repository.prototype.showList = function (name, cb) {
|
|
798
|
-
var self = this
|
|
799
|
-
cb = tools.cb(cb)
|
|
800
|
-
var fn = function (err) {
|
|
801
|
-
if (!err) self.emit('list.modified', name)
|
|
802
|
-
cb(err)
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
var list = this.getList(name)
|
|
806
|
-
if (list) {
|
|
807
|
-
list.hidden = false
|
|
808
|
-
this.saveConfig(fn)
|
|
809
|
-
} else cb()
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
/**
|
|
813
|
-
* Description
|
|
814
|
-
* @method moveList
|
|
815
|
-
* @param {} name
|
|
816
|
-
* @param {} pos
|
|
817
|
-
* @param {} cb
|
|
818
|
-
* @return
|
|
819
|
-
*/
|
|
820
|
-
Repository.prototype.moveList = function (name, pos, cb) {
|
|
821
|
-
var self = this
|
|
822
|
-
cb = tools.cb(cb)
|
|
823
|
-
var list = this.getList(name)
|
|
824
|
-
// Only modify the lists if the list name exists
|
|
825
|
-
var fn = function (err) {
|
|
826
|
-
if (!err) self.emit('list.modified', name)
|
|
827
|
-
cb(err)
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if (list) {
|
|
831
|
-
_remove(this.getLists(), { name: name })
|
|
832
|
-
this.getLists().splice(pos, 0, list)
|
|
833
|
-
this.saveConfig(fn)
|
|
834
|
-
} else cb()
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
Repository.prototype.toggleListIgnore = async function (name) {
|
|
838
|
-
return new Promise(async (resolve, reject) => {
|
|
839
|
-
var self = this
|
|
840
|
-
var list = this.getList(name)
|
|
841
|
-
if (!list) return reject(new Error('List not found'))
|
|
842
|
-
list.ignore = !list.ignore
|
|
843
|
-
await this.updateList(list.id, list)
|
|
844
|
-
this.saveConfig((err) => {
|
|
845
|
-
if (err) reject(err)
|
|
846
|
-
self.emit('list.modified', name)
|
|
847
|
-
resolve()
|
|
848
|
-
})
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
Repository.prototype.toggleList = async function (name) {
|
|
853
|
-
return new Promise(async (resolve, reject) => {
|
|
854
|
-
var self = this
|
|
855
|
-
var list = this.getList(name)
|
|
856
|
-
if (!list) return reject(new Error('List not found'))
|
|
857
|
-
list.hidden = !list.hidden
|
|
858
|
-
await this.updateList(list.id, list)
|
|
859
|
-
this.saveConfig((err) => {
|
|
860
|
-
if (err) reject(err)
|
|
861
|
-
self.emit('list.modified', name)
|
|
862
|
-
resolve()
|
|
863
|
-
})
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
Repository.prototype.updateList = function(id, {name, hidden, ignore, filter}) {
|
|
868
|
-
return new Promise((resolve, reject) => {
|
|
869
|
-
const lists = this.getLists()
|
|
870
|
-
const list = this.getListById(id, lists)
|
|
871
|
-
const oldName = list.name
|
|
872
|
-
const hasFilter = list.filter
|
|
873
|
-
list.name = name
|
|
874
|
-
list.hidden = hidden
|
|
875
|
-
list.ignore = ignore
|
|
876
|
-
if (hasFilter !== undefined) list.filter = filter
|
|
877
|
-
this.setLists(lists)
|
|
878
|
-
|
|
879
|
-
if (oldName === name || hasFilter) {
|
|
880
|
-
this.saveConfig(err => {
|
|
881
|
-
if (err) return reject(err)
|
|
882
|
-
resolve()
|
|
883
|
-
})
|
|
884
|
-
} else {
|
|
885
|
-
this.moveTasksBetweenLists(oldName, name, err => {
|
|
886
|
-
if (err) return reject(err)
|
|
887
|
-
resolve()
|
|
888
|
-
})
|
|
889
|
-
}
|
|
890
|
-
})
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
Repository.prototype.moveTasksBetweenLists = function(oldName, newName, cb) {
|
|
894
|
-
cb = tools.cb(cb)
|
|
895
|
-
// Modify the tasks
|
|
896
|
-
const tasksToModify = this.getTasksInList(oldName)
|
|
897
|
-
|
|
898
|
-
const cbfn = (err) => {
|
|
899
|
-
this.moving = false
|
|
900
|
-
if (err) return cb(err)
|
|
901
|
-
this.saveConfig((err) => {
|
|
902
|
-
if (err) return cb(err)
|
|
903
|
-
cb(null, { oldName, newName })
|
|
904
|
-
})
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const tasksByFile = {} // path: [files]
|
|
908
|
-
tasksToModify.forEach((task) => {
|
|
909
|
-
const filePath = task.path
|
|
910
|
-
if (!tasksByFile[filePath]) {
|
|
911
|
-
tasksByFile[filePath] = { file: this.getFileForTask(task), tasks: [] }
|
|
912
|
-
}
|
|
913
|
-
tasksByFile[filePath].tasks.push(task)
|
|
914
|
-
})
|
|
915
|
-
const modifyTasks = Object.values(tasksByFile).map(({ file, tasks }) => {
|
|
916
|
-
return (cb) => {
|
|
917
|
-
this.readFileContent(file, (err) => {
|
|
918
|
-
if (err) return cb(err)
|
|
919
|
-
try {
|
|
920
|
-
tasks.forEach((task) => {
|
|
921
|
-
task.list = newName
|
|
922
|
-
file.modifyTask(task, this.getConfig(), true)
|
|
923
|
-
})
|
|
924
|
-
} catch (err) {
|
|
925
|
-
return cb(err)
|
|
926
|
-
}
|
|
927
|
-
cb()
|
|
928
|
-
})
|
|
929
|
-
}
|
|
930
|
-
})
|
|
931
|
-
|
|
932
|
-
// BACKLOG Let's use saveModifiedFiles here id:15 gh:100 +fix
|
|
933
|
-
// <!--
|
|
934
|
-
// order:-810
|
|
935
|
-
// -->
|
|
936
|
-
|
|
937
|
-
this.moving = true
|
|
938
|
-
if (modifyTasks.length > 0) {
|
|
939
|
-
async.parallel(modifyTasks, (err) => {
|
|
940
|
-
if (err) return cb(err)
|
|
941
|
-
|
|
942
|
-
var fns = Object.values(tasksByFile).map(({ file }) => {
|
|
943
|
-
return (cb) => {
|
|
944
|
-
this.writeFile(file, cb)
|
|
945
|
-
}
|
|
946
|
-
})
|
|
947
|
-
|
|
948
|
-
if (fns.length > 0) {
|
|
949
|
-
async.parallel(fns, cbfn)
|
|
950
|
-
} else {
|
|
951
|
-
cbfn()
|
|
952
|
-
}
|
|
953
|
-
})
|
|
954
|
-
} else cbfn()
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
Repository.prototype.writeAndExtract = function (file, emit, cb) {
|
|
958
|
-
this.writeFile(file, emit, (err, file) => {
|
|
959
|
-
if (err) return cb(new Error('Unable to write file:' + file.path))
|
|
960
|
-
this.extractTasks(file, (err, file) => {
|
|
961
|
-
if (err) {
|
|
962
|
-
return cb(new Error('Unable to extract tasks for file:' + file.path))
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
this.addFile(file, (err) => {
|
|
966
|
-
if (err) {
|
|
967
|
-
err.message = `Unable to add file after extracting tasks: #{file.path}\n${err.message}`
|
|
968
|
-
return cb(err)
|
|
969
|
-
}
|
|
970
|
-
cb(null, file)
|
|
971
|
-
})
|
|
972
|
-
})
|
|
973
|
-
})
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
Repository.prototype.writeAndAdd = function (file, emit, cb) {
|
|
977
|
-
this.writeFile(file, emit, (err, file) => {
|
|
978
|
-
if (err) return cb(new Error('Unable to write file:' + file.path))
|
|
979
|
-
this.addFile(file, (err) => {
|
|
980
|
-
if (err) return cb(new Error('Unable to add file:' + file.path))
|
|
981
|
-
cb(null, file)
|
|
982
|
-
})
|
|
983
|
-
})
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
Repository.prototype.deleteTask = function (task, cb) {
|
|
987
|
-
if (!cb) throw new Error('task, callback required')
|
|
988
|
-
var self = this
|
|
989
|
-
var file = self.getFileForTask(task)
|
|
990
|
-
if (!file) return cb(null)
|
|
991
|
-
const execute = function (file) {
|
|
992
|
-
file.deleteTask(task, self.getConfig())
|
|
993
|
-
if (file.getContentForFile().trim() === '' && file.isMarkDownFile()) {
|
|
994
|
-
if (file.deleted) return
|
|
995
|
-
console.log('Deleting empty file:', file.path)
|
|
996
|
-
self.deleteFile(file.path, cb)
|
|
997
|
-
file.deleted = true
|
|
998
|
-
return
|
|
999
|
-
}
|
|
1000
|
-
self.writeAndExtract(file, true, cb)
|
|
1001
|
-
}
|
|
1002
|
-
if (!file.getContent()) {
|
|
1003
|
-
self.readFileContent(file, function (err, file) {
|
|
1004
|
-
if (err) return cb(err)
|
|
1005
|
-
execute(file)
|
|
1006
|
-
})
|
|
1007
|
-
} else {
|
|
1008
|
-
execute(file)
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
Repository.prototype.deleteTasks = function (tasks, cb) {
|
|
1012
|
-
if (!cb) throw new Error('tasks, callback required')
|
|
1013
|
-
var self = this
|
|
1014
|
-
|
|
1015
|
-
let files = _groupBy(tasks, (task) => task.source.path) // {path:tasks, path2:tasks}
|
|
1016
|
-
for (let [path, tasks] of Object.entries(files)) {
|
|
1017
|
-
files[path] = fastSort(tasks).desc(u => u.line)
|
|
1018
|
-
}
|
|
1019
|
-
async.eachSeries(
|
|
1020
|
-
Object.entries(files),
|
|
1021
|
-
([path, tasks], cb) => {
|
|
1022
|
-
const file = this.getFile(path)
|
|
1023
|
-
async.eachSeries(
|
|
1024
|
-
tasks,
|
|
1025
|
-
(task, cb) => {
|
|
1026
|
-
task = newCard(task, this.project, true)
|
|
1027
|
-
this.deleteTask(task, (err) => {
|
|
1028
|
-
cb(err || null, task)
|
|
1029
|
-
})
|
|
1030
|
-
},
|
|
1031
|
-
(err) => {
|
|
1032
|
-
cb(err)
|
|
1033
|
-
}
|
|
1034
|
-
)
|
|
1035
|
-
},
|
|
1036
|
-
(err) => {
|
|
1037
|
-
cb(err)
|
|
1038
|
-
}
|
|
1039
|
-
)
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
Repository.prototype.modifyTaskFromHtml = function (task, html, cb) {
|
|
1043
|
-
cb = tools.cb(cb)
|
|
1044
|
-
var file = this.getFileForTask(task)
|
|
1045
|
-
if (!file.getContent()) {
|
|
1046
|
-
this.readFileContent(file, (err, file) => {
|
|
1047
|
-
if (err) return cb(err)
|
|
1048
|
-
file.modifyTaskFromHtml(task, html)
|
|
1049
|
-
this.writeAndExtract(file, true, cb)
|
|
1050
|
-
})
|
|
1051
|
-
} else {
|
|
1052
|
-
file.modifyTaskFromHtml(task, html)
|
|
1053
|
-
this.writeAndExtract(file, true, cb)
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
Repository.prototype.modifyTaskFromContent = function (task, content, cb) {
|
|
1058
|
-
cb = tools.cb(cb)
|
|
1059
|
-
var file = this.getFileForTask(task)
|
|
1060
|
-
if (!file.getContent()) {
|
|
1061
|
-
this.readFileContent(file, (err, file) => {
|
|
1062
|
-
if (err) return cb(err)
|
|
1063
|
-
file.modifyTaskFromContent(task, content, this.getConfig())
|
|
1064
|
-
this.writeAndExtract(file, true, cb)
|
|
1065
|
-
})
|
|
1066
|
-
} else {
|
|
1067
|
-
file.modifyTaskFromContent(task, content, this.getConfig())
|
|
1068
|
-
this.writeAndExtract(file, true, cb)
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
Repository.prototype.getTaskContent = function({
|
|
1073
|
-
description,
|
|
1074
|
-
order,
|
|
1075
|
-
text,
|
|
1076
|
-
taskPrefix,
|
|
1077
|
-
taskSyntax,
|
|
1078
|
-
}) {
|
|
1079
|
-
const task = newCard(
|
|
1080
|
-
{
|
|
1081
|
-
description,
|
|
1082
|
-
meta: {},
|
|
1083
|
-
order,
|
|
1084
|
-
text,
|
|
1085
|
-
beforeText: taskPrefix,
|
|
1086
|
-
type: taskSyntax,
|
|
1087
|
-
},
|
|
1088
|
-
this.project
|
|
1089
|
-
)
|
|
1090
|
-
Task.prototype.updateOrderMeta.apply(task, [this.config])
|
|
1091
|
-
return task.description.join(eol.lf)
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
Repository.prototype.appendTask = function({file, content, list}, cb) {
|
|
1095
|
-
const config = this.getConfig()
|
|
1096
|
-
const interpretedTaskPrefix = _template(config.getTaskPrefix())({
|
|
1097
|
-
date: new Date(),
|
|
1098
|
-
}).trimEnd()
|
|
1099
|
-
const lines = eol.split(content)
|
|
1100
|
-
const text = lines[0]
|
|
1101
|
-
const taskSyntax = config.getNewCardSyntax()
|
|
1102
|
-
const taskPrefix = interpretedTaskPrefix ? `${interpretedTaskPrefix} ` : ''
|
|
1103
|
-
let fileContent = file.getContent()
|
|
1104
|
-
const journalTemplate = this.getConfig().journalTemplate
|
|
1105
|
-
fileContent = fileContent.trim()
|
|
1106
|
-
? fileContent
|
|
1107
|
-
: journalTemplate
|
|
1108
|
-
&& journalTemplate.trim()
|
|
1109
|
-
? journalTemplate + String(eol.lf)
|
|
1110
|
-
: '';
|
|
1111
|
-
|
|
1112
|
-
const length = fileContent.length
|
|
1113
|
-
const fileIsEmpty = length < 1
|
|
1114
|
-
const crlf = String(eol.crlf)
|
|
1115
|
-
let sep = fileContent.indexOf(crlf) > -1 ? crlf : String(eol.lf)
|
|
1116
|
-
if (fileContent.endsWith(sep) || fileIsEmpty) sep = ''
|
|
1117
|
-
const order = appContext().projectContext.getOrder(list)
|
|
1118
|
-
const description = lines.length > 1 ? Task.padDescription(lines.slice(1), taskPrefix) : []
|
|
1119
|
-
const { orderMeta, tokenPrefix } = config
|
|
1120
|
-
const rawTask = getRawTask({tokenPrefix, orderMeta, list, order, text, type: taskSyntax})
|
|
1121
|
-
const taskContent = config.orderMeta
|
|
1122
|
-
? this.getTaskContent({
|
|
1123
|
-
description,
|
|
1124
|
-
order,
|
|
1125
|
-
text,
|
|
1126
|
-
taskPrefix,
|
|
1127
|
-
taskSyntax,
|
|
1128
|
-
})
|
|
1129
|
-
: description.join(eol.lf)
|
|
1130
|
-
|
|
1131
|
-
let appendContent = File.trimBlankLines(
|
|
1132
|
-
`${taskPrefix}${rawTask}${eol.lf}${taskContent}`
|
|
1133
|
-
)
|
|
1134
|
-
|
|
1135
|
-
fileContent = `${fileContent}${sep}`
|
|
1136
|
-
const cardTerminator = "\n".repeat(2)
|
|
1137
|
-
file.setContent(`${fileContent}${appendContent}${cardTerminator}`)
|
|
1138
|
-
|
|
1139
|
-
this.writeAndExtract(file, true, (err, file) => {
|
|
1140
|
-
if (err) return cb(err)
|
|
1141
|
-
const task = file.getTasks().find(task => task.text === text && task.list === list)
|
|
1142
|
-
cb(null, file, task)
|
|
1143
|
-
})
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
Repository.prototype.addTaskToFile = function (filePath, list, content, cb) {
|
|
1147
|
-
cb = tools.cb(cb)
|
|
1148
|
-
const relPath = this.getRelativePath(filePath)
|
|
1149
|
-
let file = this.getFile(relPath)
|
|
1150
|
-
|
|
1151
|
-
if (file) {
|
|
1152
|
-
this.readFileContent(file, (err, file) => {
|
|
1153
|
-
if (err) return cb(err)
|
|
1154
|
-
this.appendTask({file, content, list}, cb)
|
|
1155
|
-
})
|
|
1156
|
-
} else {
|
|
1157
|
-
const modifiedTime = new Date()
|
|
1158
|
-
const createdTime = new Date()
|
|
1159
|
-
file = new File({
|
|
1160
|
-
repoId: this.path,
|
|
1161
|
-
filePath: relPath,
|
|
1162
|
-
content: '',
|
|
1163
|
-
modifiedTime,
|
|
1164
|
-
createdTime,
|
|
1165
|
-
project: this.project,
|
|
1166
|
-
})
|
|
1167
|
-
this.appendTask({file, content, list}, cb)
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
/**
|
|
1172
|
-
* Description
|
|
1173
|
-
* @method modifyTask
|
|
1174
|
-
* @param {} task
|
|
1175
|
-
* @return CallExpression
|
|
1176
|
-
*/
|
|
1177
|
-
Repository.prototype.modifyTask = function (task, writeFile, cb) {
|
|
1178
|
-
if (!Task.isTask(task)) return cb()
|
|
1179
|
-
if (_isFunction(writeFile)) {
|
|
1180
|
-
cb = writeFile
|
|
1181
|
-
writeFile = false
|
|
1182
|
-
}
|
|
1183
|
-
cb = tools.cb(cb)
|
|
1184
|
-
const config = this.getConfig()
|
|
1185
|
-
log(
|
|
1186
|
-
'Modifying Task... text:%s list:%s order:%d path:%s id:%s line:%d',
|
|
1187
|
-
task.text,
|
|
1188
|
-
task.list,
|
|
1189
|
-
task.order,
|
|
1190
|
-
task.source.path,
|
|
1191
|
-
task.id,
|
|
1192
|
-
task.line
|
|
1193
|
-
)
|
|
1194
|
-
var self = this
|
|
1195
|
-
let beforeModifyContent
|
|
1196
|
-
var file = this.getFileForTask(task)
|
|
1197
|
-
try {
|
|
1198
|
-
beforeModifyContent = file.getContent()
|
|
1199
|
-
} catch (e) {
|
|
1200
|
-
console.error(
|
|
1201
|
-
`Can't get file for task: {text:'${task.text}', path:'${task.source.path}', line:${task.line}}`
|
|
1202
|
-
)
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
const modifyTransformExtract = (cb) => {
|
|
1206
|
-
file.modifyTask(task, config, true)
|
|
1207
|
-
file.extractTasks(config)
|
|
1208
|
-
file.transformTask({config, modify:true, task})
|
|
1209
|
-
file.extractTasks(config)
|
|
1210
|
-
|
|
1211
|
-
if (!writeFile || beforeModifyContent === file.getContent()) return cb(null, task)
|
|
1212
|
-
|
|
1213
|
-
this.writeAndAdd(file, cb)
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
if (!beforeModifyContent) {
|
|
1217
|
-
this.readFileContent(file, (err) => {
|
|
1218
|
-
if (err) return cb(err)
|
|
1219
|
-
modifyTransformExtract(cb)
|
|
1220
|
-
})
|
|
1221
|
-
} else {
|
|
1222
|
-
modifyTransformExtract(cb)
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
Repository.prototype.setTaskPriority = function (_task, index, cb) {
|
|
1227
|
-
if (_task.order === '') return cb()
|
|
1228
|
-
_task.order = index * 10
|
|
1229
|
-
cb()
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
function getPreviousIndexWithDifferentOrder(tasks, pos) {
|
|
1233
|
-
let closestIndexWithOrder = -1
|
|
1234
|
-
|
|
1235
|
-
if (pos === 0) return closestIndexWithOrder
|
|
1236
|
-
|
|
1237
|
-
let lastTask = tasks[pos] || tasks [pos - 1]
|
|
1238
|
-
|
|
1239
|
-
for (
|
|
1240
|
-
let index = pos - 1;
|
|
1241
|
-
index >= 0 && closestIndexWithOrder < 0;
|
|
1242
|
-
index--
|
|
1243
|
-
) {
|
|
1244
|
-
const t = tasks[index]
|
|
1245
|
-
if (!tasksHaveSameOrder(lastTask, t)) closestIndexWithOrder = index
|
|
1246
|
-
lastTask = t
|
|
1247
|
-
}
|
|
1248
|
-
return closestIndexWithOrder
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
Repository.getPreviousIndexWithDifferentOrder = getPreviousIndexWithDifferentOrder
|
|
1252
|
-
|
|
1253
|
-
function getNextIndexWithDifferentOrder(tasks, pos) {
|
|
1254
|
-
let closestIndexWithOrder = -1
|
|
1255
|
-
|
|
1256
|
-
let lastTask = tasks[pos]
|
|
1257
|
-
|
|
1258
|
-
for (
|
|
1259
|
-
let index = pos;
|
|
1260
|
-
index < tasks.length && closestIndexWithOrder < 0;
|
|
1261
|
-
index++
|
|
1262
|
-
) {
|
|
1263
|
-
const t = tasks[index]
|
|
1264
|
-
if (!tasksHaveSameOrder(lastTask, t)) closestIndexWithOrder = index
|
|
1265
|
-
lastTask = t
|
|
1266
|
-
}
|
|
1267
|
-
return closestIndexWithOrder
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
Repository.getNextIndexWithDifferentOrder = getNextIndexWithDifferentOrder
|
|
1271
|
-
|
|
1272
|
-
function taskBeforeAndAfterHaveDifferentOrder(taskList, newPos) {
|
|
1273
|
-
const taskBefore = taskList[newPos - 1]
|
|
1274
|
-
const taskAfter = taskList[newPos]
|
|
1275
|
-
return taskBeforeHasOrder(taskList, newPos)
|
|
1276
|
-
&& taskAfterHasOrder(taskList, newPos)
|
|
1277
|
-
&& parseFloat(taskBefore.order) !== parseFloat(taskAfter.order)
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
function taskBeforeHasOrder(taskList, newPos) {
|
|
1281
|
-
const taskBefore = taskList[newPos - 1]
|
|
1282
|
-
return taskBefore && isNumber(taskBefore.order)
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
function taskAfterHasOrder(taskList, newPos) {
|
|
1286
|
-
const taskAfter = taskList[newPos]
|
|
1287
|
-
return taskAfter && isNumber(taskAfter.order)
|
|
1288
|
-
}
|
|
1289
|
-
function lastTaskHasOrder(taskList) {
|
|
1290
|
-
const lastTask = taskList[taskList.length - 1]
|
|
1291
|
-
return lastTask && isNumber(lastTask.order)
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
function getTasksToModify(task, taskList, newPos) {
|
|
1295
|
-
|
|
1296
|
-
const previousIndexWithDifferentOrder = getPreviousIndexWithDifferentOrder(taskList, newPos)
|
|
1297
|
-
const nextIndexWithDifferentOrder = getNextIndexWithDifferentOrder(taskList, newPos)
|
|
1298
|
-
const previousTaskWithOrder = taskList[previousIndexWithDifferentOrder]
|
|
1299
|
-
const nextTaskWithOrder = taskList[nextIndexWithDifferentOrder]
|
|
1300
|
-
|
|
1301
|
-
const startingOrder = previousTaskWithOrder
|
|
1302
|
-
? isNumber(previousTaskWithOrder.order) && previousTaskWithOrder.order || 0
|
|
1303
|
-
: 0;
|
|
1304
|
-
const endingOrder = nextTaskWithOrder
|
|
1305
|
-
? nextTaskWithOrder.order
|
|
1306
|
-
: null;
|
|
1307
|
-
|
|
1308
|
-
const start = previousIndexWithDifferentOrder > -1 ? previousIndexWithDifferentOrder + 1 : 0;
|
|
1309
|
-
const end = nextIndexWithDifferentOrder > -1 ? nextIndexWithDifferentOrder : newPos + 1;
|
|
1310
|
-
const changes = nextIndexWithDifferentOrder - previousIndexWithDifferentOrder + 1
|
|
1311
|
-
const increment = isNumber(endingOrder) ? (endingOrder - startingOrder) / changes : 10
|
|
1312
|
-
const tasksToModify = []
|
|
1313
|
-
let newOrder = startingOrder
|
|
1314
|
-
let order = 0
|
|
1315
|
-
if (taskList.length === 0) {
|
|
1316
|
-
order = 0
|
|
1317
|
-
} else if (newPos === 0) {
|
|
1318
|
-
order = taskList[0].order - 10
|
|
1319
|
-
} else if (newPos === taskList.length && lastTaskHasOrder(taskList)) {
|
|
1320
|
-
order = taskList[taskList.length - 1].order + 10
|
|
1321
|
-
} else if (taskBeforeAndAfterHaveDifferentOrder(taskList, newPos)) {
|
|
1322
|
-
order = (taskList[newPos].order - taskList[newPos - 1].order) / 2 + taskList[newPos - 1].order
|
|
1323
|
-
} else if (taskBeforeHasOrder(taskList, newPos) && !taskAfterHasOrder(taskList, newPos)) {
|
|
1324
|
-
order = taskList[newPos - 1].order + 10
|
|
1325
|
-
} else {
|
|
1326
|
-
for (let index = start; index < end; index++) {
|
|
1327
|
-
newOrder += increment
|
|
1328
|
-
if (index === newPos) {
|
|
1329
|
-
task.order = newOrder
|
|
1330
|
-
tasksToModify.push(task)
|
|
1331
|
-
newOrder += increment
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
if (!taskList[index]) continue
|
|
1335
|
-
taskList[index].order = newOrder
|
|
1336
|
-
tasksToModify.push(taskList[index])
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
if (tasksToModify.length === 0) {
|
|
1341
|
-
task.order = order
|
|
1342
|
-
tasksToModify.push(task)
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
return tasksToModify
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
Repository.getTasksToModify = getTasksToModify
|
|
1349
|
-
|
|
1350
|
-
function tasksHaveSameOrder(taskA, taskB) {
|
|
1351
|
-
return taskA && taskB && taskA.order + "" === taskB.order + ""
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
Repository.prototype.moveTask = function ({ task, newList = task.list, newPos }, cb) {
|
|
1355
|
-
if (!Task.isTask(task)) {
|
|
1356
|
-
task = newCard(task, this.project, true)
|
|
1357
|
-
}
|
|
1358
|
-
task = this.getTask(task.id)
|
|
1359
|
-
|
|
1360
|
-
var toListTasks = this.getTasksInList(newList)
|
|
1361
|
-
if (toListTasks === undefined)
|
|
1362
|
-
return cb(new Error(ERRORS.LIST_NOT_FOUND, newList))
|
|
1363
|
-
|
|
1364
|
-
var fromListTasks = this.getTasksInList(task.list)
|
|
1365
|
-
if (fromListTasks === undefined)
|
|
1366
|
-
return cb(new Error(ERRORS.LIST_NOT_FOUND, task.list))
|
|
1367
|
-
|
|
1368
|
-
var sameList = newList == task.list
|
|
1369
|
-
if (!sameList) task.oldList = task.list
|
|
1370
|
-
task.list = newList
|
|
1371
|
-
|
|
1372
|
-
// Move the task to the correct position in the list
|
|
1373
|
-
fromListTasks = _reject(fromListTasks, function (_task) {
|
|
1374
|
-
return _task.equals(task)
|
|
1375
|
-
})
|
|
1376
|
-
toListTasks = _reject(toListTasks, function (_task) {
|
|
1377
|
-
return _task.equals(task)
|
|
1378
|
-
})
|
|
1379
|
-
|
|
1380
|
-
const tasksToModify = getTasksToModify(task, toListTasks, newPos)
|
|
1381
|
-
task.updateOrderMeta(this.config)
|
|
1382
|
-
toListTasks.splice(newPos, 0, task)
|
|
1383
|
-
|
|
1384
|
-
const tasksToModifySorted = fastSort(tasksToModify).by([{ desc: u => u.line }, { asc: u => u.path }])
|
|
1385
|
-
async.eachSeries(
|
|
1386
|
-
tasksToModifySorted,
|
|
1387
|
-
(task, cb) => {
|
|
1388
|
-
this.modifyTask(task, true, cb)
|
|
1389
|
-
},
|
|
1390
|
-
(err) => {
|
|
1391
|
-
if (err)
|
|
1392
|
-
return cb([
|
|
1393
|
-
new Error(
|
|
1394
|
-
'moveTasks: Error while modifying tasks in the current list'
|
|
1395
|
-
),
|
|
1396
|
-
err,
|
|
1397
|
-
])
|
|
1398
|
-
cb(null, task)
|
|
1399
|
-
}
|
|
1400
|
-
)
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
Repository.prototype.moveTasks = function (tasks, newList, newPos = 0, noEmit, cb) {
|
|
1404
|
-
var log = require('debug')('moveTasks')
|
|
1405
|
-
var self = this
|
|
1406
|
-
if (this.getList(newList).filter)
|
|
1407
|
-
return cb(new Error(`Tasks can\'t be moved to a filtered list ${newList}.`))
|
|
1408
|
-
var listsModified = [newList]
|
|
1409
|
-
if (_isFunction(noEmit)) {
|
|
1410
|
-
cb = noEmit
|
|
1411
|
-
noEmit = false
|
|
1412
|
-
}
|
|
1413
|
-
this.moving = true
|
|
1414
|
-
cb = tools.cb(cb)
|
|
1415
|
-
|
|
1416
|
-
log('Move tasks to list:%s at position:%d : %j', newList, newPos, tasks)
|
|
1417
|
-
log(
|
|
1418
|
-
'newList before mods:',
|
|
1419
|
-
JSON.stringify(self.getTasksInList(newList), null, 3)
|
|
1420
|
-
)
|
|
1421
|
-
fastSort(tasks).by({desc: t => t.line})
|
|
1422
|
-
async.series(
|
|
1423
|
-
tasks.map((task, i) => {
|
|
1424
|
-
return function (cb) {
|
|
1425
|
-
const foundTask = self.getTasks().find(({source, line}) => task.source.path === source.path && task.line === line)
|
|
1426
|
-
if (foundTask) {
|
|
1427
|
-
if (listsModified.indexOf(foundTask.list) < 0) listsModified.push(foundTask.list)
|
|
1428
|
-
self.moveTask({ task: foundTask, newList, newPos: newPos + i, noEmit: true }, cb)
|
|
1429
|
-
} else {
|
|
1430
|
-
cb()
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
}),
|
|
1434
|
-
function (err) {
|
|
1435
|
-
if (err) {
|
|
1436
|
-
console.error('Error occurred while moving tasks:', err)
|
|
1437
|
-
}
|
|
1438
|
-
self.saveModifiedFiles(function (err, results) {
|
|
1439
|
-
if (err)
|
|
1440
|
-
console.error('Error occurred while saving modified files:', err)
|
|
1441
|
-
self.lastMovedFiles = results
|
|
1442
|
-
var tasksByList = listsModified.map((list) => {
|
|
1443
|
-
return {
|
|
1444
|
-
list: list,
|
|
1445
|
-
tasks: self.getTasksInList(list),
|
|
1446
|
-
}
|
|
1447
|
-
})
|
|
1448
|
-
self.moving = false
|
|
1449
|
-
cb(err, tasksByList)
|
|
1450
|
-
if (!err && !noEmit) self.emit('tasks.moved', tasks)
|
|
1451
|
-
})
|
|
1452
|
-
}
|
|
1453
|
-
)
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
Repository.prototype.moveTasksAsync = function (tasks, newList, newPos, noEmit = false) {
|
|
1457
|
-
return new Promise((resolve, reject) => {
|
|
1458
|
-
this.moveTasks(tasks, newList, newPos, noEmit, (err, tasksByList) => {
|
|
1459
|
-
if (err) return reject(err)
|
|
1460
|
-
resolve(tasksByList)
|
|
1461
|
-
})
|
|
1462
|
-
})
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
Repository.prototype.getModifiedFiles = function () {
|
|
1466
|
-
var filesToSave = []
|
|
1467
|
-
this.getFiles().forEach((file) => {
|
|
1468
|
-
if (file.isModified()) filesToSave.push(file)
|
|
1469
|
-
})
|
|
1470
|
-
return filesToSave
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
Repository.prototype.saveModifiedFiles = function (cb) {
|
|
1474
|
-
const checksum = this.checksum || function () {}
|
|
1475
|
-
var self = this
|
|
1476
|
-
var filesToSave = self.getModifiedFiles()
|
|
1477
|
-
var funcs = filesToSave.map((file) => {
|
|
1478
|
-
return function (cb) {
|
|
1479
|
-
file.checksum = checksum(file.getContent())
|
|
1480
|
-
self.writeAndExtract(file, false, cb)
|
|
1481
|
-
}
|
|
1482
|
-
})
|
|
1483
|
-
|
|
1484
|
-
if (funcs.length < 1) return cb()
|
|
1485
|
-
this.savingFiles = true
|
|
1486
|
-
async.parallel(funcs, (err) => {
|
|
1487
|
-
this.savingFiles = false
|
|
1488
|
-
if (err) return cb(err)
|
|
1489
|
-
self.emit('files.saved', filesToSave)
|
|
1490
|
-
cb()
|
|
1491
|
-
})
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
/**
|
|
1495
|
-
* Description
|
|
1496
|
-
* @method getTasks
|
|
1497
|
-
* @return tasks
|
|
1498
|
-
*/
|
|
1499
|
-
Repository.prototype.getTasks = function () {
|
|
1500
|
-
var tasks = [],
|
|
1501
|
-
self = this
|
|
1502
|
-
this.getFiles().forEach((file) => {
|
|
1503
|
-
Array.prototype.push.apply(tasks, file.getTasks())
|
|
1504
|
-
})
|
|
1505
|
-
|
|
1506
|
-
return tasks
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
Repository.prototype.getTasksByList = function (noSort) {
|
|
1510
|
-
return Repository.getTasksByList(this, this.getTasks(), noSort, true)
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
Repository.getTasksByList = function (
|
|
1514
|
-
repo,
|
|
1515
|
-
tasksAry,
|
|
1516
|
-
noSort,
|
|
1517
|
-
populateFiltered
|
|
1518
|
-
) {
|
|
1519
|
-
if (!repo) return []
|
|
1520
|
-
var tasks = {}
|
|
1521
|
-
var allTasks = noSort ? tasksAry : fastSort(tasksAry).by(DEFAULT_SORT)
|
|
1522
|
-
allTasks.forEach(function (task) {
|
|
1523
|
-
if (!tasks[task.list]) tasks[task.list] = []
|
|
1524
|
-
tasks[task.list].push(task)
|
|
1525
|
-
})
|
|
1526
|
-
var lists = repo.getLists()
|
|
1527
|
-
lists.forEach((list) => {
|
|
1528
|
-
if (list.filter && populateFiltered)
|
|
1529
|
-
return Repository.populateFilteredList(list, tasksAry)
|
|
1530
|
-
list.tasks = tasks[list.name] || []
|
|
1531
|
-
})
|
|
1532
|
-
return lists
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
Repository.populateFilteredList = function (list, tasks) {
|
|
1536
|
-
try {
|
|
1537
|
-
list.tasks = Repository.query(tasks, list.filter).map((task) => {
|
|
1538
|
-
task.filteredListName = list.name
|
|
1539
|
-
return task
|
|
1540
|
-
})
|
|
1541
|
-
} catch (e) {
|
|
1542
|
-
list.tasks = []
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
/**
|
|
1547
|
-
* Description
|
|
1548
|
-
* @method getTasksInList
|
|
1549
|
-
* @param {} name
|
|
1550
|
-
* @return ConditionalExpression
|
|
1551
|
-
*/
|
|
1552
|
-
Repository.prototype.getTasksInList = function (name, offset, limit) {
|
|
1553
|
-
if (!_isString(name)) return []
|
|
1554
|
-
var tasks = _where(this.getTasks(), { list: name })
|
|
1555
|
-
if (tasks.length === 0) return []
|
|
1556
|
-
var allTasks = fastSort(tasks).by(DEFAULT_SORT)
|
|
1557
|
-
if (isNumber(offset) && isNumber(limit))
|
|
1558
|
-
return allTasks.slice(offset, offset + limit)
|
|
1559
|
-
return allTasks
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
Repository.prototype.getTaskIndex = function (task) {
|
|
1563
|
-
const tasks = this.getTasksInList(task.list)
|
|
1564
|
-
let index = 0
|
|
1565
|
-
tasks.forEach((t, i) => {
|
|
1566
|
-
if (t.id === task.id) index = i
|
|
1567
|
-
})
|
|
1568
|
-
return index
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
Repository.prototype.serialize = function () {
|
|
1572
|
-
return JSON.stringify(this, null, 3)
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// WARN: deprecated
|
|
1576
|
-
Repository.deserialize = function (repo, cb) {
|
|
1577
|
-
cb = tools.cb(cb)
|
|
1578
|
-
repo = JSON.parse(repo)
|
|
1579
|
-
const config = new Config(repo.config)
|
|
1580
|
-
var newRepo = new Repository(repo.path, config)
|
|
1581
|
-
var count = 0
|
|
1582
|
-
repo.files.forEach((file) => {
|
|
1583
|
-
log('file:%j', file)
|
|
1584
|
-
newRepo.addFile(new File({ file, config, languages }), function (err) {
|
|
1585
|
-
if (err) return cb(err)
|
|
1586
|
-
count++
|
|
1587
|
-
log('count:%d, repo.files.length:%d', count, repo.files.length)
|
|
1588
|
-
if (count == repo.files.length) cb(null, newRepo)
|
|
1589
|
-
})
|
|
1590
|
-
})
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
Repository.regexQuery = function (tasks, queryString) {
|
|
1594
|
-
return tasks
|
|
1595
|
-
.map((task) => {
|
|
1596
|
-
const escapedQueryString = tools.escapeRegExp(queryString.trim());
|
|
1597
|
-
|
|
1598
|
-
// Regex to match occurrences not part of a URL and not already surrounded by `==`
|
|
1599
|
-
const regex = new RegExp(
|
|
1600
|
-
`(?<!https?:\\/\\/[^\\s]*?)\\b${escapedQueryString}\\b(?![^\\s]*\\/)(?<!==)(?!==)`,
|
|
1601
|
-
'gi'
|
|
1602
|
-
);
|
|
1603
|
-
|
|
1604
|
-
// Regex to detect Markdown links `[title](url)` where `queryString` is in title or URL
|
|
1605
|
-
const markdownLinkRegex = new RegExp(
|
|
1606
|
-
`\\[([^\\]]*${escapedQueryString}[^\\]]*)\\]\\(([^)]+${escapedQueryString}[^)]*)\\)`,
|
|
1607
|
-
'gi'
|
|
1608
|
-
);
|
|
1609
|
-
|
|
1610
|
-
let match = task.content.match(regex) || task.content.match(markdownLinkRegex);
|
|
1611
|
-
task.match = match;
|
|
1612
|
-
|
|
1613
|
-
if (match && queryString.trim() && task.interpretedContent) {
|
|
1614
|
-
// Replace standalone occurrences
|
|
1615
|
-
task.interpretedContent = task.interpretedContent.replace(
|
|
1616
|
-
regex,
|
|
1617
|
-
'==$&=='
|
|
1618
|
-
);
|
|
1619
|
-
|
|
1620
|
-
// Replace Markdown links where queryString is in title or URL
|
|
1621
|
-
task.interpretedContent = task.interpretedContent.replace(
|
|
1622
|
-
markdownLinkRegex,
|
|
1623
|
-
(match) => `==${match}==`
|
|
1624
|
-
);
|
|
1625
|
-
|
|
1626
|
-
task.updateDescriptionData();
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
return task;
|
|
1630
|
-
})
|
|
1631
|
-
.filter(({ match }) => match);
|
|
1632
|
-
};
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
Repository.parseHideListsFromQueryString = function (queryString) {
|
|
1636
|
-
const hideLists = []
|
|
1637
|
-
const regex = `hide:(\\s*((${LIST_NAME_PATTERN},?\\s?)+))?`
|
|
1638
|
-
queryString = queryString.replaceAll(
|
|
1639
|
-
// hide lists example `hide: list1, list2`
|
|
1640
|
-
new XRegExp(regex, 'g'),
|
|
1641
|
-
(match, allLists, lists) => {
|
|
1642
|
-
lists && lists.split(',').forEach((list) => {
|
|
1643
|
-
hideLists.push(list.trim())
|
|
1644
|
-
})
|
|
1645
|
-
return ''
|
|
1646
|
-
}
|
|
1647
|
-
).trim()
|
|
1648
|
-
return {
|
|
1649
|
-
hideLists,
|
|
1650
|
-
queryString,
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
Repository.parseSortFromQueryString = function (queryString) {
|
|
1655
|
-
const sort = []
|
|
1656
|
-
queryString = queryString.replace(
|
|
1657
|
-
/\s([+-])([A-Za-z.]+)/g,
|
|
1658
|
-
(match, order, attr) => {
|
|
1659
|
-
const direction = order === '+' ? 'asc' : 'desc'
|
|
1660
|
-
const sortString = `{ "${direction}": "function (o) { return o.${attr}; }" }`
|
|
1661
|
-
sort.push(JSONfns.parse(sortString))
|
|
1662
|
-
return ''
|
|
1663
|
-
}
|
|
1664
|
-
)
|
|
1665
|
-
return {
|
|
1666
|
-
sort,
|
|
1667
|
-
queryString,
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
Repository.parseSortFromMongoQuery = function (mongoQuery) {
|
|
1672
|
-
const sort = []
|
|
1673
|
-
for (const [key, value] of Object.entries(mongoQuery.sort)) {
|
|
1674
|
-
if (value > 0) sort.push({ asc: key })
|
|
1675
|
-
else sort.push({ desc: key })
|
|
1676
|
-
}
|
|
1677
|
-
return sort
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
Repository.sortByQuery = function (tasks, queryString = '') {
|
|
1681
|
-
queryString = replaceDateLanguage(queryString)
|
|
1682
|
-
let { sort } = Repository.parseSortFromQueryString(queryString)
|
|
1683
|
-
if (!sort || sort.length === 0) sort = DEFAULT_SORT
|
|
1684
|
-
fastSort(tasks).by(sort)
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
Repository.filterCards = function (tasks, _queryString = '') {
|
|
1688
|
-
let query
|
|
1689
|
-
_queryString = replaceDateLanguage(_queryString)
|
|
1690
|
-
let { sort, queryString } = Repository.parseSortFromQueryString(_queryString);
|
|
1691
|
-
const hideListsQuery = Repository.parseHideListsFromQueryString(queryString);
|
|
1692
|
-
queryString = hideListsQuery.queryString;
|
|
1693
|
-
const hideLists = hideListsQuery.hideLists;
|
|
1694
|
-
try {
|
|
1695
|
-
query = monquery(queryString)
|
|
1696
|
-
} catch (e) {
|
|
1697
|
-
log(`Unable to parse ${_queryString} using monquery`)
|
|
1698
|
-
}
|
|
1699
|
-
let result = []
|
|
1700
|
-
if (query) result = tasks.filter(sift.default(query))
|
|
1701
|
-
if (!query || result.length === 0) {
|
|
1702
|
-
result = Repository.regexQuery(tasks, queryString)
|
|
1703
|
-
}
|
|
1704
|
-
if (!sort || sort.length === 0) sort = DEFAULT_SORT
|
|
1705
|
-
fastSort(result).by(sort)
|
|
1706
|
-
return {
|
|
1707
|
-
result,
|
|
1708
|
-
query,
|
|
1709
|
-
sort,
|
|
1710
|
-
hideLists
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
Repository.query = function (tasks, queryString) {
|
|
1715
|
-
return Repository.filterCards(tasks, queryString).result
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
Repository.replaceDatesInQuery = function (query) {
|
|
1719
|
-
return Repository.filterObjectValues(query, (key, value) => {
|
|
1720
|
-
let date = Date.parse(value)
|
|
1721
|
-
if (date && /(\d{4})-(\d{2})-(\d{2})/.test(value)) {
|
|
1722
|
-
return date
|
|
1723
|
-
}
|
|
1724
|
-
return value
|
|
1725
|
-
})
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
Repository.filterObjectValues = function (o, cb) {
|
|
1729
|
-
if (o && typeof o === 'object') {
|
|
1730
|
-
for (const [key, value] of Object.entries(o)) {
|
|
1731
|
-
if (value && typeof value === 'object') {
|
|
1732
|
-
Repository.filterObjectValues(value, cb)
|
|
1733
|
-
} else {
|
|
1734
|
-
o[key] = cb(key, value)
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
return o
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
Repository.prototype.query = function (queryString) {
|
|
1742
|
-
const result = Repository.query(this.getTasks(), queryString)
|
|
1743
|
-
return Repository.getTasksByList(this, result, true)
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
module.exports = Repository
|