Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
/* Copyright (C) 2000 The PARI group.12This file is part of the PARI/GP package.34PARI/GP is free software; you can redistribute it and/or modify it under the5terms of the GNU General Public License as published by the Free Software6Foundation; either version 2 of the License, or (at your option) any later7version. It is distributed in the hope that it will be useful, but WITHOUT8ANY WARRANTY WHATSOEVER.910Check the License for details. You should have received a copy of it, along11with the package; see the file 'COPYING'. If not, write to the Free Software12Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */1314/*******************************************************************/15/* */16/* INTERFACE TO READLINE COMPLETION */17/* */18/*******************************************************************/19#include "pari.h"20#include "paripriv.h"2122/**************************************************************************/23static entree *current_ep = NULL;2425/* do we add () at the end of completed word? (is it a function?) */26static int27add_paren(pari_rl_interface *rl, int end)28{29entree *ep;30const char *s;3132if (end < 0 || (*rl->line_buffer)[end] == '(')33return 0; /* not from command_generator or already there */34ep = do_alias(current_ep); /* current_ep set in command_generator */35if (EpVALENCE(ep) < EpNEW)36{ /* is it a constant masked as a function (e.g Pi)? */37s = ep->help; if (!s) return 1;38while (is_keyword_char(*s)) s++;39return (*s != '=');40}41switch(EpVALENCE(ep))42{43case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;44case EpINSTALL: return 1;45}46return 0;47}4849static void50match_concat(char **matches, const char *s)51{52pari_realloc_ip((void**)matches, strlen(matches[0])+strlen(s)+1);53strcat(matches[0],s);54}5556#define add_comma(x) (x==-2) /* from default_generator */5758/* a single match, possibly modify matches[0] in place */59static void60treat_single(pari_rl_interface *rl, int code, char **matches)61{62if (add_paren(rl, code))63{64match_concat(matches,"()");65rl->back = 1;66if (*rl->point == *rl->end)67*rl->completion_append_character = '\0'; /* Do not append space. */68}69else if (add_comma(code))70match_concat(matches,",");71}72#undef add_comma7374static char **75matches_for_emacs(const char *text, char **matches)76{77if (!matches) printf("@");78else79{80int i;81printf("%s@", matches[0] + strlen(text));82if (matches[1]) print_fun_list(matches+1,0);8384/* we don't want readline to do anything, but insert some junk85* which will be erased by emacs.86*/87for (i=0; matches[i]; i++) pari_free(matches[i]);88pari_free(matches);89}90matches = (char **) pari_malloc(2*sizeof(char *));91matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");92matches[1] = NULL; printf("@E_N_D"); pari_flush();93return matches;94}9596/* Attempt to complete on the contents of TEXT. 'code' is used to97* differentiate between callers when a single match is found.98* Return the array of matches, NULL if there are none. */99static char **100get_matches(pari_rl_interface *rl, int code, const char *text, char *(*f)(const char*, int))101{102char **matches = rl->completion_matches(text, f);103if (matches && !matches[1]) treat_single(rl, code, matches);104if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);105return matches;106}107108static char *109add_prefix(const char *name, const char *text, long junk)110{111char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);112strcpy(s+junk,name); return s;113}114static void115init_prefix(const char *text, int *len, int *junk, char **TEXT)116{117long l = strlen(text), j = l-1;118while (j >= 0 && is_keyword_char(text[j])) j--;119if (j >= 7 && text[j] == '-' && !strncmp(text+(j-7),"refcard",7)) j -= 8;120j++;121*TEXT = (char*)text + j;122*junk = j;123*len = l - j;124}125126static int127is_internal(entree *ep) { return *ep->name == '_'; }128129/* Generator function for command completion. STATE lets us know whether130* to start from scratch; without any state (i.e. STATE == 0), then we131* start at the top of the list. */132static char *133hashtable_generator(const char *text, int state, entree **hash)134{135static int hashpos, len, junk;136static entree* ep;137static char *TEXT;138139/* If this is a new word to complete, initialize now:140* + indexes hashpos (GP hash list) and n (keywords specific to long help).141* + file completion and keyword completion use different word boundaries,142* have TEXT point to the keyword start.143* + save the length of TEXT for efficiency.144*/145if (!state)146{147hashpos = 0; ep = hash[hashpos];148init_prefix(text, &len, &junk, &TEXT);149}150151/* Return the next name which partially matches from the command list. */152for(;;)153if (!ep)154{155if (++hashpos >= functions_tblsz) return NULL; /* no names matched */156ep = hash[hashpos];157}158else if (is_internal(ep) || strncmp(ep->name,TEXT,len))159ep = ep->next;160else161break;162current_ep = ep; ep = ep->next;163return add_prefix(current_ep->name,text,junk);164}165/* Generator function for member completion. STATE lets us know whether166* to start from scratch; without any state (i.e. STATE == 0), then we167* start at the top of the list. */168static char *169member_generator(const char *text, int state)170{171static int hashpos, len, junk;172static entree* ep;173static char *TEXT;174entree **hash=functions_hash;175176/* If this is a new word to complete, initialize now:177* + indexes hashpos (GP hash list) and n (keywords specific to long help).178* + file completion and keyword completion use different word boundaries,179* have TEXT point to the keyword start.180* + save the length of TEXT for efficiency.181*/182if (!state)183{184hashpos = 0; ep = hash[hashpos];185init_prefix(text, &len, &junk, &TEXT);186}187188/* Return the next name which partially matches from the command list. */189for(;;)190if (!ep)191{192if (++hashpos >= functions_tblsz) return NULL; /* no names matched */193ep = hash[hashpos];194}195else if (ep->name[0]=='_' && ep->name[1]=='.'196&& !strncmp(ep->name+2,TEXT,len))197break;198else199ep = ep->next;200current_ep = ep; ep = ep->next;201return add_prefix(current_ep->name+2,text,junk);202}203static char *204command_generator(const char *text, int state)205{ return hashtable_generator(text,state, functions_hash); }206static char *207default_generator(const char *text,int state)208{ return hashtable_generator(text,state, defaults_hash); }209210static char *211ext_help_generator(const char *text, int state)212{213static int len, junk, n, def, key;214static char *TEXT;215if (!state) {216n = 0;217def = key = 1;218init_prefix(text, &len, &junk, &TEXT);219}220if (def)221{222char *s = default_generator(TEXT, state);223if (s) return add_prefix(s, text, junk);224def = 0;225}226if (key)227{228const char **L = gphelp_keyword_list();229for ( ; L[n]; n++)230if (!strncmp(L[n],TEXT,len))231return add_prefix(L[n++], text, junk);232key = 0; state = 0;233}234return command_generator(text, state);235}236237/* add a space between \<char> and following text. Attempting completion now238* would delete char. Hitting <TAB> again will complete properly */239static char **240add_space(pari_rl_interface *rl, int start)241{242char **m;243int p = *rl->point + 1;244*rl->point = start + 2;245rl->insert(1, ' '); *rl->point = p;246/*better: fake an empty completion, but don't append ' ' after it! */247*rl->completion_append_character = '\0';248m = (char**)pari_malloc(2 * sizeof(char*));249m[0] = (char*)pari_malloc(1); *(m[0]) = 0;250m[1] = NULL; return m;251}252253char **254pari_completion(pari_rl_interface *rl, char *text, int START, int END)255{256int i, first=0, start=START;257char *line = *rl->line_buffer;258259*rl->completion_append_character = ' ';260current_ep = NULL;261while (line[first] && isspace((int)line[first])) first++;262if (line[first] == '?')263{264if (line[first+1] == '?')265return get_matches(rl, -1, text, ext_help_generator);266return get_matches(rl, -1, text, command_generator);267}268269/* If the line does not begin by a backslash, then it is:270* . an old command ( if preceded by "whatnow(" ).271* . a default ( if preceded by "default(" ).272* . a member function ( if preceded by "." + keyword_chars )273* . a file name (in current directory) ( if preceded by 'read' or 'writexx' )274* . a command */275if (start >=1 && line[start] != '~') start--;276while (start && is_keyword_char(line[start])) start--;277if (line[start] == '~')278{279char *(*f)(const char*, int);280f = rl->username_completion_function;281for(i=start+1;i<=END;i++)282if (line[i] == '/') { f = rl->filename_completion_function; break; }283return get_matches(rl, -1, text, f);284}285if (line[first] == '\\')286{287if (first == start) return add_space(rl, start);288return get_matches(rl, -1, text, rl->filename_completion_function);289}290291while (start && line[start] != '('292&& line[start] != ',') start--;293if (line[start] == '(' && start)294{295int iend, j,k;296entree *ep;297char buf[200];298299i = start;300301while (i && isspace((int)line[i-1])) i--;302iend = i;303while (i && is_keyword_char(line[i-1])) i--;304305if (strncmp(line + i,"default",7) == 0)306return get_matches(rl, -2, text, default_generator);307if ( strncmp(line + i,"read",4) == 0308|| strncmp(line + i,"write",5) == 0)309return get_matches(rl, -1, text, rl->filename_completion_function);310311j = start + 1;312while (j <= END && isspace((int)line[j])) j++;313k = END;314while (k > j && isspace((int)line[k])) k--;315/* If we are in empty parens, insert the default arguments */316if ((GP_DATA->readline_state & DO_ARGS_COMPLETE) && k == j317&& (line[j] == ')' || !line[j])318&& (iend - i < (long)sizeof(buf))319&& ( strncpy(buf, line + i, iend - i),320buf[iend - i] = 0, 1)321&& (ep = is_entry(buf)) && ep->help)322{323const char *s = ep->help;324while (is_keyword_char(*s)) s++;325if (*s++ == '(')326{ /* function call: insert arguments */327const char *e = s;328while (*e && *e != ')' && *e != '(') e++;329if (*e == ')')330{ /* we just skipped over the arguments in short help text */331char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);332char **ret = (char**)pari_malloc(sizeof(char*)*2);333str[e-s] = 0;334ret[0] = str; ret[1] = NULL;335if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);336return ret;337}338}339}340}341for(i = END-1; i >= start; i--)342if (!is_keyword_char(line[i]))343{344if (line[i] == '.')345return get_matches(rl, -1, text, member_generator);346break;347}348return get_matches(rl, END, text, command_generator);349}350351static char *352pari_completion_word(char *line, long end)353{354char *s = line + end, *found_quote = NULL;355long i;356*s = 0; /* truncate at cursor position */357for (i=0; i < end; i++)358{ /* first look for unclosed string */359switch(line[i])360{361case '"':362found_quote = found_quote? NULL: line + i;363break;364case '\\': i++; break;365}366}367if (found_quote) return found_quote + 1; /* return next char after quote */368/* else find beginning of word */369while (s > line && is_keyword_char(s[-1])) s--;370return s;371}372373char **374pari_completion_matches(pari_rl_interface *rl, const char *s, long pos, long *wordpos)375{376char *text, *b;377long w;378379if (*rl->line_buffer) pari_free(*rl->line_buffer);380*rl->line_buffer = b = pari_strdup(s);381text = pari_completion_word(b, pos);382w = text - b; if (wordpos) *wordpos = w;383/* text = start of expression we complete */384*rl->end = strlen(b)-1;385*rl->point = pos;386return pari_completion(rl, text, w, pos);387}388389390