PrEditor 0.4.0__py2.py3-none-any.whl → 0.5.0__py2.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.

Potentially problematic release.


This version of PrEditor might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PrEditor
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A python REPL and Editor and console based on Qt.
5
5
  Home-page: https://github.com/blurstudio/PrEditor.git
6
6
  Author: Blur Studio
@@ -20,25 +20,25 @@ Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: Qt.py
23
- Requires-Dist: configparser >=4.0.2
24
- Requires-Dist: future >=0.18.2
25
- Requires-Dist: python-redmine >=2.1.1
26
- Requires-Dist: signalslot >=0.1.2
27
- Requires-Dist: importlib-metadata >=4.8.3 ; python_version >= "3.6"
23
+ Requires-Dist: configparser>=4.0.2
24
+ Requires-Dist: future>=0.18.2
25
+ Requires-Dist: python-redmine>=2.1.1
26
+ Requires-Dist: signalslot>=0.1.2
27
+ Requires-Dist: importlib-metadata>=4.8.3; python_version >= "3.6"
28
28
  Provides-Extra: cli
29
- Requires-Dist: click >=7.1.2 ; extra == 'cli'
30
- Requires-Dist: click-default-group ; extra == 'cli'
29
+ Requires-Dist: click>=7.1.2; extra == "cli"
30
+ Requires-Dist: click-default-group; extra == "cli"
31
31
  Provides-Extra: dev
32
- Requires-Dist: black ; extra == 'dev'
33
- Requires-Dist: covdefaults ; extra == 'dev'
34
- Requires-Dist: coverage ; extra == 'dev'
35
- Requires-Dist: flake8 ; extra == 'dev'
36
- Requires-Dist: flake8-bugbear ; extra == 'dev'
37
- Requires-Dist: pep8-naming ; extra == 'dev'
38
- Requires-Dist: pytest ; extra == 'dev'
39
- Requires-Dist: tox ; extra == 'dev'
32
+ Requires-Dist: black; extra == "dev"
33
+ Requires-Dist: covdefaults; extra == "dev"
34
+ Requires-Dist: coverage; extra == "dev"
35
+ Requires-Dist: flake8; extra == "dev"
36
+ Requires-Dist: flake8-bugbear; extra == "dev"
37
+ Requires-Dist: pep8-naming; extra == "dev"
38
+ Requires-Dist: pytest; extra == "dev"
39
+ Requires-Dist: tox; extra == "dev"
40
40
  Provides-Extra: shortcut
41
- Requires-Dist: casement >=0.1.0 ; (platform_system == "Windows") and extra == 'shortcut'
41
+ Requires-Dist: casement>=0.1.0; platform_system == "Windows" and extra == "shortcut"
42
42
 
43
43
  # PrEditor
44
44
 
@@ -11,7 +11,7 @@ preditor/plugins.py,sha256=W3DfdDEE5DtEXOXioEyIe4tuIVV1V-RLcV8LoZJWpWU,1916
11
11
  preditor/prefs.py,sha256=BPtSsdv2yuiRpIaqEml9fxlVYKHNfqQ77hp5YIQRDBg,2172
12
12
  preditor/settings.py,sha256=DV9_DbJorEnhdIvW15E7h7PswlQUsy0UlA8bXUYN0og,2206
13
13
  preditor/streamhandler_helper.py,sha256=kiU6T9WqJ3JKTTKCa7IUU8brwK7zO5UUpEzLhEfKe44,1788
14
- preditor/version.py,sha256=LXsVgL30inwYURXgt8agwHpCAGkWqXpMAmFq8N7WB4Q,142
14
+ preditor/version.py,sha256=qOgp63DKbJUTEpeNFSC6-4Z4ggiiiujx7_nJvXchvEg,142
15
15
  preditor/weakref.py,sha256=b--KomFAHcMWr3DEAIN2j3XxRhjDWKw0WABXyn1nxDg,12177
