sandy

text editor
git clone git://git.suckless.org/sandy
Log | Files | Refs | README | LICENSE

commit 47ed88be9e275f418d465b7c6f629a6491936897
parent c88ed95a963cb253ef1583db7074f3c637818ef8
Author: Dimitris Zervas <dzervas@dzervas.gr>
Date:   Wed,  9 Jul 2014 18:22:24 +0300

Added sentence key binding support

Diffstat:
config.h | 9+++++++--
sandy.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
2 files changed, 91 insertions(+), 33 deletions(-)

diff --git a/config.h b/config.h @@ -142,6 +142,7 @@ static const Key stdkeys[] = { #if VIM_BINDINGS { .keyv.c = CONTROL('['), { t_nocomm,0, 0, 0 }, f_toggle, { .i = S_Command } }, #endif +{ .keyv.c = CONTROL('['), { 0, 0, 0, 0 }, 0, { 0 } }, { .keyv.c = CONTROL('\\'),{ t_rw, 0, 0, 0 }, f_spawn, PIPE }, { .keyv.c = META('\\'), { t_rw, 0, 0, 0 }, f_spawn, SED }, { .keyv.c = CONTROL(']'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtDefault } }, @@ -153,11 +154,17 @@ static const Key stdkeys[] = { }; #if VIM_BINDINGS +/* In order for a key to execute more than 1 function, add consecutive definitions of the key + with the exact same tests */ + static const Key commkeys[] = { /* Command mode keys here */ /* keyv.c, tests, func, arg */ +{ .keyv.c = { '$' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_eol } }, +{ .keyv.c = { '^' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_bol } }, { .keyv.c = { 'a' }, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextchar } }, { .keyv.c = { 'a' }, { 0, 0, 0, 0 }, f_toggle, { .i = S_Command } }, { .keyv.c = { 'b' }, { 0, 0, 0, 0 }, f_moveboth, { .m = m_prevword } }, +{ .keyv.c = { 'd' }, { t_rw, 0, 0, 0 }, f_delete, { .i = 0 } }, { .keyv.c = { 'g' }, { 0, 0, 0, 0 }, f_moveboth, { .m = m_bof } }, { .keyv.c = { 'G' }, { 0, 0, 0, 0 }, f_moveboth, { .m = m_eof } }, { .keyv.c = { 'i' }, { 0, 0, 0, 0 }, f_toggle, { .i = S_Command } }, @@ -191,7 +198,6 @@ static const Key commkeys[] = { /* Command mode keys here */ { .keyv.c = { ' ' }, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextchar } }, /* TODO: Keybindings left: * numbers and arrows do various things (ex. 5s delete the next 5 chars) (adj) - * $ until eol (adj) * ^ until bol (adj) * q record macro (?) * w/W go to next word (adj) @@ -200,7 +206,6 @@ static const Key commkeys[] = { /* Command mode keys here */ * t/T do until char (adj) * y yank things (verb) * {/} start/end of paragraph (?) - * d delete/cut things (verb) * hjkl movement (adj) * c replace things (verb) * v visual mode. may not be implemented. diff --git a/sandy.c b/sandy.c @@ -138,6 +138,7 @@ enum { /* To use in statusflags */ S_AutoIndent = 1<<10, /* Perform autoindenting on RET */ S_DumpStdout = 1<<11, /* Dump to stdout instead of writing to a file */ S_Command = 1<<12, /* Command mode */ + S_Sentence = 1<<13, /* Sentence mode (A verb was pressed and an adjective should be pressed) */ }; enum { /* To use in Undo.flags */ @@ -158,35 +159,41 @@ static const char *envs[EnvLast] = { }; /* Variables */ -static Line *fstline; /* First line*/ -static Line *lstline; /* Last line */ -static Line *scrline; /* First line seen on screen */ -static Filepos fsel; /* Selection point on file */ -static Filepos fcur; /* Insert position on file, cursor, current position */ -static Filepos fmrk = { NULL, 0 }; /* Mark */ -static int syntx = -1; /* Current syntax index */ -static regex_t *find_res[2]; /* Compiled regex for search term */ -static int sel_re = 0; /* Index to the above, we keep 2 REs so regexec does not segfault */ -static char *fifopath = NULL; /* Path to command fifo */ -static char *filename = NULL; /* Path to file loade on buffer */ -static char *title = NULL; /* Screen title */ -static char *tmptitle = NULL; /* Screen title, temporary */ -static char *tsl_str = NULL; /* String to print to status line */ -static char *fsl_str = NULL; /* String to come back from status line */ -static WINDOW *titlewin = NULL; /* Title ncurses window, NULL if there is a status line */ -static WINDOW *textwin = NULL; /* Main ncurses window */ -static Undo *undos; /* Undo ring */ -static Undo *redos; /* Redo ring */ -static int textattrs[LastFG][LastBG]; /* Text attributes for each color pair */ -static int savestep=0; /* Index to determine the need to save in undo/redo action */ -static int fifofd; /* Command fifo file descriptor */ -static long statusflags=S_Running; /* Status flags, very important, OR'd (see enums above) */ -static int lastaction=LastNone; /* The last action we took (see enums above) */ -static int cols, lines; /* Ncurses: to use instead of COLS and LINES, wise */ -static mmask_t defmmask = 0; /* Ncurses: mouse event mask */ +static Line *fstline; /* First line*/ +static Line *lstline; /* Last line */ +static Line *scrline; /* First line seen on screen */ +static Filepos fsel; /* Selection point on file */ +static Filepos fcur; /* Insert position on file, cursor, current position */ +static Filepos fmrk = { NULL, 0 }; /* Mark */ +static int syntx = -1; /* Current syntax index */ +static regex_t *find_res[2]; /* Compiled regex for search term */ +static int sel_re = 0; /* Index to the above, we keep 2 REs so regexec does not segfault */ +static char *fifopath = NULL; /* Path to command fifo */ +static char *filename = NULL; /* Path to file loade on buffer */ +static char *title = NULL; /* Screen title */ +static char *tmptitle = NULL; /* Screen title, temporary */ +static char *tsl_str = NULL; /* String to print to status line */ +static char *fsl_str = NULL; /* String to come back from status line */ +static WINDOW *titlewin = NULL; /* Title ncurses window, NULL if there is a status line */ +static WINDOW *textwin = NULL; /* Main ncurses window */ +static Undo *undos; /* Undo ring */ +static Undo *redos; /* Redo ring */ +static int textattrs[LastFG][LastBG]; /* Text attributes for each color pair */ +static int savestep=0; /* Index to determine the need to save in undo/redo action */ +static int fifofd; /* Command fifo file descriptor */ +#if VIM_BINDINGS +static long statusflags=S_Running | S_Command; /* Status flags, very important, OR'd (see enums above) */ +#else +static long statusflags=S_Running; /* Status flags, very important, OR'd (see enums above) */ +#endif +static int lastaction=LastNone; /* The last action we took (see enums above) */ +static int cols, lines; /* Ncurses: to use instead of COLS and LINES, wise */ +static mmask_t defmmask = 0; /* Ncurses: mouse event mask */ +static void (*verb)(const Arg *arg); /* Verb of current sentence */ /* Functions */ /* f_* functions can be linked to an action or keybinding */ +static void f_adjective(const Arg*); static void f_center(const Arg*); static void f_delete(const Arg*); static void f_extsel(const Arg*); @@ -250,6 +257,7 @@ static bool t_nocomm(void); static bool t_rw(void); static bool t_redo(void); static bool t_sel(void); +static bool t_sent(void); static bool t_undo(void); static bool t_warn(void); @@ -281,6 +289,14 @@ static regex_t *syntax_res[LENGTH(syntaxes)][SYN_COLORS]; /* F_* FUNCTIONS Can be linked to an action or keybinding. Always return void and take const Arg* */ +#if VIM_BINDINGS +void +f_adjective(const Arg *arg) { + statusflags&=~S_Sentence; + verb(arg); +} +#endif + void /* Make cursor line the one in the middle of the screen if possible, refresh screen */ f_center(const Arg *arg) { int i=LINES2/2; @@ -787,6 +803,14 @@ i_edit(void) { #endif /* HANDLE_MOUSE */ for(i=0; i<LENGTH(curskeys); i++) { if(ch == curskeys[i].keyv.i && i_dotests(curskeys[i].test) ) { + +#if VIM_BINDINGS + if(statusflags & S_Sentence && curskeys[i].func != f_adjective) { + statusflags&=~S_Sentence; + break; + } +#endif + if(curskeys[i].func != f_insert) statusflags&=~(S_GroupUndo); curskeys[i].func(&(curskeys[i].arg)); break; @@ -810,6 +834,15 @@ i_edit(void) { if(!(statusflags&S_InsEsc) && ISCTRL(c[0])) { for(i=0; i<LENGTH(stdkeys); i++) { if(memcmp(c, stdkeys[i].keyv.c, sizeof stdkeys[i].keyv.c) == 0 && i_dotests(stdkeys[i].test) ) { + +#if VIM_BINDINGS + if(statusflags & S_Sentence && stdkeys[i].func != f_adjective) { + statusflags&=~S_Sentence; + break; + } +#endif + + if(stdkeys[i].func != f_insert) statusflags&=~(S_GroupUndo); stdkeys[i].func(&(stdkeys[i].arg)); break; @@ -824,6 +857,18 @@ i_edit(void) { else if(statusflags & S_Command) { for(i=0; i<LENGTH(commkeys); i++) { if(memcmp(c, commkeys[i].keyv.c, sizeof commkeys[i].keyv.c) == 0 && i_dotests(commkeys[i].test) ) { + + if(!(statusflags & S_Sentence) && commkeys[i].arg.i == 0) { + statusflags|=(long)S_Sentence; + verb=commkeys[i].func; + break; + } + + if(statusflags & S_Sentence && commkeys[i].func != f_adjective) { + statusflags&=~S_Sentence; + break; + } + if(commkeys[i].func != f_insert) statusflags&=~(S_GroupUndo); commkeys[i].func(&(commkeys[i].arg)); @@ -1462,10 +1507,10 @@ i_update(void) { statusflags&=~S_Warned; /* Reset warning */ snprintf(buf, 4, "%ld%%", (100*ncur)/nlst); #if VIM_BINDINGS - snprintf(title, BUFSIZ, "%s %s [%s]%s%s%s%s %ld,%d %s", - (t_nocomm()?"Insert":"Command"), + snprintf(title, BUFSIZ, "%s %s [%s]%s%s%s%s %ld,%d %s %s", + (statusflags&S_Command?"Command":"Insert"), #else - snprintf(title, BUFSIZ, "%s [%s]%s%s%s%s %ld,%d %s", + snprintf(title, BUFSIZ, "%s [%s]%s%s%s%s %ld,%d %s %s", #endif (statusflags&S_DumpStdout?"<Stdout>":(filename == NULL?"<No file>":filename)), (syntx>=0 ? syntaxes[syntx].name : "none"), @@ -1477,7 +1522,7 @@ i_update(void) { (scrline==fstline? (nlst<lines3?"All":"Top"): (nlst-nscr<lines3?"Bot":buf) - )); + ), (statusflags&S_Sentence?"s":"")); } if(titlewin) { int i; @@ -1738,6 +1783,15 @@ t_sel(void) { return !(fcur.l==fsel.l && fcur.o == fsel.o); } +bool /* TRUE if a sentence has started */ +t_sent(void) { +#if VIM_BINDINGS + return (statusflags & S_Sentence); +#else + return FALSE; +#endif +} + bool /* TRUE if there is anything to undo */ t_undo(void) { return (undos != NULL); @@ -1756,7 +1810,6 @@ main(int argc, char **argv){ /* Use system locale, hopefully UTF-8 */ setlocale(LC_ALL,""); - statusflags|=S_Command; ESCDELAY=0; // FIXME: Change this to something else to support num keys? for(i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != '\0'; i++) {