]> git.lyx.org Git - lyx.git/blobdiff - src/lyxfind.cpp
Keep dialog connected to External inset after Apply
[lyx.git] / src / lyxfind.cpp
index 5c72661b19dd26a3de2785e7c15c66163f4c7116..c816bcc3e867ce851932004d8498854e0cfcfa54 100644 (file)
@@ -376,7 +376,7 @@ pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
                // This causes a minor bug as undo will restore this selection,
                // which the user did not create (#8986).
                cur.innerText()->selectWord(cur, WHOLE_WORD);
-               searchstr = cur.selectionAsString(false);
+               searchstr = cur.selectionAsString(false, true);
        }
 
        // if we still don't have a search string, report the error
@@ -385,7 +385,7 @@ pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
                return make_pair(false, 0);
 
        bool have_selection = cur.selection();
-       docstring const selected = cur.selectionAsString(false);
+       docstring const selected = cur.selectionAsString(false, true);
        bool match =
                case_sens
                ? searchstr == selected
@@ -464,12 +464,11 @@ bool lyxfind(BufferView * bv, FuncRequest const & ev)
        bool matchword     = parse_bool(howto);
        bool forward       = parse_bool(howto);
 
-       return findOne(bv, search, casesensitive, matchword, forward, true, true);
+       return findOne(bv, search, casesensitive, matchword, forward, false, true);
 }
 
 
