Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2a6a593ef | |||
| 74637c8e83 | |||
| 3b401e770e | |||
| c47287e96e |
@@ -4,7 +4,7 @@ project(ke C) # Specify C language explicitly
|
|||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(KE_VERSION "2.1.1")
|
set(KE_VERSION "2.1.1")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g -Werror=stringop-truncation")
|
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g")
|
||||||
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)
|
||||||
@@ -19,16 +19,34 @@ endif()
|
|||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
# Add executable
|
set(SOURCES
|
||||||
add_executable(ke
|
|
||||||
abuf.c
|
abuf.c
|
||||||
|
core.c
|
||||||
term.c
|
term.c
|
||||||
buffer.c
|
buffer.c
|
||||||
editor.c
|
editor.c
|
||||||
core.c
|
editing.c
|
||||||
core.h
|
killring.c
|
||||||
|
process.c
|
||||||
|
undo.c
|
||||||
main.c
|
main.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(HEADERS
|
||||||
|
abuf.h
|
||||||
|
core.h
|
||||||
|
term.h
|
||||||
|
buffer.h
|
||||||
|
editor.h
|
||||||
|
editing.h
|
||||||
|
killring.h
|
||||||
|
process.h
|
||||||
|
undo.h
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Add executable
|
||||||
|
add_executable(ke ${SOURCES} ${HEADERS})
|
||||||
target_compile_definitions(ke PRIVATE KE_VERSION="ke version ${KE_VERSION}")
|
target_compile_definitions(ke PRIVATE KE_VERSION="ke version ${KE_VERSION}")
|
||||||
install(TARGETS ke RUNTIME DESTINATION bin)
|
install(TARGETS ke RUNTIME DESTINATION bin)
|
||||||
install(FILES ke.1 TYPE MAN)
|
install(FILES ke.1 TYPE MAN)
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -11,8 +11,10 @@ LDFLAGS := -fsanitize=address
|
|||||||
|
|
||||||
all: $(TARGET) test.txt
|
all: $(TARGET) test.txt
|
||||||
|
|
||||||
SRCS := main.c abuf.c term.c buffer.c editor.c core.c
|
SRCS := main.c abuf.c core.c term.c buffer.c editor.c editing.c killring.c \
|
||||||
HDRS := abuf.h term.h buffer.h editor.h core.h
|
process.c undo.c
|
||||||
|
HDRS := abuf.h core.h term.h buffer.h editor.h editing.c killring.h \
|
||||||
|
process.h undo.h
|
||||||
|
|
||||||
$(TARGET): $(SRCS)
|
$(TARGET): $(SRCS)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS)
|
||||||
|
|||||||
24
abuf.c
24
abuf.c
@@ -71,6 +71,18 @@ ab_append(abuf *buf, const char *s, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ab_append_ab(abuf *buf, const abuf *other)
|
||||||
|
{
|
||||||
|
assert(buf != NULL);
|
||||||
|
if (other == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ab_append(buf, other->b, other->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ab_prependch(abuf *buf, const char c)
|
ab_prependch(abuf *buf, const char c)
|
||||||
{
|
{
|
||||||
@@ -97,6 +109,18 @@ ab_prepend(abuf *buf, const char *s, const size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ab_prepend_ab(abuf *buf, const abuf *other)
|
||||||
|
{
|
||||||
|
assert(buf != NULL);
|
||||||
|
if (other == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ab_prepend(buf, other->b, other->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ab_free(abuf *buf)
|
ab_free(abuf *buf)
|
||||||
{
|
{
|
||||||
|
|||||||
2
abuf.h
2
abuf.h
@@ -22,8 +22,10 @@ void ab_init_cap(abuf *buf, size_t cap);
|
|||||||
void ab_resize(abuf *buf, size_t cap);
|
void ab_resize(abuf *buf, size_t cap);
|
||||||
void ab_appendch(abuf *buf, char c);
|
void ab_appendch(abuf *buf, char c);
|
||||||
void ab_append(abuf *buf, const char *s, size_t len);
|
void ab_append(abuf *buf, const char *s, size_t len);
|
||||||
|
void ab_append_ab(abuf *buf, const abuf *other);
|
||||||
void ab_prependch(abuf *buf, const char c);
|
void ab_prependch(abuf *buf, const char c);
|
||||||
void ab_prepend(abuf *buf, const char *s, const size_t len);
|
void ab_prepend(abuf *buf, const char *s, const size_t len);
|
||||||
|
void ab_prepend_ab(abuf *buf, const abuf *other);
|
||||||
void ab_free(abuf *buf);
|
void ab_free(abuf *buf);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
123
buffer.c
123
buffer.c
@@ -222,12 +222,12 @@ buffer_name(buffer *b)
|
|||||||
void
|
void
|
||||||
buffers_init(void)
|
buffers_init(void)
|
||||||
{
|
{
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
editor.buffers = NULL;
|
editor.buffers = NULL;
|
||||||
editor.bufcount = 0;
|
editor.bufcount = 0;
|
||||||
editor.curbuf = 0;
|
editor.curbuf = 0;
|
||||||
editor.bufcap = 0;
|
editor.bufcap = 0;
|
||||||
|
|
||||||
idx = buffer_add_empty();
|
idx = buffer_add_empty();
|
||||||
buffer_switch(idx);
|
buffer_switch(idx);
|
||||||
@@ -237,28 +237,29 @@ buffers_init(void)
|
|||||||
static void
|
static void
|
||||||
buffer_list_resize(void)
|
buffer_list_resize(void)
|
||||||
{
|
{
|
||||||
buffer **newlist = NULL;
|
buffer **newlist = NULL;
|
||||||
|
|
||||||
if (editor.bufcount == editor.bufcap) {
|
if (editor.bufcount == editor.bufcap) {
|
||||||
editor.bufcap = (size_t)cap_growth((int)editor.bufcap, (int)editor.bufcount + 1);
|
editor.bufcap = (size_t)cap_growth((int)editor.bufcap,
|
||||||
|
(int)editor.bufcount + 1);
|
||||||
|
|
||||||
newlist = realloc(editor.buffers, sizeof(buffer *) * editor.bufcap);
|
newlist = realloc(editor.buffers, sizeof(buffer *) * editor.bufcap);
|
||||||
assert(newlist != NULL);
|
assert(newlist != NULL);
|
||||||
editor.buffers = newlist;
|
editor.buffers = newlist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
buffer_add_empty(void)
|
buffer_add_empty(void)
|
||||||
{
|
{
|
||||||
buffer *buf = NULL;
|
buffer *buf = NULL;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
buffer_list_resize();
|
buffer_list_resize();
|
||||||
|
|
||||||
buf = calloc(1, sizeof(buffer));
|
buf = calloc(1, sizeof(buffer));
|
||||||
assert(buf != NULL);
|
assert(buf != NULL);
|
||||||
|
|
||||||
buf->curx = 0;
|
buf->curx = 0;
|
||||||
buf->cury = 0;
|
buf->cury = 0;
|
||||||
@@ -273,28 +274,23 @@ buffer_add_empty(void)
|
|||||||
buf->mark_curx = 0;
|
buf->mark_curx = 0;
|
||||||
buf->mark_cury = 0;
|
buf->mark_cury = 0;
|
||||||
|
|
||||||
editor.buffers[editor.bufcount] = buf;
|
undo_tree_init(&buf->undo);
|
||||||
idx = (int)editor.bufcount;
|
|
||||||
editor.bufcount++;
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
editor.buffers[editor.bufcount] = buf;
|
||||||
void
|
idx = (int)editor.bufcount;
|
||||||
buffer_save_current(void)
|
editor.bufcount++;
|
||||||
{
|
return idx;
|
||||||
/* No-op: editor no longer mirrors per-buffer fields */
|
|
||||||
(void)editor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer *
|
buffer *
|
||||||
buffer_current(void)
|
buffer_current(void)
|
||||||
{
|
{
|
||||||
if (editor.bufcount == 0 || editor.curbuf >= editor.bufcount) {
|
if (editor.bufcount == 0 || editor.curbuf >= editor.bufcount) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return editor.buffers[editor.curbuf];
|
|
||||||
|
return editor.buffers[editor.curbuf];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -328,58 +324,58 @@ buffer_is_unnamed_and_empty(const buffer *b)
|
|||||||
void
|
void
|
||||||
buffer_switch(const int idx)
|
buffer_switch(const int idx)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
buffer *b = NULL;
|
||||||
|
|
||||||
if (idx < 0 || (size_t)idx >= editor.bufcount) {
|
if (idx < 0 || (size_t)idx >= editor.bufcount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor.curbuf == (size_t)idx) {
|
if (editor.curbuf == (size_t)idx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
b = editor.buffers[idx];
|
b = editor.buffers[idx];
|
||||||
editor.curbuf = (size_t)idx;
|
editor.curbuf = (size_t)idx;
|
||||||
editor.dirtyex = 1;
|
editor.dirtyex = 1;
|
||||||
editor_set_status("Switched to buffer %d: %s", editor.curbuf, buffer_name(b));
|
editor_set_status("Switched to buffer %d: %s", editor.curbuf, buffer_name(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_next(void)
|
buffer_next(void)
|
||||||
{
|
{
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
|
|
||||||
if (editor.bufcount <= 1) {
|
if (editor.bufcount <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = (editor.curbuf + 1) % editor.bufcount;
|
idx = (editor.curbuf + 1) % editor.bufcount;
|
||||||
buffer_switch((int)idx);
|
buffer_switch((int)idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_prev(void)
|
buffer_prev(void)
|
||||||
{
|
{
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
|
|
||||||
if (editor.bufcount <= 1) {
|
if (editor.bufcount <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = (editor.curbuf == 0) ? (editor.bufcount - 1) : (editor.curbuf - 1);
|
idx = (editor.curbuf == 0) ? (editor.bufcount - 1) : (editor.curbuf - 1);
|
||||||
buffer_switch((int)idx);
|
buffer_switch((int)idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_close_current(void)
|
buffer_close_current(void)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
buffer *b = NULL;
|
||||||
size_t closing = 0;
|
size_t closing = 0;
|
||||||
int target = 0;
|
int target = 0;
|
||||||
int nb = 0;
|
int nb = 0;
|
||||||
|
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (editor.bufcount == 0 || editor.curbuf >= editor.bufcount) {
|
if (editor.bufcount == 0 || editor.curbuf >= editor.bufcount) {
|
||||||
@@ -464,3 +460,4 @@ buffer_switch_by_name(void)
|
|||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
buffer.h
23
buffer.h
@@ -2,23 +2,24 @@
|
|||||||
#define KE_BUFFER_H
|
#define KE_BUFFER_H
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
|
#include "undo.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct buffer {
|
typedef struct buffer {
|
||||||
size_t curx, cury;
|
size_t curx, cury;
|
||||||
size_t rx;
|
size_t rx;
|
||||||
size_t nrows;
|
size_t nrows;
|
||||||
size_t rowoffs, coloffs;
|
size_t rowoffs, coloffs;
|
||||||
abuf *row;
|
abuf *row;
|
||||||
char *filename;
|
char *filename;
|
||||||
int dirty;
|
int dirty;
|
||||||
int mark_set;
|
int mark_set;
|
||||||
size_t mark_curx, mark_cury;
|
size_t mark_curx, mark_cury;
|
||||||
|
undo_tree undo;
|
||||||
} buffer;
|
} buffer;
|
||||||
|
|
||||||
/* Access current buffer and convenient aliases for file-specific fields */
|
|
||||||
buffer *buffer_current(void);
|
|
||||||
|
|
||||||
|
buffer *buffer_current(void);
|
||||||
#define CURBUF (buffer_current())
|
#define CURBUF (buffer_current())
|
||||||
#define EROW (CURBUF->row)
|
#define EROW (CURBUF->row)
|
||||||
#define ENROWS (CURBUF->nrows)
|
#define ENROWS (CURBUF->nrows)
|
||||||
|
|||||||
38
core.c
38
core.c
@@ -1,3 +1,4 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -7,7 +8,9 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef INCLUDE_STRNSTR
|
#ifdef INCLUDE_STRNSTR
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the first occurrence of find in s, where the search is limited to the
|
* Find the first occurrence of find in s, where the search is limited to the
|
||||||
* first slen characters of s.
|
* first slen characters of s.
|
||||||
@@ -35,6 +38,40 @@ strnstr(const char *s, const char *find, size_t slen)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
path_is_dir(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (path == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(path, &st) == 0) {
|
||||||
|
return S_ISDIR(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
str_lcp2(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (!a || !b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (a[i] && b[i] && a[i] == b[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
swap_size_t(size_t *first, size_t *second)
|
swap_size_t(size_t *first, size_t *second)
|
||||||
{
|
{
|
||||||
@@ -111,3 +148,4 @@ die(const char* s)
|
|||||||
perror(s);
|
perror(s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
core.h
17
core.h
@@ -4,7 +4,18 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
#define INITIAL_CAPACITY 8
|
#ifndef KE_VERSION
|
||||||
|
#define KE_VERSION "ke dev build"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define ESCSEQ "\x1b["
|
||||||
|
#define CTRL_KEY(key) ((key)&0x1f)
|
||||||
|
#define TAB_STOP 8
|
||||||
|
#define MSG_TIMEO 3
|
||||||
|
|
||||||
|
#define TAB_STOP 8
|
||||||
|
#define INITIAL_CAPACITY 8
|
||||||
|
|
||||||
|
|
||||||
typedef enum key_press {
|
typedef enum key_press {
|
||||||
@@ -28,6 +39,8 @@ char *strnstr(const char *s, const char *find, size_t slen);
|
|||||||
#define INCLUDE_STRNSTR
|
#define INCLUDE_STRNSTR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int path_is_dir(const char *path);
|
||||||
|
size_t str_lcp2(const char *a, const char *b);
|
||||||
void swap_size_t(size_t *first, size_t *second);
|
void swap_size_t(size_t *first, size_t *second);
|
||||||
int next_power_of_2(int n);
|
int next_power_of_2(int n);
|
||||||
int cap_growth(int cap, int sz);
|
int cap_growth(int cap, int sz);
|
||||||
@@ -36,4 +49,4 @@ void kwrite(int fd, const char *buf, int len);
|
|||||||
void die(const char *s);
|
void die(const char *s);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
50
editing.h
Normal file
50
editing.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef KE_EDITING_H
|
||||||
|
#define KE_EDITING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "abuf.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* miscellaneous */
|
||||||
|
void file_open_prompt_cb(char *buf, int16_t key);
|
||||||
|
int erow_render_to_cursor(const abuf *row, int cx);
|
||||||
|
int erow_cursor_to_render(abuf *row, int rx);
|
||||||
|
int erow_init(abuf *row, int len);
|
||||||
|
void erow_insert(int at, char *s, int len);
|
||||||
|
void jump_to_position(size_t col, size_t row);
|
||||||
|
void goto_line(void);
|
||||||
|
int cursor_at_eol(void);
|
||||||
|
int iswordchar(unsigned char c);
|
||||||
|
void find_next_word(void);
|
||||||
|
void delete_next_word(void);
|
||||||
|
void find_prev_word(void);
|
||||||
|
void delete_prev_word(void);
|
||||||
|
void delete_row(size_t at);
|
||||||
|
void row_append_row(abuf *row, const char *s, int len);
|
||||||
|
void row_insert_ch(abuf *row, int at, int16_t c);
|
||||||
|
void row_delete_ch(abuf *row, int at);
|
||||||
|
void insertch(int16_t c);
|
||||||
|
void deletech(uint8_t op);
|
||||||
|
void open_file(const char *filename);
|
||||||
|
char *rows_to_buffer(int *buflen);
|
||||||
|
int save_file(void);
|
||||||
|
uint16_t is_arrow_key(int16_t c);
|
||||||
|
int16_t get_keypress(void);
|
||||||
|
char *editor_prompt(const char*, void (*cb)(char*, int16_t));
|
||||||
|
void editor_find_callback(char *query, int16_t c);
|
||||||
|
void editor_find(void);
|
||||||
|
void editor_openfile(void);
|
||||||
|
int first_nonwhitespace(abuf *row);
|
||||||
|
void move_cursor_once(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);
|
||||||
|
char *get_cloc_code_lines(const char *filename);
|
||||||
|
int dump_pidfile(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
5
editor.c
5
editor.c
@@ -16,7 +16,7 @@
|
|||||||
* Global editor instance
|
* Global editor instance
|
||||||
*/
|
*/
|
||||||
struct editor editor = {
|
struct editor editor = {
|
||||||
.cols = 0,
|
.cols = 0,
|
||||||
.rows = 0,
|
.rows = 0,
|
||||||
.mode = 0,
|
.mode = 0,
|
||||||
.killring = NULL,
|
.killring = NULL,
|
||||||
@@ -79,7 +79,6 @@ init_editor(void)
|
|||||||
void
|
void
|
||||||
reset_editor(void)
|
reset_editor(void)
|
||||||
{
|
{
|
||||||
/* Reset the current buffer's contents/state. */
|
|
||||||
buffer *b = buffer_current();
|
buffer *b = buffer_current();
|
||||||
if (b == NULL) {
|
if (b == NULL) {
|
||||||
return;
|
return;
|
||||||
@@ -91,6 +90,7 @@ reset_editor(void)
|
|||||||
}
|
}
|
||||||
free(b->row);
|
free(b->row);
|
||||||
}
|
}
|
||||||
|
|
||||||
b->row = NULL;
|
b->row = NULL;
|
||||||
b->nrows = 0;
|
b->nrows = 0;
|
||||||
b->rowoffs = 0;
|
b->rowoffs = 0;
|
||||||
@@ -107,3 +107,4 @@ reset_editor(void)
|
|||||||
b->mark_curx = 0;
|
b->mark_curx = 0;
|
||||||
b->mark_cury = 0;
|
b->mark_cury = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
356
killring.c
Normal file
356
killring.c
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "abuf.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "editing.h"
|
||||||
|
#include "editor.h"
|
||||||
|
#include "killring.h"
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_flush(void)
|
||||||
|
{
|
||||||
|
if (editor.killring != NULL) {
|
||||||
|
ab_free(editor.killring);
|
||||||
|
free(editor.killring);
|
||||||
|
editor.killring = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_yank(void)
|
||||||
|
{
|
||||||
|
if (editor.killring == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Insert killring contents at the cursor without clearing the ring.
|
||||||
|
* Interpret '\n' as an actual newline() rather than inserting a raw 0x0A
|
||||||
|
* byte, so yanked content preserves lines correctly.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < (int)editor.killring->size; i++) {
|
||||||
|
unsigned char ch = (unsigned char)editor.killring->b[i];
|
||||||
|
if (ch == '\n') {
|
||||||
|
newline();
|
||||||
|
} else {
|
||||||
|
insertch(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_start_with_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
abuf *row = NULL;
|
||||||
|
|
||||||
|
if (editor.killring != NULL) {
|
||||||
|
ab_free(editor.killring);
|
||||||
|
free(editor.killring);
|
||||||
|
editor.killring = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.killring = malloc(sizeof(abuf));
|
||||||
|
assert(editor.killring != NULL);
|
||||||
|
assert(erow_init(editor.killring, 0) == 0);
|
||||||
|
|
||||||
|
/* append one char to empty killring without affecting editor.dirty */
|
||||||
|
row = editor.killring;
|
||||||
|
|
||||||
|
row->b = realloc(row->b, row->size + 2);
|
||||||
|
assert(row->b != NULL);
|
||||||
|
row->b[row->size] = ch;
|
||||||
|
row->size++;
|
||||||
|
row->b[row->size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_append_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
abuf *row = NULL;
|
||||||
|
|
||||||
|
if (editor.killring == NULL) {
|
||||||
|
killring_start_with_char(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = editor.killring;
|
||||||
|
row->b = realloc(row->b, row->size + 2);
|
||||||
|
assert(row->b != NULL);
|
||||||
|
row->b[row->size] = ch;
|
||||||
|
row->size++;
|
||||||
|
row->b[row->size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
killring_prepend_char(unsigned char ch)
|
||||||
|
{
|
||||||
|
abuf *row = NULL;
|
||||||
|
|
||||||
|
if (editor.killring == NULL) {
|
||||||
|
killring_start_with_char(ch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = editor.killring;
|
||||||
|
row->b = realloc(row->b, row->size + 2);
|
||||||
|
assert(row->b != NULL);
|
||||||
|
memmove(&row->b[1], &row->b[0], row->size + 1);
|
||||||
|
row->b[0] = ch;
|
||||||
|
row->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
toggle_markset(void)
|
||||||
|
{
|
||||||
|
if (EMARK_SET) {
|
||||||
|
EMARK_SET = 0;
|
||||||
|
editor_set_status("Mark cleared.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EMARK_SET = 1;
|
||||||
|
EMARK_CURX = ECURX;
|
||||||
|
EMARK_CURY = ECURY;
|
||||||
|
editor_set_status("Mark set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
cursor_after_mark(void)
|
||||||
|
{
|
||||||
|
if (EMARK_CURY < ECURY) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EMARK_CURY > ECURY) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ECURX >= EMARK_CURX;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
count_chars_from_cursor_to_mark(void)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
size_t curx = ECURX;
|
||||||
|
size_t cury = ECURY;
|
||||||
|
size_t markx = EMARK_CURX;
|
||||||
|
size_t marky = EMARK_CURY;
|
||||||
|
|
||||||
|
if (!cursor_after_mark()) {
|
||||||
|
swap_size_t(&curx, &markx);
|
||||||
|
swap_size_t(&curx, &marky);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECURX = markx;
|
||||||
|
ECURY = marky;
|
||||||
|
|
||||||
|
while (ECURY != cury) {
|
||||||
|
while (!cursor_at_eol()) {
|
||||||
|
move_cursor(ARROW_RIGHT, 1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
move_cursor(ARROW_RIGHT, 1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ECURX != curx) {
|
||||||
|
count++;
|
||||||
|
move_cursor(ARROW_RIGHT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
kill_region(void)
|
||||||
|
{
|
||||||
|
size_t curx = ECURX;
|
||||||
|
size_t cury = ECURY;
|
||||||
|
size_t markx = EMARK_CURX;
|
||||||
|
size_t marky = EMARK_CURY;
|
||||||
|
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kill the current killring first */
|
||||||
|
killring_flush();
|
||||||
|
|
||||||
|
if (!cursor_after_mark()) {
|
||||||
|
swap_size_t(&curx, &markx);
|
||||||
|
swap_size_t(&cury, &marky);
|
||||||
|
}
|
||||||
|
|
||||||
|
ECURX = markx;
|
||||||
|
ECURY = marky;
|
||||||
|
|
||||||
|
while (ECURY != cury) {
|
||||||
|
while (!cursor_at_eol()) {
|
||||||
|
killring_append_char(EROW[ECURY].b[ECURX]);
|
||||||
|
move_cursor(ARROW_RIGHT, 0);
|
||||||
|
}
|
||||||
|
killring_append_char('\n');
|
||||||
|
move_cursor(ARROW_RIGHT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ECURX != curx) {
|
||||||
|
killring_append_char(EROW[ECURY].b[ECURX]);
|
||||||
|
move_cursor(ARROW_RIGHT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor_set_status("Region killed.");
|
||||||
|
/* clearing the mark needs to be done outside this function; *
|
||||||
|
* when deleting the region, the mark needs to be set too. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
indent_region(void)
|
||||||
|
{
|
||||||
|
size_t start_row = 0;
|
||||||
|
size_t end_row = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EMARK_CURY < ECURY) {
|
||||||
|
start_row = EMARK_CURY;
|
||||||
|
end_row = ECURY;
|
||||||
|
} else if (EMARK_CURY > ECURY) {
|
||||||
|
start_row = ECURY;
|
||||||
|
end_row = EMARK_CURY;
|
||||||
|
} else {
|
||||||
|
start_row = end_row = ECURY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure bounds are valid */
|
||||||
|
if (end_row >= ENROWS) {
|
||||||
|
end_row = ENROWS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_row >= ENROWS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = start_row; i <= end_row; i++) {
|
||||||
|
row_insert_ch(&EROW[i], 0, '\t');
|
||||||
|
}
|
||||||
|
|
||||||
|
ECURX = 0;
|
||||||
|
EDIRTY++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
unindent_region(void)
|
||||||
|
{
|
||||||
|
size_t start_row = 0;
|
||||||
|
size_t end_row = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t del = 0;
|
||||||
|
abuf *row = NULL;
|
||||||
|
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
editor_set_status("Mark not set.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EMARK_CURY < ECURY ||
|
||||||
|
(EMARK_CURY == ECURY && EMARK_CURX < ECURX)) {
|
||||||
|
start_row = EMARK_CURY;
|
||||||
|
end_row = ECURY;
|
||||||
|
} else {
|
||||||
|
start_row = ECURY;
|
||||||
|
end_row = EMARK_CURY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_row >= ENROWS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_row >= ENROWS) {
|
||||||
|
end_row = ENROWS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = start_row; i <= end_row; i++) {
|
||||||
|
row = &EROW[i];
|
||||||
|
|
||||||
|
if (row->size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row->b[0] == '\t') {
|
||||||
|
row_delete_ch(row, 0);
|
||||||
|
} else if (row->b[0] == ' ') {
|
||||||
|
del = 0;
|
||||||
|
|
||||||
|
while (del < TAB_STOP && del < row->size &&
|
||||||
|
row->b[del] == ' ') {
|
||||||
|
del++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (del > 0) {
|
||||||
|
/* +1 for NUL */
|
||||||
|
memmove(row->b, row->b + del,
|
||||||
|
row->size - del + 1);
|
||||||
|
row->size -= del;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ECURX = 0;
|
||||||
|
ECURY = start_row;
|
||||||
|
|
||||||
|
EDIRTY++;
|
||||||
|
editor_set_status("Region unindented");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_region(void)
|
||||||
|
{
|
||||||
|
size_t count = count_chars_from_cursor_to_mark();
|
||||||
|
size_t killed = 0;
|
||||||
|
size_t curx = ECURX;
|
||||||
|
size_t cury = ECURY;
|
||||||
|
size_t markx = EMARK_CURX;
|
||||||
|
size_t marky = EMARK_CURY;
|
||||||
|
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor_after_mark()) {
|
||||||
|
swap_size_t(&curx, &markx);
|
||||||
|
swap_size_t(&cury, &marky);
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_to_position(markx, marky);
|
||||||
|
|
||||||
|
while (killed < count) {
|
||||||
|
move_cursor(ARROW_RIGHT, 0);
|
||||||
|
deletech(KILLRING_NO_OP);
|
||||||
|
killed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ECURX != markx && ECURY != marky) {
|
||||||
|
deletech(KILLRING_NO_OP);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.kill = 1;
|
||||||
|
editor_set_status("Region killed.");
|
||||||
|
}
|
||||||
26
killring.h
Normal file
26
killring.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef KE_KILLRING_H
|
||||||
|
#define KE_KILLRING_H
|
||||||
|
|
||||||
|
|
||||||
|
#define KILLRING_NO_OP 0 /* don't touch the killring */
|
||||||
|
#define KILLRING_APPEND 1 /* append deleted chars */
|
||||||
|
#define KILLRING_PREPEND 2 /* prepend deleted chars */
|
||||||
|
#define KILLING_SET 3 /* set killring to deleted char */
|
||||||
|
#define KILLRING_FLUSH 4 /* clear the killring */
|
||||||
|
|
||||||
|
|
||||||
|
void killring_flush(void);
|
||||||
|
void killring_yank(void);
|
||||||
|
void killring_start_with_char(unsigned char ch);
|
||||||
|
void killring_append_char(unsigned char ch);
|
||||||
|
void killring_prepend_char(unsigned char ch);
|
||||||
|
void toggle_markset(void);
|
||||||
|
int cursor_after_mark(void);
|
||||||
|
int count_chars_from_cursor_to_mark(void);
|
||||||
|
void kill_region(void);
|
||||||
|
void indent_region(void);
|
||||||
|
void unindent_region(void);
|
||||||
|
void delete_region(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
438
process.c
Normal file
438
process.c
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <_string.h>
|
||||||
|
|
||||||
|
#include "editing.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "editor.h"
|
||||||
|
#include "killring.h"
|
||||||
|
#include "term.h"
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
process_kcommand(const int16_t c)
|
||||||
|
{
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
int jumpx = 0;
|
||||||
|
int jumpy = 0;
|
||||||
|
int reps = 0;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case BACKSPACE:
|
||||||
|
while (ECURX > 0) {
|
||||||
|
process_normal(BACKSPACE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (EMARK_SET) {
|
||||||
|
indent_region();
|
||||||
|
} else {
|
||||||
|
editor_set_status("Mark not set.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
if (EMARK_SET) {
|
||||||
|
unindent_region();
|
||||||
|
} else {
|
||||||
|
editor_set_status("Mark not set.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('\\'):
|
||||||
|
/* sometimes it's nice to dump core */
|
||||||
|
disable_termraw();
|
||||||
|
abort();
|
||||||
|
case '@':
|
||||||
|
if (!dump_pidfile()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHRU */
|
||||||
|
case '!':
|
||||||
|
/* useful for debugging */
|
||||||
|
editor_set_status("PID: %ld", (long) getpid());
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
toggle_markset();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY(' '):
|
||||||
|
jumpx = EMARK_CURX;
|
||||||
|
jumpy = EMARK_CURY;
|
||||||
|
EMARK_CURX = ECURX;
|
||||||
|
EMARK_CURY = ECURY;
|
||||||
|
|
||||||
|
jump_to_position(jumpx, jumpy);
|
||||||
|
editor_set_status("Jumped to mark");
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
buffer_close_current();
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (ECURX == 0 && cursor_at_eol()) {
|
||||||
|
delete_row(ECURY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reps = uarg_get();
|
||||||
|
while (reps--) {
|
||||||
|
while ((EROW[ECURY].size - ECURX) > 0) {
|
||||||
|
process_normal(DEL_KEY);
|
||||||
|
}
|
||||||
|
if (reps) {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case DEL_KEY:
|
||||||
|
case CTRL_KEY('d'):
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
delete_row(ECURY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case CTRL_KEY('e'):
|
||||||
|
if (EDIRTY && editor.dirtyex) {
|
||||||
|
editor_set_status(
|
||||||
|
"File not saved - C-k e again to open a new file anyways.");
|
||||||
|
editor.dirtyex = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editor_openfile();
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (editor.killring == NULL || editor.killring->size == 0) {
|
||||||
|
editor_set_status("The kill ring is empty.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = editor.killring ? editor.killring->size : 0;
|
||||||
|
killring_flush();
|
||||||
|
editor_set_status("Kill ring cleared (%lu characters)", len);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
buffer_next();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
buffer_prev();
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
buffer_switch_by_name();
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
goto_line();
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
editor_set_status("Mark not set.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpx = EMARK_CURX;
|
||||||
|
jumpy = EMARK_CURY;
|
||||||
|
EMARK_CURX = ECURX;
|
||||||
|
EMARK_CURY = ECURY;
|
||||||
|
|
||||||
|
jump_to_position(jumpx, jumpy);
|
||||||
|
editor_set_status("Jumped to mark; mark is now the previous location.");
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
buf = get_cloc_code_lines(EFILENAME);
|
||||||
|
|
||||||
|
editor_set_status("Lines of code: %s", buf);
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
/* todo: fix the process failed: success issue */
|
||||||
|
if (system("make") != 0) {
|
||||||
|
editor_set_status(
|
||||||
|
"process failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
} else {
|
||||||
|
editor_set_status("make: ok");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
if (EDIRTY && editor.dirtyex) {
|
||||||
|
editor_set_status(
|
||||||
|
"File not saved - C-k q again to quit.");
|
||||||
|
editor.dirtyex = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
case CTRL_KEY('q'):
|
||||||
|
exit(0);
|
||||||
|
case CTRL_KEY('r'):
|
||||||
|
if (EDIRTY && editor.dirtyex) {
|
||||||
|
editor_set_status("File not saved - C-k C-r again to reload.");
|
||||||
|
editor.dirtyex = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpx = ECURX;
|
||||||
|
jumpy = ECURY;
|
||||||
|
buf = strdup(EFILENAME);
|
||||||
|
|
||||||
|
reset_editor();
|
||||||
|
open_file(buf);
|
||||||
|
display_refresh();
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
jump_to_position(jumpx, jumpy);
|
||||||
|
editor_set_status("file reloaded");
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('s'):
|
||||||
|
case 's':
|
||||||
|
save_file();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('x'):
|
||||||
|
case 'x':
|
||||||
|
exit(save_file());
|
||||||
|
case 'u':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {}
|
||||||
|
editor_set_status("Undo not implemented.");
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {}
|
||||||
|
editor_set_status("Redo not implemented.");
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
killring_yank();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESC_KEY:
|
||||||
|
case CTRL_KEY('g'):
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isprint(c)) {
|
||||||
|
editor_set_status("unknown kcommand '%c'", c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor_set_status("unknown kcommand: %04x", c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.dirtyex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
process_normal(int16_t c)
|
||||||
|
{
|
||||||
|
size_t cols = 0;
|
||||||
|
size_t rows = 0;
|
||||||
|
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)) {
|
||||||
|
/* moving the cursor breaks a delete sequence */
|
||||||
|
editor.kill = 0;
|
||||||
|
move_cursor(c, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '\r':
|
||||||
|
newline();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('k'):
|
||||||
|
editor.mode = MODE_KCOMMAND;
|
||||||
|
return;
|
||||||
|
case BACKSPACE:
|
||||||
|
case CTRL_KEY('h'):
|
||||||
|
case CTRL_KEY('d'):
|
||||||
|
case DEL_KEY:
|
||||||
|
if (c == DEL_KEY || c == CTRL_KEY('d')) {
|
||||||
|
reps = uarg_get();
|
||||||
|
while (reps-- > 0) {
|
||||||
|
move_cursor(ARROW_RIGHT, 1);
|
||||||
|
deletech(KILLRING_APPEND);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reps = uarg_get();
|
||||||
|
while (reps-- > 0) {
|
||||||
|
deletech(KILLRING_PREPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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'):
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('l'):
|
||||||
|
if (get_winsz(&rows, &cols) == 0) {
|
||||||
|
editor.rows = rows;
|
||||||
|
editor.cols = cols;
|
||||||
|
} else {
|
||||||
|
editor_set_status("Couldn't update window size.");
|
||||||
|
}
|
||||||
|
display_refresh();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('s'):
|
||||||
|
editor_find();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('w'):
|
||||||
|
kill_region();
|
||||||
|
delete_region();
|
||||||
|
toggle_markset();
|
||||||
|
break;
|
||||||
|
case CTRL_KEY('y'):
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
killring_yank();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESC_KEY:
|
||||||
|
editor.mode = MODE_ESCAPE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (c == TAB_KEY) {
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
insertch(c);
|
||||||
|
}
|
||||||
|
} else if (c >= 0x20 && c != 0x7f) {
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps-- > 0) {
|
||||||
|
insertch(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.dirtyex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
process_escape(const int16_t c)
|
||||||
|
{
|
||||||
|
int reps = 0;
|
||||||
|
|
||||||
|
editor_set_status("hi");
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '>':
|
||||||
|
ECURY = ENROWS;
|
||||||
|
ECURX = 0;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
ECURY = 0;
|
||||||
|
ECURX = 0;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
find_prev_word();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
delete_next_word();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
find_next_word();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
toggle_markset();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if (!EMARK_SET) {
|
||||||
|
editor_set_status("mark isn't set");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
kill_region();
|
||||||
|
toggle_markset();
|
||||||
|
break;
|
||||||
|
case BACKSPACE:
|
||||||
|
reps = uarg_get();
|
||||||
|
|
||||||
|
while (reps--) {
|
||||||
|
delete_prev_word();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ESC_KEY:
|
||||||
|
case CTRL_KEY('g'):
|
||||||
|
break; /* escape from escape-mode the movie */
|
||||||
|
default:
|
||||||
|
editor_set_status("unknown ESC key: %04x", c);
|
||||||
|
}
|
||||||
|
|
||||||
|
uarg_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
process_keypress(void)
|
||||||
|
{
|
||||||
|
const int16_t c = get_keypress();
|
||||||
|
|
||||||
|
if (c <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (editor.mode) {
|
||||||
|
case MODE_KCOMMAND:
|
||||||
|
process_kcommand(c);
|
||||||
|
editor.mode = MODE_NORMAL;
|
||||||
|
break;
|
||||||
|
case MODE_NORMAL:
|
||||||
|
process_normal(c);
|
||||||
|
break;
|
||||||
|
case MODE_ESCAPE:
|
||||||
|
process_escape(c);
|
||||||
|
editor.mode = MODE_NORMAL;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
editor_set_status("we're in the %d-D space now cap'n",
|
||||||
|
editor.mode);
|
||||||
|
editor.mode = MODE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
22
process.h
Normal file
22
process.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef KE_PROCESS_H
|
||||||
|
#define KE_PROCESS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
/*
|
||||||
|
* define the keyboard input modes
|
||||||
|
* normal: no special mode
|
||||||
|
* kcommand: ^k commands
|
||||||
|
* escape: what happens when you hit escape?
|
||||||
|
*/
|
||||||
|
#define MODE_NORMAL 0
|
||||||
|
#define MODE_KCOMMAND 1
|
||||||
|
#define MODE_ESCAPE 2
|
||||||
|
|
||||||
|
void process_kcommand(int16_t c);
|
||||||
|
void process_normal(int16_t c);
|
||||||
|
void process_escape(int16_t c);
|
||||||
|
int process_keypress(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
228
term.c
228
term.c
@@ -1,13 +1,18 @@
|
|||||||
|
#include <sys/ioctl.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "editing.h"
|
||||||
|
#include "editor.h"
|
||||||
|
#include "process.h"
|
||||||
#include "term.h"
|
#include "term.h"
|
||||||
|
|
||||||
#define ESCSEQ "\x1b["
|
#define ESCSEQ "\x1b["
|
||||||
@@ -84,3 +89,224 @@ get_winsz(size_t *rows, size_t *cols)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
draw_rows(abuf *ab)
|
||||||
|
{
|
||||||
|
abuf *row = NULL;
|
||||||
|
char buf[editor.cols];
|
||||||
|
char c = 0;
|
||||||
|
size_t j = 0;
|
||||||
|
size_t filerow = 0;
|
||||||
|
size_t y = 0;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t padding = 0;
|
||||||
|
size_t printed = 0;
|
||||||
|
size_t rx = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < editor.rows; y++) {
|
||||||
|
filerow = y + EROWOFFS;
|
||||||
|
if (filerow >= ENROWS) {
|
||||||
|
if ((ENROWS == 0) && (y == editor.rows / 3)) {
|
||||||
|
len = snprintf(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
"%s",
|
||||||
|
KE_VERSION);
|
||||||
|
padding = (editor.rows - len) / 2;
|
||||||
|
|
||||||
|
if (padding) {
|
||||||
|
ab_append(ab, "|", 1);
|
||||||
|
padding--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (padding--)
|
||||||
|
ab_append(ab, " ", 1);
|
||||||
|
ab_append(ab, buf, len);
|
||||||
|
} else {
|
||||||
|
ab_append(ab, "|", 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row = &EROW[filerow];
|
||||||
|
j = 0;
|
||||||
|
rx = printed = 0;
|
||||||
|
|
||||||
|
while (j < row->size && printed < editor.cols) {
|
||||||
|
c = row->b[j];
|
||||||
|
|
||||||
|
if (rx < ECOLOFFS) {
|
||||||
|
if (c == '\t') rx += (TAB_STOP - (rx % TAB_STOP));
|
||||||
|
else if (c < 0x20) rx += 3;
|
||||||
|
else rx++;
|
||||||
|
j++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\t') {
|
||||||
|
int sp = TAB_STOP - (rx % TAB_STOP);
|
||||||
|
for (int k = 0; k < sp && printed < editor.cols; k++) {
|
||||||
|
ab_appendch(ab, ' ');
|
||||||
|
printed++;
|
||||||
|
rx++;
|
||||||
|
}
|
||||||
|
} else if (c < 0x20) {
|
||||||
|
char seq[4];
|
||||||
|
snprintf(seq, sizeof(seq), "\\%02x", c);
|
||||||
|
ab_append(ab, seq, 3);
|
||||||
|
printed += 3;
|
||||||
|
rx += 3;
|
||||||
|
} else {
|
||||||
|
ab_appendch(ab, c);
|
||||||
|
printed++;
|
||||||
|
rx++;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
len = printed;
|
||||||
|
}
|
||||||
|
ab_append(ab, ESCSEQ "K", 3);
|
||||||
|
ab_append(ab, "\r\n", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char
|
||||||
|
status_mode_char(void)
|
||||||
|
{
|
||||||
|
switch (editor.mode) {
|
||||||
|
case MODE_NORMAL:
|
||||||
|
return 'N';
|
||||||
|
case MODE_KCOMMAND:
|
||||||
|
return 'K';
|
||||||
|
case MODE_ESCAPE:
|
||||||
|
return 'E';
|
||||||
|
default:
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
draw_status_bar(abuf *ab)
|
||||||
|
{
|
||||||
|
char status[editor.cols];
|
||||||
|
char rstatus[editor.cols];
|
||||||
|
char mstatus[editor.cols];
|
||||||
|
size_t len = 0;
|
||||||
|
size_t rlen = 0;
|
||||||
|
|
||||||
|
len = snprintf(status,
|
||||||
|
sizeof(status),
|
||||||
|
"%c%cke: %.20s - %lu lines",
|
||||||
|
status_mode_char(),
|
||||||
|
EDIRTY ? '!' : '-',
|
||||||
|
EFILENAME ? EFILENAME : "[no file]",
|
||||||
|
ENROWS);
|
||||||
|
|
||||||
|
if (EMARK_SET) {
|
||||||
|
snprintf(mstatus,
|
||||||
|
sizeof(mstatus),
|
||||||
|
" | M: %lu, %lu ",
|
||||||
|
EMARK_CURX + 1,
|
||||||
|
EMARK_CURY + 1);
|
||||||
|
} else {
|
||||||
|
snprintf(mstatus, sizeof(mstatus), " | M:clear ");
|
||||||
|
}
|
||||||
|
|
||||||
|
rlen = snprintf(rstatus,
|
||||||
|
sizeof(rstatus),
|
||||||
|
"L%lu/%lu C%lu %s",
|
||||||
|
ECURY + 1,
|
||||||
|
ENROWS,
|
||||||
|
ECURX + 1,
|
||||||
|
mstatus);
|
||||||
|
|
||||||
|
ab_append(ab, ESCSEQ "7m", 4);
|
||||||
|
ab_append(ab, status, len);
|
||||||
|
while (len < editor.cols) {
|
||||||
|
if (editor.cols - len == rlen) {
|
||||||
|
ab_append(ab, rstatus, rlen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ab_append(ab, " ", 1);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ab_append(ab, ESCSEQ "m", 3);
|
||||||
|
ab_append(ab, "\r\n", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
draw_message_line(abuf *ab)
|
||||||
|
{
|
||||||
|
size_t len = strlen(editor.msg);
|
||||||
|
|
||||||
|
ab_append(ab, ESCSEQ "K", 3);
|
||||||
|
if (len > editor.cols) {
|
||||||
|
len = editor.cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len && time(NULL) - editor.msgtm < MSG_TIMEO) {
|
||||||
|
ab_append(ab, editor.msg, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
scroll(void)
|
||||||
|
{
|
||||||
|
const abuf *row = NULL;
|
||||||
|
|
||||||
|
ERX = 0;
|
||||||
|
if (ECURY < ENROWS) {
|
||||||
|
row = &EROW[ECURY];
|
||||||
|
ERX = erow_render_to_cursor(row, ECURX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ECURY < EROWOFFS) {
|
||||||
|
EROWOFFS = ECURY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ECURY >= EROWOFFS + editor.rows) {
|
||||||
|
EROWOFFS = ECURY - editor.rows + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ERX < ECOLOFFS) {
|
||||||
|
ECOLOFFS = ERX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ERX >= ECOLOFFS + editor.cols) {
|
||||||
|
ECOLOFFS = ERX - editor.cols + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
display_refresh(void)
|
||||||
|
{
|
||||||
|
char buf[32] = {0};
|
||||||
|
abuf ab = ABUF_INIT;
|
||||||
|
|
||||||
|
scroll();
|
||||||
|
|
||||||
|
ab_append(&ab, ESCSEQ "?25l", 6);
|
||||||
|
ab_append(&ab, ESCSEQ "H", 3);
|
||||||
|
display_clear(&ab);
|
||||||
|
|
||||||
|
draw_rows(&ab);
|
||||||
|
draw_status_bar(&ab);
|
||||||
|
draw_message_line(&ab);
|
||||||
|
|
||||||
|
snprintf(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
ESCSEQ "%lu;%luH",
|
||||||
|
(ECURY - EROWOFFS) + 1,
|
||||||
|
(ERX - ECOLOFFS) + 1);
|
||||||
|
ab_append(&ab, buf, kstrnlen(buf, 32));
|
||||||
|
/* ab_append(&ab, ESCSEQ "1;2H", 7); */
|
||||||
|
ab_append(&ab, ESCSEQ "?25h", 6);
|
||||||
|
|
||||||
|
kwrite(STDOUT_FILENO, ab.b, (int)ab.size);
|
||||||
|
ab_free(&ab);
|
||||||
|
}
|
||||||
|
|||||||
16
term.h
16
term.h
@@ -3,11 +3,18 @@
|
|||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
|
|
||||||
|
|
||||||
/* Terminal control/setup API */
|
/* Terminal control/setup API */
|
||||||
void enable_termraw(void);
|
void enable_termraw(void);
|
||||||
void disable_termraw(void);
|
void disable_termraw(void);
|
||||||
void setup_terminal(void);
|
void setup_terminal(void);
|
||||||
void display_clear(abuf *ab);
|
void display_clear(abuf *ab);
|
||||||
|
void draw_rows(abuf *ab);
|
||||||
|
char status_mode_char(void);
|
||||||
|
void draw_status_bar(abuf *ab);
|
||||||
|
void draw_message_line(abuf *ab);
|
||||||
|
void scroll(void);
|
||||||
|
void display_refresh(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_winsz uses the TIOCGWINSZ to get the window size.
|
* get_winsz uses the TIOCGWINSZ to get the window size.
|
||||||
@@ -19,4 +26,5 @@ void display_clear(abuf *ab);
|
|||||||
*/
|
*/
|
||||||
int get_winsz(size_t *rows, size_t *cols);
|
int get_winsz(size_t *rows, size_t *cols);
|
||||||
|
|
||||||
|
|
||||||
#endif /* KE_TERM_H */
|
#endif /* KE_TERM_H */
|
||||||
|
|||||||
146
undo.c
146
undo.c
@@ -1,6 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* undo.c: ke's undo system
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
|
#include "buffer.h"
|
||||||
#include "undo.h"
|
#include "undo.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +24,7 @@ undo_node_new(undo_kind kind)
|
|||||||
ab_init(&node->text);
|
ab_init(&node->text);
|
||||||
|
|
||||||
node->next = NULL;
|
node->next = NULL;
|
||||||
node->parent = NULL;
|
node->next = NULL;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -27,8 +33,6 @@ undo_node_new(undo_kind kind)
|
|||||||
void
|
void
|
||||||
undo_node_free(undo_node *node)
|
undo_node_free(undo_node *node)
|
||||||
{
|
{
|
||||||
undo_node *next = NULL;
|
|
||||||
|
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -100,15 +104,137 @@ undo_begin(undo_tree *tree, undo_kind kind)
|
|||||||
void
|
void
|
||||||
undo_prepend(undo_tree *tree, abuf *buf)
|
undo_prepend(undo_tree *tree, abuf *buf)
|
||||||
{
|
{
|
||||||
|
assert(tree != NULL);
|
||||||
|
assert(tree->pending != NULL);
|
||||||
|
|
||||||
|
ab_prepend_ab(&tree->pending->text, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void undo_append(undo_tree *tree, abuf *buf);
|
void
|
||||||
void undo_prependch(undo_tree *tree, char c);
|
undo_append(undo_tree *tree, abuf *buf)
|
||||||
void undo_appendch(undo_tree *tree, char c);
|
{
|
||||||
void undo_commit(undo_tree *tree);
|
assert(tree != NULL);
|
||||||
void undo_apply(struct editor *editor);
|
assert(tree->pending != NULL);
|
||||||
void editor_undo(undo_tree *tree);
|
|
||||||
void editor_redo(undo_tree *tree);
|
ab_append_ab(&tree->pending->text, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
undo_prependch(undo_tree *tree, char c)
|
||||||
|
{
|
||||||
|
assert(tree != NULL);
|
||||||
|
assert(tree->pending != NULL);
|
||||||
|
|
||||||
|
ab_prependch(&tree->pending->text, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
undo_appendch(undo_tree *tree, char c)
|
||||||
|
{
|
||||||
|
assert(tree != NULL);
|
||||||
|
assert(tree->pending != NULL);
|
||||||
|
|
||||||
|
ab_appendch(&tree->pending->text, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
undo_commit(undo_tree *tree)
|
||||||
|
{
|
||||||
|
assert(tree != NULL);
|
||||||
|
|
||||||
|
if (tree->pending == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree->root == NULL) {
|
||||||
|
assert(tree->current == NULL);
|
||||||
|
|
||||||
|
tree->root = tree->pending;
|
||||||
|
tree->current = tree->pending;
|
||||||
|
tree->pending = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(tree->current != NULL);
|
||||||
|
if (tree->current->next != NULL) {
|
||||||
|
undo_node_free_all(tree->current->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
tree->pending->prev = tree->current;
|
||||||
|
tree->current->next = tree->pending;
|
||||||
|
tree->current = tree->pending;
|
||||||
|
tree->pending = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
undo_apply_undo(struct buffer *buf)
|
||||||
|
{
|
||||||
|
undo_node *node = buf->undo.current;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
case UNDO_INSERT:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
undo_apply(struct buffer *buf, int direction)
|
||||||
|
{
|
||||||
|
(void)direction;
|
||||||
|
|
||||||
|
if (buf == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo_commit(&buf->undo);
|
||||||
|
if (buf->undo.current == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction == UNDO_DIR_UNDO) {
|
||||||
|
return undo_apply_undo(buf);
|
||||||
|
} else if (direction == UNDO_DIR_UNDO) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
editor_undo(struct buffer *buf)
|
||||||
|
{
|
||||||
|
if (buf == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo_commit(&buf->undo);
|
||||||
|
return undo_apply(buf, UNDO_DIR_UNDO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
editor_redo(struct buffer *buf)
|
||||||
|
{
|
||||||
|
if (buf == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo_commit(&buf->undo);
|
||||||
|
return undo_apply(buf, UNDO_DIR_REDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
16
undo.h
16
undo.h
@@ -1,13 +1,19 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
#include "editor.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef KE_UNDO_H
|
#ifndef KE_UNDO_H
|
||||||
#define KE_UNDO_H
|
#define KE_UNDO_H
|
||||||
|
|
||||||
|
|
||||||
|
struct buffer;
|
||||||
|
|
||||||
|
|
||||||
|
#define UNDO_DIR_UNDO -1
|
||||||
|
#define UNDO_DIR_REDO 1
|
||||||
|
|
||||||
|
|
||||||
typedef enum undo_kind {
|
typedef enum undo_kind {
|
||||||
UNDO_INSERT = 1 << 0,
|
UNDO_INSERT = 1 << 0,
|
||||||
UNDO_UNKNOWN = 1 << 1,
|
UNDO_UNKNOWN = 1 << 1,
|
||||||
@@ -20,7 +26,7 @@ typedef struct undo_node {
|
|||||||
abuf text;
|
abuf text;
|
||||||
|
|
||||||
struct undo_node *next;
|
struct undo_node *next;
|
||||||
struct undo_node *parent;
|
struct undo_node *prev;
|
||||||
|
|
||||||
} undo_node;
|
} undo_node;
|
||||||
|
|
||||||
@@ -42,9 +48,9 @@ void undo_append(undo_tree *tree, abuf *buf);
|
|||||||
void undo_prependch(undo_tree *tree, char c);
|
void undo_prependch(undo_tree *tree, char c);
|
||||||
void undo_appendch(undo_tree *tree, char c);
|
void undo_appendch(undo_tree *tree, char c);
|
||||||
void undo_commit(undo_tree *tree);
|
void undo_commit(undo_tree *tree);
|
||||||
void undo_apply(struct editor *editor);
|
int undo_apply(struct buffer *buf, int direction);
|
||||||
void editor_undo(undo_tree *tree);
|
int editor_undo(struct buffer *buf);
|
||||||
void editor_redo(undo_tree *tree);
|
int editor_redo(struct buffer *buf);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user