[hotkeys] change curly braces to move to the next/prev section

and... a bunch of other stuff
pull/1242/head
Tim Stack 4 months ago
parent 0eb394f4b8
commit ee345321f7

@ -122,6 +122,11 @@ Features:
* Added a `log_msg_values` column to the `all_logs` SQL * Added a `log_msg_values` column to the `all_logs` SQL
table that contains a JSON object with the top 5 values table that contains a JSON object with the top 5 values
for the fields extracted from the log message. for the fields extracted from the log message.
* Added `:next-section` and `:prev-section` commands for
moving to the next and previous section of a document.
For example, the next section in a man page or JSON
array. The default keymap has been changed to bind
the curly brace keys to these commands.
* Added Nextcloud log format from Adam Monsen. * Added Nextcloud log format from Adam Monsen.
* Added GitHub Event Log format for files from gharchive.org. * Added GitHub Event Log format for files from gharchive.org.
It makes a good example of a JSON-Lines format. It makes a good example of a JSON-Lines format.
@ -158,6 +163,9 @@ Interface changes:
used to draw the overlay contents now as well. (The used to draw the overlay contents now as well. (The
overlay is used to display the parser details, comments, overlay is used to display the parser details, comments,
and annotations.) and annotations.)
* The `{` and `}` keys have been changed from moving
through the "location history" to moving to the previous
and next section in a document.
* Added indent guidelines when structured data is detected. * Added indent guidelines when structured data is detected.
Breaking changes: Breaking changes:

@ -174,6 +174,8 @@ scrub_ansi_string(std::string& str, string_attrs_t* sa)
if (lhs_pair.first == '_' || rhs_pair.first == '_') { if (lhs_pair.first == '_' || rhs_pair.first == '_') {
if (sa != nullptr && bold_range.is_valid()) { if (sa != nullptr && bold_range.is_valid()) {
shift_string_attrs(
*sa, bold_range.lr_start, -bold_range.length() * 2);
sa->emplace_back(bold_range, sa->emplace_back(bold_range,
VC_STYLE.value(text_attrs{A_BOLD})); VC_STYLE.value(text_attrs{A_BOLD}));
bold_range.clear(); bold_range.clear();
@ -191,6 +193,8 @@ scrub_ansi_string(std::string& str, string_attrs_t* sa)
}); });
} else { } else {
if (sa != nullptr && ul_range.is_valid()) { if (sa != nullptr && ul_range.is_valid()) {
shift_string_attrs(
*sa, ul_range.lr_start, -ul_range.length() * 2);
sa->emplace_back( sa->emplace_back(
ul_range, VC_STYLE.value(text_attrs{A_UNDERLINE})); ul_range, VC_STYLE.value(text_attrs{A_UNDERLINE}));
ul_range.clear(); ul_range.clear();
@ -216,26 +220,25 @@ scrub_ansi_string(std::string& str, string_attrs_t* sa)
auto output_size = fill_index - sf.sf_begin; auto output_size = fill_index - sf.sf_begin;
auto erased_size = sf.length() - output_size; auto erased_size = sf.length() - output_size;
if (sa != nullptr) {
#if 0
shift_string_attrs(
*sa, caps->c_begin + sf.length() / 3, -erased_size);
#endif
sa->emplace_back(line_range{last_origin_offset_end,
sf.sf_begin + (int) output_size},
SA_ORIGIN_OFFSET.value(origin_offset));
}
if (sa != nullptr && ul_range.is_valid()) { if (sa != nullptr && ul_range.is_valid()) {
shift_string_attrs(
*sa, ul_range.lr_start, -ul_range.length() * 2);
sa->emplace_back(ul_range, sa->emplace_back(ul_range,
VC_STYLE.value(text_attrs{A_UNDERLINE})); VC_STYLE.value(text_attrs{A_UNDERLINE}));
ul_range.clear(); ul_range.clear();
} }
if (sa != nullptr && bold_range.is_valid()) { if (sa != nullptr && bold_range.is_valid()) {
shift_string_attrs(
*sa, bold_range.lr_start, -bold_range.length() * 2);
sa->emplace_back(bold_range, sa->emplace_back(bold_range,
VC_STYLE.value(text_attrs{A_BOLD})); VC_STYLE.value(text_attrs{A_BOLD}));
bold_range.clear(); bold_range.clear();
} }
if (sa != nullptr) {
sa->emplace_back(line_range{last_origin_offset_end,
sf.sf_begin + (int) output_size},
SA_ORIGIN_OFFSET.value(origin_offset));
}
str.erase(str.begin() + fill_index, str.begin() + sf.sf_end); str.erase(str.begin() + fill_index, str.begin() + sf.sf_end);
last_origin_offset_end = sf.sf_begin + output_size; last_origin_offset_end = sf.sf_begin + output_size;

@ -277,13 +277,38 @@ curses_color_to_terminal_color(int curses_color)
} }
} }
static bool
get_no_color()
{
return getenv("NO_COLOR") != nullptr;
}
static bool
get_yes_color()
{
return getenv("YES_COLOR") != nullptr;
}
static bool
get_fd_tty(int fd)
{
return isatty(fd);
}
void void
println(FILE* file, const attr_line_t& al) println(FILE* file, const attr_line_t& al)
{ {
static const auto IS_NO_COLOR = get_no_color();
static const auto IS_YES_COLOR = get_yes_color();
static const auto IS_STDOUT_TTY = get_fd_tty(STDOUT_FILENO);
static const auto IS_STDERR_TTY = get_fd_tty(STDERR_FILENO);
const auto& str = al.get_string(); const auto& str = al.get_string();
if (getenv("NO_COLOR") != nullptr if (IS_NO_COLOR || (file != stdout && file != stderr)
|| (!isatty(fileno(file)) && getenv("YES_COLOR") == nullptr)) || (((file == stdout && !IS_STDOUT_TTY)
|| (file == stderr && !IS_STDERR_TTY))
&& !IS_YES_COLOR))
{ {
fmt::print(file, "{}\n", str); fmt::print(file, "{}\n", str);
return; return;

@ -103,4 +103,14 @@ TEST_CASE("strnatcmp")
CHECK(strnatcasecmp(lhs.length(), lhs.data(), rhs.length(), rhs.data()) CHECK(strnatcasecmp(lhs.length(), lhs.data(), rhs.length(), rhs.data())
< 0); < 0);
} }
{
const std::string a = "10.112.81.15";
const std::string b = "192.168.202.254";
int ipcmp = 0;
auto rc = ipv4cmp(a.length(), a.c_str(), b.length(), b.c_str(), &ipcmp);
CHECK(rc == 1);
CHECK(ipcmp == -1);
}
} }

@ -275,13 +275,13 @@ int ipv4cmp(int a_len, nat_char const *a,
} }
for (; ai < a_len; ai++) { for (; ai < a_len; ai++) {
if (!isdigit((unsigned char)a[ai]) || a[ai] != '.') { if (!isdigit((unsigned char)a[ai]) && a[ai] != '.') {
return 0; return 0;
} }
} }
for (; bi < b_len; bi++) { for (; bi < b_len; bi++) {
if (!isdigit((unsigned char)b[bi]) || b[bi] != '.') { if (!isdigit((unsigned char)b[bi]) && b[bi] != '.') {
return 0; return 0;
} }
} }

@ -224,4 +224,21 @@ private:
bool ds_units{false}; bool ds_units{false};
}; };
inline data_token_t
to_closer(data_token_t dt)
{
switch (dt) {
case DT_XML_OPEN_TAG:
return DT_XML_CLOSE_TAG;
case DT_LCURLY:
return DT_RCURLY;
case DT_LSQUARE:
return DT_RSQUARE;
case DT_LPAREN:
return DT_RPAREN;
default:
ensure(0);
}
}
#endif #endif

