8 """ |
8 """ |
9 |
9 |
10 import contextlib |
10 import contextlib |
11 import os |
11 import os |
12 |
12 |
13 from PyQt5.QtCore import ( |
13 from PyQt6.QtCore import ( |
14 QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt |
14 QTimer, QThread, QFileInfo, pyqtSignal, QDateTime, QObject, Qt |
15 ) |
15 ) |
16 with contextlib.suppress(ImportError): |
16 with contextlib.suppress(ImportError): |
17 from PyQt5.QtSql import QSqlDatabase, QSqlQuery |
17 from PyQt6.QtSql import QSqlDatabase, QSqlQuery |
18 |
18 |
19 from E5Gui.E5Application import e5App |
19 from EricWidgets.EricApplication import ericApp |
20 |
20 |
21 import QScintilla.Lexers |
21 import QScintilla.Lexers |
22 |
22 |
23 import Globals |
23 import Globals |
24 import Utilities.ModuleParser |
24 import Utilities.ModuleParser |
132 |
132 |
133 def __autoCompletionWordSeparators(self, language): |
133 def __autoCompletionWordSeparators(self, language): |
134 """ |
134 """ |
135 Private method to get the word separator characters for a language. |
135 Private method to get the word separator characters for a language. |
136 |
136 |
137 @param language language of the APIs object (string) |
137 @param language language of the APIs object |
138 @return word separator characters (list of strings) |
138 @type str |
|
139 @return word separator characters |
|
140 @rtype list of str |
139 """ |
141 """ |
140 if language: |
142 if language: |
141 return self.__wseps.get(language, None) |
143 return self.__wseps.get(language, None) |
142 else: |
144 else: |
143 return self.__proxy.autoCompletionWordSeparators() |
145 return self.__proxy.autoCompletionWordSeparators() |
151 def __loadApiFileIfNewer(self, apiFile): |
153 def __loadApiFileIfNewer(self, apiFile): |
152 """ |
154 """ |
153 Private method to load an API file, if it is newer than the one read |
155 Private method to load an API file, if it is newer than the one read |
154 into the database. |
156 into the database. |
155 |
157 |
156 @param apiFile filename of the raw API file (string) |
158 @param apiFile filename of the raw API file |
|
159 @type str |
157 """ |
160 """ |
158 db = QSqlDatabase.database(self.__connectionName) |
161 db = QSqlDatabase.database(self.__connectionName) |
159 db.transaction() |
162 db.transaction() |
160 try: |
163 try: |
161 query = QSqlQuery(db) |
164 query = QSqlQuery(db) |
190 |
193 |
191 def __classesAttributesApi(self, module): |
194 def __classesAttributesApi(self, module): |
192 """ |
195 """ |
193 Private method to generate class api section for class attributes. |
196 Private method to generate class api section for class attributes. |
194 |
197 |
195 @param module module object to get the info from (Module) |
198 @param module module object to get the info from |
196 @return API information (list of strings) |
199 @type Module |
|
200 @return API information |
|
201 @rtype list of str |
197 """ |
202 """ |
198 api = [] |
203 api = [] |
199 modulePath = module.name.split('.') |
204 modulePath = module.name.split('.') |
200 moduleName = "{0}.".format('.'.join(modulePath)) |
205 moduleName = "{0}.".format('.'.join(modulePath)) |
201 |
206 |
269 Private method to store file info only. |
275 Private method to store file info only. |
270 |
276 |
271 Doing this avoids rereading the file whenever the API is initialized |
277 Doing this avoids rereading the file whenever the API is initialized |
272 in case the given file doesn't contain API data. |
278 in case the given file doesn't contain API data. |
273 |
279 |
274 @param apiFile file name of the API file (string) |
280 @param apiFile file name of the API file |
|
281 @type str |
275 """ |
282 """ |
276 db = QSqlDatabase.database(self.__connectionName) |
283 db = QSqlDatabase.database(self.__connectionName) |
277 db.transaction() |
284 db.transaction() |
278 try: |
285 try: |
279 query = QSqlQuery(db) |
286 query = QSqlQuery(db) |
297 |
304 |
298 def __storeApis(self, apis, bases, apiFile, language): |
305 def __storeApis(self, apis, bases, apiFile, language): |
299 """ |
306 """ |
300 Private method to put the API entries into the database. |
307 Private method to put the API entries into the database. |
301 |
308 |
302 @param apis list of api entries (list of strings) |
309 @param apis list of api entries |
303 @param bases list of base class entries (list of strings) |
310 @type list of str |
304 @param apiFile filename of the file read to get the APIs (string) |
311 @param bases list of base class entries |
305 @param language programming language of the file of the APIs (string) |
312 @type list of str |
|
313 @param apiFile filename of the file read to get the APIs |
|
314 @type str |
|
315 @param language programming language of the file of the APIs |
|
316 @type str |
306 """ |
317 """ |
307 wseps = self.__autoCompletionWordSeparators(language) |
318 wseps = self.__autoCompletionWordSeparators(language) |
308 if wseps is None: |
319 if wseps is None: |
309 return |
320 return |
310 |
321 |
427 |
438 |
428 def __deleteApiFile(self, apiFile): |
439 def __deleteApiFile(self, apiFile): |
429 """ |
440 """ |
430 Private method to delete all references to an api file. |
441 Private method to delete all references to an api file. |
431 |
442 |
432 @param apiFile filename of the raw API file (string) |
443 @param apiFile filename of the raw API file |
|
444 @type str |
433 """ |
445 """ |
434 db = QSqlDatabase.database(self.__connectionName) |
446 db = QSqlDatabase.database(self.__connectionName) |
435 db.transaction() |
447 db.transaction() |
436 try: |
448 try: |
437 query = QSqlQuery(db) |
449 query = QSqlQuery(db) |
668 """ |
680 """ |
669 Private method to initialize as a project API object. |
681 Private method to initialize as a project API object. |
670 """ |
682 """ |
671 self.__lexer = None |
683 self.__lexer = None |
672 |
684 |
673 self.__project = e5App().getObject("Project") |
685 self.__project = ericApp().getObject("Project") |
674 self.__project.projectOpened.connect(self.__projectOpened) |
686 self.__project.projectOpened.connect(self.__projectOpened) |
675 self.__project.newProject.connect(self.__projectOpened) |
687 self.__project.newProject.connect(self.__projectOpened) |
676 self.__project.projectClosed.connect(self.__projectClosed) |
688 self.__project.projectClosed.connect(self.__projectClosed) |
677 self.__project.projectFormCompiled.connect(self.__projectFormCompiled) |
689 self.__project.projectFormCompiled.connect(self.__projectFormCompiled) |
678 self.__project.projectChanged.connect(self.__projectChanged) |
690 self.__project.projectChanged.connect(self.__projectChanged) |
701 |
713 |
702 def _apiDbName(self): |
714 def _apiDbName(self): |
703 """ |
715 """ |
704 Protected method to determine the name of the database file. |
716 Protected method to determine the name of the database file. |
705 |
717 |
706 @return name of the database file (string) |
718 @return name of the database file |
|
719 @rtype str |
707 """ |
720 """ |
708 if self.__language == ApisNameProject: |
721 if self.__language == ApisNameProject: |
709 return os.path.join(self.__project.getProjectManagementDir(), |
722 return os.path.join(self.__project.getProjectManagementDir(), |
710 "project-apis.db") |
723 "project-apis.db") |
711 else: |
724 else: |
745 |
758 |
746 def __openApiDb(self): |
759 def __openApiDb(self): |
747 """ |
760 """ |
748 Private method to open the API database. |
761 Private method to open the API database. |
749 |
762 |
750 @return flag indicating the database status (boolean) |
763 @return flag indicating the database status |
|
764 @rtype bool |
751 """ |
765 """ |
752 db = QSqlDatabase.database(self.__connectionName, False) |
766 db = QSqlDatabase.database(self.__connectionName, False) |
753 if not db.isValid(): |
767 if not db.isValid(): |
754 # the database connection is a new one |
768 # the database connection is a new one |
755 db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName) |
769 db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName) |
828 |
843 |
829 def __isPrepared(self): |
844 def __isPrepared(self): |
830 """ |
845 """ |
831 Private method to check, if the database has been prepared. |
846 Private method to check, if the database has been prepared. |
832 |
847 |
833 @return flag indicating the prepared status (boolean) |
848 @return flag indicating the prepared status |
|
849 @rtype bool |
834 """ |
850 """ |
835 db = QSqlDatabase.database(self.__connectionName) |
851 db = QSqlDatabase.database(self.__connectionName) |
836 prepared = len(db.tables()) > 0 |
852 prepared = len(db.tables()) > 0 |
837 if prepared: |
853 if prepared: |
838 db.transaction() |
854 db.transaction() |
853 |
869 |
854 def getCompletions(self, start=None, context=None, followHierarchy=False): |
870 def getCompletions(self, start=None, context=None, followHierarchy=False): |
855 """ |
871 """ |
856 Public method to determine the possible completions. |
872 Public method to determine the possible completions. |
857 |
873 |
858 @keyparam start string giving the start of the word to be |
874 @param start string giving the start of the word to be |
859 completed (string) |
875 completed |
860 @keyparam context string giving the context (e.g. classname) |
876 @type str |
861 to be completed (string) |
877 @param context string giving the context (e.g. classname) |
862 @keyparam followHierarchy flag indicating to follow the hierarchy of |
878 to be completed |
863 base classes (boolean) |
879 @type str |
|
880 @param followHierarchy flag indicating to follow the hierarchy of |
|
881 base classes |
|
882 @type bool |
864 @return list of dictionaries with possible completions |
883 @return list of dictionaries with possible completions |
865 (key 'completion' contains the completion (string), |
884 (key 'completion' contains the completion, |
866 key 'context' contains the context (string) and |
885 key 'context' contains the context and |
867 key 'pictureId' contains the ID of the icon to be shown (string)) |
886 key 'pictureId' contains the ID of the icon to be shown) |
|
887 @rtype list of dict |
868 """ |
888 """ |
869 completions = [] |
889 completions = [] |
870 |
890 |
871 db = QSqlDatabase.database(self.__connectionName) |
891 db = QSqlDatabase.database(self.__connectionName) |
872 if db.isOpen() and not self.__inPreparation: |
892 if db.isOpen() and not self.__inPreparation: |
919 def getCalltips(self, acWord, commas, context=None, fullContext=None, |
939 def getCalltips(self, acWord, commas, context=None, fullContext=None, |
920 showContext=True, followHierarchy=False): |
940 showContext=True, followHierarchy=False): |
921 """ |
941 """ |
922 Public method to determine the calltips. |
942 Public method to determine the calltips. |
923 |
943 |
924 @param acWord function to get calltips for (string) |
944 @param acWord function to get calltips for |
|
945 @type str |
925 @param commas minimum number of commas contained in the calltip |
946 @param commas minimum number of commas contained in the calltip |
926 (integer) |
947 @type int |
927 @param context string giving the context (e.g. classname) (string) |
948 @param context string giving the context (e.g. classname) |
928 @param fullContext string giving the full context (string) |
949 @type str |
|
950 @param fullContext string giving the full context |
|
951 @type str |
929 @param showContext flag indicating to show the calltip context |
952 @param showContext flag indicating to show the calltip context |
930 (boolean) |
953 @type bool |
931 @keyparam followHierarchy flag indicating to follow the hierarchy of |
954 @param followHierarchy flag indicating to follow the hierarchy of |
932 base classes (boolean) |
955 base classes |
933 @return list of calltips (list of string) |
956 @type bool |
|
957 @return list of calltips |
|
958 @rtype list of str |
934 """ |
959 """ |
935 calltips = [] |
960 calltips = [] |
936 |
961 |
937 db = QSqlDatabase.database(self.__connectionName) |
962 db = QSqlDatabase.database(self.__connectionName) |
938 if db.isOpen() and not self.__inPreparation: |
963 if db.isOpen() and not self.__inPreparation: |
1002 def __enoughCommas(self, s, commas): |
1027 def __enoughCommas(self, s, commas): |
1003 """ |
1028 """ |
1004 Private method to determine, if the given string contains enough |
1029 Private method to determine, if the given string contains enough |
1005 commas. |
1030 commas. |
1006 |
1031 |
1007 @param s string to check (string) |
1032 @param s string to check |
1008 @param commas number of commas to check for (integer) |
1033 @type str |
1009 @return flag indicating, that there are enough commas (boolean) |
1034 @param commas number of commas to check for |
|
1035 @type int |
|
1036 @return flag indicating, that there are enough commas |
|
1037 @rtype bool |
1010 """ |
1038 """ |
1011 end = s.find(')') |
1039 end = s.find(')') |
1012 |
1040 |
1013 if end < 0: |
1041 if end < 0: |
1014 return False |
1042 return False |
1030 |
1058 |
1031 def __getProjectFormSources(self, normalized=False): |
1059 def __getProjectFormSources(self, normalized=False): |
1032 """ |
1060 """ |
1033 Private method to get the source files for the project forms. |
1061 Private method to get the source files for the project forms. |
1034 |
1062 |
1035 @keyparam normalized flag indicating a normalized filename is wanted |
1063 @param normalized flag indicating a normalized filename is wanted |
1036 (boolean) |
1064 @type bool |
1037 @return list of project form sources (list of strings) |
1065 @return list of project form sources |
1038 """ |
1066 @rtype list of str |
1039 if self.__project.getProjectLanguage() == "Python3": |
1067 """ |
|
1068 if self.__project.getProjectLanguage() in ( |
|
1069 "Python3", "MicroPython", "Cython" |
|
1070 ): |
1040 sourceExt = ".py" |
1071 sourceExt = ".py" |
1041 elif self.__project.getProjectLanguage() == "Ruby": |
1072 elif self.__project.getProjectLanguage() == "Ruby": |
1042 sourceExt = ".rb" |
1073 sourceExt = ".rb" |
1043 else: |
1074 else: |
1044 return [] |
1075 return [] |
1073 [f for f in self.__getProjectFormSources() if |
1105 [f for f in self.__getProjectFormSources() if |
1074 f not in apiFiles]) |
1106 f not in apiFiles]) |
1075 projectPath = self.__project.getProjectPath() |
1107 projectPath = self.__project.getProjectPath() |
1076 projectType = "" |
1108 projectType = "" |
1077 else: |
1109 else: |
1078 try: |
1110 apiFiles = Preferences.getEditorAPI( |
1079 apiFiles = Preferences.getEditorAPI( |
1111 self.__language, projectType=self.__projectType) |
1080 self.__language, projectType=self.__projectType) |
|
1081 except TypeError: |
|
1082 # older interface |
|
1083 apiFiles = Preferences.getEditorAPI(self.__language) |
|
1084 projectType = self.__projectType |
1112 projectType = self.__projectType |
1085 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, |
1113 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, |
1086 self._apiDbName(), projectPath, |
1114 self._apiDbName(), projectPath, |
1087 projectType=projectType) |
1115 projectType=projectType) |
1088 self.__worker.processing.connect( |
1116 self.__worker.processing.connect( |
1115 |
1143 |
1116 def getLexer(self): |
1144 def getLexer(self): |
1117 """ |
1145 """ |
1118 Public method to return a reference to our lexer object. |
1146 Public method to return a reference to our lexer object. |
1119 |
1147 |
1120 @return reference to the lexer object (QScintilla.Lexers.Lexer) |
1148 @return reference to the lexer object |
|
1149 @rtype Lexer |
1121 """ |
1150 """ |
1122 return self.__lexer |
1151 return self.__lexer |
1123 |
1152 |
1124 def autoCompletionWordSeparators(self): |
1153 def autoCompletionWordSeparators(self): |
1125 """ |
1154 """ |
1126 Public method to get the word separator characters. |
1155 Public method to get the word separator characters. |
1127 |
1156 |
1128 @return word separator characters (list of strings) |
1157 @return word separator characters |
|
1158 @rtype list of str |
1129 """ |
1159 """ |
1130 if self.__lexer: |
1160 if self.__lexer: |
1131 return self.__lexer.autoCompletionWordSeparators() |
1161 return self.__lexer.autoCompletionWordSeparators() |
1132 return None |
1162 return None |
1133 |
1163 |
1134 def __processingStatus(self, status, filename): |
1164 def __processingStatus(self, status, filename): |
1135 """ |
1165 """ |
1136 Private slot handling the processing signal of the API preparation |
1166 Private slot handling the processing signal of the API preparation |
1137 thread. |
1167 thread. |
1138 |
1168 |
1139 @param status preparation status (integer, one of WorkerStatus...) |
1169 @param status preparation status (one of WorkerStatus...) |
1140 @param filename name of the file being processed (string) |
1170 @type int |
|
1171 @param filename name of the file being processed |
|
1172 @type str |
1141 """ |
1173 """ |
1142 if status == WorkerStatusStarted: |
1174 if status == WorkerStatusStarted: |
1143 self.__inPreparation = True |
1175 self.__inPreparation = True |
1144 self.apiPreparationStatus.emit( |
1176 self.apiPreparationStatus.emit( |
1145 self.__language, WorkerStatusStarted, "") |
1177 self.__language, WorkerStatusStarted, "") |
1163 |
1195 |
1164 def __projectOpened(self): |
1196 def __projectOpened(self): |
1165 """ |
1197 """ |
1166 Private slot to perform actions after a project has been opened. |
1198 Private slot to perform actions after a project has been opened. |
1167 """ |
1199 """ |
1168 if self.__project.getProjectLanguage() == "Python3": |
1200 if self.__project.getProjectLanguage() in ( |
|
1201 "Python3", "MicroPython", "Cython" |
|
1202 ): |
1169 self.__discardFirst = ["self", "cls"] |
1203 self.__discardFirst = ["self", "cls"] |
1170 else: |
1204 else: |
1171 self.__discardFirst = [] |
1205 self.__discardFirst = [] |
1172 self.__lexer = QScintilla.Lexers.getLexer( |
1206 self.__lexer = QScintilla.Lexers.getLexer( |
1173 self.__project.getProjectLanguage()) |
1207 self.__project.getProjectLanguage()) |
1181 |
1215 |
1182 def __projectFormCompiled(self, filename): |
1216 def __projectFormCompiled(self, filename): |
1183 """ |
1217 """ |
1184 Private slot to handle the projectFormCompiled signal. |
1218 Private slot to handle the projectFormCompiled signal. |
1185 |
1219 |
1186 @param filename name of the form file that was compiled (string) |
1220 @param filename name of the form file that was compiled |
|
1221 @type str |
1187 """ |
1222 """ |
1188 self.__workerQueue.append(filename) |
1223 self.__workerQueue.append(filename) |
1189 self.__processQueue() |
1224 self.__processQueue() |
1190 |
1225 |
1191 def __projectChanged(self): |
1226 def __projectChanged(self): |
1198 |
1233 |
1199 def editorSaved(self, filename): |
1234 def editorSaved(self, filename): |
1200 """ |
1235 """ |
1201 Public slot to handle the editorSaved signal. |
1236 Public slot to handle the editorSaved signal. |
1202 |
1237 |
1203 @param filename name of the file that was saved (string) |
1238 @param filename name of the file that was saved |
|
1239 @type str |
1204 """ |
1240 """ |
1205 if self.__project.isProjectSource(filename): |
1241 if self.__project.isProjectSource(filename): |
1206 self.__workerQueue.append(filename) |
1242 self.__workerQueue.append(filename) |
1207 self.__processQueue() |
1243 self.__processQueue() |
1208 |
1244 |
1214 """ |
1250 """ |
1215 def __init__(self, mainWindow, parent=None): |
1251 def __init__(self, mainWindow, parent=None): |
1216 """ |
1252 """ |
1217 Constructor |
1253 Constructor |
1218 |
1254 |
1219 @param mainWindow reference to the main eric6 window (QMainWindow) |
1255 @param mainWindow reference to the main eric7 window |
1220 @param parent reference to the parent object (QObject) |
1256 @type QMainWindow |
|
1257 @param parent reference to the parent object |
|
1258 @type QObject |
1221 """ |
1259 """ |
1222 QObject.__init__(self, parent) |
1260 QObject.__init__(self, parent) |
1223 self.setObjectName("Assistant_APIsManager") |
1261 self.setObjectName("Assistant_APIsManager") |
1224 |
1262 |
1225 self.__mw = mainWindow |
1263 self.__mw = mainWindow |
1275 |
1313 |
1276 def __showMessage(self, msg): |
1314 def __showMessage(self, msg): |
1277 """ |
1315 """ |
1278 Private message to show a message in the main windows status bar. |
1316 Private message to show a message in the main windows status bar. |
1279 |
1317 |
1280 @param msg message to be shown (string) |
1318 @param msg message to be shown |
|
1319 @type str |
1281 """ |
1320 """ |
1282 if msg: |
1321 if msg: |
1283 self.__mw.statusBar().showMessage(msg, 2000) |
1322 self.__mw.statusBar().showMessage(msg, 2000) |
1284 |
1323 |
1285 def __apiPreparationStatus(self, language, status, filename): |
1324 def __apiPreparationStatus(self, language, status, filename): |
1286 """ |
1325 """ |
1287 Private slot handling the preparation status signal of an API object. |
1326 Private slot handling the preparation status signal of an API object. |
1288 |
1327 |
1289 @param language language of the API (string) |
1328 @param language language of the API |
1290 @param status preparation status (integer, one of WorkerStatus...) |
1329 @type str |
1291 @param filename name of the file being processed (string) |
1330 @param status preparation status (one of WorkerStatus...) |
|
1331 @type int |
|
1332 @param filename name of the file being processed |
|
1333 @type str |
1292 """ |
1334 """ |
1293 if language == ApisNameProject: |
1335 if language == ApisNameProject: |
1294 language = self.tr("Project") |
1336 language = self.tr("Project") |
1295 |
1337 |
1296 if status == WorkerStatusStarted: |
1338 if status == WorkerStatusStarted: |