@@ -9,12 +9,15 @@ interface Props {
99}
1010
1111function getMatches ( input : string ) : Command [ ] {
12- if ( ! input . startsWith ( '/' ) ) return [ ] ;
13- return COMMANDS . filter ( ( cmd ) => cmd . name . startsWith ( input ) ) ;
12+ if ( ! input . startsWith ( '/' ) ) {
13+ return [ ] ;
14+ }
15+ return COMMANDS . filter ( ( command ) => command . name . startsWith ( input ) ) ;
1416}
1517
1618export function Autocomplete ( { isDisabled = false , onSubmit } : Props ) {
1719 const [ value , setValue ] = useState ( '' ) ;
20+ const [ cursorPosition , setCursorPosition ] = useState ( 0 ) ;
1821 const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
1922
2023 const matches = getMatches ( value ) ;
@@ -32,6 +35,18 @@ export function Autocomplete({ isDisabled = false, onSubmit }: Props) {
3235 return ;
3336 }
3437
38+ // v8 ignore next 4
39+ if ( key . leftArrow ) {
40+ setCursorPosition ( ( position ) => Math . max ( 0 , position - 1 ) ) ;
41+ return ;
42+ }
43+
44+ // v8 ignore next 4
45+ if ( key . rightArrow ) {
46+ setCursorPosition ( ( position ) => Math . min ( value . length , position + 1 ) ) ;
47+ return ;
48+ }
49+
3550 if ( key . tab && showSuggestions ) {
3651 // v8 ignore next
3752 const match = matches [ selectedIndex ] ?? matches [ 0 ] ;
@@ -61,18 +76,25 @@ export function Autocomplete({ isDisabled = false, onSubmit }: Props) {
6176 }
6277
6378 if ( key . backspace || key . delete ) {
64- setValue ( ( v ) => v . slice ( 0 , - 1 ) ) ;
79+ setValue ( ( value ) => {
80+ const before = value . slice ( 0 , cursorPosition - 1 ) ;
81+ const after = value . slice ( cursorPosition ) ;
82+ return before + after ;
83+ } ) ;
84+ setCursorPosition ( ( position ) => Math . max ( 0 , position - 1 ) ) ;
6585 setSelectedIndex ( 0 ) ;
6686 return ;
6787 }
6888
6989 // v8 ignore next
7090 if ( char && ! key . ctrl && ! key . meta ) {
71- setValue ( ( v ) => {
72- const next = v + char ;
73- setSelectedIndex ( 0 ) ;
74- return next ;
91+ setValue ( ( value ) => {
92+ const before = value . slice ( 0 , cursorPosition ) ;
93+ const after = value . slice ( cursorPosition ) ;
94+ return before + char + after ;
7595 } ) ;
96+ setCursorPosition ( ( position ) => position + 1 ) ;
97+ setSelectedIndex ( 0 ) ;
7698 }
7799 } ,
78100 { isActive : ! isDisabled } ,
@@ -82,22 +104,35 @@ export function Autocomplete({ isDisabled = false, onSubmit }: Props) {
82104 < Box flexDirection = "column" >
83105 < Box >
84106 < Text > { UI . PROMPT_PREFIX } </ Text >
85- < Text > { value } </ Text >
107+
108+ < Text >
109+ { value . slice ( 0 , cursorPosition ) }
110+ { cursorPosition < value . length ? (
111+ < Text backgroundColor = "black" color = "white" >
112+ { value [ cursorPosition ] }
113+ </ Text >
114+ ) : (
115+ < Text backgroundColor = "black" color = "white" >
116+ { ' ' }
117+ </ Text >
118+ ) }
119+ { value . slice ( cursorPosition + 1 ) }
120+ </ Text >
86121 </ Box >
87122
88123 { showSuggestions && (
89124 < Box flexDirection = "column" >
90- { matches . map ( ( cmd , index ) => {
125+ { matches . map ( ( command , index ) => {
91126 const isHighlighted = index === selectedIndex ;
92127 return (
93- < Box key = { cmd . name } gap = { 1 } >
128+ < Box key = { command . name } gap = { 1 } >
94129 < Text
95130 color = { isHighlighted ? 'cyan' : undefined }
96131 bold = { isHighlighted }
97132 >
98- { cmd . name }
133+ { command . name }
99134 </ Text >
100- < Text dimColor > { cmd . description } </ Text >
135+ < Text dimColor > { command . description } </ Text >
101136 </ Box >
102137 ) ;
103138 } ) }
0 commit comments