Example #1
0
 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)
Example #2
0
 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)
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
    def goto_line(self, line, column=0, move=True):
        """
        Moves the text cursor to the specified position.

        :param line: Number of the line to go to (0 based)
        :param column: Optional column number. Default is 0 (start of line).
        :param move: True to move the cursor. False will return the cursor
                     without setting it on the editor.
        :return: The new text cursor
        :rtype: QtGui.QTextCursor
        """
        text_cursor = self._move_cursor_to(line)
        if column:
            text_cursor.movePosition(text_cursor.Right, text_cursor.MoveAnchor,
                                     column)
        if move:
            block = text_cursor.block()
            # unfold parent fold trigger if the block is collapsed
            try:
                folding_panel = self._editor.panels.get('FoldingPanel')
            except KeyError:
                pass
            else:
                from spyder.widgets.sourcecode.folding import FoldScope
                if not block.isVisible():
                    block = FoldScope.find_parent_scope(block)
                    if TextBlockHelper.is_collapsed(block):
                        folding_panel.toggle_fold_trigger(block)
            self._editor.setTextCursor(text_cursor)
        return text_cursor
Example #7
0
    def _highlight_caret_scope(self):
        """
        Highlight the scope surrounding the current caret position.

        This get called only if :attr:`
        spyder.widgets.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
Example #8
0
    def _draw_fold_region_background(self, block, painter):
        """
        Draw the fold region when the mouse is over and non collapsed
        indicator.

        :param top: Top position
        :param block: Current block.
        :param painter: QPainter
        """
        r = FoldScope(block)
        th = TextHelper(self.editor)
        start, end = r.get_range(ignore_blank_lines=True)
        if start > 0:
            top = th.line_pos_from_number(start)
        else:
            top = 0
        bottom = th.line_pos_from_number(end + 1)
        h = bottom - top
        if h == 0:
            h = self.sizeHint().height()
        w = self.sizeHint().width()
        self._draw_rect(QRectF(0, top, w, h), painter)
Example #9
0
    def _draw_fold_region_background(self, block, painter):
        """
        Draw the fold region when the mouse is over and non collapsed
        indicator.

        :param top: Top position
        :param block: Current block.
        :param painter: QPainter
        """
        r = FoldScope(block)
        th = TextHelper(self.editor)
        start, end = r.get_range(ignore_blank_lines=True)
        if start > 0:
            top = th.line_pos_from_number(start)
        else:
            top = 0
        bottom = th.line_pos_from_number(end + 1)
        h = bottom - top
        if h == 0:
            h = self.sizeHint().height()
        w = self.sizeHint().width()
        self._draw_rect(QRectF(0, top, w, h), painter)
Example #10
0
    def unfold_if_colapsed(self, block):
        """Unfold parent fold trigger if the block is collapsed.

        :param block: Block to unfold.
        """
        try:
            folding_panel = self._editor.panels.get('FoldingPanel')
        except KeyError:
            pass
        else:
            from spyder.widgets.sourcecode.folding import FoldScope
            if not block.isVisible():
                block = FoldScope.find_parent_scope(block)
                if TextBlockHelper.is_collapsed(block):
                    folding_panel.toggle_fold_trigger(block)
Example #11
0
    def _add_scope_decorations(self, block, start, end):
        """
        Show a scope decoration on the code 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()
Example #12
0
    def unfold_if_colapsed(self, block):
        """Unfold parent fold trigger if the block is collapsed.

        :param block: Block to unfold.
        """
        try:
            folding_panel = self._editor.panels.get('FoldingPanel')
        except KeyError:
            pass
        else:
            from spyder.widgets.sourcecode.folding import FoldScope
            if not block.isVisible():
                block = FoldScope.find_parent_scope(block)
                if TextBlockHelper.is_collapsed(block):
                    folding_panel.toggle_fold_trigger(block)
