Annotation of OpenXM_contrib/pari/src/gp/gp_rl.c, Revision 1.1.1.1
1.1 maekawa 1: /*******************************************************************/
2: /* */
3: /* INTERFACE TO READLINE COMPLETION */
4: /* */
5: /*******************************************************************/
6: /* $Id: gp_rl.c,v 1.2 1999/09/20 16:39:33 karim Exp $ */
7: #include "pari.h"
8: #include "../language/anal.h"
9: #include "gp.h"
10:
11: #ifdef READLINE
12: typedef char** (*CF)(char*, char* (*)()); /* completion function */
13: typedef char* (*GF)(char*, int); /* generator function */
14: typedef int (*RLCI)(int, int); /* rl_complete and rl_insert functions */
15:
16: BEGINEXTERN
17: #ifdef HAS_RL_MESSAGE
18: # define USE_VARARGS
19: # define PREFER_STDARG
20: #endif
21: #ifdef READLINE_LIBRARY
22: # include <readline.h>
23: #else
24: # include <readline/readline.h>
25: #endif
26: #ifndef HAS_RL_MESSAGE
27: extern int rl_message (const char *, ...);
28: extern int rl_clear_message();
29: extern int rl_begin_undo_group(), rl_end_undo_group();
30: extern int rl_read_key();
31: extern int rl_stuff_char();
32: extern char *filename_completion_function(char *text,int state);
33: extern char *username_completion_function(char *text,int state);
34: #endif
35: char **pari_completion(char *text, int start, int end);
36: extern int rl_completion_query_items;
37: extern int rl_bind_key_in_map();
38: ENDEXTERN
39:
40: void print_fun_list(char **matches, int nbli);
41: void aide(char *s, int flag);
42:
43: extern default_type gp_default_list[];
44: extern char *keyword_list[];
45: static int add_help_keywords;
46: static entree *current_ep = NULL;
47:
48: static int pari_rl_back;
49: extern RLCI rl_last_func;
50: static int do_args_complete = 1;
51: static int do_matched_insert = 0;
52: static int did_init_matched = 0;
53:
54: #ifdef HAS_RL_SAVE_PROMPT
55: # define SAVE_PROMPT() rl_save_prompt()
56: # define RESTORE_PROMPT() rl_restore_prompt()
57: #else
58: # ifdef HAS_UNDERSCORE_RL_SAVE_PROMPT
59: # define SAVE_PROMPT() _rl_save_prompt()
60: # define RESTORE_PROMPT() _rl_restore_prompt()
61: # else
62: # define SAVE_PROMPT()
63: # define RESTORE_PROMPT()
64: # endif
65: #endif
66:
67: #define ELECTRIC_PAREN 1
68: #define ARGS_COMPLETE 2
69: static int
70: change_state(char *msg, int *opt, int count)
71: {
72: int c;
73:
74: switch(count)
75: {
76: case 0: c = 0; break; /* off */
77: case -1: c = 1; break; /* on */
78: case -2: c = 1 - *opt; /* toggle */
79: }
80: *opt = c;
81: SAVE_PROMPT();
82: rl_message("%s: %s.", (long)msg, (long)(c? "on": "off"));
83: c = rl_read_key();
84: RESTORE_PROMPT();
85: rl_clear_message();
86: rl_stuff_char(c); return 1;
87: }
88:
89: /* Wrapper around rl_complete to allow insertion of () with a point in
90: between. */
91: static int
92: pari_rl_complete(int count, int key)
93: {
94: int ret;
95:
96: pari_rl_back = 0;
97: if (count <= 0)
98: return change_state("complete args", &do_args_complete, count);
99:
100: rl_begin_undo_group();
101: if (rl_last_func == pari_rl_complete)
102: rl_last_func = (RLCI) rl_complete; /* Make repeated TABs different */
103: ret = ((RLCI)rl_complete)(count,key);
104: if (pari_rl_back && (pari_rl_back <= rl_point))
105: rl_point -= pari_rl_back;
106: rl_end_undo_group(); return ret;
107: }
108:
109: static const char paropen[] = "([{";
110: static const char parclose[] = ")]}";
111:
112: /* To allow insertion of () with a point in between. */
113: static int
114: pari_rl_matched_insert(int count, int key)
115: {
116: int i = 0, ret;
117:
118: if (count <= 0)
119: return change_state("electric parens", &do_matched_insert, count);
120: while (paropen[i] && paropen[i] != key) i++;
121: if (!paropen[i] || !do_matched_insert || under_emacs)
122: return ((RLCI)rl_insert)(count,key);
123: rl_begin_undo_group();
124: ((RLCI)rl_insert)(count,key);
125: ret = ((RLCI)rl_insert)(count,parclose[i]);
126: rl_point -= count;
127: rl_end_undo_group(); return ret;
128: }
129:
130: static int
131: pari_rl_default_matched_insert(int count, int key)
132: {
133: if (!did_init_matched) {
134: did_init_matched = 1;
135: do_matched_insert = 1;
136: }
137: return pari_rl_matched_insert(count, key);
138: }
139:
140: static int
141: pari_rl_forward_sexp(int count, int key)
142: {
143: int deep = 0, dir = 1, move_point, lfail;
144:
145: if (count < 0)
146: {
147: count = -count; dir = -1;
148: if (!rl_point) goto fail;
149: rl_point--;
150: }
151: while (count || deep)
152: {
153: move_point = 1; /* Need to move point if moving left. */
154: lfail = 0; /* Do not need to fail left movement yet. */
155: while ( !is_keyword_char(rl_line_buffer[rl_point])
156: && !strchr("\"([{}])",rl_line_buffer[rl_point])
157: && !( (dir == 1)
158: ? (rl_point >= rl_end)
159: : (rl_point <= 0 && (lfail = 1))))
160: rl_point += dir;
161: if (lfail || !rl_line_buffer[rl_point]) goto fail;
162:
163: if (is_keyword_char(rl_line_buffer[rl_point]))
164: {
165: while ( is_keyword_char(rl_line_buffer[rl_point])
166: && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
167: || (move_point = 0)))
168: rl_point += dir;
169: if (!deep) count--;
170: }
171: else if (strchr(paropen,rl_line_buffer[rl_point]))
172: {
173: if (deep == 0 && dir == -1) goto fail; /* We are already out of pars. */
174: rl_point += dir;
175: deep++; if (!deep) count--;
176: }
177: else if (strchr(parclose,rl_line_buffer[rl_point]))
178: {
179: if (deep == 0 && dir == 1)
180: {
181: rl_point++; goto fail; /* Get out of pars. */
182: }
183: rl_point += dir;
184: deep--; if (!deep) count--;
185: }
186: else if (rl_line_buffer[rl_point] == '\"')
187: {
188: int bad = 1;
189:
190: rl_point += dir;
191: while ( ((rl_line_buffer[rl_point] != '\"') || (bad = 0))
192: && (!((dir == 1) ? (rl_point >= rl_end) : (rl_point <= 0))
193: || (move_point = 0)) )
194: rl_point += dir;
195: if (bad) goto fail;
196: rl_point += dir; /* Skip the other delimiter */
197: if (!deep) count--;
198: }
199: else
200: {
201: fail: ding(); return 1;
202: }
203: }
204: if (dir != 1 && move_point) rl_point++;
205: return 1;
206: }
207:
208: static int
209: pari_rl_backward_sexp(int count, int key)
210: {
211: return pari_rl_forward_sexp(-count, key);
212: }
213:
214: /* do we add () at the end of completed word? (is it a function?) */
215: static int
216: add_paren(int end)
217: {
218: entree *ep;
219: char *s;
220:
221: if (end < 0 || rl_line_buffer[end] == '(')
222: return 0; /* not from command_generator or already there */
223: ep = do_alias(current_ep); /* current_ep set in command_generator */
224: if (EpVALENCE(ep) < EpUSER)
225: { /* is it a constant masked as a function (e.g Pi)? */
226: s = ep->help; if (!s) return 1;
227: while (is_keyword_char(*s)) s++;
228: return (*s != '=');
229: }
230: switch(EpVALENCE(ep))
231: {
232: case EpUSER:
233: case EpINSTALL: return 1;
234: }
235: return 0;
236: }
237:
238: static void
239: match_concat(char **matches, char *s)
240: {
241: int i = strlen(matches[0]) + 1;
242: matches[0] = (char*) gprealloc(matches[0], i+strlen(s), i);
243: strcat(matches[0],s);
244: }
245:
246: static char **
247: matches_for_emacs(char *text, char **matches)
248: {
249: if (!matches) printf("@");
250: else
251: {
252: int i;
253: printf("%s@", matches[0] + strlen(text));
254: if (matches[1]) print_fun_list(matches+1,0);
255:
256: /* we don't want readline to do anything, but insert some junk
257: * which will be erased by emacs.
258: */
259: for (i=0; matches[i]; i++) free(matches[i]);
260: free(matches);
261: }
262: matches = (char **) gpmalloc(2*sizeof(char *));
263: matches[0] = gpmalloc(2); sprintf(matches[0],"_");
264: matches[1] = NULL; printf("@E_N_D"); pariflush();
265: return matches;
266: }
267:
268: #define add_comma(x) (x==-2) /* from default_generator */
269:
270: /* Attempt to complete on the contents of TEXT. END points to the end of the
271: * word to complete. Return the array of matches, or NULL if there aren't any.
272: */
273: static char **
274: get_matches(int end, char *text, char* f(char*,int))
275: {
276: char **matches;
277:
278: #ifdef HAS_COMPLETION_APPEND_CHAR
279: rl_completion_append_character = ' ';
280: #endif
281: current_ep = NULL;
282: matches = ((CF) completion_matches)(text, (char *(*)())f);
283: if (matches && !matches[1]) /* a single match */
284: {
285: if (add_paren(end))
286: {
287: match_concat(matches,"()");
288: pari_rl_back = 1;
289: if (rl_point == rl_end)
290: #ifdef HAS_COMPLETION_APPEND_CHAR
291: rl_completion_append_character = '\0'; /* Do not append space. */
292: #else
293: pari_rl_back = 2;
294: #endif
295: }
296: else if (add_comma(end))
297: match_concat(matches,",");
298: }
299: if (under_emacs) matches = matches_for_emacs(text,matches);
300: return matches;
301: }
302: #undef add_comma
303:
304: static char *
305: add_junk(char *name, char *text, long junk)
306: {
307: char *s = strncpy((char*) gpmalloc(strlen(name)+1+junk),text,junk);
308: strcpy(s+junk,name); return s;
309: }
310:
311: /* Generator function for command completion. STATE lets us know whether
312: * to start from scratch; without any state (i.e. STATE == 0), then we
313: * start at the top of the list.
314: */
315: static char *
316: hashtable_generator (char *text, int state, entree **hash)
317: {
318: static int hashpos, len, junk, n;
319: static entree* ep;
320: static char *TEXT;
321:
322: /* If this is a new word to complete, initialize now:
323: * + indexes hashpos (GP hash list) and n (keywords specific to long help).
324: * + file completion and keyword completion use different word boundaries,
325: * have TEXT point to the keyword start.
326: * + save the length of TEXT for efficiency.
327: */
328: if (!state)
329: {
330: n = hashpos = 0; ep=hash[hashpos];
331: len=strlen(text); junk=len-1;
332: while (junk >= 0 && is_keyword_char(text[junk])) junk--;
333: junk++; len -= junk; TEXT = text + junk;
334: }
335:
336: /* First check the keywords list */
337: if (add_help_keywords)
338: {
339: for ( ; keyword_list[n]; n++)
340: if (!strncmp(keyword_list[n],TEXT,len))
341: {
342: text = add_junk(keyword_list[n],text,junk);
343: n++; return text;
344: }
345: }
346:
347: /* Return the next name which partially matches from the command list. */
348: for(;;)
349: if (!ep)
350: {
351: if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
352: ep = hash[hashpos];
353: }
354: else if (strncmp(ep->name,TEXT,len))
355: ep = ep->next;
356: else
357: break;
358: current_ep = ep;
359: text = add_junk(ep->name,text,junk);
360: ep=ep->next; return text;
361: }
362:
363: static char *
364: command_generator (char *text, int state)
365: {
366: return hashtable_generator(text,state, functions_hash);
367: }
368: static char *
369: member_generator (char *text, int state)
370: {
371: return hashtable_generator(text,state, members_hash);
372: }
373:
374: #define DFLT 0
375: #define ENTREE 1
376:
377: static char *
378: generator(void *list, char *text, int *nn, int len, int typ)
379: {
380: char *def = NULL, *name;
381: int n = *nn;
382:
383: /* Return the next name which partially matches from list.*/
384: switch(typ)
385: {
386: case DFLT :
387: do
388: def = (((default_type *) list)[n++]).name;
389: while (def && strncmp(def,text,len));
390: break;
391:
392: case ENTREE :
393: do
394: def = (((entree *) list)[n++]).name;
395: while (def && strncmp(def,text,len));
396: }
397:
398: *nn = n;
399: if (def)
400: {
401: name = strcpy((char*) gpmalloc(strlen(def)+1), def);
402: return name;
403: }
404: return NULL; /* no names matched */
405: }
406:
407: static char *
408: old_generator(char *text,int state)
409: {
410: static int n,len;
411: static char *res;
412:
413: if (!state) { res = "a"; n=0; len=strlen(text); }
414: if (res)
415: {
416: res = generator((void *)oldfonctions,text,&n,len,ENTREE);
417: if (res) return res;
418: n=0;
419: }
420: return generator((void *)functions_oldgp,text,&n,len,ENTREE);
421: }
422:
423: static char *
424: default_generator(char *text,int state)
425: {
426: static int n,len;
427:
428: if (!state) { n=0; len=strlen(text); }
429: return generator(gp_default_list,text,&n,len,DFLT);
430: }
431:
432: char **
433: pari_completion(char *text, int start, int end)
434: {
435: int i, first=0;
436:
437: /* If the line does not begin by a backslash, then it is:
438: * . an old command ( if preceded by "whatnow(" ).
439: * . a default ( if preceded by "default(" ).
440: * . a member function ( if preceded by "." within 4 letters )
441: * . a file name (in current directory) ( if preceded by "read(" )
442: * . a command
443: */
444: if (start >=1 && rl_line_buffer[start] != '~') start--;
445: while (start && is_keyword_char(rl_line_buffer[start])) start--;
446: if (rl_line_buffer[start] == '~')
447: {
448: for(i=start+1;i<=end;i++)
449: if (rl_line_buffer[i] == '/')
450: return get_matches(-1,text,(GF)filename_completion_function);
451: return get_matches(-1,text,(GF)username_completion_function);
452: }
453:
454: while (rl_line_buffer[first] && isspace((int)rl_line_buffer[first])) first++;
455: switch (rl_line_buffer[first])
456: {
457: case '\\':
458: if (first == start) text = rl_line_buffer+start+1;
459: return get_matches(-1,text,(GF)filename_completion_function);
460: case '?':
461: if (rl_line_buffer[first+1] == '?') add_help_keywords = 1;
462: return get_matches(-1,text,command_generator);
463: }
464:
465: while (start && rl_line_buffer[start] != '('
466: && rl_line_buffer[start] != ',') start--;
467: if (rl_line_buffer[start] == '(' && start)
468: {
469: #define MAX_KEYWORD 200
470: int iend, j;
471: entree *ep;
472: char buf[MAX_KEYWORD];
473:
474: i = start;
475:
476: while (i && isspace((int)rl_line_buffer[i-1])) i--;
477: iend = i;
478: while (i && is_keyword_char(rl_line_buffer[i-1])) i--;
479:
480: if (iend - i == 7)
481: {
482: if (strncmp(rl_line_buffer + i,"default",7) == 0)
483: return get_matches(-2,text,default_generator);
484: if (strncmp(rl_line_buffer + i,"whatnow",7) == 0)
485: return get_matches(-1,text,old_generator);
486: }
487: if (iend - i >= 4)
488: {
489: if (strncmp(rl_line_buffer + i,"read",4) == 0)
490: return get_matches(-1,text,(GF)filename_completion_function);
491: }
492:
493: j = start + 1;
494: while (j && isspace((int)rl_line_buffer[j])) j++;
495: /* If we are in empty parens, insert arguments for the function: */
496: if ( (rl_line_buffer[j] == ')' || !rl_line_buffer[j] )
497: && do_args_complete
498: && (iend - i < MAX_KEYWORD)
499: && ( strncpy(buf, rl_line_buffer + i, iend - i),
500: buf[iend - i] = 0, 1)
501: && (ep = is_entry(buf)) && ep->help)
502: {
503: char *s = ep->help;
504:
505: while (is_keyword_char(*s)) s++;
506: if (*s++ == '(')
507: { /* Function, print arguments! */
508: char *endh = s;
509: while (*endh && *endh != ')' && *endh != '(') endh++;
510: if (*endh == ')')
511: { /* Well-formed help. */
512: char *str = strncpy((char*) gpmalloc(endh-s + 1), s, endh-s);
513: char **ret = (char**)gpmalloc(sizeof(char*)*2);
514: str[endh-s] = 0;
515: ret[0] = str; ret[1] = NULL;
516: if (under_emacs) ret = matches_for_emacs("",ret);
517: return ret;
518: }
519: }
520: }
521: }
522: for(i=end-1;i>=start;i--)
523: if (!is_keyword_char(rl_line_buffer[i]))
524: {
525: if (rl_line_buffer[i] == '.')
526: return get_matches(-1,text,member_generator);
527: break;
528: }
529: add_help_keywords = 0;
530: return get_matches(end,text,command_generator);
531: }
532:
533: static int
534: rl_short_help(int count, int key)
535: {
536: int p=rl_point, off=p, e = rl_end;
537: FILE *save = pari_outfile;
538: long flag = h_RL;
539:
540: while (off && is_keyword_char(rl_line_buffer[off-1])) off--;
541: rl_point = 0; rl_end = 0; pari_outfile = rl_outstream;
542: if (count < 0) flag |= h_LONG; /* long help */
543: SAVE_PROMPT();
544: rl_message("",0,0);
545: aide(rl_line_buffer + off, flag);
546: RESTORE_PROMPT();
547: rl_point = p; rl_end = e; pari_outfile = save;
548: rl_clear_message();
549: #ifdef RL_REFRESH_LINE_OLDPROTO
550: rl_refresh_line();
551: #else
552: rl_refresh_line(count,key);
553: #endif
554: return 0;
555: }
556:
557: static int
558: rl_long_help(int count, int key)
559: {
560: return rl_short_help(-1,key);
561: }
562:
563: void
564: init_readline()
565: {
566: /* Allow conditional parsing of the ~/.inputrc file. */
567: rl_readline_name = "Pari-GP";
568:
569: /* added ~, ? and , */
570: rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(?,~";
571: rl_special_prefixes = "~";
572:
573: /* custom completer */
574: #ifndef CPPFunction_defined
575: # define CPPFunction Function
576: #endif
577: rl_attempted_completion_function = (CPPFunction *) pari_completion;
578:
579: /* we always want the whole list of completions under emacs */
580: if (under_emacs) rl_completion_query_items = 0x8fff;
581:
582: #define Bind ((void(*)(int,Function*,Keymap)) rl_bind_key_in_map)
583: #define Defun ((void(*)(const char*,Function*,int)) rl_add_defun)
584:
585: Defun("short-help", (Function*) rl_short_help, -1);
586: Defun("long-help", (Function*) rl_long_help, -1);
587: Defun("pari-complete", (Function*) pari_rl_complete, '\t');
588: Defun("pari-matched-insert", (Function*) pari_rl_default_matched_insert, -1);
589: Defun("pari-forward-sexp", (Function*) pari_rl_forward_sexp, -1);
590: Defun("pari-backward-sexp", (Function*) pari_rl_backward_sexp, -1);
591:
592: Bind('h', (Function*) rl_short_help, emacs_meta_keymap);
593: Bind('H', (Function*) rl_long_help, emacs_meta_keymap);
594: Bind('h', (Function*) rl_short_help, vi_movement_keymap);
595: Bind('H', (Function*) rl_long_help, vi_movement_keymap);
596: Bind('(', (Function*) pari_rl_matched_insert, emacs_standard_keymap);
597: Bind('[', (Function*) pari_rl_matched_insert, emacs_standard_keymap);
598: Bind(6, (Function*) pari_rl_forward_sexp, emacs_meta_keymap); /* M-C-f */
599: Bind(2, (Function*) pari_rl_backward_sexp, emacs_meta_keymap); /* M-C-b */
600:
601: #ifdef EMACS_DOS_KEYMAP
602: Bind(';', (Function*) rl_short_help, emacs_dos_keymap); /* F1 */
603: Bind('T', (Function*) rl_long_help, emacs_dos_keymap); /* Shift-F1 */
604: Bind(155, (Function*) pari_rl_backward_sexp, emacs_dos_keymap); /* Alt-Left */
605: Bind(157, (Function*) pari_rl_forward_sexp, emacs_dos_keymap); /* Alt-Right*/
606: #endif
607: }
608: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>