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 }