edo.c (8545B)
1 /* osaentuhaosenuhaoesnuthaoesnutha oesnthaoesuntha snethu asoenhu saoenhtuaoesn uthaoesunthaoesuntaoeh usaoneth asoenth aoesnth aoesnthaoseuthaoseuthaoesunthaoeusnh asoentuh */ 2 #include <assert.h> 3 #include <stdarg.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <unistd.h> 8 9 #include "ui.h" 10 11 typedef struct { 12 char *buf; 13 size_t cap; 14 int len; 15 } Line; 16 17 typedef struct { 18 Line **lines; 19 char *file_name; 20 int file_size; 21 int lines_cap; 22 size_t lines_tot; 23 //int ref_count; 24 } Buffer; 25 26 typedef struct { 27 Buffer *buf; 28 int line_num; 29 int col_num; 30 int row_offset; 31 int col_offset; 32 int screen_rows; 33 int screen_cols; 34 //int pref_col; 35 } View; 36 37 /* variables */ 38 int running = 1; 39 View *vcur; 40 UI *ui; 41 42 /* function declarations */ 43 void die(const char *fmt, ...); 44 void *ecalloc(size_t nmemb, size_t size); 45 void insert_char(char *dst, char c, int len); 46 void delete_char(char *dst, int count, int len); 47 void line_insert_char(Line *line, size_t index, char c); 48 void line_delete_char(Line *line, int index, int count); 49 Line *line_create(char *content); 50 void line_destroy(Line *l); 51 void buffer_insert_line(Buffer *b, int index, Line *line); 52 void buffer_delete_line(Buffer *b, int index, int count); 53 int buffer_load_file(Buffer *b); 54 Line *buffer_get_line(Buffer *b, int index); 55 Buffer *buffer_create(char *fn); 56 void buffer_destroy(Buffer *b); 57 View *view_create(Buffer *b); 58 void view_destroy(View *v); 59 void view_cursor_hfix(View *v); 60 void view_cursor_left(View *v); 61 void view_cursor_right(View *v); 62 void view_cursor_up(View *v); 63 void view_cursor_down(View *v); 64 int view_idx2col(View *v, Line *line, int idx); 65 int view_col2idx(View *v, Line *line, int col); 66 void view_scroll_fix(View *v); 67 void draw_view(View *v); 68 69 /* function implementations */ 70 void 71 die(const char *fmt, ...) { 72 va_list ap; 73 va_start(ap, fmt); 74 vfprintf(stderr, fmt, ap); 75 va_end(ap); 76 77 if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 78 fputc(' ', stderr); 79 perror(NULL); 80 } else { 81 fputc('\n', stderr); 82 } 83 exit(0); 84 } 85 86 void * 87 ecalloc(size_t nmemb, size_t size) { 88 void *p; 89 90 if(!(p = calloc(nmemb, size))) 91 die("Cannot allocate memory."); 92 return p; 93 } 94 95 void 96 insert_char(char *dst, char c, int len) { 97 memmove(dst + 1, dst, len); 98 memcpy(dst, &c, 1); 99 } 100 101 void 102 delete_char(char *dst, int count, int len) { 103 memmove(dst, dst + count, len); 104 } 105 106 void 107 line_insert_char(Line *line, size_t index, char c) { 108 size_t newlen = line->len + 1; 109 110 assert(index >= 0 && index <= line->len); 111 112 if(newlen > line->cap) { 113 line->cap = line->cap ? line->cap * 2 : 16; 114 if(!(line->buf = realloc(line->buf, line->cap))) 115 die("Cannot reallocate memory."); 116 } 117 insert_char(&line->buf[index], c, line->len - index); 118 line->len = newlen; 119 } 120 121 void 122 line_delete_char(Line *line, int index, int count) { 123 delete_char(&line->buf[index], count, line->len - index); 124 line->len -= count; 125 } 126 127 Line * 128 line_create(char *content) { 129 Line *l = ecalloc(1, sizeof(Line)); 130 131 if(content) { 132 l->buf = strdup(content); 133 l->len = strlen(l->buf); 134 l->cap = l->len + 1; 135 } 136 return l; 137 } 138 139 void 140 line_destroy(Line *l) { 141 if(l->buf) 142 free(l->buf); 143 free(l); 144 } 145 146 void 147 buffer_insert_line(Buffer *b, int index, Line *line) { 148 size_t nb = (b->lines_tot - index) * sizeof(Line *); 149 150 assert(index >= 0 && index <= b->lines_tot); 151 if(b->lines_tot >= b->lines_cap) { 152 b->lines_cap = b->lines_cap ? b->lines_cap * 2 : 64; 153 if(!(b->lines = realloc(b->lines, sizeof(Line *) * b->lines_cap))) 154 die("realloc:"); 155 } 156 if(nb) 157 memmove(b->lines + index + 1, b->lines + index, nb); 158 b->lines[index] = line; 159 ++b->lines_tot; 160 } 161 162 void 163 buffer_delete_line(Buffer *b, int index, int count) { 164 int lines_remaining, nb; 165 int last = index + count; 166 int i; 167 168 assert(count && index < b->lines_tot && last <= b->lines_tot); 169 for(i = 0; i < count; i++) 170 line_destroy(b->lines[index + i]); 171 lines_remaining = b->lines_tot - last; 172 if(lines_remaining) { 173 nb = lines_remaining * sizeof(Line *); 174 memmove(b->lines + index, b->lines + last, nb); 175 } 176 b->lines_tot -= count; 177 } 178 179 int 180 buffer_load_file(Buffer *b) { 181 Line *l; 182 FILE *fp; 183 char *buf = NULL; 184 size_t cap; 185 int len; 186 187 if(!(fp = fopen(b->file_name, "r"))) 188 return -1; 189 while((len = getline(&buf, &cap, fp)) != -1) { 190 buf[len - 1] = 0; 191 b->file_size += len; 192 l = line_create(buf); 193 buffer_insert_line(b, b->lines_tot, l); 194 } 195 fclose(fp); 196 return 0; 197 } 198 199 Line * 200 buffer_get_line(Buffer *b, int index) { 201 if(!b->lines) 202 return NULL; 203 return b->lines[index]; 204 } 205 206 Buffer * 207 buffer_create(char *fn) { 208 Buffer *b = ecalloc(1, sizeof(Buffer)); 209 210 b->lines = NULL; 211 b->lines_tot = 0; 212 b->file_size = 0; 213 if(fn) { 214 if(!(b->file_name = strdup(fn))) 215 return NULL; 216 if(buffer_load_file(b)) 217 printf("%s: cannot load file\n", fn); 218 } 219 else { 220 /* ensure we have at least a line */ 221 buffer_insert_line(b, b->lines_tot, line_create(NULL)); 222 } 223 return b; 224 } 225 226 void 227 buffer_destroy(Buffer *b) { 228 int i; 229 230 if(b->lines) { 231 for(i = 0; i < b->lines_tot; i++) 232 line_destroy(b->lines[i]); 233 free(b->lines); 234 } 235 if(b->file_name) 236 free(b->file_name); 237 free(b); 238 } 239 240 View * 241 view_create(Buffer *b) { 242 View *v = ecalloc(1, sizeof(View)); 243 244 v->line_num = 0; 245 v->col_num = 0; 246 v->row_offset = 0; 247 v->col_offset = 0; 248 v->buf = b; 249 250 ui->get_window_size(&v->screen_rows, &v->screen_cols); 251 return v; 252 } 253 254 void 255 view_destroy(View *v) { 256 free(v); 257 } 258 259 /* actual invariant for the cursor */ 260 void 261 view_cursor_hfix(View *v) { 262 assert(v->line_num >= 0 && v->line_num< v->buf->lines_tot); 263 264 Line *l = v->buf->lines[v->line_num]; 265 266 /* TODO: < 0 needed? */ 267 if(v->col_num < 0) 268 v->col_num = 0; 269 else if(v->col_num > l->len) 270 v->col_num = l->len; 271 } 272 273 void 274 view_cursor_left(View *v) { 275 if(v->col_num) 276 --v->col_num; 277 } 278 279 void 280 view_cursor_right(View *v) { 281 Line *l = v->buf->lines[v->line_num]; 282 283 if(v->col_num < l->len) 284 ++v->col_num; 285 } 286 287 void 288 view_cursor_up(View *v) { 289 if(v->line_num) { 290 --v->line_num; 291 view_cursor_hfix(v); 292 } 293 } 294 295 void 296 view_cursor_down(View *v) { 297 if(v->line_num < v->buf->lines_tot - 1) { 298 ++v->line_num; 299 view_cursor_hfix(v); 300 } 301 } 302 303 int 304 view_idx2col(View *v, Line *line, int idx) { 305 (void)v; 306 return ui->text_width(line->buf, idx); 307 } 308 309 int 310 view_col2idx(View *v, Line *line, int col) { 311 (void)v; 312 return ui->text_index_at(line->buf, col); 313 } 314 315 void 316 view_scroll_fix(View *v) { 317 /* vertical */ 318 if (v->line_num < v->row_offset) 319 v->row_offset = v->line_num; 320 if (v->line_num >= v->row_offset + v->screen_rows) 321 v->row_offset = v->line_num - v->screen_rows + 1; 322 323 /* horizontal */ 324 if(v->col_num < v->col_offset) 325 v->col_offset = v->col_num; 326 if(v->col_num >= v->col_offset + v->screen_cols) 327 v->col_offset = v->col_num - v->screen_cols + 1; 328 } 329 330 void 331 draw_view(View *v) { 332 Line *l; 333 int row, len, y; 334 int idx, sx, shift; 335 336 ui->frame_start(); 337 view_scroll_fix(v); 338 339 for(y = 0; y < v->screen_rows; y++) { 340 row = v->row_offset + y; 341 342 if(row >= v->buf->lines_tot) { 343 ui->draw_symbol(0, y, SYM_EMPTYLINE); 344 continue; 345 } 346 347 l = buffer_get_line(v->buf, row); 348 if(!l->len) { 349 ui->draw_line(0, y, "", 0); 350 continue; 351 } 352 idx = view_col2idx(v, l, v->col_offset); 353 len = l->len - idx; 354 355 /* fine clipping */ 356 sx = view_idx2col(v, l, idx); 357 shift = sx - v->col_offset; 358 359 ui->draw_line(shift, y, l->buf + idx, len); 360 } 361 362 l = buffer_get_line(v->buf, v->line_num); 363 if(l) { 364 row = view_idx2col(v, l, v->col_num); 365 row -= v->col_offset; 366 y = v->line_num - v->row_offset; 367 } else { 368 row = y = 0; 369 } 370 371 ui->move_cursor(row, y); 372 ui->frame_flush(); 373 } 374 375 void 376 run(void) { 377 Event ev; 378 379 while(running) { 380 ev = ui->next_event(); 381 switch(ev.type) { 382 case EV_KEY: 383 if(ev.key == 'k') view_cursor_up(vcur); 384 else if(ev.key == 'j') view_cursor_down(vcur); 385 else if(ev.key == 'h') view_cursor_left(vcur); 386 else if(ev.key == 'l') view_cursor_right(vcur); 387 else if(ev.key == 'q') running = 0; 388 else if(ev.key == 'K') { 389 Line *l = line_create(NULL); 390 buffer_insert_line(vcur->buf, vcur->line_num + 0, l); 391 } 392 else if(ev.key == 'J') { 393 Line *l = line_create(NULL); 394 buffer_insert_line(vcur->buf, vcur->line_num + 1, l); 395 view_cursor_down(vcur); 396 } else { 397 line_insert_char(vcur->buf->lines[vcur->line_num], vcur->col_num, ev.key); 398 view_cursor_right(vcur); 399 } 400 break; 401 case EV_UKN: 402 break; 403 } 404 draw_view(vcur); 405 } 406 } 407 408 int 409 main(int argc, char *argv[]) { 410 char *fn = NULL; 411 412 if(argc > 2) die("Usage: %s [file]", argv[0]); 413 if(argc == 2) fn = argv[1]; 414 415 ui = &ui_tui; /* the one and only... */ 416 417 ui->init(); 418 Buffer *b = buffer_create(fn); 419 View *v = view_create(b); 420 vcur = v; /* current view */ 421 draw_view(v); 422 run(); 423 buffer_destroy(v->buf); 424 view_destroy(v); 425 ui->exit(); 426 return 0; 427 }