class_docstr: Read notebook cells from Markdown documents
class_docstr_tok: ["Read","notebook","cells","from","Markdown","documents"]
docstr: Return position of end of cell marker, and position
  of first line after cell
docstr_tok: ["Return","position","of","end","of","cell","marker",",","and","position","of","first","line","after","cell"]
code: def find_cell_end(self, lines):
      """Return position of end of cell marker, and position
      of first line after cell"""
      if self.in_region:
          for i, line in enumerate(lines):
              if self.end_region_re.match(line):
                  return i, i + 1, True
      elif self.metadata is None:
          # default markdown: (last) two consecutive blank lines, except when in code blocks
          self.cell_type = "markdown"
          prev_blank = 0
          in_explicit_code_block = False
          in_indented_code_block = False
  
          for i, line in enumerate(lines):
              if in_explicit_code_block and self.end_code_re.match(
                  line
              ):
                  in_explicit_code_block = False
                  continue
  
              if (
                  prev_blank
                  and line.startswith("    ")
                  and not _BLANK_LINE.match(line)
              ):
                  in_indented_code_block = True
                  prev_blank = 0
                  continue
  
              if (
                  in_indented_code_block
                  and not _BLANK_LINE.match(line)
                  and not line.startswith("    ")
              ):
                  in_indented_code_block = False
  
              if in_indented_code_block or in_explicit_code_block:
                  continue
  
              if self.start_region_re.match(line):
                  if i > 1 and prev_blank:
                      return i - 1, i, False
                  return i, i, False
  
              if self.start_code_re.match(line):
                  if line.startswith("```{bibliography}"):
                      in_explicit_code_block = True
                      prev_blank = 0
                      continue
  
                  # Cells with a .noeval attribute are markdown cells #347
                  _, metadata = self.options_to_metadata(
                      self.start_code_re.findall(line)[0]
                  )
                  if metadata.get(".noeval", "") is None:
                      in_explicit_code_block = True
                      prev_blank = 0
                      continue
  
                  if i > 1 and prev_blank:
                      return i - 1, i, False
                  return i, i, False
  
              if self.non_jupyter_code_re.match(line):
                  if prev_blank >= 2:
                      return i - 2, i, True
                  in_explicit_code_block = True
                  prev_blank = 0
                  continue
  
              if (
                  self.split_at_heading
                  and line.startswith("#")
                  and prev_blank >= 1
              ):
                  return i - 1, i, False
  
              if _BLANK_LINE.match(lines[i]):
                  prev_blank += 1
              elif prev_blank >= 2:
                  return i - 2, i, True
              else:
                  prev_blank = 0
      else:
          self.cell_type = "code"
          # At some point we could remove the below, in which we make sure not to break language strings
          # into multiple cells (#419). Indeed, now that the markdown cell uses one extra backtick (#712)
          # we should not have the issue any more
          parser = StringParser(self.language or self.default_language)
          for i, line in enumerate(lines):
              # skip cell header
              if i == 0:
                  continue
  
              if parser.is_quoted():
                  parser.read_line(line)
                  continue
  
              parser.read_line(line)
              if self.end_code_re.match(line):
                  return i, i + 1, True
  
      # End not found
      return len(lines), len(lines), False
  
imports: import re
  import warnings
  from copy import copy
  from nbformat.v4.nbbase import new_code_cell, new_markdown_cell, new_raw_cell
  from packaging.version import parse
  from .doxygen import doxygen_to_markdown
  from .languages import _SCRIPT_EXTENSIONS
  from .cell_metadata import is_active, is_json_metadata, rmd_options_to_metadata, text_to_metadata
  from .languages import _JUPYTER_LANGUAGES_LOWER_AND_UPPER
  from .magics import is_magic, need_explicit_marker, uncomment_magic, unescape_code_start
  from .pep8 import pep8_lines_between_cells
  from .stringparser import StringParser
label_desc: Use image node_id 4 for calling the MarkdownCellReader obj's underlying member method code with example usage: obj.find_cell_end(lines) and returns: len,  len,  False, i,  unknown,  True, i,  i,  False, i,  i,  False, unknown,  i,  False, i,  unknown,  True, unknown,  i,  False, unknown,  i,  False, unknown,  i,  True, unknown,  i,  True