diff --git a/lib/symbols b/lib/symbols
index 481aa8186c..aadcd0d006 100644
--- a/lib/symbols
+++ b/lib/symbols
@@ -300,8 +300,7 @@ spadesuit          cmsy        127 170 mathord  ♠
 # We define lyxnot as mathrel in order to have proper alignment
 lyxnot             cmsy         54  47 mathrel  /           hiddensymbol
 iffont cmsy
-# 10mu is the extra space added to relation operators
-\def\not{\lyxnot\kern-20mu}
+\def\not{\lyxnot\mathrel{\kern-11mu}}
 else
 \def\not{\kern4mu\lyxnot\kern-19mu}
 endif
@@ -669,7 +668,7 @@ hslash             msb         125   0 mathord  ℏ
 hbar               msb         126   0 mathord  ℏ
 backepsilon        msb         127   0 mathrel  ϶
 
-lyxbar             cmsy        161   0 mathord   —  hiddensymbol
+lyxbar             cmsy        161   0 mathrel   —  hiddensymbol
 lyxminus           cmsy        161   0 mathbin   —  hiddensymbol
 lyxplus            cmr          43  43 mathbin   +        hiddensymbol
 lyxeq              cmr          61  61 mathord   =        hiddensymbol
@@ -795,7 +794,7 @@ APLup              wasy          0   0 x        x
 APLdown            wasy          70  0 x        x
 APLinput           wasy          125 0 x        x
 APLcomment         wasy          127 0 x        x
-\def\APLinv{\div\kern-17.9mu\APLbox}                       wasysym
+\def\APLinv{\mathord{\div\kern-13.9mu\APLbox}}  wasysym
 APLuparrowbox      wasy         110  0 x        ⍐
 APLdownarrowbox    wasy         111  0 x        ⍗
 APLleftarrowbox    wasy         112  0 x        ⍇
@@ -976,15 +975,15 @@ bignplus           stmry 112   0 mathop     x  stmaryrd # caution: named hugenpl
 #Largerrbracketbot  stmry 126   0 mathclose  x  stmaryrd # only in the font, not the .sty caution: named Hugerrbracketbot in the font
 #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\varcopyright{\mathord{c\kern-11mu\varbigcirc}}         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\mathrel{\kern-2mu}\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\mathrel{\kern1mu}\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
@@ -1115,21 +1114,21 @@ pod                lyxblacktext  0   0 func     x     amsmath
 
 
 # mathtools.sty
-vcentcolon         cmr          58  58            mathrel :        mathtools
-ordinarycolon      cmr          58  58            mathrel :        mathtools
-\def\dblcolon{\vcentcolon\kern-10.9mu\vcentcolon} mathrel ::       mathtools
-\def\coloneqq{\vcentcolon\kern-11.2mu=}           mathrel ≔ mathtools
-\def\Coloneqq{\dblcolon\kern-11.2mu=}             mathrel ::=      mathtools
-\def\coloneq{\vcentcolon\kern-11.2mu-}            mathrel :-       mathtools
-\def\Coloneq{\dblcolon\kern-11.2mu-}              mathrel ::-      mathtools
-\def\eqqcolon{=\kern-11.2mu\vcentcolon}           mathrel ≕ mathtools
-\def\Eqqcolon{=\kern-11.2mu\dblcolon}             mathrel =::      mathtools
-\def\eqcolon{-\kern-11.2mu\vcentcolon}            mathrel -:       mathtools
-\def\Eqcolon{-\kern-11.2mu\dblcolon}              mathrel -::      mathtools
-\def\colonapprox{\vcentcolon\kern-11.2mu\approx}  mathrel :≈    mathtools
-\def\Colonapprox{\dblcolon\kern-11.2mu\approx}    mathrel ::≈   mathtools
-\def\colonsim{\vcentcolon\kern-11.2mu\sim}        mathrel :∼   mathtools
-\def\Colonsim{\dblcolon\kern-11.2mu\sim}          mathrel ::∼  mathtools
+vcentcolon         cmr          58  58    mathrel :             mathtools
+ordinarycolon      cmr          58  58    mathrel :             mathtools
+\def\dblcolon{\vcentcolon\mathrel{\kern-0.9mu}\vcentcolon}      mathrel ::       mathtools
+\def\coloneqq{\vcentcolon\mathrel{\kern-1.2mu}=}                mathrel ≔ 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 ≕ mathtools
+\def\Eqqcolon{=\mathrel{\kern-1.2mu}\dblcolon}                  mathrel =::      mathtools
+\def\eqcolon{\mathrel{-}\mathrel{\kern-1.2mu}\vcentcolon}                 mathrel -:       mathtools
+\def\Eqcolon{\mathrel{-}\mathrel{\kern-1.2mu}\dblcolon}                   mathrel -::      mathtools
+\def\colonapprox{\vcentcolon\mathrel{\kern-1.2mu}\approx}       mathrel :≈    mathtools
+\def\Colonapprox{\dblcolon\mathrel{\kern-1.2mu}\approx}         mathrel ::≈   mathtools
+\def\colonsim{\vcentcolon\mathrel{\kern-1.2mu}\sim}             mathrel :∼   mathtools
+\def\Colonsim{\dblcolon\mathrel{\kern-1.2mu}\sim}               mathrel ::∼  mathtools
 
 
 #
@@ -1144,50 +1143,53 @@ ordinarycolon      cmr          58  58            mathrel :        mathtools
 \def\notin{\not\in}                                             mathrel ∉
 \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 ⟶
-\def\longleftarrow{\leftarrow\kern-11mu\lyxbar}                 mathrel ⟵
-\def\Longrightarrow{\lyxeq\kern-9.5mu\Rightarrow}               mathrel ⟹
-\def\Longleftarrow{\Leftarrow\kern-9.5mu\lyxeq}                 mathrel ⟸
+\def\longrightarrow{\relbar\joinrel\rightarrow}                 mathrel ⟶
+\def\longleftarrow{\leftarrow\joinrel\relbar}                   mathrel ⟵
+\def\Longrightarrow{\Relbar\joinrel\Rightarrow}                 mathrel ⟹
+\def\Longleftarrow{\Leftarrow\joinrel\Relbar}                   mathrel ⟸
 \def\implies{\Longrightarrow}                                   mathrel ⟹ amsmath
 \def\impliedby{\Longleftarrow}                                  mathrel ⟸ amsmath
