]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/MathParser.cpp
Substack support for XHTML.
[lyx.git] / src / mathed / MathParser.cpp
index 7c67f0df87d167b61d32a9a18d3c857c092ee144..ad52e76105c3a0a0f8038672cdaa3230bf53237f 100644 (file)
@@ -58,6 +58,7 @@ following hack as starting point to write some macros:
 #include "InsetMathSpace.h"
 #include "InsetMathSplit.h"
 #include "InsetMathSqrt.h"
+#include "InsetMathString.h"
 #include "InsetMathTabular.h"
 #include "MathMacroTemplate.h"
 #include "MathFactory.h"
@@ -65,6 +66,7 @@ following hack as starting point to write some macros:
 #include "MathSupport.h"
 
 #include "Buffer.h"
+#include "BufferParams.h"
 #include "Encoding.h"
 #include "Lexer.h"
 
@@ -212,7 +214,7 @@ bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & cellcol)
 
 
 /*!
- * Check wether the last row is empty and remove it if yes.
+ * Check whether the last row is empty and remove it if yes.
  * Otherwise the following code
  * \verbatim
 \begin{array}{|c|c|}
@@ -223,6 +225,8 @@ bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & cellcol)
  * \endverbatim
  * will result in a grid with 3 rows (+ the dummy row that is always present),
  * because the last '\\' opens a new row.
+ * Note that this is only needed for inner-hull grid types, such as array
+ * or aligned, but not for outer-hull grid types, such as eqnarray or align.
  */
 void delEmptyLastRow(InsetMathGrid & grid)
 {
@@ -239,6 +243,19 @@ void delEmptyLastRow(InsetMathGrid & grid)
 }
 
 
+/*!
+ * Tell whether the environment name corresponds to an inner-hull grid type.
+ */
+bool innerHull(docstring const & name)
+{
+       // For [bB]matrix, [vV]matrix, and pmatrix we can check the suffix only
+       return name == "array" || name == "cases" || name == "aligned"
+               || name == "alignedat" || name == "gathered" || name == "split"
+               || name == "subarray" || name == "tabular" || name == "matrix"
+               || name.substr(1) == "matrix";
+}
+
+
 // These are TeX's catcodes
 enum CatCode {
        catEscape,     // 0    backslash
@@ -442,8 +459,6 @@ Parser::Parser(Lexer & lexer, parse_mode mode, Buffer * buf)
        : lineno_(lexer.lineNumber()), pos_(0), mode_(mode), success_(true),
          buffer_(buf)
 {
-       if (buf)
-               buf->updateMacros();
        tokenize(lexer.getStream());
        lexer.eatLine();
 }
@@ -452,8 +467,6 @@ Parser::Parser(Lexer & lexer, parse_mode mode, Buffer * buf)
 Parser::Parser(istream & is, parse_mode mode, Buffer * buf)
        : lineno_(0), pos_(0), mode_(mode), success_(true), buffer_(buf)
 {
-       if (buf)
-               buf->updateMacros();
        tokenize(is);
 }
 
@@ -461,8 +474,6 @@ Parser::Parser(istream & is, parse_mode mode, Buffer * buf)
 Parser::Parser(docstring const & str, parse_mode mode, Buffer * buf)
        : lineno_(0), pos_(0), mode_(mode), success_(true), buffer_(buf)
 {
-       if (buf)
-               buf->updateMacros();
        tokenize(str);
 }
 
@@ -618,6 +629,8 @@ void Parser::tokenize(docstring const & buffer)
                                if (!is) {
                                        error("unexpected end of input");
                                } else {
+                                       if (c == '\n')
+                                               c = ' ';
                                        docstring s(1, c);
                                        if (catcode(c) == catLetter) {
                                                // collect letters
@@ -836,18 +849,25 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                Token const & n = getToken();
                                if (n.cat() == catMath) {
                                        // TeX's $$...$$ syntax for displayed math
-                                       cell->push_back(MathAtom(new InsetMathHull(buffer_, hullEquation)));
-                                       parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
-                                       getToken(); // skip the second '$' token
+                                       if (mode == InsetMath::UNDECIDED_MODE) {
+                                               cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation)));
+                                               parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
+                                               getToken(); // skip the second '$' token
+                                       } else {
+                                               // This is not an outer hull and display math is
+                                               // not allowed inside text mode environments.
+                                               error("bad math environment");
+                                               break;
+                                       }
                                } else {
                                        // simple $...$  stuff
                                        putback();
                                        if (mode == InsetMath::UNDECIDED_MODE) {
-                                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullSimple)));
+                                               cell->push_back(MathAtom(new InsetMathHull(buf, hullSimple)));
                                                parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
                                        } else {
                                                // Don't create nested math hulls (bug #5392)
-                                               cell->push_back(MathAtom(new InsetMathEnsureMath(buffer_)));
+                                               cell->push_back(MathAtom(new InsetMathEnsureMath(buf)));
                                                parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE, InsetMath::MATH_MODE);
                                        }
                                }
