From 966687d98b0a434b31d8ba2a508a6783c4245c8a Mon Sep 17 00:00:00 2001 From: Vincent van Ravesteijn Date: Thu, 23 Jul 2009 20:08:05 +0000 Subject: [PATCH] Fix bug #6058: Change tracking not usable in version control. This patch makes sure that there are minimal changes when loading and saving a file with change tracking. - the authors are assigned a buffer_id, such that when the file is saved, they get the same id, - the authorlist is sorted according to the buffer_id, - the buffer_id is written to the file in the author list (file format change) - the ids start with 1, because 0 is internally reserved for the current Author, - when writing the file, the current author is assigned an id if he didn't already have it. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@30756 a592a061-630c-0410-9148-cb99ea01b6c8 --- development/FORMAT | 4 +++ lib/lyx2lyx/lyx_2_0.py | 76 ++++++++++++++++++++++++++++++++++++++++-- src/Author.cpp | 71 +++++++++++++++++++++++++++++++++------ src/Author.h | 14 ++++++-- src/Buffer.cpp | 4 +-- src/BufferParams.cpp | 11 ++---- src/BufferParams.h | 4 +-- src/Changes.cpp | 8 +++-- src/Changes.h | 4 +-- src/Paragraph.cpp | 2 +- src/Text.cpp | 9 ++--- 11 files changed, 170 insertions(+), 37 deletions(-) diff --git a/development/FORMAT b/development/FORMAT index da14f5dea2..9c66c43069 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -1,6 +1,10 @@ LyX file-format changes ----------------------- +2009-07-22 Vincent van Ravesteijn + * Format incremented to 369: add the author ids to the list of + authors and let the numbering start with 1 in stead of 0. + 2009-07-21 Jürgen Spitzmüller , Uwe Stöhr * Format incremented to 368: allow to use glue lengths for horizontal spaces. diff --git a/lib/lyx2lyx/lyx_2_0.py b/lib/lyx2lyx/lyx_2_0.py index 9fe7ec2707..96e681204c 100644 --- a/lib/lyx2lyx/lyx_2_0.py +++ b/lib/lyx2lyx/lyx_2_0.py @@ -947,6 +947,76 @@ def revert_hspace_glue_lengths(document): document.body[i:i+3] = subst i = i + 2 +def convert_author_id(document): + " Add the author_id to the \\author definition and make sure 0 is not used" + i = 0 + j = 1 + while True: + i = find_token(document.header, "\\author", i) + if i == -1: + break + + author = document.header[i].split(' ') + name = '\"\"' + if len(author) >= 2: + name = author[1] + email = '' + if len(author) == 3: + email = author[2] + document.header[i] = "\\author %i %s %s" % (j, name, email) + j = j + 1 + i = i + 1 + + k = 0 + while True: + k = find_token(document.body, "\\change_", k) + if k == -1: + break + + change = document.body[k].split(' '); + if len(change) == 3: + type = change[0] + author_id = int(change[1]) + time = change[2] + document.body[k] = "%s %i %s" % (type, author_id + 1, time) + k = k + 1 + +def revert_author_id(document): + " Remove the author_id from the \\author definition " + i = 0 + j = 0 + idmap = dict() + while True: + i = find_token(document.header, "\\author", i) + if i == -1: + break + author = document.header[i].split(' ') + name = '\"\"' + if len(author) >= 3: + author_id = int(author[1]) + idmap[author_id] = j + name = author[2] + email = '' + if len(author) == 4: + email = author[3] + document.header[i] = "\\author %s %s" % (name, email) + i = i + 1 + j = j + 1 + + k = 0 + while True: + k = find_token(document.body, "\\change_", k) + if k == -1: + break + + change = document.body[k].split(' '); + if len(change) == 3: + type = change[0] + author_id = int(change[1]) + time = change[2] + document.body[k] = "%s %i %s" % (type, idmap[author_id], time) + k = k + 1 + ## # Conversion hub @@ -975,10 +1045,12 @@ convert = [[346, []], [365, []], [366, []], [367, []], - [368, []] + [368, []], + [369, [convert_author_id]] ] -revert = [[367, [revert_hspace_glue_lengths]], +revert = [[368, [revert_author_id]], + [367, [revert_hspace_glue_lengths]], [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]], [365, [revert_percent_skip_lengths]], [364, [revert_paragraph_indentation]], diff --git a/src/Author.cpp b/src/Author.cpp index c2dc6eeb29..0ad9b898ea 100644 --- a/src/Author.cpp +++ b/src/Author.cpp @@ -16,6 +16,7 @@ #include "support/lassert.h" +#include #include using namespace std; @@ -33,13 +34,16 @@ bool operator==(Author const & l, Author const & r) ostream & operator<<(ostream & os, Author const & a) { // FIXME UNICODE - os << "\"" << to_utf8(a.name()) << "\" " << to_utf8(a.email()); + os << a.buffer_id() << " \"" << to_utf8(a.name()) + << "\" " << to_utf8(a.email()); + return os; } istream & operator>>(istream & is, Author & a) { string s; + is >> a.buffer_id_; getline(is, s); // FIXME UNICODE a.name_ = from_utf8(trim(token(s, '\"', 1))); @@ -48,6 +52,11 @@ istream & operator>>(istream & is, Author & a) } +bool author_smaller(Author const & lhs, Author const & rhs) { + return lhs.buffer_id() < rhs.buffer_id(); +} + + AuthorList::AuthorList() : last_id_(0) { @@ -59,13 +68,17 @@ int AuthorList::record(Author const & a) Authors::const_iterator it(authors_.begin()); Authors::const_iterator itend(authors_.end()); - for (; it != itend; ++it) { - if (it->second == a) - return it->first; + for (int i = 0; it != itend; ++it, ++i) { + if (*it == a) { + if (it->buffer_id() == 0) + // The current author is internally represented as + // author 0, but it appears he has already an id. + it->setBufferId(a.buffer_id()); + return i; + } } - - authors_[last_id_++] = a; - return last_id_ - 1; + authors_.push_back(a); + return last_id_++; } @@ -79,9 +92,8 @@ void AuthorList::record(int id, Author const & a) Author const & AuthorList::get(int id) const { - Authors::const_iterator it(authors_.find(id)); - LASSERT(it != authors_.end(), /**/); - return it->second; + LASSERT(id < (int)authors_.size() , /**/); + return authors_[id]; } @@ -97,4 +109,43 @@ AuthorList::Authors::const_iterator AuthorList::end() const } +void AuthorList::sort() { + std::sort(authors_.begin(), authors_.end(), author_smaller); +} + + +ostream & operator<<(ostream & os, AuthorList const & a) { + // Copy the authorlist, because we don't want to sort the original + AuthorList sorted = a; + sorted.sort(); + + AuthorList::Authors::const_iterator a_it = sorted.begin(); + AuthorList::Authors::const_iterator a_end = sorted.end(); + + // Find the buffer id for the current author (internal id 0), + // if he doesn't have a buffer_id yet. + if (sorted.get(0).buffer_id() == 0) { + unsigned int cur_id = 1; + for (; a_it != a_end; ++a_it) { + if (a_it->buffer_id() == cur_id) + ++cur_id; + else if (a_it->buffer_id() > cur_id) { + break; + } + } + // Set the id in both the original authorlist, + // as in the copy. + a.get(0).setBufferId(cur_id); + sorted.get(0).setBufferId(cur_id); + sorted.sort(); + } + + for (a_it = sorted.begin(); a_it != a_end; ++a_it) { + if (a_it->used()) + os << "\\author " << *a_it << "\n"; + } + return os; +} + + } // namespace lyx diff --git a/src/Author.h b/src/Author.h index 0f1e49e7a7..21f479da12 100644 --- a/src/Author.h +++ b/src/Author.h @@ -25,12 +25,16 @@ public: Author() {} /// Author(docstring const & name, docstring const & email) - : name_(name), email_(email), used_(true) {} + : name_(name), email_(email), used_(true), buffer_id_(0) {} /// docstring name() const { return name_; } /// docstring email() const { return email_; } /// + unsigned int buffer_id() const { return buffer_id_; } + /// + void setBufferId(unsigned int buffer_id) const { buffer_id_ = buffer_id; } + /// void setUsed(bool u) const { used_ = u; } /// bool used() const { return used_; } @@ -42,6 +46,8 @@ private: docstring name_; /// The author's email address docstring email_; + /// The id of the author in the lyx-file + mutable unsigned int buffer_id_; /// mutable bool used_; }; @@ -58,12 +64,16 @@ public: /// Author const & get(int id) const; /// - typedef std::map Authors; + typedef std::vector Authors; + /// + void sort(); /// Authors::const_iterator begin() const; /// Authors::const_iterator end() const; /// + friend + std::ostream & operator<<(std::ostream & os, AuthorList const & a); private: /// int last_id_; diff --git a/src/Buffer.cpp b/src/Buffer.cpp index f24eac33b9..8dd228b5b9 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -127,7 +127,7 @@ namespace { // Do not remove the comment below, so we get merge conflict in // independent branches. Instead add your own. -int const LYX_FORMAT = 368; // spitz, uwestoehr: glue lengths for the HSpace dialog +int const LYX_FORMAT = 369; // vfr: add author ids to list of authors typedef map DepClean; typedef map > RefCache; @@ -1014,7 +1014,7 @@ bool Buffer::write(ostream & ofs) const AuthorList::Authors::const_iterator a_it = params().authors().begin(); AuthorList::Authors::const_iterator a_end = params().authors().end(); for (; a_it != a_end; ++a_it) - a_it->second.setUsed(false); + a_it->setUsed(false); ParIterator const end = const_cast(this)->par_iterator_end(); ParIterator it = const_cast(this)->par_iterator_begin(); diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 250d3aa75d..96aaf7af28 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -687,7 +687,7 @@ string BufferParams::readToken(Lexer & lex, string const & token, istringstream ss(lex.getString()); Author a; ss >> a; - author_map.push_back(pimpl_->authorlist.record(a)); + author_map[a.buffer_id()] = pimpl_->authorlist.record(a); } else if (token == "\\paperorientation") { string orient; lex >> orient; @@ -954,14 +954,7 @@ void BufferParams::writeFile(ostream & os) const os << "\\tracking_changes " << convert(trackChanges) << "\n"; os << "\\output_changes " << convert(outputChanges) << "\n"; - AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin(); - AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end(); - for (; a_it != a_end; ++a_it) { - if (a_it->second.used()) - os << "\\author " << a_it->second << "\n"; - else - os << "\\author " << Author() << "\n"; - } + os << pimpl_->authorlist; } diff --git a/src/BufferParams.h b/src/BufferParams.h index 32ea2e2ae0..8afdd4814f 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -309,8 +309,8 @@ public: AuthorList & authors(); AuthorList const & authors() const; - /// map of the file's author IDs to buffer author IDs - std::vector author_map; + /// map of the file's author IDs to AuthorList indexes + std::map author_map; /// std::string const dvips_options() const; /** The return value of paperSizeName() depends on the diff --git a/src/Changes.cpp b/src/Changes.cpp index 34db257f58..a47de9cbe1 100644 --- a/src/Changes.cpp +++ b/src/Changes.cpp @@ -369,7 +369,7 @@ int Changes::latexMarkChange(odocstream & os, BufferParams const & bparams, } -void Changes::lyxMarkChange(ostream & os, int & column, +void Changes::lyxMarkChange(ostream & os, BufferParams const & bparams, int & column, Change const & old, Change const & change) { if (old == change) @@ -377,19 +377,21 @@ void Changes::lyxMarkChange(ostream & os, int & column, column = 0; + int const buffer_id = bparams.authors().get(change.author).buffer_id(); + switch (change.type) { case Change::UNCHANGED: os << "\n\\change_unchanged\n"; break; case Change::DELETED: { - os << "\n\\change_deleted " << change.author + os << "\n\\change_deleted " << buffer_id << " " << change.changetime << "\n"; break; } case Change::INSERTED: { - os << "\n\\change_inserted " << change.author + os << "\n\\change_inserted " << buffer_id << " " << change.changetime << "\n"; break; } diff --git a/src/Changes.h b/src/Changes.h index 615b1099b1..2183adf040 100644 --- a/src/Changes.h +++ b/src/Changes.h @@ -103,8 +103,8 @@ public: Change const & oldChange, Change const & change); /// output .lyx file format for transitions between changes - static void lyxMarkChange(std::ostream & os, int & column, - Change const & old, Change const & change); + static void lyxMarkChange(std::ostream & os, BufferParams const & bparams, + int & column, Change const & old, Change const & change); /// void checkAuthors(AuthorList const & authorList); diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index b7f7d49c3a..4221b754bf 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -1194,7 +1194,7 @@ void Paragraph::write(ostream & os, BufferParams const & bparams, for (pos_type i = 0; i <= size(); ++i) { Change const change = lookupChange(i); - Changes::lyxMarkChange(os, column, running_change, change); + Changes::lyxMarkChange(os, bparams, column, running_change, change); running_change = change; if (i == size()) diff --git a/src/Text.cpp b/src/Text.cpp index 92619ca71e..91714b4874 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -224,16 +224,17 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex, unsigned int aid; time_t ct; is >> aid >> ct; - if (aid >= bp.author_map.size()) { + map const & am = bp.author_map; + if (am.find(aid) == am.end()) { errorList.push_back(ErrorItem(_("Change tracking error"), bformat(_("Unknown author index for change: %1$d\n"), aid), par.id(), 0, par.size())); change = Change(Change::UNCHANGED); } else { if (token == "\\change_inserted") - change = Change(Change::INSERTED, bp.author_map[aid], ct); - else - change = Change(Change::DELETED, bp.author_map[aid], ct); + change = Change(Change::INSERTED, am.find(aid)->second, ct); + else + change = Change(Change::DELETED, am.find(aid)->second, ct); } } else { lex.eatLine(); -- 2.39.5