[session] stages

This commit is contained in:
Timothy Stack 2020-10-28 21:24:23 -07:00
parent 5e42b4cb8a
commit df3df8369c
29 changed files with 589 additions and 166 deletions

View File

@ -48,8 +48,6 @@ static pcrepp &ansi_regex()
void scrub_ansi_string(std::string &str, string_attrs_t &sa)
{
view_colors &vc = view_colors::singleton();
pcre_context_static<60> context;
pcrepp & regex = ansi_regex();
pcre_input pi(str);
@ -62,6 +60,7 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
attr_t attrs = 0;
auto bg = nonstd::optional<int>();
auto fg = nonstd::optional<int>();
auto role = nonstd::optional<int>();
size_t lpc;
switch (pi.get_substr_start(&caps[2])[0]) {
@ -135,8 +134,7 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
if (sscanf(&(str[caps[1].c_begin]), "%d", &role_int) == 1) {
if (role_int >= 0 && role_int < view_colors::VCR__MAX) {
attrs = vc.attrs_for_role(
(view_colors::role_t) role_int);
role = role_int;
has_attrs = true;
}
}
@ -158,6 +156,9 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
if (attrs) {
sa.emplace_back(lr, &view_curses::VC_STYLE, attrs);
}
role | [&lr, &sa](int r) {
sa.emplace_back(lr, &view_curses::VC_ROLE, r);
};
fg | [&lr, &sa](int color) {
sa.emplace_back(lr, &view_curses::VC_FOREGROUND, color);
};

View File

@ -133,27 +133,12 @@ filename_to_tmp_path(const std::string &filename)
return tmp_path / fs::path(subdir_name) / basename;
}
void walk_archive_files(const std::string &filename,
const std::function<void(
const fs::path&,
const fs::directory_entry &)>& callback)
{
auto tmp_path = filename_to_tmp_path(filename);
extract(filename);
for (const auto& entry : fs::recursive_directory_iterator(tmp_path)) {
if (!entry.is_regular_file()) {
continue;
}
callback(tmp_path, entry);
}
}
#if HAVE_ARCHIVE_H
static int
copy_data(const ghc::filesystem::path &path, struct archive *ar, struct archive *aw)
copy_data(const ghc::filesystem::path &path,
struct archive *ar,
struct archive *aw,
struct extract_progress *ep)
{
int r;
const void *buff;
@ -175,6 +160,7 @@ copy_data(const ghc::filesystem::path &path, struct archive *ar, struct archive
}
total += size;
ep->ep_out_size.fetch_add(size);
if ((total - last_space_check) > (1024 * 1024)) {
auto tmp_space = ghc::filesystem::space(path);
@ -187,7 +173,7 @@ copy_data(const ghc::filesystem::path &path, struct archive *ar, struct archive
}
}
void extract(const std::string &filename)
static void extract(const std::string &filename, const extract_cb &cb)
{
static int FLAGS = ARCHIVE_EXTRACT_TIME
| ARCHIVE_EXTRACT_PERM
@ -221,7 +207,9 @@ void extract(const std::string &filename)
return;
}
log_info("extracting %s to %s", filename.c_str(), tmp_path.c_str());
log_info("extracting %s to %s",
filename.c_str(),
tmp_path.c_str());
while (true) {
struct archive_entry *entry;
auto r = archive_read_next_header(arc, &entry);
@ -239,6 +227,9 @@ void extract(const std::string &filename)
auto_mem<archive_entry> wentry(archive_entry_free);
wentry = archive_entry_clone(entry);
auto entry_path = tmp_path / fs::path(archive_entry_pathname(entry));
auto prog = cb(entry_path,
archive_entry_size_is_set(entry) ?
archive_entry_size(entry) : -1);
archive_entry_copy_pathname(wentry, entry_path.c_str());
auto entry_mode = archive_entry_mode(wentry);
@ -249,7 +240,7 @@ void extract(const std::string &filename)
log_error("%s", archive_error_string(ext));
}
else if (archive_entry_size(entry) > 0) {
r = copy_data(tmp_path, arc, ext);
r = copy_data(tmp_path, arc, ext, prog);
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(ext));
}
@ -271,12 +262,29 @@ void extract(const std::string &filename)
auto_fd(open(done_path.c_str(), O_CREAT | O_WRONLY, 0600));
// TODO return errors
}
#else
void extract(const std::string &filename)
{
}
#endif
void walk_archive_files(const std::string &filename,
const extract_cb &cb,
const std::function<void(
const fs::path&,
const fs::directory_entry &)>& callback)
{
#if HAVE_ARCHIVE_H
auto tmp_path = filename_to_tmp_path(filename);
extract(filename, cb);
for (const auto& entry : fs::recursive_directory_iterator(tmp_path)) {
if (!entry.is_regular_file()) {
continue;
}
callback(tmp_path, entry);
}
#endif
}
}

View File

