#include "InsetMathArray.h"
#include "InsetMathBig.h"
#include "InsetMathBrace.h"
+#include "InsetMathCancelto.h"
#include "InsetMathChar.h"
#include "InsetMathColor.h"
#include "InsetMathComment.h"
#include "InsetMathRef.h"
#include "InsetMathRoot.h"
#include "InsetMathScript.h"
+#include "InsetMathSideset.h"
#include "InsetMathSpace.h"
#include "InsetMathSplit.h"
#include "InsetMathSqrt.h"
+#include "InsetMathStackrel.h"
+#include "InsetMathString.h"
#include "InsetMathTabular.h"
#include "MathMacroTemplate.h"
#include "MathFactory.h"
#include "MathSupport.h"
#include "Buffer.h"
+#include "BufferParams.h"
#include "Encoding.h"
#include "Lexer.h"
/*!
- * 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|}
* \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)
{
}
+/*!
+ * 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 == "smallmatrix" || name.substr(1) == "matrix";
+}
+
+
// These are TeX's catcodes
enum CatCode {
catEscape, // 0 backslash
///
char_type character() const { return char_; }
///
- docstring asString() const { return cs_.size() ? cs_ : docstring(1, char_); }
+ docstring asString() const { return !cs_.empty() ? cs_ : docstring(1, char_); }
///
- docstring asInput() const { return cs_.size() ? '\\' + cs_ : docstring(1, char_); }
+ docstring asInput() const { return !cs_.empty() ? '\\' + cs_ : docstring(1, char_); }
private:
///
ostream & operator<<(ostream & os, Token const & t)
{
- if (t.cs().size()) {
+ if (!t.cs().empty()) {
docstring const & cs = t.cs();
// FIXME: For some strange reason, the stream operator instanciate
// a new Token before outputting the contents of t.cs().
{
if (!good()) {
error("The input stream is not well...");
- putback();
return 0;
}
return tokens_[pos_++].character();
docstring Parser::getArg(char_type left, char_type right)
{
+ docstring result;
skipSpaces();
- docstring result;
+ if (!good())
+ return result;
+
char_type c = getChar();
if (c != left)
if (!is) {
error("unexpected end of input");
} else {
+ if (c == '\n')
+ c = ' ';
docstring s(1, c);
if (catcode(c) == catLetter) {
// collect letters
Token const & n = getToken();
if (n.cat() == catMath) {
// TeX's $$...$$ syntax for displayed math
- cell->push_back(MathAtom(new InsetMathHull(buf, 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();
}
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);
+ }
}
}
}
else if (t.cat() == catActive)
- cell->push_back(MathAtom(new InsetMathChar(t.character())));
+ cell->push_back(MathAtom(new InsetMathSpace(string(1, t.character()), "")));
else if (t.cat() == catBegin) {
MathData ar;
bool up = (t.cat() == catSuper);
// 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())
+ if (cell->empty())
cell->push_back(MathAtom(new InsetMathScript(buf, up)));
else if (cell->back()->asScriptInset() &&
!cell->back()->asScriptInset()->has(up))
// 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);
Token const & t = getToken();
if (t.cat() == catNewline)
break;
- s += t.asString();
+ s += t.asInput();
}
cell->push_back(MathAtom(new InsetMathComment(buf, s)));
skipSpaces();
//
else if (t.cs() == "lyxlock") {
- if (cell->size())
+ if (!cell->empty())
cell->back().nucleus()->lock(true);
}
}
else if (t.cs() == "(") {
- if (mode == InsetMath::MATH_MODE) {
- error("bad math environment");
- break;
- }
- cell->push_back(MathAtom(new InsetMathHull(buf, 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() == "[") {
// 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_;
}
}
#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())
else if (t.cs() == "sqrt") {
MathData ar;
parse(ar, FLAG_OPTION, mode);
- if (ar.size()) {
+ if (!ar.empty()) {
cell->push_back(MathAtom(new InsetMathRoot(buf)));
cell->back().nucleus()->cell(0) = ar;
parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
}
}
+ else if (t.cs() == "cancelto") {
+ MathData ar;
+ parse(ar, FLAG_ITEM, mode);
+ cell->push_back(MathAtom(new InsetMathCancelto(buf)));
+ cell->back().nucleus()->cell(1) = ar;
+ parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
+ }
+
else if (t.cs() == "unit") {
// Allowed formats \unit[val]{unit}
MathData ar;
parse(ar, FLAG_OPTION, mode);
- if (ar.size()) {
+ if (!ar.empty()) {
cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNIT)));
cell->back().nucleus()->cell(0) = ar;
parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
// Here allowed formats are \unitfrac[val]{num}{denom}
MathData ar;
parse(ar, FLAG_OPTION, mode);
- if (ar.size()) {
+ if (!ar.empty()) {
cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNITFRAC, 3)));
cell->back().nucleus()->cell(2) = ar;
} else {
parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
}
+ else if (t.cs() == "sideset") {
+ // Here allowed formats are \sideset{_{bl}^{tl}}{_{br}^{tr}}{operator}
+ MathData ar[2];
+ InsetMathScript * script[2] = {0, 0};
+ for (int i = 0; i < 2; ++i) {
+ parse(ar[i], FLAG_ITEM, mode);
+ if (ar[i].size() == 1)
+ script[i] = ar[i][0].nucleus()->asScriptInset();
+ }
+ bool const hasscript[2] = {script[0] ? true : false, script[1] ? true : false};
+ cell->push_back(MathAtom(new InsetMathSideset(buf, hasscript[0], hasscript[1])));
+ if (hasscript[0]) {
+ if (script[0]->hasDown())
+ cell->back().nucleus()->cell(1) = script[0]->down();
+ if (script[0]->hasUp())
+ cell->back().nucleus()->cell(2) = script[0]->up();
+ } else
+ cell->back().nucleus()->cell(1) = ar[0];
+ if (hasscript[1]) {
+ if (script[1]->hasDown())
+ cell->back().nucleus()->cell(2 + hasscript[0]) = script[1]->down();
+ if (script[1]->hasUp())
+ cell->back().nucleus()->cell(3 + hasscript[0]) = script[1]->up();
+ } else
+ cell->back().nucleus()->cell(2 + hasscript[0]) = ar[1];
+ parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
+ }
+
+ else if (t.cs() == "stackrel") {
+ // Here allowed formats are \stackrel[subscript]{superscript}{operator}
+ MathData ar;
+ parse(ar, FLAG_OPTION, mode);
+ cell->push_back(MathAtom(new InsetMathStackrel(buf, !ar.empty())));
+ if (!ar.empty())
+ cell->back().nucleus()->cell(2) = ar;
+ parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
+ parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
+ }
+
else if (t.cs() == "xrightarrow" || t.cs() == "xleftarrow") {
cell->push_back(createInsetMath(t.cs(), buf));
parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
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(buf, t.cs())));
- parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
- parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
+ 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") {
}
else if (name == "math") {
- if (mode == InsetMath::MATH_MODE) {
- error("bad math environment");
- break;
- }
- cell->push_back(MathAtom(new InsetMathHull(buf, 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*"
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") {
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") {
parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE);
}
- else if (t.cs() == "hspace" && nextToken().character() != '*') {
+ else if (t.cs() == "hspace") {
+ bool const prot = nextToken().character() == '*';
+ if (prot)
+ getToken();
docstring const name = t.cs();
docstring const arg = parse_verbatim_item();
Length length;
- if (isValidLength(to_utf8(arg), &length))
- cell->push_back(MathAtom(new InsetMathSpace(length)));
+ if (prot && arg == "\\fill")
+ cell->push_back(MathAtom(new InsetMathSpace("hspace*{\\fill}", "")));
+ else if (isValidLength(to_utf8(arg), &length))
+ cell->push_back(MathAtom(new InsetMathSpace(length, prot)));
else {
// Since the Length class cannot use length variables
// we must not create an InsetMathSpace.
error("'}' expected in \\" + t.cs());
return success_;
}
+ bool termination;
docstring rem;
do {
- cmd = Encodings::fromLaTeXCommand(cmd, rem);
+ cmd = Encodings::fromLaTeXCommand(cmd,
+ Encodings::MATH_CMD | Encodings::TEXT_CMD,
+ termination, rem);
for (size_t i = 0; i < cmd.size(); ++i)
cell->push_back(MathAtom(new InsetMathChar(cmd[i])));
- if (rem.size()) {
+ if (!rem.empty()) {
char_type c = rem[0];
cell->push_back(MathAtom(new InsetMathChar(c)));
cmd = rem.substr(1);
rem.clear();
} else
cmd.clear();
- } while (cmd.size());
+ } while (!cmd.empty());
}
- else if (t.cs().size()) {
- bool const is_user_macro =
- buf && (mode_ & Parse::TRACKMACRO
- ? buf->usermacros.count(t.cs()) != 0
- : buf->getMacro(t.cs(), false) != 0);
+ else if (!t.cs().empty()) {
+ bool const no_mhchem =
+ (t.cs() == "ce" || t.cs() == "cf")
+ && buf && buf->params().use_package("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") {
}
}
bool is_combining;
- char_type c =
- Encodings::fromLaTeXCommand(cmd, is_combining);
+ bool termination;
+ char_type c = Encodings::fromLaTeXCommand(cmd,
+ Encodings::MATH_CMD | Encodings::TEXT_CMD,
+ is_combining, termination);
if (is_combining) {
if (cat == catLetter)
cmd += '{';
++num_tokens;
if (cat == catLetter)
cmd += '}';
- c = Encodings::fromLaTeXCommand(cmd, is_combining);
+ c = Encodings::fromLaTeXCommand(cmd,
+ Encodings::MATH_CMD | Encodings::TEXT_CMD,
+ is_combining, termination);
}
if (c) {
+ if (termination) {
+ if (nextToken().cat() == catBegin) {
+ getToken();
+ if (nextToken().cat() == catEnd) {
+ getToken();
+ num_tokens += 2;
+ } else
+ putback();
+ } else {
+ while (nextToken().cat() == catSpace) {
+ getToken();
+ ++num_tokens;
+ }
+ }
+ }
is_unicode_symbol = true;
cell->push_back(MathAtom(new InsetMathChar(c)));
} else {
// this fails on \bigg[...\bigg]
//MathData opt;
//parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE);
- //if (opt.size()) {
+ //if (!opt.empty()) {
// start = 1;
// at.nucleus()->cell(0) = opt;
//}