X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2Finsetcommandparams.C;h=bd0c208ef2a7603e4dbc9bb076e96821eb204fe7;hb=e28331ed63062dea10d0a21b9ec12034b4b17b9a;hp=5da44d6bf9c5abc94ac597abdf678e89c8b4e444;hpb=5e802bab4d0e96ccfa67a76cbab2086f8380895d;p=lyx.git diff --git a/src/insets/insetcommandparams.C b/src/insets/insetcommandparams.C index 5da44d6bf9..bd0c208ef2 100644 --- a/src/insets/insetcommandparams.C +++ b/src/insets/insetcommandparams.C @@ -1,87 +1,206 @@ -/* 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 #include "insetcommandparams.h" -#include "lyxlex.h" + #include "debug.h" +#include "lyxlex.h" -#include "support/LOstream.h" +#include "support/lstrings.h" -using std::ostream; -using std::endl; +#include -InsetCommandParams::InsetCommandParams() -{} +namespace lyx { +using support::findToken; -InsetCommandParams::InsetCommandParams(string const & n, - string const & c, - string const & o) - : cmdname(n), contents(c), options(o), preview_(false) -{} +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; + } + + // 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; } - cmdname = b.substr(0, idx); - string tmp = b.substr(idx+4); + // 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; +} + - idx = tmp.find("|++|"); - if (idx == string::npos) { - contents = tmp; - options = ""; - } else { - contents = tmp.substr(0, idx); - options = tmp.substr(idx+4); +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) { - 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; @@ -90,61 +209,78 @@ void InsetCommandParams::scanCommand(string const & cmd) } } 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.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. " @@ -155,27 +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()+']'; - 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.getCmdName() == o2.getCmdName() - && o1.getContents() == o2.getContents() - && o1.getOptions() == o2.getOptions() - && o1.preview() == o2.preview(); + return o1.name_ == o2.name_ && + o1.info_ == o2.info_ && + o1.params_ == o2.params_ && + o1.preview_ == o2.preview_; } @@ -184,3 +454,6 @@ bool operator!=(InsetCommandParams const & o1, { return !(o1 == o2); } + + +} // namespace lyx