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 Inset::Code, 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 //FIXME This is really correct only for lstinputlistings, but it shouldn't
119 //cause a problem before we get things sorted out. Eventually, this calls
120 //InsetInclude::getParams(cmdName_), or something of the sort.
121 if (insetType == "include") {
122 static const char * const paramnames[] = {"filename", "lstparams", ""};
123 static const bool isoptional[] = {false, true};
124 static const CommandInfo info = {2, paramnames, isoptional};
128 // InsetIndex, InsetPrintIndex, InsetLabel
129 if (insetType == "index" || insetType == "index_print" || insetType == "label") {
130 static const char * const paramnames[] = {"name", ""};
131 static const bool isoptional[] = {false};
132 static const CommandInfo info = {1, paramnames, isoptional};
137 if (insetType == "nomenclature") {
138 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
139 static const bool isoptional[] = {true, false, false};
140 static const CommandInfo info = {3, paramnames, isoptional};
145 if (insetType == "nomencl_print") {
146 static const char * const paramnames[] = {"labelwidth", ""};
147 static const bool isoptional[] = {true};
148 static const CommandInfo info = {1, paramnames, isoptional};
153 if (insetType == "ref") {
154 static const char * const paramnames[] =
155 {"name", "reference", ""};
156 static const bool isoptional[] = {true, false};
157 static const CommandInfo info = {2, paramnames, isoptional};
162 if (insetType == "toc") {
163 static const char * const paramnames[] = {"type", ""};
164 static const bool isoptional[] = {false};
165 static const CommandInfo info = {1, paramnames, isoptional};
170 if (insetType == "url") {
171 static const char * const paramnames[] =
172 {"name", "target", ""};
173 static const bool isoptional[] = {true, false};
174 static const CommandInfo info = {2, 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")
197 if (insetType == "citation")
199 if (insetType == "floatlist")
201 if (insetType == "hfill")
203 if (insetType == "include")
205 if (insetType == "index")
207 if (insetType == "index_print")
208 return "print_index";
209 if (insetType == "label")
211 if (insetType == "nomenclature")
212 return "nomenclature";
213 if (insetType == "nomencl_print")
214 return "printnomenclature";
215 if (insetType == "ref")
217 if (insetType == "toc")
218 return "tableofcontents";
219 if (insetType == "url")
225 void InsetCommandParams::setCmdName(string const & name)
227 //FIXME Check command compatibility
229 BOOST_ASSERT(!insetType_.empty());
230 CommandInfo const * const info = findInfo(insetType_, cmdName_);
232 ParamVector params(info->n);
233 // Overtake parameters with the same name
234 for (size_t i = 0; i < info_->n; ++i) {
235 int j = findToken(info->paramnames, info_->paramnames[i]);
237 params[j] = params_[i];
240 std::swap(params, params_);
244 void InsetCommandParams::scanCommand(string const & cmd)
246 string tcmdname, toptions, tsecoptions, tcontents;
248 if (cmd.empty()) return;
250 enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
252 // Used to handle things like \command[foo[bar]]{foo{bar}}
255 for (string::size_type i = 0; i < cmd.length(); ++i) {
256 char const c = cmd[i];
257 if ((state == CMDNAME && c == ' ') ||
258 (state == CMDNAME && c == '[') ||
259 (state == CMDNAME && c == '{')) {
262 if ((state == OPTION && c == ']') ||
263 (state == SECOPTION && c == ']') ||
264 (state == CONTENT && c == '}')) {
265 if (nestdepth == 0) {
271 if ((state == OPTION && c == '[') ||
272 (state == SECOPTION && c == '[') ||
273 (state == CONTENT && c == '{')) {
277 case CMDNAME: tcmdname += c; break;
278 case OPTION: toptions += c; break;
279 case SECOPTION: tsecoptions += c; break;
280 case CONTENT: tcontents += c; break;
282 char const b = i? cmd[i-1]: 0;
285 } else if (c == '[' && b != ']') {
287 nestdepth = 0; // Just to be sure
288 } else if (c == '[' && b == ']') {
290 nestdepth = 0; // Just to be sure
291 } else if (c == '{') {
293 nestdepth = 0; // Just to be sure
300 // Don't mess with this.
301 if (!tcmdname.empty()) setCmdName(tcmdname);
302 if (!toptions.empty()) setOptions(toptions);
303 if (!tsecoptions.empty()) setSecOptions(tsecoptions);
304 if (!tcontents.empty()) setContents(tcontents);
306 LYXERR(Debug::PARSER) << "Command <" << cmd
307 << "> == <" << to_utf8(getCommand())
308 << "> == <" << getCmdName()
309 << '|' << getContents()
310 << '|' << getOptions()
311 << '|' << getSecOptions() << '>' << endl;
315 void InsetCommandParams::read(Lexer & lex)
320 string insetType = lex.getString();
321 if (!insetType_.empty() && insetType != insetType_) {
322 lex.printError("InsetCommand: Attempt to change type of parameters.");
323 throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
324 from_utf8("Attempt to change type of parameters."));
326 // OK, we survived...
327 insetType_ = insetType;
332 string test = lex.getString();
333 if (test != "LatexCommand") {
334 lex.printError("InsetCommand: no LatexCommand line found.");
335 throw ExceptionMessage(WarningException, _("InsetCommand error:"),
336 from_utf8("Can't find LatexCommand line."));
340 cmdName_ = lex.getString();
342 //check that this command is ok with the inset...
343 //so that'll be some kind of static call, depending
344 //upon what insetType_ is.
345 //it's possible that should go into InsetCommand.cpp,
346 //or maybe it's a standalone function.
347 info_ = findInfo(insetType_, cmdName_);
349 lex.printError("InsetCommand: Unknown inset name `$$Token'");
350 throw ExceptionMessage(WarningException,
351 _("Unknown inset name: "), from_utf8(insetType_));
357 token = lex.getString();
358 if (token == "\\end_inset")
360 // FIXME Why is preview_ read but not written?
361 if (token == "preview") {
363 preview_ = lex.getBool();
366 int const i = findToken(info_->paramnames, token);
369 params_[i] = lex.getDocString();
371 lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
372 throw ExceptionMessage(WarningException,
373 _("Inset Command: ") + from_ascii(cmdName_),
374 _("Unknown parameter name: ") + from_utf8(token));
377 if (token != "\\end_inset") {
378 lex.printError("Missing \\end_inset at this point. "
380 throw ExceptionMessage(WarningException,
381 _("Missing \\end_inset at this point."),
387 void InsetCommandParams::write(ostream & os) const
389 os << "CommandInset " << insetType_ << '\n';
390 os << "LatexCommand " << cmdName_ << '\n';
391 for (size_t i = 0; i < info_->n; ++i)
392 if (!params_[i].empty())
394 os << info_->paramnames[i] << ' '
395 << Lexer::quoteString(to_utf8(params_[i]))
400 docstring const InsetCommandParams::getCommand() const
402 docstring s = '\\' + from_ascii(cmdName_);
404 for (size_t i = 0; i < info_->n; ++i) {
405 if (info_->optional[i]) {
406 if (params_[i].empty()) {
407 // We need to write this parameter even if
408 // it is empty if nonempty optional parameters
409 // follow before the next required parameter.
410 for (size_t j = i + 1; j < info_->n; ++j) {
411 if (!info_->optional[j])
413 if (!params_[j].empty()) {
420 s += '[' + params_[i] + ']';
424 s += '{' + params_[i] + '}';
429 // Make sure that following stuff does not change the
436 std::string const InsetCommandParams::getOptions() const
438 for (size_t i = 0; i < info_->n; ++i)
439 if (info_->optional[i])
440 return to_utf8(params_[i]);
441 lyxerr << "Programming error: get nonexisting option in "
442 << insetType_ << " inset." << endl;
447 std::string const InsetCommandParams::getSecOptions() const
450 for (size_t i = 0; i < info_->n; ++i)
451 if (info_->optional[i]) {
455 return to_utf8(params_[i]);
457 // Happens in InsetCitation
458 lyxerr << "Programming error: get nonexisting second option in "
459 << insetType_ << " inset." << endl;
464 std::string const InsetCommandParams::getContents() const
466 for (size_t i = 0; i < info_->n; ++i)
467 if (!info_->optional[i])
468 return to_utf8(params_[i]);
474 void InsetCommandParams::setOptions(std::string const & o)
476 for (size_t i = 0; i < info_->n; ++i)
477 if (info_->optional[i]) {
478 params_[i] = from_utf8(o);
481 lyxerr << "Programming error: set nonexisting option in "
482 << insetType_ << " inset." << endl;
486 void InsetCommandParams::setSecOptions(std::string const & s)
489 for (size_t i = 0; i < info_->n; ++i)
490 if (info_->optional[i]) {
494 params_[i] = from_utf8(s);
498 // Happens in InsetCitation
499 lyxerr << "Programming error: set nonexisting second option in "
500 << insetType_ << " inset." << endl;
504 void InsetCommandParams::setContents(std::string const & c)
506 for (size_t i = 0; i < info_->n; ++i)
507 if (!info_->optional[i]) {
508 params_[i] = from_utf8(c);
515 docstring const & InsetCommandParams::operator[](string const & name) const
517 int const i = findToken(info_->paramnames, name);
518 BOOST_ASSERT(i >= 0);
523 docstring & InsetCommandParams::operator[](string const & name)
525 int const i = findToken(info_->paramnames, name);
526 BOOST_ASSERT(i >= 0);
531 void InsetCommandParams::clear()
533 for (size_t i = 0; i < info_->n; ++i)
538 bool operator==(InsetCommandParams const & o1,
539 InsetCommandParams const & o2)
541 return o1.insetType_ == o2.insetType_ &&
542 o1.cmdName_ == o2.cmdName_ &&
543 o1.info_ == o2.info_ &&
544 o1.params_ == o2.params_ &&
545 o1.preview_ == o2.preview_;
549 bool operator!=(InsetCommandParams const & o1,
550 InsetCommandParams const & o2)