Annotation of OpenXM_contrib/pari/src/gp/gp_rl.c, Revision 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>