ded

Dramatic EDitor
Index Commits Files Refs README LICENSE
src/editor.c (12023B)
   1 #include <assert.h>
   2 #include <stdbool.h>
   3 #include <stdio.h>
   4 #include <errno.h>
   5 #include <string.h>
   6 #include "./editor.h"
   7 #include "./common.h"
   8 
   9 void editor_backspace(Editor *e)
  10 {
  11     if (e->cursor > e->data.count) {
  12         e->cursor = e->data.count;
  13     }
  14     if (e->cursor == 0) return;
  15 
  16     memmove(
  17         &e->data.items[e->cursor - 1],
  18         &e->data.items[e->cursor],
  19         e->data.count - e->cursor
  20     );
  21     e->cursor -= 1;
  22     e->data.count -= 1;
  23     editor_retokenize(e);
  24 }
  25 
  26 void editor_delete(Editor *e)
  27 {
  28     if (e->cursor >= e->data.count) return;
  29     memmove(
  30         &e->data.items[e->cursor],
  31         &e->data.items[e->cursor + 1],
  32         e->data.count - e->cursor - 1
  33     );
  34     e->data.count -= 1;
  35     editor_retokenize(e);
  36 }
  37 
  38 // TODO: make sure that you always have new line at the end of the file while saving
  39 // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
  40 
  41 Errno editor_save_as(Editor *e, const char *file_path)
  42 {
  43     printf("Saving as %s...\n", file_path);
  44     Errno err = write_entire_file(file_path, e->data.items, e->data.count);
  45     if (err != 0) return err;
  46     e->file_path.count = 0;
  47     sb_append_cstr(&e->file_path, file_path);
  48     sb_append_null(&e->file_path);
  49     return 0;
  50 }
  51 
  52 Errno editor_save(const Editor *e)
  53 {
  54     assert(e->file_path.count > 0);
  55     printf("Saving as %s...\n", e->file_path.items);
  56     return write_entire_file(e->file_path.items, e->data.items, e->data.count);
  57 }
  58 
  59 Errno editor_load_from_file(Editor *e, const char *file_path)
  60 {
  61     printf("Loading %s\n", file_path);
  62 
  63     e->data.count = 0;
  64     Errno err = read_entire_file(file_path, &e->data);
  65     if (err != 0) return err;
  66 
  67     e->cursor = 0;
  68 
  69     editor_retokenize(e);
  70 
  71     e->file_path.count = 0;
  72     sb_append_cstr(&e->file_path, file_path);
  73     sb_append_null(&e->file_path);
  74 
  75     return 0;
  76 }
  77 
  78 size_t editor_cursor_row(const Editor *e)
  79 {
  80     assert(e->lines.count > 0);
  81     for (size_t row = 0; row < e->lines.count; ++row) {
  82         Line line = e->lines.items[row];
  83         if (line.begin <= e->cursor && e->cursor <= line.end) {
  84             return row;
  85         }
  86     }
  87     return e->lines.count - 1;
  88 }
  89 
  90 void editor_move_line_up(Editor *e)
  91 {
  92     size_t cursor_row = editor_cursor_row(e);
  93     size_t cursor_col = e->cursor - e->lines.items[cursor_row].begin;
  94     if (cursor_row > 0) {
  95         Line next_line = e->lines.items[cursor_row - 1];
  96         size_t next_line_size = next_line.end - next_line.begin;
  97         if (cursor_col > next_line_size) cursor_col = next_line_size;
  98         e->cursor = next_line.begin + cursor_col;
  99     }
 100 }
 101 
 102 void editor_move_line_down(Editor *e)
 103 {
 104     size_t cursor_row = editor_cursor_row(e);
 105     size_t cursor_col = e->cursor - e->lines.items[cursor_row].begin;
 106     if (cursor_row < e->lines.count - 1) {
 107         Line next_line = e->lines.items[cursor_row + 1];
 108         size_t next_line_size = next_line.end - next_line.begin;
 109         if (cursor_col > next_line_size) cursor_col = next_line_size;
 110         e->cursor = next_line.begin + cursor_col;
 111     }
 112 }
 113 
 114 void editor_move_char_left(Editor *e)
 115 {
 116     if (e->cursor > 0) e->cursor -= 1;
 117 }
 118 
 119 void editor_move_char_right(Editor *e)
 120 {
 121     if (e->cursor < e->data.count) e->cursor += 1;
 122 }
 123 
 124 void editor_move_word_left(Editor *e)
 125 {
 126     while (e->cursor > 0 && !isalnum(e->data.items[e->cursor - 1])) {
 127         e->cursor -= 1;
 128     }
 129     while (e->cursor > 0 && isalnum(e->data.items[e->cursor - 1])) {
 130         e->cursor -= 1;
 131     }
 132 }
 133 
 134 void editor_move_word_right(Editor *e)
 135 {
 136     while (e->cursor < e->data.count && !isalnum(e->data.items[e->cursor])) {
 137         e->cursor += 1;
 138     }
 139     while (e->cursor < e->data.count && isalnum(e->data.items[e->cursor])) {
 140         e->cursor += 1;
 141     }
 142 }
 143 
 144 void editor_insert_char(Editor *e, char x)
 145 {
 146     editor_insert_buf(e, &x, 1);
 147 }
 148 
 149 void editor_insert_buf(Editor *e, char *buf, size_t buf_len)
 150 {
 151     if (e->cursor > e->data.count) {
 152         e->cursor = e->data.count;
 153     }
 154 
 155     for (size_t i = 0; i < buf_len; ++i) {
 156         da_append(&e->data, '\0');
 157     }
 158     memmove(
 159         &e->data.items[e->cursor + buf_len],
 160         &e->data.items[e->cursor],
 161         e->data.count - e->cursor - buf_len
 162     );
 163     memcpy(&e->data.items[e->cursor], buf, buf_len);
 164     e->cursor += buf_len;
 165     editor_retokenize(e);
 166 }
 167 
 168 void editor_retokenize(Editor *e)
 169 {
 170     // Lines
 171     {
 172         e->lines.count = 0;
 173 
 174         Line line;
 175         line.begin = 0;
 176 
 177         for (size_t i = 0; i < e->data.count; ++i) {
 178             if (e->data.items[i] == '\n') {
 179                 line.end = i;
 180                 da_append(&e->lines, line);
 181                 line.begin = i + 1;
 182             }
 183         }
 184 
 185         line.end = e->data.count;
 186         da_append(&e->lines, line);
 187     }
 188 
 189     // Syntax Highlighting
 190     {
 191         e->tokens.count = 0;
 192         Lexer l = lexer_new(e->atlas, e->data.items, e->data.count);
 193         Token t = lexer_next(&l);
 194         while (t.kind != TOKEN_END) {
 195             da_append(&e->tokens, t);
 196             t = lexer_next(&l);
 197         }
 198     }
 199 }
 200 
 201 bool editor_line_starts_with(Editor *e, size_t row, size_t col, const char *prefix)
 202 {
 203     size_t prefix_len = strlen(prefix);
 204     if (prefix_len == 0) {
 205         return true;
 206     }
 207     Line line = e->lines.items[row];
 208     if (col + prefix_len - 1 >= line.end) {
 209         return false;
 210     }
 211     for (size_t i = 0; i < prefix_len; ++i) {
 212         if (prefix[i] != e->data.items[line.begin + col + i]) {
 213             return false;
 214         }
 215     }
 216     return true;
 217 }
 218 
 219 const char *editor_line_starts_with_one_of(Editor *e, size_t row, size_t col, const char **prefixes, size_t prefixes_count)
 220 {
 221     for (size_t i = 0; i < prefixes_count; ++i) {
 222         if (editor_line_starts_with(e, row, col, prefixes[i])) {
 223             return prefixes[i];
 224         }
 225     }
 226     return NULL;
 227 }
 228 
 229 void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor)
 230 {
 231     int w, h;
 232     SDL_GetWindowSize(window, &w, &h);
 233 
 234     float max_line_len = 0.0f;
 235 
 236     sr->resolution = vec2f(w, h);
 237     sr->time = (float) SDL_GetTicks() / 1000.0f;
 238 
 239     // Render selection
 240     {
 241         simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
 242         if (editor->selection) {
 243             for (size_t row = 0; row < editor->lines.count; ++row) {
 244                 size_t select_begin_chr = editor->select_begin;
 245                 size_t select_end_chr = editor->cursor;
 246                 if (select_begin_chr > select_end_chr) {
 247                     SWAP(size_t, select_begin_chr, select_end_chr);
 248                 }
 249 
 250                 Line line_chr = editor->lines.items[row];
 251 
 252                 if (select_begin_chr < line_chr.begin) {
 253                     select_begin_chr = line_chr.begin;
 254                 }
 255 
 256                 if (select_end_chr > line_chr.end) {
 257                     select_end_chr = line_chr.end;
 258                 }
 259 
 260                 if (select_begin_chr <= select_end_chr) {
 261                     Vec2f select_begin_scr = vec2f(0, -((float)row + CURSOR_OFFSET) * FREE_GLYPH_FONT_SIZE);
 262                     free_glyph_atlas_measure_line_sized(
 263                         atlas, editor->data.items + line_chr.begin, select_begin_chr - line_chr.begin,
 264                         &select_begin_scr);
 265 
 266                     Vec2f select_end_scr = select_begin_scr;
 267                     free_glyph_atlas_measure_line_sized(
 268                         atlas, editor->data.items + select_begin_chr, select_end_chr - select_begin_chr,
 269                         &select_end_scr);
 270 
 271                     Vec4f selection_color = vec4f(.25, .25, .25, 1);
 272                     simple_renderer_solid_rect(sr, select_begin_scr, vec2f(select_end_scr.x - select_begin_scr.x, FREE_GLYPH_FONT_SIZE), selection_color);
 273                 }
 274             }
 275         }
 276         simple_renderer_flush(sr);
 277     }
 278 
 279     // Render text
 280     {
 281         simple_renderer_set_shader(sr, SHADER_FOR_TEXT);
 282         for (size_t i = 0; i < editor->tokens.count; ++i) {
 283             Token token = editor->tokens.items[i];
 284             Vec2f pos = token.position;
 285             Vec4f color = vec4fs(1);
 286             switch (token.kind) {
 287             case TOKEN_PREPROC:
 288                 color = hex_to_vec4f(0x95A99FFF);
 289                 break;
 290             case TOKEN_KEYWORD:
 291                 color = hex_to_vec4f(0xFFDD33FF);
 292                 break;
 293             case TOKEN_COMMENT:
 294                 color = hex_to_vec4f(0xCC8C3CFF);
 295                 break;
 296             case TOKEN_STRING:
 297                 color = hex_to_vec4f(0x73c936ff);
 298                 break;
 299             default:
 300             {}
 301             }
 302             free_glyph_atlas_render_line_sized(atlas, sr, token.text, token.text_len, &pos, color);
 303             // TODO: the max_line_len should be calculated based on what's visible on the screen right now
 304             if (max_line_len < pos.x) max_line_len = pos.x;
 305         }
 306         simple_renderer_flush(sr);
 307     }
 308 
 309     Vec2f cursor_pos = vec2fs(0.0f);
 310     {
 311         size_t cursor_row = editor_cursor_row(editor);
 312         Line line = editor->lines.items[cursor_row];
 313         size_t cursor_col = editor->cursor - line.begin;
 314         cursor_pos.y = -((float)cursor_row + CURSOR_OFFSET) * FREE_GLYPH_FONT_SIZE;
 315         cursor_pos.x = free_glyph_atlas_cursor_pos(
 316                            atlas,
 317                            editor->data.items + line.begin, line.end - line.begin,
 318                            vec2f(0.0, cursor_pos.y),
 319                            cursor_col
 320                        );
 321     }
 322 
 323     // Render cursor
 324     simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
 325     {
 326         float CURSOR_WIDTH = 5.0f;
 327         Uint32 CURSOR_BLINK_THRESHOLD = 500;
 328         Uint32 CURSOR_BLINK_PERIOD = 1000;
 329         Uint32 t = SDL_GetTicks() - editor->last_stroke;
 330 
 331         sr->verticies_count = 0;
 332         if (t < CURSOR_BLINK_THRESHOLD || t/CURSOR_BLINK_PERIOD%2 != 0) {
 333             simple_renderer_solid_rect(
 334                 sr,
 335                 cursor_pos, vec2f(CURSOR_WIDTH, FREE_GLYPH_FONT_SIZE),
 336                 vec4fs(1));
 337         }
 338 
 339         simple_renderer_flush(sr);
 340     }
 341 
 342     // Update camera
 343     {
 344         if (max_line_len > 1000.0f) {
 345             max_line_len = 1000.0f;
 346         }
 347 
 348         float target_scale = w/3/(max_line_len*0.75); // TODO: division by 0
 349 
 350         Vec2f target = cursor_pos;
 351         float offset = 0.0f;
 352 
 353         if (target_scale > 3.0f) {
 354             target_scale = 3.0f;
 355         } else {
 356             offset = cursor_pos.x - w/3/sr->camera_scale;
 357             if (offset < 0.0f) offset = 0.0f;
 358             target = vec2f(w/3/sr->camera_scale + offset, cursor_pos.y);
 359         }
 360 
 361         sr->camera_vel = vec2f_mul(
 362                              vec2f_sub(target, sr->camera_pos),
 363                              vec2fs(2.0f));
 364         sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f;
 365 
 366         sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME)));
 367         sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME;
 368     }
 369 }
 370 
 371 void editor_update_selection(Editor *e, bool shift)
 372 {
 373     if (shift) {
 374         if (!e->selection) {
 375             e->selection = true;
 376             e->select_begin = e->cursor;
 377         }
 378     } else {
 379         if (e->selection) {
 380             e->selection = false;
 381         }
 382     }
 383 }
 384 
 385 void editor_clipboard_copy(Editor *e)
 386 {
 387     if (e->selection) {
 388         size_t begin = e->select_begin;
 389         size_t end = e->cursor;
 390         if (begin > end) SWAP(size_t, begin, end);
 391 
 392         e->clipboard.count = 0;
 393         sb_append_buf(&e->clipboard, &e->data.items[begin], end - begin + 1);
 394         sb_append_null(&e->clipboard);
 395 
 396         if (SDL_SetClipboardText(e->clipboard.items) < 0) {
 397             fprintf(stderr, "ERROR: SDL ERROR: %s\n", SDL_GetError());
 398         }
 399     }
 400 }
 401 
 402 void editor_clipboard_paste(Editor *e)
 403 {
 404     char *text = SDL_GetClipboardText();
 405     size_t text_len = strlen(text);
 406     if (text_len > 0) {
 407         editor_insert_buf(e, text, text_len);
 408     } else {
 409         fprintf(stderr, "ERROR: SDL ERROR: %s\n", SDL_GetError());
 410     }
 411     SDL_free(text);
 412 }