mirror of
https://github.com/tstack/lnav.git
synced 2024-10-26 13:16:11 +03:00
[sql] do some minimal parsing/annotation of SQL statements
Defect Number: Reviewed By: Testing Done:
This commit is contained in:
parent
0b157ff867
commit
8776f6a703
2
NEWS
2
NEWS
@ -37,6 +37,8 @@ lnav v0.8.2:
|
||||
string.
|
||||
|
||||
Interface Changes:
|
||||
* Command documentation is now displayed in a section at the bottom of
|
||||
the screen when a command is being entered.
|
||||
* The color used for text colored via ":highlight" is now based on the
|
||||
the regex instead of randomly picked so that colors are consistent
|
||||
across invocations.
|
||||
|
@ -13,6 +13,7 @@ set(diag_STAT_SRCS
|
||||
file_vtab.cc
|
||||
fs-extension-functions.cc
|
||||
grep_proc.cc
|
||||
help_text_formatter.cc
|
||||
hist_source.cc
|
||||
hotkeys.cc
|
||||
intern_string.cc
|
||||
@ -52,11 +53,13 @@ set(diag_STAT_SRCS
|
||||
sysclip.cc
|
||||
pcrepp.cc
|
||||
piper_proc.cc
|
||||
ptimec.cc
|
||||
sql_util.cc
|
||||
state-extension-functions.cc
|
||||
strnatcmp.c
|
||||
text_format.cc
|
||||
textview_curses.cc
|
||||
time-extension-functions.cc
|
||||
timer.cc
|
||||
view_curses.cc
|
||||
views_vtab.cc
|
||||
@ -83,6 +86,7 @@ set(diag_STAT_SRCS
|
||||
spookyhash/SpookyV2.cpp
|
||||
|
||||
all_logs_vtab.hh
|
||||
attr_line.hh
|
||||
auto_fd.hh
|
||||
auto_mem.hh
|
||||
auto_pid.hh
|
||||
@ -93,6 +97,7 @@ set(diag_STAT_SRCS
|
||||
concise_index.hh
|
||||
column_namer.hh
|
||||
curl_looper.hh
|
||||
doc_status_source.hh
|
||||
elem_to_json.hh
|
||||
field_overlay_source.hh
|
||||
file_vtab.hh
|
||||
@ -100,6 +105,7 @@ set(diag_STAT_SRCS
|
||||
format-text-files.hh
|
||||
grep_highlighter.hh
|
||||
help.hh
|
||||
help_text_formatter.hh
|
||||
hotkeys.hh
|
||||
init-sql.hh
|
||||
intern_string.hh
|
||||
|
@ -120,6 +120,7 @@ dist_noinst_DATA = \
|
||||
noinst_HEADERS = \
|
||||
all_logs_vtab.hh \
|
||||
ansi_scrubber.hh \
|
||||
attr_line.hh \
|
||||
auto_fd.hh \
|
||||
auto_mem.hh \
|
||||
auto_pid.hh \
|
||||
@ -136,6 +137,7 @@ noinst_HEADERS = \
|
||||
data_parser.hh \
|
||||
default-log-formats-json.hh \
|
||||
db_sub_source.hh \
|
||||
doc_status_source.hh \
|
||||
elem_to_json.hh \
|
||||
environ_vtab.hh \
|
||||
field_overlay_source.hh \
|
||||
@ -146,6 +148,7 @@ noinst_HEADERS = \
|
||||
grep_proc.hh \
|
||||
help.hh \
|
||||
help.txt \
|
||||
help_text_formatter.hh \
|
||||
hist_source.hh \
|
||||
hotkeys.hh \
|
||||
init.sql \
|
||||
@ -252,6 +255,7 @@ libdiag_a_SOURCES = \
|
||||
file_vtab.cc \
|
||||
fs-extension-functions.cc \
|
||||
grep_proc.cc \
|
||||
help_text_formatter.cc \
|
||||
hist_source.cc \
|
||||
hotkeys.cc \
|
||||
intern_string.cc \
|
||||
|
423
src/attr_line.hh
Normal file
423
src/attr_line.hh
Normal file
@ -0,0 +1,423 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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.
|
||||
*
|
||||
* @file attr_line.hh
|
||||
*/
|
||||
|
||||
#ifndef __attr_line_hh
|
||||
#define __attr_line_hh
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Encapsulates a range in a string.
|
||||
*/
|
||||
struct line_range {
|
||||
int lr_start;
|
||||
int lr_end;
|
||||
|
||||
explicit line_range(int start = -1, int end = -1) : lr_start(start), lr_end(end) { };
|
||||
|
||||
bool is_valid() const {
|
||||
return this->lr_start != -1;
|
||||
}
|
||||
|
||||
int length() const
|
||||
{
|
||||
return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start;
|
||||
};
|
||||
|
||||
bool contains(int pos) const {
|
||||
return this->lr_start <= pos && pos < this->lr_end;
|
||||
};
|
||||
|
||||
bool contains(const struct line_range &other) const {
|
||||
return this->contains(other.lr_start) && other.lr_end <= this->lr_end;
|
||||
};
|
||||
|
||||
bool intersects(const struct line_range &other) const {
|
||||
return this->contains(other.lr_start) || this->contains(other.lr_end);
|
||||
};
|
||||
|
||||
line_range intersection(const struct line_range &other) const {
|
||||
return line_range{std::max(this->lr_start, other.lr_start),
|
||||
std::min(this->lr_end, other.lr_end)};
|
||||
};
|
||||
|
||||
line_range &shift(int32_t start, int32_t amount) {
|
||||
if (this->lr_start >= start) {
|
||||
this->lr_start = std::max(0, this->lr_start + amount);
|
||||
}
|
||||
if (this->lr_end != -1 && start < this->lr_end) {
|
||||
this->lr_end += amount;
|
||||
if (this->lr_end < this->lr_start) {
|
||||
this->lr_end = this->lr_start;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
};
|
||||
|
||||
void ltrim(const char *str) {
|
||||
while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) {
|
||||
this->lr_start += 1;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator<(const struct line_range &rhs) const
|
||||
{
|
||||
if (this->lr_start < rhs.lr_start) { return true; }
|
||||
else if (this->lr_start > rhs.lr_start) { return false; }
|
||||
|
||||
if (this->lr_end == rhs.lr_end) { return false; }
|
||||
|
||||
if (this->lr_end < rhs.lr_end) { return true; }
|
||||
return false;
|
||||
};
|
||||
|
||||
bool operator==(const struct line_range &rhs) const {
|
||||
return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end);
|
||||
};
|
||||
|
||||
const char *substr(const std::string &str) const {
|
||||
if (this->lr_start == -1) {
|
||||
return str.c_str();
|
||||
}
|
||||
return &(str.c_str()[this->lr_start]);
|
||||
}
|
||||
|
||||
size_t sublen(const std::string &str) const {
|
||||
if (this->lr_start == -1) {
|
||||
return str.length();
|
||||
}
|
||||
if (this->lr_end == -1) {
|
||||
return str.length() - this->lr_start;
|
||||
}
|
||||
return this->length();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for attribute values for a substring.
|
||||
*/
|
||||
typedef union {
|
||||
void *sav_ptr;
|
||||
int64_t sav_int;
|
||||
} string_attr_value_t;
|
||||
|
||||
class string_attr_type {
|
||||
public:
|
||||
string_attr_type(const char *name = nullptr)
|
||||
: sat_name(name) {
|
||||
};
|
||||
|
||||
const char *sat_name;
|
||||
};
|
||||
typedef string_attr_type *string_attr_type_t;
|
||||
|
||||
struct string_attr {
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, void *val)
|
||||
: sa_range(lr), sa_type(type) {
|
||||
this->sa_value.sav_ptr = val;
|
||||
};
|
||||
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, int64_t val = 0)
|
||||
: sa_range(lr), sa_type(type) {
|
||||
this->sa_value.sav_int = val;
|
||||
};
|
||||
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, string_attr_value_t val)
|
||||
: sa_range(lr), sa_type(type), sa_value(val) {
|
||||
};
|
||||
|
||||
string_attr() : sa_type(NULL) { };
|
||||
|
||||
bool operator<(const struct string_attr &rhs) const
|
||||
{
|
||||
return this->sa_range < rhs.sa_range;
|
||||
};
|
||||
|
||||
struct line_range sa_range;
|
||||
string_attr_type_t sa_type;
|
||||
string_attr_value_t sa_value;
|
||||
};
|
||||
|
||||
/** A map of line ranges to attributes for that range. */
|
||||
typedef std::vector<string_attr> string_attrs_t;
|
||||
|
||||
inline string_attrs_t::const_iterator
|
||||
find_string_attr(const string_attrs_t &sa, string_attr_type_t type, int start = 0)
|
||||
{
|
||||
string_attrs_t::const_iterator iter;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
if (iter->sa_type == type && iter->sa_range.lr_start >= start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
inline string_attrs_t::iterator
|
||||
find_string_attr(string_attrs_t &sa, const struct line_range &lr)
|
||||
{
|
||||
string_attrs_t::iterator iter;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
if (lr.contains(iter->sa_range)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
inline string_attrs_t::const_iterator
|
||||
find_string_attr(const string_attrs_t &sa, size_t near)
|
||||
{
|
||||
string_attrs_t::const_iterator iter, nearest = sa.end();
|
||||
ssize_t last_diff = INT_MAX;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
auto &lr = iter->sa_range;
|
||||
|
||||
if (!lr.is_valid() || !lr.contains(near)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ssize_t diff = near - lr.lr_start;
|
||||
if (diff < last_diff) {
|
||||
last_diff = diff;
|
||||
nearest = iter;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
inline struct line_range
|
||||
find_string_attr_range(const string_attrs_t &sa, string_attr_type_t type)
|
||||
{
|
||||
string_attrs_t::const_iterator iter = find_string_attr(sa, type);
|
||||
|
||||
if (iter != sa.end()) {
|
||||
return iter->sa_range;
|
||||
}
|
||||
|
||||
return line_range();
|
||||
}
|
||||
|
||||
inline void remove_string_attr(string_attrs_t &sa, const struct line_range &lr)
|
||||
{
|
||||
string_attrs_t::iterator iter;
|
||||
|
||||
while ((iter = find_string_attr(sa, lr)) != sa.end()) {
|
||||
sa.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
inline void remove_string_attr(string_attrs_t &sa, string_attr_type_t type)
|
||||
{
|
||||
string_attrs_t::iterator iter;
|
||||
|
||||
for (auto iter = sa.begin(); iter != sa.end();) {
|
||||
if (iter->sa_type == type) {
|
||||
iter = sa.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void shift_string_attrs(string_attrs_t &sa, int32_t start, int32_t amount)
|
||||
{
|
||||
for (string_attrs_t::iterator iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
iter->sa_range.shift(start, amount);
|
||||
}
|
||||
}
|
||||
|
||||
struct text_wrap_settings {
|
||||
text_wrap_settings() : tws_indent(2), tws_width(80) {};
|
||||
|
||||
text_wrap_settings &with_indent(int indent) {
|
||||
this->tws_indent = indent;
|
||||
return *this;
|
||||
};
|
||||
|
||||
text_wrap_settings &with_width(int width) {
|
||||
this->tws_width = width;
|
||||
return *this;
|
||||
};
|
||||
|
||||
int tws_indent;
|
||||
int tws_width;
|
||||
};
|
||||
|
||||
/**
|
||||
* A line that has attributes.
|
||||
*/
|
||||
class attr_line_t {
|
||||
public:
|
||||
attr_line_t() {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
attr_line_t(const std::string &str) : al_string(str) {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
attr_line_t(const char *str) : al_string(str) {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
static inline attr_line_t from_ansi_str(const char *str) {
|
||||
attr_line_t retval;
|
||||
|
||||
return retval.with_ansi_string(str);
|
||||
};
|
||||
|
||||
/** @return The string itself. */
|
||||
std::string &get_string() { return this->al_string; };
|
||||
|
||||
/** @return The attributes for the string. */
|
||||
string_attrs_t &get_attrs() { return this->al_attrs; };
|
||||
|
||||
attr_line_t &with_string(const std::string &str) {
|
||||
this->al_string = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t &with_ansi_string(const char *str, ...);
|
||||
|
||||
attr_line_t &with_attr(const string_attr &sa) {
|
||||
this->al_attrs.push_back(sa);
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<typename T = void *>
|
||||
attr_line_t &append(const char *str,
|
||||
string_attr_type_t type = nullptr,
|
||||
T val = T()) {
|
||||
size_t start_len = this->al_string.length();
|
||||
|
||||
this->al_string.append(str);
|
||||
if (type != nullptr) {
|
||||
line_range lr{(int) start_len, (int) this->al_string.length()};
|
||||
|
||||
this->al_attrs.emplace_back(lr, type, val);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
attr_line_t &append(const attr_line_t &al, text_wrap_settings *tws = nullptr);
|
||||
|
||||
attr_line_t &append(size_t len, char c) {
|
||||
this->al_string.append(len, c);
|
||||
return *this;
|
||||
};
|
||||
|
||||
attr_line_t &insert(size_t index, size_t len, char c) {
|
||||
this->al_string.insert(index, len, c);
|
||||
|
||||
shift_string_attrs(this->al_attrs, index, len);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t &insert(size_t index, const char *str) {
|
||||
this->al_string.insert(index, str);
|
||||
|
||||
shift_string_attrs(this->al_attrs, index, strlen(str));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t &right_justify(unsigned long width) {
|
||||
long padding = width - this->length();
|
||||
if (padding > 0) {
|
||||
this->al_string.insert(0, padding, ' ');
|
||||
for (std::vector<string_attr>::iterator iter = this->al_attrs.begin();
|
||||
iter != this->al_attrs.end();
|
||||
++iter) {
|
||||
iter->sa_range.lr_start += padding;
|
||||
iter->sa_range.lr_end += padding;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ssize_t length() const {
|
||||
size_t retval = this->al_string.length();
|
||||
|
||||
for (std::vector<string_attr>::const_iterator iter = this->al_attrs.begin();
|
||||
iter != this->al_attrs.end();
|
||||
++iter) {
|
||||
retval = std::max(retval, (size_t) iter->sa_range.lr_start);
|
||||
if (iter->sa_range.lr_end != -1) {
|
||||
retval = std::max(retval, (size_t) iter->sa_range.lr_end);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::string get_substring(const line_range &lr) const {
|
||||
if (!lr.is_valid()) {
|
||||
return "";
|
||||
}
|
||||
return this->al_string.substr(lr.lr_start, lr.length());
|
||||
};
|
||||
|
||||
bool empty() const {
|
||||
return this->length() == 0;
|
||||
};
|
||||
|
||||
/** Clear the string and the attributes for the string. */
|
||||
attr_line_t &clear()
|
||||
{
|
||||
this->al_string.clear();
|
||||
this->al_attrs.clear();
|
||||
|
||||
return *this;
|
||||
};
|
||||
|
||||
attr_line_t subline(size_t start, size_t len = std::string::npos) const;
|
||||
|
||||
void split_lines(std::vector<attr_line_t> &lines) const;
|
||||
|
||||
private:
|
||||
const static size_t RESERVE_SIZE = 128;
|
||||
|
||||
std::string al_string;
|
||||
string_attrs_t al_attrs;
|
||||
};
|
||||
|
||||
#endif
|
@ -78,8 +78,10 @@ public:
|
||||
this->bss_fields[BSF_HELP].set_width(14);
|
||||
this->bss_fields[BSF_HELP].set_value("?:View Help");
|
||||
this->bss_fields[BSF_HELP].right_justify(true);
|
||||
this->bss_prompt.set_left_pad(1);
|
||||
this->bss_prompt.set_min_width(35);
|
||||
this->bss_prompt.set_share(1);
|
||||
this->bss_error.set_left_pad(1);
|
||||
this->bss_error.set_min_width(35);
|
||||
this->bss_error.set_share(1);
|
||||
};
|
||||
@ -134,10 +136,10 @@ public:
|
||||
status_field &sf = this->bss_fields[BSF_LINE_NUMBER];
|
||||
|
||||
if (lc->get_inner_height() == 0) {
|
||||
sf.set_value("L0");
|
||||
sf.set_value(" L0");
|
||||
}
|
||||
else {
|
||||
sf.set_value("L%'d", (int)lc->get_top());
|
||||
sf.set_value(" L%'d", (int)lc->get_top());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -28,42 +28,52 @@
|
||||
"c_ip" : {
|
||||
"kind" : "string",
|
||||
"collate" : "ipaddress",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The client IP address"
|
||||
},
|
||||
"cs_username" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The username passed from the client to the server"
|
||||
},
|
||||
"cs_method" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The request method"
|
||||
},
|
||||
"cs_uri_stem" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The path part of the request URI"
|
||||
},
|
||||
"cs_uri_query" : {
|
||||
"kind" : "string"
|
||||
"kind" : "string",
|
||||
"description" : "The query parameters in the request URI"
|
||||
},
|
||||
"cs_version" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The client's HTTP version"
|
||||
},
|
||||
"sc_status" : {
|
||||
"kind" : "integer",
|
||||
"foreign-key" : true,
|
||||
"rewriter" : ";SELECT :sc_status || ' (' || (SELECT message FROM http_status_codes WHERE status = :sc_status) || ') '"
|
||||
"rewriter" : ";SELECT :sc_status || ' (' || (SELECT message FROM http_status_codes WHERE status = :sc_status) || ') '",
|
||||
"description" : "The status code returned by the server"
|
||||
},
|
||||
"sc_bytes" : {
|
||||
"kind" : "integer"
|
||||
"kind" : "integer",
|
||||
"description" : "The number of bytes returned by the server"
|
||||
},
|
||||
"cs_referer" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The client's referrer"
|
||||
},
|
||||
"cs_user_agent" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The client's HTTP agent"
|
||||
}
|
||||
},
|
||||
"sample" : [
|
||||
@ -96,11 +106,13 @@
|
||||
"pid" : {
|
||||
"kind" : "integer",
|
||||
"identifier" : true,
|
||||
"foreign-key" : true
|
||||
"foreign-key" : true,
|
||||
"description" : "The ID of the process that generated the message"
|
||||
},
|
||||
"module" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The name of the module that generated the message"
|
||||
}
|
||||
},
|
||||
"sample" : [
|
||||
@ -972,16 +984,19 @@
|
||||
"log_hostname" : {
|
||||
"kind" : "string",
|
||||
"collate" : "ipaddress",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The name of the host that generated the message"
|
||||
},
|
||||
"log_procname" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true
|
||||
"identifier" : true,
|
||||
"description" : "The name of the process that generated the message"
|
||||
},
|
||||
"log_pid" : {
|
||||
"kind" : "string",
|
||||
"identifier" : true,
|
||||
"action-list" : ["dump_pid"]
|
||||
"action-list" : ["dump_pid"],
|
||||
"description" : "The ID of the process that generated the message"
|
||||
}
|
||||
},
|
||||
"action" : {
|
||||
|
70
src/doc_status_source.hh
Normal file
70
src/doc_status_source.hh
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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 _doc_status_source_hh
|
||||
#define _doc_status_source_hh
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lnav_config.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
#include "statusview_curses.hh"
|
||||
|
||||
class doc_status_source
|
||||
: public status_data_source {
|
||||
public:
|
||||
typedef enum {
|
||||
TSF_TITLE,
|
||||
TSF_STITCH_TITLE,
|
||||
|
||||
TSF__MAX
|
||||
} field_t;
|
||||
|
||||
doc_status_source()
|
||||
{
|
||||
this->tss_fields[TSF_TITLE].set_width(14);
|
||||
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_VIEW_STATUS);
|
||||
this->tss_fields[TSF_TITLE].set_value(" Command Help ");
|
||||
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
|
||||
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
|
||||
view_colors::ansi_color_pair_index(COLOR_BLUE, COLOR_WHITE));
|
||||
};
|
||||
|
||||
size_t statusview_fields(void) { return TSF__MAX; };
|
||||
|
||||
status_field &statusview_value_for_field(int field)
|
||||
{
|
||||
return this->tss_fields[field];
|
||||
};
|
||||
|
||||
private:
|
||||
status_field tss_fields[TSF__MAX];
|
||||
};
|
||||
|
||||
#endif
|
@ -76,19 +76,12 @@ sql_basename(const char *path_in)
|
||||
}
|
||||
}
|
||||
|
||||
static void sql_dirname(sqlite3_context *context,
|
||||
int argc, sqlite3_value **argv)
|
||||
static
|
||||
util::variant<const char *, string_fragment>
|
||||
sql_dirname(const char *path_in)
|
||||
{
|
||||
const char *path_in;
|
||||
ssize_t text_end;
|
||||
|
||||
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
|
||||
sqlite3_result_null(context);
|
||||
return;
|
||||
}
|
||||
|
||||
path_in = (const char *)sqlite3_value_text(argv[0]);
|
||||
|
||||
text_end = strlen(path_in) - 1;
|
||||
while (text_end >= 0 &&
|
||||
(path_in[text_end] == '/' || path_in[text_end] == '\\')) {
|
||||
@ -97,18 +90,13 @@ static void sql_dirname(sqlite3_context *context,
|
||||
|
||||
while (text_end >= 0) {
|
||||
if (path_in[text_end] == '/' || path_in[text_end] == '\\') {
|
||||
sqlite3_result_text(context,
|
||||
path_in, (int) (text_end == 0 ? 1 : text_end),
|
||||
SQLITE_TRANSIENT);
|
||||
return;
|
||||
return string_fragment(path_in, 0, text_end == 0 ? 1 : text_end);
|
||||
}
|
||||
|
||||
text_end -= 1;
|
||||
}
|
||||
|
||||
sqlite3_result_text(context,
|
||||
path_in[0] == '/' ? "/" : ".", 1,
|
||||
SQLITE_STATIC);
|
||||
return path_in[0] == '/' ? "/" : ".";
|
||||
}
|
||||
|
||||
static void sql_joinpath(sqlite3_context *context,
|
||||
@ -160,7 +148,13 @@ int fs_extension_functions(const struct FuncDef **basic_funcs,
|
||||
{"path", "The path"},
|
||||
}),
|
||||
|
||||
{ "dirname", 1, 0, SQLITE_UTF8, 0, sql_dirname },
|
||||
sqlite_func_adapter<decltype(&sql_dirname), sql_dirname>::builder(
|
||||
"dirname",
|
||||
"Extract the directory portion of a pathname",
|
||||
{
|
||||
{"path", "The path"},
|
||||
}),
|
||||
|
||||
{ "joinpath", -1, 0, SQLITE_UTF8, 0, sql_joinpath },
|
||||
|
||||
/*
|
||||
|
147
src/help_text_formatter.cc
Normal file
147
src/help_text_formatter.cc
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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 "config.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
|
||||
#include "ansi_scrubber.hh"
|
||||
#include "help_text_formatter.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
|
||||
{
|
||||
static size_t body_indent = 2;
|
||||
|
||||
text_wrap_settings tws;
|
||||
|
||||
tws.with_width(width);
|
||||
|
||||
switch (ht.ht_context) {
|
||||
case HC_COMMAND: {
|
||||
out.append("Synopsis", &view_curses::VC_STYLE, A_UNDERLINE)
|
||||
.append("\n")
|
||||
.append(body_indent, ' ')
|
||||
.append(":")
|
||||
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
|
||||
for (auto ¶m : ht.ht_parameters) {
|
||||
out.append(" ");
|
||||
out.append(param.ht_name, &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
if (param.ht_nargs == HN_ONE_OR_MORE) {
|
||||
out.append("1", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
out.append(" [");
|
||||
out.append("...", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
out.append(" ");
|
||||
out.append(param.ht_name, &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
out.append("N", &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
out.append("]");
|
||||
}
|
||||
}
|
||||
out.append(" - ")
|
||||
.append(attr_line_t::from_ansi_str(ht.ht_summary),
|
||||
&tws.with_indent(body_indent + 4))
|
||||
.append("\n");
|
||||
break;
|
||||
}
|
||||
case HC_SQL_FUNCTION: {
|
||||
bool needs_comma = false;
|
||||
|
||||
out.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
|
||||
out.append("(");
|
||||
for (auto ¶m : ht.ht_parameters) {
|
||||
if (needs_comma) {
|
||||
out.append(", ");
|
||||
}
|
||||
out.append(param.ht_name, &view_curses::VC_STYLE, A_UNDERLINE);
|
||||
needs_comma = true;
|
||||
}
|
||||
out.append(") -- ")
|
||||
.append(attr_line_t::from_ansi_str(ht.ht_summary), &tws)
|
||||
.append("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ht.ht_parameters.empty()) {
|
||||
size_t max_param_name_width = 0;
|
||||
|
||||
for (auto ¶m : ht.ht_parameters) {
|
||||
max_param_name_width = std::max(strlen(param.ht_name), max_param_name_width);
|
||||
}
|
||||
|
||||
out.append(ht.ht_parameters.size() == 1 ? "Parameter" : "Parameters",
|
||||
&view_curses::VC_STYLE,
|
||||
A_UNDERLINE)
|
||||
.append("\n");
|
||||
|
||||
for (auto ¶m : ht.ht_parameters) {
|
||||
out.append(body_indent, ' ')
|
||||
.append(param.ht_name,
|
||||
&view_curses::VC_STYLE,
|
||||
view_colors::ansi_color_pair(COLOR_CYAN, COLOR_BLACK) | A_BOLD)
|
||||
.append(max_param_name_width - strlen(param.ht_name), ' ')
|
||||
.append(" ")
|
||||
.append(attr_line_t::from_ansi_str(param.ht_summary),
|
||||
&(tws.with_indent(2 + max_param_name_width + 3)))
|
||||
.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ht.ht_example.empty()) {
|
||||
map<string, string> vars;
|
||||
|
||||
vars["name"] = ht.ht_name;
|
||||
add_ansi_vars(vars);
|
||||
|
||||
out.append(ht.ht_example.size() == 1 ? "Example" : "Examples",
|
||||
&view_curses::VC_STYLE,
|
||||
A_UNDERLINE)
|
||||
.append("\n");
|
||||
for (auto &ex : ht.ht_example) {
|
||||
attr_line_t ex_line(ex.he_cmd);
|
||||
|
||||
switch (ht.ht_context) {
|
||||
case HC_COMMAND:
|
||||
ex_line.insert(0, 1, ' ');
|
||||
ex_line.insert(0, 1, ':');
|
||||
ex_line.insert(1, ht.ht_name);
|
||||
readline_command_highlighter(ex_line, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
out.append(body_indent, ' ')
|
||||
.append(ex_line, &tws.with_indent(body_indent + 2));
|
||||
out.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
156
src/help_text_formatter.hh
Normal file
156
src/help_text_formatter.hh
Normal file
@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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_HELP_TEXT_FORMATTER_HH
|
||||
#define LNAV_HELP_TEXT_FORMATTER_HH
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "attr_line.hh"
|
||||
|
||||
enum help_context_t {
|
||||
HC_NONE,
|
||||
HC_PARAMETER,
|
||||
HC_COMMAND,
|
||||
HC_SQL_FUNCTION,
|
||||
};
|
||||
|
||||
enum help_nargs_t {
|
||||
HN_REQUIRED,
|
||||
HN_OPTIONAL,
|
||||
HN_ZERO_OR_MORE,
|
||||
HN_ONE_OR_MORE,
|
||||
};
|
||||
|
||||
enum help_parameter_format_t {
|
||||
HPF_STRING,
|
||||
HPF_REGEX,
|
||||
HPF_INTEGER,
|
||||
HPF_NUMBER,
|
||||
HPF_DATETIME,
|
||||
HPF_ENUM,
|
||||
};
|
||||
|
||||
struct help_example {
|
||||
const char *he_cmd;
|
||||
const char *he_result;
|
||||
};
|
||||
|
||||
struct help_text {
|
||||
help_context_t ht_context;
|
||||
const char *ht_name;
|
||||
const char *ht_summary;
|
||||
const char *ht_description;
|
||||
std::vector<struct help_text> ht_parameters;
|
||||
std::vector<struct help_example> ht_example;
|
||||
help_nargs_t ht_nargs;
|
||||
help_parameter_format_t ht_format;
|
||||
std::vector<const char *> ht_enum_values;
|
||||
|
||||
help_text() : ht_context(HC_NONE) {
|
||||
|
||||
};
|
||||
|
||||
help_text(const char *name, const char *summary = nullptr)
|
||||
: ht_context(HC_NONE),
|
||||
ht_name(name),
|
||||
ht_summary(summary),
|
||||
ht_description(nullptr),
|
||||
ht_nargs(HN_REQUIRED),
|
||||
ht_format(HPF_STRING) {
|
||||
if (name[0] == ':') {
|
||||
this->ht_context = HC_COMMAND;
|
||||
this->ht_name = &name[1];
|
||||
}
|
||||
}
|
||||
|
||||
help_text &command() {
|
||||
this->ht_context = HC_COMMAND;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &with_summary(const char *summary) {
|
||||
this->ht_summary = summary;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &with_parameters(const std::initializer_list<help_text> ¶ms) {
|
||||
this->ht_parameters = params;
|
||||
for (auto ¶m : this->ht_parameters) {
|
||||
param.ht_context = HC_PARAMETER;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &with_parameter(const help_text &ht) {
|
||||
this->ht_parameters.emplace_back(ht);
|
||||
this->ht_parameters.back().ht_context = HC_PARAMETER;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &with_examples(const std::initializer_list<help_example> &examples) {
|
||||
this->ht_example = examples;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &with_example(const help_example &example) {
|
||||
this->ht_example.emplace_back(example);
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &optional() {
|
||||
this->ht_nargs = HN_OPTIONAL;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &zero_or_more() {
|
||||
this->ht_nargs = HN_ZERO_OR_MORE;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &one_or_more() {
|
||||
this->ht_nargs = HN_ONE_OR_MORE;
|
||||
return *this;
|
||||
};
|
||||
|
||||
help_text &with_format(help_parameter_format_t format) {
|
||||
this->ht_format = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &with_enum_values(const std::initializer_list<const char*> &enum_values) {
|
||||
this->ht_enum_values = enum_values;
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
|
||||
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out);
|
||||
|
||||
#endif //LNAV_HELP_TEXT_FORMATTER_HH
|
@ -197,7 +197,12 @@ public:
|
||||
void set_show_scrollbar(bool ss) { this->lv_show_scrollbar = ss; };
|
||||
bool get_show_scrollbar() const { return this->lv_show_scrollbar; };
|
||||
|
||||
void set_show_bottom_border(bool val) { this->lv_show_bottom_border = val; };
|
||||
void set_show_bottom_border(bool val) {
|
||||
if (this->lv_show_bottom_border != val) {
|
||||
this->lv_show_bottom_border = val;
|
||||
this->set_needs_update();
|
||||
}
|
||||
};
|
||||
bool get_show_bottom_border() const { return this->lv_show_bottom_border; };
|
||||
|
||||
listview_curses &set_word_wrap(bool ww) {
|
||||
@ -459,7 +464,7 @@ public:
|
||||
}
|
||||
else {
|
||||
getmaxyx(this->lv_window, height, width_out);
|
||||
if (this->lv_height < 1) {
|
||||
if (this->lv_height < 0) {
|
||||
height_out = vis_line_t(height) +
|
||||
this->lv_height -
|
||||
vis_line_t(this->lv_y);
|
||||
|
68
src/lnav.cc
68
src/lnav.cc
@ -407,8 +407,9 @@ private:
|
||||
void do_update(void)
|
||||
{
|
||||
lnav_data.ld_top_source.update_time();
|
||||
lnav_data.ld_status[LNS_TOP].do_update();
|
||||
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
||||
for (auto &sc : lnav_data.ld_status) {
|
||||
sc.do_update();
|
||||
}
|
||||
refresh();
|
||||
};
|
||||
|
||||
@ -1916,17 +1917,19 @@ static void looper(void)
|
||||
textview_curses::action(update_hits));
|
||||
}
|
||||
|
||||
lnav_data.ld_doc_view.set_window(lnav_data.ld_window);
|
||||
|
||||
lnav_data.ld_status[LNS_TOP].set_top(0);
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
|
||||
for (lpc = 0; lpc < LNS__MAX; lpc++) {
|
||||
lnav_data.ld_status[lpc].set_window(lnav_data.ld_window);
|
||||
for (auto &sc : lnav_data.ld_status) {
|
||||
sc.set_window(lnav_data.ld_window);
|
||||
}
|
||||
lnav_data.ld_status[LNS_TOP].set_data_source(
|
||||
&lnav_data.ld_top_source);
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_data_source(
|
||||
&lnav_data.ld_bottom_source);
|
||||
|
||||
lnav_data.ld_match_view.set_show_bottom_border(true);
|
||||
lnav_data.ld_status[LNS_DOC].set_data_source(
|
||||
&lnav_data.ld_doc_status_source);
|
||||
|
||||
vsb.push_back(sb.get_functor());
|
||||
|
||||
@ -1939,8 +1942,11 @@ static void looper(void)
|
||||
sb.push_back(&lnav_data.ld_bottom_source.marks_wire);
|
||||
sb.push_back(&lnav_data.ld_term_extra.filename_wire);
|
||||
|
||||
lnav_data.ld_status[0].window_change();
|
||||
lnav_data.ld_status[1].window_change();
|
||||
lnav_data.ld_match_view.set_show_bottom_border(true);
|
||||
|
||||
for (auto &sc : lnav_data.ld_status) {
|
||||
sc.window_change();
|
||||
}
|
||||
|
||||
execute_file(ec, dotlnav_path("session"));
|
||||
|
||||
@ -1963,12 +1969,45 @@ static void looper(void)
|
||||
rebuild_indexes(true);
|
||||
}
|
||||
|
||||
lnav_data.ld_status[LNS_TOP].do_update();
|
||||
{
|
||||
unsigned long width, height;
|
||||
|
||||
getmaxyx(lnav_data.ld_window, height, width);
|
||||
bool doc_open = lnav_data.ld_doc_view.get_height() > 0;
|
||||
int bottom_height =
|
||||
(doc_open ? 1 : 0)
|
||||
+ lnav_data.ld_doc_view.get_height()
|
||||
+ lnav_data.ld_match_view.get_height()
|
||||
+ 1
|
||||
+ lnav_data.ld_rl_view->get_height();
|
||||
|
||||
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||
textview_curses &tc = lnav_data.ld_views[lpc];
|
||||
|
||||
tc.set_height(vis_line_t(-bottom_height));
|
||||
}
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(
|
||||
-(lnav_data.ld_match_view.get_height() + 2));
|
||||
lnav_data.ld_status[LNS_DOC].set_top(height - bottom_height);
|
||||
lnav_data.ld_status[LNS_DOC].set_enabled(doc_open);
|
||||
|
||||
lnav_data.ld_doc_view.set_y(height - bottom_height + 1);
|
||||
lnav_data.ld_match_view.set_y(
|
||||
height
|
||||
- bottom_height
|
||||
+ (doc_open ? 1 : 0)
|
||||
+ 1
|
||||
+ lnav_data.ld_doc_view.get_height());
|
||||
}
|
||||
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_data.ld_view_stack.back()->do_update();
|
||||
}
|
||||
lnav_data.ld_doc_view.do_update();
|
||||
lnav_data.ld_match_view.do_update();
|
||||
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
||||
for (auto &sc : lnav_data.ld_status) {
|
||||
sc.do_update();
|
||||
}
|
||||
rlc.do_update();
|
||||
refresh();
|
||||
|
||||
@ -2187,11 +2226,14 @@ static void looper(void)
|
||||
resizeterm(size.ws_row, size.ws_col);
|
||||
}
|
||||
rlc.window_change();
|
||||
lnav_data.ld_status[0].window_change();
|
||||
lnav_data.ld_status[1].window_change();
|
||||
for (auto &sc : lnav_data.ld_status) {
|
||||
sc.window_change();
|
||||
}
|
||||
if (!lnav_data.ld_view_stack.empty()) {
|
||||
lnav_data.ld_view_stack.back()->set_needs_update();
|
||||
}
|
||||
lnav_data.ld_doc_view.set_needs_update();
|
||||
lnav_data.ld_match_view.set_needs_update();
|
||||
}
|
||||
|
||||
if (lnav_data.ld_child_terminated) {
|
||||
@ -2934,6 +2976,8 @@ int main(int argc, char *argv[])
|
||||
.add_input_delegate(lnav_data.ld_spectro_source)
|
||||
.set_tail_space(vis_line_t(2));
|
||||
|
||||
lnav_data.ld_doc_view.set_sub_source(&lnav_data.ld_doc_source)
|
||||
.set_left(0);
|
||||
lnav_data.ld_match_view.set_left(0);
|
||||
|
||||
for (lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "listview_curses.hh"
|
||||
#include "top_status_source.hh"
|
||||
#include "bottom_status_source.hh"
|
||||
#include "doc_status_source.hh"
|
||||
#include "grep_highlighter.hh"
|
||||
#include "db_sub_source.hh"
|
||||
#include "textfile_sub_source.hh"
|
||||
@ -64,6 +65,7 @@
|
||||
#include "log_format_loader.hh"
|
||||
#include "spectro_source.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "plain_text_source.hh"
|
||||
|
||||
/** The command modes that are available while viewing a file. */
|
||||
typedef enum {
|
||||
@ -133,6 +135,7 @@ extern const char *lnav_zoom_strings[];
|
||||
typedef enum {
|
||||
LNS_TOP,
|
||||
LNS_BOTTOM,
|
||||
LNS_DOC,
|
||||
|
||||
LNS__MAX
|
||||
} lnav_status_t;
|
||||
@ -241,6 +244,7 @@ struct _lnav_data {
|
||||
statusview_curses ld_status[LNS__MAX];
|
||||
top_status_source ld_top_source;
|
||||
bottom_status_source ld_bottom_source;
|
||||
doc_status_source ld_doc_status_source;
|
||||
listview_curses::action::broadcaster ld_scroll_broadcaster;
|
||||
listview_curses::action::broadcaster ld_view_stack_broadcaster;
|
||||
|
||||
@ -249,6 +253,8 @@ struct _lnav_data {
|
||||
time_t ld_bottom_time;
|
||||
int ld_bottom_time_millis;
|
||||
|
||||
plain_text_source ld_doc_source;
|
||||
textview_curses ld_doc_view;
|
||||
textview_curses ld_match_view;
|
||||
|
||||
std::vector<textview_curses *> ld_view_stack;
|
||||
|
@ -2856,7 +2856,13 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
"adjust-log-time",
|
||||
"<date>",
|
||||
"Change the timestamps of the top file to be relative to the given date",
|
||||
com_adjust_log_time
|
||||
com_adjust_log_time,
|
||||
|
||||
help_text(":adjust-log-time")
|
||||
.with_summary("Change the timestamps of the top file to be relative to the given date")
|
||||
.with_parameter(help_text("timestamp", "The new timestamp for the top line in the view")
|
||||
.with_format(HPF_DATETIME))
|
||||
.with_example({"2017-01-02T05:33:00", ""})
|
||||
},
|
||||
|
||||
{
|
||||
@ -2864,18 +2870,37 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
"<seconds>",
|
||||
"Convert epoch time to a human-readable form",
|
||||
com_unix_time,
|
||||
|
||||
help_text(":unix-time")
|
||||
.with_summary("Convert epoch time to a human-readable form")
|
||||
.with_parameter(help_text("seconds", "The epoch timestamp to convert")
|
||||
.with_format(HPF_INTEGER))
|
||||
.with_example({"1490191111", "Wed Mar 22 06:58:31 2017 -0700 PDT -- 1490191111"})
|
||||
},
|
||||
{
|
||||
"current-time",
|
||||
NULL,
|
||||
"Print the current time in human-readable form and seconds since the epoch",
|
||||
com_current_time,
|
||||
|
||||
help_text(":current-time")
|
||||
.with_summary("Print the current time in human-readable form and seconds since the epoch")
|
||||
},
|
||||
{
|
||||
"goto",
|
||||
"<line#|N%|date>",
|
||||
"Go to the given line number, N percent into the file, or the given timestamp in the log view",
|
||||
com_goto,
|
||||
|
||||
help_text(":goto")
|
||||
.with_summary("Go to the given location in the top view")
|
||||
.with_parameter(help_text("line#|N%|date", "A line number, percent into the file, or a timestamp"))
|
||||
.with_examples(
|
||||
{
|
||||
{"22"},
|
||||
{"75%"},
|
||||
{"2017-01-01"}
|
||||
})
|
||||
},
|
||||
{
|
||||
"relative-goto",
|
||||
@ -2888,180 +2913,336 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
NULL,
|
||||
"Toggle the bookmark state for the top line in the current view",
|
||||
com_mark,
|
||||
|
||||
help_text(":mark")
|
||||
.with_summary("Toggle the bookmark state for the top line in the current view")
|
||||
},
|
||||
{
|
||||
"next-mark",
|
||||
"error|warning|search|user|file|partition",
|
||||
"Move to the next bookmark of the given type in the current view",
|
||||
com_goto_mark,
|
||||
|
||||
help_text(":next-mark")
|
||||
.with_summary("Move to the next bookmark of the given type in the current view")
|
||||
.with_parameter(help_text("type", "The type of bookmark -- error, warning, search, user, file, partition"))
|
||||
.with_example({"error"})
|
||||
},
|
||||
{
|
||||
"prev-mark",
|
||||
"error|warning|search|user|file|partition",
|
||||
"Move to the previous bookmark of the given type in the current view",
|
||||
com_goto_mark,
|
||||
|
||||
help_text(":prev-mark")
|
||||
.with_summary("Move to the previous bookmark of the given type in the current view")
|
||||
.with_parameter(help_text("type", "The type of bookmark -- error, warning, search, user, file, partition"))
|
||||
.with_example({"error"})
|
||||
},
|
||||
{
|
||||
"help",
|
||||
NULL,
|
||||
"Open the help text view",
|
||||
com_help,
|
||||
|
||||
help_text(":help")
|
||||
.with_summary("Open the help text view")
|
||||
},
|
||||
{
|
||||
"hide-fields",
|
||||
"<field-name1> [<field-name2> ... <field-nameN>]",
|
||||
"Hide log message fields by replacing them with an ellipsis",
|
||||
com_toggle_field,
|
||||
|
||||
help_text(":hide-fields")
|
||||
.with_summary("Hide log message fields by replacing them with an ellipsis")
|
||||
.with_parameter(help_text("field-name", "The name of the field to hide")
|
||||
.one_or_more())
|
||||
.with_example({"log_procname"})
|
||||
},
|
||||
{
|
||||
"show-fields",
|
||||
"<field-name> [<field-name2> ... <field-nameN>]",
|
||||
"Show log message fields that were previously hidden",
|
||||
com_toggle_field,
|
||||
|
||||
help_text(":show-fields")
|
||||
.with_summary("Show log message fields that were previously hidden")
|
||||
.with_parameter(help_text("field-name", "The name of the field to show")
|
||||
.one_or_more())
|
||||
.with_example({"log_procname"})
|
||||
},
|
||||
{
|
||||
"hide-lines-before",
|
||||
"<line#|date>",
|
||||
"Hide lines that come before the given line number or date",
|
||||
com_hide_line,
|
||||
|
||||
help_text(":hide-lines-before")
|
||||
.with_summary("Hide lines that come before the given date")
|
||||
.with_parameter(help_text("date", "An absolute or relative date"))
|
||||
.with_examples(
|
||||
{
|
||||
{"here"},
|
||||
{"6am"},
|
||||
})
|
||||
},
|
||||
{
|
||||
"hide-lines-after",
|
||||
"<line#|date>",
|
||||
"Hide lines that come after the given line number or date",
|
||||
com_hide_line,
|
||||
|
||||
help_text(":hide-lines-after")
|
||||
.with_summary("Hide lines that come after the given date")
|
||||
.with_parameter(help_text("date", "An absolute or relative date"))
|
||||
.with_examples(
|
||||
{
|
||||
{"here"},
|
||||
{"6am"},
|
||||
})
|
||||
},
|
||||
{
|
||||
"show-lines-before-and-after",
|
||||
NULL,
|
||||
"Show lines that were hidden by the 'hide-lines' commands",
|
||||
com_show_lines,
|
||||
|
||||
help_text(":show-lines-before-and-after")
|
||||
.with_summary("Show lines that were hidden by the 'hide-lines' commands")
|
||||
},
|
||||
{
|
||||
"highlight",
|
||||
"<regex>",
|
||||
"<pattern>",
|
||||
"Add coloring to log messages fragments that match the given regular expression",
|
||||
com_highlight,
|
||||
|
||||
help_text(":highlight")
|
||||
.with_summary("Add coloring to log messages fragments that match the given regular expression")
|
||||
.with_parameter(help_text("pattern", "The regular expression to match"))
|
||||
.with_example({R"(\d{3,})"})
|
||||
},
|
||||
{
|
||||
"clear-highlight",
|
||||
"<regex>",
|
||||
"<pattern>",
|
||||
"Remove a previously set highlight regular expression",
|
||||
com_clear_highlight,
|
||||
|
||||
help_text(":clear-highlight")
|
||||
.with_summary("Remove a previously set highlight regular expression")
|
||||
.with_parameter(help_text("pattern", "The regular expression previously used with :highlight"))
|
||||
.with_example({"foobar"})
|
||||
},
|
||||
{
|
||||
"filter-in",
|
||||
"<regex>",
|
||||
"Only show lines that match the given regular expression in the current view",
|
||||
com_filter,
|
||||
|
||||
help_text(":filter-in")
|
||||
.with_summary("Only show lines that match the given regular expression in the current view")
|
||||
.with_parameter(help_text("pattern", "The regular expression to match"))
|
||||
.with_example({"dhclient"})
|
||||
},
|
||||
{
|
||||
"filter-out",
|
||||
"<regex>",
|
||||
"Remove lines that match the given regular expression in the current view",
|
||||
com_filter,
|
||||
|
||||
help_text(":filter-out")
|
||||
.with_summary("Remove lines that match the given regular expression in the current view")
|
||||
.with_parameter(help_text("pattern", "The regular expression to match"))
|
||||
.with_example({"last message repeated"})
|
||||
},
|
||||
{
|
||||
"delete-filter",
|
||||
"<regex>",
|
||||
"Delete the given filter",
|
||||
com_delete_filter,
|
||||
|
||||
help_text(":filter-out")
|
||||
.with_summary("Delete the filter created with "
|
||||
ANSI_BOLD(":filter-in") " or " ANSI_BOLD(":filter-out"))
|
||||
.with_parameter(help_text("pattern", "The regular expression to match"))
|
||||
.with_example({"last message repeated"})
|
||||
},
|
||||
{
|
||||
"append-to",
|
||||
"<filename>",
|
||||
"Append marked lines in the current view to the given file",
|
||||
com_save_to,
|
||||
|
||||
help_text(":append-to")
|
||||
.with_summary("Append marked lines in the current view to the given file")
|
||||
.with_parameter(help_text("path", "The path to the file to append to"))
|
||||
.with_example({"/tmp/interesting-lines.txt"})
|
||||
},
|
||||
{
|
||||
"write-to",
|
||||
"<filename>",
|
||||
"Overwrite the given file with any marked lines in the current view",
|
||||
com_save_to,
|
||||
|
||||
help_text(":write-to")
|
||||
.with_summary("Overwrite the given file with any marked lines in the current view")
|
||||
.with_parameter(help_text("path", "The path to the file to write"))
|
||||
.with_example({"/tmp/interesting-lines.txt"})
|
||||
},
|
||||
{
|
||||
"write-csv-to",
|
||||
"<filename>",
|
||||
"Write SQL results to the given file in CSV format",
|
||||
com_save_to,
|
||||
|
||||
help_text(":write-csv-to")
|
||||
.with_summary("Write SQL results to the given file in CSV format")
|
||||
.with_parameter(help_text("path", "The path to the file to write"))
|
||||
.with_example({"/tmp/table.csv"})
|
||||
},
|
||||
{
|
||||
"write-json-to",
|
||||
"<filename>",
|
||||
"Write SQL results to the given file in JSON format",
|
||||
com_save_to,
|
||||
|
||||
help_text(":write-json-to")
|
||||
.with_summary("Write SQL results to the given file in JSON format")
|
||||
.with_parameter(help_text("path", "The path to the file to write"))
|
||||
.with_example({"/tmp/table.json"})
|
||||
},
|
||||
{
|
||||
"write-cols-to",
|
||||
"<filename>",
|
||||
"Write SQL results to the given file in a columnar format",
|
||||
com_save_to,
|
||||
|
||||
help_text(":write-cols-to")
|
||||
.with_summary("Write SQL results to the given file in a columnar format")
|
||||
.with_parameter(help_text("path", "The path to the file to write"))
|
||||
.with_example({"/tmp/table.txt"})
|
||||
},
|
||||
{
|
||||
"write-raw-to",
|
||||
"<filename>",
|
||||
"Write SQL results to the given file without any formatting",
|
||||
com_save_to,
|
||||
|
||||
help_text(":write-raw-to")
|
||||
.with_summary("Write SQL results to the given file without any formatting")
|
||||
.with_parameter(help_text("path", "The path to the file to write"))
|
||||
.with_example({"/tmp/table.txt"})
|
||||
},
|
||||
{
|
||||
"pipe-to",
|
||||
"<shell-cmd>",
|
||||
"Pipe the marked lines to the given shell command",
|
||||
com_pipe_to,
|
||||
|
||||
help_text(":pipe-to")
|
||||
.with_summary("Pipe the marked lines to the given shell command")
|
||||
.with_parameter(help_text("shell-cmd", "The shell command-line to execute"))
|
||||
.with_example({"sed -e s/foo/bar/g"})
|
||||
},
|
||||
{
|
||||
"pipe-line-to",
|
||||
"<shell-cmd>",
|
||||
"Pipe the top line to the given shell command",
|
||||
com_pipe_to,
|
||||
|
||||
help_text(":pipe-line-to")
|
||||
.with_summary("Pipe the top line to the given shell command")
|
||||
.with_parameter(help_text("shell-cmd", "The shell command-line to execute"))
|
||||
.with_example({"sed -e 's/foo/bar/g'"})
|
||||
},
|
||||
{
|
||||
"enable-filter",
|
||||
"<regex>",
|
||||
"Enable a previously created and disabled filter",
|
||||
com_enable_filter,
|
||||
|
||||
help_text(":enable-filter")
|
||||
.with_summary("Enable a previously created and disabled filter")
|
||||
.with_parameter(help_text("pattern", "The regular expression used in the filter command"))
|
||||
.with_example({"last message repeated"})
|
||||
},
|
||||
{
|
||||
"disable-filter",
|
||||
"<regex>",
|
||||
"Disable a filter created with filter-in/filter-out",
|
||||
com_disable_filter,
|
||||
|
||||
help_text(":disable-filter")
|
||||
.with_summary("Disable a filter created with filter-in/filter-out")
|
||||
.with_parameter(help_text("pattern", "The regular expression used in the filter command"))
|
||||
.with_example({"last message repeated"})
|
||||
},
|
||||
{
|
||||
"enable-word-wrap",
|
||||
NULL,
|
||||
"Enable word-wrapping for the current view",
|
||||
com_enable_word_wrap,
|
||||
|
||||
help_text(":enable-word-wrap")
|
||||
.with_summary("Enable word-wrapping for the current view")
|
||||
},
|
||||
{
|
||||
"disable-word-wrap",
|
||||
NULL,
|
||||
"Disable word-wrapping for the current view",
|
||||
com_disable_word_wrap,
|
||||
|
||||
help_text(":disable-word-wrap")
|
||||
.with_summary("Disable word-wrapping for the current view")
|
||||
},
|
||||
{
|
||||
"create-logline-table",
|
||||
"<table-name>",
|
||||
"Create an SQL table using the top line of the log view as a template",
|
||||
com_create_logline_table,
|
||||
|
||||
help_text(":create-logline-table")
|
||||
.with_summary("Create an SQL table using the top line of the log view as a template")
|
||||
.with_parameter(help_text("table-name", "The name for the new table"))
|
||||
.with_example({"task_durations"})
|
||||
},
|
||||
{
|
||||
"delete-logline-table",
|
||||
"<table-name>",
|
||||
"Delete a table created with create-logline-table",
|
||||
com_delete_logline_table,
|
||||
|
||||
help_text(":delete-logline-table")
|
||||
.with_summary("Delete a table created with create-logline-table")
|
||||
.with_parameter(help_text("table-name", "The name of the table to delete"))
|
||||
.with_example({"task_durations"})
|
||||
},
|
||||
{
|
||||
"create-search-table",
|
||||
"<table-name> [<regex>]",
|
||||
"Create an SQL table based on a regex search",
|
||||
com_create_search_table,
|
||||
|
||||
help_text(":create-search-table")
|
||||
.with_summary("Create an SQL table based on a regex search")
|
||||
.with_parameter(help_text("table-name", "The name of the table to create"))
|
||||
.with_parameter(help_text(
|
||||
"pattern",
|
||||
"The regular expression used to capture the table columns. "
|
||||
"If not given, the current search pattern is used.")
|
||||
.optional())
|
||||
.with_example({R"(task_durations duration=(?<duration>\d+))"})
|
||||
},
|
||||
{
|
||||
"delete-search-table",
|
||||
"<table-name>",
|
||||
"Delete a table created with create-search-table",
|
||||
com_delete_search_table,
|
||||
|
||||
help_text(":delete-search-table")
|
||||
.with_summary("Create an SQL table based on a regex search")
|
||||
.with_parameter(help_text("table-name", "The name of the table to create"))
|
||||
.with_example({"task_durations"})
|
||||
},
|
||||
{
|
||||
"open",
|
||||
@ -3072,24 +3253,48 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
"Open the given file(s) in lnav",
|
||||
#endif
|
||||
com_open,
|
||||
|
||||
help_text(":open")
|
||||
.with_summary(
|
||||
#ifdef HAVE_LIBCURL
|
||||
"Open the given file(s) or URLs in lnav"
|
||||
#else
|
||||
"Open the given file(s) in lnav"
|
||||
#endif
|
||||
)
|
||||
.with_parameter(
|
||||
help_text{"path", "The path to the file to open"}
|
||||
.one_or_more())
|
||||
.with_example({"~/.lnav/example", ""})
|
||||
},
|
||||
{
|
||||
"close",
|
||||
NULL,
|
||||
"Close the top file",
|
||||
com_close,
|
||||
|
||||
help_text(":close")
|
||||
.with_summary("Close the top file in the view")
|
||||
},
|
||||
{
|
||||
"partition-name",
|
||||
"<name>",
|
||||
"Mark the top line in the log view as the start of a new partition with the given name",
|
||||
com_partition_name,
|
||||
|
||||
help_text(":partition-name")
|
||||
.with_summary("Mark the top line in the log view as the start of a new partition with the given name")
|
||||
.with_parameter(help_text("name", "The name for the new partition"))
|
||||
.with_example({"reboot"})
|
||||
},
|
||||
{
|
||||
"clear-partition",
|
||||
NULL,
|
||||
"Clear the partition the top line is a part of",
|
||||
com_clear_partition,
|
||||
|
||||
help_text(":clear-partition")
|
||||
.with_summary("Clear the partition the top line is a part of")
|
||||
},
|
||||
{
|
||||
"pt-min-time",
|
||||
@ -3108,96 +3313,174 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
"<lnav-command>",
|
||||
"Add the given command to the session file (~/.lnav/session)",
|
||||
com_session,
|
||||
|
||||
help_text(":session")
|
||||
.with_summary("Add the given command to the session file (~/.lnav/session)")
|
||||
.with_parameter(help_text("lnav-command", "The lnav command to save."))
|
||||
.with_example({":highlight foobar"})
|
||||
},
|
||||
{
|
||||
"summarize",
|
||||
"<column-name>",
|
||||
"Execute a SQL query that computes the characteristics of the values in the given column",
|
||||
com_summarize,
|
||||
|
||||
help_text(":summarize")
|
||||
.with_summary("Execute a SQL query that computes the characteristics of the values in the given column")
|
||||
.with_parameter(help_text("column-name", "The name of the column to analyze."))
|
||||
.with_example({"sc_bytes"})
|
||||
},
|
||||
{
|
||||
"switch-to-view",
|
||||
"<view-name>",
|
||||
"Switch to the given view",
|
||||
com_switch_to_view,
|
||||
|
||||
help_text(":switch-to-view")
|
||||
.with_summary("Switch to the given view")
|
||||
.with_parameter(help_text("view-name", "The name of the view to switch to."))
|
||||
.with_example({"schema"})
|
||||
},
|
||||
{
|
||||
"reset-session",
|
||||
NULL,
|
||||
"Reset the session state, clearing all filters, highlights, and bookmarks",
|
||||
com_reset_session,
|
||||
|
||||
help_text(":reset-session")
|
||||
.with_summary("Reset the session state, clearing all filters, highlights, and bookmarks")
|
||||
},
|
||||
{
|
||||
"load-session",
|
||||
NULL,
|
||||
"Load the latest session state",
|
||||
com_load_session,
|
||||
|
||||
help_text(":load-session")
|
||||
.with_summary("Load the latest session state")
|
||||
},
|
||||
{
|
||||
"save-session",
|
||||
NULL,
|
||||
"Save the current state as a session",
|
||||
com_save_session,
|
||||
|
||||
help_text(":save-session")
|
||||
.with_summary("Save the current state as a session")
|
||||
},
|
||||
{
|
||||
"set-min-log-level",
|
||||
"<log-level>",
|
||||
"Set the minimum log level to display in the log view",
|
||||
com_set_min_log_level,
|
||||
|
||||
help_text(":set-min-log-level")
|
||||
.with_summary("Set the minimum log level to display in the log view")
|
||||
.with_parameter(help_text("log-level", "The new minimum log level"))
|
||||
.with_example({"error"})
|
||||
},
|
||||
{
|
||||
"redraw",
|
||||
NULL,
|
||||
"Do a full redraw of the screen",
|
||||
com_redraw,
|
||||
|
||||
help_text(":redraw")
|
||||
.with_summary("Do a full redraw of the screen")
|
||||
},
|
||||
{
|
||||
"zoom-to",
|
||||
"<zoom-level>",
|
||||
"Zoom the histogram view to the given level",
|
||||
com_zoom_to,
|
||||
|
||||
help_text(":zoom-to")
|
||||
.with_summary("Zoom the histogram view to the given level")
|
||||
.with_parameter(help_text("zoom-level", "The zoom level"))
|
||||
.with_example({"1-week"})
|
||||
},
|
||||
{
|
||||
"echo",
|
||||
"[-n] <msg>",
|
||||
"Echo the given message",
|
||||
com_echo,
|
||||
|
||||
help_text(":echo")
|
||||
.with_summary("Echo the given message")
|
||||
.with_parameter(help_text("msg", "The message to display"))
|
||||
.with_example({"Hello, World!"})
|
||||
},
|
||||
{
|
||||
"alt-msg",
|
||||
"<msg>",
|
||||
"Display a message in the alternate command position",
|
||||
com_alt_msg,
|
||||
|
||||
help_text(":alt-msg")
|
||||
.with_summary("Display a message in the alternate command position")
|
||||
.with_parameter(help_text("msg", "The message to display"))
|
||||
.with_example({"Press t to switch to the text view"})
|
||||
},
|
||||
{
|
||||
"eval",
|
||||
"<msg>",
|
||||
"Evaluate the given command/query after doing environment variable substitution",
|
||||
com_eval,
|
||||
|
||||
help_text(":eval")
|
||||
.with_summary(
|
||||
"Evaluate the given command/query after doing environment variable substitution")
|
||||
.with_parameter(help_text("command",
|
||||
"The command or query to perform substitution on."))
|
||||
.with_examples(
|
||||
{
|
||||
{":echo $HOME"},
|
||||
{";SELECT * FROM ${table}"}
|
||||
})
|
||||
},
|
||||
{
|
||||
"config",
|
||||
"<option> [<value>]",
|
||||
"Read or write a configuration option",
|
||||
com_config,
|
||||
|
||||
help_text(":config")
|
||||
.with_summary("Read or write a configuration option")
|
||||
.with_parameter(help_text("option", "The path to the option to read or write"))
|
||||
.with_parameter(help_text("value", "The value to write. If not given, the current value is returned")
|
||||
.optional())
|
||||
.with_example({"/ui/clock-format"})
|
||||
},
|
||||
{
|
||||
"save-config",
|
||||
NULL,
|
||||
"Save the current configuration state",
|
||||
com_save_config,
|
||||
|
||||
help_text(":save-config")
|
||||
.with_summary("Save the current configuration state")
|
||||
},
|
||||
{
|
||||
"reset-config",
|
||||
"<option>",
|
||||
"Reset the configuration option to its default value",
|
||||
com_reset_config,
|
||||
|
||||
help_text(":reset-config")
|
||||
.with_summary("Reset the configuration option to its default value")
|
||||
.with_parameter(help_text("option", "The path to the option to reset"))
|
||||
.with_example({"/ui/clock-format"})
|
||||
},
|
||||
{
|
||||
"spectrogram",
|
||||
"<field-name>",
|
||||
"Visualize the given message field using a spectrogram",
|
||||
com_spectrogram,
|
||||
|
||||
help_text(":spectrogram")
|
||||
.with_summary("Visualize the given message field using a spectrogram")
|
||||
.with_parameter(help_text("field-name", "The name of the numeric field to visualize."))
|
||||
.with_example({"sc_bytes"})
|
||||
},
|
||||
|
||||
{ NULL },
|
||||
|
@ -73,7 +73,7 @@ public:
|
||||
lf->read_full_message(lf->begin() + cl_copy, line);
|
||||
format->annotate(line, sa, line_values);
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
if (body.lr_end == -1 || body.length() == 0) {
|
||||
if (body.lr_end == -1) {
|
||||
this->ldt_schema_id.clear();
|
||||
return;
|
||||
}
|
||||
@ -158,7 +158,7 @@ public:
|
||||
lf->read_full_message(lf_iter, this->ldt_current_line);
|
||||
lf->get_format()->annotate(this->ldt_current_line, sa, line_values);
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
if (body.lr_end == -1 || body.length() == 0) {
|
||||
if (body.lr_end == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,10 @@ public:
|
||||
memcpy(this->ll_schema, ba.in(), sizeof(this->ll_schema));
|
||||
};
|
||||
|
||||
char get_schema() const {
|
||||
return this->ll_schema[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a partial match of the given schema against this log line.
|
||||
* Storing the full schema is not practical, so we just keep the first four
|
||||
@ -894,6 +898,7 @@ public:
|
||||
bool vd_internal;
|
||||
std::vector<std::string> vd_action_list;
|
||||
std::string vd_rewriter;
|
||||
std::string vd_description;
|
||||
};
|
||||
|
||||
struct indexed_value_def {
|
||||
|
@ -490,6 +490,11 @@ static struct json_path_handler value_def_handlers[] = {
|
||||
.with_description("A command that will rewrite this field when pretty-printing")
|
||||
.for_field(&nullobj<external_log_format::value_def>()->vd_rewriter),
|
||||
|
||||
json_path_handler("description")
|
||||
.with_synopsis("<string>")
|
||||
.with_description("A description of the field")
|
||||
.for_field(&nullobj<external_log_format::value_def>()->vd_description),
|
||||
|
||||
json_path_handler()
|
||||
};
|
||||
|
||||
|
@ -33,9 +33,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "attr_line.hh"
|
||||
|
||||
class plain_text_source
|
||||
: public text_sub_source {
|
||||
public:
|
||||
plain_text_source() {
|
||||
};
|
||||
|
||||
plain_text_source(std::string text)
|
||||
{
|
||||
size_t start = 0, end;
|
||||
@ -52,10 +57,24 @@ public:
|
||||
};
|
||||
|
||||
plain_text_source(const std::vector<std::string> &text_lines) {
|
||||
for (auto &str : text_lines) {
|
||||
this->tds_lines.emplace_back(str);
|
||||
}
|
||||
this->tds_longest_line = this->compute_longest_line();
|
||||
};
|
||||
|
||||
plain_text_source(const std::vector<attr_line_t> &text_lines) {
|
||||
this->tds_lines = text_lines;
|
||||
this->tds_longest_line = this->compute_longest_line();
|
||||
};
|
||||
|
||||
plain_text_source &replace_with(attr_line_t &text_lines) {
|
||||
this->tds_lines.clear();
|
||||
text_lines.split_lines(this->tds_lines);
|
||||
this->tds_longest_line = this->compute_longest_line();
|
||||
return *this;
|
||||
};
|
||||
|
||||
size_t text_line_count()
|
||||
{
|
||||
return this->tds_lines.size();
|
||||
@ -68,9 +87,13 @@ public:
|
||||
void text_value_for_line(textview_curses &tc,
|
||||
int row,
|
||||
std::string &value_out,
|
||||
bool no_scrub)
|
||||
{
|
||||
value_out = this->tds_lines[row];
|
||||
bool no_scrub) {
|
||||
value_out = this->tds_lines[row].get_string();
|
||||
};
|
||||
|
||||
void text_attrs_for_line(textview_curses &tc, int line,
|
||||
string_attrs_t &value_out) {
|
||||
value_out = this->tds_lines[line].get_attrs();
|
||||
};
|
||||
|
||||
size_t text_size_for_line(textview_curses &tc, int row, bool raw) {
|
||||
@ -80,15 +103,13 @@ public:
|
||||
private:
|
||||
size_t compute_longest_line() {
|
||||
size_t retval = 0;
|
||||
for (std::vector<std::string>::iterator iter = this->tds_lines.begin();
|
||||
iter != this->tds_lines.end();
|
||||
++iter) {
|
||||
retval = std::max(retval, iter->length());
|
||||
for (auto &iter : this->tds_lines) {
|
||||
retval = std::max(retval, (size_t) iter.length());
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::vector<std::string> tds_lines;
|
||||
std::vector<attr_line_t> tds_lines;
|
||||
size_t tds_longest_line;
|
||||
};
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "readline_curses.hh"
|
||||
#include "log_search_table.hh"
|
||||
#include "log_format_loader.hh"
|
||||
#include "help_text_formatter.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -59,6 +60,7 @@ void rl_change(void *dummy, readline_curses *rc)
|
||||
}
|
||||
if (iter == lnav_commands.end() ||
|
||||
iter->second.c_description == NULL) {
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(0));
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
"Enter an lnav command: " ABORT_MSG);
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
@ -89,21 +91,22 @@ void rl_change(void *dummy, readline_curses *rc)
|
||||
}
|
||||
else {
|
||||
readline_context::command_t &cmd = iter->second;
|
||||
char args_text[128] = {0};
|
||||
char help_text[1024];
|
||||
help_text &ht = cmd.c_help;
|
||||
|
||||
if (cmd.c_args != NULL && strlen(cmd.c_args) > 0) {
|
||||
snprintf(args_text, sizeof(args_text),
|
||||
" %s",
|
||||
cmd.c_args);
|
||||
if (ht.ht_name) {
|
||||
textview_curses &dtc = lnav_data.ld_doc_view;
|
||||
vector<attr_line_t> lines;
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
attr_line_t al;
|
||||
|
||||
dtc.get_dimensions(height, width);
|
||||
format_help_text_for_term(ht, min(70UL, width), al);
|
||||
al.split_lines(lines);
|
||||
lnav_data.ld_doc_source.replace_with(al);
|
||||
dtc.set_height(vis_line_t(lines.size()));
|
||||
}
|
||||
snprintf(help_text, sizeof(help_text),
|
||||
ANSI_BOLD("%s%s") " -- %s " ABORT_MSG,
|
||||
cmd.c_name,
|
||||
args_text,
|
||||
cmd.c_description);
|
||||
|
||||
lnav_data.ld_bottom_source.set_prompt(help_text);
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
lnav_data.ld_status[LNS_BOTTOM].window_change();
|
||||
}
|
||||
@ -133,6 +136,26 @@ void rl_change(void *dummy, readline_curses *rc)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LNM_SQL: {
|
||||
attr_line_t al(rc->get_line_buffer());
|
||||
const string_attrs_t &sa = al.get_attrs();
|
||||
size_t x = rc->get_x() - 1;
|
||||
|
||||
annotate_sql_statement(al);
|
||||
|
||||
if (x > 0 && x >= al.length()) {
|
||||
x -= 1;
|
||||
}
|
||||
|
||||
auto iter = find_string_attr(sa, x);
|
||||
|
||||
if (iter != sa.end()) {
|
||||
if (iter->sa_type == &SQL_FUNCTION_ATTR) {
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -218,6 +241,7 @@ void rl_abort(void *dummy, readline_curses *rc)
|
||||
lnav_view_t index = (lnav_view_t)(tc - lnav_data.ld_views);
|
||||
|
||||
lnav_data.ld_bottom_source.set_prompt("");
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(0));
|
||||
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
switch (lnav_data.ld_mode) {
|
||||
@ -241,6 +265,7 @@ void rl_callback(void *dummy, readline_curses *rc)
|
||||
string alt_msg;
|
||||
|
||||
lnav_data.ld_bottom_source.set_prompt("");
|
||||
lnav_data.ld_doc_view.set_height(vis_line_t(0));
|
||||
switch (lnav_data.ld_mode) {
|
||||
case LNM_PAGING:
|
||||
require(0);
|
||||
@ -351,7 +376,7 @@ void rl_display_matches(void *dummy, readline_curses *rc)
|
||||
const std::vector<std::string> &matches = rc->get_matches();
|
||||
textview_curses &tc = lnav_data.ld_match_view;
|
||||
unsigned long width, height;
|
||||
int max_len, cols, rows, match_height, bottom_height;
|
||||
int max_len, cols, rows, match_height;
|
||||
|
||||
getmaxyx(lnav_data.ld_window, height, width);
|
||||
|
||||
@ -360,12 +385,6 @@ void rl_display_matches(void *dummy, readline_curses *rc)
|
||||
rows = (matches.size() + cols - 1) / cols;
|
||||
|
||||
match_height = min((unsigned long)rows, (height - 4) / 2);
|
||||
bottom_height = match_height + 1 + rc->get_height();
|
||||
|
||||
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||
lnav_data.ld_views[lpc].set_height(vis_line_t(-bottom_height));
|
||||
}
|
||||
lnav_data.ld_status[LNS_BOTTOM].set_top(-bottom_height);
|
||||
|
||||
delete tc.get_sub_source();
|
||||
|
||||
@ -388,12 +407,12 @@ void rl_display_matches(void *dummy, readline_curses *rc)
|
||||
|
||||
if (match_height > 0) {
|
||||
tc.set_window(lnav_data.ld_window);
|
||||
tc.set_y(height - bottom_height + 1);
|
||||
tc.set_height(vis_line_t(match_height));
|
||||
tc.reload_data();
|
||||
}
|
||||
else {
|
||||
tc.set_window(NULL);
|
||||
tc.set_height(vis_line_t(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "lnav_util.hh"
|
||||
#include "ansi_scrubber.hh"
|
||||
#include "readline_curses.hh"
|
||||
#include "spookyhash/SpookyV2.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -458,6 +459,8 @@ void readline_curses::start(void)
|
||||
}
|
||||
else {
|
||||
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
|
||||
static uint64_t last_h1, last_h2;
|
||||
|
||||
struct itimerval itv;
|
||||
|
||||
if (current_context == this->rc_contexts.end()) {
|
||||
@ -478,13 +481,21 @@ void readline_curses::start(void)
|
||||
rl_callback_handler_remove();
|
||||
}
|
||||
else {
|
||||
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
||||
'l',
|
||||
rl_line_buffer,
|
||||
rl_end) != 0) {
|
||||
uint64_t h1 = 1, h2 = 2;
|
||||
|
||||
SpookyHash::Hash128(rl_line_buffer, rl_end, &h1, &h2);
|
||||
|
||||
if (h1 == last_h1 && h2 == last_h2) {
|
||||
// do nothing
|
||||
} else if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
||||
'l',
|
||||
rl_line_buffer,
|
||||
rl_end) != 0) {
|
||||
perror("line: write failed");
|
||||
_exit(1);
|
||||
}
|
||||
last_h1 = h1;
|
||||
last_h2 = h2;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds)) {
|
||||
@ -749,6 +760,8 @@ void readline_curses::check_poll_set(const vector<struct pollfd> &pollfds)
|
||||
case 'l':
|
||||
this->rc_line_buffer = &msg[2];
|
||||
this->rc_change.invoke(this);
|
||||
this->rc_matches.clear();
|
||||
this->rc_display_match.invoke(this);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "auto_fd.hh"
|
||||
#include "vt52_curses.hh"
|
||||
#include "log_format.hh"
|
||||
#include "help_text_formatter.hh"
|
||||
|
||||
struct exec_context;
|
||||
|
||||
@ -76,6 +77,8 @@ public:
|
||||
const char *c_description;
|
||||
command_func_t c_func;
|
||||
|
||||
struct help_text c_help;
|
||||
|
||||
void operator=(command_func_t func) {
|
||||
this->c_name = "anon";
|
||||
this->c_args = NULL;
|
||||
|
@ -352,7 +352,8 @@ void readline_regex_highlighter(attr_line_t &al, int x)
|
||||
|
||||
void readline_command_highlighter(attr_line_t &al, int x)
|
||||
{
|
||||
static const pcrepp RE_PREFIXES("^:(filter-in|filter-out|highlight)");
|
||||
static const pcrepp RE_PREFIXES(
|
||||
R"(^:(filter-in|filter-out|delete-filter|enable-filter|disable-filter|highlight|clear-highlight|create-search-table\s+[^\s]+\s+))");
|
||||
static const pcrepp SH_PREFIXES("^:(eval|open|append-to|write-to|write-csv-to|write-json-to)");
|
||||
static int keyword_attrs = (
|
||||
A_BOLD|view_colors::ansi_color_pair(COLOR_CYAN, COLOR_BLACK));
|
||||
|
110
src/sql_util.cc
110
src/sql_util.cc
@ -797,3 +797,113 @@ int sqlite_authorizer(void *pUserData, int action_code, const char *detail1,
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static string sql_keyword_re(void)
|
||||
{
|
||||
string retval = "(?:";
|
||||
|
||||
for (int lpc = 0; sql_keywords[lpc]; lpc++) {
|
||||
if (lpc > 0) {
|
||||
retval.append("|");
|
||||
}
|
||||
retval.append("\\b");
|
||||
retval.append(sql_keywords[lpc]);
|
||||
retval.append("\\b");
|
||||
}
|
||||
retval += ")";
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
string_attr_type SQL_KEYWORD_ATTR("sql_keyword");
|
||||
string_attr_type SQL_IDENTIFIER_ATTR("sql_ident");
|
||||
string_attr_type SQL_FUNCTION_ATTR("sql_func");
|
||||
string_attr_type SQL_STRING_ATTR("sql_string");
|
||||
string_attr_type SQL_OPERATOR_ATTR("sql_oper");
|
||||
string_attr_type SQL_PAREN_ATTR("sql_paren");
|
||||
string_attr_type SQL_GARBAGE_ATTR("sql_garbage");
|
||||
|
||||
void annotate_sql_statement(attr_line_t &al)
|
||||
{
|
||||
static string keyword_re_str =
|
||||
R"(\A)" + sql_keyword_re() + R"(|\.schema|\.msgformats)";
|
||||
|
||||
static struct {
|
||||
pcrepp re;
|
||||
string_attr_type_t type;
|
||||
} PATTERNS[] = {
|
||||
{ {keyword_re_str.c_str(), PCRE_CASELESS}, &SQL_KEYWORD_ATTR },
|
||||
{ {R"(\A'[^']*('(?:'[^']*')*|$))"}, &SQL_STRING_ATTR },
|
||||
{ {R"(\A(\$?\b[a-z_]\w*)|\"([^\"]+)\"|\[([^\]]+)])", PCRE_CASELESS}, &SQL_IDENTIFIER_ATTR },
|
||||
{ {R"(\A(\*|<|>|=|!|\-|\+|\|\|))"}, &SQL_OPERATOR_ATTR },
|
||||
{ {R"(\A\(|\))"}, &SQL_PAREN_ATTR },
|
||||
{ {R"(\A.)"}, &SQL_GARBAGE_ATTR },
|
||||
};
|
||||
|
||||
static pcrepp ws_pattern(R"(\A\s+)");
|
||||
|
||||
pcre_context_static<30> pc;
|
||||
pcre_input pi(al.get_string());
|
||||
string &line = al.get_string();
|
||||
string_attrs_t &sa = al.get_attrs();
|
||||
|
||||
while (pi.pi_next_offset < line.length()) {
|
||||
if (ws_pattern.match(pc, pi, PCRE_ANCHORED)) {
|
||||
continue;
|
||||
}
|
||||
for (auto &pat : PATTERNS) {
|
||||
if (pat.re.match(pc, pi, PCRE_ANCHORED)) {
|
||||
pcre_context::capture_t *cap = pc.all();
|
||||
struct line_range lr(cap->c_begin, cap->c_end);
|
||||
|
||||
sa.emplace_back(lr, pat.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_attrs_t::const_iterator iter;
|
||||
int start = 0;
|
||||
|
||||
while ((iter = find_string_attr(sa, &SQL_IDENTIFIER_ATTR, start)) != sa.end()) {
|
||||
string_attrs_t::const_iterator piter;
|
||||
bool found_open = false;
|
||||
ssize_t lpc;
|
||||
|
||||
for (lpc = iter->sa_range.lr_end; lpc < line.length(); lpc++) {
|
||||
if (line[lpc] == '(') {
|
||||
found_open = true;
|
||||
break;
|
||||
} else if (!isspace(line[lpc])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_open) {
|
||||
ssize_t pstart = lpc + 1;
|
||||
int depth = 1;
|
||||
|
||||
while (depth > 0 &&
|
||||
(piter = find_string_attr(sa, &SQL_PAREN_ATTR, pstart)) != sa.end()) {
|
||||
if (line[piter->sa_range.lr_start] == '(') {
|
||||
depth += 1;
|
||||
} else {
|
||||
depth -= 1;
|
||||
}
|
||||
pstart = piter->sa_range.lr_end;
|
||||
}
|
||||
|
||||
line_range func_range{iter->sa_range.lr_start};
|
||||
if (piter == sa.end()) {
|
||||
func_range.lr_end = line.length();
|
||||
} else {
|
||||
func_range.lr_end = piter->sa_range.lr_end;
|
||||
}
|
||||
sa.emplace_back(func_range, &SQL_FUNCTION_ATTR);
|
||||
}
|
||||
|
||||
start = iter->sa_range.lr_end;
|
||||
}
|
||||
|
||||
remove_string_attr(sa, &SQL_PAREN_ATTR);
|
||||
}
|
||||
|
@ -41,6 +41,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "attr_line.hh"
|
||||
|
||||
extern const char *sql_keywords[];
|
||||
extern const char *sql_function_names[];
|
||||
|
||||
@ -100,4 +102,15 @@ void sqlite_close_wrapper(void *mem);
|
||||
int sqlite_authorizer(void* pUserData, int action_code, const char *detail1,
|
||||
const char *detail2, const char *detail3,
|
||||
const char *detail4);
|
||||
|
||||
extern string_attr_type SQL_KEYWORD_ATTR;
|
||||
extern string_attr_type SQL_IDENTIFIER_ATTR;
|
||||
extern string_attr_type SQL_FUNCTION_ATTR;
|
||||
extern string_attr_type SQL_STRING_ATTR;
|
||||
extern string_attr_type SQL_OPERATOR_ATTR;
|
||||
extern string_attr_type SQL_PAREN_ATTR;
|
||||
extern string_attr_type SQL_GARBAGE_ATTR;
|
||||
|
||||
void annotate_sql_statement(attr_line_t &al_inout);
|
||||
|
||||
#endif
|
||||
|
@ -37,10 +37,14 @@ using namespace std;
|
||||
|
||||
void statusview_curses::do_update(void)
|
||||
{
|
||||
int top, attrs, field, field_count, left = 1, right;
|
||||
int top, attrs, field, field_count, left = 0, right;
|
||||
view_colors & vc = view_colors::singleton();
|
||||
unsigned long width, height;
|
||||
|
||||
if (!this->sc_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
getmaxyx(this->sc_window, height, width);
|
||||
if (width != this->sc_last_width) {
|
||||
this->window_change();
|
||||
@ -66,6 +70,7 @@ void statusview_curses::do_update(void)
|
||||
int x;
|
||||
|
||||
val = sf.get_value();
|
||||
left += sf.get_left_pad();
|
||||
|
||||
if (sf.is_right_justified()) {
|
||||
right -= sf.get_width();
|
||||
@ -73,7 +78,7 @@ void statusview_curses::do_update(void)
|
||||
}
|
||||
else {
|
||||
x = left;
|
||||
left += sf.get_width() + 1;
|
||||
left += sf.get_width();
|
||||
}
|
||||
this->mvwattrline(this->sc_window,
|
||||
top, x,
|
||||
|
@ -56,7 +56,8 @@ public:
|
||||
sf_cylon(false),
|
||||
sf_cylon_pos(0),
|
||||
sf_role(role),
|
||||
sf_share(0) { };
|
||||
sf_share(0),
|
||||
sf_left_pad(0) { };
|
||||
|
||||
virtual ~status_field() { };
|
||||
|
||||
@ -142,6 +143,9 @@ public:
|
||||
sa.push_back(string_attr(lr, &view_curses::VC_STYLE, COLOR_PAIR(color_pair)));
|
||||
};
|
||||
|
||||
void set_left_pad(size_t val) { this->sf_left_pad = val; };
|
||||
size_t get_left_pad() const { return this->sf_left_pad; };
|
||||
|
||||
/** @return The string value for this field. */
|
||||
attr_line_t &get_value() { return this->sf_value; };
|
||||
|
||||
@ -183,6 +187,7 @@ protected:
|
||||
attr_line_t sf_value; /*< The value to display for this field. */
|
||||
view_colors::role_t sf_role; /*< The color role for this field. */
|
||||
int sf_share;
|
||||
size_t sf_left_pad;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -216,7 +221,8 @@ public:
|
||||
: sc_source(NULL),
|
||||
sc_window(NULL),
|
||||
sc_top(0),
|
||||
sc_last_width(0) {
|
||||
sc_last_width(0),
|
||||
sc_enabled(true) {
|
||||
};
|
||||
virtual ~statusview_curses() { };
|
||||
|
||||
@ -229,6 +235,9 @@ public:
|
||||
void set_window(WINDOW *win) { this->sc_window = win; };
|
||||
WINDOW *get_window() { return this->sc_window; };
|
||||
|
||||
void set_enabled(bool value) { this->sc_enabled = value; };
|
||||
bool get_enabled() const { return this->sc_enabled; };
|
||||
|
||||
void window_change(void) {
|
||||
if (this->sc_source == NULL) {
|
||||
return;
|
||||
@ -280,6 +289,7 @@ private:
|
||||
WINDOW * sc_window;
|
||||
int sc_top;
|
||||
unsigned long sc_last_width;
|
||||
bool sc_enabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -171,18 +171,8 @@ regexp_match(const char *re, const char *str)
|
||||
}
|
||||
|
||||
static
|
||||
void extract(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
||||
json_string extract(const char *str)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
assert(argc == 1);
|
||||
|
||||
str = (const char *)sqlite3_value_text(argv[0]);
|
||||
if (!str) {
|
||||
sqlite3_result_null(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
data_scanner ds(str);
|
||||
data_parser dp(&ds);
|
||||
|
||||
@ -196,14 +186,7 @@ void extract(sqlite3_context *ctx, int argc, sqlite3_value **argv)
|
||||
|
||||
elements_to_json(gen, dp, &dp.dp_pairs);
|
||||
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
|
||||
yajl_gen_get_buf(gen, &buf, &len);
|
||||
sqlite3_result_text(ctx, (const char *) buf, len, SQLITE_TRANSIENT);
|
||||
#ifdef HAVE_SQLITE3_VALUE_SUBTYPE
|
||||
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
|
||||
#endif
|
||||
return json_string(gen);
|
||||
}
|
||||
|
||||
static
|
||||
@ -245,7 +228,12 @@ int string_extension_functions(const struct FuncDef **basic_funcs,
|
||||
{"repl", "The replacement string"},
|
||||
}),
|
||||
|
||||
{ "extract", 1, 0, SQLITE_UTF8, 0, extract },
|
||||
sqlite_func_adapter<decltype(&extract), extract>::builder(
|
||||
"extract",
|
||||
"Automatically Parse and extract data from a string",
|
||||
{
|
||||
{"str", "The string to parse"},
|
||||
}),
|
||||
|
||||
sqlite_func_adapter<decltype(
|
||||
static_cast<bool (*)(const char *, const char *)>(&startswith)),
|
||||
|
@ -95,7 +95,8 @@ public:
|
||||
time_t current_time = time(NULL);
|
||||
char buffer[32];
|
||||
|
||||
strftime(buffer, sizeof(buffer),
|
||||
buffer[0] = ' ';
|
||||
strftime(&buffer[1], sizeof(buffer) - 1,
|
||||
lnav_config.lc_ui_clock_format.c_str(),
|
||||
localtime(¤t_time));
|
||||
sf.set_value(buffer);
|
||||
|
@ -99,6 +99,106 @@ attr_line_t &attr_line_t::with_ansi_string(const char *str, ...)
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t &attr_line_t::append(const attr_line_t &al, text_wrap_settings *tws)
|
||||
{
|
||||
size_t start_len = this->al_string.length();
|
||||
|
||||
this->al_string.append(al.al_string);
|
||||
|
||||
for (auto &sa : al.al_attrs) {
|
||||
this->al_attrs.emplace_back(sa);
|
||||
|
||||
this->al_attrs.back().sa_range.shift(0, start_len);
|
||||
}
|
||||
|
||||
if (tws != nullptr && this->al_string.length() > tws->tws_width) {
|
||||
ssize_t start_pos = start_len;
|
||||
ssize_t line_start = this->al_string.rfind('\n', start_pos);
|
||||
|
||||
if (line_start == string::npos) {
|
||||
line_start = 0;
|
||||
} else {
|
||||
line_start += 1;
|
||||
}
|
||||
|
||||
ssize_t line_len = start_len - line_start;
|
||||
ssize_t usable_width = tws->tws_width - tws->tws_indent;
|
||||
ssize_t avail = max((ssize_t) 0, (ssize_t) tws->tws_width - line_len);
|
||||
|
||||
while (start_pos < this->al_string.length()) {
|
||||
ssize_t lpc;
|
||||
|
||||
for (lpc = start_pos;
|
||||
lpc < this->al_string.length() &&
|
||||
(isalnum(this->al_string[lpc]) ||
|
||||
this->al_string[lpc] == ',' ||
|
||||
this->al_string[lpc] == ';');
|
||||
lpc++) {
|
||||
if (this->al_string[lpc] == '-') {
|
||||
lpc += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (avail != usable_width && lpc - start_pos > avail) {
|
||||
this->insert(start_pos, 1, '\n')
|
||||
.insert(start_pos + 1, tws->tws_indent, ' ');
|
||||
start_pos += 1 + tws->tws_indent;
|
||||
avail = tws->tws_width - tws->tws_indent;
|
||||
} else {
|
||||
avail -= (lpc - start_pos);
|
||||
while (lpc < this->al_string.length() && avail) {
|
||||
if (isalnum(this->al_string[lpc])) {
|
||||
break;
|
||||
}
|
||||
avail -= 1;
|
||||
lpc += 1;
|
||||
}
|
||||
start_pos = lpc;
|
||||
if (!avail) {
|
||||
this->insert(start_pos, 1, '\n')
|
||||
.insert(start_pos + 1, tws->tws_indent, ' ');
|
||||
start_pos += 1 + tws->tws_indent;
|
||||
avail = usable_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t attr_line_t::subline(size_t start, size_t len) const
|
||||
{
|
||||
line_range lr{(int) start, (int) (start + len)};
|
||||
attr_line_t retval;
|
||||
|
||||
retval.al_string = this->al_string.substr(start, len);
|
||||
for (auto &sa : this->al_attrs) {
|
||||
if (!lr.intersects(sa.sa_range)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
retval.al_attrs.emplace_back(lr.intersection(sa.sa_range)
|
||||
.shift(lr.lr_start, -lr.lr_start),
|
||||
sa.sa_type,
|
||||
sa.sa_value);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void attr_line_t::split_lines(std::vector<attr_line_t> &lines) const
|
||||
{
|
||||
size_t pos = 0, next_line;
|
||||
|
||||
while ((next_line = this->al_string.find('\n', pos)) != std::string::npos) {
|
||||
lines.emplace_back(this->subline(pos, next_line - pos));
|
||||
pos = next_line + 1;
|
||||
}
|
||||
lines.emplace_back(this->subline(pos));
|
||||
}
|
||||
|
||||
void view_curses::mvwattrline(WINDOW *window,
|
||||
int y,
|
||||
int x,
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "lnav_log.hh"
|
||||
#include "attr_line.hh"
|
||||
|
||||
#define KEY_CTRL_G 7
|
||||
#define KEY_CTRL_L 12
|
||||
@ -156,266 +157,6 @@ private:
|
||||
int a_last_input;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates a range in a string.
|
||||
*/
|
||||
struct line_range {
|
||||
int lr_start;
|
||||
int lr_end;
|
||||
|
||||
line_range(int start = -1, int end = -1) : lr_start(start), lr_end(end) { };
|
||||
|
||||
bool is_valid() const {
|
||||
return this->lr_start != -1;
|
||||
}
|
||||
|
||||
int length() const
|
||||
{
|
||||
return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start;
|
||||
};
|
||||
|
||||
bool contains(int pos) const {
|
||||
return this->lr_start <= pos && pos < this->lr_end;
|
||||
};
|
||||
|
||||
bool contains(const struct line_range &other) const {
|
||||
return this->contains(other.lr_start) && other.lr_end <= this->lr_end;
|
||||
};
|
||||
|
||||
bool intersects(const struct line_range &other) const {
|
||||
return this->contains(other.lr_start) || this->contains(other.lr_end);
|
||||
};
|
||||
|
||||
void shift(int32_t start, int32_t amount) {
|
||||
if (this->lr_start >= start) {
|
||||
this->lr_start = std::max(start, this->lr_start + amount);
|
||||
}
|
||||
if (this->lr_end != -1 && start < this->lr_end) {
|
||||
this->lr_end += amount;
|
||||
if (this->lr_end < this->lr_start) {
|
||||
this->lr_end = this->lr_start;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ltrim(const char *str) {
|
||||
while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) {
|
||||
this->lr_start += 1;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator<(const struct line_range &rhs) const
|
||||
{
|
||||
if (this->lr_start < rhs.lr_start) { return true; }
|
||||
else if (this->lr_start > rhs.lr_start) { return false; }
|
||||
|
||||
if (this->lr_end == rhs.lr_end) { return false; }
|
||||
|
||||
if (this->lr_end < rhs.lr_end) { return true; }
|
||||
return false;
|
||||
};
|
||||
|
||||
bool operator==(const struct line_range &rhs) const {
|
||||
return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end);
|
||||
};
|
||||
|
||||
const char *substr(const std::string &str) const {
|
||||
if (this->lr_start == -1) {
|
||||
return str.c_str();
|
||||
}
|
||||
return &(str.c_str()[this->lr_start]);
|
||||
}
|
||||
|
||||
size_t sublen(const std::string &str) const {
|
||||
if (this->lr_start == -1) {
|
||||
return str.length();
|
||||
}
|
||||
if (this->lr_end == -1) {
|
||||
return str.length() - this->lr_start;
|
||||
}
|
||||
return this->length();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for attribute values for a substring.
|
||||
*/
|
||||
typedef union {
|
||||
void *sav_ptr;
|
||||
int64_t sav_int;
|
||||
} string_attr_value_t;
|
||||
|
||||
class string_attr_type { };
|
||||
typedef string_attr_type *string_attr_type_t;
|
||||
|
||||
struct string_attr {
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, void *val)
|
||||
: sa_range(lr), sa_type(type) {
|
||||
this->sa_value.sav_ptr = val;
|
||||
};
|
||||
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, int64_t val = 0)
|
||||
: sa_range(lr), sa_type(type) {
|
||||
this->sa_value.sav_int = val;
|
||||
};
|
||||
|
||||
string_attr() : sa_type(NULL) { };
|
||||
|
||||
bool operator<(const struct string_attr &rhs) const
|
||||
{
|
||||
return this->sa_range < rhs.sa_range;
|
||||
};
|
||||
|
||||
struct line_range sa_range;
|
||||
string_attr_type_t sa_type;
|
||||
string_attr_value_t sa_value;
|
||||
};
|
||||
|
||||
/** A map of line ranges to attributes for that range. */
|
||||
typedef std::vector<string_attr> string_attrs_t;
|
||||
|
||||
inline string_attrs_t::const_iterator
|
||||
find_string_attr(const string_attrs_t &sa, string_attr_type_t type)
|
||||
{
|
||||
string_attrs_t::const_iterator iter;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
if (iter->sa_type == type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
inline string_attrs_t::iterator
|
||||
find_string_attr(string_attrs_t &sa, const struct line_range &lr)
|
||||
{
|
||||
string_attrs_t::iterator iter;
|
||||
struct line_range retval;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
if (lr.contains(iter->sa_range)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
inline struct line_range
|
||||
find_string_attr_range(const string_attrs_t &sa, string_attr_type_t type)
|
||||
{
|
||||
string_attrs_t::const_iterator iter = find_string_attr(sa, type);
|
||||
|
||||
if (iter != sa.end()) {
|
||||
return iter->sa_range;
|
||||
}
|
||||
|
||||
return line_range();
|
||||
}
|
||||
|
||||
inline void remove_string_attr(string_attrs_t &sa, const struct line_range &lr)
|
||||
{
|
||||
string_attrs_t::iterator iter;
|
||||
|
||||
while ((iter = find_string_attr(sa, lr)) != sa.end()) {
|
||||
sa.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
inline void shift_string_attrs(string_attrs_t &sa, int32_t start, int32_t amount)
|
||||
{
|
||||
for (string_attrs_t::iterator iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
iter->sa_range.shift(start, amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A line that has attributes.
|
||||
*/
|
||||
class attr_line_t {
|
||||
public:
|
||||
attr_line_t() {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
attr_line_t(const std::string &str) : al_string(str) {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
attr_line_t(const char *str) : al_string(str) {
|
||||
this->al_attrs.reserve(RESERVE_SIZE);
|
||||
};
|
||||
|
||||
/** @return The string itself. */
|
||||
std::string &get_string() { return this->al_string; };
|
||||
|
||||
/** @return The attributes for the string. */
|
||||
string_attrs_t &get_attrs() { return this->al_attrs; };
|
||||
|
||||
attr_line_t &with_string(const std::string &str) {
|
||||
this->al_string = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
attr_line_t &with_ansi_string(const char *str, ...);
|
||||
|
||||
attr_line_t &with_attr(const string_attr &sa) {
|
||||
this->al_attrs.push_back(sa);
|
||||
return *this;
|
||||
};
|
||||
|
||||
attr_line_t &right_justify(unsigned long width) {
|
||||
long padding = width - this->length();
|
||||
if (padding > 0) {
|
||||
this->al_string.insert(0, padding, ' ');
|
||||
for (std::vector<string_attr>::iterator iter = this->al_attrs.begin();
|
||||
iter != this->al_attrs.end();
|
||||
++iter) {
|
||||
iter->sa_range.lr_start += padding;
|
||||
iter->sa_range.lr_end += padding;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ssize_t length() const {
|
||||
size_t retval = this->al_string.length();
|
||||
|
||||
for (std::vector<string_attr>::const_iterator iter = this->al_attrs.begin();
|
||||
iter != this->al_attrs.end();
|
||||
++iter) {
|
||||
retval = std::max(retval, (size_t) iter->sa_range.lr_start);
|
||||
if (iter->sa_range.lr_end != -1) {
|
||||
retval = std::max(retval, (size_t) iter->sa_range.lr_end);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
bool empty() const {
|
||||
return this->length() == 0;
|
||||
};
|
||||
|
||||
/** Clear the string and the attributes for the string. */
|
||||
attr_line_t &clear()
|
||||
{
|
||||
this->al_string.clear();
|
||||
this->al_attrs.clear();
|
||||
|
||||
return *this;
|
||||
};
|
||||
|
||||
private:
|
||||
const static size_t RESERVE_SIZE = 128;
|
||||
|
||||
std::string al_string;
|
||||
string_attrs_t al_attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that encapsulates a method to execute and the object on which to
|
||||
* execute it.
|
||||
|
@ -17,7 +17,7 @@
|
||||
*
|
||||
* 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
|
||||
* WARRANTIES OF MERCHAN`TABILITY 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;
|
||||
|
@ -21,8 +21,19 @@ add_executable(test_abbrev test_abbrev.cc
|
||||
../src/pcrepp.cc
|
||||
../src/lnav_log.cc
|
||||
../src/spookyhash/SpookyV2.cpp)
|
||||
add_executable(test_help_text_formatter test_help_text_formatter.cc
|
||||
../src/help_text_formatter.cc
|
||||
../src/view_curses.cc
|
||||
../src/lnav_log.cc
|
||||
../src/ansi_scrubber.cc
|
||||
../src/pcrepp.cc)
|
||||
add_executable(drive_sql_anno drive_sql_anno.cc ../src/lnav_log.cc ../src/pcrepp.cc)
|
||||
link_directories(/opt/local/lib)
|
||||
target_link_libraries(test_pcrepp /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_reltime /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_date_time_scanner /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_abbrev /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(drive_sql_anno /opt/local/lib/libpcre.a)
|
||||
target_link_libraries(test_help_text_formatter
|
||||
/opt/local/lib/libpcre.a
|
||||
/opt/local/lib/libncurses.a)
|
||||
|
@ -27,6 +27,7 @@ check_PROGRAMS = \
|
||||
drive_sequencer \
|
||||
drive_shlexer \
|
||||
drive_sql \
|
||||
drive_sql_anno \
|
||||
drive_view_colors \
|
||||
drive_vt52_curses \
|
||||
drive_readline_curses \
|
||||
@ -41,6 +42,7 @@ check_PROGRAMS = \
|
||||
test_concise \
|
||||
test_date_time_scanner \
|
||||
test_grep_proc2 \
|
||||
test_help_text_formatter \
|
||||
test_hist_source \
|
||||
test_json_ptr \
|
||||
test_line_buffer2 \
|
||||
@ -83,6 +85,10 @@ test_date_time_scanner_LDADD = ../src/libdiag.a $(SQLITE3_LIBS)
|
||||
test_grep_proc2_SOURCES = test_grep_proc2.cc
|
||||
test_grep_proc2_LDADD = ../src/libdiag.a $(PCRE_LIBS) -lz
|
||||
|
||||
test_help_text_formatter_SOURCES = test_help_text_formatter.cc
|
||||
test_help_text_formatter_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz \
|
||||
$(CONFIG_OBJS) $(SQLITE3_LIBS)
|
||||
|
||||
test_hist_source_SOURCES = test_hist_source.cc
|
||||
test_hist_source_LDADD = ../src/libdiag.a $(CURSES_LIB) -lz \
|
||||
$(CONFIG_OBJS)
|
||||
@ -200,6 +206,18 @@ drive_sql_LDADD = \
|
||||
$(LIBCURL) \
|
||||
-lpcrecpp
|
||||
|
||||
drive_sql_anno_SOURCES = \
|
||||
drive_sql_anno.cc
|
||||
drive_sql_anno_LDADD = \
|
||||
../src/libdiag.a \
|
||||
$(CONFIG_OBJS) \
|
||||
$(SQLITE3_LIBS) \
|
||||
$(PCRE_LIBS) \
|
||||
$(CURSES_LIB) \
|
||||
$(READLINE_LIBS) \
|
||||
$(LIBCURL) \
|
||||
-lpcrecpp
|
||||
|
||||
slicer_SOURCES = slicer.cc
|
||||
slicer_LDADD = ../src/libdiag.a
|
||||
|
||||
|
@ -88,7 +88,7 @@ public:
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, retval = EXIT_SUCCESS;
|
||||
bool wait_for_input = false;
|
||||
bool wait_for_input = false, set_height = false;
|
||||
my_source ms;
|
||||
WINDOW *win;
|
||||
|
||||
@ -104,6 +104,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 'h':
|
||||
lv.set_height(vis_line_t(atoi(optarg)));
|
||||
set_height = true;
|
||||
break;
|
||||
case 't':
|
||||
lv.set_top(vis_line_t(atoi(optarg)));
|
||||
@ -120,10 +121,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (!set_height) {
|
||||
unsigned long height, width;
|
||||
getmaxyx(win, height, width);
|
||||
lv.set_height(vis_line_t(height - lv.get_y()));
|
||||
}
|
||||
|
||||
lv.do_update();
|
||||
refresh();
|
||||
if (wait_for_input)
|
||||
if (wait_for_input) {
|
||||
getch();
|
||||
}
|
||||
endwin();
|
||||
|
||||
return retval;
|
||||
|
76
test/drive_sql_anno.cc
Normal file
76
test/drive_sql_anno.cc
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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.
|
||||
*
|
||||
* @file drive_sql_anno.cc
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "lnav.hh"
|
||||
#include "sql_util.hh"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int retval = EXIT_SUCCESS;
|
||||
|
||||
log_argv(argc, argv);
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "error: expecting an SQL statement\n");
|
||||
retval = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
attr_line_t al(argv[1]);
|
||||
|
||||
annotate_sql_statement(al);
|
||||
|
||||
for (auto &attr : al.get_attrs()) {
|
||||
auto &lr = attr.sa_range;
|
||||
|
||||
printf(" %d:%d (%s) -- %s\n",
|
||||
lr.lr_start, lr.lr_end,
|
||||
attr.sa_type->sat_name,
|
||||
al.get_substring(lr).c_str());
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
int near;
|
||||
sscanf(argv[2], "%d", &near);
|
||||
|
||||
auto iter = find_string_attr(al.get_attrs(), (size_t) near);
|
||||
if (iter != al.get_attrs().end()) {
|
||||
printf("nearest %s\n",
|
||||
al.get_substring(iter->sa_range).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
87
test/test_help_text_formatter.cc
Normal file
87
test/test_help_text_formatter.cc
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2017, 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 "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <view_curses.hh>
|
||||
#include <attr_line.hh>
|
||||
|
||||
#include "lnav_config.hh"
|
||||
#include "help_text_formatter.hh"
|
||||
|
||||
struct _lnav_config lnav_config;
|
||||
|
||||
lnav_config_listener *lnav_config_listener::LISTENER_LIST;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int retval = EXIT_SUCCESS;
|
||||
|
||||
static help_text ht = help_text(
|
||||
"regexp_replace",
|
||||
"Replace parts of a string that match a regular expression")
|
||||
.with_parameters(
|
||||
{
|
||||
{"str", "The string to perform replacements on"},
|
||||
{"re", "The regular expression to match"},
|
||||
{"repl", "The replacement string"},
|
||||
})
|
||||
.with_example(
|
||||
{
|
||||
";SELECT regexp_replace('abbb bbbc', 'b+', '') AS res",
|
||||
"a c",
|
||||
});
|
||||
|
||||
{
|
||||
setenv("TERM", "ansi", 1);
|
||||
screen_curses sc;
|
||||
view_colors::init();
|
||||
|
||||
attr_line_t al;
|
||||
|
||||
format_help_text_for_term(ht, 35, al);
|
||||
|
||||
std::vector<attr_line_t> lines;
|
||||
|
||||
al.split_lines(lines);
|
||||
|
||||
line_range lr{0, 80};
|
||||
|
||||
int y = 0;
|
||||
for (auto &line : lines) {
|
||||
view_curses::mvwattrline(sc.get_window(), y++, 0, line, lr);
|
||||
}
|
||||
|
||||
getch();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
@ -260,6 +260,18 @@ log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_hostname,log_pi
|
||||
1,<NULL>,2006-12-03 09:23:38.000,0,info,0,veridian,16442,automount,0,/auto/opt
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ";select sc_bytes from logline" \
|
||||
-c ':write-csv-to -' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "logline table is not working for defined columns" <<EOF
|
||||
sc_bytes
|
||||
134
|
||||
46210
|
||||
78929
|
||||
EOF
|
||||
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ':goto 1' \
|
||||
|
@ -145,6 +145,35 @@ Row 0:
|
||||
EOF
|
||||
|
||||
|
||||
run_test ./drive_sql "select extract('foo=1') as result"
|
||||
|
||||
check_output "" <<EOF
|
||||
Row 0:
|
||||
Column result: {"foo":1}
|
||||
EOF
|
||||
|
||||
run_test ./drive_sql "select extract('foo=1; bar=2') as result"
|
||||
|
||||
check_output "" <<EOF
|
||||
Row 0:
|
||||
Column result: {"foo":1,"bar":2}
|
||||
EOF
|
||||
|
||||
run_test ./drive_sql "select extract(null) as result"
|
||||
|
||||
check_output "" <<EOF
|
||||
Row 0:
|
||||
Column result: (null)
|
||||
EOF
|
||||
|
||||
run_test ./drive_sql "select extract(1) as result"
|
||||
|
||||
check_output "" <<EOF
|
||||
Row 0:
|
||||
Column result: {"col_0":1}
|
||||
EOF
|
||||
|
||||
|
||||
run_test ./drive_sql "SELECT * FROM regexp_capture('foo bar', '\w+ (\w+)')"
|
||||
|
||||
check_output "" <<EOF
|
||||
|
@ -79,7 +79,7 @@ int main(int argc, char *argv[])
|
||||
lnav_config.lc_ui_clock_format = "abc";
|
||||
tss.update_time();
|
||||
val = sf.get_value();
|
||||
assert(val.get_string() == "abc");
|
||||
assert(val.get_string() == " abc");
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
Loading…
Reference in New Issue
Block a user