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

preditor/__init__.py CHANGED
@@ -113,6 +113,11 @@ def configure(name, parent_callback=None, excepthook=True, logging=True, streams
113
113
  preditor.debug.BlurExcepthook.install()
114
114
 
115
115
 
116
+ def get_core_name():
117
+ """Returns the configured core_name or DEFAULT_CORE_NAME."""
118
+ return _global_config.get('core_name', DEFAULT_CORE_NAME)
119
+
120
+
116
121
  def launch(run_workbox=False, app_id=None, name=None, standalone=False):
117
122
  """Launches the preditor gui creating the QApplication instance if not
118
123
  already created.
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()
@@ -333,18 +397,27 @@ class ConsolePrEdit(QTextEdit):
333
397
  # exec which has no Return.
334
398
  wasEval = False
335
399
  startTime = time.time()
400
+
336
401
  try:
337
402
  compiled = compile(commandText, filename, 'eval')
338
403
  wasEval = True
339
404
  except Exception:
340
405
  compiled = compile(commandText, filename, 'exec')
341
- if wasEval:
342
- cmdresult = eval(compiled, __main__.__dict__, __main__.__dict__)
343
- else:
344
- exec(compiled, __main__.__dict__, __main__.__dict__)
406
+
407
+ # We wrap in try / finally so that elapsed time gets updated, even when an
408
+ # exception is raised.
409
+ try:
410
+ if wasEval:
411
+ cmdresult = eval(compiled, __main__.__dict__, __main__.__dict__)
412
+ else:
413
+ exec(compiled, __main__.__dict__, __main__.__dict__)
414
+ finally:
415
+ # Report the total time it took to execute this code.
416
+ if self.reportExecutionTime is not None:
417
+ delta = time.time() - startTime
418
+ self.reportExecutionTime((delta, commandText))
345
419
 
346
420
  # Provide user feedback when running long code execution.
347
- delta = time.time() - startTime
348
421
  if self.flash_window and self.flash_time and delta >= self.flash_time:
349
422
  if settings.OS_TYPE == "Windows":
350
423
  try:
@@ -356,9 +429,6 @@ class ConsolePrEdit(QTextEdit):
356
429
  hwnd = int(self.flash_window.winId())
357
430
  utils.flash_window(hwnd)
358
431
 
359
- # Report the total time it took to execute this code.
360
- if self.reportExecutionTime is not None:
361
- self.reportExecutionTime(delta)
362
432
  return cmdresult, wasEval
363
433
 
364
434
  def executeCommand(self):
@@ -478,6 +548,17 @@ class ConsolePrEdit(QTextEdit):
478
548
 
479
549
  completer = self.completer()
480
550
 
551
+ # Define prefix so we can determine if the exact prefix is in
552
+ # completions and highlight it. We must manually add the currently typed
553
+ # character, or remove it if backspace or delete has just been pressed.
554
+ key = event.text()
555
+ _, prefix = completer.currentObject(scope=__main__.__dict__)
556
+ isBackspaceOrDel = event.key() in (Qt.Key_Backspace, Qt.Key_Delete)
557
+ if key.isalnum() or key in ("-", "_"):
558
+ prefix += str(key)
559
+ elif isBackspaceOrDel and prefix:
560
+ prefix = prefix[:-1]
561
+
481
562
  if completer and event.key() in (
482
563
  Qt.Key_Backspace,
483
564
  Qt.Key_Delete,
@@ -560,9 +641,30 @@ class ConsolePrEdit(QTextEdit):
560
641
  or completer.wasCompletingCounter
561
642
  ):
562
643
  completer.refreshList(scope=__main__.__dict__)
563
- completer.popup().setCurrentIndex(
564
- completer.completionModel().index(0, 0)
565
- )
644
+
645
+ model = completer.completionModel()
646
+ index = model.index(0, 0)
647
+
648
+ # If option chosen, if the exact prefix exists in the
649
+ # possible completions, highlight it, even if it's not the
650
+ # topmost completion.
651
+ if self.window().uiHighlightExactCompletionACT.isChecked():
652
+ for i in range(completer.completionCount()):
653
+ completer.setCurrentRow(i)
654
+ curCompletion = completer.currentCompletion()
655
+ if prefix == curCompletion:
656
+ index = model.index(i, 0)
657
+ break
658
+ elif prefix == curCompletion.lower():
659
+ index = model.index(i, 0)
660
+ break
661
+
662
+ # Set completer current Row, so finishing the completer will use
663
+ # correct text
664
+ completer.setCurrentRow(index.row())
665
+
666
+ # Make sure that current selection is visible, ie scroll to it
667
+ completer.popup().scrollTo(index, QAbstractItemView.EnsureVisible)
566
668
 
567
669
  # show the completer for the rect
568
670
  rect = self.cursorRect()
@@ -640,6 +742,7 @@ class ConsolePrEdit(QTextEdit):
640
742
  def startInputLine(self):
641
743
  """create a new command prompt line"""
642
744
  self.startPrompt(self.prompt())
745
+ self._prevCommandIndex = 0
643
746
 
644
747
  def startPrompt(self, prompt):
645
748
  """create a new command prompt line with the given prompt
@@ -661,6 +764,11 @@ class ConsolePrEdit(QTextEdit):
661
764
 
662
765
  self.insertPlainText(inputstr)
663
766
 
767
+ scroll = self.verticalScrollBar()
768
+ maximum = scroll.maximum()
769
+ if maximum is not None:
770
+ scroll.setValue(maximum)
771
+
664
772
  def startOutputLine(self):
665
773
  """Create a new line to show output text."""
666
774
  self.startPrompt(self._outputPrompt)
@@ -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)
@@ -14,9 +14,10 @@ import __main__
14
14
  import six