Example #13
0
class TestFoldScopeHelper(object):

    test_case = """# -*- coding: utf-8 -*-
def my_add():
    a = 1
    b = 2
    return a + b
"""

    doc = QTextDocument(test_case)
    sh = PythonSH(doc, color_scheme='Spyder')
    sh.fold_detector = IndentFoldDetector()
    sh.rehighlightBlock(doc.firstBlock())
    block = doc.firstBlock()
    block = block.next()
    TextBlockHelper.set_fold_trigger(block, True)
    fold_scope = FoldScope(block)
    oed = sh.get_outlineexplorer_data()[1]

    def test_fold_scope_helper(self):
        fsh = cfd.FoldScopeHelper(None, None)
        assert isinstance(fsh, cfd.FoldScopeHelper)

    def test_fold_scope_helper_str(self):
        fsh = cfd.FoldScopeHelper(self.fold_scope, self.oed)
        assert "my_add" in str(fsh)

    def test_fold_scope_helper_str_with_parents(self):
        fsh = cfd.FoldScopeHelper(self.fold_scope, self.oed)
        fsh.parents = ["fake parent list!"]
        assert "parents:" in str(fsh)

    def test_fold_scope_helper_repr(self):
        fsh = cfd.FoldScopeHelper(self.fold_scope, self.oed)
        assert "(at 0x" in repr(fsh)

    def test_fold_scope_helper_properties(self):
        fsh = cfd.FoldScopeHelper(self.fold_scope, self.oed)
        assert fsh.range == (1, 4)
        assert fsh.start_line == 1
        assert fsh.end_line == 4
        assert fsh.name == "my_add"
        assert fsh.line == 1
        assert fsh.def_type == OED.FUNCTION_TOKEN
Example #14
0
    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._decorate_block(*region.get_range())
        else:
            region.fold()
            self._clear_scope_decos()
        self._refresh_editor_and_scrollbars()
        self.trigger_state_changed.emit(region._trigger, region.collapsed)
Example #15
0
def _get_fold_levels(editor):
    """
    Return a list of all the class/function definition ranges.

    Parameters
    ----------
    editor : :class:`spyder.widgets.sourcecode.codeeditor.CodeEditor`

    Returns
    -------
    folds : list of :class:`FoldScopeHelper`
        A list of all the class or function defintion fold points.
    """
    block = editor.document().firstBlock()
    oed = editor.get_outlineexplorer_data()

    folds = []
    parents = []
    prev = None

    while block.isValid():
        if TextBlockHelper.is_fold_trigger(block):
            try:
                data = oed[block.firstLineNumber()]

                if data.def_type in (OED.CLASS, OED.FUNCTION):
                    fsh = FoldScopeHelper(FoldScope(block), data)

                    # Determine the parents of the item using a stack.
                    _adjust_parent_stack(fsh, prev, parents)

                    # Update the parents of this FoldScopeHelper item
                    fsh.parents = copy.copy(parents)
                    folds.append(fsh)
                    prev = fsh
            except KeyError:
                pass

        block = block.next()

    return folds
Example #16
0
 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
Example #17
0
    def _highlight_caret_scope(self):
        """
        Highlight the scope of the current caret position.

        This get called only if :attr:`
        spyder.widgets.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_block(block)
        self._block_nbr = block_nbr
Example #18
0
    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._decorate_block(*region.get_range())
        else:
            region.fold()
            self._clear_scope_decos()
        self._refresh_editor_and_scrollbars()
        self.trigger_state_changed.emit(region._trigger, region.collapsed)
Example #19
0
    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-1))
            if TextBlockHelper.is_fold_trigger(block):
                if self._mouse_over_line is None:
                    # mouse enter fold scope
                    QApplication.setOverrideCursor(
                        QCursor(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_block(block)
                else:
                    # same fold scope, request highlight
                    self._mouse_over_line = block.blockNumber()
                    self._highlight_runner.request_job(
                        self._highlight_block, 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
                QApplication.restoreOverrideCursor()
            self.repaint()
Example #20
0
    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 - 1))
            if TextBlockHelper.is_fold_trigger(block):
                if self._mouse_over_line is None:
                    # mouse enter fold scope
                    QApplication.setOverrideCursor(
                        QCursor(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
                QApplication.restoreOverrideCursor()
            self.repaint()
Example #21
0
 def _on_action_toggle(self):
     """Toggle the current fold trigger."""
     block = FoldScope.find_parent_scope(self.editor.textCursor().block())
     self.toggle_fold_trigger(block)
Example #22
0
 def _on_action_toggle(self):
     """Toggle the current fold trigger."""
     block = FoldScope.find_parent_scope(self.editor.textCursor().block())
     self.toggle_fold_trigger(block)