Exemplo n.º 1
0
 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()
Exemplo n.º 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 [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)
Exemplo n.º 3
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 [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)
Exemplo n.º 4
0
 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
Exemplo n.º 5
0
 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
Exemplo n.º 6
0
 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)
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
 def expand_all(self):
     """
     Expands all fold triggers.
     """
     block = self.editor.document().firstBlock()
     while block.isValid():
         TextBlockHelper.set_fold_trigger_state(block, False)
         block.setVisible(True)
         block = block.next()
     self._clear_block_deco()
     self._refresh_editor_and_scrollbars()
     self.expand_all_triggered.emit()
Exemplo n.º 10
0
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()
Exemplo n.º 11
0
        def convert(name, editor, to_collapse):
            ti = QtWidgets.QTreeWidgetItem()
            ti.setText(0, name.name)
            if isinstance(name.icon, list):
                icon = QtGui.QIcon.fromTheme(name.icon[0],
                                             QtGui.QIcon(name.icon[1]))
            else:
                icon = QtGui.QIcon(name.icon)
            ti.setIcon(0, icon)
            name.block = editor.document().findBlockByNumber(name.line)
            ti.setData(0, QtCore.Qt.UserRole, name)
            ti.setToolTip(0, name.description)
            name.tree_item = ti
            block_data = name.block.userData()
            if block_data is None:
                block_data = TextBlockUserData()
                name.block.setUserData(block_data)
            block_data.tree_item = ti

            if to_collapse is not None and \
                    TextBlockHelper.is_collapsed(name.block):
                to_collapse.append(ti)

            for ch in name.children:
                ti_ch, to_collapse = convert(ch, editor, to_collapse)
                if ti_ch:
                    ti.addChild(ti_ch)
            return ti, to_collapse
        def convert(name, editor, to_collapse):
            ti = QtWidgets.QTreeWidgetItem()
            ti.setText(0, name.name)
            if isinstance(name.icon, list):
                icon = QtGui.QIcon.fromTheme(
                    name.icon[0], QtGui.QIcon(name.icon[1]))
            else:
                icon = QtGui.QIcon(name.icon)
            ti.setIcon(0, icon)
            name.block = editor.document().findBlockByNumber(name.line)
            ti.setData(0, QtCore.Qt.UserRole, name)
            ti.setToolTip(0, name.description)
            block_data = name.block.userData()
            if block_data is None:
                block_data = TextBlockUserData()
                name.block.setUserData(block_data)
            block_data.tree_item = ti

            if to_collapse is not None and \
                    TextBlockHelper.is_collapsed(name.block):
                to_collapse.append(ti)

            for ch in name.children:
                ti_ch, to_collapse = convert(ch, editor, to_collapse)
                if ti_ch:
                    ti.addChild(ti_ch)
            return ti, to_collapse
Exemplo n.º 13
0
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()
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
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
Exemplo n.º 17
0
 def _on_item_state_changed(self, item):
     if self._updating:
         return
     block = item.data(0, QtCore.Qt.UserRole).block
     assert isinstance(item, QtWidgets.QTreeWidgetItem)
     item_state = not item.isExpanded()
     block_state = TextBlockHelper.is_collapsed(block)
     if item_state != block_state:
         self._updating = True
         self._folding_panel.toggle_fold_trigger(block)
         self._updating = False
Exemplo n.º 18
0
 def _on_item_state_changed(self, item):
     if self._updating:
         return
     block = item.data(0, QtCore.Qt.UserRole).block
     assert isinstance(item, QtWidgets.QTreeWidgetItem)
     item_state = not item.isExpanded()
     block_state = TextBlockHelper.get_fold_trigger_state(block)
     if item_state != block_state:
         self._updating = True
         self._folding_panel.toggle_fold_trigger(block)
         self._updating = False
Exemplo n.º 19
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 = 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
Exemplo n.º 20
0
    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)
Exemplo n.º 21
0
    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)
