1
0

feat: automatically create subtask relations based on indention

This commit is contained in:
kolaente
2022-09-28 19:46:12 +02:00
parent 8c394d8024
commit cc378b83fe
3 changed files with 191 additions and 11 deletions

View File

@ -0,0 +1,109 @@
import {describe, it, expect} from 'vitest'
import {parseSubtasksViaIndention} from '@/helpers/parseSubtasksViaIndention'
describe('Parse Subtasks via Relation', () => {
it('Should not return a parent for a single task', () => {
const tasks = parseSubtasksViaIndention('single task')
expect(tasks).to.have.length(1)
expect(tasks[0].parent).toBeNull()
})
it('Should not return a parent for multiple tasks without indention', () => {
const tasks = parseSubtasksViaIndention(`task one
task two`)
expect(tasks).to.have.length(2)
expect(tasks[0].parent).toBeNull()
expect(tasks[1].parent).toBeNull()
})
it('Should return a parent for two tasks with indention', () => {
const tasks = parseSubtasksViaIndention(`parent task
sub task`)
expect(tasks).to.have.length(2)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task')
})
it('Should return a parent for multiple subtasks', () => {
const tasks = parseSubtasksViaIndention(`parent task
sub task one
sub task two`)
expect(tasks).to.have.length(3)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task one')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[2].title).to.eq('sub task two')
expect(tasks[2].parent).to.eq('parent task')
})
it('Should work with multiple indention levels', () => {
const tasks = parseSubtasksViaIndention(`parent task
sub task
sub sub task`)
expect(tasks).to.have.length(3)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[2].title).to.eq('sub sub task')
expect(tasks[2].parent).to.eq('sub task')
})
it('Should work with multiple indention levels and multiple tasks', () => {
const tasks = parseSubtasksViaIndention(`parent task
sub task
sub sub task one
sub sub task two`)
expect(tasks).to.have.length(4)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[2].title).to.eq('sub sub task one')
expect(tasks[2].parent).to.eq('sub task')
expect(tasks[3].title).to.eq('sub sub task two')
expect(tasks[3].parent).to.eq('sub task')
})
it('Should work with multiple indention levels and multiple tasks', () => {
const tasks = parseSubtasksViaIndention(`parent task
sub task
sub sub task one
sub sub sub task
sub sub task two`)
expect(tasks).to.have.length(5)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[2].title).to.eq('sub sub task one')
expect(tasks[2].parent).to.eq('sub task')
expect(tasks[3].title).to.eq('sub sub sub task')
expect(tasks[3].parent).to.eq('sub sub task one')
expect(tasks[4].title).to.eq('sub sub task two')
expect(tasks[4].parent).to.eq('sub task')
})
it('Should return a parent for multiple subtasks with special stuff', () => {
const tasks = parseSubtasksViaIndention(`* parent task
* sub task one
sub task two`)
expect(tasks).to.have.length(3)
expect(tasks[0].parent).toBeNull()
expect(tasks[0].title).to.eq('parent task')
expect(tasks[1].title).to.eq('sub task one')
expect(tasks[1].parent).to.eq('parent task')
expect(tasks[2].title).to.eq('sub task two')
expect(tasks[2].parent).to.eq('parent task')
})
it('Should not break when the first line is indented', () => {
const tasks = parseSubtasksViaIndention(' single task')
expect(tasks).to.have.length(1)
expect(tasks[0].parent).toBeNull()
})
})

View File

@ -0,0 +1,42 @@
export interface TaskWithParent {
title: string,
parent: string | null,
}
function cleanupTitle(title: string) {
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
}
const spaceRegex = /^ */
// taskTitles should be multiple lines of task tiles with indention to declare their parent/subtask
// relation between each other.
export function parseSubtasksViaIndention(taskTitles: string): TaskWithParent[] {
const titles = taskTitles.split(/[\r\n]+/)
return titles.map((t, i) => {
const task: TaskWithParent = {
title: cleanupTitle(t),
parent: null,
}
const matched = spaceRegex.exec(t)
const matchedSpaces = matched ? matched[0].length : 0
if (matchedSpaces > 0 && i > 0) {
// Go up the tree to find the first task with less indention than the current one
let pi = 1
let parentSpaces = 0
do {
task.parent = cleanupTitle(titles[i - pi])
pi++
const parentMatched = spaceRegex.exec(task.parent)
parentSpaces = parentMatched ? parentMatched[0].length : 0
} while (parentSpaces >= matchedSpaces)
task.title = cleanupTitle(t.replace(spaceRegex, ''))
task.parent = task.parent.replace(spaceRegex, '')
}
return task
})
}