]> git.lyx.org Git - lyx.git/blobdiff - src/insets/insetcommandparams.C
* src/LyXAction.C: mark goto-clear-bookmark as working without buffer
[lyx.git] / src / insets / insetcommandparams.C
index ddb78faef0b7f9cd438ca2136a3dd4b64c2d4771..bd0c208ef2a7603e4dbc9bb076e96821eb204fe7 100644 (file)
@@ -4,6 +4,7 @@
  * Licence details can be found in the file COPYING.
  *
  * \author Angus Leeming
+ * \author Georg Baum
  *
  * Full author contact details are available in file CREDITS.
  */
 #include "debug.h"
 #include "lyxlex.h"
 
+#include "support/lstrings.h"
+
+#include <boost/assert.hpp>
+
+
+namespace lyx {
+
+using support::findToken;
 
 using std::string;
 using std::endl;
 using std::ostream;
 
 
-InsetCommandParams::InsetCommandParams()
-{}
+InsetCommandParams::InsetCommandParams(string const & name)
+       : name_(name), preview_(false)
+{
+       info_ = findInfo(name);
+       BOOST_ASSERT(info_);
+       params_.resize(info_->n);
+}
+
+
+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", ""};
+               static const bool isoptional[] = {true, false};
+               static const CommandInfo info = {2, paramnames, isoptional};
+               return &info;
+       }
 
-InsetCommandParams::InsetCommandParams(string const & n,
-                                       string const & c,
-                                       string const & o,
-                                       string const & s)
-       : cmdname(n), contents(c), options(o), sec_options(s),
-       preview_(false)
-{}
+       // 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;
+       }
+
+       // 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;
+       }
+
+       // 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;
+}
+
+
+void InsetCommandParams::setCmdName(string const & name)
+{
+       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)
@@ -98,7 +245,7 @@ void InsetCommandParams::scanCommand(string const & cmd)
 
        if (lyxerr.debugging(Debug::PARSER))
                lyxerr << "Command <" <<  cmd
-                      << "> == <" << getCommand()
+                      << "> == <" << to_utf8(getCommand())
                       << "> == <" << getCmdName()
                       << '|' << getContents()
                       << '|' << getOptions()
@@ -108,24 +255,32 @@ void InsetCommandParams::scanCommand(string const & cmd)
 
 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.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. "
@@ -136,33 +291,161 @@ void InsetCommandParams::read(LyXLex & lex)
 
 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() + ']';
-       if (!getSecOptions().empty()) {
-               // for cases like \command[][sec_option]{arg}
-               if (getOptions().empty()) s += "[]";
-       s += '[' + getSecOptions() + ']';
+       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;
+               }
        }
-       s += '{' + getContents() + '}';
+       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.getCmdName() == o2.getCmdName()
-               && o1.getContents() == o2.getContents()
-               && o1.getOptions() == o2.getOptions()
-               && o1.getSecOptions() == o2.getSecOptions()
-               && o1.preview() == o2.preview();
+       return o1.name_ == o2.name_ &&
+              o1.info_ == o2.info_ &&
+              o1.params_ == o2.params_ &&
+              o1.preview_ == o2.preview_;
 }
 
 
@@ -171,3 +454,6 @@ bool operator!=(InsetCommandParams const & o1,
 {
        return !(o1 == o2);
 }
+
+
+} // namespace lyx