AssistantEric/APIsManager.py

changeset 30
8f4d794d8ee0
parent 26
b48e3ff07482
child 32
68ef15fe34c3
equal deleted inserted replaced
29:402d146da2f1 30:8f4d794d8ee0
7 Module implementing the APIsManager. 7 Module implementing the APIsManager.
8 """ 8 """
9 9
10 import os 10 import os
11 11
12 from PyQt4.QtCore import * 12 from PyQt4.QtCore import QTimer, QThread, QFileInfo, pyqtSignal, QCoreApplication, \
13 QEvent, QDateTime, QObject, Qt
13 from PyQt4.QtSql import QSqlDatabase, QSqlQuery 14 from PyQt4.QtSql import QSqlDatabase, QSqlQuery
14 15
15 from E5Gui.E5Application import e5App 16 from E5Gui.E5Application import e5App
16 17
17 import QScintilla.Lexers 18 import QScintilla.Lexers
25 WorkerFinished = QEvent.User + 2002 26 WorkerFinished = QEvent.User + 2002
26 WorkerAborted = QEvent.User + 2003 27 WorkerAborted = QEvent.User + 2003
27 28
28 ApisNameProject = "__Project__" 29 ApisNameProject = "__Project__"
29 30
31
30 class DbAPIsWorker(QThread): 32 class DbAPIsWorker(QThread):
31 """ 33 """
32 Class implementing a worker thread to prepare the API database. 34 Class implementing a worker thread to prepare the API database.
33 """ 35 """
34 populate_api_stmt = """ 36 populate_api_stmt = """
35 INSERT INTO api (acWord, context, fullContext, signature, fileId, pictureId) 37 INSERT INTO api (acWord, context, fullContext, signature, fileId, pictureId)
36 VALUES (:acWord, :context, :fullContext, :signature, :fileId, :pictureId) 38 VALUES (:acWord, :context, :fullContext, :signature, :fileId, :pictureId)
37 """ 39 """
38 populate_del_api_stmt = """ 40 populate_del_api_stmt = """
39 DELETE FROM api WHERE fileId = :fileId 41 DELETE FROM api WHERE fileId = :fileId
40 """ 42 """
53 """ 55 """
54 file_delete_id_stmt = """ 56 file_delete_id_stmt = """
55 DELETE FROM file WHERE id = :id 57 DELETE FROM file WHERE id = :id
56 """ 58 """
57 59
58 def __init__(self, proxy, language, apiFiles, projectPath = "", refresh = False): 60 def __init__(self, proxy, language, apiFiles, projectPath="", refresh=False):
59 """ 61 """
60 Constructor 62 Constructor
61 63
62 @param proxy reference to the object that is proxied (DbAPIs) 64 @param proxy reference to the object that is proxied (DbAPIs)
63 @param language language of the APIs object (string) 65 @param language language of the APIs object (string)
69 QThread.__init__(self) 71 QThread.__init__(self)
70 72
71 self.setTerminationEnabled(True) 73 self.setTerminationEnabled(True)
72 74
73 # Get the AC word separators for all of the languages that the editor supports. 75 # Get the AC word separators for all of the languages that the editor supports.
74 # This has to be before we create a new thread, because access to GUI elements 76 # This has to be before we create a new thread, because access to GUI elements
75 # is not allowed from non-gui threads. 77 # is not allowed from non-gui threads.
76 self.__wseps = {} 78 self.__wseps = {}
77 for lang in QScintilla.Lexers.getSupportedLanguages(): 79 for lang in QScintilla.Lexers.getSupportedLanguages():
78 lexer = QScintilla.Lexers.getLexer(lang) 80 lexer = QScintilla.Lexers.getLexer(lang)
79 if lexer is not None: 81 if lexer is not None:
136 @param apiFile filename of the raw API file (string) 138 @param apiFile filename of the raw API file (string)
137 """ 139 """
138 if self.__language == ApisNameProject: 140 if self.__language == ApisNameProject:
139 try: 141 try:
140 module = Utilities.ModuleParser.readModule( 142 module = Utilities.ModuleParser.readModule(
141 os.path.join(self.__projectPath, apiFile), 143 os.path.join(self.__projectPath, apiFile),
142 basename = self.__projectPath + os.sep, 144 basename=self.__projectPath + os.sep,
143 caching = False) 145 caching=False)
144 language = module.getType() 146 language = module.getType()
145 if language: 147 if language:
146 apiGenerator = APIGenerator(module) 148 apiGenerator = APIGenerator(module)
147 apis = apiGenerator.genAPI(True, "", True) 149 apis = apiGenerator.genAPI(True, "", True)
148 else: 150 else:
323 if self.__aborted: 325 if self.__aborted:
324 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerAborted))) 326 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerAborted)))
325 else: 327 else:
326 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerFinished))) 328 QCoreApplication.postEvent(self.__proxy, QEvent(QEvent.Type(WorkerFinished)))
327 329
330
328 class DbAPIs(QObject): 331 class DbAPIs(QObject):
329 """ 332 """
330 Class implementing an API storage entity. 333 Class implementing an API storage entity.
331 334
332 @signal apiPreparationFinished() emitted after the API preparation has finished 335 @signal apiPreparationFinished() emitted after the API preparation has finished
333 @signal apiPreparationStarted() emitted after the API preparation has started 336 @signal apiPreparationStarted() emitted after the API preparation has started
334 @signal apiPreparationCancelled() emitted after the API preparation has been 337 @signal apiPreparationCancelled() emitted after the API preparation has been
335 cancelled 338 cancelled
336 """ 339 """
337 apiPreparationFinished = pyqtSignal() 340 apiPreparationFinished = pyqtSignal()
338 apiPreparationStarted = pyqtSignal() 341 apiPreparationStarted = pyqtSignal()
339 apiPreparationCancelled = pyqtSignal() 342 apiPreparationCancelled = pyqtSignal()
346 """ 349 """
347 drop_mgmt_stmt = """DROP TABLE IF EXISTS mgmt""" 350 drop_mgmt_stmt = """DROP TABLE IF EXISTS mgmt"""
348 351
349 create_api_stmt = """ 352 create_api_stmt = """
350 CREATE TABLE api 353 CREATE TABLE api
351 (acWord TEXT, 354 (acWord TEXT,
352 context TEXT, 355 context TEXT,
353 fullContext TEXT, 356 fullContext TEXT,
354 signature TEXT, 357 signature TEXT,
355 fileId INTEGER, 358 fileId INTEGER,
356 pictureId INTEGER, 359 pictureId INTEGER,
357 UNIQUE(acWord, fullContext, signature) ON CONFLICT IGNORE 360 UNIQUE(acWord, fullContext, signature) ON CONFLICT IGNORE
358 ) 361 )
383 api_files_stmt = """ 386 api_files_stmt = """
384 SELECT file FROM file 387 SELECT file FROM file
385 """ 388 """
386 389
387 ac_stmt = """ 390 ac_stmt = """
388 SELECT DISTINCT acWord, fullContext, pictureId FROM api 391 SELECT DISTINCT acWord, fullContext, pictureId FROM api
389 WHERE acWord GLOB :acWord 392 WHERE acWord GLOB :acWord
390 ORDER BY acWord 393 ORDER BY acWord
391 """ 394 """
392 ac_context_stmt = """ 395 ac_context_stmt = """
393 SELECT DISTINCT acWord, fullContext, pictureId FROM api 396 SELECT DISTINCT acWord, fullContext, pictureId FROM api
394 WHERE context = :context 397 WHERE context = :context
395 ORDER BY acWord 398 ORDER BY acWord
396 """ 399 """
397 ct_stmt = """ 400 ct_stmt = """
398 SELECT DISTINCT acWord, signature, fullContext FROM api 401 SELECT DISTINCT acWord, signature, fullContext FROM api
413 """ 416 """
414 mgmt_insert_stmt = """ 417 mgmt_insert_stmt = """
415 INSERT INTO mgmt (format) VALUES ({0:d}) 418 INSERT INTO mgmt (format) VALUES ({0:d})
416 """.format(DB_VERSION) 419 """.format(DB_VERSION)
417 420
418 def __init__(self, language, newStyle, parent = None): 421 def __init__(self, language, parent=None):
419 """ 422 """
420 Constructor 423 Constructor
421 424
422 @param language language of the APIs object (string) 425 @param language language of the APIs object (string)
423 @param newStyle flag indicating usage of new style signals (bool)
424 @param parent reference to the parent object (QObject) 426 @param parent reference to the parent object (QObject)
425 """ 427 """
426 QObject.__init__(self, parent) 428 QObject.__init__(self, parent)
427 self.setObjectName("DbAPIs_{0}".format(language)) 429 self.setObjectName("DbAPIs_{0}".format(language))
428 430
429 self.__newStyle = newStyle
430 self.__inPreparation = False 431 self.__inPreparation = False
431 self.__worker = None 432 self.__worker = None
432 self.__workerQueue = [] 433 self.__workerQueue = []
433 434
434 self.__language = language 435 self.__language = language
442 Private method to initialize as a project API object. 443 Private method to initialize as a project API object.
443 """ 444 """
444 self.__lexer = None 445 self.__lexer = None
445 446
446 self.__project = e5App().getObject("Project") 447 self.__project = e5App().getObject("Project")
447 if self.__newStyle: 448 self.__project.projectOpened.connect(self.__projectOpened)
448 self.__project.projectOpened.connect(self.__projectOpened) 449 self.__project.newProject.connect(self.__projectOpened)
449 self.__project.newProject.connect(self.__projectOpened) 450 self.__project.projectClosed.connect(self.__projectClosed)
450 self.__project.projectClosed.connect(self.__projectClosed)
451 else:
452 self.connect(self.__project, SIGNAL("projectOpened"), self.__projectOpened)
453 self.connect(self.__project, SIGNAL("newProject"), self.__projectOpened)
454 self.connect(self.__project, SIGNAL("projectClosed"), self.__projectClosed)
455 if self.__project.isOpen(): 451 if self.__project.isOpen():
456 self.__projectOpened() 452 self.__projectOpened()
457 453
458 def __initAsLanguage(self): 454 def __initAsLanguage(self):
459 """ 455 """
474 Protected method to determine the name of the database file. 470 Protected method to determine the name of the database file.
475 471
476 @return name of the database file (string) 472 @return name of the database file (string)
477 """ 473 """
478 if self.__language == ApisNameProject: 474 if self.__language == ApisNameProject:
479 return os.path.join(self.__project.getProjectManagementDir(), 475 return os.path.join(self.__project.getProjectManagementDir(),
480 "project-apis.db") 476 "project-apis.db")
481 else: 477 else:
482 apiDir = os.path.join(Utilities.getConfigDir(), "APIs") 478 apiDir = os.path.join(Utilities.getConfigDir(), "APIs")
483 if not os.path.exists(apiDir): 479 if not os.path.exists(apiDir):
484 os.makedirs(apiDir) 480 os.makedirs(apiDir)
587 finally: 583 finally:
588 del query 584 del query
589 db.commit() 585 db.commit()
590 return prepared 586 return prepared
591 587
592 def getCompletions(self, start = None, context = None): 588 def getCompletions(self, start=None, context=None):
593 """ 589 """
594 Public method to determine the possible completions. 590 Public method to determine the possible completions.
595 591
596 @keyparam start string giving the start of the word to be 592 @keyparam start string giving the start of the word to be
597 completed (string) 593 completed (string)
598 @keyparam context string giving the context (e.g. classname) 594 @keyparam context string giving the context (e.g. classname)
599 to be completed (string) 595 to be completed (string)
600 @return list of dictionaries with possible completions (key 'completion' 596 @return list of dictionaries with possible completions (key 'completion'
601 contains the completion (string), key 'context' 597 contains the completion (string), key 'context'
620 query.bindValue(":context", context) 616 query.bindValue(":context", context)
621 617
622 if query is not None: 618 if query is not None:
623 query.exec_() 619 query.exec_()
624 while query.next(): 620 while query.next():
625 completions.append({"completion" : query.value(0), 621 completions.append({"completion": query.value(0),
626 "context" : query.value(1), 622 "context": query.value(1),
627 "pictureId" : query.value(2)}) 623 "pictureId": query.value(2)})
628 del query 624 del query
629 finally: 625 finally:
630 db.commit() 626 db.commit()
631 627
632 return completions 628 return completions
633 629
634 def getCalltips(self, acWord, commas, context = None, fullContext = None, 630 def getCalltips(self, acWord, commas, context=None, fullContext=None,
635 showContext = True): 631 showContext=True):
636 """ 632 """
637 Public method to determine the calltips. 633 Public method to determine the calltips.
638 634
639 @param acWord function to get calltips for (string) 635 @param acWord function to get calltips for (string)
640 @param commas minimum number of commas contained in the calltip (integer) 636 @param commas minimum number of commas contained in the calltip (integer)
683 finally: 679 finally:
684 db.commit() 680 db.commit()
685 681
686 if context and len(calltips) == 0: 682 if context and len(calltips) == 0:
687 # nothing found, try without a context 683 # nothing found, try without a context
688 calltips = self.getCalltips(acWord, commas, showContext = showContext) 684 calltips = self.getCalltips(acWord, commas, showContext=showContext)
689 685
690 return calltips 686 return calltips
691 687
692 def __enoughCommas(self, s, commas): 688 def __enoughCommas(self, s, commas):
693 """ 689 """
714 self.__createApiDB() 710 self.__createApiDB()
715 711
716 # prepare the database if neccessary 712 # prepare the database if neccessary
717 self.prepareAPIs() 713 self.prepareAPIs()
718 714
719 def prepareAPIs(self, rawList = None): 715 def prepareAPIs(self, rawList=None):
720 """ 716 """
721 Public method to prepare the APIs if neccessary. 717 Public method to prepare the APIs if neccessary.
722 718
723 @keyparam rawList list of raw API files (list of strings) 719 @keyparam rawList list of raw API files (list of strings)
724 """ 720 """
748 if self.__language == ApisNameProject: 744 if self.__language == ApisNameProject:
749 projectPath = self.__project.getProjectPath() 745 projectPath = self.__project.getProjectPath()
750 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")] 746 apiFiles = [apiFiles[0].replace(projectPath + os.sep, "")]
751 else: 747 else:
752 projectPath = "" 748 projectPath = ""
753 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath, 749 self.__worker = DbAPIsWorker(self, self.__language, apiFiles, projectPath,
754 refresh = True) 750 refresh=True)
755 self.__worker.start() 751 self.__worker.start()
756 752
757 def getLexer(self): 753 def getLexer(self):
758 """ 754 """
759 Public method to return a reference to our lexer object. 755 Public method to return a reference to our lexer object.
779 @param evt reference to the event object (QEvent) 775 @param evt reference to the event object (QEvent)
780 @return flag indicating, if the event was handled (boolean) 776 @return flag indicating, if the event was handled (boolean)
781 """ 777 """
782 if evt.type() == WorkerStarted: 778 if evt.type() == WorkerStarted:
783 self.__inPreparation = True 779 self.__inPreparation = True
784 if self.__newStyle: 780 self.apiPreparationStarted.emit()
785 self.apiPreparationStarted.emit()
786 else:
787 self.emit(SIGNAL('apiPreparationStarted()'))
788 return True 781 return True
789 782
790 elif evt.type() == WorkerFinished: 783 elif evt.type() == WorkerFinished:
791 self.__inPreparation = False 784 self.__inPreparation = False
792 if self.__newStyle: 785 self.apiPreparationFinished.emit()
793 self.apiPreparationFinished.emit()
794 else:
795 self.emit(SIGNAL('apiPreparationFinished()'))
796 QTimer.singleShot(0, self.__processQueue) 786 QTimer.singleShot(0, self.__processQueue)
797 return True 787 return True
798 788
799 elif evt.type() == WorkerAborted: 789 elif evt.type() == WorkerAborted:
800 self.__inPreparation = False 790 self.__inPreparation = False
801 if self.__newStyle: 791 self.apiPreparationCancelled.emit()
802 self.apiPreparationCancelled.emit()
803 else:
804 self.emit(SIGNAL('apiPreparationCancelled()'))
805 QTimer.singleShot(0, self.__processQueue) 792 QTimer.singleShot(0, self.__processQueue)
806 return True 793 return True
807 794
808 else: 795 else:
809 return QObject.event(self, evt) 796 return QObject.event(self, evt)
837 """ 824 """
838 if self.__project.isProjectSource(filename): 825 if self.__project.isProjectSource(filename):
839 self.__workerQueue.append(filename) 826 self.__workerQueue.append(filename)
840 self.__processQueue() 827 self.__processQueue()
841 828
829
842 class APIsManager(QObject): 830 class APIsManager(QObject):
843 """ 831 """
844 Class implementing the APIsManager class, which is the central store for 832 Class implementing the APIsManager class, which is the central store for
845 API information used by autocompletion and calltips. 833 API information used by autocompletion and calltips.
846 """ 834 """
847 def __init__(self, newStyle, parent = None): 835 def __init__(self, parent=None):
848 """ 836 """
849 Constructor 837 Constructor
850 838
851 @param newStyle flag indicating usage of new style signals (bool)
852 @param parent reference to the parent object (QObject) 839 @param parent reference to the parent object (QObject)
853 """ 840 """
854 QObject.__init__(self, parent) 841 QObject.__init__(self, parent)
855 self.setObjectName("APIsManager") 842 self.setObjectName("APIsManager")
856 843
857 self.__newStyle = newStyle
858
859 # initialize the apis dictionary 844 # initialize the apis dictionary
860 self.__apis = {} 845 self.__apis = {}
861 846
862 def reloadAPIs(self): 847 def reloadAPIs(self):
863 """ 848 """
868 853
869 def getAPIs(self, language): 854 def getAPIs(self, language):
870 """ 855 """
871 Public method to get an apis object for autocompletion/calltips. 856 Public method to get an apis object for autocompletion/calltips.
872 857
873 This method creates and loads an APIs object dynamically upon request. 858 This method creates and loads an APIs object dynamically upon request.
874 This saves memory for languages, that might not be needed at the moment. 859 This saves memory for languages, that might not be needed at the moment.
875 860
876 @param language the language of the requested api object (string) 861 @param language the language of the requested api object (string)
877 @return the apis object (APIs) 862 @return the apis object (APIs)
878 """ 863 """
880 return self.__apis[language] 865 return self.__apis[language]
881 except KeyError: 866 except KeyError:
882 if language in QScintilla.Lexers.getSupportedLanguages() or \ 867 if language in QScintilla.Lexers.getSupportedLanguages() or \
883 language == ApisNameProject: 868 language == ApisNameProject:
884 # create the api object 869 # create the api object
885 self.__apis[language] = DbAPIs(language, self.__newStyle) 870 self.__apis[language] = DbAPIs(language)
886 return self.__apis[language] 871 return self.__apis[language]
887 else: 872 else:
888 return None 873 return None
889 874
890 def deactivate(self): 875 def deactivate(self):

eric ide

mercurial