Introduce InsetIndexMacros

This adds native macros for subindexes (!level), |see and |seealso
as well as native support for ranges |( |) and pagination format
-- e.g., |textbf -- via the index dialog

Resolves #12478, #7232 and #5014

The feature is complete (incl. tex2lyx) except for

* file format change and lyx2lyx
* docbook/xhtml
* documentation
This commit is contained in:
Juergen Spitzmueller 2022-04-19 11:46:08 +02:00
parent 335f158896
commit 7d7b21ec3e
21 changed files with 1467 additions and 171 deletions

View File

@ -431,6 +431,36 @@ InsetLayout Index
PassThruChars @|!
End
InsetLayout IndexMacro:see
LabelString See
Decoration classic
Font
Size Small
EndFont
LabelFont
Color indexlabel
Size Small
EndFont
MultiPar false
CustomPars false
ForcePlain true
End
InsetLayout IndexMacro:seealso
CopyStyle IndexMacro:see
LabelString "See also"
End
InsetLayout IndexMacro:sortkey
CopyStyle IndexMacro:see
LabelString "Sort as"
End
InsetLayout IndexMacro:subindex
CopyStyle IndexMacro:see
LabelString "Subindex"
End
InsetLayout Box
LabelFont
Color foreground

View File

@ -631,6 +631,23 @@ Menuset
Menu "context-index"
IndicesContext
End
Menu "context-edit-index"
OptItem "Insert Subentry|b" "indexmacro-insert subindex"
OptItem "Insert Sortkey|k" "indexmacro-insert sortkey"
OptItem "Insert See Reference|e" "indexmacro-insert see"
OptItem "Insert See also Reference|a" "indexmacro-insert seealso"
End
#
# IndexMacro context menu
#
Menu "context-indexmacro"
OptItem "See|e" "inset-modify changetype see"
OptItem "See also|a" "inset-modify changetype seealso"
End
#
# Index Lists context menu

View File

@ -391,6 +391,7 @@ Menuset
Item "Label...|L" "label-insert"
Captions
Indices
OptSubmenu "Index Properties" "index_properties"
Item "Nomenclature Entry...|y" "nomencl-insert"
Separator
Item "Table...|T" "tabular-insert"
@ -532,6 +533,13 @@ Menuset
Item "Double Frame|u" "box-insert Doublebox"
End
Menu "index_properties"
OptItem "Subentry|b" "indexmacro-insert subindex"
OptItem "Sortkey|k" "indexmacro-insert sortkey"
OptItem "See|e" "indexmacro-insert see"
OptItem "See also|a" "indexmacro-insert seealso"
End
Menu "insert_note"
Item "LyX Note|N" "note-insert Note"
Item "Comment|C" "note-insert Comment"

View File

@ -503,6 +503,8 @@ enum FuncCode
LFUN_FINISHED_DOWN, // lasgouttes 20210629
LFUN_FINISHED_UP, // lasgouttes 20210629
LFUN_BRANCH_SYNC_ALL, // sanda 20220415
LFUN_INDEXMACRO_INSERT, // spitz 20220220
// 395
LFUN_LASTACTION // end of the table
};

View File

@ -2323,6 +2323,16 @@ void LyXAction::init()
*/
{ LFUN_IN_MATHMACROTEMPLATE, "in-mathmacrotemplate", Noop, Math },
/*!
* \var lyx::FuncCode lyx::LFUN_INDEXMACRO_INSERT
* \li Action: Inserts special Index macros into the document.
* \li Syntax: indexmacro-insert <type>
* \li Params: <type>: see, seealso, subindex, sortkey.
* \li Origin: spitz, 20 Feb 2022
* \endvar
*/
{ LFUN_INDEXMACRO_INSERT, "indexmacro-insert", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_IPAMACRO_INSERT

View File

@ -565,6 +565,7 @@ SOURCEFILESINSETS = \
insets/InsetHyperlink.cpp \
insets/InsetInclude.cpp \
insets/InsetIndex.cpp \
insets/InsetIndexMacro.cpp \
insets/InsetInfo.cpp \
insets/InsetIPA.cpp \
insets/InsetIPAMacro.cpp \

View File

@ -58,6 +58,7 @@
#include "insets/InsetGraphics.h"
#include "insets/InsetGraphicsParams.h"
#include "insets/InsetInfo.h"
#include "insets/InsetIndexMacro.h"
#include "insets/InsetIPAMacro.h"
#include "insets/InsetNewline.h"
#include "insets/InsetQuotes.h"
@ -2107,6 +2108,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_BRANCH_INSERT:
case LFUN_PHANTOM_INSERT:
case LFUN_ERT_INSERT:
case LFUN_INDEXMACRO_INSERT:
case LFUN_LISTING_INSERT:
case LFUN_MARGINALNOTE_INSERT:
case LFUN_ARGUMENT_INSERT:
@ -3285,6 +3287,14 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
}
code = HYPERLINK_CODE;
break;
case LFUN_INDEXMACRO_INSERT: {
string const arg = cmd.getArg(0);
if (arg == "sortkey")
code = INDEXMACRO_SORTKEY_CODE;
else
code = INDEXMACRO_CODE;
break;
}
case LFUN_IPAMACRO_INSERT: {
string const arg = cmd.getArg(0);
if (arg == "deco")

View File

@ -34,6 +34,7 @@
#include "insets/InsetHyperlink.h"
#include "insets/InsetInclude.h"
#include "insets/InsetIndex.h"
#include "insets/InsetIndexMacro.cpp"
#include "insets/InsetInfo.h"
#include "insets/InsetIPA.h"
#include "insets/InsetIPAMacro.h"
@ -159,6 +160,16 @@ Inset * createInsetHelper(Buffer * buf, FuncRequest const & cmd)
return new InsetIPADeco(buf, arg2);
}
case LFUN_INDEXMACRO_INSERT: {
string const arg = cmd.getArg(0);
if (arg != "see" && arg != "seealso"
&& arg != "subindex" && arg != "sortkey") {
LYXERR0("LFUN_IPAMACRO_INSERT: wrong argument");
return nullptr;
}
return new InsetIndexMacro(buf, arg);
}
case LFUN_ERT_INSERT:
return new InsetERT(buf);
@ -667,6 +678,9 @@ Inset * readInset(Lexer & lex, Buffer * buf)
inset.reset(new InsetCaption(buf, s));
} else if (tmptok == "Index") {
inset.reset(new InsetIndex(buf, InsetIndexParams()));
} else if (tmptok == "IndexMacro") {
string s = lex.getString();
inset.reset(new InsetIndexMacro(buf, s));
} else if (tmptok == "FloatList") {
inset.reset(new InsetFloatList(buf));
} else if (tmptok == "Info") {

View File

@ -20,6 +20,7 @@
#include "BufferParams.h"
#include "FuncRequest.h"
#include "IndicesList.h"
#include "insets/InsetIndex.h"
#include <QPushButton>
@ -37,6 +38,18 @@ GuiIndex::GuiIndex(GuiView & lv)
this, SLOT(slotButtonBox(QAbstractButton *)));
connect(indicesCO, SIGNAL(activated(int)), this, SLOT(change_adaptor()));
rangeCO->addItem(qt_("None"), InsetIndexParams::None);
rangeCO->addItem(qt_("Start"), InsetIndexParams::Start);
rangeCO->addItem(qt_("End"), InsetIndexParams::End);
connect(rangeCO, SIGNAL(activated(int)), this, SLOT(change_adaptor()));
pageFormatCO->addItem(qt_("Default"), toqstr("default"));
pageFormatCO->addItem(qt_("Bold"), toqstr("textbf"));
pageFormatCO->addItem(qt_("Italic"), toqstr("textit"));
pageFormatCO->addItem(qt_("Emphasized"), toqstr("emph"));
pageFormatCO->addItem(qt_("Custom"), toqstr("custom"));
connect(pageFormatCO, SIGNAL(activated(int)), this, SLOT(pageFormatChanged(int)));
bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
@ -49,11 +62,26 @@ void GuiIndex::change_adaptor()
}
void GuiIndex::pageFormatChanged(int i)
{
QString const pf = pageFormatCO->itemData(i).toString();
pageFormatLE->setEnabled(pf == "custom");
change_adaptor();
}
void GuiIndex::updateContents()
{
typedef IndicesList::const_iterator const_iterator;
IndicesList const & indiceslist = buffer().params().indiceslist();
BufferParams const & bp = buffer().masterBuffer()->params();
indicesGB->setEnabled(bp.use_indices);
QString const pf = pageFormatCO->itemData(
pageFormatCO->currentIndex()).toString();
pageFormatLE->setEnabled(pf == "custom");
IndicesList const & indiceslist = bp.indiceslist();
docstring const cur_index = params_.index;
indicesCO->clear();
@ -64,8 +92,19 @@ void GuiIndex::updateContents()
indicesCO->addItem(toqstr(it->index()),
QVariant(toqstr(it->shortcut())));
int const pos = indicesCO->findData(toqstr(cur_index));
int pos = indicesCO->findData(toqstr(cur_index));
indicesCO->setCurrentIndex(pos);
pos = pageFormatCO->findData(toqstr(params_.pagefmt));
if (pos == -1) {
pos = pageFormatCO->findData("custom");
pageFormatLE->setText(toqstr(params_.pagefmt));
} else
pageFormatLE->clear();
pageFormatCO->setCurrentIndex(pos);
pos = rangeCO->findData(params_.range);
rangeCO->setCurrentIndex(pos);
}
@ -73,7 +112,15 @@ void GuiIndex::applyView()
{
QString const index = indicesCO->itemData(
indicesCO->currentIndex()).toString();
int const range = rangeCO->itemData(
rangeCO->currentIndex()).toInt();
QString const pagefmt = pageFormatCO->itemData(
pageFormatCO->currentIndex()).toString();
params_.index = qstring_to_ucs4(index);
params_.range = InsetIndexParams::PageRange(range);
params_.pagefmt = (pagefmt == "custom")
? fromqstr(pageFormatLE->text())
: fromqstr(pagefmt);
}

