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

Reply via email to