I recently downloaded the SVN code (revision 40920) in order to look into a bug involving the ordering of listings parameters. This is bug #5203: http://www.lyx.org/trac/ticket/5203
(BTW, even on the unmodified SVN code almost every time I hit View > View Source I get a crash and a SIGSEGV error window pops up.) The complaint in #5203 is that the ordering of Listings parameters is changed to be sorted, although ordering is actually significant to the Listings package. The reason for this seems to be that the routines in InsetListingsParams.h and InsetListingsParams.cpp used a map to save the key=value pairs, but STL maps always sort their keys. I've attached modified versions of the two files. It's really a mindless substitution of a pair of vectors (one for keys, one for values) in place of the map. It seems to work, based on limited testing. My C++ is rusty, and I don't know much about the Lyx code, but someone might want to use this as a starting point for the official fix. When the attached changes are applied the lists always stay in order, both as displayed and as written to Latex. The parameter settings from the main menu are applied before the additional settings from the advanced menu. There is also a fix to bug #8144. Duplicated keys have underscores appended to them to keep them distinct. There was an attempt to validate a repeated key (they have trailing underscores) without first trimming the raw value. (Giving a key=value assignment for a key already set from the main menu counts as a repeated key.) This still does not address the issues of the semantics of setting parameter values from the main menu versus setting them in the advanced menu (which is mentioned in #5203). Parameters set from the main window are placed at the beginning of the list in the Latex output. There is also an attempt to grab parameters set from the advanced page which are settable from the main page. In that case, the string disappears in the advanced list when "Apply" is pressed. For example, basicstyle=\small will be eaten and the result will be reflected on the main menu. Yet basicstyle=\itshape will not be recognized and removed and it will stomp the font settings from the main menu. (Try setting the font to tiny from the main menu and then setting basicstyle=\color{red} from the advanced menu and view each result.) There seem to be some problems, as basicstyle=\Large is eaten but the main menu then shows Larger as the size. Huge occasionally appears as an option, but not always. Also, the disappeared items are sometimes "still there" and appear in the Latex output. They then cannot be removed. Since items from the main menu always appear first there could be problems in overriding the settings of some other parameters (like language=Python, which does multiple things). The easiest thing to do would seem to be just to turn off the matching and "disappearing" function and trust that the advanced users know that some of their advanced settings may override the settings from the main page. Then all the settings can just be generated and placed in the Latex, and the users get exactly what they asked for (hopefully intentionally). Most of the main menu commands would work fine with the above, but basicstyle is more problematic since multiple properties are set from that one command. The user may not know that setting basicstyle=\itshape will stomp the font size and font family. Yet that is already the case; the current version does not recognize and remove an itshape setting. This might be less of a problem if font shape and color were also settable from the main menu, since non-advanced users would not be forced to set those properties from the advanced menu. Yet most users will not want to globally modify those properties anyway. One possibility after turning off the disappearing function would be to add another, non-editable window to the advanced page showing the commands which will be generated by the settings on the main page. Then the users would at least have more warning that they might be stomping something there (and they could see what values to repeat in their own settings). #5203 suggests displaying everything in the one window (including the main menu key=value parameter settings) and then searching and modifying those commands when main menu options are selected. The whole search-and-modify thing brings up various issues, however, as to how it should be done.
/** * \file InsetListingsParams.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Bo Peng * * Full author contact details are available in file CREDITS. */ #include <config.h> #include <algorithm> #include "InsetListingsParams.h" #include "Length.h" #include "Lexer.h" #include "support/convert.h" #include "support/gettext.h" #include "support/lstrings.h" #include "support/textutils.h" #include <sstream> using namespace std; using namespace lyx::support; namespace lyx { namespace { enum param_type { ALL, // accept all TRUEFALSE, // accept 'true' or 'false' INTEGER, // accept an integer LENGTH, // accept a latex length ONEOF, // accept one of a few values SUBSETOF // accept a string composed of given characters }; /// Listings package parameter information. // FIXME: make this class visible outside of this file so that // FIXME: it can be used directly in the frontend and in the LyX format // FIXME: parsing. class ListingsParam { public: /// Default ctor for STL containers. ListingsParam(): onoff_(false), type_(ALL) {} /// Main ctor. ListingsParam(string const & v, bool o, param_type t, string const & i, docstring const & h) : value_(v), onoff_(o), type_(t), info_(i), hint_(h) {} /// Validate a paramater. /// \retval an empty string if \c par is valid. /// \retval otherwise an explanation WRT to \c par invalidity. docstring validate(string const & par) const; private: /// default value string value_; public: /// for option with value "true", "false". /// if onoff is true, /// "true": option /// "false": /// "other": option="other" /// onoff is false, /// "true": option=true /// "false": option=false // FIXME: this is public because of InsetListingParam::addParam() bool onoff_; private: /// validator type. /// ALL: /// TRUEFALSE: /// INTEGER: /// LENGTH: /// info is ignored. /// ONEOF /// info is a \n separated string with allowed values /// SUBSETOF /// info is a string from which par is composed of /// (e.g. floatplacement can be one or more of *tbph) param_type type_; /// information which meaning depends on parameter type. /// \sa type_ string info_; /// a help message that is displayed in the gui. docstring hint_; }; docstring ListingsParam::validate(string const & par) const { bool unclosed = false; string par2 = par; // braces are allowed if (prefixIs(par, "{") && suffixIs(par, "}") && !suffixIs(par, "\\}")) par2 = par.substr(1, par.size() - 2); // check for unmatched braces int braces = 0; for (size_t i = 0; i < par2.size(); ++i) { if (par2[i] == '{' && (i == 0 || par2[i-1] != '\\')) ++braces; else if (par2[i] == '}' && (i == 0 || par2[i-1] != '\\')) --braces; } unclosed = braces != 0; switch (type_) { case ALL: if (par2.empty() && !onoff_) { if (!hint_.empty()) return hint_; else return _("A value is expected."); } if (unclosed) return _("Unbalanced braces!"); return docstring(); case TRUEFALSE: if (par2.empty() && !onoff_) { if (!hint_.empty()) return hint_; else return _("Please specify true or false."); } if (par2 != "true" && par2 != "false") return _("Only true or false is allowed."); if (unclosed) return _("Unbalanced braces!"); return docstring(); case INTEGER: if (!isStrInt(par2)) { if (!hint_.empty()) return hint_; else return _("Please specify an integer value."); } if (convert<int>(par2) == 0 && par2[0] != '0') return _("An integer is expected."); if (unclosed) return _("Unbalanced braces!"); return docstring(); case LENGTH: if (par2.empty() && !onoff_) { if (!hint_.empty()) return hint_; else return _("Please specify a LaTeX length expression."); } if (!isValidLength(par2)) return _("Invalid LaTeX length expression."); if (unclosed) return _("Unbalanced braces!"); return docstring(); case ONEOF: { if (par2.empty() && !onoff_) { if (!hint_.empty()) return hint_; else return bformat(_("Please specify one of %1$s."), from_utf8(info_)); } // break value to allowed strings vector<string> lists; string v; for (size_t i = 0; i != info_.size(); ++i) { if (info_[i] == '\n') { lists.push_back(v); v = string(); } else v += info_[i]; } if (!v.empty()) lists.push_back(v); // good, find the string if (find(lists.begin(), lists.end(), par2) != lists.end()) { if (unclosed) return _("Unbalanced braces!"); return docstring(); } // otherwise, produce a meaningful error message. string matching_names; for (vector<string>::iterator it = lists.begin(); it != lists.end(); ++it) { if (it->size() >= par2.size() && it->substr(0, par2.size()) == par2) { if (matching_names.empty()) matching_names += *it; else matching_names += ", " + *it; } } if (matching_names.empty()) return bformat(_("Try one of %1$s."), from_utf8(info_)); else return bformat(_("I guess you mean %1$s."), from_utf8(matching_names)); return docstring(); } case SUBSETOF: if (par2.empty() && !onoff_) { if (!hint_.empty()) return hint_; else return bformat(_("Please specify one or more of '%1$s'."), from_utf8(info_)); } for (size_t i = 0; i < par2.size(); ++i) if (info_.find(par2[i], 0) == string::npos) return bformat(_("Should be composed of one or more of %1$s."), from_utf8(info_)); if (unclosed) return _("Unbalanced braces!"); return docstring(); } return docstring(); } /// languages and language/dialect combinations char const * allowed_languages = "no language\nABAP\n[R/2 4.3]ABAP\n[R/2 5.0]ABAP\n[R/3 3.1]ABAP\n" "[R/3 4.6C]ABAP\n[R/3 6.10]ABAP\nACSL\nAda\n[2005]Ada\n[83]Ada\n" "[95]Ada\nALGOL\n[60]ALGOL\n[68]ALGOL\nAssembler\n" "[Motorola68k]Assembler\n[x86masm]Assembler\nAwk\n[gnu]Awk\n[POSIX]Awk\n" "bash\nBasic\n[Visual]Basic\nC\n[ANSI]C\n[Handel]C\n[Objective]C\n" "[Sharp]C\nC++\n[ANSI]C++\n[GNU]C++\n[ISO]C++\n[Visual]C++\nCaml\n" "[light]Caml\n[Objective]Caml\nClean\nCobol\n[1974]Cobol\n[1985]Cobol\n" "[ibm]Cobol\nComal 80\ncommand.com\n[WinXP]command.com\nComsol\ncsh\n" "Delphi\nEiffel\nElan\nerlang\nEuphoria\nFortran\n[77]Fortran\n[90]Fortran\n" "[95]Fortran\nGCL\nGnuplot\nHaskell\nHTML\nIDL\n[CORBA]IDL\ninform\n" "Java\n[AspectJ]Java\nJVMIS\nksh\nLingo\nLisp\n[Auto]Lisp\nLogo\n" "make\n[gnu]make\nMathematica\n[1.0]Mathematica\n[3.0]Mathematica\n" "[5.2]Mathematica\nMatlab\nMercury\nMetaPost\nMiranda\nMizar\nML\n" "Modula-2\nMuPAD\nNASTRAN\nOberon-2\nOCL\n[decorative]OCL\n[OMG]OCL\n" "Octave\nOz\nPascal\n[Borland6]Pascal\n[Standard]Pascal\n[XSC]Pascal\n" "Perl\nPHP\nPL/I\nPlasm\nPostScript\nPOV\nProlog\nPromela\nPSTricks\n" "Python\nR\nReduce\nRexx\nRSL\nRuby\nS\n[PLUS]S\nSAS\nScilab\nsh\n" "SHELXL\nSimula\n[67]Simula\n[CII]Simula\n[DEC]Simula\n[IBM]Simula\n" "SPARQL\nSQL\ntcl\n[tk]tcl\nTeX\n[AlLaTeX]TeX\n[common]TeX\n[LaTeX]TeX\n" "[plain]TeX\n[primitive]TeX\nVBScript\nVerilog\nVHDL\n[AMS]VHDL\nVRML\n" "[97]VRML\nXML\nXSLT"; /// ListingsParam Validator. /// This class is aimed to be a singleton which is instantiated in /// \c InsetListingsParams::addParam(). // FIXME: transfer this validator to the frontend. // FIXME: avoid the use of exception. class ParValidator { public: ParValidator(); /// validate a parameter for a given name. /// return an error message if \c par is an invalid parameter. docstring validate(string const & name, string const & par) const; /// return the onoff status of a parameter \c key, if \c key is not found /// return false bool onoff(string const & key) const; private: /// key is the name of the parameter typedef map<string, ListingsParam> ListingsParams; ListingsParams all_params_; }; ParValidator::ParValidator() { docstring const empty_hint; docstring const style_hint = _("Use \\footnotesize, \\small, \\itshape, " "\\ttfamily or something like that"); docstring const frame_hint = _("none, leftline, topline, bottomline, lines, " "single, shadowbox or subset of trblTRBL"); docstring const frameround_hint = _("Enter four letters (either t = round " "or f = square) for top right, bottom " "right, bottom left and top left corner."); docstring const color_hint = _("Enter something like \\color{white}"); /// options copied from page 26 of listings manual // FIXME: add default parameters ... (which is not used now) all_params_["float"] = ListingsParam("false", true, SUBSETOF, "*tbph", empty_hint); all_params_["floatplacement"] = ListingsParam("tbp", false, SUBSETOF, "tbp", empty_hint); all_params_["aboveskip"] = ListingsParam("\\medskipamount", false, LENGTH, "", empty_hint); all_params_["belowskip"] = ListingsParam("\\medskipamount", false, LENGTH, "", empty_hint); all_params_["lineskip"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["boxpos"] = ListingsParam("", false, SUBSETOF, "bct", empty_hint); all_params_["print"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["firstline"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["lastline"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["linerange"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["showlines"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["emptylines"] = ListingsParam("", false, ALL, "", _( "Expect a number with an optional * before it")); all_params_["gobble"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["style"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["language"] = ListingsParam("", false, ONEOF, allowed_languages, empty_hint); all_params_["alsolanguage"] = ListingsParam("", false, ONEOF, allowed_languages, empty_hint); all_params_["defaultdialect"] = ListingsParam("", false, ONEOF, allowed_languages, empty_hint); all_params_["printpod"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["usekeywordsintag"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["tagstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["markfirstintag"] = ListingsParam("", false, ALL, "", style_hint); all_params_["makemacrouse"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["basicstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["identifierstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["commentstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["stringstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["keywordstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["ndkeywordstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["classoffset"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["texcsstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["directivestyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["emph"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moreemph"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deleteemph"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["emphstyle"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["delim"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moredelim"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletedelim"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["extendedchars"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["inputencoding"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["upquote"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["tabsize"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["showtabs"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["tab"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["showspaces"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["showstringspaces"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["formfeed"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["numbers"] = ListingsParam("", false, ONEOF, "none\nleft\nright", empty_hint); all_params_["stepnumber"] = ListingsParam("", false, INTEGER, "", empty_hint); all_params_["numberfirstline"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["numberstyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["numbersep"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["numberblanklines"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["firstnumber"] = ListingsParam("", false, ALL, "", _("auto, last or a number")); all_params_["name"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["thelstnumber"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["title"] = ListingsParam("", false, ALL, "", empty_hint); // this option is not handled in the parameter box all_params_["caption"] = ListingsParam("", false, ALL, "", _( "This parameter should not be entered here. Please use the caption " "edit box (when using the child document dialog) or " "menu Insert->Caption (when defining a listing inset)")); // this option is not handled in the parameter box all_params_["label"] = ListingsParam("", false, ALL, "",_( "This parameter should not be entered here. Please use the label " "edit box (when using the child document dialog) or " "menu Insert->Label (when defining a listing inset)")); all_params_["nolol"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["captionpos"] = ListingsParam("", false, SUBSETOF, "tb", empty_hint); all_params_["abovecaptionskip"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["belowcaptionskip"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["linewidth"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["xleftmargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["xrightmargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["resetmargins"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["breaklines"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["breakatwhitespace"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["prebreak"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["postbreak"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["breakindent"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["breakautoindent"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["frame"] = ListingsParam("", false, ALL, "", frame_hint); all_params_["frameround"] = ListingsParam("", false, SUBSETOF, "tf", frameround_hint); all_params_["framesep"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["rulesep"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["framerule"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["framexleftmargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["framexrightmargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["framextopmargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["framexbottommargin"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["backgroundcolor"] = ListingsParam("", false, ALL, "", color_hint ); all_params_["rulecolor"] = ListingsParam("", false, ALL, "", color_hint ); all_params_["fillcolor"] = ListingsParam("", false, ALL, "", color_hint ); all_params_["rulesepcolor"] = ListingsParam("", false, ALL, "", color_hint ); all_params_["frameshape"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["index"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moreindex"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deleteindex"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["indexstyle"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["columns"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["flexiblecolumns"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["keepspaces"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["basewidth"] = ListingsParam("", false, LENGTH, "", empty_hint); all_params_["fontadjust"] = ListingsParam("", true, TRUEFALSE, "", empty_hint); all_params_["texcl"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["mathescape"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["escapechar"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["escapeinside"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["escapebegin"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["escapeend"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["fancyvrb"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["fvcmdparams"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morefvcmdparams"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["keywordsprefix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["keywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morekeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletekeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["ndkeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morendkeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletendkeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["texcs"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moretexcs"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletetexcs"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["directives"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moredirectives"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletedirectives"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["sensitive"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["alsoletter"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["alsodigit"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["alsoother"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["otherkeywords"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["tag"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["string"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morestring"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletestring"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["comment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morecomment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletecomment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["keywordcomment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morekeywordcomment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletekeywordcomment"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["keywordcommentsemicolon"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["podcomment"] = ListingsParam("", false, ALL, "", empty_hint); // the following are experimental listings features all_params_["procnamekeys"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["moreprocnamekeys"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deleteprocnamekeys"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["procnamestyle"] = ListingsParam("", false, ALL, "", style_hint); all_params_["indexprocnames"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["hyperref"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["morehyperref"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["deletehyperref"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["hyperanchor"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["hyperlink"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["literate"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["lgrindef"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangebeginprefix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangebeginsuffix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangeendprefix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangeendsuffix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangeprefix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["rangesuffix"] = ListingsParam("", false, ALL, "", empty_hint); all_params_["includerangemarker"] = ListingsParam("", false, TRUEFALSE, "", empty_hint); all_params_["multicols"] = ListingsParam("", false, INTEGER, "", empty_hint); } docstring ParValidator::validate(string const & name, string const & par) const { if (name.empty()) return _("Invalid (empty) listing parameter name."); if (name[0] == '?') { string suffix = trim(string(name, 1)); string param_names; ListingsParams::const_iterator it = all_params_.begin(); ListingsParams::const_iterator end = all_params_.end(); for (; it != end; ++it) { if (suffix.empty() || contains(it->first, suffix)) { if (!param_names.empty()) param_names += ", "; param_names += it->first; } } if (suffix.empty()) return bformat( _("Available listing parameters are %1$s"), from_ascii(param_names)); else return bformat( _("Available listings parameters containing string \"%1$s\" are %2$s"), from_utf8(suffix), from_utf8(param_names)); } // locate name in parameter table ListingsParams::const_iterator it = all_params_.find(name); if (it != all_params_.end()) { docstring msg = it->second.validate(par); if (msg.empty()) return msg; else return bformat(_("Parameter %1$s: "), from_utf8(name)) + msg; } else { // otherwise, produce a meaningful error message. string matching_names; ListingsParams::const_iterator end = all_params_.end(); for (it = all_params_.begin(); it != end; ++it) { if (prefixIs(it->first, name)) { if (!matching_names.empty()) matching_names += ", "; matching_names += it->first; } } if (matching_names.empty()) return bformat(_("Unknown listing parameter name: %1$s"), from_utf8(name)); else return bformat(_("Parameters starting with '%1$s': %2$s"), from_utf8(name), from_utf8(matching_names)); } return docstring(); } bool ParValidator::onoff(string const & name) const { // locate name in parameter table ListingsParams::const_iterator it = all_params_.find(name); if (it != all_params_.end()) return it->second.onoff_; else return false; } } // namespace anon. // define a global ParValidator ParValidator * par_validator = 0; InsetListingsParams::InsetListingsParams() : inline_(false), params_keys_(), params_values_(), status_(InsetCollapsable::Open) { } InsetListingsParams::InsetListingsParams(string const & par, bool in, InsetCollapsable::CollapseStatus s) : inline_(in), params_keys_(), params_values_(), status_(s) { // this will activate parameter validation. fromEncodedString(par); } void InsetListingsParams::write(ostream & os) const { if (inline_) os << "true "; else os << "false "; os << status_ << " \"" << encodedString() << "\""; } void InsetListingsParams::read(Lexer & lex) { lex >> inline_; int s = InsetCollapsable::Collapsed; lex >> s; status_ = static_cast<InsetCollapsable::CollapseStatus>(s); string par; lex >> par; fromEncodedString(par); } string InsetListingsParams::params(string const & sep) const { string par; vector<string>::const_iterator it_keys = params_keys_.begin(); vector<string>::const_iterator it_values = params_values_.begin(); for (; it_keys != params_keys_.end(); ++it_keys, ++it_values) { if (!par.empty()) par += sep; // key=value,key=value1 is stored in params_ as key=value,key_=value1. if (it_values->empty()) par += rtrim(*it_keys, "_"); else par += rtrim(*it_keys, "_") + '=' + *it_values; } return par; } vector<string>::const_iterator InsetListingsParams::find_in_params_(string const & key) const // This utility function just does a find like in a map, but uses the key/value // pairs in the vectors of strings params_keys_ and params_values_. It returns // an iterator into the values vector. { vector<string>::const_iterator it_keys = params_keys_.begin(); vector<string>::const_iterator it_values = params_values_.begin(); for (; it_keys != params_keys_.end(); ++it_keys, ++it_values) { if (*it_keys == key) break; } return it_values; } void InsetListingsParams::addParam(string const & key, string const & value, bool replace) { if (key.empty()) return; // duplicate parameters! string keyname = key; if (!replace && find_in_params_(key) != params_values_.end()) // key=value,key=value1 is allowed in listings // use key_, key__, key___ etc to avoid name conflict while (find_in_params_(keyname += '_') != params_values_.end()) { } // check onoff flag // onoff parameter with value false if (!par_validator) par_validator = new ParValidator(); if (par_validator->onoff(key) && (value == "false" || value == "{false}")) { params_keys_.push_back(keyname); params_values_.push_back(string()); } // if the parameter is surrounded with {}, good else if (prefixIs(value, "{") && suffixIs(value, "}")) { params_keys_.push_back(keyname); params_values_.push_back(value); } // otherwise, check if {} is needed. Add {} to all values with // non-ascii/number characters, just to be safe else { bool has_special_char = false; for (size_t i = 0; i < value.size(); ++i) if (!isAlnumASCII(value[i])) { has_special_char = true; break; } if (has_special_char) { params_keys_.push_back(keyname); params_values_.push_back("{" + value + "}"); } else { params_keys_.push_back(keyname); params_values_.push_back(value); } } } void InsetListingsParams::addParams(string const & par) { string key; string value; bool isValue = false; int braces = 0; for (size_t i = 0; i < par.size(); ++i) { // end of par if (par[i] == '\n') { addParam(trim(key), trim(value)); key = string(); value = string(); isValue = false; continue; } else if (par[i] == ',' && braces == 0) { addParam(trim(key), trim(value)); key = string(); value = string(); isValue = false; continue; } else if (par[i] == '=' && braces == 0) { isValue = true; continue; } else if (par[i] == '{' && i > 0 && par[i-1] != '\\') // don't count a brace in first position ++braces; else if (par[i] == '}' && i != par.size() - 1 && (i == 0 || (i > 0 && par[i-1] != '\\'))) --braces; if (isValue) value += par[i]; else key += par[i]; } if (!trim(key).empty()) addParam(trim(key), trim(value)); } void InsetListingsParams::setParams(string const & par) { params_keys_.clear(); params_values_.clear(); addParams(par); } string InsetListingsParams::encodedString() const { // Encode string! // '"' is handled differently because it will // terminate a lyx token. string par = params(); // '"' is now " ==> '"' is now &quot; par = subst(par, "&", "&"); // '"' is now &quot; ==> '"' is now &quot; par = subst(par, "\"", """); return par; } string InsetListingsParams::separatedParams(bool keepComma) const { if (keepComma) return params(",\n"); else return params("\n"); } void InsetListingsParams::fromEncodedString(string const & in) { // Decode string! Reversal of encodedString string par = in; // '"' is now &quot; ==> '"' is now &quot; par = subst(par, """, "\""); // '"' is now &quot; ==> '"' is now " par = subst(par, "&", "&"); setParams(par); } bool InsetListingsParams::isFloat() const { return find_in_params_("float") != params_values_.end(); } string InsetListingsParams::getParamValue(string const & param) const { // is this parameter defined? vector<string>::const_iterator it_values = find_in_params_(param); string par = (it_values == params_values_.end()) ? string() : *it_values; if (prefixIs(par, "{") && suffixIs(par, "}")) return par.substr(1, par.size() - 2); else return par; } docstring InsetListingsParams::validate() const { docstring msg; if (!par_validator) par_validator = new ParValidator(); // return msg for first key=value pair which is incomplete or has an error vector<string>::const_iterator it_keys = params_keys_.begin(); vector<string>::const_iterator it_values = params_values_.begin(); for (; it_keys != params_keys_.end(); ++it_keys, ++it_values) { msg = par_validator->validate(rtrim(*it_keys, "_"), *it_values); // key trimmed if (!msg.empty()) return msg; } return msg; } } // namespace lyx
// -*- C++ -*- /** * \file InsetListingsParams.h * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Bo Peng * * Full author contact details are available in file CREDITS. */ #ifndef INSETLISTINGSPARAMS_H #define INSETLISTINGSPARAMS_H #include "InsetCollapsable.h" #include <map> namespace lyx { class InsetListingsParams { public: /// InsetListingsParams(); /// InsetListingsParams(std::string const &, bool in=false, InsetCollapsable::CollapseStatus s = InsetCollapsable::Open); /// write parameters to an ostream void write(std::ostream &) const; /// read parameters from an ostream void read(Lexer &); /// valid parameter string std::string params(std::string const & sep=",") const; /// add key=value to params_. key_=value will be used if key=value already exists /// unless replace=true. void addParam(std::string const & key, std::string const & value, bool replace = false); /// add a few parameters void addParams(std::string const & par); /// set params_ with par, throw an exception if par is valid void setParams(std::string const & par); /// generate a parameter string that can be safely save and restored /// by lyx' lexer std::string encodedString() const; /// newline (\n) separated parameters. comma can be removed. /// One possible complication is that , may appear in option value. std::string separatedParams(bool keepComma = false) const; /// get parameter from encoded string void fromEncodedString(std::string const & par); /// bool isInline() const { return inline_; } /// bool isFloat() const; /// InsetCollapsable::CollapseStatus status() const { return status_; } /// void setInline(bool i) { inline_ = i; } /// get value of option \c param std::string getParamValue(std::string const & param) const; /// void clear() { params_keys_.clear(); params_values_.clear(); } /// validate parameter, return an error message docstring validate() const; private: /// inline or normal listings bool inline_; /// key-value pairs of the parameters // The key-value pairs are kept in two separate vectors in order to maintain // their order of insertion. The function find_in_params_ is a utility function // which does the equivalent of find in a map. Given a key it returns an // iterator pointing to the value associated with it (in the params_values_ // vector). std::vector<std::string>::const_iterator find_in_params_(std::string const & key) const; std::vector<std::string> params_keys_; std::vector<std::string> params_values_; /// collapsable status InsetCollapsable::CollapseStatus status_; }; } // namespace lyx #endif