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