commit c0000cc405063044fb4eca3f04ab35f69fe3dd74
Author: Juergen Spitzmueller <[email protected]>
Date: Tue Dec 27 12:06:54 2016 +0100
Improve quotation mark opening/closing guess
Fixes: #8831
This introduces
* a new inset member isPartOfTextSequence() that returns
whether the inset produces something visible at the current position
in the text flow
* an isOpenPunctuation() test that returns whether a character is in the
'Punctuation, Open' unicode class. This is used instead of just checking
for two (Western, ASCII) opening brackets
It also fixes the isChar() and isLetter() value of InsetSpecialChar,
since some types have not been assigned correctly.
---
src/Text3.cpp | 37 +++++++++++++++++++++++++++++++++----
src/insets/Inset.h | 2 ++
src/insets/InsetArgument.h | 2 ++
src/insets/InsetBibitem.h | 2 ++
src/insets/InsetFloat.h | 2 ++
src/insets/InsetIndex.h | 2 ++
src/insets/InsetLabel.h | 2 ++
src/insets/InsetMarginal.h | 2 ++
src/insets/InsetQuotes.cpp | 10 +++-------
src/insets/InsetSpecialChar.cpp | 6 ++++++
src/insets/InsetSpecialChar.h | 4 +++-
src/insets/InsetWrap.h | 2 ++
src/support/lstrings.cpp | 12 ++++++++++++
src/support/textutils.h | 3 +++
14 files changed, 76 insertions(+), 12 deletions(-)
diff --git a/src/Text3.cpp b/src/Text3.cpp
index b9619bf..cc0a96f 100644
--- a/src/Text3.cpp
+++ b/src/Text3.cpp
@@ -1548,11 +1548,40 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
while (pos > 0 && par.isDeleted(pos - 1))
--pos;
+ bool const inner = (cmd.getArg(0) == "single" || cmd.getArg(0)
== "inner");
+
+ // Guess quote side.
+ // A space triggers an opening quote. This is passed if the
preceding
+ // char/inset is a space or at paragraph start.
char_type c = ' ';
- if (pos > 0 && (!cur.prevInset() ||
!cur.prevInset()->isSpace()))
- c = par.getChar(pos - 1);
- InsetQuotesParams::QuoteLevel const quote_level =
- (cmd.getArg(0) == "single" || cmd.getArg(0) ==
"inner")
+ if (pos > 0 && !par.isSpace(pos - 1)) {
+ if (cur.prevInset() && cur.prevInset()->lyxCode() ==
QUOTE_CODE) {
+ // If an opening double quotation mark
precedes, and this
+ // is a single quote, make it opening as well
+ InsetQuotes & ins =
+ static_cast<InsetQuotes
&>(*cur.prevInset());
+ string const type = ins.getType();
+ if (!suffixIs(type, "ld") || !inner)
+ c = par.getChar(pos - 1);
+ }
+ else if (!cur.prevInset()
+ || (cur.prevInset() && cur.prevInset()->isChar()))
+ // If a char precedes, pass that and let
InsetQuote decide
+ c = par.getChar(pos - 1);
+ else {
+ while (pos > 0) {
+ if (par.getInset(pos - 1)
+ && !par.getInset(pos -
1)->isPartOfTextSequence()) {
+ // skip "invisible" insets
+ --pos;
+ continue;
+ }
+ c = par.getChar(pos - 1);
+ break;
+ }
+ }
+ }
+ InsetQuotesParams::QuoteLevel const quote_level = inner
? InsetQuotesParams::SecondaryQuotes :
InsetQuotesParams::PrimaryQuotes;
cur.insert(new InsetQuotes(cur.buffer(), c, quote_level,
cmd.getArg(1), cmd.getArg(2)));
cur.buffer()->updateBuffer();
diff --git a/src/insets/Inset.h b/src/insets/Inset.h
index 4f6db3a..1e8b916 100644
--- a/src/insets/Inset.h
+++ b/src/insets/Inset.h
@@ -411,6 +411,8 @@ public:
/// Is the content of this inset part of the output document?
virtual bool producesOutput() const { return true; }
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ virtual bool isPartOfTextSequence() const { return producesOutput(); }
/// \return Tool tip for this inset.
/// This default implementation returns an empty string. This can be
diff --git a/src/insets/InsetArgument.h b/src/insets/InsetArgument.h
index 0a5d000..fb7e290 100644
--- a/src/insets/InsetArgument.h
+++ b/src/insets/InsetArgument.h
@@ -115,6 +115,8 @@ protected:
void doDispatch(Cursor & cur, FuncRequest & cmd);
///
Inset * clone() const { return new InsetArgument(*this); }
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
//@}
};
diff --git a/src/insets/InsetBibitem.h b/src/insets/InsetBibitem.h
index f18356b..41497e1 100644
--- a/src/insets/InsetBibitem.h
+++ b/src/insets/InsetBibitem.h
@@ -86,6 +86,8 @@ private:
void doDispatch(Cursor & cur, FuncRequest & cmd);
///
Inset * clone() const { return new InsetBibitem(*this); }
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
///@}
/// \name Private functions inherited from InsetCommand class
diff --git a/src/insets/InsetFloat.h b/src/insets/InsetFloat.h
index f146e11..e05f567 100644
--- a/src/insets/InsetFloat.h
+++ b/src/insets/InsetFloat.h
@@ -109,6 +109,8 @@ private:
void doDispatch(Cursor & cur, FuncRequest & cmd);
///
Inset * clone() const { return new InsetFloat(*this); }
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
///
TexString getCaption(OutputParams const &) const;
diff --git a/src/insets/InsetIndex.h b/src/insets/InsetIndex.h
index b1aa9b4..c0e23d8 100644
--- a/src/insets/InsetIndex.h
+++ b/src/insets/InsetIndex.h
@@ -83,6 +83,8 @@ private:
std::string contextMenuName() const;
///
Inset * clone() const { return new InsetIndex(*this); }
+ /// Is the content of this inset part of the immediate text sequence?
+ bool isPartOfTextSequence() const { return false; }
///
friend class InsetIndexParams;
diff --git a/src/insets/InsetLabel.h b/src/insets/InsetLabel.h
index a5e5e3e..27f445c 100644
--- a/src/insets/InsetLabel.h
+++ b/src/insets/InsetLabel.h
@@ -59,6 +59,8 @@ public:
///
void addToToc(DocIterator const & di, bool output_active,
UpdateType utype) const;
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
//@}
/// \name Static public methods obligated for InsetCommand derived
classes
diff --git a/src/insets/InsetMarginal.h b/src/insets/InsetMarginal.h
index 237beee..939c0d2 100644
--- a/src/insets/InsetMarginal.h
+++ b/src/insets/InsetMarginal.h
@@ -38,6 +38,8 @@ public:
///
void addToToc(DocIterator const & di, bool output_active,
UpdateType utype) const;
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
private:
///
Inset * clone() const { return new InsetMarginal(*this); }
diff --git a/src/insets/InsetQuotes.cpp b/src/insets/InsetQuotes.cpp
index 1191eb1..6ed66b2 100644
--- a/src/insets/InsetQuotes.cpp
+++ b/src/insets/InsetQuotes.cpp
@@ -41,6 +41,7 @@
#include "support/docstream.h"
#include "support/gettext.h"
#include "support/lstrings.h"
+#include "support/textutils.h"
#include <string.h>
@@ -526,15 +527,10 @@ docstring InsetQuotes::layoutName() const
void InsetQuotes::setSide(char_type c)
{
// Decide whether opening or closing quote
- switch (c) {
- case ' ':
- case '(':
- case '[':
+ if (lyx::isSpace(c) || isOpenPunctuation(c))
side_ = InsetQuotesParams::OpeningQuote;// opening quote
- break;
- default:
+ else
side_ = InsetQuotesParams::ClosingQuote;// closing quote
- }
}
diff --git a/src/insets/InsetSpecialChar.cpp b/src/insets/InsetSpecialChar.cpp
index 25c8a3a..ff4c720 100644
--- a/src/insets/InsetSpecialChar.cpp
+++ b/src/insets/InsetSpecialChar.cpp
@@ -571,6 +571,12 @@ void InsetSpecialChar::validate(LaTeXFeatures & features)
const
}
+bool InsetSpecialChar::isChar() const
+{
+ return kind_ != HYPHENATION || kind_ != LIGATURE_BREAK;
+}
+
+
bool InsetSpecialChar::isLetter() const
{
return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
diff --git a/src/insets/InsetSpecialChar.h b/src/insets/InsetSpecialChar.h
index ebdfb1a..6474c57 100644
--- a/src/insets/InsetSpecialChar.h
+++ b/src/insets/InsetSpecialChar.h
@@ -89,11 +89,13 @@ public:
void validate(LaTeXFeatures &) const;
/// should this inset be handled like a normal character?
- bool isChar() const { return true; }
+ bool isChar() const;
/// is this equivalent to a letter?
bool isLetter() const;
/// should we break lines after this inset?
bool isLineSeparator() const;
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return isChar(); }
private:
Inset * clone() const { return new InsetSpecialChar(*this); }
diff --git a/src/insets/InsetWrap.h b/src/insets/InsetWrap.h
index af1ae53..588a3bc 100644
--- a/src/insets/InsetWrap.h
+++ b/src/insets/InsetWrap.h
@@ -89,6 +89,8 @@ private:
docstring layoutName() const;
///
Inset * clone() const { return new InsetWrap(*this); }
+ /// Is the content of this inset part of the immediate (visible) text
sequence?
+ bool isPartOfTextSequence() const { return false; }
///
InsetWrapParams params_;
diff --git a/src/support/lstrings.cpp b/src/support/lstrings.cpp
index 0dd8075..99591d0 100644
--- a/src/support/lstrings.cpp
+++ b/src/support/lstrings.cpp
@@ -182,6 +182,18 @@ bool isASCII(char_type c)
}
+bool isOpenPunctuation(char_type c)
+{
+ if (!is_utf16(c)) {
+ // assume that no non-utf16 character is an op
+ // c outside the UCS4 range is catched as well
+ return false;
+ }
+ QChar const qc = ucs4_to_qchar(c);
+ return qc.category() == QChar::Punctuation_Open;
+}
+
+
namespace support {
int compare_no_case(docstring const & s, docstring const & s2)
diff --git a/src/support/textutils.h b/src/support/textutils.h
index 78e34cb..f596588 100644
--- a/src/support/textutils.h
+++ b/src/support/textutils.h
@@ -53,6 +53,9 @@ bool isAlnumASCII(char_type c);
/// return whether \p c is in the ASCII range
bool isASCII(char_type c);
+/// return whether \p c is in the 'Punctuation, Open' unicode category
+bool isOpenPunctuation(char_type c);
+
} // namespace lyx
#endif // TEXTUTILS_H