@ -32,22 +32,38 @@
#ifndef lnav_archive_manager_hh
#define lnav_archive_manager_hh
#include <atomic>
#include <string>
#include <functional>
#include <utility>
#include "ghc/filesystem.hpp"
namespace archive_manager {
struct extract_progress {
extract_progress(ghc::filesystem::path path,
ssize_t total) : ep_path(std::move(path)),
ep_total_size(total)
{}
const ghc::filesystem::path ep_path;
const ssize_t ep_total_size;
std::atomic<size_t> ep_out_size{0};
};
using extract_cb = std::function<extract_progress *(
const ghc::filesystem::path &, ssize_t)>;
bool is_archive(const std::string &filename);
ghc::filesystem::path filename_to_tmp_path(const std::string &filename);
void walk_archive_files(const std::string &filename,
const extract_cb &cb,
const std::function<void(
const ghc::filesystem::path&,
const ghc::filesystem::directory_entry&)>&);
void extract(const std::string &filename);
const ghc::filesystem::path &,
const ghc::filesystem::directory_entry &)> &);
}
#endif

View File

@ -45,6 +45,10 @@ std::string file_size(ssize_t value)
" ", "K", "M", "G", "T", "P", "E",
};
if (value < 0) {
return "Unknown";
}
auto exp = floor(std::min(log(value) / LN1024, (double) UNITS.size()));
auto divisor = pow(1024, exp);

View File

@ -89,3 +89,16 @@ size_t unquote(char *dst, const char *str, size_t len)
return index;
}
void truncate_to(std::string &str, size_t len)
{
static const std::string ELLIPSIS = "\xE2\x8B\xAF";
if (str.length() <= len) {
return;
}
size_t half_width = str.size() / 2 - 1;
str.erase(half_width, str.length() - (half_width * 2));
str.insert(half_width, ELLIPSIS);
}

View File

@ -72,4 +72,6 @@ inline bool endswith(const std::string& str, const char (&suffix) [N])
return strcmp(&str[str.size() - N], suffix) == 0;
}
void truncate_to(std::string &str, size_t len);
#endif

View File

@ -30,6 +30,7 @@
#include "config.h"
#include "base/humanize.hh"
#include "base/string_util.hh"
#include "lnav.hh"
#include "files_sub_source.hh"
@ -43,21 +44,23 @@ files_sub_source::files_sub_source()
bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch)
{
switch (ch) {
case '\t':
case KEY_BTAB:
case 'q':
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_files_view.reload_data();
return true;
case KEY_ENTER:
case '\r': {
if (lnav_data.ld_active_files.fc_files.empty()) {
auto &fc = lnav_data.ld_active_files;
if (fc.fc_files.empty() && fc.fc_other_files.empty()) {
return true;
}
auto sel = (int) lv.get_selection();
sel -= fc.fc_other_files.size();
if (sel < 0) {
return true;
}
auto& lss = lnav_data.ld_log_source;
auto &lf = lnav_data.ld_active_files.fc_files[lv.get_selection()];
auto &lf = fc.fc_files[sel];
if (!lf->is_visible()) {
lf->show();
@ -86,11 +89,20 @@ bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch)
}
case ' ': {
if (lnav_data.ld_active_files.fc_files.empty()) {
auto &fc = lnav_data.ld_active_files;
if (fc.fc_files.empty() && fc.fc_other_files.empty()) {
return true;
}
auto &lf = lnav_data.ld_active_files.fc_files[lv.get_selection()];
auto sel = (int) lv.get_selection();
sel -= fc.fc_other_files.size();
if (sel < 0) {
return true;
}
auto &lf = fc.fc_files[sel];
lf->set_visibility(!lf->is_visible());
auto top_view = *lnav_data.ld_view_stack.top();
auto tss = top_view->get_sub_source();
@ -128,7 +140,9 @@ void files_sub_source::list_input_handle_scroll_out(listview_curses &lv)
size_t files_sub_source::text_line_count()
{
return lnav_data.ld_active_files.fc_files.size();
const auto &fc = lnav_data.ld_active_files;
return fc.fc_other_files.size() + fc.fc_files.size();
}
size_t files_sub_source::text_line_width(textview_curses &curses)
@ -140,16 +154,40 @@ 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 &lf = lnav_data.ld_active_files.fc_files[line];
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, dim.second - 30));
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.filename().string();
truncate_to(fn, filename_width);
value_out = fmt::format(
FMT_STRING(" {:<{}} {}"),
fn, filename_width, iter->second);
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] = "";
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);
value_out = fmt::format(
FMT_STRING(" {:<40} {:>8} {} \u2014 {}"),
lf->get_unique_path(),
FMT_STRING(" {:<{}} {:>8} {} \u2014 {}"),
fn,
filename_width,
humanize::file_size(lf->get_index_size()),
start_time,
end_time);
@ -158,10 +196,33 @@ void files_sub_source::text_value_for_line(textview_curses &tc, int line,
void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
string_attrs_t &value_out)
{
auto &vcolors = view_colors::singleton();
bool selected = lnav_data.ld_mode == LNM_FILES && line == tc.get_selection();
int bg = selected ? COLOR_WHITE : COLOR_BLACK;
auto &lf = lnav_data.ld_active_files.fc_files[line];
const auto &fc = lnav_data.ld_active_files;
auto &vcolors = view_colors::singleton();
const auto dim = tc.get_dimensions();
auto filename_width =
std::min(fc.fc_largest_path_length,
std::max((size_t) 40, dim.second - 30));
int fg = selected ? COLOR_BLACK : COLOR_WHITE;
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(fg));
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_BACKGROUND,
vcolors.ansi_to_theme_color(bg));
if (line < fc.fc_other_files.size()) {
if (line == fc.fc_other_files.size() - 1) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_STYLE,
A_UNDERLINE);
}
return;
}
line -= fc.fc_other_files.size();
auto &lf = fc.fc_files[line];
chtype visible = lf->is_visible() ? ACS_DIAMOND : ' ';
value_out.emplace_back(line_range{2, 3}, &view_curses::VC_GRAPHIC, visible);
@ -174,18 +235,14 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW);
}
value_out.emplace_back(line_range{41 + 4, 41 + 10},
&view_curses::VC_FOREGROUND,
COLOR_WHITE);
value_out.emplace_back(line_range{41 + 10, 41 + 12},
&view_curses::VC_STYLE,
A_BOLD);
int fg = selected ? COLOR_BLACK : COLOR_WHITE;
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(fg));
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_BACKGROUND,
vcolors.ansi_to_theme_color(bg));
auto lr = line_range{
(int) filename_width + 3 + 4,
(int) filename_width + 3 + 10,
};
value_out.emplace_back(lr, &view_curses::VC_FOREGROUND, COLOR_WHITE);
lr.lr_start = lr.lr_end;
lr.lr_end += 2;
value_out.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD);
}
size_t files_sub_source::text_size_for_line(textview_curses &tc, int line,
@ -193,3 +250,34 @@ size_t files_sub_source::text_size_for_line(textview_curses &tc, int line,
{
return 0;
}
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) {
auto &fc = lnav_data.ld_active_files;
auto &sp = fc.fc_progress;
std::lock_guard<std::mutex> guard(sp->sp_mutex);
if (!sp->sp_extractions.empty()) {
static char PROG[] = "-\\|/";
const auto& prog = sp->sp_extractions.front();
value_out.with_ansi_string(fmt::format(
"{} Extracting "
ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
"... {:>8}/{}",
PROG[this->fos_counter % sizeof(PROG)],
prog.ep_path.filename().string(),
humanize::file_size(prog.ep_out_size),
humanize::file_size(prog.ep_total_size)));
this->fos_counter += 1;
return true;
}
}
return false;
}