-\def\mapsto{\mapstochar\kern-9mu\rightarrow}                    mathrel ↤
-\def\longmapsto{\mapstochar\kern-6mu\lyxbar\kern-11mu\rightarrow} mathrel ⟻
-\def\models{\vert\kern-3mu\lyxeq}                               mathrel ⊨
+\def\mapsto{\mapstochar\mathrel{\kern-2mu}\rightarrow}          mathrel ↤
+\def\longmapsto{\mapstochar\joinrel\relbar\joinrel\rightarrow}  mathrel ⟻
+\def\models{\mathrel{\vert}\joinrel\Relbar}                     mathrel ⊨
 else
 \def\implies{=>}                                                mathrel ⟹ 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-10mu\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
-\def\dashleftarrow{\lyxleft\lyxdabar\lyxdabar}                  mathrel &#x290E; amssymb
+\def\dashrightarrow{\mathrel{\lyxdabar\lyxdabar\lyxright}}      mathrel &#x290F; amssymb
+\def\dashleftarrow{\mathrel{\lyxleft\lyxdabar\lyxdabar}}        mathrel &#x290E; amssymb
 else
 \def\dashrightarrow{- - \rightarrow}                            mathrel &#x290F; amssymb
 \def\dashleftarrow{\leftarrow{} - -}                            mathrel &#x290E; amssymb
 endif
 \def\dasharrow{\dashrightarrow}                                 mathrel &#x290F; amssymb
 iffont msb
-\def\Join{\ltimes\kern-22mu\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
diff --git a/src/Dimension.h b/src/Dimension.h
index bd8f10d8f2..0607be6eba 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/Makefile.am b/src/Makefile.am
index f3dd870077..f87d0290b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -403,6 +403,7 @@ SOURCEFILESMATHED = \
 	mathed/InsetMathCancelto.cpp \
 	mathed/InsetMathCases.cpp \
 	mathed/InsetMathChar.cpp \
+	mathed/InsetMathClass.cpp \
 	mathed/InsetMathColor.cpp \
 	mathed/InsetMathComment.cpp \
 	mathed/InsetMathDecoration.cpp \
@@ -447,16 +448,18 @@ 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/MathRow.cpp \
 	mathed/MathStream.cpp \
 	mathed/MathSupport.cpp \
 	mathed/TextPainter.cpp
@@ -473,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 \
@@ -518,6 +522,7 @@ HEADERFILESMATHED = \
 	mathed/InsetMathDiagram.h \
 	mathed/MathAtom.h \
 	mathed/MathAutoCorrect.h \
+	mathed/MathClass.h \
 	mathed/MathData.h \
 	mathed/MathCompletionList.h \
 	mathed/MathExtern.h \
@@ -528,6 +533,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/MetricsInfo.cpp b/src/MetricsInfo.cpp
index ea3bf0247e..fdde05f3d7 100644
--- a/src/MetricsInfo.cpp
+++ b/src/MetricsInfo.cpp
@@ -38,8 +38,8 @@ namespace lyx {
 
 MetricsBase::MetricsBase(BufferView * b, FontInfo f, int w)
 	: bv(b), font(move(f)), style(LM_ST_TEXT), fontname("mathnormal"),
-	  textwidth(w), solid_line_thickness_(1), solid_line_offset_(1),
-	  dotted_line_thickness_(1)
+	  textwidth(w), macro_nesting(0),
+	  solid_line_thickness_(1), solid_line_offset_(1), dotted_line_thickness_(1)
 {
 	if (lyxrc.zoom >= 200) {
 		// derive the line thickness from zoom factor
diff --git a/src/MetricsInfo.h b/src/MetricsInfo.h
index d2dd8b7bcf..a48b45c563 100644
--- a/src/MetricsInfo.h
+++ b/src/MetricsInfo.h
@@ -65,6 +65,8 @@ public:
 	std::string fontname;
 	/// This is the width available in pixels
 	int textwidth;
+	/// count wether the current mathdata is nested in macro(s)
+	int macro_nesting;
 
 	/// Temporarily change a full font.
 	Changer changeFontSet(std::string const & font, bool cond = true);
diff --git a/src/insets/InsetCode.h b/src/insets/InsetCode.h
index 1df6800d2d..7d4632cd16 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/InsetMath.cpp b/src/mathed/InsetMath.cpp
index 2fb1ff5a55..cbf33cdf87 100644
--- a/src/mathed/InsetMath.cpp
+++ b/src/mathed/InsetMath.cpp
@@ -13,15 +13,18 @@
 
 #include "InsetMath.h"
 #include "MathData.h"
+#include "MathRow.h"
 #include "MathStream.h"
 
+#include "MetricsInfo.h"
+
 #include "support/debug.h"
 #include "support/docstream.h"
 #include "support/gettext.h"
+#include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
 
-#include "support/lassert.h"
 
 using namespace std;
 
@@ -49,6 +52,52 @@ MathData const & InsetMath::cell(idx_type) const
 }
 
 
+MathClass InsetMath::mathClass() const
+{
+	return MC_ORD;
+}
+
+
+bool InsetMath::addToMathRow(MathRow & mrow, MetricsInfo & mi) const
+{
+	MathRow::Element e(MathRow::INSET, mi);
+	e.inset = this;
+	e.mclass = mathClass();
+	mrow.push_back(e);
+	return true;
+}
+
+void InsetMath::metricsMarkers(MetricsInfo & mi, Dimension & dim,
+                           int framesize) const
+{
+	if (!mi.base.macro_nesting)
+		Inset::metricsMarkers(dim, framesize);
+}
+
+
+void InsetMath::metricsMarkers2(MetricsInfo & mi, Dimension & dim,
+                            int framesize) const
+{
+	if (!mi.base.macro_nesting)
+		Inset::metricsMarkers2(dim, framesize);
+}
+
+
+void InsetMath::drawMarkers(PainterInfo & pi, int x, int y) const
+{
+	if (!pi.base.macro_nesting)
+		Inset::drawMarkers(pi, x, y);
+}
+
+
+void InsetMath::drawMarkers2(PainterInfo & pi, int x, int y) const
+{
+	if (!pi.base.macro_nesting)
+		Inset::drawMarkers2(pi, x, y);
+}
+
+
+
 void InsetMath::dump() const
 {
 	lyxerr << "---------------------------------------------" << endl;
diff --git a/src/mathed/InsetMath.h b/src/mathed/InsetMath.h
index da84af1010..e8c893bda5 100644
--- a/src/mathed/InsetMath.h
+++ b/src/mathed/InsetMath.h
@@ -13,6 +13,8 @@
 #ifndef MATH_INSET_H
 #define MATH_INSET_H
 
+#include "MathClass.h"
+
 #include "insets/Inset.h"
 
 
@@ -51,7 +53,10 @@ inclusion in the "real LyX insets" FormulaInset and FormulaMacroInset.
 
 */
 
+class Cursor;
 class OutputParams;
+class MetricsInfo;
+
 class InsetMathArray;
 class InsetMathAMSArray;
 class InsetMathBrace;
@@ -70,7 +75,6 @@ class InsetMathSpace;
 class InsetMathSpecialChar;
 class InsetMathSymbol;
 class InsetMathUnknown;
-
 class InsetMathRef;
 
 class HtmlStream;
@@ -85,7 +89,7 @@ class WriteStream;
 class MathData;
 class MathMacroTemplate;
 class MathMacro;
-class Cursor;
+class MathRow;
 class TextPainter;
 class TextMetricsInfo;
 class ReplaceData;
@@ -160,14 +164,22 @@ 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;
+	/// Add this inset to a math row. Return true if contents got added
+	virtual bool addToMathRow(MathRow &, MetricsInfo & mi) const;
+
+	/// draw four angular markers
+	void drawMarkers(PainterInfo & pi, int x, int y) const;
+	/// draw two angular markers
+	void drawMarkers2(PainterInfo & pi, int x, int y) const;
+	/// add space for markers
+	void metricsMarkers(MetricsInfo & mi, Dimension & dim, int framesize = 1) const;
+	/// add space for markers
+	void metricsMarkers2(MetricsInfo & mi, Dimension & dim, int framesize = 1) 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/InsetMathBoldSymbol.cpp b/src/mathed/InsetMathBoldSymbol.cpp
index 215bd5ff30..ade29f7de1 100644
--- a/src/mathed/InsetMathBoldSymbol.cpp
+++ b/src/mathed/InsetMathBoldSymbol.cpp
@@ -51,7 +51,7 @@ void InsetMathBoldSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	//Changer dummy = mi.base.changeFontSet("mathbf");
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 	++dim.wid;  // for 'double stroke'
 }
 
diff --git a/src/mathed/InsetMathBox.cpp b/src/mathed/InsetMathBox.cpp
index e9e483c29f..d0ac82f1a6 100644
--- a/src/mathed/InsetMathBox.cpp
+++ b/src/mathed/InsetMathBox.cpp
@@ -83,7 +83,7 @@ void InsetMathBox::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	Changer dummy = mi.base.changeFontSet("textnormal");
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
@@ -135,7 +135,7 @@ void InsetMathFBox::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	Changer dummy = mi.base.changeFontSet("textnormal");
 	cell(0).metrics(mi, dim);
-	metricsMarkers2(dim, 3); // 1 pixel space, 1 frame, 1 space
+	metricsMarkers2(mi, dim, 3); // 1 pixel space, 1 frame, 1 space
 }
 
 
@@ -246,7 +246,7 @@ void InsetMathMakebox::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.des += 1;
 	}
 	
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
@@ -360,7 +360,7 @@ InsetMathBoxed::InsetMathBoxed(Buffer * buf)
 void InsetMathBoxed::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers2(dim, 3); // 1 pixel space, 1 frame, 1 space
+	metricsMarkers2(mi, dim, 3); // 1 pixel space, 1 frame, 1 space
 }
 
 
