commit f3256ee2cddabbe8d763013525c3e27f6ad0b3dc
Author: Guillaume Munch <g...@lyx.org>
Date:   Sun Sep 4 03:21:19 2016 +0100

    texstring and otexstringstream
    
    texstring is a pair of a docstring and a corresponding TexRow. The row 
count in
    the TexRow has to match the number of lines in the docstring.
    
    otexstringstream is an output string stream that can be used to create
    texstrings (i.e. it's an odocstringstream that records the TexRow 
information
    and let us extract a texstring from it).
    
    texstrings can be passed around and output to otexstream and otexrowstream,
    which produces an accurate TexRow information by concatenating TexRows.
---
 src/Paragraph.cpp         |    4 +-
 src/TexRow.cpp            |   30 ++++++++++++++++++++----
 src/TexRow.h              |   37 +++++++++++++++++++++++++++++-
 src/insets/InsetFloat.cpp |   10 ++++----
 src/texstream.cpp         |   56 +++++++++++++++++++++++++++++++++++++++-----
 src/texstream.h           |   31 +++++++++++++++++++++---
 6 files changed, 144 insertions(+), 24 deletions(-)

diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index 3bdb4cc..3bae5bc 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -1118,7 +1118,7 @@ void Paragraph::Private::latexInset(BufferParams const & 
bparams,
                }
        }
 
