so many updates

This commit is contained in:
Kyle Isom 2020-02-12 17:37:28 -08:00
parent 3f9a95287a
commit 5de05e3f0a
6 changed files with 464 additions and 432 deletions

View File

@ -1,5 +1,5 @@
BIN := ke BIN := ke
OBJS := abuf.o erow.o main.o OBJS := main.o
LDFLAGS := LDFLAGS :=
CFLAGS := -pedantic -Wall -Werror -Wextra -O2 -std=c99 -g -fno-builtin-memmove CFLAGS := -pedantic -Wall -Werror -Wextra -O2 -std=c99 -g -fno-builtin-memmove

View File

@ -1,27 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include "defs.h"
void
ab_append(struct abuf *buf, const char *s, int len)
{
char *nc = realloc(buf->b, buf->len + len);
if (nc == NULL) {
abort();
}
memcpy(&nc[buf->len], s, len);
buf->b = nc;
buf->len += len; /* DANGER: overflow */
}
void
ab_free(struct abuf *buf)
{
free(buf->b);
buf->b = NULL;
}

View File

@ -1,72 +0,0 @@
#ifndef KE_DEFS_H
#define KE_DEFS_H
#include <termios.h>
#define TAB_STOP 8
/*
* abuf is an append buffer.
*/
struct abuf {
char *b;
int len;
};
#define ABUF_INIT {NULL, 0}
/*
* erow is an editor row
*/
struct erow {
char *line;
char *render;
int size;
int rsize;
};
struct editor_t {
struct termios entry_term;
int rows, cols;
int curx, cury;
int rx;
int mode;
int nrows;
int rowoffs, coloffs;
struct erow *row;
char *filename;
int dirty;
int dirtyex;
char msg[80];
time_t msgtm;
} _editor;
static struct editor_t *editor = &_editor;
/*
* Function declarations.
*/
/* append buffer */
void ab_append(struct abuf *buf, const char *s, int len);
void ab_free(struct abuf *buf);
/* editor row */
int erow_render_to_cursor(struct erow *row, int cx);
int erow_cursor_to_render(struct erow *row, int rx);
void erow_update(struct erow *row);
void erow_insert(int at, char *s, int len);
void erow_free(struct erow *row);
#endif /* KE_DEFS_H */

156
ke/erow.c
View File

@ -1,156 +0,0 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "defs.h"
static char
byte_to_hihex(char c)
{
c = (c >> 4) & 0xf;
if (c < 10) {
return c + 0x30;
}
return c + 0x41;
}
static char
byte_to_lohex(char c)
{
c = c & 0x0f;
if (c < 10) {
return c + 0x30;
}
return c + 0x37;
}
int
erow_render_to_cursor(struct erow *row, int cx)
{
int rx = 0;
int j;
for (j = 0; j < cx; j++) {
if (row->line[j] == '\t') {
rx += (TAB_STOP-1) - (rx%TAB_STOP);
} else if (row->line[j] < 0x20) {
rx += 2;
}
rx++;
}
return rx;
}
int
erow_cursor_to_render(struct erow *row, int rx)
{
int cur_rx = 0;
int curx = 0;
for (curx = 0; curx < row->size; curx++) {
if (row->line[curx] == '\t') {
cur_rx += (TAB_STOP - 1) - (cur_rx % TAB_STOP);
} else if (row->line[curx] < 0x20) {
cur_rx += 2;
}
cur_rx++;
if (cur_rx > rx) {
break;
}
}
return curx;
}
void
erow_update(struct erow *row)
{
int i = 0, j;
int tabs = 0;
int ctrl = 0;
/*
* TODO(kyle): I'm not thrilled with this double-render.
*/
for (j = 0; j < row->size; j++) {
if (row->line[j] == '\t') {
tabs++;
} else if (!isprint(row->line[j])) {
ctrl++;
}
}
if (row->rsize) {
free(row->render);
}
row->render = NULL;
row->render = malloc(row->size + (tabs * (TAB_STOP-1)) + (ctrl * 3) + 1);
assert(row->render != NULL);
for (j = 0; j < row->size; j++) {
if (row->line[j] == '\t') {
do {
row->render[i++] = ' ';
} while ((i % TAB_STOP) != 0);
} else if (!isprint(row->line[j])) {
row->render[i++] = '\\';
row->render[i++] = byte_to_hihex(row->line[j]);
row->render[i++] = byte_to_lohex(row->line[j]);
} else {
row->render[i++] = row->line[j];
}
}
row->render[i] = '\0';
row->rsize = i;
}
void
erow_insert(int at, char *s, int len)
{
if (at < 0 || at > editor->nrows) {
return;
}
editor->row = realloc(editor->row, sizeof(struct erow) * (editor->nrows + 1));
assert(editor->row != NULL);
if (at < editor->nrows) {
memmove(&editor->row[at+1], &editor->row[at],
sizeof(struct erow) * (editor->nrows - at + 1));
}
editor->row[at].size = len;
editor->row[at].line = malloc(len+1);
memcpy(editor->row[at].line, s, len);
editor->row[at].line[len] = '\0';
editor->row[at].rsize = 0;
editor->row[at].render = NULL;
erow_update(&editor->row[at]);
editor->nrows++;
}
void
erow_free(struct erow *row)
{
free(row->render);
free(row->line);
row->render = NULL;
row->line = NULL;
}

