]> git.lyx.org Git - lyx.git/blobdiff - src/lyxfind.cpp
Account for old versions of Pygments
[lyx.git] / src / lyxfind.cpp
index 69a9b80883c37eaeb7bc14084484fa94e2b6920d..0ec9fcb7a039208226b4b3d0858dbd4ab1d48666 100644 (file)
@@ -39,6 +39,7 @@
 #include "mathed/InsetMath.h"
 #include "mathed/InsetMathGrid.h"
 #include "mathed/InsetMathHull.h"
+#include "mathed/MathData.h"
 #include "mathed/MathStream.h"
 #include "mathed/MathSupport.h"
 
@@ -132,7 +133,8 @@ bool searchAllowed(docstring const & str)
 
 
 bool findOne(BufferView * bv, docstring const & searchstr,
-            bool case_sens, bool whole, bool forward, bool find_del = true)
+            bool case_sens, bool whole, bool forward,
+            bool find_del = true, bool check_wrap = false)
 {
        if (!searchAllowed(searchstr))
                return false;
@@ -149,6 +151,32 @@ bool findOne(BufferView * bv, docstring const & searchstr,
 
        if (match_len > 0)
                bv->putSelectionAt(cur, match_len, !forward);
+       else if (check_wrap) {
+               DocIterator cur_orig(bv->cursor());
+               docstring q;
+               if (forward)
+                       q = _("End of file reached while searching forward.\n"
+                         "Continue searching from the beginning?");
+               else
+                       q = _("Beginning of file reached while searching backward.\n"
+                         "Continue searching from the end?");
+               int wrap_answer = frontend::Alert::prompt(_("Wrap search?"),
+                       q, 0, 1, _("&Yes"), _("&No"));
+               if (wrap_answer == 0) {
+                       if (forward) {
+                               bv->cursor().clear();
+                               bv->cursor().push_back(CursorSlice(bv->buffer().inset()));
+                       } else {
+                               bv->cursor().setCursor(doc_iterator_end(&bv->buffer()));
+                               bv->cursor().backwardPos();
+                       }
+                       bv->clearSelection();
+                       if (findOne(bv, searchstr, case_sens, whole, forward, find_del, false))
+                               return true;
+               }
+               bv->cursor().setCursor(cur_orig);
+               return false;
+       }
 
        return match_len > 0;
 }
@@ -221,11 +249,12 @@ pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
                           bool whole, bool forward, bool findnext)
 {
        Cursor & cur = bv->cursor();
+       bool found = false;
        if (!cur.selection()) {
                // no selection, non-empty search string: find it
                if (!searchstr.empty()) {
-                       findOne(bv, searchstr, case_sens, whole, forward);
-                       return make_pair(true, 0);
+                       found = findOne(bv, searchstr, case_sens, whole, forward, true, findnext);
+                       return make_pair(found, 0);
                }
                // empty search string
                if (!cur.inTexted())
@@ -253,8 +282,8 @@ pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
        // no selection or current selection is not search word:
        // just find the search word
        if (!have_selection || !match) {
-               findOne(bv, searchstr, case_sens, whole, forward);
-               return make_pair(true, 0);
+               found = findOne(bv, searchstr, case_sens, whole, forward, true, findnext);
+               return make_pair(found, 0);
        }
 
        // we're now actually ready to replace. if the buffer is
@@ -269,7 +298,7 @@ pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
                        cur.pos() = cur.lastpos());
        }
        if (findnext)
-               findOne(bv, searchstr, case_sens, whole, forward, false);
+               findOne(bv, searchstr, case_sens, whole, forward, false, findnext);
 
        return make_pair(true, 1);
 }
@@ -323,7 +352,7 @@ bool lyxfind(BufferView * bv, FuncRequest const & ev)
        bool matchword     = parse_bool(howto);
        bool forward       = parse_bool(howto);
 
-       return findOne(bv, search, casesensitive, matchword, forward);
+       return findOne(bv, search, casesensitive, matchword, forward, true, true);
 }
 
 
@@ -380,7 +409,7 @@ bool lyxreplace(BufferView * bv,
        } 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))
+               if (findOne(bv, search, casesensitive, matchword, forward, true, findnext))
                        update = true;
                else
                        bv->message(_("String not found."));
