Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prompt to the remote branch checkout menu #3652

Merged
merged 3 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions pkg/gui/context/menu_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func NewMenuContext(
type MenuViewModel struct {
c *ContextCommon
menuItems []*types.MenuItem
prompt string
promptLines []string
columnAlignment []utils.Alignment
*FilteredListViewModel[*types.MenuItem]
}
Expand All @@ -73,6 +75,23 @@ func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem, columnAlignment
self.columnAlignment = columnAlignment
}

func (self *MenuViewModel) GetPrompt() string {
return self.prompt
}

func (self *MenuViewModel) SetPrompt(prompt string) {
self.prompt = prompt
self.promptLines = nil
}

func (self *MenuViewModel) GetPromptLines() []string {
return self.promptLines
}

func (self *MenuViewModel) SetPromptLines(promptLines []string) {
self.promptLines = promptLines
}

// TODO: move into presentation package
func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
menuItems := self.FilteredListViewModel.GetItems()
Expand All @@ -94,14 +113,22 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
}

func (self *MenuViewModel) GetNonModelItems() []*NonModelItem {
result := []*NonModelItem{}
result = append(result, lo.Map(self.promptLines, func(line string, _ int) *NonModelItem {
return &NonModelItem{
Index: 0,
Column: 0,
Content: line,
}
})...)

// Don't display section headers when we are filtering, and the filter mode
// is fuzzy. The reason is that filtering changes the order of the items
// (they are sorted by best match), so all the sections would be messed up.
if self.FilteredListViewModel.IsFiltering() && self.c.UserConfig.Gui.UseFuzzySearch() {
return []*NonModelItem{}
return result
}

result := []*NonModelItem{}
menuItems := self.FilteredListViewModel.GetItems()
var prevSection *types.MenuSection = nil
for i, menuItem := range menuItems {
Expand Down
61 changes: 53 additions & 8 deletions pkg/gui/controllers/helpers/confirmation_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,44 +63,59 @@ func (self *ConfirmationHelper) DeactivateConfirmationPrompt() {

// Temporary hack: we're just duplicating the logic in `gocui.lineWrap`
func getMessageHeight(wrap bool, message string, width int) int {
return len(wrapMessageToWidth(wrap, message, width))
}

func wrapMessageToWidth(wrap bool, message string, width int) []string {
lines := strings.Split(message, "\n")
if !wrap {
return len(strings.Split(message, "\n"))
return lines
}

lineCount := 0
lines := strings.Split(message, "\n")
wrappedLines := make([]string, 0, len(lines))

for _, line := range lines {
n := 0
offset := 0
lastWhitespaceIndex := -1
for i, currChr := range line {
rw := runewidth.RuneWidth(currChr)
n += rw

if n > width {
if currChr == ' ' {
wrappedLines = append(wrappedLines, line[offset:i])
offset = i + 1
n = 0
} else if currChr == '-' {
wrappedLines = append(wrappedLines, line[offset:i])
offset = i
n = rw
} else if lastWhitespaceIndex != -1 && lastWhitespaceIndex+1 != i {
if line[lastWhitespaceIndex] == '-' {
wrappedLines = append(wrappedLines, line[offset:lastWhitespaceIndex+1])
offset = lastWhitespaceIndex + 1
n = i - lastWhitespaceIndex
} else {
wrappedLines = append(wrappedLines, line[offset:lastWhitespaceIndex])
offset = lastWhitespaceIndex + 1
n = i - lastWhitespaceIndex + 1
}
} else {
wrappedLines = append(wrappedLines, line[offset:i])
offset = i
n = rw
}
lineCount++
lastWhitespaceIndex = -1
} else if currChr == ' ' || currChr == '-' {
lastWhitespaceIndex = i
}
}
lineCount++

wrappedLines = append(wrappedLines, line[offset:])
}

return lineCount
return wrappedLines
}

func (self *ConfirmationHelper) getPopupPanelDimensions(wrap bool, prompt string) (int, int, int, int) {
Expand Down Expand Up @@ -358,7 +373,9 @@ func (self *ConfirmationHelper) resizeMenu() {
itemCount := self.c.Contexts().Menu.UnfilteredLen()
offset := 3
panelWidth := self.getPopupPanelWidth()
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset)
contentWidth := panelWidth - 2 // minus 2 for the frame
promptLinesCount := self.layoutMenuPrompt(contentWidth)
x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset+promptLinesCount)
menuBottom := y1 - offset
_, _ = self.c.GocuiGui().SetView(self.c.Views().Menu.Name(), x0, y0, x1, menuBottom, 0)

Expand All @@ -368,11 +385,39 @@ func (self *ConfirmationHelper) resizeMenu() {
if selectedItem != nil {
tooltip = self.TooltipForMenuItem(selectedItem)
}
contentWidth := panelWidth - 2 // minus 2 for the frame
tooltipHeight := getMessageHeight(true, tooltip, contentWidth) + 2 // plus 2 for the frame
_, _ = self.c.GocuiGui().SetView(self.c.Views().Tooltip.Name(), x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0)
}

// Wraps the lines of the menu prompt to the available width and rerenders the
// menu if neeeded. Returns the number of lines the prompt takes up.
func (self *ConfirmationHelper) layoutMenuPrompt(contentWidth int) int {
oldPromptLines := self.c.Contexts().Menu.GetPromptLines()
var promptLines []string
prompt := self.c.Contexts().Menu.GetPrompt()
if len(prompt) > 0 {
promptLines = wrapMessageToWidth(true, prompt, contentWidth)
promptLines = append(promptLines, "")
}
self.c.Contexts().Menu.SetPromptLines(promptLines)
if len(oldPromptLines) != len(promptLines) {
// The number of lines in the prompt has changed; this happens either
// because we're now showing a menu that has a prompt, and the previous
// menu didn't (or vice versa), or because the user is resizing the
// terminal window while a menu with a prompt is open.

// We need to rerender to give the menu context a chance to update its
// non-model items, and reinitialize the data it uses for converting
// between view index and model index.
_ = self.c.Contexts().Menu.HandleRender()

// Then we need to refocus to ensure the cursor is in the right place in
// the view.
_ = self.c.Contexts().Menu.HandleFocus(types.OnFocusOpts{})
}
return len(promptLines)
}

func (self *ConfirmationHelper) resizeConfirmationPanel() {
suggestionsViewHeight := 0
if self.c.Views().Suggestions.Visible {
Expand Down
1 change: 1 addition & 0 deletions pkg/gui/controllers/helpers/refs_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func (self *RefsHelper) CheckoutRemoteBranch(fullBranchName string, localBranchN
Title: utils.ResolvePlaceholderString(self.c.Tr.RemoteBranchCheckoutTitle, map[string]string{
"branchName": fullBranchName,
}),
Prompt: self.c.Tr.RemoteBranchCheckoutPrompt,
Items: []*types.MenuItem{
{
Label: self.c.Tr.CheckoutTypeNewBranch,
Expand Down
1 change: 1 addition & 0 deletions pkg/gui/menu_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
}

gui.State.Contexts.Menu.SetMenuItems(opts.Items, opts.ColumnAlignment)
gui.State.Contexts.Menu.SetPrompt(opts.Prompt)
gui.State.Contexts.Menu.SetSelection(0)

gui.Views.Menu.Title = opts.Title
Expand Down
1 change: 1 addition & 0 deletions pkg/gui/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const (

type CreateMenuOptions struct {
Title string
Prompt string // a message that will be displayed above the menu options
Items []*MenuItem
HideCancel bool
ColumnAlignment []utils.Alignment
Expand Down
2 changes: 2 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ type TranslationSet struct {
CheckoutByName string
CheckoutByNameTooltip string
RemoteBranchCheckoutTitle string
RemoteBranchCheckoutPrompt string
CheckoutTypeNewBranch string
CheckoutTypeNewBranchTooltip string
CheckoutTypeDetachedHead string
Expand Down Expand Up @@ -1079,6 +1080,7 @@ func EnglishTranslationSet() TranslationSet {
CheckoutByName: "Checkout by name",
CheckoutByNameTooltip: "Checkout by name. In the input box you can enter '-' to switch to the last branch.",
RemoteBranchCheckoutTitle: "Checkout {{.branchName}}",
RemoteBranchCheckoutPrompt: "How would you like to check out this branch?",
CheckoutTypeNewBranch: "New local branch",
CheckoutTypeNewBranchTooltip: "Checkout the remote branch as a local branch, tracking the remote branch.",
CheckoutTypeDetachedHead: "Detached head",
Expand Down
Loading