View File

@ -31,6 +31,7 @@ public:
private Q_SLOTS:
void change_adaptor();
void pageFormatChanged(int);
private:
/// Apply changes

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>262</width>
<height>121</height>
<width>384</width>
<height>288</height>
</rect>
</property>
<property name="windowTitle">
@ -16,50 +16,119 @@
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
<widget class="QGroupBox" name="indicesGB">
<property name="title">
<string>Available I&amp;ndexes</string>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="indicesLA">
<property name="text">
<string>Available I&amp;ndexes:</string>
</property>
<property name="buddy">
<cstring>indicesCO</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="indicesCO">
<property name="toolTip">
<string>Select the index this entry should be listed in.</string>
</property>
</widget>
</item>
</layout>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QComboBox" name="indicesCO">
<property name="toolTip">
<string>Select the index this entry should be listed in.</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="paginationGB">
<property name="title">
<string>&amp;Pagination</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="rangeLA">
<property name="text">
<string>Page &amp;Range:</string>
</property>
<property name="buddy">
<cstring>rangeCO</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="rangeCO">
<property name="toolTip">
<string>If the entry spans multiple pages, you can start or end the range here</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>112</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="pageFormatLA">
<property name="text">
<string>&amp;Format:</string>
</property>
<property name="buddy">
<cstring>pageFormatCO</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="pageFormatCO">
<property name="toolTip">
<string>Customize the format of the page number here. Note that the format is not used with &quot;See&quot; and &quot;See also&quot; references.</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="pageFormatLE">
<property name="toolTip">
<string>Enter custom command here (without leading backslash).</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>

View File

@ -239,6 +239,10 @@ enum InsetCode {
///
COUNTER_CODE,
///
INDEXMACRO_CODE, // 110
///
INDEXMACRO_SORTKEY_CODE,
///
INSET_CODE_SIZE
};

View File

