1 #include "display_buffer.hh"
2 
3 #include "assert.hh"
4 #include "buffer.hh"
5 #include "buffer_utils.hh"
6 #include "face_registry.hh"
7 #include "utf8.hh"
8 
9 #include "face_registry.hh"
10 
11 namespace Kakoune
12 {
13 
get_iterator(const Buffer & buffer,BufferCoord coord)14 BufferIterator get_iterator(const Buffer& buffer, BufferCoord coord)
15 {
16     // Correct one past the end of line as next line
17     if (not buffer.is_end(coord) and coord.column == buffer[coord.line].length())
18         coord = coord.line+1;
19     return buffer.iterator_at(coord);
20 }
21 
content() const22 StringView DisplayAtom::content() const
23 {
24     switch (m_type)
25     {
26         case Range:
27         {
28             auto line = (*m_buffer)[m_range.begin.line];
29             if (m_range.begin.line == m_range.end.line)
30                 return line.substr(m_range.begin.column, m_range.end.column - m_range.begin.column);
31             else if (m_range.begin.line+1 == m_range.end.line and m_range.end.column == 0)
32                 return line.substr(m_range.begin.column);
33             break;
34         }
35         case Text:
36         case ReplacedRange:
37             return m_text;
38     }
39     kak_assert(false);
40     return {};
41 }
42 
length() const43 ColumnCount DisplayAtom::length() const
44 {
45     switch (m_type)
46     {
47         case Range:
48             return utf8::column_distance(get_iterator(*m_buffer, m_range.begin),
49                                          get_iterator(*m_buffer, m_range.end));
50         case Text:
51         case ReplacedRange:
52             return m_text.column_length();
53     }
54     kak_assert(false);
55     return 0;
56 }
57 
trim_begin(ColumnCount count)58 void DisplayAtom::trim_begin(ColumnCount count)
59 {
60     if (m_type == Range)
61         m_range.begin = utf8::advance(get_iterator(*m_buffer, m_range.begin),
62                                       get_iterator(*m_buffer, m_range.end),
63                                       count).coord();
64     else
65         m_text = m_text.substr(count).str();
66 }
67 
trim_end(ColumnCount count)68 void DisplayAtom::trim_end(ColumnCount count)
69 {
70     if (m_type == Range)
71         m_range.end = utf8::advance(get_iterator(*m_buffer, m_range.end),
72                                     get_iterator(*m_buffer, m_range.begin),
73                                     -count).coord();
74     else
75         m_text = m_text.substr(0, m_text.column_length() - count).str();
76 }
77 
DisplayLine(AtomList atoms)78 DisplayLine::DisplayLine(AtomList atoms)
79     : m_atoms(std::move(atoms))
80 {
81     compute_range();
82 }
83 
split(iterator it,BufferCoord pos)84 DisplayLine::iterator DisplayLine::split(iterator it, BufferCoord pos)
85 {
86     kak_assert(it->type() == DisplayAtom::Range);
87     kak_assert(it->begin() < pos);
88     kak_assert(it->end() > pos);
89 
90     DisplayAtom atom = *it;
91     atom.m_range.end = pos;
92     it->m_range.begin = pos;
93     return m_atoms.insert(it, std::move(atom));
94 }
95 
split(iterator it,ColumnCount count)96 DisplayLine::iterator DisplayLine::split(iterator it, ColumnCount count)
97 {
98     kak_assert(count > 0);
99     kak_assert(count < it->length());
100 
101     if (it->type() == DisplayAtom::Text or it->type() == DisplayAtom::ReplacedRange)
102     {
103         DisplayAtom atom = *it;
104         atom.m_text = atom.m_text.substr(0, count).str();
105         it->m_text = it->m_text.substr(count).str();
106         return m_atoms.insert(it, std::move(atom));
107     }
108     auto pos = utf8::advance(get_iterator(it->buffer(), it->begin()),
109                              get_iterator(it->buffer(), it->end()),
110                              count).coord();
111     return split(it, pos);
112 }
113 
split(BufferCoord pos)114 DisplayLine::iterator DisplayLine::split(BufferCoord pos)
115 {
116     auto it = find_if(begin(), end(), [pos](const DisplayAtom& a) {
117         return (a.has_buffer_range() && a.begin() >= pos) ||
118                (a.type() == DisplayAtom::Range and a.end() > pos);
119     });
120     if (it == end() or it->begin() >= pos)
121         return it;
122     return ++split(it, pos);
123 }
124 
insert(iterator it,DisplayAtom atom)125 DisplayLine::iterator DisplayLine::insert(iterator it, DisplayAtom atom)
126 {
127     if (atom.has_buffer_range())
128     {
129         m_range.begin  = std::min(m_range.begin, atom.begin());
130         m_range.end = std::max(m_range.end, atom.end());
131     }
132     auto res = m_atoms.insert(it, std::move(atom));
133     compute_range();
134     return res;
135 }
136 
push_back(DisplayAtom atom)137 void DisplayLine::push_back(DisplayAtom atom)
138 {
139     if (atom.has_buffer_range())
140     {
141         m_range.begin  = std::min(m_range.begin, atom.begin());
142         m_range.end = std::max(m_range.end, atom.end());
143     }
144     m_atoms.push_back(std::move(atom));
145 }
146 
erase(iterator beg,iterator end)147 DisplayLine::iterator DisplayLine::erase(iterator beg, iterator end)
148 {
149     auto res = m_atoms.erase(beg, end);
150     compute_range();
151     return res;
152 }
153 
optimize()154 void DisplayLine::optimize()
155 {
156     if (m_atoms.empty())
157         return;
158 
159     auto atom_it = m_atoms.begin();
160     for (auto next_it = atom_it + 1; next_it != m_atoms.end(); ++next_it)
161     {
162         auto& atom = *atom_it;
163         auto& next = *next_it;
164 
165         const auto type = atom.type();
166         if (type == next.type() and atom.face == next.face)
167         {
168             if (type == DisplayAtom::Text)
169                 atom.m_text += next.m_text;
170             else if ((type == DisplayAtom::Range or
171                       type == DisplayAtom::ReplacedRange) and
172                      next.begin() == atom.end())
173             {
174                 atom.m_range.end = next.end();
175                 if (type == DisplayAtom::ReplacedRange)
176                     atom.m_text += next.m_text;
177             }
178             else
179                 *++atom_it = std::move(*next_it);
180         }
181         else
182             *++atom_it = std::move(*next_it);
183     }
184     m_atoms.erase(atom_it+1, m_atoms.end());
185 }
186 
length() const187 ColumnCount DisplayLine::length() const
188 {
189     ColumnCount len = 0;
190     for (auto& atom : m_atoms)
191         len += atom.length();
192     return len;
193 }
194 
trim(ColumnCount first_col,ColumnCount col_count)195 bool DisplayLine::trim(ColumnCount first_col, ColumnCount col_count)
196 {
197     for (auto it = begin(); first_col > 0 and it != end(); )
198     {
199         auto len = it->length();
200         if (len <= first_col)
201         {
202             m_atoms.erase(it);
203             first_col -= len;
204         }
205         else
206         {
207             it->trim_begin(first_col);
208             first_col = 0;
209         }
210     }
211     auto it = begin();
212     for (; it != end() and col_count > 0; ++it)
213         col_count -= it->length();
214 
215     bool did_trim = it != end() || col_count < 0;
216     if (col_count < 0)
217         (it-1)->trim_end(-col_count);
218     m_atoms.erase(it, end());
219 
220     compute_range();
221     return did_trim;
222 }
223 
224 const BufferRange init_range{ {INT_MAX, INT_MAX}, {INT_MIN, INT_MIN} };
225 
compute_range()226 void DisplayLine::compute_range()
227 {
228     m_range = init_range;
229     for (auto& atom : m_atoms)
230     {
231         if (not atom.has_buffer_range())
232             continue;
233         m_range.begin  = std::min(m_range.begin, atom.begin());
234         m_range.end = std::max(m_range.end, atom.end());
235     }
236     if (m_range == init_range)
237         m_range = { { 0, 0 }, { 0, 0 } };
238     kak_assert(m_range.begin <= m_range.end);
239 }
240 
compute_range()241 void DisplayBuffer::compute_range()
242 {
243     m_range = init_range;
244     for (auto& line : m_lines)
245     {
246         m_range.begin  = std::min(line.range().begin, m_range.begin);
247         m_range.end = std::max(line.range().end, m_range.end);
248     }
249     if (m_range == init_range)
250         m_range = { { 0, 0 }, { 0, 0 } };
251     kak_assert(m_range.begin <= m_range.end);
252 }
253 
optimize()254 void DisplayBuffer::optimize()
255 {
256     for (auto& line : m_lines)
257         line.optimize();
258 }
259 
parse_display_line(StringView line,Face & face,const FaceRegistry & faces,const HashMap<String,DisplayLine> & builtins)260 DisplayLine parse_display_line(StringView line, Face& face, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
261 {
262     DisplayLine res;
263     bool was_antislash = false;
264     auto pos = line.begin();
265     String content;
266     for (auto it = line.begin(), end = line.end(); it != end; ++it)
267     {
268         const char c = *it;
269         if (c == '{')
270         {
271             if (was_antislash)
272             {
273                 content += StringView{pos, it};
274                 content.back() = '{';
275                 pos = it + 1;
276             }
277             else
278             {
279                 content += StringView{pos, it};
280                 if (not content.empty())
281                     res.push_back({std::move(content), face});
282                 content.clear();
283                 auto closing = std::find(it+1, end, '}');
284                 if (closing == end)
285                     throw runtime_error("unclosed face definition");
286                 if (*(it+1) == '{' and closing+1 != end and *(closing+1) == '}')
287                 {
288                     auto builtin_it = builtins.find(StringView{it+2, closing});
289                     if (builtin_it == builtins.end())
290                         throw runtime_error(format("undefined atom {}", StringView{it+2, closing}));
291                     for (auto& atom : builtin_it->value)
292                         res.push_back(atom);
293                     // closing is now at the first char of "}}", advance it to the second
294                     ++closing;
295                 }
296                 else if (closing == it+2 and *(it+1) == '\\')
297                 {
298                     pos = closing + 1;
299                     break;
300                 }
301                 else
302                     face = faces[{it+1, closing}];
303                 it = closing;
304                 pos = closing + 1;
305             }
306         }
307         if (c == '\n' or c == '\t') // line breaks and tabs are forbidden, replace with space
308         {
309             content += StringView{pos, it+1};
310             content.back() = ' ';
311             pos = it + 1;
312         }
313 
314         if (c == '\\')
315         {
316             if (was_antislash)
317             {
318                 content += StringView{pos, it};
319                 pos = it + 1;
320                 was_antislash = false;
321             }
322             else
323                 was_antislash = true;
324         }
325         else
326             was_antislash = false;
327     }
328     content += StringView{pos, line.end()};
329     if (not content.empty())
330         res.push_back({std::move(content), face});
331     return res;
332 }
333 
parse_display_line(StringView line,const FaceRegistry & faces,const HashMap<String,DisplayLine> & builtins)334 DisplayLine parse_display_line(StringView line, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
335 {
336     Face face{};
337     return parse_display_line(line, face, faces, builtins);
338 }
339 
parse_display_line_list(StringView content,const FaceRegistry & faces,const HashMap<String,DisplayLine> & builtins)340 DisplayLineList parse_display_line_list(StringView content, const FaceRegistry& faces, const HashMap<String, DisplayLine>& builtins)
341 {
342     return content | split<StringView>('\n')
343                    | transform([&, face=Face{}](StringView s) mutable {
344                          return parse_display_line(s, face, faces, builtins);
345                      })
346                    | gather<DisplayLineList>();
347 }
348 
349 }
350