-
Notifications
You must be signed in to change notification settings - Fork 0
/
fileCompleterExample.cpp
172 lines (142 loc) · 5.12 KB
/
fileCompleterExample.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <histedit.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "completer.h"
#include <vector>
#include <string>
using namespace std;
/// example of a filename completer.
// This is an undocumented function in libedit. It takes a
// filename and expands any ~ (user-relative) names, allocating
// a new string for the result. Because it's an undocumented function
// it might change!
extern "C" {
char *fn_tilde_expand(const char *txt);
}
// Editline uses this to get the prompt.
const char *getPrompt(){
return ">> ";
}
// This is the completion iterator.
class FileAutocompleteIterator : public completer::Iterator {
// list of completion candidates.
vector<string> data;
// our current position in that list.
vector<string>::iterator it;
// add the contents of a directory to the list of completion
// candidates.
void addDirContents(string prefix,const char *dir,
const char *match,int matchlen){
DIR *d = opendir(dir);
if(d){
while(dirent *e = readdir(d)){
if(!match || !strncmp(match,e->d_name,matchlen)){
// printf("added %s\n",e->d_name);
if(e->d_type == DT_DIR){
// add with trailing slash if a dir
data.push_back(prefix+string(e->d_name)+"/");
} else {
// otherwise just add
data.push_back(prefix+string(e->d_name));
}
}
}
}
}
public:
// construct the list of possible matches
virtual void first(const char *stringstart,int length){
data.clear(); // discard existing data
const char *ptr;
if(length){
// find the last slash
for(ptr=stringstart+length-1;ptr>stringstart;--ptr){
if(*ptr=='/')break;
}
int len = ptr-stringstart;
if(!len) {
// there is no slash, so no directory element, match
// against files in the current working directory
addDirContents("",".",stringstart,length);
} else {
// there is a directory element, add the matching
// contents - get the file element to match against
string dir = string(stringstart,len);
addDirContents(dir+"/",
dir.c_str(),
ptr+1,length-len-1);
}
} else {
// a zero length argument, just add everything in the current
// directory
addDirContents("",".",NULL,0);
}
// if an unambiguous match, add a space if it doesn't
// end with a slash.
if(data.size()==1){
string& s = data[0];
if(s.length() && s[s.length()-1]!='/')
s+=" ";
}
it = data.begin();
}
// iterate the list of possible matches
virtual const char *next(){
if(it == data.end())return NULL;
return (*it++).c_str();
}
// This gets called before anything else happens and may replace
// the entire word. This returns a string which must be freed, or NULL.
// Here it is used to perform tilde expansion on filenames.
virtual const char *modString(const char *stringstart,int len){
if(len && *stringstart=='~'){
// make a null-terminated copy of the word
char *tmp = (char *)malloc(len+1);
memcpy(tmp,stringstart,len);
tmp[len]=0;
// pass it to tilde expansion, free the buffer, return result
// which the caller must free.
const char *ret = fn_tilde_expand(tmp);
free(tmp);
return ret;
} else
return NULL;
}
// this iterator does its own padding (adding space or slash)
// on unambiguous matches, so we turn off automatic space
// addition at the end of those.
virtual bool doSpacePadding(){return false;}
};
int main(int argc,char *argv[]){
// basic EditLine and history initialisation
EditLine *el = el_init(argv[0],stdin,stdout,stderr);
el_set(el,EL_PROMPT,&getPrompt);
el_set(el,EL_EDITOR,"emacs");
History *hist = history_init();
HistEvent ev;
history(hist,&ev,H_SETSIZE,800);
el_set(el,EL_HIST,history,hist);
// create our file iterator, set up the completer with it
// and tell the completer which characters are word separators.
FileAutocompleteIterator fileiter;
completer::setup(el,&fileiter,"\t\n ");
// the main loop
for(;;){
// standard editline read
int count;
const char *line = el_gets(el,&count);
// if we got a result, print it out (note that editline
// returns string with a newline at the end)
if(line)
printf("FILE: %s",line);
else break;
}
completer::shutdown(el);
return 0;
}