X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmathed%2FInsetMathMacro.cpp;h=d2c35c7e5bea913e1d857fc319871945de87d0db;hb=e5bb491e2e10a8097284d11eae5501a3673731cb;hp=d2d4a8bc5d24c86c0447e085eb64c84748578a45;hpb=29378af4cdf9db7a7d6a72a9df34b8593f73f0e8;p=lyx.git diff --git a/src/mathed/InsetMathMacro.cpp b/src/mathed/InsetMathMacro.cpp index d2d4a8bc5d..d2c35c7e5b 100644 --- a/src/mathed/InsetMathMacro.cpp +++ b/src/mathed/InsetMathMacro.cpp @@ -15,9 +15,11 @@ #include "InsetMathMacro.h" #include "InsetMathChar.h" +#include "InsetMathScript.h" #include "MathCompletionList.h" #include "MathExtern.h" #include "MathFactory.h" +#include "MathRow.h" #include "MathStream.h" #include "MathSupport.h" @@ -38,7 +40,7 @@ #include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/RefChanger.h" +#include "support/Changer.h" #include "support/textutils.h" #include @@ -63,13 +65,19 @@ public: asArray(def, def_); } /// + void setBuffer(Buffer & buffer) override + { + Inset::setBuffer(buffer); + def_.setBuffer(buffer); + } + /// void setOwner(InsetMathMacro * mathMacro) { mathMacro_ = mathMacro; } /// InsetMathMacro const * owner() { return mathMacro_; } /// - marker_type marker(BufferView const *) const { return NO_MARKER; } + marker_type marker(BufferView const *) const override { return marker_type::NO_MARKER; } /// - InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; } + InsetCode lyxCode() const override { return ARGUMENT_PROXY_CODE; } /// The math data to use for display MathData const & displayCell(BufferView const * bv) const { @@ -79,12 +87,12 @@ public: return use_def_arg ? def_ : mathMacro_->cell(idx_); } /// - bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const + bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const override { // macro arguments are in macros LATTEST(mathMacro_->nesting() > 0); /// The macro nesting can change display of insets. Change it locally. - Changer chg = make_change(mi.base.macro_nesting, + Changer chg = changeVar(mi.base.macro_nesting, mathMacro_->nesting() == 1 ? 0 : mathMacro_->nesting()); MathRow::Element e_beg(mi, MathRow::BEGIN); @@ -114,17 +122,17 @@ public: return has_contents; } /// - void beforeMetrics() const + void beforeMetrics() const override { mathMacro_->macro()->unlock(); } /// - void afterMetrics() const + void afterMetrics() const override { mathMacro_->macro()->lock(); } /// - void beforeDraw(PainterInfo const & pi) const + void beforeDraw(PainterInfo const & pi) const override { // if the macro is being edited, then the painter is in // monochrome mode. @@ -132,42 +140,42 @@ public: pi.pain.leaveMonochromeMode(); } /// - void afterDraw(PainterInfo const & pi) const + void afterDraw(PainterInfo const & pi) const override { if (mathMacro_->editMetrics(pi.base.bv)) - pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend); + pi.pain.enterMonochromeMode(Color_mathmacroblend); } /// - void metrics(MetricsInfo &, Dimension &) const { + void metrics(MetricsInfo &, Dimension &) const override { // This should never be invoked, since InsetArgumentProxy insets are linearized LATTEST(false); } /// - void draw(PainterInfo &, int, int) const { + void draw(PainterInfo &, int, int) const override { // This should never be invoked, since InsetArgumentProxy insets are linearized LATTEST(false); } /// - int kerning(BufferView const * bv) const + int kerning(BufferView const * bv) const override { return displayCell(bv).kerning(bv); } // write(), normalize(), infoize() and infoize2() are not needed since // InsetMathMacro uses the definition and not the expanded cells. /// - void maple(MapleStream & ms) const { ms << mathMacro_->cell(idx_); } + void maple(MapleStream & ms) const override { ms << mathMacro_->cell(idx_); } /// - void maxima(MaximaStream & ms) const { ms << mathMacro_->cell(idx_); } + void maxima(MaximaStream & ms) const override { ms << mathMacro_->cell(idx_); } /// - void mathematica(MathematicaStream & ms) const { ms << mathMacro_->cell(idx_); } + void mathematica(MathematicaStream & ms) const override { ms << mathMacro_->cell(idx_); } /// - void mathmlize(MathStream & ms) const { ms << mathMacro_->cell(idx_); } + void mathmlize(MathMLStream & ms) const override { ms << mathMacro_->cell(idx_); } /// - void htmlize(HtmlStream & ms) const { ms << mathMacro_->cell(idx_); } + void htmlize(HtmlStream & ms) const override { ms << mathMacro_->cell(idx_); } /// - void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); } + void octave(OctaveStream & os) const override { os << mathMacro_->cell(idx_); } /// - MathClass mathClass() const + MathClass mathClass() const override { return MC_UNKNOWN; // This can be refined once the pointer issues are fixed. I did not @@ -178,7 +186,7 @@ public: private: /// - Inset * clone() const + Inset * clone() const override { return new InsetArgumentProxy(*this); } @@ -198,8 +206,8 @@ public: : name_(name), displayMode_(DISPLAY_INIT), expanded_(buf), definition_(buf), attachedArgsNum_(0), optionals_(0), nextFoldMode_(true), macroBackup_(buf), - macro_(0), needsUpdate_(false), isUpdating_(false), - appetite_(9), nesting_(0) + macro_(nullptr), needsUpdate_(false), isUpdating_(false), + appetite_(9), nesting_(0), limits_(AUTO_LIMITS) { } /// Update the pointers to our owner of all expanded macros. @@ -232,7 +240,7 @@ public: /// mutable std::map editing_; /// - std::string requires_; + std::string required_; /// update macro representation bool needsUpdate_; /// @@ -241,6 +249,8 @@ public: size_t appetite_; /// Level of nesting in macros (including this one) int nesting_; + /// + Limits limits_; }; @@ -298,7 +308,11 @@ InsetMathMacro::InsetMathMacro(Buffer * buf, docstring const & name) InsetMathMacro::InsetMathMacro(InsetMathMacro const & that) : InsetMathNest(that), d(new Private(*that.d)) { - setBuffer(*that.buffer_); + // FIXME This should not really be necessary, but when we are + // initializing the table of global macros, we create macros + // with no associated Buffer. + if (that.buffer_) + setBuffer(*that.buffer_); d->updateChildren(this); } @@ -335,11 +349,11 @@ bool InsetMathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const return InsetMath::addToMathRow(mrow, mi); /// The macro nesting can change display of insets. Change it locally. - Changer chg = make_change(mi.base.macro_nesting, d->nesting_); + Changer chg = changeVar(mi.base.macro_nesting, d->nesting_); MathRow::Element e_beg(mi, MathRow::BEGIN); e_beg.inset = this; - e_beg.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : NO_MARKER; + e_beg.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER; mrow.push_back(e_beg); d->macro_->lock(); @@ -358,12 +372,57 @@ bool InsetMathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const MathRow::Element e_end(mi, MathRow::END); e_end.inset = this; - e_end.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : NO_MARKER; + e_end.marker = (d->nesting_ == 1) ? marker(mi.base.bv) : marker_type::NO_MARKER; mrow.push_back(e_end); return has_contents; } + +/// Whether the inset allows \(no)limits +bool InsetMathMacro::allowsLimitsChange() const +{ + // similar to the code in mathClass(), except that we search for + // the right-side class. + MathClass mc = MC_UNKNOWN; + if (MacroData const * m = macroBackup()) { + // If it is a global macro and is defined explicitly + if (m->symbol()) + mc = string_to_class(m->symbol()->extra); + } + // Otherwise guess from the expanded macro + if (mc == MC_UNKNOWN) + mc = d->expanded_.lastMathClass(); + + return mc == MC_OP; +} + + +Limits InsetMathMacro::defaultLimits(bool display) const +{ + if (d->expanded_.empty()) + return NO_LIMITS; + // Guess from the expanded macro + InsetMath const * in = d->expanded_.back().nucleus(); + Limits const lim = in->limits() == AUTO_LIMITS + ? in->defaultLimits(display) : in->limits(); + LATTEST(lim != AUTO_LIMITS); + return lim; +} + + +Limits InsetMathMacro::limits() const +{ + return d->limits_; +} + + +void InsetMathMacro::limits(Limits lim) +{ + d->limits_ = lim; +} + + void InsetMathMacro::beforeMetrics() const { d->macro_->lock(); @@ -379,7 +438,7 @@ void InsetMathMacro::afterMetrics() const void InsetMathMacro::beforeDraw(PainterInfo const & pi) const { if (d->editing_[pi.base.bv]) - pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend); + pi.pain.enterMonochromeMode(Color_mathmacroblend); } @@ -486,36 +545,36 @@ bool InsetMathMacro::editMetrics(BufferView const * bv) const } -InsetMath::marker_type InsetMathMacro::marker(BufferView const * bv) const +marker_type InsetMathMacro::marker(BufferView const * bv) const { if (nargs() == 0) - return NO_MARKER; + return marker_type::NO_MARKER; switch (d->displayMode_) { case DISPLAY_INIT: case DISPLAY_INTERACTIVE_INIT: - return NO_MARKER; + return marker_type::NO_MARKER; case DISPLAY_UNFOLDED: - return MARKER; + return marker_type::MARKER; case DISPLAY_NORMAL: switch (lyxrc.macro_edit_style) { case LyXRC::MACRO_EDIT_INLINE: - return MARKER2; + return marker_type::MARKER2; case LyXRC::MACRO_EDIT_INLINE_BOX: - return d->editing_[bv] ? BOX_MARKER : MARKER2; + return d->editing_[bv] ? marker_type::BOX_MARKER : marker_type::MARKER2; case LyXRC::MACRO_EDIT_LIST: - return MARKER2; + return marker_type::MARKER2; } } // please gcc 4.6 - return NO_MARKER; + return marker_type::NO_MARKER; } void InsetMathMacro::metrics(MetricsInfo & mi, Dimension & dim) const { /// The macro nesting can change display of insets. Change it locally. - Changer chg = make_change(mi.base.macro_nesting, d->nesting_); + Changer chg = changeVar(mi.base.macro_nesting, d->nesting_); // set edit mode for which we will have calculated metrics. But only d->editing_[mi.base.bv] = editMode(mi.base.bv); @@ -595,7 +654,7 @@ void InsetMathMacro::updateMacro(MacroContext const & mc) d->needsUpdate_ = true; } } else { - d->macro_ = 0; + d->macro_ = nullptr; } } @@ -630,14 +689,14 @@ void InsetMathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc, UpdateLocker locker(*this); // known macro? - if (d->macro_ == 0) + if (d->macro_ == nullptr) return; // remember nesting level of this macro d->nesting_ = nesting; // update requires - d->requires_ = d->macro_->requires(); + d->required_ = d->macro_->required(); if (!d->needsUpdate_ // non-normal mode? We are done! @@ -660,17 +719,21 @@ void InsetMathMacro::updateRepresentation(Cursor * cur, MacroContext const & mc, values[i].insert(0, MathAtom(proxy)); } // expanding macro with the values - // Only update the argument macros if anything was expanded, otherwise - // we would get an endless loop (bug 9140). UpdateLocker does not work - // in this case, since MacroData::expand() creates new InsetMathMacro - // objects, so this would be a different recursion path than the one - // protected by UpdateLocker. - if (d->macro_->expand(values, d->expanded_)) { + // Only update the argument macros if anything was expanded or the LyX + // representation part does not contain the macro itself, otherwise we + // would get an endless loop (bugs 9140 and 11595). UpdateLocker does + // not work in this case, since MacroData::expand() creates new + // InsetMathMacro objects, so this would be a different recursion path + // than the one protected by UpdateLocker. + docstring const & display = d->macro_->display(); + docstring const latexname = from_ascii("\\") + macroName(); + bool const ret = d->macro_->expand(values, d->expanded_); + d->expanded_.setBuffer(buffer()); + if (ret && !support::contains(display, latexname)) { if (utype == OutputUpdate && !d->expanded_.empty()) d->expanded_.updateMacros(cur, mc, utype, nesting); } // get definition for list edit mode - docstring const & display = d->macro_->display(); asArray(display.empty() ? d->macro_->definition() : display, d->definition_, Parse::QUIET | Parse::MACRODEF); } @@ -733,7 +796,7 @@ void InsetMathMacro::draw(PainterInfo & pi, int x, int y) const pi.pain.text(x, y, from_ascii(":"), labelFont); x += strw2; - // draw paramter + // draw parameter cell(i).draw(pi, x, y); // next line @@ -779,7 +842,7 @@ void InsetMathMacro::setDisplayMode(InsetMathMacro::DisplayMode mode, int appeti InsetMathMacro::DisplayMode InsetMathMacro::computeDisplayMode() const { - if (d->nextFoldMode_ == true && d->macro_ && !d->macro_->locked()) + if (d->nextFoldMode_ && d->macro_ && !d->macro_->locked()) return DISPLAY_NORMAL; else return DISPLAY_UNFOLDED; @@ -800,11 +863,13 @@ bool InsetMathMacro::validName() const return false;*/ // valid characters? - for (size_t i = 0; i= 'a' && n[i] <= 'z') - && !(n[i] >= 'A' && n[i] <= 'Z') - && n[i] != '*') - return false; + if (n.size() > 1) { + for (char_type c : n) { + if (!(c >= 'a' && c <= 'z') + && !(c >= 'A' && c <= 'Z') + && c != '*') + return false; + } } return true; @@ -861,23 +926,15 @@ MathClass InsetMathMacro::mathClass() const InsetMath::mode_type InsetMathMacro::currentMode() const { - // There is no way to guess the mode of user defined macros, so they are - // always assumed to be mathmode. Only the global macros defined in - // lib/symbols may be textmode. - mode_type mode = modeToEnsure(); - return (mode == UNDECIDED_MODE) ? MATH_MODE : mode; -} - - -InsetMath::mode_type InsetMathMacro::modeToEnsure() const -{ - // User defined macros can be either text mode or math mode for output and - // display. There is no way to guess. For global macros defined in - // lib/symbols, we ensure textmode if flagged as such, otherwise we ensure - // math mode. - if (MacroData const * m = macroBackup()) - if (m->symbol()) - return (m->symbol()->extra == "textmode") ? TEXT_MODE : MATH_MODE; + // User defined macros are always assumed to be mathmode macros. + // Only the global macros defined in lib/symbols may be textmode. + if (MacroData const * m = macroBackup()) { + if (m->symbol() && m->symbol()->extra == "textmode") + return TEXT_MODE; + else + return MATH_MODE; + } + // Unknown macros are undecided. return UNDECIDED_MODE; } @@ -901,13 +958,13 @@ void InsetMathMacro::validate(LaTeXFeatures & features) const // instant preview is on for math, in which case we will be missing // the corresponding requirements. // In this case, we get the required info from the global macro table. - if (!d->requires_.empty()) - features.require(d->requires_); + if (!d->required_.empty()) + features.require(d->required_); else if (!d->macro_) { // Update requires for known global macros. MacroData const * data = MacroTable::globalMacros().get(name()); - if (data && !data->requires().empty()) - features.require(data->requires()); + if (data && !data->required().empty()) + features.require(data->required()); } if (name() == "binom") @@ -915,7 +972,16 @@ void InsetMathMacro::validate(LaTeXFeatures & features) const // validate the cells and the definition if (displayMode() == DISPLAY_NORMAL) { - d->definition_.validate(features); + // Don't update requirements if the macro comes from + // the symbols file and has not been redefined. + MathWordList const & words = mathedWordList(); + MathWordList::const_iterator it = words.find(name()); + MacroNameSet macros; + buffer().listMacroNames(macros); + if (it == words.end() || it->second.inset != "macro" + || macros.find(name()) != macros.end()) { + d->definition_.validate(features); + } InsetMathNest::validate(features); } } @@ -939,7 +1005,7 @@ Inset * InsetMathMacro::editXY(Cursor & cur, int x, int y) } -void InsetMathMacro::removeArgument(Inset::pos_type pos) { +void InsetMathMacro::removeArgument(pos_type pos) { if (d->displayMode_ == DISPLAY_NORMAL) { LASSERT(size_t(pos) < cells_.size(), return); cells_.erase(cells_.begin() + pos); @@ -954,7 +1020,7 @@ void InsetMathMacro::removeArgument(Inset::pos_type pos) { } -void InsetMathMacro::insertArgument(Inset::pos_type pos) { +void InsetMathMacro::insertArgument(pos_type pos) { if (d->displayMode_ == DISPLAY_NORMAL) { LASSERT(size_t(pos) <= cells_.size(), return); cells_.insert(cells_.begin() + pos, MathData()); @@ -1067,12 +1133,10 @@ bool InsetMathMacro::folded() const } -void InsetMathMacro::write(WriteStream & os) const +void InsetMathMacro::write(TeXMathStream & os) const { - mode_type mode = modeToEnsure(); - bool textmode_macro = mode == TEXT_MODE; - bool needs_mathmode = mode == MATH_MODE; - MathEnsurer ensurer(os, needs_mathmode, true, textmode_macro); + mode_type mode = currentMode(); + MathEnsurer ensurer(os, mode == MATH_MODE, true, mode == TEXT_MODE); // non-normal mode if (d->displayMode_ != DISPLAY_NORMAL) { @@ -1086,15 +1150,6 @@ void InsetMathMacro::write(WriteStream & os) const // we should be ok to continue even if this fails. LATTEST(d->macro_); - // We may already be in the argument of a macro - bool const inside_macro = os.insideMacro(); - os.insideMacro(true); - - // Enclose in braces to avoid latex errors with xargs if we have - // optional arguments and are in the optional argument of a macro - if (d->optionals_ && inside_macro) - os << '{'; - // Always protect macros in a fragile environment if (os.fragile()) os << "\\protect"; @@ -1114,7 +1169,32 @@ void InsetMathMacro::write(WriteStream & os) const // print out optionals for (i=0; i < cells_.size() && i < emptyOptFrom; ++i) { first = false; - os << "[" << cell(i) << "]"; + // For correctly parsing it when a document is reloaded, we + // need to enclose an optional argument in braces if it starts + // with a script inset with empty nucleus or ends with a + // delimiter-size-modifier macro (see #10497 and #11346). + // We also need to do that when the optional argument + // contains macros with optionals. + bool braced = false; + size_type last = cell(i).size() - 1; + if (!cell(i).empty() && cell(i)[last]->asUnknownInset()) { + latexkeys const * l = in_word_set(cell(i)[last]->name()); + braced = (l && l->inset == "big"); + } else if (!cell(i).empty() && cell(i)[0]->asScriptInset()) { + braced = cell(i)[0]->asScriptInset()->nuc().empty(); + } else { + for (size_type j = 0; j < cell(i).size(); ++j) { + InsetMathMacro const * ma = cell(i)[j]->asMacro(); + if (ma && ma->optionals()) { + braced = true; + break; + } + } + } + if (braced) + os << "[{" << cell(i) << "}]"; + else + os << "[" << cell(i) << "]"; } // skip the tailing empty optionals @@ -1133,13 +1213,15 @@ void InsetMathMacro::write(WriteStream & os) const first = false; } - // Close the opened brace or add space if there was no argument - if (d->optionals_ && inside_macro) - os << '}'; - else if (first) + // add space if there was no argument + // or add braces if we have optionals but none are present and [ follows + if (first) { os.pendingSpace(true); + os.useBraces(d->optionals_ > 0); + } - os.insideMacro(inside_macro); + // write \(no)limits modifiers if relevant + writeLimits(os); } @@ -1161,16 +1243,17 @@ void InsetMathMacro::mathematica(MathematicaStream & os) const } -void InsetMathMacro::mathmlize(MathStream & os) const +void InsetMathMacro::mathmlize(MathMLStream & ms) const { // macro_ is 0 if this is an unknown macro LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL); if (d->macro_) { - docstring const xmlname = d->macro_->xmlname(); + docstring const xmlname = (ms.xmlMode()) ? d->macro_->xmlname() : d->macro_->htmlname(); if (!xmlname.empty()) { char const * type = d->macro_->MathMLtype(); - os << '<' << type << "> " << xmlname << " '; + ms << "<" << from_ascii(ms.namespacedTag(type)) << ">" + << xmlname + << ""; return; } } @@ -1178,7 +1261,7 @@ void InsetMathMacro::mathmlize(MathStream & os) const // this means that we do not recognize the macro throw MathExportException(); } - os << d->expanded_; + ms << d->expanded_; } @@ -1187,7 +1270,7 @@ void InsetMathMacro::htmlize(HtmlStream & os) const // macro_ is 0 if this is an unknown macro LATTEST(d->macro_ || d->displayMode_ != DISPLAY_NORMAL); if (d->macro_) { - docstring const xmlname = d->macro_->xmlname(); + docstring const xmlname = d->macro_->htmlname(); if (!xmlname.empty()) { os << ' ' << xmlname << ' '; return; @@ -1225,7 +1308,6 @@ bool InsetMathMacro::completionSupported(Cursor const & cur) const return InsetMathNest::completionSupported(cur); return lyxrc.completion_popup_math - && displayMode() == DISPLAY_UNFOLDED && cur.bv().cursor().pos() == int(name().size()); } @@ -1236,7 +1318,6 @@ bool InsetMathMacro::inlineCompletionSupported(Cursor const & cur) const return InsetMathNest::inlineCompletionSupported(cur); return lyxrc.completion_inline_math - && displayMode() == DISPLAY_UNFOLDED && cur.bv().cursor().pos() == int(name().size()); } @@ -1328,4 +1409,10 @@ void InsetMathMacro::completionPosAndDim(Cursor const & cur, int & x, int & y, } +void InsetMathMacro::setBuffer(Buffer & buffer) +{ + d->definition_.setBuffer(buffer); + InsetMathNest::setBuffer(buffer); +} + } // namespace lyx