mirror of https://github.com/tstack/lnav
[gantt] add a gantt chart view
parent
7c8d32308a
commit
f71300ba1d
@ -0,0 +1,479 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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 <chrono>
|
||||
|
||||
#include "gantt_source.hh"
|
||||
|
||||
#include "base/humanize.hh"
|
||||
#include "base/humanize.time.hh"
|
||||
#include "base/math_util.hh"
|
||||
#include "md4cpp.hh"
|
||||
#include "sql_util.hh"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace lnav::roles::literals;
|
||||
using namespace md4cpp::literals;
|
||||
|
||||
static const std::vector<std::chrono::seconds> TIME_SPANS = {
|
||||
5min,
|
||||
15min,
|
||||
1h,
|
||||
8h,
|
||||
24h,
|
||||
7 * 24h,
|
||||
30 * 24h,
|
||||
365 * 24h,
|
||||
};
|
||||
|
||||
gantt_header_overlay::gantt_header_overlay(std::shared_ptr<gantt_source> src)
|
||||
: gho_src(src)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
gantt_header_overlay::list_static_overlay(const listview_curses& lv,
|
||||
int y,
|
||||
int bottom,
|
||||
attr_line_t& value_out)
|
||||
{
|
||||
if (y >= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->gho_src->gs_time_order.empty()) {
|
||||
if (y == 0) {
|
||||
value_out.append("No operations found"_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bounds = this->gho_src->get_time_bounds_for(lv.get_selection());
|
||||
auto width = lv.get_dimensions().second - 1;
|
||||
|
||||
char datebuf[64];
|
||||
|
||||
if (y == 0) {
|
||||
auto lb = this->gho_src->gs_lower_bound;
|
||||
auto ub = this->gho_src->gs_upper_bound;
|
||||
|
||||
double span = ub.tv_sec - lb.tv_sec;
|
||||
double per_ch = span / (double) width;
|
||||
sql_strftime(datebuf, sizeof(datebuf), lb, 'T');
|
||||
value_out.appendf(FMT_STRING(" {}"), datebuf);
|
||||
|
||||
auto upper_size = sql_strftime(datebuf, sizeof(datebuf), ub, 'T');
|
||||
value_out.append(width - value_out.length() - upper_size - 1, ' ')
|
||||
.append(datebuf);
|
||||
|
||||
auto lr = line_range{};
|
||||
if (lb.tv_sec < bounds.first.tv_sec) {
|
||||
auto start_diff = bounds.first.tv_sec - lb.tv_sec;
|
||||
lr.lr_start = start_diff / per_ch;
|
||||
} else {
|
||||
lr.lr_start = 0;
|
||||
}
|
||||
if (lb.tv_sec < bounds.second.tv_sec) {
|
||||
auto start_diff = bounds.second.tv_sec - lb.tv_sec;
|
||||
lr.lr_end = start_diff / per_ch;
|
||||
} else {
|
||||
lr.lr_end = 1;
|
||||
}
|
||||
if (lr.lr_start == lr.lr_end) {
|
||||
lr.lr_end += 1;
|
||||
}
|
||||
|
||||
value_out.get_attrs().emplace_back(
|
||||
lr, VC_ROLE.value(role_t::VCR_CURSOR_LINE));
|
||||
value_out.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS_INFO));
|
||||
} else if (y == 1) {
|
||||
sql_strftime(datebuf, sizeof(datebuf), bounds.first, 'T');
|
||||
value_out.appendf(FMT_STRING(" {}"), datebuf);
|
||||
|
||||
auto upper_size
|
||||
= sql_strftime(datebuf, sizeof(datebuf), bounds.second, 'T');
|
||||
value_out.append(width - value_out.length() - upper_size - 5, ' ')
|
||||
.append(datebuf);
|
||||
value_out.with_attr_for_all(VC_ROLE.value(role_t::VCR_CURSOR_LINE));
|
||||
} else {
|
||||
value_out.append(" Duration "_h1)
|
||||
.append("|", VC_GRAPHIC.value(ACS_VLINE))
|
||||
.append(" ")
|
||||
.append("\u2718"_error)
|
||||
.append("\u25b2"_warning)
|
||||
.append(" ")
|
||||
.append("|", VC_GRAPHIC.value(ACS_VLINE))
|
||||
.append(" Operation"_h1);
|
||||
auto hdr_attrs = text_attrs{};
|
||||
hdr_attrs.ta_attrs = A_UNDERLINE;
|
||||
value_out.get_attrs().emplace_back(line_range{0, -1},
|
||||
VC_STYLE.value(hdr_attrs));
|
||||
value_out.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS_INFO));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gantt_source::gantt_source(textview_curses& log_view,
|
||||
logfile_sub_source& lss,
|
||||
plain_text_source& preview_source,
|
||||
preview_status_source& preview_status_source)
|
||||
: gs_log_view(log_view), gs_lss(lss), gs_preview_source(preview_source),
|
||||
gs_preview_status_source(preview_status_source)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<timeval, timeval>
|
||||
gantt_source::get_time_bounds_for(int line)
|
||||
{
|
||||
static const int CONTEXT_LINES = 5;
|
||||
|
||||
const auto& low_row
|
||||
= this->gs_time_order[std::max(0, line - CONTEXT_LINES)];
|
||||
const auto& sel_row = this->gs_time_order[line];
|
||||
const auto& high_row = this->gs_time_order[std::min(
|
||||
line + CONTEXT_LINES, (int) this->gs_time_order.size() - 1)];
|
||||
auto high_tv_sec = std::max(sel_row.or_value.otr_end.tv_sec,
|
||||
high_row.or_value.otr_begin.tv_sec);
|
||||
|
||||
auto duration
|
||||
= std::chrono::seconds{high_tv_sec - low_row.or_value.otr_begin.tv_sec};
|
||||
auto span_iter
|
||||
= std::upper_bound(TIME_SPANS.begin(), TIME_SPANS.end(), duration);
|
||||
if (span_iter == TIME_SPANS.end()) {
|
||||
--span_iter;
|
||||
}
|
||||
auto span_secs = span_iter->count() - 60;
|
||||
struct timeval lower_tv = {
|
||||
rounddown(low_row.or_value.otr_begin.tv_sec, 60),
|
||||
0,
|
||||
};
|
||||
lower_tv.tv_sec -= span_secs / 2;
|
||||
struct timeval upper_tv = {
|
||||
static_cast<time_t>(roundup_size(high_tv_sec, 60)),
|
||||
0,
|
||||
};
|
||||
upper_tv.tv_sec += span_secs / 2;
|
||||
|
||||
return {lower_tv, upper_tv};
|
||||
}
|
||||
|
||||
size_t
|
||||
gantt_source::text_line_count()
|
||||
{
|
||||
return this->gs_time_order.size();
|
||||
}
|
||||
|
||||
void
|
||||
gantt_source::text_value_for_line(textview_curses& tc,
|
||||
int line,
|
||||
std::string& value_out,
|
||||
text_sub_source::line_flags_t flags)
|
||||
{
|
||||
if (line < this->gs_time_order.size()) {
|
||||
const auto& row = this->gs_time_order[line];
|
||||
auto duration = row.or_value.otr_end - row.or_value.otr_begin;
|
||||
auto duration_str = fmt::format(
|
||||
FMT_STRING(" {: >13}"),
|
||||
humanize::time::duration::from_tv(duration).to_string());
|
||||
|
||||
this->gs_rendered_line.clear();
|
||||
|
||||
auto total_msgs = row.or_value.get_total_msgs();
|
||||
this->gs_rendered_line
|
||||
.append(duration_str, VC_ROLE.value(role_t::VCR_OFFSET_TIME))
|
||||
.append(" ")
|
||||
.append(lnav::roles::error(humanize::sparkline(
|
||||
row.or_value.get_error_count(), total_msgs)))
|
||||
.append(lnav::roles::warning(humanize::sparkline(
|
||||
row.or_value.otr_level_counts[log_level_t::LEVEL_WARNING],
|
||||
total_msgs)))
|
||||
.append(" ")
|
||||
.append(lnav::roles::identifier(row.or_name.to_string()))
|
||||
.append(this->gs_opid_width - row.or_name.length(), ' ');
|
||||
for (const auto& desc_pair : row.or_value.otr_description) {
|
||||
this->gs_rendered_line.append(" ");
|
||||
this->gs_rendered_line.append(desc_pair.second);
|
||||
}
|
||||
this->gs_rendered_line.with_attr_for_all(
|
||||
VC_ROLE.value(role_t::VCR_COMMENT));
|
||||
|
||||
value_out = this->gs_rendered_line.get_string();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gantt_source::text_attrs_for_line(textview_curses& tc,
|
||||
int line,
|
||||
string_attrs_t& value_out)
|
||||
{
|
||||
if (line < this->gs_time_order.size()) {
|
||||
const auto& row = this->gs_time_order[line];
|
||||
|
||||
value_out = this->gs_rendered_line.get_attrs();
|
||||
|
||||
auto lr = line_range{};
|
||||
auto sel_bounds = this->get_time_bounds_for(tc.get_selection());
|
||||
|
||||
if (row.or_value.otr_begin <= sel_bounds.second
|
||||
&& sel_bounds.first <= row.or_value.otr_end)
|
||||
{
|
||||
static const int INDENT = 22;
|
||||
|
||||
auto width = tc.get_dimensions().second;
|
||||
|
||||
if (width > INDENT) {
|
||||
width -= INDENT;
|
||||
double span
|
||||
= sel_bounds.second.tv_sec - sel_bounds.first.tv_sec;
|
||||
double per_ch = span / (double) width;
|
||||
|
||||
if (row.or_value.otr_begin <= sel_bounds.first) {
|
||||
lr.lr_start = INDENT;
|
||||
} else {
|
||||
auto start_diff = row.or_value.otr_begin.tv_sec
|
||||
- sel_bounds.first.tv_sec;
|
||||
|
||||
lr.lr_start = INDENT + start_diff / per_ch;
|
||||
}
|
||||
|
||||
if (sel_bounds.second < row.or_value.otr_end) {
|
||||
lr.lr_end = -1;
|
||||
} else {
|
||||
auto end_diff
|
||||
= row.or_value.otr_end.tv_sec - sel_bounds.first.tv_sec;
|
||||
|
||||
lr.lr_end = INDENT + end_diff / per_ch;
|
||||
if (lr.lr_start == lr.lr_end) {
|
||||
lr.lr_end += 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto block_attrs = text_attrs{};
|
||||
block_attrs.ta_attrs = A_REVERSE;
|
||||
value_out.emplace_back(lr, VC_STYLE.value(block_attrs));
|
||||
}
|
||||
}
|
||||
auto alt_row_index = line % 4;
|
||||
if (alt_row_index == 2 || alt_row_index == 3) {
|
||||
value_out.emplace_back(line_range{0, -1},
|
||||
VC_ROLE.value(role_t::VCR_ALT_ROW));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
gantt_source::text_size_for_line(textview_curses& tc,
|
||||
int line,
|
||||
text_sub_source::line_flags_t raw)
|
||||
{
|
||||
return this->gs_total_width;
|
||||
}
|
||||
|
||||
void
|
||||
gantt_source::rebuild_indexes()
|
||||
{
|
||||
auto& bm = this->tss_view->get_bookmarks();
|
||||
auto& bm_errs = bm[&logfile_sub_source::BM_ERRORS];
|
||||
auto& bm_warns = bm[&logfile_sub_source::BM_WARNINGS];
|
||||
|
||||
bm_errs.clear();
|
||||
bm_warns.clear();
|
||||
|
||||
this->gs_lower_bound = {};
|
||||
this->gs_upper_bound = {};
|
||||
this->gs_opid_width = 0;
|
||||
this->gs_total_width = 0;
|
||||
|
||||
auto max_desc_width = size_t{0};
|
||||
|
||||
log_opid_map active_opids;
|
||||
for (const auto& ld : this->gs_lss) {
|
||||
if (ld->get_file_ptr() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
safe::ReadAccess<logfile::safe_opid_map> r_opid_map(
|
||||
ld->get_file_ptr()->get_opids());
|
||||
|
||||
for (const auto& pair : *r_opid_map) {
|
||||
auto iter = this->gs_opid_map.find(pair.first);
|
||||
if (iter == this->gs_opid_map.end()) {
|
||||
auto opid = pair.first.to_owned(this->gs_allocator);
|
||||
auto emp_res
|
||||
= this->gs_opid_map.emplace(opid, opid_description_defs{});
|
||||
iter = emp_res.first;
|
||||
}
|
||||
|
||||
auto active_iter = active_opids.find(pair.first);
|
||||
if (active_iter == active_opids.end()) {
|
||||
active_opids.emplace(iter->first, pair.second);
|
||||
} else {
|
||||
active_iter->second |= pair.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<struct timeval, opid_row> time_order_map;
|
||||
for (const auto& pair : active_opids) {
|
||||
if (this->gs_lower_bound.tv_sec == 0
|
||||
|| pair.second.otr_begin < this->gs_lower_bound)
|
||||
{
|
||||
this->gs_lower_bound = pair.second.otr_begin;
|
||||
}
|
||||
if (this->gs_upper_bound.tv_sec == 0
|
||||
|| this->gs_upper_bound < pair.second.otr_end)
|
||||
{
|
||||
this->gs_upper_bound = pair.second.otr_end;
|
||||
}
|
||||
if (pair.first.length() > this->gs_opid_width) {
|
||||
this->gs_opid_width = pair.first.length();
|
||||
}
|
||||
time_order_map.emplace(pair.second.otr_begin,
|
||||
opid_row{pair.first, pair.second});
|
||||
}
|
||||
this->gs_time_order.clear();
|
||||
for (const auto& pair : time_order_map) {
|
||||
if (pair.second.or_value.get_error_count() > 0) {
|
||||
bm_errs.insert_once(vis_line_t(this->gs_time_order.size()));
|
||||
} else if (pair.second.or_value
|
||||
.otr_level_counts[log_level_t::LEVEL_WARNING]
|
||||
> 0)
|
||||
{
|
||||
bm_warns.insert_once(vis_line_t(this->gs_time_order.size()));
|
||||
}
|
||||
auto total_desc_width = size_t{0};
|
||||
for (const auto& desc : pair.second.or_value.otr_description) {
|
||||
total_desc_width += 1 + desc.second.length();
|
||||
}
|
||||
if (total_desc_width > max_desc_width) {
|
||||
max_desc_width = total_desc_width;
|
||||
}
|
||||
this->gs_time_order.emplace_back(pair.second);
|
||||
}
|
||||
this->gs_total_width = 8 + this->gs_opid_width + max_desc_width;
|
||||
}
|
||||
|
||||
nonstd::optional<vis_line_t>
|
||||
gantt_source::row_for_time(struct timeval time_bucket)
|
||||
{
|
||||
auto iter = std::lower_bound(this->gs_time_order.begin(),
|
||||
this->gs_time_order.end(),
|
||||
time_bucket,
|
||||
[](const opid_row& lhs, const timeval& rhs) {
|
||||
return lhs.or_value.otr_begin < rhs;
|
||||
});
|
||||
if (iter == this->gs_time_order.end()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return vis_line_t(std::distance(this->gs_time_order.begin(), iter));
|
||||
}
|
||||
|
||||
nonstd::optional<struct timeval>
|
||||
gantt_source::time_for_row(vis_line_t row)
|
||||
{
|
||||
if (row >= this->gs_time_order.size()) {
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return this->gs_time_order[row].or_value.otr_begin;
|
||||
}
|
||||
|
||||
size_t
|
||||
gantt_source::text_line_width(textview_curses& curses)
|
||||
{
|
||||
return this->gs_total_width;
|
||||
}
|
||||
|
||||
void
|
||||
gantt_source::text_selection_changed(textview_curses& tc)
|
||||
{
|
||||
static const size_t MAX_PREVIEW_LINES = 5;
|
||||
|
||||
auto sel = tc.get_selection();
|
||||
|
||||
this->gs_preview_source.clear();
|
||||
if (sel >= this->gs_time_order.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& row = this->gs_time_order[sel];
|
||||
|
||||
auto low_vl = this->gs_lss.row_for_time(row.or_value.otr_begin);
|
||||
auto high_tv = row.or_value.otr_end;
|
||||
high_tv.tv_sec += 1;
|
||||
auto high_vl = this->gs_lss.row_for_time(high_tv);
|
||||
|
||||
if (!low_vl || !high_vl) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto preview_content = attr_line_t();
|
||||
auto msgs_remaining = size_t{MAX_PREVIEW_LINES};
|
||||
auto win = this->gs_lss.window_at(low_vl.value(), high_vl.value());
|
||||
auto id_hash = hash_str(row.or_name.data(), row.or_name.length());
|
||||
for (const auto& msg_line : win) {
|
||||
if (!msg_line.get_logline().match_opid_hash(id_hash)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& sa = msg_line.get_attrs();
|
||||
auto opid_opt = get_string_attr(sa, logline::L_OPID);
|
||||
|
||||
if (opid_opt) {
|
||||
auto opid_range = opid_opt.value().saw_string_attr->sa_range;
|
||||
auto opid_sf = msg_line.to_string(opid_range);
|
||||
|
||||
if (opid_sf == row.or_name) {
|
||||
std::vector<attr_line_t> rows_al(1);
|
||||
|
||||
this->gs_log_view.listview_value_for_rows(
|
||||
this->gs_log_view, msg_line.get_vis_line(), rows_al);
|
||||
|
||||
preview_content.append(rows_al[0]).append("\n");
|
||||
msgs_remaining -= 1;
|
||||
if (msgs_remaining == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (msgs_remaining > 0) {
|
||||
preview_content.append("\u2800\n");
|
||||
msgs_remaining -= 1;
|
||||
}
|
||||
|
||||
this->gs_preview_source.replace_with(preview_content);
|
||||
this->gs_preview_status_source.get_description().set_value(
|
||||
"Messages with opid: %.*s", row.or_name.length(), row.or_name.data());
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
#ifndef lnav_gantt_source_hh
|
||||
#define lnav_gantt_source_hh
|
||||
|
||||
#include "logfile_sub_source.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "preview_status_source.hh"
|
||||
#include "textview_curses.hh"
|
||||
|
||||
class gantt_source
|
||||
: public text_sub_source
|
||||
, public text_time_translator {
|
||||
public:
|
||||
explicit gantt_source(textview_curses& log_view,
|
||||
logfile_sub_source& lss,
|
||||
plain_text_source& preview_source,
|
||||
preview_status_source& preview_status_source);
|
||||
|
||||
size_t text_line_count() override;
|
||||
|
||||
size_t text_line_width(textview_curses& curses) override;
|
||||
|
||||
void text_value_for_line(textview_curses& tc,
|
||||
int line,
|
||||
std::string& value_out,
|
||||
line_flags_t flags) override;
|
||||
|
||||
void text_attrs_for_line(textview_curses& tc,
|
||||
int line,
|
||||
string_attrs_t& value_out) override;
|
||||
|
||||
size_t text_size_for_line(textview_curses& tc,
|
||||
int line,
|
||||
line_flags_t raw) override;
|
||||
|
||||
void text_selection_changed(textview_curses& tc) override;
|
||||
|
||||
nonstd::optional<vis_line_t> row_for_time(
|
||||
struct timeval time_bucket) override;
|
||||
nonstd::optional<struct timeval> time_for_row(vis_line_t row) override;
|
||||
|
||||
void rebuild_indexes();
|
||||
|
||||
std::pair<timeval, timeval> get_time_bounds_for(int line);
|
||||
|
||||
textview_curses& gs_log_view;
|
||||
logfile_sub_source& gs_lss;
|
||||
plain_text_source& gs_preview_source;
|
||||
preview_status_source& gs_preview_status_source;
|
||||
ArenaAlloc::Alloc<char> gs_allocator{64 * 1024};
|
||||
|
||||
struct opid_description_defs {};
|
||||
|
||||
using gantt_opid_map
|
||||
= robin_hood::unordered_map<string_fragment,
|
||||
opid_description_defs,
|
||||
frag_hasher,
|
||||
std::equal_to<string_fragment>>;
|
||||
|
||||
gantt_opid_map gs_opid_map;
|
||||
|
||||
struct opid_row {
|
||||
string_fragment or_name;
|
||||
opid_time_range or_value;
|
||||
};
|
||||
|
||||
attr_line_t gs_rendered_line;
|
||||
size_t gs_opid_width{0};
|
||||
size_t gs_total_width{0};
|
||||
std::vector<opid_row> gs_time_order;
|
||||
struct timeval gs_lower_bound {};
|
||||
struct timeval gs_upper_bound {};
|
||||
};
|
||||
|
||||
class gantt_header_overlay : public list_overlay_source {
|
||||
public:
|
||||
gantt_header_overlay(std::shared_ptr<gantt_source> src);
|
||||
|
||||
bool list_static_overlay(const listview_curses& lv,
|
||||
int y,
|
||||
int bottom,
|
||||
attr_line_t& value_out) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<gantt_source> gho_src;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue