@@ -53,11 +53,11 @@ class Yash {
5353
5454 // / @brief Sets the print function to be used
5555 // / @param print The YashPrint funcion to be used
56- void setPrint (YashPrint print) { m_printFunction = print; }
56+ void setPrint (YashPrint print) { m_printFunction = std::move ( print) ; }
5757
5858 // / @brief Prints the specified text using the print function
5959 // / @param text The text to be printed
60- void print (const char * text) { m_printFunction (text); }
60+ void print (const char * text) const { m_printFunction (text); }
6161
6262 // / @brief Prints the specified text using the print function. Printf style formating can be used
6363 void printf (const char * fmt, ...)
@@ -80,7 +80,7 @@ class Yash {
8080 // / @param requiredArguments A size_t with the number of required arguments (default 0)
8181 void addCommand (const std::string& command, const std::string& description, YashFunction function, size_t requiredArguments = 0 )
8282 {
83- addCommand (command, " " , description, function, requiredArguments);
83+ addCommand (command, " " , description, std::move ( function) , requiredArguments);
8484 }
8585
8686 // / @brief Adds a command with a sub command to the shell
@@ -92,7 +92,7 @@ class Yash {
9292 void addCommand (const std::string& command, const std::string& subCommand, const std::string& description, YashFunction function, size_t requiredArguments = 0 )
9393 {
9494 auto fullCommand = subCommand.empty () ? command : command + s_commandDelimiter + subCommand;
95- m_functions.emplace (fullCommand, Function (description, function, requiredArguments));
95+ m_functions.emplace (fullCommand, Function (description, std::move ( function) , requiredArguments));
9696 }
9797
9898 // / @brief Removes a command from the shell
@@ -119,7 +119,7 @@ class Yash {
119119 case ' \n ' :
120120 case ' \r ' :
121121 print (" \r\n " );
122- if (m_command.size ()) {
122+ if (! m_command.empty ()) {
123123 runCommand ();
124124 m_commands.push_back (m_command);
125125
@@ -130,21 +130,41 @@ class Yash {
130130 m_commandsIndex = m_commands.end ();
131131 } else
132132 print (m_prompt.c_str ());
133+ m_pos = m_command.length ();
133134 break ;
134135 case EndOfText:
135136 m_command.clear ();
136137 printCommand ();
138+ m_pos = m_command.length ();
137139 break ;
138140 case Del:
139141 case Backspace:
140- if (m_command.size ()) {
141- print (s_clearCharacter);
142- m_command.erase (m_command.size () - 1 );
142+ if (!m_command.empty ()) {
143+ if (m_pos == m_command.length ()) {
144+ print (s_clearCharacter);
145+ m_command.erase (m_command.length () - 1 );
146+ m_pos = m_command.length ();
147+ } else if (m_pos) {
148+ m_command.erase (--m_pos, 1 );
149+ print (s_moveCursorBackward);
150+
151+ for (size_t i = m_pos; i < m_command.length (); i++) {
152+ print (std::string (1 , m_command.at (i)).c_str ());
153+ }
154+
155+ print (std::string (1 , ' ' ).c_str ());
156+ print (s_clearCharacter); // clear unused char at the end
157+
158+ for (size_t i = m_pos; i < m_command.length (); i++) {
159+ print (s_moveCursorBackward);
160+ }
161+ }
143162 }
144163 break ;
145164 case Tab:
146165 printDescriptions (true );
147166 printCommand ();
167+ m_pos = m_command.length ();
148168 break ;
149169 case Esc:
150170 m_ctrlState = CtrlState::Esc;
@@ -154,23 +174,129 @@ class Yash {
154174 m_ctrlState = CtrlState::LeftBracket;
155175 return ;
156176 default :
157- if (character == Up && m_ctrlState == CtrlState::LeftBracket) {
158- if (m_commandsIndex != m_commands.begin ()) {
159- m_command = *--m_commandsIndex;
160- printCommand ();
161- }
162- } else if (character == Down && m_ctrlState == CtrlState::LeftBracket) {
163- if (m_commandsIndex != m_commands.end ()) {
164- ++m_commandsIndex;
165- if (m_commandsIndex != m_commands.end ())
166- m_command = *m_commandsIndex;
167- else
168- m_command.clear ();
169- printCommand ();
177+ if (m_ctrlState == CtrlState::LeftBracket) {
178+ m_ctrlSeq += character;
179+ for (const auto &it : m_supportedCtrlSeq) {
180+ if (m_ctrlSeq.compare (0 , m_ctrlSeq.length (), it.first , 0 , m_ctrlSeq.length ()) == 0 ) {
181+ if (m_ctrlSeq.length () == it.first .length ()) {
182+ switch (it.second ) {
183+ case CtrlSeq::Up:
184+ if (m_commandsIndex != m_commands.begin ()) {
185+ m_command = *--m_commandsIndex;
186+ printCommand ();
187+ m_pos = m_command.length ();
188+ }
189+ break ;
190+ case CtrlSeq::Down:
191+ if (m_commandsIndex != m_commands.end ()) {
192+ ++m_commandsIndex;
193+ if (m_commandsIndex != m_commands.end ()) {
194+ m_command = *m_commandsIndex;
195+ } else {
196+ m_command.clear ();
197+ }
198+ printCommand ();
199+ m_pos = m_command.length ();
200+ }
201+ break ;
202+ case CtrlSeq::Right:
203+ if (m_pos != m_command.length ()) {
204+ print (s_moveCursorForward);
205+ m_pos++;
206+ }
207+ break ;
208+ case CtrlSeq::Left:
209+ if (m_pos) {
210+ print (s_moveCursorBackward);
211+ m_pos--;
212+ }
213+ break ;
214+ case CtrlSeq::Home:
215+ while (m_pos) {
216+ print (s_moveCursorBackward);
217+ m_pos--;
218+ }
219+ break ;
220+ case CtrlSeq::Delete:
221+ if (m_pos != m_command.length ()) {
222+ m_command.erase (m_pos, 1 );
223+
224+ print (std::string (1 , ' ' ).c_str ());
225+ print (s_clearCharacter); // clear deleted char
226+
227+ for (size_t i = m_pos; i < m_command.length (); i++) {
228+ print (std::string (1 , m_command.at (i)).c_str ());
229+ }
230+
231+ print (std::string (1 , ' ' ).c_str ());
232+ print (s_clearCharacter); // clear unused char at the end
233+
234+ for (size_t i = m_pos; i < m_command.length (); i++) {
235+ print (s_moveCursorBackward);
236+ }
237+ }
238+ break ;
239+ case CtrlSeq::End:
240+ while (m_pos != m_command.length ()) {
241+ print (s_moveCursorForward);
242+ m_pos++;
243+ }
244+ break ;
245+ case CtrlSeq::CtrlRight:
246+ while (m_pos != m_command.length () && m_command.at (m_pos) == ' ' ) { // skip spaces until we find the first char
247+ print (s_moveCursorForward);
248+ m_pos++;
249+ }
250+ while (m_pos != m_command.length () && m_command.at (m_pos) != ' ' ) { // skip chars until we find the first space
251+ print (s_moveCursorForward);
252+ m_pos++;
253+ }
254+ break ;
255+ case CtrlSeq::CtrlLeft:
256+ if (m_pos && m_pos == m_command.length ()) { // step inside the m_command range
257+ print (s_moveCursorBackward);
258+ m_pos--;
259+ }
260+ if (m_pos && m_pos != m_command.length () && m_command.at (m_pos) != ' ' ) { // skip the first char
261+ print (s_moveCursorBackward);
262+ m_pos--;
263+ }
264+ while (m_pos && m_pos != m_command.length () && m_command.at (m_pos) == ' ' ) { // skip spaces until we find the first char
265+ print (s_moveCursorBackward);
266+ m_pos--;
267+ }
268+ while (m_pos && m_pos != m_command.length () && m_command.at (m_pos) != ' ' ) { // skip chars until we find the first space
269+ print (s_moveCursorBackward);
270+ m_pos--;
271+ }
272+ if (m_pos && m_pos != m_command.length () && m_command.at (m_pos) == ' ' ) { // step forward if we hit a space in order to highlight a char
273+ print (s_moveCursorForward);
274+ m_pos++;
275+ }
276+ break ;
277+ }
278+ } else {
279+ return ; // check the next ctrl character
280+ }
281+ }
170282 }
283+ m_ctrlSeq.clear ();
171284 } else {
172- print (std::string (1 , character).c_str ());
173- m_command += character;
285+ if (m_pos == m_command.length ()) {
286+ print (std::string (1 , character).c_str ());
287+ m_command += character;
288+ m_pos = m_command.length ();
289+ } else {
290+ print (std::string (1 , character).c_str ());
291+ m_command.insert (m_pos++, 1 , character);
292+
293+ for (size_t i = m_pos; i < m_command.length (); i++) {
294+ print (std::string (1 , m_command.at (i)).c_str ());
295+ }
296+ for (size_t i = m_pos; i < m_command.length (); i++) {
297+ print (s_moveCursorBackward);
298+ }
299+ }
174300 }
175301 break ;
176302 }
@@ -186,6 +312,8 @@ class Yash {
186312 Esc = 27 ,
187313 Up = 65 ,
188314 Down = 66 ,
315+ Right = 67 ,
316+ Left = 68 ,
189317 LeftBracket = 91 ,
190318 Del = 127 ,
191319 };
@@ -197,15 +325,26 @@ class Yash {
197325 };
198326
199327 struct Function {
200- Function (std::string description, YashFunction function, size_t requiredArguments)
201- : m_description(description)
202- , m_function(function)
328+ Function (std::string description, YashFunction function, size_t requiredArguments)
329+ : m_description(std::move( description) )
330+ , m_function(std::move( function) )
203331 , m_requiredArguments(requiredArguments) {}
204332
205333 std::string m_description;
206334 YashFunction m_function;
207335 size_t m_requiredArguments;
208336 };
337+ enum class CtrlSeq {
338+ Up,
339+ Down,
340+ Right,
341+ Left,
342+ Home,
343+ Delete,
344+ End,
345+ CtrlRight,
346+ CtrlLeft
347+ };
209348
210349 void runCommand ()
211350 {
@@ -215,7 +354,7 @@ class Yash {
215354 auto args = m_command.substr (command.size ());
216355 char *token = std::strtok (args.data (), s_commandDelimiter);
217356 while (token) {
218- arguments.push_back (token);
357+ arguments.emplace_back (token);
219358 token = std::strtok (nullptr , s_commandDelimiter);
220359 }
221360
@@ -246,7 +385,9 @@ class Yash {
246385
247386 for (const auto & [command, description] : descriptions) {
248387 std::string alignment ((maxCommandSize + 2 ) - command.size (), ' ' );
249- auto line { command + alignment + description + " \r\n " };
388+ std::string line;
389+ line.reserve (command.size () + alignment.size () + description.size () + 2 );
390+ line.append (command).append (alignment).append (description).append (" \r\n " );
250391 print (line.c_str ());
251392 }
252393 }
@@ -299,15 +440,30 @@ class Yash {
299440 static constexpr const char * s_clearLine = " \033 [2K\033 [100D" ;
300441 static constexpr const char * s_clearScreen = " \033 [2J\x1B [H" ;
301442 static constexpr const char * s_clearCharacter = " \033 [1D \033 [1D" ;
443+ static constexpr const char * s_moveCursorForward = " \033 [1C" ;
444+ static constexpr const char * s_moveCursorBackward = " \033 [1D" ;
302445 static constexpr const char * s_commandDelimiter = " " ;
303446 std::map<std::string, Function> m_functions;
304447 std::vector<std::string> m_commands;
305448 std::vector<std::string>::const_iterator m_commandsIndex;
449+ size_t m_pos{0 };
306450 std::string m_command;
307451 std::string m_prompt;
308452 YashPrint m_printFunction;
309- std::array<char , 128 > m_buffer;
453+ std::array<char , 256 > m_buffer{} ;
310454 CtrlState m_ctrlState { CtrlState::None };
455+ std::string m_ctrlSeq;
456+ const std::map<std::string, CtrlSeq> m_supportedCtrlSeq = {
457+ {" A" , CtrlSeq::Up},
458+ {" B" , CtrlSeq::Down},
459+ {" C" , CtrlSeq::Right},
460+ {" D" , CtrlSeq::Left},
461+ {" 1~" , CtrlSeq::Home},
462+ {" 3~" , CtrlSeq::Delete},
463+ {" 4~" , CtrlSeq::End},
464+ {" 1;5C" , CtrlSeq::CtrlRight},
465+ {" 1;5D" , CtrlSeq::CtrlLeft}
466+ };
311467};
312468
313469} // namespace Yash
0 commit comments