The branch, betterspacing, has been updated.
  discards  6fd73db634edae49e412de4b8f96c5408f91694e (commit)
  discards  0890cdd65cc4473950c95c36284cd94949cbc182 (commit)
  discards  d6257cc226891123dd19157529465c85ad973d68 (commit)
  discards  d26d2b1a5b586a09121902462589b99b3e25cf97 (commit)
  discards  18bb4ae1dfbdb8a3df60011dacb1b6f7f2b77a35 (commit)
  discards  341a6b899934002b755c6ce5937ef4900b831f7b (commit)
  discards  e722866b05c8f3c8beb58d408b7101cff0a57b4f (commit)
  discards  168ba261275c7b3ea99d413cc981a86334376b86 (commit)
  discards  3e0bb2f442917db8de563506306e8dc796da14d4 (commit)
  discards  e12dabcf427c21c8f40fda5d42744f652cddeb26 (commit)
  discards  ba84c78f013193883f5277932ebaa5c87a0bf7d3 (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (6fd73db634edae49e412de4b8f96c5408f91694e)
            \
             N -- N -- N (849c811c1e09482ed80db1c1438b3b9eefef66fd)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

- Log -----------------------------------------------------------------

commit 849c811c1e09482ed80db1c1438b3b9eefef66fd
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Thu Oct 13 20:07:45 2016 +0200

    Cleanup of lib/symbols
    
    Try as far as possible to use the same definitions as in the LaTeX files.
    
    An example of that is the definition and subsequent use of \joinrel,
    \relbar and \Relbar.

diff --git a/lib/symbols b/lib/symbols
index 7b969ad..4d2fc16 100644
--- a/lib/symbols
+++ b/lib/symbols
@@ -291,7 +291,7 @@ spadesuit          cmsy        127 170 mathord  &spades;
 lyxnot             cmsy         54  47 mathrel  /
 iffont cmsy
 # 9mu = 0.5em which is the extra space added to relation operators
-\def\not{\lyxnot\kern-9mu}
+\def\not{\lyxnot}
 else
 \def\not{\kern4mu\lyxnot\kern-19mu}
 endif
@@ -659,8 +659,7 @@ hslash             msb         125   0 mathord  &plankv;
 hbar               msb         126   0 mathord  &planck;
 backepsilon        msb         127   0 mathrel  &bepsi;
 
-lyxbar             cmsy        161   0 mathord  &mdash;
-lyxeq              cmr          61   0 mathord  =
+lyxbar             cmsy        161   0 mathrel  &mdash;
 lyxdabar           msa          57   0 mathord  &ndash;
 lyxright           msa          75   0 mathord  &rarr;
 lyxleft            msa          76   0 mathord  &larr;
@@ -962,14 +961,14 @@ bignplus           stmry 112   0 mathop     x  stmaryrd # 
caution: named hugenpl
 #rrbracketex        stmry 127   0 mathclose  x  stmaryrd # only in the font, 
not the .sty caution: named Hugerrbracketex in the font
 
 \def\varcopyright{c\kern-14mu\varbigcirc}                   stmaryrd
-\def\longarrownot{\kern5.5mu\arrownot\kern-5.5mu}           stmaryrd
-\def\Longarrownot{\kern5.5mu\Arrownot\kern-5.5mu}           stmaryrd
-\def\Mapsto{\Mapstochar\kern-9mu\Rightarrow}                stmaryrd
+\def\longarrownot{\mathrel{\kern5.5mu}\arrownot\mathrel{\kern-5.5mu}} stmaryrd
+\def\Longarrownot{\mathrel{\kern5.5mu}\Arrownot\mathrel{\kern-5.5mu}} stmaryrd
+\def\Mapsto{\Mapstochar\Rightarrow}                         stmaryrd
 \def\mapsfrom{\leftarrow\kern-9mu\mapsfromchar}             stmaryrd
 \def\Mapsfrom{\Leftarrow\kern-9mu\Mapsfromchar}             stmaryrd
-\def\Longmapsto{\Mapstochar\kern-7mu\Longrightarrow}        stmaryrd
-\def\longmapsfrom{\longleftarrow\kern-7mu\mapsfromchar}     stmaryrd
-\def\Longmapsfrom{\Longleftarrow\kern-7mu\Mapsfromchar}     stmaryrd
+\def\Longmapsto{\Mapstochar\Longrightarrow}                 stmaryrd
+\def\longmapsfrom{\longleftarrow\mapsfromchar}              stmaryrd
+\def\Longmapsfrom{\Longleftarrow\Mapsfromchar}              stmaryrd
 
 # symbols from the mhchem package, all of them are equivalent to a math symbol
 # mhchem is not loaded because these commands can only be used inside
@@ -1128,29 +1127,32 @@ pod                lyxblacktext  0   0 func     x     
amsmath
 \def\notin{\not\in}                                             mathrel &notin;
 \def\slash{/}
 
-\def\longleftrightarrow{\leftarrow\kern-12.5mu\rightarrow}
-\def\Longleftrightarrow{\Leftarrow\kern-12.5mu\Rightarrow}
-\def\iff{\Leftarrow\kern-12.5mu\Rightarrow}
+\def\joinrel{\mathrel{\kern-3mu}}
+\def\relbar{\lyxbar}
+\def\Relbar{\mathrel{=}}
+\def\longleftrightarrow{\leftarrow\joinrel\rightarrow}
+\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow}
+\def\iff{\Leftarrow\joinrel\Rightarrow}
 \def\doteq{\stackrel{\cdot}{=}}
 
 iffont cmsy
-\def\longrightarrow{\lyxbar\kern-11mu\rightarrow}               mathrel &xrarr;
-\def\longleftarrow{\leftarrow\kern-11mu\lyxbar}                 mathrel &xlarr;
-\def\Longrightarrow{\lyxeq\kern-9.5mu\Rightarrow}               mathrel 
&#x27F9;
-\def\Longleftarrow{\Leftarrow\kern-9.5mu\lyxeq}                 mathrel 
&#x27F8;
+\def\longrightarrow{\relbar\joinrel\rightarrow}                 mathrel &xrarr;
+\def\longleftarrow{\leftarrow\joinrel\relbar}                   mathrel &xlarr;
+\def\Longrightarrow{\Relbar\joinrel\Rightarrow}                 mathrel 
&#x27F9;
+\def\Longleftarrow{\Leftarrow\joinrel\Relbar}                   mathrel 
&#x27F8;
 \def\implies{\Longrightarrow}                                   mathrel 
&#x27F9; amsmath
 \def\impliedby{\Longleftarrow}                                  mathrel 
&#x27F8; amsmath
-\def\mapsto{\mapstochar\kern-9mu\rightarrow}                    mathrel 
&#x21A4;
-\def\longmapsto{\mapstochar\kern-6mu\lyxbar\kern-11mu\rightarrow} mathrel 
&#x27FB;
-\def\models{\vert\kern-7mu\lyxeq}                               mathrel &vDash;
+\def\mapsto{\mapstochar\rightarrow}                             mathrel 
&#x21A4;
+\def\longmapsto{\mapstochar\relbar\rightarrow}                  mathrel 
&#x27FB;
+\def\models{\mathrel{\vert}\joinrel\Relbar}                         mathrel 
&vDash;
 else
 \def\implies{=>}                                                mathrel 
&#x27F9; amsmath
 \def\impliedby{<=}                                              mathrel 
&#x27F8; amsmath
 endif
 iffont cmm
-\def\hookrightarrow{\lhook\kern-12mu\rightarrow}                mathrel 
&#x21AA;
-\def\hookleftarrow{\leftarrow\kern-12mu\rhook}                  mathrel 
&#x21A9;
-\def\bowtie{\triangleright\kern-6mu\triangleleft}               mathrel 
&#x22C8;
+\def\hookrightarrow{\lhook\joinrel\rightarrow}                  mathrel 
&#x21AA;
+\def\hookleftarrow{\leftarrow\joinrel\rhook}                    mathrel 
&#x21A9;
+\def\bowtie{\mathrel\triangleright\joinrel\mathrel\triangleleft} mathrel 
&#x22C8;
 endif
 iffont msa
 \def\dashrightarrow{\lyxdabar\lyxdabar\lyxright}                mathrel 
&#x290F; amssymb
@@ -1161,17 +1163,17 @@ else
 endif
 \def\dasharrow{\dashrightarrow}                                 mathrel 
&#x290F; amssymb
 iffont msb
-\def\Join{\ltimes\kern-18.5mu\rtimes}                           amssymb
+\def\Join{\mathrel{\ltimes\kern-13.5mu\rtimes}}                 amssymb
 else
 \def\Join{|x|}                                                  amssymb
 endif
-# Fixme: latin-1 chars in text file
+# FIXME: UTF-8 chars in text file
 \def\AA{\AA}{Å} textmode &Aring;  amstext,lyxmathsym
 \def\O{\O}{Ø}   textmode &Oslash; amstext,lyxmathsym
 
 iffont cmsy
 # The \sim is placed too high...
-\def\cong{\stackrel{_\sim}{=}}                                  mathrel &cong;
+\def\cong{\stackrel{\sim}{=}}                                   mathrel &cong;
 lyxsurd               cmsy        112 0 mathord  &radic;
 \def\surd{^\lyxsurd}                                            mathord &radic;
 \def\textdegree{\kern-1mu^{\circ}\kern-4mu} textmode &deg; 
textcomp,amstext,lyxmathsym

commit 65279ea6b7c54bfa74feb944e7b0dbfe56a7c706
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 5 22:14:48 2016 +0200

    Only display a blue rectangle for editable empty insets
    
    Empty insets should use a minimal amount of space, especially when
    they are part of a built-in macro in lib/symbols.
    
    With this change, blue rectangles signal actually editable places.
    Empty macros in editable data are shown as grey boxes, but they do not
    appear when further nested.
    
    This is done by adding a new type BOX of MathAtomSpacing object and a
    MetricsInfo::macro_nesting that keeps track of macros (and is reset to
    0 in editable macro arguments).

diff --git a/src/Dimension.h b/src/Dimension.h
index bd8f10d..0607be6 100644
--- a/src/Dimension.h
+++ b/src/Dimension.h
@@ -32,6 +32,8 @@ public:
        void operator+=(Dimension const & dim);
        /// set to empty box
        void clear() { wid = asc = des = 0; }
+       /// check if box is empty
+       bool empty() const { return wid == 0 && asc == 0 && wid == 0; }
        /// get height
        int height() const { return asc + des; }
        /// get ascent
diff --git a/src/MetricsInfo.cpp b/src/MetricsInfo.cpp
index 589e924..82ec7b4 100644
--- a/src/MetricsInfo.cpp
+++ b/src/MetricsInfo.cpp
@@ -87,7 +87,7 @@ Changer MetricsBase::changeFontSet(string const & name, bool 
cond)
 
 MetricsInfo::MetricsInfo(BufferView * bv, FontInfo font, int textwidth,
                          MacroContext const & mc)