-bool lyxreplace(BufferView * bv,
-               FuncRequest const & ev, bool has_deleted)
+bool lyxreplace(BufferView * bv, FuncRequest const & ev)
 {
        if (!bv || ev.action() != LFUN_WORD_REPLACE)
                return false;
@@ -491,40 +490,31 @@ bool lyxreplace(BufferView * bv,
 
        bool update = false;
 
-       if (!has_deleted) {
-               int replace_count = 0;
-               if (all) {
-                       replace_count = replaceAll(bv, search, rplc, casesensitive, matchword);
-                       update = replace_count > 0;
-               } else {
-                       pair<bool, int> rv =
-                               replaceOne(bv, search, rplc, casesensitive, matchword, forward, findnext);
-                       update = rv.first;
-                       replace_count = rv.second;
-               }
+       int replace_count = 0;
+       if (all) {
+               replace_count = replaceAll(bv, search, rplc, casesensitive, matchword);
+               update = replace_count > 0;
+       } else {
+               pair<bool, int> rv =
+                       replaceOne(bv, search, rplc, casesensitive, matchword, forward, findnext);
+               update = rv.first;
+               replace_count = rv.second;
+       }
 
-               Buffer const & buf = bv->buffer();
-               if (!update) {
-                       // emit message signal.
-                       buf.message(_("String not found."));
+       Buffer const & buf = bv->buffer();
+       if (!update) {
+               // emit message signal.
+               buf.message(_("String not found."));
+       } else {
+               if (replace_count == 0) {
+                       buf.message(_("String found."));
+               } else if (replace_count == 1) {
+                       buf.message(_("String has been replaced."));
                } else {
-                       if (replace_count == 0) {
-                               buf.message(_("String found."));
-                       } else if (replace_count == 1) {
-                               buf.message(_("String has been replaced."));
-                       } else {
-                               docstring const str =
-                                       bformat(_("%1$d strings have been replaced."), replace_count);
-                               buf.message(str);
-                       }
+                       docstring const str =
+                               bformat(_("%1$d strings have been replaced."), replace_count);
+                       buf.message(str);
                }
-       } else if (findnext) {
-               // if we have deleted characters, we do not replace at all, but
-               // rather search for the next occurence
-               if (findOne(bv, search, casesensitive, matchword, forward, true, findnext))
-                       update = true;
-               else
-                       bv->message(_("String not found."));
        }
        return update;
 }
@@ -650,6 +640,7 @@ typedef vector<pair<string, string> > Escapes;
 
 /// A map of symbols and their escaped equivalent needed within a regex.
 /// @note Beware of order
+/*
 Escapes const & get_regexp_escapes()
 {
        typedef std::pair<std::string, std::string> P;
@@ -673,8 +664,10 @@ Escapes const & get_regexp_escapes()
        }
        return escape_map;
 }
+*/
 
 /// A map of lyx escaped strings and their unescaped equivalent.
+/*
 Escapes const & get_lyx_unescapes()
 {
        typedef std::pair<std::string, std::string> P;
@@ -693,8 +686,10 @@ Escapes const & get_lyx_unescapes()
        }
        return escape_map;
 }
+*/
 
 /// A map of escapes turning a regexp matching text to one matching latex.
+/*
 Escapes const & get_regexp_latex_escapes()
 {
        typedef std::pair<std::string, std::string> P;
@@ -712,10 +707,12 @@ Escapes const & get_regexp_latex_escapes()
        }
        return escape_map;
 }
+*/
 
 /** @todo Probably the maps need to be migrated to regexps, in order to distinguish if
  ** the found occurrence were escaped.
  **/
+/*
 string apply_escapes(string s, Escapes const & escape_map)
 {
        LYXERR(Debug::FIND, "Escaping: '" << s << "'");
@@ -733,55 +730,92 @@ string apply_escapes(string s, Escapes const & escape_map)
        LYXERR(Debug::FIND, "Escaped : '" << s << "'");
        return s;
 }
+*/
 
 
+string string2regex(string in)
+{
+       static std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s\$\\])" };
+       string temp = std::regex_replace(in, specialChars,  R"(\$&)" );
+       string temp2("");
+       size_t lastpos = 0;
+       size_t fl_pos = 0;
+       int offset = 1;
+       while (fl_pos < temp.size()) {
+               fl_pos = temp.find("\\\\foreignlanguage", lastpos + offset);
+               if (fl_pos == string::npos)
+                       break;
+               offset = 16;
+               temp2 += temp.substr(lastpos, fl_pos - lastpos);
+               temp2 += "\\n";
+               lastpos = fl_pos;
+       }
+       if (lastpos == 0)
+               return(temp);
+       if (lastpos < temp.size()) {
+               temp2 += temp.substr(lastpos, temp.size() - lastpos);
+       }
+       return temp2;
+}
+
+string correctRegex(string t)
+{
+       /* Convert \backslash => \
+        * and \{, \}, \[, \] => {, }, [, ]
+        */
+       string s("");
+       regex wordre("(\\\\)*(\\\\((backslash|mathcircumflex) ?|[\\[\\]\\{\\}]))");
+       size_t lastpos = 0;
+       smatch sub;
+       for (sregex_iterator it(t.begin(), t.end(), wordre), end; it != end; ++it) {
+               sub = *it;
+               string replace;
+               if ((sub.position(2) - sub.position(0)) % 2 == 1) {
+                       continue;
+               }
+               else {
+                       if (sub.str(4) == "backslash")
+                               replace = "\\";
+                       else if (sub.str(4) == "mathcircumflex")
+                               replace = "^";
+                       else
+                               replace = sub.str(3);
+               }
+               if (lastpos < (size_t) sub.position(2))
+                       s += t.substr(lastpos, sub.position(2) - lastpos);
+               s += replace;
+               lastpos = sub.position(2) + sub.length(2);
+       }
+       if (lastpos == 0)
+               return t;
+       else if (lastpos < t.length())
+               s += t.substr(lastpos, t.length() - lastpos);
+       return s;
+}
+
 /// Within \regexp{} apply get_lyx_unescapes() only (i.e., preserve regexp semantics of the string),
 /// while outside apply get_lyx_unescapes()+get_regexp_escapes().
 /// If match_latex is true, then apply regexp_latex_escapes() to \regexp{} contents as well.
-string escape_for_regex(string s, bool match_latex)
+string escape_for_regex(string s)
 {
-       size_t pos = 0;
-       while (pos < s.size()) {
-               size_t new_pos = s.find("\\regexp{", pos);
-               if (new_pos == string::npos)
-                       new_pos = s.size();
-               string t;
-               if (new_pos > pos) {
-                       // outside regexp
-                       LYXERR(Debug::FIND, "new_pos: " << new_pos);
-                       t = apply_escapes(s.substr(pos, new_pos - pos), get_lyx_unescapes());
-                       LYXERR(Debug::FIND, "t [lyx]: " << t);
-                       t = apply_escapes(t, get_regexp_escapes());
-                       LYXERR(Debug::FIND, "t [rxp]: " << t);
-                       s.replace(pos, new_pos - pos, t);
-                       new_pos = pos + t.size();
-                       LYXERR(Debug::FIND, "Regexp after escaping: " << s);
-                       LYXERR(Debug::FIND, "new_pos: " << new_pos);
-                       if (new_pos == s.size())
-                               break;
-               }
-               // Might fail if \\endregexp{} is preceeded by unexpected stuff (weird escapes)
-               size_t end_pos = s.find("\\endregexp{}}", new_pos + 8);
-               LYXERR(Debug::FIND, "end_pos: " << end_pos);
-               t = s.substr(new_pos + 8, end_pos - (new_pos + 8));
-               LYXERR(Debug::FIND, "t in regexp      : " << t);
-               t = apply_escapes(t, get_lyx_unescapes());
-               LYXERR(Debug::FIND, "t in regexp after unescapes [lyx]: " << t);
-               if (match_latex) {
-                       t = apply_escapes(t, get_regexp_latex_escapes());
-                       LYXERR(Debug::FIND, "t in regexp after latex_escapes [ltx]: " << t);
+       size_t lastpos = 0;
+       string result = "";
+       while (lastpos < s.size()) {
+               size_t regex_pos = s.find("\\regexp{", lastpos);
+               if (regex_pos == string::npos) {
+                       regex_pos = s.size();
                }
-               if (end_pos == s.size()) {
-                       s.replace(new_pos, end_pos - new_pos, t);
-                       LYXERR(Debug::FIND, "Regexp after \\regexp{} removal: " << s);
-                       break;
+               if (regex_pos > lastpos) {
+                       result += string2regex(s.substr(lastpos, regex_pos-lastpos));
+                       lastpos = regex_pos;
+                       if (lastpos == s.size())
+                               break;
                }
-               s.replace(new_pos, end_pos + 13 - new_pos, t);
-               LYXERR(Debug::FIND, "Regexp after \\regexp{...\\endregexp{}} removal: " << s);
-               pos = new_pos + t.size();
-               LYXERR(Debug::FIND, "pos: " << pos);
+               size_t end_pos = s.find("\\endregexp{}}", regex_pos + 8);
+               result += correctRegex(s.substr(regex_pos + 8, end_pos -(regex_pos + 8)));
+               lastpos = end_pos + 13;
        }
-       return s;
+       return result;
 }
 
 
@@ -1428,6 +1462,9 @@ static void buildAccentsMap()
   accents["i"] = "ı";
   accents["jmath"] = "ȷ";
   accents["cdot"] = "·";
+  accents["textasciicircum"] = "^";
+  accents["mathcircumflex"] = "^";
+  accents["sim"] = "~";
   accents["guillemotright"] = "»";
   accents["guillemotleft"] = "«";
   accents["hairspace"]     = getutf8(0xf0000); // select from free unicode plane 15
@@ -1520,7 +1557,7 @@ void Intervall::removeAccents()
     buildAccentsMap();
   static regex const accre("\\\\(([\\S]|grave|breve|ddot|dot|acute|dacute|mathring|check|hat|bar|tilde|subdot|ogonek|"
          "cedilla|subring|textsubring|subhat|textsubcircum|subtilde|textsubtilde|dgrave|textdoublegrave|rcap|textroundcap|slashed)\\{[^\\{\\}]+\\}"
-      "|((i|imath|jmath|cdot|[a-z]+space)|((backslash )?([lL]y[xX]|[tT]e[xX]|[lL]a[tT]e[xX]e?|lyxarrow))|guillemot(left|right))(?![a-zA-Z]))");
+      "|((i|imath|jmath|cdot|[a-z]+space)|((backslash )?([lL]y[xX]|[tT]e[xX]|[lL]a[tT]e[xX]e?|lyxarrow))|guillemot(left|right)|textasciicircum|mathcircumflex|sim)(?![a-zA-Z]))");
   smatch sub;
   for (sregex_iterator itacc(par.begin(), par.end(), accre), end; itacc != end; ++itacc) {
     sub = *itacc;
@@ -2048,6 +2085,10 @@ void LatexInfo::buildEntries(bool isPatternString)
           key += interval_.par.substr(params, optend-params);
           evaluatingOptional = true;
           optionalEnd = optend;
+          if (found.keytype == KeyInfo::isSectioning) {
+            // Remove optional values (but still keep in header)
+            interval_.addIntervall(params, optend);
+          }
         }
         string token = sub.str(7);
         int closings;
@@ -2083,10 +2124,10 @@ void LatexInfo::buildEntries(bool isPatternString)
           }
           else {
             found._dataStart = found._tokenstart + found._tokensize;
-          } 
+          }
           closings = 0;
         }
