# Path: meld/meldbuffer.py
# class BufferLines:
#     """Gtk.TextBuffer shim with line-based access and optional filtering
# 
#     This class allows a Gtk.TextBuffer to be treated as a list of lines of
#     possibly-filtered text. If no filter is given, the raw output from the
#     Gtk.TextBuffer is used.
#     """
# 
#     #: Cached copy of the (possibly filtered) text in a single line,
#     #: where an entry of None indicates that there is no cached result
#     #: available.
#     lines: List[Optional[str]]
# 
#     def __init__(self, buf, textfilter=None, *, cache_debug: bool = False):
#         self.buf = buf
#         if textfilter is not None:
#             self.textfilter = textfilter
#         else:
#             self.textfilter = lambda x, buf, start_iter, end_iter: x
# 
#         self.lines = [None] * self.buf.get_line_count()
#         self.mark = buf.create_mark(
#             "bufferlines-insert", buf.get_start_iter(), True,
#         )
# 
#         buf.connect("insert-text", self.on_insert_text),
#         buf.connect("delete-range", self.on_delete_range),
#         buf.connect_after("insert-text", self.after_insert_text),
#         if cache_debug:
#             buf.connect_after("insert-text", self._check_cache_invariant)
#             buf.connect_after("delete-range", self._check_cache_invariant)
# 
#     def _check_cache_invariant(self, *args: Any) -> None:
#         if len(self.lines) != len(self):
#             log.error(
#                 "Cache line count does not match buffer line count: "
#                 f"{len(self.lines)} != {len(self)}",
#             )
# 
#     def on_insert_text(self, buf, it, text, textlen):
#         buf.move_mark(self.mark, it)
# 
#     def after_insert_text(self, buf, it, newtext, textlen):
#         start_idx = buf.get_iter_at_mark(self.mark).get_line()
#         end_idx = it.get_line() + 1
#         # Replace the insertion-point cache line with a list of empty
#         # lines. In the single-line case this will be a single element
#         # substitution; for multi-line inserts, we will replace the
#         # single insertion point line with several empty cache lines.
#         self.lines[start_idx:start_idx + 1] = [None] * (end_idx - start_idx)
# 
#     def on_delete_range(self, buf, it0, it1):
#         start_idx = it0.get_line()
#         end_idx = it1.get_line() + 1
#         self.lines[start_idx:end_idx] = [None]
# 
#     def __getitem__(self, key):
#         if isinstance(key, slice):
#             lo, hi, _ = key.indices(self.buf.get_line_count())
# 
#             for idx in range(lo, hi):
#                 if self.lines[idx] is None:
#                     self.lines[idx] = self[idx]
# 
#             return self.lines[lo:hi]
# 
#         elif isinstance(key, int):
#             if key >= len(self):
#                 raise IndexError
# 
#             if self.lines[key] is None:
#                 line_start = self.buf.get_iter_at_line_or_eof(key)
#                 line_end = line_start.copy()
#                 if not line_end.ends_line():
#                     line_end.forward_to_line_end()
#                 txt = self.buf.get_text(line_start, line_end, False)
#                 txt = self.textfilter(txt, self.buf, line_start, line_end)
#                 self.lines[key] = txt
# 
#             return self.lines[key]
# 
#     def __len__(self):
#         return self.buf.get_line_count()
# 
# class MeldBuffer(GtkSource.Buffer):
# 
#     __gtype_name__ = "MeldBuffer"
# 
#     __gsettings_bindings__ = (
#         ('highlight-syntax', 'highlight-syntax'),
#     )
# 
#     def __init__(self):
#         super().__init__()
#         bind_settings(self)
#         self.data = MeldBufferData()
#         self.undo_sequence = None
# 
#     def do_begin_user_action(self, *args):
#         if self.undo_sequence:
#             self.undo_sequence.begin_group()
# 
#     def do_end_user_action(self, *args):
#         if self.undo_sequence:
#             self.undo_sequence.end_group()
# 
#     def get_iter_at_line_or_eof(self, line):
#         """Return a Gtk.TextIter at the given line, or the end of the buffer.
# 
#         This method is like get_iter_at_line, but if asked for a position past
#         the end of the buffer, this returns the end of the buffer; the
#         get_iter_at_line behaviour is to return the start of the last line in
#         the buffer.
#         """
#         if line >= self.get_line_count():
#             return self.get_end_iter()
#         return self.get_iter_at_line(line)
# 
#     def insert_at_line(self, line, text):
#         """Insert text at the given line, or the end of the buffer.
# 
#         This method is like insert, but if asked to insert something past the
#         last line in the buffer, this will insert at the end, and will add a
#         linebreak before the inserted text. The last line in a Gtk.TextBuffer
#         is guaranteed never to have a newline, so we need to handle this.
#         """
#         if line >= self.get_line_count():
#             # TODO: We need to insert a linebreak here, but there is no
#             # way to be certain what kind of linebreak to use.
#             text = "\n" + text
#         it = self.get_iter_at_line_or_eof(line)
#         self.insert(it, text)
#         return it


from unittest import mock
from meld.meldbuffer import BufferLines, MeldBuffer
import pytest




text = ("""0
1
2
3
4
5
6
7
8
9
10""")


@pytest.fixture(scope='module', autouse=True)
def mock_bind_settings():
    with mock.patch('meld.meldbuffer.bind_settings', mock.DEFAULT):
        yield


@pytest.fixture
def buffer_setup():
    buf = MeldBuffer()
    buf.set_text(text)