@@ -389,21 +418,57 @@ bool lyxreplace(BufferView * bv,
 }
 
 
-bool findNextChange(DocIterator & cur)
+bool findNextChange(BufferView * bv, Cursor & cur, bool const check_wrap)
 {
        for (; cur; cur.forwardPos())
                if (cur.inTexted() && cur.paragraph().isChanged(cur.pos()))
                        return true;
+
+       if (check_wrap) {
+               DocIterator cur_orig(bv->cursor());
+               docstring q = _("End of file reached while searching forward.\n"
+                         "Continue searching from the beginning?");
+               int wrap_answer = frontend::Alert::prompt(_("Wrap search?"),
+                       q, 0, 1, _("&Yes"), _("&No"));
+               if (wrap_answer == 0) {
+                       bv->cursor().clear();
+                       bv->cursor().push_back(CursorSlice(bv->buffer().inset()));
+                       bv->clearSelection();
+                       cur.setCursor(bv->cursor().selectionBegin());
+                       if (findNextChange(bv, cur, false))
+                               return true;
+               }
+               bv->cursor().setCursor(cur_orig);
+       }
+
        return false;
 }
 
 
-bool findPreviousChange(DocIterator & cur)
+bool findPreviousChange(BufferView * bv, Cursor & cur, bool const check_wrap)
 {
        for (cur.backwardPos(); cur; cur.backwardPos()) {
                if (cur.inTexted() && cur.paragraph().isChanged(cur.pos()))
                        return true;
        }
+
+       if (check_wrap) {
+               DocIterator cur_orig(bv->cursor());
+               docstring q = _("Beginning of file reached while searching backward.\n"
+                         "Continue searching from the end?");
+               int wrap_answer = frontend::Alert::prompt(_("Wrap search?"),
+                       q, 0, 1, _("&Yes"), _("&No"));
+               if (wrap_answer == 0) {
+                       bv->cursor().setCursor(doc_iterator_end(&bv->buffer()));
+                       bv->cursor().backwardPos();
+                       bv->clearSelection();
+                       cur.setCursor(bv->cursor().selectionBegin());
+                       if (findPreviousChange(bv, cur, false))
+                               return true;
+               }
+               bv->cursor().setCursor(cur_orig);
+       }
+
        return false;
 }
 
@@ -448,7 +513,7 @@ bool findChange(BufferView * bv, bool forward)
        Cursor cur(*bv);
        cur.setCursor(forward ? bv->cursor().selectionEnd()
                      : bv->cursor().selectionBegin());
-       forward ? findNextChange(cur) : findPreviousChange(cur);
+       forward ? findNextChange(bv, cur, true) : findPreviousChange(bv, cur, true);
        return selectChange(cur, forward);
 }
 
@@ -491,7 +556,7 @@ Escapes const & get_regexp_escapes()
                escape_map.push_back(P(".", "_x_."));
                escape_map.push_back(P("\\", "(?:\\\\|\\\\backslash)"));
                escape_map.push_back(P("~", "(?:\\\\textasciitilde|\\\\sim)"));
-               escape_map.push_back(P("^", "(?:\\^|\\\\textasciicircum\\{\\}|\\\\mathcircumflex)"));
+               escape_map.push_back(P("^", "(?:\\^|\\\\textasciicircum\\{\\}|\\\\textasciicircum|\\\\mathcircumflex)"));
                escape_map.push_back(P("_x_", "\\"));
        }
        return escape_map;
@@ -524,12 +589,12 @@ Escapes const & get_regexp_latex_escapes()
 
        static Escapes escape_map;
        if (escape_map.empty()) {
-               escape_map.push_back(P("\\\\", "(?:\\\\\\\\|\\\\backslash|\\\\textbackslash\\{\\})"));
+               escape_map.push_back(P("\\\\", "(?:\\\\\\\\|\\\\backslash|\\\\textbackslash\\{\\}|\\\\textbackslash)"));
                escape_map.push_back(P("(<?!\\\\\\\\textbackslash)\\{", "\\\\\\{"));
                escape_map.push_back(P("(<?!\\\\\\\\textbackslash\\\\\\{)\\}", "\\\\\\}"));
                escape_map.push_back(P("\\[", "\\{\\[\\}"));
                escape_map.push_back(P("\\]", "\\{\\]\\}"));
-               escape_map.push_back(P("\\^", "(?:\\^|\\\\textasciicircum\\{\\}|\\\\mathcircumflex)"));
+               escape_map.push_back(P("\\^", "(?:\\^|\\\\textasciicircum\\{\\}|\\\\textasciicircum|\\\\mathcircumflex)"));
                escape_map.push_back(P("%", "\\\\\\%"));
        }
        return escape_map;
