Add simple backspacing
This commit is contained in:
parent
7a9727a550
commit
818ef8d40b
10 changed files with 135 additions and 39 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ kilo
|
||||||
.cache
|
.cache
|
||||||
.clangd
|
.clangd
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
test_file
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef BUFFER_H
|
#ifndef BUFFER_H
|
||||||
#define BUFFER_H
|
#define BUFFER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
struct erow {
|
struct erow {
|
||||||
|
@ -15,6 +17,7 @@ struct buffer {
|
||||||
char *filename;
|
char *filename;
|
||||||
struct erow *rows;
|
struct erow *rows;
|
||||||
int n_rows;
|
int n_rows;
|
||||||
|
bool modified;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct buffer *buffer_create(void);
|
struct buffer *buffer_create(void);
|
||||||
|
@ -24,6 +27,7 @@ ERRCODE buffer_write_file(struct buffer *buffer);
|
||||||
|
|
||||||
void erow_update_rendering(struct erow *erow);
|
void erow_update_rendering(struct erow *erow);
|
||||||
void erow_insert_char(struct erow *erow, int at, char c);
|
void erow_insert_char(struct erow *erow, int at, char c);
|
||||||
|
void erow_delete_char(struct erow *erow, int at);
|
||||||
int erow_cx_to_rx(struct erow *erow, int cx);
|
int erow_cx_to_rx(struct erow *erow, int cx);
|
||||||
int erow_rx_to_cx(struct erow *erow, int rx);
|
int erow_rx_to_cx(struct erow *erow, int rx);
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
|
||||||
|
void command_quit(void);
|
||||||
void command_move_cursor(KEY key);
|
void command_move_cursor(KEY key);
|
||||||
void command_insert_char(char c);
|
void command_insert_char(char c);
|
||||||
|
void command_delete_char(void);
|
||||||
void command_save_buffer(void);
|
void command_save_buffer(void);
|
||||||
|
|
||||||
#endif // COMMANDS_H
|
#endif // COMMANDS_H
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct editor_state {
|
||||||
int cx, cy, rx;
|
int cx, cy, rx;
|
||||||
int row_off, col_off;
|
int row_off, col_off;
|
||||||
int screenrows, screencols;
|
int screenrows, screencols;
|
||||||
|
int quit_times;
|
||||||
|
|
||||||
struct buffer *current_buf;
|
struct buffer *current_buf;
|
||||||
struct termios orig_termios;
|
struct termios orig_termios;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||||
|
|
||||||
typedef int ERRCODE;
|
typedef int ERRCODE;
|
||||||
|
#define RETURN(code) do {errcode = code; goto END;} while(0)
|
||||||
|
|
||||||
void die(const char *context);
|
void die(const char *context);
|
||||||
|
|
||||||
|
|
91
src/buffer.c
91
src/buffer.c
|
@ -10,15 +10,17 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static void buffer_free_rows(struct buffer *buffer);
|
static void buffer_free_rows(struct buffer *buffer);
|
||||||
|
static char *buffer_get_string(struct buffer *buffer, size_t *len_p);
|
||||||
|
|
||||||
struct buffer *buffer_create(void) {
|
struct buffer *buffer_create(void) {
|
||||||
struct buffer *buf = malloc(sizeof(struct buffer));
|
struct buffer *buffer = malloc(sizeof(struct buffer));
|
||||||
|
|
||||||
buf->filename = NULL;
|
buffer->filename = NULL;
|
||||||
buf->rows = NULL;
|
buffer->rows = NULL;
|
||||||
buf->n_rows = 0;
|
buffer->n_rows = 0;
|
||||||
|
buffer->modified = false;
|
||||||
|
|
||||||
return buf;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle file not existing
|
// TODO: Handle file not existing
|
||||||
|
@ -57,33 +59,40 @@ void buffer_read_file(struct buffer *buffer, const char *filename) {
|
||||||
|
|
||||||
free(line_buffer);
|
free(line_buffer);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
buffer->modified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERRCODE buffer_write_file(struct buffer *buffer) {
|
ERRCODE buffer_write_file(struct buffer *buffer) {
|
||||||
|
ERRCODE errcode = 0;
|
||||||
|
|
||||||
|
int fd = -1;
|
||||||
|
char *write_buffer = NULL;
|
||||||
|
|
||||||
if (buffer->filename == NULL)
|
if (buffer->filename == NULL)
|
||||||
return -1;
|
RETURN(-1);
|
||||||
|
|
||||||
size_t len = 0;
|
size_t len;
|
||||||
|
write_buffer = buffer_get_string(buffer, &len);
|
||||||
|
|
||||||
for (int i = 0; i < buffer->n_rows; i++)
|
fd = open(buffer->filename, O_RDWR | O_TRUNC | O_CREAT, 0644);
|
||||||
len += buffer->rows[i].n_chars + 1;
|
if (fd == -1)
|
||||||
|
RETURN(-2);
|
||||||
|
|
||||||
char *write_buffer = malloc(len);
|
if (write(fd, write_buffer, len) != (ssize_t) len)
|
||||||
char *p = write_buffer;
|
RETURN(-3);
|
||||||
for (int i = 0; i < buffer->n_rows; i++) {
|
|
||||||
struct erow *row = buffer->rows+i;
|
|
||||||
memcpy(p, row->chars, row->n_chars);
|
|
||||||
p += row->n_chars;
|
|
||||||
*p = '\n';
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = open(buffer->filename, O_RDWR | O_TRUNC | O_CREAT, 0644);
|
END:
|
||||||
write(fd, write_buffer, len);
|
if (fd != -1)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
if (write_buffer)
|
||||||
free(write_buffer);
|
free(write_buffer);
|
||||||
|
|
||||||
return 0;
|
if (errcode == 0)
|
||||||
|
buffer->modified = false;
|
||||||
|
|
||||||
|
return errcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_append_row(struct buffer *buffer, const char *chars, int n_chars) {
|
void buffer_append_row(struct buffer *buffer, const char *chars, int n_chars) {
|
||||||
|
@ -98,6 +107,8 @@ void buffer_append_row(struct buffer *buffer, const char *chars, int n_chars) {
|
||||||
new_row->n_rchars = 0;
|
new_row->n_rchars = 0;
|
||||||
|
|
||||||
erow_update_rendering(new_row);
|
erow_update_rendering(new_row);
|
||||||
|
|
||||||
|
buffer->modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buffer_free(struct buffer *buffer) {
|
void buffer_free(struct buffer *buffer) {
|
||||||
|
@ -112,6 +123,25 @@ static void buffer_free_rows(struct buffer *buffer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *buffer_get_string(struct buffer *buffer, size_t *len_p) {
|
||||||
|
*len_p = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer->n_rows; i++)
|
||||||
|
*len_p += buffer->rows[i].n_chars + 1;
|
||||||
|
|
||||||
|
char *write_buffer = malloc(*len_p);
|
||||||
|
char *p = write_buffer;
|
||||||
|
for (int i = 0; i < buffer->n_rows; i++) {
|
||||||
|
struct erow *row = buffer->rows+i;
|
||||||
|
memcpy(p, row->chars, row->n_chars);
|
||||||
|
p += row->n_chars;
|
||||||
|
*p = '\n';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void erow_update_rendering(struct erow *erow) {
|
void erow_update_rendering(struct erow *erow) {
|
||||||
|
@ -160,6 +190,25 @@ void erow_insert_char(struct erow *erow, int at, char c) {
|
||||||
erow->n_chars++;
|
erow->n_chars++;
|
||||||
|
|
||||||
erow_update_rendering(erow);
|
erow_update_rendering(erow);
|
||||||
|
|
||||||
|
// TODO: Seems like a bad idea
|
||||||
|
E.current_buf->modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erow_delete_char(struct erow *erow, int at) {
|
||||||
|
if (at < 0) at = 0;
|
||||||
|
if (at > erow->n_chars) at = erow->n_chars;
|
||||||
|
|
||||||
|
if (at != 0) {
|
||||||
|
memmove(erow->chars + at, erow->chars + at + 1, erow->n_chars - at);
|
||||||
|
erow->n_chars--;
|
||||||
|
erow->chars = realloc(erow->chars, erow->n_chars);
|
||||||
|
E.cx--;
|
||||||
|
erow_update_rendering(erow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
E.current_buf->modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int erow_cx_to_rx(struct erow *erow, int cx) {
|
int erow_cx_to_rx(struct erow *erow, int cx) {
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "kilo.h"
|
#include "kilo.h"
|
||||||
#include "buffer.h"
|
#include "terminal.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static void cursor_adjust_viewport(void);
|
static void cursor_adjust_viewport(void);
|
||||||
static void cursor_check_file_bounds(bool horizontal);
|
static void cursor_check_file_bounds(bool horizontal);
|
||||||
static void cursor_update_rx(bool horizontal);
|
static void cursor_update_rx(bool horizontal);
|
||||||
|
|
||||||
|
void command_quit(void) {
|
||||||
|
if (E.current_buf->modified && E.quit_times) {
|
||||||
|
editor_set_message("Unsaved changed! Press quit %d more time(s)", E.quit_times);
|
||||||
|
E.quit_times--;
|
||||||
|
} else {
|
||||||
|
terminal_clear();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void command_move_cursor(KEY key) {
|
void command_move_cursor(KEY key) {
|
||||||
struct erow *rows = E.current_buf->rows;
|
struct erow *rows = E.current_buf->rows;
|
||||||
int n_rows = E.current_buf->n_rows;
|
int n_rows = E.current_buf->n_rows;
|
||||||
|
@ -64,13 +78,24 @@ void command_insert_char(char c) {
|
||||||
E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx);
|
E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void command_delete_char(void) {
|
||||||
|
struct erow *row = E.current_buf->rows + E.cy;
|
||||||
|
|
||||||
|
if (E.cx == 0) {
|
||||||
|
// TODO
|
||||||
|
} else
|
||||||
|
erow_delete_char(row, E.cx);
|
||||||
|
|
||||||
|
E.rx = erow_cx_to_rx(E.current_buf->rows+E.cy, E.cx);
|
||||||
|
}
|
||||||
|
|
||||||
void command_save_buffer(void) {
|
void command_save_buffer(void) {
|
||||||
ERRCODE errcode = buffer_write_file(E.current_buf);
|
ERRCODE errcode = buffer_write_file(E.current_buf);
|
||||||
|
|
||||||
if (errcode == 0)
|
if (errcode == 0)
|
||||||
editor_set_message("Saved file %s", E.current_buf->filename);
|
editor_set_message("Saved file %s", E.current_buf->filename);
|
||||||
else if (errcode == -1)
|
else
|
||||||
editor_set_message("Save failed, no filename");
|
editor_set_message("Save failed: ERRCODE %d: %s", errcode, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cursor_check_file_bounds(bool horizontal) {
|
static void cursor_check_file_bounds(bool horizontal) {
|
||||||
|
@ -87,7 +112,13 @@ static void cursor_check_file_bounds(bool horizontal) {
|
||||||
E.cy = n_rows;
|
E.cy = n_rows;
|
||||||
|
|
||||||
if (E.cx < 0) {
|
if (E.cx < 0) {
|
||||||
if (E.cy > 0 && horizontal) E.cx = rows[--E.cy].n_chars;
|
if (E.cy > 0 && horizontal)
|
||||||
|
{
|
||||||
|
current_row = &rows[--E.cy];
|
||||||
|
max_x = current_row->n_chars;
|
||||||
|
|
||||||
|
E.cx = max_x;
|
||||||
|
}
|
||||||
else E.cx = 0;
|
else E.cx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/input.c
20
src/input.c
|
@ -1,8 +1,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
#include "terminal.h"
|
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "kilo.h"
|
||||||
|
#include "terminal.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
void input_process_key(void) {
|
void input_process_key(void) {
|
||||||
|
@ -20,28 +21,31 @@ void input_process_key(void) {
|
||||||
command_move_cursor(c);
|
command_move_cursor(c);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CTRL_KEY('Q'):
|
||||||
|
command_quit();
|
||||||
|
return;
|
||||||
|
|
||||||
case CTRL_KEY('S'):
|
case CTRL_KEY('S'):
|
||||||
command_save_buffer();
|
command_save_buffer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CTRL_KEY('Q'):
|
|
||||||
terminal_clear();
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
|
// TODO
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BACKSPACE:
|
case BACKSPACE:
|
||||||
case CTRL_KEY('H'):
|
case CTRL_KEY('H'):
|
||||||
case DEL:
|
case DEL:
|
||||||
|
command_delete_char();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CTRL_KEY('L'):
|
case CTRL_KEY('L'):
|
||||||
// case NOP:
|
case NOP:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
command_insert_char(c);
|
command_insert_char(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
E.quit_times = 3;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ void editor_init(char *filename) {
|
||||||
if (terminal_get_win_size(&E.screenrows, &E.screencols) == -1)
|
if (terminal_get_win_size(&E.screenrows, &E.screencols) == -1)
|
||||||
die("term_get_win_size");
|
die("term_get_win_size");
|
||||||
E.screenrows -= 2;
|
E.screenrows -= 2;
|
||||||
|
E.quit_times = 3;
|
||||||
|
|
||||||
E.current_buf = buffer_create();
|
E.current_buf = buffer_create();
|
||||||
if (filename)
|
if (filename)
|
||||||
|
|
4
src/ui.c
4
src/ui.c
|
@ -72,7 +72,9 @@ static void ui_draw_statusbar(struct append_buf *draw_buf) {
|
||||||
ab_append(draw_buf, "\x1b[7m", 4);
|
ab_append(draw_buf, "\x1b[7m", 4);
|
||||||
|
|
||||||
char *display = E.current_buf->filename ? E.current_buf->filename : "[NO NAME]";
|
char *display = E.current_buf->filename ? E.current_buf->filename : "[NO NAME]";
|
||||||
len = sprintf(buf, "%s -- %d lines", display, E.current_buf->n_rows);
|
char *modified = E.current_buf->modified ? "(modified) " : "";
|
||||||
|
int n_rows = E.current_buf->n_rows;
|
||||||
|
len = sprintf(buf, "%s %s-- %d lines", display, modified, n_rows);
|
||||||
memcpy(status_buf, buf, len);
|
memcpy(status_buf, buf, len);
|
||||||
|
|
||||||
len = sprintf(buf, "%d:%d", E.cy + 1, E.rx + 1);
|
len = sprintf(buf, "%d:%d", E.cy + 1, E.rx + 1);
|
||||||
|
|
Reference in a new issue