16
16
  preditor/cores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  preditor/cores/core.py,sha256=ZXB4bts05DMQtc-dtkk81chIWNqYXImMuWCYuUHwLdA,2389
@@ -23,36 +23,38 @@ preditor/gui/__init__.py,sha256=vie2aDVpGjrDYxK1H2pTVIgFjIk6B8nNf6rWNwCVJ3Y,3132
23
23
  preditor/gui/app.py,sha256=RRiaDpUdWYoVkCFThMfsSSFG63jRt7LKx2QOx8lSdK4,5810
24
24
  preditor/gui/codehighlighter.py,sha256=fYrEi2qzKmZcTOCv3qRymGAqC7z4bRzO9IGa6cn-LF4,7199
25
25
  preditor/gui/completer.py,sha256=T6fl_4xGVjVtGNuR6ajD1ngjEhrKR6tZ9zPJDYoKn8E,7379
26
- preditor/gui/console.py,sha256=_5v4Vh_04wz1oqaxnZ8Rp818x3igz6Tumgo3-T7TCnA,30759
26
+ preditor/gui/console.py,sha256=AW4FbiJ3kiw9w_mCJ-ZlwdTSRDpCsr4de3RT_ezN25U,35383
27
27
  preditor/gui/dialog.py,sha256=Vw8Wflb2P3TngO8b-2suWP6JKHBVHdbGgYiRDKzYRlQ,7488
28
28
  preditor/gui/drag_tab_bar.py,sha256=5J-BSKQzS6_WuYxxGqmnUJr6AriTAvwfVK7Q3p7knrM,7933
29
- preditor/gui/editor_chooser.py,sha256=v82SPuxPgai3XiRWi9ypT0aRkbV6_IrbfBoAljM9pRY,1986
29
+ preditor/gui/editor_chooser.py,sha256=lv1eY0UJOulX1l-P-ZQEoneYz6BNX2VkXEbg3GUu1ag,1991
30
30
  preditor/gui/errordialog.py,sha256=FZzSykNtqgTZ-CKEsLFXfcw_k8zwx7g_aMaHbpnq2xI,3110
31
31
  preditor/gui/find_files.py,sha256=vh19jY571Z4E9IWSL4VyYcNJWYXzwxBHzBOzxWjI9QM,4223
32
32
  preditor/gui/level_buttons.py,sha256=yPFIWKc0bgKLrP2XHyBqNuvvTnXZqGdtN_p27jSb1Og,11925
33
33
  preditor/gui/logger_window_handler.py,sha256=VTNhaoFUnConE3NHj9KaDZlVoifix8xCbCuN5Ozjz0M,1482
34
- preditor/gui/loggerwindow.py,sha256=hSVNH36CL_zrHStNqfjN-uiXnMUYlWXt6an8meVfifE,47760
34
+ preditor/gui/loggerwindow.py,sha256=B4QjU1VjCD6YnbDAnatC-rvi7p_u_yHx9KwIwtF1mYE,51285
35
35
  preditor/gui/newtabwidget.py,sha256=5aCWn9xAl5h1oZACqVuEsOAbzKTS2RegrLI41gROC8A,1971
36
36
  preditor/gui/redmine_login_dialog.py,sha256=cMPBuecSZD5yjycNkMwHa1AbdwgGoyHvX8anIvWjEFo,1893
37
- preditor/gui/set_text_editor_path_dialog.py,sha256=6mbHAXEVQhaWyg0N67f54aZpd5fp6puGWzeM0tPepiU,2251
38
- preditor/gui/status_label.py,sha256=UUSw2HSi5u8PA6W0pIeiaBN_LzrsJwb_AZAaGXQXBY8,2397
37
+ preditor/gui/set_text_editor_path_dialog.py,sha256=bTrYM0nU-pE2m8LIpeAUlmvP257wrcAvEdaoOURw9p8,2264
38
+ preditor/gui/status_label.py,sha256=C0SNW7LQzNK-oL_iHs2FQcnAmzcOmM1CTvSo1wP1kiM,2873
39
+ preditor/gui/suggest_path_quotes_dialog.py,sha256=QUJf_9hs8wOO6bFOr8_Z2xnNhSA8TfKFMOzheUqUDnY,1822
39
40
  preditor/gui/window.py,sha256=bZAEKQDM6V4aew1nlSTPyq_tG-_IoSvyXHcZxrdFMaE,6924