-       : base(bv, font, textwidth), macrocontext(mc)
+       : base(bv, font, textwidth), macro_nesting(0), macrocontext(mc)
 {}
 
 
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index d2dd8b7..415fe25 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -101,6 +101,8 @@ public:
 
        ///
        MetricsBase base;
+       /// count wether the current mathdata is nested in macro(s)
+       int macro_nesting;
        /// The context to resolve macros
        MacroContext const & macrocontext;
 };
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index 23c9bd0..d2f73ad 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -56,9 +56,9 @@ MathClass InsetMath::mathClass() const
 }
 
 
-bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const
+bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 {
-       MathAtomSpacing mas;
+       MathAtomSpacing mas(MathAtomSpacing::INSET, mi);
        mas.inset = this;
        mas.mclass = mathClass();
        mrow.push_back(mas);
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 3d3e1ef..92e4055 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -167,7 +167,7 @@ public:
        /// The class of the math object (used primarily for spacing)
        virtual MathClass mathClass() const;
        /// Add this inset to a math row. Return true if contents got added
-       virtual bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const;
 
        /// identifies things that can get scripts
        virtual bool isScriptable() const { return false; }
diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp
index 8b7a89d..f2100af 100644
--- a/src/mathed/MathData.cpp
+++ b/src/mathed/MathData.cpp
@@ -31,7 +31,6 @@
 #include "mathed/InsetMathUnknown.h"
 
 #include "frontends/FontMetrics.h"
-#include "frontends/Painter.h"
 
 #include "support/debug.h"
 #include "support/docstream.h"
@@ -217,7 +216,7 @@ void MathData::touch() const
 }
 
 
-bool MathData::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 {
        bool has_contents = false;
        BufferView * bv = mi.base.bv;
@@ -275,12 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
        slevel_ = (4 * xascent) / 5;
        sshift_ = xascent / 4;
 
-       if (empty()) {
-               // Cache the dimension.
-               mi.base.bv->coordCache().arrays().add(this, dim);
-               return;
-       }
-
        MathRow mrow(mi, this);
        mrow_cache_[mi.base.bv] = mrow;
        mrow.metrics(mi, dim);
@@ -299,11 +292,6 @@ void MathData::draw(PainterInfo & pi, int const x, int 
const y) const
 
        Dimension const & dim = bv.coordCache().getArrays().dim(this);
 
-       if (empty()) {
-               pi.pain.rectangle(x, y - dim.ascent(), dim.width(), 
dim.height(), Color_mathline);
-               return;
-       }
-
        // don't draw outside the workarea
        if (y + dim.descent() <= 0
                || y - dim.ascent() >= bv.workHeight()
diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h
index fa82ee9..9eae466 100644
--- a/src/mathed/MathData.h
+++ b/src/mathed/MathData.h
@@ -122,7 +122,7 @@ public:
        MathAtom const & operator[](pos_type) const;
 
        /// Add this array to a math row. Return true if contents got added
-       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       bool addToMathRow(MathRow &, MetricsInfo & mi) const;
 
        /// rebuild cached metrics information
        void metrics(MetricsInfo & mi, Dimension & dim) const;
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 9cc6c79..ffe9506 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,28 +68,48 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
-       bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
-       {
-               MathAtomSpacing mas(MathAtomSpacing::BEG_ARG);
-               mas.macro = mathMacro_;
-               mas.ar = &mathMacro_->cell(idx_);
-               mrow.push_back(mas);
+       bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const {
+               // macro arguments are in macros
+               LATTEST(mi.macro_nesting > 0);
+               if (mi.macro_nesting == 1)
+                       mi.macro_nesting = 0;
+
+               MathAtomSpacing mas_beg(MathAtomSpacing::BEG_ARG, mi);
+               mas_beg.macro = mathMacro_;
+               mas_beg.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(mas_beg);
 
                mathMacro_->macro()->unlock();
-               bool const has_contents = 
mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               bool has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, 
mi);
                mathMacro_->macro()->lock();
 
-               mas.kind = MathAtomSpacing::END_ARG;
-               mrow.push_back(mas);
+               if (mi.macro_nesting == 0)
+                       mi.macro_nesting = 1;
 
-               if (has_contents)
-                       return true;
-               // if there was no contents, then we insert the empty macro 
inset
-               // instead.
-               return InsetMath::addToMathRow(mrow, mi);
+               // if there was no contents, and the contents is editable,
+               // then we insert a box instead.
+               if (!has_contents && mi.macro_nesting == 1) {
+                       MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+                       mas.color = Color_mathline;
+                       mrow.push_back(mas);
+                       has_contents = true;
+               }
+
+               MathAtomSpacing mas_end(MathAtomSpacing::END_ARG, mi);
+               mas_end.macro = mathMacro_;
+               mas_end.ar = &mathMacro_->cell(idx_);
+
+               mrow.push_back(mas_end);
+
+               return has_contents;
        }
        ///
        void metrics(MetricsInfo & mi, Dimension & dim) const {
+               // macro arguments are in macros
+               LATTEST(mi.macro_nesting > 0);
+               if (mi.macro_nesting == 1)
+                       mi.macro_nesting = 0;
+
                mathMacro_->macro()->unlock();
                mathMacro_->cell(idx_).metrics(mi, dim);
 
@@ -98,6 +118,8 @@ public:
                        def_.metrics(mi, dim);
 
                mathMacro_->macro()->lock();
+               if (mi.macro_nesting == 0)
+                       mi.macro_nesting = 1;
        }
        // write(), normalize(), infoize() and infoize2() are not needed since
        // MathMacro uses the definition and not the expanded cells.
@@ -287,31 +309,42 @@ MathMacro::~MathMacro()
 }
 
 
-bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
 {
        // set edit mode for which we will have calculated row.
        // This is the same as what is done in metrics().
        d->editing_[mi.base.bv] = editMode(mi.base.bv);
 
-       if (displayMode() == MathMacro::DISPLAY_NORMAL
-           && !d->editing_[mi.base.bv]) {
-               MathAtomSpacing mas(MathAtomSpacing::BEG_MACRO);
-               mas.macro = this;
-               mrow.push_back(mas);
+       if (displayMode() != MathMacro::DISPLAY_NORMAL
+           || d->editing_[mi.base.bv])
+               return InsetMath::addToMathRow(mrow, mi);
 
-               d->macro_->lock();
-               bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
-               d->macro_->unlock();
+       ++mi.macro_nesting;
 
-               mas.kind = MathAtomSpacing::END_MACRO;
-               mrow.push_back(mas);
+       MathAtomSpacing mas_beg(MathAtomSpacing::BEG_MACRO, mi);
+       mas_beg.macro = this;
+       mrow.push_back(mas_beg);
 
-               if (has_contents)
-                       return true;
-               // if there was no contents, then we insert the empty macro 
inset
-               // instead.
+       d->macro_->lock();
+       bool has_contents = d->expanded_.addToMathRow(mrow, mi);
+       d->macro_->unlock();
+
+       // if there was no contents and the array is editable, then we
+       // insert a grey box instead.
+       if (!has_contents && mi.macro_nesting == 1) {
+               MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+               mas.color = Color_mathmacroblend;
+               mrow.push_back(mas);
+               has_contents = true;
        }
-       return InsetMath::addToMathRow(mrow, mi);
+
+       --mi.macro_nesting;
+
+       MathAtomSpacing mas_end(MathAtomSpacing::END_MACRO, mi);
+       mas_end.macro = this;
+       mrow.push_back(mas_end);
+
+       return has_contents;
 }
 
 
@@ -407,6 +440,9 @@ bool MathMacro::editMetrics(BufferView const * bv) const
 
 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
 {
+       // the macro contents is not editable (except the arguments)
+       ++mi.macro_nesting;
+
        // set edit mode for which we will have calculated metrics. But only
        d->editing_[mi.base.bv] = editMode(mi.base.bv);
 
@@ -495,6 +531,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        dim.des += 2;
                }
        }
+
+       // restore macro nesting
+       --mi.macro_nesting;
 }
 
 
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 9c6fdb1..4eb93ba 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -39,7 +39,7 @@ public:
        ///
        /// If the macro is in normal edit mode, dissolve its contents in
        /// the row. Otherwise, just insert the inset.
-       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       bool addToMathRow(MathRow &, MetricsInfo & mi) const;
        ///
        void draw(PainterInfo & pi, int x, int y) const;
        /// draw selection background
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
index d785480..64008e1 100644
--- a/src/mathed/MathRow.cpp
+++ b/src/mathed/MathRow.cpp
@@ -22,6 +22,7 @@
 #include "CoordCache.h"
 #include "MetricsInfo.h"
 
+#include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
 #include "support/debug.h"
