Skip to content

Commit

Permalink
libutil: Add Pos::getSnippetUpTo(Pos)
Browse files Browse the repository at this point in the history
  • Loading branch information
roberth committed Jul 9, 2024
1 parent 8787968 commit 07cf330
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/libutil/position.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,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) : 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 "";
}


}
2 changes: 2 additions & 0 deletions src/libutil/position.hh
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ struct Pos
bool operator!=(const Pos & rhs) const = default;
bool operator<(const Pos & rhs) const;

std::string getSnippetUpTo(const Pos & end) const;

struct LinesIterator {
using difference_type = size_t;
using value_type = std::string_view;
Expand Down
120 changes: 120 additions & 0 deletions tests/unit/libutil/position.cc
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 07cf330

Please sign in to comment.