]> git.lyx.org Git - features.git/commitdiff
Implement properly \limits and \nolimits
authorJean-Marc Lasgouttes <lasgouttes@lyx.org>
Sun, 19 Jul 2020 17:56:07 +0000 (19:56 +0200)
committerJean-Marc Lasgouttes <lasgouttes@lyx.org>
Mon, 20 Jul 2020 21:20:26 +0000 (23:20 +0200)
These are now properties of insets that can be operators :
InsetMathSymbols, InsetMathDecoration (for over/underbrace) and
InsetMathMacro (for its contents).

Each of these has a limit_ member that allows to remember a limit
forcing and a member defaultLimits() that indicates what to do in the
absence of such forcing. Moreover the write() method calls
writeLimits().

This allows to simplify the definitions of integrals in lib/symbols by
defining the integrals as macros of their "op" version, as it is done in
the style files.

Also, many hardcoded assumptions can now be removed.

The handling of LFUN_MATH_LIMITS is now done in InsetNest, which tries
successively to apply the limit change to (1) the character after
cursor, (2) the character before cursor and (3) the character at the
end of the inset (useful for script insets?)

The new code allows to define
  \newcommand\int{\intop\limits}
but not
  \newcommand\makelimits#1{#1\limits}

It is also possible to type explicitly \limits or \nolimits to modify
a symbol.

15 files changed:
lib/symbols
src/mathed/InsetMath.cpp
src/mathed/InsetMath.h
src/mathed/InsetMathDecoration.cpp
src/mathed/InsetMathDecoration.h
src/mathed/InsetMathMacro.cpp
src/mathed/InsetMathMacro.h
src/mathed/InsetMathNest.cpp
src/mathed/InsetMathScript.cpp
src/mathed/InsetMathScript.h
src/mathed/InsetMathSymbol.cpp
src/mathed/InsetMathSymbol.h
src/mathed/MathData.cpp
src/mathed/MathData.h
src/mathed/MathParser.cpp

index 4da0aac80f290bd46ba3f4ad3195a9c2b928bef8..09236fe559c59843be2763e1aa6a52445ce123bc 100644 (file)
@@ -1029,70 +1029,60 @@ tbond              cmsy        180 186 mathord  x  x
 # If the wasysym integrals are really wanted then one has to load the package
 # manually and disable automatic loading of amsmath and esint.
 iffont esint
-int                esint    001|002    0  mathop  &int;         &#x222B;        esint|amsmath
 intop              esint    001|002    0  mathop  &int;         &#x222B;        esint
-iint               esint    003|004    0  mathop  &Int;         &#x222C;        esint|amsmath
 iintop             esint    003|004    0  mathop  &Int;         &#x222C;        esint
-iiint              esint    005|006    0  mathop  &tint;        &#x222D;        esint|amsmath
 iiintop            esint    005|006    0  mathop  &tint;        &#x222D;        esint
-iiiint             esint    007|008    0  mathop  &qint;        &#x2A0C;        esint|amsmath
 iiiintop           esint    007|008    0  mathop  &qint;        &#x2A0C;        esint
 #9 codepoint forbidden in qt4, 10,12,13 in qt5
-oint               esint    043|044    0  mathop  &conint;      &#x222E;        esint
 ointop             esint    043|044    0  mathop  &conint;      &#x222E;        esint
-oiint              esint    045|046    0  mathop  &Conint;      &#x222F;        esint
 oiintop            esint    045|046    0  mathop  &Conint;      &#x222F;        esint
-sqint              esint    015|016    0  mathop  &quatint;     &#x2A16;        esint
 sqintop            esint    015|016    0  mathop  &quatint;     &#x2A16;        esint
-sqiint             esint    017|018    0  mathop  x             esint
 sqiintop           esint    017|017    0  mathop  x             esint
-dotsint            esint    041|042    0  mathop  &int;&ctdot;&int;     &#x222B;&#x22EF;&#x222B;    esint
 dotsintop          esint    041|042    0  mathop  &int;&ctdot;&int;     &#x222B;&#x22EF;&#x222B;    esint
-ointctrclockwise   esint    023|024    0  mathop  &awconint;    &#x2233;        esint
 ointctrclockwiseop esint    023|024    0  mathop  &awconint;    &#x2233;        esint
-ointclockwise      esint    025|026    0  mathop  &cwconint;    &#x2232;        esint
 ointclockwiseop    esint    025|026    0  mathop  &cwconint;    &#x2232;        esint
 else
-int                cmex       82|90  242  mathop  &int;         &#x222B;        esint|amsmath
 intop              cmex       82|90  242  mathop  &int;         &#x222B;        esint
-iint               wasy     115|120    0  mathop  &Int;         &#x222C;        esint|amsmath
 iintop             wasy     115|120    0  mathop  &Int          &#x222C;        esint
-iiint              wasy     116|121    0  mathop  &tint;        &#x222D;        esint|amsmath
 iiintop            wasy     116|121    0  mathop  &tint;        &#x222D;        esint
