Fix bug 1395 by Stefan Schimanski:

Locking counter added to MacroData: it is increased before drawing/
metric calculations and decreased afterwards in InsetMathMacro. If a  
macro is already locked at that point, "Self reference: \foo" is  
drawn instead of the macro definition to avoid endless loops.  
Moreover inside of the arguments of the macro the counter is  
temporarily decreased as those cases do not cause loops. (fixes bug  
#1395)

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@17836 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2007-04-17 13:15:00 +00:00
parent 8874db5f22
commit 71e8668375
10 changed files with 176 additions and 93 deletions

View File

@ -113,6 +113,7 @@ public:
virtual InsetMathAMSArray const * asAMSArrayInset() const { return 0; } virtual InsetMathAMSArray const * asAMSArrayInset() const { return 0; }
virtual InsetMathArray * asArrayInset() { return 0; } virtual InsetMathArray * asArrayInset() { return 0; }
virtual InsetMathArray const * asArrayInset() const { return 0; } virtual InsetMathArray const * asArrayInset() const { return 0; }
virtual InsetMathBrace * asBraceInset() { return 0; }
virtual InsetMathBrace const * asBraceInset() const { return 0; } virtual InsetMathBrace const * asBraceInset() const { return 0; }
virtual InsetMathChar const * asCharInset() const { return 0; } virtual InsetMathChar const * asCharInset() const { return 0; }
virtual InsetMathDelim * asDelimInset() { return 0; } virtual InsetMathDelim * asDelimInset() { return 0; }

View File

@ -25,8 +25,6 @@ public:
InsetMathBrace(); InsetMathBrace();
/// ///
InsetMathBrace(MathArray const & ar); InsetMathBrace(MathArray const & ar);
///
InsetMathBrace const * asBraceInset() const { return this; }
/// we write extra braces in any case... /// we write extra braces in any case...
bool extraBraces() const { return true; } bool extraBraces() const { return true; }
/// ///
@ -47,6 +45,11 @@ public:
void mathmlize(MathStream &) const; void mathmlize(MathStream &) const;
/// ///
void infoize(odocstream & os) const; void infoize(odocstream & os) const;
/// identifies brace insets
InsetMathBrace * asBraceInset() { return this; }
/// identifies brace insets
InsetMathBrace const * asBraceInset() const { return this; }
private: private:
virtual std::auto_ptr<InsetBase> doClone() const; virtual std::auto_ptr<InsetBase> doClone() const;
}; };

View File

