diff --git a/internal/parser/parser.go b/internal/parser/parser.go index cb0721c..8bcc5ed 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -14,16 +14,16 @@ import ( // Task represents a parsed todo item. type Task struct { - Raw string `json:"raw"` - Completed bool `json:"completed"` - CompletionDate *time.Time `json:"completion_date,omitempty"` - Priority *rune `json:"priority,omitempty"` // 'A'..'Z' - CreationDate *time.Time `json:"creation_date,omitempty"` - DueDate *time.Time `json:"due_date,omitempty"` // parsed from due:YYYY-MM-DD - Description string `json:"description"` // description with projects/contexts/metadata removed - Projects []string `json:"projects"` - Contexts []string `json:"contexts"` - Metadata map[string]string `json:"metadata"` + Raw string `json:"raw"` + Completed bool `json:"completed"` + CompletionDate *time.Time `json:"completion_date,omitempty"` + Priority *rune `json:"priority,omitempty"` // 'A'..'Z' + CreationDate *time.Time `json:"creation_date,omitempty"` + DueDate *time.Time `json:"due_date,omitempty"` // parsed from due:YYYY-MM-DD + Description string `json:"description"` // description with projects/contexts/metadata removed + Projects []string `json:"projects"` + Contexts []string `json:"contexts"` + Metadata map[string][]string `json:"metadata"` } func (t Task) ToRemind() string { @@ -37,13 +37,19 @@ func (t Task) ToRemind() string { sb.WriteString(" ++5 INFO \"Calendar: TODO.TX\" ") if len(t.Metadata) > 0 { - for k, v := range t.Metadata { + for k, values := range t.Metadata { if k == "due" { continue } // k must have the first letter uppercase for Remind - k = strings.ToUpper(k[:1]) + k[1:] - sb.WriteString(fmt.Sprintf("INFO \"%s: %s\" ", k, v)) + kUpper := strings.ToUpper(k[:1]) + k[1:] + if len(values) == 1 { + sb.WriteString(fmt.Sprintf("INFO \"%s: %s\" ", kUpper, values[0])) + } else { + for i, v := range values { + sb.WriteString(fmt.Sprintf("INFO \"%s%d: %s\" ", kUpper, i+1, v)) + } + } } } @@ -82,13 +88,20 @@ func (t Task) ToRemind() string { } if len(t.Metadata) > 0 { - for k := range t.Metadata { + for k, values := range t.Metadata { if k == "due" { continue } // uppercase first letter for Remind - k = strings.ToUpper(k[:1]) + k[1:] - sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", k, k)) + kUpper := strings.ToUpper(k[:1]) + k[1:] + if len(values) == 1 { + sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", kUpper, kUpper)) + } else { + for i := range values { + kNumbered := fmt.Sprintf("%s%d", kUpper, i+1) + sb.WriteString(fmt.Sprintf("%%_%s: %%<%s>", kNumbered, kNumbered)) + } + } } } @@ -181,7 +194,7 @@ func ParseReader(r io.Reader) ([]Task, []error) { func ParseLine(line string) (Task, error) { t := Task{ Raw: line, - Metadata: make(map[string]string), + Metadata: make(map[string][]string), } working := strings.TrimSpace(line) @@ -262,14 +275,14 @@ func ParseLine(line string) (Task, error) { } val += " " + toks[i] } - t.Metadata["location"] = val + t.Metadata["location"] = append(t.Metadata["location"], val) continue } if !strings.ContainsAny(tok, " \t") && strings.Contains(tok, ":") { parts := strings.SplitN(tok, ":", 2) k, v := parts[0], parts[1] if k != "" && v != "" && !isProtocolKey(k) { - t.Metadata[k] = v + t.Metadata[k] = append(t.Metadata[k], v) if k == "due" && dateRe.MatchString(v) { if dt, err := time.Parse(dateLayout, v); err == nil { t.DueDate = &dt @@ -277,6 +290,7 @@ func ParseLine(line string) (Task, error) { } continue } + } descParts = append(descParts, tok) } diff --git a/main.go b/main.go index 46114fa..459aae4 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ var ( inputFile string outputFile string debug bool - version = "v2.0.0" + version = "v3.0.0" ) func main() {