@@ -48,7 +48,7 @@ export class Terminal {
48
48
* Handle enter key press to clear the prompt input.
49
49
*/
50
50
document . addEventListener ( "keydown" , ( event : KeyboardEvent ) => {
51
- const input = this . inputElement . textContent ?. replace ( / \xA0 / g , " " ) . trim ( ) ?? "" ;
51
+ const input = this . getInput ( ) ;
52
52
53
53
switch ( event . key ) {
54
54
case "ArrowLeft" :
@@ -91,8 +91,8 @@ export class Terminal {
91
91
} ) ;
92
92
}
93
93
94
- private onEnter ( input : string ) {
95
- const { command, args } = getCommandFromInput ( input ) ;
94
+ private onEnter ( input : string ) : void {
95
+ const { command, args } = getCommandFromInput ( input . trim ( ) ) ;
96
96
this . setInput ( ) ;
97
97
this . clearOutput ( ) ;
98
98
@@ -105,31 +105,42 @@ export class Terminal {
105
105
return ;
106
106
}
107
107
108
- private onTab ( input : string ) {
108
+ private onTab ( input : string ) : void {
109
+ this . clearOutput ( ) ;
110
+
109
111
if ( input . length === 0 ) {
112
+ this . print ( ...Terminal . commands . map ( ( command : Command ) => command . name ) ) ;
110
113
return ;
111
114
}
112
115
113
- this . clearOutput ( ) ;
114
-
115
116
const { command, args } = getCommandFromInput ( input ) ;
116
117
117
- // Autocomplete command arguments
118
- if ( command && command instanceof AutocompletingCommand && args . length > 0 ) {
119
- const completions = command . autocomplete ( args [ args . length - 1 ] ) ;
120
- if ( completions . length === 1 ) {
121
- args [ args . length - 1 ] = completions [ 0 ] ;
122
- this . setInput ( `${ command . name } ${ args . join ( " " ) } ` ) ;
123
- }
118
+ if ( command && command instanceof AutocompletingCommand ) {
119
+ // If the input is exactly the command name (with no space after), don't autocomplete arguments.
120
+ if ( input === command . name ) return ;
124
121
125
- if ( completions . length > 1 ) {
126
- this . print ( ...completions ) ;
127
- this . setInput ( `${ command . name } ${ longestCommonPrefix ( completions ) } ` ) ;
128
- }
122
+ this . autocompleteArguments ( command , args ) ;
129
123
return ;
130
124
}
131
125
132
- // Autocomplete command names
126
+ this . autocompleteCommands ( input ) ;
127
+ }
128
+
129
+ private autocompleteArguments ( command : AutocompletingCommand , args : string [ ] = [ ] ) {
130
+ const argument = args [ args . length - 1 ] ?? "" ;
131
+ const suggestions = command . suggestAutocompletions ( argument ) ;
132
+
133
+ if ( suggestions . length === 1 ) {
134
+ this . setInput ( `${ command . name } ${ suggestions [ 0 ] } ` ) ;
135
+ }
136
+
137
+ if ( suggestions . length > 1 ) {
138
+ this . print ( ...suggestions ) ;
139
+ this . setInput ( `${ command . name } ${ longestCommonPrefix ( suggestions ) } ` ) ;
140
+ }
141
+ }
142
+
143
+ private autocompleteCommands ( input : string ) : void {
133
144
const matchingCommandNames = Terminal . commands
134
145
. filter ( ( command : Command ) => command . name . startsWith ( input ) )
135
146
. map ( ( command : Command ) => command . name ) ;
@@ -148,10 +159,9 @@ export class Terminal {
148
159
this . print ( ...matchingCommandNames ) ;
149
160
this . setInput ( longestCommonPrefix ( matchingCommandNames ) ) ;
150
161
}
151
- return ;
152
162
}
153
163
154
- private moveCaretToEnd ( ) {
164
+ private moveCaretToEnd ( ) : void {
155
165
const range = document . createRange ( ) ;
156
166
const selection = window . getSelection ( ) ;
157
167
@@ -162,21 +172,26 @@ export class Terminal {
162
172
selection ?. addRange ( range ) ;
163
173
}
164
174
165
- public print ( ...lines : string [ ] ) {
175
+ public print ( ...lines : string [ ] ) : void {
166
176
lines . forEach ( ( line : string ) => {
167
177
const outputElement = document . createElement ( "pre" ) ;
168
178
outputElement . textContent = line ;
169
179
this . terminalOutputElement . appendChild ( outputElement ) ;
170
180
} ) ;
171
181
}
172
182
173
- public setInput ( newInput : string = "" ) {
174
- this . inputElement . textContent = newInput ;
175
- this . promptBlurElement . textContent = newInput ;
183
+ private getInput ( ) {
184
+ return this . inputElement . textContent ?. replace ( / \xA0 / g, " " ) ?? "" ;
185
+ }
186
+
187
+ public setInput ( input : string = "" ) : void {
188
+ const formattedInput = input . replace ( / / g, "\xA0" ) ;
189
+ this . inputElement . textContent = formattedInput ;
190
+ this . promptBlurElement . textContent = formattedInput ;
176
191
this . moveCaretToEnd ( ) ;
177
192
}
178
193
179
- public clearOutput ( ) {
194
+ public clearOutput ( ) : void {
180
195
while ( this . terminalOutputElement ?. firstChild ) {
181
196
this . terminalOutputElement . removeChild ( this . terminalOutputElement . firstChild ) ;
182
197
}
0 commit comments