7 Module implementing an editor for simple editing tasks. |
7 Module implementing an editor for simple editing tasks. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 import re |
11 import re |
|
12 import contextlib |
|
13 |
|
14 import editorconfig |
12 |
15 |
13 from PyQt5.QtCore import ( |
16 from PyQt5.QtCore import ( |
14 pyqtSignal, Qt, QSignalMapper, QPoint, QTimer, QFileInfo, QSize, |
17 pyqtSignal, Qt, QSignalMapper, QPoint, QTimer, QFileInfo, QSize, |
15 QCoreApplication |
18 QCoreApplication |
16 ) |
19 ) |
92 matchingPairs = ['()', '[]', '{}', '<>', "''", '""'] |
93 matchingPairs = ['()', '[]', '{}', '<>', "''", '""'] |
93 # __IGNORE_WARNING_M613__ |
94 # __IGNORE_WARNING_M613__ |
94 if text in matchingPairs: |
95 if text in matchingPairs: |
95 self.delete() |
96 self.delete() |
96 |
97 |
97 super(MiniScintilla, self).editorCommand(cmd) |
98 super().editorCommand(cmd) |
98 |
99 |
99 def keyPressEvent(self, ev): |
100 def keyPressEvent(self, ev): |
100 """ |
101 """ |
101 Protected method to handle the user input a key at a time. |
102 Protected method to handle the user input a key at a time. |
102 |
103 |
111 @param encString string to use to enclose the selection |
112 @param encString string to use to enclose the selection |
112 (one or two characters) |
113 (one or two characters) |
113 @type str |
114 @type str |
114 """ |
115 """ |
115 startChar = encString[0] |
116 startChar = encString[0] |
116 if len(encString) == 2: |
117 endChar = encString[1] if len(encString) == 2 else startChar |
117 endChar = encString[1] |
|
118 else: |
|
119 endChar = startChar |
|
120 |
118 |
121 sline, sindex, eline, eindex = self.getSelection() |
119 sline, sindex, eline, eindex = self.getSelection() |
122 replaceText = startChar + self.selectedText() + endChar |
120 replaceText = startChar + self.selectedText() + endChar |
123 self.beginUndoAction() |
121 self.beginUndoAction() |
124 self.replaceSelectedText(replaceText) |
122 self.replaceSelectedText(replaceText) |
127 |
125 |
128 txt = ev.text() |
126 txt = ev.text() |
129 |
127 |
130 # See it is text to insert. |
128 # See it is text to insert. |
131 if len(txt) and txt >= " ": |
129 if len(txt) and txt >= " ": |
132 if self.hasSelectedText(): |
130 if ( |
133 if txt in MiniScintilla.EncloseChars: |
131 self.hasSelectedText() and |
134 encloseSelectedText(MiniScintilla.EncloseChars[txt]) |
132 txt in MiniScintilla.EncloseChars |
135 ev.accept() |
133 ): |
136 return |
134 encloseSelectedText(MiniScintilla.EncloseChars[txt]) |
|
135 ev.accept() |
|
136 return |
137 |
137 |
138 super(MiniScintilla, self).keyPressEvent(ev) |
138 super().keyPressEvent(ev) |
139 else: |
139 else: |
140 ev.ignore() |
140 ev.ignore() |
141 |
141 |
142 def focusInEvent(self, event): |
142 def focusInEvent(self, event): |
143 """ |
143 """ |
150 |
150 |
151 @param event the event object |
151 @param event the event object |
152 @type QFocusEvent |
152 @type QFocusEvent |
153 """ |
153 """ |
154 self.mw.editorActGrp.setEnabled(True) |
154 self.mw.editorActGrp.setEnabled(True) |
155 try: |
155 with contextlib.suppress(AttributeError): |
156 self.setCaretWidth(self.mw.caretWidth) |
156 self.setCaretWidth(self.mw.caretWidth) |
157 except AttributeError: |
|
158 pass |
|
159 |
157 |
160 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
158 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
161 |
159 |
162 super(MiniScintilla, self).focusInEvent(event) |
160 super().focusInEvent(event) |
163 |
161 |
164 def focusOutEvent(self, event): |
162 def focusOutEvent(self, event): |
165 """ |
163 """ |
166 Protected method called when the editor loses focus. |
164 Protected method called when the editor loses focus. |
167 |
165 |
169 @type QFocusEvent |
167 @type QFocusEvent |
170 """ |
168 """ |
171 self.mw.editorActGrp.setEnabled(False) |
169 self.mw.editorActGrp.setEnabled(False) |
172 self.setCaretWidth(0) |
170 self.setCaretWidth(0) |
173 |
171 |
174 super(MiniScintilla, self).focusOutEvent(event) |
172 super().focusOutEvent(event) |
175 |
173 |
176 def removeTrailingWhitespace(self): |
174 def removeTrailingWhitespace(self): |
177 """ |
175 """ |
178 Public method to remove trailing whitespace. |
176 Public method to remove trailing whitespace. |
179 """ |
177 """ |
214 @param filename name of the file to open (string) |
212 @param filename name of the file to open (string) |
215 @param filetype type of the source file (string) |
213 @param filetype type of the source file (string) |
216 @param parent reference to the parent widget (QWidget) |
214 @param parent reference to the parent widget (QWidget) |
217 @param name object name of the window (string) |
215 @param name object name of the window (string) |
218 """ |
216 """ |
219 super(MiniEditor, self).__init__(parent) |
217 super().__init__(parent) |
220 if name is not None: |
218 if name is not None: |
221 self.setObjectName(name) |
219 self.setObjectName(name) |
222 self.setWindowIcon(UI.PixmapCache.getIcon("editor")) |
220 self.setWindowIcon(UI.PixmapCache.getIcon("editor")) |
223 |
221 |
224 self.setStyle(Preferences.getUI("Style"), |
222 self.setStyle(Preferences.getUI("Style"), |
2738 @type bool |
2736 @type bool |
2739 """ |
2737 """ |
2740 if "[*]" not in self.windowTitle(): |
2738 if "[*]" not in self.windowTitle(): |
2741 self.setWindowTitle(self.tr("[*] - {0}") |
2739 self.setWindowTitle(self.tr("[*] - {0}") |
2742 .format(self.tr("Mini Editor"))) |
2740 .format(self.tr("Mini Editor"))) |
2743 super(MiniEditor, self).setWindowModified(modified) |
2741 super().setWindowModified(modified) |
2744 |
2742 |
2745 def __setCurrentFile(self, fileName): |
2743 def __setCurrentFile(self, fileName): |
2746 """ |
2744 """ |
2747 Private method to register the file name of the current file. |
2745 Private method to register the file name of the current file. |
2748 |
2746 |
2749 @param fileName name of the file to register (string) |
2747 @param fileName name of the file to register (string) |
2750 """ |
2748 """ |
2751 self.__curFile = fileName |
2749 self.__curFile = fileName |
2752 |
2750 |
2753 if not self.__curFile: |
2751 shownName = ( |
2754 shownName = self.tr("Untitled") |
2752 self.tr("Untitled") |
2755 else: |
2753 if not self.__curFile else |
2756 shownName = self.__strippedName(self.__curFile) |
2754 self.__strippedName(self.__curFile) |
|
2755 ) |
2757 |
2756 |
2758 self.setWindowTitle(self.tr("{0}[*] - {1}") |
2757 self.setWindowTitle(self.tr("{0}[*] - {1}") |
2759 .format(shownName, self.tr("Mini Editor"))) |
2758 .format(shownName, self.tr("Mini Editor"))) |
2760 |
2759 |
2761 self.__textEdit.setModified(False) |
2760 self.__textEdit.setModified(False) |
2860 |
2859 |
2861 # set margin 2 settings |
2860 # set margin 2 settings |
2862 self.__textEdit.setMarginWidth(2, 16) |
2861 self.__textEdit.setMarginWidth(2, 16) |
2863 if Preferences.getEditor("FoldingMargin"): |
2862 if Preferences.getEditor("FoldingMargin"): |
2864 folding = Preferences.getEditor("FoldingStyle") |
2863 folding = Preferences.getEditor("FoldingStyle") |
2865 try: |
2864 with contextlib.suppress(AttributeError): |
2866 folding = QsciScintilla.FoldStyle(folding) |
2865 folding = QsciScintilla.FoldStyle(folding) |
2867 except AttributeError: |
|
2868 pass |
|
2869 self.__textEdit.setFolding(folding) |
2866 self.__textEdit.setFolding(folding) |
2870 self.__textEdit.setFoldMarginColors( |
2867 self.__textEdit.setFoldMarginColors( |
2871 Preferences.getEditorColour("FoldmarginBackground"), |
2868 Preferences.getEditorColour("FoldmarginBackground"), |
2872 Preferences.getEditorColour("FoldmarginBackground")) |
2869 Preferences.getEditorColour("FoldmarginBackground")) |
2873 self.__textEdit.setFoldMarkersColors( |
2870 self.__textEdit.setFoldMarkersColors( |
3397 self.__textEdit.setLexer(self.lexer_) |
3394 self.__textEdit.setLexer(self.lexer_) |
3398 if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None: |
3395 if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None: |
3399 self.__textEdit.SCN_STYLENEEDED.connect(self.__styleNeeded) |
3396 self.__textEdit.SCN_STYLENEEDED.connect(self.__styleNeeded) |
3400 |
3397 |
3401 # get the font for style 0 and set it as the default font |
3398 # get the font for style 0 and set it as the default font |
3402 if pyname and pyname.startswith("Pygments|"): |
3399 key = ( |
3403 key = 'Scintilla/Guessed/style0/font' |
3400 'Scintilla/Guessed/style0/font' |
3404 else: |
3401 if pyname and pyname.startswith("Pygments|") else |
3405 key = 'Scintilla/{0}/style0/font'.format(self.lexer_.language()) |
3402 'Scintilla/{0}/style0/font'.format(self.lexer_.language()) |
|
3403 ) |
3406 fdesc = Preferences.Prefs.settings.value(key) |
3404 fdesc = Preferences.Prefs.settings.value(key) |
3407 if fdesc is not None: |
3405 if fdesc is not None: |
3408 font = QFont(fdesc[0], int(fdesc[1])) |
3406 font = QFont(fdesc[0], int(fdesc[1])) |
3409 self.lexer_.setDefaultFont(font) |
3407 self.lexer_.setDefaultFont(font) |
3410 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
3408 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
3455 elif self.filetype in ["Python", "Python3", "MicroPython"]: |
3453 elif self.filetype in ["Python", "Python3", "MicroPython"]: |
3456 bindName = "dummy.py" |
3454 bindName = "dummy.py" |
3457 |
3455 |
3458 if not bindName and line0.startswith("#!"): |
3456 if not bindName and line0.startswith("#!"): |
3459 # #! marker detection |
3457 # #! marker detection |
3460 if "python3" in line0: |
3458 if ( |
3461 bindName = "dummy.py" |
3459 "python3" in line0 or |
3462 self.filetype = "Python3" |
3460 "python" in line0 |
3463 elif "python" in line0: |
3461 ): |
3464 bindName = "dummy.py" |
3462 bindName = "dummy.py" |
3465 self.filetype = "Python3" |
3463 self.filetype = "Python3" |
3466 elif ("/bash" in line0 or "/sh" in line0): |
3464 elif ("/bash" in line0 or "/sh" in line0): |
3467 bindName = "dummy.sh" |
3465 bindName = "dummy.sh" |
3468 elif "ruby" in line0: |
3466 elif "ruby" in line0: |
3547 if wc is None: |
3545 if wc is None: |
3548 pattern = r"\b[\w_]+\b" |
3546 pattern = r"\b[\w_]+\b" |
3549 else: |
3547 else: |
3550 wc = re.sub(r'\w', "", wc) |
3548 wc = re.sub(r'\w', "", wc) |
3551 pattern = r"\b[\w{0}]+\b".format(re.escape(wc)) |
3549 pattern = r"\b[\w{0}]+\b".format(re.escape(wc)) |
3552 if self.__textEdit.caseSensitive(): |
3550 rx = ( |
3553 rx = re.compile(pattern) |
3551 re.compile(pattern) |
3554 else: |
3552 if self.__textEdit.caseSensitive() else |
3555 rx = re.compile(pattern, re.IGNORECASE) |
3553 re.compile(pattern, re.IGNORECASE) |
|
3554 ) |
3556 |
3555 |
3557 text = self.text(line) |
3556 text = self.text(line) |
3558 for match in rx.finditer(text): |
3557 for match in rx.finditer(text): |
3559 start, end = match.span() |
3558 start, end = match.span() |
3560 if start <= index <= end: |
3559 if start <= index <= end: |