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