X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Flyxfind.cpp;h=6e3b324bfa66be867f2bee4d4292bfd6b2ee3422;hb=3923c3abf4206c170876f509a08899b3b7c6c1f5;hp=e7771a8ff6320f958737af2121cf04d87581c229;hpb=047004cb0fca7247a6e13dde9f22c9a56ab245e6;p=lyx.git diff --git a/src/lyxfind.cpp b/src/lyxfind.cpp index e7771a8ff6..6e3b324bfa 100644 --- a/src/lyxfind.cpp +++ b/src/lyxfind.cpp @@ -14,6 +14,7 @@ */ #include +#include #include "lyxfind.h" @@ -56,13 +57,8 @@ //#define ResultsDebug #define USE_QT_FOR_SEARCH #if defined(USE_QT_FOR_SEARCH) - #include // sets QT_VERSION - #if (QT_VERSION >= 0x050000) - #include - #define QTSEARCH 1 - #else - #define QTSEARCH 0 - #endif + #include + #define QTSEARCH 1 #else #define QTSEARCH 0 #endif @@ -293,9 +289,11 @@ bool findOne(BufferView * bv, docstring const & searchstr, bool find_del, bool check_wrap, bool const auto_wrap, bool instant, bool onlysel) { + bool const had_selection = bv->cursor().selection(); + // Clean up previous selections with empty searchstr on instant if (searchstr.empty() && instant) { - if (bv->cursor().selection()) { + if (had_selection) { bv->setCursor(bv->cursor().selectionBegin()); bv->clearSelection(); } @@ -305,9 +303,10 @@ bool findOne(BufferView * bv, docstring const & searchstr, if (!searchAllowed(searchstr)) return false; - DocIterator const endcur = forward ? bv->cursor().selectionEnd() : bv->cursor().selectionBegin(); + DocIterator const startcur = bv->cursor().selectionBegin(); + DocIterator const endcur = bv->cursor().selectionEnd(); - if (onlysel && bv->cursor().selection()) { + if (onlysel && had_selection) { docstring const matchstring = bv->cursor().selectionAsString(false); docstring const lcmatchsting = support::lowercase(matchstring); if (matchstring == searchstr || (!case_sens && lcmatchsting == lowercase(searchstr))) { @@ -333,11 +332,11 @@ bool findOne(BufferView * bv, docstring const & searchstr, int match_len = forward ? findForward(cur, endcur, match, find_del, onlysel) - : findBackwards(cur, endcur, match, find_del, onlysel); + : findBackwards(cur, startcur, match, find_del, onlysel); if (match_len > 0) bv->putSelectionAt(cur, match_len, !forward); - else if (onlysel) { + else if (onlysel && had_selection) { docstring q = _("The search string was not found within the selection.\n" "Continue search outside?"); int search_answer = frontend::Alert::prompt(_("Search outside selection?"), @@ -351,7 +350,6 @@ bool findOne(BufferView * bv, docstring const & searchstr, return false; } else if (check_wrap) { - DocIterator cur_orig(bv->cursor()); bool wrap = auto_wrap; if (!auto_wrap) { docstring q; @@ -384,7 +382,13 @@ bool findOne(BufferView * bv, docstring const & searchstr, find_del, false, false, false, false)) return true; } - bv->cursor().setCursor(cur_orig); + bv->setCursor(startcur); + + // restore original selection + if (had_selection) { + bv->cursor().resetAnchor(); + bv->setSelection(startcur, endcur); + } return false; } @@ -460,7 +464,7 @@ int replaceAll(BufferView * bv, if (had_selection) { endcur.fixIfBroken(); bv->cursor().resetAnchor(); - bv->setCursorSelectionTo(endcur); + bv->setSelection(startcur, endcur); } return num; @@ -835,13 +839,47 @@ string string2regex(string in) // normal blanks blanks++; } - else if ((tempx[i] == '\302' && tempx[i+1] == '\240') - || (tempx[i] == '\342' && tempx[i+1] == '\200')) { - // protected space - // thin space + else if (tempx[i] == '\302' && tempx[i+1] == '\240') { + // Normal Space blanks++; i++; } + else if (tempx[i] == '\342') { + if (tempx[i+1] == '\200') { + if ((tempx[i+2] == '\257') + || (tempx[i+2] == '\203') + || (tempx[i+2] == '\202')) { + // Non-breaking Thin (1/6 em) + // Quad(1 em), (Double quad counts as 2 blanks) + // Half Quad + blanks++; + i += 2; + } + else if (tempx[i+2] == '\213') { + // Ignoring parts of Medium and Thick + i += 2; + continue; + } + else if ((tempx[i+2] == '\204') || (tempx[i+2] == '\205')) { + // Thick + // Medium + blanks++; + i += 2; + } + } + else if (tempx[i+1] == '\201') { + if (tempx[i+2] == '\240') { + // Ignoring parts of half quad + i += 2; + continue; + } + } + else if ((tempx[i+1] == '\220') && (tempx[i+2] == '\243')) { + // Visible space + blanks++; + i += 2; + } + } else { if (blanks > 0) { temp += getRegexSpaceCount(blanks); @@ -901,7 +939,7 @@ string correctRegex(string t, bool withformat) } else { if (sub.str(4) == "backslash") { - replace = "\\"; + replace = string("\\"); { // transforms '\backslash \{' into '\{' string next = t.substr(sub.position(2) + sub.str(2).length(), 2); @@ -917,6 +955,9 @@ string correctRegex(string t, bool withformat) } else if (sub.str(4) == "mathcircumflex") replace = "^"; + else if ((sub.str(4) == "negthinspace") || (sub.str(4) == "negmedspace") || (sub.str(4) == "negthickspace")) { + replace = accents[sub.str(4)+"{}"]; + } else if (backslashed) { backslashed = false; if (withformat) { @@ -1066,13 +1107,19 @@ public: ** constructor as opt.search, under the opt.* options settings. ** ** @param at_begin - ** If set, then match is searched only against beginning of text starting at cur. - ** If unset, then match is searched anywhere in text starting at cur. + ** If set to MatchStringAdv::MatchFromStart, + ** then match is searched only against beginning of text starting at cur. + ** Otherwise the match is searched anywhere in text starting at cur. ** ** @return ** The length of the matching text, or zero if no match was found. **/ - MatchResult operator()(DocIterator const & cur, int len = -1, bool at_begin = true) const; + enum matchType { + MatchAnyPlace, + MatchFromStart + }; + string matchTypeAsString(matchType const x) const { return (x == MatchFromStart ? "MatchFromStart" : "MatchAnyPlace"); } + MatchResult operator()(DocIterator const & cur, int len, matchType at_begin) const; #if QTSEARCH bool regexIsValid; string regexError; @@ -1088,7 +1135,7 @@ public: private: /// Auxiliary find method (does not account for opt.matchword) - MatchResult findAux(DocIterator const & cur, int len = -1, bool at_begin = true) const; + MatchResult findAux(DocIterator const & cur, int len, matchType at_begin) const; void CreateRegexp(FindAndReplaceOptions const & opt, string regexp_str, string regexp2_str, string par_as_string = ""); /** Normalize a stringified or latexified LyX paragraph. @@ -1104,7 +1151,7 @@ private: ** @todo Normalization should also expand macros, if the corresponding ** search option was checked. **/ - string normalize(docstring const & s, bool ignore_fomat) const; + string convertLF2Space(docstring const & s, bool ignore_fomat) const; // normalized string to search string par_as_string; // regular expression to use for searching @@ -1121,10 +1168,10 @@ private: // par_as_string after removal of lead_as_string string par_as_string_nolead; // unmatched open braces in the search string/regexp - int open_braces; + int open_braces = 0; // number of (.*?) subexpressions added at end of search regexp for closing // environments, math mode, styles, etc... - int close_wildcards; + int close_wildcards = 0; public: // Are we searching with regular expressions ? bool use_regexp = false; @@ -1428,8 +1475,8 @@ public: string par; int ignoreidx; static vector borders; - int depts[MAXOPENED]; - int closes[MAXOPENED]; + static vector depts; + static vector closes; int actualdeptindex; int previousNotIgnored(int) const; int nextNotIgnored(int) const; @@ -1454,6 +1501,8 @@ public: }; vector Intervall::borders = vector(30); +vector Intervall::depts = vector(30); +vector Intervall::closes = vector(30); int Intervall::isOpeningPar(int pos) const { @@ -1487,6 +1536,8 @@ void Intervall::setForDefaultLang(KeyInfo const & defLang) const } } +#if 0 +// Not needed, because dpts and closes are now dynamically expanded static void checkDepthIndex(int val) { static int maxdepthidx = MAXOPENED-2; @@ -1500,6 +1551,7 @@ static void checkDepthIndex(int val) LYXERR(Debug::INFO, "maxdepthidx now " << val); } } +#endif #if 0 // Not needed, because borders are now dynamically expanded @@ -1955,11 +2007,11 @@ static void buildAccentsMap() 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 + accents["negthinspace{}"]= getutf8(0xf0003); // to omit backslashed latex macros accents["medspace"] = getutf8(0xf0004); // See https://en.wikipedia.org/wiki/Private_Use_Areas - accents["negmedspace"] = getutf8(0xf0005); + accents["negmedspace{}"] = getutf8(0xf0005); accents["thickspace"] = getutf8(0xf0006); - accents["negthickspace"] = getutf8(0xf0007); + accents["negthickspace{}"]= getutf8(0xf0007); accents["lyx"] = getutf8(0xf0010); // Used logos accents["LyX"] = getutf8(0xf0010); accents["tex"] = getutf8(0xf0011); @@ -2049,7 +2101,8 @@ void Intervall::removeAccents() if (accents.empty()) buildAccentsMap(); static regex const accre("\\\\(" - "([\\S]|[A-Za-z]+)\\{[^\\{\\}]+\\}" + "([\\S]|[A-Za-z]+)\\{[^\\\\\\{\\}]*\\}" + "|([\\S]|[A-Za-z]+)\\{\\\\[ij](math)?\\}" "|(" "(backslash ([lL]y[xX]|[tT]e[xX]|[lL]a[tT]e[xX]e?|lyxarrow))" "|[A-Za-z]+" @@ -2085,9 +2138,13 @@ void Intervall::removeAccents() void Intervall::handleOpenP(int i) { actualdeptindex++; + if ((size_t) actualdeptindex >= depts.size()) { + depts.resize(actualdeptindex + 30); + closes.resize(actualdeptindex + 30); + } depts[actualdeptindex] = i+1; closes[actualdeptindex] = -1; - checkDepthIndex(actualdeptindex); + // checkDepthIndex(actualdeptindex); } void Intervall::handleCloseP(int i, bool closingAllowed) @@ -2407,7 +2464,7 @@ void LatexInfo::buildEntries(bool isPatternString) } // Ignore language if there is math somewhere in pattern-string if (isPatternString) { - for (auto s: usedText) { + for (auto const & s: usedText) { // Remove entries created in previous search runs keys.erase(s); } @@ -2763,7 +2820,8 @@ void LatexInfo::buildKeys(bool isPatternString) if (keysBuilt && !isPatternString) return; // Keys to ignore in any case - makeKey("text|textcyrillic|lyxmathsym|ensuremath", KeyInfo(KeyInfo::headRemove, 1, true), true); + makeKey("text|lyxmathsym|ensuremath", KeyInfo(KeyInfo::headRemove, 1, true), true); + makeKey("nonumber|notag", KeyInfo(KeyInfo::headRemove, 0, true), true); // Known standard keys with 1 parameter. // Split is done, if not at start of region makeKey("textsf|textss|texttt", KeyInfo(KeyInfo::isStandard, 1, ignoreFormats.getFamily()), isPatternString); @@ -3641,7 +3699,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt) CreateRegexp(opt, "", "", ""); return; } - use_regexp = lyx::to_utf8(ds).find("\\regexp{") != std::string::npos; + use_regexp = ds.find(from_utf8("\\regexp{")) != std::string::npos; if (opt.replace_all && previous_single_replace) { previous_single_replace = false; num_replaced = 0; @@ -3651,9 +3709,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt) previous_single_replace = true; } // When using regexp, braces are hacked already by escape_for_regex() - par_as_string = normalize(ds, opt.ignoreformat); - open_braces = 0; - close_wildcards = 0; + par_as_string = convertLF2Space(ds, opt.ignoreformat); size_t lead_size = 0; // correct the language settings @@ -3730,7 +3786,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt) break; } if (lng < par_as_string.size()) - par_as_string = par_as_string.substr(0,lng); + par_as_string.resize(lng); } LYXERR(Debug::FINDVERBOSE, "par_as_string after correctRegex is '" << par_as_string << "'"); if ((lng > 0) && (par_as_string[0] == '^')) { @@ -3757,7 +3813,7 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt) } if (opt.matchword) { modifyRegexForMatchWord(par_as_string); - opt.matchword = false; + // opt.matchword = false; } regexp_str = "(" + lead_as_regexp + ")()" + par_as_string; regexp2_str = "(" + lead_as_regexp + ")(.*?)" + par_as_string; @@ -3768,18 +3824,15 @@ MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions & opt) } } -MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_begin) const +MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, MatchStringAdv::matchType at_begin) const { MatchResult mres; mres.searched_size = len; - if (at_begin && - (opt.restr == FindAndReplaceOptions::R_ONLY_MATHS && !cur.inMathed()) ) - return mres; docstring docstr = stringifyFromForSearch(opt, cur, len); string str; - str = normalize(docstr, opt.ignoreformat); + str = convertLF2Space(docstr, opt.ignoreformat); if (!opt.ignoreformat) { str = correctlanguagesetting(str, false, !opt.ignoreformat); // remove closing '}' and '\n' to allow for use of '$' in regex @@ -3802,12 +3855,12 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be LASSERT(use_regexp, /**/); { // use_regexp always true - LYXERR(Debug::FINDVERBOSE, "Searching in regexp mode: at_begin=" << at_begin); + LYXERR(Debug::FINDVERBOSE, "Searching in regexp mode: at_begin=" << matchTypeAsString(at_begin)); #if QTSEARCH QString qstr = QString::fromStdString(str); QRegularExpression const *p_regexp; QRegularExpression::MatchType flags = QRegularExpression::NormalMatch; - if (at_begin) { + if (at_begin == MatchStringAdv::MatchFromStart) { p_regexp = ®exp; } else { p_regexp = ®exp2; @@ -3818,7 +3871,7 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be #else regex const *p_regexp; regex_constants::match_flag_type flags; - if (at_begin) { + if (at_begin == MatchStringAdv::MatchFromStart) { flags = regex_constants::match_continuous; p_regexp = ®exp; } else { @@ -3932,23 +3985,20 @@ MatchResult MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_be } -MatchResult MatchStringAdv::operator()(DocIterator const & cur, int len, bool at_begin) const +MatchResult MatchStringAdv::operator()(DocIterator const & cur, int len, MatchStringAdv::matchType at_begin) const { MatchResult mres = findAux(cur, len, at_begin); - int res = mres.match_len; LYXERR(Debug::FINDVERBOSE, - "res=" << res << ", at_begin=" << at_begin + "res=" << mres.match_len << ", at_begin=" << matchTypeAsString(at_begin) << ", matchAtStart=" << opt.matchAtStart << ", inTexted=" << cur.inTexted()); - if (opt.matchAtStart) { - if (cur.pos() != 0) - mres.match_len = 0; - else if (mres.match_prefix > 0) - mres.match_len = 0; - return mres; + if (mres.match_len > 0) { + if (opt.matchAtStart) { + if (cur.pos() > 0 || mres.match_prefix > 0) + mres.match_len = 0; + } } - else - return mres; + return mres; } #if 0 @@ -3976,8 +4026,7 @@ static bool simple_replace(string &t, string from, string to) } #endif -#if 1 -static string convertLF2Space(docstring const &s, bool ignore_format) +string MatchStringAdv::convertLF2Space(docstring const &s, bool ignore_format) const { // Using original docstring to handle '\n' @@ -4010,7 +4059,7 @@ static string convertLF2Space(docstring const &s, bool ignore_format) if ((pos > start + 2) && (s[pos+1] == '~' || isSpace(s[pos+1]) || s[pos-3] == '~' || isSpace(s[pos-3]))) { - // discard '\n' + // discard "\\\\\n", do not replace with space dospace = false; } } @@ -4044,77 +4093,6 @@ static string convertLF2Space(docstring const &s, bool ignore_format) return(t.str()); } -#else -static string convertLF2Space(docstring const & s, bool ignore_format) -{ - // Using utf8-converted string to handle '\n' - - string t; - t = lyx::to_utf8(s); - // Remove \n at begin - while (!t.empty() && t[0] == '\n') - t = t.substr(1); - // Remove \n* at end - while (!t.empty() && t[t.size() - 1] == '\n') { - t = t.substr(0, t.size() - 1); - } - size_t pos; - // Handle all other '\n' - while ((pos = t.find("\n")) != string::npos) { - if (pos > 1 && t[pos-1] == '\\' && t[pos-2] == '\\' ) { - // Handle '\\\n' - if (isPrintableNonspace(t[pos+1]) && ((pos < 3) || isPrintableNonspace(t[pos-3]))) { - t.replace(pos-2, 3, " "); - } - else { - // Already a space there - t.replace(pos-2, 3, ""); - } - } - else { - if (!isAlnumASCII(t[pos+1]) || !isAlnumASCII(t[pos-1])) { - // '\n' adjacent to non-alpha-numerics, discard - t.replace(pos, 1, ""); - } - else { - // Replace all other \n with spaces - t.replace(pos, 1, " "); - } - if (!ignore_format) { - size_t count = 0; - while ((pos > count + 1) && (t[pos - 1 -count] == '%')) { - count++; - } - if (count > 0) { - t.replace(pos - count, count, ""); - } - } - } - } - return(t); - -} -#endif - -string MatchStringAdv::normalize(docstring const & s, bool ignore_format) const -{ - string t = convertLF2Space(s, ignore_format); - - // The following replaces are not appropriate in non-format-search mode - if (!ignore_format) { - // Remove stale empty \emph{}, \textbf{} and similar blocks from latexify - // Kornel: Added textsl, textsf, textit, texttt and noun - // + allow to seach for colored text too - LYXERR(Debug::FINDVERBOSE, "Removing stale empty macros from: " << t); - while (regex_replace(t, t, "\\\\(emph|noun|text(bf|sl|sf|it|tt)|(u|uu)line|(s|x)out|uwave)(\\{(\\{\\})?\\})+", "")) - LYXERR(Debug::FINDVERBOSE, " further removing stale empty \\emph{}, \\textbf{} macros from: " << t); - while (regex_replace(t, t, "\\\\((sub)?(((sub)?section)|paragraph)|part)\\*?(\\{(\\{\\})?\\})+", "")) - LYXERR(Debug::FINDVERBOSE, " further removing stale empty \\section{}, \\part{}, \\paragraph{} macros from: " << t); - while (regex_replace(t, t, "\\\\(foreignlanguage|textcolor|item)\\{[a-z]+\\}(\\{(\\{\\})?\\})+", "")); - } - return t; -} - docstring stringifyFromCursor(DocIterator const & cur, int len) { LYXERR(Debug::FINDVERBOSE, "Stringifying with len=" << len << " from cursor at pos: " << cur); @@ -4295,13 +4273,13 @@ MatchResult findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, Mat // 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 = (expected.match_prefix == 0); + MatchStringAdv::matchType at_begin = (expected.match_prefix == 0) ? MatchStringAdv::MatchFromStart : MatchStringAdv::MatchAnyPlace; if (!match.opt.forward && match.opt.ignoreformat) { if (expected.pos > 0) return fail; } - LASSERT(at_begin, /**/); - if (expected.match_len > 0 && at_begin) { + LASSERT(at_begin == MatchStringAdv::MatchFromStart, /**/); + if (expected.match_len > 0 && at_begin == MatchStringAdv::MatchFromStart) { // Search for deepest match old_cur = cur; max_match = expected; @@ -4333,11 +4311,14 @@ MatchResult findAdvFinalize(DocIterator & cur, MatchStringAdv const & match, Mat } else { // (expected.match_len <= 0) - mres = match(cur); /* match valid only if not searching whole words */ + mres = match(cur, -1, MatchStringAdv::MatchFromStart); /* match valid only if not searching whole words */ displayMres(mres, "Start with negative match", cur); max_match = mres; } - if (max_match.match_len <= 0) return fail; + // Only now we are really at_begin + if ((max_match.match_len <= 0) || + (match.opt.restr == FindAndReplaceOptions::R_ONLY_MATHS && !cur.inMathed())) + return fail; LYXERR(Debug::FINDVERBOSE, "Ok"); // Compute the match length @@ -4429,15 +4410,20 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) { if (!cur) return 0; - bool repeat = false; + int repeat = 0; DocIterator orig_cur; // to be used if repeat not successful MatchResult orig_mres; + do { + orig_cur = cur; + cur.forwardPos(); + } while (cur.depth() > orig_cur.depth()); + cur = orig_cur; while (!theApp()->longOperationCancelled() && cur) { //(void) findAdvForwardInnermost(cur); LYXERR(Debug::FINDVERBOSE, "findForwardAdv() cur: " << cur); - MatchResult mres = match(cur, -1, false); + MatchResult mres = match(cur, -1, MatchStringAdv::MatchAnyPlace); string msg = "Starting"; - if (repeat) + if (repeat > 0) msg = "Repeated"; displayMres(mres, msg + " findForwardAdv", cur) int match_len = mres.match_len; @@ -4446,8 +4432,13 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) match_len = 0; } if (match_len <= 0) { - // This should exit nested insets, if any, or otherwise undefine the currsor. - cur.pos() = cur.lastpos(); + if (repeat > 0) { + repeat--; + } + else { + // This should exit nested insets, if any, or otherwise undefine the currsor. + cur.pos() = cur.lastpos(); + } LYXERR(Debug::FINDVERBOSE, "Advancing pos: cur=" << cur); cur.forwardPos(); } @@ -4473,9 +4464,9 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) continue; } cur.pos() = cur.pos() + increment; - MatchResult mres2 = match(cur, -1, false); + MatchResult mres2 = match(cur, -1, MatchStringAdv::MatchAnyPlace); displayMres(mres2, "findForwardAdv loop", cur) - switch (interpretMatch(mres, mres2)) { + switch (interpretMatch(mres, mres2)) { case MatchResult::newIsTooFar: // behind the expected match firstInvalid = increment; @@ -4483,7 +4474,7 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) increment /= 2; break; case MatchResult::newIsBetter: - // not reached yet, but cur.pos()+increment is bettert + // not reached yet, but cur.pos()+increment is better mres = mres2; firstInvalid -= increment; if (increment > firstInvalid*3/4) @@ -4496,7 +4487,7 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) default: // Todo@ // Handle not like MatchResult::newIsTooFar - LYXERR0( "Probably too far: Increment = " << increment << " match_prefix = " << mres.match_prefix); + LYXERR(Debug::FINDVERBOSE, "Probably too far: Increment = " << increment << " match_prefix = " << mres.match_prefix); firstInvalid--; increment = increment*3/4; cur = old_cur; @@ -4506,14 +4497,14 @@ int findForwardAdv(DocIterator & cur, MatchStringAdv & match) if (mres.match_len > 0) { if (mres.match_prefix + mres.pos - mres.leadsize > 0) { // The match seems to indicate some deeper level - repeat = true; + repeat = 2; orig_cur = cur; orig_mres = mres; cur.forwardPos(); continue; } } - else if (repeat) { + else if (repeat > 0) { // should never be reached. cur = orig_cur; mres = orig_mres; @@ -4549,7 +4540,8 @@ MatchResult findMostBackwards(DocIterator & cur, MatchStringAdv const & match, M LYXERR(Debug::FINDVERBOSE, "findMostBackwards(): cur=" << cur); DocIterator new_cur = cur; new_cur.backwardPos(); - if (new_cur == cur || &new_cur.inset() != &inset || !match(new_cur).match_len) + if (new_cur == cur || &new_cur.inset() != &inset + || match(new_cur, -1, MatchStringAdv::MatchFromStart).match_len <= 0) break; MatchResult new_mr = findAdvFinalize(new_cur, match, expected); if (new_mr.match_len == mr.match_len) @@ -4575,7 +4567,7 @@ int findBackwardsAdv(DocIterator & cur, MatchStringAdv & match) bool pit_changed = false; do { cur.pos() = 0; - MatchResult found_match = match(cur, -1, false); + MatchResult found_match = match(cur, -1, MatchStringAdv::MatchAnyPlace); if (found_match.match_len > 0) { if (pit_changed) @@ -4585,7 +4577,7 @@ int findBackwardsAdv(DocIterator & cur, MatchStringAdv & match) LYXERR(Debug::FINDVERBOSE, "findBackAdv2: cur: " << cur); DocIterator cur_prev_iter; do { - found_match = match(cur); + found_match = match(cur, -1, MatchStringAdv::MatchFromStart); LYXERR(Debug::FINDVERBOSE, "findBackAdv3: found_match=" << (found_match.match_len > 0) << ", cur: " << cur); if (found_match.match_len > 0) { @@ -4747,7 +4739,7 @@ static int findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, Ma return 0; LASSERT(sel_len > 0, return 0); - if (!matchAdv(sel_beg, sel_len).match_len) + if (matchAdv(sel_beg, sel_len, MatchStringAdv::MatchFromStart).match_len <= 0) return 0; // Build a copy of the replace buffer, adapted to the KeepCase option @@ -4815,6 +4807,10 @@ static int findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, Ma return 1; } +static bool isWordChar(char_type c) +{ + return isLetterChar(c) || isNumberChar(c); +} /// Perform a FindAdv operation. bool findAdv(BufferView * bv, FindAndReplaceOptions & opt) @@ -4840,8 +4836,52 @@ bool findAdv(BufferView * bv, FindAndReplaceOptions & opt) bv->putSelectionAt(bv->cursor().selectionBegin(), length, !opt.forward); num_replaced += findAdvReplace(bv, opt, matchAdv); cur = bv->cursor(); - if (opt.forward) + if (opt.forward) { + if (opt.matchword && cur.pos() > 0) { // Skip word-characters if we are in the mid of a word + if (cur.inTexted()) { + Paragraph const & par = cur.paragraph(); + int len_limit, new_pos; + if (cur.lastpos() < par.size()) + len_limit = cur.lastpos(); + else + len_limit = par.size(); + for (new_pos = cur.pos() - 1; new_pos < len_limit; new_pos++) { + if (!isWordChar(par.getChar(new_pos))) + break; + } + if (new_pos > cur.pos()) + cur.pos() = new_pos; + } + else if (cur.inMathed()) { + // Check if 'cur.pos()-1' and 'cur.pos()' both point to a letter, + // I am not sure, we should consider the selection + bool sel = bv->cursor().selection(); + if (!sel && cur.pos() < cur.lastpos()) { + CursorSlice const & cs = cur.top(); + MathData md = cs.cell(); + int len = -1; + MathData::const_iterator it_end = md.end(); + MathData md2; + // Start the check with one character before actual cursor position + for (MathData::const_iterator it = md.begin() + cs.pos() - 1; + it != it_end; ++it) + md2.push_back(*it); + docstring inp = asString(md2); + for (len = 0; (unsigned) len < inp.size() && len + cur.pos() <= cur.lastpos(); len++) { + if (!isWordChar(inp[len])) + break; + } + // len == 0 means previous char was a word separator + // len == 1 search starts with a word separator + // len == 2 ... we have to skip len -1 chars + if (len > 1) + cur.pos() = cur.pos() + len - 1; + } + } + opt.matchword = false; + } pos_len = findForwardAdv(cur, matchAdv); + } else pos_len = findBackwardsAdv(cur, matchAdv); } catch (exception & ex) {