Add basic support for cprotect

This allows (some) verbatim contents in macros, such as \url's with
specific chars (#, % etc.) in section headings or footnotes (#449)
or comments in captions (#9313).

The mentioned two bugs are fixed by this commit.

Note that the implementation is still rather basic and might need
extension for other cases.
This commit is contained in:
Juergen Spitzmueller 2018-04-13 17:46:37 +02:00
parent 6cffd468e0
commit 2ba584957c
13 changed files with 189 additions and 3 deletions

View File

@ -299,6 +299,7 @@
\TestPackage{chicago} \TestPackage{chicago}
\TestPackage{color} % this one should be there if graphics.sty is there. \TestPackage{color} % this one should be there if graphics.sty is there.
\TestPackage{covington} \TestPackage{covington}
\TestPackage{cprotect}
\TestPackage{csquotes} \TestPackage{csquotes}
\TestPackage[koi8-r.def]{cyrillic} \TestPackage[koi8-r.def]{cyrillic}
\TestPackage{dvipost} \TestPackage{dvipost}

View File

@ -19663,6 +19663,79 @@ protect
not not
\emph default \emph default
whether the command should itself be protected.) Default is false. whether the command should itself be protected.) Default is false.
\change_inserted -712698321 1523633958
\end_layout
\begin_layout Description
\change_inserted -712698321 1523634088
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\change_inserted -712698321 1523633961
NeedCProtect
\end_layout
\end_inset
[
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\change_inserted -712698321 1523633958
\emph on
0
\end_layout
\end_inset
,
\begin_inset space \thinspace{}
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\change_inserted -712698321 1523633958
1
\end_layout
\end_inset
] This causes macros that contain this inset to be protected with
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\change_inserted -712698321 1523634038
\backslash
cprotect
\change_unchanged
\end_layout
\end_inset
(cf.
package
\family sans
cprotect
\family default
) if necessary and thus allows (some) verbatim stuff in macros.
Default is false.
\change_unchanged
\end_layout \end_layout
\begin_layout Description \begin_layout Description

View File

@ -7069,6 +7069,34 @@ cancelto
. .
\end_layout \end_layout
\begin_layout Subsection
cprotect
\end_layout
\begin_layout Description
Found:
\begin_inset Info
type "package"
arg "cprotect"
\end_inset
\end_layout
\begin_layout Description
CTAN:
\family typewriter
macros/latex/contrib/cprotect/
\end_layout
\begin_layout Description
Notes: The package
\family sans
cprotect
\family default
is used to allow (some) verbatim stuff in macros.
\end_layout
\begin_layout Subsection \begin_layout Subsection
CJKutf8 CJKutf8
\end_layout \end_layout

View File

@ -150,6 +150,7 @@ InsetLayout Note:Comment
EndHTMLStyle EndHTMLStyle
AddToToc note AddToToc note
IsTocCaption true IsTocCaption true
NeedCProtect true
End End
@ -552,6 +553,7 @@ InsetLayout "Flex:URL"
PassThru true PassThru true
FreeSpacing true FreeSpacing true
ForceLTR true ForceLTR true
NeedCProtect true
Font Font
Family Typewriter Family Typewriter
Color urltext Color urltext

View File

