namespace lyx {
+namespace {
+
+void output_arguments(ostream &, Parser &, bool, bool, bool, Context &,
+ Layout::LaTeXArgMap const &);
+
+}
+
+
void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
Context const & context, InsetLayout const * layout)
{
newcontext.layout = &context.textclass.plainLayout();
else
newcontext.font = context.font;
+ if (layout)
+ output_arguments(os, p, outer, false, false, newcontext,
+ layout->latexargs());
parse_text(p, os, flags, outer, newcontext);
+ if (layout)
+ output_arguments(os, p, outer, false, true, newcontext,
+ layout->postcommandargs());
newcontext.check_end_layout(os);
}
/// Known special characters which need skip_spaces_braces() afterwards
char const * const known_special_chars[] = {"ldots",
"lyxarrow", "textcompwordmark",
-"slash", "textasciitilde", "textasciicircum", "textbackslash", 0};
+"slash", "textasciitilde", "textasciicircum", "textbackslash",
+"LyX", "TeX", "LaTeXe",
+"LaTeX", 0};
+
+/// special characters from known_special_chars which may have a \\protect before
+char const * const known_special_protect_chars[] = {"LyX", "TeX",
+"LaTeXe", "LaTeX", 0};
/// the same as known_special_chars with .lyx names
char const * const known_coded_special_chars[] = {"\\SpecialChar \\ldots{}\n",
"\\SpecialChar \\menuseparator\n", "\\SpecialChar \\textcompwordmark{}\n",
-"\\SpecialChar \\slash{}\n", "~", "^", "\n\\backslash\n", 0};
+"\\SpecialChar \\slash{}\n", "~", "^", "\n\\backslash\n",
+"\\SpecialChar \\LyX\n", "\\SpecialChar \\TeX\n", "\\SpecialChar \\LaTeXe\n",
+"\\SpecialChar \\LaTeX\n", 0};
/*!
* Graphics file extensions known by the dvips driver of the graphics package.
"hfill{}", "dotfill{}", "hrulefill{}", "leftarrowfill{}", "rightarrowfill{}",
"upbracefill{}", "downbracefill{}", 0};
-/// These are translated by LyX to commands like "\\LyX{}", so we have to put
-/// them in ERT. "LaTeXe" must come before "LaTeX"!
-char const * const known_phrases[] = {"LyX", "TeX", "LaTeXe", "LaTeX", 0};
-char const * const known_coded_phrases[] = {"LyX", "TeX", "LaTeX2e", "LaTeX", 0};
-int const known_phrase_lengths[] = {3, 5, 7, 0};
-
/// known TIPA combining diacritical marks
char const * const known_tipa_marks[] = {"textsubwedge", "textsubumlaut",
"textsubtilde", "textseagull", "textsubbridge", "textinvsubbridge",
}
+void output_arguments(ostream & os, Parser & p, bool outer, bool need_layout, bool post,
+ Context & context, Layout::LaTeXArgMap const & latexargs)
+{
+ if (need_layout) {
+ context.check_layout(os);
+ need_layout = false;
+ } else
+ need_layout = true;
+ int i = 0;
+ Layout::LaTeXArgMap::const_iterator lait = latexargs.begin();
+ Layout::LaTeXArgMap::const_iterator const laend = latexargs.end();
+ for (; lait != laend; ++lait) {
+ ++i;
+ eat_whitespace(p, os, context, false);
+ if (lait->second.mandatory) {
+ if (p.next_token().cat() != catBegin)
+ break;
+ p.get_token(); // eat '{'
+ if (need_layout) {
+ context.check_layout(os);
+ need_layout = false;
+ }
+ begin_inset(os, "Argument ");
+ if (post)
+ os << "post:";
+ os << i << "\nstatus collapsed\n\n";
+ parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
+ end_inset(os);
+ } else {
+ if (p.next_token().cat() == catEscape ||
+ p.next_token().character() != '[')
+ continue;
+ p.get_token(); // eat '['
+ if (need_layout) {
+ context.check_layout(os);
+ need_layout = false;
+ }
+ begin_inset(os, "Argument ");
+ if (post)
+ os << "post:";
+ os << i << "\nstatus collapsed\n\n";
+ parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
+ end_inset(os);
+ }
+ eat_whitespace(p, os, context, false);
+ }
+}
+
+
void output_command_layout(ostream & os, Parser & p, bool outer,
Context & parent_context,
Layout const * newlayout)
context.need_end_deeper = true;
}
context.check_deeper(os);
- context.check_layout(os);
- // FIXME: Adjust to format 446!
- // Since format 446, layouts do not require anymore all optional
- // arguments before the required ones. Needs to be implemented!
- int optargs = 0;
- while (optargs < context.layout->optArgs()) {
- eat_whitespace(p, os, context, false);
- if (p.next_token().cat() == catEscape ||
- p.next_token().character() != '[')
- break;
- p.get_token(); // eat '['
- // FIXME: Just a workaround. InsetArgument::updateBuffer
- // will compute a proper ID for all "999" Arguments
- // (which is also what lyx2lyx produces).
- // However, tex2lyx should be able to output proper IDs
- // itself.
- begin_inset(os, "Argument 999\n");
- os << "status collapsed\n\n";
- parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
- end_inset(os);
- eat_whitespace(p, os, context, false);
- ++optargs;
- }
- int reqargs = 0;
- while (reqargs < context.layout->requiredArgs()) {
- eat_whitespace(p, os, context, false);
- if (p.next_token().cat() != catBegin)
- break;
- p.get_token(); // eat '{'
- // FIXME: Just a workaround. InsetArgument::updateBuffer
- // will compute a proper ID for all "999" Arguments
- // (which is also what lyx2lyx produces).
- // However, tex2lyx should be able to output proper IDs
- // itself.
- begin_inset(os, "Argument 999\n");
- os << "status collapsed\n\n";
- parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
- end_inset(os);
- eat_whitespace(p, os, context, false);
- ++reqargs;
- }
+ output_arguments(os, p, outer, true, false, context,
+ context.layout->latexargs());
parse_text(p, os, FLAG_ITEM, outer, context);
+ output_arguments(os, p, outer, false, true, context,
+ context.layout->postcommandargs());
context.check_end_layout(os);
if (parent_context.deeper_paragraph) {
// We must suppress the "end deeper" because we
}
context.check_deeper(os);
// handle known optional and required arguments
- // FIXME: Since format 446, layouts do not require anymore all optional
- // arguments before the required ones. Needs to be implemented!
// Unfortunately LyX can't handle arguments of list arguments (bug 7468):
// It is impossible to place anything after the environment name,
// but before the first \\item.
- if (context.layout->latextype == LATEX_ENVIRONMENT) {
- bool need_layout = true;
- int optargs = 0;
- while (optargs < context.layout->optArgs()) {
- eat_whitespace(p, os, context, false);
- if (p.next_token().cat() == catEscape ||
- p.next_token().character() != '[')
- break;
- p.get_token(); // eat '['
- if (need_layout) {
- context.check_layout(os);
- need_layout = false;
- }
- // FIXME: Just a workaround. InsetArgument::updateBuffer
- // will compute a proper ID for all "999" Arguments
- // (which is also what lyx2lyx produces).
- // However, tex2lyx should be able to output proper IDs
- // itself.
- begin_inset(os, "Argument 999\n");
- os << "status collapsed\n\n";
- parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
- end_inset(os);
- eat_whitespace(p, os, context, false);
- ++optargs;
- }
- int reqargs = 0;
- while (reqargs < context.layout->requiredArgs()) {
- eat_whitespace(p, os, context, false);
- if (p.next_token().cat() != catBegin)
- break;
- p.get_token(); // eat '{'
- if (need_layout) {
- context.check_layout(os);
- need_layout = false;
- }
- // FIXME: Just a workaround. InsetArgument::updateBuffer
- // will compute a proper ID for all "999" Arguments
- // (which is also what lyx2lyx produces).
- // However, tex2lyx should be able to output proper IDs
- // itself.
- begin_inset(os, "Argument 999\n");
- os << "status collapsed\n\n";
- parse_text_in_inset(p, os, FLAG_BRACE_LAST, outer, context);
- end_inset(os);
- eat_whitespace(p, os, context, false);
- ++reqargs;
- }
- }
+ if (context.layout->latextype == LATEX_ENVIRONMENT)
+ output_arguments(os, p, outer, false, false, context,
+ context.layout->latexargs());
parse_text(p, os, FLAG_END, outer, context);
+ if (context.layout->latextype == LATEX_ENVIRONMENT)
+ output_arguments(os, p, outer, false, true, context,
+ context.layout->postcommandargs());
context.check_end_layout(os);
if (parent_context.deeper_paragraph) {
// We must suppress the "end deeper" because we
<< "status collapsed\n";
if (newinsetlayout->isPassThru()) {
string const arg = p.verbatimEnvironment(name);
- Context context(true, parent_context.textclass,
+ Context context(true, parent_context.textclass,
&parent_context.textclass.plainLayout(),
parent_context.layout);
output_ert(os, arg, parent_context);
bool const use_natbib = isProvided("natbib");
bool const use_jurabib = isProvided("jurabib");
string last_env;
- while (p.good()) {
- Token const & t = p.get_token();
// it is impossible to determine the correct encoding for non-CJK Japanese.
// Therefore write a note at the beginning of the document
is_nonCJKJapanese = false;
}
+ while (p.good()) {
+ Token const & t = p.get_token();
#ifdef FILEDEBUG
debugToken(cerr, t, flags);
#endif
//
// cat codes
//
+ bool const starred = p.next_token().asInput() == "*";
+ string const starredname(starred ? (t.cs() + '*') : t.cs());
if (t.cat() == catMath) {
// we are inside some text mode thingy, so opening new math is allowed
context.check_layout(os);
else if (t.cat() == catLetter) {
context.check_layout(os);
- // Workaround for bug 4752.
- // FIXME: This whole code block needs to be removed
- // when the bug is fixed and tex2lyx produces
- // the updated file format.
- // The replacement algorithm in LyX is so stupid that
- // it even translates a phrase if it is part of a word.
- bool handled = false;
- for (int const * l = known_phrase_lengths; *l; ++l) {
- string phrase = t.cs();
- for (int i = 1; i < *l && p.next_token().isAlnumASCII(); ++i)
- phrase += p.get_token().cs();
- if (is_known(phrase, known_coded_phrases)) {
- output_ert_inset(os, phrase, context);
- handled = true;
- break;
- } else {
- for (size_t i = 1; i < phrase.length(); ++i)
- p.putback();
- }
- }
- if (!handled)
- os << t.cs();
+ os << t.cs();
}
else if (t.cat() == catOther ||
t.cat() == catAlign ||
t.cat() == catParameter) {
- // This translates "&" to "\\&" which may be wrong...
context.check_layout(os);
- os << t.cs();
+ if (t.asInput() == "-" && p.next_token().asInput() == "-" &&
+ context.merging_hyphens_allowed &&
+ context.font.family != "ttfamily" &&
+ !context.layout->pass_thru) {
+ if (p.next_next_token().asInput() == "-") {
+ // --- is emdash
+ os << to_utf8(docstring(1, 0x2014));
+ p.get_token();
+ } else
+ // -- is endash
+ os << to_utf8(docstring(1, 0x2013));
+ p.get_token();
+ } else
+ // This translates "&" to "\\&" which may be wrong...
+ os << t.cs();
}
else if (p.isParagraph()) {
// {}
Token const prev = p.prev_token();
p.get_token();
- if (p.next_token().character() == '`' ||
- (prev.character() == '-' &&
- p.next_token().character() == '-'))
- ; // ignore it in {}`` or -{}-
+ if (p.next_token().character() == '`')
+ ; // ignore it in {}``
else
output_ert_inset(os, "{}", context);
} else if (next.cat() == catEscape &&
preamble.registerAutomaticallyLoadedPackage(*it);
}
- else if (t.cs() == "caption") {
- bool starred = false;
- if (p.next_token().asInput() == "*") {
- p.get_token();
- starred = true;
- }
- p.skip_spaces();
- context.check_layout(os);
- p.skip_spaces();
- if (starred)
- begin_inset(os, "Caption LongTableNoNumber\n");
- else
- begin_inset(os, "Caption Standard\n");
- Context newcontext(true, context.textclass, 0, 0, context.font);
- newcontext.check_layout(os);
- // FIXME InsetArgument is now properly implemented in InsetLayout
- // (for captions, but also for others)
- if (p.next_token().cat() != catEscape &&
- p.next_token().character() == '[') {
- p.get_token(); // eat '['
- begin_inset(os, "Argument 1\n");
- os << "status collapsed\n";
- parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
- end_inset(os);
- eat_whitespace(p, os, context, false);
- }
- parse_text(p, os, FLAG_ITEM, outer, context);
- context.check_end_layout(os);
- // We don't need really a new paragraph, but
- // we must make sure that the next item gets a \begin_layout.
- context.new_paragraph(os);
- end_inset(os);
- p.skip_spaces();
- newcontext.check_end_layout(os);
- }
-
else if (t.cs() == "subfloat") {
- // the syntax is \subfloat[caption]{content}
+ // the syntax is \subfloat[list entry][sub caption]{content}
// if it is a table of figure depends on the surrounding float
+ // FIXME: second optional argument is not parsed
bool has_caption = false;
p.skip_spaces();
// do nothing if there is no outer float
else if (t.cs() == "textipa") {
context.check_layout(os);
begin_inset(os, "IPA\n");
+ bool merging_hyphens_allowed = context.merging_hyphens_allowed;
+ context.merging_hyphens_allowed = false;
parse_text_in_inset(p, os, FLAG_ITEM, outer, context);
+ context.merging_hyphens_allowed = merging_hyphens_allowed;
end_inset(os);
preamble.registerAutomaticallyLoadedPackage("tipa");
preamble.registerAutomaticallyLoadedPackage("tipx");
end_inset(os);
}
- else if (is_known(t.cs(), known_phrases) ||
- (t.cs() == "protect" &&
- p.next_token().cat() == catEscape &&
- is_known(p.next_token().cs(), known_phrases))) {
- // LyX sometimes puts a \protect in front, so we have to ignore it
- // FIXME: This needs to be changed when bug 4752 is fixed.
- where = is_known(
- t.cs() == "protect" ? p.get_token().cs() : t.cs(),
- known_phrases);
- context.check_layout(os);
- os << known_coded_phrases[where - known_phrases];
- skip_spaces_braces(p);
- }
-
// handle refstyle first to catch \eqref which can also occur
// without refstyle. Only recognize these commands if
// refstyle.sty was found in the preamble (otherwise \eqref
context.check_layout(os);
begin_inset(os, "script ");
os << t.cs().substr(4) << '\n';
- parse_text_in_inset(p, os, FLAG_ITEM, false, context);
+ newinsetlayout = findInsetLayout(context.textclass, t.cs(), true);
+ parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
end_inset(os);
if (t.cs() == "textsubscript")
preamble.registerAutomaticallyLoadedPackage("subscript");
context.font.language, lang);
}
- else if (prefixIs(t.cs(), "text")
+ else if (prefixIs(t.cs(), "text") && preamble.usePolyglossia()
&& is_known(t.cs().substr(4), preamble.polyglossia_languages)) {
// scheme is \textLANGUAGE{text} where LANGUAGE is in polyglossia_languages[]
string lang;
p.setEncoding(enc, Encoding::inputenc);
}
- else if ((where = is_known(t.cs(), known_special_chars))) {
+ else if (is_known(t.cs(), known_special_chars) ||
+ (t.cs() == "protect" &&
+ p.next_token().cat() == catEscape &&
+ is_known(p.next_token().cs(), known_special_protect_chars))) {
+ // LyX sometimes puts a \protect in front, so we have to ignore it
+ where = is_known(
+ t.cs() == "protect" ? p.get_token().cs() : t.cs(),
+ known_special_chars);
context.check_layout(os);
os << known_coded_special_chars[where - known_special_chars];
skip_spaces_braces(p);
}
else if ((t.cs() == "nobreakdash" && p.next_token().asInput() == "-") ||
+ (t.cs() == "protect" && p.next_token().asInput() == "\\nobreakdash" &&
+ p.next_next_token().asInput() == "-") ||
(t.cs() == "@" && p.next_token().asInput() == ".")) {
+ // LyX sometimes puts a \protect in front, so we have to ignore it
+ if (t.cs() == "protect")
+ p.get_token();
context.check_layout(os);
os << "\\SpecialChar \\" << t.cs()
<< p.get_token().asInput() << '\n';
string delim = p.get_token().asInput();
Parser::Arg arg = p.verbatimStuff(delim);
if (arg.first)
- output_ert_inset(os, "\\verb" + delim
+ output_ert_inset(os, "\\verb" + delim
+ arg.second + delim, context);
else
cerr << "invalid \\verb command. Skipping" << endl;
}
else if (t.cs() == "hspace" || t.cs() == "vspace") {
- bool starred = false;
- if (p.next_token().asInput() == "*") {
+ if (starred)
p.get_token();
- starred = true;
- }
string name = t.asInput();
string const length = p.verbatim_item();
string unit;
}
}
- if (t.cs()[0] == 'h' && (known_unit || known_hspace)) {
+ // check for glue lengths
+ bool is_gluelength = false;
+ string gluelength = length;
+ string::size_type i = length.find(" minus");
+ if (i == string::npos) {
+ i = length.find(" plus");
+ if (i != string::npos)
+ is_gluelength = true;
+ } else
+ is_gluelength = true;
+ // if yes transform "9xx minus 8yy plus 7zz"
+ // to "9xx-8yy+7zz"
+ if (is_gluelength) {
+ i = gluelength.find(" minus");
+ if (i != string::npos)
+ gluelength.replace(i, 7, "-");
+ i = gluelength.find(" plus");
+ if (i != string::npos)
+ gluelength.replace(i, 6, "+");
+ }
+
+ if (t.cs()[0] == 'h' && (known_unit || known_hspace || is_gluelength)) {
// Literal horizontal length or known variable
context.check_layout(os);
begin_inset(os, "space ");
os << unit;
os << "}";
if (known_unit && !known_hspace)
- os << "\n\\length "
- << translate_len(length);
+ os << "\n\\length " << translate_len(length);
+ if (is_gluelength)
+ os << "\n\\length " << gluelength;
end_inset(os);
- } else if (known_unit || known_vspace) {
+ } else if (known_unit || known_vspace || is_gluelength) {
// Literal vertical length or known variable
context.check_layout(os);
begin_inset(os, "VSpace ");
os << unit;
if (known_unit && !known_vspace)
os << translate_len(length);
+ if (is_gluelength)
+ os << gluelength;
if (starred)
os << '*';
end_inset(os);
}
// The single '=' is meant here.
- else if ((newinsetlayout = findInsetLayout(context.textclass, t.cs(), true))) {
+ else if ((newinsetlayout = findInsetLayout(context.textclass, starredname, true))) {
+ if (starred)
+ p.get_token();
p.skip_spaces();
context.check_layout(os);
- begin_inset(os, "Flex ");
- os << to_utf8(newinsetlayout->name()) << '\n'
- << "status collapsed\n";
+ docstring const name = newinsetlayout->name();
+ bool const caption = name.find(from_ascii("Caption:")) == 0;
+ if (caption) {
+ begin_inset(os, "Caption ");
+ os << to_utf8(name.substr(8)) << '\n';
+ } else {
+ begin_inset(os, "Flex ");
+ os << to_utf8(name) << '\n'
+ << "status collapsed\n";
+ }
if (newinsetlayout->isPassThru()) {
// set catcodes to verbatim early, just in case.
p.setCatcodes(VERBATIM_CATCODES);
newcontext.layout = &context.textclass.plainLayout();
output_ert(os, arg, newcontext);
} else
-
parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
+ if (caption)
+ p.skip_spaces();
end_inset(os);
}
// Only use text mode commands, since we are in text mode here,
// and math commands may be invalid (bug 6797)
string name = t.asInput();
- // handle the dingbats and Cyrillic
- if (name == "\\ding" || name == "\\textcyr")
+ // handle the dingbats, cyrillic and greek
+ if (name == "\\ding" || name == "\\textcyr" ||
+ (name == "\\textgreek" && !preamble.usePolyglossia()))
name = name + '{' + p.getArg('{', '}') + '}';
// handle the ifsym characters
else if (name == "\\textifsymbol") {