40
- preditor/gui/workbox_mixin.py,sha256=4tUiOWRFc0tEOAAo9BDD3idlkfO6t4VCuL4tklYNnTY,12543
41
- preditor/gui/workbox_text_edit.py,sha256=fJY7eo1bpMyzLri5DfdVb2vad9znJjBvzLkY34T8gb4,3678
42
- preditor/gui/workboxwidget.py,sha256=1uGSPZv3GxmOAIsA4oTaiPUHl003Yp6IA_rXanIToC4,8966
41
+ preditor/gui/workbox_mixin.py,sha256=kUVHtK-flyux3PZ9GkOYnjCJMLRcZESFJXipsgNgI0U,13851
42
+ preditor/gui/workbox_text_edit.py,sha256=B_xObwwobKzpMuTmwkczTGW9cOem2oF8pZDezCqwPCo,4385
43
+ preditor/gui/workboxwidget.py,sha256=BDmV3tu5HaZPTpc_h8UtiAag-w7pEKUBNSkqAIz_7IU,10056
43
44
  preditor/gui/fuzzy_search/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  preditor/gui/fuzzy_search/fuzzy_search.py,sha256=6npanW6aLmG_ezdHmpsKN7p2zwck_Qq6YUy9duCNN8Q,3595
45
- preditor/gui/group_tab_widget/__init__.py,sha256=KpQKkX_FyFQLy2UtBQcLoMuj0eRA7E_2-KXrTwPqA9k,12530
46
+ preditor/gui/group_tab_widget/__init__.py,sha256=rUvlURDiq2nncgEcGv5LvUNUVl3uLqJ_j4lQtZ8fMQE,12568
46
47
  preditor/gui/group_tab_widget/grouped_tab_menu.py,sha256=lopd3mjHJIE_5xLdlZL0ghuBzsYo36vO1OZE6L6tqbI,1288
47
48
  preditor/gui/group_tab_widget/grouped_tab_models.py,sha256=zlmbcv3JAC5BzjKpm4CM7Hobibm1T_3GjaPoWIQbXiQ,3970
48
- preditor/gui/group_tab_widget/grouped_tab_widget.py,sha256=EwzYVqhJaBaYS9tdd4FXUl-PxjplWtOi7f0t99De_bw,2803
49
+ preditor/gui/group_tab_widget/grouped_tab_widget.py,sha256=Z08LE8-ABakpM5Lk8cnntfjtdsmoR_T2ycgSEor7iY0,2928
49
50
  preditor/gui/group_tab_widget/one_tab_widget.py,sha256=PdZAXjmV9oTHakh8aFYRlLnfwcoT459vQWVKgpZZx4Y,1971
50
51
  preditor/gui/ui/editor_chooser.ui,sha256=cYSVHK0A4-zst6JyDkrBZK9qcAontbhV4Mmnw5ps72E,2736
51
52
  preditor/gui/ui/errordialog.ui,sha256=H1wJJVU1t7kbnYkzGtGa8SBVpKjesJG_0imdBkJaEUY,2034
52
53
  preditor/gui/ui/find_files.ui,sha256=8mdD3Vg3ofRMChdKntxiDHO3JXHQSKjjY6OLY_1W5lc,3328
