commit 351f8205de780100d4d584e299aed39234a6d036
parent a1d230d67c6f077a4e4395d10cf81dffff2cc5aa
Author: Rafael Garcia <rafael.garcia.gallego@gmail.com>
Date: Thu, 2 Jun 2011 10:09:54 +0200
Implement Ctrl-Q warning, Ctrl-t to copy as per cls. Changed default keybindings to use Control only, updated manpage. Simplified commands in fifo.
Diffstat:
TODO | | | 1 | - |
config.control_only.h | | | 280 | ------------------------------------------------------------------------------- |
config.def.h | | | 148 | ++++++++++++++++++++++++++++++++----------------------------------------------- |
config.old.h | | | 311 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
sandy.1 | | | 89 | ++++++++++++++++++++++++++++--------------------------------------------------- |
sandy.c | | | 25 | +++++++++++++++++-------- |
6 files changed, 418 insertions(+), 436 deletions(-)
diff --git a/TODO b/TODO
@@ -1,5 +1,4 @@
In no particular order, at sandy.c:
-- Bind ^Z to stop, handle sigcont
- Rework m_*word
- Create m_*vline
- Bracket matching?
diff --git a/config.control_only.h b/config.control_only.h
@@ -1,280 +0,0 @@
-/* A simplified way to customize */
-#define HILIGHT_CURRENT 1
-#define SHOW_NONPRINT 0
-#define HILIGHT_SYNTAX 1 /* TODO: implement this */
-
-/* Things unlikely to be changed, yet still in the config.h file */
-static const bool isutf8 = TRUE;
-static const char fifobase[] = "/tmp/sandyfifo.";
-static int tabstop = 8; /* Not const, as it may be changed via param */
-/* static const char systempath[] = "/etc/sandy"; */
-/* static const char userpath[] = ".sandy"; */ /* Relative to $HOME */
-
-#if SHOW_NONPRINT /* TODO: show newline character too (as $) */
-static const char tabstr[3] = { (char)0xC2, (char)0xBB, 0x00 }; /* Double right arrow */
-static const char spcstr[3] = { (char)0xC2, (char)0xB7, 0x00 }; /* Middle dot */
-static const char nlstr[2] = { '$', 0x00 }; /* '$' is tradition for EOL */
-#else
-static const char tabstr[2] = { ' ', 0 };
-static const char spcstr[2] = { ' ', 0 };
-static const char nlstr[1] = { 0 };
-#endif
-
-/* Args to f_spawn */
-#define PROMPT(prompt, default, cmd) { .v = (const char *[]){ "/bin/sh", "-c", \
- "dmenu -v >/dev/null 2>&1 || DISPLAY=\"\";"\
- "if [ -n \"$DISPLAY\" ]; then arg=\"`echo \\\"" default "\\\" | dmenu -p '" prompt "'`\";" \
- "else printf \"\033[0;0H\033[7m"prompt"\033[K\033[0m \"; read arg; fi &&" \
- "echo " cmd " \"$arg\" > ${SANDY_FIFO}", NULL } }
-
-#define FIND PROMPT("Find:", "${SANDY_FIND}", "find")
-#define FINDBW PROMPT("Find (back):", "${SANDY_FIND}", "findbw")
-#define PIPE PROMPT("Pipe:", "${SANDY_PIPE}", "pipe")
-#define SAVEAS PROMPT("Save as:", "${SANDY_FILE}", "save")
-#define LINE PROMPT("Line:", "${SANDY_LINE}", "line")
-#define SYNTAX PROMPT("Syntax:", "${SANDY_SYNTAX}", "syntax")
-
-/* Args to f_pipe / f_pipero */
-/* TODO: make sandy-sel to wrap xsel or standalone */
-#define TOCLIP { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -ib || cat > ~/.sandy.clipboard" }
-#define FROMCLIP { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -ob || cat ~/.sandy.clipboard" }
-#define TOSEL { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -i || cat > ~/.sandy.selection" }
-#define FROMSEL { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -o || cat ~/.sandy.selection" }
-
-/* Hooks are launched from the main code */
-#define HOOK_SAVE_NO_FILE f_spawn (&(const Arg)SAVEAS)
-#define HOOK_SELECT_MOUSE f_pipero(&(const Arg)TOSEL)
-#undef HOOK_DELETE_ALL /* This affects every delete */
-#undef HOOK_SELECT_ALL /* This affects every selection */
-
-/* Key-bindings and stuff */
-/* WARNING: use CONTROL(ch) ONLY with '@', (caps)A-Z, '[', '\', ']', '^', '_' or '?' */
-/* otherwise it may not mean what you think. See man 7 ascii for more info */
-#define CONTROL(ch) {(ch ^ 0x40)}
-#define META(ch) {0x1B, ch}
-
-static const Key curskeys[] = { /* Don't use CONTROL or META here */
-/* keyv, tests, func, arg */
-{ {KEY_BACKSPACE}, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } },
-{ {KEY_BACKSPACE}, { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ {KEY_DC}, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } },
-{ {KEY_DC}, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
-{ {KEY_IC}, { t_sel, 0, 0, 0 }, f_pipero, TOCLIP },
-{ {KEY_SDC}, { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ {KEY_SIC}, { t_rw, 0, 0, 0 }, f_pipe, FROMCLIP },
-{ {KEY_HOME}, { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
-{ {KEY_END}, { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
-{ {KEY_SHOME}, { 0, 0, 0, 0 }, f_move, { .m = m_bof } },
-{ {KEY_SEND}, { 0, 0, 0, 0 }, f_move, { .m = m_eof } },
-{ {KEY_PPAGE}, { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } },
-{ {KEY_NPAGE}, { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } },
-{ {KEY_UP}, { 0, 0, 0, 0 }, f_move, { .m = m_prevline } },
-{ {KEY_DOWN}, { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
-{ {KEY_LEFT}, { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
-{ {KEY_RIGHT}, { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
-{ {KEY_SLEFT}, { 0, 0, 0, 0 }, f_move, { .m = m_prevword } },
-{ {KEY_SRIGHT}, { 0, 0, 0, 0 }, f_move, { .m = m_nextword } },
-};
-
-static const Key stdkeys[] = {
-/* keyv, test, func, arg */
-{ CONTROL('@'), { 0, 0, 0, 0 }, f_mark, { 0 } },
-{ CONTROL('A'), { t_bol, 0, 0, 0 }, f_move, { .m = m_prevscr } },
-{ CONTROL('A'), { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
-{ CONTROL('B'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
-{ CONTROL('C'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('C'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } },
-{ CONTROL('C'), { 0, 0, 0, 0 }, f_select, { .m = m_nextword } },
-{ CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
-{ CONTROL('D'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
-{ CONTROL('E'), { t_eol, 0, 0, 0 }, f_move, { .m = m_nextscr } },
-{ CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
-{ CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
-{ CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } },
-{ CONTROL('G'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Selecting } },
-{ CONTROL('H'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ CONTROL('H'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
-{ CONTROL('I'), { t_sel, t_rw, 0, 0 }, f_pipelines, { .v = "sed 's/^/\\t/'" } },
-{ CONTROL('I'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\t" } },
-{ CONTROL('J'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
-{ CONTROL('J'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
-{ CONTROL('K'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } },
-{ CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } },
-{ CONTROL('K'), { 0, 0, 0, 0 }, f_select, { .m = m_eol } },
-{ CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } },
-{ CONTROL('M'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
-{ CONTROL('M'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
-{ CONTROL('N'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
-{ CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */
-{ CONTROL('O'), { 0, 0, 0, 0 }, f_move, { .m = m_tomark } },
-{ CONTROL('P'), { 0, 0, 0, 0 }, f_move, { .m = m_prevline } },
-{ CONTROL('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
-{ CONTROL('R'), { t_sel, 0, 0, 0 }, f_findbw, { 0 } },
-{ CONTROL('R'), { 0, 0, 0, 0 }, f_spawn, FINDBW },
-{ CONTROL('S'), { t_sel, 0, 0, 0 }, f_findfw, { 0 } },
-{ CONTROL('S'), { 0, 0, 0, 0 }, f_spawn, FIND },
-{ CONTROL('T'), { 0, 0, 0, 0 }, f_spawn , LINE },
-{ CONTROL('U'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('U'), { t_bol, t_rw, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ CONTROL('U'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_bol } },
-{ CONTROL('U'), { 0, 0, 0, 0 }, f_select, { .m = m_bol } },
-{ CONTROL('V'), { 0, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } },
-{ CONTROL('W'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('W'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevword } },
-{ CONTROL('W'), { 0, 0, 0, 0 }, f_select, { .m = m_prevword } },
-{ CONTROL('X'), { t_mod ,0, 0, 0 }, f_save, { 0 } },
-{ CONTROL('X'), { 0 ,0, 0, 0 }, f_toggle, { .i = S_Running } },
-{ CONTROL('Y'), { t_rw, 0, 0, 0 }, f_pipe, FROMCLIP },
-{ CONTROL('Z'), { 0 ,0, 0, 0 }, f_suspend, { 0 } },
-{ CONTROL('['), { 0, 0, 0, 0 }, f_spawn, SYNTAX }, /* TODO: Sam's? */
-{ CONTROL('\\'),{ 0, 0, 0, 0 }, f_spawn, PIPE },
-{ CONTROL(']'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtDefault } },
-{ CONTROL('^'), { t_redo,t_rw, 0, 0 }, f_undo, { .i = -1 } },
-/*{ CONTROL('^'), { t_rw, 0, 0, 0 }, f_undo, { .i = 0 } }, */ /* TODO: repeat, implement */
-{ CONTROL('_'), { t_undo,t_rw, 0, 0 }, f_undo, { .i = 1 } },
-{ CONTROL('?'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ CONTROL('?'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
-};
-
-/* Commands read at the fifo */
-static Command cmds[] = { /* Use only f_ funcs that take Arg.v */
-/* \0, regex, tests, func */
-{NULL, "^find (.*)$", { 0, 0 }, f_findfw },
-{NULL, "^findbw (.*)$", { 0, 0 }, f_findbw },
-{NULL, "^pipe (.*)$", { t_rw, 0 }, f_pipe },
-{NULL, "^pipe (.*)$", { 0, 0 }, f_pipero },
-{NULL, "^save (.*)$", { 0, 0 }, f_save },
-{NULL, "^syntax (.*)$", { 0, 0 }, f_syntax },
-{NULL, "^line (.*)$", { 0, 0 }, f_line },
-{NULL, "^offset (.*)$", { 0, 0 }, f_offset },
-};
-
-/* Syntax color definition */
-#define B "(^| |\t|\\(|\\)|\\[|\\]|\\{|\\}|$)"
-
-static Syntax syntaxes[] = {
-{"c", NULL, "\\.(c(pp|xx)?|h(pp|xx)?|cc)$", { NULL }, {
- /* HiRed */ "",
- /* HiGreen */ B"(for|if|while|do|else|case|default|switch|try|throw|catch|operator|new|delete)"B,
- /* LoGreen */ B"(float|double|bool|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline|((s?size)|((u_?)?int(8|16|32|64|ptr)))_t|class|namespace|template|public|protected|private|typename|this|friend|virtual|using|mutable|volatile|register|explicit)"B,
- /* HiMag */ B"(goto|continue|break|return)"B,
- /* LoMag */ "(^#(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma))|"B"\\<[A-Z_][0-9A-Z_]+\\>"B"",
- /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
- /* LoRed */ "(\"(\\\\.|[^\"])*\")",
- /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
- } },
-
-{"sh", NULL, "\\.sh$", { NULL }, {
- /* HiRed */ "",
- /* HiGreen */ "^[0-9A-Z_]+\\(\\)",
- /* LoGreen */ B"(case|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|shift|then|time|until|while)"B,
- /* HiMag */ "",
- /* LoMag */ "\"(\\\\.|[^\"])*\"",
- /* HiBlue */ "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)",
- /* LoRed */ "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?",
- /* LoBlue */ "#.*$",
- } },
-
-{"makefile", NULL, "(Makefile[^/]*|\\.mk)$", { NULL }, {
- /* HiRed */ "",
- /* HiGreen */ "",
- /* LoGreen */ "\\$+[{(][a-zA-Z0-9_-]+[})]",
- /* HiMag */ B"(if|ifeq|else|endif)"B,
- /* LoMag */ "",
- /* HiBlue */ "^[^ ]+:",
- /* LoRed */ "[:=]",
- /* LoBlue */ "#.*$",
- } },
-
-{"man", NULL, "\\.[1-9]x?$", { NULL }, {
- /* HiRed */ "\\.(BR?|I[PR]?).*$",
- /* HiGreen */ "",
- /* LoGreen */ "\\.(S|T)H.*$",
- /* HiMag */ "\\.(br|DS|RS|RE|PD)",
- /* LoMag */ "(\\.(S|T)H|\\.TP)",
- /* HiBlue */ "\\.(BR?|I[PR]?|PP)",
- /* LoRed */ "",
- /* LoBlue */ "\\\\f[BIPR]",
- } },
-
-{"vala", NULL, "\\.(vapi|vala)$", { NULL }, {
- /* HiRed */ B"[A-Z_][0-9A-Z_]+\\>",
- /* HiGreen */ B"(for|if|while|do|else|case|default|switch|get|set|value|out|ref|enum)"B,
- /* LoGreen */ B"(uint|uint8|uint16|uint32|uint64|bool|byte|ssize_t|size_t|char|double|string|float|int|long|short|this|base|transient|void|true|false|null|unowned|owned)"B,
- /* HiMag */ B"(try|catch|throw|finally|continue|break|return|new|sizeof|signal|delegate)"B,
- /* LoMag */ B"(abstract|class|final|implements|import|instanceof|interface|using|private|public|static|strictfp|super|throws)"B,
- /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
- /* LoRed */ "\"(\\\\.|[^\"])*\"",
- /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
- } },
-{"java", NULL, "\\.java$", { NULL }, {
- /* HiRed */ B"[A-Z_][0-9A-Z_]+\\>",
- /* HiGreen */ B"(for|if|while|do|else|case|default|switch)"B,
- /* LoGreen */ B"(boolean|byte|char|double|float|int|long|short|transient|void|true|false|null)"B,
- /* HiMag */ B"(try|catch|throw|finally|continue|break|return|new)"B,
- /* LoMag */ B"(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|this|super|synchronized|throws|volatile)"B,
- /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
- /* LoRed */ "\"(\\\\.|[^\"])*\"",
- /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
- } },
-};
-
-/* Colors */
-static const short fgcolors[LastFG] = {
- [DefFG] = -1,
- [CurFG] = (HILIGHT_CURRENT?COLOR_BLACK:-1),
- [SelFG] = COLOR_BLACK,
- [SpcFG] = COLOR_WHITE,
- [CtrlFG] = COLOR_RED,
- [Syn0FG] = COLOR_RED,
- [Syn1FG] = COLOR_GREEN,
- [Syn2FG] = COLOR_GREEN,
- [Syn3FG] = COLOR_MAGENTA,
- [Syn4FG] = COLOR_MAGENTA,
- [Syn5FG] = COLOR_BLUE,
- [Syn6FG] = COLOR_RED,
- [Syn7FG] = COLOR_BLUE,
-};
-
-static const int colorattrs[LastFG] = {
- [DefFG] = 0,
- [CurFG] = 0,
- [SelFG] = 0,
- [SpcFG] = A_DIM,
- [CtrlFG] = A_DIM,
- [Syn0FG] = A_BOLD,
- [Syn1FG] = A_BOLD,
- [Syn2FG] = 0,
- [Syn3FG] = A_BOLD,
- [Syn4FG] = 0,
- [Syn5FG] = A_BOLD,
- [Syn6FG] = 0,
- [Syn7FG] = 0,
-};
-
-static const int bwattrs[LastFG] = {
- [DefFG] = 0,
- [CurFG] = 0,
- [SelFG] = A_REVERSE,
- [SpcFG] = A_DIM,
- [CtrlFG] = A_DIM,
- [Syn0FG] = A_BOLD,
- [Syn1FG] = A_BOLD,
- [Syn2FG] = A_BOLD,
- [Syn3FG] = A_BOLD,
- [Syn4FG] = A_BOLD,
- [Syn5FG] = A_BOLD,
- [Syn6FG] = A_BOLD,
- [Syn7FG] = A_BOLD,
-};
-
-static const short bgcolors[LastBG] = {
- [DefBG] = -1,
- [CurBG] = (HILIGHT_CURRENT?COLOR_CYAN:-1),
- [SelBG] = COLOR_YELLOW,
-};
-
diff --git a/config.def.h b/config.def.h
@@ -25,14 +25,13 @@ static const char nlstr[1] = { 0 };
"dmenu -v >/dev/null 2>&1 || DISPLAY=\"\";"\
"if [ -n \"$DISPLAY\" ]; then arg=\"`echo \\\"" default "\\\" | dmenu -p '" prompt "'`\";" \
"else printf \"\033[0;0H\033[7m"prompt"\033[K\033[0m \"; read arg; fi &&" \
- "echo " cmd " \"$arg\" > ${SANDY_FIFO}", NULL } }
+ "echo " cmd "\"$arg\" > ${SANDY_FIFO}", NULL } }
-#define FIND PROMPT("Find:", "${SANDY_FIND}", "find")
-#define FINDBW PROMPT("Find (back):", "${SANDY_FIND}", "findbw")
-#define PIPE PROMPT("Pipe:", "${SANDY_PIPE}", "pipe")
-#define SAVEAS PROMPT("Save as:", "${SANDY_FILE}", "save")
-#define LINE PROMPT("Line:", "${SANDY_LINE}", "line")
-#define SYNTAX PROMPT("Syntax:", "${SANDY_SYNTAX}", "syntax")
+#define FIND PROMPT("Find:", "${SANDY_FIND}", "/")
+#define FINDBW PROMPT("Find (back):", "${SANDY_FIND}", "?")
+#define PIPE PROMPT("Pipe:", "${SANDY_PIPE}", "|")
+#define SAVEAS PROMPT("Save as:", "${SANDY_FILE}", "w")
+#define CMD_P PROMPT("Command:", "/\n?\n|\nw\nsyntax\noffset", "")
/* Args to f_pipe / f_pipero */
/* TODO: make sandy-sel to wrap xsel or standalone */
@@ -78,109 +77,81 @@ static const Key curskeys[] = { /* Don't use CONTROL or META here */
static const Key stdkeys[] = {
/* keyv, test, func, arg */
-/* You probably know these as TAB, Enter and Return */
+{ CONTROL('@'), { 0, 0, 0, 0 }, f_mark, { 0 } },
+{ CONTROL('A'), { t_bol, 0, 0, 0 }, f_move, { .m = m_prevscr } },
+{ CONTROL('A'), { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
+{ CONTROL('B'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
+{ CONTROL('C'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('C'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } },
+{ CONTROL('C'), { 0, 0, 0, 0 }, f_select, { .m = m_nextword } },
+{ CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
+{ CONTROL('D'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
+{ CONTROL('E'), { t_eol, 0, 0, 0 }, f_move, { .m = m_nextscr } },
+{ CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
+{ CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
+{ CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } },
+{ CONTROL('G'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Selecting } },
+{ CONTROL('H'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ CONTROL('H'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
{ CONTROL('I'), { t_sel, t_rw, 0, 0 }, f_pipelines, { .v = "sed 's/^/\\t/'" } },
{ CONTROL('I'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\t" } },
{ CONTROL('J'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
{ CONTROL('J'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+{ CONTROL('K'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } },
+{ CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } },
+{ CONTROL('K'), { 0, 0, 0, 0 }, f_select, { .m = m_eol } },
+{ CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } },
{ CONTROL('M'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
{ CONTROL('M'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
-
-/* Cursor movement, also when selecting */
-{ CONTROL('A'), { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
-{ CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
-{ CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
-{ META('f'), { 0, 0, 0, 0 }, f_move, { .m = m_nextword } },
-{ CONTROL('B'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
-{ META('b'), { 0, 0, 0, 0 }, f_move, { .m = m_prevword } },
{ CONTROL('N'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+{ CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */
+{ CONTROL('O'), { 0, 0, 0, 0 }, f_move, { .m = m_tomark } },
{ CONTROL('P'), { 0, 0, 0, 0 }, f_move, { .m = m_prevline } },
-{ META(','), { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } },
-{ META('.'), { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } },
-{ META('<'), { 0, 0, 0, 0 }, f_move, { .m = m_bof } },
-{ META('>'), { 0, 0, 0, 0 }, f_move, { .m = m_eof } },
-{ META('g'), { 0, 0, 0, 0 }, f_spawn, LINE },
-
-/* Finding and selecting */
-{ CONTROL('S'), { 0, 0, 0, 0 }, f_spawn, FIND },
+{ CONTROL('Q'), { t_warn,t_mod,0, 0 }, f_toggle, { .i = S_Running } },
+{ CONTROL('Q'), { t_mod, 0, 0, 0 }, f_warn, { .v = "WARNING! File modified!!!" } },
+{ CONTROL('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
+{ CONTROL('R'), { t_sel, 0, 0, 0 }, f_findbw, { 0 } },
{ CONTROL('R'), { 0, 0, 0, 0 }, f_spawn, FINDBW },
-{ META('n'), { 0, 0, 0, 0 }, f_findfw, { 0 } },
-{ META('p'), { 0, 0, 0, 0 }, f_findbw, { 0 } },
-{ CONTROL('X'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtDefault } },
-{ META('x'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtAll } },
-{ CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } },
-{ CONTROL('G'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Selecting } },
-{ CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */
-{ META('i'), { 0, 0, 0, 0 }, f_toggle, { .i = S_CaseIns } },
-{ META('s'), { t_sel, 0, 0, 0 }, f_pipero, { .v = "(sed 's/$/\\n/;2q' | (read arg && echo find \"$arg\" > ${SANDY_FIFO}))" } },
-{ META('r'), { t_sel, 0, 0, 0 }, f_pipero, { .v = "(sed 's/$/\\n/;2q' | (read arg && echo findbw \"$arg\" > ${SANDY_FIFO}))" } },
-
-/* Text deletion */
-{ CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
-{ CONTROL('D'), { 0, 0, 0, 0 }, f_select, { .m = m_nextchar } },
-{ CONTROL('?'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ CONTROL('?'), { 0, 0, 0, 0 }, f_select, { .m = m_prevchar } },
-{ CONTROL('H'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
-{ CONTROL('H'), { 0, 0, 0, 0 }, f_select, { .m = m_prevchar } },
+{ CONTROL('S'), { t_sel, 0, 0, 0 }, f_findfw, { 0 } },
+{ CONTROL('S'), { 0, 0, 0, 0 }, f_spawn, FIND },
+{ CONTROL('T'), { 0, 0, 0, 0 }, f_pipero , TOCLIP },
{ CONTROL('U'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
{ CONTROL('U'), { t_bol, t_rw, 0, 0 }, f_delete, { .m = m_prevchar } },
{ CONTROL('U'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_bol } },
{ CONTROL('U'), { 0, 0, 0, 0 }, f_select, { .m = m_bol } },
-{ CONTROL('K'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } },
-{ CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } },
-{ CONTROL('K'), { 0, 0, 0, 0 }, f_select, { .m = m_eol } },
+{ CONTROL('V'), { 0, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } },
{ CONTROL('W'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
{ CONTROL('W'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevword } },
{ CONTROL('W'), { 0, 0, 0, 0 }, f_select, { .m = m_prevword } },
-{ META('d'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
-{ META('d'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } },
-{ META('d'), { 0, 0, 0, 0 }, f_select, { .m = m_nextword } },
-
-/* Mark operation */
-{ META(' '), { 0, 0, 0, 0 }, f_mark, { 0 } },
-{ CONTROL('@'), { 0, 0, 0, 0 }, f_move, { .m = m_tomark } },
-
-/* File operations */
-{ CONTROL('Q'), { t_mod, 0, 0, 0 }, f_title, { .v = "WARNING! File not saved! Press META+SHIFT+Q to quit" } },
-{ CONTROL('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
-{ META('q'), { t_mod, 0, 0, 0 }, f_title, { .v = "WARNING! File not saved!" } },
-{ META('q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
-{ META('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
-{ META('w'), { 0, 0, 0, 0 }, f_save, { 0 } },
-{ META('W'), { 0, 0, 0, 0 }, f_spawn, SAVEAS },
-
-/* Text piping and modification */
-{ CONTROL('\\'),{ 0, 0, 0, 0 }, f_spawn, PIPE },
+{ CONTROL('X'), { t_mod ,0, 0, 0 }, f_save, { 0 } },
+{ CONTROL('X'), { 0 ,0, 0, 0 }, f_toggle, { .i = S_Running } },
{ CONTROL('Y'), { t_rw, 0, 0, 0 }, f_pipe, FROMCLIP },
-{ CONTROL('C'), { t_sel, 0, 0, 0 }, f_pipero, TOCLIP },
-
-/* Undo / Redo */
-{ CONTROL('_'), { t_undo,t_rw, 0, 0 }, f_undo, { .i = +1 } },
+{ CONTROL('Z'), { 0 ,0, 0, 0 }, f_suspend, { 0 } },
+{ CONTROL('['), { 0, 0, 0, 0 }, f_spawn, CMD_P }, /* TODO: Sam's? */
+{ CONTROL('\\'),{ 0, 0, 0, 0 }, f_spawn, PIPE },
+{ CONTROL(']'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtDefault } },
{ CONTROL('^'), { t_redo,t_rw, 0, 0 }, f_undo, { .i = -1 } },
-
-/* Others */
-{ CONTROL('Z'), { 0, 0, 0, 0 }, f_suspend, { 0 } },
-{ CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } },
-{ CONTROL('V'), { 0, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } },
-{ META('R'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Readonly } },
-{ META('S'), { 0, 0, 0, 0 }, f_spawn, SYNTAX },
+/*{ CONTROL('^'), { t_rw, 0, 0, 0 }, f_undo, { .i = 0 } }, */ /* TODO: repeat, implement */
+{ CONTROL('_'), { t_undo,t_rw, 0, 0 }, f_undo, { .i = 1 } },
+{ CONTROL('?'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ CONTROL('?'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
};
/* Commands read at the fifo */
static Command cmds[] = { /* Use only f_ funcs that take Arg.v */
-/* \0, regex, tests, func */
-{NULL, "^find (.*)$", { 0, 0 }, f_findfw },
-{NULL, "^findbw (.*)$", { 0, 0 }, f_findbw },
-{NULL, "^pipe (.*)$", { t_rw, 0 }, f_pipe },
-{NULL, "^pipe (.*)$", { 0, 0 }, f_pipero },
-{NULL, "^save (.*)$", { 0, 0 }, f_save },
-{NULL, "^syntax (.*)$", { 0, 0 }, f_syntax },
-{NULL, "^line (.*)$", { 0, 0 }, f_line },
-{NULL, "^offset (.*)$", { 0, 0 }, f_offset },
+/* \0, regex, tests, func */
+{NULL, "^([0-9]+)$", { 0, 0 }, f_line },
+{NULL, "^/(.*)$", { 0, 0 }, f_findfw },
+{NULL, "^\\?(.*)$", { 0, 0 }, f_findbw },
+{NULL, "^\\|[ \t]*(.*)$", { t_rw, 0 }, f_pipe },
+{NULL, "^\\|[ \t]*(.*)$", { 0, 0 }, f_pipero },
+{NULL, "^w[ \t]*(.*)$", { 0, 0 }, f_save },
+{NULL, "^syntax (.*)$", { 0, 0 }, f_syntax },
+{NULL, "^offset (.*)$", { 0, 0 }, f_offset },
};
/* Syntax color definition */
@@ -241,7 +212,6 @@ static Syntax syntaxes[] = {
/* LoRed */ "\"(\\\\.|[^\"])*\"",
/* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
} },
-
{"java", NULL, "\\.java$", { NULL }, {
/* HiRed */ B"[A-Z_][0-9A-Z_]+\\>",
/* HiGreen */ B"(for|if|while|do|else|case|default|switch)"B,
diff --git a/config.old.h b/config.old.h
@@ -0,0 +1,311 @@
+/* A simplified way to customize */
+#define HILIGHT_CURRENT 1
+#define SHOW_NONPRINT 0
+#define HILIGHT_SYNTAX 1 /* TODO: implement this */
+
+/* Things unlikely to be changed, yet still in the config.h file */
+static const bool isutf8 = TRUE;
+static const char fifobase[] = "/tmp/sandyfifo.";
+static int tabstop = 8; /* Not const, as it may be changed via param */
+/* static const char systempath[] = "/etc/sandy"; */
+/* static const char userpath[] = ".sandy"; */ /* Relative to $HOME */
+
+#if SHOW_NONPRINT /* TODO: show newline character too (as $) */
+static const char tabstr[3] = { (char)0xC2, (char)0xBB, 0x00 }; /* Double right arrow */
+static const char spcstr[3] = { (char)0xC2, (char)0xB7, 0x00 }; /* Middle dot */
+static const char nlstr[2] = { '$', 0x00 }; /* '$' is tradition for EOL */
+#else
+static const char tabstr[2] = { ' ', 0 };
+static const char spcstr[2] = { ' ', 0 };
+static const char nlstr[1] = { 0 };
+#endif
+
+/* Args to f_spawn */
+#define PROMPT(prompt, default, cmd) { .v = (const char *[]){ "/bin/sh", "-c", \
+ "dmenu -v >/dev/null 2>&1 || DISPLAY=\"\";"\
+ "if [ -n \"$DISPLAY\" ]; then arg=\"`echo \\\"" default "\\\" | dmenu -p '" prompt "'`\";" \
+ "else printf \"\033[0;0H\033[7m"prompt"\033[K\033[0m \"; read arg; fi &&" \
+ "echo " cmd " \"$arg\" > ${SANDY_FIFO}", NULL } }
+
+#define FIND PROMPT("Find:", "${SANDY_FIND}", "find")
+#define FINDBW PROMPT("Find (back):", "${SANDY_FIND}", "findbw")
+#define PIPE PROMPT("Pipe:", "${SANDY_PIPE}", "pipe")
+#define SAVEAS PROMPT("Save as:", "${SANDY_FILE}", "save")
+#define LINE PROMPT("Line:", "${SANDY_LINE}", "line")
+#define SYNTAX PROMPT("Syntax:", "${SANDY_SYNTAX}", "syntax")
+
+/* Args to f_pipe / f_pipero */
+/* TODO: make sandy-sel to wrap xsel or standalone */
+#define TOCLIP { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -ib || cat > ~/.sandy.clipboard" }
+#define FROMCLIP { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -ob || cat ~/.sandy.clipboard" }
+#define TOSEL { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -i || cat > ~/.sandy.selection" }
+#define FROMSEL { .v = "xsel -h >/dev/null 2>&1 && test -n \"$DISPLAY\" && xsel -o || cat ~/.sandy.selection" }
+
+/* Hooks are launched from the main code */
+#define HOOK_SAVE_NO_FILE f_spawn (&(const Arg)SAVEAS)
+#define HOOK_SELECT_MOUSE f_pipero(&(const Arg)TOSEL)
+#undef HOOK_DELETE_ALL /* This affects every delete */
+#undef HOOK_SELECT_ALL /* This affects every selection */
+
+/* Key-bindings and stuff */
+/* WARNING: use CONTROL(ch) ONLY with '@', (caps)A-Z, '[', '\', ']', '^', '_' or '?' */
+/* otherwise it may not mean what you think. See man 7 ascii for more info */
+#define CONTROL(ch) {(ch ^ 0x40)}
+#define META(ch) {0x1B, ch}
+
+static const Key curskeys[] = { /* Don't use CONTROL or META here */
+/* keyv, tests, func, arg */
+{ {KEY_BACKSPACE}, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } },
+{ {KEY_BACKSPACE}, { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ {KEY_DC}, { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } },
+{ {KEY_DC}, { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
+{ {KEY_IC}, { t_sel, 0, 0, 0 }, f_pipero, TOCLIP },
+{ {KEY_SDC}, { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ {KEY_SIC}, { t_rw, 0, 0, 0 }, f_pipe, FROMCLIP },
+{ {KEY_HOME}, { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
+{ {KEY_END}, { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
+{ {KEY_SHOME}, { 0, 0, 0, 0 }, f_move, { .m = m_bof } },
+{ {KEY_SEND}, { 0, 0, 0, 0 }, f_move, { .m = m_eof } },
+{ {KEY_PPAGE}, { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } },
+{ {KEY_NPAGE}, { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } },
+{ {KEY_UP}, { 0, 0, 0, 0 }, f_move, { .m = m_prevline } },
+{ {KEY_DOWN}, { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+{ {KEY_LEFT}, { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
+{ {KEY_RIGHT}, { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
+{ {KEY_SLEFT}, { 0, 0, 0, 0 }, f_move, { .m = m_prevword } },
+{ {KEY_SRIGHT}, { 0, 0, 0, 0 }, f_move, { .m = m_nextword } },
+};
+
+static const Key stdkeys[] = {
+/* keyv, test, func, arg */
+/* You probably know these as TAB, Enter and Return */
+{ CONTROL('I'), { t_sel, t_rw, 0, 0 }, f_pipelines, { .v = "sed 's/^/\\t/'" } },
+{ CONTROL('I'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\t" } },
+{ CONTROL('J'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
+{ CONTROL('J'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+{ CONTROL('M'), { t_rw, 0, 0, 0 }, f_insert, { .v = "\n" } },
+{ CONTROL('M'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+
+/* Cursor movement, also when selecting */
+{ CONTROL('A'), { 0, 0, 0, 0 }, f_move, { .m = m_bol } },
+{ CONTROL('E'), { 0, 0, 0, 0 }, f_move, { .m = m_eol } },
+{ CONTROL('F'), { 0, 0, 0, 0 }, f_move, { .m = m_nextchar } },
+{ META('f'), { 0, 0, 0, 0 }, f_move, { .m = m_nextword } },
+{ CONTROL('B'), { 0, 0, 0, 0 }, f_move, { .m = m_prevchar } },
+{ META('b'), { 0, 0, 0, 0 }, f_move, { .m = m_prevword } },
+{ CONTROL('N'), { 0, 0, 0, 0 }, f_move, { .m = m_nextline } },
+{ CONTROL('P'), { 0, 0, 0, 0 }, f_move, { .m = m_prevline } },
+{ META(','), { 0, 0, 0, 0 }, f_move, { .m = m_prevscr } },
+{ META('.'), { 0, 0, 0, 0 }, f_move, { .m = m_nextscr } },
+{ META('<'), { 0, 0, 0, 0 }, f_move, { .m = m_bof } },
+{ META('>'), { 0, 0, 0, 0 }, f_move, { .m = m_eof } },
+{ META('g'), { 0, 0, 0, 0 }, f_spawn, LINE },
+
+/* Finding and selecting */
+{ CONTROL('S'), { 0, 0, 0, 0 }, f_spawn, FIND },
+{ CONTROL('R'), { 0, 0, 0, 0 }, f_spawn, FINDBW },
+{ META('n'), { 0, 0, 0, 0 }, f_findfw, { 0 } },
+{ META('p'), { 0, 0, 0, 0 }, f_findbw, { 0 } },
+{ CONTROL('X'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtDefault } },
+{ META('x'), { 0, 0, 0, 0 }, f_extsel, { .i = ExtAll } },
+{ CONTROL('G'), { t_sel, 0, 0, 0 }, f_select, { .m = m_stay } },
+{ CONTROL('G'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Selecting } },
+{ CONTROL('O'), { t_sel, 0, 0, 0 }, f_select, { .m = m_tosel } }, /* Swap fsel and fcur */
+{ META('i'), { 0, 0, 0, 0 }, f_toggle, { .i = S_CaseIns } },
+{ META('s'), { t_sel, 0, 0, 0 }, f_pipero, { .v = "(sed 's/$/\\n/;2q' | (read arg && echo find \"$arg\" > ${SANDY_FIFO}))" } },
+{ META('r'), { t_sel, 0, 0, 0 }, f_pipero, { .v = "(sed 's/$/\\n/;2q' | (read arg && echo findbw \"$arg\" > ${SANDY_FIFO}))" } },
+
+/* Text deletion */
+{ CONTROL('D'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('D'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextchar } },
+{ CONTROL('D'), { 0, 0, 0, 0 }, f_select, { .m = m_nextchar } },
+{ CONTROL('?'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('?'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ CONTROL('?'), { 0, 0, 0, 0 }, f_select, { .m = m_prevchar } },
+{ CONTROL('H'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('H'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ CONTROL('H'), { 0, 0, 0, 0 }, f_select, { .m = m_prevchar } },
+{ CONTROL('U'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('U'), { t_bol, t_rw, 0, 0 }, f_delete, { .m = m_prevchar } },
+{ CONTROL('U'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_bol } },
+{ CONTROL('U'), { 0, 0, 0, 0 }, f_select, { .m = m_bol } },
+{ CONTROL('K'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('K'), { t_eol, t_rw, 0, 0 }, f_delete, { .m = m_nextchar } },
+{ CONTROL('K'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_eol } },
+{ CONTROL('K'), { 0, 0, 0, 0 }, f_select, { .m = m_eol } },
+{ CONTROL('W'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ CONTROL('W'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_prevword } },
+{ CONTROL('W'), { 0, 0, 0, 0 }, f_select, { .m = m_prevword } },
+{ META('d'), { t_sel, t_rw, 0, 0 }, f_pipe, TOCLIP },
+{ META('d'), { t_rw, 0, 0, 0 }, f_delete, { .m = m_nextword } },
+{ META('d'), { 0, 0, 0, 0 }, f_select, { .m = m_nextword } },
+
+/* Mark operation */
+{ META(' '), { 0, 0, 0, 0 }, f_mark, { 0 } },
+{ CONTROL('@'), { 0, 0, 0, 0 }, f_move, { .m = m_tomark } },
+
+/* File operations */
+{ CONTROL('Q'), { t_mod, 0, 0, 0 }, f_title, { .v = "WARNING! File not saved! Press META+SHIFT+Q to quit" } },
+{ CONTROL('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
+{ META('q'), { t_mod, 0, 0, 0 }, f_title, { .v = "WARNING! File not saved!" } },
+{ META('q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
+{ META('Q'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Running } },
+{ META('w'), { 0, 0, 0, 0 }, f_save, { 0 } },
+{ META('W'), { 0, 0, 0, 0 }, f_spawn, SAVEAS },
+
+/* Text piping and modification */
+{ CONTROL('\\'),{ 0, 0, 0, 0 }, f_spawn, PIPE },
+{ CONTROL('Y'), { t_rw, 0, 0, 0 }, f_pipe, FROMCLIP },
+{ CONTROL('C'), { t_sel, 0, 0, 0 }, f_pipero, TOCLIP },
+
+/* Undo / Redo */
+{ CONTROL('_'), { t_undo,t_rw, 0, 0 }, f_undo, { .i = +1 } },
+{ CONTROL('^'), { t_redo,t_rw, 0, 0 }, f_undo, { .i = -1 } },
+
+/* Others */
+{ CONTROL('Z'), { 0, 0, 0, 0 }, f_suspend, { 0 } },
+{ CONTROL('L'), { 0, 0, 0, 0 }, f_center, { 0 } },
+{ CONTROL('V'), { 0, 0, 0, 0 }, f_toggle, { .i = S_InsEsc } },
+{ META('R'), { 0, 0, 0, 0 }, f_toggle, { .i = S_Readonly } },
+{ META('S'), { 0, 0, 0, 0 }, f_spawn, SYNTAX },
+};
+
+/* Commands read at the fifo */
+static Command cmds[] = { /* Use only f_ funcs that take Arg.v */
+/* \0, regex, tests, func */
+{NULL, "^find (.*)$", { 0, 0 }, f_findfw },
+{NULL, "^findbw (.*)$", { 0, 0 }, f_findbw },
+{NULL, "^pipe (.*)$", { t_rw, 0 }, f_pipe },
+{NULL, "^pipe (.*)$", { 0, 0 }, f_pipero },
+{NULL, "^save (.*)$", { 0, 0 }, f_save },
+{NULL, "^syntax (.*)$", { 0, 0 }, f_syntax },
+{NULL, "^line (.*)$", { 0, 0 }, f_line },
+{NULL, "^offset (.*)$", { 0, 0 }, f_offset },
+};
+
+/* Syntax color definition */
+#define B "(^| |\t|\\(|\\)|\\[|\\]|\\{|\\}|$)"
+
+static Syntax syntaxes[] = {
+{"c", NULL, "\\.(c(pp|xx)?|h(pp|xx)?|cc)$", { NULL }, {
+ /* HiRed */ "",
+ /* HiGreen */ B"(for|if|while|do|else|case|default|switch|try|throw|catch|operator|new|delete)"B,
+ /* LoGreen */ B"(float|double|bool|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline|((s?size)|((u_?)?int(8|16|32|64|ptr)))_t|class|namespace|template|public|protected|private|typename|this|friend|virtual|using|mutable|volatile|register|explicit)"B,
+ /* HiMag */ B"(goto|continue|break|return)"B,
+ /* LoMag */ "(^#(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma))|"B"\\<[A-Z_][0-9A-Z_]+\\>"B"",
+ /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
+ /* LoRed */ "(\"(\\\\.|[^\"])*\")",
+ /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
+ } },
+
+{"sh", NULL, "\\.sh$", { NULL }, {
+ /* HiRed */ "",
+ /* HiGreen */ "^[0-9A-Z_]+\\(\\)",
+ /* LoGreen */ B"(case|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|shift|then|time|until|while)"B,
+ /* HiMag */ "",
+ /* LoMag */ "\"(\\\\.|[^\"])*\"",
+ /* HiBlue */ "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|!|=|&|\\|)",
+ /* LoRed */ "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?",
+ /* LoBlue */ "#.*$",
+ } },
+
+{"makefile", NULL, "(Makefile[^/]*|\\.mk)$", { NULL }, {
+ /* HiRed */ "",
+ /* HiGreen */ "",
+ /* LoGreen */ "\\$+[{(][a-zA-Z0-9_-]+[})]",
+ /* HiMag */ B"(if|ifeq|else|endif)"B,
+ /* LoMag */ "",
+ /* HiBlue */ "^[^ ]+:",
+ /* LoRed */ "[:=]",
+ /* LoBlue */ "#.*$",
+ } },
+
+{"man", NULL, "\\.[1-9]x?$", { NULL }, {
+ /* HiRed */ "\\.(BR?|I[PR]?).*$",
+ /* HiGreen */ "",
+ /* LoGreen */ "\\.(S|T)H.*$",
+ /* HiMag */ "\\.(br|DS|RS|RE|PD)",
+ /* LoMag */ "(\\.(S|T)H|\\.TP)",
+ /* HiBlue */ "\\.(BR?|I[PR]?|PP)",
+ /* LoRed */ "",
+ /* LoBlue */ "\\\\f[BIPR]",
+ } },
+
+{"vala", NULL, "\\.(vapi|vala)$", { NULL }, {
+ /* HiRed */ B"[A-Z_][0-9A-Z_]+\\>",
+ /* HiGreen */ B"(for|if|while|do|else|case|default|switch|get|set|value|out|ref|enum)"B,
+ /* LoGreen */ B"(uint|uint8|uint16|uint32|uint64|bool|byte|ssize_t|size_t|char|double|string|float|int|long|short|this|base|transient|void|true|false|null|unowned|owned)"B,
+ /* HiMag */ B"(try|catch|throw|finally|continue|break|return|new|sizeof|signal|delegate)"B,
+ /* LoMag */ B"(abstract|class|final|implements|import|instanceof|interface|using|private|public|static|strictfp|super|throws)"B,
+ /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
+ /* LoRed */ "\"(\\\\.|[^\"])*\"",
+ /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
+ } },
+
+{"java", NULL, "\\.java$", { NULL }, {
+ /* HiRed */ B"[A-Z_][0-9A-Z_]+\\>",
+ /* HiGreen */ B"(for|if|while|do|else|case|default|switch)"B,
+ /* LoGreen */ B"(boolean|byte|char|double|float|int|long|short|transient|void|true|false|null)"B,
+ /* HiMag */ B"(try|catch|throw|finally|continue|break|return|new)"B,
+ /* LoMag */ B"(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|this|super|synchronized|throws|volatile)"B,
+ /* HiBlue */ "(\\(|\\)|\\{|\\}|\\[|\\])",
+ /* LoRed */ "\"(\\\\.|[^\"])*\"",
+ /* LoBlue */ "(//.*|/\\*([^*]|\\*[^/])*\\*/|/\\*([^*]|\\*[^/])*$|^([^/]|/[^*])*\\*/)",
+ } },
+};
+
+/* Colors */
+static const short fgcolors[LastFG] = {
+ [DefFG] = -1,
+ [CurFG] = (HILIGHT_CURRENT?COLOR_BLACK:-1),
+ [SelFG] = COLOR_BLACK,
+ [SpcFG] = COLOR_WHITE,
+ [CtrlFG] = COLOR_RED,
+ [Syn0FG] = COLOR_RED,
+ [Syn1FG] = COLOR_GREEN,
+ [Syn2FG] = COLOR_GREEN,
+ [Syn3FG] = COLOR_MAGENTA,
+ [Syn4FG] = COLOR_MAGENTA,
+ [Syn5FG] = COLOR_BLUE,
+ [Syn6FG] = COLOR_RED,
+ [Syn7FG] = COLOR_BLUE,
+};
+
+static const int colorattrs[LastFG] = {
+ [DefFG] = 0,
+ [CurFG] = 0,
+ [SelFG] = 0,
+ [SpcFG] = A_DIM,
+ [CtrlFG] = A_DIM,
+ [Syn0FG] = A_BOLD,
+ [Syn1FG] = A_BOLD,
+ [Syn2FG] = 0,
+ [Syn3FG] = A_BOLD,
+ [Syn4FG] = 0,
+ [Syn5FG] = A_BOLD,
+ [Syn6FG] = 0,
+ [Syn7FG] = 0,
+};
+
+static const int bwattrs[LastFG] = {
+ [DefFG] = 0,
+ [CurFG] = 0,
+ [SelFG] = A_REVERSE,
+ [SpcFG] = A_DIM,
+ [CtrlFG] = A_DIM,
+ [Syn0FG] = A_BOLD,
+ [Syn1FG] = A_BOLD,
+ [Syn2FG] = A_BOLD,
+ [Syn3FG] = A_BOLD,
+ [Syn4FG] = A_BOLD,
+ [Syn5FG] = A_BOLD,
+ [Syn6FG] = A_BOLD,
+ [Syn7FG] = A_BOLD,
+};
+
+static const short bgcolors[LastBG] = {
+ [DefBG] = -1,
+ [CurBG] = (HILIGHT_CURRENT?COLOR_CYAN:-1),
+ [SelBG] = COLOR_YELLOW,
+};
+
diff --git a/sandy.1 b/sandy.1
@@ -3,7 +3,7 @@
sandy \- simple ncurses text editor
.SH SYNOPSIS
.B sandy
-.RB [ \-hru ]
+.RB [ \-hrS ]
.RB [ \-t\ tabstop ]
.RB [ \-s\ syntax ]
.RB [File | -]
@@ -20,16 +20,16 @@ Opens the selected file read-only.
.B \-s syntax
Manually choose the syntax colors.
.TP
+.B \-S
+Use no syntax colors at all.
+.TP
.B \-t tabstop
Use selected tabstop in characters.
.TP
-.B \-u
-Reverse UTF-8 behavior. UTF-8 processing is on by default.
-.TP
.B \-v
Prints version information to standard output, then exits.
.SH USAGE
-The default key-bindings try to stick to the "emacs-mode" present in modern shells (specifically mksh(1)), which actually differs a bit from what emacs(1) does. External input is handled using dmenu(1) if the DISPLAY environment variable is set, read using sh(1) otherwise. Similarly, X11 clipboard interaction occurs through xsel(1) if DISPLAY is set, using a hidden file in the HOME directory otherwise.
+The default key-bindings try to stick to the "emacs-mode" present in modern shells (specifically mksh(1)), ignoring binding chains and replacing or removing all meta bindings. External input is handled using dmenu(1) if the DISPLAY environment variable is set, read using sh(1) otherwise. Similarly, X11 clipboard interaction occurs through xsel(1) if DISPLAY is set, using a hidden file in the HOME directory otherwise.
.SS Cursor movement
Cursor movement bindings can also be used to select text by pressing Ctrl\-g to start a selection (see below)
.TP
@@ -45,67 +45,46 @@ Move cursor to next line.
.B Ctrl\-p or Up
Move cursor to previous line.
.TP
-.B Meta\-f or Shift\-Right
+.B Shift\-Right
Move cursor to the end of this or next word.
.TP
-.B Meta\-b or Shift\-Left
+.B Shift\-Left
Move cursor to the start of this or previous word.
.TP
.B Ctrl\-a or Home
-Move cursor to the beginning of the current line.
+Move cursor to the beginning of the current line or, if already there, one full screen up.
.TP
.B Ctrl\-e or End
-Move cursor to the end of the current line.
+Move cursor to the end of the current line or, if already there, one full screen down.
.TP
-.B Meta\-, or Prev
+.B Prev
Move cursor one full screen up.
.TP
-.B Meta\-. or Next
+.B Next
Move cursor one full screen down.
.TP
-.B Meta\-< or Shift-Home
+.B Shift-Home
Move cursor to the beginning of the file.
.TP
-.B Meta\-> or Shift-End
+.B Shift-End
Move cursor to the end of the file.
-.TP
-.B Meta\-g
-Prompt for a line number and go there.
.SS Finding and selecting
Text is searched and selected using POSIX regular expressions
.TP
.B Ctrl\-s
-Prompt for a search regex forward.
-.TP
-.B Meta\-s
-Search forward using the (first line of the) current selection as a regex.
+Prompt for a search regex forward or, if text is already selected, repeat last search forward.
.TP
.B Ctrl\-r
-Prompt for a search regex backwards.
-.TP
-.B Meta\-r
-Search backwards using the (first line of the) current selection as a regex.
-.TP
-.B Meta\-n
-Find next occurrence of last search forward.
-.TP
-.B Meta\-p
-Find next occurrence of last search backwards.
+Prompt for a search regex backwards or, if text is already selected, repeat last search backwards.
.TP
-.B Ctrl\-x
+.B Ctrl\-]
Extend selection to word, line, or full file from current.
.TP
-.B Meta\-x
-Select full file.
-.TP
.B Ctrl\-g
Cancel selection, or start selecting text manually.
.TP
.B Ctrl\-o
-Move to the opposite size of the selection, usually to change it.
-.TP
-.B Meta\-i
-Toggle case (in)sensitive search.
+Move to the opposite size of the selection, usually to change it (move to the mark if no selection).
.SS Deleting
If any text is selected, any of these bindings deletes it and, unless Backspace or Delete, moves it to the clipboard. Otherwise they behave as per this text. In read-only mode, these bindings select text instead.
.TP
@@ -124,29 +103,23 @@ Delete to end of line or join with next line.
.B Ctrl\-w
Delete to beginning of word or previous word.
.TP
-.B Meta\-d
+.B Ctrl\-c
Delete to end of word or next word.
.SS Mark
The mark is a single fixed position in the file that can be used as bookmark.
.TP
-.B Meta\-Space
+.B Ctrl\-Space or Ctrl\-\@
Set mark in current position.
.TP
-.B Ctrl\-` or Ctrl\-Space
-Go to mark. Prepend with Ctrl\-g to select to mark.
+.B Ctrl\-o
+Go to mark (unless there is a selection). Prepend with Ctrl\-g to select to mark.
.SS File operations
.TP
-.B Ctrl-q or Meta\-q
-Quit if the file has not been modified.
+.B Ctrl-q
+Quit if the file has not been modified. Repeat to force quit.
.TP
-.B Meta\-Shift\-q
-Quit unconditionally.
-.TP
-.B Meta\-w
-Write changes to disk.
-.TP
-.B Meta\-Shift\-w
-Prompt for a different filename to save the file as.
+.B Ctrl\-x
+Write changes to disk or quit if unmodified.
.SS Text operations
These operations affect the selected text if any.
.TP
@@ -156,7 +129,7 @@ Prompt for a command to pipe text through.
.B Ctrl\-y or Shift-Insert
Replace (paste, yank) with text from the clipboard.
.TP
-.B Ctrl\-c or Insert
+.B Ctrl\-t or Insert
Copy to the clipboard.
.TP
.B Shift-Delete
@@ -164,16 +137,16 @@ Copy to the clipboard and delete.
.SS Other
.TP
.B Ctrl\-l
-Center screen in current line.
+Center screen in current line, refresh.
.TP
.B Ctrl\-v
Insert next character as-is.
.TP
-.B Meta\-Shift\-r
-Toggle read-only status for file.
+.B Ctrl\-z
+Suspend editor to the shell.
.TP
-.B Meta\-Shift\-s
-Prompt for a file syntax to use.
+.B Ctrl\-\[ or Escape
+Prompt for a command or line number to go to.
.SH SEE ALSO
.BR dmenu(1)
.BR xsel(1)
diff --git a/sandy.c b/sandy.c
@@ -116,6 +116,7 @@ enum { /* To use in statusflags */
S_DirtyScr = 1<<6,
S_DirtyDown = 1<<7,
S_NeedResize = 1<<8,
+ S_Warned = 1<<9,
};
enum { /* To use in Undo.flags */
@@ -158,7 +159,7 @@ 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 int statusflags=S_Running; /* Status flags, very important, OR'd (see enums above) */
+static long statusflags=S_Running; /* Status flags, very important, OR'd (see enums above) */
static int cols, lines; /* Ncurses: to use instead of COLS and LINES, wise */
static mmask_t defmmask=ALL_MOUSE_EVENTS; /* Ncurses: mouse event mask */
@@ -182,9 +183,9 @@ static void f_select(const Arg*);
static void f_spawn(const Arg*);
static void f_suspend(const Arg*);
static void f_syntax(const Arg *arg);
-static void f_title(const Arg *arg);
static void f_toggle(const Arg *arg);
static void f_undo(const Arg*);
+static void f_warn(const Arg *arg);
/* i_* funcions are called from inside the main code only */
static Filepos i_addtext(char*, Filepos);
@@ -226,6 +227,7 @@ static bool t_rw(void);
static bool t_redo(void);
static bool t_sel(void);
static bool t_undo(void);
+static bool t_warn(void);
/* m_ functions represent a cursor movement and can be passed in an Arg */
static Filepos m_bof(Filepos);
@@ -462,12 +464,6 @@ f_syntax(const Arg *arg) {
setenv(envs[EnvSyntax], "none", 1);
}
-void /* Set screen title to arg->v, useful for debug or errors */
-f_title(const Arg *arg) {
- tmptitle=(char*)arg->v;
- statusflags&=~S_Selecting;
-}
-
void /* Toggle the arg->i statusflag. Careful with this one! */
f_toggle(const Arg *arg) {
statusflags^=(char)arg->i;
@@ -515,6 +511,13 @@ f_undo(const Arg *arg) {
statusflags|=S_Modified;
}
+void /* Set screen title to arg->v, set warning bit */
+f_warn(const Arg *arg) {
+ tmptitle=(char*)arg->v;
+ statusflags|=S_Warned;
+}
+
+
/* I_* FUNCTIONS
Called internally from the program code */
@@ -1356,6 +1359,7 @@ i_update(void) {
if(tmptitle)
strncpy(title, tmptitle, BUFSIZ);
else {
+ statusflags&=~S_Warned; /* Reset warning */
snprintf(buf, 4, "%ld%%", (100*ncur)/nlst);
snprintf(title, BUFSIZ, "%s [%s]%s%s%s%s %ld,%d %s",
(filename == NULL?"<No file>":filename),
@@ -1599,6 +1603,11 @@ t_undo(void) {
return (undos != NULL);
}
+bool /* TRUE if we have warned the file is modified */
+t_warn(void) {
+ return (statusflags & S_Warned);
+}
+
int /* main() starts everything else */
main(int argc, char **argv){