83 qRect = self.contentsRect()
84 self.
debugArea.setGeometry(QRect(qRect.left(),
89 if (self.horizontalScrollBar().isVisible()):
90 scrollBarHeight = self.horizontalScrollBar().height()
95 qRect.height() - scrollBarHeight))
113 """This method draws the current lineNumberArea for while"""
114 blockColor = QColor(self.palette().base().color()).darker(120)
115 if (self.palette().base().color().lightness() < 128):
116 blockColor = QColor(self.palette().base().color()).lighter(120)
117 if (self.palette().base().color().lightness() < 1):
118 blockColor = QColor(43, 43, 43)
120 painter.fillRect(event.rect(), blockColor)
122 block = self.firstVisibleBlock()
123 blockNumber = block.blockNumber()
124 top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
125 bottom = top + int(self.blockBoundingRect(block).height())
126 while block.isValid()
and top <= event.rect().bottom():
127 if block.isVisible()
and bottom >= event.rect().top():
128 number = str(blockNumber + 1)
129 painter.setPen(self.palette().text().color())
130 painter.drawText(0, top, self.
lineNumberArea.width() - 3, self.fontMetrics().height(),
131 Qt.AlignmentFlag.AlignRight, number)
135 bottom = top + int(self.blockBoundingRect(block).height())
139 if self.
scripter.debugcontroller.isActive
and self.
scripter.debugcontroller.currentLine:
140 lineNumber = self.
scripter.debugcontroller.currentLine
141 block = self.document().findBlockByLineNumber(lineNumber - 1)
144 cursor = QTextCursor(block)
145 self.setTextCursor(cursor)
148 top = int(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
152 painter.drawPixmap(QPoint(0, top), pixmap)
171 def wheelEvent(self, e):
172 """When the CTRL is pressed during the wheelEvent, zoomIn and zoomOut
174 if e.modifiers() == Qt.KeyboardModifier.ControlModifier:
175 delta = e.angleDelta().y()
181 super(CodeEditor, self).wheelEvent(e)
183 def keyPressEvent(self, e):
184 modifiers = e.modifiers()
185 if (e.key() == Qt.Key.Key_Tab):
187 elif e.key() == Qt.Key.Key_Backtab:
189 elif modifiers == MODIFIER_COMMENT and e.key() == KEY_COMMENT:
191 elif e.key() == Qt.Key.Key_Return:
192 super(CodeEditor, self).keyPressEvent(e)
195 super(CodeEditor, self).keyPressEvent(e)
197 def isEmptyBlock(self, blockNumber):
198 """ test whether block with number blockNumber contains any non-whitespace
199 If only whitespace: return true, else return false"""
202 cursor = self.textCursor()
203 cursor.movePosition(QTextCursor.MoveOperation.Start)
204 cursor.movePosition(QTextCursor.MoveOperation.NextBlock, n=blockNumber)
205 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
206 cursor.movePosition(QTextCursor.MoveOperation.EndOfLine, QTextCursor.MoveMode.KeepAnchor)
207 text = cursor.selectedText()
208 if text.strip() == "":
214 # tab key has been pressed. Indent current line or selected block by self.indent_width
216 cursor = self.textCursor()
217 # is there a selection?
219 selectionStart = cursor.selectionStart()
220 selectionEnd = cursor.selectionEnd()
222 if selectionStart == selectionEnd and cursor.atBlockEnd():
223 # ie no selection and don't insert in the middle of text
224 # something smarter might skip whitespace and add a tab in front of
225 # the next non whitespace character
226 cursor.insertText(" " * self.indent_width)
229 cursor.setPosition(selectionStart)
230 startBlock = cursor.blockNumber()
231 cursor.setPosition(selectionEnd)
232 endBlock = cursor.blockNumber()
234 cursor.movePosition(QTextCursor.MoveOperation.Start)
235 cursor.movePosition(QTextCursor.MoveOperation.NextBlock, n=startBlock)
237 for i in range(0, endBlock - startBlock + 1):
238 if not self.isEmptyBlock(startBlock + i): # Don't insert whitespace on empty lines
239 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
240 cursor.insertText(" " * self.indent_width)
242 cursor.movePosition(QTextCursor.MoveOperation.NextBlock)
244 # QT maintains separate cursors, so don't need to track or reset user's cursor
246 def dedentBlock(self, blockNumber):
247 # dedent the line at blockNumber
248 cursor = self.textCursor()
249 cursor.movePosition(QTextCursor.MoveOperation.Start)
250 cursor.movePosition(QTextCursor.MoveOperation.NextBlock, n=blockNumber)
252 for _ in range(self.indent_width):
253 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
254 cursor.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.ModeMode.KeepAnchor)
255 if cursor.selectedText() == " ": # need to test each char
256 cursor.removeSelectedText()
258 break # stop deleting!
263 cursor = self.textCursor()
264 selectionStart = cursor.selectionStart()
265 selectionEnd = cursor.selectionEnd()
267 cursor.setPosition(selectionStart)
268 startBlock = cursor.blockNumber()
269 cursor.setPosition(selectionEnd)
270 endBlock = cursor.blockNumber()
272 if endBlock < startBlock:
273 startBlock, endBlock = endBlock, startBlock
275 for blockNumber in range(startBlock, endBlock + 1):
276 self.dedentBlock(blockNumber)
278 def autoindent(self):
279 r"""The return key has just been pressed (and processed by the editor)
280 now insert leading spaces to reflect an appropriate indent level
281 against the previous line.
282 This will depend on the end of the previous line. If it ends:
283 * with a colon (:) then indent to a new indent level
284 * with a comma (,) then this is an implied continuation line, probably
285 in the middle of a function's parameter list
286 - look for last open bracket on previous line (, [ or {
287 - if found indent to that level + one character,
288 - otherwise use previous line whitespace, this is probably a list or
289 parameter list so line up with other elements
290 * with a backslash (\) then this is a continuation line, probably
291 on the RHS of an assignment
292 - similar rules as for comma, but if there is an = character
293 use that plus one indent level if that is greater
294 * if it is an open bracket of some sort treat similarly to comma
297 * anything else - a new line at the same indent level. This will preserve
298 the indent level of whitespace lines. User can shift-tab to dedent
302 cursor = self.textCursor()
303 block = cursor.block()
304 block = block.previous()
306 indentLevel = len(text) - len(text.lstrip()) # base indent level
310 lastChar = text.rstrip()[-1]
314 # work out indent level
315 if lastChar == CHAR_COLON:
316 indentLevel = indentLevel + self.indent_width
317 elif lastChar == CHAR_COMMA: # technically these are mutually exclusive so if would work
319 for c in [CHAR_OPEN_BRACE, CHAR_OPEN_BRACKET, CHAR_OPEN_SQUARE_BRACKET]:
320 braceLevels.append(text.rfind(c))
321 bracePosition = max(braceLevels)
322 if bracePosition > 0:
323 indentLevel = bracePosition + 1
324 elif lastChar == CHAR_CONTINUATION:
326 for c in [CHAR_OPEN_BRACE, CHAR_OPEN_BRACKET, CHAR_OPEN_SQUARE_BRACKET]:
327 braceLevels.append(text.rfind(c))
328 bracePosition = max(braceLevels)
329 equalPosition = text.rfind(CHAR_EQUALS)
330 if bracePosition > equalPosition:
331 indentLevel = bracePosition + 1
332 if equalPosition > bracePosition:
333 indentLevel = equalPosition + self.indent_width
334 # otherwise they're the same - ie both -1 so use base indent level
335 elif lastChar in [CHAR_OPEN_BRACE, CHAR_OPEN_BRACKET, CHAR_OPEN_SQUARE_BRACKET]:
336 indentLevel = len(text.rstrip())
339 cursor.insertText(CHAR_SPACE * indentLevel)
341 def toggleComment(self):
342 """Toggle lines of selected text to/from either comment or uncomment
343 selected text is obtained from text cursor
344 If selected text contains both commented and uncommented text this will
345 flip the state of each line - which may not be desirable.
348 cursor = self.textCursor()
349 selectionStart = cursor.selectionStart()
350 selectionEnd = cursor.selectionEnd()
352 cursor.setPosition(selectionStart)
353 startBlock = cursor.blockNumber()
354 cursor.setPosition(selectionEnd)
355 endBlock = cursor.blockNumber()
357 cursor.movePosition(QTextCursor.MoveOperation.Start)
358 cursor.movePosition(QTextCursor.MoveOperation.NextBlock, n=startBlock)
360 for _ in range(0, endBlock - startBlock + 1):
361 # Test for empty line (if the line is empty moving the cursor right will overflow
362 # to next line, throwing the line tracking off)
363 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
364 p1 = cursor.position()
365 cursor.movePosition(QTextCursor.MoveOperation.EndOfLine)
366 p2 = cursor.position()
367 if p1 == p2: # empty line - comment it
368 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
369 cursor.insertText(CHAR_COMMENT)
370 cursor.movePosition(QTextCursor.MoveOperation.NextBlock)
373 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
374 cursor.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor)
375 text = cursor.selectedText()
377 if text == CHAR_COMMENT:
378 cursor.removeSelectedText()
380 cursor.movePosition(QTextCursor.MoveOperation.StartOfLine)
381 cursor.insertText(CHAR_COMMENT)
383 cursor.movePosition(QTextCursor.MoveOperation.NextBlock)