# Path: meld/meldbuffer.py
# class BufferLines:
#     """Gtk.TextBuffer shim with line-based access and optional filtering
# 
#     This class allows a Gtk.TextBuffer to be treated as a list of lines of
#     possibly-filtered text. If no filter is given, the raw output from the
#     Gtk.TextBuffer is used.
#     """
# 
#     #: Cached copy of the (possibly filtered) text in a single line,
#     #: where an entry of None indicates that there is no cached result
#     #: available.
#     lines: List[Optional[str]]
# 
#     def __init__(self, buf, textfilter=None, *, cache_debug: bool = False):
#         self.buf = buf
#         if textfilter is not None:
#             self.textfilter = textfilter
#         else:
#             self.textfilter = lambda x, buf, start_iter, end_iter: x
# 
#         self.lines = [None] * self.buf.get_line_count()
#         self.mark = buf.create_mark(
#             "bufferlines-insert", buf.get_start_iter(), True,
#         )
# 
#         buf.connect("insert-text", self.on_insert_text),
#         buf.connect("delete-range", self.on_delete_range),
#         buf.connect_after("insert-text", self.after_insert_text),
#         if cache_debug:
#             buf.connect_after("insert-text", self._check_cache_invariant)
#             buf.connect_after("delete-range", self._check_cache_invariant)
# 
#     def _check_cache_invariant(self, *args: Any) -> None:
#         if len(self.lines) != len(self):
#             log.error(
#                 "Cache line count does not match buffer line count: "
#                 f"{len(self.lines)} != {len(self)}",
#             )
# 
#     def on_insert_text(self, buf, it, text, textlen):
#         buf.move_mark(self.mark, it)
# 
#     def after_insert_text(self, buf, it, newtext, textlen):
#         start_idx = buf.get_iter_at_mark(self.mark).get_line()
#         end_idx = it.get_line() + 1
#         # Replace the insertion-point cache line with a list of empty
#         # lines. In the single-line case this will be a single element
#         # substitution; for multi-line inserts, we will replace the
#         # single insertion point line with several empty cache lines.
#         self.lines[start_idx:start_idx + 1] = [None] * (end_idx - start_idx)
# 
#     def on_delete_range(self, buf, it0, it1):
#         start_idx = it0.get_line()
#         end_idx = it1.get_line() + 1
#         self.lines[start_idx:end_idx] = [None]
# 
#     def __getitem__(self, key):
#         if isinstance(key, slice):
#             lo, hi, _ = key.indices(self.buf.get_line_count())
# 
#             for idx in range(lo, hi):
#                 if self.lines[idx] is None:
#                     self.lines[idx] = self[idx]
# 
#             return self.lines[lo:hi]
# 
#         elif isinstance(key, int):
#             if key >= len(self):
#                 raise IndexError
# 
#             if self.lines[key] is None:
#                 line_start = self.buf.get_iter_at_line_or_eof(key)
#                 line_end = line_start.copy()
#                 if not line_end.ends_line():
#                     line_end.forward_to_line_end()
#                 txt = self.buf.get_text(line_start, line_end, False)
#                 txt = self.textfilter(txt, self.buf, line_start, line_end)
#                 self.lines[key] = txt
# 
#             return self.lines[key]
# 
#     def __len__(self):
#         return self.buf.get_line_count()
# 
# class MeldBuffer(GtkSource.Buffer):
# 
#     __gtype_name__ = "MeldBuffer"
# 
#     __gsettings_bindings__ = (
#         ('highlight-syntax', 'highlight-syntax'),
#     )
# 
#     def __init__(self):
#         super().__init__()
#         bind_settings(self)
#         self.data = MeldBufferData()
#         self.undo_sequence = None
# 
#     def do_begin_user_action(self, *args):
#         if self.undo_sequence:
#             self.undo_sequence.begin_group()
# 
#     def do_end_user_action(self, *args):
#         if self.undo_sequence:
#             self.undo_sequence.end_group()
# 
#     def get_iter_at_line_or_eof(self, line):
#         """Return a Gtk.TextIter at the given line, or the end of the buffer.
# 
#         This method is like get_iter_at_line, but if asked for a position past
#         the end of the buffer, this returns the end of the buffer; the
#         get_iter_at_line behaviour is to return the start of the last line in
#         the buffer.
#         """
#         if line >= self.get_line_count():
#             return self.get_end_iter()
#         return self.get_iter_at_line(line)
# 
#     def insert_at_line(self, line, text):
#         """Insert text at the given line, or the end of the buffer.
# 
#         This method is like insert, but if asked to insert something past the
#         last line in the buffer, this will insert at the end, and will add a
#         linebreak before the inserted text. The last line in a Gtk.TextBuffer
#         is guaranteed never to have a newline, so we need to handle this.
#         """
#         if line >= self.get_line_count():
#             # TODO: We need to insert a linebreak here, but there is no
#             # way to be certain what kind of linebreak to use.
#             text = "\n" + text
#         it = self.get_iter_at_line_or_eof(line)
#         self.insert(it, text)
#         return it


# Path: test/test_buffer_lines.py
from unittest import mock
from meld.meldbuffer import BufferLines, MeldBuffer
import pytest





text = ("""0
1
2
3
4
5
6
7
8
9
10""")


@pytest.fixture(scope='module', autouse=True)
def mock_bind_settings():
    with mock.patch('meld.meldbuffer.bind_settings', mock.DEFAULT):
        yield


@pytest.fixture
def buffer_setup():
    buf = MeldBuffer()
    buf.set_text(text)

    buffer_lines = BufferLines(buf)