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 //FIXME There is no reason now for this to take a string argument.
39 //It'd be much more robust if it took an InsetCode, since then
40 //the compiler would do some checking for us.
41 InsetCommandParams::InsetCommandParams(string const & insetType)
42 : insetType_(insetType), preview_(false)
44 cmdName_ = getDefaultCmd(insetType);
45 info_ = findInfo(insetType, cmdName_);
47 params_.resize(info_->n);
51 InsetCommandParams::InsetCommandParams(string const & insetType,
52 string const & cmdName)
53 : insetType_(insetType), cmdName_(cmdName), preview_(false)
55 info_ = findInfo(insetType, cmdName);
57 params_.resize(info_->n);
61 //FIXME This should go into the Insets themselves...so they will tell
62 //us what parameters they want.
63 //Should this just vanish in favor of the two arg version, or is there
64 //a reason to use it in some cases? What should happen in the single
65 //arg case, then? Maybe use the default? or leave that up to the inset?
66 InsetCommandParams::CommandInfo const *
67 InsetCommandParams::findInfo(std::string const & insetType)
69 // No parameter may be named "preview", because that is a required
70 // flag for all commands.
73 if (insetType == "bibitem") {
74 static const char * const paramnames[] = {"label", "key", ""};
75 static const bool isoptional[] = {true, false};
76 static const CommandInfo info = {2, paramnames, isoptional};
81 if (insetType == "bibtex") {
82 static const char * const paramnames[] =
83 {"options", "btprint", "bibfiles", ""};
84 static const bool isoptional[] = {true, true, false};
85 static const CommandInfo info = {3, paramnames, isoptional};
90 if (insetType == "citation") {
91 // standard cite does only take one argument if jurabib is
92 // not used, but jurabib extends this to two arguments, so
93 // we have to allow both here. InsetCitation takes care that
94 // LaTeX output is nevertheless correct.
95 static const char * const paramnames[] =
96 {"after", "before", "key", ""};
97 static const bool isoptional[] = {true, true, false};
98 static const CommandInfo info = {3, paramnames, isoptional};
103 if (insetType == "floatlist") {
104 static const char * const paramnames[] = {"type", ""};
105 static const bool isoptional[] = {false};
106 static const CommandInfo info = {1, paramnames, isoptional};
111 if (insetType == "hfill") {
112 static const char * const paramnames[] = {""};
113 static const CommandInfo info = {0, paramnames, 0};
118 if (insetType == "href") {
119 static const char * const paramnames[] =
120 {"name", "target", ""};
121 static const bool isoptional[] = {true, false};
122 static const CommandInfo info = {2, paramnames, isoptional};
127 //FIXME This is really correct only for lstinputlistings, but it shouldn't
128 //cause a problem before we get things sorted out. Eventually, this calls
129 //InsetInclude::getParams(cmdName_), or something of the sort.
130 if (insetType == "include") {
131 static const char * const paramnames[] = {"filename", "lstparams", ""};
132 static const bool isoptional[] = {false, true};
133 static const CommandInfo info = {2, paramnames, isoptional};
137 // InsetIndex, InsetPrintIndex, InsetLabel
138 if (insetType == "index" || insetType == "index_print" || insetType == "label") {
139 static const char * const paramnames[] = {"name", ""};
140 static const bool isoptional[] = {false};
141 static const CommandInfo info = {1, paramnames, isoptional};
146 if (insetType == "nomenclature") {
147 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
148 static const bool isoptional[] = {true, false, false};
149 static const CommandInfo info = {3, paramnames, isoptional};
154 if (insetType == "nomencl_print") {
155 static const char * const paramnames[] = {"labelwidth", ""};
156 static const bool isoptional[] = {true};
157 static const CommandInfo info = {1, paramnames, isoptional};
162 if (insetType == "ref") {
163 static const char * const paramnames[] =
164 {"name", "reference", ""};
165 static const bool isoptional[] = {true, false};
166 static const CommandInfo info = {2, paramnames, isoptional};
171 if (insetType == "toc") {
172 static const char * const paramnames[] = {"type", ""};
173 static const bool isoptional[] = {false};
174 static const CommandInfo info = {1, paramnames, isoptional};
182 //FIXME Will eventually call a static method, etc.
183 InsetCommandParams::CommandInfo const *
184 InsetCommandParams::findInfo(std::string const & insetType,
185 std::string const & cmdName)
187 return findInfo(insetType);
191 //FIXME Should call InsetBibitem::getDefaultCmd(), eg
192 std::string InsetCommandParams::getDefaultCmd(std::string insetType) {
193 if (insetType == "bibitem")
195 if (insetType == "bibtex")
196 return "bibtex"; //this is an unused dummy
197 if (insetType == "citation")
199 if (insetType == "floatlist")
200 return "listoftables";
201 if (insetType == "hfill")
203 if (insetType == "href")
205 if (insetType == "include")
207 if (insetType == "index")
209 if (insetType == "index_print")
210 return "print_index";
211 if (insetType == "label")
213 if (insetType == "nomenclature")
214 return "nomenclature";
215 if (insetType == "nomencl_print")
216 return "printnomenclature";
217 if (insetType == "ref")
219 if (insetType == "toc")
220 return "tableofcontents";
226 void InsetCommandParams::setCmdName(string const & name)
228 //FIXME Check command compatibility
230 BOOST_ASSERT(!insetType_.empty());
231 CommandInfo const * const info = findInfo(insetType_, cmdName_);
233 ParamVector params(info->n);
234 // Overtake parameters with the same name
235 for (size_t i = 0; i < info_->n; ++i) {
236 int j = findToken(info->paramnames, info_->paramnames[i]);
238 params[j] = params_[i];
241 std::swap(params, params_);
245 void InsetCommandParams::scanCommand(string const & cmd)
247 string tcmdname, toptions, tsecoptions, tcontents;
249 if (cmd.empty()) return;
251 enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
253 // Used to handle things like \command[foo[bar]]{foo{bar}}
256 for (string::size_type i = 0; i < cmd.length(); ++i) {
257 char const c = cmd[i];
258 if ((state == CMDNAME && c == ' ') ||
259 (state == CMDNAME && c == '[') ||
260 (state == CMDNAME && c == '{')) {
263 if ((state == OPTION && c == ']') ||
264 (state == SECOPTION && c == ']') ||
265 (state == CONTENT && c == '}')) {
266 if (nestdepth == 0) {
272 if ((state == OPTION && c == '[') ||
273 (state == SECOPTION && c == '[') ||
274 (state == CONTENT && c == '{')) {
278 case CMDNAME: tcmdname += c; break;
279 case OPTION: toptions += c; break;
280 case SECOPTION: tsecoptions += c; break;
281 case CONTENT: tcontents += c; break;
283 char const b = i? cmd[i-1]: 0;
286 } else if (c == '[' && b != ']') {
288 nestdepth = 0; // Just to be sure
289 } else if (c == '[' && b == ']') {
291 nestdepth = 0; // Just to be sure
292 } else if (c == '{') {
294 nestdepth = 0; // Just to be sure
301 // Don't mess with this.
302 if (!tcmdname.empty()) setCmdName(tcmdname);
303 if (!toptions.empty()) setOptions(toptions);
304 if (!tsecoptions.empty()) setSecOptions(tsecoptions);
305 if (!tcontents.empty()) setContents(tcontents);
307 LYXERR(Debug::PARSER) << "Command <" << cmd
308 << "> == <" << to_utf8(getCommand())
309 << "> == <" << getCmdName()
310 << '|' << getContents()
311 << '|' << getOptions()
312 << '|' << getSecOptions() << '>' << endl;
316 void InsetCommandParams::read(Lexer & lex)
321 string insetType = lex.getString();
322 if (!insetType_.empty() && insetType != insetType_) {
323 lex.printError("InsetCommand: Attempt to change type of parameters.");
324 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
325 from_utf8("Attempt to change type of parameters."));
327 // OK, we survived...
328 insetType_ = insetType;
333 string test = lex.getString();
334 if (test != "LatexCommand") {
335 lex.printError("InsetCommand: no LatexCommand line found.");
336 throw ExceptionMessage(WarningException, _("InsetCommand error:"),
337 from_utf8("Can't find LatexCommand line."));
341 cmdName_ = lex.getString();
343 //check that this command is ok with the inset...
344 //so that'll be some kind of static call, depending
345 //upon what insetType_ is.
346 //it's possible that should go into InsetCommand.cpp,
347 //or maybe it's a standalone function.
348 info_ = findInfo(insetType_, cmdName_);
350 lex.printError("InsetCommand: Unknown inset name `$$Token'");
351 throw ExceptionMessage(WarningException,
352 _("Unknown inset name: "), from_utf8(insetType_));
358 token = lex.getString();
359 if (token == "\\end_inset")
361 // FIXME Why is preview_ read but not written?
362 if (token == "preview") {
364 preview_ = lex.getBool();
367 int const i = findToken(info_->paramnames, token);
370 params_[i] = lex.getDocString();
372 lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
373 throw ExceptionMessage(WarningException,
374 _("Inset Command: ") + from_ascii(cmdName_),
375 _("Unknown parameter name: ") + from_utf8(token));
378 if (token != "\\end_inset") {
379 lex.printError("Missing \\end_inset at this point. "
381 throw ExceptionMessage(WarningException,
382 _("Missing \\end_inset at this point."),
388 void InsetCommandParams::write(ostream & os) const
390 os << "CommandInset " << insetType_ << '\n';
391 os << "LatexCommand " << cmdName_ << '\n';
392 for (size_t i = 0; i < info_->n; ++i)
393 if (!params_[i].empty())
395 os << info_->paramnames[i] << ' '
396 << Lexer::quoteString(to_utf8(params_[i]))
401 docstring const InsetCommandParams::getCommand() const
403 docstring s = '\\' + from_ascii(cmdName_);
405 for (size_t i = 0; i < info_->n; ++i) {
406 if (info_->optional[i]) {
407 if (params_[i].empty()) {
408 // We need to write this parameter even if
409 // it is empty if nonempty optional parameters
410 // follow before the next required parameter.
411 for (size_t j = i + 1; j < info_->n; ++j) {
412 if (!info_->optional[j])
414 if (!params_[j].empty()) {
421 s += '[' + params_[i] + ']';
425 s += '{' + params_[i] + '}';
430 // Make sure that following stuff does not change the
437 std::string const InsetCommandParams::getOptions() const
439 for (size_t i = 0; i < info_->n; ++i)
440 if (info_->optional[i])
441 return to_utf8(params_[i]);
442 lyxerr << "Programming error: get nonexisting option in "
443 << insetType_ << " inset." << endl;
448 std::string const InsetCommandParams::getSecOptions() const
451 for (size_t i = 0; i < info_->n; ++i)
452 if (info_->optional[i]) {
456 return to_utf8(params_[i]);
458 // Happens in InsetCitation
459 lyxerr << "Programming error: get nonexisting second option in "
460 << insetType_ << " inset." << endl;
465 std::string const InsetCommandParams::getContents() const
467 for (size_t i = 0; i < info_->n; ++i)
468 if (!info_->optional[i])
469 return to_utf8(params_[i]);
475 void InsetCommandParams::setOptions(std::string const & o)
477 for (size_t i = 0; i < info_->n; ++i)
478 if (info_->optional[i]) {
479 params_[i] = from_utf8(o);
482 lyxerr << "Programming error: set nonexisting option in "
483 << insetType_ << " inset." << endl;
487 void InsetCommandParams::setSecOptions(std::string const & s)
490 for (size_t i = 0; i < info_->n; ++i)
491 if (info_->optional[i]) {
495 params_[i] = from_utf8(s);
499 // Happens in InsetCitation
500 lyxerr << "Programming error: set nonexisting second option in "
501 << insetType_ << " inset." << endl;
505 void InsetCommandParams::setContents(std::string const & c)
507 for (size_t i = 0; i < info_->n; ++i)
508 if (!info_->optional[i]) {
509 params_[i] = from_utf8(c);
516 docstring const & InsetCommandParams::operator[](string const & name) const
518 int const i = findToken(info_->paramnames, name);
519 BOOST_ASSERT(i >= 0);
524 docstring & InsetCommandParams::operator[](string const & name)
526 int const i = findToken(info_->paramnames, name);
527 BOOST_ASSERT(i >= 0);
532 void InsetCommandParams::clear()
534 for (size_t i = 0; i < info_->n; ++i)
539 bool operator==(InsetCommandParams const & o1,
540 InsetCommandParams const & o2)
542 return o1.insetType_ == o2.insetType_ &&
543 o1.cmdName_ == o2.cmdName_ &&
544 o1.info_ == o2.info_ &&
545 o1.params_ == o2.params_ &&
546 o1.preview_ == o2.preview_;
550 bool operator!=(InsetCommandParams const & o1,
551 InsetCommandParams const & o2)