-\def\iiiint{\int\kern-6mu\int\kern-6mu\int\kern-6mu\int}        esint|amsmath
 \def\iiiintop{\int\kern-6mu\int\kern-6mu\int\kern-6mu\int}      esint
-\def\dotsint{\int\kern-3mu\cdots\kern-3mu\int}                  esint
-\def\dotsintop{\int\kern-3mu\cdots\kern-3mu\int}                esint
-oint               cmex       72|73    0  mathop  &conint;      &#x222E;        esint
 ointop             cmex       72|73    0  mathop  &conint;      &#x222E;        esint
-oiint              wasy     118|123    0  mathop  &Conint;      &#x222F;        esint
 oiintop            wasy     118|123    0  mathop  &Conint;      &#x222F;        esint
-\def\sqint{\square\kern-17mu\int\kern6mu}                       esint
 \def\sqintop{\square\kern-17mu\int\kern6mu}                     esint
-\def\sqiint{\square\kern-20mu\iint\kern3mu}                     esint
 \def\sqiintop{\square\kern-20mu\iint\kern3mu}                   esint
-\def\ointctrclockwise{\circlearrowleft\kern-21mu\int\kern6mu}   esint
+\def\dotsintop{\int\kern-3mu\cdots\kern-3mu\int}                esint
 \def\ointctrclockwiseop{\circlearrowleft\kern-21mu\int\kern6mu} esint
-\def\ointclockwise{\circlearrowright\kern-21mu\int\kern6mu}     esint
 \def\ointclockwiseop{\circlearrowright\kern-21mu\int\kern6mu}   esint
 endif
+\def\int{\intop\nolimits}           mathop  &int;         &#x222B;        esint|amsmath
+\def\iint{\iintop\nolimits}         mathop  &Int;         &#x222C;        esint|amsmath
+\def\iiint{\iiintop\nolimits}       mathop  &tint;        &#x222D;        esint|amsmath
+\def\iiiint{\iiiintop\nolimits}     mathop  &qint;        &#x2A0C;        esint|amsmath
+\def\oint{\ointop\nolimits}         mathop  &conint;      &#x222E;        esint
+\def\oiint{\oiintop\nolimits}       mathop  &Conint;      &#x222F;        esint
+\def\sqint{\sqintop\nolimits}       mathop  &quatint;     &#x2A16;        esint
+\def\sqiint{\sqiintop\nolimits}     mathop  x             esint
+\def\dotsint{\dotsintop\nolimits}   mathop  &int;&ctdot;&int;     &#x222B;&#x22EF;&#x222B;
+\def\ointctrclockwise{\ointctrclockwise\nolimits} mathop  &cwconint;    &#x2232;        esint
+\def\ointclockwise{\ointclockwise\nolimits}       mathop  &cwconint;    &#x2232;        esint
 
-varointclockwise   esint    027|028    0  mathop  &cwconint;    &#x2232;        esint
 varointclockwiseop esint    027|028    0  mathop  &cwconint;    &#x2232;        esint
-varointctrclockwise esint   029|030    0  mathop  &awconint;    &#x2233;        esint
 varointctrclockwiseop esint 029|030    0  mathop  &awconint;    &#x2233;        esint
-fint               esint    031|032    0  mathop  &#x2a0f;      &#x2a0f;        esint
 fintop             esint    031|032    0  mathop  &#x2a0f;      &#x2a0f;        esint
-varoiint           esint    033|034    0  mathop  &Conint;      &#x222F;        esint
 varoiintop         esint    033|034    0  mathop  &Conint;      &#x222F;        esint
-landupint          esint    035|036    0  mathop  x             x               esint
 landupintop        esint    035|036    0  mathop  x             x               esint
-landdownint        esint    037|038    0  mathop  x             x               esint
 landdownintop      esint    037|038    0  mathop  x             x               esint
 
+\def\varointclockwise{\varointclockwiseop\limits}       mathop  &cwconint;    &#x2232;        esint
+\def\varointctrclockwise{\varointctrclockwiseop\limits} mathop  &awconint;    &#x2233;        esint
+\def\fint{\fintop\limits}                mathop  &#x2a0f;      &#x2a0f;        esint
+\def\varoiint{\varoiintop\limits}        mathop  &Conint;      &#x222F;        esint
+\def\landupint{\landupintop\limits}      mathop  x             x               esint
+\def\landdownint{\landdownintop\limits}  mathop  x             x               esint
+
 
 # From the amsmath package:
-\def\idotsint{\int\kern-3mu\cdots\kern-3mu\int}                 amsmath
+\def\idotsint{\int\kern-3mu\cdots\kern-3mu\int\limits}                 amsmath
 
 
 log                lyxblacktext  0   0 func     x  x
index 8228153b95ceb99629481b0106cad0e3e76857e5..bddd82fe7a261df04a129cfff9f46f9fda9e2ea8 100644 (file)
@@ -52,12 +52,6 @@ MathData const & InsetMath::cell(idx_type) const
 }
 
 
