diff --git a/TODO b/TODO index 535a8e4..9143510 100644 --- a/TODO +++ b/TODO @@ -2,13 +2,17 @@ In no particular order, at sandy.c: - Update manpage - Create m_*vline - Bracket matching? -- Number modifier? - Smart end? is it really needed? - BUG: high CPU usage on continuous resize - BUG: Deal with the bigger-than-window line - Improve regex search (backwards!!) - Improve syntax highlight, multiline? - Group delete undos? +- Show line numbers +- Show some documentation when you press tab in command mode +- FINDBW prompt? is it needed? +- Support multiple marks (with S_Parameter) +- Instead of delete, just cut At config.def.h: - Bindings! diff --git a/config.def.h b/config.def.h index a280e06..f726350 100644 --- a/config.def.h +++ b/config.def.h @@ -3,6 +3,7 @@ #define HILIGHT_SYNTAX 1 #define SHOW_NONPRINT 0 #define HANDLE_MOUSE 1 +#define VIM_BINDINGS 1 /* Things unlikely to be changed, yet still in the config.h file */ static const bool isutf8 = TRUE; @@ -22,7 +23,6 @@ static const char nlstr[1] = { 0 }; #endif /* Helper config functions, not used in main code */ -static void f_moveboth(const Arg*); static void f_pipeai(const Arg*); static void f_pipeline(const Arg*); static void f_pipenull(const Arg*); @@ -44,20 +44,20 @@ static void f_pipenull(const Arg*); #define CMD_P PROMPT("Command:", "/\n?\nw\nq\n!\nsyntax\noffset\nicase\nro\nai\ndump", "") /* Args to f_pipe and friends, simple examples are inlined instead */ -#define TOCLIP { .v = "tee /tmp/.sandy.clipboard.$USER | xsel -ib 2>/dev/null" } -#define FROMCLIP { .v = "xsel -ob 2>/dev/null || cat /tmp/.sandy.clipboard.$USER" } -#define TOSEL { .v = "tee /tmp/.sandy.selection.$USER | xsel -i 2>/dev/null" } -#define FROMSEL { .v = "xsel -o 2>/dev/null || cat /tmp/.sandy.selection.$USER" } -#define AUTOINDENT { .v = "awk 'BEGIN{ l=\"\\n\" }; \ +#define TOCLIP "tee /tmp/.sandy.clipboard.$USER | xsel -ib 2>/dev/null" +#define FROMCLIP "xsel -ob 2>/dev/null || cat /tmp/.sandy.clipboard.$USER" +#define TOSEL "tee /tmp/.sandy.selection.$USER | xsel -i 2>/dev/null" +#define FROMSEL "xsel -o 2>/dev/null || cat /tmp/.sandy.selection.$USER" +#define AUTOINDENT "awk 'BEGIN{ l=\"\\n\" }; \ { if(match($0, \"^[\t ]+[^\t ]\")) l=substr($0, RSTART, RLENGTH-1); \ else l=\"\"; \ if(FNR==NR && $0 ~ /^[\t ]+$/) print \"\"; \ else print }; \ - END{ ORS=\"\"; print l }' 2>/dev/null" } -#define CAPITALIZE { .v = "awk 'BEGIN{ ORS=\"\" }; \ + END{ ORS=\"\"; print l }' 2>/dev/null" +#define CAPITALIZE "awk 'BEGIN{ ORS=\"\" }; \ { for ( i=1; i <= NF; i++) { $i=tolower($i) ; sub(\".\", substr(toupper($i),1,1) , $i) } \ if(FNR==NF) print $0; \ - else print $0\"\\n\" }' 2>/dev/null" } + else print $0\"\\n\" }' 2>/dev/null" /* Hooks are launched from the main code */ #define HOOK_SAVE_NO_FILE f_spawn (&(const Arg)SAVEAS) @@ -72,26 +72,32 @@ static void f_pipenull(const Arg*); static const Key curskeys[] = { /* Plain keys here, no CONTROL or META */ /* keyv.i, tests, func, arg */ -{ .keyv.i = KEY_BACKSPACE, { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, -{ .keyv.i = KEY_DC, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, -{ .keyv.i = KEY_DC, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, -{ .keyv.i = KEY_SDC, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, -{ .keyv.i = KEY_SDC, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, -{ .keyv.i = KEY_IC, { t_sel, 0, 0, 0 }, f_pipero, TOCLIP }, -{ .keyv.i = KEY_SIC, { t_rw, 0, 0, 0 }, f_pipenull, FROMCLIP }, -{ .keyv.i = KEY_HOME, { t_ai, 0, 0, 0 }, f_moveboth, { .m = m_smartbol } }, -{ .keyv.i = KEY_HOME, { 0, 0, 0, 0 }, f_moveboth, { .m = m_bol } }, -{ .keyv.i = KEY_END, { 0, 0, 0, 0 }, f_moveboth, { .m = m_eol } }, -{ .keyv.i = KEY_SHOME, { 0, 0, 0, 0 }, f_moveboth, { .m = m_bof } }, -{ .keyv.i = KEY_SEND, { 0, 0, 0, 0 }, f_moveboth, { .m = m_eof } }, -{ .keyv.i = KEY_PPAGE, { 0, 0, 0, 0 }, f_moveboth, { .m = m_prevscr } }, -{ .keyv.i = KEY_NPAGE, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextscr } }, -{ .keyv.i = KEY_UP, { 0, 0, 0, 0 }, f_moveboth, { .m = m_prevline } }, -{ .keyv.i = KEY_DOWN, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextline } }, -{ .keyv.i = KEY_LEFT, { 0, 0, 0, 0 }, f_moveboth, { .m = m_prevchar } }, -{ .keyv.i = KEY_RIGHT, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextchar } }, -{ .keyv.i = KEY_SLEFT, { 0, 0, 0, 0 }, f_moveboth, { .m = m_prevword } }, -{ .keyv.i = KEY_SRIGHT, { 0, 0, 0, 0 }, f_moveboth, { .m = m_nextword } }, +{ .keyv.i = KEY_BACKSPACE, { t_rw, t_nocomm,0,0 }, f_delete, { .m = m_prevchar } }, +{ .keyv.i = KEY_BACKSPACE, { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } }, +{ .keyv.i = KEY_DC, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, +{ .keyv.i = KEY_DC, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.i = KEY_SDC, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, +{ .keyv.i = KEY_SDC, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.i = KEY_IC, { t_sel, 0, 0, 0 }, f_pipero, { .v = TOCLIP } }, +{ .keyv.i = KEY_SIC, { t_rw, 0, 0, 0 }, f_pipenull, { .v = FROMCLIP } }, +{ .keyv.i = KEY_HOME, { t_ai, 0, 0, 0 }, f_move, { .m = m_smartbol } }, +{ .keyv.i = KEY_HOME, { 0, 0, 0, 0 }, f_move, { .m = m_bol } }, +{ .keyv.i = KEY_END, { 0, 0, 0, 0 }, f_move, { .m = m_eol } }, +{ .keyv.i = KEY_SHOME, { 0, 0, 0, 0 }, f_move, { .m = m_bof } }, +{ .keyv.i = KEY_SEND, { 0, 0, 0, 0 }, f_move, { .m = m_eof } }, +{ .keyv.i = KEY_PPAGE, { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } }, +{ .keyv.i = KEY_NPAGE, { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } }, +{ .keyv.i = KEY_UP, { t_sent,0, 0, 0 }, f_adjective, { .m = m_prevline } }, +{ .keyv.i = KEY_UP, { 0, 0, 0, 0 }, f_move, { .m = m_prevline } }, +{ .keyv.i = KEY_DOWN, { t_sent,0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.i = KEY_DOWN, { t_sent,0, 0, 0 }, f_adjective, { .m = m_nextline } }, +{ .keyv.i = KEY_DOWN, { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.i = KEY_LEFT, { t_sent,0, 0, 0 }, f_adjective, { .m = m_prevchar } }, +{ .keyv.i = KEY_LEFT, { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } }, +{ .keyv.i = KEY_RIGHT, { t_sent,0, 0, 0 }, f_adjective, { .m = m_nextchar } }, +{ .keyv.i = KEY_RIGHT, { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } }, +{ .keyv.i = KEY_SLEFT, { 0, 0, 0, 0 }, f_move, { .m = m_prevword } }, +{ .keyv.i = KEY_SRIGHT, { 0, 0, 0, 0 }, f_move, { .m = m_nextword } }, }; static const Key stdkeys[] = { @@ -103,72 +109,146 @@ static const Key stdkeys[] = { { .keyv.c = CONTROL('A'), { 0, 0, 0, 0 }, f_move, { .m = m_bol } }, { .keyv.c = CONTROL('B'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } }, { .keyv.c = META('b'), { 0, 0, 0, 0 }, f_move, { .m = m_prevword } }, -{ .keyv.c = CONTROL('C'), { t_warn,t_mod,0, 0 }, f_toggle, { .i = S_Running } }, -{ .keyv.c = CONTROL('C'), { t_mod, 0, 0, 0 }, f_toggle, { .i = S_Warned } }, -{ .keyv.c = CONTROL('C'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } }, -{ .keyv.c = META('c'), { t_sel, t_rw, 0, 0 }, f_pipe, CAPITALIZE }, -{ .keyv.c = CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP }, -{ .keyv.c = CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, -{ .keyv.c = META('d'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } }, -{ .keyv.c = CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } }, -{ .keyv.c = CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } }, -{ .keyv.c = META('f'), { 0, 0, 0, 0 }, f_move, { .m = m_nextword } }, -{ .keyv.c = CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } }, -{ .keyv.c = CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, -{ .keyv.c = CONTROL('I'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\t" } }, -{ .keyv.c = CONTROL('J'), { t_rw, t_ai, 0, 0 }, f_pipeai, AUTOINDENT } , -{ .keyv.c = CONTROL('J'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } }, -{ .keyv.c = CONTROL('J'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, -{ .keyv.c = CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } }, -{ .keyv.c = CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } }, -{ .keyv.c = CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } }, +{ .keyv.c = CONTROL('C'), { t_warn,t_mod,0, 0 }, f_toggle, { .i = S_Running } }, +{ .keyv.c = CONTROL('C'), { t_mod, 0, 0, 0 }, f_toggle, { .i = S_Warned } }, +{ .keyv.c = CONTROL('C'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } }, +{ .keyv.c = META('c'), { t_sel, t_rw, 0, 0 }, f_pipe, { .v = CAPITALIZE } }, +{ .keyv.c = CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, { .v = TOCLIP } }, +{ .keyv.c = CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.c = META('d'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } }, +{ .keyv.c = CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } }, +{ .keyv.c = CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } }, +{ .keyv.c = META('f'), { 0, 0, 0, 0 }, f_move, { .m = m_nextword } }, +{ .keyv.c = CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } }, +{ .keyv.c = CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, +{ .keyv.c = CONTROL('I'), { t_rw, t_nocomm,0,0 }, f_insert, { .v = "\t" } }, +{ .keyv.c = CONTROL('J'), { t_rw, t_ai, 0, 0 }, f_pipeai, { .v = AUTOINDENT } }, +{ .keyv.c = CONTROL('J'), { t_rw, t_nocomm,0,0 }, f_insert, { .v = "\n" } }, +{ .keyv.c = CONTROL('J'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.c = CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.c = CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } }, +{ .keyv.c = CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } }, { .keyv.c = META('l'), { t_sel, t_rw, 0, 0 }, f_pipe, { .v = "tr [A-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ] [a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ]" } }, /* Lowercase */ -{ .keyv.c = CONTROL('M'), { t_rw, t_ai, 0, 0 }, f_pipeai, AUTOINDENT } , -{ .keyv.c = CONTROL('M'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } }, -{ .keyv.c = CONTROL('M'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, -{ .keyv.c = CONTROL('N'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, -{ .keyv.c = CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */ -{ .keyv.c = CONTROL('P'), { 0, 0, 0, 0 }, f_move, { .m = m_prevline } }, -{ .keyv.c = CONTROL('Q'), { t_rw, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } }, -{ .keyv.c = CONTROL('R'), { t_sel, 0, 0, 0 }, f_findbw, { 0 } }, -{ .keyv.c = CONTROL('R'), { 0, 0, 0, 0 }, f_spawn, FINDBW }, -{ .keyv.c = META('r'), { 0, 0, 0, 0 }, f_findbw, { 0 } }, -{ .keyv.c = CONTROL('S'), { t_sel, 0, 0, 0 }, f_findfw, { 0 } }, +{ .keyv.c = CONTROL('M'), { t_rw, t_ai, 0, 0 }, f_pipeai, { .v = AUTOINDENT } } , +{ .keyv.c = CONTROL('M'), { t_rw, t_nocomm,0,0 }, f_insert, { .v = "\n" } }, +{ .keyv.c = CONTROL('M'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.c = CONTROL('N'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.c = CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */ +{ .keyv.c = CONTROL('P'), { 0, 0, 0, 0 }, f_move, { .m = m_prevline } }, +{ .keyv.c = CONTROL('Q'), { t_rw, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } }, +{ .keyv.c = CONTROL('R'), { t_redo,t_rw, 0, 0 }, f_undo, { .i = -1 } }, +{ .keyv.c = META('r'), { 0, 0, 0, 0 }, f_findbw, { 0 } }, +{ .keyv.c = CONTROL('S'), { t_sel, 0, 0, 0 }, f_findfw, { 0 } }, { .keyv.c = CONTROL('S'), { 0, 0, 0, 0 }, f_spawn, FIND }, -{ .keyv.c = META('s'), { 0, 0, 0, 0 }, f_findfw, { 0 } }, -{ .keyv.c = CONTROL('T'), { 0, 0, 0, 0 }, f_pipero , TOCLIP }, -{ .keyv.c = CONTROL('U'), { t_bol, t_rw, 0, 0 }, f_delete, { .m = m_prevchar } }, -{ .keyv.c = CONTROL('U'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_bol } }, +{ .keyv.c = META('s'), { 0, 0, 0, 0 }, f_findfw, { 0 } }, +{ .keyv.c = CONTROL('T'), { 0, 0, 0, 0 }, f_pipero , { .v = TOCLIP } }, +{ .keyv.c = CONTROL('U'), { t_bol, t_rw, 0, 0 }, f_delete, { .m = m_prevchar } }, +{ .keyv.c = CONTROL('U'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_bol } }, { .keyv.c = META('u'), { t_sel, t_rw, 0, 0 }, f_pipe, { .v = "tr [a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ] [A-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ] | sed 's/ß/SS/g'" } }, /* Uppercase */ -{ .keyv.c = CONTROL('V'), { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } }, -{ .keyv.c = META('v'), { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } }, -{ .keyv.c = CONTROL('W'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevword } }, +{ .keyv.c = CONTROL('V'), { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } }, +{ .keyv.c = META('v'), { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } }, +{ .keyv.c = CONTROL('W'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevword } }, { .keyv.c = CONTROL('X'), { t_mod, t_rw, 0, 0 }, f_save, { 0 } }, { .keyv.c = CONTROL('X'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } }, { .keyv.c = META('x'), { 0, 0, 0, 0 }, f_spawn, CMD_P }, -{ .keyv.c = CONTROL('Y'), { t_rw, 0, 0, 0 }, f_pipenull, FROMCLIP }, -{ .keyv.c = CONTROL('Z'), { 0 ,0, 0, 0 }, f_suspend, { 0 } }, +{ .keyv.c = CONTROL('Y'), { t_rw, 0, 0, 0 }, f_pipenull, { .v = FROMCLIP } }, +{ .keyv.c = CONTROL('Z'), { 0, 0, 0, 0 }, f_suspend, { 0 } }, +{ .keyv.c = CONTROL('['), { t_vis, 0, 0, 0 }, f_toggle, { .i = S_Visual } }, +#if VIM_BINDINGS +{ .keyv.c = CONTROL('['), { t_nocomm,0, 0, 0 }, f_toggle, { .i = S_Command } }, +#else { .keyv.c = CONTROL('['), { 0, 0, 0, 0 }, f_spawn, CMD_P }, -{ .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 } }, +#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 } }, { .keyv.c = CONTROL('^'), { t_redo,t_rw, 0, 0 }, f_undo, { .i = -1 } }, { .keyv.c = CONTROL('^'), { t_rw, 0, 0, 0 }, f_repeat, { 0 } }, { .keyv.c = META('6'), { t_rw, 0, 0, 0 }, f_pipeline, { .v = "tr '\n' ' '" } }, /* Join lines */ { .keyv.c = META('5'), { t_sel, t_rw, 0, 0 }, f_spawn, REPLACE }, -{ .keyv.c = CONTROL('_'), { t_undo,t_rw, 0, 0 }, f_undo, { .i = 1 } }, -{ .keyv.c = CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, -{ .keyv.c = META(','), { 0, 0, 0, 0 }, f_move, { .m = m_bof } }, -{ .keyv.c = META('.'), { 0, 0, 0, 0 }, f_move, { .m = m_eof } }, +{ .keyv.c = CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, +{ .keyv.c = META(','), { 0, 0, 0, 0 }, f_move, { .m = m_bof } }, +{ .keyv.c = META('.'), { 0, 0, 0, 0 }, f_move, { .m = m_eof } }, }; +#if VIM_BINDINGS +// TODO: add better paste support (if whole line was yanked, append above, not where you are) +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_move, { .m = m_nextchar } }, +{ .keyv.c = { 'a' }, { 0, 0, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'b' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_prevword } }, +{ .keyv.c = { 'b' }, { 0, 0, 0, 0 }, f_move, { .m = m_prevword } }, +{ .keyv.c = { 'c' }, { t_rw, 0, 0, 0 }, f_delete, { .i = 0 } }, +{ .keyv.c = { 'd' }, { t_rw, 0, 0, 0 }, f_delete, { .i = 0 } }, +{ .keyv.c = { 'g' }, { 0, 0, 0, 0 }, f_move, { .m = m_bof } }, +{ .keyv.c = { 'G' }, { 0, 0, 0, 0 }, f_move, { .m = m_eof } }, +{ .keyv.c = { 'h' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_prevchar } }, +{ .keyv.c = { 'h' }, { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } }, +{ .keyv.c = { 'i' }, { 0, 0, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'j' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_nextline } }, +{ .keyv.c = { 'j' }, { 0, 0, 0, 0 }, f_move, { .m = m_nextline } }, +{ .keyv.c = { 'k' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_prevline } }, +{ .keyv.c = { 'k' }, { 0, 0, 0, 0 }, f_move, { .m = m_prevline } }, +{ .keyv.c = { 'l' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_nextchar } }, +{ .keyv.c = { 'l' }, { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } }, +{ .keyv.c = { 'm' }, { 0, 0, 0, 0 }, f_mark, { 0 } }, +{ .keyv.c = { 'n' }, { t_sel, 0, 0, 0 }, f_findfw, { 0 } }, +{ .keyv.c = { 'N' }, { t_sel, 0, 0, 0 }, f_findbw, { 0 } }, +{ .keyv.c = { 'o' }, { t_rw, t_ai, 0, 0 }, f_move, { .m = m_eol } }, +{ .keyv.c = { 'o' }, { t_rw, t_ai, 0, 0 }, f_pipeai, { .v = AUTOINDENT } }, +{ .keyv.c = { 'o' }, { t_rw, t_ai, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'o' }, { t_rw, 0, 0, 0 }, f_move, { .m = m_eol } }, +{ .keyv.c = { 'o' }, { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } }, +{ .keyv.c = { 'o' }, { t_rw, 0, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'O' }, { t_rw, t_ai, 0, 0 }, f_move, { .m = m_bol } }, +{ .keyv.c = { 'O' }, { t_rw, t_ai, 0, 0 }, f_pipeai, { .v = AUTOINDENT } }, +{ .keyv.c = { 'O' }, { t_rw, t_ai, 0, 0 }, f_move, { .m = m_prevline } }, +{ .keyv.c = { 'O' }, { t_rw, t_ai, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'O' }, { t_rw, 0, 0, 0 }, f_move, { .m = m_bol } }, +{ .keyv.c = { 'O' }, { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } }, +{ .keyv.c = { 'O' }, { t_rw, 0, 0, 0 }, f_move, { .m = m_prevline } }, +{ .keyv.c = { 'O' }, { t_rw, 0, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'p' }, { t_rw, 0, 0, 0 }, f_pipenull, { .v = FROMCLIP } }, +{ .keyv.c = { 's' }, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, +{ .keyv.c = { 's' }, { t_sel, t_rw, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 's' }, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.c = { 's' }, { t_rw, 0, 0, 0 }, f_toggle, { .i = S_Command } }, +{ .keyv.c = { 'u' }, { t_undo,t_rw, 0, 0 }, f_undo, { .i = 1 } }, +{ .keyv.c = { 'v' }, { 0, 0, 0, 0 }, f_toggle, { .i = S_Visual } }, +{ .keyv.c = { 'w' }, { t_sent,0, 0, 0 }, f_adjective, { .m = m_nextword } }, +{ .keyv.c = { 'w' }, { 0, 0, 0, 0 }, f_move, { .m = m_nextword } }, +{ .keyv.c = { 'x' }, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, +{ .keyv.c = { 'x' }, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } }, +{ .keyv.c = { 'X' }, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, +{ .keyv.c = { 'X' }, { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } }, +{ .keyv.c = { 'y' }, { t_rw, 0, 0, 0 }, f_pipero, { .i = 0, .v = TOCLIP } }, +{ .keyv.c = { ';' }, { 0, 0, 0, 0 }, f_spawn, CMD_P }, +{ .keyv.c = { ':' }, { 0, 0, 0, 0 }, f_spawn, CMD_P }, +{ .keyv.c = { '\'' }, { 0, 0, 0, 0 }, f_move, { .m = m_tomark } }, +{ .keyv.c = { '.' }, { t_rw, 0, 0, 0 }, f_repeat, { 0 } }, +{ .keyv.c = { '/' }, { 0, 0, 0, 0 }, f_spawn, FIND }, +{ .keyv.c = { ' ' }, { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } }, +/* TODO: Keybindings left: + * e/E go to the end of the word (adj) (?) + * r replace char (verb) + * t/T do until char (adj) + * i do inside (adj) (ex. diw deletes current word) + * ident + */ +}; +#endif + #if HANDLE_MOUSE /*Mouse clicks */ static const Click clks[] = { /* mouse mask, fcur / fsel, tests, func, arg */ {BUTTON1_CLICKED, { TRUE , TRUE }, { 0, 0, 0 }, 0, { 0 } }, -{BUTTON3_CLICKED, { TRUE , FALSE }, { t_sel, 0, 0 }, f_pipero, TOSEL }, -{BUTTON2_CLICKED, { FALSE, FALSE }, { 0, 0, 0 }, f_pipenull, FROMSEL }, +{BUTTON3_CLICKED, { TRUE , FALSE }, { t_sel, 0, 0 }, f_pipero, { .v = TOSEL } }, +{BUTTON2_CLICKED, { FALSE, FALSE }, { 0, 0, 0 }, f_pipenull, { .v = FROMSEL } }, //{BUTTON4_CLICKED, { FALSE, FALSE }, { 0, 0, 0 }, f_move, { .m = m_prevscr } }, //{BUTTON5_CLICKED, { FALSE, FALSE }, { 0, 0, 0 }, f_move, { .m = m_nextscr } }, /* ^^ NCurses is a sad old library.... it does not include button 5 nor cursor movement in its mouse declaration by default */ @@ -338,17 +418,12 @@ static const short bgcolors[LastBG] = { }; /* Helper config functions implementation */ -void /* Move both cursor and selection point, thus cancelling the selection */ -f_moveboth(const Arg *arg) { - fsel=fcur=arg->m(fcur); -} - void /* Pipe selection from bol, then select last line only, special for autoindenting */ f_pipeai(const Arg *arg) { i_sortpos(&fsel, &fcur); fsel.o=0; f_pipe(arg); - fsel.l=fcur.l; fsel.o=0; + fsel=fcur; } void /* Pipe full lines including the selection */ @@ -362,4 +437,3 @@ f_pipenull(const Arg *arg) { fsel=fcur; f_pipe(arg); } - diff --git a/config.mk b/config.mk index 495fdf1..0ae10a3 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # sandy version -VERSION = 0.5 +VERSION = 0.6 # Customize below to fit your system diff --git a/sandy.c b/sandy.c index 5d049f3..f4e8320 100644 --- a/sandy.c +++ b/sandy.c @@ -137,6 +137,11 @@ enum { /* To use in statusflags */ S_GroupUndo = 1<<9, /* Last action was an insert, so another insert should group with it, set automatically */ 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. Pass the next command's parameters (if adjective) to the verb's function */ + S_Parameter = 1<<14, /* Parameter mode. Pass the next character as parameter to the function of the command */ + S_Multiply = 1<<15, /* Multiply mode. Replay a command x times */ + S_Visual = 1<<16, /* Visual mode. You just select things */ }; enum { /* To use in Undo.flags */ @@ -157,35 +162,38 @@ 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 */ +static long statusflags=S_Running | S_Command; /* 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 void (*verb)(const Arg *arg); /* Verb of current sentence */ +static int multiply = 1; /* Times to replay a command */ /* 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*); @@ -225,6 +233,7 @@ static void i_killundos(Undo**); static Line *i_lineat(unsigned long); static unsigned long i_lineno(Line*); static void i_mouse(void); +static void i_multiply(void (*func)(const Arg *arg), const Arg arg); static void i_pipetext(const char*); static void i_readfifo(void); static void i_readfile(char*); @@ -245,10 +254,13 @@ static bool t_ai(void); static bool t_bol(void); static bool t_eol(void); static bool t_mod(void); +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_vis(void); static bool t_warn(void); /* m_ functions represent a cursor movement and can be passed in an Arg */ @@ -279,6 +291,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 /* VIM_BINDINGS */ + 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; @@ -351,7 +371,7 @@ f_insert(const Arg *arg) { i_addundo(TRUE, fcur, newcur, strdup((char*)arg->v)); if(fcur.l!=newcur.l) fsel=newcur; } - fcur=newcur; + fcur=fsel=newcur; statusflags|=(S_Modified|S_GroupUndo); lastaction=LastInsert; } @@ -374,6 +394,7 @@ f_mark(const Arg *arg) { void /* Move cursor and extend/shrink selection as per arg->m */ f_move(const Arg *arg) { fcur=arg->m(fcur); + if(!t_vis()) fsel=fcur; } void /* Got to atoi(arg->v) position in the current line */ @@ -530,7 +551,7 @@ f_undo(const Arg *arg) { i_deltext(start, end); fcur=fsel=start; } else - fcur=i_addtext(u->str, fcur); + fcur=fsel=i_addtext(u->str, fcur); if(isredo) redos=u->prev, u->prev=undos, undos=u; else @@ -739,8 +760,9 @@ i_dotests(bool (*const a[])(void)) { void /* Main editing loop */ i_edit(void) { - int ch, i; + int ch, i, j; char c[7]; + bool pass; fd_set fds; Filepos oldsel, oldcur; @@ -785,8 +807,20 @@ i_edit(void) { #endif /* HANDLE_MOUSE */ for(i=0; i= '0' && ch <= '9' && !(statusflags & S_Parameter)) { + if(statusflags & S_Multiply) { + multiply*=10; + multiply+=(int)ch-'0'; + } else { + statusflags|=S_Multiply; + multiply=(int)ch-'0'; + } + } else for(i=0; i":(filename == NULL?"":filename)), (syntx>=0 ? syntaxes[syntx].name : "none"), (t_mod()?"[+]":""), @@ -1665,6 +1804,7 @@ m_tosel(Filepos pos) { /* T_* FUNCTIONS Used to test for conditions, take no arguments and return bool. */ + bool /* TRUE is autoindent is on */ t_ai(void) { return (statusflags & S_AutoIndent); @@ -1685,6 +1825,15 @@ t_mod(void) { return (statusflags & S_Modified); } +bool /* TRUE if we are not in command mode */ +t_nocomm(void) { +#if VIM_BINDINGS + return !(statusflags & S_Command); +#else + return TRUE; +#endif /* VIM_BINDINGS */ +} + bool /* TRUE if the file is writable */ t_rw(void) { return !(statusflags & S_Readonly); @@ -1700,11 +1849,25 @@ 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 /* VIM_BINDINGS */ +} + bool /* TRUE if there is anything to undo */ t_undo(void) { return (undos != NULL); } +bool /* TRUE if we are in visual mode */ +t_vis(void) { + return (statusflags & S_Visual); +} + bool /* TRUE if we have warned the file is modified */ t_warn(void) { return (statusflags & S_Warned); @@ -1718,6 +1881,7 @@ main(int argc, char **argv){ /* Use system locale, hopefully UTF-8 */ setlocale(LC_ALL,""); + 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++) { if(!strcmp(argv[i], "-r")) { @@ -1742,7 +1906,7 @@ main(int argc, char **argv){ i++; break; } else if(!strcmp(argv[i], "-v")) - i_die("sandy-"VERSION", © 2011 sandy engineers, see LICENSE for details\n"); + i_die("sandy-"VERSION", © 2014 sandy engineers, see LICENSE for details\n"); else i_usage(); }