@@ -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,6 +152,7 @@ void killring_append_char(unsigned char ch);
void killring_prepend_char ( unsigned char ch ) ;
void toggle_markset ( void ) ;
int cursor_after_mark ( void ) ;
void indent_region ( void ) ;
/* miscellaneous */
void die ( const char * s ) ;
@@ -790,6 +788,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 )
@@ -828,8 +870,8 @@ delete_region(void)
void
die ( const char * s )
{
write ( STDOUT_FILENO , " \x1b [2J " , 4 ) ;
write ( STDOUT_FILENO , " \x1b [H " , 3 ) ;
( void ) write ( STDOUT_FILENO , " \x1b [2J " , 4 ) ;
( void ) write ( STDOUT_FILENO , " \x1b [H " , 3 ) ;
perror ( s ) ;
exit ( 1 ) ;
@@ -874,11 +916,13 @@ 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 ) ;
free ( query ) ;
}
@@ -1439,17 +1483,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 +1513,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 ) ;
@@ -1587,6 +1631,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 +1675,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 +1782,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 +1845,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 ) {
@@ -1921,12 +2012,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 ;
@@ -2054,8 +2150,8 @@ void
display_clear ( struct abuf * ab )
{
if ( ab = = NULL ) {
write ( STDOUT_FILENO , ESCSEQ " 2J " , 4 ) ;
write ( STDOUT_FILENO , ESCSEQ " H " , 3 ) ;
( void ) write ( STDOUT_FILENO , ESCSEQ " 2J " , 4 ) ;
( void ) write ( STDOUT_FILENO , ESCSEQ " H " , 3 ) ;
} else {
ab_append ( ab , ESCSEQ " 2J " , 4 ) ;
ab_append ( ab , ESCSEQ " H " , 3 ) ;
@@ -2269,7 +2365,7 @@ display_refresh(void)
/* ab_append(&ab, ESCSEQ "1;2H", 7); */
ab_append ( & ab , ESCSEQ " ?25h " , 6 ) ;
write ( STDOUT_FILENO , ab . b , ab . len ) ;
( void ) write ( STDOUT_FILENO , ab . b , ab . len ) ;
ab_free ( & ab ) ;
}
@@ -2312,7 +2408,6 @@ loop(void)
int
main ( int argc , char * argv [ ] )
{
// Set locale for proper UTF-8 handling
setlocale ( LC_ALL , " " ) ;
setup_terminal ( ) ;