Cthulhu  0.2.10
Cthulhu compiler collection
text_rich.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "common_extra.h"
4 
5 #include "format/notify.h"
6 
7 #include "memory/memory.h"
8 
9 #include "scan/node.h"
10 
11 #include "io/io.h"
12 
13 #include "std/set.h"
14 #include "std/str.h"
15 #include "std/typed/vector.h"
16 #include "std/vector.h"
17 
18 #include "arena/arena.h"
19 
20 #include "base/util.h"
21 #include "base/panic.h"
22 
23 #include "core/macros.h"
24 
25 #include <ctype.h>
26 
27 #define COLOUR_PATH eColourBlue
28 #define COLOUR_UNDERLINE eColourMagenta
29 #define COLOUR_SEGMENT eColourGreen
30 #define COLOUR_NOTE_STAR eColourYellow
31 #define COLOUR_MESSAGE eColourRed
32 
33 typedef struct rich_t
34 {
36 
38  const event_t *event;
39 
41 
42  // the largest line number in the report
43  size_t largest_line;
44 
46 
47  size_t max_columns;
48 
50 } rich_t;
51 
52 static void print_report_header(rich_t *rich, const char *message)
53 {
54  CTASSERT(rich != NULL);
55  CTASSERT(message != NULL);
56 
57  const diagnostic_t *diag = rich->event->diagnostic;
58  text_config_t config = rich->config;
59 
60  const char *sev = get_severity_name(rich->severity);
61  colour_t colour = get_severity_colour(rich->severity);
62 
63  char *coloured = colour_format(rich->fmt, colour, "%s [%s]:", sev, diag->id);
64 
65  io_printf(config.io, "%s %s\n", coloured, message);
66 }
67 
68 static where_t get_first_line(const typevec_t *segments)
69 {
70  CTASSERT(segments != NULL);
71 
72  size_t len = typevec_len(segments);
73  size_t first_line = SIZE_MAX;
74 
75  for (size_t i = 0; i < len; i++)
76  {
77  const segment_t *segment = typevec_offset(segments, i);
78  CTASSERT(segment != NULL);
79 
80  if (!node_has_line(&segment->node))
81  continue;
82 
83  where_t where = node_get_location(&segment->node);
84  first_line = CT_MIN(first_line, where.first_line);
85  break;
86  }
87 
88  where_t where = {
89  .first_line = first_line,
90  .first_column = 0,
91  .last_line = first_line,
92  .last_column = 0,
93  };
94 
95  return where;
96 }
97 
98 static void print_scan_header(rich_t *rich, size_t largest, size_t line, const scan_t *scan)
99 {
100  CTASSERT(rich != NULL);
101  CTASSERT(scan != NULL);
102 
103  text_config_t config = rich->config;
104 
105  const char *name = scan_path(scan);
106  const char *lang = scan_language(scan);
107  char *padding = str_repeat(" ", get_num_width(largest), rich->arena);
108 
109  if (scan_is_builtin(scan))
110  {
111  char *coloured = colour_format(rich->fmt, COLOUR_PATH, "<%s>", name);
112 
113  io_printf(config.io, " %s => %s\n", padding, coloured);
114  }
115  else
116  {
117  size_t display_line = get_offset_line(config.config.zeroth_line, line);
118 
119  io_printf(config.io, " %s => %s [%s:%zu]\n", padding, lang, name, display_line);
120  }
121 }
122 
123 static void print_file_header(rich_t *rich, const node_t *node)
124 {
125  CTASSERT(rich != NULL);
126  CTASSERT(node != NULL);
127 
128  const scan_t *scan = node_get_scan(node);
129  text_config_t config = rich->config;
130  int width = get_num_width(rich->largest_line);
131  size_t line = get_line_number(config.config, node);
132 
133  if (node_has_line(node))
134  {
135  const char *name = scan_path(scan);
136  const char *lang = scan_language(scan);
137 
138  char *padding = str_repeat(" ", width, rich->arena);
139 
140  io_printf(config.io, " %s => %s [%s:%zu]\n", padding, lang, name, line);
141  }
142  else
143  {
144  print_scan_header(rich, width, 0, scan);
145  }
146 }
147 
148 static char *fmt_underline(text_cache_t *cache, const node_t *node, size_t limit, arena_t *arena)
149 {
150  CTASSERT(cache != NULL);
151 
152  where_t where = node_get_location(node);
153 
154  text_view_t view = cache_get_line(cache, where.first_line);
155  size_t width = CT_MIN(view.length, limit);
156 
157  typevec_t *padding = typevec_new(sizeof(char), width, arena);
158  for (size_t i = 0; i < width; i++)
159  {
160  if (i >= where.first_column) break;
161 
162  char c = view.text[i];
163  typevec_push(padding, c == '\t' ? "\t" : " ");
164  }
165  typevec_push(padding, "\0");
166 
167  size_t underline_width = width;
168  if (where.first_line == where.last_line)
169  {
170  underline_width = CT_MIN(where.last_column - where.first_column, 1);
171  }
172 
173  const char *lines = (underline_width > 1) ? str_repeat("~", underline_width - 1, arena) : "";
174  char *underline = str_format(arena, "%s^%s", (char*)typevec_data(padding), lines);
175 
176  return underline;
177 }
178 
179 static void print_file_segment(rich_t *rich, const node_t *node, const char *message)
180 {
181  CTASSERT(rich != NULL);
182  CTASSERT(node != NULL);
183  CTASSERT(message != NULL);
184 
185  text_config_t config = rich->config;
186 
187  const scan_t *scan = node_get_scan(node);
188  where_t where = node_get_location(node);
189  size_t data_line = where.first_line;
190 
191  size_t display_line = get_line_number(config.config, node);
192  int width = get_num_width(CT_MAX(display_line, rich->largest_line));
193  char *padding = str_repeat(" ", width, rich->arena);
194  char *line = fmt_left_align(rich->arena, width, "%zu", display_line);
195 
196  text_cache_t *file = cache_emplace_scan(rich->file_cache, scan);
197  text_t source = cache_escape_line(file, data_line, config.colours, rich->max_columns);
198 
199  // get the first line of the message
200  vector_t *lines = str_split(message, "\n", rich->arena);
201  char *first = vector_get(lines, 0);
202 
203  if (vector_len(lines) > 1)
204  {
205  char *one = colour_text(rich->fmt, COLOUR_SEGMENT, "(1)");
206  first = str_format(rich->arena, "%s %s", one, first);
207  }
208 
209  char *underline = fmt_underline(file, node, rich->max_columns, rich->arena);
210  char *coloured_underline = colour_text(rich->fmt, COLOUR_UNDERLINE, underline);
211 
212  const char *pretext = !isspace(source.text[0]) ? " " : "";
213 
214  io_printf(config.io, " %s |\n", padding);
215  io_printf(config.io, " %s |%s%.*s\n", line, pretext, (int)source.length, source.text);
216  io_printf(config.io, " %s |%s%s %s.\n", padding, pretext, coloured_underline, first);
217 
218  size_t extra_padding = ctu_strlen(underline);
219  char *extra = str_repeat(" ", extra_padding, rich->arena);
220 
221  size_t len = vector_len(lines);
222  int align = get_num_width(len);
223 
224  for (size_t i = 1; i < len; i++)
225  {
226  const char *it = vector_get(lines, i);
227  char *aligned = fmt_left_align(rich->arena, align, "(%zu)", i + 1);
228  char *coloured = colour_text(rich->fmt, COLOUR_SEGMENT, aligned);
229  io_printf(config.io, " %s |%s%s %s %s.\n", padding, pretext, extra, coloured, it);
230  }
231 }
232 
233 static void print_segment_message(rich_t *rich, const char *message)
234 {
235  CTASSERT(rich != NULL);
236  CTASSERT(message != NULL);
237 
238  text_config_t config = rich->config;
239 
240  vector_t *lines = str_split(message, "\n", rich->arena);
241  size_t len = vector_len(lines);
242 
243  for (size_t i = 0; i < len; i++)
244  {
245  char *it = vector_get(lines, i);
246  char *coloured = colour_format(rich->fmt, COLOUR_SEGMENT, "(%zu)", i + 1);
247  io_printf(config.io, " %s %s\n", coloured, it);
248  }
249 }
250 
251 static void print_file_segments(rich_t *rich, const typevec_t *segments)
252 {
253  CTASSERT(rich != NULL);
254  CTASSERT(segments != NULL);
255 
256  size_t len = typevec_len(segments);
257  for (size_t i = 0; i < len; i++)
258  {
259  const segment_t *segment = typevec_offset(segments, i);
260  CTASSERT(segment != NULL);
261 
262  if (node_has_line(&segment->node))
263  {
264  print_file_segment(rich, &segment->node, segment->message);
265  }
266  else
267  {
268  // just print the message
269  print_segment_message(rich, segment->message);
270  }
271  }
272 }
273 
274 static bool nodes_overlap(const node_t *lhs, const node_t *rhs)
275 {
276  CTASSERT(lhs != NULL);
277  CTASSERT(rhs != NULL);
278 
279  where_t lhs_where = node_get_location(lhs);
280  where_t rhs_where = node_get_location(rhs);
281 
282  // if the elements overlap at all then they are considered to be the same
283 
284  if (lhs_where.first_line <= rhs_where.first_line &&
285  lhs_where.last_line >= rhs_where.first_line)
286  {
287  return true;
288  }
289 
290  if (lhs_where.first_line <= rhs_where.last_line &&
291  lhs_where.last_line >= rhs_where.last_line)
292  {
293  return true;
294  }
295 
296  return false;
297 }
298 
299 static typevec_t *collect_segments(rich_t *rich, const typevec_t *all, const scan_t *scan)
300 {
301  CTASSERT(rich != NULL);
302  CTASSERT(scan != NULL);
303 
304  if (all == NULL)
305  return typevec_new(sizeof(segment_t), 0, rich->arena);
306 
307  size_t count = typevec_len(all);
308  typevec_t *primary = typevec_new(sizeof(segment_t), count, rich->arena);
309  for (size_t i = 0; i < count; i++)
310  {
311  const segment_t *segment = typevec_offset(all, i);
312  CTASSERT(segment != NULL);
313 
314  const node_t *other = &segment->node;
315  if (node_get_scan(other) != scan)
316  continue;
317 
318  if (node_has_line(other))
319  {
320  where_t where = node_get_location(other);
321  rich->largest_line = CT_MAX(rich->largest_line, where.first_line);
322  }
323 
324  typevec_push(primary, segment);
325  }
326 
327  return primary;
328 }
329 
330 typedef struct join_result_t
331 {
334 } join_result_t;
335 
336 static join_result_t join_node_messages(rich_t *rich, const segment_t *segment, segment_t *other, const char *primary)
337 {
338  CTASSERT(rich != NULL);
339  CTASSERT(segment != NULL);
340  CTASSERT(other != NULL);
341 
342  if (!nodes_overlap(&segment->node, &other->node))
343  {
344  join_result_t result = {
345  .joined_nodes = false,
346  .used_primary = false,
347  };
348 
349  return result;
350  }
351 
352  join_result_t result = {
353  .joined_nodes = true,
354  .used_primary = false,
355  };
356 
357  if (segment->message == primary)
358  {
359  result.used_primary = true;
360 
361  char *coloured = colour_text(rich->fmt, COLOUR_MESSAGE, segment->message);
362  other->message = str_format(rich->arena, "%s\n%s", coloured, other->message);
363  }
364  else if (other->message == primary)
365  {
366  result.used_primary = true;
367 
368  char *coloured = colour_text(rich->fmt, COLOUR_MESSAGE, other->message);
369  other->message = str_format(rich->arena, "%s\n%s", segment->message, coloured);
370  }
371  else
372  {
373  other->message = str_format(rich->arena, "%s\n%s", segment->message, other->message);
374  }
375 
376  return result;
377 }
378 
379 static typevec_t *merge_segments(rich_t *rich, const typevec_t *segments, const char *primary)
380 {
381  CTASSERT(rich != NULL);
382  CTASSERT(segments != NULL);
383 
384  // now merge segments that share the same span
385  size_t len = typevec_len(segments);
386  typevec_t *result = typevec_new(sizeof(segment_t), len, rich->arena);
387  for (size_t i = 0; i < len; i++)
388  {
389  const segment_t *segment = typevec_offset(segments, i);
390  CTASSERT(segment != NULL);
391 
392  bool found = false;
393  for (size_t j = 0; j < typevec_len(result); j++)
394  {
395  segment_t *other = typevec_offset(result, j);
396  CTASSERT(other != NULL);
397 
398  join_result_t join = join_node_messages(rich, segment, other, primary);
399 
400  if (join.joined_nodes)
401  {
402  if (join.used_primary)
403  {
404  primary = NULL;
405  }
406 
407  found = true;
408  break;
409  }
410  }
411 
412  if (!found)
413  {
414  typevec_push(result, segment);
415  }
416  }
417 
418  return result;
419 }
420 
421 static typevec_t *collect_primary_segments(rich_t *rich, const typevec_t *all, const event_t *event)
422 {
423  CTASSERT(rich != NULL);
424  CTASSERT(event != NULL);
425 
426  const scan_t *scan = node_get_scan(&event->node);
427  typevec_t *primary = collect_segments(rich, all, scan);
428 
429  segment_t event_segment = {
430  .node = event->node,
431  .message = event->message,
432  };
433 
434  typevec_push(primary, &event_segment);
435 
436  // now merge segments that share the same span
437  typevec_t *result = merge_segments(rich, primary, event->message);
438 
439  return result;
440 }
441 
442 static size_t longest_segment_line(const typevec_t *segments)
443 {
444  CTASSERT(segments != NULL);
445 
446  size_t len = typevec_len(segments);
447  size_t longest = 0;
448 
449  for (size_t i = 0; i < len; i++)
450  {
451  const segment_t *segment = typevec_offset(segments, i);
452  CTASSERT(segment != NULL);
453 
454  if (!node_has_line(&segment->node))
455  continue;
456 
457  where_t where = node_get_location(&segment->node);
458  longest = CT_MAX(longest, where.first_line);
459  }
460 
461  return longest;
462 }
463 
464 static void print_extra_files(rich_t *rich)
465 {
466  CTASSERT(rich != NULL);
467  const event_t *event = rich->event;
468 
469  if (event->segments == NULL)
470  return;
471 
472  // find all remaining files
473  set_t *scans = set_new(10, kTypeInfoPtr, rich->arena);
474  size_t len = typevec_len(event->segments);
475  for (size_t i = 0; i < len; i++)
476  {
477  const segment_t *segment = typevec_offset(event->segments, i);
478  CTASSERT(segment != NULL);
479 
480  const scan_t *scan = node_get_scan(&segment->node);
481  set_add(scans, scan);
482  }
483 
484  const scan_t *root = node_get_scan(&event->node);
485 
486  set_iter_t iter = set_iter(scans);
487  while (set_has_next(&iter))
488  {
489  const scan_t *scan = set_next(&iter);
490  CTASSERT(scan != NULL);
491 
492  if (scan == root)
493  continue;
494 
495  typevec_t *all = collect_segments(rich, event->segments, scan);
496  typevec_t *merged = merge_segments(rich, all, NULL);
497 
498  size_t largest_line = longest_segment_line(merged);
499  where_t where = get_first_line(merged);
500 
501  print_scan_header(rich, largest_line, where.first_line, scan);
502  print_file_segments(rich, merged);
503  }
504 }
505 
506 static void print_rich_notes(rich_t *rich)
507 {
508  CTASSERT(rich != NULL);
509 
510  const event_t *event = rich->event;
511  if (event->notes == NULL)
512  return;
513 
514  text_config_t config = rich->config;
515 
516  char *coloured = colour_text(rich->fmt, COLOUR_NOTE_STAR, "*");
517  size_t len = vector_len(event->notes);
518  for (size_t i = 0; i < len; i++)
519  {
520  const char *note = vector_get(event->notes, i);
521  CTASSERT(note != NULL);
522 
523  io_printf(config.io, " %s %s\n", coloured, note);
524  }
525 }
526 
527 STA_DECL
528 void text_report_rich(text_config_t config, const event_t *event)
529 {
530  CTASSERT(config.io != NULL);
531  CTASSERT(event != NULL);
532 
533  file_config_t info = config.config;
534 
535  arena_t *arena = get_global_arena();
536 
538  .pallete = config.colours,
539  .arena = arena,
540  };
541 
542  rich_t ctx = {
543  .arena = arena,
544  .config = config,
545  .event = event,
546  .severity = get_severity(event->diagnostic, info.override_fatal),
547  .file_cache = config.cache != NULL ? config.cache : cache_map_new(4, arena),
548  .max_columns = info.max_columns == 0 ? 240 : info.max_columns,
549  .fmt = fmt
550  };
551 
552  print_report_header(&ctx, event->message);
553 
554  typevec_t *primary = collect_primary_segments(&ctx, event->segments, event);
555  size_t count = typevec_len(primary);
556  CT_UNUSED(count);
557 
558  print_file_header(&ctx, &event->node);
559  print_file_segments(&ctx, primary);
560 
561  print_extra_files(&ctx);
562 
563  print_rich_notes(&ctx);
564 
565  if (config.cache == NULL)
567 }
STA_DECL char * colour_format(format_context_t context, colour_t idx, const char *fmt,...)
Definition: colour.c:65
CT_NODISCARD CT_PUREFN CT_SCAN_API const char * scan_path(const scan_t *scan)
get the path of a scanner
Definition: scan.c:56
CT_NODISCARD CT_PUREFN CT_SCAN_API const char * scan_language(const scan_t *scan)
get the source language of a scanner
Definition: scan.c:48
CT_NODISCARD CT_PUREFN CT_SCAN_API bool scan_is_builtin(const scan_t *scan)
check if a scanner is the builtin scanner
Definition: scan.c:41
colour_t get_severity_colour(severity_t severity)
Definition: common_extra.c:52
bool node_has_line(const node_t *node)
Definition: common_extra.c:129
void cache_map_delete(cache_map_t *map)
Definition: common_extra.c:262
const char * get_severity_name(severity_t severity)
Definition: common_extra.c:38
text_cache_t * cache_emplace_scan(cache_map_t *map, const scan_t *scan)
Definition: common_extra.c:295
size_t get_line_number(file_config_t config, const node_t *node)
Definition: common_extra.c:121
severity_t get_severity(const diagnostic_t *diag, bool override_fatal)
Definition: common_extra.c:30
text_t cache_escape_line(text_cache_t *cache, size_t line, const colour_pallete_t *colours, size_t column_limit)
Definition: common_extra.c:355
text_view_t cache_get_line(text_cache_t *cache, size_t line)
Definition: common_extra.c:310
cache_map_t * cache_map_new(size_t size, arena_t *arena)
Definition: common_extra.c:251
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
CT_NODISCARD CT_PUREFN CT_BASE_API size_t ctu_strlen(const char *str)
get the length of a string not including the null terminator equivalent to strlen but with safety che...
Definition: util.c:87
colour_t
a colour code
Definition: colour.h:23
STA_RET_STRING CT_FORMAT_API char * colour_text(format_context_t context, colour_t idx, const char *text)
add colour to a string
Definition: colour.c:56
CT_MEMORY_API arena_t * get_global_arena(void)
get the global memory arena
Definition: memory.c:16
CT_NODISCARD CT_STD_API set_t * set_new(size_t size, hash_info_t info, arena_t *arena)
create a new set
Definition: set.c:51
CT_STD_API const void * set_add(set_t *set, const void *key)
add a key to a set
Definition: set.c:85
CT_NODISCARD CT_PUREFN CT_STD_API set_iter_t set_iter(set_t *set)
acquire a set iterator for a set
Definition: set.c:268
CT_NODISCARD CT_STD_API const void * set_next(set_iter_t *iter)
get the next item from a set iterator
Definition: set.c:288
CT_NODISCARD CT_PUREFN CT_STD_API bool set_has_next(set_iter_t *iter)
check if a set iterator has more items
Definition: set.c:301
CT_IO_API size_t io_printf(io_t *io, STA_FORMAT_STRING const char *fmt,...)
printf to an io object
CT_NODISCARD CT_PUREFN CT_SCAN_API const scan_t * node_get_scan(const node_t *node)
get the associated source file of a node
Definition: node.c:57
CT_NODISCARD CT_PUREFN CT_SCAN_API where_t node_get_location(const node_t *node)
get the location of a node inside its source file
Definition: node.c:65
#define CT_MIN(L, R)
Definition: macros.h:38
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
#define CT_MAX(L, R)
Definition: macros.h:34
severity_t
the default severity of a diagnostic
Definition: diagnostic.h:18
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
CT_NODISCARD CT_STD_API char * str_repeat(const char *str, size_t times, arena_t *arena)
repeat a string
Definition: str.c:336
CT_NODISCARD CT_STD_API vector_t * str_split(const char *str, const char *sep, arena_t *arena)
split a string into a vector by a separator
Definition: str.c:557
CT_NODISCARD STA_FORMAT_STRING const char * fmt
Definition: str.h:68
CT_STD_API const hash_info_t kTypeInfoPtr
type information for a generic pointer this operates on the pointer itself and not the data it points...
Definition: typeinfo.c:41
CT_STD_API void * typevec_push(typevec_t *vec, const void *src)
push a value onto the vector
Definition: vector.c:156
CT_NODISCARD CT_PUREFN CT_STD_API size_t typevec_len(const typevec_t *vec)
get the length of a vector
Definition: vector.c:120
CT_NODISCARD CT_PUREFN CT_STD_API void * typevec_data(const typevec_t *vec)
get a pointer to the underlying data
Definition: vector.c:199
CT_NODISCARD CT_STD_API typevec_t * typevec_new(size_t width, size_t len, arena_t *arena)
create a new typed vector on the heap
Definition: vector.c:77
CT_NODISCARD CT_PUREFN CT_STD_API void * typevec_offset(const typevec_t *vec, size_t index)
get a pointer to the value at the given index
Definition: vector.c:191
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
STA_DECL char * str_format(arena_t *arena, const char *fmt,...)
Definition: str.c:97
an allocator object
Definition: arena.h:86
a diagnostic
Definition: diagnostic.h:27
const char * id
the id of the diagnostic should be in the format [A-Z]{2,3}[0-9]{4} e.g. CLI0001 this is not enforced...
Definition: diagnostic.h:34
an event handle TODO: make this and segment_t opaque
Definition: notify.h:36
node_t node
the primary node that this event is attached to
Definition: notify.h:41
const diagnostic_t * diagnostic
the related diagnostic
Definition: notify.h:38
STA_FIELD_STRING char * message
the primary message
Definition: notify.h:44
typevec_t * segments
extra segments that this event is attached to
Definition: notify.h:48
the configuration for a file
Definition: notify.h:49
bool override_fatal
if true all warnings are treated as fatal
Definition: notify.h:58
size_t max_columns
the maximum number of columns to print set to 0 to use default
Definition: notify.h:55
bool zeroth_line
the zeroth line of a file is the first line
Definition: notify.h:51
a formatting context when using colours
Definition: colour.h:46
bool joined_nodes
Definition: text_rich.c:332
bool used_primary
Definition: text_rich.c:333
a position in a source file
Definition: node.h:23
size_t max_columns
Definition: text_rich.c:47
format_context_t fmt
Definition: text_rich.c:49
text_config_t config
Definition: text_rich.c:37
size_t largest_line
Definition: text_rich.c:43
arena_t * arena
Definition: text_rich.c:35
cache_map_t * file_cache
Definition: text_rich.c:45
severity_t severity
Definition: text_rich.c:40
const event_t * event
Definition: text_rich.c:38
a source file scanner
Definition: scan.h:24
a segment inside an event
Definition: notify.h:57
node_t node
the related node
Definition: notify.h:59
STA_FIELD_STRING char * message
the message associated with this segment
Definition: notify.h:62
a set iterator handle
Definition: set.h:78
an unordered hash set
Definition: set.c:19
io_t * io
Definition: notify.h:81
const colour_pallete_t * colours
Definition: notify.h:78
file_config_t config
Definition: notify.h:75
cache_map_t * cache
a shared cache between all reports, set to NULL to disable caching
Definition: notify.h:72
a range of text
Definition: text.h:14
size_t length
the number of characters in the text
Definition: text.h:19
a non-owning view of text
Definition: text.h:24
size_t length
the number of characters in the text
Definition: text.h:30
A vector with a fixed type size.
Definition: vector.h:24
a generic vector of pointers
Definition: vector.c:16
a location inside a scanner locations are inclusive and 0-based
Definition: where.h:23
ctu_column_t first_column
the first column of the location
Definition: where.h:31
ctu_column_t last_column
the last column of the location
Definition: where.h:34
ctu_line_t last_line
the last line of the location
Definition: where.h:28
ctu_line_t first_line
the first line of the location
Definition: where.h:25
size_t get_offset_line(bool zero_indexed_lines, size_t line)
colour defines
Definition: common.c:12
char * fmt_left_align(arena_t *arena, size_t width, const char *fmt,...)
Definition: common.c:36
int get_num_width(size_t num)
get the width of a number if it were printed as base10
Definition: common.c:21
#define COLOUR_SEGMENT
Definition: text_rich.c:29
STA_DECL void text_report_rich(text_config_t config, const event_t *event)
Definition: text_rich.c:528
#define COLOUR_UNDERLINE
Definition: text_rich.c:28
#define COLOUR_NOTE_STAR
Definition: text_rich.c:30
#define COLOUR_PATH
Definition: text_rich.c:27
#define COLOUR_MESSAGE
Definition: text_rich.c:31