diff --git a/src/mathed/InsetMathBrace.cpp b/src/mathed/InsetMathBrace.cpp
index 52ccc01b83..6870fa609d 100644
--- a/src/mathed/InsetMathBrace.cpp
+++ b/src/mathed/InsetMathBrace.cpp
@@ -55,7 +55,7 @@ void InsetMathBrace::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc = max(dim0.asc, t.asc);
 	dim.des = max(dim0.des, t.des);
 	dim.wid = dim0.width() + 2 * t.wid;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathCancel.cpp b/src/mathed/InsetMathCancel.cpp
index dcda5b63ea..0a8aad90fa 100644
--- a/src/mathed/InsetMathCancel.cpp
+++ b/src/mathed/InsetMathCancel.cpp
@@ -38,7 +38,7 @@ Inset * InsetMathCancel::clone() const
 void InsetMathCancel::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathCancelto.cpp b/src/mathed/InsetMathCancelto.cpp
index 6a04cfa246..6170cbd6fa 100644
--- a/src/mathed/InsetMathCancelto.cpp
+++ b/src/mathed/InsetMathCancelto.cpp
@@ -47,7 +47,7 @@ void InsetMathCancelto::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc = max(dim0.ascent() + 2, dim0.ascent() + dim1.ascent()) + 2 + 8;
 	dim.des = max(dim0.descent() - 2, dim1.descent()) + 2;
 	dim.wid = dim0.width() + dim1.width() + 10;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathChar.cpp b/src/mathed/InsetMathChar.cpp
index ae843bf00d..1669515b66 100644
--- a/src/mathed/InsetMathChar.cpp
+++ b/src/mathed/InsetMathChar.cpp
@@ -121,8 +121,6 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim = fm.dimension(char_);
 		kerning_ = fm.rbearing(char_) - dim.wid;
 	}
-	if (mathfont && isMathPunct())
-		dim.wid += mathed_thinmuskip(mi.base.font);
 }
 
 
@@ -266,22 +264,19 @@ void InsetMathChar::htmlize(HtmlStream & ms) const
 }
 
 
