]> git.lyx.org Git - lyx.git/blob - src/insets/InsetCommandParams.cpp
merge src/frontends/controllers/biblio, character, frnt_lang, helper_funcs and tex_he...
[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 "lyxlex.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.C.
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         // InsetIndex, InsetPrintIndex, InsetLabel
117         if (name == "index" || name == "printindex" || name == "label") {
118                 static const char * const paramnames[] = {"name", ""};
119                 static const bool isoptional[] = {false};
120                 static const CommandInfo info = {1, paramnames, isoptional};
121                 return &info;
122         }
123
124         // InsetNomencl
125         if (name == "nomenclature") {
126                 static const char * const paramnames[] = {"prefix", "symbol", "description", ""};
127                 static const bool isoptional[] = {true, false, false};
128                 static const CommandInfo info = {3, paramnames, isoptional};
129                 return &info;
130         }
131
132         // InsetPrintNomencl
133         if (name == "printnomenclature") {
134                 static const char * const paramnames[] = {"labelwidth", ""};
135                 static const bool isoptional[] = {true};
136                 static const CommandInfo info = {1, paramnames, isoptional};
137                 return &info;
138         }
139
140         // InsetRef
141         if (name == "eqref" || name == "pageref" || name == "vpageref" ||
142             name == "vref" || name == "prettyref" || name == "ref") {
143                 static const char * const paramnames[] =
144                                 {"name", "reference", ""};
145                 static const bool isoptional[] = {true, false};
146                 static const CommandInfo info = {2, paramnames, isoptional};
147                 return &info;
148         }
149
150         // InsetTOC
151         if (name == "tableofcontents") {
152                 static const char * const paramnames[] = {"type", ""};
153                 static const bool isoptional[] = {false};
154                 static const CommandInfo info = {1, paramnames, isoptional};
155                 return &info;
156         }
157
158         // InsetUrl
159         if (name == "htmlurl" || name == "url") {
160                 static const char * const paramnames[] =
161                                 {"name", "target", ""};
162                 static const bool isoptional[] = {true, false};
163                 static const CommandInfo info = {2, paramnames, isoptional};
164                 return &info;
165         }
166
167         return 0;
168 }
169
170
171 void InsetCommandParams::setCmdName(string const & name)
172 {
173         name_ = name;
174         CommandInfo const * const info = findInfo(name);
175         BOOST_ASSERT(info);
176         ParamVector params(info->n);
177         // Overtake parameters with the same name
178         for (size_t i = 0; i < info_->n; ++i) {
179                 int j = findToken(info->paramnames, info_->paramnames[i]);
180                 if (j >= 0)
181                         params[j] = params_[i];
182         }
183         info_ = info;
184         std::swap(params, params_);
185 }
186
187
188 void InsetCommandParams::scanCommand(string const & cmd)
189 {
190         string tcmdname, toptions, tsecoptions, tcontents;
191
192         if (cmd.empty()) return;
193
194         enum { WS, CMDNAME, OPTION, SECOPTION, CONTENT } state = WS;
195
196         // Used to handle things like \command[foo[bar]]{foo{bar}}
197         int nestdepth = 0;
198
199         for (string::size_type i = 0; i < cmd.length(); ++i) {
200                 char const c = cmd[i];
201                 if ((state == CMDNAME && c == ' ') ||
202                     (state == CMDNAME && c == '[') ||
203                     (state == CMDNAME && c == '{')) {
204                         state = WS;
205                 }
206                 if ((state == OPTION  && c == ']') ||
207                     (state == SECOPTION  && c == ']') ||
208                     (state == CONTENT && c == '}')) {
209                         if (nestdepth == 0) {
210                                 state = WS;
211                         } else {
212                                 --nestdepth;
213                         }
214                 }
215                 if ((state == OPTION  && c == '[') ||
216                     (state == SECOPTION  && c == '[') ||
217                     (state == CONTENT && c == '{')) {
218                         ++nestdepth;
219                 }
220                 switch (state) {
221                 case CMDNAME:   tcmdname += c; break;
222                 case OPTION:    toptions += c; break;
223                 case SECOPTION: tsecoptions += c; break;
224                 case CONTENT:   tcontents += c; break;
225                 case WS: {
226                         char const b = i? cmd[i-1]: 0;
227                         if (c == '\\') {
228                                 state = CMDNAME;
229                         } else if (c == '[' && b != ']') {
230                                 state = OPTION;
231                                 nestdepth = 0; // Just to be sure
232                         } else if (c == '[' && b == ']') {
233                                 state = SECOPTION;
234                                 nestdepth = 0; // Just to be sure
235                         } else if (c == '{') {
236                                 state = CONTENT;
237                                 nestdepth = 0; // Just to be sure
238                         }
239                         break;
240                 }
241                 }
242         }
243
244         // Don't mess with this.
245         if (!tcmdname.empty())  setCmdName(tcmdname);
246         if (!toptions.empty())  setOptions(toptions);
247         if (!tsecoptions.empty())  setSecOptions(tsecoptions);
248         if (!tcontents.empty()) setContents(tcontents);
249
250         LYXERR(Debug::PARSER) << "Command <" <<  cmd
251                 << "> == <" << to_utf8(getCommand())
252                 << "> == <" << getCmdName()
253                 << '|' << getContents()
254                 << '|' << getOptions()
255                 << '|' << getSecOptions() << '>' << endl;
256 }
257
258
259 void InsetCommandParams::read(LyXLex & lex)
260 {
261         if (lex.isOK()) {
262                 lex.next();
263                 name_ = lex.getString();
264                 info_ = findInfo(name_);
265                 if (!info_) {
266                         lex.printError("InsetCommand: Unknown inset name `$$Token'");
267                         throw ExceptionMessage(WarningException,
268                                 _("Unknown inset name: "),
269                                 from_utf8(name_));
270                 }
271         }
272
273         string token;
274         while (lex.isOK()) {
275                 lex.next();
276                 token = lex.getString();
277                 if (token == "\\end_inset")
278                         break;
279                 // FIXME Why is preview_ read but not written?
280                 if (token == "preview") {
281                         lex.next();
282                         preview_ = lex.getBool();
283                         continue;
284                 }
285                 int const i = findToken(info_->paramnames, token);
286                 if (i >= 0) {
287                         lex.next(true);
288                         params_[i] = lex.getDocString();
289                 } else {
290                         lex.printError("Unknown parameter name `$$Token' for command " + name_);
291                         throw ExceptionMessage(WarningException,
292                                 _("Inset Command: ") + from_ascii(name_),
293                                 _("Unknown parameter name: ") + from_utf8(token));
294                 }
295         }
296         if (token != "\\end_inset") {
297                 lex.printError("Missing \\end_inset at this point. "
298                                "Read: `$$Token'");
299                 throw ExceptionMessage(WarningException,
300                         _("Missing \\end_inset at this point."),
301                         from_utf8(token));
302         }
303 }
304
305
306 void InsetCommandParams::write(ostream & os) const
307 {
308         os << "LatexCommand " << name_ << '\n';
309         for (size_t i = 0; i < info_->n; ++i)
310                 if (!params_[i].empty())
311                         // FIXME UNICODE
312                         os << info_->paramnames[i] << ' '
313                            << LyXLex::quoteString(to_utf8(params_[i]))
314                            << '\n';
315 }
316
317
318 docstring const InsetCommandParams::getCommand() const
319 {
320         docstring s = '\\' + from_ascii(name_);
321         bool noparam = true;
322         for (size_t i = 0; i < info_->n; ++i) {
323                 if (info_->optional[i]) {
324                         if (params_[i].empty()) {
325                                 // We need to write this parameter even if
326                                 // it is empty if nonempty optional parameters
327                                 // follow before the next required parameter.
328                                 for (size_t j = i + 1; j < info_->n; ++j) {
329                                         if (!info_->optional[j])
330                                                 break;
331                                         if (!params_[j].empty()) {
332                                                 s += "[]";
333                                                 noparam = false;
334                                                 break;
335                                         }
336                                 }
337                         } else {
338                                 s += '[' + params_[i] + ']';
339                                 noparam = false;
340                         }
341                 } else {
342                         s += '{' + params_[i] + '}';
343                         noparam = false;
344                 }
345         }
346         if (noparam)
347                 // Make sure that following stuff does not change the
348                 // command name.
349                 s += "{}";
350         return s;
351 }
352
353
354 std::string const InsetCommandParams::getOptions() const
355 {
356         for (size_t i = 0; i < info_->n; ++i)
357                 if (info_->optional[i])
358                         return to_utf8(params_[i]);
359         lyxerr << "Programming error: get nonexisting option in "
360                << name_ << " inset." << endl;; 
361         return string();
362 }
363
364
365 std::string const InsetCommandParams::getSecOptions() const
366 {
367         bool first = true;
368         for (size_t i = 0; i < info_->n; ++i)
369                 if (info_->optional[i]) {
370                         if (first)
371                                 first = false;
372                         else
373                                 return to_utf8(params_[i]);
374                 }
375         // Happens in InsetCitation
376         lyxerr << "Programming error: get nonexisting second option in "
377                << name_ << " inset." << endl;; 
378         return string();
379 }
380
381
382 std::string const InsetCommandParams::getContents() const
383 {
384         for (size_t i = 0; i < info_->n; ++i)
385                 if (!info_->optional[i])
386                         return to_utf8(params_[i]);
387         BOOST_ASSERT(false);
388         return string();
389 }
390
391
392 void InsetCommandParams::setOptions(std::string const & o)
393 {
394         for (size_t i = 0; i < info_->n; ++i)
395                 if (info_->optional[i]) {
396                         params_[i] = from_utf8(o);
397                         return;
398                 }
399         lyxerr << "Programming error: set nonexisting option in "
400                << name_ << " inset." << endl;; 
401 }
402
403
404 void InsetCommandParams::setSecOptions(std::string const & s)
405 {
406         bool first = true;
407         for (size_t i = 0; i < info_->n; ++i)
408                 if (info_->optional[i]) {
409                         if (first)
410                                 first = false;
411                         else {
412                                 params_[i] = from_utf8(s);
413                                 return;
414                         }
415                 }
416         // Happens in InsetCitation
417         lyxerr << "Programming error: set nonexisting second option in "
418                << name_ << " inset." << endl;; 
419 }
420
421
422 void InsetCommandParams::setContents(std::string const & c)
423 {
424         for (size_t i = 0; i < info_->n; ++i)
425                 if (!info_->optional[i]) {
426                         params_[i] = from_utf8(c);
427                         return;
428                 }
429         BOOST_ASSERT(false);
430 }
431
432
433 docstring const & InsetCommandParams::operator[](string const & name) const
434 {
435         int const i = findToken(info_->paramnames, name);
436         BOOST_ASSERT(i >= 0);
437         return params_[i];
438 }
439
440
441 docstring & InsetCommandParams::operator[](string const & name)
442 {
443         int const i = findToken(info_->paramnames, name);
444         BOOST_ASSERT(i >= 0);
445         return params_[i];
446 }
447
448
449 void InsetCommandParams::clear()
450 {
451         for (size_t i = 0; i < info_->n; ++i)
452                 params_[i].clear();
453 }
454
455
456 bool operator==(InsetCommandParams const & o1,
457                 InsetCommandParams const & o2)
458 {
459         return o1.name_ == o2.name_ &&
460                o1.info_ == o2.info_ &&
461                o1.params_ == o2.params_ &&
462                o1.preview_ == o2.preview_;
463 }
464
465
466 bool operator!=(InsetCommandParams const & o1,
467                 InsetCommandParams const & o2)
468 {
469         return !(o1 == o2);
470 }
471
472
473 } // namespace lyx