1 
2 /******************************************************************************
3 * MODULE     : edit_complete.cpp
4 * DESCRIPTION: Tab completion
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "edit_interface.hpp"
13 #include "hashset.hpp"
14 #include "analyze.hpp"
15 #include "connect.hpp"
16 #include "dictionary.hpp"
17 
18 /******************************************************************************
19 * Finding completions in text
20 ******************************************************************************/
21 
22 static void
find_completions(drd_info drd,tree t,hashset<string> & h,string prefix="")23 find_completions (
24   drd_info drd, tree t, hashset<string>& h, string prefix= "")
25 {
26   if (is_atomic (t)) {
27     string s= t->label;
28     int i= 0, n= N(s);
29     while (i<n) {
30       if (is_iso_alpha (s[i])) {
31 	int start= i;
32 	while ((i<n) && (is_iso_alpha (s[i]))) i++;
33 	string r= s (start, i);
34 	if (starts (r, prefix) && (r != prefix))
35 	  h->insert (r (N(prefix), N(r)));
36       }
37       else skip_symbol (s, i);
38     }
39   }
40   else {
41     int i, n= N(t);
42     for (i=0; i<n; i++)
43       if (drd->is_accessible_child (t, i))
44 	find_completions (drd, t[i], h, prefix);
45   }
46 }
47 
48 static array<string>
find_completions(drd_info drd,tree t,string prefix="")49 find_completions (drd_info drd, tree t, string prefix= "") {
50   hashset<string> h;
51   find_completions (drd, t, h, prefix);
52   return as_completions (h);
53 }
54 
55 /******************************************************************************
56 * Completion mode
57 ******************************************************************************/
58 
59 bool
complete_try()60 edit_interface_rep::complete_try () {
61   tree st= subtree (et, path_up (tp));
62   if (is_compound (st)) return false;
63   string s= st->label, ss;
64   int end= last_item (tp);
65   array<string> a;
66   if (inside (LABEL) || inside (REFERENCE) || inside (PAGEREF)) {
67     if (end != N(s)) return false;
68     ss= copy (s);
69     tree t= get_labels ();
70     int i, n= N(t);
71     for (i=0; i<n; i++)
72       if (is_atomic (t[i]) && starts (t[i]->label, s))
73 	a << string (t[i]->label (N(s), N(t[i]->label)));
74   }
75   else {
76     if ((end==0) || (!is_iso_alpha (s[end-1])) ||
77 	((end!=N(s)) && is_iso_alpha (s[end]))) return false;
78     int start= end-1;
79     while ((start>0) && is_iso_alpha (s[start-1])) start--;
80     ss= s (start, end);
81     a= find_completions (drd, et, ss);
82   }
83   if (N(a) == 0) return false;
84   complete_start (ss, a);
85   return true;
86 }
87 
88 void
complete_message()89 edit_interface_rep::complete_message () {
90   int i, n= N(completions);
91   string s= "";
92   string sep= translate (", ");  // Might be needed for oriental languages
93   for (i=1; i<min(n,11); i++) {
94     int j= (completion_pos + i) % n;
95     if (i != 1) s << sep;
96     s << completion_prefix << completions[j];
97   }
98   set_message (concat ("Other completions: ", verbatim (s)), "tab");
99 }
100 
101 void
complete_start(string prefix,array<string> compls)102 edit_interface_rep::complete_start (string prefix, array<string> compls) {
103   // check consistency
104   tree st= subtree (et, path_up (tp));
105   if (is_compound (st)) return;
106   string s= st->label;
107   int end= last_item (tp);
108   if ((end<N(prefix)) || (s (end-N(prefix), end) != prefix)) return;
109 
110   // perform first completion and switch to completion mode if necessary
111   if (N (compls) == 1) {
112     string s= compls[0];
113     if (ends (s, "()")) // temporary fix for Pari
114       insert_tree (s, path (N(s)-1));
115     else insert_tree (s);
116     completions= array<string> ();
117   }
118   else {
119     completion_prefix= prefix;
120     completions      = close_completions (compls);
121     completion_pos   = 0;
122     insert_tree (completions[0]);
123     complete_message ();
124     beep ();
125     set_input_mode (INPUT_COMPLETE);
126   }
127 }
128 
129 bool
complete_keypress(string key)130 edit_interface_rep::complete_keypress (string key) {
131   set_message ("", "");
132   if (key == "space") key= " ";
133   if ((key != "tab") && (key != "S-tab")) {
134     set_input_normal (); return false; }
135   tree st= subtree (et, path_up (tp));
136   if (is_compound (st)) {
137     set_input_normal (); return false; }
138   string s= st->label;
139   int end= last_item (tp);
140   string old_s= completions [completion_pos];
141   string test= completion_prefix * old_s;
142   if ((end<N(test)) || (s (end-N(test), end) != test)) {
143     set_input_normal (); return false; }
144 
145   if (key == "tab") completion_pos++;
146   else completion_pos--;
147   if (completion_pos < 0) completion_pos= N(completions)-1;
148   if (completion_pos >= N(completions)) completion_pos= 0;
149   string new_s= completions [completion_pos];
150   remove (path_up (tp) * (end-N(old_s)), N(old_s));
151   insert (path_up (tp) * (end-N(old_s)), new_s);
152   complete_message ();
153   return true;
154 }
155 
156 /******************************************************************************
157 * Tab completion inside sessions
158 ******************************************************************************/
159 
160 static string cursor_symbol ("[tmcursor]");
161 
162 static tree
put_cursor(tree t,path p)163 put_cursor (tree t, path p) {
164   if (is_atomic (t)) {
165     string s= t->label;
166     return s (0, p->item) * cursor_symbol * s (p->item, N(s));
167   }
168   else {
169     if (p == path (0)) return tree (CONCAT, cursor_symbol, t);
170     else if (p == path (1)) return tree (CONCAT, t, cursor_symbol);
171     else {
172       int i, n= N(t);
173       tree u (t, n);
174       for (i=0; i<n; i++)
175 	if (i == p->item) u[i]= put_cursor (t[i], p->next);
176 	else u[i]= t[i];
177       return u;
178     }
179   }
180 }
181 
182 string
session_complete_command(tree tt)183 edit_interface_rep::session_complete_command (tree tt) {
184   path p= reverse (obtain_ip (tt));
185   tree st= subtree (et, p);
186   if ((N(tp) <= N(p)) || (tp[N(p)] != 1)) return "";
187   tree t= put_cursor (st[1], tail (tp, N(p)+1));
188   // cout << t << LF;
189 
190   (void) eval ("(use-modules (utils plugins plugin-cmd))");
191   string lan= get_env_string (PROG_LANGUAGE);
192   string ses= get_env_string (PROG_SESSION);
193   string s  = as_string (call ("verbatim-serialize", lan, tree_to_stree (t)));
194   s= s (0, N(s)-1);
195 
196   int pos= search_forwards (cursor_symbol, s);
197   if (pos == -1) return "";
198   s= s (0, pos) * s (pos + N(cursor_symbol), N(s));
199   // cout << s << ", " << pos << LF;
200   return "(complete " * scm_quote (s) * " " * as_string (pos) * ")";
201 }
202 
203 void
custom_complete(tree r)204 edit_interface_rep::custom_complete (tree r) {
205   if (!is_tuple (r)) return;
206   int i, n= N(r);
207   string prefix;
208   array<string> compls;
209   for (i=0; i<n; i++)
210     if (is_atomic (r[i])) {
211       string l= r[i]->label;
212       if (is_quoted (l)) l= scm_unquote (l);
213       if (prefix == "") prefix= l;
214       else compls << l;
215     }
216   // cout << prefix << ", " << compls << LF;
217 
218   if ((prefix == "") || (N(compls) == 0)) return;
219   complete_start (prefix, compls);
220 }
221