This patch addresses the remaining issue with crashes on ill-formed
inset-insert minibuffer commands. I've also added some comments
expressing my understanding of what's going on here.
Comments welcome. Testing desired.
Richard
--
==================================================================
Richard G Heck, Jr
Professor of Philosophy
Brown University
http://frege.brown.edu/heck/
==================================================================
Get my public key from http://sks.keyserver.penguin.de
Hash: 0x1DE91F1E66FFBDEC
Learn how to sign your email using Thunderbird and GnuPG at:
http://dudu.dyn.2-h.org/nist/gpg-enigmail-howto
Index: insetcommandparams.C
===================================================================
--- insetcommandparams.C (revision 17850)
+++ insetcommandparams.C (working copy)
@@ -35,20 +35,39 @@
using support::WarningException;
InsetCommandParams::InsetCommandParams(string const & name)
- : name_(name), preview_(false)
+ : name_(name), type_(command2ParamType(name)), preview_(false)
{
info_ = findInfo(name);
BOOST_ASSERT(info_);
params_.resize(info_->n);
}
+//NOTE To add a new command to this file:
+// (i) If this represents a new inset type, add a new value to the
+// InsetParamType enum in insetcommandparams.h and add an
+// appropriate branch to command2ParamType. If not, then expand
+// the relevant branch there.
+// (ii) Add the relevant information to findInfo(). The possible parameters
+// should be specified in the order in which they should be output for
+// LaTeX. The isoptional[] array specifies whether the parameter is
+// optional in the sense of LaTeX. So, for example, getCommand() for
+// bibtex will output:
+// \bibtex[options][btprint]{bibfiles}
+// with the optional arguments printed only when necessary.
+// No parameter may be named "preview", because that is a required
+// flag for all commands.
+//It might seem obvious to use command2ParamType here and do a
+//check on that instead of on name. That, however, would assume that
+//the list of parameters does not depend up the command associated
+//with the inset. At present (4/07), that is true, but it could change.
+//If not, then perhaps this change should be made. That would simplify
+//findInfo() and allow removal of the complex code in setCmdName().
+
+//See the discussion in http://bugzilla.lyx.org/show_bug.cgi?id=3463.
InsetCommandParams::CommandInfo const *
InsetCommandParams::findInfo(std::string const & name)
{
- // No parameter may be named "preview", because that is a required
- // flag for all commands.
-
// InsetBibitem
if (name == "bibitem") {
static const char * const paramnames[] = {"label", "key", ""};
@@ -164,17 +183,115 @@
return &info;
}
- return 0;
+ lyxerr << "InsetCommandParams: Unknown inset type." << endl;
+ throw ExceptionMessage(WarningException,
+ _("Unknown inset type"),
+ from_utf8("Unknown inset type"));
}
+InsetCommandParams::InsetParamType InsetCommandParams::command2ParamType(std::string const & cmd)
+{
+ // InsetBibitem
+ if (cmd == "bibitem")
+ return ICP_BIBITEM;
+
+ // InsetBibtex
+ if (cmd == "bibtex")
+ return ICP_BIBTEX;
+
+ // InsetCitation
+ // FIXME: Use is_possible_cite_command() in
+ // src/frontends/controllers/biblio.C, see comment in src/factory.C.
+ if (cmd == "cite" || cmd == "citet" || cmd == "citep" || cmd == "citealt" ||
+ cmd == "citealp" || cmd == "citeauthor" || cmd == "citeyear" ||
+ cmd == "citeyearpar" || cmd == "citet*" || cmd == "citep*" ||
+ cmd == "citealt*" || cmd == "citealp*" ||
+ cmd == "citeauthor*" || cmd == "Citet" || cmd == "Citep" ||
+ cmd == "Citealt" || cmd == "Citealp" || cmd == "Citeauthor" ||
+ cmd == "Citet*" || cmd == "Citep*" || cmd == "Citealt*" ||
+ cmd == "Citealp*" || cmd == "Citeauthor*" ||
+ cmd == "citefield" || cmd == "citetitle" || cmd == "cite*")
+ return ICP_CITATION;
+
+ // InsetFloatlist
+ if (cmd == "floatlist")
+ return ICP_FLOATLIST;
+
+ // InsetHfill
+ if (cmd == "hfill")
+ return ICP_HFILL;
+
+ // InsetInclude
+ if (cmd == "include" || cmd == "input" || cmd == "verbatiminput" ||
+ cmd == "verbatiminput*")
+ return ICP_INCLUDE;
+
+ // InsetIndex
+ if (cmd == "index")
+ return ICP_INDEX;
+
+ //InsetPrintIndex
+ if (cmd == "printindex")
+ return ICP_PRINTINDEX;
+
+ //InsetLabel
+ if (cmd == "label")
+ return ICP_LABEL;
+
+ // InsetNomencl
+ if (cmd == "nomenclature")
+ return ICP_NOMENCL;
+
+ // InsetPrintNomencl
+ if (cmd == "printnomenclature")
+ return ICP_PRINTNOMENCL;
+
+ // InsetRef
+ if (cmd == "eqref" || cmd == "pageref" || cmd == "vpageref" ||
+ cmd == "vref" || cmd == "prettyref" || cmd == "ref")
+ return ICP_REF;
+
+ // InsetTOC
+ if (cmd == "tableofcontents")
+ return ICP_TOC;
+
+ // InsetUrl
+ if (cmd == "htmlurl" || cmd == "url")
+ return ICP_URL;
+
+ lyxerr << "InsetCommandParams: Unknown inset name: " << cmd << endl;
+ throw ExceptionMessage(WarningException,
+ _("Unknown inset command"),
+ from_utf8(cmd));
+}
+
+
+bool InsetCommandParams::isCompatible(string const & cmd)
+{
+ return command2ParamType(cmd) == type_;
+}
+
+
void InsetCommandParams::setCmdName(string const & name)
{
+ if (!isCompatible(name)) {
+ lyxerr <<
+ "InsetCommandParams: Attempt to convert type of inset from " <<
+ name_ << " to " << name << "." << endl;
+ throw ExceptionMessage(WarningException,
+ _("Invalid type conversion"),
+ from_utf8("Invalid conversion of inset type in InsetCommandParams"));
+ }
name_ = name;
- CommandInfo const * const info = findInfo(name);
+
+ //NOTE The following code is necessary only if we want to allow different
+ //commands associated with a single inset to take different parameters.
+ //No extant inset behaves this way.
+ CommandInfo const * const info = findInfo(name_);
BOOST_ASSERT(info);
ParamVector params(info->n);
- // Overtake parameters with the same name
+ //Get values for new parameters from old ones with the same name
for (size_t i = 0; i < info_->n; ++i) {
int j = findToken(info->paramnames, info_->paramnames[i]);
if (j >= 0)
@@ -260,14 +377,15 @@
{
if (lex.isOK()) {
lex.next();
- name_ = lex.getString();
- info_ = findInfo(name_);
- if (!info_) {
- lex.printError("InsetCommand: Unknown inset name `$$Token'");
+ string name = lex.getString();
+ if (!isCompatible(name)) {
+ lex.printError("InsetCommandParams: Attempt to convert type of inset from " + name_ + " to `$$Token'");
throw ExceptionMessage(WarningException,
- _("Unknown inset name: "),
- from_utf8(name_));
+ _("Invalid type conversion"),
+ from_utf8("Invalid conversion of inset type in InsetCommandParams::read()."));
}
+ name_ = name;
+ info_ = findInfo(name_);
}
string token;
@@ -457,6 +575,7 @@
InsetCommandParams const & o2)
{
return o1.name_ == o2.name_ &&
+ o1.type_ == o2.type_ &&
o1.info_ == o2.info_ &&
o1.params_ == o2.params_ &&
o1.preview_ == o2.preview_;
Index: insetcommandparams.h
===================================================================
--- insetcommandparams.h (revision 17850)
+++ insetcommandparams.h (working copy)
@@ -23,11 +23,15 @@
class LyXLex;
+//FIXME Support for keyval would be nice here and may be necessary
+//for support of some kinds of commands.
class InsetCommandParams {
public:
/// Construct parameters for command \p name. \p name must be known.
explicit InsetCommandParams(std::string const & name);
- ///
+ /// Reads a string representing parameters for this command, as it would
+ /// appear in a LyX file or as entered in the minibuffer, etc. Throws an
+ /// ExceptionMessage if the string is ill-formed.
void read(LyXLex &);
/// Parse the command
/// FIXME remove
@@ -46,9 +50,12 @@
public:
/// FIXME remove
std::string const getContents() const;
- /// Set the name to \p n. This must be a known name. All parameters
- /// are cleared except those that exist also in the new command.
- /// What matters here is the parameter name, not position.
+ /// Set the name to \p n. This must be compatible with the type of inset
+ /// for which this object was originally created, in the sense that it is
+ /// a command valid for that kind of inset. Throws an ExceptionMessage
+ /// otherwise.
+ /// All parameters are cleared except those that exist also in the new
+ /// command. What matters here is the parameter name, not position.
void setCmdName(std::string const & n);
private:
/// FIXME remove
@@ -68,6 +75,9 @@
void preview(bool p) { preview_ = p; }
/// Clear the values of all parameters
void clear();
+ /// Checks if cmd is of the right type for this inset
+ bool isCompatible(std::string const & cmd);
+
private:
///
@@ -79,18 +89,56 @@
/// Tells whether a parameter is optional
bool const * optional;
};
- /// Get information for command \p name.
- /// Returns 0 if the command is not known.
+ /// Get information on what parameters command \p name takes.
+ /// Throws an ExceptionMessage if the command is not known.
static CommandInfo const * findInfo(std::string const & name);
+ //It's not obvious this is the right place for this enum. Note that it
+ //serves a different purpose from the one in insetbase.C and so can't be
+ //replaced by it.
+ /// Types of insets for which we might be parameters
+ enum InsetParamType {
+ ///
+ ICP_BIBITEM,
+ ///
+ ICP_BIBTEX,
+ ///
+ ICP_CITATION,
+ ///
+ ICP_FLOATLIST,
+ ///
+ ICP_HFILL,
+ ///
+ ICP_INCLUDE,
+ ///
+ ICP_INDEX,
+ ///
+ ICP_PRINTINDEX,
+ ///
+ ICP_LABEL,
+ ///
+ ICP_NOMENCL,
+ ///
+ ICP_PRINTNOMENCL,
+ ///
+ ICP_REF,
+ ///
+ ICP_TOC,
+ ///
+ ICP_URL
+ };
+ /// Throws an ExceptionMessage if the command is not known.
+ InsetParamType command2ParamType(std::string const & cmd);
/// Description of all command properties
CommandInfo const * info_;
/// The name of this command as it appears in .lyx and .tex files
std::string name_;
+ /// The type of inset for which we are the parameters
+ InsetParamType type_;
///
typedef std::vector<docstring> ParamVector;
- /// The parameters (both optional and required ones). The order is
- /// the same that is required for LaTeX output. The size of params_
- /// is always info_->n.
+ /// The values of the parameters (both optional and required ones).
+ /// The order is the same that is required for LaTeX output. The size of
+ /// params_ is always info_->n.
ParamVector params_;
///
bool preview_;