feat(console): filter match highlight + match count
- Highlight the matched substring (case-insensitive) in every visible line with a
translucent yellow behind the text, drawn per wrap-segment like the selection highlight.
- Show a live match count next to the filter input ("N matches", red when zero).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -553,6 +553,12 @@ void ConsoleTab::renderToolbar(ConsoleCommandExecutor& exec)
|
||||
float filterW = std::min(schema::UI().drawElement("tabs.console", "filter-max-width").size, filterAvail * schema::UI().drawElement("tabs.console", "filter-width-ratio").size);
|
||||
ImGui::SetNextItemWidth(filterW);
|
||||
ImGui::InputTextWithHint("##ConsoleFilter", TR("console_filter_hint"), filter_text_, sizeof(filter_text_));
|
||||
if (filter_text_[0] != '\0') {
|
||||
ImGui::SameLine(0, Layout::spacingSm());
|
||||
std::string mc = std::to_string(filter_match_count_) + " " + TR("console_matches");
|
||||
Type().textColored(TypeStyle::Caption,
|
||||
filter_match_count_ > 0 ? OnSurfaceMedium() : Error(), mc.c_str());
|
||||
}
|
||||
|
||||
// Zoom +/- buttons (right side of toolbar)
|
||||
ImGui::SameLine();
|
||||
@@ -612,6 +618,13 @@ void ConsoleTab::renderOutput()
|
||||
s_app_messages_enabled,
|
||||
COLOR_DAEMON, COLOR_ERROR, COLOR_RPC};
|
||||
bool has_text_filter = !outputFilter.text.empty();
|
||||
// Lowercased needle for in-line match highlighting.
|
||||
std::string filter_lower;
|
||||
if (has_text_filter) {
|
||||
filter_lower = std::string(filter_text_);
|
||||
std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
}
|
||||
bool has_filter = has_text_filter || !outputFilter.daemonMessagesEnabled ||
|
||||
!outputFilter.rpcTraceEnabled || !outputFilter.appMessagesEnabled ||
|
||||
outputFilter.errorsOnly;
|
||||
@@ -628,6 +641,7 @@ void ConsoleTab::renderOutput()
|
||||
}
|
||||
}
|
||||
int visible_count = static_cast<int>(visible_indices_.size());
|
||||
filter_match_count_ = has_text_filter ? visible_count : 0;
|
||||
|
||||
// Calculate wrapped heights AND build sub-row segments for each visible line.
|
||||
// Each segment records which bytes of the source text appear on that visual
|
||||
@@ -881,6 +895,23 @@ void ConsoleTab::renderOutput()
|
||||
selColor);
|
||||
}
|
||||
|
||||
// Filter match highlight (case-insensitive) within this segment.
|
||||
if (has_text_filter && !filter_lower.empty() && seg.byteStart < seg.byteEnd) {
|
||||
std::string segLower(segStart, segEnd);
|
||||
std::transform(segLower.begin(), segLower.end(), segLower.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
size_t pos = 0;
|
||||
while ((pos = segLower.find(filter_lower, pos)) != std::string::npos) {
|
||||
float xs = font->CalcTextSizeA(fontSize, FLT_MAX, 0, segStart, segStart + static_cast<int>(pos)).x;
|
||||
float xe = font->CalcTextSizeA(fontSize, FLT_MAX, 0, segStart,
|
||||
segStart + static_cast<int>(pos + filter_lower.size())).x;
|
||||
dl->AddRectFilled(ImVec2(lineOrigin.x + xs, rowY),
|
||||
ImVec2(lineOrigin.x + xe, rowY + seg.height),
|
||||
IM_COL32(255, 210, 0, 70));
|
||||
pos += filter_lower.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Render text segment
|
||||
if (seg.byteStart < seg.byteEnd) {
|
||||
dl->AddText(font, fontSize,
|
||||
|
||||
@@ -152,6 +152,7 @@ private:
|
||||
|
||||
// Output filter
|
||||
char filter_text_[128] = {0};
|
||||
mutable int filter_match_count_ = 0; // lines matching the text filter (for the toolbar)
|
||||
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
||||
|
||||
// Wrapped line height caching (for variable-height text wrapping)
|
||||
|
||||
@@ -983,6 +983,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["console_help_stop"] = " stop - Stop the daemon";
|
||||
strings_["console_line_count"] = "%zu lines";
|
||||
strings_["console_new_lines"] = "new";
|
||||
strings_["console_matches"] = "matches";
|
||||
strings_["console_new_lines"] = "%d new lines";
|
||||
strings_["console_no_daemon"] = "No daemon";
|
||||
strings_["console_not_connected"] = "Error: Not connected to daemon";
|
||||
|
||||
Reference in New Issue
Block a user