<commit_before>/*  This file is part of the Kate project.
 *
 *  Copyright (C) 2010 <NAME> <<EMAIL>>
 *
 *  Based on code of the SmartCursor/Range by:
 *  Copyright (C) 2003-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 as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  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 "katetextrange.h"
#include "katetextbuffer.h"

namespace Kate {

TextRange::TextRange (TextBuffer &buffer, const KTextEditor::Range &range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior)
  : m_buffer (buffer)
  , m_start (buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert)
  , m_end (buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert)
  , m_view (0)
  , m_feedback (0)
  , m_zDepth (0.0)
  , m_attributeOnlyForViews (false)
  , m_invalidateIfEmpty (emptyBehavior == InvalidateIfEmpty)
{
  // remember this range in buffer
  m_buffer.m_ranges.insert (this);

  // check if range now invalid, there can happen no feedback, as m_feedback == 0
  checkValidity ();
}

TextRange::~TextRange ()
{
  /**
   * reset feedback, don't want feedback during destruction
   */
  m_feedback = 0;

  // remove range from m_ranges
  fixLookup (m_start.line(), m_end.line(), -1, -1);

  // remove this range from the buffer
  m_buffer.m_ranges.remove (this);

  /**
   * trigger update, if we have attribute
   * notify right view
   * here we can ignore feedback, even with feedback, we want none if the range is deleted!
   */
  if (m_attribute)
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */);
}

void TextRange::setInsertBehaviors (InsertBehaviors _insertBehaviors)
{
  /**
   * nothing to do?
   */
  if (_insertBehaviors == insertBehaviors ())
    return;

  /**
   * modify cursors
   */
  m_start.setInsertBehavior ((_insertBehaviors & ExpandLeft) ? KTextEditor::MovingCursor::StayOnInsert : KTextEditor::MovingCursor::MoveOnInsert);
  m_end.setInsertBehavior ((_insertBehaviors & ExpandRight) ? KTextEditor::MovingCursor::MoveOnInsert : KTextEditor::MovingCursor::StayOnInsert);

  /**
   * notify world
   */
  if (m_attribute || m_feedback)
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */);
}

KTextEditor::MovingRange::InsertBehaviors TextRange::insertBehaviors () const
{
  InsertBehaviors behaviors = DoNotExpand;

  if (m_start.insertBehavior() == KTextEditor::MovingCursor::StayOnInsert)
    behaviors = behaviors & ExpandLeft;

  if (m_end.insertBehavior() == KTextEditor::MovingCursor::MoveOnInsert)
    behaviors = behaviors & ExpandRight;

  return behaviors;
}

void TextRange::setEmptyBehavior (EmptyBehavior emptyBehavior)
{
  /**
   * nothing to do?
   */
  if (m_invalidateIfEmpty == (emptyBehavior == InvalidateIfEmpty))
    return;

  /**
   * remember value
   */
  m_invalidateIfEmpty = (emptyBehavior == InvalidateIfEmpty);

  /**
   * invalidate range?
   */
  if (end() <= start())
    setRange (KTextEditor::Range::invalid());
}

void TextRange::setRange (const KTextEditor::Range &range)
{
  // avoid work if nothing changed!
  if (range == toRange())
    return;

  // remember old line range
  int oldStartLine = m_start.line();
  int oldEndLine = m_end.line();

  // change start and end cursor
  m_start.setPosition (range.start ());
  m_end.setPosition (range.end ());

  // check if range now invalid, don't emit feedback here, will be handled below
  // otherwise you can't delete ranges in feedback!
  checkValidity (oldStartLine, oldEndLine, false);

  // no attribute or feedback set, be done
  if (!m_attribute && !m_feedback)
    return;

  // get full range
  int startLineMin = oldStartLine;
  if (oldStartLine == -1 || (m_start.line() != -1 && m_start.line() < oldStartLine))
    startLineMin = m_start.line();

  int endLineMax = oldEndLine;
  if (oldEndLine == -1 || m_end.line() > oldEndLine)
    endLineMax = m_end.line();

  /**
   * notify buffer about attribute change, it will propagate the changes
   * notify right view
   */
  m_buffer.notifyAboutRangeChange (m_view, startLineMin, endLineMax, m_attribute);

  // perhaps need to notify stuff!
  if (m_feedback) {
    // do this last: may delete this range
    if (!toRange().isValid())
      m_feedback->rangeInvalid (this);
    else if (toRange().isEmpty())
      m_feedback->rangeEmpty (this);
  }
}

