aboutsummaryrefslogtreecommitdiff
path: root/config.def.h
blob: b9f3f18949f5db6b26c3144966d08c56ac69b858 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
    BB Key Bindings

    User-defined key bindings go in config.h, which is created by running `make`
    (config.def.h is for keeping the defaults around, just in case)

    The basic structure is:
        <list of keys to bind>
        <program to run>
        <description> (for the help menu)
        <flags> (whether to run in a full terminal window or silently, etc.)

    When the scripts are run, the following values are provided as environment variables:

        $@ (the list of arguments): the full paths of the selected files
        $BBCURSOR: the full name of the file under the cursor
        $BB_DEPTH: the number of `bb` instances deep (in case you want to run a
            shell and have that shell print something special in the prompt)
        $BBCMD: a file to which `bb` commands can be written (used internally)

    In order to modify bb's internal state, you can call `bb +cmd`, where "cmd"
    is one of the following commands (or a unique prefix of one):

        .:[01]                    Whether to show "." in each directory
        ..:[01]                   Whether to show ".." in each directory
        cd:<path>                 Navigate to <path>
        columns:<columns>         Change which columns are visible, and in what order
        deselect:<filename>       Deselect <filename>
        dotfiles:[01]             Whether dotfiles are visible
        goto:<filename>           Move the cursor to <filename> (changing directory if needed)
        interleave:[01]           Whether or not directories should be interleaved with files in the display
        jump:<key>                Jump to the mark associated with <key>
        mark:<key>[=<path>]       Associate <key> with <path> (or current dir, if blank)
        move:<num*>               Move the cursor a numeric amount
        quit                      Quit bb
        refresh                   Refresh the file listing
        scroll:<num*>             Scroll the view a numeric amount
        select:<filename>         Select <filename>
        sort:([+-]method)+        List of sortings (if equal on one, move to the next)
        spread:<num*>             Spread the selection state at the cursor
        toggle:<filename>         Toggle the selection status of <filename>

    Internally, bb will write the commands (NUL terminated) to $BBCMD, if
    $BBCMD is set, and read the file when file browsing resumes. These commands
    can also be passed to bb at startup, and will run immediately.
    E.g. `bb +col:n +sort:r .` will launch `bb` only showing the name column, randomly sorted.

    *Note: for numeric-based commands (like scroll), the number can be either
        an absolute value or a relative value (starting with '+' or '-'), and/or
        a percent (ending with '%'). Scrolling and moving, '%' means percent of
        screen height, and '%n' means percent of number of files (e.g. +50% means
        half a screen height down, and 100%n means the last file)

 */
#include "bterm.h"

// Configurable options:
#define KEY_DELAY 50
#define SCROLLOFF MIN(5, (termheight-4)/2)

#define QUOTE(s) #s

#define CMDFILE_FORMAT "/tmp/bb.XXXXXX"

#define SORT_INDICATOR  "↓"
#define RSORT_INDICATOR "↑"
#define SELECTED_INDICATOR " \033[31;7m \033[0m"
#define NOT_SELECTED_INDICATOR "  "

#define TITLE_COLOR      "\033[37;1m"
#define NORMAL_COLOR     "\033[37m"
#define CURSOR_COLOR     "\033[43;30;1m"
#define LINK_COLOR       "\033[35m"
#define DIR_COLOR        "\033[34m"
#define EXECUTABLE_COLOR "\033[31m"

#define PAUSE " read -n1 -p '\033[2mPress any key to continue...\033[0m\033[?25l'"

#ifndef FUZZY
#define FUZZY(prompt) "fzy --prompt=\"" prompt "\""
#endif

#ifndef ASK
#ifdef ASKECHO
#define ASK(var, prompt) var "=\"$(" ASKECHO(prompt) ")\""
#else
#define ASK(var, prompt) "read -p \"" prompt "\" " var
#endif
#endif

#ifndef ASKECHO
#define ASKECHO(prompt) ASK("REPLY", prompt) " && echo \"$REPLY\""
#endif

#define NORMAL_TERM     (1<<0)
#define AT_CURSOR       (1<<1)

#define MAX_REBINDINGS 8

typedef struct {
    int keys[MAX_REBINDINGS+1];
    const char *command;
    const char *description;
    int flags;
} binding_t;

typedef struct {
    int width;
    const char *name;
} column_t;

