sandy

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

commit b55e85b730136a073724c9f149ebe174ce27eefa
parent de8e7863fbf29ee41290753a3657f96a675e4f23
Author: Rafael Garcia <rafael.garcia.gallego@gmail.com>
Date:   Wed, 10 Aug 2011 03:33:07 +0200

Autoindent is back (start sandy with -a), now using a awk one liner rather than hardcode in C; can also be set/unset. Helper functions moved to the config file for now.
Diffstat:
README | 1+
TODO | 1-
config.def.h | 29+++++++++++++++++++++++++++++
sandy.1 | 5++++-
sandy.c | 28+++++++++++-----------------
5 files changed, 45 insertions(+), 19 deletions(-)

diff --git a/README b/README @@ -28,6 +28,7 @@ Use the following syntax: sandy [-r] [-S | -s SYNTAX] [-t TABSTOP] [File] Where: +-a starts with autoindent -r opens the file read-only -S use no syntax colors at all. -s SYNTAX lets you specify the syntax colors for this file diff --git a/TODO b/TODO @@ -2,7 +2,6 @@ In no particular order, at sandy.c: - Update manpage - Create m_*vline - Bracket matching? -- Autoindenting? - Number modifier? - Smart end? is it really needed? - BUG: high CPU usage on continuous resize diff --git a/config.def.h b/config.def.h @@ -20,6 +20,11 @@ static const char spcstr[2] = { ' ', 0 }; static const char nlstr[1] = { 0 }; #endif +/* Helper config functions, not used in main code */ +static void f_moveb(const Arg*); +static void f_pipeb(const Arg*); +static void f_pipelines(const Arg*); + /* Args to f_spawn */ #define PROMPT(prompt, default, cmd) { .v = (const char *[]){ "/bin/sh", "-c", \ "dmenu -v >/dev/null 2>&1 || DISPLAY=\"\";"\ @@ -99,12 +104,14 @@ static const Key stdkeys[] = { { .keyv.c = CONTROL('H'), { t_sel, t_rw, 0, 0 }, f_delete, { .m = m_tosel } }, { .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_pipeb, { .v = "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 }'" } } , { .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 = META('l'), { t_sel, t_rw, 0, 0 }, f_pipe, { .v = "tr [A-Z] [a-z]" } }, +{ .keyv.c = CONTROL('M'), { t_rw, t_ai, 0, 0 }, f_pipeb, { .v = "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 }'" } } , { .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 } }, @@ -156,6 +163,7 @@ static const Command cmds[] = { /* if(arg == 0) arg.v=regex_match */ {"^offset (.*)$", { 0, 0, 0 }, f_offset, { 0 } }, {"^set icase$", { 0, 0, 0 }, f_toggle, { .i = S_CaseIns } }, {"^set ro$", { 0, 0, 0 }, f_toggle, { .i = S_Readonly } }, +{"^set ai$", { 0, 0, 0 }, f_toggle, { .i = S_AutoIndent } }, {"^q$", { t_mod, 0, 0 }, f_toggle, { .i = S_Warned } }, {"^q$", { 0, 0, 0 }, f_toggle, { .i = S_Running } }, {"^q!$", { 0, 0, 0 }, f_toggle, { .i = S_Running } }, @@ -291,3 +299,24 @@ static const short bgcolors[LastBG] = { [SelBG] = COLOR_YELLOW, }; +/* Helper config functions implementation */ +void /* Move cursor as per arg->m, then cancel selection */ +f_moveb(const Arg *arg) { + fsel=fcur=arg->m(fcur); +} + +void /* Pipe selection from bol, then cancel selection */ +f_pipeb(const Arg *arg) { + i_sortpos(&fsel, &fcur); + fsel.o=0; + f_pipe(arg); + fsel=fcur; +} + +void /* Pipe full lines including the selection */ +f_pipelines(const Arg *arg) { + f_extsel(&(const Arg){ .i = ExtLines }); + i_pipetext(arg->v); + statusflags|=S_Modified; + lastaction=LastPipe; +} diff --git a/sandy.1 b/sandy.1 @@ -3,7 +3,7 @@ sandy \- simple ncurses text editor .SH SYNOPSIS .B sandy -.RB [ \-hruS ] +.RB [ \-ahruS ] .RB [ \-t .IR tabstop ] .RB [ \-s @@ -16,6 +16,9 @@ commands. A small degree of external control can be achieved by writing to a named pipe. .SH OPTIONS .TP +.B \-a +Autoindents text while editing. +.TP .B \-h Prints usage information to stderr, then exits. .TP diff --git a/sandy.c b/sandy.c @@ -127,6 +127,7 @@ enum { /* To use in statusflags */ S_NeedResize = 1<<7, S_Warned = 1<<8, S_GroupUndo = 1<<9, + S_AutoIndent = 1<<10, }; enum { /* To use in Undo.flags */ @@ -185,10 +186,8 @@ static void f_insert(const Arg*); static void f_line(const Arg*); static void f_mark(const Arg*); static void f_move(const Arg*); -static void f_moveb(const Arg*); static void f_offset(const Arg*); static void f_pipe(const Arg*); -static void f_pipelines(const Arg*); static void f_pipero(const Arg*); static void f_repeat(const Arg*); static void f_save(const Arg*); @@ -233,6 +232,7 @@ static void i_usage(void); static bool i_writefile(char*); /* t_* functions to know whether to process an action or keybinding */ +static bool t_ai(void); static bool t_bol(void); static bool t_eol(void); static bool t_mod(void); @@ -363,11 +363,6 @@ f_move(const Arg *arg) { fcur=arg->m(fcur); } -void /* Move cursor as per arg->m, then copy at selection */ -f_moveb(const Arg *arg) { - fsel=fcur=arg->m(fcur); -} - void /* Got to atoi(arg->v) position in the current line */ f_offset(const Arg *arg) { fcur.o=atoi(arg->v); @@ -382,14 +377,6 @@ f_pipe(const Arg *arg) { lastaction=LastPipe; } -void /* Pipe full lines including the selection through arg->v external command. Your responsibility: call only if t_rw() */ -f_pipelines(const Arg *arg) { - f_extsel(&(const Arg){ .i = ExtLines }); - i_pipetext(arg->v); - statusflags|=S_Modified; - lastaction=LastPipe; -} - void /* Pipe selection through arg->v external command but do not update text on screen */ f_pipero(const Arg *arg) { char oldsf=statusflags; @@ -1405,12 +1392,13 @@ i_update(void) { else { statusflags&=~S_Warned; /* Reset warning */ snprintf(buf, 4, "%ld%%", (100*ncur)/nlst); - snprintf(title, BUFSIZ, "%s [%s]%s%s%s %ld,%d %s", + snprintf(title, BUFSIZ, "%s [%s]%s%s%s%s %ld,%d %s", (filename == NULL?"<No file>":filename), (syntx>=0 ? syntaxes[syntx].name : "none"), (t_mod()?"[+]":""), (!t_rw()?"[RO]":""), (statusflags&S_CaseIns?"[icase]":""), + (statusflags&S_AutoIndent?"[ai]":""), ncur, (int)fcur.o, (scrline==fstline? (nlst<lines3?"All":"Top"): @@ -1439,7 +1427,7 @@ i_update(void) { void /* Print help, die */ i_usage(void) { fputs("sandy - simple editor\n", stderr); - i_die("usage: sandy [-r] [-u] [-t TABSTOP] [-s SYNTAX] [file | -]\n"); + i_die("usage: sandy [-a] [-r] [-u] [-t TABSTOP] [-s SYNTAX] [file | -]\n"); } bool /* Write buffer to disk */ @@ -1625,6 +1613,10 @@ 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); +} bool /* TRUE at the beginning of line */ t_bol(void) { @@ -1678,6 +1670,8 @@ main(int argc, char **argv){ for(i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != '\0'; i++) { if(!strcmp(argv[i], "-r")) { statusflags|=S_Readonly; + } else if(!strcmp(argv[i], "-a")) { + statusflags|=S_AutoIndent; } else if(!strcmp(argv[i], "-t")) { if(++i < argc) { tabstop=atoi(argv[i]);