Back to index

texmacs  1.0.7.15
edit_search.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : edit_search.cpp
00004 * DESCRIPTION: search and query replace
00005 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "Replace/edit_replace.hpp"
00013 #include "Interface/edit_interface.hpp"
00014 #include "drd_std.hpp"
00015 #include "drd_mode.hpp"
00016 #include "analyze.hpp"
00017 
00018 /******************************************************************************
00019 * Constructor and destructor
00020 ******************************************************************************/
00021 
00022 edit_replace_rep::edit_replace_rep () {}
00023 edit_replace_rep::~edit_replace_rep () {}
00024 
00025 /******************************************************************************
00026 * Structural search routines
00027 ******************************************************************************/
00028 
00029 bool
00030 edit_replace_rep::inside (string what) {
00031   return inside (make_tree_label (what));
00032 }
00033 
00034 path
00035 edit_replace_rep::search_upwards (string what) {
00036   return search_upwards (make_tree_label (what));
00037 }
00038 
00039 bool
00040 edit_replace_rep::inside (tree_label l) {
00041   return !is_nil (search_upwards (l));
00042 }
00043 
00044 path
00045 edit_replace_rep::search_upwards (tree_label l) {
00046   path p= path_up (tp, 2);
00047   while (!is_func (subtree (et, p), l)) {
00048     if (p == rp) return path ();
00049     p= path_up (p);
00050   }
00051   return p;
00052 }
00053 
00054 path
00055 edit_replace_rep::search_parent_upwards (tree_label l, int& last) {
00056   path p= path_up (tp);
00057   while (!is_func (subtree (et, path_up (p)), l)) {
00058     p= path_up (p);
00059     if (p == rp) {
00060       last= -1;
00061       return path ();
00062     }
00063   }
00064   last= last_item (p);
00065   return path_up (p);
00066 }
00067 
00068 path
00069 edit_replace_rep::search_parent_upwards (tree_label l) {
00070   int last;
00071   path p= search_parent_upwards (l, last);
00072   return p * last;
00073 }
00074 
00075 bool
00076 edit_replace_rep::inside_with (string var, string val) {
00077   return !is_nil (search_upwards_with (var, val));
00078 }
00079 
00080 path
00081 edit_replace_rep::search_upwards_with (string var, string val) {
00082   path p= path_up (tp);
00083   while (true) {
00084     p= path_up (p);
00085     if (p == rp) return path ();
00086     tree st= subtree (et, p);
00087     if (is_func (st, WITH) && (st[0] == var) && (st[1] == val)) return p;
00088   }
00089 }
00090 
00091 string
00092 edit_replace_rep::inside_which (tree t) {
00093   path p= search_upwards_in_set (t);
00094   if ((p == rp) || is_nil (p)) return "";
00095   tree st= subtree (et, p);
00096   if (is_func (st, COMPOUND)) return as_string (st[0]);
00097   else return as_string (L(st));
00098 }
00099 
00100 path
00101 edit_replace_rep::search_upwards_in_set (tree t) {
00102   if (!is_tuple (t)) return path ();
00103   int i, n=N(t);
00104   path p= path_up (tp);
00105   while (true) {
00106     p= path_up (p);
00107     if (p == rp) return path ();
00108     tree st= subtree (et, p);
00109     for (i=0; i<n; i++) {
00110       if (is_atomic (t[i])) {
00111        string s= t[i]->label;
00112        if (is_quoted (s)) s= raw_unquote (s);
00113        if (std_contains (s)) {
00114          tree_label l= as_tree_label (s);
00115          if (is_func (st, l)) return p;
00116        }
00117        else if (is_compound (st, s)) return p;
00118       }
00119       else if (is_func (st, L(t))) return p;
00120     }
00121   }
00122 }
00123 
00124 static bool
00125 is_accessible_path (drd_info drd, tree t, path p) {
00126   if (is_nil (p)) return true;
00127   return
00128     drd->is_accessible_child (t, p->item) &&
00129     (p->item < N(t)) &&
00130     is_accessible_path (drd, t[p->item], p->next);
00131 }
00132 
00133 path
00134 edit_replace_rep::search_previous_compound (path init, string which) {
00135   path p= init;
00136   while (true) {
00137     if (p == rp) return init;
00138     if (last_item (p) == 0) p= path_up (p);
00139     else {
00140       p= path_dec (p);
00141       while (true) {
00142        tree st= subtree (et, p);
00143        if (arity (st) == 0) break;
00144        p= p * (N(st)-1);
00145       }
00146     }
00147     if (is_accessible_path (drd, et, p) &&
00148        is_compound (subtree (et, p), which))
00149       return p;
00150   }
00151 }
00152 
00153 path
00154 edit_replace_rep::search_next_compound (path init, string which) {
00155   path p= init;
00156   while (true) {
00157     if (p == rp) return init;
00158     if (last_item (p) == (N (subtree (et, path_up (p))) - 1)) p= path_up (p);
00159     else {
00160       p= path_inc (p);
00161       while (true) {
00162        tree st= subtree (et, p);
00163        if (arity (st) == 0) break;
00164        p= p * 0;
00165       }
00166     }
00167     if (is_accessible_path (drd, et, p) &&
00168        is_compound (subtree (et, p), which))
00169       return p;
00170   }
00171 }
00172 
00173 /******************************************************************************
00174 * Test whether we found a match
00175 ******************************************************************************/
00176 
00177 static bool
00178 test_match (tree t, tree pat) {
00179   // FIXME: we use empty strings for wildcards now
00180   // we should support real wildcards and regular expressions later
00181   if (pat == "") return true;
00182   else if (is_atomic (t) || is_atomic (pat)) return t == pat;
00183   else if ((L(t) != L(pat)) || (N(t) != N(pat))) return false;
00184   else {
00185     int i, n= N(t);
00186     for (i=0; i<n; i++)
00187       if (!test_match (t[i], pat[i]))
00188        return false;
00189     return true;
00190   }
00191 }
00192 
00193 path
00194 edit_replace_rep::test_sub (path p, tree t) {
00195   // cout << "Test " << subtree (et, path_up (p))
00196   //      << " :: " << t << " at " << p << "\n";
00197   if (is_concat (t) && (N(t) > 1)) {
00198     if (N(p) <= 1) return p;
00199     tree st= subtree (et, path_up (p, 2));
00200     if (!is_concat (st)) return p;
00201     int i, l= last_item (path_up (p)), n= N(t);
00202     if (N(st) < (n + l)) return p;
00203     if ((t[0] != "") && (p == end (et, path_up (p)))) return p;
00204     if (test_sub (p, t[0]) != end (et, path_up (p))) return p;
00205     for (i=1; i<n-1; i++)
00206       if (!test_match (st[l+i], t[i]))
00207        return p;
00208     path r= path_up (p, 2) * path (l+n-1, start (st[l+n-1], path ()));
00209     path q= test_sub (r, t[n-1]);
00210     if (q == r) return p;
00211     return q;
00212   }
00213   else {
00214     tree st= subtree (et, path_up (p));
00215     if (is_compound (t)) {
00216       if (!is_compound (st)) return p;
00217       // cout << "Test with " << st << "\n";
00218       if (last_item (p) != 0) return p;
00219       if (!test_match (st, t)) return p;
00220       return path_inc (p);
00221     }
00222     else {
00223       if (is_compound (st)) return p;
00224       int l= last_item (p);
00225       // cout << "Test with " << st->label << " at " << l << "\n";
00226       if (N(st->label) < (N(t->label) + l)) return p;
00227       if (st->label (l, l + N(t->label)) != t->label) return p;
00228       return path_add (p, N (t->label));
00229     }
00230   }
00231 }
00232 
00233 path
00234 edit_replace_rep::test (path p, tree t) {
00235   path q= test_sub (p, t);
00236   if (q == p) return p;
00237   string mode= as_string (get_env_value (MODE, p));
00238   string lan = as_string (get_env_value (MODE_LANGUAGE (mode), p));
00239   if (search_mode != mode) return p;
00240   if (search_lan != lan) return p;
00241   return q;
00242 }
00243 
00244 /******************************************************************************
00245 * Traversal of the edit tree
00246 ******************************************************************************/
00247 
00248 void
00249 edit_replace_rep::step_ascend (bool forward) {
00250   // cout << "Step ascend at " << search_at << "\n";
00251   search_at= path_add (path_up (search_at), forward? 1: -1);
00252   tree st;
00253   int  l;
00254   while (true) {
00255     st= subtree (et, path_up (search_at));
00256     l = last_item (search_at);
00257     // cout << "  st= " << st << "\n";
00258     // cout << "  l = " << l << "\n";
00259     if ((l<0) || (l>=N(st)) || drd->is_accessible_child (st, l)) break;
00260     search_at= path_add (search_at, forward? 1: -1);
00261   }
00262 
00263   if (forward) {
00264     if (l == N(st)) {
00265       if (is_atom (search_at / rp)) search_at= rp;
00266       else step_ascend (forward);
00267     }
00268     else step_descend (forward);
00269   }
00270   else {
00271     if (l == -1) {
00272       if (is_atom (search_at / rp)) search_at= rp;
00273       else step_ascend (forward);
00274     }
00275     else step_descend (forward);
00276   }
00277 }
00278 
00279 void
00280 edit_replace_rep::step_descend (bool forward) {
00281   // cout << "Step descend at " << search_at << "\n";
00282   tree st= subtree (et, search_at);
00283   // cout << "  st= " << st << "\n";
00284   int last= (is_atomic (st)? N(st->label): N(st)-1);
00285   search_at= search_at * (forward? 0: last);
00286   if (is_format (st))
00287     step_descend (forward);
00288 }
00289 
00290 void
00291 edit_replace_rep::step_horizontal (bool forward) {
00292   // cout << "Step horizontal at " << search_at << "\n";
00293   if (search_at == rp) step_descend (forward);
00294   else {
00295     tree st  = subtree (et, path_up (search_at));
00296     int  l   = last_item (search_at);
00297 
00298     // cout << "  st= " << st << "\n";
00299     if (forward) {
00300       if (l == right_index (st)) step_ascend (forward);
00301       else {
00302        if (is_atomic (st)) {
00303          if (st->label[l]=='<') {
00304            string s= st->label;
00305            while ((l<N(s)) && (s[l]!='>')) l++;
00306            if (l<N(s)) l++;
00307            search_at= path_up (search_at) * l;
00308          }
00309          else search_at= path_inc (search_at);
00310        }
00311        else {
00312          int i;
00313          for (i=l; i<N(st); i++)
00314            if (drd->is_accessible_child (st, i)) {
00315              search_at= path_up (search_at) * i;
00316              step_descend (forward);
00317              return;
00318            }
00319          step_ascend (forward);
00320        }
00321       }
00322     }
00323     else {
00324       if (l == 0) step_ascend (forward);
00325       else {
00326        if (is_atomic (st)) {
00327          if (st->label[l-1]=='>') {
00328            string s= st->label;
00329            l--;
00330            while ((l>0) && (s[l]!='<')) l--;
00331            search_at= path_up (search_at) * l;
00332          }
00333          else search_at= path_dec (search_at);
00334        }
00335        else {
00336          int i;
00337          for (i=l; i>=0; i--)
00338            if (drd->is_accessible_child (st, i)) {
00339              search_at= path_up (search_at) * i;
00340              step_descend (forward);
00341              return;
00342            }
00343          step_ascend (forward);
00344        }
00345       }
00346     }
00347   }
00348 }
00349 
00350 void
00351 edit_replace_rep::next_match (bool forward) {
00352   // cout << "Next match at " << search_at << "\n";
00353   while (true) {
00354     if (search_at == rp) {
00355       set_selection (tp, tp);
00356       notify_change (THE_SELECTION);
00357       return;
00358     }
00359     search_end= test (search_at, search_what);
00360     if (search_end != search_at) {
00361       go_to (copy (search_end));
00362       show_cursor_if_hidden ();
00363       set_selection (search_at, search_end);
00364       notify_change (THE_SELECTION);
00365       return;
00366     }
00367     int new_mode= DRD_ACCESS_HIDDEN;
00368     if (get_init_string (MODE) == "src" || inside ("show-preamble"))
00369       new_mode= DRD_ACCESS_SOURCE;
00370     int old_mode= set_access_mode (new_mode);
00371     step_horizontal (forward);
00372     set_access_mode (old_mode);
00373   }
00374 }
00375 
00376 /******************************************************************************
00377 * Searching
00378 ******************************************************************************/
00379 
00380 void
00381 edit_replace_rep::search_start (bool flag) {
00382   string r ("forward search");
00383   if (!flag) r= "backward search";
00384 
00385   forward     = flag;
00386   search_mode = copy (get_env_string (MODE));
00387   search_lan  = copy (get_env_string (MODE_LANGUAGE (search_mode)));
00388   search_at   = tp;
00389   search_what = tree ("");
00390   where_stack = list<path> ();
00391   what_stack  = tree ("");
00392   set_input_mode (INPUT_SEARCH);
00393   set_message ("Searching", r);
00394 }
00395 
00396 void
00397 edit_replace_rep::search_next (bool forward) {
00398   string r ("forward search");
00399   if (!forward) r= "backward search";
00400   string w= as_string (search_what);
00401   if (is_compound (search_what)) w= "compound expression";
00402 
00403   next_match (forward);
00404   if (search_at == rp) {
00405     set_message (concat ("No more matches for ", verbatim (w)), r);
00406     beep ();
00407   }
00408   else set_message (concat ("Searching ", verbatim (w)), r);
00409 }
00410 
00411 void
00412 edit_replace_rep::search_next (tree what, bool forward, bool step) {
00413   where_stack= list<path> (copy (search_at), where_stack);
00414   what_stack = tuple (copy (search_what), what_stack);
00415   search_what= copy (what);
00416   if (step) step_horizontal (forward);
00417   search_next (forward);
00418 }
00419 
00420 void
00421 edit_replace_rep::search_stop () {
00422   tree t= tuple ("texmacs", search_what, search_mode, search_lan);
00423   set_input_normal ();
00424   if (search_what != "")
00425     selection_raw_set ("search", t);
00426 }
00427 
00428 void
00429 edit_replace_rep::search_button_next () {
00430   set_input_mode (INPUT_SEARCH);
00431   search_next (search_what, forward, true);
00432 }
00433 
00434 bool
00435 edit_replace_rep::search_keypress (string s) {
00436   set_message ("", "");
00437   if (s == "space") s= " ";
00438   if (N(s) == 1) {
00439     if (is_atomic (search_what))
00440       search_next (as_string (search_what) * s, forward, false);
00441   }
00442   else {
00443     if ((s == "left") || (s == "right") ||
00444         (s == "up") || (s == "down") ||
00445         (s == "pageup") || (s == "pagedown") ||
00446         (s == "begin") || (s == "end")) {
00447       search_stop ();
00448       return false;      
00449     }
00450     else if ((s == "C-c") || (s == "C-g"))
00451       search_stop ();
00452     else if ((s == "next") || (s == "previous")) {
00453       if (search_what == "") {
00454         tree t= selection_raw_get ("search");
00455         if (is_tuple (t, "texmacs", 3) &&
00456             (t[1] != "") &&
00457             (t[2] == search_mode) &&
00458             (t[3] == search_lan))
00459           search_next (t[1], s != "previous", true);
00460       }
00461       else search_next (search_what, s != "previous", true);
00462     }
00463     else if ((s == "delete") || (s == "backspace")) {
00464       if (is_nil (where_stack))
00465         search_stop ();
00466       else if (is_atom (where_stack)) {
00467         go_to (where_stack->item);
00468         search_stop ();
00469       }
00470       else {
00471         search_at  = where_stack->item;
00472         where_stack= where_stack->next;
00473         search_what= what_stack[0];
00474         what_stack = what_stack[1];
00475         search_next (forward);
00476       }
00477     }
00478     else if ((s == "C-left") || (s == "C-right")) {
00479       // FIXME: integrate better with general searching mechanism
00480       path p= path_up (search_at);
00481       while ((p != rp) && (!is_extension (subtree (et, path_up (p)))))
00482         p= path_up (p);
00483       if (p == rp) return true;
00484       path r= path_up (p);
00485       string w= as_string (L (subtree (et, r)));
00486       path q= (s == "C-right") ?
00487         search_next_compound (r, w) :
00488         search_previous_compound (r, w);
00489       if (q == r) {
00490         set_message ("No more matches", "search similar structure");
00491         beep ();
00492       }
00493       else {
00494         q= q * min (N (subtree (et, q)) - 1, last_item (p));
00495         search_at= end (et, q);
00496         go_to (copy (search_at));
00497       }
00498     }
00499   }
00500   return true;
00501 }
00502 
00503 tree
00504 edit_replace_rep::search_tree() {
00505   return search_what;
00506 }
00507 
00508 /******************************************************************************
00509 * Replacing
00510 ******************************************************************************/
00511 
00512 void
00513 edit_replace_rep::replace_start (tree what, tree by, bool flag) {
00514   forward     = flag;
00515   search_mode = copy (get_env_string (MODE));
00516   search_lan  = copy (get_env_string (MODE_LANGUAGE (search_mode)));
00517   search_at   = tp;
00518   search_what = copy (what);
00519   replace_by  = copy (by);
00520   nr_replaced = 0;
00521   set_input_mode (INPUT_REPLACE);
00522   if (search_what == "") {
00523     tree t= selection_raw_get ("search");
00524     if (is_tuple (t, "texmacs", 3) &&
00525         (t[1] != "") &&
00526         (t[2] == search_mode) &&
00527         (t[3] == search_lan))
00528       search_what= t[1];
00529     t= selection_raw_get ("replace");
00530     if (is_tuple (t, "texmacs", 3) &&
00531         (t[1] != "") &&
00532         (t[2] == search_mode) &&
00533         (t[3] == search_lan))
00534       replace_by= t[1];
00535   }
00536   replace_next ();
00537 }
00538 
00539 void
00540 edit_replace_rep::replace_next () {
00541   string r ("forward replace");
00542   if (!forward) r= "backward replace";
00543 
00544   next_match (forward);
00545   if (search_at == rp) {
00546     tree l= concat ("Replaced ", as_string (nr_replaced), " occurrences");
00547     if (nr_replaced == 0) l= "No matches found";
00548     if (nr_replaced == 1) l= "Replaced one occurrence";
00549     set_message (l, r);
00550     beep ();
00551     set_input_normal ();
00552   }
00553   else set_message ("Replace (y,n,a)?", r);
00554 }
00555 
00556 bool
00557 edit_replace_rep::replace_keypress (string s) {
00558   set_message ("", "");
00559   if (s == "space") s= " ";
00560   if ((s == "C-c") || (s == "C-g") || (s == "escape"))
00561     set_input_normal ();
00562   else if (s == "y") {
00563     nr_replaced++;
00564     go_to (copy (search_end));
00565     cut (search_at, search_end);
00566     insert_tree (copy (replace_by));
00567     search_at= copy (tp);
00568     replace_next ();
00569   }
00570   else if (s == "n") {
00571     step_horizontal (forward);
00572     replace_next ();
00573   }
00574   else if (s == "a" || s == "!") {
00575     while (search_at != rp) {
00576       nr_replaced++;
00577       go_to (copy (search_end));
00578       cut (search_at, search_end);
00579       insert_tree (copy (replace_by));
00580       search_at= copy (tp);
00581       replace_next ();
00582     }
00583   }
00584   return true;
00585 }