Add launcher completion (adapt philpep FTS based completion, thanks philpep)
This commit is contained in:
parent
9c7c3a79e2
commit
ca4fd18e38
256
src/launcher.c
256
src/launcher.c
@ -3,8 +3,10 @@
|
|||||||
* For license, see COPYING.
|
* For license, see COPYING.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <X11/Xutil.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <fts.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
#include "wmfs.h"
|
#include "wmfs.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
@ -12,6 +14,223 @@
|
|||||||
#include "infobar.h"
|
#include "infobar.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
fts_alphasort(const FTSENT **a, const FTSENT **b)
|
||||||
|
{
|
||||||
|
return (strcmp((*a)->fts_name, (*b)->fts_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **
|
||||||
|
complete_on_command(char *start)
|
||||||
|
{
|
||||||
|
char **paths, *path, *p, **namelist = NULL;
|
||||||
|
int count;
|
||||||
|
FTS *tree;
|
||||||
|
FTSENT *node;
|
||||||
|
|
||||||
|
if(!(path = getenv("PATH")) || !start)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* split PATH into paths */
|
||||||
|
path = p = xstrdup(path);
|
||||||
|
|
||||||
|
for(count = 1, p = path; strchr(p, ':'); ++p, ++count);
|
||||||
|
|
||||||
|
paths = xcalloc(count, sizeof(*paths));
|
||||||
|
|
||||||
|
for(paths[0] = p = path, count = 1; (p = strchr(p, ':')); ++p, ++count)
|
||||||
|
{
|
||||||
|
paths[count] = p + 1;
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
paths[count] = NULL;
|
||||||
|
|
||||||
|
if(!(tree = fts_open(paths, FTS_NOCHDIR, fts_alphasort)))
|
||||||
|
{
|
||||||
|
warn("fts_open");
|
||||||
|
free(paths);
|
||||||
|
free(path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
while((node = fts_read(tree)))
|
||||||
|
{
|
||||||
|
if(node->fts_level > 0)
|
||||||
|
fts_set(tree, node, FTS_SKIP);
|
||||||
|
|
||||||
|
if(node->fts_level
|
||||||
|
&& (node->fts_info & FTS_F)
|
||||||
|
&& (node->fts_info & FTS_NS)
|
||||||
|
&& (node->fts_statp->st_mode & S_IXOTH)
|
||||||
|
&& !strncmp(node->fts_name, start, strlen(start)))
|
||||||
|
{
|
||||||
|
namelist = xrealloc(namelist, ++count, sizeof(*namelist));
|
||||||
|
namelist[count - 1] = xstrdup(node->fts_name + strlen(start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count)
|
||||||
|
{
|
||||||
|
namelist = xrealloc(namelist, ++count, sizeof(*namelist));
|
||||||
|
namelist[count - 1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fts_close(tree))
|
||||||
|
warn("fts_close");
|
||||||
|
|
||||||
|
free(paths);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
return namelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Complete a filename or directory name.
|
||||||
|
* works like complete_on_command.
|
||||||
|
*/
|
||||||
|
static char **
|
||||||
|
complete_on_files(char *start)
|
||||||
|
{
|
||||||
|
char *p, *home, *path, *dirname = NULL, *paths[2], **namelist = NULL;
|
||||||
|
int count;
|
||||||
|
FTS *tree;
|
||||||
|
FTSENT *node;
|
||||||
|
|
||||||
|
p = start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search the directory to open and set
|
||||||
|
* the beginning of file to complete on pointer 'p'.
|
||||||
|
*/
|
||||||
|
if(*p == '\0' || !strrchr(p, '/'))
|
||||||
|
path = xstrdup(".");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!(home = getenv("HOME")))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* remplace ~ by $HOME in dirname */
|
||||||
|
if(!strncmp(p, "~/", 2) && home)
|
||||||
|
xasprintf(&dirname, "%s%s", home, p+1);
|
||||||
|
else
|
||||||
|
dirname = xstrdup(p);
|
||||||
|
|
||||||
|
/* Set p to filename to be complete
|
||||||
|
* and path the directory containing the file
|
||||||
|
* /foooooo/baaaaaar/somethinglikethis<tab>
|
||||||
|
* <---- path - ---><------- p ------>
|
||||||
|
*/
|
||||||
|
p = strrchr(dirname, '/');
|
||||||
|
if(p != dirname)
|
||||||
|
{
|
||||||
|
*(p++) = '\0';
|
||||||
|
path = xstrdup(dirname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = xstrdup("/");
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paths[0] = path;
|
||||||
|
paths[1] = NULL;
|
||||||
|
|
||||||
|
if(!(tree = fts_open(paths, FTS_NOCHDIR, fts_alphasort)))
|
||||||
|
{
|
||||||
|
warn("fts_open");
|
||||||
|
free(dirname);
|
||||||
|
free(path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
while((node = fts_read(tree)))
|
||||||
|
{
|
||||||
|
if(node->fts_level > 0)
|
||||||
|
fts_set(tree, node, FTS_SKIP);
|
||||||
|
|
||||||
|
if(node->fts_level && !strncmp(node->fts_name, p, strlen(p)))
|
||||||
|
{
|
||||||
|
namelist = xrealloc(namelist, ++count, sizeof(*namelist));
|
||||||
|
namelist[count - 1] = xstrdup(node->fts_name + strlen(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count)
|
||||||
|
{
|
||||||
|
namelist = xrealloc(namelist, ++count, sizeof(*namelist));
|
||||||
|
namelist[count - 1] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fts_close(tree))
|
||||||
|
warn("fts_close");
|
||||||
|
|
||||||
|
free(dirname);
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
return namelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
complete_cache_free(struct launcher_ccache *cache)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* release memory */
|
||||||
|
free(cache->start);
|
||||||
|
|
||||||
|
if(cache->namelist)
|
||||||
|
{
|
||||||
|
for(i = 0; cache->namelist[i]; i++)
|
||||||
|
free(cache->namelist[i]);
|
||||||
|
free(cache->namelist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init */
|
||||||
|
cache->hits = 0;
|
||||||
|
cache->start = NULL;
|
||||||
|
cache->namelist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
complete(struct launcher_ccache *cache, char *start)
|
||||||
|
{
|
||||||
|
char *p = NULL, *comp = NULL;
|
||||||
|
|
||||||
|
if(!start || !cache)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if((p = strrchr(start, ' ')))
|
||||||
|
p++;
|
||||||
|
else
|
||||||
|
p = start;
|
||||||
|
|
||||||
|
if(cache->start && !strcmp(cache->start, start))
|
||||||
|
{
|
||||||
|
if(cache->namelist && !cache->namelist[cache->hits])
|
||||||
|
cache->hits = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
complete_cache_free(cache);
|
||||||
|
cache->start = xstrdup(start);
|
||||||
|
|
||||||
|
if(p == start)
|
||||||
|
cache->namelist = complete_on_command(p);
|
||||||
|
else
|
||||||
|
cache->namelist = complete_on_files(p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cache->namelist && cache->namelist[cache->hits])
|
||||||
|
comp = cache->namelist[cache->hits];
|
||||||
|
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
#define LAUNCHER_INIT_ELEM(width) \
|
#define LAUNCHER_INIT_ELEM(width) \
|
||||||
SLIST_FOREACH(ib, &W->screen->infobars, next) \
|
SLIST_FOREACH(ib, &W->screen->infobars, next) \
|
||||||
{ \
|
{ \
|
||||||
@ -29,10 +248,11 @@ launcher_process(struct launcher *l)
|
|||||||
{
|
{
|
||||||
struct infobar *ib;
|
struct infobar *ib;
|
||||||
struct element *e;
|
struct element *e;
|
||||||
bool loop = true;
|
struct launcher_ccache cache = {NULL, NULL, 0};
|
||||||
char buf[512] = { 0 };
|
bool loop = true, found = false, lastwastab = false;
|
||||||
|
char tmpbuf[512] = { 0 }, buf[512] = { 0 };
|
||||||
char tmp[32] = { 0 };
|
char tmp[32] = { 0 };
|
||||||
char *p, *data, *arg, *cmd = xstrdup(l->command);
|
char *p, *data, *arg, *end, *cmd = xstrdup(l->command);
|
||||||
int i, pos = 0, histpos = 0;
|
int i, pos = 0, histpos = 0;
|
||||||
void (*func)(Uicb);
|
void (*func)(Uicb);
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
@ -140,16 +360,41 @@ launcher_process(struct launcher *l)
|
|||||||
loop = false;
|
loop = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* TODO: Completion */
|
/* Completion */
|
||||||
case XK_Tab:
|
case XK_Tab:
|
||||||
|
buf[pos] = '\0';
|
||||||
|
if(lastwastab)
|
||||||
|
++cache.hits;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cache.hits = 0;
|
||||||
|
strncpy(tmpbuf, buf, sizeof(tmpbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pos && (end = complete(&cache, tmpbuf)))
|
||||||
|
{
|
||||||
|
strncpy(buf, tmpbuf, sizeof(buf));
|
||||||
|
strncat(buf, end, sizeof(buf));
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastwastab = true;
|
||||||
|
|
||||||
|
/* start a new round of tabbing */
|
||||||
|
if(!found)
|
||||||
|
cache.hits = 0;
|
||||||
|
|
||||||
|
pos = strlen(buf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XK_BackSpace:
|
case XK_BackSpace:
|
||||||
|
lastwastab = false;
|
||||||
if(pos)
|
if(pos)
|
||||||
buf[--pos] = '\0';
|
buf[--pos] = '\0';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
lastwastab = false;
|
||||||
strncat(buf, tmp, sizeof(tmp));
|
strncat(buf, tmp, sizeof(tmp));
|
||||||
++pos;
|
++pos;
|
||||||
break;
|
break;
|
||||||
@ -174,6 +419,7 @@ launcher_process(struct launcher *l)
|
|||||||
|
|
||||||
XUngrabKeyboard(W->dpy, CurrentTime);
|
XUngrabKeyboard(W->dpy, CurrentTime);
|
||||||
|
|
||||||
|
complete_cache_free(&cache);
|
||||||
free(cmd);
|
free(cmd);
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
|
|||||||
22
src/util.c
22
src/util.c
@ -46,6 +46,28 @@ xcalloc(size_t nmemb, size_t size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** realloc with error support and size_t overflow check
|
||||||
|
* \param ptr old pointer
|
||||||
|
* \param nmemb number of objects
|
||||||
|
* \param size size of single object
|
||||||
|
* \return non null void pointer
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
xrealloc(void *ptr, size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
if(SIZE_MAX / nmemb < size)
|
||||||
|
err(EXIT_FAILURE, "xrealloc(%p, %zu, %zu), "
|
||||||
|
"size_t overflow detected", ptr, nmemb, size);
|
||||||
|
|
||||||
|
if((ret = realloc(ptr, nmemb * size)) == NULL)
|
||||||
|
err(EXIT_FAILURE, "realloc(%p, %zu)", ptr, nmemb * size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** asprintf wrapper
|
/** asprintf wrapper
|
||||||
* \param strp target string
|
* \param strp target string
|
||||||
* \param fmt format
|
* \param fmt format
|
||||||
|
|||||||
@ -86,6 +86,7 @@ str_to_position(char *str)
|
|||||||
|
|
||||||
void *xmalloc(size_t nmemb, size_t size);
|
void *xmalloc(size_t nmemb, size_t size);
|
||||||
void *xcalloc(size_t nmemb, size_t size);
|
void *xcalloc(size_t nmemb, size_t size);
|
||||||
|
void *xrealloc(void *ptr, size_t nmemb, size_t size);
|
||||||
int xasprintf(char **strp, const char *fmt, ...);
|
int xasprintf(char **strp, const char *fmt, ...);
|
||||||
char *xstrdup(const char *str);
|
char *xstrdup(const char *str);
|
||||||
pid_t spawn(const char *format, ...);
|
pid_t spawn(const char *format, ...);
|
||||||
|
|||||||
@ -292,6 +292,13 @@ struct launcher
|
|||||||
SLIST_ENTRY(launcher) next;
|
SLIST_ENTRY(launcher) next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct launcher_ccache
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
char **namelist;
|
||||||
|
size_t hits;
|
||||||
|
};
|
||||||
|
|
||||||
struct _systray
|
struct _systray
|
||||||
{
|
{
|
||||||
struct geo geo;
|
struct geo geo;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user