Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Added vim-like keybindings for navigating Select and MultiSelect (#124)
Browse files Browse the repository at this point in the history
* j and k can be used to navigate select and multi select

* added test case for vim navigation

* added docs for j/k movement

* added j/k test to MultiSelect
  • Loading branch information
AlecAivazis authored Feb 27, 2018
1 parent 1089360 commit 9f89d9d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 34 deletions.
56 changes: 28 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Survey

[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/gopkg.in/AlecAivazis/survey.v1)

Expand Down Expand Up @@ -59,16 +60,16 @@ func main() {

1. [Examples](#examples)
1. [Prompts](#prompts)
1. [Input](#input)
1. [Password](#password)
1. [Confirm](#confirm)
1. [Select](#select)
1. [MultiSelect](#multiselect)
1. [Editor](#editor)
1. [Input](#input)
1. [Password](#password)
1. [Confirm](#confirm)
1. [Select](#select)
1. [MultiSelect](#multiselect)
1. [Editor](#editor)
1. [Validation](#validation)
1. [Built-in Validators](#built-in-validators)
1. [Built-in Validators](#built-in-validators)
1. [Help Text](#help-text)
1. [Changing the input rune](#changing-the-input-run)
1. [Changing the input rune](#changing-the-input-run)
1. [Custom Types](#custom-types)
1. [Customizing Output](#customizing-output)
1. [Versioning](#versioning)
Expand Down Expand Up @@ -101,7 +102,6 @@ prompt := &survey.Input{
survey.AskOne(prompt, &name, nil)
```


### Password

<img src="https://media.giphy.com/media/26FmQr6mUivkq71GE/giphy.gif" width="400px" />
Expand All @@ -114,7 +114,6 @@ prompt := &survey.Password{
survey.AskOne(prompt, &password, nil)
```


### Confirm

<img src="https://media.giphy.com/media/3oKIPgsUmTp4m3eo4E/giphy.gif" width="400px"/>
Expand All @@ -127,7 +126,6 @@ prompt := &survey.Confirm{
survey.AskOne(prompt, &name, nil)
```


### Select

<img src="https://media.giphy.com/media/3oKIPxigmMu5YqpUPK/giphy.gif" width="400px"/>
Expand All @@ -141,6 +139,8 @@ prompt := &survey.Select{
survey.AskOne(prompt, &color, nil)
```

The user can cycle through the options with the j and k keys to do down and up respectively.

By default, the select prompt is limited to showing 7 options at a time
and will paginate lists of options longer than that. To increase, you can either
change the global `survey.PageSize`, or set the `PageSize` field on the prompt:
Expand All @@ -162,6 +162,8 @@ prompt := &survey.MultiSelect{
survey.AskOne(prompt, &days, nil)
```

The user can cycle through the options with the j and k keys to do down and up, respectively.

By default, the MultiSelect prompt is limited to showing 7 options at a time
and will paginate lists of options longer than that. To increase, you can either
change the global `survey.PageSize`, or set the `PageSize` field on the prompt:
Expand All @@ -172,11 +174,10 @@ prompt := &survey.MultiSelect{..., PageSize: 10}

### Editor

Launches the user's preferred editor (defined by the $EDITOR environment variable) on a
temporary file. Once the user exits their editor, the contents of the temporary file are read in as
Launches the user's preferred editor (defined by the $EDITOR environment variable) on a
temporary file. Once the user exits their editor, the contents of the temporary file are read in as
the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.


## Validation

Validating individual responses for a particular question can be done by defining a
Expand All @@ -201,11 +202,11 @@ q := &survey.Question{
`survey` comes prepackaged with a few validators to fit common situations. Currently these
validators include:

| name | valid types | description | notes |
|--------------|-----------------|---------------------------------------------------------------|--------------------|
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | |
| name | valid types | description | notes |
| ------------ | ----------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | |

## Help Text

Expand All @@ -226,7 +227,6 @@ In some situations, `?` is a perfectly valid response. To handle this, you can c
looks for by setting the `HelpInputRune` variable in `survey/core`:

```golang

import (
"gopkg.in/AlecAivazis/survey.v1"
surveyCore "gopkg.in/AlecAivazis/survey.v1/core"
Expand Down Expand Up @@ -278,14 +278,14 @@ survey.AskOne(
Customizing the icons and various parts of survey can easily be done by setting the following variables
in `survey/core`:

| name | default | description |
|---------------------|----------------|-------------------------------------------------------------------|
| ErrorIcon | | Before an error |
| HelpIcon | | Before help text |
| QuestionIcon | ? | Before the message of a prompt |
| SelectFocusIcon | | Marks the current focus in `Select` and `MultiSelect` prompts |
| MarkedOptionIcon | | Marks a chosen selection in a `MultiSelect` prompt |
| UnmarkedOptionIcon | | Marks an unselected option in a `MultiSelect` prompt |
| name | default | description |
| ------------------ | ------- | ------------------------------------------------------------- |
| ErrorIcon | | Before an error |
| HelpIcon | | Before help text |
| QuestionIcon | ? | Before the message of a prompt |
| SelectFocusIcon | | Marks the current focus in `Select` and `MultiSelect` prompts |
| MarkedOptionIcon | | Marks a chosen selection in a `MultiSelect` prompt |
| UnmarkedOptionIcon | | Marks an unselected option in a `MultiSelect` prompt |

## Versioning

Expand Down
4 changes: 2 additions & 2 deletions multiselect.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var MultiSelectQuestionTemplate = `

// OnChange is called on every keypress.
func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
if key == terminal.KeyArrowUp {
if key == terminal.KeyArrowUp || key == 'k' {
// if we are at the top of the list
if m.selectedIndex == 0 {
// go to the bottom
Expand All @@ -70,7 +70,7 @@ func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune,
// decrement the selected index
m.selectedIndex--
}
} else if key == terminal.KeyArrowDown {
} else if key == terminal.KeyArrowDown || key == 'j' {
// if we are at the bottom of the list
if m.selectedIndex == len(m.Options)-1 {
// start at the top
Expand Down
8 changes: 4 additions & 4 deletions select.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPo
// if the user pressed the enter key
if key == terminal.KeyEnter {
return []rune(s.Options[s.selectedIndex]), 0, true
// if the user pressed the up arrow
} else if key == terminal.KeyArrowUp {
// if the user pressed the up arrow or 'k' to emulate vim
} else if key == terminal.KeyArrowUp || key == 'k' {
s.useDefault = false

// if we are at the top of the list
Expand All @@ -73,8 +73,8 @@ func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPo
// otherwise we are not at the top of the list so decrement the selected index
s.selectedIndex--
}
// if the user pressed down and there is room to move
} else if key == terminal.KeyArrowDown {
// if the user pressed down or 'j' to emulate vim
} else if key == terminal.KeyArrowDown || key == 'j' {
s.useDefault = false
// if we are at the bottom of the list
if s.selectedIndex == len(s.Options)-1 {
Expand Down
7 changes: 7 additions & 0 deletions tests/multiselect.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ var table = []TestUtil.TestTableEntry{
Default: []string{"Sundayaa"},
}, &answer,
},
{
"can navigate with j/k", &survey.MultiSelect{
Message: "What days do you prefer:",
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
Default: []string{"Sundayaa"},
}, &answer,
},
}

func main() {
Expand Down
6 changes: 6 additions & 0 deletions tests/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ var goodTable = []TestUtil.TestTableEntry{
Options: []string{"red", "blue"},
}, &answer,
},
{
"can navigate with j/k", &survey.Select{
Message: "Choose one:",
Options: []string{"red", "blue", "green"},
}, &answer,
},
}

var badTable = []TestUtil.TestTableEntry{
Expand Down

0 comments on commit 9f89d9d

Please sign in to comment.