Exemplo n.º 22
0
    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
Exemplo n.º 23
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.get_fold_trigger_state(block):
                self._add_scope_decorations(block, start, end)
Exemplo n.º 24
0
 def _on_item_state_changed(self, item):
     if self._updating:
         return
     if item == self._root_item:
         item_state = not item.isExpanded()
         if item_state:
             QtCore.QTimer.singleShot(
                 1, self._editor.folding_panel.collapse_all)
         else:
             QtCore.QTimer.singleShot(
                 1, self._editor.folding_panel.expand_all)
     else:
         block = item.data(0, QtCore.Qt.UserRole).block
         assert isinstance(item, QtWidgets.QTreeWidgetItem)
         item_state = not item.isExpanded()
         block_state = TextBlockHelper.get_fold_trigger_state(block)
         if item_state != block_state:
             self._editor.folding_panel.toggle_fold_trigger(block)
Exemplo n.º 25
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._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)
Exemplo n.º 26
0
    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 convert(name, editor, to_collapse):
            ti = QtWidgets.QTreeWidgetItem()
            ti.setText(0, name.name)
            ti.setIcon(0, QtGui.QIcon(name.icon))
            name.block = editor.document().findBlockByNumber(name.line)
            ti.setData(0, QtCore.Qt.UserRole, name)
            block_data = name.block.userData()
            if block_data is None:
                block_data = TextBlockUserData()
                name.block.setUserData(block_data)
            block_data.tree_item = ti

            if to_collapse is not None and \
                    TextBlockHelper.get_fold_trigger_state(name.block):
                to_collapse.append(ti)

            for ch in name.children:
                ti_ch, to_collapse = convert(ch, editor, to_collapse)
                if ti_ch:
                    ti.addChild(ti_ch)
            return ti, to_collapse
Exemplo n.º 28
0
    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
Exemplo n.º 29
0
        def convert(name, editor, to_collapse):
            ti = QtWidgets.QTreeWidgetItem()
            ti.setText(0, name.name)
            ti.setIcon(0, QtGui.QIcon(name.icon))
            name.block = editor.document().findBlockByNumber(name.line)
            ti.setData(0, QtCore.Qt.UserRole, name)
            block_data = name.block.userData()
            if block_data is None:
                block_data = TextBlockUserData()
                name.block.setUserData(block_data)
            block_data.tree_item = ti

            if to_collapse is not None and \
                    TextBlockHelper.get_fold_trigger_state(name.block):
                to_collapse.append(ti)

            for ch in name.children:
                ti_ch, to_collapse = convert(ch, editor, to_collapse)
                if ti_ch:
                    ti.addChild(ti_ch)
            return ti, to_collapse
Exemplo n.º 30
0
    def _convert_name(self, name, editor):
        ti = QtWidgets.QTreeWidgetItem()
        ti.setText(0, name.name)
        ti.setIcon(0, QtGui.QIcon(ICONS[name.node_type]))
        ti.setToolTip(0, name.description)
        name.block = editor.document().findBlockByNumber(name.line)
        ti.setData(0, QtCore.Qt.UserRole, name)

        block_data = name.block.userData()
        if block_data is None:
            block_data = TextBlockUserData()
            name.block.setUserData(block_data)
        block_data.tree_item = ti

        if TextBlockHelper.get_fold_trigger_state(name.block):
            self._to_collapse.append(ti)

        for child in name.children:
            ti_ch = self._convert_name(child, editor)
            ti.addChild(ti_ch)

        return ti
Exemplo n.º 31
0
    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()
Exemplo n.º 32
0
    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
Exemplo n.º 33
0
    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
Exemplo n.º 34
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))
            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()