@@ -859,8 +879,19 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        }
 
                        else {
-                               error("something strange in the parser");
-                               break;
+                               Token const & n = getToken();
+                               if (n.cat() == catMath) {
+                                       error("something strange in the parser");
+                                       break;
+                               } else {
+                                       // This is inline math ($...$), but the parser thinks we are
+                                       // already in math mode and latex would issue an error, unless we
+                                       // are inside a text mode user macro. We have no way to tell, so
+                                       // let's play safe by using \ensuremath, as it will work in any case.
+                                       putback();
+                                       cell->push_back(MathAtom(new InsetMathEnsureMath(buf)));
+                                       parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE, InsetMath::MATH_MODE);
+                               }
                        }
                }
 
@@ -918,14 +949,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        // we need no new script inset if the last thing was a scriptinset,
                        // which has that script already not the same script already
                        if (!cell->size())
-                               cell->push_back(MathAtom(new InsetMathScript(buffer_, up)));
+                               cell->push_back(MathAtom(new InsetMathScript(buf, up)));
                        else if (cell->back()->asScriptInset() &&
                                        !cell->back()->asScriptInset()->has(up))
                                cell->back().nucleus()->asScriptInset()->ensure(up);
                        else if (cell->back()->asScriptInset())
-                               cell->push_back(MathAtom(new InsetMathScript(buffer_, up)));
+                               cell->push_back(MathAtom(new InsetMathScript(buf, up)));
                        else
-                               cell->back() = MathAtom(new InsetMathScript(buffer_, cell->back(), up));
+                               cell->back() = MathAtom(new InsetMathScript(buf, cell->back(), up));
                        InsetMathScript * p = cell->back().nucleus()->asScriptInset();
                        // special handling of {}-bases
                        // Here we could remove the brace inset for things
@@ -939,6 +970,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        // in an unreliable way. See this thread
                        // http://www.mail-archive.com/lyx-devel%40lists.lyx.org/msg104917.html
                        // for more details.
+                       // However, we remove empty braces because they look
+                       // ugly on screen and we are sure that they were added
+                       // by the write() method (and will be re-added on save).
+                       if (p->nuc().size() == 1 &&
+                           p->nuc().back()->asBraceInset() &&
+                           p->nuc().back()->asBraceInset()->cell(0).empty())
+                               p->nuc().erase(0);
+
                        parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode);
                        if (limits) {
                                p->limits(limits);
@@ -978,7 +1017,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        break;
                                s += t.asString();
                        }
-                       cell->push_back(MathAtom(new InsetMathComment(buffer_, s)));
+                       cell->push_back(MathAtom(new InsetMathComment(buf, s)));
                        skipSpaces();
                }
 
@@ -1018,9 +1057,12 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        if (nextToken().cat() == catBegin)
                                parse(display, FLAG_ITEM, InsetMath::MATH_MODE);
                        
