From cfed2612eb4569f8f2308f4fc3bd0839de3c879a Mon Sep 17 00:00:00 2001 From: Georg Baum Date: Tue, 10 Aug 2004 09:40:53 +0000 Subject: [PATCH] handle natbib and jurabib packages and citation commands git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@8882 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/tex2lyx/ChangeLog | 15 +++++ src/tex2lyx/preamble.C | 112 ++++++++++++++++++++++++++++--- src/tex2lyx/tex2lyx.h | 3 + src/tex2lyx/text.C | 147 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 268 insertions(+), 9 deletions(-) diff --git a/src/tex2lyx/ChangeLog b/src/tex2lyx/ChangeLog index 9520dad905..8ac816b515 100644 --- a/src/tex2lyx/ChangeLog +++ b/src/tex2lyx/ChangeLog @@ -1,3 +1,18 @@ +2004-08-10 Georg Baum + + * preamble.C (split_options): new, split package options into vector + * preamble.C (add_package): new, add a package with options to + used_packages + * preamble.C, tex2lyx.h (used_packages): new map of used packages + and options + * preamble.C (handle_package): handle natbib and jurabib package + * preamble.C (handle_package): output a message for unhandled options + * preamble.C (parse_preamble): handle \jurabibsetup + * text.C (known_natbib_commands): new list of known natbib ccommands + * text.C (known_jurabib_commands): new list of known jurabib ccommands + * text.C (getCiteArguments): new, parse cite command arguments + * text.C (parse_text): handle natbib and jurabib citation commands + 2004-07-29 Georg Baum * preamble.C (end_preamble): change file format from 228 to 235 diff --git a/src/tex2lyx/preamble.C b/src/tex2lyx/preamble.C index d4f80b9b1a..cd61b2f583 100644 --- a/src/tex2lyx/preamble.C +++ b/src/tex2lyx/preamble.C @@ -39,6 +39,8 @@ using lyx::support::LibFileSearch; // special columntypes extern std::map special_columns; +std::map > used_packages; + namespace { const char * const known_languages[] = { "austrian", "babel", "bahasa", @@ -103,8 +105,68 @@ void handle_opt(vector & opts, char const * const * what, string & targe } -void handle_package(string const & name, string const & options) +/*! + * Split a package options string (keyval format) into a vector. + * Example input: + * authorformat=smallcaps, + * commabeforerest, + * titleformat=colonsep, + * bibformat={tabular,ibidem,numbered} + */ +vector split_options(string const & input) { + vector options; + string option; + Parser p(input); + while (p.good()) { + Token const & t = p.get_token(); + if (t.asInput() == ",") { + options.push_back(option); + option.erase(); + } else if (t.asInput() == "=") { + option += '='; + p.skip_spaces(true); + if (p.next_token().asInput() == "{") + option += '{' + p.getArg('{', '}') + '}'; + } else if (t.cat() != catSpace) + option += t.asInput(); + } + + if (!option.empty()) + options.push_back(option); + + return options; +} + + +/*! + * Add package \p name with options \p options to used_packages. + * Remove options from \p options that we don't want to output. + */ +void add_package(string const & name, vector & options) +{ + // every package inherits the global options + if (used_packages.find(name) == used_packages.end()) + used_packages[name] = split_options(h_options); + + vector & v = used_packages[name]; + v.insert(v.end(), options.begin(), options.end()); + if (name == "jurabib") { + // Don't output the order argument (see the cite command + // handling code in text.C). + vector::iterator end = + remove(options.begin(), options.end(), "natbiborder"); + end = remove(options.begin(), end, "jurabiborder"); + options.erase(end, options.end()); + } +} + + +void handle_package(string const & name, string const & opts) +{ + vector options = split_options(opts); + add_package(name, options); + //cerr << "handle_package: '" << name << "'\n"; if (name == "a4wide") { h_papersize = "a4paper"; @@ -121,21 +183,42 @@ void handle_package(string const & name, string const & options) ; // ignore this else if (name == "fontenc") ; // ignore this - else if (name == "inputenc") - h_inputencoding = options; - else if (name == "makeidx") + else if (name == "inputenc") { + h_inputencoding = opts; + options.clear(); + } else if (name == "makeidx") ; // ignore this else if (name == "verbatim") ; // ignore this else if (is_known(name, known_languages)) { h_language = name; h_quotes_language = name; - } else { - if (!options.empty()) - h_preamble << "\\usepackage[" << options << "]{" << name << "}\n"; - else - h_preamble << "\\usepackage{" << name << "}\n"; + } else if (name == "natbib") { + h_cite_engine = "natbib_authoryear"; + vector::iterator it = + find(options.begin(), options.end(), "authoryear"); + if (it != options.end()) + options.erase(it); + else { + it = find(options.begin(), options.end(), "numbers"); + if (it != options.end()) { + h_cite_engine = "natbib_numerical"; + options.erase(it); + } + } + } else if (name == "jurabib") { + h_cite_engine = "jurabib"; + } else if (options.empty()) + h_preamble << "\\usepackage{" << name << "}\n"; + else { + h_preamble << "\\usepackage[" << opts << "]{" << name << "}\n"; + options.clear(); } + + // We need to do something with the options... + if (!options.empty()) + cerr << "Ignoring options '" << join(options, ",") + << "' of package " << name << '.' << endl; } @@ -362,6 +445,17 @@ LyXTextClass const parse_preamble(Parser & p, ostream & os, string const & force h_preamble << "\\begin{" << name << "}"; } + else if (t.cs() == "jurabibsetup") { + vector jurabibsetup = + split_options(p.getArg('{', '}')); + // add jurabibsetup to the jurabib package options + add_package("jurabib", jurabibsetup); + if (!jurabibsetup.empty()) { + h_preamble << "\\jurabibsetup{" + << join(jurabibsetup, ",") << '}'; + } + } + else if (!t.cs().empty()) h_preamble << '\\' << t.cs(); } diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index f93bc0398e..650d9fcc75 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -26,6 +26,9 @@ class Context; /// in preamble.C LyXTextClass const parse_preamble(Parser & p, std::ostream & os, std::string const & forceclass); +/// used packages with options +extern std::map > used_packages; + /// in text.C void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer, diff --git a/src/tex2lyx/text.C b/src/tex2lyx/text.C index 37b553bdbc..4c369e4838 100644 --- a/src/tex2lyx/text.C +++ b/src/tex2lyx/text.C @@ -22,6 +22,8 @@ #include "support/tostr.h" #include "support/filetools.h" +#include + #include #include #include @@ -82,6 +84,29 @@ namespace { char const * const known_latex_commands[] = { "ref", "cite", "label", "index", "printindex", "pageref", "url", "vref", "vpageref", "prettyref", "eqref", 0 }; +/*! + * natbib commands. + * We can't put these into known_latex_commands because the argument order + * is reversed in lyx if there are 2 arguments. + * The starred forms are also known. + */ +char const * const known_natbib_commands[] = { "cite", "citet", "citep", +"citealt", "citealp", "citeauthor", "citeyear", "citeyearpar", +"citefullauthor", "Citet", "Citep", "Citealt", "Citealp", "Citeauthor", 0 }; + +/*! + * jurabib commands. + * We can't put these into known_latex_commands because the argument order + * is reversed in lyx if there are 2 arguments. + * No starred form other than "cite*" known. + */ +char const * const known_jurabib_commands[] = { "cite", "citet", "citep", +"citealt", "citealp", "citeauthor", "citeyear", "citeyearpar", "fullcite", +// jurabib commands not (yet) supported by LyX: +// "footcite", "footcitet", "footcitep", "footcitealt", "footcitealp", +// "footciteauthor", "footciteyear", "footciteyearpar", +"citefield", "citetitle", "cite*", 0 }; + /// LaTeX names for quotes char const * const known_quotes[] = { "glqq", "grqq", "quotedblbase", "textquotedblleft", "quotesinglbase", "guilsinglleft", "guilsinglright", 0}; @@ -821,6 +846,35 @@ void parse_text_attributes(Parser & p, ostream & os, unsigned flags, bool outer, os << '\n' << attribute << ' ' << oldvalue << " \n"; } + +/// get the arguments of a natbib or jurabib citation command +std::pair getCiteArguments(Parser & p, ostream & os, + Context & context, bool natbibOrder) +{ + // We need to distinguish "" and "[]", so we can't use p.getOpt(). + + // text before the citation + string before; + // text after the citation + string after; + + eat_whitespace(p, os, context, false); + if (p.next_token().asInput() == "[") { + after = '[' + p.getArg('[', ']') + ']'; + eat_whitespace(p, os, context, false); + if (natbibOrder) { + if (p.next_token().asInput() == "[") { + before = after; + after = '[' + p.getArg('[', ']') + ']'; + } + } else { + if (p.next_token().asInput() == "[") + before = '[' + p.getArg('[', ']') + ']'; + } + } + return std::make_pair(before, after); +} + } // anonymous namespace @@ -830,6 +884,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, LyXLayout_ptr newlayout; // Store the latest bibliographystyle (needed for bibtex inset) string bibliographystyle; + bool const use_natbib = used_packages.find("natbib") != used_packages.end(); + bool const use_jurabib = used_packages.find("jurabib") != used_packages.end(); while (p.good()) { Token const & t = p.get_token(); @@ -1402,7 +1458,98 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer, os << "\n\\" << t.cs() << " default \n"; } + else if (use_natbib && + is_known(t.cs(), known_natbib_commands) && + ((t.cs() != "citefullauthor" && + t.cs() != "citeyear" && + t.cs() != "citeyearpar") || + p.next_token().asInput() != "*")) { + context.check_layout(os); + // tex lyx + // \citet[before][after]{a} \citet[after][before]{a} + // \citet[before][]{a} \citet[][before]{a} + // \citet[after]{a} \citet[after]{a} + // \citet{a} \citet{a} + string command = '\\' + t.cs(); + if (p.next_token().asInput() == "*") { + command += '*'; + p.get_token(); + } + if (command == "\\citefullauthor") + // alternative name for "\\citeauthor*" + command = "\\citeauthor*"; + + // text before the citation + string before; + // text after the citation + string after; + + boost::tie(before, after) = + getCiteArguments(p, os, context, true); + if (command == "\\cite") { + // \cite without optional argument means + // \citet, \cite with at least one optional + // argument means \citep. + if (before.empty() && after.empty()) + command = "\\citet"; + else + command = "\\citep"; + } + if (before.empty() && after == "[]") + // avoid \citet[]{a} + after.erase(); + else if (before == "[]" && after == "[]") { + // avoid \citet[][]{a} + before.erase(); + after.erase(); + } + begin_inset(os, "LatexCommand "); + os << command << after << before + << '{' << p.verbatim_item() << "}\n"; + end_inset(os); + } + + else if (use_jurabib && + is_known(t.cs(), known_jurabib_commands)) { + context.check_layout(os); + string const command = '\\' + t.cs(); + char argumentOrder = '\0'; + vector const & options = used_packages["jurabib"]; + if (std::find(options.begin(), options.end(), + "natbiborder") != options.end()) + argumentOrder = 'n'; + else if (std::find(options.begin(), options.end(), + "jurabiborder") != options.end()) + argumentOrder = 'j'; + + // text before the citation + string before; + // text after the citation + string after; + + boost::tie(before, after) = + getCiteArguments(p, os, context, + argumentOrder != 'j'); + string const citation = p.verbatim_item(); + if (!before.empty() && argumentOrder == '\0') { + cerr << "Warning: Assuming argument order " + << "of jurabib version 0.6 for\n'" + << command << before << after << '{' + << citation << "}'.\n" + << "Add 'jurabiborder' to the jurabib " + << "package options if you used an\n" + << "earlier jurabib version." << endl; + } + begin_inset(os, "LatexCommand "); + os << command << after << before + << '{' << citation << "}\n"; + end_inset(os); + } + else if (is_known(t.cs(), known_latex_commands)) { + // This needs to be after the check for natbib and + // jurabib commands, because "cite" has different + // arguments with natbib and jurabib. context.check_layout(os); begin_inset(os, "LatexCommand "); os << '\\' << t.cs();