2003-08-23 00:17:00 +00:00
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
* \file lyxfind.cpp
|
2003-08-23 00:17:00 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
2008-12-20 16:00:47 +00:00
|
|
|
* License details can be found in the file COPYING.
|
2003-08-23 00:17:00 +00:00
|
|
|
*
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Lars Gullik Bjønnes
|
2003-08-23 00:17:00 +00:00
|
|
|
* \author John Levon
|
2008-11-14 15:58:50 +00:00
|
|
|
* \author Jürgen Vigna
|
2003-11-04 12:01:15 +00:00
|
|
|
* \author Alfredo Braunstein
|
2008-11-15 23:30:27 +00:00
|
|
|
* \author Tommaso Cucinotta
|
2003-08-23 00:17:00 +00:00
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
|
|
|
|
2001-03-06 10:20:33 +00:00
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "lyxfind.h"
|
2003-09-09 22:13:45 +00:00
|
|
|
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Buffer.h"
|
2006-12-29 23:54:48 +00:00
|
|
|
#include "buffer_funcs.h"
|
2010-01-10 12:37:50 +00:00
|
|
|
#include "BufferList.h"
|
2008-11-15 23:30:27 +00:00
|
|
|
#include "BufferParams.h"
|
|
|
|
#include "BufferView.h"
|
2007-10-18 15:29:51 +00:00
|
|
|
#include "Changes.h"
|
2008-11-15 23:30:27 +00:00
|
|
|
#include "Cursor.h"
|
|
|
|
#include "CutAndPaste.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "FuncRequest.h"
|
2010-02-09 16:11:13 +00:00
|
|
|
#include "LyX.h"
|
2008-11-15 23:30:27 +00:00
|
|
|
#include "output_latex.h"
|
2010-01-10 12:37:50 +00:00
|
|
|
#include "OutputParams.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Paragraph.h"
|
|
|
|
#include "ParIterator.h"
|
2008-11-15 23:30:27 +00:00
|
|
|
#include "TexRow.h"
|
|
|
|
#include "Text.h"
|
2010-01-10 12:37:50 +00:00
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
#include "frontends/Application.h"
|
2010-01-10 12:37:50 +00:00
|
|
|
#include "frontends/alert.h"
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
#include "mathed/InsetMath.h"
|
|
|
|
#include "mathed/InsetMathGrid.h"
|
|
|
|
#include "mathed/InsetMathHull.h"
|
|
|
|
#include "mathed/MathStream.h"
|
2003-09-09 22:13:45 +00:00
|
|
|
|
2005-01-06 16:39:35 +00:00
|
|
|
#include "support/convert.h"
|
2008-02-18 07:14:42 +00:00
|
|
|
#include "support/debug.h"
|
2006-12-10 11:52:46 +00:00
|
|
|
#include "support/docstream.h"
|
2008-02-18 07:14:42 +00:00
|
|
|
#include "support/gettext.h"
|
2008-12-20 16:00:47 +00:00
|
|
|
#include "support/lassert.h"
|
2010-01-10 12:37:50 +00:00
|
|
|
#include "support/lstrings.h"
|
2009-12-30 18:40:18 +00:00
|
|
|
|
2010-06-29 17:09:40 +00:00
|
|
|
#include "support/regex.h"
|
2008-12-20 16:00:47 +00:00
|
|
|
#include <boost/next_prior.hpp>
|
2008-11-15 23:30:27 +00:00
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2007-12-12 18:57:56 +00:00
|
|
|
using namespace lyx::support;
|
2007-12-12 10:16:00 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
namespace lyx {
|
2004-01-07 17:00:03 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2006-12-10 11:52:46 +00:00
|
|
|
bool parse_bool(docstring & howto)
|
2004-01-07 17:00:03 +00:00
|
|
|
{
|
|
|
|
if (howto.empty())
|
|
|
|
return false;
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring var;
|
2004-01-07 17:00:03 +00:00
|
|
|
howto = split(howto, var, ' ');
|
2008-11-15 23:30:27 +00:00
|
|
|
return var == "1";
|
2004-01-07 17:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
class MatchString : public binary_function<Paragraph, pos_type, int>
|
2003-07-27 12:02:58 +00:00
|
|
|
{
|
2003-11-04 12:01:15 +00:00
|
|
|
public:
|
2006-12-10 11:52:46 +00:00
|
|
|
MatchString(docstring const & str, bool cs, bool mw)
|
2010-10-13 18:24:53 +00:00
|
|
|
: str(str), case_sens(cs), whole_words(mw)
|
2004-01-07 14:07:46 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
// returns true if the specified string is at the specified position
|
2007-06-10 15:07:21 +00:00
|
|
|
// del specifies whether deleted strings in ct mode will be considered
|
2010-11-22 07:50:34 +00:00
|
|
|
int operator()(Paragraph const & par, pos_type pos, bool del = true) const
|
2004-01-07 17:00:03 +00:00
|
|
|
{
|
2010-10-13 18:24:53 +00:00
|
|
|
return par.find(str, case_sens, whole_words, pos, del);
|
2003-07-27 12:02:58 +00:00
|
|
|
}
|
2004-01-26 17:00:09 +00:00
|
|
|
|
2003-11-04 12:01:15 +00:00
|
|
|
private:
|
2004-01-07 14:07:46 +00:00
|
|
|
// search string
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring str;
|
2004-01-07 14:07:46 +00:00
|
|
|
// case sensitive
|
2010-10-13 18:24:53 +00:00
|
|
|
bool case_sens;
|
2004-01-07 14:07:46 +00:00
|
|
|
// match whole words only
|
2010-10-13 18:24:53 +00:00
|
|
|
bool whole_words;
|
2003-11-04 12:01:15 +00:00
|
|
|
};
|
2003-07-27 12:02:58 +00:00
|
|
|
|
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
int findForward(DocIterator & cur, MatchString const & match,
|
2007-06-10 15:07:21 +00:00
|
|
|
bool find_del = true)
|
2001-03-06 10:20:33 +00:00
|
|
|
{
|
2004-04-01 08:58:45 +00:00
|
|
|
for (; cur; cur.forwardChar())
|
2010-11-22 07:50:34 +00:00
|
|
|
if (cur.inTexted()) {
|
|
|
|
int len = match(cur.paragraph(), cur.pos(), find_del);
|
|
|
|
if (len > 0)
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
return 0;
|
2003-11-04 12:01:15 +00:00
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
2001-08-13 10:09:50 +00:00
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
int findBackwards(DocIterator & cur, MatchString const & match,
|
2007-06-10 15:07:21 +00:00
|
|
|
bool find_del = true)
|
2003-11-04 12:01:15 +00:00
|
|
|
{
|
2004-09-17 16:28:47 +00:00
|
|
|
while (cur) {
|
|
|
|
cur.backwardChar();
|
2010-11-22 07:50:34 +00:00
|
|
|
if (cur.inTexted()) {
|
|
|
|
int len = match(cur.paragraph(), cur.pos(), find_del);
|
|
|
|
if (len > 0)
|
|
|
|
return len;
|
|
|
|
}
|
2004-09-17 16:28:47 +00:00
|
|
|
}
|
2010-11-22 07:50:34 +00:00
|
|
|
return 0;
|
2003-11-04 12:01:15 +00:00
|
|
|
}
|
2003-03-19 14:45:22 +00:00
|
|
|
|
2003-11-04 12:01:15 +00:00
|
|
|
|
2009-04-05 19:11:25 +00:00
|
|
|
bool findChange(DocIterator & cur, bool next)
|
2003-11-04 12:01:15 +00:00
|
|
|
{
|
2009-05-07 12:40:35 +00:00
|
|
|
if (!next)
|
2009-04-05 19:11:25 +00:00
|
|
|
cur.backwardPos();
|
|
|
|
for (; cur; next ? cur.forwardPos() : cur.backwardPos())
|
2009-08-04 22:44:23 +00:00
|
|
|
if (cur.inTexted() && cur.paragraph().isChanged(cur.pos())) {
|
2009-04-05 19:11:25 +00:00
|
|
|
if (!next)
|
|
|
|
// if we search backwards, take a step forward
|
|
|
|
// to correctly set the anchor
|
|
|
|
cur.forwardPos();
|
2003-11-17 09:02:10 +00:00
|
|
|
return true;
|
2009-04-05 19:11:25 +00:00
|
|
|
}
|
|
|
|
|
2003-11-17 09:02:10 +00:00
|
|
|
return false;
|
2001-03-06 10:20:33 +00:00
|
|
|
}
|
2002-06-24 20:28:12 +00:00
|
|
|
|
2001-03-06 10:20:33 +00:00
|
|
|
|
2010-10-13 18:27:40 +00:00
|
|
|
bool searchAllowed(docstring const & str)
|
2001-03-06 10:20:33 +00:00
|
|
|
{
|
2003-11-04 12:01:15 +00:00
|
|
|
if (str.empty()) {
|
2007-11-13 23:50:28 +00:00
|
|
|
frontend::Alert::error(_("Search error"), _("Search string is empty"));
|
2001-07-20 14:18:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-21 13:03:55 +00:00
|
|
|
return true;
|
2001-03-06 10:20:33 +00:00
|
|
|
}
|
2002-06-24 20:28:12 +00:00
|
|
|
|
2003-11-10 09:06:48 +00:00
|
|
|
|
2010-10-13 18:19:21 +00:00
|
|
|
bool findOne(BufferView * bv, docstring const & searchstr,
|
2010-10-13 18:24:53 +00:00
|
|
|
bool case_sens, bool whole, bool forward, bool find_del = true)
|
2002-06-18 15:44:30 +00:00
|
|
|
{
|
2010-10-13 18:27:40 +00:00
|
|
|
if (!searchAllowed(searchstr))
|
2003-11-04 12:01:15 +00:00
|
|
|
return false;
|
|
|
|
|
2004-03-31 19:11:56 +00:00
|
|
|
DocIterator cur = bv->cursor();
|
2002-06-18 15:44:30 +00:00
|
|
|
|
2010-10-13 18:24:53 +00:00
|
|
|
MatchString const match(searchstr, case_sens, whole);
|
2003-11-17 09:02:10 +00:00
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
int match_len = forward ? findForward(cur, match, find_del) :
|
2007-06-10 15:07:21 +00:00
|
|
|
findBackwards(cur, match, find_del);
|
2003-11-17 09:02:10 +00:00
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
if (match_len > 0)
|
|
|
|
bv->putSelectionAt(cur, match_len, !forward);
|
2002-06-18 15:44:30 +00:00
|
|
|
|
2010-11-22 07:50:34 +00:00
|
|
|
return match_len > 0;
|
2002-06-18 15:44:30 +00:00
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-11-04 12:01:15 +00:00
|
|
|
int replaceAll(BufferView * bv,
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring const & searchstr, docstring const & replacestr,
|
2010-10-13 18:24:53 +00:00
|
|
|
bool case_sens, bool whole)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2007-08-21 13:03:55 +00:00
|
|
|
Buffer & buf = bv->buffer();
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2010-10-13 18:27:40 +00:00
|
|
|
if (!searchAllowed(searchstr) || buf.isReadonly())
|
2003-11-04 12:01:15 +00:00
|
|
|
return 0;
|
2004-01-26 17:00:09 +00:00
|
|
|
|
2010-03-21 10:36:59 +00:00
|
|
|
DocIterator cur_orig(bv->cursor());
|
|
|
|
|
2010-10-13 18:24:53 +00:00
|
|
|
MatchString const match(searchstr, case_sens, whole);
|
2003-11-04 12:01:15 +00:00
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
int const rsize = replacestr.size();
|
|
|
|
int const ssize = searchstr.size();
|
2003-11-17 09:02:10 +00:00
|
|
|
|
2008-08-15 19:24:56 +00:00
|
|
|
Cursor cur(*bv);
|
2008-11-17 11:46:07 +00:00
|
|
|
cur.setCursor(doc_iterator_begin(&buf));
|
2010-11-22 07:50:34 +00:00
|
|
|
int match_len = findForward(cur, match, false);
|
|
|
|
while (match_len > 0) {
|
2008-10-28 18:00:21 +00:00
|
|
|
// Backup current cursor position and font.
|
|
|
|
pos_type const pos = cur.pos();
|
|
|
|
Font const font = cur.paragraph().getFontSettings(buf.params(), pos);
|
2008-08-15 19:24:56 +00:00
|
|
|
cur.recordUndo();
|
2010-11-22 07:50:34 +00:00
|
|
|
int striked = ssize - cur.paragraph().eraseChars(pos, pos + match_len,
|
2006-10-20 13:16:15 +00:00
|
|
|
buf.params().trackChanges);
|
2006-12-10 11:52:46 +00:00
|
|
|
cur.paragraph().insert(pos, replacestr, font,
|
2007-05-28 22:27:45 +00:00
|
|
|
Change(buf.params().trackChanges ?
|
|
|
|
Change::INSERTED : Change::UNCHANGED));
|
2004-03-25 09:16:36 +00:00
|
|
|
for (int i = 0; i < rsize + striked; ++i)
|
|
|
|
cur.forwardChar();
|
2003-11-04 12:01:15 +00:00
|
|
|
++num;
|
2010-11-22 07:50:34 +00:00
|
|
|
match_len = findForward(cur, match, false);
|
2003-11-04 12:01:15 +00:00
|
|
|
}
|
2003-11-17 09:02:10 +00:00
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
bv->putSelectionAt(doc_iterator_begin(&buf), 0, false);
|
2010-03-21 10:36:59 +00:00
|
|
|
|
|
|
|
cur_orig.fixIfBroken();
|
|
|
|
bv->setCursor(cur_orig);
|
|
|
|
|
2003-11-04 12:01:15 +00:00
|
|
|
return num;
|
|
|
|
}
|
2003-04-16 00:39:24 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2010-10-14 14:32:58 +00:00
|
|
|
// the idea here is that we are going to replace the string that
|
|
|
|
// is selected IF it is the search string.
|
|
|
|
// if there is a selection, but it is not the search string, then
|
|
|
|
// we basically ignore it. (FIXME We ought to replace only within
|
|
|
|
// the selection.)
|
|
|
|
// if there is no selection, then:
|
|
|
|
// (i) if some search string has been provided, then we find it.
|
|
|
|
// (think of how the dialog works when you hit "replace" the
|
|
|
|
// first time.)
|
|
|
|
// (ii) if no search string has been provided, then we treat the
|
|
|
|
// word the cursor is in as the search string. (why? i have no
|
|
|
|
// idea.) but this only works in text?
|
|
|
|
//
|
|
|
|
// returns the number of replacements made (one, if any) and
|
|
|
|
// whether anything at all was done.
|
|
|
|
pair<bool, int> replaceOne(BufferView * bv, docstring searchstr,
|
|
|
|
docstring const & replacestr, bool case_sens,
|
|
|
|
bool whole, bool forward)
|
2003-11-17 09:02:10 +00:00
|
|
|
{
|
2010-10-14 14:32:58 +00:00
|
|
|
Cursor & cur = bv->cursor();
|
|
|
|
if (!cur.selection()) {
|
|
|
|
// no selection, non-empty search string: find it
|
|
|
|
if (!searchstr.empty()) {
|
|
|
|
findOne(bv, searchstr, case_sens, whole, forward);
|
|
|
|
return pair<bool, int>(true, 0);
|
|
|
|
}
|
|
|
|
// empty search string
|
|
|
|
if (!cur.inTexted())
|
|
|
|
// bail in math
|
2010-11-14 23:23:20 +00:00
|
|
|
return pair<bool, int>(false, 0);
|
2010-10-14 14:32:58 +00:00
|
|
|
// select current word and treat it as the search string
|
|
|
|
cur.innerText()->selectWord(cur, WHOLE_WORD);
|
|
|
|
searchstr = cur.selectionAsString(false);
|
2009-07-04 23:02:27 +00:00
|
|
|
}
|
2010-10-14 14:32:58 +00:00
|
|
|
|
|
|
|
// if we still don't have a search string, report the error
|
|
|
|
// and abort.
|
|
|
|
if (!searchAllowed(searchstr))
|
|
|
|
return pair<bool, int>(false, 0);
|
|
|
|
|
|
|
|
bool have_selection = cur.selection();
|
|
|
|
docstring const selected = cur.selectionAsString(false);
|
|
|
|
bool match =
|
|
|
|
case_sens ? searchstr == selected
|
|
|
|
: compare_no_case(searchstr, selected) == 0;
|
|
|
|
|
|
|
|
// no selection or current selection is not search word:
|
|
|
|
// just find the search word
|
|
|
|
if (!have_selection || !match) {
|
2010-10-13 18:24:53 +00:00
|
|
|
findOne(bv, searchstr, case_sens, whole, forward);
|
2010-10-14 14:32:58 +00:00
|
|
|
return pair<bool, int>(true, 0);
|
2003-11-17 09:02:10 +00:00
|
|
|
}
|
|
|
|
|
2010-10-14 14:32:58 +00:00
|
|
|
// we're now actually ready to replace. if the buffer is
|
|
|
|
// read-only, we can't, though.
|
|
|
|
if (bv->buffer().isReadonly())
|
|
|
|
return pair<bool, int>(false, 0);
|
2003-11-17 09:02:10 +00:00
|
|
|
|
2010-10-13 18:24:53 +00:00
|
|
|
cap::replaceSelectionWithString(cur, replacestr, forward);
|
2010-12-30 15:34:09 +00:00
|
|
|
if (forward) {
|
|
|
|
cur.pos() += replacestr.length();
|
|
|
|
LASSERT(cur.pos() <= cur.lastpos(), /* */);
|
|
|
|
} else {
|
|
|
|
cur.pos() -= replacestr.length();
|
|
|
|
LASSERT(cur.pos() >= 0, /* */);
|
|
|
|
}
|
2010-10-13 18:24:53 +00:00
|
|
|
findOne(bv, searchstr, case_sens, whole, forward, false);
|
2004-01-26 17:00:09 +00:00
|
|
|
|
2010-10-14 14:32:58 +00:00
|
|
|
return pair<bool, int>(true, 1);
|
2003-11-04 12:01:15 +00:00
|
|
|
}
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
} // namespace anon
|
|
|
|
|
|
|
|
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring const find2string(docstring const & search,
|
2004-03-25 09:16:36 +00:00
|
|
|
bool casesensitive, bool matchword, bool forward)
|
|
|
|
{
|
2006-12-10 11:52:46 +00:00
|
|
|
odocstringstream ss;
|
2004-03-25 09:16:36 +00:00
|
|
|
ss << search << '\n'
|
|
|
|
<< int(casesensitive) << ' '
|
|
|
|
<< int(matchword) << ' '
|
|
|
|
<< int(forward);
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-04 23:02:27 +00:00
|
|
|
docstring const replace2string(docstring const & replace,
|
|
|
|
docstring const & search, bool casesensitive, bool matchword,
|
|
|
|
bool all, bool forward)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2006-12-10 11:52:46 +00:00
|
|
|
odocstringstream ss;
|
2009-07-04 23:02:27 +00:00
|
|
|
ss << replace << '\n'
|
|
|
|
<< search << '\n'
|
2004-03-25 09:16:36 +00:00
|
|
|
<< int(casesensitive) << ' '
|
|
|
|
<< int(matchword) << ' '
|
|
|
|
<< int(all) << ' '
|
|
|
|
<< int(forward);
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-13 18:53:41 +00:00
|
|
|
bool lyxfind(BufferView * bv, FuncRequest const & ev)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2010-04-09 19:00:42 +00:00
|
|
|
if (!bv || ev.action() != LFUN_WORD_FIND)
|
2008-02-10 18:52:32 +00:00
|
|
|
return false;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2007-12-12 19:28:07 +00:00
|
|
|
//lyxerr << "find called, cmd: " << ev << endl;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// data is of the form
|
|
|
|
// "<search>
|
|
|
|
// <casesensitive> <matchword> <forward>"
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring search;
|
|
|
|
docstring howto = split(ev.argument(), search, '\n');
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
bool casesensitive = parse_bool(howto);
|
|
|
|
bool matchword = parse_bool(howto);
|
|
|
|
bool forward = parse_bool(howto);
|
|
|
|
|
2010-10-13 18:19:21 +00:00
|
|
|
return findOne(bv, search, casesensitive, matchword, forward);
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-14 14:32:58 +00:00
|
|
|
bool lyxreplace(BufferView * bv,
|
|
|
|
FuncRequest const & ev, bool has_deleted)
|
2004-03-25 09:16:36 +00:00
|
|
|
{
|
2010-04-09 19:00:42 +00:00
|
|
|
if (!bv || ev.action() != LFUN_WORD_REPLACE)
|
2010-10-13 18:53:41 +00:00
|
|
|
return false;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
// data is of the form
|
|
|
|
// "<search>
|
|
|
|
// <replace>
|
|
|
|
// <casesensitive> <matchword> <all> <forward>"
|
2006-12-10 11:52:46 +00:00
|
|
|
docstring search;
|
|
|
|
docstring rplc;
|
2009-07-04 23:02:27 +00:00
|
|
|
docstring howto = split(ev.argument(), rplc, '\n');
|
|
|
|
howto = split(howto, search, '\n');
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
bool casesensitive = parse_bool(howto);
|
|
|
|
bool matchword = parse_bool(howto);
|
|
|
|
bool all = parse_bool(howto);
|
|
|
|
bool forward = parse_bool(howto);
|
|
|
|
|
2010-10-14 14:32:58 +00:00
|
|
|
int replace_count = 0;
|
|
|
|
bool update = false;
|
|
|
|
|
2007-06-19 21:48:04 +00:00
|
|
|
if (!has_deleted) {
|
2010-10-14 14:32:58 +00:00
|
|
|
if (all) {
|
|
|
|
replace_count = replaceAll(bv, search, rplc, casesensitive, matchword);
|
|
|
|
update = replace_count > 0;
|
|
|
|
} else {
|
|
|
|
pair<bool, int> rv =
|
|
|
|
replaceOne(bv, search, rplc, casesensitive, matchword, forward);
|
|
|
|
update = rv.first;
|
|
|
|
replace_count = rv.second;
|
|
|
|
}
|
2009-05-07 12:40:35 +00:00
|
|
|
|
2010-10-13 18:59:24 +00:00
|
|
|
Buffer const & buf = bv->buffer();
|
2010-10-14 14:32:58 +00:00
|
|
|
if (!update) {
|
2006-08-23 21:14:43 +00:00
|
|
|
// emit message signal.
|
2007-08-21 13:03:55 +00:00
|
|
|
buf.message(_("String not found!"));
|
2004-03-25 09:16:36 +00:00
|
|
|
} else {
|
2010-10-14 14:32:58 +00:00
|
|
|
if (replace_count == 0) {
|
|
|
|
buf.message(_("String found."));
|
|
|
|
} else if (replace_count == 1) {
|
2007-08-21 13:03:55 +00:00
|
|
|
buf.message(_("String has been replaced."));
|
2007-06-19 21:48:04 +00:00
|
|
|
} else {
|
2010-10-13 18:57:57 +00:00
|
|
|
docstring const str =
|
|
|
|
bformat(_("%1$d strings have been replaced."), replace_count);
|
2007-08-21 13:03:55 +00:00
|
|
|
buf.message(str);
|
2007-06-19 21:48:04 +00:00
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
2007-06-19 21:48:04 +00:00
|
|
|
} else {
|
|
|
|
// if we have deleted characters, we do not replace at all, but
|
|
|
|
// rather search for the next occurence
|
2010-10-13 18:19:21 +00:00
|
|
|
if (findOne(bv, search, casesensitive, matchword, forward))
|
2010-10-14 14:32:58 +00:00
|
|
|
update = true;
|
2008-02-10 19:05:09 +00:00
|
|
|
else
|
2007-06-19 21:48:04 +00:00
|
|
|
bv->message(_("String not found!"));
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
2010-10-14 14:32:58 +00:00
|
|
|
return update;
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool findNextChange(BufferView * bv)
|
|
|
|
{
|
2009-04-05 19:11:25 +00:00
|
|
|
return findChange(bv, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool findPreviousChange(BufferView * bv)
|
|
|
|
{
|
|
|
|
return findChange(bv, false);
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2009-04-05 19:11:25 +00:00
|
|
|
|
|
|
|
bool findChange(BufferView * bv, bool next)
|
|
|
|
{
|
2009-04-05 20:16:32 +00:00
|
|
|
if (bv->cursor().selection()) {
|
|
|
|
// set the cursor at the beginning or at the end of the selection
|
|
|
|
// before searching. Otherwise, the current change will be found.
|
2010-04-15 17:49:15 +00:00
|
|
|
if (next != (bv->cursor().top() > bv->cursor().normalAnchor()))
|
2009-04-05 20:16:32 +00:00
|
|
|
bv->cursor().setCursorToAnchor();
|
|
|
|
}
|
|
|
|
|
2009-04-05 19:11:25 +00:00
|
|
|
DocIterator cur = bv->cursor();
|
2009-05-07 12:40:35 +00:00
|
|
|
|
|
|
|
// Are we within a change ? Then first search forward (backward),
|
2009-04-15 22:07:59 +00:00
|
|
|
// clear the selection and search the other way around (see the end
|
|
|
|
// of this function). This will avoid changes to be selected half.
|
|
|
|
bool search_both_sides = false;
|
2010-01-06 17:29:39 +00:00
|
|
|
DocIterator tmpcur = cur;
|
|
|
|
// Leave math first
|
|
|
|
while (tmpcur.inMathed())
|
|
|
|
tmpcur.pop_back();
|
|
|
|
Change change_next_pos
|
|
|
|
= tmpcur.paragraph().lookupChange(tmpcur.pos());
|
|
|
|
if (change_next_pos.changed() && cur.inMathed()) {
|
|
|
|
cur = tmpcur;
|
|
|
|
search_both_sides = true;
|
|
|
|
} else if (tmpcur.pos() > 0 && tmpcur.inTexted()) {
|
2009-05-07 12:40:35 +00:00
|
|
|
Change change_prev_pos
|
2010-01-06 17:29:39 +00:00
|
|
|
= tmpcur.paragraph().lookupChange(tmpcur.pos() - 1);
|
2009-04-15 22:07:59 +00:00
|
|
|
if (change_next_pos.isSimilarTo(change_prev_pos))
|
|
|
|
search_both_sides = true;
|
|
|
|
}
|
|
|
|
|
2009-04-05 19:11:25 +00:00
|
|
|
if (!findChange(cur, next))
|
2004-03-25 09:16:36 +00:00
|
|
|
return false;
|
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
bv->cursor().setCursor(cur);
|
|
|
|
bv->cursor().resetAnchor();
|
2006-04-05 23:56:29 +00:00
|
|
|
|
2009-04-05 19:11:25 +00:00
|
|
|
if (!next)
|
|
|
|
// take a step into the change
|
|
|
|
cur.backwardPos();
|
|
|
|
|
2006-05-05 23:11:19 +00:00
|
|
|
Change orig_change = cur.paragraph().lookupChange(cur.pos());
|
2004-03-25 09:16:36 +00:00
|
|
|
|
2007-08-13 13:36:19 +00:00
|
|
|
CursorSlice & tip = cur.top();
|
2009-04-05 19:11:25 +00:00
|
|
|
if (next) {
|
|
|
|
for (; !tip.at_end(); tip.forwardPos()) {
|
|
|
|
Change change = tip.paragraph().lookupChange(tip.pos());
|
2010-01-31 00:21:52 +00:00
|
|
|
if (!change.isSimilarTo(orig_change))
|
2009-04-05 19:11:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2009-04-15 22:09:27 +00:00
|
|
|
for (; !tip.at_begin();) {
|
|
|
|
tip.backwardPos();
|
2009-04-05 19:11:25 +00:00
|
|
|
Change change = tip.paragraph().lookupChange(tip.pos());
|
2010-01-31 00:21:52 +00:00
|
|
|
if (!change.isSimilarTo(orig_change)) {
|
2009-04-05 19:11:25 +00:00
|
|
|
// take a step forward to correctly set the selection
|
|
|
|
tip.forwardPos();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
}
|
2007-02-01 17:26:29 +00:00
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
// Now put cursor to end of selection:
|
|
|
|
bv->cursor().setCursor(cur);
|
|
|
|
bv->cursor().setSelection();
|
2004-09-17 16:28:47 +00:00
|
|
|
|
2009-04-15 22:07:59 +00:00
|
|
|
if (search_both_sides) {
|
|
|
|
bv->cursor().setSelection(false);
|
|
|
|
findChange(bv, !next);
|
|
|
|
}
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
typedef vector<pair<string, string> > Escapes;
|
|
|
|
|
|
|
|
/// A map of symbols and their escaped equivalent needed within a regex.
|
|
|
|
Escapes const & get_regexp_escapes()
|
|
|
|
{
|
|
|
|
static Escapes escape_map;
|
|
|
|
if (escape_map.empty()) {
|
|
|
|
escape_map.push_back(pair<string, string>("\\", "\\\\"));
|
|
|
|
escape_map.push_back(pair<string, string>("^", "\\^"));
|
|
|
|
escape_map.push_back(pair<string, string>("$", "\\$"));
|
|
|
|
escape_map.push_back(pair<string, string>("{", "\\{"));
|
|
|
|
escape_map.push_back(pair<string, string>("}", "\\}"));
|
|
|
|
escape_map.push_back(pair<string, string>("[", "\\["));
|
|
|
|
escape_map.push_back(pair<string, string>("]", "\\]"));
|
|
|
|
escape_map.push_back(pair<string, string>("(", "\\("));
|
|
|
|
escape_map.push_back(pair<string, string>(")", "\\)"));
|
|
|
|
escape_map.push_back(pair<string, string>("+", "\\+"));
|
|
|
|
escape_map.push_back(pair<string, string>("*", "\\*"));
|
|
|
|
escape_map.push_back(pair<string, string>(".", "\\."));
|
|
|
|
}
|
|
|
|
return escape_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A map of lyx escaped strings and their unescaped equivalent.
|
|
|
|
Escapes const & get_lyx_unescapes() {
|
|
|
|
static Escapes escape_map;
|
|
|
|
if (escape_map.empty()) {
|
|
|
|
escape_map.push_back(pair<string, string>("{*}", "*"));
|
|
|
|
escape_map.push_back(pair<string, string>("{[}", "["));
|
|
|
|
escape_map.push_back(pair<string, string>("\\$", "$"));
|
|
|
|
escape_map.push_back(pair<string, string>("\\backslash{}", "\\"));
|
|
|
|
escape_map.push_back(pair<string, string>("\\backslash", "\\"));
|
|
|
|
escape_map.push_back(pair<string, string>("\\sim ", "~"));
|
|
|
|
escape_map.push_back(pair<string, string>("\\^", "^"));
|
|
|
|
}
|
|
|
|
return escape_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @todo Probably the maps need to be migrated to regexps, in order to distinguish if
|
|
|
|
** the found occurrence were escaped.
|
|
|
|
**/
|
|
|
|
string apply_escapes(string s, Escapes const & escape_map)
|
|
|
|
{
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Escaping: '" << s << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
Escapes::const_iterator it;
|
|
|
|
for (it = escape_map.begin(); it != escape_map.end(); ++it) {
|
2009-08-17 14:39:00 +00:00
|
|
|
// LYXERR(Debug::FIND, "Escaping " << it->first << " as " << it->second);
|
2008-11-15 23:30:27 +00:00
|
|
|
unsigned int pos = 0;
|
|
|
|
while (pos < s.length() && (pos = s.find(it->first, pos)) < s.length()) {
|
|
|
|
s.replace(pos, it->first.length(), it->second);
|
2009-08-17 14:39:00 +00:00
|
|
|
// LYXERR(Debug::FIND, "After escape: " << s);
|
2008-11-15 23:30:27 +00:00
|
|
|
pos += it->second.length();
|
2009-08-17 14:39:00 +00:00
|
|
|
// LYXERR(Debug::FIND, "pos: " << pos);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
}
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Escaped : '" << s << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the position of the closing brace matching the open one at s[pos],
|
|
|
|
** or s.size() if not found.
|
|
|
|
**/
|
|
|
|
size_t find_matching_brace(string const & s, size_t pos)
|
|
|
|
{
|
2008-12-20 16:00:47 +00:00
|
|
|
LASSERT(s[pos] == '{', /* */);
|
2008-11-15 23:30:27 +00:00
|
|
|
int open_braces = 1;
|
|
|
|
for (++pos; pos < s.size(); ++pos) {
|
|
|
|
if (s[pos] == '\\')
|
|
|
|
++pos;
|
|
|
|
else if (s[pos] == '{')
|
|
|
|
++open_braces;
|
|
|
|
else if (s[pos] == '}') {
|
|
|
|
--open_braces;
|
|
|
|
if (open_braces == 0)
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s.size();
|
|
|
|
}
|
|
|
|
|
2009-08-15 15:35:14 +00:00
|
|
|
/// Within \regexp{} apply get_regex_escapes(), while outside apply get_lyx_unescapes().
|
2008-11-15 23:30:27 +00:00
|
|
|
string escape_for_regex(string s)
|
|
|
|
{
|
|
|
|
size_t pos = 0;
|
|
|
|
while (pos < s.size()) {
|
2010-02-07 21:44:31 +00:00
|
|
|
size_t new_pos = s.find("\\regexp{{{", pos);
|
|
|
|
if (new_pos == string::npos)
|
|
|
|
new_pos = s.size();
|
|
|
|
LYXERR(Debug::FIND, "new_pos: " << new_pos);
|
|
|
|
string t = apply_escapes(s.substr(pos, new_pos - pos), get_lyx_unescapes());
|
|
|
|
LYXERR(Debug::FIND, "t : " << t);
|
|
|
|
t = apply_escapes(t, get_regexp_escapes());
|
|
|
|
LYXERR(Debug::FIND, "t : " << t);
|
|
|
|
s.replace(pos, new_pos - pos, t);
|
|
|
|
new_pos = pos + t.size();
|
|
|
|
LYXERR(Debug::FIND, "Regexp after escaping: " << s);
|
|
|
|
LYXERR(Debug::FIND, "new_pos: " << new_pos);
|
|
|
|
if (new_pos == s.size())
|
|
|
|
break;
|
|
|
|
size_t end_pos = s.find("}}}", new_pos + 10); // find_matching_brace(s, new_pos + 7);
|
|
|
|
LYXERR(Debug::FIND, "end_pos: " << end_pos);
|
|
|
|
t = apply_escapes(s.substr(new_pos + 10, end_pos - (new_pos + 10)), get_lyx_unescapes());
|
|
|
|
LYXERR(Debug::FIND, "t : " << t);
|
|
|
|
if (end_pos == s.size()) {
|
|
|
|
s.replace(new_pos, end_pos - new_pos, t);
|
|
|
|
pos = s.size();
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Regexp after \\regexp{} removal: " << s);
|
2010-02-07 21:44:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
s.replace(new_pos, end_pos + 3 - new_pos, t);
|
|
|
|
LYXERR(Debug::FIND, "Regexp after \\regexp{} removal: " << s);
|
|
|
|
pos = new_pos + t.size();
|
|
|
|
LYXERR(Debug::FIND, "pos: " << pos);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2010-06-29 17:09:40 +00:00
|
|
|
/// Wrapper for lyx::regex_replace with simpler interface
|
2008-11-15 23:30:27 +00:00
|
|
|
bool regex_replace(string const & s, string & t, string const & searchstr,
|
|
|
|
string const & replacestr)
|
|
|
|
{
|
2010-06-29 17:09:40 +00:00
|
|
|
lyx::regex e(searchstr);
|
2008-11-15 23:30:27 +00:00
|
|
|
ostringstream oss;
|
|
|
|
ostream_iterator<char, char> it(oss);
|
2010-06-29 17:09:40 +00:00
|
|
|
lyx::regex_replace(it, s.begin(), s.end(), e, replacestr);
|
2008-11-15 23:30:27 +00:00
|
|
|
// tolerate t and s be references to the same variable
|
|
|
|
bool rv = (s != oss.str());
|
|
|
|
t = oss.str();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Checks if supplied string segment is well-formed from the standpoint of matching open-closed braces.
|
|
|
|
**
|
|
|
|
** Verify that closed braces exactly match open braces. This avoids that, for example,
|
|
|
|
** \frac{.*}{x} matches \frac{x+\frac{y}{x}}{z} with .* being 'x+\frac{y'.
|
|
|
|
**
|
|
|
|
** @param unmatched
|
|
|
|
** Number of open braces that must remain open at the end for the verification to succeed.
|
|
|
|
**/
|
|
|
|
bool braces_match(string::const_iterator const & beg,
|
2010-02-07 21:44:31 +00:00
|
|
|
string::const_iterator const & end,
|
|
|
|
int unmatched = 0)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
|
|
|
int open_pars = 0;
|
|
|
|
string::const_iterator it = beg;
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Checking " << unmatched << " unmatched braces in '" << string(beg, end) << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
for (; it != end; ++it) {
|
|
|
|
// Skip escaped braces in the count
|
|
|
|
if (*it == '\\') {
|
|
|
|
++it;
|
|
|
|
if (it == end)
|
|
|
|
break;
|
|
|
|
} else if (*it == '{') {
|
|
|
|
++open_pars;
|
|
|
|
} else if (*it == '}') {
|
|
|
|
if (open_pars == 0) {
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Found unmatched closed brace");
|
2008-11-15 23:30:27 +00:00
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
--open_pars;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (open_pars != unmatched) {
|
2009-09-04 13:06:43 +00:00
|
|
|
LYXERR(Debug::FIND, "Found " << open_pars
|
|
|
|
<< " instead of " << unmatched
|
|
|
|
<< " unmatched open braces at the end of count");
|
2008-11-15 23:30:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Braces match as expected");
|
2008-11-15 23:30:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The class performing a match between a position in the document and the FindAdvOptions.
|
|
|
|
**/
|
|
|
|
class MatchStringAdv {
|
|
|
|
public:
|
2009-12-30 18:40:18 +00:00
|
|
|
MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const & opt);
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
/** 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.
|
|
|
|
**
|
2008-12-20 16:00:47 +00:00
|
|
|
** @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.
|
2009-05-07 12:40:35 +00:00
|
|
|
**
|
2008-11-15 23:30:27 +00:00
|
|
|
** @return
|
|
|
|
** The length of the matching text, or zero if no match was found.
|
|
|
|
**/
|
2008-12-20 16:00:47 +00:00
|
|
|
int operator()(DocIterator const & cur, int len = -1, bool at_begin = true) const;
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
/// buffer
|
2009-12-30 18:40:18 +00:00
|
|
|
lyx::Buffer * p_buf;
|
|
|
|
/// first buffer on which search was started
|
|
|
|
lyx::Buffer * const p_first_buf;
|
2008-11-15 23:30:27 +00:00
|
|
|
/// options
|
2009-01-14 15:34:56 +00:00
|
|
|
FindAndReplaceOptions const & opt;
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
private:
|
2010-01-09 12:39:29 +00:00
|
|
|
/// Auxiliary find method (does not account for opt.matchword)
|
|
|
|
int findAux(DocIterator const & cur, int len = -1, bool at_begin = true) const;
|
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
/** Normalize a stringified or latexified LyX paragraph.
|
|
|
|
**
|
|
|
|
** Normalize means:
|
|
|
|
** <ul>
|
|
|
|
** <li>if search is not casesensitive, then lowercase the string;
|
|
|
|
** <li>remove any newline at begin or end of the string;
|
|
|
|
** <li>replace any newline in the middle of the string with a simple space;
|
|
|
|
** <li>remove stale empty styles and environments, like \emph{} and \textbf{}.
|
|
|
|
** </ul>
|
|
|
|
**
|
|
|
|
** @todo Normalization should also expand macros, if the corresponding
|
|
|
|
** search option was checked.
|
|
|
|
**/
|
|
|
|
string normalize(docstring const & s) const;
|
|
|
|
// normalized string to search
|
|
|
|
string par_as_string;
|
|
|
|
// regular expression to use for searching
|
2010-06-29 17:09:40 +00:00
|
|
|
lyx::regex regexp;
|
2008-12-20 16:00:47 +00:00
|
|
|
// same as regexp, but prefixed with a ".*"
|
2010-06-29 17:09:40 +00:00
|
|
|
lyx::regex regexp2;
|
2008-11-15 23:30:27 +00:00
|
|
|
// unmatched open braces in the search string/regexp
|
|
|
|
int open_braces;
|
|
|
|
// number of (.*?) subexpressions added at end of search regexp for closing
|
|
|
|
// environments, math mode, styles, etc...
|
|
|
|
int close_wildcards;
|
2011-02-07 20:36:40 +00:00
|
|
|
// Are we searching with regular expressions ?
|
|
|
|
bool use_regexp;
|
2008-11-15 23:30:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
static docstring buffer_to_latex(Buffer & buffer)
|
|
|
|
{
|
|
|
|
OutputParams runparams(&buffer.params().encoding());
|
|
|
|
odocstringstream ods;
|
|
|
|
otexstream os(ods);
|
|
|
|
runparams.nice = true;
|
|
|
|
runparams.flavor = OutputParams::LATEX;
|
|
|
|
runparams.linelen = 80; //lyxrc.plaintext_linelen;
|
|
|
|
// No side effect of file copying and image conversion
|
|
|
|
runparams.dryrun = true;
|
|
|
|
buffer.texrow().reset();
|
|
|
|
pit_type const endpit = buffer.paragraphs().size();
|
|
|
|
for (pit_type pit = 0; pit != endpit; ++pit) {
|
|
|
|
TeXOnePar(buffer, buffer.text(),
|
|
|
|
pit, os, buffer.texrow(), runparams);
|
|
|
|
LYXERR(Debug::FIND, "searchString up to here: " << ods.str());
|
|
|
|
}
|
|
|
|
return ods.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static docstring stringifySearchBuffer(Buffer & buffer, FindAndReplaceOptions const & opt) {
|
|
|
|
docstring str;
|
|
|
|
if (!opt.ignoreformat) {
|
|
|
|
str = buffer_to_latex(buffer);
|
|
|
|
} else {
|
|
|
|
ParIterator it = buffer.par_iterator_begin();
|
|
|
|
ParIterator end = buffer.par_iterator_end();
|
|
|
|
OutputParams runparams(&buffer.params().encoding());
|
|
|
|
odocstringstream os;
|
|
|
|
runparams.nice = true;
|
|
|
|
runparams.flavor = OutputParams::LATEX;
|
|
|
|
runparams.linelen = 100000; //lyxrc.plaintext_linelen;
|
|
|
|
runparams.dryrun = true;
|
|
|
|
for (; it != end; ++it) {
|
|
|
|
LYXERR(Debug::FIND, "Adding to search string: '"
|
|
|
|
<< it->asString(false)
|
|
|
|
<< "'");
|
|
|
|
str +=
|
|
|
|
it->stringify(pos_type(0), it->size(),
|
|
|
|
AS_STR_INSETS, runparams);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Return separation pos between the leading material and the rest
|
|
|
|
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 ", ""))
|
|
|
|
LYXERR(Debug::FIND, " after removing leading $, \\[ , \\emph{, \\textbf{, etc.: " << t);
|
|
|
|
return s.find(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-30 18:40:18 +00:00
|
|
|
MatchStringAdv::MatchStringAdv(lyx::Buffer & buf, FindAndReplaceOptions const & opt)
|
|
|
|
: p_buf(&buf), p_first_buf(&buf), opt(opt)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2011-02-07 20:36:40 +00:00
|
|
|
Buffer & find_buf = *theBufferList().getBuffer(FileName(to_utf8(opt.find_buf_name)), true);
|
|
|
|
par_as_string = normalize(stringifySearchBuffer(find_buf, opt));
|
2008-11-15 23:30:27 +00:00
|
|
|
open_braces = 0;
|
|
|
|
close_wildcards = 0;
|
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
use_regexp = !opt.ignoreformat || par_as_string.find("\\regexp") != std::string::npos;
|
|
|
|
|
|
|
|
if (!use_regexp) {
|
2008-11-15 23:30:27 +00:00
|
|
|
// Remove trailing closure of math, macros and environments, so to catch parts of them.
|
|
|
|
do {
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
if (regex_replace(par_as_string, par_as_string, "(.*)[[:blank:]]\\'", "$1"))
|
|
|
|
continue;
|
|
|
|
if (regex_replace(par_as_string, par_as_string, "(.*[^\\\\]) ?\\$\\'", "$1"))
|
|
|
|
continue;
|
|
|
|
// @todo need to account for open square braces as well ?
|
|
|
|
if (regex_replace(par_as_string, par_as_string, "(.*[^\\\\]) ?\\\\\\]\\'", "$1"))
|
|
|
|
continue;
|
|
|
|
if (regex_replace(par_as_string, par_as_string, "(.*[^\\\\]) ?\\\\end\\{[a-zA-Z_]*\\}\\'", "$1"))
|
|
|
|
continue;
|
|
|
|
if (regex_replace(par_as_string, par_as_string, "(.*[^\\\\]) ?\\}\\'", "$1")) {
|
|
|
|
++open_braces;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} while (true);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Open braces: " << open_braces);
|
|
|
|
LYXERR(Debug::FIND, "Built MatchStringAdv object: par_as_string = '" << par_as_string << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
} else {
|
2011-02-07 20:36:40 +00:00
|
|
|
size_t lead_size = identifyLeading(par_as_string);
|
|
|
|
string lead_as_regexp;
|
|
|
|
if (lead_size > 0) {
|
|
|
|
lead_as_regexp = escape_for_regex(par_as_string.substr(0, lead_size));
|
|
|
|
par_as_string = par_as_string.substr(lead_size, par_as_string.size() - lead_size);
|
|
|
|
LYXERR(Debug::FIND, "lead_as_regexp is '" << lead_as_regexp << "'");
|
|
|
|
LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
|
|
|
|
}
|
2008-11-15 23:30:27 +00:00
|
|
|
par_as_string = escape_for_regex(par_as_string);
|
|
|
|
// Insert (.*?) before trailing closure of math, macros and environments, so to catch parts of them.
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
if (
|
|
|
|
// Insert .* before trailing '\$' ('$' has been escaped by escape_for_regex)
|
|
|
|
regex_replace(par_as_string, par_as_string, "(.*[^\\\\])(\\\\\\$)\\'", "$1(.*?)$2")
|
|
|
|
// Insert .* before trailing '\\\]' ('\]' has been escaped by escape_for_regex)
|
|
|
|
|| regex_replace(par_as_string, par_as_string, "(.*[^\\\\])(\\\\\\\\\\\\\\])\\'", "$1(.*?)$2")
|
|
|
|
// Insert .* before trailing '\\end\{...}' ('\end{...}' has been escaped by escape_for_regex)
|
2009-09-04 13:06:43 +00:00
|
|
|
|| regex_replace(par_as_string, par_as_string,
|
|
|
|
"(.*[^\\\\])(\\\\\\\\end\\\\\\{[a-zA-Z_]*\\\\\\})\\'", "$1(.*?)$2")
|
2008-11-15 23:30:27 +00:00
|
|
|
// Insert .* before trailing '\}' ('}' has been escaped by escape_for_regex)
|
|
|
|
|| regex_replace(par_as_string, par_as_string, "(.*[^\\\\])(\\\\\\})\\'", "$1(.*?)$2")
|
|
|
|
) {
|
|
|
|
++close_wildcards;
|
|
|
|
}
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "par_as_string now is '" << par_as_string << "'");
|
|
|
|
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);
|
2009-08-17 07:06:01 +00:00
|
|
|
// If entered regexp must match at begin of searched string buffer
|
2011-02-07 20:36:40 +00:00
|
|
|
string regexp_str = string("\\`") + lead_as_regexp + par_as_string;
|
|
|
|
LYXERR(Debug::FIND, "Setting regexp to : " << regexp_str << endl);
|
|
|
|
regexp = lyx::regex(regexp_str);
|
|
|
|
|
2009-08-17 07:06:01 +00:00
|
|
|
// If entered regexp may match wherever in searched string buffer
|
2011-02-07 20:36:40 +00:00
|
|
|
string regexp2_str = string("\\`.*") + lead_as_regexp + ".*" + par_as_string;
|
|
|
|
LYXERR(Debug::FIND, "Setting regexp2 to: " << regexp2_str << endl);
|
|
|
|
regexp2 = lyx::regex(regexp2_str);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-20 16:00:47 +00:00
|
|
|
|
2010-01-09 12:39:29 +00:00
|
|
|
int MatchStringAdv::findAux(DocIterator const & cur, int len, bool at_begin) const
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2008-12-20 16:00:47 +00:00
|
|
|
docstring docstr = stringifyFromForSearch(opt, cur, len);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Matching against '" << lyx::to_utf8(docstr) << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
string str = normalize(docstr);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "After normalization: '" << str << "'");
|
2011-02-07 20:36:40 +00:00
|
|
|
if (! use_regexp) {
|
2010-11-20 11:03:24 +00:00
|
|
|
LYXERR(Debug::FIND, "Searching in normal mode: par_as_string='" << par_as_string << "', str='" << str << "'");
|
2008-12-20 16:00:47 +00:00
|
|
|
if (at_begin) {
|
2010-11-20 11:03:24 +00:00
|
|
|
LYXERR(Debug::FIND, "size=" << par_as_string.size() << ", substr='" << str.substr(0, par_as_string.size()) << "'");
|
2008-12-20 16:00:47 +00:00
|
|
|
if (str.substr(0, par_as_string.size()) == par_as_string)
|
|
|
|
return par_as_string.size();
|
|
|
|
} else {
|
2010-11-20 15:57:54 +00:00
|
|
|
string t = par_as_string;
|
|
|
|
while (regex_replace(t, t, "\\\\(emph|textbf|subsubsection|subsection|section|subparagraph|paragraph|part)\\{", "")
|
|
|
|
|| regex_replace(t, t, "^\\$", "")
|
|
|
|
|| regex_replace(t, t, "^\\\\\\[ ", ""))
|
2010-11-20 18:28:28 +00:00
|
|
|
LYXERR(Debug::FIND, " after removing leading $, \\[ , \\emph{, \\textbf{, etc.: " << t);
|
2010-11-20 15:57:54 +00:00
|
|
|
size_t pos = str.find(t);
|
2008-12-20 16:00:47 +00:00
|
|
|
if (pos != string::npos)
|
|
|
|
return par_as_string.size();
|
|
|
|
}
|
2008-11-15 23:30:27 +00:00
|
|
|
} else {
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND, "Searching in regexp mode: at_begin=" << at_begin);
|
2009-09-04 13:06:43 +00:00
|
|
|
// Try all possible regexp matches,
|
|
|
|
//until one that verifies the braces match test is found
|
2010-06-29 17:09:40 +00:00
|
|
|
regex const *p_regexp = at_begin ? ®exp : ®exp2;
|
|
|
|
sregex_iterator re_it(str.begin(), str.end(), *p_regexp);
|
|
|
|
sregex_iterator re_it_end;
|
2008-11-15 23:30:27 +00:00
|
|
|
for (; re_it != re_it_end; ++re_it) {
|
2010-06-29 17:09:40 +00:00
|
|
|
match_results<string::const_iterator> const & m = *re_it;
|
2008-11-15 23:30:27 +00:00
|
|
|
// Check braces on the segment that matched the entire regexp expression,
|
|
|
|
// plus the last subexpression, if a (.*?) was inserted in the constructor.
|
2010-11-20 18:28:28 +00:00
|
|
|
if (!braces_match(m[0].first, m[0].second, open_braces))
|
2008-11-15 23:30:27 +00:00
|
|
|
return 0;
|
|
|
|
// Check braces on segments that matched all (.*?) subexpressions.
|
|
|
|
for (size_t i = 1; i < m.size(); ++i)
|
2010-11-20 18:28:28 +00:00
|
|
|
if (!braces_match(m[i].first, m[i].second))
|
2008-11-15 23:30:27 +00:00
|
|
|
return false;
|
2009-09-04 13:06:43 +00:00
|
|
|
// Exclude from the returned match length any length
|
|
|
|
// due to close wildcards added at end of regexp
|
2008-11-15 23:30:27 +00:00
|
|
|
if (close_wildcards == 0)
|
|
|
|
return m[0].second - m[0].first;
|
|
|
|
else
|
|
|
|
return m[m.size() - close_wildcards].first - m[0].first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-09 12:39:29 +00:00
|
|
|
int MatchStringAdv::operator()(DocIterator const & cur, int len, bool at_begin) const
|
|
|
|
{
|
|
|
|
int res = findAux(cur, len, at_begin);
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND,
|
|
|
|
"res=" << res << ", at_begin=" << at_begin << ", matchword=" << opt.matchword << ", inTexted=" << cur.inTexted());
|
2010-01-09 12:39:29 +00:00
|
|
|
if (res == 0 || !at_begin || !opt.matchword || !cur.inTexted())
|
|
|
|
return res;
|
|
|
|
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) : true;
|
|
|
|
LYXERR(Debug::FIND,
|
|
|
|
"cur.pos()=" << cur.pos() << ", res=" << res
|
|
|
|
<< ", separ: " << ws_left << ", " << ws_right
|
|
|
|
<< endl);
|
|
|
|
if (ws_left && ws_right)
|
|
|
|
return res;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
string MatchStringAdv::normalize(docstring const & s) const
|
|
|
|
{
|
|
|
|
string t;
|
|
|
|
if (! opt.casesensitive)
|
|
|
|
t = lyx::to_utf8(lowercase(s));
|
|
|
|
else
|
|
|
|
t = lyx::to_utf8(s);
|
|
|
|
// Remove \n at begin
|
|
|
|
while (t.size() > 0 && t[0] == '\n')
|
|
|
|
t = t.substr(1);
|
|
|
|
// Remove \n at end
|
|
|
|
while (t.size() > 0 && t[t.size() - 1] == '\n')
|
|
|
|
t = t.substr(0, t.size() - 1);
|
|
|
|
size_t pos;
|
|
|
|
// Replace all other \n with spaces
|
|
|
|
while ((pos = t.find("\n")) != string::npos)
|
|
|
|
t.replace(pos, 1, " ");
|
|
|
|
// Remove stale empty \emph{}, \textbf{} and similar blocks from latexify
|
2009-08-22 16:06:19 +00:00
|
|
|
LYXERR(Debug::FIND, "Removing stale empty \\emph{}, \\textbf{}, \\*section{} macros from: " << t);
|
2010-11-20 15:57:54 +00:00
|
|
|
while (regex_replace(t, t, "\\\\(emph|textbf|subsubsection|subsection|section|subparagraph|paragraph|part)(\\{\\})+", ""))
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, " further removing stale empty \\emph{}, \\textbf{} macros from: " << t);
|
2008-11-15 23:30:27 +00:00
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
docstring stringifyFromCursor(DocIterator const & cur, int len)
|
|
|
|
{
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Stringifying with len=" << len << " from cursor at pos: " << cur);
|
2008-11-15 23:30:27 +00:00
|
|
|
if (cur.inTexted()) {
|
|
|
|
Paragraph const & par = cur.paragraph();
|
|
|
|
// TODO what about searching beyond/across paragraph breaks ?
|
|
|
|
// TODO Try adding a AS_STR_INSERTS as last arg
|
2009-09-04 13:06:43 +00:00
|
|
|
pos_type end = ( len == -1 || cur.pos() + len > int(par.size()) ) ?
|
|
|
|
int(par.size()) : cur.pos() + len;
|
2008-12-20 16:00:47 +00:00
|
|
|
OutputParams runparams(&cur.buffer()->params().encoding());
|
|
|
|
odocstringstream os;
|
|
|
|
runparams.nice = true;
|
|
|
|
runparams.flavor = OutputParams::LATEX;
|
|
|
|
runparams.linelen = 100000; //lyxrc.plaintext_linelen;
|
|
|
|
// No side effect of file copying and image conversion
|
|
|
|
runparams.dryrun = true;
|
2009-09-04 13:06:43 +00:00
|
|
|
LYXERR(Debug::FIND, "Stringifying with cur: "
|
2009-09-04 13:52:49 +00:00
|
|
|
<< cur << ", from pos: " << cur.pos() << ", end: " << end);
|
2008-12-20 16:00:47 +00:00
|
|
|
return par.stringify(cur.pos(), end, AS_STR_INSETS, runparams);
|
2008-11-15 23:30:27 +00:00
|
|
|
} else if (cur.inMathed()) {
|
|
|
|
odocstringstream os;
|
|
|
|
CursorSlice cs = cur.top();
|
|
|
|
MathData md = cs.cell();
|
2009-09-04 13:06:43 +00:00
|
|
|
MathData::const_iterator it_end =
|
|
|
|
( ( len == -1 || cs.pos() + len > int(md.size()) )
|
|
|
|
? md.end() : md.begin() + cs.pos() + len );
|
2008-11-15 23:30:27 +00:00
|
|
|
for (MathData::const_iterator it = md.begin() + cs.pos(); it != it_end; ++it)
|
|
|
|
os << *it;
|
|
|
|
return os.str();
|
|
|
|
}
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Don't know how to stringify from here: " << cur);
|
2008-11-15 23:30:27 +00:00
|
|
|
return docstring();
|
|
|
|
}
|
|
|
|
|
2009-08-19 15:14:28 +00:00
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
/** Computes the LaTeX export of buf starting from cur and ending len positions
|
|
|
|
* after cur, if len is positive, or at the paragraph or innermost inset end
|
|
|
|
* if len is -1.
|
|
|
|
*/
|
2008-12-20 16:00:47 +00:00
|
|
|
docstring latexifyFromCursor(DocIterator const & cur, int len)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Latexifying with len=" << len << " from cursor at pos: " << cur);
|
|
|
|
LYXERR(Debug::FIND, " with cur.lastpost=" << cur.lastpos() << ", cur.lastrow="
|
2008-11-15 23:30:27 +00:00
|
|
|
<< cur.lastrow() << ", cur.lastcol=" << cur.lastcol());
|
2008-12-20 16:00:47 +00:00
|
|
|
Buffer const & buf = *cur.buffer();
|
|
|
|
LASSERT(buf.isLatex(), /* */);
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
TexRow texrow;
|
|
|
|
odocstringstream ods;
|
Introduce a wrapper class for odocstream to help ensuring that no
blank lines may be inadvertently output. This is achieved by using two
special iomanip-like variables (breakln and safebreakln) in the lyx::
namespace. When they are inserted in the stream, a newline is output
only if not already at the beginning of a line. The difference between
breakln and safebreakln is that, if needed, the former outputs '\n'
and the latter "%\n".
In future, the new class will also be used for counting the number of
newlines issued. Even if the infractrure for doing that is already in
place, the counting is essentially still done the old way.
There are still places in the code where the functionality of the
class could be used, most probably. ATM, it is used for InsetTabular,
InsetListings, InsetFloat, and InsetText.
The Comment and GreyedOut insets required a special treatment and a
new InsetLayout parameter (Display) has been introduced. The default
for Display is "true", meaning that the corresponding latex
environment is of "display" type, i.e., it stands on its own, whereas
"false" means that the contents appear inline with the text. The
latter is the case for both Comment and GreyedOut insets.
Mostly, the only visible effects on latex exports should be the
disappearing of some redundant % chars and the appearing/disappearing
of null {} latex groups after a comment or lyxgreyedout environments
(they are related to the presence or absence of a space immediately
after those environments), as well as the fact that math environments
are now started on their own lines.
As a last thing, only the latex code between \begin{document} and
\end{document} goes through the new class, the preamble being directly
output through odocstream, as usual.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37360 a592a061-630c-0410-9148-cb99ea01b6c8
2011-01-29 02:41:13 +00:00
|
|
|
otexstream os(ods);
|
2008-11-15 23:30:27 +00:00
|
|
|
OutputParams runparams(&buf.params().encoding());
|
|
|
|
runparams.nice = false;
|
|
|
|
runparams.flavor = OutputParams::LATEX;
|
|
|
|
runparams.linelen = 8000; //lyxrc.plaintext_linelen;
|
|
|
|
// No side effect of file copying and image conversion
|
|
|
|
runparams.dryrun = true;
|
|
|
|
|
|
|
|
if (cur.inTexted()) {
|
2010-12-18 15:57:27 +00:00
|
|
|
// @TODO what about searching beyond/across paragraph breaks ?
|
|
|
|
pos_type endpos = cur.paragraph().size();
|
|
|
|
if (len != -1 && endpos > cur.pos() + len)
|
|
|
|
endpos = cur.pos() + len;
|
Introduce a wrapper class for odocstream to help ensuring that no
blank lines may be inadvertently output. This is achieved by using two
special iomanip-like variables (breakln and safebreakln) in the lyx::
namespace. When they are inserted in the stream, a newline is output
only if not already at the beginning of a line. The difference between
breakln and safebreakln is that, if needed, the former outputs '\n'
and the latter "%\n".
In future, the new class will also be used for counting the number of
newlines issued. Even if the infractrure for doing that is already in
place, the counting is essentially still done the old way.
There are still places in the code where the functionality of the
class could be used, most probably. ATM, it is used for InsetTabular,
InsetListings, InsetFloat, and InsetText.
The Comment and GreyedOut insets required a special treatment and a
new InsetLayout parameter (Display) has been introduced. The default
for Display is "true", meaning that the corresponding latex
environment is of "display" type, i.e., it stands on its own, whereas
"false" means that the contents appear inline with the text. The
latter is the case for both Comment and GreyedOut insets.
Mostly, the only visible effects on latex exports should be the
disappearing of some redundant % chars and the appearing/disappearing
of null {} latex groups after a comment or lyxgreyedout environments
(they are related to the presence or absence of a space immediately
after those environments), as well as the fact that math environments
are now started on their own lines.
As a last thing, only the latex code between \begin{document} and
\end{document} goes through the new class, the preamble being directly
output through odocstream, as usual.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37360 a592a061-630c-0410-9148-cb99ea01b6c8
2011-01-29 02:41:13 +00:00
|
|
|
TeXOnePar(buf, *cur.innerText(), cur.pit(), os, texrow, runparams,
|
2010-12-18 15:57:27 +00:00
|
|
|
string(), cur.pos(), endpos);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Latexified text: '" << lyx::to_utf8(ods.str()) << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
} else if (cur.inMathed()) {
|
|
|
|
// Retrieve the math environment type, and add '$' or '$[' or others (\begin{equation}) accordingly
|
|
|
|
for (int s = cur.depth() - 1; s >= 0; --s) {
|
|
|
|
CursorSlice const & cs = cur[s];
|
|
|
|
if (cs.asInsetMath() && cs.asInsetMath() && cs.asInsetMath()->asHullInset()) {
|
|
|
|
WriteStream ws(ods);
|
|
|
|
cs.asInsetMath()->asHullInset()->header_write(ws);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CursorSlice const & cs = cur.top();
|
|
|
|
MathData md = cs.cell();
|
|
|
|
MathData::const_iterator it_end = ( ( len == -1 || cs.pos() + len > int(md.size()) )
|
|
|
|
? md.end() : md.begin() + cs.pos() + len );
|
|
|
|
for (MathData::const_iterator it = md.begin() + cs.pos(); it != it_end; ++it)
|
|
|
|
ods << *it;
|
|
|
|
|
2008-11-17 11:46:07 +00:00
|
|
|
// Retrieve the math environment type, and add '$' or '$]'
|
|
|
|
// or others (\end{equation}) accordingly
|
2008-11-15 23:30:27 +00:00
|
|
|
for (int s = cur.depth() - 1; s >= 0; --s) {
|
|
|
|
CursorSlice const & cs = cur[s];
|
2008-11-17 11:46:07 +00:00
|
|
|
InsetMath * inset = cs.asInsetMath();
|
|
|
|
if (inset && inset->asHullInset()) {
|
|
|
|
WriteStream ws(ods);
|
|
|
|
inset->asHullInset()->footer_write(ws);
|
|
|
|
break;
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
}
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Latexified math: '" << lyx::to_utf8(ods.str()) << "'");
|
2008-11-15 23:30:27 +00:00
|
|
|
} else {
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Don't know how to stringify from here: " << cur);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
return ods.str();
|
|
|
|
}
|
|
|
|
|
2009-12-30 18:40:18 +00:00
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
/** Finalize an advanced find operation, advancing the cursor to the innermost
|
|
|
|
** position that matches, plus computing the length of the matching text to
|
|
|
|
** be selected
|
|
|
|
**/
|
|
|
|
int findAdvFinalize(DocIterator & cur, MatchStringAdv const & match)
|
|
|
|
{
|
2008-11-17 11:46:07 +00:00
|
|
|
// Search the foremost position that matches (avoids find of entire math
|
|
|
|
// inset when match at start of it)
|
2008-11-15 23:30:27 +00:00
|
|
|
size_t d;
|
2008-11-17 11:46:07 +00:00
|
|
|
DocIterator old_cur(cur.buffer());
|
2008-11-15 23:30:27 +00:00
|
|
|
do {
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Forwarding one step (searching for innermost match)");
|
2008-11-15 23:30:27 +00:00
|
|
|
d = cur.depth();
|
|
|
|
old_cur = cur;
|
|
|
|
cur.forwardPos();
|
|
|
|
} while (cur && cur.depth() > d && match(cur) > 0);
|
|
|
|
cur = old_cur;
|
2008-12-20 16:00:47 +00:00
|
|
|
LASSERT(match(cur) > 0, /* */);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "Ok");
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
// Compute the match length
|
|
|
|
int len = 1;
|
2010-02-02 20:36:12 +00:00
|
|
|
if (cur.pos() + len > cur.lastpos())
|
|
|
|
return 0;
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
|
2008-11-15 23:30:27 +00:00
|
|
|
while (cur.pos() + len <= cur.lastpos() && match(cur, len) == 0) {
|
|
|
|
++len;
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "verifying unmatch with len = " << len);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
2008-11-17 11:46:07 +00:00
|
|
|
// Length of matched text (different from len param)
|
|
|
|
int old_len = match(cur, len);
|
2008-11-15 23:30:27 +00:00
|
|
|
int new_len;
|
|
|
|
// Greedy behaviour while matching regexps
|
|
|
|
while ((new_len = match(cur, len + 1)) > old_len) {
|
|
|
|
++len;
|
|
|
|
old_len = new_len;
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "verifying match with len = " << len);
|
2008-11-15 23:30:27 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2008-12-20 16:00:47 +00:00
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
/// Finds forward
|
2009-12-30 18:40:18 +00:00
|
|
|
int findForwardAdv(DocIterator & cur, MatchStringAdv & match)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2008-11-17 11:46:07 +00:00
|
|
|
if (!cur)
|
2008-11-15 23:30:27 +00:00
|
|
|
return 0;
|
2011-02-08 09:11:58 +00:00
|
|
|
while (cur) {
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND, "findForwardAdv() cur: " << cur);
|
|
|
|
if (match(cur, -1, false)) {
|
2011-02-08 09:11:58 +00:00
|
|
|
for (; cur; cur.forwardPos()) {
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND, "Advancing cur: " << cur);
|
|
|
|
if (match(cur)) {
|
|
|
|
// Sometimes in finalize we understand it wasn't a match
|
|
|
|
// and we need to continue the outest loop
|
|
|
|
int len = findAdvFinalize(cur, match);
|
|
|
|
if (len > 0)
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cur)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (cur.pit() < cur.lastpit()) {
|
|
|
|
LYXERR(Debug::FIND, "Advancing par: cur=" << cur);
|
2010-01-04 12:29:38 +00:00
|
|
|
cur.forwardPar();
|
2011-02-07 20:36:40 +00:00
|
|
|
} else {
|
|
|
|
// This should exit nested insets, if any, or otherwise undefine the currsor.
|
|
|
|
cur.pos() = cur.lastpos();
|
|
|
|
LYXERR(Debug::FIND, "Advancing pos: cur=" << cur);
|
2010-01-04 12:29:38 +00:00
|
|
|
cur.forwardPos();
|
2008-12-20 16:00:47 +00:00
|
|
|
}
|
2010-01-04 12:29:38 +00:00
|
|
|
}
|
2008-11-15 23:30:27 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-30 21:50:55 +00:00
|
|
|
|
2009-08-17 08:08:21 +00:00
|
|
|
/// Find the most backward consecutive match within same paragraph while searching backwards.
|
2010-05-02 22:33:36 +00:00
|
|
|
int findMostBackwards(DocIterator & cur, MatchStringAdv const & match)
|
2010-01-04 12:29:38 +00:00
|
|
|
{
|
2009-08-17 08:08:21 +00:00
|
|
|
DocIterator cur_begin = doc_iterator_begin(cur.buffer());
|
2010-03-20 13:59:46 +00:00
|
|
|
DocIterator tmp_cur = cur;
|
2010-05-02 22:33:36 +00:00
|
|
|
int len = findAdvFinalize(tmp_cur, match);
|
2010-03-20 13:59:46 +00:00
|
|
|
Inset & inset = cur.inset();
|
|
|
|
for (; cur != cur_begin; cur.backwardPos()) {
|
|
|
|
LYXERR(Debug::FIND, "findMostBackwards(): cur=" << cur);
|
|
|
|
DocIterator new_cur = cur;
|
|
|
|
new_cur.backwardPos();
|
|
|
|
if (new_cur == cur || &new_cur.inset() != &inset || !match(new_cur))
|
|
|
|
break;
|
|
|
|
int new_len = findAdvFinalize(new_cur, match);
|
|
|
|
if (new_len == len)
|
|
|
|
break;
|
|
|
|
len = new_len;
|
2009-08-17 08:08:21 +00:00
|
|
|
}
|
2010-03-20 13:59:46 +00:00
|
|
|
LYXERR(Debug::FIND, "findMostBackwards(): exiting with cur=" << cur);
|
2010-05-02 22:33:36 +00:00
|
|
|
return len;
|
2009-08-17 08:08:21 +00:00
|
|
|
}
|
2008-12-20 16:00:47 +00:00
|
|
|
|
2010-01-04 12:29:38 +00:00
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
/// Finds backwards
|
2009-12-30 18:40:18 +00:00
|
|
|
int findBackwardsAdv(DocIterator & cur, MatchStringAdv & match) {
|
2009-08-17 08:08:21 +00:00
|
|
|
if (! cur)
|
|
|
|
return 0;
|
2010-01-04 12:29:38 +00:00
|
|
|
// Backup of original position
|
2009-08-17 08:08:21 +00:00
|
|
|
DocIterator cur_begin = doc_iterator_begin(cur.buffer());
|
2010-01-04 12:29:38 +00:00
|
|
|
if (cur == cur_begin)
|
|
|
|
return 0;
|
2010-03-20 13:59:46 +00:00
|
|
|
cur.backwardPos();
|
|
|
|
DocIterator cur_orig(cur);
|
2009-08-17 08:08:21 +00:00
|
|
|
bool found_match;
|
2010-01-04 12:29:38 +00:00
|
|
|
bool pit_changed = false;
|
|
|
|
found_match = false;
|
2008-12-20 16:00:47 +00:00
|
|
|
do {
|
2009-08-17 08:08:21 +00:00
|
|
|
cur.pos() = 0;
|
|
|
|
found_match = match(cur, -1, false);
|
2010-01-04 12:29:38 +00:00
|
|
|
|
2009-08-17 08:08:21 +00:00
|
|
|
if (found_match) {
|
2010-01-04 12:29:38 +00:00
|
|
|
if (pit_changed)
|
|
|
|
cur.pos() = cur.lastpos();
|
|
|
|
else
|
|
|
|
cur.pos() = cur_orig.pos();
|
|
|
|
LYXERR(Debug::FIND, "findBackAdv2: cur: " << cur);
|
|
|
|
DocIterator cur_prev_iter;
|
2010-03-20 13:59:46 +00:00
|
|
|
do {
|
2010-01-04 12:29:38 +00:00
|
|
|
found_match = match(cur);
|
2009-09-04 13:06:43 +00:00
|
|
|
LYXERR(Debug::FIND, "findBackAdv3: found_match="
|
2010-01-04 12:29:38 +00:00
|
|
|
<< found_match << ", cur: " << cur);
|
2010-05-02 22:33:36 +00:00
|
|
|
if (found_match)
|
|
|
|
return findMostBackwards(cur, match);
|
|
|
|
|
2010-03-20 13:59:46 +00:00
|
|
|
// Stop if begin of document reached
|
|
|
|
if (cur == cur_begin)
|
2009-08-17 08:08:21 +00:00
|
|
|
break;
|
2009-12-26 22:10:14 +00:00
|
|
|
cur_prev_iter = cur;
|
2009-08-17 08:08:21 +00:00
|
|
|
cur.backwardPos();
|
2010-03-20 13:59:46 +00:00
|
|
|
} while (true);
|
2008-12-20 16:00:47 +00:00
|
|
|
}
|
2010-01-04 12:29:38 +00:00
|
|
|
if (cur == cur_begin)
|
2009-12-30 22:21:23 +00:00
|
|
|
break;
|
2010-01-04 12:29:38 +00:00
|
|
|
if (cur.pit() > 0)
|
|
|
|
--cur.pit();
|
|
|
|
else
|
|
|
|
cur.backwardPos();
|
|
|
|
pit_changed = true;
|
2011-02-08 09:11:58 +00:00
|
|
|
} while (true);
|
2008-11-15 23:30:27 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-30 18:40:18 +00:00
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
} // anonym namespace
|
|
|
|
|
|
|
|
|
2009-01-14 15:34:56 +00:00
|
|
|
docstring stringifyFromForSearch(FindAndReplaceOptions const & opt,
|
2008-11-15 23:30:27 +00:00
|
|
|
DocIterator const & cur, int len)
|
|
|
|
{
|
2011-01-26 23:06:02 +00:00
|
|
|
LASSERT(cur.pos() >= 0 && cur.pos() <= cur.lastpos(), /* */);
|
2008-11-17 11:46:07 +00:00
|
|
|
if (!opt.ignoreformat)
|
2008-12-20 16:00:47 +00:00
|
|
|
return latexifyFromCursor(cur, len);
|
2008-11-15 23:30:27 +00:00
|
|
|
else
|
|
|
|
return stringifyFromCursor(cur, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
FindAndReplaceOptions::FindAndReplaceOptions(docstring const & find_buf_name, bool casesensitive,
|
2008-11-15 23:30:27 +00:00
|
|
|
bool matchword, bool forward, bool expandmacros, bool ignoreformat,
|
2011-02-07 20:36:40 +00:00
|
|
|
docstring const & repl_buf_name, bool keep_case,
|
2009-12-30 18:40:18 +00:00
|
|
|
SearchScope scope)
|
2011-02-07 20:36:40 +00:00
|
|
|
: find_buf_name(find_buf_name), casesensitive(casesensitive), matchword(matchword),
|
2009-08-20 00:36:52 +00:00
|
|
|
forward(forward), expandmacros(expandmacros), ignoreformat(ignoreformat),
|
2011-02-07 20:36:40 +00:00
|
|
|
repl_buf_name(repl_buf_name), keep_case(keep_case), scope(scope)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-08-19 22:55:38 +00:00
|
|
|
|
2010-10-13 18:30:37 +00:00
|
|
|
namespace {
|
2009-08-19 22:55:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
/** Check if 'len' letters following cursor are all non-lowercase */
|
|
|
|
static bool allNonLowercase(DocIterator const & cur, int len) {
|
|
|
|
pos_type end_pos = cur.pos() + len;
|
|
|
|
for (pos_type pos = cur.pos(); pos != end_pos; ++pos)
|
|
|
|
if (isLowerCase(cur.paragraph().getChar(pos)))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Check if first letter is upper case and second one is lower case */
|
|
|
|
static bool firstUppercase(DocIterator const & cur) {
|
|
|
|
char_type ch1, ch2;
|
|
|
|
if (cur.pos() >= cur.lastpos() - 1) {
|
|
|
|
LYXERR(Debug::FIND, "No upper-case at cur: " << cur);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ch1 = cur.paragraph().getChar(cur.pos());
|
|
|
|
ch2 = cur.paragraph().getChar(cur.pos()+1);
|
|
|
|
bool result = isUpperCase(ch1) && isLowerCase(ch2);
|
|
|
|
LYXERR(Debug::FIND, "firstUppercase(): "
|
2009-09-04 13:06:43 +00:00
|
|
|
<< "ch1=" << ch1 << "(" << char(ch1) << "), ch2="
|
|
|
|
<< ch2 << "(" << char(ch2) << ")"
|
2009-08-19 22:55:38 +00:00
|
|
|
<< ", result=" << result << ", cur=" << cur);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Make first letter of supplied buffer upper-case, and the rest lower-case.
|
|
|
|
**
|
|
|
|
** \fixme What to do with possible further paragraphs in replace buffer ?
|
|
|
|
**/
|
|
|
|
static void changeFirstCase(Buffer & buffer, TextCase first_case, TextCase others_case) {
|
|
|
|
ParagraphList::iterator pit = buffer.paragraphs().begin();
|
|
|
|
pos_type right = pos_type(1);
|
|
|
|
pit->changeCase(buffer.params(), pos_type(0), right, first_case);
|
|
|
|
right = pit->size() + 1;
|
|
|
|
pit->changeCase(buffer.params(), right, right, others_case);
|
|
|
|
}
|
2010-10-13 18:30:37 +00:00
|
|
|
} // anon namespace
|
2009-08-19 22:55:38 +00:00
|
|
|
|
2010-01-30 10:11:24 +00:00
|
|
|
///
|
|
|
|
static void findAdvReplace(BufferView * bv, FindAndReplaceOptions const & opt, MatchStringAdv & matchAdv)
|
|
|
|
{
|
|
|
|
Cursor & cur = bv->cursor();
|
2011-02-07 20:36:40 +00:00
|
|
|
if (opt.repl_buf_name == docstring())
|
2010-01-30 10:11:24 +00:00
|
|
|
return;
|
2011-02-07 20:36:40 +00:00
|
|
|
|
2010-01-30 10:11:24 +00:00
|
|
|
DocIterator sel_beg = cur.selectionBegin();
|
|
|
|
DocIterator sel_end = cur.selectionEnd();
|
2010-02-22 21:44:59 +00:00
|
|
|
if (&sel_beg.inset() != &sel_end.inset()
|
|
|
|
|| sel_beg.pit() != sel_end.pit())
|
|
|
|
return;
|
2010-01-30 10:11:24 +00:00
|
|
|
int sel_len = sel_end.pos() - sel_beg.pos();
|
2010-02-22 21:44:59 +00:00
|
|
|
LYXERR(Debug::FIND, "sel_beg: " << sel_beg << ", sel_end: " << sel_end
|
|
|
|
<< ", sel_len: " << sel_len << endl);
|
2010-01-30 10:11:24 +00:00
|
|
|
if (sel_len == 0)
|
|
|
|
return;
|
|
|
|
LASSERT(sel_len > 0, /**/);
|
|
|
|
|
|
|
|
if (!matchAdv(sel_beg, sel_len))
|
|
|
|
return;
|
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
// Build a copy of the replace buffer, adapted to the KeepCase option
|
|
|
|
Buffer & repl_buffer_orig = *theBufferList().getBuffer(FileName(to_utf8(opt.repl_buf_name)), true);
|
|
|
|
ostringstream oss;
|
|
|
|
repl_buffer_orig.write(oss);
|
|
|
|
string lyx = oss.str();
|
2010-01-30 10:11:24 +00:00
|
|
|
Buffer repl_buffer("", false);
|
|
|
|
repl_buffer.setUnnamed(true);
|
|
|
|
LASSERT(repl_buffer.readString(lyx), /**/);
|
|
|
|
if (opt.keep_case && sel_len >= 2) {
|
|
|
|
if (cur.inTexted()) {
|
|
|
|
if (firstUppercase(cur))
|
|
|
|
changeFirstCase(repl_buffer, text_uppercase, text_lowercase);
|
|
|
|
else if (allNonLowercase(cur, sel_len))
|
|
|
|
changeFirstCase(repl_buffer, text_uppercase, text_uppercase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cap::cutSelection(cur, false, false);
|
|
|
|
if (!cur.inMathed()) {
|
2010-12-30 17:59:59 +00:00
|
|
|
repl_buffer.changeLanguage(
|
|
|
|
repl_buffer.language(),
|
|
|
|
cur.getFont().language());
|
2010-01-30 10:11:24 +00:00
|
|
|
LYXERR(Debug::FIND, "Replacing by pasteParagraphList()ing repl_buffer");
|
2010-12-29 19:59:41 +00:00
|
|
|
LYXERR(Debug::FIND, "Before pasteParagraphList() cur=" << cur << endl);
|
2010-01-30 10:11:24 +00:00
|
|
|
cap::pasteParagraphList(cur, repl_buffer.paragraphs(),
|
|
|
|
repl_buffer.params().documentClassPtr(),
|
|
|
|
bv->buffer().errorList("Paste"));
|
2010-12-29 19:59:41 +00:00
|
|
|
LYXERR(Debug::FIND, "After pasteParagraphList() cur=" << cur << endl);
|
2011-01-26 23:54:12 +00:00
|
|
|
sel_len = repl_buffer.paragraphs().begin()->size();
|
2010-01-30 10:11:24 +00:00
|
|
|
} else {
|
|
|
|
odocstringstream ods;
|
Introduce a wrapper class for odocstream to help ensuring that no
blank lines may be inadvertently output. This is achieved by using two
special iomanip-like variables (breakln and safebreakln) in the lyx::
namespace. When they are inserted in the stream, a newline is output
only if not already at the beginning of a line. The difference between
breakln and safebreakln is that, if needed, the former outputs '\n'
and the latter "%\n".
In future, the new class will also be used for counting the number of
newlines issued. Even if the infractrure for doing that is already in
place, the counting is essentially still done the old way.
There are still places in the code where the functionality of the
class could be used, most probably. ATM, it is used for InsetTabular,
InsetListings, InsetFloat, and InsetText.
The Comment and GreyedOut insets required a special treatment and a
new InsetLayout parameter (Display) has been introduced. The default
for Display is "true", meaning that the corresponding latex
environment is of "display" type, i.e., it stands on its own, whereas
"false" means that the contents appear inline with the text. The
latter is the case for both Comment and GreyedOut insets.
Mostly, the only visible effects on latex exports should be the
disappearing of some redundant % chars and the appearing/disappearing
of null {} latex groups after a comment or lyxgreyedout environments
(they are related to the presence or absence of a space immediately
after those environments), as well as the fact that math environments
are now started on their own lines.
As a last thing, only the latex code between \begin{document} and
\end{document} goes through the new class, the preamble being directly
output through odocstream, as usual.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37360 a592a061-630c-0410-9148-cb99ea01b6c8
2011-01-29 02:41:13 +00:00
|
|
|
otexstream os(ods);
|
2010-01-30 10:11:24 +00:00
|
|
|
OutputParams runparams(&repl_buffer.params().encoding());
|
|
|
|
runparams.nice = false;
|
|
|
|
runparams.flavor = OutputParams::LATEX;
|
|
|
|
runparams.linelen = 8000; //lyxrc.plaintext_linelen;
|
|
|
|
runparams.dryrun = true;
|
|
|
|
TexRow texrow;
|
Introduce a wrapper class for odocstream to help ensuring that no
blank lines may be inadvertently output. This is achieved by using two
special iomanip-like variables (breakln and safebreakln) in the lyx::
namespace. When they are inserted in the stream, a newline is output
only if not already at the beginning of a line. The difference between
breakln and safebreakln is that, if needed, the former outputs '\n'
and the latter "%\n".
In future, the new class will also be used for counting the number of
newlines issued. Even if the infractrure for doing that is already in
place, the counting is essentially still done the old way.
There are still places in the code where the functionality of the
class could be used, most probably. ATM, it is used for InsetTabular,
InsetListings, InsetFloat, and InsetText.
The Comment and GreyedOut insets required a special treatment and a
new InsetLayout parameter (Display) has been introduced. The default
for Display is "true", meaning that the corresponding latex
environment is of "display" type, i.e., it stands on its own, whereas
"false" means that the contents appear inline with the text. The
latter is the case for both Comment and GreyedOut insets.
Mostly, the only visible effects on latex exports should be the
disappearing of some redundant % chars and the appearing/disappearing
of null {} latex groups after a comment or lyxgreyedout environments
(they are related to the presence or absence of a space immediately
after those environments), as well as the fact that math environments
are now started on their own lines.
As a last thing, only the latex code between \begin{document} and
\end{document} goes through the new class, the preamble being directly
output through odocstream, as usual.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37360 a592a061-630c-0410-9148-cb99ea01b6c8
2011-01-29 02:41:13 +00:00
|
|
|
TeXOnePar(repl_buffer, repl_buffer.text(), 0, os, texrow, runparams);
|
2010-01-30 10:11:24 +00:00
|
|
|
//repl_buffer.getSourceCode(ods, 0, repl_buffer.paragraphs().size(), false);
|
|
|
|
docstring repl_latex = ods.str();
|
|
|
|
LYXERR(Debug::FIND, "Latexified replace_buffer: '" << repl_latex << "'");
|
|
|
|
string s;
|
|
|
|
regex_replace(to_utf8(repl_latex), s, "\\$(.*)\\$", "$1");
|
|
|
|
regex_replace(s, s, "\\\\\\[(.*)\\\\\\]", "$1");
|
|
|
|
repl_latex = from_utf8(s);
|
|
|
|
LYXERR(Debug::FIND, "Replacing by niceInsert()ing latex: '" << repl_latex << "'");
|
2011-01-26 23:54:12 +00:00
|
|
|
sel_len = cur.niceInsert(repl_latex);
|
2010-01-30 10:11:24 +00:00
|
|
|
}
|
2011-01-26 23:54:12 +00:00
|
|
|
cur.pos() -= sel_len;
|
|
|
|
if (cur.pos() < 0)
|
|
|
|
cur.pos() = 0;
|
|
|
|
LYXERR(Debug::FIND, "Putting selection at cur=" << cur << " with len: " << sel_len);
|
|
|
|
bv->putSelectionAt(DocIterator(cur), sel_len, !opt.forward);
|
2010-11-20 11:04:40 +00:00
|
|
|
bv->processUpdateFlags(Update::Force);
|
2010-01-30 10:11:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-15 23:30:27 +00:00
|
|
|
/// Perform a FindAdv operation.
|
2009-01-14 15:34:56 +00:00
|
|
|
bool findAdv(BufferView * bv, FindAndReplaceOptions const & opt)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2010-01-30 10:11:24 +00:00
|
|
|
DocIterator cur;
|
2010-12-29 19:59:41 +00:00
|
|
|
int match_len = 0;
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
try {
|
2010-01-30 10:11:24 +00:00
|
|
|
MatchStringAdv matchAdv(bv->buffer(), opt);
|
|
|
|
findAdvReplace(bv, opt, matchAdv);
|
|
|
|
cur = bv->cursor();
|
2008-11-15 23:30:27 +00:00
|
|
|
if (opt.forward)
|
|
|
|
match_len = findForwardAdv(cur, matchAdv);
|
|
|
|
else
|
|
|
|
match_len = findBackwardsAdv(cur, matchAdv);
|
|
|
|
} catch (...) {
|
2010-06-29 17:09:40 +00:00
|
|
|
// This may only be raised by lyx::regex()
|
2008-12-07 17:18:30 +00:00
|
|
|
bv->message(_("Invalid regular expression!"));
|
2008-11-15 23:30:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match_len == 0) {
|
2008-12-07 17:18:30 +00:00
|
|
|
bv->message(_("Match not found!"));
|
2008-11-15 23:30:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-01-30 10:11:24 +00:00
|
|
|
bv->message(_("Match found!"));
|
2009-12-30 18:40:18 +00:00
|
|
|
|
2010-01-30 10:11:24 +00:00
|
|
|
LYXERR(Debug::FIND, "Putting selection at cur=" << cur << " with len: " << match_len);
|
|
|
|
bv->putSelectionAt(cur, match_len, !opt.forward);
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-10 12:37:50 +00:00
|
|
|
ostringstream & operator<<(ostringstream & os, FindAndReplaceOptions const & opt)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2011-02-07 20:36:40 +00:00
|
|
|
os << to_utf8(opt.find_buf_name) << "\nEOSS\n"
|
2008-11-15 23:30:27 +00:00
|
|
|
<< opt.casesensitive << ' '
|
|
|
|
<< opt.matchword << ' '
|
|
|
|
<< opt.forward << ' '
|
|
|
|
<< opt.expandmacros << ' '
|
|
|
|
<< opt.ignoreformat << ' '
|
2011-02-07 20:36:40 +00:00
|
|
|
<< to_utf8(opt.repl_buf_name) << "\nEOSS\n"
|
2009-12-30 18:40:18 +00:00
|
|
|
<< opt.keep_case << ' '
|
|
|
|
<< int(opt.scope);
|
2008-11-15 23:30:27 +00:00
|
|
|
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "built: " << os.str());
|
2008-11-15 23:30:27 +00:00
|
|
|
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2011-02-07 20:36:40 +00:00
|
|
|
|
2010-01-10 12:37:50 +00:00
|
|
|
istringstream & operator>>(istringstream & is, FindAndReplaceOptions & opt)
|
2008-11-15 23:30:27 +00:00
|
|
|
{
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "parsing");
|
2008-11-15 23:30:27 +00:00
|
|
|
string s;
|
|
|
|
string line;
|
|
|
|
getline(is, line);
|
|
|
|
while (line != "EOSS") {
|
|
|
|
if (! s.empty())
|
|
|
|
s = s + "\n";
|
|
|
|
s = s + line;
|
|
|
|
if (is.eof()) // Tolerate malformed request
|
|
|
|
break;
|
|
|
|
getline(is, line);
|
|
|
|
}
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND, "file_buf_name: '" << s << "'");
|
|
|
|
opt.find_buf_name = from_utf8(s);
|
|
|
|
is >> opt.casesensitive >> opt.matchword >> opt.forward >> opt.expandmacros >> opt.ignoreformat;
|
2009-01-14 15:34:56 +00:00
|
|
|
is.get(); // Waste space before replace string
|
|
|
|
s = "";
|
|
|
|
getline(is, line);
|
|
|
|
while (line != "EOSS") {
|
|
|
|
if (! s.empty())
|
|
|
|
s = s + "\n";
|
|
|
|
s = s + line;
|
|
|
|
if (is.eof()) // Tolerate malformed request
|
|
|
|
break;
|
|
|
|
getline(is, line);
|
|
|
|
}
|
2011-02-07 20:36:40 +00:00
|
|
|
LYXERR(Debug::FIND, "repl_buf_name: '" << s << "'");
|
|
|
|
opt.repl_buf_name = from_utf8(s);
|
2009-08-19 22:55:38 +00:00
|
|
|
is >> opt.keep_case;
|
2009-12-30 18:40:18 +00:00
|
|
|
int i;
|
|
|
|
is >> i;
|
|
|
|
opt.scope = FindAndReplaceOptions::SearchScope(i);
|
2009-08-17 14:39:00 +00:00
|
|
|
LYXERR(Debug::FIND, "parsed: " << opt.casesensitive << ' ' << opt.matchword << ' ' << opt.forward << ' '
|
2011-02-07 20:36:40 +00:00
|
|
|
<< opt.expandmacros << ' ' << opt.ignoreformat << ' ' << opt.keep_case);
|
2008-11-15 23:30:27 +00:00
|
|
|
return is;
|
|
|
|
}
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
} // lyx namespace
|