-       int prev_rows = os.texrow().rows();
+       size_t const previous_row_count = os.texrow().rows();
 
        try {
                runparams.lastid = id_;
@@ -1138,7 +1138,7 @@ void Paragraph::Private::latexInset(BufferParams const & 
bparams,
                                os << '}';
        }
 
-       if (os.texrow().rows() > prev_rows) {
+       if (os.texrow().rows() > previous_row_count) {
                os.texrow().start(owner_->id(), i + 1);
                column = 0;
        } else {
diff --git a/src/TexRow.cpp b/src/TexRow.cpp
index 6f0e804..49712a9 100644
--- a/src/TexRow.cpp
+++ b/src/TexRow.cpp
@@ -34,13 +34,27 @@ using namespace std;
 namespace lyx {
 
 
+void TexString::validate()
+{
+       size_t lines = 1 + count(str.begin(), str.end(), '\n');
+       size_t rows = texrow.rows();
+       bool valid = lines == rows;
+       if (!valid)
+               LYXERR0("TexString has " << lines << " lines but " << rows << " 
rows." );
+       // Assert in devel mode.  This is important to catch bugs early, 
otherwise
+       // they might be hard to notice and find.  Recover gracefully in release
+       // mode.
+       LASSERT(valid, texrow.setRows(lines));
+}
+
+
 bool TexRow::RowEntryList::addEntry(RowEntry entry)
 {
        if (!entry.is_math) {
-               if (!isNone(text_entry_))
-                       return false;
-               else
+               if (isNone(text_entry_))
                        text_entry_ = entry.text;
+               else if (!v_.empty() && TexRow::sameParOrInsetMath(v_.back(), 
entry))
+                       return false;
        }
        forceAddEntry(entry);
        return true;
@@ -499,12 +513,18 @@ pair<int,int> TexRow::rowFromCursor(Cursor const & cur) 
const
 }
 
 
-int TexRow::rows() const
+size_t TexRow::rows() const
 {
        return rowlist_.size();
 }
 
 
+void TexRow::setRows(size_t r)
+{
+       rowlist_.resize(r, RowEntryList());
+}
+
+
 // debugging functions
 
 ///
@@ -544,7 +564,7 @@ void TexRow::prepend(docstring_list & tex) const
 LyXErr & operator<<(LyXErr & l, TexRow const & texrow)
 {
        if (l.enabled()) {
-               for (int i = 0; i < texrow.rows(); i++) {
+               for (size_t i = 0; i < texrow.rows(); i++) {
                        int id,pos;
                        if (texrow.getIdFromRow(i+1,id,pos) && id>0)
                                l << i+1 << ":" << id << ":" << pos << "\n";
diff --git a/src/TexRow.h b/src/TexRow.h
index 4969f3a..3ca5172 100644
--- a/src/TexRow.h
+++ b/src/TexRow.h
@@ -89,6 +89,14 @@ public:
        ///
        TexRow();
 
+       /// Copy can be expensive and is not usually useful for TexRow.
+       /// Force explicit copy, prefer move instead. This also prevents
+       /// move()s from being converted into copy silently.
+       explicit TexRow(TexRow const & other) = default;
+       TexRow(TexRow && other) = default;
+       TexRow & operator=(TexRow const & other) = default;
+       TexRow & operator=(TexRow && other) = default;
+
        /// Clears structure.
        void reset();
 
@@ -164,7 +172,9 @@ public:
        std::pair<int,int> rowFromCursor(Cursor const & dit) const;
 
        /// Returns the number of rows contained
-       int rows() const;
+       size_t rows() const;
+       /// Fill or trim to reach the row count \param r
+       void setRows(size_t r);
 
        /// appends texrow. the final line of this is merged with the first 
line of
        /// texrow.
@@ -183,6 +193,31 @@ private:
 };
 
 
+/// TexString : dumb struct to pass around docstrings with TexRow information.
+/// They are best created using oTexStringstream.
+/// They can be output to otexrowstreams and otexstreams.
+/// A valid TexString has as many newlines in str as in texrow. Be careful not
+/// to introduce a mismatch between the line and the row counts, as this will
+/// assert in devel mode when outputting to a otexstream.
+struct TexString {
+       ///
+       docstring str;
+       ///
+       TexRow texrow;
+       /// Copy can be expensive and is not usually useful for TexString.
+       /// Force explicit copy, prefer move instead. This also prevents
+       /// move()s from being converted into copy silently.
+       explicit TexString(TexString const &) = default;
+       TexString(TexString && other) = default;
+       TexString & operator=(TexString const & other) = default;
+       TexString & operator=(TexString && other) = default;
+       ///
+       TexString() = default;
+       /// ensure that the string and the TexRow have as many newlines.
+       void validate();
+};
+
+
 // Standard container needs a complete type
 class TexRow::RowEntryList {
        // For each row we store a list of one special TextEntry and several
diff --git a/src/insets/InsetFloat.cpp b/src/insets/InsetFloat.cpp
index 49fd3bb..5d966af 100644
--- a/src/insets/InsetFloat.cpp
+++ b/src/insets/InsetFloat.cpp
@@ -516,14 +516,14 @@ void InsetFloat::getCaption(otexstream & os,
        ins->getArgs(os, runparams);
 
        os << '[';
-       odocstringstream ods;
-       otexstream oss(ods);
-       ins->getArgument(oss, runparams);
-       docstring arg = ods.str();
+       otexstringstream os2;
+       ins->getArgument(os2, runparams);
+       TexString ts = os2.release();
+       docstring & arg = ts.str;
        // Protect ']'
        if (arg.find(']') != docstring::npos)
                arg = '{' + arg + '}';
-       os.append(arg, move(oss.texrow()));
+       os << move(ts);
        os << ']';
 }
 
diff --git a/src/texstream.cpp b/src/texstream.cpp
index e312d2c..be1168a 100644
--- a/src/texstream.cpp
+++ b/src/texstream.cpp
@@ -49,13 +49,6 @@ unique_ptr<TexRow> otexrowstream::releaseTexRow()
 }
 
 
-void otexrowstream::append(docstring const & str, TexRow texrow)
-{
-       os_ << str;
-       texrow_->append(move(texrow));
-}
-
-
 void otexrowstream::put(char_type const & c)
 {
        os_.put(c);
@@ -76,6 +69,22 @@ void otexstream::put(char_type const & c)
 }
 
 
+size_t otexstringstream::length()
+{
+       auto pos = ods_.tellp();
+       return (pos >= 0) ? size_t(pos) : 0;
+}
+
+
+TexString otexstringstream::release()
+{
+       TexString ts{ods_.str(), TexRow()};
+       swap(ts.texrow, texrow());
+       ods_ = odocstringstream();
+       return ts;
+}
+
+
 BreakLine breakln;
 SafeBreakLine safebreakln;
 
@@ -112,6 +121,7 @@ otexrowstream & operator<<(otexrowstream & ots, 
odocstream_manip pf)
        return ots;
 }
 
+
 otexstream & operator<<(otexstream & ots, odocstream_manip pf)
 {
        otexrowstream & otrs = ots;
@@ -123,6 +133,38 @@ otexstream & operator<<(otexstream & ots, odocstream_manip 
pf)
 }
 
 
+otexrowstream & operator<<(otexrowstream & ots, TexString ts)
+{
+       ts.validate();
+       ots.os() << move(ts.str);
+       ots.texrow().append(move(ts.texrow));
+       return ots;
+}
+
+
+otexstream & operator<<(otexstream & ots, TexString ts)
+{
+       size_t const len = ts.str.length();
+       // Check whether there is something to output
+       if (len == 0)
+               return ots;
+
+       otexrowstream & otrs = ots;
+       if (ots.protectSpace()) {
+               if (!ots.canBreakLine() && ts.str[0] == ' ')
+                       otrs << "{}";
+               ots.protectSpace(false);
+       }
+
+       if (len > 1)
+               ots.canBreakLine(ts.str[len - 2] != '\n');
+       ots.lastChar(ts.str[len - 1]);
+
+       otrs << move(ts);
+       return ots;
+}
+
+
 otexrowstream & operator<<(otexrowstream & ots, docstring const & s)
 {
        ots.os() << s;
diff --git a/src/texstream.h b/src/texstream.h
index 53040d1..9c882b9 100644
--- a/src/texstream.h
+++ b/src/texstream.h
@@ -18,11 +18,9 @@
 namespace lyx {
 
 class TexRow;
+class TexString;
 
 
-// declared below
-class otexstringstream;
-
 /** Wrapper class for odocstream.
     This class is used to automatically count the lines of the exported latex
     code.
@@ -43,7 +41,7 @@ public:
        ///
        void put(char_type const & c);
        ///
-       void append(docstring const & str, TexRow texrow);
+       void append(TexString ts);
 private:
        ///
        odocstream & os_;
@@ -54,6 +52,8 @@ private:
 ///
 otexrowstream & operator<<(otexrowstream &, odocstream_manip);
 ///
+otexrowstream & operator<<(otexrowstream &, TexString);
+///
 otexrowstream & operator<<(otexrowstream &, docstring const &);
 ///
 otexrowstream & operator<<(otexrowstream &, std::string const &);
@@ -85,6 +85,8 @@ public:
        ///
        void put(char_type const & c);
        ///
+       void append(TexString ts);
+       ///
        void canBreakLine(bool breakline) { canbreakline_ = breakline; }
        ///
        bool canBreakLine() const { return canbreakline_; }
@@ -114,6 +116,25 @@ private:
        char_type lastchar_;
 };
 
+
+/// because we need to pass ods_ to the base class
+struct otexstringstream_helper { odocstringstream ods_; };
+
+/// otexstringstream : a odocstringstream with tex/row correspondence
+class otexstringstream : otexstringstream_helper, public otexstream {
+public:
+       otexstringstream() : otexstringstream_helper(), otexstream(ods_) {}
+       ///
+       docstring str() const { return ods_.str(); }
+       ///
+       size_t length();
+       ///
+       bool empty() { return 0 == length(); }
+       /// move-returns the contents and reset the texstream
+       TexString release();
+};
+
+
 /// Helper structs for breaking a line
 struct BreakLine {
        char n;
@@ -133,6 +154,8 @@ otexstream & operator<<(otexstream &, SafeBreakLine);
 ///
 otexstream & operator<<(otexstream &, odocstream_manip);
 ///
+otexstream & operator<<(otexstream &, TexString);
+///
 otexstream & operator<<(otexstream &, docstring const &);
 ///
 otexstream & operator<<(otexstream &, std::string const &);

Reply via email to