#include "BufferParams.h"
#include "Changes.h"
+#include "Font.h"
#include "insets/InsetText.h"
#include "support/lassert.h"
+#include "support/qstring_helpers.h"
#include <boost/next_prior.hpp>
size_t DocRange::length() const
{
- pit_type startpit = from.pit();
- pit_type endpit = to.pit();
- ParagraphList const & ps_ = from.text()->paragraphs();
-
- ParagraphList pars(boost::next(ps_.begin(), startpit),
- boost::next(ps_.begin(), endpit + 1));
-
- // Remove the end of the last paragraph; afterwards, remove the
- // beginning of the first paragraph.
- Paragraph & back = pars.back();
- back.eraseChars(to.pos(), back.size(), false);
- Paragraph & front = pars.front();
- front.eraseChars(0, from.pos(), false);
-
- ParagraphList::const_iterator pit = pars.begin();
- ParagraphList::const_iterator end_it = pars.end();
-
+ ParagraphList const & ps = from.text()->paragraphs();
size_t length = 0;
- for (; pit != end_it; ++pit)
- length += pit->size() + 1;
-
- // The last paragraph has no paragraph-end
- --length;
- return length;
+ pit_type pit = from.pit();
+ pit_type const endpit = to.pit();
+ for (; pit < endpit; ++pit)
+ length += ps[pit].size() + 1;
+ length += to.pos() - from.pos();
+ return length;
}
Vn_.clear();
}
-
/// Gets the value at index. If it is not in the vector
- /// the default value is returned.
- T & get(int index) {
- if (-index <= int(Vn_.size()) && index < int(Vp_.size()))
- return index >= 0 ? Vp_[index] : Vn_[-index-1];
- else
- return default_;
- }
-
- /// Sets the value at index if it already
- /// is in the vector. Otherwise it will be added to the
- /// end padded with the default value.
- void set(int index, T const & t) {
- if (index >= -int(Vn_.size()) && index < int(Vp_.size())) {
- if (index >= 0)
- Vp_[index] = t;
- else
- Vn_[-index-1] = t;
- } else {
- while (index > int(Vp_.size()))
- Vp_.push_back(default_);
- while (index < -int(Vn_.size()) - 1)
- Vn_.push_back(default_);
-
- if (index >= 0)
- Vp_.push_back(t);
- else
- Vn_.push_back(t);
- }
+ /// the default value is inserted and returned.
+ T & operator[](int index) {
+ vector<T> & V = index >= 0 ? Vp_ : Vn_;
+ unsigned int const ii = index >= 0 ? index : -index - 1;
+ while (ii >= V.size())
+ V.push_back(default_);
+ return V[ii];
}
private:
public:
///
Impl(Compare const & compare)
- : abort_(false), compare_(compare)
+ : abort_(false), compare_(compare), recursion_level_(0), D_(0)
{}
///
/// Set to true to cancel the algorithm
bool abort_;
+ ///
+ QString status() {
+ QString status;
+ status += toqstr("recursion level:") + " " + QString::number(recursion_level_)
+ + " " + toqstr("differences:") + " " + QString::number(D_);
+ return status;
+ }
+
private:
/// Finds the middle snake and returns the length of the
/// shortest edit script.
- int find_middle_snake(DocRangePair const & rp, DocPair & middle_snake);
+ int findMiddleSnake(DocRangePair const & rp, DocPair & middle_snake);
enum SnakeResult {
NoSnake,
/// Retrieve the middle snake when there is overlap between
/// the forward and backward path.
- SnakeResult retrieve_middle_snake(int k, int D, Direction direction,
+ SnakeResult retrieveMiddleSnake(int k, int D, Direction direction,
DocPair & middle_snake);
/// Find the the furthest reaching D-path (number of horizontal
/// and vertical steps; differences between the old and new
/// document) in the k-diagonal (vertical minus horizontal steps).
- void furthest_Dpath_kdiagonal(int D, int k,
+ void furthestDpathKdiagonal(int D, int k,
DocRangePair const & rp, Direction direction);
/// Is there overlap between the forward and backward path
/// Processes the splitted chunks. It either adds them as deleted,
/// as added, or call diff_i for further processing.
- void diff_part(DocRangePair const & rp);
+ void diffPart(DocRangePair const & rp);
/// Runs the algorithm for the inset located at /c it and /c it_n
/// and adds the result to /c pars.
- void diff_inset(Inset * inset, DocPair const & p);
+ void diffInset(Inset * inset, DocPair const & p);
/// Adds the snake to the destination buffer. The algorithm will
/// recursively be applied to any InsetTexts that are within the snake.
- void process_snake(DocRangePair const & rp);
+ void processSnake(DocRangePair const & rp);
/// Writes the range to the destination buffer
void writeToDestBuffer(DocRange const & range,
compl_vector<DocIterator> nrp;
compl_vector<DocIterator> ors;
compl_vector<DocIterator> nrs;
+
+ /// The number of differences in the path the algorithm
+ /// is currently processing.
+ int D_;
};
/////////////////////////////////////////////////////////////////////
: new_buffer(new_buf), old_buffer(old_buf), dest_buffer(dest_buf),
options_(options), pimpl_(new Impl(*this))
{
+ connect(&status_timer_, SIGNAL(timeout()),
+ this, SLOT(doStatusMessage()));
+ status_timer_.start(1000);
+}
+
+
+void Compare::doStatusMessage()
+{
+ statusMessage(pimpl_->status());
}
dest_buffer->params() = options_.settings_from_new
? new_buffer->params() : old_buffer->params();
+ doStatusMessage();
+
// do the real work
if (!doCompare())
return;
}
-static void get_paragraph_list(DocRange const & range,
+static void getParagraphList(DocRange const & range,
ParagraphList & pars)
{
// Clone the paragraphs within the selection.
static bool equal(DocIterator & o, DocIterator & n) {
+ // Explicitly check for this, so we won't call
+ // Paragraph::getChar for the last pos.
+ bool const o_lastpos = o.pos() == o.lastpos();
+ bool const n_lastpos = n.pos() == n.lastpos();
+ if (o_lastpos || n_lastpos)
+ return o_lastpos && n_lastpos;
+
Paragraph const & old_par = o.text()->getPar(o.pit());
Paragraph const & new_par = n.text()->getPar(n.pit());
/// position in the old and new file and they are synchronously
/// moved along the snake. The function returns true if a snake
/// was found.
-static bool traverse_snake(DocPair & p, DocRangePair const & range,
+static bool traverseSnake(DocPair & p, DocRangePair const & range,
Direction direction)
{
bool ret = false;
/////////////////////////////////////////////////////////////////////
-void Compare::Impl::furthest_Dpath_kdiagonal(int D, int k,
+void Compare::Impl::furthestDpathKdiagonal(int D, int k,
DocRangePair const & rp, Direction direction)
{
- compl_vector<DocIterator> * op = direction == Forward ? &ofp : &orp;
- compl_vector<DocIterator> * np = direction == Forward ? &nfp : &nrp;
- compl_vector<DocIterator> * os = direction == Forward ? &ofs : &ors;
- compl_vector<DocIterator> * ns = direction == Forward ? &nfs : &nrs;
+ compl_vector<DocIterator> & op = direction == Forward ? ofp : orp;
+ compl_vector<DocIterator> & np = direction == Forward ? nfp : nrp;
+ compl_vector<DocIterator> & os = direction == Forward ? ofs : ors;
+ compl_vector<DocIterator> & ns = direction == Forward ? nfs : nrs;
// A vertical step means stepping one character in the new document.
bool vertical_step = k == -D;
if (!vertical_step && k != D) {
vertical_step = direction == Forward
- ? op->get(k - 1) < op->get(k + 1)
- : op->get(k - 1) > op->get(k + 1);
+ ? op[k - 1] < op[k + 1] : op[k - 1] > op[k + 1];
}
// Where do we take the step from ?
int const kk = vertical_step ? k + 1 : k - 1;
- DocPair p(op->get(kk), np->get(kk));
+ DocPair p(op[kk], np[kk]);
+ DocPair const s(os[kk], ns[kk]);
// If D==0 we simulate a vertical step from (0,-1) by doing nothing.
if (D != 0) {
}
// Traverse snake
- if (traverse_snake(p, rp, direction)) {
+ if (traverseSnake(p, rp, direction)) {
// Record last snake
- os->set(k, p.o);
- ns->set(k, p.n);
+ os[k] = p.o;
+ ns[k] = p.n;
} else {
// Copy last snake from the previous step
- os->set(k, os->get(kk));
- ns->set(k, ns->get(kk));
+ os[k] = s.o;
+ ns[k] = s.n;
}
//Record new position
- op->set(k, p.o);
- np->set(k, p.n);
+ op[k] = p.o;
+ np[k] = p.n;
}
if (kk <= D && kk >= -D) {
// Do we have overlap ?
if (odd_offset_)
- return ofp.get(k) >= orp.get(kk) && nfp.get(k) >= nrp.get(kk);
+ return ofp[k] >= orp[kk] && nfp[k] >= nrp[kk];
else
- return ofp.get(kk) >= orp.get(k) && nfp.get(kk) >= nrp.get(k);
+ return ofp[kk] >= orp[k] && nfp[kk] >= nrp[k];
}
return false;
}
-Compare::Impl::SnakeResult Compare::Impl::retrieve_middle_snake(
+Compare::Impl::SnakeResult Compare::Impl::retrieveMiddleSnake(
int k, int D, Direction direction, DocPair & middle_snake)
{
- compl_vector<DocIterator> * os = direction == Forward ? &ofs : &ors;
- compl_vector<DocIterator> * ns = direction == Forward ? &nfs : &nrs;
- compl_vector<DocIterator> * os_r = direction == Forward ? &ors : &ofs;
- compl_vector<DocIterator> * ns_r = direction == Forward ? &nrs : &nfs;
+ compl_vector<DocIterator> & os = direction == Forward ? ofs : ors;
+ compl_vector<DocIterator> & ns = direction == Forward ? nfs : nrs;
+ compl_vector<DocIterator> & os_r = direction == Forward ? ors : ofs;
+ compl_vector<DocIterator> & ns_r = direction == Forward ? nrs : nfs;
// The diagonal while doing the backward search
int kk = -k + offset_reverse_diagonal_;
// Did we find a snake ?
- if (os->get(k).empty() && os_r->get(kk).empty()) {
+ if (os[k].empty() && os_r[kk].empty()) {
// No, there is no snake at all, in which case
// the length of the shortest edit script is M+N.
LASSERT(2 * D - odd_offset_ == M_ + N_, /**/);
return NoSnake;
}
- if (os->get(k).empty()) {
+ if (os[k].empty()) {
// Yes, but there is only 1 snake and we found it in the
// reverse path.
- middle_snake.o = os_r->get(kk);
- middle_snake.n = ns_r->get(kk);
+ middle_snake.o = os_r[kk];
+ middle_snake.n = ns_r[kk];
return SingleSnake;
}
- middle_snake.o = os->get(k);
- middle_snake.n = ns->get(k);
+ middle_snake.o = os[k];
+ middle_snake.n = ns[k];
return NormalSnake;
}
-int Compare::Impl::find_middle_snake(DocRangePair const & rp,
+int Compare::Impl::findMiddleSnake(DocRangePair const & rp,
DocPair & middle_snake)
{
// The lengths of the old and new chunks.
ors.reset(DocIterator());
nrs.reset(DocIterator());
+ // In the formula below, the "+ 1" ensures we round like ceil()
+ int const D_max = (M_ + N_ + 1)/2;
// D is the number of horizontal and vertical steps, i.e.
// different characters in the old and new chunk.
- int const D_max = ceil(((double)M_ + N_)/2);
for (int D = 0; D <= D_max; ++D) {
+ // to be used in the status messages
+ D_ = D;
// Forward and reverse paths
for (int f = 0; f < 2; ++f) {
// Diagonals between -D and D can be reached by a D-path
for (int k = -D; k <= D; k += 2) {
// Find the furthest reaching D-path on this diagonal
- furthest_Dpath_kdiagonal(D, k, rp, direction);
+ furthestDpathKdiagonal(D, k, rp, direction);
// Only check for overlap for forward paths if the offset is odd
// and only for reverse paths if the offset is even.
// Do the forward and backward paths overlap ?
if (overlap(k, D - odd_offset_)) {
- retrieve_middle_snake(k, D, direction, middle_snake);
+ retrieveMiddleSnake(k, D, direction, middle_snake);
return 2 * D - odd_offset_;
}
}
+ if (abort_)
+ return 0;
}
}
}
DocRangePair rp(old_buf_, new_buf_);
DocPair from = rp.from();
- traverse_snake(from, rp, Forward);
+ traverseSnake(from, rp, Forward);
DocRangePair const snake(rp.from(), from);
- process_snake(snake);
+ processSnake(snake);
// Start the recursive algorithm
- diff_i(rp);
+ DocRangePair rp_new(from, rp.to());
+ if (!rp_new.o.empty() || !rp_new.n.empty())
+ diff_i(rp_new);
for (pit_type p = 0; p < (pit_type)dest_pars_->size(); ++p) {
(*dest_pars_)[p].setBuffer(const_cast<Buffer &>(*dest_buf));
void Compare::Impl::diff_i(DocRangePair const & rp)
{
+ if (abort_)
+ return;
+
// The middle snake
DocPair middle_snake;
// Divides the problem into two smaller problems, split around
// the snake in the middle.
- int const L_ses = find_middle_snake(rp, middle_snake);
+ int const L_ses = findMiddleSnake(rp, middle_snake);
// Set maximum of progress bar
if (++recursion_level_ == 1)
} else {
// Retrieve the complete snake
DocPair first_part_end = middle_snake;
- traverse_snake(first_part_end, rp, Backward);
+ traverseSnake(first_part_end, rp, Backward);
DocRangePair first_part(rp.from(), first_part_end);
DocPair second_part_begin = middle_snake;
- traverse_snake(second_part_begin, rp, Forward);
+ traverseSnake(second_part_begin, rp, Forward);
DocRangePair second_part(second_part_begin, rp.to());
// Split the string in three parts:
// 1. in front of the snake
- diff_part(first_part);
+ diffPart(first_part);
// 2. the snake itself, and
DocRangePair const snake(first_part.to(), second_part.from());
- process_snake(snake);
+ processSnake(snake);
// 3. behind the snake.
- diff_part(second_part);
+ diffPart(second_part);
}
--recursion_level_;
}
-void Compare::Impl::diff_part(DocRangePair const & rp)
+void Compare::Impl::diffPart(DocRangePair const & rp)
{
// Is there a finite length string in both buffers, if not there
// is an empty string and we write the other one to the buffer.
}
-void Compare::Impl::diff_inset(Inset * inset, DocPair const & p)
+void Compare::Impl::diffInset(Inset * inset, DocPair const & p)
{
// Find the dociterators for the beginning and the
// end of the inset, for the old and new document.
}
-void Compare::Impl::process_snake(DocRangePair const & rp)
+void Compare::Impl::processSnake(DocRangePair const & rp)
{
ParagraphList pars;
- get_paragraph_list(rp.o, pars);
+ getParagraphList(rp.o, pars);
// Find insets in this paragaph list
DocPair it = rp.from();
pos_type const pos = pit ? it.o.pos() : it.o.pos() - rp.o.from.pos();
inset = pars[pit].getInset(pos);
LASSERT(inset, /**/);
- diff_inset(inset, it);
+ diffInset(inset, it);
}
}
writeToDestBuffer(pars);
Change::Type type)
{
ParagraphList pars;
- get_paragraph_list(range, pars);
+ getParagraphList(range, pars);
pos_type size = 0;