summaryrefslogtreecommitdiffstats
path: root/src/files_sub_source.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/files_sub_source.cc')
-rw-r--r--src/files_sub_source.cc437
1 files changed, 437 insertions, 0 deletions
diff --git a/src/files_sub_source.cc b/src/files_sub_source.cc
new file mode 100644
index 0000000..48a3f56
--- /dev/null
+++ b/src/files_sub_source.cc
@@ -0,0 +1,437 @@
+/**
+ * Copyright (c) 2020, Timothy Stack
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Timothy Stack nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "files_sub_source.hh"
+
+#include "base/ansi_scrubber.hh"
+#include "base/humanize.hh"
+#include "base/humanize.network.hh"
+#include "base/opt_util.hh"
+#include "base/string_util.hh"
+#include "config.h"
+#include "lnav.hh"
+#include "mapbox/variant.hpp"
+
+namespace files_model {
+files_list_selection
+from_selection(vis_line_t sel_vis)
+{
+ auto& fc = lnav_data.ld_active_files;
+ int sel = (int) sel_vis;
+
+ if (sel < fc.fc_name_to_errors.size()) {
+ auto iter = fc.fc_name_to_errors.begin();
+
+ std::advance(iter, sel);
+ return error_selection::build(sel, iter);
+ }
+
+ sel -= fc.fc_name_to_errors.size();
+
+ if (sel < fc.fc_other_files.size()) {
+ auto iter = fc.fc_other_files.begin();
+
+ std::advance(iter, sel);
+ return other_selection::build(sel, iter);
+ }
+
+ sel -= fc.fc_other_files.size();
+
+ if (sel < fc.fc_files.size()) {
+ auto iter = fc.fc_files.begin();
+
+ std::advance(iter, sel);
+ return file_selection::build(sel, iter);
+ }
+
+ return no_selection{};
+}
+} // namespace files_model
+
+files_sub_source::files_sub_source() {}
+
+bool
+files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
+{
+ switch (ch) {
+ case KEY_ENTER:
+ case '\r': {
+ auto sel = files_model::from_selection(lv.get_selection());
+
+ sel.match([](files_model::no_selection) {},
+ [](files_model::error_selection) {},
+ [](files_model::other_selection) {},
+ [&](files_model::file_selection& fs) {
+ auto& lss = lnav_data.ld_log_source;
+ auto lf = *fs.sb_iter;
+
+ lss.find_data(lf) | [](auto ld) {
+ ld->set_visibility(true);
+ lnav_data.ld_log_source.text_filters_changed();
+ };
+
+ if (lf->get_format() != nullptr) {
+ auto& log_view = lnav_data.ld_views[LNV_LOG];
+ lss.row_for_time(lf->front().get_timeval()) |
+ [](auto row) {
+ lnav_data.ld_views[LNV_LOG].set_top(row);
+ };
+ ensure_view(&log_view);
+ } else {
+ auto& tv = lnav_data.ld_views[LNV_TEXT];
+ auto& tss = lnav_data.ld_text_source;
+
+ tss.to_front(lf);
+ tv.reload_data();
+ ensure_view(&tv);
+ }
+
+ lv.reload_data();
+ lnav_data.ld_mode = ln_mode_t::PAGING;
+ });
+
+ return true;
+ }
+
+ case ' ': {
+ auto sel = files_model::from_selection(lv.get_selection());
+
+ sel.match([](files_model::no_selection) {},
+ [](files_model::error_selection) {},
+ [](files_model::other_selection) {},
+ [&](files_model::file_selection& fs) {
+ auto& lss = lnav_data.ld_log_source;
+ auto lf = *fs.sb_iter;
+
+ lss.find_data(lf) | [](auto ld) {
+ ld->set_visibility(!ld->ld_visible);
+ };
+
+ auto top_view = *lnav_data.ld_view_stack.top();
+ auto tss = top_view->get_sub_source();
+
+ if (tss != nullptr) {
+ tss->text_filters_changed();
+ top_view->reload_data();
+ }
+
+ lv.reload_data();
+ });
+ return true;
+ }
+ case 'n': {
+ execute_command(lnav_data.ld_exec_context, "next-mark search");
+ return true;
+ }
+ case 'N': {
+ execute_command(lnav_data.ld_exec_context, "prev-mark search");
+ return true;
+ }
+ case '/': {
+ execute_command(lnav_data.ld_exec_context, "prompt search-files");
+ return true;
+ }
+ case 'X': {
+ auto sel = files_model::from_selection(lv.get_selection());
+
+ sel.match(
+ [](files_model::no_selection) {},
+ [&](files_model::error_selection& es) {
+ auto& fc = lnav_data.ld_active_files;
+
+ fc.fc_file_names.erase(es.sb_iter->first);
+
+ auto name_iter = fc.fc_file_names.begin();
+ while (name_iter != fc.fc_file_names.end()) {
+ if (name_iter->first == es.sb_iter->first) {
+ name_iter = fc.fc_file_names.erase(name_iter);
+ continue;
+ }
+
+ auto rp_opt = humanize::network::path::from_str(
+ name_iter->first);
+
+ if (rp_opt) {
+ auto rp = *rp_opt;
+
+ if (fmt::to_string(rp.home()) == es.sb_iter->first)
+ {
+ fc.fc_other_files.erase(name_iter->first);
+ name_iter = fc.fc_file_names.erase(name_iter);
+ continue;
+ }
+ }
+ ++name_iter;
+ }
+
+ fc.fc_name_to_errors.erase(es.sb_iter);
+ fc.fc_invalidate_merge = true;
+ lv.reload_data();
+ },
+ [](files_model::other_selection) {},
+ [](files_model::file_selection) {});
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
+{
+ lnav_data.ld_mode = ln_mode_t::PAGING;
+ lnav_data.ld_filter_view.reload_data();
+}
+
+size_t
+files_sub_source::text_line_count()
+{
+ const auto& fc = lnav_data.ld_active_files;
+
+ return fc.fc_name_to_errors.size() + fc.fc_other_files.size()
+ + fc.fc_files.size();
+}
+
+size_t
+files_sub_source::text_line_width(textview_curses& curses)
+{
+ return 512;
+}
+
+void
+files_sub_source::text_value_for_line(textview_curses& tc,
+ int line,
+ std::string& value_out,
+ text_sub_source::line_flags_t flags)
+{
+ const auto dim = tc.get_dimensions();
+ const auto& fc = lnav_data.ld_active_files;
+ auto filename_width
+ = std::min(fc.fc_largest_path_length,
+ std::max((size_t) 40, (size_t) dim.second - 30));
+
+ if (line < fc.fc_name_to_errors.size()) {
+ auto iter = fc.fc_name_to_errors.begin();
+ std::advance(iter, line);
+ auto path = ghc::filesystem::path(iter->first);
+ auto fn = path.filename().string();
+
+ truncate_to(fn, filename_width);
+ value_out = fmt::format(FMT_STRING(" {:<{}} {}"),
+ fn,
+ filename_width,
+ iter->second.fei_description);
+ return;
+ }
+
+ line -= fc.fc_name_to_errors.size();
+
+ if (line < fc.fc_other_files.size()) {
+ auto iter = fc.fc_other_files.begin();
+ std::advance(iter, line);
+ auto path = ghc::filesystem::path(iter->first);
+ auto fn = path.string();
+
+ truncate_to(fn, filename_width);
+ value_out = fmt::format(FMT_STRING(" {:<{}} {:14} {}"),
+ fn,
+ filename_width,
+ iter->second.ofd_format,
+ iter->second.ofd_description);
+ return;
+ }
+
+ line -= fc.fc_other_files.size();
+
+ const auto& lf = fc.fc_files[line];
+ auto fn = lf->get_unique_path();
+ char start_time[64] = "", end_time[64] = "";
+ std::vector<std::string> file_notes;
+
+ if (lf->get_format() != nullptr) {
+ sql_strftime(start_time, sizeof(start_time), lf->front().get_timeval());
+ sql_strftime(end_time, sizeof(end_time), lf->back().get_timeval());
+ }
+ truncate_to(fn, filename_width);
+ for (const auto& pair : lf->get_notes()) {
+ file_notes.push_back(pair.second);
+ }
+ value_out = fmt::format(FMT_STRING(" {:<{}} {:>8} {} \u2014 {} {}"),
+ fn,
+ filename_width,
+ humanize::file_size(lf->get_index_size(),
+ humanize::alignment::columnar),
+ start_time,
+ end_time,
+ fmt::join(file_notes, "; "));
+ this->fss_last_line_len
+ = filename_width + 23 + strlen(start_time) + strlen(end_time);
+}
+
+void
+files_sub_source::text_attrs_for_line(textview_curses& tc,
+ int line,
+ string_attrs_t& value_out)
+{
+ bool selected
+ = lnav_data.ld_mode == ln_mode_t::FILES && line == tc.get_selection();
+ const auto& fc = lnav_data.ld_active_files;
+ const auto dim = tc.get_dimensions();
+ auto filename_width
+ = std::min(fc.fc_largest_path_length,
+ std::max((size_t) 40, (size_t) dim.second - 30));
+
+ if (selected) {
+ value_out.emplace_back(line_range{0, 1},
+ VC_GRAPHIC.value(ACS_RARROW));
+ }
+
+ if (line < fc.fc_name_to_errors.size()) {
+ if (selected) {
+ value_out.emplace_back(
+ line_range{0, -1},
+ VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
+ }
+
+ value_out.emplace_back(
+ line_range{4 + (int) filename_width, -1},
+ VC_ROLE_FG.value(role_t::VCR_ERROR));
+ return;
+ }
+ line -= fc.fc_name_to_errors.size();
+
+ if (line < fc.fc_other_files.size()) {
+ if (selected) {
+ value_out.emplace_back(
+ line_range{0, -1},
+ VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
+ }
+ if (line == fc.fc_other_files.size() - 1) {
+ value_out.emplace_back(line_range{0, -1},
+ VC_STYLE.value(text_attrs{A_UNDERLINE}));
+ }
+ return;
+ }
+
+ line -= fc.fc_other_files.size();
+
+ if (selected) {
+ value_out.emplace_back(
+ line_range{0, -1},
+ VC_ROLE.value(role_t::VCR_FOCUSED));
+ }
+
+ auto& lss = lnav_data.ld_log_source;
+ auto& lf = fc.fc_files[line];
+ auto ld_opt = lss.find_data(lf);
+
+ chtype visible = ACS_DIAMOND;
+ if (ld_opt && !ld_opt.value()->ld_visible) {
+ visible = ' ';
+ }
+ value_out.emplace_back(line_range{2, 3},
+ VC_GRAPHIC.value(visible));
+ if (visible == ACS_DIAMOND) {
+ value_out.emplace_back(line_range{2, 3},
+ VC_FOREGROUND.value(COLOR_GREEN));
+ }
+
+ auto lr = line_range{
+ (int) filename_width + 3 + 4,
+ (int) filename_width + 3 + 10,
+ };
+ value_out.emplace_back(lr, VC_STYLE.value(text_attrs{A_BOLD}));
+
+ lr.lr_start = this->fss_last_line_len;
+ lr.lr_end = -1;
+ value_out.emplace_back(lr, VC_FOREGROUND.value(COLOR_YELLOW));
+}
+
+size_t
+files_sub_source::text_size_for_line(textview_curses& tc,
+ int line,
+ text_sub_source::line_flags_t raw)
+{
+ return 0;
+}
+
+static auto
+spinner_index()
+{
+ auto now = ui_clock::now();
+
+ return std::chrono::duration_cast<std::chrono::milliseconds>(
+ now.time_since_epoch())
+ .count()
+ / 100;
+}
+
+bool
+files_overlay_source::list_value_for_overlay(const listview_curses& lv,
+ int y,
+ int bottom,
+ vis_line_t line,
+ attr_line_t& value_out)
+{
+ if (y == 0) {
+ static const char PROG[] = "-\\|/";
+ constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
+
+ auto& fc = lnav_data.ld_active_files;
+ auto fc_prog = fc.fc_progress;
+ safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
+
+ if (!sp->sp_extractions.empty()) {
+ const auto& prog = sp->sp_extractions.front();
+
+ value_out.with_ansi_string(fmt::format(
+ "{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
+ "... {:>8}/{}",
+ PROG[spinner_index() % PROG_SIZE],
+ prog.ep_path.filename().string(),
+ humanize::file_size(prog.ep_out_size,
+ humanize::alignment::none),
+ humanize::file_size(prog.ep_total_size,
+ humanize::alignment::none)));
+ return true;
+ }
+ if (!sp->sp_tailers.empty()) {
+ auto first_iter = sp->sp_tailers.begin();
+
+ value_out.with_ansi_string(fmt::format(
+ "{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
+ ": {}",
+ PROG[spinner_index() % PROG_SIZE],
+ first_iter->first,
+ first_iter->second.tp_message));
+ return true;
+ }
+ }
+ return false;
+}