return off;
}
+unsigned buf_bow(Buf* buf, unsigned off)
+{
+ for (; isword(buf_get(buf, off-1)); off--);
+ return off;
+}
+
+unsigned buf_eow(Buf* buf, unsigned off)
+{
+ for (; isword(buf_get(buf, off)); off++);
+ return off-1;
+}
+
+unsigned buf_lscan(Buf* buf, unsigned off, Rune r)
+{
+ for (; r != buf_get(buf, off); off--);
+ return off;
+}
+
+unsigned buf_rscan(Buf* buf, unsigned off, Rune r)
+{
+ for (; r != buf_get(buf, off); off++);
+ return off;
+}
+
unsigned buf_end(Buf* buf) {
size_t bufsz = buf->bufend - buf->bufstart;
size_t gapsz = buf->gapend - buf->gapstart;
#include <stdarg.h>
#include <string.h>
-/* Utility Functions
- *****************************************************************************/
-typedef struct {
- uint8_t* buf; /* memory mapped byte buffer */
- size_t len; /* length of the buffer */
-} FMap;
-
-FMap fmap(char* path);
-void funmap(FMap file);
-void die(const char* fmt, ...);
-uint32_t getmillis(void);
-
/* Unicode Handling
*****************************************************************************/
enum {
void fputrune(Rune rune, FILE* f);
int runewidth(unsigned col, Rune r);
+/* Utility Functions
+ *****************************************************************************/
+typedef struct {
+ uint8_t* buf; /* memory mapped byte buffer */
+ size_t len; /* length of the buffer */
+} FMap;
+
+FMap fmap(char* path);
+void funmap(FMap file);
+void die(const char* fmt, ...);
+uint32_t getmillis(void);
+bool isword(Rune r);
+
+
/* Buffer management functions
*****************************************************************************/
typedef struct buf {
bool buf_iseol(Buf* buf, unsigned pos);
unsigned buf_bol(Buf* buf, unsigned pos);
unsigned buf_eol(Buf* buf, unsigned pos);
+
+unsigned buf_bow(Buf* buf, unsigned pos);
+unsigned buf_eow(Buf* buf, unsigned pos);
+unsigned buf_lscan(Buf* buf, unsigned pos, Rune r);
+unsigned buf_rscan(Buf* buf, unsigned pos, Rune r);
+
unsigned buf_end(Buf* buf);
unsigned buf_byrune(Buf* buf, unsigned pos, int count);
unsigned buf_byline(Buf* buf, unsigned pos, int count);
/* variable for holding the currently selected color scheme */
extern enum ColorScheme ColorBase;
extern Buf Buffer;
-extern unsigned CursorPos;
extern unsigned TargetCol;
extern unsigned DotBeg;
extern unsigned DotEnd;
}
static void cursor_up(void) {
- CursorPos = buf_byline(&Buffer, CursorPos, -1);
- CursorPos = buf_setcol(&Buffer, CursorPos, TargetCol);
+ DotBeg = DotEnd = buf_byline(&Buffer, DotEnd, -1);
+ DotBeg = DotEnd = buf_setcol(&Buffer, DotEnd, TargetCol);
}
static void cursor_dn(void) {
- CursorPos = buf_byline(&Buffer, CursorPos, 1);
- CursorPos = buf_setcol(&Buffer, CursorPos, TargetCol);
+ DotBeg = DotEnd = buf_byline(&Buffer, DotEnd, 1);
+ DotBeg = DotEnd = buf_setcol(&Buffer, DotEnd, TargetCol);
}
static void cursor_left(void) {
- CursorPos = buf_byrune(&Buffer, CursorPos, -1);
- TargetCol = buf_getcol(&Buffer, CursorPos);
+ DotBeg = DotEnd = buf_byrune(&Buffer, DotEnd, -1);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
}
static void cursor_right(void) {
- CursorPos = buf_byrune(&Buffer, CursorPos, 1);
- TargetCol = buf_getcol(&Buffer, CursorPos);
+ DotBeg = DotEnd = buf_byrune(&Buffer, DotEnd, 1);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
}
static void cursor_bol(void) {
- CursorPos = buf_bol(&Buffer, CursorPos);
+ DotBeg = DotEnd = buf_bol(&Buffer, DotEnd);
TargetCol = 0;
}
static void cursor_eol(void) {
- CursorPos = buf_eol(&Buffer, CursorPos);
+ DotBeg = DotEnd = buf_eol(&Buffer, DotEnd);
TargetCol = (unsigned)-1;
}
-static void backspace(void) {
- if (CursorPos == 0) return;
- buf_del(&Buffer, --CursorPos);
- TargetCol = buf_getcol(&Buffer, CursorPos);
-}
-
-static void delete(void) {
- buf_del(&Buffer, CursorPos);
-}
-
static void insert_before(void) {
Buffer.insert_mode = true;
}
static void insert_after(void) {
- CursorPos++;
+ DotEnd++;
Buffer.insert_mode = true;
}
if (!Buffer.modified || num_clicks >= 2)
exit(0);
}
+static void dot_delete(void) {
+ if (DotEnd == buf_end(&Buffer)) return;
+ size_t n = DotEnd - DotBeg;
+ bool insert = Buffer.insert_mode;
+ if (!insert || !n) n++;
+ Buffer.insert_mode = true;
+ for (size_t i = 0; i < n; i++)
+ buf_del(&Buffer, DotBeg);
+ DotEnd = DotBeg;
+ TargetCol = buf_getcol(&Buffer, DotEnd);
+ Buffer.insert_mode = insert;
+}
+
+static void dot_backspace(void) {
+ if (DotBeg > 0 && DotBeg == DotEnd) DotBeg--;
+ while (DotBeg < DotEnd)
+ buf_del(&Buffer, --DotEnd);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
+}
static void insert(Rune r) {
if (!Buffer.insert_mode) return;
- buf_ins(&Buffer, CursorPos++, r);
- TargetCol = buf_getcol(&Buffer, CursorPos);
+ buf_ins(&Buffer, DotEnd++, r);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
}
/*****************************************************************************/
} KeyBinding_T;
static KeyBinding_T Normal[] = {
- { KEY_F6, toggle_colors },
- { KEY_UP, cursor_up },
- { KEY_DOWN, cursor_dn },
- { KEY_LEFT, cursor_left },
- { KEY_RIGHT, cursor_right },
- { KEY_HOME, cursor_bol },
- { KEY_END, cursor_eol },
- { 'q', quit },
- { 's', write },
- { 'a', insert_after },
- { 'i', insert_before },
- { 'k', cursor_up },
- { 'j', cursor_dn },
- { 'h', cursor_left },
- { 'l', cursor_right },
- { '0', cursor_bol },
- { '$', cursor_eol },
- { 0, NULL }
+ { KEY_F6, toggle_colors },
+ { KEY_UP, cursor_up },
+ { KEY_DOWN, cursor_dn },
+ { KEY_LEFT, cursor_left },
+ { KEY_RIGHT, cursor_right },
+ { KEY_HOME, cursor_bol },
+ { KEY_END, cursor_eol },
+ { KEY_DELETE, dot_delete },
+ { 'q', quit },
+ { 's', write },
+ { 'a', insert_after },
+ { 'i', insert_before },
+ { 'k', cursor_up },
+ { 'j', cursor_dn },
+ { 'h', cursor_left },
+ { 'l', cursor_right },
+ { '0', cursor_bol },
+ { '$', cursor_eol },
+ { 'd', dot_delete },
+ { 0, NULL }
};
static KeyBinding_T Insert[] = {
{ KEY_HOME, cursor_bol },
{ KEY_END, cursor_eol },
{ KEY_ESCAPE, exit_insert },
- { KEY_DELETE, delete },
- { KEY_BACKSPACE, backspace },
+ { KEY_DELETE, dot_delete },
+ { KEY_BACKSPACE, dot_backspace },
{ 0, NULL }
};
void move_cursor(MouseEvent* mevnt) {
if (mevnt->y == 0) return;
- CursorPos = screen_getoff(&Buffer, CursorPos, mevnt->y-1, mevnt->x);
- TargetCol = buf_getcol(&Buffer, CursorPos);
+ DotEnd = screen_getoff(&Buffer, DotEnd, mevnt->y-1, mevnt->x);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
+ DotBeg = DotEnd;
}
-void select_word(MouseEvent* mevnt) {
- (void)mevnt;
-}
-
-void select_line(MouseEvent* mevnt) {
+void select(MouseEvent* mevnt) {
(void)mevnt;
+ unsigned bol = buf_bol(&Buffer, DotEnd);
+ Rune r = buf_get(&Buffer, DotEnd);
+ if (DotEnd == bol || r == '\n' || r == RUNE_CRLF) {
+ DotBeg = bol;
+ DotEnd = buf_eol(&Buffer, DotEnd);
+ } else if (isword(r)) {
+ DotBeg = buf_bow(&Buffer, DotEnd);
+ DotEnd = buf_eow(&Buffer, DotEnd);
+ if (Buffer.insert_mode) DotEnd++;
+ } else if (r == '(' || r == ')') {
+ DotBeg = buf_lscan(&Buffer, DotEnd, '(');
+ DotEnd = buf_rscan(&Buffer, DotEnd, ')');
+ if (Buffer.insert_mode) DotEnd++;
+ } else if (r == '[' || r == ']') {
+ DotBeg = buf_lscan(&Buffer, DotEnd, '[');
+ DotEnd = buf_rscan(&Buffer, DotEnd, ']');
+ if (Buffer.insert_mode) DotEnd++;
+ } else if (r == '{' || r == '}') {
+ DotBeg = buf_lscan(&Buffer, DotEnd, '{');
+ DotEnd = buf_rscan(&Buffer, DotEnd, '}');
+ if (Buffer.insert_mode) DotEnd++;
+ } else {
+ /* scan for big word */
+ }
}
void scrollup(MouseEvent* mevnt) {
(void)mevnt;
- CursorPos = buf_byline(&Buffer, CursorPos, -ScrollLines);
+ DotBeg = DotEnd = buf_byline(&Buffer, DotEnd, -ScrollLines);
}
void scrolldn(MouseEvent* mevnt) {
(void)mevnt;
- CursorPos = buf_byline(&Buffer, CursorPos, ScrollLines);
+ DotBeg = DotEnd = buf_byline(&Buffer, DotEnd, ScrollLines);
}
/*****************************************************************************/
void (*Actions[5][3])(MouseEvent* mevnt) = {
[MOUSE_LEFT] = {
[SINGLE_CLICK] = move_cursor,
- [DOUBLE_CLICK] = select_word,
- [TRIPLE_CLICK] = select_line,
+ [DOUBLE_CLICK] = select,
+ [TRIPLE_CLICK] = unused,
},
[MOUSE_MIDDLE] = {
[SINGLE_CLICK] = unused,
}
static void handle_drag(MouseEvent* mevnt) {
- (void)mevnt;
+ if (mevnt->y == 0 || mevnt->button != MOUSE_LEFT) return;
+ DotEnd = screen_getoff(&Buffer, DotEnd, mevnt->y-1, mevnt->x);
+ TargetCol = buf_getcol(&Buffer, DotEnd);
}
void handle_mouse(MouseEvent* mevnt) {
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <ctype.h>
#ifdef __MACH__
#define CLOCK_MONOTONIC 0
if (file.buf)
munmap(file.buf, file.len);
}
+
+bool isword(Rune r) {
+ return (r < 127 && (isalnum(r) || r == '_'));
+}
+
#include "edit.h"
-#define BKGCLR (clr(CLR_BASE03))
-#define GTRCLR (clr(CLR_BASE02))
-#define CSRCLR (clr(CLR_BASE3))
-#define TXTCLR (clr(CLR_BASE0))
-
Buf Buffer;
-unsigned CursorPos = 0;
unsigned TargetCol = 0;
unsigned DotBeg = 0;
unsigned DotEnd = 0;
static int init(void) {
atexit(deinit);
signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal
+ setlocale(LC_CTYPE, "");
+ XSetLocaleModifiers("");
/* open the X display and get basic attributes */
if (!(X.display = XOpenDisplay(0)))
die("cannot open display");
static MouseEvent event;
if (e->type == MotionNotify) {
event.type = MouseMove;
- event.button = MOUSE_NONE;
+ event.button = MOUSE_LEFT;
event.x = e->xmotion.x / Fonts.base.width;
event.y = e->xmotion.y / Fonts.base.height;
} else {
static void handle_event(XEvent* e) {
switch (e->type) {
- case FocusIn: if (X.xic) XSetICFocus(X.xic); break;
- case FocusOut: if (X.xic) XUnsetICFocus(X.xic); break;
- case KeyPress: handle_key(getkey(e)); break;
- case ButtonPress: handle_mouse(getmouse(e)); break;
+ case FocusIn: if (X.xic) XSetICFocus(X.xic); break;
+ case FocusOut: if (X.xic) XUnsetICFocus(X.xic); break;
+ case KeyPress: handle_key(getkey(e)); break;
+ case ButtonPress: handle_mouse(getmouse(e)); break;
+ case MotionNotify: handle_mouse(getmouse(e)); break;
case ConfigureNotify: // Resize the window
if (e->xconfigure.width != X.width || e->xconfigure.height != X.height) {
X.width = e->xconfigure.width;
void draw_glyphs(unsigned x, unsigned y, UGlyph* glyphs, size_t rlen, size_t ncols) {
XftGlyphFontSpec specs[rlen];
- int i = 0;
+ size_t i = 0;
while (rlen) {
int numspecs = 0;
- int attr = glyphs[i].attr;
+ uint32_t attr = glyphs[i].attr;
while (i < ncols && glyphs[i].attr == attr) {
font_find(&(specs[numspecs]), glyphs[i].rune);
specs[numspecs].x = x;
UGlyph* csrrune = screen_getglyph(csry, csrx, &rwidth);
csrrune->attr = (CLR_BASE3 << 8 | CLR_BASE03);
if (Buffer.insert_mode) {
- XftDrawRect(X.xft, CSRCLR, csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
+ XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
} else {
- XftDrawRect(X.xft, CSRCLR, csrx * Fonts.base.width, (csry+1) * Fonts.base.height, rwidth * Fonts.base.width, Fonts.base.height);
+ XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, rwidth * Fonts.base.width, Fonts.base.height);
draw_glyphs(csrx * Fonts.base.width, (csry+2) * Fonts.base.height, csrrune, 1, rwidth);
}
}
static void redraw(void) {
- uint32_t start = getmillis();
+ //uint32_t start = getmillis();
/* draw the background colors */
- XftDrawRect(X.xft, BKGCLR, 0, 0, X.width, X.height);
- XftDrawRect(X.xft, GTRCLR, 79 * Fonts.base.width, 0, Fonts.base.width, X.height);
+ XftDrawRect(X.xft, clr(CLR_BASE03), 0, 0, X.width, X.height);
+ XftDrawRect(X.xft, clr(CLR_BASE02), 79 * Fonts.base.width, 0, Fonts.base.width, X.height);
XftDrawRect(X.xft, clr(CLR_BASE02), 0, 0, X.width, Fonts.base.height);
XftDrawRect(X.xft, clr(CLR_BASE01), 0, Fonts.base.height, X.width, 1);
/* update the screen buffer and retrieve cursor coordinates */
unsigned csrx, csry;
- screen_update(&Buffer, CursorPos, &csrx, &csry);
+ screen_update(&Buffer, DotEnd, &csrx, &csry);
/* flush the screen buffer */
unsigned nrows, ncols;
/* flush pixels to the screen */
XCopyArea(X.display, X.pixmap, X.window, X.gc, 0, 0, X.width, X.height, 0, 0);
XFlush(X.display);
- printf("refresh: %u\n", getmillis() - start);
+ //printf("refresh: %u\n", getmillis() - start);
}
int main(int argc, char** argv) {
buf_init(&Buffer);
if (argc > 1)
buf_load(&Buffer, argv[1]);
- /* main x11 event loop */
- setlocale(LC_CTYPE, "");
- XSetLocaleModifiers("");
+ /* initialize the display engine */
init();
+ /* main x11 event loop */
XEvent e;
while (true) {
XPeekEvent(X.display,&e);