Add syncing

This commit is contained in:
Martin Pander
2026-02-03 16:04:47 +01:00
parent 2e33893e29
commit 44ddbc0f47
12 changed files with 480 additions and 72 deletions

View File

@ -83,7 +83,16 @@ func (i *Interval) GetString(field string) string {
if len(i.Tags) == 0 {
return ""
}
return strings.Join(i.Tags, " ")
// Extract and filter special tags (uuid:, project:)
_, _, displayTags := ExtractSpecialTags(i.Tags)
return strings.Join(displayTags, " ")
case "project":
project := ExtractProject(i.Tags)
if project == "" {
return "(none)"
}
return project
case "duration":
return i.GetDuration()

51
timewarrior/tags.go Normal file
View File

@ -0,0 +1,51 @@
package timewarrior
import (
"strings"
)
// Special tag prefixes for metadata
const (
UUIDPrefix = "uuid:"
ProjectPrefix = "project:"
)
// ExtractSpecialTags parses tags and separates special prefixed tags from display tags.
// Returns: uuid, project, and remaining display tags (description + user tags)
func ExtractSpecialTags(tags []string) (uuid string, project string, displayTags []string) {
displayTags = make([]string, 0, len(tags))
for _, tag := range tags {
switch {
case strings.HasPrefix(tag, UUIDPrefix):
uuid = strings.TrimPrefix(tag, UUIDPrefix)
case strings.HasPrefix(tag, ProjectPrefix):
project = strings.TrimPrefix(tag, ProjectPrefix)
default:
// Regular tag (description or user tag)
displayTags = append(displayTags, tag)
}
}
return uuid, project, displayTags
}
// ExtractUUID extracts just the UUID from tags (for sync operations)
func ExtractUUID(tags []string) string {
for _, tag := range tags {
if strings.HasPrefix(tag, UUIDPrefix) {
return strings.TrimPrefix(tag, UUIDPrefix)
}
}
return ""
}
// ExtractProject extracts just the project name from tags
func ExtractProject(tags []string) string {
for _, tag := range tags {
if strings.HasPrefix(tag, ProjectPrefix) {
return strings.TrimPrefix(tag, ProjectPrefix)
}
}
return ""
}

View File

@ -142,10 +142,9 @@ func formatTagsForCombination(tags []string) string {
return strings.Join(formatted, " ")
}
func (ts *TimeSquire) GetIntervals(filter ...string) Intervals {
ts.mutex.Lock()
defer ts.mutex.Unlock()
// getIntervalsUnlocked fetches intervals without acquiring mutex (for internal use only).
// Caller must hold ts.mutex.
func (ts *TimeSquire) getIntervalsUnlocked(filter ...string) Intervals {
args := append(ts.defaultArgs, "export")
if filter != nil {
args = append(args, filter...)
@ -176,6 +175,14 @@ func (ts *TimeSquire) GetIntervals(filter ...string) Intervals {
return intervals
}
// GetIntervals fetches intervals with the given filter, acquiring mutex lock.
func (ts *TimeSquire) GetIntervals(filter ...string) Intervals {
ts.mutex.Lock()
defer ts.mutex.Unlock()
return ts.getIntervalsUnlocked(filter...)
}
func (ts *TimeSquire) StartTracking(tags []string) error {
ts.mutex.Lock()
defer ts.mutex.Unlock()
@ -347,8 +354,8 @@ func (ts *TimeSquire) GetActive() *Interval {
return nil
}
// Get the active interval
intervals := ts.GetIntervals()
// Get the active interval using unlocked version (we already hold the mutex)
intervals := ts.getIntervalsUnlocked()
for _, interval := range intervals {
if interval.End == "" {
return interval