#ifndef TOML11_REGION_IMPL_HPP #define TOML11_REGION_IMPL_HPP #include "../fwd/region_fwd.hpp" #include "../utility.hpp" #include "../version.hpp" #include #include #include #include #include #include namespace toml { inline namespace TOML11_INLINE_VERSION_NAMESPACE { namespace detail { // a value defined in [first, last). // Those source must be the same. Instread, `region` does not make sense. TOML11_INLINE region::region(const location& first, const location& last) : source_(first.source()), source_name_(first.source_name()), length_(last.get_location() - first.get_location()), first_(first.get_location()), first_line_(first.line_number()), first_column_(first.column_number()), last_(last.get_location()), last_line_(last.line_number()), last_column_(last.column_number()) { assert(first.source() == last.source()); assert(first.source_name() == last.source_name()); } // shorthand of [loc, loc+1) TOML11_INLINE region::region(const location& loc) : source_(loc.source()), source_name_(loc.source_name()), length_(0), first_line_(0), first_column_(0), last_line_(0), last_column_(0) { // if the file ends with LF, the resulting region points no char. if(loc.eof()) { if(loc.get_location() == 0) { this->length_ = 0; this->first_ = 0; this->first_line_ = 0; this->first_column_ = 0; this->last_ = 0; this->last_line_ = 0; this->last_column_ = 0; } else { const auto first = prev(loc); this->first_ = first.get_location(); this->first_line_ = first.line_number(); this->first_column_ = first.column_number(); this->last_ = loc.get_location(); this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number(); this->length_ = 1; } } else { this->first_ = loc.get_location(); this->first_line_ = loc.line_number(); this->first_column_ = loc.column_number(); this->last_ = loc.get_location() + 1; this->last_line_ = loc.line_number(); this->last_column_ = loc.column_number() + 1; this->length_ = 1; } } TOML11_INLINE region::char_type region::at(std::size_t i) const { if(this->last_ <= this->first_ + i) { throw std::out_of_range("range::at: index " + std::to_string(i) + " exceeds length " + std::to_string(this->length_)); } const auto iter = std::next(this->source_->cbegin(), static_cast(this->first_ + i)); return *iter; } TOML11_INLINE region::const_iterator region::begin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::end() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE region::const_iterator region::cbegin() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->first_)); } TOML11_INLINE region::const_iterator region::cend() const noexcept { return std::next(this->source_->cbegin(), static_cast(this->last_)); } TOML11_INLINE std::string region::as_string() const { if(this->is_ok()) { const auto begin = std::next(this->source_->cbegin(), static_cast(this->first_)); const auto end = std::next(this->source_->cbegin(), static_cast(this->last_ )); return ::toml::detail::make_string(begin, end); } else { return std::string(""); } } TOML11_INLINE std::pair region::take_line(const_iterator begin, const_iterator end) const { // To omit long line, we cap region by before/after 30 chars const auto dist_before = std::distance(source_->cbegin(), begin); const auto dist_after = std::distance(end, source_->cend()); const const_iterator capped_begin = (dist_before <= 30) ? source_->cbegin() : std::prev(begin, 30); const const_iterator capped_end = (dist_after <= 30) ? source_->cend() : std::next(end, 30); const auto lf = char_type('\n'); const auto lf_before = std::find(cxx::make_reverse_iterator(begin), cxx::make_reverse_iterator(capped_begin), lf); const auto lf_after = std::find(end, capped_end, lf); auto offset = static_cast(std::distance(lf_before.base(), begin)); std::string retval = make_string(lf_before.base(), lf_after); if(lf_before.base() != source_->cbegin() && *lf_before != lf) { retval = "... " + retval; offset += 4; } if(lf_after != source_->cend() && *lf_after != lf) { retval = retval + " ..."; } return std::make_pair(retval, offset); } TOML11_INLINE std::vector> region::as_lines() const { assert(this->is_ok()); if(this->length_ == 0) { return std::vector>{ std::make_pair("", std::size_t(0)) }; } // Consider the following toml file // ``` // array = [ // 1, 2, 3, // ] # comment // ``` // and the region represnets // ``` // [ // 1, 2, 3, // ] // ``` // but we want to show the following. // ``` // array = [ // 1, 2, 3, // ] # comment // ``` // So we need to find LFs before `begin` and after `end`. // // But, if region ends with LF, it should not include the next line. // ``` // a = 42 // ^^^- with the last LF // ``` // So we start from `end-1` when looking for LF. const auto begin_idx = static_cast(this->first_); const auto end_idx = static_cast(this->last_) - 1; // length_ != 0, so begin < end. then begin <= end-1 assert(begin_idx <= end_idx); const auto begin = std::next(this->source_->cbegin(), begin_idx); const auto end = std::next(this->source_->cbegin(), end_idx); assert(this->first_line_number() <= this->last_line_number()); if(this->first_line_number() == this->last_line_number()) { return std::vector>{ this->take_line(begin, end) }; } // we have multiple lines. `begin` and `end` points different lines. // that means that there is at least one `LF` between `begin` and `end`. const auto after_begin = std::distance(begin, this->source_->cend()); const auto before_end = std::distance(this->source_->cbegin(), end); const_iterator capped_file_end = this->source_->cend(); const_iterator capped_file_begin = this->source_->cbegin(); if(60 < after_begin) {capped_file_end = std::next(begin, 50);} if(60 < before_end) {capped_file_begin = std::prev(end, 50);} const auto lf = char_type('\n'); const auto first_line_end = std::find(begin, capped_file_end, lf); const auto last_line_begin = std::find(capped_file_begin, end, lf); const auto first_line = this->take_line(begin, first_line_end); const auto last_line = this->take_line(last_line_begin, end); if(this->first_line_number() + 1 == this->last_line_number()) { return std::vector>{ first_line, last_line }; } else { return std::vector>{ first_line, std::make_pair("...", 0), last_line }; } } } // namespace detail } // TOML11_INLINE_VERSION_NAMESPACE } // namespace toml #endif // TOML11_REGION_IMPL_HPP