]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
The roundtrip of the math manual produces a compilable document now:
[lyx.git] / src / tex2lyx / text.cpp
index 637a60919789c49f7974cf8f9fdeb3bc2f1f3836..7f633f4a04623915658616a44d54907a5e1a6dc8 100644 (file)
@@ -41,10 +41,15 @@ namespace lyx {
 
 
 void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
-               Context const & context)
+               Context const & context, InsetLayout const * layout)
 {
+       bool const forcePlainLayout =
+               layout ? layout->forcePlainLayout() : false;
        Context newcontext(true, context.textclass);
-       newcontext.font = context.font;
+       if (forcePlainLayout)
+               newcontext.layout = &context.textclass.plainLayout();
+       else
+               newcontext.font = context.font;
        parse_text(p, os, flags, outer, newcontext);
        newcontext.check_end_layout(os);
 }
@@ -52,6 +57,17 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
 
 namespace {
 
+void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
+               Context const & context, string const & name)
+{
+       InsetLayout const * layout = 0;
+       DocumentClass::InsetLayouts::const_iterator it =
+               context.textclass.insetLayouts().find(from_ascii(name));
+       if (it != context.textclass.insetLayouts().end())
+               layout = &(it->second);
+       parse_text_in_inset(p, os, flags, outer, context, layout);
+}
+
 /// parses a paragraph snippet, useful for example for \\emph{...}
 void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
                Context & context)
