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 [Qt.Key_Backspace, 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 _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 [Qt.Key_Backspace, 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 process_block(self, current_block, previous_block, text): """ Processes a block and setup its folding info. This method call ``detect_fold_level`` and handles most of the tricky corner cases so that all you have to do is focus on getting the proper fold level foreach meaningful block, skipping the blank ones. :param current_block: current block to process :param previous_block: previous block :param text: current block text """ prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block) if text.strip() == '' or self.editor.is_comment(current_block): # blank or comment line always have the same level # as the previous line fold_level = prev_fold_level else: fold_level = self.detect_fold_level( previous_block, current_block) if fold_level > self.limit: fold_level = self.limit prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block) if fold_level > prev_fold_level: # apply on previous blank or comment lines block = current_block.previous() while block.isValid() and (block.text().strip() == '' or self.editor.is_comment(block)): TextBlockHelper.set_fold_lvl(block, fold_level) block = block.previous() TextBlockHelper.set_fold_trigger( block, True) # update block fold level if text.strip() and not self.editor.is_comment(previous_block): TextBlockHelper.set_fold_trigger( previous_block, fold_level > prev_fold_level) TextBlockHelper.set_fold_lvl(current_block, fold_level) # user pressed enter at the beginning of a fold trigger line # the previous blank or comment line will keep the trigger state # and the new line (which actually contains the trigger) must use # the prev state (and prev state must then be reset). prev = current_block.previous() # real prev block (may be blank) if (prev and prev.isValid() and (prev.text().strip() == '' or self.editor.is_comment(prev)) and TextBlockHelper.is_fold_trigger(prev)): # prev line has the correct trigger fold state TextBlockHelper.set_collapsed( current_block, TextBlockHelper.is_collapsed( prev)) # make empty or comment line not a trigger TextBlockHelper.set_fold_trigger(prev, False) TextBlockHelper.set_collapsed(prev, False)
def process_block(self, current_block, previous_block, text): """ Processes a block and setup its folding info. This method call ``detect_fold_level`` and handles most of the tricky corner cases so that all you have to do is focus on getting the proper fold level foreach meaningful block, skipping the blank ones. :param current_block: current block to process :param previous_block: previous block :param text: current block text """ prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block) if text.strip() == '' or self.editor.is_comment(current_block): # blank or comment line always have the same level # as the previous line fold_level = prev_fold_level else: fold_level = self.detect_fold_level(previous_block, current_block) if fold_level > self.limit: fold_level = self.limit prev_fold_level = TextBlockHelper.get_fold_lvl(previous_block) if fold_level > prev_fold_level: # apply on previous blank or comment lines block = current_block.previous() while block.isValid() and (block.text().strip() == '' or self.editor.is_comment(block)): TextBlockHelper.set_fold_lvl(block, fold_level) block = block.previous() TextBlockHelper.set_fold_trigger(block, True) # update block fold level if text.strip() and not self.editor.is_comment(previous_block): TextBlockHelper.set_fold_trigger(previous_block, fold_level > prev_fold_level) TextBlockHelper.set_fold_lvl(current_block, fold_level) # user pressed enter at the beginning of a fold trigger line # the previous blank or comment line will keep the trigger state # and the new line (which actually contains the trigger) must use # the prev state (and prev state must then be reset). prev = current_block.previous() # real prev block (may be blank) if (prev and prev.isValid() and (prev.text().strip() == '' or self.editor.is_comment(prev)) and TextBlockHelper.is_fold_trigger(prev)): # prev line has the correct trigger fold state TextBlockHelper.set_collapsed(current_block, TextBlockHelper.is_collapsed(prev)) # make empty or comment line not a trigger TextBlockHelper.set_fold_trigger(prev, False) TextBlockHelper.set_collapsed(prev, False)
def _highlight_block(self, block): """ Highlights the current fold scope. :param block: Block that starts the current fold scope. """ scope = FoldScope(block) if (self._current_scope is None or self._current_scope.get_range() != scope.get_range()): self._current_scope = scope self._clear_scope_decos() # highlight current scope with darker or lighter color start, end = scope.get_range() if not TextBlockHelper.is_collapsed(block): self._decorate_block(start, end)
def _highlight_surrounding_scopes(self, block): """ Highlights the scopes surrounding the current fold scope. :param block: Block that starts the current fold scope. """ scope = FoldScope(block) if (self._current_scope is None or self._current_scope.get_range() != scope.get_range()): self._current_scope = scope self._clear_scope_decos() # highlight surrounding parent scopes with a darker color start, end = scope.get_range() if not TextBlockHelper.is_collapsed(block): self._add_scope_decorations(block, start, end)
def paintEvent(self, event): # Paints the fold indicators and the possible fold region background # on the folding panel. super(FoldingPanel, self).paintEvent(event) painter = 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.is_collapsed(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 paintEvent(self, event): # Paints the fold indicators and the possible fold region background # on the folding panel. super(FoldingPanel, self).paintEvent(event) painter = 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.is_collapsed(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 collapsed(self): """Returns True if the block is collasped, False if it is expanded.""" return TextBlockHelper.is_collapsed(self._trigger)