From 5068d34c08f951a7ea6257d305a1627b09a95817 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 19:44:55 +0200 Subject: Adding upstream version 0.11.1. Signed-off-by: Daniel Baumann --- src/plain_text_source.cc | 373 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 src/plain_text_source.cc (limited to 'src/plain_text_source.cc') diff --git a/src/plain_text_source.cc b/src/plain_text_source.cc new file mode 100644 index 0000000..4786335 --- /dev/null +++ b/src/plain_text_source.cc @@ -0,0 +1,373 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "plain_text_source.hh" + +#include "base/itertools.hh" +#include "config.h" + +static std::vector +to_text_line(const std::vector& lines) +{ + file_off_t off = 0; + + return lines | lnav::itertools::map([&off](const auto& elem) { + auto retval = plain_text_source::text_line{ + off, + elem, + }; + + off += elem.length() + 1; + return retval; + }); +} + +plain_text_source::plain_text_source(const std::string& text) +{ + size_t start = 0, end; + + while ((end = text.find('\n', start)) != std::string::npos) { + size_t len = (end - start); + this->tds_lines.emplace_back(start, text.substr(start, len)); + start = end + 1; + } + if (start < text.length()) { + this->tds_lines.emplace_back(start, text.substr(start)); + } + this->tds_longest_line = this->compute_longest_line(); +} + +plain_text_source::plain_text_source(const std::vector& text_lines) +{ + this->replace_with(text_lines); +} + +plain_text_source::plain_text_source(const std::vector& text_lines) + : tds_lines(to_text_line(text_lines)) +{ + this->tds_longest_line = this->compute_longest_line(); +} + +plain_text_source& +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; + for (auto& line : text_lines.split_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(); + return *this; +} + +plain_text_source& +plain_text_source::replace_with(const std::vector& text_lines) +{ + file_off_t off = 0; + for (const auto& str : text_lines) { + this->tds_lines.emplace_back(off, str); + off += str.length() + 1; + } + this->tds_longest_line = this->compute_longest_line(); + return *this; +} + +void +plain_text_source::clear() +{ + this->tds_lines.clear(); + this->tds_longest_line = 0; + this->tds_text_format = text_format_t::TF_UNKNOWN; +} + +plain_text_source& +plain_text_source::truncate_to(size_t max_lines) +{ + while (this->tds_lines.size() > max_lines) { + this->tds_lines.pop_back(); + } + return *this; +} + +size_t +plain_text_source::text_line_width(textview_curses& curses) +{ + return this->tds_longest_line; +} + +void +plain_text_source::text_value_for_line(textview_curses& tc, + int row, + std::string& value_out, + text_sub_source::line_flags_t flags) +{ + value_out = this->tds_lines[row].tl_value.get_string(); +} + +void +plain_text_source::text_attrs_for_line(textview_curses& tc, + int line, + string_attrs_t& value_out) +{ + value_out = this->tds_lines[line].tl_value.get_attrs(); + if (this->tds_reverse_selection && tc.is_selectable() + && tc.get_selection() == line) + { + value_out.emplace_back(line_range{0, -1}, + VC_STYLE.value(text_attrs{A_REVERSE})); + } +} + +size_t +plain_text_source::text_size_for_line(textview_curses& tc, + int row, + text_sub_source::line_flags_t flags) +{ + return this->tds_lines[row].tl_value.length(); +} + +text_format_t +plain_text_source::get_text_format() const +{ + return this->tds_text_format; +} + +size_t +plain_text_source::compute_longest_line() +{ + size_t retval = 0; + for (auto& iter : this->tds_lines) { + retval = std::max(retval, (size_t) iter.tl_value.length()); + } + return retval; +} + +nonstd::optional +plain_text_source::line_for_offset(file_off_t off) const +{ + struct cmper { + bool operator()(const file_off_t& lhs, const text_line& rhs) + { + return lhs < rhs.tl_offset; + } + + bool operator()(const text_line& lhs, const file_off_t& rhs) + { + return lhs.tl_offset < rhs; + } + }; + + if (this->tds_lines.empty()) { + return nonstd::nullopt; + } + + auto iter = std::lower_bound( + this->tds_lines.begin(), this->tds_lines.end(), off, cmper{}); + if (iter == this->tds_lines.end()) { + if (this->tds_lines.back().contains_offset(off)) { + return nonstd::make_optional( + vis_line_t(std::distance(this->tds_lines.end() - 1, iter))); + } + return nonstd::nullopt; + } + + if (!iter->contains_offset(off) && iter != this->tds_lines.begin()) { + --iter; + } + + return nonstd::make_optional( + vis_line_t(std::distance(this->tds_lines.begin(), iter))); +} + +void +plain_text_source::text_crumbs_for_line(int line, + std::vector& crumbs) +{ + const auto initial_size = crumbs.size(); + const auto& tl = this->tds_lines[line]; + + this->tds_doc_sections.m_sections_tree.visit_overlapping( + tl.tl_offset, + [&crumbs, initial_size, meta = &this->tds_doc_sections, this]( + const auto& iv) { + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key) + | lnav::itertools::append(iv.value); + crumbs.template emplace_back( + iv.value, + [meta, path]() { return meta->possibility_provider(path); }, + [this, meta, path](const auto& key) { + auto curr_node = lnav::document::hier_node::lookup_path( + meta->m_sections_root.get(), path); + if (!curr_node) { + return; + } + auto* parent_node = curr_node.value()->hn_parent; + + if (parent_node == nullptr) { + return; + } + key.template match( + [this, parent_node](const std::string& str) { + auto sib_iter + = parent_node->hn_named_children.find(str); + if (sib_iter + == parent_node->hn_named_children.end()) { + return; + } + this->line_for_offset(sib_iter->second->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + }, + [this, parent_node](size_t index) { + if (index >= parent_node->hn_children.size()) { + return; + } + auto sib = parent_node->hn_children[index].get(); + this->line_for_offset(sib->hn_start) | + [this](const auto new_top) { + this->tss_view->set_top(new_top); + }; + }); + }); + }); + + auto path = crumbs | lnav::itertools::skip(initial_size) + | lnav::itertools::map(&breadcrumb::crumb::c_key); + auto node = lnav::document::hier_node::lookup_path( + this->tds_doc_sections.m_sections_root.get(), path); + + if (node && !node.value()->hn_children.empty()) { + auto poss_provider = [curr_node = node.value()]() { + std::vector retval; + for (const auto& child : curr_node->hn_named_children) { + retval.template emplace_back(child.first); + } + return retval; + }; + auto path_performer = [this, curr_node = node.value()]( + const breadcrumb::crumb::key_t& value) { + value.template match( + [this, curr_node](const std::string& str) { + auto child_iter = curr_node->hn_named_children.find(str); + 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, curr_node](size_t index) { + 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); + }; + }); + }; + crumbs.emplace_back( + "", "\u22ef", std::move(poss_provider), std::move(path_performer)); + crumbs.back().c_expected_input = node.value()->hn_named_children.empty() + ? breadcrumb::crumb::expected_input_t::index + : breadcrumb::crumb::expected_input_t::index_or_exact; + } +} + +nonstd::optional +plain_text_source::row_for_anchor(const std::string& id) +{ + nonstd::optional retval; + + if (this->tds_doc_sections.m_sections_root == nullptr) { + return retval; + } + + lnav::document::hier_node::depth_first( + this->tds_doc_sections.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 + = text_anchors::to_anchor_string(child_pair.first); + + if (child_anchor == id) { + retval = this->line_for_offset(child_pair.second->hn_start); + } + } + }); + + return retval; +} + +std::unordered_set +plain_text_source::get_anchors() +{ + std::unordered_set retval; + + lnav::document::hier_node::depth_first( + this->tds_doc_sections.m_sections_root.get(), + [&retval](const lnav::document::hier_node* node) { + for (const auto& child_pair : node->hn_named_children) { + retval.emplace( + text_anchors::to_anchor_string(child_pair.first)); + } + }); + + return retval; +} + +nonstd::optional +plain_text_source::anchor_for_row(vis_line_t vl) +{ + nonstd::optional retval; + + if (vl > this->tds_lines.size() + || this->tds_doc_sections.m_sections_root == nullptr) + { + return retval; + } + + const auto& tl = this->tds_lines[vl]; + + 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; }); + }); + + return retval; +} -- cgit v1.2.3