Source code for pyqode.python.folding

"""
This module contains the python code fold detector.
"""
import re
from pyqode.core.api import IndentFoldDetector, TextBlockHelper, TextHelper


[docs]class PythonFoldDetector(IndentFoldDetector): """ Python specific fold detector. Python is an indent based language so we use indentation for detecting the outline but we discard regions with higher indentation if they do not follow a trailing ':'. That way, only the real logical blocks are displayed. We also add some trickery to make import regions and docstring appear with an higher fold level than they should be (in order to make them foldable). """ #: regex which identifies a single line docstring _single_line_docstring = re.compile(r'""".*"""') def _strip_comments(self, prev_block): txt = prev_block.text().strip() if prev_block else '' if txt.find('#') != -1: txt = txt[:txt.find('#')].strip() return txt def _handle_docstrings(self, block, lvl, prev_block): if block.docstring: is_start = block.text().strip().startswith('"""') if is_start: TextBlockHelper.get_fold_lvl(prev_block) + 1 else: pblock = block.previous() while pblock.isValid() and pblock.text().strip() == '': pblock = pblock.previous() is_start = pblock.text().strip().startswith('"""') if is_start: return TextBlockHelper.get_fold_lvl(pblock) + 1 else: return TextBlockHelper.get_fold_lvl(pblock) # fix end of docstring elif prev_block and prev_block.text().strip().endswith('"""'): single_line = self._single_line_docstring.match( prev_block.text().strip()) if single_line: TextBlockHelper.set_fold_lvl(prev_block, lvl) else: TextBlockHelper.set_fold_lvl( prev_block, TextBlockHelper.get_fold_lvl( prev_block.previous())) return lvl def _handle_imports(self, block, lvl, prev_block): txt = block.text() indent = len(txt) - len(txt.lstrip()) if (hasattr(block, 'import_stmt') and prev_block and 'import ' in prev_block.text() and indent == 0): return 1 return lvl
[docs] def detect_fold_level(self, prev_block, block): """ Perfoms fold level detection for current block (take previous block into account). :param prev_block: previous block, None if `block` is the first block. :param block: block to analyse. :return: block fold level """ # Python is an indent based language so use indentation for folding # makes sense but we restrict new regions to indentation after a ':', # that way only the real logical blocks are displayed. lvl = super(PythonFoldDetector, self).detect_fold_level( prev_block, block) # cancel false indentation, indentation can only happen if there is # ':' on the previous line prev_lvl = TextBlockHelper.get_fold_lvl(prev_block) if prev_block and lvl > prev_lvl and not ( self._strip_comments(prev_block).endswith(':')): lvl = prev_lvl lvl = self._handle_docstrings(block, lvl, prev_block) lvl = self._handle_imports(block, lvl, prev_block) return lvl