Run updateBuffer when adding/merging changes

Following 4a4ded22, the enabling of some change-related functions is
handled in updateBuffer. However, this method is not ran at every
document change for performance reasons.

This patch adds code to every place that modifies
Paragraph::Private::changes_ that checks whether the `changedness' of
the paragraph, err... changes.

To this end, a new helper struct is introduced that remembers
paragraph state at contruction time, and compares it to new state in
the destructor.

New forceUpdate/needUpdate methods are added to Buffer class, since
the cursor is in general not available in the places where these
changes are made.

Fixes bug #12074.
This commit is contained in:
Jean-Marc Lasgouttes 2021-01-28 10:10:18 +01:00
parent e6bc78d9e8
commit f3a0e8ff9a
4 changed files with 91 additions and 3 deletions

View File

@ -354,6 +354,9 @@ public:
/// whether the bibinfo cache is valid
mutable bool bibinfo_cache_valid_;
///
mutable bool need_update;
private:
int word_count_;
int char_count_;
@ -460,7 +463,7 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_,
internal_buffer(false), read_only(readonly_), file_fully_loaded(false),
need_format_backup(false), ignore_parent(false), macro_lock(false),
externally_modified_(false), bibinfo_cache_valid_(false),
word_count_(0), char_count_(0), blank_count_(0)
need_update(false), word_count_(0), char_count_(0), blank_count_(0)
{
refreshFileMonitor();
if (!cloned_buffer_) {
@ -5286,6 +5289,18 @@ void Buffer::updateBuffer(ParIterator & parit, UpdateType utype, bool const dele
}
void Buffer::forceUpdate() const
{
d->need_update = true;
}
bool Buffer::needUpdate() const
{
return d->need_update;
}
int Buffer::spellCheck(DocIterator & from, DocIterator & to,
WordLangTuple & word_lang, docstring_list & suggestions) const
{

View File

@ -735,6 +735,10 @@ public:
void updateBuffer(UpdateScope scope, UpdateType utype) const;
///
void updateBuffer(ParIterator & parit, UpdateType utype, bool const deleted = false) const;
/// Forces an updateBuffer() call
void forceUpdate() const;
/// Do we need to call updateBuffer()?
bool needUpdate() const;
/// Spellcheck starting from \p from.
/// \p from initial position, will then points to the next misspelled
@ -771,7 +775,7 @@ public:
int wordCount() const;
int charCount(bool with_blanks) const;
/// FIXME: dummy function for now
///
bool areChangesPresent() const;
///

View File

@ -2417,7 +2417,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
// Do we have a selection?
theSelection().haveSelection(cursor().selection());
if (cur.needBufferUpdate()) {
if (cur.needBufferUpdate() || buffer().needUpdate()) {
cur.clearBufferUpdate();
buffer().updateBuffer();
}

View File

@ -564,6 +564,48 @@ Paragraph::Private::Private(Private const & p, Paragraph * owner,
}
/////////////////////////////////////////////////////////////////////
//
// Paragraph
//
/////////////////////////////////////////////////////////////////////
namespace {
/** This helper class should be instantiated at the start of methods
* that can create or merge changes. If as a result the value of
* Paragraph::isChanged is modified, it makes sure that updateBuffer()
* will be run.
*/
struct ChangesMonitor {
///
ChangesMonitor(Paragraph & par)
: par_(par), was_changed_(par.isChanged()) {}
///
~ChangesMonitor()
{
/* We may need to run updateBuffer to check whether the buffer
* contains changes (and toggle the changes toolbar). We do it
* when:
* 1. the `changedness' of the paragraph has changed,
* 2. and we are not in the situation where the buffer has changes
* and new changes are added to the paragraph.
*/
if (par_.isChanged() != was_changed_
&& par_.inInset().isBufferValid()
&& !(par_.inInset().buffer().areChangesPresent() && par_.isChanged()))
par_.inInset().buffer().forceUpdate();
}
private:
///
Paragraph const & par_;
///
bool was_changed_;
};
}
void Paragraph::addChangesToToc(DocIterator const & cdit, Buffer const & buf,
bool output_active, TocBackend & backend) const
{
@ -629,6 +671,9 @@ Change Paragraph::parEndChange() const
void Paragraph::setChange(Change const & change)
{
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
// beware of the imaginary end-of-par character!
d->changes_.set(change, 0, size() + 1);
@ -655,6 +700,9 @@ void Paragraph::setChange(Change const & change)
void Paragraph::setChange(pos_type pos, Change const & change)
{
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
LASSERT(pos >= 0 && pos <= size(), return);
d->changes_.set(change, pos);
@ -674,6 +722,9 @@ Change const & Paragraph::lookupChange(pos_type pos) const
void Paragraph::acceptChanges(pos_type start, pos_type end)
{
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
LASSERT(start >= 0 && start <= size(), return);
LASSERT(end > start && end <= size() + 1, return);
@ -712,6 +763,9 @@ void Paragraph::rejectChanges(pos_type start, pos_type end)
LASSERT(start >= 0 && start <= size(), return);
LASSERT(end > start && end <= size() + 1, return);
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
for (pos_type pos = start; pos < end; ++pos) {
switch (lookupChange(pos).type) {
case Change::UNCHANGED:
@ -747,6 +801,9 @@ void Paragraph::Private::insertChar(pos_type pos, char_type c,
{
LASSERT(pos >= 0 && pos <= int(text_.size()), return);
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*owner_);
// track change
changes_.insert(change, pos);
@ -779,6 +836,9 @@ bool Paragraph::insertInset(pos_type pos, Inset * inset,
LASSERT(inset, return false);
LASSERT(pos >= 0 && pos <= size(), return false);
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
// Paragraph::insertInset() can be used in cut/copy/paste operation where
// d->inset_owner_ is not set yet.
if (d->inset_owner_ && !d->inset_owner_->insetAllowed(inset->lyxCode()))
@ -801,6 +861,9 @@ bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
{
LASSERT(pos >= 0 && pos <= size(), return false);
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
// keep the logic here in sync with the logic of isMergedOnEndOfParDeletion()
if (trackChanges) {
@ -1707,6 +1770,9 @@ void Paragraph::insert(pos_type pos, docstring const & str,
void Paragraph::appendChar(char_type c, Font const & font,
Change const & change)
{
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
// track change
d->changes_.insert(change, d->text_.size());
// when appending characters, no need to update tables
@ -1719,6 +1785,9 @@ void Paragraph::appendChar(char_type c, Font const & font,
void Paragraph::appendString(docstring const & s, Font const & font,
Change const & change)
{
// Make sure that Buffer::hasChangesPresent is updated
ChangesMonitor cm(*this);
pos_type end = s.size();
size_t oldsize = d->text_.size();
size_t newsize = oldsize + end;