* check for file changes on save
* check for file changes when window regains focus
-The Rest:
+The Rest:
* add a distinct state for pointer move versus drag
* Add a SaveAs tag that takes an argument for the filename to save as
* Add a GoTo tag for ctags lookup and line number jump
-* Add a tools dir to namespace utility scripts only useful inside the editor
* implement command diffing logic to optimize the undo/redo log
* add command line flags to toggle options (Tabs, Indent, etc..)
* Add a ctrl+space shortcut to autocomplete ctag
* off by one error on scrolling up with wrapped lines
+* 100% coverage with unit and unit-integration tests
+* shortcut to repeat previous operation
# Auxillary Programs
* Acme-like window manager
* Win-like terminal emulator
* File browser
-* Webkit-based web browser
-
-# Graphical User Interface
-
-* Display line location and num lines in status
-
-# Maybe Someday Features
-
-* Implement fuse file-system backend?
-* Spell checker integration
CC = c99
CFLAGS = -g -O0 $(INCS)
+#CC = gcc
+#CFLAGS = --std=c99 -Wall -Wextra -Werror $(INCS)
+#CFLAGS += -Wno-sign-compare -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers
+
# Linker Setup
LD = $(CC)
LDFLAGS = $(LIBS) -lX11 -lXft -lfontconfig
/* cleanup old data if there is any */
if (buf->bufstart) free(buf->bufstart);
buf_logclear(buf);
-
+
/* reset the state to defaults */
buf->modified = false;
buf->expand_tabs = true;
buf->path = stringdup(path);
char* addr = strrchr(buf->path, ':');
if (addr) *addr = '\0', addr++;
-
+
/* load the file and determine the character set */
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 */
for (size_t i = 0; i < file.len;) {
Rune r;
}
buf_insert(buf, false, buf_end(buf), r);
}
-
+
/* reset buffer state */
buf->modified = false;
buf_logclear(buf);
unsigned buf_change(Buf* buf, unsigned beg, unsigned end) {
/* delete the range first */
unsigned off = buf_delete(buf, beg, end);
- /* now create a new insert item of length 0 witht he same transaction id as
- the delete. This will cause subsequent inserts to be coalesced into the
+ /* now create a new insert item of length 0 witht he same transaction id as
+ the delete. This will cause subsequent inserts to be coalesced into the
same transaction */
- Log* dellog = buf->undo;
+ Log* dellog = buf->undo;
Log* inslog = (Log*)calloc(sizeof(Log), 1);
inslog->transid = dellog->transid;
inslog->insert = true;
void buf_getblock(Buf* buf, Rune first, Rune last, Sel* sel) {
int balance = 0, dir;
unsigned beg = sel->end, end = sel->end, off;
-
+
/* figure out which end of the block we're starting at */
if (buf_get(buf, end) == first)
dir = +1, balance++, beg = end++;
dir = -1, balance--, beg = end--;
else
return;
-
+
/* scan for a blanced set of braces */
while (true) {
if (buf_get(buf, end) == first)
balance++;
else if (buf_get(buf, end) == last)
balance--;
-
+
if (balance == 0 || end >= buf_end(buf) || end == 0)
break;
else
end += dir;
}
-
+
/* bail if we failed to find a block */
if (balance != 0) return;
-
+
/* update the passed in selection */
if (end > beg) beg++, end--;
sel->beg = beg, sel->end = end;
void buf_lastins(Buf* buf, size_t* beg, size_t* end) {
Log* log = buf->undo;
- if (log && log->insert) {
- *beg = log->data.ins.beg;
- *end = log->data.ins.end;
+ unsigned opbeg = *end, opend = *end;
+ if (log && log->insert)
+ opbeg = log->data.ins.end, opend = log->data.ins.end;
+
+ unsigned delsize = 0;
+ //printf("start: %u-%u\n", opbeg, opend);
+ for (; log; log = log->next) {
+ if (log->insert) {
+ unsigned ibeg = log->data.ins.beg,
+ iend = log->data.ins.end - delsize;
+ //printf("ins: %u-%u\n", ibeg, iend);
+ if (iend < ibeg || ibeg > opbeg || iend < opbeg) break;
+ if (ibeg < opbeg && iend > opend) break;
+ opbeg = ibeg, delsize = 0;
+ } else {
+ //printf("del: %u-%u\n", log->data.del.off, log->data.del.off+log->data.del.len);
+ /* bail if the delete doesnt overlap */
+ if(log->data.del.off != opbeg) break;
+ delsize = log->data.del.len;
+ }
}
+ //printf("finish: %u-%u\n\n", opbeg, opend);
+ *beg = opbeg, *end = opend;
}
buf_insert(&(view->buffer), false, view->selection.end++, '\n');
view->selection.beg++;
}
+ unsigned beg = view->selection.beg;
view_putstr(view, str);
- view_selprev(view);
+ view->selection.beg = beg;
}
char* view_getstr(View* view, Sel* range) {
view->selection.end = buf_eol(buf, view->selection.end);
unsigned off = buf_bol(buf, view->selection.end);
if (num_selected(view->selection) == 0) return;
-
+
do {
if (dir == RIGHT) {
buf_insert(buf, true, off, '\t');
}
}
off = buf_byline(buf, off, UP);
-
+
} while (off && off >= view->selection.beg);
}
CHECK(getsel(EDIT)->end == 1);
CHECK('\n' == buf_get(getbuf(EDIT), 0));
}
-
+
/* Key Handling - Cursor Movement - Basic
*************************************************************************/
TEST(left should do nothing for empty buffer) {
CHECK(getsel(EDIT)->beg == 2);
CHECK(getsel(EDIT)->end == 2);
}
-
+
/* Key Handling - Unix Editing Shortcuts
*************************************************************************/
TEST(ctrl+u should do nothing for empty buffer) {
CHECK(getsel(EDIT)->end == 3);
}
- TEST(esc should select nothing if no previous insert) {
+ TEST(esc should select previously edited text) {
setup_view(EDIT, "foob", CRLF, 4);
send_keys(ModNone, KEY_BACKSPACE);
send_keys(ModNone, KEY_ESCAPE);
- CHECK(getsel(EDIT)->beg == 3);
+ CHECK(getsel(EDIT)->beg == 0);
CHECK(getsel(EDIT)->end == 3);
}
CHECK(ExitCode == 0);
CHECK(verify_text(TAGS, "File is modified. Repeat action twice in < 250ms to quit."));
}
-
+
TEST(Save should save changes to disk with crlf line endings) {
setup_view(TAGS, "", CRLF, 0);
view_init(getview(EDIT), "docs/crlf.txt");
view_init(getview(EDIT), "docs/lf.txt");
CHECK(verify_text(EDIT, "this file\nuses\nunix\nline\nendings\n"));
}
-
+
TEST(Cut and Paste tags should move selection to new location) {
setup_view(EDIT, "foo\nbar\nbaz\n", CRLF, 0);
getview(EDIT)->selection = (Sel){ 0, 8, 0 };
send_keys(ModCtrl, 'f');
}
#endif
-
+
TEST(Tabs should set indent style to tabs) {
setup_view(TAGS, "Tabs", CRLF, 0);
getview(TAGS)->selection = (Sel){ 0, 4, 0 };