Exemplo n.º 35
0
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
Exemplo n.º 36
0
    def highlight_block(self, text, block):
        """
        Highlight a block of text.

        :param text: The text of the block to highlight
        :param block: The QTextBlock to highlight
        """
        self._check_formats()
        prev_block = block.previous()
        prev_state = TextBlockHelper.get_state(prev_block)
        self.setFormat(0, len(text), self.formats["normal"])
        no_formats = True
        match = self.PROG.search(text)
        state = self.NORMAL
        while match:
            for key, value in list(match.groupdict().items()):
                if value:
                    no_formats = False
                    start, end = match.span(key)
                    if key == 'tag' and len(set(text)) != 1:
                            # 2 different characters -> not a header,
                            # probably a table
                            continue
                    self.setFormat(start, end - start, self.formats[key])
                    if key == 'comment':
                        state = self.INSIDE_COMMENT
                    if key == 'string' and not match.group(0).endswith('`'):
                        state = self.INSIDE_STRING
                    # make sure to highlight previous block
                    if key == 'tag':
                        state = self.INSIDE_HEADER
                        pblock = block.previous()
                        if pblock.isValid() and pblock.text() and \
                                prev_state != self.INSIDE_HEADER:
                            self._block_to_rehighlight = pblock
                            QtCore.QTimer.singleShot(
                                1, self._rehighlight_block)

            match = self.PROG.search(text, match.end())

        if no_formats:
            nblock = block.next()
            indent = len(text) - len(text.lstrip())
            if nblock.isValid() and self.PROG_HEADER.match(nblock.text()) and \
                    len(set(nblock.text())) == 1:
                self.setFormat(0, len(text), self.formats["tag"])
                state = self.INSIDE_HEADER
            elif prev_state == self.INSIDE_COMMENT and (
                    indent > 0 or not len(text)):
                self.setFormat(0, len(text), self.formats["comment"])
                state = self.INSIDE_COMMENT
            elif prev_state == self.INSIDE_STRING:
                # check if end string found -> highlight match only otherwise
                # highlight whole line
                match = self.PROG_END_STRING.match(text)
                if match:
                    end = match.end()
                else:
                    state = self.INSIDE_STRING
                    end = len(text)
                self.setFormat(0, end, self.formats["string"])
        TextBlockHelper.set_state(block, state)
Exemplo n.º 37
0
    def highlight_block(self, text, block):
        prev_block = block.previous()
        prev_state = TextBlockHelper.get_state(prev_block)
        if prev_state == self.INSIDE_DQ3STRING:
            offset = -4
            text = r'""" ' + text
        elif prev_state == self.INSIDE_SQ3STRING:
            offset = -4
            text = r"''' " + text
        elif prev_state == self.INSIDE_DQSTRING:
            offset = -2
            text = r'" ' + text
        elif prev_state == self.INSIDE_SQSTRING:
            offset = -2
            text = r"' " + text
        else:
            offset = 0

        import_stmt = None
        # set docstring dynamic attribute, used by the fold detector.
        block.docstring = False

        self.setFormat(0, len(text), self.formats["normal"])

        state = self.NORMAL
        match = self.PROG.search(text)
        while match:
            for key, value in list(match.groupdict().items()):
                if value:
                    start, end = match.span(key)
                    start = max([0, start + offset])
                    end = max([0, end + offset])
                    if key == "uf_sq3string":
                        self.setFormat(start, end - start,
                                       self.formats["docstring"])
                        block.docstring = True
                        state = self.INSIDE_SQ3STRING
                    elif key == "uf_dq3string":
                        self.setFormat(start, end - start,
                                       self.formats["docstring"])
                        block.docstring = True
                        state = self.INSIDE_DQ3STRING
                    elif key == "uf_sqstring":
                        self.setFormat(start, end - start,
                                       self.formats["string"])
                        state = self.INSIDE_SQSTRING
                    elif key == "uf_dqstring":
                        self.setFormat(start, end - start,
                                       self.formats["string"])
                        state = self.INSIDE_DQSTRING
                    elif key == 'builtin_fct':
                        # trick to highlight __init__, __add__ and so on with
                        # builtin color
                        self.setFormat(start, end - start,
                                       self.formats["constant"])
                    else:
                        if ('"""' in value or "'''" in value) and \
                                key != 'comment':
                            # highlight docstring with a different color
                            block.docstring = True
                            self.setFormat(start, end - start,
                                           self.formats["docstring"])
                        elif key == 'decorator':
                            # highlight decorators
                            self.setFormat(start, end - start,
                                           self.formats["decorator"])
                        elif value in ['self', 'cls']:
                            # highlight self attribute
                            self.setFormat(start, end - start,
                                           self.formats["self"])
                        else:
                            # highlight all other tokens
                            self.setFormat(start, end - start,
                                           self.formats[key])
                        if key == "keyword":
                            if value in ("def", "class"):
                                match1 = self.IDPROG.match(text, end)
                                if match1:
                                    start1, end1 = match1.span(1)
                                    fmt_key = ('definition' if value == 'class'
                                               else 'function')
                                    fmt = self.formats[fmt_key]
                                    self.setFormat(start1, end1 - start1, fmt)
                        if key == 'namespace':
                            import_stmt = text.strip()
                            # color all the "as" words on same line, except
                            # if in a comment; cheap approximation to the
                            # truth
                            if '#' in text:
                                endpos = text.index('#')
                            else:
                                endpos = len(text)
                            while True:
                                match1 = self.ASPROG.match(text, end,
                                                           endpos)
                                if not match1:
                                    break
                                start, end = match1.span(1)
                                self.setFormat(start, end - start,
                                               self.formats["namespace"])
            # next match
            match = self.PROG.search(text, match.end())
        TextBlockHelper.set_state(block, state)

        # update import zone
        if import_stmt is not None:
            block.import_stmt = import_stmt
            self.import_statements.append(block)
            block.import_stmt = True
        elif block.docstring:
            self.docstrings.append(block)
