PrEditor 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. preditor/__init__.py +315 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +165 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +318 -0
  6. preditor/constants.py +13 -0
  7. preditor/contexts.py +210 -0
  8. preditor/cores/__init__.py +0 -0
  9. preditor/cores/core.py +20 -0
  10. preditor/dccs/.hab.json +10 -0
  11. preditor/dccs/maya/PrEditor_maya.mod +1 -0
  12. preditor/dccs/maya/README.md +22 -0
  13. preditor/dccs/maya/plug-ins/PrEditor_maya.py +141 -0
  14. preditor/dccs/studiomax/PackageContents.xml +32 -0
  15. preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
  16. preditor/dccs/studiomax/README.md +17 -0
  17. preditor/dccs/studiomax/preditor.ms +16 -0
  18. preditor/dccs/studiomax/preditor_menu.mnx +7 -0
  19. preditor/debug.py +149 -0
  20. preditor/delayable_engine/__init__.py +302 -0
  21. preditor/delayable_engine/delayables.py +85 -0
  22. preditor/enum.py +728 -0
  23. preditor/excepthooks.py +165 -0
  24. preditor/gui/__init__.py +56 -0
  25. preditor/gui/app.py +163 -0
  26. preditor/gui/codehighlighter.py +289 -0
  27. preditor/gui/completer.py +237 -0
  28. preditor/gui/console.py +605 -0
  29. preditor/gui/console_base.py +911 -0
  30. preditor/gui/dialog.py +181 -0
  31. preditor/gui/drag_tab_bar.py +625 -0
  32. preditor/gui/editor_chooser.py +57 -0
  33. preditor/gui/errordialog.py +69 -0
  34. preditor/gui/find_files.py +137 -0
  35. preditor/gui/fuzzy_search/__init__.py +0 -0
  36. preditor/gui/fuzzy_search/fuzzy_search.py +97 -0
  37. preditor/gui/group_tab_widget/__init__.py +0 -0
  38. preditor/gui/group_tab_widget/group_tab_widget.py +528 -0
  39. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  40. preditor/gui/group_tab_widget/grouped_tab_models.py +107 -0
  41. preditor/gui/group_tab_widget/grouped_tab_widget.py +223 -0
  42. preditor/gui/group_tab_widget/one_tab_widget.py +96 -0
  43. preditor/gui/level_buttons.py +358 -0
  44. preditor/gui/logger_window_handler.py +77 -0
  45. preditor/gui/logger_window_plugin.py +35 -0
  46. preditor/gui/loggerwindow.py +2405 -0
  47. preditor/gui/newtabwidget.py +69 -0
  48. preditor/gui/output_console.py +11 -0
  49. preditor/gui/qtdesigner/__init__.py +21 -0
  50. preditor/gui/qtdesigner/_log_plugin.py +29 -0
  51. preditor/gui/qtdesigner/console_base_plugin.py +48 -0
  52. preditor/gui/qtdesigner/console_predit_plugin.py +48 -0
  53. preditor/gui/set_text_editor_path_dialog.py +61 -0
  54. preditor/gui/status_label.py +99 -0
  55. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  56. preditor/gui/ui/editor_chooser.ui +93 -0
  57. preditor/gui/ui/errordialog.ui +74 -0
  58. preditor/gui/ui/find_files.ui +140 -0
  59. preditor/gui/ui/loggerwindow.ui +1909 -0
  60. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  61. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  62. preditor/gui/window.py +161 -0
  63. preditor/gui/workbox_mixin.py +1139 -0
  64. preditor/gui/workbox_text_edit.py +136 -0
  65. preditor/gui/workboxwidget.py +315 -0
  66. preditor/logging_config.py +55 -0
  67. preditor/osystem.py +401 -0
  68. preditor/plugins.py +118 -0
  69. preditor/prefs.py +381 -0
  70. preditor/resource/environment_variables.html +26 -0
  71. preditor/resource/error_mail.html +85 -0
  72. preditor/resource/error_mail_inline.html +41 -0
  73. preditor/resource/img/README.md +17 -0
  74. preditor/resource/img/arrow_forward.png +0 -0
  75. preditor/resource/img/check-bold.png +0 -0
  76. preditor/resource/img/chevron-down.png +0 -0
  77. preditor/resource/img/chevron-up.png +0 -0
  78. preditor/resource/img/close-thick.png +0 -0
  79. preditor/resource/img/comment-edit.png +0 -0
  80. preditor/resource/img/content-copy.png +0 -0
  81. preditor/resource/img/content-cut.png +0 -0
  82. preditor/resource/img/content-duplicate.png +0 -0
  83. preditor/resource/img/content-paste.png +0 -0
  84. preditor/resource/img/content-save.png +0 -0
  85. preditor/resource/img/debug_disabled.png +0 -0
  86. preditor/resource/img/eye-check.png +0 -0
  87. preditor/resource/img/file-plus.png +0 -0
  88. preditor/resource/img/file-remove.png +0 -0
  89. preditor/resource/img/format-align-left.png +0 -0
  90. preditor/resource/img/format-letter-case-lower.png +0 -0
  91. preditor/resource/img/format-letter-case-upper.png +0 -0
  92. preditor/resource/img/format-letter-case.svg +1 -0
  93. preditor/resource/img/information.png +0 -0
  94. preditor/resource/img/logging_critical.png +0 -0
  95. preditor/resource/img/logging_custom.png +0 -0
  96. preditor/resource/img/logging_debug.png +0 -0
  97. preditor/resource/img/logging_error.png +0 -0
  98. preditor/resource/img/logging_info.png +0 -0
  99. preditor/resource/img/logging_not_set.png +0 -0
  100. preditor/resource/img/logging_warning.png +0 -0
  101. preditor/resource/img/marker.png +0 -0
  102. preditor/resource/img/play.png +0 -0
  103. preditor/resource/img/playlist-play.png +0 -0
  104. preditor/resource/img/plus-minus-variant.png +0 -0
  105. preditor/resource/img/preditor.ico +0 -0
  106. preditor/resource/img/preditor.png +0 -0
  107. preditor/resource/img/preditor.psd +0 -0
  108. preditor/resource/img/preditor.svg +44 -0
  109. preditor/resource/img/regex.svg +1 -0
  110. preditor/resource/img/restart.svg +1 -0
  111. preditor/resource/img/skip-forward-outline.png +0 -0
  112. preditor/resource/img/skip-next-outline.png +0 -0
  113. preditor/resource/img/skip-next.png +0 -0
  114. preditor/resource/img/skip-previous.png +0 -0
  115. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  116. preditor/resource/img/text-search-variant.png +0 -0
  117. preditor/resource/img/warning-big.png +0 -0
  118. preditor/resource/lang/python.json +30 -0
  119. preditor/resource/pref_updates/pref_updates.json +17 -0
  120. preditor/resource/settings.ini +25 -0
  121. preditor/resource/stylesheet/Bright.css +76 -0
  122. preditor/resource/stylesheet/Dark.css +210 -0
  123. preditor/scintilla/__init__.py +40 -0
  124. preditor/scintilla/delayables/__init__.py +11 -0
  125. preditor/scintilla/delayables/smart_highlight.py +97 -0
  126. preditor/scintilla/delayables/spell_check.py +174 -0
  127. preditor/scintilla/documenteditor.py +1924 -0
  128. preditor/scintilla/finddialog.py +68 -0
  129. preditor/scintilla/lang/__init__.py +80 -0
  130. preditor/scintilla/lang/config/bash.ini +15 -0
  131. preditor/scintilla/lang/config/batch.ini +14 -0
  132. preditor/scintilla/lang/config/cpp.ini +19 -0
  133. preditor/scintilla/lang/config/css.ini +19 -0
  134. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  135. preditor/scintilla/lang/config/html.ini +21 -0
  136. preditor/scintilla/lang/config/javascript.ini +24 -0
  137. preditor/scintilla/lang/config/lua.ini +16 -0
  138. preditor/scintilla/lang/config/maxscript.ini +20 -0
  139. preditor/scintilla/lang/config/mel.ini +18 -0
  140. preditor/scintilla/lang/config/mu.ini +22 -0
  141. preditor/scintilla/lang/config/nsi.ini +19 -0
  142. preditor/scintilla/lang/config/perl.ini +19 -0
  143. preditor/scintilla/lang/config/puppet.ini +19 -0
  144. preditor/scintilla/lang/config/python.ini +28 -0
  145. preditor/scintilla/lang/config/ruby.ini +19 -0
  146. preditor/scintilla/lang/config/sql.ini +7 -0
  147. preditor/scintilla/lang/config/xml.ini +21 -0
  148. preditor/scintilla/lang/config/yaml.ini +18 -0
  149. preditor/scintilla/lang/language.py +240 -0
  150. preditor/scintilla/lexers/__init__.py +0 -0
  151. preditor/scintilla/lexers/cpplexer.py +22 -0
  152. preditor/scintilla/lexers/javascriptlexer.py +27 -0
  153. preditor/scintilla/lexers/maxscriptlexer.py +235 -0
  154. preditor/scintilla/lexers/mellexer.py +369 -0
  155. preditor/scintilla/lexers/mulexer.py +33 -0
  156. preditor/scintilla/lexers/pythonlexer.py +42 -0
  157. preditor/scintilla/ui/finddialog.ui +160 -0
  158. preditor/settings.py +71 -0
  159. preditor/stream/__init__.py +72 -0
  160. preditor/stream/console_handler.py +169 -0
  161. preditor/stream/director.py +144 -0
  162. preditor/stream/manager.py +97 -0
  163. preditor/streamhandler_helper.py +46 -0
  164. preditor/utils/__init__.py +191 -0
  165. preditor/utils/call_stack.py +86 -0
  166. preditor/utils/cute.py +106 -0
  167. preditor/utils/stylesheets.py +54 -0
  168. preditor/utils/text_search.py +338 -0
  169. preditor/version.py +34 -0
  170. preditor/weakref.py +363 -0
  171. preditor-2.1.0.dist-info/METADATA +308 -0
  172. preditor-2.1.0.dist-info/RECORD +179 -0
  173. preditor-2.1.0.dist-info/WHEEL +5 -0
  174. preditor-2.1.0.dist-info/entry_points.txt +19 -0
  175. preditor-2.1.0.dist-info/licenses/LICENSE +165 -0
  176. preditor-2.1.0.dist-info/top_level.txt +3 -0
  177. tests/encodings/test_ecoding.py +33 -0
  178. tests/find_files/test_find_files.py +74 -0
  179. tests/ide/test_delayable_engine.py +171 -0
