mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[spectro] add a summary overlay to the selected position and show log messages in a panel
This commit is contained in:
parent
cec6c3a225
commit
52fe2db376
6
NEWS
6
NEWS
@ -18,6 +18,12 @@ lnav v0.10.2:
|
||||
(activated by the -m option), a log format can be created from
|
||||
a regular expression entry on regex101.com and existing patterns
|
||||
can be edited.
|
||||
* In the spectrogram view, the selected value range is now shown by
|
||||
an overlay that includes a summary of the range and the number of
|
||||
values that fall in that range. There is also a detail panel at
|
||||
the bottom that shows the log-messages/DB-rows whose values are in
|
||||
that range. You can then press TAB to focus on the detail view
|
||||
and scroll around.
|
||||
* Add initial support for pcap(3) files using tshark(1).
|
||||
* To make it possible to automate some operations, there is now an
|
||||
"lnav_events" table that is updated when internal events occur
|
||||
|
@ -51,6 +51,8 @@ set(TIME_FORMATS
|
||||
"%a %b %d %H:%M:%S %Z %Y"
|
||||
"%a %b %d %H:%M:%S "
|
||||
"%a %b %d %H:%M:%S.%L "
|
||||
"%a %b %d %H:%M "
|
||||
"%a %b %e %H:%M:%S %Z %Y"
|
||||
"%d/%b/%Y:%H:%M:%S +0000"
|
||||
"%d/%b/%Y:%H:%M:%S %z"
|
||||
"%d-%b-%Y %H:%M:%S %z"
|
||||
@ -328,6 +330,8 @@ add_library(
|
||||
string-extension-functions.cc
|
||||
sysclip.cc
|
||||
piper_proc.cc
|
||||
pollable.cc
|
||||
spectro_impls.cc
|
||||
spectro_source.cc
|
||||
sql_commands.cc
|
||||
sql_util.cc
|
||||
@ -415,6 +419,7 @@ add_library(
|
||||
papertrail_proc.hh
|
||||
pcap_manager.hh
|
||||
plain_text_source.hh
|
||||
pollable.hh
|
||||
pretty_printer.hh
|
||||
preview_status_source.hh
|
||||
pugixml/pugiconfig.hpp
|
||||
@ -436,6 +441,7 @@ add_library(
|
||||
shlex.hh
|
||||
shlex.resolver.hh
|
||||
simdutf8check.h
|
||||
spectro_impls.hh
|
||||
spectro_source.hh
|
||||
sqlitepp.hh
|
||||
sql_help.hh
|
||||
|
@ -258,6 +258,7 @@ noinst_HEADERS = \
|
||||
pcap_manager.hh \
|
||||
piper_proc.hh \
|
||||
plain_text_source.hh \
|
||||
pollable.hh \
|
||||
pretty_printer.hh \
|
||||
preview_status_source.hh \
|
||||
ptimec.hh \
|
||||
@ -281,6 +282,7 @@ noinst_HEADERS = \
|
||||
shlex.hh \
|
||||
shlex.resolver.hh \
|
||||
simdutf8check.h \
|
||||
spectro_impls.hh \
|
||||
spectro_source.hh \
|
||||
sqlitepp.hh \
|
||||
sql_help.hh \
|
||||
@ -422,6 +424,7 @@ libdiag_a_SOURCES = \
|
||||
papertrail_proc.cc \
|
||||
pcap_manager.cc \
|
||||
plain_text_source.cc \
|
||||
pollable.cc \
|
||||
pretty_printer.cc \
|
||||
ptimec_rt.cc \
|
||||
readline_callbacks.cc \
|
||||
@ -435,6 +438,7 @@ libdiag_a_SOURCES = \
|
||||
session_data.cc \
|
||||
shared_buffer.cc \
|
||||
shlex.cc \
|
||||
spectro_impls.cc \
|
||||
spectro_source.cc \
|
||||
sqlite-extension-func.cc \
|
||||
statusview_curses.cc \
|
||||
|
@ -30,6 +30,7 @@ add_library(
|
||||
auto_fd.hh
|
||||
auto_mem.hh
|
||||
auto_pid.hh
|
||||
bus.hh
|
||||
date_time_scanner.hh
|
||||
enum_util.hh
|
||||
fs_util.hh
|
||||
|
@ -25,6 +25,7 @@ noinst_HEADERS = \
|
||||
auto_fd.hh \
|
||||
auto_mem.hh \
|
||||
auto_pid.hh \
|
||||
bus.hh \
|
||||
date_time_scanner.hh \
|
||||
enum_util.hh \
|
||||
file_range.hh \
|
||||
|
64
src/base/bus.hh
Normal file
64
src/base/bus.hh
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2022, 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_bus_hh
|
||||
#define lnav_bus_hh
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "lnav_log.hh"
|
||||
|
||||
template<typename T>
|
||||
class bus {
|
||||
public:
|
||||
bus() = default;
|
||||
|
||||
virtual ~bus() { require(this->b_components.empty()); }
|
||||
|
||||
bus(const bus<T>&) = delete;
|
||||
|
||||
void attach(T* component)
|
||||
{
|
||||
this->b_components.template emplace_back(component);
|
||||
}
|
||||
|
||||
void detach(T* component)
|
||||
{
|
||||
auto iter = std::find(
|
||||
this->b_components.begin(), this->b_components.end(), component);
|
||||
require(iter != this->b_components.end());
|
||||
|
||||
this->b_components.erase(iter);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<T*> b_components;
|
||||
};
|
||||
|
||||
#endif
|
@ -65,11 +65,28 @@ create_factory() noexcept
|
||||
|
||||
template<typename T, typename... Annotations>
|
||||
struct bind : singleton_storage<T, Annotations...> {
|
||||
template<typename I = T,
|
||||
std::enable_if_t<has_injectable<I>::value, bool> = true>
|
||||
static bool to_singleton() noexcept
|
||||
{
|
||||
static T storage;
|
||||
typename I::injectable* i = nullptr;
|
||||
singleton_storage<T, Annotations...>::ss_owner
|
||||
= create_from_injectable<I>(i)();
|
||||
singleton_storage<T, Annotations...>::ss_data
|
||||
= singleton_storage<T, Annotations...>::ss_owner.get();
|
||||
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
|
||||
|
||||
singleton_storage<T, Annotations...>::ss_data = &storage;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename I = T,
|
||||
std::enable_if_t<!has_injectable<I>::value, bool> = true>
|
||||
static bool to_singleton() noexcept
|
||||
{
|
||||
singleton_storage<T, Annotations...>::ss_owner = std::make_shared<T>();
|
||||
singleton_storage<T, Annotations...>::ss_data
|
||||
= singleton_storage<T, Annotations...>::ss_owner.get();
|
||||
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -78,12 +95,14 @@ struct bind : singleton_storage<T, Annotations...> {
|
||||
{
|
||||
singleton_storage<T, Annotations...>::ss_data
|
||||
= f(::injector::get<Args>()...);
|
||||
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool to_instance(T* data) noexcept
|
||||
{
|
||||
singleton_storage<T, Annotations...>::ss_data = data;
|
||||
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -92,6 +111,7 @@ struct bind : singleton_storage<T, Annotations...> {
|
||||
{
|
||||
singleton_storage<T, Annotations...>::ss_factory
|
||||
= details::create_factory<I>();
|
||||
singleton_storage<T, Annotations...>::ss_scope = scope::none;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -43,6 +43,12 @@
|
||||
|
||||
namespace injector {
|
||||
|
||||
enum class scope {
|
||||
undefined,
|
||||
none,
|
||||
singleton,
|
||||
};
|
||||
|
||||
template<typename Annotation>
|
||||
void force_linking(Annotation anno);
|
||||
|
||||
@ -63,10 +69,16 @@ struct singleton_storage {
|
||||
{
|
||||
static int _[] = {0, (force_linking(Annotations{}), 0)...};
|
||||
(void) _;
|
||||
assert(ss_data != nullptr);
|
||||
return ss_data;
|
||||
}
|
||||
|
||||
static std::shared_ptr<T> get_owner()
|
||||
{
|
||||
static int _[] = {0, (force_linking(Annotations{}), 0)...};
|
||||
(void) _;
|
||||
return ss_owner;
|
||||
}
|
||||
|
||||
static std::shared_ptr<T> create()
|
||||
{
|
||||
static int _[] = {0, (force_linking(Annotations{}), 0)...};
|
||||
@ -75,13 +87,21 @@ struct singleton_storage {
|
||||
}
|
||||
|
||||
protected:
|
||||
static scope ss_scope;
|
||||
static T* ss_data;
|
||||
static std::shared_ptr<T> ss_owner;
|
||||
static std::function<std::shared_ptr<T>()> ss_factory;
|
||||
};
|
||||
|
||||
template<typename T, typename... Annotations>
|
||||
T* singleton_storage<T, Annotations...>::ss_data = nullptr;
|
||||
|
||||
template<typename T, typename... Annotations>
|
||||
scope singleton_storage<T, Annotations...>::ss_scope = scope::undefined;
|
||||
|
||||
template<typename T, typename... Annotations>
|
||||
std::shared_ptr<T> singleton_storage<T, Annotations...>::ss_owner;
|
||||
|
||||
template<typename T, typename... Annotations>
|
||||
std::function<std::shared_ptr<T>()>
|
||||
singleton_storage<T, Annotations...>::ss_factory;
|
||||
@ -153,14 +173,33 @@ template<class T>
|
||||
struct is_vector<std::vector<T>> : std::true_type {
|
||||
};
|
||||
|
||||
template<typename I, typename R, typename... IArgs, typename... Args>
|
||||
std::function<std::shared_ptr<I>()> create_from_injectable(R (*)(IArgs...),
|
||||
Args&... args);
|
||||
|
||||
template<typename T,
|
||||
typename... Args,
|
||||
std::enable_if_t<has_injectable<typename T::element_type>::value,
|
||||
bool> = true,
|
||||
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
|
||||
T
|
||||
get(Args&... args)
|
||||
{
|
||||
typename T::element_type::injectable* i = nullptr;
|
||||
|
||||
return create_from_injectable<typename T::element_type>(i, args...)();
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename... Annotations,
|
||||
std::enable_if_t<!has_injectable<typename T::element_type>::value,
|
||||
bool> = true,
|
||||
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
|
||||
T
|
||||
get()
|
||||
{
|
||||
return singleton_storage<typename T::element_type,
|
||||
Annotations...>::create();
|
||||
Annotations...>::get_owner();
|
||||
}
|
||||
|
||||
template<typename T, std::enable_if_t<is_vector<T>::value, bool> = true>
|
||||
@ -170,6 +209,15 @@ get()
|
||||
return multiple_storage<typename T::value_type::element_type>::create();
|
||||
}
|
||||
|
||||
template<typename I, typename R, typename... IArgs, typename... Args>
|
||||
std::function<std::shared_ptr<I>()>
|
||||
create_from_injectable(R (*)(IArgs...), Args&... args)
|
||||
{
|
||||
return [&]() {
|
||||
return std::make_shared<I>(args..., ::injector::get<IArgs>()...);
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace injector
|
||||
|
||||
#endif
|
||||
|
@ -115,7 +115,13 @@ const char* lnav_log_crash_dir;
|
||||
nonstd::optional<const struct termios*> lnav_log_orig_termios;
|
||||
// NOTE: This mutex is leaked so that it is not destroyed during exit.
|
||||
// Otherwise, any attempts to log will fail.
|
||||
static std::mutex* lnav_log_mutex = new std::mutex();
|
||||
static std::mutex*
|
||||
lnav_log_mutex()
|
||||
{
|
||||
static auto* retval = new std::mutex();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static std::vector<log_state_dumper*>&
|
||||
DUMPER_LIST()
|
||||
@ -306,7 +312,7 @@ log_msg(lnav_log_level_t level,
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> log_lock(*lnav_log_mutex);
|
||||
std::lock_guard<std::mutex> log_lock(*lnav_log_mutex());
|
||||
|
||||
{
|
||||
// get the base name of the file. NB: can't use basename() since it
|
||||
@ -365,7 +371,7 @@ log_msg(lnav_log_level_t level,
|
||||
void
|
||||
log_msg_extra(const char* fmt, ...)
|
||||
{
|
||||
std::lock_guard<std::mutex> mg(*lnav_log_mutex);
|
||||
std::lock_guard<std::mutex> mg(*lnav_log_mutex());
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
@ -382,7 +388,7 @@ log_msg_extra(const char* fmt, ...)
|
||||
void
|
||||
log_msg_extra_complete()
|
||||
{
|
||||
std::lock_guard<std::mutex> mg(*lnav_log_mutex);
|
||||
std::lock_guard<std::mutex> mg(*lnav_log_mutex());
|
||||
auto line = log_alloc();
|
||||
line[0] = '\n';
|
||||
log_ring.lr_length += 1;
|
||||
|
@ -63,4 +63,11 @@ roundup_size(size_t size, int step)
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
abs_diff(T a, T b)
|
||||
{
|
||||
return a > b ? a - b : b - a;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "base/opt_util.hh"
|
||||
#include "config.h"
|
||||
#include "files_sub_source.hh"
|
||||
#include "filter_sub_source.hh"
|
||||
#include "lnav.hh"
|
||||
|
||||
static auto TOGGLE_MSG = "Press " ANSI_BOLD("TAB") " to edit ";
|
||||
@ -246,11 +247,11 @@ filter_help_status_source::statusview_fields()
|
||||
}
|
||||
|
||||
if (lnav_data.ld_mode == ln_mode_t::FILTER) {
|
||||
auto& editor = lnav_data.ld_filter_source;
|
||||
static auto* editor = injector::get<filter_sub_source*>();
|
||||
auto& lv = lnav_data.ld_filter_view;
|
||||
auto& fs = tss->get_filters();
|
||||
|
||||
if (editor.fss_editing) {
|
||||
if (editor->fss_editing) {
|
||||
auto tf = *(fs.begin() + lv.get_selection());
|
||||
auto lang = tf->get_lang() == filter_lang_t::SQL ? "an SQL"
|
||||
: "a regular";
|
||||
|
@ -37,23 +37,25 @@
|
||||
#include "readline_highlighters.hh"
|
||||
#include "readline_possibilities.hh"
|
||||
|
||||
filter_sub_source::filter_sub_source()
|
||||
filter_sub_source::filter_sub_source(std::shared_ptr<readline_curses> editor)
|
||||
: fss_editor(editor)
|
||||
{
|
||||
this->fss_regex_context.set_highlighter(readline_regex_highlighter)
|
||||
.set_append_character(0);
|
||||
this->fss_editor.add_context(filter_lang_t::REGEX, this->fss_regex_context);
|
||||
this->fss_editor->add_context(filter_lang_t::REGEX,
|
||||
this->fss_regex_context);
|
||||
this->fss_sql_context.set_highlighter(readline_sqlite_highlighter)
|
||||
.set_append_character(0);
|
||||
this->fss_editor.add_context(filter_lang_t::SQL, this->fss_sql_context);
|
||||
this->fss_editor.set_change_action(
|
||||
this->fss_editor->add_context(filter_lang_t::SQL, this->fss_sql_context);
|
||||
this->fss_editor->set_change_action(
|
||||
bind_mem(&filter_sub_source::rl_change, this));
|
||||
this->fss_editor.set_perform_action(
|
||||
this->fss_editor->set_perform_action(
|
||||
bind_mem(&filter_sub_source::rl_perform, this));
|
||||
this->fss_editor.set_abort_action(
|
||||
this->fss_editor->set_abort_action(
|
||||
bind_mem(&filter_sub_source::rl_abort, this));
|
||||
this->fss_editor.set_display_match_action(
|
||||
this->fss_editor->set_display_match_action(
|
||||
bind_mem(&filter_sub_source::rl_display_matches, this));
|
||||
this->fss_editor.set_display_next_action(
|
||||
this->fss_editor->set_display_next_action(
|
||||
bind_mem(&filter_sub_source::rl_display_next, this));
|
||||
this->fss_match_view.set_sub_source(&this->fss_match_source);
|
||||
this->fss_match_view.set_height(0_vl);
|
||||
@ -67,10 +69,10 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
if (this->fss_editing) {
|
||||
switch (ch) {
|
||||
case KEY_CTRL_RBRACKET:
|
||||
this->fss_editor.abort();
|
||||
this->fss_editor->abort();
|
||||
return true;
|
||||
default:
|
||||
this->fss_editor.handle_key(ch);
|
||||
this->fss_editor->handle_key(ch);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -156,26 +158,26 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
|
||||
this->fss_editing = true;
|
||||
|
||||
add_view_text_possibilities(&this->fss_editor,
|
||||
add_view_text_possibilities(this->fss_editor.get(),
|
||||
filter_lang_t::REGEX,
|
||||
"*",
|
||||
top_view,
|
||||
text_quoting::regex);
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y()
|
||||
+ (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(25);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
|
||||
this->fss_editor->set_window(lv.get_window());
|
||||
this->fss_editor->set_visible(true);
|
||||
this->fss_editor->set_y(
|
||||
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor->set_left(25);
|
||||
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor->focus(filter_lang_t::REGEX, "", "");
|
||||
this->fss_filter_state = true;
|
||||
ef->disable();
|
||||
return true;
|
||||
}
|
||||
case 'o': {
|
||||
textview_curses* top_view = *lnav_data.ld_view_stack.top();
|
||||
text_sub_source* tss = top_view->get_sub_source();
|
||||
filter_stack& fs = tss->get_filters();
|
||||
auto* top_view = *lnav_data.ld_view_stack.top();
|
||||
auto* tss = top_view->get_sub_source();
|
||||
auto& fs = tss->get_filters();
|
||||
auto filter_index = fs.next_index();
|
||||
|
||||
if (!filter_index) {
|
||||
@ -192,18 +194,18 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
|
||||
this->fss_editing = true;
|
||||
|
||||
add_view_text_possibilities(&this->fss_editor,
|
||||
add_view_text_possibilities(this->fss_editor.get(),
|
||||
filter_lang_t::REGEX,
|
||||
"*",
|
||||
top_view,
|
||||
text_quoting::regex);
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y()
|
||||
+ (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(25);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
|
||||
this->fss_editor->set_window(lv.get_window());
|
||||
this->fss_editor->set_visible(true);
|
||||
this->fss_editor->set_y(
|
||||
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor->set_left(25);
|
||||
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor->focus(filter_lang_t::REGEX, "", "");
|
||||
this->fss_filter_state = true;
|
||||
ef->disable();
|
||||
return true;
|
||||
@ -226,17 +228,17 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
? text_quoting::sql
|
||||
: text_quoting::regex;
|
||||
add_view_text_possibilities(
|
||||
&this->fss_editor, tf->get_lang(), "*", top_view, tq);
|
||||
this->fss_editor.get(), tf->get_lang(), "*", top_view, tq);
|
||||
add_filter_expr_possibilities(
|
||||
&this->fss_editor, filter_lang_t::SQL, "*");
|
||||
this->fss_editor.set_window(lv.get_window());
|
||||
this->fss_editor.set_visible(true);
|
||||
this->fss_editor.set_y(lv.get_y()
|
||||
+ (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor.set_left(25);
|
||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor.focus(tf->get_lang(), "");
|
||||
this->fss_editor.rewrite_line(0, tf->get_id().c_str());
|
||||
this->fss_editor.get(), filter_lang_t::SQL, "*");
|
||||
this->fss_editor->set_window(lv.get_window());
|
||||
this->fss_editor->set_visible(true);
|
||||
this->fss_editor->set_y(
|
||||
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
|
||||
this->fss_editor->set_left(25);
|
||||
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
|
||||
this->fss_editor->focus(tf->get_lang(), "");
|
||||
this->fss_editor->rewrite_line(0, tf->get_id().c_str());
|
||||
this->fss_filter_state = tf->is_enabled();
|
||||
tf->disable();
|
||||
tss->text_filters_changed();
|
||||
@ -565,7 +567,7 @@ filter_sub_source::rl_perform(readline_curses* rc)
|
||||
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
|
||||
lnav_data.ld_filter_help_status_source.fss_prompt.clear();
|
||||
this->fss_editing = false;
|
||||
this->fss_editor.set_visible(false);
|
||||
this->fss_editor->set_visible(false);
|
||||
this->tss_view->reload_data();
|
||||
}
|
||||
|
||||
@ -585,7 +587,7 @@ filter_sub_source::rl_abort(readline_curses* rc)
|
||||
top_view->reload_data();
|
||||
fs.delete_filter("");
|
||||
this->tss_view->reload_data();
|
||||
this->fss_editor.set_visible(false);
|
||||
this->fss_editor->set_visible(false);
|
||||
this->fss_editing = false;
|
||||
this->tss_view->set_needs_update();
|
||||
tf->set_enabled(this->fss_filter_state);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#ifndef filter_sub_source_hh
|
||||
#define filter_sub_source_hh
|
||||
|
||||
#include "base/injector.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "readline_curses.hh"
|
||||
#include "textview_curses.hh"
|
||||
@ -38,7 +39,12 @@ class filter_sub_source
|
||||
: public text_sub_source
|
||||
, public list_input_delegate {
|
||||
public:
|
||||
filter_sub_source();
|
||||
filter_sub_source(std::shared_ptr<readline_curses> editor);
|
||||
|
||||
using injectable
|
||||
= filter_sub_source(std::shared_ptr<readline_curses> editor);
|
||||
|
||||
filter_sub_source(const filter_sub_source*) = delete;
|
||||
|
||||
~filter_sub_source() override = default;
|
||||
|
||||
@ -75,7 +81,7 @@ public:
|
||||
|
||||
readline_context fss_regex_context{"filter-regex", nullptr, false};
|
||||
readline_context fss_sql_context{"filter-sql", nullptr, false};
|
||||
readline_curses fss_editor;
|
||||
std::shared_ptr<readline_curses> fss_editor;
|
||||
plain_text_source fss_match_source;
|
||||
textview_curses fss_match_view;
|
||||
|
||||
|
@ -47,8 +47,11 @@
|
||||
#include "vis_line.hh"
|
||||
|
||||
template<typename LineType>
|
||||
grep_proc<LineType>::grep_proc(pcre* code, grep_proc_source<LineType>& gps)
|
||||
: gp_pcre(code), gp_source(gps)
|
||||
grep_proc<LineType>::grep_proc(pcre* code,
|
||||
grep_proc_source<LineType>& gps,
|
||||
std::shared_ptr<pollable_supervisor> ps)
|
||||
: pollable(ps, pollable::category::background), gp_pcre(code),
|
||||
gp_source(gps)
|
||||
{
|
||||
require(this->invariant());
|
||||
|
||||
@ -88,7 +91,9 @@ grep_proc<LineType>::start()
|
||||
{
|
||||
require(this->invariant());
|
||||
|
||||
log_debug("grep_proc(%p): start", this);
|
||||
if (this->gp_child_started || this->gp_queue.empty()) {
|
||||
log_debug("grep_proc(%p): nothing to do?", this);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,6 +131,8 @@ grep_proc<LineType>::start()
|
||||
this->gp_child_queue_size = this->gp_queue.size();
|
||||
|
||||
this->gp_queue.clear();
|
||||
|
||||
log_debug("grep_proc(%p): started child %d", this, this->gp_child);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -402,4 +409,17 @@ grep_proc<LineType>::invalidate()
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename LineType>
|
||||
void
|
||||
grep_proc<LineType>::update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
if (this->gp_line_buffer.get_fd() != -1) {
|
||||
pollfds.push_back(
|
||||
(struct pollfd){this->gp_line_buffer.get_fd(), POLLIN, 0});
|
||||
}
|
||||
if (this->gp_err_pipe != -1) {
|
||||
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
|
||||
}
|
||||
}
|
||||
|
||||
template class grep_proc<vis_line_t>;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "base/lnav_log.hh"
|
||||
#include "line_buffer.hh"
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "pollable.hh"
|
||||
#include "strong_int.hh"
|
||||
|
||||
template<typename LineType>
|
||||
@ -159,7 +160,7 @@ public:
|
||||
* library directly.
|
||||
*/
|
||||
template<typename LineType>
|
||||
class grep_proc {
|
||||
class grep_proc : public pollable {
|
||||
public:
|
||||
class error : public std::exception {
|
||||
public:
|
||||
@ -175,16 +176,22 @@ public:
|
||||
* @param code The pcre code to run over the lines of input.
|
||||
* @param gps The source of the data to match.
|
||||
*/
|
||||
grep_proc(pcre* code, grep_proc_source<LineType>& gps);
|
||||
grep_proc(pcre* code,
|
||||
grep_proc_source<LineType>& gps,
|
||||
std::shared_ptr<pollable_supervisor> ps);
|
||||
|
||||
grep_proc(std::shared_ptr<pollable_supervisor>);
|
||||
|
||||
using injectable = grep_proc(std::shared_ptr<pollable_supervisor>);
|
||||
|
||||
virtual ~grep_proc();
|
||||
|
||||
/** @param gpd The sink to send resuls to. */
|
||||
/** @param gpd The sink to send results to. */
|
||||
void set_sink(grep_proc_sink<LineType>* gpd) { this->gp_sink = gpd; }
|
||||
|
||||
grep_proc& invalidate();
|
||||
|
||||
/** @param gpd The sink to send results to. */
|
||||
/** @param gpc The control to send results to. */
|
||||
void set_control(grep_proc_control* gpc) { this->gp_control = gpc; }
|
||||
|
||||
/** @return The sink to send results to. */
|
||||
@ -216,23 +223,14 @@ public:
|
||||
*/
|
||||
void start();
|
||||
|
||||
void update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
if (this->gp_line_buffer.get_fd() != -1) {
|
||||
pollfds.push_back(
|
||||
(struct pollfd){this->gp_line_buffer.get_fd(), POLLIN, 0});
|
||||
}
|
||||
if (this->gp_err_pipe != -1) {
|
||||
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
|
||||
}
|
||||
}
|
||||
void update_poll_set(std::vector<struct pollfd>& pollfds) override;
|
||||
|
||||
/**
|
||||
* Check the fd_set to see if there is any new data to be processed.
|
||||
*
|
||||
* @param ready_rfds The set of ready-to-read file descriptors.
|
||||
*/
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds);
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds) override;
|
||||
|
||||
/** Check the invariants for this object. */
|
||||
bool invariant()
|
||||
|
@ -828,6 +828,8 @@ handle_paging_key(int ch)
|
||||
});
|
||||
|
||||
tc->reload_data();
|
||||
} else if (tc == &lnav_data.ld_views[LNV_SPECTRO]) {
|
||||
lnav_data.ld_mode = ln_mode_t::SPECTRO_DETAILS;
|
||||
} else if (tc_tss != nullptr && tc_tss->tss_supports_filtering) {
|
||||
lnav_data.ld_mode = lnav_data.ld_last_config_mode;
|
||||
lnav_data.ld_filter_view.reload_data();
|
||||
|
@ -54,10 +54,17 @@ listview_curses::reload_data()
|
||||
this->lv_top
|
||||
= std::max(0_vl, vis_line_t(this->get_inner_height() - 1));
|
||||
}
|
||||
if (this->get_inner_height() == 0) {
|
||||
this->lv_selection = 0_vl;
|
||||
} else if (this->lv_selection >= this->get_inner_height()) {
|
||||
this->lv_selection = this->get_inner_height() - 1_vl;
|
||||
if (this->lv_selectable) {
|
||||
if (this->get_inner_height() == 0) {
|
||||
this->lv_selection = 0_vl;
|
||||
} else if (this->lv_selection >= this->get_inner_height()) {
|
||||
this->set_selection(this->get_inner_height() - 1_vl);
|
||||
} else {
|
||||
auto curr_sel = this->lv_selection;
|
||||
|
||||
this->lv_selection = -1_vl;
|
||||
this->set_selection(curr_sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->vc_needs_update = true;
|
||||
@ -482,8 +489,8 @@ listview_curses::set_top(vis_line_t top, bool suppress_flash)
|
||||
auto bot = this->get_bottom();
|
||||
|
||||
if (bot != -1_vl) {
|
||||
if (this->lv_selection > bot) {
|
||||
this->lv_selection = bot;
|
||||
if (this->lv_selection > (bot - this->lv_tail_space)) {
|
||||
this->lv_selection = bot - this->lv_tail_space;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -559,9 +566,48 @@ listview_curses::scroll_selection_into_view()
|
||||
}
|
||||
if (this->lv_selection < 0_vl) {
|
||||
this->set_top(0_vl);
|
||||
} else if (this->lv_selection >= (this->lv_top + height - 1)) {
|
||||
this->set_top(this->lv_selection - height + 2_vl, true);
|
||||
} else if (this->lv_selection
|
||||
>= (this->lv_top + height - this->lv_tail_space)) {
|
||||
this->set_top(this->lv_selection - height + 1_vl + this->lv_tail_space,
|
||||
true);
|
||||
} else if (this->lv_selection < this->lv_top) {
|
||||
this->set_top(this->lv_selection, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
listview_curses::set_selection(vis_line_t sel)
|
||||
{
|
||||
if (this->lv_selectable) {
|
||||
auto inner_height = this->get_inner_height();
|
||||
if (this->lv_selection != sel && sel >= 0_vl && sel < inner_height) {
|
||||
auto found = false;
|
||||
vis_line_t step;
|
||||
|
||||
if (sel == 0_vl) {
|
||||
step = 1_vl;
|
||||
} else if (sel == inner_height - 1_vl) {
|
||||
step = -1_vl;
|
||||
} else if (sel < this->lv_selection) {
|
||||
step = -1_vl;
|
||||
} else {
|
||||
step = 1_vl;
|
||||
}
|
||||
while (sel < inner_height) {
|
||||
if (this->lv_source->listview_is_row_selectable(*this, sel)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
sel += step;
|
||||
}
|
||||
if (found) {
|
||||
this->lv_selection = sel;
|
||||
this->scroll_selection_into_view();
|
||||
this->lv_source->listview_selection_changed(*this);
|
||||
this->set_needs_update();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this->set_top(sel);
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,14 @@ public:
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual bool listview_is_row_selectable(const listview_curses& lv,
|
||||
vis_line_t row)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void listview_selection_changed(const listview_curses& lv) {}
|
||||
};
|
||||
|
||||
class list_gutter_source {
|
||||
@ -194,14 +202,7 @@ public:
|
||||
|
||||
bool is_selectable() const { return this->lv_selectable; }
|
||||
|
||||
void set_selection(vis_line_t sel)
|
||||
{
|
||||
if (this->lv_selection != sel) {
|
||||
this->lv_selection = sel;
|
||||
this->scroll_selection_into_view();
|
||||
this->set_needs_update();
|
||||
}
|
||||
}
|
||||
void set_selection(vis_line_t sel);
|
||||
|
||||
void scroll_selection_into_view();
|
||||
|
||||
@ -209,7 +210,10 @@ public:
|
||||
|
||||
vis_line_t get_selection() const
|
||||
{
|
||||
return this->lv_selection;
|
||||
if (this->lv_selectable) {
|
||||
return this->lv_selection;
|
||||
}
|
||||
return this->lv_top;
|
||||
}
|
||||
|
||||
listview_curses& set_word_wrap(bool ww)
|
||||
@ -553,4 +557,5 @@ protected:
|
||||
lv_mode_t lv_mouse_mode{lv_mode_t::NONE};
|
||||
vis_line_t lv_tail_space{1};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
229
src/lnav.cc
229
src/lnav.cc
@ -93,6 +93,7 @@
|
||||
#include "CLI/CLI.hpp"
|
||||
#include "dump_internals.hh"
|
||||
#include "environ_vtab.hh"
|
||||
#include "filter_sub_source.hh"
|
||||
#include "fstat_vtab.hh"
|
||||
#include "grep_proc.hh"
|
||||
#include "help-txt.h"
|
||||
@ -119,6 +120,7 @@
|
||||
#include "regexp_vtab.hh"
|
||||
#include "service_tags.hh"
|
||||
#include "session_data.hh"
|
||||
#include "spectro_source.hh"
|
||||
#include "sql_help.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
@ -167,8 +169,6 @@ using namespace lnav::roles::literals;
|
||||
static std::vector<std::string> DEFAULT_FILES;
|
||||
static auto intern_lifetime = intern_string::get_table_lifetime();
|
||||
|
||||
struct lnav_data_t lnav_data;
|
||||
|
||||
const int ZOOM_LEVELS[] = {
|
||||
1,
|
||||
30,
|
||||
@ -217,6 +217,12 @@ static const std::vector<std::string> DEFAULT_DB_KEY_NAMES = {
|
||||
|
||||
const static size_t MAX_STDIN_CAPTURE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
static auto bound_pollable_supervisor
|
||||
= injector::bind<pollable_supervisor>::to_singleton();
|
||||
|
||||
static auto bound_filter_sub_source
|
||||
= injector::bind<filter_sub_source>::to_singleton();
|
||||
|
||||
static auto bound_active_files = injector::bind<file_collection>::to_instance(
|
||||
+[]() { return &lnav_data.ld_active_files; });
|
||||
|
||||
@ -278,6 +284,8 @@ force_linking(services::main_t anno)
|
||||
|
||||
static breadcrumb_curses breadcrumb_view;
|
||||
|
||||
struct lnav_data_t lnav_data;
|
||||
|
||||
bool
|
||||
setup_logline_table(exec_context& ec)
|
||||
{
|
||||
@ -848,10 +856,39 @@ handle_key(int ch)
|
||||
case ln_mode_t::FILES:
|
||||
return handle_config_ui_key(ch);
|
||||
|
||||
case ln_mode_t::SPECTRO_DETAILS: {
|
||||
if (ch == '\t' || ch == 'q') {
|
||||
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||
return true;
|
||||
}
|
||||
if (lnav_data.ld_spectro_details_view.handle_key(ch)) {
|
||||
return true;
|
||||
}
|
||||
switch (ch) {
|
||||
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-spectro-details");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case ln_mode_t::COMMAND:
|
||||
case ln_mode_t::SEARCH:
|
||||
case ln_mode_t::SEARCH_FILTERS:
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
case ln_mode_t::CAPTURE:
|
||||
case ln_mode_t::SQL:
|
||||
case ln_mode_t::EXEC:
|
||||
@ -946,6 +983,9 @@ wait_for_pipers()
|
||||
static void
|
||||
looper()
|
||||
{
|
||||
static auto* ps = injector::get<pollable_supervisor*>();
|
||||
static auto* filter_source = injector::get<filter_sub_source*>();
|
||||
|
||||
try {
|
||||
auto_fd errpipe[2];
|
||||
auto_fd::pipe(errpipe);
|
||||
@ -964,11 +1004,13 @@ looper()
|
||||
readline_context search_filters_context(
|
||||
"search-filters", nullptr, false);
|
||||
readline_context search_files_context("search-files", nullptr, false);
|
||||
readline_context search_spectro_details_context(
|
||||
"search-spectro-details", nullptr, false);
|
||||
readline_context index_context("capture");
|
||||
readline_context sql_context("sql", sql_cmd_map, false);
|
||||
readline_context exec_context("exec");
|
||||
readline_context user_context("user");
|
||||
readline_curses rlc;
|
||||
auto rlc = injector::get<std::shared_ptr<readline_curses>>();
|
||||
sig_atomic_t overlay_counter = 0;
|
||||
int lpc;
|
||||
|
||||
@ -979,6 +1021,8 @@ looper()
|
||||
readline_regex_highlighter);
|
||||
search_files_context.set_append_character(0).set_highlighter(
|
||||
readline_regex_highlighter);
|
||||
search_spectro_details_context.set_append_character(0).set_highlighter(
|
||||
readline_regex_highlighter);
|
||||
sql_context.set_highlighter(readline_sqlite_highlighter)
|
||||
.set_quote_chars("\"")
|
||||
.with_readline_var((char**) &rl_completer_word_break_characters,
|
||||
@ -994,19 +1038,21 @@ looper()
|
||||
auto& sb = lnav_data.ld_scroll_broadcaster;
|
||||
auto& vsb = lnav_data.ld_view_stack_broadcaster;
|
||||
|
||||
rlc.add_context(ln_mode_t::COMMAND, command_context);
|
||||
rlc.add_context(ln_mode_t::SEARCH, search_context);
|
||||
rlc.add_context(ln_mode_t::SEARCH_FILTERS, search_filters_context);
|
||||
rlc.add_context(ln_mode_t::SEARCH_FILES, search_files_context);
|
||||
rlc.add_context(ln_mode_t::CAPTURE, index_context);
|
||||
rlc.add_context(ln_mode_t::SQL, sql_context);
|
||||
rlc.add_context(ln_mode_t::EXEC, exec_context);
|
||||
rlc.add_context(ln_mode_t::USER, user_context);
|
||||
rlc.start();
|
||||
rlc->add_context(ln_mode_t::COMMAND, command_context);
|
||||
rlc->add_context(ln_mode_t::SEARCH, search_context);
|
||||
rlc->add_context(ln_mode_t::SEARCH_FILTERS, search_filters_context);
|
||||
rlc->add_context(ln_mode_t::SEARCH_FILES, search_files_context);
|
||||
rlc->add_context(ln_mode_t::SEARCH_SPECTRO_DETAILS,
|
||||
search_spectro_details_context);
|
||||
rlc->add_context(ln_mode_t::CAPTURE, index_context);
|
||||
rlc->add_context(ln_mode_t::SQL, sql_context);
|
||||
rlc->add_context(ln_mode_t::EXEC, exec_context);
|
||||
rlc->add_context(ln_mode_t::USER, user_context);
|
||||
rlc->start();
|
||||
|
||||
lnav_data.ld_filter_source.fss_editor.start();
|
||||
filter_source->fss_editor->start();
|
||||
|
||||
lnav_data.ld_rl_view = &rlc;
|
||||
lnav_data.ld_rl_view = rlc.get();
|
||||
|
||||
lnav_data.ld_rl_view->add_possibility(
|
||||
ln_mode_t::COMMAND, "viewname", lnav_view_strings);
|
||||
@ -1088,19 +1134,19 @@ looper()
|
||||
|
||||
execute_examples();
|
||||
|
||||
rlc.set_window(lnav_data.ld_window);
|
||||
rlc.set_y(-1);
|
||||
rlc.set_focus_action(rl_focus);
|
||||
rlc.set_change_action(rl_change);
|
||||
rlc.set_perform_action(rl_callback);
|
||||
rlc.set_alt_perform_action(rl_alt_callback);
|
||||
rlc.set_timeout_action(rl_search);
|
||||
rlc.set_abort_action(lnav_rl_abort);
|
||||
rlc.set_display_match_action(rl_display_matches);
|
||||
rlc.set_display_next_action(rl_display_next);
|
||||
rlc.set_blur_action(rl_blur);
|
||||
rlc.set_completion_request_action(rl_completion_request);
|
||||
rlc.set_alt_value(HELP_MSG_2(
|
||||
rlc->set_window(lnav_data.ld_window);
|
||||
rlc->set_y(-1);
|
||||
rlc->set_focus_action(rl_focus);
|
||||
rlc->set_change_action(rl_change);
|
||||
rlc->set_perform_action(rl_callback);
|
||||
rlc->set_alt_perform_action(rl_alt_callback);
|
||||
rlc->set_timeout_action(rl_search);
|
||||
rlc->set_abort_action(lnav_rl_abort);
|
||||
rlc->set_display_match_action(rl_display_matches);
|
||||
rlc->set_display_next_action(rl_display_next);
|
||||
rlc->set_blur_action(rl_blur);
|
||||
rlc->set_completion_request_action(rl_completion_request);
|
||||
rlc->set_alt_value(HELP_MSG_2(
|
||||
e, E, "to move forward/backward through error messages"));
|
||||
|
||||
(void) curs_set(0);
|
||||
@ -1127,20 +1173,21 @@ looper()
|
||||
breadcrumb_view.set_y(0);
|
||||
breadcrumb_view.set_window(lnav_data.ld_window);
|
||||
breadcrumb_view.set_line_source(lnav_crumb_source);
|
||||
auto event_handler = [](auto&& tc) {
|
||||
auto top_view = lnav_data.ld_view_stack.top();
|
||||
|
||||
if (top_view && *top_view == &tc) {
|
||||
lnav_data.ld_bottom_source.update_search_term(tc);
|
||||
}
|
||||
};
|
||||
for (lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||
lnav_data.ld_views[lpc].set_window(lnav_data.ld_window);
|
||||
lnav_data.ld_views[lpc].set_y(1);
|
||||
lnav_data.ld_views[lpc].set_height(
|
||||
vis_line_t(-(rlc.get_height() + 1)));
|
||||
vis_line_t(-(rlc->get_height() + 1)));
|
||||
lnav_data.ld_views[lpc].set_scroll_action(sb);
|
||||
lnav_data.ld_views[lpc].set_search_action(update_hits);
|
||||
lnav_data.ld_views[lpc].tc_state_event_handler = [](auto&& tc) {
|
||||
auto top_view = lnav_data.ld_view_stack.top();
|
||||
|
||||
if (top_view && *top_view == &tc) {
|
||||
lnav_data.ld_bottom_source.update_search_term(tc);
|
||||
}
|
||||
};
|
||||
lnav_data.ld_views[lpc].tc_state_event_handler = event_handler;
|
||||
}
|
||||
|
||||
lnav_data.ld_doc_view.set_window(lnav_data.ld_window);
|
||||
@ -1167,7 +1214,25 @@ looper()
|
||||
|
||||
lnav_data.ld_user_message_view.set_window(lnav_data.ld_window);
|
||||
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
|
||||
lnav_data.ld_spectro_details_view.set_window(lnav_data.ld_window);
|
||||
lnav_data.ld_spectro_details_view.set_show_scrollbar(true);
|
||||
lnav_data.ld_spectro_details_view.set_height(5_vl);
|
||||
lnav_data.ld_spectro_details_view.set_sub_source(
|
||||
&lnav_data.ld_spectro_no_details_source);
|
||||
lnav_data.ld_spectro_details_view.tc_state_event_handler
|
||||
= event_handler;
|
||||
lnav_data.ld_spectro_details_view.set_scroll_action(sb);
|
||||
lnav_data.ld_spectro_no_details_source.replace_with(
|
||||
attr_line_t().append(
|
||||
lnav::roles::comment(" No details available")));
|
||||
lnav_data.ld_spectro_source->ss_details_view
|
||||
= &lnav_data.ld_spectro_details_view;
|
||||
lnav_data.ld_spectro_source->ss_no_details_source
|
||||
= &lnav_data.ld_spectro_no_details_source;
|
||||
lnav_data.ld_spectro_source->ss_exec_context
|
||||
= &lnav_data.ld_exec_context;
|
||||
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc->get_height() + 1));
|
||||
for (auto& sc : lnav_data.ld_status) {
|
||||
sc.set_window(lnav_data.ld_window);
|
||||
}
|
||||
@ -1181,6 +1246,10 @@ looper()
|
||||
&lnav_data.ld_doc_status_source);
|
||||
lnav_data.ld_status[LNS_PREVIEW].set_data_source(
|
||||
&lnav_data.ld_preview_status_source);
|
||||
lnav_data.ld_spectro_status_source
|
||||
= std::make_unique<spectro_status_source>();
|
||||
lnav_data.ld_status[LNS_SPECTRO].set_data_source(
|
||||
lnav_data.ld_spectro_status_source.get());
|
||||
|
||||
lnav_data.ld_match_view.set_show_bottom_border(true);
|
||||
lnav_data.ld_user_message_view.set_show_bottom_border(true);
|
||||
@ -1372,6 +1441,7 @@ looper()
|
||||
lnav_data.ld_example_view.do_update();
|
||||
lnav_data.ld_match_view.do_update();
|
||||
lnav_data.ld_preview_view.do_update();
|
||||
lnav_data.ld_spectro_details_view.do_update();
|
||||
lnav_data.ld_user_message_view.do_update();
|
||||
if (ui_clock::now() >= next_status_update_time) {
|
||||
for (auto& sc : lnav_data.ld_status) {
|
||||
@ -1379,8 +1449,8 @@ looper()
|
||||
}
|
||||
next_status_update_time = ui_clock::now() + 100ms;
|
||||
}
|
||||
if (lnav_data.ld_filter_source.fss_editing) {
|
||||
lnav_data.ld_filter_source.fss_match_view.set_needs_update();
|
||||
if (filter_source->fss_editing) {
|
||||
filter_source->fss_match_view.set_needs_update();
|
||||
}
|
||||
breadcrumb_view.do_update();
|
||||
// These updates need to be done last so their readline views can
|
||||
@ -1402,7 +1472,7 @@ looper()
|
||||
if (lnav_data.ld_mode != ln_mode_t::FILTER
|
||||
&& lnav_data.ld_mode != ln_mode_t::FILES)
|
||||
{
|
||||
rlc.do_update();
|
||||
rlc->do_update();
|
||||
}
|
||||
refresh();
|
||||
|
||||
@ -1415,10 +1485,11 @@ looper()
|
||||
case ln_mode_t::SEARCH:
|
||||
case ln_mode_t::SEARCH_FILTERS:
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
case ln_mode_t::SQL:
|
||||
case ln_mode_t::EXEC:
|
||||
case ln_mode_t::USER:
|
||||
if (rlc.consume_ready_for_input()) {
|
||||
if (rlc->consume_ready_for_input()) {
|
||||
// log_debug("waiting for readline input")
|
||||
view_curses::awaiting_user_input();
|
||||
}
|
||||
@ -1430,15 +1501,8 @@ looper()
|
||||
}
|
||||
}
|
||||
}
|
||||
rlc.update_poll_set(pollfds);
|
||||
lnav_data.ld_filter_source.fss_editor.update_poll_set(pollfds);
|
||||
|
||||
for (auto& tc : lnav_data.ld_views) {
|
||||
tc.update_poll_set(pollfds);
|
||||
}
|
||||
lnav_data.ld_filter_view.update_poll_set(pollfds);
|
||||
lnav_data.ld_files_view.update_poll_set(pollfds);
|
||||
|
||||
ps->update_poll_set(pollfds);
|
||||
ui_now = ui_clock::now();
|
||||
auto poll_to
|
||||
= (!changes && ui_now < loop_deadline && session_stage >= 1)
|
||||
@ -1505,6 +1569,7 @@ looper()
|
||||
case ln_mode_t::PAGING:
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
if (old_gen
|
||||
== lnav_data.ld_active_files
|
||||
.fc_files_generation) {
|
||||
@ -1518,6 +1583,7 @@ looper()
|
||||
case ln_mode_t::SEARCH:
|
||||
case ln_mode_t::SEARCH_FILTERS:
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
case ln_mode_t::CAPTURE:
|
||||
case ln_mode_t::SQL:
|
||||
case ln_mode_t::EXEC:
|
||||
@ -1528,20 +1594,13 @@ looper()
|
||||
next_rebuild_time = next_rescan_time;
|
||||
}
|
||||
|
||||
for (auto& tc : lnav_data.ld_views) {
|
||||
tc.check_poll_set(pollfds);
|
||||
}
|
||||
|
||||
ps->check_poll_set(pollfds);
|
||||
lnav_data.ld_view_stack.top() |
|
||||
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
|
||||
|
||||
auto old_mode = lnav_data.ld_mode;
|
||||
auto old_file_names_size
|
||||
= lnav_data.ld_active_files.fc_file_names.size();
|
||||
rlc.check_poll_set(pollfds);
|
||||
lnav_data.ld_filter_source.fss_editor.check_poll_set(pollfds);
|
||||
lnav_data.ld_filter_view.check_poll_set(pollfds);
|
||||
lnav_data.ld_files_view.check_poll_set(pollfds);
|
||||
|
||||
if (lnav_data.ld_mode != old_mode) {
|
||||
switch (lnav_data.ld_mode) {
|
||||
@ -1692,9 +1751,9 @@ looper()
|
||||
if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
|
||||
resizeterm(size.ws_row, size.ws_col);
|
||||
}
|
||||
rlc.do_update();
|
||||
rlc.window_change();
|
||||
lnav_data.ld_filter_source.fss_editor.window_change();
|
||||
rlc->do_update();
|
||||
rlc->window_change();
|
||||
filter_source->fss_editor->window_change();
|
||||
for (auto& sc : lnav_data.ld_status) {
|
||||
sc.window_change();
|
||||
}
|
||||
@ -1704,6 +1763,7 @@ looper()
|
||||
lnav_data.ld_match_view.set_needs_update();
|
||||
lnav_data.ld_filter_view.set_needs_update();
|
||||
lnav_data.ld_files_view.set_needs_update();
|
||||
lnav_data.ld_spectro_details_view.set_needs_update();
|
||||
lnav_data.ld_user_message_view.set_needs_update();
|
||||
}
|
||||
|
||||
@ -1728,10 +1788,6 @@ looper()
|
||||
gather_pipers();
|
||||
}
|
||||
|
||||
if (lnav_data.ld_meta_search) {
|
||||
lnav_data.ld_meta_search->start();
|
||||
}
|
||||
|
||||
if (lnav_data.ld_view_stack.empty()
|
||||
|| (lnav_data.ld_view_stack.size() == 1
|
||||
&& starting_view_stack_size == 2
|
||||
@ -1751,22 +1807,15 @@ wait_for_children()
|
||||
{
|
||||
std::vector<struct pollfd> pollfds;
|
||||
struct timeval to = {0, 333000};
|
||||
|
||||
if (lnav_data.ld_meta_search) {
|
||||
lnav_data.ld_meta_search->start();
|
||||
}
|
||||
static auto* ps = injector::get<pollable_supervisor*>();
|
||||
|
||||
do {
|
||||
pollfds.clear();
|
||||
|
||||
for (auto& tc : lnav_data.ld_views) {
|
||||
tc.update_poll_set(pollfds);
|
||||
}
|
||||
lnav_data.ld_filter_view.update_poll_set(pollfds);
|
||||
lnav_data.ld_files_view.update_poll_set(pollfds);
|
||||
auto update_res = ps->update_poll_set(pollfds);
|
||||
|
||||
if (pollfds.empty()) {
|
||||
return;
|
||||
if (update_res.ur_background == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int rc = poll(&pollfds[0], pollfds.size(), to.tv_usec / 1000);
|
||||
@ -1781,15 +1830,10 @@ wait_for_children()
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& tc : lnav_data.ld_views) {
|
||||
tc.check_poll_set(pollfds);
|
||||
|
||||
lnav_data.ld_view_stack.top() |
|
||||
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
|
||||
}
|
||||
lnav_data.ld_filter_view.check_poll_set(pollfds);
|
||||
lnav_data.ld_files_view.check_poll_set(pollfds);
|
||||
} while (true);
|
||||
ps->check_poll_set(pollfds);
|
||||
lnav_data.ld_view_stack.top() |
|
||||
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
|
||||
} while (lnav_data.ld_looping);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -2293,6 +2337,7 @@ main(int argc, char* argv[])
|
||||
"/usr/share/terminfo:/lib/terminfo:/usr/share/lib/terminfo",
|
||||
0);
|
||||
|
||||
auto* filter_source = injector::get<filter_sub_source*>();
|
||||
lnav_data.ld_vtab_manager = std::make_unique<log_vtab_manager>(
|
||||
lnav_data.ld_db, lnav_data.ld_views[LNV_LOG], lnav_data.ld_log_source);
|
||||
|
||||
@ -2326,20 +2371,22 @@ main(int argc, char* argv[])
|
||||
lnav_data.ld_views[LNV_DB].set_sub_source(&lnav_data.ld_db_row_source);
|
||||
lnav_data.ld_db_overlay.dos_labels = &lnav_data.ld_db_row_source;
|
||||
lnav_data.ld_views[LNV_DB].set_overlay_source(&lnav_data.ld_db_overlay);
|
||||
lnav_data.ld_spectro_source = std::make_unique<spectrogram_source>();
|
||||
lnav_data.ld_views[LNV_SPECTRO]
|
||||
.set_sub_source(&lnav_data.ld_spectro_source)
|
||||
.set_overlay_source(&lnav_data.ld_spectro_source)
|
||||
.add_input_delegate(lnav_data.ld_spectro_source)
|
||||
.set_tail_space(2_vl);
|
||||
.set_sub_source(lnav_data.ld_spectro_source.get())
|
||||
.set_overlay_source(lnav_data.ld_spectro_source.get())
|
||||
.add_input_delegate(*lnav_data.ld_spectro_source)
|
||||
.set_tail_space(4_vl);
|
||||
lnav_data.ld_views[LNV_SPECTRO].set_selectable(true);
|
||||
|
||||
lnav_data.ld_doc_view.set_sub_source(&lnav_data.ld_doc_source);
|
||||
lnav_data.ld_example_view.set_sub_source(&lnav_data.ld_example_source);
|
||||
lnav_data.ld_match_view.set_sub_source(&lnav_data.ld_match_source);
|
||||
lnav_data.ld_preview_view.set_sub_source(&lnav_data.ld_preview_source);
|
||||
lnav_data.ld_filter_view.set_sub_source(&lnav_data.ld_filter_source)
|
||||
.add_input_delegate(lnav_data.ld_filter_source)
|
||||
.add_child_view(&lnav_data.ld_filter_source.fss_match_view)
|
||||
.add_child_view(&lnav_data.ld_filter_source.fss_editor);
|
||||
lnav_data.ld_filter_view.set_sub_source(filter_source)
|
||||
.add_input_delegate(*filter_source)
|
||||
.add_child_view(&filter_source->fss_match_view)
|
||||
.add_child_view(filter_source->fss_editor.get());
|
||||
lnav_data.ld_files_view.set_sub_source(&lnav_data.ld_files_source)
|
||||
.add_input_delegate(lnav_data.ld_files_source);
|
||||
lnav_data.ld_user_message_view.set_sub_source(
|
||||
|
13
src/lnav.hh
13
src/lnav.hh
@ -54,7 +54,6 @@
|
||||
#include "file_collection.hh"
|
||||
#include "files_sub_source.hh"
|
||||
#include "filter_status_source.hh"
|
||||
#include "filter_sub_source.hh"
|
||||
#include "grep_highlighter.hh"
|
||||
#include "hist_source.hh"
|
||||
#include "input_dispatcher.hh"
|
||||
@ -68,12 +67,14 @@
|
||||
#include "readline_curses.hh"
|
||||
#include "relative_time.hh"
|
||||
#include "safe/safe.h"
|
||||
#include "spectro_source.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "statusview_curses.hh"
|
||||
#include "textfile_sub_source.hh"
|
||||
#include "view_helpers.hh"
|
||||
|
||||
class spectrogram_source;
|
||||
class spectro_status_source;
|
||||
|
||||
enum {
|
||||
LNB_HEADLESS,
|
||||
LNB_MANAGEMENT,
|
||||
@ -96,6 +97,7 @@ typedef enum {
|
||||
LNS_FILTER_HELP,
|
||||
LNS_DOC,
|
||||
LNS_PREVIEW,
|
||||
LNS_SPECTRO,
|
||||
|
||||
LNS__MAX
|
||||
} lnav_status_t;
|
||||
@ -198,6 +200,7 @@ struct lnav_data_t {
|
||||
filter_help_status_source ld_filter_help_status_source;
|
||||
doc_status_source ld_doc_status_source;
|
||||
preview_status_source ld_preview_status_source;
|
||||
std::unique_ptr<spectro_status_source> ld_spectro_status_source;
|
||||
bool ld_preview_hidden;
|
||||
int64_t ld_preview_generation{0};
|
||||
action_broadcaster<listview_curses> ld_scroll_broadcaster;
|
||||
@ -207,7 +210,6 @@ struct lnav_data_t {
|
||||
|
||||
plain_text_source ld_doc_source;
|
||||
textview_curses ld_doc_view;
|
||||
filter_sub_source ld_filter_source;
|
||||
textview_curses ld_filter_view;
|
||||
files_sub_source ld_files_source;
|
||||
files_overlay_source ld_files_overlay;
|
||||
@ -222,18 +224,19 @@ struct lnav_data_t {
|
||||
textview_curses ld_user_message_view;
|
||||
std::chrono::time_point<std::chrono::steady_clock>
|
||||
ld_user_message_expiration;
|
||||
textview_curses ld_spectro_details_view;
|
||||
plain_text_source ld_spectro_no_details_source;
|
||||
|
||||
view_stack<textview_curses> ld_view_stack;
|
||||
textview_curses* ld_last_view;
|
||||
textview_curses ld_views[LNV__MAX];
|
||||
std::shared_ptr<grep_proc<vis_line_t>> ld_meta_search;
|
||||
vis_line_t ld_search_start_line;
|
||||
readline_curses* ld_rl_view;
|
||||
|
||||
logfile_sub_source ld_log_source;
|
||||
hist_source2 ld_hist_source2;
|
||||
int ld_zoom_level;
|
||||
spectrogram_source ld_spectro_source;
|
||||
std::unique_ptr<spectrogram_source> ld_spectro_source;
|
||||
|
||||
textfile_sub_source ld_text_source;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2007-2012, Timothy Stack
|
||||
* Copyright (c) 2007-2022, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -73,6 +73,7 @@
|
||||
#include "service_tags.hh"
|
||||
#include "session_data.hh"
|
||||
#include "shlex.hh"
|
||||
#include "spectro_impls.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
#include "sysclip.hh"
|
||||
#include "tailer/tailer.looper.hh"
|
||||
@ -221,7 +222,7 @@ com_adjust_log_time(exec_context& ec,
|
||||
} else if (lnav_data.ld_views[LNV_LOG].get_inner_height() == 0) {
|
||||
return ec.make_error("no log messages");
|
||||
} else if (args.size() >= 2) {
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
struct timeval top_time, time_diff;
|
||||
struct timeval new_time = {0, 0};
|
||||
content_line_t top_content;
|
||||
@ -230,7 +231,7 @@ com_adjust_log_time(exec_context& ec,
|
||||
struct exttm tm;
|
||||
std::shared_ptr<logfile> lf;
|
||||
|
||||
top_line = lnav_data.ld_views[LNV_LOG].get_top();
|
||||
top_line = lnav_data.ld_views[LNV_LOG].get_selection();
|
||||
top_content = lss.at(top_line);
|
||||
lf = lss.find(top_content);
|
||||
|
||||
@ -375,7 +376,7 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
} else if (args.size() > 1) {
|
||||
std::string all_args = remaining_args(cmdline, args);
|
||||
auto* tc = *lnav_data.ld_view_stack.top();
|
||||
auto ttt = dynamic_cast<text_time_translator*>(tc->get_sub_source());
|
||||
auto* ttt = dynamic_cast<text_time_translator*>(tc->get_sub_source());
|
||||
int line_number, consumed;
|
||||
date_time_scanner dts;
|
||||
const char* scan_end = nullptr;
|
||||
@ -508,7 +509,7 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
} else {
|
||||
tc->get_sub_source()->get_location_history() |
|
||||
[new_top](auto lh) { lh->loc_history_append(new_top); };
|
||||
tc->set_top(new_top);
|
||||
tc->set_selection(new_top);
|
||||
|
||||
retval = "";
|
||||
}
|
||||
@ -564,7 +565,7 @@ com_mark(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
|
||||
if (args.empty() || lnav_data.ld_view_stack.empty()) {
|
||||
} else if (!ec.ec_dry_run) {
|
||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||
auto* tc = *lnav_data.ld_view_stack.top();
|
||||
lnav_data.ld_last_user_mark[tc] = tc->get_top();
|
||||
tc->toggle_user_mark(&textview_curses::BM_USER,
|
||||
vis_line_t(lnav_data.ld_last_user_mark[tc]));
|
||||
@ -761,7 +762,7 @@ com_goto_mark(exec_context& ec,
|
||||
[new_top](auto lh) {
|
||||
lh->loc_history_append(new_top.value());
|
||||
};
|
||||
tc->set_top(new_top.value());
|
||||
tc->set_selection(new_top.value());
|
||||
}
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
}
|
||||
@ -783,10 +784,10 @@ com_goto_location(exec_context& ec,
|
||||
tc->get_sub_source()->get_location_history() |
|
||||
[tc, &args](auto lh) {
|
||||
return args[0] == "prev-location"
|
||||
? lh->loc_history_back(tc->get_top())
|
||||
: lh->loc_history_forward(tc->get_top());
|
||||
? lh->loc_history_back(tc->get_selection())
|
||||
: lh->loc_history_forward(tc->get_selection());
|
||||
}
|
||||
| [tc](auto new_top) { tc->set_top(new_top); };
|
||||
| [tc](auto new_top) { tc->set_selection(new_top); };
|
||||
};
|
||||
}
|
||||
|
||||
@ -2900,15 +2901,14 @@ com_comment(exec_context& ec,
|
||||
return ec.make_error(
|
||||
"The :comment command only works in the log view");
|
||||
}
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
std::map<content_line_t, bookmark_metadata>& bm
|
||||
= lss.get_user_bookmark_metadata();
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
auto& bm = lss.get_user_bookmark_metadata();
|
||||
|
||||
args[1] = trim(remaining_args(cmdline, args));
|
||||
|
||||
tc->set_user_mark(&textview_curses::BM_META, tc->get_top(), true);
|
||||
|
||||
bookmark_metadata& line_meta = bm[lss.at(tc->get_top())];
|
||||
auto& line_meta = bm[lss.at(tc->get_top())];
|
||||
|
||||
line_meta.bm_comment = args[1];
|
||||
lss.set_line_meta_changed();
|
||||
@ -3207,10 +3207,8 @@ com_clear_partition(exec_context& ec,
|
||||
} else if (args.size() == 1) {
|
||||
textview_curses& tc = lnav_data.ld_views[LNV_LOG];
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
bookmark_vector<vis_line_t>& bv
|
||||
= tc.get_bookmarks()[&textview_curses::BM_META];
|
||||
std::map<content_line_t, bookmark_metadata>& bm
|
||||
= lss.get_user_bookmark_metadata();
|
||||
auto& bv = tc.get_bookmarks()[&textview_curses::BM_META];
|
||||
auto& bm = lss.get_user_bookmark_metadata();
|
||||
nonstd::optional<vis_line_t> part_start;
|
||||
|
||||
if (binary_search(bv.begin(), bv.end(), tc.get_top())) {
|
||||
@ -3621,12 +3619,12 @@ com_zoom_to(exec_context& ec,
|
||||
for (int lpc = 0; lpc < lnav_zoom_strings.size() && !found; lpc++) {
|
||||
if (strcasecmp(args[1].c_str(), lnav_zoom_strings[lpc].c_str())
|
||||
== 0) {
|
||||
spectrogram_source& ss = lnav_data.ld_spectro_source;
|
||||
auto& ss = *lnav_data.ld_spectro_source;
|
||||
struct timeval old_time;
|
||||
|
||||
lnav_data.ld_zoom_level = lpc;
|
||||
|
||||
textview_curses& hist_view = lnav_data.ld_views[LNV_HISTOGRAM];
|
||||
auto& hist_view = lnav_data.ld_views[LNV_HISTOGRAM];
|
||||
|
||||
if (hist_view.get_inner_height() > 0) {
|
||||
auto old_time_opt = lnav_data.ld_hist_source2.time_for_row(
|
||||
@ -3642,19 +3640,20 @@ com_zoom_to(exec_context& ec,
|
||||
}
|
||||
}
|
||||
|
||||
textview_curses& spectro_view = lnav_data.ld_views[LNV_SPECTRO];
|
||||
auto& spectro_view = lnav_data.ld_views[LNV_SPECTRO];
|
||||
|
||||
if (spectro_view.get_inner_height() > 0) {
|
||||
auto old_time_opt
|
||||
= lnav_data.ld_spectro_source.time_for_row(
|
||||
lnav_data.ld_views[LNV_SPECTRO].get_top());
|
||||
= lnav_data.ld_spectro_source->time_for_row(
|
||||
lnav_data.ld_views[LNV_SPECTRO].get_selection());
|
||||
ss.ss_granularity = ZOOM_LEVELS[lnav_data.ld_zoom_level];
|
||||
ss.invalidate();
|
||||
spectro_view.reload_data();
|
||||
if (old_time_opt) {
|
||||
lnav_data.ld_spectro_source.row_for_time(
|
||||
lnav_data.ld_spectro_source->row_for_time(
|
||||
old_time_opt.value())
|
||||
| [](auto new_top) {
|
||||
lnav_data.ld_views[LNV_SPECTRO].set_top(
|
||||
lnav_data.ld_views[LNV_SPECTRO].set_selection(
|
||||
new_top);
|
||||
};
|
||||
}
|
||||
@ -4399,306 +4398,6 @@ com_reset_config(exec_context& ec,
|
||||
return Ok(retval);
|
||||
}
|
||||
|
||||
class log_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
log_spectro_value_source(intern_string_t colname)
|
||||
: lsvs_colname(colname), lsvs_begin_time(0), lsvs_end_time(0),
|
||||
lsvs_found(false)
|
||||
{
|
||||
this->update_stats();
|
||||
};
|
||||
|
||||
void update_stats()
|
||||
{
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
logfile_sub_source::iterator iter;
|
||||
|
||||
this->lsvs_begin_time = 0;
|
||||
this->lsvs_end_time = 0;
|
||||
this->lsvs_stats.clear();
|
||||
for (iter = lss.begin(); iter != lss.end(); iter++) {
|
||||
std::shared_ptr<logfile> lf = (*iter)->get_file();
|
||||
|
||||
if (lf == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto format = lf->get_format();
|
||||
const auto* stats = format->stats_for_value(this->lsvs_colname);
|
||||
|
||||
if (stats == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ll = lf->begin();
|
||||
|
||||
if (this->lsvs_begin_time == 0
|
||||
|| ll->get_time() < this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = ll->get_time();
|
||||
}
|
||||
ll = lf->end();
|
||||
--ll;
|
||||
if (ll->get_time() > this->lsvs_end_time) {
|
||||
this->lsvs_end_time = ll->get_time();
|
||||
}
|
||||
|
||||
this->lsvs_found = true;
|
||||
this->lsvs_stats.merge(*stats);
|
||||
}
|
||||
|
||||
if (this->lsvs_begin_time) {
|
||||
time_t filtered_begin_time
|
||||
= lss.find_line(lss.at(0_vl))->get_time();
|
||||
time_t filtered_end_time
|
||||
= lss.find_line(lss.at(vis_line_t(lss.text_line_count() - 1)))
|
||||
->get_time();
|
||||
|
||||
if (filtered_begin_time > this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = filtered_begin_time;
|
||||
}
|
||||
if (filtered_end_time < this->lsvs_end_time) {
|
||||
this->lsvs_end_time = filtered_end_time;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out)
|
||||
{
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
|
||||
if (lss.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->lsvs_begin_time;
|
||||
sb_out.sb_end_time = this->lsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->lsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->lsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->lsvs_stats.lvs_count;
|
||||
};
|
||||
|
||||
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out)
|
||||
{
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
vis_line_t begin_line
|
||||
= lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
|
||||
vis_line_t end_line = lss.find_from_time(sr.sr_end_time)
|
||||
.value_or(lss.text_line_count());
|
||||
std::vector<logline_value> values;
|
||||
string_attrs_t sa;
|
||||
|
||||
for (vis_line_t curr_line = begin_line; curr_line < end_line;
|
||||
++curr_line) {
|
||||
content_line_t cl = lss.at(curr_line);
|
||||
std::shared_ptr<logfile> lf = lss.find(cl);
|
||||
auto ll = lf->begin() + cl;
|
||||
auto format = lf->get_format();
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
if (!ll->is_message()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lf->read_full_message(ll, sbr);
|
||||
sa.clear();
|
||||
values.clear();
|
||||
format->annotate(cl, sbr, sa, values, false);
|
||||
|
||||
std::vector<logline_value>::iterator lv_iter;
|
||||
|
||||
lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
row_out.add_value(
|
||||
sr, lv_iter->lv_value.d, ll->is_marked());
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
row_out.add_value(
|
||||
sr, lv_iter->lv_value.i, ll->is_marked());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max)
|
||||
{
|
||||
// XXX need to refactor this and the above method
|
||||
textview_curses& log_tc = lnav_data.ld_views[LNV_LOG];
|
||||
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||
vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl);
|
||||
vis_line_t end_line
|
||||
= lss.find_from_time(end_time).value_or(lss.text_line_count());
|
||||
std::vector<logline_value> values;
|
||||
string_attrs_t sa;
|
||||
|
||||
for (vis_line_t curr_line = begin_line; curr_line < end_line;
|
||||
++curr_line) {
|
||||
content_line_t cl = lss.at(curr_line);
|
||||
std::shared_ptr<logfile> lf = lss.find(cl);
|
||||
auto ll = lf->begin() + cl;
|
||||
auto format = lf->get_format();
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
if (!ll->is_message()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lf->read_full_message(ll, sbr);
|
||||
sa.clear();
|
||||
values.clear();
|
||||
format->annotate(cl, sbr, sa, values, false);
|
||||
|
||||
std::vector<logline_value>::iterator lv_iter;
|
||||
|
||||
lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
if (range_min <= lv_iter->lv_value.d
|
||||
&& lv_iter->lv_value.d <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
if (range_min <= lv_iter->lv_value.i
|
||||
&& lv_iter->lv_value.i <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
intern_string_t lsvs_colname;
|
||||
logline_value_stats lsvs_stats;
|
||||
time_t lsvs_begin_time;
|
||||
time_t lsvs_end_time;
|
||||
bool lsvs_found;
|
||||
};
|
||||
|
||||
class db_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
db_spectro_value_source(std::string colname)
|
||||
: dsvs_colname(std::move(colname))
|
||||
{
|
||||
this->update_stats();
|
||||
}
|
||||
|
||||
void update_stats()
|
||||
{
|
||||
this->dsvs_begin_time = 0;
|
||||
this->dsvs_end_time = 0;
|
||||
this->dsvs_stats.clear();
|
||||
|
||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||
stacked_bar_chart<std::string>& chart = dls.dls_chart;
|
||||
date_time_scanner dts;
|
||||
|
||||
this->dsvs_column_index = dls.column_name_to_index(this->dsvs_colname);
|
||||
|
||||
if (!dls.has_log_time_column()) {
|
||||
this->dsvs_error_msg
|
||||
= "no 'log_time' column found or not in ascending order, "
|
||||
"unable to create spectrogram";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->dsvs_column_index) {
|
||||
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
|
||||
this->dsvs_error_msg
|
||||
= "column is not numeric -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dls.dls_rows.empty()) {
|
||||
this->dsvs_error_msg = "empty result set";
|
||||
return;
|
||||
}
|
||||
|
||||
stacked_bar_chart<std::string>::bucket_stats_t bs
|
||||
= chart.get_stats_for(this->dsvs_colname);
|
||||
|
||||
this->dsvs_begin_time = dls.dls_time_column.front().tv_sec;
|
||||
this->dsvs_end_time = dls.dls_time_column.back().tv_sec;
|
||||
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
|
||||
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
|
||||
this->dsvs_stats.lvs_count = dls.dls_rows.size();
|
||||
}
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out) override
|
||||
{
|
||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||
|
||||
if (dls.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->dsvs_begin_time;
|
||||
sb_out.sb_end_time = this->dsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->dsvs_stats.lvs_count;
|
||||
}
|
||||
|
||||
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out) override
|
||||
{
|
||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||
auto end_row = dls.row_for_time({sr.sr_end_time, 0})
|
||||
.value_or(dls.dls_rows.size());
|
||||
|
||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||
double value = 0.0;
|
||||
|
||||
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
|
||||
"%lf",
|
||||
&value);
|
||||
|
||||
row_out.add_value(sr, value, false);
|
||||
}
|
||||
}
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max) override{};
|
||||
|
||||
std::string dsvs_colname;
|
||||
logline_value_stats dsvs_stats;
|
||||
time_t dsvs_begin_time{0};
|
||||
time_t dsvs_end_time{0};
|
||||
nonstd::optional<size_t> dsvs_column_index;
|
||||
std::string dsvs_error_msg;
|
||||
};
|
||||
|
||||
static Result<std::string, lnav::console::user_message>
|
||||
com_spectrogram(exec_context& ec,
|
||||
std::string cmdline,
|
||||
@ -4712,13 +4411,13 @@ com_spectrogram(exec_context& ec,
|
||||
retval = "";
|
||||
} else if (args.size() == 2) {
|
||||
std::string colname = remaining_args(cmdline, args);
|
||||
spectrogram_source& ss = lnav_data.ld_spectro_source;
|
||||
auto& ss = *lnav_data.ld_spectro_source;
|
||||
bool found = false;
|
||||
|
||||
ss.ss_granularity = ZOOM_LEVELS[lnav_data.ld_zoom_level];
|
||||
if (ss.ss_value_source != NULL) {
|
||||
if (ss.ss_value_source != nullptr) {
|
||||
delete ss.ss_value_source;
|
||||
ss.ss_value_source = NULL;
|
||||
ss.ss_value_source = nullptr;
|
||||
}
|
||||
ss.invalidate();
|
||||
|
||||
@ -4728,10 +4427,9 @@ com_spectrogram(exec_context& ec,
|
||||
|
||||
if (!dsvs->dsvs_error_msg.empty()) {
|
||||
return ec.make_error("{}", dsvs->dsvs_error_msg);
|
||||
} else {
|
||||
ss.ss_value_source = dsvs.release();
|
||||
found = true;
|
||||
}
|
||||
ss.ss_value_source = dsvs.release();
|
||||
found = true;
|
||||
} else {
|
||||
std::unique_ptr<log_spectro_value_source> lsvs(
|
||||
new log_spectro_value_source(intern_string::lookup(colname)));
|
||||
@ -4739,16 +4437,16 @@ com_spectrogram(exec_context& ec,
|
||||
if (!lsvs->lsvs_found) {
|
||||
return ec.make_error("unknown numeric message field -- {}",
|
||||
colname);
|
||||
} else {
|
||||
ss.ss_value_source = lsvs.release();
|
||||
found = true;
|
||||
}
|
||||
ss.ss_value_source = lsvs.release();
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
ss.text_selection_changed(lnav_data.ld_views[LNV_SPECTRO]);
|
||||
ensure_view(&lnav_data.ld_views[LNV_SPECTRO]);
|
||||
|
||||
if (lnav_data.ld_rl_view != NULL) {
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_2(z, Z, "to zoom in/out"));
|
||||
}
|
||||
@ -4978,6 +4676,24 @@ search_files_prompt(std::vector<std::string>& args)
|
||||
ANSI_BOLD("CTRL+]") " to abort)");
|
||||
}
|
||||
|
||||
static void
|
||||
search_spectro_details_prompt(std::vector<std::string>& args)
|
||||
{
|
||||
lnav_data.ld_mode = ln_mode_t::SEARCH_SPECTRO_DETAILS;
|
||||
add_view_text_possibilities(lnav_data.ld_rl_view,
|
||||
ln_mode_t::SEARCH_SPECTRO_DETAILS,
|
||||
"*",
|
||||
&lnav_data.ld_spectro_details_view,
|
||||
text_quoting::regex);
|
||||
lnav_data.ld_rl_view->focus(ln_mode_t::SEARCH_SPECTRO_DETAILS,
|
||||
cget(args, 2).value_or("/"),
|
||||
cget(args, 3).value_or(""));
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
"Search for: "
|
||||
"(Press " ANSI_BOLD("CTRL+J") " to jump to a previous hit and "
|
||||
ANSI_BOLD("CTRL+]") " to abort)");
|
||||
}
|
||||
|
||||
static void
|
||||
sql_prompt(std::vector<std::string>& args)
|
||||
{
|
||||
@ -5032,6 +4748,7 @@ com_prompt(exec_context& ec,
|
||||
{"search", search_prompt},
|
||||
{"search-filters", search_filters_prompt},
|
||||
{"search-files", search_files_prompt},
|
||||
{"search-spectro-details", search_spectro_details_prompt},
|
||||
{"sql", sql_prompt},
|
||||
{"user", user_prompt},
|
||||
};
|
||||
|
@ -735,6 +735,22 @@ public:
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool hide_field(const intern_string_t field_name, bool val) override
|
||||
{
|
||||
auto fd_iter
|
||||
= std::find_if(this->blf_field_defs.begin(),
|
||||
this->blf_field_defs.end(),
|
||||
[field_name](const field_def& elem) {
|
||||
return elem.fd_meta.lvm_name == field_name;
|
||||
});
|
||||
if (fd_iter == this->blf_field_defs.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_iter->fd_meta.lvm_user_hidden = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<log_format> specialized(int fmt_lock = -1) override
|
||||
{
|
||||
auto retval = std::make_shared<bro_log_format>(*this);
|
||||
@ -1337,6 +1353,22 @@ public:
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool hide_field(const intern_string_t field_name, bool val) override
|
||||
{
|
||||
auto fd_iter
|
||||
= std::find_if(this->wlf_field_defs.begin(),
|
||||
this->wlf_field_defs.end(),
|
||||
[field_name](const field_def& elem) {
|
||||
return elem.fd_meta.lvm_name == field_name;
|
||||
});
|
||||
if (fd_iter == this->wlf_field_defs.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_iter->fd_meta.lvm_user_hidden = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<log_format> specialized(int fmt_lock = -1) override
|
||||
{
|
||||
auto retval = std::make_shared<w3c_log_format>(*this);
|
||||
|
@ -1340,7 +1340,9 @@ vt_filter(sqlite3_vtab_cursor* p_vtc,
|
||||
if (vl_max_opt) {
|
||||
p_cur->log_cursor.lc_end_line = vl_max_opt.value();
|
||||
for (const auto& msg_info :
|
||||
vt->lss->window_at(vl_max_opt.value())) {
|
||||
vt->lss->window_at(vl_max_opt.value(),
|
||||
vis_line_t(vt->lss->text_line_count())))
|
||||
{
|
||||
if (log_time_range->tr_end.value()
|
||||
< msg_info.get_logline().get_timeval()) {
|
||||
break;
|
||||
|
@ -1963,7 +1963,7 @@ logline_window::begin()
|
||||
logline_window::iterator
|
||||
logline_window::end()
|
||||
{
|
||||
return {this->lw_source, vis_line_t(this->lw_source.text_line_count())};
|
||||
return {this->lw_source, this->lw_end_line};
|
||||
}
|
||||
|
||||
logline_window::logmsg_info::logmsg_info(logfile_sub_source& lss, vis_line_t vl)
|
||||
|
@ -156,8 +156,10 @@ private:
|
||||
|
||||
class logline_window {
|
||||
public:
|
||||
logline_window(logfile_sub_source& lss, vis_line_t start_line)
|
||||
: lw_source(lss), lw_start_line(start_line)
|
||||
logline_window(logfile_sub_source& lss,
|
||||
vis_line_t start_vl,
|
||||
vis_line_t end_vl)
|
||||
: lw_source(lss), lw_start_line(start_vl), lw_end_line(end_vl)
|
||||
{
|
||||
}
|
||||
|
||||
@ -177,6 +179,12 @@ public:
|
||||
return this->li_string_attrs;
|
||||
}
|
||||
|
||||
const std::vector<logline_value>& get_values() const
|
||||
{
|
||||
this->load_msg();
|
||||
return this->li_line_values;
|
||||
}
|
||||
|
||||
std::string to_string(const struct line_range& lr) const;
|
||||
|
||||
private:
|
||||
@ -218,6 +226,7 @@ public:
|
||||
private:
|
||||
logfile_sub_source& lw_source;
|
||||
vis_line_t lw_start_line;
|
||||
vis_line_t lw_end_line;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -615,9 +624,9 @@ public:
|
||||
return this->at(vl);
|
||||
}
|
||||
|
||||
logline_window window_at(vis_line_t vl)
|
||||
logline_window window_at(vis_line_t start_vl, vis_line_t end_vl)
|
||||
{
|
||||
return logline_window(*this, vl);
|
||||
return logline_window(*this, start_vl, end_vl);
|
||||
}
|
||||
|
||||
log_accel::direction_t get_line_accel_direction(vis_line_t vl);
|
||||
|
103
src/pollable.cc
Normal file
103
src/pollable.cc
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2022, 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 "pollable.hh"
|
||||
|
||||
#include "base/lnav_log.hh"
|
||||
|
||||
pollable::pollable(std::shared_ptr<pollable_supervisor> supervisor,
|
||||
category cat)
|
||||
: p_supervisor(supervisor), p_category(cat)
|
||||
{
|
||||
log_debug("pollable attach %p to %p", this, this->p_supervisor.get());
|
||||
this->p_supervisor->attach(this);
|
||||
}
|
||||
|
||||
pollable::~pollable()
|
||||
{
|
||||
log_debug("pollable detach %p from %p", this, this->p_supervisor.get());
|
||||
this->p_supervisor->detach(this);
|
||||
}
|
||||
|
||||
pollable_supervisor::update_result
|
||||
pollable_supervisor::update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
update_result retval;
|
||||
size_t old_size = pollfds.size();
|
||||
|
||||
for (auto& pol : this->b_components) {
|
||||
pol->update_poll_set(pollfds);
|
||||
switch (pol->get_category()) {
|
||||
case pollable::category::background:
|
||||
retval.ur_background += pollfds.size() - old_size;
|
||||
break;
|
||||
case pollable::category::interactive:
|
||||
retval.ur_interactive += pollfds.size() - old_size;
|
||||
break;
|
||||
}
|
||||
old_size = pollfds.size();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
pollable_supervisor::check_poll_set(const std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
std::vector<pollable*> visited;
|
||||
auto found_new = false;
|
||||
|
||||
// TODO move this loop into the superclass
|
||||
do {
|
||||
found_new = false;
|
||||
for (auto* pol : this->b_components) {
|
||||
if (std::find(visited.begin(), visited.end(), pol) == visited.end())
|
||||
{
|
||||
visited.emplace_back(pol);
|
||||
pol->check_poll_set(pollfds);
|
||||
found_new = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found_new);
|
||||
}
|
||||
|
||||
size_t
|
||||
pollable_supervisor::count(pollable::category cat)
|
||||
{
|
||||
size_t retval = 0;
|
||||
|
||||
for (const auto* pol : this->b_components) {
|
||||
if (pol->get_category() == cat) {
|
||||
retval += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
79
src/pollable.hh
Normal file
79
src/pollable.hh
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2022, 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_pollable_hh
|
||||
#define lnav_pollable_hh
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include "base/bus.hh"
|
||||
|
||||
class pollable_supervisor;
|
||||
|
||||
class pollable {
|
||||
public:
|
||||
enum class category {
|
||||
background,
|
||||
interactive,
|
||||
};
|
||||
|
||||
pollable(std::shared_ptr<pollable_supervisor> supervisor, category cat);
|
||||
|
||||
pollable(const pollable&) = delete;
|
||||
|
||||
virtual ~pollable();
|
||||
|
||||
category get_category() const { return this->p_category; }
|
||||
|
||||
virtual void update_poll_set(std::vector<struct pollfd>& pollfds) = 0;
|
||||
|
||||
virtual void check_poll_set(const std::vector<struct pollfd>& pollfds) = 0;
|
||||
|
||||
private:
|
||||
std::shared_ptr<pollable_supervisor> p_supervisor;
|
||||
const category p_category;
|
||||
};
|
||||
|
||||
class pollable_supervisor : public bus<pollable> {
|
||||
public:
|
||||
struct update_result {
|
||||
size_t ur_background{0};
|
||||
size_t ur_interactive{0};
|
||||
};
|
||||
|
||||
update_result update_poll_set(std::vector<struct pollfd>& pollfds);
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds);
|
||||
|
||||
size_t count(pollable::category cat);
|
||||
};
|
||||
|
||||
#endif
|
@ -410,6 +410,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
||||
case ln_mode_t::SEARCH:
|
||||
case ln_mode_t::SEARCH_FILTERS:
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
name = "$search";
|
||||
break;
|
||||
|
||||
@ -492,6 +493,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::EXEC:
|
||||
case ln_mode_t::USER:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
return;
|
||||
}
|
||||
|
||||
@ -573,6 +575,9 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
new_mode = ln_mode_t::FILES;
|
||||
break;
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
new_mode = ln_mode_t::SPECTRO_DETAILS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -583,6 +588,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
case ln_mode_t::PAGING:
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
require(0);
|
||||
break;
|
||||
|
||||
@ -616,6 +622,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
case ln_mode_t::SEARCH:
|
||||
case ln_mode_t::SEARCH_FILTERS:
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
case ln_mode_t::CAPTURE:
|
||||
rl_search_internal(rc, old_mode, true);
|
||||
if (!rc->get_value().empty()) {
|
||||
|
@ -591,8 +591,10 @@ readline_context::save()
|
||||
hs = nullptr;
|
||||
}
|
||||
|
||||
readline_curses::readline_curses()
|
||||
: rc_focus(noop_func{}), rc_change(noop_func{}), rc_perform(noop_func{}),
|
||||
readline_curses::readline_curses(
|
||||
std::shared_ptr<pollable_supervisor> supervisor)
|
||||
: pollable(supervisor, pollable::category::interactive),
|
||||
rc_focus(noop_func{}), rc_change(noop_func{}), rc_perform(noop_func{}),
|
||||
rc_alt_perform(noop_func{}), rc_timeout(noop_func{}),
|
||||
rc_abort(noop_func{}), rc_display_match(noop_func{}),
|
||||
rc_display_next(noop_func{}), rc_blur(noop_func{}),
|
||||
@ -1468,3 +1470,15 @@ readline_curses::set_attr_value(const attr_line_t& value)
|
||||
this->rc_value_expiration = time(nullptr) + VALUE_EXPIRATION;
|
||||
this->set_needs_update();
|
||||
}
|
||||
|
||||
void
|
||||
readline_curses::update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
if (this->rc_pty[RCF_MASTER] != -1) {
|
||||
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
|
||||
}
|
||||
if (this->rc_command_pipe[RCF_MASTER] != -1) {
|
||||
pollfds.push_back(
|
||||
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "base/result.h"
|
||||
#include "help_text_formatter.hh"
|
||||
#include "log_format.hh"
|
||||
#include "pollable.hh"
|
||||
#include "readline_context.hh"
|
||||
#include "vt52_curses.hh"
|
||||
|
||||
@ -70,7 +71,9 @@ extern exec_context INIT_EXEC_CONTEXT;
|
||||
* vt52 translation is done by the parent class, vt52_curses, while this class
|
||||
* takes care of the communication between the two processes.
|
||||
*/
|
||||
class readline_curses : public vt52_curses {
|
||||
class readline_curses
|
||||
: public vt52_curses
|
||||
, public pollable {
|
||||
public:
|
||||
using action = std::function<void(readline_curses*)>;
|
||||
|
||||
@ -85,9 +88,11 @@ public:
|
||||
|
||||
static const int VALUE_EXPIRATION = 20;
|
||||
|
||||
readline_curses();
|
||||
readline_curses(std::shared_ptr<pollable_supervisor>);
|
||||
~readline_curses() override;
|
||||
|
||||
using injectable = readline_curses(std::shared_ptr<pollable_supervisor>);
|
||||
|
||||
void add_context(int id, readline_context& rc)
|
||||
{
|
||||
this->rc_contexts[id] = &rc;
|
||||
@ -156,16 +161,11 @@ public:
|
||||
|
||||
std::string get_alt_value() const { return this->rc_alt_value; }
|
||||
|
||||
void update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
|
||||
pollfds.push_back(
|
||||
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
|
||||
}
|
||||
void update_poll_set(std::vector<struct pollfd>& pollfds) override;
|
||||
|
||||
void handle_key(int ch);
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds);
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds) override;
|
||||
|
||||
void focus(int context,
|
||||
const std::string& prompt,
|
||||
|
397
src/spectro_impls.cc
Normal file
397
src/spectro_impls.cc
Normal file
@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Copyright (c) 2022, 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 "spectro_impls.hh"
|
||||
|
||||
#include "lnav.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
|
||||
class filtered_sub_source : public text_sub_source {
|
||||
public:
|
||||
size_t text_line_count() override { return this->fss_lines.size(); }
|
||||
|
||||
void text_value_for_line(textview_curses& tc,
|
||||
int line,
|
||||
std::string& value_out,
|
||||
line_flags_t flags) override
|
||||
{
|
||||
this->fss_delegate->text_value_for_line(
|
||||
tc, this->fss_lines[line], value_out, flags);
|
||||
}
|
||||
|
||||
size_t text_size_for_line(textview_curses& tc,
|
||||
int line,
|
||||
line_flags_t raw) override
|
||||
{
|
||||
return this->fss_delegate->text_size_for_line(
|
||||
tc, this->fss_lines[line], raw);
|
||||
}
|
||||
|
||||
void text_attrs_for_line(textview_curses& tc,
|
||||
int line,
|
||||
string_attrs_t& value_out) override
|
||||
{
|
||||
this->fss_delegate->text_attrs_for_line(
|
||||
tc, this->fss_lines[line], value_out);
|
||||
}
|
||||
|
||||
text_sub_source* fss_delegate;
|
||||
std::vector<vis_line_t> fss_lines;
|
||||
};
|
||||
|
||||
log_spectro_value_source::log_spectro_value_source(intern_string_t colname)
|
||||
: lsvs_colname(colname)
|
||||
{
|
||||
this->update_stats();
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::update_stats()
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
|
||||
this->lsvs_begin_time = 0;
|
||||
this->lsvs_end_time = 0;
|
||||
this->lsvs_stats.clear();
|
||||
for (auto& ls : lss) {
|
||||
auto* lf = ls->get_file_ptr();
|
||||
|
||||
if (lf == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto format = lf->get_format();
|
||||
const auto* stats = format->stats_for_value(this->lsvs_colname);
|
||||
|
||||
if (stats == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ll = lf->begin();
|
||||
|
||||
if (this->lsvs_begin_time == 0
|
||||
|| ll->get_time() < this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = ll->get_time();
|
||||
}
|
||||
ll = lf->end();
|
||||
--ll;
|
||||
if (ll->get_time() > this->lsvs_end_time) {
|
||||
this->lsvs_end_time = ll->get_time();
|
||||
}
|
||||
|
||||
this->lsvs_found = true;
|
||||
this->lsvs_stats.merge(*stats);
|
||||
}
|
||||
|
||||
if (this->lsvs_begin_time) {
|
||||
time_t filtered_begin_time = lss.find_line(lss.at(0_vl))->get_time();
|
||||
time_t filtered_end_time
|
||||
= lss.find_line(lss.at(vis_line_t(lss.text_line_count() - 1)))
|
||||
->get_time();
|
||||
|
||||
if (filtered_begin_time > this->lsvs_begin_time) {
|
||||
this->lsvs_begin_time = filtered_begin_time;
|
||||
}
|
||||
if (filtered_end_time < this->lsvs_end_time) {
|
||||
this->lsvs_end_time = filtered_end_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
|
||||
if (lss.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->lsvs_begin_time;
|
||||
sb_out.sb_end_time = this->lsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->lsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->lsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->lsvs_stats.lvs_count;
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out)
|
||||
{
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
|
||||
auto end_line
|
||||
= lss.find_from_time(sr.sr_end_time).value_or(lss.text_line_count());
|
||||
|
||||
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
|
||||
const auto& ll = msg_info.get_logline();
|
||||
if (ll.get_time() >= sr.sr_end_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& values = msg_info.get_values();
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
row_out.add_value(sr, lv_iter->lv_value.d, ll.is_marked());
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER: {
|
||||
row_out.add_value(sr, lv_iter->lv_value.i, ll.is_marked());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
|
||||
double range_min,
|
||||
double range_max) {
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
auto retval = std::make_unique<filtered_sub_source>();
|
||||
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
|
||||
auto end_line = lss.find_from_time(sr.sr_end_time)
|
||||
.value_or(lss.text_line_count());
|
||||
|
||||
retval->fss_delegate = &lss;
|
||||
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
|
||||
const auto& ll = msg_info.get_logline();
|
||||
if (ll.get_time() >= sr.sr_end_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& values = msg_info.get_values();
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
if (range_min <= lv_iter->lv_value.d
|
||||
&& lv_iter->lv_value.d < range_max) {
|
||||
retval->fss_lines.emplace_back(
|
||||
msg_info.get_vis_line());
|
||||
}
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
if (range_min <= lv_iter->lv_value.i
|
||||
&& lv_iter->lv_value.i < range_max) {
|
||||
retval->fss_lines.emplace_back(
|
||||
msg_info.get_vis_line());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
log_spectro_value_source::spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max)
|
||||
{
|
||||
// XXX need to refactor this and the above method
|
||||
auto& log_tc = lnav_data.ld_views[LNV_LOG];
|
||||
auto& lss = lnav_data.ld_log_source;
|
||||
vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl);
|
||||
vis_line_t end_line
|
||||
= lss.find_from_time(end_time).value_or(lss.text_line_count());
|
||||
std::vector<logline_value> values;
|
||||
string_attrs_t sa;
|
||||
|
||||
for (vis_line_t curr_line = begin_line; curr_line < end_line; ++curr_line) {
|
||||
content_line_t cl = lss.at(curr_line);
|
||||
std::shared_ptr<logfile> lf = lss.find(cl);
|
||||
auto ll = lf->begin() + cl;
|
||||
auto format = lf->get_format();
|
||||
shared_buffer_ref sbr;
|
||||
|
||||
if (!ll->is_message()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lf->read_full_message(ll, sbr);
|
||||
sa.clear();
|
||||
values.clear();
|
||||
format->annotate(cl, sbr, sa, values, false);
|
||||
|
||||
auto lv_iter = find_if(values.begin(),
|
||||
values.end(),
|
||||
logline_value_cmp(&this->lsvs_colname));
|
||||
|
||||
if (lv_iter != values.end()) {
|
||||
switch (lv_iter->lv_meta.lvm_kind) {
|
||||
case value_kind_t::VALUE_FLOAT:
|
||||
if (range_min <= lv_iter->lv_value.d
|
||||
&& lv_iter->lv_value.d <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
case value_kind_t::VALUE_INTEGER:
|
||||
if (range_min <= lv_iter->lv_value.i
|
||||
&& lv_iter->lv_value.i <= range_max) {
|
||||
log_tc.toggle_user_mark(&textview_curses::BM_USER,
|
||||
curr_line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db_spectro_value_source::db_spectro_value_source(std::string colname)
|
||||
: dsvs_colname(std::move(colname))
|
||||
{
|
||||
this->update_stats();
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::update_stats()
|
||||
{
|
||||
this->dsvs_begin_time = 0;
|
||||
this->dsvs_end_time = 0;
|
||||
this->dsvs_stats.clear();
|
||||
|
||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||
stacked_bar_chart<std::string>& chart = dls.dls_chart;
|
||||
date_time_scanner dts;
|
||||
|
||||
this->dsvs_column_index = dls.column_name_to_index(this->dsvs_colname);
|
||||
|
||||
if (!dls.has_log_time_column()) {
|
||||
this->dsvs_error_msg
|
||||
= "no 'log_time' column found or not in ascending order, "
|
||||
"unable to create spectrogram";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->dsvs_column_index) {
|
||||
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
|
||||
this->dsvs_error_msg = "column is not numeric -- " + this->dsvs_colname;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dls.dls_rows.empty()) {
|
||||
this->dsvs_error_msg = "empty result set";
|
||||
return;
|
||||
}
|
||||
|
||||
stacked_bar_chart<std::string>::bucket_stats_t bs
|
||||
= chart.get_stats_for(this->dsvs_colname);
|
||||
|
||||
this->dsvs_begin_time = dls.dls_time_column.front().tv_sec;
|
||||
this->dsvs_end_time = dls.dls_time_column.back().tv_sec;
|
||||
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
|
||||
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
|
||||
this->dsvs_stats.lvs_count = dls.dls_rows.size();
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
|
||||
{
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
|
||||
if (dls.text_line_count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->update_stats();
|
||||
|
||||
sb_out.sb_begin_time = this->dsvs_begin_time;
|
||||
sb_out.sb_end_time = this->dsvs_end_time;
|
||||
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
|
||||
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
|
||||
sb_out.sb_count = this->dsvs_stats.lvs_count;
|
||||
}
|
||||
|
||||
void
|
||||
db_spectro_value_source::spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out)
|
||||
{
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||
auto end_row
|
||||
= dls.row_for_time({sr.sr_end_time, 0}).value_or(dls.dls_rows.size());
|
||||
|
||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||
double value = 0.0;
|
||||
|
||||
sscanf(
|
||||
dls.dls_rows[lpc][this->dsvs_column_index.value()], "%lf", &value);
|
||||
|
||||
row_out.add_value(sr, value, false);
|
||||
}
|
||||
|
||||
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
|
||||
double range_min,
|
||||
double range_max) {
|
||||
auto& dls = lnav_data.ld_db_row_source;
|
||||
auto retval = std::make_unique<filtered_sub_source>();
|
||||
|
||||
retval->fss_delegate = &dls;
|
||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||
auto end_row = dls.row_for_time({sr.sr_end_time, 0})
|
||||
.value_or(dls.dls_rows.size());
|
||||
|
||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||
double value = 0.0;
|
||||
|
||||
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
|
||||
"%lf",
|
||||
&value);
|
||||
if (range_min <= value && value < range_max) {
|
||||
retval->fss_lines.emplace_back(lpc);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
}
|
87
src/spectro_impls.hh
Normal file
87
src/spectro_impls.hh
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2007-2022, 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_spectro_impls_hh
|
||||
#define lnav_spectro_impls_hh
|
||||
|
||||
#include "log_format.hh"
|
||||
#include "spectro_source.hh"
|
||||
|
||||
class log_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
log_spectro_value_source(intern_string_t colname);
|
||||
|
||||
void update_stats();
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out) override;
|
||||
|
||||
void spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out) override;
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max) override;
|
||||
|
||||
intern_string_t lsvs_colname;
|
||||
logline_value_stats lsvs_stats;
|
||||
time_t lsvs_begin_time{0};
|
||||
time_t lsvs_end_time{0};
|
||||
bool lsvs_found{false};
|
||||
};
|
||||
|
||||
class db_spectro_value_source : public spectrogram_value_source {
|
||||
public:
|
||||
db_spectro_value_source(std::string colname);
|
||||
|
||||
void update_stats();
|
||||
|
||||
void spectro_bounds(spectrogram_bounds& sb_out) override;
|
||||
|
||||
void spectro_row(spectrogram_request& sr,
|
||||
spectrogram_row& row_out) override;
|
||||
|
||||
void spectro_mark(textview_curses& tc,
|
||||
time_t begin_time,
|
||||
time_t end_time,
|
||||
double range_min,
|
||||
double range_max) override
|
||||
{
|
||||
}
|
||||
|
||||
std::string dsvs_colname;
|
||||
logline_value_stats dsvs_stats;
|
||||
time_t dsvs_begin_time{0};
|
||||
time_t dsvs_end_time{0};
|
||||
nonstd::optional<size_t> dsvs_column_index;
|
||||
std::string dsvs_error_msg;
|
||||
};
|
||||
|
||||
#endif
|
@ -33,17 +33,38 @@
|
||||
|
||||
#include "base/ansi_scrubber.hh"
|
||||
#include "base/math_util.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "config.h"
|
||||
|
||||
nonstd::optional<size_t>
|
||||
spectrogram_row::nearest_column(size_t current) const
|
||||
{
|
||||
nonstd::optional<size_t> retval;
|
||||
nonstd::optional<size_t> nearest_distance;
|
||||
|
||||
for (size_t lpc = 0; lpc < this->sr_width; lpc++) {
|
||||
if (this->sr_values[lpc].rb_counter == 0) {
|
||||
continue;
|
||||
}
|
||||
auto curr_distance = abs_diff(lpc, current);
|
||||
|
||||
if (!retval || curr_distance < nearest_distance.value()) {
|
||||
retval = lpc;
|
||||
nearest_distance = abs_diff(lpc, current);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case 'm': {
|
||||
if (this->ss_cursor_top < 0
|
||||
|| (size_t) this->ss_cursor_top >= this->text_line_count()
|
||||
|| this->ss_cursor_column == -1
|
||||
|| this->ss_value_source == nullptr)
|
||||
auto sel = lv.get_selection();
|
||||
if (sel < 0 || (size_t) sel >= this->text_line_count()
|
||||
|| !this->ss_cursor_column || this->ss_value_source == nullptr)
|
||||
{
|
||||
alerter::singleton().chime();
|
||||
return true;
|
||||
@ -53,9 +74,10 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
vis_line_t height;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
width -= 2;
|
||||
|
||||
spectrogram_bounds& sb = this->ss_cached_bounds;
|
||||
auto begin_time_opt = this->time_for_row(this->ss_cursor_top);
|
||||
auto& sb = this->ss_cached_bounds;
|
||||
auto begin_time_opt = this->time_for_row(sel);
|
||||
if (!begin_time_opt) {
|
||||
return true;
|
||||
}
|
||||
@ -67,9 +89,9 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
|
||||
column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
|
||||
/ (double) (width - 1);
|
||||
range_min
|
||||
= sb.sb_min_value_out + this->ss_cursor_column * column_size;
|
||||
range_max = range_min + column_size + column_size * 0.01;
|
||||
range_min = sb.sb_min_value_out
|
||||
+ this->ss_cursor_column.value_or(0) * column_size;
|
||||
range_max = range_min + column_size;
|
||||
this->ss_value_source->spectro_mark((textview_curses&) lv,
|
||||
begin_time.tv_sec,
|
||||
end_time.tv_sec,
|
||||
@ -79,29 +101,30 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
lv.reload_data();
|
||||
return true;
|
||||
}
|
||||
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT: {
|
||||
auto sel = lv.get_selection();
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
string_attrs_t sa;
|
||||
|
||||
this->ss_cursor_top = lv.get_top();
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
this->text_attrs_for_line(
|
||||
(textview_curses&) lv, this->ss_cursor_top, sa);
|
||||
this->text_attrs_for_line((textview_curses&) lv, sel, sa);
|
||||
|
||||
if (sa.empty()) {
|
||||
this->ss_cursor_column = -1;
|
||||
this->ss_cursor_column = nonstd::nullopt;
|
||||
return true;
|
||||
}
|
||||
|
||||
string_attrs_t::iterator current;
|
||||
if (!this->ss_cursor_column) {
|
||||
lv.set_selection(0_vl);
|
||||
}
|
||||
struct line_range lr(this->ss_cursor_column.value(),
|
||||
this->ss_cursor_column.value() + 1);
|
||||
|
||||
struct line_range lr(this->ss_cursor_column,
|
||||
this->ss_cursor_column + 1);
|
||||
|
||||
current = find_string_attr(sa, lr);
|
||||
auto current = find_string_attr(sa, lr);
|
||||
|
||||
if (current != sa.end()) {
|
||||
if (ch == KEY_LEFT) {
|
||||
@ -141,27 +164,87 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
|
||||
vis_line_t row,
|
||||
attr_line_t& value_out)
|
||||
{
|
||||
if (y != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string& line = value_out.get_string();
|
||||
char buf[128];
|
||||
vis_line_t height;
|
||||
unsigned long width;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
width -= 2;
|
||||
|
||||
if (y > 0) {
|
||||
auto sel = lv.get_selection();
|
||||
auto selected_y = sel - lv.get_top() + 2;
|
||||
|
||||
if (y == selected_y && this->ss_cursor_column) {
|
||||
const auto& s_row = this->load_row(lv, sel);
|
||||
const auto& bucket
|
||||
= s_row.sr_values[this->ss_cursor_column.value()];
|
||||
auto& sb = this->ss_cached_bounds;
|
||||
spectrogram_request sr(sb);
|
||||
|
||||
auto sel_time = rounddown(sb.sb_begin_time, this->ss_granularity)
|
||||
+ sel * this->ss_granularity;
|
||||
sr.sr_width = width;
|
||||
sr.sr_begin_time = sel_time;
|
||||
sr.sr_end_time = sel_time + this->ss_granularity;
|
||||
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
|
||||
/ (double) (width - 1);
|
||||
auto range_min = sb.sb_min_value_out
|
||||
+ this->ss_cursor_column.value() * sr.sr_column_size;
|
||||
auto range_max = range_min + sr.sr_column_size;
|
||||
|
||||
const auto desc = fmt::format(
|
||||
FMT_STRING("{} value{} in the range {:.6Lg}-{:.6Lg} "),
|
||||
bucket.rb_counter,
|
||||
bucket.rb_counter == 1 ? "" : "s",
|
||||
range_min,
|
||||
range_max);
|
||||
auto mark_offset = this->ss_cursor_column.value();
|
||||
auto mark_is_before = true;
|
||||
|
||||
if (this->ss_cursor_column.value() + desc.size() > width) {
|
||||
mark_offset -= desc.size();
|
||||
mark_is_before = false;
|
||||
}
|
||||
value_out.append(mark_offset, ' ');
|
||||
if (mark_is_before) {
|
||||
value_out.append("\u25b2 ");
|
||||
}
|
||||
value_out.append(lnav::roles::comment(desc));
|
||||
if (!mark_is_before) {
|
||||
value_out.append("\u25b2 ");
|
||||
}
|
||||
|
||||
if (this->ss_details_view != nullptr) {
|
||||
if (s_row.sr_details_source_provider) {
|
||||
auto row_details_source = s_row.sr_details_source_provider(
|
||||
sr, range_min, range_max);
|
||||
|
||||
this->ss_details_view->set_sub_source(
|
||||
row_details_source.get());
|
||||
this->ss_details_source = std::move(row_details_source);
|
||||
} else {
|
||||
this->ss_details_view->set_sub_source(
|
||||
this->ss_no_details_source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& line = value_out.get_string();
|
||||
char buf[128];
|
||||
|
||||
this->cache_bounds();
|
||||
|
||||
if (this->ss_cached_line_count == 0) {
|
||||
value_out.with_ansi_string(ANSI_ROLE("error: no log data"),
|
||||
role_t::VCR_ERROR);
|
||||
value_out.append(lnav::roles::error("error: no log data"));
|
||||
return true;
|
||||
}
|
||||
|
||||
spectrogram_bounds& sb = this->ss_cached_bounds;
|
||||
spectrogram_thresholds& st = this->ss_cached_thresholds;
|
||||
auto& sb = this->ss_cached_bounds;
|
||||
auto& st = this->ss_cached_thresholds;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
|
||||
line = buf;
|
||||
@ -259,8 +342,7 @@ spectrogram_source::text_value_for_line(textview_curses& tc,
|
||||
std::string& value_out,
|
||||
text_sub_source::line_flags_t flags)
|
||||
{
|
||||
spectrogram_row& s_row = this->load_row(tc, row);
|
||||
|
||||
const auto& s_row = this->load_row(tc, row);
|
||||
char tm_buffer[128];
|
||||
struct tm tm;
|
||||
|
||||
@ -282,14 +364,6 @@ spectrogram_source::text_value_for_line(textview_curses& tc,
|
||||
value_out[lpc] = 'x';
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ss_cursor_top == row && this->ss_cursor_column != -1) {
|
||||
if (value_out[this->ss_cursor_column] == 'x') {
|
||||
value_out[this->ss_cursor_column] = '*';
|
||||
} else {
|
||||
value_out[this->ss_cursor_column] = '+';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -356,7 +430,7 @@ spectrogram_source::cache_bounds()
|
||||
= (diff + this->ss_granularity - 1) / this->ss_granularity;
|
||||
|
||||
int64_t samples_per_row = sb.sb_count / this->ss_cached_line_count;
|
||||
spectrogram_thresholds& st = this->ss_cached_thresholds;
|
||||
auto& st = this->ss_cached_thresholds;
|
||||
|
||||
st.st_yellow_threshold = samples_per_row / 2;
|
||||
st.st_green_threshold = st.st_yellow_threshold / 2;
|
||||
@ -369,8 +443,8 @@ spectrogram_source::cache_bounds()
|
||||
}
|
||||
}
|
||||
|
||||
spectrogram_row&
|
||||
spectrogram_source::load_row(textview_curses& tc, int row)
|
||||
const spectrogram_row&
|
||||
spectrogram_source::load_row(const listview_curses& tc, int row)
|
||||
{
|
||||
this->cache_bounds();
|
||||
|
||||
@ -380,12 +454,11 @@ spectrogram_source::load_row(textview_curses& tc, int row)
|
||||
tc.get_dimensions(height, width);
|
||||
width -= 2;
|
||||
|
||||
spectrogram_bounds& sb = this->ss_cached_bounds;
|
||||
auto& sb = this->ss_cached_bounds;
|
||||
spectrogram_request sr(sb);
|
||||
time_t row_time;
|
||||
|
||||
sr.sr_width = width;
|
||||
row_time = rounddown(sb.sb_begin_time, this->ss_granularity)
|
||||
auto row_time = rounddown(sb.sb_begin_time, this->ss_granularity)
|
||||
+ row * this->ss_granularity;
|
||||
sr.sr_begin_time = row_time;
|
||||
sr.sr_end_time = row_time + this->ss_granularity;
|
||||
@ -393,17 +466,67 @@ spectrogram_source::load_row(textview_curses& tc, int row)
|
||||
sr.sr_column_size
|
||||
= (sb.sb_max_value_out - sb.sb_min_value_out) / (double) (width - 1);
|
||||
|
||||
spectrogram_row& s_row = this->ss_row_cache[row_time];
|
||||
auto& s_row = this->ss_row_cache[row_time];
|
||||
|
||||
if (s_row.sr_values == nullptr || s_row.sr_width != width
|
||||
if (s_row.sr_values.empty() || s_row.sr_width != width
|
||||
|| s_row.sr_column_size != sr.sr_column_size)
|
||||
{
|
||||
s_row.sr_width = width;
|
||||
s_row.sr_column_size = sr.sr_column_size;
|
||||
delete[] s_row.sr_values;
|
||||
s_row.sr_values = new spectrogram_row::row_bucket[width + 1];
|
||||
s_row.sr_values.clear();
|
||||
s_row.sr_values.resize(width + 1);
|
||||
this->ss_value_source->spectro_row(sr, s_row);
|
||||
}
|
||||
|
||||
return s_row;
|
||||
}
|
||||
|
||||
bool
|
||||
spectrogram_source::text_is_row_selectable(textview_curses& tc, vis_line_t row)
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& s_row = this->load_row(tc, row);
|
||||
auto nearest_column
|
||||
= s_row.nearest_column(this->ss_cursor_column.value_or(0));
|
||||
|
||||
return nearest_column.has_value();
|
||||
}
|
||||
|
||||
void
|
||||
spectrogram_source::text_selection_changed(textview_curses& tc)
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& s_row = this->load_row(tc, tc.get_selection());
|
||||
this->ss_cursor_column
|
||||
= s_row.nearest_column(this->ss_cursor_column.value_or(0));
|
||||
}
|
||||
|
||||
spectro_status_source::spectro_status_source()
|
||||
{
|
||||
this->sss_fields[F_TITLE].set_width(9);
|
||||
this->sss_fields[F_TITLE].set_role(role_t::VCR_STATUS_TITLE);
|
||||
this->sss_fields[F_TITLE].set_value(" Details ");
|
||||
|
||||
this->sss_fields[F_HELP].right_justify(true);
|
||||
this->sss_fields[F_HELP].set_width(20);
|
||||
this->sss_fields[F_HELP].set_value("Press " ANSI_BOLD("TAB") " to focus ");
|
||||
this->sss_fields[F_HELP].set_left_pad(1);
|
||||
}
|
||||
|
||||
size_t
|
||||
spectro_status_source::statusview_fields()
|
||||
{
|
||||
return F_MAX;
|
||||
}
|
||||
|
||||
status_field&
|
||||
spectro_status_source::statusview_value_for_field(int field)
|
||||
{
|
||||
return this->sss_fields[field];
|
||||
}
|
||||
|
@ -32,14 +32,17 @@
|
||||
#ifndef spectro_source_hh
|
||||
#define spectro_source_hh
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "statusview_curses.hh"
|
||||
#include "textview_curses.hh"
|
||||
|
||||
struct exec_context;
|
||||
|
||||
struct spectrogram_bounds {
|
||||
time_t sb_begin_time{0};
|
||||
time_t sb_end_time{0};
|
||||
@ -64,30 +67,33 @@ struct spectrogram_request {
|
||||
};
|
||||
|
||||
struct spectrogram_row {
|
||||
~spectrogram_row()
|
||||
{
|
||||
delete[] this->sr_values;
|
||||
}
|
||||
spectrogram_row() = default;
|
||||
spectrogram_row(const spectrogram_row&) = delete;
|
||||
|
||||
struct row_bucket {
|
||||
int rb_counter{0};
|
||||
int rb_marks{0};
|
||||
};
|
||||
|
||||
row_bucket* sr_values{nullptr};
|
||||
std::vector<row_bucket> sr_values;
|
||||
unsigned long sr_width{0};
|
||||
double sr_column_size{0.0};
|
||||
std::function<std::unique_ptr<text_sub_source>(
|
||||
const spectrogram_request&, double range_min, double range_max)>
|
||||
sr_details_source_provider;
|
||||
|
||||
void add_value(spectrogram_request& sr, double value, bool marked)
|
||||
{
|
||||
long index = lrint((value - sr.sr_bounds.sb_min_value_out)
|
||||
/ sr.sr_column_size);
|
||||
long index = std::floor((value - sr.sr_bounds.sb_min_value_out)
|
||||
/ sr.sr_column_size);
|
||||
|
||||
this->sr_values[index].rb_counter += 1;
|
||||
if (marked) {
|
||||
this->sr_values[index].rb_marks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
nonstd::optional<size_t> nearest_column(size_t current) const;
|
||||
};
|
||||
|
||||
class spectrogram_value_source {
|
||||
@ -119,7 +125,7 @@ public:
|
||||
{
|
||||
this->ss_cached_bounds.sb_count = 0;
|
||||
this->ss_row_cache.clear();
|
||||
this->ss_cursor_column = -1;
|
||||
this->ss_cursor_column = nonstd::nullopt;
|
||||
}
|
||||
|
||||
bool list_input_handle_key(listview_curses& lv, int ch) override;
|
||||
@ -141,6 +147,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool text_is_row_selectable(textview_curses& tc, vis_line_t row) override;
|
||||
|
||||
void text_selection_changed(textview_curses& tc) override;
|
||||
|
||||
nonstd::optional<struct timeval> time_for_row(vis_line_t row) override;
|
||||
|
||||
nonstd::optional<vis_line_t> row_for_time(
|
||||
@ -157,16 +167,38 @@ public:
|
||||
|
||||
void cache_bounds();
|
||||
|
||||
spectrogram_row& load_row(textview_curses& tc, int row);
|
||||
const spectrogram_row& load_row(const listview_curses& lv, int row);
|
||||
|
||||
textview_curses* ss_details_view;
|
||||
text_sub_source* ss_no_details_source;
|
||||
exec_context* ss_exec_context;
|
||||
std::unique_ptr<text_sub_source> ss_details_source;
|
||||
int ss_granularity{60};
|
||||
spectrogram_value_source* ss_value_source{nullptr};
|
||||
spectrogram_bounds ss_cached_bounds;
|
||||
spectrogram_thresholds ss_cached_thresholds;
|
||||
size_t ss_cached_line_count{0};
|
||||
std::map<time_t, spectrogram_row> ss_row_cache;
|
||||
vis_line_t ss_cursor_top;
|
||||
int ss_cursor_column{-1};
|
||||
std::unordered_map<time_t, spectrogram_row> ss_row_cache;
|
||||
nonstd::optional<size_t> ss_cursor_column;
|
||||
};
|
||||
|
||||
class spectro_status_source : public status_data_source {
|
||||
public:
|
||||
enum field_t {
|
||||
F_TITLE,
|
||||
F_HELP,
|
||||
|
||||
F_MAX
|
||||
};
|
||||
|
||||
spectro_status_source();
|
||||
|
||||
size_t statusview_fields() override;
|
||||
|
||||
status_field& statusview_value_for_field(int field) override;
|
||||
|
||||
private:
|
||||
status_field sss_fields[F_MAX];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "textview_curses.hh"
|
||||
|
||||
#include "base/ansi_scrubber.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/time_util.hh"
|
||||
#include "config.h"
|
||||
#include "data_parser.hh"
|
||||
@ -609,7 +610,8 @@ textview_curses::execute_search(const std::string& regex_orig)
|
||||
highlight_map_t& hm = this->get_highlights();
|
||||
hm[{highlight_source_t::PREVIEW, "search"}] = hl;
|
||||
|
||||
auto gp = std::make_unique<grep_proc<vis_line_t>>(code, *this);
|
||||
auto gp = injector::get<std::shared_ptr<grep_proc<vis_line_t>>>(
|
||||
code, *this);
|
||||
|
||||
gp->set_sink(this);
|
||||
auto top = this->get_top();
|
||||
@ -624,13 +626,14 @@ textview_curses::execute_search(const std::string& regex_orig)
|
||||
}
|
||||
gp->start();
|
||||
|
||||
this->tc_search_child = std::make_unique<grep_highlighter>(
|
||||
this->tc_search_child = std::make_shared<grep_highlighter>(
|
||||
gp, highlight_source_t::PREVIEW, "search", hm);
|
||||
|
||||
if (this->tc_sub_source != nullptr) {
|
||||
this->tc_sub_source->get_grepper() | [this, code](auto pair) {
|
||||
auto sgp = std::make_shared<grep_proc<vis_line_t>>(
|
||||
code, *pair.first);
|
||||
auto sgp
|
||||
= injector::get<std::shared_ptr<grep_proc<vis_line_t>>>(
|
||||
code, *pair.first);
|
||||
|
||||
sgp->set_sink(pair.second);
|
||||
sgp->queue_request(0_vl);
|
||||
@ -790,6 +793,39 @@ textview_curses::redo_search()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
textview_curses::listview_is_row_selectable(const listview_curses& lv,
|
||||
vis_line_t row)
|
||||
{
|
||||
if (this->tc_sub_source != nullptr) {
|
||||
return this->tc_sub_source->text_is_row_selectable(*this, row);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
textview_curses::listview_selection_changed(const listview_curses& lv)
|
||||
{
|
||||
if (this->tc_sub_source != nullptr) {
|
||||
this->tc_sub_source->text_selection_changed(*this);
|
||||
}
|
||||
}
|
||||
|
||||
textview_curses&
|
||||
textview_curses::set_sub_source(text_sub_source* src)
|
||||
{
|
||||
if (this->tc_sub_source != src) {
|
||||
this->tc_bookmarks.clear();
|
||||
this->tc_sub_source = src;
|
||||
if (src) {
|
||||
src->register_view(this);
|
||||
}
|
||||
this->reload_data();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
text_time_translator::scroll_invoked(textview_curses* tc)
|
||||
{
|
||||
|
@ -292,6 +292,13 @@ public:
|
||||
|
||||
virtual size_t text_line_width(textview_curses& curses) { return INT_MAX; }
|
||||
|
||||
virtual bool text_is_row_selectable(textview_curses& tc, vis_line_t row)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void text_selection_changed(textview_curses& tc) {}
|
||||
|
||||
/**
|
||||
* Get the value for a line.
|
||||
*
|
||||
@ -490,15 +497,7 @@ public:
|
||||
|
||||
void set_user_mark(const bookmark_type_t* bm, vis_line_t vl, bool marked);
|
||||
|
||||
textview_curses& set_sub_source(text_sub_source* src)
|
||||
{
|
||||
this->tc_sub_source = src;
|
||||
if (src) {
|
||||
src->register_view(this);
|
||||
}
|
||||
this->reload_data();
|
||||
return *this;
|
||||
}
|
||||
textview_curses& set_sub_source(text_sub_source* src);
|
||||
|
||||
text_sub_source* get_sub_source() const { return this->tc_sub_source; }
|
||||
|
||||
@ -547,6 +546,10 @@ public:
|
||||
|
||||
void textview_value_for_row(vis_line_t line, attr_line_t& value_out);
|
||||
|
||||
bool listview_is_row_selectable(const listview_curses& lv, vis_line_t row);
|
||||
|
||||
void listview_selection_changed(const listview_curses& lv);
|
||||
|
||||
size_t listview_size_for_row(const listview_curses& lv, vis_line_t row)
|
||||
{
|
||||
return this->tc_sub_source->text_size_for_line(*this, row);
|
||||
@ -669,26 +672,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void update_poll_set(std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->update_poll_set(pollfds);
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->update_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
void check_poll_set(const std::vector<struct pollfd>& pollfds)
|
||||
{
|
||||
if (this->tc_search_child) {
|
||||
this->tc_search_child->get_grep_proc()->check_poll_set(pollfds);
|
||||
}
|
||||
if (this->tc_source_search_child) {
|
||||
this->tc_source_search_child->check_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_current_search() const
|
||||
{
|
||||
return this->tc_current_search;
|
||||
@ -717,7 +700,7 @@ public:
|
||||
protected:
|
||||
class grep_highlighter {
|
||||
public:
|
||||
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>>& gp,
|
||||
grep_highlighter(std::shared_ptr<grep_proc<vis_line_t>>& gp,
|
||||
highlight_source_t source,
|
||||
std::string hl_name,
|
||||
highlight_map_t& hl_map)
|
||||
@ -738,7 +721,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
|
||||
std::shared_ptr<grep_proc<vis_line_t>> gh_grep_proc;
|
||||
highlight_source_t gh_hl_source;
|
||||
std::string gh_hl_name;
|
||||
highlight_map_t& gh_hl_map;
|
||||
@ -768,7 +751,7 @@ protected:
|
||||
|
||||
std::string tc_current_search;
|
||||
std::string tc_previous_search;
|
||||
std::unique_ptr<grep_highlighter> tc_search_child;
|
||||
std::shared_ptr<grep_highlighter> tc_search_child;
|
||||
std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child;
|
||||
};
|
||||
|
||||
|
@ -535,8 +535,11 @@ layout_views()
|
||||
bool preview_status_open
|
||||
= !lnav_data.ld_preview_status_source.get_description().empty();
|
||||
bool filter_status_open = false;
|
||||
auto is_spectro = false;
|
||||
|
||||
lnav_data.ld_view_stack.top() | [&](auto tc) {
|
||||
is_spectro = (tc == &lnav_data.ld_views[LNV_SPECTRO]);
|
||||
|
||||
text_sub_source* tss = tc->get_sub_source();
|
||||
|
||||
if (tss == nullptr) {
|
||||
@ -601,7 +604,8 @@ layout_views()
|
||||
|
||||
int bottom_height = (doc_open ? 1 : 0) + doc_height
|
||||
+ (preview_status_open ? 1 : 0) + preview_height + 1 // bottom status
|
||||
+ match_height + um_height + lnav_data.ld_rl_view->get_height();
|
||||
+ match_height + um_height + lnav_data.ld_rl_view->get_height()
|
||||
+ (is_spectro && !doc_open ? 5 : 0);
|
||||
|
||||
for (auto& tc : lnav_data.ld_views) {
|
||||
tc.set_height(vis_line_t(-(bottom_height + (filter_status_open ? 1 : 0)
|
||||
@ -622,6 +626,10 @@ layout_views()
|
||||
lnav_data.ld_status[LNS_PREVIEW].set_top(height - bottom_height
|
||||
+ (doc_open ? 1 : 0) + doc_height);
|
||||
lnav_data.ld_status[LNS_PREVIEW].set_visible(preview_status_open);
|
||||
lnav_data.ld_status[LNS_SPECTRO].set_top(height - bottom_height - 1);
|
||||
lnav_data.ld_status[LNS_SPECTRO].set_visible(is_spectro);
|
||||
lnav_data.ld_status[LNS_SPECTRO].set_enabled(lnav_data.ld_mode
|
||||
== ln_mode_t::SPECTRO_DETAILS);
|
||||
|
||||
if (!doc_open || doc_side_by_side) {
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(doc_height));
|
||||
@ -656,6 +664,13 @@ layout_views()
|
||||
+ (doc_open ? 1 : 0) + doc_height);
|
||||
lnav_data.ld_user_message_view.set_y(
|
||||
height - lnav_data.ld_rl_view->get_height() - match_height - um_height);
|
||||
|
||||
lnav_data.ld_spectro_details_view.set_y(height - bottom_height);
|
||||
lnav_data.ld_spectro_details_view.set_height(
|
||||
is_spectro && !doc_open ? 5_vl : 0_vl);
|
||||
lnav_data.ld_spectro_details_view.set_width(width);
|
||||
lnav_data.ld_spectro_details_view.set_title("spectro-details");
|
||||
|
||||
lnav_data.ld_match_view.set_y(height - lnav_data.ld_rl_view->get_height()
|
||||
- match_height);
|
||||
lnav_data.ld_rl_view->set_width(width);
|
||||
@ -893,9 +908,9 @@ next_cluster(nonstd::optional<vis_line_t> (bookmark_vector<vis_line_t>::*f)(
|
||||
const bookmark_type_t* bt,
|
||||
const vis_line_t top)
|
||||
{
|
||||
textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode);
|
||||
vis_bookmarks& bm = tc->get_bookmarks();
|
||||
bookmark_vector<vis_line_t>& bv = bm[bt];
|
||||
auto* tc = get_textview_for_mode(lnav_data.ld_mode);
|
||||
auto& bm = tc->get_bookmarks();
|
||||
auto& bv = bm[bt];
|
||||
bool top_is_marked = binary_search(bv.begin(), bv.end(), top);
|
||||
vis_line_t last_top(top), tc_height;
|
||||
nonstd::optional<vis_line_t> new_top = top;
|
||||
@ -1009,7 +1024,7 @@ search_forward_from(textview_curses* tc)
|
||||
{
|
||||
vis_line_t height,
|
||||
retval = tc->is_selectable() ? tc->get_selection() : tc->get_top();
|
||||
key_repeat_history& krh = lnav_data.ld_key_repeat_history;
|
||||
auto& krh = lnav_data.ld_key_repeat_history;
|
||||
unsigned long width;
|
||||
|
||||
tc->get_dimensions(height, width);
|
||||
@ -1031,6 +1046,9 @@ get_textview_for_mode(ln_mode_t mode)
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::FILES:
|
||||
return &lnav_data.ld_files_view;
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
return &lnav_data.ld_spectro_details_view;
|
||||
default:
|
||||
return *lnav_data.ld_view_stack.top();
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ enum class ln_mode_t : int {
|
||||
BREADCRUMBS,
|
||||
FILTER,
|
||||
FILES,
|
||||
SPECTRO_DETAILS,
|
||||
SEARCH_SPECTRO_DETAILS,
|
||||
COMMAND,
|
||||
SEARCH,
|
||||
SEARCH_FILTERS,
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
retval = read_result.isOk();
|
||||
}
|
||||
}
|
||||
} catch (line_buffer::error& e) {
|
||||
} catch (const line_buffer::error& e) {
|
||||
fprintf(stderr,
|
||||
"error: source buffer error %d %s\n",
|
||||
this->ms_buffer.get_fd(),
|
||||
@ -87,7 +87,7 @@ private:
|
||||
|
||||
class my_sink : public grep_proc_sink<vis_line_t> {
|
||||
public:
|
||||
my_sink() : ms_finished(false){};
|
||||
my_sink() : ms_finished(false) {}
|
||||
|
||||
void grep_match(grep_proc<vis_line_t>& gp,
|
||||
vis_line_t line,
|
||||
@ -95,7 +95,7 @@ public:
|
||||
int end)
|
||||
{
|
||||
printf("%d:%d:%d\n", (int) line, start, end);
|
||||
};
|
||||
}
|
||||
|
||||
void grep_capture(grep_proc<vis_line_t>& gp,
|
||||
vis_line_t line,
|
||||
@ -104,12 +104,9 @@ public:
|
||||
char* capture)
|
||||
{
|
||||
fprintf(stderr, "%d(%d:%d)%s\n", (int) line, start, end, capture);
|
||||
};
|
||||
}
|
||||
|
||||
void grep_end(grep_proc<vis_line_t>& gp)
|
||||
{
|
||||
this->ms_finished = true;
|
||||
};
|
||||
void grep_end(grep_proc<vis_line_t>& gp) { this->ms_finished = true; }
|
||||
|
||||
bool ms_finished;
|
||||
};
|
||||
@ -135,10 +132,11 @@ main(int argc, char* argv[])
|
||||
{
|
||||
fprintf(stderr, "error: invalid pattern -- %s\n", errptr);
|
||||
} else {
|
||||
auto psuperv = std::make_shared<pollable_supervisor>();
|
||||
my_source ms(fd);
|
||||
my_sink msink;
|
||||
|
||||
grep_proc<vis_line_t> gp(code, ms);
|
||||
grep_proc<vis_line_t> gp(code, ms, psuperv);
|
||||
|
||||
gp.set_sink(&msink);
|
||||
gp.queue_request();
|
||||
@ -147,10 +145,10 @@ main(int argc, char* argv[])
|
||||
while (!msink.ms_finished) {
|
||||
vector<struct pollfd> pollfds;
|
||||
|
||||
gp.update_poll_set(pollfds);
|
||||
psuperv->update_poll_set(pollfds);
|
||||
poll(&pollfds[0], pollfds.size(), -1);
|
||||
|
||||
gp.check_poll_set(pollfds);
|
||||
psuperv->check_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,8 +92,9 @@ main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
auto psuperv = std::make_shared<pollable_supervisor>();
|
||||
readline_context context("test", &COMMANDS);
|
||||
readline_curses rlc;
|
||||
readline_curses rlc(psuperv);
|
||||
|
||||
rlc.add_context(1, context);
|
||||
rlc.start();
|
||||
@ -119,7 +120,7 @@ main(int argc, char* argv[])
|
||||
int rc;
|
||||
|
||||
pollfds.push_back((struct pollfd){STDIN_FILENO, POLLIN, 0});
|
||||
rlc.update_poll_set(pollfds);
|
||||
psuperv->update_poll_set(pollfds);
|
||||
|
||||
rlc.do_update();
|
||||
refresh();
|
||||
@ -145,7 +146,7 @@ main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
rlc.check_poll_set(pollfds);
|
||||
psuperv->check_poll_set(pollfds);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,14 +384,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_sql.sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1ed6e8b3194bf16477cce9542fe1d9750560cbd1.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1ed6e8b3194bf16477cce9542fe1d9750560cbd1.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1fb2a7366f0f271643e0ee30d76e03b643ff7dce.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_1fb2a7366f0f271643e0ee30d76e03b643ff7dce.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_264a70cabc0609e47d9e39b23376321c9aec6e23.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_264a70cabc0609e47d9e39b23376321c9aec6e23.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.err \
|
||||
@ -408,8 +402,6 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_3f6b92e7948d40b9d2df06112f30ce899bb0d7f5.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_3f6b92e7948d40b9d2df06112f30ce899bb0d7f5.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.err \
|
||||
$(srcdir)/%reldir%/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.out \
|
||||
$(srcdir)/%reldir%/test_sql.sh_4629b626c65a85d7a5595571e195b67afca272ba.err \
|
||||
|
@ -1,3 +1,4 @@
|
||||
Min: 0 [42m [0m 1-23 [43m [0m 24-48 [41m [0m 49+ Max: 291690
|
||||
[41m [0m[43mT[0m[42mh[0m[42mu[0m[42m [0m[42mN[0mo[42mv[0m[42m [0m[42m0[0m[42m3[0m[42m [0m[42m0[0m[42m0[0m[42m:[0m1[42m5[0m[42m:[0m00[42m [0m [42m [0m[42m [0m [42m [0m[42m [0m [42m [0m [42m [0m [42m [0m [42m [0m
|
||||
[43m [0m[42mT[0m[42mh[0m[42mu[0m[42m [0m[42mN[0m[42mo[0mv[42m [0m03 [42m0[0m[42m0[0m:2[42m0[0m:00
|
||||
Min: 0 [42m [0m 1-23 [43m [0m 24-48 [41m [0m 49+ Max: 291690
|
||||
[41m [0m[43mT[0m[42mh[0m[42mu[0m[42m [0m[42mN[0m[42mo[0m[42mv[0m[42m [0m[42m0[0m[42m3[0m[42m [0m[42m0[0m[42m0[0m:1[42m5[0m:00[42m [0m [42m [0m[42m [0m [42m [0m[42m [0m [42m [0m [42m [0m [42m [0m [42m [0m
|
||||
▲ [32m70 values in the range 0-3788.18 [0m
|
||||
[43m [0m[42mT[0m[42mh[0m[42mu[0m[42m [0m[42mN[0m[42mo[0mv[42m [0m03 [42m0[0m0:[42m2[0m0:00
|
||||
|
@ -124,9 +124,10 @@ main(int argc, char* argv[])
|
||||
pcre_refcount(code, 1);
|
||||
assert(code != NULL);
|
||||
|
||||
auto psuperv = std::make_shared<pollable_supervisor>();
|
||||
{
|
||||
my_source ms;
|
||||
grep_proc<vis_line_t> gp(code, ms);
|
||||
grep_proc<vis_line_t> gp(code, ms, psuperv);
|
||||
|
||||
gp.queue_request(10_vl, 14_vl);
|
||||
gp.queue_request(0_vl, 3_vl);
|
||||
@ -136,7 +137,8 @@ main(int argc, char* argv[])
|
||||
|
||||
{
|
||||
my_sleeper_source mss;
|
||||
grep_proc<vis_line_t>* gp = new grep_proc<vis_line_t>(code, mss);
|
||||
grep_proc<vis_line_t>* gp
|
||||
= new grep_proc<vis_line_t>(code, mss, psuperv);
|
||||
int status;
|
||||
|
||||
gp->queue_request();
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "config.h"
|
||||
#include "lnav.hh"
|
||||
#include "service_tags.hh"
|
||||
#include "spectro_source.hh"
|
||||
|
||||
struct lnav_data_t lnav_data;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user