From 1a9fd9b4b05e8822075731c3dd72faa12d32c23b Mon Sep 17 00:00:00 2001 From: Martin Pander Date: Tue, 17 Feb 2026 20:57:21 +0100 Subject: [PATCH] Clean up task editor and time editor --- common/keymap.go | 8 +-- common/task.go | 6 +- main.go | 9 ++- pages/taskEditor.go | 22 +------ pages/timeEditor.go | 10 ++- taskwarrior/models.go | 8 +-- taskwarrior/taskwarrior.go | 106 ++++++++++++++++++++------------ taskwarrior/taskwarrior_test.go | 3 +- timewarrior/models.go | 14 ++--- timewarrior/timewarrior.go | 70 +++++++++++---------- 10 files changed, 141 insertions(+), 115 deletions(-) diff --git a/common/keymap.go b/common/keymap.go index bca6068..c74f8d5 100644 --- a/common/keymap.go +++ b/common/keymap.go @@ -106,13 +106,13 @@ func NewKeymap() *Keymap { ), NextPage: key.NewBinding( - key.WithKeys("]"), - key.WithHelp("[", "Next page"), + key.WithKeys("]", "L"), + key.WithHelp("]/L", "Next page"), ), PrevPage: key.NewBinding( - key.WithKeys("["), - key.WithHelp("]", "Previous page"), + key.WithKeys("[", "H"), + key.WithHelp("[/H", "Previous page"), ), SetReport: key.NewBinding( diff --git a/common/task.go b/common/task.go index c97c4a7..6e79341 100644 --- a/common/task.go +++ b/common/task.go @@ -178,7 +178,7 @@ func (t *Task) GetString(fieldWFormat string) string { return t.Recur default: - slog.Error(fmt.Sprintf("Field not implemented: %s", field)) + slog.Error("Field not implemented", "field", field) return "" } } @@ -214,7 +214,7 @@ func formatDate(date string, format string) string { dtformat := "20060102T150405Z" dt, err := time.Parse(dtformat, date) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return "" } @@ -238,7 +238,7 @@ func formatDate(date string, format string) string { case "countdown": return parseCountdown(time.Since(dt)) default: - slog.Error(fmt.Sprintf("Date format not implemented: %s", format)) + slog.Error("Date format not implemented", "format", format) return "" } } diff --git a/main.go b/main.go index 237b1e4..060cb55 100644 --- a/main.go +++ b/main.go @@ -40,12 +40,15 @@ func main() { timewConfigPath = "" } - ts := taskwarrior.NewTaskSquire(taskrcPath) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ts := taskwarrior.NewTaskSquire(ctx, taskrcPath) if ts == nil { log.Fatal("Failed to initialize TaskSquire. Please check your Taskwarrior installation and taskrc file.") } - tws := timewarrior.NewTimeSquire(timewConfigPath) - ctx := context.Background() + + tws := timewarrior.NewTimeSquire(ctx, timewConfigPath) common := common.NewCommon(ctx, ts, tws) file, err := os.OpenFile("/tmp/tasksquire.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) diff --git a/pages/taskEditor.go b/pages/taskEditor.go index 351e738..77a5a91 100644 --- a/pages/taskEditor.go +++ b/pages/taskEditor.go @@ -3,7 +3,6 @@ package pages import ( "fmt" "log/slog" - "strings" "tasksquire/common" "time" @@ -510,6 +509,9 @@ func NewTaskEdit(com *common.Common, task *taskwarrior.Task, isNew bool) *taskEd udaValues := make(map[string]*string) for _, uda := range com.Udas { + if uda.Name == "parenttask" { + continue + } switch uda.Type { case taskwarrior.UdaTypeNumeric: val := "" @@ -691,13 +693,9 @@ type tagEdit struct { fields []huh.Field cursor int - - newTagsValue *string } func NewTagEdit(common *common.Common, selected *[]string, options []string) *tagEdit { - newTags := "" - defaultKeymap := huh.NewDefaultKeyMap() t := tagEdit{ @@ -711,14 +709,7 @@ func NewTagEdit(common *common.Common, selected *[]string, options []string) *ta Filterable(true). WithKeyMap(defaultKeymap). WithTheme(common.Styles.Form), - huh.NewInput(). - Title("New Tags"). - Value(&newTags). - Inline(true). - Prompt(": "). - WithTheme(common.Styles.Form), }, - newTagsValue: &newTags, } return &t @@ -1173,13 +1164,6 @@ func (p *TaskEditorPage) updateTasksCmd() tea.Msg { // p.task.Project = *p.areas[0].(*taskEdit).newProjectName // } - if *(p.areas[1].(*tagEdit).newTagsValue) != "" { - newTags := strings.Split(*p.areas[1].(*tagEdit).newTagsValue, " ") - if len(newTags) > 0 { - p.task.Tags = append(p.task.Tags, newTags...) - } - } - // Sync timestamp fields from the timeEdit area (area 2) p.areas[2].(*timeEdit).syncToTaskFields() diff --git a/pages/timeEditor.go b/pages/timeEditor.go index 40e86fb..02c9a14 100644 --- a/pages/timeEditor.go +++ b/pages/timeEditor.go @@ -568,7 +568,15 @@ func (p *TimeEditorPage) View() string { helpStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("8")) sections = append(sections, helpStyle.Render("tab/shift+tab: navigate • enter: select/save • esc: cancel")) - return lipgloss.JoinVertical(lipgloss.Left, sections...) + content := lipgloss.JoinVertical(lipgloss.Left, sections...) + + return lipgloss.Place( + p.common.Width(), + p.common.Height(), + lipgloss.Left, + lipgloss.Top, + content, + ) } func (p *TimeEditorPage) SetSize(width, height int) { diff --git a/taskwarrior/models.go b/taskwarrior/models.go index 573b095..c422b1d 100644 --- a/taskwarrior/models.go +++ b/taskwarrior/models.go @@ -229,7 +229,7 @@ func (t *Task) GetString(fieldWFormat string) string { } } - slog.Error(fmt.Sprintf("Field not implemented: %s", field)) + slog.Error("Field not implemented", "field", field) return "" } @@ -262,7 +262,7 @@ func (t *Task) GetDate(field string) time.Time { dt, err := time.Parse(dtformat, dateString) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return time.Time{} } return dt @@ -384,7 +384,7 @@ func formatDate(date string, format string) string { dt, err := time.Parse(dtformat, date) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return "" } @@ -408,7 +408,7 @@ func formatDate(date string, format string) string { case "countdown": return parseCountdown(time.Since(dt)) default: - slog.Error(fmt.Sprintf("Date format not implemented: %s", format)) + slog.Error("Date format not implemented", "format", format) return "" } } diff --git a/taskwarrior/taskwarrior.go b/taskwarrior/taskwarrior.go index 0e695d2..d2dfea4 100644 --- a/taskwarrior/taskwarrior.go +++ b/taskwarrior/taskwarrior.go @@ -5,6 +5,7 @@ package taskwarrior import ( "bytes" + "context" "encoding/json" "fmt" "log/slog" @@ -111,11 +112,12 @@ type TaskSquire struct { config *TWConfig reports Reports contexts Contexts + ctx context.Context mutex sync.Mutex } -func NewTaskSquire(configLocation string) *TaskSquire { +func NewTaskSquire(ctx context.Context, configLocation string) *TaskSquire { if _, err := exec.LookPath(twBinary); err != nil { slog.Error("Taskwarrior not found") return nil @@ -125,6 +127,7 @@ func NewTaskSquire(configLocation string) *TaskSquire { ts := &TaskSquire{ configLocation: configLocation, defaultArgs: defaultArgs, + ctx: ctx, mutex: sync.Mutex{}, } ts.config = ts.extractConfig() @@ -169,17 +172,20 @@ func (ts *TaskSquire) GetTasks(report *Report, filter ...string) Tasks { exportArgs = append(exportArgs, report.Name) } - cmd := exec.Command(twBinary, append(args, exportArgs...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(args, exportArgs...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting report:", err) + if ts.ctx.Err() == context.Canceled { + return nil + } + slog.Error("Failed getting report", "error", err) return nil } tasks := make(Tasks, 0) err = json.Unmarshal(output, &tasks) if err != nil { - slog.Error("Failed unmarshalling tasks:", err) + slog.Error("Failed unmarshalling tasks", "error", err) return nil } @@ -198,10 +204,10 @@ func (ts *TaskSquire) GetTasks(report *Report, filter ...string) Tasks { } func (ts *TaskSquire) getIds(filter []string) string { - cmd := exec.Command(twBinary, append(append(ts.defaultArgs, filter...), "_ids")...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(append(ts.defaultArgs, filter...), "_ids")...) out, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting field:", err) + slog.Error("Failed getting field", "error", err) return "" } @@ -219,7 +225,7 @@ func (ts *TaskSquire) GetContext(context string) *Context { if context, ok := ts.contexts[context]; ok { return context } else { - slog.Error(fmt.Sprintf("Context not found: %s", context.Name)) + slog.Error("Context not found", "name", context) return nil } } @@ -248,11 +254,11 @@ func (ts *TaskSquire) GetProjects() []string { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_projects"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"_projects"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting projects:", err) + slog.Error("Failed getting projects", "error", err) return nil } @@ -286,11 +292,11 @@ func (ts *TaskSquire) GetTags() []string { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_tags"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"_tags"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting tags:", err) + slog.Error("Failed getting tags", "error", err) return nil } @@ -333,10 +339,13 @@ func (ts *TaskSquire) GetUdas() []Uda { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, "_udas")...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, "_udas")...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting config:", err) + if ts.ctx.Err() == context.Canceled { + return nil + } + slog.Error("Failed getting UDAs", "error", err) return nil } @@ -345,7 +354,7 @@ func (ts *TaskSquire) GetUdas() []Uda { if uda != "" { udatype := UdaType(ts.config.Get(fmt.Sprintf("uda.%s.type", uda))) if udatype == "" { - slog.Error(fmt.Sprintf("UDA type not found: %s", uda)) + slog.Error("UDA type not found", "uda", uda) continue } @@ -376,9 +385,9 @@ func (ts *TaskSquire) SetContext(context *Context) error { return nil } - cmd := exec.Command(twBinary, []string{"context", context.Name}...) + cmd := exec.CommandContext(ts.ctx, twBinary, []string{"context", context.Name}...) if err := cmd.Run(); err != nil { - slog.Error("Failed setting context:", err) + slog.Error("Failed setting context", "error", err) return err } @@ -433,14 +442,17 @@ func (ts *TaskSquire) ImportTask(task *Task) { tasks, err := json.Marshal(Tasks{task}) if err != nil { - slog.Error("Failed marshalling task:", err) + slog.Error("Failed marshalling task", "error", err) } - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"import", "-"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"import", "-"}...)...) cmd.Stdin = bytes.NewBuffer(tasks) out, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed modifying task:", err, string(out)) + if ts.ctx.Err() == context.Canceled { + return + } + slog.Error("Failed modifying task", "error", err, "output", string(out)) } } @@ -448,10 +460,10 @@ func (ts *TaskSquire) SetTaskDone(task *Task) { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"done", task.Uuid}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"done", task.Uuid}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed setting task done:", err) + slog.Error("Failed setting task done", "error", err) } } @@ -459,10 +471,10 @@ func (ts *TaskSquire) DeleteTask(task *Task) { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{task.Uuid, "delete"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{task.Uuid, "delete"}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed deleting task:", err) + slog.Error("Failed deleting task", "error", err) } } @@ -471,10 +483,10 @@ func (ts *TaskSquire) Undo() { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"undo"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"undo"}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed undoing task:", err) + slog.Error("Failed undoing task", "error", err) } } @@ -482,10 +494,10 @@ func (ts *TaskSquire) StartTask(task *Task) { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"start", task.Uuid}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"start", task.Uuid}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed starting task:", err) + slog.Error("Failed starting task", "error", err) } } @@ -493,10 +505,10 @@ func (ts *TaskSquire) StopTask(task *Task) { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"stop", task.Uuid}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"stop", task.Uuid}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed stopping task:", err) + slog.Error("Failed stopping task", "error", err) } } @@ -504,25 +516,28 @@ func (ts *TaskSquire) StopActiveTasks() { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"+ACTIVE", "export"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"+ACTIVE", "export"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting active tasks:", "error", err, "output", string(output)) + if ts.ctx.Err() == context.Canceled { + return + } + slog.Error("Failed getting active tasks", "error", err, "output", string(output)) return } tasks := make(Tasks, 0) err = json.Unmarshal(output, &tasks) if err != nil { - slog.Error("Failed unmarshalling active tasks:", err) + slog.Error("Failed unmarshalling active tasks", "error", err) return } for _, task := range tasks { - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"stop", task.Uuid}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"stop", task.Uuid}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed stopping task:", err) + slog.Error("Failed stopping task", "error", err) } } } @@ -531,10 +546,13 @@ func (ts *TaskSquire) GetInformation(task *Task) string { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{fmt.Sprintf("uuid:%s", task.Uuid), "information"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{fmt.Sprintf("uuid:%s", task.Uuid), "information"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting task information:", err) + if ts.ctx.Err() == context.Canceled { + return "" + } + slog.Error("Failed getting task information", "error", err) return "" } @@ -545,17 +563,20 @@ func (ts *TaskSquire) AddTaskAnnotation(uuid string, annotation string) { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{uuid, "annotate", annotation}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{uuid, "annotate", annotation}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed adding annotation:", err) + slog.Error("Failed adding annotation", "error", err) } } func (ts *TaskSquire) extractConfig() *TWConfig { - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_show"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"_show"}...)...) output, err := cmd.CombinedOutput() if err != nil { + if ts.ctx.Err() == context.Canceled { + return nil + } slog.Error("Failed getting config", "error", err, "output", string(output)) return nil } @@ -564,7 +585,7 @@ func (ts *TaskSquire) extractConfig() *TWConfig { } func (ts *TaskSquire) extractReports() Reports { - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_config"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"_config"}...)...) output, err := cmd.CombinedOutput() if err != nil { return nil @@ -610,10 +631,13 @@ func extractReports(config string) []string { } func (ts *TaskSquire) extractContexts() Contexts { - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_context"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"_context"}...)...) output, err := cmd.CombinedOutput() if err != nil { + if ts.ctx.Err() == context.Canceled { + return nil + } slog.Error("Failed getting contexts", "error", err, "output", string(output)) return nil } diff --git a/taskwarrior/taskwarrior_test.go b/taskwarrior/taskwarrior_test.go index 823b7f6..6d5ed87 100644 --- a/taskwarrior/taskwarrior_test.go +++ b/taskwarrior/taskwarrior_test.go @@ -1,6 +1,7 @@ package taskwarrior import ( + "context" "fmt" "os" "testing" @@ -56,7 +57,7 @@ func TestTaskSquire_GetContext(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.prep() - ts := NewTaskSquire(tt.fields.configLocation) + ts := NewTaskSquire(context.Background(), tt.fields.configLocation) if got := ts.GetActiveContext(); got.Name != tt.want { t.Errorf("TaskSquire.GetContext() = %v, want %v", got, tt.want) } diff --git a/timewarrior/models.go b/timewarrior/models.go index 91e0e6c..30cc0c0 100644 --- a/timewarrior/models.go +++ b/timewarrior/models.go @@ -104,7 +104,7 @@ func (i *Interval) GetString(field string) string { return "" default: - slog.Error(fmt.Sprintf("Field not implemented: %s", field)) + slog.Error("Field not implemented", "field", field) return "" } } @@ -112,7 +112,7 @@ func (i *Interval) GetString(field string) string { func (i *Interval) GetDuration() string { start, err := time.Parse(dtformat, i.Start) if err != nil { - slog.Error("Failed to parse start time:", err) + slog.Error("Failed to parse start time", "error", err) return "" } @@ -122,7 +122,7 @@ func (i *Interval) GetDuration() string { } else { end, err = time.Parse(dtformat, i.End) if err != nil { - slog.Error("Failed to parse end time:", err) + slog.Error("Failed to parse end time", "error", err) return "" } } @@ -134,7 +134,7 @@ func (i *Interval) GetDuration() string { func (i *Interval) GetStartTime() time.Time { dt, err := time.Parse(dtformat, i.Start) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return time.Time{} } return dt @@ -146,7 +146,7 @@ func (i *Interval) GetEndTime() time.Time { } dt, err := time.Parse(dtformat, i.End) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return time.Time{} } return dt @@ -187,7 +187,7 @@ func formatDate(date string, format string) string { dt, err := time.Parse(dtformat, date) if err != nil { - slog.Error("Failed to parse time:", err) + slog.Error("Failed to parse time", "error", err) return "" } @@ -211,7 +211,7 @@ func formatDate(date string, format string) string { case "relative": return parseDurationVague(time.Until(dt)) default: - slog.Error(fmt.Sprintf("Date format not implemented: %s", format)) + slog.Error("Date format not implemented", "format", format) return "" } } diff --git a/timewarrior/timewarrior.go b/timewarrior/timewarrior.go index ab7b4c4..9fe87e0 100644 --- a/timewarrior/timewarrior.go +++ b/timewarrior/timewarrior.go @@ -4,6 +4,7 @@ package timewarrior import ( "bytes" + "context" "encoding/json" "fmt" "log/slog" @@ -44,11 +45,12 @@ type TimeSquire struct { configLocation string defaultArgs []string config *TWConfig + ctx context.Context mutex sync.Mutex } -func NewTimeSquire(configLocation string) *TimeSquire { +func NewTimeSquire(ctx context.Context, configLocation string) *TimeSquire { if _, err := exec.LookPath(twBinary); err != nil { slog.Error("Timewarrior not found") return nil @@ -57,6 +59,7 @@ func NewTimeSquire(configLocation string) *TimeSquire { ts := &TimeSquire{ configLocation: configLocation, defaultArgs: []string{}, + ctx: ctx, mutex: sync.Mutex{}, } ts.config = ts.extractConfig() @@ -75,11 +78,11 @@ func (ts *TimeSquire) GetTags() []string { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"tags"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"tags"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting tags:", err) + slog.Error("Failed getting tags", "error", err) return nil } @@ -150,17 +153,20 @@ func (ts *TimeSquire) getIntervalsUnlocked(filter ...string) Intervals { args = append(args, filter...) } - cmd := exec.Command(twBinary, args...) + cmd := exec.CommandContext(ts.ctx, twBinary, args...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting intervals:", err) + if ts.ctx.Err() == context.Canceled { + return nil + } + slog.Error("Failed getting intervals", "error", err) return nil } intervals := make(Intervals, 0) err = json.Unmarshal(output, &intervals) if err != nil { - slog.Error("Failed unmarshalling intervals:", err) + slog.Error("Failed unmarshalling intervals", "error", err) return nil } @@ -194,9 +200,9 @@ func (ts *TimeSquire) StartTracking(tags []string) error { args := append(ts.defaultArgs, "start") args = append(args, tags...) - cmd := exec.Command(twBinary, args...) + cmd := exec.CommandContext(ts.ctx, twBinary, args...) if err := cmd.Run(); err != nil { - slog.Error("Failed starting tracking:", err) + slog.Error("Failed starting tracking", "error", err) return err } @@ -207,9 +213,9 @@ func (ts *TimeSquire) StopTracking() error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, "stop")...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, "stop")...) if err := cmd.Run(); err != nil { - slog.Error("Failed stopping tracking:", err) + slog.Error("Failed stopping tracking", "error", err) return err } @@ -220,9 +226,9 @@ func (ts *TimeSquire) ContinueTracking() error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, "continue")...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, "continue")...) if err := cmd.Run(); err != nil { - slog.Error("Failed continuing tracking:", err) + slog.Error("Failed continuing tracking", "error", err) return err } @@ -233,9 +239,9 @@ func (ts *TimeSquire) ContinueInterval(id int) error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"continue", fmt.Sprintf("@%d", id)}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"continue", fmt.Sprintf("@%d", id)}...)...) if err := cmd.Run(); err != nil { - slog.Error("Failed continuing interval:", err) + slog.Error("Failed continuing interval", "error", err) return err } @@ -246,9 +252,9 @@ func (ts *TimeSquire) CancelTracking() error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, "cancel")...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, "cancel")...) if err := cmd.Run(); err != nil { - slog.Error("Failed canceling tracking:", err) + slog.Error("Failed canceling tracking", "error", err) return err } @@ -259,9 +265,9 @@ func (ts *TimeSquire) DeleteInterval(id int) error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"delete", fmt.Sprintf("@%d", id)}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"delete", fmt.Sprintf("@%d", id)}...)...) if err := cmd.Run(); err != nil { - slog.Error("Failed deleting interval:", err) + slog.Error("Failed deleting interval", "error", err) return err } @@ -272,9 +278,9 @@ func (ts *TimeSquire) FillInterval(id int) error { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"fill", fmt.Sprintf("@%d", id)}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"fill", fmt.Sprintf("@%d", id)}...)...) if err := cmd.Run(); err != nil { - slog.Error("Failed filling interval:", err) + slog.Error("Failed filling interval", "error", err) return err } @@ -287,9 +293,9 @@ func (ts *TimeSquire) JoinInterval(id int) error { // Join the current interval with the previous one // The previous interval has id+1 (since intervals are ordered newest first) - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"join", fmt.Sprintf("@%d", id+1), fmt.Sprintf("@%d", id)}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"join", fmt.Sprintf("@%d", id+1), fmt.Sprintf("@%d", id)}...)...) if err := cmd.Run(); err != nil { - slog.Error("Failed joining interval:", err) + slog.Error("Failed joining interval", "error", err) return err } @@ -303,7 +309,7 @@ func (ts *TimeSquire) ModifyInterval(interval *Interval, adjust bool) error { // Export the modified interval intervals, err := json.Marshal(Intervals{interval}) if err != nil { - slog.Error("Failed marshalling interval:", err) + slog.Error("Failed marshalling interval", "error", err) return err } @@ -314,11 +320,11 @@ func (ts *TimeSquire) ModifyInterval(interval *Interval, adjust bool) error { } // Import the modified interval - cmd := exec.Command(twBinary, args...) + cmd := exec.CommandContext(ts.ctx, twBinary, args...) cmd.Stdin = bytes.NewBuffer(intervals) out, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed modifying interval:", err, string(out)) + slog.Error("Failed modifying interval", "error", err, "output", string(out)) return err } @@ -334,10 +340,10 @@ func (ts *TimeSquire) GetSummary(filter ...string) string { args = append(args, filter...) } - cmd := exec.Command(twBinary, args...) + cmd := exec.CommandContext(ts.ctx, twBinary, args...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting summary:", err) + slog.Error("Failed getting summary", "error", err) return "" } @@ -348,7 +354,7 @@ func (ts *TimeSquire) GetActive() *Interval { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"get", "dom.active"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"get", "dom.active"}...)...) output, err := cmd.CombinedOutput() if err != nil || string(output) == "0\n" { return nil @@ -369,18 +375,18 @@ func (ts *TimeSquire) Undo() { ts.mutex.Lock() defer ts.mutex.Unlock() - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"undo"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"undo"}...)...) err := cmd.Run() if err != nil { - slog.Error("Failed undoing:", err) + slog.Error("Failed undoing", "error", err) } } func (ts *TimeSquire) extractConfig() *TWConfig { - cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"show"}...)...) + cmd := exec.CommandContext(ts.ctx, twBinary, append(ts.defaultArgs, []string{"show"}...)...) output, err := cmd.CombinedOutput() if err != nil { - slog.Error("Failed getting config:", err) + slog.Error("Failed getting config", "error", err) return nil }