aboutsummaryrefslogtreecommitdiff
path: root/src/core/search_handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/search_handler.cc')
-rw-r--r--src/core/search_handler.cc81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/core/search_handler.cc b/src/core/search_handler.cc
new file mode 100644
index 0000000..cab9e9c
--- /dev/null
+++ b/src/core/search_handler.cc
@@ -0,0 +1,81 @@
+/*
+ * Ricochet - A minimalist CLI web browser for fast, distraction-free navigation.
+ * Copyright (C) 2026 Richard Qin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "ricochet/core/search_handler.hh"
+
+namespace ricochet::core {
+
+void find_search_matches( PageData& page_data, const std::string& query )
+{
+ page_data.last_search = query;
+ page_data.match_lines.clear();
+ page_data.current_match_idx = 0;
+ for ( std::size_t i = 0; i < page_data.original_lines.size(); ++i ) {
+ if ( page_data.original_lines[i].find( query ) != std::string::npos ) {
+ page_data.match_lines.push_back( i );
+ }
+ }
+}
+
+void apply_search_highlight( PageData& page_data, const std::string& query )
+{
+ page_data.lines = page_data.original_lines;
+ if ( page_data.match_lines.empty() ) {
+ return;
+ }
+
+ const std::string hl_start = "\033[43;30m";
+ const std::string hl_end = "\033[0m";
+
+ for ( const auto match_idx : page_data.match_lines ) {
+ auto& line = page_data.lines[match_idx];
+ std::size_t pos = 0;
+ std::string replacement;
+ replacement.reserve( hl_start.length() + query.length() + hl_end.length() );
+ replacement += hl_start;
+ replacement += query;
+ replacement += hl_end;
+ while ( ( pos = line.find( query, pos ) ) != std::string::npos ) {
+ line.replace( pos, query.length(), replacement );
+ pos += hl_start.length() + query.length() + hl_end.length();
+ }
+ }
+}
+
+void execute_search( PageData& page_data, const std::string& query, std::size_t& scroll_y, bool next_match )
+{
+ if ( query.empty() ) {
+ return;
+ }
+
+ if ( !next_match || query != page_data.last_search ) {
+ find_search_matches( page_data, query );
+ apply_search_highlight( page_data, query );
+ } else if ( !page_data.match_lines.empty() ) {
+ page_data.current_match_idx = ( page_data.current_match_idx + 1 ) % page_data.match_lines.size();
+ }
+
+ if ( page_data.match_lines.empty() ) {
+ return;
+ }
+
+ const std::size_t target_line = page_data.match_lines[page_data.current_match_idx];
+ scroll_y = ( target_line > 5 ) ? ( target_line - 5 ) : 0;
+}
+
+} // namespace ricochet::core