// @TODO Support \item[text]
// Kornel: Added textsl, textsf, textit, texttt and noun
// + allow to search for colored text too
- while (regex_replace(t, t, REGEX_BOS "\\\\(((emph|noun|minisec|text(bf|md|sl|sf|it|tt))|((textcolor|foreignlanguage)\\{[a-z]+\\})|(u|uu)line|(s|x)out|uwave)|((sub)?(((sub)?section)|paragraph)|part|chapter)\\*?)\\{", "")
+ while (regex_replace(t, t, REGEX_BOS "\\\\(((footnotesize|tiny|scriptsize|small|large|Large|LARGE|huge|Huge|emph|noun|minisec|text(bf|md|sl|sf|it|tt))|((textcolor|foreignlanguage)\\{[a-z]+\\})|(u|uu)line|(s|x)out|uwave)|((sub)?(((sub)?section)|paragraph)|part|chapter)\\*?)\\{", "")
|| regex_replace(t, t, REGEX_BOS "\\$", "")
|| regex_replace(t, t, REGEX_BOS "\\\\\\[ ", "")
|| regex_replace(t, t, REGEX_BOS " ?\\\\item\\{[a-z]+\\}", "")
static Features identifyFeatures(string const & s)
{
static regex const feature("\\\\(([a-z]+(\\{([a-z]+)\\}|\\*)?))\\{");
- static regex const valid("^(((emph|noun|text(bf|md|sl|sf|it|tt)|(textcolor|foreignlanguage|item)\\{[a-z]+\\})|(u|uu)line|(s|x)out|uwave)|((sub)?(((sub)?section)|paragraph)|part|chapter)\\*?)$");
+ static regex const valid("^(((footnotesize|tiny|scriptsize|small|large|Large|LARGE|huge|Huge|emph|noun|text(bf|md|sl|sf|it|tt)|(textcolor|foreignlanguage|item)\\{[a-z]+\\})|(u|uu)line|(s|x)out|uwave)|((sub)?(((sub)?section)|paragraph)|part|chapter)\\*?)$");
smatch sub;
bool displ = true;
Features info;
class KeyInfo {
public:
enum KeyType {
+ /* Char type with content discarded
+ * like \hspace{1cm} */
+ noContent,
+ /* Char, like \backslash */
isChar,
+ /* \part, \section*, ... */
isSectioning,
- isMain, /* for \\foreignlanguage */
+ /* \foreignlanguage{ngerman}, ... */
+ isMain,
+ /* inside \code{} or \footnote{}
+ * to discard language in content */
+ noMain,
isRegex,
+ /* \begin{eqnarray}...\end{eqnarray}, ... $...$ */
isMath,
+ /* fonts, colors, markups, ... */
isStandard,
+ /* footnotesize, ... large, ...
+ * Ignore all of them */
isSize,
invalid,
+ /* inputencoding, shortcut, ...
+ * Discard also content, because they do not help in search */
doRemove,
+ /* item */
isList,
- isIgnored /* to be ignored by creating infos */
+ /* tex, latex, ... like isChar */
+ isIgnored
};
KeyInfo()
: keytype(invalid),
head(""),
+ _tokensize(-1),
+ _tokenstart(-1),
+ _dataStart(-1),
+ _dataEnd(-1),
parenthesiscount(1),
disabled(false),
used(false)
{};
KeyInfo(KeyType type, int parcount, bool disable)
: keytype(type),
+ _tokensize(-1),
+ _tokenstart(-1),
+ _dataStart(-1),
+ _dataEnd(-1),
parenthesiscount(parcount),
disabled(disable),
used(false) {};
class Intervall {
bool isPatternString;
public:
- Intervall(bool isPattern) : isPatternString(isPattern), ignoreidx(-1), actualdeptindex(0) { depts[0] = 0;};
+ explicit Intervall(bool isPattern) :
+ isPatternString(isPattern),
+ ignoreidx(-1),
+ actualdeptindex(0) { depts[0] = 0; closes[0] = 0;};
string par;
int ignoreidx;
int depts[MAXOPENED];
void removeHead(KeyInfo&, int count=0);
public:
- LatexInfo(string par, bool isPatternString) : interval(isPatternString) {
+ LatexInfo(string par, bool isPatternString) : entidx(-1), interval(isPatternString) {
interval.par = par;
buildKeys(isPatternString);
entries = vector<KeyInfo>();
return entries[keyinfo];
};
void setForDefaultLang(int upTo) {interval.setForDefaultLang(upTo);};
+ void addIntervall(int low, int up) { interval.addIntervall(low, up); };
};
m.mathSize = end - start;
entries.push_back(m);
}
- bool evaluating(size_t pos) {
- while (actualIdx < entries.size()) {
- if (pos < entries[actualIdx].mathStart)
- return false;
- if (pos < entries[actualIdx].mathEnd)
- return true;
- actualIdx++;
- }
- return false;
- }
bool empty() { return entries.empty(); };
size_t getEndPos() {
if (entries.empty() || (actualIdx >= entries.size())) {
bool evaluatingRegexp = false;
MathInfo mi;
bool evaluatingMath = false;
+ bool evaluatingCode = false;
+ size_t codeEnd = 0;
+ int codeStart = -1;
KeyInfo found;
bool math_end_waiting = false;
size_t math_pos = 10000;
if (key == "") {
if (sub.str(0)[0] == '\\')
key = sub.str(0)[1];
- else
+ else {
key = sub.str(0);
+ if (key == "$") {
+ size_t k_pos = sub.position(size_t(0));
+ if ((k_pos > 0) && (interval.par[k_pos - 1] == '\\')) {
+ // Escaped '$', ignoring
+ continue;
+ }
+ }
+ }
};
if (evaluatingRegexp) {
if (sub.str(1).compare("endregexp") == 0) {
found._tokenstart = pos - count;
if (sub.str(1).compare(0, 5, "begin") == 0) {
size_t pos1 = pos + sub.str(0).length();
- if (interval.par[pos1] == '[') {
- pos1 = interval.findclosing(pos1+1, interval.par.length(), '[', ']')+1;
- }
- if (interval.par[pos1] == '{') {
- found._dataEnd = interval.findclosing(pos1+1, interval.par.length()) + 1;
+ if (sub.str(5).compare("cjk") == 0) {
+ pos1 = interval.findclosing(pos1+1, interval.par.length()) + 1;
+ if ((interval.par[pos1] == '{') && (interval.par[pos1+1] == '}'))
+ pos1 += 2;
+ found.keytype = KeyInfo::isMain;
+ found._dataStart = pos1;
+ found._dataEnd = interval.par.length();
+ found.disabled = keys["foreignlanguage"].disabled;
+ found.used = keys["foreignlanguage"].used;
+ found._tokensize = pos1 - found._tokenstart;
+ found.head = interval.par.substr(found._tokenstart, found._tokensize);
}
else {
- found._dataEnd = pos1;
+ if (interval.par[pos1] == '[') {
+ pos1 = interval.findclosing(pos1+1, interval.par.length(), '[', ']')+1;
+ }
+ if (interval.par[pos1] == '{') {
+ found._dataEnd = interval.findclosing(pos1+1, interval.par.length()) + 1;
+ }
+ else {
+ found._dataEnd = pos1;
+ }
+ found._dataStart = found._dataEnd;
+ found._tokensize = count + found._dataEnd - pos;
+ found.parenthesiscount = 0;
+ found.disabled = true;
}
- found._dataStart = found._dataEnd;
}
else {
+ // Handle "\end{...}"
found._dataStart = pos + sub.str(0).length();
found._dataEnd = found._dataStart;
+ found._tokensize = count + found._dataEnd - pos;
+ found.parenthesiscount = 0;
+ found.disabled = true;
}
- found._tokensize = count + found._dataEnd - pos;
- found.parenthesiscount = 0;
- found.disabled = true;
}
}
else if (found.keytype != KeyInfo::isRegex) {
found._tokenstart = sub.position(size_t(0));
if (found.parenthesiscount == 0) {
// Probably to be discarded
- char following = interval.par[sub.position(size_t(0)) + sub.str(3).length() + 1];
+ size_t following_pos = sub.position(size_t(0)) + sub.str(3).length() + 1;
+ char following = interval.par[following_pos];
if (following == ' ')
found.head = "\\" + sub.str(3) + " ";
else if (following == '=') {
found._tokensize = found.head.length();
found._dataStart = found._tokenstart + found.head.length();
size_t endpos = interval.findclosing(found._dataStart, interval.par.length());
+ if (found.keytype == KeyInfo::noMain) {
+ evaluatingCode = true;
+ codeEnd = endpos;
+ codeStart = found._dataStart;
+ }
+ else if (evaluatingCode) {
+ if (size_t(found._dataStart) > codeEnd)
+ evaluatingCode = false;
+ else if (found.keytype == KeyInfo::isMain) {
+ // Disable this key, treate it as standard
+ found.keytype = KeyInfo::isStandard;
+ found.disabled = true;
+ if ((codeEnd == interval.par.length()) &&
+ (found._tokenstart == codeStart)) {
+ // trickery, because the code inset starts
+ // with \selectlanguage ...
+ codeEnd = endpos;
+ if (entries.size() > 1) {
+ entries[entries.size()-1]._dataEnd = codeEnd;
+ }
+ }
+ }
+ }
if ((endpos == interval.par.length()) &&
(found.keytype == KeyInfo::doRemove)) {
// Missing closing => error in latex-input?
}
else
found._dataEnd = endpos;
- if (isPatternString) {
- keys[key].used = true;
- }
+ }
+ if (isPatternString) {
+ keys[key].used = true;
}
}
entries.push_back(found);
static bool keysBuilt = false;
if (keysBuilt && !isPatternString) return;
- // Know standard keys with 1 parameter.
+ // 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);
makeKey("textbf", KeyInfo(KeyInfo::isStandard, 1, ignoreFormats.getSeries()), isPatternString);
// Know charaters
// No split
- makeKey("backslash|textbackslash|textasciicircum|textasciitilde|ldots", KeyInfo(KeyInfo::isChar, 1, false), isPatternString);
+ makeKey("backslash|textbackslash|slash", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ makeKey("textasciicircum|textasciitilde", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ makeKey("textasciiacute", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ makeKey("dots|ldots", 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);
+ makeKey("negthickspace|negmedspace|negthinspace", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ // Skip
+ makeKey("enskip|smallskip|medskip|bigskip|vfill", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ // Custom space/skip, remove the content (== length value)
+ makeKey("vspace|hspace|mspace", KeyInfo(KeyInfo::noContent, 1, false), isPatternString);
// Found in fr/UserGuide.lyx
makeKey("og|fg", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
-
+ // quotes
+ makeKey("textquotedbl|quotesinglbase|lyxarrow", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
+ makeKey("textquotedblleft|textquotedblright", KeyInfo(KeyInfo::isChar, 0, false), isPatternString);
// Known macros to remove (including their parameter)
// No split
- makeKey("inputencoding|shortcut|label|ref", KeyInfo(KeyInfo::doRemove, 1, false), isPatternString);
+ makeKey("inputencoding|shortcut|label|ref|index", KeyInfo(KeyInfo::doRemove, 1, false), isPatternString);
+
+ // handle like standard keys with 1 parameter.
+ makeKey("url|href|vref|thanks", KeyInfo(KeyInfo::isStandard, 1, false), isPatternString);
// Macros to remove, but let the parameter survive
// No split
- makeKey("url|href|menuitem|footnote|code|index|textmd", KeyInfo(KeyInfo::isStandard, 1, true), isPatternString);
+ makeKey("menuitem|textmd|textrm", KeyInfo(KeyInfo::isStandard, 1, true), isPatternString);
+
+ // Remove language spec from content of these insets
+ makeKey("code|footnote", KeyInfo(KeyInfo::noMain, 1, false), isPatternString);
// Same effect as previous, parameter will survive (because there is no one anyway)
// No split
- makeKey("noindent", KeyInfo(KeyInfo::isStandard, 0, true), isPatternString);
+ makeKey("noindent|textcompwordmark", KeyInfo(KeyInfo::isStandard, 0, true), isPatternString);
// Remove table decorations
makeKey("hline|tabularnewline|toprule|bottomrule|midrule", KeyInfo(KeyInfo::doRemove, 0, true), isPatternString);
// Discard shape-header
makeKey("triangleuppar|triangledownpar|droppar", KeyInfo(KeyInfo::isStandard, 1, true), isPatternString);
makeKey("triangleleftpar|shapepar|dropuppar", KeyInfo(KeyInfo::isStandard, 1, true), isPatternString);
// like ('tiny{}' or '\tiny ' ... )
- makeKey("footnotesize|tiny|scriptsize|small|large|Large|LARGE|huge|Huge", KeyInfo(KeyInfo::isSize, 0, true), isPatternString);
+ makeKey("footnotesize|tiny|scriptsize|small|large|Large|LARGE|huge|Huge", KeyInfo(KeyInfo::isSize, 0, false), isPatternString);
// Survives, like known character
- makeKey("lyx|latex|tex", KeyInfo(KeyInfo::isIgnored, 0, false), isPatternString);
+ makeKey("lyx|latex|latexe|tex", KeyInfo(KeyInfo::isIgnored, 0, false), isPatternString);
makeKey("item", KeyInfo(KeyInfo::isList, 1, false), isPatternString);
makeKey("begin|end", KeyInfo(KeyInfo::isMath, 1, false), isPatternString);
makeKey("[|]", KeyInfo(KeyInfo::isMath, 1, false), isPatternString);
makeKey("$", KeyInfo(KeyInfo::isMath, 1, false), isPatternString);
- makeKey("par|uldepth|ULdepth|protect", KeyInfo(KeyInfo::doRemove, 0, true), isPatternString);
+ makeKey("par|uldepth|ULdepth|protect|nobreakdash", KeyInfo(KeyInfo::isStandard, 0, true), isPatternString);
+ // Remove RTL/LTR marker
+ makeKey("l|r|textlr|textfr|textar|beginl|endl", KeyInfo(KeyInfo::isStandard, 0, true), isPatternString);
if (isPatternString) {
// Allow the first searched string to rebuild the keys too
int i = 0;
for (idx = 0; idx <= ignoreidx; idx++) {
if (i < lastpos) {
- int printsize;
if (i <= borders[idx].low) {
+ int printsize;
if (borders[idx].low > lastpos)
printsize = lastpos - i;
else
int nextKeyIdx = 0;
switch (actual.keytype)
{
+ case KeyInfo::noContent: { /* char like "\hspace{2cm}" */
+ interval.addIntervall(actual._dataStart, actual._dataEnd);
+ }
+ // fall through
case KeyInfo::isChar: {
nextKeyIdx = getNextKey();
break;
}
case KeyInfo::isSize: {
- if (actual.disabled) {
- // Allways disabled
+ if (actual.disabled || (interval.par[actual._dataStart] != '{') || (interval.par[actual._dataStart-1] == ' ')) {
processRegion(actual._dataEnd, actual._dataEnd+1); /* remove possibly following {} */
interval.addIntervall(actual._tokenstart, actual._dataEnd+1);
nextKeyIdx = getNextKey();
} else {
+ // Here _dataStart points to '{', so correct it
+ actual._dataStart += 1;
+ actual._tokensize += 1;
+ actual.parenthesiscount = 1;
+ if (interval.par[actual._dataStart] == '}') {
+ // Determine the end if used like '{\tiny{}...}'
+ actual._dataEnd = interval.findclosing(actual._dataStart+1, interval.par.length()) + 1;
+ interval.addIntervall(actual._dataStart, actual._dataStart+1);
+ }
+ else {
+ // Determine the end if used like '\tiny{...}'
+ actual._dataEnd = interval.findclosing(actual._dataStart, interval.par.length()) + 1;
+ }
// Split on this key if not at start
int start = interval.nextNotIgnored(previousStart);
if (start < actual._tokenstart) {
}
break;
}
+ case KeyInfo::noMain:
+ // fall through
case KeyInfo::isStandard: {
if (actual.disabled) {
removeHead(actual);
// Remove the key with all parameters and following spaces
size_t pos;
for (pos = actual._dataEnd+1; pos < interval.par.length(); pos++) {
- if (interval.par[pos] != ' ')
+ if ((interval.par[pos] != ' ') && (interval.par[pos] != '%'))
break;
}
interval.addIntervall(actual._tokenstart, pos);
break;
}
case KeyInfo::isMain: {
+ if (interval.par.substr(actual._dataStart, 2) == "% ")
+ interval.addIntervall(actual._dataStart, actual._dataStart+2);
if (actual.disabled) {
removeHead(actual);
if ((interval.par.substr(actual._dataStart, 3) == " \\[") ||
output_end = interval.nextNotIgnored(actual._dataEnd);
else
output_end = interval.nextNotIgnored(end);
- if (interval.nextNotIgnored(actual._dataStart) < output_end)
+ if ((actual.keytype == KeyInfo::isMain) && actual.disabled) {
+ interval.addIntervall(actual._tokenstart, actual._tokenstart+actual._tokensize);
+ }
+ // Remove possible empty data
+ int dstart = interval.nextNotIgnored(actual._dataStart);
+ while ((dstart < output_end) && (interval.par[dstart] == '{')) {
+ interval.addIntervall(dstart, dstart+1);
+ int dend = interval.findclosing(dstart+1, output_end);
+ interval.addIntervall(dend, dend+1);
+ dstart = interval.nextNotIgnored(dstart+1);
+ }
+ if (dstart < output_end)
interval.output(os, output_end);
interval.addIntervall(actual._tokenstart, end);
return nextKeyIdx;
firstKey = DummyKey;
(void) li.setNextKey(firstkeyIdx);
}
+ else {
+ if (par.substr(firstKey._dataStart, 2) == "% ")
+ li.addIntervall(firstKey._dataStart, firstKey._dataStart+2);
+ }
nextkeyIdx = li.process(os, firstKey);
while (nextkeyIdx >= 0) {
// Check for a possible gap between the last
skip++;
else if ((s[i+j] == '{') && s[i+j+1] == '}')
skip += 2;
+ else if ((s[i+j] == '{') && (i + j + 1 >= len))
+ skip++;
break;
}
skip++;
else
result = m[m.size() - close_wildcards].first - m[0].first;
- size_t pos = m.position(0);
+ size_t pos = m.position(size_t(0));
// Ignore last closing characters
while (result > 0) {
if (str[pos+result-1] == '}')
<< ", inTexted=" << cur.inTexted());
if (res == 0 || !at_begin || !opt.matchword || !cur.inTexted())
return res;
+ if ((len > 0) && (res < len))
+ return 0;
Paragraph const & par = cur.paragraph();
bool ws_left = (cur.pos() > 0)
? par.isWordSeparator(cur.pos() - 1)
: true;
- bool ws_right = (cur.pos() + res < par.size())
- ? par.isWordSeparator(cur.pos() + res)
+ bool ws_right = (cur.pos() + len < par.size())
+ ? par.isWordSeparator(cur.pos() + len)
: true;
LYXERR(Debug::FIND,
"cur.pos()=" << cur.pos() << ", res=" << res
<< ", separ: " << ws_left << ", " << ws_right
+ << ", len: " << len
<< endl);
- if (ws_left && ws_right)
- return res;
+ if (ws_left && ws_right) {
+ // Check for word separators inside the found 'word'
+ for (int i = 0; i < len; i++) {
+ if (par.isWordSeparator(cur.pos() + i))
+ return 0;
+ }
+ return res;
+ }
return 0;
}
cur.forwardPos();
} while (cur && cur.depth() > d && match(cur) > 0);
cur = old_cur;
- if (match(cur) <= 0) return 0;
+ int max_match = match(cur); /* match valid only if not searching whole words */
+ if (max_match <= 0) return 0;
LYXERR(Debug::FIND, "Ok");
// Compute the match length
- int len = 1;
+ int len = 1;
if (cur.pos() + len > cur.lastpos())
- return 0;
- LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
- while (cur.pos() + len <= cur.lastpos() && match(cur, len) <= 0) {
- ++len;
- LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
- }
- // Length of matched text (different from len param)
- int old_match = match(cur, len);
- if (old_match < 0)
- old_match = 0;
- int prev_old_match = old_match;
- int old_len = len;
- int step = 20;
- int new_match;
- if (match.opt.matchword)
- step = 1;
- while (step == 20) {
- if (cur.pos() + len + step >= cur.lastpos()) {
- step = 1;
- len = old_len;
- old_match = prev_old_match;
- }
- else {
- new_match = match(cur, len + step);
- if (new_match > old_match) {
- prev_old_match = old_match;
- old_match = new_match;
- old_len = len;
- len += step;
- }
- else {
- step = 1;
- len = old_len;
- old_match = prev_old_match;
- }
- }
- }
- // Greedy behaviour while matching regexps
- bool examining = true;
- while (examining) {
- examining = false;
- // Kornel: The loop is needed, since it looks like
- // incrementing 'cur.pos()' does not always lead to the following
- // char which we could then match.
- int maxcnt;
- if (match.opt.matchword)
- maxcnt = 2;
- else
- maxcnt = 5;
- for (int count = 1; count < maxcnt; ++count) {
- if (cur.pos() + len + count > cur.lastpos()) {
- break;
- }
- new_match = match(cur, len + count);
- if (new_match > old_match) {
- len += count;
- old_match = new_match;
- examining = true;
- break;
- }
- }
+ return 0;
+ if (match.opt.matchword) {
+ LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
+ while (cur.pos() + len <= cur.lastpos() && match(cur, len) <= 0) {
+ ++len;
+ LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
+ }
+ // Length of matched text (different from len param)
+ int old_match = match(cur, len);
+ if (old_match < 0)
+ old_match = 0;
+ int new_match;
+ // Greedy behaviour while matching regexps
+ while ((new_match = match(cur, len + 1)) > old_match) {
+ ++len;
+ old_match = new_match;
+ LYXERR(Debug::FIND, "verifying match with len = " << len);
+ }
+ if (old_match == 0)
+ len = 0;
+ }
+ else {
+ int minl = 1;
+ int maxl = cur.lastpos() - cur.pos();
+ // Greedy behaviour while matching regexps
+ while (maxl > minl) {
+ int actual_match = match(cur, len);
+ if (actual_match >= max_match) {
+ // actual_match > max_match _can_ happen,
+ // if the search area splits
+ // some following word so that the regex
+ // (e.g. 'r.*r\b' matches 'r' from the middle of the
+ // splitted word)
+ // This means, the len value is too big
+ maxl = len;
+ len = (int)((maxl + minl)/2);
+ }
+ else {
+ // (actual_match < max_match)
+ minl = len + 1;
+ len = (int)((maxl + minl)/2);
+ }
+ }
}
return len;
}
int match_len_zero_count = 0;
for (; !theApp()->longOperationCancelled() && cur; cur.forwardPos()) {
LYXERR(Debug::FIND, "Advancing cur: " << cur);
+ int match_len3 = match(cur, 1);
+ if (match_len3 < 0)
+ continue;
int match_len2 = match(cur);
LYXERR(Debug::FIND, "match_len2: " << match_len2);
if (match_len2 > 0) {