The attached patch is supposed to address bug #6912 by slightly
generalizing our argument handling again. In order to avoid including
<vector> in Layout.h, I've started here the process of pimpling a few
things. Unfortunately, something I'm doing is causing LyX to crash in
Argument::~Argument(), in particular, when it tries to delete the vector
that is the sole member of that class. So LyX crashes when you try to
load a document or create a new one. Can someone tell me what I've done
wrong?
Thanks,
Richard
Index: src/Layout.cpp
===================================================================
--- src/Layout.cpp (revision 35492)
+++ src/Layout.cpp (working copy)
@@ -20,12 +20,15 @@
#include "TextClass.h"
#include "support/debug.h"
+#include "support/docstring.h"
#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/Messages.h"
#include "support/regex.h"
+#include <vector>
+
using namespace std;
using namespace lyx::support;
@@ -73,7 +76,6 @@
LT_ENDLABELTYPE,
LT_LATEXNAME,
LT_LATEXPARAM,
- LT_OPTARGS,
LT_LATEXTYPE,
LT_LEFTMARGIN,
LT_NEED_PROTECT,
@@ -108,7 +110,7 @@
LT_HTMLTITLE,
LT_SPELLCHECK,
LT_REFPREFIX,
- LT_REQARGS,
+ LT_ARGUMENTS,
LT_INTITLE // keep this last!
};
@@ -152,8 +154,6 @@
htmlforcecss_ = false;
htmltitle_ = false;
spellcheck = true;
- optargs = 0;
- reqargs = 0;
}
@@ -163,6 +163,7 @@
LexerKeyword layoutTags[] = {
{ "align", LT_ALIGN },
{ "alignpossible", LT_ALIGNPOSSIBLE },
+ { "arguments", LT_ARGUMENTS },
{ "babelpreamble", LT_BABELPREAMBLE },
{ "bottomsep", LT_BOTTOMSEP },
{ "category", LT_CATEGORY },
@@ -212,7 +213,6 @@
{ "newline", LT_NEWLINE },
{ "nextnoindent", LT_NEXTNOINDENT },
{ "obsoletedby", LT_OBSOLETEDBY },
- { "optionalargs", LT_OPTARGS },
{ "parbreakisnewline", LT_PARBREAK_IS_NEWLINE },
{ "parindent", LT_PARINDENT },
{ "parsep", LT_PARSEP },
@@ -220,7 +220,6 @@
{ "passthru", LT_PASS_THRU },
{ "preamble", LT_PREAMBLE },
{ "refprefix", LT_REFPREFIX },
- { "requiredargs", LT_REQARGS },
{ "requires", LT_REQUIRES },
{ "rightmargin", LT_RIGHTMARGIN },
{ "spacing", LT_SPACING },
@@ -326,14 +325,17 @@
lex >> toclevel;
break;
- case LT_OPTARGS:
- lex >> optargs;
+ case LT_ARGUMENTS: {
+ docstring tmp;
+ lex >> tmp;
+ tmp = lowercase(tmp);
+ if (tmp == from_ascii("none"))
+ arguments_.clear();
+ else
+ arguments_.setArguments(to_utf8(tmp));
break;
+ }
- case LT_REQARGS:
- lex >> reqargs;
- break;
-
case LT_NEED_PROTECT:
lex >> needprotect;
break;
@@ -1093,4 +1095,72 @@
}
+// The point of doing this right now, as simple as it is, is to
+// keep from having to include <vector> in Layout.cpp. It also will
+// set up more flexibility for later.
+class Arguments::Impl {
+public:
+ vector<LaTeXArgumentType> args;
+};
+
+
+Arguments::Arguments() : d_(new Impl())
+{}
+
+
+Arguments::~Arguments()
+{
+ delete d_;
+}
+
+
+Arguments::Arguments(string const & s) : d_(new Impl())
+{
+ setArguments(s);
+}
+
+
+bool Arguments::empty() const
+{
+ return d_->args.empty();
+}
+
+
+int Arguments::numargs() const
+{
+ return d_->args.size();
+}
+
+
+LaTeXArgumentType Arguments::operator[](int i) const
+{
+ LASSERT(i < numargs(), return NO_ARG);
+ return d_->args[i];
+}
+
+
+void Arguments::clear()
+{
+ d_->args.clear();
+}
+
+
+void Arguments::setArguments(const std::string & s)
+{
+ d_->args.clear();
+ string tmp = ascii_lowercase(s);
+ for (unsigned int i = 0; i < s.size(); ++i) {
+ char c = tmp[i];
+ switch (c) {
+ case 'r': d_->args.push_back(REQUIRED_ARG); break;
+ case 'o': d_->args.push_back(OPTIONAL_ARG); break;
+ case 'b': d_->args.push_back(BEAMER_ARG); break;
+ case 's': d_->args.push_back(SPECIAL_ARG); break;
+ default:
+ LYXERR0("Unrecognized Argument flag" << c);
+ break;
+ }
+ }
+}
+
} // namespace lyx
Index: src/Layout.h
===================================================================
--- src/Layout.h (revision 35492)
+++ src/Layout.h (working copy)
@@ -17,17 +17,44 @@
#include "FontInfo.h"
#include "LayoutEnums.h"
#include "Spacing.h"
-#include "support/docstring.h"
+#include "support/strfwd.h"
#include <set>
-#include <string>
namespace lyx {
class Language;
+class Layout;
class Lexer;
class TextClass;
+/// Represents the arguments, optional and required,
+/// for a given layout.
+class Arguments {
+public:
+ ///
+ Arguments();
+ ///
+ ~Arguments();
+ ///
+ Arguments(std::string const &);
+ ///
+ bool empty() const;
+ ///
+ int numargs() const;
+ ///
+ LaTeXArgumentType operator[](int) const;
+ ///
+ void setArguments(std::string const &);
+ ///
+ void clear();
+private:
+ class Impl;
+ Impl * d_;
+ friend class Layout;
+};
+
+
/* Fixed labels are printed flushright, manual labels flushleft.
* MARGIN_MANUAL and MARGIN_FIRST_DYNAMIC are *only* for LABEL_MANUAL,
* MARGIN_DYNAMIC and MARGIN_STATIC are *not* for LABEL_MANUAL.
@@ -243,15 +270,8 @@
bool intitle;
/// Is the content to go in the preamble rather than the body?
bool inpreamble;
- /// Number of requried arguments for this command or environment
- unsigned int reqargs;
- /// Number of optional arguments for this command or environment
- /// These MUST come at the beginning, so:
- /// \cmd[opt1][opt2]{req1}{here is the text from LyX}
- /// is fine. But:
- /// \cmd[opt1]{req1}[opt2]{here is the text from LyX}
- /// is not.
- unsigned int optargs;
+ /// Arguments for this command or environment
+ Arguments const & arguments() const { return arguments_; }
/// Which counter to step
docstring counter;
/// Prefix to use when creating labels
@@ -379,6 +399,8 @@
docstring babelpreamble_;
/// Packages needed for this layout
std::set<std::string> requires_;
+ ///
+ Arguments arguments_;
};
} // namespace lyx
Index: src/LayoutEnums.h
===================================================================
--- src/LayoutEnums.h (revision 35492)
+++ src/LayoutEnums.h (working copy)
@@ -143,6 +143,15 @@
END_LABEL_STATIC
};
+///
+enum LaTeXArgumentType {
+ REQUIRED_ARG, // output with {}
+ OPTIONAL_ARG, // output with []
+ BEAMER_ARG, // output with <>
+ SPECIAL_ARG, // output verbatim
+ NO_ARG // dummy value for weirdness
+};
+
} // namespace lyx
#endif
Index: src/LyX.cpp
===================================================================
--- src/LyX.cpp (revision 35492)
+++ src/LyX.cpp (working copy)
@@ -808,6 +808,7 @@
// Set up command definitions
pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
+ // FIXME
// Set up bindings
pimpl_->toplevel_keymap_.read("site");
pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
Index: src/output_latex.cpp
===================================================================
--- src/output_latex.cpp (revision 35492)
+++ src/output_latex.cpp (working copy)
@@ -168,8 +168,8 @@
if (style.isEnvironment()) {
os << "\\begin{" << from_ascii(style.latexname()) << '}';
- if (style.optargs != 0 || style.reqargs != 0) {
- int ret = latexArgInsets(*pit, os, runparams, style.reqargs, style.optargs);
+ if (!style.arguments().empty()) {
+ int ret = latexArgInsets(*pit, os, runparams, style.arguments());
while (ret > 0) {
texrow.newline();
--ret;
@@ -280,56 +280,60 @@
int latexArgInsets(Paragraph const & par, odocstream & os,
- OutputParams const & runparams, unsigned int reqargs,
- unsigned int optargs)
+ OutputParams const & runparams, Arguments const & args)
{
- unsigned int totalargs = reqargs + optargs;
- list<InsetArgument const *> ilist;
+ unsigned int const numargs = args.numargs();
+ if (numargs == 0)
+ return 0;
+ list<InsetArgument const *> ilist;
+
InsetList::const_iterator it = par.insetList().begin();
InsetList::const_iterator end = par.insetList().end();
for (; it != end; ++it) {
if (it->inset->lyxCode() == ARG_CODE) {
- if (ilist.size() >= totalargs) {
- LYXERR0("WARNING: Found extra argument inset.");
- continue;
- }
InsetArgument const * ins =
static_cast<InsetArgument const *>(it->inset);
ilist.push_back(ins);
}
+ if (ilist.size() == numargs)
+ break;
}
- if (!reqargs && ilist.size() == 0)
- return 0;
-
int lines = 0;
- bool const have_optional_args = ilist.size() > reqargs;
- if (have_optional_args) {
- unsigned int todo = ilist.size() - reqargs;
- for (unsigned int i = 0; i < todo; ++i) {
- InsetArgument const * ins = ilist.front();
- ilist.pop_front();
- lines += ins->latexArgument(os, runparams, true);
- }
+ // we use the Argument insets to output required arguments first,
+ // and then to output optional arguments, if we have some left.
+ // so we need to know how many required arguments there are.
+ // this will then be used to keep track of how many of them
+ // are left.
+ // this is not ideal, but a full re-working of the argument
+ // structures has to wait for 2.1.
+ unsigned int required_args = 0;
+ for (unsigned int i = 0; i < numargs; i++) {
+ if (args[i] == REQUIRED_ARG)
+ required_args++;
}
- // we should now have no more insets than there are required
- // arguments.
- LASSERT(ilist.size() <= reqargs, /* */);
- if (!reqargs)
- return lines;
-
- for (unsigned int i = 0; i < reqargs; ++i) {
- if (ilist.empty())
- // a required argument wasn't given, so we output {}
- os << "{}";
- else {
- InsetArgument const * ins = ilist.front();
- ilist.pop_front();
- lines += ins->latexArgument(os, runparams, false);
+ for (unsigned int i = 0; i < numargs; i++) {
+ LaTeXArgumentType argtype = args[i];
+ if (ilist.empty()) {
+ if (argtype == REQUIRED_ARG)
+ // a required argument wasn't given
+ os << "{}";
+ continue;
}
- }
+
+ InsetArgument const * ins = ilist.front();
+ if (argtype == REQUIRED_ARG)
+ required_args--;
+ else if (required_args >= ilist.size()) {
+ // we need more required arguments than we have, so this one
+ // will get ignored
+ continue;
+ }
+ ilist.pop_front();
+ lines += ins->latexArgument(os, runparams, argtype);
+ }
return lines;
}
@@ -583,8 +587,8 @@
os << '\\' << from_ascii(style.latexname());
// Separate handling of optional argument inset.
- if (style.optargs != 0 || style.reqargs != 0) {
- int ret = latexArgInsets(*pit, os, runparams, style.reqargs, style.optargs);
+ if (!style.arguments().empty()) {
+ int ret = latexArgInsets(*pit, os, runparams, style.arguments());
while (ret > 0) {
texrow.newline();
--ret;
Index: src/output_latex.h
===================================================================
--- src/output_latex.h (revision 35492)
+++ src/output_latex.h (working copy)
@@ -22,6 +22,7 @@
namespace lyx {
+class Arguments;
class Buffer;
class BufferParams;
class Encoding;
@@ -37,7 +38,7 @@
/// must all come first.
int latexArgInsets(Paragraph const & par,
odocstream & os, OutputParams const & runparams,
- unsigned int reqargs, unsigned int optargs);
+ Arguments const & args);
/** Export \p paragraphs of buffer \p buf to LaTeX.
Don't use a temporary stringstream for \p os if the final output is
Index: src/Text3.cpp
===================================================================
--- src/Text3.cpp (revision 35492)
+++ src/Text3.cpp (working copy)
@@ -2365,8 +2365,7 @@
break;
case LFUN_ARGUMENT_INSERT: {
code = ARG_CODE;
- Layout const & lay = cur.paragraph().layout();
- int const numargs = lay.reqargs + lay.optargs;
+ int const numargs = cur.paragraph().layout().arguments().numargs();
enable = cur.paragraph().insetList().count(ARG_CODE) < numargs;
break;
}
Index: src/TextClass.cpp
===================================================================
--- src/TextClass.cpp (revision 35492)
+++ src/TextClass.cpp (working copy)
@@ -60,7 +60,7 @@
// development/updatelayouts.sh script, to update the format of
// all of our layout files.
//
-int const LAYOUT_FORMAT = 28;
+int const LAYOUT_FORMAT = 29;
namespace {
Index: src/insets/InsetCaption.cpp
===================================================================
--- src/insets/InsetCaption.cpp (revision 35492)
+++ src/insets/InsetCaption.cpp (working copy)
@@ -232,7 +232,7 @@
// optional argument.
runparams.moving_arg = true;
os << "\\caption";
- int l = latexArgInsets(paragraphs()[0], os, runparams, 0, 1);
+ int l = latexArgInsets(paragraphs()[0], os, runparams, Arguments("o"));
os << '{';
l += InsetText::latex(os, runparams);
os << "}\n";
@@ -288,7 +288,7 @@
int InsetCaption::getOptArg(odocstream & os,
OutputParams const & runparams) const
{
- return latexArgInsets(paragraphs()[0], os, runparams, 0, 1);
+ return latexArgInsets(paragraphs()[0], os, runparams, Arguments("o"));
}
Index: src/tex2lyx/text.cpp
===================================================================
--- src/tex2lyx/text.cpp (revision 35492)
+++ src/tex2lyx/text.cpp (working copy)
@@ -455,7 +455,24 @@
context.check_deeper(os);
context.check_layout(os);
unsigned int optargs = 0;
- while (optargs < context.layout->optargs) {
+
+ // FIXME
+ // tex2lyx cannot handle the new required arguments, so we
+ // have to make sure we're only dealing with optional ones
+ // so we check all the arguments, and if we find one that is
+ // not optional, we pretend we don't know about any of them.
+ // eventually, this should be fixed, of course, once tex2lyx
+ // can handle the InsetArgument stuff.
+ Arguments const & layargs = context.layout->arguments();
+ unsigned int numargs = layargs.numargs();
+ for (unsigned int i = 0; i < numargs; i++) {
+ if (layargs[i] != OPTIONAL_ARG) {
+ numargs = 0;
+ break;
+ }
+ }
+
+ while (optargs < numargs) {
eat_whitespace(p, os, context, false);
if (p.next_token().character() != '[')
break;
Index: lib/scripts/layout2layout.py
===================================================================
--- lib/scripts/layout2layout.py (revision 35492)
+++ lib/scripts/layout2layout.py (working copy)
@@ -100,6 +100,9 @@
# Incremented to format 28, 6 August 2010 by lasgouttes
# Added ParbreakIsNewline tag for Layout and InsetLayout.
+# Incremented to format 29, 24 September 2010 by rgh
+# Changed OptionalArgs and RequiredArgs to Arguments.
+
# Do not forget to document format change in Customization
# Manual (section "Declaring a new text class").
@@ -107,7 +110,7 @@
# development/tools/updatelayouts.sh script to update all
# layout files to the new format.
-currentFormat = 28
+currentFormat = 29
def usage(prog_name):
@@ -191,6 +194,8 @@
re_Type = re.compile(r'\s*Type\s+(\w+)', re.IGNORECASE)
re_Builtin = re.compile(r'^(\s*)LaTeXBuiltin\s+(\w*)', re.IGNORECASE)
re_True = re.compile(r'^\s*(?:true|1)\s*$', re.IGNORECASE)
+ re_OptArgs = re.compile(r'^(\s*)OptionalArgs\s+(\d+)\s*$', re.IGNORECASE)
+ re_ReqArgs = re.compile(r'^(\s*)RequiredArgs\s+(\d+)\s*$', re.IGNORECASE)
# counters for sectioning styles (hardcoded in 1.3)
counters = {"part" : "\\Roman{part}",
@@ -279,6 +284,38 @@
i += 1
continue
+ # Given the way layout2layout currently works, there is no way that
+ # this can actually be correct, in the general case, the problem case
+ # being one where we have BOTH optional and required arguments. At the
+ # moment, however, there are no such cases in LyX's own layouts and,
+ # in all likelihood, no one has made use of this possibility yet.
+ if format == 28:
+ m = re_OptArgs.match(lines[i])
+ if m:
+ sp = m.group(1)
+ nargs = int(m.group(2))
+ argtype = "o"
+ else:
+ m = re_ReqArgs.match(lines[i])
+ if m:
+ sp = m.group(1)
+ nargs = int(m.group(2))
+ argtype = "r"
+ else:
+ i += 1
+ continue
+ if nargs == 0:
+ newline = sp + "Arguments none"
+ else:
+ newline = sp + "Arguments "
+ j = 0
+ while j < nargs:
+ newline += argtype
+ j += 1
+ lines[i] = newline
+ i += 1
+ continue
+
# Only new features
if format >= 24 and format <= 27:
i += 1