-        if (interval_.par.substr(found._dataStart-1, 15).compare("\\endarguments{}") == 0) {
+        if (interval_.par.substr(found._dataStart, 15).compare("\\endarguments{}") == 0) {
           found._dataStart += 15;
         }
         size_t endpos;
@@ -2999,7 +3040,6 @@ static void modifyRegexForMatchWord(string &t)
 MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
        : p_buf(&buf), p_first_buf(&buf), opt(opt)
 {
-       static std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s\\])" };
        Buffer & find_buf = *theBufferList().getBuffer(FileName(to_utf8(opt.find_buf_name)), true);
        docstring const & ds = stringifySearchBuffer(find_buf, opt);
        use_regexp = lyx::to_utf8(ds).find("\\regexp{") != std::string::npos;
@@ -3019,6 +3059,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
        size_t lead_size = 0;
        // correct the language settings
        par_as_string = correctlanguagesetting(par_as_string, true, !opt.ignoreformat);
+       opt.matchstart = false;
        if (!use_regexp) {
                identifyClosing(par_as_string); // Removes math closings ($, ], ...) at end of string
                if (opt.ignoreformat) {
@@ -3028,9 +3069,9 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
                        lead_size = identifyLeading(par_as_string);
                }
                lead_as_string = par_as_string.substr(0, lead_size);
-               string lead_as_regex_string = std::regex_replace(lead_as_string, specialChars,  R"(\$&)" );
+               string lead_as_regex_string = string2regex(lead_as_string);
                par_as_string_nolead = par_as_string.substr(lead_size, par_as_string.size() - lead_size);
-               string par_as_regex_string_nolead = std::regex_replace(par_as_string_nolead, specialChars,  R"(\$&)" );
+               string par_as_regex_string_nolead = string2regex(par_as_string_nolead);
                /* Handle whole words too in this case
                */
                if (opt.matchword) {
@@ -3041,6 +3082,8 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
                string regexp2_str = "(" + lead_as_regex_string + ")(.*?)" + par_as_regex_string_nolead;
                CreateRegexp(opt, regexp_str, regexp2_str);
                use_regexp = true;
+               LYXERR(Debug::FIND, "Setting regexp to : '" << regexp_str << "'");
+               LYXERR(Debug::FIND, "Setting regexp2 to: '" << regexp2_str << "'");
                return;
        }
 
@@ -3056,17 +3099,19 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
        {
                string lead_as_regexp;
                if (lead_size > 0) {
-                       // @todo No need to search for \regexp{} insets in leading material
-                       lead_as_regexp = std::regex_replace(par_as_string.substr(0, lead_size), specialChars,  R"(\$&)" );
-                       // lead_as_regexp = escape_for_regex(par_as_string.substr(0, lead_size), !opt.ignoreformat);
+                       lead_as_regexp = string2regex(par_as_string.substr(0, lead_size));
+                       regex_replace(par_as_string_nolead, par_as_string_nolead, "}$", "");
                        par_as_string = par_as_string_nolead;
                        LYXERR(Debug::FIND, "lead_as_regexp is '" << lead_as_regexp << "'");
                        LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
                }
                LYXERR(Debug::FIND, "par_as_string before escape_for_regex() is '" << par_as_string << "'");
-               par_as_string = escape_for_regex(par_as_string, !opt.ignoreformat);
+               par_as_string = escape_for_regex(par_as_string);
                // Insert (.*?) before trailing closure of math, macros and environments, so to catch parts of them.
                LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
+               LYXERR(Debug::FIND, "par_as_string after correctRegex is '" << par_as_string << "'");
+               ++close_wildcards;
+               /*
                if (
                        // Insert .* before trailing '\$' ('$' has been escaped by escape_for_regex)
                        regex_replace(par_as_string, par_as_string, "(.*[^\\\\])(\\\\\\$)\\'", "$1(.*?)$2")
@@ -3080,9 +3125,10 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
                        ) {
                        ++close_wildcards;
                }
+               */
+               size_t lng = par_as_string.size();
                if (!opt.ignoreformat) {
                        // Remove extra '\}' at end if not part of \{\.\}
-                       size_t lng = par_as_string.size();
                        while(lng > 2) {
                                if (par_as_string.substr(lng-2, 2).compare("\\}") == 0) {
                                        if (lng >= 6) {
@@ -3098,9 +3144,13 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
                        if (lng < par_as_string.size())
                                par_as_string = par_as_string.substr(0,lng);
                }
+               if ((lng > 0) && (par_as_string[0] == '^')) {
+                       par_as_string = par_as_string.substr(1);
+                       --lng;
+                       opt.matchstart = true;
+               }
                LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
                LYXERR(Debug::FIND, "Open braces: " << open_braces);
-               LYXERR(Debug::FIND, "Close .*?  : " << close_wildcards);
                LYXERR(Debug::FIND, "Replaced text (to be used as regex): " << par_as_string);
 
                // If entered regexp must match at begin of searched string buffer
@@ -3117,11 +3167,11 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
                                while (regex_replace(par_as_string, par_as_string, orig, dest));
                        }
                        /* opt.matchword is ignored if using regex
-                         but expanding par_as_string with "\\b" is not appropriate here
+                         so expanding par_as_string with "\\b" seems appropriate here
                          if regex contains for instance '.*' or '.+'
                          1.) Nothing to do, if 'par_as_string' contains "\\b" already.
                              (Means, that the user knows how to handle whole words
-                         2.) else replace '.' with "\\S" and prepend + append "\\b"
+                         2.) else replace '.' with "\\S" and wrap the regex with "\\b"
                        */
                        if (opt.matchword) {
                                modifyRegexForMatchWord(par_as_string);
@@ -3383,8 +3433,18 @@ MatchResult MatchStringAdv::operator()(DocIterator const & cur, int len, bool at
        int res = mres.match_len;
        LYXERR(Debug::FIND,
               "res=" << res << ", at_begin=" << at_begin
-              << ", matchword=" << opt.matchword
+              << ", matchstart=" << opt.matchstart
               << ", inTexted=" << cur.inTexted());
+       if (opt.matchstart) {
+               if (cur.pos() != 0)
+                       mres.match_len = 0;
+               else if (mres.match_prefix > 0)
+                       mres.match_len = 0;
+               return mres;
+       }
+       else
+               return mres;
+       /* DEAD CODE follows
        if (res == 0 || !at_begin || !opt.matchword || !cur.inTexted())
                return mres;
        if ((len > 0) && (res < len)) {
@@ -3420,6 +3480,7 @@ MatchResult MatchStringAdv::operator()(DocIterator const & cur, int len, bool at
        }
        mres.match_len = 0;
        return mres;
+       */
 }
 
 #if 0
@@ -3625,10 +3686,13 @@ static void displayMResult(MatchResult &mres, int increment)
        #define displayMres(s,i)
 #endif
 
+/*
+ * Not good, we miss possible matches containing also characters not found in
+ * the innermost depth.
 static bool findAdvForwardInnermost(DocIterator & cur)
 {
        size_t d;
-       DocIterator old_cur(cur.buffer());
+       DocIterator old_cur = cur;
        int forwardCount = 0;
        do {
                d = cur.depth();
@@ -3652,6 +3716,7 @@ static bool findAdvForwardInnermost(DocIterator & cur)
        else
                return false;
 }
+*/
 
 /** Finalize an advanced find operation, advancing the cursor to the innermost
  ** position that matches, plus computing the length of the matching text to
@@ -3672,7 +3737,8 @@ MatchResult &findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, Ma
        // so the search for "www" gives prefix_len = 7 (== sizeof("http://")
        // and although we search for only 3 chars, we find the whole hyperlink inset
        bool at_begin = (expected.match_prefix == 0);
-       if (findAdvForwardInnermost(cur)) {
+       //if (findAdvForwardInnermost(cur)) {
+       if (0) {
                mres = match(cur, -1, at_begin);
                displayMres(mres, 0);
                if (expected.match_len > 0) {
@@ -3683,7 +3749,7 @@ MatchResult &findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, Ma
                        if (mres.match_len <= 0)
                                return fail;
                }
-               max_match = mres.match_len;
+               max_match = mres;
        }
        else if (expected.match_len < 0) {
                mres = match(cur);      /* match valid only if not searching whole words */
@@ -3799,7 +3865,7 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match)
        if (!cur)
                return 0;
        while (!theApp()->longOperationCancelled() && cur) {
-               (void) findAdvForwardInnermost(cur);
+               //(void) findAdvForwardInnermost(cur);
                LYXERR(Debug::FIND, "findForwardAdv() cur: " << cur);
                MatchResult mres = match(cur, -1, false);
                displayMres(mres,-1)