commit 0b54650f0e7f1eae39f93444cac6c8525811975b
Author: Juergen Spitzmueller <[email protected]>
Date: Thu Apr 4 15:46:49 2019 +0200
tex2lyx: improve module support
The current heuristics only considered modules with styles that defined
a searched command in their preamble, and only for commands/environments
that were defined in the document's preamble. This limited the module
support drastically.
The new heuristics also checks for commands coming from packages. If the
command is not (re-)defined in the document preamble, it checks modules
that provide a style with a matching LaTeXName, checks for their
requirements and matches those with the packages loaded by the document.
If no module provides a searched style, but we found modules that load
packages that are loaded in the imported tex file, and if those packages
are not auto-loaded by LyX anyway, we also load this module.
fixes: #11259, part of #8229
---
src/tex2lyx/Preamble.cpp | 14 +++-
src/tex2lyx/Preamble.h | 2 +
src/tex2lyx/TODO.txt | 2 -
src/tex2lyx/test/test-modules.lyx.lyx | 28 +------
src/tex2lyx/test/test-structure.lyx.lyx | 1 +
src/tex2lyx/tex2lyx.cpp | 148 +++++++++++++++++++++----------
6 files changed, 119 insertions(+), 76 deletions(-)
diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp
index b0ac779..3929885 100644
--- a/src/tex2lyx/Preamble.cpp
+++ b/src/tex2lyx/Preamble.cpp
@@ -373,6 +373,12 @@ bool Preamble::isPackageUsed(string const & package) const
}
+bool Preamble::isPackageAutoLoaded(string const & package) const
+{
+ return auto_packages.find(package) != auto_packages.end();
+}
+
+
vector<string> Preamble::getPackageOptions(string const & package) const
{
map<string, vector<string> >::const_iterator it =
used_packages.find(package);
@@ -390,6 +396,10 @@ void
Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
void Preamble::addModule(string const & module)
{
+ for (auto const & m : used_modules) {
+ if (m == module)
+ return;
+ }
used_modules.push_back(module);
}
@@ -998,8 +1008,10 @@ void Preamble::handle_package(Parser &p, string const &
name,
else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
name == "esint" || name == "mhchem" || name == "mathdots" ||
name == "mathtools" || name == "stackrel" ||
- name == "stmaryrd" || name == "undertilde")
+ name == "stmaryrd" || name == "undertilde") {
h_use_packages[name] = "2";
+ registerAutomaticallyLoadedPackage(name);
+ }
else if (name == "babel") {
h_language_package = "default";
diff --git a/src/tex2lyx/Preamble.h b/src/tex2lyx/Preamble.h
index 63b812b..6c11d88 100644
--- a/src/tex2lyx/Preamble.h
+++ b/src/tex2lyx/Preamble.h
@@ -67,6 +67,8 @@ public:
///
bool isPackageUsed(std::string const & package) const;
///
+ bool isPackageAutoLoaded(std::string const & package) const;
+ ///
std::vector<std::string>
getPackageOptions(std::string const & package) const;
/// Tell that \p package will be loaded automatically by LyX.
diff --git a/src/tex2lyx/TODO.txt b/src/tex2lyx/TODO.txt
index 1706ef4..d1760ac 100644
--- a/src/tex2lyx/TODO.txt
+++ b/src/tex2lyx/TODO.txt
@@ -39,8 +39,6 @@ Format LaTeX feature LyX feature
General
-* Consider layouts in modules that do not have a preamble definition (see
#11259).
-
* Use the language information provided by Language.cpp and the languages file
(for babel/lyx/polyglossia name, quote style etc.)
instead of hardcoding this information in Preamble.cpp.
diff --git a/src/tex2lyx/test/test-modules.lyx.lyx
b/src/tex2lyx/test/test-modules.lyx.lyx
index bd85449..ea45609 100644
--- a/src/tex2lyx/test/test-modules.lyx.lyx
+++ b/src/tex2lyx/test/test-modules.lyx.lyx
@@ -106,32 +106,8 @@ theorem.
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 Plain Layout
-
-\backslash
-begin{lem}
-\end_layout
-
-\end_inset
-
- this is a lemma
-\begin_inset ERT
-status collapsed
-
-\begin_layout Plain Layout
-
-\backslash
-end{lem}
-\end_layout
-
-\end_inset
-
-
+\begin_layout Lemma
+this is a lemma
\end_layout
\begin_layout Theorem
diff --git a/src/tex2lyx/test/test-structure.lyx.lyx
b/src/tex2lyx/test/test-structure.lyx.lyx
index b148b1c..6c4e9c7 100644
--- a/src/tex2lyx/test/test-structure.lyx.lyx
+++ b/src/tex2lyx/test/test-structure.lyx.lyx
@@ -43,6 +43,7 @@
\options dummyoption
\use_default_options false
\begin_modules
+fixltx2e
logicalmkup
\end_modules
\maintain_unincluded_children false
diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp
index 6437d99..0333034 100644
--- a/src/tex2lyx/tex2lyx.cpp
+++ b/src/tex2lyx/tex2lyx.cpp
@@ -321,17 +321,18 @@ bool checkModule(string const & name, bool command)
// Cache to avoid slowdown by repated searches
static set<string> failed[2];
- // Only add the module if the command was actually defined in the LyX
preamble
+ // Record whether the command was actually defined in the LyX preamble
bool theorem = false;
+ bool preamble_def = true;
if (command) {
if (possible_textclass_commands.find('\\' + name) ==
possible_textclass_commands.end())
- return false;
+ preamble_def = false;
} else {
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;
+ preamble_def = false;
}
}
if (failed[command].find(name) != failed[command].end())
@@ -341,12 +342,16 @@ bool checkModule(string const & name, bool command)
LayoutFile const & baseClass = LayoutFileList::get()[textclass.name()];
// Try to find a module that defines the command.
- // Only add it if the definition can be found in the preamble of the
- // style that corresponds to the command. This is a heuristic and
- // different from the way how we parse the builtin commands of the
- // text class (in that case we only compare the name), but it is
- // needed since it is not unlikely that two different modules define a
+ // For commands with preamble definitions we prefer modules where the
definition
+ // can be found in the preamble of the style that corresponds to the
command.
+ // For others we check whether the command or module requires a package
that is loaded
+ // in the tex file and use a style with the respective command.
+ // This is a heuristic and different from the way how we parse the
builtin
+ // commands of the text class (in that case we only compare the name),
+ // but it is needed since it is not unlikely that two different modules
define a
// command with the same name.
+ string found_module;
+ vector<string> potential_modules;
ModuleMap::iterator const end = modules.end();
for (ModuleMap::iterator it = modules.begin(); it != end; ++it) {
string const module = it->first;
@@ -358,53 +363,102 @@ bool checkModule(string const & name, bool command)
continue;
DocumentClassConstPtr c = it->second;
Layout const * layout = findLayoutWithoutModule(*c, name,
command);
- InsetLayout const * insetlayout = layout ? 0 :
+ InsetLayout const * insetlayout = layout ? nullptr :
findInsetLayoutWithoutModule(*c, name, command);
docstring dpre;
- if (layout)
+ std::set<std::string> cmd_reqs;
+ bool found_style = false;
+ if (layout) {
+ found_style = true;
dpre = layout->preamble();
- else if (insetlayout)
+ std::set<std::string> lreqs = layout->requires();
+ if (!lreqs.empty())
+ cmd_reqs.insert(lreqs.begin(), lreqs.end());
+ } else if (insetlayout) {
+ found_style = true;
dpre = insetlayout->preamble();
- if (dpre.empty())
+ std::set<std::string> lreqs = insetlayout->requires();
+ if (!lreqs.empty())
+ cmd_reqs.insert(lreqs.begin(), lreqs.end());
+ }
+ if (dpre.empty() && preamble_def)
continue;
- bool add = false;
- if (command) {
- FullCommand const & cmd =
- possible_textclass_commands['\\' + name];
- if (dpre.find(cmd.def) != docstring::npos)
- add = true;
- } else if (theorem) {
- FullCommand const & thm =
- possible_textclass_theorems[name];
- if (dpre.find(thm.def) != docstring::npos)
- add = true;
- } else {
- FullEnvironment const & env =
- possible_textclass_environments[name];
- if (dpre.find(env.beg) != docstring::npos &&
- dpre.find(env.end) != docstring::npos)
- add = true;
+ bool const package_cmd = dpre.empty();
+ bool match_req = false;
+ if (package_cmd) {
+ std::set<std::string> mreqs = it->second->requires();
+ if (!mreqs.empty())
+ cmd_reqs.insert(mreqs.begin(), mreqs.end());
+ for (auto const & pack : cmd_reqs) {
+ // If a requirement of the module matches a
used package
+ // we load the module except if we have an
auto-loaded package
+ // which is only required generally by the
module, and the module
+ // does not provide the [inset]layout we are
looking for.
+ // This heuristics should
+ // * load modules if the provide a style we
don't have in the class
+ // * load modules that provide a package
support generally (such as fixltx2e)
+ // * not unnecessarily load modules just
because they require a package which we
+ // load anyway.
+ if (preamble.isPackageUsed(pack)
+ && (found_style ||
!preamble.isPackageAutoLoaded(pack))) {
+ if (found_style)
+ match_req = true;
+ else
+ potential_modules.push_back(module);
+ break;
+ }
+ }
+ }
+ bool add = match_req;
+ if (preamble_def) {
+ if (command) {
+ FullCommand const & cmd =
+ possible_textclass_commands['\\' +
name];
+ if (dpre.find(cmd.def) != docstring::npos)
+ add = true;
+ } else if (theorem) {
+ FullCommand const & thm =
+ possible_textclass_theorems[name];
+ if (dpre.find(thm.def) != docstring::npos)
+ add = true;
+ } else {
+ FullEnvironment const & env =
+ possible_textclass_environments[name];
+ if (dpre.find(env.beg) != docstring::npos &&
+ dpre.find(env.end) != docstring::npos)
+ add = true;
+ }
}
if (add) {
- vector<string> v;
- LayoutModuleList mods;
- // addModule is necessary in order to catch required
modules
- // as well (see #11156)
- if (!addModule(module, baseClass, mods, v))
+ found_module = module;
+ break;
+ }
+ }
+ if (found_module.empty()) {
+ // take one of the second row
+ if (!potential_modules.empty())
+ found_module = potential_modules.front();
+ }
+
+ if (!found_module.empty()) {
+ vector<string> v;
+ LayoutModuleList mods;
+ // addModule is necessary in order to catch required modules
+ // as well (see #11156)
+ if (!addModule(found_module, baseClass, mods, v))
+ return false;
+ for (auto const & mod : mods) {
+ if (!used_modules.moduleCanBeAdded(mod, &baseClass))
return false;
- for (auto const & mod : mods) {
- if (!used_modules.moduleCanBeAdded(mod,
&baseClass))
- return false;
- FileName layout_file = libFileSearch("layouts",
mod, "module");
- if (textclass.read(layout_file,
TextClass::MODULE)) {
- used_modules.push_back(mod);
- // speed up further searches:
- // the module does not need to be
checked anymore.
- ModuleMap::iterator const it =
modules.find(mod);
- if (it != modules.end())
- modules.erase(it);
- return true;
- }
+ FileName layout_file = libFileSearch("layouts", mod,
"module");
+ if (textclass.read(layout_file, TextClass::MODULE)) {
+ used_modules.push_back(mod);
+ // speed up further searches:
+ // the module does not need to be checked
anymore.
+ ModuleMap::iterator const it =
modules.find(mod);
+ if (it != modules.end())
+ modules.erase(it);
+ return true;
}
}
}