@ -11,6 +11,7 @@
#include <config.h>
#include "InsetIndex.h"
#include "InsetIndexMacro.h"
#include "Buffer.h"
#include "BufferParams.h"
@ -19,10 +20,13 @@
#include "Cursor.h"
#include "DispatchResult.h"
#include "Encoding.h"
#include "ErrorList.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "IndicesList.h"
#include "InsetList.h"
#include "Language.h"
#include "LaTeX.h"
#include "LaTeXFeatures.h"
#include "Lexer.h"
#include "output_latex.h"
@ -37,6 +41,7 @@
#include "support/FileName.h"
#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/Translator.h"
#include "frontends/alert.h"
@ -51,6 +56,62 @@ using namespace lyx::support;
namespace lyx {
namespace {
typedef Translator<string, InsetIndexParams::PageRange> PageRangeTranslator;
typedef Translator<docstring, InsetIndexParams::PageRange> PageRangeTranslatorLoc;
PageRangeTranslator const init_insetindexpagerangetranslator()
{
PageRangeTranslator translator("none", InsetIndexParams::None);
translator.addPair("start", InsetIndexParams::Start);
translator.addPair("end", InsetIndexParams::End);
return translator;
}
PageRangeTranslator const init_insetindexpagerangetranslator_latex()
{
PageRangeTranslator translator("", InsetIndexParams::None);
translator.addPair("(", InsetIndexParams::Start);
translator.addPair(")", InsetIndexParams::End);
return translator;
}
PageRangeTranslatorLoc const init_insetindexpagerangetranslator_loc()
{
PageRangeTranslatorLoc translator(docstring(), InsetIndexParams::None);
translator.addPair(_("Starts page range"), InsetIndexParams::Start);
translator.addPair(_("Ends page range"), InsetIndexParams::End);
return translator;
}
PageRangeTranslator const & insetindexpagerangetranslator()
{
static PageRangeTranslator const prtranslator =
init_insetindexpagerangetranslator();
return prtranslator;
}
PageRangeTranslatorLoc const & insetindexpagerangetranslator_loc()
{
static PageRangeTranslatorLoc const translator =
init_insetindexpagerangetranslator_loc();
return translator;
}
PageRangeTranslator const & insetindexpagerangetranslator_latex()
{
static PageRangeTranslator const lttranslator =
init_insetindexpagerangetranslator_latex();
return lttranslator;
}
} // namespace anon
/////////////////////////////////////////////////////////////////////
//
// InsetIndex
@ -59,7 +120,7 @@ namespace lyx {
InsetIndex::InsetIndex(Buffer * buf, InsetIndexParams const & params)
: InsetCollapsible(buf), params_(params)
: InsetCollapsible(buf), params_(params)
{}
@ -91,106 +152,108 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
return;
}
// For the sorting key, we use the plaintext version
odocstringstream ourplain;
InsetText::plaintext(ourplain, runparams);
// These are the LaTeX and plaintext representations
docstring latexstr = ourlatex.str();
docstring plainstr = ourplain.str();
// This will get what follows | if anything does,
// the command (e.g., see, textbf) for pagination
// formatting
docstring cmd;
// Check for the | separator to strip the cmd.
// This goes wrong on an escaped "|", but as the escape
// character can be changed in style files, we cannot
// prevent that.
size_t pos = latexstr.find(from_ascii("|"));
if (pos != docstring::npos) {
// Put the bit after "|" into cmd...
cmd = latexstr.substr(pos + 1);
// ...and erase that stuff from latexstr
latexstr = latexstr.erase(pos);
// ...as well as from plainstr
size_t ppos = plainstr.find(from_ascii("|"));
if (ppos < plainstr.size())
plainstr.erase(ppos);
else
LYXERR0("The `|' separator was not found in the plaintext version!");
}
// Separate the entries and subentries, i.e., split on "!".
// This goes wrong on an escaped "!", but as the escape
// character can be changed in style files, we cannot
// prevent that.
std::vector<docstring> const levels =
getVectorFromString(latexstr, from_ascii("!"), true);
std::vector<docstring> const levels_plain =
getVectorFromString(plainstr, from_ascii("!"), true);
vector<docstring>::const_iterator it = levels.begin();
vector<docstring>::const_iterator end = levels.end();
vector<docstring>::const_iterator it2 = levels_plain.begin();
bool first = true;
for (; it != end; ++it) {
// The separator needs to be put back when
// writing the levels, except for the first level
if (!first)
os << '!';
else
first = false;
// Now here comes the reason for this whole procedure:
// We try to correctly sort macros and formatted strings.
// If we find a command, prepend a plain text
// version of the content to get sorting right,
// e.g. \index{LyX@\LyX}, \index{text@\textbf{text}}.
// We do this on all levels.
// We don't do it if the level already contains a '@', though.
if (contains(*it, '\\') && !contains(*it, '@')) {
// Plaintext might return nothing (e.g. for ERTs).
// In that case, we use LaTeX.
docstring const spart =
(it2 < levels_plain.end() && !(*it2).empty())
? *it2 : *it;
// Now we need to validate that all characters in
// the sorting part are representable in the current
// encoding. If not try the LaTeX macro which might
// or might not be a good choice, and issue a warning.
pair<docstring, docstring> spart_latexed =
runparams.encoding->latexString(spart, runparams.dryrun);
if (!spart_latexed.second.empty())
LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
if (spart != spart_latexed.first && !runparams.dryrun) {
// FIXME: warning should be passed to the error dialog
frontend::Alert::warning(_("Index sorting failed"),
bformat(_("LyX's automatic index sorting algorithm faced\n"
"problems with the entry '%1$s'.\n"
"Please specify the sorting of this entry manually, as\n"
"explained in the User Guide."), spart));
}
// Remove remaining \'s from the sort key
docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
// Plain quotes need to be escaped, however (#10649), as this
// is the default escape character
ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
// Now insert the sortkey, separated by '@'.
os << ppart;
os << '@';
if (hasSortKey()) {
getSortkey(os, runparams);
os << "@";
os << ourlatex.str();
getSubentries(os, runparams);
if (hasSeeRef()) {
os << "|";
os << insetindexpagerangetranslator_latex().find(params_.range);
getSeeRefs(os, runparams);
}
} else {
// We check whether we need a sort key.
// If so, we use the plaintext version
odocstringstream ourplain;
InsetText::plaintext(ourplain, runparams);
// These are the LaTeX and plaintext representations
docstring latexstr = ourlatex.str();
docstring plainstr = ourplain.str();
// This will get what follows | if anything does,
// the command (e.g., see, textbf) for pagination
// formatting
docstring cmd;
if (hasSeeRef()) {
odocstringstream seeref;
otexstream otsee(seeref);
getSeeRefs(otsee, runparams);
cmd = seeref.str();
} else if (!params_.pagefmt.empty() && params_.pagefmt != "default") {
cmd = from_utf8(params_.pagefmt);
} else {
// Check for the | separator to strip the cmd.
// This goes wrong on an escaped "|", but as the escape
// character can be changed in style files, we cannot
// prevent that.
size_t pos = latexstr.find(from_ascii("|"));
if (pos != docstring::npos) {
// Put the bit after "|" into cmd...
cmd = latexstr.substr(pos + 1);
// ...and erase that stuff from latexstr
latexstr = latexstr.erase(pos);
// ...as well as from plainstr
size_t ppos = plainstr.find(from_ascii("|"));
if (ppos < plainstr.size())
plainstr.erase(ppos);
else
LYXERR0("The `|' separator was not found in the plaintext version!");
}
}
odocstringstream subentries;
otexstream otsub(subentries);
getSubentries(otsub, runparams);
if (subentries.str().empty()) {
// Separate the entries and subentries, i.e., split on "!".
// This goes wrong on an escaped "!", but as the escape
// character can be changed in style files, we cannot
// prevent that.
std::vector<docstring> const levels =
getVectorFromString(latexstr, from_ascii("!"), true);
std::vector<docstring> const levels_plain =
getVectorFromString(plainstr, from_ascii("!"), true);
vector<docstring>::const_iterator it = levels.begin();
vector<docstring>::const_iterator end = levels.end();
vector<docstring>::const_iterator it2 = levels_plain.begin();
bool first = true;
for (; it != end; ++it) {
// The separator needs to be put back when
// writing the levels, except for the first level
if (!first)
os << '!';
else
first = false;
// Now here comes the reason for this whole procedure:
// We try to correctly sort macros and formatted strings.
// If we find a command, prepend a plain text
// version of the content to get sorting right,
// e.g. \index{LyX@\LyX}, \index{text@\textbf{text}}.
// We do this on all levels.
// We don't do it if the level already contains a '@', though.
// Plaintext might return nothing (e.g. for ERTs).
// In that case, we use LaTeX.
docstring const spart = (levels_plain.empty() || (*it2).empty()) ? *it : *it2;
processLatexSorting(os, runparams, *it, spart);
if (it2 < levels_plain.end())
++it2;
}
} else {
processLatexSorting(os, runparams, latexstr, plainstr);
os << subentries.str();
}
// At last, re-insert the command, separated by "|"
if (!cmd.empty()) {
os << "|"
<< insetindexpagerangetranslator_latex().find(params_.range)
<< cmd;
}
// Insert the actual level text
docstring const tpart = *it;
os << tpart;
if (it2 < levels_plain.end())
++it2;
}
// At last, re-insert the command, separated by "|"
if (!cmd.empty()) {
os << "|" << cmd;
}
os << '}';
@ -203,6 +266,45 @@ void InsetIndex::latex(otexstream & ios, OutputParams const & runparams_in) cons
}
void InsetIndex::processLatexSorting(otexstream & os, OutputParams const & runparams,
docstring const latex, docstring const spart) const
{
if (contains(latex, '\\') && !contains(latex, '@')) {
// Now we need to validate that all characters in
// the sorting part are representable in the current
// encoding. If not try the LaTeX macro which might
// or might not be a good choice, and issue a warning.
pair<docstring, docstring> spart_latexed =
runparams.encoding->latexString(spart, runparams.dryrun);
if (!spart_latexed.second.empty())
LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
if (spart != spart_latexed.first && !runparams.dryrun) {
TeXErrors terr;
ErrorList & errorList = buffer().errorList("Export");
docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
"problems with the entry '%1$s'.\n"
"Please specify the sorting of this entry manually, as "
"explained in the User Guide."), spart);
Paragraph const & par = buffer().paragraphs().front();
errorList.push_back(ErrorItem(_("Index sorting failed"), s,
{par.id(), 0}, {par.id(), -1}));
buffer().bufferErrors(terr, errorList);
}
// Remove remaining \'s from the sort key
docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
// Plain quotes need to be escaped, however (#10649), as this
// is the default escape character
ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
// Now insert the sortkey, separated by '@'.
os << ppart;
os << '@';
}
// Insert the actual level text
os << latex;
}
void InsetIndex::docbook(XMLStream & xs, OutputParams const & runparams) const
{
// Get the content of the inset as LaTeX, as some things may be encoded as ERT (like {}).
@ -445,6 +547,8 @@ void InsetIndex::doDispatch(Cursor & cur, FuncRequest & cmd)
InsetIndex::string2params(to_utf8(cmd.argument()), params);
cur.recordUndoInset(this);
params_.index = params.index;
params_.range = params.range;
params_.pagefmt = params.pagefmt;
// what we really want here is a TOC update, but that means
// a full buffer update
cur.forceBufferUpdate();
@ -485,6 +589,9 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
flag.setEnabled(realbuffer.params().use_indices);
return true;
}
case LFUN_INDEXMACRO_INSERT:
return macrosPossible(cmd.getArg(0));
default:
return InsetCollapsible::getStatus(cur, cmd, flag);
@ -492,6 +599,125 @@ bool InsetIndex::getStatus(Cursor & cur, FuncRequest const & cmd,
}
void InsetIndex::getSortkey(otexstream & os, OutputParams const & runparams) const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
InsetIndexMacro const & iim =
static_cast<InsetIndexMacro const &>(inset);
iim.getLatex(os, runparams);
return;
}
}
}
void InsetIndex::getSubentries(otexstream & os, OutputParams const & runparams) const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
int i = 0;
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_CODE) {
InsetIndexMacro const & iim =
static_cast<InsetIndexMacro const &>(inset);
if (iim.params().type == InsetIndexMacroParams::Subindex) {
++i;
if (i > 2)
return;
os << "!";
iim.getLatex(os, runparams);
}
}
}
}
void InsetIndex::getSeeRefs(otexstream & os, OutputParams const & runparams) const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_CODE) {
InsetIndexMacro const & iim =
static_cast<InsetIndexMacro const &>(inset);
if (iim.params().type == InsetIndexMacroParams::See
|| iim.params().type == InsetIndexMacroParams::Seealso) {
iim.getLatex(os, runparams);
return;
}
}
}
}
bool InsetIndex::hasSeeRef() const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_CODE) {
InsetIndexMacro const & iim =
static_cast<InsetIndexMacro const &>(inset);
if (iim.params().type == InsetIndexMacroParams::See
|| iim.params().type == InsetIndexMacroParams::Seealso)
return true;
}
}
return false;
}
bool InsetIndex::hasSortKey() const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
return true;
}
return false;
}
bool InsetIndex::macrosPossible(string const type) const
{
if (type != "see" && type != "seealso"
&& type != "sortkey" && type != "subindex")
return false;
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
int subidxs = 0;
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (type == "sortkey" && inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
return false;
if (inset.lyxCode() == INDEXMACRO_CODE) {
InsetIndexMacro const & iim = static_cast<InsetIndexMacro const &>(inset);
if ((type == "see" || type == "seealso")
&& (iim.params().type == InsetIndexMacroParams::See
|| iim.params().type == InsetIndexMacroParams::Seealso))
return false;
if (type == "subindex"
&& iim.params().type == InsetIndexMacroParams::Subindex) {
++subidxs;
if (subidxs > 1)
return false;
}
}
}
return true;
}
ColorCode InsetIndex::labelColor() const
{
if (params_.index.empty() || params_.index == from_ascii("idx"))
@ -520,7 +746,21 @@ docstring InsetIndex::toolTip(BufferView const &, int, int) const
tip += ")";
}
tip += ": ";
return toolTipText(tip);
docstring res = toolTipText(tip);
if (!insetindexpagerangetranslator_loc().find(params_.range).empty())
res += "\n" + insetindexpagerangetranslator_loc().find(params_.range);
if (!params_.pagefmt.empty() && params_.pagefmt != "default") {
res += "\n" + _("Pagination format:") + " ";
if (params_.pagefmt == "textbf")
res += _("bold");
else if (params_.pagefmt == "textit")
res += _("italic");
else if (params_.pagefmt == "emph")
res += _("emphasized");
else
res += from_utf8(params_.pagefmt);
}
return res;
}
@ -541,9 +781,14 @@ docstring const InsetIndex::buttonLabel(BufferView const & bv) const
label += ")";
}
docstring res;
if (!il.contentaslabel() || geometry(bv) != ButtonOnly)
return label;
return getNewLabel(label);
res = label;
else
res = getNewLabel(label);
if (!insetindexpagerangetranslator_latex().find(params_.range).empty())
res += " " + from_ascii(insetindexpagerangetranslator_latex().find(params_.range));
return res;
}
@ -621,12 +866,33 @@ string InsetIndex::contextMenuName() const
}
bool InsetIndex::hasSettings() const
string InsetIndex::contextMenu(BufferView const & bv, int x, int y) const
{
return buffer().masterBuffer()->params().use_indices;
// We override the implementation of InsetCollapsible,
// because we have eytra entries.
string owncm = "context-edit-index;";
return owncm + InsetCollapsible::contextMenu(bv, x, y);
}
bool InsetIndex::hasSettings() const
{
return true;
}
bool InsetIndex::insetAllowed(InsetCode code) const
{
switch (code) {
case INDEXMACRO_CODE:
case INDEXMACRO_SORTKEY_CODE:
return true;
case INDEX_CODE:
return false;
default:
return InsetCollapsible::insetAllowed(code);
}
}
/////////////////////////////////////////////////////////////////////
@ -644,6 +910,12 @@ void InsetIndexParams::write(ostream & os) const
else
os << "idx";
os << '\n';
os << "range "
<< insetindexpagerangetranslator().find(range)
<< '\n';
os << "pageformat "
<< pagefmt
<< '\n';
}
@ -653,6 +925,16 @@ void InsetIndexParams::read(Lexer & lex)
index = lex.getDocString();
else
index = from_ascii("idx");
if (lex.checkFor("range")) {
string st = lex.getString();
if (lex.eatLine()) {
st = lex.getString();
range = insetindexpagerangetranslator().find(lex.getString());
}
}
if (lex.checkFor("pageformat") && lex.eatLine()) {
pagefmt = lex.getString();
}
}