-MathClass InsetMath::mathClass() const
-{
-       return MC_ORD;
-}
-
-
 InsetMath::marker_type InsetMath::marker(BufferView const *) const
 {
        return nargs() > 0 ? MARKER : NO_MARKER;
@@ -74,6 +68,19 @@ bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 }
 
 
+/// write LaTeX and LyX code
+void InsetMath::writeLimits(WriteStream & os) const
+{
+       if (limits() == LIMITS) {
+               os << "\\limits";
+               os.pendingSpace(true);
+       } else if (limits() == NO_LIMITS) {
+               os << "\\nolimits ";
+               os.pendingSpace(true);
+       }
+}
+
+
 void InsetMath::dump() const
 {
        lyxerr << "---------------------------------------------" << endl;
index 421cd162b99d6be92c7e529404bd3a64f4edda79..988d3c358ed7884d526dcf6aad6a3e60a885be2e 100644 (file)
@@ -39,6 +39,17 @@ enum HullType {
 HullType hullType(docstring const & name);
 docstring hullName(HullType type);
 
+
+enum Limits {
+       // what is obtained with \c \\nolimits
+       NO_LIMITS = -1,
+       // the default
+       AUTO_LIMITS = 0,
+       // what is obtained with \c \\limits
+       LIMITS = 1
+};
+
+
 /**
 
 Abstract base class for all math objects.  A math insets is for use of the
@@ -173,7 +184,7 @@ public:
        virtual InsetMathSpecialChar const * asSpecialCharInset() const { return nullptr; }
 
        /// The class of the math object (used primarily for spacing)
-       virtual MathClass mathClass() const;
+       virtual MathClass mathClass() const { return MC_ORD; }
        /// Add this inset to a math row. Return true if contents got added
        virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const;
        /// Hook that is run before metrics computation starts
@@ -185,15 +196,22 @@ public:
        /// Hook that is run after drawing
        virtual void afterDraw(PainterInfo const &) const {}
 
-       /// identifies things that can get scripts
-       virtual bool isScriptable() const { return false; }
        /// will this get written as a single block in {..}
        virtual bool extraBraces() const { return false; }
 
        /// return the content as char if the inset is able to do so
        virtual char_type getChar() const { return 0; }
-       /// identifies things that can get \limits or \nolimits
-       virtual bool takesLimits() const { return false; }
+
+       /// Whether the inset allows \(no)limits
+       bool allowsLimitsChange() const { return mathClass() == MC_OP; }
+       /// The default limits value
+       virtual Limits defaultLimits() const { return NO_LIMITS; }
+       /// whether the inset has limit-like sub/superscript
+       virtual Limits limits() const { return AUTO_LIMITS; }
+       /// sets types of sub/superscripts
+       virtual void limits(Limits) {}
+       /// write limits status for LaTeX and LyX code
+       void writeLimits(WriteStream & os) const;
 
        /// replace things by other things
        virtual void replace(ReplaceData &) {}
index 17a0a953c3da06c79ce7fea4d540f357c9f519b4..0349c69ee5f158a04997a38b5cb10cbccf272a3f 100644 (file)
@@ -38,7 +38,7 @@ namespace lyx {
 
 
 InsetMathDecoration::InsetMathDecoration(Buffer * buf, latexkeys const * key)
-       : InsetMathNest(buf, 1), key_(key), dh_(0), dy_(0), dw_(0)
+       : InsetMathNest(buf, 1), key_(key)
 {
 //     lyxerr << " creating deco " << key->name << endl;
 }
@@ -64,12 +64,6 @@ MathClass InsetMathDecoration::mathClass() const
 }
 
 
-bool InsetMathDecoration::isScriptable() const
-{
-       return mathClass() == MC_OP;
-}
-
-
 bool InsetMathDecoration::protect() const
 {
        return
@@ -151,6 +145,7 @@ void InsetMathDecoration::write(WriteStream & os) const
        os << '\\' << key_->name << '{';
        ModeSpecifier specifier(os, currentMode());
        os << cell(0) << '}';
+       writeLimits(os);
 }
 
 
index a5d5e9c9b8d53677a3fa8a87ca9665a8143cf378..d6d925230b93c68d0c3993ac1054f9a51db1f621 100644 (file)
@@ -39,8 +39,12 @@ public:
        void infoize(odocstream & os) const;
        ///
        MathClass mathClass() const;
-       ///
-       bool isScriptable() const;
+       /// The default limits value
+       Limits defaultLimits() const { return allowsLimitsChange() ? LIMITS : NO_LIMITS; }
+       /// whether the inset has limit-like sub/superscript
+       Limits limits() const { return limits_; }
+       /// sets types of sub/superscripts
+       void limits(Limits lim) { limits_ = lim; }
        ///
        void validate(LaTeXFeatures & features) const;
        ///
@@ -60,12 +64,15 @@ private:
 
        ///
        latexkeys const * key_;
+       ///
+       Limits limits_ = AUTO_LIMITS;
+       // FIXME: this should depend on BufferView
        /// height cache of deco
-       mutable int dh_;
+       mutable int dh_ = 0;
        /// vertical offset cache of deco
-       mutable int dy_;
+       mutable int dy_ = 0;
        /// width for non-wide deco
-       mutable int dw_;
+       mutable int dw_ = 0;
 };
 
 } // namespace lyx
index e119dbefefa7b69b4babaabaab552ae52ccbae8d..144318130ae52bddf49208f234c6b78e2bd5f37d 100644 (file)
@@ -371,6 +371,38 @@ bool InsetMathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
        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() 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() : in->limits();
+       LATTEST(lim != AUTO_LIMITS);
+       return lim;
+}
+
+
 void InsetMathMacro::beforeMetrics() const
 {
        d->macro_->lock();
@@ -1155,6 +1187,9 @@ void InsetMathMacro::write(WriteStream & os) const
        // add space if there was no argument
        if (first)
                os.pendingSpace(true);
+
+       // write \(no)limits modifiers if relevant
+       writeLimits(os);
 }
 
 
index 80340df6f761f26db52c7b9b77796676e5635d09..9a4bcc929fdf7158dfe13badb9c512220ffe5f7f 100644 (file)
@@ -41,6 +41,16 @@ public:
        /// If the macro is in normal edit mode, dissolve its contents in
        /// the row. Otherwise, just insert the inset.
        bool addToMathRow(MathRow &, MetricsInfo & mi) const;
+
+       /// Whether the inset allows \(no)limits
+       bool allowsLimitsChange() const;
+       /// The default limits value
+       Limits defaultLimits() const;
+       /// whether the inset has limit-like sub/superscript
+       Limits limits() const { return limits_; }
+       /// sets types of sub/superscripts
+       void limits(Limits lim) { limits_ = lim; }
+
        ///
        void beforeMetrics() const;
        ///
@@ -184,6 +194,9 @@ private:
        ///
        bool editMode(BufferView const * bv) const;
 
+       ///
+       Limits limits_ = AUTO_LIMITS;
+
        ///
        class Private;
        ///
index 1a943c97ee12ca18a8d18820290b3768525de304..06d1c12e56decab243ebb92b48c7972dbc834a8f 100644 (file)
@@ -518,7 +518,6 @@ void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
        // FIXME: support other font changes here as well?
 }
 
-
 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
        //LYXERR0("InsetMathNest: request: " << cmd);
@@ -1277,6 +1276,32 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
                }
                break;
 
+       case LFUN_MATH_LIMITS: {
+               InsetMath * in = 0;
+               if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+                       in = &cur.nextMath();
+               else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+                       in = &cur.prevMath();
+               else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+                       in = cur.cell().back().nucleus();
+               // only when nucleus allows this
+               if (!in)
+                       return;
+               cur.recordUndoInset();
+               if (!cmd.argument().empty()) {
+                       if (cmd.argument() == "limits")
+                               in->limits(LIMITS);
+                       else if (cmd.argument() == "nolimits")
+                               in->limits(NO_LIMITS);
+                       else
+                               in->limits(AUTO_LIMITS);
+               } else if (in->limits() == AUTO_LIMITS)
+                       in->limits(in->defaultLimits() == LIMITS ? NO_LIMITS : LIMITS);
+               else
+                       in->limits(AUTO_LIMITS);
+               return;
+       }
+
        default:
                InsetMath::doDispatch(cur, cmd);
                break;
@@ -1457,6 +1482,29 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
                break;
        }
 
+       case LFUN_MATH_LIMITS: {
+               InsetMath * in = 0;
+               if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
+                       in = &cur.nextMath();
+               else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
+                       in = &cur.prevMath();
+               else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
+                       in = cur.cell().back().nucleus();
+               if (in) {
+                       if (!cmd.argument().empty()) {
+                               if (cmd.argument() == "limits")
+                                       flag.setOnOff(in->limits() == LIMITS);
+                               else if (cmd.argument() == "nolimits")
+                                       flag.setOnOff(in->limits() == NO_LIMITS);
+                               else
+                                       flag.setOnOff(in->limits() == AUTO_LIMITS);
+                       }
+                       flag.setEnabled(true);
+               } else
+                       flag.setEnabled(false);
+               return true;
+       }
+
        default:
                ret = false;
                break;
@@ -1858,6 +1906,15 @@ bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
 
 bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
 {
+       if (str == "\\limits" || str == "\\nolimits") {
+               if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange()) {
+                       cur.prevMath().limits(str == "\\limits" ? LIMITS : NO_LIMITS);
+                       return true;
+               } else {
+                       cur.message(bformat(_("Cannot apply %1$s here."), str));
+                       return false;
+               }
+       }
        // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
        // possible
        if (!cur.empty() && cur.pos() > 0 &&
index 3e995fd99cdca1a8798cb80441116740e4e543d6..62aab03af0530fecddf8865fe9e82196975f6999 100644 (file)
 #include "InsetMathScript.h"
 
 #include "InsetMathBrace.h"
-#include "InsetMathSymbol.h"
 #include "MathData.h"
 #include "MathStream.h"
 #include "MathSupport.h"
 
 #include "BufferView.h"
 #include "Cursor.h"
-#include "DispatchResult.h"
-#include "FuncRequest.h"
-#include "FuncStatus.h"
 #include "LaTeXFeatures.h"
 #include "MetricsInfo.h"
 
@@ -37,17 +33,17 @@ namespace lyx {
 
 
 InsetMathScript::InsetMathScript(Buffer * buf)
-       : InsetMathNest(buf, 1), cell_1_is_up_(false), limits_(0)
+       : InsetMathNest(buf, 1), cell_1_is_up_(false)
 {}
 
 
 InsetMathScript::InsetMathScript(Buffer * buf, bool up)
-       : InsetMathNest(buf, 2), cell_1_is_up_(up), limits_(0)
+       : InsetMathNest(buf, 2), cell_1_is_up_(up)
 {}
 
 
 InsetMathScript::InsetMathScript(Buffer * buf, MathAtom const & at, bool up)
-       : InsetMathNest(buf, 2), cell_1_is_up_(up), limits_(0)
+       : InsetMathNest(buf, 2), cell_1_is_up_(up)
 {
        LATTEST(nargs() >= 1);
        cell(0).push_back(at);
@@ -179,7 +175,7 @@ int InsetMathScript::dy0(BufferView const & bv) const
        if (!hasDown())
                return nd;
        int des = down().dimension(bv).ascent();
-       if (hasLimits())
+       if (has_limits_)
                des += nd + 2;
        else {
                int na = nasc(bv);
@@ -195,7 +191,7 @@ int InsetMathScript::dy1(BufferView const & bv) const
        if (!hasUp())
                return na;
        int asc = up().dimension(bv).descent();
-       if (hasLimits())
+       if (has_limits_)
                asc += na + 2;
        else {
                int nd = ndes(bv);
@@ -210,7 +206,7 @@ int InsetMathScript::dx0(BufferView const & bv) const
 {
        LASSERT(hasDown(), return 0);
        Dimension const dim = dimension(bv);
-       return hasLimits() ? (dim.wid - down().dimension(bv).width()) / 2
+       return has_limits_ ? (dim.wid - down().dimension(bv).width()) / 2
                : nwid(bv) + min(nker(&bv), 0);
 }
 
@@ -219,7 +215,7 @@ int InsetMathScript::dx1(BufferView const & bv) const
 {
        LASSERT(hasUp(), return 0);
        Dimension const dim = dimension(bv);
-       return hasLimits() ? (dim.wid - up().dimension(bv).width()) / 2
+       return has_limits_ ? (dim.wid - up().dimension(bv).width()) / 2
                : nwid(bv) + max(nker(&bv), 0);
 }
 
@@ -227,7 +223,7 @@ int InsetMathScript::dx1(BufferView const & bv) const
 int InsetMathScript::dxx(BufferView const & bv) const
 {
        Dimension const dim = dimension(bv);
-       return hasLimits() ? (dim.wid - nwid(bv)) / 2  :  0;
+       return has_limits_ ? (dim.wid - nwid(bv)) / 2  :  0;
 }
 
 
@@ -275,16 +271,21 @@ MathClass InsetMathScript::mathClass() const
 
 void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
 {
-       Changer dummy2 = mi.base.changeEnsureMath();
        Dimension dim0;
        Dimension dim1;
        Dimension dim2;
-       cell(0).metrics(mi, dim0);
-       Changer dummy = mi.base.changeScript();
-       if (nargs() > 1)
-               cell(1).metrics(mi, dim1, !hasLimits());
-       if (nargs() > 2)
-               cell(2).metrics(mi, dim2, !hasLimits());
+       {
+               // Keeps the changers local
+               Changer dummy2 = mi.base.changeEnsureMath();
+               cell(0).metrics(mi, dim0);
+               Changer dummy = mi.base.changeScript();
+               if (nargs() > 1)
+                       cell(1).metrics(mi, dim1, !has_limits_);
+               if (nargs() > 2)
+                       cell(2).metrics(mi, dim2, !has_limits_);
+       }
+       // we store this, because it is much easier
+       has_limits_ = hasLimits(mi.base.font);
 
        dim.wid = 0;
        BufferView & bv = *mi.base.bv;
@@ -296,7 +297,7 @@ void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
        if (hasDown())
                dimdown = down().dimension(bv);
 
-       if (hasLimits()) {
+       if (has_limits_) {
                dim.wid = nwid(bv);
                if (hasUp())
                        dim.wid = max(dim.wid, dimup.width());
@@ -365,47 +366,17 @@ void InsetMathScript::drawT(TextPainter & pain, int x, int y) const
 }
 
 
-// FIXME: See InsetMathSymbol::takesLimits, which seems to attempt the
-// same in a hardcoded way. takeLimits use is currently commented out in
-// InsetMathScript::metrics. It seems that the mathop test is general
-// enough, but only time will tell.
-bool InsetMathScript::allowsLimits() const
-{
-       if (nuc().empty())
-               return false;
-       // Only makes sense for insets of mathop class
-       if (nuc().back()->mathClass() != MC_OP)
-               return false;
-       return true;
-}
-
-
-bool InsetMathScript::hasLimits() const
+bool InsetMathScript::hasLimits(FontInfo const & font) const
 {
-       // obvious cases
-       if (limits_ == 1)
-               return true;
-       if (limits_ == -1)
+       if (font.style() != DISPLAY_STYLE)
                return false;
-
-       // we can only display limits if the nucleus wants some
-       if (!allowsLimits())
-               return false;
-       // FIXME: this is some hardcoding done in InsetMathSymbol::metrics.
-       if (!nuc().back()->isScriptable())
+       if (nuc().empty())
                return false;
 
-       if (nuc().back()->asSymbolInset()) {
-               // \intop is an alias for \int\limits, \ointop == \oint\limits
-               if (nuc().back()->asSymbolInset()->name().find(from_ascii("intop")) != string::npos)
-                       return true;
-               // per default \int has limits beside the \int even in displayed formulas
-               if (nuc().back()->asSymbolInset()->name().find(from_ascii("int")) != string::npos)
-                       return false;
-       }
-
-       // assume "real" limits for everything else
-       return true;
+       Limits const lim = nuc().back()->limits() == AUTO_LIMITS
+               ? nuc().back()->defaultLimits() : nuc().back()->limits();
+       LASSERT(lim != AUTO_LIMITS, return false);
+       return lim == LIMITS;
 }
 
 
@@ -481,7 +452,8 @@ bool InsetMathScript::idxUpDown(Cursor & cur, bool up) const
                        return false;
                // go up/down only if in the last position
                // or in the first position of something with displayed limits
-               if (cur.pos() == cur.lastpos() || (cur.pos() == 0 && hasLimits())) {
+               if (cur.pos() == cur.lastpos()
+                                || (cur.pos() == 0 && has_limits_)) {
                        cur.idx() = idxOfScript(up);
                        cur.pos() = 0;
                        return true;
@@ -519,20 +491,12 @@ void InsetMathScript::write(WriteStream & os) const
 {
        MathEnsurer ensurer(os);
 
-       if (!nuc().empty()) {
+       if (!nuc().empty())
                os << nuc();
-               //if (nuc().back()->takesLimits()) {
-                       if (limits_ == -1)
-                               os << "\\nolimits ";
-                       if (limits_ == 1)
-                               os << "\\limits ";
-               //}
-       } else {
-               if (os.firstitem())
-                       LYXERR(Debug::MATHED, "suppressing {} when writing");
-               else
-                       os << "{}";
-       }
+       else if (os.firstitem())
+               LYXERR(Debug::MATHED, "suppressing {} when writing");
+       else
+               os << "{}";
 
        if (hasDown() /*&& !down().empty()*/)
                os << "_{" << down() << '}';
@@ -616,7 +580,8 @@ void InsetMathScript::mathmlize(MathStream & ms) const
 {
        bool d = hasDown() && !down().empty();
        bool u = hasUp() && !up().empty();
-       bool l = hasLimits();
+       // FIXME: the MathStream should be able to give us this information
+       bool l = has_limits_;
 
        if (u && d)
                ms << MTag(l ? "munderover" : "msubsup");
@@ -680,8 +645,7 @@ void InsetMathScript::infoize(odocstream & os) const
 
 void InsetMathScript::infoize2(odocstream & os) const
 {
-       if (limits_)
-               os << from_ascii(limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
+       os << from_ascii(has_limits_ == 1 ? ", Displayed limits" : ", Inlined limits");
 }
 
 
@@ -745,57 +709,6 @@ bool InsetMathScript::notifyCursorLeaves(Cursor const & old, Cursor & cur)
 }
 
 
-void InsetMathScript::doDispatch(Cursor & cur, FuncRequest & cmd)
-{
-       //LYXERR("InsetMathScript: request: " << cmd);
-
-       if (cmd.action() == LFUN_MATH_LIMITS) {
-               // only when nucleus allows this
-               if (!allowsLimits())
-                       return;
-               cur.recordUndoInset();
-               if (!cmd.argument().empty()) {
-                       if (cmd.argument() == "limits")
-                               limits_ = 1;
-                       else if (cmd.argument() == "nolimits")
-                               limits_ = -1;
-                       else
-                               limits_ = 0;
-               } else if (limits_ == 0)
-                       limits_ = hasLimits() ? -1 : 1;
-               else
-                       limits_ = 0;
-               return;
-       }
-
-       InsetMathNest::doDispatch(cur, cmd);
-}
-
-
-bool InsetMathScript::getStatus(Cursor & cur, FuncRequest const & cmd,
-                               FuncStatus & flag) const
-{
-       if (cmd.action() == LFUN_MATH_LIMITS) {
-               // only when nucleus allows this
-               if (allowsLimits()) {
-                       if (!cmd.argument().empty()) {
-                               if (cmd.argument() == "limits")
-                                       flag.setOnOff(limits_ == 1);
-                               else if (cmd.argument() == "nolimits")
-                                       flag.setOnOff(limits_ == -1);
-                               else
-                                       flag.setOnOff(limits_ == 0);
-                       }
-                       flag.setEnabled(true);
-               } else
-                       flag.setEnabled(false);
-               return true;
-       }
-
-       return InsetMathNest::getStatus(cur, cmd, flag);
-}
-
-
 // the idea for dual scripts came from the eLyXer code
 void InsetMathScript::validate(LaTeXFeatures & features) const
 {
index 7d7d852fb1211ef05867cf01a388ba86ee384659..bc048e2f1c2f61ab30c44e5c3a734d8a891127b3 100644 (file)
@@ -72,10 +72,6 @@ public:
        ///
        InsetMathScript * asScriptInset();
 
-       /// set limits
-       void limits(int lim) { limits_ = lim; }
-       /// get limits
-       int limits() const { return limits_; }
        /// returns subscript. Always run 'hasDown' or 'has(false)' before!
        MathData const & down() const;
        /// returns subscript. Always run 'hasDown' or 'has(false)' before!
@@ -108,11 +104,6 @@ public:
        InsetCode lyxCode() const { return MATH_SCRIPT_CODE; }
        ///
        void validate(LaTeXFeatures &features) const;
-protected:
-       virtual void doDispatch(Cursor & cur, FuncRequest & cmd);
-       /// do we want to handle this event?
-       bool getStatus(Cursor & cur, FuncRequest const & cmd,
-               FuncStatus & status) const;
 private:
        virtual Inset * clone() const;
        /// returns x offset for main part
@@ -138,17 +129,15 @@ private:
        /// shifts the superscript to the right, and a negative value shifts the
        /// subscript to the left.
        int nker(BufferView const * bv) const;
-       /// can one change how scripts are drawn?
-       bool allowsLimits() const;
-       /// where do we have to draw the scripts?
-       bool hasLimits() const;
+       /// do we we have to draw the scripts above/below nucleus?
+       bool hasLimits(FontInfo const &) const;
        /// clean up empty cells and return true if a cell has been deleted.
        bool notifyCursorLeaves(Cursor const & old, Cursor & cur);
 
        /// possible subscript (index 0) and superscript (index 1)
        bool cell_1_is_up_;
-       /// 1 - "limits", -1 - "nolimits", 0 - "default"
-       int limits_;
+       /// remember whether we are in display mode (used by mathml output)
+       mutable bool has_limits_ = false;
 };
 
 
index d354d016a5d0cce4f7e48f3e9fd42d90542e5cbb..276fab9ddcc8918836bdc3ab9725f5105fae1132 100644 (file)
 #include "support/textutils.h"
 #include "support/unique_ptr.h"
 
+using namespace std;
 
 namespace lyx {
 
 InsetMathSymbol::InsetMathSymbol(latexkeys const * l)
-       : sym_(l), h_(0), kerning_(0), scriptable_(false)
+       : sym_(l)
 {}
 
 
 InsetMathSymbol::InsetMathSymbol(char const * name)
-       : sym_(in_word_set(from_ascii(name))), h_(0),
-         kerning_(0), scriptable_(false)
+       : sym_(in_word_set(from_ascii(name)))
 {}
 
 
 InsetMathSymbol::InsetMathSymbol(docstring const & name)
-       : sym_(in_word_set(name)), h_(0), kerning_(0), scriptable_(false)
+       : sym_(in_word_set(name))
 {}
 
 
@@ -58,6 +58,14 @@ docstring InsetMathSymbol::name() const
 }
 
 
+/// The default limits value
+Limits InsetMathSymbol::defaultLimits() const
+{
+       return (allowsLimitsChange() && sym_->extra != "func")
+                       ? LIMITS : NO_LIMITS;
+}
+
+
 void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        mathedSymbolDim(mi.base, dim, sym_);
@@ -82,16 +90,6 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
                dim.asc += h_;
                dim.des -= h_;
        }
-
-       // set scriptable_
-       //FIXME: get rid of that? Only "funclim" probably, along with
-       // class==MC_OP. The issue is to implement \limits properly.
-       scriptable_ = false;
-       if (mi.base.font.style() == DISPLAY_STYLE)
-               if (sym_->inset == "cmex" || sym_->inset == "esint" ||
-                   sym_->extra == "funclim" ||
-                   (sym_->inset == "stmry" && sym_->extra == "mathop"))
-                       scriptable_ = true;
 }
 
 
