@@ -88,17 +88,17 @@ struct abuf {
/* editor row */
struct erow {
char * line ;
char * render ;
char * line ;
char * render ;
int size ;
int rsize ;
int size ;
int rsize ;
int cap ;
int cap ;
} ;
typedef enum undo_flag {
typedef enum undo_type {
UNDO_INSERT = 1 < < 0 , /* insertch */
UNDO_DELETE = 1 < < 1 , /* deletech */
UNDO_PASTE = 1 < < 2 , /* yank */
@@ -115,7 +115,7 @@ typedef struct undo_node {
int row , col ;
struct abuf text ;
struct undo_node * next ;
struct undo_node * child ;
struct undo_node * parent ;
} undo_node_t ;
@@ -187,6 +187,8 @@ 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 ) ;
char nibble_to_hex ( char c ) ;
void swap_int ( int * a , int * b ) ;
@@ -242,24 +244,17 @@ void erow_free(struct erow *row);
*/
undo_node_t * undo_node_new ( undo_flag_t type ) ;
void undo_node_free ( undo_node_t * node ) ;
void undo_node_free_branch ( undo_node_t * node ) ;
undo_tree_t * undo_tree_new ( void ) ;
void undo_tree_free ( undo_tree_t * ut ) ;
int undo_continue_pending ( undo_flag_t type ) ;
void undo_tree_free ( undo_tree_t * tree ) ;
void undo_begin ( undo_flag_t type ) ;
void undo_append_char ( char c ) ;
void undo_append ( const char * data , size_t len ) ;
void undo_prepend_char ( char c ) ;
void undo_prepend ( const char * data , size_t len ) ;
void undo_commit ( void ) ;
void undo_discard_redo_branches ( struct undo_node * from ) ;
undo_node_t * undo_parent_of ( undo_node_t * node ) ;
void undo_apply ( const undo_node_t * node , int direction ) ;
// void editor_undo(void);
// void editor_redo(void);
// void undo_mark_saved(void);
// int undo_depth(const undo_tree_t *t);
// int undo_can_undo(const undo_tree_t *t);
// int undo_can_redo(const undo_tree_t *t);
// void undo_tree_debug_dump(const undo_tree_t *t);
void editor_undo ( void ) ;
void editor_redo ( void ) ;
/* kill ring, marking, etc... */
void killring_flush ( void ) ;
@@ -284,7 +279,10 @@ int cursor_at_eol(void);
void delete_row ( int at ) ;
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_insert_block ( struct erow * row , int at ,
const char * data , size_t len ) ;
void row_delete_ch ( struct erow * row , int at ) ;
void row_delete_block ( struct erow * row , int at , size_t len ) ;
void insertch ( int16_t c ) ;
void deletech ( uint8_t op ) ;
void open_file ( const char * filename ) ;
@@ -472,6 +470,7 @@ reset_editor(void)
erow_free ( & editor . row [ i ] ) ;
}
free ( editor . row ) ;
editor . row = NULL ;
if ( editor . filename ! = NULL ) {
free ( editor . filename ) ;
@@ -527,6 +526,27 @@ ab_append(struct abuf *buf, const char *s, size_t len)
}
void
ab_prependch ( struct abuf * buf , const char c )
{
ab_prepend ( buf , & c , 1 ) ;
}
void
ab_prepend ( struct abuf * buf , const char * s , const size_t len )
{
char * nc = realloc ( buf - > b , buf - > len + len ) ;
assert ( nc ! = NULL ) ;
memmove ( nc + len , nc , buf - > len ) ;
memcpy ( nc , s , len ) ;
buf - > b = nc ;
buf - > len + = len ;
}
void
ab_free ( struct abuf * buf )
{
@@ -772,18 +792,20 @@ erow_free(struct erow *row)
free ( row - > line ) ;
row - > render = NULL ;
row - > line = NULL ;
row - > cap = 0 ;
row - > size = 0 ;
}
undo_node_t *
undo_node_new ( undo_flag_t type )
undo_node_new ( const undo_flag_t type )
{
undo_node_t * node = calloc1 ( sizeof ( undo_node_t ) ) ;
node - > type = type ;
node - > row = node - > col = 0 ;
node - > next = NULL ;
node - > child = NULL ;
node - > parent = NULL ;
ab_init ( & node - > text ) ;
@@ -798,32 +820,16 @@ undo_node_free(undo_node_t *node)
return ;
}
assert ( node - > next - > parent = = node ) ;
ab_free ( & node - > text ) ;
undo_node_free ( node - > child ) ;
undo_node_free ( node - > next ) ;
node = node - > next ;
free ( node - > parent ) ;
node - > parent = NULL ;
free ( node ) ;
}
void
undo_node_free_branch ( undo_node_t * node )
{
undo_node_t * next = NULL ;
if ( node = = NULL ) {
return ;
}
while ( node ! = NULL ) {
next = node - > next ;
undo_node_free ( node - > child ) ;
ab_free ( & node - > text ) ;
free ( node ) ;
node = next ;
}
}
undo_tree_t *
undo_tree_new ( void )
{
@@ -840,52 +846,32 @@ undo_tree_new(void)
void
undo_tree_free ( undo_tree_t * ut )
undo_tree_free ( undo_tree_t * tree )
{
if ( ut = = NULL ) {
if ( tree = = NULL ) {
return ;
}
undo_node_free ( ut - > root ) ;
undo_node_free ( ut - > pending ) ;
undo_node_free ( tree - > root ) ;
if ( tree - > pending ! = NULL ) {
undo_node_free ( tree - > pending ) ;
}
if ( debug_log = = NULL ) {
ut - > root = NULL ;
ut - > current = NULL ;
ut - > saved = NULL ;
ut - > pending = NULL ;
tree - > root = NULL ;
tree - > current = NULL ;
tree - > saved = NULL ;
tree - > pending = NULL ;
} else {
ut - > root =
ut - > current =
ut - > saved =
ut - > pending =
tree - > root =
tree - > current =
tree - > saved =
tree - > pending =
( void * ) 0xDEADBEEF ;
}
free ( ut ) ;
}
int
undo_continue_pending ( undo_flag_t type )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * node = tree - > pending ;
/* no pending node, so we need to start anew. */
if ( node = = NULL ) {
return 0 ;
}
if ( node - > type ! = type ) {
return 0 ;
}
if ( node - > row ! = editor . cury | | node - > col ! = editor . curx ) {
return 0 ;
}
return 1 ;
free ( tree ) ;
}
@@ -894,26 +880,22 @@ undo_continue_pending(undo_flag_t type)
* if a new pending sequence doesn't need to be created.
*/
void
undo_begin ( const undo_flag_t type )
undo_begin ( undo_flag_t type )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * node = tree - > pending ;
struct undo_tree * tree = editor . undo ;
if ( undo_continue_pending ( type ) ) {
return ;
}
assert ( tree ! = NULL ) ;
if ( tree - > pending ! = NULL ) {
if ( tree - > pending - > type = = type ) {
return ;
}
undo_commit ( ) ;
}
node = undo_node_new ( type ) ;
assert ( node ! = NULL ) ;
node - > type = type ;
node - > row = editor . cury ;
node - > col = editor . curx ;
tree - > pending = node ;
tree - > pending = undo_node_new ( type ) ;
tree - > pending - > row = editor . cury ;
tree - > pending - > col = editor . curx ;
}
@@ -939,199 +921,112 @@ undo_append(const char *data, const size_t len)
}
/* Finish the current batch and link it into the tree */
void
undo_prepend_char ( const char c )
{
undo_node_t * node = editor . undo - > pending ;
assert ( node ! = NULL ) ;
ab_prependch ( & node - > text , c ) ;
}
void
undo_prepend ( const char * data , const size_t len )
{
undo_node_t * node = editor . undo - > pending ;
assert ( node ! = NULL ) ;
ab_prepend ( & node - > text , data , len ) ;
}
/*
* undo_commit commits the current pending tree into the root.
*
* Finish the current batch and link it into the tree.
* */
void
undo_commit ( void )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * node = NULL ;
assert ( tree ! = NULL ) ;
if ( tree - > pending = = NULL ) {
return ; /* nothing to commit */
return ; /* no pending operation */
}
node = tree - > pending ;
tree - > pending = NULL ;
if ( tree - > current & & tree - > current - > child ) {
undo_node_free_branch ( tree - > current - > child ) ;
tree - > current - > child = NULL ;
}
if ( tree - > root = = NULL ) {
/* First edit ever */
tree - > root = node ;
tree - > current = node ;
} else if ( tree - > current = = NULL ) {
/* this shouldn't happen, so throw an
* assert to catch it.
*/
assert ( tree - > current ! = NULL ) ;
tree - > root = tree - > current = node ;
} else {
tree - > current - > child = node ;
tree - > current = node ;
}
if ( tree - > saved & & tree - > current ! = tree - > saved ) {
tree - > saved = NULL ;
}
editor . dirty = 1 ;
}
void
undo_discard_redo_branches ( struct undo_node * from )
{
undo_node_free ( from - > child ) ;
from - > child = NULL ;
undo_node_free ( from - > next ) ;
from - > next = NULL ;
}
undo_node_t *
undo_parent_of ( undo_node_t * node )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * parent = tree - > root ;
if ( tree - > root = = node ) {
return NULL ;
}
parent = tree - > root ;
while ( parent ! = NULL & & parent - > child ! = node ) {
parent = parent - > child ;
}
if ( parent = = NULL ) {
return NULL ;
}
return parent ;
}
void
editor_undo ( void )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * node = tree - > current ;
undo_node_t * parent = NULL ;
if ( node = = NULL | | node = = tree - > root ) {
editor_set_status ( " Nothing to undo. " ) ;
return ;
}
parent = undo_parent_of ( node ) ;
assert ( parent ! = NULL ) ;
undo_apply ( node , - 1 ) ;
tree - > current = parent ;
display_refresh ( ) ;
}
void
editor_redo ( void )
{
undo_tree_t * tree = editor . undo ;
undo_node_t * node = tree - > current ;
if ( node = = NULL | | node - > child = = NULL ) {
editor_set_status ( " Nothing to redo. " ) ;
return ;
}
tree - > current = node - > child ;
undo_apply ( node - > child , 1 ) ;
display_refresh ( ) ;
}
void
undo_apply ( const struct undo_node * node , const int direction )
{
int row = node - > row ;
int col = node - > col ;
const char * data = node - > text . b ;
size_t len = node - > text . len ;
int redo ;
jump_to_position ( col , row ) ;
assert ( node ! = NULL ) ;
redo = direction < 0 ? 1 : 0 ;
switch ( node - > type ) {
case UNDO_PASTE :
case UNDO_INSERT :
if ( direction > 0 ) {
for ( size_t i = 0 ; i < len ; i + + ) {
insertch ( ( unsigned char ) data [ i ] ) ;
}
} else {
for ( size_t i = 0 ; i < len ; i + + ) {
deletech ( KILLRING_NO_OP ) ;
}
}
break ;
case UNDO_DELETE :
if ( direction > 0 ) {
for ( size_t i = 0 ; i < len ; i + + ) {
deletech ( KILLRING_NO_OP ) ;
}
} else {
for ( size_t i = 0 ; i < len ; i + + ) {
insertch ( ( unsigned char ) data [ i ] ) ;
}
}
break ;
case UNDO_NEWLINE :
if ( direction > 0 ) {
newline ( ) ;
} else {
if ( editor . cury > 0 ) {
editor . curx = editor . row [ editor . cury - 1 ] . size ;
row_append_row ( & editor . row [ editor . cury - 1 ] ,
editor . row [ editor . cury ] . line ,
editor . row [ editor . cury ] . size ) ;
delete_row ( editor . cury ) ;
}
}
break ;
case UNDO_DELETE_ROW :
if ( direction > 0 ) {
/* redo = delete the whole row again */
delete_row ( editor . cury ) ;
} else {
/* undo = re-insert the saved row (including final '\n') */
if ( len > 0 & & data [ len - 1 ] = = ' \n ' ) len - - ;
erow_insert ( editor . cury , ( char * ) data , len ) ;
/* cursor goes to start of re-inserted row */
editor . curx = 0 ;
}
break ;
case UNDO_INDENT :
case UNDO_KILL_REGION :
/* These are more complex – you can implement later */
/* For now just move cursor and say "not implemented yet" */
editor_set_status ( " Undo of %s not implemented yet " ,
direction > 0 ? " redo " : " complex op " ) ;
break ;
default :
editor_set_status ( " Unknown undo type: %d " , node - > type ) ;
break ;
if ( redo ) {
editor_set_status ( " Redo isn't implemented. " ) ;
} else {
editor_set_status ( " Undo isn't implemented " ) ;
}
}
}
void
undo_apply_current ( int direction )
{
undo_tree_t * tree = editor . undo ;
assert ( tree ! = NULL ) ;
undo_commit ( ) ;
if ( tree - > current = = NULL ) {
editor_set_status ( " No further undo. " ) ;
return ;
}
editor . dirty = 1 ;
assert ( tree - > current - > next = = NULL ) ;
undo_apply ( tree - > current , direction ) ;
tree - > current = tree - > current - > parent ;
undo_node_free ( tree - > current - > next ) ;
}
void
editor_undo ( void )
{
/*
* pending should already have been committed, but it doesn't
* hurt to be a little paranoid.
*/
undo_commit ( ) ;
undo_apply_current ( 1 ) ;
}
void
editor_redo ( void )
{
/*
* pending should already have been committed, but it doesn't
* hurt to be a little paranoid.
*/
undo_commit ( ) ;
undo_apply_current ( - 1 ) ;
}
@@ -1165,6 +1060,10 @@ killring_yank(void)
insertch ( ch ) ;
}
}
undo_begin ( UNDO_PASTE ) ;
undo_append ( editor . killring - > line , editor . killring - > size ) ;
undo_commit ( ) ; /* atomic */
}
@@ -1793,6 +1692,38 @@ row_insert_ch(struct erow *row, int at, int16_t c)
}
void
row_insert_block ( struct erow * row , int at , const char * data , size_t len )
{
if ( at < 0 ) {
at = 0 ;
}
if ( at > row - > size ) {
at = row - > size ;
}
if ( len = = 0 ) {
return ;
}
if ( row - > size + ( int ) len + 1 > row - > cap ) {
row - > cap = cap_growth ( row - > cap , row - > size + len + 1 ) ;
row - > line = realloc ( row - > line , row - > cap ) ;
assert ( row - > line ! = NULL ) ;
}
memmove ( row - > line + at + len , row - > line + at ,
row - > size - at + 1 ) ; /* +1 for NUL */
memcpy ( row - > line + at , data , len ) ;
row - > size + = len ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
editor . dirty + + ;
}
void
row_delete_ch ( struct erow * row , int at )
{
@@ -1807,9 +1738,32 @@ row_delete_ch(struct erow *row, int at)
}
void
row_delete_block ( struct erow * row , int at , size_t len )
{
if ( at < 0 | | at > = row - > size | | len = = 0 ) {
return ;
}
if ( at + ( int ) len > row - > size ) {
len = row - > size - at ;
}
memmove ( row - > line + at , row - > line + at + len ,
row - > size - ( at + len ) + 1 ) ;
row - > size - = len ;
row - > line [ row - > size ] = ' \0 ' ;
erow_update ( row ) ;
editor . dirty + + ;
}
void
insertch ( int16_t c )
{
undo_begin ( UNDO_INSERT ) ;
/*
* insert_ch doesn't need to worry about how to update a
* a row; it can just figure out where the cursor is
@@ -1826,7 +1780,12 @@ insertch(int16_t c)
row_insert_ch ( & editor . row [ editor . cury ] ,
editor . curx ,
( int16_t ) ( c & 0xff ) ) ;
undo_append_char ( ( char ) c ) ;
editor . curx + + ;
if ( editor . undo - > pending ! = NULL ) {
editor . undo - > pending - > col = editor . curx ;
}
editor . dirty + + ;
}
@@ -1834,8 +1793,8 @@ insertch(int16_t c)
void
deletech ( uint8_t op )
{
struct erow * row = NULL ;
unsigned char dch = 0 ;
struct erow * row = NULL ;
unsigned char dch = 0 ;
if ( editor . cury > = editor . nrows ) {
return ;
@@ -1852,9 +1811,12 @@ deletech(uint8_t op)
dch = ' \n ' ;
}
undo_begin ( UNDO_DELETE ) ;
if ( editor . curx > 0 ) {
row_delete_ch ( row , editor . curx - 1 ) ;
editor . curx - - ;
undo_append_char ( dch ) ;
} else {
editor . curx = editor . row [ editor . cury - 1 ] . size ;
row_append_row ( & editor . row [ editor . cury - 1 ] ,
@@ -1866,6 +1828,11 @@ deletech(uint8_t op)
delete_row ( editor . cury ) ;
editor . no_kill = prev ;
editor . cury - - ;
undo_append_char ( ' \n ' ) ;
}
if ( editor . undo - > pending ! = NULL ) {
editor . undo - > pending - > col = editor . curx ;
}
if ( op = = KILLRING_FLUSH ) {
@@ -2378,8 +2345,10 @@ first_nonwhitespace(struct erow *row)
void
move_cursor_once ( int16_t c , int interactive )
{
struct erow * row ;
int reps ;
struct erow * row ;
int reps = 0 ;
undo_commit ( ) ;
row = ( editor . cury > = editor . nrows ) ? NULL : & editor . row [ editor . cury ] ;
@@ -2500,6 +2469,8 @@ newline(void)
{
struct erow * row = NULL ;
undo_begin ( UNDO_NEWLINE ) ;
if ( editor . cury > = editor . nrows ) {
erow_insert ( editor . cury , " " , 0 ) ;
editor . cury + + ;
@@ -2523,6 +2494,9 @@ newline(void)
/* BREAK THE KILL CHAIN \m/ */
editor . kill = 0 ;
/* newlines aren't batched */
undo_commit ( ) ;
}
@@ -2584,6 +2558,8 @@ process_kcommand(int16_t c)
int jumpy = 0 ;
int reps = 0 ;
undo_commit ( ) ;
switch ( c ) {
case BACKSPACE :
while ( editor . curx > 0 ) {
@@ -2741,7 +2717,11 @@ process_kcommand(int16_t c)
case ' x ' :
exit ( save_file ( ) ) ;
case ' u ' :
editor_undo ( ) ;
reps = uarg_get ( ) ;
while ( reps - - ) {
editor_undo ( ) ;
}
break ;
case ' U ' :
editor_redo ( ) ;
@@ -2775,6 +2755,8 @@ process_normal(int16_t c)
{
int reps = 0 ;
undo_commit ( ) ;
/* C-u handling – must be the very first thing */
if ( c = = CTRL_KEY ( ' u ' ) ) {
uarg_start ( ) ;
@@ -2846,7 +2828,7 @@ process_normal(int16_t c)
killring_yank ( ) ;
}
break ;
case ESC_KEY :
editor . mode = MODE_ESCAPE ;
break ;
@@ -2876,6 +2858,8 @@ process_escape(int16_t c)
{
editor_set_status ( " hi " ) ;
undo_commit ( ) ;
switch ( c ) {
case ' > ' :
editor . cury = editor . nrows ;
@@ -3367,11 +3351,7 @@ deathknell(void)
debug_log = NULL ;
}
if ( editor . killring ! = NULL ) {
erow_free ( editor . killring ) ;
free ( editor . killring ) ;
editor . killring = NULL ;
}
killring_flush ( ) ;
reset_editor ( ) ;
disable_termraw ( ) ;