-bool InsetMathChar::isMathBin() const
+MathClass InsetMathChar::mathClass() const
 {
-	return subst_ && subst_->extra == "mathbin";
-}
-
-
-bool InsetMathChar::isMathRel() const
-{
-	return subst_ && subst_->extra == "mathrel";
-}
-
-
-bool InsetMathChar::isMathPunct() const
-{
-	return support::contains(",;", static_cast<char>(char_))
-		|| (subst_ && subst_->extra == "mathpunct");
+	// this information comes from fontmath.ltx in LaTeX source.
+	char const ch = static_cast<char>(char_);
+	if (subst_)
+		return string_to_class(subst_->extra);
+	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 ea190211b7..6e7ff72c9c 100644
--- a/src/mathed/InsetMathChar.h
+++ b/src/mathed/InsetMathChar.h
@@ -51,11 +51,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/InsetMathClass.cpp b/src/mathed/InsetMathClass.cpp
new file mode 100644
index 0000000000..0237f250d0
--- /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(mi, dim);
+}
+
+
+void InsetMathClass::draw(PainterInfo & pi, int x, int y) const
+{
+	cell(0).draw(pi, x + 1, 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 0000000000..f50040d3b4
--- /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/InsetMathColor.cpp b/src/mathed/InsetMathColor.cpp
index 0a6d0aa6b9..966fe5e99a 100644
--- a/src/mathed/InsetMathColor.cpp
+++ b/src/mathed/InsetMathColor.cpp
@@ -49,7 +49,7 @@ Inset * InsetMathColor::clone() const
 void InsetMathColor::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathComment.cpp b/src/mathed/InsetMathComment.cpp
index ff91991aa8..b8c157b2c2 100644
--- a/src/mathed/InsetMathComment.cpp
+++ b/src/mathed/InsetMathComment.cpp
@@ -50,7 +50,7 @@ Inset * InsetMathComment::clone() const
 void InsetMathComment::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathDecoration.cpp b/src/mathed/InsetMathDecoration.cpp
index cd34d4d241..acc49743fa 100644
--- a/src/mathed/InsetMathDecoration.cpp
+++ b/src/mathed/InsetMathDecoration.cpp
@@ -122,7 +122,7 @@ void InsetMathDecoration::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.des += dh_ + 2;
 	}
 
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathDelim.cpp b/src/mathed/InsetMathDelim.cpp
index 68e4430514..4ab179e7b2 100644
--- a/src/mathed/InsetMathDelim.cpp
+++ b/src/mathed/InsetMathDelim.cpp
@@ -115,7 +115,7 @@ void InsetMathDelim::metrics(MetricsInfo & mi, Dimension & dim) const
 		dw_ = 8;
 	if (dw_ < 4)
 		dw_ = 4;
-	dim.wid = dim0.width() + 2 * dw_ + 2 * mathed_thinmuskip(mi.base.font);
+	dim.wid = dim0.width() + 2 * dw_;
 	dim.asc = max(a0, d0) + h0;
 	dim.des = max(a0, d0) - h0;
 }
@@ -125,10 +125,9 @@ void InsetMathDelim::draw(PainterInfo & pi, int x, int y) const
 {
 	Dimension const dim = dimension(*pi.base.bv);
 	int const b = y - dim.asc;
-	int const skip = mathed_thinmuskip(pi.base.font);
-	cell(0).draw(pi, x + dw_ + skip, y);
-	mathed_draw_deco(pi, x + skip, b, dw_, dim.height(), left_);
-	mathed_draw_deco(pi, x + dim.width() - dw_ - skip,
+	cell(0).draw(pi, x + dw_, y);
+	mathed_draw_deco(pi, x, b, dw_, dim.height(), left_);
+	mathed_draw_deco(pi, x + dim.width() - dw_,
 		b, dw_, dim.height(), right_);
 	setPosCache(pi, x, y);
 }
diff --git a/src/mathed/InsetMathDelim.h b/src/mathed/InsetMathDelim.h
index 3a09ede2f3..5a7e816404 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/InsetMathEnsureMath.cpp b/src/mathed/InsetMathEnsureMath.cpp
index d1e69fb6c2..382c6fedd8 100644
--- a/src/mathed/InsetMathEnsureMath.cpp
+++ b/src/mathed/InsetMathEnsureMath.cpp
@@ -41,7 +41,7 @@ void InsetMathEnsureMath::metrics(MetricsInfo & mi, Dimension & dim) const
 	bool really_change_font = isTextFont(mi.base.fontname);
 	Changer dummy = mi.base.changeFontSet("mathnormal", really_change_font);
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathEnv.cpp b/src/mathed/InsetMathEnv.cpp
index cc613fd440..7582201e61 100644
--- a/src/mathed/InsetMathEnv.cpp
+++ b/src/mathed/InsetMathEnv.cpp
@@ -39,7 +39,7 @@ Inset * InsetMathEnv::clone() const
 void InsetMathEnv::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathFont.cpp b/src/mathed/InsetMathFont.cpp
index 40623f6ec6..825cf332c4 100644
--- a/src/mathed/InsetMathFont.cpp
+++ b/src/mathed/InsetMathFont.cpp
@@ -86,7 +86,7 @@ void InsetMathFont::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	Changer dummy = mi.base.changeFontSet(font());
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathFontOld.cpp b/src/mathed/InsetMathFontOld.cpp
index 20f3b1d45b..aa574bd1c6 100644
--- a/src/mathed/InsetMathFontOld.cpp
+++ b/src/mathed/InsetMathFontOld.cpp
@@ -61,7 +61,7 @@ void InsetMathFontOld::metrics(MetricsInfo & mi, Dimension & dim) const
 
 	Changer dummy = mi.base.changeFontSet(fontname, really_change_font);
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathFrac.cpp b/src/mathed/InsetMathFrac.cpp
index ab876e060d..6254924db8 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;
@@ -182,7 +207,7 @@ void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
 			dim.des = dim1.height() + 2 - 5;
 		}
 	}
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
@@ -557,7 +582,7 @@ void InsetMathBinom::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc = dim0.height() + 4 + 5;
 	dim.des = dim1.height() + 4 - 5;
 	dim.wid = max(dim0.wid, dim1.wid) + 2 * dw(dim.height()) + 4;
-	metricsMarkers2(dim);
+	metricsMarkers2(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathFrac.h b/src/mathed/InsetMathFrac.h
index c030c8a51a..96b62930ba 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/InsetMathGrid.cpp b/src/mathed/InsetMathGrid.cpp
index 053958d1c5..fca8f380d2 100644
--- a/src/mathed/InsetMathGrid.cpp
+++ b/src/mathed/InsetMathGrid.cpp
@@ -588,7 +588,7 @@ void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
 	}
 */
 	dim.wid += leftMargin() + rightMargin();
-	metricsMarkers2(dim);
+	metricsMarkers2(mi, dim);
 	// Cache the inset dimension.
 	setDimCache(mi, dim);
 }
diff --git a/src/mathed/InsetMathHull.cpp b/src/mathed/InsetMathHull.cpp
index e2ceb5c903..dc943bb520 100644
--- a/src/mathed/InsetMathHull.cpp
+++ b/src/mathed/InsetMathHull.cpp
@@ -108,7 +108,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/InsetMathLefteqn.cpp b/src/mathed/InsetMathLefteqn.cpp
index ec4fe7e9b0..176c7431cf 100644
--- a/src/mathed/InsetMathLefteqn.cpp
+++ b/src/mathed/InsetMathLefteqn.cpp
@@ -34,7 +34,7 @@ void InsetMathLefteqn::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc += 2;
 	dim.des += 2;
 	dim.wid = 4;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathOverset.cpp b/src/mathed/InsetMathOverset.cpp
