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_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 detect_fold_level(self, prev_block, block): if prev_block: prev_text = prev_block.text().strip() else: prev_text = '' if '[]' not in prev_text and '{}' not in prev_text: if prev_text.endswith(('{', '[')): return TextBlockHelper.get_fold_lvl(prev_block) + 1 if prev_text.replace(',', '').endswith(('}', ']')): return TextBlockHelper.get_fold_lvl(prev_block) - 1 return TextBlockHelper.get_fold_lvl(prev_block)
def collapse_all(self): """ Collapses all triggers and makes all blocks with fold level > 0 invisible. """ self._clear_block_deco() block = self.editor.document().firstBlock() last = self.editor.document().lastBlock() while block.isValid(): lvl = TextBlockHelper.get_fold_lvl(block) trigger = TextBlockHelper.is_fold_trigger(block) if trigger: if lvl == 0: self._show_previous_blank_lines(block) TextBlockHelper.set_fold_trigger_state(block, True) block.setVisible(lvl == 0) if block == last and block.text().strip() == '': block.setVisible(True) self._show_previous_blank_lines(block) block = block.next() self._refresh_editor_and_scrollbars() tc = self.editor.textCursor() tc.movePosition(tc.Start) self.editor.setTextCursor(tc) self.collapse_all_triggered.emit()
def detect_fold_level(self, prev_block, block): ctext, ptext = self.stripped_texts(block, prev_block) if not self.editor.free_format: ctext = self.normalize_text(ctext) ptext = self.normalize_text(ptext) if regex.DIVISION.indexIn( ctext) != -1 and not ctext.lstrip().startswith('*'): return OFFSET_DIVISION elif regex.SECTION.indexIn( ctext) != -1 and not ctext.lstrip().startswith('*'): return OFFSET_SECTION else: # anywhere else, folding is mostly based on the indentation level indent = self.get_indent(ctext) pindent = self.get_indent(ptext) if ctext.strip().upper().startswith('END-') and self.is_valid( prev_block) and pindent > indent: # find previous block with the same indent, use it's fold level + 1 to include # the end-branch statement in the fold scope pblock = prev_block while self.is_valid(pblock) and (pindent != indent or len(ptext.strip()) == 0): pblock = pblock.previous() ptext = self.normalize_text(pblock.text()) pindent = self.get_indent(ptext) lvl = TextBlockHelper.get_fold_lvl(pblock.next()) else: lvl = OFFSET_OTHER + indent # if not self.editor.free_format and (ctext.lstrip().startswith('-') or ctext.lstrip().startswith('*')): if not self.editor.free_format and ( ctext.lstrip().startswith('-')): # use previous fold level lvl = TextBlockHelper.get_fold_lvl(prev_block) if not self.editor.free_format and ctext.strip().startswith('*'): if regex.DIVISION.indexIn( ptext) != -1 and not ptext.lstrip().startswith('*'): lvl = OFFSET_SECTION elif regex.SECTION.indexIn( ptext) != -1 and not ptext.lstrip().startswith('*'): return OFFSET_SECTION + 2 else: lvl = TextBlockHelper.get_fold_lvl(prev_block) return lvl
def find_parent_scope(block): """ Find parent scope, if the block is not a fold trigger. """ original = block if not TextBlockHelper.is_fold_trigger(block): # search level of next non blank line while block.text().strip() == '' and block.isValid(): block = block.next() ref_lvl = TextBlockHelper.get_fold_lvl(block) - 1 block = original while (block.blockNumber() and (not TextBlockHelper.is_fold_trigger(block) or TextBlockHelper.get_fold_lvl(block) > ref_lvl)): block = block.previous() return block
def test_fold_limit(editor): editor.syntax_highlighter.fold_detector.limit = 1 editor.file.open('test/test_api/folding_cases/foo.py') block = editor.document().firstBlock() while block.blockNumber() < editor.blockCount() - 1: assert TextBlockHelper.get_fold_lvl(block) <= 1 block = block.next() editor.syntax_highlighter.fold_detector.limit = sys.maxsize
def get_parent_scopes(block): """ Gets the list of hierarchical parent scopes of the current block. :param block: current block :return: list of QTextBlock """ scopes = [block] scope = FoldScope.find_parent_scope(block) while scope is not None: ref = TextBlockHelper.get_fold_lvl(scopes[-1]) if TextBlockHelper.get_fold_lvl(scope) < ref: # ignore sibling scopes scopes.append(scope) if scope.blockNumber() == 0: # stop scope = None else: # next parent scope = FoldScope.find_parent_scope(scope.previous()) return reversed(scopes)
def get_parent_scopes(block): """ Gets the list of hierarchical parent scopes of the current block. :param block: current block :return: list of QTextBlock """ scopes = [block] scope = FoldScope.find_parent_scope(block) while scope is not None: ref = TextBlockHelper.get_fold_lvl(scopes[-1]) if TextBlockHelper.get_fold_lvl(scope) < ref: # ignore sibling scopes scopes.append(scope) if scope.blockNumber() == 0: # stop scope = None else: # next parent scope = FoldScope.find_parent_scope(scope.previous()) return reversed(scopes)
def detect_fold_level(self, prev_block, block): ctext, ptext = self.stripped_texts(block, prev_block) if not self.editor.free_format: ctext = self.normalize_text(ctext) ptext = self.normalize_text(ptext) if regex.DIVISION.indexIn(ctext) != -1 and not ctext.lstrip().startswith('*'): return OFFSET_DIVISION elif regex.SECTION.indexIn(ctext) != -1 and not ctext.lstrip().startswith('*'): return OFFSET_SECTION else: # anywhere else, folding is mostly based on the indentation level indent = self.get_indent(ctext) pindent = self.get_indent(ptext) if ctext.strip().upper().startswith('END-') and self.is_valid(prev_block) and pindent > indent: # find previous block with the same indent, use it's fold level + 1 to include # the end-branch statement in the fold scope pblock = prev_block while self.is_valid(pblock) and (pindent != indent or len(ptext.strip()) == 0): pblock = pblock.previous() ptext = self.normalize_text(pblock.text()) pindent = self.get_indent(ptext) lvl = TextBlockHelper.get_fold_lvl(pblock.next()) else: lvl = OFFSET_OTHER + indent # if not self.editor.free_format and (ctext.lstrip().startswith('-') or ctext.lstrip().startswith('*')): if not self.editor.free_format and (ctext.lstrip().startswith('-')): # use previous fold level lvl = TextBlockHelper.get_fold_lvl(prev_block) if not self.editor.free_format and ctext.strip().startswith('*'): if regex.DIVISION.indexIn(ptext) != -1 and not ptext.lstrip().startswith('*'): lvl = OFFSET_SECTION elif regex.SECTION.indexIn(ptext) != -1 and not ptext.lstrip().startswith('*'): return OFFSET_SECTION + 2 else: lvl = TextBlockHelper.get_fold_lvl(prev_block) return lvl
def test_collapse_all(editor): panel = get_panel(editor) QTest.qWait(1000) panel.collapse_all() QTest.qWait(1000) block = editor.document().firstBlock() while block.blockNumber() < editor.document().blockCount() - 1: blank_line = len(block.text().strip()) == 0 if TextBlockHelper.get_fold_lvl(block) > 0: if not blank_line: assert block.isVisible() is False else: assert block.isVisible() is True if TextBlockHelper.is_fold_trigger(block): assert TextBlockHelper.is_collapsed(block) is True block = block.next()
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
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
def detect_fold_level(self, prev_block, block): if not prev_block: return 0 ctext, ptext = self.stripped_texts(block, prev_block) if ctext.endswith('DIVISION.'): if 'DATA' in ctext: self.data_division = block self._data_div_txt = block.text() if 'PROCEDURE' in ctext: self.proc_division = block self._proc_div_txt = block.text() return 0 elif ctext.endswith('SECTION.'): return 1 elif ptext.endswith('DIVISION.'): return 1 elif ptext.endswith('SECTION.'): return 2 # in case of replace all or simply if the user deleted the data or # proc div. if (self.proc_division and self.proc_division.text() != self._proc_div_txt): self.proc_division = None if (self.data_division and self.data_division.text() != self._data_div_txt): self.data_division = None # inside PROCEDURE DIVISION if (self.proc_division and self.proc_division.isValid() and block.blockNumber() > self.proc_division.blockNumber()): # we only detect outline of paragraphes if regex.PARAGRAPH_PATTERN.indexIn(block.text()) != -1: # paragraph return 1 else: # content of a paragraph if regex.PARAGRAPH_PATTERN.indexIn(prev_block.text()) != -1: return 2 else: cstxt = ctext.lstrip() pstxt = ptext.lstrip() plvl = TextBlockHelper.get_fold_lvl(prev_block) if regex.LOOP_PATTERN.indexIn(pstxt) != -1: pstxt = '$L$O$OP$' if pstxt in ['END-IF', 'END-PERFORM', 'END-READ']: if cstxt in ['ELSE']: return plvl - 2 return plvl - 1 if cstxt in ['ELSE']: return plvl - 1 for token in ['IF', 'ELSE', '$L$O$OP$', 'READ']: if pstxt.startswith(token): return plvl + 1 return plvl # INSIDE DATA DIVISION elif (self.data_division and self.data_division.isValid() and block.blockNumber() > self.data_division.blockNumber() + 1): # here folding is based on the indentation level offset = 6 indent = ((len(ctext) - len(ctext.lstrip()) - offset) // self.editor.tab_length) return 2 + indent # other lines follow their previous fold level plvl = TextBlockHelper.get_fold_lvl(prev_block) return plvl