Exemplo n.º 38
0
    def highlight_block(self, text, block):
        """
        Highlight a block of text.

        :param text: The text of the block to highlight
        :param block: The QTextBlock to highlight
        """
        self._check_formats()
        prev_block = block.previous()
        prev_state = TextBlockHelper.get_state(prev_block)
        self.setFormat(0, len(text), self.formats["normal"])
        no_formats = True
        match = self.PROG.search(text)
        state = self.NORMAL
        while match:
            for key, value in list(match.groupdict().items()):
                if value:
                    no_formats = False
                    start, end = match.span(key)
                    if key == 'tag' and len(set(text)) != 1:
                        # 2 different characters -> not a header,
                        # probably a table
                        continue
                    self.setFormat(start, end - start, self.formats[key])
                    if key == 'comment':
                        state = self.INSIDE_COMMENT
                    if key == 'string' and not match.group(0).endswith('`'):
                        state = self.INSIDE_STRING
                    # make sure to highlight previous block
                    if key == 'tag':
                        state = self.INSIDE_HEADER
                        pblock = block.previous()
                        if pblock.isValid() and pblock.text() and \
                                prev_state != self.INSIDE_HEADER:
                            self._block_to_rehighlight = pblock
                            QtCore.QTimer.singleShot(1,
                                                     self._rehighlight_block)

            match = self.PROG.search(text, match.end())

        if no_formats:
            nblock = block.next()
            indent = len(text) - len(text.lstrip())
            if nblock.isValid() and self.PROG_HEADER.match(nblock.text()) and \
                    len(set(nblock.text())) == 1:
                self.setFormat(0, len(text), self.formats["tag"])
                state = self.INSIDE_HEADER
            elif prev_state == self.INSIDE_COMMENT and (indent > 0
                                                        or not len(text)):
                self.setFormat(0, len(text), self.formats["comment"])
                state = self.INSIDE_COMMENT
            elif prev_state == self.INSIDE_STRING:
                # check if end string found -> highlight match only otherwise
                # highlight whole line
                match = self.PROG_END_STRING.match(text)
                if match:
                    end = match.end()
                else:
                    state = self.INSIDE_STRING
                    end = len(text)
                self.setFormat(0, end, self.formats["string"])
        TextBlockHelper.set_state(block, state)
