@@ -19,6 +19,8 @@
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <locale.h>
# include <wchar.h>
# include <termios.h>
# include <time.h>
# include <unistd.h>
@@ -49,6 +51,13 @@
# define INITIAL_CAPACITY 64
# define KILLRING_NO_OP 0 /* don't touch the killring */
# define KILLRING_APPEND 1 /* append deleted chars */
# define KILLRING_PREPEND 2 /* prepend deleted chars */
# define KILLING_SET 3 /* set killring to deleted char */
# define KILLRING_FLUSH 4 /* clear the killring */
int
next_power_of_2 ( int n )
{
@@ -122,12 +131,22 @@ void editor_set_status(const char *fmt, ...);
void die ( const char * s ) ;
int get_winsz ( int * rows , int * cols ) ;
void goto_line ( void ) ;
int cursor_at_eol ( void ) ;
void find_next_word ( void ) ;
void delete_next_word ( void ) ;
void find_prev_word ( void ) ;
void delete_prev_word ( void ) ;
void delete_row ( int at ) ;
void killring_start_with_char ( unsigned char ch ) ;
void killring_append_char ( unsigned char ch ) ;
void killring_prepend_char ( unsigned char ch ) ;
void killring_flush ( void ) ;
void killring_yank ( void ) ;
void row_append_row ( struct erow * row , char * s , int len ) ;
void row_insert_ch ( struct erow * row , int at , int16_t c ) ;
void row_delete_ch ( struct erow * row , int at ) ;
void insertch ( int16_t c ) ;
void deletech ( void ) ;
void deletech ( uint8_t op ) ;
void open_file ( const char * filename ) ;
char * rows_to_buffer ( int * buflen ) ;
int save_file ( void ) ;
@@ -189,6 +208,9 @@ struct editor_t {
int nrows ;
int rowoffs , coloffs ;
struct erow * row ;
struct erow * killring ;
int killing ; /* are we in a contiguous delete sequence? */
int suppress_killrow ; /* internal flag: don't add to killring inside delete_row */
char * filename ;
int dirty ;
int dirtyex ;
@@ -204,6 +226,9 @@ struct editor_t {
. rowoffs = 0 ,
. coloffs = 0 ,
. row = NULL ,
. killring = NULL ,
. killing = 0 ,
. suppress_killrow = 0 ,
. filename = NULL ,
. dirty = 0 ,
. dirtyex = 0 ,
@@ -231,6 +256,9 @@ init_editor(void)
editor . nrows = 0 ;
editor . rowoffs = editor . coloffs = 0 ;
editor . row = NULL ;
editor . killring = NULL ;
editor . killing = 0 ;
editor . suppress_killrow = 0 ;
editor . msg [ 0 ] = ' \0 ' ;
editor . msgtm = 0 ;
@@ -256,6 +284,12 @@ reset_editor(void)
editor . filename = NULL ;
}
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
free ( editor . killring ) ;
editor . killring = NULL ;
}
init_editor ( ) ;
}
@@ -298,25 +332,64 @@ nibble_to_hex(char c)
{
c & = 0xf ;
if ( c < 10 ) {
return c + 0x30 ;
return ( char ) ( ' 0 ' + c ) ;
}
return c + 0x41 ;
return ( char ) ( ' A ' + ( c - 10 ) ) ;
}
int
erow_render_to_cursor ( struct erow * row , int cx )
{
int rx = 0 ;
in t j ;
int rx = 0 ;
size_ t j = 0 ;
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 ;
wchar_t wc ;
mbstate_t st ;
memset ( & st , 0 , sizeof ( st ) ) ;
while ( j < ( size_t ) cx & & j < ( size_t ) row - > size ) {
unsigned char b = ( unsigned char ) row - > line [ j ] ;
if ( b = = ' \t ' ) {
rx + = ( TAB_STOP - 1 ) - ( rx % TAB_STOP ) ;
rx + + ;
j + + ;
continue ;
}
if ( b < 0x20 ) {
/* render as \xx -> width 3 */
rx + = 3 ;
j + + ;
continue ;
}
size_t rem = ( size_t ) row - > size - j ;
size_t n = mbrtowc ( & wc , & row - > line [ j ] , rem , & st ) ;
if ( n = = ( size_t ) - 2 ) {
/* incomplete sequence at end; treat one byte */
rx + = 1 ;
j + = 1 ;
memset ( & st , 0 , sizeof ( st ) ) ;
}
else if ( n = = ( size_t ) - 1 ) {
/* invalid byte; consume one and reset state */
rx + = 1 ;
j + = 1 ;
memset ( & st , 0 , sizeof ( st ) ) ;
}
else if ( n = = 0 ) {
/* null character */
rx + = 0 ;
j + = 1 ;
}
else {
int w = wcwidth ( wc ) ;
if ( w < 0 ) w = 1 ; /* non-printable -> treat as width 1 */
rx + = w ;
j + = n ;
}
rx + + ;
}
return rx ;
@@ -326,23 +399,60 @@ erow_render_to_cursor(struct erow *row, int cx)
int
erow_cursor_to_render ( struct erow * row , int rx )
{
int cur_rx = 0 ;
int curx = 0 ;
int cur_rx = 0 ;
size_t j = 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 ;
wchar_t wc ;
mbstate_t st ;
memset ( & st , 0 , sizeof ( st ) ) ;
while ( j < ( size_t ) row - > size ) {
int w = 0 ;
size_t adv = 1 ;
unsigned char b = ( unsigned char ) row - > line [ j ] ;
if ( b = = ' \t ' ) {
int add = ( TAB_STOP - 1 ) - ( cur_rx % TAB_STOP ) ;
w = add + 1 ;
adv = 1 ;
/* tabs are single byte */
}
cur_rx + + ;
else if ( b < 0x20 ) {
w = 3 ; /* "\\xx" */
adv = 1 ;
}
else {
size_t rem = ( size_t ) row - > size - j ;
size_t n = mbrtowc ( & wc , & row - > line [ j ] , rem , & st ) ;
if ( cur_rx > rx ) {
if ( n = = ( size_t ) - 2 | | n = = ( size_t ) - 1 ) {
/* invalid/incomplete */
w = 1 ;
adv = 1 ;
memset ( & st , 0 , sizeof ( st ) ) ;
}
else if ( n = = 0 ) {
w = 0 ;
adv = 1 ;
}
else {
int ww = wcwidth ( wc ) ;
if ( ww < 0 ) ww = 1 ;
w = ww ;
adv = n ;
}
}
if ( cur_rx + w > rx ) {
break ;
}
cur_rx + = w ;
j + = adv ;
}
return curx ;
return ( int ) j ;
}
@@ -359,29 +469,35 @@ erow_update(struct erow *row)
for ( j = 0 ; j < row - > size ; j + + ) {
if ( row - > line [ j ] = = ' \t ' ) {
tabs + + ;
} else if ( ! isprint ( row - > line [ j ] ) ) {
}
else if ( ( unsigned char ) row - > line [ j ] < 0x20 ) {
/* treat only ASCII control characters as non-printable */
ctrl + + ;
}
}
if ( row - > rsize ) {
if ( row - > rsize | | row - > render ! = NULL ) {
free ( row - > render ) ;
row - > rsize = 0 ;
}
row - > render = NULL ;
row - > render = malloc ( row - > size + ( tabs * ( TAB_STOP - 1 ) ) + ( ctrl * 3 ) + 1 ) ;
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 ] ) ) {
}
while ( ( i % TAB_STOP ) ! = 0 ) ;
}
else if ( ( unsigned char ) row - > line [ j ] < 0x20 ) {
row - > render [ i + + ] = ' \\ ' ;
row - > render [ i + + ] = nibble_to_hex ( row - > line [ j ] > > 4 ) ;
row - > render [ i + + ] = nibble_to_hex ( row - > line [ j ] & 0x0f ) ;
} else {
}
else {
/* leave UTF-8 multibyte bytes untouched so terminal can render */
row - > render [ i + + ] = row - > line [ j ] ;
}
}
@@ -438,6 +554,105 @@ erow_insert(int at, char *s, int len)
}
void
killring_flush ( void )
{
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
free ( editor . killring ) ;
editor . killring = NULL ;
}
}
void
killring_yank ( void )
{
if ( editor . killring = = NULL ) {
return ;
}
/*
* Insert killring contents at the cursor without clearing the ring.
* Interpret '\n' as an actual newline() rather than inserting a raw 0x0A
* byte, so yanked content preserves lines correctly.
*/
for ( int i = 0 ; i < editor . killring - > size ; i + + ) {
unsigned char ch = ( unsigned char ) editor . killring - > line [ i ] ;
if ( ch = = ' \n ' ) {
newline ( ) ;
}
else {
insertch ( ch ) ;
}
}
}
void
killring_start_with_char ( unsigned char ch )
{
struct erow * row = NULL ;
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
free ( editor . killring ) ;
editor . killring = NULL ;
}
editor . killring = malloc ( sizeof ( struct erow ) ) ;
assert ( editor . killring ! = NULL ) ;
assert ( erow_init ( editor . killring , 0 ) = = 0 ) ;
/* append one char to empty killring without affecting editor.dirty */
row = editor . killring ;
row - > line = realloc ( row - > line , row - > size + 2 ) ;
assert ( row - > line ! = NULL ) ;
row - > line [ row - > size ] = ch ;
row - > size + + ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
}
void
killring_append_char ( unsigned char ch )
{
struct erow * row = NULL ;
if ( editor . killring = = NULL ) {
killring_start_with_char ( ch ) ;
return ;
}
row = editor . killring ;
row - > line = realloc ( row - > line , row - > size + 2 ) ;
assert ( row - > line ! = NULL ) ;
row - > line [ row - > size ] = ch ;
row - > size + + ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
}
void
killring_prepend_char ( unsigned char ch )
{
if ( editor . killring = = NULL ) {
killring_start_with_char ( ch ) ;
return ;
}
struct 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 ) ;
row - > line [ 0 ] = ch ;
row - > size + + ;
erow_update ( row ) ;
}
void
erow_free ( struct erow * row )
{
@@ -463,6 +678,7 @@ die(const char *s)
}
/*
* get_winsz uses the TIOCGWINSZ to get the window size.
*
@@ -508,6 +724,123 @@ goto_line(void)
}
int
cursor_at_eol ( void )
{
assert ( editor . curx > = 0 ) ;
assert ( editor . cury > = 0 ) ;
assert ( editor . cury < = editor . nrows ) ;
assert ( editor . curx < = editor . row [ editor . cury ] . size ) ;
return editor . curx = = editor . row [ editor . cury ] . size ;
}
void
find_next_word ( void )
{
while ( cursor_at_eol ( ) ) {
move_cursor ( ARROW_RIGHT ) ;
}
if ( isalnum ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
while ( ! isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) & & ! cursor_at_eol ( ) ) {
move_cursor ( ARROW_RIGHT ) ;
}
return ;
}
if ( isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
while ( isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
move_cursor ( ARROW_RIGHT ) ;
}
find_next_word ( ) ;
}
}
void
delete_next_word ( void )
{
while ( cursor_at_eol ( ) ) {
move_cursor ( ARROW_RIGHT ) ;
deletech ( KILLRING_APPEND ) ;
}
if ( isalnum ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
while ( ! isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) & & ! cursor_at_eol ( ) ) {
move_cursor ( ARROW_RIGHT ) ;
deletech ( KILLRING_APPEND ) ;
}
return ;
}
if ( isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
while ( isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
move_cursor ( ARROW_RIGHT ) ;
deletech ( KILLRING_APPEND ) ;
}
delete_next_word ( ) ;
}
}
void
find_prev_word ( void )
{
if ( editor . cury = = 0 & & editor . curx = = 0 ) {
return ;
}
move_cursor ( ARROW_LEFT ) ;
while ( cursor_at_eol ( ) | | isspace ( editor . row [ editor . cury ] . line [ editor . curx ] ) ) {
if ( editor . cury = = 0 & & editor . curx = = 0 ) {
return ;
}
move_cursor ( ARROW_LEFT ) ;
}
while ( editor . curx > 0 & & ! isspace ( editor . row [ editor . cury ] . line [ editor . curx - 1 ] ) ) {
move_cursor ( ARROW_LEFT ) ;
}
}
void
delete_prev_word ( void )
{
if ( editor . cury = = 0 & & editor . curx = = 0 ) {
return ;
}
deletech ( KILLRING_PREPEND ) ;
while ( editor . cury > 0 | | editor . curx > 0 ) {
if ( editor . curx = = 0 ) {
deletech ( KILLRING_PREPEND ) ;
continue
}
if ( ! isspace ( editor . row [ editor . cury ] . line [ editor . curx - 1 ] ) ) {
break ;
}
deletech ( KILLRING_PREPEND ) ;
}
while ( editor . curx > 0 ) {
if ( isspace ( editor . row [ editor . cury ] . line [ editor . curx - 1 ] ) ) {
break ;
}
deletech ( KILLRING_PREPEND ) ;
}
}
void
delete_row ( int at )
{
@@ -515,9 +848,43 @@ delete_row(int at)
return ;
}
/*
* Update killring with the deleted row's contents followed by a newline
* unless this deletion is an internal merge triggered by deletech at
* start-of-line. In that case, deletech will account for the single
* newline itself and we must NOT also push the entire row here.
*/
if ( ! editor . suppress_killrow ) {
struct erow * r = & editor . row [ at ] ;
/* Start or continue the kill sequence based on editor.killing */
if ( r - > size > 0 ) {
/* push raw bytes of the line */
if ( ! editor . killing ) {
killring_start_with_char ( ( unsigned char ) r - > line [ 0 ] ) ;
for ( int i = 1 ; i < r - > size ; i + + ) {
killring_append_char ( ( unsigned char ) r - > line [ i ] ) ;
}
} else {
for ( int i = 0 ; i < r - > size ; i + + ) {
killring_append_char ( ( unsigned char ) r - > line [ i ] ) ;
}
}
killring_append_char ( ' \n ' ) ;
editor . killing = 1 ;
} else {
if ( ! editor . killing ) {
killring_start_with_char ( ' \n ' ) ;
}
else {
killring_append_char ( ' \n ' ) ;
}
editor . killing = 1 ;
}
}
erow_free ( & editor . row [ at ] ) ;
memmove ( & editor . row [ at ] , & editor . row [ at + 1 ] ,
sizeof ( struct erow ) * ( editor . nrows - at - 1 ) ) ;
memmove ( & editor . row [ at ] , & editor . row [ at + 1 ] ,
sizeof ( struct erow ) * ( editor . nrows - at - 1 ) ) ;
editor . nrows - - ;
editor . dirty + + ;
}
@@ -563,6 +930,7 @@ row_delete_ch(struct erow *row, int at)
if ( at < 0 | | at > = row - > size ) {
return ;
}
memmove ( & row - > line [ at ] , & row - > line [ at + 1 ] , row - > size - at ) ;
row - > size - - ;
erow_update ( row ) ;
@@ -582,16 +950,24 @@ insertch(int16_t c)
erow_insert ( editor . nrows , " " , 0 ) ;
}
row_insert_ch ( & editor . row [ editor . cury ] , editor . curx , ( char ) ( c & 0xff ) ) ;
/* Any insertion breaks a delete sequence for killring chaining. */
editor . killing = 0 ;
/* Ensure we pass a non-negative byte value to avoid assert(c > 0). */
row_insert_ch ( & editor . row [ editor . cury ] , editor . curx ,
( int16_t ) ( c & 0xff ) ) ;
editor . curx + + ;
editor . dirty + + ;
}
/*
* deletech
*/
void
deletech ( void )
deletech ( uint8_t op )
{
struct erow * row = NULL ;
struct erow * row = NULL ;
unsigned char dch = 0 ;
if ( editor . cury > = editor . nrows ) {
return ;
@@ -602,16 +978,60 @@ deletech(void)
}
row = & editor . row [ editor . cury ] ;
/* determine which character is being deleted for killring purposes */
if ( editor . curx > 0 ) {
dch = ( unsigned char ) row - > line [ editor . curx - 1 ] ;
}
else {
/* deleting at start of line merges with previous line: treat as newline */
dch = ' \n ' ;
}
/* update buffer */
if ( editor . curx > 0 ) {
row_delete_ch ( row , editor . curx - 1 ) ;
editor . curx - - ;
} else {
}
else {
editor . curx = editor . row [ editor . cury - 1 ] . size ;
row_append_row ( & editor . row [ editor . cury - 1 ] , row - > line ,
row - > size ) ;
row - > size ) ;
int prev = editor . suppress_killrow ;
editor . suppress_killrow = 1 ; /* prevent killring update on internal row delete */
delete_row ( editor . cury ) ;
editor . suppress_killrow = prev ;
editor . cury - - ;
}
/* update killring if requested */
if ( op = = KILLRING_FLUSH ) {
killring_flush ( ) ;
editor . killing = 0 ;
return ;
}
if ( op = = KILLRING_NO_OP ) {
/* Do not modify killring or chaining state. */
return ;
}
if ( ! editor . killing ) {
killring_start_with_char ( dch ) ;
editor . killing = 1 ;
return ;
}
if ( op = = KILLING_SET ) {
killring_start_with_char ( dch ) ;
editor . killing = 1 ;
}
else if ( op = = KILLRING_APPEND ) {
killring_append_char ( dch ) ;
}
else if ( op = = KILLRING_PREPEND ) {
killring_prepend_char ( dch ) ;
}
}
@@ -627,7 +1047,7 @@ open_file(const char *filename)
editor . filename = strdup ( filename ) ;
assert ( editor . filename ! = NULL ) ;
editor . dirty = 0 ;
if ( ( fp = fopen ( filename , " r " ) ) = = NULL ) {
if ( errno = = ENOENT ) {
@@ -779,13 +1199,17 @@ is_arrow_key(int16_t c)
int16_t
get_keypress ( void )
{
char seq [ 3 ] ;
char c = - 1 ;
char seq [ 3 ] ;
/* read raw byte so UTF-8 bytes (>=0x80) are not sign-extended */
unsigned char uc = 0 ;
int16_t c ;
if ( read ( STDIN_FILENO , & c , 1 ) = = - 1 ) {
if ( read ( STDIN_FILENO , & u c, 1 ) = = - 1 ) {
die ( " get_keypress:read " ) ;
}
c = ( int16_t ) uc ;
if ( c = = 0x1b ) {
if ( read ( STDIN_FILENO , & seq [ 0 ] , 1 ) ! = 1 ) return c ;
if ( read ( STDIN_FILENO , & seq [ 1 ] , 1 ) ! = 1 ) return c ;
@@ -793,7 +1217,7 @@ get_keypress(void)
if ( seq [ 0 ] = = ' [ ' ) {
if ( seq [ 1 ] < ' A ' ) {
if ( read ( STDIN_FILENO , & seq [ 2 ] , 1 ) ! = 1 )
return c ;
return c ;
if ( seq [ 2 ] = = ' ~ ' ) {
switch ( seq [ 1 ] ) {
case ' 1 ' : return HOME_KEY ;
@@ -805,7 +1229,8 @@ get_keypress(void)
case ' 8 ' : return END_KEY ;
}
}
} else {
}
else {
switch ( seq [ 1 ] ) {
case ' A ' : return ARROW_UP ;
case ' B ' : return ARROW_DOWN ;
@@ -818,7 +1243,8 @@ get_keypress(void)
/* nada */ ;
}
}
} else if ( seq [ 0 ] = = ' O ' ) {
}
else if ( seq [ 0 ] = = ' O ' ) {
switch ( seq [ 1 ] ) {
case ' F ' : return END_KEY ;
case ' H ' : return HOME_KEY ;
@@ -865,14 +1291,14 @@ editor_prompt(char *prompt, void (*cb)(char *, int16_t))
}
return buf ;
}
} else if ( ! iscntrl ( c ) & & c < 128 ) {
} else if ( ( c = = TAB_KEY ) | | ( c > = 0x20 & & c ! = 0x7f ) ) {
if ( buflen = = bufsz - 1 ) {
bufsz * = 2 ;
buf = realloc ( buf , bufsz ) ;
assert ( buf ! = NULL ) ;
}
buf [ buflen + + ] = c ;
buf [ buflen + + ] = ( char ) ( c & 0xff ) ;
buf [ buflen ] = ' \0 ' ;
}
@@ -984,8 +1410,8 @@ editor_openfile(void)
void
move_cursor ( int16_t c )
{
struct erow * row ;
int reps ;
struct erow * row ;
int reps ;
row = ( editor . cury > = editor . nrows ) ? NULL : & editor . row [ editor . cury ] ;
@@ -1006,7 +1432,13 @@ move_cursor(int16_t c)
case CTRL_KEY ( ' f ' ) :
if ( row & & editor . curx < row - > size ) {
editor . curx + + ;
} else if ( row & & editor . curx = = row - > size ) {
/* skip over UTF-8 continuation bytes */
while ( row & & editor . curx < row - > size & &
( ( unsigned char ) row - > line [ editor . curx ] & 0xC0 ) = = 0x80 ) {
editor . curx + + ;
}
}
else if ( row & & editor . curx = = row - > size ) {
editor . cury + + ;
editor . curx = 0 ;
}
@@ -1015,17 +1447,30 @@ move_cursor(int16_t c)
case CTRL_KEY ( ' b ' ) :
if ( editor . curx > 0 ) {
editor . curx - - ;
} else if ( editor . cury > 0 ) {
/* move to the start byte if we landed on a continuation */
while ( editor . curx > 0 & &
( ( unsigned char ) row - > line [ editor . curx ] & 0xC0 ) = = 0x80 ) {
editor . curx - - ;
}
}
else if ( editor . cury > 0 ) {
editor . cury - - ;
editor . curx = editor . row [ editor . cury ] . size ;
/* ensure at a codepoint boundary at end of previous line */
row = & editor . row [ editor . cury ] ;
while ( editor . curx > 0 & &
( ( unsigned char ) row - > line [ editor . curx ] & 0xC0 ) = = 0x80 ) {
editor . curx - - ;
}
}
break ;
case PG_UP :
case PG_DN : {
case PG_DN :
if ( c = = PG_UP ) {
editor . cury = editor . rowoffs ;
} else if ( c = = PG_DN ) {
editor . cury = editor . rowoffs + editor . rows - 1 ;
}
else if ( c = = PG_DN ) {
editor . cury = editor . rowoffs + editor . rows - 1 ;
if ( editor . cury > editor . nrows ) {
editor . cury = editor . nrows ;
}
@@ -1037,7 +1482,6 @@ move_cursor(int16_t c)
}
break ;
}
case HOME_KEY :
case CTRL_KEY ( ' a ' ) :
@@ -1055,8 +1499,7 @@ move_cursor(int16_t c)
}
row = ( editor . cury > = editor . nrows ) ?
NULL : & editor . row [ editor . cury ] ;
row = ( editor . cury > = editor . nrows ) ? NULL : & editor . row [ editor . cury ] ;
reps = row ? row - > size : 0 ;
if ( editor . curx > reps ) {
editor . curx = reps ;
@@ -1067,22 +1510,24 @@ move_cursor(int16_t c)
void
newline ( void )
{
struct erow * row = NULL ;
struct erow * row = NULL ;
if ( editor . curx = = 0 ) {
erow_insert ( editor . cury , " " , 0 ) ;
} else {
row = & editor . row [ editor . cury ] ;
erow_insert ( editor . cury + 1 , & row - > line [ editor . curx ] ,
row - > size - editor . curx ) ;
row = & editor . row [ editor . cury ] ;
row - > size = editor . curx ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
}
if ( editor . curx = = 0 ) {
erow_insert ( editor . cury , " " , 0 ) ;
} else {
row = & editor . row [ editor . cury ] ;
erow_insert ( editor . cury + 1 , & row - > line [ editor . curx ] ,
row - > size - editor . curx ) ;
row = & editor . row [ editor . cury ] ;
row - > size = editor . curx ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
}
editor . cury + + ;
editor . curx = 0 ;
editor . cury + + ;
editor . curx = 0 ;
/* Any insertion breaks a delete sequence for killring chaining. */
editor . killing = 0 ;
}
@@ -1109,9 +1554,15 @@ process_kcommand(int16_t c)
delete_row ( editor . cury ) ;
break ;
case ' d ' :
if ( editor . curx = = 0 & & cursor_at_eol ( ) ) {
delete_row ( editor . cury ) ;
return ;
}
while ( ( editor . row [ editor . cury ] . size - editor . curx ) > 0 ) {
process_normal ( DEL_KEY ) ;
}
break ;
case ' g ' :
case CTRL_KEY ( ' g ' ) :
@@ -1128,6 +1579,11 @@ process_kcommand(int16_t c)
abort ( ) ;
case ' e ' :
case CTRL_KEY ( ' e ' ) :
if ( editor . dirty & & editor . dirtyex ) {
editor_set_status ( " File not saved - C-k e again to open a new file anyways. " ) ;
editor . dirtyex = 0 ;
return ;
}
editor_openfile ( ) ;
break ;
case ' f ' :
@@ -1140,6 +1596,12 @@ process_kcommand(int16_t c)
editor_set_status ( " make: ok " ) ;
}
break ;
case ' y ' :
killring_yank ( ) ;
break ;
default :
editor_set_status ( " unknown kcommand: %04x " , c ) ;
return ;
}
editor . dirtyex = 1 ;
@@ -1151,6 +1613,8 @@ void
process_normal ( int16_t c )
{
if ( is_arrow_key ( c ) ) {
/* moving the cursor breaks a delete sequence */
editor . killing = 0 ;
move_cursor ( c ) ;
return ;
}
@@ -1168,9 +1632,11 @@ process_normal(int16_t c)
case DEL_KEY :
if ( c = = DEL_KEY | | c = = CTRL_KEY ( ' d ' ) ) {
move_cursor ( ARROW_RIGHT ) ;
deletech ( KILLRING_APPEND ) ;
}
else {
deletech ( KILLRING_PREPEND ) ;
}
deletech ( ) ;
break ;
case CTRL_KEY ( ' l ' ) :
display_refresh ( ) ;
@@ -1182,7 +1648,8 @@ process_normal(int16_t c)
editor . mode = MODE_ESCAPE ;
break ;
default :
if ( isprint ( c ) | | c = = TAB_KEY ) {
/* Insert any printable byte: ASCII 0x20– 0x7E and all bytes >=0x80. */
if ( ( c = = TAB_KEY ) | | ( c > = 0x20 & & c ! = 0x7f ) ) {
insertch ( c ) ;
}
break ;
@@ -1195,8 +1662,6 @@ process_normal(int16_t c)
void
process_escape ( int16_t c )
{
struct erow * row = & editor . row [ editor . cury ] ;
editor_set_status ( " hi " ) ;
switch ( c ) {
@@ -1208,16 +1673,17 @@ process_escape(int16_t c)
editor . cury = 0 ;
editor . curx = 0 ;
break ;
case ' b ' :
find_prev_word ( ) ;
break ;
case ' d ' :
delete_next_word ( ) ;
break ;
case ' f ' :
find_next_word ( ) ;
break ;
case BACKSPACE :
if ( isalnum ( row - > line [ editor . curx ] ) ) {
editor_set_status ( " is alnum " ) ;
while ( editor . curx > 0 & & isalnum ( row - > line [ editor . curx ] ) ) {
process_normal ( BACKSPACE ) ;
}
} else {
editor_set_status ( " not alnum " ) ;
process_normal ( BACKSPACE ) ;
}
delete_prev_word ( ) ;
break ;
default :
editor_set_status ( " unknown ESC key: %04x " , c ) ;
@@ -1533,6 +1999,9 @@ loop(void)
int
main ( int argc , char * argv [ ] )
{
// Set locale for proper UTF-8 handling
setlocale ( LC_ALL , " " ) ;
setup_terminal ( ) ;
init_editor ( ) ;