X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Flyxfind.cpp;h=0ec9fcb7a039208226b4b3d0858dbd4ab1d48666;hb=28be7d552f62cc02fa86d7f79201d089bfb2d7b5;hp=cc8d1cf12ff07abb9c6b74cf476a117862e6ecc5;hpb=670efa8f646218f2a378f0cc614c4c37a9f6b89a;p=lyx.git diff --git a/src/lyxfind.cpp b/src/lyxfind.cpp index cc8d1cf12f..0ec9fcb7a0 100644 --- a/src/lyxfind.cpp +++ b/src/lyxfind.cpp @@ -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 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 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 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("( it(oss); lyx::regex_replace(it, s.begin(), s.end(), e, replacestr); @@ -728,7 +793,7 @@ static docstring buffer_to_latex(Buffer & buffer) { OutputParams runparams(&buffer.params().encoding()); odocstringstream ods; - otexstream os(ods, false); + otexstream os(ods); runparams.nice = true; runparams.flavor = OutputParams::LATEX; runparams.linelen = 80; //lyxrc.plaintext_linelen; @@ -776,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); } @@ -792,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; } @@ -868,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); } @@ -886,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 = ®exp; } 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 = ®exp2; } + sregex_iterator re_it(str.begin(), str.end(), *p_regexp, flags); + if (re_it == sregex_iterator()) + return 0; + match_results 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 ? ®exp : ®exp2; - 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 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; } @@ -1045,10 +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()); odocstringstream ods; - otexstream os(ods, false); + otexstream os(ods); OutputParams runparams(&buf.params().encoding()); runparams.nice = false; runparams.flavor = OutputParams::LATEX; @@ -1344,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(); @@ -1394,7 +1477,7 @@ static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, M sel_len = repl_buffer.paragraphs().begin()->size(); } else if (cur.inMathed()) { odocstringstream ods; - otexstream os(ods, false); + otexstream os(ods); OutputParams runparams(&repl_buffer.params().encoding()); runparams.nice = false; runparams.flavor = OutputParams::LATEX; @@ -1405,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()); @@ -1433,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();