mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-16 16:18:22 +00:00
ba738d0167
The enum DisplayType is replaced with the flags RowFlags that can be combined. Here is the correspondence between the old DisplayType and the new Inset::RowFlags: DisplayType RowFLags Meaning Inline Inline plain inline inset -- BreakBefore row ends before this inset -- BreakAfter the row ends after this inset AlignCenter Display the inset is centered on its own row AlignLeft Display | AlignLeft the inset is left-aligned on its row AlignRight Display | AlignRight the inset is right-aligned on its row -- RowAfter an extra row is needed after this inset Display is just a shortcut for BreakBefore | BreakAfter. The flags for the newline inset will be BreakAfter | RowAfter, while the separator inset will just use BreakAfter. This groundwork does not introduce any new feature at this point. It aims to remve the numerous isNewLine and isSeparator all over the code, and to eventually optional break after some insets like spaces (see #11621). Most display() methods are renamed to rowFlags(). Some are removed because they returned Inline. Now display() is only a helper function for hull insets.
641 lines
19 KiB
C++
641 lines
19 KiB
C++
/**
|
|
* \file InsetListings.cpp
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author Bo Peng
|
|
* \author Jürgen Spitzmüller
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "InsetListings.h"
|
|
|
|
#include "Buffer.h"
|
|
#include "BufferView.h"
|
|
#include "BufferParams.h"
|
|
#include "Counters.h"
|
|
#include "Cursor.h"
|
|
#include "DispatchResult.h"
|
|
#include "Encoding.h"
|
|
#include "FuncRequest.h"
|
|
#include "FuncStatus.h"
|
|
#include "InsetCaption.h"
|
|
#include "Language.h"
|
|
#include "LaTeXFeatures.h"
|
|
#include "Lexer.h"
|
|
#include "output_latex.h"
|
|
#include "output_xhtml.h"
|
|
#include "OutputParams.h"
|
|
#include "TextClass.h"
|
|
#include "TexRow.h"
|
|
#include "texstream.h"
|
|
|
|
#include "support/debug.h"
|
|
#include "support/docstream.h"
|
|
#include "support/gettext.h"
|
|
#include "support/lstrings.h"
|
|
#include "support/lassert.h"
|
|
|
|
#include "frontends/alert.h"
|
|
#include "frontends/Application.h"
|
|
|
|
#include "support/regex.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
using namespace lyx::support;
|
|
|
|
namespace lyx {
|
|
|
|
|
|
InsetListings::InsetListings(Buffer * buf, InsetListingsParams const & par)
|
|
: InsetCaptionable(buf,"listing")
|
|
{
|
|
params_.setMinted(buffer().params().use_minted);
|
|
status_ = par.status();
|
|
}
|
|
|
|
|
|
InsetListings::~InsetListings()
|
|
{
|
|
hideDialogs("listings", this);
|
|
}
|
|
|
|
|
|
Inset::RowFlags InsetListings::rowFlags() const
|
|
{
|
|
return params().isInline() || params().isFloat() ? Inline : Display | AlignLeft;
|
|
}
|
|
|
|
|
|
docstring InsetListings::layoutName() const
|
|
{
|
|
if (buffer().params().use_minted)
|
|
return from_ascii("MintedListings");
|
|
else
|
|
return from_ascii("Listings");
|
|
}
|
|
|
|
|
|
void InsetListings::write(ostream & os) const
|
|
{
|
|
os << "listings" << "\n";
|
|
InsetListingsParams const & par = params();
|
|
// parameter string is encoded to be a valid lyx token.
|
|
string opt = par.encodedString();
|
|
if (!opt.empty())
|
|
os << "lstparams \"" << opt << "\"\n";
|
|
if (par.isInline())
|
|
os << "inline true\n";
|
|
else
|
|
os << "inline false\n";
|
|
InsetCaptionable::write(os);
|
|
}
|
|
|
|
|
|
void InsetListings::read(Lexer & lex)
|
|
{
|
|
while (lex.isOK()) {
|
|
lex.next();
|
|
string token = lex.getString();
|
|
if (token == "lstparams") {
|
|
lex.next();
|
|
string const value = lex.getString();
|
|
params().fromEncodedString(value);
|
|
} else if (token == "inline") {
|
|
lex.next();
|
|
params().setInline(lex.getBool());
|
|
} else {
|
|
// no special option, push back 'status' etc
|
|
lex.pushToken(token);
|
|
break;
|
|
}
|
|
}
|
|
InsetCaptionable::read(lex);
|
|
}
|
|
|
|
|
|
Encoding const * InsetListings::forcedEncoding(Encoding const * inner_enc,
|
|
Encoding const * outer_enc) const
|
|
{
|
|
// The listings package cannot deal with multi-byte-encoded
|
|
// glyphs, except for Xe/LuaTeX (with non-TeX fonts) or pLaTeX.
|
|
// Minted can deal with all encodings.
|
|
if (buffer().params().use_minted
|
|
|| inner_enc->name() == "utf8-plain"
|
|
|| inner_enc->package() == Encoding::japanese
|
|
|| inner_enc->hasFixedWidth())
|
|
return 0;
|
|
|
|
// We try if there's a singlebyte encoding for the outer
|
|
// language; if not, fall back to latin1.
|
|
// Power-users can set inputenc to utf8-plain to bypass this workaround
|
|
// and provide alternatives in the user-preamble.
|
|
return (outer_enc->hasFixedWidth()) ?
|
|
outer_enc : encodings.fromLyXName("iso8859-1");
|
|
}
|
|
|
|
|
|
void InsetListings::latex(otexstream & os, OutputParams const & runparams) const
|
|
{
|
|
string param_string = params().params();
|
|
// NOTE: I use {} to quote text, which is an experimental feature
|
|
// of the listings package (see page 25 of the manual)
|
|
bool const isInline = params().isInline();
|
|
bool const use_minted = buffer().params().use_minted;
|
|
static regex const reg1("(.*)(basicstyle=\\{)([^\\}]*)(\\\\ttfamily)([^\\}]*)(\\})(.*)");
|
|
static regex const reg2("(.*)(basicstyle=\\{)([^\\}]*)(\\\\rmfamily)([^\\}]*)(\\})(.*)");
|
|
static regex const reg3("(.*)(basicstyle=\\{)([^\\}]*)(\\\\sffamily)([^\\}]*)(\\})(.*)");
|
|
static regex const reg4("(.*)(basicstyle=\\{)([^\\}]*)(\\\\(tiny|scriptsize|footnotesize|small|normalsize|large|Large))([^\\}]*)(\\})(.*)");
|
|
static regex const reg5("(.*)(fontfamily=)(tt|sf|rm)(.*)");
|
|
static regex const reg6("(.*)(fontsize=\\{)(\\\\(tiny|scriptsize|footnotesize|small|normalsize|large|Large))(\\})(.*)");
|
|
if (use_minted) {
|
|
// If params have been entered with "listings", and then the user switched to "minted",
|
|
// we have params that need to be translated.
|
|
// FIXME: We should use a backend-abstract syntax in listings params instead!
|
|
// Substitute fontstyle option
|
|
smatch sub;
|
|
if (regex_match(param_string, sub, reg1))
|
|
param_string = sub.str(1) + "fontfamily=tt," + sub.str(2) + sub.str(3)
|
|
+ sub.str(5) + sub.str(6) + sub.str(7);
|
|
if (regex_match(param_string, sub, reg2))
|
|
param_string = sub.str(1) + "fontfamily=rm," + sub.str(2) + sub.str(3)
|
|
+ sub.str(5) + sub.str(6) + sub.str(7);
|
|
if (regex_match(param_string, sub, reg3))
|
|
param_string = sub.str(1) + "fontfamily=sf," + sub.str(2) + sub.str(3)
|
|
+ sub.str(5) + sub.str(6) + sub.str(7);
|
|
// as well as fontsize option
|
|
if (regex_match(param_string, sub, reg4))
|
|
param_string = sub.str(1) + "fontsize={" + sub.str(4) + sub.str(3) + sub.str(7) + sub.str(8);
|
|
} else {
|
|
// And the same vice versa
|
|
// Substitute fontstyle option
|
|
smatch sub;
|
|
string basicstyle;
|
|
if (regex_match(param_string, sub, reg5)) {
|
|
basicstyle = "\\" + sub.str(3) + "family";
|
|
param_string = sub.str(1) + sub.str(4);
|
|
}
|
|
// as well as fontsize option
|
|
if (regex_match(param_string, sub, reg6)) {
|
|
basicstyle += sub.str(3);
|
|
param_string = sub.str(1) + sub.str(6);
|
|
}
|
|
if (!basicstyle.empty())
|
|
param_string = rtrim(param_string, ",") + ",basicstyle={" + basicstyle + "}";
|
|
}
|
|
if (runparams.use_polyglossia && runparams.local_font->isRightToLeft()) {
|
|
// We need to use the *latin switches (#11554)
|
|
smatch sub;
|
|
if (regex_match(param_string, sub, reg1))
|
|
param_string = sub.str(1) + sub.str(2) + sub.str(3) + sub.str(4)
|
|
+ "latin" + sub.str(5) + sub.str(6) + sub.str(7);
|
|
if (regex_match(param_string, sub, reg2))
|
|
param_string = sub.str(1) + sub.str(2) + sub.str(3) + sub.str(4)
|
|
+ "latin" + sub.str(5) + sub.str(6) + sub.str(7);
|
|
if (regex_match(param_string, sub, reg3))
|
|
param_string = sub.str(1) + sub.str(2) + sub.str(3) + sub.str(4)
|
|
+ "latin" + sub.str(5) + sub.str(6) + sub.str(7);
|
|
}
|
|
string minted_language;
|
|
string float_placement;
|
|
bool const isfloat = params().isFloat();
|
|
if (use_minted && (isfloat || contains(param_string, "language="))) {
|
|
// Get float placement and/or language of the code,
|
|
// then remove the relative options.
|
|
vector<string> opts =
|
|
getVectorFromString(param_string, ",", false);
|
|
for (size_t i = 0; i < opts.size(); ++i) {
|
|
if (prefixIs(opts[i], "float")) {
|
|
if (prefixIs(opts[i], "float="))
|
|
float_placement = opts[i].substr(6);
|
|
opts.erase(opts.begin() + int(i--));
|
|
}
|
|
else if (prefixIs(opts[i], "language=")) {
|
|
minted_language = opts[i].substr(9);
|
|
opts.erase(opts.begin() + int(i--));
|
|
}
|
|
}
|
|
param_string = getStringFromVector(opts, ",");
|
|
}
|
|
// Minted needs a language specification
|
|
if (minted_language.empty()) {
|
|
// If a language has been set globally, use that,
|
|
// otherwise use TeX by default
|
|
string const & blp = buffer().params().listings_params;
|
|
size_t start = blp.find("language=");
|
|
if (start != string::npos) {
|
|
start += strlen("language=");
|
|
size_t len = blp.find(",", start);
|
|
if (len != string::npos)
|
|
len -= start;
|
|
minted_language = blp.substr(start, len);
|
|
} else
|
|
minted_language = "TeX";
|
|
}
|
|
|
|
// get the paragraphs. We can not output them directly to given odocstream
|
|
// because we can not yet determine the delimiter character of \lstinline
|
|
docstring code;
|
|
docstring uncodable;
|
|
ParagraphList::const_iterator par = paragraphs().begin();
|
|
ParagraphList::const_iterator end = paragraphs().end();
|
|
|
|
bool encoding_switched = false;
|
|
Encoding const * const save_enc = runparams.encoding;
|
|
|
|
Encoding const * const outer_encoding =
|
|
(runparams.local_font != 0) ?
|
|
runparams.local_font->language()->encoding()
|
|
: buffer().params().language->encoding();
|
|
Encoding const * fixedlstenc = forcedEncoding(runparams.encoding, outer_encoding);
|
|
if (fixedlstenc) {
|
|
// We need to switch to a singlebyte encoding, due to
|
|
// the restrictions of the listings package (see above).
|
|
// This needs to be consistent with
|
|
// LaTeXFeatures::getTClassI18nPreamble().
|
|
// We need to put this into a group in order to prevent encoding leaks
|
|
// (happens with cprotect).
|
|
os << "\\bgroup";
|
|
switchEncoding(os.os(), buffer().params(), runparams, *fixedlstenc, true);
|
|
runparams.encoding = fixedlstenc;
|
|
encoding_switched = true;
|
|
}
|
|
|
|
bool const captionfirst = !isfloat && par->isInset(0)
|
|
&& par->getInset(0)->lyxCode() == CAPTION_CODE;
|
|
|
|
while (par != end) {
|
|
pos_type const siz = par->size();
|
|
bool captionline = false;
|
|
for (pos_type i = 0; i < siz; ++i) {
|
|
if (i == 0 && par->isInset(i) && i + 1 == siz)
|
|
captionline = true;
|
|
// ignore all struck out text and (caption) insets
|
|
if (par->isDeleted(i)
|
|
|| (par->isInset(i) && par->getInset(i)->lyxCode() == CAPTION_CODE))
|
|
continue;
|
|
if (par->isInset(i)) {
|
|
// Currently, this can only be a quote inset
|
|
// that is output as plain quote here, but
|
|
// we use more generic code anyway.
|
|
otexstringstream ots;
|
|
OutputParams rp = runparams;
|
|
rp.pass_thru = true;
|
|
par->getInset(i)->latex(ots, rp);
|
|
code += ots.str();
|
|
continue;
|
|
}
|
|
char_type c = par->getChar(i);
|
|
// we can only output characters covered by the current
|
|
// encoding!
|
|
try {
|
|
if (runparams.encoding->encodable(c))
|
|
code += c;
|
|
else if (runparams.dryrun) {
|
|
code += "<" + _("LyX Warning: ")
|
|
+ _("uncodable character") + " '";
|
|
code += docstring(1, c);
|
|
code += "'>";
|
|
} else
|
|
uncodable += c;
|
|
} catch (EncodingException & /* e */) {
|
|
if (runparams.dryrun) {
|
|
code += "<" + _("LyX Warning: ")
|
|
+ _("uncodable character") + " '";
|
|
code += docstring(1, c);
|
|
code += "'>";
|
|
} else
|
|
uncodable += c;
|
|
}
|
|
}
|
|
++par;
|
|
// for the inline case, if there are multiple paragraphs
|
|
// they are simply joined. Otherwise, expect latex errors.
|
|
if (par != end && !isInline && !captionline)
|
|
code += "\n";
|
|
}
|
|
if (isInline) {
|
|
static const docstring delimiters =
|
|
from_utf8("!*()-=+|;:'\"`,<.>/?QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm");
|
|
|
|
size_t pos = delimiters.find_first_not_of(code);
|
|
|
|
// This code piece contains all possible special character? !!!
|
|
// Replace ! with a warning message and use ! as delimiter.
|
|
if (pos == string::npos) {
|
|
docstring delim_error = "<" + _("LyX Warning: ")
|
|
+ _("no more lstline delimiters available") + ">";
|
|
code = subst(code, from_ascii("!"), delim_error);
|
|
pos = 0;
|
|
if (!runparams.dryrun && !runparams.silent) {
|
|
// FIXME: warning should be passed to the error dialog
|
|
frontend::Alert::warning(_("Running out of delimiters"),
|
|
_("For inline program listings, one character must be reserved\n"
|
|
"as a delimiter. One of the listings, however, uses all available\n"
|
|
"characters, so none is left for delimiting purposes.\n"
|
|
"For the time being, I have replaced '!' by a warning, but you\n"
|
|
"must investigate!"));
|
|
}
|
|
}
|
|
docstring const delim(1, delimiters[pos]);
|
|
if (use_minted) {
|
|
os << "\\mintinline";
|
|
if (!param_string.empty())
|
|
os << "[" << from_utf8(param_string) << "]";
|
|
os << "{" << ascii_lowercase(minted_language) << "}";
|
|
} else {
|
|
os << "\\lstinline";
|
|
if (!param_string.empty())
|
|
os << "[" << from_utf8(param_string) << "]";
|
|
else if (pos >= delimiters.find('Q'))
|
|
// We need to terminate the command before
|
|
// the delimiter
|
|
os << " ";
|
|
}
|
|
os << delim << code << delim;
|
|
} else if (use_minted) {
|
|
OutputParams rp = runparams;
|
|
rp.moving_arg = true;
|
|
TexString caption = getCaption(rp);
|
|
if (isfloat) {
|
|
os << breakln << "\\begin{listing}";
|
|
if (!float_placement.empty())
|
|
os << '[' << float_placement << "]";
|
|
} else if (captionfirst && !caption.str.empty()) {
|
|
os << breakln << "\\lyxmintcaption[t]{"
|
|
<< move(caption) << "}\n";
|
|
}
|
|
os << breakln << "\\begin{minted}";
|
|
if (!param_string.empty())
|
|
os << "[" << param_string << "]";
|
|
os << "{" << ascii_lowercase(minted_language) << "}\n"
|
|
<< code << breakln << "\\end{minted}\n";
|
|
if (isfloat) {
|
|
if (!caption.str.empty())
|
|
os << "\\caption{" << move(caption) << "}\n";
|
|
os << "\\end{listing}\n";
|
|
} else if (!captionfirst && !caption.str.empty()) {
|
|
os << breakln << "\\lyxmintcaption[b]{"
|
|
<< move(caption) << "}";
|
|
}
|
|
} else {
|
|
OutputParams rp = runparams;
|
|
rp.moving_arg = true;
|
|
TexString caption = getCaption(rp);
|
|
os << breakln << "\\begin{lstlisting}";
|
|
if (param_string.empty() && caption.str.empty())
|
|
os << "\n";
|
|
else {
|
|
if (!runparams.nice)
|
|
os << safebreakln;
|
|
os << "[";
|
|
if (!caption.str.empty()) {
|
|
os << "caption={" << move(caption) << '}';
|
|
if (!param_string.empty())
|
|
os << ',';
|
|
}
|
|
os << from_utf8(param_string) << "]\n";
|
|
}
|
|
os << code << breakln << "\\end{lstlisting}\n";
|
|
}
|
|
|
|
if (encoding_switched){
|
|
// Switch back
|
|
switchEncoding(os.os(), buffer().params(),
|
|
runparams, *save_enc, true, true);
|
|
os << "\\egroup" << breakln;
|
|
runparams.encoding = save_enc;
|
|
}
|
|
|
|
if (!uncodable.empty() && !runparams.silent) {
|
|
// issue a warning about omitted characters
|
|
// FIXME: should be passed to the error dialog
|
|
if (fixedlstenc)
|
|
frontend::Alert::warning(_("Uncodable characters in listings inset"),
|
|
bformat(_("The following characters in one of the program listings are\n"
|
|
"not representable in the current encoding and have been omitted:\n%1$s.\n"
|
|
"This is due to a restriction of the listings package, which does\n"
|
|
"not support your encoding '%2$s'.\n"
|
|
"Toggling 'Use non-TeX fonts' in Document > Settings...\n"
|
|
"might help."),
|
|
uncodable, _(runparams.encoding->guiName())));
|
|
else
|
|
frontend::Alert::warning(_("Uncodable characters in listings inset"),
|
|
bformat(_("The following characters in one of the program listings are\n"
|
|
"not representable in the current encoding and have been omitted:\n%1$s."),
|
|
uncodable));
|
|
}
|
|
}
|
|
|
|
|
|
docstring InsetListings::xhtml(XMLStream & os, OutputParams const & rp) const
|
|
{
|
|
odocstringstream ods;
|
|
XMLStream out(ods);
|
|
|
|
bool const isInline = params().isInline();
|
|
if (isInline)
|
|
out << xml::CompTag("br");
|
|
else {
|
|
out << xml::StartTag("div", "class='float-listings'");
|
|
docstring caption = getCaptionHTML(rp);
|
|
if (!caption.empty())
|
|
out << xml::StartTag("div", "class='listings-caption'")
|
|
<< XMLStream::ESCAPE_NONE
|
|
<< caption << xml::EndTag("div");
|
|
}
|
|
|
|
InsetLayout const & il = getLayout();
|
|
string const & tag = il.htmltag();
|
|
string attr = "class ='listings";
|
|
string const lang = params().getParamValue("language");
|
|
if (!lang.empty())
|
|
attr += " " + lang;
|
|
attr += "'";
|
|
out << xml::StartTag(tag, attr);
|
|
OutputParams newrp = rp;
|
|
newrp.html_disable_captions = true;
|
|
// We don't want to convert dashes here. That's the only conversion we
|
|
// do for XHTML, so this is safe.
|
|
newrp.pass_thru = true;
|
|
docstring def = InsetText::insetAsXHTML(out, newrp, InsetText::JustText);
|
|
out << xml::EndTag(tag);
|
|
|
|
if (isInline) {
|
|
out << xml::CompTag("br");
|
|
// escaping will already have been done
|
|
os << XMLStream::ESCAPE_NONE << ods.str();
|
|
} else {
|
|
out << xml::EndTag("div");
|
|
// In this case, this needs to be deferred, but we'll put it
|
|
// before anything the text itself deferred.
|
|
def = ods.str() + '\n' + def;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
|
|
string InsetListings::contextMenuName() const
|
|
{
|
|
return "context-listings";
|
|
}
|
|
|
|
|
|
void InsetListings::doDispatch(Cursor & cur, FuncRequest & cmd)
|
|
{
|
|
switch (cmd.action()) {
|
|
|
|
case LFUN_INSET_MODIFY: {
|
|
cur.recordUndoInset(this);
|
|
InsetListings::string2params(to_utf8(cmd.argument()), params());
|
|
break;
|
|
}
|
|
|
|
case LFUN_INSET_DIALOG_UPDATE:
|
|
cur.bv().updateDialog("listings", params2string(params()));
|
|
break;
|
|
|
|
default:
|
|
InsetCaptionable::doDispatch(cur, cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool InsetListings::getStatus(Cursor & cur, FuncRequest const & cmd,
|
|
FuncStatus & status) const
|
|
{
|
|
switch (cmd.action()) {
|
|
case LFUN_INSET_MODIFY:
|
|
case LFUN_INSET_DIALOG_UPDATE:
|
|
status.setEnabled(true);
|
|
return true;
|
|
case LFUN_CAPTION_INSERT: {
|
|
// the inset outputs at most one caption
|
|
if (params().isInline() || getCaptionInset()) {
|
|
status.setEnabled(false);
|
|
return true;
|
|
}
|
|
}
|
|
// fall through
|
|
default:
|
|
return InsetCaptionable::getStatus(cur, cmd, status);
|
|
}
|
|
}
|
|
|
|
|
|
docstring const InsetListings::buttonLabel(BufferView const & bv) const
|
|
{
|
|
// FIXME UNICODE
|
|
if (decoration() == InsetLayout::CLASSIC)
|
|
return isOpen(bv) ? _("Listing") : getNewLabel(_("Listing"));
|
|
else
|
|
return getNewLabel(_("Listing"));
|
|
}
|
|
|
|
|
|
void InsetListings::validate(LaTeXFeatures & features) const
|
|
{
|
|
features.useInsetLayout(getLayout());
|
|
string param_string = params().params();
|
|
if (buffer().params().use_minted) {
|
|
features.require("minted");
|
|
OutputParams rp = features.runparams();
|
|
if (!params().isFloat() && !getCaption(rp).str.empty())
|
|
features.require("lyxmintcaption");
|
|
if (features.usePolyglossia() && features.hasRTLLanguage())
|
|
// minted loads color, but color must be loaded before bidi
|
|
// (i.e., polyglossia)
|
|
features.require("color");
|
|
} else {
|
|
features.require("listings");
|
|
if (contains(param_string, "\\color"))
|
|
features.require("color");
|
|
}
|
|
InsetCaptionable::validate(features);
|
|
}
|
|
|
|
|
|
bool InsetListings::showInsetDialog(BufferView * bv) const
|
|
{
|
|
bv->showDialog("listings", params2string(params()),
|
|
const_cast<InsetListings *>(this));
|
|
return true;
|
|
}
|
|
|
|
|
|
TexString InsetListings::getCaption(OutputParams const & runparams) const
|
|
{
|
|
InsetCaption const * ins = getCaptionInset();
|
|
if (ins == 0)
|
|
return TexString();
|
|
|
|
otexstringstream os;
|
|
ins->getArgs(os, runparams);
|
|
ins->getArgument(os, runparams);
|
|
|
|
// TODO: The code below should be moved to support, and then the test
|
|
// in ../tests should be moved there as well.
|
|
|
|
// the caption may contain \label{} but the listings
|
|
// package prefer caption={}, label={}
|
|
TexString cap = os.release();
|
|
if (buffer().params().use_minted
|
|
|| !contains(cap.str, from_ascii("\\label{")))
|
|
return cap;
|
|
// convert from
|
|
// blah1\label{blah2} blah3
|
|
// to
|
|
// blah1 blah3},label={blah2
|
|
// to form options
|
|
// caption={blah1 blah3},label={blah2}
|
|
//
|
|
// NOTE that } is not allowed in blah2.
|
|
regex const reg("(.*)\\\\label\\{(.*?)\\}(.*)");
|
|
string const new_cap("$1$3},label={$2");
|
|
// Remove potential \protect'ion of \label.
|
|
docstring capstr = subst(cap.str, from_ascii("\\protect\\label"),
|
|
from_ascii("\\label"));
|
|
// TexString validity: the substitution preserves the number of newlines.
|
|
// Moreover we assume that $2 does not contain newlines, so that the texrow
|
|
// information remains accurate.
|
|
// Replace '\n' with an improbable character from Private Use Area-A
|
|
// and then return to '\n' after the regex replacement.
|
|
capstr = subst(capstr, char_type('\n'), 0xffffd);
|
|
cap.str = subst(from_utf8(regex_replace(to_utf8(capstr), reg, new_cap)),
|
|
0xffffd, char_type('\n'));
|
|
return cap;
|
|
}
|
|
|
|
|
|
void InsetListings::string2params(string const & in,
|
|
InsetListingsParams & params)
|
|
{
|
|
params = InsetListingsParams();
|
|
if (in.empty())
|
|
return;
|
|
istringstream data(in);
|
|
Lexer lex;
|
|
lex.setStream(data);
|
|
// discard "listings", which is only used to determine inset
|
|
lex.next();
|
|
params.read(lex);
|
|
}
|
|
|
|
|
|
string InsetListings::params2string(InsetListingsParams const & params)
|
|
{
|
|
ostringstream data;
|
|
data << "listings" << ' ';
|
|
params.write(data);
|
|
return data.str();
|
|
}
|
|
|
|
|
|
} // namespace lyx
|