diff options
Diffstat (limited to '')
-rw-r--r-- | src/plain_text_source.cc | 268 |
1 files changed, 250 insertions, 18 deletions
diff --git a/src/plain_text_source.cc b/src/plain_text_source.cc index 632a541..608c623 100644 --- a/src/plain_text_source.cc +++ b/src/plain_text_source.cc @@ -31,6 +31,7 @@ #include "base/itertools.hh" #include "config.h" +#include "scn/scn.h" static std::vector<plain_text_source::text_line> to_text_line(const std::vector<attr_line_t>& lines) @@ -79,14 +80,44 @@ plain_text_source::replace_with(const attr_line_t& text_lines) { this->tds_lines.clear(); 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; - for (auto& line : text_lines.split_lines()) { + 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; } @@ -99,6 +130,24 @@ plain_text_source::replace_with(const std::vector<std::string>& text_lines) off += str.length() + 1; } 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(const std::vector<attr_line_t>& text_lines) +{ + file_off_t off = 0; + for (const auto& al : text_lines) { + this->tds_lines.emplace_back(off, al); + off += al.length() + 1; + } + this->tds_longest_line = this->compute_longest_line(); + if (this->tss_view != nullptr) { + this->tss_view->set_needs_update(); + } return *this; } @@ -108,6 +157,9 @@ plain_text_source::clear() this->tds_lines.clear(); this->tds_longest_line = 0; this->tds_text_format = text_format_t::TF_UNKNOWN; + if (this->tss_view != nullptr) { + this->tss_view->set_needs_update(); + } } plain_text_source& @@ -116,6 +168,9 @@ plain_text_source::truncate_to(size_t max_lines) while (this->tds_lines.size() > max_lines) { this->tds_lines.pop_back(); } + if (this->tss_view != nullptr) { + this->tss_view->set_needs_update(); + } return *this; } @@ -132,6 +187,18 @@ plain_text_source::text_value_for_line(textview_curses& tc, text_sub_source::line_flags_t flags) { value_out = this->tds_lines[row].tl_value.get_string(); + this->tds_line_indent_size = 0; + for (const auto& ch : value_out) { + if (ch == ' ') { + this->tds_line_indent_size += 1; + } else if (ch == '\t') { + do { + this->tds_line_indent_size += 1; + } while (this->tds_line_indent_size % 8); + } else { + break; + } + } } void @@ -146,6 +213,18 @@ plain_text_source::text_attrs_for_line(textview_curses& tc, value_out.emplace_back(line_range{0, -1}, VC_STYLE.value(text_attrs{A_REVERSE})); } + for (const auto& indent : this->tds_doc_sections.m_indents) { + if (indent < this->tds_line_indent_size) { + auto guide_lr = line_range{ + (int) indent, + (int) (indent + 1), + line_range::unit::codepoint, + }; + value_out.emplace_back(guide_lr, + VC_BLOCK_ELEM.value(block_elem_t{ + L'\u258f', role_t::VCR_INDENT_GUIDE})); + } + } } size_t @@ -247,7 +326,11 @@ plain_text_source::text_crumbs_for_line(int line, } this->line_for_offset(sib_iter->second->hn_start) | [this](const auto new_top) { - this->tss_view->set_top(new_top); + this->tss_view->set_selection(new_top); + if (this->tss_view->is_selectable()) { + this->tss_view->set_top(new_top - 2_vl, + false); + } }; }, [this, parent_node](size_t index) { @@ -258,6 +341,10 @@ plain_text_source::text_crumbs_for_line(int line, this->line_for_offset(sib->hn_start) | [this](const auto new_top) { this->tss_view->set_selection(new_top); + if (this->tss_view->is_selectable()) { + this->tss_view->set_top(new_top - 2_vl, + false); + } }; }); }); @@ -284,7 +371,7 @@ plain_text_source::text_crumbs_for_line(int line, if (child_iter != curr_node->hn_named_children.end()) { this->line_for_offset(child_iter->second->hn_start) | [this](const auto new_top) { - this->tss_view->set_top(new_top); + this->tss_view->set_selection(new_top); }; } }, @@ -292,7 +379,7 @@ plain_text_source::text_crumbs_for_line(int line, auto* child = curr_node->hn_children[index].get(); this->line_for_offset(child->hn_start) | [this](const auto new_top) { - this->tss_view->set_top(new_top); + this->tss_view->set_selection(new_top); }; }); }; @@ -313,16 +400,47 @@ plain_text_source::row_for_anchor(const std::string& id) 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( - this->tds_doc_sections.m_sections_root.get(), + meta.m_sections_root.get(), [this, &id, &retval](const lnav::document::hier_node* node) { for (const auto& child_pair : node->hn_named_children) { - auto child_anchor + const auto& child_anchor = text_anchors::to_anchor_string(child_pair.first); - if (child_anchor == id) { - retval = this->line_for_offset(child_pair.second->hn_start); + if (child_anchor != id) { + continue; } + + retval = this->line_for_offset(child_pair.second->hn_start); + break; } }); @@ -358,16 +476,130 @@ plain_text_source::anchor_for_row(vis_line_t 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( - tl.tl_offset, [&retval](const lnav::document::section_interval_t& iv) { - retval = iv.value.match( - [](const std::string& str) { - return nonstd::make_optional( - text_anchors::to_anchor_string(str)); - }, - [](size_t) { return nonstd::nullopt; }); - }); + if (path_for_line.empty()) { + 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); + } else if (!md.m_sections_root->hn_children.empty()) { + return this->line_for_offset( + md.m_sections_root->hn_children[0]->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; } |