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