aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bb.c223
-rw-r--r--config.def.h91
2 files changed, 183 insertions, 131 deletions
diff --git a/bb.c b/bb.c
index 194ff90..454215a 100644
--- a/bb.c
+++ b/bb.c
@@ -24,7 +24,7 @@
#include "config.h"
#include "bterm.h"
-#define BB_VERSION "0.11.3"
+#define BB_VERSION "0.12.0"
#ifndef PATH_MAX
#define PATH_MAX 4096
@@ -142,6 +142,7 @@ static void set_scroll(bb_t *bb, int i);
static entry_t* load_entry(bb_t *bb, const char *path);
static void remove_entry(entry_t *e);
static void sort_files(bb_t *bb);
+static void normalize_path(const char *root, const char *path, char *pbuf);
static int cd_to(bb_t *bb, const char *path);
static void populate_files(bb_t *bb, const char *path);
static bb_result_t execute_cmd(bb_t *bb, const char *cmd);
@@ -380,6 +381,11 @@ void render(bb_t *bb)
fputs_escaped(tty_out, bb->path, color);
fputs(" \033[K\033[0m", tty_out);
+ static const char *help = "Press '?' to see key bindings ";
+ move_cursor(tty_out, MAX(0, termwidth - (int)strlen(help)), 0);
+ fputs(help, tty_out);
+ fputs("\033[K\033[0m", tty_out);
+
// Columns
move_cursor(tty_out, 0, 1);
fputs("\033[0;44;30m\033[K", tty_out);
@@ -442,6 +448,7 @@ void render(bb_t *bb)
entry_t *entry = files[i];
if (i == bb->cursor) fputs(CURSOR_COLOR, tty_out);
+ int use_fullname = strcmp(bb->path, "<selection>") == 0;
int x = 0;
for (int col = 0; bb->columns[col]; col++) {
fprintf(tty_out, "\033[%d;%dH\033[K", y+1, x+1);
@@ -500,8 +507,9 @@ void render(bb_t *bb)
if (i == bb->cursor) strcat(color, CURSOR_COLOR);
fputs(color, tty_out);
- if (entry->no_esc) fputs(entry->name, tty_out);
- else entry->no_esc |= !fputs_escaped(tty_out, entry->name, color);
+ char *name = use_fullname ? entry->fullname : entry->name;
+ if (entry->no_esc) fputs(name, tty_out);
+ else entry->no_esc |= !fputs_escaped(tty_out, name, color);
if (E_ISDIR(entry)) fputs("/", tty_out);
@@ -531,11 +539,15 @@ void render(bb_t *bb)
fputs(" \033[K\033[0m", tty_out); // Reset color and attributes
}
- static const char *help = "Press '?' to see key bindings ";
- move_cursor(tty_out, 0, termheight - 1);
- fputs("\033[K", tty_out);
- move_cursor(tty_out, MAX(0, termwidth - (int)strlen(help)), termheight - 1);
- fputs(help, tty_out);
+ move_cursor(tty_out, MAX(0, termwidth - 14), termheight - 1);
+ if (bb->firstselected) {
+ int n = 0;
+ for (entry_t *s = bb->firstselected; s; s = s->selected.next) ++n;
+ fprintf(tty_out, "\033[41;30m% 4d Selected \033[0m", n);
+ } else {
+ fputs("\033[0m\033[K", tty_out);
+ }
+
lastcursor = bb->cursor;
lastscroll = bb->scroll;
fflush(tty_out);
@@ -673,21 +685,20 @@ void set_cursor(bb_t *bb, int newcur)
if (newcur > bb->nfiles - 1) newcur = bb->nfiles - 1;
if (newcur < 0) newcur = 0;
bb->cursor = newcur;
- if (bb->nfiles <= termheight - 4)
+ if (bb->nfiles <= termheight - 4) {
+ bb->scroll = 0;
return;
+ }
if (newcur < bb->scroll + SCROLLOFF)
bb->scroll = newcur - SCROLLOFF;
else if (newcur > bb->scroll + (termheight-4) - SCROLLOFF)
bb->scroll = newcur - (termheight-4) + SCROLLOFF;
- if (bb->nfiles <= termheight - 4) {
- bb->scroll = 0;
- } else {
- int max_scroll = bb->nfiles - (termheight-4) - 1;
- if (max_scroll < 0) max_scroll = 0;
- if (bb->scroll > max_scroll) bb->scroll = max_scroll;
- if (bb->scroll < 0) bb->scroll = 0;
- }
+
+ int max_scroll = bb->nfiles - (termheight-4) - 1;
+ if (max_scroll < 0) max_scroll = 0;
+ if (bb->scroll > max_scroll) bb->scroll = max_scroll;
+ if (bb->scroll < 0) bb->scroll = 0;
}
/*
@@ -799,43 +810,63 @@ void sort_files(bb_t *bb)
bb->dirty = 1;
}
-int cd_to(bb_t *bb, const char *path)
+/*
+ * Prepend `root` to relative paths, replace "~" with $HOME, remove ".",
+ * replace "/foo/baz/../" with "/foo/", and make sure there's a trailing
+ * slash. The normalized path is stored in `normalized`.
+ */
+void normalize_path(const char *root, const char *path, char *normalized)
{
- char pbuf[PATH_MAX];
if (path[0] == '~' && (path[1] == '\0' || path[1] == '/')) {
char *home;
- if (!(home = getenv("HOME")))
- return BB_INVALID;
- strcpy(pbuf, home);
- strcat(pbuf, path+1);
+ if (!(home = getenv("HOME"))) return;
+ strcpy(normalized, home);
+ ++path;
} else if (path[0] == '/') {
+ normalized[0] = '\0';
+ } else {
+ strcpy(normalized, root);
+ }
+ strcat(normalized, path);
+
+ if (normalized[strlen(normalized)-1] != '/')
+ strcat(normalized, "/");
+
+ char *src = normalized, *dest = normalized;
+ while (*src) {
+ if (strncmp(src, "/./", 3) == 0) {
+ src += 2;
+ } else if (strncmp(src, "/../", 4) == 0) {
+ src += 3;
+ while (dest > normalized && *(--dest) != '/')
+ ;
+ }
+ *(dest++) = *(src++);
+ }
+ *dest = '\0';
+}
+
+int cd_to(bb_t *bb, const char *path)
+{
+ char pbuf[PATH_MAX];
+ if (strcmp(path, "<selection>") == 0) {
strcpy(pbuf, path);
+ if (bb->marks['-']) free(bb->marks['-']);
+ bb->marks['-'] = memcheck(strdup(bb->path));
+ } else if (strcmp(path, "..") == 0 && strcmp(bb->path, "<selection>") == 0) {
+ if (!bb->marks['-']) return -1;
+ strcpy(pbuf, bb->marks['-']);
+ if (chdir(pbuf)) return -1;
} else {
- strcpy(pbuf, bb->path);
- strcat(pbuf, path);
+ normalize_path(bb->path, path, pbuf);
+ if (chdir(pbuf)) return -1;
}
- if (pbuf[strlen(pbuf)-1] != '/')
- strcat(pbuf, "/");
-
- while (1) {
- char *p;
- if ((p = strstr(pbuf, "/../"))) {
- if (p == pbuf) return 0;
- char *end = p + 3;
- char *start = p - 1;
- while (start > pbuf && *start != '/') --start;
- memmove(start, end, strlen(end)+1);
- continue;
- }
- if ((p = strstr(pbuf, "/./"))) {
- memmove(p, p+2, strlen(p+2)+1);
- continue;
- }
- break;
+ if (strcmp(bb->path, "<selection>") != 0) {
+ if (bb->marks['-']) free(bb->marks['-']);
+ bb->marks['-'] = memcheck(strdup(bb->path));
}
- if (chdir(pbuf)) return -1;
populate_files(bb, pbuf);
return 0;
}
@@ -860,7 +891,8 @@ void populate_files(bb_t *bb, const char *path)
bb->files = NULL;
}
- int old_scroll = bb->scroll;
+ int old_scroll = bb->scroll, old_cursor = bb->cursor;
+ int samedir = path && strcmp(bb->path, path) == 0;
bb->nfiles = 0;
bb->cursor = 0;
bb->scroll = 0;
@@ -868,46 +900,60 @@ void populate_files(bb_t *bb, const char *path)
if (path == NULL || !path[0])
return;
- DIR *dir = opendir(path);
- if (!dir)
- err("Couldn't open dir: %s", path);
-
- if (path != bb->path)
- strcpy(bb->path, path);
-
- if (bb->path[strlen(bb->path)-1] != '/')
- strcat(bb->path, "/");
-
size_t cap = 0;
- char pathbuf[PATH_MAX];
- strcpy(pathbuf, bb->path);
- size_t pathbuflen = strlen(pathbuf);
- for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
- if (dp->d_name[0] == '.') {
- if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
- if (!bb->show_dotdot) continue;
- } else if (dp->d_name[1] == '\0') {
- if (!bb->show_dot) continue;
- } else if (!bb->show_dotfiles) continue;
+ if (strcmp(path, "<selection>") == 0) {
+ for (entry_t *e = bb->firstselected; e; e = e->selected.next) {
+ if ((size_t)bb->nfiles + 1 > cap) {
+ cap += 100;
+ bb->files = memcheck(realloc(bb->files, cap*sizeof(void*)));
+ }
+ e->index = bb->nfiles;
+ bb->files[bb->nfiles++] = e;
}
- if ((size_t)bb->nfiles + 1 > cap) {
- cap += 100;
- bb->files = memcheck(realloc(bb->files, cap*sizeof(void*)));
+ } else {
+ DIR *dir = opendir(path);
+ if (!dir)
+ err("Couldn't open dir: %s", path);
+
+ if (path[strlen(path)-1] != '/')
+ err("No terminating slash on '%s'", path);
+
+ char pathbuf[PATH_MAX];
+ strcpy(pathbuf, path);
+ size_t pathbuflen = strlen(pathbuf);
+ for (struct dirent *dp; (dp = readdir(dir)) != NULL; ) {
+ if (dp->d_name[0] == '.') {
+ if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') {
+ if (!bb->show_dotdot) continue;
+ } else if (dp->d_name[1] == '\0') {
+ if (!bb->show_dot) continue;
+ } else if (!bb->show_dotfiles) continue;
+ }
+ if ((size_t)bb->nfiles + 1 > cap) {
+ cap += 100;
+ bb->files = memcheck(realloc(bb->files, cap*sizeof(void*)));
+ }
+ strcpy(&pathbuf[pathbuflen], dp->d_name);
+ entry_t *entry = load_entry(bb, pathbuf);
+ if (!entry) err("Failed to load entry: '%s'", dp->d_name);
+ entry->index = bb->nfiles;
+ bb->files[bb->nfiles++] = entry;
}
- strcpy(&pathbuf[pathbuflen], dp->d_name);
- entry_t *entry = load_entry(bb, pathbuf);
- if (!entry) err("Failed to load entry: '%s'", dp->d_name);
- entry->index = bb->nfiles;
- bb->files[bb->nfiles++] = entry;
+ closedir(dir);
}
- closedir(dir);
+
+ if (path != bb->path)
+ strcpy(bb->path, path);
// TODO: this may have some weird aliasing issues, but eh, it's simple and effective
for (int i = 0; i < bb->nfiles; i++)
bb->files[i]->shufflepos = rand();
sort_files(bb);
- set_scroll(bb, old_scroll);
+ if (samedir) {
+ set_cursor(bb, old_cursor);
+ set_scroll(bb, old_scroll);
+ }
}
/*
@@ -948,7 +994,7 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
switch (cmd[1]) {
case 'e': { // +deselect:
if (!value && !bb->nfiles) return BB_INVALID;
- if (!value) value = bb->files[bb->cursor]->name;
+ if (!value) value = bb->files[bb->cursor]->fullname;
if (strcmp(value, "*") == 0) {
clear_selection(bb);
return BB_OK;
@@ -990,6 +1036,7 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
}
case 'j': { // +jump:
if (!value) return BB_INVALID;
+ bb->dirty = 1;
char key = value[0];
if (bb->marks[(int)key]) {
value = bb->marks[(int)key];
@@ -1059,7 +1106,7 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
case '\0': case 'e': // +select:
if (!value && !bb->nfiles) return BB_INVALID;
- if (!value) value = bb->files[bb->cursor]->name;
+ if (!value) value = bb->files[bb->cursor]->fullname;
if (strcmp(value, "*") == 0) {
for (int i = 0; i < bb->nfiles; i++) {
if (strcmp(bb->files[i]->name, ".")
@@ -1083,7 +1130,7 @@ bb_result_t execute_cmd(bb_t *bb, const char *cmd)
}
case 't': { // +toggle:
if (!value && !bb->nfiles) return BB_INVALID;
- if (!value) value = bb->files[bb->cursor]->name;
+ if (!value) value = bb->files[bb->cursor]->fullname;
entry_t *e = load_entry(bb, value);
if (e) toggle_entry(bb, e);
return BB_OK;
@@ -1102,6 +1149,7 @@ void bb_browse(bb_t *bb, const char *path)
int lastwidth = termwidth, lastheight = termheight;
int check_cmds = 1;
+ bb->marks['-'] = memcheck(strdup(path));
cd_to(bb, path);
bb->scroll = 0;
bb->cursor = 0;
@@ -1215,7 +1263,6 @@ void bb_browse(bb_t *bb, const char *path)
close_term();
raise(SIGTSTP);
init_term();
- fputs(T_ON(T_ALT_SCREEN), tty_out);
bb->dirty = 1;
goto redraw;
@@ -1275,8 +1322,10 @@ void bb_browse(bb_t *bb, const char *path)
quit:
populate_files(bb, NULL);
- fputs(T_LEAVE_BBMODE, tty_out);
- cleanup();
+ if (tty_out) {
+ fputs(T_LEAVE_BBMODE, tty_out);
+ cleanup();
+ }
}
/*
@@ -1307,7 +1356,7 @@ void print_bindings(void)
}
*p = '\0';
printf("\033[1m\033[%dG%s\033[0m", width/2 - 1 - (int)strlen(buf), buf);
- printf("\033[0m\033[%dG\033[34;1m%s\033[0m", width/2 + 1, bindings[i].description);
+ printf("\033[0m\033[%dG\033[34m%s\033[0m", width/2 + 1, bindings[i].description);
printf("\033[0m\n");
}
printf("\n");
@@ -1431,14 +1480,16 @@ int main(int argc, char *argv[])
signal(SIGPROF, cleanup_and_exit);
signal(SIGSEGV, cleanup_and_exit);
- char *real = realpath(initial_path, NULL);
- if (!real || chdir(real)) err("Not a valid path: %s\n", initial_path);
+ char path[PATH_MAX], curdir[PATH_MAX];
+ getcwd(curdir, PATH_MAX);
+ strcat(curdir, "/");
+ normalize_path(curdir, initial_path, path);
+ if (chdir(path)) err("Not a valid path: %s\n", path);
bb_t *bb = memcheck(calloc(1, sizeof(bb_t)));
bb->columns[0] = COL_NAME;
strcpy(bb->sort, "+n");
- bb_browse(bb, real);
- free(real);
+ bb_browse(bb, path);
if (bb->firstselected && print_selection) {
for (entry_t *e = bb->firstselected; e; e = e->selected.next) {
diff --git a/config.def.h b/config.def.h
index fcf0838..6a26999 100644
--- a/config.def.h
+++ b/config.def.h
@@ -99,7 +99,7 @@ const char *startupcmds[] = {
//////////////////////////////////////////////
// Set some default marks:
"+mark:0", "+mark:~=~", "+mark:h=~", "+mark:/=/", "+mark:c=~/.config",
- "+mark:l=~/.local",
+ "+mark:l=~/.local", "+mark:s=<selection>",
// Default column and sorting options:
"+sort:+n", "+col:*smpn", "+..",
NULL, // NULL-terminated array
@@ -122,20 +122,19 @@ const int colwidths[128] = {
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
#endif
extern binding_t bindings[];
+#define EM(s) "\033[33;4m" s "\033[0;34m"
binding_t bindings[] = {
//////////////////////////////////////////////////////////////////////////
// User-defined custom scripts can go here
// 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", "Show the help menu", NORMAL_TERM},
- {{'q', 'Q'}, "+quit", "Quit"},
- {{'k', KEY_ARROW_UP}, "+move:-1", "Move up"},
- {{'j', KEY_ARROW_DOWN}, "+move:+1", "Move down"},
- {{'h', KEY_ARROW_LEFT}, "+cd:..", "Go up a folder"},
- {{'l', KEY_ARROW_RIGHT}, "test -d \"$BBCURSOR\" && bb \"+cd:$BBCURSOR\"", "Enter a folder"},
- {{' ','v','V'}, "+toggle", "Toggle selection"},
- {{'e'}, "$EDITOR \"$@\"", "Edit file in $EDITOR", NORMAL_TERM},
+ {{'?', KEY_F1}, "bb -b | $PAGER -r", EM("Help")" menu", NORMAL_TERM},
+ {{'q', 'Q'}, "+quit", EM("Quit")},
+ {{'j', KEY_ARROW_DOWN}, "+move:+1", EM("Next")" file"},
+ {{'k', KEY_ARROW_UP}, "+move:-1", EM("Previous")" file"},
+ {{'h', KEY_ARROW_LEFT}, "+cd:..", EM("Parent")" directory"},
+ {{'l', KEY_ARROW_RIGHT}, "test -d \"$BBCURSOR\" && bb \"+cd:$BBCURSOR\"", EM("Enter")" a directory"},
{{'\r', KEY_MOUSE_DOUBLE_LEFT},
#ifdef __APPLE__
QUOTE(
@@ -151,24 +150,27 @@ elif file -bi "$BBCURSOR" | grep '^\(text/\|inode/empty\)' >/dev/null; then $EDI
else xdg-open "$BBCURSOR"; fi
)/*ENDQUOTE*/,
#endif
- "Open file", NORMAL_TERM},
- {{'f'}, "bb \"+g:`fzf`\"", "Fuzzy search for file", NORMAL_TERM},
- {{'/'}, "bb \"+g:`ls -a|fzf`\"", "Fuzzy select file", NORMAL_TERM},
- {{'L'}, PIPE_SELECTION_TO "$PAGER", "List all selected files", NORMAL_TERM},
- {{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+d:*' +r", "Delete files", AT_CURSOR},
- {{'D'}, "rm -rf \"$@\"; bb '+d:*' +r", "Delete files without confirmation"},
- {{'M'}, "mv -i \"$@\" .; bb '+d:*' +r", "Move files to current folder"},
- {{'c'}, "cp -i \"$@\" .; bb +r", "Copy files to current folder"},
- {{'C'}, "for f; do cp \"$f\" \"$f.copy\"; done; bb +r", "Clone files"},
- {{'n'}, "name=`bb '?New file: '` && touch \"$name\"; bb +r \"+goto:$name\"", "New file"},
- {{'N'}, "name=`bb '?New dir: '` && mkdir \"$name\"; bb +r \"+goto:$name\"", "New folder"},
+ EM("Open")" file/directory", NORMAL_TERM},
+ {{' ','v','V'}, "+toggle", EM("Toggle")" selection"},
+ {{KEY_ESC}, "+deselect:*", EM("Clear")" selection"},
+ {{'e'}, "$EDITOR \"$@\"", EM("Edit")" file in $EDITOR", NORMAL_TERM},
+ {{KEY_CTRL_F}, "bb \"+g:`fzf`\"", EM("Fuzzy search")" for file", NORMAL_TERM},
+ {{'/'}, "bb \"+g:`ls -a|fzf`\"", EM("Fuzzy select")" file", NORMAL_TERM},
+ {{'d', KEY_DELETE}, "rm -rfi \"$@\"; bb '+de:*' +r", EM("Delete")" files", AT_CURSOR},
+ {{'D'}, "rm -rf \"$@\"; bb '+de:*' +r", EM("Delete")" files (without confirmation)"},
+ {{'M'}, "mv -i \"$@\" .; bb '+de:*' +r; for f; do bb \"+sel:`pwd`/`basename \"$f\"`\"; done",
+ EM("Move")" files to current directory"},
+ {{'c'}, "cp -i \"$@\" .; bb +r", EM("Copy")" files to current directory"},
+ {{'C'}, "bb '+de:*'; for f; do cp \"$f\" \"$f.copy\" && bb \"+sel:$f.copy\"; done; bb +r", EM("Clone")" files"},
+ {{'n'}, "name=`bb '?New file: '` && touch \"$name\"; bb +r \"+goto:$name\"", EM("New file")},
+ {{'N'}, "name=`bb '?New dir: '` && mkdir \"$name\"; bb +r \"+goto:$name\"", EM("New directory")},
{{'|'}, "cmd=`bb '?|'` && " PIPE_SELECTION_TO "sh -c \"$cmd\" && " PAUSE "; bb +r",
- "Pipe selected files to a command"},
+ EM("Pipe")" selected files to a command"},
{{':'}, "$SHELL -c \"`bb '?:'`\" -- \"$@\"; " PAUSE "; bb +refresh",
- "Run a command"},
- {{'>'}, "$SHELL", "Open a shell", NORMAL_TERM},
- {{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set mark"},
- {{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", "Jump to mark"},
+ EM("Run")" a command"},
+ {{'>'}, "$SHELL", "Open a "EM("shell"), NORMAL_TERM},
+ {{'m'}, "read -n1 -p 'Mark: ' m && bb \"+mark:$m;$PWD\"", "Set "EM("mark")},
+ {{'\''}, "read -n1 -p 'Jump: ' j && bb \"+jump:$j\"", EM("Jump")" to mark"},
{{'r'}, QUOTE(
bb '+deselect:*' +refresh;
@@ -177,7 +179,7 @@ for f; do
test "$f" != "$renamed" && mv -i "$f" "$renamed"; then
test $BBSELECTED && bb "+select:$renamed";
elif test $BBSELECTED; then bb "+select:$f"; fi
-done)/*ENDQUOTE*/, "Rename files", AT_CURSOR},
+done)/*ENDQUOTE*/, EM("Rename")" files", AT_CURSOR},
{{'R'}, QUOTE(
if patt="`bb '?Rename pattern: ' 's/'`"; then true; else bb +r; exit; fi;
@@ -188,32 +190,31 @@ for f; do
if test "$f" != "$renamed" && mv -i "$f" "$renamed"; then
test $BBSELECTED && bb "+select:$renamed";
elif test $BBSELECTED; then bb "+select:$f"; fi
-done)/*ENDQUOTE*/, "Regex rename files", AT_CURSOR},
+done)/*ENDQUOTE*/, EM("Regex rename")" files", AT_CURSOR},
// TODO debug:
{{'P'}, "patt=`bb '?Select pattern: '` && "
"for f; do echo \"$f\" | grep \"$patt\" >/dev/null 2>/dev/null && bb \"+sel:$f\"; done",
- "Regex select files"},
- {{'J'}, "+spread:+1", "Spread selection down"},
- {{'K'}, "+spread:-1", "Spread selection up"},
- {{'b'}, "bb \"+`bb '?bb +'`\"", "Run a bb command"},
+ EM("Regex select")" files"},
+ {{'J'}, "+spread:+1", EM("Spread")" selection down"},
+ {{'K'}, "+spread:-1", EM("Spread")" selection up"},
+ {{'b'}, "bb \"+`bb '?bb +'`\"", "Run a "EM("bb command")},
{{'s'}, "read -n1 -p 'Sort \033[1m(a)\033[22mlphabetic "
"\033[1m(s)\033[22mize \033[1m(m)\033[22modification \033[1m(c)\033[22mcreation "
"\033[1m(a)\033[22maccess \033[1m(r)\033[22mandom \033[1m(p)\033[22mermissions:\033[0m ' sort "
- "&& bb \"+sort:+$sort\"", "Sort by..."},
- {{'#'}, "bb \"+col:`bb '?Set columns: '`\"", "Set columns"},
- {{'.'}, "bb +dotfiles", "Toggle dotfiles"},
- {{'g', KEY_HOME}, "+move:0", "Go to first file"},
- {{'G', KEY_END}, "+move:100%n", "Go to last file"},
- {{KEY_ESC}, "+deselect:*", "Clear selection"},
- {{KEY_F5, KEY_CTRL_R}, "+refresh", "Refresh"},
- {{KEY_CTRL_A}, "+select:*", "Select all files in current folder"},
- {{KEY_PGDN}, "+scroll:+100%", "Page down"},
- {{KEY_PGUP}, "+scroll:-100%", "Page up"},
- {{KEY_CTRL_D}, "+scroll:+50%", "Half page down"},
- {{KEY_CTRL_U}, "+scroll:-50%", "Half page up"},
- {{KEY_MOUSE_WHEEL_DOWN}, "+scroll:+3", "Scroll down"},
- {{KEY_MOUSE_WHEEL_UP}, "+scroll:-3", "Scroll up"},
+ "&& bb \"+sort:+$sort\"", EM("Sort")" by..."},
+ {{'#'}, "bb \"+col:`bb '?Set columns: '`\"", "Set "EM("columns")},
+ {{'.'}, "bb +dotfiles", "Toggle "EM("dotfiles")},
+ {{'g', KEY_HOME}, "+move:0", "Go to "EM("first")" file"},
+ {{'G', KEY_END}, "+move:100%n", "Go to "EM("last")" file"},
+ {{KEY_F5, KEY_CTRL_R}, "+refresh", EM("Refresh")},
+ {{KEY_CTRL_A}, "+select:*", EM("Select all")" files in current directory"},
+ {{KEY_PGDN}, "+scroll:+100%", EM("Page down")},
+ {{KEY_PGUP}, "+scroll:-100%", EM("Page up")},
+ {{KEY_CTRL_D}, "+scroll:+50%", EM("Half page down")},
+ {{KEY_CTRL_U}, "+scroll:-50%", EM("Half page up")},
+ {{KEY_MOUSE_WHEEL_DOWN}, "+scroll:+3", EM("Scroll down")},
+ {{KEY_MOUSE_WHEEL_UP}, "+scroll:-3", EM("Scroll up")},
{{0}}, // Array must be 0-terminated
};
#ifdef __APPLE__