Skip to content

Commit cc41fe7

Browse files
committed
fix: duplicates, cursor positioning
1 parent ce4b3c2 commit cc41fe7

File tree

2 files changed

+63
-17
lines changed

2 files changed

+63
-17
lines changed

cli/cliui/select.go

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ type multiSelectModel struct {
378378
message string
379379
canceled bool
380380
selected bool
381-
isInputMode bool // New field to track if we're adding a custom option
381+
isCustomInputMode bool // New field to track if we're adding a custom option
382382
customInput string // New field to store custom input
383383
enableCustomInput bool // New field to control whether custom input is allowed
384384
}
@@ -391,7 +391,7 @@ func (multiSelectModel) Init() tea.Cmd {
391391
func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
392392
var cmd tea.Cmd
393393

394-
if m.isInputMode {
394+
if m.isCustomInputMode {
395395
return m.handleCustomInputMode(msg)
396396
}
397397

@@ -409,7 +409,7 @@ func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
409409
case tea.KeyEnter:
410410
// Switch to custom input mode if we're on the "+ Add custom value:" option
411411
if m.enableCustomInput && m.cursor == len(m.filteredOptions()) {
412-
m.isInputMode = true
412+
m.isCustomInputMode = true
413413
return m, nil
414414
}
415415
if len(m.options) != 0 {
@@ -427,17 +427,15 @@ func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
427427
return m, nil
428428

429429
case tea.KeyUp:
430-
options := m.filteredOptions()
431-
maxIndex := len(options)
430+
maxIndex := m.getMaxIndex()
432431
if m.cursor > 0 {
433432
m.cursor--
434433
} else {
435434
m.cursor = maxIndex
436435
}
437436

438437
case tea.KeyDown:
439-
options := m.filteredOptions()
440-
maxIndex := len(options)
438+
maxIndex := m.getMaxIndex()
441439
if m.cursor < maxIndex {
442440
m.cursor++
443441
} else {
@@ -473,6 +471,16 @@ func (m multiSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
473471
return m, cmd
474472
}
475473

474+
func (m multiSelectModel) getMaxIndex() int {
475+
options := m.filteredOptions()
476+
if m.enableCustomInput {
477+
// Include the "+ Add custom value" entry
478+
return len(options)
479+
}
480+
// Includes only the actual options
481+
return len(options) - 1
482+
}
483+
476484
// handleCustomInputMode manages keyboard interactions when in custom input mode
477485
func (m *multiSelectModel) handleCustomInputMode(msg tea.Msg) (tea.Model, tea.Cmd) {
478486
keyMsg, ok := msg.(tea.KeyMsg)
@@ -499,15 +507,44 @@ func (m *multiSelectModel) handleCustomInputMode(msg tea.Msg) (tea.Model, tea.Cm
499507

500508
// handleCustomInputSubmission processes the submission of custom input
501509
func (m *multiSelectModel) handleCustomInputSubmission() (tea.Model, tea.Cmd) {
502-
if m.customInput != "" {
503-
m.options = append(m.options, &multiSelectOption{
504-
option: m.customInput,
505-
chosen: true,
506-
})
510+
if m.customInput == "" {
511+
m.isCustomInputMode = false
512+
return m, nil
513+
}
514+
515+
// Clear search to ensure option is visible and cursor points to the new option
516+
m.search.SetValue("")
517+
518+
// Check for duplicates
519+
for i, opt := range m.options {
520+
if strings.EqualFold(opt.option, m.customInput) {
521+
// If the option exists but isn't chosen, select it
522+
if !opt.chosen {
523+
opt.chosen = true
524+
}
525+
526+
// Point cursor to the new option
527+
m.cursor = i
528+
529+
// Reset custom input mode to disabled
530+
m.isCustomInputMode = false
531+
m.customInput = ""
532+
return m, nil
533+
}
507534
}
508-
// Reset input state regardless of whether input was empty
535+
536+
// Add new unique option
537+
m.options = append(m.options, &multiSelectOption{
538+
option: m.customInput,
539+
chosen: true,
540+
})
541+
542+
// Point cursor to the newly added option
543+
m.cursor = len(m.options) - 1
544+
545+
// Reset custom input mode to disabled
509546
m.customInput = ""
510-
m.isInputMode = false
547+
m.isCustomInputMode = false
511548
return m, nil
512549
}
513550

@@ -531,7 +568,7 @@ func (m multiSelectModel) View() string {
531568
return s.String()
532569
}
533570

534-
if m.isInputMode {
571+
if m.isCustomInputMode {
535572
_, _ = s.WriteString(fmt.Sprintf("%s\nEnter custom value: %s\n", msg, m.customInput))
536573
return s.String()
537574
}

cli/prompts.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ func (RootCmd) promptExample() *serpent.Command {
4141
Default: "",
4242
Value: serpent.StringArrayOf(&multiSelectValues),
4343
}
44+
45+
enableCustomInput bool
46+
enableCustomInputOption = serpent.Option{
47+
Name: "enable-custom-input",
48+
Description: "Enable custom input option in multi-select.",
49+
Required: false,
50+
Flag: "enable-custom-input",
51+
Value: serpent.BoolOf(&enableCustomInput),
52+
}
4453
)
4554
cmd := &serpent.Command{
4655
Use: "prompt-example",
@@ -159,12 +168,12 @@ func (RootCmd) promptExample() *serpent.Command {
159168
"Code", "Chairs", "Whale", "Diamond", "Carrot",
160169
},
161170
Defaults: []string{"Code"},
162-
EnableCustomInput: true,
171+
EnableCustomInput: enableCustomInput,
163172
})
164173
}
165174
_, _ = fmt.Fprintf(inv.Stdout, "%q are nice choices.\n", strings.Join(multiSelectValues, ", "))
166175
return multiSelectError
167-
}, useThingsOption),
176+
}, useThingsOption, enableCustomInputOption),
168177
promptCmd("rich-parameter", func(inv *serpent.Invocation) error {
169178
value, err := cliui.RichSelect(inv, cliui.RichSelectOptions{
170179
Options: []codersdk.TemplateVersionParameterOption{

0 commit comments

Comments
 (0)