2 * \file insetcommandparams.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS.
14 #include "insetcommandparams.h"
19 #include "support/lstrings.h"
21 #include <boost/assert.hpp>
26 using support::findToken;
33 InsetCommandParams::InsetCommandParams(string const & name)
34 : name_(name), preview_(false)
36 info_ = findInfo(name);
38 params_.resize(info_->n);
42 InsetCommandParams::CommandInfo const *
43 InsetCommandParams::findInfo(std::string const & name)
45 // No parameter may be named "preview", because that is a required
46 // flag for all commands.
49 if (name == "bibitem") {
50 static const char * const paramnames[] = {"label", "key", ""};
51 static const bool isoptional[] = {true, false};
52 static const CommandInfo info = {2, paramnames, isoptional};
57 if (name == "bibtex") {
58 static const char * const paramnames[] =
59 {"options", "btprint", "bibfiles", ""};
60 static const bool isoptional[] = {true, true, false};
61 static const CommandInfo info = {3, paramnames, isoptional};
66 // FIXME: Use is_possible_cite_command() in
67 // src/frontends/controllers/biblio.C, see comment in src/factory.C.
68 if (name == "cite" || name == "citet" || name == "citep" || name == "citealt" ||
69 name == "citealp" || name == "citeauthor" || name == "citeyear" ||
70 name == "citeyearpar" || name == "citet*" || name == "citep*" ||
71 name == "citealt*" || name == "citealp*" ||
72 name == "citeauthor*" || name == "Citet" || name == "Citep" ||
73 name == "Citealt" || name == "Citealp" || name == "Citeauthor" ||
74 name == "Citet*" || name == "Citep*" || name == "Citealt*" ||
75 name == "Citealp*" || name == "Citeauthor*" ||
76 name == "citefield" || name == "citetitle" || name == "cite*") {
77 // standard cite does only take one argument if jurabib is
78 // not used, but jurabib extends this to two arguments, so
79 // we have to allow both here. InsetCitation takes care that
80 // LaTeX output is nevertheless correct.
81 static const char * const paramnames[] =
82 {"after", "before", "key", ""};
83 static const bool isoptional[] = {true, true, false};
84 static const CommandInfo info = {3, paramnames, isoptional};
89 if (name == "floatlist") {
90 static const char * const paramnames[] = {"type", ""};
91 static const bool isoptional[] = {false};
92 static const CommandInfo info = {1, paramnames, isoptional};
97 if (name == "hfill") {
98 static const char * const paramnames[] = {""};
99 static const CommandInfo info = {0, paramnames, 0};
104 if (name == "include" || name == "input" || name == "verbatiminput" ||
105 name == "verbatiminput*") {
106 static const char * const paramnames[] = {"filename", ""};
107 static const bool isoptional[] = {false};
108 static const CommandInfo info = {1, paramnames, isoptional};
112 // InsetIndex, InsetPrintIndex, InsetLabel
113 if (name == "index" || name == "printindex" || name == "label") {
114 static const char * const paramnames[] = {"name", ""};
115 static const bool isoptional[] = {false};
116 static const CommandInfo info = {1, paramnames, isoptional};
121 if (name == "nomenclature") {
122 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
123 static const bool isoptional[] = {true, false, false};
124 static const CommandInfo info = {3, paramnames, isoptional};
129 if (name == "printnomenclature") {
130 static const char * const paramnames[] = {"labelwidth", ""};
131 static const bool isoptional[] = {true};
132 static const CommandInfo info = {1, paramnames, isoptional};
137 if (name == "eqref" || name == "pageref" || name == "vpageref" ||
138 name == "vref" || name == "prettyref" || name == "ref") {
139 static const char * const paramnames[] =
140 {"name", "reference", ""};
141 static const bool isoptional[] = {true, false};
142 static const CommandInfo info = {2, paramnames, isoptional};
147 if (name == "tableofcontents") {
148 static const char * const paramnames[] = {"type", ""};
149 static const bool isoptional[] = {false};
150 static const CommandInfo info = {1, paramnames, isoptional};
155 if (name == "htmlurl" || name == "url") {
156 static const char * const paramnames[] =
157 {"name", "target", ""};
158 static const bool isoptional[] = {true, false};
159 static const CommandInfo info = {2, paramnames, isoptional};
167 void InsetCommandParams::setCmdName(string const & name)
170 CommandInfo const * const info = findInfo(name);
172 ParamVector params(info->n);
173 // Overtake parameters with the same name
174 for (size_t i = 0; i < info_->n; ++i) {
175 int j = findToken(info->paramnames, info_->paramnames[i]);
177 params[j] = params_[i];
180 std::swap(params, params_);
184 void InsetCommandParams::scanCommand(string const & cmd)
186 string tcmdname, toptions, tsecoptions, tcontents;
188 if (cmd.empty()) return;
190 enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
192 // Used to handle things like \command[foo[bar]]{foo{bar}}
195 for (string::size_type i = 0; i < cmd.length(); ++i) {
196 char const c = cmd[i];
197 if ((state == CMDNAME && c == ' ') ||
198 (state == CMDNAME && c == '[') ||
199 (state == CMDNAME && c == '{')) {
202 if ((state == OPTION && c == ']') ||
203 (state == SECOPTION && c == ']') ||
204 (state == CONTENT && c == '}')) {
205 if (nestdepth == 0) {
211 if ((state == OPTION && c == '[') ||
212 (state == SECOPTION && c == '[') ||
213 (state == CONTENT && c == '{')) {
217 case CMDNAME: tcmdname += c; break;
218 case OPTION: toptions += c; break;
219 case SECOPTION: tsecoptions += c; break;
220 case CONTENT: tcontents += c; break;
222 char const b = i? cmd[i-1]: 0;
225 } else if (c == '[' && b != ']') {
227 nestdepth = 0; // Just to be sure
228 } else if (c == '[' && b == ']') {
230 nestdepth = 0; // Just to be sure
231 } else if (c == '{') {
233 nestdepth = 0; // Just to be sure
240 // Don't mess with this.
241 if (!tcmdname.empty()) setCmdName(tcmdname);
242 if (!toptions.empty()) setOptions(toptions);
243 if (!tsecoptions.empty()) setSecOptions(tsecoptions);
244 if (!tcontents.empty()) setContents(tcontents);
246 if (lyxerr.debugging(Debug::PARSER))
247 lyxerr << "Command <" << cmd
248 << "> == <" << to_utf8(getCommand())
249 << "> == <" << getCmdName()
250 << '|' << getContents()
251 << '|' << getOptions()
252 << '|' << getSecOptions() << '>' << endl;
256 void InsetCommandParams::read(LyXLex & lex)
260 name_ = lex.getString();
261 info_ = findInfo(name_);
263 lex.printError("InsetCommand: Unknown inset name `$$Token'");
269 token = lex.getString();
270 if (token == "\\end_inset")
272 // FIXME Why is preview_ read but not written?
273 if (token == "preview") {
275 preview_ = lex.getBool();
278 int const i = findToken(info_->paramnames, token);
281 params_[i] = lex.getDocString();
283 lex.printError("Unknown parameter name `$$Token' for command " + name_);
285 if (token != "\\end_inset") {
286 lex.printError("Missing \\end_inset at this point. "
292 void InsetCommandParams::write(ostream & os) const
294 os << "LatexCommand " << name_ << '\n';
295 for (size_t i = 0; i < info_->n; ++i)
296 if (!params_[i].empty())
298 os << info_->paramnames[i] << ' '
299 << LyXLex::quoteString(to_utf8(params_[i]))
304 docstring const InsetCommandParams::getCommand() const
306 docstring s = '\\' + from_ascii(name_);
308 for (size_t i = 0; i < info_->n; ++i) {
309 if (info_->optional[i]) {
310 if (params_[i].empty()) {
311 // We need to write this parameter even if
312 // it is empty if nonempty optional parameters
313 // follow before the next required parameter.
314 for (size_t j = i + 1; j < info_->n; ++j) {
315 if (!info_->optional[j])
317 if (!params_[j].empty()) {
324 s += '[' + params_[i] + ']';
328 s += '{' + params_[i] + '}';
333 // Make sure that following stuff does not change the
340 std::string const InsetCommandParams::getOptions() const
342 for (size_t i = 0; i < info_->n; ++i)
343 if (info_->optional[i])
344 return to_utf8(params_[i]);
345 lyxerr << "Programming error: get nonexisting option in "
346 << name_ << " inset." << endl;;
351 std::string const InsetCommandParams::getSecOptions() const
354 for (size_t i = 0; i < info_->n; ++i)
355 if (info_->optional[i]) {
359 return to_utf8(params_[i]);
361 // Happens in InsetCitation
362 lyxerr << "Programming error: get nonexisting second option in "
363 << name_ << " inset." << endl;;
368 std::string const InsetCommandParams::getContents() const
370 for (size_t i = 0; i < info_->n; ++i)
371 if (!info_->optional[i])
372 return to_utf8(params_[i]);
378 void InsetCommandParams::setOptions(std::string const & o)
380 for (size_t i = 0; i < info_->n; ++i)
381 if (info_->optional[i]) {
382 params_[i] = from_utf8(o);
385 lyxerr << "Programming error: set nonexisting option in "
386 << name_ << " inset." << endl;;
390 void InsetCommandParams::setSecOptions(std::string const & s)
393 for (size_t i = 0; i < info_->n; ++i)
394 if (info_->optional[i]) {
398 params_[i] = from_utf8(s);
402 // Happens in InsetCitation
403 lyxerr << "Programming error: set nonexisting second option in "
404 << name_ << " inset." << endl;;
408 void InsetCommandParams::setContents(std::string const & c)
410 for (size_t i = 0; i < info_->n; ++i)
411 if (!info_->optional[i]) {
412 params_[i] = from_utf8(c);
419 docstring const & InsetCommandParams::operator[](string const & name) const
421 int const i = findToken(info_->paramnames, name);
422 BOOST_ASSERT(i >= 0);
427 docstring & InsetCommandParams::operator[](string const & name)
429 int const i = findToken(info_->paramnames, name);
430 BOOST_ASSERT(i >= 0);
435 void InsetCommandParams::clear()
437 for (size_t i = 0; i < info_->n; ++i)
442 bool operator==(InsetCommandParams const & o1,
443 InsetCommandParams const & o2)
445 return o1.name_ == o2.name_ &&
446 o1.info_ == o2.info_ &&
447 o1.params_ == o2.params_ &&
448 o1.preview_ == o2.preview_;
452 bool operator!=(InsetCommandParams const & o1,
453 InsetCommandParams const & o2)