]> git.lyx.org Git - lyx.git/blobdiff - src/lyxfind.cpp
Show plural and caps status for formatted references. Rest of #11073.
[lyx.git] / src / lyxfind.cpp
index 1c8cdb3b66ff730320bea95bdb5a04067b4947a4..58502a0c7bde1a5bc46edc9dfeda357db9cffa4f 100644 (file)
@@ -51,6 +51,8 @@
 
 #include <map>
 #include <regex>
+
+//#define ResultsDebug
 #define USE_QT_FOR_SEARCH
 #if defined(USE_QT_FOR_SEARCH)
        #include <QtCore>       // sets QT_VERSION
@@ -797,7 +799,7 @@ bool regex_replace(string const & s, string & t, string const & searchstr,
        return rv;
 }
 
-
+#if 0
 /** Checks if supplied string segment is well-formed from the standpoint of matching open-closed braces.
  **
  ** Verify that closed braces exactly match open braces. This avoids that, for example,
@@ -851,7 +853,7 @@ bool braces_match(string const & beg,
        LYXERR(Debug::FIND, "Braces match as expected");
        return true;
 }
-
+#endif
 
 class MatchResult {
 public:
@@ -865,7 +867,8 @@ public:
        int match2end;
        int pos;
        int leadsize;
-       MatchResult(): match_len(0),match_prefix(0),match2end(0), pos(0),leadsize(0) {};
+       vector <string> result = vector <string>();
+       MatchResult(int len = 0): match_len(len),match_prefix(0),match2end(0), pos(0),leadsize(0) {};
 };
 
 static MatchResult::range interpretMatch(MatchResult &oldres, MatchResult &newres)
@@ -956,8 +959,24 @@ private:
 public:
        // Are we searching with regular expressions ?
        bool use_regexp;
+       static int valid_matches;
+       static vector <string> matches;
+       void FillResults(MatchResult &found_mr);
 };
 
+int MatchStringAdv::valid_matches = 0;
+vector <string> MatchStringAdv::matches = vector <string>(10);
+
+void MatchStringAdv::FillResults(MatchResult &found_mr)
+{
+  if (found_mr.match_len > 0) {
+    valid_matches = found_mr.result.size();
+    for (size_t i = 0; i < found_mr.result.size(); i++)
+      matches[i] = found_mr.result[i];
+  }
+  else
+    valid_matches = 0;
+}
 
 static docstring buffer_to_latex(Buffer & buffer)
 {
@@ -1407,6 +1426,8 @@ static void buildAccentsMap()
   accents["i"] = "ı";
   accents["jmath"] = "ȷ";
   accents["cdot"] = "·";
+  accents["guillemotright"] = "»";
+  accents["guillemotleft"] = "«";
   accents["hairspace"]     = getutf8(0xf0000); // select from free unicode plane 15
   accents["thinspace"]     = getutf8(0xf0002); // and used _only_ by findadv
   accents["negthinspace"]  = getutf8(0xf0003); // to omit backslashed latex macros
@@ -1497,7 +1518,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)))(?![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))(?![a-zA-Z]))");
   smatch sub;
   for (sregex_iterator itacc(par.begin(), par.end(), accre), end; itacc != end; ++itacc) {
     sub = *itacc;
@@ -1838,13 +1859,6 @@ void LatexInfo::buildEntries(bool isPatternString)
         key = sub.str(2)[1];
       else {
         key = sub.str(2);
-        if (key == "$") {
-          size_t k_pos = sub.position(size_t(2));
-          if ((k_pos > 0) && (interval_.par[k_pos - 1] == '\\')) {
-            // Escaped '$', ignoring
-            continue;
-          }
-        }
       }
     }
     if (keys.find(key) != keys.end()) {
@@ -2183,7 +2197,6 @@ void LatexInfo::buildKeys(bool isPatternString)
   makeKey("textasciicircum|textasciitilde", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
   makeKey("textasciiacute|texemdash",       KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
   makeKey("dots|ldots",                     KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
-  makeKey("guillemotright|guillemotleft",   KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
   // Spaces
   makeKey("quad|qquad|hfill|dotfill",               KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
   makeKey("textvisiblespace|nobreakspace",          KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
@@ -3178,11 +3191,6 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be
                QRegularExpressionMatch match = p_regexp->match(qstr, 0, flags);
                if (!match.hasMatch())
                        return mres;
-               // Check braces on segments that matched all (.*?) subexpressions,
-               // except the last "padding" one inserted by lyx.
-               for (int i = 3; i < match.lastCapturedIndex(); ++i)
-                       if (!braces_match(match.captured(i), open_braces))
-                               return mres;
 #else
                regex const *p_regexp;
                regex_constants::match_flag_type flags;
@@ -3197,49 +3205,25 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be
                if (re_it == sregex_iterator())
                        return mres;
                match_results<string::const_iterator> const & m = *re_it;
-               // Check braces on segments that matched all (.*?) subexpressions,
-               // except the last "padding" one inserted by lyx.
-               for (size_t i = 3; i < m.size() - 1; ++i)
-                       if (!braces_match(m[i], open_braces))
-                               return mres;
 #endif
-               // Exclude from the returned match length any length
-               // due to close wildcards added at end of regexp
-               // and also the length of the leading (e.g. '\emph{}')
+               // Whole found string, including the leading
+               // std: m[0].second - m[0].first
+               // Qt: match.capturedEnd(0) - match.capturedStart(0)
                //
-               // Whole found string, including the leading: m[0].second - m[0].first
-               // Size of the leading string: m[1].second - m[1].first
+               // Size of the leading string
+               // std: m[1].second - m[1].first
+               // Qt: match.capturedEnd(1) - match.capturedStart(1)
                int leadingsize = 0;
-               int result;
 #if QTSEARCH
                if (match.lastCapturedIndex() > 0) {
                        leadingsize = match.capturedEnd(1) - match.capturedStart(1);
                }
-               int lastidx = match.lastCapturedIndex();
-               for (int i = 0; i <= lastidx; i++) {
-                       LYXERR(Debug::FIND, "Match " << i << " is " << match.capturedEnd(i) - match.capturedStart(i) << " long");
-               }
-               if (close_wildcards == 0)
-                       result = match.capturedEnd(0) - match.capturedStart(0);
-               else
-                       result =  match.capturedStart(lastidx + 1 - close_wildcards) - match.capturedStart(0);
 
 #else
                if (m.size() > 2) {
                        leadingsize = m[1].second - m[1].first;
                }
-               for (size_t i = 0; i < m.size(); i++) {
-                       LYXERR(Debug::FIND, "Match " << i << " is " << m[i].second - m[i].first << " long");
-               }
-               if (close_wildcards == 0)
-                       result = m[0].second - m[0].first;
-               else
-                       result =  m[m.size() - close_wildcards].first - m[0].first;
 #endif
-               if (result > leadingsize)
-                       result -= leadingsize;
-               else
-                       result = 0;
 #if QTSEARCH
                mres.match_prefix = match.capturedEnd(2) - match.capturedStart(2);
                mres.match_len = match.capturedEnd(0) - match.capturedEnd(2);
@@ -3298,6 +3282,23 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be
                if (mres.match2end < 0)
                  mres.match_len = 0;
                mres.leadsize = leadingsize;
+#if QTSEARCH
+               if (mres.match_len > 0) {
+                 string a0 = match.captured(0).mid(mres.pos + mres.match_prefix, mres.match_len).toStdString();
+                 mres.result.push_back(a0);
+                 for (int i = 3; i <= match.lastCapturedIndex(); i++) {
+                   mres.result.push_back(match.captured(i).toStdString());
+                 }
+               }
+#else
+               if (mres.match_len > 0) {
+                 string a0 = m[0].str().substr(mres.pos + mres.match_prefix, mres.match_len);
+                 mres.result.push_back(a0);
+                 for (size_t i = 3; i < m.size(); i++) {
+                   mres.result.push_back(m[i]);
+                 }
+               }
+#endif
                return mres;
        }
 
@@ -3571,7 +3572,7 @@ docstring latexifyFromCursor(DocIterator const & cur, int len)
        return ods.str();
 }
 
-#if 0
+#if defined(ResultsDebug)
 // Debugging output
 static void displayMResult(MatchResult &mres, int increment)
 {
@@ -3580,6 +3581,8 @@ static void displayMResult(MatchResult &mres, int increment)
   LYXERR0( "match_len: " << mres.match_len);
   LYXERR0( "match_prefix: " << mres.match_prefix);
   LYXERR0( "match2end: " << mres.match2end);
+  for (size_t i = 0; i < mres.result.size(); i++)
+    LYXERR0( "Match " << i << " = \"" << mres.result[i] << "\"");
 }
        #define displayMres(s,i) displayMResult(s,i);
 #else
@@ -3618,47 +3621,48 @@ static bool findAdvForwardInnermost(DocIterator & cur)
  ** position that matches, plus computing the length of the matching text to
  ** be selected
  **/