index f05bbd7df7..b791c2482f 100644
--- a/src/mathed/InsetMathOverset.cpp
+++ b/src/mathed/InsetMathOverset.cpp
@@ -39,7 +39,7 @@ void InsetMathOverset::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.wid = max(dim0.width(), dim1.wid) + 4;
 	dim.asc = dim1.asc + dim0.height() + 4;
 	dim.des = dim1.des;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathPhantom.cpp b/src/mathed/InsetMathPhantom.cpp
index 4a190ea760..6d0b88df78 100644
--- a/src/mathed/InsetMathPhantom.cpp
+++ b/src/mathed/InsetMathPhantom.cpp
@@ -39,7 +39,7 @@ Inset * InsetMathPhantom::clone() const
 void InsetMathPhantom::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathRoot.cpp b/src/mathed/InsetMathRoot.cpp
index 2b4b668b9d..aa0a81bce1 100644
--- a/src/mathed/InsetMathRoot.cpp
+++ b/src/mathed/InsetMathRoot.cpp
@@ -47,7 +47,7 @@ void InsetMathRoot::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc = max(dim0.ascent()  + 5, dim1.ascent())  + 2;
 	dim.des = max(dim0.descent() - 5, dim1.descent()) + 2;
 	dim.wid = dim0.width() + dim1.width() + 10;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathScript.cpp b/src/mathed/InsetMathScript.cpp
index b4cd5909be..7363c88554 100644
--- a/src/mathed/InsetMathScript.cpp
+++ b/src/mathed/InsetMathScript.cpp
@@ -273,6 +273,19 @@ int InsetMathScript::nker(BufferView const * bv) const
 }
 
 
+MathClass InsetMathScript::mathClass() const
+{
+	// FIXME: this is a hack, since the class will not be correct if
+	// the nucleus has several elements.
+	// The correct implementation would require to linearize the nucleus.
+	if (nuc().empty())
+		return MC_ORD;
+	else
+		// return the class of last element since this is the one that counts.
+		return nuc().back()->mathClass();
+}
+
+
 void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	Dimension dim0;
@@ -320,7 +333,7 @@ void InsetMathScript::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.des = max(nd, des);
 	} else
 		dim.des = nd;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathScript.h b/src/mathed/InsetMathScript.h
index e84886b7d3..d0707439b9 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/InsetMathSideset.cpp b/src/mathed/InsetMathSideset.cpp
index 37f437db4f..b04eca7730 100644
--- a/src/mathed/InsetMathSideset.cpp
+++ b/src/mathed/InsetMathSideset.cpp
@@ -221,7 +221,7 @@ void InsetMathSideset::metrics(MetricsInfo & mi, Dimension & dim) const
 	int nd = ndes(bv);
 	int des = dyb(bv) + max(dimbl.descent(), dimbr.descent());
 	dim.des = max(nd, des);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathSize.cpp b/src/mathed/InsetMathSize.cpp
index 31cdb6a9e8..46ea29b30f 100644
--- a/src/mathed/InsetMathSize.cpp
+++ b/src/mathed/InsetMathSize.cpp
@@ -45,7 +45,7 @@ void InsetMathSize::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	Changer dummy = mi.base.changeStyle(style_);
 	cell(0).metrics(mi, dim);
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathSqrt.cpp b/src/mathed/InsetMathSqrt.cpp
index d04eab3abf..3e49abccb2 100644
--- a/src/mathed/InsetMathSqrt.cpp
+++ b/src/mathed/InsetMathSqrt.cpp
@@ -41,7 +41,7 @@ void InsetMathSqrt::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.asc += 4;
 	dim.des += 2;
 	dim.wid += 12;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathStackrel.cpp b/src/mathed/InsetMathStackrel.cpp
index 9412d99182..e34448a419 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;
@@ -70,7 +77,7 @@ void InsetMathStackrel::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.asc = dim1.ascent() + dim0.height() + 4;
 		dim.des = dim1.descent();
 	}
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathStackrel.h b/src/mathed/InsetMathStackrel.h
index 91862a7792..d6ac815182 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 620ca226a2..01bafe614d 100644
--- a/src/mathed/InsetMathSymbol.cpp
+++ b/src/mathed/InsetMathSymbol.cpp
@@ -70,7 +70,7 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.asc += h_;
 		dim.des -= h_;
 	}
