summaryrefslogtreecommitdiff
path: root/src/line_modification.cc
blob: 4e24e9c8b5dcd6306ecc10a28f3160b8dfca899c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "line_modification.hh"

#include "buffer.hh"

namespace Kakoune
{

static LineModification make_line_modif(const Buffer::Change& change)
{
    LineCount num_added = 0, num_removed = 0;
    if (change.type == Buffer::Change::Insert)
    {
        num_added = change.end.line - change.begin.line;
         // inserted a new line at buffer end but end coord is on same line
        if (change.at_end and change.end.column != 0)
            ++num_added;
    }
    else
    {
        num_removed = change.end.line - change.begin.line;
        // removed last line, but end coord is on same line
        if (change.at_end and change.end.column != 0)
            ++num_removed;
    }
    // modified a line
    if (not change.at_end and
        (change.begin.column != 0 or change.end.column != 0))
    {
        ++num_removed;
        ++num_added;
    }
    return { change.begin.line, change.begin.line, num_removed, num_added };
}

Vector<LineModification> compute_line_modifications(const Buffer& buffer, size_t timestamp)
{
    Vector<LineModification> res;
    for (auto& buf_change : buffer.changes_since(timestamp))
    {
        auto change = make_line_modif(buf_change);

        auto pos = std::upper_bound(res.begin(), res.end(), change.new_line,
                                    [](const LineCount& l, const LineModification& c)
                                    { return l < c.new_line; });

        if (pos != res.begin())
        {
            auto& prev = *(pos-1);
            if (change.new_line <= prev.new_line + prev.num_added)
            {
                --pos;
                const LineCount removed_from_previously_added_by_pos =
                    clamp(pos->new_line + pos->num_added - change.new_line,
                          0_line, std::min(pos->num_added, change.num_removed));

                pos->num_removed += change.num_removed - removed_from_previously_added_by_pos;
                pos->num_added += change.num_added - removed_from_previously_added_by_pos;
            }
            else
            {
                change.old_line -= prev.diff();
                pos = res.insert(pos, change);
            }
        }
        else
            pos = res.insert(pos, change);

        auto next = pos + 1;
        auto diff = buf_change.end.line - buf_change.begin.line;
        if (buf_change.type == Buffer::Change::Erase)
        {
            auto delend = std::upper_bound(next, res.end(), change.new_line + change.num_removed,
                                           [](const LineCount& l, const LineModification& c)
                                           { return l < c.new_line; });

            for (auto it = next; it != delend; ++it)
            {
                const LineCount removed_from_previously_added_by_it =
                    std::min(it->num_added, change.new_line + change.num_removed - it->new_line);

                pos->num_removed += it->num_removed - removed_from_previously_added_by_it;
                pos->num_added += it->num_added - removed_from_previously_added_by_it;
            }
            next = res.erase(next, delend);

            for (auto it = next; it != res.end(); ++it)
                it->new_line -= diff;
        }
        else
        {
            for (auto it = next; it != res.end(); ++it)
                it->new_line += diff;
        }
    }
    return res;
}

}