@ -33,8 +33,49 @@ using std::endl;
using std::vector; using std::vector;
MathMacro::MathMacro(docstring const & name, int numargs) /// This class is the value of a macro argument, technically
: InsetMathNest(numargs), name_(name) /// a wrapper of the cells of MathMacro.
class MathMacroArgumentValue : public InsetMathDim {
public:
///
MathMacroArgumentValue(MathArray const * value) : value_(value) {}
///
bool metrics(MetricsInfo & mi, Dimension & dim) const;
///
void draw(PainterInfo &, int x, int y) const;
private:
std::auto_ptr<InsetBase> doClone() const;
MathArray const * value_;
};
auto_ptr<InsetBase> MathMacroArgumentValue::doClone() const
{
return auto_ptr<InsetBase>(new MathMacroArgumentValue(*this));
}
bool MathMacroArgumentValue::metrics(MetricsInfo & mi, Dimension & dim) const
{
value_->metrics(mi, dim);
metricsMarkers2(dim);
if (dim_ == dim)
return false;
dim_ = dim;
return true;
}
void MathMacroArgumentValue::draw(PainterInfo & pi, int x, int y) const
{
value_->draw(pi, x, y);
}
MathMacro::MathMacro(docstring const & name)
: InsetMathNest(0), name_(name)
{} {}
@ -80,7 +121,12 @@ bool MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
dim.des += c.height() + 10; dim.des += c.height() + 10;
} }
} else { } else {
MacroTable::globalMacros().get(name()).expand(cells_, expanded_); // create MathMacroArgumentValue object pointing to the cells of the macro
MacroData const & macro = MacroTable::globalMacros().get(name());
vector<MathArray> values(nargs());
for (size_t i = 0; i != nargs(); ++i)
values[i].insert(0, MathAtom(new MathMacroArgumentValue(&cells_[i])));
macro.expand(values, expanded_);
expanded_.metrics(mi, dim); expanded_.metrics(mi, dim);
} }
metricsMarkers2(dim); metricsMarkers2(dim);
@ -141,18 +187,23 @@ void MathMacro::validate(LaTeXFeatures & features) const
InsetBase * MathMacro::editXY(LCursor & cur, int x, int y) InsetBase * MathMacro::editXY(LCursor & cur, int x, int y)
{ {
// We may have 0 arguments, but InsetMathNest requires at least one. // We may have 0 arguments, but InsetMathNest requires at least one.
if (nargs() > 0) { if (nargs() > 0)
// Prevent crash due to cold coordcache
// FIXME: This is only a workaround, the call of
// InsetMathNest::editXY is correct. The correct fix would
// ensure that the coordcache of the arguments is valid.
if (!editing(&cur.bv())) {
edit(cur, true);
return this;
}
return InsetMathNest::editXY(cur, x, y); return InsetMathNest::editXY(cur, x, y);
} else
return this; return this;
}
void MathMacro::detachArguments(std::vector<MathArray> &args)
{
args = cells_;
cells_ = std::vector<MathArray>();
}
void MathMacro::attachArguments(std::vector<MathArray> const &args)
{
cells_ = args;
} }
@ -179,31 +230,22 @@ bool MathMacro::notifyCursorLeaves(LCursor & cur)
void MathMacro::maple(MapleStream & os) const void MathMacro::maple(MapleStream & os) const
{ {
updateExpansion();
lyx::maple(expanded_, os); lyx::maple(expanded_, os);
} }
void MathMacro::mathmlize(MathStream & os) const void MathMacro::mathmlize(MathStream & os) const
{ {
updateExpansion();
lyx::mathmlize(expanded_, os); lyx::mathmlize(expanded_, os);
} }
void MathMacro::octave(OctaveStream & os) const void MathMacro::octave(OctaveStream & os) const
{ {
updateExpansion();
lyx::octave(expanded_, os); lyx::octave(expanded_, os);
} }
void MathMacro::updateExpansion() const
{
//expanded_.substitute(*this);
}
void MathMacro::infoize(odocstream & os) const void MathMacro::infoize(odocstream & os) const
{ {
os << "Macro: " << name(); os << "Macro: " << name();
@ -213,7 +255,6 @@ void MathMacro::infoize(odocstream & os) const
void MathMacro::infoize2(odocstream & os) const void MathMacro::infoize2(odocstream & os) const
{ {
os << "Macro: " << name(); os << "Macro: " << name();
} }

View File

@ -26,7 +26,11 @@ namespace lyx {
class MathMacro : public InsetMathNest { class MathMacro : public InsetMathNest {
public: public:
/// A macro can be built from an existing template /// A macro can be built from an existing template
MathMacro(docstring const & name, int numargs); MathMacro(docstring const & name);
///
virtual MathMacro * asMacro() { return this; }
///
virtual MathMacro const * asMacro() const { return this; }
/// ///
void draw(PainterInfo & pi, int x, int y) const; void draw(PainterInfo & pi, int x, int y) const;
/// ///
@ -38,6 +42,8 @@ public:
{ drawMarkers2(pi, x, y); } { drawMarkers2(pi, x, y); }
/// ///
bool metrics(MetricsInfo & mi, Dimension & dim) const; bool metrics(MetricsInfo & mi, Dimension & dim) const;
///
bool metricsExpanded(MetricsInfo & mi, Dimension & dim) const;
/// get cursor position /// get cursor position
void cursorPos(BufferView const & bv, CursorSlice const & sl, void cursorPos(BufferView const & bv, CursorSlice const & sl,
bool boundary, int & x, int & y) const; bool boundary, int & x, int & y) const;
@ -52,7 +58,9 @@ public:
/// ///
docstring name() const; docstring name() const;
/// ///
void setExpansion(MathArray const & exp, MathArray const & args) const; void detachArguments(std::vector<MathArray> &args);
///
void attachArguments(std::vector<MathArray> const &args);
/// ///
void validate(LaTeXFeatures &) const; void validate(LaTeXFeatures &) const;
@ -70,14 +78,10 @@ public:
private: private:
virtual std::auto_ptr<InsetBase> doClone() const; virtual std::auto_ptr<InsetBase> doClone() const;
///
void updateExpansion() const;
///
void expand() const;
/// name of macro /// name of macro
docstring name_; docstring name_;
/// the unexpanded macro defintition /// the macro template
mutable MathArray tmpl_; mutable MathArray tmpl_;
/// the macro substituted with our args /// the macro substituted with our args
mutable MathArray expanded_; mutable MathArray expanded_;

View File

@ -14,6 +14,7 @@
#include "InsetMathFont.h" #include "InsetMathFont.h"
#include "InsetMathScript.h" #include "InsetMathScript.h"
#include "InsetMathMacro.h" #include "InsetMathMacro.h"
#include "InsetMathBrace.h"
#include "MathMacroTable.h" #include "MathMacroTable.h"
#include "MathStream.h" #include "MathStream.h"
#include "MathSupport.h" #include "MathSupport.h"
@ -239,7 +240,6 @@ bool isInside(DocIterator const & it, MathArray const & ar,
} }
void MathArray::metrics(MetricsInfo & mi) const void MathArray::metrics(MetricsInfo & mi) const
{ {
frontend::FontMetrics const & fm = theFontMetrics(mi.base.font); frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
@ -256,37 +256,16 @@ void MathArray::metrics(MetricsInfo & mi) const
if (empty()) if (empty())
return; return;
const_cast<MathArray*>(this)->updateMacros( mi );
dim_.asc = 0; dim_.asc = 0;
dim_.wid = 0; dim_.wid = 0;
Dimension d; Dimension d;
//BufferView & bv = *mi.base.bv; for (size_t i = 0; i != size(); ++i) {
//Buffer const & buf = *bv.buffer();
for (size_t i = 0, n = size(); i != n; ++i) {
MathAtom const & at = operator[](i); MathAtom const & at = operator[](i);
#if 0
MathMacro const * mac = at->asMacro();
if (mac && buf.hasMacro(mac->name())) {
MacroData const & tmpl = buf.getMacro(mac->name());
int numargs = tmpl.numargs();
if (i + numargs > n)
numargs = n - i - 1;
lyxerr << "metrics:found macro: " << mac->name()
<< " numargs: " << numargs << endl;
if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
MathArray args(begin() + i + 1, begin() + i + numargs + 1);
MathArray exp;
tmpl.expand(args, exp);
mac->setExpansion(exp, args);
mac->metricsExpanded(mi, d);
dim_.wid += mac->widthExpanded();
i += numargs;
continue;
}
}
#endif
at->metrics(mi, d); at->metrics(mi, d);
dim_ += d; dim_ += d;
if (i == n - 1) if (i == size() - 1)
kerning_ = at->kerning(); kerning_ = at->kerning();
} }
} }
@ -312,23 +291,6 @@ void MathArray::draw(PainterInfo & pi, int x, int y) const
for (size_t i = 0, n = size(); i != n; ++i) { for (size_t i = 0, n = size(); i != n; ++i) {
MathAtom const & at = operator[](i); MathAtom const & at = operator[](i);
#if 0
Buffer const & buf = bv.buffer();
// special macro handling
MathMacro const * mac = at->asMacro();
if (mac && buf.hasMacro(mac->name())) {
MacroData const & tmpl = buf.getMacro(mac->name());
int numargs = tmpl.numargs();
if (i + numargs > n)
numargs = n - i - 1;
if (!isInside(bv.cursor(), *this, i + 1, i + numargs + 1)) {
mac->drawExpanded(pi, x, y);
x += mac->widthExpanded();
i += numargs;
continue;
}
}
#endif
bv.coordCache().insets().add(at.nucleus(), x, y); bv.coordCache().insets().add(at.nucleus(), x, y);
at->drawSelection(pi, x, y); at->drawSelection(pi, x, y);
at->draw(pi, x, y); at->draw(pi, x, y);
@ -363,6 +325,74 @@ void MathArray::drawT(TextPainter & pain, int x, int y) const
} }
void MathArray::updateMacros(MetricsInfo & mi) {
Buffer *buf = mi.base.bv->buffer();
// go over the array and look for macros
for (size_t i = 0; i != size(); ++i) {
InsetMath * at = operator[](i).nucleus();
MathMacro * macroInset = at->asMacro();
if (macroInset) {
// get arity of macro or 0 if unknown
size_t numargs = 0;
if (buf->hasMacro(macroInset->name())) {
MacroData const & macro = buf->getMacro(macroInset->name());
numargs = macro.numargs();
}
// arity of macro changed?
if (macroInset->nargs() != numargs) {
// detach all arguments
std::vector<MathArray> detachedArgs;
macroInset->detachArguments( detachedArgs );
// too many arguments in the macro inset?
if (detachedArgs.size() > numargs) {
// insert overlap back as braces
std::vector<MathArray> overlap(detachedArgs.begin()+numargs, detachedArgs.end());
detachedArgs.erase(detachedArgs.begin()+numargs, detachedArgs.end());
for (size_t j = 0; j < overlap.size(); ++j) {
MathArray const & arg = overlap[j];
if (arg.size() == 1)
insert(i+j+1, MathAtom(new InsetMathBrace(arg)));
else
insert(i+j+1, arg[0]);
}
i += overlap.size();
} else {
// insert some cells from the array into the macro inset
size_t missingArgs = numargs-detachedArgs.size();
size_t j;
for (j = 0; j < missingArgs && i+1+j < size(); ++j) {
MathAtom & cell = operator[](i+1+j);
InsetMathBrace const * brace = cell->asBraceInset();
if (brace) {
// found brace, convert into argument
detachedArgs.push_back(brace->cell(0));
} else {
MathArray array;
array.insert(0, cell);
detachedArgs.push_back(array);
}
}
// remove them from the array
erase(begin()+i+1, begin()+i+1+j);
// enough for the macro inset now?
// Add some empty ones of necessary
for (; j < missingArgs; ++j)
detachedArgs.insert(detachedArgs.end(), MathArray());
}
// attach arguments back to macro inset
macroInset->attachArguments(detachedArgs);
}
}
}
}
int MathArray::pos2x(size_type pos) const int MathArray::pos2x(size_type pos) const
{ {
return pos2x(pos, 0); return pos2x(pos, 0);

View File

@ -172,6 +172,9 @@ protected:
mutable int sshift_; mutable int sshift_;
mutable int kerning_; mutable int kerning_;
/// attach/detach brace inset to macros
void updateMacros(MetricsInfo & mi);
private: private:
/// is this an exact match at this position? /// is this an exact match at this position?
bool find1(MathArray const & ar, size_type pos) const; bool find1(MathArray const & ar, size_type pos) const;

View File

@ -392,15 +392,7 @@ MathAtom createInsetMath(docstring const & s)
if (s == "vphantom") if (s == "vphantom")
return MathAtom(new InsetMathPhantom(InsetMathPhantom::vphantom)); return MathAtom(new InsetMathPhantom(InsetMathPhantom::vphantom));
if (MacroTable::globalMacros().has(s)) return MathAtom(new MathMacro(s));
return MathAtom(new MathMacro(s,
MacroTable::globalMacros().get(s).numargs()));
//if (MacroTable::localMacros().has(s))
// return MathAtom(new MathMacro(s,
// MacroTable::localMacros().get(s).numargs()));
//lyxerr << "creating unknown inset '" << s << "'" << endl;
return MathAtom(new InsetMathUnknown(s));
} }

View File

@ -41,7 +41,7 @@ MacroData::MacroData()
MacroData::MacroData(docstring const & def, int numargs, docstring const & disp, string const & requires) MacroData::MacroData(docstring const & def, int numargs, docstring const & disp, string const & requires)
: def_(def), numargs_(numargs), disp_(disp), requires_(requires) : def_(def), numargs_(numargs), disp_(disp), requires_(requires), lockCount_(0)
{} {}

View File

@ -4,7 +4,7 @@
* This file is part of LyX, the document processor. * This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING. * Licence details can be found in the file COPYING.
* *
* \author André nitz * \author AndrÈ Pˆnitz
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
@ -41,6 +41,13 @@ public:
/// ///
std::string & requires() { return requires_; } std::string & requires() { return requires_; }
///
int lock() { return ++lockCount_; }
///
bool locked() const { return lockCount_!=0; }
///
void unlock() { --lockCount_; assert(lockCount_>=0); }
private: private:
/// ///
docstring def_; docstring def_;
@ -50,6 +57,8 @@ private:
docstring disp_; docstring disp_;
/// ///
std::string requires_; std::string requires_;
///
int lockCount_;
}; };

View File

@ -105,7 +105,7 @@ docstring MathMacroTemplate::name() const
docstring MathMacroTemplate::prefix() const docstring MathMacroTemplate::prefix() const
{ {
return bformat(_(" Macro: %1$s: "), name_); return bformat(_(" Macro \\%1$s: "), name_);
} }