53
- preditor/gui/ui/loggerwindow.ui,sha256=JVpvGWrnI4cQdX4DFwfsCMEnNh1iipc1Vxihcg8FJvU,30035
54
+ preditor/gui/ui/loggerwindow.ui,sha256=IGoeWPDI4Qym01mTkUR49MDhxpZBWYRHr7s746WfYkw,31528
54
55
  preditor/gui/ui/redmine_login_dialog.ui,sha256=PmGuJWvBcSDLya5UblFj5brwDH9VL2fJEveH-r5-nJ8,2911
55
- preditor/gui/ui/set_text_editor_path_dialog.ui,sha256=QoSorDlyfUoNSrHQRP1Yrls5az4TyUsTQw_qYOe_Ljc,4004
56
+ preditor/gui/ui/set_text_editor_path_dialog.ui,sha256=VLBpTvGneXAi9RX1dRd6oPwKoZhQ_m5HO1j1qXPhTxc,5201
57
+ preditor/gui/ui/suggest_path_quotes_dialog.ui,sha256=0hcr7kEFmmMVEp7vv18mDpvWZ0zOrojlkTLhIWCFKww,5923
56
58
  preditor/resource/environment_variables.html,sha256=-uWicgOkour-sxtY-crrjMiLMyJzAzshwUXKqSm6TmI,3134
57
59
  preditor/resource/error_mail.html,sha256=oCzynF3QSHi_Xg1wlmU5M5u1Q05mp7QV0ZtQ4e8KQDA,2400
58
60
  preditor/resource/error_mail_inline.html,sha256=ceZ8HReyTirMOX-xNgVB9kMO8QhpbHI4-WObFeOG9KY,4014
@@ -106,7 +108,7 @@ preditor/resource/lang/python.json,sha256=CXiQh0jcgd-OCrM-s9IF7s4o-g5WRA4vDaAyTR
106
108
  preditor/resource/stylesheet/Bright.css,sha256=wxzpkhcOC9GzALb0NYAMASnZmXEe9urZBniHNImwTc4,2186
107
109
  preditor/resource/stylesheet/Dark.css,sha256=l3n9Z89JNlVDWKDtMTlipQkSSVPSJXbKd0tOMWnx7PQ,5085
108
110
  preditor/scintilla/__init__.py,sha256=AoUc-fcp2D3F10FDFsp_ZzUr4zioDH0mvdubSF7sRG0,523
109
- preditor/scintilla/documenteditor.py,sha256=_dt4J1DcmGPbtujqXsaSz0Li00T6I9IEduaVKMRAQl0,77900
111
+ preditor/scintilla/documenteditor.py,sha256=6EfqJLcG2ZqzDUxvdtIrHyuVSF47goWpGrVEBMJN3pw,77919
110
112
  preditor/scintilla/finddialog.py,sha256=vcYPj74LbCy4KXdh0eJyOb8i31pJKgOZitKpgW4zhBM,2346
111
113
  preditor/scintilla/delayables/__init__.py,sha256=lj9tMc3IL2QeaN858Ixt_n7clJngbKqG2sk66vIcFcQ,275
112
114
  preditor/scintilla/delayables/smart_highlight.py,sha256=JRwGp-pVwx75zJ5NoJ8UuBg4-jK4eQfgD4eM4ZfDbGI,3502
@@ -147,9 +149,9 @@ preditor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
149
  preditor/utils/cute.py,sha256=LfF8gXMAkkQAdo4mm6J9aVkDLwWZbE6prQ0moDbtCys,1045
148
150
  preditor/utils/stylesheets.py,sha256=EVWZNq3WnaRiyUPoYMKQo_dLEwbRyKu26b03I1JDA-s,1622
149
151
  preditor/utils/text_search.py,sha256=21kuSDTpLIPUcriB81WP1kWfzuDBuP13ZtHUtypP5jE,14218
