<commit_before>/* This file is part of the KDE libraries
   Copyright (C) 2005 <NAME> <<EMAIL>>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/
#include "katesmartmanager.h"

#include "katedocument.h"
#include "kateedit.h"
#include "katesmartcursor.h"
#include "katesmartrange.h"

#include <QThread>
#include <QMutexLocker>

#include <kdebug.h>


static QThreadStorage<QMap<KateSmartManager*,int> *> m_usingRevision;

//Uncomment this to debug the translation of ranges. If that is enabled,
//all ranges are first translated completely separately out of the translation process,
//and at the end the result is compared. If the result mismatches, an assertion is triggered.
// #define DEBUG_TRANSLATION

static const int s_defaultGroupSize = 40;
static const int s_minimumGroupSize = 20;
static const int s_maximumGroupSize = 60;

using namespace KTextEditor;

static void translate(KateEditInfo* edit, Cursor& ret, SmartCursor::InsertBehavior insertBehavior)
{
  // NOTE: copied from KateSmartCursor::translate()
  // If this cursor is before the edit, no action is required
  if (ret < edit->start())
    return;

  // If this cursor is on a line affected by the edit
  if (edit->oldRange().overlapsLine(ret.line())) {
    // If this cursor is at the start of the edit
    if (ret == edit->start()) {
      // And it doesn't need to move, no action is required
      if (insertBehavior == SmartCursor::StayOnInsert)
        return;
    }

    // Calculate the new position
    Cursor newPos;
    if (edit->oldRange().contains(ret)) {
      if (insertBehavior == SmartCursor::MoveOnInsert)
        ret = edit->newRange().end();
      else
        ret = edit->start();

    } else {
      ret += edit->translate();
    }

    return;
  }

  // just need to adjust line number
  ret.setLine(ret.line() + edit->translate().line());
}

#ifdef DEBUG_TRANSLATION
struct KateSmartManager::KateTranslationDebugger {
  KateTranslationDebugger(KateSmartManager* manager, KateEditInfo* edit) : m_manager(manager), m_edit(edit) {
    manager->m_currentKateTranslationDebugger = this;
    foreach(KateSmartRange* range, manager->m_topRanges)
      addRange(range);
  }

  ~KateTranslationDebugger() {
    m_manager->m_currentKateTranslationDebugger = 0;
  }

  void addRange(SmartRange* _range) {

    KateSmartRange* range = dynamic_cast<KateSmartRange*>(_range);
    Q_ASSERT(range);

    RangeTranslation translation;
    translation.from = *range;
    KTextEditor::Cursor toStart = range->start();
    KTextEditor::Cursor toEnd = range->end();

    translate(m_edit, toStart, (range->insertBehavior() & SmartRange::ExpandLeft) ? SmartCursor::StayOnInsert : SmartCursor::MoveOnInsert);
    translate(m_edit, toEnd, (range->insertBehavior() & SmartRange::ExpandRight) ? SmartCursor::MoveOnInsert : SmartCursor::StayOnInsert);
    translation.to = KTextEditor::Range(toStart, toEnd);

    m_rangeTranslations[range] = translation;
    m_cursorTranslations[&range->smartStart()] = CursorTranslation(range->start(), toStart);
    m_cursorTranslations[&range->smartEnd()] = CursorTranslation(range->end(), toEnd);

    foreach(SmartRange* child, range->childRanges())
      addRange(child);
  }

  void verifyAll() {
    for(QMap<const SmartRange*, RangeTranslation>::iterator it = m_rangeTranslations.begin(); it != m_rangeTranslations.end(); ++it) {
      if(*it.key() != it.value().to) {
    kDebug() << "mismatch. Translation should be:" << it.value().to << "translation is:" << *it.key() << "from:" << it.value().from;
      kDebug() << "edit:" << m_edit->oldRange() << m_edit->newRange();
    Q_ASSERT(0);
      }
    }
  }

  void verifyChange(SmartCursor* _cursor) {
    if(!m_cursorTranslations.contains(_cursor))
      return;
    return;
    KateSmartCursor* cursor = dynamic_cast<KateSmartCursor*>(_cursor);
    Q_ASSERT(cursor);
    KTextEditor::Cursor realPos( cursor->line() + cursor->smartGroup()->newStartLine(), cursor->column());
    kDebug() << "changing cursor from" << m_cursorTranslations[cursor].from << "to" << realPos;

    if(m_cursorTranslations[cursor].to != realPos) {
      kDebug() << "mismatch. Translation of cursor should be:" << m_cursorTranslations[cursor].to << "is:" << realPos << "from:" << m_cursorTranslations[cursor].from;
      kDebug() << "edit:" << m_edit->oldRange() << m_edit->newRange();
      Q_ASSERT(0);
    }
  }

  struct RangeTranslation {
    KTextEditor::Range from, to;
  };

  struct CursorTranslation {
    CursorTranslation() {
    }
    CursorTranslation(const KTextEditor::Cursor& _from, const KTextEditor::Cursor& _to) : from(_from), to(_to) {
    }
    KTextEditor::Cursor from, to;
  };

  QMap<const SmartRange*, RangeTranslation> m_rangeTranslations;
  QMap<const SmartCursor*, CursorTranslation> m_cursorTranslations;
  KateSmartManager* m_manager;
  KateEditInfo* m_edit;
};
#endif
KateSmartManager::KateSmartManager(KateDocument* parent)
  : QObject(parent)
  , m_firstGroup(new KateSmartGroup(0, 0, 0L, 0L))
  , m_invalidGroup(new KateSmartGroup(-1, -1, 0L, 0L))
  , m_clearing(false)
  , m_currentKateTranslationDebugger(0)
{
  connect(doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(slotTextChanged(KateEditInfo*)));
  //connect(doc(), SIGNAL(textChanged(Document*)), SLOT(verifyCorrect()));<commit_msg>make debug messages in the KateTranslationDebugger warnings, so they are shown before the assert even without the enabled debug area<commit_after>/* This file is part of the KDE libraries
   Copyright (C) 2005 <NAME> <<EMAIL>>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/
#include "katesmartmanager.h"

#include "katedocument.h"
#include "kateedit.h"
#include "katesmartcursor.h"
#include "katesmartrange.h"

#include <QThread>
#include <QMutexLocker>

#include <kdebug.h>


static QThreadStorage<QMap<KateSmartManager*,int> *> m_usingRevision;

//Uncomment this to debug the translation of ranges. If that is enabled,
//all ranges are first translated completely separately out of the translation process,
//and at the end the result is compared. If the result mismatches, an assertion is triggered.
// #define DEBUG_TRANSLATION

static const int s_defaultGroupSize = 40;
static const int s_minimumGroupSize = 20;
static const int s_maximumGroupSize = 60;

using namespace KTextEditor;

static void translate(KateEditInfo* edit, Cursor& ret, SmartCursor::InsertBehavior insertBehavior)
{
  // NOTE: copied from KateSmartCursor::translate()
  // If this cursor is before the edit, no action is required
  if (ret < edit->start())
    return;

  // If this cursor is on a line affected by the edit
  if (edit->oldRange().overlapsLine(ret.line())) {
    // If this cursor is at the start of the edit
    if (ret == edit->start()) {
      // And it doesn't need to move, no action is required
      if (insertBehavior == SmartCursor::StayOnInsert)
        return;
    }

    // Calculate the new position
    Cursor newPos;
    if (edit->oldRange().contains(ret)) {
      if (insertBehavior == SmartCursor::MoveOnInsert)
        ret = edit->newRange().end();
      else
        ret = edit->start();

    } else {
      ret += edit->translate();
    }

    return;
  }

  // just need to adjust line number
  ret.setLine(ret.line() + edit->translate().line());
}

#ifdef DEBUG_TRANSLATION
struct KateSmartManager::KateTranslationDebugger {
  KateTranslationDebugger(KateSmartManager* manager, KateEditInfo* edit) : m_manager(manager), m_edit(edit) {
    manager->m_currentKateTranslationDebugger = this;
    foreach(KateSmartRange* range, manager->m_topRanges)
      addRange(range);
  }

  ~KateTranslationDebugger() {
    m_manager->m_currentKateTranslationDebugger = 0;
  }

  void addRange(SmartRange* _range) {

    KateSmartRange* range = dynamic_cast<KateSmartRange*>(_range);
    Q_ASSERT(range);

    RangeTranslation translation;
    translation.from = *range;
    KTextEditor::Cursor toStart = range->start();
    KTextEditor::Cursor toEnd = range->end();

    translate(m_edit, toStart, (range->insertBehavior() & SmartRange::ExpandLeft) ? SmartCursor::StayOnInsert : SmartCursor::MoveOnInsert);
    translate(m_edit, toEnd, (range->insertBehavior() & SmartRange::ExpandRight) ? SmartCursor::MoveOnInsert : SmartCursor::StayOnInsert);
    translation.to = KTextEditor::Range(toStart, toEnd);

    m_rangeTranslations[range] = translation;
    m_cursorTranslations[&range->smartStart()] = CursorTranslation(range->start(), toStart);
    m_cursorTranslations[&range->smartEnd()] = CursorTranslation(range->end(), toEnd);

    foreach(SmartRange* child, range->childRanges())
      addRange(child);
  }

  void verifyAll() {
    for(QMap<const SmartRange*, RangeTranslation>::iterator it = m_rangeTranslations.begin(); it != m_rangeTranslations.end(); ++it) {
      if(*it.key() != it.value().to) {
        kWarning() << "mismatch. Translation should be:" << it.value().to << "translation is:" << *it.key() << "from:" << it.value().from;
        kWarning() << "edit:" << m_edit->oldRange() << m_edit->newRange();
        Q_ASSERT(0);
      }
    }
  }

  void verifyChange(SmartCursor* _cursor) {
    if(!m_cursorTranslations.contains(_cursor))
      return;
    return;
    KateSmartCursor* cursor = dynamic_cast<KateSmartCursor*>(_cursor);
    Q_ASSERT(cursor);
    KTextEditor::Cursor realPos( cursor->line() + cursor->smartGroup()->newStartLine(), cursor->column());
    kDebug() << "changing cursor from" << m_cursorTranslations[cursor].from << "to" << realPos;

    if(m_cursorTranslations[cursor].to != realPos) {
      kWarning() << "mismatch. Translation of cursor should be:" << m_cursorTranslations[cursor].to << "is:" << realPos << "from:" << m_cursorTranslations[cursor].from;
      kWarning() << "edit:" << m_edit->oldRange() << m_edit->newRange();
      Q_ASSERT(0);
    }
  }

  struct RangeTranslation {
    KTextEditor::Range from, to;
  };

  struct CursorTranslation {
    CursorTranslation() {
    }
    CursorTranslation(const KTextEditor::Cursor& _from, const KTextEditor::Cursor& _to) : from(_from), to(_to) {
    }
    KTextEditor::Cursor from, to;
  };

  QMap<const SmartRange*, RangeTranslation> m_rangeTranslations;
  QMap<const SmartCursor*, CursorTranslation> m_cursorTranslations;
  KateSmartManager* m_manager;
  KateEditInfo* m_edit;
};
#endif
KateSmartManager::KateSmartManager(KateDocument* parent)
  : QObject(parent)
  , m_firstGroup(new KateSmartGroup(0, 0, 0L, 0L))
  , m_invalidGroup(new KateSmartGroup(-1, -1, 0L, 0L))
  , m_clearing(false)
  , m_currentKateTranslationDebugger(0)
{
  connect(doc()->history(), SIGNAL(editDone(KateEditInfo*)), SLOT(slotTextChanged(KateEditInfo*)));
  //connect(doc(), SIGNAL(textChanged(Document*)), SLOT(verifyCorrect()));
{'fraction_non_alphanumeric': 0.1248020008336807, 'fraction_numerical': 0.00566902876198416, 'mean_word_length': 4.331555555555555, 'pattern_counts': {'":': 0, '<': 119, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2990905', 'n_tokens_mistral': 3723, 'n_tokens_neox': 3539, 'n_words': 982}