@ -27,6 +27,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <algorithm>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -60,6 +61,117 @@ hier_node::lookup_child(section_key_t key) const
})); }));
} }
nonstd::optional<size_t>
hier_node::child_index(const hier_node* hn) const
{
size_t retval = 0;
for (const auto& child : this->hn_children) {
if (child.get() == hn) {
return retval;
}
retval += 1;
}
return nonstd::nullopt;
}
nonstd::optional<hier_node::child_neighbors_result>
hier_node::child_neighbors(const lnav::document::hier_node* hn,
file_off_t offset) const
{
auto index_opt = this->child_index(hn);
if (!index_opt) {
return nonstd::nullopt;
}
hier_node::child_neighbors_result retval;
if (index_opt.value() == 0) {
if (this->hn_parent != nullptr) {
auto parent_neighbors_opt
= this->hn_parent->child_neighbors(this, offset);
if (parent_neighbors_opt) {
retval.cnr_previous = parent_neighbors_opt->cnr_previous;
}
} else {
retval.cnr_previous = hn;
}
} else {
const auto* prev_hn = this->hn_children[index_opt.value() - 1].get();
if (hn->hn_line_number == 0
|| (hn->hn_line_number - prev_hn->hn_line_number) > 1)
{
retval.cnr_previous = prev_hn;
} else if (this->hn_parent != nullptr) {
auto parent_neighbors_opt
= this->hn_parent->child_neighbors(this, offset);
if (parent_neighbors_opt) {
retval.cnr_previous = parent_neighbors_opt->cnr_previous;
}
}
}
if (index_opt.value() == this->hn_children.size() - 1) {
if (this->hn_parent != nullptr) {
auto parent_neighbors_opt
= this->hn_parent->child_neighbors(this, offset);
if (parent_neighbors_opt) {
retval.cnr_next = parent_neighbors_opt->cnr_next;
}
} else if (!hn->hn_children.empty()) {
for (const auto& child : hn->hn_children) {
if (child->hn_start > offset) {
retval.cnr_next = child.get();
break;
}
}
}
} else {
const auto* next_hn = this->hn_children[index_opt.value() + 1].get();
if (next_hn->hn_start > offset
&& (hn->hn_line_number == 0
|| (next_hn->hn_line_number - hn->hn_line_number) > 1))
{
retval.cnr_next = next_hn;
} else if (this->hn_parent != nullptr) {
auto parent_neighbors_opt
= this->hn_parent->child_neighbors(this, offset);
if (parent_neighbors_opt) {
retval.cnr_next = parent_neighbors_opt->cnr_next;
}
}
}
return retval;
}
nonstd::optional<hier_node::child_neighbors_result>
hier_node::line_neighbors(size_t ln) const
{
if (this->hn_children.empty()) {
return nonstd::nullopt;
}
hier_node::child_neighbors_result retval;
for (const auto& child : this->hn_children) {
if (child->hn_line_number > ln) {
retval.cnr_next = child.get();
break;
}
retval.cnr_previous = child.get();
}
return retval;
}
nonstd::optional<const hier_node*> nonstd::optional<const hier_node*>
hier_node::lookup_path(const hier_node* root, hier_node::lookup_path(const hier_node* root,
const std::vector<section_key_t>& path) const std::vector<section_key_t>& path)
@ -81,11 +193,24 @@ hier_node::lookup_path(const hier_node* root,
return retval; return retval;
} }
std::vector<section_key_t>
metadata::path_for_range(size_t start, size_t stop)
{
std::vector<section_key_t> retval;
this->m_sections_tree.visit_overlapping(
start, stop, [&retval](const lnav::document::section_interval_t& iv) {
retval.emplace_back(iv.value);
});
return retval;
}
struct metadata_builder { struct metadata_builder {
std::vector<section_interval_t> mb_intervals; std::vector<section_interval_t> mb_intervals;
std::vector<section_type_interval_t> mb_type_intervals; std::vector<section_type_interval_t> mb_type_intervals;
std::unique_ptr<hier_node> mb_root_node; std::unique_ptr<hier_node> mb_root_node;
std::set<size_t> mb_indents; std::set<size_t> mb_indents;
text_format_t mb_text_format{text_format_t::TF_UNKNOWN};
metadata to_metadata() && metadata to_metadata() &&
{ {
@ -94,6 +219,7 @@ struct metadata_builder {
std::move(this->mb_root_node), std::move(this->mb_root_node),
std::move(this->mb_type_intervals), std::move(this->mb_type_intervals),
std::move(this->mb_indents), std::move(this->mb_indents),
this->mb_text_format,
}; };
} }
}; };
@ -238,6 +364,16 @@ discover_metadata_int(const attr_line_t& al, metadata_builder& mb)
} }
}); });
hier_node::depth_first(
mb.mb_root_node.get(), [&orig_attrs](hier_node* node) {
auto off_opt = get_string_attr(
orig_attrs, &SA_ORIGIN_OFFSET, node->hn_start);
if (off_opt) {
node->hn_start += off_opt.value()->sa_value.get<int64_t>();
}
});
if (!root_node->hn_children.empty() if (!root_node->hn_children.empty()
|| !root_node->hn_named_children.empty()) || !root_node->hn_named_children.empty())
{ {
@ -284,6 +420,7 @@ public:
{ {
metadata_builder mb; metadata_builder mb;
mb.mb_text_format = this->sw_text_format;
while (true) { while (true) {
auto tokenize_res auto tokenize_res
= this->sw_scanner.tokenize2(this->sw_text_format); = this->sw_scanner.tokenize2(this->sw_text_format);
@ -334,17 +471,26 @@ public:
this->sw_interval_state.resize(this->sw_depth + 1); this->sw_interval_state.resize(this->sw_depth + 1);
this->sw_hier_nodes.push_back( this->sw_hier_nodes.push_back(
std::make_unique<hier_node>()); std::make_unique<hier_node>());
this->sw_container_tokens.push_back(to_closer(dt));
break; break;
case DT_XML_CLOSE_TAG: { case DT_XML_CLOSE_TAG: {
auto term = this->flush_values(); auto term = this->flush_values();
if (this->sw_depth > 0) { if (this->sw_depth > 0) {
if (term) { auto found = false;
this->append_child_node(term); do {
} if (this->sw_container_tokens.back() == dt) {
this->sw_interval_state.pop_back(); found = true;
this->sw_hier_stage }
= std::move(this->sw_hier_nodes.back()); if (term) {
this->sw_hier_nodes.pop_back(); this->append_child_node(term);
term = nonstd::nullopt;
}
this->sw_interval_state.pop_back();
this->sw_hier_stage
= std::move(this->sw_hier_nodes.back());
this->sw_hier_nodes.pop_back();
this->sw_container_tokens.pop_back();
} while (!found);
} }
this->append_child_node(el.e_capture); this->append_child_node(el.e_capture);
if (this->sw_depth > 0) { if (this->sw_depth > 0) {
@ -418,6 +564,7 @@ public:
this->sw_interval_state.resize(this->sw_depth + 1); this->sw_interval_state.resize(this->sw_depth + 1);
this->sw_hier_nodes.push_back( this->sw_hier_nodes.push_back(
std::make_unique<hier_node>()); std::make_unique<hier_node>());
this->sw_container_tokens.push_back(to_closer(dt));
} else { } else {
this->sw_values.emplace_back(el); this->sw_values.emplace_back(el);
} }
@ -426,37 +573,54 @@ public:
case DT_RCURLY: case DT_RCURLY:
case DT_RSQUARE: case DT_RSQUARE:
case DT_RPAREN: case DT_RPAREN:
if (this->is_structured_text()) { if (this->is_structured_text()
&& !this->sw_container_tokens.empty()
&& std::find(this->sw_container_tokens.begin(),
this->sw_container_tokens.end(),
dt)
!= this->sw_container_tokens.end())
{
auto term = this->flush_values(); auto term = this->flush_values();
if (this->sw_depth > 0) { if (this->sw_depth > 0) {
this->append_child_node(term); auto found = false;
this->sw_depth -= 1; do {
this->sw_interval_state.pop_back(); if (this->sw_container_tokens.back() == dt) {
this->sw_hier_stage found = true;
= std::move(this->sw_hier_nodes.back()); }
this->sw_hier_nodes.pop_back(); this->append_child_node(term);
if (this->sw_interval_state.back().is_start) { term = nonstd::nullopt;
data_scanner::capture_t obj_cap = { this->sw_depth -= 1;
static_cast<int>( this->sw_interval_state.pop_back();
this->sw_interval_state.back() this->sw_hier_stage
.is_start.value()), = std::move(this->sw_hier_nodes.back());
el.e_capture.c_end, this->sw_hier_nodes.pop_back();
}; if (this->sw_interval_state.back().is_start) {
data_scanner::capture_t obj_cap = {
auto sf = this->sw_scanner.to_string_fragment( static_cast<int>(
obj_cap); this->sw_interval_state.back()
if (!sf.find('\n')) { .is_start.value()),
this->sw_hier_stage->hn_named_children el.e_capture.c_end,
.clear(); };
this->sw_hier_stage->hn_children.clear();
while (!this->sw_intervals.empty() auto sf
&& this->sw_intervals.back().start = this->sw_scanner.to_string_fragment(
> obj_cap.c_begin) obj_cap);
{ if (!sf.find('\n')) {
this->sw_intervals.pop_back(); this->sw_hier_stage->hn_named_children
.clear();
this->sw_hier_stage->hn_children
.clear();
while (
!this->sw_intervals.empty()
&& this->sw_intervals.back().start
> obj_cap.c_begin)
{
this->sw_intervals.pop_back();
}
} }
} }
} this->sw_container_tokens.pop_back();
} while (!found);
} }
} }
this->sw_values.emplace_back(el); this->sw_values.emplace_back(el);
@ -650,18 +814,22 @@ private:
auto new_key = ivstate.is_name.empty() auto new_key = ivstate.is_name.empty()
? lnav::document::section_key_t{top_node->hn_children.size()} ? lnav::document::section_key_t{top_node->hn_children.size()}
: lnav::document::section_key_t{ivstate.is_name}; : lnav::document::section_key_t{ivstate.is_name};
this->sw_intervals.emplace_back(iv_start, iv_stop, new_key);
auto* retval = new_node.get(); auto* retval = new_node.get();
new_node->hn_parent = top_node; new_node->hn_parent = top_node;
new_node->hn_start = this->sw_intervals.back().start; new_node->hn_start = iv_start;
new_node->hn_line_number = ivstate.is_line_number; new_node->hn_line_number = ivstate.is_line_number;
if (!ivstate.is_name.empty()) { if (this->sw_depth == 1
top_node->hn_named_children.insert({ || new_node->hn_line_number != top_node->hn_line_number)
ivstate.is_name, {
retval, this->sw_intervals.emplace_back(iv_start, iv_stop, new_key);
}); if (!ivstate.is_name.empty()) {
top_node->hn_named_children.insert({
ivstate.is_name,
retval,
});
}
top_node->hn_children.emplace_back(std::move(new_node));
} }
top_node->hn_children.emplace_back(std::move(new_node));
ivstate.is_start = nonstd::nullopt; ivstate.is_start = nonstd::nullopt;
ivstate.is_line_number = 0; ivstate.is_line_number = 0;
ivstate.is_name.clear(); ivstate.is_name.clear();
@ -676,6 +844,7 @@ private:
bool sw_at_start{true}; bool sw_at_start{true};
std::set<size_t> sw_indents; std::set<size_t> sw_indents;
std::vector<element> sw_values{}; std::vector<element> sw_values{};
std::vector<data_token_t> sw_container_tokens;
std::vector<interval_state> sw_interval_state; std::vector<interval_state> sw_interval_state;
std::vector<lnav::document::section_interval_t> sw_intervals; std::vector<lnav::document::section_interval_t> sw_intervals;
std::vector<lnav::document::section_type_interval_t> sw_type_intervals; std::vector<lnav::document::section_type_interval_t> sw_type_intervals;

@ -69,6 +69,18 @@ struct hier_node {
nonstd::optional<hier_node*> lookup_child(section_key_t key) const; nonstd::optional<hier_node*> lookup_child(section_key_t key) const;
nonstd::optional<size_t> child_index(const hier_node* hn) const;
struct child_neighbors_result {
nonstd::optional<const hier_node*> cnr_previous;
nonstd::optional<const hier_node*> cnr_next;
};
nonstd::optional<child_neighbors_result> child_neighbors(
const hier_node* hn, file_off_t offset) const;
nonstd::optional<child_neighbors_result> line_neighbors(size_t ln) const;
nonstd::optional<size_t> find_line_number(const std::string& str) const nonstd::optional<size_t> find_line_number(const std::string& str) const
{ {
auto iter = this->hn_named_children.find(str); auto iter = this->hn_named_children.find(str);
@ -115,6 +127,9 @@ struct metadata {
std::unique_ptr<hier_node> m_sections_root; std::unique_ptr<hier_node> m_sections_root;
section_types_tree_t m_section_types_tree; section_types_tree_t m_section_types_tree;
std::set<size_t> m_indents; std::set<size_t> m_indents;
text_format_t m_text_format{text_format_t::TF_UNKNOWN};
std::vector<section_key_t> path_for_range(size_t start, size_t stop);
std::vector<breadcrumb::possibility> possibility_provider( std::vector<breadcrumb::possibility> possibility_provider(
const std::vector<section_key_t>& path); const std::vector<section_key_t>& path);

@ -476,26 +476,33 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
{ {
auto line_meta_opt = this->fos_lss.find_bookmark_metadata(row); auto line_meta_opt = this->fos_lss.find_bookmark_metadata(row);
auto file_and_line = this->fos_lss.find_line_with_file(row); if (!this->fos_contexts.empty()
if (file_and_line && !file_and_line->second->is_continued()) { && this->fos_contexts.top().c_show_applicable_annotations)
auto applicable_anno = lnav::log::annotate::applicable(row); {
if (!applicable_anno.empty() auto file_and_line = this->fos_lss.find_line_with_file(row);
&& (!line_meta_opt
|| line_meta_opt.value()->bm_annotations.la_pairs.empty())) if (file_and_line && !file_and_line->second->is_continued()) {
{ auto applicable_anno = lnav::log::annotate::applicable(row);
auto anno_msg if (!applicable_anno.empty()
= attr_line_t(" ") && (!line_meta_opt
.append(":memo:"_emoji) || line_meta_opt.value()->bm_annotations.la_pairs.empty()))
.append(" Annotations available, ") {
.append(lv.get_selection() == row auto anno_msg
? "use " = attr_line_t(" ")
: "focus on this line and use ") .append(":memo:"_emoji)
.append(":annotate"_quoted_code) .append(" Annotations available, ")
.append(" to apply them") .append(lv.get_selection() == row
.append(lv.get_selection() == row ? " to this line" : "") ? "use "
.with_attr_for_all(VC_ROLE.value(role_t::VCR_COMMENT)); : "focus on this line and use ")
.append(":annotate"_quoted_code)
dst.emplace_back(anno_msg); .append(" to apply them")
.append(lv.get_selection() == row ? " to this line"
: "")
.with_attr_for_all(
VC_ROLE.value(role_t::VCR_COMMENT));
dst.emplace_back(anno_msg);
}
} }
} }

@ -78,15 +78,20 @@ public:
} }
struct context { struct context {
context(std::string prefix, bool show, bool show_discovered) context(std::string prefix,
bool show,
bool show_discovered,
bool show_applicable_annotations)
: c_prefix(std::move(prefix)), c_show(show), : c_prefix(std::move(prefix)), c_show(show),
c_show_discovered(show_discovered) c_show_discovered(show_discovered),
c_show_applicable_annotations(show_applicable_annotations)
{ {
} }
std::string c_prefix; std::string c_prefix;
bool c_show{false}; bool c_show{false};
bool c_show_discovered{true}; bool c_show_discovered{true};
bool c_show_applicable_annotations{true};
}; };
std::stack<context> fos_contexts; std::stack<context> fos_contexts;

@ -39,6 +39,9 @@
}, },
{ {
"line": "2022-06-01T13:23:25.310 [2376]DEBUG:com.vmware.vherd.base.detwist:method = com.vmware.appliance.version1.system.version.get, args = ()" "line": "2022-06-01T13:23:25.310 [2376]DEBUG:com.vmware.vherd.base.detwist:method = com.vmware.appliance.version1.system.version.get, args = ()"
},
{
"line": "2023-07-19T02:47:11 AM UTC [1670]DEBUG:firewall-reload:Processing system service 'sshd' firewall rules."
} }
] ]
} }

@ -665,7 +665,7 @@
:goto #screenshots :goto #screenshots
**See Also** **See Also**
:ref:`next_location`, :ref:`next_mark`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`relative_goto` :ref:`next_location`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_section`, :ref:`relative_goto`
---- ----
@ -873,7 +873,7 @@
Move to the next position in the location history Move to the next position in the location history
**See Also** **See Also**
:ref:`goto`, :ref:`next_mark`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`relative_goto` :ref:`goto`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_section`, :ref:`relative_goto`
---- ----
@ -896,7 +896,20 @@
:next-mark error :next-mark error
**See Also** **See Also**
:ref:`goto`, :ref:`hide_unmarked_lines`, :ref:`mark`, :ref:`next_location`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_mark`, :ref:`relative_goto` :ref:`goto`, :ref:`hide_unmarked_lines`, :ref:`mark`, :ref:`next_location`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_mark`, :ref:`prev_section`, :ref:`relative_goto`
----
.. _next_section:
:next-section
^^^^^^^^^^^^^
Move to the next section in the document
**See Also**
:ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_section`, :ref:`relative_goto`
---- ----
@ -1003,7 +1016,7 @@
Move to the previous position in the location history Move to the previous position in the location history
**See Also** **See Also**
:ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`prev_mark`, :ref:`relative_goto` :ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_mark`, :ref:`prev_section`, :ref:`relative_goto`
---- ----
@ -1026,7 +1039,20 @@
:prev-mark error :prev-mark error
**See Also** **See Also**
:ref:`goto`, :ref:`hide_unmarked_lines`, :ref:`mark`, :ref:`next_location`, :ref:`next_mark`, :ref:`next_mark`, :ref:`prev_location`, :ref:`relative_goto` :ref:`goto`, :ref:`hide_unmarked_lines`, :ref:`mark`, :ref:`next_location`, :ref:`next_mark`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_section`, :ref:`relative_goto`
----
.. _prev_section:
:prev-section
^^^^^^^^^^^^^
Move to the previous section in the document
**See Also**
:ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`relative_goto`
---- ----
@ -1143,7 +1169,7 @@
:relative-goto -10% :relative-goto -10%
**See Also** **See Also**
:ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`prev_location`, :ref:`prev_mark` :ref:`goto`, :ref:`next_location`, :ref:`next_mark`, :ref:`next_section`, :ref:`prev_location`, :ref:`prev_mark`, :ref:`prev_section`
---- ----

@ -11,8 +11,8 @@
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view", "keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}Z${ansi_norm} to zoom in/out", "keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}Z${ansi_norm} to zoom in/out",
"keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages", "keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages",
"keymap_def_prev_location": "Press ${ansi_bold}{${ansi_norm} to move to the previous location in history", "keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in history",
"keymap_def_next_location": "Press ${ansi_bold}}${ansi_norm} to move to the next location in history", "keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in history",
"keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines", "keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines",
"keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs" "keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs"
}, },
@ -153,11 +153,11 @@
"command": ":prev-mark" "command": ":prev-mark"
}, },
"x7d": { "x7d": {
"command": ":next-location", "command": ":next-section",
"alt-msg": "${keymap_def_prev_location}" "alt-msg": "${keymap_def_prev_location}"
}, },
"x7b": { "x7b": {
"command": ":prev-location", "command": ":prev-section",
"alt-msg": "${keymap_def_next_location}" "alt-msg": "${keymap_def_next_location}"
}, },
"x3f": { "x3f": {

@ -719,6 +719,9 @@ listview_curses::shift_selection(shift_amount_t sa)
break; break;
} }
if (this->is_selectable()) { if (this->is_selectable()) {
if (this->lv_selection == -1_vl) {
this->lv_selection = this->lv_top;
}
auto new_selection = this->lv_selection + vis_line_t(offset); auto new_selection = this->lv_selection + vis_line_t(offset);
if (new_selection < 0_vl) { if (new_selection < 0_vl) {

@ -2826,7 +2826,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
.set_word_wrap(false); .set_word_wrap(false);
auto log_fos = new field_overlay_source(lnav_data.ld_log_source, auto log_fos = new field_overlay_source(lnav_data.ld_log_source,
lnav_data.ld_text_source); lnav_data.ld_text_source);
log_fos->fos_contexts.emplace("", false, true); log_fos->fos_contexts.emplace("", false, true, true);
lnav_data.ld_views[LNV_LOG] lnav_data.ld_views[LNV_LOG]
.set_sub_source(&lnav_data.ld_log_source) .set_sub_source(&lnav_data.ld_log_source)
#if 0 #if 0
@ -3357,6 +3357,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
textview_curses *log_tc, *text_tc, *tc; textview_curses *log_tc, *text_tc, *tc;
bool output_view = true; bool output_view = true;
log_fos->fos_contexts.top().c_show_applicable_annotations
= false;
view_colors::init(true); view_colors::init(true);
rescan_files(true); rescan_files(true);
wait_for_pipers(); wait_for_pipers();

@ -1144,6 +1144,62 @@ com_goto_location(exec_context& ec,
return Ok(retval); return Ok(retval);
} }
static Result<std::string, lnav::console::user_message>
com_next_section(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;
if (args.empty()) {
} else if (!ec.ec_dry_run) {
auto* tc = *lnav_data.ld_view_stack.top();
auto* ta = dynamic_cast<text_anchors*>(tc->get_sub_source());
if (ta == nullptr) {
return ec.make_error("view does not support sections");
}
auto adj_opt = ta->adjacent_anchor(tc->get_selection(),
text_anchors::direction::next);
if (!adj_opt) {
return ec.make_error("no next section found");
}
tc->set_selection(adj_opt.value());
}
return Ok(retval);
}
static Result<std::string, lnav::console::user_message>
com_prev_section(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;
if (args.empty()) {
} else if (!ec.ec_dry_run) {
auto* tc = *lnav_data.ld_view_stack.top();
auto* ta = dynamic_cast<text_anchors*>(tc->get_sub_source());
if (ta == nullptr) {
return ec.make_error("view does not support sections");
}
auto adj_opt = ta->adjacent_anchor(tc->get_selection(),
text_anchors::direction::prev);
if (!adj_opt) {
return ec.make_error("no previous section found");
}
tc->set_selection(adj_opt.value());
}
return Ok(retval);
}
static bool static bool
csv_needs_quoting(const std::string& str) csv_needs_quoting(const std::string& str)
{ {
@ -1738,11 +1794,8 @@ com_save_to(exec_context& ec,
size_t count = 0; size_t count = 0;
if (fos != nullptr) { if (fos != nullptr) {
fos->fos_contexts.push(field_overlay_source::context{ fos->fos_contexts.push(
"", field_overlay_source::context{"", false, false, false});
false,
false,
});
} }
auto y = 0_vl; auto y = 0_vl;
@ -5820,6 +5873,24 @@ readline_context::command_t STD_COMMANDS[] = {
help_text(":prev-location") help_text(":prev-location")
.with_summary("Move to the previous position in the location history") .with_summary("Move to the previous position in the location history")
.with_tags({"navigation"})}, .with_tags({"navigation"})},
{
"next-section",
com_next_section,
help_text(":next-section")
.with_summary("Move to the next section in the document")
.with_tags({"navigation"}),
},
{
"prev-section",
com_prev_section,
help_text(":prev-section")
.with_summary("Move to the previous section in the document")
.with_tags({"navigation"}),
},
{"help", {"help",
com_help, com_help,

@ -402,6 +402,7 @@ public:
bro_log_format() bro_log_format()
{ {
this->lf_structured = true;
this->lf_is_self_describing = true; this->lf_is_self_describing = true;
this->lf_time_ordered = false; this->lf_time_ordered = false;
@ -703,7 +704,6 @@ public:
if (!this->blf_format_name.empty() && !this->blf_separator.empty() if (!this->blf_format_name.empty() && !this->blf_separator.empty()
&& !this->blf_field_defs.empty()) && !this->blf_field_defs.empty())
{ {
dst.clear();
return this->scan_int(dst, li, sbr, sbc); return this->scan_int(dst, li, sbr, sbc);
} }
@ -1059,6 +1059,7 @@ public:
{ {
this->lf_is_self_describing = true; this->lf_is_self_describing = true;
this->lf_time_ordered = false; this->lf_time_ordered = false;
this->lf_structured = true;
} }
const intern_string_t get_name() const override const intern_string_t get_name() const override

@ -310,6 +310,7 @@ logfile::process_prefix(shared_buffer_ref& sbr,
this->lf_index.size(), this->lf_index.size(),
li.li_file_range.fr_offset, li.li_file_range.fr_offset,
li.li_file_range.fr_size); li.li_file_range.fr_size);
auto starting_index_size = this->lf_index.size();
size_t prev_index_size = this->lf_index.size(); size_t prev_index_size = this->lf_index.size();
for (const auto& curr : root_formats) { for (const auto& curr : root_formats) {
if (this->lf_index.size() if (this->lf_index.size()
@ -437,13 +438,13 @@ logfile::process_prefix(shared_buffer_ref& sbr,
*/ */
const auto& last_line = this->lf_index.back(); const auto& last_line = this->lf_index.back();
for (size_t lpc = 0; lpc < this->lf_index.size() - 1; lpc++) { require_lt(starting_index_size, this->lf_index.size());
for (size_t lpc = 0; lpc < starting_index_size; lpc++) {
if (this->lf_format->lf_multiline) { if (this->lf_format->lf_multiline) {
this->lf_index[lpc].set_time(last_line.get_time());
this->lf_index[lpc].set_millis(last_line.get_millis());
if (this->lf_format->lf_structured) { if (this->lf_format->lf_structured) {
this->lf_index[lpc].set_ignore(true); this->lf_index[lpc].set_ignore(true);
} else {
this->lf_index[lpc].set_time(last_line.get_time());
this->lf_index[lpc].set_millis(last_line.get_millis());
} }
} else { } else {
this->lf_index[lpc].set_time(last_line.get_time()); this->lf_index[lpc].set_time(last_line.get_time());

@ -311,6 +311,11 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
value_out = this->lss_token_value; value_out = this->lss_token_value;
} }
{
auto lr = line_range{0, (int) this->lss_token_value.length()};
this->lss_token_attrs.emplace_back(lr, SA_ORIGINAL_LINE.value());
}
if (!this->lss_token_line->is_continued() if (!this->lss_token_line->is_continued()
&& (this->lss_token_file->is_time_adjusted() && (this->lss_token_file->is_time_adjusted()
|| ((format->lf_timestamp_flags & ETF_ZONE_SET || ((format->lf_timestamp_flags & ETF_ZONE_SET
@ -425,6 +430,7 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
auto relstr = this->get_time_offset_for_line(tc, row_vl); auto relstr = this->get_time_offset_for_line(tc, row_vl);
value_out = fmt::format(FMT_STRING("{: >12}|{}"), relstr, value_out); value_out = fmt::format(FMT_STRING("{: >12}|{}"), relstr, value_out);
} }
this->lss_in_value_for_line = false; this->lss_in_value_for_line = false;
} }
@ -459,8 +465,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
const auto& line_values = this->lss_token_values; const auto& line_values = this->lss_token_values;
lr.lr_start = 0; lr.lr_start = 0;
lr.lr_end = this->lss_token_value.length(); lr.lr_end = -1;
value_out.emplace_back(lr, SA_ORIGINAL_LINE.value());
value_out.emplace_back( value_out.emplace_back(
lr, SA_LEVEL.value(this->lss_token_line->get_msg_level())); lr, SA_LEVEL.value(this->lss_token_line->get_msg_level()));

@ -31,6 +31,7 @@
#include "base/itertools.hh" #include "base/itertools.hh"
#include "config.h" #include "config.h"
#include "scn/scn.h"
static std::vector<plain_text_source::text_line> static std::vector<plain_text_source::text_line>
to_text_line(const std::vector<attr_line_t>& lines) to_text_line(const std::vector<attr_line_t>& lines)
@ -79,7 +80,30 @@ plain_text_source::replace_with(const attr_line_t& text_lines)
{ {
this->tds_lines.clear(); this->tds_lines.clear();
this->tds_doc_sections = lnav::document::discover_metadata(text_lines); this->tds_doc_sections = lnav::document::discover_metadata(text_lines);
file_off_t off = 0;
auto lines = text_lines.split_lines();
while (!lines.empty() && lines.back().empty()) {
lines.pop_back();
}
for (auto& line : lines) {
auto line_len = line.length() + 1;
this->tds_lines.emplace_back(off, std::move(line));
off += line_len;
}
this->tds_longest_line = this->compute_longest_line();
if (this->tss_view != nullptr) {
this->tss_view->set_needs_update();
}
return *this;
}
plain_text_source&
plain_text_source::replace_with_mutable(attr_line_t& text_lines,
text_format_t tf)
{
this->tds_lines.clear();
this->tds_doc_sections
= lnav::document::discover_structure(text_lines, line_range{0, -1}, tf);
file_off_t off = 0; file_off_t off = 0;
auto lines = text_lines.split_lines(); auto lines = text_lines.split_lines();
while (!lines.empty() && lines.back().empty()) { while (!lines.empty() && lines.back().empty()) {
@ -368,16 +392,47 @@ plain_text_source::row_for_anchor(const std::string& id)
return retval; return retval;
} }
const auto& meta = this->tds_doc_sections;
auto is_ptr = startswith(id, "#/");
if (is_ptr) {
auto hier_sf = string_fragment::from_str(id).consume_n(2).value();
std::vector<lnav::document::section_key_t> path;
while (!hier_sf.empty()) {
auto comp_pair = hier_sf.split_when(string_fragment::tag1{'/'});
auto scan_res
= scn::scan_value<int64_t>(comp_pair.first.to_string_view());
if (scan_res && scan_res.empty()) {
path.emplace_back(scan_res.value());
} else {
path.emplace_back(json_ptr::decode(comp_pair.first));
}
hier_sf = comp_pair.second;
}
auto lookup_res = lnav::document::hier_node::lookup_path(
meta.m_sections_root.get(), path);
if (lookup_res) {
retval = this->line_for_offset(lookup_res.value()->hn_start);
}
return retval;
}
lnav::document::hier_node::depth_first( lnav::document::hier_node::depth_first(
this->tds_doc_sections.m_sections_root.get(), meta.m_sections_root.get(),
[this, &id, &retval](const lnav::document::hier_node* node) { [this, &id, &retval](const lnav::document::hier_node* node) {
for (const auto& child_pair : node->hn_named_children) { for (const auto& child_pair : node->hn_named_children) {
auto child_anchor const auto& child_anchor
= text_anchors::to_anchor_string(child_pair.first); = text_anchors::to_anchor_string(child_pair.first);
if (child_anchor == id) { if (child_anchor != id) {
retval = this->line_for_offset(child_pair.second->hn_start); continue;
} }
retval = this->line_for_offset(child_pair.second->hn_start);
break;
} }
}); });
@ -413,16 +468,127 @@ plain_text_source::anchor_for_row(vis_line_t vl)
} }
const auto& tl = this->tds_lines[vl]; const auto& tl = this->tds_lines[vl];
auto& md = this->tds_doc_sections;
auto path_for_line = md.path_for_range(
tl.tl_offset, tl.tl_offset + tl.tl_value.al_string.length());
this->tds_doc_sections.m_sections_tree.visit_overlapping( if (path_for_line.empty()) {
tl.tl_offset, [&retval](const lnav::document::section_interval_t& iv) { return nonstd::nullopt;
retval = iv.value.match( }
[](const std::string& str) {
return nonstd::make_optional(
text_anchors::to_anchor_string(str));
},
[](size_t) { return nonstd::nullopt; });
});
return retval; if ((path_for_line.size() == 1
|| this->tds_text_format == text_format_t::TF_MARKDOWN)
&& path_for_line.back().is<std::string>())
{
return text_anchors::to_anchor_string(
path_for_line.back().get<std::string>());
}
auto comps = path_for_line | lnav::itertools::map([](const auto& elem) {
return elem.match(
[](const std::string& str) {
return json_ptr::encode_str(str);
},
[](size_t index) { return fmt::to_string(index); });
});
return fmt::format(FMT_STRING("#/{}"),
fmt::join(comps.begin(), comps.end(), "/"));
}
nonstd::optional<vis_line_t>
plain_text_source::adjacent_anchor(vis_line_t vl, text_anchors::direction dir)
{
if (vl > this->tds_lines.size()
|| this->tds_doc_sections.m_sections_root == nullptr)
{
return nonstd::nullopt;
}
const auto& tl = this->tds_lines[vl];
auto path_for_line = this->tds_doc_sections.path_for_range(
tl.tl_offset, tl.tl_offset + tl.tl_value.al_string.length());
auto& md = this->tds_doc_sections;
if (path_for_line.empty()) {
auto neighbors_res = md.m_sections_root->line_neighbors(vl);
if (!neighbors_res) {
return nonstd::nullopt;
}
switch (dir) {
case text_anchors::direction::prev: {
if (neighbors_res->cnr_previous) {
return this->line_for_offset(
neighbors_res->cnr_previous.value()->hn_start);
}
break;
}
case text_anchors::direction::next: {
if (neighbors_res->cnr_next) {
return this->line_for_offset(
neighbors_res->cnr_next.value()->hn_start);
}
break;
}
}
return nonstd::nullopt;
}
auto last_key = path_for_line.back();
path_for_line.pop_back();
auto parent_opt = lnav::document::hier_node::lookup_path(
md.m_sections_root.get(), path_for_line);
if (!parent_opt) {
return nonstd::nullopt;
}
auto parent = parent_opt.value();
auto child_hn = parent->lookup_child(last_key);
if (!child_hn) {
// XXX "should not happen"
return nonstd::nullopt;
}
auto neighbors_res = parent->child_neighbors(
child_hn.value(), tl.tl_offset + tl.tl_value.al_string.length() + 1);
if (!neighbors_res) {
return nonstd::nullopt;
}
if (neighbors_res->cnr_previous && last_key.is<std::string>()) {
auto neighbor_sub
= neighbors_res->cnr_previous.value()->lookup_child(last_key);
if (neighbor_sub) {
neighbors_res->cnr_previous = neighbor_sub;
}
}
if (neighbors_res->cnr_next && last_key.is<std::string>()) {
auto neighbor_sub
= neighbors_res->cnr_next.value()->lookup_child(last_key);
if (neighbor_sub) {
neighbors_res->cnr_next = neighbor_sub;
}
}
switch (dir) {
case text_anchors::direction::prev: {
if (neighbors_res->cnr_previous) {
return this->line_for_offset(
neighbors_res->cnr_previous.value()->hn_start);
}
break;
}
case text_anchors::direction::next: {
if (neighbors_res->cnr_next) {
return this->line_for_offset(
neighbors_res->cnr_next.value()->hn_start);
}
break;
}
}
return nonstd::nullopt;
} }

@ -73,6 +73,9 @@ public:
return *this; return *this;
} }
plain_text_source& replace_with_mutable(attr_line_t& text_lines,
text_format_t tf);
plain_text_source& replace_with(const attr_line_t& text_lines); plain_text_source& replace_with(const attr_line_t& text_lines);
plain_text_source& replace_with(const std::vector<std::string>& text_lines); plain_text_source& replace_with(const std::vector<std::string>& text_lines);
@ -123,6 +126,8 @@ public:
nonstd::optional<vis_line_t> row_for_anchor(const std::string& id) override; nonstd::optional<vis_line_t> row_for_anchor(const std::string& id) override;
nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override; nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override;
std::unordered_set<std::string> get_anchors() override; std::unordered_set<std::string> get_anchors() override;
nonstd::optional<vis_line_t> adjacent_anchor(vis_line_t vl,
direction dir) override;
protected: protected:
size_t compute_longest_line(); size_t compute_longest_line();

@ -77,14 +77,14 @@ pretty_printer::append_to(attr_line_t& al)
= this->pp_stream.tellp(); = this->pp_stream.tellp();
this->pp_interval_state.back().is_name this->pp_interval_state.back().is_name
= tok_res->to_string(); = tok_res->to_string();
this->descend(); this->descend(DT_XML_CLOSE_TAG);
} else { } else {
this->pp_values.emplace_back(el); this->pp_values.emplace_back(el);
} }
continue; continue;
case DT_XML_CLOSE_TAG: case DT_XML_CLOSE_TAG:
this->flush_values(); this->flush_values();
this->ascend(); this->ascend(el.e_token);
this->append_child_node(); this->append_child_node();
this->write_element(el); this->write_element(el);
this->start_new_line(); this->start_new_line();
@ -94,7 +94,7 @@ pretty_printer::append_to(attr_line_t& al)
case DT_LPAREN: case DT_LPAREN:
this->flush_values(true); this->flush_values(true);
this->pp_values.emplace_back(el); this->pp_values.emplace_back(el);
this->descend(); this->descend(to_closer(el.e_token));
this->pp_interval_state.back().is_start this->pp_interval_state.back().is_start
= this->pp_stream.tellp(); = this->pp_stream.tellp();
continue; continue;
@ -105,7 +105,7 @@ pretty_printer::append_to(attr_line_t& al)
if (this->pp_body_lines.top()) { if (this->pp_body_lines.top()) {
this->start_new_line(); this->start_new_line();
} }
this->ascend(); this->ascend(el.e_token);
this->write_element(el); this->write_element(el);
continue; continue;
case DT_COMMA: case DT_COMMA:
@ -135,7 +135,7 @@ pretty_printer::append_to(attr_line_t& al)
this->pp_values.emplace_back(el); this->pp_values.emplace_back(el);
} }
while (this->pp_depth > 0) { while (this->pp_depth > 0) {
this->ascend(); this->ascend(this->pp_container_tokens.back());
} }
this->flush_values(); this->flush_values();
@ -341,30 +341,47 @@ pretty_printer::start_new_line()
} }
void void
pretty_printer::ascend() pretty_printer::ascend(data_token_t dt)
{ {
if (this->pp_depth > 0) { if (this->pp_depth > 0) {
int lines = this->pp_body_lines.top(); if (this->pp_container_tokens.back() != dt
this->pp_depth -= 1; && std::find(this->pp_container_tokens.begin(),
this->pp_body_lines.pop(); this->pp_container_tokens.end(),
this->pp_body_lines.top() += lines; dt)
== this->pp_container_tokens.end())
if (!this->pp_is_xml) { {
this->append_child_node(); return;
} }
this->pp_interval_state.pop_back();
this->pp_hier_stage = std::move(this->pp_hier_nodes.back()); auto found = false;
this->pp_hier_nodes.pop_back(); do {
if (this->pp_container_tokens.back() == dt) {
found = true;
}
int lines = this->pp_body_lines.top();
this->pp_depth -= 1;
this->pp_body_lines.pop();
this->pp_body_lines.top() += lines;
if (!this->pp_is_xml) {
this->append_child_node();
}
this->pp_interval_state.pop_back();
this->pp_hier_stage = std::move(this->pp_hier_nodes.back());
this->pp_hier_nodes.pop_back();
this->pp_container_tokens.pop_back();
} while (!found);
} else { } else {
this->pp_body_lines.top() = 0; this->pp_body_lines.top() = 0;
} }
} }
void void
pretty_printer::descend() pretty_printer::descend(data_token_t dt)
{ {
this->pp_depth += 1; this->pp_depth += 1;
this->pp_body_lines.push(0); this->pp_body_lines.push(0);
this->pp_container_tokens.push_back(dt);
this->pp_interval_state.resize(this->pp_depth + 1); this->pp_interval_state.resize(this->pp_depth + 1);
this->pp_hier_nodes.push_back( this->pp_hier_nodes.push_back(
std::make_unique<lnav::document::hier_node>()); std::make_unique<lnav::document::hier_node>());

@ -97,9 +97,9 @@ public:
std::set<size_t> take_indents() { return std::move(this->pp_indents); } std::set<size_t> take_indents() { return std::move(this->pp_indents); }
private: private:
void descend(); void descend(data_token_t dt);
void ascend(); void ascend(data_token_t dt);
void start_new_line(); void start_new_line();
@ -120,6 +120,7 @@ private:
int pp_depth{0}; int pp_depth{0};
int pp_line_length{0}; int pp_line_length{0};
int pp_soft_indent{0}; int pp_soft_indent{0};
std::vector<data_token_t> pp_container_tokens{};
std::stack<int> pp_body_lines{}; std::stack<int> pp_body_lines{};
data_scanner* pp_scanner; data_scanner* pp_scanner;
string_attrs_t pp_attrs; string_attrs_t pp_attrs;

@ -904,7 +904,7 @@ rl_focus(readline_curses* rc)
auto fos = (field_overlay_source*) lnav_data.ld_views[LNV_LOG] auto fos = (field_overlay_source*) lnav_data.ld_views[LNV_LOG]
.get_overlay_source(); .get_overlay_source();
fos->fos_contexts.emplace("", false, true); fos->fos_contexts.emplace("", false, true, true);
get_textview_for_mode(lnav_data.ld_mode)->save_current_search(); get_textview_for_mode(lnav_data.ld_mode)->save_current_search();
} }

@ -560,26 +560,26 @@ textfile_sub_source::text_crumbs_for_line(
rend_iter->second.rf_text_source->text_crumbs_for_line(line, crumbs); rend_iter->second.rf_text_source->text_crumbs_for_line(line, crumbs);
} }
if (lf->has_line_metadata()) {
auto* lfo
= dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
if (line < 0 || line >= lfo->lfo_filter_state.tfs_index.size()) {
return;
}
auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[line];
char ts[64];
sql_strftime(ts, sizeof(ts), ll_iter->get_timeval(), 'T');
crumbs.emplace_back(
std::string(ts),
[]() -> std::vector<breadcrumb::possibility> { return {}; },
[](const auto& key) {});
}
auto meta_iter = this->tss_doc_metadata.find(lf->get_filename()); auto meta_iter = this->tss_doc_metadata.find(lf->get_filename());
if (meta_iter == this->tss_doc_metadata.end() if (meta_iter == this->tss_doc_metadata.end()
|| meta_iter->second.ms_metadata.m_sections_tree.empty()) || meta_iter->second.ms_metadata.m_sections_tree.empty())
{ {
if (lf->has_line_metadata()) {
auto* lfo = dynamic_cast<line_filter_observer*>(
lf->get_logline_observer());
if (line < 0 || line >= lfo->lfo_filter_state.tfs_index.size()) {
return;
}
auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[line];
char ts[64];
sql_strftime(ts, sizeof(ts), ll_iter->get_timeval(), 'T');
crumbs.emplace_back(
std::string(ts),
[]() -> std::vector<breadcrumb::possibility> { return {}; },
[](const auto& key) {});
}
} else { } else {
auto* lfo auto* lfo
= dynamic_cast<line_filter_observer*>(lf->get_logline_observer()); = dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
@ -849,6 +849,7 @@ textfile_sub_source::rescan_files(
rf.rf_mtime = st.st_mtime; rf.rf_mtime = st.st_mtime;
rf.rf_file_size = st.st_size; rf.rf_file_size = st.st_size;
rf.rf_text_source = std::make_unique<plain_text_source>(); rf.rf_text_source = std::make_unique<plain_text_source>();
rf.rf_text_source->set_text_format(lf->get_text_format());
rf.rf_text_source->register_view(this->tss_view); rf.rf_text_source->register_view(this->tss_view);
if (parse_res.isOk()) { if (parse_res.isOk()) {
auto& lf_meta = lf->get_embedded_metadata(); auto& lf_meta = lf->get_embedded_metadata();
@ -931,7 +932,7 @@ textfile_sub_source::rescan_files(
this->tss_doc_metadata[lf->get_filename()] this->tss_doc_metadata[lf->get_filename()]
= metadata_state{ = metadata_state{
st.st_mtime, st.st_mtime,
static_cast<file_ssize_t>(content.length()), static_cast<file_ssize_t>(lf->get_index_size()),
lnav::document::discover_structure( lnav::document::discover_structure(
content, content,
line_range{0, -1}, line_range{0, -1},
@ -945,7 +946,7 @@ textfile_sub_source::rescan_files(
this->tss_doc_metadata[lf->get_filename()] this->tss_doc_metadata[lf->get_filename()]
= metadata_state{ = metadata_state{
st.st_mtime, st.st_mtime,
static_cast<file_ssize_t>(st.st_size), static_cast<file_ssize_t>(lf->get_index_size()),
{}, {},
}; };
} }
@ -1159,6 +1160,142 @@ textfile_sub_source::get_anchors()
return retval; return retval;
} }
static nonstd::optional<vis_line_t>
to_vis_line(const std::shared_ptr<logfile>& lf, file_off_t off)
{
auto ll_opt = lf->line_for_offset(off);
if (ll_opt != lf->end()) {
return vis_line_t(std::distance(lf->cbegin(), ll_opt.value()));
}
return nonstd::nullopt;
}
nonstd::optional<vis_line_t>
textfile_sub_source::adjacent_anchor(vis_line_t vl, text_anchors::direction dir)
{
auto lf = this->current_file();
if (!lf) {
return nonstd::nullopt;
}
log_debug("adjacent_anchor: %s:L%d:%s",
lf->get_filename().c_str(),
vl,
dir == text_anchors::direction::prev ? "prev" : "next");
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
if (rend_iter != this->tss_rendered_files.end()) {
return rend_iter->second.rf_text_source->adjacent_anchor(vl, dir);
}
auto iter = this->tss_doc_metadata.find(lf->get_filename());
if (iter == this->tss_doc_metadata.end()) {
log_debug(" no metadata available");
return nonstd::nullopt;
}
auto& md = iter->second.ms_metadata;
auto* lfo = dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
if (vl >= lfo->lfo_filter_state.tfs_index.size()
|| md.m_sections_root == nullptr)
{
return nonstd::nullopt;
}
auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[vl];
auto line_offsets = lf->get_file_range(ll_iter, false);
log_debug(
" range %d:%d", line_offsets.fr_offset, line_offsets.next_offset());
auto path_for_line
= md.path_for_range(line_offsets.fr_offset, line_offsets.next_offset());
if (path_for_line.empty()) {
log_debug(" no path found");
auto neighbors_res = md.m_sections_root->line_neighbors(vl);
if (!neighbors_res) {
return nonstd::nullopt;
}
switch (dir) {
case text_anchors::direction::prev: {
if (neighbors_res->cnr_previous) {
return to_vis_line(
lf, neighbors_res->cnr_previous.value()->hn_start);
}
break;
}
case text_anchors::direction::next: {
if (neighbors_res->cnr_next) {
return to_vis_line(
lf, neighbors_res->cnr_next.value()->hn_start);
}
break;
}
}
return nonstd::nullopt;
}
auto last_key = path_for_line.back();
path_for_line.pop_back();
auto parent_opt = lnav::document::hier_node::lookup_path(
md.m_sections_root.get(), path_for_line);
if (!parent_opt) {
return nonstd::nullopt;
}
auto parent = parent_opt.value();
auto child_hn = parent->lookup_child(last_key);
if (!child_hn) {
// XXX "should not happen"
return nonstd::nullopt;
}
auto neighbors_res = parent->child_neighbors(
child_hn.value(), line_offsets.next_offset() + 1);
if (!neighbors_res) {
log_debug(" no neighbors found");
return nonstd::nullopt;
}
log_debug(" neighbors p:%d n:%d",
neighbors_res->cnr_previous.has_value(),
neighbors_res->cnr_next.has_value());
if (neighbors_res->cnr_previous && last_key.is<std::string>()) {
auto neighbor_sub
= neighbors_res->cnr_previous.value()->lookup_child(last_key);
if (neighbor_sub) {
neighbors_res->cnr_previous = neighbor_sub;
}
}
if (neighbors_res->cnr_next && last_key.is<std::string>()) {
auto neighbor_sub
= neighbors_res->cnr_next.value()->lookup_child(last_key);
if (neighbor_sub) {
neighbors_res->cnr_next = neighbor_sub;
}
}
switch (dir) {
case text_anchors::direction::prev: {
if (neighbors_res->cnr_previous) {
return to_vis_line(
lf, neighbors_res->cnr_previous.value()->hn_start);
}
break;
}
case text_anchors::direction::next: {
if (neighbors_res->cnr_next) {
return to_vis_line(lf,
neighbors_res->cnr_next.value()->hn_start);
}
break;
}
}
return nonstd::nullopt;
}
nonstd::optional<std::string> nonstd::optional<std::string>
textfile_sub_source::anchor_for_row(vis_line_t vl) textfile_sub_source::anchor_for_row(vis_line_t vl)
{ {
@ -1181,36 +1318,34 @@ textfile_sub_source::anchor_for_row(vis_line_t vl)
if (vl >= lfo->lfo_filter_state.tfs_index.size()) { if (vl >= lfo->lfo_filter_state.tfs_index.size()) {
return nonstd::nullopt; return nonstd::nullopt;
} }
auto& md = iter->second.ms_metadata;
auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[vl]; auto ll_iter = lf->begin() + lfo->lfo_filter_state.tfs_index[vl];
auto ll_next_iter = ll_iter + 1; auto line_offsets = lf->get_file_range(ll_iter, false);
auto end_offset = (ll_next_iter == lf->end()) auto path_for_line
? lf->get_index_size() - 1 = md.path_for_range(line_offsets.fr_offset, line_offsets.next_offset());
: ll_next_iter->get_offset() - 1;
std::vector<std::string> collector;
iter->second.ms_metadata.m_sections_tree.visit_overlapping(
ll_iter->get_offset(),
end_offset,
[&collector](const lnav::document::section_interval_t& iv) {
collector.emplace_back(iv.value.match(
[](const std::string& str) { return str; },
[](size_t index) { return fmt::to_string(index); }));
});
if (collector.empty()) { if (path_for_line.empty()) {
return nonstd::nullopt; return nonstd::nullopt;
} }
if (collector.size() == 1) { if ((path_for_line.size() == 1
return text_anchors::to_anchor_string(collector.front()); || md.m_text_format == text_format_t::TF_MARKDOWN)
&& path_for_line.back().is<std::string>())
{
return text_anchors::to_anchor_string(
path_for_line.back().get<std::string>());
} }
for (auto& elem : collector) { auto comps = path_for_line | lnav::itertools::map([](const auto& elem) {
elem = json_ptr::encode_str(elem); return elem.match(
} [](const std::string& str) {
return json_ptr::encode_str(str);
},
[](size_t index) { return fmt::to_string(index); });
});
return fmt::format(FMT_STRING("#/{}"), return fmt::format(FMT_STRING("#/{}"),
fmt::join(collector.begin(), collector.end(), "/")); fmt::join(comps.begin(), comps.end(), "/"));
} }
bool bool

@ -147,6 +147,9 @@ public:
nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override; nonstd::optional<std::string> anchor_for_row(vis_line_t vl) override;
nonstd::optional<vis_line_t> adjacent_anchor(vis_line_t vl,
direction dir) override;
std::unordered_set<std::string> get_anchors() override; std::unordered_set<std::string> get_anchors() override;
void quiesce() override; void quiesce() override;

@ -314,6 +314,17 @@ public:
virtual nonstd::optional<vis_line_t> row_for_anchor(const std::string& id) virtual nonstd::optional<vis_line_t> row_for_anchor(const std::string& id)
= 0; = 0;
enum class direction {
prev,
next,
};
virtual nonstd::optional<vis_line_t> adjacent_anchor(vis_line_t vl,
direction dir)
{
return nonstd::nullopt;
}
virtual nonstd::optional<std::string> anchor_for_row(vis_line_t vl) = 0; virtual nonstd::optional<std::string> anchor_for_row(vis_line_t vl) = 0;
virtual std::unordered_set<std::string> get_anchors() = 0; virtual std::unordered_set<std::string> get_anchors() = 0;

@ -234,7 +234,6 @@ sql_timezone(std::string tz_str, string_fragment ts_str)
} }
alb.append(" unrecognized input"); alb.append(" unrecognized input");
} }
log_debug("wtf %s", ts_attr.get_string().c_str());
um.with_note(ts_attr); um.with_note(ts_attr);
throw um; throw um;
} }

@ -17,6 +17,7 @@ TIME_FORMATS = \
"%Y-%m-%d %H:%M:%S:%L" \ "%Y-%m-%d %H:%M:%S:%L" \
"%Y-%m-%d %H:%M:%S" \ "%Y-%m-%d %H:%M:%S" \
"%Y-%m-%d %H:%M" \ "%Y-%m-%d %H:%M" \
"%Y-%m-%dT%H:%M:%S %p %Z" \
"%Y-%m-%dT%H:%M:%S.%N%z" \ "%Y-%m-%dT%H:%M:%S.%N%z" \
"%y-%m-%dT%H:%M:%S.%N%z" \ "%y-%m-%dT%H:%M:%S.%N%z" \
"%Y-%m-%dT%H:%M:%S.%f%z" \ "%Y-%m-%dT%H:%M:%S.%f%z" \

@ -86,7 +86,7 @@ looper::handler_looper::loop_body()
if (exec_res.isErr()) { if (exec_res.isErr()) {
auto um = exec_res.unwrapErr(); auto um = exec_res.unwrapErr();
log_error( log_error(
"wtf %s", "%s",
um.to_attr_line().get_string().c_str()); um.to_attr_line().get_string().c_str());
} }
}); });

@ -192,6 +192,12 @@ view_curses::mvwattrline(WINDOW* window,
char_index += 1; char_index += 1;
break; break;
case '\b':
expanded_line.append("\u232b");
utf_adjustments.emplace_back(lpc, -1);
char_index += 1;
break;
case '\r': case '\r':
case '\n': case '\n':
expanded_line.push_back(' '); expanded_line.push_back(' ');

@ -144,6 +144,11 @@ public:
this->tds_doc_sections.m_indents = std::move(indents); this->tds_doc_sections.m_indents = std::move(indents);
} }
void set_sections_root(std::unique_ptr<lnav::document::hier_node>&& hn)
{
this->tds_doc_sections.m_sections_root = std::move(hn);
}
void text_crumbs_for_line(int line, void text_crumbs_for_line(int line,
std::vector<breadcrumb::crumb>& crumbs) override std::vector<breadcrumb::crumb>& crumbs) override
{ {
@ -299,8 +304,8 @@ public:
= interval_tree::Interval<file_off_t, lnav::document::hier_node*>; = interval_tree::Interval<file_off_t, lnav::document::hier_node*>;
std::shared_ptr<lnav::document::sections_tree_t> pss_interval_tree; std::shared_ptr<lnav::document::sections_tree_t> pss_interval_tree;
std::vector<std::unique_ptr<lnav::document::hier_node>> pss_hier_nods;
std::shared_ptr<hier_tree_t> pss_hier_tree; std::shared_ptr<hier_tree_t> pss_hier_tree;
std::unique_ptr<lnav::document::hier_node> pss_root_node;
}; };
static void static void
@ -324,7 +329,7 @@ open_pretty_view()
return; return;
} }
std::vector<attr_line_t> full_text; attr_line_t full_text;
delete pretty_tc->get_sub_source(); delete pretty_tc->get_sub_source();
pretty_tc->set_sub_source(nullptr); pretty_tc->set_sub_source(nullptr);
@ -361,6 +366,11 @@ open_pretty_view()
al.get_string(), al.get_string(),
text_sub_source::RF_FULL | text_sub_source::RF_REWRITE); text_sub_source::RF_FULL | text_sub_source::RF_REWRITE);
lss.text_attrs_for_line(*log_tc, vl, al.get_attrs()); lss.text_attrs_for_line(*log_tc, vl, al.get_attrs());
{
const auto orig_lr
= find_string_attr_range(al.get_attrs(), &SA_ORIGINAL_LINE);
require(orig_lr.is_valid());
}
scrub_ansi_string(al.get_string(), &al.get_attrs()); scrub_ansi_string(al.get_string(), &al.get_attrs());
if (log_tc->get_hide_fields()) { if (log_tc->get_hide_fields()) {
al.apply_hide(); al.apply_hide();
@ -368,6 +378,7 @@ open_pretty_view()
const auto orig_lr const auto orig_lr
= find_string_attr_range(al.get_attrs(), &SA_ORIGINAL_LINE); = find_string_attr_range(al.get_attrs(), &SA_ORIGINAL_LINE);
require(orig_lr.is_valid());
const auto body_lr const auto body_lr
= find_string_attr_range(al.get_attrs(), &SA_BODY); = find_string_attr_range(al.get_attrs(), &SA_BODY);
auto orig_al = al.subline(orig_lr.lr_start, orig_lr.length()); auto orig_al = al.subline(orig_lr.lr_start, orig_lr.length());
@ -419,7 +430,8 @@ open_pretty_view()
} }
}); });
line_off += pretty_line.get_string().length(); line_off += pretty_line.get_string().length();
full_text.emplace_back(pretty_line); full_text.append(pretty_line);
full_text.append("\n");
} }
first_line = false; first_line = false;
@ -467,29 +479,28 @@ open_pretty_view()
data_scanner ds(orig_al.get_string()); data_scanner ds(orig_al.get_string());
pretty_printer pp(&ds, orig_al.get_attrs()); pretty_printer pp(&ds, orig_al.get_attrs());
attr_line_t pretty_al;
pp.append_to(pretty_al); pp.append_to(full_text);
pretty_al.rtrim();
all_intervals = pp.take_intervals(); all_intervals = pp.take_intervals();
hier_nodes.emplace_back(pp.take_hier_root()); hier_nodes.emplace_back(pp.take_hier_root());
hier_tree_vec.emplace_back( hier_tree_vec.emplace_back(
0, pretty_al.length(), hier_nodes.back().get()); 0, full_text.length(), hier_nodes.back().get());
pretty_indents = pp.take_indents(); pretty_indents = pp.take_indents();
pretty_al.split_lines(full_text);
} }
} }
auto* pts = new pretty_sub_source(); auto* pts = new pretty_sub_source();
pts->pss_interval_tree = std::make_shared<lnav::document::sections_tree_t>( pts->pss_interval_tree = std::make_shared<lnav::document::sections_tree_t>(
std::move(all_intervals)); std::move(all_intervals));
pts->pss_hier_nods = std::move(hier_nodes); auto root_node = std::make_unique<lnav::document::hier_node>();
root_node->hn_children = std::move(hier_nodes);
pts->pss_hier_tree = std::make_shared<pretty_sub_source::hier_tree_t>( pts->pss_hier_tree = std::make_shared<pretty_sub_source::hier_tree_t>(
std::move(hier_tree_vec)); std::move(hier_tree_vec));
pts->pss_root_node = std::move(root_node);
pts->set_indents(std::move(pretty_indents)); pts->set_indents(std::move(pretty_indents));
pts->replace_with(full_text); pts->replace_with_mutable(full_text,
top_tc->get_sub_source()->get_text_format());
pretty_tc->set_sub_source(pts); pretty_tc->set_sub_source(pts);
if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) { if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) {
pretty_tc->set_top(0_vl); pretty_tc->set_top(0_vl);
@ -498,12 +509,6 @@ open_pretty_view()
pretty_tc->redo_search(); pretty_tc->redo_search();
} }
template<typename T>
static void
ignore_case(const T&)
{
}
static void static void
build_all_help_text() build_all_help_text()
{ {

@ -253,6 +253,7 @@ dist_noinst_DATA = \
bad-config2/formats/invalid-config/config.truncated.json \ bad-config2/formats/invalid-config/config.truncated.json \
bad-config-json/formats/invalid-json/format.json \ bad-config-json/formats/invalid-json/format.json \
bad-config-json/formats/invalid-key/format.json \ bad-config-json/formats/invalid-key/format.json \
books.json \
books.xml \ books.xml \
file_for_dot_read.sql \ file_for_dot_read.sql \
datafile_simple.0 \ datafile_simple.0 \

@ -0,0 +1,14 @@
{
"catalog": [
{
"author": "Gambardella, Matthew",
"title": "XML Developer's Guide",
"description": "An in-depth look at creating applications with XML."
},
{
"author": "Ralls, Kim",
"title": "Midnight Rain",
"description": "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
}
]
}

@ -1198,10 +1198,14 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_text_file.sh_0bba304f34ae07c4fa9e91e0b42f5fe98654a6a8.out \ $(srcdir)/%reldir%/test_text_file.sh_0bba304f34ae07c4fa9e91e0b42f5fe98654a6a8.out \
$(srcdir)/%reldir%/test_text_file.sh_11fd274911e45a743b4de616888a64183d07cb76.err \ $(srcdir)/%reldir%/test_text_file.sh_11fd274911e45a743b4de616888a64183d07cb76.err \
$(srcdir)/%reldir%/test_text_file.sh_11fd274911e45a743b4de616888a64183d07cb76.out \ $(srcdir)/%reldir%/test_text_file.sh_11fd274911e45a743b4de616888a64183d07cb76.out \
$(srcdir)/%reldir%/test_text_file.sh_143a40164c93c7ec44a66e7940b92b128a421147.err \
$(srcdir)/%reldir%/test_text_file.sh_143a40164c93c7ec44a66e7940b92b128a421147.out \
$(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.err \ $(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.err \
$(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.out \ $(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.out \
$(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.err \ $(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.err \
$(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.out \ $(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.out \
$(srcdir)/%reldir%/test_text_file.sh_4dd174410d702a7b4be794fb6fa2c8889bd768d6.err \
$(srcdir)/%reldir%/test_text_file.sh_4dd174410d702a7b4be794fb6fa2c8889bd768d6.out \
$(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.err \ $(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.err \
$(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.out \ $(srcdir)/%reldir%/test_text_file.sh_5b51b55dff7332c5bee2c9b797c401c5614d574a.out \
$(srcdir)/%reldir%/test_text_file.sh_5e9320f18d066e6fc930dbbffc357af64312bd4b.err \ $(srcdir)/%reldir%/test_text_file.sh_5e9320f18d066e6fc930dbbffc357af64312bd4b.err \

@ -4839,7 +4839,7 @@
"alt-msg": "" "alt-msg": ""
}, },
"x7b": { "x7b": {
"command": ":prev-location", "command": ":prev-section",
"alt-msg": "${keymap_def_next_location}" "alt-msg": "${keymap_def_next_location}"
}, },
"x7c": { "x7c": {
@ -4847,7 +4847,7 @@
"alt-msg": "" "alt-msg": ""
}, },
"x7d": { "x7d": {
"command": ":next-location", "command": ":next-section",
"alt-msg": "${keymap_def_prev_location}" "alt-msg": "${keymap_def_prev_location}"
} }
}, },
@ -5031,11 +5031,11 @@
"keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages", "keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages",
"keymap_def_db_view": "Press ${ansi_bold}v${ansi_norm}/${ansi_bold}V${ansi_norm} to switch to the SQL result view", "keymap_def_db_view": "Press ${ansi_bold}v${ansi_norm}/${ansi_bold}V${ansi_norm} to switch to the SQL result view",
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view", "keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
"keymap_def_next_location": "Press ${ansi_bold}}${ansi_norm} to move to the next location in history",
"keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines", "keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines",
"keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in history",
"keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks", "keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks",
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view", "keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_prev_location": "Press ${ansi_bold}{${ansi_norm} to move to the previous location in history", "keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in history",
"keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result", "keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result",
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view", "keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
"keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs", "keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs",

@ -3,11 +3,11 @@
/global/keymap_def_clear -> default-keymap.json:13 /global/keymap_def_clear -> default-keymap.json:13
/global/keymap_def_db_view -> default-keymap.json:8 /global/keymap_def_db_view -> default-keymap.json:8
/global/keymap_def_hist_view -> default-keymap.json:9 /global/keymap_def_hist_view -> default-keymap.json:9
/global/keymap_def_next_location -> default-keymap.json:15
/global/keymap_def_next_mark -> default-keymap.json:16 /global/keymap_def_next_mark -> default-keymap.json:16
/global/keymap_def_next_section -> default-keymap.json:15
/global/keymap_def_next_user_mark -> default-keymap.json:7 /global/keymap_def_next_user_mark -> default-keymap.json:7
/global/keymap_def_pop_view -> default-keymap.json:11 /global/keymap_def_pop_view -> default-keymap.json:11
/global/keymap_def_prev_location -> default-keymap.json:14 /global/keymap_def_prev_section -> default-keymap.json:14
/global/keymap_def_scroll_horiz -> default-keymap.json:6 /global/keymap_def_scroll_horiz -> default-keymap.json:6
/global/keymap_def_text_view -> default-keymap.json:10 /global/keymap_def_text_view -> default-keymap.json:10
/global/keymap_def_time_offset -> default-keymap.json:17 /global/keymap_def_time_offset -> default-keymap.json:17

@ -1092,7 +1092,8 @@ For support questions, email:
number, percent into the file, number, percent into the file,
timestamp, or an anchor in a text file timestamp, or an anchor in a text file
See Also See Also
:next-location, :next-mark, :prev-location, :prev-mark, :relative-goto :next-location, :next-mark, :next-section, :prev-location, :prev-mark,
:prev-section, :relative-goto
Examples Examples
#1 To go to line 22: #1 To go to line 22:
:goto 22  :goto 22 
@ -1232,7 +1233,8 @@ For support questions, email:
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
Move to the next position in the location history Move to the next position in the location history
See Also See Also
:goto, :next-mark, :prev-location, :prev-mark, :relative-goto :goto, :next-mark, :next-section, :prev-location, :prev-mark,
:prev-section, :relative-goto
:next-mark type1 [... typeN] :next-mark type1 [... typeN]
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
@ -1241,14 +1243,21 @@ For support questions, email:
type The type of bookmark -- error, warning, search, type The type of bookmark -- error, warning, search,
user, file, meta user, file, meta
See Also See Also
:goto, :hide-unmarked-lines, :mark, :next-location, :prev-location, :goto, :hide-unmarked-lines, :mark, :next-location, :next-section,
:prev-mark, :prev-mark, :relative-goto :prev-location, :prev-mark, :prev-mark, :prev-section, :relative-goto
Example Example
#1 To go to the next error: #1 To go to the next error:
:next-mark error  :next-mark error 
:next-section
══════════════════════════════════════════════════════════════════════
Move to the next section in the document
See Also
:goto, :next-location, :next-mark, :prev-location, :prev-mark,
:prev-section, :relative-goto
:open path1 [... pathN] :open path1 [... pathN]
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
Open the given file(s) in lnav. Opening files on machines Open the given file(s) in lnav. Opening files on machines
@ -1315,7 +1324,8 @@ For support questions, email:
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
Move to the previous position in the location history Move to the previous position in the location history
See Also See Also
:goto, :next-location, :next-mark, :prev-mark, :relative-goto :goto, :next-location, :next-mark, :next-section, :prev-mark,
:prev-section, :relative-goto
:prev-mark type1 [... typeN] :prev-mark type1 [... typeN]
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
@ -1326,13 +1336,21 @@ For support questions, email:
user, file, meta user, file, meta
See Also See Also
:goto, :hide-unmarked-lines, :mark, :next-location, :next-mark, :goto, :hide-unmarked-lines, :mark, :next-location, :next-mark,
:next-mark, :prev-location, :relative-goto :next-mark, :next-section, :prev-location, :prev-section,
:relative-goto
Example Example
#1 To go to the previous error: #1 To go to the previous error:
:prev-mark error  :prev-mark error 
:prev-section
══════════════════════════════════════════════════════════════════════
Move to the previous section in the document
See Also
:goto, :next-location, :next-mark, :next-section, :prev-location,
:prev-mark, :relative-goto
:prompt type [--alt] [prompt] [initial-value] :prompt type [--alt] [prompt] [initial-value]
══════════════════════════════════════════════════════════════════════ ══════════════════════════════════════════════════════════════════════
Open the given prompt Open the given prompt
@ -1409,7 +1427,8 @@ For support questions, email:
Parameter Parameter
line-count|N% The amount to move the view by. line-count|N% The amount to move the view by.
See Also See Also
:goto, :next-location, :next-mark, :prev-location, :prev-mark :goto, :next-location, :next-mark, :next-section, :prev-location,
:prev-mark, :prev-section
Examples Examples
#1 To move 22 lines down in the view: #1 To move 22 lines down in the view:
:relative-goto +22  :relative-goto +22 

@ -33,5 +33,6 @@ Apr 7 07:32:56 Tim-Stacks-iMac.local logger[234]: Bad data {
abc, abc,
123, 123,
456 456
) )
}] }
]

@ -1,8 +1,8 @@
{"ts": "2013-09-06T20:00:48.124817Z", "lvl": "TRACE", "msg": "trace test"} {"ts": "2013-09-06T20:00:48.124817Z", "lvl": "TRACE", "msg": "trace test"}
{"ts": "2013-09-06T20:00:49.124817Z", "lvl": "INFO", "msg": "Starting up \u001B[0;32mservice\u001B[0m"} {"ts": "2013-09-06T20:00:49.124817Z", "lvl": "INFO", "msg": "Starting up \u001B[0;32mservice\u001B[0m"}
{"ts": "2013-09-06T22:00:49.124817Z", "lvl": "INFO", "msg": "Shutting down service", "user": "steve@example.com"} {"ts": "2013-09-06T22:00:49.124817Z", "lvl": "INFO", "msg": "Shutting down service", "user": "steve@example.com"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG5", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG5", "msg": "D\bDetails...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG4", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG4", "msg": "D\bDe\betails...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG3", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG3", "msg": "Details...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG2", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG2", "msg": "Details...\n"}
{"ts": "2013-09-06 22:01:00Z", "lvl": "DEBUG", "msg": "Details..."} {"ts": "2013-09-06 22:01:00Z", "lvl": "DEBUG", "msg": "Details..."}

@ -6,9 +6,9 @@
[2013-09-06T22:00:49.124] ⋮ Shutting down service [2013-09-06T22:00:49.124] ⋮ Shutting down service
user: steve@example.com user: steve@example.com
[2013-09-06T22:00:59.124] ⋮ Details... [2013-09-06T22:00:59.124] ⋮ Details...
[2013-09-06T22:00:59.124] ⋮ Details... [2013-09-06T22:00:59.124] ⋮ Details...
[2013-09-06T22:00:59.124] ⋮ Details... [2013-09-06T22:00:59.124] ⋮ Details...

@ -6,10 +6,10 @@
[2013-09-06T22:00:49.124000Z] ⋮ Shutting down servicebork bork bork [2013-09-06T22:00:49.124000Z] ⋮ Shutting down servicebork bork bork
user: mailto:steve@example.com user: mailto:steve@example.com
[2013-09-06T22:00:59.124000Z] ⋮ Details... [2013-09-06T22:00:59.124000Z] ⋮ Details...
bork bork bork bork bork bork
[2013-09-06T22:00:59.124000Z] ⋮ Details... [2013-09-06T22:00:59.124000Z] ⋮ Details...
bork bork bork bork bork bork
[2013-09-06T22:00:59.124000Z] ⋮ Details... [2013-09-06T22:00:59.124000Z] ⋮ Details...

@ -2,6 +2,4 @@
├ org.lnav.test: ├ org.lnav.test:
╰ Hello, World! ╰ Hello, World!
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7" 192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
📝 Annotations available, focus on this line and use :annotate to apply them
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7" 192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
📝 Annotations available, focus on this line and use :annotate to apply them

@ -0,0 +1,5 @@
"title": "Midnight Rain",
"description": "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
}
]
}

@ -0,0 +1,6 @@
"author": "Ralls, Kim",
"title": "Midnight Rain",
"description": "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
}
]
}

@ -0,0 +1,5 @@
"title": "Midnight Rain",
"description": "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
}
]
}

@ -0,0 +1,6 @@
"author": "Ralls, Kim",
"title": "Midnight Rain",
"description": "A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."
}
]
}

@ -1,8 +1,8 @@
{"ts": "2013-09-06T20:00:48.124817Z", "lvl": "TRACE", "msg": "trace test"} {"ts": "2013-09-06T20:00:48.124817Z", "lvl": "TRACE", "msg": "trace test"}
{"ts": "2013-09-06T20:00:49.124817Z", "lvl": "INFO", "msg": "Starting up \u001B[0;32mservice\u001B[0m"} {"ts": "2013-09-06T20:00:49.124817Z", "lvl": "INFO", "msg": "Starting up \u001B[0;32mservice\u001B[0m"}
{"ts": "2013-09-06T22:00:49.124817Z", "lvl": "INFO", "msg": "Shutting down service", "user": "steve@example.com"} {"ts": "2013-09-06T22:00:49.124817Z", "lvl": "INFO", "msg": "Shutting down service", "user": "steve@example.com"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG5", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG5", "msg": "D\bDetails...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG4", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG4", "msg": "D\bDe\betails...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG3", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG3", "msg": "Details...\n"}
{"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG2", "msg": "Details...\n"} {"ts": "2013-09-06T22:00:59.124817Z", "lvl": "DEBUG2", "msg": "Details...\n"}
{"ts": "2013-09-06 22:01:00Z", "lvl": "DEBUG", "msg": "Details..."} {"ts": "2013-09-06 22:01:00Z", "lvl": "DEBUG", "msg": "Details..."}

@ -79,3 +79,21 @@ run_cap_test ${lnav_test} -n \
-c ';SELECT top_meta FROM lnav_top_view' \ -c ';SELECT top_meta FROM lnav_top_view' \
-c ':write-json-to -' \ -c ':write-json-to -' \
${test_dir}/formats/jsontest/format.json ${test_dir}/formats/jsontest/format.json
run_cap_test ${lnav_test} -n \
-c ':goto 3' \
-c ':next-section' \
${test_dir}/books.json
run_cap_test ${lnav_test} -n \
-c ':goto 3' \
-c ':next-section' \
< ${test_dir}/books.json
run_cap_test ${lnav_test} -n \
-c ':goto #/catalog/1/title' \
${test_dir}/books.json
run_cap_test ${lnav_test} -n \
-c ':goto #/catalog/1/title' \
< ${test_dir}/books.json

Loading…
Cancel
Save