150
- PrEditor-0.4.0.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
151
- PrEditor-0.4.0.dist-info/METADATA,sha256=uWRRXD5RnLauKw3NobDW8znh1VsatdJXz5PAa0M9itQ,9277
152
- PrEditor-0.4.0.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
153
- PrEditor-0.4.0.dist-info/entry_points.txt,sha256=mpe0HFD_oIEBNPTJNyUEbmMV6Ivrp4EuYyZ34C5d_PU,519
154
- PrEditor-0.4.0.dist-info/top_level.txt,sha256=iX1_mrUOky_BQr2oG0l_MbEUYF6idyjiWSzu9z3irIw,9
155
- PrEditor-0.4.0.dist-info/RECORD,,
152
+ PrEditor-0.5.0.dist-info/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
153
+ PrEditor-0.5.0.dist-info/METADATA,sha256=WZmyP5dRi31mnNpq1L0nO-jSWRZv5CTDl9JtBN6ulXI,9256
154
+ PrEditor-0.5.0.dist-info/WHEEL,sha256=M4n4zmFKzQZk4mLCcycNIzIXO7YPKE_b5Cw4PnhHEg8,109
155
+ PrEditor-0.5.0.dist-info/entry_points.txt,sha256=mpe0HFD_oIEBNPTJNyUEbmMV6Ivrp4EuYyZ34C5d_PU,519
156
+ PrEditor-0.5.0.dist-info/top_level.txt,sha256=iX1_mrUOky_BQr2oG0l_MbEUYF6idyjiWSzu9z3irIw,9
157
+ PrEditor-0.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
preditor/gui/console.py CHANGED
@@ -9,17 +9,20 @@ import sys
9
9
  import time
10
10
  import traceback
11
11
  from builtins import str as text
12
+ from fractions import Fraction
13
+ from functools import partial
12
14
 
13
15
  import __main__
14
16
  from Qt import QtCompat
15
- from Qt.QtCore import QPoint, Qt
17
+ from Qt.QtCore import QPoint, Qt, QTimer
16
18
  from Qt.QtGui import QColor, QFontMetrics, QTextCharFormat, QTextCursor, QTextDocument
17
- from Qt.QtWidgets import QAction, QApplication, QTextEdit
19
+ from Qt.QtWidgets import QAbstractItemView, QAction, QApplication, QTextEdit
18
20
 
19
21
  from .. import debug, settings, stream
20
22
  from ..streamhandler_helper import StreamHandlerHelper
21
23
  from .codehighlighter import CodeHighlighter
22
24
  from .completer import PythonCompleter
25
+ from .suggest_path_quotes_dialog import SuggestPathQuotesDialog
23
26
 
24
27
 
25
28
  class ConsolePrEdit(QTextEdit):
@@ -47,6 +50,7 @@ class ConsolePrEdit(QTextEdit):
47
50
  # If still using a #
48
51
  self._outputPrompt = '#Result: '
49
52
  # Method used to update the gui when code is executed
53
+ self.clearExecutionTime = None
50
54
  self.reportExecutionTime = None
51
55
 
52
56
  self._firstShow = True
@@ -102,9 +106,46 @@ class ConsolePrEdit(QTextEdit):
102
106
  self.clickPos = None
103
107
  self.anchor = None
104
108
 
109
+ # Make sure console cursor is visible. It can get it's width set to 0 with
110
+ # unusual(ie not 100%) os display scaling.
111
+ if not self.cursorWidth():
112
+ self.setCursorWidth(1)
113
+
114
+ def doubleSingleShotSetScrollValue(self, origPercent):
115
+ """This double QTimer.singleShot monkey business seems to be the only way
116
+ to get scroll.maximum() to update properly so that we calc newValue
117
+ correctly. It's quite silly. Apparently, the important part is that
118
+ calling scroll.maximum() has had a pause since the font had been set.
119
+ """
120
+
121
+ def singleShotSetScrollValue(self, origPercent):
122
+ scroll = self.verticalScrollBar()
123
+ maximum = scroll.maximum()
124
+ if maximum is not None:
125
+ newValue = round(origPercent * maximum)
126
+ QTimer.singleShot(1, partial(scroll.setValue, newValue))
127
+
128
+ # The 100 ms timer amount is somewhat arbitrary. It must be more than
129
+ # some value to work, but what that value is is unknown, and may change
130
+ # under various circumstances. Briefly disable updates for smoother transition.
131
+ self.setUpdatesEnabled(False)
132
+ try:
133
+ QTimer.singleShot(100, partial(singleShotSetScrollValue, self, origPercent))
134
+ finally:
135
+ self.setUpdatesEnabled(True)
136
+
105
137
  def setConsoleFont(self, font):
