diff options
Diffstat (limited to '')
-rw-r--r-- | src/files_sub_source.cc | 437 |
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; +} |