Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a574df2ab7 | |||
| a9bcb0d36b | |||
| 7b20e9ee37 | |||
| 5d581c1c2f | |||
| 78e4f84f7b |
@@ -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 "2.0.0")
|
set(KE_VERSION "2.1.0")
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
50
abuf.c
50
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)
|
||||||
{
|
{
|
||||||
@@ -19,25 +28,31 @@ ab_init(abuf *buf)
|
|||||||
void
|
void
|
||||||
ab_init_cap(abuf *buf, const size_t cap)
|
ab_init_cap(abuf *buf, const size_t cap)
|
||||||
{
|
{
|
||||||
buf->b = calloc(cap, 1);
|
ab_init(buf);
|
||||||
buf->size = 0;
|
|
||||||
buf->cap = cap;
|
if (cap > 0) {
|
||||||
|
ab_resize(buf, cap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ab_resize(abuf *buf, size_t cap)
|
ab_resize(abuf *buf, size_t cap)
|
||||||
{
|
{
|
||||||
cap = cap_growth(buf->cap, cap);
|
char *newbuf = NULL;
|
||||||
buf->b = realloc(buf->b, cap);
|
|
||||||
assert(buf->b != NULL);
|
cap = cap_growth(buf->cap, cap) + 1;
|
||||||
|
newbuf = realloc(buf->b, cap);
|
||||||
|
assert(newbuf != NULL);
|
||||||
buf->cap = cap;
|
buf->cap = cap;
|
||||||
|
buf->b = newbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 +60,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 +74,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 +83,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);
|
||||||
|
|||||||
4
abuf.h
4
abuf.h
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* abuf.h - append/prepend buffer utilities
|
* abuf.h - append/prepend buffer utilities
|
||||||
*/
|
*/
|
||||||
#ifndef ABUF_H
|
#ifndef KE_ABUF_H
|
||||||
#define ABUF_H
|
#define KE_ABUF_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|||||||
190
buffer.c
190
buffer.c
@@ -35,12 +35,13 @@ static int
|
|||||||
buffer_find_exact_by_name(const char *name)
|
buffer_find_exact_by_name(const char *name)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
buffer *b = NULL;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < editor.bufcount; i++) {
|
for (i = 0; i < editor.bufcount; i++) {
|
||||||
b = editor.buffers[i];
|
b = editor.buffers[i];
|
||||||
const char *full = b->filename;
|
const char *full = b->filename;
|
||||||
const char *base = buf_basename(full);
|
const char *base = buf_basename(full);
|
||||||
@@ -66,11 +67,13 @@ static int
|
|||||||
buffer_collect_prefix_matches(const char *prefix, int *out_idx, const int max_out)
|
buffer_collect_prefix_matches(const char *prefix, int *out_idx, const int max_out)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
buffer *b = NULL;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int matched = 0;
|
int matched = 0;
|
||||||
|
size_t i = 0;
|
||||||
size_t plen = (prefix ? strlen(prefix) : 0);
|
size_t plen = (prefix ? strlen(prefix) : 0);
|
||||||
|
|
||||||
for (int i = 0; i < editor.bufcount; i++) {
|
for (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;
|
||||||
@@ -205,49 +208,6 @@ buffer_switch_prompt_cb(char *buf, const int16_t key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
buffer_bind_to_editor(const buffer *b)
|
|
||||||
{
|
|
||||||
if (b == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.curx = b->curx;
|
|
||||||
editor.cury = b->cury;
|
|
||||||
editor.rx = b->rx;
|
|
||||||
editor.nrows = b->nrows;
|
|
||||||
editor.rowoffs = b->rowoffs;
|
|
||||||
editor.coloffs = b->coloffs;
|
|
||||||
editor.row = b->row;
|
|
||||||
editor.filename = b->filename;
|
|
||||||
editor.dirty = b->dirty;
|
|
||||||
editor.mark_set = b->mark_set;
|
|
||||||
editor.mark_curx = b->mark_curx;
|
|
||||||
editor.mark_cury = b->mark_cury;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
buffer_extract_from_editor(buffer *b)
|
|
||||||
{
|
|
||||||
if (b == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
b->curx = editor.curx;
|
|
||||||
b->cury = editor.cury;
|
|
||||||
b->rx = editor.rx;
|
|
||||||
b->nrows = editor.nrows;
|
|
||||||
b->rowoffs = editor.rowoffs;
|
|
||||||
b->coloffs = editor.coloffs;
|
|
||||||
b->row = editor.row;
|
|
||||||
b->filename = editor.filename;
|
|
||||||
b->dirty = editor.dirty;
|
|
||||||
b->mark_set = editor.mark_set;
|
|
||||||
b->mark_curx = editor.mark_curx;
|
|
||||||
b->mark_cury = editor.mark_cury;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
buffer_name(buffer *b)
|
buffer_name(buffer *b)
|
||||||
{
|
{
|
||||||
@@ -262,29 +222,43 @@ 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 = -1;
|
editor.curbuf = 0;
|
||||||
|
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 == editor.bufcap) {
|
||||||
|
editor.bufcap = (size_t)cap_growth((int)editor.bufcap, (int)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);
|
||||||
|
|
||||||
buf->curx = 0;
|
buf->curx = 0;
|
||||||
buf->cury = 0;
|
buf->cury = 0;
|
||||||
@@ -299,34 +273,28 @@ 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;
|
editor.buffers[editor.bufcount] = buf;
|
||||||
idx = editor.bufcount;
|
idx = (int)editor.bufcount;
|
||||||
editor.bufcount++;
|
editor.bufcount++;
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_save_current(void)
|
buffer_save_current(void)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
/* No-op: editor no longer mirrors per-buffer fields */
|
||||||
|
(void)editor;
|
||||||
if (editor.curbuf < 0 || editor.curbuf >= editor.bufcount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
b = editor.buffers[editor.curbuf];
|
|
||||||
buffer_extract_from_editor(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer *
|
buffer *
|
||||||
buffer_current(void)
|
buffer_current(void)
|
||||||
{
|
{
|
||||||
if (editor.curbuf < 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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -360,75 +328,69 @@ 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 || idx >= editor.bufcount) {
|
if (idx < 0 || (size_t)idx >= editor.bufcount) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor.curbuf == idx) {
|
if (editor.curbuf == (size_t)idx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor.curbuf >= 0) {
|
b = editor.buffers[idx];
|
||||||
buffer_save_current();
|
editor.curbuf = (size_t)idx;
|
||||||
}
|
editor.dirtyex = 1;
|
||||||
|
editor_set_status("Switched to buffer %d: %s", editor.curbuf, buffer_name(b));
|
||||||
b = editor.buffers[idx];
|
|
||||||
buffer_bind_to_editor(b);
|
|
||||||
editor.curbuf = idx;
|
|
||||||
editor.dirtyex = 1;
|
|
||||||
editor_set_status("Switched to buffer %d: %s", editor.curbuf, buffer_name(b));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_next(void)
|
buffer_next(void)
|
||||||
{
|
{
|
||||||
int 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(idx);
|
buffer_switch((int)idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_prev(void)
|
buffer_prev(void)
|
||||||
{
|
{
|
||||||
int idx = 0;
|
size_t idx = 0;
|
||||||
|
|
||||||
if (editor.bufcount <= 1) {
|
if (editor.bufcount <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = (editor.curbuf - 1 + editor.bufcount) % editor.bufcount;
|
idx = (editor.curbuf == 0) ? (editor.bufcount - 1) : (editor.curbuf - 1);
|
||||||
buffer_switch(idx);
|
buffer_switch((int)idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
buffer_close_current(void)
|
buffer_close_current(void)
|
||||||
{
|
{
|
||||||
buffer *b = NULL;
|
buffer *b = NULL;
|
||||||
int closing = 0;
|
size_t closing = 0;
|
||||||
int target = 0;
|
int target = 0;
|
||||||
int nb = 0;
|
int nb = 0;
|
||||||
|
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (editor.curbuf < 0 || editor.curbuf >= editor.bufcount) {
|
if (editor.bufcount == 0 || editor.curbuf >= editor.bufcount) {
|
||||||
editor_set_status("No buffer to close.");
|
editor_set_status("No buffer to close.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
closing = editor.curbuf;
|
closing = editor.curbuf;
|
||||||
|
|
||||||
target = -1;
|
|
||||||
if (editor.bufcount > 1) {
|
if (editor.bufcount > 1) {
|
||||||
target = (closing - 1 >= 0) ? (closing - 1) : (closing + 1);
|
target = (closing > 0) ? (int) (closing - 1) : (int) (closing + 1);
|
||||||
buffer_switch(target);
|
buffer_switch(target);
|
||||||
} else {
|
} else {
|
||||||
nb = buffer_add_empty();
|
nb = buffer_add_empty();
|
||||||
@@ -438,7 +400,7 @@ buffer_close_current(void)
|
|||||||
b = editor.buffers[closing];
|
b = editor.buffers[closing];
|
||||||
if (b) {
|
if (b) {
|
||||||
if (b->row) {
|
if (b->row) {
|
||||||
for (int i = 0; i < b->nrows; i++) {
|
for (size_t i = 0; i < b->nrows; i++) {
|
||||||
ab_free(&b->row[i]);
|
ab_free(&b->row[i]);
|
||||||
}
|
}
|
||||||
free(b->row);
|
free(b->row);
|
||||||
@@ -451,11 +413,11 @@ buffer_close_current(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
memmove(&editor.buffers[closing], &editor.buffers[closing + 1],
|
memmove(&editor.buffers[closing], &editor.buffers[closing + 1],
|
||||||
sizeof(buffer *) * (editor.bufcount - closing - 1));
|
sizeof(buffer *) * (editor.bufcount - closing - 1));
|
||||||
|
|
||||||
editor.bufcount--;
|
editor.bufcount--;
|
||||||
if (editor.bufcount == 0) {
|
if (editor.bufcount == 0) {
|
||||||
editor.curbuf = -1;
|
editor.curbuf = 0;
|
||||||
} else {
|
} else {
|
||||||
if (editor.curbuf > closing) {
|
if (editor.curbuf > closing) {
|
||||||
editor.curbuf--;
|
editor.curbuf--;
|
||||||
@@ -464,7 +426,7 @@ buffer_close_current(void)
|
|||||||
|
|
||||||
editor.dirtyex = 1;
|
editor.dirtyex = 1;
|
||||||
editor_set_status("Closed buffer. Now on %s",
|
editor_set_status("Closed buffer. Now on %s",
|
||||||
buffer_name(editor.buffers[editor.curbuf]));
|
buffer_name(editor.buffers[editor.curbuf]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -497,7 +459,7 @@ buffer_switch_by_name(void)
|
|||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
buffer_switch(idx);
|
buffer_switch(idx);
|
||||||
} else {
|
} else {
|
||||||
editor_set_status("No such buffer: %s", name);
|
editor_set_status("No open buffer: %s", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
|
|||||||
14
buffer.h
14
buffer.h
@@ -1,19 +1,19 @@
|
|||||||
#ifndef BUFFER_H
|
#ifndef KE_BUFFER_H
|
||||||
#define BUFFER_H
|
#define KE_BUFFER_H
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct buffer {
|
typedef struct buffer {
|
||||||
int curx, cury;
|
size_t curx, cury;
|
||||||
int rx;
|
size_t rx;
|
||||||
int nrows;
|
size_t nrows;
|
||||||
int rowoffs, coloffs;
|
size_t rowoffs, coloffs;
|
||||||
abuf *row;
|
abuf *row;
|
||||||
char *filename;
|
char *filename;
|
||||||
int dirty;
|
int dirty;
|
||||||
int mark_set;
|
int mark_set;
|
||||||
int mark_curx, mark_cury;
|
size_t mark_curx, mark_cury;
|
||||||
} buffer;
|
} buffer;
|
||||||
|
|
||||||
/* Access current buffer and convenient aliases for file-specific fields */
|
/* Access current buffer and convenient aliases for file-specific fields */
|
||||||
|
|||||||
13
core.c
13
core.c
@@ -35,19 +35,8 @@ strnstr(const char *s, const char *find, size_t slen)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
char
|
|
||||||
nibble_to_hex(char c)
|
|
||||||
{
|
|
||||||
c &= 0xf;
|
|
||||||
if (c < 10) {
|
|
||||||
return (char)('0' + c);
|
|
||||||
}
|
|
||||||
return (char)('A' + (c - 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
swap_int(int *first, int *second)
|
swap_size_t(size_t *first, size_t *second)
|
||||||
{
|
{
|
||||||
*first ^= *second;
|
*first ^= *second;
|
||||||
*second ^= *first;
|
*second ^= *first;
|
||||||
|
|||||||
8
core.h
8
core.h
@@ -4,7 +4,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
#define calloc1(sz) calloc(1, sz)
|
|
||||||
#define INITIAL_CAPACITY 8
|
#define INITIAL_CAPACITY 8
|
||||||
|
|
||||||
|
|
||||||
@@ -29,10 +28,9 @@ char *strnstr(const char *s, const char *find, size_t slen);
|
|||||||
#define INCLUDE_STRNSTR
|
#define INCLUDE_STRNSTR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char nibble_to_hex(char c);
|
void swap_size_t(size_t *first, size_t *second);
|
||||||
void swap_int(int *first, int *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);
|
|
||||||
size_t kstrnlen(const char *buf, size_t max);
|
size_t kstrnlen(const char *buf, size_t max);
|
||||||
void kwrite(int fd, const char *buf, int len);
|
void kwrite(int fd, const char *buf, int len);
|
||||||
void die(const char *s);
|
void die(const char *s);
|
||||||
|
|||||||
95
editor.c
95
editor.c
@@ -16,31 +16,20 @@
|
|||||||
* Global editor instance
|
* Global editor instance
|
||||||
*/
|
*/
|
||||||
struct editor editor = {
|
struct editor editor = {
|
||||||
.cols = 0,
|
.cols = 0,
|
||||||
.rows = 0,
|
.rows = 0,
|
||||||
.curx = 0,
|
.mode = 0,
|
||||||
.cury = 0,
|
|
||||||
.rx = 0,
|
|
||||||
.mode = 0,
|
|
||||||
.nrows = 0,
|
|
||||||
.rowoffs = 0,
|
|
||||||
.coloffs = 0,
|
|
||||||
.row = NULL,
|
|
||||||
.killring = NULL,
|
.killring = NULL,
|
||||||
.kill = 0,
|
.kill = 0,
|
||||||
.no_kill = 0,
|
.no_kill = 0,
|
||||||
.filename = NULL,
|
.dirtyex = 0,
|
||||||
.dirty = 0,
|
.uarg = 0,
|
||||||
.dirtyex = 0,
|
.ucount = 0,
|
||||||
.mark_set = 0,
|
.msgtm = 0,
|
||||||
.mark_curx = 0,
|
.buffers = NULL,
|
||||||
.mark_cury = 0,
|
|
||||||
.uarg = 0,
|
|
||||||
.ucount = 0,
|
|
||||||
.msgtm = 0,
|
|
||||||
.buffers = NULL,
|
|
||||||
.bufcount = 0,
|
.bufcount = 0,
|
||||||
.curbuf = -1,
|
.curbuf = 0,
|
||||||
|
.bufcap = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -69,29 +58,19 @@ init_editor(void)
|
|||||||
editor.rows--; /* status bar */
|
editor.rows--; /* status bar */
|
||||||
editor.rows--; /* message line */
|
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:
|
/* don't clear out the kill ring:
|
||||||
* killing / yanking across files is helpful, and killring
|
* killing / yanking across files is helpful, and killring
|
||||||
* is initialized to NULL at program start.
|
* is initialized to NULL at program start.
|
||||||
*/
|
*/
|
||||||
editor.kill = 0;
|
editor.kill = 0;
|
||||||
editor.no_kill = 0;
|
editor.no_kill = 0;
|
||||||
|
|
||||||
editor.msg[0] = '\0';
|
editor.msg[0] = '\0';
|
||||||
editor.msgtm = 0;
|
editor.msgtm = 0;
|
||||||
|
|
||||||
editor.dirty = 0;
|
|
||||||
editor.mark_set = 0;
|
|
||||||
editor.mark_cury = editor.mark_curx = 0;
|
|
||||||
|
|
||||||
/* 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,25 +79,31 @@ init_editor(void)
|
|||||||
void
|
void
|
||||||
reset_editor(void)
|
reset_editor(void)
|
||||||
{
|
{
|
||||||
/* Clear current working set. Notably, does not reset terminal
|
/* Reset the current buffer's contents/state. */
|
||||||
* or buffers list. */
|
buffer *b = buffer_current();
|
||||||
for (int i = 0; i < editor.nrows; i++) {
|
if (b == NULL) {
|
||||||
ab_free(&editor.row[i]);
|
return;
|
||||||
}
|
|
||||||
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;
|
if (b->row) {
|
||||||
editor.mark_set = 0;
|
for (size_t i = 0; i < b->nrows; i++) {
|
||||||
editor.mark_cury = editor.mark_curx = 0;
|
ab_free(&b->row[i]);
|
||||||
|
}
|
||||||
|
free(b->row);
|
||||||
|
}
|
||||||
|
b->row = NULL;
|
||||||
|
b->nrows = 0;
|
||||||
|
b->rowoffs = 0;
|
||||||
|
b->coloffs = 0;
|
||||||
|
b->rx = 0;
|
||||||
|
b->curx = 0;
|
||||||
|
b->cury = 0;
|
||||||
|
if (b->filename) {
|
||||||
|
free(b->filename);
|
||||||
|
b->filename = NULL;
|
||||||
|
}
|
||||||
|
b->dirty = 0;
|
||||||
|
b->mark_set = 0;
|
||||||
|
b->mark_curx = 0;
|
||||||
|
b->mark_cury = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
28
editor.h
28
editor.h
@@ -1,5 +1,5 @@
|
|||||||
#ifndef EDITOR_H
|
#ifndef KE_EDITOR_H
|
||||||
#define EDITOR_H
|
#define KE_EDITOR_H
|
||||||
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -9,32 +9,20 @@
|
|||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
/* TODO(kyle): remove the "per-buffer" fields completely from the editor. */
|
|
||||||
|
|
||||||
struct editor {
|
struct editor {
|
||||||
int rows, cols;
|
size_t rows, cols;
|
||||||
int curx, cury; /* per-buffer */
|
|
||||||
int rx; /* per-buffer */
|
|
||||||
int mode;
|
int mode;
|
||||||
int nrows; /* per-buffer */
|
|
||||||
int rowoffs, coloffs; /* per-buffer */
|
|
||||||
abuf *row; /* per-buffer */
|
|
||||||
abuf *killring;
|
abuf *killring;
|
||||||
int kill; /* KILL CHAIN (\m/) */
|
int kill; /* KILL CHAIN (\m/) */
|
||||||
int no_kill; /* don't kill in delete_row */
|
int no_kill; /* don't kill in delete_row */
|
||||||
char *filename; /* per-buffer */
|
|
||||||
int dirty; /* per-buffer */
|
|
||||||
int dirtyex;
|
int dirtyex;
|
||||||
char msg[80];
|
char msg[80];
|
||||||
int mark_set; /* per-buffer */
|
|
||||||
int mark_curx, mark_cury; /* per-buffer */
|
|
||||||
int uarg, ucount; /* C-u support */
|
int uarg, ucount; /* C-u support */
|
||||||
time_t msgtm;
|
time_t msgtm;
|
||||||
|
struct buffer **buffers; /* array of buffers */
|
||||||
/* Multi-buffer support */
|
size_t bufcount; /* number of buffers */
|
||||||
struct buffer **buffers; /* array of buffers */
|
size_t curbuf; /* current buffer index */
|
||||||
int bufcount; /* number of buffers */
|
size_t bufcap; /* current buffer capacity */
|
||||||
int curbuf; /* current buffer index */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -44,4 +32,4 @@ void init_editor(void);
|
|||||||
void reset_editor(void);
|
void reset_editor(void);
|
||||||
|
|
||||||
|
|
||||||
#endif /* EDITOR_H */
|
#endif /* KE_EDITOR_H */
|
||||||
|
|||||||
154
scratch.c
154
scratch.c
@@ -2,15 +2,20 @@
|
|||||||
* scratch.c - ideas in progress
|
* scratch.c - ideas in progress
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "abuf.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define REFLOW_MARGIN 72
|
#define REFLOW_MARGIN 72
|
||||||
|
|
||||||
void
|
void
|
||||||
reflow_region(void)
|
reflow_region(void)
|
||||||
{
|
{
|
||||||
int start_row, end_row, i, col, wlen, this_len;
|
int start_row, end_row, i, col, wlen, this_len;
|
||||||
struct erow *row;
|
abuf *row;
|
||||||
struct abuf buf = ABUF_INIT;
|
struct abuf buf = ABUF_INIT;
|
||||||
struct abuf out = ABUF_INIT;
|
struct abuf out = ABUF_INIT;
|
||||||
int in_paragraph = 0;
|
int in_paragraph = 0;
|
||||||
int indent_len = 0;
|
int indent_len = 0;
|
||||||
char indent[REFLOW_MARGIN + 1];
|
char indent[REFLOW_MARGIN + 1];
|
||||||
@@ -19,38 +24,38 @@ reflow_region(void)
|
|||||||
char *p = NULL;
|
char *p = NULL;
|
||||||
char *s = NULL;
|
char *s = NULL;
|
||||||
|
|
||||||
if (editor.mark_set) {
|
if (EMARK_SET) {
|
||||||
if (editor.mark_cury < editor.cury ||
|
if (EMARK_CURY < ECURY ||
|
||||||
(editor.mark_cury == editor.cury &&
|
(EMARK_CURY == ECURY &&
|
||||||
editor.mark_curx < editor.curx)) {
|
EMARK_CURX < ECURX)) {
|
||||||
start_row = editor.mark_cury;
|
start_row = EMARK_CURY;
|
||||||
end_row = editor.cury;
|
end_row = ECURY;
|
||||||
} else {
|
} else {
|
||||||
start_row = editor.cury;
|
start_row = ECURY;
|
||||||
end_row = editor.mark_cury;
|
end_row = EMARK_CURY;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
start_row = end_row = editor.cury;
|
start_row = end_row = ECURY;
|
||||||
while (start_row > 0 && editor.row[start_row - 1].size > 0) {
|
while (start_row > 0 && EROW[start_row - 1].size > 0) {
|
||||||
start_row--;
|
start_row--;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (end_row < editor.nrows - 1 &&
|
while (end_row < ENROWS - 1 &&
|
||||||
editor.row[end_row + 1].size > 0) {
|
EROW[end_row + 1].size > 0) {
|
||||||
end_row++;
|
end_row++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_row >= editor.nrows) {
|
if (start_row >= ENROWS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end_row >= editor.nrows) {
|
if (end_row >= ENROWS) {
|
||||||
end_row = editor.nrows - 1;
|
end_row = ENROWS - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = start_row; i <= end_row; i++) {
|
for (i = start_row; i <= end_row; i++) {
|
||||||
row = &editor.row[i];
|
row = &EROW[i];
|
||||||
|
|
||||||
if (row->size == 0) {
|
if (row->size == 0) {
|
||||||
if (in_paragraph) {
|
if (in_paragraph) {
|
||||||
@@ -62,21 +67,21 @@ reflow_region(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_paragraph) {
|
if (!in_paragraph) {
|
||||||
indent_len = 0;
|
indent_len = 0;
|
||||||
while (indent_len < row->size &&
|
while (indent_len < (int)row->size &&
|
||||||
(row->line[indent_len] == ' ' ||
|
(row->b[indent_len] == ' ' ||
|
||||||
row->line[indent_len] == '\t')) {
|
row->b[indent_len] == '\t')) {
|
||||||
indent[indent_len] = row->line[indent_len], indent_len++;
|
indent[indent_len] = row->b[indent_len], indent_len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
indent[indent_len] = '\0';
|
indent[indent_len] = '\0';
|
||||||
in_paragraph = 1;
|
in_paragraph = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ab_append(&buf, row->line + indent_len, row->size - indent_len);
|
ab_append(&buf, row->b + indent_len, row->size - indent_len);
|
||||||
ab_append(&buf, " ", 1);
|
ab_append(&buf, " ", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_paragraph) {
|
if (in_paragraph) {
|
||||||
ab_append(&buf, "\n", 1);
|
ab_append(&buf, "\n", 1);
|
||||||
@@ -145,18 +150,57 @@ reflow_region(void)
|
|||||||
ab_free(&out);
|
ab_free(&out);
|
||||||
|
|
||||||
|
|
||||||
for (i = end_row; i >= start_row; i--) {
|
for (i = end_row; i >= start_row; i--) {
|
||||||
delete_row(i);
|
delete_row(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
s = buf.b;
|
s = buf.b;
|
||||||
while ((e = strchr(s, '\n'))) {
|
while ((e = strchr(s, '\n'))) {
|
||||||
erow_insert(start_row++, s, e - s);
|
erow_insert(start_row++, s, e - s);
|
||||||
s = e + 1;
|
s = e + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ab_free(&buf);
|
ab_free(&buf);
|
||||||
|
|
||||||
editor.dirty++;
|
EDIRTY++;
|
||||||
editor_set_status("Region reflowed to %d columns", REFLOW_MARGIN);
|
editor_set_status("Region reflowed to %d columns", REFLOW_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void clamp_curx_to_row(void)
|
||||||
|
{
|
||||||
|
abuf *row = NULL;
|
||||||
|
int maxx = 0;
|
||||||
|
|
||||||
|
if (ECURY >= ENROWS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = &EROW[ECURY];
|
||||||
|
if (ECURX < 0) {
|
||||||
|
ECURX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxx = (int) row->size;
|
||||||
|
if (ECURX > maxx) {
|
||||||
|
ECURX = maxx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void set_cursor(int col, int row)
|
||||||
|
{
|
||||||
|
if (row < 0) {
|
||||||
|
row = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row > ENROWS) {
|
||||||
|
row = ENROWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECURY = row;
|
||||||
|
ECURX = col;
|
||||||
|
|
||||||
|
clamp_curx_to_row();
|
||||||
}
|
}
|
||||||
16
term.c
16
term.c
@@ -71,16 +71,16 @@ setup_terminal(void)
|
|||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
get_winsz(int *rows, int *cols)
|
get_winsz(size_t *rows, size_t *cols)
|
||||||
{
|
{
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
|
|
||||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*cols = ws.ws_col;
|
*cols = (size_t)ws.ws_col;
|
||||||
*rows = ws.ws_row;
|
*rows = (size_t)ws.ws_row;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
8
term.h
8
term.h
@@ -1,5 +1,5 @@
|
|||||||
#ifndef TERM_H
|
#ifndef KE_TERM_H
|
||||||
#define TERM_H
|
#define KE_TERM_H
|
||||||
|
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
|
|
||||||
@@ -17,6 +17,6 @@ void display_clear(abuf *ab);
|
|||||||
* on this for now because it's bloaty and this works on OpenBSD and
|
* on this for now because it's bloaty and this works on OpenBSD and
|
||||||
* Linux, at least.
|
* Linux, at least.
|
||||||
*/
|
*/
|
||||||
int get_winsz(int *rows, int *cols);
|
int get_winsz(size_t *rows, size_t *cols);
|
||||||
|
|
||||||
#endif /* TERM_H */
|
#endif /* KE_TERM_H */
|
||||||
|
|||||||
42
undo.c
42
undo.c
@@ -1,8 +1,10 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include "abuf.h"
|
#include "abuf.h"
|
||||||
#include "undo.h"
|
#include "undo.h"
|
||||||
|
|
||||||
|
|
||||||
undo_node
|
undo_node *
|
||||||
undo_node_new(undo_kind kind)
|
undo_node_new(undo_kind kind)
|
||||||
{
|
{
|
||||||
undo_node *node = NULL;
|
undo_node *node = NULL;
|
||||||
@@ -13,10 +15,12 @@ undo_node_new(undo_kind kind)
|
|||||||
node->kind = kind;
|
node->kind = kind;
|
||||||
node->row = node->col = 0;
|
node->row = node->col = 0;
|
||||||
|
|
||||||
abuf_init(node->text);
|
ab_init(&node->text);
|
||||||
|
|
||||||
node->next = NULL;
|
node->next = NULL;
|
||||||
node->parent = NULL;
|
node->parent = NULL;
|
||||||
|
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -26,11 +30,10 @@ undo_node_free(undo_node *node)
|
|||||||
undo_node *next = NULL;
|
undo_node *next = NULL;
|
||||||
|
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
return NULL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
abuf_free(node-text);
|
ab_free(&node->text);
|
||||||
next = node->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +47,10 @@ undo_node_free_all(undo_node *node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (node != NULL) {
|
while (node != NULL) {
|
||||||
|
next = node->next;
|
||||||
undo_node_free(node);
|
undo_node_free(node);
|
||||||
free(node);
|
free(node);
|
||||||
node = node->next;
|
node = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +69,7 @@ undo_tree_init(undo_tree *tree)
|
|||||||
void
|
void
|
||||||
undo_tree_free(undo_tree *tree)
|
undo_tree_free(undo_tree *tree)
|
||||||
{
|
{
|
||||||
assert(tree == NULL);
|
assert(tree != NULL);
|
||||||
|
|
||||||
undo_node_free(tree->pending);
|
undo_node_free(tree->pending);
|
||||||
undo_node_free_all(tree->root);
|
undo_node_free_all(tree->root);
|
||||||
@@ -86,19 +90,25 @@ undo_begin(undo_tree *tree, undo_kind kind)
|
|||||||
undo_commit(tree);
|
undo_commit(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
pending = undo_new_new(kind);
|
pending = undo_node_new(kind);
|
||||||
assert(pending != NULL);
|
assert(pending != NULL);
|
||||||
|
|
||||||
tree->pending = pending;
|
tree->pending = pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void undo_prepend(abuf *buf);
|
void
|
||||||
void undo_append(buf *buf);
|
undo_prepend(undo_tree *tree, abuf *buf)
|
||||||
void undo_prependch(char c);
|
{
|
||||||
void undo_appendch(char c);
|
|
||||||
void undo_commit(void);
|
}
|
||||||
void undo_apply(undo_node *node);
|
|
||||||
void editor_undo(void);
|
|
||||||
void editor_redo(void);
|
void undo_append(undo_tree *tree, abuf *buf);
|
||||||
|
void undo_prependch(undo_tree *tree, char c);
|
||||||
|
void undo_appendch(undo_tree *tree, char c);
|
||||||
|
void undo_commit(undo_tree *tree);
|
||||||
|
void undo_apply(struct editor *editor);
|
||||||
|
void editor_undo(undo_tree *tree);
|
||||||
|
void editor_redo(undo_tree *tree);
|
||||||
|
|
||||||
|
|||||||
26
undo.h
26
undo.h
@@ -1,5 +1,11 @@
|
|||||||
#ifndef KE_UNDO
|
#include <stddef.h>
|
||||||
#define KE_UNDO
|
|
||||||
|
#include "abuf.h"
|
||||||
|
#include "editor.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef KE_UNDO_H
|
||||||
|
#define KE_UNDO_H
|
||||||
|
|
||||||
|
|
||||||
typedef enum undo_kind {
|
typedef enum undo_kind {
|
||||||
@@ -9,7 +15,7 @@ typedef enum undo_kind {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct undo_node {
|
typedef struct undo_node {
|
||||||
undo_kind op;
|
undo_kind kind;
|
||||||
size_t row, col;
|
size_t row, col;
|
||||||
abuf text;
|
abuf text;
|
||||||
|
|
||||||
@@ -31,14 +37,14 @@ void undo_node_free(undo_node *node);
|
|||||||
void undo_tree_init(undo_tree *tree);
|
void undo_tree_init(undo_tree *tree);
|
||||||
void undo_tree_free(undo_tree *tree);
|
void undo_tree_free(undo_tree *tree);
|
||||||
void undo_begin(undo_tree *tree, undo_kind kind);
|
void undo_begin(undo_tree *tree, undo_kind kind);
|
||||||
void undo_prepend(abuf *buf);
|
void undo_prepend(undo_tree *tree, abuf *buf);
|
||||||
void undo_append(buf *buf);
|
void undo_append(undo_tree *tree, abuf *buf);
|
||||||
void undo_prependch(char c);
|
void undo_prependch(undo_tree *tree, char c);
|
||||||
void undo_appendch(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(undo_node *node);
|
void undo_apply(struct editor *editor);
|
||||||
void editor_undo(void);
|
void editor_undo(undo_tree *tree);
|
||||||
void editor_redo(void);
|
void editor_redo(undo_tree *tree);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user