Clean up task editor and time editor
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
}
|
||||
|
||||
9
main.go
9
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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user