def request_completion(self): """ Requests a code completion at the current cursor position. """ _logger().debug('request code completion') self._col = self.editor.textCursor().positionInBlock() - len( self.completion_prefix) helper = TextHelper(self.editor) if not self._request_cnt: # only check first byte tc = self.editor.textCursor() while tc.atBlockEnd() and not tc.atBlockStart() and \ tc.position(): tc.movePosition(tc.Left) disabled_zone = TextHelper(self.editor).is_comment_or_string(tc) if disabled_zone: _logger().debug( "cc: cancel request, cursor is in a disabled zone") return False self._request_cnt += 1 self._collect_completions( self.editor.toPlainText(), helper.current_line_nbr(), helper.current_column_nbr() - len(self.completion_prefix), self.editor.file.path, self.editor.file.encoding, self.completion_prefix) return True return False
def test_line_indent(editor): editor.setPlainText(src, 'text/x-python', 'utf-8') assert TextHelper(editor).line_indent(0) == 0 assert TextHelper(editor).line_indent(1) == 4 editor.file.open(__file__) assert TextHelper(editor).line_indent(TextHelper(editor).line_count() - 2) == 4
def test_clear_selection(editor): editor.file.open(__file__) helper = TextHelper(editor) TextHelper(editor).select_lines(0, 2) assert helper.selected_text() != '' TextHelper(editor).clear_selection() assert helper.selected_text() == ''
def test_line_nbr_from_position(editor): editor.repaint() sys.stderr.write(str(editor.visible_blocks)) assert TextHelper(editor).line_nbr_from_position( TextHelper(editor).line_pos_from_number(0)) is not None assert TextHelper(editor).line_nbr_from_position(-1) == -1 QTest.qWait(100)
def paintEvent(self, event): # Paints the line numbers self._line_color_u = drift_color(self._background_brush.color(), 250) self._line_color_s = drift_color(self._background_brush.color(), 280) Panel.paintEvent(self, event) if self.isVisible(): painter = QtGui.QPainter(self) # get style options (font, size) width = self.width() height = self.editor.fontMetrics().height() font = self.editor.font() bold_font = self.editor.font() bold_font.setBold(True) pen = QtGui.QPen(self._line_color_u) pen_selected = QtGui.QPen(self._line_color_s) painter.setFont(font) # get selection range sel_start, sel_end = TextHelper(self.editor).selection_range() has_sel = sel_start != sel_end cl = TextHelper(self.editor).current_line_nbr() # draw every visible blocks for top, line, block in self.editor.visible_blocks: if ((has_sel and sel_start <= line <= sel_end) or (not has_sel and cl == line)): painter.setPen(pen_selected) painter.setFont(bold_font) else: painter.setPen(pen) painter.setFont(font) painter.drawText(-3, top, width, height, QtCore.Qt.AlignRight, str(line + 1))
def mouseMoveEvent(self, e): # Updates end of selection if we are currently selecting if self._selecting: end_pos = e.pos().y() start_line = TextHelper(self.editor).line_nbr_from_position( self._sel_start) end_line = TextHelper(self.editor).line_nbr_from_position(end_pos) TextHelper(self.editor).select_lines(start_line, end_line)
def test_line_pos_from_number(editor): assert TextHelper(editor).line_pos_from_number(0) is not None # out of range line will return the bottom of the document or the top assert TextHelper(editor).line_pos_from_number(-1) == 0 pos = TextHelper(editor).line_pos_from_number( TextHelper(editor).line_count() + 10) assert pos is not None assert pos > 0
def test_select_lines(editor): TextHelper(editor).select_lines(0, 4) QTest.qWait(100) QTest.qWait(1000) assert TextHelper(editor).selection_range() == (0, 4) TextHelper(editor).select_lines(-1, 10) assert TextHelper(editor).selection_range() == (0, 10) editor.decorations.clear()
def mousePressEvent(self, e): """ Starts selecting """ self._selecting = True self._sel_start = e.pos().y() start = end = TextHelper(self.editor).line_nbr_from_position( self._sel_start) TextHelper(self.editor).select_lines(start, end)
def test_do_home_key(editor): QTest.qWait(2000) helper = TextHelper(editor) helper.goto_line(336, 29) assert editor.textCursor().positionInBlock() == 29 assert TextHelper(editor).line_indent() == 4 editor._do_home_key() assert editor.textCursor().positionInBlock() == 4 editor._do_home_key() assert editor.textCursor().positionInBlock() == 0
def test_keep_tc(editor): @keep_tc_pos def move_cursor(editor, arg): assert arg == 'arg' TextHelper(editor).goto_line(4) l, c = TextHelper(editor).cursor_position() move_cursor(editor, 'arg') nl, nc = TextHelper(editor).cursor_position() assert l == nl and c == nc
def test_goto_line(editor): assert editor.textCursor().blockNumber() == 0 assert editor.textCursor().columnNumber() == 0 cursor = TextHelper(editor).goto_line(2, 0, move=False) QTest.qWait(100) assert editor.textCursor().blockNumber() != cursor.blockNumber() assert editor.textCursor().columnNumber() == cursor.columnNumber() cursor = TextHelper(editor).goto_line(9, move=True) QTest.qWait(100) assert editor.textCursor().blockNumber() == cursor.blockNumber() == 9 assert editor.textCursor().columnNumber() == cursor.columnNumber() == 0 assert TextHelper(editor).current_line_nbr() == 9 assert TextHelper(editor).current_column_nbr() == 0
def mouseMoveEvent(self, e): # Updates end of selection if we are currently selecting if self._selecting: end_pos = e.pos().y() end_line = TextHelper(self.editor).line_nbr_from_position(end_pos) if end_line == -1 and self.editor.visible_blocks: # take last visible block if end_pos < 50: _, end_line, _ = self.editor.visible_blocks[0] end_line -= 1 else: _, end_line, _ = self.editor.visible_blocks[-1] end_line += 1 TextHelper(self.editor).select_lines(self._start_line, end_line)
def mouseMoveEvent(self, event): # Requests a tooltip if the cursor is currently over a marker. line = TextHelper(self.editor).line_nbr_from_position(event.pos().y()) markers = self.marker_for_line(line) text = '\n'.join([marker.description for marker in markers if marker.description]) if len(markers): if self._previous_line != line: top = TextHelper(self.editor).line_pos_from_number( markers[0].position) if top: self._job_runner.request_job(self._display_tooltip, text, top) else: self._job_runner.cancel_requests() self._previous_line = line
def _do_home_key(self, event=None, select=False): """ Performs home key action """ cursor = self.textCursor() move = QtGui.QTextCursor.MoveAnchor if select: move = QtGui.QTextCursor.KeepAnchor indent = TextHelper(self).line_indent() # Scenario 1: We're on an unindented block. In that case, we jump back # to the start of the visible line, but not all the way to the back of # the block. This is what you would expect when working with text and # line wrapping is enabled. if not indent: cursor.movePosition(QtGui.QTextCursor.StartOfLine, move) else: delta = self.textCursor().positionInBlock() - indent # Scenario 2: We're on an indented block. In that case, we move # back to the indented position. This is what you would expect when # working with code. if delta > 0: cursor.movePosition(QtGui.QTextCursor.Left, move, delta) # Scenario 3: We're on an indented block, but we're already at the # start of the indentation. In that case, we jump back to the # beginning of the block. else: cursor.movePosition(QtGui.QTextCursor.StartOfBlock, move) self.setTextCursor(cursor) if event: event.accept()
def test_selected_text(editor): helper = TextHelper(editor) helper.goto_line(2, 1, move=True) QTest.qWait(100) assert helper.word_under_cursor().selectedText() == 'T' assert helper.word_under_cursor( select_whole_word=True).selectedText() == 'This'
def cut(self): tc = self.textCursor() helper = TextHelper(self) tc.beginEditBlock() no_selection = False if not helper.current_line_text().strip(): tc.deleteChar() else: if not self.textCursor().hasSelection(): no_selection = True TextHelper(self).select_whole_line() super(CodeEdit, self).cut() if no_selection: tc.deleteChar() tc.endEditBlock() self.setTextCursor(tc)
def provide_ide_current_word(self): editor = self._current_editor() if editor is None: return u'' return TextHelper(editor).word_under_cursor( select_whole_word=True).selectedText()
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 test_indent(editor): # disable indenter mode -> indent should not do anything editor.modes.get(modes.IndenterMode).enabled = False TextHelper(editor).goto_line(0, move=True) first_line = get_first_line(editor) editor.indent() assert get_first_line(editor) == first_line editor.un_indent() assert get_first_line(editor) == first_line # enable indenter mode, call to indent/un_indent should now work editor.modes.get(modes.IndenterMode).enabled = True TextHelper(editor).goto_line(0) editor.indent() assert get_first_line(editor) == editor.tab_length * ' ' + first_line editor.un_indent() assert get_first_line(editor) == first_line
def copy(self): """ Copy the selected text to the clipboard. If no text was selected, the entire line is copied (this feature can be turned off by setting :attr:`select_line_on_copy_empty` to False. """ if self.select_line_on_copy_empty and not self.textCursor().hasSelection(): TextHelper(self).select_whole_line() super(CodeEdit, self).copy()
def on_install(self, editor): self._completer = QtWidgets.QCompleter([""], editor) self._completer.setCompletionMode(self._completer.PopupCompletion) self._completer.activated.connect(self._insert_completion) self._completer.highlighted.connect( self._on_selected_completion_changed) self._completer.setModel(QtGui.QStandardItemModel()) self._helper = TextHelper(editor) Mode.on_install(self, editor)
def zoom_in(self, increment=1): """ Zooms in the editor (makes the font bigger). :param increment: zoom level increment. Default is 1. """ self.zoom_level += increment TextHelper(self).mark_whole_doc_dirty() self._reset_stylesheet()
def goto_line(self): """ Shows the *go to line dialog* and go to the selected line. """ helper = TextHelper(self) line, result = DlgGotoLine.get_line( self, helper.current_line_nbr(), helper.line_count()) if not result: return return helper.goto_line(line, move=True)
def test_duplicate_line(editor): QTest.qWait(1000) TextHelper(editor).goto_line(0) editor.duplicate_line() assert editor.toPlainText().startswith(get_first_line(editor) + '\n' + get_first_line(editor)) editor.setPlainText('foo', '', 'utf-8') editor.duplicate_line() assert editor.toPlainText() == 'foo\nfoo' assert editor.textCursor().position() == 7
def test_clean_document(editor): TextHelper(editor).clean_document() count = TextHelper(editor).line_count() TextHelper(editor).set_line_text(0, '""" ') editor.appendPlainText("") editor.appendPlainText("") editor.appendPlainText("") assert TextHelper(editor).line_count() == count + 3 TextHelper(editor).select_lines(0, TextHelper(editor).line_count()) TextHelper(editor).clean_document() QTest.qWait(100) assert TextHelper(editor).line_count() == count
def split(self): """ Split the code editor widget, return a clone of the widget ready to be used (and synchronised with its original). Splitting the widget is done in 2 steps: - first we clone the widget, you can override ``clone`` if your widget needs additional arguments. - then we link the two text document and disable some modes on the cloned instance (such as the watcher mode). """ # cache cursor position so that the clone open at the current cursor # pos l, c = TextHelper(self).cursor_position() clone = self.clone() self.link(clone) TextHelper(clone).goto_line(l, c) self.clones.append(clone) return clone
def cut(self): """ Cuts the selected text or the whole line if no text was selected. """ tc = self.textCursor() helper = TextHelper(self) tc.beginEditBlock() no_selection = False sText = tc.selection().toPlainText() if not helper.current_line_text() and sText.count("\n") > 1: tc.deleteChar() else: if not self.textCursor().hasSelection(): no_selection = True TextHelper(self).select_whole_line() super(CodeEdit, self).cut() if no_selection: tc.deleteChar() tc.endEditBlock() self.setTextCursor(tc)
def zoom_in(self, increment=1): """ Zooms in the editor (makes the font bigger). :param increment: zoom level increment. Default is 1. """ # When called through an action, the first argument is a bool if isinstance(increment, bool): increment = 1 self.zoom_level += increment TextHelper(self).mark_whole_doc_dirty() self._reset_stylesheet()
def test_cut_no_selection(editor): assert isinstance(editor, CodeEdit) editor.setPlainText('''Line 1 Line 2 Line 3''', '', '') helper = TextHelper(editor) # eat empty line helper.goto_line(0) assert helper.line_count() == 3 editor.cut() assert helper.line_count() == 2