58
ke/ke.1 Normal file
View File

@ -0,0 +1,58 @@
.Dd $Mdocdate$
.Dt KE section
.Os
.Sh NAME
.Nm ke
.Nd Kyle's text editor.
.Sh SYNPOSIS
.Nm ke
.Op Ar files
.Sh DESCRIPTION
.Nm
is Kyle's text editor and is probably ill-suited to everyone else. It
was inspired by Antirez' kilo text editor by way of someone's writeup
of the process of writing a text editor from scratch. It has keybindings
inspired by VDE (and the Wordstar family) and emacs; its spiritual parent
is
.Xr mg 1 .
.Sh KEYBINDINGS
K-command mode is entered using C-k. This is taken from Wordstar and just
so happens to be blessed with starting with a most excellent letter of
grandeur. Many commands work with and without control; for example,
saving a file can be done with either C-k s or C-k C-s.
.Pp
.Bl -tag -width xxxxxxxxxxxx -offset indent
.It C-k BACKSPACE
Delete from the cursor to the beginning of the line.
.It C-k d
Delete from the cursor to the end of the line.
.It C-k e
Edit a new file. Also C-k C-e.
.It C-k f
Incremental find.
.It C-k g
Go to a specific line. Also C-k C-g.
.It C-k m
Run make(1).
.It C-k q
exit the editor. Also C-k C-q.
.It C-k s
save the file, prompting for a filename if needed. Also C-k C-s.
.It C-k w
save the file and exit. Also C-k C-w.
.It C-k \
Dump core.
.El
.Sh FIND
The find operation is an incremental search. The up or left arrow keys will
go to the previous result, while the down or right arrow keys will go to
the next result. Unfortunately, the search starts from the top of the file
each time. This is a known bug.
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr mg 1
.Sh AUTHORS
.An Kyle Isom Aq Mt kyle@imap.cc
.Sh CAVEATS
This is pretty buggy and will probably eat whatever you type.

581
ke/main.c
View File

