瀏覽代碼

Support search as you type

master
Arun Prakash Jana 7 年之前
父節點
當前提交
1fdcaef4f5
沒有發現已知的金鑰在資料庫的簽署中 GPG 金鑰 ID: A75979F35C080412
共有 3 個檔案被更改,包括 177 行新增29 行删除
  1. +5
    -3
      README.md
  2. +8
    -2
      nnn.1
  3. +164
    -24
      nnn.c

+ 5
- 3
README.md 查看文件

@@ -59,6 +59,7 @@ Have fun with it! PRs are welcome. Check out [#1](https://github.com/jarun/nnn/i
- Super-easy navigation with roll-over at edges
- Jump HOME or back to the last visited directory (as usual!)
- Jump to initial dir, chdir prompt, cd ..... (with . as PWD)
- Search-as-you-type
- Desktop opener integration to handle mime types
- Customizable bash script nlay to handle known file types
- Disk usage analyzer mode
@@ -66,7 +67,6 @@ Have fun with it! PRs are welcome. Check out [#1](https://github.com/jarun/nnn/i
- Show media information (needs mediainfo)
- Sort by modification time, size
- Sort numeric names in numeric order (1, 2, ... 10, 11, ...)
- Search directory contents using regex expressions
- Spawn a shell in the current directory
- Invoke file path copier (*easy* shell integration)
- Quit and change directory (*easy* shell integration)
@@ -168,9 +168,11 @@ Right, Enter, l, ^M | Open file or enter dir

Filters support regexes to display only the matched entries in the current directory view. This effectively allows searching through the directory tree for a particular entry.

Filters do not stack on top of each other. They are applied anew every time.
Filters do not stack on top of each other. They are applied anew every time. There are 3 ways to reset a filter:

An empty filter expression resets the filter.
An empty filter expression, a search with no results or an extra backspace at the filter prompt (like vi).

If you want to list all matches starting with the filter expression (a common use case), start the expression with a `^` (caret) symbol.

If nnn is invoked as root the default filter will also match hidden files.



+ 8
- 2
nnn.1 查看文件

@@ -138,9 +138,15 @@ entries in the current directory view. This effectively allows
searching through the directory tree for a particular entry.
.Pp
Filters do not stack on top of each other. They are applied anew
every time.
every time. There are 3 ways to reset a filter:
.Pp
An empty filter expression resets the filter.
An empty filter expression, a search with no results or an extra backspace at
the filter prompt (like vi).
.Pp
If you want to list all matches starting with the filter expression (a common
use case), start the expression with a
.Pa ^
(caret) symbol.
.Pp
If
.Nm


+ 164
- 24
nnn.c 查看文件

@@ -29,6 +29,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <readline/readline.h>

#define __USE_XOPEN_EXTENDED
@@ -137,6 +138,8 @@ extern int add_history(const char *);
extern void add_history(const char *string);
#endif

extern int wget_wch(WINDOW *, wint_t *);

/* Global context */
static struct entry *dents;
static int ndents, cur, total_dents;
@@ -171,6 +174,8 @@ static const char *size_units[] = {"B", "K", "M", "G", "T", "P", "E", "Z", "Y"};
static void printmsg(char *);
static void printwarn(void);
static void printerr(int, char *);
static int dentfind(struct entry *dents, int n, char *path);
static void redraw(char *path);

static rlim_t
max_openfds()
@@ -561,13 +566,16 @@ printprompt(char *str)
/* Returns SEL_* if key is bound and 0 otherwise.
* Also modifies the run and env pointers (used on SEL_{RUN,RUNARG}) */
static int
nextsel(char **run, char **env)
nextsel(char **run, char **env, int *ch)
{
int c;
int c = *ch;
unsigned int i;
static unsigned int len = LEN(bindings);

c = getch();
if (c == 0)
c = getch();
else
*ch = 0;
if (c == -1)
idle++;
else
@@ -582,20 +590,159 @@ nextsel(char **run, char **env)
return 0;
}

static char *
readln(void)
static int
fill(struct entry **dents,
int (*filter)(regex_t *, char *), regex_t *re)
{
static struct entry _dent;
static int count, n;
n = 0;

for (count = 0; count < ndents; count++) {
if (filter(re, (*dents)[count].name) == 0)
continue;

if (n != count) {
/* Copy to tmp */
xstrlcpy(_dent.name, (*dents)[n].name, NAME_MAX);
_dent.mode = (*dents)[n].mode;
_dent.t = (*dents)[n].t;
_dent.size = (*dents)[n].size;
_dent.bsize = (*dents)[n].bsize;

/* Copy count to n */
xstrlcpy((*dents)[n].name, (*dents)[count].name, NAME_MAX);
(*dents)[n].mode = (*dents)[count].mode;
(*dents)[n].t = (*dents)[count].t;
(*dents)[n].size = (*dents)[count].size;
(*dents)[n].bsize = (*dents)[count].bsize;

/* Copy tmp to count */
xstrlcpy((*dents)[count].name, _dent.name, NAME_MAX);
(*dents)[count].mode = _dent.mode;
(*dents)[count].t = _dent.t;
(*dents)[count].size = _dent.size;
(*dents)[count].bsize = _dent.bsize;
}

n++;
}

return n;
}

static int
matches(char *fltr)
{
static regex_t re;

/* Search filter */
if (setfilter(&re, fltr) != 0)
return -1;

ndents = fill(&dents, visible, &re);
qsort(dents, ndents, sizeof(*dents), entrycmp);

return 0;
}

static int
readln(char *path)
{
static char ln[LINE_MAX];
static char ln[LINE_MAX << 2];
static wchar_t wln[LINE_MAX];
static wint_t ch[2] = {0};
int r, total = ndents;
int oldcur = cur;
int len = 1;
char *pln = ln + 1;

memset(wln, 0, LINE_MAX << 2);
wln[0] = '/';
ln[0] = '/';
ln[1] = '\0';
cur = 0;

timeout(-1);
echo();
curs_set(TRUE);
memset(ln, 0, sizeof(ln));
wgetnstr(stdscr, ln, sizeof(ln) - 1);
printprompt(ln);

while ((r = wget_wch(stdscr, ch)) != ERR) {
if (r == OK) {
switch(*ch) {
case '\r': // with nonl(), this is ENTER key value
if (len == 1) {
cur = oldcur;
*ch = CONTROL('L');
goto end;
}


if (matches(pln) == -1)
goto end;

redraw(path);
goto end;
case 127: // handle DEL
if (len == 1) {
cur = oldcur;
*ch = CONTROL('L');
goto end;
}

if (len == 2)
cur = oldcur;

wln[--len] = '\0';
wcstombs(ln, wln, LINE_MAX << 2);
ndents = total;
if (matches(pln) == -1)
continue;
redraw(path);
printprompt(ln);
break;
default:
wln[len++] = (wchar_t)*ch;
wln[len] = '\0';
wcstombs(ln, wln, LINE_MAX << 2);
ndents = total;
if (matches(pln) == -1)
continue;
redraw(path);
printprompt(ln);
}
} else if (r == KEY_CODE_YES) {
switch(*ch) {
case KEY_DC:
case KEY_BACKSPACE:
if (len == 1) {
cur = oldcur;
*ch = CONTROL('L');
goto end;
}

if (len == 2)
cur = oldcur;

wln[--len] = '\0';
wcstombs(ln, wln, LINE_MAX << 2);
ndents = total;
if (matches(pln) == -1)
continue;
redraw(path);
printprompt(ln);
break;
default:
goto end;
}
}
}
end:
noecho();
curs_set(FALSE);
timeout(1000);
return ln[0] ? ln : NULL;
return *ch;
}

static int
@@ -1278,8 +1425,7 @@ browse(char *ipath, char *ifilter)
static char fltr[LINE_MAX];
char *mime, *dir, *tmp, *run, *env;
struct stat sb;
regex_t re;
int r, fd;
int r, fd, filtered = FALSE;
enum action sel = SEL_RUNARG + 1;

xstrlcpy(path, ipath, sizeof(path));
@@ -1299,7 +1445,9 @@ nochange:
/* Exit if parent has exited */
if (getppid() == 1)
_exit(0);
sel = nextsel(&run, &env);

sel = nextsel(&run, &env, &filtered);

switch (sel) {
case SEL_CDQUIT:
{
@@ -1341,7 +1489,7 @@ nochange:
case SEL_GOIN:
/* Cannot descend in empty directories */
if (ndents == 0)
goto nochange;
goto begin;

mkpath(path, dents[cur].name, newpath, sizeof(newpath));
DPRINTF_S(newpath);
@@ -1428,21 +1576,13 @@ nochange:
goto nochange;
}
case SEL_FLTR:
/* Read filter */
printprompt("filter: ");
tmp = readln();
if (tmp == NULL)
tmp = ifilter;
/* Check and report regex errors */
r = setfilter(&re, tmp);
if (r != 0)
goto nochange;
xstrlcpy(fltr, tmp, sizeof(fltr));
filtered = readln(path);
xstrlcpy(fltr, ifilter, sizeof(fltr));
DPRINTF_S(fltr);
/* Save current */
if (ndents > 0)
mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
goto begin;
goto nochange;
case SEL_NEXT:
if (cur < ndents - 1)
cur++;


Loading…
取消
儲存