View File

@ -59,4 +59,12 @@ public:
bool fss_filter_state{false};
};
struct files_overlay_source : public list_overlay_source {
bool list_value_for_overlay(const listview_curses &lv, int y, int bottom,
vis_line_t line,
attr_line_t &value_out) override;
size_t fos_counter{0};
};
#endif

View File

@ -33,7 +33,7 @@
#include "filter_status_source.hh"
static auto TOGGLE_MSG = "Press " ANSI_BOLD("TAB") " to edit ";
static auto EXIT_MSG = "Press " ANSI_BOLD("TAB") " to exit ";
static auto EXIT_MSG = "Press " ANSI_BOLD("q") " to exit ";
static auto CREATE_HELP = ANSI_BOLD("i") "/" ANSI_BOLD("o") ": Create in/out";
static auto ENABLE_HELP = ANSI_BOLD("SPC") ": ";
@ -45,9 +45,11 @@ static auto JUMP_HELP = ANSI_BOLD("ENTER") ": Jump To";
filter_status_source::filter_status_source()
{
this->tss_fields[TSF_TITLE].set_width(9);
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_value(" Filters ");
this->tss_fields[TSF_TITLE].set_value(
" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
@ -62,6 +64,18 @@ filter_status_source::filter_status_source()
this->tss_fields[TSF_FILTERED].set_share(1);
this->tss_fields[TSF_FILTERED].set_role(view_colors::VCR_STATUS);
this->tss_fields[TSF_FILES_TITLE].set_width(7);
this->tss_fields[TSF_FILES_TITLE].set_role(
view_colors::VCR_STATUS_DISABLED_TITLE);
this->tss_fields[TSF_FILES_TITLE].set_value(
" " ANSI_ROLE("F") "iles ",
view_colors::VCR_STATUS_HOTKEY);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_width(2);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS,
view_colors::VCR_STATUS);
this->tss_fields[TSF_HELP].right_justify(true);
this->tss_fields[TSF_HELP].set_width(20);
this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG);
@ -77,10 +91,55 @@ filter_status_source::filter_status_source()
size_t filter_status_source::statusview_fields()
{
if (lnav_data.ld_mode == LNM_FILTER) {
this->tss_fields[TSF_HELP].set_value(EXIT_MSG);
switch (lnav_data.ld_mode) {
case LNM_SEARCH_FILTERS:
case LNM_SEARCH_FILES:
this->tss_fields[TSF_HELP].set_value("");
break;
case LNM_FILTER:
case LNM_FILES:
this->tss_fields[TSF_HELP].set_value(EXIT_MSG);
break;
default:
this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG);
break;
}
if (lnav_data.ld_mode == LNM_FILES ||
lnav_data.ld_mode == LNM_SEARCH_FILES) {
this->tss_fields[TSF_FILES_TITLE].set_value(
" " ANSI_ROLE("F") "iles ",
view_colors::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_FILES_TITLE]
.set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_TITLE].set_value(
" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_HOTKEY);
this->tss_fields[TSF_TITLE]
.set_role(view_colors::VCR_STATUS_DISABLED_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS,
view_colors::VCR_STATUS);
} else {
this->tss_fields[TSF_HELP].set_value(TOGGLE_MSG);
this->tss_fields[TSF_FILES_TITLE].set_value(
" " ANSI_ROLE("F") "iles ",
view_colors::VCR_STATUS_HOTKEY);
this->tss_fields[TSF_FILES_TITLE]
.set_role(view_colors::VCR_STATUS_DISABLED_TITLE);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE,
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL);
this->tss_fields[TSF_TITLE].set_value(
" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_TITLE]
.set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
}
if (this->tss_prompt.empty() && this->tss_error.empty()) {
@ -164,7 +223,7 @@ void filter_status_source::update_filtered(text_sub_source *tss)
this->bss_last_filtered_count = tss->get_filtered_count();
timer.start_fade(this->bss_filter_counter, 3);
}
sf.set_value("%'9d Lines not shown", tss->get_filtered_count());
sf.set_value("%'9d Lines not shown ", tss->get_filtered_count());
}
}
@ -222,13 +281,22 @@ size_t filter_help_status_source::statusview_fields()
}
} else if (lnav_data.ld_mode == LNM_FILES &&
lnav_data.ld_session_loaded) {
if (lnav_data.ld_active_files.fc_files.empty()) {
const auto &fc = lnav_data.ld_active_files;
if (fc.fc_files.empty() && fc.fc_other_files.empty()) {
this->fss_help.clear();
return;
}
auto &lv = lnav_data.ld_files_view;
auto sel = lv.get_selection();
auto sel = (int) lv.get_selection();
if (sel < fc.fc_other_files.size()) {
this->fss_help.clear();
return;
}
sel -= fc.fc_other_files.size();
auto &lf = lnav_data.ld_active_files.fc_files[sel];
this->fss_help.set_value(" %s%s %s",

View File

@ -27,8 +27,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _filter_status_source_hh
#define _filter_status_source_hh
#ifndef lnav_filter_status_source_hh
#define lnav_filter_status_source_hh
#include <string>
@ -39,6 +39,8 @@ class filter_status_source
: public status_data_source {
public:
typedef enum {
TSF_FILES_TITLE,
TSF_FILES_RIGHT_STITCH,
TSF_TITLE,
TSF_STITCH_TITLE,
TSF_COUNT,

View File

@ -72,12 +72,6 @@ bool filter_sub_source::list_input_handle_key(listview_curses &lv, int ch)
}
switch (ch) {
case '\t':
case KEY_BTAB:
case 'q':
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_filter_view.reload_data();
return true;
case 'f': {
auto top_view = *lnav_data.ld_view_stack.top();
auto tss = top_view->get_sub_source();

View File

@ -233,15 +233,30 @@
"title": "/ui/theme-defs/<theme_name>/status-styles/inactive",
"$ref": "#/definitions/style"
},
"title-hotkey": {
"description": "Styling for hotkey highlights in titles",
"title": "/ui/theme-defs/<theme_name>/status-styles/title-hotkey",
"$ref": "#/definitions/style"
},
"title": {
"description": "Styling for title sections of status bars",
"title": "/ui/theme-defs/<theme_name>/status-styles/title",
"$ref": "#/definitions/style"
},
"disabled-title": {
"description": "Styling for title sections of status bars",
"title": "/ui/theme-defs/<theme_name>/status-styles/disabled-title",
"$ref": "#/definitions/style"
},
"subtitle": {
"description": "Styling for subtitle sections of status bars",
"title": "/ui/theme-defs/<theme_name>/status-styles/subtitle",
"$ref": "#/definitions/style"
},
"hotkey": {
"description": "Styling for hotkey highlights of status bars",
"title": "/ui/theme-defs/<theme_name>/status-styles/hotkey",
"$ref": "#/definitions/style"
}
},
"additionalProperties": false

View File

@ -321,6 +321,20 @@ void listview_curses::do_update()
#endif
}
void listview_curses::shift_selection(int offset)
{
vis_line_t new_selection = this->lv_selection + vis_line_t(offset);
if (new_selection >= 0_vl &&
new_selection < this->get_inner_height()) {
this->set_selection(new_selection);
this->scroll_selection_into_view();
} else if (!alerter::singleton().chime()) {
// XXX Disabling for now...
// this->delegate_scroll_out();
}
}
static int scroll_polarity(mouse_button_t button)
{
return button == BUTTON_SCROLL_UP ? -1 : 1;

View File

@ -222,17 +222,7 @@ public:
}
}
void shift_selection(int offset) {
vis_line_t new_selection = this->lv_selection + vis_line_t(offset);
if (new_selection >= 0_vl &&
new_selection < this->get_inner_height()) {
this->set_selection(new_selection);
this->scroll_selection_into_view();
} else if (!alerter::singleton().chime()) {
this->delegate_scroll_out();
}
}
void shift_selection(int offset);
vis_line_t get_selection() const {
return this->lv_selection;
@ -544,6 +534,14 @@ public:
}
};
std::pair<vis_line_t, unsigned long> get_dimensions() const {
unsigned long width;
vis_line_t height;
this->get_dimensions(height, width);
return std::make_pair(height, width);
}
/** This method should be called when the data source has changed. */
virtual void reload_data();

View File

@ -129,6 +129,7 @@
#include "fstat_vtab.hh"
#include "textfile_highlighters.hh"
#include "base/future_util.hh"
#include "base/humanize.hh"
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
@ -404,14 +405,15 @@ private:
for (auto &sc : lnav_data.ld_status) {
sc.do_update();
}
if (!lnav_data.ld_session_loaded && lnav_data.ld_mode == LNM_FILES) {
auto iter = std::find(lnav_data.ld_active_files.fc_files.begin(),
lnav_data.ld_active_files.fc_files.end(), lf);
if (lnav_data.ld_mode == LNM_FILES) {
auto &fc = lnav_data.ld_active_files;
auto iter = std::find(fc.fc_files.begin(),
fc.fc_files.end(), lf);
if (iter != lnav_data.ld_active_files.fc_files.end()) {
auto index = std::distance(lnav_data.ld_active_files.fc_files.begin(),
iter);
lnav_data.ld_files_view.set_selection(vis_line_t(index));
if (iter != fc.fc_files.end()) {
auto index = std::distance(fc.fc_files.begin(), iter);
lnav_data.ld_files_view.set_selection(
vis_line_t(fc.fc_other_files.size() + index));
lnav_data.ld_files_view.reload_data();
lnav_data.ld_files_view.do_update();
}
@ -504,7 +506,8 @@ public:
auto iter = session_data.sd_file_states.find(lf->get_filename());
if (iter != session_data.sd_file_states.end()) {
log_debug("found state for log file");
log_debug("found state for log file %d",
iter->second.fs_is_visible);
lf->set_visibility(iter->second.fs_is_visible);
}
}
@ -982,10 +985,25 @@ void file_collection::regenerate_unique_file_names()
unique_path_generator upg;
for (const auto& lf : this->fc_files) {
upg.add_source(shared_ptr<logfile>(lf));
upg.add_source(lf);
}
upg.generate();
this->fc_largest_path_length = 0;
for (const auto& lf : this->fc_files) {
const auto& path = lf->get_unique_path();
if (path.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = path.length();
}
}
for (const auto& pair : this->fc_other_files) {
auto bn = ghc::filesystem::path(pair.first).filename().string();
if (bn.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = bn.length();
}
}
}
void file_collection::merge(const file_collection& other)
@ -1074,7 +1092,7 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
if (this->fc_other_files.find(filename) != this->fc_other_files.end()) {
return make_ready_future(retval);
}
return std::async(std::launch::async, [filename, &loo]() {
return std::async(std::launch::async, [filename, &loo, prog=this->fc_progress]() {
file_format_t ff = detect_file_format(filename);
file_collection retval;
@ -1087,8 +1105,16 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
case file_format_t::FF_ARCHIVE: {
retval.fc_other_files[filename] = "Archive";
archive_manager::walk_archive_files(
filename, [&filename, &retval](const auto& tmp_path,
const auto& entry) {
filename,
[prog](const auto& path, const auto total) {
std::lock_guard<std::mutex> guard(prog->sp_mutex);
prog->sp_extractions.clear();
prog->sp_extractions.emplace_back(path, total);
return &prog->sp_extractions.back();
},
[&filename, &retval](const auto& tmp_path,
const auto& entry) {
auto ext = entry.path().extension();
if (ext == ".jar" || ext == ".war" || ext == ".zip") {
return;
@ -1116,6 +1142,10 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
.with_non_utf_visibility(false)
.with_visible_size_limit(128 * 1024);
});
{
std::lock_guard<std::mutex> guard(prog->sp_mutex);
prog->sp_extractions.clear();
}
break;
}
@ -1222,6 +1252,11 @@ file_collection file_collection::rescan_files(bool required)
} else {
fq.push_back(watch_logfile(pair.first, pair.second, required));
}
if (retval.fc_files.size() >= 100) {
log_debug("too many new files, breaking...");
break;
}
}
fq.pop_to();
@ -1300,6 +1335,51 @@ public:
private:
};
static bool handle_config_ui_key(int ch)
{
nonstd::optional<ln_mode_t> new_mode;
if (ch == 'F') {
new_mode = LNM_FILES;
} else if (ch == 'T') {
new_mode = LNM_FILTER;
}
if (!lnav_data.ld_filter_source.fss_editing &&
(ch == '\t' || ch == KEY_BTAB)) {
if (lnav_data.ld_mode == LNM_FILES) {
new_mode = LNM_FILTER;
} else {
new_mode = LNM_FILES;
}
}
if (ch == 'q') {
lnav_data.ld_mode = LNM_PAGING;
} else if (new_mode) {
lnav_data.ld_last_config_mode = new_mode.value();
lnav_data.ld_mode = new_mode.value();
lnav_data.ld_files_view.reload_data();
lnav_data.ld_filter_view.reload_data();
lnav_data.ld_status[LNS_FILTER].set_needs_update();
} else {
switch (lnav_data.ld_mode) {
case LNM_FILES:
if (!lnav_data.ld_files_view.handle_key(ch)) {
return handle_paging_key(ch);
}
break;
case LNM_FILTER:
if (!lnav_data.ld_filter_view.handle_key(ch)) {
return handle_paging_key(ch);
}
break;
default:
ensure(0);
}
}
return true;
}
static bool handle_key(int ch) {
lnav_data.ld_input_state.push_back(ch);
@ -1313,23 +1393,8 @@ static bool handle_key(int ch) {
return handle_paging_key(ch);
case LNM_FILTER:
if (ch == 'F') {
lnav_data.ld_last_config_mode = LNM_FILES;
lnav_data.ld_mode = LNM_FILES;
lnav_data.ld_files_view.reload_data();
} else if (!lnav_data.ld_filter_view.handle_key(ch)) {
return handle_paging_key(ch);
}
break;
case LNM_FILES:
if (ch == 'T') {
lnav_data.ld_last_config_mode = LNM_FILTER;
lnav_data.ld_mode = LNM_FILTER;
lnav_data.ld_filter_view.reload_data();
} else if (!lnav_data.ld_files_view.handle_key(ch)) {
return handle_paging_key(ch);
}
break;
return handle_config_ui_key(ch);
case LNM_COMMAND:
case LNM_SEARCH:
@ -1671,6 +1736,7 @@ static void looper()
lnav_data.ld_files_view.set_selectable(true);
lnav_data.ld_files_view.set_window(lnav_data.ld_window);
lnav_data.ld_files_view.set_show_scrollbar(true);
lnav_data.ld_files_view.set_overlay_source(&lnav_data.ld_files_overlay);
lnav_data.ld_status[LNS_TOP].set_top(0);
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
@ -1751,18 +1817,22 @@ static void looper()
log_debug("rescan started");
file_collection active_copy;
active_copy.merge(lnav_data.ld_active_files);
active_copy.fc_progress = lnav_data.ld_active_files.fc_progress;
future<file_collection> rescan_future =
std::async(std::launch::async,
&file_collection::rescan_files,
active_copy,
false);
bool initial_rescan_completed = false;
int session_stage = 0;
while (lnav_data.ld_looping) {
vector<struct pollfd> pollfds;
struct timeval to = { 0, 333000 };
int rc;
auto poll_to = initial_rescan_completed ?
timeval{ 0, 333000 } :
timeval{ 0, 0 };
size_t starting_view_stack_size = lnav_data.ld_view_stack.vs_views.size();
int rc;
gettimeofday(&current_time, nullptr);
@ -1770,13 +1840,40 @@ static void looper()
layout_views();
if (rescan_future.wait_for(0s) == std::future_status::ready) {
auto scan_timeout = initial_rescan_completed ? 0s : 10ms;
if (rescan_future.wait_for(scan_timeout) ==
std::future_status::ready) {
auto new_files = rescan_future.get();
if (!initial_rescan_completed &&
new_files.fc_file_names.empty()) {
new_files.fc_file_names.empty() &&
new_files.fc_files.empty()) {
initial_rescan_completed = true;
load_session();
if (session_data.sd_save_time) {
std::string ago;
ago = time_ago(session_data.sd_save_time);
lnav_data.ld_rl_view->set_value(
("restored session from " ANSI_BOLD_START) +
ago +
(ANSI_NORM "; press Ctrl-R to reset session"));
}
lnav_data.ld_session_loaded = true;
session_stage += 1;
log_debug("file count %d",
lnav_data.ld_active_files.fc_files.size())
}
update_active_files(new_files);
if (!initial_rescan_completed) {
auto &fview = lnav_data.ld_files_view;
auto height = fview.get_inner_height();
if (height > 0_vl) {
fview.set_selection(height - 1_vl);
}
}
active_copy.clear();
active_copy.merge(lnav_data.ld_active_files);
@ -1785,7 +1882,11 @@ static void looper()
active_copy,
false);
}
rebuild_indexes();
if (initial_rescan_completed) {
rebuild_indexes();
} else {
lnav_data.ld_files_view.set_overlay_needs_update();
}
lnav_data.ld_view_stack.do_update();
lnav_data.ld_doc_view.do_update();
@ -1795,15 +1896,16 @@ static void looper()
for (auto &sc : lnav_data.ld_status) {
sc.do_update();
}
rlc.do_update();
if (lnav_data.ld_filter_source.fss_editing) {
lnav_data.ld_filter_source.fss_match_view.set_needs_update();
}
switch (lnav_data.ld_mode) {
case LNM_FILTER:
case LNM_SEARCH_FILTERS:
lnav_data.ld_filter_view.set_needs_update();
lnav_data.ld_filter_view.do_update();
break;
case LNM_SEARCH_FILES:
case LNM_FILES:
lnav_data.ld_files_view.set_needs_update();
lnav_data.ld_files_view.do_update();
@ -1811,6 +1913,10 @@ static void looper()
default:
break;
}
if (lnav_data.ld_mode != LNM_FILTER &&
lnav_data.ld_mode != LNM_FILES) {
rlc.do_update();
}
refresh();
if (lnav_data.ld_session_loaded) {
@ -1830,10 +1936,11 @@ static void looper()
lnav_data.ld_filter_view.update_poll_set(pollfds);
lnav_data.ld_files_view.update_poll_set(pollfds);
if (lnav_data.ld_input_dispatcher.in_escape()) {
to.tv_usec = 15000;
if (initial_rescan_completed &&
lnav_data.ld_input_dispatcher.in_escape()) {
poll_to.tv_usec = 15000;
}
rc = poll(&pollfds[0], pollfds.size(), to.tv_usec / 1000);
rc = poll(&pollfds[0], pollfds.size(), poll_to.tv_usec / 1000);
gettimeofday(&current_time, nullptr);
lnav_data.ld_input_dispatcher.poll(current_time);
@ -1935,22 +2042,6 @@ static void looper()
initial_build = true;
}
if (!lnav_data.ld_session_loaded) {
load_session();
if (session_data.sd_save_time) {
std::string ago;
ago = time_ago(session_data.sd_save_time);
lnav_data.ld_rl_view->set_value(
("restored session from " ANSI_BOLD_START) +
ago +
(ANSI_NORM "; press Ctrl-R to reset session"));
}
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_session_loaded = true;
}
if (initial_build) {
vector<pair<Result<string, string>, string>> cmd_results;
@ -1965,6 +2056,21 @@ static void looper()
last_cmd_result.second);
}
}
if (session_stage == 1) {
for (size_t view_index = 0;
view_index < LNV__MAX;
view_index++) {
const auto &vs = session_data.sd_view_states[view_index];
if (vs.vs_top > 0) {
lnav_data.ld_views[view_index]
.set_top(vis_line_t(vs.vs_top));
}
}
lnav_data.ld_mode = LNM_PAGING;
session_stage += 1;
}
}
if (lnav_data.ld_winched) {

View File

@ -70,6 +70,7 @@
#include "filter_status_source.hh"
#include "preview_status_source.hh"
#include "sql_util.hh"
#include "archive_manager.hh"
/** The command modes that are available while viewing a file. */
typedef enum {
@ -184,17 +185,10 @@ private:
};
struct key_repeat_history {
key_repeat_history()
: krh_key(0),
krh_count(0) {
this->krh_last_press_time.tv_sec = 0;
this->krh_last_press_time.tv_usec = 0;
}
int krh_key;
int krh_count;
vis_line_t krh_start_line;
struct timeval krh_last_press_time;
int krh_key{0};
int krh_count{0};
vis_line_t krh_start_line{0_vl};
struct timeval krh_last_press_time{0, 0};
void update(int ch, vis_line_t top) {
struct timeval now, diff;
@ -217,6 +211,11 @@ struct key_repeat_history {
};
};
struct scan_progress {
std::mutex sp_mutex;
std::list<archive_manager::extract_progress> sp_extractions;
};
struct file_collection {
std::map<std::string, std::string> fc_name_to_errors;
std::map<std::string, logfile_open_options> fc_file_names;
@ -226,6 +225,10 @@ struct file_collection {
fc_renamed_files;
std::set<std::string> fc_closed_files;
std::map<std::string, std::string> fc_other_files;
std::shared_ptr<scan_progress> fc_progress;
size_t fc_largest_path_length{0};
file_collection() : fc_progress(std::make_shared<scan_progress>()) {}
void clear() {
this->fc_name_to_errors.clear();
@ -286,6 +289,7 @@ struct _lnav_data {
filter_sub_source ld_filter_source;
textview_curses ld_filter_view;
files_sub_source ld_files_source;
files_overlay_source ld_files_overlay;
textview_curses ld_files_view;
plain_text_source ld_example_source;
textview_curses ld_example_view;

View File

@ -4123,6 +4123,7 @@ static void search_prompt(vector<string> &args)
static void search_filters_prompt(vector<string> &args)
{
lnav_data.ld_mode = LNM_SEARCH_FILTERS;
lnav_data.ld_filter_view.reload_data();
add_view_text_possibilities(lnav_data.ld_rl_view,
LNM_SEARCH_FILTERS,
"*",

View File

@ -669,18 +669,36 @@ static struct json_path_container theme_status_styles_handlers = {
return &root->lt_style_inactive_status;
})
.with_children(style_config_handlers),
yajlpp::property_handler("title-hotkey")
.with_description("Styling for hotkey highlights in titles")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_title_hotkey;
})
.with_children(style_config_handlers),
yajlpp::property_handler("title")
.with_description("Styling for title sections of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_title;
})
.with_children(style_config_handlers),
yajlpp::property_handler("disabled-title")
.with_description("Styling for title sections of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_disabled_title;
})
.with_children(style_config_handlers),
yajlpp::property_handler("subtitle")
.with_description("Styling for subtitle sections of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_subtitle;
})
.with_children(style_config_handlers)
.with_children(style_config_handlers),
yajlpp::property_handler("hotkey")
.with_description("Styling for hotkey highlights of status bars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_status_hotkey;
})
.with_children(style_config_handlers),
};
static struct json_path_container theme_log_level_styles_handlers = {

View File

@ -589,8 +589,9 @@ logfile_sub_source::rebuild_result logfile_sub_source::rebuild_index()
if (retval == rebuild_result::rr_no_change) {
retval = rebuild_result::rr_appended_lines;
}
if (!this->lss_index.empty()) {
logline &new_file_line = lf[ld.ld_lines_indexed - 1];
if (!this->lss_index.empty() &&
lf.size() > ld.ld_lines_indexed) {
logline &new_file_line = lf[ld.ld_lines_indexed];
content_line_t cl = this->lss_index.back();
logline *last_indexed_line = this->find_line(cl);

View File

@ -748,11 +748,7 @@ static int read_top_line(yajlpp_parse_context *ypc, long long value)
ypc->get_path_fragment(-2));
view_index = view_name - lnav_view_strings;
if (view_index < LNV__MAX) {
textview_curses &tc = lnav_data.ld_views[view_index];
if (value != -1 && value < tc.get_inner_height()) {
tc.set_top(vis_line_t(value));
}
session_data.sd_view_states[view_index].vs_top = value;
}
return 1;

View File

@ -35,14 +35,21 @@
#include <map>
#include <string>
#include "lnav.hh"
struct file_state {
bool fs_is_visible{true};
};
struct view_state {
int64_t vs_top{0};
};
struct session_data_t {
uint64_t sd_save_time{0};
bool sd_time_offset{false};
std::map<std::string, file_state> sd_file_states;
view_state sd_view_states[LNV__MAX];
};
extern struct session_data_t session_data;

View File

@ -44,7 +44,6 @@ void status_field::set_value(std::string value)
sa.clear();
scrub_ansi_string(value, sa);
this->sf_value.with_string(value);
if (this->sf_cylon) {

View File

@ -54,7 +54,7 @@ public:
sf_role(role) {
};
virtual ~status_field() { };
virtual ~status_field() = default;
/** @param value The new value for this field. */
void set_value(std::string value);

View File

@ -143,7 +143,10 @@ struct lnav_theme {
style_config lt_style_skewed_time;
style_config lt_style_offset_time;
style_config lt_style_status_title;
style_config lt_style_status_title_hotkey;
style_config lt_style_status_disabled_title;
style_config lt_style_status_subtitle;
style_config lt_style_status_hotkey;
style_config lt_style_keyword;
style_config lt_style_string;
style_config lt_style_comment;

View File

@ -90,6 +90,11 @@
"background-color": "Blue",
"bold": true
},
"disabled-title": {
"color": "Black",
"background-color": "Silver",
"bold": true
},
"subtitle": {
"color": "Black",
"background-color": "Teal"
@ -113,6 +118,17 @@
"inactive": {
"color": "Silver",
"background-color": "Grey37"
},
"title-hotkey": {
"color": "Teal",
"background-color": "Blue",
"underline": true
},
"hotkey": {
"color": "Purple",
"background-color": "Silver",
"underline": true,
"bold": true
}
},
"log-level-styles": {

View File

@ -101,6 +101,11 @@
}
},
"status-styles": {
"disabled-title": {
"color": "#5394ec",
"background-color": "#353535",
"bold": true
},
"title": {
"color": "#f6f6f6",
"background-color": "#5394ec",
@ -111,6 +116,16 @@
"background-color": "#66d9ee",
"bold": true
},
"title-hotkey": {
"color": "$black",
"background-color": "#5394ec",
"underline": true
},
"hotkey": {
"color": "#fff",
"background-color": "#353535",
"underline": true
},
"text": {
"color": "#f6f6f6",
"background-color": "#353535"

View File

@ -4,6 +4,7 @@
"theme-defs": {
"night-owl": {
"vars": {
"black": "#011627",
"red": "#ff6868",
"green": "#007f00",
"yellow": "#cdcd00",
@ -125,6 +126,18 @@
"inactive": {
"color": "#f0f0f0",
"background-color": "#0F1F2B"
},
"hotkey": {
"color": "#2d5a80",
"background-color": "#162d40",
"bold": true,
"underline": true
},
"title-hotkey": {
"color": "$black",
"background-color": "#2d5a80",
"bold": true,
"underline": true
}
},
"log-level-styles": {

View File

@ -825,6 +825,16 @@ void view_colors::init_roles(const lnav_theme &lt,
this->vc_role_colors[VCR_STATUS_SUBTITLE] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_subtitle, lt.lt_style_status, reporter);
this->vc_role_colors[VCR_STATUS_HOTKEY] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_hotkey, lt.lt_style_status,
reporter);
this->vc_role_colors[VCR_STATUS_TITLE_HOTKEY] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_title_hotkey, lt.lt_style_status,
reporter);
this->vc_role_colors[VCR_STATUS_DISABLED_TITLE] = this->to_attrs(
color_pair_base, lt, lt.lt_style_status_disabled_title, lt.lt_style_status,
reporter);
{
style_config stitch_sc;

View File

@ -327,6 +327,9 @@ public:
VCR_STATUS_STITCH_NORMAL_TO_SUB,
VCR_STATUS_STITCH_TITLE_TO_NORMAL,
VCR_STATUS_STITCH_NORMAL_TO_TITLE,
VCR_STATUS_TITLE_HOTKEY,
VCR_STATUS_DISABLED_TITLE,
VCR_STATUS_HOTKEY,
VCR_INACTIVE_STATUS,
VCR_SCROLLBAR,
VCR_SCROLLBAR_ERROR,