From 78a7743166f539861cdd0465d98a4c5d3f40ac5c Mon Sep 17 00:00:00 2001 From: Georg Baum Date: Fri, 26 Oct 2012 22:23:16 +0200 Subject: [PATCH] Import theorems correctly (bugs #5702, #5776) Actually tex2lyx can handle modules since some time (#5702), but not theorems (#5776). Now the following issues are fixed: - Modules that depend on other modules can be loaded, since the dependencies are loaded first - Default moduls of the text class are loaded correctly - \newtheorem is recognized as a command that defines new environments and treated similar to \newenvironment --- src/LayoutModuleList.cpp | 49 ++++---- src/LayoutModuleList.h | 5 +- src/tex2lyx/Makefile.am | 1 + src/tex2lyx/Preamble.cpp | 16 +++ src/tex2lyx/test/CMakeLists.txt | 2 +- src/tex2lyx/test/runtests.py | 4 +- src/tex2lyx/test/test-modules.lyx.lyx | 156 ++++++++++++++++++++++++++ src/tex2lyx/test/test-modules.tex | 43 +++++++ src/tex2lyx/tex2lyx.cpp | 105 +++++++++++++++-- src/tex2lyx/tex2lyx.h | 4 + 10 files changed, 355 insertions(+), 30 deletions(-) create mode 100644 src/tex2lyx/test/test-modules.lyx.lyx create mode 100644 src/tex2lyx/test/test-modules.tex diff --git a/src/LayoutModuleList.cpp b/src/LayoutModuleList.cpp index a6507212a8..d532b2d142 100644 --- a/src/LayoutModuleList.cpp +++ b/src/LayoutModuleList.cpp @@ -67,10 +67,8 @@ bool LayoutModuleList::moduleCanBeAdded(string const & modName, if (!lm) return true; - // Is this module explicitly excluded by the document class? - const_iterator const exclmodstart = lay->excludedModules().begin(); - const_iterator const exclmodend = lay->excludedModules().end(); - if (find(exclmodstart, exclmodend, modName) != exclmodend) + // Does this module conflict with the document class or any loaded modules? + if (moduleConflicts(modName, lay)) return false; // Is this module already provided by the document class? @@ -79,26 +77,13 @@ bool LayoutModuleList::moduleCanBeAdded(string const & modName, if (find(provmodstart, provmodend, modName) != provmodend) return false; - // Check for conflicts with used modules - // first the provided modules... - const_iterator provmodit = provmodstart; - for (; provmodit != provmodend; ++provmodit) { - if (!LyXModule::areCompatible(modName, *provmodit)) - return false; - } - // and then the selected modules - const_iterator mit = begin(); - const_iterator const men = end(); - for (; mit != men; ++mit) - if (!LyXModule::areCompatible(modName, *mit)) - return false; - // Check whether some required module is available vector const reqs = lm->getRequiredModules(); if (reqs.empty()) return true; - mit = begin(); // reset + const_iterator mit = begin(); + const_iterator const men = end(); vector::const_iterator rit = reqs.begin(); vector::const_iterator ren = reqs.end(); bool foundone = false; @@ -114,6 +99,32 @@ bool LayoutModuleList::moduleCanBeAdded(string const & modName, } +bool LayoutModuleList::moduleConflicts(string const & modName, + LayoutFile const * const lay) const +{ + // Is this module explicitly excluded by the document class? + const_iterator const exclmodstart = lay->excludedModules().begin(); + const_iterator const exclmodend = lay->excludedModules().end(); + if (find(exclmodstart, exclmodend, modName) != exclmodend) + return true; + // Check for conflicts with used modules + // first the provided modules... + const_iterator provmodit = lay->providedModules().begin(); + const_iterator const provmodend = lay->providedModules().end(); + for (; provmodit != provmodend; ++provmodit) { + if (!LyXModule::areCompatible(modName, *provmodit)) + return true; + } + // and then the selected modules + const_iterator mit = begin(); + const_iterator const men = end(); + for (; mit != men; ++mit) + if (!LyXModule::areCompatible(modName, *mit)) + return true; + return false; +} + + void LayoutModuleList::addDefaultModules(LayoutFile const * const lay, std::list removedModules) { diff --git a/src/LayoutModuleList.h b/src/LayoutModuleList.h index 245b6a7e69..f5741dede1 100644 --- a/src/LayoutModuleList.h +++ b/src/LayoutModuleList.h @@ -57,7 +57,10 @@ public: std::list const & list() const { return lml_; } /// Checks to make sure module's requriements are satisfied, that it does /// not conflict with already-present modules, isn't already loaded, etc. - bool moduleCanBeAdded(std::string const & modName, + bool moduleCanBeAdded(std::string const & modName, + LayoutFile const * const lay) const; + /// Like !moduleCanBeAdded(), but does not check requirements + bool moduleConflicts(std::string const & modName, LayoutFile const * const lay) const; /// If the user changes the base class for a given document, then the /// associated module list has to be updated. This just calls diff --git a/src/tex2lyx/Makefile.am b/src/tex2lyx/Makefile.am index b0be0afd96..1c296a725f 100644 --- a/src/tex2lyx/Makefile.am +++ b/src/tex2lyx/Makefile.am @@ -28,6 +28,7 @@ TEST_FILES = \ test/foo.png \ test/test-insets.tex \ test/test.ltx \ + test/test-modules.tex \ test/test-structure.tex \ test/XeTeX-polyglossia.tex \ test/xfigtest.fig \ diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 430708c0fc..a567426a68 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -1428,6 +1428,22 @@ void Preamble::parse(Parser & p, string const & forceclass, } + else if (t.cs() == "newtheorem") { + string const name = p.getArg('{', '}'); + string const opt1 = p.getFullOpt(); + string const opt2 = p.getFullOpt(); + string const body = p.verbatim_item(); + string const opt3 = p.getFullOpt(); + + add_known_theorem(name, opt1, !opt2.empty(), + from_utf8("\\newtheorem{" + name + '}' + + opt1 + opt2 + '{' + body + '}' + opt3)); + + if (!in_lyx_preamble) + h_preamble << "\\newtheorem{" << name << '}' + << opt1 << opt2 << '{' << '}' << opt3; + } + else if (t.cs() == "def") { string name = p.get_token().cs(); // In fact, name may be more than the name: diff --git a/src/tex2lyx/test/CMakeLists.txt b/src/tex2lyx/test/CMakeLists.txt index 365d953f69..5e1cd31cf3 100644 --- a/src/tex2lyx/test/CMakeLists.txt +++ b/src/tex2lyx/test/CMakeLists.txt @@ -9,7 +9,7 @@ project(test) set(_test_depend "test.ltx") set(_test_output) -foreach(_arg test-structure test-insets box-color-size-space-align CJK XeTeX-polyglossia) +foreach(_arg test-structure test-insets test-modules box-color-size-space-align CJK XeTeX-polyglossia) list(APPEND _test_depend "${TOP_SRC_DIR}/src/tex2lyx/test/${_arg}.tex") list(APPEND _test_output "${CMAKE_CURRENT_BINARY_DIR}/${_arg}.lyx.tex") list(APPEND _test_output "${CMAKE_CURRENT_BINARY_DIR}/${_arg}.lyx.lyx") diff --git a/src/tex2lyx/test/runtests.py b/src/tex2lyx/test/runtests.py index 094f5d6c0a..13d62f67ab 100755 --- a/src/tex2lyx/test/runtests.py +++ b/src/tex2lyx/test/runtests.py @@ -46,8 +46,8 @@ def main(argv): outputdir = os.path.join(os.path.dirname(tex2lyx), "test") files = ['test.ltx', 'test-structure.tex', 'test-insets.tex', \ - 'box-color-size-space-align.tex', 'CJK.tex', \ - 'XeTeX-polyglossia.tex'] + 'test-modules.tex', 'box-color-size-space-align.tex', \ + 'CJK.tex', 'XeTeX-polyglossia.tex'] errors = [] overwrite = (outputdir == inputdir) diff --git a/src/tex2lyx/test/test-modules.lyx.lyx b/src/tex2lyx/test/test-modules.lyx.lyx new file mode 100644 index 0000000000..89297fd6dd --- /dev/null +++ b/src/tex2lyx/test/test-modules.lyx.lyx @@ -0,0 +1,156 @@ +#LyX file created by tex2lyx 2.1.0dev +\lyxformat 445 +\begin_document +\begin_header +\textclass amsart +\begin_preamble +\usepackage{babel} + +\end_preamble +\use_default_options false +\begin_modules +theorems-ams +\end_modules +\maintain_unincluded_children false +\language english +\language_package default +\inputencoding latin9 +\fontencoding T1 +\font_roman default +\font_sans default +\font_typewriter default +\font_math auto +\font_default_family default +\use_non_tex_fonts false +\font_sc false +\font_osf false +\font_sf_scale 100 +\font_tt_scale 100 +\graphics default +\default_output_format default +\output_sync 0 +\bibtex_command default +\index_command default +\paperfontsize default +\spacing single +\use_hyperref false +\papersize default +\use_geometry false +\use_package amsmath 1 +\use_package amssymb 0 +\use_package esint 1 +\use_package mathdots 0 +\use_package mathtools 0 +\use_package mhchem 0 +\use_package undertilde 0 +\cite_engine basic +\cite_engine_type numerical +\biblio_style plain +\use_bibtopic false +\use_indices false +\paperorientation portrait +\suppress_date false +\justification true +\use_refstyle 0 +\index Index +\shortcut idx +\color #008000 +\end_index +\secnumdepth 3 +\tocdepth 3 +\paragraph_separation indent +\paragraph_indentation default +\quotes_language english +\papercolumns 1 +\papersides 1 +\paperpagestyle default +\tracking_changes false +\output_changes false +\html_math_output 0 +\html_css_as_file 0 +\html_be_strict false +\end_header + +\begin_body + +\begin_layout Standard + +This is a dummy file +\end_layout + +\begin_layout Standard + +It has a theorem, a lemma and a proof. +\end_layout + +\begin_layout Standard + +The theorem is recognized is a style provided by the module theorems-ams, since the preamble code matches. +\end_layout + +\begin_layout Standard + +The lemma is not recognized as a command provided by a module, since the preamble code is from an older version of +\begin_inset ERT +status collapsed + +\begin_layout Standard + +LyX +\end_layout + +\end_inset + +, and modules are only loaded if the preamble code matches (otherwise you could easily get completely different output for some often used names like +\backslash +theorem. +\end_layout + +\begin_layout Standard + +The proof is recognized as a builtin style provided by the text class. +\end_layout + +\begin_layout Standard + + +\begin_inset ERT +status collapsed + +\begin_layout Standard + + +\backslash +begin{lem} +\end_layout + +\end_inset + + this is a lemma +\begin_inset ERT +status collapsed + +\begin_layout Standard + + +\backslash +end{lem} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Theorem + +this is the theorem +\end_layout + +\begin_layout Proof + +this is the proof +\end_layout + +\end_body +\end_document diff --git a/src/tex2lyx/test/test-modules.tex b/src/tex2lyx/test/test-modules.tex new file mode 100644 index 0000000000..b27ada9053 --- /dev/null +++ b/src/tex2lyx/test/test-modules.tex @@ -0,0 +1,43 @@ +%% LyX 1.6.1 created this file. For more info, see http://www.lyx.org/. +%% Do not edit unless you really know what you are doing. +\documentclass[oneside,english]{amsart} +\usepackage[T1]{fontenc} +\usepackage[latin9]{inputenc} +\usepackage{amsthm} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands. +\numberwithin{equation}{section} %% Comment out for sequentially-numbered +\numberwithin{figure}{section} %% Comment out for sequentially-numbered +\providecommand{\theoremname}{Theorem} +\theoremstyle{plain} +\theoremstyle{plain} +\newtheorem{thm}{\protect\theoremname} + \theoremstyle{plain} + \newtheorem{lem}[thm]{Lemma} + +\usepackage{babel} + +\begin{document} +This is a dummy file + +It has a theorem, a lemma and a proof. + +The theorem is recognized is a style provided by the module theorems-ams, +since the preamble code matches. + +The lemma is not recognized as a command provided by a module, since the +preamble code is from an older version of LyX, and modules are only loaded +if the preamble code matches (otherwise you could easily get completely +different output for some often used names like \textbackslash theorem. + +The proof is recognized as a builtin style provided by the text class. + +\begin{lem} +this is a lemma\end{lem} +\begin{thm} +this is the theorem\end{thm} +\begin{proof} +this is the proof +\end{proof} + +\end{document} diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index 7561774633..0e6afd9f51 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -36,6 +36,7 @@ #include "support/Systemcall.h" #include +#include #include #include #include @@ -150,6 +151,7 @@ CommandMap known_environments; CommandMap known_math_environments; FullCommandMap possible_textclass_commands; FullEnvironmentMap possible_textclass_environments; +FullCommandMap possible_textclass_theorems; int const LYX_FORMAT = LYX_FORMAT_TEX2LYX; /// used modules @@ -206,6 +208,17 @@ void add_known_environment(string const & environment, string const & o1, } +void add_known_theorem(string const & theorem, string const & o1, + bool o2, docstring const & definition) +{ + vector arguments; + convertArgs(o1, o2, arguments); + if (!definition.empty()) + possible_textclass_theorems[theorem] = + FullCommand(arguments, definition); +} + + Layout const * findLayoutWithoutModule(TextClass const & textclass, string const & name, bool command) { @@ -239,6 +252,69 @@ namespace { typedef map ModuleMap; ModuleMap modules; + + +bool addModule(string const module, LayoutFile const & baseClass, LayoutModuleList & m, vector & visited) +{ + // avoid endless loop for circular dependency + vector::const_iterator const vb = visited.begin(); + vector::const_iterator const ve = visited.end(); + if (find(vb, ve, module) != ve) { + cerr << "Circular dependency detected for module " << module << '\n'; + return false; + } + LyXModule const * const lm = theModuleList[module]; + if (!lm) { + cerr << "Could not find module " << module << " in module list.\n"; + return false; + } + bool foundone = false; + LayoutModuleList::const_iterator const exclmodstart = baseClass.excludedModules().begin(); + LayoutModuleList::const_iterator const exclmodend = baseClass.excludedModules().end(); + LayoutModuleList::const_iterator const provmodstart = baseClass.providedModules().begin(); + LayoutModuleList::const_iterator const provmodend = baseClass.providedModules().end(); + vector const reqs = lm->getRequiredModules(); + if (reqs.empty()) + foundone = true; + else { + LayoutModuleList::const_iterator mit = m.begin(); + LayoutModuleList::const_iterator men = m.end(); + vector::const_iterator rit = reqs.begin(); + vector::const_iterator ren = reqs.end(); + for (; rit != ren; ++rit) { + if (find(mit, men, *rit) != men) { + foundone = true; + break; + } + if (find(provmodstart, provmodend, *rit) != provmodend) { + foundone = true; + break; + } + } + if (!foundone) { + visited.push_back(module); + for (rit = reqs.begin(); rit != ren; ++rit) { + if (find(exclmodstart, exclmodend, *rit) == exclmodend) { + if (addModule(*rit, baseClass, m, visited)) { + foundone = true; + break; + } + } + } + visited.pop_back(); + } + } + if (!foundone) { + cerr << "Could not add required modules for " << module << ".\n"; + return false; + } + if (!m.moduleCanBeAdded(module, &baseClass)) + return false; + m.push_back(module); + return true; +} + + void initModules() { // Create list of dummy document classes if not already done. @@ -253,10 +329,9 @@ void initModules() for (; it != end; ++it) { string const module = it->getID(); LayoutModuleList m; - // FIXME this excludes all modules that depend on another one - if (!m.moduleCanBeAdded(module, &baseClass)) + vector v; + if (!addModule(module, baseClass, m, v)) continue; - m.push_back(module); modules[module] = getDocumentClass(baseClass, m); } init = false; @@ -292,12 +367,17 @@ bool checkModule(string const & name, bool command) static set failed[2]; // Only add the module if the command was actually defined in the LyX preamble + bool theorem = false; if (command) { if (possible_textclass_commands.find('\\' + name) == possible_textclass_commands.end()) return false; } else { - if (possible_textclass_environments.find(name) == possible_textclass_environments.end()) - return false; + if (possible_textclass_environments.find(name) == possible_textclass_environments.end()) { + if (possible_textclass_theorems.find(name) != possible_textclass_theorems.end()) + theorem = true; + else + return false; + } } if (failed[command].find(name) != failed[command].end()) return false; @@ -315,7 +395,7 @@ bool checkModule(string const & name, bool command) ModuleMap::iterator const end = modules.end(); for (ModuleMap::iterator it = modules.begin(); it != end; ++it) { string const module = it->first; - if (!used_modules.moduleCanBeAdded(module, &baseClass)) + if (used_modules.moduleConflicts(module, &baseClass)) continue; if (findLayoutWithoutModule(textclass, name, command)) continue; @@ -338,6 +418,11 @@ bool checkModule(string const & name, bool command) possible_textclass_commands['\\' + name]; if (preamble.find(cmd.def) != docstring::npos) add = true; + } else if (theorem) { + FullCommand const & thm = + possible_textclass_theorems[name]; + if (preamble.find(thm.def) != docstring::npos) + add = true; } else { FullEnvironment const & env = possible_textclass_environments[name]; @@ -756,6 +841,12 @@ bool tex2lyx(idocstream & is, ostream & os, string encoding) //p.dump(); preamble.parse(p, documentclass, textclass); + list removed_modules; + LayoutFile const & baseClass = LayoutFileList::get()[textclass.name()]; + if (!used_modules.adaptToBaseClass(&baseClass, removed_modules)) { + cerr << "Could not load default modules for text class." << endl; + return false; + } // Load preloaded modules. // This needs to be done after the preamble is parsed, since the text @@ -795,7 +886,7 @@ bool tex2lyx(idocstream & is, ostream & os, string encoding) preamble.addModule(*it); } if (!preamble.writeLyXHeader(os, !active_environments.empty())) { - cerr << "Could write LyX file header." << endl; + cerr << "Could not write LyX file header." << endl; return false; } diff --git a/src/tex2lyx/tex2lyx.h b/src/tex2lyx/tex2lyx.h index 37585fddf4..e3da48b478 100644 --- a/src/tex2lyx/tex2lyx.h +++ b/src/tex2lyx/tex2lyx.h @@ -106,6 +106,8 @@ void add_known_command(std::string const & command, std::string const & o1, extern void add_known_environment(std::string const & environment, std::string const & o1, bool o2, docstring const & beg, docstring const & end); +extern void add_known_theorem(std::string const & theorem, + std::string const & o1, bool o2, docstring const & definition); extern Layout const * findLayoutWithoutModule(TextClass const & textclass, std::string const & name, bool command); extern InsetLayout const * findInsetLayoutWithoutModule( @@ -164,6 +166,8 @@ extern CommandMap known_math_environments; extern FullCommandMap possible_textclass_commands; /// Environments that might be defined by the document class or modules extern FullEnvironmentMap possible_textclass_environments; +/// Theorems that might be defined by the document class or modules +extern FullCommandMap possible_textclass_theorems; /// extern bool noweb_mode; /// Did we recognize any pdflatex-only construct?