libutil: Add Pos::getSnippetUpTo(Pos)

This commit is contained in:
Robert Hensing 2024-07-09 11:43:07 +02:00
parent 63f520fd00
commit e5af7cbeb9
3 changed files with 168 additions and 0 deletions

View file

@ -110,4 +110,50 @@ void Pos::LinesIterator::bump(bool atFirst)
input.remove_prefix(eol);
}
std::string Pos::getSnippetUpTo(const Pos & end) const {
assert(this->origin == end.origin);
if (end.line < this->line)
return "";
if (auto source = getSource()) {
auto firstLine = LinesIterator(*source);
for (auto i = 1; i < this->line; ++i) {
++firstLine;
}
auto lastLine = LinesIterator(*source);
for (auto i = 1; i < end.line; ++i) {
++lastLine;
}
LinesIterator linesEnd;
std::string result;
for (auto i = firstLine; i != linesEnd; ++i) {
auto firstColumn = i == firstLine ? (this->column ? this->column - 1 : 0) : 0;
if (firstColumn > i->size())
firstColumn = i->size();
auto lastColumn = i == lastLine ? (end.column ? end.column - 1 : 0) : std::numeric_limits<int>::max();
if (lastColumn < firstColumn)
lastColumn = firstColumn;
if (lastColumn > i->size())
lastColumn = i->size();
result += i->substr(firstColumn, lastColumn - firstColumn);
if (i == lastLine) {
break;
} else {
result += '\n';
}
}
return result;
}
return "";
}
}

View file

@ -63,6 +63,8 @@ struct Pos
bool operator==(const Pos & rhs) const = default;
auto operator<=>(const Pos & rhs) const = default;
std::string getSnippetUpTo(const Pos & end) const;
struct LinesIterator {
using difference_type = size_t;
using value_type = std::string_view;

View file

@ -0,0 +1,120 @@
#include <gtest/gtest.h>
#include "position.hh"
namespace nix {
inline Pos::Origin makeStdin(std::string s)
{
return Pos::Stdin{make_ref<std::string>(s)};
}
TEST(Position, getSnippetUpTo_0)
{
Pos::Origin o = makeStdin("");
Pos p(1, 1, o);
ASSERT_EQ(p.getSnippetUpTo(p), "");
}
TEST(Position, getSnippetUpTo_1)
{
Pos::Origin o = makeStdin("x");
{
// NOTE: line and column are actually 1-based indexes
Pos start(0, 0, o);
Pos end(99, 99, o);
ASSERT_EQ(start.getSnippetUpTo(start), "");
ASSERT_EQ(start.getSnippetUpTo(end), "x");
ASSERT_EQ(end.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(start), "");
}
{
// NOTE: line and column are actually 1-based indexes
Pos start(0, 99, o);
Pos end(99, 0, o);
ASSERT_EQ(start.getSnippetUpTo(start), "");
// "x" might be preferable, but we only care about not crashing for invalid inputs
ASSERT_EQ(start.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(start), "");
}
{
Pos start(1, 1, o);
Pos end(1, 99, o);
ASSERT_EQ(start.getSnippetUpTo(start), "");
ASSERT_EQ(start.getSnippetUpTo(end), "x");
ASSERT_EQ(end.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(start), "");
}
{
Pos start(1, 1, o);
Pos end(99, 99, o);
ASSERT_EQ(start.getSnippetUpTo(start), "");
ASSERT_EQ(start.getSnippetUpTo(end), "x");
ASSERT_EQ(end.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(start), "");
}
}
TEST(Position, getSnippetUpTo_2)
{
Pos::Origin o = makeStdin("asdf\njkl\nqwer");
{
Pos start(1, 1, o);
Pos end(1, 2, o);
ASSERT_EQ(start.getSnippetUpTo(start), "");
ASSERT_EQ(start.getSnippetUpTo(end), "a");
ASSERT_EQ(end.getSnippetUpTo(end), "");
ASSERT_EQ(end.getSnippetUpTo(start), "");
}
{
Pos start(1, 2, o);
Pos end(1, 3, o);
ASSERT_EQ(start.getSnippetUpTo(end), "s");
}
{
Pos start(1, 2, o);
Pos end(2, 2, o);
ASSERT_EQ(start.getSnippetUpTo(end), "sdf\nj");
}
{
Pos start(1, 2, o);
Pos end(3, 2, o);
ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl\nq");
}
{
Pos start(1, 2, o);
Pos end(2, 99, o);
ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl");
}
{
Pos start(1, 4, o);
Pos end(2, 99, o);
ASSERT_EQ(start.getSnippetUpTo(end), "f\njkl");
}
{
Pos start(1, 5, o);
Pos end(2, 99, o);
ASSERT_EQ(start.getSnippetUpTo(end), "\njkl");
}
{
Pos start(1, 6, o); // invalid: starting column past last "line character", ie at the newline
Pos end(2, 99, o);
ASSERT_EQ(start.getSnippetUpTo(end), "\njkl"); // jkl might be acceptable for this invalid start position
}
{
Pos start(1, 1, o);
Pos end(2, 0, o); // invalid
ASSERT_EQ(start.getSnippetUpTo(end), "asdf\n");
}
}
TEST(Position, example_1)
{
Pos::Origin o = makeStdin(" unambiguous = \n /** Very close */\n x: x;\n# ok\n");
Pos start(2, 5, o);
Pos end(2, 22, o);
ASSERT_EQ(start.getSnippetUpTo(end), "/** Very close */");
}
} // namespace nix