summaryrefslogtreecommitdiff
path: root/src/buffer.hh
blob: 2c4b004274a4ef476cf23c12e635ecb34f2d3021 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#ifndef buffer_hh_INCLUDED
#define buffer_hh_INCLUDED

#include <vector>
#include <list>
#include <memory>

#include "line_and_column.hh"
#include "option_manager.hh"
#include "hook_manager.hh"
#include "string.hh"
#include "units.hh"

namespace Kakoune
{

class Buffer;

struct BufferCoord : LineAndColumn<BufferCoord, LineCount, ByteCount>
{
    constexpr BufferCoord(LineCount line = 0, ByteCount column = 0)
        : LineAndColumn(line, column) {}
};

// A BufferIterator permits to iterate over the characters of a buffer
class BufferIterator
{
public:
    typedef char value_type;
    typedef size_t difference_type;
    typedef const value_type* pointer;
    typedef const value_type& reference;
    typedef std::bidirectional_iterator_tag iterator_category;

    BufferIterator() : m_buffer(nullptr) {}
    BufferIterator(const Buffer& buffer, BufferCoord coord);

    bool operator== (const BufferIterator& iterator) const;
    bool operator!= (const BufferIterator& iterator) const;
    bool operator<  (const BufferIterator& iterator) const;
    bool operator<= (const BufferIterator& iterator) const;
    bool operator>  (const BufferIterator& iterator) const;
    bool operator>= (const BufferIterator& iterator) const;

    char   operator* () const;
    size_t operator- (const BufferIterator& iterator) const;

    BufferIterator operator+ (ByteCount size) const;
    BufferIterator operator- (ByteCount size) const;

    BufferIterator& operator+= (ByteCount size);
    BufferIterator& operator-= (ByteCount size);

    BufferIterator& operator++ ();
    BufferIterator& operator-- ();

    BufferIterator operator++ (int);
    BufferIterator operator-- (int);

    void clamp(bool avoid_eol);

    bool is_begin() const;
    bool is_end() const;
    bool is_valid() const;

    void on_insert(const BufferCoord& begin, const BufferCoord& end);
    void on_erase(const BufferCoord& begin, const BufferCoord& end);

    const Buffer& buffer() const;
    const BufferCoord& coord() const { return m_coord; }
    LineCount  line() const { return m_coord.line; }
    ByteCount column() const { return m_coord.column; }
    ByteCount offset() const;

private:
    safe_ptr<const Buffer> m_buffer;
    BufferCoord   m_coord;
};

class BufferChangeListener
{
public:
    virtual void on_insert(const BufferIterator& begin, const BufferIterator& end) = 0;
    virtual void on_erase(const BufferIterator& begin, const BufferIterator& end) = 0;
};

// A Buffer is a in-memory representation of a file
//
// The Buffer class permits to read and mutate this file
// representation. It also manage modifications undo/redo and
// provides tools to deal with the line/column nature of text.
class Buffer : public SafeCountable
{
public:
    enum class Flags
    {
        None = 0,
        File = 1,
        New  = 2,
        Fifo = 4,
        NoUndo = 8,
    };

    Buffer(String name, Flags flags, std::vector<String> lines = { "\n" });
    Buffer(const Buffer&) = delete;
    Buffer& operator= (const Buffer&) = delete;
    ~Buffer();

    Flags flags() const { return m_flags; }
    Flags& flags() { return m_flags; }

    void insert(BufferIterator pos, String content);
    void erase(BufferIterator begin, BufferIterator end);

    size_t         timestamp() const { return m_timestamp; }

    void           begin_undo_group();
    void           end_undo_group();
    bool           undo();
    bool           redo();

    String         string(const BufferIterator& begin,
                          const BufferIterator& end) const;

    BufferIterator begin() const;
    BufferIterator end() const;
    ByteCount      character_count() const;
    LineCount      line_count() const;
    ByteCount      line_length(LineCount line) const;
    const String&  line_content(LineCount line) const
    { return m_lines[line].content; }

    // returns an iterator at given coordinates. line_and_column is
    // clamped according to avoid_eol.
    BufferIterator iterator_at(const BufferCoord& line_and_column,
                               bool avoid_eol = false) const;

    // returns nearest valid coordinates from given ones
    // if avoid_eol, clamp to character before eol if line is not empty
    BufferCoord    clamp(const BufferCoord& line_and_column,
                         bool avoid_eol = false) const;

    // returns an iterator pointing to the first character of the line
    // iterator is on
    BufferIterator iterator_at_line_begin(const BufferIterator& iterator) const;
    // the same but taking a line number instead of an iterator
    BufferIterator iterator_at_line_begin(LineCount line) const;

    // returns an iterator pointing to the character after the last of the
    // line iterator is on (which is the first of the next line if iterator is
    // not on the last one)
    BufferIterator iterator_at_line_end(const BufferIterator& iterator) const;
    // the same but taking a line number instead of an iterator
    BufferIterator iterator_at_line_end(LineCount line) const;

    const String& name() const { return m_name; }

    // returns true if the buffer is in a different state than
    // the last time it was saved
    bool is_modified() const;

    // notify the buffer that it was saved in the current state
    void notify_saved();

    OptionManager&       options()       { return m_options; }
    const OptionManager& options() const { return m_options; }
    HookManager&         hooks()         { return m_hooks; }
    const HookManager&   hooks()   const { return m_hooks; }

    void add_change_listener(BufferChangeListener& listener) const;
    void remove_change_listener(BufferChangeListener& listener) const;

private:
    friend class BufferIterator;

    void check_invariant() const;

    struct Line
    {
        ByteCount start;
        String    content;

        ByteCount length() const { return content.length(); }
    };
    struct LineList : std::vector<Line>
    {
        Line& operator[](LineCount line)
        { return std::vector<Line>::operator[]((int)line); }

        const Line& operator[](LineCount line) const
        { return std::vector<Line>::operator[]((int)line); }
    };
    LineList m_lines;

    void do_insert(const BufferIterator& pos, const String& content);
    void do_erase(const BufferIterator& begin, const BufferIterator& end);

    String  m_name;
    Flags   m_flags;

    struct Modification;
    typedef std::vector<Modification> UndoGroup;

    std::vector<UndoGroup>           m_history;
    std::vector<UndoGroup>::iterator m_history_cursor;
    UndoGroup                        m_current_undo_group;

    void apply_modification(const Modification& modification);
    void revert_modification(const Modification& modification);

    size_t m_last_save_undo_index;
    size_t m_timestamp;

    // this is mutable as adding or removing listeners is not muting the
    // buffer observable state.
    mutable std::vector<BufferChangeListener*> m_change_listeners;

    OptionManager m_options;
    HookManager   m_hooks;
};

constexpr Buffer::Flags operator|(Buffer::Flags lhs, Buffer::Flags rhs)
{
    return (Buffer::Flags)((int) lhs | (int) rhs);
}

inline Buffer::Flags& operator|=(Buffer::Flags& lhs, Buffer::Flags rhs)
{
    (int&) lhs |= (int) rhs;
    return lhs;
}

constexpr bool operator&(Buffer::Flags lhs, Buffer::Flags rhs)
{
    return ((int) lhs & (int) rhs) != 0;
}

inline Buffer::Flags& operator&=(Buffer::Flags& lhs, Buffer::Flags rhs)
{
    (int&) lhs &= (int) rhs;
    return lhs;
}

constexpr Buffer::Flags operator~(Buffer::Flags lhs)
{
    return (Buffer::Flags)(~(int)lhs);
}


}

#include "buffer_iterator.inl.hh"

#endif // buffer_hh_INCLUDED