src/eric7/QScintilla/Editor.py

branch
eric7-maintenance
changeset 10534
783d835d7fe4
parent 10460
3b34efa2857c
parent 10517
aecd5a8c958c
child 10616
4aa36fcd4a30
equal deleted inserted replaced
10461:5fbbda78c175 10534:783d835d7fe4
9 9
10 import bisect 10 import bisect
11 import collections 11 import collections
12 import contextlib 12 import contextlib
13 import difflib 13 import difflib
14 import enum
14 import os 15 import os
15 import pathlib 16 import pathlib
16 import re 17 import re
17 18
18 import editorconfig 19 import editorconfig
29 Qt, 30 Qt,
30 QTimer, 31 QTimer,
31 pyqtSignal, 32 pyqtSignal,
32 pyqtSlot, 33 pyqtSlot,
33 ) 34 )
34 from PyQt6.QtGui import QAction, QActionGroup, QFont, QPainter, QPalette, QPixmap 35 from PyQt6.QtGui import (
36 QAction,
37 QActionGroup,
38 QCursor,
39 QFont,
40 QPainter,
41 QPalette,
42 QPixmap,
43 )
35 from PyQt6.QtPrintSupport import ( 44 from PyQt6.QtPrintSupport import (
36 QAbstractPrintDialog, 45 QAbstractPrintDialog,
37 QPrintDialog, 46 QPrintDialog,
38 QPrinter, 47 QPrinter,
39 QPrintPreviewDialog, 48 QPrintPreviewDialog,
40 ) 49 )
41 from PyQt6.QtWidgets import QApplication, QDialog, QInputDialog, QLineEdit, QMenu 50 from PyQt6.QtWidgets import (
51 QApplication,
52 QDialog,
53 QInputDialog,
54 QLineEdit,
55 QMenu,
56 QToolTip,
57 )
42 58
43 from eric7 import Globals, Preferences, Utilities 59 from eric7 import Globals, Preferences, Utilities
44 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction 60 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction
45 from eric7.CodeFormatting.BlackUtilities import aboutBlack 61 from eric7.CodeFormatting.BlackUtilities import aboutBlack
46 from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction 62 from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction
65 ReferencesListID = 3 81 ReferencesListID = 3
66 82
67 ReferenceItem = collections.namedtuple( # noqa: U200 83 ReferenceItem = collections.namedtuple( # noqa: U200
68 "ReferenceItem", ["modulePath", "codeLine", "line", "column"] 84 "ReferenceItem", ["modulePath", "codeLine", "line", "column"]
69 ) 85 )
86
87
88 class EditorIconId(enum.IntEnum):
89 """
90 Class defining the completion icon IDs.
91 """
92
93 Class = 1
94 ClassProtected = 2
95 ClassPrivate = 3
96 Method = 4
97 MethodProtected = 5
98 MethodPrivate = 6
99 Attribute = 7
100 AttributeProtected = 8
101 AttributePrivate = 9
102 Enum = 10
103 Keywords = 11
104 Module = 12
105
106 FromDocument = 99
107 TemplateImage = 100
108
109
110 class EditorWarningKind(enum.Enum):
111 """
112 Class defining the kind of warnings supported by the Editor class.
113 """
114
115 Code = 1
116 Python = 2
117 Style = 3
118 Info = 4
119 Error = 5
70 120
71 121
72 class Editor(QsciScintillaCompat): 122 class Editor(QsciScintillaCompat):
73 """ 123 """
74 Class implementing the editor component of the eric IDE. 124 Class implementing the editor component of the eric IDE.
144 lastEditPositionAvailable = pyqtSignal() 194 lastEditPositionAvailable = pyqtSignal()
145 refreshed = pyqtSignal() 195 refreshed = pyqtSignal()
146 settingsRead = pyqtSignal() 196 settingsRead = pyqtSignal()
147 mouseDoubleClick = pyqtSignal(QPoint, Qt.MouseButton) 197 mouseDoubleClick = pyqtSignal(QPoint, Qt.MouseButton)
148 198
149 # TODO: convert to an enum.Enum
150 WarningCode = 1
151 WarningPython = 2
152 WarningStyle = 3
153 WarningInfo = 4
154 WarningError = 5
155
156 # TODO: convert to an enum.IntEnum (also in Assistant plugin)
157 # Autocompletion icon definitions
158 ClassID = 1
159 ClassProtectedID = 2
160 ClassPrivateID = 3
161 MethodID = 4
162 MethodProtectedID = 5
163 MethodPrivateID = 6
164 AttributeID = 7
165 AttributeProtectedID = 8
166 AttributePrivateID = 9
167 EnumID = 10
168 KeywordsID = 11
169 ModuleID = 12
170
171 FromDocumentID = 99
172
173 TemplateImageID = 100
174
175 # Cooperation related definitions 199 # Cooperation related definitions
176 Separator = "@@@" 200 Separator = "@@@"
177 201
178 StartEditToken = "START_EDIT" 202 StartEditToken = "START_EDIT"
179 EndEditToken = "END_EDIT" 203 EndEditToken = "END_EDIT"
479 editor.addClone(self) 503 editor.addClone(self)
480 504
481 self.gotoLine(1) 505 self.gotoLine(1)
482 506
483 # connect the mouse hover signals 507 # connect the mouse hover signals
508 # mouse hover for the editor margins
509 self.SCN_DWELLSTART.connect(self.__marginHoverStart)
510 self.SCN_DWELLEND.connect(self.__marginHoverEnd)
511
512 # mouse hover help for the editor text
484 self.SCN_DWELLSTART.connect(self.__showMouseHoverHelp) 513 self.SCN_DWELLSTART.connect(self.__showMouseHoverHelp)
485 self.SCN_DWELLEND.connect(self.__cancelMouseHoverHelp) 514 self.SCN_DWELLEND.connect(self.__cancelMouseHoverHelp)
486 self.__mouseHoverHelp = None 515 self.__mouseHoverHelp = None
487 self.__showingMouseHoverHelp = False 516 self.__showingMouseHoverHelp = False
488 517
655 Private method to register images for autocompletion lists. 684 Private method to register images for autocompletion lists.
656 """ 685 """
657 # finale size of the completion images 686 # finale size of the completion images
658 imageSize = QSize(22, 22) 687 imageSize = QSize(22, 22)
659 688
660 self.registerImage(self.ClassID, EricPixmapCache.getPixmap("class", imageSize))
661 self.registerImage( 689 self.registerImage(
662 self.ClassProtectedID, 690 EditorIconId.Class,
691 EricPixmapCache.getPixmap("class", imageSize),
692 )
693 self.registerImage(
694 EditorIconId.ClassProtected,
663 EricPixmapCache.getPixmap("class_protected", imageSize), 695 EricPixmapCache.getPixmap("class_protected", imageSize),
664 ) 696 )
665 self.registerImage( 697 self.registerImage(
666 self.ClassPrivateID, EricPixmapCache.getPixmap("class_private", imageSize) 698 EditorIconId.ClassPrivate,
699 EricPixmapCache.getPixmap("class_private", imageSize),
667 ) 700 )
668 self.registerImage( 701 self.registerImage(
669 self.MethodID, EricPixmapCache.getPixmap("method", imageSize) 702 EditorIconId.Method,
703 EricPixmapCache.getPixmap("method", imageSize),
670 ) 704 )
671 self.registerImage( 705 self.registerImage(
672 self.MethodProtectedID, 706 EditorIconId.MethodProtected,
673 EricPixmapCache.getPixmap("method_protected", imageSize), 707 EricPixmapCache.getPixmap("method_protected", imageSize),
674 ) 708 )
675 self.registerImage( 709 self.registerImage(
676 self.MethodPrivateID, EricPixmapCache.getPixmap("method_private", imageSize) 710 EditorIconId.MethodPrivate,
711 EricPixmapCache.getPixmap("method_private", imageSize),
677 ) 712 )
678 self.registerImage( 713 self.registerImage(
679 self.AttributeID, EricPixmapCache.getPixmap("attribute", imageSize) 714 EditorIconId.Attribute,
715 EricPixmapCache.getPixmap("attribute", imageSize),
680 ) 716 )
681 self.registerImage( 717 self.registerImage(
682 self.AttributeProtectedID, 718 EditorIconId.AttributeProtected,
683 EricPixmapCache.getPixmap("attribute_protected", imageSize), 719 EricPixmapCache.getPixmap("attribute_protected", imageSize),
684 ) 720 )
685 self.registerImage( 721 self.registerImage(
686 self.AttributePrivateID, 722 EditorIconId.AttributePrivate,
687 EricPixmapCache.getPixmap("attribute_private", imageSize), 723 EricPixmapCache.getPixmap("attribute_private", imageSize),
688 ) 724 )
689 self.registerImage(self.EnumID, EricPixmapCache.getPixmap("enum", imageSize))
690 self.registerImage( 725 self.registerImage(
691 self.KeywordsID, EricPixmapCache.getPixmap("keywords", imageSize) 726 EditorIconId.Enum,
727 EricPixmapCache.getPixmap("enum", imageSize),
692 ) 728 )
693 self.registerImage( 729 self.registerImage(
694 self.ModuleID, EricPixmapCache.getPixmap("module", imageSize) 730 EditorIconId.Keywords,
695 ) 731 EricPixmapCache.getPixmap("keywords", imageSize),
696 732 )
697 self.registerImage( 733 self.registerImage(
698 self.FromDocumentID, EricPixmapCache.getPixmap("editor", imageSize) 734 EditorIconId.Module,
735 EricPixmapCache.getPixmap("module", imageSize),
699 ) 736 )
700 737
701 self.registerImage( 738 self.registerImage(
702 self.TemplateImageID, EricPixmapCache.getPixmap("templateViewer", imageSize) 739 EditorIconId.FromDocument,
740 EricPixmapCache.getPixmap("editor", imageSize),
741 )
742
743 self.registerImage(
744 EditorIconId.TemplateImage,
745 EricPixmapCache.getPixmap("templateViewer", imageSize),
703 ) 746 )
704 747
705 def addClone(self, editor): 748 def addClone(self, editor):
706 """ 749 """
707 Public method to add a clone to our list. 750 Public method to add a clone to our list.
2573 @type int 2616 @type int
2574 """ 2617 """
2575 if self.inLinesChanged: 2618 if self.inLinesChanged:
2576 return 2619 return
2577 2620
2578 for handle in self.breaks: 2621 for handle in list(self.breaks):
2579 if self.markerLine(handle) == line - 1: 2622 if self.markerLine(handle) == line - 1:
2580 del self.breaks[handle] 2623 del self.breaks[handle]
2581 self.markerDeleteHandle(handle) 2624 self.markerDeleteHandle(handle)
2582 self.__markerMap.update() 2625 self.__markerMap.update()
2583 return 2626 return
2875 Public method to toggle a bookmark. 2918 Public method to toggle a bookmark.
2876 2919
2877 @param line line number of the bookmark 2920 @param line line number of the bookmark
2878 @type int 2921 @type int
2879 """ 2922 """
2880 for handle in self.bookmarks: 2923 for handle in self.bookmarks[:]:
2881 if self.markerLine(handle) == line - 1: 2924 if self.markerLine(handle) == line - 1:
2882 self.bookmarks.remove(handle) 2925 self.bookmarks.remove(handle)
2883 self.markerDeleteHandle(handle) 2926 self.markerDeleteHandle(handle)
2884 break 2927 break
2885 else: 2928 else:
3508 self.lastModified = pathlib.Path(fn).stat().st_mtime 3551 self.lastModified = pathlib.Path(fn).stat().st_mtime
3509 3552
3510 @pyqtSlot() 3553 @pyqtSlot()
3511 def __convertTabs(self): 3554 def __convertTabs(self):
3512 """ 3555 """
3513 Private slot to convert tabulators to spaces. 3556 Private slot to automatically convert tabulators to spaces upon loading.
3514 """ 3557 """
3515 if ( 3558 if (
3516 (not self.__getEditorConfig("TabForIndentation")) 3559 (not self.__getEditorConfig("TabForIndentation"))
3517 and Preferences.getEditor("ConvertTabsOnLoad") 3560 and Preferences.getEditor("ConvertTabsOnLoad")
3518 and not (self.lexer_ and self.lexer_.alwaysKeepTabs()) 3561 and not (self.lexer_ and self.lexer_.alwaysKeepTabs())
3519 ): 3562 ):
3520 txt = self.text() 3563 self.expandTabs()
3521 txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth")) 3564
3522 if txtExpanded != txt: 3565 @pyqtSlot()
3523 self.beginUndoAction() 3566 def expandTabs(self):
3524 self.setText(txt) 3567 """
3525 self.endUndoAction() 3568 Public slot to expand tabulators to spaces.
3526 3569 """
3527 self.setModified(True) 3570 txt = self.text()
3571 txtExpanded = txt.expandtabs(self.__getEditorConfig("TabWidth"))
3572 if txtExpanded != txt:
3573 self.beginUndoAction()
3574 self.setText(txtExpanded)
3575 self.endUndoAction()
3576
3577 self.setModified(True)
3528 3578
3529 def __removeTrailingWhitespace(self): 3579 def __removeTrailingWhitespace(self):
3530 """ 3580 """
3531 Private method to remove trailing whitespace. 3581 Private method to remove trailing whitespace.
3532 """ 3582 """
3926 elif margin == self.__indicMargin: 3976 elif margin == self.__indicMargin:
3927 if self.markersAtLine(line) & (1 << self.syntaxerror): 3977 if self.markersAtLine(line) & (1 << self.syntaxerror):
3928 self.__showSyntaxError(line) 3978 self.__showSyntaxError(line)
3929 elif self.markersAtLine(line) & (1 << self.warning): 3979 elif self.markersAtLine(line) & (1 << self.warning):
3930 self.__showWarning(line) 3980 self.__showWarning(line)
3981
3982 @pyqtSlot(int, int, int)
3983 def __marginHoverStart(self, pos, x, y):
3984 """
3985 Private slot showing the text of a syntax error or a warning marker.
3986
3987 @param pos mouse position into the document
3988 @type int
3989 @param x x-value of mouse screen position
3990 @type int
3991 @param y y-value of mouse screen position
3992 @type int
3993 """
3994 margin = self.__marginNumber(x)
3995 if margin == self.__indicMargin:
3996 # determine width of all margins; needed to calculate document line
3997 width = 0
3998 for margin in range(5):
3999 width += self.marginWidth(margin)
4000
4001 message = ""
4002 line = self.lineIndexFromPoint(QPoint(width + 1, y))[0]
4003 if self.markersAtLine(line) & (1 << self.syntaxerror):
4004 for handle in self.syntaxerrors:
4005 if self.markerLine(handle) == line:
4006 message = "\n".join([e[0] for e in self.syntaxerrors[handle]])
4007 break
4008 elif self.markersAtLine(line) & (1 << self.warning):
4009 for handle in self._warnings:
4010 if self.markerLine(handle) == line:
4011 message = "\n".join([w[0] for w in self._warnings[handle]])
4012 break
4013
4014 if message:
4015 QToolTip.showText(QCursor.pos(), message)
4016
4017 @pyqtSlot()
4018 def __marginHoverEnd(self):
4019 """
4020 Private slot cancelling the display of syntax error or a warning marker text.
4021 """
4022 QToolTip.hideText()
3931 4023
3932 @pyqtSlot() 4024 @pyqtSlot()
3933 def handleMonospacedEnable(self): 4025 def handleMonospacedEnable(self):
3934 """ 4026 """
3935 Public slot to handle the Use Monospaced Font context menu entry. 4027 Public slot to handle the Use Monospaced Font context menu entry.
6665 self.syntaxCheckService is None 6757 self.syntaxCheckService is None
6666 or fileType not in self.syntaxCheckService.getLanguages() 6758 or fileType not in self.syntaxCheckService.getLanguages()
6667 ): 6759 ):
6668 return 6760 return
6669 6761
6670 if Preferences.getEditor("AutoCheckSyntax") and Preferences.getEditor( 6762 if Preferences.getEditor("OnlineSyntaxCheck"):
6671 "OnlineSyntaxCheck"
6672 ):
6673 self.__onlineSyntaxCheckTimer.stop() 6763 self.__onlineSyntaxCheckTimer.stop()
6674 6764
6675 if self.isPy3File(): 6765 if self.isPy3File():
6676 additionalBuiltins = ( 6766 additionalBuiltins = (
6677 self.project.getData("CHECKERSPARMS", "SyntaxChecker", {}).get( 6767 self.project.getData("CHECKERSPARMS", "SyntaxChecker", {}).get(
6737 if error: 6827 if error:
6738 _fn, lineno, col, code, msg = error 6828 _fn, lineno, col, code, msg = error
6739 self.toggleSyntaxError(lineno, col, True, msg) 6829 self.toggleSyntaxError(lineno, col, True, msg)
6740 6830
6741 for _fn, lineno, col, _code, msg in problems.get("py_warnings", []): 6831 for _fn, lineno, col, _code, msg in problems.get("py_warnings", []):
6742 self.toggleWarning(lineno, col, True, msg, warningType=Editor.WarningPython) 6832 self.toggleWarning(
6833 lineno, col, True, msg, warningType=EditorWarningKind.Python
6834 )
6743 6835
6744 for _fn, lineno, col, _code, msg in problems.get("warnings", []): 6836 for _fn, lineno, col, _code, msg in problems.get("warnings", []):
6745 self.toggleWarning(lineno, col, True, msg, warningType=Editor.WarningCode) 6837 self.toggleWarning(
6838 lineno, col, True, msg, warningType=EditorWarningKind.Code
6839 )
6746 6840
6747 self.updateVerticalScrollBar() 6841 self.updateVerticalScrollBar()
6748 6842
6749 @pyqtSlot() 6843 @pyqtSlot()
6750 def __initOnlineSyntaxCheck(self): 6844 def __initOnlineSyntaxCheck(self):
7195 ########################################################################### 7289 ###########################################################################
7196 ## Warning handling methods below 7290 ## Warning handling methods below
7197 ########################################################################### 7291 ###########################################################################
7198 7292
7199 def toggleWarning( 7293 def toggleWarning(
7200 self, line, col, setWarning, msg="", warningType=WarningCode # noqa: U100 7294 self,
7295 line,
7296 col, # noqa: U100
7297 setWarning,
7298 msg="",
7299 warningType=EditorWarningKind.Code,
7201 ): 7300 ):
7202 """ 7301 """
7203 Public method to toggle a warning indicator. 7302 Public method to toggle a warning indicator.
7204 7303
7205 Note: This method is used to set pyflakes and code style warnings. 7304 Note: This method is used to set pyflakes and code style warnings.
7212 set or deleted 7311 set or deleted
7213 @type bool 7312 @type bool
7214 @param msg warning message 7313 @param msg warning message
7215 @type str 7314 @type str
7216 @param warningType type of warning message 7315 @param warningType type of warning message
7217 @type int 7316 @type EditorWarningKind
7218 """ 7317 """
7219 if line == 0: 7318 if line == 0:
7220 line = 1 7319 line = 1
7221 # hack to show a warning marker, if line is reported to be 0 7320 # hack to show a warning marker, if line is reported to be 0
7222 7321
7316 @pyqtSlot() 7415 @pyqtSlot()
7317 def clearFlakesWarnings(self): 7416 def clearFlakesWarnings(self):
7318 """ 7417 """
7319 Public slot to clear all pyflakes warnings. 7418 Public slot to clear all pyflakes warnings.
7320 """ 7419 """
7321 self.__clearTypedWarning(Editor.WarningCode) 7420 self.__clearTypedWarning(EditorWarningKind.Code)
7322 self.__clearTypedWarning(Editor.WarningPython) 7421 self.__clearTypedWarning(EditorWarningKind.Python)
7323 7422
7324 @pyqtSlot() 7423 @pyqtSlot()
7325 def clearStyleWarnings(self): 7424 def clearStyleWarnings(self):
7326 """ 7425 """
7327 Public slot to clear all style warnings. 7426 Public slot to clear all style warnings.
7328 """ 7427 """
7329 self.__clearTypedWarning(Editor.WarningStyle) 7428 self.__clearTypedWarning(EditorWarningKind.Style)
7330 7429
7331 @pyqtSlot() 7430 @pyqtSlot()
7332 def clearInfoWarnings(self): 7431 def clearInfoWarnings(self):
7333 """ 7432 """
7334 Public slot to clear all info warnings. 7433 Public slot to clear all info warnings.
7335 """ 7434 """
7336 self.__clearTypedWarning(Editor.WarningInfo) 7435 self.__clearTypedWarning(EditorWarningKind.Info)
7337 7436
7338 @pyqtSlot() 7437 @pyqtSlot()
7339 def clearErrorWarnings(self): 7438 def clearErrorWarnings(self):
7340 """ 7439 """
7341 Public slot to clear all error warnings. 7440 Public slot to clear all error warnings.
7342 """ 7441 """
7343 self.__clearTypedWarning(Editor.WarningError) 7442 self.__clearTypedWarning(EditorWarningKind.Error)
7344 7443
7345 @pyqtSlot() 7444 @pyqtSlot()
7346 def clearCodeWarnings(self): 7445 def clearCodeWarnings(self):
7347 """ 7446 """
7348 Public slot to clear all code warnings. 7447 Public slot to clear all code warnings.
7349 """ 7448 """
7350 self.__clearTypedWarning(Editor.WarningCode) 7449 self.__clearTypedWarning(EditorWarningKind.Code)
7351 7450
7352 def __clearTypedWarning(self, warningKind): 7451 def __clearTypedWarning(self, warningKind):
7353 """ 7452 """
7354 Private method to clear warnings of a specific kind. 7453 Private method to clear warnings of a specific kind.
7355 7454
7356 @param warningKind kind of warning to clear (Editor.WarningCode, 7455 @param warningKind kind of warning to clear
7357 Editor.WarningPython, Editor.WarningStyle) 7456 @type EditorWarningKind
7358 @type int
7359 """ 7457 """
7360 for handle in list(self._warnings): 7458 for handle in list(self._warnings):
7361 issues = [] 7459 issues = []
7362 for msg, warningType in self._warnings[handle]: 7460 for msg, warningType in self._warnings[handle]:
7363 if warningType == warningKind: 7461 if warningType == warningKind:
7485 7583
7486 # step 1: do warnings 7584 # step 1: do warnings
7487 for handle in self._warnings: 7585 for handle in self._warnings:
7488 if self.markerLine(handle) == line: 7586 if self.markerLine(handle) == line:
7489 for msg, warningType in self._warnings[handle]: 7587 for msg, warningType in self._warnings[handle]:
7490 if warningType == Editor.WarningInfo: 7588 if warningType == EditorWarningKind.Info:
7491 infoAnnotations.append(self.tr("Info: {0}").format(msg)) 7589 infoAnnotations.append(self.tr("Info: {0}").format(msg))
7492 elif warningType == Editor.WarningError: 7590 elif warningType == EditorWarningKind.Error:
7493 errorAnnotations.append(self.tr("Error: {0}").format(msg)) 7591 errorAnnotations.append(self.tr("Error: {0}").format(msg))
7494 elif warningType == Editor.WarningStyle: 7592 elif warningType == EditorWarningKind.Style:
7495 styleAnnotations.append(self.tr("Style: {0}").format(msg)) 7593 styleAnnotations.append(self.tr("Style: {0}").format(msg))
7496 elif warningType == Editor.WarningPython: 7594 elif warningType == EditorWarningKind.Python:
7497 warningAnnotations.append(msg) 7595 warningAnnotations.append(msg)
7498 else: 7596 else:
7499 warningAnnotations.append( 7597 warningAnnotations.append(
7500 self.tr("Warning: {0}").format(msg) 7598 self.tr("Warning: {0}").format(msg)
7501 ) 7599 )
8595 return 8693 return
8596 elif len(templateNames) > 1: 8694 elif len(templateNames) > 1:
8597 self.showUserList( 8695 self.showUserList(
8598 TemplateCompletionListID, 8696 TemplateCompletionListID,
8599 [ 8697 [
8600 "{0}?{1:d}".format(t, self.TemplateImageID) 8698 "{0}?{1:d}".format(t, EditorIconId.TemplateImage)
8601 for t in templateNames 8699 for t in templateNames
8602 ], 8700 ],
8603 ) 8701 )
8604 return 8702 return
8605 8703

eric ide

mercurial