]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
fix msvc linker error: for the linker struct and class isn't the same here
[lyx.git] / src / tex2lyx / text.cpp
index c3457fb8f14f264cf667c9d06d0c4457b42b86e6..0c49bba9dafe85fc0e362e5a3736a6d1a535bc25 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)
@@ -393,6 +409,53 @@ bool skip_braces(Parser & p)
 }
 
 
+/// replace LaTeX commands in \p s from the unicodesymbols file with their
+/// unciode points
+docstring convert_unicodesymbols(docstring s)
+{
+       odocstringstream os;
+       for (size_t i = 0; i < s.size();) {
+               if (s[i] != '\\') {
+                       os << s[i++];
+                       continue;
+               }
+               s = s.substr(i);
+               docstring rem;
+               docstring parsed = encodings.fromLaTeXCommand(s, rem,
+                               Encodings::TEXT_CMD);
+               os << parsed;
+               s = rem;
+               if (s.empty() || s[0] != '\\')
+                       i = 0;
+               else
+                       i = 1;
+       }
+       return os.str();
+}
+
+
+/// try to convert \p s to a valid InsetCommand argument
+string convert_command_inset_arg(string s)
+{
+       if (isAscii(s))
+               // since we don't know the input encoding we can't use from_utf8
+               s = to_utf8(convert_unicodesymbols(from_ascii(s)));
+       // LyX cannot handle newlines in a latex command
+       return subst(s, "\n", " ");
+}
+
+
+void handle_backslash(ostream & os, string const & s)
+{
+       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+               if (*it == '\\')
+                       os << "\n\\backslash\n";
+               else
+                       os << *it;
+       }
+}
+
+
 void handle_ert(ostream & os, string const & s, Context & context)
 {
        // We must have a valid layout before outputting the ERT inset.
@@ -422,12 +485,7 @@ void handle_comment(ostream & os, string const & s, Context & context)
        begin_inset(os, "ERT");
        os << "\nstatus collapsed\n";
        newcontext.check_layout(os);
-       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
-               if (*it == '\\')
-                       os << "\n\\backslash\n";
-               else
-                       os << *it;
-       }
+       handle_backslash(os, s);
        // make sure that our comment is the last thing on the line
        newcontext.new_paragraph(os);
        newcontext.check_layout(os);
@@ -436,27 +494,25 @@ void handle_comment(ostream & os, string const & s, Context & context)
 }
 
 
-Layout const * findLayout(TextClass const & textclass, string const & name)
+Layout const * findLayout(TextClass const & textclass, string const & name, bool command)
 {
-       DocumentClass::const_iterator lit = textclass.begin();
-       DocumentClass::const_iterator len = textclass.end();
-       for (; lit != len; ++lit)
-               if (lit->latexname() == name)
-                       return &*lit;
-       return 0;
+       Layout const * layout = findLayoutWithoutModule(textclass, name, command);
+       if (layout)
+               return layout;
+       if (checkModule(name, command))
+               return findLayoutWithoutModule(textclass, name, command);
+       return layout;
 }
 
 
 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;
+       InsetLayout const * insetlayout = findInsetLayoutWithoutModule(textclass, name, command);
+       if (insetlayout)
+               return insetlayout;
+       if (checkModule(name, command))
+               return findInsetLayoutWithoutModule(textclass, name, command);
+       return insetlayout;
 }
 
 
@@ -627,7 +683,13 @@ void parse_arguments(string const & command,
                        break;
                case optional:
                        // true because we must not eat whitespace
-                       ert += p.getOpt(true);
+                       // if an optional arg follows me must not strip the
+                       // brackets from this one
+                       if (i < no_arguments - 1 &&
+                           template_arguments[i+1] == optional)
+                               ert += p.getFullOpt(true);
+                       else
+                               ert += p.getOpt(true);
                        break;
                }
        }
