10366:411df92e881f | 10460:3b34efa2857c |
---|---|
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 | 2 |
3 # Copyright (c) 2002 - 2023 Detlev Offenbach <detlev@die-offenbachs.de> | 3 # Copyright (c) 2002 - 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
4 # | 4 # |
5 | 5 |
6 """ | 6 """ |
7 Module implementing the editor component of the eric IDE. | 7 Module implementing the editor component of the eric IDE. |
8 """ | 8 """ |
49 from eric7.EricGui.EricOverrideCursor import EricOverrideCursor | 49 from eric7.EricGui.EricOverrideCursor import EricOverrideCursor |
50 from eric7.EricUtilities.EricCache import EricCache | 50 from eric7.EricUtilities.EricCache import EricCache |
51 from eric7.EricWidgets import EricFileDialog, EricMessageBox | 51 from eric7.EricWidgets import EricFileDialog, EricMessageBox |
52 from eric7.EricWidgets.EricApplication import ericApp | 52 from eric7.EricWidgets.EricApplication import ericApp |
53 from eric7.Globals import recentNameBreakpointConditions | 53 from eric7.Globals import recentNameBreakpointConditions |
54 from eric7.SystemUtilities import OSUtilities, PythonUtilities | 54 from eric7.SystemUtilities import FileSystemUtilities, OSUtilities, PythonUtilities |
55 from eric7.UI import PythonDisViewer | 55 from eric7.UI import PythonDisViewer |
56 from eric7.Utilities import MouseUtilities | 56 from eric7.Utilities import MouseUtilities |
57 | 57 |
58 from . import Exporters, Lexers, TypingCompleters | 58 from . import Exporters, Lexers, TypingCompleters |
59 from .EditorMarkerMap import EditorMarkerMap | 59 from .EditorMarkerMap import EditorMarkerMap |
144 lastEditPositionAvailable = pyqtSignal() | 144 lastEditPositionAvailable = pyqtSignal() |
145 refreshed = pyqtSignal() | 145 refreshed = pyqtSignal() |
146 settingsRead = pyqtSignal() | 146 settingsRead = pyqtSignal() |
147 mouseDoubleClick = pyqtSignal(QPoint, Qt.MouseButton) | 147 mouseDoubleClick = pyqtSignal(QPoint, Qt.MouseButton) |
148 | 148 |
149 # TODO: convert to an enum.Enum | |
149 WarningCode = 1 | 150 WarningCode = 1 |
150 WarningPython = 2 | 151 WarningPython = 2 |
151 WarningStyle = 3 | 152 WarningStyle = 3 |
152 WarningInfo = 4 | 153 WarningInfo = 4 |
153 WarningError = 5 | 154 WarningError = 5 |
154 | 155 |
156 # TODO: convert to an enum.IntEnum (also in Assistant plugin) | |
155 # Autocompletion icon definitions | 157 # Autocompletion icon definitions |
156 ClassID = 1 | 158 ClassID = 1 |
157 ClassProtectedID = 2 | 159 ClassProtectedID = 2 |
158 ClassPrivateID = 3 | 160 ClassPrivateID = 3 |
159 MethodID = 4 | 161 MethodID = 4 |
416 if editor is None: | 418 if editor is None: |
417 if self.fileName: | 419 if self.fileName: |
418 if not Utilities.MimeTypes.isTextFile(self.fileName): | 420 if not Utilities.MimeTypes.isTextFile(self.fileName): |
419 raise OSError() | 421 raise OSError() |
420 | 422 |
421 if self.isLocalFile() and pathlib.Path(self.fileName).exists(): | 423 if ( |
424 FileSystemUtilities.isPlainFileName(self.fileName) | |
425 and pathlib.Path(self.fileName).exists() | |
426 ): | |
422 fileSizeKB = pathlib.Path(self.fileName).stat().st_size // 1024 | 427 fileSizeKB = pathlib.Path(self.fileName).stat().st_size // 1024 |
423 if fileSizeKB > Preferences.getEditor("RejectFilesize"): | 428 if fileSizeKB > Preferences.getEditor("RejectFilesize"): |
424 EricMessageBox.warning( | 429 EricMessageBox.warning( |
425 None, | 430 None, |
426 self.tr("Open File"), | 431 self.tr("Open File"), |
630 @param name name of the current file | 635 @param name name of the current file |
631 @type str | 636 @type str |
632 """ | 637 """ |
633 renamed = self.fileName != name | 638 renamed = self.fileName != name |
634 | 639 |
640 oldFileName = self.fileName | |
635 self.fileName = name | 641 self.fileName = name |
636 | 642 |
637 if renamed: | 643 if renamed: |
638 self.vm.setEditorName(self, self.fileName) | 644 self.vm.setEditorName(self, self.fileName) |
645 self.vm.removeWatchedFilePath(oldFileName) | |
646 self.vm.addWatchedFilePath(self.fileName) | |
639 | 647 |
640 if self.fileName: | 648 if self.fileName: |
641 self.__fileNameExtension = os.path.splitext(self.fileName)[1][1:].lower() | 649 self.__fileNameExtension = os.path.splitext(self.fileName)[1][1:].lower() |
642 else: | 650 else: |
643 self.__fileNameExtension = "" | 651 self.__fileNameExtension = "" |
644 | |
645 def isLocalFile(self): | |
646 """ | |
647 Public method to check, if the editor contains a local file. | |
648 | |
649 @return flag indicating a local file | |
650 @rtype bool | |
651 """ | |
652 return not self.fileName.startswith(("device:", "remote:")) | |
653 | |
654 def isDeviceFile(self): | |
655 """ | |
656 Public method to check, if the editor contains a MCU device file. | |
657 | |
658 @return flag indicating a MCU device file | |
659 @rtype bool | |
660 """ | |
661 return self.fileName.startswith("device:") | |
662 | |
663 def isRemoteFile(self): | |
664 """ | |
665 Public method to check, if the editor contains a remote file. | |
666 | |
667 @return flag indicating a remote file | |
668 @rtype bool | |
669 """ | |
670 return self.fileName.startswith("remote:") | |
671 | 652 |
672 def __registerImages(self): | 653 def __registerImages(self): |
673 """ | 654 """ |
674 Private method to register images for autocompletion lists. | 655 Private method to register images for autocompletion lists. |
675 """ | 656 """ |
765 def __bindName(self, line0): | 746 def __bindName(self, line0): |
766 """ | 747 """ |
767 Private method to generate a dummy filename for binding a lexer. | 748 Private method to generate a dummy filename for binding a lexer. |
768 | 749 |
769 @param line0 first line of text to use in the generation process | 750 @param line0 first line of text to use in the generation process |
770 (string) | 751 @type str |
771 @return dummy file name to be used for binding a lexer (string) | 752 @return dummy file name to be used for binding a lexer |
753 @rtype str | |
772 """ | 754 """ |
773 bindName = "" | 755 bindName = "" |
774 line0 = line0.lower() | 756 line0 = line0.lower() |
775 | 757 |
776 # check first line if it does not start with #! | 758 # check first line if it does not start with #! |
840 | 822 |
841 def getMenu(self, menuName): | 823 def getMenu(self, menuName): |
842 """ | 824 """ |
843 Public method to get a reference to the main context menu or a submenu. | 825 Public method to get a reference to the main context menu or a submenu. |
844 | 826 |
845 @param menuName name of the menu (string) | 827 @param menuName name of the menu |
846 @return reference to the requested menu (QMenu) or None | 828 @type str |
829 @return reference to the requested menu or None | |
830 @rtype QMenu | |
847 """ | 831 """ |
848 try: | 832 try: |
849 return self.__menus[menuName] | 833 return self.__menus[menuName] |
850 except KeyError: | 834 except KeyError: |
851 return None | 835 return None |
852 | 836 |
853 def hasMiniMenu(self): | 837 def hasMiniMenu(self): |
854 """ | 838 """ |
855 Public method to check the miniMenu flag. | 839 Public method to check the miniMenu flag. |
856 | 840 |
857 @return flag indicating a minimized context menu (boolean) | 841 @return flag indicating a minimized context menu |
842 @rtype bool | |
858 """ | 843 """ |
859 return self.miniMenu | 844 return self.miniMenu |
860 | 845 |
861 def __initContextMenu(self): | 846 def __initContextMenu(self): |
862 """ | 847 """ |
1212 self.languagesActGrp.addAction(self.noLanguageAct) | 1197 self.languagesActGrp.addAction(self.noLanguageAct) |
1213 menu.addSeparator() | 1198 menu.addSeparator() |
1214 | 1199 |
1215 self.supportedLanguages = {} | 1200 self.supportedLanguages = {} |
1216 supportedLanguages = Lexers.getSupportedLanguages() | 1201 supportedLanguages = Lexers.getSupportedLanguages() |
1217 languages = sorted(supportedLanguages.keys()) | 1202 for language in sorted(supportedLanguages): |
1218 for language in languages: | |
1219 if language != "Guessed": | 1203 if language != "Guessed": |
1220 self.supportedLanguages[language] = supportedLanguages[language][:2] | 1204 self.supportedLanguages[language] = supportedLanguages[language][:2] |
1221 act = menu.addAction( | 1205 act = menu.addAction( |
1222 EricPixmapCache.getIcon(supportedLanguages[language][2]), | 1206 EricPixmapCache.getIcon(supportedLanguages[language][2]), |
1223 self.supportedLanguages[language][0], | 1207 self.supportedLanguages[language][0], |
1523 | 1507 |
1524 def exportFile(self, exporterFormat): | 1508 def exportFile(self, exporterFormat): |
1525 """ | 1509 """ |
1526 Public method to export the file. | 1510 Public method to export the file. |
1527 | 1511 |
1528 @param exporterFormat format the file should be exported into (string) | 1512 @param exporterFormat format the file should be exported into |
1513 @type str | |
1529 """ | 1514 """ |
1530 if exporterFormat: | 1515 if exporterFormat: |
1531 exporter = Exporters.getExporter(exporterFormat, self) | 1516 exporter = Exporters.getExporter(exporterFormat, self) |
1532 if exporter: | 1517 if exporter: |
1533 exporter.exportSource() | 1518 exporter.exportSource() |
1563 | 1548 |
1564 def __selectPygmentsLexer(self): | 1549 def __selectPygmentsLexer(self): |
1565 """ | 1550 """ |
1566 Private method to select a specific pygments lexer. | 1551 Private method to select a specific pygments lexer. |
1567 | 1552 |
1568 @return name of the selected pygments lexer (string) | 1553 @return name of the selected pygments lexer |
1554 @rtype str | |
1569 """ | 1555 """ |
1570 from pygments.lexers import get_all_lexers # __IGNORE_WARNING_I102__ | 1556 from pygments.lexers import get_all_lexers # __IGNORE_WARNING_I102__ |
1571 | 1557 |
1572 lexerList = sorted(lex[0] for lex in get_all_lexers()) | 1558 lexerList = sorted(lex[0] for lex in get_all_lexers()) |
1573 try: | 1559 try: |
1591 | 1577 |
1592 def __languageMenuTriggered(self, act): | 1578 def __languageMenuTriggered(self, act): |
1593 """ | 1579 """ |
1594 Private method to handle the selection of a lexer language. | 1580 Private method to handle the selection of a lexer language. |
1595 | 1581 |
1596 @param act reference to the action that was triggered (QAction) | 1582 @param act reference to the action that was triggered |
1583 @type QAction | |
1597 """ | 1584 """ |
1598 if act == self.noLanguageAct: | 1585 if act == self.noLanguageAct: |
1599 self.__resetLanguage() | 1586 self.__resetLanguage() |
1600 elif act == self.pygmentsAct: | 1587 elif act == self.pygmentsAct: |
1601 self.setLanguage("dummy.pygments") | 1588 self.setLanguage("dummy.pygments") |
1614 | 1601 |
1615 def __languageChanged(self, language, propagate=True): | 1602 def __languageChanged(self, language, propagate=True): |
1616 """ | 1603 """ |
1617 Private method handling a change of a connected editor's language. | 1604 Private method handling a change of a connected editor's language. |
1618 | 1605 |
1619 @param language language to be set (string) | 1606 @param language language to be set |
1620 @param propagate flag indicating to propagate the change (boolean) | 1607 @type str |
1608 @param propagate flag indicating to propagate the change | |
1609 @type bool | |
1621 """ | 1610 """ |
1622 if language == "": | 1611 if language == "": |
1623 self.__resetLanguage(propagate=propagate) | 1612 self.__resetLanguage(propagate=propagate) |
1624 elif language == "Guessed": | 1613 elif language == "Guessed": |
1625 self.setLanguage("dummy.pygments", propagate=propagate) | 1614 self.setLanguage("dummy.pygments", propagate=propagate) |
1635 | 1624 |
1636 def __resetLanguage(self, propagate=True): | 1625 def __resetLanguage(self, propagate=True): |
1637 """ | 1626 """ |
1638 Private method used to reset the language selection. | 1627 Private method used to reset the language selection. |
1639 | 1628 |
1640 @param propagate flag indicating to propagate the change (boolean) | 1629 @param propagate flag indicating to propagate the change |
1630 @type bool | |
1641 """ | 1631 """ |
1642 if self.lexer_ is not None and ( | 1632 if self.lexer_ is not None and ( |
1643 self.lexer_.lexer() == "container" or self.lexer_.lexer() is None | 1633 self.lexer_.lexer() == "container" or self.lexer_.lexer() is None |
1644 ): | 1634 ): |
1645 with contextlib.suppress(TypeError): | 1635 with contextlib.suppress(TypeError): |
1669 def setLanguage(self, filename, initTextDisplay=True, propagate=True, pyname=""): | 1659 def setLanguage(self, filename, initTextDisplay=True, propagate=True, pyname=""): |
1670 """ | 1660 """ |
1671 Public method to set a lexer language. | 1661 Public method to set a lexer language. |
1672 | 1662 |
1673 @param filename filename used to determine the associated lexer | 1663 @param filename filename used to determine the associated lexer |
1674 language (string) | 1664 language |
1665 @type str | |
1675 @param initTextDisplay flag indicating an initialization of the text | 1666 @param initTextDisplay flag indicating an initialization of the text |
1676 display is required as well (boolean) | 1667 display is required as well |
1677 @param propagate flag indicating to propagate the change (boolean) | 1668 @type bool |
1678 @param pyname name of the pygments lexer to use (string) | 1669 @param propagate flag indicating to propagate the change |
1670 @type bool | |
1671 @param pyname name of the pygments lexer to use | |
1672 @type str | |
1679 """ | 1673 """ |
1680 # clear all warning and syntax error markers | 1674 # clear all warning and syntax error markers |
1681 self.clearSyntaxError() | 1675 self.clearSyntaxError() |
1682 self.clearWarnings() | 1676 self.clearWarnings() |
1683 | 1677 |
1736 | 1730 |
1737 def __encodingsMenuTriggered(self, act): | 1731 def __encodingsMenuTriggered(self, act): |
1738 """ | 1732 """ |
1739 Private method to handle the selection of an encoding. | 1733 Private method to handle the selection of an encoding. |
1740 | 1734 |
1741 @param act reference to the action that was triggered (QAction) | 1735 @param act reference to the action that was triggered |
1736 @type QAction | |
1742 """ | 1737 """ |
1743 encoding = act.data() | 1738 encoding = act.data() |
1744 self.setModified(True) | 1739 self.setModified(True) |
1745 self.__encodingChanged("{0}-selected".format(encoding)) | 1740 self.__encodingChanged("{0}-selected".format(encoding)) |
1746 | 1741 |
1771 | 1766 |
1772 def __normalizedEncoding(self, encoding=""): | 1767 def __normalizedEncoding(self, encoding=""): |
1773 """ | 1768 """ |
1774 Private method to calculate the normalized encoding string. | 1769 Private method to calculate the normalized encoding string. |
1775 | 1770 |
1776 @param encoding encoding to be normalized (string) | 1771 @param encoding encoding to be normalized |
1777 @return normalized encoding (string) | 1772 @type str |
1773 @return normalized encoding | |
1774 @rtype str | |
1778 """ | 1775 """ |
1779 if not encoding: | 1776 if not encoding: |
1780 encoding = self.encoding | 1777 encoding = self.encoding |
1781 return ( | 1778 return ( |
1782 encoding.replace("-default", "") | 1779 encoding.replace("-default", "") |
1793 | 1790 |
1794 def __eolMenuTriggered(self, act): | 1791 def __eolMenuTriggered(self, act): |
1795 """ | 1792 """ |
1796 Private method to handle the selection of an eol type. | 1793 Private method to handle the selection of an eol type. |
1797 | 1794 |
1798 @param act reference to the action that was triggered (QAction) | 1795 @param act reference to the action that was triggered |
1796 @type QAction | |
1799 """ | 1797 """ |
1800 eol = act.data() | 1798 eol = act.data() |
1801 self.setEolModeByEolString(eol) | 1799 self.setEolModeByEolString(eol) |
1802 self.convertEols(self.eolMode()) | 1800 self.convertEols(self.eolMode()) |
1803 | 1801 |
1818 if not self.inEolChanged: | 1816 if not self.inEolChanged: |
1819 self.inEolChanged = True | 1817 self.inEolChanged = True |
1820 eol = self.getLineSeparator() | 1818 eol = self.getLineSeparator() |
1821 self.eolChanged.emit(eol) | 1819 self.eolChanged.emit(eol) |
1822 self.inEolChanged = False | 1820 self.inEolChanged = False |
1821 | |
1822 def convertEols(self, eolMode): | |
1823 """ | |
1824 Public method to convert the end-of-line marker. | |
1825 | |
1826 This variant of the method emits a signal to update the IDE after | |
1827 the original method was called. | |
1828 | |
1829 @param eolMode end-of-line mode | |
1830 @type QsciScintilla.EolMode | |
1831 """ | |
1832 super().convertEols(eolMode) | |
1833 self.__eolChanged() | |
1823 | 1834 |
1824 @pyqtSlot() | 1835 @pyqtSlot() |
1825 def __showContextMenuSpellCheck(self): | 1836 def __showContextMenuSpellCheck(self): |
1826 """ | 1837 """ |
1827 Private slot handling the aboutToShow signal of the spell check | 1838 Private slot handling the aboutToShow signal of the spell check |
2017 | 2028 |
2018 def getLexer(self): | 2029 def getLexer(self): |
2019 """ | 2030 """ |
2020 Public method to retrieve a reference to the lexer object. | 2031 Public method to retrieve a reference to the lexer object. |
2021 | 2032 |
2022 @return the lexer object (Lexer) | 2033 @return the lexer object |
2034 @rtype Lexer | |
2023 """ | 2035 """ |
2024 return self.lexer_ | 2036 return self.lexer_ |
2025 | 2037 |
2026 def getLanguage(self, normalized=True, forPygments=False): | 2038 def getLanguage(self, normalized=True, forPygments=False): |
2027 """ | 2039 """ |
2028 Public method to retrieve the language of the editor. | 2040 Public method to retrieve the language of the editor. |
2029 | 2041 |
2030 @param normalized flag indicating to normalize some Pygments | 2042 @param normalized flag indicating to normalize some Pygments |
2031 lexer names (boolean) | 2043 lexer names |
2044 @type bool | |
2032 @param forPygments flag indicating to normalize some lexer | 2045 @param forPygments flag indicating to normalize some lexer |
2033 names for Pygments (boolean) | 2046 names for Pygments |
2034 @return language of the editor (string) | 2047 @type bool |
2048 @return language of the editor | |
2049 @rtype str | |
2035 """ | 2050 """ |
2036 if self.apiLanguage == "Guessed" or self.apiLanguage.startswith("Pygments|"): | 2051 if self.apiLanguage == "Guessed" or self.apiLanguage.startswith("Pygments|"): |
2037 lang = self.lexer_.name() | 2052 lang = self.lexer_.name() |
2038 if normalized and lang in ("Python 2.x", "Python"): | 2053 if normalized and lang in ("Python 2.x", "Python"): |
2039 # adjust some Pygments lexer names | 2054 # adjust some Pygments lexer names |
2080 | 2095 |
2081 def getCompleter(self): | 2096 def getCompleter(self): |
2082 """ | 2097 """ |
2083 Public method to retrieve a reference to the completer object. | 2098 Public method to retrieve a reference to the completer object. |
2084 | 2099 |
2085 @return the completer object (CompleterBase) | 2100 @return the completer object |
2101 @rtype CompleterBase | |
2086 """ | 2102 """ |
2087 return self.completer | 2103 return self.completer |
2088 | 2104 |
2089 @pyqtSlot(bool) | 2105 @pyqtSlot(bool) |
2090 def __modificationChanged(self, m): | 2106 def __modificationChanged(self, m): |
2160 | 2176 |
2161 def setNoName(self, noName): | 2177 def setNoName(self, noName): |
2162 """ | 2178 """ |
2163 Public method to set the display string for an unnamed editor. | 2179 Public method to set the display string for an unnamed editor. |
2164 | 2180 |
2165 @param noName display string for this unnamed editor (string) | 2181 @param noName display string for this unnamed editor |
2182 @type str | |
2166 """ | 2183 """ |
2167 self.noName = noName | 2184 self.noName = noName |
2168 | 2185 |
2169 def getNoName(self): | 2186 def getNoName(self): |
2170 """ | 2187 """ |
2171 Public method to get the display string for an unnamed editor. | 2188 Public method to get the display string for an unnamed editor. |
2172 | 2189 |
2173 @return display string for this unnamed editor (string) | 2190 @return display string for this unnamed editor |
2191 @rtype str | |
2174 """ | 2192 """ |
2175 return self.noName | 2193 return self.noName |
2176 | 2194 |
2177 def getFileName(self): | 2195 def getFileName(self): |
2178 """ | 2196 """ |
2179 Public method to return the name of the file being displayed. | 2197 Public method to return the name of the file being displayed. |
2180 | 2198 |
2181 @return filename of the displayed file (string) | 2199 @return filename of the displayed file |
2200 @rtype str | |
2182 """ | 2201 """ |
2183 return self.fileName | 2202 return self.fileName |
2184 | 2203 |
2185 def getFileType(self): | 2204 def getFileType(self): |
2186 """ | 2205 """ |
2187 Public method to return the type of the file being displayed. | 2206 Public method to return the type of the file being displayed. |
2188 | 2207 |
2189 @return type of the displayed file (string) | 2208 @return type of the displayed file |
2209 @rtype str | |
2190 """ | 2210 """ |
2191 return self.filetype | 2211 return self.filetype |
2192 | 2212 |
2193 def getFileTypeByFlag(self): | 2213 def getFileTypeByFlag(self): |
2194 """ | 2214 """ |
2195 Public method to return the type of the file, if it was set by an | 2215 Public method to return the type of the file, if it was set by an |
2196 eflag: marker. | 2216 eflag: marker. |
2197 | 2217 |
2198 @return type of the displayed file, if set by an eflag: marker or an | 2218 @return type of the displayed file, if set by an eflag: marker or an |
2199 empty string (string) | 2219 empty string |
2220 @rtype str | |
2200 """ | 2221 """ |
2201 if self.filetypeByFlag: | 2222 if self.filetypeByFlag: |
2202 return self.filetype | 2223 return self.filetype |
2203 else: | 2224 else: |
2204 return "" | 2225 return "" |
2205 | 2226 |
2206 def determineFileType(self): | 2227 def determineFileType(self): |
2207 """ | 2228 """ |
2208 Public method to determine the file type using various tests. | 2229 Public method to determine the file type using various tests. |
2209 | 2230 |
2210 @return type of the displayed file or an empty string (string) | 2231 @return type of the displayed file or an empty string |
2232 @rtype str | |
2211 """ | 2233 """ |
2212 ftype = self.filetype | 2234 ftype = self.filetype |
2213 if not ftype: | 2235 if not ftype: |
2214 pyVer = self.__getPyVersion() | 2236 pyVer = self.__getPyVersion() |
2215 if pyVer: | 2237 if pyVer: |
2223 | 2245 |
2224 def getEncoding(self): | 2246 def getEncoding(self): |
2225 """ | 2247 """ |
2226 Public method to return the current encoding. | 2248 Public method to return the current encoding. |
2227 | 2249 |
2228 @return current encoding (string) | 2250 @return current encoding |
2251 @rtype str | |
2229 """ | 2252 """ |
2230 return self.encoding | 2253 return self.encoding |
2231 | 2254 |
2232 def __getPyVersion(self): | 2255 def __getPyVersion(self): |
2233 """ | 2256 """ |
2234 Private method to return the Python main version or 0 if it's | 2257 Private method to return the Python main version or 0 if it's |
2235 not a Python file at all. | 2258 not a Python file at all. |
2236 | 2259 |
2237 @return Python version or 0 if it's not a Python file (int) | 2260 @return Python version or 0 if it's not a Python file |
2261 @rtype int | |
2238 """ | 2262 """ |
2239 return PythonUtilities.determinePythonVersion(self.fileName, self.text(0), self) | 2263 return PythonUtilities.determinePythonVersion(self.fileName, self.text(0), self) |
2240 | 2264 |
2241 def isPyFile(self): | 2265 def isPyFile(self): |
2242 """ | 2266 """ |
2243 Public method to return a flag indicating a Python (2 or 3) file. | 2267 Public method to return a flag indicating a Python (2 or 3) file. |
2244 | 2268 |
2245 @return flag indicating a Python3 file (boolean) | 2269 @return flag indicating a Python3 file |
2270 @rtype bool | |
2246 """ | 2271 """ |
2247 return self.__getPyVersion() == 3 | 2272 return self.__getPyVersion() == 3 |
2248 | 2273 |
2249 def isPy3File(self): | 2274 def isPy3File(self): |
2250 """ | 2275 """ |
2251 Public method to return a flag indicating a Python3 file. | 2276 Public method to return a flag indicating a Python3 file. |
2252 | 2277 |
2253 @return flag indicating a Python3 file (boolean) | 2278 @return flag indicating a Python3 file |
2279 @rtype bool | |
2254 """ | 2280 """ |
2255 return self.__getPyVersion() == 3 | 2281 return self.__getPyVersion() == 3 |
2256 | 2282 |
2257 def isMicroPythonFile(self): | 2283 def isMicroPythonFile(self): |
2258 """ | 2284 """ |
2280 | 2306 |
2281 def isRubyFile(self): | 2307 def isRubyFile(self): |
2282 """ | 2308 """ |
2283 Public method to return a flag indicating a Ruby file. | 2309 Public method to return a flag indicating a Ruby file. |
2284 | 2310 |
2285 @return flag indicating a Ruby file (boolean) | 2311 @return flag indicating a Ruby file |
2312 @rtype bool | |
2286 """ | 2313 """ |
2287 if self.filetype == "Ruby": | 2314 if self.filetype == "Ruby": |
2288 return True | 2315 return True |
2289 | 2316 |
2290 if self.filetype == "": | 2317 if self.filetype == "": |
2303 | 2330 |
2304 def isJavascriptFile(self): | 2331 def isJavascriptFile(self): |
2305 """ | 2332 """ |
2306 Public method to return a flag indicating a Javascript file. | 2333 Public method to return a flag indicating a Javascript file. |
2307 | 2334 |
2308 @return flag indicating a Javascript file (boolean) | 2335 @return flag indicating a Javascript file |
2336 @rtype bool | |
2309 """ | 2337 """ |
2310 if self.filetype == "JavaScript": | 2338 if self.filetype == "JavaScript": |
2311 return True | 2339 return True |
2312 | 2340 |
2313 if ( | 2341 if ( |
2344 | 2372 |
2345 def highlight(self, line=None, error=False, syntaxError=False): # noqa: U100 | 2373 def highlight(self, line=None, error=False, syntaxError=False): # noqa: U100 |
2346 """ | 2374 """ |
2347 Public method to highlight [or de-highlight] a particular line. | 2375 Public method to highlight [or de-highlight] a particular line. |
2348 | 2376 |
2349 @param line line number to highlight (integer) | 2377 @param line line number to highlight |
2378 @type int | |
2350 @param error flag indicating whether the error highlight should be | 2379 @param error flag indicating whether the error highlight should be |
2351 used (boolean) | 2380 used |
2352 @param syntaxError flag indicating a syntax error (boolean) | 2381 @type bool |
2382 @param syntaxError flag indicating a syntax error | |
2383 @type bool | |
2353 """ | 2384 """ |
2354 if line is None: | 2385 if line is None: |
2355 self.lastHighlight = None | 2386 self.lastHighlight = None |
2356 if self.lastErrorMarker is not None: | 2387 if self.lastErrorMarker is not None: |
2357 self.markerDeleteHandle(self.lastErrorMarker) | 2388 self.markerDeleteHandle(self.lastErrorMarker) |
2374 | 2405 |
2375 def getHighlightPosition(self): | 2406 def getHighlightPosition(self): |
2376 """ | 2407 """ |
2377 Public method to return the position of the highlight bar. | 2408 Public method to return the position of the highlight bar. |
2378 | 2409 |
2379 @return line number of the highlight bar (integer) | 2410 @return line number of the highlight bar |
2411 @rtype int | |
2380 """ | 2412 """ |
2381 if self.lastHighlight is not None: | 2413 if self.lastHighlight is not None: |
2382 return self.markerLine(self.lastHighlight) | 2414 return self.markerLine(self.lastHighlight) |
2383 else: | 2415 else: |
2384 return 1 | 2416 return 1 |
2401 annotationLinesAdded, # noqa: U100 | 2433 annotationLinesAdded, # noqa: U100 |
2402 ): | 2434 ): |
2403 """ | 2435 """ |
2404 Private method to handle changes of the number of lines. | 2436 Private method to handle changes of the number of lines. |
2405 | 2437 |
2406 @param pos start position of change (integer) | 2438 @param pos start position of change |
2407 @param mtype flags identifying the change (integer) | 2439 @type int |
2408 @param text text that is given to the Undo system (string) | 2440 @param mtype flags identifying the change |
2409 @param length length of the change (integer) | 2441 @type int |
2410 @param linesAdded number of added/deleted lines (integer) | 2442 @param text text that is given to the Undo system |
2411 @param line line number of a fold level or marker change (integer) | 2443 @type str |
2412 @param foldNow new fold level (integer) | 2444 @param length length of the change |
2413 @param foldPrev previous fold level (integer) | 2445 @type int |
2446 @param linesAdded number of added/deleted lines | |
2447 @type int | |
2448 @param line line number of a fold level or marker change | |
2449 @type int | |
2450 @param foldNow new fold level | |
2451 @type int | |
2452 @param foldPrev previous fold level | |
2453 @type int | |
2414 @param token ??? | 2454 @param token ??? |
2455 @type int | |
2415 @param annotationLinesAdded number of added/deleted annotation lines | 2456 @param annotationLinesAdded number of added/deleted annotation lines |
2416 (integer) | 2457 @type int |
2417 """ | 2458 """ |
2418 if mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT): | 2459 if mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT): |
2419 # 1. set/reset the autosave timer | 2460 # 1. set/reset the autosave timer |
2420 if self.__autosaveInterval > 0: | 2461 if self.__autosaveInterval > 0: |
2421 self.__autosaveTimer.start(self.__autosaveInterval * 1000) | 2462 self.__autosaveTimer.start(self.__autosaveInterval * 1000) |
2526 Public method to clear a breakpoint. | 2567 Public method to clear a breakpoint. |
2527 | 2568 |
2528 Note: This doesn't clear the breakpoint in the debugger, | 2569 Note: This doesn't clear the breakpoint in the debugger, |
2529 it just deletes it from the editor internal list of breakpoints. | 2570 it just deletes it from the editor internal list of breakpoints. |
2530 | 2571 |
2531 @param line line number of the breakpoint (integer) | 2572 @param line line number of the breakpoint |
2573 @type int | |
2532 """ | 2574 """ |
2533 if self.inLinesChanged: | 2575 if self.inLinesChanged: |
2534 return | 2576 return |
2535 | 2577 |
2536 for handle in self.breaks: | 2578 for handle in self.breaks: |
2542 | 2584 |
2543 def newBreakpointWithProperties(self, line, properties): | 2585 def newBreakpointWithProperties(self, line, properties): |
2544 """ | 2586 """ |
2545 Public method to set a new breakpoint and its properties. | 2587 Public method to set a new breakpoint and its properties. |
2546 | 2588 |
2547 @param line line number of the breakpoint (integer) | 2589 @param line line number of the breakpoint |
2548 @param properties properties for the breakpoint (tuple) | 2590 @type int |
2591 @param properties properties for the breakpoint | |
2549 (condition, temporary flag, enabled flag, ignore count) | 2592 (condition, temporary flag, enabled flag, ignore count) |
2593 @type tuple of (str, bool, bool, int) | |
2550 """ | 2594 """ |
2551 if not properties[2]: | 2595 if not properties[2]: |
2552 marker = self.dbreakpoint | 2596 marker = self.dbreakpoint |
2553 elif properties[0]: | 2597 elif properties[0]: |
2554 marker = properties[1] and self.tcbreakpoint or self.cbreakpoint | 2598 marker = properties[1] and self.tcbreakpoint or self.cbreakpoint |
2563 | 2607 |
2564 def __toggleBreakpoint(self, line, temporary=False): | 2608 def __toggleBreakpoint(self, line, temporary=False): |
2565 """ | 2609 """ |
2566 Private method to toggle a breakpoint. | 2610 Private method to toggle a breakpoint. |
2567 | 2611 |
2568 @param line line number of the breakpoint (integer) | 2612 @param line line number of the breakpoint |
2569 @param temporary flag indicating a temporary breakpoint (boolean) | 2613 @type int |
2614 @param temporary flag indicating a temporary breakpoint | |
2615 @type bool | |
2570 """ | 2616 """ |
2571 for handle in self.breaks: | 2617 for handle in self.breaks: |
2572 if self.markerLine(handle) == line - 1: | 2618 if self.markerLine(handle) == line - 1: |
2573 # delete breakpoint or toggle it to the next state | 2619 # delete breakpoint or toggle it to the next state |
2574 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) | 2620 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) |
2586 | 2632 |
2587 def __addBreakPoint(self, line, temporary): | 2633 def __addBreakPoint(self, line, temporary): |
2588 """ | 2634 """ |
2589 Private method to add a new breakpoint. | 2635 Private method to add a new breakpoint. |
2590 | 2636 |
2591 @param line line number of the breakpoint (integer) | 2637 @param line line number of the breakpoint |
2592 @param temporary flag indicating a temporary breakpoint (boolean) | 2638 @type int |
2639 @param temporary flag indicating a temporary breakpoint | |
2640 @type bool | |
2593 """ | 2641 """ |
2594 if self.fileName and self.isPyFile(): | 2642 if self.fileName and self.isPyFile(): |
2595 linestarts = PythonDisViewer.linestarts(self.text()) | 2643 linestarts = PythonDisViewer.linestarts(self.text()) |
2596 if line not in linestarts: | 2644 if line not in linestarts: |
2597 if Preferences.getDebugger("IntelligentBreakpoints"): | 2645 if Preferences.getDebugger("IntelligentBreakpoints"): |
2618 | 2666 |
2619 def __toggleBreakpointEnabled(self, line): | 2667 def __toggleBreakpointEnabled(self, line): |
2620 """ | 2668 """ |
2621 Private method to toggle a breakpoints enabled status. | 2669 Private method to toggle a breakpoints enabled status. |
2622 | 2670 |
2623 @param line line number of the breakpoint (integer) | 2671 @param line line number of the breakpoint |
2672 @type int | |
2624 """ | 2673 """ |
2625 for handle in self.breaks: | 2674 for handle in self.breaks: |
2626 if self.markerLine(handle) == line - 1: | 2675 if self.markerLine(handle) == line - 1: |
2627 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) | 2676 index = self.breakpointModel.getBreakPointIndex(self.fileName, line) |
2628 self.breakpointModel.setBreakPointEnabledByIndex( | 2677 self.breakpointModel.setBreakPointEnabledByIndex( |
2633 def curLineHasBreakpoint(self): | 2682 def curLineHasBreakpoint(self): |
2634 """ | 2683 """ |
2635 Public method to check for the presence of a breakpoint at the current | 2684 Public method to check for the presence of a breakpoint at the current |
2636 line. | 2685 line. |
2637 | 2686 |
2638 @return flag indicating the presence of a breakpoint (boolean) | 2687 @return flag indicating the presence of a breakpoint |
2688 @rtype bool | |
2639 """ | 2689 """ |
2640 line, _ = self.getCursorPosition() | 2690 line, _ = self.getCursorPosition() |
2641 return self.markersAtLine(line) & self.breakpointMask != 0 | 2691 return self.markersAtLine(line) & self.breakpointMask != 0 |
2642 | 2692 |
2643 def getBreakpointLines(self): | 2693 def getBreakpointLines(self): |
2644 """ | 2694 """ |
2645 Public method to get the lines containing a breakpoint. | 2695 Public method to get the lines containing a breakpoint. |
2646 | 2696 |
2647 @return list of lines containing a breakpoint (list of integer) | 2697 @return list of lines containing a breakpoint |
2698 @rtype list of int | |
2648 """ | 2699 """ |
2649 lines = [] | 2700 lines = [] |
2650 line = -1 | 2701 line = -1 |
2651 while True: | 2702 while True: |
2652 line = self.markerFindNext(line + 1, self.breakpointMask) | 2703 line = self.markerFindNext(line + 1, self.breakpointMask) |
2658 | 2709 |
2659 def hasBreakpoints(self): | 2710 def hasBreakpoints(self): |
2660 """ | 2711 """ |
2661 Public method to check for the presence of breakpoints. | 2712 Public method to check for the presence of breakpoints. |
2662 | 2713 |
2663 @return flag indicating the presence of breakpoints (boolean) | 2714 @return flag indicating the presence of breakpoints |
2715 @rtype bool | |
2664 """ | 2716 """ |
2665 return len(self.breaks) > 0 | 2717 return len(self.breaks) > 0 |
2666 | 2718 |
2667 @pyqtSlot() | 2719 @pyqtSlot() |
2668 def __menuToggleTemporaryBreakpoint(self): | 2720 def __menuToggleTemporaryBreakpoint(self): |
2820 | 2872 |
2821 def toggleBookmark(self, line): | 2873 def toggleBookmark(self, line): |
2822 """ | 2874 """ |
2823 Public method to toggle a bookmark. | 2875 Public method to toggle a bookmark. |
2824 | 2876 |
2825 @param line line number of the bookmark (integer) | 2877 @param line line number of the bookmark |
2878 @type int | |
2826 """ | 2879 """ |
2827 for handle in self.bookmarks: | 2880 for handle in self.bookmarks: |
2828 if self.markerLine(handle) == line - 1: | 2881 if self.markerLine(handle) == line - 1: |
2829 self.bookmarks.remove(handle) | 2882 self.bookmarks.remove(handle) |
2830 self.markerDeleteHandle(handle) | 2883 self.markerDeleteHandle(handle) |
2839 def getBookmarks(self): | 2892 def getBookmarks(self): |
2840 """ | 2893 """ |
2841 Public method to retrieve the bookmarks. | 2894 Public method to retrieve the bookmarks. |
2842 | 2895 |
2843 @return sorted list of all lines containing a bookmark | 2896 @return sorted list of all lines containing a bookmark |
2844 (list of integer) | 2897 @rtype list of int |
2845 """ | 2898 """ |
2846 bmlist = [] | 2899 bmlist = [] |
2847 for handle in self.bookmarks: | 2900 for handle in self.bookmarks: |
2848 bmlist.append(self.markerLine(handle) + 1) | 2901 bmlist.append(self.markerLine(handle) + 1) |
2849 | 2902 |
2852 | 2905 |
2853 def getBookmarkLines(self): | 2906 def getBookmarkLines(self): |
2854 """ | 2907 """ |
2855 Public method to get the lines containing a bookmark. | 2908 Public method to get the lines containing a bookmark. |
2856 | 2909 |
2857 @return list of lines containing a bookmark (list of integer) | 2910 @return list of lines containing a bookmark |
2911 @rtype list of int | |
2858 """ | 2912 """ |
2859 lines = [] | 2913 lines = [] |
2860 line = -1 | 2914 line = -1 |
2861 while True: | 2915 while True: |
2862 line = self.markerFindNext(line + 1, 1 << self.bookmark) | 2916 line = self.markerFindNext(line + 1, 1 << self.bookmark) |
2868 | 2922 |
2869 def hasBookmarks(self): | 2923 def hasBookmarks(self): |
2870 """ | 2924 """ |
2871 Public method to check for the presence of bookmarks. | 2925 Public method to check for the presence of bookmarks. |
2872 | 2926 |
2873 @return flag indicating the presence of bookmarks (boolean) | 2927 @return flag indicating the presence of bookmarks |
2928 @rtype bool | |
2874 """ | 2929 """ |
2875 return len(self.bookmarks) > 0 | 2930 return len(self.bookmarks) > 0 |
2876 | 2931 |
2877 @pyqtSlot() | 2932 @pyqtSlot() |
2878 def menuToggleBookmark(self): | 2933 def menuToggleBookmark(self): |
2996 def __printPreview(self, printer): | 3051 def __printPreview(self, printer): |
2997 """ | 3052 """ |
2998 Private slot to generate a print preview. | 3053 Private slot to generate a print preview. |
2999 | 3054 |
3000 @param printer reference to the printer object | 3055 @param printer reference to the printer object |
3001 (QScintilla.Printer.Printer) | 3056 @type QScintilla.Printer.Printer |
3002 """ | 3057 """ |
3003 printer.printRange(self) | 3058 printer.printRange(self) |
3004 | 3059 |
3005 ########################################################################### | 3060 ########################################################################### |
3006 ## Task handling methods below | 3061 ## Task handling methods below |
3008 | 3063 |
3009 def getTaskLines(self): | 3064 def getTaskLines(self): |
3010 """ | 3065 """ |
3011 Public method to get the lines containing a task. | 3066 Public method to get the lines containing a task. |
3012 | 3067 |
3013 @return list of lines containing a task (list of integer) | 3068 @return list of lines containing a task |
3069 @rtype list of int | |
3014 """ | 3070 """ |
3015 lines = [] | 3071 lines = [] |
3016 line = -1 | 3072 line = -1 |
3017 while True: | 3073 while True: |
3018 line = self.markerFindNext(line + 1, 1 << self.taskmarker) | 3074 line = self.markerFindNext(line + 1, 1 << self.taskmarker) |
3024 | 3080 |
3025 def hasTaskMarkers(self): | 3081 def hasTaskMarkers(self): |
3026 """ | 3082 """ |
3027 Public method to determine, if this editor contains any task markers. | 3083 Public method to determine, if this editor contains any task markers. |
3028 | 3084 |
3029 @return flag indicating the presence of task markers (boolean) | 3085 @return flag indicating the presence of task markers |
3086 @rtype bool | |
3030 """ | 3087 """ |
3031 return self.__hasTaskMarkers | 3088 return self.__hasTaskMarkers |
3032 | 3089 |
3033 @pyqtSlot() | 3090 @pyqtSlot() |
3034 def nextTask(self): | 3091 def nextTask(self): |
3115 | 3172 |
3116 def __createChangeMarkerPixmap(self, key, size=16): | 3173 def __createChangeMarkerPixmap(self, key, size=16): |
3117 """ | 3174 """ |
3118 Private method to create a pixmap for the change markers. | 3175 Private method to create a pixmap for the change markers. |
3119 | 3176 |
3120 @param key key of the color to use (string) | 3177 @param key key of the color to use |
3121 @param size size of the pixmap (integer) | 3178 @type str |
3122 @return create pixmap (QPixmap) | 3179 @param size size of the pixmap |
3180 @type int | |
3181 @return create pixmap | |
3182 @rtype QPixmap | |
3123 """ | 3183 """ |
3124 pixmap = QPixmap(size, size) | 3184 pixmap = QPixmap(size, size) |
3125 pixmap.fill(Qt.GlobalColor.transparent) | 3185 pixmap.fill(Qt.GlobalColor.transparent) |
3126 painter = QPainter(pixmap) | 3186 painter = QPainter(pixmap) |
3127 painter.fillRect(size - 4, 0, 4, size, Preferences.getEditorColour(key)) | 3187 painter.fillRect(size - 4, 0, 4, size, Preferences.getEditorColour(key)) |
3238 | 3298 |
3239 def getChangeLines(self): | 3299 def getChangeLines(self): |
3240 """ | 3300 """ |
3241 Public method to get the lines containing a change. | 3301 Public method to get the lines containing a change. |
3242 | 3302 |
3243 @return list of lines containing a change (list of integer) | 3303 @return list of lines containing a change |
3304 @rtype list of int | |
3244 """ | 3305 """ |
3245 lines = [] | 3306 lines = [] |
3246 line = -1 | 3307 line = -1 |
3247 while True: | 3308 while True: |
3248 line = self.markerFindNext(line + 1, self.changeMarkersMask) | 3309 line = self.markerFindNext(line + 1, self.changeMarkersMask) |
3254 | 3315 |
3255 def hasChangeMarkers(self): | 3316 def hasChangeMarkers(self): |
3256 """ | 3317 """ |
3257 Public method to determine, if this editor contains any change markers. | 3318 Public method to determine, if this editor contains any change markers. |
3258 | 3319 |
3259 @return flag indicating the presence of change markers (boolean) | 3320 @return flag indicating the presence of change markers |
3321 @rtype bool | |
3260 """ | 3322 """ |
3261 return self.__hasChangeMarkers | 3323 return self.__hasChangeMarkers |
3262 | 3324 |
3263 @pyqtSlot() | 3325 @pyqtSlot() |
3264 def nextChange(self): | 3326 def nextChange(self): |
3304 | 3366 |
3305 def __processFlags(self): | 3367 def __processFlags(self): |
3306 """ | 3368 """ |
3307 Private method to extract flags and process them. | 3369 Private method to extract flags and process them. |
3308 | 3370 |
3309 @return list of change flags (list of string) | 3371 @return list of change flags |
3372 @rtype list of str | |
3310 """ | 3373 """ |
3311 txt = self.text() | 3374 txt = self.text() |
3312 flags = Utilities.extractFlags(txt) | 3375 flags = Utilities.extractFlags(txt) |
3313 | 3376 |
3314 changedFlags = [] | 3377 changedFlags = [] |
3336 | 3399 |
3337 def checkDirty(self): | 3400 def checkDirty(self): |
3338 """ | 3401 """ |
3339 Public method to check dirty status and open a message window. | 3402 Public method to check dirty status and open a message window. |
3340 | 3403 |
3341 @return flag indicating successful reset of the dirty flag (boolean) | 3404 @return flag indicating successful reset of the dirty flag |
3405 @rtype bool | |
3342 """ | 3406 """ |
3343 if self.isModified(): | 3407 if self.isModified(): |
3344 fn = self.fileName | 3408 fn = self.fileName |
3345 if fn is None: | 3409 if fn is None: |
3346 fn = self.noName | 3410 fn = self.noName |
3347 res = EricMessageBox.okToClearData( | 3411 res = EricMessageBox.okToClearData( |
3348 self, | 3412 self, |
3349 self.tr("File Modified"), | 3413 self.tr("File Modified"), |
3350 self.tr("<p>The file <b>{0}</b> has unsaved changes.</p>").format(fn), | 3414 self.tr("<p>The file <b>{0}</b> has unsaved changes.</p>").format(fn), |
3351 self.saveFile if not self.isRemoteFile() else None, | 3415 self.saveFile |
3416 if not FileSystemUtilities.isRemoteFileName(self.fileName) | |
3417 else None, | |
3352 ) | 3418 ) |
3353 if res: | 3419 if res: |
3354 self.vm.setEditorName(self, self.fileName) | 3420 self.vm.setEditorName(self, self.fileName) |
3355 return res | 3421 return res |
3356 | 3422 |
3374 self.redo() | 3440 self.redo() |
3375 else: | 3441 else: |
3376 break | 3442 break |
3377 # Couldn't find the unmodified state | 3443 # Couldn't find the unmodified state |
3378 | 3444 |
3379 def readFile(self, fn, createIt=False, encoding=""): | 3445 def readFile(self, fn, createIt=False, encoding="", noempty=False): |
3380 """ | 3446 """ |
3381 Public method to read the text from a file. | 3447 Public method to read the text from a file. |
3382 | 3448 |
3383 @param fn filename to read from (string) | 3449 @param fn filename to read from |
3450 @type str | |
3384 @param createIt flag indicating the creation of a new file, if the | 3451 @param createIt flag indicating the creation of a new file, if the |
3385 given one doesn't exist (boolean) | 3452 given one doesn't exist (defaults to False) |
3386 @param encoding encoding to be used to read the file (string) | 3453 @type bool (optional) |
3454 @param encoding encoding to be used to read the file (defaults to "") | |
3387 (Note: this parameter overrides encoding detection) | 3455 (Note: this parameter overrides encoding detection) |
3456 @type str (optional) | |
3457 @param noempty flag indicating to not set an empty text (defaults to False) | |
3458 @type bool (optional) | |
3388 """ | 3459 """ |
3389 self.__loadEditorConfig(fileName=fn) | 3460 self.__loadEditorConfig(fileName=fn) |
3390 | 3461 |
3391 try: | 3462 try: |
3392 with EricOverrideCursor(): | 3463 with EricOverrideCursor(): |
3410 "<p>Reason: {1}</p>" | 3481 "<p>Reason: {1}</p>" |
3411 ).format(fn, str(why)), | 3482 ).format(fn, str(why)), |
3412 ) | 3483 ) |
3413 raise | 3484 raise |
3414 | 3485 |
3486 if noempty and not bool(txt): | |
3487 return | |
3488 | |
3415 with EricOverrideCursor(): | 3489 with EricOverrideCursor(): |
3416 modified = False | |
3417 | |
3418 self.setText(txt) | 3490 self.setText(txt) |
3491 self.setModified(False) | |
3419 | 3492 |
3420 # get eric specific flags | 3493 # get eric specific flags |
3421 self.__processFlags() | 3494 self.__processFlags() |
3422 | 3495 |
3423 # perform automatic EOL conversion | 3496 # perform automatic EOL conversion |
3426 ) or Preferences.getEditor("AutomaticEOLConversion"): | 3499 ) or Preferences.getEditor("AutomaticEOLConversion"): |
3427 self.convertEols(self.eolMode()) | 3500 self.convertEols(self.eolMode()) |
3428 else: | 3501 else: |
3429 fileEol = self.detectEolString(txt) | 3502 fileEol = self.detectEolString(txt) |
3430 self.setEolModeByEolString(fileEol) | 3503 self.setEolModeByEolString(fileEol) |
3504 self.__eolChanged() | |
3431 | 3505 |
3432 self.extractTasks() | 3506 self.extractTasks() |
3433 | 3507 |
3434 self.setModified(modified) | |
3435 self.lastModified = pathlib.Path(fn).stat().st_mtime | 3508 self.lastModified = pathlib.Path(fn).stat().st_mtime |
3436 | 3509 |
3437 @pyqtSlot() | 3510 @pyqtSlot() |
3438 def __convertTabs(self): | 3511 def __convertTabs(self): |
3439 """ | 3512 """ |
3537 | 3610 |
3538 def __getSaveFileName(self, path=None): | 3611 def __getSaveFileName(self, path=None): |
3539 """ | 3612 """ |
3540 Private method to get the name of the file to be saved. | 3613 Private method to get the name of the file to be saved. |
3541 | 3614 |
3542 @param path directory to save the file in (string) | 3615 @param path directory to save the file in |
3543 @return file name (string) | 3616 @type str |
3617 @return file name | |
3618 @rtype str | |
3544 """ | 3619 """ |
3545 # save to project, if a project is loaded | 3620 # save to project, if a project is loaded |
3546 if self.project.isOpen(): | 3621 if self.project.isOpen(): |
3547 if self.fileName and self.project.startswithProjectPath(self.fileName): | 3622 if self.fileName and self.project.startswithProjectPath(self.fileName): |
3548 path = os.path.dirname(self.fileName) | 3623 path = os.path.dirname(self.fileName) |
3600 | 3675 |
3601 def saveFileCopy(self, path=None): | 3676 def saveFileCopy(self, path=None): |
3602 """ | 3677 """ |
3603 Public method to save a copy of the file. | 3678 Public method to save a copy of the file. |
3604 | 3679 |
3605 @param path directory to save the file in (string) | 3680 @param path directory to save the file in |
3606 @return flag indicating success (boolean) | 3681 @type str |
3682 @return flag indicating success | |
3683 @rtype bool | |
3607 """ | 3684 """ |
3608 fn = self.__getSaveFileName(path) | 3685 fn = self.__getSaveFileName(path) |
3609 if not fn: | 3686 if not fn: |
3610 return False | 3687 return False |
3611 | 3688 |
3618 | 3695 |
3619 def saveFile(self, saveas=False, path=None): | 3696 def saveFile(self, saveas=False, path=None): |
3620 """ | 3697 """ |
3621 Public method to save the text to a file. | 3698 Public method to save the text to a file. |
3622 | 3699 |
3623 @param saveas flag indicating a 'save as' action (boolean) | 3700 @param saveas flag indicating a 'save as' action |
3624 @param path directory to save the file in (string) | 3701 @type bool |
3625 @return flag indicating success (boolean) | 3702 @param path directory to save the file in |
3626 """ | 3703 @type str |
3627 if not saveas and (not self.isModified() or self.isRemoteFile()): | 3704 @return flag indicating success |
3705 @rtype bool | |
3706 """ | |
3707 if not saveas and ( | |
3708 not self.isModified() or FileSystemUtilities.isRemoteFileName(self.fileName) | |
3709 ): | |
3628 # do nothing if text wasn't changed or is a remote file | 3710 # do nothing if text wasn't changed or is a remote file |
3629 return False | 3711 return False |
3630 | 3712 |
3631 if self.isDeviceFile(): | 3713 if FileSystemUtilities.isDeviceFileName(self.fileName): |
3632 return self.__saveDeviceFile(saveas=saveas) | 3714 return self.__saveDeviceFile(saveas=saveas) |
3633 | 3715 |
3634 newName = None | 3716 newName = None |
3635 if saveas or self.fileName == "": | 3717 if saveas or self.fileName == "": |
3636 saveas = True | 3718 saveas = True |
3736 # Convert the file name to device path separators ('/') and ensure, | 3818 # Convert the file name to device path separators ('/') and ensure, |
3737 # intermediate directories exist (creating them if necessary) | 3819 # intermediate directories exist (creating them if necessary) |
3738 fn = fn.replace("\\", "/") | 3820 fn = fn.replace("\\", "/") |
3739 if "/" in fn: | 3821 if "/" in fn: |
3740 dn = fn.rsplit("/", 1)[0] | 3822 dn = fn.rsplit("/", 1)[0] |
3741 filemanager.makedirs(dn.replace("device:", "")) | 3823 filemanager.makedirs(FileSystemUtilities.plainFileName(dn)) |
3742 success = filemanager.writeFile(fn.replace("device:", ""), self.text()) | 3824 success = filemanager.writeFile( |
3825 FileSystemUtilities.plainFileName(fn), self.text() | |
3826 ) | |
3743 if success: | 3827 if success: |
3744 self.setFileName(fn) | 3828 self.setFileName(fn) |
3745 self.setModified(False) | 3829 self.setModified(False) |
3746 self.resetOnlineChangeTraceInfo() | 3830 self.resetOnlineChangeTraceInfo() |
3747 return success | 3831 return success |
3750 | 3834 |
3751 def handleRenamed(self, fn): | 3835 def handleRenamed(self, fn): |
3752 """ | 3836 """ |
3753 Public method to handle the editorRenamed signal. | 3837 Public method to handle the editorRenamed signal. |
3754 | 3838 |
3755 @param fn filename to be set for the editor (string). | 3839 @param fn filename to be set for the editor |
3840 @type str | |
3756 """ | 3841 """ |
3757 self.__clearBreakpoints(fn) | 3842 self.__clearBreakpoints(fn) |
3758 | 3843 |
3759 self.setFileName(fn) | 3844 self.setFileName(fn) |
3760 self.setWindowTitle(self.fileName) | 3845 self.setWindowTitle(self.fileName) |
3936 | 4021 |
3937 def getWordLeft(self, line, index): | 4022 def getWordLeft(self, line, index): |
3938 """ | 4023 """ |
3939 Public method to get the word to the left of a position. | 4024 Public method to get the word to the left of a position. |
3940 | 4025 |
3941 @param line number of line to look at (int) | 4026 @param line number of line to look at |
3942 @param index position to look at (int) | 4027 @type int |
3943 @return the word to the left of that position (string) | 4028 @param index position to look at |
4029 @type int | |
4030 @return the word to the left of that position | |
4031 @rtype str | |
3944 """ | 4032 """ |
3945 return self.getWord(line, index, 1) | 4033 return self.getWord(line, index, 1) |
3946 | 4034 |
3947 def getWordRight(self, line, index): | 4035 def getWordRight(self, line, index): |
3948 """ | 4036 """ |
3949 Public method to get the word to the right of a position. | 4037 Public method to get the word to the right of a position. |
3950 | 4038 |
3951 @param line number of line to look at (int) | 4039 @param line number of line to look at |
3952 @param index position to look at (int) | 4040 @type int |
3953 @return the word to the right of that position (string) | 4041 @param index position to look at |
4042 @type int | |
4043 @return the word to the right of that position | |
4044 @rtype str | |
3954 """ | 4045 """ |
3955 return self.getWord(line, index, 2) | 4046 return self.getWord(line, index, 2) |
3956 | 4047 |
3957 def getCurrentWord(self): | 4048 def getCurrentWord(self): |
3958 """ | 4049 """ |
3959 Public method to get the word at the current position. | 4050 Public method to get the word at the current position. |
3960 | 4051 |
3961 @return the word at that current position (string) | 4052 @return the word at that current position |
4053 @rtype str | |
3962 """ | 4054 """ |
3963 line, index = self.getCursorPosition() | 4055 line, index = self.getCursorPosition() |
3964 return self.getWord(line, index) | 4056 return self.getWord(line, index) |
3965 | 4057 |
3966 def getCurrentWordBoundaries(self): | 4058 def getCurrentWordBoundaries(self): |
3967 """ | 4059 """ |
3968 Public method to get the word boundaries at the current position. | 4060 Public method to get the word boundaries at the current position. |
3969 | 4061 |
3970 @return tuple with start and end indexes of the current word | 4062 @return tuple with start and end indexes of the current word |
3971 (integer, integer) | 4063 @rtype tuple of (int, int) |
3972 """ | 4064 """ |
3973 line, index = self.getCursorPosition() | 4065 line, index = self.getCursorPosition() |
3974 return self.getWordBoundaries(line, index) | 4066 return self.getWordBoundaries(line, index) |
3975 | 4067 |
3976 def selectWord(self, line, index): | 4068 def selectWord(self, line, index): |
3977 """ | 4069 """ |
3978 Public method to select the word at a position. | 4070 Public method to select the word at a position. |
3979 | 4071 |
3980 @param line number of line to look at (int) | 4072 @param line number of line to look at |
3981 @param index position to look at (int) | 4073 @type int |
4074 @param index position to look at | |
4075 @type int | |
3982 """ | 4076 """ |
3983 start, end = self.getWordBoundaries(line, index) | 4077 start, end = self.getWordBoundaries(line, index) |
3984 self.setSelection(line, start, line, end) | 4078 self.setSelection(line, start, line, end) |
3985 | 4079 |
3986 def selectCurrentWord(self): | 4080 def selectCurrentWord(self): |
3993 def __getCharacter(self, pos): | 4087 def __getCharacter(self, pos): |
3994 """ | 4088 """ |
3995 Private method to get the character to the left of the current position | 4089 Private method to get the character to the left of the current position |
3996 in the current line. | 4090 in the current line. |
3997 | 4091 |
3998 @param pos position to get character at (integer) | 4092 @param pos position to get character at |
3999 @return requested character or "", if there are no more (string) and | 4093 @type int |
4000 the next position (i.e. pos - 1) | 4094 @return requested character or "", if there are no more and the next position |
4095 (i.e. pos - 1) | |
4096 @rtype tuple of (str, int) | |
4001 """ | 4097 """ |
4002 if pos <= 0: | 4098 if pos <= 0: |
4003 return "", pos | 4099 return "", pos |
4004 | 4100 |
4005 pos = self.positionBefore(pos) | 4101 pos = self.positionBefore(pos) |
4015 """ | 4111 """ |
4016 Public method to determine the selection or the current word for the | 4112 Public method to determine the selection or the current word for the |
4017 next search operation. | 4113 next search operation. |
4018 | 4114 |
4019 @param selectionOnly flag indicating that only selected text should be | 4115 @param selectionOnly flag indicating that only selected text should be |
4020 returned (boolean) | 4116 returned |
4021 @return selection or current word (string) | 4117 @type bool |
4118 @return selection or current word | |
4119 @rtype str | |
4022 """ | 4120 """ |
4023 if self.hasSelectedText(): | 4121 if self.hasSelectedText(): |
4024 text = self.selectedText() | 4122 text = self.selectedText() |
4025 if "\r" in text or "\n" in text: | 4123 if "\r" in text or "\n" in text: |
4026 # the selection contains at least a newline, it is | 4124 # the selection contains at least a newline, it is |
4037 | 4135 |
4038 def setSearchIndicator(self, startPos, indicLength): | 4136 def setSearchIndicator(self, startPos, indicLength): |
4039 """ | 4137 """ |
4040 Public method to set a search indicator for the given range. | 4138 Public method to set a search indicator for the given range. |
4041 | 4139 |
4042 @param startPos start position of the indicator (integer) | 4140 @param startPos start position of the indicator |
4043 @param indicLength length of the indicator (integer) | 4141 @type int |
4142 @param indicLength length of the indicator | |
4143 @type int | |
4044 """ | 4144 """ |
4045 self.setIndicatorRange(self.searchIndicator, startPos, indicLength) | 4145 self.setIndicatorRange(self.searchIndicator, startPos, indicLength) |
4046 line = self.lineIndexFromPosition(startPos)[0] | 4146 line = self.lineIndexFromPosition(startPos)[0] |
4047 if line not in self.__searchIndicatorLines: | 4147 if line not in self.__searchIndicatorLines: |
4048 self.__searchIndicatorLines.append(line) | 4148 self.__searchIndicatorLines.append(line) |
4111 | 4211 |
4112 def getSearchIndicatorLines(self): | 4212 def getSearchIndicatorLines(self): |
4113 """ | 4213 """ |
4114 Public method to get the lines containing a search indicator. | 4214 Public method to get the lines containing a search indicator. |
4115 | 4215 |
4116 @return list of lines containing a search indicator (list of integer) | 4216 @return list of lines containing a search indicator |
4217 @rtype list of int | |
4117 """ | 4218 """ |
4118 return self.__searchIndicatorLines[:] | 4219 return self.__searchIndicatorLines[:] |
4119 | 4220 |
4120 def updateMarkerMap(self): | 4221 def updateMarkerMap(self): |
4121 """ | 4222 """ |
4174 def __isCommentedLine(self, line, commentStr): | 4275 def __isCommentedLine(self, line, commentStr): |
4175 """ | 4276 """ |
4176 Private method to check, if the given line is a comment line as | 4277 Private method to check, if the given line is a comment line as |
4177 produced by the configured comment rules. | 4278 produced by the configured comment rules. |
4178 | 4279 |
4179 @param line text of the line to check (string) | 4280 @param line text of the line to check |
4180 @param commentStr comment string to check against (string) | 4281 @type str |
4181 @return flag indicating a commented line (boolean) | 4282 @param commentStr comment string to check against |
4283 @type str | |
4284 @return flag indicating a commented line | |
4285 @rtype bool | |
4182 """ | 4286 """ |
4183 if Preferences.getEditor("CommentColumn0"): | 4287 if Preferences.getEditor("CommentColumn0"): |
4184 return line.startswith(commentStr) | 4288 return line.startswith(commentStr) |
4185 else: | 4289 else: |
4186 return line.strip().startswith(commentStr) | 4290 return line.strip().startswith(commentStr) |
4187 | 4291 |
4188 @pyqtSlot() | 4292 @pyqtSlot() |
4189 def toggleCommentBlock(self): | 4293 def toggleComment(self): |
4190 """ | 4294 """ |
4191 Public slot to toggle the comment of a block. | 4295 Public slot to toggle a block or stream comment. |
4296 | |
4297 If the lexer supports a block comment, that is used for toggling the | |
4298 comment. Otherwise a stream comment is used if that is supported. If | |
4299 none of these are supported, the request is ignored silently. | |
4300 """ | |
4301 if self.lexer_ is not None: | |
4302 if self.lexer_.canBlockComment(): | |
4303 self.__toggleBlockComment() | |
4304 elif self.lexer_.canStreamComment(): | |
4305 self.__toggleStreamComment() | |
4306 | |
4307 @pyqtSlot() | |
4308 def __toggleBlockComment(self): | |
4309 """ | |
4310 Private slot to toggle the comment of a block. | |
4192 | 4311 |
4193 If the editor contains selected text and the start line is not commented, it | 4312 If the editor contains selected text and the start line is not commented, it |
4194 will be commented. Otherwise the selection will be un-commented. In case there | 4313 will be commented. Otherwise the selection will be un-commented. In case there |
4195 is no selected text and the current line is not commented, it will be commented. | 4314 is no selected text and the current line is not commented, it will be commented. |
4196 If is commented, the comment block will be removed. | 4315 If is commented, the comment block will be removed. |
4232 self.setSelection(begline, 0, endline, self.lineLength(endline)) | 4351 self.setSelection(begline, 0, endline, self.lineLength(endline)) |
4233 self.uncommentLineOrSelection() | 4352 self.uncommentLineOrSelection() |
4234 self.setCursorPosition(line, index - len(commentStr)) | 4353 self.setCursorPosition(line, index - len(commentStr)) |
4235 | 4354 |
4236 @pyqtSlot() | 4355 @pyqtSlot() |
4237 def commentLine(self): | 4356 def __commentLine(self): |
4238 """ | 4357 """ |
4239 Public slot to comment the current line. | 4358 Private slot to comment the current line. |
4240 """ | 4359 """ |
4241 if self.lexer_ is None or not self.lexer_.canBlockComment(): | 4360 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
4242 return | 4361 return |
4243 | 4362 |
4244 line, index = self.getCursorPosition() | 4363 line, index = self.getCursorPosition() |
4250 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) | 4369 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) |
4251 self.insertAt(self.lexer_.commentStr(), line, pos) | 4370 self.insertAt(self.lexer_.commentStr(), line, pos) |
4252 self.endUndoAction() | 4371 self.endUndoAction() |
4253 | 4372 |
4254 @pyqtSlot() | 4373 @pyqtSlot() |
4255 def uncommentLine(self): | 4374 def __uncommentLine(self): |
4256 """ | 4375 """ |
4257 Public slot to uncomment the current line. | 4376 Private slot to uncomment the current line. |
4258 """ | 4377 """ |
4259 if self.lexer_ is None or not self.lexer_.canBlockComment(): | 4378 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
4260 return | 4379 return |
4261 | 4380 |
4262 commentStr = self.lexer_.commentStr() | 4381 commentStr = self.lexer_.commentStr() |
4277 self.setSelection(line, pos, line, pos + len(commentStr)) | 4396 self.setSelection(line, pos, line, pos + len(commentStr)) |
4278 self.removeSelectedText() | 4397 self.removeSelectedText() |
4279 self.endUndoAction() | 4398 self.endUndoAction() |
4280 | 4399 |
4281 @pyqtSlot() | 4400 @pyqtSlot() |
4282 def commentSelection(self): | 4401 def __commentSelection(self): |
4283 """ | 4402 """ |
4284 Public slot to comment the current selection. | 4403 Private slot to comment the current selection. |
4285 """ | 4404 """ |
4286 if self.lexer_ is None or not self.lexer_.canBlockComment(): | 4405 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
4287 return | 4406 return |
4288 | 4407 |
4289 if not self.hasSelectedText(): | 4408 if not self.hasSelectedText(): |
4308 # change the selection accordingly | 4427 # change the selection accordingly |
4309 self.setSelection(lineFrom, 0, endLine + 1, 0) | 4428 self.setSelection(lineFrom, 0, endLine + 1, 0) |
4310 self.endUndoAction() | 4429 self.endUndoAction() |
4311 | 4430 |
4312 @pyqtSlot() | 4431 @pyqtSlot() |
4313 def uncommentSelection(self): | 4432 def __uncommentSelection(self): |
4314 """ | 4433 """ |
4315 Public slot to uncomment the current selection. | 4434 Private slot to uncomment the current selection. |
4316 """ | 4435 """ |
4317 if self.lexer_ is None or not self.lexer_.canBlockComment(): | 4436 if self.lexer_ is None or not self.lexer_.canBlockComment(): |
4318 return | 4437 return |
4319 | 4438 |
4320 if not self.hasSelectedText(): | 4439 if not self.hasSelectedText(): |
4360 | 4479 |
4361 @pyqtSlot() | 4480 @pyqtSlot() |
4362 def commentLineOrSelection(self): | 4481 def commentLineOrSelection(self): |
4363 """ | 4482 """ |
4364 Public slot to comment the current line or current selection. | 4483 Public slot to comment the current line or current selection. |
4365 """ | 4484 |
4366 if self.hasSelectedText(): | 4485 If the lexer supports a block comment, that is used for commenting. |
4367 self.commentSelection() | 4486 Otherwise a stream comment is used if that is supported. If none of |
4368 else: | 4487 these are supported, the request is ignored silently. |
4369 self.commentLine() | 4488 """ |
4489 if self.lexer_ is not None: | |
4490 if self.lexer_.canBlockComment(): | |
4491 if self.hasSelectedText(): | |
4492 self.__commentSelection() | |
4493 else: | |
4494 self.__commentLine() | |
4495 elif self.lexer_.canStreamComment(): | |
4496 # delegate to the stream comment method | |
4497 self.streamCommentLineOrSelection() | |
4370 | 4498 |
4371 @pyqtSlot() | 4499 @pyqtSlot() |
4372 def uncommentLineOrSelection(self): | 4500 def uncommentLineOrSelection(self): |
4373 """ | 4501 """ |
4374 Public slot to uncomment the current line or current selection. | 4502 Public slot to uncomment the current line or current selection. |
4375 """ | 4503 |
4376 if self.hasSelectedText(): | 4504 If the lexer supports a block comment, that is used for uncommenting. |
4377 self.uncommentSelection() | 4505 Otherwise a stream comment is used if that is supported. If none of |
4378 else: | 4506 these are supported, the request is ignored silently. |
4379 self.uncommentLine() | 4507 """ |
4380 | 4508 if self.lexer_ is not None: |
4381 @pyqtSlot() | 4509 if self.lexer_.canBlockComment(): |
4382 def streamCommentLine(self): | 4510 if self.hasSelectedText(): |
4383 """ | 4511 self.__uncommentSelection() |
4384 Public slot to stream comment the current line. | 4512 else: |
4513 self.__uncommentLine() | |
4514 elif self.lexer_.canStreamComment(): | |
4515 # delegate to the stream uncomment method | |
4516 self.streamUncommentLineOrSelection() | |
4517 | |
4518 def __isStreamCommentedLine(self, line, streamCommentStr): | |
4519 """ | |
4520 Private method to check, if the line is commented by a stream comment. | |
4521 | |
4522 @param line text of the line to check | |
4523 @type str | |
4524 @param streamCommentStr dictionary containing the stream comment start and | |
4525 end strings | |
4526 @type dict | |
4527 @return flag indicating a stream commented line | |
4528 @rtype bool | |
4529 """ | |
4530 line = line.strip() | |
4531 return line.startswith(streamCommentStr["start"]) and line.endswith( | |
4532 streamCommentStr["end"] | |
4533 ) | |
4534 | |
4535 @pyqtSlot() | |
4536 def __toggleStreamComment(self): | |
4537 """ | |
4538 Private slot to toggle the comment of a block. | |
4539 | |
4540 If the editor contains selected text and the start line is not commented, it | |
4541 will be commented. Otherwise the selection will be un-commented. In case there | |
4542 is no selected text and the current line is not commented, it will be commented. | |
4543 If is commented, the comment block will be removed. | |
4385 """ | 4544 """ |
4386 if self.lexer_ is None or not self.lexer_.canStreamComment(): | 4545 if self.lexer_ is None or not self.lexer_.canStreamComment(): |
4387 return | 4546 return |
4388 | 4547 |
4389 commentStr = self.lexer_.streamCommentStr() | 4548 streamCommentStr = self.lexer_.streamCommentStr() |
4390 line, index = self.getCursorPosition() | 4549 line, index = self.getCursorPosition() |
4391 | 4550 |
4392 self.beginUndoAction() | 4551 if self.hasSelectedText(): |
4393 self.insertAt(commentStr["end"], line, self.lineLength(line)) | 4552 # Check if the selection starts with a stream comment string. |
4394 self.insertAt(commentStr["start"], line, 0) | 4553 if self.text(self.getSelection()[0]).startswith(streamCommentStr["start"]): |
4395 self.endUndoAction() | 4554 self.streamUncommentLineOrSelection() |
4396 | 4555 else: |
4397 @pyqtSlot() | 4556 self.streamCommentLineOrSelection() |
4398 def streamCommentSelection(self): | 4557 elif self.__isStreamCommentedLine(self.text(line), streamCommentStr): |
4399 """ | 4558 # It is a stream commented line. |
4400 Public slot to comment the current selection. | 4559 self.streamUncommentLineOrSelection() |
4560 elif self.text(line).lstrip(" \t").startswith(streamCommentStr["start"]): | |
4561 # The cursor is at the first line of a stream comment. | |
4562 pos = len(self.text(line).replace(self.text(line).lstrip(" \t"), "")) | |
4563 endline = line | |
4564 lines = self.lines() | |
4565 while endline < lines and not self.text(endline).rstrip().endswith( | |
4566 streamCommentStr["end"] | |
4567 ): | |
4568 endline += 1 | |
4569 | |
4570 # Uncomment the determined block and reset the cursor position | |
4571 self.setSelection(line, pos, endline, self.lineLength(endline)) | |
4572 self.uncommentLineOrSelection() | |
4573 self.setCursorPosition(line, index - len(streamCommentStr["start"])) | |
4574 elif self.text(line).rstrip().endswith(streamCommentStr["end"]): | |
4575 # The cursor is at the last line of a stream comment. | |
4576 begline = line | |
4577 while begline > 0 and not self.text(begline).lstrip(" \t").startswith( | |
4578 streamCommentStr["start"] | |
4579 ): | |
4580 begline -= 1 | |
4581 pos = len(self.text(begline).replace(self.text(begline).lstrip(" \t"), "")) | |
4582 | |
4583 # Uncomment the determined block and reset the cursor position | |
4584 self.setSelection(begline, pos, line, self.lineLength(line)) | |
4585 self.uncommentLineOrSelection() | |
4586 self.setCursorPosition( | |
4587 line, min(index, self.lineLength(line) - len(self.getLineSeparator())) | |
4588 ) | |
4589 else: | |
4590 # No selected text and the current line does not start with a stream comment | |
4591 # string, so comment the line. | |
4592 self.streamCommentLineOrSelection() | |
4593 | |
4594 @pyqtSlot() | |
4595 def __streamCommentLine(self): | |
4596 """ | |
4597 Private slot to stream comment the current line. | |
4401 """ | 4598 """ |
4402 if self.lexer_ is None or not self.lexer_.canStreamComment(): | 4599 if self.lexer_ is None or not self.lexer_.canStreamComment(): |
4403 return | 4600 return |
4404 | 4601 |
4602 streamCommentStr = self.lexer_.streamCommentStr() | |
4603 line, index = self.getCursorPosition() | |
4604 | |
4605 self.beginUndoAction() | |
4606 self.insertAt( | |
4607 streamCommentStr["end"], | |
4608 line, | |
4609 self.lineLength(line) - len(self.getLineSeparator()), | |
4610 ) | |
4611 self.insertAt(streamCommentStr["start"], line, 0) | |
4612 self.endUndoAction() | |
4613 | |
4614 @pyqtSlot() | |
4615 def __streamCommentSelection(self): | |
4616 """ | |
4617 Private slot to comment the current selection. | |
4618 """ | |
4619 if self.lexer_ is None or not self.lexer_.canStreamComment(): | |
4620 return | |
4621 | |
4405 if not self.hasSelectedText(): | 4622 if not self.hasSelectedText(): |
4406 return | 4623 return |
4407 | 4624 |
4408 commentStr = self.lexer_.streamCommentStr() | 4625 streamCommentStr = self.lexer_.streamCommentStr() |
4409 | 4626 |
4410 # get the selection boundaries | 4627 # get the selection boundaries |
4411 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() | 4628 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4412 if indexTo == 0: | 4629 if indexTo == 0: |
4413 endLine = lineTo - 1 | 4630 endLine = lineTo - 1 |
4414 endIndex = self.lineLength(endLine) | 4631 endIndex = self.lineLength(endLine) - len(self.getLineSeparator()) |
4415 else: | 4632 else: |
4416 endLine = lineTo | 4633 endLine = lineTo |
4417 endIndex = indexTo | 4634 endIndex = indexTo |
4418 | 4635 |
4419 self.beginUndoAction() | 4636 self.beginUndoAction() |
4420 self.insertAt(commentStr["end"], endLine, endIndex) | 4637 self.insertAt(streamCommentStr["end"], endLine, endIndex) |
4421 self.insertAt(commentStr["start"], lineFrom, indexFrom) | 4638 self.insertAt(streamCommentStr["start"], lineFrom, indexFrom) |
4422 | 4639 |
4423 # change the selection accordingly | 4640 # change the selection accordingly |
4424 if indexTo > 0: | 4641 if indexTo > 0: |
4425 indexTo += len(commentStr["end"]) | 4642 indexTo += len(streamCommentStr["end"]) |
4426 if lineFrom == endLine: | 4643 if lineFrom == endLine: |
4427 indexTo += len(commentStr["start"]) | 4644 indexTo += len(streamCommentStr["start"]) |
4428 self.setSelection(lineFrom, indexFrom, lineTo, indexTo) | 4645 self.setSelection(lineFrom, indexFrom, lineTo, indexTo) |
4429 self.endUndoAction() | 4646 self.endUndoAction() |
4430 | 4647 |
4431 @pyqtSlot() | 4648 @pyqtSlot() |
4649 def __streamUncommentLine(self): | |
4650 """ | |
4651 Private slot to stream uncomment the current line. | |
4652 """ | |
4653 if self.lexer_ is None or not self.lexer_.canStreamComment(): | |
4654 return | |
4655 | |
4656 streamCommentStr = self.lexer_.streamCommentStr() | |
4657 line, index = self.getCursorPosition() | |
4658 | |
4659 # check if line starts and ends with the stream comment strings | |
4660 if not self.__isStreamCommentedLine(self.text(line), streamCommentStr): | |
4661 return | |
4662 | |
4663 self.beginUndoAction() | |
4664 # 1. remove comment end string | |
4665 self.setSelection( | |
4666 line, | |
4667 self.lineLength(line) | |
4668 - len(self.getLineSeparator()) | |
4669 - len(streamCommentStr["end"]), | |
4670 line, | |
4671 self.lineLength(line) - len(self.getLineSeparator()), | |
4672 ) | |
4673 self.removeSelectedText() | |
4674 | |
4675 # 2. remove comment start string | |
4676 lineText = self.text(line) | |
4677 pos = len(lineText.replace(lineText.lstrip(" \t"), "")) | |
4678 self.setSelection(line, pos, line, pos + len(streamCommentStr["start"])) | |
4679 self.removeSelectedText() | |
4680 self.endUndoAction() | |
4681 | |
4682 @pyqtSlot() | |
4683 def __streamUncommentSelection(self): | |
4684 """ | |
4685 Private slot to stream uncomment the current selection. | |
4686 """ | |
4687 if self.lexer_ is None or not self.lexer_.canStreamComment(): | |
4688 return | |
4689 | |
4690 if not self.hasSelectedText(): | |
4691 return | |
4692 | |
4693 streamCommentStr = self.lexer_.streamCommentStr() | |
4694 | |
4695 # get the selection boundaries | |
4696 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() | |
4697 if indexTo == 0: | |
4698 endLine = lineTo - 1 | |
4699 endIndex = self.lineLength(endLine) - len(self.getLineSeparator()) | |
4700 else: | |
4701 endLine = lineTo | |
4702 endIndex = indexTo | |
4703 | |
4704 self.beginUndoAction() | |
4705 self.setSelection(lineFrom, indexFrom, endLine, endIndex) | |
4706 selTxt = self.selectedText() | |
4707 if selTxt.endswith(streamCommentStr["end"]): | |
4708 self.setSelection( | |
4709 endLine, endIndex - len(streamCommentStr["end"]), endLine, endIndex | |
4710 ) | |
4711 self.removeSelectedText() | |
4712 | |
4713 # modify selection end accordingly | |
4714 if indexTo > 0: | |
4715 indexTo -= len(streamCommentStr["end"]) | |
4716 if selTxt.startswith(streamCommentStr["start"]): | |
4717 self.setSelection( | |
4718 lineFrom, | |
4719 indexFrom, | |
4720 lineFrom, | |
4721 indexFrom + len(streamCommentStr["start"]), | |
4722 ) | |
4723 self.removeSelectedText() | |
4724 | |
4725 # modify selection end accordingly | |
4726 if lineFrom == lineTo and indexTo > 0: | |
4727 indexTo -= len(streamCommentStr["start"]) | |
4728 self.endUndoAction() | |
4729 | |
4730 # now set the new selection | |
4731 self.setSelection(lineFrom, indexFrom, lineTo, indexTo) | |
4732 | |
4733 @pyqtSlot() | |
4432 def streamCommentLineOrSelection(self): | 4734 def streamCommentLineOrSelection(self): |
4433 """ | 4735 """ |
4434 Public slot to stream comment the current line or current selection. | 4736 Public slot to stream comment the current line or current selection. |
4435 """ | 4737 """ |
4436 if self.hasSelectedText(): | 4738 if self.hasSelectedText(): |
4437 self.streamCommentSelection() | 4739 self.__streamCommentSelection() |
4438 else: | 4740 else: |
4439 self.streamCommentLine() | 4741 self.__streamCommentLine() |
4440 | 4742 |
4441 @pyqtSlot() | 4743 @pyqtSlot() |
4442 def boxCommentLine(self): | 4744 def streamUncommentLineOrSelection(self): |
4443 """ | 4745 """ |
4444 Public slot to box comment the current line. | 4746 Public slot to stream uncomment the current line or current selection. |
4747 """ | |
4748 if self.hasSelectedText(): | |
4749 self.__streamUncommentSelection() | |
4750 else: | |
4751 self.__streamUncommentLine() | |
4752 | |
4753 @pyqtSlot() | |
4754 def __boxCommentLine(self): | |
4755 """ | |
4756 Private slot to box comment the current line. | |
4445 """ | 4757 """ |
4446 if self.lexer_ is None or not self.lexer_.canBoxComment(): | 4758 if self.lexer_ is None or not self.lexer_.canBoxComment(): |
4447 return | 4759 return |
4448 | 4760 |
4449 commentStr = self.lexer_.boxCommentStr() | 4761 boxCommentStr = self.lexer_.boxCommentStr() |
4450 line, index = self.getCursorPosition() | 4762 line, index = self.getCursorPosition() |
4451 | 4763 |
4452 eol = self.getLineSeparator() | 4764 eol = self.getLineSeparator() |
4453 self.beginUndoAction() | 4765 self.beginUndoAction() |
4454 self.insertAt(eol, line, self.lineLength(line)) | 4766 self.insertAt(eol, line, self.lineLength(line)) |
4455 self.insertAt(commentStr["end"], line + 1, 0) | 4767 self.insertAt(boxCommentStr["end"], line + 1, 0) |
4456 self.insertAt(commentStr["middle"], line, 0) | 4768 self.insertAt(boxCommentStr["middle"], line, 0) |
4457 self.insertAt(eol, line, 0) | 4769 self.insertAt(eol, line, 0) |
4458 self.insertAt(commentStr["start"], line, 0) | 4770 self.insertAt(boxCommentStr["start"], line, 0) |
4459 self.endUndoAction() | 4771 self.endUndoAction() |
4460 | 4772 |
4461 @pyqtSlot() | 4773 @pyqtSlot() |
4462 def boxCommentSelection(self): | 4774 def __boxCommentSelection(self): |
4463 """ | 4775 """ |
4464 Public slot to box comment the current selection. | 4776 Private slot to box comment the current selection. |
4465 """ | 4777 """ |
4466 if self.lexer_ is None or not self.lexer_.canBoxComment(): | 4778 if self.lexer_ is None or not self.lexer_.canBoxComment(): |
4467 return | 4779 return |
4468 | 4780 |
4469 if not self.hasSelectedText(): | 4781 if not self.hasSelectedText(): |
4470 return | 4782 return |
4471 | 4783 |
4472 commentStr = self.lexer_.boxCommentStr() | 4784 boxCommentStr = self.lexer_.boxCommentStr() |
4473 | 4785 |
4474 # get the selection boundaries | 4786 # get the selection boundaries |
4475 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() | 4787 lineFrom, indexFrom, lineTo, indexTo = self.getSelection() |
4476 endLine = lineTo if indexTo else lineTo - 1 | 4788 endLine = lineTo if indexTo else lineTo - 1 |
4477 | 4789 |
4478 self.beginUndoAction() | 4790 self.beginUndoAction() |
4479 # iterate over the lines | 4791 # iterate over the lines |
4480 for line in range(lineFrom, endLine + 1): | 4792 for line in range(lineFrom, endLine + 1): |
4481 self.insertAt(commentStr["middle"], line, 0) | 4793 self.insertAt(boxCommentStr["middle"], line, 0) |
4482 | 4794 |
4483 # now do the comments before and after the selection | 4795 # now do the comments before and after the selection |
4484 eol = self.getLineSeparator() | 4796 eol = self.getLineSeparator() |
4485 self.insertAt(eol, endLine, self.lineLength(endLine)) | 4797 self.insertAt(eol, endLine, self.lineLength(endLine)) |
4486 self.insertAt(commentStr["end"], endLine + 1, 0) | 4798 self.insertAt(boxCommentStr["end"], endLine + 1, 0) |
4487 self.insertAt(eol, lineFrom, 0) | 4799 self.insertAt(eol, lineFrom, 0) |
4488 self.insertAt(commentStr["start"], lineFrom, 0) | 4800 self.insertAt(boxCommentStr["start"], lineFrom, 0) |
4489 | 4801 |
4490 # change the selection accordingly | 4802 # change the selection accordingly |
4491 self.setSelection(lineFrom, 0, endLine + 3, 0) | 4803 self.setSelection(lineFrom, 0, endLine + 3, 0) |
4492 self.endUndoAction() | 4804 self.endUndoAction() |
4493 | 4805 |
4495 def boxCommentLineOrSelection(self): | 4807 def boxCommentLineOrSelection(self): |
4496 """ | 4808 """ |
4497 Public slot to box comment the current line or current selection. | 4809 Public slot to box comment the current line or current selection. |
4498 """ | 4810 """ |
4499 if self.hasSelectedText(): | 4811 if self.hasSelectedText(): |
4500 self.boxCommentSelection() | 4812 self.__boxCommentSelection() |
4501 else: | 4813 else: |
4502 self.boxCommentLine() | 4814 self.__boxCommentLine() |
4503 | 4815 |
4504 ########################################################################### | 4816 ########################################################################### |
4505 ## Indentation handling methods below | 4817 ## Indentation handling methods below |
4506 ########################################################################### | 4818 ########################################################################### |
4507 | 4819 |
4508 def __indentLine(self, indent=True): | 4820 def __indentLine(self, indent=True): |
4509 """ | 4821 """ |
4510 Private method to indent or unindent the current line. | 4822 Private method to indent or unindent the current line. |
4511 | 4823 |
4512 @param indent flag indicating an indent operation (boolean) | 4824 @param indent flag indicating an indent operation |
4513 <br />If the flag is true, an indent operation is performed. | 4825 <br />If the flag is true, an indent operation is performed. |
4514 Otherwise the current line is unindented. | 4826 Otherwise the current line is unindented. |
4827 @type bool | |
4515 """ | 4828 """ |
4516 line, index = self.getCursorPosition() | 4829 line, index = self.getCursorPosition() |
4517 self.beginUndoAction() | 4830 self.beginUndoAction() |
4518 if indent: | 4831 if indent: |
4519 self.indent(line) | 4832 self.indent(line) |
4527 | 4840 |
4528 def __indentSelection(self, indent=True): | 4841 def __indentSelection(self, indent=True): |
4529 """ | 4842 """ |
4530 Private method to indent or unindent the current selection. | 4843 Private method to indent or unindent the current selection. |
4531 | 4844 |
4532 @param indent flag indicating an indent operation (boolean) | 4845 @param indent flag indicating an indent operation |
4533 <br />If the flag is true, an indent operation is performed. | 4846 <br />If the flag is true, an indent operation is performed. |
4534 Otherwise the current line is unindented. | 4847 Otherwise the current line is unindented. |
4848 @type bool | |
4535 """ | 4849 """ |
4536 if not self.hasSelectedText(): | 4850 if not self.hasSelectedText(): |
4537 return | 4851 return |
4538 | 4852 |
4539 # get the selection | 4853 # get the selection |
4646 | 4960 |
4647 def isLastEditPositionAvailable(self): | 4961 def isLastEditPositionAvailable(self): |
4648 """ | 4962 """ |
4649 Public method to check, if a last edit position is available. | 4963 Public method to check, if a last edit position is available. |
4650 | 4964 |
4651 @return flag indicating availability (boolean) | 4965 @return flag indicating availability |
4966 @rtype bool | |
4652 """ | 4967 """ |
4653 return self.__lastEditPosition is not None | 4968 return self.__lastEditPosition is not None |
4654 | 4969 |
4655 def gotoLastEditPosition(self): | 4970 def gotoLastEditPosition(self): |
4656 """ | 4971 """ |
4661 | 4976 |
4662 def gotoMethodClass(self, goUp=False): | 4977 def gotoMethodClass(self, goUp=False): |
4663 """ | 4978 """ |
4664 Public method to go to the next Python method or class definition. | 4979 Public method to go to the next Python method or class definition. |
4665 | 4980 |
4666 @param goUp flag indicating the move direction (boolean) | 4981 @param goUp flag indicating the move direction |
4982 @type bool | |
4667 """ | 4983 """ |
4668 if self.isPyFile() or self.isRubyFile(): | 4984 if self.isPyFile() or self.isRubyFile(): |
4669 lineNo = self.getCursorPosition()[0] | 4985 lineNo = self.getCursorPosition()[0] |
4670 line = self.text(lineNo) | 4986 line = self.text(lineNo) |
4671 if line.strip().startswith(("class ", "def ", "module ")): | 4987 if line.strip().startswith(("class ", "def ", "module ")): |
5210 | 5526 |
5211 def canAutoCompleteFromAPIs(self): | 5527 def canAutoCompleteFromAPIs(self): |
5212 """ | 5528 """ |
5213 Public method to check for API availablity. | 5529 Public method to check for API availablity. |
5214 | 5530 |
5215 @return flag indicating autocompletion from APIs is available (boolean) | 5531 @return flag indicating autocompletion from APIs is available |
5532 @rtype bool | |
5216 """ | 5533 """ |
5217 return self.acAPI | 5534 return self.acAPI |
5218 | 5535 |
5219 def autoCompleteQScintilla(self): | 5536 def autoCompleteQScintilla(self): |
5220 """ | 5537 """ |
5245 def setAutoCompletionEnabled(self, enable): | 5562 def setAutoCompletionEnabled(self, enable): |
5246 """ | 5563 """ |
5247 Public method to enable/disable autocompletion. | 5564 Public method to enable/disable autocompletion. |
5248 | 5565 |
5249 @param enable flag indicating the desired autocompletion status | 5566 @param enable flag indicating the desired autocompletion status |
5250 (boolean) | 5567 @type bool |
5251 """ | 5568 """ |
5252 if enable: | 5569 if enable: |
5253 autoCompletionSource = Preferences.getEditor("AutoCompletionSource") | 5570 autoCompletionSource = Preferences.getEditor("AutoCompletionSource") |
5254 if autoCompletionSource == QsciScintilla.AutoCompletionSource.AcsDocument: | 5571 if autoCompletionSource == QsciScintilla.AutoCompletionSource.AcsDocument: |
5255 self.setAutoCompletionSource( | 5572 self.setAutoCompletionSource( |
5321 def __isStartChar(self, ch): | 5638 def __isStartChar(self, ch): |
5322 """ | 5639 """ |
5323 Private method to check, if a character is an autocompletion start | 5640 Private method to check, if a character is an autocompletion start |
5324 character. | 5641 character. |
5325 | 5642 |
5326 @param ch character to be checked (one character string) | 5643 @param ch character to be checked |
5327 @return flag indicating the result (boolean) | 5644 @type str |
5645 @return flag indicating the result | |
5646 @rtype bool | |
5328 """ | 5647 """ |
5329 if self.lexer_ is None: | 5648 if self.lexer_ is None: |
5330 return False | 5649 return False |
5331 | 5650 |
5332 wseps = self.lexer_.autoCompletionWordSeparators() | 5651 wseps = self.lexer_.autoCompletionWordSeparators() |
5410 def autoComplete(self, auto=False, context=True): | 5729 def autoComplete(self, auto=False, context=True): |
5411 """ | 5730 """ |
5412 Public method to start auto-completion. | 5731 Public method to start auto-completion. |
5413 | 5732 |
5414 @param auto flag indicating a call from the __charAdded method | 5733 @param auto flag indicating a call from the __charAdded method |
5415 (boolean) | 5734 @type bool |
5416 @param context flag indicating to complete a context (boolean) | 5735 @param context flag indicating to complete a context |
5736 @type bool | |
5417 """ | 5737 """ |
5418 if auto and not Preferences.getEditor("AutoCompletionEnabled"): | 5738 if auto and not Preferences.getEditor("AutoCompletionEnabled"): |
5419 # auto-completion is disabled | 5739 # auto-completion is disabled |
5420 return | 5740 return |
5421 | 5741 |
5462 def __autoComplete(self, auto=True, context=None): | 5782 def __autoComplete(self, auto=True, context=None): |
5463 """ | 5783 """ |
5464 Private method to start auto-completion via plug-ins. | 5784 Private method to start auto-completion via plug-ins. |
5465 | 5785 |
5466 @param auto flag indicating a call from the __charAdded method | 5786 @param auto flag indicating a call from the __charAdded method |
5467 (boolean) | 5787 @type bool |
5468 @param context flag indicating to complete a context | 5788 @param context flag indicating to complete a context |
5469 @type bool or None | 5789 @type bool or None |
5470 """ | 5790 """ |
5471 self.__acCompletions.clear() | 5791 self.__acCompletions.clear() |
5472 self.__acCompletionsFinished = 0 | 5792 self.__acCompletionsFinished = 0 |
5633 def canProvideDynamicAutoCompletion(self): | 5953 def canProvideDynamicAutoCompletion(self): |
5634 """ | 5954 """ |
5635 Public method to test the dynamic auto-completion availability. | 5955 Public method to test the dynamic auto-completion availability. |
5636 | 5956 |
5637 @return flag indicating the availability of dynamic auto-completion | 5957 @return flag indicating the availability of dynamic auto-completion |
5638 (boolean) | 5958 @rtype bool |
5639 """ | 5959 """ |
5640 return ( | 5960 return ( |
5641 self.acAPI | 5961 self.acAPI |
5642 or bool(self.__completionListHookFunctions) | 5962 or bool(self.__completionListHookFunctions) |
5643 or bool(self.__completionListAsyncHookFunctions) | 5963 or bool(self.__completionListAsyncHookFunctions) |
5700 | 6020 |
5701 def canProvideCallTipps(self): | 6021 def canProvideCallTipps(self): |
5702 """ | 6022 """ |
5703 Public method to test the calltips availability. | 6023 Public method to test the calltips availability. |
5704 | 6024 |
5705 @return flag indicating the availability of calltips (boolean) | 6025 @return flag indicating the availability of calltips |
6026 @rtype bool | |
5706 """ | 6027 """ |
5707 return self.acAPI or bool(self.__ctHookFunctions) | 6028 return self.acAPI or bool(self.__ctHookFunctions) |
5708 | 6029 |
5709 def callTip(self): | 6030 def callTip(self): |
5710 """ | 6031 """ |
5826 | 6147 |
5827 def __adjustedCallTipPosition(self, ctshift, pos): | 6148 def __adjustedCallTipPosition(self, ctshift, pos): |
5828 """ | 6149 """ |
5829 Private method to calculate an adjusted position for showing calltips. | 6150 Private method to calculate an adjusted position for showing calltips. |
5830 | 6151 |
5831 @param ctshift amount the calltip shall be shifted (integer) | 6152 @param ctshift amount the calltip shall be shifted |
5832 @param pos position into the text (integer) | 6153 @type int |
5833 @return new position for the calltip (integer) | 6154 @param pos position into the text |
6155 @type int | |
6156 @return new position for the calltip | |
6157 @rtype int | |
5834 """ | 6158 """ |
5835 ct = pos | 6159 ct = pos |
5836 if ctshift: | 6160 if ctshift: |
5837 ctmin = self.SendScintilla( | 6161 ctmin = self.SendScintilla( |
5838 QsciScintilla.SCI_POSITIONFROMLINE, | 6162 QsciScintilla.SCI_POSITIONFROMLINE, |
5861 | 6185 |
5862 def __marginNumber(self, xPos): | 6186 def __marginNumber(self, xPos): |
5863 """ | 6187 """ |
5864 Private method to calculate the margin number based on a x position. | 6188 Private method to calculate the margin number based on a x position. |
5865 | 6189 |
5866 @param xPos x position (integer) | 6190 @param xPos x position |
6191 @type int | |
5867 @return margin number (integer, -1 for no margin) | 6192 @return margin number (integer, -1 for no margin) |
6193 @rtype int | |
5868 """ | 6194 """ |
5869 width = 0 | 6195 width = 0 |
5870 for margin in range(5): | 6196 for margin in range(5): |
5871 width += self.marginWidth(margin) | 6197 width += self.marginWidth(margin) |
5872 if xPos <= width: | 6198 if xPos <= width: |
6164 def __reopenWithEncodingMenuTriggered(self, act): | 6490 def __reopenWithEncodingMenuTriggered(self, act): |
6165 """ | 6491 """ |
6166 Private method to handle the rereading of the file with a selected | 6492 Private method to handle the rereading of the file with a selected |
6167 encoding. | 6493 encoding. |
6168 | 6494 |
6169 @param act reference to the action that was triggered (QAction) | 6495 @param act reference to the action that was triggered |
6496 @type QAction | |
6170 """ | 6497 """ |
6171 encoding = act.data() | 6498 encoding = act.data() |
6172 self.readFile(self.fileName, encoding=encoding) | 6499 self.readFile(self.fileName, encoding=encoding) |
6173 self.__convertTabs() | 6500 self.__convertTabs() |
6174 self.__checkEncoding() | 6501 self.__checkEncoding() |
6303 | 6630 |
6304 def __shouldAutosave(self): | 6631 def __shouldAutosave(self): |
6305 """ | 6632 """ |
6306 Private method to check the autosave flags. | 6633 Private method to check the autosave flags. |
6307 | 6634 |
6308 @return flag indicating this editor should be saved (boolean) | 6635 @return flag indicating this editor should be saved |
6636 @rtype bool | |
6309 """ | 6637 """ |
6310 return ( | 6638 return ( |
6311 bool(self.fileName) | 6639 bool(self.fileName) |
6312 and not self.__autosaveManuallyDisabled | 6640 and not self.__autosaveManuallyDisabled |
6313 and not self.isReadOnly() | 6641 and not self.isReadOnly() |
6583 | 6911 |
6584 def getCoverageLines(self): | 6912 def getCoverageLines(self): |
6585 """ | 6913 """ |
6586 Public method to get the lines containing a coverage marker. | 6914 Public method to get the lines containing a coverage marker. |
6587 | 6915 |
6588 @return list of lines containing a coverage marker (list of integer) | 6916 @return list of lines containing a coverage marker |
6917 @rtype list of int | |
6589 """ | 6918 """ |
6590 lines = [] | 6919 lines = [] |
6591 line = -1 | 6920 line = -1 |
6592 while True: | 6921 while True: |
6593 line = self.markerFindNext(line + 1, 1 << self.notcovered) | 6922 line = self.markerFindNext(line + 1, 1 << self.notcovered) |
6599 | 6928 |
6600 def hasCoverageMarkers(self): | 6929 def hasCoverageMarkers(self): |
6601 """ | 6930 """ |
6602 Public method to test, if there are coverage markers. | 6931 Public method to test, if there are coverage markers. |
6603 | 6932 |
6604 @return flag indicating the presence of coverage markers (boolean) | 6933 @return flag indicating the presence of coverage markers |
6934 @rtype bool | |
6605 """ | 6935 """ |
6606 return len(self.notcoveredMarkers) > 0 | 6936 return len(self.notcoveredMarkers) > 0 |
6607 | 6937 |
6608 @pyqtSlot() | 6938 @pyqtSlot() |
6609 def nextUncovered(self): | 6939 def nextUncovered(self): |
6750 self.syntaxerrors[handle].append((msg, index)) | 7080 self.syntaxerrors[handle].append((msg, index)) |
6751 if show: | 7081 if show: |
6752 self.setCursorPosition(line - 1, index) | 7082 self.setCursorPosition(line - 1, index) |
6753 self.ensureLineVisible(line - 1) | 7083 self.ensureLineVisible(line - 1) |
6754 else: | 7084 else: |
6755 for handle in list(self.syntaxerrors.keys()): | 7085 for handle in list(self.syntaxerrors): |
6756 if self.markerLine(handle) == line - 1: | 7086 if self.markerLine(handle) == line - 1: |
6757 del self.syntaxerrors[handle] | 7087 del self.syntaxerrors[handle] |
6758 self.markerDeleteHandle(handle) | 7088 self.markerDeleteHandle(handle) |
6759 self.syntaxerrorToggled.emit(self) | 7089 self.syntaxerrorToggled.emit(self) |
6760 | 7090 |
6763 | 7093 |
6764 def getSyntaxErrorLines(self): | 7094 def getSyntaxErrorLines(self): |
6765 """ | 7095 """ |
6766 Public method to get the lines containing a syntax error. | 7096 Public method to get the lines containing a syntax error. |
6767 | 7097 |
6768 @return list of lines containing a syntax error (list of integer) | 7098 @return list of lines containing a syntax error |
7099 @rtype list of int | |
6769 """ | 7100 """ |
6770 lines = [] | 7101 lines = [] |
6771 line = -1 | 7102 line = -1 |
6772 while True: | 7103 while True: |
6773 line = self.markerFindNext(line + 1, 1 << self.syntaxerror) | 7104 line = self.markerFindNext(line + 1, 1 << self.syntaxerror) |
6779 | 7110 |
6780 def hasSyntaxErrors(self): | 7111 def hasSyntaxErrors(self): |
6781 """ | 7112 """ |
6782 Public method to check for the presence of syntax errors. | 7113 Public method to check for the presence of syntax errors. |
6783 | 7114 |
6784 @return flag indicating the presence of syntax errors (boolean) | 7115 @return flag indicating the presence of syntax errors |
7116 @rtype bool | |
6785 """ | 7117 """ |
6786 return len(self.syntaxerrors) > 0 | 7118 return len(self.syntaxerrors) > 0 |
6787 | 7119 |
6788 @pyqtSlot() | 7120 @pyqtSlot() |
6789 def gotoSyntaxError(self): | 7121 def gotoSyntaxError(self): |
6802 @pyqtSlot() | 7134 @pyqtSlot() |
6803 def clearSyntaxError(self): | 7135 def clearSyntaxError(self): |
6804 """ | 7136 """ |
6805 Public slot to handle the 'Clear all syntax error' context menu action. | 7137 Public slot to handle the 'Clear all syntax error' context menu action. |
6806 """ | 7138 """ |
6807 for handle in list(self.syntaxerrors.keys()): | 7139 for handle in list(self.syntaxerrors): |
6808 line = self.markerLine(handle) + 1 | 7140 line = self.markerLine(handle) + 1 |
6809 self.toggleSyntaxError(line, 0, False) | 7141 self.toggleSyntaxError(line, 0, False) |
6810 | 7142 |
6811 self.syntaxerrors.clear() | 7143 self.syntaxerrors.clear() |
6812 self.syntaxerrorToggled.emit(self) | 7144 self.syntaxerrorToggled.emit(self) |
6907 self.markerLine(handle) == line - 1 | 7239 self.markerLine(handle) == line - 1 |
6908 and warn not in self._warnings[handle] | 7240 and warn not in self._warnings[handle] |
6909 ): | 7241 ): |
6910 self._warnings[handle].append(warn) | 7242 self._warnings[handle].append(warn) |
6911 else: | 7243 else: |
6912 for handle in list(self._warnings.keys()): | 7244 for handle in list(self._warnings): |
6913 if self.markerLine(handle) == line - 1: | 7245 if self.markerLine(handle) == line - 1: |
6914 del self._warnings[handle] | 7246 del self._warnings[handle] |
6915 self.markerDeleteHandle(handle) | 7247 self.markerDeleteHandle(handle) |
6916 self.syntaxerrorToggled.emit(self) | 7248 self.syntaxerrorToggled.emit(self) |
6917 # signal is also used for warnings | 7249 # signal is also used for warnings |
6921 | 7253 |
6922 def getWarningLines(self): | 7254 def getWarningLines(self): |
6923 """ | 7255 """ |
6924 Public method to get the lines containing a warning. | 7256 Public method to get the lines containing a warning. |
6925 | 7257 |
6926 @return list of lines containing a warning (list of integer) | 7258 @return list of lines containing a warning |
7259 @rtype list of int | |
6927 """ | 7260 """ |
6928 lines = [] | 7261 lines = [] |
6929 line = -1 | 7262 line = -1 |
6930 while True: | 7263 while True: |
6931 line = self.markerFindNext(line + 1, 1 << self.warning) | 7264 line = self.markerFindNext(line + 1, 1 << self.warning) |
6937 | 7270 |
6938 def hasWarnings(self): | 7271 def hasWarnings(self): |
6939 """ | 7272 """ |
6940 Public method to check for the presence of warnings. | 7273 Public method to check for the presence of warnings. |
6941 | 7274 |
6942 @return flag indicating the presence of warnings (boolean) | 7275 @return flag indicating the presence of warnings |
7276 @rtype bool | |
6943 """ | 7277 """ |
6944 return len(self._warnings) > 0 | 7278 return len(self._warnings) > 0 |
6945 | 7279 |
6946 @pyqtSlot() | 7280 @pyqtSlot() |
6947 def nextWarning(self): | 7281 def nextWarning(self): |
7019 """ | 7353 """ |
7020 Private method to clear warnings of a specific kind. | 7354 Private method to clear warnings of a specific kind. |
7021 | 7355 |
7022 @param warningKind kind of warning to clear (Editor.WarningCode, | 7356 @param warningKind kind of warning to clear (Editor.WarningCode, |
7023 Editor.WarningPython, Editor.WarningStyle) | 7357 Editor.WarningPython, Editor.WarningStyle) |
7024 """ | 7358 @type int |
7025 for handle in list(self._warnings.keys()): | 7359 """ |
7360 for handle in list(self._warnings): | |
7026 issues = [] | 7361 issues = [] |
7027 for msg, warningType in self._warnings[handle]: | 7362 for msg, warningType in self._warnings[handle]: |
7028 if warningType == warningKind: | 7363 if warningType == warningKind: |
7029 continue | 7364 continue |
7030 | 7365 |
7137 | 7472 |
7138 def __setAnnotation(self, line): | 7473 def __setAnnotation(self, line): |
7139 """ | 7474 """ |
7140 Private method to set the annotations for the given line. | 7475 Private method to set the annotations for the given line. |
7141 | 7476 |
7142 @param line number of the line that needs annotation (integer) | 7477 @param line number of the line that needs annotation |
7478 @type int | |
7143 """ | 7479 """ |
7144 if hasattr(QsciScintilla, "annotate"): | 7480 if hasattr(QsciScintilla, "annotate"): |
7145 warningAnnotations = [] | 7481 warningAnnotations = [] |
7146 errorAnnotations = [] | 7482 errorAnnotations = [] |
7147 styleAnnotations = [] | 7483 styleAnnotations = [] |
7281 def __getMacroName(self): | 7617 def __getMacroName(self): |
7282 """ | 7618 """ |
7283 Private method to select a macro name from the list of macros. | 7619 Private method to select a macro name from the list of macros. |
7284 | 7620 |
7285 @return Tuple of macro name and a flag, indicating, if the user | 7621 @return Tuple of macro name and a flag, indicating, if the user |
7286 pressed ok or canceled the operation. (string, boolean) | 7622 pressed ok or canceled the operation. |
7623 @rtype tuple of (str, bool) | |
7287 """ | 7624 """ |
7288 qs = [] | 7625 qs = [] |
7289 for s in self.macros: | 7626 for s in self.macros: |
7290 qs.append(s) | 7627 qs.append(s) |
7291 qs.sort() | 7628 qs.sort() |
7471 | 7808 |
7472 This overwritten method redirects the action to our | 7809 This overwritten method redirects the action to our |
7473 ViewManager.closeEditor, which in turn calls our closeIt | 7810 ViewManager.closeEditor, which in turn calls our closeIt |
7474 method. | 7811 method. |
7475 | 7812 |
7476 @return flag indicating a successful close of the editor (boolean) | 7813 @return flag indicating a successful close of the editor |
7814 @rtype bool | |
7477 """ | 7815 """ |
7478 return self.vm.closeEditor(self) | 7816 return self.vm.closeEditor(self) |
7479 | 7817 |
7480 def closeIt(self): | 7818 def closeIt(self): |
7481 """ | 7819 """ |
7565 | 7903 |
7566 @param event the event object | 7904 @param event the event object |
7567 @type QFocusEvent | 7905 @type QFocusEvent |
7568 """ | 7906 """ |
7569 self.recolor() | 7907 self.recolor() |
7908 | |
7570 self.vm.editActGrp.setEnabled(True) | 7909 self.vm.editActGrp.setEnabled(True) |
7571 self.vm.editorActGrp.setEnabled(True) | 7910 self.vm.editorActGrp.setEnabled(True) |
7572 self.vm.copyActGrp.setEnabled(True) | 7911 self.vm.copyActGrp.setEnabled(True) |
7573 self.vm.viewActGrp.setEnabled(True) | 7912 self.vm.viewActGrp.setEnabled(True) |
7574 self.vm.searchActGrp.setEnabled(True) | 7913 self.vm.searchActGrp.setEnabled(True) |
7914 | |
7575 with contextlib.suppress(AttributeError): | 7915 with contextlib.suppress(AttributeError): |
7576 self.setCaretWidth(self.caretWidth) | 7916 self.setCaretWidth(self.caretWidth) |
7577 self.__updateReadOnly(False) | 7917 self.__updateReadOnly(False) |
7918 self.setCursorFlashTime(QApplication.cursorFlashTime()) | |
7919 | |
7920 super().focusInEvent(event) | |
7921 | |
7922 def focusOutEvent(self, event): | |
7923 """ | |
7924 Protected method called when the editor loses focus. | |
7925 | |
7926 @param event the event object | |
7927 @type QFocusEvent | |
7928 """ | |
7929 if Preferences.getEditor("AutosaveOnFocusLost") and self.__shouldAutosave(): | |
7930 self.saveFile() | |
7931 | |
7932 self.vm.editorActGrp.setEnabled(False) | |
7933 self.setCaretWidth(0) | |
7934 | |
7935 self.cancelCallTips() | |
7936 | |
7937 super().focusOutEvent(event) | |
7938 | |
7939 def changeEvent(self, evt): | |
7940 """ | |
7941 Protected method called to process an event. | |
7942 | |
7943 This implements special handling for the events showMaximized, | |
7944 showMinimized and showNormal. The windows caption is shortened | |
7945 for the minimized mode and reset to the full filename for the | |
7946 other modes. This is to make the editor windows work nicer | |
7947 with the QWorkspace. | |
7948 | |
7949 @param evt the event, that was generated | |
7950 @type QEvent | |
7951 """ | |
7952 if evt.type() == QEvent.Type.WindowStateChange and bool(self.fileName): | |
7953 cap = ( | |
7954 os.path.basename(self.fileName) | |
7955 if self.windowState() == Qt.WindowState.WindowMinimized | |
7956 else self.fileName | |
7957 ) | |
7958 if self.checkReadOnly(): | |
7959 cap = self.tr("{0} (ro)").format(cap) | |
7960 self.setWindowTitle(cap) | |
7961 | |
7962 super().changeEvent(evt) | |
7963 | |
7964 def mousePressEvent(self, event): | |
7965 """ | |
7966 Protected method to handle the mouse press event. | |
7967 | |
7968 @param event the mouse press event | |
7969 @type QMouseEvent | |
7970 """ | |
7971 if event.button() == Qt.MouseButton.XButton1: | |
7972 self.undo() | |
7973 event.accept() | |
7974 elif event.button() == Qt.MouseButton.XButton2: | |
7975 self.redo() | |
7976 event.accept() | |
7977 elif event.button() == Qt.MouseButton.LeftButton and bool( | |
7978 event.modifiers() | |
7979 & (Qt.KeyboardModifier.MetaModifier | Qt.KeyboardModifier.AltModifier) | |
7980 ): | |
7981 line, index = self.lineIndexFromPoint(event.position().toPoint()) | |
7982 self.addCursor(line, index) | |
7983 event.accept() | |
7984 else: | |
7985 self.vm.eventFilter(self, event) | |
7986 super().mousePressEvent(event) | |
7987 | |
7988 def mouseDoubleClickEvent(self, evt): | |
7989 """ | |
7990 Protected method to handle mouse double click events. | |
7991 | |
7992 @param evt reference to the mouse event | |
7993 @type QMouseEvent | |
7994 """ | |
7995 super().mouseDoubleClickEvent(evt) | |
7996 | |
7997 # accept all double click events even if not handled by QScintilla | |
7998 evt.accept() | |
7999 | |
8000 self.mouseDoubleClick.emit(evt.position().toPoint(), evt.buttons()) | |
8001 | |
8002 def wheelEvent(self, evt): | |
8003 """ | |
8004 Protected method to handle wheel events. | |
8005 | |
8006 @param evt reference to the wheel event | |
8007 @type QWheelEvent | |
8008 """ | |
8009 delta = evt.angleDelta().y() | |
8010 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: | |
8011 if delta < 0: | |
8012 self.zoomOut() | |
8013 elif delta > 0: | |
8014 self.zoomIn() | |
8015 evt.accept() | |
8016 return | |
8017 | |
8018 if evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: | |
8019 if delta < 0: | |
8020 self.gotoMethodClass(False) | |
8021 elif delta > 0: | |
8022 self.gotoMethodClass(True) | |
8023 evt.accept() | |
8024 return | |
8025 | |
8026 super().wheelEvent(evt) | |
8027 | |
8028 def event(self, evt): | |
8029 """ | |
8030 Public method handling events. | |
8031 | |
8032 @param evt reference to the event | |
8033 @type QEvent | |
8034 @return flag indicating, if the event was handled | |
8035 @rtype bool | |
8036 """ | |
8037 if evt.type() == QEvent.Type.Gesture: | |
8038 self.gestureEvent(evt) | |
8039 return True | |
8040 | |
8041 return super().event(evt) | |
8042 | |
8043 def gestureEvent(self, evt): | |
8044 """ | |
8045 Protected method handling gesture events. | |
8046 | |
8047 @param evt reference to the gesture event | |
8048 @type QGestureEvent | |
8049 """ | |
8050 pinch = evt.gesture(Qt.GestureType.PinchGesture) | |
8051 if pinch: | |
8052 if pinch.state() == Qt.GestureState.GestureStarted: | |
8053 zoom = (self.getZoom() + 10) / 10.0 | |
8054 pinch.setTotalScaleFactor(zoom) | |
8055 elif pinch.state() == Qt.GestureState.GestureUpdated: | |
8056 zoom = int(pinch.totalScaleFactor() * 10) - 10 | |
8057 if zoom <= -9: | |
8058 zoom = -9 | |
8059 pinch.setTotalScaleFactor(0.1) | |
8060 elif zoom >= 20: | |
8061 zoom = 20 | |
8062 pinch.setTotalScaleFactor(3.0) | |
8063 self.zoomTo(zoom) | |
8064 evt.accept() | |
8065 | |
8066 def resizeEvent(self, evt): | |
8067 """ | |
8068 Protected method handling resize events. | |
8069 | |
8070 @param evt reference to the resize event | |
8071 @type QResizeEvent | |
8072 """ | |
8073 super().resizeEvent(evt) | |
8074 self.__markerMap.calculateGeometry() | |
8075 | |
8076 def viewportEvent(self, evt): | |
8077 """ | |
8078 Protected method handling event of the viewport. | |
8079 | |
8080 @param evt reference to the event | |
8081 @type QEvent | |
8082 @return flag indiating that the event was handled | |
8083 @rtype bool | |
8084 """ | |
8085 with contextlib.suppress(AttributeError): | |
8086 self.__markerMap.calculateGeometry() | |
8087 return super().viewportEvent(evt) | |
8088 | |
8089 def __updateReadOnly(self, bForce=True): | |
8090 """ | |
8091 Private method to update the readOnly information for this editor. | |
8092 | |
8093 If bForce is True, then updates everything regardless if | |
8094 the attributes have actually changed, such as during | |
8095 initialization time. A signal is emitted after the | |
8096 caption change. | |
8097 | |
8098 @param bForce True to force change, False to only update and emit | |
8099 signal if there was an attribute change. | |
8100 @type bool | |
8101 """ | |
8102 if self.fileName == "" or not FileSystemUtilities.isPlainFileName( | |
8103 self.fileName | |
8104 ): | |
8105 return | |
8106 | |
8107 readOnly = self.checkReadOnly() | |
8108 if not bForce and (readOnly == self.isReadOnly()): | |
8109 return | |
8110 | |
8111 cap = self.fileName | |
8112 if readOnly: | |
8113 cap = self.tr("{0} (ro)".format(cap)) | |
8114 self.setReadOnly(readOnly) | |
8115 self.setWindowTitle(cap) | |
8116 self.captionChanged.emit(cap, self) | |
8117 | |
8118 def checkReadOnly(self): | |
8119 """ | |
8120 Public method to check the 'read only' state. | |
8121 | |
8122 @return flag indicate a 'read only' state | |
8123 @rtype bool | |
8124 """ | |
8125 return ( | |
8126 FileSystemUtilities.isPlainFileName(self.fileName) | |
8127 and not os.access(self.fileName, os.W_OK) | |
8128 ) or self.isReadOnly() | |
8129 | |
8130 @pyqtSlot() | |
8131 def checkRereadFile(self): | |
8132 """ | |
8133 Public slot to check, if the file needs to be re-read, and refresh it if | |
8134 needed. | |
8135 """ | |
7578 if ( | 8136 if ( |
7579 self.vm.editorsCheckFocusInEnabled() | 8137 self.fileName |
7580 and not self.inReopenPrompt | |
7581 and self.fileName | |
7582 and pathlib.Path(self.fileName).exists() | 8138 and pathlib.Path(self.fileName).exists() |
7583 and pathlib.Path(self.fileName).stat().st_mtime != self.lastModified | 8139 and pathlib.Path(self.fileName).stat().st_mtime != self.lastModified |
7584 ): | 8140 ): |
7585 self.inReopenPrompt = True | |
7586 if Preferences.getEditor("AutoReopen") and not self.isModified(): | 8141 if Preferences.getEditor("AutoReopen") and not self.isModified(): |
7587 self.refresh() | 8142 self.refresh() |
7588 else: | 8143 else: |
7589 msg = self.tr( | 8144 msg = self.tr( |
7590 """<p>The file <b>{0}</b> has been changed while it""" | 8145 """<p>The file <b>{0}</b> has been changed while it""" |
7607 if res: | 8162 if res: |
7608 self.refresh() | 8163 self.refresh() |
7609 else: | 8164 else: |
7610 # do not prompt for this change again... | 8165 # do not prompt for this change again... |
7611 self.lastModified = pathlib.Path(self.fileName).stat().st_mtime | 8166 self.lastModified = pathlib.Path(self.fileName).stat().st_mtime |
7612 self.inReopenPrompt = False | 8167 |
7613 | 8168 @pyqtSlot() |
7614 self.setCursorFlashTime(QApplication.cursorFlashTime()) | 8169 def recordModificationTime(self): |
7615 | 8170 """ |
7616 super().focusInEvent(event) | 8171 Public slot to record the modification time of our file. |
7617 | 8172 """ |
7618 def focusOutEvent(self, event): | 8173 if self.fileName and pathlib.Path(self.fileName).exists(): |
7619 """ | 8174 self.lastModified = pathlib.Path(self.fileName).stat().st_mtime |
7620 Protected method called when the editor loses focus. | |
7621 | |
7622 @param event the event object | |
7623 @type QFocusEvent | |
7624 """ | |
7625 if Preferences.getEditor("AutosaveOnFocusLost") and self.__shouldAutosave(): | |
7626 self.saveFile() | |
7627 | |
7628 self.vm.editorActGrp.setEnabled(False) | |
7629 self.setCaretWidth(0) | |
7630 | |
7631 self.cancelCallTips() | |
7632 | |
7633 super().focusOutEvent(event) | |
7634 | |
7635 def changeEvent(self, evt): | |
7636 """ | |
7637 Protected method called to process an event. | |
7638 | |
7639 This implements special handling for the events showMaximized, | |
7640 showMinimized and showNormal. The windows caption is shortened | |
7641 for the minimized mode and reset to the full filename for the | |
7642 other modes. This is to make the editor windows work nicer | |
7643 with the QWorkspace. | |
7644 | |
7645 @param evt the event, that was generated | |
7646 @type QEvent | |
7647 """ | |
7648 if evt.type() == QEvent.Type.WindowStateChange and bool(self.fileName): | |
7649 cap = ( | |
7650 os.path.basename(self.fileName) | |
7651 if self.windowState() == Qt.WindowState.WindowMinimized | |
7652 else self.fileName | |
7653 ) | |
7654 if self.checkReadOnly(): | |
7655 cap = self.tr("{0} (ro)").format(cap) | |
7656 self.setWindowTitle(cap) | |
7657 | |
7658 super().changeEvent(evt) | |
7659 | |
7660 def mousePressEvent(self, event): | |
7661 """ | |
7662 Protected method to handle the mouse press event. | |
7663 | |
7664 @param event the mouse press event | |
7665 @type QMouseEvent | |
7666 """ | |
7667 if event.button() == Qt.MouseButton.XButton1: | |
7668 self.undo() | |
7669 event.accept() | |
7670 elif event.button() == Qt.MouseButton.XButton2: | |
7671 self.redo() | |
7672 event.accept() | |
7673 elif event.button() == Qt.MouseButton.LeftButton and bool( | |
7674 event.modifiers() | |
7675 & (Qt.KeyboardModifier.MetaModifier | Qt.KeyboardModifier.AltModifier) | |
7676 ): | |
7677 line, index = self.lineIndexFromPoint(event.position().toPoint()) | |
7678 self.addCursor(line, index) | |
7679 event.accept() | |
7680 else: | |
7681 self.vm.eventFilter(self, event) | |
7682 super().mousePressEvent(event) | |
7683 | |
7684 def mouseDoubleClickEvent(self, evt): | |
7685 """ | |
7686 Protected method to handle mouse double click events. | |
7687 | |
7688 @param evt reference to the mouse event | |
7689 @type QMouseEvent | |
7690 """ | |
7691 super().mouseDoubleClickEvent(evt) | |
7692 | |
7693 # accept all double click events even if not handled by QScintilla | |
7694 evt.accept() | |
7695 | |
7696 self.mouseDoubleClick.emit(evt.position().toPoint(), evt.buttons()) | |
7697 | |
7698 def wheelEvent(self, evt): | |
7699 """ | |
7700 Protected method to handle wheel events. | |
7701 | |
7702 @param evt reference to the wheel event | |
7703 @type QWheelEvent | |
7704 """ | |
7705 delta = evt.angleDelta().y() | |
7706 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: | |
7707 if delta < 0: | |
7708 self.zoomOut() | |
7709 elif delta > 0: | |
7710 self.zoomIn() | |
7711 evt.accept() | |
7712 return | |
7713 | |
7714 if evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: | |
7715 if delta < 0: | |
7716 self.gotoMethodClass(False) | |
7717 elif delta > 0: | |
7718 self.gotoMethodClass(True) | |
7719 evt.accept() | |
7720 return | |
7721 | |
7722 super().wheelEvent(evt) | |
7723 | |
7724 def event(self, evt): | |
7725 """ | |
7726 Public method handling events. | |
7727 | |
7728 @param evt reference to the event | |
7729 @type QEvent | |
7730 @return flag indicating, if the event was handled | |
7731 @rtype bool | |
7732 """ | |
7733 if evt.type() == QEvent.Type.Gesture: | |
7734 self.gestureEvent(evt) | |
7735 return True | |
7736 | |
7737 return super().event(evt) | |
7738 | |
7739 def gestureEvent(self, evt): | |
7740 """ | |
7741 Protected method handling gesture events. | |
7742 | |
7743 @param evt reference to the gesture event | |
7744 @type QGestureEvent | |
7745 """ | |
7746 pinch = evt.gesture(Qt.GestureType.PinchGesture) | |
7747 if pinch: | |
7748 if pinch.state() == Qt.GestureState.GestureStarted: | |
7749 zoom = (self.getZoom() + 10) / 10.0 | |
7750 pinch.setTotalScaleFactor(zoom) | |
7751 elif pinch.state() == Qt.GestureState.GestureUpdated: | |
7752 zoom = int(pinch.totalScaleFactor() * 10) - 10 | |
7753 if zoom <= -9: | |
7754 zoom = -9 | |
7755 pinch.setTotalScaleFactor(0.1) | |
7756 elif zoom >= 20: | |
7757 zoom = 20 | |
7758 pinch.setTotalScaleFactor(3.0) | |
7759 self.zoomTo(zoom) | |
7760 evt.accept() | |
7761 | |
7762 def resizeEvent(self, evt): | |
7763 """ | |
7764 Protected method handling resize events. | |
7765 | |
7766 @param evt reference to the resize event | |
7767 @type QResizeEvent | |
7768 """ | |
7769 super().resizeEvent(evt) | |
7770 self.__markerMap.calculateGeometry() | |
7771 | |
7772 def viewportEvent(self, evt): | |
7773 """ | |
7774 Protected method handling event of the viewport. | |
7775 | |
7776 @param evt reference to the event | |
7777 @type QEvent | |
7778 @return flag indiating that the event was handled | |
7779 @rtype bool | |
7780 """ | |
7781 with contextlib.suppress(AttributeError): | |
7782 self.__markerMap.calculateGeometry() | |
7783 return super().viewportEvent(evt) | |
7784 | |
7785 def __updateReadOnly(self, bForce=True): | |
7786 """ | |
7787 Private method to update the readOnly information for this editor. | |
7788 | |
7789 If bForce is True, then updates everything regardless if | |
7790 the attributes have actually changed, such as during | |
7791 initialization time. A signal is emitted after the | |
7792 caption change. | |
7793 | |
7794 @param bForce True to force change, False to only update and emit | |
7795 signal if there was an attribute change. | |
7796 """ | |
7797 if self.fileName == "" or not self.isLocalFile(): | |
7798 return | |
7799 | |
7800 readOnly = self.checkReadOnly() | |
7801 if not bForce and (readOnly == self.isReadOnly()): | |
7802 return | |
7803 | |
7804 cap = self.fileName | |
7805 if readOnly: | |
7806 cap = self.tr("{0} (ro)".format(cap)) | |
7807 self.setReadOnly(readOnly) | |
7808 self.setWindowTitle(cap) | |
7809 self.captionChanged.emit(cap, self) | |
7810 | |
7811 def checkReadOnly(self): | |
7812 """ | |
7813 Public method to check the 'read only' state. | |
7814 | |
7815 @return flag indicate a 'read only' state | |
7816 @rtype bool | |
7817 """ | |
7818 return ( | |
7819 self.isLocalFile() and not os.access(self.fileName, os.W_OK) | |
7820 ) or self.isReadOnly() | |
7821 | 8175 |
7822 @pyqtSlot() | 8176 @pyqtSlot() |
7823 def refresh(self): | 8177 def refresh(self): |
7824 """ | 8178 """ |
7825 Public slot to refresh the editor contents. | 8179 Public slot to refresh the editor contents. |
7847 self.close() | 8201 self.close() |
7848 return | 8202 return |
7849 | 8203 |
7850 # reread the file | 8204 # reread the file |
7851 try: | 8205 try: |
7852 self.readFile(self.fileName) | 8206 self.readFile(self.fileName, noempty=True) |
7853 except OSError: | 8207 except OSError: |
7854 # do not prompt for this change again... | 8208 # do not prompt for this change again... |
7855 self.lastModified = QDateTime.currentDateTime() | 8209 self.lastModified = QDateTime.currentDateTime() |
7856 self.setModified(False) | 8210 self.setModified(False) |
7857 self.__convertTabs() | 8211 self.__convertTabs() |
7878 | 8232 |
7879 def setMonospaced(self, on): | 8233 def setMonospaced(self, on): |
7880 """ | 8234 """ |
7881 Public method to set/reset a monospaced font. | 8235 Public method to set/reset a monospaced font. |
7882 | 8236 |
7883 @param on flag to indicate usage of a monospace font (boolean) | 8237 @param on flag to indicate usage of a monospace font |
8238 @type bool | |
7884 """ | 8239 """ |
7885 if on: | 8240 if on: |
7886 if not self.lexer_: | 8241 if not self.lexer_: |
7887 f = Preferences.getEditorOtherFonts("MonospacedFont") | 8242 f = Preferences.getEditorOtherFonts("MonospacedFont") |
7888 self.monospacedStyles(f) | 8243 self.monospacedStyles(f) |
7910 | 8265 |
7911 def dragEnterEvent(self, event): | 8266 def dragEnterEvent(self, event): |
7912 """ | 8267 """ |
7913 Protected method to handle the drag enter event. | 8268 Protected method to handle the drag enter event. |
7914 | 8269 |
7915 @param event the drag enter event (QDragEnterEvent) | 8270 @param event the drag enter event |
8271 @type QDragEnterEvent | |
7916 """ | 8272 """ |
7917 self.inDragDrop = event.mimeData().hasUrls() | 8273 self.inDragDrop = event.mimeData().hasUrls() |
7918 if self.inDragDrop: | 8274 if self.inDragDrop: |
7919 event.acceptProposedAction() | 8275 event.acceptProposedAction() |
7920 else: | 8276 else: |
7922 | 8278 |
7923 def dragMoveEvent(self, event): | 8279 def dragMoveEvent(self, event): |
7924 """ | 8280 """ |
7925 Protected method to handle the drag move event. | 8281 Protected method to handle the drag move event. |
7926 | 8282 |
7927 @param event the drag move event (QDragMoveEvent) | 8283 @param event the drag move event |
8284 @type QDragMoveEvent | |
7928 """ | 8285 """ |
7929 if self.inDragDrop: | 8286 if self.inDragDrop: |
7930 event.accept() | 8287 event.accept() |
7931 else: | 8288 else: |
7932 super().dragMoveEvent(event) | 8289 super().dragMoveEvent(event) |
7933 | 8290 |
7934 def dragLeaveEvent(self, event): | 8291 def dragLeaveEvent(self, event): |
7935 """ | 8292 """ |
7936 Protected method to handle the drag leave event. | 8293 Protected method to handle the drag leave event. |
7937 | 8294 |
7938 @param event the drag leave event (QDragLeaveEvent) | 8295 @param event the drag leave event |
8296 @type QDragLeaveEvent | |
7939 """ | 8297 """ |
7940 if self.inDragDrop: | 8298 if self.inDragDrop: |
7941 self.inDragDrop = False | 8299 self.inDragDrop = False |
7942 event.accept() | 8300 event.accept() |
7943 else: | 8301 else: |
7945 | 8303 |
7946 def dropEvent(self, event): | 8304 def dropEvent(self, event): |
7947 """ | 8305 """ |
7948 Protected method to handle the drop event. | 8306 Protected method to handle the drop event. |
7949 | 8307 |
7950 @param event the drop event (QDropEvent) | 8308 @param event the drop event |
8309 @type QDropEvent | |
7951 """ | 8310 """ |
7952 if event.mimeData().hasUrls(): | 8311 if event.mimeData().hasUrls(): |
7953 for url in event.mimeData().urls(): | 8312 for url in event.mimeData().urls(): |
7954 fname = url.toLocalFile() | 8313 fname = url.toLocalFile() |
7955 if fname: | 8314 if fname: |
7975 | 8334 |
7976 def __initContextMenuResources(self): | 8335 def __initContextMenuResources(self): |
7977 """ | 8336 """ |
7978 Private method used to setup the Resources context sub menu. | 8337 Private method used to setup the Resources context sub menu. |
7979 | 8338 |
7980 @return reference to the generated menu (QMenu) | 8339 @return reference to the generated menu |
8340 @rtype QMenu | |
7981 """ | 8341 """ |
7982 menu = QMenu(self.tr("Resources")) | 8342 menu = QMenu(self.tr("Resources")) |
7983 | 8343 |
7984 menu.addAction(self.tr("Add file..."), self.__addFileResource) | 8344 menu.addAction(self.tr("Add file..."), self.__addFileResource) |
7985 menu.addAction(self.tr("Add files..."), self.__addFileResources) | 8345 menu.addAction(self.tr("Add files..."), self.__addFileResources) |
8208 def editorCommand(self, cmd): | 8568 def editorCommand(self, cmd): |
8209 """ | 8569 """ |
8210 Public method to perform a simple editor command. | 8570 Public method to perform a simple editor command. |
8211 | 8571 |
8212 @param cmd the scintilla command to be performed | 8572 @param cmd the scintilla command to be performed |
8573 @type int | |
8213 """ | 8574 """ |
8214 if cmd == QsciScintilla.SCI_TAB: | 8575 if cmd == QsciScintilla.SCI_TAB: |
8215 try: | 8576 try: |
8216 templateViewer = ericApp().getObject("TemplateViewer") | 8577 templateViewer = ericApp().getObject("TemplateViewer") |
8217 except KeyError: | 8578 except KeyError: |
8269 | 8630 |
8270 def __applyTemplate(self, templateName, language): | 8631 def __applyTemplate(self, templateName, language): |
8271 """ | 8632 """ |
8272 Private method to apply a template by name. | 8633 Private method to apply a template by name. |
8273 | 8634 |
8274 @param templateName name of the template to apply (string) | 8635 @param templateName name of the template to apply |
8636 @type str | |
8275 @param language name of the language (group) to get the template | 8637 @param language name of the language (group) to get the template |
8276 from (string) | 8638 from |
8639 @type str | |
8277 """ | 8640 """ |
8278 try: | 8641 try: |
8279 templateViewer = ericApp().getObject("TemplateViewer") | 8642 templateViewer = ericApp().getObject("TemplateViewer") |
8280 except KeyError: | 8643 except KeyError: |
8281 # template viewer is not active | 8644 # template viewer is not active |
8358 | 8721 |
8359 def __setSpellingLanguage(self, language, pwl="", pel=""): | 8722 def __setSpellingLanguage(self, language, pwl="", pel=""): |
8360 """ | 8723 """ |
8361 Private method to set the spell checking language. | 8724 Private method to set the spell checking language. |
8362 | 8725 |
8363 @param language spell checking language to be set (string) | 8726 @param language spell checking language to be set |
8364 @param pwl name of the personal/project word list (string) | 8727 @type str |
8365 @param pel name of the personal/project exclude list (string) | 8728 @param pwl name of the personal/project word list |
8729 @type str | |
8730 @param pel name of the personal/project exclude list | |
8731 @type str | |
8366 """ | 8732 """ |
8367 if self.spell and self.spell.getLanguage() != language: | 8733 if self.spell and self.spell.getLanguage() != language: |
8368 self.spell.setLanguage(language, pwl=pwl, pel=pel) | 8734 self.spell.setLanguage(language, pwl=pwl, pel=pel) |
8369 self.spell.checkDocumentIncrementally() | 8735 self.spell.checkDocumentIncrementally() |
8370 | 8736 |
8601 Public method to get some share status info. | 8967 Public method to get some share status info. |
8602 | 8968 |
8603 @return tuple indicating, if the editor is sharable, the sharing | 8969 @return tuple indicating, if the editor is sharable, the sharing |
8604 status, if it is inside a locally initiated shared edit session | 8970 status, if it is inside a locally initiated shared edit session |
8605 and if it is inside a remotely initiated shared edit session | 8971 and if it is inside a remotely initiated shared edit session |
8606 (boolean, boolean, boolean, boolean) | 8972 @rtype tuple of (bool, bool, bool, bool) |
8607 """ | 8973 """ |
8608 return ( | 8974 return ( |
8609 ( | 8975 ( |
8610 bool(self.fileName) | 8976 bool(self.fileName) |
8611 and self.project.isOpen() | 8977 and self.project.isOpen() |
8618 | 8984 |
8619 def shareConnected(self, connected): | 8985 def shareConnected(self, connected): |
8620 """ | 8986 """ |
8621 Public method to handle a change of the connected state. | 8987 Public method to handle a change of the connected state. |
8622 | 8988 |
8623 @param connected flag indicating the connected state (boolean) | 8989 @param connected flag indicating the connected state |
8990 @type bool | |
8624 """ | 8991 """ |
8625 if not connected: | 8992 if not connected: |
8626 self.__inRemoteSharedEdit = False | 8993 self.__inRemoteSharedEdit = False |
8627 self.setReadOnly(False) | 8994 self.setReadOnly(False) |
8628 self.__updateReadOnly() | 8995 self.__updateReadOnly() |
8632 | 8999 |
8633 def shareEditor(self, share): | 9000 def shareEditor(self, share): |
8634 """ | 9001 """ |
8635 Public method to set the shared status of the editor. | 9002 Public method to set the shared status of the editor. |
8636 | 9003 |
8637 @param share flag indicating the share status (boolean) | 9004 @param share flag indicating the share status |
9005 @type bool | |
8638 """ | 9006 """ |
8639 self.__isShared = share | 9007 self.__isShared = share |
8640 if not share: | 9008 if not share: |
8641 self.shareConnected(False) | 9009 self.shareConnected(False) |
8642 | 9010 |
8669 | 9037 |
8670 def cancelSharedEdit(self, send=True): | 9038 def cancelSharedEdit(self, send=True): |
8671 """ | 9039 """ |
8672 Public method to cancel a shared edit session for the editor. | 9040 Public method to cancel a shared edit session for the editor. |
8673 | 9041 |
8674 @param send flag indicating to send the CancelEdit command (boolean) | 9042 @param send flag indicating to send the CancelEdit command |
9043 @type bool | |
8675 """ | 9044 """ |
8676 self.__inSharedEdit = False | 9045 self.__inSharedEdit = False |
8677 self.__savedText = "" | 9046 self.__savedText = "" |
8678 if send: | 9047 if send: |
8679 self.__send(Editor.CancelEditToken) | 9048 self.__send(Editor.CancelEditToken) |
8680 | 9049 |
8681 def __send(self, token, args=None): | 9050 def __send(self, token, args=None): |
8682 """ | 9051 """ |
8683 Private method to send an editor command to remote editors. | 9052 Private method to send an editor command to remote editors. |
8684 | 9053 |
8685 @param token command token (string) | 9054 @param token command token |
8686 @param args arguments for the command (string) | 9055 @type str |
9056 @param args arguments for the command | |
9057 @type str | |
8687 """ | 9058 """ |
8688 if self.vm.isConnected(): | 9059 if self.vm.isConnected(): |
8689 msg = "" | 9060 msg = "" |
8690 if token in ( | 9061 if token in ( |
8691 Editor.StartEditToken, | 9062 Editor.StartEditToken, |
8717 | 9088 |
8718 def __dispatchCommand(self, command): | 9089 def __dispatchCommand(self, command): |
8719 """ | 9090 """ |
8720 Private method to dispatch received commands. | 9091 Private method to dispatch received commands. |
8721 | 9092 |
8722 @param command command to be processed (string) | 9093 @param command command to be processed |
9094 @type str | |
8723 """ | 9095 """ |
8724 token, argsString = command.split(Editor.Separator, 1) | 9096 token, argsString = command.split(Editor.Separator, 1) |
8725 if token == Editor.StartEditToken: | 9097 if token == Editor.StartEditToken: |
8726 self.__processStartEditCommand(argsString) | 9098 self.__processStartEditCommand(argsString) |
8727 elif token == Editor.CancelEditToken: | 9099 elif token == Editor.CancelEditToken: |
8760 def __calculateChanges(self, old, new): | 9132 def __calculateChanges(self, old, new): |
8761 """ | 9133 """ |
8762 Private method to determine change commands to convert old text into | 9134 Private method to determine change commands to convert old text into |
8763 new text. | 9135 new text. |
8764 | 9136 |
8765 @param old old text (string) | 9137 @param old old text |
8766 @param new new text (string) | 9138 @type str |
8767 @return commands to change old into new (string) | 9139 @param new new text |
9140 @type str | |
9141 @return commands to change old into new | |
9142 @rtype str | |
8768 """ | 9143 """ |
8769 oldL = old.splitlines() | 9144 oldL = old.splitlines() |
8770 newL = new.splitlines() | 9145 newL = new.splitlines() |
8771 matcher = difflib.SequenceMatcher(None, oldL, newL) | 9146 matcher = difflib.SequenceMatcher(None, oldL, newL) |
8772 | 9147 |
8892 | 9267 |
8893 def __searchCurrentWord(self, forward=True): | 9268 def __searchCurrentWord(self, forward=True): |
8894 """ | 9269 """ |
8895 Private slot to search the next occurrence of the current word. | 9270 Private slot to search the next occurrence of the current word. |
8896 | 9271 |
8897 @param forward flag indicating the search direction (boolean) | 9272 @param forward flag indicating the search direction |
9273 @type bool | |
8898 """ | 9274 """ |
8899 self.hideFindIndicator() | 9275 self.hideFindIndicator() |
8900 line, index = self.getCursorPosition() | 9276 line, index = self.getCursorPosition() |
8901 word = self.getCurrentWord() | 9277 word = self.getCurrentWord() |
8902 wordStart, wordEnd = self.getCurrentWordBoundaries() | 9278 wordStart, wordEnd = self.getCurrentWordBoundaries() |
8990 | 9366 |
8991 # step 3: sort the lines | 9367 # step 3: sort the lines |
8992 eol = self.getLineSeparator() | 9368 eol = self.getLineSeparator() |
8993 lastWithEol = True | 9369 lastWithEol = True |
8994 newLines = [] | 9370 newLines = [] |
8995 for txt in sorted(selText.keys(), key=keyFun, reverse=reverse): | 9371 for txt in sorted(selText, key=keyFun, reverse=reverse): |
8996 for line in selText[txt]: | 9372 for line in selText[txt]: |
8997 txt = txtLines[line] | 9373 txt = txtLines[line] |
8998 if not txt.endswith(eol): | 9374 if not txt.endswith(eol): |
8999 lastWithEol = False | 9375 lastWithEol = False |
9000 txt += eol | 9376 txt += eol |
9133 a plug-in. | 9509 a plug-in. |
9134 | 9510 |
9135 @param name name of the plug-in | 9511 @param name name of the plug-in |
9136 @type str | 9512 @type str |
9137 """ | 9513 """ |
9138 for key in list(self.__mouseClickHandlers.keys()): | 9514 for key in list(self.__mouseClickHandlers): |
9139 if self.__mouseClickHandlers[key][0] == name: | 9515 if self.__mouseClickHandlers[key][0] == name: |
9140 del self.__mouseClickHandlers[key] | 9516 del self.__mouseClickHandlers[key] |
9141 | 9517 |
9142 def gotoReferenceHandler(self, referencesList): | 9518 def gotoReferenceHandler(self, referencesList): |
9143 """ | 9519 """ |
9223 @return EditorConfig dictionary | 9599 @return EditorConfig dictionary |
9224 @rtype dict | 9600 @rtype dict |
9225 """ | 9601 """ |
9226 editorConfig = {} | 9602 editorConfig = {} |
9227 | 9603 |
9228 if fileName and self.isLocalFile(): | 9604 if fileName and FileSystemUtilities.isPlainFileName(self.fileName): |
9229 try: | 9605 try: |
9230 editorConfig = editorconfig.get_properties(fileName) | 9606 editorConfig = editorconfig.get_properties(fileName) |
9231 except editorconfig.EditorConfigError: | 9607 except editorconfig.EditorConfigError: |
9232 EricMessageBox.warning( | 9608 EricMessageBox.warning( |
9233 self, | 9609 self, |
9256 @type bool | 9632 @type bool |
9257 @param config reference to an EditorConfig object or None | 9633 @param config reference to an EditorConfig object or None |
9258 @type dict | 9634 @type dict |
9259 @return value of requested setting or None if nothing was found and | 9635 @return value of requested setting or None if nothing was found and |
9260 nodefault parameter was True | 9636 nodefault parameter was True |
9261 @rtype any | 9637 @rtype Any |
9262 """ | 9638 """ |
9263 if config is None: | 9639 if config is None: |
9264 config = self.__editorConfig | 9640 config = self.__editorConfig |
9265 | 9641 |
9266 if not config: | 9642 if not config: |
9317 Public method to get the requested option via EditorConfig. | 9693 Public method to get the requested option via EditorConfig. |
9318 | 9694 |
9319 @param option Preferences option key | 9695 @param option Preferences option key |
9320 @type str | 9696 @type str |
9321 @return value of requested setting | 9697 @return value of requested setting |
9322 @rtype any | 9698 @rtype Any |
9323 """ | 9699 """ |
9324 return self.__getEditorConfig(option) | 9700 return self.__getEditorConfig(option) |
9325 | 9701 |
9326 def __getOverrideValue(self, option): | 9702 def __getOverrideValue(self, option): |
9327 """ | 9703 """ |
9328 Private method to get an override value for the current file type. | 9704 Private method to get an override value for the current file type. |
9329 | 9705 |
9330 @param option Preferences option key | 9706 @param option Preferences option key |
9331 @type str | 9707 @type str |
9332 @return override value; None in case nothing is defined | 9708 @return override value; None in case nothing is defined |
9333 @rtype any | 9709 @rtype Any |
9334 """ | 9710 """ |
9335 if option in ("TabWidth", "IndentWidth"): | 9711 if option in ("TabWidth", "IndentWidth"): |
9336 overrides = Preferences.getEditor("TabIndentOverride") | 9712 overrides = Preferences.getEditor("TabIndentOverride") |
9337 language = self.filetype or self.apiLanguage | 9713 language = self.filetype or self.apiLanguage |
9338 if language in overrides: | 9714 if language in overrides: |