def test_dynamic_folding_insert_section(): # test insert section under empty data division # data division (which is not a fold trigger initially) block will become a fold trigger editor = CobolCodeEdit() editor.setPlainText(section_code, '', '') th = TextHelper(editor) block = editor.document().findBlockByNumber(2) assert TextBlockHelper.is_fold_trigger(block) is False cursor = th.goto_line(2, column=len(' DATA DIVISION.')) cursor.insertText('\n WORKING-STORAGE SECTION.') assert TextBlockHelper.is_fold_trigger(block) is True
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 _on_key_pressed(self, event): """ Override key press to select the current scope if the user wants to deleted a folded scope (without selecting it). """ delete_request = event.key() in [QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Delete] if event.text() or delete_request: cursor = self.editor.textCursor() if cursor.hasSelection(): # change selection to encompass the whole scope. positions_to_check = cursor.selectionStart(), cursor.selectionEnd() else: positions_to_check = (cursor.position(), ) for pos in positions_to_check: block = self.editor.document().findBlock(pos) th = TextBlockHelper() if th.is_fold_trigger(block) and th.is_collapsed(block): self.toggle_fold_trigger(block) if delete_request and cursor.hasSelection(): scope = FoldScope(self.find_parent_scope(block)) tc = TextHelper(self.editor).select_lines(*scope.get_range()) if tc.selectionStart() > cursor.selectionStart(): start = cursor.selectionStart() else: start = tc.selectionStart() if tc.selectionEnd() < cursor.selectionEnd(): end = cursor.selectionEnd() else: end = tc.selectionEnd() tc.setPosition(start) tc.setPosition(end, tc.KeepAnchor) self.editor.setTextCursor(tc)
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_expand_all(editor): panel = get_panel(editor) QTest.qWait(1000) panel.collapse_all() QTest.qWait(1000) panel.expand_all() block = editor.document().firstBlock() while block.blockNumber() < editor.document().blockCount() - 1: assert block.isVisible() if TextBlockHelper.is_fold_trigger(block): assert TextBlockHelper.is_collapsed(block) is False block = block.next()
def test_mouse_press(editor): panel = get_panel(editor) panel.highlight_caret_scope = False # fold child block toggle_fold_trigger(editor, 15, panel) block = editor.document().findBlockByNumber(14) assert TextBlockHelper.is_fold_trigger(block) is True assert TextBlockHelper.is_collapsed(block) is True block = block.next() while block.blockNumber() < 21: assert block.isVisible() is False block = block.next() # fold top level block toggle_fold_trigger(editor, 9, panel) block = editor.document().findBlockByNumber(8) assert TextBlockHelper.is_fold_trigger(block) block = block.next() while block.blockNumber() < 27: if block.blockNumber() == 14: assert TextBlockHelper.is_fold_trigger(block) is True assert TextBlockHelper.is_collapsed(block) is True assert block.isVisible() is False block = block.next() # unfold it top level block toggle_fold_trigger(editor, 9, panel) block = editor.document().findBlockByNumber(8) assert TextBlockHelper.is_fold_trigger(block) block = block.next() while block.blockNumber() < 27: if 14 < block.blockNumber() < 22: assert block.isVisible() is False else: assert block.isVisible() is True block = block.next() # cleanup QTest.mouseMove(panel, QtCore.QPoint(0, 0)) panel.leaveEvent(None) editor.setFocus() panel.highlight_caret_scope = True
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 toggle_fold_trigger(self, block): """ Toggle a fold trigger block (expand or collapse it). :param block: The QTextBlock to expand/collapse """ if not TextBlockHelper.is_fold_trigger(block): return region = FoldScope(block) if region.collapsed: region.unfold() if self._mouse_over_line is not None: self._add_scope_decorations( region._trigger, *region.get_range()) else: region.fold() self._clear_scope_decos() self._refresh_editor_and_scrollbars() self.trigger_state_changed.emit(region._trigger, region.collapsed)
def paintEvent(self, event): # Paints the fold indicators and the possible fold region background # on the folding panel. super(FoldingPanel, self).paintEvent(event) painter = QtGui.QPainter(self) # Draw background over the selected non collapsed fold region if self._mouse_over_line is not None: block = self.editor.document().findBlockByNumber( self._mouse_over_line) try: self._draw_fold_region_background(block, painter) except ValueError: pass # Draw fold triggers for top_position, line_number, block in self.editor.visible_blocks: if TextBlockHelper.is_fold_trigger(block): collapsed = TextBlockHelper.get_fold_trigger_state(block) mouse_over = self._mouse_over_line == line_number self._draw_fold_indicator( top_position, mouse_over, collapsed, painter) if collapsed: # check if the block already has a decoration, it might # have been folded by the parent editor/document in the # case of cloned editor for deco in self._block_decos: if deco.block == block: # no need to add a deco, just go to the next block break else: self._add_fold_decoration(block, FoldScope(block)) else: for deco in self._block_decos: # check if the block decoration has been removed, it # might have been unfolded by the parent # editor/document in the case of cloned editor if deco.block == block: # remove it and self._block_decos.remove(deco) self.editor.decorations.remove(deco) del deco break
def _highlight_caret_scope(self): """ Highlight the scope surrounding the current caret position. This get called only if :attr:` pyqode.core.panels.FoldingPanel.highlight_care_scope` is True. """ cursor = self.editor.textCursor() block_nbr = cursor.blockNumber() if self._block_nbr != block_nbr: block = FoldScope.find_parent_scope( self.editor.textCursor().block()) try: s = FoldScope(block) except ValueError: self._clear_scope_decos() else: self._mouse_over_line = block.blockNumber() if TextBlockHelper.is_fold_trigger(block): self._highlight_surrounding_scopes(block) self._block_nbr = block_nbr
def open(self, path, encoding=None, use_cached_encoding=True): encoding = self.detect_encoding(path) super(PyFileManager, self).open( path, encoding=encoding, use_cached_encoding=use_cached_encoding) try: folding_panel = self.editor.panels.get('FoldingPanel') except KeyError: pass else: # fold imports and/or docstrings blocks_to_fold = [] sh = self.editor.syntax_highlighter if self.fold_imports and sh.import_statements: blocks_to_fold += sh.import_statements if self.fold_docstrings and sh.docstrings: blocks_to_fold += sh.docstrings for block in blocks_to_fold: if TextBlockHelper.is_fold_trigger(block): folding_panel.toggle_fold_trigger(block)
def _add_scope_decorations(self, block, start, end): """ Show a scope decoration on the editor widget :param start: Start line :param end: End line """ try: parent = FoldScope(block).parent() except ValueError: parent = None if TextBlockHelper.is_fold_trigger(block): base_color = self._get_scope_highlight_color() factor_step = 5 if base_color.lightness() < 128: factor_step = 10 factor = 70 else: factor = 100 while parent: # highlight parent scope parent_start, parent_end = parent.get_range() self._add_scope_deco( start, end + 1, parent_start, parent_end, base_color, factor) # next parent scope start = parent_start end = parent_end parent = parent.parent() factor += factor_step # global scope parent_start = 0 parent_end = self.editor.document().blockCount() self._add_scope_deco( start, end + 1, parent_start, parent_end, base_color, factor + factor_step) else: self._clear_scope_decos()
def mouseMoveEvent(self, event): """ Detect mouser over indicator and highlight the current scope in the editor (up and down decoration arround the foldable text when the mouse is over an indicator). :param event: event """ super(FoldingPanel, self).mouseMoveEvent(event) th = TextHelper(self.editor) line = th.line_nbr_from_position(event.pos().y()) if line >= 0: block = FoldScope.find_parent_scope( self.editor.document().findBlockByNumber(line)) if TextBlockHelper.is_fold_trigger(block): if self._mouse_over_line is None: # mouse enter fold scope QtWidgets.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.PointingHandCursor)) if self._mouse_over_line != block.blockNumber() and \ self._mouse_over_line is not None: # fold scope changed, a previous block was highlighter so # we quickly update our highlighting self._mouse_over_line = block.blockNumber() self._highlight_surrounding_scopes(block) else: # same fold scope, request highlight self._mouse_over_line = block.blockNumber() self._highlight_runner.request_job( self._highlight_surrounding_scopes, block) self._highight_block = block else: # no fold scope to highlight, cancel any pending requests self._highlight_runner.cancel_requests() self._mouse_over_line = None QtWidgets.QApplication.restoreOverrideCursor() self.repaint()