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
This commit is contained in:
Georg Baum 2012-10-26 22:23:16 +02:00
parent 1094128fe1
commit 78a7743166
10 changed files with 355 additions and 30 deletions

View File

@ -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<string> const reqs = lm->getRequiredModules();
if (reqs.empty())
return true;
mit = begin(); // reset
const_iterator mit = begin();
const_iterator const men = end();
vector<string>::const_iterator rit = reqs.begin();
vector<string>::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<string> removedModules)
{

View File

@ -57,7 +57,10 @@ public:
std::list<std::string> 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

View File

@ -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 \

View File

@ -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:

View File

@ -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")

View File

@ -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)

View File

@ -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

View File

@ -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}

View File

@ -36,6 +36,7 @@
#include "support/Systemcall.h"
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
@ -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<ArgumentType> 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<string, DocumentClassPtr> ModuleMap;
ModuleMap modules;
bool addModule(string const module, LayoutFile const & baseClass, LayoutModuleList & m, vector<string> & visited)
{
// avoid endless loop for circular dependency
vector<string>::const_iterator const vb = visited.begin();
vector<string>::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<string> const reqs = lm->getRequiredModules();
if (reqs.empty())
foundone = true;
else {
LayoutModuleList::const_iterator mit = m.begin();
LayoutModuleList::const_iterator men = m.end();
vector<string>::const_iterator rit = reqs.begin();
vector<string>::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<string> 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<string> 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<string> 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;
}

View File

@ -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?