-                       cell->push_back(MathAtom(new MathMacroTemplate(buffer_, name,
-                               nargs, 0, MacroTypeDef,
+                       cell->push_back(MathAtom(new MathMacroTemplate(buf,
+                               name, nargs, 0, MacroTypeDef,
                                vector<MathData>(), def, display)));
+
+                       if (buf && (mode_ & Parse::TRACKMACRO))
+                               buf->usermacros.insert(name);
                }
                
                else if (t.cs() == "newcommand" ||
@@ -1063,9 +1105,12 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        if (nextToken().cat() == catBegin)
                                parse(display, FLAG_ITEM, InsetMath::MATH_MODE);
                        
-                       cell->push_back(MathAtom(new MathMacroTemplate(buffer_, name,
-                               nargs, optionals, MacroTypeNewcommand,
+                       cell->push_back(MathAtom(new MathMacroTemplate(buf,
+                               name, nargs, optionals, MacroTypeNewcommand,
                                optionalValues, def, display)));
+
+                       if (buf && (mode_ & Parse::TRACKMACRO))
+                               buf->usermacros.insert(name);
                }
                
                else if (t.cs() == "newcommandx" ||
@@ -1181,18 +1226,17 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        if (nextToken().cat() == catBegin)
                                parse(display, FLAG_ITEM, InsetMath::MATH_MODE);
 
-                       cell->push_back(MathAtom(new MathMacroTemplate(buffer_, name,
-                               nargs, optionals, MacroTypeNewcommandx,
+                       cell->push_back(MathAtom(new MathMacroTemplate(buf,
+                               name, nargs, optionals, MacroTypeNewcommandx,
                                optionalValues, def, display)));
+
+                       if (buf && (mode_ & Parse::TRACKMACRO))
+                               buf->usermacros.insert(name);
                }
 
                else if (t.cs() == "(") {
-                       if (mode == InsetMath::MATH_MODE) {
-                               error("bad math environment");
-                               break;
-                       }
-                       cell->push_back(MathAtom(new InsetMathHull(buffer_, hullSimple)));
-                       parse2(cell->back(), FLAG_SIMPLE2, InsetMath::MATH_MODE, false);
+                       cell->push_back(MathAtom(new InsetMathEnsureMath(buf)));
+                       parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE2, InsetMath::MATH_MODE);
                }
 
                else if (t.cs() == "[") {
@@ -1200,7 +1244,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                error("bad math environment");
                                break;
                        }
-                       cell->push_back(MathAtom(new InsetMathHull(buffer_, hullEquation)));
+                       cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation)));
                        parse2(cell->back(), FLAG_EQUATION, InsetMath::MATH_MODE, false);
                }
 
@@ -1229,7 +1273,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        // probably need to refine this test.
                                        // Right now we only have to test for
                                        // single line hull insets.
-                                       if (grid.nrows() > 1)
+                                       if (grid.nrows() > 1 && innerHull(name))
                                                delEmptyLastRow(grid);
                                        return success_;
                                }
@@ -1303,11 +1347,15 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                }
 #endif
 
-               else if (t.cs() == "limits")
-                       limits = 1;
-
-               else if (t.cs() == "nolimits")
-                       limits = -1;
+               else if (t.cs() == "limits" || t.cs() == "nolimits") {
+                       CatCode const cat = nextToken().cat();
+                       if (cat == catSuper || cat == catSub)
+                               limits = t.cs() == "limits" ? 1 : -1;
+                       else {
+                               MathAtom at = createInsetMath(t.cs(), buf);
+                               cell->push_back(at);
+                       }
+               }
 
                else if (t.cs() == "nonumber") {
                        if (grid.asHullInset())
@@ -1327,11 +1375,11 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        MathData ar;
                        parse(ar, FLAG_OPTION, mode);
                        if (ar.size()) {
-                               cell->push_back(MathAtom(new InsetMathRoot(buffer_)));
+                               cell->push_back(MathAtom(new InsetMathRoot(buf)));
                                cell->back().nucleus()->cell(0) = ar;
                                parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
                        } else {
-                               cell->push_back(MathAtom(new InsetMathSqrt(buffer_)));
+                               cell->push_back(MathAtom(new InsetMathSqrt(buf)));
                                parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
                        }
                }
@@ -1341,11 +1389,11 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        MathData ar;
                        parse(ar, FLAG_OPTION, mode);
                        if (ar.size()) {
-                               cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::UNIT)));
+                               cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNIT)));
                                cell->back().nucleus()->cell(0) = ar;
                                parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
                        } else {
-                               cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::UNIT, 1)));
+                               cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNIT, 1)));
                                parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
                        }
                }
@@ -1355,10 +1403,10 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        MathData ar;
                        parse(ar, FLAG_OPTION, mode);
                        if (ar.size()) {
-                               cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::UNITFRAC, 3)));
+                               cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNITFRAC, 3)));
                                cell->back().nucleus()->cell(2) = ar;
                        } else {
-                               cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::UNITFRAC)));
+                               cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNITFRAC)));
                        }
                        parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
                        parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
@@ -1369,11 +1417,11 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        docstring const arg = getArg('[', ']');
                        //lyxerr << "got so far: '" << arg << "'" << endl;                              
                                if (arg == "l")
-                                       cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::CFRACLEFT)));
+                                       cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::CFRACLEFT)));
                                else if (arg == "r")
-                                       cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::CFRACRIGHT)));
+                                       cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::CFRACRIGHT)));
                                else if (arg.empty() || arg == "c")