@@ -130,8 +146,8 @@ char const * const known_coded_quotes[] = { "prd", "ard", "ard", "ard",
 char const * const known_sizes[] = { "tiny", "scriptsize", "footnotesize",
 "small", "normalsize", "large", "Large", "LARGE", "huge", "Huge", 0};
 
-/// the same as known_sizes with .lyx names plus a default entry
-char const * const known_coded_sizes[] = { "default", "tiny", "scriptsize", "footnotesize",
+/// the same as known_sizes with .lyx names
+char const * const known_coded_sizes[] = { "tiny", "scriptsize", "footnotesize",
 "small", "normal", "large", "larger", "largest", "huge", "giant", 0};
 
 /// LaTeX 2.09 names for font families
@@ -201,6 +217,12 @@ char const * const known_coded_spaces[] = { "space{}", "space{}",
 "negthinspace{}", "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};
+
 
 /// splits "x=z, y=b" into a map and an ordered keyword vector
 void split_map(string const & s, map<string, string> & res, vector<string> & keys)
@@ -441,6 +463,19 @@ Layout const * findLayout(TextClass const & textclass, string const & name)
 }
 
 
+InsetLayout const * findInsetLayout(TextClass const & textclass, string const & name, bool command)
+{
+       DocumentClass::InsetLayouts::const_iterator it = textclass.insetLayouts().begin();
+       DocumentClass::InsetLayouts::const_iterator en = textclass.insetLayouts().end();
+       for (; it != en; ++it)
+               if (it->second.latexname() == name &&
+                   ((command && it->second.latextype() == InsetLayout::COMMAND) ||
+                    (!command && it->second.latextype() == InsetLayout::ENVIRONMENT)))
+                       return &(it->second);
+       return 0;
+}
+
+
 void eat_whitespace(Parser &, ostream &, Context &, bool);
 
 
@@ -449,7 +484,7 @@ void eat_whitespace(Parser &, ostream &, Context &, bool);
  * This should be called after a command has been parsed that is not put into
  * ERT, and where LyX adds "{}" if needed.
  */
-void skip_spaces_braces(Parser & p)
+void skip_spaces_braces(Parser & p, bool keepws = false)
 {
        /* The following four examples produce the same typeset output and
           should be handled by this function:
@@ -465,7 +500,7 @@ void skip_spaces_braces(Parser & p)
        // results in different output in some cases.
        bool const skipped_spaces = p.skip_spaces(true);
        bool const skipped_braces = skip_braces(p);
-       if (skipped_spaces && !skipped_braces)
+       if (keepws && skipped_spaces && !skipped_braces)
                // put back the space (it is better handled by check_space)
                p.unskip_spaces(true);
 }
@@ -475,6 +510,15 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                           Context & parent_context,
                           Layout const * newlayout)
 {
+       TeXFont const oldFont = parent_context.font;
+       // save the current font size
+       string const size = oldFont.size;
+       // reset the font size to default, because the font size switches
+       // don't affect section headings and the like
+       parent_context.font.size = Context::normalfont.size;
+       // we only need to write the font change if we have an open layout
+       if (!parent_context.atParagraphStart())
+               output_font_change(os, oldFont, parent_context.font);
        parent_context.check_end_layout(os);
        Context context(true, parent_context.textclass, newlayout,
                        parent_context.layout, parent_context.font);
@@ -489,7 +533,8 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
        unsigned int optargs = 0;
        while (optargs < context.layout->optargs) {
                eat_whitespace(p, os, context, false);
-               if (p.next_token().character() != '[') 
+               if (p.next_token().cat() == catEscape ||
+                   p.next_token().character() != '[') 
                        break;
                p.get_token(); // eat '['
                begin_inset(os, "OptArg\n");
@@ -499,14 +544,10 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                eat_whitespace(p, os, context, false);
                ++optargs;
        }
-#if 0
-       // This is the code needed to parse required arguments, but 
-       // required arguments come into being only much later than the
-       // file format tex2lyx is presently outputting.
        unsigned int reqargs = 0;
-       while (reqargs < context.layout->reqargs) {
+       while (LYX_FORMAT >= 392 && reqargs < context.layout->reqargs) {
                eat_whitespace(p, os, context, false);
-               if (p.next_token().character() != '{') 
+               if (p.next_token().cat() != catBegin)
                        break;
                p.get_token(); // eat '{'
                begin_inset(os, "OptArg\n");
@@ -516,7 +557,6 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
                eat_whitespace(p, os, context, false);
                ++reqargs;
        }
-#endif
        parse_text(p, os, FLAG_ITEM, outer, context);
        context.check_end_layout(os);
        if (parent_context.deeper_paragraph) {
@@ -528,6 +568,9 @@ void output_command_layout(ostream & os, Parser & p, bool outer,
        // We don't need really a new paragraph, but
        // we must make sure that the next item gets a \begin_layout.
        parent_context.new_paragraph(os);
+       // Set the font size to the original value. No need to output it here
+       // (Context::begin_layout() will do that if needed)
+       parent_context.font.size = size;
 }
 
 
@@ -585,6 +628,15 @@ void parse_arguments(string const & command,
                        parse_text(p, os, FLAG_ITEM, outer, context);
                        ert = "}";
                        break;
+               case item:
+                       // This argument consists only of a single item.
+                       // The presence of '{' or not must be preserved.
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin)
+                               ert += '{' + p.verbatim_item() + '}';
+                       else
+                               ert += p.verbatim_item();
+                       break;
                case verbatim:
                        // This argument may contain special characters
                        ert += '{' + p.verbatim_item() + '}';
@@ -617,65 +669,129 @@ bool parse_command(string const & command, Parser & p, ostream & os,
 
 
 /// Parses a minipage or parbox
-void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
-              Context & parent_context, bool use_parbox)
+void parse_box(Parser & p, ostream & os, unsigned outer_flags,
+               unsigned inner_flags, bool outer, Context & parent_context,
+               string const & outer_type, string const & special,
+               string const & inner_type)
 {
        string position;
        string inner_pos;
+       string hor_pos = "c";
        // We need to set the height to the LaTeX default of 1\\totalheight
        // for the case when no height argument is given
        string height_value = "1";
        string height_unit = "in";
        string height_special = "totalheight";
        string latex_height;
-       if (p.next_token().asInput() == "[") {
+       if (!inner_type.empty() && p.hasOpt()) {
                position = p.getArg('[', ']');
                if (position != "t" && position != "c" && position != "b") {
+                       cerr << "invalid position " << position << " for "
+                            << inner_type << endl;
                        position = "c";
-                       cerr << "invalid position for minipage/parbox" << endl;
                }
-               if (p.next_token().asInput() == "[") {
+               if (p.hasOpt()) {
                        latex_height = p.getArg('[', ']');
                        translate_box_len(latex_height, height_value, height_unit, height_special);
 
-                       if (p.next_token().asInput() == "[") {
+                       if (p.hasOpt()) {
                                inner_pos = p.getArg('[', ']');
                                if (inner_pos != "c" && inner_pos != "t" &&
                                    inner_pos != "b" && inner_pos != "s") {
+                                       cerr << "invalid inner_pos "
+                                            << inner_pos << " for "
+                                            << inner_type << endl;
                                        inner_pos = position;
-                                       cerr << "invalid inner_pos for minipage/parbox"
-                                            << endl;
                                }
                        }
                }
        }
        string width_value;
        string width_unit;
-       string const latex_width = p.verbatim_item();
+       string latex_width;
+       if (inner_type.empty()) {
+               if (special.empty())
+                       latex_width = "\\columnwidth";
+               else {
+                       Parser p2(special);
+                       latex_width = p2.getOptContent();
+                       string const opt = p2.getOptContent();
+                       if (!opt.empty()) {
+                               hor_pos = opt;
+                               if (hor_pos != "l" && hor_pos != "c" &&
+                                   hor_pos != "r") {
+                                       cerr << "invalid hor_pos " << hor_pos
+                                            << " for " << outer_type << endl;
+                                       hor_pos = "c";
+                               }
+                       }
+               }
+       } else
+               latex_width = p.verbatim_item();
        translate_len(latex_width, width_value, width_unit);
-       if (contains(width_unit, '\\') || contains(height_unit, '\\')) {
-               // LyX can't handle length variables
-               ostringstream ss;
-               if (use_parbox)
-                       ss << "\\parbox";
+       // LyX can't handle length variables
+       bool use_ert = contains(width_unit, '\\') || contains(height_unit, '\\');
+       if (!use_ert && !outer_type.empty() && !inner_type.empty()) {
+               // Look whether there is some content after the end of the
+               // inner box, but before the end of the outer box.
+               // If yes, we need to output ERT.
+               p.pushPosition();
+               if (inner_flags & FLAG_END)
+                       p.verbatimEnvironment(inner_type);
                else
-                       ss << "\\begin{minipage}";
-               if (!position.empty())
-                       ss << '[' << position << ']';
-               if (!latex_height.empty())
-                       ss << '[' << latex_height << ']';
-               if (!inner_pos.empty())
-                       ss << '[' << inner_pos << ']';
-               ss << "{" << latex_width << "}";
-               if (use_parbox)
-                       ss << '{';
+                       p.verbatim_item();
+               p.skip_spaces(true);
+               if ((outer_type == "framed" && p.next_token().asInput() != "\\end") ||
+                   (outer_type != "framed" && p.next_token().cat() != catEnd)) {
+                       // something is between the end of the inner box and
+                       // the end of the outer box, so we need to use ERT.
+                       use_ert = true;
+               }
+               p.popPosition();
+       }
+       if (use_ert) {
+               ostringstream ss;
+               if (!outer_type.empty()) {
+                       if (outer_flags & FLAG_END)
+                               ss << "\\begin{" << outer_type << '}';
+                       else {
+                               ss << '\\' << outer_type << '{';
+                               if (!special.empty())
+                                       ss << special;
+                       }
+               }
+               if (!inner_type.empty()) {
+                       if (inner_flags & FLAG_END)
+                               ss << "\\begin{" << inner_type << '}';
+                       else
+                               ss << '\\' << inner_type;
+                       if (!position.empty())
+                               ss << '[' << position << ']';
+                       if (!latex_height.empty())
+                               ss << '[' << latex_height << ']';
+                       if (!inner_pos.empty())
+                               ss << '[' << inner_pos << ']';
+                       ss << '{' << latex_width << '}';
+                       if (!(inner_flags & FLAG_END))
+                               ss << '{';
+               }
                handle_ert(os, ss.str(), parent_context);
-               parent_context.new_paragraph(os);
-               parse_text_in_inset(p, os, flags, outer, parent_context);
-               if (use_parbox)
-                       handle_ert(os, "}", parent_context);
-               else
-                       handle_ert(os, "\\end{minipage}", parent_context);
+               if (!inner_type.empty()) {
+                       parse_text(p, os, inner_flags, outer, parent_context);
+                       if (inner_flags & FLAG_END)
+                               handle_ert(os, "\\end{" + inner_type + '}',
+                                          parent_context);
+                       else
+                               handle_ert(os, "}", parent_context);
+               }
+               if (!outer_type.empty()) {
+                       parse_text(p, os, outer_flags, outer, parent_context);
+                       if (outer_flags & FLAG_END)
+                               handle_ert(os, "\\end{" + outer_type + '}',
+                                          parent_context);
+                       else
+                               handle_ert(os, "}", parent_context);
+               }
        } else {
                // LyX does not like empty positions, so we have
                // to set them to the LaTeX default values here.
@@ -684,18 +800,54 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
                if (inner_pos.empty())
                        inner_pos = position;
                parent_context.check_layout(os);
-               begin_inset(os, "Box Frameless\n");
+               begin_inset(os, "Box ");
+               if (outer_type == "framed")
+                       os << "Framed\n";
+               else if (outer_type == "framebox")
+                       os << "Boxed\n";
+               else if (outer_type == "shadowbox")
+                       os << "Shadowbox\n";
+               else if (outer_type == "shaded")
+                       os << "Shaded\n";
+               else if (outer_type == "doublebox")
+                       os << "Doublebox\n";
+               else if (outer_type.empty())
+                       os << "Frameless\n";
+               else
+                       os << outer_type << '\n';
                os << "position \"" << position << "\"\n";
-               os << "hor_pos \"c\"\n";
-               os << "has_inner_box 1\n";
+               os << "hor_pos \"" << hor_pos << "\"\n";
+               os << "has_inner_box " << !inner_type.empty() << "\n";
                os << "inner_pos \"" << inner_pos << "\"\n";
-               os << "use_parbox " << use_parbox << "\n";
+               os << "use_parbox " << (inner_type == "parbox") << '\n';
                os << "width \"" << width_value << width_unit << "\"\n";
                os << "special \"none\"\n";
                os << "height \"" << height_value << height_unit << "\"\n";
                os << "height_special \"" << height_special << "\"\n";
                os << "status open\n\n";
-               parse_text_in_inset(p, os, flags, outer, parent_context);
+               Context context(true, parent_context.textclass);
+               context.font = parent_context.font;
+
+               // If we have no inner box the contens will be read with the outer box
+               if (!inner_type.empty())
+                       parse_text(p, os, inner_flags, outer, context);
+
+               // Ensure that the end of the outer box is parsed correctly:
+               // The opening brace has been eaten by parse_outer_box()
+               if (!outer_type.empty() && (outer_flags & FLAG_ITEM)) {
+                       outer_flags &= ~FLAG_ITEM;
+                       outer_flags |= FLAG_BRACE_LAST;
+               }
+
+               // Find end of outer box, output contents if inner_type is
+               // empty and output possible comments
+               if (!outer_type.empty()) {
+                       // This does not output anything but comments if
+                       // inner_type is not empty (see use_ert)
+                       parse_text(p, os, outer_flags, outer, context);
+               }
+
+               context.check_end_layout(os);
                end_inset(os);
 #ifdef PRESERVE_LAYOUT
                // LyX puts a % after the end of the minipage
@@ -712,7 +864,7 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.skip_spaces();
                        // We add a protected space if something real follows
                        if (p.good() && p.next_token().cat() != catComment) {
-                               begin_inset(os, "Space ~\n");
+                               begin_inset(os, "space ~\n");
                                end_inset(os);
                        }
                }
@@ -721,6 +873,54 @@ void parse_box(Parser & p, ostream & os, unsigned flags, bool outer,
 }
 
 
+void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
+                     Context & parent_context, string const & outer_type,
+                     string const & special)
+{
+       eat_whitespace(p, os, parent_context, false);
+       if (flags & FLAG_ITEM) {
+               // Eat '{'
+               if (p.next_token().cat() == catBegin)
+                       p.get_token();
+               else
+                       cerr << "Warning: Ignoring missing '{' after \\"
+                            << outer_type << '.' << endl;
+               eat_whitespace(p, os, parent_context, false);
+       }
+       string inner;
+       unsigned int inner_flags = 0;
+       if (outer_type == "shaded") {
+               // These boxes never have an inner box
+               ;
+       } else if (p.next_token().asInput() == "\\parbox") {
+               inner = p.get_token().cs();
+               inner_flags = FLAG_ITEM;
+       } else if (p.next_token().asInput() == "\\begin") {
+               // Is this a minipage?
+               p.pushPosition();
+               p.get_token();
+               inner = p.getArg('{', '}');
+               p.popPosition();
+               if (inner == "minipage") {
+                       p.get_token();
+                       p.getArg('{', '}');
+                       eat_whitespace(p, os, parent_context, false);
+                       inner_flags = FLAG_END;
+               } else
+                       inner = "";
+       }
+       if (inner_flags == FLAG_END) {
+               active_environments.push_back(inner);
+               parse_box(p, os, flags, FLAG_END, outer, parent_context,
+                         outer_type, special, inner);
+               active_environments.pop_back();
+       } else {
+               parse_box(p, os, flags, inner_flags, outer, parent_context,
+                         outer_type, special, inner);
+       }
+}
+
+
 /// parse an unknown environment
 void parse_unknown_environment(Parser & p, string const & name, ostream & os,
                               unsigned flags, bool outer,
@@ -779,9 +979,8 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                eat_whitespace(p, os, parent_context, false);
                parent_context.check_layout(os);
                begin_inset(os, "Float " + unstarred_name + "\n");
-               if (p.next_token().asInput() == "[") {
+               if (p.hasOpt())
                        os << "placement " << p.getArg('[', ']') << '\n';
-               }
                os << "wide " << convert<string>(is_starred)
                   << "\nsideways false"
                   << "\nstatus open\n\n";
@@ -795,7 +994,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
 
        else if (name == "minipage") {
                eat_whitespace(p, os, parent_context, false);
-               parse_box(p, os, FLAG_END, outer, parent_context, false);
+               parse_box(p, os, 0, FLAG_END, outer, parent_context, "", "", name);
                p.skip_spaces();
        }
 
@@ -822,22 +1021,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
 
        else if (name == "framed" || name == "shaded") {
                eat_whitespace(p, os, parent_context, false);
-               parent_context.check_layout(os);
-               if (name == "framed")
-                       begin_inset(os, "Box Framed\n");
-               else
-                       begin_inset(os, "Box Shaded\n");
-               os << "position \"t\"\n"
-                     "hor_pos \"c\"\n"
-                     "has_inner_box 0\n"
-                     "inner_pos \"t\"\n"
-                     "width \"100col%\"\n"
-                     "special \"none\"\n"
-                     "height \"1in\"\n"
-                     "height_special \"totalheight\"\n"
-                     "status open\n";
-               parse_text_in_inset(p, os, FLAG_END, outer, parent_context);
-               end_inset(os);
+               parse_outer_box(p, os, FLAG_END, outer, parent_context, name, "");
                p.skip_spaces();
        }
 
@@ -902,7 +1086,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        // the two environments as one otherwise (bug 5716)
                        docstring const sep = from_ascii("--Separator--");
                        TeX2LyXDocClass const & textclass(parent_context.textclass);
-                       if (LYX_FORMAT >= 273 && textclass.hasLayout(sep)) {
+                       if (textclass.hasLayout(sep)) {
                                Context newcontext(parent_context);
                                newcontext.layout = &(textclass[sep]);
                                newcontext.check_layout(os);
@@ -1167,8 +1351,12 @@ void parse_noweb(Parser & p, ostream & os, Context & context)
                // at all.
                if (t.cat() == catEscape)
                        os << subst(t.asInput(), "\\", "\n\\backslash\n");
-               else
-                       os << subst(t.asInput(), "\n", "\n\\newline\n");
+               else {
+                       ostringstream oss;
+                       begin_inset(oss, "Newline newline");
+                       end_inset(oss);
+                       os << subst(t.asInput(), "\n", oss.str());
+               }
                // The scrap chunk is ended by an @ at the beginning of a line.
                // After the @ the line may contain a comment and/or
                // whitespace, but nothing else.
@@ -1307,7 +1495,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                Context & context)
 {
        Layout const * newlayout = 0;
-       // Store the latest bibliographystyle (needed for bibtex inset)
+       InsetLayout const * newinsetlayout = 0;
+       // Store the latest bibliographystyle and nocite{*} option
+       // (needed for bibtex inset)
+       string btprint;
        string bibliographystyle;
        bool const use_natbib = used_packages.find("natbib") != used_packages.end();
        bool const use_jurabib = used_packages.find("jurabib") != used_packages.end();
@@ -1316,7 +1507,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                Token const & t = p.get_token();
 
 #ifdef FILEDEBUG
-               cerr << "t: " << t << " flags: " << flags << "\n";
+               debugToken(cerr, t, flags);
 #endif
 
                if (flags & FLAG_ITEM) {
@@ -1335,9 +1526,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        flags |= FLAG_LEAVE;
                }
 
-               if (t.character() == ']' && (flags & FLAG_BRACK_LAST))
+               if (t.cat() != catEscape && t.character() == ']' &&
+                   (flags & FLAG_BRACK_LAST))
                        return;
-               if (t.character() == '}' && (flags & FLAG_BRACE_LAST))
+               if (t.cat() == catEnd && (flags & FLAG_BRACE_LAST))
                        return;
 
                // If there is anything between \end{env} and \begin{env} we
@@ -1434,8 +1626,33 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        handle_ert(os, s, context);
                }
 
-               else if (t.cat() == catLetter ||
-                              t.cat() == catOther ||
+               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)) {
+                                       handle_ert(os, phrase, context);
+                                       handled = true;
+                                       break;
+                               } else {
+                                       for (size_t i = 1; i < phrase.length(); ++i)
+                                               p.putback();
+                               }
+                       }
+                       if (!handled)
+                               os << t.cs();
+               }
+
+               else if (t.cat() == catOther ||
                               t.cat() == catAlign ||
                               t.cat() == catParameter) {
                        // This translates "&" to "\\&" which may be wrong...
@@ -1457,7 +1674,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                if (context.layout->free_spacing)
                                        os << ' ';
                                else {
-                                       begin_inset(os, "Space ~\n");
+                                       begin_inset(os, "space ~\n");
                                        end_inset(os);
                                }
                        } else
@@ -1610,7 +1827,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.skip_spaces();
                        string s;
                        bool optarg = false;
-                       if (p.next_token().character() == '[') {
+                       if (p.next_token().cat() != catEscape &&
+                           p.next_token().character() == '[') {
                                p.get_token(); // eat '['
                                s = parse_text_snippet(p, FLAG_BRACK_LAST,
                                                       outer, context);
@@ -1689,20 +1907,9 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                         context.new_layout_allowed &&
                         (newlayout = findLayout(context.textclass, t.cs() + '*')) &&
                         newlayout->isCommand()) {
-                       TeXFont const oldFont = context.font;
-                       // save the current font size
-                       string const size = oldFont.size;
-                       // reset the font size to default, because the
-                       // font size switches don't affect section
-                       // headings and the like
-                       context.font.size = known_coded_sizes[0];
-                       output_font_change(os, oldFont, context.font);
                        // write the layout
                        p.get_token();
                        output_command_layout(os, p, outer, context, newlayout);
-                       // set the font size to the original value
-                       context.font.size = size;
-                       output_font_change(os, oldFont, context.font);
                        p.skip_spaces();
                }
 
@@ -1710,33 +1917,21 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (context.new_layout_allowed &&
                         (newlayout = findLayout(context.textclass, t.cs())) &&
                         newlayout->isCommand()) {
-                       TeXFont const oldFont = context.font;
-                       // save the current font size
-                       string const size = oldFont.size;
-                       // reset the font size to default, because the font size switches don't
-                       // affect section headings and the like
-                       context.font.size = known_coded_sizes[0];
-                       output_font_change(os, oldFont, context.font);
                        // write the layout
                        output_command_layout(os, p, outer, context, newlayout);
-                       // set the font size to the original value
-                       context.font.size = size;
-                       output_font_change(os, oldFont, context.font);
                        p.skip_spaces();
                }
 
                else if (t.cs() == "caption") {
-                       // FIXME: this should get some cleanup. All
-                       // the \begin_layout:s are output by the
-                       // Context class!
                        p.skip_spaces();
                        context.check_layout(os);
                        p.skip_spaces();
                        begin_inset(os, "Caption\n\n");
-                       os << "\\begin_layout " 
-                          << to_utf8(context.textclass.defaultLayout().name()) 
-                          << '\n';
-                       if (p.next_token().character() == '[') {
+                       Context newcontext(true, context.textclass);
+                       newcontext.font = context.font;
+                       newcontext.check_layout(os);
+                       if (p.next_token().cat() != catEscape &&
+                           p.next_token().character() == '[') {
                                p.get_token(); // eat '['
                                begin_inset(os, "OptArg\n");
                                os << "status collapsed\n";
@@ -1751,7 +1946,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        context.new_paragraph(os);
                        end_inset(os);
                        p.skip_spaces();
-                       os << "\\end_layout\n";
+                       newcontext.check_end_layout(os);
                }
 
                else if (t.cs() == "includegraphics") {
@@ -1795,8 +1990,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                                     << endl;
                                        }
                                        name = dvips_name;
-                               } else if (!pdftex_name.empty())
+                               } else if (!pdftex_name.empty()) {
                                        name = pdftex_name;
+                                       pdflatex = true;
+                               }
                        }
 
                        if (makeAbsPath(name, path).exists())
@@ -2085,6 +2282,20 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        os << "\\lyxline";
                }
 
+               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.
+                       char const * const * 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);
+               }
+
                else if (is_known(t.cs(), known_ref_commands)) {
                        context.check_layout(os);
                        begin_command_inset(os, "ref", t.cs());
@@ -2203,21 +2414,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
-               else if (t.cs() == "cite") {
+               else if (t.cs() == "cite"
+                       || t.cs() == "nocite") {
                        context.check_layout(os);
                        // LyX cannot handle newlines in a latex command
                        string after = subst(p.getOptContent(), "\n", " ");
-                       begin_command_inset(os, "citation", "cite");
-                       os << "after " << '"' << after << '"' << "\n";
-                       os << "key " << '"' << subst(p.verbatim_item(), "\n", " ") << '"' << "\n";
-                       end_inset(os);
+                       string key = subst(p.verbatim_item(), "\n", " ");
+                       // store the case that it is "\nocite{*}" to use it later for
+                       // the BibTeX inset
+                       if (key != "*") {
+                               begin_command_inset(os, "citation", t.cs());
+                               os << "after " << '"' << after << '"' << "\n";
+                               os << "key " << '"' << key << '"' << "\n";
+                               end_inset(os);
+                       } else if (t.cs() == "nocite")
+                               btprint = key;
                }
 
                else if (t.cs() == "index") {
                        context.check_layout(os);
                        begin_inset(os, "Index\n");
                        os << "status collapsed\n";
-                       parse_text_in_inset(p, os, FLAG_ITEM, false, context);
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
                        end_inset(os);
                }
 
@@ -2255,14 +2473,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        skip_spaces_braces(p);
                }
 
-               else if (t.cs() == "url") {
-                       context.check_layout(os);
-                       begin_inset(os, "Flex URL\n");
-                       os << "status collapsed\n";
-                       parse_text_in_inset(p, os, FLAG_ITEM, false, context);
-                       end_inset(os);
-               }
-
                else if (LYX_FORMAT >= 408 &&
                         (t.cs() == "textsuperscript" || t.cs() == "textsubscript")) {
                        context.check_layout(os);
@@ -2290,9 +2500,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        char const * const * where = is_known(t.cs(), known_sizes);
                        context.check_layout(os);
                        TeXFont const oldFont = context.font;
-                       // the font size index differs by 1, because the known_coded_sizes
-                       // has additionally a "default" entry
-                       context.font.size = known_coded_sizes[where - known_sizes + 1];
+                       context.font.size = known_coded_sizes[where - known_sizes];
                        output_font_change(os, oldFont, context.font);
                        eat_whitespace(p, os, context, false);
                }
@@ -2379,19 +2587,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        // save the language for the case that a
                        // \foreignlanguage is used 
 
-                       context.font.language = subst(p.verbatim_item(), "\n", " ");
-                       os << "\\lang " << context.font.language << "\n";
+                       context.font.language = babel2lyx(p.verbatim_item());
+                       os << "\n\\lang " << context.font.language << "\n";
                }
 
                else if (t.cs() == "foreignlanguage") {
-                       context.check_layout(os);
-                       os << "\n\\lang " << subst(p.verbatim_item(), "\n", " ") << "\n";
-                       os << subst(p.verbatim_item(), "\n", " ");
-                       // FIXME: the second argument of selectlanguage
-                       // has to be parsed (like for \textsf, for
-                       // example). 
-                       // set back to last selectlanguage
-                       os << "\n\\lang " << context.font.language << "\n";
+                       string const lang = babel2lyx(p.verbatim_item());
+                       parse_text_attributes(p, os, FLAG_ITEM, outer,
+                                             context, "\\lang",
+                                             context.font.language, lang);
                }
 
                else if (t.cs() == "inputencoding") {
@@ -2400,19 +2604,6 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        p.setEncoding(enc);
                }
 
-               else if (t.cs() == "LyX" || t.cs() == "TeX"
-                        || t.cs() == "LaTeX") {
-                       context.check_layout(os);
-                       os << t.cs();
-                       skip_spaces_braces(p);
-               }
-
-               else if (t.cs() == "LaTeXe") {
-                       context.check_layout(os);
-                       os << "LaTeX2e";
-                       skip_spaces_braces(p);
-               }
-
                else if (t.cs() == "ldots") {
                        context.check_layout(os);
                        os << "\\SpecialChar \\ldots{}\n";
@@ -2431,15 +2622,16 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        skip_spaces_braces(p);
                }
 
-               else if (LYX_FORMAT >= 307 && t.cs() == "slash") {
+               else if (t.cs() == "slash") {
                        context.check_layout(os);
                        os << "\\SpecialChar \\slash{}\n";
                        skip_spaces_braces(p);
                }
 
-               else if (LYX_FORMAT >= 307 && t.cs() == "nobreakdash") {
+               else if (t.cs() == "nobreakdash" && p.next_token().asInput() == "-") {
                        context.check_layout(os);
-                       os << "\\SpecialChar \\nobreakdash\n";
+                       os << "\\SpecialChar \\nobreakdash-\n";
+                       p.get_token();
                }
 
                else if (t.cs() == "textquotedbl") {
@@ -2538,21 +2730,27 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                else if (t.cs() == "\\") {
                        context.check_layout(os);
-                       string const next = p.next_token().asInput();
-                       if (next == "[")
+                       if (p.hasOpt())
                                handle_ert(os, "\\\\" + p.getOpt(), context);
-                       else if (next == "*") {
+                       else if (p.next_token().asInput() == "*") {
                                p.get_token();
+                               // getOpt() eats the following space if there
+                               // is no optional argument, but that is OK
+                               // here since it has no effect in the output.
                                handle_ert(os, "\\\\*" + p.getOpt(), context);
                        }
                        else {
-                               os << "\n\\newline\n";
+                               begin_inset(os, "Newline newline");
+                               end_inset(os);
                        }
                }
 
-               else if (t.cs() == "newline") {
+               else if (t.cs() == "newline" ||
+                        (t.cs() == "linebreak" && !p.hasOpt())) {
                        context.check_layout(os);
-                       os << "\n\\" << t.cs() << "\n";
+                       begin_inset(os, "Newline ");
+                       os << t.cs();
+                       end_inset(os);
                        skip_spaces_braces(p);
                }
 
@@ -2577,7 +2775,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                if (!tex_name.empty())
                                        filename = tex_name;
                        }
-                       bool xfig = false;
+                       bool external = false;
                        string outname;
                        if (makeAbsPath(filename, path).exists()) {
                                string const abstexname =
@@ -2589,20 +2787,55 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                fix_relative_filename(filename);
                                string const lyxname =
                                        changeExtension(filename, ".lyx");
-                               if (t.cs() == "input" && FileName(absfigname).exists()) {
+                               bool xfig = false;
+                               external = FileName(absfigname).exists();
+                               if (t.cs() == "input") {
+                                       string const ext = getExtension(abstexname);
+
+                                       // Combined PS/LaTeX:
+                                       // x.eps, x.pstex_t (old xfig)
+                                       // x.pstex, x.pstex_t (new xfig, e.g. 3.2.5)
                                        FileName const absepsname(
                                                changeExtension(abstexname, ".eps"));
+                                       FileName const abspstexname(
+                                               changeExtension(abstexname, ".pstex"));
+                                       bool const xfigeps =
+                                               (absepsname.exists() ||
+                                                abspstexname.exists()) &&
+                                               ext == "pstex_t";
+
+                                       // Combined PDF/LaTeX:
+                                       // x.pdf, x.pdftex_t (old xfig)
+                                       // x.pdf, x.pdf_t (new xfig, e.g. 3.2.5)
                                        FileName const abspdfname(
                                                changeExtension(abstexname, ".pdf"));
-                                       string const ext = getExtension(abstexname);
                                        bool const xfigpdf =
-                                               abspdfname.exists() && ext == "pdftex_t";
-                                       bool const xfigeps  =
-                                               absepsname.exists() && ext == "pstex_t";
-                                       xfig = xfigpdf || xfigeps;
+                                               abspdfname.exists() &&
+                                               (ext == "pdftex_t" || ext == "pdf_t");
+                                       if (xfigpdf)
+                                               pdflatex = true;
+
+                                       // Combined PS/PDF/LaTeX:
+                                       // x_pspdftex.eps, x_pspdftex.pdf, x.pspdftex
+                                       string const absbase2(
+                                               removeExtension(abstexname) + "_pspdftex");
+                                       FileName const abseps2name(
+                                               addExtension(absbase2, ".eps"));
+                                       FileName const abspdf2name(
+                                               addExtension(absbase2, ".pdf"));
+                                       bool const xfigboth =
+                                               abspdf2name.exists() &&
+                                               abseps2name.exists() && ext == "pspdftex";
+
+                                       xfig = xfigpdf || xfigeps || xfigboth;
+                                       external = external && xfig;
                                }
-                               if (xfig) {
+                               if (external) {
                                        outname = changeExtension(filename, ".fig");
+                               } else if (xfig) {
+                                       // Don't try to convert, the result
+                                       // would be full of ERT.
+                                       outname = filename;
                                } else if (t.cs() != "verbatiminput" &&
                                    tex2lyx(abstexname, FileName(abslyxname),
                                            p.getEncoding())) {
@@ -2615,7 +2848,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                     << filename << "'." << endl;
                                outname = filename;
                        }
-                       if (xfig) {
+                       if (external) {
                                begin_inset(os, "External\n");
                                os << "\ttemplate XFig\n"
                                   << "\tfilename " << outname << '\n';
@@ -2630,14 +2863,41 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "bibliographystyle") {
                        // store new bibliographystyle
                        bibliographystyle = p.verbatim_item();
-                       // output new bibliographystyle.
-                       // This is only necessary if used in some other macro than \bibliography.
-                       handle_ert(os, "\\bibliographystyle{" + bibliographystyle + "}", context);
+                       // If any other command than \bibliography and
+                       // \nocite{*} follows, we need to output the style
+                       // (because it might be used by that command).
+                       // Otherwise, it will automatically be output by LyX.
+                       p.pushPosition();
+                       bool output = true;
+                       for (Token t2 = p.get_token(); p.good(); t2 = p.get_token()) {
+                               if (t2.cat() == catBegin)
+                                       break;
+                               if (t2.cat() != catEscape)
+                                       continue;
+                               if (t2.cs() == "nocite") {
+                                       if (p.getArg('{', '}') == "*")
+                                               continue;
+                               } else if (t2.cs() == "bibliography")
+                                       output = false;
+                               break;
+                       }
+                       p.popPosition();
+                       if (output) {
+                               handle_ert(os,
+                                       "\\bibliographystyle{" + bibliographystyle + '}',
+                                       context);
+                       }
                }
 
                else if (t.cs() == "bibliography") {
                        context.check_layout(os);
                        begin_command_inset(os, "bibtex", "bibtex");
+                       if (!btprint.empty()) {
+                               os << "btprint " << '"' << "btPrintAll" << '"' << "\n";
+                               // clear the string because the next BibTeX inset can be without the
+                               // \nocite{*} option
+                               btprint.clear();
+                       }
                        os << "bibfiles " << '"' << p.verbatim_item() << '"' << "\n";
                        // Do we have a bibliographystyle set?
                        if (!bibliographystyle.empty())
@@ -2646,8 +2906,18 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "parbox")
-                       parse_box(p, os, FLAG_ITEM, outer, context, true);
-               
+                       parse_box(p, os, 0, FLAG_ITEM, outer, context, "", "", t.cs());
+
+               else if (t.cs() == "ovalbox" || t.cs() == "Ovalbox" ||
+                        t.cs() == "shadowbox" || t.cs() == "doublebox")
+                       parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), "");
+
+               else if (t.cs() == "framebox") {
+                       string special = p.getOpt();
+                       special += p.getOpt();
+                       parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), special);
+               }
+
                //\makebox() is part of the picture environment and different from \makebox{}
                //\makebox{} will be parsed by parse_box when bug 2956 is fixed
                else if (t.cs() == "makebox") {
@@ -2675,7 +2945,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (is_known(t.cs(), known_spaces)) {
                        char const * const * where = is_known(t.cs(), known_spaces);
                        context.check_layout(os);
-                       begin_inset(os, "Space ");
+                       begin_inset(os, "space ");
                        os << '\\' << known_coded_spaces[where - known_spaces]
                           << '\n';
                        end_inset(os);
@@ -2693,18 +2963,28 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "newpage" ||
-                       t.cs() == "clearpage" ||
-                       t.cs() == "cleardoublepage") {
+                        (t.cs() == "pagebreak" && !p.hasOpt()) ||
+                        t.cs() == "clearpage" ||
+                        t.cs() == "cleardoublepage") {
                        context.check_layout(os);
-                       os << "\n\\" << t.cs() << "\n";
+                       begin_inset(os, "Newpage ");
+                       os << t.cs();
+                       end_inset(os);
                        skip_spaces_braces(p);
                }
 
-               else if (t.cs() == "newcommand" ||
+               else if (t.cs() == "DeclareRobustCommand" ||
+                        t.cs() == "DeclareRobustCommandx" ||
+                        t.cs() == "newcommand" ||
+                        t.cs() == "newcommandx" ||
                         t.cs() == "providecommand" ||
-                        t.cs() == "renewcommand") {
-                       // providecommand could be handled by parse_command(),
-                       // but we need to call add_known_command() here.
+                        t.cs() == "providecommandx" ||
+                        t.cs() == "renewcommand" ||
+                        t.cs() == "renewcommandx") {
+                       // DeclareRobustCommand, DeclareRobustCommandx,
+                       // providecommand and providecommandx could be handled
+                       // by parse_command(), but we need to call
+                       // add_known_command() here.
                        string name = t.asInput();
                        if (p.next_token().asInput() == "*") {
                                // Starred form. Eat '*'
@@ -2719,7 +2999,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                           opt1 + opt2 +
                                           '{' + p.verbatim_item() + '}';
 
-                       if (t.cs() == "providecommand")
+                       if (t.cs() == "DeclareRobustCommand" ||
+                           t.cs() == "DeclareRobustCommandx" ||
+                           t.cs() == "providecommand" ||
+                           t.cs() == "providecommandx" ||
+                           name[name.length()-1] == '*')
                                handle_ert(os, ert, context);
                        else {
                                context.check_layout(os);
@@ -2728,17 +3012,50 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                end_inset(os);
                        }
                }
-               
-               else if (t.cs() == "vspace") {
+
+               else if (t.cs() == "let" && p.next_token().asInput() != "*") {
+                       // let could be handled by parse_command(),
+                       // but we need to call add_known_command() here.
+                       string ert = t.asInput();
+                       string name;
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin) {
+                               name = p.verbatim_item();
+                               ert += '{' + name + '}';
+                       } else {
+                               name = p.verbatim_item();
+                               ert += name;
+                       }
+                       string command;
+                       p.skip_spaces();
+                       if (p.next_token().cat() == catBegin) {
+                               command = p.verbatim_item();
+                               ert += '{' + command + '}';
+                       } else {
+                               command = p.verbatim_item();
+                               ert += command;
+                       }
+                       // If command is known, make name known too, to parse
+                       // its arguments correctly. For this reason we also
+                       // have commands in syntax.default that are hardcoded.
+                       CommandMap::iterator it = known_commands.find(command);
+                       if (it != known_commands.end())
+                               known_commands[t.asInput()] = it->second;
+                       handle_ert(os, ert, context);
+               }
+
+               else if (t.cs() == "hspace" || t.cs() == "vspace") {
                        bool starred = false;
                        if (p.next_token().asInput() == "*") {
                                p.get_token();
                                starred = true;
                        }
+                       string name = t.asInput();
                        string const length = p.verbatim_item();
                        string unit;
                        string valstring;
                        bool valid = splitLatexLength(length, valstring, unit);
+                       bool known_hspace = false;
                        bool known_vspace = false;
                        bool known_unit = false;
                        double value;
@@ -2746,21 +3063,31 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                istringstream iss(valstring);
                                iss >> value;
                                if (value == 1.0) {
-                                       if (unit == "\\smallskipamount") {
-                                               unit = "smallskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\medskipamount") {
-                                               unit = "medskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\bigskipamount") {
-                                               unit = "bigskip";
-                                               known_vspace = true;
-                                       } else if (unit == "\\fill") {
-                                               unit = "vfill";
-                                               known_vspace = true;
+                                       if (t.cs()[0] == 'h') {
+                                               if (unit == "\\fill") {
+                                                       if (!starred) {
+                                                               unit = "";
+                                                               name = "\\hfill";
+                                                       }
+                                                       known_hspace = true;
+                                               }
+                                       } else {
+                                               if (unit == "\\smallskipamount") {
+                                                       unit = "smallskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\medskipamount") {
+                                                       unit = "medskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\bigskipamount") {
+                                                       unit = "bigskip";
+                                                       known_vspace = true;
+                                               } else if (unit == "\\fill") {
+                                                       unit = "vfill";
+                                                       known_vspace = true;
+                                               }
                                        }
                                }
-                               if (!known_vspace) {
+                               if (!known_hspace && !known_vspace) {
                                        switch (unitFromString(unit)) {
                                        case Length::SP:
                                        case Length::PT:
@@ -2782,8 +3109,23 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                }
                        }
 
-                       if (known_unit || known_vspace) {
-                               // Literal length or known variable
+                       if (t.cs()[0] == 'h' && (known_unit || known_hspace)) {
+                               // Literal horizontal length or known variable
+                               context.check_layout(os);
+                               begin_inset(os, "space ");
+                               os << name;
+                               if (starred)
+                                       os << '*';
+                               os << '{';
+                               if (known_hspace)
+                                       os << unit;
+                               os << "}";
+                               if (known_unit && !known_hspace)
+                                       os << "\n\\length "
+                                          << translate_len(length);
+                               end_inset(os);
+                       } else if (known_unit || known_vspace) {
+                               // Literal vertical length or known variable
                                context.check_layout(os);
                                begin_inset(os, "VSpace ");
                                if (known_unit)
@@ -2793,8 +3135,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                        os << '*';
                                end_inset(os);
                        } else {
-                               // LyX can't handle other length variables in Inset VSpace
-                               string name = t.asInput();
+                               // LyX can't handle other length variables in Inset VSpace/space
                                if (starred)
                                        name += '*';
                                if (valid) {
@@ -2809,6 +3150,17 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        }
                }
 
+               // The single '=' is meant here.
+               else if ((newinsetlayout = findInsetLayout(context.textclass, t.cs(), true))) {
+                       p.skip_spaces();
+                       context.check_layout(os);
+                       begin_inset(os, "Flex ");
+                       os << to_utf8(newinsetlayout->name()) << '\n'
+                          << "status collapsed\n";
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
+                       end_inset(os);
+               }
+
                else {
                        // try to see whether the string is in unicodesymbols
                        // Only use text mode commands, since we are in text mode here,