README.md | 1 + nnn.1 | 10 ++++++++++ src/nnn.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++----- diff --git a/README.md b/README.md index 52d8d3b50b085ff57e7a8e90ef42be57b1469f1b..baa3c4cbc007bb8865d088e67302fe243fefe7b7 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ - Ordered pure numeric names by default (visit `/proc`) - Case-insensitive version (_aka_ natural) sort - By name, access/change/mod (default) time, size, extn - Reverse sort + - Directory-specific ordering - Mimes - Preview hovered files in FIFO-based previewer - Open with desktop opener or specify a custom opener diff --git a/nnn.1 b/nnn.1 index a172f39cb49bb37080a9eb1736cf8c27a41d9087..d0502c2e4c4ec7e123364e16de94d7f219c2c21f 100644 --- a/nnn.1 +++ b/nnn.1 @@ -127,6 +127,7 @@ .Pp .Fl "T key" sort order keys: 'a'u / 'd'u / 'e'xtension / 'r'everse / 's'ize / 't'ime / 'v'ersion + capitalize to reverse (except 'r') .Pp .Fl u use selection if available, don't prompt to choose between selection and hovered entry @@ -427,6 +428,15 @@ y:-!sync* | Flush cached writes ----------------------------------- + ------------------------------------------------- Online docs: https://github.com/jarun/nnn/tree/master/plugins +.Ed +.Pp +\fBNNN_ORDER:\fR directory-specific sort key. +.Bd -literal + export NNN_ORDER='t:/home/user/Downloads;S:/tmp' + + NOTE: Sort keys can be a/d/e/r/s/t/v (see program option -T). + Capitalize to reverse (except 'r'). + Path must be absolute. .Ed .Pp \fBNNN_COLORS:\fR string of color numbers for each context, e.g.: diff --git a/src/nnn.c b/src/nnn.c index 167fdf3a31fd49bd503ede1efb27167170a109bb..82b3fcf85580053f58be3a0d8e40c83c71b070f4 100644 --- a/src/nnn.c +++ b/src/nnn.c @@ -166,6 +166,9 @@ #define MAX(x, y) ((x) > (y) ? (x) : (y)) #define ISODD(x) ((x) & 1) #define ISBLANK(x) ((x) == ' ' || (x) == '\t') #define TOUPPER(ch) (((ch) >= 'a' && (ch) <= 'z') ? ((ch) - 'a' + 'A') : (ch)) +#define TOLOWER(ch) (((ch) >= 'A' && (ch) <= 'Z') ? ((ch) - 'A' + 'a') : (ch)) +#define ISUPPER_(ch) ((ch) >= 'A' && (ch) <= 'Z') +#define ISLOWER_(ch) ((ch) >= 'a' && (ch) <= 'z') #define CMD_LEN_MAX (PATH_MAX + ((NAME_MAX + 1) << 1)) #define ALIGN_UP(x, A) ((((x) + (A) - 1) / (A)) * (A)) #define READLINE_MAX 256 @@ -430,9 +433,11 @@ #endif static uint_t idletimeout, selbufpos, selbuflen; static ushort_t xlines, xcols; static ushort_t idle; -static uchar_t maxbm, maxplug; +static uchar_t maxbm, maxplug, maxorder; +static uchar_t cfgsort[CTX_MAX + 1]; static char *bmstr; static char *pluginstr; +static char *orderstr; static char *opener; static char *editor; static char *enveditor; @@ -457,6 +462,7 @@ static struct entry *pdents; static blkcnt_t dir_blocks; static kv *bookmark; static kv *plug; +static kv *order; static uchar_t tmpfplen, homelen; static uchar_t blk_shift = BLK_SHIFT_512; #ifndef NOMOUSE @@ -684,8 +690,9 @@ #define NNN_PIPE 7 #define NNN_MCLICK 8 #define NNN_SEL 9 #define NNN_ARCHIVE 10 -#define NNN_HELP 11 /* strings end here */ -#define NNN_TRASH 12 /* flags begin here */ +#define NNN_ORDER 11 +#define NNN_HELP 12 /* strings end here */ +#define NNN_TRASH 13 /* flags begin here */ static const char * const env_cfg[] = { "NNN_OPTS", @@ -699,6 +706,7 @@ "NNN_PIPE", "NNN_MCLICK", "NNN_SEL", "NNN_ARCHIVE", + "NNN_ORDER", "NNN_HELP", "NNN_TRASH", }; @@ -3788,6 +3796,22 @@ DPRINTF_S("Invalid key"); return NULL; } +static int get_kv_key(kv *kvarr, char *val, uchar_t max, uchar_t id) +{ + if (!kvarr) + return -1; + + if (id != NNN_ORDER) /* For now this function supports only order string */ + return -1; + + for (int r = 0; kvarr[r].key && r < max; ++r) { + if (xstrcmp((orderstr + kvarr[r].off), val) == 0) + return kvarr[r].key; + } + + return -1; +} + static void resetdircolor(int flags) { /* Directories are always shown on top, clear the color when moving to first file */ @@ -4171,13 +4195,19 @@ g_ctx[tmpcfg.curctx].c_name[0] = '\0'; g_ctx[tmpcfg.curctx].c_cfg = tmpcfg; - if (ctxr->c_cfg.ctxactive) /* Switch to saved context */ + if (ctxr->c_cfg.ctxactive) { /* Switch to saved context */ tmpcfg = ctxr->c_cfg; - else { /* Set up a new context from current context */ + /* Skip ordering an open context */ + cfgsort[CTX_MAX] = cfgsort[nextctx]; + cfgsort[nextctx] = '0'; + } else { /* Set up a new context from current context */ ctxr->c_cfg.ctxactive = 1; xstrsncpy(ctxr->c_path, path, PATH_MAX); ctxr->c_last[0] = ctxr->c_name[0] = ctxr->c_fltr[0] = ctxr->c_fltr[1] = '\0'; ctxr->c_cfg = tmpcfg; + /* If already in an ordered dir, clear ordering for the new context and let it order */ + if (cfgsort[cfg.curctx] == 'z') + cfgsort[nextctx] = 'z'; } tmpcfg.curctx = nextctx; @@ -5950,7 +5980,13 @@ } static int set_sort_flags(int r) { - bool session = !r; + bool session = (r == '\0'); + bool reverse = FALSE; + + if (ISUPPER_(r) && (r != 'R') && (r != 'C')) { + reverse = TRUE; + r = TOLOWER(r); + } /* Set the correct input in case of a session load */ if (session) { @@ -6059,6 +6095,13 @@ break; default: return 0; } + + if (reverse) { + cfg.reverse = 1; + entrycmpfn = &reventrycmp; + } + + cfgsort[cfg.curctx] = (uchar_t)r; return r; } @@ -6488,7 +6531,8 @@ if (!pnamebuf) errexit(); begin: - /* Can fail when permissions change while browsing. + /* + * Can fail when permissions change while browsing. * It's assumed that path IS a directory when we are here. */ if (chdir(path) == -1) { @@ -6532,6 +6576,16 @@ haiku_nm_active = FALSE; watch = FALSE; } #endif + + if (cfgsort[cfg.curctx] == 'z') + set_sort_flags('c'); + if (order && (!cfgsort[cfg.curctx] || (cfgsort[cfg.curctx] == 'c')) + && ((r = get_kv_key(order, path, maxorder, NNN_ORDER)) > 0)) { + set_sort_flags(r); + cfgsort[cfg.curctx] = 'z'; + } + if (cfgsort[cfg.curctx] == '0') + cfgsort[cfg.curctx] = cfgsort[CTX_MAX]; populate(path, lastname); if (g_state.interrupt) { @@ -8353,6 +8407,12 @@ /* Parse plugins string */ if (!parsekvpair(&plug, &pluginstr, NNN_PLUG, &maxplug)) { msg(env_cfg[NNN_PLUG]); + return EXIT_FAILURE; + } + + /* Parse order string */ + if (!parsekvpair(&order, &orderstr, NNN_ORDER, &maxorder)) { + msg(env_cfg[NNN_ORDER]); return EXIT_FAILURE; }