-                                       cell->push_back(MathAtom(new InsetMathFrac(buffer_, InsetMathFrac::CFRAC)));
+                                       cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::CFRAC)));
                                else {
                                        error("found invalid optional argument");
                                        break;
@@ -1390,9 +1438,15 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
 
                else if (t.cs() == "ref" || t.cs() == "eqref" || t.cs() == "prettyref"
                          || t.cs() == "pageref" || t.cs() == "vpageref" || t.cs() == "vref") {
-                       cell->push_back(MathAtom(new InsetMathRef(buffer_, t.cs())));
-                       parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
-                       parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
+                       cell->push_back(MathAtom(new InsetMathRef(buf, t.cs())));
+                       docstring const opt = parse_verbatim_option();
+                       docstring const ref = parse_verbatim_item();
+                       if (!opt.empty()) {
+                               cell->back().nucleus()->cell(1).push_back(
+                                       MathAtom(new InsetMathString(opt)));
+                       }
+                       cell->back().nucleus()->cell(0).push_back(
+                                       MathAtom(new InsetMathString(ref)));
                }
 
                else if (t.cs() == "left") {
@@ -1427,7 +1481,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        if (name == "array" || name == "subarray") {
                                docstring const valign = parse_verbatim_option() + 'c';
                                docstring const halign = parse_verbatim_item();
-                               cell->push_back(MathAtom(new InsetMathArray(buffer_, name,
+                               cell->push_back(MathAtom(new InsetMathArray(buf, name,
                                        InsetMathGrid::guessColumns(halign), 1, (char)valign[0], halign)));
                                parse2(cell->back(), FLAG_END, mode, false);
                        }
@@ -1435,7 +1489,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        else if (name == "tabular") {
                                docstring const valign = parse_verbatim_option() + 'c';
                                docstring const halign = parse_verbatim_item();
-                               cell->push_back(MathAtom(new InsetMathTabular(buffer_, name,
+                               cell->push_back(MathAtom(new InsetMathTabular(buf, name,
                                        InsetMathGrid::guessColumns(halign), 1, (char)valign[0], halign)));
                                parse2(cell->back(), FLAG_END, InsetMath::TEXT_MODE, false);
                        }
@@ -1449,17 +1503,13 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                docstring const valign = parse_verbatim_option() + 'c';
                                // ignore this for a while
                                getArg('{', '}');
-                               cell->push_back(MathAtom(new InsetMathSplit(buffer_, name, (char)valign[0])));
+                               cell->push_back(MathAtom(new InsetMathSplit(buf, name, (char)valign[0])));
                                parse2(cell->back(), FLAG_END, mode, false);
                        }
 
                        else if (name == "math") {
-                               if (mode == InsetMath::MATH_MODE) {
-                                       error("bad math environment");
-                                       break;
-                               }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullSimple)));
-                               parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, true);
+                               cell->push_back(MathAtom(new InsetMathEnsureMath(buf)));
+                               parse(cell->back().nucleus()->cell(0), FLAG_END, InsetMath::MATH_MODE);
                        }
 
                        else if (name == "equation" || name == "equation*"
@@ -1468,7 +1518,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullEquation)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, (name == "equation"));
                        }
 
@@ -1477,7 +1527,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullEqnArray)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullEqnArray)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1486,7 +1536,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullAlign)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullAlign)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1495,7 +1545,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullFlAlign)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullFlAlign)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1506,7 +1556,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                }
                                // ignore this for a while
                                getArg('{', '}');
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullAlignAt)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullAlignAt)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1517,7 +1567,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                }
                                // ignore this for a while
                                getArg('{', '}');
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullXAlignAt)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullXAlignAt)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1528,7 +1578,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                }
                                // ignore this for a while
                                getArg('{', '}');
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullXXAlignAt)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullXXAlignAt)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1537,7 +1587,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullMultline)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullMultline)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1546,7 +1596,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                        error("bad math environment");
                                        break;
                                }
-                               cell->push_back(MathAtom(new InsetMathHull(buffer_, hullGather)));
+                               cell->push_back(MathAtom(new InsetMathHull(buf, hullGather)));
                                parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
                        }
 
@@ -1557,7 +1607,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                } else if (l->inset == "split") {
                                        docstring const valign = parse_verbatim_option() + 'c';
                                        cell->push_back(MathAtom(
-                                               new InsetMathSplit(buffer_, name, (char)valign[0])));
+                                               new InsetMathSplit(buf, name, (char)valign[0])));
                                        parse2(cell->back(), FLAG_END, mode, false);
                                } else {
                                        success_ = false;
@@ -1570,7 +1620,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                                       << "'." << endl;
                                        }
                                        // create generic environment inset
