Skip to content

Commit 55aaa49

Browse files
committed
Add special-case completion logic for first-position
1 parent 4621be7 commit 55aaa49

File tree

6 files changed

+93
-12
lines changed

6 files changed

+93
-12
lines changed

access.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* access.c -- access testing and path searching ($Revision: 1.2 $) */
22

3-
#define REQUIRE_STAT 1
43
#define REQUIRE_PARAM 1
54

65
#include "es.h"
@@ -40,7 +39,7 @@ static Boolean ingroupset(gidset_t gid) {
4039
return FALSE;
4140
}
4241

43-
static int testperm(struct stat *stat, unsigned int perm) {
42+
extern int testperm(struct stat *stat, unsigned int perm) {
4443
unsigned int mask;
4544
static gidset_t uid, gid;
4645
static Boolean initialized = FALSE;

es.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ extern void setnoexport(List *list);
198198
extern void addtolist(void *arg, char *key, void *value);
199199
extern List *listvars(Boolean internal);
200200
extern List *varswithprefix(char *prefix);
201+
extern List *fnswithprefix(char *prefix);
201202

202203
typedef struct Push Push;
203204
extern Push *pushlist;
@@ -216,6 +217,7 @@ extern void printstatus(int pid, int status);
216217

217218
/* access.c */
218219

220+
extern int testperm(struct stat *stat, unsigned int perm);
219221
extern char *checkexecutable(char *file);
220222

221223

@@ -283,7 +285,7 @@ extern void *erealloc(void *p, size_t n);
283285
extern void efree(void *p);
284286
extern void ewrite(int fd, const char *s, size_t n);
285287
extern long eread(int fd, char *buf, size_t n);
286-
extern Boolean isabsolute(char *path);
288+
extern Boolean isabsolute(const char *path);
287289
extern Boolean streq2(const char *s, const char *t1, const char *t2);
288290

289291

input.c

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -529,9 +529,68 @@ static char *list_completion_function(const char *text, int state) {
529529
return result;
530530
}
531531

532-
char **builtin_completion(const char *text, int UNUSED start, int UNUSED end) {
533-
char **matches = NULL;
532+
/* detect if we're currently at the start of a line. works ~80% well */
533+
static Boolean cmdstart(int point) {
534+
int i;
535+
Boolean quote = FALSE, start = TRUE;
536+
for (i = 0; i < point; i++) {
537+
char c = rl_line_buffer[i];
538+
switch (c) {
539+
case '\'':
540+
quote = !quote;
541+
break;
542+
case '&': case '|': case '{': case '`':
543+
if (!quote) start = TRUE;
544+
break;
545+
/* \n doesn't work right :( */
546+
case ' ': case '\t': case '\n': case '!':
547+
break;
548+
default:
549+
if (!quote) start = FALSE;
550+
}
551+
}
552+
return start;
553+
}
534554

555+
/* first-position completion. includes
556+
* - built-ins: local let for fn %closure match
557+
* - functions
558+
* - if absolute (including home), absolute executable files
559+
*/
560+
static List *listexecutables(char *text) {
561+
int i = 0;
562+
char *s;
563+
List *compl = NULL;
564+
static char *builtins[] = {"local", "let", "for", "fn", "%closure", "match"};
565+
for (i = 0; i < 6; i++)
566+
if (strneq(text, builtins[i], strlen(text)))
567+
compl = mklist(mkstr(builtins[i]), compl);
568+
569+
compl = append(fnswithprefix(text), compl);
570+
571+
if (isabsolute(text) || *text == '~') {
572+
i = 0;
573+
/* goofy hack :) */
574+
while ((s = rl_filename_completion_function(text, i)) != NULL) {
575+
i = 1;
576+
struct stat st;
577+
/* TODO: ~/foo doesn't stat(), so don't try */
578+
if (*s != '~') {
579+
if (stat(s, &st) == -1)
580+
continue;
581+
/* 1 == EXEC */
582+
if (testperm(&st, 1) != 0)
583+
continue;
584+
}
585+
/* TODO: recurse in directories? */
586+
compl = mklist(mkstr(s), compl);
587+
}
588+
}
589+
return compl;
590+
}
591+
592+
char **builtin_completion(const char *text, int start, int UNUSED end) {
593+
/* variable or primitive completion */
535594
if (*text == '$') {
536595
wordslistgen = varswithprefix;
537596
complprefix = "$";
@@ -543,14 +602,22 @@ char **builtin_completion(const char *text, int UNUSED start, int UNUSED end) {
543602
case '^': complprefix = "$^"; break;
544603
case '#': complprefix = "$#"; break;
545604
}
546-
matches = rl_completion_matches(text, list_completion_function);
605+
return rl_completion_matches(text, list_completion_function);
547606
}
548607

549608
/* ~foo => username. ~foo/bar already gets completed as filename. */
550-
if (!matches && *text == '~' && !strchr(text, '/'))
551-
matches = rl_completion_matches(text, rl_username_completion_function);
609+
if (*text == '~' && !strchr(text, '/'))
610+
return rl_completion_matches(text, rl_username_completion_function);
611+
612+
/* first-word completion, which is ~special~ */
613+
if (cmdstart(start)) {
614+
wordslistgen = listexecutables;
615+
complprefix = "";
616+
rl_attempted_completion_over = 1;
617+
return rl_completion_matches(text, list_completion_function);
618+
}
552619

553-
return matches;
620+
return NULL; /* fall back to normal filename completion */
554621
}
555622
#endif /* HAVE_READLINE */
556623

stdenv.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@
4444
#include <sys/ioctl.h>
4545
#endif
4646

47-
#if REQUIRE_STAT
4847
#include <sys/stat.h>
49-
#endif
5048

5149
#if REQUIRE_DIRENT
5250
#if HAVE_DIRENT_H

util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extern void uerror(char *s) {
3434
}
3535

3636
/* isabsolute -- test to see if pathname begins with "/", "./", or "../" */
37-
extern Boolean isabsolute(char *path) {
37+
extern Boolean isabsolute(const char *path) {
3838
return path[0] == '/'
3939
|| (path[0] == '.' && (path[1] == '/'
4040
|| (path[1] == '.' && path[2] == '/')));

var.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ static void listwithprefix(void *arg, char *key, void *value) {
357357
addtolist(arg, key, value);
358358
}
359359

360+
static void fnwithprefix(void *arg, char *key, void *value) {
361+
if (strneq(key, "fn-", 3) &&
362+
strneq(key + 3, list_prefix, strlen(list_prefix)))
363+
addtolist(arg, key + 3, value);
364+
}
365+
360366
/* listvars -- return a list of all the (dynamic) variables */
361367
extern List *listvars(Boolean internal) {
362368
Ref(List *, varlist, NULL);
@@ -365,6 +371,15 @@ extern List *listvars(Boolean internal) {
365371
RefReturn(varlist);
366372
}
367373

374+
/* fnswithprefix -- return a list of all the (dynamic) functions
375+
* matching the given prefix */
376+
extern List *fnswithprefix(char *prefix) {
377+
Ref(List *, fnlist, NULL);
378+
list_prefix = prefix;
379+
dictforall(vars, fnwithprefix, &fnlist);
380+
RefReturn(fnlist);
381+
}
382+
368383
/* varswithprefix -- return a list of all the (dynamic) variables
369384
* matching the given prefix */
370385
extern List *varswithprefix(char *prefix) {

0 commit comments

Comments
 (0)