CharInfo: allow to store several commands (both text and math) for each character.

Only the first one is returned by default with existing methods to guarantee compatibility with existing code.
This commit is contained in:
Thibaut Cuvelier 2022-02-18 22:11:04 +01:00
parent faf0e9ee13
commit 3f9e21b826
4 changed files with 121 additions and 92 deletions

View File

@ -52,7 +52,7 @@ void BufferEncodings::validate(char_type c, LaTeXFeatures & features, bool for_m
CharInfo const & ci = Encodings::unicodeCharInfo(c); CharInfo const & ci = Encodings::unicodeCharInfo(c);
if (ci.isUnicodeSymbol()) { if (ci.isUnicodeSymbol()) {
// In mathed, c could be used both in textmode and mathmode // In mathed, c could be used both in textmode and mathmode
docstring const textcommand = ci.textcommand(); docstring const textcommand = ci.textCommand();
bool const math_mode = for_mathed && isMathCmd(c); bool const math_mode = for_mathed && isMathCmd(c);
bool const use_math = math_mode || bool const use_math = math_mode ||
(!for_mathed && textcommand.empty()); (!for_mathed && textcommand.empty());
@ -65,9 +65,9 @@ void BufferEncodings::validate(char_type c, LaTeXFeatures & features, bool for_m
// and if we do not use unicode-math // and if we do not use unicode-math
if ((math_mode && !unicode_math) if ((math_mode && !unicode_math)
|| (use_math && !plain_utf8)) { || (use_math && !plain_utf8)) {
string const mathpreamble = ci.mathpreamble(); string const mathpreamble = ci.mathPreamble();
if (!mathpreamble.empty()) { if (!mathpreamble.empty()) {
if (ci.mathfeature()) { if (ci.mathFeature()) {
string feats = mathpreamble; string feats = mathpreamble;
while (!feats.empty()) { while (!feats.empty()) {
string feat; string feat;
@ -80,9 +80,9 @@ void BufferEncodings::validate(char_type c, LaTeXFeatures & features, bool for_m
} }
// with utf8-plain, we do not load packages (see #7766) // with utf8-plain, we do not load packages (see #7766)
if (use_text && !plain_utf8) { if (use_text && !plain_utf8) {
string const textpreamble = ci.textpreamble(); string const textpreamble = ci.textPreamble();
if (!textpreamble.empty()) { if (!textpreamble.empty()) {
if (ci.textfeature()) { if (ci.textFeature()) {
string feats = textpreamble; string feats = textpreamble;
while (!feats.empty()) { while (!feats.empty()) {
string feat; string feat;

View File

@ -75,12 +75,23 @@ const char * EncodingException::what() const noexcept
CharInfo::CharInfo( CharInfo::CharInfo(
docstring const & textcommand, docstring const & mathcommand, docstring const & text_command, docstring const & math_command,
std::string const & textpreamble, std::string const & mathpreamble, std::string const & text_preamble, std::string const & math_preamble,
std::string const & tipashortcut, unsigned int flags) std::string const & tipa_shortcut, unsigned int flags)
: textcommand_(textcommand), mathcommand_(mathcommand), : text_commands_({text_command}), math_commands_({math_command}),
textpreamble_(textpreamble), mathpreamble_(mathpreamble), text_preamble_(text_preamble), math_preamble_(math_preamble),
tipashortcut_(tipashortcut), flags_(flags) tipa_shortcut_(tipa_shortcut), flags_(flags)
{
}
CharInfo::CharInfo(
std::vector<docstring> const & text_commands, std::vector<docstring> const & math_commands,
std::string const & text_preamble, std::string const & math_preamble,
std::string const & tipa_shortcut, unsigned int flags)
: text_commands_(text_commands), math_commands_(math_commands),
text_preamble_(text_preamble), math_preamble_(math_preamble),
tipa_shortcut_(tipa_shortcut), flags_(flags)
{ {
} }
@ -216,11 +227,11 @@ pair<docstring, bool> Encoding::latexChar(char_type c) const
CharInfoMap::const_iterator const it = unicodesymbols.find(c); CharInfoMap::const_iterator const it = unicodesymbols.find(c);
if (it == unicodesymbols.end()) if (it == unicodesymbols.end())
throw EncodingException(c); throw EncodingException(c);
// at least one of mathcommand and textcommand is nonempty // at least one of mathCommand and textCommand is nonempty
if (it->second.textcommand().empty()) if (it->second.textCommand().empty())
return make_pair( return make_pair(
"\\ensuremath{" + it->second.mathcommand() + '}', false); "\\ensuremath{" + it->second.mathCommand() + '}', false);
return make_pair(it->second.textcommand(), !it->second.textnotermination()); return make_pair(it->second.textCommand(), !it->second.textNoTermination());
} }
@ -302,17 +313,17 @@ bool Encodings::latexMathChar(char_type c, bool mathmode,
addMathSym(c); addMathSym(c);
return false; return false;
} }
// at least one of mathcommand and textcommand is nonempty // at least one of mathCommand and textCommand is nonempty
bool use_math = (mathmode && !it->second.mathcommand().empty()) || bool use_math = (mathmode && !it->second.mathCommand().empty()) ||
(!mathmode && it->second.textcommand().empty()); (!mathmode && it->second.textCommand().empty());
if (use_math) { if (use_math) {
command = it->second.mathcommand(); command = it->second.mathCommand();
needsTermination = !it->second.mathnotermination(); needsTermination = !it->second.mathNoTermination();
addMathCmd(c); addMathCmd(c);
} else { } else {
if (!encoding || command.empty()) { if (!encoding || command.empty()) {
command = it->second.textcommand(); command = it->second.textCommand();
needsTermination = !it->second.textnotermination(); needsTermination = !it->second.textNoTermination();
} }
if (mathmode) if (mathmode)
addMathSym(c); addMathSym(c);
@ -331,23 +342,31 @@ char_type Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
for (combining = false; it != end; ++it) { for (combining = false; it != end; ++it) {
if (it->second.deprecated()) if (it->second.deprecated())
continue; continue;
docstring const math = it->second.mathcommand();
docstring const text = it->second.textcommand(); if (cmdtype & MATH_CMD) {
if ((cmdtype & MATH_CMD) && math == cmd) { for (const docstring& math : it->second.mathCommands()) {
combining = it->second.combining(); if ((cmdtype & MATH_CMD) && math == cmd) {
needsTermination = !it->second.mathnotermination(); combining = it->second.combining();
if (req && it->second.mathfeature() && needsTermination = !it->second.mathNoTermination();
!it->second.mathpreamble().empty()) if (req && it->second.mathFeature() &&
req->insert(it->second.mathpreamble()); !it->second.mathPreamble().empty())
return it->first; req->insert(it->second.mathPreamble());
return it->first;
}
}
} }
if ((cmdtype & TEXT_CMD) && text == cmd) {
combining = it->second.combining(); if (cmdtype & TEXT_CMD) {
needsTermination = !it->second.textnotermination(); for (const docstring& text : it->second.textCommands()) {
if (req && it->second.textfeature() && if (text == cmd) {
!it->second.textpreamble().empty()) combining = it->second.combining();
req->insert(it->second.textpreamble()); needsTermination = !it->second.textNoTermination();
return it->first; if (req && it->second.textFeature() &&
!it->second.textPreamble().empty())
req->insert(it->second.textPreamble());
return it->first;
}
}
} }
} }
needsTermination = false; needsTermination = false;
@ -418,9 +437,9 @@ docstring Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
for (; it != uniend; ++it) { for (; it != uniend; ++it) {
if (it->second.deprecated()) if (it->second.deprecated())
continue; continue;
docstring const math = mathmode ? it->second.mathcommand() docstring const math = mathmode ? it->second.mathCommand()
: docstring(); : docstring();
docstring const text = textmode ? it->second.textcommand() docstring const text = textmode ? it->second.textCommand()
: docstring(); : docstring();
if (!combcmd.empty() && it->second.combining() && if (!combcmd.empty() && it->second.combining() &&
(math == combcmd || text == combcmd)) (math == combcmd || text == combcmd))
@ -496,16 +515,16 @@ docstring Encodings::fromLaTeXCommand(docstring const & cmd, int cmdtype,
i = j + 1; i = j + 1;
unicmd_size = cur_size; unicmd_size = cur_size;
if (math == tmp) if (math == tmp)
needsTermination = !it->second.mathnotermination(); needsTermination = !it->second.mathNoTermination();
else else
needsTermination = !it->second.textnotermination(); needsTermination = !it->second.textNoTermination();
if (req) { if (req) {
if (math == tmp && it->second.mathfeature() && if (math == tmp && it->second.mathFeature() &&
!it->second.mathpreamble().empty()) !it->second.mathPreamble().empty())
req->insert(it->second.mathpreamble()); req->insert(it->second.mathPreamble());
if (text == tmp && it->second.textfeature() && if (text == tmp && it->second.textFeature() &&
!it->second.textpreamble().empty()) !it->second.textPreamble().empty())
req->insert(it->second.textpreamble()); req->insert(it->second.textPreamble());
} }
} }
} }
@ -584,7 +603,7 @@ string const Encodings::TIPAShortcut(char_type c)
{ {
CharInfoMap::const_iterator const it = unicodesymbols.find(c); CharInfoMap::const_iterator const it = unicodesymbols.find(c);
if (it != unicodesymbols.end()) if (it != unicodesymbols.end())
return it->second.tipashortcut(); return it->second.tipaShortcut();
return string(); return string();
} }
@ -595,11 +614,11 @@ string const Encodings::isKnownScriptChar(char_type const c)
if (it == unicodesymbols.end()) if (it == unicodesymbols.end())
return string(); return string();
// FIXME: parse complex textpreamble (may be list or alternatives, // FIXME: parse complex textPreamble (may be list or alternatives,
// e.g., "subscript,textgreek" or "textcomp|textgreek") // e.g., "subscript,textgreek" or "textcomp|textgreek")
if (it->second.textpreamble() == "textgreek" if (it->second.textPreamble() == "textgreek"
|| it->second.textpreamble() == "textcyrillic") || it->second.textPreamble() == "textcyrillic")
return it->second.textpreamble(); return it->second.textPreamble();
return string(); return string();
} }
@ -627,7 +646,7 @@ bool Encodings::isUnicodeTextOnly(char_type c)
return false; return false;
CharInfoMap::const_iterator const it = unicodesymbols.find(c); CharInfoMap::const_iterator const it = unicodesymbols.find(c);
return it == unicodesymbols.end() || it->second.mathcommand().empty(); return it == unicodesymbols.end() || it->second.mathCommand().empty();
} }
@ -754,7 +773,7 @@ void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
} else if (flag == "notermination=none") { } else if (flag == "notermination=none") {
flags &= ~CharInfoTextNoTermination; flags &= ~CharInfoTextNoTermination;
flags &= ~CharInfoMathNoTermination; flags &= ~CharInfoMathNoTermination;
} else if (contains(flag, "tipashortcut=")) { } else if (contains(flag, "tipaShortcut=")) {
tipashortcut = split(flag, '='); tipashortcut = split(flag, '=');
} else if (flag == "deprecated") { } else if (flag == "deprecated") {
flags |= CharInfoDeprecated; flags |= CharInfoDeprecated;
@ -765,7 +784,7 @@ void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
<< "'." << endl; << "'." << endl;
} }
} }
// mathcommand and mathpreamble have been added for 1.6.0. // mathCommand and mathPreamble have been added for 1.6.0.
// make them optional so that old files still work. // make them optional so that old files still work.
int const lineno = symbolslex.lineNumber(); int const lineno = symbolslex.lineNumber();
bool breakout = false; bool breakout = false;
@ -773,7 +792,7 @@ void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
string mathpreamble; string mathpreamble;
if (symbolslex.next(true)) { if (symbolslex.next(true)) {
if (symbolslex.lineNumber() != lineno) { if (symbolslex.lineNumber() != lineno) {
// line in old format without mathcommand and mathpreamble // line in old format without mathCommand and mathPreamble
getNextToken = false; getNextToken = false;
} else { } else {
mathcommand = symbolslex.getDocString(); mathcommand = symbolslex.getDocString();
@ -781,10 +800,10 @@ void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
flags |= CharInfoMathNoTermination; flags |= CharInfoMathNoTermination;
if (symbolslex.next(true)) { if (symbolslex.next(true)) {
if (symbolslex.lineNumber() != lineno) { if (symbolslex.lineNumber() != lineno) {
// line in new format with mathcommand only // line in new format with mathCommand only
getNextToken = false; getNextToken = false;
} else { } else {
// line in new format with mathcommand and mathpreamble // line in new format with mathCommand and mathPreamble
mathpreamble = symbolslex.getString(); mathpreamble = symbolslex.getString();
} }
} else } else
@ -810,12 +829,12 @@ void Encodings::read(FileName const & encfile, FileName const & symbolsfile)
textpreamble, mathpreamble, textpreamble, mathpreamble,
tipashortcut, flags); tipashortcut, flags);
LYXERR(Debug::INFO, "Read unicode symbol " << symbol << " '" LYXERR(Debug::INFO, "Read unicode symbol " << symbol << " '"
<< to_utf8(info.textcommand()) << "' '" << info.textpreamble() << to_utf8(info.textCommand()) << "' '" << info.textPreamble()
<< " '" << info.textfeature() << ' ' << info.textnotermination() << " '" << info.textFeature() << ' ' << info.textNoTermination()
<< ' ' << to_utf8(info.mathcommand()) << "' '" << info.mathpreamble() << ' ' << to_utf8(info.mathCommand()) << "' '" << info.mathPreamble()
<< "' " << info.mathfeature() << ' ' << info.mathnotermination() << "' " << info.mathFeature() << ' ' << info.mathNoTermination()
<< ' ' << info.combining() << ' ' << info.force() << ' ' << info.combining() << ' ' << info.force()
<< ' ' << info.forceselected()); << ' ' << info.forceSelected());
// we assume that at least one command is nonempty when using unicodesymbols // we assume that at least one command is nonempty when using unicodesymbols
if (info.isUnicodeSymbol()) { if (info.isUnicodeSymbol()) {

View File

@ -62,52 +62,62 @@ class CharInfo {
public: public:
CharInfo() : flags_(0) {} CharInfo() : flags_(0) {}
CharInfo( CharInfo(
docstring const & textcommand, docstring const & mathcommand, docstring const & text_command, docstring const & math_command,
std::string const & textpreamble, std::string const & mathpreamble, std::string const & text_preamble, std::string const & math_preamble,
std::string const & tipashortcut, unsigned int flags); std::string const & tipa_shortcut, unsigned int flags);
CharInfo(
std::vector<docstring> const & text_commands, std::vector<docstring> const & math_commands,
std::string const & text_preamble, std::string const & math_preamble,
std::string const & tipa_shortcut, unsigned int flags);
// we assume that at least one command is nonempty when using unicodesymbols // we assume that at least one command is nonempty when using unicodesymbols
bool isUnicodeSymbol() const { return !textcommand_.empty() || !mathcommand_.empty(); } bool isUnicodeSymbol() const { return !text_commands_.empty() || !math_commands_.empty(); }
/// LaTeX command (text mode) for this character /// LaTeX command (text mode) for this character
docstring const textcommand() const { return textcommand_; } docstring textCommand() const { return text_commands_[0]; }
/// All known LaTeX commands (text mode) for this character
std::vector<docstring> textCommands() const { return text_commands_; }
/// LaTeX command (math mode) for this character /// LaTeX command (math mode) for this character
docstring mathcommand() const { return mathcommand_; } docstring mathCommand() const { return math_commands_[0]; }
/// All known LaTeX commands (math mode) for this character
std::vector<docstring> mathCommands() const { return math_commands_; }
/// Needed LaTeX preamble (or feature) for text mode /// Needed LaTeX preamble (or feature) for text mode
std::string textpreamble() const { return textpreamble_; } std::string textPreamble() const { return text_preamble_; }
/// Needed LaTeX preamble (or feature) for math mode /// Needed LaTeX preamble (or feature) for math mode
std::string mathpreamble() const { return mathpreamble_; } std::string mathPreamble() const { return math_preamble_; }
/// Is this a combining character? /// Is this a combining character?
bool combining() const { return flags_ & CharInfoCombining; } bool combining() const { return flags_ & CharInfoCombining; }
/// Is \c textpreamble a feature known by LaTeXFeatures, or a raw LaTeX /// Is \c textPreamble a feature known by LaTeXFeatures, or a raw LaTeX
/// command? /// command?
bool textfeature() const { return flags_ & CharInfoTextFeature; } bool textFeature() const { return flags_ & CharInfoTextFeature; }
/// Is \c mathpreamble a feature known by LaTeXFeatures, or a raw LaTeX /// Is \c mathPreamble a feature known by LaTeXFeatures, or a raw LaTeX
/// command? /// command?
bool mathfeature() const { return flags_ & CharInfoMathFeature; } bool mathFeature() const { return flags_ & CharInfoMathFeature; }
/// Always force the LaTeX command, even if the encoding contains /// Always force the LaTeX command, even if the encoding contains
/// this character? /// this character?
bool force() const { return flags_ & CharInfoForce; } bool force() const { return flags_ & CharInfoForce; }
/// Force the LaTeX command for some encodings? /// Force the LaTeX command for some encodings?
bool forceselected() const { return flags_ & CharInfoForceSelected; } bool forceSelected() const { return flags_ & CharInfoForceSelected; }
/// Disable LaTeX command => char_type conversion for this deprecated symbol? /// Disable LaTeX command => char_type conversion for this deprecated symbol?
bool deprecated() const { return flags_ & CharInfoDeprecated; } bool deprecated() const { return flags_ & CharInfoDeprecated; }
/// TIPA shortcut /// TIPA shortcut
std::string const tipashortcut() const { return tipashortcut_; } std::string const tipaShortcut() const { return tipa_shortcut_; }
/// \c textcommand needs no termination (such as {} or space). /// \c textCommand needs no termination (such as {} or space).
bool textnotermination() const { return flags_ & CharInfoTextNoTermination; } bool textNoTermination() const { return flags_ & CharInfoTextNoTermination; }
/// \c mathcommand needs no termination (such as {} or space). /// \c mathCommand needs no termination (such as {} or space).
bool mathnotermination() const { return flags_ & CharInfoMathNoTermination; } bool mathNoTermination() const { return flags_ & CharInfoMathNoTermination; }
/// ///
private: private:
/// LaTeX command (text mode) for this character /// LaTeX commands (text mode) for this character. The first one is the default, the others
trivdocstring textcommand_; /// are only present for compatibility other ways users may encode the character
/// LaTeX command (math mode) for this character std::vector<trivdocstring> text_commands_;
trivdocstring mathcommand_; /// LaTeX command (math mode) for this character. The first one is the default, the others
// /// are only present for compatibility other ways users may encode the character
std::vector<trivdocstring> math_commands_;
/// Needed LaTeX preamble (or feature) for text mode /// Needed LaTeX preamble (or feature) for text mode
trivstring textpreamble_; trivstring text_preamble_;
/// Needed LaTeX preamble (or feature) for math mode /// Needed LaTeX preamble (or feature) for math mode
trivstring mathpreamble_; trivstring math_preamble_;
/// TIPA shortcut /// TIPA shortcut
trivstring tipashortcut_; trivstring tipa_shortcut_;
/// feature flags /// feature flags
unsigned int flags_; unsigned int flags_;
}; };
@ -288,7 +298,7 @@ public:
/** /**
* Do we have to wrap in \text this character when in mathmode? * Do we have to wrap in \text this character when in mathmode?
* This is true if \p c is not ascii and the "mathalpha" flag is not * This is true if \p c is not ascii and the "mathalpha" flag is not
* set and a mathcommand is not defined in the unicodesymbols file. * set and a mathCommand is not defined in the unicodesymbols file.
*/ */
static bool isUnicodeTextOnly(char_type c); static bool isUnicodeTextOnly(char_type c);
/** /**

View File

@ -1535,9 +1535,9 @@ void Paragraph::Private::validate(LaTeXFeatures & features) const
else if (features.runparams().main_fontenc != "T1" else if (features.runparams().main_fontenc != "T1"
|| ((&owner_->getFontSettings(bp, i))->language()->internalFontEncoding())) || ((&owner_->getFontSettings(bp, i))->language()->internalFontEncoding()))
features.require("textquotedbl"); features.require("textquotedbl");
} else if (ci.textfeature() && contains(ci.textpreamble(), '=')) { } else if (ci.textFeature() && contains(ci.textPreamble(), '=')) {
// features that depend on the font or input encoding // features that depend on the font or input encoding
string feats = ci.textpreamble(); string feats = ci.textPreamble();
string fontenc = (&owner_->getFontSettings(bp, i))->language()->fontenc(bp); string fontenc = (&owner_->getFontSettings(bp, i))->language()->fontenc(bp);
if (fontenc.empty()) if (fontenc.empty())
fontenc = features.runparams().main_fontenc; fontenc = features.runparams().main_fontenc;