diff --git a/.goreleaser.yml b/.goreleaser.yml index 28c8c267..ba068113 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -7,7 +7,6 @@ builds: goos: - darwin - linux - - windows goarch: - 386 - amd64 diff --git a/CHANGELOG.md b/CHANGELOG.md index fac76321..a5dd7e2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## v0.10.0 + ### ⚡️ Added * DataDog module is now scrollable and interactive, by [@Seanstoppable](https://github.com/Seanstoppable) diff --git a/checklist/checklist.go b/checklist/checklist.go index 7e293878..d65e1d9f 100644 --- a/checklist/checklist.go +++ b/checklist/checklist.go @@ -53,21 +53,6 @@ func (list *Checklist) Delete() { list.Prev() } -// Demote moves the selected item down in the checklist -func (list *Checklist) Demote() { - if list.IsUnselectable() { - return - } - - j := list.selected + 1 - if j >= len(list.Items) { - j = 0 - } - - list.Swap(list.selected, j) - list.selected = j -} - // IsSelectable returns true if the checklist has selectable items, false if it does not func (list *Checklist) IsSelectable() bool { return list.selected >= 0 && list.selected < len(list.Items) @@ -78,14 +63,6 @@ func (list *Checklist) IsUnselectable() bool { return !list.IsSelectable() } -// Next selects the next item in the checklist -func (list *Checklist) Next() { - list.selected = list.selected + 1 - if list.selected >= len(list.Items) { - list.selected = 0 - } -} - // LongestLine returns the length of the longest checklist item's text func (list *Checklist) LongestLine() int { maxLen := 0 @@ -99,29 +76,6 @@ func (list *Checklist) LongestLine() int { return maxLen } -// Prev selects the previous item in the checklist -func (list *Checklist) Prev() { - list.selected = list.selected - 1 - if list.selected < 0 { - list.selected = len(list.Items) - 1 - } -} - -// Promote moves the selected item up in the checklist -func (list *Checklist) Promote() { - if list.IsUnselectable() { - return - } - - j := list.selected - 1 - if j < 0 { - j = len(list.Items) - 1 - } - - list.Swap(list.selected, j) - list.selected = j -} - func (list *Checklist) Selected() int { return list.selected } @@ -182,6 +136,54 @@ func (list *Checklist) Update(text string) { item.Text = text } +/* -------------------- Item Movement -------------------- */ + +// Prev selects the previous item UP in the checklist +func (list *Checklist) Prev() { + list.selected = list.selected - 1 + if list.selected < 0 { + list.selected = len(list.Items) - 1 + } +} + +// Next selects the next item DOWN in the checklist +func (list *Checklist) Next() { + list.selected = list.selected + 1 + if list.selected >= len(list.Items) { + list.selected = 0 + } +} + +// Promote moves the selected item UP in the checklist +func (list *Checklist) Promote() { + if list.IsUnselectable() { + return + } + + k := list.selected - 1 + if k < 0 { + k = len(list.Items) - 1 + } + + list.Swap(list.selected, k) + list.selected = k +} + +// Demote moves the selected item DOWN in the checklist +func (list *Checklist) Demote() { + if list.IsUnselectable() { + return + } + + j := list.selected + 1 + if j >= len(list.Items) { + j = 0 + } + + list.Swap(list.selected, j) + list.selected = j +} + /* -------------------- Sort Interface -------------------- */ func (list *Checklist) Len() int { diff --git a/modules/git/widget.go b/modules/git/widget.go index 68f66fa4..52d067b4 100644 --- a/modules/git/widget.go +++ b/modules/git/widget.go @@ -216,25 +216,3 @@ func (widget *Widget) findGitRepositories(repositories []*GitRepo, directory str return repositories } - -func (widget *Widget) Next() { - widget.Idx = widget.Idx + 1 - if widget.Idx == len(widget.GitRepos) { - widget.Idx = 0 - } - - if widget.DisplayFunction != nil { - widget.DisplayFunction() - } -} - -func (widget *Widget) Prev() { - widget.Idx = widget.Idx - 1 - if widget.Idx < 0 { - widget.Idx = len(widget.GitRepos) - 1 - } - - if widget.DisplayFunction != nil { - widget.DisplayFunction() - } -} diff --git a/modules/todoist/display.go b/modules/todoist/display.go index ca4aeaba..fa839f71 100644 --- a/modules/todoist/display.go +++ b/modules/todoist/display.go @@ -19,7 +19,7 @@ func (widget *Widget) display() { title := fmt.Sprintf("[green]%s[white]", proj.Project.Name) _, _, width, _ := widget.View.GetRect() - str := widget.settings.common.SigilStr(len(widget.projects), widget.idx, width) + "\n" + str := widget.settings.common.SigilStr(len(widget.projects), widget.Idx, width) + "\n" maxLen := proj.LongestLine() diff --git a/modules/todoist/keyboard.go b/modules/todoist/keyboard.go index b06cfccf..34479d3b 100644 --- a/modules/todoist/keyboard.go +++ b/modules/todoist/keyboard.go @@ -6,14 +6,14 @@ func (widget *Widget) initializeKeyboardControls() { widget.SetKeyboardChar("/", widget.ShowHelp, "Show/hide this help prompt") widget.SetKeyboardChar("c", widget.Close, "Close item") widget.SetKeyboardChar("d", widget.Delete, "Delete item") - widget.SetKeyboardChar("h", widget.PreviousProject, "Select previous project") + widget.SetKeyboardChar("h", widget.Prev, "Select previous project") widget.SetKeyboardChar("j", widget.Up, "Select previous item") widget.SetKeyboardChar("k", widget.Down, "Select next item") - widget.SetKeyboardChar("l", widget.NextProject, "Select next project") + widget.SetKeyboardChar("l", widget.Next, "Select next project") widget.SetKeyboardChar("r", widget.Refresh, "Refresh widget") widget.SetKeyboardKey(tcell.KeyDown, widget.Down, "Select next item") - widget.SetKeyboardKey(tcell.KeyLeft, widget.PreviousProject, "Select previous project") - widget.SetKeyboardKey(tcell.KeyRight, widget.NextProject, "Select next project") + widget.SetKeyboardKey(tcell.KeyLeft, widget.Prev, "Select previous project") + widget.SetKeyboardKey(tcell.KeyRight, widget.Next, "Select next project") widget.SetKeyboardKey(tcell.KeyUp, widget.Up, "Select previous item") } diff --git a/modules/todoist/widget.go b/modules/todoist/widget.go index a6dae460..9600e627 100644 --- a/modules/todoist/widget.go +++ b/modules/todoist/widget.go @@ -11,8 +11,8 @@ import ( type Widget struct { wtf.KeyboardWidget wtf.TextWidget + wtf.MultiSourceWidget - idx int projects []*Project settings *Settings } @@ -20,8 +20,9 @@ type Widget struct { // NewWidget creates a new instance of a widget func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget { widget := Widget{ - KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common), - TextWidget: wtf.NewTextWidget(app, settings.common, true), + KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common), + TextWidget: wtf.NewTextWidget(app, settings.common, true), + MultiSourceWidget: wtf.NewMultiSourceWidget(settings.common, "project", "projects"), settings: settings, } @@ -31,6 +32,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) * widget.initializeKeyboardControls() widget.View.SetInputCapture(widget.InputCapture) + widget.SetDisplayFunction(widget.display) widget.KeyboardWidget.SetView(widget.View) @@ -40,7 +42,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) * /* -------------------- Exported Functions -------------------- */ func (widget *Widget) CurrentProject() *Project { - return widget.ProjectAt(widget.idx) + return widget.ProjectAt(widget.Idx) } func (widget *Widget) ProjectAt(idx int) *Project { @@ -51,24 +53,6 @@ func (widget *Widget) ProjectAt(idx int) *Project { return widget.projects[idx] } -func (w *Widget) NextProject() { - w.idx = w.idx + 1 - if w.idx == len(w.projects) { - w.idx = 0 - } - - w.display() -} - -func (w *Widget) PreviousProject() { - w.idx = w.idx - 1 - if w.idx < 0 { - w.idx = len(w.projects) - 1 - } - - w.display() -} - func (w *Widget) Refresh() { if w.Disabled() || w.CurrentProject() == nil { return diff --git a/wtf/utils.go b/wtf/utils.go index fc743dec..4ac6dbe3 100644 --- a/wtf/utils.go +++ b/wtf/utils.go @@ -6,6 +6,7 @@ import ( "os/exec" "regexp" "runtime" + "strconv" "strings" "github.com/wtfutil/wtf/utils" @@ -21,6 +22,14 @@ const TimestampFormat = "2006-01-02T15:04:05-0700" var OpenFileUtil = "open" +// CenterText takes a string and a width and pads the left and right of the string with +// empty spaces to ensure that the string is in the middle of the returned value +// +// Example: +// +// x := CenterText("cat", 11) +// > " cat " +// func CenterText(str string, width int) string { if width < 0 { width = 0 @@ -29,6 +38,7 @@ func CenterText(str string, width int) string { return fmt.Sprintf("%[1]*s", -width, fmt.Sprintf("%[1]*s", (width+len(str))/2, str)) } +// ExecuteCommand executes an external command on the local machine as the current user func ExecuteCommand(cmd *exec.Cmd) string { stdout, err := cmd.StdoutPipe() if err != nil { @@ -52,6 +62,14 @@ func ExecuteCommand(cmd *exec.Cmd) string { return str } +// Exclude takes a slice of strings and a target string and returns the contents of the original +// slice of strings without the target string in it +// +// Example: +// +// x := Exclude([]string{"cat", "dog", "rat"}, "dog") +// > []string{"cat", "rat"} +// func Exclude(strs []string, val string) bool { for _, str := range strs { if val == str { @@ -61,16 +79,33 @@ func Exclude(strs []string, val string) bool { return true } +// FindMatch takes a regex pattern and a string of data and returns back all the matches +// in that string func FindMatch(pattern string, data string) [][]string { r := regexp.MustCompile(pattern) return r.FindAllStringSubmatch(data, -1) } +// NameFromEmail takes an email address and returns the part that comes before the @ symbol +// +// Example: +// +// NameFromEmail("test_user@example.com") +// > "Test_user" +// func NameFromEmail(email string) string { parts := strings.Split(email, "@") return strings.Title(strings.Replace(parts[0], ".", " ", -1)) } +// NamesFromEmails takes a slice of email addresses and returns a slice of the parts that +// come before the @ symbol +// +// Example: +// +// NamesFromEmail("test_user@example.com", "other_user@example.com") +// > []string{"Test_user", "Other_user"} +// func NamesFromEmails(emails []string) []string { names := []string{} @@ -112,6 +147,7 @@ func PadRow(offset int, max int) string { return strings.Repeat(" ", padSize) } +// ReadFileBytes reads the contents of a file and returns those contents as a slice of bytes func ReadFileBytes(filePath string) ([]byte, error) { fileData, err := ioutil.ReadFile(filePath) if err != nil { @@ -123,6 +159,7 @@ func ReadFileBytes(filePath string) ([]byte, error) { /* -------------------- Map Conversion -------------------- */ +// MapToStrs takes a map of interfaces and returns a map of strings func MapToStrs(aMap map[string]interface{}) map[string]string { results := make(map[string]string) @@ -135,6 +172,7 @@ func MapToStrs(aMap map[string]interface{}) map[string]string { /* -------------------- Slice Conversion -------------------- */ +// ToInts takes a slice of interfaces and returns a slice of ints func ToInts(slice []interface{}) []int { results := []int{} @@ -145,11 +183,17 @@ func ToInts(slice []interface{}) []int { return results } +// ToStrs takes a slice of interfaces and returns a slice of strings func ToStrs(slice []interface{}) []string { results := []string{} for _, val := range slice { - results = append(results, val.(string)) + switch val.(type) { + case int: + results = append(results, strconv.Itoa(val.(int))) + case string: + results = append(results, val.(string)) + } } return results