|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2007 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the APIsManager. |
|
8 """ |
|
9 |
|
10 import os |
|
11 |
|
12 from PyQt5.QtCore import QDir, QFileInfo, pyqtSignal, QObject |
|
13 from PyQt5.Qsci import QsciAPIs |
|
14 |
|
15 from . import Lexers |
|
16 import Preferences |
|
17 import Globals |
|
18 |
|
19 |
|
20 class APIs(QObject): |
|
21 """ |
|
22 Class implementing an API storage entity. |
|
23 |
|
24 @signal apiPreparationFinished() emitted after the API preparation has |
|
25 finished |
|
26 @signal apiPreparationCancelled() emitted after the API preparation has |
|
27 been cancelled |
|
28 @signal apiPreparationStarted() emitted after the API preparation has |
|
29 started |
|
30 """ |
|
31 apiPreparationFinished = pyqtSignal() |
|
32 apiPreparationCancelled = pyqtSignal() |
|
33 apiPreparationStarted = pyqtSignal() |
|
34 |
|
35 def __init__(self, language, projectType="", forPreparation=False, |
|
36 parent=None): |
|
37 """ |
|
38 Constructor |
|
39 |
|
40 @param language language of the APIs object |
|
41 @type str |
|
42 @param projectType type of the project |
|
43 @type str |
|
44 @param forPreparation flag indicating this object is just needed |
|
45 for a preparation process |
|
46 @type bool |
|
47 @param parent reference to the parent object |
|
48 @type QObject |
|
49 """ |
|
50 super().__init__(parent) |
|
51 if projectType: |
|
52 self.setObjectName("APIs_{0}_{1}".format(language, projectType)) |
|
53 else: |
|
54 self.setObjectName("APIs_{0}".format(language)) |
|
55 |
|
56 self.__inPreparation = False |
|
57 self.__language = language |
|
58 self.__projectType = projectType |
|
59 self.__forPreparation = forPreparation |
|
60 self.__lexer = Lexers.getLexer(self.__language) |
|
61 self.__apifiles = Preferences.getEditorAPI(self.__language, |
|
62 self.__projectType) |
|
63 self.__apifiles.sort() |
|
64 if self.__lexer is None: |
|
65 self.__apis = None |
|
66 else: |
|
67 self.__apis = QsciAPIs(self.__lexer) |
|
68 self.__apis.apiPreparationFinished.connect( |
|
69 self.__apiPreparationFinished) |
|
70 self.__apis.apiPreparationCancelled.connect( |
|
71 self.__apiPreparationCancelled) |
|
72 self.__apis.apiPreparationStarted.connect( |
|
73 self.__apiPreparationStarted) |
|
74 self.__loadAPIs() |
|
75 |
|
76 def __loadAPIs(self): |
|
77 """ |
|
78 Private method to load the APIs. |
|
79 """ |
|
80 if self.__apis.isPrepared(): |
|
81 # load a prepared API file |
|
82 if ( |
|
83 not self.__forPreparation and |
|
84 Preferences.getEditor("AutoPrepareAPIs") |
|
85 ): |
|
86 self.prepareAPIs() |
|
87 self.__apis.loadPrepared(self.__preparedName()) |
|
88 else: |
|
89 # load the raw files and prepare the API file |
|
90 if ( |
|
91 not self.__forPreparation and |
|
92 Preferences.getEditor("AutoPrepareAPIs") |
|
93 ): |
|
94 self.prepareAPIs(ondemand=True) |
|
95 |
|
96 def reloadAPIs(self): |
|
97 """ |
|
98 Public method to reload the API information. |
|
99 """ |
|
100 if ( |
|
101 not self.__forPreparation and |
|
102 Preferences.getEditor("AutoPrepareAPIs") |
|
103 ): |
|
104 self.prepareAPIs() |
|
105 self.__loadAPIs() |
|
106 |
|
107 def getQsciAPIs(self): |
|
108 """ |
|
109 Public method to get a reference to QsciAPIs object. |
|
110 |
|
111 @return reference to the QsciAPIs object (QsciAPIs) |
|
112 """ |
|
113 if ( |
|
114 not self.__forPreparation and |
|
115 Preferences.getEditor("AutoPrepareAPIs") |
|
116 ): |
|
117 self.prepareAPIs() |
|
118 return self.__apis |
|
119 |
|
120 def isEmpty(self): |
|
121 """ |
|
122 Public method to check, if the object has API files configured. |
|
123 |
|
124 @return flag indicating no API files have been configured (boolean) |
|
125 """ |
|
126 return len(self.__apifiles) == 0 |
|
127 |
|
128 def __apiPreparationFinished(self): |
|
129 """ |
|
130 Private method called to save an API, after it has been prepared. |
|
131 """ |
|
132 self.__apis.savePrepared(self.__preparedName()) |
|
133 self.__inPreparation = False |
|
134 self.apiPreparationFinished.emit() |
|
135 |
|
136 def __apiPreparationCancelled(self): |
|
137 """ |
|
138 Private method called, after the API preparation process has been |
|
139 cancelled. |
|
140 """ |
|
141 self.__inPreparation = False |
|
142 self.apiPreparationCancelled.emit() |
|
143 |
|
144 def __apiPreparationStarted(self): |
|
145 """ |
|
146 Private method called, when the API preparation process started. |
|
147 """ |
|
148 self.__inPreparation = True |
|
149 self.apiPreparationStarted.emit() |
|
150 |
|
151 def prepareAPIs(self, ondemand=False, rawList=None): |
|
152 """ |
|
153 Public method to prepare the APIs if necessary. |
|
154 |
|
155 @param ondemand flag indicating a requested preparation (boolean) |
|
156 @param rawList list of raw API files (list of strings) |
|
157 """ |
|
158 if self.__apis is None or self.__inPreparation: |
|
159 return |
|
160 |
|
161 needsPreparation = False |
|
162 if ondemand: |
|
163 needsPreparation = True |
|
164 else: |
|
165 # check, if a new preparation is necessary |
|
166 preparedAPIs = self.__preparedName() |
|
167 if preparedAPIs: |
|
168 preparedAPIsInfo = QFileInfo(preparedAPIs) |
|
169 if not preparedAPIsInfo.exists(): |
|
170 needsPreparation = True |
|
171 else: |
|
172 preparedAPIsTime = preparedAPIsInfo.lastModified() |
|
173 apifiles = sorted(Preferences.getEditorAPI( |
|
174 self.__language, self.__projectType)) |
|
175 if self.__apifiles != apifiles: |
|
176 needsPreparation = True |
|
177 for apifile in apifiles: |
|
178 if ( |
|
179 QFileInfo(apifile).lastModified() > |
|
180 preparedAPIsTime |
|
181 ): |
|
182 needsPreparation = True |
|
183 break |
|
184 |
|
185 if needsPreparation: |
|
186 # do the preparation |
|
187 self.__apis.clear() |
|
188 if rawList: |
|
189 apifiles = rawList |
|
190 else: |
|
191 apifiles = Preferences.getEditorAPI( |
|
192 self.__language, self.__projectType) |
|
193 for apifile in apifiles: |
|
194 self.__apis.load(apifile) |
|
195 self.__apis.prepare() |
|
196 self.__apifiles = apifiles |
|
197 |
|
198 def cancelPreparation(self): |
|
199 """ |
|
200 Public slot to cancel the APIs preparation. |
|
201 """ |
|
202 self.__apis and self.__apis.cancelPreparation() |
|
203 |
|
204 def installedAPIFiles(self): |
|
205 """ |
|
206 Public method to get a list of installed API files. |
|
207 |
|
208 @return list of installed API files (list of strings) |
|
209 """ |
|
210 if self.__apis is not None: |
|
211 if Globals.isWindowsPlatform(): |
|
212 qsciPath = os.path.join( |
|
213 Globals.getPyQt5ModulesDirectory(), "qsci") |
|
214 if os.path.exists(qsciPath): |
|
215 # it's the installer |
|
216 if self.__lexer.lexerName() is not None: |
|
217 apidir = os.path.join(qsciPath, "api", |
|
218 self.__lexer.lexerName()) |
|
219 fnames = [] |
|
220 filist = QDir(apidir).entryInfoList( |
|
221 ["*.api"], QDir.Filter.Files, |
|
222 QDir.SortFlag.IgnoreCase) |
|
223 for fi in filist: |
|
224 fnames.append(fi.absoluteFilePath()) |
|
225 return fnames |
|
226 else: |
|
227 return [] |
|
228 |
|
229 return self.__apis.installedAPIFiles() |
|
230 else: |
|
231 return [] |
|
232 |
|
233 def __preparedName(self): |
|
234 """ |
|
235 Private method returning the default name of a prepared API file. |
|
236 |
|
237 @return complete filename for the Prepared APIs file (string) |
|
238 """ |
|
239 apisDir = os.path.join(Globals.getConfigDir(), "APIs") |
|
240 if self.__apis is not None: |
|
241 if self.__projectType: |
|
242 filename = "{0}_{1}.pap".format(self.__language, |
|
243 self.__projectType) |
|
244 else: |
|
245 filename = "{0}.pap".format(self.__language) |
|
246 return os.path.join(apisDir, filename) |
|
247 else: |
|
248 return "" |
|
249 |
|
250 |
|
251 class APIsManager(QObject): |
|
252 """ |
|
253 Class implementing the APIsManager class, which is the central store for |
|
254 API information used by autocompletion and calltips. |
|
255 """ |
|
256 def __init__(self, parent=None): |
|
257 """ |
|
258 Constructor |
|
259 |
|
260 @param parent reference to the parent object (QObject) |
|
261 """ |
|
262 super().__init__(parent) |
|
263 self.setObjectName("APIsManager") |
|
264 |
|
265 self.__apis = {} |
|
266 |
|
267 def reloadAPIs(self): |
|
268 """ |
|
269 Public slot to reload the api information. |
|
270 """ |
|
271 for api in list(self.__apis.values()): |
|
272 api and api.reloadAPIs() |
|
273 |
|
274 def getAPIs(self, language, projectType="", forPreparation=False): |
|
275 """ |
|
276 Public method to get an APIs object for autocompletion/calltips. |
|
277 |
|
278 This method creates and loads an APIs object dynamically upon request. |
|
279 This saves memory for languages, that might not be needed at the |
|
280 moment. |
|
281 |
|
282 @param language language of the requested APIs object |
|
283 @type str |
|
284 @param projectType type of the project |
|
285 @type str |
|
286 @param forPreparation flag indicating the requested APIs object is just |
|
287 needed for a preparation process |
|
288 @type bool |
|
289 @return reference to the APIs object |
|
290 @rtype APIs |
|
291 """ |
|
292 if forPreparation: |
|
293 return APIs(language, projectType=projectType, |
|
294 forPreparation=forPreparation) |
|
295 else: |
|
296 try: |
|
297 return self.__apis[(language, projectType)] |
|
298 except KeyError: |
|
299 if language in Lexers.getSupportedApiLanguages(): |
|
300 # create the api object |
|
301 self.__apis[(language, projectType)] = APIs( |
|
302 language, projectType=projectType) |
|
303 return self.__apis[(language, projectType)] |
|
304 else: |
|
305 return None |