-                                       cell->push_back(MathAtom(new InsetMathEnv(buffer_, name)));
+                                       cell->push_back(MathAtom(new InsetMathEnv(buf, name)));
                                        parse(cell->back().nucleus()->cell(0), FLAG_END, mode);
                                }
                        }
@@ -1583,7 +1633,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                               << to_utf8(name) << "'" << endl;
                                }
                                // create generic environment inset
-                               cell->push_back(MathAtom(new InsetMathEnv(buffer_, name)));
+                               cell->push_back(MathAtom(new InsetMathEnv(buf, name)));
                                parse(cell->back().nucleus()->cell(0), FLAG_END, mode);
                        }
                }
@@ -1637,14 +1687,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
 
                else if (t.cs() == "color") {
                        docstring const color = parse_verbatim_item();
-                       cell->push_back(MathAtom(new InsetMathColor(buffer_, true, color)));
+                       cell->push_back(MathAtom(new InsetMathColor(buf, true, color)));
                        parse(cell->back().nucleus()->cell(0), flags, mode);
                        return success_;
                }
 
                else if (t.cs() == "textcolor") {
                        docstring const color = parse_verbatim_item();
-                       cell->push_back(MathAtom(new InsetMathColor(buffer_, false, color)));
+                       cell->push_back(MathAtom(new InsetMathColor(buf, false, color)));
                        parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE);
                }
 
@@ -1657,6 +1707,11 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                else if (t.cs() == "substack") {
                        cell->push_back(createInsetMath(t.cs(), buf));
                        parse2(cell->back(), FLAG_ITEM, mode, false);
+                       // Delete empty last row if present
+                       InsetMathGrid & subgrid =
+                               *(cell->back().nucleus()->asGridInset());
+                       if (subgrid.nrows() > 1)
+                               delEmptyLastRow(subgrid);
                }
 
                else if (t.cs() == "xymatrix") {
@@ -1665,6 +1720,19 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                os << getToken().asInput();
                        cell->push_back(createInsetMath(t.cs() + os.str(), buf));
                        parse2(cell->back(), FLAG_ITEM, mode, false);
+                       // Delete empty last row if present
+                       InsetMathGrid & subgrid =
+                               *(cell->back().nucleus()->asGridInset());
+                       if (subgrid.nrows() > 1)
+                               delEmptyLastRow(subgrid);
+               }
+
+               else if (t.cs() == "Diagram") {
+                       odocstringstream os;
+                       while (good() && nextToken().cat() != catBegin)
+                               os << getToken().asInput();
+                       cell->push_back(createInsetMath(t.cs() + os.str(), buf));
+                       parse2(cell->back(), FLAG_ITEM, mode, false);
                }
 
                else if (t.cs() == "framebox" || t.cs() == "makebox") {
@@ -1692,10 +1760,9 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                        else {
                                // Since the Length class cannot use length variables
                                // we must not create an InsetMathSpace.
-                               cell->push_back(MathAtom(new MathMacro(buffer_, name)));
+                               cell->push_back(MathAtom(new MathMacro(buf, name)));
                                MathData ar;
-                               mathed_parse_cell(ar, '{' + arg + '}',
-                                               Parse::NORMAL);
+                               mathed_parse_cell(ar, '{' + arg + '}', mode_);
                                cell->append(ar);
                        }
                }
@@ -1763,8 +1830,16 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                }
 
                else if (t.cs().size()) {
-                       bool const is_user_macro =
-                               buffer_ && buffer_->getMacro(t.cs(), false);
+                       bool const no_mhchem =
+                               (t.cs() == "ce" || t.cs() == "cf")
+                               && buf && buf->params().use_mhchem ==
+                                               BufferParams::package_off;
+
+                       bool const is_user_macro = no_mhchem ||
+                               (buf && (mode_ & Parse::TRACKMACRO
+                                        ? buf->usermacros.count(t.cs()) != 0
+                                        : buf->getMacro(t.cs(), false) != 0));
+
                        latexkeys const * l = in_word_set(t.cs());
                        if (l && !is_user_macro) {
                                if (l->inset == "big") {
@@ -1854,7 +1929,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags,
                                }
                                if (!is_unicode_symbol) {
                                        MathAtom at = is_user_macro ?
-                                               MathAtom(new MathMacro(buffer_, t.cs()))
+                                               MathAtom(new MathMacro(buf, t.cs()))
                                                : createInsetMath(t.cs(), buf);
                                        InsetMath::mode_type m = mode;
                                        //if (m == InsetMath::UNDECIDED_MODE)