-	// set striptable_
+	// set scriptable_
 	scriptable_ = false;
 	if (mi.base.style == LM_ST_DISPLAY)
 		if (sym_->inset == "cmex" || sym_->inset == "esint" ||
@@ -92,30 +92,21 @@ 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
-{
-	return sym_->extra == "mathpunct";
-}
-
-
 bool InsetMathSymbol::isOrdAlpha() const
 {
 	return sym_->extra == "mathord" || sym_->extra == "mathalpha";
 }
 
 
+MathClass InsetMathSymbol::mathClass() const
+{
+	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;
+}
+
+
 bool InsetMathSymbol::isScriptable() const
 {
 	return scriptable_;
diff --git a/src/mathed/InsetMathSymbol.h b/src/mathed/InsetMathSymbol.h
index 6ca3ded7d1..67b33fd85a 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/InsetMathUnderset.cpp b/src/mathed/InsetMathUnderset.cpp
index 46f5277543..2027cf2337 100644
--- a/src/mathed/InsetMathUnderset.cpp
+++ b/src/mathed/InsetMathUnderset.cpp
@@ -40,7 +40,7 @@ void InsetMathUnderset::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.wid = max(dim0.width(), dim1.width()) + 4;
 	dim.asc = dim1.ascent();
 	dim.des = dim1.descent() + dim0.height() + 4;
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/InsetMathXArrow.cpp b/src/mathed/InsetMathXArrow.cpp
index 96fdc83aac..a4c1c12c75 100644
--- a/src/mathed/InsetMathXArrow.cpp
+++ b/src/mathed/InsetMathXArrow.cpp
@@ -51,7 +51,7 @@ void InsetMathXArrow::metrics(MetricsInfo & mi, Dimension & dim) const
 	dim.wid = max(dim0.width(), dim1.width()) + 10;
 	dim.asc = dim0.height() + 10;
 	dim.des = dim1.height();
-	metricsMarkers(dim);
+	metricsMarkers(mi, dim);
 }
 
 
diff --git a/src/mathed/MathClass.cpp b/src/mathed/MathClass.cpp
new file mode 100644
index 0000000000..a46aad3794
--- /dev/null
+++ b/src/mathed/MathClass.cpp
@@ -0,0 +1,180 @@
+/**
+ * \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 "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 {
+
+
+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;
+}
+
+
+/*
+ * 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
new file mode 100644
index 0000000000..169bfdb51a
--- /dev/null
+++ b/src/mathed/MathClass.h
@@ -0,0 +1,69 @@
+// -*- 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 {
+
+class MetricsBase;
+
+/* 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);
+
+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
+
+#endif
diff --git a/src/mathed/MathData.cpp b/src/mathed/MathData.cpp
index bd4d53ead2..f2100afb34 100644
--- a/src/mathed/MathData.cpp
+++ b/src/mathed/MathData.cpp
@@ -30,12 +30,10 @@
 
 #include "mathed/InsetMathUnknown.h"
 
+#include "frontends/FontMetrics.h"
+
 #include "support/debug.h"
 #include "support/docstream.h"
-
-#include "frontends/FontMetrics.h"
-#include "frontends/Painter.h"
-
 #include "support/gettext.h"
 #include "support/lassert.h"
 #include "support/lyxalgo.h"
@@ -218,6 +216,34 @@ void MathData::touch() const
 }
 
 
+bool MathData::addToMathRow(MathRow & mrow, MetricsInfo & 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,53 +273,18 @@ 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.
-		mi.base.bv->coordCache().arrays().add(this, dim);
-		return;
-	}
+	MathRow mrow(mi, this);
+	mrow_cache_[mi.base.bv] = mrow;
+	mrow.metrics(mi, dim);
+	kerning_ = mrow.kerning(mi.base.bv);
 
-	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;
-
-		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;
@@ -301,11 +292,6 @@ void MathData::draw(PainterInfo & pi, int x, int 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()
@@ -313,48 +299,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 4b79f805fe..9eae46671a 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 & 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/MathFactory.cpp b/src/mathed/MathFactory.cpp
index bb7be8ba6b..f00093746f 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));
 }
 
diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp
index 4a94e03189..a5b1803f6d 100644
--- a/src/mathed/MathMacro.cpp
+++ b/src/mathed/MathMacro.cpp
@@ -68,7 +68,49 @@ public:
 	///
 	InsetCode lyxCode() const { return ARGUMENT_PROXY_CODE; }
 	///
+	bool addToMathRow(MathRow & mrow, MetricsInfo & mi) const
+	{
+		// macro arguments are in macros
+		LATTEST(mi.base.macro_nesting > 0);
+		if (mi.base.macro_nesting == 1)
+			mi.base.macro_nesting = 0;
+
+		MathRow::Element e_beg(MathRow::BEG_ARG, mi);
+		e_beg.macro = mathMacro_;
+		e_beg.ar = &mathMacro_->cell(idx_);
+		mrow.push_back(e_beg);
+
+		mathMacro_->macro()->unlock();
+		bool has_contents = mathMacro_->cell(idx_).addToMathRow(mrow, mi);
+		mathMacro_->macro()->lock();
+
+		// if there was no contents, and the contents is editable,
+		// then we insert a box instead.
+		if (!has_contents && mi.base.macro_nesting == 0) {
+			MathRow::Element e(MathRow::BOX, mi);
+			e.color = Color_mathline;
+			mrow.push_back(e);
+			has_contents = true;
+		}
+
+		if (mi.base.macro_nesting == 0)
+			mi.base.macro_nesting = 1;
+
+		MathRow::Element e_end(MathRow::END_ARG, mi);
+		e_end.macro = mathMacro_;
+		e_end.ar = &mathMacro_->cell(idx_);
+
+		mrow.push_back(e_end);
+
+		return has_contents;
+	}
+	///
 	void metrics(MetricsInfo & mi, Dimension & dim) const {
+		// macro arguments are in macros
+		LATTEST(mi.base.macro_nesting > 0);
+		if (mi.base.macro_nesting == 1)
+			mi.base.macro_nesting = 0;
+
 		mathMacro_->macro()->unlock();
 		mathMacro_->cell(idx_).metrics(mi, dim);
 
@@ -77,6 +119,8 @@ public:
 			def_.metrics(mi, dim);
 
 		mathMacro_->macro()->lock();
+		if (mi.base.macro_nesting == 0)
+			mi.base.macro_nesting = 1;
 	}
 	// write(), normalize(), infoize() and infoize2() are not needed since
 	// MathMacro uses the definition and not the expanded cells.
@@ -94,6 +138,10 @@ public:
 	void octave(OctaveStream & os) const { os << mathMacro_->cell(idx_); }
 	///
 	void draw(PainterInfo & pi, int x, int y) const {
+		LATTEST(pi.base.macro_nesting > 0);
+		if (pi.base.macro_nesting == 1)
+			pi.base.macro_nesting = 0;
+
 		if (mathMacro_->editMetrics(pi.base.bv)) {
 			// The only way a ArgumentProxy can appear is in a cell of the
 			// MathMacro. Moreover the cells are only drawn in the DISPLAY_FOLDED
@@ -111,6 +159,9 @@ public:
 			def_.draw(pi, x, y);
 		} else
 			mathMacro_->cell(idx_).draw(pi, x, y);
+
+		if (pi.base.macro_nesting == 0)
+			pi.base.macro_nesting = 1;
 	}
 	///
 	size_t idx() const { return idx_; }
@@ -266,6 +317,45 @@ MathMacro::~MathMacro()
 }
 
 
+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])
+		return InsetMath::addToMathRow(mrow, mi);
+
+	MathRow::Element e_beg(MathRow::BEG_MACRO, mi);
+	e_beg.macro = this;
+	mrow.push_back(e_beg);
+
+	++mi.base.macro_nesting;
+
+	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.base.macro_nesting == 1) {
+		MathRow::Element e(MathRow::BOX, mi);
+		e.color = Color_mathmacroblend;
+		mrow.push_back(e);
+		has_contents = true;
+	}
+
+	--mi.base.macro_nesting;
+
+	MathRow::Element e_end(MathRow::END_MACRO, mi);
+	e_end.macro = this;
+	mrow.push_back(e_end);
+
+	return has_contents;
+}
+
+
 Inset * MathMacro::clone() const
 {
 	MathMacro * copy = new MathMacro(*this);
@@ -344,7 +434,7 @@ bool MathMacro::editMode(BufferView const * bv) const {
 }
 
 
-MacroData const * MathMacro::macro()
+MacroData const * MathMacro::macro() const
 {
 	return d->macro_;
 }
@@ -358,6 +448,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.base.macro_nesting;
+
 	// set edit mode for which we will have calculated metrics. But only
 	d->editing_[mi.base.bv] = editMode(mi.base.bv);
 
@@ -371,7 +464,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.wid += bsdim.width() + 1;
 		dim.asc = max(bsdim.ascent(), dim.ascent());
 		dim.des = max(bsdim.descent(), dim.descent());
-		metricsMarkers(dim);
+		metricsMarkers(mi, dim);
 	} else if (lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_LIST
 		   && d->editing_[mi.base.bv]) {
 		// Macro will be edited in a old-style list mode here:
@@ -411,7 +504,7 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
 		dim.asc += 1;
 		dim.des += 1;
 		dim.wid += 2;
-		metricsMarkers2(dim);
+		metricsMarkers2(mi, dim);
 	} else {
 		LBUFERR(d->macro_);
 
@@ -446,6 +539,9 @@ void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
 			dim.des += 2;
 		}
 	}
+
+	// restore macro nesting
+	--mi.base.macro_nesting;
 }
 
 
@@ -614,6 +710,9 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
 				  dim.height() - 2, Color_mathmacroframe);
 		drawMarkers2(pi, expx, expy);
 	} else {
+		// the macro contents is not editable (except the arguments)
+		++pi.base.macro_nesting;
+
 		bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX;
 		bool upshape = currentMode() == TEXT_MODE;
 		Changer dummy = pi.base.font.changeShape(upshape ? UP_SHAPE
@@ -648,8 +747,11 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const
 		} else
 			d->expanded_.draw(pi, expx, expy);
 
+		--pi.base.macro_nesting;
+
 		if (!drawBox)
 			drawMarkers(pi, x, y);
+
 	}
 
 	// edit mode changed?
diff --git a/src/mathed/MathMacro.h b/src/mathed/MathMacro.h
index 1a8654ac40..4eb93ba534 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 & 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/MathMacroTemplate.cpp b/src/mathed/MathMacroTemplate.cpp
index cf7427205d..b4d1c1cfcb 100644
--- a/src/mathed/MathMacroTemplate.cpp
+++ b/src/mathed/MathMacroTemplate.cpp
@@ -269,7 +269,7 @@ Inset * InsetMathWrapper::clone() const
 void InsetMathWrapper::metrics(MetricsInfo & mi, Dimension & dim) const
 {
 	value_->metrics(mi, dim);
-	//metricsMarkers2(dim);
+	//metricsMarkers2(mi, dim);
 }
 
 
diff --git a/src/mathed/MathRow.cpp b/src/mathed/MathRow.cpp
new file mode 100644
index 0000000000..3dbfda6974
--- /dev/null
+++ b/src/mathed/MathRow.cpp
@@ -0,0 +1,319 @@
+/**
+ * \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/FontMetrics.h"
+#include "frontends/Painter.h"
+
+#include "support/debug.h"
+#include "support/docstring.h"
+#include "support/lassert.h"
+
+#include <ostream>
+
+using namespace std;
+
+namespace lyx {
+
+
+MathRow::Element::Element(Type t, MetricsInfo &mi)
+	: type(t), macro_nesting(mi.base.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(Element(BEGIN, mi));
+	back().mclass = MC_OPEN;
+
+	// Then insert the MathData argument
+	bool const has_contents = ar->addToMathRow(*this, mi);
+
+	// empty arrays are visible when they are editable
+	// we reserve the necessary space anyway (even if nothing gets drawn)
+	if (!has_contents) {
+		Element e(BOX, mi);
+		e.color = Color_mathline;
+		push_back(e);
+	}
+
+	// Finally there is a dummy element of type "close"
+	push_back(Element(END, mi));
+	back().mclass = MC_CLOSE;
+
+	/* Do spacing only in math mode. This test is a bit clumsy,
+	 * but it is used in other places for guessing the current mode.
+	 */
+	if (!isMathFont(mi.base.fontname))
+		return;
+
+	// update classes
+	for (int i = 1 ; i != static_cast<int>(elements_.size()) - 1 ; ++i) {
+		if (elements_[i].type != INSET)
+			continue;
+		update_class(elements_[i].mclass, elements_[before(i)].mclass,
+		             elements_[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>(elements_.size()) ; ++i) {
+		if (elements_[i].type != INSET)
+			continue;
+		Element & bef = elements_[before(i)];
+		int spc = class_spacing(bef.mclass, elements_[i].mclass, mi.base);
+		bef.after = spc / 2;
+		// this is better than spc / 2 to avoid rounding problems
+		elements_[i].before = spc - spc / 2;
+	}
+	// Do not lose spacing allocated to extremities
+	if (!elements_.empty()) {
+		elements_[after(0)].before += elements_.front().after;
+		elements_[before(elements_.size() - 1)].after += elements_.back().before;
+	}
+}
+
+
+int MathRow::before(int i) const
+{
+	do
+		--i;
+	while (elements_[i].type != BEGIN
+		   && elements_[i].type != INSET);
+
+	return i;
+}
+
+
+int MathRow::after(int i) const
+{
+	do
+		++i;
+	while (elements_[i].type != END
+		   && elements_[i].type != 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 (Element const & e : elements_) {
+		Dimension d;
+		mi.base.macro_nesting = e.macro_nesting;
+		switch (e.type) {
+		case BEGIN:
+		case END:
+			break;
+		case INSET:
+			e.inset->metrics(mi, d);
+			d.wid += e.before + e.after;
+			coords.insets().add(e.inset, d);
+			break;
+		case BEG_MACRO:
+			e.macro->macro()->lock();
+			// Add a macro to current list
+			dim_macros[e.macro] = Dimension();
+			break;
+		case END_MACRO:
+			LATTEST(dim_macros.find(e.macro) != dim_macros.end());
+			e.macro->macro()->unlock();
+			// Cache the dimension of the macro and remove it from
+			// tracking map.
+			coords.insets().add(e.macro, dim_macros[e.macro]);
+			dim_macros.erase(e.macro);
+			break;
+			// This is basically like macros
+		case BEG_ARG:
+			if (e.macro)
+				e.macro->macro()->unlock();
+			dim_arrays[e.ar] = Dimension();
+			break;
+		case END_ARG:
+			LATTEST(dim_arrays.find(e.ar) != dim_arrays.end());
+			if (e.macro)
+				e.macro->macro()->lock();
+			coords.arrays().add(e.ar, dim_arrays[e.ar]);
+			dim_arrays.erase(e.ar);
+			break;
+		case BOX:
+			d = theFontMetrics(mi.base.font).dimension('I');
+			d.wid += e.before + e.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 (e.compl_text.empty())
+			continue;
+		FontInfo font = mi.base.font;
+		augmentFont(font, "mathnormal");
+		dim.wid += mathed_string_width(font, e.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 (Element const & e : elements_) {
+		pi.base.macro_nesting = e.macro_nesting;
+		switch (e.type) {
+		case INSET: {
+			// 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(e.inset);
+			Dimension d2 = d;
+			d2.wid -= e.before + e.after;
+			coords.insets().add(e.inset, d2);
+			e.inset->drawSelection(pi, x + e.before, y);
+			e.inset->draw(pi, x + e.before, y);
+			coords.insets().add(e.inset, x, y);
+			coords.insets().add(e.inset, d);
+			x += d.wid;
+			break;
+		}
+		case BEG_MACRO:
+			coords.insets().add(e.macro, x, y);
+			break;
+		case BEG_ARG:
+			coords.arrays().add(e.ar, x, y);
+			// if the macro is being edited, then the painter is in
+			// monochrome mode.
+			if (e.macro->editMetrics(pi.base.bv))
+				pi.pain.leaveMonochromeMode();
+			break;
+		case END_ARG:
+			if (e.macro->editMetrics(pi.base.bv))
+				pi.pain.enterMonochromeMode(Color_mathbg, Color_mathmacroblend);
+			break;
+		case BOX: {
+			Dimension const d = theFontMetrics(pi.base.font).dimension('I');
+			// the box is not visible in non-editable context (except for grey macro boxes).
+			if (e.macro_nesting == 0 || e.color == Color_mathmacroblend)
+				pi.pain.rectangle(x + e.before, y - d.ascent(),
+								  d.width(), d.height(), e.color);
+			x += d.wid;
+			break;
+		}
+		case BEGIN:
+		case END:
+		case END_MACRO:
+			break;
+		}
+
+		if (e.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 = e.compl_text.substr(0, e.compl_unique_to);
+		docstring const s2 = e.compl_text.substr(e.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 (elements_.empty())
+		return 0;
+	InsetMath const * inset = elements_[before(elements_.size() - 1)].inset;
+	return inset ? inset->kerning(bv) : 0;
+}
+
+
+ostream & operator<<(ostream & os, MathRow::Element const & e)
+{
+	switch (e.type) {
+	case MathRow::BEGIN:
+		os << "{";
+		break;
+	case MathRow::END:
+		os << "}";
+		break;
+	case MathRow::INSET:
+		os << "<" << e.before << "-"
+		   << to_utf8(class_to_string(e.mclass))
+		   << "-" << e.after << ">";
+		break;
+	case MathRow::BEG_MACRO:
+		os << "\\" << to_utf8(e.macro->name()) << "[";
+		break;
+	case MathRow::END_MACRO:
+		os << "]";
+		break;
+	case MathRow::BEG_ARG:
+		os << "#(";
+		break;
+	case MathRow::END_ARG:
+		os << ")";
+		break;
+	case MathRow::BOX:
+		os << "@";
+		break;
+	}
+	return os;
+}
+
+
+ostream & operator<<(ostream & os, MathRow const & mrow)
+{
+	for (MathRow::Element const & e : mrow)
+		os << e << "  ";
+	return os;
+}
+
+} // namespace lyx
diff --git a/src/mathed/MathRow.h b/src/mathed/MathRow.h
new file mode 100644
index 0000000000..3d31e1cbcb
--- /dev/null
+++ b/src/mathed/MathRow.h
@@ -0,0 +1,145 @@
+// -*- 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 "ColorCode.h"
+
+#include "support/docstring.h"
+
+#include <vector>
+
+namespace lyx {
+
+class BufferView;
+class Dimension;
+class MetricsInfo;
+class PainterInfo;
+
+class InsetMath;
+class MathData;
+class MathMacro;
+
+/*
+ * 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:
+	// What row elements can be
+	enum Type {
+		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
+		BOX // an empty box
+	};
+
+	// An elements, together with its spacing
+	struct Element
+	{
+		///
+		Element(Type t, MetricsInfo & mi);
+
+		/// Classifies the contents of the object
+		Type type;
+		/// count wether the current mathdata is nested in macro(s)
+		int macro_nesting;
+
+		/// When type 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 type is BEG_MACRO, END_MACRO, BEG_ARG, END_ARG
+		/// the math macro
+		MathMacro const * macro;
+
+		// type is BEG_ARG, END_ARG
+		MathData const * ar;
+
+		// type is BOX
+		ColorCode color;
+	};
+
+	///
+	MathRow() {};
+	///
+	typedef std::vector<Element> Elements;
+	///
+	typedef Elements::iterator iterator;
+	///
+	typedef Elements::const_iterator const_iterator;
+	///
+	iterator begin() { return elements_.begin(); }
+	///
+	iterator end() { return elements_.end(); }
+	///
+	const_iterator begin() const { return elements_.begin(); }
+	///
+	const_iterator end() const { return elements_.end(); }
+	//
+	void push_back(Element const & e) { elements_.push_back(e); }
+	//
+	Element & back() { return elements_.back(); }
+
+	// create the math row by unwinding all macros in the MathData and
+	// compute the spacings.
+	MathRow(MetricsInfo & 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;
+
+	///
+	Elements elements_;
+};
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow::Element const & elt);
+
+///
+std::ostream & operator<<(std::ostream & os, MathRow const & mrow);
+
+
+} // namespace lyx
+
+#endif
diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp
index 0c610b1e69..3d708406d3 100644
--- a/src/mathed/MathSupport.cpp
+++ b/src/mathed/MathSupport.cpp
@@ -528,6 +528,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)
@@ -671,13 +674,6 @@ void mathedSymbolDim(MetricsInfo & mi, Dimension & dim, latexkeys const * sym)
 	std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
 	Changer dummy = mi.base.changeFontSet(font);
 	mathed_string_dim(mi.base.font, sym->draw, dim);
-	// seperate things a bit
-	if (sym->extra == "mathbin")
-		dim.wid += 2 * mathed_medmuskip(mi.base.font);
-	else if (sym->extra == "mathrel")
-		dim.wid += 2 * mathed_thickmuskip(mi.base.font);
-	else if (sym->extra == "mathpunct")
-		dim.wid += mathed_thinmuskip(mi.base.font);
 }
 
 
@@ -693,10 +689,6 @@ void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym)
 		sym->extra == "mathalpha" &&
 		pi.base.fontname == "mathit";
 	std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
-	if (sym->extra == "mathbin")
-		x += mathed_medmuskip(pi.base.font);
-	else if (sym->extra == "mathrel")
-		x += mathed_thickmuskip(pi.base.font);
 
 	Changer dummy = pi.base.changeFontSet(font);
 	pi.draw(x, y, sym->draw);