-/* This file is part of
- * ======================================================
+/**
+ * \file insetcommandparams.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
*
- * LyX, The Document Processor
+ * \author Angus Leeming
+ * \author Georg Baum
*
- * Copyright 2002-2002 The LyX Team.
- *
- * ====================================================== */
-
+ * Full author contact details are available in file CREDITS.
+ */
-#ifdef __GNUG__
-#pragma implementation
-#endif
+#include <config.h>
#include "insetcommandparams.h"
-#include "lyxlex.h"
+
#include "debug.h"
+#include "lyxlex.h"
+
+#include "support/lstrings.h"
+#include <boost/assert.hpp>
-InsetCommandParams::InsetCommandParams()
-{}
+namespace lyx {
-InsetCommandParams::InsetCommandParams(string const & n,
- string const & c,
- string const & o)
- : cmdname(n), contents(c), options(o)
-{}
+using support::findToken;
+using std::string;
+using std::endl;
+using std::ostream;
-string const InsetCommandParams::getAsString() const
+
+InsetCommandParams::InsetCommandParams(string const & name)
+ : name_(name), preview_(false)
{
- return cmdname + "|++|" + contents + "|++|" + options;
+ info_ = findInfo(name);
+ BOOST_ASSERT(info_);
+ params_.resize(info_->n);
}
-void InsetCommandParams::setFromString(string const & b)
+InsetCommandParams::CommandInfo const *
+InsetCommandParams::findInfo(std::string const & name)
{
- string::size_type idx = b.find("|++|");
- if (idx == string::npos) {
- cmdname = b;
- contents = "";
- options = "";
- return;
+ // 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", ""};
+ static const bool isoptional[] = {true, false};
+ static const CommandInfo info = {2, paramnames, isoptional};
+ return &info;
}
- cmdname = b.substr(0, idx);
- string tmp = b.substr(idx+4);
+ // InsetBibtex
+ if (name == "bibtex") {
+ static const char * const paramnames[] =
+ {"options", "btprint", "bibfiles", ""};
+ static const bool isoptional[] = {true, true, false};
+ static const CommandInfo info = {3, paramnames, isoptional};
+ return &info;
+ }
- idx = tmp.find("|++|");
- if (idx == string::npos) {
- contents = tmp;
- options = "";
- } else {
- contents = tmp.substr(0, idx);
- options = tmp.substr(idx+4);
+ // InsetCitation
+ // FIXME: Use is_possible_cite_command() in
+ // src/frontends/controllers/biblio.C, see comment in src/factory.C.
+ if (name == "cite" || name == "citet" || name == "citep" || name == "citealt" ||
+ name == "citealp" || name == "citeauthor" || name == "citeyear" ||
+ name == "citeyearpar" || name == "citet*" || name == "citep*" ||
+ name == "citealt*" || name == "citealp*" ||
+ name == "citeauthor*" || name == "Citet" || name == "Citep" ||
+ name == "Citealt" || name == "Citealp" || name == "Citeauthor" ||
+ name == "Citet*" || name == "Citep*" || name == "Citealt*" ||
+ name == "Citealp*" || name == "Citeauthor*" ||
+ name == "citefield" || name == "citetitle" || name == "cite*") {
+ // standard cite does only take one argument if jurabib is
+ // not used, but jurabib extends this to two arguments, so
+ // we have to allow both here. InsetCitation takes care that
+ // LaTeX output is nevertheless correct.
+ static const char * const paramnames[] =
+ {"after", "before", "key", ""};
+ static const bool isoptional[] = {true, true, false};
+ static const CommandInfo info = {3, paramnames, isoptional};
+ return &info;
}
-}
+ // InsetFloatlist
+ if (name == "floatlist") {
+ static const char * const paramnames[] = {"type", ""};
+ static const bool isoptional[] = {false};
+ static const CommandInfo info = {1, paramnames, isoptional};
+ return &info;
+ }
-bool InsetCommandParams::operator==(InsetCommandParams const & o) const
-{
- return cmdname == o.cmdname && contents == o.contents
- && options == o.options;
+ // InsetHfill
+ if (name == "hfill") {
+ static const char * const paramnames[] = {""};
+ static const CommandInfo info = {0, paramnames, 0};
+ return &info;
+ }
+
+ // InsetInclude
+ if (name == "include" || name == "input" || name == "verbatiminput" ||
+ name == "verbatiminput*") {
+ static const char * const paramnames[] = {"filename", ""};
+ static const bool isoptional[] = {false};
+ static const CommandInfo info = {1, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetIndex, InsetPrintIndex, InsetLabel
+ if (name == "index" || name == "printindex" || name == "label") {
+ static const char * const paramnames[] = {"name", ""};
+ static const bool isoptional[] = {false};
+ static const CommandInfo info = {1, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetNomencl
+ if (name == "nomenclature") {
+ static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
+ static const bool isoptional[] = {true, false, false};
+ static const CommandInfo info = {3, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetPrintNomencl
+ if (name == "printnomenclature") {
+ static const char * const paramnames[] = {"labelwidth", ""};
+ static const bool isoptional[] = {true};
+ static const CommandInfo info = {1, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetRef
+ if (name == "eqref" || name == "pageref" || name == "vpageref" ||
+ name == "vref" || name == "prettyref" || name == "ref") {
+ static const char * const paramnames[] =
+ {"name", "reference", ""};
+ static const bool isoptional[] = {true, false};
+ static const CommandInfo info = {2, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetTOC
+ if (name == "tableofcontents") {
+ static const char * const paramnames[] = {"type", ""};
+ static const bool isoptional[] = {false};
+ static const CommandInfo info = {1, paramnames, isoptional};
+ return &info;
+ }
+
+ // InsetUrl
+ if (name == "htmlurl" || name == "url") {
+ static const char * const paramnames[] =
+ {"name", "target", ""};
+ static const bool isoptional[] = {true, false};
+ static const CommandInfo info = {2, paramnames, isoptional};
+ return &info;
+ }
+
+ return 0;
}
-bool InsetCommandParams::operator!=(InsetCommandParams const & o) const
+void InsetCommandParams::setCmdName(string const & name)
{
- return !(*this == o);
+ name_ = name;
+ CommandInfo const * const info = findInfo(name);
+ BOOST_ASSERT(info);
+ ParamVector params(info->n);
+ // Overtake parameters with the same name
+ for (size_t i = 0; i < info_->n; ++i) {
+ int j = findToken(info->paramnames, info_->paramnames[i]);
+ if (j >= 0)
+ params[j] = params_[i];
+ }
+ info_ = info;
+ std::swap(params, params_);
}
void InsetCommandParams::scanCommand(string const & cmd)
{
- string tcmdname, toptions, tcontents;
+ string tcmdname, toptions, tsecoptions, tcontents;
if (cmd.empty()) return;
- enum { WS, CMDNAME, OPTION, CONTENT } state = WS;
+ enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
// Used to handle things like \command[foo[bar]]{foo{bar}}
int nestdepth = 0;
for (string::size_type i = 0; i < cmd.length(); ++i) {
- char c = cmd[i];
+ char const c = cmd[i];
if ((state == CMDNAME && c == ' ') ||
(state == CMDNAME && c == '[') ||
(state == CMDNAME && c == '{')) {
state = WS;
}
if ((state == OPTION && c == ']') ||
+ (state == SECOPTION && c == ']') ||
(state == CONTENT && c == '}')) {
if (nestdepth == 0) {
state = WS;
}
}
if ((state == OPTION && c == '[') ||
+ (state == SECOPTION && c == '[') ||
(state == CONTENT && c == '{')) {
++nestdepth;
}
switch (state) {
case CMDNAME: tcmdname += c; break;
case OPTION: toptions += c; break;
+ case SECOPTION: tsecoptions += c; break;
case CONTENT: tcontents += c; break;
- case WS:
+ case WS: {
+ char const b = i? cmd[i-1]: 0;
if (c == '\\') {
state = CMDNAME;
- } else if (c == '[') {
+ } else if (c == '[' && b != ']') {
state = OPTION;
nestdepth = 0; // Just to be sure
+ } else if (c == '[' && b == ']') {
+ state = SECOPTION;
+ nestdepth = 0; // Just to be sure
} else if (c == '{') {
state = CONTENT;
nestdepth = 0; // Just to be sure
}
break;
}
+ }
}
// Don't mess with this.
if (!tcmdname.empty()) setCmdName(tcmdname);
if (!toptions.empty()) setOptions(toptions);
+ if (!tsecoptions.empty()) setSecOptions(tsecoptions);
if (!tcontents.empty()) setContents(tcontents);
if (lyxerr.debugging(Debug::PARSER))
lyxerr << "Command <" << cmd
- << "> == <" << getCommand()
+ << "> == <" << to_utf8(getCommand())
<< "> == <" << getCmdName()
<< '|' << getContents()
- << '|' << getOptions() << '>' << endl;
+ << '|' << getOptions()
+ << '|' << getSecOptions() << '>' << endl;
}
void InsetCommandParams::read(LyXLex & lex)
{
- string token;
-
- if (lex.eatLine()) {
- token = lex.getString();
- scanCommand(token);
- } else {
- lex.printError("InsetCommand: Parse error: `$$Token'");
+ if (lex.isOK()) {
+ lex.next();
+ name_ = lex.getString();
+ info_ = findInfo(name_);
+ if (!info_)
+ lex.printError("InsetCommand: Unknown inset name `$$Token'");
}
+ string token;
while (lex.isOK()) {
- lex.nextToken();
+ lex.next();
token = lex.getString();
if (token == "\\end_inset")
break;
+ // FIXME Why is preview_ read but not written?
+ if (token == "preview") {
+ lex.next();
+ preview_ = lex.getBool();
+ continue;
+ }
+ int const i = findToken(info_->paramnames, token);
+ if (i >= 0) {
+ lex.next(true);
+ params_[i] = lex.getDocString();
+ } else
+ lex.printError("Unknown parameter name `$$Token' for command " + name_);
}
if (token != "\\end_inset") {
lex.printError("Missing \\end_inset at this point. "
void InsetCommandParams::write(ostream & os) const
{
- os << "LatexCommand " << getCommand() << "\n";
+ os << "LatexCommand " << name_ << '\n';
+ for (size_t i = 0; i < info_->n; ++i)
+ if (!params_[i].empty())
+ // FIXME UNICODE
+ os << info_->paramnames[i] << ' '
+ << LyXLex::quoteString(to_utf8(params_[i]))
+ << '\n';
}
-string const InsetCommandParams::getCommand() const
+docstring const InsetCommandParams::getCommand() const
{
- string s;
- if (!getCmdName().empty()) s += "\\"+getCmdName();
- if (!getOptions().empty()) s += "["+getOptions()+']';
- s += "{"+getContents()+'}';
+ docstring s = '\\' + from_ascii(name_);
+ bool noparam = true;
+ for (size_t i = 0; i < info_->n; ++i) {
+ if (info_->optional[i]) {
+ if (params_[i].empty()) {
+ // We need to write this parameter even if
+ // it is empty if nonempty optional parameters
+ // follow before the next required parameter.
+ for (size_t j = i + 1; j < info_->n; ++j) {
+ if (!info_->optional[j])
+ break;
+ if (!params_[j].empty()) {
+ s += "[]";
+ noparam = false;
+ break;
+ }
+ }
+ } else {
+ s += '[' + params_[i] + ']';
+ noparam = false;
+ }
+ } else {
+ s += '{' + params_[i] + '}';
+ noparam = false;
+ }
+ }
+ if (noparam)
+ // Make sure that following stuff does not change the
+ // command name.
+ s += "{}";
return s;
}
+
+std::string const InsetCommandParams::getOptions() const
+{
+ for (size_t i = 0; i < info_->n; ++i)
+ if (info_->optional[i])
+ return to_utf8(params_[i]);
+ lyxerr << "Programming error: get nonexisting option in "
+ << name_ << " inset." << endl;;
+ return string();
+}
+
+
+std::string const InsetCommandParams::getSecOptions() const
+{
+ bool first = true;
+ for (size_t i = 0; i < info_->n; ++i)
+ if (info_->optional[i]) {
+ if (first)
+ first = false;
+ else
+ return to_utf8(params_[i]);
+ }
+ // Happens in InsetCitation
+ lyxerr << "Programming error: get nonexisting second option in "
+ << name_ << " inset." << endl;;
+ return string();
+}
+
+
+std::string const InsetCommandParams::getContents() const
+{
+ for (size_t i = 0; i < info_->n; ++i)
+ if (!info_->optional[i])
+ return to_utf8(params_[i]);
+ BOOST_ASSERT(false);
+ return string();
+}
+
+
+void InsetCommandParams::setOptions(std::string const & o)
+{
+ for (size_t i = 0; i < info_->n; ++i)
+ if (info_->optional[i]) {
+ params_[i] = from_utf8(o);
+ return;
+ }
+ lyxerr << "Programming error: set nonexisting option in "
+ << name_ << " inset." << endl;;
+}
+
+
+void InsetCommandParams::setSecOptions(std::string const & s)
+{
+ bool first = true;
+ for (size_t i = 0; i < info_->n; ++i)
+ if (info_->optional[i]) {
+ if (first)
+ first = false;
+ else {
+ params_[i] = from_utf8(s);
+ return;
+ }
+ }
+ // Happens in InsetCitation
+ lyxerr << "Programming error: set nonexisting second option in "
+ << name_ << " inset." << endl;;
+}
+
+
+void InsetCommandParams::setContents(std::string const & c)
+{
+ for (size_t i = 0; i < info_->n; ++i)
+ if (!info_->optional[i]) {
+ params_[i] = from_utf8(c);
+ return;
+ }
+ BOOST_ASSERT(false);
+}
+
+
+docstring const & InsetCommandParams::operator[](string const & name) const
+{
+ int const i = findToken(info_->paramnames, name);
+ BOOST_ASSERT(i >= 0);
+ return params_[i];
+}
+
+
+docstring & InsetCommandParams::operator[](string const & name)
+{
+ int const i = findToken(info_->paramnames, name);
+ BOOST_ASSERT(i >= 0);
+ return params_[i];
+}
+
+
+void InsetCommandParams::clear()
+{
+ for (size_t i = 0; i < info_->n; ++i)
+ params_[i].clear();
+}
+
+
+bool operator==(InsetCommandParams const & o1,
+ InsetCommandParams const & o2)
+{
+ return o1.name_ == o2.name_ &&
+ o1.info_ == o2.info_ &&
+ o1.params_ == o2.params_ &&
+ o1.preview_ == o2.preview_;
+}
+
+
+bool operator!=(InsetCommandParams const & o1,
+ InsetCommandParams const & o2)
+{
+ return !(o1 == o2);
+}
+
+
+} // namespace lyx