251 lines
7.9 KiB
C++
251 lines
7.9 KiB
C++
#ifndef TOML11_REGION_IMPL_HPP
|
|
#define TOML11_REGION_IMPL_HPP
|
|
|
|
#include "../fwd/region_fwd.hpp"
|
|
#include "../utility.hpp"
|
|
#include "../version.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <cassert>
|
|
|
|
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<difference_type>(this->first_ + i));
|
|
return *iter;
|
|
}
|
|
|
|
TOML11_INLINE region::const_iterator region::begin() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->first_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::end() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->last_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::cbegin() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->first_));
|
|
}
|
|
TOML11_INLINE region::const_iterator region::cend() const noexcept
|
|
{
|
|
return std::next(this->source_->cbegin(),
|
|
static_cast<difference_type>(this->last_));
|
|
}
|
|
|
|
TOML11_INLINE std::string region::as_string() const
|
|
{
|
|
if(this->is_ok())
|
|
{
|
|
const auto begin = std::next(this->source_->cbegin(), static_cast<difference_type>(this->first_));
|
|
const auto end = std::next(this->source_->cbegin(), static_cast<difference_type>(this->last_ ));
|
|
return ::toml::detail::make_string(begin, end);
|
|
}
|
|
else
|
|
{
|
|
return std::string("");
|
|
}
|
|
}
|
|
|
|
TOML11_INLINE std::pair<std::string, std::size_t>
|
|
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::size_t>(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<std::pair<std::string, std::size_t>> region::as_lines() const
|
|
{
|
|
assert(this->is_ok());
|
|
if(this->length_ == 0)
|
|
{
|
|
return std::vector<std::pair<std::string, std::size_t>>{
|
|
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<difference_type>(this->first_);
|
|
const auto end_idx = static_cast<difference_type>(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<std::pair<std::string, std::size_t>>{
|
|
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<std::pair<std::string, std::size_t>>{
|
|
first_line, last_line
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return std::vector<std::pair<std::string, std::size_t>>{
|
|
first_line, std::make_pair("...", 0), last_line
|
|
};
|
|
}
|
|
}
|
|
|
|
} // namespace detail
|
|
} // TOML11_INLINE_VERSION_NAMESPACE
|
|
} // namespace toml
|
|
#endif // TOML11_REGION_IMPL_HPP
|