Straight-up Bugs:
-* fix crash on saving read-only file
-* fix crash on save to file that can't be created
* tab inserts dont coalesce like one would expect
The Future:
/* Utility Functions
*****************************************************************************/
+/* Memory-mapped file representation */
typedef struct {
uint8_t* buf; /* memory mapped byte buffer */
size_t len; /* length of the buffer */
/* Buffer management functions
*****************************************************************************/
+/* undo/redo list item */
typedef struct Log {
struct Log* next; /* pointer to next operation in the stack */
bool insert; /* whether this operation was an insert or delete */
} data;
} Log;
+/* gap buffer main data structure */
typedef struct buf {
- char* path; /* the path to the open file */
- int charset; /* the character set of the buffer */
- int crlf; /* tracks whether the file uses dos style line endings */
- size_t bufsize; /* size of the buffer in runes */
- Rune* bufstart; /* start of the data buffer */
- Rune* bufend; /* end of the data buffer */
- Rune* gapstart; /* start of the gap */
- Rune* gapend; /* end of the gap */
- Log* undo; /* undo list */
- Log* redo; /* redo list */
- bool modified; /* tracks whether the buffer has been modified */
- bool expand_tabs; /* tracks current mode */
- bool copy_indent; /* copy the indent level from the previous line on new lines */
- uint transid; /* tracks the last used transaction id for log entries */
+ char* path; /* the path to the open file */
+ int charset; /* the character set of the buffer */
+ int crlf; /* tracks whether the file uses dos style line endings */
+ size_t bufsize; /* size of the buffer in runes */
+ Rune* bufstart; /* start of the data buffer */
+ Rune* bufend; /* end of the data buffer */
+ Rune* gapstart; /* start of the gap */
+ Rune* gapend; /* end of the gap */
+ Log* undo; /* undo list */
+ Log* redo; /* redo list */
+ bool modified; /* tracks whether the buffer has been modified */
+ bool expand_tabs; /* tracks current mode */
+ bool copy_indent; /* copy the indent level from the previous line on new lines */
+ uint transid; /* tracks the last used transaction id for log entries */
+ void (*errfn)(char*); /* callback for error messages */
} Buf;
+/* cursor/selection representation */
typedef struct {
size_t beg;
size_t end;
size_t col;
} Sel;
-void buf_init(Buf* buf);
+void buf_init(Buf* buf, void (*errfn)(char*));
unsigned buf_load(Buf* buf, char* path);
void buf_save(Buf* buf);
Rune buf_get(Buf* buf, unsigned pos);
enum {
BINARY = 0, /* binary encoded file */
UTF_8, /* UTF-8 encoded file */
+
+ /* these arent used but are reserved for later */
UTF_16BE, /* UTF-16 encoding, big-endian */
UTF_16LE, /* UTF-16 encoding, little-endian */
UTF_32BE, /* UTF-32 encoding, big-endian */
DOWN = +1
};
-void view_init(View* view, char* file);
+void view_init(View* view, char* file, void (*errfn)(char*));
size_t view_limitrows(View* view, size_t maxrows, size_t ncols);
void view_resize(View* view, size_t nrows, size_t ncols);
void view_update(View* view, size_t* csrx, size_t* csry);
MouseFunc right;
} MouseConfig;
-void win_window(char* name);
-void win_dialog(char* name);
+void win_window(char* name, void (*errfn)(char*));
+void win_dialog(char* name, void (*errfn)(char*));
void win_loop(void);
void win_settext(WinRegion id, char* text);
void win_setruler(size_t ruler);
/*****************************************************************************/
-void buf_init(Buf* buf) {
+void buf_init(Buf* buf, void (*errfn)(char*)) {
/* cleanup old data if there is any */
if (buf->bufstart) {
free(buf->bufstart);
buf->gapend = buf->bufend;
buf->undo = NULL;
buf->redo = NULL;
+ buf->errfn = errfn;
assert(buf->bufstart);
}
FMap file = mmap_readonly(buf->path);
filetype(buf, file);
- if (buf->charset > UTF_8)
- die("Unsupported character set");
-
/* read the file contents into the buffer */
buf_resize(buf, next_size(file.len));
for (size_t i = 0; i < file.len;) {
buf_insert(buf, false, buf_end(buf), '\n');
size_t wrlen = 0;
- if (!buf->path) return;
- FMap file = mmap_readwrite(buf->path, buf_end(buf) * UTF_MAX);
- for (unsigned i = 0, end = buf_end(buf); i < end; i++) {
- Rune r = buf_get(buf, i);
- if (r == RUNE_CRLF) {
- file.buf[wrlen++] = '\r';
- file.buf[wrlen++] = '\n';
- } else if (buf->charset == BINARY) {
- file.buf[wrlen++] = (char)r;
+ if (buf->path) {
+ FMap file = mmap_readwrite(buf->path, buf_end(buf) * UTF_MAX);
+ if (file.buf) {
+ for (unsigned i = 0, end = buf_end(buf); i < end; i++) {
+ Rune r = buf_get(buf, i);
+ if (r == RUNE_CRLF) {
+ file.buf[wrlen++] = '\r';
+ file.buf[wrlen++] = '\n';
+ } else if (buf->charset == BINARY) {
+ file.buf[wrlen++] = (char)r;
+ } else {
+ wrlen += utf8encode((char*)&(file.buf[wrlen]), r);
+ }
+ }
+ mmap_close(file);
+ truncate(buf->path, wrlen);
+ buf->modified = false;
} else {
- wrlen += utf8encode((char*)&(file.buf[wrlen]), r);
+ buf->errfn("Failed to open file for writing");
}
+ } else {
+ buf->errfn("Need a filename: SaveAs ");
}
- mmap_close(file);
- truncate(buf->path, wrlen);
- buf->modified = false;
}
/*****************************************************************************/
return sz;
}
-uint64_t getmillis(void) {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time);
- uint64_t ms = ((uint64_t)time.tv_sec * (uint64_t)1000);
- ms += ((uint64_t)time.tv_nsec / (uint64_t)1000000);
- return ms;
-}
-
-void die(const char* msgfmt, ...) {
- va_list args;
- va_start(args, msgfmt);
- fprintf(stderr, "Error: ");
- vfprintf(stderr, msgfmt, args);
- fprintf(stderr, "\n");
- va_end(args);
- exit(EXIT_FAILURE);
-}
-
FMap mmap_readonly(char* path) {
FMap file = { .buf = NULL, .len = 0 };
int fd;
FMap mmap_readwrite(char* path, size_t sz) {
FMap file = { .buf = NULL, .len = 0 };
int fd = open(path, O_CREAT|O_RDWR, 0644);
- if (fd < 0) die("could not open/create file");
- ftruncate(fd, sz);
- file.buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- file.len = sz;
- if (file.buf == MAP_FAILED)
- die("memory mapping of file failed");
- close(fd);
+ if (fd >= 0) {
+ ftruncate(fd, sz);
+ void* buf = mmap(NULL, pagealign(sz), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (buf != MAP_FAILED) {
+ file.buf = buf;
+ file.len = sz;
+ }
+ close(fd);
+ }
return file;
}
munmap(file.buf, file.len);
}
+uint64_t getmillis(void) {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ uint64_t ms = ((uint64_t)time.tv_sec * (uint64_t)1000);
+ ms += ((uint64_t)time.tv_nsec / (uint64_t)1000000);
+ return ms;
+}
+
+void die(const char* msgfmt, ...) {
+ va_list args;
+ va_start(args, msgfmt);
+ fprintf(stderr, "Error: ");
+ vfprintf(stderr, msgfmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(EXIT_FAILURE);
+}
+
char* stringdup(const char* s) {
char* ns = (char*)malloc(strlen(s) + 1);
assert(ns);
return pos;
}
-void view_init(View* view, char* file) {
+void view_init(View* view, char* file, void (*errfn)(char*)) {
if (view->nrows) {
for (size_t i = 0; i < view->nrows; i++)
free(view->rows[i]);
free(view->rows);
}
- buf_init(&(view->buffer));
+ buf_init(&(view->buffer), errfn);
view->selection = (Sel){ 0 };
if (file) {
view->selection.end = buf_load(&(view->buffer), file);
static bool selvisible(View* view) {
if (!view->nrows) return true;
unsigned beg = view->rows[0]->off;
- unsigned end = view->rows[view->nrows-1]->off +
+ unsigned end = view->rows[view->nrows-1]->off +
view->rows[view->nrows-1]->rlen;
return (view->selection.beg >= beg && view->selection.end <= end);
}
static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0};
KeyBinding* Keys = NULL;
-static void win_init(void) {
+static void win_init(void (*errfn)(char*)) {
for (int i = 0; i < SCROLL; i++)
- view_init(&(Regions[i].view), NULL);
+ view_init(&(Regions[i].view), NULL, errfn);
x11_init(&Config);
Font = x11_font_load(FONTNAME);
}
-void win_window(char* name) {
- win_init();
+void win_window(char* name, void (*errfn)(char*)) {
+ win_init(errfn);
x11_window(name, Width, Height);
}
-void win_dialog(char* name) {
- win_init();
+void win_dialog(char* name, void (*errfn)(char*)) {
+ win_init(errfn);
x11_dialog(name, Width, Height);
}
die("Could not init fontconfig.\n");
FcPattern* pattern = FcNameParse((FcChar8 *)name);
if (!pattern)
- die("st: can't open font %s\n", name);
+ die("can't open font %s\n", name);
/* load the base font */
FcResult result;
x11_deinit();
}
+void onerror(char* msg) {
+
+}
+
#ifndef TEST
int main(int argc, char** argv) {
- win_window("term");
+ win_window("term", onerror);
//win_setkeys(&Bindings);
//win_setmouse(&MouseHandlers);
win_loop();
static Buf TestBuf;
+static void onerror(char* msg) {
+}
+
static void set_buffer_text(char* str) {
int i = 0;
- buf_init(&TestBuf);
+ buf_init(&TestBuf, onerror);
TestBuf.crlf = 1;
for (Rune* curr = TestBuf.bufstart; curr < TestBuf.bufend; curr++)
*curr = '-';
/* Insertions
*************************************************************************/
TEST(buf_insert should insert at 0 in empty buf) {
- buf_init(&TestBuf);
+ buf_init(&TestBuf, onerror);
buf_insert(&TestBuf, false, 0, 'a');
CHECK(buf_text_eq("a"));
}
TEST(buf_insert should insert at 0) {
- buf_init(&TestBuf);
+ buf_init(&TestBuf, onerror);
buf_insert(&TestBuf, false, 0, 'b');
buf_insert(&TestBuf, false, 0, 'a');
CHECK(buf_text_eq("ab"));
}
TEST(buf_insert should insert at 1) {
- buf_init(&TestBuf);
+ buf_init(&TestBuf, onerror);
buf_insert(&TestBuf, false, 0, 'a');
buf_insert(&TestBuf, false, 1, 'b');
CHECK(buf_text_eq("ab"));
}
TEST(buf_insert should insert at 1) {
- buf_init(&TestBuf);
+ buf_init(&TestBuf, onerror);
buf_insert(&TestBuf, false, 0, 'a');
buf_insert(&TestBuf, false, 1, 'c');
buf_insert(&TestBuf, false, 1, 'b');
static void initialize(void) {
ShellCmd[0] = "/bin/sh";
- win_window("edit");
+ win_window("edit", onerror);
XDisplay = XOpenDisplay(NULL);
win_setkeys(Bindings);
//win_setmouse(&MouseHandlers);
TEST(Save should save changes to disk with crlf line endings) {
setup_view(TAGS, "", CRLF, 0);
- view_init(win_view(EDIT), "testdocs/crlf.txt");
+ view_init(win_view(EDIT), "testdocs/crlf.txt", onerror);
CHECK(verify_text(EDIT, "this file\r\nuses\r\ndos\r\nline\r\nendings\r\n"));
exec("Save");
- view_init(win_view(EDIT), "testdocs/crlf.txt");
+ view_init(win_view(EDIT), "testdocs/crlf.txt", onerror);
CHECK(verify_text(EDIT, "this file\r\nuses\r\ndos\r\nline\r\nendings\r\n"));
}
TEST(Save should save changes to disk with lf line endings) {
setup_view(TAGS, "", CRLF, 0);
- view_init(win_view(EDIT), "testdocs/lf.txt");
+ view_init(win_view(EDIT), "testdocs/lf.txt", onerror);
CHECK(verify_text(EDIT, "this file\nuses\nunix\nline\nendings\n"));
exec("Save");
- view_init(win_view(EDIT), "testdocs/lf.txt");
+ view_init(win_view(EDIT), "testdocs/lf.txt", onerror);
CHECK(verify_text(EDIT, "this file\nuses\nunix\nline\nendings\n"));
}
/* Action Callbacks
******************************************************************************/
+static void onerror(char* msg) {
+ view_append(win_view(TAGS), msg);
+}
+
static void quit(void) {
static uint64_t before = 0;
uint64_t now = getmillis();
win_setregion(EDIT);
} else {
if (!buf->path && !buf->modified) {
- view_init(win_view(EDIT), pick);
+ view_init(win_view(EDIT), pick, onerror);
} else {
OpenCmd[1] = chomp(pick);
cmdrun(OpenCmd, NULL);
ShellCmd[0] = getenv("SHELL");
if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh";
/* Create the window and enter the event loop */
- win_window("edit");
+ win_window("edit", onerror);
char* tags = getenv("EDITTAGS");
win_settext(TAGS, (tags ? tags : DEFAULT_TAGS));
win_setruler(80);
- view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL));
+ view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL), onerror);
win_setkeys(Bindings);
win_loop();
return 0;
View* view = win_view(EDIT);
view->selection = (Sel){0,0,0};
Sel selection = (Sel){0,0,0};
-
+
score();
unsigned off = (ChoiceIdx >= view->nrows ? ChoiceIdx-view->nrows+1 : 0);
for (int i = 0; (i < vec_size(&Choices)) && (i < view->nrows); i++) {
/* Main Routine
*****************************************************************************/
+static void onerror(char* msg) {
+
+}
+
static void accept(void) {
x11_deinit();
}
char* title = getenv("XPICKTITLE");
load_choices();
if (vec_size(&Choices) > 1) {
- win_dialog("xpick");
+ win_dialog("xpick", onerror);
win_setkeys(Bindings);
win_settext(STATUS, (title ? title : "xpick"));
if (argc >= 2) {