106
138
  """Set the console's font and adjust the tabStopWidth"""
139
+
140
+ # Capture the scroll bar's current position (by percentage of max)
141
+ origPercent = None
142
+ scroll = self.verticalScrollBar()
143
+ if scroll.maximum():
144
+ origPercent = Fraction(scroll.value(), scroll.maximum())
145
+
146
+ # Set console and completer popup fonts
107
147
  self.setFont(font)
148
+ self.completer().popup().setFont(font)
108
149
 
109
150
  # Set the setTabStopWidth for the console's font
110
151
  tab_width = 4
@@ -117,6 +158,10 @@ class ConsolePrEdit(QTextEdit):
117
158
  fontPixelWidth = QFontMetrics(font).width(" ")
118
159
  self.setTabStopWidth(fontPixelWidth * tab_width)
119
160
 
161
+ # Scroll to same relative position where we started
162
+ if origPercent is not None:
163
+ self.doubleSingleShotSetScrollValue(origPercent)
164
+
120
165
  def mousePressEvent(self, event):
121
166
  """Overload of mousePressEvent to capture click position, so on release, we can
122
167
  check release position. If it's the same (ie user clicked vs click-drag to
@@ -213,6 +258,23 @@ class ConsolePrEdit(QTextEdit):
213
258
  return
214
259
 
215
260
  if modulePath:
261
+ # Check if cmdTempl filepaths aren't wrapped in double=quotes to handle
262
+ # spaces. If not, suggest to user to update the template, offering the
263
+ # suggested change.
264
+ pattern = r"(?<!\")({\w+Path})(?!\")"
265
+ repl = r'"\g<1>"'
266
+ quotedCmdTempl = re.sub(pattern, repl, cmdTempl)
267
+ if quotedCmdTempl != cmdTempl:
268
+ # Instantiate dialog to maybe show (unless user previously chose "Don't
269
+ # ask again")
270
+ dialog = SuggestPathQuotesDialog(
271
+ self.window(), cmdTempl, quotedCmdTempl
272
+ )
273
+ self.window().maybeDisplayDialog(dialog)
274
+
275
+ # Refresh cmdTempl in case user just had it changed.
276
+ cmdTempl = window.textEditorCmdTempl
277
+
216
278
  # Attempt to create command from template and run the command
217
279
  try:
218
280
  command = cmdTempl.format(
@@ -317,6 +379,8 @@ class ConsolePrEdit(QTextEdit):
317
379
  self._foregroundColor = color
318
380
 
319
381
  def executeString(self, commandText, filename='<ConsolePrEdit>', extraPrint=True):
382
+ if self.clearExecutionTime is not None:
383
+ self.clearExecutionTime()
320
384
  cursor = self.textCursor()
321
385
  cursor.select(QTextCursor.BlockUnderCursor)
322
386
  line = cursor.selectedText()
@@ -358,7 +422,7 @@ class ConsolePrEdit(QTextEdit):
358
422
 
359
423
  # Report the total time it took to execute this code.
360
424
  if self.reportExecutionTime is not None:
361
- self.reportExecutionTime(delta)
425
+ self.reportExecutionTime((delta, commandText))
362
426
  return cmdresult, wasEval
363
427
 
364
428
  def executeCommand(self):
@@ -478,6 +542,17 @@ class ConsolePrEdit(QTextEdit):
478
542
 
479
543
  completer = self.completer()
480
544
 
545
+ # Define prefix so we can determine if the exact prefix is in
546
+ # completions and highlight it. We must manually add the currently typed
547
+ # character, or remove it if backspace or delete has just been pressed.
548
+ key = event.text()
549
+ _, prefix = completer.currentObject(scope=__main__.__dict__)
550
+ isBackspaceOrDel = event.key() in (Qt.Key_Backspace, Qt.Key_Delete)
551
+ if key.isalnum() or key in ("-", "_"):
552
+ prefix += str(key)
553
+ elif isBackspaceOrDel and prefix:
554
+ prefix = prefix[:-1]
555
+
481
556
  if completer and event.key() in (
482
557
  Qt.Key_Backspace,
483
558
  Qt.Key_Delete,
@@ -560,9 +635,30 @@ class ConsolePrEdit(QTextEdit):
560
635
  or completer.wasCompletingCounter
561
636
  ):
562
637
  completer.refreshList(scope=__main__.__dict__)
563
- completer.popup().setCurrentIndex(
564
- completer.completionModel().index(0, 0)
565
- )
638
+
639
+ model = completer.completionModel()
640
+ index = model.index(0, 0)
641
+
642
+ # If option chosen, if the exact prefix exists in the
643
+ # possible completions, highlight it, even if it's not the
644
+ # topmost completion.
645
+ if self.window().uiHighlightExactCompletionACT.isChecked():
646
+ for i in range(completer.completionCount()):
647
+ completer.setCurrentRow(i)
648
+ curCompletion = completer.currentCompletion()
649
+ if prefix == curCompletion:
650
+ index = model.index(i, 0)
651
+ break
652
+ elif prefix == curCompletion.lower():
653
+ index = model.index(i, 0)
654
+ break
655
+
656
+ # Set completer current Row, so finishing the completer will use
657
+ # correct text
658
+ completer.setCurrentRow(index.row())
659
+
660
+ # Make sure that current selection is visible, ie scroll to it
661
+ completer.popup().scrollTo(index, QAbstractItemView.EnsureVisible)
566
662
 
567
663
  # show the completer for the rect
568
664
  rect = self.cursorRect()
@@ -640,6 +736,7 @@ class ConsolePrEdit(QTextEdit):
640
736
  def startInputLine(self):
641
737
  """create a new command prompt line"""
642
738
  self.startPrompt(self.prompt())
739
+ self._prevCommandIndex = 0
643
740
 
644
741
  def startPrompt(self, prompt):
645
742
  """create a new command prompt line with the given prompt
