@@ -550,6 +550,66 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
550550 cursor_index = 0 ;
551551 break ;
552552 }
553+ else if (c == ' \t ' )
554+ {
555+ if (!tab_completion_callback_)
556+ {
557+ continue ;
558+ }
559+
560+ std::vector<std::string> suggestions = tab_completion_callback_ (read_str);
561+ if (suggestions.empty ())
562+ {
563+ continue ;
564+ }
565+
566+ // Locate the last whitespace-delimited token in the line
567+ size_t last_space = read_str.find_last_of (" \t " );
568+ size_t token_start = (last_space == std::string::npos) ? 0 : last_space + 1 ;
569+ std::string current_token = read_str.substr (token_start);
570+
571+ // Compute the longest common prefix of all suggestions
572+ std::string lcp = suggestions[0 ];
573+ for (size_t i = 1 ; i < suggestions.size (); i++)
574+ {
575+ size_t j = 0 ;
576+ while (j < lcp.size () && j < suggestions[i].size () && lcp[j] == suggestions[i][j])
577+ {
578+ j++;
579+ }
580+ lcp.resize (j);
581+ }
582+
583+ if (suggestions.size () == 1 || lcp.size () > current_token.size ())
584+ {
585+ // Replace the last token with the unique match or the extended common prefix
586+ const std::string completion = (suggestions.size () == 1 ) ? suggestions[0 ] : lcp;
587+ std::string new_line = read_str.substr (0 , token_start) + completion;
588+ update_line (" >> " , read_str, cursor_index,
589+ [new_line](std::string& line, size_t & index)
590+ {
591+ line = new_line;
592+ index = line.size ();
593+ });
594+ }
595+ else
596+ {
597+ // Several matches: move cursor to end of line, list candidates, redraw prompt
598+ update_line (" >> " , read_str, cursor_index,
599+ [](std::string& line, size_t & index)
600+ {
601+ index = line.size ();
602+ });
603+
604+ std::cout << " \n " ;
605+ for (const auto & s : suggestions)
606+ {
607+ std::cout << " " << s << " \n " ;
608+ }
609+ std::cout << " \033 [38;5;82m" << " >> " << " \033 [0m" << read_str << std::flush;
610+ cursor_index = read_str.size ();
611+ }
612+ }
553613 else
554614 {
555615 char ch = static_cast <char >(c);
@@ -592,6 +652,12 @@ bool StdinEventHandler::is_ignoring_input() const noexcept
592652 return ignore_input_.load (std::memory_order_relaxed);
593653}
594654
655+ void StdinEventHandler::set_tab_completion_callback (
656+ std::function<std::vector<std::string>(const std::string&)> callback) noexcept
657+ {
658+ tab_completion_callback_ = std::move (callback);
659+ }
660+
595661} /* namespace event */
596662} /* namespace utils */
597663} /* namespace eprosima */
0 commit comments