summaryrefslogtreecommitdiff
path: root/src/changes.hh
blob: 638a4bcb7e451b24cb096b7bc75171de625f52d9 (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
99
100
101
102
103
104
105
106
107
108
109
110
111
#ifndef changes_hh_INCLUDED
#define changes_hh_INCLUDED

#include "buffer.hh"
#include "coord.hh"

namespace Kakoune
{

// This tracks position changes for changes that are done
// in a forward way (each change takes place at a position)
// *after* the previous one.
struct ForwardChangesTracker
{
    BufferCoord cur_pos; // last change position at current modification
    BufferCoord old_pos; // last change position at start

    void update(const Buffer::Change& change);
    void update(const Buffer& buffer, size_t& timestamp);

    BufferCoord get_old_coord(BufferCoord coord) const;
    BufferCoord get_new_coord(BufferCoord coord) const;
    BufferCoord get_new_coord_tolerant(BufferCoord coord) const;

    bool relevant(const Buffer::Change& change, BufferCoord old_coord) const;
};

const Buffer::Change* forward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);
const Buffer::Change* backward_sorted_until(const Buffer::Change* first, const Buffer::Change* last);

template<typename RangeContainer>
void update_forward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
{
    ForwardChangesTracker changes_tracker;
    auto advance_while_relevant = [&, it = changes.begin()]
                                  (const BufferCoord& pos) mutable {
        while (it != changes.end() and changes_tracker.relevant(*it, pos))
            changes_tracker.update(*it++);
    };

    for (auto& range : ranges)
    {
        auto& first = get_first(range);
        auto& last = get_last(range);
        advance_while_relevant(first);
        first = changes_tracker.get_new_coord_tolerant(first);

        advance_while_relevant(last);
        last = changes_tracker.get_new_coord_tolerant(last);
    }
}

template<typename RangeContainer>
void update_backward(ConstArrayView<Buffer::Change> changes, RangeContainer& ranges)
{
    ForwardChangesTracker changes_tracker;
    auto advance_while_relevant = [&, it = changes.rbegin()]
                                  (const BufferCoord& pos) mutable {
        while (it != changes.rend())
        {
            const Buffer::Change change{it->type,
                                        changes_tracker.get_new_coord(it->begin),
                                        changes_tracker.get_new_coord(it->end)};
            if (not changes_tracker.relevant(change, pos))
                break;
            changes_tracker.update(change);
            ++it;
        }
    };

    for (auto& range : ranges)
    {
        auto& first = get_first(range);
        auto& last = get_last(range);
        advance_while_relevant(first);
        first = changes_tracker.get_new_coord_tolerant(first);

        advance_while_relevant(last);
        last = changes_tracker.get_new_coord_tolerant(last);
    }
}

template<typename RangeContainer>
void update_ranges(Buffer& buffer, size_t& timestamp, RangeContainer& ranges)
{
    if (timestamp == buffer.timestamp())
        return;

    auto changes = buffer.changes_since(timestamp);
    for (auto change_it = changes.begin(); change_it != changes.end(); )
    {
        auto forward_end = forward_sorted_until(change_it, changes.end());
        auto backward_end = backward_sorted_until(change_it, changes.end());

        if (forward_end >= backward_end)
        {
            update_forward({ change_it, forward_end }, ranges);
            change_it = forward_end;
        }
        else
        {
            update_backward({ change_it, backward_end }, ranges);
            change_it = backward_end;
        }
    }
    timestamp = buffer.timestamp();
}

}

#endif // changes_hh_INCLUDED