@@ -36,22 +37,33 @@ namespace lyx {
 
 
 MathAtomSpacing::MathAtomSpacing(Kind k, MathClass const mc)
-       : kind(k),
+       : kind(k), macro_nesting(0),
          inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
-         macro(0)
+         macro(0), color(Color_red)
 {}
 
 
-MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
-{
-       if (ar->empty())
-               return;
+MathAtomSpacing::MathAtomSpacing(Kind k, MetricsInfo &mi)
+       : kind(k), macro_nesting(mi.macro_nesting),
+         inset(0), mclass(MC_ORD), before(0), after(0), compl_unique_to(0),
+         macro(0), color(Color_red)
+{}
 
+
+MathRow::MathRow(MetricsInfo & mi, MathData const * ar)
+{
        // First there is a dummy element of type "open"
        push_back(MathAtomSpacing(MathAtomSpacing::BEGIN, MC_OPEN));
 
        // Then insert the MathData argument
-       ar->addToMathRow(*this, mi);
+       bool const has_contents = ar->addToMathRow(*this, mi);
+
+       // empty arrays are visible when they are editable
+       if (!has_contents && mi.macro_nesting == 0) {
+               MathAtomSpacing mas(MathAtomSpacing::BOX, mi);
+               mas.color = Color_mathline;
+               push_back(mas);
+       }
 
        // Finally there is a dummy element of type "close"
        push_back(MathAtomSpacing(MathAtomSpacing::END, MC_CLOSE));
@@ -117,6 +129,7 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
 
        for (MathAtomSpacing const & mas : *this) {
                Dimension d;
+               mi.macro_nesting = mas.macro_nesting;
                switch (mas.kind) {
                case MathAtomSpacing::BEGIN:
                case MathAtomSpacing::END:
@@ -125,12 +138,6 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        mas.inset->metrics(mi, d);
                        d.wid += mas.before + mas.after;
                        coords.insets().add(mas.inset, d);
-                       dim += d;
-                       // Now add the dimension to current macros and 
arguments.
-                       for (auto & dim_macro : dim_macros)
-                               dim_macro.second += d;
-                       for (auto & dim_array : dim_arrays)
-                               dim_array.second += d;
                        break;
                case MathAtomSpacing::BEG_MACRO:
                        mas.macro->macro()->lock();
@@ -158,6 +165,19 @@ void MathRow::metrics(MetricsInfo & mi, Dimension & dim) 
const
                        coords.arrays().add(mas.ar, dim_arrays[mas.ar]);
                        dim_arrays.erase(mas.ar);
                        break;
+               case MathAtomSpacing::BOX:
+                       d = theFontMetrics(mi.base.font).dimension('I');
+                       d.wid += mas.before + mas.after;
+                       break;
+               }
+
+               if (!d.empty()) {
+                       dim += d;
+                       // Now add the dimension to current macros and 
arguments.
+                       for (auto & dim_macro : dim_macros)
+                               dim_macro.second += d;
+                       for (auto & dim_array : dim_arrays)
+                               dim_array.second += d;
                }
 
                if (mas.compl_text.empty())
@@ -174,7 +194,6 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
 {
        CoordCache & coords = pi.base.bv->coordCache();
        for (MathAtomSpacing const & mas : *this) {
-               Dimension d;
                switch (mas.kind) {
                case MathAtomSpacing::INSET: {
                        coords.insets().add(mas.inset, x, y);
@@ -205,6 +224,13 @@ void MathRow::draw(PainterInfo & pi, int x, int const y) 
const
                        if (mas.macro->editMetrics(pi.base.bv))
                                pi.pain.enterMonochromeMode(Color_mathbg, 
Color_mathmacroblend);
                        break;
+               case MathAtomSpacing::BOX: {
+                       Dimension const d = 
theFontMetrics(pi.base.font).dimension('I');
+                       pi.pain.rectangle(x + mas.before, y - d.ascent(),
+                                         d.width(), d.height(), mas.color);
+                       x += d.wid;
+                       break;
+               }
                case MathAtomSpacing::BEGIN:
                case MathAtomSpacing::END:
                case MathAtomSpacing::END_MACRO:
@@ -271,6 +297,9 @@ ostream & operator<<(ostream & os, MathAtomSpacing const & 
mas)
        case MathAtomSpacing::END_ARG:
                os << ")";
                break;
+       case MathAtomSpacing::BOX:
+               os << "@";
+               break;
        }
        return os;
 }
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
index 4b26d91..e191290 100644
--- a/src/mathed/MathRow.h
+++ b/src/mathed/MathRow.h
@@ -14,6 +14,8 @@
 
 #include "MathClass.h"
 
+#include "ColorCode.h"
+
 #include "support/docstring.h"
 
 #include <vector>
@@ -40,13 +42,18 @@ struct MathAtomSpacing
                END_ARG, // a macro argument ends here
                BEGIN, // dummy element before row
                END, // dummy element after row
+               BOX // an empty box
        };
 
        ///
        MathAtomSpacing(Kind k = INSET, MathClass const mc = MC_ORD);
+       ///
+       MathAtomSpacing(Kind k, MetricsInfo & mi);
 
        /// Classifies the contents of the object
        Kind kind;
+       /// count wether the current mathdata is nested in macro(s)
+       int macro_nesting;
 
        /// When kind is INSET
        /// the math inset
@@ -66,6 +73,9 @@ struct MathAtomSpacing
 
        // kind is BEG_ARG, END_ARG
        MathData const * ar;
+
+       // kind is BOX
+       ColorCode color;
 };
 
 /*
@@ -85,7 +95,7 @@ public:
 
        // create the math row by unwinding all macros in the MathData and
        // compute the spacings.
-       MathRow(MetricsInfo const & mi, MathData const * ar);
+       MathRow(MetricsInfo & mi, MathData const * ar);
 
        //
        void metrics(MetricsInfo & mi, Dimension & dim) const;

commit d16571d27e9ac432bccd3e141e33bd00658aa927
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Wed Oct 5 00:25:38 2016 +0200

    Add support for \mathbin and friends
    
    All they do is change the class of the elements that they contain.
    
    As an application, use \mathrel in lib/symbols. This allows to mostly
    use the mathtools.sty definitions as is.

diff --git a/lib/symbols b/lib/symbols
index ba2b6d3..7b969ad 100644
--- a/lib/symbols
+++ b/lib/symbols
@@ -1100,20 +1100,20 @@ pod                lyxblacktext  0   0 func     x     
amsmath
 
 
 # mathtools.sty
-\def\vcentcolon{:}                                              mathrel :      
  mathtools
-\def\dblcolon{\vcentcolon\kern-8mu\vcentcolon}                  mathrel ::     
  mathtools
-\def\coloneqq{\vcentcolon\kern-7mu=}                            mathrel 
&#x2254; mathtools
-\def\Coloneqq{\dblcolon\kern-7mu=}                              mathrel ::=    
  mathtools
-\def\coloneq{\vcentcolon\kern-7mu-}                             mathrel :-     
  mathtools
-\def\Coloneq{\dblcolon\kern-7mu-}                               mathrel ::-    
  mathtools
-\def\eqqcolon{=\kern-8mu\vcentcolon}                            mathrel 
&#x2255; mathtools
-\def\Eqqcolon{=\kern-8mu\dblcolon}                              mathrel =::    
  mathtools
-\def\eqcolon{-\kern-8mu\vcentcolon}                             mathrel -:     
  mathtools
-\def\Eqcolon{-\kern-8mu\dblcolon}                               mathrel -::    
  mathtools
-\def\colonapprox{\vcentcolon\kern-7mu\approx}                   mathrel :&ap;  
  mathtools
-\def\Colonapprox{\dblcolon\kern-7mu\approx}                     mathrel ::&ap; 
  mathtools
-\def\colonsim{\vcentcolon\kern-7mu\sim}                         mathrel :&sim; 
  mathtools
-\def\Colonsim{\dblcolon\kern-7mu\sim}                           mathrel 
::&sim;  mathtools
+\def\vcentcolon{\mathrel{:}}                                              
mathrel :        mathtools
+\def\dblcolon{\vcentcolon\mathrel{\kern-1.2mu}\vcentcolon}                  
mathrel ::       mathtools
+\def\coloneqq{\vcentcolon\mathrel{\kern-1.2mu}=}                            
mathrel &#x2254; mathtools
+\def\Coloneqq{\dblcolon\mathrel{\kern-1.2mu}=}                              
mathrel ::=      mathtools
+\def\coloneq{\vcentcolon\mathrel{\kern-1.2mu}\mathrel{-}}                      
       mathrel :-       mathtools
+\def\Coloneq{\dblcolon\mathrel{\kern-1.2mu}\mathrel{-}}                        
       mathrel ::-      mathtools
+\def\eqqcolon{=\mathrel{\kern-1.2mu}\vcentcolon}                            
mathrel &#x2255; mathtools
+\def\Eqqcolon{=\mathrel{\kern-1.2mu}\dblcolon}                              
mathrel =::      mathtools
+\def\eqcolon{-\mathrel{\kern-1.2mu}\vcentcolon}                             
mathrel -:       mathtools
+\def\Eqcolon{-\mathrel{\kern-1.2mu}\dblcolon}                               
mathrel -::      mathtools
+\def\colonapprox{\vcentcolon\mathrel{\kern-1.2mu}\approx}                   
mathrel :&ap;    mathtools
+\def\Colonapprox{\dblcolon\mathrel{\kern-1.2mu}\approx}                     
mathrel ::&ap;   mathtools
+\def\colonsim{\vcentcolon\mathrel{\kern-1.2mu}\sim}                         
mathrel :&sim;   mathtools
+\def\Colonsim{\dblcolon\mathrel{\kern-1.2mu}\sim}                           
mathrel ::&sim;  mathtools
 
 
 #
diff --git a/src/Makefile.am b/src/Makefile.am
index 44f92db..43037e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -401,6 +401,7 @@ SOURCEFILESMATHED = \
        mathed/InsetMath.cpp \
        mathed/InsetMathCases.cpp \
        mathed/InsetMathChar.cpp \
+       mathed/InsetMathClass.cpp \
        mathed/InsetMathColor.cpp \
        mathed/InsetMathComment.cpp \
        mathed/InsetMathDecoration.cpp \
@@ -475,6 +476,7 @@ HEADERFILESMATHED = \
        mathed/InsetMathCancelto.h \
        mathed/InsetMathCases.h \
        mathed/InsetMathChar.h \
+       mathed/InsetMathClass.h \
        mathed/InsetMathColor.h \
        mathed/InsetMathComment.h \
        mathed/InsetMathDelim.h \
diff --git a/src/insets/InsetCode.h b/src/insets/InsetCode.h
index 1df6800..7d4632c 100644
--- a/src/insets/InsetCode.h
+++ b/src/insets/InsetCode.h
@@ -235,6 +235,8 @@ enum InsetCode {
        ///
        IPADECO_CODE,
        ///
+       MATH_CLASS_CODE,
+       ///
        INSET_CODE_SIZE
 };
 
diff --git a/src/mathed/InsetMathClass.cpp b/src/mathed/InsetMathClass.cpp
new file mode 100644
index 0000000..10a9600
--- /dev/null
+++ b/src/mathed/InsetMathClass.cpp
@@ -0,0 +1,57 @@
+/**
+ * \file InsetMathClass.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author André Pönitz
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "InsetMathClass.h"
+
+#include "support/docstream.h"
+
+
+namespace lyx {
+
+InsetMathClass::InsetMathClass(Buffer * buf, MathClass mc)
+       : InsetMathNest(buf, 1), math_class_(mc)
+{}
+
+
+Inset * InsetMathClass::clone() const
+{
+       return new InsetMathClass(*this);
+}
+
+
+void InsetMathClass::metrics(MetricsInfo & mi, Dimension & dim) const
+{
+       cell(0).metrics(mi, dim);
+       metricsMarkers(dim);
+}
+
+
+void InsetMathClass::draw(PainterInfo & pi, int x, int y) const
+{
+       cell(0).draw(pi, x + 2, y);
+       drawMarkers(pi, x, y);
+}
+
+
+docstring InsetMathClass::name() const
+{
+       return class_to_string(math_class_);
+}
+
+
+void InsetMathClass::infoize(odocstream & os) const
+{
+       os << name() << " ";
+}
+
+
+} // namespace lyx
diff --git a/src/mathed/InsetMathClass.h b/src/mathed/InsetMathClass.h
new file mode 100644
index 0000000..f50040d
--- /dev/null
+++ b/src/mathed/InsetMathClass.h
@@ -0,0 +1,50 @@
+// -*- C++ -*-
+/**
+ * \file InsetMathClass.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author André Pönitz
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef MATH_CLASSINSET_H
+#define MATH_CLASSINSET_H
+
+#include "MathClass.h"
+
+#include "InsetMathNest.h"
+
+
+namespace lyx {
+
+
+/// Support for LaTeX's \\mathxxx class-changing commands
+
+class InsetMathClass : public InsetMathNest {
+public:
+       ///
+       InsetMathClass(Buffer * buf, MathClass);
+       ///
+       docstring name() const;
+       ///
+       MathClass mathClass() const { return math_class_; }
+       ///
+       void metrics(MetricsInfo & mi, Dimension & dim) const;
+       ///
+       void draw(PainterInfo & pi, int x, int y) const;
+       ///
+       void infoize(odocstream & os) const;
+       ///
+       InsetCode lyxCode() const { return MATH_CLASS_CODE; }
+
+private:
+       virtual Inset * clone() const;
+       ///
+       MathClass math_class_;
+};
+
+
+} // namespace lyx
+#endif
diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp
index bb7be8b..f000937 100644
--- a/src/mathed/MathFactory.cpp
+++ b/src/mathed/MathFactory.cpp
@@ -19,6 +19,7 @@
 #include "InsetMathCancel.h"
 #include "InsetMathCancelto.h"
 #include "InsetMathCases.h"
+#include "InsetMathClass.h"
 #include "InsetMathColor.h"
 #include "InsetMathDecoration.h"
 #include "InsetMathDots.h"
@@ -662,10 +663,13 @@ MathAtom createInsetMath(docstring const & s, Buffer * 
buf)
                return MathAtom(new InsetMathSpecialChar(s));
        if (s == " ")
                return MathAtom(new InsetMathSpace(" ", ""));
-
        if (s == "regexp")
                return MathAtom(new InsetMathHull(buf, hullRegexp));
 
+       MathClass const mc = string_to_class(s);
+       if (mc != MC_UNKNOWN)
+               return MathAtom(new InsetMathClass(buf, mc));
+
        return MathAtom(new MathMacro(buf, s));
 }
 

commit 93098e760bfdc8b21a7df1a7f2a78d1596435a48
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon Aug 22 14:17:01 2016 +0200

    Set correctly the spacing between atoms in MathData
    
    New MathRow class which contains the description of a MathData object
    in terms of math class insets. Macros and their arguments used in the
    MathData object are dissolved (replaced with their contents) so that
    all math insets are typeset as a string together.
    
    To this end, we introduce methods addToMathRow() that allows each
    inset/mathdata to insert itself in terms of insets, begin/end macro
    and begin/end macro argument.
    
      + the class class and spacing are computed using the MathClass helpers.
    
      + metrics/draw of insets is delegated to the relevant methods. The
      case of draw is trickier, since many draw() methods rely on their
      metrics without any spacing added.
    
      + only the macros that not currently edited and are in mode
      DISPLAY_NORMAL are dissolved. The others are considered as normal
      math insets. The goal of handling spacing of macros is to be able to
      use proper macro definitions in lib/symbols.
    
      + The dimension and position of the macros and arguments are set 
separately.
    
    The MathRow data is cached in the MathData object in a
    bufferview-dependent way (possibly different dpi for different
    screens). Most of the work of MathData::metrics/draw is delegated to
    MathRow metrics/draw.

diff --git a/src/Makefile.am b/src/Makefile.am
index f37092e..44f92db 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -458,6 +458,7 @@ SOURCEFILESMATHED = \
        mathed/MathMacroArgument.cpp \
        mathed/MathMacroTemplate.cpp \
        mathed/MathParser.cpp \
+       mathed/MathRow.cpp \
        mathed/MathStream.cpp \
        mathed/MathSupport.cpp \
        mathed/TextPainter.cpp
@@ -530,6 +531,7 @@ HEADERFILESMATHED = \
        mathed/MathMacroTemplate.h \
        mathed/MathParser.h \
        mathed/MathParser_flags.h \
+       mathed/MathRow.h \
        mathed/ReplaceData.h \
        mathed/MathStream.h \
        mathed/MathSupport.h \
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index aec53d0..23c9bd0 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -13,6 +13,7 @@
 
 #include "InsetMath.h"
 #include "MathData.h"
+#include "MathRow.h"
 #include "MathStream.h"
 
 #include "support/debug.h"
@@ -55,6 +56,16 @@ MathClass InsetMath::mathClass() const
 }
 
 
+bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo const &) const
+{
+       MathAtomSpacing mas;
+       mas.inset = this;
+       mas.mclass = mathClass();
+       mrow.push_back(mas);
+       return true;
+}
+
+
 void InsetMath::dump() const
 {
        lyxerr << "---------------------------------------------" << endl;
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 0046218..3d3e1ef 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -54,7 +54,10 @@ inclusion in the "real LyX insets" FormulaInset and 
FormulaMacroInset.
 
 */
 
+class Cursor;
 class OutputParams;
+class MetricsInfo;
+
 class InsetMathArray;
 class InsetMathAMSArray;
 class InsetMathBrace;
@@ -73,7 +76,6 @@ class InsetMathSpace;
 class InsetMathSpecialChar;
 class InsetMathSymbol;
 class InsetMathUnknown;
-
 class InsetMathRef;
 
 class HtmlStream;
@@ -87,7 +89,7 @@ class WriteStream;
 
 class MathMacroTemplate;
 class MathMacro;
-class Cursor;
+class MathRow;
 class TextPainter;
 class TextMetricsInfo;
 class ReplaceData;
@@ -164,6 +166,8 @@ public:
 
        /// The class of the math object (used primarily for spacing)
        virtual MathClass mathClass() const;
+       /// Add this inset to a math row. Return true if contents got added
+       virtual bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
 
        /// identifies things that can get scripts
        virtual bool isScriptable() const { return false; }
diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp
index bd4d53e..8b7a89d 100644
--- a/src/mathed/MathData.cpp
+++ b/src/mathed/MathData.cpp
@@ -30,12 +30,11 @@
 
 #include "mathed/InsetMathUnknown.h"
 
-#include "support/debug.h"
-#include "support/docstream.h"
-
 #include "frontends/FontMetrics.h"
 #include "frontends/Painter.h"
 
+#include "support/debug.h"
+#include "support/docstream.h"
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lyxalgo.h"
@@ -218,6 +217,34 @@ void MathData::touch() const
 }
 
 
+bool MathData::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+{
+       bool has_contents = false;
+       BufferView * bv = mi.base.bv;
+       MathData * ar = const_cast<MathData*>(this);
+       ar->updateMacros(&bv->cursor(), mi.macrocontext,
+                        InternalUpdate);
+
+       // FIXME: for completion, try to insert the relevant data in the
+       // mathrow (like is done for text rows). We could add a pair of
+       // InsetMathColor inset, but these come with extra spacing of
+       // their own.
+       DocIterator const & inlineCompletionPos = bv->inlineCompletionPos();
+       bool const has_completion = inlineCompletionPos.inMathed()
+               && &inlineCompletionPos.cell() == this;
+       size_t const compl_pos = has_completion ? inlineCompletionPos.pos() : 0;
+
+       for (size_t i = 0 ; i < size() ; ++i) {
+               has_contents |= (*this)[i]->addToMathRow(mrow, mi);
+               if (i + 1 == compl_pos) {
+                       mrow.back().compl_text = bv->inlineCompletion();
+                       mrow.back().compl_unique_to = 
bv->inlineCompletionUniqueChars();
+               }
+       }
+       return has_contents;
+}
+
+
 #if 0
 namespace {
 
@@ -247,7 +274,6 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
        mindes_ = (3 * xascent) / 4;
        slevel_ = (4 * xascent) / 5;
        sshift_ = xascent / 4;
-       kerning_ = 0;
 
        if (empty()) {
                // Cache the dimension.
@@ -255,45 +281,17 @@ void MathData::metrics(MetricsInfo & mi, Dimension & dim) 
const
                return;
        }
 
-       Cursor & cur = mi.base.bv->cursor();
-       const_cast<MathData*>(this)->updateMacros(&cur, mi.macrocontext, 
InternalUpdate);
-
-       DocIterator const & inlineCompletionPos = 
mi.base.bv->inlineCompletionPos();
-       MathData const * inlineCompletionData = 0;
-       if (inlineCompletionPos.inMathed())
-               inlineCompletionData = &inlineCompletionPos.cell();
-
-       dim.asc = 0;
-       dim.wid = 0;
-       Dimension d;
-       CoordCache::Insets & coords = mi.base.bv->coordCache().insets();
-       for (pos_type i = 0, n = size(); i != n; ++i) {
-               MathAtom const & at = operator[](i);
-               at->metrics(mi, d);
-               coords.add(at.nucleus(), d);
-               dim += d;
-               if (i == n - 1)
-                       kerning_ = at->kerning(mi.base.bv);
-
-               // HACK to draw completion suggestion inline
-               if (inlineCompletionData != this
-                   || size_t(inlineCompletionPos.pos()) != i + 1)
-                       continue;
+       MathRow mrow(mi, this);
+       mrow_cache_[mi.base.bv] = mrow;
+       mrow.metrics(mi, dim);
+       kerning_ = mrow.kerning(mi.base.bv);
 
-               docstring const & completion = mi.base.bv->inlineCompletion();
-               if (completion.length() == 0)
-                       continue;
-
-               FontInfo font = mi.base.font;
-               augmentFont(font, "mathnormal");
-               dim.wid += mathed_string_width(font, completion);
-       }
        // Cache the dimension.
        mi.base.bv->coordCache().arrays().add(this, dim);
 }
 
 
-void MathData::draw(PainterInfo & pi, int x, int y) const
+void MathData::draw(PainterInfo & pi, int const x, int const y) const
 {
        //lyxerr << "MathData::draw: x: " << x << " y: " << y << endl;
        BufferView & bv  = *pi.base.bv;
@@ -313,48 +311,8 @@ void MathData::draw(PainterInfo & pi, int x, int y) const
                || x >= bv. workWidth())
                return;
 
-       DocIterator const & inlineCompletionPos = bv.inlineCompletionPos();
-       MathData const * inlineCompletionData = 0;
-       if (inlineCompletionPos.inMathed())
-               inlineCompletionData = &inlineCompletionPos.cell();
-
-       CoordCache::Insets & coords = pi.base.bv->coordCache().insets();
-       for (size_t i = 0, n = size(); i != n; ++i) {
-               MathAtom const & at = operator[](i);
-               coords.add(at.nucleus(), x, y);
-               at->drawSelection(pi, x, y);
-               at->draw(pi, x, y);
-               x += coords.dim(at.nucleus()).wid;
-
-               // Is the inline completion here?
-               if (inlineCompletionData != this
-                   || size_t(inlineCompletionPos.pos()) != i + 1)
-                       continue;
-               docstring const & completion = bv.inlineCompletion();
-               if (completion.length() == 0)
-                       continue;
-               FontInfo f = pi.base.font;
-               augmentFont(f, "mathnormal");
-
-               // draw the unique and the non-unique completion part
-               // Note: this is not time-critical as it is
-               // only done once per screen.
-               size_t uniqueTo = bv.inlineCompletionUniqueChars();
-               docstring s1 = completion.substr(0, uniqueTo);
-               docstring s2 = completion.substr(uniqueTo);
-
-               if (!s1.empty()) {
-                       f.setColor(Color_inlinecompletion);
-                       pi.pain.text(x, y, s1, f);
-                       x += mathed_string_width(f, s1);
-               }
-
-               if (!s2.empty()) {
-                       f.setColor(Color_nonunique_inlinecompletion);
-                       pi.pain.text(x, y, s2, f);
-                       x += mathed_string_width(f, s2);
-               }
-       }
+       MathRow const & mrow = mrow_cache_[pi.base.bv];
+       mrow.draw(pi, x, y);
 }
 
 
diff --git a/src/mathed/MathData.h b/src/mathed/MathData.h
index 4b79f80..fa82ee9 100644
--- a/src/mathed/MathData.h
+++ b/src/mathed/MathData.h
@@ -16,7 +16,9 @@
 #define MATH_DATA_H
 
 #include "Dimension.h"
+
 #include "MathAtom.h"
+#include "MathRow.h"
 
 #include "OutputEnums.h"
 
@@ -24,6 +26,7 @@
 
 #include <cstddef>
 #include <vector>
+#include <map>
 
 
 namespace lyx {
@@ -117,6 +120,10 @@ public:
        MathAtom & operator[](pos_type);
        /// checked read access
        MathAtom const & operator[](pos_type) const;
+
+       /// Add this array to a math row. Return true if contents got added
+       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+
        /// rebuild cached metrics information
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
@@ -177,6 +184,9 @@ protected:
        mutable int kerning_;
        Buffer * buffer_;
 
+       /// cached object that describes typeset data
+       mutable std::map<BufferView*, MathRow> mrow_cache_;
+
 private:
        /// is this an exact match at this position?
        bool find1(MathData const & ar, size_type pos) const;
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 06a55bf..9cc6c79 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,6 +68,27 @@ public:
        ///
        InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
        ///
+       bool addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+       {
+               MathAtomSpacing mas(MathAtomSpacing::BEG_ARG);
+               mas.macro = mathMacro_;
+               mas.ar = &mathMacro_->cell(idx_);
+               mrow.push_back(mas);
+
+               mathMacro_->macro()->unlock();
+               bool const has_contents = 
mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+               mathMacro_->macro()->lock();
+
+               mas.kind = MathAtomSpacing::END_ARG;
+               mrow.push_back(mas);
+
+               if (has_contents)
+                       return true;
+               // if there was no contents, then we insert the empty macro 
inset
+               // instead.
+               return InsetMath::addToMathRow(mrow, mi);
+       }
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const {
                mathMacro_->macro()->unlock();
                mathMacro_->cell(idx_).metrics(mi, dim);
@@ -266,6 +287,34 @@ MathMacro::~MathMacro()
 }
 
 
+bool MathMacro::addToMathRow(MathRow & mrow, MetricsInfo const & mi) const
+{
+       // set edit mode for which we will have calculated row.
+       // This is the same as what is done in metrics().
+       d->editing_[mi.base.bv] = editMode(mi.base.bv);
+
+       if (displayMode() == MathMacro::DISPLAY_NORMAL
+           && !d->editing_[mi.base.bv]) {
+               MathAtomSpacing mas(MathAtomSpacing::BEG_MACRO);
+               mas.macro = this;
+               mrow.push_back(mas);
+
+               d->macro_->lock();
+               bool const has_contents = d->expanded_.addToMathRow(mrow, mi);
+               d->macro_->unlock();
+
+               mas.kind = MathAtomSpacing::END_MACRO;
+               mrow.push_back(mas);
+
+               if (has_contents)
+                       return true;
+               // if there was no contents, then we insert the empty macro 
inset
+               // instead.
+       }
+       return InsetMath::addToMathRow(mrow, mi);
+}
+
+
 Inset * MathMacro::clone() const
 {
        MathMacro * copy = new MathMacro(*this);
@@ -344,7 +393,7 @@ bool MathMacro::editMode(BufferView const * bv) const {
 }
 
 
-MacroData const * MathMacro::macro()
+MacroData const * MathMacro::macro() const
 {
        return d->macro_;
 }
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 1a8654a..9c6fdb1 100644
--- a/src/mathed/MathMacro.h
+++ b/src/mathed/MathMacro.h
@@ -37,6 +37,10 @@ public:
        ///
        virtual MathMacro const * asMacro() const { return this; }
        ///
+       /// If the macro is in normal edit mode, dissolve its contents in
+       /// the row. Otherwise, just insert the inset.
+       bool addToMathRow(MathRow &, MetricsInfo const & mi) const;
+       ///
        void draw(PainterInfo & pi, int x, int y) const;
        /// draw selection background
        void drawSelection(PainterInfo & pi, int x, int y) const;
@@ -45,6 +49,8 @@ public:
        { drawMarkers2(pi, x, y); }
        ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
+       /// was the macro in edit mode when computing metrics?
+       bool editMetrics(BufferView const * bv) const;
        ///
        int kerning(BufferView const * bv) const;
        /// get cursor position
@@ -117,6 +123,8 @@ public:
        ///
        docstring name() const;
        ///
+       MacroData const * macro() const;
+       ///
        docstring macroName() const;
        ///
        bool validName() const;
@@ -151,10 +159,6 @@ protected:
        /// attach arguments (maybe less than arity at the end of an MathData),
        /// including the optional ones (even if it can be empty here)
        void attachArguments(std::vector<MathData> const & args, size_t arity, 
int optionals);
-       ///
-       MacroData const * macro();
-       ///
-       bool editMetrics(BufferView const * bv) const;
 
 private:
        ///
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
new file mode 100644
index 0000000..d785480
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,286 @@
+/**
+ * \file MathRow.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "MathRow.h"
+
+#include "InsetMath.h"
+#include "MathClass.h"
+#include "MathData.h"
+#include "MathMacro.h"
+#include "MathSupport.h"
+
+#include "BufferView.h"
+#include "CoordCache.h"
+#include "MetricsInfo.h"
+
+#include "frontends/Painter.h"
+
+#include "support/debug.h"
+#include "support/docstring.h"
+#include "support/lassert.h"
+
+#include <ostream>
+
+using namespace std;
+
+namespace lyx {
+
+
+MathAtomSpacing::MathAtomSpacing(Kind k, MathClass const mc)
+       : kind(k),
+         inset(0), mclass(mc), before(0), after(0), compl_unique_to(0),
+         macro(0)
+{}
+
+
+MathRow::MathRow(MetricsInfo const & mi, MathData const * ar)
+{
+       if (ar->empty())
+               return;
+
+       // First there is a dummy element of type "open"
+       push_back(MathAtomSpacing(MathAtomSpacing::BEGIN, MC_OPEN));
+
+       // Then insert the MathData argument
+       ar->addToMathRow(*this, mi);
+
+       // Finally there is a dummy element of type "close"
+       push_back(MathAtomSpacing(MathAtomSpacing::END, MC_CLOSE));
+
+       // update classes
+       for (int i = 1 ; i != static_cast<int>(size()) - 1 ; ++i) {
+               if ((*this)[i].kind != MathAtomSpacing::INSET)
+                       continue;
+               update_class((*this)[i].mclass, (*this)[before(i)].mclass,
+                            (*this)[after(i)].mclass);
+       }
+
+       // set spacing
+       // We go to the end to handle spacing at the end of equation
+       for (int i = 1 ; i != static_cast<int>(size()) ; ++i) {
+               if ((*this)[i].kind != MathAtomSpacing::INSET)
+                       continue;
+               MathAtomSpacing & bef = (*this)[before(i)];
+               int spc = class_spacing(bef.mclass, (*this)[i].mclass, mi.base);
+               bef.after = spc / 2;
+               // this is better than spc / 2 to avoid rounding problems
+               (*this)[i].before = spc - spc / 2;
+       }
+       // Do not lose spacing allocated to extremities
+       if (!empty()) {
+               (*this)[after(0)].before += front().after;
+               (*this)[before(size() - 1)].after += back().before;
+       }
+}
+
+
+int MathRow::before(int i) const
+{
+       do
+               --i;
+       while ((*this)[i].kind != MathAtomSpacing::BEGIN
+                  && (*this)[i].kind != MathAtomSpacing::INSET);
+
+       return i;
+}
+
+
+int MathRow::after(int i) const
+{
+       do
+               ++i;
+       while ((*this)[i].kind != MathAtomSpacing::END
+                  && (*this)[i].kind != MathAtomSpacing::INSET);
+
+       return i;
+}
+
+
+void MathRow::metrics(MetricsInfo & mi, Dimension & dim) const
+{
+       dim.asc = 0;
+       dim.wid = 0;
+       // In order to compute the dimension of macros and their
+       // arguments, it is necessary to keep track of them.
+       map<MathMacro const *, Dimension> dim_macros;
+       map<MathData const *, Dimension> dim_arrays;
+       CoordCache & coords = mi.base.bv->coordCache();
+
+       for (MathAtomSpacing const & mas : *this) {
+               Dimension d;
+               switch (mas.kind) {
+               case MathAtomSpacing::BEGIN:
+               case MathAtomSpacing::END:
+                       break;
+               case MathAtomSpacing::INSET:
+                       mas.inset->metrics(mi, d);
+                       d.wid += mas.before + mas.after;
+                       coords.insets().add(mas.inset, d);
+                       dim += d;
+                       // Now add the dimension to current macros and 
arguments.
+                       for (auto & dim_macro : dim_macros)
+                               dim_macro.second += d;
+                       for (auto & dim_array : dim_arrays)
+                               dim_array.second += d;
+                       break;
+               case MathAtomSpacing::BEG_MACRO:
+                       mas.macro->macro()->lock();
+                       // Add a macro to current list
+                       dim_macros[mas.macro] = Dimension();
+                       break;
+               case MathAtomSpacing::END_MACRO:
+                       LATTEST(dim_macros.find(mas.macro) != dim_macros.end());
+                       mas.macro->macro()->unlock();
+                       // Cache the dimension of the macro and remove it from
+                       // tracking map.
+                       coords.insets().add(mas.macro, dim_macros[mas.macro]);
+                       dim_macros.erase(mas.macro);
+                       break;
+                       // This is basically like macros
+               case MathAtomSpacing::BEG_ARG:
+                       if (mas.macro)
+                               mas.macro->macro()->unlock();
+                       dim_arrays[mas.ar] = Dimension();
+                       break;
+               case MathAtomSpacing::END_ARG:
+                       LATTEST(dim_arrays.find(mas.ar) != dim_arrays.end());
+                       if (mas.macro)
+                               mas.macro->macro()->lock();
+                       coords.arrays().add(mas.ar, dim_arrays[mas.ar]);
+                       dim_arrays.erase(mas.ar);
+                       break;
+               }
+
+               if (mas.compl_text.empty())
+                       continue;
+               FontInfo font = mi.base.font;
+               augmentFont(font, "mathnormal");
+               dim.wid += mathed_string_width(font, mas.compl_text);
+       }
+       LATTEST(dim_macros.empty() && dim_arrays.empty());
+}
+
+
+void MathRow::draw(PainterInfo & pi, int x, int const y) const
+{
+       CoordCache & coords = pi.base.bv->coordCache();
+       for (MathAtomSpacing const & mas : *this) {
+               Dimension d;
+               switch (mas.kind) {
+               case MathAtomSpacing::INSET: {
+                       coords.insets().add(mas.inset, x, y);
+                       // This is hackish: the math inset does not know that 
space
+                       // has been added before and after it; we alter its 
dimension
+                       // while it is drawing, because it relies on this value.
+                       Dimension const d = coords.insets().dim(mas.inset);
+                       Dimension d2 = d;
+                       d2.wid -= mas.before + mas.after;
+                       coords.insets().add(mas.inset, d2);
+                       mas.inset->drawSelection(pi, x + mas.before, y);
+                       mas.inset->draw(pi, x + mas.before, y);
+                       coords.insets().add(mas.inset, d);
+                       x += d.wid;
+                       break;
+               }
+               case MathAtomSpacing::BEG_MACRO:
+                       coords.insets().add(mas.macro, x, y);
+                       break;
+               case MathAtomSpacing::BEG_ARG:
+                       coords.arrays().add(mas.ar, x, y);
+                       // if the macro is being edited, then the painter is in
+                       // monochrome mode.
+                       if (mas.macro->editMetrics(pi.base.bv))
+                               pi.pain.leaveMonochromeMode();
+                       break;
+               case MathAtomSpacing::END_ARG:
+                       if (mas.macro->editMetrics(pi.base.bv))
+                               pi.pain.enterMonochromeMode(Color_mathbg, 
Color_mathmacroblend);
+                       break;
+               case MathAtomSpacing::BEGIN:
+               case MathAtomSpacing::END:
+               case MathAtomSpacing::END_MACRO:
+                       break;
+               }
+
+               if (mas.compl_text.empty())
+                       continue;
+               FontInfo f = pi.base.font;
+               augmentFont(f, "mathnormal");
+
+               // draw the unique and the non-unique completion part
+               // Note: this is not time-critical as it is
+               // only done once per screen.
+               docstring const s1 = mas.compl_text.substr(0, 
mas.compl_unique_to);
+               docstring const s2 = mas.compl_text.substr(mas.compl_unique_to);
+
+               if (!s1.empty()) {
+                       f.setColor(Color_inlinecompletion);
+                       pi.pain.text(x, y, s1, f);
+                       x += mathed_string_width(f, s1);
+               }
+               if (!s2.empty()) {
+                       f.setColor(Color_nonunique_inlinecompletion);
+                       pi.pain.text(x, y, s2, f);
+                       x += mathed_string_width(f, s2);
+               }
+       }
+}
+
+
+int MathRow::kerning(BufferView const * bv) const
+{
+       if (empty())
+               return 0;
+       InsetMath const * inset = (*this)[before(size() - 1)].inset;
+       return inset ? inset->kerning(bv) : 0;
+}
+
+
+ostream & operator<<(ostream & os, MathAtomSpacing const & mas)
+{
+       switch (mas.kind) {
+       case MathAtomSpacing::BEGIN:
+               os << "{";
+               break;
+       case MathAtomSpacing::END:
+               os << "}";
+               break;
+       case MathAtomSpacing::INSET:
+               os << "<" << mas.before << "-"
+                  << to_utf8(class_to_string(mas.mclass))
+                  << "-" << mas.after << ">";
+               break;
+       case MathAtomSpacing::BEG_MACRO:
+               os << "\\" << to_utf8(mas.macro->name()) << "[";
+               break;
+       case MathAtomSpacing::END_MACRO:
+               os << "]";
+               break;
+       case MathAtomSpacing::BEG_ARG:
+               os << "#(";
+               break;
+       case MathAtomSpacing::END_ARG:
+               os << ")";
+               break;
+       }
+       return os;
+}
+
+
+ostream & operator<<(ostream & os, MathRow const & mrow)
+{
+       for (MathAtomSpacing const & mas : mrow)
+               os << mas << "  ";
+       return os;
+}
+
+} // namespace lyx
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
new file mode 100644
index 0000000..4b26d91
--- /dev/null
+++ b/src/mathed/MathRow.h
@@ -0,0 +1,114 @@
+// -*- C++ -*-
+/**
+ * \file MathRow.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef MATH_ROW_H
+#define MATH_ROW_H
+
+#include "MathClass.h"
+
+#include "support/docstring.h"
+
+#include <vector>
+
+namespace lyx {
+
+class BufferView;
+class Dimension;
+class MetricsInfo;
+class PainterInfo;
+
+class InsetMath;
+class MathData;
+class MathMacro;
+
+// A sequence of elements, together with their spacing
+struct MathAtomSpacing
+{
+       enum Kind {
+               INSET, // this element is a plain inset
+               BEG_MACRO, // a macro begins here
+               END_MACRO, // a macro ends here
+               BEG_ARG, // a macro argument begins here
+               END_ARG, // a macro argument ends here
+               BEGIN, // dummy element before row
+               END, // dummy element after row
+       };
+
+       ///
+       MathAtomSpacing(Kind k = INSET, MathClass const mc = MC_ORD);
+
+       /// Classifies the contents of the object
+       Kind kind;
+
+       /// When kind is INSET
+       /// the math inset
+       InsetMath const * inset;
+       /// the class of the inset
+       MathClass mclass;
+       /// the spacing around the inset
+       int before, after;
+       // Non empty when there is a completion to draw
+       docstring compl_text;
+       // the number of characters forming the unique part.
+       size_t compl_unique_to;
+
+       /// When kind is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG
+       /// the math macro
+       MathMacro const * macro;
+
+       // kind is BEG_ARG, END_ARG
+       MathData const * ar;
+};
+
+/*
+ * While for editing purpose it is important that macros are counted
+ * as a single element, this is not the case for display. To get the
+ * spacing correct, it is necessary to dissolve all the macros that
+ * can be, along with their arguments. Then one obtains a
+ * representation of the MathData contents as a string of insets and
+ * then spacing can be done properly.
+ *
+ * This is the purpose of the MathRow class.
+ */
+class MathRow : public std::vector<MathAtomSpacing>
+{
+public:
+       MathRow() {};
+
+       // create the math row by unwinding all macros in the MathData and
+       // compute the spacings.
+       MathRow(MetricsInfo const & mi, MathData const * ar);
+
+       //
+       void metrics(MetricsInfo & mi, Dimension & dim) const;
+       //
+       void draw(PainterInfo & pi, int const x, int const y) const;
+
+       /// superscript kerning
+       int kerning(BufferView const *) const;
+
+private:
+       // Index of the first inset element before position i
+       int before(int i) const;
+       // Index of the first inset element after position i
+       int after(int i) const;
+};
+
+///
+std::ostream & operator<<(std::ostream & os, MathAtomSpacing const & mas);
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow const & mrow);
+
+
+} // namespace lyx
+
+#endif

commit 2838d2116014fd6b3be8d8d8961cc684fc1990ad
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon Aug 22 14:15:28 2016 +0200

    Implement computation of spacing according to the TeXBook
    
    This implements the relevant math typography rules described in the
    Appendix G of the TeXbook. More precisely, for each atom
    
      + the class is computed by implementing rules 5 and 6 of Appendix G
    
      + the spacing is computed according to the table p. 170
    
    This code is not used at this point.

diff --git a/src/mathed/MathClass.cpp b/src/mathed/MathClass.cpp
index fae7654..a46aad3 100644
--- a/src/mathed/MathClass.cpp
+++ b/src/mathed/MathClass.cpp
@@ -11,10 +11,17 @@
 #include <config.h>
 
 #include "MathClass.h"
+#include "MathSupport.h"
 
+#include "MetricsInfo.h"
+#include "FontInfo.h"
+
+#include "support/debug.h"
 #include "support/docstring.h"
 #include "support/lassert.h"
 
+#include <ostream>
+
 using namespace std;
 
 namespace lyx {
@@ -79,4 +86,95 @@ MathClass string_to_class(docstring const &s)
 }
 
 
+/*
+ * The TeXbook presents in Appendix G a set of 22 rules (!) explaining
+ * how to typeset mathematic formulas. Of interest here are rules 5
+ * and 6:
+
+ * 5. If the current item is a Bin atom, and if this was the first
+ *    atom in the list, or if the most recent previous atom was Bin,
+ *    Op, Rel, Open, or Punct, change the current Bin to Ord [and
+ *    continue with Rule 14. Otherwise continue with Rule 17]
+ *
+ * 6. If the current item is a Rel or Close or Punct atom, and if the
+ *    most recent previous atom was Bin, change that previous Bin to
+ *    Ord. [Continue with Rule 17.]
+ */
+void update_class(MathClass & mc, MathClass const prev, MathClass const next)
+{
+       if (mc == MC_BIN
+               && (prev == MC_BIN || prev == MC_OP || prev == MC_OPEN
+                       || prev == MC_PUNCT || prev == MC_REL
+                       || next == MC_CLOSE || next == MC_PUNCT || next == 
MC_REL))
+               mc = MC_ORD;
+}
+
+
+/*
+ * This table of spacing between two classes can be found p. 170 of
+ * The TeXbook.
+ *
+ * The line is the class of the first class, and the column the second
+ * class. The number encodes the spacing between the two atoms, as
+ * follows
+ *
+ * + 0: no spacing
+ * + 1: thin mu skip
+ * + 2: med mu skip
+ * + 3: thick mu skip
+ * + 9: should never happen
+ * + negative value: either 0 if the atom is in script or scriptscript mode,
+ *   or the spacing given by the absolute value.
+ */
+int pair_spc[MC_UNKNOWN][MC_UNKNOWN] = {
+//      ORD    OP   BIN   REL  OPEN CLOSE PUNCT INNER
+       {  0,    1,   -2,   -3,    0,    0,    0,   -1}, // ORD
+       {  1,    1,    9,   -3,    0,    0,    0,   -1}, // OP
+       { -2,   -2,    9,    9,   -2,    9,    9,   -2}, // BIN
+       { -3,   -3,    9,    0,   -3,    0,    0,   -3}, // REL
+       {  0,    0,    9,    0,    0,    0,    0,    0}, // OPEN
+       {  0,    1,   -2,   -3,    0,    0,    0,   -1}, // CLOSE
+       { -1,   -1,    9,   -1,   -1,   -1,   -1,   -1}, // PUNCT
+       { -1,    1,   -2,   -3,   -1,    0,   -1,   -1}, // INNER
+};
+
+
+int class_spacing(MathClass const mc1, MathClass const mc2,
+                  MetricsBase const & mb)
+{
+       int spc_code = pair_spc[mc1][mc2];
+       //lyxerr << class_to_string(mc1) << "+" << class_to_string(mc2)
+       //         << "=" << spc_code << " @" << mb.style << endl;
+       if (spc_code < 0) {
+               switch (mb.style) {
+               case LM_ST_DISPLAY:
+               case LM_ST_TEXT:
+                       spc_code = abs(spc_code);
+                       break;
+               case LM_ST_SCRIPT:
+               case LM_ST_SCRIPTSCRIPT:
+                       spc_code = 0;
+               }
+       }
+
+       int spc = 0;
+       switch(spc_code) {
+       case 0:
+               break;
+       case 1:
+               spc = mathed_thinmuskip(mb.font);
+               break;
+       case 2:
+               spc = mathed_medmuskip(mb.font);
+               break;
+       case 3:
+               spc = mathed_thickmuskip(mb.font);
+               break;
+       default:
+               LYXERR0("Impossible pair of classes: (" << mc1 << ", " << mc2 
<< ")");
+               LATTEST(false);
+       }
+       return spc;
+}
+
 } // namespace lyx
diff --git a/src/mathed/MathClass.h b/src/mathed/MathClass.h
index ceedd8d..169bfdb 100644
--- a/src/mathed/MathClass.h
+++ b/src/mathed/MathClass.h
@@ -16,6 +16,7 @@
 
 namespace lyx {
 
+class MetricsBase;
 
 /* The TeXbook, p. 158:
  *
@@ -58,6 +59,10 @@ MathClass string_to_class(docstring const &);
 
 docstring const class_to_string(MathClass);
 
+void update_class(MathClass & mc, MathClass const prev, MathClass const next);
+
+int class_spacing(MathClass const mc1, MathClass const mc2,
+                  MetricsBase const & mb);
 
 } // namespace lyx
 
diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp
index 5c3e84f..d49ff1c 100644
--- a/src/mathed/MathSupport.cpp
+++ b/src/mathed/MathSupport.cpp
@@ -527,6 +527,9 @@ int mathed_font_em(FontInfo const & font)
  * punctuation, and is put around inner objects, except where these
  * are followed by a close or preceded by an open symbol, and except
  * if the other object is a large operator or a binary relation.
+ *
+ * See the file MathClass.cpp for a formal implementation of the rules
+ * above.
  */
 
 int mathed_thinmuskip(FontInfo font)

commit a664f65e03b58bb15ea06090434e5bbbd971d181
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon May 30 10:33:35 2016 +0200

    Introduce the notion of math class
    
    This done according to the TeXbook. This class replaces the individual
    isMathXXX() methods. The mathClass() method (currently unused) is
    provided for the following insets:
    
     * InsetMathChar (with a revised list of affected characters)
     * InsetMathSymbol: the class is given by the `extra' field
       Operators defined in lib/symbols (e.g. \log) are MC_OP
     * InsetMathFrac is MC_INNER (except nicefrac and units)
     * InsetDelimiters is MC_INNER
     * InsetStackrel is MC_REL
     * The class of InsetScript is the class of the first element of its
       nucleus (yes, it is a hack, but doing it right is more work).
    
    Remove the explicit spacing that was done in the different insets. The 
spacing
    will be reintroduced properly in a forthcoming commit.

diff --git a/src/Makefile.am b/src/Makefile.am
index dd51584..f37092e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -447,14 +447,15 @@ SOURCEFILESMATHED = \
        mathed/InsetMathXArrow.cpp \
        mathed/InsetMathXYMatrix.cpp \
        mathed/InsetMathDiagram.cpp \
+       mathed/MacroTable.cpp \
        mathed/MathAtom.cpp \
        mathed/MathAutoCorrect.cpp \
+       mathed/MathClass.cpp \
        mathed/MathData.cpp \
        mathed/MathExtern.cpp \
        mathed/MathFactory.cpp \
        mathed/MathMacro.cpp \
        mathed/MathMacroArgument.cpp \
-       mathed/MacroTable.cpp \
        mathed/MathMacroTemplate.cpp \
        mathed/MathParser.cpp \
        mathed/MathStream.cpp \
@@ -518,6 +519,7 @@ HEADERFILESMATHED = \
        mathed/InsetMathDiagram.h \
        mathed/MathAtom.h \
        mathed/MathAutoCorrect.h \
+       mathed/MathClass.h \
        mathed/MathData.h \
        mathed/MathCompletionList.h \
        mathed/MathExtern.h \
diff --git a/src/mathed/InsetMath.cpp b/src/mathed/InsetMath.cpp
index 2fb1ff5..aec53d0 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -49,6 +49,12 @@ MathData const & InsetMath::cell(idx_type) const
 }
 
 
+MathClass InsetMath::mathClass() const
+{
+       return MC_ORD;
+}
+
+
 void InsetMath::dump() const
 {
        lyxerr << "---------------------------------------------" << endl;
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index 3bab681..0046218 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -13,6 +13,7 @@
 #ifndef MATH_INSET_H
 #define MATH_INSET_H
 
+#include "MathClass.h"
 #include "MathData.h"
 
 #include "insets/Inset.h"
@@ -161,14 +162,11 @@ public:
        virtual InsetMathRef            * asRefInset()            { return 0; }
        virtual InsetMathSpecialChar const * asSpecialCharInset() const { 
return 0; }
 
+       /// The class of the math object (used primarily for spacing)
+       virtual MathClass mathClass() const;
+
        /// identifies things that can get scripts
        virtual bool isScriptable() const { return false; }
-       /// identifies a binary operators (used for spacing)
-       virtual bool isMathBin() const { return false; }
-       /// identifies relational operators (used for spacing and splitting 
equations)
-       virtual bool isMathRel() const { return false; }
-       /// identifies punctuation (used for spacing)
-       virtual bool isMathPunct() const { return false; }
        /// will this get written as a single block in {..}
        virtual bool extraBraces() const { return false; }
 
diff --git a/src/mathed/InsetMathChar.cpp b/src/mathed/InsetMathChar.cpp
index 813067e..1632f0d 100644
--- a/src/mathed/InsetMathChar.cpp
+++ b/src/mathed/InsetMathChar.cpp
@@ -69,20 +69,12 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & 
dim) const
                dim = fm.dimension(char_);
                kerning_ = fm.rbearing(char_) - dim.wid;
        }
-       if (isMathBin())
-               dim.wid += 2 * mathed_medmuskip(mi.base.font);
-       else if (isMathRel())
-               dim.wid += 2 * mathed_thickmuskip(mi.base.font);
-       else if (isMathPunct())
-               dim.wid += mathed_thinmuskip(mi.base.font);
-       else if (char_ == '\'')
+       if (char_ == '\'')
                // FIXME: don't know where this is coming from
                dim.wid += mathed_thinmuskip(mi.base.font);
 #else
        whichFont(font_, code_, mi);
        dim = theFontMetrics(font_).dimension(char_);
-       if (isBinaryOp(char_, code_))
-               dim.wid += 2 * theFontMetrics(font_).width(' ');
        lyxerr << "InsetMathChar::metrics: " << dim << endl;
 #endif
 }
@@ -91,11 +83,7 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & 
dim) const
 void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
 {
        //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << 
std::endl;
-       if (isMathBin())
-               x += mathed_medmuskip(pi.base.font);
-       else if (isMathRel())
-               x += mathed_thickmuskip(pi.base.font);
-       else if (char_ == '\'')
+       if (char_ == '\'')
                x += mathed_thinmuskip(pi.base.font) / 2;
 #if 1
        if (char_ == '=' && has_math_fonts) {
@@ -235,21 +223,21 @@ void InsetMathChar::htmlize(HtmlStream & ms) const
 }
 
 
-bool InsetMathChar::isMathBin() const
-{
-       return support::contains("+-*", static_cast<char>(char_));
-}
-
-
-bool InsetMathChar::isMathRel() const
-{
-       return support::contains("<>=:", static_cast<char>(char_));
-}
-
-
-bool InsetMathChar::isMathPunct() const
+MathClass InsetMathChar::mathClass() const
 {
-       return support::contains(",;", static_cast<char>(char_));
+       // this information comes from fontmath.ltx in LaTeX source.
+       char const ch = static_cast<char>(char_);
+       if (support::contains("+-*", ch))
+               return MC_BIN;
+       else if (support::contains("<>=:", ch))
+               return MC_REL;
+       else if (support::contains(",;", ch))
+               return MC_PUNCT;
+       else if (support::contains("([", ch))
+               return MC_OPEN;
+       else if (support::contains(")]!?", ch))
+               return MC_CLOSE;
+       else return MC_ORD;
 }
 
 
diff --git a/src/mathed/InsetMathChar.h b/src/mathed/InsetMathChar.h
index 67bbc64..d79bf51 100644
--- a/src/mathed/InsetMathChar.h
+++ b/src/mathed/InsetMathChar.h
@@ -49,11 +49,7 @@ public:
        ///
        char_type getChar() const { return char_; }
        ///
-       bool isMathBin() const;
-       ///
-       bool isMathRel() const;
-       ///
-       bool isMathPunct() const;
+       MathClass mathClass() const;
        ///
        InsetCode lyxCode() const { return MATH_CHAR_CODE; }
 
diff --git a/src/mathed/InsetMathDelim.h b/src/mathed/InsetMathDelim.h
index 3a09ede..5a7e816 100644
--- a/src/mathed/InsetMathDelim.h
+++ b/src/mathed/InsetMathDelim.h
@@ -30,6 +30,8 @@ public:
        InsetMathDelim * asDelimInset() { return this; }
        ///
        InsetMathDelim const * asDelimInset() const { return this; }
+       ///
+       MathClass mathClass() const { return MC_INNER; }
        /// is it (...)?
        bool isParenthesis() const;
        /// is it [...]?
diff --git a/src/mathed/InsetMathFrac.cpp b/src/mathed/InsetMathFrac.cpp
index ab876e0..263587e 100644
--- a/src/mathed/InsetMathFrac.cpp
+++ b/src/mathed/InsetMathFrac.cpp
@@ -123,6 +123,31 @@ bool InsetMathFrac::idxBackward(Cursor & cur) const
 }
 
 
+MathClass InsetMathFrac::mathClass() const
+{
+       // Generalized fractions are of inner class (see The TeXbook, p. 292)
+       // But stuff from the unit/nicefrac packages are not real fractions.
+       MathClass mc = MC_ORD;
+       switch (kind_) {
+       case ATOP:
+       case OVER:
+       case FRAC:
+       case DFRAC:
+       case TFRAC:
+       case CFRAC:
+       case CFRACLEFT:
+       case CFRACRIGHT:
+               mc = MC_INNER;
+               break;
+       case NICEFRAC:
+       case UNITFRAC:
+       case UNIT:
+               break;
+       }
+       return mc;
+}
+
+
 void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        Dimension dim0, dim1, dim2;
diff --git a/src/mathed/InsetMathFrac.h b/src/mathed/InsetMathFrac.h
index c030c8a..96b6293 100644
--- a/src/mathed/InsetMathFrac.h
+++ b/src/mathed/InsetMathFrac.h
@@ -62,6 +62,8 @@ public:
        ///
        bool idxBackward(Cursor &) const;
        ///
+       MathClass mathClass() const;
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
        void draw(PainterInfo &, int x, int y) const;
@@ -117,6 +119,8 @@ public:
        void write(WriteStream & os) const;
        ///
        void normalize(NormalStream &) const;
+       /// Generalized fractions are of inner class (see The TeXbook, p.292)
+       MathClass mathClass() const { return MC_INNER; }
        ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index c4f1207..5563117 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -109,7 +109,7 @@ namespace {
        size_t firstRelOp(MathData const & ar)
        {
                for (MathData::const_iterator it = ar.begin(); it != ar.end(); 
++it)
-                       if ((*it)->isMathRel())
+                       if ((*it)->mathClass() == MC_REL)
                                return it - ar.begin();
                return ar.size();
        }
diff --git a/src/mathed/InsetMathScript.cpp b/src/mathed/InsetMathScript.cpp
index b4cd590..37dcae3 100644
--- a/src/mathed/InsetMathScript.cpp
+++ b/src/mathed/InsetMathScript.cpp
@@ -273,6 +273,18 @@ int InsetMathScript::nker(BufferView const * bv) const
 }
 
 
+MathClass InsetMathScript::mathClass() const
+{
+       // FIXME: this is a hack, since the class will not be correct if
+       // cell 0 has several elements.
+       // The correct implementation would require to dissolve cell 0.
+       if (cell(0).empty())
+               return MC_ORD;
+       else
+               return cell(0)[0]->mathClass();
+}
+
+
 void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        Dimension dim0;
diff --git a/src/mathed/InsetMathScript.h b/src/mathed/InsetMathScript.h
index e84886b..d070743 100644
--- a/src/mathed/InsetMathScript.h
+++ b/src/mathed/InsetMathScript.h
@@ -33,6 +33,8 @@ public:
        ///
        mode_type currentMode() const { return MATH_MODE; }
        ///
+       MathClass mathClass() const;
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
        void draw(PainterInfo & pi, int x, int y) const;
diff --git a/src/mathed/InsetMathStackrel.cpp b/src/mathed/InsetMathStackrel.cpp
index 9412d99..3dda0da 100644
--- a/src/mathed/InsetMathStackrel.cpp
+++ b/src/mathed/InsetMathStackrel.cpp
@@ -52,6 +52,13 @@ bool InsetMathStackrel::idxUpDown(Cursor & cur, bool up) 
const
 }
 
 
+MathClass InsetMathStackrel::mathClass() const
+{
+       // FIXME: update this when/if \stackbin is supported
+       return MC_REL;
+}
+
+
 void InsetMathStackrel::metrics(MetricsInfo & mi, Dimension & dim) const
 {
        Dimension dim1;
diff --git a/src/mathed/InsetMathStackrel.h b/src/mathed/InsetMathStackrel.h
index 91862a7..d6ac815 100644
--- a/src/mathed/InsetMathStackrel.h
+++ b/src/mathed/InsetMathStackrel.h
@@ -24,6 +24,8 @@ public:
        ///
        bool idxUpDown(Cursor &, bool up) const;
        ///
+       MathClass mathClass() const;
+       ///
        void metrics(MetricsInfo & mi, Dimension & dim) const;
        ///
        void draw(PainterInfo & pi, int x, int y) const;
@@ -35,7 +37,7 @@ public:
        void mathmlize(MathStream &) const;
        ///
        void htmlize(HtmlStream &) const;
-       /// 
+       ///
        void validate(LaTeXFeatures &) const;
        ///
        InsetCode lyxCode() const { return MATH_STACKREL_CODE; }
diff --git a/src/mathed/InsetMathSymbol.cpp b/src/mathed/InsetMathSymbol.cpp
index 68c8247..dcde289 100644
--- a/src/mathed/InsetMathSymbol.cpp
+++ b/src/mathed/InsetMathSymbol.cpp
@@ -79,16 +79,6 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & 
dim) const
                dim.asc += h_;
                dim.des -= h_;
        }
-       // seperate things a bit
-       if (isMathBin())
-               dim.wid += 2 * mathed_medmuskip(mi.base.font);
-       else if (isMathRel())
-               dim.wid += 2 * mathed_thickmuskip(mi.base.font);
-       else if (isMathPunct())
-               dim.wid += mathed_thinmuskip(mi.base.font);
-       // FIXME: I see no reason for this
-       //else
-       //      dim.wid += support::iround(0.1667 * em);
 
        scriptable_ = false;
        if (mi.base.style == LM_ST_DISPLAY)
@@ -110,13 +100,6 @@ void InsetMathSymbol::draw(PainterInfo & pi, int x, int y) 
const
                                         sym_->extra == "mathalpha" &&
                                         pi.base.fontname == "mathit";
        std::string const font = italic_upcase_greek ? "cmm" : sym_->inset;
-       if (isMathBin())
-               x += mathed_medmuskip(pi.base.font);
-       else if (isMathRel())
-               x += mathed_thickmuskip(pi.base.font);
-       // FIXME: I see no reason for this
-       //else
-       //      x += support::iround(0.0833 * em);
 
        Changer dummy = pi.base.changeFontSet(font);
        pi.draw(x, y - h_, sym_->draw);
@@ -129,27 +112,18 @@ InsetMath::mode_type InsetMathSymbol::currentMode() const
 }
 
 
-bool InsetMathSymbol::isMathBin() const
-{
-       return sym_->extra == "mathbin";
-}
-
-
-bool InsetMathSymbol::isMathRel() const
-{
-       return sym_->extra == "mathrel";
-}
-
-
-bool InsetMathSymbol::isMathPunct() const
+bool InsetMathSymbol::isOrdAlpha() const
 {
-       return sym_->extra == "mathpunct";
+       return sym_->extra == "mathord" || sym_->extra == "mathalpha";
 }
 
 
-bool InsetMathSymbol::isOrdAlpha() const
+MathClass InsetMathSymbol::mathClass() const
 {
-       return sym_->extra == "mathord" || sym_->extra == "mathalpha";
+       if (sym_->extra == "func" || sym_->extra == "funclim")
+               return MC_OP;
+       MathClass const mc = string_to_class(sym_->extra);
+       return (mc == MC_UNKNOWN) ? MC_ORD : mc;
 }
 
 
diff --git a/src/mathed/InsetMathSymbol.h b/src/mathed/InsetMathSymbol.h
index 6ca3ded..67b33fd 100644
--- a/src/mathed/InsetMathSymbol.h
+++ b/src/mathed/InsetMathSymbol.h
@@ -40,11 +40,7 @@ public:
        ///
        mode_type currentMode() const;
        ///
-       bool isMathRel() const;
-       ///
-       bool isMathBin() const;
-       ///
-       bool isMathPunct() const;
+       MathClass mathClass() const;
        ///
        bool isOrdAlpha() const;
        /// do we take scripts?
diff --git a/src/mathed/MathClass.cpp b/src/mathed/MathClass.cpp
new file mode 100644
index 0000000..fae7654
--- /dev/null
+++ b/src/mathed/MathClass.cpp
@@ -0,0 +1,82 @@
+/**
+ * \file MathClass.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "MathClass.h"
+
+#include "support/docstring.h"
+#include "support/lassert.h"
+
+using namespace std;
+
+namespace lyx {
+
+
+docstring const class_to_string(MathClass const mc)
+{
+       string s;
+       switch (mc) {
+       case MC_ORD:
+               s = "mathord";
+               break;
+       case MC_OP:
+               s = "mathop";
+               break;
+       case MC_BIN:
+               s = "mathbin";
+               break;
+       case MC_REL:
+               s = "mathrel";
+               break;
+       case MC_OPEN:
+               s = "mathopen";
+               break;
+       case MC_CLOSE:
+               s = "mathclose";
+               break;
+       case MC_PUNCT:
+               s = "mathpunct";
+               break;
+       case MC_INNER:
+               s = "mathinner";
+               break;
+       case MC_UNKNOWN:
+               LATTEST(false);
+               s = "mathord";
+       }
+       return from_ascii(s);
+}
+
+
+MathClass string_to_class(docstring const &s)
+{
+       if (s  == "mathop")
+               return MC_OP;
+       else if (s  == "mathbin")
+               return MC_BIN;
+       else if (s  == "mathrel")
+               return MC_REL;
+       else if (s  == "mathopen")
+               return MC_OPEN;
+       else if (s  == "mathclose")
+               return MC_CLOSE;
+       else if (s  == "mathpunct")
+               return MC_PUNCT;
+       else if (s  == "mathinner")
+               return MC_INNER;
+       else  if (s  == "mathord")
+               return MC_ORD;
+       else
+               return MC_UNKNOWN;
+}
+
+
+} // namespace lyx
diff --git a/src/mathed/MathClass.h b/src/mathed/MathClass.h
new file mode 100644
index 0000000..ceedd8d
--- /dev/null
+++ b/src/mathed/MathClass.h
@@ -0,0 +1,64 @@
+// -*- C++ -*-
+/**
+ * \file MathClass.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jean-Marc Lasgouttes
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef MATH_CLASS_H
+#define MATH_CLASS_H
+
+#include "support/strfwd.h"
+
+namespace lyx {
+
+
+/* The TeXbook, p. 158:
+ *
+ * There are thirteen kinds of atoms, each of which might act
+ * differently in a formula; for example, ‘(’ is an Open atom because
+ * it comes from an opening. Here is a complete list of the different
+ * kinds:
+
+ * + Ord: an ordinary atom like ‘x’
+ * + Op: a large operator atom like ‘\sum’
+ * + Bin: a binary operation atom like ‘+’
+ * + Rel: a relation atom like ‘=’
+ * + Open: an opening atom like ‘(’
+ * + Close: a closing atom like ‘)’
+ * + Punct: a punctuation atom like ‘,’
+ * + Inner: an inner atom like ‘\frac{1}{2}’
+ * + Over: an overline atom like ‘\overline{x}’
+ * + Under: an underline atom like ‘\underline{x}’
+ * + Acc: an accented atom like ‘\hat{x}’
+ * + Rad: a radical atom like ‘\sqrt{2}’
+ * + Vcent: a vbox to be centered, produced by \vcenter.
+ *
+ * Over, Under, Acc, Rad and Vcent are not considered in the enum
+ * below. The relvant elements will be considered as Ord.
+ */
+enum MathClass {
+       MC_ORD,
+       MC_OP,
+       MC_BIN,
+       MC_REL,
+       MC_OPEN,
+       MC_CLOSE,
+       MC_PUNCT,
+       MC_INNER,
+       MC_UNKNOWN
+};
+
+
+MathClass string_to_class(docstring const &);
+
+docstring const class_to_string(MathClass);
+
+
+} // namespace lyx
+
+#endif

-----------------------------------------------------------------------

Summary of changes:
 src/mathed/InsetMathStackrel.cpp |    2 +-
 src/mathed/MathRow.cpp           |    3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)


hooks/post-receive
-- 
Repository for new features

Reply via email to