tab_completion.pmod
Go to the documentation of this file.
1 inherit Tools.Hilfe.Evaluator;
2 inherit Tools.Hilfe;
3 
4  mapping myvars = ([ ]);
5  object server_fp;
6  object mainhandler;
7  Stdio.Readline readln = Stdio.Readline(Stdio.stdin);
8  mapping(string:mixed) constants = constants; //fetching Evaluator constants
9 
10 mapping base_objects(Evaluator e)
11 {
12  return all_constants() + e->constants + e->variables + myvars;
13 }
14 
15 void set_server_filepath(object o)
16 {
17  server_fp = o;
18 }
19 
20 array(object|array(string)) resolv(Evaluator e, array completable,
21  void|object base, void|string type)
22 {
23  if (e->variables->DEBUG_COMPLETIONS)
24  e->safe_write("resolv(%O, %O, %O)\n", completable, base, type);
25  if (!sizeof(completable))
26  return ({ base, completable, type });
27  if (stringp(completable[0]) &&
28  completable[0] == array_sscanf(completable[0], "%[ \t\r\n]")[0])
29  return ({ base, completable[1..], type });
30  if (typeof_token(completable[0]) == "argumentgroup" && type != "autodoc")
31  return resolv(e, completable, master()->show_doc(base), "autodoc");
32  int flag2 = 0;
33  if(!base && completable[0]==".")
34  {
35  if (sizeof(completable) == 1)
36  return ({ 0, completable, "module" });
37  else
38  {
39  catch
40  {
41  // quick and dirty attempt to load a local module
42  base=compile_string(sprintf("object o=.%s;", completable[1]), 0)()->o;
43  };
44  if (!base)
45  return ({ 0, completable, type });
46  if (objectp(base))
47  return resolv(e, completable[2..], base, "module");
48  return resolv(e, completable[2..], base);
49  }
50  }
51  if (!base && sizeof(completable) > 1)
52  {
53  if (completable[0] == "master" && sizeof(completable) >=2
54  && typeof_token(completable[1]) == "argumentgroup")
55  {
56  return resolv(e, completable[2..], master(), "object");
57  }
58  if (base=base_objects(e)[completable[0]])
59  {
60  return resolv(e, completable[1..], base, "object");
61  }
62  if ((sizeof(completable) > 1)
63  && (base=master()->root_module[completable[0]]))
64  return resolv(e, completable[1..], base, "module");
65  return ({ 0, completable, type });
66  }
67 
68  if (sizeof(completable) > 1)
69  {
70  object newbase;
71  if (reference[completable[0]] && sizeof(completable) > 1)
72  return resolv(e, completable[1..], base);
73  if (type == "autodoc")
74  {
75  if (typeof_token(completable[0]) == "symbol"
76  && (newbase = base->findObject(completable[0])))
77  return resolv(e, completable[1..], newbase, type);
78  else if (sizeof(completable) > 2
79  && typeof_token(completable[0]) == "argumentgroup"
80  && typeof_token(completable[1]) == "reference")
81  return resolv(e, completable[2..], base, type);
82  else
83  return ({ base, completable, type });
84  }
85  if (!functionp(base) && (newbase=base[completable[0]]) )
86  return resolv(e, completable[1..], newbase, type);
87  }
88  return ({ base, completable, type });
89 }
90 
91 
92  array get_resolvable(array tokens, void|int debug)
93  {
94 
95  array completable = ({});
96  string tokentype;
97 
98  foreach(reverse(tokens);; string token)
99  {
100  string _tokentype = typeof_token(token);
101 
102  if (debug)
103  write(sprintf("%O = %s\n", token, _tokentype));
104 
105  if ( ( _tokentype == "reference" &&
106  (!tokentype || tokentype == "symbol"))
107  || (_tokentype == "symbol" && (!tokentype
108  || (< "reference", "referencegroup",
109  "argumentgroup" >)[tokentype]))
110  || ( (<"argumentgroup", "referencegroup" >)[_tokentype]
111  && (!tokentype || tokentype == "reference"))
112  )
113  {
114  completable += ({ token });
115  tokentype = _tokentype;
116  }
117  else if (_tokentype == "whitespace")
118  ;
119  else
120  break;
121  }
122 
123  // keep the last whitespace
124  if (arrayp(tokens) && sizeof(tokens) &&
125  typeof_token(tokens[-1]) == "whitespace")
126  completable = ({ " " }) + completable;
127  return reverse(completable);
128  }
129 
130 
131  void set(mapping vars)
132  {
133  myvars = vars;
134  }
135 
136  void load_hilferc() {
137  if(string home=getenv("HOME")||getenv("USERPROFILE"))
138  if(string s=Stdio.read_file(home+"/.hilferc"))
139  map(s/"\n", add_buffer);
140  }
141 
142 
143  array tokenize(string input)
144  {
145  array tokens = Parser.Pike.split(input);
146  if (variables->DEBUG_COMPLETIONS)
147  readln->message(sprintf("\n\ntokenize(%O): %O\n\n", input, tokens));
148  // drop the linebreak that split appends
149  if (tokens[-1] == "\n")
150  tokens = tokens[..<1];
151  else if (tokens[-1][-1] == '\n')
152  tokens[-1] = tokens[-1][..<1];
153 
154  tokens = Parser.Pike.group(tokens);
155  return tokens;
156  }
157 
158 
159 string input;
160  void handle_completions(string key)
161  {
162 // write("KEY IS : "+key);
163  mixed old_handler = master()->get_inhibit_compile_errors();
164  HilfeCompileHandler handler = HilfeCompileHandler(sizeof(backtrace()));
165  master()->set_inhibit_compile_errors(handler);
166 
167  array tokens;
168  input = readln->gettext()[..readln->getcursorpos()-1];
169  // write("INPUT IS "+input);
170  array|string completions;
171 
172  mixed error = catch
173  {
174  tokens = tokenize(input);
175  };
176 // write("Tokens are : %O",tokens);
177 
178  int flag = 0;
179  if(error)
180  {
181  if (objectp(error) && error->is_unterminated_string_error)
182  {
183  // THE set_attribute and get_attribute tab completion is here
184 
185  error = catch
186  {
187  array toks=({ });
188  mixed error5 = catch{
189  toks = tokenize(input+"\")");
190  };
191  if(error5==0)
192  {
193  int b = 0;
194  b=search(toks,"set_attribute");
195  if(b!=(sizeof(toks)-2)) //second last token is set_attribute, last token is the string inside with double quotes.
196  b=search(toks,"query_attribute");
197  if(b==(sizeof(toks)-2)) //because if set/query_attribute is at the end it will be second last token, which is -2
198  {
199  if(b!=-1) //not needed
200  {
201  if((myvars[toks[b-2]])) //usually it is like x->y->z->set_attribute(""), so z will be our const/var which is -2 from loc.
202  {
203  completions = indices(base_objects(this)[toks[b-2]]->get_attributes());
204  string rest = toks[b+1][1]-"\"";
205  if(rest!="")
206  {
207  array(string) temp = ({ });
208  int i = 0;
209  int l = sizeof(rest);
210  foreach(completions, string str)
211  {
212  if(str[0 .. (l-1)]==rest)
213  {
214  temp = temp + ({str});
215  i++;
216  }
217  }
218  if(sizeof(temp)==1)
219  completions = (string)(temp[0]-rest);
220  else
221  completions = temp;
222  }
223  flag = 1;
224  }
225  }
226  }
227  }
228  if(flag==0)
229  {
230  mixed outer_error = catch{
231  array tokens3 = ({ });
232  mixed error = catch{
233  tokens3 = tokenize(input+"\")");
234  };
235  if(error==0)
236  {
237  int a = 0;
238  if((a=search(tokens3,"OBJ"))!=-1)
239  {
240  int b = a+1;
241  if(arrayp(tokens3[b]))
242  {
243  completions = show_path_completions(tokens3[a+1][1]);
244  }
245  else
246  throw("no toks[a+1]\n");
247  }
248  }
249  };
250  if(outer_error)
251  completions = get_file_completions((input/"\"")[-1]);
252  }
253  };
254  }
255 
256  if (error)
257  {
258  if(!objectp(error))
259  error = Error.mkerror(error);
260  readln->message(sprintf("%s\nAn error occurred, attempting to complete your input!\nPlease include the backtrace above and the line below in your report:\ninput: %s\n", error->describe(), input));
261  completions = ({});
262  }
263  }
264  if (tokens && !completions)
265  {
266  array completable = get_resolvable(tokens, variables->DEBUG_COMPLETIONS);
267 
268  if (completable && sizeof(completable))
269  {
270  error = catch
271  {
272  completions = get_module_completions(completable);
273  };
274  error = Error.mkerror(error);
275  }
276  else if (!tokens || !sizeof(tokens))
277  completions = sort(indices(master()->root_module)) +
278  sort(indices(base_objects(this)));
279  // FIXME: base_objects should not be sorted like this
280 
281  if (!completions || !sizeof(completions))
282  {
283  string token = tokens[-1];
284  if( sizeof(tokens) >= 2 && typeof_token(token) == "whitespace" )
285  token = tokens[-2];
286 
287  if (variables->DEBUG_COMPLETIONS)
288  readln->message(sprintf("type: %s\n", typeof_token(token)));
289 
290  completions = sort(indices(master()->root_module)) +
291  sort(indices(base_objects(this)));
292 
293 
294  switch(typeof_token(token))
295  {
296  case "symbol":
297  case "literal":
298  case "postfix":
299  completions = (array)(infix+seperator);
300  break;
301  case "prefix":
302  case "infix":
303  case "seperator":
304  default:
305  completions += (array)prefix;
306  }
307  foreach(reverse(tokens);; string token)
308  {
309  if (group[token])
310  {
311  completions += ({ group[token] }) ;
312  break;
313  }
314  }
315  }
316 
317  if (error)
318  {
319  readln->message(sprintf("%s\nAn error occurred, attempting to complete your input!\nPlease include the backtrace above and the lines below in your report:\ninput: %s\ntokens: %O\ncompletable: %O\n", error->describe(), input, tokens, completable, ));
320  }
321  else if (variables->DEBUG_COMPLETIONS)
322  readln->message(sprintf("input: %s\ntokens: %O\ncompletable: %O\ncompletions: %O\n", input, tokens, completable, completions));
323  }
324  handler->show_errors();
325  handler->show_warnings();
326  master()->set_inhibit_compile_errors(old_handler);
327 // write("Completions are %O",completions);
328  if(completions && sizeof(completions))
329  {
330  if(stringp(completions))
331  {
332  readln->insert(completions, readln->getcursorpos());
333  }
334  else
335  {
336  readln->list_completions(completions);
337  }
338  }
339  }
340 
341 mixed show_path_completions(string cur_path)
342 {
343  cur_path = cur_path - "\""; //trimming "s in suppose "/"-> /
344  mixed demo = ({ });
345  if(cur_path=="")
346  {
347  demo = "/";
348  }
349  else
350  {
351  string rest="";
352  array parts = ({});
353  parts = cur_path/"/";
354  if(sizeof(parts)!=2)
355  {
356  cur_path = parts[0 .. sizeof(parts)-2]*"/";
357  rest = parts[-1];
358  }
359  else
360  {
361  cur_path="/";
362  rest = parts[1];
363  }
364  object a;
365  mixed error2 = catch{
366  a = server_fp->path_to_object(cur_path);
367  };
368  array objs = ({ });
369  mixed error= catch{
370  string x_s="";
371  objs=a->get_inventory();
372  foreach(objs, object x)
373  {
374  if(cur_path!="/")
375  x_s = x->query_attribute("OBJ_PATH") - cur_path - "/";
376  else
377  x_s = x->query_attribute("OBJ_PATH") - "/";
378  mixed error3 = catch{
379  if(x_s[0 .. sizeof(rest)-1]==rest)
380  demo = demo + ({ x_s });
381  };
382  }
383  if(sizeof(demo)==1)
384  {
385  demo = demo[0]-(rest);
386  if(demo=="")
387  demo = "/";
388  }
389  else if(sizeof(demo)!=0)
390  {
391  string common = String.common_prefix(demo);
392  if((common!="")&&((common-rest)!=""))
393  demo = common-(rest);
394  }
395  };
396  }
397  return demo;
398 }
399 
400 mapping reftypes = ([ "module":".",
401  "object":"->",
402  "mapping":"->",
403  "function":"(",
404  "program":"(",
405  "method":"(",
406  "class":"(",
407  ]);
408 
409 array low_get_module_completions(array completable, object base, void|string type, void|int(0..1) space)
410 {
411  if (variables->DEBUG_COMPLETIONS)
412  safe_write(sprintf("low_get_module_completions(%O\n, %O, %O, %O)\n", completable, base, type, space));
413 
414  if (!completable)
415  completable = ({});
416 
417  mapping other = ([]);
418  array modules = ({});
419  mixed error;
420 
421  if (base && !sizeof(completable))
422  {
423  if (space)
424  return (array)infix;
425  if (type == "autodoc")
426  return ({ reftypes[base->objtype||base->objects[0]->objtype]||"" });
427  if (objectp(base))
428  return ({ reftypes[type||"object"] });
429  if (mappingp(base))
430  return ({ reftypes->object });
431  else if(functionp(base))
432  return ({ reftypes->function });
433  else if (programp(base))
434  return ({ reftypes->program });
435  else
436  return (array)infix;
437  }
438 
439  if (!base && sizeof(completable) && completable[0] == ".")
440  {
441  array modules = sort(get_dir("."));
442  if (sizeof(completable) > 1)
443  {
444  modules = Array.filter(modules, has_prefix, completable[1]);
445  if (sizeof(modules) == 1)
446  return ({ (modules[0]/".")[0][sizeof(completable[1])..] });
447  string prefix = String.common_prefix(modules)[sizeof(completable[1])..];
448  if (prefix)
449  return ({ prefix });
450 
451  if (sizeof(completable) == 2)
452  return modules;
453  else
454  return ({});
455  }
456  else
457  return modules;
458  }
459  else if (!base)
460  {
461  if (type == "autodoc")
462  {
463  if (variables->DEBUG_COMPLETIONS)
464  safe_write("autodoc without base\n");
465  return ({});
466  }
467  other = base_objects(this);
468  base = master()->root_module;
469  }
470 
471  if (type == "autodoc")
472  {
473  if (base->docGroups)
474  modules = Array.uniq(Array.flatten(base->docGroups->objects->name));
475  else
476  return ({});
477  }
478  else
479  {
480  error = catch
481  {
482  modules = sort(indices(base));
483  };
484  error = Error.mkerror(error);
485  }
486 
487  if (sizeof(other))
488  modules += indices(other);
489 
490  if (sizeof(completable) == 1)
491  {
492  if (type == "autodoc"
493  && typeof_token(completable[0]) == "argumentgroup")
494  if (space)
495  return (array)infix;
496  else
497  return ({ reftypes->object });
498  if (reference[completable[0]])
499  return modules;
500  if (!stringp(completable[0]))
501  return ({});
502 
503 
504  modules = sort((array(string))modules);
505  modules = Array.filter(modules, has_prefix, completable[0]);
506  string prefix = String.common_prefix(modules);
507  string module;
508 
509  if (prefix == completable[0] && sizeof(modules)>1 && (base[prefix]||other[prefix]))
510  return modules + low_get_module_completions(({}), base[prefix]||other[prefix], type, space);
511 
512  prefix = prefix[sizeof(completable[0])..];
513  if (sizeof(prefix))
514  return ({ prefix });
515 
516  if (sizeof(modules)>1)
517  return modules;
518  else if (!sizeof(modules))
519  return ({});
520  else
521  {
522  module = modules[0];
523  modules = ({});
524  object thismodule;
525 
526  if(other && other[module])
527  {
528  thismodule = other[module];
529  type = "object";
530  }
531  else if (intp(base[module]) || floatp(base[module]) || stringp(base[module]) )
532  return (array)infix;
533  else
534  {
535  thismodule = base[module];
536  if (!type)
537  type = "module";
538  }
539 
540  return low_get_module_completions(({}), thismodule, type, space);
541  }
542  }
543 
544  if (completable && sizeof(completable))
545  {
546  if ( (< "reference", "argumentgroup" >)[typeof_token(completable[0])])
547  return low_get_module_completions(completable[1..], base, type||reference[completable[0]], space);
548  else
549  safe_write(sprintf("UNHANDLED CASE: completable: %O\nbase: %O\n", completable, base));
550  }
551 
552  return modules;
553  }
554 
555  array|string get_module_completions(array completable)
556  {
557  array rest = completable;
558  object base;
559  string type;
560  int(0..1) space;
561 
562  if (!completable)
563  completable = ({});
564 
565  if (completable[-1]==' ')
566  {
567  space = true;
568  completable = completable[..<1];
569  }
570 
571  if (sizeof(completable) > 1)
572  [base, rest, type] = resolv(this, completable);
573 
574  if (variables->DEBUG_COMPLETIONS)
575  safe_write(sprintf("get_module_completions(%O): %O, %O, %O\n", completable, base, rest, type));
576  array completions = low_get_module_completions(rest, base, type, space);
577  if (sizeof(completions) == 1)
578  return completions[0];
579  else
580  return completions;
581  }
582  array|string get_file_completions(string path)
583  {
584  array files = ({});
585  if ( (< "", ".", ".." >)[path-"../"] )
586  files += ({ ".." });
587 
588  if (!sizeof(path) || path[0] != '/')
589  path = "./"+path;
590 
591  string dir = dirname(path);
592  string file = basename(path);
593  catch
594  {
595  files += get_dir(dir);
596  };
597 
598  if (!sizeof(files))
599  return ({});
600 
601  array completions = Array.filter(files, has_prefix, file);
602  string prefix = String.common_prefix(completions)[sizeof(file)..];
603 
604  if (sizeof(prefix))
605  {
606  return prefix;
607  }
608 
609  mapping filetypes = ([ "dir":"/", "lnk":"@", "reg":"" ]);
610 
611  if (sizeof(completions) == 1 && file_stat(dir+"/"+completions[0])->isdir )
612  {
613  return "/";
614  }
615  else
616  {
617  foreach(completions; int count; string item)
618  {
619  Stdio.Stat stat = file_stat(dir+"/"+item);
620  if (stat)
621  completions[count] += filetypes[stat->type]||"";
622 
623  stat = file_stat(dir+"/"+item, 1);
624  if (stat->type == "lnk")
625  completions[count] += filetypes["lnk"];
626  }
627  return completions;
628  }
629  }
630