1 #ifndef buffer_hh_INCLUDED
2 #define buffer_hh_INCLUDED
3
4 #include "clock.hh"
5 #include "coord.hh"
6 #include "constexpr_utils.hh"
7 #include "enum.hh"
8 #include "file.hh"
9 #include "optional.hh"
10 #include "range.hh"
11 #include "safe_ptr.hh"
12 #include "scope.hh"
13 #include "shared_string.hh"
14 #include "value.hh"
15 #include "vector.hh"
16
17 #include <sys/types.h>
18 #include <ctime>
19
20 namespace Kakoune
21 {
22
23 enum class EolFormat
24 {
25 Lf,
26 Crlf
27 };
28
enum_desc(Meta::Type<EolFormat>)29 constexpr auto enum_desc(Meta::Type<EolFormat>)
30 {
31 return make_array<EnumDesc<EolFormat>>({
32 { EolFormat::Lf, "lf" },
33 { EolFormat::Crlf, "crlf" },
34 });
35 }
36
37 enum class ByteOrderMark
38 {
39 None,
40 Utf8
41 };
42
enum_desc(Meta::Type<ByteOrderMark>)43 constexpr auto enum_desc(Meta::Type<ByteOrderMark>)
44 {
45 return make_array<EnumDesc<ByteOrderMark>>({
46 { ByteOrderMark::None, "none" },
47 { ByteOrderMark::Utf8, "utf8" },
48 });
49 }
50
51 class Buffer;
52
53 constexpr timespec InvalidTime = { -1, -1 };
54
55 // A BufferIterator permits to iterate over the characters of a buffer
56 class BufferIterator
57 {
58 public:
59 using value_type = char;
60 using difference_type = ssize_t;
61 using pointer = const value_type*;
62 using reference = const value_type&;
63 // computing the distance between two iterator can be
64 // costly, so this is not strictly random access
65 using iterator_category = std::bidirectional_iterator_tag;
66
BufferIterator()67 BufferIterator() noexcept : m_buffer{nullptr}, m_line{} {}
68 BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept;
69
70 bool operator== (const BufferIterator& iterator) const noexcept;
71 bool operator!= (const BufferIterator& iterator) const noexcept;
72 bool operator< (const BufferIterator& iterator) const noexcept;
73 bool operator<= (const BufferIterator& iterator) const noexcept;
74 bool operator> (const BufferIterator& iterator) const noexcept;
75 bool operator>= (const BufferIterator& iterator) const noexcept;
76
77 bool operator== (const BufferCoord& coord) const noexcept;
78 bool operator!= (const BufferCoord& coord) const noexcept;
79
80 const char& operator* () const noexcept;
81 const char& operator[](size_t n) const noexcept;
82 size_t operator- (const BufferIterator& iterator) const;
83
84 BufferIterator operator+ (ByteCount size) const;
85 BufferIterator operator- (ByteCount size) const;
86
87 BufferIterator& operator+= (ByteCount size);
88 BufferIterator& operator-= (ByteCount size);
89
90 BufferIterator& operator++ ();
91 BufferIterator& operator-- ();
92
93 BufferIterator operator++ (int);
94 BufferIterator operator-- (int);
95
coord() const96 const BufferCoord& coord() const noexcept { return m_coord; }
operator BufferCoord() const97 explicit operator BufferCoord() const noexcept { return m_coord; }
98 using Sentinel = BufferCoord;
99
100 private:
101 SafePtr<const Buffer> m_buffer;
102 BufferCoord m_coord;
103 StringView m_line;
104 };
105
106 using BufferLines = Vector<StringDataPtr, MemoryDomain::BufferContent>;
107 using BufferRange = Range<BufferCoord>;
108
109 // A Buffer is a in-memory representation of a file
110 //
111 // The Buffer class permits to read and mutate this file
112 // representation. It also manage modifications undo/redo and
113 // provides tools to deal with the line/column nature of text.
114 class Buffer final : public SafeCountable, public Scope, private OptionManagerWatcher
115 {
116 public:
117 enum class Flags
118 {
119 None = 0,
120 File = 1 << 0,
121 New = 1 << 1,
122 Fifo = 1 << 2,
123 NoUndo = 1 << 3,
124 NoHooks = 1 << 4,
125 Debug = 1 << 5,
126 ReadOnly = 1 << 6,
127 };
with_bit_ops(Meta::Type<Flags>)128 friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
129
130 enum class HistoryId : size_t { First = 0, Invalid = (size_t)-1 };
131
132 Buffer(String name, Flags flags, BufferLines lines,
133 ByteOrderMark bom = ByteOrderMark::None,
134 EolFormat eolformat = EolFormat::Lf,
135 FsStatus fs_status = {InvalidTime, {}, {}});
136 Buffer(const Buffer&) = delete;
137 Buffer& operator= (const Buffer&) = delete;
138 ~Buffer();
139
flags() const140 Flags flags() const { return m_flags; }
flags()141 Flags& flags() { return m_flags; }
142
143 bool set_name(String name);
144 void update_display_name();
145
146 BufferRange insert(BufferCoord pos, StringView content);
147 BufferCoord erase(BufferCoord begin, BufferCoord end);
148 BufferRange replace(BufferCoord begin, BufferCoord end, StringView content);
149
150 size_t timestamp() const;
151 void set_fs_status(FsStatus);
152 const FsStatus& fs_status() const;
153
154 void commit_undo_group();
155 bool undo(size_t count = 1);
156 bool redo(size_t count = 1);
157 bool move_to(HistoryId id);
current_history_id() const158 HistoryId current_history_id() const noexcept { return m_history_id; }
next_history_id() const159 HistoryId next_history_id() const noexcept { return (HistoryId)m_history.size(); }
160
161 String string(BufferCoord begin, BufferCoord end) const;
162 StringView substr(BufferCoord begin, BufferCoord end) const;
163
164 const char& byte_at(BufferCoord c) const;
165 ByteCount distance(BufferCoord begin, BufferCoord end) const;
166 BufferCoord advance(BufferCoord coord, ByteCount count) const;
167 BufferCoord next(BufferCoord coord) const;
168 BufferCoord prev(BufferCoord coord) const;
169
170 BufferCoord char_next(BufferCoord coord) const;
171 BufferCoord char_prev(BufferCoord coord) const;
172
173 BufferCoord back_coord() const;
174 BufferCoord end_coord() const;
175
176 bool is_valid(BufferCoord c) const;
177 bool is_end(BufferCoord c) const;
178
179 BufferIterator begin() const;
180 BufferIterator end() const;
181 LineCount line_count() const;
182
183 Optional<BufferCoord> last_modification_coord() const;
184
operator [](LineCount line) const185 StringView operator[](LineCount line) const
186 { return m_lines[line]; }
187
line_storage(LineCount line) const188 const StringDataPtr& line_storage(LineCount line) const
189 { return m_lines.get_storage(line); }
190
191 // returns an iterator at given coordinates. clamp line_and_column
192 BufferIterator iterator_at(BufferCoord coord) const;
193
194 // returns nearest valid coordinates from given ones
195 BufferCoord clamp(BufferCoord coord) const;
196
197 BufferCoord offset_coord(BufferCoord coord, CharCount offset, ColumnCount, bool) const;
198 BufferCoordAndTarget offset_coord(BufferCoordAndTarget coord, LineCount offset, ColumnCount tabstop, bool avoid_eol) const;
199
name() const200 const String& name() const { return m_name; }
display_name() const201 const String& display_name() const { return m_display_name; }
202
203 // returns true if the buffer is in a different state than
204 // the last time it was saved
205 bool is_modified() const;
206
207 // notify the buffer that it was saved in the current state
208 void notify_saved(FsStatus status);
209
values() const210 ValueMap& values() const { return m_values; }
211
212 void run_hook_in_own_context(Hook hook, StringView param,
213 String client_name = {});
214
215 void reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, FsStatus status);
216
217 void check_invariant() const;
218
219 struct Change
220 {
221 enum Type : char { Insert, Erase };
222 Type type;
223 BufferCoord begin;
224 BufferCoord end;
225 };
226 ConstArrayView<Change> changes_since(size_t timestamp) const;
227
228 String debug_description() const;
229
230 // Methods called by the buffer manager
231 void on_registered();
232 void on_unregistered();
233
234 void throw_if_read_only() const;
235
236 // A Modification holds a single atomic modification to Buffer
237 struct Modification
238 {
239 enum Type { Insert, Erase };
240
241 Type type;
242 BufferCoord coord;
243 StringDataPtr content;
244
245 Modification inverse() const;
246 };
247
248 using UndoGroup = Vector<Modification, MemoryDomain::BufferMeta>;
249
250 struct HistoryNode : UseMemoryDomain<MemoryDomain::BufferMeta>
251 {
252 HistoryNode(HistoryId parent);
253
254 HistoryId parent;
255 HistoryId redo_child = HistoryId::Invalid;
256 TimePoint committed;
257 UndoGroup undo_group;
258 };
259
history() const260 const Vector<HistoryNode>& history() const { return m_history; }
current_undo_group() const261 const UndoGroup& current_undo_group() const { return m_current_undo_group; }
262
263 private:
264 void on_option_changed(const Option& option) override;
265
266 BufferRange do_insert(BufferCoord pos, StringView content);
267 BufferCoord do_erase(BufferCoord begin, BufferCoord end);
268
269 void apply_modification(const Modification& modification);
270 void revert_modification(const Modification& modification);
271
272 struct LineList : BufferLines
273 {
274 [[gnu::always_inline]]
get_storageKakoune::Buffer::LineList275 StringDataPtr& get_storage(LineCount line)
276 { return BufferLines::operator[]((int)line); }
277
278 [[gnu::always_inline]]
get_storageKakoune::Buffer::LineList279 const StringDataPtr& get_storage(LineCount line) const
280 { return BufferLines::operator[]((int)line); }
281
282 [[gnu::always_inline]]
operator []Kakoune::Buffer::LineList283 StringView operator[](LineCount line) const
284 { return get_storage(line)->strview(); }
285
frontKakoune::Buffer::LineList286 StringView front() const { return BufferLines::front()->strview(); }
backKakoune::Buffer::LineList287 StringView back() const { return BufferLines::back()->strview(); }
288 };
289 LineList m_lines;
290
291 String m_name;
292 String m_display_name;
293 Flags m_flags;
294
295 Vector<HistoryNode> m_history;
296 HistoryId m_history_id = HistoryId::Invalid;
297 HistoryId m_last_save_history_id = HistoryId::Invalid;
298 UndoGroup m_current_undo_group;
299
history_node(HistoryId id)300 HistoryNode& history_node(HistoryId id) { return m_history[(size_t)id]; }
history_node(HistoryId id) const301 const HistoryNode& history_node(HistoryId id) const { return m_history[(size_t)id]; }
current_history_node()302 HistoryNode& current_history_node() { return m_history[(size_t)m_history_id]; }
current_history_node() const303 const HistoryNode& current_history_node() const { return m_history[(size_t)m_history_id]; }
304
305 Vector<Change, MemoryDomain::BufferMeta> m_changes;
306
307 FsStatus m_fs_status;
308
309 // Values are just data holding by the buffer, they are not part of its
310 // observable state
311 mutable ValueMap m_values;
312 };
313
314 }
315
316 #include "buffer.inl.hh"
317
318 #endif // buffer_hh_INCLUDED
319