diff --git a/ui/components/footer/footer.go b/ui/components/footer/footer.go index e1a533d2..3e656207 100644 --- a/ui/components/footer/footer.go +++ b/ui/components/footer/footer.go @@ -99,7 +99,7 @@ func (m Model) View() string { } if m.ShowAll { - keymap := keys.GetKeyMap(m.ctx.View) + keymap := keys.CreateKeyMapForView(m.ctx.View) fullHelp := m.help.View(keymap) return lipgloss.JoinVertical(lipgloss.Top, footer, fullHelp) } diff --git a/ui/keys/branchKeys.go b/ui/keys/branchKeys.go index 13d981ad..5c2b3479 100644 --- a/ui/keys/branchKeys.go +++ b/ui/keys/branchKeys.go @@ -32,7 +32,7 @@ var BranchKeys = BranchKeyMap{ ), Delete: key.NewBinding( key.WithKeys("d", "backspace"), - key.WithHelp("d/backspace", "checkout"), + key.WithHelp("d/backspace", "delete"), ), ViewPRs: key.NewBinding( key.WithKeys("s"), diff --git a/ui/keys/keys.go b/ui/keys/keys.go index 1a1b6eec..214af3a9 100644 --- a/ui/keys/keys.go +++ b/ui/keys/keys.go @@ -31,7 +31,7 @@ type KeyMap struct { Quit key.Binding } -func GetKeyMap(viewType config.ViewType) help.KeyMap { +func CreateKeyMapForView(viewType config.ViewType) help.KeyMap { Keys.viewType = viewType return Keys } @@ -42,8 +42,10 @@ func (k KeyMap) ShortHelp() []key.Binding { func (k KeyMap) FullHelp() [][]key.Binding { var additionalKeys []key.Binding - if k.viewType == config.PRsView || k.viewType == config.RepoView { + if k.viewType == config.PRsView { additionalKeys = PRFullHelp() + } else if k.viewType == config.RepoView { + additionalKeys = BranchFullHelp() } else { additionalKeys = IssueFullHelp() } diff --git a/ui/modelUtils.go b/ui/modelUtils.go index f4080563..9e96bb21 100644 --- a/ui/modelUtils.go +++ b/ui/modelUtils.go @@ -2,10 +2,12 @@ package ui import ( "bytes" + "errors" "fmt" "maps" "os" "os/exec" + "reflect" "text/template" "time" @@ -82,7 +84,7 @@ func (m *Model) executeKeybinding(key string) tea.Cmd { return m.runCustomIssueCommand(keybinding.Command, data) } } - case config.PRsView, config.RepoView: + case config.PRsView: for _, keybinding := range m.ctx.Config.Keybindings.Prs { if keybinding.Key != key || keybinding.Command == "" { continue @@ -95,6 +97,19 @@ func (m *Model) executeKeybinding(key string) tea.Cmd { return m.runCustomPRCommand(keybinding.Command, data) } } + case config.RepoView: + for _, keybinding := range m.ctx.Config.Keybindings.Branches { + if keybinding.Key != key || keybinding.Command == "" { + continue + } + + log.Debug("executing keybind", "key", keybinding.Key, "command", keybinding.Command) + + switch data := currRowData.(type) { + case *data.PullRequestData: + return m.runCustomBranchCommand(keybinding.Command, data) + } + } default: // Not a valid case - ignore it } @@ -112,7 +127,9 @@ func (m *Model) runCustomCommand(commandTemplate string, contextData *map[string input := map[string]any{} // Merge data specific to the context the command is being run in onto any common data, overwriting duplicate keys. - maps.Copy(input, *contextData) + if contextData != nil { + maps.Copy(input, *contextData) + } // Append in the local RepoPath only if it can be found if repoPath, ok := common.GetRepoLocalPath(input["RepoName"].(string), m.ctx.Config.RepoPaths); ok { @@ -156,7 +173,21 @@ func (m *Model) runCustomIssueCommand(commandTemplate string, issueData *data.Is ) } +func (m *Model) runCustomBranchCommand(commandTemplate string, branchData *data.PullRequestData) tea.Cmd { + if reflect.ValueOf(branchData).IsNil() { + return m.executeCustomCommand(commandTemplate) + } + return m.runCustomCommand(commandTemplate, + &map[string]any{ + "RepoName": branchData.GetRepoNameWithOwner(), + "PrNumber": branchData.Number, + "HeadRefName": branchData.HeadRefName, + "BaseRefName": branchData.BaseRefName, + }) +} + func (m *Model) executeCustomCommand(cmd string) tea.Cmd { + log.Debug("executing custom command", "cmd", cmd) shell := os.Getenv("SHELL") if shell == "" { shell = "sh" @@ -169,7 +200,7 @@ func (m *Model) executeCustomCommand(cmd string) tea.Cmd { if mdErr != nil { return constants.ErrMsg{Err: mdErr} } - return constants.ErrMsg{Err: fmt.Errorf( + return constants.ErrMsg{Err: errors.New( lipgloss.JoinVertical(lipgloss.Left, fmt.Sprintf("Whoops, got an error: %s", err), md, @@ -198,3 +229,23 @@ func (m *Model) notify(text string) tea.Cmd { return tea.Sequence(startCmd, finishCmd) } + +func (m *Model) notifyErr(text string) tea.Cmd { + id := fmt.Sprint(time.Now().Unix()) + startCmd := m.ctx.StartTask( + context.Task{ + Id: id, + StartText: text, + FinishedText: text, + State: context.TaskStart, + }) + + finishCmd := func() tea.Msg { + return constants.TaskFinishedMsg{ + TaskId: id, + Err: errors.New(text), + } + } + + return tea.Sequence(startCmd, finishCmd) +} diff --git a/ui/ui.go b/ui/ui.go index 5114b8e0..fe2494ba 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -3,6 +3,7 @@ package ui import ( "fmt" "os" + "reflect" "sort" "strings" "time" @@ -246,22 +247,32 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case key.Matches(msg, m.keys.CopyNumber): - number := fmt.Sprint(m.getCurrRowData().GetNumber()) - err := clipboard.WriteAll(number) + row := m.getCurrRowData() var cmd tea.Cmd + if reflect.ValueOf(row).IsNil() { + cmd = m.notifyErr("Current selection isn't associated with a PR/Issue") + return m, cmd + } + number := fmt.Sprint(row.GetNumber()) + err := clipboard.WriteAll(number) if err != nil { - cmd = m.notify(fmt.Sprintf("Failed copying to clipboard %v", err)) + cmd = m.notifyErr(fmt.Sprintf("Failed copying to clipboard %v", err)) } else { cmd = m.notify(fmt.Sprintf("Copied %s to clipboard", number)) } return m, cmd case key.Matches(msg, m.keys.CopyUrl): - url := m.getCurrRowData().GetUrl() - err := clipboard.WriteAll(url) var cmd tea.Cmd + row := m.getCurrRowData() + if reflect.ValueOf(row).IsNil() { + cmd = m.notifyErr("Current selection isn't associated with a PR/Issue") + return m, cmd + } + url := row.GetUrl() + err := clipboard.WriteAll(url) if err != nil { - cmd = m.notify(fmt.Sprintf("Failed copying to clipboard %v", err)) + cmd = m.notifyErr(fmt.Sprintf("Failed copying to clipboard %v", err)) } else { cmd = m.notify(fmt.Sprintf("Copied %s to clipboard", url)) } @@ -798,6 +809,14 @@ func (m *Model) isUserDefinedKeybinding(msg tea.KeyMsg) bool { } } + if m.ctx.View == config.RepoView { + for _, keybinding := range m.ctx.Config.Keybindings.Branches { + if keybinding.Builtin == "" && keybinding.Key == msg.String() { + return true + } + } + } + return false }