-int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expected_len, int prefix_len = 0)
+MatchResult &findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, MatchResult const & expected = MatchResult(-1))
 {
        // Search the foremost position that matches (avoids find of entire math
        // inset when match at start of it)
        DocIterator old_cur(cur.buffer());
        MatchResult mres;
-       int max_match;
+       static MatchResult fail = MatchResult();
+       static MatchResult max_match;
        // If (prefix_len > 0) means that forwarding 1 position will remove the complete entry
        // Happens with e.g. hyperlinks
        // either one sees "http://www.bla.bla" or nothing
        // 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 = (prefix_len == 0);
+       bool at_begin = (expected.match_prefix == 0);
        if (findAdvForwardInnermost(cur)) {
                mres = match(cur, -1, at_begin);
                displayMres(mres, 0);
-               if (expected_len > 0) {
-                       if (mres.match_len < expected_len)
-                               return 0;
+               if (expected.match_len > 0) {
+                       if (mres.match_len < expected.match_len)
+                               return fail;
                }
                else {
                        if (mres.match_len <= 0)
-                               return 0;
+                               return fail;
                }
                max_match = mres.match_len;
        }
-       else if (expected_len < 0) {
+       else if (expected.match_len < 0) {
                mres = match(cur);      /* match valid only if not searching whole words */
                displayMres(mres, 0);
-               max_match = mres.match_len;
+               max_match = mres;
        }
        else {
-               max_match = expected_len;
+               max_match = expected;
        }
-       if (max_match <= 0) return 0;
+       if (max_match.match_len <= 0) return fail;
        LYXERR(Debug::FIND, "Ok");
 
        // Compute the match length
         int len = 1;
        if (cur.pos() + len > cur.lastpos())
-         return 0;
+         return fail;
        // regexp should use \w+, \S+, or \b(some string)\b
        // to search for whole words
        if (match.opt.matchword && !match.use_regexp) {
@@ -3668,18 +3672,17 @@ int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expecte
             LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
           }
           // Length of matched text (different from len param)
-          int old_match = match(cur, len, at_begin).match_len;
-          if (old_match < 0)
-            old_match = 0;
-          int new_match;
+          static MatchResult old_match = match(cur, len, at_begin);
+          if (old_match.match_len < 0)
+            old_match = fail;
+          MatchResult new_match;
           // Greedy behaviour while matching regexps
-          while ((new_match = match(cur, len + 1, at_begin).match_len) > old_match) {
+          while ((new_match = match(cur, len + 1, at_begin)).match_len > old_match.match_len) {
             ++len;
             old_match = new_match;
             LYXERR(Debug::FIND, "verifying   match with len = " << len);
           }
-          if (old_match == 0)
-            len = 0;
+         return old_match;
         }
         else {
           int minl = 1;
@@ -3690,7 +3693,7 @@ int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expecte
            mres2 = match(cur, len, at_begin);
            displayMres(mres2, len);
             int actual_match = mres2.match_len;
-            if (actual_match >= max_match) {
+            if (actual_match >= max_match.match_len) {
               // actual_match > max_match _can_ happen,
               // if the search area splits
               // some following word so that the regex
@@ -3712,7 +3715,7 @@ int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expecte
           old_cur = cur;
           // Search for real start of matched characters
           while (len > 1) {
-            int actual_match;
+            MatchResult actual_match;
             do {
               cur.forwardPos();
             } while (cur.depth() > old_cur.depth()); /* Skip inner insets */
@@ -3723,8 +3726,8 @@ int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expecte
             }
             if (cur.pos() != old_cur.pos()) {
               // OK, forwarded 1 pos in actual inset
-              actual_match = match(cur, len-1, at_begin).match_len;
-              if (actual_match == max_match) {
+              actual_match = match(cur, len-1, at_begin);
+              if (actual_match.match_len == max_match.match_len) {
                 // Ha, got it! The shorter selection has the same match length
                 len--;
                 old_cur = cur;
@@ -3737,17 +3740,22 @@ int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, int expecte
             }
             else {
               LYXERR(Debug::INFO, "cur.pos() == old_cur.pos(), this should never happen");
-              actual_match = match(cur, len, at_begin).match_len;
-              if (actual_match == max_match)
+              actual_match = match(cur, len, at_begin);
+              if (actual_match.match_len == max_match.match_len) {
                 old_cur = cur;
+               max_match = actual_match;
+             }
             }
           }
+         if (len == 0)
+           return fail;
+         else
+           return max_match;
        }
-       return len;
 }
 
 /// Finds forward
-int findForwardAdv(DocIterator & cur, MatchStringAdv const & match)
+int findForwardAdv(DocIterator & cur, MatchStringAdv & match)
 {
        if (!cur)
                return 0;
@@ -3815,9 +3823,11 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv const & match)
                        // LYXERR0("Leaving first loop");
                        {
                          LYXERR(Debug::FIND, "Finalizing 1");
-                         int len = findAdvFinalize(cur, match, mres.match_len, mres.match_prefix);
-                         if (len > 0)
-                           return len;
+                         MatchResult found_match = findAdvFinalize(cur, match, mres);
+                         if (found_match.match_len > 0) {
+                           match.FillResults(found_match);
+                           return found_match.match_len;
+                         }
                          else {
                            // try next possible match
                            cur.forwardPos();
@@ -3855,9 +3865,10 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv const & match)
                                        // Sometimes in finalize we understand it wasn't a match
                                        // and we need to continue the outest loop
                                        LYXERR(Debug::FIND, "Finalizing 2");
-                                       int len = findAdvFinalize(cur, match, mres.match_len);
-                                       if (len > 0) {
-                                               return len;
+                                       MatchResult mres4 = findAdvFinalize(cur, match, mres.match_len);
+                                       if (mres4.match_len > 0) {
+                                               match.FillResults(mres4);
+                                               return mres4.match_len;
                                        }
                                }
                                if (match_len2 > 0)
@@ -3889,11 +3900,11 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv const & match)
 
 
 /// Find the most backward consecutive match within same paragraph while searching backwards.
-int findMostBackwards(DocIterator & cur, MatchStringAdv const & match)
+MatchResult &findMostBackwards(DocIterator & cur, MatchStringAdv const & match)
 {
        DocIterator cur_begin = doc_iterator_begin(cur.buffer());
        DocIterator tmp_cur = cur;
-       int len = findAdvFinalize(tmp_cur, match, -1);
+       static MatchResult mr = findAdvFinalize(tmp_cur, match, MatchResult(-1));
        Inset & inset = cur.inset();
        for (; cur != cur_begin; cur.backwardPos()) {
                LYXERR(Debug::FIND, "findMostBackwards(): cur=" << cur);
@@ -3901,13 +3912,13 @@ int findMostBackwards(DocIterator & cur, MatchStringAdv const & match)
                new_cur.backwardPos();
                if (new_cur == cur || &new_cur.inset() != &inset || !match(new_cur).match_len)
                        break;
-               int new_len = findAdvFinalize(new_cur, match, -1);
-               if (new_len == len)
+               MatchResult new_mr = findAdvFinalize(new_cur, match, MatchResult(-1));
+               if (new_mr.match_len == mr.match_len)
                        break;
-               len = new_len;
+               mr = new_mr;
        }
        LYXERR(Debug::FIND, "findMostBackwards(): exiting with cur=" << cur);
-       return len;
+       return mr;
 }
 
 
@@ -3938,8 +3949,11 @@ int findBackwardsAdv(DocIterator & cur, MatchStringAdv & match)
                                found_match = (match(cur).match_len > 0);
                                LYXERR(Debug::FIND, "findBackAdv3: found_match="
                                       << found_match << ", cur: " << cur);
-                               if (found_match)
-                                       return findMostBackwards(cur, match);
+                               if (found_match) {
+                                       MatchResult found_mr = findMostBackwards(cur, match);
+                                       match.FillResults(found_mr);
+                                       return found_mr.match_len;
+                               }
 
                                // Stop if begin of document reached
                                if (cur == cur_begin)
@@ -4040,9 +4054,38 @@ static void changeFirstCase(Buffer & buffer, TextCase first_case, TextCase other
        right = pit->size();
        pit->changeCase(buffer.params(), pos_type(1), right, others_case);
 }
-
 } // namespace
 
+#if 1
+static bool replaceMatches(string &t, int maxmatchnum, vector <string> const & replacements)
+{
+  // Should replace the string "$" + std::to_string(matchnum) with replacement
+  // if the char '$' is not prefixed with odd number of char '\\'
+  static regex const rematch("(\\\\)*(\\$\\$([0-9]))");
+  string s;
+  size_t lastpos = 0;
+  smatch sub;
+  for (sregex_iterator it(t.begin(), t.end(), rematch), end; it != end; ++it) {
+    sub = *it;
+    if ((sub.position(2) - sub.position(0)) % 2 == 1)
+      continue;
+    int num = stoi(sub.str(3), nullptr, 10);
+    if (num >= maxmatchnum)
+      continue;
+    if (lastpos < (size_t) sub.position(2))
+      s += t.substr(lastpos, sub.position(2) - lastpos);
+    s += replacements[num];
+    lastpos = sub.position(2) + sub.length(2);
+  }
+  if (lastpos == 0)
+    return false;
+  else if (lastpos < t.length())
+    s += t.substr(lastpos, t.length() - lastpos);
+  t = s;
+  return true;
+}
+#endif
+
 ///
 static int findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, MatchStringAdv & matchAdv)
 {
@@ -4073,6 +4116,9 @@ static int findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, Ma
        ostringstream oss;
        repl_buffer_orig.write(oss);
        string lyx = oss.str();
+       if (matchAdv.valid_matches > 0) {
+         replaceMatches(lyx, matchAdv.valid_matches, matchAdv.matches);
+       }
        Buffer repl_buffer("", false);
        repl_buffer.setUnnamed(true);
        LASSERT(repl_buffer.readString(lyx), return 0);