void TextRange::checkValidity (int oldStartLine, int oldEndLine, bool notifyAboutChange)
{
  /**
   * check if any cursor is invalid or the range is zero size and it should be invalidated then
   */
  if (!m_start.toCursor().isValid() || !m_end.toCursor().isValid() || (m_invalidateIfEmpty && m_end.toCursor() <= m_start.toCursor())) {
    m_start.setPosition (-1, -1);
    m_end.setPosition (-1, -1);
  }

  /**
   * for ranges which are allowed to become empty, normalize them, if the end has moved to the front of the start
   */
  if (!m_invalidateIfEmpty && m_end.toCursor() < m_start.toCursor())
    m_end.setPosition (m_start.toCursor());

  // fix lookup
  fixLookup (oldStartLine, oldEndLine, m_start.line(), m_end.line());

  // perhaps need to notify stuff!
  if (notifyAboutChange && m_feedback) {
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), false /* attribute not interesting here */);

    // do this last: may delete this range
    if (!toRange().isValid())
      m_feedback->rangeInvalid (this);
    else if (toRange().isEmpty())
      m_feedback->rangeEmpty (this);
  }
}

void TextRange::fixLookup (int oldStartLine, int oldEndLine, int startLine, int endLine)
{
  // nothing changed?
  if (oldStartLine == startLine && oldEndLine == endLine)<commit_msg>mwolff:<commit_after>/*  This file is part of the Kate project.
 *
 *  Copyright (C) 2010 <NAME> <<EMAIL>>
 *
 *  Based on code of the SmartCursor/Range by:
 *  Copyright (C) 2003-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 as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  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 "katetextrange.h"
#include "katetextbuffer.h"

namespace Kate {

TextRange::TextRange (TextBuffer &buffer, const KTextEditor::Range &range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior)
  : m_buffer (buffer)
  , m_start (buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert)
  , m_end (buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert)
  , m_view (0)
  , m_feedback (0)
  , m_zDepth (0.0)
  , m_attributeOnlyForViews (false)
  , m_invalidateIfEmpty (emptyBehavior == InvalidateIfEmpty)
{
  // remember this range in buffer
  m_buffer.m_ranges.insert (this);

  // check if range now invalid, there can happen no feedback, as m_feedback == 0
  checkValidity ();
}

TextRange::~TextRange ()
{
  /**
   * reset feedback, don't want feedback during destruction
   */
  m_feedback = 0;

  // remove range from m_ranges
  fixLookup (m_start.line(), m_end.line(), -1, -1);

  // remove this range from the buffer
  m_buffer.m_ranges.remove (this);

  /**
   * trigger update, if we have attribute
   * notify right view
   * here we can ignore feedback, even with feedback, we want none if the range is deleted!
   */
  if (m_attribute)
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */);
}

void TextRange::setInsertBehaviors (InsertBehaviors _insertBehaviors)
{
  /**
   * nothing to do?
   */
  if (_insertBehaviors == insertBehaviors ())
    return;

  /**
   * modify cursors
   */
  m_start.setInsertBehavior ((_insertBehaviors & ExpandLeft) ? KTextEditor::MovingCursor::StayOnInsert : KTextEditor::MovingCursor::MoveOnInsert);
  m_end.setInsertBehavior ((_insertBehaviors & ExpandRight) ? KTextEditor::MovingCursor::MoveOnInsert : KTextEditor::MovingCursor::StayOnInsert);

  /**
   * notify world
   */
  if (m_attribute || m_feedback)
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), true /* we have a attribute */);
}

