X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetCommandParams.cpp;h=f1804a29d9cefb8762b56064b5b86a08d86c0944;hb=0362c6aae73c293d1c20277c12d362acfe0b2ef6;hp=3fd14f7a11c46e15e383ebf4bf3505b1c33cabfd;hpb=3070a548e6c7f1cceeb408a107e8118a9923666d;p=lyx.git diff --git a/src/insets/InsetCommandParams.cpp b/src/insets/InsetCommandParams.cpp index 3fd14f7a11..f1804a29d9 100644 --- a/src/insets/InsetCommandParams.cpp +++ b/src/insets/InsetCommandParams.cpp @@ -5,6 +5,7 @@ * * \author Angus Leeming * \author Georg Baum + * \author Richard Heck * * Full author contact details are available in file CREDITS. */ @@ -13,344 +14,257 @@ #include "InsetCommandParams.h" -#include "debug.h" -#include "gettext.h" +#include "InsetBibitem.h" +#include "InsetBibtex.h" +#include "InsetCitation.h" +#include "InsetFloatList.h" +#include "InsetHFill.h" +#include "InsetHyperlink.h" +#include "InsetInclude.h" +#include "InsetIndex.h" +#include "InsetLabel.h" +#include "InsetNomencl.h" +#include "InsetRef.h" +#include "InsetTOC.h" + #include "Lexer.h" +#include "support/debug.h" #include "support/ExceptionMessage.h" +#include "support/gettext.h" #include "support/lstrings.h" +#include "support/docstream.h" #include +using namespace std; +using namespace lyx::support; namespace lyx { -using support::findToken; - -using std::string; -using std::endl; -using std::ostream; - -using support::ExceptionMessage; -using support::WarningException; +ParamInfo::ParamData::ParamData(std::string const & s, bool b) : + name_(s), optional_(b) +{} -//FIXME There is no reason now for this to take a string argument. -//It'd be much more robust if it took an InsetCode, since then -//the compiler would do some checking for us. -InsetCommandParams::InsetCommandParams(string const & insetType) - : insetType_(insetType), preview_(false) +bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const { - cmdName_ = getDefaultCmd(insetType); - info_ = findInfo(insetType, cmdName_); - BOOST_ASSERT(info_); - params_.resize(info_->n); + return name() == rhs.name() && isOptional() == rhs.isOptional(); } -InsetCommandParams::InsetCommandParams(string const & insetType, - string const & cmdName) - : insetType_(insetType), cmdName_(cmdName), preview_(false) +bool ParamInfo::hasParam(std::string const & name) const { - info_ = findInfo(insetType, cmdName); - BOOST_ASSERT(info_); - params_.resize(info_->n); -} - - -//FIXME This should go into the Insets themselves...so they will tell -//us what parameters they want. -//Should this just vanish in favor of the two arg version, or is there -//a reason to use it in some cases? What should happen in the single -//arg case, then? Maybe use the default? or leave that up to the inset? -InsetCommandParams::CommandInfo const * -InsetCommandParams::findInfo(std::string const & insetType) -{ - // No parameter may be named "preview", because that is a required - // flag for all commands. - - // InsetBibitem - if (insetType == "bibitem") { - static const char * const paramnames[] = {"label", "key", ""}; - static const bool isoptional[] = {true, false}; - static const CommandInfo info = {2, paramnames, isoptional}; - return &info; - } - - // InsetBibtex - if (insetType == "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 - if (insetType == "citation") { - // 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 (insetType == "floatlist") { - static const char * const paramnames[] = {"type", ""}; - static const bool isoptional[] = {false}; - static const CommandInfo info = {1, paramnames, isoptional}; - return &info; - } - - // InsetHfill - if (insetType == "hfill") { - static const char * const paramnames[] = {""}; - static const CommandInfo info = {0, paramnames, 0}; - return &info; + const_iterator it = begin(); + for (; it != end(); ++it) { + if (it->name() == name) + return true; } + return false; +} - // InsetHyperlink - if (insetType == "href") { - static const char * const paramnames[] = - {"name", "target", ""}; - static const bool isoptional[] = {true, false}; - static const CommandInfo info = {2, paramnames, isoptional}; - return &info; - } - // InsetInclude - //FIXME This is really correct only for lstinputlistings, but it shouldn't - //cause a problem before we get things sorted out. Eventually, this calls - //InsetInclude::getParams(cmdName_), or something of the sort. - if (insetType == "include") { - static const char * const paramnames[] = {"filename", "lstparams", ""}; - static const bool isoptional[] = {false, true}; - static const CommandInfo info = {2, paramnames, isoptional}; - return &info; - } +void ParamInfo::add(std::string const & name, bool opt) +{ + info_.push_back(ParamData(name, opt)); +} - // InsetIndex, InsetPrintIndex, InsetLabel - if (insetType == "index" || insetType == "index_print" || insetType == "label") { - static const char * const paramnames[] = {"name", ""}; - static const bool isoptional[] = {false}; - static const CommandInfo info = {1, paramnames, isoptional}; - return &info; - } - // InsetNomencl - if (insetType == "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; +bool ParamInfo::operator==(ParamInfo const & rhs) const +{ + // the idea here is to check each ParamData for equality + const_iterator itL = begin(); + const_iterator itR = rhs.begin(); + const_iterator endL = end(); + const_iterator endR = rhs.end(); + while (true) { + // if they both end together, return true + if (itL == endL && itR == endR) + return true; + // but if one ends before the other, return false + if (itL == endL || itR == endR) + return false; + //check this one for equality + if (*itL != *itR) + return false; + // equal, so check the next one + ++itL; + ++itR; } +} - // InsetPrintNomencl - if (insetType == "nomencl_print") { - static const char * const paramnames[] = {"labelwidth", ""}; - static const bool isoptional[] = {true}; - static const CommandInfo info = {1, paramnames, isoptional}; - return &info; - } - // InsetRef - if (insetType == "ref") { - static const char * const paramnames[] = - {"name", "reference", ""}; - static const bool isoptional[] = {true, false}; - static const CommandInfo info = {2, paramnames, isoptional}; - return &info; - } +InsetCommandParams::InsetCommandParams(InsetCode code) + : insetCode_(code), preview_(false) +{ + cmdName_ = getDefaultCmd(code); + info_ = findInfo(code, cmdName_); +} - // InsetTOC - if (insetType == "toc") { - static const char * const paramnames[] = {"type", ""}; - static const bool isoptional[] = {false}; - static const CommandInfo info = {1, paramnames, isoptional}; - return &info; - } - return 0; +InsetCommandParams::InsetCommandParams(InsetCode code, + string const & cmdName) + : insetCode_(code), cmdName_(cmdName), preview_(false) +{ + info_ = findInfo(code, cmdName); } -//FIXME Will eventually call a static method, etc. -InsetCommandParams::CommandInfo const * - InsetCommandParams::findInfo(std::string const & insetType, - std::string const & cmdName) +ParamInfo const & InsetCommandParams::findInfo( + InsetCode code, string const & cmdName) { - return findInfo(insetType); + switch (code) { + case BIBITEM_CODE: + return InsetBibitem::findInfo(cmdName); + case BIBTEX_CODE: + return InsetBibtex::findInfo(cmdName); + case CITE_CODE: + return InsetCitation::findInfo(cmdName); + case FLOAT_LIST_CODE: + return InsetFloatList::findInfo(cmdName); + case HFILL_CODE: + return InsetHFill::findInfo(cmdName); + case HYPERLINK_CODE: + return InsetHyperlink::findInfo(cmdName); + case INCLUDE_CODE: + return InsetInclude::findInfo(cmdName); + case INDEX_PRINT_CODE: + return InsetPrintIndex::findInfo(cmdName); + case LABEL_CODE: + return InsetLabel::findInfo(cmdName); + case NOMENCL_CODE: + return InsetNomencl::findInfo(cmdName); + case NOMENCL_PRINT_CODE: + return InsetPrintNomencl::findInfo(cmdName); + case REF_CODE: + return InsetRef::findInfo(cmdName); + case TOC_CODE: + return InsetTOC::findInfo(cmdName); + default: + BOOST_ASSERT(false); + } + static const ParamInfo pi; + return pi; // to silence the warning } -//FIXME Should call InsetBibitem::getDefaultCmd(), eg -std::string InsetCommandParams::getDefaultCmd(std::string insetType) { - if (insetType == "bibitem") - return "bibitem"; - if (insetType == "bibtex") - return "bibtex"; //this is an unused dummy - if (insetType == "citation") - return "cite"; - if (insetType == "floatlist") - return "listoftables"; - if (insetType == "hfill") - return "hfill"; - if (insetType == "href") - return "href"; - if (insetType == "include") - return "include"; - if (insetType == "index") - return "index"; - if (insetType == "index_print") - return "print_index"; - if (insetType == "label") - return "label"; - if (insetType == "nomenclature") - return "nomenclature"; - if (insetType == "nomencl_print") - return "printnomenclature"; - if (insetType == "ref") - return "ref"; - if (insetType == "toc") - return "tableofcontents"; - BOOST_ASSERT(false); - return ""; +string InsetCommandParams::getDefaultCmd(InsetCode code) { + switch (code) { + case BIBITEM_CODE: + return InsetBibitem::defaultCommand(); + case BIBTEX_CODE: + return InsetBibtex::defaultCommand(); + case CITE_CODE: + return InsetCitation::defaultCommand(); + case FLOAT_LIST_CODE: + return InsetFloatList::defaultCommand(); + case HFILL_CODE: + return InsetHFill::defaultCommand(); + case HYPERLINK_CODE: + return InsetHyperlink::defaultCommand(); + case INCLUDE_CODE: + return InsetInclude::defaultCommand(); + case INDEX_PRINT_CODE: + return InsetPrintIndex::defaultCommand(); + case LABEL_CODE: + return InsetLabel::defaultCommand(); + case NOMENCL_CODE: + return InsetNomencl::defaultCommand(); + case NOMENCL_PRINT_CODE: + return InsetPrintNomencl::defaultCommand(); + case REF_CODE: + return InsetRef::defaultCommand(); + case TOC_CODE: + return InsetTOC::defaultCommand(); + default: + BOOST_ASSERT(false); + } + return string(); // silence the warning } -void InsetCommandParams::setCmdName(string const & name) +bool InsetCommandParams::isCompatibleCommand( + InsetCode code, string const & s) { - //FIXME Check command compatibility - cmdName_ = name; - BOOST_ASSERT(!insetType_.empty()); - CommandInfo const * const info = findInfo(insetType_, cmdName_); - 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]; + switch (code) { + case BIBITEM_CODE: + return InsetBibitem::isCompatibleCommand(s); + case BIBTEX_CODE: + return InsetBibtex::isCompatibleCommand(s); + case CITE_CODE: + return InsetCitation::isCompatibleCommand(s); + case FLOAT_LIST_CODE: + return InsetFloatList::isCompatibleCommand(s); + case HFILL_CODE: + return InsetHFill::isCompatibleCommand(s); + case HYPERLINK_CODE: + return InsetHyperlink::isCompatibleCommand(s); + case INCLUDE_CODE: + return InsetInclude::isCompatibleCommand(s); + case INDEX_PRINT_CODE: + return InsetPrintIndex::isCompatibleCommand(s); + case LABEL_CODE: + return InsetLabel::isCompatibleCommand(s); + case NOMENCL_CODE: + return InsetNomencl::isCompatibleCommand(s); + case NOMENCL_PRINT_CODE: + return InsetPrintNomencl::isCompatibleCommand(s); + case REF_CODE: + return InsetRef::isCompatibleCommand(s); + case TOC_CODE: + return InsetTOC::isCompatibleCommand(s); + default: + BOOST_ASSERT(false); } - info_ = info; - std::swap(params, params_); + return false; // silence the warning } -void InsetCommandParams::scanCommand(string const & cmd) +void InsetCommandParams::setCmdName(string const & name) { - string tcmdname, toptions, tsecoptions, tcontents; - - if (cmd.empty()) return; - - 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 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; - } else { - --nestdepth; - } - } - 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: { - char const b = i? cmd[i-1]: 0; - if (c == '\\') { - state = CMDNAME; - } 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; - } - } + if (!isCompatibleCommand(insetCode_, cmdName_)){ + LYXERR0("InsetCommand: Incompatible command name " << + name << "."); + throw ExceptionMessage(WarningException, _("InsetCommand Error: "), + _("Incompatible command name.")); } - // 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); - - LYXERR(Debug::PARSER) << "Command <" << cmd - << "> == <" << to_utf8(getCommand()) - << "> == <" << getCmdName() - << '|' << getContents() - << '|' << getOptions() - << '|' << getSecOptions() << '>' << endl; + cmdName_ = name; + info_ = findInfo(insetCode_, cmdName_); } void InsetCommandParams::read(Lexer & lex) { - //FIXME if (lex.isOK()) { lex.next(); - string insetType = lex.getString(); - if (!insetType_.empty() && insetType != insetType_) { - lex.printError("InsetCommand: Attempt to change type of parameters."); - throw ExceptionMessage(WarningException, _("InsetCommand Error: "), - from_utf8("Attempt to change type of parameters.")); + string const insetType = lex.getString(); + InsetCode const code = insetCode(insetType); + if (code != insetCode_) { + lex.printError("InsetCommandParams: Attempt to change type of inset."); + throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "), + _("Attempt to change type of parameters.")); } - // OK, we survived... - insetType_ = insetType; } if (lex.isOK()) { lex.next(); - string test = lex.getString(); + string const test = lex.getString(); if (test != "LatexCommand") { - lex.printError("InsetCommand: no LatexCommand line found."); - throw ExceptionMessage(WarningException, _("InsetCommand error:"), - from_utf8("Can't find LatexCommand line.")); + lex.printError("InsetCommandParams: No LatexCommand line found."); + throw ExceptionMessage(WarningException, _("InsetCommandParams error:"), + _("Can't find LatexCommand line.")); } } lex.next(); cmdName_ = lex.getString(); - //FIXME - //check that this command is ok with the inset... - //so that'll be some kind of static call, depending - //upon what insetType_ is. - //it's possible that should go into InsetCommand.cpp, - //or maybe it's a standalone function. - info_ = findInfo(insetType_, cmdName_); - if (!info_) { - lex.printError("InsetCommand: Unknown inset name `$$Token'"); - throw ExceptionMessage(WarningException, - _("Unknown inset name: "), from_utf8(insetType_)); + if (!isCompatibleCommand(insetCode_, cmdName_)){ + lex.printError("InsetCommandParams: Incompatible command name " + cmdName_ + "."); + throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "), + _("Incompatible command name.")); } + + info_ = findInfo(insetCode_, cmdName_); string token; while (lex.isOK()) { @@ -358,20 +272,18 @@ void InsetCommandParams::read(Lexer & lex) 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) { + if (info_.hasParam(token)) { lex.next(true); - params_[i] = lex.getDocString(); + params_[token] = lex.getDocString(); } else { lex.printError("Unknown parameter name `$$Token' for command " + cmdName_); throw ExceptionMessage(WarningException, - _("Inset Command: ") + from_ascii(cmdName_), + _("InsetCommandParams: ") + from_ascii(cmdName_), _("Unknown parameter name: ") + from_utf8(token)); } } @@ -387,14 +299,22 @@ void InsetCommandParams::read(Lexer & lex) void InsetCommandParams::write(ostream & os) const { - os << "CommandInset " << insetType_ << '\n'; + os << "CommandInset " << insetType() << '\n'; os << "LatexCommand " << cmdName_ << '\n'; - for (size_t i = 0; i < info_->n; ++i) - if (!params_[i].empty()) + if (preview_) + os << "preview true\n"; + ParamInfo::const_iterator it = info_.begin(); + ParamInfo::const_iterator end = info_.end(); + for (; it != end; ++it) { + std::string const & name = it->name(); + docstring const & data = (*this)[name]; + if (!data.empty()) { // FIXME UNICODE - os << info_->paramnames[i] << ' ' - << Lexer::quoteString(to_utf8(params_[i])) + os << name << ' ' + << Lexer::quoteString(to_utf8(data)) << '\n'; + } + } } @@ -402,28 +322,36 @@ docstring const InsetCommandParams::getCommand() const { docstring s = '\\' + from_ascii(cmdName_); 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] + ']'; + ParamInfo::const_iterator it = info_.begin(); + ParamInfo::const_iterator end = info_.end(); + for (; it != end; ++it) { + std::string const & name = it->name(); + docstring const & data = (*this)[name]; + if (!it->isOptional()) { + s += '{' + data + '}'; + noparam = false; + continue; + } + if (!data.empty()) { + s += '[' + data + ']'; + noparam = false; + continue; + } + // This param is therefore optional but empty. + // But we need to write it anyway if nonempty + // optional parameters follow before the next + // required parameter. + 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; + break; } - } else { - s += '{' + params_[i] + '}'; - noparam = false; } } if (noparam) @@ -434,112 +362,54 @@ docstring const InsetCommandParams::getCommand() const } -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 " - << insetType_ << " 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 " - << insetType_ << " 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 " - << insetType_ << " 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 " - << insetType_ << " inset." << endl; +namespace { + //predicate for what follows + bool paramIsNonOptional(ParamInfo::ParamData pi) + { + return !pi.isOptional(); + } } - -void InsetCommandParams::setContents(std::string const & c) +docstring const InsetCommandParams::getFirstNonOptParam() const { - for (size_t i = 0; i < info_->n; ++i) - if (!info_->optional[i]) { - params_[i] = from_utf8(c); - return; - } - BOOST_ASSERT(false); + ParamInfo::const_iterator it = + find_if(info_.begin(), info_.end(), paramIsNonOptional); + if (it == info_.end()) + BOOST_ASSERT(false); + return (*this)[it->name()]; } docstring const & InsetCommandParams::operator[](string const & name) const { - int const i = findToken(info_->paramnames, name); - BOOST_ASSERT(i >= 0); - return params_[i]; + static const docstring dummy; //so we don't return a ref to temporary + if (!info_.hasParam(name)) + BOOST_ASSERT(false); + ParamMap::const_iterator data = params_.find(name); + if (data == params_.end() || data->second.empty()) + return dummy; + return data->second; } docstring & InsetCommandParams::operator[](string const & name) { - int const i = findToken(info_->paramnames, name); - BOOST_ASSERT(i >= 0); - return params_[i]; + if (!info_.hasParam(name)) + BOOST_ASSERT(false); + return params_[name]; } void InsetCommandParams::clear() { - for (size_t i = 0; i < info_->n; ++i) - params_[i].clear(); + params_.clear(); } bool operator==(InsetCommandParams const & o1, InsetCommandParams const & o2) { - return o1.insetType_ == o2.insetType_ && + return o1.insetCode_ == o2.insetCode_ && o1.cmdName_ == o2.cmdName_ && o1.info_ == o2.info_ && o1.params_ == o2.params_ &&