From edb3d69cfdb1de92b1aa13f99d54f1b1e495e295 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 21 May 2018 13:52:50 -0700 Subject: [PATCH] adding nested header support (mostly in summary); gofmt --- mdbj-migrate/migrate.go | 18 +++++----- mdbj-summary/2018-05-07-TEST.md | 14 ++++++++ mdbj-summary/summary.go | 50 ++++++++++++++++---------- mdbj-summary/summary_test.go | 7 ++-- process/process.go | 64 ++++++++++++++++++++++++--------- 5 files changed, 107 insertions(+), 46 deletions(-) diff --git a/mdbj-migrate/migrate.go b/mdbj-migrate/migrate.go index 233ded6..2e6754a 100644 --- a/mdbj-migrate/migrate.go +++ b/mdbj-migrate/migrate.go @@ -1,13 +1,13 @@ package main import ( - "os" - "time" - "log" - "github.com/dballard/markdown-bullet-journal/process" "fmt" + "github.com/dballard/markdown-bullet-journal/process" + "log" + "os" "strconv" "strings" + "time" ) const template = `# Work @@ -37,14 +37,14 @@ func (ph *processHandler) Writeln(line string) { } // NOP -func (ph *processHandler) Eof() {} +func (ph *processHandler) Eof() {} func (ph *processHandler) NewFile() {} -func (ph *processHandler) ProcessLine(line string, indentLevel int, stack []string, todo bool, done bool, repTask process.RepTask) { +func (ph *processHandler) ProcessLine(line string, indentLevel int, headerStack []string, lineStack []string, todo bool, done bool, repTask process.RepTask) { // TODO: handle [x] numXnum if !done || repTask.Is { if repTask.Is { - ph.Writeln(strings.Repeat("\t", indentLevel) + "- [ ] 0x" + strconv.Itoa(repTask.B) + stack[len(stack)-1] ) + ph.Writeln(strings.Repeat("\t", indentLevel) + "- [ ] 0x" + strconv.Itoa(repTask.B) + lineStack[len(lineStack)-1]) } else { ph.Writeln(line) } @@ -57,7 +57,7 @@ func main() { fileName := time.Now().Format("2006-01-02") + ".md" - if _, err := os.Stat(fileName); os.IsNotExist(err) { + if _, err := os.Stat(fileName); os.IsNotExist(err) { ph.File, err = os.Create(fileName) if err != nil { log.Fatal("Cannot open: ", fileName, " > ", err) @@ -73,7 +73,7 @@ func main() { ph.File.WriteString(template) } else { lastFile := files[len(files)-1] - fmt.Println ("Migrating " + lastFile + " to " + fileName) + fmt.Println("Migrating " + lastFile + " to " + fileName) process.ProcessFile(ph, lastFile) } } diff --git a/mdbj-summary/2018-05-07-TEST.md b/mdbj-summary/2018-05-07-TEST.md index 437605f..aec9472 100644 --- a/mdbj-summary/2018-05-07-TEST.md +++ b/mdbj-summary/2018-05-07-TEST.md @@ -22,6 +22,20 @@ - [ ] not done - note +# Nested Header + +## With Nothing + +- note +- [ ] undone + +## With something done + +- more notes +- [ ] a partly done thing + - [ ] more to do + - [x] the one done thing + # Repetition - [x] 5x5 things diff --git a/mdbj-summary/summary.go b/mdbj-summary/summary.go index aba430f..e7855a1 100644 --- a/mdbj-summary/summary.go +++ b/mdbj-summary/summary.go @@ -1,19 +1,23 @@ package main import ( - "runtime" - "os" "github.com/dballard/markdown-bullet-journal/process" "log" + "os" + "runtime" "strconv" "strings" ) +type header struct { + text string + printed bool +} + type processHandler struct { File *os.File totalCount, doneCount int - header string - headerPrinted bool + headers []header } func (ph *processHandler) Writeln(line string) { @@ -23,23 +27,36 @@ func (ph *processHandler) Writeln(line string) { func (ph *processHandler) NewFile() { ph.totalCount = 0 ph.doneCount = 0 - ph.header = "" - ph.headerPrinted = false - + ph.headers = []header{} } func (ph *processHandler) Eof() { - ph.Writeln(strconv.Itoa(ph.doneCount) + " / " + strconv.Itoa(ph.totalCount)) + ph.Writeln(strconv.Itoa(ph.doneCount) + " / " + strconv.Itoa(ph.totalCount)) } -func (ph *processHandler) ProcessLine(line string, indentLevel int, stack []string, todo bool, done bool, repTask process.RepTask) { +func (ph *processHandler) handleHeaderPrint() { + for i, header := range ph.headers { + if ! header.printed { + ph.Writeln("\t" + strings.Repeat("#", i+1) + " " + header.text) + ph.headers[i].printed = true + } + } +} + +func (ph *processHandler) ProcessLine(line string, indentLevel int, headerStack []string, lineStack []string, todo bool, done bool, repTask process.RepTask) { if strings.Trim(line, " \t\n\r") == "" { return } if line[0] == '#' { - ph.header = line[2:] - ph.headerPrinted = false; - return + last := headerStack[len(headerStack)-1] + if len(headerStack) > len(ph.headers) { + ph.headers = append(ph.headers, header{ last, false }) + } else if len(headerStack) == len(ph.headers) { + ph.headers[len(ph.headers)-1] = header{last, false} + } else if len(headerStack) < len(ph.headers) { + ph.headers = ph.headers[: len(headerStack)] + ph.headers[len(ph.headers)-1] = header{last, false} + } } // inc count of todo items (rep tasks shouldnt count towards outstanding todo, unless done) @@ -48,18 +65,15 @@ func (ph *processHandler) ProcessLine(line string, indentLevel int, stack []stri } if done { - if !ph.headerPrinted { - ph.Writeln("\t# " + ph.header) - ph.headerPrinted = true - } + ph.handleHeaderPrint() ph.doneCount += 1 repStr := "" if repTask.Is { - repStr = strconv.Itoa( repTask.A * repTask.B) + repStr = strconv.Itoa(repTask.A * repTask.B) // inc todo count here since we did a thing, its done, and we dont want a higher done count than total ph.totalCount += 1 } - ph.Writeln("\t\t" + repStr + strings.Join(stack, " / ")) + ph.Writeln("\t\t" + repStr + strings.Join(lineStack, " / ")) } } diff --git a/mdbj-summary/summary_test.go b/mdbj-summary/summary_test.go index 3eac167..bf75f59 100644 --- a/mdbj-summary/summary_test.go +++ b/mdbj-summary/summary_test.go @@ -16,9 +16,12 @@ const EXPECTED = ` # Test Data nesting1 / nesting 2 / nesting 3 / nesting 4 / nesting 5 not nested + # Nested Header + ## With something done + a partly done thing / the one done thing # Repetition 25 things -4 / 11 +5 / 15 ` func TestSummary(t *testing.T) { @@ -54,6 +57,6 @@ func TestSummary(t *testing.T) { line := strings.Count(string(result[:diffLoc]), "\n") errorStr := string(result[int(math.Max(0, float64(diffLoc - 10))) : int(math.Min(float64(len(result)), float64(diffLoc + 10))) ]) - t.Errorf("Summary results do not match expected:\nfirst difference at line %v: '%v'\n%v<---->\n%v\n", line, errorStr, string(result), EXPECTED) + t.Errorf("Summary results do not match expected:\nfirst difference at line %v\nexpected char: '%c'\nactual char: '%v'\nline: '%v'\n%v<---->\n%v\n", line, EXPECTED[diffLoc], string(result[diffLoc]), errorStr, string(result), EXPECTED) } } diff --git a/process/process.go b/process/process.go index 2ac71c5..3b5c0ff 100644 --- a/process/process.go +++ b/process/process.go @@ -1,23 +1,31 @@ package process import ( - "os" - "log" "bufio" - "regexp" "io/ioutil" + "log" + "os" + "regexp" "strconv" "strings" ) +var ( + repTaskRegExp = regexp.MustCompile("^([0-9]*)[xX]([0-9]*)") + headerExp = regexp.MustCompile("^(#+) *(.+)") +) + type RepTask struct { - Is bool + Is bool A, B int } -type ProcessHandler interface { +type Flags struct { +} + +type ProcessHandler interface { Writeln(line string) - ProcessLine(line string, indentLevel int, stack []string, todo bool, done bool, repTask RepTask) + ProcessLine(line string, indentLevel int, headerStack []string, lineStack []string, todo bool, done bool, repTask RepTask) Eof() NewFile() } @@ -40,6 +48,14 @@ func GetFiles() (filteredFiles []string) { return } +func max(x, y int) int { + if x >= y { + return x + } else { + return y + } +} + func ProcessFile(ph ProcessHandler, fileName string) { file, err := os.Open(fileName) if err != nil { @@ -48,7 +64,9 @@ func ProcessFile(ph ProcessHandler, fileName string) { defer file.Close() ph.NewFile() - stack := make([]string, 0) + headerStack := make([]string, 1) + lineStack := make([]string, 0) + //flags := Flags{} scanner := bufio.NewScanner(file) indentPattern := "" @@ -71,29 +89,42 @@ func ProcessFile(ph ProcessHandler, fileName string) { } else { indentLevel = strings.Count(startSpaces.FindString(line), indentPattern) } + + if headerExp.MatchString(line) { + matches := headerExp.FindStringSubmatch(line) + if len(matches[1]) > len(headerStack) { + headerStack = append(headerStack, matches[2]) + } else if len(matches[1]) == len(headerStack) { + headerStack[len(headerStack)-1] = matches[2] + } else if len(matches[1]) < len(headerStack) { + headerStack = headerStack[:len(matches[1])] + headerStack[len(headerStack)-1] = matches[2] + } + } + todo := false done := false var repTask RepTask - if indentLevel < len(stack)-1 { - stack = stack[: indentLevel+1] + if indentLevel < len(lineStack)-1 { + lineStack = lineStack[:indentLevel+1] } - if indentLevel == len(stack)-1 { - stack[len(stack)-1], todo, done, repTask = getText(line, indentLevel, indentPattern) + if indentLevel == len(lineStack)-1 { + lineStack[len(lineStack)-1], todo, done, repTask = getText(line, indentLevel, indentPattern) } - if indentLevel >= len(stack) { + if indentLevel >= len(lineStack) { row := "" row, todo, done, repTask = getText(line, indentLevel, indentPattern) - stack = append(stack, row) + lineStack = append(lineStack, row) } - ph.ProcessLine(line, indentLevel, stack, todo, done, repTask) + ph.ProcessLine(line, indentLevel, headerStack, lineStack, todo, done, repTask) } ph.Eof() } func getText(str string, indentLevel int, indentPattern string) (text string, todo bool, done bool, repTask RepTask) { //fmt.Printf("indentLevel: %v str: '%s'\n", indentLevel, str ) - if len(str) < (indentLevel*4 +2) { + if len(str) < (indentLevel*4 + 2) { return "", false, false, RepTask{false, 0, 0} } str = strings.TrimLeft(str, strings.Repeat(indentPattern, indentLevel)) @@ -110,7 +141,6 @@ func getText(str string, indentLevel int, indentPattern string) (text string, to text = text[4:] } - repTaskRegExp := regexp.MustCompile("^([0-9]*)[xX]([0-9]*)") if repTaskRegExp.MatchString(text) { repTask.Is = true matches := repTaskRegExp.FindStringSubmatch(text) @@ -121,4 +151,4 @@ func getText(str string, indentLevel int, indentPattern string) (text string, to } } return -} \ No newline at end of file +}