KTextEditor::MovingRange::InsertBehaviors TextRange::insertBehaviors () const
{
  InsertBehaviors behaviors = DoNotExpand;

  if (m_start.insertBehavior() == KTextEditor::MovingCursor::StayOnInsert)
    behaviors = behaviors & ExpandLeft;

  if (m_end.insertBehavior() == KTextEditor::MovingCursor::MoveOnInsert)
    behaviors = behaviors & ExpandRight;

  return behaviors;
}

void TextRange::setEmptyBehavior (EmptyBehavior emptyBehavior)
{
  /**
   * nothing to do?
   */
  if (m_invalidateIfEmpty == (emptyBehavior == InvalidateIfEmpty))
    return;

  /**
   * remember value
   */
  m_invalidateIfEmpty = (emptyBehavior == InvalidateIfEmpty);

  /**
   * invalidate range?
   */
  if (end() <= start())
    setRange (KTextEditor::Range::invalid());
}

void TextRange::setRange (const KTextEditor::Range &range)
{
  // avoid work if nothing changed!
  if (range == toRange())
    return;

  // remember old line range
  int oldStartLine = m_start.line();
  int oldEndLine = m_end.line();

  // change start and end cursor
  m_start.setPosition (range.start ());
  m_end.setPosition (range.end ());

  // check if range now invalid, don't emit feedback here, will be handled below
  // otherwise you can't delete ranges in feedback!
  checkValidity (oldStartLine, oldEndLine, false);

  // no attribute or feedback set, be done
  if (!m_attribute && !m_feedback)
    return;

  // get full range
  int startLineMin = oldStartLine;
  if (oldStartLine == -1 || (m_start.line() != -1 && m_start.line() < oldStartLine))
    startLineMin = m_start.line();

  int endLineMax = oldEndLine;
  if (oldEndLine == -1 || m_end.line() > oldEndLine)
    endLineMax = m_end.line();

  /**
   * notify buffer about attribute change, it will propagate the changes
   * notify right view
   */
  m_buffer.notifyAboutRangeChange (m_view, startLineMin, endLineMax, m_attribute);

  // perhaps need to notify stuff!
  if (m_feedback) {
    // do this last: may delete this range
    if (!toRange().isValid())
      m_feedback->rangeInvalid (this);
    else if (toRange().isEmpty())
      m_feedback->rangeEmpty (this);
  }
}

void TextRange::checkValidity (int oldStartLine, int oldEndLine, bool notifyAboutChange)
{
  /**
   * check if any cursor is invalid or the range is zero size and it should be invalidated then
   */
  if (!m_start.isValid() || !m_end.isValid() || (m_invalidateIfEmpty && m_end <= m_start)) {
    m_start.setPosition (-1, -1);
    m_end.setPosition (-1, -1);
  }

  /**
   * for ranges which are allowed to become empty, normalize them, if the end has moved to the front of the start
   */
  if (!m_invalidateIfEmpty && m_end < m_start)
    m_end.setPosition (m_start);

  // fix lookup
  fixLookup (oldStartLine, oldEndLine, m_start.line(), m_end.line());

  // perhaps need to notify stuff!
  if (notifyAboutChange && m_feedback) {
    m_buffer.notifyAboutRangeChange (m_view, m_start.line(), m_end.line(), false /* attribute not interesting here */);

    // do this last: may delete this range
    if (!toRange().isValid())
      m_feedback->rangeInvalid (this);
    else if (toRange().isEmpty())
      m_feedback->rangeEmpty (this);
  }
}

void TextRange::fixLookup (int oldStartLine, int oldEndLine, int startLine, int endLine)
{
  // nothing changed?
  if (oldStartLine == startLine && oldEndLine == endLine)
{'fraction_non_alphanumeric': 0.11906427294629673, 'fraction_numerical': 0.006062019118675682, 'mean_word_length': 3.870552611657835, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 25, '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': '6513731', 'n_tokens_mistral': 4104, 'n_tokens_neox': 3969, 'n_words': 1223}