github.com/aretext/aretext@v1.3.0/state/textfield.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/aretext/aretext/text"
     7  )
     8  
     9  // Same as Linux PATH_MAX.
    10  const maxTextFieldLen = 4096
    11  
    12  // TextFieldAction is the action to perform with the text input by the user.
    13  type TextFieldAction func(*EditorState, string) error
    14  
    15  // TextFieldAutocompleteFunc retrieves autocomplete suffixes for a given prefix.
    16  // It is acceptable to return an empty slice if there are no autocompletions,
    17  // but every string in the slice must have non-zero length.
    18  type TextFieldAutocompleteFunc func(prefix string) ([]string, error)
    19  
    20  // TextFieldState represents the state of the text field.
    21  // This is used to enter text such as the file path
    22  // when creating a new file from within the editor.
    23  type TextFieldState struct {
    24  	promptText            string
    25  	inputText             text.RuneStack
    26  	action                TextFieldAction
    27  	prevInputMode         InputMode
    28  	autocompleteFunc      TextFieldAutocompleteFunc // Set to nil to disable autocompletion.
    29  	autocompleteSuffixes  []string
    30  	autocompleteSuffixIdx int
    31  }
    32  
    33  func (s *TextFieldState) PromptText() string {
    34  	return s.promptText
    35  }
    36  
    37  func (s *TextFieldState) InputText() string {
    38  	return s.inputText.String()
    39  }
    40  
    41  func (s *TextFieldState) AutocompleteSuffix() string {
    42  	if s.autocompleteSuffixIdx < len(s.autocompleteSuffixes) {
    43  		return s.autocompleteSuffixes[s.autocompleteSuffixIdx]
    44  	} else {
    45  		return ""
    46  	}
    47  }
    48  
    49  func (s *TextFieldState) applyAutocomplete() {
    50  	for _, r := range s.AutocompleteSuffix() {
    51  		s.inputText.Push(r)
    52  	}
    53  	s.autocompleteSuffixes = nil
    54  	s.autocompleteSuffixIdx = 0
    55  }
    56  
    57  func ShowTextField(state *EditorState, promptText string, action TextFieldAction, autocompleteFunc TextFieldAutocompleteFunc) {
    58  	state.textfield = &TextFieldState{
    59  		promptText:       promptText,
    60  		action:           action,
    61  		prevInputMode:    state.inputMode,
    62  		autocompleteFunc: autocompleteFunc,
    63  	}
    64  	setInputMode(state, InputModeTextField)
    65  }
    66  
    67  func HideTextField(state *EditorState) {
    68  	prevInputMode := state.textfield.prevInputMode
    69  	state.textfield = &TextFieldState{}
    70  	setInputMode(state, prevInputMode)
    71  }
    72  
    73  func AppendRuneToTextField(state *EditorState, r rune) {
    74  	state.textfield.applyAutocomplete()
    75  	inputText := &state.textfield.inputText
    76  	if inputText.Len() < maxTextFieldLen {
    77  		inputText.Push(r)
    78  	}
    79  	SetStatusMsg(state, StatusMsg{})
    80  }
    81  
    82  func DeleteRuneFromTextField(state *EditorState) {
    83  	state.textfield.applyAutocomplete()
    84  	state.textfield.inputText.Pop()
    85  	SetStatusMsg(state, StatusMsg{})
    86  }
    87  
    88  func ExecuteTextFieldAction(state *EditorState) {
    89  	state.textfield.applyAutocomplete()
    90  	action := state.textfield.action
    91  	inputText := state.textfield.InputText()
    92  	err := action(state, inputText)
    93  	if err != nil {
    94  		// If the action failed, show the error as a status message,
    95  		// but remain in the input mode so the user can edit/retry.
    96  		SetStatusMsg(state, StatusMsg{
    97  			Style: StatusMsgStyleError,
    98  			Text:  err.Error(),
    99  		})
   100  		return
   101  	}
   102  
   103  	// The action completed successfully, so hide the text field.
   104  	HideTextField(state)
   105  }
   106  
   107  // AutocompleteTextField performs autocompletion on the text field input.
   108  // If there are multiple matching suffixes, repeated invocations will cycle
   109  // through the options (including the original input).
   110  func AutocompleteTextField(state *EditorState) {
   111  	tf := state.textfield
   112  	if tf.autocompleteFunc == nil {
   113  		// Autocomplete disabled.
   114  		return
   115  	}
   116  
   117  	// If we already have autocomplete suffixes, cycle through them.
   118  	if len(tf.autocompleteSuffixes) > 0 {
   119  		tf.autocompleteSuffixIdx = (tf.autocompleteSuffixIdx + 1) % len(tf.autocompleteSuffixes)
   120  		return
   121  	}
   122  
   123  	// Otherwise, retrieve suffixes for the current prefix.
   124  	prefix := tf.inputText.String()
   125  	suffixes, err := tf.autocompleteFunc(prefix)
   126  	if err != nil {
   127  		SetStatusMsg(state, StatusMsg{
   128  			Style: StatusMsgStyleError,
   129  			Text:  fmt.Sprintf("Error occurred during autocomplete: %s", err),
   130  		})
   131  		return
   132  	}
   133  
   134  	SetStatusMsg(state, StatusMsg{})
   135  
   136  	if len(suffixes) > 0 {
   137  		tf.autocompleteSuffixes = append(suffixes, "") // Last item is always "" to show just the prefix.
   138  		tf.autocompleteSuffixIdx = 0
   139  	}
   140  }