@@ -122,23 +120,6 @@ MathClass InsetMathSymbol::mathClass() const
 }
 
 
-bool InsetMathSymbol::isScriptable() const
-{
-       return scriptable_;
-}
-
-
-bool InsetMathSymbol::takesLimits() const
-{
-       return
-               sym_->inset == "cmex" ||
-               sym_->inset == "lyxboldsymb" ||
-               sym_->inset == "esint" ||
-               sym_->extra == "funclim" ||
-               (sym_->inset == "stmry" && sym_->extra == "mathop");
-}
-
-
 void InsetMathSymbol::normalize(NormalStream & os) const
 {
        os << "[symbol " << name() << ']';
@@ -242,6 +223,7 @@ void InsetMathSymbol::write(WriteStream & os) const
                return;
 
        os.pendingSpace(true);
+       writeLimits(os);
 }
 
 
index 67b33fd85a96dc19b03994afc47fc7963bcd520a..bbf37f9673464a07e747ba37292510212c1b611d 100644 (file)
@@ -19,9 +19,7 @@ namespace lyx {
 class latexkeys;
 
 
-/** "normal" symbols that don't take limits and don't grow in displayed
- *  formulae.
- */
+// \xxx symbols that may take limits or grow in displayed formulæ.
 class InsetMathSymbol : public InsetMath {
 public:
        ///
@@ -43,10 +41,12 @@ public:
        MathClass mathClass() const;
        ///
        bool isOrdAlpha() const;
-       /// do we take scripts?
-       bool isScriptable() const;
-       /// do we take \limits or \nolimits?
-       bool takesLimits() const;
+       /// The default limits value
+       Limits defaultLimits() const;
+       /// whether the inset has limit-like sub/superscript
+       Limits limits() const { return limits_; }
+       /// sets types of sub/superscripts
+       void limits(Limits lim) { limits_ = lim; }
        /// identifies SymbolInset as such
        InsetMathSymbol const * asSymbolInset() const { return this; }
        /// the LaTeX name of the symbol (without the backslash)
@@ -83,11 +83,14 @@ private:
        ///
        latexkeys const * sym_;
        ///
-       mutable int h_;
-       /// cached superscript kerning
-       mutable int kerning_;
+       Limits limits_ = AUTO_LIMITS;
+
+       // FIXME: these depend on BufferView
+
        ///
-       mutable bool scriptable_;
+       mutable int h_ = 0;
+       /// cached superscript kerning
+       mutable int kerning_ = 0;
 };
 
 } // namespace lyx
