universal args, more work on undo.
This commit is contained in:
4
TODO
4
TODO
@@ -13,6 +13,6 @@
|
|||||||
[x] mark/region
|
[x] mark/region
|
||||||
|
|
||||||
What would it take for ke to be your daily drives?
|
What would it take for ke to be your daily drives?
|
||||||
[ ] C-u (repeat actions)
|
[x] C-u (repeat actions)
|
||||||
[x] Alt nav (backspace, delete, f, b, etc)
|
[x] Alt nav (backspace, delete, f, b, etc)
|
||||||
[ ] undo tree (C-k u)
|
[-] undo tree (C-k u/U)
|
||||||
|
|||||||
7
ke.1
7
ke.1
@@ -60,7 +60,9 @@ Reload the current buffer from disk.
|
|||||||
.It C-k s
|
.It C-k s
|
||||||
Save the file, prompting for a filename if needed. Also C-k C-s.
|
Save the file, prompting for a filename if needed. Also C-k C-s.
|
||||||
.It C-k u
|
.It C-k u
|
||||||
Undo changes. Not implemented yet, placeholder.
|
Undo changes.
|
||||||
|
.It C-k U
|
||||||
|
Redo changes.
|
||||||
.It C-k x
|
.It C-k x
|
||||||
save the file and exit. Also C-k C-x.
|
save the file and exit. Also C-k C-x.
|
||||||
.It C-k y
|
.It C-k y
|
||||||
@@ -76,6 +78,9 @@ In general, C-g cancels an operation.
|
|||||||
Refresh the display.
|
Refresh the display.
|
||||||
.It C-s
|
.It C-s
|
||||||
Incremental find.
|
Incremental find.
|
||||||
|
.It C-u
|
||||||
|
Universal argument. C-u followed by numbers will repeat an
|
||||||
|
operation n times.
|
||||||
.It C-w
|
.It C-w
|
||||||
Kill the region if the mark is set.
|
Kill the region if the mark is set.
|
||||||
.It C-y
|
.It C-y
|
||||||
|
|||||||
291
main.c
291
main.c
@@ -149,6 +149,7 @@ struct editor_t {
|
|||||||
char msg[80];
|
char msg[80];
|
||||||
int mark_set;
|
int mark_set;
|
||||||
int mark_curx, mark_cury;
|
int mark_curx, mark_cury;
|
||||||
|
int uarg, ucount; /* C-u support */
|
||||||
time_t msgtm;
|
time_t msgtm;
|
||||||
undo_tree_t *undo;
|
undo_tree_t *undo;
|
||||||
} editor = {
|
} editor = {
|
||||||
@@ -170,6 +171,9 @@ struct editor_t {
|
|||||||
.mark_set = 0,
|
.mark_set = 0,
|
||||||
.mark_curx = 0,
|
.mark_curx = 0,
|
||||||
.mark_cury = 0,
|
.mark_cury = 0,
|
||||||
|
.uarg = 0,
|
||||||
|
.ucount = 0,
|
||||||
|
.undo = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -293,12 +297,19 @@ void editor_find_callback(char *query, int16_t c);
|
|||||||
void editor_find(void);
|
void editor_find(void);
|
||||||
char *editor_prompt(char*, void (*cb)(char*, int16_t));
|
char *editor_prompt(char*, void (*cb)(char*, int16_t));
|
||||||
void editor_openfile(void);
|
void editor_openfile(void);
|
||||||
|
void move_cursor_once(int16_t c, int interactive);
|
||||||
void move_cursor(int16_t c, int interactive);
|
void move_cursor(int16_t c, int interactive);
|
||||||
|
void uarg_start(void);
|
||||||
|
void uarg_digit(int d);
|
||||||
|
void uarg_clear(void);
|
||||||
|
int uarg_get(void);
|
||||||
void newline(void);
|
void newline(void);
|
||||||
void process_kcommand(int16_t c);
|
void process_kcommand(int16_t c);
|
||||||
void process_normal(int16_t c);
|
void process_normal(int16_t c);
|
||||||
void process_escape(int16_t c);
|
void process_escape(int16_t c);
|
||||||
int process_keypress(void);
|
int process_keypress(void);
|
||||||
|
char *get_cloc_code_lines(const char *filename);
|
||||||
|
int dump_pidfile(void);
|
||||||
void enable_termraw(void);
|
void enable_termraw(void);
|
||||||
void display_clear(struct abuf *ab);
|
void display_clear(struct abuf *ab);
|
||||||
void disable_termraw(void);
|
void disable_termraw(void);
|
||||||
@@ -2365,7 +2376,7 @@ first_nonwhitespace(struct erow *row)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
move_cursor(int16_t c, int interactive)
|
move_cursor_once(int16_t c, int interactive)
|
||||||
{
|
{
|
||||||
struct erow *row;
|
struct erow *row;
|
||||||
int reps;
|
int reps;
|
||||||
@@ -2480,6 +2491,17 @@ move_cursor(int16_t c, int interactive)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
move_cursor(int16_t c, int interactive)
|
||||||
|
{
|
||||||
|
int n = uarg_get();
|
||||||
|
|
||||||
|
while (n-- > 0) {
|
||||||
|
move_cursor_once(c, interactive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
newline(void)
|
newline(void)
|
||||||
{
|
{
|
||||||
@@ -2503,99 +2525,68 @@ newline(void)
|
|||||||
|
|
||||||
editor.cury++;
|
editor.cury++;
|
||||||
editor.curx = 0;
|
editor.curx = 0;
|
||||||
/* Any insertion breaks a delete sequence for killring chaining. */
|
/* BREAK THE KILL CHAIN \m/ */
|
||||||
editor.kill = 0;
|
editor.kill = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char
|
void
|
||||||
*get_cloc_code_lines(const char *filename)
|
uarg_start(void)
|
||||||
{
|
{
|
||||||
char command[512];
|
if (editor.uarg == 0) {
|
||||||
char buffer[256];
|
editor.ucount = 0;
|
||||||
char *result = NULL;
|
}else {
|
||||||
FILE* pipe = NULL;
|
if (editor.ucount == 0) {
|
||||||
size_t len = 0;
|
editor.ucount = 1;
|
||||||
|
|
||||||
if (editor.filename == NULL) {
|
|
||||||
snprintf(command, sizeof(command),
|
|
||||||
"buffer has no associated file.");
|
|
||||||
result = malloc((kstrnlen(command, sizeof(command))) + 1);
|
|
||||||
assert(result != NULL);
|
|
||||||
strcpy(result, command);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editor.dirty) {
|
|
||||||
snprintf(command, sizeof(command),
|
|
||||||
"buffer must be saved first.");
|
|
||||||
result = malloc((kstrnlen(command, sizeof(command))) + 1);
|
|
||||||
assert(result != NULL);
|
|
||||||
strcpy(result, command);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(command,
|
|
||||||
sizeof(command),
|
|
||||||
"cloc --quiet %s | tail -2 | head -1 | awk '{print $5}'",
|
|
||||||
filename);
|
|
||||||
|
|
||||||
pipe = popen(command, "r");
|
|
||||||
if (!pipe) {
|
|
||||||
snprintf(command, sizeof(command), "Error getting LOC: %s", strerror(errno));
|
|
||||||
result = (char*)malloc(sizeof(buffer) + 1);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
|
||||||
len = strlen(buffer);
|
|
||||||
if (len > 0 && buffer[len - 1] == '\n') {
|
|
||||||
buffer[len - 1] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
result = malloc(strlen(buffer) + 1);
|
|
||||||
assert(result != NULL);
|
|
||||||
if (result) {
|
|
||||||
strcpy(result, buffer);
|
|
||||||
pclose(pipe);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
editor.ucount *= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
pclose(pipe);
|
editor.uarg = 1;
|
||||||
char *zero = malloc(2);
|
editor_set_status("C-u %d", editor.ucount);
|
||||||
if (zero) {
|
|
||||||
strcpy(zero, "0");
|
|
||||||
return zero;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
void
|
||||||
dump_pidfile(void)
|
uarg_digit(int d)
|
||||||
{
|
{
|
||||||
FILE* pid_file = NULL;
|
if (editor.uarg == 0) {
|
||||||
|
editor.uarg = 1;
|
||||||
if ((pid_file = fopen("ke.pid", "w")) == NULL) {
|
editor.ucount = 0;
|
||||||
editor_set_status("Failed to dump PID file: %s", strerror(errno));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(pid_file, "%ld", (long)getpid());
|
editor.ucount = editor.ucount * 10 + d;
|
||||||
fclose(pid_file);
|
editor_set_status("C-u %d", editor.ucount);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
|
||||||
|
void
|
||||||
|
uarg_clear(void)
|
||||||
|
{
|
||||||
|
editor.uarg = 0;
|
||||||
|
editor.ucount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
uarg_get(void)
|
||||||
|
{
|
||||||
|
int n = editor.ucount > 0 ? editor.ucount : 1;
|
||||||
|
|
||||||
|
uarg_clear();
|
||||||
|
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
process_kcommand(int16_t c)
|
process_kcommand(int16_t c)
|
||||||
{
|
{
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int jumpx = 0;
|
int jumpx = 0;
|
||||||
int jumpy = 0;
|
int jumpy = 0;
|
||||||
|
int reps = 0;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case BACKSPACE:
|
case BACKSPACE:
|
||||||
@@ -2654,15 +2645,25 @@ process_kcommand(int16_t c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((editor.row[editor.cury].size - editor.curx) >
|
reps = uarg_get();
|
||||||
0) {
|
while (reps--) {
|
||||||
process_normal(DEL_KEY);
|
while ((editor.row[editor.cury].size -
|
||||||
|
editor.curx) > 0) {
|
||||||
|
process_normal(DEL_KEY);
|
||||||
|
}
|
||||||
|
if (reps) {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case DEL_KEY:
|
case DEL_KEY:
|
||||||
case CTRL_KEY('d'):
|
case CTRL_KEY('d'):
|
||||||
delete_row(editor.cury);
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
delete_row(editor.cury);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
case CTRL_KEY('e'):
|
case CTRL_KEY('e'):
|
||||||
@@ -2750,7 +2751,11 @@ process_kcommand(int16_t c)
|
|||||||
editor_redo();
|
editor_redo();
|
||||||
break;
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
killring_yank();
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
killring_yank();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ESC_KEY:
|
case ESC_KEY:
|
||||||
case CTRL_KEY('g'):
|
case CTRL_KEY('g'):
|
||||||
@@ -2772,6 +2777,20 @@ process_kcommand(int16_t c)
|
|||||||
void
|
void
|
||||||
process_normal(int16_t c)
|
process_normal(int16_t c)
|
||||||
{
|
{
|
||||||
|
int reps = 0;
|
||||||
|
|
||||||
|
/* C-u handling – must be the very first thing */
|
||||||
|
if (c == CTRL_KEY('u')) {
|
||||||
|
uarg_start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* digits after a C-u are part of the argument */
|
||||||
|
if (editor.uarg && c >= '0' && c <= '9') {
|
||||||
|
uarg_digit(c - '0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_arrow_key(c)) {
|
if (is_arrow_key(c)) {
|
||||||
/* moving the cursor breaks a delete sequence */
|
/* moving the cursor breaks a delete sequence */
|
||||||
editor.kill = 0;
|
editor.kill = 0;
|
||||||
@@ -2791,12 +2810,26 @@ process_normal(int16_t c)
|
|||||||
case CTRL_KEY('d'):
|
case CTRL_KEY('d'):
|
||||||
case DEL_KEY:
|
case DEL_KEY:
|
||||||
if (c == DEL_KEY || c == CTRL_KEY('d')) {
|
if (c == DEL_KEY || c == CTRL_KEY('d')) {
|
||||||
move_cursor(ARROW_RIGHT, 1);
|
reps = uarg_get();
|
||||||
deletech(KILLRING_APPEND);
|
while (reps-- > 0) {
|
||||||
|
move_cursor(ARROW_RIGHT, 1);
|
||||||
|
deletech(KILLRING_APPEND);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
deletech(KILLRING_PREPEND);
|
reps = uarg_get();
|
||||||
|
while (reps-- > 0) {
|
||||||
|
deletech(KILLRING_PREPEND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CTRL_KEY('a'): /* beginning of line */
|
||||||
|
case HOME_KEY:
|
||||||
|
move_cursor(CTRL_KEY('a'), 1);
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('e'): /* end of line */
|
||||||
|
case END_KEY:
|
||||||
|
move_cursor(CTRL_KEY('e'), 1);
|
||||||
|
break;
|
||||||
case CTRL_KEY('g'):
|
case CTRL_KEY('g'):
|
||||||
break;
|
break;
|
||||||
case CTRL_KEY('l'):
|
case CTRL_KEY('l'):
|
||||||
@@ -2811,16 +2844,29 @@ process_normal(int16_t c)
|
|||||||
toggle_markset();
|
toggle_markset();
|
||||||
break;
|
break;
|
||||||
case CTRL_KEY('y'):
|
case CTRL_KEY('y'):
|
||||||
killring_yank();
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
killring_yank();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESC_KEY:
|
case ESC_KEY:
|
||||||
editor.mode = MODE_ESCAPE;
|
editor.mode = MODE_ESCAPE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (c == TAB_KEY) {
|
if (c == TAB_KEY) {
|
||||||
insertch(c);
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
insertch(c);
|
||||||
|
}
|
||||||
} else if (c >= 0x20 && c != 0x7f) {
|
} else if (c >= 0x20 && c != 0x7f) {
|
||||||
insertch(c);
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
insertch(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2908,6 +2954,87 @@ process_keypress(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char
|
||||||
|
*get_cloc_code_lines(const char *filename)
|
||||||
|
{
|
||||||
|
char command[512];
|
||||||
|
char buffer[256];
|
||||||
|
char *result = NULL;
|
||||||
|
FILE* pipe = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if (editor.filename == NULL) {
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"buffer has no associated file.");
|
||||||
|
result = malloc((kstrnlen(command, sizeof(command))) + 1);
|
||||||
|
assert(result != NULL);
|
||||||
|
strcpy(result, command);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editor.dirty) {
|
||||||
|
snprintf(command, sizeof(command),
|
||||||
|
"buffer must be saved first.");
|
||||||
|
result = malloc((kstrnlen(command, sizeof(command))) + 1);
|
||||||
|
assert(result != NULL);
|
||||||
|
strcpy(result, command);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(command,
|
||||||
|
sizeof(command),
|
||||||
|
"cloc --quiet %s | tail -2 | head -1 | awk '{print $5}'",
|
||||||
|
filename);
|
||||||
|
|
||||||
|
pipe = popen(command, "r");
|
||||||
|
if (!pipe) {
|
||||||
|
snprintf(command, sizeof(command), "Error getting LOC: %s", strerror(errno));
|
||||||
|
result = (char*)malloc(sizeof(buffer) + 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
||||||
|
len = strlen(buffer);
|
||||||
|
if (len > 0 && buffer[len - 1] == '\n') {
|
||||||
|
buffer[len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
result = malloc(strlen(buffer) + 1);
|
||||||
|
assert(result != NULL);
|
||||||
|
if (result) {
|
||||||
|
strcpy(result, buffer);
|
||||||
|
pclose(pipe);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(pipe);
|
||||||
|
char *zero = malloc(2);
|
||||||
|
if (zero) {
|
||||||
|
strcpy(zero, "0");
|
||||||
|
return zero;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
dump_pidfile(void)
|
||||||
|
{
|
||||||
|
FILE* pid_file = NULL;
|
||||||
|
|
||||||
|
if ((pid_file = fopen("ke.pid", "w")) == NULL) {
|
||||||
|
editor_set_status("Failed to dump PID file: %s", strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(pid_file, "%ld", (long)getpid());
|
||||||
|
fclose(pid_file);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A text editor needs the terminal to be in raw mode; but the default
|
* A text editor needs the terminal to be in raw mode; but the default
|
||||||
* is to be in canonical (cooked) mode, which is a buffered input mode.
|
* is to be in canonical (cooked) mode, which is a buffered input mode.
|
||||||
|
|||||||
Reference in New Issue
Block a user