2 * \file InsetListingsParams.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
14 #include "InsetListingsParams.h"
20 #include <boost/assert.hpp>
22 #include "support/lstrings.h"
23 #include "support/convert.h"
29 using lyx::support::trim;
30 using lyx::support::isStrInt;
31 using lyx::support::prefixIs;
32 using lyx::support::suffixIs;
33 using lyx::support::getVectorFromString;
40 TRUEFALSE, // accept 'true' or 'false'
41 INTEGER, // accept an integer
42 LENGTH, // accept an latex length
43 ONEOF, // accept one of a few values
44 SUBSETOF, // accept a string composed of given characters
48 /** Information about each parameter
50 struct listings_param_info {
51 /// name of the parameter
55 // for option with value "true", "false",
59 // "other": option="other"
61 // "true": option=true
62 // "false": option=false
72 // info is a \n separated string with allowed values
74 // info is a string from which par is composed of
75 // (e.g. floatplacement can be one or more of tbph)
82 char const * allowed_languages =
83 "no language\nBAP\nACSL\nAda\nALGOL\nC\nC++\nCaml\nClean\nCobol\n"
84 "Comal 80\ncsh\nDelphi\nEiffel\nElan\nEuphoria\nFortran\nHaskell\n"
85 "HTML\nIDL\nJava\nLisp\nLogo\nmake\nMathematica\nMatlab\nMercury\n"
86 "Miranda\nML\nModula-2\nOberon-2\nOCL\nPascal\nPerl\nPHP\nPL/I\nPOV\n"
87 "Python\nProlog\nR\nS\nSAS\nSHELXL\nSimula\ntcl\nSQL\nTeX\nVBScript\n"
90 char const * style_hint = "Use \\footnotessize, \\small, \\itshape, \\ttfamily or something like that";
91 char const * frame_hint = "none, leftline, topline, bottomline, lines, single, shadowbox or subset of trblTRBL";
92 char const * frameround_hint = "The foru letters (t or f) attached to top right, bottom right, bottom left and top left corner.";
93 char const * color_hint = "Enter something like \\color{white}";
95 /// options copied from page 26 of listings manual
96 // FIXME: add default parameters ... (which is not used now)
97 listings_param_info const listings_param_table[] = {
98 { "float", "false", true, SUBSETOF, "tbph", "" },
99 { "floatplacement", "tbp", false, SUBSETOF, "tbph", "" },
100 { "aboveskip", "\\medskipamount", false, LENGTH, "", "" },
101 { "belowskip", "\\medskipamount", false, LENGTH, "", "" },
102 { "lineskip", "", false, LENGTH, "", "" },
103 { "boxpos", "", false, SUBSETOF, "bct", "" },
104 { "print", "", false, TRUEFALSE, "", "" },
105 { "firstline", "", false, INTEGER, "", "" },
106 { "lastline", "", false, INTEGER, "", "" },
107 { "showlines", "", false, TRUEFALSE, "", "" },
108 { "emptylines", "", false, ALL, "", "Expect a number with an optional * before it" },
109 { "gobble", "", false, INTEGER, "", "" },
110 { "style", "", false, ALL, "", "" },
111 { "language", "", false, ONEOF, allowed_languages, "" },
112 { "alsolanguage", "", false, ONEOF, allowed_languages, "" },
113 { "defaultdialect", "", false, ONEOF, allowed_languages, "" },
114 { "printpod", "", false, TRUEFALSE, "", "" },
115 { "usekeywordsintag", "", false, TRUEFALSE, "", "" },
116 { "tagstyle", "", false, ALL, "", style_hint },
117 { "markfirstintag", "", false, ALL, "", style_hint },
118 { "makemacrouse", "", false, TRUEFALSE, "", "" },
119 { "basicstyle", "", false, ALL, "", style_hint },
120 { "identifierstyle", "", false, ALL, "", style_hint },
121 { "commentstyle", "", false, ALL, "", style_hint },
122 { "stringstyle", "", false, ALL, "", style_hint },
123 { "keywordstyle", "", false, ALL, "", style_hint },
124 { "ndkeywordstyle", "", false, ALL, "", style_hint },
125 { "classoffset", "", false, INTEGER, "", "" },
126 { "texcsstyle", "", false, ALL, "", style_hint },
127 { "directivestyle", "", false, ALL, "", style_hint },
128 { "emph", "", false, ALL, "", "" },
129 { "moreemph", "", false, ALL, "", "" },
130 { "deleteemph", "", false, ALL, "", "" },
131 { "emphstyle", "", false, ALL, "", "" },
132 { "delim", "", false, ALL, "", "" },
133 { "moredelim", "", false, ALL, "", "" },
134 { "deletedelim", "", false, ALL, "", "" },
135 { "extendedchars", "", false, TRUEFALSE, "", "" },
136 { "inputencoding", "", false, ALL, "", "" },
137 { "upquote", "", false, TRUEFALSE, "", "" },
138 { "tabsize", "", false, INTEGER, "", "" },
139 { "showtabs", "", false, ALL, "", "" },
140 { "tab", "", false, ALL, "", "" },
141 { "showspaces", "", false, TRUEFALSE, "", "" },
142 { "showstringspaces", "", false, TRUEFALSE, "", "" },
143 { "formfeed", "", false, ALL, "", "" },
144 { "numbers", "", false, ONEOF, "none\nleft\nright", "" },
145 { "stepnumber", "", false, INTEGER, "", "" },
146 { "numberfirstline", "", false, TRUEFALSE, "", "" },
147 { "numberstyle", "", false, ALL, "", style_hint },
148 { "numbersep", "", false, LENGTH, "", "" },
149 { "numberblanklines", "", false, ALL, "", "" },
150 { "firstnumber", "", false, ALL, "", "auto, last or a number" },
151 { "name", "", false, ALL, "", "" },
152 { "thelstnumber", "", false, ALL, "", "" },
153 { "title", "", false, ALL, "", "" },
154 // this option is not handled in the parameter box
155 { "caption", "", false, ALL, "", "This parameter should not be entered here. "
156 "Please use caption editbox (Include dialog) or insert->caption (listings inset)" },
157 // this option is not handled in the parameter box
158 { "label", "", false, ALL, "", "This parameter should not be entered here."
159 "Please use label editbox (Include dialog) or insert->caption (listings inset)"},
160 { "nolol", "", false, TRUEFALSE, "", "" },
161 { "captionpos", "", false, SUBSETOF, "tb", "" },
162 { "abovecaptionskip", "", false, LENGTH, "", "" },
163 { "belowcaptionskip", "", false, LENGTH, "", "" },
164 { "linewidth", "", false, LENGTH, "", "" },
165 { "xleftmargin", "", false, LENGTH, "", "" },
166 { "xrightmargin", "", false, LENGTH, "", "" },
167 { "resetmargins", "", false, TRUEFALSE, "", "" },
168 { "breaklines", "", false, TRUEFALSE, "", "" },
169 { "prebreak", "", false, ALL, "", "" },
170 { "postbreak", "", false, ALL, "", "" },
171 { "breakindent", "", false, LENGTH, "", "" },
172 { "breakautoindent", "", false, TRUEFALSE, "", "" },
173 { "frame", "", false, ALL, "", frame_hint },
174 { "frameround", "", false, SUBSETOF, "tf", frameround_hint },
175 { "framesep", "", false, LENGTH, "", "" },
176 { "rulesep", "", false, LENGTH, "", "" },
177 { "framerule", "", false, LENGTH, "", "" },
178 { "framexleftmargin", "", false, LENGTH, "", "" },
179 { "framexrightmargin", "", false, LENGTH, "", "" },
180 { "framextopmargin", "", false, LENGTH, "", "" },
181 { "framexbottommargin", "", false, LENGTH, "", "" },
182 { "backgroundcolor", "", false, ALL, "", color_hint },
183 { "rulecolor", "", false, ALL, "", color_hint },
184 { "fillcolor", "", false, ALL, "", color_hint },
185 { "rulesepcolor", "", false, ALL, "", color_hint },
186 { "frameshape", "", false, ALL, "", "" },
187 { "index", "", false, ALL, "", "" },
188 { "moreindex", "", false, ALL, "", "" },
189 { "deleteindex", "", false, ALL, "", "" },
190 { "indexstyle", "", false, ALL, "", "" },
191 { "columns", "", false, ALL, "", "" },
192 { "flexiblecolumns", "", false, ALL, "", "" },
193 { "keepspaces", "", false, TRUEFALSE, "", "" },
194 { "basewidth", "", false, LENGTH, "", "" },
195 { "fontadjust", "", true, TRUEFALSE, "", "" },
196 { "texcl", "", false, TRUEFALSE, "", "" },
197 { "mathescape", "", false, TRUEFALSE, "", "" },
198 { "escapechar", "", false, ALL, "", "" },
199 { "escapeinside", "", false, ALL, "", "" },
200 { "escepeinside", "", false, ALL, "", "" },
201 { "escepebegin", "", false, ALL, "", "" },
202 { "escepeend", "", false, ALL, "", "" },
203 { "fancyvrb", "", false, TRUEFALSE, "", "" },
204 { "fvcmdparams", "", false, ALL, "", "" },
205 { "morefvcmdparams", "", false, ALL, "", "" },
206 { "keywordsprefix", "", false, ALL, "", "" },
207 { "keywords", "", false, ALL, "", "" },
208 { "morekeywords", "", false, ALL, "", "" },
209 { "deletekeywords", "", false, ALL, "", "" },
210 { "ndkeywords", "", false, ALL, "", "" },
211 { "morendkeywords", "", false, ALL, "", "" },
212 { "deletendkeywords", "", false, ALL, "", "" },
213 { "texcs", "", false, ALL, "", "" },
214 { "moretexcs", "", false, ALL, "", "" },
215 { "deletetexcs", "", false, ALL, "", "" },
216 { "directives", "", false, ALL, "", "" },
217 { "moredirectives", "", false, ALL, "", "" },
218 { "deletedirectives", "", false, ALL, "", "" },
219 { "sensitive", "", false, ALL, "", "" },
220 { "alsoletter", "", false, ALL, "", "" },
221 { "alsodigit", "", false, ALL, "", "" },
222 { "alsoother", "", false, ALL, "", "" },
223 { "otherkeywords", "", false, ALL, "", "" },
224 { "tag", "", false, ALL, "", "" },
225 { "string", "", false, ALL, "", "" },
226 { "morestring", "", false, ALL, "", "" },
227 { "deletestring", "", false, ALL, "", "" },
228 { "comment", "", false, ALL, "", "" },
229 { "morecomment", "", false, ALL, "", "" },
230 { "deletecomment", "", false, ALL, "", "" },
231 { "keywordcomment", "", false, ALL, "", "" },
232 { "morekeywordcomment", "", false, ALL, "", "" },
233 { "deletekeywordcomment", "", false, ALL, "", "" },
234 { "keywordcommentsemicolon", "", false, ALL, "", "" },
235 { "podcomment", "", false, ALL, "", "" },
236 { "", "", false, ALL, "", ""}
243 parValidator(string const & name);
245 /// validate given parameter
246 /// invalidParam will be thrown if invalid
247 /// parameter is found.
248 void validate(std::string const & par) const;
254 listings_param_info const * info;
258 parValidator::parValidator(string const & n)
262 throw invalidParam("Invalid (empty) listings param name.");
263 else if (name == "?") {
266 while (listings_param_table[idx].name != string()) {
269 pars += listings_param_table[idx].name;
272 throw invalidParam("Available listings parameters are " + pars);
274 // locate name in parameter table
276 while (listings_param_table[idx].name != name && listings_param_table[idx].name != string())
279 if (listings_param_table[idx].name != "") {
280 info = &listings_param_table[idx];
283 // otherwise, produce a meaningful error message.
284 string matching_names;
285 for (size_t i = 0; i < idx; ++i) {
286 string n(listings_param_table[i].name);
287 if (n.size() >= name.size() && n.substr(0, name.size()) == name) {
288 if (matching_names.empty())
291 matching_names += ", " + n;
294 if (matching_names.empty())
295 throw invalidParam("Unknown listings param name: " + name);
297 throw invalidParam("Parameters starting with '" + name +
298 "': " + matching_names);
302 void parValidator::validate(std::string const & par) const
304 switch (info->type) {
306 if (par.empty() && !info->onoff) {
307 if (info->hint != "")
308 throw invalidParam(info->hint);
310 throw invalidParam("An value is expected");
314 if (par.empty() && !info->onoff) {
315 if (info->hint != "")
316 throw invalidParam(info->hint);
318 throw invalidParam("Please specify true or false");
320 if (par != "true" && par != "false")
321 throw invalidParam("Only true or false is allowed for parameter" + name);
325 if (!isStrInt(par)) {
326 if (info->hint != "")
327 throw invalidParam(info->hint);
329 throw invalidParam("Please specify an integer value");
331 if (convert<int>(par) == 0 && par[0] != '0')
332 throw invalidParam("An integer is expected for parameter " + name);
336 if (par.empty() && !info->onoff) {
337 if (info->hint != "")
338 throw invalidParam(info->hint);
340 throw invalidParam("Please specify a latex length expression");
342 if (!isValidLength(par))
343 throw invalidParam("Invalid latex length expression for parameter " + name);
347 if (par.empty() && !info->onoff) {
348 if (info->hint != "")
349 throw invalidParam(info->hint);
351 throw invalidParam("Please specify one of " + string(info->info));
353 // break value to allowed strings
354 vector<string> lists;
356 for (size_t i = 0; info->info[i] != '\0'; ++i) {
357 if (info->info[i] == '\n') {
366 // good, find the string
367 if (std::find(lists.begin(), lists.end(), par) != lists.end())
369 // otherwise, produce a meaningful error message.
370 string matching_names;
371 for (vector<string>::iterator it = lists.begin();
372 it != lists.end(); ++it) {
373 if (it->size() >= par.size() && it->substr(0, par.size()) == par) {
374 if (matching_names.empty())
375 matching_names += *it;
377 matching_names += ", " + *it;
380 if (matching_names.empty())
381 throw invalidParam("Try one of " + string(info->info));
383 throw invalidParam("I guess you mean " + matching_names);
387 if (par.empty() && !info->onoff) {
388 if (info->hint != "")
389 throw invalidParam(info->hint);
391 throw invalidParam("Please specify one or more of " + string(info->info));
393 for (size_t i = 0; i < par.size(); ++i)
394 if (string(info->info).find(par[i], 0) == string::npos)
395 throw invalidParam("Parameter " + name +
396 " should be composed of one or more of " + info->info);
403 InsetListingsParams::InsetListingsParams() :
404 inline_(false), params_(), keys_(0), status_(InsetCollapsable::Open)
409 InsetListingsParams::InsetListingsParams(string const & par, bool in, InsetCollapsable::CollapseStatus s)
410 : inline_(in), params_(), keys_(0), status_(s)
412 // this will activate parameter validation.
413 fromEncodedString(par);
417 void InsetListingsParams::write(ostream & os) const
423 os << status_ << " \"" << encodedString() << "\"";
427 void InsetListingsParams::read(Lexer & lex)
433 status_ = static_cast<InsetCollapsable::CollapseStatus>(s);
436 fromEncodedString(par);
440 void InsetListingsParams::addParam(string const & key, string const & value)
444 // exception may be thown.
445 parValidator(key.c_str()).validate(value);
446 // duplicate parameters!
447 if (find(keys_.begin(), keys_.end(), key) != keys_.end())
448 throw invalidParam("Parameter " + key + " has already been defined");
450 keys_.push_back(key);
451 if (!params_.empty())
458 while (listings_param_table[idx].name != key)
460 BOOST_ASSERT(listings_param_table[idx].name == key);
461 if (listings_param_table[idx].onoff && value == "false")
464 params_ += key + '=' + value;
469 void InsetListingsParams::addParams(string const & par)
473 bool isValue = false;
475 for (size_t i = 0; i < par.size(); ++i) {
477 if (par[i] == '\n') {
478 addParam(trim(key), trim(value));
483 } else if (par[i] == ',' && braces == 0) {
484 addParam(trim(key), trim(value));
489 } else if (par[i] == '=' && braces == 0) {
492 } else if (par[i] == '{' && par[i - 1] == '=')
494 else if (par[i] == '}' && (i == par.size() - 1 || par[i + 1] == ','))
502 if (!trim(key).empty())
503 addParam(trim(key), trim(value));
507 void InsetListingsParams::setParams(string const & par)
515 string InsetListingsParams::encodedString() const
519 // '"' should be handled differently because it will
520 // terminate a lyx token. Right now, it is silently ignored.
522 for (size_t i = 0; i < params_.size(); ++i) {
523 BOOST_ASSERT(params_[i] != '\n');
524 if (params_[i] != '"')
531 string InsetListingsParams::separatedParams(bool keepComma) const
533 // , might be used as regular parameter option so
534 // the prcess might be more complicated than what I am doing here
537 for (size_t i = 0; i < params_.size(); ++i)
538 if (params_[i] == ',' && braces == 0) {
543 } else if (params_[i] == '{' && params_[i - 1] == '=') {
546 } else if (params_[i] == '}' && (i == params_.size() -1 || params_[i + 1] == ',')) {
555 void InsetListingsParams::fromEncodedString(string const & in)
558 // Do nothing because " was silently ignored.
563 string InsetListingsParams::getParamValue(string const & param) const
565 // is this parameter defined?
566 if (find(keys_.begin(), keys_.end(), param) == keys_.end())
568 // if so, search for it
569 vector<string> pars = getVectorFromString(separatedParams(), "\n");
570 for (vector<string>::iterator it = pars.begin(); it != pars.end(); ++it)
571 if (prefixIs(*it, param + "=")) {
572 string par = it->substr(param.size() + 1);
573 if (prefixIs(par, "{") && suffixIs(par, "}"))
574 return par.substr(1, par.size() - 2);
578 // if param= is not found, should be something like float, return ""