From 08deecf1954f6cf748812057ba5cdd9a0425bfc9 Mon Sep 17 00:00:00 2001 From: Grant Ammons Date: Tue, 3 Oct 2017 07:20:02 -0400 Subject: Refactor the formatter to an interface and rename it This change refactors the formatter to be an interface called Printer. `NewApp` will use the functionality of `ScreenPrinter`, which is what I refactored the old `Formatter` class into. There is a new class the implements the `Printer` interface called `MemoryPrinter`, which will simply store the `groups` as they were passed to it. This will allow tests in `app_test.go` to become much more powerful, since `App` no longer needs to be tightly coupled to printing to the screen. --- todolist/app.go | 13 ++-- todolist/formatter.go | 152 --------------------------------------------- todolist/printer.go | 5 ++ todolist/screen_printer.go | 131 ++++++++++++++++++++++++++++++++++++++ todolist/util.go | 20 ++++++ 5 files changed, 164 insertions(+), 157 deletions(-) delete mode 100644 todolist/formatter.go create mode 100644 todolist/printer.go create mode 100644 todolist/screen_printer.go (limited to 'todolist') diff --git a/todolist/app.go b/todolist/app.go index 24e2a61..ad02569 100644 --- a/todolist/app.go +++ b/todolist/app.go @@ -9,11 +9,16 @@ import ( type App struct { TodoStore Store + Printer Printer TodoList *TodoList } func NewApp() *App { - app := &App{TodoList: &TodoList{}, TodoStore: NewFileStore()} + app := &App{ + TodoList: &TodoList{}, + Printer: NewScreenPrinter(), + TodoStore: NewFileStore(), + } return app } @@ -179,8 +184,7 @@ func (a *App) HandleNotes(input string) { } else if parser.ParseShowNote(todo, input) { groups := map[string][]*Todo{} groups[""] = append(groups[""], todo) - formatter := NewFormatter(&GroupedTodos{Groups: groups}) - formatter.Print(true) + a.Printer.Print(&GroupedTodos{Groups: groups}, false) return } a.Save() @@ -202,9 +206,8 @@ func (a *App) ListTodos(input string) { filtered := NewFilter(a.TodoList.Todos()).Filter(input) grouped := a.getGroups(input, filtered) - formatter := NewFormatter(grouped) re, _ := regexp.Compile(`^ln`) - formatter.Print(re.MatchString(input)) + a.Printer.Print(grouped, re.MatchString(input)) } func (a *App) PrioritizeTodo(input string) { diff --git a/todolist/formatter.go b/todolist/formatter.go deleted file mode 100644 index e7863db..0000000 --- a/todolist/formatter.go +++ /dev/null @@ -1,152 +0,0 @@ -package todolist - -import ( - "fmt" - "os" - "regexp" - "sort" - "strconv" - "strings" - "text/tabwriter" - "time" - - "github.com/fatih/color" -) - -type Formatter struct { - GroupedTodos *GroupedTodos - Writer *tabwriter.Writer -} - -func NewFormatter(todos *GroupedTodos) *Formatter { - w := new(tabwriter.Writer) - w.Init(os.Stdout, 0, 8, 0, '\t', 0) - formatter := &Formatter{GroupedTodos: todos, Writer: w} - return formatter -} - -func (f *Formatter) Print(printNotes bool) { - cyan := color.New(color.FgCyan).SprintFunc() - - var keys []string - for key := range f.GroupedTodos.Groups { - keys = append(keys, key) - } - sort.Strings(keys) - - for _, key := range keys { - fmt.Fprintf(f.Writer, "\n %s\n", cyan(key)) - for _, todo := range f.GroupedTodos.Groups[key] { - f.printTodo(todo) - if printNotes { - for nid, note := range todo.Notes { - fmt.Fprintf(f.Writer, " %s\t%s\t\n", - cyan(strconv.Itoa(nid)), note) - } - } - } - } - f.Writer.Flush() -} - -func (f *Formatter) printTodo(todo *Todo) { - yellow := color.New(color.FgYellow) - if todo.IsPriority { - yellow.Add(color.Bold, color.Italic) - } - fmt.Fprintf(f.Writer, " %s\t%s\t%s\t%s\t\n", - yellow.SprintFunc()(strconv.Itoa(todo.Id)), - f.formatCompleted(todo.Completed), - f.formatDue(todo.Due, todo.IsPriority), - f.formatSubject(todo.Subject, todo.IsPriority)) -} - -func (f *Formatter) formatDue(due string, isPriority bool) string { - blue := color.New(color.FgBlue) - red := color.New(color.FgRed) - - if isPriority { - blue.Add(color.Bold, color.Italic) - red.Add(color.Bold, color.Italic) - } - - if due == "" { - return blue.SprintFunc()(" ") - } - dueTime, err := time.Parse("2006-01-02", due) - - if err != nil { - fmt.Println(err) - fmt.Println("This may due to the corruption of .todos.json file.") - os.Exit(-1) - } - - if isToday(dueTime) { - return blue.SprintFunc()("today") - } else if isTomorrow(dueTime) { - return blue.SprintFunc()("tomorrow") - } else if isPastDue(dueTime) { - return red.SprintFunc()(dueTime.Format("Mon Jan 2")) - } else { - return blue.SprintFunc()(dueTime.Format("Mon Jan 2")) - } -} - -func isToday(t time.Time) bool { - nowYear, nowMonth, nowDay := time.Now().Date() - timeYear, timeMonth, timeDay := t.Date() - return nowYear == timeYear && - nowMonth == timeMonth && - nowDay == timeDay -} - -func isTomorrow(t time.Time) bool { - nowYear, nowMonth, nowDay := time.Now().AddDate(0, 0, 1).Date() - timeYear, timeMonth, timeDay := t.Date() - return nowYear == timeYear && - nowMonth == timeMonth && - nowDay == timeDay -} - -func isPastDue(t time.Time) bool { - return time.Now().After(t) -} - -func (f *Formatter) formatSubject(subject string, isPriority bool) string { - - red := color.New(color.FgRed) - magenta := color.New(color.FgMagenta) - white := color.New(color.FgWhite) - - if isPriority { - red.Add(color.Bold, color.Italic) - magenta.Add(color.Bold, color.Italic) - white.Add(color.Bold, color.Italic) - } - - splitted := strings.Split(subject, " ") - projectRegex, _ := regexp.Compile(`\+[\p{L}\d_]+`) - contextRegex, _ := regexp.Compile(`\@[\p{L}\d_]+`) - - coloredWords := []string{} - - for _, word := range splitted { - if projectRegex.MatchString(word) { - coloredWords = append(coloredWords, magenta.SprintFunc()(word)) - } else if contextRegex.MatchString(word) { - coloredWords = append(coloredWords, red.SprintFunc()(word)) - } else { - coloredWords = append(coloredWords, white.SprintFunc()(word)) - } - } - return strings.Join(coloredWords, " ") - -} - -func (f *Formatter) formatCompleted(completed bool) string { - if completed { - return "[x]" - } else { - return "[ ]" - } -} diff --git a/todolist/printer.go b/todolist/printer.go new file mode 100644 index 0000000..805cc6e --- /dev/null +++ b/todolist/printer.go @@ -0,0 +1,5 @@ +package todolist + +type Printer interface { + Print(*GroupedTodos, bool) +} diff --git a/todolist/screen_printer.go b/todolist/screen_printer.go new file mode 100644 index 0000000..eab0c39 --- /dev/null +++ b/todolist/screen_printer.go @@ -0,0 +1,131 @@ +package todolist + +import ( + "fmt" + "os" + "regexp" + "sort" + "strconv" + "strings" + "text/tabwriter" + "time" + + "github.com/fatih/color" +) + +type ScreenPrinter struct { + Writer *tabwriter.Writer +} + +func NewScreenPrinter() *ScreenPrinter { + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + formatter := &ScreenPrinter{Writer: w} + return formatter +} + +func (f *ScreenPrinter) Print(groupedTodos *GroupedTodos, printNotes bool) { + cyan := color.New(color.FgCyan).SprintFunc() + + var keys []string + for key := range groupedTodos.Groups { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + fmt.Fprintf(f.Writer, "\n %s\n", cyan(key)) + for _, todo := range groupedTodos.Groups[key] { + f.printTodo(todo) + if printNotes { + for nid, note := range todo.Notes { + fmt.Fprintf(f.Writer, " %s\t%s\t\n", + cyan(strconv.Itoa(nid)), note) + } + } + } + } + f.Writer.Flush() +} + +func (f *ScreenPrinter) printTodo(todo *Todo) { + yellow := color.New(color.FgYellow) + if todo.IsPriority { + yellow.Add(color.Bold, color.Italic) + } + fmt.Fprintf(f.Writer, " %s\t%s\t%s\t%s\t\n", + yellow.SprintFunc()(strconv.Itoa(todo.Id)), + f.formatCompleted(todo.Completed), + f.formatDue(todo.Due, todo.IsPriority), + f.formatSubject(todo.Subject, todo.IsPriority)) +} + +func (f *ScreenPrinter) formatDue(due string, isPriority bool) string { + blue := color.New(color.FgBlue) + red := color.New(color.FgRed) + + if isPriority { + blue.Add(color.Bold, color.Italic) + red.Add(color.Bold, color.Italic) + } + + if due == "" { + return blue.SprintFunc()(" ") + } + dueTime, err := time.Parse("2006-01-02", due) + + if err != nil { + fmt.Println(err) + fmt.Println("This may due to the corruption of .todos.json file.") + os.Exit(-1) + } + + if isToday(dueTime) { + return blue.SprintFunc()("today") + } else if isTomorrow(dueTime) { + return blue.SprintFunc()("tomorrow") + } else if isPastDue(dueTime) { + return red.SprintFunc()(dueTime.Format("Mon Jan 2")) + } else { + return blue.SprintFunc()(dueTime.Format("Mon Jan 2")) + } +} + +func (f *ScreenPrinter) formatSubject(subject string, isPriority bool) string { + + red := color.New(color.FgRed) + magenta := color.New(color.FgMagenta) + white := color.New(color.FgWhite) + + if isPriority { + red.Add(color.Bold, color.Italic) + magenta.Add(color.Bold, color.Italic) + white.Add(color.Bold, color.Italic) + } + + splitted := strings.Split(subject, " ") + projectRegex, _ := regexp.Compile(`\+[\p{L}\d_]+`) + contextRegex, _ := regexp.Compile(`\@[\p{L}\d_]+`) + + coloredWords := []string{} + + for _, word := range splitted { + if projectRegex.MatchString(word) { + coloredWords = append(coloredWords, magenta.SprintFunc()(word)) + } else if contextRegex.MatchString(word) { + coloredWords = append(coloredWords, red.SprintFunc()(word)) + } else { + coloredWords = append(coloredWords, white.SprintFunc()(word)) + } + } + return strings.Join(coloredWords, " ") + +} + +func (f *ScreenPrinter) formatCompleted(completed bool) string { + if completed { + return "[x]" + } else { + return "[ ]" + } +} diff --git a/todolist/util.go b/todolist/util.go index ff3dbba..6fa93f3 100644 --- a/todolist/util.go +++ b/todolist/util.go @@ -59,3 +59,23 @@ func pluralize(count int, singular, plural string) string { } return singular } + +func isToday(t time.Time) bool { + nowYear, nowMonth, nowDay := time.Now().Date() + timeYear, timeMonth, timeDay := t.Date() + return nowYear == timeYear && + nowMonth == timeMonth && + nowDay == timeDay +} + +func isTomorrow(t time.Time) bool { + nowYear, nowMonth, nowDay := time.Now().AddDate(0, 0, 1).Date() + timeYear, timeMonth, timeDay := t.Date() + return nowYear == timeYear && + nowMonth == timeMonth && + nowDay == timeDay +} + +func isPastDue(t time.Time) bool { + return time.Now().After(t) +} -- cgit v1.3