Exemplo n.º 39
0
 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
Exemplo n.º 40
0
    def highlight_block(self, text, block):
        prev_block = block.previous()
        prev_state = TextBlockHelper.get_state(prev_block)
        if prev_state == self.INSIDE_DQ3STRING:
            offset = -4
            text = r'""" ' + text
        elif prev_state == self.INSIDE_SQ3STRING:
            offset = -4
            text = r"''' " + text
        elif prev_state == self.INSIDE_DQSTRING:
            offset = -2
            text = r'" ' + text
        elif prev_state == self.INSIDE_SQSTRING:
            offset = -2
            text = r"' " + text
        else:
            offset = 0

        import_stmt = None
        # set docstring dynamic attribute, used by the fold detector.
        block.docstring = False

        self.setFormat(0, len(text), self.formats["normal"])

        state = self.NORMAL
        match = self.PROG.search(text)
        while match:
            for key, value in list(match.groupdict().items()):
                if value:
                    start, end = match.span(key)
                    start = max([0, start + offset])
                    end = max([0, end + offset])
                    if key == "uf_sq3string":
                        self.setFormat(start, end - start,
                                       self.formats["docstring"])
                        block.docstring = True
                        state = self.INSIDE_SQ3STRING
                    elif key == "uf_dq3string":
                        self.setFormat(start, end - start,
                                       self.formats["docstring"])
                        block.docstring = True
                        state = self.INSIDE_DQ3STRING
                    elif key == "uf_sqstring":
                        self.setFormat(start, end - start,
                                       self.formats["string"])
                        state = self.INSIDE_SQSTRING
                    elif key == "uf_dqstring":
                        self.setFormat(start, end - start,
                                       self.formats["string"])
                        state = self.INSIDE_DQSTRING
                    elif key == 'builtin_fct':
                        # trick to highlight __init__, __add__ and so on with
                        # builtin color
                        self.setFormat(start, end - start,
                                       self.formats["constant"])
                    else:
                        if ('"""' in value or "'''" in value) and \
                                key != 'comment':
                            # highlight docstring with a different color
                            block.docstring = True
                            self.setFormat(start, end - start,
                                           self.formats["docstring"])
                        elif key == 'decorator':
                            # highlight decorators
                            self.setFormat(start, end - start,
                                           self.formats["decorator"])
                        elif value in ['self', 'cls']:
                            # highlight self attribute
                            self.setFormat(start, end - start,
                                           self.formats["self"])
                        else:
                            # highlight all other tokens
                            self.setFormat(start, end - start,
                                           self.formats[key])
                        if key == "keyword":
                            if value in ("def", "class"):
                                match1 = self.IDPROG.match(text, end)
                                if match1:
                                    start1, end1 = match1.span(1)
                                    fmt_key = ('definition' if value == 'class'
                                               else 'function')
                                    fmt = self.formats[fmt_key]
                                    self.setFormat(start1, end1 - start1, fmt)
                        if key == 'namespace':
                            import_stmt = text.strip()
                            # color all the "as" words on same line, except
                            # if in a comment; cheap approximation to the
                            # truth
                            if '#' in text:
                                endpos = text.index('#')
                            else:
                                endpos = len(text)
                            while True:
                                match1 = self.ASPROG.match(text, end, endpos)
                                if not match1:
                                    break
                                start, end = match1.span(1)
                                self.setFormat(start, end - start,
                                               self.formats["namespace"])
            # next match
            match = self.PROG.search(text, match.end())
        TextBlockHelper.set_state(block, state)

        # update import zone
        if import_stmt is not None:
            block.import_stmt = import_stmt
            self.import_statements.append(block)
            block.import_stmt = True
        elif block.docstring:
            self.docstrings.append(block)