2 * \file InsetCommandParams.cpp
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"
20 #include "support/ExceptionMessage.h"
21 #include "support/lstrings.h"
23 #include <boost/assert.hpp>
28 using support::findToken;
34 using support::ExceptionMessage;
35 using support::WarningException;
38 InsetCommandParams::InsetCommandParams(InsetCode code)
39 : insetCode_(code), preview_(false)
41 cmdName_ = getDefaultCmd(code);
42 info_ = findInfo(code, cmdName_);
44 params_.resize(info_->n);
48 InsetCommandParams::InsetCommandParams(InsetCode code,
49 string const & cmdName)
50 : insetCode_(code), cmdName_(cmdName), preview_(false)
52 info_ = findInfo(code, cmdName);
54 params_.resize(info_->n);
58 //FIXME This should go into the Insets themselves...so they will tell
59 //us what parameters they want.
60 //Should this just vanish in favor of the two arg version, or is there
61 //a reason to use it in some cases? What should happen in the single
62 //arg case, then? Maybe use the default? or leave that up to the inset?
63 InsetCommandParams::CommandInfo const *
64 InsetCommandParams::findInfo(InsetCode code)
66 // No parameter may be named "preview", because that is a required
67 // flag for all commands.
71 static const char * const paramnames[] = {"label", "key", ""};
72 static const bool isoptional[] = {true, false};
73 static const CommandInfo info = {2, paramnames, isoptional};
77 static const char * const paramnames[] =
78 {"options", "btprint", "bibfiles", ""};
79 static const bool isoptional[] = {true, true, false};
80 static const CommandInfo info = {3, paramnames, isoptional};
84 // standard cite does only take one argument if jurabib is
85 // not used, but jurabib extends this to two arguments, so
86 // we have to allow both here. InsetCitation takes care that
87 // LaTeX output is nevertheless correct.
88 static const char * const paramnames[] =
89 {"after", "before", "key", ""};
90 static const bool isoptional[] = {true, true, false};
91 static const CommandInfo info = {3, paramnames, isoptional};
94 case FLOAT_LIST_CODE: {
95 static const char * const paramnames[] = {"type", ""};
96 static const bool isoptional[] = {false};
97 static const CommandInfo info = {1, paramnames, isoptional};
101 static const char * const paramnames[] = {""};
102 static const CommandInfo info = {0, paramnames, 0};
105 case HYPERLINK_CODE: {
106 static const char * const paramnames[] =
107 {"name", "target", ""};
108 static const bool isoptional[] = {true, false};
109 static const CommandInfo info = {2, paramnames, isoptional};
113 //This is only correct for the case of listings, but it'll do for now.
114 //In the other cases, this second parameter should just be empty.
115 static const char * const paramnames[] = {"filename", "lstparams", ""};
116 static const bool isoptional[] = {false, true};
117 static const CommandInfo info = {2, paramnames, isoptional};
121 case INDEX_PRINT_CODE:
123 static const char * const paramnames[] = {"name", ""};
124 static const bool isoptional[] = {false};
125 static const CommandInfo info = {1, paramnames, isoptional};
129 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
130 static const bool isoptional[] = {true, false, false};
131 static const CommandInfo info = {3, paramnames, isoptional};
134 case NOMENCL_PRINT_CODE: {
135 static const char * const paramnames[] = {"labelwidth", ""};
136 static const bool isoptional[] = {true};
137 static const CommandInfo info = {1, paramnames, isoptional};
141 static const char * const paramnames[] =
142 {"name", "reference", ""};
143 static const bool isoptional[] = {true, false};
144 static const CommandInfo info = {2, paramnames, isoptional};
148 static const char * const paramnames[] = {"type", ""};
149 static const bool isoptional[] = {false};
150 static const CommandInfo info = {1, paramnames, isoptional};
160 //FIXME Will eventually call a static method, etc.
161 InsetCommandParams::CommandInfo const *
162 InsetCommandParams::findInfo(InsetCode code,
163 std::string const &/* cmdName*/)
165 return findInfo(code);
169 //FIXME Should call InsetBibitem::getDefaultCmd(), eg
170 std::string InsetCommandParams::getDefaultCmd(InsetCode code) {
175 return "bibtex"; //this is an unused dummy
178 case FLOAT_LIST_CODE:
179 return "listoftables";
188 case INDEX_PRINT_CODE:
193 return "nomenclature";
194 case NOMENCL_PRINT_CODE:
195 return "printnomenclature";
199 return "tableofcontents";
207 void InsetCommandParams::setCmdName(string const & name)
209 //FIXME Check command compatibility
211 CommandInfo const * const info = findInfo(insetCode_, cmdName_);
213 lyxerr << "Command '" << name << "' is not compatible with a '" <<
214 insetType() << "' inset." << std::endl;
217 ParamVector params(info->n);
218 // Overtake parameters with the same name
219 for (size_t i = 0; i < info_->n; ++i) {
220 int j = findToken(info->paramnames, info_->paramnames[i]);
222 params[j] = params_[i];
225 std::swap(params, params_);
229 void InsetCommandParams::scanCommand(string const & cmd)
231 string tcmdname, toptions, tsecoptions, tcontents;
233 if (cmd.empty()) return;
235 enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
237 // Used to handle things like \command[foo[bar]]{foo{bar}}
240 for (string::size_type i = 0; i < cmd.length(); ++i) {
241 char const c = cmd[i];
242 if ((state == CMDNAME && c == ' ') ||
243 (state == CMDNAME && c == '[') ||
244 (state == CMDNAME && c == '{')) {
247 if ((state == OPTION && c == ']') ||
248 (state == SECOPTION && c == ']') ||
249 (state == CONTENT && c == '}')) {
250 if (nestdepth == 0) {
256 if ((state == OPTION && c == '[') ||
257 (state == SECOPTION && c == '[') ||
258 (state == CONTENT && c == '{')) {
262 case CMDNAME: tcmdname += c; break;
263 case OPTION: toptions += c; break;
264 case SECOPTION: tsecoptions += c; break;
265 case CONTENT: tcontents += c; break;
267 char const b = i? cmd[i-1]: 0;
270 } else if (c == '[' && b != ']') {
272 nestdepth = 0; // Just to be sure
273 } else if (c == '[' && b == ']') {
275 nestdepth = 0; // Just to be sure
276 } else if (c == '{') {
278 nestdepth = 0; // Just to be sure
285 // Don't mess with this.
286 if (!tcmdname.empty()) setCmdName(tcmdname);
287 if (!toptions.empty()) setOptions(toptions);
288 if (!tsecoptions.empty()) setSecOptions(tsecoptions);
289 if (!tcontents.empty()) setContents(tcontents);
291 LYXERR(Debug::PARSER) << "Command <" << cmd
292 << "> == <" << to_utf8(getCommand())
293 << "> == <" << getCmdName()
294 << '|' << getContents()
295 << '|' << getOptions()
296 << '|' << getSecOptions() << '>' << endl;
300 void InsetCommandParams::read(Lexer & lex)
305 string const insetType = lex.getString();
306 InsetCode const code = insetCode(insetType);
307 if (code != insetCode_) {
308 lex.printError("InsetCommand: Attempt to change type of parameters.");
309 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
310 from_utf8("Attempt to change type of parameters."));
316 string const test = lex.getString();
317 if (test != "LatexCommand") {
318 lex.printError("InsetCommand: no LatexCommand line found.");
319 throw ExceptionMessage(WarningException, _("InsetCommand error:"),
320 from_utf8("Can't find LatexCommand line."));
324 cmdName_ = lex.getString();
326 //check that this command is ok with the inset...
327 //so that'll be some kind of static call, depending
328 //upon what insetType_ is.
329 //it's possible that should go into InsetCommand.cpp,
330 //or maybe it's a standalone function.
331 info_ = findInfo(insetCode_, cmdName_);
333 lex.printError("InsetCommand: Unknown inset name `$$Token'");
334 throw ExceptionMessage(WarningException,
335 _("Unknown inset name: "), from_utf8(insetType()));
341 token = lex.getString();
342 if (token == "\\end_inset")
344 // FIXME Why is preview_ read but not written?
345 if (token == "preview") {
347 preview_ = lex.getBool();
350 int const i = findToken(info_->paramnames, token);
353 params_[i] = lex.getDocString();
355 lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
356 throw ExceptionMessage(WarningException,
357 _("Inset Command: ") + from_ascii(cmdName_),
358 _("Unknown parameter name: ") + from_utf8(token));
361 if (token != "\\end_inset") {
362 lex.printError("Missing \\end_inset at this point. "
364 throw ExceptionMessage(WarningException,
365 _("Missing \\end_inset at this point."),
371 void InsetCommandParams::write(ostream & os) const
373 os << "CommandInset " << insetType() << '\n';
374 os << "LatexCommand " << cmdName_ << '\n';
375 for (size_t i = 0; i < info_->n; ++i)
376 if (!params_[i].empty())
378 os << info_->paramnames[i] << ' '
379 << Lexer::quoteString(to_utf8(params_[i]))
384 docstring const InsetCommandParams::getCommand() const
386 docstring s = '\\' + from_ascii(cmdName_);
388 for (size_t i = 0; i < info_->n; ++i) {
389 if (info_->optional[i]) {
390 if (params_[i].empty()) {
391 // We need to write this parameter even if
392 // it is empty if nonempty optional parameters
393 // follow before the next required parameter.
394 for (size_t j = i + 1; j < info_->n; ++j) {
395 if (!info_->optional[j])
397 if (!params_[j].empty()) {
404 s += '[' + params_[i] + ']';
408 s += '{' + params_[i] + '}';
413 // Make sure that following stuff does not change the
420 std::string const InsetCommandParams::getOptions() const
422 for (size_t i = 0; i < info_->n; ++i)
423 if (info_->optional[i])
424 return to_utf8(params_[i]);
425 lyxerr << "Programming error: get nonexisting option in "
426 << insetType() << " inset." << endl;
431 std::string const InsetCommandParams::getSecOptions() const
434 for (size_t i = 0; i < info_->n; ++i)
435 if (info_->optional[i]) {
439 return to_utf8(params_[i]);
441 // Happens in InsetCitation
442 lyxerr << "Programming error: get nonexisting second option in "
443 << insetType() << " inset." << endl;
448 std::string const InsetCommandParams::getContents() const
450 for (size_t i = 0; i < info_->n; ++i)
451 if (!info_->optional[i])
452 return to_utf8(params_[i]);
458 void InsetCommandParams::setOptions(std::string const & o)
460 for (size_t i = 0; i < info_->n; ++i)
461 if (info_->optional[i]) {
462 params_[i] = from_utf8(o);
465 lyxerr << "Programming error: set nonexisting option in "
466 << insetType() << " inset." << endl;
470 void InsetCommandParams::setSecOptions(std::string const & s)
473 for (size_t i = 0; i < info_->n; ++i)
474 if (info_->optional[i]) {
478 params_[i] = from_utf8(s);
482 // Happens in InsetCitation
483 lyxerr << "Programming error: set nonexisting second option in "
484 << insetType() << " inset." << endl;
488 void InsetCommandParams::setContents(std::string const & c)
490 for (size_t i = 0; i < info_->n; ++i)
491 if (!info_->optional[i]) {
492 params_[i] = from_utf8(c);
499 docstring const & InsetCommandParams::operator[](string const & name) const
501 int const i = findToken(info_->paramnames, name);
502 BOOST_ASSERT(i >= 0);
507 docstring & InsetCommandParams::operator[](string const & name)
509 int const i = findToken(info_->paramnames, name);
510 BOOST_ASSERT(i >= 0);
515 void InsetCommandParams::clear()
517 for (size_t i = 0; i < info_->n; ++i)
522 bool operator==(InsetCommandParams const & o1,
523 InsetCommandParams const & o2)
525 return o1.insetCode_ == o2.insetCode_ &&
526 o1.cmdName_ == o2.cmdName_ &&
527 o1.info_ == o2.info_ &&
528 o1.params_ == o2.params_ &&
529 o1.preview_ == o2.preview_;
533 bool operator!=(InsetCommandParams const & o1,
534 InsetCommandParams const & o2)