@@ -0,0 +1,605 @@
1
+ from __future__ import absolute_import
2
+
3
+ import re
4
+ import string
5
+ import sys
6
+ import time
7
+ import traceback
8
+ from builtins import str as text
9
+ from functools import partial
10
+ from typing import Optional
11
+
12
+ import __main__
13
+ from Qt.QtCore import QPoint, Qt, QTimer
14
+ from Qt.QtGui import QKeySequence, QTextCursor, QTextDocument
15
+ from Qt.QtWidgets import QAbstractItemView, QAction, QApplication, QWidget
16
+
17
+ from .. import settings
18
+ from ..constants import StreamType
19
+ from ..utils import Truncate
20
+ from ..utils.cute import QtPropertyInit
21
+ from .completer import PythonCompleter
22
+ from .console_base import ConsoleBase
23
+ from .loggerwindow import LoggerWindow
24
+
25
+
26
+ class ConsolePrEdit(ConsoleBase):
27
+ # Ensure the error prompt only shows up once.
28
+ _errorPrompted = False
29
+
30
+ _consolePrompt = '>>> '
31
+
32
+ # Note: Changing _outputPrompt may require updating resource\lang\python.xml
33
+ # If still using a #
34
+ _outputPrompt = '#Result: '
35
+
36
+ def __init__(self, parent: QWidget, controller: Optional[LoggerWindow] = None):
37
+ super(ConsolePrEdit, self).__init__(parent, controller=controller)
38
+
39
+ # Method used to update the gui when code is executed
40
+ self.clearExecutionTime = None
41
+ self.reportExecutionTime = None
42
+
43
+ # store the error buffer
44
+ self._completer = None
45
+
46
+ # When executing code, that takes longer than this seconds, flash the window
47
+ self.flash_window = None
48
+
49
+ # Store previous commands to retrieve easily
50
+ self._prevCommands = []
51
+ self._prevCommandIndex = 0
52
+ self._prevCommandsMax = 100
53
+
54
+ # create the completer
55
+ self.setCompleter(PythonCompleter(self))
56
+
57
+ self.uiClearToLastPromptACT = QAction('Clear to Last', self)
58
+ self.uiClearToLastPromptACT.triggered.connect(self.clearToLastPrompt)
59
+ self.uiClearToLastPromptACT.setShortcut(
60
+ QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_Backspace)
61
+ )
62
+ self.addAction(self.uiClearToLastPromptACT)
63
+
64
+ # Make sure console cursor is visible. It can get it's width set to 0 with
65
+ # unusual(ie not 100%) os display scaling.
66
+ if not self.cursorWidth():
67
+ self.setCursorWidth(1)
68
+
69
+ # The act of changing from no scroll bar to a scroll bar can add up to 1
70
+ # second of time to the process of outputting text, so, just always have
71
+ # it on.
72
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
73
+
74
+ def doubleSingleShotSetScrollValue(self, origPercent):
75
+ """This double QTimer.singleShot monkey business seems to be the only way
76
+ to get scroll.maximum() to update properly so that we calc newValue
77
+ correctly. It's quite silly. Apparently, the important part is that
78
+ calling scroll.maximum() has had a pause since the font had been set.
79
+ """
80
+
81
+ def singleShotSetScrollValue(self, origPercent):
82
+ scroll = self.verticalScrollBar()
83
+ maximum = scroll.maximum()
84
+ if maximum is not None:
85
+ newValue = round(origPercent * maximum)
86
+ QTimer.singleShot(1, partial(scroll.setValue, newValue))
87
+
88
+ # The 100 ms timer amount is somewhat arbitrary. It must be more than
89
+ # some value to work, but what that value is is unknown, and may change
90
+ # under various circumstances. Briefly disable updates for smoother transition.
91
+ self.setUpdatesEnabled(False)
92
+ try:
93
+ QTimer.singleShot(100, partial(singleShotSetScrollValue, self, origPercent))
94
+ finally:
95
+ self.setUpdatesEnabled(True)
96
+
97
+ def keyReleaseEvent(self, event):
98
+ """Override of keyReleaseEvent to determine when to end navigation of
99
+ previous commands
100
+ """
101
+ if event.key() == Qt.Key.Key_Alt:
102
+ self._prevCommandIndex = 0
103
+ else:
104
+ event.ignore()
105
+
106
+ def getPrevCommand(self):
107
+ """Find and display the previous command in stack"""
108
+ self._prevCommandIndex -= 1
109
+
110
+ if abs(self._prevCommandIndex) > len(self._prevCommands):
111
+ self._prevCommandIndex += 1
112
+
113
+ if self._prevCommands:
114
+ self.setCommand()
115
+
116
+ def getNextCommand(self):
117
+ """Find and display the next command in stack"""
118
+ self._prevCommandIndex += 1
119
+ self._prevCommandIndex = min(self._prevCommandIndex, 0)
120
+
121
+ if self._prevCommands:
122
+ self.setCommand()
123
+
124
+ def setCommand(self):
125
+ """Do the displaying of currently chosen command"""
126
+ prevCommand = ''
127
+ if self._prevCommandIndex:
128
+ prevCommand = self._prevCommands[self._prevCommandIndex]
129
+
130
+ cursor = self.textCursor()
131
+ cursor.select(QTextCursor.SelectionType.LineUnderCursor)
132
+ if cursor.selectedText().startswith(self._consolePrompt):
133
+ prevCommand = "{}{}".format(self._consolePrompt, prevCommand)
134
+ cursor.insertText(prevCommand)
135
+ self.setTextCursor(cursor)
136
+
137
+ def clear(self):
138
+ """clears the text in the editor"""
139
+ super(ConsoleBase, self).clear()
140
+ self.startInputLine()
141
+ # Note: Don't use the regular `super()` call here as it would result
142
+ # in multiple calls to repaint, just call the base Qt class's clear.
143
+ self.maybeRepaint(force=True)
144
+
145
+ def clearToLastPrompt(self):
146
+ # store the current cursor position so we can restore when we are done
147
+ currentCursor = self.textCursor()
148
+ # move to the end of the document so we can search backwards
149
+ cursor = self.textCursor()
150
+ cursor.movePosition(QTextCursor.MoveOperation.End)
151
+ self.setTextCursor(cursor)
152
+ # Check if the last line is a empty prompt. If so, then preform two finds so we
153
+ # find the prompt we are looking for instead of this empty prompt
154
+ findCount = (
155
+ 2 if self.toPlainText()[-len(self.prompt()) :] == self.prompt() else 1
156
+ )
157
+ for _ in range(findCount):
158
+ self.find(self.prompt(), QTextDocument.FindFlag.FindBackward)
159
+ # move to the end of the found line, select the rest of the text and remove it
160
+ # preserving history if there is anything to remove.
161
+ cursor = self.textCursor()
162
+ cursor.movePosition(QTextCursor.MoveOperation.EndOfLine)
163
+ cursor.movePosition(
164
+ QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor
165
+ )
166
+ txt = cursor.selectedText()
167
+ if txt:
168
+ self.setTextCursor(cursor)
169
+ self.insertPlainText('')
170
+ # Restore the cursor position to its original location
171
+ self.setTextCursor(currentCursor)
172
+
173
+ def completer(self):
174
+ """returns the completer instance that is associated with this editor"""
175
+ return self._completer
176
+
177
+ def executeString(
178
+ self,
179
+ commandText,
180
+ consoleLine=None,
181
+ filename='<ConsolePrEdit>',
182
+ extraPrint=True,
183
+ echoResult=False,
184
+ truncate=False,
185
+ ):
186
+ # These vars helps with faking code lines in tracebacks for stdin input, which
187
+ # workboxes are, and py3 doesn't include in the traceback
188
+ self.consoleLine = consoleLine or ""
189
+
190
+ if self.clearExecutionTime is not None:
191
+ self.clearExecutionTime()
192
+ cursor = self.textCursor()
193
+ cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
194
+ line = cursor.selectedText()
195
+ if line and line[0] not in string.printable:
196
+ line = line[1:]
197
+
198
+ if line.startswith(self.prompt()) and extraPrint:
199
+ self.write("\n", stream_type=StreamType.RESULT)
200
+
201
+ cmdresult = None
202
+ # https://stackoverflow.com/a/29456463
203
+ # If you want to get the result of the code, you have to call eval
204
+ # however eval does not accept multiple statements. For that you need
205
+ # exec which has no Return.
206
+ wasEval = False
207
+ startTime = time.time()
208
+
209
+ try:
210
+ compiled = compile(commandText, filename, 'eval')
211
+ wasEval = True
212
+ except Exception:
213
+ compiled = compile(commandText, filename, 'exec')
214
+
215
+ # We wrap in try / finally so that elapsed time gets updated, even when an
216
+ # exception is raised.
217
+ try:
218
+ if wasEval:
219
+ cmdresult = eval(compiled, __main__.__dict__, __main__.__dict__)
220
+ else:
221
+ exec(compiled, __main__.__dict__, __main__.__dict__)
222
+ finally:
223
+ # Report the total time it took to execute this code.
224
+ if self.reportExecutionTime is not None:
225
+ delta = time.time() - startTime
226
+ self.reportExecutionTime((delta, commandText))
227
+
228
+ # Provide user feedback when running long code execution.
229
+ if self.controller:
230
+ flash_time = self.controller.uiFlashTimeSPIN.value()
231
+ if self.flash_window and flash_time and delta >= flash_time:
232
+ if settings.OS_TYPE == "Windows":
233
+ try:
234
+ from casement import utils
235
+ except ImportError:
236
+ # If casement is not installed, flash window is disabled
237
+ pass
238
+ else:
239
+ hwnd = int(self.flash_window.winId())
240
+ utils.flash_window(hwnd)
241
+
242
+ if echoResult and wasEval:
243
+ # If the selected code was a statement print the result of the statement.
244
+ ret = repr(cmdresult)
245
+ self.startOutputLine()
246
+ if truncate:
247
+ self.write(
248
+ Truncate(ret).middle(100) + "\n", stream_type=StreamType.RESULT
249
+ )
250
+ else:
251
+ self.write(ret + "\n", stream_type=StreamType.RESULT)
252
+
253
+ return cmdresult, wasEval
254
+
255
+ def executeCommand(self):
256
+ """executes the current line of code"""
257
+
258
+ # Not using workbox, so clear this
259
+ self.consoleLine = ""
260
+
261
+ # grab the command from the line
262
+ block = self.textCursor().block().text()
263
+ p = '{prompt}(.*)'.format(prompt=re.escape(self.prompt()))
264
+ results = re.search(p, block)
265
+ if results:
266
+ commandText = results.groups()[0]
267
+ # if the cursor position is at the end of the line
268
+ if self.textCursor().atEnd():
269
+ # insert a new line
270
+ self.insertPlainText('\n')
271
+
272
+ # update prevCommands list, but only if commandText is not the most
273
+ # recent prevCommand, or there are no previous commands
274
+ hasText = len(commandText) > 0
275
+ prevCmds = self._prevCommands
276
+ notPrevCmd = not prevCmds or prevCmds[-1] != commandText
277
+ if hasText and notPrevCmd:
278
+ self._prevCommands.append(commandText)
279
+ # limit length of prevCommand list to max number of prev commands
280
+ self._prevCommands = self._prevCommands[-1 * self._prevCommandsMax :]
281
+
282
+ # evaluate the command
283
+ cmdresult, wasEval = self.executeString(
284
+ commandText, consoleLine=commandText
285
+ )
286
+
287
+ # print the resulting commands
288
+ if cmdresult is not None:
289
+ # When writing to additional stdout's not including a new line
290
+ # makes the output not match the formatting you get inside the
291
+ # console.
292
+ self.write(u'{}\n'.format(cmdresult))
293
+ # NOTE: I am using u'' above so unicode strings in python 2
294
+ # don't get converted to str objects.
295
+
296
+ self.startInputLine()
297
+
298
+ # otherwise, move the command to the end of the line
299
+ else:
300
+ self.startInputLine()
301
+ self.insertPlainText(commandText)
302
+
303
+ # if no command, then start a new line
304
+ else:
305
+ self.startInputLine()
306
+
307
+ def flush(self):
308
+ pass
309
+
310
+ def focusInEvent(self, event):
311
+ """overload the focus in event to ensure the completer has the proper widget"""
312
+ if self.completer():
313
+ self.completer().setWidget(self)
314
+ super().focusInEvent(event)
315
+
316
+ def insertCompletion(self, completion):
317
+ """inserts the completion text into the editor"""
318
+ if self.completer().widget() == self:
319
+ cursor = self.textCursor()
320
+ cursor.select(QTextCursor.SelectionType.WordUnderCursor)
321
+ cursor.insertText(completion)
322
+ self.setTextCursor(cursor)
323
+
324
+ def insertFromMimeData(self, mimeData):
325
+ html = False
326
+ if mimeData.hasHtml():
327
+ txt = mimeData.html()
328
+ html = True
329
+ else:
330
+ txt = mimeData.text()
331
+
332
+ doc = QTextDocument()
333
+
334
+ if html:
335
+ doc.setHtml(txt)
336
+ else:
337
+ doc.setPlainText(txt)
338
+
339
+ txt = doc.toPlainText()
340
+
341
+ exp = re.compile(
342
+ (
343
+ r'[^A-Za-z0-9\~\!\@\#\$\%\^\&\*\(\)\_\+\{\}\|\:'
344
+ r'\"\<\>\?\`\-\=\[\]\\\;\'\,\.\/ \t\n]'
345
+ )
346
+ )
347
+ newText = text(txt)
348
+ for each in exp.findall(newText):
349
+ newText = newText.replace(each, '?')
350
+
351
+ self.insertPlainText(newText)
352
+
353
+ def isatty(self):
354
+ """Return True if the stream is interactive (i.e., connected to a terminal/tty
355
+ device).
356
+ """
357
+ # This method is required for pytest to run in a DCC. Returns False so the
358
+ # output does not contain cursor control characters that disrupt the visual
359
+ # display of the output.
360
+ return False
361
+
362
+ def lastError(self):
363
+ try:
364
+ return ''.join(
365
+ traceback.format_exception(
366
+ sys.last_type, sys.last_value, sys.last_traceback
367
+ )
368
+ )
369
+ except AttributeError:
370
+ # last_traceback, last_type and last_value do not always exist
371
+ return ''
372
+
373
+ def keyPressEvent(self, event):
374
+ """overload the key press event to handle custom events"""
375
+
376
+ completer = self.completer()
377
+
378
+ # Define prefix so we can determine if the exact prefix is in
379
+ # completions and highlight it. We must manually add the currently typed
380
+ # character, or remove it if backspace or delete has just been pressed.
381
+ key = event.text()
382
+ _, prefix = completer.currentObject(scope=__main__.__dict__)
383
+ isBackspaceOrDel = event.key() in (Qt.Key.Key_Backspace, Qt.Key.Key_Delete)
384
+ if key.isalnum() or key in ("-", "_"):
385
+ prefix += str(key)
386
+ elif isBackspaceOrDel and prefix:
387
+ prefix = prefix[:-1]
388
+
389
+ if completer and event.key() in (
390
+ Qt.Key.Key_Backspace,
391
+ Qt.Key.Key_Delete,
392
+ Qt.Key.Key_Escape,
393
+ ):
394
+ completer.hideDocumentation()
395
+
396
+ # enter || return keys will execute the command
397
+ if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
398
+ if completer.popup().isVisible():
399
+ completer.clear()
400
+ event.ignore()
401
+ else:
402
+ self.executeCommand()
403
+
404
+ # home key will move the cursor to home
405
+ elif event.key() == Qt.Key.Key_Home:
406
+ self.moveToHome()
407
+
408
+ # otherwise, ignore the event for completion events
409
+ elif event.key() in (Qt.Key.Key_Tab, Qt.Key.Key_Backtab):
410
+ if not completer.popup().isVisible():
411
+ # The completer does not get updated if its not visible while typing.
412
+ # We are about to complete the text using it so ensure its updated.
413
+ completer.refreshList(scope=__main__.__dict__)
414
+ completer.popup().setCurrentIndex(
415
+ completer.completionModel().index(0, 0)
416
+ )
417
+ # Insert the correct text and clear the completion model
418
+ index = completer.popup().currentIndex()
419
+ self.insertCompletion(index.data(Qt.ItemDataRole.DisplayRole))
420
+ completer.clear()
421
+
422
+ elif event.key() == Qt.Key.Key_Escape and completer.popup().isVisible():
423
+ completer.clear()
424
+
425
+ # other wise handle the keypress
426
+ else:
427
+ # define special key sequences
428
+ modifiers = QApplication.instance().keyboardModifiers()
429
+ ctrlSpace = (
430
+ event.key() == Qt.Key.Key_Space
431
+ and modifiers == Qt.KeyboardModifier.ControlModifier
432
+ )
433
+ ctrlM = (
434
+ event.key() == Qt.Key.Key_M
435
+ and modifiers == Qt.KeyboardModifier.ControlModifier
436
+ )
437
+ ctrlI = (
438
+ event.key() == Qt.Key.Key_I
439
+ and modifiers == Qt.KeyboardModifier.ControlModifier
440
+ )
441
+
442
+ # Process all events we do not want to override
443
+ if not (ctrlSpace or ctrlM or ctrlI):
444
+ super().keyPressEvent(event)
445
+
446
+ if self.controller:
447
+ if ctrlI:
448
+ self.controller.toggleCaseSensitive()
449
+ if ctrlM:
450
+ self.controller.cycleCompleterMode()
451
+
452
+ # check for particular events for the completion
453
+ if completer:
454
+ # look for documentation popups
455
+ if event.key() == Qt.Key.Key_ParenLeft:
456
+ rect = self.cursorRect()
457
+ point = self.mapToGlobal(QPoint(rect.x(), rect.y()))
458
+ completer.showDocumentation(pos=point, scope=__main__.__dict__)
459
+
460
+ # hide documentation popups
461
+ elif event.key() == Qt.Key.Key_ParenRight:
462
+ completer.hideDocumentation()
463
+
464
+ # determine if we need to show the popup or if it already is visible, we
465
+ # need to update it
466
+ elif (
467
+ event.key() == Qt.Key.Key_Period
468
+ or event.key() == Qt.Key.Key_Escape
469
+ or completer.popup().isVisible()
470
+ or ctrlSpace
471
+ or ctrlI
472
+ or ctrlM
473
+ or completer.wasCompletingCounter
474
+ ):
475
+ completer.refreshList(scope=__main__.__dict__)
476
+
477
+ model = completer.completionModel()
478
+ index = model.index(0, 0)
479
+
480
+ # If option chosen, if the exact prefix exists in the
481
+ # possible completions, highlight it, even if it's not the
482
+ # topmost completion.
483
+ if (
484
+ self.controller
485
+ and self.controller.uiHighlightExactCompletionCHK.isChecked()
486
+ ):
487
+ for i in range(completer.completionCount()):
488
+ completer.setCurrentRow(i)
489
+ curCompletion = completer.currentCompletion()
490
+ if prefix == curCompletion:
491
+ index = model.index(i, 0)
492
+ break
493
+ elif prefix == curCompletion.lower():
494
+ index = model.index(i, 0)
495
+ break
496
+
497
+ # Set completer current Row, so finishing the completer will use
498
+ # correct text
499
+ completer.setCurrentRow(index.row())
500
+
501
+ # Make sure that current selection is visible, ie scroll to it
502
+ completer.popup().scrollTo(
503
+ index, QAbstractItemView.ScrollHint.EnsureVisible
504
+ )
505
+
506
+ # show the completer for the rect
507
+ rect = self.cursorRect()
508
+ rect.setWidth(
509
+ completer.popup().sizeHintForColumn(0)
510
+ + completer.popup().verticalScrollBar().sizeHint().width()
511
+ )
512
+ completer.complete(rect)
513
+
514
+ if completer.popup().isVisible():
515
+ completer.wasCompleting = True
516
+ completer.wasCompletingCounter = 0
517
+
518
+ if completer.wasCompleting and not completer.popup().isVisible():
519
+ wasCompletingCounterMax = completer.wasCompletingCounterMax
520
+ if completer.wasCompletingCounter <= wasCompletingCounterMax:
521
+ if event.key() not in (Qt.Key.Key_Backspace, Qt.Key.Key_Left):
522
+ completer.wasCompletingCounter += 1
523
+ else:
524
+ completer.wasCompletingCounter = 0
525
+ completer.wasCompleting = False
526
+
527
+ def moveToHome(self):
528
+ """moves the cursor to the home location"""
529
+ mode = QTextCursor.MoveMode.MoveAnchor
530
+ # select the home
531
+ if (
532
+ QApplication.instance().keyboardModifiers()
533
+ == Qt.KeyboardModifier.ShiftModifier
534
+ ):
535
+ mode = QTextCursor.MoveMode.KeepAnchor
536
+ # grab the cursor
537
+ cursor = self.textCursor()
538
+ if (
539
+ QApplication.instance().keyboardModifiers()
540
+ == Qt.KeyboardModifier.ControlModifier
541
+ ):
542
+ # move to the top of the document if control is pressed
543
+ cursor.movePosition(QTextCursor.MoveOperation.Start)
544
+ else:
545
+ # Otherwise just move it to the start of the line
546
+ cursor.movePosition(QTextCursor.MoveOperation.StartOfBlock, mode)
547
+ # move the cursor to the end of the prompt.
548
+ cursor.movePosition(QTextCursor.MoveOperation.Right, mode, len(self.prompt()))
549
+ self.setTextCursor(cursor)
550
+
551
+ def onFirstShow(self, event) -> bool:
552
+ if not super().onFirstShow(event):
553
+ # It's already been shown, nothing to do.
554
+ return False
555
+
556
+ # This is the first showing of this widget, ensure the first input
557
+ # prompt is styled by any active stylesheet
558
+ self.startInputLine()
559
+ return True
560
+
561
+ def outputPrompt(self):
562
+ """The prompt used to output a result."""
563
+ return self._outputPrompt
564
+
565
+ def prompt(self):
566
+ return self._consolePrompt
567
+
568
+ def setCompleter(self, completer):
569
+ """sets the completer instance for this widget"""
570
+ if completer:
571
+ self._completer = completer
572
+ completer.setWidget(self)
573
+ completer.activated.connect(self.insertCompletion)
574
+
575
+ def startInputLine(self):
576
+ """create a new command prompt line"""
577
+ self.startPrompt(self.prompt())
578
+ self._prevCommandIndex = 0
579
+
580
+ def startOutputLine(self):
581
+ """Create a new line to show output text."""
582
+ self.startPrompt(self._outputPrompt)
583
+
584
+ def removeCurrentLine(self):
585
+ self.moveCursor(QTextCursor.MoveOperation.End, QTextCursor.MoveMode.MoveAnchor)
586
+ self.moveCursor(
587
+ QTextCursor.MoveOperation.StartOfLine, QTextCursor.MoveMode.MoveAnchor
588
+ )
589
+ self.moveCursor(QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor)
590
+ self.textCursor().removeSelectedText()
591
+ self.textCursor().deletePreviousChar()
592
+ self.insertPlainText("\n")
593
+
594
+ # This main console should have these settings enabled by default
595
+ stream_clear = QtPropertyInit('_stream_clear', True)
596
+ stream_disable_writes = QtPropertyInit('_stream_disable_writes', True)
597
+ stream_replay = QtPropertyInit('_stream_replay', True)
598
+ stream_echo_stderr = QtPropertyInit(
599
+ '_stream_echo_stderr', True, callback=ConsoleBase.update_streams
600
+ )
601
+ stream_echo_stdout = QtPropertyInit(
602
+ '_stream_echo_stdout', True, callback=ConsoleBase.update_streams
603
+ )
604
+ stream_echo_result = QtPropertyInit("_stream_echo_result", True)
605
+ """Enable StreamType.RESULT output when running code using PrEditor."""