View File

@ -21,15 +21,24 @@ namespace lyx {
class InsetIndexParams {
public:
enum PageRange {
None,
Start,
End
};
///
explicit InsetIndexParams(docstring const & b = docstring())
: index(b) {}
: index(b), range(None), pagefmt("default") {}
///
void write(std::ostream & os) const;
///
void read(Lexer & lex);
///
docstring index;
///
PageRange range;
///
std::string pagefmt;
};
@ -63,6 +72,9 @@ private:
///
void latex(otexstream &, OutputParams const &) const override;
///
void processLatexSorting(otexstream &, OutputParams const &,
docstring const, docstring const) const;
///
bool showInsetDialog(BufferView *) const override;
///
bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const override;
@ -80,11 +92,27 @@ private:
/// Updates needed features for this inset.
void validate(LaTeXFeatures & features) const override;
///
void getSortkey(otexstream &, OutputParams const &) const;
///
void getSubentries(otexstream &, OutputParams const &) const;
///
void getSeeRefs(otexstream &, OutputParams const &) const;
///
bool hasSeeRef() const;
///
bool hasSortKey() const;
///
bool macrosPossible(std::string const type) const;
///
std::string contextMenuName() const override;
///
std::string contextMenu(BufferView const &, int, int) const override;
///
Inset * clone() const override { return new InsetIndex(*this); }
/// Is the content of this inset part of the immediate text sequence?
bool isPartOfTextSequence() const override { return false; }
///
bool insetAllowed(InsetCode code) const override;
///
friend class InsetIndexParams;

View File

@ -0,0 +1,384 @@
/**
* \file InsetIndexMacro.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Jürgen Spitzmüller
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "InsetIndexMacro.h"
#include "Buffer.h"
#include "BufferParams.h"
#include "Cursor.h"
#include "Dimension.h"
#include "Encoding.h"
#include "ErrorList.h"
#include "FontInfo.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "InsetLayout.h"
#include "InsetList.h"
#include "LaTeX.h"
#include "LaTeXFeatures.h"
#include "Lexer.h"
#include "MetricsInfo.h"
#include "xml.h"
#include "texstream.h"
#include "frontends/alert.h"
#include "support/debug.h"
#include "support/docstream.h"
#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/Translator.h"
using namespace std;
using namespace lyx::support;
namespace lyx {
namespace {
typedef Translator<string, InsetIndexMacroParams::Type> InsetIndexMacroTranslator;
typedef Translator<docstring, InsetIndexMacroParams::Type> InsetIndexMacroTranslatorLoc;
InsetIndexMacroTranslator const init_insetindexmacrotranslator()
{
InsetIndexMacroTranslator translator("see", InsetIndexMacroParams::See);
translator.addPair("seealso", InsetIndexMacroParams::Seealso);
translator.addPair("subindex", InsetIndexMacroParams::Subindex);
translator.addPair("sortkey", InsetIndexMacroParams::Sortkey);
return translator;
}
InsetIndexMacroTranslatorLoc const init_insetindexmacrotranslator_loc()
{
InsetIndexMacroTranslatorLoc translator(_("See"), InsetIndexMacroParams::See);
translator.addPair(_("See also"), InsetIndexMacroParams::Seealso);
translator.addPair(_("Subindex"), InsetIndexMacroParams::Subindex);
translator.addPair(_("Sort as"), InsetIndexMacroParams::Sortkey);
return translator;
}
InsetIndexMacroTranslator const & insetindexmacrotranslator()
{
static InsetIndexMacroTranslator const macrotranslator =
init_insetindexmacrotranslator();
return macrotranslator;
}
InsetIndexMacroTranslatorLoc const & insetindexmacrotranslator_loc()
{
static InsetIndexMacroTranslatorLoc const translator =
init_insetindexmacrotranslator_loc();
return translator;
}
} // namespace
InsetIndexMacroParams::InsetIndexMacroParams()
: type(See)
{}
void InsetIndexMacroParams::write(ostream & os) const
{
string const label = insetindexmacrotranslator().find(type);
os << "IndexMacro " << label << "\n";
}
void InsetIndexMacroParams::read(Lexer & lex)
{
string label;
lex >> label;
if (lex)
type = insetindexmacrotranslator().find(label);
}
/////////////////////////////////////////////////////////////////////
//
// InsetIndexMacro
//
/////////////////////////////////////////////////////////////////////
InsetIndexMacro::InsetIndexMacro(Buffer * buf, string const & label)
: InsetCollapsible(buf)
{
setDrawFrame(true);
setFrameColor(Color_insetframe);
params_.type = insetindexmacrotranslator().find(label);
}
InsetIndexMacro::~InsetIndexMacro()
{}
docstring InsetIndexMacro::layoutName() const
{
return from_ascii("IndexMacro:" + insetindexmacrotranslator().find(params_.type));
}
InsetCode InsetIndexMacro::lyxCode() const
{
return params_.type == InsetIndexMacroParams::Sortkey
? INDEXMACRO_SORTKEY_CODE
: INDEXMACRO_CODE;
}
void InsetIndexMacro::write(ostream & os) const
{
params_.write(os);
InsetCollapsible::write(os);
}
void InsetIndexMacro::read(Lexer & lex)
{
params_.read(lex);
InsetCollapsible::read(lex);
}
void InsetIndexMacro::getLatex(otexstream & os, OutputParams const & runparams) const
{
if (params_.type == InsetIndexMacroParams::Subindex) {
if (hasSortKey()) {
getSortkey(os, runparams);
os << "@";
} else {
odocstringstream ourlatex;
otexstream ots(ourlatex);
InsetText::latex(ots, runparams);
odocstringstream ourplain;
InsetText::plaintext(ourplain, runparams);
// These are the LaTeX and plaintext representations
docstring latexstr = ourlatex.str();
docstring plainstr = ourplain.str();
processLatexSorting(os, runparams, latexstr, plainstr);
}
return;
}
if (params_.type == InsetIndexMacroParams::See)
os << "see{";
else if (params_.type == InsetIndexMacroParams::Seealso)
os << "seealso{";
InsetCollapsible::latex(os, runparams);
if (params_.type == InsetIndexMacroParams::See
|| params_.type == InsetIndexMacroParams::Seealso)
os << "}";
}
int InsetIndexMacro::getPlaintext(odocstringstream & os,
OutputParams const & runparams, size_t max_length) const
{
return InsetText::plaintext(os, runparams, max_length);
}
void InsetIndexMacro::getDocbook(XMLStream & xs, OutputParams const & runparams) const
{
InsetText::docbook(xs, runparams);
}
docstring InsetIndexMacro::getXhtml(XMLStream & xs, OutputParams const & runparams) const
{
return InsetText::xhtml(xs, runparams);
}
void InsetIndexMacro::doDispatch(Cursor & cur, FuncRequest & cmd)
{
switch (cmd.action()) {
case LFUN_INSET_MODIFY: {
if (cmd.getArg(0) == "changetype") {
cur.recordUndoInset(this);
params_.type = insetindexmacrotranslator().find(cmd.getArg(1));
break;
}
InsetCollapsible::doDispatch(cur, cmd);
break;
}
default:
InsetCollapsible::doDispatch(cur, cmd);
break;
}
}
bool InsetIndexMacro::getStatus(Cursor & cur, FuncRequest const & cmd,
FuncStatus & flag) const
{
switch (cmd.action()) {
case LFUN_INSET_MODIFY:
if (cmd.getArg(0) == "changetype") {
docstring const newtype = from_utf8(cmd.getArg(1));
bool const enabled = (params_.type == InsetIndexMacroParams::See
|| params_.type == InsetIndexMacroParams::Seealso)
&& (newtype == "see" || newtype == "seealso");
flag.setEnabled(enabled);
flag.setOnOff(
newtype == from_ascii(insetindexmacrotranslator().find(params_.type)));
return true;
}
return InsetCollapsible::getStatus(cur, cmd, flag);
default:
return InsetCollapsible::getStatus(cur, cmd, flag);
}
}
void InsetIndexMacro::processLatexSorting(otexstream & os, OutputParams const & runparams,
docstring const latex, docstring const plain) const
{
if (contains(latex, '\\') && !contains(latex, '@')) {
// Plaintext might return nothing (e.g. for ERTs).
// In that case, we use LaTeX.
docstring const spart = (plain.empty()) ? latex : plain;
// Now we need to validate that all characters in
// the sorting part are representable in the current
// encoding. If not try the LaTeX macro which might
// or might not be a good choice, and issue a warning.
pair<docstring, docstring> spart_latexed =
runparams.encoding->latexString(spart, runparams.dryrun);
if (!spart_latexed.second.empty())
LYXERR0("Uncodable character in index entry. Sorting might be wrong!");
if (spart != spart_latexed.first && !runparams.dryrun) {
TeXErrors terr;
ErrorList & errorList = buffer().errorList("Export");
docstring const s = bformat(_("LyX's automatic index sorting algorithm faced "
"problems with the sub-entry '%1$s'.\n"
"Please specify the sorting of this entry manually, as "
"explained in the User Guide."), spart);
Paragraph const & par = buffer().paragraphs().front();
errorList.push_back(ErrorItem(_("Index sorting failed"), s,
{par.id(), 0}, {par.id(), -1}));
buffer().bufferErrors(terr, errorList);
}
// Remove remaining \'s from the sort key
docstring ppart = subst(spart_latexed.first, from_ascii("\\"), docstring());
// Plain quotes need to be escaped, however (#10649), as this
// is the default escape character
ppart = subst(ppart, from_ascii("\""), from_ascii("\\\""));
// Now insert the sortkey, separated by '@'.
os << ppart;
os << '@';
}
// Insert the actual level text
os << latex;
}
docstring InsetIndexMacro::toolTip(BufferView const &, int, int) const
{
return insetindexmacrotranslator_loc().find(params_.type);
}
string InsetIndexMacro::params2string(InsetIndexMacroParams const & params)
{
ostringstream data;
data << "IndexMacro" << ' ';
params.write(data);
return data.str();
}
void InsetIndexMacro::string2params(string const & in, InsetIndexMacroParams & params)
{
params = InsetIndexMacroParams();
if (in.empty())
return;
istringstream data(in);
Lexer lex;
lex.setStream(data);
lex.setContext("InsetIndexMacro::string2params");
lex >> "IndexMacro" >> "see";
params.read(lex);
}
bool InsetIndexMacro::hasSortKey() const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE)
return true;
}
return false;
}
void InsetIndexMacro::getSortkey(otexstream & os, OutputParams const & runparams) const
{
Paragraph const & par = paragraphs().front();
InsetList::const_iterator it = par.insetList().begin();
for (; it != par.insetList().end(); ++it) {
Inset & inset = *it->inset;
if (inset.lyxCode() == INDEXMACRO_SORTKEY_CODE) {
InsetIndexMacro const & iim =
static_cast<InsetIndexMacro const &>(inset);
iim.getLatex(os, runparams);
return;
}
}
}
string InsetIndexMacro::contextMenuName() const
{
return "context-indexmacro";
}
string InsetIndexMacro::contextMenu(BufferView const & bv, int x, int y) const
{
// We override the implementation of InsetCollapsible,
// because we have eytra entries.
string owncm = "context-edit-index;";
return owncm + InsetCollapsible::contextMenu(bv, x, y);
}
bool InsetIndexMacro::insetAllowed(InsetCode code) const
{
switch (code) {
case INDEX_CODE:
return false;
case INDEXMACRO_SORTKEY_CODE:
return (params_.type == InsetIndexMacroParams::Subindex
&& !hasSortKey());
default:
return InsetCollapsible::insetAllowed(code);
}
}
} // namespace lyx

View File

@ -0,0 +1,125 @@
// -*- C++ -*-
/**
* \file InsetIndexMacro.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Jürgen Spitzmüller
*
* Full author contact details are available in file CREDITS.
*/
#ifndef INSET_INSETMACRO_H
#define INSET_INSETMACRO_H
#include "Inset.h"
#include "InsetCollapsible.h"
namespace lyx {
class LaTeXFeatures;
class InsetIndexMacroParams
{
public:
enum Type {
See,
Seealso,
Subindex,
Sortkey
};
///
InsetIndexMacroParams();
///
void write(std::ostream & os) const;
///
void read(Lexer & lex);
///
Type type;
};
/////////////////////////////////////////////////////////////////////////
//
// InsetIndexMacro
//
/////////////////////////////////////////////////////////////////////////
/// Used to insert index references
class InsetIndexMacro : public InsetCollapsible
{
public:
///
InsetIndexMacro(Buffer *, std::string const &);
///
~InsetIndexMacro();
///
static std::string params2string(InsetIndexMacroParams const &);
///
static void string2params(std::string const &, InsetIndexMacroParams &);
///
InsetIndexMacroParams const & params() const { return params_; }
///
void getLatex(otexstream &, OutputParams const &) const;
///
int getPlaintext(odocstringstream &, OutputParams const &, size_t) const;
///
void getDocbook(XMLStream &, OutputParams const &) const;
private:
///
InsetCode lyxCode() const override;
///
docstring layoutName() const override;
///
void write(std::ostream &) const override;
///
void read(Lexer & lex) override;
///
bool neverIndent() const override { return true; }
/// We do not output anything directly to the stream
void latex(otexstream &, OutputParams const &) const override {};
/// We do not output anything directly to the stream
int plaintext(odocstringstream &, OutputParams const &, size_t) const override { return 0; };
/// We do not output anything directly to the stream
void docbook(XMLStream &, OutputParams const &) const override {};
/// We do not output anything directly to the stream
docstring xhtml(XMLStream &, OutputParams const &) const override { return docstring(); };
///
docstring getXhtml(XMLStream &, OutputParams const &) const;
///
bool allowSpellCheck() const override { return false; }
///
bool insetAllowed(InsetCode code) const override;
///
bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const override;
///
void doDispatch(Cursor & cur, FuncRequest & cmd) override;
///
docstring toolTip(BufferView const & bv, int x, int y) const override;
///
void processLatexSorting(otexstream &, OutputParams const &,
docstring const, docstring const) const;
///
bool hasSortKey() const;
///
void getSortkey(otexstream &, OutputParams const &) const;
///
std::string contextMenuName() const override;
///
std::string contextMenu(BufferView const &, int, int) const override;
///
Inset * clone() const override { return new InsetIndexMacro(*this); }
/// used by the constructors
void init();
///
friend class InsetIndexMacroParams;
///
InsetIndexMacroParams params_;
};
} // namespace lyx
#endif

View File

@ -1040,6 +1040,10 @@ bool InsetText::insetAllowed(InsetCode code) const
case QUOTE_CODE:
case COUNTER_CODE:
return true;
// These are only allowed in index insets
case INDEXMACRO_CODE:
case INDEXMACRO_SORTKEY_CODE:
return false;
default:
return !isPassThru();
}

