@@ -2,11 +2,7 @@
* kyle's editor
*
* first version is a run-through of the kilo editor walkthrough as a
* set of guiderails. I've made a lot of changes and did some things
* differently. keep an eye for for kte, kyle's text editor - the
* rewrite that will be coming out... when it comes out.
*
* https://viewsourcecode.org/snaptoken/kilo/
* set of guiderails. I've made a lot of changes.
*/
# include <sys/ioctl.h>
@@ -21,6 +17,7 @@
# include <string.h>
# include <locale.h>
# include <wchar.h>
# include <wctype.h>
# include <termios.h>
# include <time.h>
# include <unistd.h>
@@ -155,10 +152,16 @@ void killring_append_char(unsigned char ch);
void killring_prepend_char ( unsigned char ch ) ;
void toggle_markset ( void ) ;
int cursor_after_mark ( void ) ;
int count_chars_from_cursor_to_mark ( void ) ;
void kill_region ( void ) ;
void indent_region ( void ) ;
void delete_region ( void ) ;
/* miscellaneous */
void kwrite ( int fd , const char * buf , int len ) ;
void die ( const char * s ) ;
int get_winsz ( int * rows , int * cols ) ;
void jump_to_position ( int col , int row ) ;
void goto_line ( void ) ;
int cursor_at_eol ( void ) ;
void delete_row ( int at ) ;
@@ -790,6 +793,50 @@ kill_region(void)
}
void
indent_region ( void )
{
int start_row , end_row ;
int i ;
if ( ! editor . mark_set ) {
return ;
}
if ( editor . mark_cury < editor . cury ) {
start_row = editor . mark_cury ;
end_row = editor . cury ;
} else if ( editor . mark_cury > editor . cury ) {
start_row = editor . cury ;
end_row = editor . mark_cury ;
} else {
start_row = end_row = editor . cury ;
}
/* Ensure bounds are valid */
if ( start_row < 0 ) {
start_row = 0 ;
}
if ( end_row > = editor . nrows ) {
end_row = editor . nrows - 1 ;
}
if ( start_row > = editor . nrows | | end_row < 0 ) {
return ;
}
/* Prepend a tab character to every row in the region */
for ( i = start_row ; i < = end_row ; i + + ) {
row_insert_ch ( & editor . row [ i ] , 0 , ' \t ' ) ;
}
/* Move cursor to beginning of the line it was on */
editor . curx = 0 ;
editor . dirty + + ;
}
/* call after kill_region */
void
delete_region ( void )
@@ -811,8 +858,7 @@ delete_region(void)
swap_int ( & cury , & marky ) ;
}
editor . curx = markx ;
editor . cury = marky ;
jump_to_position ( markx , marky ) ;
while ( killed < count ) {
move_cursor ( ARROW_RIGHT ) ;
@@ -820,16 +866,34 @@ delete_region(void)
killed + + ;
}
while ( editor . curx ! = markx & & editor . cury ! = marky ) {
deletech ( KILLRING_NO_OP ) ;
}
editor . kill = 1 ;
editor_set_status ( " Region killed. " ) ;
}
void
kwrite ( const int fd , const char * buf , const int len )
{
int wlen = 0 ;
wlen = write ( fd , buf , len ) ;
assert ( wlen ! = - 1 ) ;
assert ( wlen = = len ) ;
if ( wlen = = - 1 ) {
abort ( ) ;
}
}
void
die ( const char * s )
{
( void ) write ( STDOUT_FILENO , " \x1b [2J " , 4 ) ;
( void ) write ( STDOUT_FILENO , " \x1b [H " , 3 ) ;
k write( STDOUT_FILENO , " \x1b [2J " , 4 ) ;
k write( STDOUT_FILENO , " \x1b [H " , 3 ) ;
perror ( s ) ;
exit ( 1 ) ;
@@ -860,6 +924,15 @@ get_winsz(int *rows, int *cols)
}
void
jump_to_position ( const int x , const int y )
{
editor . curx = x ;
editor . cury = y ;
editor . rowoffs = editor . cury - ( editor . rows / 2 ) ;
}
void
goto_line ( void )
{
@@ -874,11 +947,12 @@ goto_line(void)
if ( lineno < 1 | | lineno > = editor . nrows ) {
editor_set_status ( " Line number must be between 1 and %d. " ,
editor . nrows ) ;
free ( query ) ;
return ;
}
editor . cury = lineno - 1 ;
editor . rowoffs = editor . cury - ( editor . rows / 2 ) ;
jump_to_position ( 0 , lineno - 1 ) ;
free ( query ) ;
}
@@ -1439,17 +1513,17 @@ get_keypress(void)
char *
editor_prompt ( char * prompt , void ( * cb ) ( char * , int16_t ) )
{
size_t bufsz = 128 ;
char * buf = malloc ( bufsz ) ;
size_t buflen = 0 ;
int16_t c ;
size_t bufsz = 128 ;
char * buf = malloc ( bufsz ) ;
size_t buflen = 0 ;
int16_t c ;
buf [ 0 ] = ' \0 ' ;
while ( 1 ) {
editor_set_status ( prompt , buf ) ;
display_refresh ( ) ;
while ( ( c = get_keypress ( ) ) < = 0 ) ;
while ( ( c = get_keypress ( ) ) < = 0 ) ;
if ( c = = DEL_KEY | | c = = CTRL_KEY ( ' h ' ) | | c = = BACKSPACE ) {
if ( buflen ! = 0 ) {
buf [ - - buflen ] = ' \0 ' ;
@@ -1469,7 +1543,7 @@ editor_prompt(char *prompt, void (*cb)(char *, int16_t))
}
return buf ;
}
} else if ( ( c = = TAB_KEY ) | | ( c > = 0x20 & & c ! = 0x7f ) ) {
} else if ( ( c = = TAB_KEY ) | | ( c > = 0x20 & & c < 0x7f ) ) {
if ( buflen = = bufsz - 1 ) {
bufsz * = 2 ;
buf = realloc ( buf , bufsz ) ;
@@ -1498,7 +1572,7 @@ editor_find_callback(char *query, int16_t c)
char * match ;
struct erow * row ;
if ( c = = ' \r ' | | c = = ESC_KEY ) {
if ( c = = ' \r ' | | c = = ESC_KEY | | c = = CTRL_KEY ( ' g ' ) ) {
/* reset search */
lmatch = - 1 ;
dir = 1 ;
@@ -1587,6 +1661,41 @@ editor_openfile(void)
}
int
first_nonwhitespace ( struct erow * row )
{
int pos ;
wchar_t wc ;
mbstate_t state ;
size_t len ;
if ( row = = NULL ) {
return 0 ;
}
memset ( & state , 0 , sizeof ( state ) ) ;
pos = 0 ;
while ( pos < row - > size ) {
len = mbrtowc ( & wc , & row - > line [ pos ] , row - > size - pos , & state ) ;
if ( len = = ( size_t ) - 1 | | len = = ( size_t ) - 2 ) {
/* Invalid or incomplete sequence, stop here */
break ;
}
if ( len = = 0 ) {
/* Null character, stop here */
break ;
}
if ( ! iswspace ( wc ) ) {
/* Found non-whitespace character */
break ;
}
pos + = len ;
}
return pos ;
}
void
move_cursor ( int16_t c )
{
@@ -1596,86 +1705,97 @@ move_cursor(int16_t c)
row = ( editor . cury > = editor . nrows ) ? NULL : & editor . row [ editor . cury ] ;
switch ( c ) {
case ARROW_UP :
case CTRL_KEY ( ' p ' ) :
if ( editor . cury > 0 ) {
editor . cury - - ;
}
break ;
case ARROW_DOWN :
case CTRL_KEY ( ' n ' ) :
if ( editor . cury < editor . nrows ) {
editor . cury + + ;
}
break ;
case ARROW_RIGHT :
case CTRL_KEY ( ' f ' ) :
if ( row & & editor . curx < row - > size ) {
case ARROW_UP :
case CTRL_KEY ( ' p ' ) :
if ( editor . cury > 0 ) {
editor . cury - - ;
row = ( editor . cury > = editor . nrows )
? NULL
: & editor . row [ editor . cury ] ;
editor . curx = first_nonwhitespace ( row ) ;
}
break ;
case ARROW_DOWN :
case CTRL_KEY ( ' n ' ) :
if ( editor . cury < editor . nrows ) {
editor . cury + + ;
row = ( editor . cury > = editor . nrows )
? NULL
: & editor . row [ editor . cury ] ;
editor . curx = first_nonwhitespace ( row ) ;
}
break ;
case ARROW_RIGHT :
case CTRL_KEY ( ' f ' ) :
if ( row & & editor . curx < row - > size ) {
editor . curx + + ;
/* skip over UTF-8 continuation bytes */
while ( row & & editor . curx < row - > size & &
( ( unsigned char ) row - > line [ editor . curx ] &
0xC0 ) = = 0x80 ) {
editor . curx + + ;
/* 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 ;
}
break ;
case ARROW_LEFT :
case CTRL_KEY ( ' b ' ) :
if ( editor . curx > 0 ) {
} else if ( row & & editor . curx = = row - > size ) {
editor . cury + + ;
row = ( editor . cury > = editor . nrows )
? NULL
: & editor . row [ editor . cury ] ;
editor . curx = first_nonwhitespace ( row ) ;
}
break ;
case ARROW_LEFT :
case CTRL_KEY ( ' b ' ) :
if ( editor . curx > 0 ) {
editor . curx - - ;
/* 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 - - ;
/* 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 :
if ( c = = PG_UP ) {
editor . cury = editor . rowoffs ;
} else if ( c = = PG_DN ) {
editor . cury = editor . rowoffs + editor . rows - 1 ;
if ( editor . cury > editor . nrows ) {
editor . cury = editor . nrows ;
}
}
reps = editor . rows ;
while ( - - reps ) {
move_cursor ( c = = PG_UP ? ARROW_UP : ARROW_DOWN ) ;
}
break ;
case HOME_KEY :
case CTRL_KEY ( ' a ' ) :
editor . curx = 0 ;
break ;
case END_KEY :
case CTRL_KEY ( ' e ' ) :
if ( editor . nrows = = 0 ) {
break ;
}
} 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 :
if ( c = = PG_UP ) {
editor . cury = editor . rowoffs ;
} else if ( c = = PG_DN ) {
editor . cury = editor . rowoffs + editor . rows - 1 ;
if ( editor . cury > editor . nrows ) {
editor . cury = editor . nrows ;
}
}
reps = editor . rows ;
while ( - - reps ) {
move_cursor ( c = = PG_UP ? ARROW_UP : ARROW_DOWN ) ;
}
break ;
case HOME_KEY :
case CTRL_KEY ( ' a ' ) :
editor . curx = 0 ;
break ;
case END_KEY :
case CTRL_KEY ( ' e ' ) :
if ( editor . nrows = = 0 ) {
break ;
default :
break ;
}
editor . curx = editor . row [ editor . cury ] . size ;
break ;
default :
break ;
}
@@ -1692,7 +1812,10 @@ newline(void)
{
struct erow * row = NULL ;
if ( editor . curx = = 0 ) {
if ( editor . cury > = editor . nrows ) {
/* At or past end of file, insert empty line */
erow_insert ( editor . cury , " " , 0 ) ;
} else if ( editor . curx = = 0 ) {
erow_insert ( editor . cury , " " , 0 ) ;
} else {
row = & editor . row [ editor . cury ] ;
@@ -1752,13 +1875,11 @@ get_cloc_code_lines(const char* filename)
}
if ( fgets ( buffer , sizeof ( buffer ) , pipe ) ! = NULL ) {
// Remove trailing newline
len = strlen ( buffer ) ;
if ( len > 0 & & buffer [ len - 1 ] = = ' \n ' ) {
buffer [ len - 1 ] = ' \0 ' ;
}
// Allocate and copy the string
result = malloc ( strlen ( buffer ) + 1 ) ;
assert ( result ! = NULL ) ;
if ( result ) {
@@ -1820,7 +1941,6 @@ process_kcommand(int16_t c)
break ;
case ' g ' :
case CTRL_KEY ( ' g ' ) :
goto_line ( ) ;
break ;
case BACKSPACE :
@@ -1866,13 +1986,20 @@ process_kcommand(int16_t c)
case ' y ' :
killring_yank ( ) ;
break ;
case ESC_KEY :
case CTRL_KEY ( ' g ' ) :
break ;
default :
if ( isprint ( c ) ) {
editor_set_status ( " unknown kcommand '%c' " , c ) ;
break ;
}
editor_set_status ( " unknown kcommand: %04x " , c ) ;
return ;
}
editor . dirtyex = 1 ;
return ;
}
@@ -1904,6 +2031,8 @@ process_normal(int16_t c)
deletech ( KILLRING_PREPEND ) ;
}
break ;
case CTRL_KEY ( ' g ' ) :
break ;
case CTRL_KEY ( ' l ' ) :
display_refresh ( ) ;
break ;
@@ -1921,12 +2050,17 @@ process_normal(int16_t c)
case ESC_KEY :
editor . mode = MODE_ESCAPE ;
break ;
default :
/* Insert any printable byte: ASCII 0x20– 0x7E and all bytes >=0x80. */
if ( ( c = = TAB_KEY ) | | ( c > = 0x20 & & c ! = 0x7f ) ) {
insertch ( c ) ;
}
break ;
default :
if ( c = = TAB_KEY ) {
if ( editor . mark_set ) {
indent_region ( ) ;
} else {
insertch ( c ) ;
}
} else if ( c > = 0x20 & & c ! = 0x7f ) {
insertch ( c ) ;
}
break ;
}
editor . dirtyex = 1 ;
@@ -1970,6 +2104,9 @@ process_escape(int16_t c)
case BACKSPACE :
delete_prev_word ( ) ;
break ;
case ESC_KEY :
case CTRL_KEY ( ' g ' ) :
break ; /* escape out of escape-mode */
default :
editor_set_status ( " unknown ESC key: %04x " , c ) ;
}
@@ -2054,8 +2191,8 @@ void
display_clear ( struct abuf * ab )
{
if ( ab = = NULL ) {
( void ) write ( STDOUT_FILENO , ESCSEQ " 2J " , 4 ) ;
( void ) write ( STDOUT_FILENO , ESCSEQ " H " , 3 ) ;
k write( STDOUT_FILENO , ESCSEQ " 2J " , 4 ) ;
k write( STDOUT_FILENO , ESCSEQ " H " , 3 ) ;
} else {
ab_append ( ab , ESCSEQ " 2J " , 4 ) ;
ab_append ( ab , ESCSEQ " H " , 3 ) ;
@@ -2269,7 +2406,7 @@ display_refresh(void)
/* ab_append(&ab, ESCSEQ "1;2H", 7); */
ab_append ( & ab , ESCSEQ " ?25h " , 6 ) ;
( void ) write ( STDOUT_FILENO , ab . b , ab . len ) ;
k write( STDOUT_FILENO , ab . b , ab . len ) ;
ab_free ( & ab ) ;
}
@@ -2312,7 +2449,6 @@ loop(void)
int
main ( int argc , char * argv [ ] )
{
// Set locale for proper UTF-8 handling
setlocale ( LC_ALL , " " ) ;
setup_terminal ( ) ;