imdone-core 1.46.2 → 1.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.imdoneignore +1 -0
- package/lib/ProjectContext.js +1 -1
- package/lib/adapters/file-gateway.js +2 -2
- package/lib/adapters/parsers/file/LocalFileParserFactory.js +23 -18
- package/lib/adapters/parsers/file/ReadStreamCodeParser.js +88 -95
- package/lib/adapters/parsers/file/ReadStreamMarkdownParser.js +8 -10
- package/lib/adapters/parsers/file/ReadStreamTaskParser.js +25 -33
- package/lib/adapters/parsers/task/CardContentParser.js +42 -12
- package/lib/adapters/parsers/task/CodeStyleTaskParser.js +2 -2
- package/lib/adapters/parsers/task/HashStyleTaskParser.js +9 -6
- package/lib/adapters/parsers/task/MarkdownStyleTaskParser.js +2 -2
- package/lib/adapters/parsers/task/TaskParser.js +2 -4
- package/lib/config.js +50 -14
- package/lib/context/StreamingApplicationContext.js +31 -0
- package/lib/default-settings.js +1 -0
- package/lib/file.js +38 -54
- package/lib/mixins/repo-fs-store.js +1 -16
- package/lib/plugins/archive-plugin.js +6 -1
- package/lib/plugins/plugin-manager.js +37 -26
- package/lib/project.js +2 -0
- package/lib/repository.js +12 -15
- package/lib/task.js +5 -11
- package/lib/usecases/get-tasks-in-file.js +31 -0
- package/package.json +4 -2
- package/tasks.md +0 -21
package/.imdoneignore
CHANGED
package/lib/ProjectContext.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const FileProjectContext = require('./domain/entities/FileProjectContext')
|
|
2
|
-
const { isNumber } = require('./task')
|
|
2
|
+
const { isNumber } = require('./adapters/parsers/task/CardContentParser')
|
|
3
3
|
|
|
4
4
|
module.exports = class ProjectContext extends FileProjectContext {
|
|
5
5
|
constructor(repo) {
|
|
@@ -2,7 +2,6 @@ const { createReadStream } = require('fs')
|
|
|
2
2
|
const ReadStreamMarkdownParser = require('./ReadStreamMarkdownParser')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const languages = require('../../../languages')
|
|
5
|
-
const appContext = require('../../../context/ApplicationContext')
|
|
6
5
|
const ReadStreamCodeParser = require('./ReadStreamCodeParser')
|
|
7
6
|
|
|
8
7
|
function getLang(languages, filePath) {
|
|
@@ -15,21 +14,27 @@ function getLanguages(config) {
|
|
|
15
14
|
return { ...languages, ...configLanguages }
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
// DOING Use task line parsers for vs code extension and obsidian plugin
|
|
18
|
+
// <!--
|
|
19
|
+
// order:-10000030
|
|
20
|
+
// -->
|
|
21
|
+
|
|
22
|
+
module.exports.getFileParser = async function getFileParser (filePath, config, readStream) {
|
|
23
|
+
if (!readStream) {
|
|
24
|
+
readStream = createReadStream(filePath)
|
|
25
|
+
}
|
|
26
|
+
const languages = getLanguages(config)
|
|
27
|
+
const lang = getLang(languages, filePath)
|
|
28
|
+
if (lang.name === 'text') {
|
|
29
|
+
return new ReadStreamMarkdownParser(readStream, config)
|
|
30
|
+
} else {
|
|
31
|
+
const parser = new ReadStreamCodeParser(
|
|
32
|
+
readStream,
|
|
33
|
+
filePath,
|
|
34
|
+
lang,
|
|
35
|
+
config.lists
|
|
36
|
+
)
|
|
37
|
+
await parser.init()
|
|
38
|
+
return parser
|
|
39
|
+
}
|
|
35
40
|
}
|
|
@@ -1,121 +1,114 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const ReadStreamTaskParser = require('./ReadStreamTaskParser')
|
|
3
3
|
const CodeStyleTaskParser = require('../task/CodeStyleTaskParser')
|
|
4
|
+
const languages = require('../../../languages')
|
|
4
5
|
const extractComments = require('multilang-extract-comments')
|
|
5
6
|
const eol = require('eol')
|
|
6
|
-
const ReadLine = require('readline')
|
|
7
7
|
const { Readable } = require('stream')
|
|
8
8
|
|
|
9
|
-
async function streamToString(stream) {
|
|
10
|
-
const chunks = []
|
|
11
|
-
|
|
12
|
-
for await (const chunk of stream) {
|
|
13
|
-
chunks.push(Buffer.from(chunk))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return Buffer.concat(chunks).toString('utf-8')
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function stringToStream(string) {
|
|
20
|
-
const lines = eol.split(string).map((line) => line + String(eol.auto))
|
|
21
|
-
return Readable.from(lines)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
9
|
module.exports = class ReadStreamCodeParser extends ReadStreamTaskParser {
|
|
25
|
-
constructor(readStream, filePath, lang) {
|
|
10
|
+
constructor(readStream, filePath, lang, lists) {
|
|
26
11
|
super(readStream)
|
|
12
|
+
this.lists = lists
|
|
27
13
|
this.filePath = filePath
|
|
28
14
|
this.lang = lang
|
|
29
15
|
this.taskParsers = [new CodeStyleTaskParser()]
|
|
30
16
|
}
|
|
31
17
|
|
|
32
18
|
async init() {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
let line = null
|
|
39
|
-
let lines = []
|
|
40
|
-
let lineNo = 1
|
|
41
|
-
let comment = null
|
|
42
|
-
do {
|
|
43
|
-
const { value, done } = await this.readLine()
|
|
44
|
-
line = done ? null : value
|
|
45
|
-
comment = this.getCommentAtLine(lineNo)
|
|
46
|
-
let lineInComment = ''
|
|
47
|
-
if (comment) {
|
|
48
|
-
lineInComment = comment.lines[lineNo - comment.begin]
|
|
49
|
-
}
|
|
50
|
-
lines.push(lineInComment)
|
|
51
|
-
lineNo += 1
|
|
52
|
-
} while (line !== null)
|
|
53
|
-
this.readInterface.close()
|
|
54
|
-
this.readStream = Readable.from(
|
|
55
|
-
lines.map((line) => line + String(eol.auto))
|
|
56
|
-
)
|
|
57
|
-
this.readInterface = this.getReadInterface()
|
|
19
|
+
const code = await streamToString(this.readStream)
|
|
20
|
+
// TODO Getting tokens from lists should be done higher up in the controller
|
|
21
|
+
const tokens = this.lists.filter(list => !list.filter).map(list => list.name)
|
|
22
|
+
this.cards = this.parseCards(code, tokens)
|
|
58
23
|
}
|
|
59
24
|
|
|
60
25
|
async readTask() {
|
|
61
|
-
|
|
62
|
-
task.multiLineComment =
|
|
63
|
-
this.getCommentAtLine(task.line).info.type === 'multiline'
|
|
64
|
-
task.descriptionStartsWith = task.multiLineComment
|
|
65
|
-
? this.lang.block.ignore
|
|
66
|
-
: this.lang.symbol
|
|
67
|
-
|
|
68
|
-
return task
|
|
26
|
+
return this.cards.shift()
|
|
69
27
|
}
|
|
70
28
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
29
|
+
parseCards(code, tokens = ['TODO']) {
|
|
30
|
+
const comments = this.parseComments(code);
|
|
31
|
+
const cards = [];
|
|
32
|
+
|
|
33
|
+
Object.keys(comments).forEach((comment) => {
|
|
34
|
+
const { begin, end, content } = comments[comment];
|
|
35
|
+
const line = begin;
|
|
36
|
+
const lastLine = end - 1;
|
|
37
|
+
const lines = content.split('\n');
|
|
38
|
+
|
|
39
|
+
// Check if the comment contains "TODO"
|
|
40
|
+
lines.forEach(codeLine => {
|
|
41
|
+
if (tokens.find((token) => codeLine.includes(token))) {
|
|
42
|
+
// Get all lines from start to end
|
|
43
|
+
|
|
44
|
+
cards.push({
|
|
45
|
+
content,
|
|
46
|
+
line,
|
|
47
|
+
lastLine,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return cards;
|
|
75
53
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
console: false,
|
|
82
|
-
})
|
|
54
|
+
|
|
55
|
+
parseComments(code) {
|
|
56
|
+
const options = getLanguageOptions(this.filePath);
|
|
57
|
+
const comments = extractComments(code, options);
|
|
58
|
+
return comments;
|
|
83
59
|
}
|
|
60
|
+
|
|
61
|
+
}
|
|
84
62
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
const comments = extractComments(content, options)
|
|
105
|
-
const contentLines = eol.split(content)
|
|
106
|
-
return Object.values(comments).map((comment) => {
|
|
107
|
-
const lines = eol.split(comment.content)
|
|
108
|
-
const linesInContent = contentLines.slice(comment.begin - 1, comment.end)
|
|
109
|
-
let contentStartIndex = linesInContent.findIndex((line) =>
|
|
110
|
-
line.includes(lines[0])
|
|
111
|
-
)
|
|
112
|
-
for (let i = 0; i < contentStartIndex; i++) {
|
|
113
|
-
lines.unshift('')
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
...comment,
|
|
117
|
-
lines,
|
|
63
|
+
|
|
64
|
+
function getLanguageOptions(filePath) {
|
|
65
|
+
const extension = path.extname(filePath)
|
|
66
|
+
const language = languages[extension]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
name,
|
|
71
|
+
symbol,
|
|
72
|
+
block,
|
|
73
|
+
} = language ||
|
|
74
|
+
{
|
|
75
|
+
name: 'text',
|
|
76
|
+
symbol: '',
|
|
77
|
+
block: {
|
|
78
|
+
start: undefined,
|
|
79
|
+
end: undefined,
|
|
80
|
+
ignore: undefined,
|
|
118
81
|
}
|
|
119
|
-
}
|
|
82
|
+
};
|
|
83
|
+
// Use lang to get comment patterns
|
|
84
|
+
|
|
85
|
+
const {
|
|
86
|
+
start,
|
|
87
|
+
ignore: middle,
|
|
88
|
+
end
|
|
89
|
+
} = block || {};
|
|
90
|
+
|
|
91
|
+
return language && {
|
|
92
|
+
pattern: {
|
|
93
|
+
name,
|
|
94
|
+
nameMatchers: extension && [extension] || [],
|
|
95
|
+
singleLineComment: symbol && [{ start: symbol }] || [],
|
|
96
|
+
multiLineComment: start && end && [{ start, middle, end }] || [],
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function streamToString(stream) {
|
|
102
|
+
const chunks = []
|
|
103
|
+
|
|
104
|
+
for await (const chunk of stream) {
|
|
105
|
+
chunks.push(Buffer.from(chunk))
|
|
120
106
|
}
|
|
107
|
+
|
|
108
|
+
return Buffer.concat(chunks).toString('utf-8')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function stringToStream(string) {
|
|
112
|
+
const lines = eol.split(string).map((line) => line + String(eol.auto))
|
|
113
|
+
return Readable.from(lines)
|
|
121
114
|
}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
const ReadStreamTaskParser = require('./ReadStreamTaskParser')
|
|
2
2
|
const HashStyleTaskParser = require('../task/HashStyleTaskParser')
|
|
3
3
|
const MarkdownStyleTaskParser = require('../task/MarkdownStyleTaskParser')
|
|
4
|
-
const CheckStyleTaskParser = require('../task/CheckStyleTaskParser')
|
|
5
|
-
const appContext = require('../../../context/ApplicationContext')
|
|
4
|
+
// const CheckStyleTaskParser = require('../task/CheckStyleTaskParser')
|
|
6
5
|
|
|
7
6
|
module.exports = class ReadStreamMarkdownParser extends ReadStreamTaskParser {
|
|
8
|
-
constructor(readStream) {
|
|
9
|
-
super(readStream)
|
|
10
|
-
const { config } = appContext()
|
|
7
|
+
constructor(readStream, config) {
|
|
8
|
+
super(readStream, config)
|
|
11
9
|
this.taskParsers = [
|
|
12
|
-
new HashStyleTaskParser(),
|
|
13
|
-
new MarkdownStyleTaskParser(),
|
|
10
|
+
new HashStyleTaskParser(config),
|
|
11
|
+
new MarkdownStyleTaskParser(config),
|
|
14
12
|
]
|
|
15
|
-
if (config.isAddCheckBoxTasks()) {
|
|
16
|
-
|
|
17
|
-
}
|
|
13
|
+
// if (config.isAddCheckBoxTasks()) {
|
|
14
|
+
// this.taskParsers.push(new CheckStyleTaskParser())
|
|
15
|
+
// }
|
|
18
16
|
}
|
|
19
17
|
}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
const appContext = require('../../../context/ApplicationContext')
|
|
2
|
-
const Config = require('../../../config')
|
|
3
1
|
const ReadLine = require('readline')
|
|
4
|
-
const
|
|
5
|
-
const END_TAG = '</card>'
|
|
2
|
+
const blankLinesToEndTask = 2
|
|
6
3
|
|
|
7
4
|
module.exports = class ReadStreamTaskParser {
|
|
8
5
|
constructor(readStream) {
|
|
9
|
-
this.config = appContext().config
|
|
10
6
|
this.readStream = readStream
|
|
11
7
|
this.task = null
|
|
12
8
|
this.line = null
|
|
13
9
|
this.taskParsers = []
|
|
14
10
|
this.lineNo = 0
|
|
15
11
|
this.blankLines = 0
|
|
16
|
-
this.blankLinesToEndTask = this.config.blankLinesToEndTask
|
|
17
12
|
this.readInterface = ReadLine.createInterface({
|
|
18
13
|
input: this.readStream,
|
|
19
14
|
crlfDelay: Infinity,
|
|
@@ -23,9 +18,8 @@ module.exports = class ReadStreamTaskParser {
|
|
|
23
18
|
|
|
24
19
|
async readTask() {
|
|
25
20
|
let taskOrStreamDone = false
|
|
26
|
-
this.task = null
|
|
21
|
+
this.task = this.nextTask || null
|
|
27
22
|
this.blankLines = 0
|
|
28
|
-
this.hasStartTag = false
|
|
29
23
|
|
|
30
24
|
try {
|
|
31
25
|
while (!taskOrStreamDone) {
|
|
@@ -50,7 +44,7 @@ module.exports = class ReadStreamTaskParser {
|
|
|
50
44
|
throw error
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
this.
|
|
47
|
+
this.trimLinesToEndTask()
|
|
54
48
|
return this.task
|
|
55
49
|
}
|
|
56
50
|
|
|
@@ -59,19 +53,24 @@ module.exports = class ReadStreamTaskParser {
|
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
isEndOfTask(line) {
|
|
62
|
-
if (this.task && this.task.hasCardTags) return true
|
|
63
|
-
if (this.hasStartTag) return false
|
|
64
|
-
|
|
65
56
|
if (line.trim() === '') {
|
|
66
57
|
this.blankLines++
|
|
67
58
|
} else {
|
|
68
59
|
this.blankLines = 0
|
|
69
60
|
}
|
|
70
61
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
this.nextTask = this.getTaskFromLine(line)
|
|
63
|
+
|
|
64
|
+
if (this.blankLines > blankLinesToEndTask - 1) {
|
|
65
|
+
this.nextTask = undefined
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.nextTask) {
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
getTaskFromLine(line) {
|
|
@@ -84,30 +83,23 @@ module.exports = class ReadStreamTaskParser {
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
addLineToTask(line) {
|
|
87
|
-
|
|
88
|
-
this.hasStartTag = true
|
|
89
|
-
} else if (line.trim() === END_TAG && this.hasStartTag) {
|
|
90
|
-
this.task.hasCardTags = true
|
|
91
|
-
} else {
|
|
92
|
-
this.task.description.push(line)
|
|
93
|
-
}
|
|
94
|
-
|
|
86
|
+
this.task.description.push(line)
|
|
95
87
|
this.task.lastLine = this.lineNo
|
|
96
88
|
}
|
|
97
89
|
|
|
98
|
-
|
|
90
|
+
trimLinesToEndTask() {
|
|
99
91
|
if (!this.task) return
|
|
100
92
|
if (
|
|
101
|
-
this.blankLines ==
|
|
102
|
-
this.blankLinesToEndTask > 1 &&
|
|
103
|
-
!this.task.hasCardTags
|
|
93
|
+
this.blankLines == blankLinesToEndTask
|
|
104
94
|
) {
|
|
105
|
-
this.task.description.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.task.
|
|
95
|
+
this.task.description.length = this.task.description.length - (this.blankLines - 1)
|
|
96
|
+
} else if (this.nextTask){
|
|
97
|
+
this.task.description.length = this.task.description.length - (this.blankLines + 1)
|
|
98
|
+
} else {
|
|
99
|
+
this.task.description.length = this.task.description.length - this.blankLines
|
|
110
100
|
}
|
|
101
|
+
|
|
102
|
+
this.task.lastLine = this.task.line + this.task.description.length - 1
|
|
111
103
|
}
|
|
112
104
|
|
|
113
105
|
close() {
|
|
@@ -5,24 +5,35 @@ const FILE_TYPES = {
|
|
|
5
5
|
MARKDOWN: 'markdown',
|
|
6
6
|
CODE: 'code',
|
|
7
7
|
}
|
|
8
|
+
const TASK_TYPES = {
|
|
9
|
+
CODE: 'CODE',
|
|
10
|
+
HASHTAG: 'HASHTAG',
|
|
11
|
+
MARKDOWN: 'MARKDOWN',
|
|
12
|
+
}
|
|
8
13
|
|
|
14
|
+
const DEFAULT_TOKEN_PREFIX = '#'
|
|
9
15
|
const START_TAG = '<card>'
|
|
10
16
|
const END_TAG = '</card>'
|
|
11
17
|
const CODE_STYLE_PATTERN = '([A-Z]+[A-Z-_]+?)((:)(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$'
|
|
12
18
|
const LIST_NAME_PATTERN = '[\\p{L}0-9-_]{2,}' // [a-zA-Z-_]+?
|
|
13
|
-
const HASH_STYLE_REGEX = new XRegExp(
|
|
14
|
-
`#(${LIST_NAME_PATTERN})(:(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$`,
|
|
15
|
-
'gm'
|
|
16
|
-
)
|
|
17
|
-
const HASH_STYLE_META_ORDER_REGEX = new XRegExp(
|
|
18
|
-
`#(${LIST_NAME_PATTERN})[ \\t]+(.+)$`, 'gm'
|
|
19
|
-
)
|
|
20
19
|
const LINK_STYLE_REGEX = new XRegExp(
|
|
21
20
|
`(\\[.*\\]\\s?)?(\\[(.+)\\]\\(#(${LIST_NAME_PATTERN})(:)(-?[\\d.]+(?:e-?\\d+)?)?\\))`,
|
|
22
21
|
'gm'
|
|
23
22
|
)
|
|
24
23
|
|
|
25
24
|
const CHECK_REGEX = /^(\s*- )\[([x ])\]/
|
|
25
|
+
|
|
26
|
+
function getHashStyleRegex(tokenPrefix = DEFAULT_TOKEN_PREFIX, orderMeta = false) {
|
|
27
|
+
return orderMeta ?
|
|
28
|
+
new XRegExp(
|
|
29
|
+
`${tokenPrefix}(${LIST_NAME_PATTERN})[ \\t]+(.+)$`, 'gm'
|
|
30
|
+
) :
|
|
31
|
+
new XRegExp(
|
|
32
|
+
`${tokenPrefix}(${LIST_NAME_PATTERN})(:(-?[\\d.]+(?:e-?\\d+)?)?)?[ \\t]+(.+)$`,
|
|
33
|
+
'gm'
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
function getCheckedData(content) {
|
|
27
38
|
const taskRegex = new RegExp(CHECK_REGEX)
|
|
28
39
|
const result = taskRegex.exec(content)
|
|
@@ -65,10 +76,10 @@ function isCheckBoxTask (config, line, beforeText) {
|
|
|
65
76
|
function hasTaskInText(config, text, isCodeFile) {
|
|
66
77
|
return (
|
|
67
78
|
(isCodeFile && hasCodeStyleTask(config, text)) ||
|
|
68
|
-
((new XRegExp(
|
|
69
|
-
new XRegExp(
|
|
79
|
+
((new XRegExp(getHashStyleRegex(config.tokenPrefix)).test(text) ||
|
|
80
|
+
new XRegExp(getHashStyleRegex(config.tokenPrefix, true)).test(text) ||
|
|
70
81
|
new XRegExp(LINK_STYLE_REGEX).test(text)) &&
|
|
71
|
-
config.lists.find((list) => text.includes(
|
|
82
|
+
config.lists.find((list) => text.includes(`${config.tokenPrefix}${list.name}`)))
|
|
72
83
|
)
|
|
73
84
|
}
|
|
74
85
|
|
|
@@ -276,15 +287,34 @@ function getTaskContent ({
|
|
|
276
287
|
getTextFileTaskContent({ config, content, beforeText, fileType })
|
|
277
288
|
}
|
|
278
289
|
|
|
290
|
+
function getRawTask ({tokenPrefix = DEFAULT_TOKEN_PREFIX, orderMeta, beforeText = '', hasColon = false, list, order= '', text, type}) {
|
|
291
|
+
if (type === TASK_TYPES.MARKDOWN) {
|
|
292
|
+
return `${beforeText}[${text}](${tokenPrefix}${list}:${
|
|
293
|
+
orderMeta ? '' : order
|
|
294
|
+
})`
|
|
295
|
+
}
|
|
296
|
+
const colon = hasColon || (isNumber(order) && !orderMeta) ? ':' : '';
|
|
297
|
+
return `${beforeText}${tokenPrefix}${list}${colon}${
|
|
298
|
+
orderMeta ? '' : order
|
|
299
|
+
} ${text}`
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isNumber (value) {
|
|
303
|
+
return !isNaN(parseFloat(value)) && isFinite(value)
|
|
304
|
+
}
|
|
305
|
+
|
|
279
306
|
module.exports = {
|
|
280
307
|
FILE_TYPES,
|
|
308
|
+
TASK_TYPES,
|
|
281
309
|
getTaskContent,
|
|
310
|
+
getRawTask,
|
|
282
311
|
isBeforeTextMarkdownList,
|
|
283
312
|
padDescription,
|
|
284
313
|
getCheckedData,
|
|
314
|
+
hasTaskInText,
|
|
315
|
+
isNumber,
|
|
316
|
+
getHashStyleRegex,
|
|
285
317
|
CHECK_REGEX,
|
|
286
318
|
LIST_NAME_PATTERN,
|
|
287
|
-
HASH_STYLE_REGEX,
|
|
288
|
-
HASH_STYLE_META_ORDER_REGEX,
|
|
289
319
|
LINK_STYLE_REGEX
|
|
290
320
|
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
const TaskParser = require('./TaskParser')
|
|
2
|
+
const { getHashStyleRegex } = require('./CardContentParser')
|
|
2
3
|
|
|
3
4
|
module.exports = class HashStyleTaskParser extends TaskParser {
|
|
4
|
-
constructor() {
|
|
5
|
-
super('HASHTAG')
|
|
5
|
+
constructor(config) {
|
|
6
|
+
super('HASHTAG', config)
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
get pattern() {
|
|
9
|
-
|
|
10
|
+
const config = this.config
|
|
11
|
+
return getHashStyleRegex(config.tokenPrefix, config.orderMeta)
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
parse(lineContent, line, matchResult) {
|
|
13
|
-
const [
|
|
15
|
+
const [rawTask, list, orderGroup, order, text] = matchResult
|
|
16
|
+
|
|
14
17
|
return {
|
|
15
|
-
beforeText,
|
|
18
|
+
// beforeText,
|
|
16
19
|
text,
|
|
17
20
|
order,
|
|
18
21
|
list,
|
|
19
22
|
line,
|
|
20
|
-
colon,
|
|
23
|
+
colon: orderGroup && orderGroup.startsWith(':'),
|
|
21
24
|
type: this.type,
|
|
22
25
|
}
|
|
23
26
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const Config = require('../../../config')
|
|
2
|
-
const appContext = require('../../../context/ApplicationContext')
|
|
3
1
|
|
|
4
2
|
class Task {
|
|
5
3
|
constructor({ beforeText, text, list, line, order, checked, colon, type }) {
|
|
@@ -17,8 +15,8 @@ class Task {
|
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
module.exports = class TaskParser {
|
|
20
|
-
constructor(type) {
|
|
21
|
-
this.config =
|
|
18
|
+
constructor(type, config) {
|
|
19
|
+
this.config = config
|
|
22
20
|
this.type = type
|
|
23
21
|
}
|
|
24
22
|
|
package/lib/config.js
CHANGED
|
@@ -3,7 +3,19 @@
|
|
|
3
3
|
const _cloneDeep = require('lodash.clonedeep')
|
|
4
4
|
const _assign = require('lodash.assign')
|
|
5
5
|
const _get = require('lodash.get')
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
JOURNAL_TYPE,
|
|
8
|
+
DEFAULT_CONFIG,
|
|
9
|
+
CONFIG_FILE_YML,
|
|
10
|
+
CONFIG_DIR
|
|
11
|
+
} = require('./constants')
|
|
12
|
+
const _path = require('path')
|
|
13
|
+
const findUp = require('find-up')
|
|
14
|
+
const { loadYAML } = require('./adapters/yaml')
|
|
15
|
+
const {
|
|
16
|
+
exists,
|
|
17
|
+
readFile,
|
|
18
|
+
} = require('./adapters/file-gateway')
|
|
7
19
|
|
|
8
20
|
/**
|
|
9
21
|
* Description
|
|
@@ -187,20 +199,12 @@ class Config {
|
|
|
187
199
|
return _get(this, 'settings.cards.archiveCompleted', false)
|
|
188
200
|
}
|
|
189
201
|
|
|
190
|
-
get
|
|
191
|
-
return this.
|
|
202
|
+
get tokenPrefix() {
|
|
203
|
+
return _get(this, 'settings.cards.tokenPrefix', '#')
|
|
192
204
|
}
|
|
193
205
|
|
|
194
|
-
get
|
|
195
|
-
|
|
196
|
-
_get(this, 'settings.cards.blankLinesToEndTask', 1)
|
|
197
|
-
)
|
|
198
|
-
if (blankLinesToEndTask > 2) {
|
|
199
|
-
blankLinesToEndTask = 2
|
|
200
|
-
} else if (blankLinesToEndTask < 1) {
|
|
201
|
-
blankLinesToEndTask = 1
|
|
202
|
-
}
|
|
203
|
-
return blankLinesToEndTask
|
|
206
|
+
get doneList() {
|
|
207
|
+
return this.getDoneList()
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
getMetaSep() {
|
|
@@ -209,5 +213,37 @@ class Config {
|
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
215
|
|
|
212
|
-
Config.newDefaultConfig = (config = {}) => new Config(
|
|
216
|
+
Config.newDefaultConfig = (config = {}) => new Config(
|
|
217
|
+
{
|
|
218
|
+
...DEFAULT_CONFIG,
|
|
219
|
+
lists: [
|
|
220
|
+
...DEFAULT_CONFIG.lists
|
|
221
|
+
],
|
|
222
|
+
...config
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
Config.findImdonePath = async (cwd) => {
|
|
227
|
+
const configDir = await findUp(CONFIG_DIR, { cwd, type: 'directory' })
|
|
228
|
+
return _path.dirname(configDir)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Config.getYamlConfig = async (filePath) => {
|
|
232
|
+
if (!(await exists(filePath))) return
|
|
233
|
+
const configData = await readFile(filePath, 'utf-8')
|
|
234
|
+
let configLike = {}
|
|
235
|
+
try {
|
|
236
|
+
configLike = loadYAML(configData.toString())
|
|
237
|
+
} catch (e) {
|
|
238
|
+
/* noop */
|
|
239
|
+
}
|
|
240
|
+
return configLike
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
Config.load = async (imdonePath) => {
|
|
244
|
+
const configPath = _path.join(imdonePath, CONFIG_FILE_YML)
|
|
245
|
+
const configLike = await Config.getYamlConfig(configPath)
|
|
246
|
+
return new Config(configLike)
|
|
247
|
+
}
|
|
248
|
+
|
|
213
249
|
module.exports = Config
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const Config = require('../config')
|
|
2
|
+
|
|
3
|
+
class ApplicationContext {
|
|
4
|
+
constructor({
|
|
5
|
+
project,
|
|
6
|
+
repo,
|
|
7
|
+
projectContext,
|
|
8
|
+
projectReader,
|
|
9
|
+
projectPresenter,
|
|
10
|
+
}) {
|
|
11
|
+
this.project = project;
|
|
12
|
+
this.repo = repo;
|
|
13
|
+
this.projectContext = projectContext;
|
|
14
|
+
this.projectReader = projectReader;
|
|
15
|
+
this.projectPresenter = projectPresenter;
|
|
16
|
+
this.innerConfig = Config.newDefaultConfig()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
set config(config) {
|
|
20
|
+
this.innerConfig = config
|
|
21
|
+
this.repo && (this.repo.config = config)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get config() {
|
|
25
|
+
return (this.repo && this.repo.config) || this.innerConfig
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
const context = new ApplicationContext({})
|
|
30
|
+
|
|
31
|
+
module.exports = () => context
|
package/lib/default-settings.js
CHANGED
package/lib/file.js
CHANGED
|
@@ -24,14 +24,14 @@ var _clone = require('lodash.clone'),
|
|
|
24
24
|
{
|
|
25
25
|
FILE_TYPES,
|
|
26
26
|
getTaskContent,
|
|
27
|
+
getRawTask,
|
|
28
|
+
getHashStyleRegex,
|
|
29
|
+
hasTaskInText,
|
|
27
30
|
LIST_NAME_PATTERN,
|
|
28
|
-
HASH_STYLE_REGEX,
|
|
29
|
-
HASH_STYLE_META_ORDER_REGEX,
|
|
30
31
|
LINK_STYLE_REGEX,
|
|
31
32
|
} = require('./adapters/parsers/task/CardContentParser')
|
|
32
33
|
|
|
33
34
|
const appContext = require('./context/ApplicationContext')
|
|
34
|
-
const { isNumber } = require('./task')
|
|
35
35
|
const { hasBlankLines } = require('./tools')
|
|
36
36
|
|
|
37
37
|
const ERRORS = {
|
|
@@ -296,6 +296,11 @@ File.prototype.transformTasks = function (config, modify) {
|
|
|
296
296
|
.forEach(task => this.transformTask({config, modify, task}))
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// TODO Blank lines should be allowed in code tasks
|
|
300
|
+
// #imdone-1.47.0
|
|
301
|
+
// <!--
|
|
302
|
+
// order:-120
|
|
303
|
+
// -->
|
|
299
304
|
File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
300
305
|
var self = this
|
|
301
306
|
var codeStyleRegex = new RegExp(CODE_STYLE_PATTERN, 'gm')
|
|
@@ -346,7 +351,7 @@ File.prototype.extractCodeStyleTasks = function (config, pos, content) {
|
|
|
346
351
|
}
|
|
347
352
|
|
|
348
353
|
File.prototype.extractHashStyleTasks = function (config, pos, content) {
|
|
349
|
-
var hashStyleRegex =
|
|
354
|
+
var hashStyleRegex = getHashStyleRegex(config.tokenPrefix)
|
|
350
355
|
var lang = this.getLang()
|
|
351
356
|
var result
|
|
352
357
|
while ((result = hashStyleRegex.exec(content)) !== null) {
|
|
@@ -449,7 +454,7 @@ File.prototype.extractCheckboxTasks = function (config, pos, content) {
|
|
|
449
454
|
content: content.substring(result.index),
|
|
450
455
|
pos: posInContent,
|
|
451
456
|
})
|
|
452
|
-
task.rawTask =
|
|
457
|
+
task.rawTask = getRawTask({tokenPrefix: config.tokenPrefix, orderMeta: config.orderMeta, list, text, type})
|
|
453
458
|
tasks.push(task)
|
|
454
459
|
}
|
|
455
460
|
if (tasks) {
|
|
@@ -484,17 +489,6 @@ File.prototype.extractCheckboxTasks = function (config, pos, content) {
|
|
|
484
489
|
}
|
|
485
490
|
}
|
|
486
491
|
|
|
487
|
-
File.getRawTask = function (type, text, list, order) {
|
|
488
|
-
const TYPES = Task.Types
|
|
489
|
-
if (type === TYPES.MARKDOWN) {
|
|
490
|
-
return `[${text}](#${list}:${order})`
|
|
491
|
-
} else if (appContext().repo.config.orderMeta) {
|
|
492
|
-
return `#${list} ${text}`
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
return `#${list}:${order} ${text}`
|
|
496
|
-
}
|
|
497
|
-
|
|
498
492
|
File.prototype.extractTasks = function (config) {
|
|
499
493
|
this.tasks = []
|
|
500
494
|
if (this.isMarkDownFile()) {
|
|
@@ -628,7 +622,7 @@ File.prototype.deleteTask = function (task, config) {
|
|
|
628
622
|
config
|
|
629
623
|
)
|
|
630
624
|
} else if (task.type === Task.Types.HASHTAG) {
|
|
631
|
-
this.deleteCodeOrHashTask(
|
|
625
|
+
this.deleteCodeOrHashTask(getHashStyleRegex(config.tokenPrefix), task, config)
|
|
632
626
|
}
|
|
633
627
|
} else {
|
|
634
628
|
const fileContent = this.getContentLines()
|
|
@@ -643,19 +637,6 @@ File.prototype.deleteTask = function (task, config) {
|
|
|
643
637
|
}
|
|
644
638
|
}
|
|
645
639
|
|
|
646
|
-
File.TASK_TYPE_TEMPLATES = {
|
|
647
|
-
HASHTAG: (t, config) => {
|
|
648
|
-
const colon = t.hasColon || (isNumber(t.order) && !config.orderMeta) ? ':' : '';
|
|
649
|
-
return `${t.beforeText || ''}#${t.list}${colon}${
|
|
650
|
-
config.orderMeta ? '' : t.order
|
|
651
|
-
} ${t.text}`;
|
|
652
|
-
},
|
|
653
|
-
MARKDOWN: (t, config) =>
|
|
654
|
-
`${t.beforeText || ''}[${t.text}](#${t.list}:${
|
|
655
|
-
config.orderMeta ? '' : t.order
|
|
656
|
-
})`,
|
|
657
|
-
}
|
|
658
|
-
|
|
659
640
|
File.prototype.modifyTask = function (task, config, noEmit) {
|
|
660
641
|
task.orderModified = true
|
|
661
642
|
task.updateOrderMeta(config)
|
|
@@ -669,7 +650,16 @@ File.prototype.modifyTask = function (task, config, noEmit) {
|
|
|
669
650
|
} else {
|
|
670
651
|
task.updateContent()
|
|
671
652
|
const fileContent = this.getContentLines()
|
|
672
|
-
let text =
|
|
653
|
+
let text = getRawTask({
|
|
654
|
+
tokenPrefix: config.tokenPrefix,
|
|
655
|
+
orderMeta: config.orderMeta,
|
|
656
|
+
beforeText: task.beforeText,
|
|
657
|
+
hasColon: task.hasColon,
|
|
658
|
+
list: task.list,
|
|
659
|
+
order: task.order,
|
|
660
|
+
text: task.text,
|
|
661
|
+
type: task.type,
|
|
662
|
+
})
|
|
673
663
|
const linesBefore = fileContent.slice(0, task.line - 1)
|
|
674
664
|
if (this.isCodeFile()) {
|
|
675
665
|
const linesBeforeLength = linesBefore.join(lineEnd).length
|
|
@@ -741,9 +731,8 @@ File.prototype.modifyCodeOrHashTask = function (re, task, config, noEmit) {
|
|
|
741
731
|
} else text = task.text
|
|
742
732
|
} else text = task.text
|
|
743
733
|
if (/[a-z]/.test(task.list)) task.type = Task.Types.HASHTAG
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
var taskContent = `${task.beforeText || ''}${hash}${task.list}${inLineOrder} ${text}`
|
|
734
|
+
const tokenPrefix = task.type === Task.Types.HASHTAG ? config.tokenPrefix : ''
|
|
735
|
+
var taskContent = getRawTask({tokenPrefix, orderMeta: config.orderMeta, list: task.list, order: task.order, text, type: task.type})
|
|
747
736
|
task.line = this.getLineNumber(index)
|
|
748
737
|
task.id = getTaskId(self.getPath(), task.line, task.text)
|
|
749
738
|
this.setContent(beforeContent + taskContent + afterContent)
|
|
@@ -1111,19 +1100,20 @@ File.prototype.removeTask = function (task) {
|
|
|
1111
1100
|
|
|
1112
1101
|
File.prototype.ignoreCodeBlocks = function () {
|
|
1113
1102
|
// BACKLOG:-100 This is not used, but we need a way to ignore tasks in code blocks. Maybe save position ranges
|
|
1114
|
-
|
|
1115
|
-
var
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
if (this.isMarkDownFile()) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1126
|
-
return cleanContent
|
|
1103
|
+
// ```javascript
|
|
1104
|
+
// var cleanContent = this.content
|
|
1105
|
+
// var replacer = function (block) {
|
|
1106
|
+
// block = block.replace(new XRegExp(LINK_STYLE_REGEX), '**TASK**')
|
|
1107
|
+
// block = block.replace(getHashStyleRegex(config.tokenPrefix), '**TASK**')
|
|
1108
|
+
// return block
|
|
1109
|
+
// }
|
|
1110
|
+
// if (this.isMarkDownFile()) {
|
|
1111
|
+
// cleanContent = this.content
|
|
1112
|
+
// .replace(new RegExp(CODE_BLOCK_REGEX), replacer)
|
|
1113
|
+
// .replace(new RegExp(INLINE_CODE_REGEX), replacer)
|
|
1114
|
+
// }
|
|
1115
|
+
// return cleanContent
|
|
1116
|
+
// ```
|
|
1127
1117
|
}
|
|
1128
1118
|
|
|
1129
1119
|
File.prototype.isMarkDownFile = function () {
|
|
@@ -1261,13 +1251,7 @@ File.prototype.hasCodeStyleTask = function (config, text) {
|
|
|
1261
1251
|
}
|
|
1262
1252
|
|
|
1263
1253
|
File.prototype.hasTaskInText = function (config, text) {
|
|
1264
|
-
return (
|
|
1265
|
-
this.hasCodeStyleTask(config, text) ||
|
|
1266
|
-
((new XRegExp(HASH_STYLE_REGEX).test(text) ||
|
|
1267
|
-
new XRegExp(HASH_STYLE_META_ORDER_REGEX).test(text) ||
|
|
1268
|
-
new XRegExp(LINK_STYLE_REGEX).test(text)) &&
|
|
1269
|
-
config.lists.find((list) => text.includes(`#${list.name}`)))
|
|
1270
|
-
)
|
|
1254
|
+
return hasTaskInText(config, text, this.isCodeFile())
|
|
1271
1255
|
}
|
|
1272
1256
|
|
|
1273
1257
|
File.prototype.isInBlockComment = function (content) {
|
|
@@ -255,7 +255,7 @@ function mixin(repo, fs = require('fs')) {
|
|
|
255
255
|
cb = tools.cb(cb)
|
|
256
256
|
repo.loadIgnore()
|
|
257
257
|
// new
|
|
258
|
-
let configData = this.getYamlConfig() ||
|
|
258
|
+
let configData = this.getYamlConfig() || {}
|
|
259
259
|
repo.updateConfig(configData, cb)
|
|
260
260
|
}
|
|
261
261
|
|
|
@@ -263,21 +263,6 @@ function mixin(repo, fs = require('fs')) {
|
|
|
263
263
|
return repo.getFullPath(CONFIG_FILE)
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
repo.getJsonConfig = function () {
|
|
267
|
-
const filePath = repo.getFullPath(CONFIG_FILE)
|
|
268
|
-
if (!fs.existsSync(filePath)) return
|
|
269
|
-
const configData = fs.readFileSync(filePath)
|
|
270
|
-
let configLike = {}
|
|
271
|
-
try {
|
|
272
|
-
configLike = JSON.parse(configData.toString())
|
|
273
|
-
if (configLike.exclude) delete configLike.exclude
|
|
274
|
-
if (configLike.watcher) delete configLike.watcher
|
|
275
|
-
} catch (e) {
|
|
276
|
-
/* noop */
|
|
277
|
-
}
|
|
278
|
-
return configLike
|
|
279
|
-
}
|
|
280
|
-
|
|
281
266
|
repo.getYamlConfig = function () {
|
|
282
267
|
const filePath = repo.getFullPath(CONFIG_FILE_YML)
|
|
283
268
|
if (!fs.existsSync(filePath)) return
|
|
@@ -52,7 +52,12 @@ module.exports = class ArchivePlugin extends Plugin {
|
|
|
52
52
|
],
|
|
53
53
|
task.content
|
|
54
54
|
)
|
|
55
|
-
|
|
55
|
+
// TODO This should use the CardContentParser to get the raw task
|
|
56
|
+
// <!--
|
|
57
|
+
// #imdone-1.47.0
|
|
58
|
+
// order:-110
|
|
59
|
+
// -->
|
|
60
|
+
const taskContent = `${task.beforeText}${config.tokenPrefix}${task.list} ${content}\n\n\n`
|
|
56
61
|
|
|
57
62
|
this.fileGateway.preparePathForWriting(newPath)
|
|
58
63
|
this.fileGateway.appendFileSync(newPath, taskContent)
|
|
@@ -95,48 +95,47 @@ module.exports = class PluginManager extends Emitter {
|
|
|
95
95
|
const installPath = _path.join(this.pluginPath, name)
|
|
96
96
|
await downloadRepo(version, installPath)
|
|
97
97
|
console.log(`Done installing ${name}`)
|
|
98
|
-
this.loadPlugin(installPath)
|
|
98
|
+
await this.loadPlugin(installPath)
|
|
99
99
|
this.emit('plugin-installed', name)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
async loadPlugins() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.exists(this.pluginPath).then((path) => {
|
|
107
|
-
if (!path) return resolve()
|
|
108
|
-
fs.readdir(this.pluginPath, { withFileTypes: true }, (err, paths) => {
|
|
109
|
-
if (err) return reject(err)
|
|
110
|
-
paths
|
|
111
|
-
.filter(
|
|
112
|
-
(entry) =>
|
|
113
|
-
entry.name !== 'node_modules' &&
|
|
114
|
-
(entry.isDirectory() || entry.isSymbolicLink())
|
|
115
|
-
)
|
|
116
|
-
.map((entry) => _path.join(this.pluginPath, entry.name))
|
|
117
|
-
.forEach((path) => {
|
|
118
|
-
this.loadPlugin(path)
|
|
119
|
-
})
|
|
120
|
-
resolve()
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
}
|
|
103
|
+
for (const pluginPath of this.defaultPlugins) {
|
|
104
|
+
await this.loadPlugin(pluginPath)
|
|
105
|
+
}
|
|
125
106
|
|
|
126
|
-
|
|
107
|
+
const path = await this.exists(this.pluginPath)
|
|
108
|
+
if (!path) return
|
|
109
|
+
const paths = await fs.promises.readdir(this.pluginPath, { withFileTypes: true })
|
|
110
|
+
const pluginPaths = paths
|
|
111
|
+
.filter(
|
|
112
|
+
(entry) =>
|
|
113
|
+
entry.name !== 'node_modules' &&
|
|
114
|
+
(entry.isDirectory() || entry.isSymbolicLink())
|
|
115
|
+
)
|
|
116
|
+
.map((entry) => _path.join(this.pluginPath, entry.name))
|
|
117
|
+
|
|
118
|
+
for (const path of pluginPaths) {
|
|
119
|
+
await this.loadPlugin(path)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async loadPlugin(path) {
|
|
127
125
|
console.log('Loading plugin: ', path)
|
|
128
126
|
try {
|
|
129
127
|
// eslint-disable-next-line
|
|
130
128
|
delete require.cache[require.resolve(path)]
|
|
131
129
|
// eslint-disable-next-line
|
|
132
130
|
const pluginClass = require(path)
|
|
133
|
-
this.createPlugin(pluginClass, path)
|
|
131
|
+
const pluginInstance = await this.createPlugin(pluginClass, path)
|
|
132
|
+
if (pluginInstance.init) await pluginInstance.init()
|
|
134
133
|
} catch (e) {
|
|
135
134
|
console.error(`Error loading plugin at: ${path}`, e)
|
|
136
135
|
}
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
createPlugin(pluginClass, path) {
|
|
138
|
+
async createPlugin(pluginClass, path) {
|
|
140
139
|
if (!this.isPlugin(pluginClass))
|
|
141
140
|
throw new Error(`${pluginClass.name} is not a plugin`)
|
|
142
141
|
const name = pluginClass.name.toString()
|
|
@@ -164,6 +163,8 @@ module.exports = class PluginManager extends Emitter {
|
|
|
164
163
|
pluginClass,
|
|
165
164
|
info,
|
|
166
165
|
}
|
|
166
|
+
|
|
167
|
+
return pluginInstance
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
destroyPlugins() {
|
|
@@ -268,6 +269,16 @@ module.exports = class PluginManager extends Emitter {
|
|
|
268
269
|
})
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
async onTaskFound(task) {
|
|
273
|
+
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
274
|
+
try {
|
|
275
|
+
if (pluginInstance.onTaskFound) await pluginInstance.onTaskFound(task)
|
|
276
|
+
} catch (e) {
|
|
277
|
+
this.pluginError('onTaskFound', pluginInstance, e)
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
|
|
271
282
|
async onBeforeAddTask({path, list, content, tags, contexts, meta, useCardTemplate}) {
|
|
272
283
|
await this.eachPluginAsync(async ({ pluginInstance }) => {
|
|
273
284
|
try {
|
package/lib/project.js
CHANGED
|
@@ -62,6 +62,8 @@ module.exports = class WorkerProject extends Project {
|
|
|
62
62
|
this.repo.on(event, (data) => onChange(this, event, data))
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
+
this.repo.on('task.found', (task) => this.pluginManager.onTaskFound(task))
|
|
66
|
+
|
|
65
67
|
const promise = new Promise((resolve, reject) => {
|
|
66
68
|
this.repo.init((err, files) => {
|
|
67
69
|
if (err) {
|
package/lib/repository.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { isNumber } = require('./task')
|
|
4
|
-
|
|
5
3
|
var _get = require('lodash.get'),
|
|
6
4
|
_isObject = require('lodash.isobject'),
|
|
7
5
|
_isFunction = require('lodash.isfunction'),
|
|
@@ -37,9 +35,12 @@ var _get = require('lodash.get'),
|
|
|
37
35
|
functionRegex = require('function-regex'),
|
|
38
36
|
Task = require('./task'),
|
|
39
37
|
newCard = require('./card'),
|
|
40
|
-
{ replaceDateLanguage } = require('./adapters/parsers/DateLanguageParser')
|
|
38
|
+
{ replaceDateLanguage } = require('./adapters/parsers/DateLanguageParser'),
|
|
39
|
+
{ getRawTask, isNumber } = require('./adapters/parsers/task/CardContentParser')
|
|
40
|
+
|
|
41
41
|
|
|
42
42
|
const appContext = require('./context/ApplicationContext')
|
|
43
|
+
const get = require('lodash.get')
|
|
43
44
|
|
|
44
45
|
var ERRORS = constants.ERRORS,
|
|
45
46
|
DEFAULT_FILE_PATTERN = constants.DEFAULT_FILE_PATTERN,
|
|
@@ -1100,8 +1101,8 @@ Repository.prototype.appendTask = function({file, content, list}, cb) {
|
|
|
1100
1101
|
const description =
|
|
1101
1102
|
lines.length > 1 ? Task.padDescription(lines.slice(1), taskPrefix) : []
|
|
1102
1103
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1104
|
+
const rawTask = getRawTask({tokenPrefix: config.tokenPrefix, orderMeta: config.orderMeta, list, order, text, type: taskSyntax})
|
|
1105
|
+
const taskContent = config.orderMeta
|
|
1105
1106
|
? this.getTaskContent({
|
|
1106
1107
|
description,
|
|
1107
1108
|
order,
|
|
@@ -1111,18 +1112,14 @@ Repository.prototype.appendTask = function({file, content, list}, cb) {
|
|
|
1111
1112
|
})
|
|
1112
1113
|
: description.join(eol.lf)
|
|
1113
1114
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
} else {
|
|
1119
|
-
const inLineOrder = config.orderMeta ? '' : `:${orderString}`
|
|
1120
|
-
appendContent = `${taskPrefix}#${list}${inLineOrder} ${text}${eol.lf}${taskContent}`
|
|
1121
|
-
}
|
|
1122
|
-
appendContent = File.trimBlankLines(appendContent)
|
|
1115
|
+
let appendContent = File.trimBlankLines(
|
|
1116
|
+
`${taskPrefix}${rawTask}${eol.lf}${taskContent}`
|
|
1117
|
+
)
|
|
1118
|
+
|
|
1123
1119
|
fileContent = `${fileContent}${sep}`
|
|
1124
1120
|
const cardTerminator = "\n".repeat(2)
|
|
1125
1121
|
file.setContent(`${fileContent}${appendContent}${cardTerminator}`)
|
|
1122
|
+
|
|
1126
1123
|
this.writeAndExtract(file, true, (err, file) => {
|
|
1127
1124
|
if (err) return cb(err)
|
|
1128
1125
|
const task = file.getTasks().find(task => task.text === text && task.list === list)
|
|
@@ -1541,7 +1538,7 @@ Repository.prototype.getTasksInList = function (name, offset, limit) {
|
|
|
1541
1538
|
var tasks = _where(this.getTasks(), { list: name })
|
|
1542
1539
|
if (tasks.length === 0) return []
|
|
1543
1540
|
var allTasks = fastSort(tasks).by(DEFAULT_SORT)
|
|
1544
|
-
if (
|
|
1541
|
+
if (isNumber(offset) && isNumber(limit))
|
|
1545
1542
|
return allTasks.slice(offset, offset + limit)
|
|
1546
1543
|
return allTasks
|
|
1547
1544
|
}
|
package/lib/task.js
CHANGED
|
@@ -16,8 +16,10 @@ var _clone = require('lodash.clone'),
|
|
|
16
16
|
|
|
17
17
|
const {
|
|
18
18
|
CHECK_REGEX,
|
|
19
|
+
TASK_TYPES,
|
|
19
20
|
getCheckedData,
|
|
20
|
-
isBeforeTextMarkdownList
|
|
21
|
+
isBeforeTextMarkdownList,
|
|
22
|
+
isNumber
|
|
21
23
|
} = require('./adapters/parsers/task/CardContentParser.js')
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -234,11 +236,7 @@ Task.prototype.getCheckedData = function () {
|
|
|
234
236
|
return getCheckedData(this.beforeText)
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
Task.Types =
|
|
238
|
-
CODE: 'CODE',
|
|
239
|
-
HASHTAG: 'HASHTAG',
|
|
240
|
-
MARKDOWN: 'MARKDOWN',
|
|
241
|
-
}
|
|
239
|
+
Task.Types = TASK_TYPES
|
|
242
240
|
|
|
243
241
|
Task.AnyLanguageGroup =
|
|
244
242
|
'#\\w:%/.$-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC'
|
|
@@ -422,7 +420,7 @@ Task.getMetaOrderRegex = function (config) {
|
|
|
422
420
|
|
|
423
421
|
Task.prototype.updateOrderMeta = function (config, descContent = this.description.join(lineEnd)) {
|
|
424
422
|
if (config.orderMeta) {
|
|
425
|
-
if (!
|
|
423
|
+
if (!isNumber(this.order)) return this.order = ''
|
|
426
424
|
const addNewLine = config.isMetaNewLine()
|
|
427
425
|
const metaSep = config.getMetaSep()
|
|
428
426
|
if (this.meta.order) {
|
|
@@ -778,8 +776,4 @@ Task.prototype.equals = function (task) {
|
|
|
778
776
|
)
|
|
779
777
|
}
|
|
780
778
|
|
|
781
|
-
Task.isNumber = function(n) {
|
|
782
|
-
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
779
|
module.exports = Task
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const Config = require('../config')
|
|
2
|
+
const { getFileParser } = require('../adapters/parsers/file/LocalFileParserFactory')
|
|
3
|
+
const { Readable } = require('stream');
|
|
4
|
+
|
|
5
|
+
// DOING Use task line parsers for vs code extension and obsidian plugin
|
|
6
|
+
// <!--
|
|
7
|
+
// order:-10000030
|
|
8
|
+
// -->
|
|
9
|
+
export async function getTasks({filePath, content}) {
|
|
10
|
+
const projectPath = await Config.findImdonePath(filePath)
|
|
11
|
+
const config = await Config.load(projectPath)
|
|
12
|
+
|
|
13
|
+
// create a readstream from the content and pass it to getFileParser
|
|
14
|
+
const readStream = new Readable({
|
|
15
|
+
read() {
|
|
16
|
+
this.push(content);
|
|
17
|
+
this.push(null); // Signals the end of the stream
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const parser = await getFileParser(filePath, config, readStream)
|
|
22
|
+
|
|
23
|
+
const tasks = []
|
|
24
|
+
let task = true
|
|
25
|
+
while (task) {
|
|
26
|
+
task = await parser.readTask()
|
|
27
|
+
if (task) tasks.push(task)
|
|
28
|
+
}
|
|
29
|
+
parser.close()
|
|
30
|
+
return tasks
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imdone-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.1",
|
|
4
4
|
"description": "imdone-core",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -56,10 +56,11 @@
|
|
|
56
56
|
"eol": "^0.9.1",
|
|
57
57
|
"escape-string-regexp": "^1.0.5",
|
|
58
58
|
"fast-sort": "^2.2.0",
|
|
59
|
+
"find-up": "^5.0.0",
|
|
59
60
|
"function-regex": "^1.0.2",
|
|
60
61
|
"gray-matter": "^4.0.2",
|
|
61
62
|
"ignore": "^5.2.0",
|
|
62
|
-
"imdone-api": "^
|
|
63
|
+
"imdone-api": "^1.47.0",
|
|
63
64
|
"isbinaryfile": "^3.0.2",
|
|
64
65
|
"js-yaml": "^3.14.1",
|
|
65
66
|
"json-fns": "^1.0.0",
|
|
@@ -98,6 +99,7 @@
|
|
|
98
99
|
"multilang-extract-comments": "^0.4.0",
|
|
99
100
|
"open": "^9.1.0",
|
|
100
101
|
"ora": "^5.4.1",
|
|
102
|
+
"parse-code-context": "^1.0.0",
|
|
101
103
|
"prompts": "^2.4.2",
|
|
102
104
|
"readdirp": "^3.6.0",
|
|
103
105
|
"remove-markdown": "^0.3.0",
|
package/tasks.md
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
backlog
|
|
2
|
-
====
|
|
3
|
-
|
|
4
|
-
## Ungrouped Tasks
|
|
5
|
-
|
|
6
|
-
## Phase 1
|
|
7
|
-
|
|
8
|
-
### TODO
|
|
9
|
-
|
|
10
|
-
- [ ] Another new task in a new file
|
|
11
|
-
<!-- +urgent +important @piascikj group:"Phase 1" -->
|
|
12
|
-
<!-- created:2023-09-02T03:48:18.338Z sid: -->
|
|
13
|
-
|
|
14
|
-
## Phase 2
|
|
15
|
-
|
|
16
|
-
### DOING
|
|
17
|
-
|
|
18
|
-
- [ ] Another new task in a new files
|
|
19
|
-
<!-- +urgent +important @piascikj group:"Phase 2" -->
|
|
20
|
-
<!-- created:2023-09-02T04:02:09.680Z sid: -->
|
|
21
|
-
|