15
15
  from Qt import QtCompat, QtCore, QtWidgets
16
16
  from Qt.QtCore import QByteArray, Qt, QTimer, Signal, Slot
17
- from Qt.QtGui import QCursor, QFont, QFontDatabase, QIcon, QTextCursor
17
+ from Qt.QtGui import QCursor, QFont, QIcon, QTextCursor
18
18
  from Qt.QtWidgets import (
19
19
  QApplication,
20
+ QFontDialog,
20
21
  QInputDialog,
21
22
  QMessageBox,
22
23
  QTextBrowser,
@@ -29,6 +30,7 @@ from .. import (
29
30
  about_preditor,
30
31
  core,
31
32
  debug,
33
+ get_core_name,
32
34
  osystem,
33
35
  plugins,
34
36
  prefs,
@@ -59,7 +61,7 @@ class LoggerWindow(Window):
59
61
 
60
62
  def __init__(self, parent, name=None, run_workbox=False, standalone=False):
61
63
  super(LoggerWindow, self).__init__(parent=parent)
62
- self.name = name if name else DEFAULT_CORE_NAME
64
+ self.name = name if name else get_core_name()
63
65
  self.aboutToClearPathsEnabled = False
64
66
  self._stylesheet = 'Bright'
65
67
 
@@ -75,6 +77,7 @@ class LoggerWindow(Window):
75
77
  loadUi(__file__, self)
76
78
 
77
79
  self.uiConsoleTXT.flash_window = self
80
+ self.uiConsoleTXT.clearExecutionTime = self.clearExecutionTime
78
81
  self.uiConsoleTXT.reportExecutionTime = self.reportExecutionTime
79
82
  self.uiClearToLastPromptACT.triggered.connect(
80
83
  self.uiConsoleTXT.clearToLastPrompt
@@ -119,12 +122,38 @@ class LoggerWindow(Window):
119
122
  self.uiCloseLoggerACT.triggered.connect(self.closeLogger)
120
123
 
121
124
  self.uiRunAllACT.triggered.connect(self.execAll)
122
- self.uiRunSelectedACT.triggered.connect(self.execSelected)
125
+ # Even though the RunSelected actions (with shortcuts) are connected
126
+ # here, this only affects if the action is chosen from the menu. The
127
+ # shortcuts are always intercepted by the workbox document editor. To
128
+ # handle this, the workbox.keyPressEvent method will perceive the
129
+ # shortcut press, and call .execSelected, which will then ultimately call
130
+ # workbox.__exec_selected__
131
+ self.uiRunSelectedACT.triggered.connect(
132
+ partial(self.execSelected, truncate=True)
133
+ )
134
+ self.uiRunSelectedDontTruncateACT.triggered.connect(
135
+ partial(self.execSelected, truncate=False)
136
+ )
123
137
 
124
- self.uiAutoCompleteEnabledACT.toggled.connect(self.setAutoCompleteEnabled)
138
+ self.uiConsoleAutoCompleteEnabledACT.toggled.connect(
139
+ partial(self.setAutoCompleteEnabled, console=True)
140
+ )
141
+ self.uiWorkboxAutoCompleteEnabledACT.toggled.connect(
142
+ partial(self.setAutoCompleteEnabled, console=False)
143
+ )
125
144
 
126
145
  self.uiAutoCompleteCaseSensitiveACT.toggled.connect(self.setCaseSensitive)
127
146
 
147
+ self.uiSelectMonospaceFontACT.triggered.connect(
148
+ partial(self.selectFont, monospace=True)
149
+ )
150
+ self.uiSelectProportionalFontACT.triggered.connect(
151
+ partial(self.selectFont, proportional=True)
152
+ )
153
+ self.uiSelectAllFontACT.triggered.connect(
154
+ partial(self.selectFont, monospace=True, proportional=True)
155
+ )
156
+
128
157
  # Setup ability to cycle completer mode, and create action for each mode
129
158
  self.completerModeCycle = itertools.cycle(CompleterMode)
130
159
  # create CompleterMode submenu
@@ -234,27 +263,9 @@ class LoggerWindow(Window):
234
263
  # Make action shortcuts available anywhere in the Logger
235
264
  self.addAction(self.uiClearLogACT)
236
265
 
266
+ self.dont_ask_again = []
237
267
  self.restorePrefs()
238
268
 
239
- # add font menu list
240
- curFamily = self.console().font().family()
241
- fontDB = QFontDatabase()
242
- fontFamilies = fontDB.families(QFontDatabase.Latin)
243
- monospaceFonts = [fam for fam in fontFamilies if fontDB.isFixedPitch(fam)]
244
-
245
- self.uiMonospaceFontMENU.clear()
246
- self.uiProportionalFontMENU.clear()
247
-
248
- for family in fontFamilies:
249
- if family in monospaceFonts:
250
- action = self.uiMonospaceFontMENU.addAction(family)
251
- else:
252
- action = self.uiProportionalFontMENU.addAction(family)
253
- action.setObjectName(u'ui{}FontACT'.format(family))
254
- action.setCheckable(True)
255
- action.setChecked(family == curFamily)
256
- action.triggered.connect(partial(self.selectFont, action))
257
-
258
269
  # add stylesheet menu options.
259
270
  for style_name in stylesheets.stylesheets():
260
271
  action = self.uiStyleMENU.addAction(style_name)
@@ -276,6 +287,9 @@ class LoggerWindow(Window):
276
287
  )
277
288
  )
278
289
 
290
+ self.setWorkboxFontBasedOnConsole()
291
+ self.setEditorChooserFontBasedOnConsole()
292
+
279
293
  self.setup_run_workbox()
280
294
 
281
295
  if not standalone:
@@ -429,7 +443,7 @@ class LoggerWindow(Window):
429
443
  if not workbox.hasFocus():
430
444
  return
431
445
 
432
- text = workbox.__selected_text__()
446
+ text, _line = workbox.__selected_text__(selectText=True)
433
447
  if not text:
434
448
  line, index = workbox.__cursor_position__()
435
449
  text = workbox.__text__(line)
@@ -506,22 +520,14 @@ class LoggerWindow(Window):
506
520
  delta = event.angleDelta().y()
507
521
 
508
522
  # convert delta to +1 or -1, depending
509
- delta = delta / abs(delta)
523
+ delta = delta // abs(delta)
510
524
  minSize = 5
511
525
  maxSize = 50
512
526
  font = self.console().font()
513
527
  newSize = font.pointSize() + delta
514
528
  newSize = max(min(newSize, maxSize), minSize)
515
529
 
516
- font.setPointSize(newSize)
517
- self.console().setConsoleFont(font)
518
-
519
- for workbox in self.uiWorkboxTAB.all_widgets():
520
- marginsFont = workbox.__margins_font__()
521
- marginsFont.setPointSize(newSize)
522
- workbox.__set_margins_font__(marginsFont)
523
-
524
- workbox.__set_font__(font)
530
+ self.setFontSize(newSize)
525
531
  else:
526
532
  Window.wheelEvent(self, event)
527
533
 
@@ -539,41 +545,76 @@ class LoggerWindow(Window):
539
545
  menu = action.parentWidget()
540
546
  QToolTip.showText(QCursor.pos(), text, menu)
541
547
 
542
- def findCurrentFontAction(self):
543
- """Find and return current font's action"""
544
- actions = self.uiMonospaceFontMENU.actions()
545
- actions.extend(self.uiProportionalFontMENU.actions())
546
-
547
- action = None
548
- for act in actions:
549
- if act.isChecked():
550
- action = act
551
- break
552
-
553
- return action
554
-
555
- def selectFont(self, action):
556
- """Set console and workbox font to current font
548
+ def selectFont(self, monospace=False, proportional=False):
549
+ """Present a QFontChooser dialog, offering, monospace, proportional, or all
550
+ fonts, based on user choice. If a font is chosen, set it on the console and
551
+ workboxes.
557
552
 
558
553
  Args:
559
554
  action (QAction): menu action associated with chosen font
560
555
  """
556
+ origFont = self.console().font()
557
+ curFontFamily = origFont.family()
558
+
559
+ if monospace and proportional:
560
+ options = QFontDialog.MonospacedFonts | QFontDialog.ProportionalFonts
561
+ kind = "monospace or proportional "
562
+ elif monospace:
563
+ options = QFontDialog.MonospacedFonts
564
+ kind = "monospace "
565
+ elif proportional:
566
+ options = QFontDialog.ProportionalFonts
567
+ kind = "proportional "
568
+
569
+ # Present a QFontDialog for user to choose a font
570
+ title = "Pick a {} font. Current font is: {}".format(kind, curFontFamily)
571
+ newFont, okClicked = QFontDialog.getFont(origFont, self, title, options=options)
572
+
573
+ if okClicked:
574
+ self.console().setConsoleFont(newFont)
575
+ self.setWorkboxFontBasedOnConsole()
576
+ self.setEditorChooserFontBasedOnConsole()
577
+
578
+ def setFontSize(self, newSize):
579
+ """Update the font size in the console and current workbox.
561
580
 
562
- actions = self.uiMonospaceFontMENU.actions()
563
- actions.extend(self.uiProportionalFontMENU.actions())
581
+ Args:
582
+ newSize (int): The new size to set the font
583
+ """
584
+ font = self.console().font()
585
+ font.setPointSize(newSize)
586
+ self.console().setConsoleFont(font)
564
587
 
565
- for act in actions:
566
- act.setChecked(act == action)
588
+ self.setWorkboxFontBasedOnConsole()
589
+ self.setEditorChooserFontBasedOnConsole()
567
590
 
568
- family = action.text()
591
+ def setWorkboxFontBasedOnConsole(self):
592
+ """If the current workbox's font is different to the console's font, set it to
593
+ match.
594
+ """
569
595
  font = self.console().font()
570
- font.setFamily(family)
571
- self.console().setConsoleFont(font)
572
596
 
573
- for workbox in self.uiWorkboxTAB.all_widgets():
597
+ workboxGroup = self.uiWorkboxTAB.currentWidget()
598
+ if workboxGroup is None:
599
+ return
600
+
601
+ workbox = workboxGroup.currentWidget()
602
+ if workbox is None:
603
+ return
604
+
605
+ if workbox.__font__() != font:
574
606
  workbox.__set_margins_font__(font)
575
607
  workbox.__set_font__(font)
576
608
 
609
+ def setEditorChooserFontBasedOnConsole(self):
610
+ """Set the EditorChooser font to match console. This helps with legibility when
611
+ using EditorChooser.
612
+ """
613
+ font = self.console().font()
614
+ for child in self.uiEditorChooserWGT.children():
615
+ if hasattr(child, "font"):
616
+ child.setFont(font)
617
+
577
618
  @classmethod
578
619
  def _genPrefName(cls, baseName, index):
579
620
  if index:
@@ -635,11 +676,21 @@ class LoggerWindow(Window):
635
676
  prompt = console.prompt()
636
677
  console.startPrompt(prompt)
637
678
 
638
- def execSelected(self):
639
- """Clears the console before executing selected workbox code"""
679
+ def execSelected(self, truncate=True):
680
+ """Clears the console before executing selected workbox code.
681
+
682
+ NOTE! This method is not called when the uiRunSelectedACT is triggered,
683
+ because the workbox will always intercept it. So instead, the workbox's
684
+ keyPressEvent will notice the shortcut and call this method.
685
+ """
686
+
640
687
  if self.uiClearBeforeRunningACT.isChecked():
641
688
  self.clearLog()
642
- self.current_workbox().__exec_selected__()
689
+
690
+ self.current_workbox().__exec_selected__(truncate=truncate)
691
+
692
+ if self.uiAutoPromptACT.isChecked():
693
+ self.console().startInputLine()
643
694
 
644
695
  def keyPressEvent(self, event):
645
696
  # Fix 'Maya : Qt tools lose focus' https://redmine.blur.com/issues/34430
@@ -652,6 +703,11 @@ class LoggerWindow(Window):
652
703
  if self.uiClearLogOnRefreshACT.isChecked():
653
704
  self.clearLog()
654
705
 
706
+ def clearExecutionTime(self):
707
+ """Update status text with hyphens to indicate execution has begun."""
708
+ self.setStatusText('Exec: -.- Seconds')
709
+ QApplication.instance().processEvents()
710
+
655
711
  def reportExecutionTime(self, seconds):
656
712
  """Update status text with seconds passed in."""
657
713
  self.uiStatusLBL.showSeconds(seconds)
@@ -671,11 +727,15 @@ class LoggerWindow(Window):
671
727
  'SplitterSize': self.uiSplitterSPLIT.sizes(),
672
728
  'tabIndent': self.uiIndentationsTabsACT.isChecked(),
673
729
  'copyIndentsAsSpaces': self.uiCopyTabsToSpacesACT.isChecked(),
674
- 'hintingEnabled': self.uiAutoCompleteEnabledACT.isChecked(),
730
+ 'hintingEnabled': self.uiConsoleAutoCompleteEnabledACT.isChecked(),
731
+ 'workboxHintingEnabled': (
732
+ self.uiWorkboxAutoCompleteEnabledACT.isChecked()
733
+ ),
675
734
  'spellCheckEnabled': self.uiSpellCheckEnabledACT.isChecked(),
676
735
  'wordWrap': self.uiWordWrapACT.isChecked(),
677
736
  'clearBeforeRunning': self.uiClearBeforeRunningACT.isChecked(),
678
737
  'clearBeforeEnvRefresh': self.uiClearLogOnRefreshACT.isChecked(),
738
+ 'uiSelectTextACT': self.uiSelectTextACT.isChecked(),
679
739
  'toolbarStates': six.text_type(self.saveState().toHex(), 'utf-8'),
680
740
  'consoleFont': self.console().font().toString(),
681
741
  'uiAutoSaveSettingssACT': self.uiAutoSaveSettingssACT.isChecked(),
@@ -693,6 +753,10 @@ class LoggerWindow(Window):
693
753
  ),
694
754
  'find_files_context': self.uiFindInWorkboxesWGT.uiContextSPN.value(),
695
755
  'find_files_text': self.uiFindInWorkboxesWGT.uiFindTXT.text(),
756
+ 'uiHighlightExactCompletionACT': (
757
+ self.uiHighlightExactCompletionACT.isChecked()
758
+ ),
759
+ 'dont_ask_again': self.dont_ask_again,
696
760
  }
697
761
  )
698
762
 
@@ -727,6 +791,15 @@ class LoggerWindow(Window):
727
791
  with open(filename, 'w') as fp:
728
792
  json.dump(pref, fp, indent=4)
729
793
 
794
+ def maybeDisplayDialog(self, dialog):
795
+ """If user hasn't previously opted to not show this particular dialog again,
796
+ show it.
797
+ """
798
+ if dialog.objectName() in self.dont_ask_again:
799
+ return
800
+
801
+ dialog.exec_()
802
+
730
803
  def restartLogger(self):
731
804
  """Closes this PrEditor instance and starts a new process with the same
732
805
  cli arguments.
@@ -776,21 +849,20 @@ class LoggerWindow(Window):
776
849
  self.setWindowState(Qt.WindowStates(pref.get('windowState', 0)))
777
850
  self.uiIndentationsTabsACT.setChecked(pref.get('tabIndent', True))
778
851
  self.uiCopyTabsToSpacesACT.setChecked(pref.get('copyIndentsAsSpaces', False))
779
- self.uiAutoCompleteEnabledACT.setChecked(pref.get('hintingEnabled', True))
780
852
 
781
853
  # completer settings
782
854
  self.setCaseSensitive(pref.get('caseSensitive', True))
783
855
  completerMode = CompleterMode(pref.get('completerMode', 0))
784
856
  self.cycleToCompleterMode(completerMode)
785
857
  self.setCompleterMode(completerMode)
858
+ self.uiHighlightExactCompletionACT.setChecked(
859
+ pref.get('uiHighlightExactCompletionACT', False)
860
+ )
786
861
 
787
862
  self.setSpellCheckEnabled(self.uiSpellCheckEnabledACT.isChecked())
788
863
  self.uiSpellCheckEnabledACT.setChecked(pref.get('spellCheckEnabled', False))
789
864
  self.uiSpellCheckEnabledACT.setDisabled(False)
790
865
 
791
- self.uiConsoleTXT.completer().setEnabled(
792
- self.uiAutoCompleteEnabledACT.isChecked()
793
- )
794
866
  self.uiAutoSaveSettingssACT.setChecked(pref.get('uiAutoSaveSettingssACT', True))
795
867
 
796
868
  self.uiAutoPromptACT.setChecked(pref.get('uiAutoPromptACT', False))
@@ -814,7 +886,7 @@ class LoggerWindow(Window):
814
886
 
815
887
  # External text editor filepath and command template
816
888
  defaultExePath = r"C:\Program Files\Sublime Text 3\sublime_text.exe"
817
- defaultCmd = r"{exePath} {modulePath}:{lineNum}"
889
+ defaultCmd = r'"{exePath}" "{modulePath}":{lineNum}'
818
890
  self.textEditorPath = pref.get('textEditorPath', defaultExePath)
819
891
  self.textEditorCmdTempl = pref.get('textEditorCmdTempl', defaultCmd)
820
892
 
@@ -823,6 +895,7 @@ class LoggerWindow(Window):
823
895
  self.uiClearBeforeRunningACT.setChecked(pref.get('clearBeforeRunning', False))
824
896
  self.uiClearLogOnRefreshACT.setChecked(pref.get('clearBeforeEnvRefresh', False))
825
897
  self.setClearBeforeRunning(self.uiClearBeforeRunningACT.isChecked())
898
+ self.uiSelectTextACT.setChecked(pref.get('uiSelectTextACT', True))
826
899
 
827
900
  self._stylesheet = pref.get('currentStyleSheet', 'Bright')
828
901
  if self._stylesheet == 'Custom':
@@ -833,6 +906,13 @@ class LoggerWindow(Window):
833
906
 
834
907
  self.uiWorkboxTAB.restore_prefs(pref.get('workbox_prefs', {}))
835
908
 
909
+ hintingEnabled = pref.get('hintingEnabled', True)
910
+ self.uiConsoleAutoCompleteEnabledACT.setChecked(hintingEnabled)
911
+ self.setAutoCompleteEnabled(hintingEnabled, console=True)
912
+ workboxHintingEnabled = pref.get('workboxHintingEnabled', True)
913
+ self.uiWorkboxAutoCompleteEnabledACT.setChecked(workboxHintingEnabled)
914
+ self.setAutoCompleteEnabled(workboxHintingEnabled, console=False)
915
+
836
916
  # Ensure the correct workbox stack page is shown
837
917
  self.update_workbox_stack()
838
918
 
@@ -842,6 +922,8 @@ class LoggerWindow(Window):
842
922
  if font.fromString(_font):
843
923
  self.console().setConsoleFont(font)
844
924
 
925
+ self.dont_ask_again = pref.get('dont_ask_again', [])
926
+
845
927
  def restoreToolbars(self, pref=None):
846
928
  if pref is None:
847
929
  pref = self.load_prefs()
@@ -851,10 +933,12 @@ class LoggerWindow(Window):
851
933
  state = QByteArray.fromHex(bytes(state, 'utf-8'))
852
934
  self.restoreState(state)
853
935
 
854
- def setAutoCompleteEnabled(self, state):
855
- self.uiConsoleTXT.completer().setEnabled(state)
856
- for workbox, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
857
- workbox.__set_auto_complete_enabled__(state)
936
+ def setAutoCompleteEnabled(self, state, console=True):
937
+ if console:
938
+ self.uiConsoleTXT.completer().setEnabled(state)
939
+ else:
940
+ for workbox, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
941
+ workbox.__set_auto_complete_enabled__(state)
858
942
 
859
943
  def setSpellCheckEnabled(self, state):
860
944
  try: