Compare commits
3 Commits
multi-buff
...
v2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d581c1c2f | |||
| 78e4f84f7b | |||
| 734eb6e67d |
@@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.15)
|
|||||||
project(ke C) # Specify C language explicitly
|
project(ke C) # Specify C language explicitly
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(KE_VERSION "1.5.6")
|
set(KE_VERSION "2.0.1")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g")
|
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g -Werror=stringop-truncation")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE")
|
||||||
|
|
||||||
# Optionally enable AddressSanitizer (ASan)
|
# Optionally enable AddressSanitizer (ASan)
|
||||||
|
|||||||
33
abuf.c
33
abuf.c
@@ -6,6 +6,15 @@
|
|||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
abuf_grow(abuf *buf, size_t delta)
|
||||||
|
{
|
||||||
|
if (buf->cap - buf->size < delta) {
|
||||||
|
ab_resize(buf, buf->cap + delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ab_init(abuf *buf)
|
ab_init(abuf *buf)
|
||||||
{
|
{
|
||||||
@@ -38,6 +47,7 @@ ab_resize(abuf *buf, size_t cap)
|
|||||||
void
|
void
|
||||||
ab_appendch(abuf *buf, char c)
|
ab_appendch(abuf *buf, char c)
|
||||||
{
|
{
|
||||||
|
abuf_grow(buf, 1);
|
||||||
ab_append(buf, &c, 1);
|
ab_append(buf, &c, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,20 +55,10 @@ ab_appendch(abuf *buf, char c)
|
|||||||
void
|
void
|
||||||
ab_append(abuf *buf, const char *s, size_t len)
|
ab_append(abuf *buf, const char *s, size_t len)
|
||||||
{
|
{
|
||||||
char *nc = buf->b;
|
char *nc = NULL;
|
||||||
size_t sz = buf->size + len;
|
|
||||||
|
|
||||||
if (sz >= buf->cap) {
|
abuf_grow(buf, len);
|
||||||
while (sz > buf->cap) {
|
nc = buf->b;
|
||||||
if (buf->cap == 0) {
|
|
||||||
buf->cap = 1;
|
|
||||||
} else {
|
|
||||||
buf->cap *= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nc = realloc(nc, buf->cap);
|
|
||||||
assert(nc != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&nc[buf->size], s, len);
|
memcpy(&nc[buf->size], s, len);
|
||||||
buf->b = nc;
|
buf->b = nc;
|
||||||
@@ -69,6 +69,8 @@ ab_append(abuf *buf, const char *s, size_t len)
|
|||||||
void
|
void
|
||||||
ab_prependch(abuf *buf, const char c)
|
ab_prependch(abuf *buf, const char c)
|
||||||
{
|
{
|
||||||
|
abuf_grow(buf, 1);
|
||||||
|
|
||||||
ab_prepend(buf, &c, 1);
|
ab_prepend(buf, &c, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +78,10 @@ ab_prependch(abuf *buf, const char c)
|
|||||||
void
|
void
|
||||||
ab_prepend(abuf *buf, const char *s, const size_t len)
|
ab_prepend(abuf *buf, const char *s, const size_t len)
|
||||||
{
|
{
|
||||||
char *nc = realloc(buf->b, buf->size + len);
|
char *nc = NULL;
|
||||||
|
|
||||||
|
abuf_grow(buf, len);
|
||||||
|
nc = buf->b;
|
||||||
assert(nc != NULL);
|
assert(nc != NULL);
|
||||||
|
|
||||||
memmove(nc + len, nc, buf->size);
|
memmove(nc + len, nc, buf->size);
|
||||||
|
|||||||
22
buffer.c
22
buffer.c
@@ -71,6 +71,7 @@ buffer_collect_prefix_matches(const char *prefix, int *out_idx, const int max_ou
|
|||||||
size_t plen = (prefix ? strlen(prefix) : 0);
|
size_t plen = (prefix ? strlen(prefix) : 0);
|
||||||
|
|
||||||
for (int i = 0; i < editor.bufcount; i++) {
|
for (int i = 0; i < editor.bufcount; i++) {
|
||||||
|
matched = 0;
|
||||||
b = editor.buffers[i];
|
b = editor.buffers[i];
|
||||||
|
|
||||||
const char *cand1 = b->filename;
|
const char *cand1 = b->filename;
|
||||||
@@ -267,21 +268,35 @@ buffers_init(void)
|
|||||||
editor.buffers = NULL;
|
editor.buffers = NULL;
|
||||||
editor.bufcount = 0;
|
editor.bufcount = 0;
|
||||||
editor.curbuf = -1;
|
editor.curbuf = -1;
|
||||||
|
editor.bufcap = 0;
|
||||||
|
|
||||||
idx = buffer_add_empty();
|
idx = buffer_add_empty();
|
||||||
buffer_switch(idx);
|
buffer_switch(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
buffer_list_resize(void)
|
||||||
|
{
|
||||||
|
buffer **newlist = NULL;
|
||||||
|
|
||||||
|
if (editor.bufcount == (int)editor.bufcap) {
|
||||||
|
editor.bufcap = cap_growth((int)editor.bufcap, editor.bufcount + 1);
|
||||||
|
|
||||||
|
newlist = realloc(editor.buffers, sizeof(buffer *) * editor.bufcap);
|
||||||
|
assert(newlist != NULL);
|
||||||
|
editor.buffers = newlist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
buffer_add_empty(void)
|
buffer_add_empty(void)
|
||||||
{
|
{
|
||||||
buffer *buf = NULL;
|
buffer *buf = NULL;
|
||||||
buffer **newlist = realloc(editor.buffers, sizeof(buffer *) * (editor.bufcount + 1));
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
assert(newlist != NULL);
|
buffer_list_resize();
|
||||||
editor.buffers = newlist;
|
|
||||||
|
|
||||||
buf = calloc(1, sizeof(buffer));
|
buf = calloc(1, sizeof(buffer));
|
||||||
assert(buf != NULL);
|
assert(buf != NULL);
|
||||||
@@ -418,6 +433,7 @@ buffer_close_current(void)
|
|||||||
int target = 0;
|
int target = 0;
|
||||||
int nb = 0;
|
int nb = 0;
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
if (editor.curbuf < 0 || editor.curbuf >= editor.bufcount) {
|
if (editor.curbuf < 0 || editor.curbuf >= editor.bufcount) {
|
||||||
editor_set_status("No buffer to close.");
|
editor_set_status("No buffer to close.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
1
buffer.h
1
buffer.h
@@ -43,7 +43,6 @@ void buffer_prev(void);
|
|||||||
void buffer_switch_by_name(void);
|
void buffer_switch_by_name(void);
|
||||||
void buffer_close_current(void);
|
void buffer_close_current(void);
|
||||||
const char *buffer_name(buffer *b);
|
const char *buffer_name(buffer *b);
|
||||||
/* Helpers */
|
|
||||||
int buffer_is_unnamed_and_empty(const buffer *b);
|
int buffer_is_unnamed_and_empty(const buffer *b);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
editor.c
1
editor.c
@@ -92,6 +92,7 @@ init_editor(void)
|
|||||||
|
|
||||||
/* initialize buffer system on first init */
|
/* initialize buffer system on first init */
|
||||||
if (editor.buffers == NULL && editor.bufcount == 0) {
|
if (editor.buffers == NULL && editor.bufcount == 0) {
|
||||||
|
editor.bufcap = 0;
|
||||||
buffers_init();
|
buffers_init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
editor.h
5
editor.h
@@ -32,9 +32,10 @@ struct editor {
|
|||||||
time_t msgtm;
|
time_t msgtm;
|
||||||
|
|
||||||
/* Multi-buffer support */
|
/* Multi-buffer support */
|
||||||
struct buffer **buffers; /* array of buffers */
|
struct buffer **buffers; /* array of buffers */
|
||||||
int bufcount; /* number of buffers */
|
int bufcount; /* number of buffers */
|
||||||
int curbuf; /* current buffer index */
|
int curbuf; /* current buffer index */
|
||||||
|
size_t bufcap; /* current buffer capacity */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
13
ke.1
13
ke.1
@@ -32,8 +32,11 @@ Toggle the mark.
|
|||||||
If the mark is set, unindent the region.
|
If the mark is set, unindent the region.
|
||||||
.It C-k =
|
.It C-k =
|
||||||
If the mark is set, indent the region.
|
If the mark is set, indent the region.
|
||||||
|
.It C-k b
|
||||||
|
Switch to a buffer.
|
||||||
.It C-k c
|
.It C-k c
|
||||||
Clear (flush) the kill ring.
|
Close the current buffer. If no other buffers are open, an empty
|
||||||
|
buffer will be opened. To exit, use C-k q.
|
||||||
.It C-k d
|
.It C-k d
|
||||||
Delete from the cursor to the end of the line.
|
Delete from the cursor to the end of the line.
|
||||||
.It C-k C-d
|
.It C-k C-d
|
||||||
@@ -41,7 +44,7 @@ Delete the entire line.
|
|||||||
.It C-k e
|
.It C-k e
|
||||||
Edit a new file. Also C-k C-e.
|
Edit a new file. Also C-k C-e.
|
||||||
.It C-k f
|
.It C-k f
|
||||||
Incremental find.
|
Flush the kill ring.
|
||||||
.It C-k g
|
.It C-k g
|
||||||
Go to a specific line.
|
Go to a specific line.
|
||||||
.It C-k j
|
.It C-k j
|
||||||
@@ -50,6 +53,8 @@ Jump to the mark.
|
|||||||
List the number of lines of code in a saved file.
|
List the number of lines of code in a saved file.
|
||||||
.It C-k m
|
.It C-k m
|
||||||
Run make(1).
|
Run make(1).
|
||||||
|
.It C-k p
|
||||||
|
Switch to the next buffer.
|
||||||
.It C-k q
|
.It C-k q
|
||||||
Exit the editor. If the file has unsaved changes,
|
Exit the editor. If the file has unsaved changes,
|
||||||
a warning will be printed; a second C-k q will exit.
|
a warning will be printed; a second C-k q will exit.
|
||||||
@@ -60,9 +65,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.
|
Undo changes (not implemented; marking this k-command as taken).
|
||||||
.It C-k U
|
.It C-k U
|
||||||
Redo changes.
|
Redo changes (not implemented; marking this k-command as taken).
|
||||||
.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
|
||||||
|
|||||||
82
main.c
82
main.c
@@ -298,8 +298,15 @@ file_open_prompt_cb(char *buf, const int16_t key)
|
|||||||
strncat(newbuf, "/", sizeof(newbuf) - strlen(newbuf) - 1);
|
strncat(newbuf, "/", sizeof(newbuf) - strlen(newbuf) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(buf, newbuf[0] ? newbuf : names[0], 127);
|
/* copy to input buffer (max 127 chars + NUL) avoiding truncation warnings */
|
||||||
buf[127] = '\0';
|
do {
|
||||||
|
const char *src__ = newbuf[0] ? newbuf : names[0];
|
||||||
|
size_t cap__ = 128u;
|
||||||
|
size_t len__ = strnlen(src__, cap__ - 1);
|
||||||
|
memcpy(buf, src__, len__);
|
||||||
|
buf[len__] = '\0';
|
||||||
|
} while (0);
|
||||||
|
|
||||||
editor_set_status("Unique match: %s%s", names[0], isdir[0] ? "/" : "");
|
editor_set_status("Unique match: %s%s", names[0], isdir[0] ? "/" : "");
|
||||||
} else {
|
} else {
|
||||||
cur = strlen(base);
|
cur = strlen(base);
|
||||||
@@ -318,8 +325,14 @@ file_open_prompt_cb(char *buf, const int16_t key)
|
|||||||
strncat(newbuf, base, sizeof(newbuf) - strlen(newbuf) - 1);
|
strncat(newbuf, base, sizeof(newbuf) - strlen(newbuf) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(buf, newbuf, 127);
|
/* copy to input buffer (max 127 chars + NUL) avoiding truncation warnings */
|
||||||
buf[127] = '\0';
|
do {
|
||||||
|
const char *src__ = newbuf;
|
||||||
|
size_t cap__ = 128u;
|
||||||
|
size_t len__ = strnlen(src__, cap__ - 1);
|
||||||
|
memcpy(buf, src__, len__);
|
||||||
|
buf[len__] = '\0';
|
||||||
|
} while (0);
|
||||||
|
|
||||||
size_t used = 0;
|
size_t used = 0;
|
||||||
used += snprintf(msg + used, sizeof(msg) - used, "%d matches: ", n);
|
used += snprintf(msg + used, sizeof(msg) - used, "%d matches: ", n);
|
||||||
@@ -842,22 +855,35 @@ delete_region(void)
|
|||||||
void
|
void
|
||||||
jump_to_position(int col, int row)
|
jump_to_position(int col, int row)
|
||||||
{
|
{
|
||||||
/* clamp position */
|
if (ENROWS <= 0) {
|
||||||
|
ECURX = 0;
|
||||||
|
ECURY = 0;
|
||||||
|
|
||||||
|
editor.curx = ECURX;
|
||||||
|
editor.cury = ECURY;
|
||||||
|
|
||||||
|
display_refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (row < 0) {
|
if (row < 0) {
|
||||||
row = 0;
|
row = 0;
|
||||||
} else if (row > editor.nrows) {
|
} else if (row >= ENROWS) {
|
||||||
row = editor.nrows - 1;
|
row = ENROWS - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col < 0) {
|
if (col < 0) {
|
||||||
col = 0;
|
col = 0;
|
||||||
} else if (row >= 0 && row < ENROWS && col > (int) EROW[row].size) {
|
} else if (col > (int) EROW[row].size) {
|
||||||
col = (int) EROW[row].size;
|
col = (int) EROW[row].size;
|
||||||
}
|
}
|
||||||
|
|
||||||
ECURX = col;
|
ECURX = col;
|
||||||
ECURY = row;
|
ECURY = row;
|
||||||
|
|
||||||
|
editor.curx = ECURX;
|
||||||
|
editor.cury = ECURY;
|
||||||
|
|
||||||
display_refresh();
|
display_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,7 +899,7 @@ goto_line(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineno = atoi(query);
|
lineno = atoi(query);
|
||||||
if (lineno < 1 || lineno >= ENROWS) {
|
if (lineno < 1 || lineno > ENROWS) {
|
||||||
editor_set_status("Line number must be between 1 and %d.",
|
editor_set_status("Line number must be between 1 and %d.",
|
||||||
ENROWS);
|
ENROWS);
|
||||||
free(query);
|
free(query);
|
||||||
@@ -1684,27 +1710,27 @@ editor_find(void)
|
|||||||
void
|
void
|
||||||
editor_openfile(void)
|
editor_openfile(void)
|
||||||
{
|
{
|
||||||
char *filename;
|
char *filename = NULL;
|
||||||
|
buffer *cur = NULL;
|
||||||
|
int nb = NULL;
|
||||||
|
|
||||||
/* Add TAB completion for path input */
|
filename = editor_prompt("Load file: %s", file_open_prompt_cb);
|
||||||
filename = editor_prompt("Load file: %s", file_open_prompt_cb);
|
if (filename == NULL) {
|
||||||
if (filename == NULL) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* If the only buffer is an unnamed, empty buffer, reuse it */
|
cur = buffer_current();
|
||||||
buffer *cur = buffer_current();
|
if (editor.bufcount == 1 && buffer_is_unnamed_and_empty(cur)) {
|
||||||
if (editor.bufcount == 1 && buffer_is_unnamed_and_empty(cur)) {
|
open_file(filename);
|
||||||
open_file(filename);
|
buffer_save_current();
|
||||||
buffer_save_current();
|
} else {
|
||||||
} else {
|
nb = buffer_add_empty();
|
||||||
/* Open into a new buffer */
|
buffer_switch(nb);
|
||||||
int nb = buffer_add_empty();
|
open_file(filename);
|
||||||
buffer_switch(nb);
|
buffer_save_current();
|
||||||
open_file(filename);
|
}
|
||||||
buffer_save_current();
|
|
||||||
}
|
free(filename);
|
||||||
free(filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user