8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 import re |
11 import re |
12 import difflib |
12 import difflib |
|
13 import contextlib |
|
14 |
|
15 import editorconfig |
13 |
16 |
14 from PyQt5.QtCore import ( |
17 from PyQt5.QtCore import ( |
15 pyqtSignal, pyqtSlot, Qt, QDir, QTimer, QModelIndex, QFileInfo, |
18 pyqtSignal, pyqtSlot, Qt, QDir, QTimer, QModelIndex, QFileInfo, |
16 QCryptographicHash, QEvent, QDateTime, QPoint |
19 QCryptographicHash, QEvent, QDateTime, QPoint |
17 ) |
20 ) |
686 elif self.filetype in ["Python", "Python3", "MicroPython"]: |
687 elif self.filetype in ["Python", "Python3", "MicroPython"]: |
687 bindName = "dummy.py" |
688 bindName = "dummy.py" |
688 |
689 |
689 if not bindName and line0.startswith("#!"): |
690 if not bindName and line0.startswith("#!"): |
690 # #! marker detection |
691 # #! marker detection |
691 if "python3" in line0: |
692 if ( |
692 bindName = "dummy.py" |
693 "python3" in line0 or |
693 self.filetype = "Python3" |
694 "python" in line0 or |
694 elif "python" in line0: |
695 "pypy3" in line0 or |
695 bindName = "dummy.py" |
696 "pypy" in line0 |
696 self.filetype = "Python3" |
697 ): |
697 elif "pypy3" in line0: |
|
698 bindName = "dummy.py" |
|
699 self.filetype = "Python3" |
|
700 elif "pypy" in line0: |
|
701 bindName = "dummy.py" |
698 bindName = "dummy.py" |
702 self.filetype = "Python3" |
699 self.filetype = "Python3" |
703 elif ("/bash" in line0 or "/sh" in line0): |
700 elif ("/bash" in line0 or "/sh" in line0): |
704 bindName = "dummy.sh" |
701 bindName = "dummy.sh" |
705 elif "ruby" in line0: |
702 elif "ruby" in line0: |
1615 |
1612 |
1616 def __checkEncoding(self): |
1613 def __checkEncoding(self): |
1617 """ |
1614 """ |
1618 Private method to check the selected encoding of the encodings submenu. |
1615 Private method to check the selected encoding of the encodings submenu. |
1619 """ |
1616 """ |
1620 try: |
1617 with contextlib.suppress(AttributeError, KeyError): |
1621 (self.supportedEncodings[self.__normalizedEncoding()] |
1618 (self.supportedEncodings[self.__normalizedEncoding()] |
1622 .setChecked(True)) |
1619 .setChecked(True)) |
1623 except (AttributeError, KeyError): |
|
1624 pass |
|
1625 |
1620 |
1626 def __encodingChanged(self, encoding, propagate=True): |
1621 def __encodingChanged(self, encoding, propagate=True): |
1627 """ |
1622 """ |
1628 Private slot to handle a change of the encoding. |
1623 Private slot to handle a change of the encoding. |
1629 |
1624 |
1672 |
1667 |
1673 def __checkEol(self): |
1668 def __checkEol(self): |
1674 """ |
1669 """ |
1675 Private method to check the selected eol type of the eol submenu. |
1670 Private method to check the selected eol type of the eol submenu. |
1676 """ |
1671 """ |
1677 try: |
1672 with contextlib.suppress(AttributeError, TypeError): |
1678 self.supportedEols[self.getLineSeparator()].setChecked(True) |
1673 self.supportedEols[self.getLineSeparator()].setChecked(True) |
1679 except (AttributeError, TypeError): |
|
1680 pass |
|
1681 |
1674 |
1682 def __eolChanged(self): |
1675 def __eolChanged(self): |
1683 """ |
1676 """ |
1684 Private slot to handle a change of the eol mode. |
1677 Private slot to handle a change of the eol mode. |
1685 """ |
1678 """ |
1712 def __checkSpellLanguage(self): |
1705 def __checkSpellLanguage(self): |
1713 """ |
1706 """ |
1714 Private slot to check the selected spell check language action. |
1707 Private slot to check the selected spell check language action. |
1715 """ |
1708 """ |
1716 language = self.getSpellingLanguage() |
1709 language = self.getSpellingLanguage() |
1717 try: |
1710 with contextlib.suppress(AttributeError, KeyError): |
1718 self.supportedSpellLanguages[language].setChecked(True) |
1711 self.supportedSpellLanguages[language].setChecked(True) |
1719 except (AttributeError, KeyError): |
|
1720 pass |
|
1721 |
1712 |
1722 def __spellLanguageChanged(self, language, propagate=True): |
1713 def __spellLanguageChanged(self, language, propagate=True): |
1723 """ |
1714 """ |
1724 Private slot to handle a change of the spell check language. |
1715 Private slot to handle a change of the spell check language. |
1725 |
1716 |
1811 self.__setMarginsDisplay() |
1802 self.__setMarginsDisplay() |
1812 if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None: |
1803 if self.lexer_.lexer() == "container" or self.lexer_.lexer() is None: |
1813 self.SCN_STYLENEEDED.connect(self.__styleNeeded) |
1804 self.SCN_STYLENEEDED.connect(self.__styleNeeded) |
1814 |
1805 |
1815 # get the font for style 0 and set it as the default font |
1806 # get the font for style 0 and set it as the default font |
1816 if pyname and pyname.startswith("Pygments|"): |
1807 key = ( |
1817 key = 'Scintilla/Guessed/style0/font' |
1808 'Scintilla/Guessed/style0/font' |
1818 else: |
1809 if pyname and pyname.startswith("Pygments|") else |
1819 key = 'Scintilla/{0}/style0/font'.format(self.lexer_.language()) |
1810 'Scintilla/{0}/style0/font'.format(self.lexer_.language()) |
|
1811 ) |
1820 fdesc = Preferences.Prefs.settings.value(key) |
1812 fdesc = Preferences.Prefs.settings.value(key) |
1821 if fdesc is not None: |
1813 if fdesc is not None: |
1822 font = QFont(fdesc[0], int(fdesc[1])) |
1814 font = QFont(fdesc[0], int(fdesc[1])) |
1823 self.lexer_.setDefaultFont(font) |
1815 self.lexer_.setDefaultFont(font) |
1824 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
1816 self.lexer_.readSettings(Preferences.Prefs.settings, "Scintilla") |
1827 |
1819 |
1828 # now set the lexer properties |
1820 # now set the lexer properties |
1829 self.lexer_.initProperties() |
1821 self.lexer_.initProperties() |
1830 |
1822 |
1831 # initialize the lexer APIs settings |
1823 # initialize the lexer APIs settings |
1832 if self.project.isOpen() and self.project.isProjectFile(filename): |
1824 projectType = ( |
1833 projectType = self.project.getProjectType() |
1825 self.project.getProjectType() |
1834 else: |
1826 if self.project.isOpen() and self.project.isProjectFile(filename) |
1835 projectType = "" |
1827 else "" |
|
1828 ) |
1836 api = self.vm.getAPIsManager().getAPIs(self.apiLanguage, |
1829 api = self.vm.getAPIsManager().getAPIs(self.apiLanguage, |
1837 projectType=projectType) |
1830 projectType=projectType) |
1838 if api is not None and not api.isEmpty(): |
1831 if api is not None and not api.isEmpty(): |
1839 self.lexer_.setAPIs(api.getQsciAPIs()) |
1832 self.lexer_.setAPIs(api.getQsciAPIs()) |
1840 self.acAPI = True |
1833 self.acAPI = True |
2163 @return flag indicating a Javascript file (boolean) |
2156 @return flag indicating a Javascript file (boolean) |
2164 """ |
2157 """ |
2165 if self.filetype == "JavaScript": |
2158 if self.filetype == "JavaScript": |
2166 return True |
2159 return True |
2167 |
2160 |
2168 if self.filetype == "": |
2161 if ( |
2169 if ( |
2162 self.filetype == "" and |
2170 self.fileName and |
2163 self.fileName and |
2171 os.path.splitext(self.fileName)[1] == ".js" |
2164 os.path.splitext(self.fileName)[1] == ".js" |
2172 ): |
2165 ): |
2173 self.filetype = "JavaScript" |
2166 self.filetype = "JavaScript" |
2174 return True |
2167 return True |
2175 |
2168 |
2176 return False |
2169 return False |
2177 |
2170 |
2178 def highlightVisible(self): |
2171 def highlightVisible(self): |
2179 """ |
2172 """ |
2246 @param annotationLinesAdded number of added/deleted annotation lines |
2239 @param annotationLinesAdded number of added/deleted annotation lines |
2247 (integer) |
2240 (integer) |
2248 """ |
2241 """ |
2249 if ( |
2242 if ( |
2250 mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) and |
2243 mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) and |
2251 linesAdded != 0 |
2244 linesAdded != 0 and |
|
2245 self.breaks |
2252 ): |
2246 ): |
2253 if self.breaks: |
2247 bps = [] # list of breakpoints |
2254 bps = [] # list of breakpoints |
2248 for handle, (ln, cond, temp, enabled, ignorecount) in ( |
2255 for handle, (ln, cond, temp, enabled, ignorecount) in ( |
2249 self.breaks.items() |
2256 self.breaks.items() |
2250 ): |
2257 ): |
2251 line = self.markerLine(handle) + 1 |
2258 line = self.markerLine(handle) + 1 |
2252 if ln != line: |
2259 if ln != line: |
2253 bps.append((ln, line)) |
2260 bps.append((ln, line)) |
2254 self.breaks[handle] = (line, cond, temp, enabled, |
2261 self.breaks[handle] = (line, cond, temp, enabled, |
2255 ignorecount) |
2262 ignorecount) |
2256 self.inLinesChanged = True |
2263 self.inLinesChanged = True |
2257 for ln, line in sorted(bps, reverse=linesAdded > 0): |
2264 for ln, line in sorted(bps, reverse=linesAdded > 0): |
2258 index1 = self.breakpointModel.getBreakPointIndex( |
2265 index1 = self.breakpointModel.getBreakPointIndex( |
2259 self.fileName, ln) |
2266 self.fileName, ln) |
2260 index2 = self.breakpointModel.index(index1.row(), 1) |
2267 index2 = self.breakpointModel.index(index1.row(), 1) |
2261 self.breakpointModel.setData(index2, line) |
2268 self.breakpointModel.setData(index2, line) |
2262 self.inLinesChanged = False |
2269 self.inLinesChanged = False |
|
2270 |
2263 |
2271 def __restoreBreakpoints(self): |
2264 def __restoreBreakpoints(self): |
2272 """ |
2265 """ |
2273 Private method to restore the breakpoints. |
2266 Private method to restore the breakpoints. |
2274 """ |
2267 """ |
2847 self.markerDeleteAll(self.taskmarker) |
2840 self.markerDeleteAll(self.taskmarker) |
2848 self.taskViewer.clearFileTasks(self.fileName) |
2841 self.taskViewer.clearFileTasks(self.fileName) |
2849 self.__hasTaskMarkers = False |
2842 self.__hasTaskMarkers = False |
2850 |
2843 |
2851 # now search tasks and record them |
2844 # now search tasks and record them |
2852 lineIndex = -1 |
2845 for lineIndex, line in enumerate(txtList): |
2853 for line in txtList: |
|
2854 lineIndex += 1 |
|
2855 shouldBreak = False |
2846 shouldBreak = False |
2856 |
2847 |
2857 if line.endswith("__NO-TASK__"): |
2848 if line.endswith("__NO-TASK__"): |
2858 # ignore potential task marker |
2849 # ignore potential task marker |
2859 continue |
2850 continue |
3254 permissions = os.stat(fn).st_mode |
3245 permissions = os.stat(fn).st_mode |
3255 perms_valid = True |
3246 perms_valid = True |
3256 except OSError: |
3247 except OSError: |
3257 # if there was an error, ignore it |
3248 # if there was an error, ignore it |
3258 perms_valid = False |
3249 perms_valid = False |
3259 try: |
3250 with contextlib.suppress(OSError): |
3260 os.remove(bfn) |
3251 os.remove(bfn) |
3261 except OSError: |
3252 with contextlib.suppress(OSError): |
3262 # if there was an error, ignore it |
|
3263 pass |
|
3264 try: |
|
3265 os.rename(fn, bfn) |
3253 os.rename(fn, bfn) |
3266 except OSError: |
|
3267 # if there was an error, ignore it |
|
3268 pass |
|
3269 |
3254 |
3270 # now write text to the file fn |
3255 # now write text to the file fn |
3271 try: |
3256 try: |
3272 editorConfigEncoding = self.__getEditorConfig( |
3257 editorConfigEncoding = self.__getEditorConfig( |
3273 "DefaultEncoding", nodefault=True, config=config) |
3258 "DefaultEncoding", nodefault=True, config=config) |
3297 if ( |
3282 if ( |
3298 self.fileName and |
3283 self.fileName and |
3299 self.project.startswithProjectPath(self.fileName) |
3284 self.project.startswithProjectPath(self.fileName) |
3300 ): |
3285 ): |
3301 path = os.path.dirname(self.fileName) |
3286 path = os.path.dirname(self.fileName) |
3302 else: |
3287 elif not self.fileName: |
3303 path = self.project.getProjectPath() |
3288 path = self.project.getProjectPath() |
3304 |
3289 |
3305 if not path and self.fileName: |
3290 if not path and self.fileName: |
3306 path = os.path.dirname(self.fileName) |
3291 path = os.path.dirname(self.fileName) |
3307 if not path: |
3292 if not path: |
3362 fn = self.__getSaveFileName(path) |
3347 fn = self.__getSaveFileName(path) |
3363 if not fn: |
3348 if not fn: |
3364 return False |
3349 return False |
3365 |
3350 |
3366 res = self.writeFile(fn) |
3351 res = self.writeFile(fn) |
3367 if res: |
3352 if ( |
|
3353 res and |
|
3354 self.project.isOpen() and |
|
3355 self.project.startswithProjectPath(fn) |
|
3356 ): |
3368 # save to project, if a project is loaded |
3357 # save to project, if a project is loaded |
3369 if ( |
3358 self.project.appendFile(fn) |
3370 self.project.isOpen() and |
|
3371 self.project.startswithProjectPath(fn) |
|
3372 ): |
|
3373 self.project.appendFile(fn) |
|
3374 |
3359 |
3375 return res |
3360 return res |
3376 |
3361 |
3377 def saveFile(self, saveas=False, path=None): |
3362 def saveFile(self, saveas=False, path=None): |
3378 """ |
3363 """ |
3582 if wc is None or not useWordChars: |
3567 if wc is None or not useWordChars: |
3583 pattern = r"\b[\w_]+\b" |
3568 pattern = r"\b[\w_]+\b" |
3584 else: |
3569 else: |
3585 wc = re.sub(r'\w', "", wc) |
3570 wc = re.sub(r'\w', "", wc) |
3586 pattern = r"\b[\w{0}]+\b".format(re.escape(wc)) |
3571 pattern = r"\b[\w{0}]+\b".format(re.escape(wc)) |
3587 if self.caseSensitive(): |
3572 rx = ( |
3588 rx = re.compile(pattern) |
3573 re.compile(pattern) |
3589 else: |
3574 if self.caseSensitive() else |
3590 rx = re.compile(pattern, re.IGNORECASE) |
3575 re.compile(pattern, re.IGNORECASE) |
|
3576 ) |
3591 |
3577 |
3592 text = self.text(line) |
3578 text = self.text(line) |
3593 for match in rx.finditer(text): |
3579 for match in rx.finditer(text): |
3594 start, end = match.span() |
3580 start, end = match.span() |
3595 if start <= index <= end: |
3581 if start <= index <= end: |
3691 |
3677 |
3692 pos = self.positionBefore(pos) |
3678 pos = self.positionBefore(pos) |
3693 ch = self.charAt(pos) |
3679 ch = self.charAt(pos) |
3694 |
3680 |
3695 # Don't go past the end of the previous line |
3681 # Don't go past the end of the previous line |
3696 if ch == '\n' or ch == '\r': |
3682 if ch in ('\n', '\r'): |
3697 return "", pos |
3683 return "", pos |
3698 |
3684 |
3699 return ch, pos |
3685 return ch, pos |
3700 |
3686 |
3701 def getSearchText(self, selectionOnly=False): |
3687 def getSearchText(self, selectionOnly=False): |
3897 line, index = self.getCursorPosition() |
3883 line, index = self.getCursorPosition() |
3898 self.beginUndoAction() |
3884 self.beginUndoAction() |
3899 if Preferences.getEditor("CommentColumn0"): |
3885 if Preferences.getEditor("CommentColumn0"): |
3900 self.insertAt(self.lexer_.commentStr(), line, 0) |
3886 self.insertAt(self.lexer_.commentStr(), line, 0) |
3901 else: |
3887 else: |
3902 self.insertAt(self.lexer_.commentStr(), line, |
3888 lineText = self.text(line) |
3903 self.indentation(line)) |
3889 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) |
|
3890 self.insertAt(self.lexer_.commentStr(), line, pos) |
3904 self.endUndoAction() |
3891 self.endUndoAction() |
3905 |
3892 |
3906 def uncommentLine(self): |
3893 def uncommentLine(self): |
3907 """ |
3894 """ |
3908 Public slot to uncomment the current line. |
3895 Public slot to uncomment the current line. |
3921 # now remove the comment string |
3908 # now remove the comment string |
3922 self.beginUndoAction() |
3909 self.beginUndoAction() |
3923 if Preferences.getEditor("CommentColumn0"): |
3910 if Preferences.getEditor("CommentColumn0"): |
3924 self.setSelection(line, 0, line, len(commentStr)) |
3911 self.setSelection(line, 0, line, len(commentStr)) |
3925 else: |
3912 else: |
3926 self.setSelection(line, self.indentation(line), |
3913 lineText = self.text(line) |
3927 line, self.indentation(line) + len(commentStr)) |
3914 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) |
|
3915 self.setSelection(line, pos, line, pos + len(commentStr)) |
3928 self.removeSelectedText() |
3916 self.removeSelectedText() |
3929 self.endUndoAction() |
3917 self.endUndoAction() |
3930 |
3918 |
3931 def commentSelection(self): |
3919 def commentSelection(self): |
3932 """ |
3920 """ |
3940 |
3928 |
3941 commentStr = self.lexer_.commentStr() |
3929 commentStr = self.lexer_.commentStr() |
3942 |
3930 |
3943 # get the selection boundaries |
3931 # get the selection boundaries |
3944 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
3932 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
3945 if indexTo == 0: |
3933 endLine = lineTo if indexTo else lineTo - 1 |
3946 endLine = lineTo - 1 |
|
3947 else: |
|
3948 endLine = lineTo |
|
3949 |
3934 |
3950 self.beginUndoAction() |
3935 self.beginUndoAction() |
3951 # iterate over the lines |
3936 # iterate over the lines |
3952 for line in range(lineFrom, endLine + 1): |
3937 for line in range(lineFrom, endLine + 1): |
3953 if Preferences.getEditor("CommentColumn0"): |
3938 if Preferences.getEditor("CommentColumn0"): |
3954 self.insertAt(commentStr, line, 0) |
3939 self.insertAt(commentStr, line, 0) |
3955 else: |
3940 else: |
3956 self.insertAt(commentStr, line, self.indentation(line)) |
3941 lineText = self.text(line) |
|
3942 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) |
|
3943 self.insertAt(commentStr, line, pos) |
3957 |
3944 |
3958 # change the selection accordingly |
3945 # change the selection accordingly |
3959 self.setSelection(lineFrom, 0, endLine + 1, 0) |
3946 self.setSelection(lineFrom, 0, endLine + 1, 0) |
3960 self.endUndoAction() |
3947 self.endUndoAction() |
3961 |
3948 |
3971 |
3958 |
3972 commentStr = self.lexer_.commentStr() |
3959 commentStr = self.lexer_.commentStr() |
3973 |
3960 |
3974 # get the selection boundaries |
3961 # get the selection boundaries |
3975 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
3962 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
3976 if indexTo == 0: |
3963 endLine = lineTo if indexTo else lineTo - 1 |
3977 endLine = lineTo - 1 |
|
3978 else: |
|
3979 endLine = lineTo |
|
3980 |
3964 |
3981 self.beginUndoAction() |
3965 self.beginUndoAction() |
3982 # iterate over the lines |
3966 # iterate over the lines |
3983 for line in range(lineFrom, endLine + 1): |
3967 for line in range(lineFrom, endLine + 1): |
3984 # check if line starts with our comment string (i.e. was commented |
3968 # check if line starts with our comment string (i.e. was commented |
3987 continue |
3971 continue |
3988 |
3972 |
3989 if Preferences.getEditor("CommentColumn0"): |
3973 if Preferences.getEditor("CommentColumn0"): |
3990 self.setSelection(line, 0, line, len(commentStr)) |
3974 self.setSelection(line, 0, line, len(commentStr)) |
3991 else: |
3975 else: |
3992 self.setSelection(line, |
3976 lineText = self.text(line) |
3993 self.indentation(line), |
3977 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) |
3994 line, |
3978 self.setSelection(line, pos, line, pos + len(commentStr)) |
3995 self.indentation(line) + len(commentStr)) |
|
3996 self.removeSelectedText() |
3979 self.removeSelectedText() |
3997 |
3980 |
3998 # adjust selection start |
3981 # adjust selection start |
3999 if line == lineFrom: |
3982 if line == lineFrom: |
4000 indexFrom -= len(commentStr) |
3983 indexFrom -= len(commentStr) |
4117 |
4100 |
4118 commentStr = self.lexer_.boxCommentStr() |
4101 commentStr = self.lexer_.boxCommentStr() |
4119 |
4102 |
4120 # get the selection boundaries |
4103 # get the selection boundaries |
4121 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4104 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4122 if indexTo == 0: |
4105 endLine = lineTo if indexTo else lineTo - 1 |
4123 endLine = lineTo - 1 |
|
4124 else: |
|
4125 endLine = lineTo |
|
4126 |
4106 |
4127 self.beginUndoAction() |
4107 self.beginUndoAction() |
4128 # iterate over the lines |
4108 # iterate over the lines |
4129 for line in range(lineFrom, endLine + 1): |
4109 for line in range(lineFrom, endLine + 1): |
4130 self.insertAt(commentStr['middle'], line, 0) |
4110 self.insertAt(commentStr['middle'], line, 0) |
4184 if not self.hasSelectedText(): |
4164 if not self.hasSelectedText(): |
4185 return |
4165 return |
4186 |
4166 |
4187 # get the selection |
4167 # get the selection |
4188 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4168 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4189 |
4169 endLine = lineTo if indexTo else lineTo - 1 |
4190 if indexTo == 0: |
|
4191 endLine = lineTo - 1 |
|
4192 else: |
|
4193 endLine = lineTo |
|
4194 |
4170 |
4195 self.beginUndoAction() |
4171 self.beginUndoAction() |
4196 # iterate over the lines |
4172 # iterate over the lines |
4197 for line in range(lineFrom, endLine + 1): |
4173 for line in range(lineFrom, endLine + 1): |
4198 if indent: |
4174 if indent: |
4546 |
4522 |
4547 # set folding margin settings |
4523 # set folding margin settings |
4548 if Preferences.getEditor("FoldingMargin"): |
4524 if Preferences.getEditor("FoldingMargin"): |
4549 self.setMarginWidth(self.__foldMargin, 16) |
4525 self.setMarginWidth(self.__foldMargin, 16) |
4550 folding = Preferences.getEditor("FoldingStyle") |
4526 folding = Preferences.getEditor("FoldingStyle") |
4551 try: |
4527 with contextlib.suppress(AttributeError): |
4552 folding = QsciScintilla.FoldStyle(folding) |
4528 folding = QsciScintilla.FoldStyle(folding) |
4553 except AttributeError: |
|
4554 pass |
|
4555 self.setFolding(folding, self.__foldMargin) |
4529 self.setFolding(folding, self.__foldMargin) |
4556 self.setFoldMarginColors( |
4530 self.setFoldMarginColors( |
4557 Preferences.getEditorColour("FoldmarginBackground"), |
4531 Preferences.getEditorColour("FoldmarginBackground"), |
4558 Preferences.getEditorColour("FoldmarginBackground")) |
4532 Preferences.getEditorColour("FoldmarginBackground")) |
4559 self.setFoldMarkersColors( |
4533 self.setFoldMarkersColors( |
4599 self.setIndentationGuidesForegroundColor( |
4573 self.setIndentationGuidesForegroundColor( |
4600 Preferences.getEditorColour("IndentationGuidesForeground")) |
4574 Preferences.getEditorColour("IndentationGuidesForeground")) |
4601 if Preferences.getEditor("ShowWhitespace"): |
4575 if Preferences.getEditor("ShowWhitespace"): |
4602 self.setWhitespaceVisibility( |
4576 self.setWhitespaceVisibility( |
4603 QsciScintilla.WhitespaceVisibility.WsVisible) |
4577 QsciScintilla.WhitespaceVisibility.WsVisible) |
4604 try: |
4578 with contextlib.suppress(AttributeError): |
4605 self.setWhitespaceForegroundColor( |
4579 self.setWhitespaceForegroundColor( |
4606 Preferences.getEditorColour("WhitespaceForeground")) |
4580 Preferences.getEditorColour("WhitespaceForeground")) |
4607 self.setWhitespaceBackgroundColor( |
4581 self.setWhitespaceBackgroundColor( |
4608 Preferences.getEditorColour("WhitespaceBackground")) |
4582 Preferences.getEditorColour("WhitespaceBackground")) |
4609 self.setWhitespaceSize( |
4583 self.setWhitespaceSize( |
4610 Preferences.getEditor("WhitespaceSize")) |
4584 Preferences.getEditor("WhitespaceSize")) |
4611 except AttributeError: |
|
4612 # QScintilla before 2.5 doesn't support this |
|
4613 pass |
|
4614 else: |
4585 else: |
4615 self.setWhitespaceVisibility( |
4586 self.setWhitespaceVisibility( |
4616 QsciScintilla.WhitespaceVisibility.WsInvisible) |
4587 QsciScintilla.WhitespaceVisibility.WsInvisible) |
4617 self.setEolVisibility(Preferences.getEditor("ShowEOL")) |
4588 self.setEolVisibility(Preferences.getEditor("ShowEOL")) |
4618 self.setAutoIndent(Preferences.getEditor("AutoIndentation")) |
4589 self.setAutoIndent(Preferences.getEditor("AutoIndentation")) |
4694 self.highlightIndicator, QsciScintilla.INDIC_FULLBOX, |
4665 self.highlightIndicator, QsciScintilla.INDIC_FULLBOX, |
4695 Preferences.getEditorColour("HighlightMarker")) |
4666 Preferences.getEditorColour("HighlightMarker")) |
4696 |
4667 |
4697 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
4668 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
4698 |
4669 |
4699 try: |
4670 with contextlib.suppress(AttributeError): |
4700 if Preferences.getEditor("AnnotationsEnabled"): |
4671 if Preferences.getEditor("AnnotationsEnabled"): |
4701 self.setAnnotationDisplay( |
4672 self.setAnnotationDisplay( |
4702 QsciScintilla.AnnotationDisplay.AnnotationBoxed) |
4673 QsciScintilla.AnnotationDisplay.AnnotationBoxed) |
4703 else: |
4674 else: |
4704 self.setAnnotationDisplay( |
4675 self.setAnnotationDisplay( |
4705 QsciScintilla.AnnotationDisplay.AnnotationHidden) |
4676 QsciScintilla.AnnotationDisplay.AnnotationHidden) |
4706 except AttributeError: |
|
4707 pass |
|
4708 self.__setAnnotationStyles() |
4677 self.__setAnnotationStyles() |
4709 |
4678 |
4710 if Preferences.getEditor("OverrideEditAreaColours"): |
4679 if Preferences.getEditor("OverrideEditAreaColours"): |
4711 self.setColor(Preferences.getEditorColour("EditAreaForeground")) |
4680 self.setColor(Preferences.getEditorColour("EditAreaForeground")) |
4712 self.setPaper(Preferences.getEditorColour("EditAreaBackground")) |
4681 self.setPaper(Preferences.getEditorColour("EditAreaBackground")) |
4787 Preferences.getEditorColour("CallTipsForeground")) |
4756 Preferences.getEditorColour("CallTipsForeground")) |
4788 self.setCallTipsHighlightColor( |
4757 self.setCallTipsHighlightColor( |
4789 Preferences.getEditorColour("CallTipsHighlight")) |
4758 Preferences.getEditorColour("CallTipsHighlight")) |
4790 self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible")) |
4759 self.setCallTipsVisible(Preferences.getEditor("CallTipsVisible")) |
4791 calltipsStyle = Preferences.getEditor("CallTipsStyle") |
4760 calltipsStyle = Preferences.getEditor("CallTipsStyle") |
4792 try: |
4761 with contextlib.suppress(AttributeError): |
4793 self.setCallTipsPosition( |
4762 self.setCallTipsPosition( |
4794 Preferences.getEditor("CallTipsPosition")) |
4763 Preferences.getEditor("CallTipsPosition")) |
4795 except AttributeError: |
|
4796 pass |
|
4797 |
4764 |
4798 if Preferences.getEditor("CallTipsEnabled"): |
4765 if Preferences.getEditor("CallTipsEnabled"): |
4799 if calltipsStyle == QsciScintilla.CallTipsStyle.CallTipsNoContext: |
4766 if calltipsStyle == QsciScintilla.CallTipsStyle.CallTipsNoContext: |
4800 self.setCallTipsStyle( |
4767 self.setCallTipsStyle( |
4801 QsciScintilla.CallTipsStyle.CallTipsNoContext) |
4768 QsciScintilla.CallTipsStyle.CallTipsNoContext) |
4942 """ |
4909 """ |
4943 if self.lexer_ is None: |
4910 if self.lexer_ is None: |
4944 return False |
4911 return False |
4945 |
4912 |
4946 wseps = self.lexer_.autoCompletionWordSeparators() |
4913 wseps = self.lexer_.autoCompletionWordSeparators() |
4947 for wsep in wseps: |
4914 return any(wsep.endswith(ch) for wsep in wseps) |
4948 if wsep.endswith(ch): |
|
4949 return True |
|
4950 |
|
4951 return False |
|
4952 |
4915 |
4953 def __autocompletionCancelled(self): |
4916 def __autocompletionCancelled(self): |
4954 """ |
4917 """ |
4955 Private slot to handle the cancellation of an auto-completion list. |
4918 Private slot to handle the cancellation of an auto-completion list. |
4956 """ |
4919 """ |
5046 self.__acTimer.stop() |
5009 self.__acTimer.stop() |
5047 self.__acContext = context |
5010 self.__acContext = context |
5048 self.__acTimer.start() |
5011 self.__acTimer.start() |
5049 else: |
5012 else: |
5050 self.__autoComplete(auto, context) |
5013 self.__autoComplete(auto, context) |
5051 elif not auto: |
|
5052 self.autoCompleteQScintilla() |
|
5053 elif ( |
5014 elif ( |
5054 self.autoCompletionSource() != |
5015 not auto or |
5055 QsciScintilla.AutoCompletionSource.AcsNone |
5016 (self.autoCompletionSource() != |
|
5017 QsciScintilla.AutoCompletionSource.AcsNone) |
5056 ): |
5018 ): |
5057 self.autoCompleteQScintilla() |
5019 self.autoCompleteQScintilla() |
5058 |
5020 |
5059 def __getAcText(self): |
5021 def __getAcText(self): |
5060 """ |
5022 """ |
5064 @rtype str |
5026 @rtype str |
5065 """ |
5027 """ |
5066 line, col = self.getCursorPosition() |
5028 line, col = self.getCursorPosition() |
5067 text = self.text(line) |
5029 text = self.text(line) |
5068 try: |
5030 try: |
5069 if self.__isStartChar(text[col - 1]): |
5031 acText = ( |
5070 acText = self.getWordLeft(line, col - 1) + text[col - 1] |
5032 self.getWordLeft(line, col - 1) + text[col - 1] |
5071 else: |
5033 if self.__isStartChar(text[col - 1]) else |
5072 acText = self.getWordLeft(line, col) |
5034 self.getWordLeft(line, col) |
|
5035 ) |
5073 except IndexError: |
5036 except IndexError: |
5074 acText = "" |
5037 acText = "" |
5075 |
5038 |
5076 return acText |
5039 return acText |
5077 |
5040 |
5089 |
5052 |
5090 # Suppress empty completions |
5053 # Suppress empty completions |
5091 if auto and self.__acText == '': |
5054 if auto and self.__acText == '': |
5092 return |
5055 return |
5093 |
5056 |
5094 if self.__acCacheEnabled: |
5057 completions = ( |
5095 completions = self.__acCache.get(self.__acText) |
5058 self.__acCache.get(self.__acText) |
5096 else: |
5059 if self.__acCacheEnabled else |
5097 completions = None |
5060 None |
|
5061 ) |
5098 if completions is not None: |
5062 if completions is not None: |
5099 # show list with cached entries |
5063 # show list with cached entries |
5100 if self.isListActive(): |
5064 if self.isListActive(): |
5101 self.cancelList() |
5065 self.cancelList() |
5102 |
5066 |
5169 Private method to show the completions list. |
5133 Private method to show the completions list. |
5170 |
5134 |
5171 @param completions completions to be shown |
5135 @param completions completions to be shown |
5172 @type list of str or set of str |
5136 @type list of str or set of str |
5173 """ |
5137 """ |
5174 if Preferences.getEditor("AutoCompletionReversedList"): |
5138 acCompletions = ( |
5175 acCompletions = sorted( |
5139 sorted( |
5176 list(completions), |
5140 list(completions), |
5177 key=self.__replaceLeadingUnderscores) |
5141 key=self.__replaceLeadingUnderscores) |
5178 else: |
5142 if Preferences.getEditor("AutoCompletionReversedList") else |
5179 acCompletions = sorted(list(completions)) |
5143 sorted(list(completions)) |
|
5144 ) |
5180 self.showUserList(EditorAutoCompletionListID, acCompletions) |
5145 self.showUserList(EditorAutoCompletionListID, acCompletions) |
5181 |
5146 |
5182 def __replaceLeadingUnderscores(self, txt): |
5147 def __replaceLeadingUnderscores(self, txt): |
5183 """ |
5148 """ |
5184 Private method to replace the first two underlines for invers sorting. |
5149 Private method to replace the first two underlines for invers sorting. |
5354 self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL) |
5319 self.SendScintilla(QsciScintilla.SCI_CALLTIPCANCEL) |
5355 |
5320 |
5356 if not found: |
5321 if not found: |
5357 return |
5322 return |
5358 |
5323 |
|
5324 callTips = [] |
5359 if self.__ctHookFunctions: |
5325 if self.__ctHookFunctions: |
5360 callTips = [] |
|
5361 for key in self.__ctHookFunctions: |
5326 for key in self.__ctHookFunctions: |
5362 callTips.extend(self.__ctHookFunctions[key](self, pos, commas)) |
5327 callTips.extend(self.__ctHookFunctions[key](self, pos, commas)) |
5363 callTips = list(set(callTips)) |
5328 callTips = list(set(callTips)) |
5364 callTips.sort() |
5329 callTips.sort() |
|
5330 else: |
|
5331 # try QScintilla calltips |
|
5332 super().callTip() |
|
5333 return |
5365 if len(callTips) == 0: |
5334 if len(callTips) == 0: |
5366 if Preferences.getEditor("CallTipsScintillaOnFail"): |
5335 if Preferences.getEditor("CallTipsScintillaOnFail"): |
5367 # try QScintilla calltips |
5336 # try QScintilla calltips |
5368 super(Editor, self).callTip() |
5337 super().callTip() |
5369 return |
5338 return |
5370 |
5339 |
5371 ctshift = 0 |
5340 ctshift = 0 |
5372 for ct in callTips: |
5341 for ct in callTips: |
5373 shift = ct.index("(") |
5342 shift = ct.index("(") |
5374 if ctshift < shift: |
5343 if ctshift < shift: |
5375 ctshift = shift |
5344 ctshift = shift |
5376 |
5345 |
5377 cv = self.callTipsVisible() |
5346 cv = self.callTipsVisible() |
5378 if cv > 0: |
5347 ct = ( |
5379 # this is just a safe guard |
5348 # this is just a safe guard |
5380 ct = self._encodeString("\n".join(callTips[:cv])) |
5349 self._encodeString("\n".join(callTips[:cv])) |
5381 else: |
5350 if cv > 0 else |
5382 # until here and unindent below |
5351 # until here and unindent below |
5383 ct = self._encodeString("\n".join(callTips)) |
5352 self._encodeString("\n".join(callTips)) |
|
5353 ) |
5384 |
5354 |
5385 self.SendScintilla(QsciScintilla.SCI_CALLTIPSHOW, |
5355 self.SendScintilla(QsciScintilla.SCI_CALLTIPSHOW, |
5386 self.__adjustedCallTipPosition(ctshift, pos), ct) |
5356 self.__adjustedCallTipPosition(ctshift, pos), ct) |
5387 if b'\n' in ct: |
5357 if b'\n' in ct: |
5388 return |
5358 return |
5432 QsciScintilla.SCI_POSITIONFROMLINE, |
5402 QsciScintilla.SCI_POSITIONFROMLINE, |
5433 self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, ct)) |
5403 self.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, ct)) |
5434 if ct - ctshift < ctmin: |
5404 if ct - ctshift < ctmin: |
5435 ct = ctmin |
5405 ct = ctmin |
5436 else: |
5406 else: |
5437 ct = ct - ctshift |
5407 ct -= ctshift |
5438 return ct |
5408 return ct |
5439 |
5409 |
5440 ################################################################# |
5410 ################################################################# |
5441 ## Methods needed by the code documentation viewer |
5411 ## Methods needed by the code documentation viewer |
5442 ################################################################# |
5412 ################################################################# |
5536 else: |
5506 else: |
5537 self.menuActs["Comment"].setEnabled(False) |
5507 self.menuActs["Comment"].setEnabled(False) |
5538 self.menuActs["Uncomment"].setEnabled(False) |
5508 self.menuActs["Uncomment"].setEnabled(False) |
5539 self.menuActs["StreamComment"].setEnabled(False) |
5509 self.menuActs["StreamComment"].setEnabled(False) |
5540 self.menuActs["BoxComment"].setEnabled(False) |
5510 self.menuActs["BoxComment"].setEnabled(False) |
|
5511 |
|
5512 cline = self.getCursorPosition()[0] |
|
5513 line = self.text(cline) |
|
5514 self.menuActs["Docstring"].setEnabled( |
|
5515 self.getDocstringGenerator().isFunctionStart(line)) |
5541 |
5516 |
5542 self.menuActs["TypingAidsEnabled"].setEnabled( |
5517 self.menuActs["TypingAidsEnabled"].setEnabled( |
5543 self.completer is not None) |
5518 self.completer is not None) |
5544 self.menuActs["TypingAidsEnabled"].setChecked( |
5519 self.menuActs["TypingAidsEnabled"].setChecked( |
5545 self.completer is not None and self.completer.isEnabled()) |
5520 self.completer is not None and self.completer.isEnabled()) |
5573 else: |
5548 else: |
5574 self.menuActs["NewSplit"].setIcon( |
5549 self.menuActs["NewSplit"].setIcon( |
5575 UI.PixmapCache.getIcon("splitVertical")) |
5550 UI.PixmapCache.getIcon("splitVertical")) |
5576 |
5551 |
5577 self.menuActs["Tools"].setEnabled(not self.toolsMenu.isEmpty()) |
5552 self.menuActs["Tools"].setEnabled(not self.toolsMenu.isEmpty()) |
5578 |
|
5579 cline = self.getCursorPosition()[0] |
|
5580 line = self.text(cline) |
|
5581 self.menuActs["Docstring"].setEnabled( |
|
5582 self.getDocstringGenerator().isFunctionStart(line)) |
|
5583 |
5553 |
5584 self.showMenu.emit("Main", self.menu, self) |
5554 self.showMenu.emit("Main", self.menu, self) |
5585 |
5555 |
5586 def __showContextMenuAutocompletion(self): |
5556 def __showContextMenuAutocompletion(self): |
5587 """ |
5557 """ |
6381 Public slot to handle the 'Goto syntax error' context menu action. |
6351 Public slot to handle the 'Goto syntax error' context menu action. |
6382 """ |
6352 """ |
6383 seline = self.markerFindNext(0, 1 << self.syntaxerror) |
6353 seline = self.markerFindNext(0, 1 << self.syntaxerror) |
6384 if seline >= 0: |
6354 if seline >= 0: |
6385 index = 0 |
6355 index = 0 |
6386 for handle in self.syntaxerrors.keys(): |
6356 for handle in self.syntaxerrors: |
6387 if self.markerLine(handle) == seline: |
6357 if self.markerLine(handle) == seline: |
6388 index = self.syntaxerrors[handle][0][1] |
6358 index = self.syntaxerrors[handle][0][1] |
6389 self.setCursorPosition(seline, index) |
6359 self.setCursorPosition(seline, index) |
6390 self.ensureLineVisible(seline) |
6360 self.ensureLineVisible(seline) |
6391 |
6361 |
6687 warningAnnotations = [] |
6657 warningAnnotations = [] |
6688 errorAnnotations = [] |
6658 errorAnnotations = [] |
6689 styleAnnotations = [] |
6659 styleAnnotations = [] |
6690 |
6660 |
6691 # step 1: do warnings |
6661 # step 1: do warnings |
6692 for handle in self.warnings.keys(): |
6662 for handle in self.warnings: |
6693 if self.markerLine(handle) == line: |
6663 if self.markerLine(handle) == line: |
6694 for msg, warningType in self.warnings[handle]: |
6664 for msg, warningType in self.warnings[handle]: |
6695 if warningType == self.WarningStyle: |
6665 if warningType == self.WarningStyle: |
6696 styleAnnotations.append( |
6666 styleAnnotations.append( |
6697 self.tr("Style: {0}").format(msg)) |
6667 self.tr("Style: {0}").format(msg)) |
6698 else: |
6668 else: |
6699 warningAnnotations.append( |
6669 warningAnnotations.append( |
6700 self.tr("Warning: {0}").format(msg)) |
6670 self.tr("Warning: {0}").format(msg)) |
6701 |
6671 |
6702 # step 2: do syntax errors |
6672 # step 2: do syntax errors |
6703 for handle in self.syntaxerrors.keys(): |
6673 for handle in self.syntaxerrors: |
6704 if self.markerLine(handle) == line: |
6674 if self.markerLine(handle) == line: |
6705 for msg, _ in self.syntaxerrors[handle]: |
6675 for msg, _ in self.syntaxerrors[handle]: |
6706 errorAnnotations.append( |
6676 errorAnnotations.append( |
6707 self.tr("Error: {0}").format(msg)) |
6677 self.tr("Error: {0}").format(msg)) |
6708 |
6678 |
6966 |
6936 |
6967 def undo(self): |
6937 def undo(self): |
6968 """ |
6938 """ |
6969 Public method to undo the last recorded change. |
6939 Public method to undo the last recorded change. |
6970 """ |
6940 """ |
6971 super(Editor, self).undo() |
6941 super().undo() |
6972 self.undoAvailable.emit(self.isUndoAvailable()) |
6942 self.undoAvailable.emit(self.isUndoAvailable()) |
6973 self.redoAvailable.emit(self.isRedoAvailable()) |
6943 self.redoAvailable.emit(self.isRedoAvailable()) |
6974 |
6944 |
6975 def redo(self): |
6945 def redo(self): |
6976 """ |
6946 """ |
6977 Public method to redo the last recorded change. |
6947 Public method to redo the last recorded change. |
6978 """ |
6948 """ |
6979 super(Editor, self).redo() |
6949 super().redo() |
6980 self.undoAvailable.emit(self.isUndoAvailable()) |
6950 self.undoAvailable.emit(self.isUndoAvailable()) |
6981 self.redoAvailable.emit(self.isRedoAvailable()) |
6951 self.redoAvailable.emit(self.isRedoAvailable()) |
6982 |
6952 |
6983 def close(self, alsoDelete=False): |
6953 def close(self, alsoDelete=False): |
6984 """ |
6954 """ |
7020 self.__processSyntaxCheckError) |
6990 self.__processSyntaxCheckError) |
7021 |
6991 |
7022 if self.spell: |
6992 if self.spell: |
7023 self.spell.stopIncrementalCheck() |
6993 self.spell.stopIncrementalCheck() |
7024 |
6994 |
7025 try: |
6995 with contextlib.suppress(TypeError): |
7026 self.project.projectPropertiesChanged.disconnect( |
6996 self.project.projectPropertiesChanged.disconnect( |
7027 self.__projectPropertiesChanged) |
6997 self.__projectPropertiesChanged) |
7028 except TypeError: |
|
7029 pass |
|
7030 |
6998 |
7031 if self.fileName: |
6999 if self.fileName: |
7032 self.taskViewer.clearFileTasks(self.fileName, True) |
7000 self.taskViewer.clearFileTasks(self.fileName, True) |
7033 |
7001 |
7034 super(Editor, self).close() |
7002 super().close() |
7035 |
7003 |
7036 def keyPressEvent(self, ev): |
7004 def keyPressEvent(self, ev): |
7037 """ |
7005 """ |
7038 Protected method to handle the user input a key at a time. |
7006 Protected method to handle the user input a key at a time. |
7039 |
7007 |
7048 @param encString string to use to enclose the selection |
7016 @param encString string to use to enclose the selection |
7049 (one or two characters) |
7017 (one or two characters) |
7050 @type str |
7018 @type str |
7051 """ |
7019 """ |
7052 startChar = encString[0] |
7020 startChar = encString[0] |
7053 if len(encString) == 2: |
7021 endChar = encString[1] if len(encString) == 2 else startChar |
7054 endChar = encString[1] |
|
7055 else: |
|
7056 endChar = startChar |
|
7057 |
7022 |
7058 sline, sindex, eline, eindex = self.getSelection() |
7023 sline, sindex, eline, eindex = self.getSelection() |
7059 replaceText = startChar + self.selectedText() + endChar |
7024 replaceText = startChar + self.selectedText() + endChar |
7060 self.beginUndoAction() |
7025 self.beginUndoAction() |
7061 self.replaceSelectedText(replaceText) |
7026 self.replaceSelectedText(replaceText) |
7064 |
7029 |
7065 txt = ev.text() |
7030 txt = ev.text() |
7066 |
7031 |
7067 # See it is text to insert. |
7032 # See it is text to insert. |
7068 if len(txt) and txt >= " ": |
7033 if len(txt) and txt >= " ": |
7069 if self.hasSelectedText(): |
7034 if ( |
7070 if txt in Editor.EncloseChars: |
7035 self.hasSelectedText() and |
7071 encloseSelectedText(Editor.EncloseChars[txt]) |
7036 txt in Editor.EncloseChars |
7072 ev.accept() |
7037 ): |
7073 return |
7038 encloseSelectedText(Editor.EncloseChars[txt]) |
|
7039 ev.accept() |
|
7040 return |
7074 |
7041 |
7075 super(Editor, self).keyPressEvent(ev) |
7042 super().keyPressEvent(ev) |
7076 else: |
7043 else: |
7077 ev.ignore() |
7044 ev.ignore() |
7078 |
7045 |
7079 def focusInEvent(self, event): |
7046 def focusInEvent(self, event): |
7080 """ |
7047 """ |
7092 self.vm.editActGrp.setEnabled(True) |
7059 self.vm.editActGrp.setEnabled(True) |
7093 self.vm.editorActGrp.setEnabled(True) |
7060 self.vm.editorActGrp.setEnabled(True) |
7094 self.vm.copyActGrp.setEnabled(True) |
7061 self.vm.copyActGrp.setEnabled(True) |
7095 self.vm.viewActGrp.setEnabled(True) |
7062 self.vm.viewActGrp.setEnabled(True) |
7096 self.vm.searchActGrp.setEnabled(True) |
7063 self.vm.searchActGrp.setEnabled(True) |
7097 try: |
7064 with contextlib.suppress(AttributeError): |
7098 self.setCaretWidth(self.caretWidth) |
7065 self.setCaretWidth(self.caretWidth) |
7099 except AttributeError: |
|
7100 pass |
|
7101 self.__updateReadOnly(False) |
7066 self.__updateReadOnly(False) |
7102 if ( |
7067 if ( |
7103 self.vm.editorsCheckFocusInEnabled() and |
7068 self.vm.editorsCheckFocusInEnabled() and |
7104 not self.inReopenPrompt and self.fileName and |
7069 not self.inReopenPrompt and self.fileName and |
7105 QFileInfo(self.fileName).lastModified().toString() != |
7070 QFileInfo(self.fileName).lastModified().toString() != |
7131 self.lastModified = QFileInfo(self.fileName).lastModified() |
7096 self.lastModified = QFileInfo(self.fileName).lastModified() |
7132 self.inReopenPrompt = False |
7097 self.inReopenPrompt = False |
7133 |
7098 |
7134 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
7099 self.setCursorFlashTime(QApplication.cursorFlashTime()) |
7135 |
7100 |
7136 super(Editor, self).focusInEvent(event) |
7101 super().focusInEvent(event) |
7137 |
7102 |
7138 def focusOutEvent(self, event): |
7103 def focusOutEvent(self, event): |
7139 """ |
7104 """ |
7140 Protected method called when the editor loses focus. |
7105 Protected method called when the editor loses focus. |
7141 |
7106 |
7143 @type QFocusEvent |
7108 @type QFocusEvent |
7144 """ |
7109 """ |
7145 self.vm.editorActGrp.setEnabled(False) |
7110 self.vm.editorActGrp.setEnabled(False) |
7146 self.setCaretWidth(0) |
7111 self.setCaretWidth(0) |
7147 |
7112 |
7148 super(Editor, self).focusOutEvent(event) |
7113 super().focusOutEvent(event) |
7149 |
7114 |
7150 def changeEvent(self, evt): |
7115 def changeEvent(self, evt): |
7151 """ |
7116 """ |
7152 Protected method called to process an event. |
7117 Protected method called to process an event. |
7153 |
7118 |
7173 cap = self.fileName |
7138 cap = self.fileName |
7174 if self.isReadOnly(): |
7139 if self.isReadOnly(): |
7175 cap = self.tr("{0} (ro)").format(cap) |
7140 cap = self.tr("{0} (ro)").format(cap) |
7176 self.setWindowTitle(cap) |
7141 self.setWindowTitle(cap) |
7177 |
7142 |
7178 super(Editor, self).changeEvent(evt) |
7143 super().changeEvent(evt) |
7179 |
7144 |
7180 def mousePressEvent(self, event): |
7145 def mousePressEvent(self, event): |
7181 """ |
7146 """ |
7182 Protected method to handle the mouse press event. |
7147 Protected method to handle the mouse press event. |
7183 |
7148 |
7184 @param event the mouse press event |
7149 @param event the mouse press event |
7185 @type QMouseEvent |
7150 @type QMouseEvent |
7186 """ |
7151 """ |
7187 self.vm.eventFilter(self, event) |
7152 self.vm.eventFilter(self, event) |
7188 super(Editor, self).mousePressEvent(event) |
7153 super().mousePressEvent(event) |
7189 |
7154 |
7190 def mouseDoubleClickEvent(self, evt): |
7155 def mouseDoubleClickEvent(self, evt): |
7191 """ |
7156 """ |
7192 Protected method to handle mouse double click events. |
7157 Protected method to handle mouse double click events. |
7193 |
7158 |
7194 @param evt reference to the mouse event |
7159 @param evt reference to the mouse event |
7195 @type QMouseEvent |
7160 @type QMouseEvent |
7196 """ |
7161 """ |
7197 super(Editor, self).mouseDoubleClickEvent(evt) |
7162 super().mouseDoubleClickEvent(evt) |
7198 |
7163 |
7199 self.mouseDoubleClick.emit(evt.pos(), evt.buttons()) |
7164 self.mouseDoubleClick.emit(evt.pos(), evt.buttons()) |
7200 |
7165 |
7201 def wheelEvent(self, evt): |
7166 def wheelEvent(self, evt): |
7202 """ |
7167 """ |
7235 """ |
7200 """ |
7236 if evt.type() == QEvent.Type.Gesture: |
7201 if evt.type() == QEvent.Type.Gesture: |
7237 self.gestureEvent(evt) |
7202 self.gestureEvent(evt) |
7238 return True |
7203 return True |
7239 |
7204 |
7240 return super(Editor, self).event(evt) |
7205 return super().event(evt) |
7241 |
7206 |
7242 def gestureEvent(self, evt): |
7207 def gestureEvent(self, evt): |
7243 """ |
7208 """ |
7244 Protected method handling gesture events. |
7209 Protected method handling gesture events. |
7245 |
7210 |
7267 Protected method handling resize events. |
7232 Protected method handling resize events. |
7268 |
7233 |
7269 @param evt reference to the resize event |
7234 @param evt reference to the resize event |
7270 @type QResizeEvent |
7235 @type QResizeEvent |
7271 """ |
7236 """ |
7272 super(Editor, self).resizeEvent(evt) |
7237 super().resizeEvent(evt) |
7273 self.__markerMap.calculateGeometry() |
7238 self.__markerMap.calculateGeometry() |
7274 |
7239 |
7275 def viewportEvent(self, evt): |
7240 def viewportEvent(self, evt): |
7276 """ |
7241 """ |
7277 Protected method handling event of the viewport. |
7242 Protected method handling event of the viewport. |
7279 @param evt reference to the event |
7244 @param evt reference to the event |
7280 @type QEvent |
7245 @type QEvent |
7281 @return flag indiating that the event was handled |
7246 @return flag indiating that the event was handled |
7282 @rtype bool |
7247 @rtype bool |
7283 """ |
7248 """ |
7284 try: |
7249 with contextlib.suppress(AttributeError): |
7285 self.__markerMap.calculateGeometry() |
7250 self.__markerMap.calculateGeometry() |
7286 except AttributeError: |
7251 return super().viewportEvent(evt) |
7287 # ignore this - there seems to be a runtime issue when the editor |
|
7288 # is created |
|
7289 pass |
|
7290 return super(Editor, self).viewportEvent(evt) |
|
7291 |
7252 |
7292 def __updateReadOnly(self, bForce=True): |
7253 def __updateReadOnly(self, bForce=True): |
7293 """ |
7254 """ |
7294 Private method to update the readOnly information for this editor. |
7255 Private method to update the readOnly information for this editor. |
7295 |
7256 |
7395 def clearStyles(self): |
7356 def clearStyles(self): |
7396 """ |
7357 """ |
7397 Public method to set the styles according the selected Qt style |
7358 Public method to set the styles according the selected Qt style |
7398 or the selected editor colours. |
7359 or the selected editor colours. |
7399 """ |
7360 """ |
7400 super(Editor, self).clearStyles() |
7361 super().clearStyles() |
7401 if Preferences.getEditor("OverrideEditAreaColours"): |
7362 if Preferences.getEditor("OverrideEditAreaColours"): |
7402 self.setColor(Preferences.getEditorColour("EditAreaForeground")) |
7363 self.setColor(Preferences.getEditorColour("EditAreaForeground")) |
7403 self.setPaper(Preferences.getEditorColour("EditAreaBackground")) |
7364 self.setPaper(Preferences.getEditorColour("EditAreaBackground")) |
7404 |
7365 |
7405 ################################################################# |
7366 ################################################################# |
7414 """ |
7375 """ |
7415 self.inDragDrop = event.mimeData().hasUrls() |
7376 self.inDragDrop = event.mimeData().hasUrls() |
7416 if self.inDragDrop: |
7377 if self.inDragDrop: |
7417 event.acceptProposedAction() |
7378 event.acceptProposedAction() |
7418 else: |
7379 else: |
7419 super(Editor, self).dragEnterEvent(event) |
7380 super().dragEnterEvent(event) |
7420 |
7381 |
7421 def dragMoveEvent(self, event): |
7382 def dragMoveEvent(self, event): |
7422 """ |
7383 """ |
7423 Protected method to handle the drag move event. |
7384 Protected method to handle the drag move event. |
7424 |
7385 |
7425 @param event the drag move event (QDragMoveEvent) |
7386 @param event the drag move event (QDragMoveEvent) |
7426 """ |
7387 """ |
7427 if self.inDragDrop: |
7388 if self.inDragDrop: |
7428 event.accept() |
7389 event.accept() |
7429 else: |
7390 else: |
7430 super(Editor, self).dragMoveEvent(event) |
7391 super().dragMoveEvent(event) |
7431 |
7392 |
7432 def dragLeaveEvent(self, event): |
7393 def dragLeaveEvent(self, event): |
7433 """ |
7394 """ |
7434 Protected method to handle the drag leave event. |
7395 Protected method to handle the drag leave event. |
7435 |
7396 |
7459 self.tr("Drop Error"), |
7420 self.tr("Drop Error"), |
7460 self.tr("""<p><b>{0}</b> is not a file.</p>""") |
7421 self.tr("""<p><b>{0}</b> is not a file.</p>""") |
7461 .format(fname)) |
7422 .format(fname)) |
7462 event.acceptProposedAction() |
7423 event.acceptProposedAction() |
7463 else: |
7424 else: |
7464 super(Editor, self).dropEvent(event) |
7425 super().dropEvent(event) |
7465 |
7426 |
7466 self.inDragDrop = False |
7427 self.inDragDrop = False |
7467 |
7428 |
7468 ################################################################# |
7429 ################################################################# |
7469 ## Support for Qt resources files |
7430 ## Support for Qt resources files |
7732 matchingPairs = ['()', '[]', '{}', '<>', "''", '""'] |
7693 matchingPairs = ['()', '[]', '{}', '<>', "''", '""'] |
7733 # __IGNORE_WARNING_M613__ |
7694 # __IGNORE_WARNING_M613__ |
7734 if text in matchingPairs: |
7695 if text in matchingPairs: |
7735 self.delete() |
7696 self.delete() |
7736 |
7697 |
7737 super(Editor, self).editorCommand(cmd) |
7698 super().editorCommand(cmd) |
7738 |
7699 |
7739 def __applyTemplate(self, templateName, language): |
7700 def __applyTemplate(self, templateName, language): |
7740 """ |
7701 """ |
7741 Private method to apply a template by name. |
7702 Private method to apply a template by name. |
7742 |
7703 |
7801 |
7762 |
7802 def projectClosed(self): |
7763 def projectClosed(self): |
7803 """ |
7764 """ |
7804 Public slot to handle the closing of a project. |
7765 Public slot to handle the closing of a project. |
7805 """ |
7766 """ |
7806 try: |
7767 with contextlib.suppress(TypeError): |
7807 self.project.projectPropertiesChanged.disconnect( |
7768 self.project.projectPropertiesChanged.disconnect( |
7808 self.__projectPropertiesChanged) |
7769 self.__projectPropertiesChanged) |
7809 except TypeError: |
|
7810 pass |
|
7811 |
7770 |
7812 ####################################################################### |
7771 ####################################################################### |
7813 ## Spell checking related methods |
7772 ## Spell checking related methods |
7814 ####################################################################### |
7773 ####################################################################### |
7815 |
7774 |
7873 def setAutoSpellChecking(self): |
7832 def setAutoSpellChecking(self): |
7874 """ |
7833 """ |
7875 Public method to set the automatic spell checking. |
7834 Public method to set the automatic spell checking. |
7876 """ |
7835 """ |
7877 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
7836 if Preferences.getEditor("AutoSpellCheckingEnabled"): |
7878 try: |
7837 with contextlib.suppress(TypeError): |
7879 self.SCN_CHARADDED.connect( |
7838 self.SCN_CHARADDED.connect( |
7880 self.__spellCharAdded, Qt.ConnectionType.UniqueConnection) |
7839 self.__spellCharAdded, Qt.ConnectionType.UniqueConnection) |
7881 except TypeError: |
|
7882 pass |
|
7883 self.spell.checkDocumentIncrementally() |
7840 self.spell.checkDocumentIncrementally() |
7884 else: |
7841 else: |
7885 try: |
7842 with contextlib.suppress(TypeError): |
7886 self.SCN_CHARADDED.disconnect(self.__spellCharAdded) |
7843 self.SCN_CHARADDED.disconnect(self.__spellCharAdded) |
7887 except TypeError: |
|
7888 pass |
|
7889 self.clearAllIndicators(self.spellingIndicator) |
7844 self.clearAllIndicators(self.spellingIndicator) |
7890 |
7845 |
7891 def isSpellCheckRegion(self, pos): |
7846 def isSpellCheckRegion(self, pos): |
7892 """ |
7847 """ |
7893 Public method to check, if the given position is within a region, that |
7848 Public method to check, if the given position is within a region, that |
7903 @return flag indicating pos is in a spell check region |
7858 @return flag indicating pos is in a spell check region |
7904 @rtype bool |
7859 @rtype bool |
7905 """ |
7860 """ |
7906 if self.__spellCheckStringsOnly: |
7861 if self.__spellCheckStringsOnly: |
7907 if ( |
7862 if ( |
7908 self.__fileNameExtension in |
7863 (self.__fileNameExtension in |
7909 Preferences.getEditor("FullSpellCheckExtensions") |
7864 Preferences.getEditor("FullSpellCheckExtensions")) or |
7910 ): |
7865 (not self.__fileNameExtension and |
7911 return True |
7866 Preferences.getEditor("FullSpellCheckUnknown")) |
7912 elif ( |
|
7913 not self.__fileNameExtension and |
|
7914 Preferences.getEditor("FullSpellCheckUnknown") |
|
7915 ): |
7867 ): |
7916 return True |
7868 return True |
7917 else: |
7869 else: |
7918 style = self.styleAt(pos) |
7870 style = self.styleAt(pos) |
7919 if self.lexer_ is not None: |
7871 if self.lexer_ is not None: |
8347 wordStart, wordEnd = self.getCurrentWordBoundaries() |
8299 wordStart, wordEnd = self.getCurrentWordBoundaries() |
8348 wordStartPos = self.positionFromLineIndex(line, wordStart) |
8300 wordStartPos = self.positionFromLineIndex(line, wordStart) |
8349 wordEndPos = self.positionFromLineIndex(line, wordEnd) |
8301 wordEndPos = self.positionFromLineIndex(line, wordEnd) |
8350 |
8302 |
8351 regExp = re.compile(r"\b{0}\b".format(word)) |
8303 regExp = re.compile(r"\b{0}\b".format(word)) |
8352 if forward: |
8304 startPos = wordEndPos if forward else wordStartPos |
8353 startPos = wordEndPos |
|
8354 else: |
|
8355 startPos = wordStartPos |
|
8356 |
8305 |
8357 matches = [m for m in regExp.finditer(self.text())] |
8306 matches = [m for m in regExp.finditer(self.text())] |
8358 if matches: |
8307 if matches: |
8359 if forward: |
8308 if forward: |
8360 matchesAfter = [m for m in matches if m.start() >= startPos] |
8309 matchesAfter = [m for m in matches if m.start() >= startPos] |
8469 modifiers = evt.modifiers() |
8418 modifiers = evt.modifiers() |
8470 button = evt.button() |
8419 button = evt.button() |
8471 key = (int(modifiers), int(button)) |
8420 key = (int(modifiers), int(button)) |
8472 |
8421 |
8473 self.vm.eventFilter(self, evt) |
8422 self.vm.eventFilter(self, evt) |
8474 super(Editor, self).mouseReleaseEvent(evt) |
8423 super().mouseReleaseEvent(evt) |
8475 |
8424 |
8476 if ( |
8425 if ( |
8477 button != Qt.MouseButton.NoButton and |
8426 button != Qt.MouseButton.NoButton and |
8478 Preferences.getEditor("MouseClickHandlersEnabled") and |
8427 Preferences.getEditor("MouseClickHandlersEnabled") and |
8479 key in self.__mouseClickHandlers |
8428 key in self.__mouseClickHandlers |