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