Checkpoint file split.
This commit is contained in:
@@ -21,12 +21,13 @@ include(GNUInstallDirs)
|
||||
|
||||
# Add executable
|
||||
add_executable(ke
|
||||
main.c
|
||||
abuf.c
|
||||
term.c
|
||||
buffer.c
|
||||
editor.c
|
||||
core.c
|
||||
core.h
|
||||
main.c
|
||||
)
|
||||
target_compile_definitions(ke PRIVATE KE_VERSION="ke version ${KE_VERSION}")
|
||||
install(TARGETS ke RUNTIME DESTINATION bin)
|
||||
|
||||
7
Makefile
7
Makefile
@@ -11,7 +11,8 @@ LDFLAGS := -fsanitize=address
|
||||
|
||||
all: $(TARGET) test.txt
|
||||
|
||||
SRCS := main.c abuf.c term.c buffer.c
|
||||
SRCS := main.c abuf.c term.c buffer.c editor.c core.c
|
||||
HDRS := abuf.h term.h buffer.h editor.h core.h
|
||||
|
||||
$(TARGET): $(SRCS)
|
||||
$(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS)
|
||||
@@ -33,3 +34,7 @@ test.txt:
|
||||
gdb:
|
||||
@test -f $(TARGET).pid || (echo "error: $(TARGET).pid not found" >&2; exit 1)
|
||||
@gdb -p $$(cat $(TARGET).pid | tr -d ' \t\n\r') ./$(TARGET)
|
||||
|
||||
.PHONY: cloc
|
||||
cloc:
|
||||
cloc $(SRCS) $(HDRS)
|
||||
|
||||
22
abuf.c
22
abuf.c
@@ -3,10 +3,11 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "abuf.h"
|
||||
#include "core.h"
|
||||
|
||||
|
||||
void
|
||||
abuf_init(abuf *buf)
|
||||
ab_init(abuf *buf)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
|
||||
@@ -15,6 +16,25 @@ abuf_init(abuf *buf)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_init_cap(abuf *buf, const size_t cap)
|
||||
{
|
||||
buf->b = calloc(cap, 1);
|
||||
buf->size = 0;
|
||||
buf->cap = cap;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_resize(abuf *buf, size_t cap)
|
||||
{
|
||||
cap = cap_growth(buf->cap, cap);
|
||||
buf->b = realloc(buf->b, cap);
|
||||
assert(buf->b != NULL);
|
||||
buf->cap = cap;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ab_appendch(abuf *buf, char c)
|
||||
{
|
||||
|
||||
3
abuf.h
3
abuf.h
@@ -13,10 +13,13 @@ typedef struct abuf {
|
||||
size_t cap;
|
||||
} abuf;
|
||||
|
||||
|
||||
#define ABUF_INIT {NULL, 0, 0}
|
||||
|
||||
|
||||
void ab_init(abuf *buf);
|
||||
void ab_init_cap(abuf *buf, size_t cap);
|
||||
void ab_resize(abuf *buf, size_t cap);
|
||||
void ab_appendch(abuf *buf, char c);
|
||||
void ab_append(abuf *buf, const char *s, size_t len);
|
||||
void ab_prependch(abuf *buf, const char c);
|
||||
|
||||
63
buffer.c
63
buffer.c
@@ -1,4 +1,4 @@
|
||||
/* buffer.c - buffer management implementation */
|
||||
/* buffer.c - multiple file buffers */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
@@ -8,13 +8,15 @@
|
||||
|
||||
#include "abuf.h"
|
||||
#include "buffer.h"
|
||||
#include "core.h"
|
||||
#include "editor.h"
|
||||
|
||||
|
||||
#define NO_NAME "[No Name]"
|
||||
|
||||
|
||||
/* externs from other modules */
|
||||
char *editor_prompt(char *, void (*cb)(char *, int16_t));
|
||||
char *editor_prompt(const char *, void (*cb)(char *, int16_t));
|
||||
|
||||
|
||||
static const char *
|
||||
@@ -28,6 +30,7 @@ buf_basename(const char *path)
|
||||
return (slash != NULL) ? (slash + 1) : path;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
buffer_find_exact_by_name(const char *name)
|
||||
{
|
||||
@@ -158,11 +161,10 @@ buffer_switch_prompt_cb(char *buf, const int16_t key)
|
||||
size_t need = 0;
|
||||
size_t used = 0;
|
||||
|
||||
if (key != 9) { /* TODO(kyle): extract TAB_KEY */
|
||||
return; /* TAB key */
|
||||
if (key != TAB_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
n = buffer_collect_prefix_matches(buf, idxs, 64);
|
||||
if (n <= 0) {
|
||||
editor_set_status("No matches");
|
||||
@@ -204,7 +206,7 @@ buffer_switch_prompt_cb(char *buf, const int16_t key)
|
||||
|
||||
|
||||
static void
|
||||
buffer_bind_to_editor(buffer *b)
|
||||
buffer_bind_to_editor(const buffer *b)
|
||||
{
|
||||
if (b == NULL) {
|
||||
return;
|
||||
@@ -225,7 +227,8 @@ buffer_bind_to_editor(buffer *b)
|
||||
}
|
||||
|
||||
|
||||
static void buffer_extract_from_editor(buffer *b)
|
||||
static void
|
||||
buffer_extract_from_editor(buffer *b)
|
||||
{
|
||||
if (b == NULL) {
|
||||
return;
|
||||
@@ -317,6 +320,43 @@ buffer_save_current(void)
|
||||
}
|
||||
|
||||
|
||||
buffer *
|
||||
buffer_current(void)
|
||||
{
|
||||
if (editor.curbuf < 0 || editor.curbuf >= editor.bufcount) {
|
||||
return NULL;
|
||||
}
|
||||
return editor.buffers[editor.curbuf];
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
buffer_is_unnamed_and_empty(const buffer *b)
|
||||
{
|
||||
if (b == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->filename != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->dirty) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->nrows != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->row != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
buffer_switch(const int idx)
|
||||
{
|
||||
@@ -355,7 +395,9 @@ buffer_next(void)
|
||||
buffer_switch(idx);
|
||||
}
|
||||
|
||||
void buffer_prev(void)
|
||||
|
||||
void
|
||||
buffer_prev(void)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
@@ -433,6 +475,11 @@ buffer_switch_by_name(void)
|
||||
int idx = 0;
|
||||
int n = 0;
|
||||
|
||||
if (editor.bufcount <= 1) {
|
||||
editor_set_status("No other buffers.");
|
||||
return;
|
||||
}
|
||||
|
||||
name = editor_prompt("Switch buffer (name, TAB to complete): %s", buffer_switch_prompt_cb);
|
||||
if (name == NULL) {
|
||||
return;
|
||||
|
||||
19
buffer.h
19
buffer.h
@@ -16,6 +16,23 @@ typedef struct buffer {
|
||||
int mark_curx, mark_cury;
|
||||
} buffer;
|
||||
|
||||
/* Access current buffer and convenient aliases for file-specific fields */
|
||||
buffer *buffer_current(void);
|
||||
|
||||
#define CURBUF (buffer_current())
|
||||
#define EROW (CURBUF->row)
|
||||
#define ENROWS (CURBUF->nrows)
|
||||
#define ECURX (CURBUF->curx)
|
||||
#define ECURY (CURBUF->cury)
|
||||
#define ERX (CURBUF->rx)
|
||||
#define EROWOFFS (CURBUF->rowoffs)
|
||||
#define ECOLOFFS (CURBUF->coloffs)
|
||||
#define EFILENAME (CURBUF->filename)
|
||||
#define EDIRTY (CURBUF->dirty)
|
||||
#define EMARK_SET (CURBUF->mark_set)
|
||||
#define EMARK_CURX (CURBUF->mark_curx)
|
||||
#define EMARK_CURY (CURBUF->mark_cury)
|
||||
|
||||
|
||||
void buffers_init(void);
|
||||
int buffer_add_empty(void);
|
||||
@@ -26,6 +43,8 @@ void buffer_prev(void);
|
||||
void buffer_switch_by_name(void);
|
||||
void buffer_close_current(void);
|
||||
const char *buffer_name(buffer *b);
|
||||
/* Helpers */
|
||||
int buffer_is_unnamed_and_empty(const buffer *b);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
64
core.c
64
core.c
@@ -35,6 +35,70 @@ strnstr(const char *s, const char *find, size_t slen)
|
||||
#endif
|
||||
|
||||
|
||||
char
|
||||
nibble_to_hex(char c)
|
||||
{
|
||||
c &= 0xf;
|
||||
if (c < 10) {
|
||||
return (char)('0' + c);
|
||||
}
|
||||
return (char)('A' + (c - 10));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
swap_int(int *first, int *second)
|
||||
{
|
||||
*first ^= *second;
|
||||
*second ^= *first;
|
||||
*first ^= *second;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
next_power_of_2(int n)
|
||||
{
|
||||
if (n < 2) {
|
||||
n = 2;
|
||||
}
|
||||
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cap_growth(int cap, int sz)
|
||||
{
|
||||
if (cap == 0) {
|
||||
cap = INITIAL_CAPACITY;
|
||||
}
|
||||
|
||||
while (cap <= sz) {
|
||||
cap = next_power_of_2(cap + 1);
|
||||
}
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
kstrnlen(const char *buf, const size_t max)
|
||||
{
|
||||
if (buf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strnlen(buf, max);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
kwrite(const int fd, const char* buf, const int len)
|
||||
{
|
||||
|
||||
11
core.h
11
core.h
@@ -1,9 +1,11 @@
|
||||
#ifndef KE_CORE_H
|
||||
#define KE_CORE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#define calloc1(sz) calloc(1, sz)
|
||||
#include <stddef.h>
|
||||
#define INITIAL_CAPACITY 8
|
||||
|
||||
|
||||
typedef enum key_press {
|
||||
@@ -21,12 +23,17 @@ typedef enum key_press {
|
||||
PG_DN = 1008,
|
||||
} key_press;
|
||||
|
||||
|
||||
#ifndef strnstr
|
||||
char *strnstr(const char *s, const char *find, size_t slen);
|
||||
#define INCLUDE_STRNSTR
|
||||
#endif
|
||||
|
||||
|
||||
char nibble_to_hex(char c);
|
||||
void swap_int(int *first, int *second);
|
||||
int next_power_of_2(int n);
|
||||
int cap_growth(int cap, int sz);
|
||||
size_t kstrnlen(const char *buf, size_t max);
|
||||
void kwrite(int fd, const char *buf, int len);
|
||||
void die(const char *s);
|
||||
|
||||
|
||||
124
editor.c
Normal file
124
editor.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* editor.c - editor-wide state and functions */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "abuf.h"
|
||||
#include "buffer.h"
|
||||
#include "core.h"
|
||||
#include "editor.h"
|
||||
#include "term.h"
|
||||
|
||||
/*
|
||||
* Global editor instance
|
||||
*/
|
||||
struct editor editor = {
|
||||
.cols = 0,
|
||||
.rows = 0,
|
||||
.curx = 0,
|
||||
.cury = 0,
|
||||
.rx = 0,
|
||||
.mode = 0,
|
||||
.nrows = 0,
|
||||
.rowoffs = 0,
|
||||
.coloffs = 0,
|
||||
.row = NULL,
|
||||
.killring = NULL,
|
||||
.kill = 0,
|
||||
.no_kill = 0,
|
||||
.filename = NULL,
|
||||
.dirty = 0,
|
||||
.dirtyex = 0,
|
||||
.mark_set = 0,
|
||||
.mark_curx = 0,
|
||||
.mark_cury = 0,
|
||||
.uarg = 0,
|
||||
.ucount = 0,
|
||||
.msgtm = 0,
|
||||
.buffers = NULL,
|
||||
.bufcount = 0,
|
||||
.curbuf = -1,
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
editor_set_status(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(editor.msg, sizeof(editor.msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
editor.msgtm = time(NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_editor(void)
|
||||
{
|
||||
editor.cols = 0;
|
||||
editor.rows = 0;
|
||||
|
||||
if (get_winsz(&editor.rows, &editor.cols) == -1) {
|
||||
die("can't get window size");
|
||||
}
|
||||
editor.rows--; /* status bar */
|
||||
editor.rows--; /* message line */
|
||||
|
||||
editor.curx = editor.cury = 0;
|
||||
editor.rx = 0;
|
||||
|
||||
editor.nrows = 0;
|
||||
editor.rowoffs = editor.coloffs = 0;
|
||||
editor.row = NULL;
|
||||
|
||||
/* don't clear out the kill ring:
|
||||
* killing / yanking across files is helpful, and killring
|
||||
* is initialized to NULL at program start.
|
||||
*/
|
||||
editor.kill = 0;
|
||||
editor.no_kill = 0;
|
||||
|
||||
editor.msg[0] = '\0';
|
||||
editor.msgtm = 0;
|
||||
|
||||
editor.dirty = 0;
|
||||
editor.mark_set = 0;
|
||||
editor.mark_cury = editor.mark_curx = 0;
|
||||
|
||||
/* initialize buffer system on first init */
|
||||
if (editor.buffers == NULL && editor.bufcount == 0) {
|
||||
buffers_init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
reset_editor(void)
|
||||
{
|
||||
/* Clear current working set. Notably, does not reset terminal
|
||||
* or buffers list. */
|
||||
for (int i = 0; i < editor.nrows; i++) {
|
||||
ab_free(&editor.row[i]);
|
||||
}
|
||||
free(editor.row);
|
||||
|
||||
editor.row = NULL;
|
||||
editor.nrows = 0;
|
||||
editor.rowoffs = editor.coloffs = 0;
|
||||
editor.curx = editor.cury = 0;
|
||||
editor.rx = 0;
|
||||
|
||||
if (editor.filename != NULL) {
|
||||
free(editor.filename);
|
||||
editor.filename = NULL;
|
||||
}
|
||||
|
||||
editor.dirty = 0;
|
||||
editor.mark_set = 0;
|
||||
editor.mark_cury = editor.mark_curx = 0;
|
||||
}
|
||||
47
editor.h
Normal file
47
editor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef EDITOR_H
|
||||
#define EDITOR_H
|
||||
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#include "abuf.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
||||
/* TODO(kyle): remove the "per-buffer" fields completely from the editor. */
|
||||
|
||||
struct editor {
|
||||
int rows, cols;
|
||||
int curx, cury; /* per-buffer */
|
||||
int rx; /* per-buffer */
|
||||
int mode;
|
||||
int nrows; /* per-buffer */
|
||||
int rowoffs, coloffs; /* per-buffer */
|
||||
abuf *row; /* per-buffer */
|
||||
abuf *killring;
|
||||
int kill; /* KILL CHAIN (\m/) */
|
||||
int no_kill; /* don't kill in delete_row */
|
||||
char *filename; /* per-buffer */
|
||||
int dirty; /* per-buffer */
|
||||
int dirtyex;
|
||||
char msg[80];
|
||||
int mark_set; /* per-buffer */
|
||||
int mark_curx, mark_cury; /* per-buffer */
|
||||
int uarg, ucount; /* C-u support */
|
||||
time_t msgtm;
|
||||
|
||||
/* Multi-buffer support */
|
||||
struct buffer **buffers; /* array of buffers */
|
||||
int bufcount; /* number of buffers */
|
||||
int curbuf; /* current buffer index */
|
||||
};
|
||||
|
||||
|
||||
extern struct editor editor;
|
||||
void editor_set_status(const char *fmt, ...);
|
||||
void init_editor(void);
|
||||
void reset_editor(void);
|
||||
|
||||
|
||||
#endif /* EDITOR_H */
|
||||
Reference in New Issue
Block a user