Per Abdel's suggestion that we focus on bug-fixing at this point, this will be the last patch in this series for a bit. But I wanted to get this done before I forget what it is I was doing, so here it is.

The idea behind this patch is to make real key-value support for InsetCommand parameters possible. This should be particularly useful for the listings version of InsetInclude, though we would need some kind of UI for it before it would really be helpful. (See below for some thoughts.) This doesn't substantially change anything else, though some things do get re-arranged a bit.

Basically, the idea is this. First, we introduce a whole range of parameter types: Normal LaTeX optional and required parameters; ones for LyX's internal use (like embed); and finally, in connection with keyval, ones that represent keys and ones that represent optional and required arguments where the keyval stuff will appear. (I'm assuming here that there will always be exactly one of those, and that it will accept only keyval-type material.) The parameters themselves are stored in a map, so it's really only the output routines that need to care about the different types of parameters.

Regarding the frontend, it seems to me that something like the following would work:
(i) scan the parameter list for LATEX_KEY type parameters
(ii) the dialog will have a series of lines, each of which has a combo box listing the acceptable keys and a QLineEdit for entering its value, as well as a "delete" button of some sort for removing this key and its value
(iii) there should be an "add line" button to add a new line, activated only when all other lines are filled with values
Probably not even too hard.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@23235 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Richard Heck 2008-02-25 22:13:45 +00:00
parent f8d516c7af
commit 2fb02d20f1
13 changed files with 216 additions and 90 deletions

View File

@ -53,8 +53,8 @@ ParamInfo const & InsetBibitem::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("label", true); param_info_.add("label", ParamInfo::LATEX_OPTIONAL);
param_info_.add("key", false); param_info_.add("key", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -57,10 +57,10 @@ ParamInfo const & InsetBibtex::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("options", true); param_info_.add("btprint", ParamInfo::LATEX_OPTIONAL);
param_info_.add("btprint", true); param_info_.add("bibfiles", ParamInfo::LATEX_REQUIRED);
param_info_.add("bibfiles", false); param_info_.add("embed", ParamInfo::LYX_INTERNAL);
param_info_.add("embed", false); param_info_.add("options", ParamInfo::LYX_INTERNAL);
} }
return param_info_; return param_info_;
} }

View File

