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