Ilya Zakharevich on Sun, 19 Apr 1998 07:45:06 +0200


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Implementation of wise completion


The following patch implements wise completion discussed earlier today
(partially in private email) and implements electric ( [ and {.

In the real PARI release electricity of parens after TAB and
electricity of ( [ { should be configurable, since many people do not
like electric keys ;-).

Enjoy,
Ilya

P.S.  I also fixed a buglet with prototypes.

--- ./src/gp/gp_rl.c~	Sat Feb  7 11:12:04 1998
+++ ./src/gp/gp_rl.c	Sun Apr 19 01:35:34 1998
@@ -32,6 +32,48 @@ static entree *current_ep = NULL;
 typedef char** (*CF)(char*, char * (*)()); /* completion function */
 typedef char* (*GF)(char*,int); /* generator function */
 
+static int pari_rl_back;
+extern Function *rl_last_func;
+
+/* Wrapper around rl_complete to allow insertion of () with a point in
+   between. */
+static int
+pari_rl_complete(int count, int key)
+{
+    int ret;
+    
+    pari_rl_back = 0;
+    rl_begin_undo_group();
+    if (rl_last_func == pari_rl_complete)
+	rl_last_func = rl_complete;	/* Make repeated TABs different */
+    ret = rl_complete(count,key);
+    if (pari_rl_back && (pari_rl_back <= rl_point))
+	rl_point -= pari_rl_back;
+    rl_end_undo_group();
+    return ret;
+}
+
+static const char paropen[] = "([{";
+static const char parclose[] = ")]}";
+
+/* To allow insertion of () with a point in between. */
+static int
+pari_rl_matched_insert(int count, int key)
+{
+    int i = 0, ret;
+    
+    while (paropen[i] && paropen[i] != key)
+	i++;
+    if (!paropen[i])
+	return rl_insert(count,key);
+    rl_begin_undo_group();
+    rl_insert(count,key);
+    ret = rl_insert(count,parclose[i]);
+    rl_point -= count;
+    rl_end_undo_group();
+    return ret;
+}
+
 /* Attempt to complete on the contents of TEXT. START and END show the
  * region of TEXT that contains the word to complete.  We can use the
  * entire line in case we want to do some simple parsing.  Return the
@@ -40,18 +82,31 @@ typedef char* (*GF)(char*,int); /* gener
 static char **
 get_matches(int end, char *text, char* f(char*,int))
 {
-  char **matches = ((CF) completion_matches)(text, (char *(*)(ANYARG))f);
-
+  char **matches;
+  
+  rl_completion_append_character = ' ';
+  current_ep = NULL;
+  matches = ((CF) completion_matches)(text, (char *(*)(ANYARG))f);
   if (matches && ! matches[1] /* only one match          */
-	      && ! current_ep /* this is not a variable  */
+	      && current_ep
+                 /* this is not a variable  */
+	      && (EpVALENCE(current_ep) != EpVAR)
               && end >= 0     /* from command_completion */
               && rl_line_buffer[end] != '(' )
   {
     int i=strlen(matches[0]);
-    matches[0] = (char*) gprealloc(matches[0], i+2, i);
-    strcat(matches[0],"(");
+    /* Check whether it is a constant masked as a function: */
+    char *s = current_ep->help;
+    while (is_keyword_char(*s))
+	s++;
+    if (*s != '=') {			/* Is not a variable, after all!  */
+	matches[0] = (char*) gprealloc(matches[0], i+3, i);
+	strcat(matches[0],"()");
+	pari_rl_back = 1;
+	if (rl_point == rl_end) 
+	    rl_completion_append_character = '\0'; /* Do not append space. */
+    }
   }
-  current_ep = NULL;
 
   if (under_emacs)
   {
@@ -131,7 +186,7 @@ command_generator (char *text, int  stat
       ep = ep->next;
     else
       break;
-  if (EpVALENCE(ep)==EpVAR) current_ep = ep;
+  current_ep = ep;
   text = add_junk(ep->name,text,junk);
   ep=ep->next; return text;
 }
@@ -255,24 +310,60 @@ pari_completion (char *text, int start, 
 
   while (start && rl_line_buffer[start] != '('
                && rl_line_buffer[start] != ',') start--;
-  if (rl_line_buffer[start] == '(')
+  if (rl_line_buffer[start] == '(' && start)
   {
-    beg = rl_line_buffer + start;
-    if (start >= 7)
+#define MAX_KEYWORD 200
+    int iend, j;
+    entree *ep;
+    char buf[MAX_KEYWORD];
+
+    i = start;
+
+    while (i && isspace(rl_line_buffer[i - 1])) i--;
+    iend = i;
+    while (i && is_keyword_char(rl_line_buffer[i - 1])) i--;
+    
+    if (iend - i == 7)
     {
-      s = beg-7;
-      if (!strncmp(s,"default",7))
+      if (strncmp(rl_line_buffer + i,"default",7) == 0)
 	return get_matches(-1,text,default_generator);
-      if (!strncmp(s,"whatnow",7))
+      if (strncmp(rl_line_buffer + i,"whatnow",7) == 0)
 	return get_matches(-1,text,old_generator);
-    }
+    } 
 
-    if (start>=4)
+    if (iend - i >= 4)
     {
-      s = beg-4;
-      if (!strncmp(s,"read",4))
+      if (strncmp(rl_line_buffer + i,"read",4) == 0)
 	return get_matches(-1,text,filename_completion_function);
     }
+    
+    j = start + 1;
+    while (j && isspace(rl_line_buffer[j])) j++;
+    /* If we are in empty parens, insert arguments for the function: */
+    if ( (rl_line_buffer[j] == ')' || !rl_line_buffer[j] )
+	 && (iend - i < MAX_KEYWORD)
+	 && ( strncpy(buf, rl_line_buffer + i, iend - i),
+	      buf[iend - i] = 0, 1)
+	 && (ep = is_entry(buf, functions_hash)) && ep->help) {
+	char *s = ep->help;
+
+	while (is_keyword_char(*s))
+	    s++;
+	if (*s == '(') {		/* Function, print arguments! */
+	    char *endh = ++s;
+	    while (*endh && *endh != ')' && *endh != '(')
+		endh++;
+	    if (*endh == ')') {		/* Well-formed help.  */
+		char *str = strncpy((char*) gpmalloc(endh - s + 1), 
+				    s, endh - s);
+		char **ret = (char**)gpmalloc(sizeof(char*)*2);
+		str[endh - s] = 0;
+		ret[0] = str;
+		ret[1] = NULL;
+		return ret;
+	    }
+	}
+    }
   }
   for(i=end-1;i>=start;i--)
     if (!is_keyword_char(rl_line_buffer[i]))
@@ -332,15 +423,20 @@ init_readline(int i)
 
   Defun("short-help", (Function*) rl_short_help, -1);
   Defun("long-help", (Function*) rl_long_help, -1);
+  Defun("pari-complete", (Function*) pari_rl_complete, '\t');
+  Defun("pari-matched-insert", (Function*) pari_rl_matched_insert, -1);
 
   Bind('h', (Function*) rl_short_help, emacs_meta_keymap);
   Bind('H', (Function*) rl_long_help,  emacs_meta_keymap);
   Bind('h', (Function*) rl_short_help, vi_movement_keymap);
   Bind('H', (Function*) rl_long_help,  vi_movement_keymap);
+  Bind('(', (Function*) pari_rl_matched_insert, emacs_standard_keymap);
+  Bind('[', (Function*) pari_rl_matched_insert, emacs_standard_keymap);
+  Bind('{', (Function*) pari_rl_matched_insert, emacs_standard_keymap);
 
 #  ifdef EMACS_DOS_KEYMAP
-     Bind(';', (char*) rl_short_help, emacs_dos_keymap); /* F1 */
-     Bind('T', (char*) rl_long_help,  emacs_dos_keymap); /* Shift-F1 */
+     Bind(';', (Function*) rl_short_help, emacs_dos_keymap); /* F1 */
+     Bind('T', (Function*) rl_long_help,  emacs_dos_keymap); /* Shift-F1 */
 #  endif
 }
 #endif