@@ -698,8 +760,8 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        latex_width = "\\columnwidth";
                else {
                        Parser p2(special);
-                       latex_width = p2.getOptContent();
-                       string const opt = p2.getOptContent();
+                       latex_width = p2.getArg('[', ']');
+                       string const opt = p2.getArg('[', ']');
                        if (!opt.empty()) {
                                hor_pos = opt;
                                if (hor_pos != "l" && hor_pos != "c" &&
@@ -905,6 +967,33 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
 }
 
 
+void parse_listings(Parser & p, ostream & os, Context & parent_context)
+{
+       parent_context.check_layout(os);
+       begin_inset(os, "listings\n");
+       os << "inline false\n"
+          << "status collapsed\n";
+       Context context(true, parent_context.textclass);
+       context.layout = &parent_context.textclass.plainLayout();
+       context.check_layout(os);
+       string const s = p.verbatimEnvironment("lstlisting");
+       for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
+               if (*it == '\\')
+                       os << "\n\\backslash\n";
+               else if (*it == '\n') {
+                       // avoid adding an empty paragraph at the end
+                       if (it + 1 != et) {
+                               context.new_paragraph(os);
+                               context.check_layout(os);
+                       }
+               } else
+                       os << *it;
+       }
+       context.check_end_layout(os);
+       end_inset(os);
+}
+
+
 /// parse an unknown environment
 void parse_unknown_environment(Parser & p, string const & name, ostream & os,
                               unsigned flags, bool outer,
@@ -936,6 +1025,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        string & last_env, Context & parent_context)
 {
        Layout const * newlayout;
+       InsetLayout const * newinsetlayout = 0;
        string const name = p.getArg('{', '}');
        const bool is_starred = suffixIs(name, '*');
        string const unstarred_name = rtrim(name, "*");
@@ -1009,6 +1099,17 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                p.skip_spaces();
        }
 
+       else if (name == "lstlisting") {
+               eat_whitespace(p, os, parent_context, false);
+               // FIXME handle listings with parameters
+               if (p.hasOpt())
+                       parse_unknown_environment(p, name, os, FLAG_END,
+                                                 outer, parent_context);
+               else
+                       parse_listings(p, os, parent_context);
+               p.skip_spaces();
+       }
+
        else if (!parent_context.new_layout_allowed)
                parse_unknown_environment(p, name, os, FLAG_END, outer,
                                          parent_context);
@@ -1053,8 +1154,7 @@ void parse_environment(Parser & p, ostream & os, bool outer,
        }
 
        // The single '=' is meant here.
-       else if ((newlayout = findLayout(parent_context.textclass, name)) &&
-                 newlayout->isEnvironment()) {
+       else if ((newlayout = findLayout(parent_context.textclass, name, false))) {
                eat_whitespace(p, os, parent_context, false);
                Context context(true, parent_context.textclass, newlayout,
                                parent_context.layout, parent_context.font);
@@ -1113,6 +1213,17 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                p.skip_spaces();
        }
 
+       // The single '=' is meant here.
+       else if ((newinsetlayout = findInsetLayout(parent_context.textclass, name, false))) {
+               eat_whitespace(p, os, parent_context, false);
+               parent_context.check_layout(os);
+               begin_inset(os, "Flex ");
+               os << to_utf8(newinsetlayout->name()) << '\n'
+                  << "status collapsed\n";
+               parse_text_in_inset(p, os, FLAG_END, false, parent_context, newinsetlayout);
+               end_inset(os);
+       }
+
        else if (name == "appendix") {
                // This is no good latex style, but it works and is used in some documents...
                eat_whitespace(p, os, parent_context, false);
@@ -1848,10 +1959,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                else if (t.cs() == "bibitem") {
                        context.set_item();
                        context.check_layout(os);
-                       begin_command_inset(os, "bibitem", "bibitem");
-                       os << "label \"" << p.getOptContent() << "\"\n";
-                       os << "key \"" << p.verbatim_item() << "\"\n";
-                       end_inset(os);
+                       string label = convert_command_inset_arg(p.getArg('[', ']'));
+                       string key = convert_command_inset_arg(p.verbatim_item());
+                       if (contains(label, '\\') || contains(key, '\\')) {
+                               // LyX can't handle LaTeX commands in labels or keys
+                               handle_ert(os, t.asInput() + '[' + label +
+                                              "]{" + p.verbatim_item() + '}',
+                                          context);
+                       } else {
+                               begin_command_inset(os, "bibitem", "bibitem");
+                               os << "label \"" << label << "\"\n"
+                                     "key \"" << key << "\"\n";
+                               end_inset(os);
+                       }
                }
 
                else if (is_macro(p))
@@ -1889,8 +2009,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                // Must attempt to parse "Section*" before "Section".
                else if ((p.next_token().asInput() == "*") &&
                         context.new_layout_allowed &&
-                        (newlayout = findLayout(context.textclass, t.cs() + '*')) &&
-                        newlayout->isCommand()) {
+                        (newlayout = findLayout(context.textclass, t.cs() + '*', true))) {
                        // write the layout
                        p.get_token();
                        output_command_layout(os, p, outer, context, newlayout);
@@ -1899,8 +2018,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
 
                // Section headings and the like
                else if (context.new_layout_allowed &&
-                        (newlayout = findLayout(context.textclass, t.cs())) &&
-                        newlayout->isCommand()) {
+                        (newlayout = findLayout(context.textclass, t.cs(), true))) {
                        // write the layout
                        output_command_layout(os, p, outer, context, newlayout);
                        p.skip_spaces();
@@ -2281,13 +2399,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (is_known(t.cs(), known_ref_commands)) {
-                       context.check_layout(os);
-                       begin_command_inset(os, "ref", t.cs());
-                       // LyX cannot handle newlines in a latex command
-                       // FIXME: Move the substitution into parser::getOpt()?
-                       os << subst(p.getOpt(), "\n", " ");
-                       os << "reference " << '"' << subst(p.verbatim_item(), "\n", " ") << '"' << "\n";
-                       end_inset(os);
+                       string const opt = p.getOpt();
+                       if (opt.empty()) {
+                               context.check_layout(os);
+                               begin_command_inset(os, "ref", t.cs());
+                               os << "reference \""
+                                  << convert_command_inset_arg(p.verbatim_item())
+                                  << "\"\n";
+                               end_inset(os);
+                       } else {
+                               // LyX does not support optional arguments of ref commands
+                               handle_ert(os, t.asInput() + '[' + opt + "]{" +
+                                              p.verbatim_item() + "}", context);
+                       }
                }
 
                else if (use_natbib &&
@@ -2333,19 +2457,19 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (!after.empty()) {
                                after.erase(0, 1);
                                after.erase(after.length() - 1, 1);
-                               // LyX cannot handle newlines in the parameter
-                               after = subst(after, "\n", " ");
+                               after = convert_command_inset_arg(after);
                        }
                        if (!before.empty()) {
                                before.erase(0, 1);
                                before.erase(before.length() - 1, 1);
-                               // LyX cannot handle newlines in the parameter
-                               before = subst(before, "\n", " ");
+                               before = convert_command_inset_arg(before);
                        }
                        begin_command_inset(os, "citation", command);
                        os << "after " << '"' << after << '"' << "\n";
                        os << "before " << '"' << before << '"' << "\n";
-                       os << "key " << '"' << p.verbatim_item() << '"' << "\n";
+                       os << "key \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
                        end_inset(os);
                }
 
@@ -2401,9 +2525,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                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", " ");
-                       string key = subst(p.verbatim_item(), "\n", " ");
+                       string after = convert_command_inset_arg(p.getArg('[', ']'));
+                       string key = convert_command_inset_arg(p.verbatim_item());
                        // store the case that it is "\nocite{*}" to use it later for
                        // the BibTeX inset
                        if (key != "*") {
@@ -2419,27 +2542,30 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        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);
                }
 
                else if (t.cs() == "nomenclature") {
                        context.check_layout(os);
                        begin_command_inset(os, "nomenclature", "nomenclature");
-                       // LyX cannot handle newlines in a latex command
-                       string prefix = subst(p.getOptContent(), "\n", " ");
+                       string prefix = convert_command_inset_arg(p.getArg('[', ']'));
                        if (!prefix.empty())
                                os << "prefix " << '"' << prefix << '"' << "\n";
-                       os << "symbol " << '"' << subst(p.verbatim_item(), "\n", " ") << '"' << "\n";
-                       os << "description " << '"' << subst(p.verbatim_item(), "\n", " ") << '"' << "\n";
+                       os << "symbol " << '"'
+                          << convert_command_inset_arg(p.verbatim_item());
+                       os << "\"\ndescription \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
                        end_inset(os);
                }
                
                else if (t.cs() == "label") {
                        context.check_layout(os);
                        begin_command_inset(os, "label", "label");
-                       // LyX cannot handle newlines in a latex command
-                       os << "name " << '"' << subst(p.verbatim_item(), "\n", " ") << '"' << "\n";
+                       os << "name \""
+                          << convert_command_inset_arg(p.verbatim_item())
+                          << "\"\n";
                        end_inset(os);
                }
 
@@ -2457,14 +2583,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);
@@ -2905,7 +3023,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), "");
 
                else if (t.cs() == "framebox") {
-                       string special = p.getOpt();
+                       string special = p.getFullOpt();
                        special += p.getOpt();
                        parse_outer_box(p, os, FLAG_ITEM, outer, context, t.cs(), special);
                }
@@ -2984,7 +3102,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                name += '*';
                        }
                        string const command = p.verbatim_item();
-                       string const opt1 = p.getOpt();
+                       string const opt1 = p.getFullOpt();
                        string const opt2 = p.getFullOpt();
                        add_known_command(command, opt1, !opt2.empty());
                        string const ert = name + '{' + command + '}' +
@@ -3149,7 +3267,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        begin_inset(os, "Flex ");
                        os << to_utf8(newinsetlayout->name()) << '\n'
                           << "status collapsed\n";
-                       parse_text_in_inset(p, os, FLAG_ITEM, false, context);
+                       parse_text_in_inset(p, os, FLAG_ITEM, false, context, newinsetlayout);
                        end_inset(os);
                }