11 import difflib |
11 import difflib |
12 |
12 |
13 from PyQt4.QtCore import QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, \ |
13 from PyQt4.QtCore import QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, \ |
14 pyqtSlot, QCryptographicHash, QEvent, QDateTime, QRegExp, Qt |
14 pyqtSlot, QCryptographicHash, QEvent, QDateTime, QRegExp, Qt |
15 from PyQt4.QtGui import QCursor, QPrinter, QPrintDialog, QLineEdit, QActionGroup, \ |
15 from PyQt4.QtGui import QCursor, QPrinter, QPrintDialog, QLineEdit, QActionGroup, \ |
16 QDialog, QAbstractPrintDialog, QInputDialog, QApplication, QMenu, QPalette, QFont |
16 QDialog, QAbstractPrintDialog, QInputDialog, QApplication, QMenu, QPalette, QFont, \ |
|
17 QPixmap |
17 from PyQt4.Qsci import QsciScintilla, QsciMacro, QsciStyledText |
18 from PyQt4.Qsci import QsciScintilla, QsciMacro, QsciStyledText |
18 |
19 |
19 from E5Gui.E5Application import e5App |
20 from E5Gui.E5Application import e5App |
20 from E5Gui import E5FileDialog, E5MessageBox |
21 from E5Gui import E5FileDialog, E5MessageBox |
21 |
22 |
73 function has been configured |
74 function has been configured |
74 @signal coverageMarkersShown(bool) emitted after the coverage markers have been |
75 @signal coverageMarkersShown(bool) emitted after the coverage markers have been |
75 shown or cleared |
76 shown or cleared |
76 @signal taskMarkersUpdated(QsciScintillaCompat) emitted when the task markers |
77 @signal taskMarkersUpdated(QsciScintillaCompat) emitted when the task markers |
77 were updated |
78 were updated |
|
79 @signal changeMarkersUpdated(QsciScintillaCompat) emitted when the change markers |
|
80 were updated |
78 @signal showMenu(str, QMenu, QsciScintillaCompat) emitted when a menu is about |
81 @signal showMenu(str, QMenu, QsciScintillaCompat) emitted when a menu is about |
79 to be shown. The name of the menu, a reference to the menu and a reference |
82 to be shown. The name of the menu, a reference to the menu and a reference |
80 to the editor are given. |
83 to the editor are given. |
81 @signal languageChanged(str) emitted when the editors language was set. The |
84 @signal languageChanged(str) emitted when the editors language was set. The |
82 language is passed as a parameter. |
85 language is passed as a parameter. |
99 bookmarkToggled = pyqtSignal(QsciScintillaCompat) |
102 bookmarkToggled = pyqtSignal(QsciScintillaCompat) |
100 syntaxerrorToggled = pyqtSignal(QsciScintillaCompat) |
103 syntaxerrorToggled = pyqtSignal(QsciScintillaCompat) |
101 autoCompletionAPIsAvailable = pyqtSignal(bool) |
104 autoCompletionAPIsAvailable = pyqtSignal(bool) |
102 coverageMarkersShown = pyqtSignal(bool) |
105 coverageMarkersShown = pyqtSignal(bool) |
103 taskMarkersUpdated = pyqtSignal(QsciScintillaCompat) |
106 taskMarkersUpdated = pyqtSignal(QsciScintillaCompat) |
|
107 changeMarkersUpdated = pyqtSignal(QsciScintillaCompat) |
104 showMenu = pyqtSignal(str, QMenu, QsciScintillaCompat) |
108 showMenu = pyqtSignal(str, QMenu, QsciScintillaCompat) |
105 languageChanged = pyqtSignal(str) |
109 languageChanged = pyqtSignal(str) |
106 eolChanged = pyqtSignal(str) |
110 eolChanged = pyqtSignal(str) |
107 encodingChanged = pyqtSignal(str) |
111 encodingChanged = pyqtSignal(str) |
108 lastEditPositionAvailable = pyqtSignal() |
112 lastEditPositionAvailable = pyqtSignal() |
411 self.__registerImages() |
418 self.__registerImages() |
412 |
419 |
413 # connect signals after loading the text |
420 # connect signals after loading the text |
414 self.textChanged.connect(self.__textChanged) |
421 self.textChanged.connect(self.__textChanged) |
415 |
422 |
416 # create the online syntax check timer |
423 # initialize the online syntax check timer |
417 self.__initOnlineSyntaxCheck() |
424 self.__initOnlineSyntaxCheck() |
|
425 |
|
426 # initialize the online change trace timer |
|
427 self.__initOnlineChangeTrace() |
418 |
428 |
419 if self.fileName and \ |
429 if self.fileName and \ |
420 self.project.isOpen() and \ |
430 self.project.isOpen() and \ |
421 self.project.isProjectSource(self.fileName): |
431 self.project.isProjectSource(self.fileName): |
422 self.project.projectPropertiesChanged.connect(self.__projectPropertiesChanged) |
432 self.project.projectPropertiesChanged.connect(self.__projectPropertiesChanged) |
1001 self.indicMarginMenu.addAction(self.trUtf8('Next task'), |
1011 self.indicMarginMenu.addAction(self.trUtf8('Next task'), |
1002 self.nextTask) |
1012 self.nextTask) |
1003 self.marginMenuActs["PreviousTaskMarker"] = \ |
1013 self.marginMenuActs["PreviousTaskMarker"] = \ |
1004 self.indicMarginMenu.addAction(self.trUtf8('Previous task'), |
1014 self.indicMarginMenu.addAction(self.trUtf8('Previous task'), |
1005 self.previousTask) |
1015 self.previousTask) |
|
1016 self.indicMarginMenu.addSeparator() |
|
1017 self.marginMenuActs["NextChangeMarker"] = \ |
|
1018 self.indicMarginMenu.addAction(self.trUtf8('Next change'), |
|
1019 self.nextChange) |
|
1020 self.marginMenuActs["PreviousChangeMarker"] = \ |
|
1021 self.indicMarginMenu.addAction(self.trUtf8('Previous change'), |
|
1022 self.previousChange) |
1006 |
1023 |
1007 self.indicMarginMenu.aboutToShow.connect(self.__showContextMenuMargin) |
1024 self.indicMarginMenu.aboutToShow.connect(self.__showContextMenuMargin) |
1008 |
1025 |
1009 def __initContextMenuUnifiedMargins(self): |
1026 def __initContextMenuUnifiedMargins(self): |
1010 """ |
1027 """ |
1081 self.nextTask) |
1098 self.nextTask) |
1082 self.marginMenuActs["PreviousTaskMarker"] = \ |
1099 self.marginMenuActs["PreviousTaskMarker"] = \ |
1083 self.marginMenu.addAction(self.trUtf8('Previous task'), |
1100 self.marginMenu.addAction(self.trUtf8('Previous task'), |
1084 self.previousTask) |
1101 self.previousTask) |
1085 self.marginMenu.addSeparator() |
1102 self.marginMenu.addSeparator() |
|
1103 self.marginMenuActs["NextChangeMarker"] = \ |
|
1104 self.marginMenu.addAction(self.trUtf8('Next change'), |
|
1105 self.nextChange) |
|
1106 self.marginMenuActs["PreviousChangeMarker"] = \ |
|
1107 self.marginMenu.addAction(self.trUtf8('Previous change'), |
|
1108 self.previousChange) |
|
1109 self.marginMenu.addSeparator() |
1086 self.marginMenuActs["LMBbookmarks"] = \ |
1110 self.marginMenuActs["LMBbookmarks"] = \ |
1087 self.marginMenu.addAction(self.trUtf8('LMB toggles bookmarks'), |
1111 self.marginMenu.addAction(self.trUtf8('LMB toggles bookmarks'), |
1088 self.__lmBbookmarks) |
1112 self.__lmBbookmarks) |
1089 self.marginMenuActs["LMBbookmarks"].setCheckable(True) |
1113 self.marginMenuActs["LMBbookmarks"].setCheckable(True) |
1090 self.marginMenuActs["LMBbookmarks"].setChecked(False) |
1114 self.marginMenuActs["LMBbookmarks"].setChecked(False) |
2360 shouldBreak = True |
2384 shouldBreak = True |
2361 break |
2385 break |
2362 if shouldBreak: |
2386 if shouldBreak: |
2363 break |
2387 break |
2364 self.taskMarkersUpdated.emit(self) |
2388 self.taskMarkersUpdated.emit(self) |
|
2389 |
|
2390 ############################################################################ |
|
2391 ## Change tracing methods below |
|
2392 ############################################################################ |
|
2393 |
|
2394 def __initOnlineChangeTrace(self): |
|
2395 """ |
|
2396 Private slot to initialize the online change trace. |
|
2397 """ |
|
2398 self.__hasChangeMarkers = False |
|
2399 self.__oldText = self.text() |
|
2400 self.__onlineChangeTraceTimer = QTimer(self) |
|
2401 self.__onlineChangeTraceTimer.setSingleShot(True) |
|
2402 self.__onlineChangeTraceTimer.setInterval( |
|
2403 Preferences.getEditor("OnlineChangeTraceInterval")) |
|
2404 self.__onlineChangeTraceTimer.timeout.connect( |
|
2405 self.__onlineChangeTraceTimerTimeout) |
|
2406 self.textChanged.connect(self.__resetOnlineChangeTraceTimer) |
|
2407 |
|
2408 def __resetOnlineChangeTraceTimer(self): |
|
2409 """ |
|
2410 Private method to reset the online syntax check timer. |
|
2411 """ |
|
2412 if Preferences.getEditor("OnlineChangeTrace"): |
|
2413 self.__onlineChangeTraceTimer.stop() |
|
2414 self.__onlineChangeTraceTimer.start() |
|
2415 |
|
2416 def __onlineChangeTraceTimerTimeout(self): |
|
2417 """ |
|
2418 Private slot to mark added and changed lines. |
|
2419 """ |
|
2420 self.__deleteAllChangeMarkers() |
|
2421 |
|
2422 oldL = self.__oldText.splitlines() |
|
2423 newL = self.text().splitlines() |
|
2424 matcher = difflib.SequenceMatcher(None, oldL, newL) |
|
2425 |
|
2426 for token, i1, i2, j1, j2 in matcher.get_opcodes(): |
|
2427 if token in ["insert", "replace"]: |
|
2428 for lineNo in range(j1, j2): |
|
2429 self.markerAdd(lineNo, self.__changeMarker) |
|
2430 self.__hasChangeMarkers = True |
|
2431 |
|
2432 if self.__hasChangeMarkers: |
|
2433 self.changeMarkersUpdated.emit(self) |
|
2434 |
|
2435 def __resetOnlineChangeTraceInfo(self): |
|
2436 """ |
|
2437 Private slot to reset the online change trace info. |
|
2438 """ |
|
2439 self.__oldText = self.text() |
|
2440 self.__deleteAllChangeMarkers() |
|
2441 |
|
2442 def __deleteAllChangeMarkers(self): |
|
2443 """ |
|
2444 Private slot to delete all change markers. |
|
2445 """ |
|
2446 self.markerDeleteAll(self.__changeMarker) |
|
2447 self.__hasChangeMarkers = False |
|
2448 self.changeMarkersUpdated.emit(self) |
|
2449 |
|
2450 def hasChangeMarkers(self): |
|
2451 """ |
|
2452 Public method to determine, if this editor contains any change markers. |
|
2453 |
|
2454 @return flag indicating the presence of change markers (boolean) |
|
2455 """ |
|
2456 return self.__hasChangeMarkers |
|
2457 |
|
2458 def nextChange(self): |
|
2459 """ |
|
2460 Public slot to handle the 'Next change' context menu action. |
|
2461 """ |
|
2462 line, index = self.getCursorPosition() |
|
2463 if line == self.lines() - 1: |
|
2464 line = 0 |
|
2465 else: |
|
2466 line += 1 |
|
2467 changeline = self.markerFindNext(line, 1 << self.__changeMarker) |
|
2468 if changeline < 0: |
|
2469 # wrap around |
|
2470 changeline = self.markerFindNext(0, 1 << self.__changeMarker) |
|
2471 if changeline >= 0: |
|
2472 self.setCursorPosition(changeline, 0) |
|
2473 self.ensureLineVisible(changeline) |
|
2474 |
|
2475 def previousChange(self): |
|
2476 """ |
|
2477 Public slot to handle the 'Previous task' context menu action. |
|
2478 """ |
|
2479 line, index = self.getCursorPosition() |
|
2480 if line == 0: |
|
2481 line = self.lines() - 1 |
|
2482 else: |
|
2483 line -= 1 |
|
2484 changeline = self.markerFindPrevious(line, 1 << self.__changeMarker) |
|
2485 if changeline < 0: |
|
2486 # wrap around |
|
2487 changeline = self.markerFindPrevious( |
|
2488 self.lines() - 1, 1 << self.__changeMarker) |
|
2489 if changeline >= 0: |
|
2490 self.setCursorPosition(changeline, 0) |
|
2491 self.ensureLineVisible(changeline) |
2365 |
2492 |
2366 ############################################################################ |
2493 ############################################################################ |
2367 ## Flags handling methods below |
2494 ## Flags handling methods below |
2368 ############################################################################ |
2495 ############################################################################ |
2369 |
2496 |
3521 self.__onlineSyntaxCheckTimer.setInterval( |
3649 self.__onlineSyntaxCheckTimer.setInterval( |
3522 Preferences.getEditor("OnlineSyntaxCheckInterval") * 1000) |
3650 Preferences.getEditor("OnlineSyntaxCheckInterval") * 1000) |
3523 else: |
3651 else: |
3524 self.__onlineSyntaxCheckTimer.stop() |
3652 self.__onlineSyntaxCheckTimer.stop() |
3525 |
3653 |
|
3654 if Preferences.getEditor("OnlineChangeTrace"): |
|
3655 self.__onlineChangeTraceTimer.setInterval( |
|
3656 Preferences.getEditor("OnlineChangeTraceInterval")) |
|
3657 else: |
|
3658 self.__onlineChangeTraceTimer.stop() |
|
3659 self.__deleteAllChangeMarkers() |
|
3660 changePixmap = QPixmap(16, 16) |
|
3661 changePixmap.fill(Preferences.getEditorColour("OnlineChangeTraceMarker")) |
|
3662 self.markerDefine(changePixmap, self.__changeMarker) |
|
3663 |
3526 # refresh the annotations display |
3664 # refresh the annotations display |
3527 self.__refreshAnnotations() |
3665 self.__refreshAnnotations() |
3528 |
3666 |
3529 def __setLineMarkerColours(self): |
3667 def __setLineMarkerColours(self): |
3530 """ |
3668 """ |
3566 (1 << self.errorline) | \ |
3704 (1 << self.errorline) | \ |
3567 (1 << self.bookmark) | \ |
3705 (1 << self.bookmark) | \ |
3568 (1 << self.syntaxerror) | \ |
3706 (1 << self.syntaxerror) | \ |
3569 (1 << self.notcovered) | \ |
3707 (1 << self.notcovered) | \ |
3570 (1 << self.taskmarker) | \ |
3708 (1 << self.taskmarker) | \ |
3571 (1 << self.warning) |
3709 (1 << self.warning) | \ |
|
3710 (1 << self.__changeMarker) |
3572 self.setMarginWidth(1, 16) |
3711 self.setMarginWidth(1, 16) |
3573 self.setMarginSensitivity(1, True) |
3712 self.setMarginSensitivity(1, True) |
3574 self.setMarginMarkerMask(1, margin1Mask) |
3713 self.setMarginMarkerMask(1, margin1Mask) |
3575 |
3714 |
3576 self.__linenoMargin = 0 |
3715 self.__linenoMargin = 0 |
3600 self.setMarginMarkerMask(self.__bpMargin, marginBpMask) |
3739 self.setMarginMarkerMask(self.__bpMargin, marginBpMask) |
3601 |
3740 |
3602 marginIndicMask = (1 << self.syntaxerror) | \ |
3741 marginIndicMask = (1 << self.syntaxerror) | \ |
3603 (1 << self.notcovered) | \ |
3742 (1 << self.notcovered) | \ |
3604 (1 << self.taskmarker) | \ |
3743 (1 << self.taskmarker) | \ |
3605 (1 << self.warning) |
3744 (1 << self.warning) | \ |
|
3745 (1 << self.__changeMarker) |
3606 self.setMarginWidth(self.__indicMargin, 16) |
3746 self.setMarginWidth(self.__indicMargin, 16) |
3607 self.setMarginSensitivity(self.__indicMargin, True) |
3747 self.setMarginSensitivity(self.__indicMargin, True) |
3608 self.setMarginMarkerMask(self.__indicMargin, marginIndicMask) |
3748 self.setMarginMarkerMask(self.__indicMargin, marginIndicMask) |
3609 |
3749 |
3610 # set linenumber margin settings |
3750 # set linenumber margin settings |
4394 self.marginMenuActs["PreviousTaskMarker"].setEnabled(True) |
4534 self.marginMenuActs["PreviousTaskMarker"].setEnabled(True) |
4395 self.marginMenuActs["NextTaskMarker"].setEnabled(True) |
4535 self.marginMenuActs["NextTaskMarker"].setEnabled(True) |
4396 else: |
4536 else: |
4397 self.marginMenuActs["PreviousTaskMarker"].setEnabled(False) |
4537 self.marginMenuActs["PreviousTaskMarker"].setEnabled(False) |
4398 self.marginMenuActs["NextTaskMarker"].setEnabled(False) |
4538 self.marginMenuActs["NextTaskMarker"].setEnabled(False) |
|
4539 |
|
4540 if self.__hasChangeMarkers: |
|
4541 self.marginMenuActs["PreviousChangeMarker"].setEnabled(True) |
|
4542 self.marginMenuActs["NextChangeMarker"].setEnabled(True) |
|
4543 else: |
|
4544 self.marginMenuActs["PreviousChangeMarker"].setEnabled(False) |
|
4545 self.marginMenuActs["NextChangeMarker"].setEnabled(False) |
4399 |
4546 |
4400 self.showMenu.emit("Margin", self.sender(), self) |
4547 self.showMenu.emit("Margin", self.sender(), self) |
4401 |
4548 |
4402 def __showContextMenuChecks(self): |
4549 def __showContextMenuChecks(self): |
4403 """ |
4550 """ |