diff --git a/.gitignore b/.gitignore index a1338d6..59f1f3e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ +.idea/ diff --git a/Markdown-Bullet-Journal-icon.png b/Markdown-Bullet-Journal-icon.png new file mode 100644 index 0000000..30678ae Binary files /dev/null and b/Markdown-Bullet-Journal-icon.png differ diff --git a/Markdown-Bullet-Journal.png b/Markdown-Bullet-Journal.png new file mode 100644 index 0000000..d07bf2f Binary files /dev/null and b/Markdown-Bullet-Journal.png differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f4be55 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# Markdown Bullet Journal + +![Markdown Bullet Journal Logo](https://github.com/dballard/markdown-bullet-journal/raw/master/src/Markdown-Bullet-Journal.png "Markdown Bullet Journal Logo") + +Markdown Bullet Journal is a digital adaptation of analog tech. I found having a running todo list with daily migrations dropping done items worked best for my workflow. Add a simple summary app to show you all you have accomplished and I'm happy. + +These are a simple set of utilities that work for me. Nothing fancy + +## mdbj-migrate + +When run in a directory, takes the last dated .md file, copies it to a new file with today's date, and dropes all lines marked completed (with a '[x]'). + +## mdbj-summary + +Consumes all dated .md files in the directory and prints out all done tasks (lines with '[x]'). Properly collapses nested items into one line names like + +- Complex task + - [ ] Subpart A + - [x] Task 1 + +into + +"Complex task / Subpart A / Task 1" + +## Markdown supported + +The basics of headers with '#' + +Nested lists with '-' and indentation + +Todo and done with '[ ]' and '[x]' + +Obviously you can use other markdown features such as **bold**, *italics* and [Links](https://guides.github.com/features/mastering-markdown/) but none of these trigger any special treatment with regards to Markdown Bullet Journal. + +See the included demo file for a better idea. + +### Extra Markdown Bullet Journal 'modules' + +#### Daily Repetitive Tasks + +These are tasks you might want to do a subset of on any given day, and possibly several times. You would like it tracked, but on migration you would like it 'reset to 0' not dropped. In my case I use it with a list of exercises I pick one to do a few times a day. + +- [x] 4x10 - Pushups +- [ ] 0x10 - Crunches +- [ ] 0x10 - Lunges +- [x] 1x5 - minutes of meditation + +Will get output as: + +- 40 pushups +- 5 minutes of meditation + +And then on migration the '4' and '1' will get reset to 0 and the tasks will not get dropped + diff --git a/mdbj-summary/summary.go b/mdbj-summary/summary.go new file mode 100644 index 0000000..0a6d066 --- /dev/null +++ b/mdbj-summary/summary.go @@ -0,0 +1,107 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + "bufio" + "strings" + "fmt" + "regexp" +) + +func main() { + files, err := ioutil.ReadDir("./") + if err != nil { + log.Fatal(err) + } + + for _, file := range files { + if file.Name()[len(file.Name())-3:] == ".md" { + genReport(file) + } + } + +} + +func genReport(fileInfo os.FileInfo) { + file, err := os.Open(fileInfo.Name()) + if err != nil { + log.Fatal(err) + } + defer file.Close() + fmt.Println("") + fmt.Println(fileInfo.Name()) + + header := "" + headerPrinted := false + stack := make([]string, 0) + + total := 0 + doneCount := 0 + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if scanner.Text() == "# Daily Health" { + break + } + if strings.Trim(scanner.Text(), " \t\n\r") == "" { + continue + } + if scanner.Text()[0] == '#' { + header = scanner.Text()[2:] + headerPrinted = false; + continue + } + + startSpaces := regexp.MustCompile("^ *") + indentLevel := len(startSpaces.Find([]byte(scanner.Text())))/4 + todo := false + done := false + if indentLevel < len(stack)-1 { + stack = stack[: indentLevel+1] + } + if indentLevel == len(stack)-1 { + stack[len(stack)-1], todo, done = getText(scanner.Text(), indentLevel) + } + if indentLevel >= len(stack) { + line := "" + line, todo, done = getText(scanner.Text(), indentLevel) + stack = append(stack, line) + } + + if todo { + total += 1 + } + + if done { + if !headerPrinted { + fmt.Println(" # " + header) + headerPrinted = true + } + doneCount += 1 + fmt.Println(" " + strings.Join(stack, " / ")) + } + } + fmt.Println(doneCount, "/", total) +} + +func getText(str string, indentLevel int) (text string, todo bool, done bool) { + //fmt.Printf("indentLevel: %v str: '%s'\n", indentLevel, str ) + if len(str) < (indentLevel*4 +2) { + return "", false, false + } + text = str[indentLevel*4 +2:] + done = false + todo = false + if text[0] == '[' { + todo = true + if text[1] == 'x' || text[1] == 'X' { + done = true + } + if len(text) > 4 { + text = text[4:] + } + } + return +}