-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparse.ts
143 lines (124 loc) · 3.64 KB
/
parse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { marked } from 'marked'
//FIXME THIS CODE IS FILTHY! FILTHY I SAY!
interface Output {
"right-now"?: string
"now-now"?: string
"just-now"?: string
now?: string
}
export interface Comment {
id: number
body?: string
}
function is_correct_comment(comment: marked.TokensList, comment_title?: string): boolean {
for (const token of comment) {
if (token.type !== 'heading') continue
if (comment_title) {
if (squish(token.text) == squish(comment_title)) return true
} else {
// if no title we're looking for an unnamed list
//NOTE possibly this sucks
switch (squish(token.text)) {
case 'nownow':
case 'rightnow':
case 'justnow':
case 'now':
return true
}
}
// only care about the first heading
return false
}
return false
}
export function parse(comments: Comment[], comment_title?: string): Output {
for (const comment of comments) {
if (!comment.body) continue
const lexed = marked.lexer(comment.body)
if (!is_correct_comment(lexed, comment_title)) continue
return parse2(lexed)
}
return {}
}
function parse2(tokens: marked.TokensList): Output {
const output: Output = {}
const iterator = tokens[Symbol.iterator]()
let item = iterator.next()
const rawUntilNextHeading = (heading: string) => {
let out = ''
item = iterator.next()
while (!item.done && item.value.type !== 'heading') {
try {
if (item.value.type as string !== 'list') {
continue // drop everything else
}
for (const task of (item.value as marked.Tokens.List).items) {
if (task.type != 'list_item') { out += task.raw; continue }
if (task.checked) continue
const { body, n, zwsps } = parseTaskLine(task.text)
switch (heading) {
case 'now':
case 'justnow':
if (zwsps < 6) {
const cipher = [...Array(zwsps+1)].map(() => '\u200B').join('')
const count = n ? ` [${n}]` : ''
out += `${body}${count}${cipher}\n`
break
}
/* falls through */
case 'nownow':
case 'rightnow':
out += `${body} [${(n ?? 0) + 1}]\n`
break
default:
out += task.raw
}
}
} finally {
item = iterator.next()
}
}
return out
}
const append = (key: keyof Output, body: string) => {
if (output[key]) {
output[key] += `\n${body.trim()}`
} else {
output[key] = body.trim()
}
}
while (!item.done) {
const value = item.value
if (value.type !== 'heading') { item = iterator.next(); continue }
const squished = squish(value.text)
switch (squished) {
case 'rightnow':
append('right-now', rawUntilNextHeading(squished))
break
case 'nownow':
append('now-now', rawUntilNextHeading(squished))
break
case 'justnow':
append('just-now', rawUntilNextHeading(squished))
break
case 'now':
append('now', rawUntilNextHeading(squished))
break
default:
item = iterator.next()
}
}
return output
}
function parseTaskLine(input: string): { body: string, n?: number, zwsps: number } {
const match = input.match(/^(.*?)\s*(\[(\d+)\])?(\u200B*)$/)
if (!match) { throw new Error(`Could not parse task line: ${input}`) }
const body = `- [ ] ${match[1].trim()}`
let n: number | undefined = parseInt(match[3])
if (Number.isNaN(n)) { n = undefined }
const zwsps = match[4].length
return {body, n, zwsps}
}
function squish(input: string): string {
return input.toLowerCase().replace(/(\s|-)+/g, '').trim()
}