@ -21,8 +21,6 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "defs.h"
#define KE_VERSION "0.9.1" #define KE_VERSION "0.9.1"
#define ESCSEQ "\x1b[" #define ESCSEQ "\x1b["
@ -40,11 +38,72 @@
#define MODE_ESCAPE 2 #define MODE_ESCAPE 2
#define TAB_STOP 8
/*
* abuf is an append buffer.
*/
struct abuf {
char *b;
int len;
};
#define ABUF_INIT {NULL, 0}
/*
* erow is an editor row
*/
struct erow {
char *line;
char *render;
int size;
int rsize;
};
struct editor_t {
struct termios entry_term;
int rows, cols;
int curx, cury;
int rx;
int mode;
int nrows;
int rowoffs, coloffs;
struct erow *row;
char *filename;
int dirty;
int dirtyex;
char msg[80];
time_t msgtm;
} editor;
/*
* Function declarations.
*/
/* append buffer */
void ab_append(struct abuf *buf, const char *s, int len);
void ab_free(struct abuf *buf);
/* editor row */
int erow_render_to_cursor(struct erow *row, int cx);
int erow_cursor_to_render(struct erow *row, int rx);
void erow_update(struct erow *row);
void erow_insert(int at, char *s, int len);
void erow_free(struct erow *row);
void editor_set_status(const char *fmt, ...); void editor_set_status(const char *fmt, ...);
/* miscellaneous */
void display_refresh(); void display_refresh();
char *editor_prompt(char *, void (*cb)(char *, int16_t)); char *editor_prompt(char *, void (*cb)(char *, int16_t));
void init_editor(); void init_editor();
void process_normal(int16_t c); void process_normal(int16_t c);
void disable_termraw();
enum KeyPress { enum KeyPress {
@ -63,7 +122,176 @@ enum KeyPress {
}; };
void disable_termraw(); void
ab_append(struct abuf *buf, const char *s, int len)
{
char *nc = realloc(buf->b, buf->len + len);
if (nc == NULL) {
abort();
}
memcpy(&nc[buf->len], s, len);
buf->b = nc;
buf->len += len; /* DANGER: overflow */
}
void
ab_free(struct abuf *buf)
{
free(buf->b);
buf->b = NULL;
}
static char
byte_to_hihex(char c)
{
c = (c >> 4) & 0xf;
if (c < 10) {
return c + 0x30;
}
return c + 0x41;
}
static char
byte_to_lohex(char c)
{
c = c & 0x0f;
if (c < 10) {
return c + 0x30;
}
return c + 0x37;
}
int
erow_render_to_cursor(struct erow *row, int cx)
{
int rx = 0;
int j;
for (j = 0; j < cx; j++) {
if (row->line[j] == '\t') {
rx += (TAB_STOP-1) - (rx%TAB_STOP);
} else if (row->line[j] < 0x20) {
rx += 2;
}
rx++;
}
return rx;
}
int
erow_cursor_to_render(struct erow *row, int rx)
{
int cur_rx = 0;
int curx = 0;
for (curx = 0; curx < row->size; curx++) {
if (row->line[curx] == '\t') {
cur_rx += (TAB_STOP - 1) - (cur_rx % TAB_STOP);
} else if (row->line[curx] < 0x20) {
cur_rx += 2;
}
cur_rx++;
if (cur_rx > rx) {
break;
}
}
return curx;
}
void
erow_update(struct erow *row)
{
int i = 0, j;
int tabs = 0;
int ctrl = 0;
/*
* TODO(kyle): I'm not thrilled with this double-render.
*/
for (j = 0; j < row->size; j++) {
if (row->line[j] == '\t') {
tabs++;
} else if (!isprint(row->line[j])) {
ctrl++;
}
}
if (row->rsize) {
free(row->render);
row->rsize = 0;
}
row->render = NULL;
row->render = malloc(row->size + (tabs * (TAB_STOP-1)) + (ctrl * 3) + 1);
assert(row->render != NULL);
for (j = 0; j < row->size; j++) {
if (row->line[j] == '\t') {
do {
row->render[i++] = ' ';
} while ((i % TAB_STOP) != 0);
} else if (!isprint(row->line[j])) {
row->render[i++] = '\\';
row->render[i++] = byte_to_hihex(row->line[j]);
row->render[i++] = byte_to_lohex(row->line[j]);
} else {
row->render[i++] = row->line[j];
}
}
row->render[i] = '\0';
row->rsize = i;
}
void
erow_insert(int at, char *s, int len)
{
if (at < 0 || at > editor.nrows) {
return;
}
editor.row = realloc(editor.row, sizeof(struct erow) * (editor.nrows + 1));
assert(editor.row != NULL);
if (at < editor.nrows) {
memmove(&editor.row[at+1], &editor.row[at],
sizeof(struct erow) * (editor.nrows - at + 1));
}
editor.row[at].size = len;
editor.row[at].line = malloc(len+1);
memcpy(editor.row[at].line, s, len);
editor.row[at].line[len] = '\0';
editor.row[at].rsize = 0;
editor.row[at].render = NULL;
erow_update(&editor.row[at]);
editor.nrows++;
}
void
erow_free(struct erow *row)
{
free(row->render);
free(row->line);
row->render = NULL;
row->line = NULL;
}
void void
@ -116,28 +344,28 @@ goto_line()
} }
lineno = atoi(query); lineno = atoi(query);
if (lineno < 1 || lineno >= editor->nrows) { if (lineno < 1 || lineno >= editor.nrows) {
editor_set_status("Line number must be between 1 and %d.", editor->nrows); editor_set_status("Line number must be between 1 and %d.", editor.nrows);
return; return;
} }
editor->cury = lineno - 1; editor.cury = lineno - 1;
editor->rowoffs = editor->cury - (editor->rows / 2); editor.rowoffs = editor.cury - (editor.rows / 2);
} }
void void
delete_row(int at) delete_row(int at)
{ {
if (at < 0 || at >= editor->nrows) { if (at < 0 || at >= editor.nrows) {
return; return;
} }
erow_free(&editor->row[at]); erow_free(&editor.row[at]);
memmove(&editor->row[at], &editor->row[at+1], memmove(&editor.row[at], &editor.row[at+1],
sizeof(struct erow) * (editor->nrows - at - 1)); sizeof(struct erow) * (editor.nrows - at - 1));
editor->nrows--; editor.nrows--;
editor->dirty++; editor.dirty++;
} }
@ -150,7 +378,7 @@ row_append_row(struct erow *row, char *s, int len)
row->size += len; row->size += len;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
erow_update(row); erow_update(row);
editor->dirty++; editor.dirty++;
} }
@ -184,7 +412,7 @@ row_delete_ch(struct erow *row, int at)
memmove(&row->line[at], &row->line[at+1], row->size+1); memmove(&row->line[at], &row->line[at+1], row->size+1);
row->size--; row->size--;
erow_update(row); erow_update(row);
editor->dirty++; editor.dirty++;
} }
@ -196,13 +424,13 @@ insertch(int16_t c)
* a row; it can just figure out where the cursor is * a row; it can just figure out where the cursor is
* at and what to do. * at and what to do.
*/ */
if (editor->cury == editor->nrows) { if (editor.cury == editor.nrows) {
erow_insert(editor->nrows, "", 0); erow_insert(editor.nrows, "", 0);
} }
row_insert_ch(&editor->row[editor->cury], editor->curx, (char)(c & 0xff)); row_insert_ch(&editor.row[editor.cury], editor.curx, (char)(c & 0xff));
editor->curx++; editor.curx++;
editor->dirty++; editor.dirty++;
} }
@ -211,24 +439,24 @@ deletech()
{ {
struct erow *row = NULL; struct erow *row = NULL;
if (editor->cury >= editor->nrows) { if (editor.cury >= editor.nrows) {
return; return;
} }
if (editor->cury == 0 && editor->curx == 0) { if (editor.cury == 0 && editor.curx == 0) {
return; return;
} }
row = &editor->row[editor->cury]; row = &editor.row[editor.cury];
if (editor->curx > 0) { if (editor.curx > 0) {
row_delete_ch(row, editor->curx - 1); row_delete_ch(row, editor.curx - 1);
editor->curx--; editor.curx--;
} else { } else {
editor->curx = editor->row[editor->cury - 1].size; editor.curx = editor.row[editor.cury - 1].size;
row_append_row(&editor->row[editor->cury - 1], row->line, row_append_row(&editor.row[editor.cury - 1], row->line,
row->size); row->size);
delete_row(editor->cury); delete_row(editor.cury);
editor->cury--; editor.cury--;
} }
} }
@ -241,11 +469,11 @@ open_file(const char *filename)
ssize_t linelen; ssize_t linelen;
FILE *fp = NULL; FILE *fp = NULL;
free(editor->filename); free(editor.filename);
editor->filename = strdup(filename); editor.filename = strdup(filename);
assert(editor->filename != NULL); assert(editor.filename != NULL);
editor->dirty = 0; editor.dirty = 0;
if ((fp = fopen(filename, "r")) == NULL) { if ((fp = fopen(filename, "r")) == NULL) {
if (errno == ENOENT) { if (errno == ENOENT) {
editor_set_status("[new file]"); editor_set_status("[new file]");
@ -261,7 +489,7 @@ open_file(const char *filename)
linelen--; linelen--;
} }
erow_insert(editor->nrows, line, linelen); erow_insert(editor.nrows, line, linelen);
} }
} }
@ -282,9 +510,9 @@ rows_to_buffer(int *buflen)
char *buf = NULL; char *buf = NULL;
char *p = NULL; char *p = NULL;
for (j = 0; j < editor->nrows; j++) { for (j = 0; j < editor.nrows; j++) {
/* extra byte for newline */ /* extra byte for newline */
len += editor->row[j].size+1; len += editor.row[j].size+1;
} }
if (len == 0) { if (len == 0) {
@ -296,9 +524,9 @@ rows_to_buffer(int *buflen)
assert(buf != NULL); assert(buf != NULL);
p = buf; p = buf;
for (j = 0; j < editor->nrows; j++) { for (j = 0; j < editor.nrows; j++) {
memcpy(p, editor->row[j].line, editor->row[j].size); memcpy(p, editor.row[j].line, editor.row[j].size);
p += editor->row[j].size; p += editor.row[j].size;
*p++ = '\n'; *p++ = '\n';
} }
@ -314,21 +542,21 @@ save_file()
int status = 1; /* will be used as exit code */ int status = 1; /* will be used as exit code */
char *buf; char *buf;
if (!editor->dirty) { if (!editor.dirty) {
editor_set_status("No changes to save."); editor_set_status("No changes to save.");
return 0; return 0;
} }
if (editor->filename == NULL) { if (editor.filename == NULL) {
editor->filename = editor_prompt("Filename: %s", NULL); editor.filename = editor_prompt("Filename: %s", NULL);
if (editor->filename == NULL) { if (editor.filename == NULL) {
editor_set_status("Save aborted."); editor_set_status("Save aborted.");
return 0; return 0;
} }
} }
buf = rows_to_buffer(&len); buf = rows_to_buffer(&len);
if ((fd = open(editor->filename, O_RDWR|O_CREAT, 0644)) == -1) { if ((fd = open(editor.filename, O_RDWR|O_CREAT, 0644)) == -1) {
goto save_exit; goto save_exit;
} }
@ -356,12 +584,12 @@ save_exit:
if (status != 0) { if (status != 0) {
buf = strerror(errno); buf = strerror(errno);
editor_set_status("Error writing %s: %s", editor->filename, editor_set_status("Error writing %s: %s", editor.filename,
buf); buf);
} else { } else {
editor_set_status("Wrote %d bytes to %s.", len, editor_set_status("Wrote %d bytes to %s.", len,
editor->filename); editor.filename);
editor->dirty = 0; editor.dirty = 0;
} }
return status; return status;
@ -529,29 +757,29 @@ editor_find_callback(char *query, int16_t c)
} }
current = lmatch; current = lmatch;
for (i = 0; i < editor->nrows; i++) { for (i = 0; i < editor.nrows; i++) {
current += dir; current += dir;
if (current == -1) { if (current == -1) {
current = editor->nrows - 1; current = editor.nrows - 1;
} else if (current == editor->nrows) { } else if (current == editor.nrows) {
current = 0; current = 0;
} }
row = &editor->row[current]; row = &editor.row[current];
match = strstr(row->render, query); match = strstr(row->render, query);
if (match) { if (match) {
lmatch = current; lmatch = current;
editor->cury = current; editor.cury = current;
editor->curx = erow_render_to_cursor(row, editor.curx = erow_render_to_cursor(row,
match - row->render); match - row->render);
if (editor->curx > row->size) { if (editor.curx > row->size) {
editor->curx = row->size; editor.curx = row->size;
} }
/* /*
* after this, scroll will put the match line at * after this, scroll will put the match line at
* the top of the screen. * the top of the screen.
*/ */
editor->rowoffs = editor->nrows; editor.rowoffs = editor.nrows;
break; break;
} }
} }
@ -564,20 +792,20 @@ void
editor_find() editor_find()
{ {
char *query; char *query;
int scx = editor->curx; int scx = editor.curx;
int scy = editor->cury; int scy = editor.cury;
int sco = editor->coloffs; int sco = editor.coloffs;
int sro = editor->rowoffs; int sro = editor.rowoffs;
query = editor_prompt("Search (ESC to cancel): %s", query = editor_prompt("Search (ESC to cancel): %s",
editor_find_callback); editor_find_callback);
if (query) { if (query) {
free(query); free(query);
} else { } else {
editor->curx = scx; editor.curx = scx;
editor->cury = scy; editor.cury = scy;
editor->coloffs = sco; editor.coloffs = sco;
editor->rowoffs = sro; editor.rowoffs = sro;
} }
display_refresh(); display_refresh();
@ -595,7 +823,7 @@ editor_openfile()
return; return;
} }
free(editor->row); free(editor.row);
init_editor(); init_editor();
open_file(filename); open_file(filename);
@ -608,51 +836,51 @@ move_cursor(int16_t c)
struct erow *row; struct erow *row;
int reps; int reps;
row = (editor->cury >= editor->nrows) ? NULL : &editor->row[editor->cury]; row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury];
switch (c) { switch (c) {
case ARROW_UP: case ARROW_UP:
case CTRL_KEY('p'): case CTRL_KEY('p'):
if (editor->cury > 0) { if (editor.cury > 0) {
editor->cury--; editor.cury--;
} }
break; break;
case ARROW_DOWN: case ARROW_DOWN:
case CTRL_KEY('n'): case CTRL_KEY('n'):
if (editor->cury < editor->nrows) { if (editor.cury < editor.nrows) {
editor->cury++; editor.cury++;
} }
break; break;
case ARROW_RIGHT: case ARROW_RIGHT:
case CTRL_KEY('f'): case CTRL_KEY('f'):
if (row && editor->curx < row->size) { if (row && editor.curx < row->size) {
editor->curx++; editor.curx++;
} else if (row && editor->curx == row->size) { } else if (row && editor.curx == row->size) {
editor->cury++; editor.cury++;
editor->curx = 0; editor.curx = 0;
} }
break; break;
case ARROW_LEFT: case ARROW_LEFT:
case CTRL_KEY('b'): case CTRL_KEY('b'):
if (editor->curx > 0) { if (editor.curx > 0) {
editor->curx--; editor.curx--;
} else if (editor->cury > 0) { } else if (editor.cury > 0) {
editor->cury--; editor.cury--;
editor->curx = editor->row[editor->cury].size; editor.curx = editor.row[editor.cury].size;
} }
break; break;
case PG_UP: case PG_UP:
case PG_DN: { case PG_DN: {
if (c == PG_UP) { if (c == PG_UP) {
editor->cury = editor->rowoffs; editor.cury = editor.rowoffs;
} else if (c == PG_DN) { } else if (c == PG_DN) {
editor->cury = editor->rowoffs + editor->rows-1; editor.cury = editor.rowoffs + editor.rows-1;
if (editor->cury > editor->nrows) { if (editor.cury > editor.nrows) {
editor->cury = editor->nrows; editor.cury = editor.nrows;
} }
} }
reps = editor->rows; reps = editor.rows;
while (--reps) { while (--reps) {
move_cursor(c == PG_UP ? ARROW_UP : ARROW_DOWN); move_cursor(c == PG_UP ? ARROW_UP : ARROW_DOWN);
} }
@ -660,25 +888,25 @@ move_cursor(int16_t c)
case HOME_KEY: case HOME_KEY:
case CTRL_KEY('a'): case CTRL_KEY('a'):
editor->curx = 0; editor.curx = 0;
break; break;
case END_KEY: case END_KEY:
case CTRL_KEY('e'): case CTRL_KEY('e'):
if (editor->nrows == 0) { if (editor.nrows == 0) {
break; break;
} }
editor->curx = editor->row[editor->cury].size; editor.curx = editor.row[editor.cury].size;
break; break;
default: default:
break; break;
} }
row = (editor->cury >= editor->nrows) ? row = (editor.cury >= editor.nrows) ?
NULL : &editor->row[editor->cury]; NULL : &editor.row[editor.cury];
reps = row ? row->size : 0; reps = row ? row->size : 0;
if (editor->curx > reps) { if (editor.curx > reps) {
editor->curx = reps; editor.curx = reps;
} }
} }
@ -688,20 +916,20 @@ newline()
{ {
struct erow *row = NULL; struct erow *row = NULL;
if (editor->curx == 0) { if (editor.curx == 0) {
erow_insert(editor->cury, "", 0); erow_insert(editor.cury, "", 0);
} else { } else {
row = &editor->row[editor->cury]; row = &editor.row[editor.cury];
erow_insert(editor->cury + 1, &row->line[editor->curx], erow_insert(editor.cury + 1, &row->line[editor.curx],
row->size - editor->curx); row->size - editor.curx);
row = &editor->row[editor->cury]; row = &editor.row[editor.cury];
row->size = editor->curx; row->size = editor.curx;
row->line[row->size] = '\0'; row->line[row->size] = '\0';
erow_update(row); erow_update(row);
} }
editor->cury++; editor.cury++;
editor->curx = 0; editor.curx = 0;
} }
@ -711,9 +939,9 @@ process_kcommand(int16_t c)
switch (c) { switch (c) {
case 'q': case 'q':
case CTRL_KEY('q'): case CTRL_KEY('q'):
if (editor->dirty && editor->dirtyex) { if (editor.dirty && editor.dirtyex) {
editor_set_status("File not saved - C-k q again to quit."); editor_set_status("File not saved - C-k q again to quit.");
editor->dirtyex = 0; editor.dirtyex = 0;
return; return;
} }
exit(0); exit(0);
@ -725,7 +953,7 @@ process_kcommand(int16_t c)
case 'w': case 'w':
exit(save_file()); exit(save_file());
case 'd': case 'd':
while ((editor->row[editor->cury].size - editor->curx) > 0) { while ((editor.row[editor.cury].size - editor.curx) > 0) {
process_normal(DEL_KEY); process_normal(DEL_KEY);
} }
break; break;
@ -734,7 +962,7 @@ process_kcommand(int16_t c)
goto_line(); goto_line();
break; break;
case BACKSPACE: case BACKSPACE:
while (editor->curx > 0) { while (editor.curx > 0) {
process_normal(BACKSPACE); process_normal(BACKSPACE);
} }
break; break;
@ -758,7 +986,7 @@ process_kcommand(int16_t c)
break; break;
} }
editor->dirtyex = 1; editor.dirtyex = 1;
return; return;
} }
@ -776,7 +1004,7 @@ process_normal(int16_t c)
newline(); newline();
break; break;
case CTRL_KEY('k'): case CTRL_KEY('k'):
editor->mode = MODE_KCOMMAND; editor.mode = MODE_KCOMMAND;
return; return;
case BACKSPACE: case BACKSPACE:
case CTRL_KEY('h'): case CTRL_KEY('h'):
@ -792,7 +1020,7 @@ process_normal(int16_t c)
display_refresh(); display_refresh();
break; break;
case ESC_KEY: case ESC_KEY:
editor->mode = MODE_ESCAPE; editor.mode = MODE_ESCAPE;
break; break;
default: default:
if (isprint(c) || c == TAB_KEY) { if (isprint(c) || c == TAB_KEY) {
@ -801,7 +1029,7 @@ process_normal(int16_t c)
break; break;
} }
editor->dirtyex = 1; editor.dirtyex = 1;
} }
@ -810,12 +1038,12 @@ process_escape(int16_t c)
{ {
switch (c) { switch (c) {
case '>': case '>':
editor->cury = editor->nrows; editor.cury = editor.nrows;
editor->curx = 0; editor.curx = 0;
break; break;
case '<': case '<':
editor->cury = 0; editor.cury = 0;
editor->curx = 0; editor.curx = 0;
} }
} }
@ -831,20 +1059,21 @@ process_keypress()
return 0; return 0;
} }
switch (editor->mode) { switch (editor.mode) {
case MODE_KCOMMAND: case MODE_KCOMMAND:
process_kcommand(c); process_kcommand(c);
editor->mode = MODE_NORMAL; editor.mode = MODE_NORMAL;
break; break;
case MODE_NORMAL: case MODE_NORMAL:
process_normal(c); process_normal(c);
break; break;
case MODE_ESCAPE: case MODE_ESCAPE:
process_escape(c); process_escape(c);
editor.mode = MODE_NORMAL;
break; break;
default: default:
editor_set_status("we're in the %d-D space now cap'n", editor->mode); editor_set_status("we're in the %d-D space now cap'n", editor.mode);
editor->mode = MODE_NORMAL; editor.mode = MODE_NORMAL;
} }
return 1; return 1;
@ -911,7 +1140,7 @@ disable_termraw()
{ {
display_clear(NULL); display_clear(NULL);
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &editor->entry_term) == -1) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &editor.entry_term) == -1) {
die("couldn't disable terminal raw mode"); die("couldn't disable terminal raw mode");
} }
} }
@ -920,7 +1149,7 @@ disable_termraw()
void void
setup_terminal() setup_terminal()
{ {
if (tcgetattr(STDIN_FILENO, &editor->entry_term) == -1) { if (tcgetattr(STDIN_FILENO, &editor.entry_term) == -1) {
die("can't snapshot terminal settings"); die("can't snapshot terminal settings");
} }
atexit(disable_termraw); atexit(disable_termraw);
@ -931,19 +1160,19 @@ setup_terminal()
void void
draw_rows(struct abuf *ab) draw_rows(struct abuf *ab)
{ {
assert(editor->cols >= 0); assert(editor.cols >= 0);
char buf[editor->cols]; char buf[editor.cols];
int buflen, filerow, padding; int buflen, filerow, padding;
int y; int y;
for (y = 0; y < editor->rows; y++) { for (y = 0; y < editor.rows; y++) {
filerow = y + editor->rowoffs; filerow = y + editor.rowoffs;
if (filerow >= editor->nrows) { if (filerow >= editor.nrows) {
if ((editor->nrows == 0) && (y == editor->rows / 3)) { if ((editor.nrows == 0) && (y == editor.rows / 3)) {
buflen = snprintf(buf, sizeof(buf), buflen = snprintf(buf, sizeof(buf),
"ke v%s", KE_VERSION); "ke v%s", KE_VERSION);
padding = (editor->rows - buflen) / 2; padding = (editor.rows - buflen) / 2;
if (padding) { if (padding) {
ab_append(ab, "|", 1); ab_append(ab, "|", 1);
@ -956,16 +1185,16 @@ draw_rows(struct abuf *ab)
ab_append(ab, "|", 1); ab_append(ab, "|", 1);
} }
} else { } else {
erow_update(&editor->row[filerow]); erow_update(&editor.row[filerow]);
buflen = editor->row[filerow].rsize - editor->coloffs; buflen = editor.row[filerow].rsize - editor.coloffs;
if (buflen < 0) { if (buflen < 0) {
buflen = 0; buflen = 0;
} }
if (buflen > editor->cols) { if (buflen > editor.cols) {
buflen = editor->cols; buflen = editor.cols;
} }
ab_append(ab, editor->row[filerow].render+editor->coloffs, ab_append(ab, editor.row[filerow].render+editor.coloffs,
buflen); buflen);
} }
ab_append(ab, ESCSEQ "K", 3); ab_append(ab, ESCSEQ "K", 3);
@ -977,7 +1206,7 @@ draw_rows(struct abuf *ab)
static char static char
status_mode_char() status_mode_char()
{ {
switch (editor->mode) { switch (editor.mode) {
case MODE_NORMAL: case MODE_NORMAL:
return 'N'; return 'N';
case MODE_KCOMMAND: case MODE_KCOMMAND:
@ -993,22 +1222,22 @@ status_mode_char()
void void
draw_status_bar(struct abuf *ab) draw_status_bar(struct abuf *ab)
{ {
char status[editor->cols]; char status[editor.cols];
char rstatus[editor->cols]; char rstatus[editor.cols];
int len, rlen; int len, rlen;
len = snprintf(status, sizeof(status), "%c%cke: %.20s - %d lines", len = snprintf(status, sizeof(status), "%c%cke: %.20s - %d lines",
status_mode_char(), status_mode_char(),
editor->dirty ? '!' : '-', editor.dirty ? '!' : '-',
editor->filename ? editor->filename : "[no file]", editor.filename ? editor.filename : "[no file]",
editor->nrows); editor.nrows);
rlen = snprintf(rstatus, sizeof(rstatus), "L%d/%d C%d", rlen = snprintf(rstatus, sizeof(rstatus), "L%d/%d C%d",
editor->cury+1, editor->nrows, editor->curx+1); editor.cury+1, editor.nrows, editor.curx+1);
ab_append(ab, ESCSEQ "7m", 4); ab_append(ab, ESCSEQ "7m", 4);
ab_append(ab, status, len); ab_append(ab, status, len);
while (len < editor->cols) { while (len < editor.cols) {
if ((editor->cols - len) == rlen) { if ((editor.cols - len) == rlen) {
ab_append(ab, rstatus, rlen); ab_append(ab, rstatus, rlen);
break; break;
} }
@ -1023,15 +1252,15 @@ draw_status_bar(struct abuf *ab)
void void
draw_message_line(struct abuf *ab) draw_message_line(struct abuf *ab)
{ {
int len = strlen(editor->msg); int len = strlen(editor.msg);
ab_append(ab, ESCSEQ "K", 3); ab_append(ab, ESCSEQ "K", 3);
if (len > editor->cols) { if (len > editor.cols) {
len = editor->cols; len = editor.cols;
} }
if (len && ((time(NULL) - editor->msgtm) < MSG_TIMEO)) { if (len && ((time(NULL) - editor.msgtm) < MSG_TIMEO)) {
ab_append(ab, editor->msg, len); ab_append(ab, editor.msg, len);
} }
} }
@ -1039,27 +1268,27 @@ draw_message_line(struct abuf *ab)
void void
scroll() scroll()
{ {
editor->rx = 0; editor.rx = 0;
if (editor->cury < editor->nrows) { if (editor.cury < editor.nrows) {
editor->rx = erow_render_to_cursor( editor.rx = erow_render_to_cursor(
&editor->row[editor->cury], &editor.row[editor.cury],
editor->curx); editor.curx);
} }
if (editor->cury < editor->rowoffs) { if (editor.cury < editor.rowoffs) {
editor->rowoffs = editor->cury; editor.rowoffs = editor.cury;
} }
if (editor->cury >= editor->rowoffs + editor->rows) { if (editor.cury >= editor.rowoffs + editor.rows) {
editor->rowoffs = editor->cury - editor->rows + 1; editor.rowoffs = editor.cury - editor.rows + 1;
} }
if (editor->rx < editor->coloffs) { if (editor.rx < editor.coloffs) {
editor->coloffs = editor->rx; editor.coloffs = editor.rx;
} }
if (editor->rx >= editor->coloffs + editor->cols) { if (editor.rx >= editor.coloffs + editor.cols) {
editor->coloffs = editor->rx - editor->cols + 1; editor.coloffs = editor.rx - editor.cols + 1;
} }
} }
@ -1080,8 +1309,8 @@ display_refresh()
draw_message_line(&ab); draw_message_line(&ab);
snprintf(buf, sizeof(buf), ESCSEQ "%d;%dH", snprintf(buf, sizeof(buf), ESCSEQ "%d;%dH",
(editor->cury-editor->rowoffs)+1, (editor.cury-editor.rowoffs)+1,
(editor->rx-editor->coloffs)+1); (editor.rx-editor.coloffs)+1);
ab_append(&ab, buf, strnlen(buf, 32)); ab_append(&ab, buf, strnlen(buf, 32));
/* ab_append(&ab, ESCSEQ "1;2H", 7); */ /* ab_append(&ab, ESCSEQ "1;2H", 7); */
ab_append(&ab, ESCSEQ "?25h", 6); ab_append(&ab, ESCSEQ "?25h", 6);
@ -1097,10 +1326,10 @@ editor_set_status(const char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(editor->msg, sizeof(editor->msg), fmt, ap); vsnprintf(editor.msg, sizeof(editor.msg), fmt, ap);
va_end(ap); va_end(ap);
editor->msgtm = time(NULL); editor.msgtm = time(NULL);
} }
@ -1131,26 +1360,26 @@ loop()
void void
init_editor() init_editor()
{ {
editor->cols = 0; editor.cols = 0;
editor->rows = 0; editor.rows = 0;
if (get_winsz(&editor->rows, &editor->cols) == -1) { if (get_winsz(&editor.rows, &editor.cols) == -1) {
die("can't get window size"); die("can't get window size");
} }
editor->rows--; /* status bar */ editor.rows--; /* status bar */
editor->rows--; /* message line */ editor.rows--; /* message line */
editor->curx = editor->cury = 0; editor.curx = editor.cury = 0;
editor->rx = 0; editor.rx = 0;
editor->nrows = 0; editor.nrows = 0;
editor->rowoffs = editor->coloffs = 0; editor.rowoffs = editor.coloffs = 0;
editor->row = NULL; editor.row = NULL;
editor->msg[0] = '\0'; editor.msg[0] = '\0';
editor->msgtm = 0; editor.msgtm = 0;
editor->dirty = 0; editor.dirty = 0;
} }