]> git.lyx.org Git - lyx.git/blobdiff - src/lyxfind.cpp
FindAdv: Allow regex searches at start of paragraph
[lyx.git] / src / lyxfind.cpp
index a82fef2e3f2e644590a9e00d6787b20a9c1a0f77..abbabf9a3491270bb7d712e178ceda210813c13f 100644 (file)
@@ -893,7 +893,7 @@ static MatchResult::range interpretMatch(MatchResult &oldres, MatchResult &newre
 
 class MatchStringAdv {
 public:
-       MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const & opt);
+       MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt);
 
        /** Tests if text starting at the supplied position matches with the one provided to the MatchStringAdv
         ** constructor as opt.search, under the opt.* options settings.
@@ -2971,7 +2971,32 @@ void MatchStringAdv::CreateRegexp(FindAndReplaceOptions const & opt, string rege
 #endif
 }
 
-MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const & opt)
+static void modifyRegexForMatchWord(string &t)
+{
+       string s("");
+       regex wordre("(\\\\)*((\\.|\\\\b))");
+       size_t lastpos = 0;
+       smatch sub;
+       for (sregex_iterator it(t.begin(), t.end(), wordre), end; it != end; ++it) {
+               sub = *it;
+               if ((sub.position(2) - sub.position(0)) % 2 == 1) {
+                       continue;
+               }
+               else if (sub.str(2) == "\\\\b")
+                       return;
+               if (lastpos < (size_t) sub.position(2))
+                       s += t.substr(lastpos, sub.position(2) - lastpos);
+               s += "\\S";
+               lastpos = sub.position(2) + sub.length(2);
+       }
+       if (lastpos == 0)
+               return;
+       else if (lastpos < t.length())
+               s += t.substr(lastpos, t.length() - lastpos);
+      t = "\\b" + s + "\\b";
+}
+
+MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt)
        : p_buf(&buf), p_first_buf(&buf), opt(opt)
 {
        static std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s\\])" };
@@ -3006,6 +3031,12 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const &
                string lead_as_regex_string = std::regex_replace(lead_as_string, specialChars,  R"(\$&)" );
                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"(\$&)" );
+               /* Handle whole words too in this case
+               */
+               if (opt.matchword) {
+                       par_as_regex_string_nolead = "\\b" + par_as_regex_string_nolead + "\\b";
+                       opt.matchword = false;
+               }
                string regexp_str = "(" + lead_as_regex_string + ")()" + par_as_regex_string_nolead;
                string regexp2_str = "(" + lead_as_regex_string + ")(.*?)" + par_as_regex_string_nolead;
                CreateRegexp(opt, regexp_str, regexp2_str);
@@ -3025,9 +3056,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const &
        {
                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);
                        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 << "'");
@@ -3066,6 +3095,11 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const &
                        }
                        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);
@@ -3085,6 +3119,17 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const &
                                string dest = "\\" + std::to_string(i+2);
                                while (regex_replace(par_as_string, par_as_string, orig, dest));
                        }
+                       /* opt.matchword is ignored if using regex
+                         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 wrap the regex with "\\b"
+                       */
+                       if (opt.matchword) {
+                               modifyRegexForMatchWord(par_as_string);
+                               opt.matchword = false;
+                       }
                        regexp_str = "(" + lead_as_regexp + ")()" + par_as_string;
                        regexp2_str = "(" + lead_as_regexp + ")(.*?)" + par_as_string;
                }
@@ -3341,8 +3386,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)) {
@@ -3378,6 +3433,7 @@ MatchResult MatchStringAdv::operator()(DocIterator const & cur, int len, bool at
        }
        mres.match_len = 0;
        return mres;
+       */
 }
 
 #if 0
@@ -4179,7 +4235,7 @@ static int findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, Ma
 
 
 /// Perform a FindAdv operation.
-bool findAdv(BufferView * bv, FindAndReplaceOptions const & opt)
+bool findAdv(BufferView * bv, FindAndReplaceOptions & opt)
 {
        DocIterator cur;
        int pos_len = 0;