From e8c932ff842ced0b26c205ed6983102b6e7e50c2 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sat, 10 Dec 2016 11:53:42 +0100 Subject: [PATCH] Enable InsetQuote in verbatim and Hebrew In this context, the inset outputs straight quotes in LaTeX. File format change. Fixes: #7309 --- development/FORMAT | 5 +++ lib/lyx2lyx/lyx_2_3.py | 72 ++++++++++++++++++++++++++++++++++-- src/Text3.cpp | 28 +++++--------- src/insets/InsetListings.cpp | 14 ++++++- src/insets/InsetListings.h | 2 +- src/insets/InsetQuotes.cpp | 45 +++++++++++++++------- src/insets/InsetQuotes.h | 7 ++++ src/insets/InsetText.cpp | 3 +- src/version.h | 4 +- 9 files changed, 139 insertions(+), 41 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index 225d5caaa3..2a06d49772 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -11,6 +11,11 @@ adjustments are made to tex2lyx and bugs are fixed in lyx2lyx. ----------------------- +2016-12-10 Jürgen Spitzmüller + * Format incremented to 517: InsetQuote now works in verbatim + and Hebrew. On reversion, it is replaced by straight quotes + in these contexts. + 2016-12-07 Günter Milde * Format incremented to 516: Removed \inputenc value "pt254" diff --git a/lib/lyx2lyx/lyx_2_3.py b/lib/lyx2lyx/lyx_2_3.py index 804d0e62c2..4d42b41f1b 100644 --- a/lib/lyx2lyx/lyx_2_3.py +++ b/lib/lyx2lyx/lyx_2_3.py @@ -25,13 +25,14 @@ import sys, os # Uncomment only what you need to import, please. -from parser_tools import find_end_of#, find_token, find_tokens, \ +from parser_tools import find_end_of, find_token_backwards#, +# find_token, find_tokens, \ # find_token_exact, find_end_of_inset, find_end_of_layout, \ -# find_token_backwards, is_in_inset, get_value, get_quoted_value, \ +# is_in_inset, get_value, get_quoted_value, \ # del_token, check_token, get_option_value, get_bool_value from parser_tools import find_token, find_end_of_inset, get_value, \ - get_bool_value + get_bool_value, get_containing_layout from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert # get_ert, lyx2latex, \ @@ -492,6 +493,67 @@ def revert_syriac(document): "\\end_layout", ""] +def revert_quotes(document): + " Revert Quote Insets in verbatim or Hebrew context to plain quotes " + + # First handle verbatim insets + i = 0 + j = 0 + while i < len(document.body): + words = document.body[i].split() + if len(words) > 1 and words[0] == "\\begin_inset" and \ + ( words[1] in ["ERT", "listings"] or words[2] == "URL" ): + j = find_end_of_inset(document.body, i) + if j == -1: + document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i)) + i += 1 + continue + while True: + k = find_token(document.body, '\\begin_inset Quotes', i, j) + if k == -1: + i += 1 + break + l = find_end_of_inset(document.body, k) + if l == -1: + document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k)) + i = k + continue + replace = "\"" + if document.body[k].endswith("s"): + replace = "'" + document.body[k:l+1] = [replace] + else: + i += 1 + continue + + # Now handle Hebrew + i = 0 + j = 0 + while True: + k = find_token(document.body, '\\begin_inset Quotes', i, j) + if k == -1: + return + l = find_end_of_inset(document.body, k) + if l == -1: + document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k)) + i = k + continue + hebrew = False + parent = get_containing_layout(document.body, k) + ql = find_token_backwards(document.body, "\\lang", k) + if ql == -1 or ql < parent[1]: + hebrew = document.language == "hebrew" + elif document.body[ql] == "\\lang hebrew": + hebrew = True + if hebrew: + replace = "\"" + if document.body[k].endswith("s"): + replace = "'" + document.body[k:l+1] = [replace] + i += 1 + + + ## # Conversion hub # @@ -506,10 +568,12 @@ convert = [ [514, []], [515, []], [516, [convert_inputenc]], + [517, []] ] revert = [ - [516, []], + [516, [revert_quotes]], + [515, []], [514, [revert_urdu, revert_syriac]], [513, [revert_amharic, revert_asturian, revert_kannada, revert_khmer]], [512, [revert_bosnian, revert_friulan, revert_macedonian, revert_piedmontese, revert_romansh]], diff --git a/src/Text3.cpp b/src/Text3.cpp index a2a0cc729e..a7c3cd00df 100644 --- a/src/Text3.cpp +++ b/src/Text3.cpp @@ -1548,26 +1548,15 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd) while (pos > 0 && par.isDeleted(pos - 1)) --pos; - BufferParams const & bufparams = bv->buffer().params(); - bool const hebrew = - par.getFontSettings(bufparams, pos).language()->lang() == "hebrew"; - bool const allow_inset_quote = !(par.isPassThru() || hebrew); - string const arg = to_utf8(cmd.argument()); - if (allow_inset_quote) { - char_type c = ' '; - if (pos > 0 && (!cur.prevInset() || !cur.prevInset()->isSpace())) - c = par.getChar(pos - 1); - InsetQuotes::QuoteTimes const quote_type = (arg == "single") - ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes; - cur.insert(new InsetQuotes(cur.buffer(), c, quote_type)); - cur.posForward(); - } else { - // The cursor might have been invalidated by the replaceSelection. - cur.buffer()->changed(true); - string const quote_string = (arg == "single") ? "'" : "\""; - lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, quote_string)); - } + char_type c = ' '; + if (pos > 0 && (!cur.prevInset() || !cur.prevInset()->isSpace())) + c = par.getChar(pos - 1); + InsetQuotes::QuoteTimes const quote_type = (arg == "single") + ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes; + cur.insert(new InsetQuotes(cur.buffer(), c, quote_type)); + cur.buffer()->updateBuffer(); + cur.posForward(); break; } @@ -2860,6 +2849,7 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd, case LFUN_QUOTE_INSERT: // always allow this, since we will inset a raw quote // if an inset is not allowed. + allow_in_passthru = true; break; case LFUN_SPECIALCHAR_INSERT: code = SPECIALCHAR_CODE; diff --git a/src/insets/InsetListings.cpp b/src/insets/InsetListings.cpp index a92c09525c..bec33b12cb 100644 --- a/src/insets/InsetListings.cpp +++ b/src/insets/InsetListings.cpp @@ -158,8 +158,20 @@ void InsetListings::latex(otexstream & os, OutputParams const & runparams) const 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)) + 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! diff --git a/src/insets/InsetListings.h b/src/insets/InsetListings.h index 14dbda94d0..902fa0437e 100644 --- a/src/insets/InsetListings.h +++ b/src/insets/InsetListings.h @@ -78,7 +78,7 @@ private: /// TexString getCaption(OutputParams const &) const; /// - bool insetAllowed(InsetCode c) const { return c == CAPTION_CODE; } + bool insetAllowed(InsetCode c) const { return c == CAPTION_CODE || c == QUOTE_CODE; } /// InsetListingsParams params_; diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp index 0773e284ef..130375dfb0 100644 --- a/src/insets/InsetQuotes.cpp +++ b/src/insets/InsetQuotes.cpp @@ -24,6 +24,8 @@ #include "MetricsInfo.h" #include "OutputParams.h" #include "output_xhtml.h" +#include "Paragraph.h" +#include "ParIterator.h" #include "texstream.h" #include "frontends/FontMetrics.h" @@ -103,7 +105,7 @@ InsetQuotes::InsetQuotes(Buffer * buf, string const & str) : Inset(buf) } InsetQuotes::InsetQuotes(Buffer * buf, char_type c, QuoteTimes t) - : Inset(buf), times_(t) + : Inset(buf), times_(t), pass_thru_(false) { if (buf) { language_ = buf->params().quotes_language; @@ -188,17 +190,18 @@ void InsetQuotes::parseString(string const & s) } -// FIXME: should we add a language or a font parameter member? docstring InsetQuotes::displayString() const { - Language const * loclang = - isBufferValid() ? buffer().params().language : 0; + // In PassThru and Hebrew, we use straight quotes + if (pass_thru_ || context_lang_ == "he_IL") + return (times_ == DoubleQuotes) ? from_ascii("\"") : from_ascii("'"); + int const index = quote_index[side_][language_]; docstring retdisp = docstring(1, display_quote_char[times_][index]); - // in french, thin spaces are added inside double quotes + // in French, thin spaces are added inside double quotes // FIXME: this should be done by a separate quote type. - if (times_ == DoubleQuotes && loclang && prefixIs(loclang->code(), "fr")) { + if (times_ == DoubleQuotes && prefixIs(context_lang_, "fr")) { // THIN SPACE (U+2009) char_type const thin_space = 0x2009; if (side_ == LeftQuote) @@ -253,7 +256,13 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const const int quoteind = quote_index[side_][language_]; string qstr; - if (language_ == FrenchQuotes && times_ == DoubleQuotes + // In some context, we output plain quotes + bool const force_plain = + runparams.pass_thru + || runparams.local_font->language()->lang() == "hebrew"; + if (force_plain) + qstr = (times_ == DoubleQuotes) ? "\"" : "'"; + else if (language_ == FrenchQuotes && times_ == DoubleQuotes && prefixIs(runparams.local_font->language()->code(), "fr") && !runparams.use_polyglossia) { if (side_ == LeftQuote) @@ -273,14 +282,16 @@ void InsetQuotes::latex(otexstream & os, OutputParams const & runparams) const qstr = latex_quote_babel[times_][quoteind]; } - // Always guard against unfortunate ligatures (!` ?` `` '' ,, << >>) - char_type const lastchar = os.lastChar(); - if (prefixIs(qstr, "`")) { - if (lastchar == '!' || lastchar == '?') + if (!force_plain) { + // Always guard against unfortunate ligatures (!` ?` `` '' ,, << >>) + char_type const lastchar = os.lastChar(); + if (prefixIs(qstr, "`")) { + if (lastchar == '!' || lastchar == '?') + qstr.insert(0, "{}"); + } + if (qstr[1] == lastchar) qstr.insert(0, "{}"); } - if (qstr[1] == lastchar) - qstr.insert(0, "{}"); os << from_ascii(qstr); } @@ -327,6 +338,14 @@ void InsetQuotes::forOutliner(docstring & os, size_t const, bool const) const } +void InsetQuotes::updateBuffer(ParIterator const & it, UpdateType /* utype*/) +{ + BufferParams const & bp = buffer().masterBuffer()->params(); + pass_thru_ = it.paragraph().isPassThru(); + context_lang_ = it.paragraph().getFontSettings(bp, it.pos()).language()->code(); +} + + void InsetQuotes::validate(LaTeXFeatures & features) const { char type = quote_char[quote_index[side_][language_]]; diff --git a/src/insets/InsetQuotes.h b/src/insets/InsetQuotes.h index 49bd84205d..b26c5d3235 100644 --- a/src/insets/InsetQuotes.h +++ b/src/insets/InsetQuotes.h @@ -90,6 +90,9 @@ public: /// void forOutliner(docstring &, size_t const maxlen, bool const) const; + /// Update the contextual information of this inset + void updateBuffer(ParIterator const &, UpdateType); + /// void validate(LaTeXFeatures &) const; /// @@ -118,6 +121,10 @@ private: QuoteTimes times_; /// std::string fontenc_; + /// Code of the contextual language + std::string context_lang_; + /// Is this in a pass-thru context? + bool pass_thru_; }; } // namespace lyx diff --git a/src/insets/InsetText.cpp b/src/insets/InsetText.cpp index 3c1bb23f20..607f7f577a 100644 --- a/src/insets/InsetText.cpp +++ b/src/insets/InsetText.cpp @@ -768,8 +768,9 @@ ParagraphList & InsetText::paragraphs() bool InsetText::insetAllowed(InsetCode code) const { switch (code) { - // Arguments are also allowed in PassThru insets + // Arguments and (plain) quotes are also allowed in PassThru insets case ARG_CODE: + case QUOTE_CODE: return true; default: return !isPassThru(); diff --git a/src/version.h b/src/version.h index 15f687c135..5e67fa9604 100644 --- a/src/version.h +++ b/src/version.h @@ -32,8 +32,8 @@ extern char const * const lyx_version_info; // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -#define LYX_FORMAT_LYX 516 // Removed \inputenc value "pt254" -#define LYX_FORMAT_TEX2LYX 516 +#define LYX_FORMAT_LYX 517 // spitz: quote inset in verbatim +#define LYX_FORMAT_TEX2LYX 517 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX #ifndef _MSC_VER