@@ -44,7 +44,7 @@ class EditorChooser(QWidget):
44
44
  current = self.editor_name()
45
45
  self.uiWorkboxEditorDDL.blockSignals(True)
46
46
  self.uiWorkboxEditorDDL.clear()
47
- for name, _ in sorted(plugins.editors()):
47
+ for name, _ in sorted(set(plugins.editors())):
48
48
  self.uiWorkboxEditorDDL.addItem(name)
49
49
 
50
50
  self.uiWorkboxEditorDDL.setCurrentIndex(
@@ -110,7 +110,7 @@ class GroupTabWidget(OneTabWidget):
110
110
  # Create the first editor tab and make it visible
111
111
  editor = parent.add_new_editor(title)
112
112
  self.setCurrentIndex(self.indexOf(parent))
113
-
113
+ self.window().focusToWorkbox()
114
114
  return parent, editor
115
115
 
116
116
  def all_widgets(self):
@@ -71,5 +71,8 @@ class GroupedTabWidget(OneTabWidget):
71
71
  if editor and editor.isVisible():
72
72
  editor.__show__()
73
73
 
74
+ if hasattr(self.window(), "setWorkboxFontBasedOnConsole"):
75
+ self.window().setWorkboxFontBasedOnConsole()
76
+
74
77
  def update_closable_tabs(self):
75
78
  self.setTabsClosable(self.count() != 1)