X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmathed%2FMathExtern.cpp;h=1f9356f8f241bdaf97de18bbf4f619b4e31f6749;hb=c013799887eb5c330f3cff4d51542028683fe1bb;hp=ee7ceebe4f29ffd40bb2bf3a92dc54131d03405f;hpb=8a0134cc8ce7de41d6d6911a1c4219ca958f2b71;p=lyx.git diff --git a/src/mathed/MathExtern.cpp b/src/mathed/MathExtern.cpp index ee7ceebe4f..1f9356f8f2 100644 --- a/src/mathed/MathExtern.cpp +++ b/src/mathed/MathExtern.cpp @@ -35,11 +35,17 @@ #include "MathParser.h" #include "MathStream.h" +#include "Encoding.h" + #include "support/debug.h" #include "support/docstream.h" #include "support/FileName.h" #include "support/filetools.h" +#include "support/gettext.h" #include "support/lstrings.h" +#include "support/TempFile.h" +#include "support/textutils.h" +#include "support/unique_ptr.h" #include #include @@ -54,6 +60,7 @@ namespace lyx { namespace { enum ExternalMath { + HTML, MAPLE, MAXIMA, MATHEMATICA, @@ -68,7 +75,7 @@ static char const * function_names[] = { "det", "dim", "exp", "gcd", "hom", "inf", "ker", "lg", "lim", "liminf", "limsup", "ln", "log", "max", "min", "sec", "sin", "sinh", "sup", - "tan", "tanh", "Pr", 0 + "tan", "tanh", "Pr", nullptr }; static size_t const npos = lyx::docstring::npos; @@ -108,7 +115,7 @@ bool extractScript(MathData & ar, // try to extract an "argument" to some function. // returns position behind the argument MathData::iterator extractArgument(MathData & ar, - MathData::iterator pos, MathData::iterator last, + MathData::iterator pos, MathData::iterator last, ExternalMath kind, bool function = false) { // nothing to get here @@ -120,7 +127,7 @@ MathData::iterator extractArgument(MathData & ar, // leave out delimiters if this is a function argument // unless we are doing MathML, in which case we do want // the delimiters - if (function && kind != MATHML) { + if (function && kind != MATHML && kind != HTML) { MathData const & arg = (*pos)->asDelimInset()->cell(0); MathData::const_iterator cur = arg.begin(); MathData::const_iterator end = arg.end(); @@ -184,7 +191,7 @@ void extractStrings(MathData & ar) if (!ar[i]->asCharInset()) continue; docstring s = charSequence(ar.begin() + i, ar.end()); - ar[i] = MathAtom(new InsetMathString(s)); + ar[i] = MathAtom(new InsetMathString(ar.buffer(), s)); ar.erase(i + 1, i + s.size()); } //lyxerr << "\nStrings to: " << ar << endl; @@ -204,7 +211,7 @@ void extractMatrices(MathData & ar) continue; if (!arr.front()->asGridInset()) continue; - ar[i] = MathAtom(new InsetMathMatrix(*(arr.front()->asGridInset()), + ar[i] = MathAtom(new InsetMathMatrix(*(arr.front()->asGridInset()), inset->left_, inset->right_)); } @@ -285,10 +292,23 @@ bool testString(MathAtom const & at, char const * const str) return testString(at, from_ascii(str)); } + +bool testSymbol(MathAtom const & at, docstring const & name) +{ + return at->asSymbolInset() && at->asSymbolInset()->name() == name; +} + + +bool testSymbol(MathAtom const & at, char const * const name) +{ + return testSymbol(at, from_ascii(name)); +} + + // search end of nested sequence MathData::iterator endNestSearch( MathData::iterator it, - MathData::iterator last, + const MathData::iterator& last, TestItemFunc testOpen, TestItemFunc testClose ) @@ -335,7 +355,7 @@ void replaceNested( // -// split scripts into seperate super- and subscript insets. sub goes in +// split scripts into separate super- and subscript insets. sub goes in // front of super... // @@ -351,7 +371,7 @@ void splitScripts(MathData & ar) continue; // we must have a nucleus if we only have a superscript - if (!script->hasDown() && script->nuc().size() == 0) + if (!script->hasDown() && script->nuc().empty()) continue; if (script->nuc().size() == 1) { @@ -364,7 +384,7 @@ void splitScripts(MathData & ar) // create extra script inset and move superscript over InsetMathScript * p = ar[i].nucleus()->asScriptInset(); - auto_ptr q(new InsetMathScript(buf, true)); + auto q = make_unique(buf, true); swap(q->up(), p->up()); p->removeScript(true); @@ -466,7 +486,7 @@ void extractNumbers(MathData & ar) docstring s = digitSequence(ar.begin() + i, ar.end()); - ar[i] = MathAtom(new InsetMathNumber(s)); + ar[i] = MathAtom(new InsetMathNumber(ar.buffer(), s)); ar.erase(i + 1, i + s.size()); } //lyxerr << "\nNumbers to: " << ar << endl; @@ -493,7 +513,7 @@ bool testCloseParen(MathAtom const & at) MathAtom replaceParenDelims(const MathData & ar) { return MathAtom(new InsetMathDelim(const_cast(ar.buffer()), - from_ascii("("), from_ascii(")"), ar)); + from_ascii("("), from_ascii(")"), ar, true)); } @@ -512,16 +532,56 @@ bool testCloseBracket(MathAtom const & at) MathAtom replaceBracketDelims(const MathData & ar) { return MathAtom(new InsetMathDelim(const_cast(ar.buffer()), - from_ascii("["), from_ascii("]"), ar)); + from_ascii("["), from_ascii("]"), ar, true)); } -// replace '('...')' and '['...']' sequences by a real InsetMathDelim +bool testOpenVert(MathAtom const & at) +{ + return testSymbol(at, "lvert"); +} + + +bool testCloseVert(MathAtom const & at) +{ + return testSymbol(at, "rvert"); +} + + +MathAtom replaceVertDelims(const MathData & ar) +{ + return MathAtom(new InsetMathDelim(const_cast(ar.buffer()), + from_ascii("lvert"), from_ascii("rvert"), ar, true)); +} + + +bool testOpenAngled(MathAtom const & at) +{ + return testSymbol(at, "langle"); +} + + +bool testCloseAngled(MathAtom const & at) +{ + return testSymbol(at, "rangle"); +} + + +MathAtom replaceAngledDelims(const MathData & ar) +{ + return MathAtom(new InsetMathDelim(const_cast(ar.buffer()), + from_ascii("langle"), from_ascii("rangle"), ar, true)); +} + + +// replace '('...')', '['...']', '|'...'|', and '<'...'>' sequences by a real InsetMathDelim void extractDelims(MathData & ar) { //lyxerr << "\nDelims from: " << ar << endl; replaceNested(ar, testOpenParen, testCloseParen, replaceParenDelims); replaceNested(ar, testOpenBracket, testCloseBracket, replaceBracketDelims); + replaceNested(ar, testOpenVert, testCloseVert, replaceVertDelims); + replaceNested(ar, testOpenAngled, testCloseAngled, replaceAngledDelims); //lyxerr << "\nDelims to: " << ar << endl; } @@ -536,6 +596,9 @@ void extractDelims(MathData & ar) // assume 'extractDelims' ran before void extractFunctions(MathData & ar, ExternalMath kind) { + // FIXME From what I can see, this is quite broken right now, for reasons + // I will note below. (RGH) + // we need at least two items... if (ar.size() < 2) return; @@ -550,9 +613,26 @@ void extractFunctions(MathData & ar, ExternalMath kind) docstring name; // is it a function? // it certainly is if it is well known... + + // FIXME This will never give us anything. When we get here, *it will + // never point at a string, but only at a character. I.e., if we are + // working on "sin(x)", then we are seeing: + // [char s mathalpha][char i mathalpha][char n mathalpha][delim ( ) [char x mathalpha]] + // and of course we will not find the function name "sin" in there, but + // rather "n(x)". + // + // It appears that we original ran extractStrings() before we ran + // extractFunctions(), but Andre changed this at f200be55, I think + // because this messed up what he was trying to do with "dx" in the + // context of integrals. + // + // This could be fixed by looking at a charSequence instead of just at + // the various characters, one by one. But I am not sure I understand + // exactly what we are trying to do here. And it involves a lot of + // guessing. if (!extractFunctionName(*it, name)) { // is this a user defined function? - // it it probably not, if it doesn't have a name. + // probably not, if it doesn't have a name. if (!extractString(*it, name)) continue; // it is not if it has no argument @@ -563,19 +643,19 @@ void extractFunctions(MathData & ar, ExternalMath kind) InsetMathDelim const * del = (*jt)->asDelimInset(); if (!del || del->cell(0).size() != 1) continue; - // fall trough into main branch + // fall through into main branch } // do we have an exponent like in // 'sin' '^2' 'x' -> 'sin(x)' '^2' - MathData exp; + MathData exp(buf); extractScript(exp, jt, ar.end(), true); // create a proper inset as replacement - auto_ptr p(new InsetMathExFunc(buf, name)); + auto p = lyx::make_unique(buf, name); // jt points to the "argument". Get hold of this. - MathData::iterator st = + MathData::iterator st = extractArgument(p->cell(0), jt, ar.end(), kind, true); // replace the function name by a real function inset @@ -595,18 +675,6 @@ void extractFunctions(MathData & ar, ExternalMath kind) // search integrals // -bool testSymbol(MathAtom const & at, docstring const & name) -{ - return at->asSymbolInset() && at->asSymbolInset()->name() == name; -} - - -bool testSymbol(MathAtom const & at, char const * const name) -{ - return at->asSymbolInset() && at->asSymbolInset()->name() == from_ascii(name); -} - - bool testIntSymbol(MathAtom const & at) { return testSymbol(at, from_ascii("int")); @@ -618,7 +686,7 @@ bool testIntegral(MathAtom const & at) return testIntSymbol(at) || ( at->asScriptInset() - && at->asScriptInset()->nuc().size() + && !at->asScriptInset()->nuc().empty() && testIntSymbol(at->asScriptInset()->nuc().back()) ); } @@ -656,8 +724,8 @@ void extractIntegrals(MathData & ar, ExternalMath kind) if (!testIntegral(*it)) continue; - // core ist part from behind the scripts to the 'd' - auto_ptr p(new InsetMathExInt(buf, from_ascii("int"))); + // core is part from behind the scripts to the 'd' + auto p = lyx::make_unique(buf, from_ascii("int")); // handle scripts if available if (!testIntSymbol(*it)) { @@ -718,7 +786,7 @@ bool testSum(MathAtom const & at) return testSumSymbol(at) || ( at->asScriptInset() - && at->asScriptInset()->nuc().size() + && !at->asScriptInset()->nuc().empty() && testSumSymbol(at->asScriptInset()->nuc().back()) ); } @@ -742,23 +810,23 @@ void extractSums(MathData & ar) continue; // create a proper inset as replacement - auto_ptr p(new InsetMathExInt(buf, from_ascii("sum"))); + auto p = lyx::make_unique(buf, from_ascii("sum")); // collect lower bound and summation index InsetMathScript const * sub = ar[i]->asScriptInset(); if (sub && sub->hasDown()) { // try to figure out the summation index from the subscript - MathData const & ar = sub->down(); + MathData const & md = sub->down(); MathData::const_iterator xt = - find_if(ar.begin(), ar.end(), &testEqualSign); - if (xt != ar.end()) { + find_if(md.begin(), md.end(), &testEqualSign); + if (xt != md.end()) { // we found a '=', use everything in front of that as index, // and everything behind as lower index - p->cell(1) = MathData(buf, ar.begin(), xt); - p->cell(2) = MathData(buf, xt + 1, ar.end()); + p->cell(1) = MathData(buf, md.begin(), xt); + p->cell(2) = MathData(buf, xt + 1, md.end()); } else { // use everything as summation index, don't use scripts. - p->cell(1) = ar; + p->cell(1) = md; } } @@ -799,7 +867,7 @@ bool testDiffItem(MathAtom const & at) bool testDiffArray(MathData const & ar) { - return ar.size() && testDiffItem(ar.front()); + return !ar.empty() && testDiffItem(ar.front()); } @@ -830,7 +898,7 @@ void extractDiff(MathData & ar) } // create a proper diff inset - auto_ptr diff(new InsetMathDiff(buf)); + auto diff = lyx::make_unique(buf); // collect function, let jt point behind last used item MathData::iterator jt = it + 1; @@ -869,7 +937,11 @@ void extractDiff(MathData & ar) int mult = 1; if (extractNumber(script->up(), mult)) { //lyxerr << "mult: " << mult << endl; - for (int i = 0; i < mult; ++i) + if (mult < 0 || mult > 1000) { + lyxerr << "Cannot differentiate less than 0 or more than 1000 times !" << endl; + continue; + } + for (int ii = 0; ii < mult; ++ii) diff->addDer(MathData(buf, dt + 1, st)); } } else { @@ -928,7 +1000,7 @@ void extractLims(MathData & ar) MathData x0 = MathData(buf, st + 1, s.end()); // use something behind the script as core - MathData f; + MathData f(buf); MathData::iterator tt = extractTerm(f, it + 1, ar.end()); // cleanup @@ -948,21 +1020,22 @@ void extractLims(MathData & ar) void extractStructure(MathData & ar, ExternalMath kind) { //lyxerr << "\nStructure from: " << ar << endl; - if (kind != MATHML) + if (kind != MATHML && kind != HTML) splitScripts(ar); extractDelims(ar); extractIntegrals(ar, kind); - if (kind != MATHML) + if (kind != MATHML && kind != HTML) extractSums(ar); extractNumbers(ar); extractMatrices(ar); - extractFunctions(ar, kind); - extractDets(ar); - extractDiff(ar); - extractExps(ar); - extractLims(ar); - if (kind != MATHML) + if (kind != MATHML && kind != HTML) { + extractFunctions(ar, kind); + extractDets(ar); + extractDiff(ar); + extractExps(ar); + extractLims(ar); extractStrings(ar); + } //lyxerr << "\nStructure to: " << ar << endl; } @@ -973,7 +1046,13 @@ namespace { { // In order to avoid parsing problems with command interpreters // we pass input data through a file - FileName const cas_tmpfile = FileName::tempName("casinput"); + // Since the CAS is supposed to read the temp file we need + // to unlock it on windows (bug 10262). + unique_ptr tempfile(new TempFile("casinput")); + tempfile->setAutoRemove(false); + FileName const cas_tmpfile = tempfile->name(); + tempfile.reset(); + if (cas_tmpfile.empty()) { lyxerr << "Warning: cannot create temporary file." << endl; @@ -988,7 +1067,7 @@ namespace { << "\ninput: '" << data << "'" << endl; cmd_ret const ret = runCommand(command); cas_tmpfile.removeFile(); - return ret.second; + return ret.result; } size_t get_matching_brace(string const & str, size_t i) @@ -1076,9 +1155,9 @@ namespace { vector tmp = getVectorFromString(out, "$$"); if (tmp.size() < 2) - return MathData(); + return MathData(nullptr); - out = subst(tmp[1], "\\>", string()); + out = subst(subst(tmp[1], "\\>", string()), "{\\it ", "\\mathit{"); lyxerr << "output: '" << out << "'" << endl; // Ugly code that tries to make the result prettier @@ -1096,7 +1175,6 @@ namespace { + out.substr(k + 1); //lyxerr << "output: " << out << endl; i = out.find("\\mathchoice", i); - break; } i = out.find("\\over"); @@ -1115,7 +1193,7 @@ namespace { //lyxerr << "output: " << out << endl; i = out.find("\\over", i + 4); } - MathData res; + MathData res(nullptr); mathed_parse_cell(res, from_utf8(out)); return res; } @@ -1174,7 +1252,7 @@ namespace { istringstream is(out); string line; getline(is, line); - if (line.find("on line") != 0) + if (!prefixIs(line, "on line")) break; // error message not identified getline(is, line); size_t pos = line.find('^'); @@ -1193,7 +1271,7 @@ namespace { // change \_ into _ // - MathData res; + MathData res(nullptr); mathed_parse_cell(res, from_utf8(out)); return res; } @@ -1253,7 +1331,7 @@ namespace { // ansi control sequence before, such as '\033[?1034hans = ' size_t i = out.find("ans = "); if (i == string::npos) - return MathData(); + return MathData(nullptr); out = out.substr(i + 6); // parse output as matrix or single number @@ -1338,7 +1416,7 @@ namespace { size_t pos2 = out.find("In[2]:="); if (pos1 == string::npos || pos2 == string::npos) - return MathData(); + return MathData(nullptr); // get everything from pos1+17 to pos2 out = out.substr(pos1 + 17, pos2 - pos1 - 17); @@ -1349,27 +1427,172 @@ namespace { prettifyMathematicaOutput(out, "Muserfunction", true, false); prettifyMathematicaOutput(out, "Mvariable", false, false); - MathData res; + MathData res(nullptr); mathed_parse_cell(res, from_utf8(out)); return res; } -} +} // namespace -} // anon namespace +} // namespace -void write(MathData const & dat, WriteStream & wi) +void write(MathData const & dat, TeXMathStream & wi) { - MathData ar = dat; - extractStrings(ar); wi.firstitem() = true; - for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) { - (*it)->write(wi); + docstring s; + for (MathData::const_iterator it = dat.begin(); it != dat.end(); ++it) { + InsetMathChar const * const c = (*it)->asCharInset(); + if (c) + s += c->getChar(); + else { + if (!s.empty()) { + writeString(s, wi); + s.clear(); + } + (*it)->write(wi); + wi.firstitem() = false; + } + } + if (!s.empty()) { + writeString(s, wi); wi.firstitem() = false; } } +void writeString(docstring const & s, TeXMathStream & os) +{ + if (!os.latex()) { + os << s; + return; + } + + docstring str = s; + if (os.asciiOnly()) + str = escape(s); + + if (os.output() == TeXMathStream::wsSearchAdv) { + os << str; + return; + } + + if (os.lockedMode()) { + bool space; + docstring cmd; + for (char_type c : str) { + try { + Encodings::latexMathChar(c, true, os.encoding(), cmd, space); + os << cmd; + os.pendingSpace(space); + } catch (EncodingException const & e) { + switch (os.output()) { + case TeXMathStream::wsDryrun: { + os << "<" << _("LyX Warning: ") + << _("uncodable character") << " '"; + os << docstring(1, e.failed_char); + os << "'>"; + break; + } + case TeXMathStream::wsPreview: { + // indicate the encoding error by a boxed '?' + os << "{\\fboxsep=1pt\\fbox{?}}"; + LYXERR0("Uncodable character" << " '" + << docstring(1, e.failed_char) + << "'"); + break; + } + case TeXMathStream::wsDefault: + default: + // throw again + throw; + } + } + } + return; + } + + // We may already be inside an \ensuremath command. + bool in_forced_mode = os.pendingBrace(); + + // We will take care of matching braces. + os.pendingBrace(false); + + for (char_type const c : str) { + bool mathmode = in_forced_mode ? os.textMode() : !os.textMode(); + docstring command(1, c); + try { + bool termination = false; + if (isASCII(c) || + Encodings::latexMathChar(c, mathmode, os.encoding(), command, termination)) { + if (os.textMode()) { + if (in_forced_mode) { + // we were inside \lyxmathsym + os << '}'; + os.textMode(false); + in_forced_mode = false; + } + if (!isASCII(c) && os.textMode()) { + os << "\\ensuremath{"; + os.textMode(false); + in_forced_mode = true; + } + } else if (isASCII(c) && in_forced_mode) { + // we were inside \ensuremath + os << '}'; + os.textMode(true); + in_forced_mode = false; + } + } else if (!os.textMode()) { + if (in_forced_mode) { + // we were inside \ensuremath + os << '}'; + in_forced_mode = false; + } else { + os << "\\lyxmathsym{"; + in_forced_mode = true; + } + os.textMode(true); + } + os << command; + // We may need a space if the command contains a macro + // and the last char is ASCII. + if (termination) + os.pendingSpace(true); + } catch (EncodingException const & e) { + switch (os.output()) { + case TeXMathStream::wsDryrun: { + os << "<" << _("LyX Warning: ") + << _("uncodable character") << " '"; + os << docstring(1, e.failed_char); + os << "'>"; + break; + } + case TeXMathStream::wsPreview: { + // indicate the encoding error by a boxed '?' + os << "{\\fboxsep=1pt\\fbox{?}}"; + LYXERR0("Uncodable character" << " '" + << docstring(1, e.failed_char) + << "'"); + break; + } + case TeXMathStream::wsDefault: + default: + // throw again + throw; + } + } + } + + if (in_forced_mode && os.textMode()) { + // We have to care for closing \lyxmathsym + os << '}'; + os.textMode(false); + } else { + os.pendingBrace(in_forced_mode); + } +} + + void normalize(MathData const & ar, NormalStream & os) { for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) @@ -1413,30 +1636,48 @@ void mathematica(MathData const & dat, MathematicaStream & os) } -docstring mathmlize(MathData const & dat, MathStream & os) +void mathmlize(MathData const & dat, MathMLStream & ms) { MathData ar = dat; extractStructure(ar, MATHML); - docstring retval; - if (ar.size() == 0) - os << ""; - else if (ar.size() == 1) - retval = ar.front()->mathmlize(os); - else { - os << MTag("mrow"); + if (ar.empty()) { + if (!ms.inText()) + ms << CTag("mrow"); + } else if (ar.size() == 1) { + ms << ar.front(); + } else { + if (!ms.inText()) + ms << MTag("mrow"); for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) - retval += (*it)->mathmlize(os); - os << ETag("mrow"); + (*it)->mathmlize(ms); + if (!ms.inText()) + ms << ETag("mrow"); } - return retval; } + +void htmlize(MathData const & dat, HtmlStream & os) +{ + MathData ar = dat; + extractStructure(ar, HTML); + if (ar.empty()) + return; + if (ar.size() == 1) { + os << ar.front(); + return; + } + for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) + (*it)->htmlize(os); +} + + // convert this inset somehow to a number bool extractNumber(MathData const & ar, int & i) { idocstringstream is(charSequence(ar.begin(), ar.end())); is >> i; - return is; + // Do not convert is implicitly to bool, since that is forbidden in C++11. + return !is.fail(); } @@ -1444,7 +1685,8 @@ bool extractNumber(MathData const & ar, double & d) { idocstringstream is(charSequence(ar.begin(), ar.end())); is >> d; - return is; + // Do not convert is implicitly to bool, since that is forbidden in C++11. + return !is.fail(); } @@ -1476,12 +1718,12 @@ MathData pipeThroughExtern(string const & lang, docstring const & extra, FileName const file = libFileSearch("mathed", "extern_" + lang); if (file.empty()) { lyxerr << "converter to '" << lang << "' not found" << endl; - return MathData(); + return MathData(nullptr); } // run external sript - string out = captureOutput(file.absFilename(), data); - MathData res; + string out = captureOutput(file.absFileName(), data); + MathData res(nullptr); mathed_parse_cell(res, from_utf8(out)); return res; }