@ -908,6 +908,7 @@ char const * simplefeatures[] = {
// note that the package order here will be the same in the LaTeX-output // note that the package order here will be the same in the LaTeX-output
"array", "array",
"verbatim", "verbatim",
"cprotect",
"longtable", "longtable",
"rotating", "rotating",
"latexsym", "latexsym",

View File

@ -1694,6 +1694,8 @@ void Paragraph::write(ostream & os, BufferParams const & bparams,
void Paragraph::validate(LaTeXFeatures & features) const void Paragraph::validate(LaTeXFeatures & features) const
{ {
d->validate(features); d->validate(features);
if (needsCProtection())
features.require("cprotect");
} }
@ -3376,6 +3378,17 @@ bool Paragraph::isHardHyphenOrApostrophe(pos_type pos) const
} }
bool Paragraph::needsCProtection() const
{
pos_type size = d->text_.size();
for (pos_type i = 0; i < size; ++i)
if (isInset(i))
return getInset(i)->needsCProtection();
return false;
}
FontSpan const & Paragraph::getSpellRange(pos_type pos) const FontSpan const & Paragraph::getSpellRange(pos_type pos) const
{ {
return d->speller_state_.getRange(pos); return d->speller_state_.getRange(pos);

View File

@ -424,6 +424,9 @@ public:
/// True if the element at this point is a hard hyphen or a apostrophe /// True if the element at this point is a hard hyphen or a apostrophe
/// If it is enclosed by spaces return false /// If it is enclosed by spaces return false
bool isHardHyphenOrApostrophe(pos_type pos) const; bool isHardHyphenOrApostrophe(pos_type pos) const;
/// Return true if this paragraph has verbatim content that needs to be
/// protected by \cprotect
bool needsCProtection() const;
/// returns true if at least one line break or line separator has been deleted /// returns true if at least one line break or line separator has been deleted
/// at the beginning of the paragraph (either physically or logically) /// at the beginning of the paragraph (either physically or logically)

View File

@ -594,6 +594,9 @@ public:
/// reject the changes within the inset /// reject the changes within the inset
virtual void rejectChanges() {} virtual void rejectChanges() {}
///
virtual bool needsCProtection() const { return false; }
/// ///
virtual ColorCode backgroundColor(PainterInfo const &) const; virtual ColorCode backgroundColor(PainterInfo const &) const;
/// ///

View File

@ -42,8 +42,8 @@ InsetLayout::InsetLayout() :
htmlisblock_(true), multipar_(true), custompars_(true), htmlisblock_(true), multipar_(true), custompars_(true),
forceplain_(false), passthru_(false), parbreakisnewline_(false), forceplain_(false), passthru_(false), parbreakisnewline_(false),
freespacing_(false), keepempty_(false), forceltr_(false), freespacing_(false), keepempty_(false), forceltr_(false),
forceownlines_(false), needprotect_(false), intoc_(false), forceownlines_(false), needprotect_(false), needcprotect_(false),
spellcheck_(true), resetsfont_(false), display_(true), intoc_(false), spellcheck_(true), resetsfont_(false), display_(true),
forcelocalfontswitch_(false), add_to_toc_(false), is_toc_caption_(false) forcelocalfontswitch_(false), add_to_toc_(false), is_toc_caption_(false)
{ {
labelfont_.setColor(Color_error); labelfont_.setColor(Color_error);
@ -119,6 +119,7 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass)
IL_OBSOLETEDBY, IL_OBSOLETEDBY,
IL_KEEPEMPTY, IL_KEEPEMPTY,
IL_MULTIPAR, IL_MULTIPAR,
IL_NEEDCPROTECT,
IL_NEEDPROTECT, IL_NEEDPROTECT,
IL_PASSTHRU, IL_PASSTHRU,
IL_PASSTHRU_CHARS, IL_PASSTHRU_CHARS,
@ -174,6 +175,7 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass)
{ "leftdelim", IL_LEFTDELIM }, { "leftdelim", IL_LEFTDELIM },
{ "lyxtype", IL_LYXTYPE }, { "lyxtype", IL_LYXTYPE },
{ "multipar", IL_MULTIPAR }, { "multipar", IL_MULTIPAR },
{ "needcprotect", IL_NEEDCPROTECT },
{ "needprotect", IL_NEEDPROTECT }, { "needprotect", IL_NEEDPROTECT },
{ "obsoletedby", IL_OBSOLETEDBY }, { "obsoletedby", IL_OBSOLETEDBY },
{ "parbreakisnewline", IL_PARBREAKISNEWLINE }, { "parbreakisnewline", IL_PARBREAKISNEWLINE },
@ -322,6 +324,9 @@ bool InsetLayout::read(Lexer & lex, TextClass const & tclass)
case IL_NEEDPROTECT: case IL_NEEDPROTECT:
lex >> needprotect_; lex >> needprotect_;
break; break;
case IL_NEEDCPROTECT:
lex >> needcprotect_;
break;
case IL_CONTENTASLABEL: case IL_CONTENTASLABEL:
lex >> contentaslabel_; lex >> contentaslabel_;
break; break;

View File

@ -162,6 +162,8 @@ public:
/// ///
bool isNeedProtect() const { return needprotect_; } bool isNeedProtect() const { return needprotect_; }
/// ///
bool needsCProtect() const { return needcprotect_; }
///
bool isFreeSpacing() const { return freespacing_; } bool isFreeSpacing() const { return freespacing_; }
/// ///
bool isKeepEmpty() const { return keepempty_; } bool isKeepEmpty() const { return keepempty_; }
@ -283,6 +285,8 @@ private:
bool forceownlines_; bool forceownlines_;
/// ///
bool needprotect_; bool needprotect_;
///
bool needcprotect_;
/// should the contents be written to TOC strings? /// should the contents be written to TOC strings?
bool intoc_; bool intoc_;
/// check spelling of this inset? /// check spelling of this inset?

View File

@ -458,7 +458,9 @@ void InsetText::latex(otexstream & os, OutputParams const & runparams) const
// FIXME UNICODE // FIXME UNICODE
// FIXME \protect should only be used for fragile // FIXME \protect should only be used for fragile
// commands, but we do not provide this information yet. // commands, but we do not provide this information yet.
if (runparams.moving_arg) if (hasCProtectContent())
os << "\\cprotect";
else if (runparams.moving_arg)
os << "\\protect"; os << "\\protect";
os << '\\' << from_utf8(il.latexname()); os << '\\' << from_utf8(il.latexname());
if (!il.latexargs().empty()) if (!il.latexargs().empty())
@ -760,6 +762,19 @@ ParagraphList & InsetText::paragraphs()
} }
bool InsetText::hasCProtectContent() const
{
ParagraphList const & pars = paragraphs();
pit_type pend = paragraphs().size();
for (pit_type pit = 0; pit != pend; ++pit) {
Paragraph const & par = pars[pit];
if (par.needsCProtection())
return true;
}
return false;
}
bool InsetText::insetAllowed(InsetCode code) const bool InsetText::insetAllowed(InsetCode code) const
{ {
switch (code) { switch (code) {
@ -1070,4 +1085,35 @@ InsetText::XHTMLOptions operator|(InsetText::XHTMLOptions a1, InsetText::XHTMLOp
return static_cast<InsetText::XHTMLOptions>((int)a1 | (int)a2); return static_cast<InsetText::XHTMLOptions>((int)a1 | (int)a2);
} }
bool InsetText::needsCProtection() const
{
if (!getLayout().needsCProtect())
return false;
// Environments need cprotection regardless the content
if (getLayout().latextype() == InsetLayout::ENVIRONMENT)
return true;
// Commands need cprotection if they contain specific chars
int const nchars_escape = 9;
static char_type const chars_escape[nchars_escape] = {
'&', '_', '$', '%', '#', '^', '{', '}', '\\'};
ParagraphList const & pars = paragraphs();
pit_type pend = paragraphs().size();
for (pit_type pit = 0; pit != pend; ++pit) {
Paragraph const & par = pars[pit];
if (par.needsCProtection())
return true;
docstring const pars = par.asString();
for (int k = 0; k < nchars_escape; k++) {
if (contains(pars, chars_escape[k]))
return true;
}
}
return false;
}
} // namespace lyx } // namespace lyx

View File

@ -223,6 +223,9 @@ public:
/// ///
bool confirmDeletion() const { return !text().empty(); } bool confirmDeletion() const { return !text().empty(); }
///
bool needsCProtection() const;
protected: protected:
/// ///
void iterateForToc(DocIterator const & cdit, bool output_active, void iterateForToc(DocIterator const & cdit, bool output_active,
@ -238,6 +241,8 @@ private:
void closeAddToTocForParagraph(pit_type start, pit_type end, void closeAddToTocForParagraph(pit_type start, pit_type end,
TocBackend & backend) const; TocBackend & backend) const;
/// ///
bool hasCProtectContent() const;
///
bool drawFrame_; bool drawFrame_;
/// ///
ColorCode frame_color_; ColorCode frame_color_;

View File

@ -654,6 +654,8 @@ void parStartCommand(Paragraph const & par, otexstream & os,
{ {
switch (style.latextype) { switch (style.latextype) {
case LATEX_COMMAND: case LATEX_COMMAND:
if (par.needsCProtection())
os << "\\cprotect";
os << '\\' << from_ascii(style.latexname()); os << '\\' << from_ascii(style.latexname());
// Command arguments // Command arguments