Skip to content

Commit 3fc9a46

Browse files
committed
[#24495] Initial approach
Signed-off-by: danipiza <dpizarrogallego@gmail.com>
1 parent 355e332 commit 3fc9a46

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

cpp_utils/include/cpp_utils/event/StdinEventHandler.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ class StdinEventHandler : public EventHandler<std::string>
9797
CPP_UTILS_DllAPI
9898
bool is_ignoring_input() const noexcept;
9999

100+
/**
101+
* @brief Set a callback invoked when the user presses Tab.
102+
*
103+
* The callback receives the full current input line and returns a list of
104+
* candidate completions for the last whitespace-delimited token. The handler
105+
* then performs standard completion behavior: if a single match (or a longer
106+
* common prefix) exists, the token is extended in place; if several matches
107+
* exist, they are listed below the prompt, which is then redrawn.
108+
*
109+
* Pass an empty std::function to disable Tab completion.
110+
*/
111+
CPP_UTILS_DllAPI
112+
void set_tab_completion_callback(
113+
std::function<std::vector<std::string>(const std::string&)> callback) noexcept;
114+
100115
protected:
101116

102117
/**
@@ -167,6 +182,9 @@ class StdinEventHandler : public EventHandler<std::string>
167182
//! Whether to ignore input (e.g. during interactive commands like echo)
168183
std::atomic<bool> ignore_input_{false};
169184

185+
//! Optional callback invoked on Tab to obtain completion candidates for the current line
186+
std::function<std::vector<std::string>(const std::string&)> tab_completion_callback_;
187+
170188
};
171189

172190
} /* namespace event */

cpp_utils/src/cpp/event/StdinEventHandler.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)