@@ -73,21 +73,19 @@
# define calloc1(sz) calloc(1, sz)
static FILE * debug_log = NULL ;
/* append buffer */
struct abuf {
typedef struct abuf {
char * b ;
size_t len ;
size_t cap ;
} ;
} abuf ;
# define ABUF_INIT {NULL, 0, 0}
/* editor row */
struct erow {
typedef struct erow {
char * line ;
char * render ;
@@ -96,14 +94,38 @@ struct erow {
int cap ;
int dirty ;
} ;
} erow ;
typedef enum undo_kind {
UNDO_INSERT = 1 < < 0 ,
UNDO_UNKNOWN = 1 < < 1 ,
} undo_kind ;
typedef struct undo_node {
undo_kind op ;
size_t row , col ;
abuf text ;
struct undo_node * next ;
struct undo_node * parent ;
} undo_node ;
typedef struct undo_tree {
undo_node * root ; /* the start of the undo sequence */
undo_node * current ; /* where we are currently at */
undo_node * pending ; /* the current undo operations being built */
} undo_tree ;
/*
* editor is the global editor state; it should be broken out
* to buffers and screen state, probably.
*/
struct editor_t {
struct editor {
struct termios entry_term ;
int rows , cols ;
int curx , cury ;
@@ -111,8 +133,8 @@ struct editor_t {
int mode ;
int nrows ;
int rowoffs , coloffs ;
struct erow * row ;
struct erow * killring ;
erow * row ;
erow * killring ;
int kill ; /* KILL CHAIN (sounds metal) */
int no_kill ; /* don't kill in delete_row */
char * filename ;
@@ -154,22 +176,22 @@ void reset_editor(void);
int next_power_of_2 ( int n ) ;
int cap_growth ( int cap , int sz ) ;
size_t kstrnlen ( const char * buf , const size_t max ) ;
void ab_init ( struct abuf * buf ) ;
void ab_appendch ( struct abuf * buf , char c ) ;
void ab_append ( struct abuf * buf , const char * s , size_t len ) ;
void ab_prependch ( struct abuf * buf , char c ) ;
void ab_prepend ( struct abuf * buf , const char * s , size_t len ) ;
void ab_free ( struct abuf * buf ) ;
void ab_init ( abuf * buf ) ;
void ab_appendch ( abuf * buf , char c ) ;
void ab_append ( abuf * buf , const char * s , size_t len ) ;
void ab_prependch ( abuf * buf , char c ) ;
void ab_prepend ( abuf * buf , const char * s , size_t len ) ;
void ab_free ( abuf * buf ) ;
char nibble_to_hex ( char c ) ;
void swap_int ( int * a , int * b ) ;
/* editor rows */
int erow_render_to_cursor ( struct erow * row , int cx ) ;
int erow_cursor_to_render ( struct erow * row , int rx ) ;
int erow_init ( struct erow * row , int len ) ;
void erow_update ( struct erow * row ) ;
int erow_render_to_cursor ( erow * row , int cx ) ;
int erow_cursor_to_render ( erow * row , int rx ) ;
int erow_init ( erow * row , int len ) ;
void erow_update ( erow * row ) ;
void erow_insert ( int at , char * s , int len ) ;
void erow_free ( struct erow * row ) ;
void erow_free ( erow * row ) ;
/* kill ring, marking, etc... */
@@ -198,8 +220,8 @@ void delete_next_word(void);
void find_prev_word ( void ) ;
void delete_prev_word ( void ) ;
void delete_row ( int at ) ;
void row_insert_ch ( struct erow * row , int at , int16_t c ) ;
void row_delete_ch ( struct erow * row , int at ) ;
void row_insert_ch ( erow * row , int at , int16_t c ) ;
void row_delete_ch ( erow * row , int at ) ;
void insertch ( int16_t c ) ;
void deletech ( uint8_t op ) ;
void open_file ( const char * filename ) ;
@@ -212,6 +234,7 @@ void editor_find_callback(char *query, int16_t c);
void editor_find ( void ) ;
char * editor_prompt ( char * , void ( * cb ) ( char * , int16_t ) ) ;
void editor_openfile ( void ) ;
int first_nonwhitespace ( erow * row ) ;
void move_cursor_once ( int16_t c , int interactive ) ;
void move_cursor ( int16_t c , int interactive ) ;
void uarg_start ( void ) ;
@@ -226,18 +249,18 @@ int process_keypress(void);
char * get_cloc_code_lines ( const char * filename ) ;
int dump_pidfile ( void ) ;
void enable_termraw ( void ) ;
void display_clear ( struct abuf * ab ) ;
void display_clear ( abuf * ab ) ;
void disable_termraw ( void ) ;
void setup_terminal ( void ) ;
void draw_rows ( struct abuf * ab ) ;
void draw_rows ( abuf * ab ) ;
char status_mode_char ( void ) ;
void draw_status_bar ( struct abuf * ab ) ;
void draw_message_line ( struct abuf * ab ) ;
void draw_status_bar ( abuf * ab ) ;
void draw_message_line ( abuf * ab ) ;
void scroll ( void ) ;
void display_refresh ( void ) ;
void editor_set_status ( const char * fmt , . . . ) ;
void loop ( void ) ;
void enable_debugging ( const char * logfile ) ;
void enable_debugging ( void ) ;
void deathknell ( void ) ;
static void signal_handler ( int sig ) ;
static void install_signal_handlers ( void ) ;
@@ -392,7 +415,7 @@ reset_editor(void)
void
ab_init ( struct abuf * buf )
ab_init ( abuf * buf )
{
buf - > b = NULL ;
buf - > len = 0 ;
@@ -401,14 +424,14 @@ ab_init(struct abuf *buf)
void
ab_appendch ( struct abuf * buf , char c )
ab_appendch ( abuf * buf , char c )
{
ab_append ( buf , & c , 1 ) ;
}
void
ab_append ( struct abuf * buf , const char * s , size_t len )
ab_append ( abuf * buf , const char * s , size_t len )
{
char * nc = buf - > b ;
size_t sz = buf - > len + len ;
@@ -432,14 +455,14 @@ ab_append(struct abuf *buf, const char *s, size_t len)
void
ab_prependch ( struct abuf * buf , const char c )
ab_prependch ( abuf * buf , const char c )
{
ab_prepend ( buf , & c , 1 ) ;
}
void
ab_prepend ( struct abuf * buf , const char * s , const size_t len )
ab_prepend ( abuf * buf , const char * s , const size_t len )
{
char * nc = realloc ( buf - > b , buf - > len + len ) ;
assert ( nc ! = NULL ) ;
@@ -453,7 +476,7 @@ ab_prepend(struct abuf *buf, const char *s, const size_t len)
void
ab_free ( struct abuf * buf )
ab_free ( abuf * buf )
{
free ( buf - > b ) ;
buf - > b = NULL ;
@@ -483,7 +506,7 @@ swap_int(int *a, int *b)
int
erow_render_to_cursor ( struct erow * row , int cx )
erow_render_to_cursor ( erow * row , int cx )
{
int rx = 0 ;
size_t j = 0 ;
@@ -507,6 +530,12 @@ erow_render_to_cursor(struct erow *row, int cx)
continue ;
}
if ( b < 0x80 ) {
rx + + ;
j + + ;
continue ;
}
size_t rem = ( size_t ) row - > size - j ;
size_t n = mbrtowc ( & wc , & row - > line [ j ] , rem , & st ) ;
@@ -538,7 +567,7 @@ erow_render_to_cursor(struct erow *row, int cx)
int
erow_cursor_to_render ( struct erow * row , int rx )
erow_cursor_to_render ( erow * row , int rx )
{
int cur_rx = 0 ;
size_t j = 0 ;
@@ -560,6 +589,9 @@ erow_cursor_to_render(struct erow *row, int rx)
} else if ( b < 0x20 ) {
w = 3 ; /* "\\xx" */
adv = 1 ;
} else if ( b < 0x80 ) {
w = 1 ;
adv = 1 ;
} else {
size_t rem = ( size_t ) row - > size - j ;
size_t n = mbrtowc ( & wc , & row - > line [ j ] , rem , & st ) ;
@@ -594,7 +626,7 @@ erow_cursor_to_render(struct erow *row, int rx)
int
erow_init ( struct erow * row , int len )
erow_init ( erow * row , int len )
{
row - > size = len ;
row - > rsize = 0 ;
@@ -615,7 +647,7 @@ erow_init(struct erow *row, int len)
void
erow_update ( struct erow * row )
erow_update ( erow * row )
{
int i = 0 , j ;
int tabs = 0 ;
@@ -665,7 +697,7 @@ erow_update(struct erow *row)
void
erow_insert ( int at , char * s , int len )
{
struct erow row ;
erow row ;
if ( at < 0 | | at > editor . nrows ) {
return ;
@@ -676,13 +708,13 @@ erow_insert(int at, char *s, int len)
row . line [ len ] = 0 ;
editor . row = realloc ( editor . row ,
sizeof ( struct erow ) * ( editor . nrows + 1 ) ) ;
sizeof ( 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 ) ) ;
sizeof ( erow ) * ( editor . nrows - at ) ) ;
}
editor . row [ at ] = row ;
@@ -692,7 +724,7 @@ erow_insert(int at, char *s, int len)
void
erow_free ( struct erow * row )
erow_free ( erow * row )
{
free ( row - > render ) ;
free ( row - > line ) ;
@@ -737,7 +769,7 @@ killring_yank(void)
void
killring_start_with_char ( unsigned char ch )
{
struct erow * row = NULL ;
erow * row = NULL ;
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
@@ -745,7 +777,7 @@ killring_start_with_char(unsigned char ch)
editor . killring = NULL ;
}
editor . killring = malloc ( sizeof ( struct erow ) ) ;
editor . killring = malloc ( sizeof ( erow ) ) ;
assert ( editor . killring ! = NULL ) ;
assert ( erow_init ( editor . killring , 0 ) = = 0 ) ;
@@ -764,7 +796,7 @@ killring_start_with_char(unsigned char ch)
void
killring_append_char ( unsigned char ch )
{
struct erow * row = NULL ;
erow * row = NULL ;
if ( editor . killring = = NULL ) {
killring_start_with_char ( ch ) ;
@@ -789,7 +821,7 @@ killring_prepend_char(unsigned char ch)
return ;
}
struct erow * row = editor . killring ;
erow * row = editor . killring ;
row - > line = realloc ( row - > line , row - > size + 2 ) ;
assert ( row - > line ! = NULL ) ;
memmove ( & row - > line [ 1 ] , & row - > line [ 0 ] , row - > size + 1 ) ;
@@ -957,8 +989,8 @@ indent_region(void)
void
unindent_region ( void )
{
int start_row , end_row , i , del ;
struct erow * row ;
int start_row , end_row , i , del ;
erow * row ;
if ( ! editor . mark_set ) {
editor_set_status ( " Mark not set. " ) ;
@@ -1122,7 +1154,6 @@ jump_to_position(int col, int row)
editor . curx = col ;
editor . cury = row ;
scroll ( ) ;
display_refresh ( ) ;
}
@@ -1283,6 +1314,8 @@ delete_prev_word(void)
void
delete_row ( int at )
{
erow * row = NULL ;
if ( at < 0 | | at > = editor . nrows ) {
return ;
}
@@ -1294,21 +1327,21 @@ delete_row(int at)
* newline itself and we must NOT also push the entire row here.
*/
if ( ! editor . no_kill ) {
struct erow * r = & editor . row [ at ] ;
row = & editor . row [ at ] ;
/* Start or continue the kill sequence based on editor.killing */
if ( r - > size > 0 ) {
if ( row - > size > 0 ) {
/* push raw bytes of the line */
if ( ! editor . kill ) {
killring_start_with_char (
( unsigned char ) r - > line [ 0 ] ) ;
for ( int i = 1 ; i < r - > size ; i + + ) {
( unsigned char ) row - > line [ 0 ] ) ;
for ( int i = 1 ; i < row - > size ; i + + ) {
killring_append_char (
( unsigned char ) r - > line [ i ] ) ;
( unsigned char ) row - > line [ i ] ) ;
}
} else {
for ( int i = 0 ; i < r - > size ; i + + ) {
for ( int i = 0 ; i < row - > size ; i + + ) {
killring_append_char (
( unsigned char ) r - > line [ i ] ) ;
( unsigned char ) row - > line [ i ] ) ;
}
}
killring_append_char ( ' \n ' ) ;
@@ -1326,14 +1359,14 @@ delete_row(int at)
erow_free ( & editor . row [ at ] ) ;
memmove ( & editor . row [ at ] ,
& editor . row [ at + 1 ] ,
sizeof ( struct erow ) * ( editor . nrows - at - 1 ) ) ;
sizeof ( erow ) * ( editor . nrows - at - 1 ) ) ;
editor . nrows - - ;
editor . dirty + + ;
}
void
row_append_row ( struct erow * row , char * s , int len )
row_append_row ( erow * row , char * s , int len )
{
row - > line = realloc ( row - > line , row - > size + len + 1 ) ;
assert ( row - > line ! = NULL ) ;
@@ -1346,7 +1379,7 @@ row_append_row(struct erow *row, char *s, int len)
void
row_insert_ch ( struct erow * row , int at , int16_t c )
row_insert_ch ( erow * row , int at , int16_t c )
{
/*
* row_insert_ch just concerns itself with how to update a row.
@@ -1367,7 +1400,7 @@ row_insert_ch(struct erow *row, int at, int16_t c)
void
row_delete_ch ( struct erow * row , int at )
row_delete_ch ( erow * row , int at )
{
if ( at < 0 | | at > = row - > size ) {
return ;
@@ -1407,8 +1440,9 @@ insertch(int16_t c)
void
deletech ( uint8_t op )
{
struct erow * row = NULL ;
erow * row = NULL ;
unsigned char dch = 0 ;
int prev = 0 ;
if ( editor . cury > = editor . nrows ) {
return ;
@@ -1433,7 +1467,8 @@ deletech(uint8_t op)
row_append_row ( & editor . row [ editor . cury - 1 ] ,
row - > line ,
row - > size ) ;
int prev = editor . no_kill ;
prev = editor . no_kill ;
editor . no_kill = 1 ;
delete_row ( editor . cury ) ;
@@ -1781,7 +1816,7 @@ editor_find_callback(char* query, int16_t c)
static int last_match = - 1 ; /* row index of last match */
static int direction = 1 ; /* 1 = forward, -1 = backward */
static char last_query [ 128 ] = { 0 } ; /* remember last successful query */
struct erow * row ;
erow * row ;
int saved_cx = editor . curx ;
int saved_cy = editor . cury ;
size_t qlen = strlen ( query ) ;
@@ -1910,7 +1945,7 @@ editor_openfile(void)
int
first_nonwhitespace ( struct erow * row )
first_nonwhitespace ( erow * row )
{
int pos ;
wchar_t wc ;
@@ -1928,6 +1963,14 @@ first_nonwhitespace(struct erow *row)
}
while ( pos < row - > size ) {
if ( ( unsigned char ) row - > line [ pos ] < 0x80 ) {
if ( ! isspace ( ( unsigned char ) row - > line [ pos ] ) ) {
return pos ;
}
pos + + ;
continue ;
}
len = mbrtowc ( & wc , & row - > line [ pos ] , row - > size - pos , & state ) ;
if ( len = = ( size_t ) - 1 | | len = = ( size_t ) - 2 ) {
break ;
@@ -1951,8 +1994,8 @@ first_nonwhitespace(struct erow *row)
void
move_cursor_once ( int16_t c , int interactive )
{
struct erow * row ;
int reps = 0 ;
erow * row ;
int reps = 0 ;
row = ( editor . cury > = editor . nrows ) ? NULL : & editor . row [ editor . cury ] ;
@@ -2071,7 +2114,7 @@ move_cursor(int16_t c, int interactive)
void
newline ( void )
{
struct erow * row = NULL ;
erow * row = NULL ;
if ( editor . cury > = editor . nrows ) {
erow_insert ( editor . cury , " " , 0 ) ;
@@ -2303,8 +2346,10 @@ process_kcommand(int16_t c)
reset_editor ( ) ;
open_file ( buf ) ;
display_refresh ( ) ;
free ( buf ) ;
jump_to_position ( jumpx , jumpy ) ;
editor_set_status ( " file reloaded " ) ;
break ;
case CTRL_KEY ( ' s ' ) :
case ' s ' :
@@ -2348,7 +2393,9 @@ process_kcommand(int16_t c)
void
process_normal ( int16_t c )
{
int rep s = 0 ;
int col s = 0 ;
int rows = 0 ;
int reps = 0 ;
/* C-u handling – must be the very first thing */
if ( c = = CTRL_KEY ( ' u ' ) ) {
@@ -2404,6 +2451,12 @@ process_normal(int16_t c)
case CTRL_KEY ( ' g ' ) :
break ;
case CTRL_KEY ( ' l ' ) :
if ( get_winsz ( & rows , & cols ) = = 0 ) {
editor . rows = rows ;
editor . cols = cols ;
} else {
editor_set_status ( " Couldn't update window size. " ) ;
}
display_refresh ( ) ;
break ;
case CTRL_KEY ( ' s ' ) :
@@ -2449,6 +2502,8 @@ process_normal(int16_t c)
void
process_escape ( int16_t c )
{
int reps = 0 ;
editor_set_status ( " hi " ) ;
switch ( c ) {
@@ -2461,13 +2516,25 @@ process_escape(int16_t c)
editor . curx = 0 ;
break ;
case ' b ' :
find_prev_word ( ) ;
reps = uarg_get ( ) ;
while ( reps - - ) {
find_prev_word ( ) ;
}
break ;
case ' d ' :
delete_next_word ( ) ;
reps = uarg_get ( ) ;
while ( reps - - ) {
delete_next_word ( ) ;
}
break ;
case ' f ' :
find_next_word ( ) ;
reps = uarg_get ( ) ;
while ( reps - - ) {
find_next_word ( ) ;
}
break ;
case ' m ' :
toggle_markset ( ) ;
@@ -2481,7 +2548,11 @@ process_escape(int16_t c)
toggle_markset ( ) ;
break ;
case BACKSPACE :
delete_prev_word ( ) ;
reps = uarg_get ( ) ;
while ( reps - - ) {
delete_prev_word ( ) ;
}
break ;
case ESC_KEY :
case CTRL_KEY ( ' g ' ) :
@@ -2489,6 +2560,8 @@ process_escape(int16_t c)
default :
editor_set_status ( " unknown ESC key: %04x " , c ) ;
}
uarg_clear ( ) ;
}
@@ -2648,7 +2721,7 @@ enable_termraw(void)
void
display_clear ( struct abuf * ab )
display_clear ( abuf * ab )
{
if ( ab = = NULL ) {
kwrite ( STDOUT_FILENO , ESCSEQ " 2J " , 4 ) ;
@@ -2683,14 +2756,14 @@ setup_terminal(void)
void
draw_rows ( struct abuf * ab )
draw_rows ( abuf * ab )
{
assert ( editor . cols > = 0 ) ;
struct erow * row ;
char buf [ editor . cols ] ;
int len , filerow , padding ;
int y ;
erow * row ;
char buf [ editor . cols ] ;
int len , filerow , padding ;
int y ;
for ( y = 0 ; y < editor . rows ; y + + ) {
filerow = y + editor . rowoffs ;
@@ -2755,7 +2828,7 @@ status_mode_char(void)
void
draw_status_bar ( struct abuf * ab )
draw_status_bar ( abuf * ab )
{
char status [ editor . cols ] ;
char rstatus [ editor . cols ] ;
@@ -2805,7 +2878,7 @@ draw_status_bar(struct abuf *ab)
void
draw_message_line ( struct abuf * ab )
draw_message_line ( abuf * ab )
{
int len = strlen ( editor . msg ) ;
@@ -2823,7 +2896,8 @@ draw_message_line(struct abuf *ab)
void
scroll ( void )
{
struct erow * row = NULL ;
erow * row = NULL ;
editor . rx = 0 ;
if ( editor . cury < editor . nrows ) {
row = & editor . row [ editor . cury ] ;
@@ -2855,8 +2929,8 @@ scroll(void)
void
display_refresh ( void )
{
char buf [ 32 ] ;
struct abuf ab = ABUF_INIT ;
char buf [ 32 ] ;
abuf ab = ABUF_INIT ;
scroll ( ) ;
@@ -2875,7 +2949,7 @@ display_refresh(void)
( editor . rx - editor . coloffs ) + 1 ) ;
ab_append ( & ab , buf , kstrnlen ( buf , 32 ) ) ;
/* ab_append(&ab, ESCSEQ "1;2H", 7); */
ab_append ( & ab , ESCSEQ " ?25l " , 6 ) ;
ab_append ( & ab , ESCSEQ " ?25h " , 6 ) ;
kwrite ( STDOUT_FILENO , ab . b , ab . len ) ;
ab_free ( & ab ) ;
@@ -2900,6 +2974,15 @@ kbhit(void)
{
int bytes_waiting ;
ioctl ( STDIN_FILENO , FIONREAD , & bytes_waiting ) ;
if ( bytes_waiting < 0 ) {
editor_set_status ( " kbhit: FIONREAD failed: %s " , strerror ( errno ) ) ;
/* if FIONREAD fails, we need to assume we should read. this
* will default to a much slower input sequence, but it'll work.
*/
return 1 ;
}
return bytes_waiting > 0 ;
}
@@ -2929,21 +3012,11 @@ loop(void)
void
enable_debugging ( const char * logfile )
enable_debugging ( void )
{
time_t now ;
if ( debug_log ! = NULL ) {
fclose ( debug_log ) ;
}
if ( ( debug_log = fopen ( logfile , " w " ) ) = = NULL ) {
fprintf ( stderr , " Failed to open error log! \n " ) ;
fprintf ( stderr , " \t %s \n " , strerror ( errno ) ) ;
}
dump_pidfile ( ) ;
stderr = debug_log ;
now = time ( & now ) ;
printf ( " time: %s \n " , ctime ( & now ) ) ;
@@ -2955,12 +3028,7 @@ enable_debugging(const char *logfile)
void
deathknell ( void )
{
if ( debug_log ! = NULL ) {
fflush ( stderr ) ;
fclose ( debug_log ) ;
debug_log = NULL ;
}
fflush ( stderr ) ;
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
@@ -3017,9 +3085,12 @@ install_signal_handlers(void)
int
main ( int argc , char * argv [ ] )
{
char * logfil e = " debug-ke.log " ;
int opt ;
int debug = 0 ;
char * fnam e = NULL ;
char * lnarg = NULL ;
int lineno = 0 ;
int opt ;
int debug = 0 ;
int jump = 0 ;
install_signal_handlers ( ) ;
@@ -3028,9 +3099,6 @@ main(int argc, char *argv[])
case ' d ' :
debug = 1 ;
break ;
case ' f ' :
logfile = optarg ;
break ;
default :
fprintf ( stderr , " Usage: ke [-d] [-f logfile] [path] \n " ) ;
exit ( EXIT_FAILURE ) ;
@@ -3042,17 +3110,38 @@ main(int argc, char *argv[])
setlocale ( LC_ALL , " " ) ;
if ( debug ) {
enable_debugging ( logfile ) ;
enable_debugging ( ) ;
}
setup_terminal ( ) ;
init_editor ( ) ;
if ( argc > 0 ) {
open_file ( argv [ 0 ] ) ;
fname = argv [ 0 ] ;
}
if ( argc > 1 ) {
lnarg = argv [ 0 ] ;
fname = argv [ 1 ] ;
if ( lnarg [ 0 ] = = ' + ' ) {
lnarg + + ;
}
lineno = atoi ( lnarg ) ;
jump = 1 ;
}
if ( fname ! = NULL ) {
open_file ( fname ) ;
}
editor_set_status ( " C-k q to exit / C-k d to dump core " ) ;
if ( jump ) {
if ( lineno < 1 ) {
editor_set_status ( " Invalid line number %s " , lnarg ) ;
} else {
jump_to_position ( 0 , lineno - 1 ) ;
}
}
display_clear ( NULL ) ;
loop ( ) ;