]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
Whitespace cleanup
[lyx.git] / src / insets / InsetCommandParams.cpp
1 /**
2  * \file InsetCommandParams.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Georg Baum
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetCommandParams.h"
15
16 #include "debug.h"
17 #include "gettext.h"
18 #include "Lexer.h"
19
20 #include "support/ExceptionMessage.h"
21 #include "support/lstrings.h"
22
23 #include <boost/assert.hpp>
24
25
26 namespace lyx {
27
28 using support::findToken;
29
30 using std::string;
31 using std::endl;
32 using std::ostream;
33
34 using support::ExceptionMessage;
35 using support::WarningException;
36
37 InsetCommandParams::InsetCommandParams(string const & name)
38         : name_(name), preview_(false)
39 {
40         info_ = findInfo(name);
41         BOOST_ASSERT(info_);
42         params_.resize(info_->n);
43 }
44
45
46 InsetCommandParams::CommandInfo const *
47 InsetCommandParams::findInfo(std::string const & name)
48 {
49         // No parameter may be named "preview", because that is a required
50         // flag for all commands.
51
52         // InsetBibitem
53         if (name == "bibitem") {
54                 static const char * const paramnames[] = {"label", "key", ""};
55                 static const bool isoptional[] = {true, false};
56                 static const CommandInfo info = {2, paramnames, isoptional};
57                 return &info;
58         }
59
60         // InsetBibtex
61         if (name == "bibtex") {
62                 static const char * const paramnames[] =
63                                 {"options", "btprint", "bibfiles", ""};
64                 static const bool isoptional[] = {true, true, false};
65                 static const CommandInfo info = {3, paramnames, isoptional};
66                 return &info;
67         }
68
69         // InsetCitation
70         // FIXME: Use is_possible_cite_command() in
71         // src/frontends/controllers/frontend_helpers.cpp, see comment in src/factory.cpp.
72         if (name == "cite" || name == "citet" || name == "citep" || name == "citealt" ||
73             name == "citealp" || name == "citeauthor" || name == "citeyear" ||
74             name == "citeyearpar" || name == "citet*" || name == "citep*" ||
75             name == "citealt*" || name == "citealp*" ||
76             name == "citeauthor*" || name == "Citet" || name == "Citep" ||
77             name == "Citealt" || name == "Citealp" || name == "Citeauthor" ||
78             name == "Citet*" || name == "Citep*" || name == "Citealt*" ||
79             name == "Citealp*" || name == "Citeauthor*" ||
80             name == "citefield" || name == "citetitle" || name == "cite*") {
81                 // standard cite does only take one argument if jurabib is
82                 // not used, but jurabib extends this to two arguments, so
83                 // we have to allow both here. InsetCitation takes care that
84                 // LaTeX output is nevertheless correct.
85                 static const char * const paramnames[] =
86                                 {"after", "before", "key", ""};
87                 static const bool isoptional[] = {true, true, false};
88                 static const CommandInfo info = {3, paramnames, isoptional};
89                 return &info;
90         }
91
92         // InsetFloatlist
93         if (name == "floatlist") {
94                 static const char * const paramnames[] = {"type", ""};
95                 static const bool isoptional[] = {false};
96                 static const CommandInfo info = {1, paramnames, isoptional};
97                 return &info;
98         }
99
100         // InsetHfill
101         if (name == "hfill") {
102                 static const char * const paramnames[] = {""};
103                 static const CommandInfo info = {0, paramnames, 0};
104                 return &info;
105         }
106
107         // InsetInclude
108         if (name == "include" || name == "input" || name == "verbatiminput" ||
109             name == "verbatiminput*") {
110                 static const char * const paramnames[] = {"filename", ""};
111                 static const bool isoptional[] = {false};
112                 static const CommandInfo info = {1, paramnames, isoptional};
113                 return &info;
114         }
115
116         if (name == "lstinputlisting") {
117                 static const char * const paramnames[] = {"filename", "lstparams", ""};
118                 static const bool isoptional[] = {false, true};
119                 static const CommandInfo info = {2, paramnames, isoptional};
120                 return &info;
121         }
122
123         // InsetIndex, InsetPrintIndex, InsetLabel
124         if (name == "index" || name == "printindex" || name == "label") {
125                 static const char * const paramnames[] = {"name", ""};
126                 static const bool isoptional[] = {false};
127                 static const CommandInfo info = {1, paramnames, isoptional};
128                 return &info;
129         }
130
131         // InsetNomencl
132         if (name == "nomenclature") {
133                 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
134                 static const bool isoptional[] = {true, false, false};
135                 static const CommandInfo info = {3, paramnames, isoptional};
136                 return &info;
137         }
138
139         // InsetPrintNomencl
140         if (name == "printnomenclature") {
141                 static const char * const paramnames[] = {"labelwidth", ""};
142                 static const bool isoptional[] = {true};
143                 static const CommandInfo info = {1, paramnames, isoptional};
144                 return &info;
145         }
146
147         // InsetRef
148         if (name == "eqref" || name == "pageref" || name == "vpageref" ||
149             name == "vref" || name == "prettyref" || name == "ref") {
150                 static const char * const paramnames[] =
151                                 {"name", "reference", ""};
152                 static const bool isoptional[] = {true, false};
153                 static const CommandInfo info = {2, paramnames, isoptional};
154                 return &info;
155         }
156
157         // InsetTOC
158         if (name == "tableofcontents") {
159                 static const char * const paramnames[] = {"type", ""};
160                 static const bool isoptional[] = {false};
161                 static const CommandInfo info = {1, paramnames, isoptional};
162                 return &info;
163         }
164
165         // InsetUrl
166         if (name == "htmlurl" || name == "url") {
167                 static const char * const paramnames[] =
168                                 {"name", "target", ""};
169                 static const bool isoptional[] = {true, false};
170                 static const CommandInfo info = {2, paramnames, isoptional};
171                 return &info;
172         }
173
174         return 0;
175 }
176
177
178 void InsetCommandParams::setCmdName(string const & name)
179 {
180         name_ = name;
181         CommandInfo const * const info = findInfo(name);
182         BOOST_ASSERT(info);
183         ParamVector params(info->n);
184         // Overtake parameters with the same name
185         for (size_t i = 0; i < info_->n; ++i) {
186                 int j = findToken(info->paramnames, info_->paramnames[i]);
187                 if (j >= 0)
188                         params[j] = params_[i];
189         }
190         info_ = info;
191         std::swap(params, params_);
192 }
193
194
195 void InsetCommandParams::scanCommand(string const & cmd)
196 {
197         string tcmdname, toptions, tsecoptions, tcontents;
198
199         if (cmd.empty()) return;
200
201         enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
202
203         // Used to handle things like \command[foo[bar]]{foo{bar}}
204         int nestdepth = 0;
205
206         for (string::size_type i = 0; i < cmd.length(); ++i) {
207                 char const c = cmd[i];
208                 if ((state == CMDNAME && c == ' ') ||
209                     (state == CMDNAME && c == '[') ||
210                     (state == CMDNAME && c == '{')) {
211                         state = WS;
212                 }
213                 if ((state == OPTION  && c == ']') ||
214                     (state == SECOPTION  && c == ']') ||
215                     (state == CONTENT && c == '}')) {
216                         if (nestdepth == 0) {
217                                 state = WS;
218                         } else {
219                                 --nestdepth;
220                         }
221                 }
222                 if ((state == OPTION  && c == '[') ||
223                     (state == SECOPTION  && c == '[') ||
224                     (state == CONTENT && c == '{')) {
225                         ++nestdepth;
226                 }
227                 switch (state) {
228                 case CMDNAME:   tcmdname += c; break;
229                 case OPTION:    toptions += c; break;
230                 case SECOPTION: tsecoptions += c; break;
231                 case CONTENT:   tcontents += c; break;
232                 case WS: {
233                         char const b = i? cmd[i-1]: 0;
234                         if (c == '\\') {
235                                 state = CMDNAME;
236                         } else if (c == '[' && b != ']') {
237                                 state = OPTION;
238                                 nestdepth = 0; // Just to be sure
239                         } else if (c == '[' && b == ']') {
240                                 state = SECOPTION;
241                                 nestdepth = 0; // Just to be sure
242                         } else if (c == '{') {
243                                 state = CONTENT;
244                                 nestdepth = 0; // Just to be sure
245                         }
246                         break;
247                 }
248                 }
249         }
250
251         // Don't mess with this.
252         if (!tcmdname.empty())  setCmdName(tcmdname);
253         if (!toptions.empty())  setOptions(toptions);
254         if (!tsecoptions.empty())  setSecOptions(tsecoptions);
255         if (!tcontents.empty()) setContents(tcontents);
256
257         LYXERR(Debug::PARSER) << "Command <" <<  cmd
258                 << "> == <" << to_utf8(getCommand())
259                 << "> == <" << getCmdName()
260                 << '|' << getContents()
261                 << '|' << getOptions()
262                 << '|' << getSecOptions() << '>' << endl;
263 }
264
265
266 void InsetCommandParams::read(Lexer & lex)
267 {
268         if (lex.isOK()) {
269                 lex.next();
270                 name_ = lex.getString();
271                 info_ = findInfo(name_);
272                 if (!info_) {
273                         lex.printError("InsetCommand: Unknown inset name `$$Token'");
274                         throw ExceptionMessage(WarningException,
275                                 _("Unknown inset name: "),
276                                 from_utf8(name_));
277                 }
278         }
279
280         string token;
281         while (lex.isOK()) {
282                 lex.next();
283                 token = lex.getString();
284                 if (token == "\\end_inset")
285                         break;
286                 // FIXME Why is preview_ read but not written?
287                 if (token == "preview") {
288                         lex.next();
289                         preview_ = lex.getBool();
290                         continue;
291                 }
292                 int const i = findToken(info_->paramnames, token);
293                 if (i >= 0) {
294                         lex.next(true);
295                         params_[i] = lex.getDocString();
296                 } else {
297                         lex.printError("Unknown parameter name `$$Token' for command " + name_);
298                         throw ExceptionMessage(WarningException,
299                                 _("Inset Command: ") + from_ascii(name_),
300                                 _("Unknown parameter name: ") + from_utf8(token));
301                 }
302         }
303         if (token != "\\end_inset") {
304                 lex.printError("Missing \\end_inset at this point. "
305                                "Read: `$$Token'");
306                 throw ExceptionMessage(WarningException,
307                         _("Missing \\end_inset at this point."),
308                         from_utf8(token));
309         }
310 }
311
312
313 void InsetCommandParams::write(ostream & os) const
314 {
315         os << "LatexCommand " << name_ << '\n';
316         for (size_t i = 0; i < info_->n; ++i)
317                 if (!params_[i].empty())
318                         // FIXME UNICODE
319                         os << info_->paramnames[i] << ' '
320                            << Lexer::quoteString(to_utf8(params_[i]))
321                            << '\n';
322 }
323
324
325 docstring const InsetCommandParams::getCommand() const
326 {
327         docstring s = '\\' + from_ascii(name_);
328         bool noparam = true;
329         for (size_t i = 0; i < info_->n; ++i) {
330                 if (info_->optional[i]) {
331                         if (params_[i].empty()) {
332                                 // We need to write this parameter even if
333                                 // it is empty if nonempty optional parameters
334                                 // follow before the next required parameter.
335                                 for (size_t j = i + 1; j < info_->n; ++j) {
336                                         if (!info_->optional[j])
337                                                 break;
338                                         if (!params_[j].empty()) {
339                                                 s += "[]";
340                                                 noparam = false;
341                                                 break;
342                                         }
343                                 }
344                         } else {
345                                 s += '[' + params_[i] + ']';
346                                 noparam = false;
347                         }
348                 } else {
349                         s += '{' + params_[i] + '}';
350                         noparam = false;
351                 }
352         }
353         if (noparam)
354                 // Make sure that following stuff does not change the
355                 // command name.
356                 s += "{}";
357         return s;
358 }
359
360
361 std::string const InsetCommandParams::getOptions() const
362 {
363         for (size_t i = 0; i < info_->n; ++i)
364                 if (info_->optional[i])
365                         return to_utf8(params_[i]);
366         lyxerr << "Programming error: get nonexisting option in "
367                << name_ << " inset." << endl;;
368         return string();
369 }
370
371
372 std::string const InsetCommandParams::getSecOptions() const
373 {
374         bool first = true;
375         for (size_t i = 0; i < info_->n; ++i)
376                 if (info_->optional[i]) {
377                         if (first)
378                                 first = false;
379                         else
380                                 return to_utf8(params_[i]);
381                 }
382         // Happens in InsetCitation
383         lyxerr << "Programming error: get nonexisting second option in "
384                << name_ << " inset." << endl;;
385         return string();
386 }
387
388
389 std::string const InsetCommandParams::getContents() const
390 {
391         for (size_t i = 0; i < info_->n; ++i)
392                 if (!info_->optional[i])
393                         return to_utf8(params_[i]);
394         BOOST_ASSERT(false);
395         return string();
396 }
397
398
399 void InsetCommandParams::setOptions(std::string const & o)
400 {
401         for (size_t i = 0; i < info_->n; ++i)
402                 if (info_->optional[i]) {
403                         params_[i] = from_utf8(o);
404                         return;
405                 }
406         lyxerr << "Programming error: set nonexisting option in "
407                << name_ << " inset." << endl;;
408 }
409
410
411 void InsetCommandParams::setSecOptions(std::string const & s)
412 {
413         bool first = true;
414         for (size_t i = 0; i < info_->n; ++i)
415                 if (info_->optional[i]) {
416                         if (first)
417                                 first = false;
418                         else {
419                                 params_[i] = from_utf8(s);
420                                 return;
421                         }
422                 }
423         // Happens in InsetCitation
424         lyxerr << "Programming error: set nonexisting second option in "
425                << name_ << " inset." << endl;;
426 }
427
428
429 void InsetCommandParams::setContents(std::string const & c)
430 {
431         for (size_t i = 0; i < info_->n; ++i)
432                 if (!info_->optional[i]) {
433                         params_[i] = from_utf8(c);
434                         return;
435                 }
436         BOOST_ASSERT(false);
437 }
438
439
440 docstring const & InsetCommandParams::operator[](string const & name) const
441 {
442         int const i = findToken(info_->paramnames, name);
443         BOOST_ASSERT(i >= 0);
444         return params_[i];
445 }
446
447
448 docstring & InsetCommandParams::operator[](string const & name)
449 {
450         int const i = findToken(info_->paramnames, name);
451         BOOST_ASSERT(i >= 0);
452         return params_[i];
453 }
454
455
456 void InsetCommandParams::clear()
457 {
458         for (size_t i = 0; i < info_->n; ++i)
459                 params_[i].clear();
460 }
461
462
463 bool operator==(InsetCommandParams const & o1,
464                 InsetCommandParams const & o2)
465 {
466         return o1.name_ == o2.name_ &&
467                o1.info_ == o2.info_ &&
468                o1.params_ == o2.params_ &&
469                o1.preview_ == o2.preview_;
470 }
471
472
473 bool operator!=(InsetCommandParams const & o1,
474                 InsetCommandParams const & o2)
475 {
476         return !(o1 == o2);
477 }
478
479
480 } // namespace lyx