@ -390,9 +390,9 @@ ParamInfo const & InsetCitation::findInfo(string const & /* cmdName */)
// we have to allow both here. InsetCitation takes care that // we have to allow both here. InsetCitation takes care that
// LaTeX output is nevertheless correct. // LaTeX output is nevertheless correct.
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("after", true); param_info_.add("after", ParamInfo::LATEX_OPTIONAL);
param_info_.add("before", true); param_info_.add("before", ParamInfo::LATEX_OPTIONAL);
param_info_.add("key", false); param_info_.add("key", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -30,10 +30,10 @@
#include "Lexer.h" #include "Lexer.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/docstream.h"
#include "support/ExceptionMessage.h" #include "support/ExceptionMessage.h"
#include "support/gettext.h" #include "support/gettext.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/docstream.h"
#include <boost/assert.hpp> #include <boost/assert.hpp>
@ -42,55 +42,68 @@ using namespace lyx::support;
namespace lyx { namespace lyx {
ParamInfo::ParamData::ParamData(std::string const & s, bool b) : ParamInfo::ParamData::ParamData(std::string const & s, ParamType t) :
name_(s), optional_(b) name_(s), type_(t)
{} {}
bool ParamInfo::ParamData::isOptional() const
{
return type_ == ParamInfo::LATEX_OPTIONAL ||
type_ == ParamInfo::LATEX_KV_OPTIONAL;
}
bool ParamInfo::ParamData::isKeyValArg() const
{
return type_ == ParamInfo::LATEX_KV_REQUIRED
|| type_ == ParamInfo::LATEX_KV_OPTIONAL;
}
bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const
{ {
return name() == rhs.name() && isOptional() == rhs.isOptional(); return name() == rhs.name() && type() == rhs.type();
} }
bool ParamInfo::hasParam(std::string const & name) const bool ParamInfo::hasParam(std::string const & name) const
{ {
const_iterator it = begin(); const_iterator it = begin();
for (; it != end(); ++it) { const_iterator last = end();
for (; it != last; ++it) {
if (it->name() == name) if (it->name() == name)
return true; return !it->isKeyValArg();
} }
return false; return false;
} }
void ParamInfo::add(std::string const & name, bool opt) void ParamInfo::add(std::string const & name, ParamType type)
{ {
info_.push_back(ParamData(name, opt)); info_.push_back(ParamData(name, type));
} }
bool ParamInfo::operator==(ParamInfo const & rhs) const bool ParamInfo::operator==(ParamInfo const & rhs) const
{ {
// the idea here is to check each ParamData for equality if (size() != rhs.size())
const_iterator itL = begin(); return false;
const_iterator itR = rhs.begin(); return equal(begin(), end(), rhs.begin());
const_iterator endL = end(); }
const_iterator endR = rhs.end();
while (true) {
// if they both end together, return true ParamInfo::ParamData const &
if (itL == endL && itR == endR) ParamInfo::operator[](std::string const & name) const
return true; {
// but if one ends before the other, return false BOOST_ASSERT(hasParam(name));
if (itL == endL || itR == endR) const_iterator it = begin();
return false; const_iterator last = end();
//check this one for equality for (; it != last; ++it) {
if (*itL != *itR) if (it->name() == name)
return false; return *it;
// equal, so check the next one
++itL;
++itR;
} }
return *it; // silence warning
} }
@ -318,6 +331,65 @@ void InsetCommandParams::write(ostream & os) const
} }
docstring InsetCommandParams::makeKeyValArgument() const
{
odocstringstream os;
bool didone = false;
ParamInfo::const_iterator it = info_.begin();
ParamInfo::const_iterator end = info_.end();
for (; it != end; ++it) {
if (!it->isKey())
continue;
string const & name = it->name();
docstring const & data = (*this)[name];
if (data.empty())
continue;
if (didone)
os << ",";
else
didone = true;
os << from_utf8(name) << "=" << data;
}
return os.str();
}
bool InsetCommandParams::writeEmptyOptional(ParamInfo::const_iterator ci) const
{
if (!ci->isOptional())
BOOST_ASSERT(false);
++ci; // we want to start with the next one
ParamInfo::const_iterator end = info_.end();
for (; ci != end; ++ci) {
switch (ci->type()) {
case ParamInfo::LATEX_KEY:
case ParamInfo::LYX_INTERNAL:
break;
case ParamInfo::LATEX_REQUIRED:
case ParamInfo::LATEX_KV_REQUIRED:
return false;
case ParamInfo::LATEX_OPTIONAL: {
std::string const & name = ci->name();
docstring const & data = (*this)[name];
if (!data.empty())
return true;
break;
}
case ParamInfo::LATEX_KV_OPTIONAL: {
docstring data = makeKeyValArgument();
if (!data.empty())
return true;
break;
}
} //end switch
}
return false;
}
docstring const InsetCommandParams::getCommand() const docstring const InsetCommandParams::getCommand() const
{ {
docstring s = '\\' + from_ascii(cmdName_); docstring s = '\\' + from_ascii(cmdName_);
@ -326,33 +398,45 @@ docstring const InsetCommandParams::getCommand() const
ParamInfo::const_iterator end = info_.end(); ParamInfo::const_iterator end = info_.end();
for (; it != end; ++it) { for (; it != end; ++it) {
std::string const & name = it->name(); std::string const & name = it->name();
docstring const & data = (*this)[name]; switch (it->type()) {
if (!it->isOptional()) { case ParamInfo::LATEX_KEY:
case ParamInfo::LYX_INTERNAL:
break;
case ParamInfo::LATEX_REQUIRED: {
docstring const & data = (*this)[name];
s += '{' + data + '}'; s += '{' + data + '}';
noparam = false; noparam = false;
continue; break;
} }
if (!data.empty()) { case ParamInfo::LATEX_KV_REQUIRED: {
s += '[' + data + ']'; s += "{" + makeKeyValArgument() + "}";
noparam = false; noparam = false;
continue; break;
} }
// This param is therefore optional but empty. case ParamInfo::LATEX_OPTIONAL: {
// But we need to write it anyway if nonempty docstring const & data = (*this)[name];
// optional parameters follow before the next if (!data.empty()) {
// required parameter. s += '[' + data + ']';
ParamInfo::const_iterator it2 = it;
for (++it2; it2 != end; ++it2) {
if (!it2->isOptional())
break;
std::string const & name2 = it2->name();
docstring const & data2 = (*this)[name2];
if (!data2.empty()) {
s += "[]";
noparam = false; noparam = false;
break; } else if (writeEmptyOptional(it)) {
s += "[]";
noparam = false;
} }
break;
} }
case ParamInfo::LATEX_KV_OPTIONAL: {
docstring data = makeKeyValArgument();
if (!data.empty()) {
s += '[' + data + ']';
noparam = false;
} else if (writeEmptyOptional(it)) {
s += "[]";
noparam = false;
}
break;
}
} //end switch
} }
if (noparam) if (noparam)
// Make sure that following stuff does not change the // Make sure that following stuff does not change the
@ -362,18 +446,11 @@ docstring const InsetCommandParams::getCommand() const
} }
namespace {
//predicate for what follows
bool paramIsNonOptional(ParamInfo::ParamData pi)
{
return !pi.isOptional();
}
}
docstring const InsetCommandParams::getFirstNonOptParam() const docstring const InsetCommandParams::getFirstNonOptParam() const
{ {
ParamInfo::const_iterator it = ParamInfo::const_iterator it =
find_if(info_.begin(), info_.end(), paramIsNonOptional); find_if(info_.begin(), info_.end(),
not1(mem_fun_ref(&ParamInfo::ParamData::isOptional)));
if (it == info_.end()) if (it == info_.end())
BOOST_ASSERT(false); BOOST_ASSERT(false);
return (*this)[it->name()]; return (*this)[it->name()];
@ -383,8 +460,7 @@ docstring const InsetCommandParams::getFirstNonOptParam() const
docstring const & InsetCommandParams::operator[](string const & name) const docstring const & InsetCommandParams::operator[](string const & name) const
{ {
static const docstring dummy; //so we don't return a ref to temporary static const docstring dummy; //so we don't return a ref to temporary
if (!info_.hasParam(name)) BOOST_ASSERT(info_.hasParam(name));
BOOST_ASSERT(false);
ParamMap::const_iterator data = params_.find(name); ParamMap::const_iterator data = params_.find(name);
if (data == params_.end() || data->second.empty()) if (data == params_.end() || data->second.empty())
return dummy; return dummy;
@ -394,8 +470,8 @@ docstring const & InsetCommandParams::operator[](string const & name) const
docstring & InsetCommandParams::operator[](string const & name) docstring & InsetCommandParams::operator[](string const & name)
{ {
if (!info_.hasParam(name)) BOOST_ASSERT(info_.hasParam(name));
BOOST_ASSERT(false); ParamInfo::ParamData const & pd = info_[name];
return params_[name]; return params_[name];
} }

View File

@ -29,17 +29,50 @@ class Lexer;
class ParamInfo { class ParamInfo {
public: public:
/// Types of parameters
/// WARNING: LATEX_KV_* `parameters' aren't really parameters at all
/// but merely markers for where the keyval-type parameters should
/// appear in the LaTeX output. ParamInfo::hasParam(name) therefore
/// returns FALSE if the corresponding `parameter' is of type
/// LATEX_KV_*.
/// It is assumed here that there is exactly one argument that accepts
/// the key=value pairs.
enum ParamType {
LATEX_OPTIONAL, /// normal optional argument
LATEX_REQUIRED, /// normal required argument
LATEX_KV_OPTIONAL, /// optional argument that uses keyval
LATEX_KV_REQUIRED, /// required argument that uses keyval
LATEX_KEY, /// a key to be used with keyval argument
LYX_INTERNAL /// a parameter used internally by LyX
};
/// ///
class ParamData { class ParamData {
// No parameter may be named "preview", because that is a required // No parameter may be named "preview", because that is a required
// flag for all commands. // flag for all commands.
public: public:
/// ///
ParamData(std::string const &, bool); ParamData(std::string const &, ParamType);
/// ///
std::string name() const { return name_; } std::string name() const { return name_; }
/// ///
bool isOptional() const { return optional_; } ParamType type() const { return type_; }
/// whether this is a key for use with keyval
bool isKey() const
{ return type_ == LATEX_KEY; }
/// whether this is an optional LaTeX argument
inline bool isOptional() const;
/// whether this is a keyval argument
inline bool isKeyValArg() const;
#if 0
//presently unused but perhaps useful at some point
/// whether this is a required LaTeX argument
bool isRequired() const
{ return type_ == ParamInfo::LATEX_REQUIRED ||
type_ == ParamInfo::LATEX_KV_REQUIRED; }
/// whether this is a LaTeX argument
inline bool isLaTeXArgument() const
{ return isOptional() || isRequired(); }
#endif
/// ///
bool operator==(ParamData const &) const; bool operator==(ParamData const &) const;
/// ///
@ -49,11 +82,11 @@ public:
/// ///
std::string name_; std::string name_;
/// ///
bool optional_; ParamType type_;
}; };
/// adds a new parameter /// adds a new parameter
void add(std::string const & name, bool optional); void add(std::string const & name, ParamType type);
/// ///
bool empty() const { return info_.empty(); } bool empty() const { return info_.empty(); }
/// ///
@ -61,12 +94,17 @@ public:
/// ///
typedef std::vector<ParamData>::const_iterator const_iterator; typedef std::vector<ParamData>::const_iterator const_iterator;
/// ///
const_iterator begin() const { return info_.begin(); } const_iterator const begin() const { return info_.begin(); }
///
const_iterator end() const { return info_.end(); }
/// ///
const_iterator const end() const { return info_.end(); }
/// \return true if name corresponds to a parameter of some sort.
/// \return false if the parameter does not exist at all of it it
/// corresponds to a `parameter' of type LATEX_KV_*; these do not
/// really represent parameters but just argument places.
bool hasParam(std::string const & name) const; bool hasParam(std::string const & name) const;
/// ///
ParamData const & operator[](std::string const & name) const;
///
bool operator==(ParamInfo const &) const; bool operator==(ParamInfo const &) const;
private: private:
/// ///
@ -103,8 +141,12 @@ public:
/// ways that make removal hard. /// ways that make removal hard.
docstring const getFirstNonOptParam() const; docstring const getFirstNonOptParam() const;
/// get parameter \p name /// get parameter \p name
/// WARNING: You cannot access LATEX_KV_* arguments in this way.
/// LyX will assert if you attempt to do so.
docstring const & operator[](std::string const & name) const; docstring const & operator[](std::string const & name) const;
/// set parameter \p name /// set parameter \p name
/// WARNING: You cannot access LATEX_KV_* arguments in this way.
/// LyX will assert if you attempt to do so.
docstring & operator[](std::string const & name); docstring & operator[](std::string const & name);
/// ///
bool preview() const { return preview_; } bool preview() const { return preview_; }
@ -128,6 +170,11 @@ private:
static bool isCompatibleCommand(InsetCode code, std::string const & s); static bool isCompatibleCommand(InsetCode code, std::string const & s);
/// ///
std::string getDefaultCmd(InsetCode); std::string getDefaultCmd(InsetCode);
///
docstring makeKeyValArgument() const;
/// checks whether we need to write an empty optional parameter
/// \return true if a non-empty optional parameter follows ci
bool writeEmptyOptional(ParamInfo::const_iterator ci) const;
/// Description of all command properties /// Description of all command properties
ParamInfo info_; ParamInfo info_;
/// what kind of inset we're the parameters for /// what kind of inset we're the parameters for
@ -135,6 +182,9 @@ private:
/// The name of this command as it appears in .lyx and .tex files /// The name of this command as it appears in .lyx and .tex files
std::string cmdName_; std::string cmdName_;
/// ///
// if we need to allow more than one value for a parameter, this
// could be made a multimap. it may be that the only thing that
// would then need changing is operator[].
typedef std::map<std::string, docstring> ParamMap; typedef std::map<std::string, docstring> ParamMap;
/// The parameters, by name. /// The parameters, by name.
ParamMap params_; ParamMap params_;

View File

@ -52,7 +52,7 @@ ParamInfo const & InsetFloatList::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("type", false); param_info_.add("type", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -37,9 +37,9 @@ ParamInfo const & InsetHyperlink::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("name", true); param_info_.add("name", ParamInfo::LATEX_OPTIONAL);
param_info_.add("target", false); param_info_.add("target", ParamInfo::LATEX_REQUIRED);
param_info_.add("type", false); param_info_.add("type", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -173,9 +173,9 @@ ParamInfo const & InsetInclude::findInfo(string const & /* cmdName */)
// In the other cases, this second parameter should just be empty. // In the other cases, this second parameter should just be empty.
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("filename", false); param_info_.add("filename", ParamInfo::LATEX_REQUIRED);
param_info_.add("embed", false); param_info_.add("lstparams", ParamInfo::LATEX_OPTIONAL);
param_info_.add("lstparams", true); param_info_.add("embed", ParamInfo::LYX_INTERNAL);
} }
return param_info_; return param_info_;
} }

View File

@ -84,7 +84,7 @@ ParamInfo const & InsetPrintIndex::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("name", false); param_info_.add("name", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -37,7 +37,7 @@ ParamInfo const & InsetLabel::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("name", false); param_info_.add("name", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -39,9 +39,9 @@ ParamInfo const & InsetNomencl::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("prefix", true); param_info_.add("prefix", ParamInfo::LATEX_OPTIONAL);
param_info_.add("symbol", false); param_info_.add("symbol", ParamInfo::LATEX_REQUIRED);
param_info_.add("description", false); param_info_.add("description", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }
@ -92,7 +92,7 @@ ParamInfo const & InsetPrintNomencl::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("labelwidth", true); param_info_.add("labelwidth", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -56,8 +56,8 @@ ParamInfo const & InsetRef::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("name", true); param_info_.add("name", ParamInfo::LATEX_OPTIONAL);
param_info_.add("reference", false); param_info_.add("reference", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }

View File

@ -36,7 +36,7 @@ ParamInfo const & InsetTOC::findInfo(string const & /* cmdName */)
{ {
static ParamInfo param_info_; static ParamInfo param_info_;
if (param_info_.empty()) { if (param_info_.empty()) {
param_info_.add("type", false); param_info_.add("type", ParamInfo::LATEX_REQUIRED);
} }
return param_info_; return param_info_;
} }