@@ -607,7 +672,7 @@ string escape_for_regex(string s, bool match_latex)
 bool regex_replace(string const & s, string & t, string const & searchstr,
                   string const & replacestr)
 {
-       lyx::regex e(searchstr);
+       lyx::regex e(searchstr, regex_constants::ECMAScript);
        ostringstream oss;
        ostream_iterator<char, char> it(oss);
        lyx::regex_replace(it, s.begin(), s.end(), e, replacestr);
@@ -727,9 +792,8 @@ private:
 static docstring buffer_to_latex(Buffer & buffer)
 {
        OutputParams runparams(&buffer.params().encoding());
-       TexRow texrow(false);
        odocstringstream ods;
-       otexstream os(ods, texrow);
+       otexstream os(ods);
        runparams.nice = true;
        runparams.flavor = OutputParams::LATEX;
        runparams.linelen = 80; //lyxrc.plaintext_linelen;
@@ -777,11 +841,11 @@ static size_t identifyLeading(string const & s)
 {
        string t = s;
        // @TODO Support \item[text]
-       while (regex_replace(t, t, "^\\\\(emph|textbf|subsubsection|subsection|section|subparagraph|paragraph|part)\\*?\\{", "")
-              || regex_replace(t, t, "^\\$", "")
-              || regex_replace(t, t, "^\\\\\\[ ", "")
-              || regex_replace(t, t, "^\\\\item ", "")
-              || regex_replace(t, t, "^\\\\begin\\{[a-zA-Z_]*\\*?\\} ", ""))
+       while (regex_replace(t, t, REGEX_BOS "\\\\(emph|textbf|subsubsection|subsection|section|subparagraph|paragraph|part)\\*?\\{", "")
+              || regex_replace(t, t, REGEX_BOS "\\$", "")
+              || regex_replace(t, t, REGEX_BOS "\\\\\\[ ", "")
+              || regex_replace(t, t, REGEX_BOS "\\\\item ", "")
+              || regex_replace(t, t, REGEX_BOS "\\\\begin\\{[a-zA-Z_]*\\*?\\} ", ""))
                LYXERR(Debug::FIND, "  after removing leading $, \\[ , \\emph{, \\textbf{, etc.: '" << t << "'");
        return s.find(t);
 }
@@ -793,13 +857,13 @@ static int identifyClosing(string & t)
        int open_braces = 0;
        do {
                LYXERR(Debug::FIND, "identifyClosing(): t now is '" << t << "'");
-               if (regex_replace(t, t, "(.*[^\\\\])\\$\\'", "$1"))
+               if (regex_replace(t, t, "(.*[^\\\\])\\$" REGEX_EOS, "$1"))
                        continue;
-               if (regex_replace(t, t, "(.*[^\\\\]) \\\\\\]\\'", "$1"))
+               if (regex_replace(t, t, "(.*[^\\\\]) \\\\\\]" REGEX_EOS, "$1"))
                        continue;
-               if (regex_replace(t, t, "(.*[^\\\\]) \\\\end\\{[a-zA-Z_]*\\*?\\}\\'", "$1"))
+               if (regex_replace(t, t, "(.*[^\\\\]) \\\\end\\{[a-zA-Z_]*\\*?\\}" REGEX_EOS, "$1"))
                        continue;
-               if (regex_replace(t, t, "(.*[^\\\\])\\}\\'", "$1")) {
+               if (regex_replace(t, t, "(.*[^\\\\])\\}" REGEX_EOS, "$1")) {
                        ++open_braces;
                        continue;
                }
@@ -869,13 +933,14 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const &
                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
-               string regexp_str = string("\\`") + lead_as_regexp + par_as_string;
+               string regexp_str = lead_as_regexp + par_as_string;
                LYXERR(Debug::FIND, "Setting regexp to : '" << regexp_str << "'");
                regexp = lyx::regex(regexp_str);
 
                // If entered regexp may match wherever in searched string buffer
-               string regexp2_str = string("\\`.*") + lead_as_regexp + ".*" + par_as_string;
+               string regexp2_str = lead_as_regexp + ".*" + par_as_string;
                LYXERR(Debug::FIND, "Setting regexp2 to: '" << regexp2_str << "'");
                regexp2 = lyx::regex(regexp2_str);
        }
@@ -887,47 +952,63 @@ int MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_begin) con
        if (at_begin &&
                (opt.restr == FindAndReplaceOptions::R_ONLY_MATHS && !cur.inMathed()) )
                return 0;
+
        docstring docstr = stringifyFromForSearch(opt, cur, len);
-       LYXERR(Debug::FIND, "Matching against     '" << lyx::to_utf8(docstr) << "'");
        string str = normalize(docstr, true);
+       LYXERR(Debug::FIND, "Matching against     '" << lyx::to_utf8(docstr) << "'");
        LYXERR(Debug::FIND, "After normalization: '" << str << "'");
-       if (! use_regexp) {
-               LYXERR(Debug::FIND, "Searching in normal mode: par_as_string='" << par_as_string << "', str='" << str << "'");
-               LYXERR(Debug::FIND, "Searching in normal mode: lead_as_string='" << lead_as_string << "', par_as_string_nolead='" << par_as_string_nolead << "'");
+
+       if (use_regexp) {
+               LYXERR(Debug::FIND, "Searching in regexp mode: at_begin=" << at_begin);
+               regex const *p_regexp;
+               regex_constants::match_flag_type flags;
                if (at_begin) {
-                       LYXERR(Debug::FIND, "size=" << par_as_string.size() << ", substr='" << str.substr(0, par_as_string.size()) << "'");
-                       if (str.substr(0, par_as_string.size()) == par_as_string)
-                               return par_as_string.size();
+                       flags = regex_constants::match_continuous;
+                       p_regexp = &regexp;
                } else {
-                       size_t pos = str.find(par_as_string_nolead);
-                       if (pos != string::npos)
-                               return par_as_string.size();
+                       flags = regex_constants::match_default;
+                       p_regexp = &regexp2;
                }
+               sregex_iterator re_it(str.begin(), str.end(), *p_regexp, flags);
+               if (re_it == sregex_iterator())
+                       return 0;
+               match_results<string::const_iterator> const & m = *re_it;
+
+               // Check braces on the segment that matched the entire regexp expression,
+               // plus the last subexpression, if a (.*?) was inserted in the constructor.
+               if (!braces_match(m[0].first, m[0].second, open_braces))
+                       return 0;
+       
+               // Check braces on segments that matched all (.*?) subexpressions,
+               // except the last "padding" one inserted by lyx.
+               for (size_t i = 1; i < m.size() - 1; ++i)
+                       if (!braces_match(m[i].first, m[i].second))
+                               return false;
+       
+               // Exclude from the returned match length any length
+               // due to close wildcards added at end of regexp
+               if (close_wildcards == 0)
+                       return m[0].second - m[0].first;
+
+               return m[m.size() - close_wildcards].first - m[0].first;
+       }
+
+       // else !use_regexp: but all code paths above return
+       LYXERR(Debug::FIND, "Searching in normal mode: par_as_string='"
+                                << par_as_string << "', str='" << str << "'");
+       LYXERR(Debug::FIND, "Searching in normal mode: lead_as_string='"
+                                << lead_as_string << "', par_as_string_nolead='"
+                                << par_as_string_nolead << "'");
+
+       if (at_begin) {
+               LYXERR(Debug::FIND, "size=" << par_as_string.size()
+                                        << ", substr='" << str.substr(0, par_as_string.size()) << "'");
+               if (str.substr(0, par_as_string.size()) == par_as_string)
+                       return par_as_string.size();
        } else {
-               LYXERR(Debug::FIND, "Searching in regexp mode: at_begin=" << at_begin);
-               // Try all possible regexp matches,
-               //until one that verifies the braces match test is found
-               regex const *p_regexp = at_begin ? &regexp : &regexp2;
-               sregex_iterator re_it(str.begin(), str.end(), *p_regexp);
-               sregex_iterator re_it_end;
-               for (; re_it != re_it_end; ++re_it) {
-                       match_results<string::const_iterator> const & m = *re_it;
-                       // Check braces on the segment that matched the entire regexp expression,
-                       // plus the last subexpression, if a (.*?) was inserted in the constructor.
-                       if (!braces_match(m[0].first, m[0].second, open_braces))
-                               return 0;
-                       // Check braces on segments that matched all (.*?) subexpressions,
-                       // except the last "padding" one inserted by lyx.
-                       for (size_t i = 1; i < m.size() - 1; ++i)
-                               if (!braces_match(m[i].first, m[i].second))
-                                       return false;
-                       // Exclude from the returned match length any length
-                       // due to close wildcards added at end of regexp
-                       if (close_wildcards == 0)
-                               return m[0].second - m[0].first;
-                       else
-                               return m[m.size() - close_wildcards].first - m[0].first;
-               }
+               size_t pos = str.find(par_as_string_nolead);
+               if (pos != string::npos)
+                       return par_as_string.size();
        }
        return 0;
 }
@@ -1046,11 +1127,9 @@ docstring latexifyFromCursor(DocIterator const & cur, int len)
        LYXERR(Debug::FIND, "  with cur.lastpost=" << cur.lastpos() << ", cur.lastrow="
               << cur.lastrow() << ", cur.lastcol=" << cur.lastcol());
        Buffer const & buf = *cur.buffer();
-       LBUFERR(buf.params().isLatex());
 
-       TexRow texrow(false);
        odocstringstream ods;
-       otexstream os(ods, texrow);
+       otexstream os(ods);
        OutputParams runparams(&buf.params().encoding());
        runparams.nice = false;
        runparams.flavor = OutputParams::LATEX;
@@ -1346,7 +1425,9 @@ static void changeFirstCase(Buffer & buffer, TextCase first_case, TextCase other
 static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, MatchStringAdv & matchAdv)
 {
        Cursor & cur = bv->cursor();
-       if (opt.repl_buf_name == docstring())
+       if (opt.repl_buf_name == docstring()
+           || theBufferList().getBuffer(FileName(to_utf8(opt.repl_buf_name)), true) == 0
+           || theBufferList().getBuffer(FileName(to_utf8(opt.find_buf_name)), true) == 0)
                return;
 
        DocIterator sel_beg = cur.selectionBegin();
@@ -1395,9 +1476,8 @@ static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, M
                LYXERR(Debug::FIND, "After pasteParagraphList() cur=" << cur << endl);
                sel_len = repl_buffer.paragraphs().begin()->size();
        } else if (cur.inMathed()) {
-               TexRow texrow(false);
                odocstringstream ods;
-               otexstream os(ods, texrow);
+               otexstream os(ods);
                OutputParams runparams(&repl_buffer.params().encoding());
                runparams.nice = false;
                runparams.flavor = OutputParams::LATEX;
@@ -1408,10 +1488,8 @@ static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, M
                docstring repl_latex = ods.str();
                LYXERR(Debug::FIND, "Latexified replace_buffer: '" << repl_latex << "'");
                string s;
-               // false positive from coverity
-               // coverity[CHECKED_RETURN]
-               regex_replace(to_utf8(repl_latex), s, "\\$(.*)\\$", "$1");
-               regex_replace(s, s, "\\\\\\[(.*)\\\\\\]", "$1");
+               (void)regex_replace(to_utf8(repl_latex), s, "\\$(.*)\\$", "$1");
+               (void)regex_replace(s, s, "\\\\\\[(.*)\\\\\\]", "$1");
                repl_latex = from_utf8(s);
                LYXERR(Debug::FIND, "Replacing by insert()ing latex: '" << repl_latex << "' cur=" << cur << " with depth=" << cur.depth());
                MathData ar(cur.buffer());
@@ -1436,6 +1514,11 @@ bool findAdv(BufferView * bv, FindAndReplaceOptions const & opt)
        DocIterator cur;
        int match_len = 0;
 
+       // e.g., when invoking word-findadv from mini-buffer wither with
+       //       wrong options syntax or before ever opening advanced F&R pane
+       if (theBufferList().getBuffer(FileName(to_utf8(opt.find_buf_name)), true) == 0)
+               return false;
+
        try {
                MatchStringAdv matchAdv(bv->buffer(), opt);
                int length = bv->cursor().selectionEnd().pos() - bv->cursor().selectionBegin().pos();