index 42aa3cbb9b15b6b4c6940cd9e6f175c1b6833538..90f41eb242c89dd9391e4b0cf97e169c103302e1 100644 (file)
@@ -966,6 +966,18 @@ MathClass MathData::mathClass() const
 }
 
 
+MathClass MathData::lastMathClass() const
+{
+       MathClass res = MC_ORD;
+       for (MathAtom const & at : *this) {
+               MathClass mc = at->mathClass();
+               if (mc != MC_UNKNOWN)
+                       return res = mc;
+       }
+       return res;
+}
+
+
 ostream & operator<<(ostream & os, MathData const & ar)
 {
        odocstringstream oss;
index fa6e80d31b429acbb22375a2e6d2a97fc627e178..34749802fedabfc6af1029c8515aeede9be04185 100644 (file)
@@ -140,8 +140,10 @@ public:
        void metricsT(TextMetricsInfo const & mi, Dimension & dim) const;
        /// redraw cell using cache metrics information
        void drawT(TextPainter & pi, int x, int y) const;
-       /// approximate the math class of the data
+       /// approximate mathclass of the data
        MathClass mathClass() const;
+       /// math class of last interesting element
+       MathClass lastMathClass() const;
 
        /// access to cached x coordinate of last drawing
        int xo(BufferView const & bv) const;
index 56ca6dbe2b8389fff6572c894c12a745b653d410..c3bea0d540e7363c79ed3159d4f4cbe6634be7d0 100644 (file)
@@ -797,7 +797,6 @@ void Parser::parse2(MathAtom & at, const unsigned flags, const mode_type mode,
 bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
        const mode_type mode, const bool numbered)
 {
-       int limits = 0;
        InsetMathGrid::row_type cellrow = 0;
        InsetMathGrid::col_type cellcol = 0;
        MathData * cell = &grid.cell(grid.index(cellrow, cellcol));
@@ -1006,10 +1005,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                p->nuc().erase(0);
 
                        parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode);
-                       if (limits) {
-                               p->limits(limits);
-                               limits = 0;
-                       }
                }
 
                else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
@@ -1408,9 +1403,8 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                }
 
                else if (t.cs() == "limits" || t.cs() == "nolimits") {
-                       CatCode const cat = nextToken().cat();
-                       if (cat == catSuper || cat == catSub)
-                               limits = t.cs() == "limits" ? 1 : -1;
+                       if (!cell->empty())
+                               cell->back()->limits(t.cs() == "limits" ? LIMITS : NO_LIMITS);
                        else {
                                MathAtom at = createInsetMath(t.cs(), buf);
                                cell->push_back(at);