// These commands will run at startup (before command-line arguments)
extern const char *startupcmds[];
extern const column_t columns[128];

const char *startupcmds[] = {
    //////////////////////////////////////////////
    // User-defined startup commands can go here
    //////////////////////////////////////////////
    // Set some default marks:
    "+mark:0", "+mark:~=~", "+mark:h=~", "+mark:/=/", "+mark:c=~/.config",
    "+mark:l=~/.local", "+mark:s=<selection>",
    // Default column and sorting options:
    "+sort:+n", "+col:*smpn", "+..",
    NULL, // NULL-terminated array
};

// Column widths:
const column_t columns[128] = {
    ['*'] = {2,  "*"},
    ['a'] = {21, "      Accessed"},
    ['c'] = {21, "      Created"},
    ['m'] = {21, "      Modified"},
    ['n'] = {40, "Name"},
    ['p'] = {5,  "Permissions"},
    ['r'] = {2,  "Random"},
    ['s'] = {9,  "Size"},
};

#ifdef __APPLE__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#endif
extern binding_t bindings[];
#define B(s) "\033[1m" s "\033[22m"
binding_t bindings[] = {
    /*************************************************************************
     * User-defined custom scripts can go here
     * Format is: {{keys}, "script", "help text", flags}
     *
     * For longer scripts you can use QUOTE(...) instead of "..." and all
     * quotation marks and newlines and backslashes will get escaped properly.
     *
     * Please note that these are sh scripts, not bash scripts, so bash-isms
     * won't work unless you make your script use `bash -c "<your script>"`
     ************************************************************************/
    {{'?', KEY_F1}, "bb -b | $PAGER -r", B("Help")" menu", NORMAL_TERM},
    {{'q', 'Q'}, "+quit", B("Quit")},
    {{'j', KEY_ARROW_DOWN}, "+move:+1", B("Next")" file"},
    {{'k', KEY_ARROW_UP}, "+move:-1", B("Previous")" file"},
    {{'h', KEY_ARROW_LEFT}, "+cd:..", B("Parent")" directory"},
    {{'l', KEY_ARROW_RIGHT}, "test -d \"$BBCURSOR\" && bb \"+cd:$BBCURSOR\"", B("Enter")" a directory"},
    {{'\r', KEY_MOUSE_DOUBLE_LEFT},
#ifdef __APPLE__
        QUOTE(
            if test -d "$BBCURSOR"; then bb "+cd:$BBCURSOR";
            elif test -x "$BBCURSOR"; then "$BBCURSOR"; read -p 'Press any key to continue...' -n1;
            elif file -bI "$BBCURSOR" | grep '^\(text/\|inode/empty\)' >/dev/null; then $EDITOR "$BBCURSOR";
            else open "$BBCURSOR"; fi
        )/*ENDQUOTE*/,
#else
        QUOTE(
            if test -d "$BBCURSOR"; then bb "+cd:$BBCURSOR";
            elif file -bi "$BBCURSOR" | grep '^\(text/\|inode/empty\)' >/dev/null; then $EDITOR "$BBCURSOR";
            else xdg-open "$BBCURSOR"; fi
        )/*ENDQUOTE*/,
#endif
        B("Open")" file/directory", NORMAL_TERM},
    {{' ','v','V'}, "+toggle", B("Toggle")" selection"},
    {{KEY_ESC}, "+deselect:*", B("Clear")" selection"},
    {{'e'}, "$EDITOR \"$@\"", B("Edit")" file in $EDITOR", NORMAL_TERM},
    {{KEY_CTRL_F}, "bb \"+g:`find | "FUZZY("Find: ")"`\" +r", B("Fuzzy search")" for file"},
    {{'/'}, "bb \"+g:`ls -a | " FUZZY("Pick: ") "`\" +r", B("Fuzzy pick")" file"},
    {{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+de:*' +r", B("Delete")" files", AT_CURSOR},
    {{'D'}, "rm -rf \"$@\"; bb '+de:*' +r", B("Delete")" files (without confirmation)"},
    {{'M'}, "mv -i \"$@\" .; bb '+de:*' +r; for f; do bb \"+sel:`pwd`/`basename \"$f\"`\"; done",
        B("Move")" files to current directory"},
    {{'c'}, "cp -i \"$@\" .; bb +r", B("Copy")" files to current directory"},
    {{'C'}, "bb '+de:*'; for f; do cp \"$f\" \"$f.copy\" && bb \"+sel:$f.copy\"; done; bb +r", B("Clone")" files"},
    {{'n'}, ASK("name", "New file: ")" && touch \"$name\"; bb +r \"+goto:$name\"", B("New file")},
    {{'N'}, ASK("name", "New dir: ")" && mkdir \"$name\"; bb +r \"+goto:$name\"", B("New directory")},
    {{KEY_CTRL_G}, "bb \"+cd:`" ASKECHO("Go to directory: ") "`\" +r", B("Go to")" directory"},
    {{'|'}, ASK("cmd", "|") " && printf '%s\\n' \"$@\" | sh -c \"$cmd\"; " PAUSE "; bb +r",
        B("Pipe")" selected files to a command"},
    {{':'}, "sh -c \"`" ASKECHO(":") "`\" -- \"$@\"; " PAUSE "; bb +refresh",
        B("Run")" a command"},
    {{'>'}, "$SHELL", "Open a "B("shell"), NORMAL_TERM},
    {{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set "B("mark")},
    {{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", B("Jump")" to mark"},

    {{'r'},
        QUOTE(
            bb '+deselect:*' +refresh; 
            for f; do 
                if renamed="$(dirname "$f")/$(ask --initial="$(basename "$f")" 'Rename: ')" &&
                    test "$f" != "$renamed" && mv -i "$f" "$renamed"; then 
                    test $BBSELECTED && bb "+select:$renamed"; 
                elif test $BBSELECTED; then bb "+select:$f"; fi 
            done)/*ENDQUOTE*/,
        B("Rename")" files", AT_CURSOR},

    {{'R'},
        QUOTE(
            if patt="`ask --initial='s/' 'Rename pattern: '`"; then true; else bb +r; exit; fi;
            if sed -E "$patt" </dev/null; then true; else read -p 'Press any key to continue...' -n1; bb +r; exit; fi;
            bb '+deselect:*' +refresh; 
            for f; do 
                renamed="`dirname "$f"`/`basename "$f" | sed -E "$patt"`"; 
                if test "$f" != "$renamed" && mv -i "$f" "$renamed"; then 
                    test $BBSELECTED && bb "+select:$renamed"; 
                elif test $BBSELECTED; then bb "+select:$f"; fi 
            done)/*ENDQUOTE*/,
        B("Regex rename")" files", AT_CURSOR},
    
    {{'P'},
        QUOTE(
            patt=`ask 'Select pattern: '` &&
            for f in *; do echo "$f" | grep "$patt" >/dev/null 2>/dev/null && bb "+sel:$f"; done
        )/*ENDQUOTE*/,
        B("Regex select")" files"},

    {{'J'}, "+spread:+1", B("Spread")" selection down"},
    {{'K'}, "+spread:-1", B("Spread")" selection up"},
    {{'b'}, "bb \"+`"ASKECHO("bb +")"`\"", "Run a "B("bb command")},
    {{'s'},
        ("sort=\"$(ask -q 'Sort (n)ame (s)ize (m)odification (c)reation (a)ccess (r)andom "
         "(p)ermissions: ' n s m c a r p)\""
         "&& bb \"+sort:+$sort\""),
        B("Sort")" by..."},

    {{'#'}, "bb \"+col:`"ASKECHO("Set columns: ")"`\"", "Set "B("columns")},
    {{'.'}, "bb +dotfiles", "Toggle "B("dotfiles")},
    {{'g', KEY_HOME}, "+move:0", "Go to "B("first")" file"},
    {{'G', KEY_END}, "+move:100%n", "Go to "B("last")" file"},
    {{KEY_F5, KEY_CTRL_R}, "+refresh", B("Refresh")},
    {{KEY_CTRL_A}, "+select:*", B("Select all")" files in current directory"},
    {{KEY_PGDN}, "+scroll:+100%", B("Page down")},
    {{KEY_PGUP}, "+scroll:-100%", B("Page up")},
    {{KEY_CTRL_D}, "+scroll:+50%", B("Half page down")},
    {{KEY_CTRL_U}, "+scroll:-50%", B("Half page up")},
    {{KEY_MOUSE_WHEEL_DOWN}, "+scroll:+3", B("Scroll down")},
    {{KEY_MOUSE_WHEEL_UP}, "+scroll:-3", B("Scroll up")},
    {{0}}, // Array must be 0-terminated
};
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif

// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1