edo

Experimental text editor.
Log | Files | Refs | LICENSE

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 }