mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-25 10:58:52 +00:00
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
This commit is contained in:
parent
e5dfb57a9f
commit
966687d98b
@ -1,6 +1,10 @@
|
||||
LyX file-format changes
|
||||
-----------------------
|
||||
|
||||
2009-07-22 Vincent van Ravesteijn <vfr@lyx.org>
|
||||
* 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 <spitz@lyx.org>, Uwe Stöhr <uwestoehr@web.de>
|
||||
* Format incremented to 368: allow to use glue lengths for
|
||||
horizontal spaces.
|
||||
|
@ -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]],
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "support/lassert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <istream>
|
||||
|
||||
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
|
||||
|
14
src/Author.h
14
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<int, Author> Authors;
|
||||
typedef std::vector<Author> 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_;
|
||||
|
@ -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<string, bool> DepClean;
|
||||
typedef map<docstring, pair<InsetLabel const *, Buffer::References> > 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<Buffer *>(this)->par_iterator_end();
|
||||
ParIterator it = const_cast<Buffer *>(this)->par_iterator_begin();
|
||||
|
@ -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<string>(trackChanges) << "\n";
|
||||
os << "\\output_changes " << convert<string>(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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -309,8 +309,8 @@ public:
|
||||
AuthorList & authors();
|
||||
AuthorList const & authors() const;
|
||||
|
||||
/// map of the file's author IDs to buffer author IDs
|
||||
std::vector<unsigned int> author_map;
|
||||
/// map of the file's author IDs to AuthorList indexes
|
||||
std::map<unsigned int, int> author_map;
|
||||
///
|
||||
std::string const dvips_options() const;
|
||||
/** The return value of paperSizeName() depends on the
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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<unsigned int, int> 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);
|
||||
change = Change(Change::INSERTED, am.find(aid)->second, ct);
|
||||
else
|
||||
change = Change(Change::DELETED, bp.author_map[aid], ct);
|
||||
change = Change(Change::DELETED, am.find(aid)->second, ct);
|
||||
}
|
||||
} else {
|
||||
lex.eatLine();
|
||||
|
Loading…
Reference in New Issue
Block a user