View File

@ -486,7 +486,40 @@ bool Parser::hasOpt(string const & l)
}
Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
bool Parser::hasIdxMacros(string const & c, string const & e)
{
// Check for index entry separator (! or @),
// consider escaping via "
// \p e marks a terminating delimiter¸
// remember current position
unsigned int oldpos = pos_;
// skip spaces and comments
bool retval = false;
while (good()) {
get_token();
if (isParagraph()) {
putback();
break;
}
if (curr_token().cat() == catEnd)
break;
if (!e.empty() && curr_token().asInput() == e
&& prev_token().asInput() != "\"")
break;
if (curr_token().asInput() == c
&& prev_token().asInput() != "\"") {
retval = true;
break;
}
continue;
}
pos_ = oldpos;
return retval;
}
Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping, char e)
{
skip_spaces(true);
@ -495,25 +528,28 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
if (! good())
return make_pair(false, string());
int group_level = 0;
int group_level = (left == '{') ? 1 : 0;
string result;
Token t = get_token();
if (t.cat() == catComment || t.cat() == catEscape ||
t.character() != left) {
if (left != char()
&& (t.cat() == catComment || t.cat() == catEscape
|| t.character() != left)) {
putback();
return make_pair(false, string());
} else {
while (good()) {
t = get_token();
// honor grouping
if (left != '{' && t.cat() == catBegin) {
if (t.cat() == catBegin) {
++group_level;
continue;
if (left != '{')
continue;
}
if (left != '{' && t.cat() == catEnd) {
if (group_level > 0 && t.cat() == catEnd) {
--group_level;
continue;
if (left != '{')
continue;
}
// Ignore comments
if (t.cat() == catComment) {
@ -525,6 +561,10 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
if (t.cat() != catEscape && t.character() == right
&& group_level == 0)
break;
} else if (e != char()) {
if (prev_token().character() != e && t.character() == right
&& group_level == 0)
break;
} else {
if (t.character() == right) {
if (t.cat() == catEscape)
@ -540,9 +580,9 @@ Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
}
string Parser::getArg(char left, char right, bool allow_escaping)
string Parser::getArg(char left, char right, bool allow_escaping, char e)
{
return getFullArg(left, right, allow_escaping).second;
return getFullArg(left, right, allow_escaping, e).second;
}

View File

@ -215,17 +215,22 @@ public:
/// Does an optional argument follow after the current token?
bool hasOpt(std::string const & l = "[");
/// Does this index entry has levels?
bool hasIdxMacros(std::string const & c,
std::string const & e = std::string());
///
typedef std::pair<bool, std::string> Arg;
/*!
* Get an argument enclosed by \p left and \p right.
* If \p allow_escaping is true, a right delimiter escaped by a
* backslash does not count as delimiter, but is included in the
* argument.
* argument. The \p e allows for a different escape character
* (used in index insets)
* \returns whether an argument was found in \p Arg.first and the
* argument in \p Arg.second. \see getArg().
*/
Arg getFullArg(char left, char right, bool allow_escaping = true);
Arg getFullArg(char left, char right, bool allow_escaping = true,
char e = char());
/*!
* Get an argument enclosed by \p left and \p right.
* If \p allow_escaping is true, a right delimiter escaped by a
@ -236,7 +241,8 @@ public:
* getFullArg() if you need to know whether there was an empty
* argument or no argument at all.
*/
std::string getArg(char left, char right, bool allow_escaping = true);
std::string getArg(char left, char right, bool allow_escaping = true,
char e = char());
/*!
* Like getOpt(), but distinguishes between a missing argument ""
* and an empty argument "[]".

View File

@ -48,7 +48,8 @@ extern std::string rgbcolor2code(std::string const & name);
std::string translate_len(std::string const &);
void parse_text(Parser & p, std::ostream & os, unsigned flags, bool outer,
Context & context, std::string const & rdelim = "");
Context & context, std::string const & rdelim = "",
std::string const & rdelimesc = "");
void check_comment_bib(std::ostream & os, Context & context);
void fix_child_filename(std::string & name);
@ -68,7 +69,8 @@ std::string find_file(std::string const & name, std::string const & path,
void parse_text_in_inset(Parser & p, std::ostream & os, unsigned flags,
bool outer, Context & context,
InsetLayout const * layout = nullptr,
std::string const & rdelim = "");
std::string const & rdelim = "",
std::string const & rdelimesc = "");
/// Guess document language from \p p if CJK is used.
/// \p lang is used for all non-CJK contents.

View File

@ -55,7 +55,7 @@ void output_arguments(ostream &, Parser &, bool, bool, const string &, Context &
void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
Context & context, InsetLayout const * layout,
string const & rdelim)
string const & rdelim, string const & rdelimesc)
{
bool const forcePlainLayout =
layout ? layout->forcePlainLayout() : false;
@ -78,7 +78,7 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
parse_text(p, oss, FLAG_RDELIM, outer, dummy,
string(1, context.latexparam.back()));
}
parse_text(p, os, flags, outer, newcontext, rdelim);
parse_text(p, os, flags, outer, newcontext, rdelim, rdelimesc);
if (layout)
output_arguments(os, p, outer, false, "post", newcontext,
layout->postcommandargs());
@ -91,7 +91,8 @@ namespace {
void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
Context const & context, string const & name,
string const & rdelim = string())
string const & rdelim = string(),
string const & rdelimesc = string())
{
InsetLayout const * layout = 0;
DocumentClass::InsetLayouts::const_iterator it =
@ -99,17 +100,18 @@ void parse_text_in_inset(Parser & p, ostream & os, unsigned flags, bool outer,
if (it != context.textclass.insetLayouts().end())
layout = &(it->second);
Context newcontext = context;
parse_text_in_inset(p, os, flags, outer, newcontext, layout, rdelim);
parse_text_in_inset(p, os, flags, outer, newcontext, layout, rdelim, rdelimesc);
}
/// parses a paragraph snippet, useful for example for \\emph{...}
void parse_text_snippet(Parser & p, ostream & os, unsigned flags, bool outer,
Context & context)
Context & context, string const & rdelim = string(),
string const & rdelimesc = string())
{
Context newcontext(context);
// Don't inherit the paragraph-level extra stuff
newcontext.par_extra_stuff.clear();
parse_text(p, os, flags, outer, newcontext);
parse_text(p, os, flags, outer, newcontext, rdelim, rdelimesc);
// Make sure that we don't create invalid .lyx files
context.need_layout = newcontext.need_layout;
context.need_end_layout = newcontext.need_end_layout;
@ -1506,6 +1508,218 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
}
void parse_index_entry(Parser & p, ostream & os, Context & context, string const & kind)
{
// write inset header
begin_inset(os, "Index ");
os << kind;
// Parse for post argument (|...)
p.pushPosition();
string const marg = p.getArg('{', '}');
p.popPosition();
char lc = char();
bool inpost = false;
bool startrange = false;
bool endrange = false;
string post;
for (string::const_iterator it = marg.begin(), et = marg.end(); it != et; ++it) {
char c = *it;
if (inpost) {
if (post.empty() && c == '(')
startrange = true;
else if (post.empty() && c == ')')
endrange = true;
else
post += c;
}
if (!inpost && (c == '|' && lc != '"'))
inpost = true;
lc = c;
}
if (startrange)
os << "\nrange start";
else if (endrange)
os << "\nrange end";
else
os << "\nrange none";
bool const see = prefixIs(post, "see{");
bool const seealso = prefixIs(post, "seealso{");
if (!post.empty() && !see && !seealso)
os << "\npageformat " << post;
else
os << "\npageformat default";
os << "\nstatus collapsed\n";
bool main = true;
// save position
p.pushPosition();
// Check for levels
if (p.hasIdxMacros("!")) {
// Index entry with levels
while (p.hasIdxMacros("!")) {
if (main) {
// swallow brace
p.get_token();
os << "\\begin_layout Plain Layout\n";
} else {
begin_inset(os, "IndexMacro subindex");
os << "\nstatus collapsed\n";
}
// Check for (level-specific) sortkey
if (p.hasIdxMacros("@", "!")) {
if (!main)
os << "\\begin_layout Plain Layout\n";
begin_inset(os, "IndexMacro sortkey");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
end_inset(os);
}
parse_text_snippet(p, os, FLAG_RDELIM, false, context, "!", "\"");
if (!main) {
os << "\n\\end_layout\n";
end_inset(os);
}
main = false;
}
if (!main) {
begin_inset(os, "IndexMacro subindex");
os << "\nstatus collapsed\n";
}
// Final level
// Check for (level-specific) sortkey
if (p.hasIdxMacros("@", "!")) {
if (main) {
// swallow brace
p.get_token();
}
os << "\\begin_layout Plain Layout\n";
begin_inset(os, "IndexMacro sortkey");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
end_inset(os);
if (post.empty() && !startrange && !endrange) {
parse_text_snippet(p, os, FLAG_BRACE_LAST, false, context);
p.dropPosition();
} else {
// Handle post-argument
parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
if (see || seealso) {
while (p.next_token().character() != '{' && p.good())
p.get_token();
// this ends the subinset, as the see[also] insets
// must come at main index inset
os << "\n\\end_layout\n";
end_inset(os);
if (see)
begin_inset(os, "IndexMacro see");
else
begin_inset(os, "IndexMacro seealso");
os << "\nstatus collapsed\n";
os << "\\begin_layout Plain Layout\n";
parse_text_snippet(p, os, FLAG_ITEM, false, context);
}
p.popPosition();
// swallow argument
p.getArg('{', '}');
}
os << "\n\\end_layout\n";
} else {
if (post.empty() && !startrange && !endrange) {
parse_text_in_inset(p, os, FLAG_BRACE_LAST, false, context, "IndexMacro subindex");
p.dropPosition();
} else {
// Handle post-argument
if (see || seealso) {
os << "\\begin_layout Plain Layout\n";
parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
while (p.next_token().character() != '{' && p.good())
p.get_token();
// this ends the subinset, as the see[also] insets
// must come at main index inset
os << "\n\\end_layout\n";
end_inset(os);
if (see)
begin_inset(os, "IndexMacro see");
else
begin_inset(os, "IndexMacro seealso");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
} else
parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "Index", "|", "\"");
p.popPosition();
// swallow argument
p.getArg('{', '}');
}
}
if (!main)
end_inset(os);
os << "\n\\end_layout\n";
} else {
// Index without any levels
// Check for sortkey
if (p.hasIdxMacros("@", "!")) {
// swallow brace
p.get_token();
os << "\\begin_layout Plain Layout\n";
begin_inset(os, "IndexMacro sortkey");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "IndexMacro sortkey", "@", "\"");
end_inset(os);
if (post.empty() && !startrange && !endrange) {
parse_text_snippet(p, os, FLAG_BRACE_LAST, false, context);
p.dropPosition();
} else {
parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
if (see || seealso) {
while (p.next_token().character() != '{' && p.good())
p.get_token();
if (see)
begin_inset(os, "IndexMacro see");
else
begin_inset(os, "IndexMacro seealso");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
end_inset(os);
}
p.popPosition();
// swallow argument
p.getArg('{', '}');
}
os << "\n\\end_layout\n";
} else {
if (post.empty() && !startrange && !endrange) {
parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
p.dropPosition();
} else {
// Handle post-argument
// swallow brace
p.get_token();
if (see || seealso) {
os << "\\begin_layout Plain Layout\n";
parse_text_snippet(p, os, FLAG_RDELIM, false, context, "|", "\"");
while (p.next_token().character() != '{' && p.good())
p.get_token();
if (see)
begin_inset(os, "IndexMacro see");
else
begin_inset(os, "IndexMacro seealso");
os << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_ITEM, false, context, "IndexMacro see");
end_inset(os);
os << "\n\\end_layout\n";
} else
parse_text_in_inset(p, os, FLAG_RDELIM, false, context, "Index", "|", "\"");
p.popPosition();
// swallow argument
p.getArg('{', '}');
}
}
}
end_inset(os);
}
void parse_listings(Parser & p, ostream & os, Context & parent_context,
bool in_line, bool use_minted)
{
@ -2901,7 +3115,7 @@ void fix_child_filename(string & name)
void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
Context & context, string const & rdelim)
Context & context, string const & rdelim, string const & rdelimesc)
{
Layout const * newlayout = 0;
InsetLayout const * newinsetlayout = 0;
@ -2990,7 +3204,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
if (rdelim.size() > 1)
tok += p.next_token().asInput();
if (t.cat() != catEscape && !rdelim.empty()
&& tok == rdelim && (flags & FLAG_RDELIM)) {
&& tok == rdelim && (flags & FLAG_RDELIM)
&& (rdelimesc.empty() || p.prev_token().asInput() != rdelimesc)) {
if (rdelim.size() > 1)
p.get_token(); // eat rdelim
return;
@ -4712,10 +4927,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
string const arg = (t.cs() == "sindex" && p.hasOpt()) ?
p.getArg('[', ']') : "";
string const kind = arg.empty() ? "idx" : arg;
begin_inset(os, "Index ");
os << kind << "\nstatus collapsed\n";
parse_text_in_inset(p, os, FLAG_ITEM, false, context, "Index");
end_inset(os);
parse_index_entry(p, os, context, kind);
if (kind != "idx")
preamble.registerAutomaticallyLoadedPackage("splitidx");
continue;