Sun, 17 Sep 2017 17:03:16 +0200
Implemented the distributed History dialog and moved the Undo/Redo functions to this dialog.
--- a/PluginRope.e4p Sat Sep 16 18:51:19 2017 +0200 +++ b/PluginRope.e4p Sun Sep 17 17:03:16 2017 +0200 @@ -211,6 +211,7 @@ <Form>RefactoringRope/ExtractDialog.ui</Form> <Form>RefactoringRope/GetterSetterDialog.ui</Form> <Form>RefactoringRope/HelpDialog.ui</Form> + <Form>RefactoringRope/HistoryDialog.ui</Form> <Form>RefactoringRope/InlineArgumentDefaultDialog.ui</Form> <Form>RefactoringRope/InlineDialog.ui</Form> <Form>RefactoringRope/IntroduceFactoryDialog.ui</Form>
--- a/RefactoringRope/HistoryDialog.py Sat Sep 16 18:51:19 2017 +0200 +++ b/RefactoringRope/HistoryDialog.py Sun Sep 17 17:03:16 2017 +0200 @@ -9,127 +9,308 @@ from __future__ import unicode_literals -from PyQt5.QtCore import Qt, pyqtSlot -from PyQt5.QtWidgets import QDialogButtonBox, QListWidgetItem, QApplication +from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel +from PyQt5.QtGui import QBrush, QColor, QTextCursor +from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, \ + QAbstractButton from E5Gui.E5Application import e5App from E5Gui import E5MessageBox -from PreviewDialogBase import PreviewDialogBase +from Ui_HistoryDialog import Ui_HistoryDialog +import Globals import Utilities -class HistoryDialog(PreviewDialogBase): +class HistoryDialog(QDialog, Ui_HistoryDialog): """ Class implementing the History dialog. """ ChangeIDRole = Qt.UserRole - def __init__(self, refactoring, changes, isUndo, parent=None): + def __init__(self, refactoring, filename="", parent=None): """ Constructor @param refactoring reference to the main refactoring object - (Refactoring) - @param changes list of ChangeSet objects - (list of rope.base.change.ChangeSet) - @param isUndo flag indicating an undo history dialog (boolean) - @param parent reference to the parent widget (QWidget) + @type Refactoring + @param filename name of the file to show the history for + @type str + @param parent reference to the parent widget + @type QWidget """ - PreviewDialogBase.__init__(self, parent) + QDialog.__init__(self, parent) + self.setupUi(self) + self.setWindowFlags(Qt.Window) + + if Globals.isWindowsPlatform(): + self.previewEdit.setFontFamily("Lucida Console") + else: + self.previewEdit.setFontFamily("Monospace") + + self.formats = {} + self.formats[' '] = self.previewEdit.currentCharFormat() + charFormat = self.previewEdit.currentCharFormat() + charFormat.setBackground(QBrush(QColor(190, 237, 190))) + self.formats['+'] = charFormat + charFormat = self.previewEdit.currentCharFormat() + charFormat.setBackground(QBrush(QColor(237, 190, 190))) + self.formats['-'] = charFormat + charFormat = self.previewEdit.currentCharFormat() + charFormat.setBackground(QBrush(QColor(190, 190, 237))) + self.formats['@'] = charFormat + charFormat = self.previewEdit.currentCharFormat() + charFormat.setBackground(QBrush(QColor(124, 124, 124))) + self.formats['?'] = charFormat + charFormat = self.previewEdit.currentCharFormat() + charFormat.setBackground(QBrush(QColor(190, 190, 190))) + self.formats['='] = charFormat self.__refactoring = refactoring - self.__isUndo = isUndo + self.__filename = filename + + if not filename: + self.header.setText(self.tr("<b>Project History</b>")) + else: + self.header.setText(self.tr("<b>File History: {0}</b>").format( + filename)) - if self.__isUndo: - self.__actionButton = self.buttonBox.addButton( - self.tr("&Undo"), - QDialogButtonBox.AcceptRole) - self.description.setText(self.tr("Undoable Changes")) - title = self.tr("Undo History") - else: - self.__actionButton = self.buttonBox.addButton( - self.tr("&Redo"), - QDialogButtonBox.AcceptRole) - self.description.setText(self.tr("Redoable Changes")) - title = self.tr("Redo History") - self.buttonBox.addButton(QDialogButtonBox.Close) - self.setWindowTitle(title) + self.__undoButton = self.buttonBox.addButton( + self.tr("&Undo"), QDialogButtonBox.ActionRole) + self.__redoButton = self.buttonBox.addButton( + self.tr("&Redo"), QDialogButtonBox.ActionRole) + self.__refreshButton = self.buttonBox.addButton( + self.tr("Re&fresh"), QDialogButtonBox.ActionRole) + self.__clearButton = self.buttonBox.addButton( + self.tr("&Clear History"), QDialogButtonBox.ActionRole) # populate the list - self.__changes = {} - for change in changes: - self.__changes[id(change)] = change - itm = QListWidgetItem(str(change), self.changesList) - itm.setData(HistoryDialog.ChangeIDRole, id(change)) - if self.changesList.count() > 0: - self.changesList.item(0).setSelected(True) + self.__refreshHistories() + + def __appendText(self, txt, charFormat): + """ + Private method to append text to the end of the preview pane. + + @param txt text to insert + @type str + @param charFormat text format to be used + @type QTextCharFormat + """ + tc = self.previewEdit.textCursor() + tc.movePosition(QTextCursor.End) + self.previewEdit.setTextCursor(tc) + self.previewEdit.setCurrentCharFormat(charFormat) + self.previewEdit.insertPlainText(txt) - @pyqtSlot(QListWidgetItem, QListWidgetItem) - def on_changesList_currentItemChanged(self, current, previous): + @pyqtSlot(QAbstractButton) + def on_buttonBox_clicked(self, button): + """ + Private slot handling the selection of a dialog button. + + @param button reference to the button clicked + @type QAbstractButton """ - Private slot called when a change is selected. + if button == QDialogButtonBox.Close: + self.close() + elif button == self.__undoButton: + self.__undoChanges() + elif button == self.__redoButton: + self.__redoChanges() + elif button == self.__refreshButton: + self.__refreshHistories() + elif button == self.__clearButton: + self.__clearHistory() + + def __currentItemChanged(self, current): + """ + Private method to request change data of an item. - @param current reference to the new current item (QListWidgetItem) - @param previous reference to the old current item (QListWidgetItem) + @param current reference to the item to get change data for + @type QListWidgetItem """ if current is None: return self.previewEdit.clear() changeId = current.data(HistoryDialog.ChangeIDRole) - change = self.__changes[changeId] - for line in change.get_description().splitlines(True): - try: - charFormat = self.formats[line[0]] - except (IndexError, KeyError): - charFormat = self.formats[' '] - self._appendText(line, charFormat) + self.__refactoring.sendJson("History", { + "Subcommand": "GetChange", + "Id": changeId, + }) + + @pyqtSlot(QListWidgetItem, QListWidgetItem) + def on_redoChangesList_currentItemChanged(self, current, previous): + """ + Private slot handling a change of the current redo change. + + @param current reference to the new current redo item + @type QListWidgetItem + @param previous reference to the previous current redo item + @type QListWidgetItem + """ + self.__redoButton.setEnabled(current is not None) + self.__currentItemChanged(current) - def accept(self): + @pyqtSlot(QListWidgetItem) + def on_redoChangesList_itemClicked(self, item): + """ + Private slot handling a click on a redo entry. + + @param item reference to the clicked item + @type QListWidgetItem """ - Public slot to undo the selected set of changes. + self.__currentItemChanged(item) + + @pyqtSlot(QListWidgetItem, QListWidgetItem) + def on_undoChangesList_currentItemChanged(self, current, previous): + """ + Private slot handling a change of the current undo change. + + @param current reference to the new current undo item + @type QListWidgetItem + @param previous reference to the previous current undo item + @type QListWidgetItem """ - changeId = self.changesList.currentItem()\ - .data(HistoryDialog.ChangeIDRole) - change = self.__changes[changeId] + self.__undoButton.setEnabled(current is not None) + self.__currentItemChanged(current) + + @pyqtSlot(QListWidgetItem) + def on_undoChangesList_itemClicked(self, item): + """ + Private slot handling a click on an undo entry. - if self.__isUndo: - res = E5MessageBox.yesNo( - None, - self.tr("Undo refactorings"), - self.tr("""Shall all refactorings up to <b>{0}</b>""" - """ be undone?""") - .format(Utilities.html_encode(str(change)))) - else: - res = E5MessageBox.yesNo( - None, - self.tr("Redo refactorings"), - self.tr("""Shall all refactorings up to <b>{0}</b>""" - """ be redone?""") - .format(Utilities.html_encode(str(change)))) + @param item reference to the clicked item + @type QListWidgetItem + """ + self.__currentItemChanged(item) + + def __undoChanges(self): + """ + Private method to undo the selected set of changes. + """ + currentUndoItem = self.undoChangesList.currentItem() + change = currentUndoItem.text() + changeId = currentUndoItem.data(HistoryDialog.ChangeIDRole) + res = E5MessageBox.yesNo( + None, + self.tr("Undo refactorings"), + self.tr("""Shall all refactorings up to <b>{0}</b>""" + """ be undone?""") + .format(Utilities.html_encode(change))) + if res: + if not self.__refactoring.confirmAllBuffersSaved(): + return + + self.__refactoring.sendJson("History", { + "Subcommand": "Undo", + "Id": changeId, + }) + + def __redoChanges(self): + """ + Private method to redo the selected set of changes. + """ + currentRedoItem = self.redoChangesList.currentItem() + change = currentRedoItem.text() + changeId = currentRedoItem.data(HistoryDialog.ChangeIDRole) + res = E5MessageBox.yesNo( + None, + self.tr("Redo refactorings"), + self.tr("""Shall all refactorings up to <b>{0}</b>""" + """ be redone?""") + .format(Utilities.html_encode(change))) if res: if not self.__refactoring.confirmAllBuffersSaved(): return - from ProgressHandle import ProgressHandle - handle = ProgressHandle(change.description, False, self) - handle.show() - QApplication.processEvents() - if self.__isUndo: - self.__refactoring.getProject().history.undo( - change, task_handle=handle) - else: - self.__refactoring.getProject().history.redo( - change, task_handle=handle) - handle.reset() + self.__refactoring.sendJson("History", { + "Subcommand": "Redo", + "Id": changeId, + }) + + def __refreshHistories(self): + """ + Private method to refresh the undo and redo history lists. + """ + self.__undoButton.setEnabled(False) + self.__redoButton.setEnabled(False) + self.__refreshButton.setEnabled(False) + self.__clearButton.setEnabled(False) + + self.undoChangesList.clear() + self.redoChangesList.clear() + self.previewEdit.clear() + + self.__refactoring.sendJson("History", { + "Subcommand": "Get", + "Filename": self.__filename + }) + + def __clearHistory(self): + """ + Private method to clear the refactoring history. + """ + res = E5MessageBox.yesNo( + None, + self.tr("Clear History"), + self.tr("Do you really want to clear the refactoring history?")) + if res: + self.sendJson("History", { + "Subcommand": "Clear", + }) - self.__refactoring.refreshEditors(change) + self.historyCleared() + + def historyCleared(self): + """ + Public method to indicate, that the refactoring history was cleared + through the menu. + """ + self.__refreshHistories() + + def processHistoryCommand(self, data): + """ + Public method to process the data sent by the refactoring client. + + @param data dictionary containing the history data + @type dict + """ + subcommand = data["Subcommand"] + if subcommand == "Histories": + for change, changeId in data["Undo"]: + itm = QListWidgetItem(change, self.undoChangesList) + itm.setData(HistoryDialog.ChangeIDRole, changeId) + for change, changeId in data["Redo"]: + itm = QListWidgetItem(change, self.redoChangesList) + itm.setData(HistoryDialog.ChangeIDRole, changeId) + if self.undoChangesList.count() > 0: + self.undoChangesList.setCurrentItem( + self.undoChangesList.item(0), + QItemSelectionModel.Select) + elif self.redoChangesList.count() > 0: + self.redoChangesList.setCurrentItem( + self.redoChangesList.item(0), + QItemSelectionModel.Select) + + self.__refreshButton.setEnabled(True) + if self.undoChangesList.count() > 0 or \ + self.redoChangesList.count() > 0: + self.__clearButton.setEnabled(True) + + elif subcommand == "ChangeDescription": + for line in data["Description"].splitlines(True): + try: + charFormat = self.formats[line[0]] + except (IndexError, KeyError): + charFormat = self.formats[' '] + self.__appendText(line, charFormat) + + elif subcommand in ["Undo", "Redo"]: + self.__refactoring.refreshEditors(data["ChangedFiles"]) p = e5App().getObject("Project") if p.isDirty(): p.saveProject() - PreviewDialogBase.accept(self) - else: - self.reject() + self.raise_() + self.__refreshHistories()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RefactoringRope/HistoryDialog.ui Sun Sep 17 17:03:16 2017 +0200 @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>HistoryDialog</class> + <widget class="QDialog" name="HistoryDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>Refactoring History</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLabel" name="header"> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QSplitter" name="splitter_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSplitter" name="splitter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QWidget" name=""> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="description"> + <property name="text"> + <string>Undo History</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="undoChangesList"> + <property name="toolTip"> + <string>Select a change to preview on the right</string> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name=""> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="description_2"> + <property name="text"> + <string>Redo History</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="redoChangesList"> + <property name="toolTip"> + <string>Select a change to preview on the right</string> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QTextEdit" name="previewEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + <property name="lineWrapMode"> + <enum>QTextEdit::NoWrap</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>undoChangesList</tabstop> + <tabstop>redoChangesList</tabstop> + <tabstop>previewEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>HistoryDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>227</x> + <y>578</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>HistoryDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>295</x> + <y>584</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- a/RefactoringRope/JsonClient.py Sat Sep 16 18:51:19 2017 +0200 +++ b/RefactoringRope/JsonClient.py Sun Sep 17 17:03:16 2017 +0200 @@ -59,7 +59,8 @@ Private method to receive a JSON encode command and data from the server. """ - line = self.__connection.recv(1024 * 1024, socket.MSG_PEEK) # 1M buffer + line = self.__connection.recv(1024 * 1024, socket.MSG_PEEK) + # 1MB buffer eol = line.find(b'\n') @@ -139,7 +140,7 @@ def poll(self): """ - Public method to check and receive one message (if available) + Public method to check and receive one message (if available). """ try: rrdy, wrdy, xrdy = select.select([self.__connection], [], [], 0)
--- a/RefactoringRope/JsonServer.py Sat Sep 16 18:51:19 2017 +0200 +++ b/RefactoringRope/JsonServer.py Sun Sep 17 17:03:16 2017 +0200 @@ -11,7 +11,7 @@ import json -from PyQt5.QtCore import pyqtSlot, QProcess, QThread +from PyQt5.QtCore import pyqtSlot, QProcess from PyQt5.QtNetwork import QTcpServer, QHostAddress from E5Gui import E5MessageBox @@ -133,7 +133,7 @@ """ pass - def sendJson(self, command, params): + def sendJson(self, command, params, flush=False): """ Public method to send a single refactoring command to the client. @@ -141,6 +141,8 @@ @type str @param params dictionary of named parameters for the command @type dict + @param flush flag indicating to flush the data to the socket + @type bool """ commandDict = { "jsonrpc": "2.0", @@ -151,6 +153,8 @@ if self.__connection is not None: self.__connection.write(cmd.encode('utf8', 'backslashreplace')) + if flush: + self.__connection.flush() def startClient(self, interpreter, clientScript, clientArgs): """ @@ -183,12 +187,10 @@ """ Public method to stop the client process. """ - self.sendJson("Exit", {}) + self.sendJson("Exit", {}, flush=True) - QThread.msleep(200) + if self.__connection is not None: + self.__connection.waitForDisconnected() self.__clientProcess.close() self.__clientProcess = None - -# -# eflag: noqa = M801
--- a/RefactoringRope/Refactoring.py Sat Sep 16 18:51:19 2017 +0200 +++ b/RefactoringRope/Refactoring.py Sun Sep 17 17:03:16 2017 +0200 @@ -60,8 +60,9 @@ self.__ropeConfig = {} self.__mainMenu = None + self.__progressDialog = None self.__helpDialog = None - self.__progressDialog = None + self.__historyDialog = None from FileSystemCommands import E5FileSystemCommands self.__fsCommands = E5FileSystemCommands(self.__e5project) @@ -77,6 +78,8 @@ "FileSystemCommand": self.__fsCommands.processFileSystemCommand, "ClientException": self.__processClientException, + + "HistoryResult": self.__processHistoryResult, } def initActions(self): @@ -489,102 +492,40 @@ self.actions.append(self.refactoringMethodToMethodObjectAct) ##################################################### - ## Undo/Redo actions + ## History actions ##################################################### - self.refactoringUndoAct = E5Action( - self.tr('Undo'), - self.tr('&Undo'), - 0, 0, - self, 'refactoring_undo') - self.refactoringUndoAct.setStatusTip(self.tr( - 'Undo the last refactoring')) - self.refactoringUndoAct.setWhatsThis(self.tr( - """<b>Undo</b>""" - """<p>Undo the last refactoring.</p>""" - )) - self.refactoringUndoAct.triggered.connect( - self.__undo) - self.actions.append(self.refactoringUndoAct) - - self.refactoringRedoAct = E5Action( - self.tr('Redo'), - self.tr('Re&do'), + self.refactoringProjectHistoryAct = E5Action( + self.tr('Show Project History'), + self.tr('Show Project History...'), 0, 0, - self, 'refactoring_redo') - self.refactoringRedoAct.setStatusTip(self.tr( - 'Redo the last refactoring')) - self.refactoringRedoAct.setWhatsThis(self.tr( - """<b>Redo</b>""" - """<p>Redo the last refactoring.</p>""" - )) - self.refactoringRedoAct.triggered.connect( - self.__redo) - self.actions.append(self.refactoringRedoAct) - - self.refactoringUndoHistoryAct = E5Action( - self.tr('Show Project Undo History'), - self.tr('Show Project Undo History'), - 0, 0, - self, 'refactoring_show_project_undo_history') - self.refactoringUndoHistoryAct.setStatusTip(self.tr( - 'Show the undo history of the project')) - self.refactoringUndoHistoryAct.setWhatsThis(self.tr( - """<b>Show Project Undo History</b>""" - """<p>Opens a dialog to show the undo history list of""" + self, 'refactoring_show_project_history') + self.refactoringProjectHistoryAct.setStatusTip(self.tr( + 'Show the refactoring history of the project')) + self.refactoringProjectHistoryAct.setWhatsThis(self.tr( + """<b>Show Project History</b>""" + """<p>This opens a dialog to show the refactoring history of""" """ the project.</p>""" )) - self.refactoringUndoHistoryAct.triggered.connect( - self.__showProjectUndoHistory) - self.actions.append(self.refactoringUndoHistoryAct) + self.refactoringProjectHistoryAct.triggered.connect( + self.__showProjectHistory) + self.actions.append(self.refactoringProjectHistoryAct) - self.refactoringRedoHistoryAct = E5Action( - self.tr('Show Project Redo History'), - self.tr('Show Project Redo History'), + self.refactoringFileHistoryAct = E5Action( + self.tr('Show Current File History'), + self.tr('Show Current File History...'), 0, 0, - self, 'refactoring_show_project_redo_history') - self.refactoringRedoHistoryAct.setStatusTip(self.tr( - 'Show the redo history of the project')) - self.refactoringRedoHistoryAct.setWhatsThis(self.tr( - """<b>Show Project Redo History</b>""" - """<p>Opens a dialog to show the redo history list of""" - """ the project.</p>""" - )) - self.refactoringRedoHistoryAct.triggered.connect( - self.__showProjectRedoHistory) - self.actions.append(self.refactoringRedoHistoryAct) - - self.refactoringUndoFileHistoryAct = E5Action( - self.tr('Show Current File Undo History'), - self.tr('Show Current File Undo History'), - 0, 0, - self, 'refactoring_show_file_undo_history') - self.refactoringUndoFileHistoryAct.setStatusTip(self.tr( - 'Show the undo history of the current file')) - self.refactoringUndoFileHistoryAct.setWhatsThis(self.tr( - """<b>Show Current File Undo History</b>""" - """<p>Opens a dialog to show the undo history list of""" + self, 'refactoring_show_file_history') + self.refactoringFileHistoryAct.setStatusTip(self.tr( + 'Show the refactoring history of the current file')) + self.refactoringFileHistoryAct.setWhatsThis(self.tr( + """<b>Show Current File History</b>""" + """<p>This opens a dialog to show the refactoring history of""" """ the current file.</p>""" )) - self.refactoringUndoFileHistoryAct.triggered.connect( - self.__showFileUndoHistory) - self.actions.append(self.refactoringUndoFileHistoryAct) - - self.refactoringRedoFileHistoryAct = E5Action( - self.tr('Show Current File Redo History'), - self.tr('Show Current File Redo History'), - 0, 0, - self, 'refactoring_show_file_redo_history') - self.refactoringRedoFileHistoryAct.setStatusTip(self.tr( - 'Show the redo history of the current file')) - self.refactoringRedoFileHistoryAct.setWhatsThis(self.tr( - """<b>Show Current File Redo History</b>""" - """<p>Opens a dialog to show the redo history list of""" - """ the current file.</p>""" - )) - self.refactoringRedoFileHistoryAct.triggered.connect( - self.__showFileRedoHistory) - self.actions.append(self.refactoringRedoFileHistoryAct) + self.refactoringFileHistoryAct.triggered.connect( + self.__showFileHistory) + self.actions.append(self.refactoringFileHistoryAct) self.refactoringClearHistoryAct = E5Action( self.tr('Clear History'), @@ -745,7 +686,6 @@ smenu.addAction(self.queryImplementationsAct) smenu = menu.addMenu(self.tr("&Refactoring")) - smenu.aboutToShow.connect(self.__showRefactoringMenu) smenu.addAction(self.refactoringRenameAct) smenu.addAction(self.refactoringRenameLocalAct) smenu.addAction(self.refactoringChangeOccurrencesAct) @@ -784,17 +724,11 @@ imenu.addAction(self.refactoringImportsHandleLongAct) smenu.addSeparator() - smenu.addAction(self.refactoringUndoAct) - smenu.addAction(self.refactoringRedoAct) - smenu.addSeparator() hmenu = smenu.addMenu(self.tr("History")) hmenu.aboutToShow.connect(self.__showRefactoringHistoryMenu) - hmenu.addAction(self.refactoringUndoHistoryAct) - hmenu.addAction(self.refactoringUndoFileHistoryAct) - hmenu.addSeparator() - hmenu.addAction(self.refactoringRedoHistoryAct) - hmenu.addAction(self.refactoringRedoFileHistoryAct) + hmenu.addAction(self.refactoringProjectHistoryAct) + hmenu.addAction(self.refactoringFileHistoryAct) hmenu.addSeparator() hmenu.addAction(self.refactoringClearHistoryAct) @@ -828,103 +762,18 @@ self.__ropeConfig["RopeVersion"], self.__ropeConfig["RopeCopyright"]))) - def __canUndo(self): - """ - Private slot to check, if there are changes to be undone. - - @return flag indicating, that undoable changes are available (boolean) - """ - return False - return self.__project is not None and \ - len(self.__project.history.undo_list) > 0 - - def __canRedo(self): - """ - Private slot to check, if there are changes to be redone. - - @return flag indicating, that redoable changes are available (boolean) - """ - return False - return self.__project is not None and \ - len(self.__project.history.redo_list) > 0 - - def __getFileUndoList(self, resource): - """ - Private slot to get a list of undoable changes. - - @param resource file resource to filter against - (rope.base.resources.File) - @return list of change objects (list of rope.base.change.Change) - """ - undoList = [] - for change in self.__project.history.undo_list: - if resource in change.get_changed_resources(): - undoList.append(change) - return undoList - - def __getFileRedoList(self, resource): - """ - Private slot to get a list of redoable changes. - - @param resource file resource to filter against - (rope.base.resources.File) - @return list of change objects (list of rope.base.change.Change) - """ - redoList = [] - for change in self.__project.history.redo_list: - if resource in change.get_changed_resources(): - redoList.append(change) - return redoList - - def __canUndoFile(self, resource): - """ - Private slot to check, if there are undoable changes for a resource. - - @param resource file resource to check against - (rope.base.resources.File) - @return flag indicating, that undoable changes are available (boolean) - """ - return self.__canUndo() and len(self.__getFileUndoList(resource)) > 0 - - def __canRedoFile(self, resource): - """ - Private slot to check, if there are redoable changes for a resource. - - @param resource file resource to check against - (rope.base.resources.File) - @return flag indicating, that redoable changes are available (boolean) - """ - return self.__canRedo() and len(self.__getFileRedoList(resource)) > 0 - - def __showRefactoringMenu(self): - """ - Private slot called before the refactoring menu is shown. - """ - self.refactoringUndoAct.setEnabled(self.__canUndo()) - self.refactoringRedoAct.setEnabled(self.__canRedo()) - def __showRefactoringHistoryMenu(self): """ Private slot called before the refactoring history menu is shown. """ aw = e5App().getObject("ViewManager").activeWindow() - resource = None - if aw is not None and self.__project is not None: - filename = aw.getFileName() - if filename is not None: - resource = rope.base.libutils.path_to_resource( - self.__project, filename) + enable = aw is not None and bool(aw.getFileName()) - self.refactoringUndoHistoryAct.setEnabled(self.__canUndo()) - self.refactoringUndoFileHistoryAct.setEnabled( - resource is not None and self.__canUndoFile(resource)) - self.refactoringRedoHistoryAct.setEnabled(self.__canRedo()) - self.refactoringRedoFileHistoryAct.setEnabled( - resource is not None and self.__canRedoFile(resource)) + self.refactoringFileHistoryAct.setEnabled(enable) - def __handleRopeError(self, result): + def handleRopeError(self, result): """ - Private method to handle a rope error. + Public method to handle a rope error. @param result dictionary containing the error information @type dict @@ -1779,122 +1628,39 @@ self.dlg.show() ##################################################### - ## Undo/Redo refactorings + ## Refactoring History ##################################################### - def __undo(self): - """ - Private slot to undo the last refactoring. + def __showProjectHistory(self): """ - title = self.tr("Undo refactoring") - history = self.__project.history - res = E5MessageBox.yesNo( - None, - title, - self.tr("""Shall the refactoring <b>{0}</b> be undone?""") - .format(Utilities.html_encode( - history.undo_list[-1].description))) - if res: - if not self.confirmAllBuffersSaved(): - return - - from ProgressHandle import ProgressHandle - changes = history.undo_list[-1] - handle = ProgressHandle(self.tr("Undo"), False, self.__ui) - handle.show() - QApplication.processEvents() - try: - history.undo(task_handle=handle) - except Exception as err: - self.handleRopeError(err, title, handle) - handle.reset() - self.refreshEditors(changes) - if self.__e5project.isDirty(): - self.__e5project.saveProject() - - def __redo(self): - """ - Private slot to redo the last refactoring. + Private method to show the project refactoring history. """ - title = self.tr("Redo refactoring") - history = self.__project.history - res = E5MessageBox.yesNo( - None, - title, - self.tr("""Shall the refactoring <b>{0}</b> be redone?""") - .format(Utilities.html_encode( - history.redo_list[-1].description))) - if res: - if not self.confirmAllBuffersSaved(): - return - - from ProgressHandle import ProgressHandle - changes = history.redo_list[-1] - handle = ProgressHandle(self.tr("Redo"), False, self.__ui) - handle.show() - QApplication.processEvents() - try: - history.redo(task_handle=handle) - except Exception as err: - self.handleRopeError(err, title, handle) - handle.reset() - self.refreshEditors(changes) - if self.__e5project.isDirty(): - self.__e5project.saveProject() + if self.__historyDialog is not None: + self.__historyDialog.close() + + from HistoryDialog import HistoryDialog + self.__historyDialog = HistoryDialog(self, parent=self.__ui) + self.__historyDialog.finished.connect(self.__historyDialogClosed) + self.__historyDialog.show() - def __showProjectUndoHistory(self): - """ - Private method to show list of changes available for an undo operation. - """ - from HistoryDialog import HistoryDialog - undoList = list(reversed(self.__project.history.undo_list)) - self.dlg = HistoryDialog(self, undoList, True, self.__ui) - self.dlg.show() - - def __showProjectRedoHistory(self): + def __showFileHistory(self): """ - Private method to show list of changes available for a redo operation. - """ - from HistoryDialog import HistoryDialog - redoList = self.__project.history.redo_list - self.dlg = HistoryDialog(self, redoList, False, self.__ui) - self.dlg.show() - - def __showFileUndoHistory(self): - """ - Private method to show list of changes related to the current file - available for an undo operation. + Private method to show the refactoring history of the current file. """ aw = e5App().getObject("ViewManager").activeWindow() if aw is None: return - from HistoryDialog import HistoryDialog - filename = aw.getFileName() - resource = rope.base.libutils.path_to_resource( - self.__project, filename) - undoList = list(reversed(self.__getFileUndoList(resource))) - self.dlg = HistoryDialog(self, undoList, True, self.__ui) - self.dlg.show() - - def __showFileRedoHistory(self): - """ - Private method to show list of changes related to the current file - available for a redo operation. - """ - aw = e5App().getObject("ViewManager").activeWindow() - - if aw is None: - return + if self.__historyDialog is not None: + self.__historyDialog.close() from HistoryDialog import HistoryDialog filename = aw.getFileName() - resource = rope.base.libutils.path_to_resource( - self.__project, filename) - redoList = self.__getFileRedoList(resource) - self.dlg = HistoryDialog(self, redoList, False, self.__ui) - self.dlg.show() + if filename: + self.__historyDialog = HistoryDialog( + self, filename=filename, parent=self.__ui) + self.__historyDialog.show() def __clearHistory(self): """ @@ -1903,10 +1669,31 @@ res = E5MessageBox.yesNo( None, self.tr("Clear History"), - self.tr("""Do you really want to clear the undo""" - """ and redo history?""")) + self.tr("Do you really want to clear the refactoring history?")) if res: - self.__project.history.clear() + self.sendJson("History", { + "Subcommand": "Clear", + }) + + if self.__historyDialog is not None: + self.__historyDialog.historyCleared() + + def __processHistoryResult(self, result): + """ + Private method to process the history data sent by the refactoring + client. + + @param result dictionary containing the history data + @type dict + """ + if self.handleRopeError(result) and self.__historyDialog is not None: + self.__historyDialog.processHistoryCommand(result) + + def __historyDialogClosed(self): + """ + Private slot handling the closing of the history dialog. + """ + self.__historyDialog = None ##################################################### ## Find actions including mouse click handler @@ -1944,7 +1731,7 @@ @param result dictionary containing the result data @type dict """ - if self.__handleRopeError(result): + if self.handleRopeError(result): title = result["Title"] if result["EntriesCount"] > 0: from MatchesDialog import MatchesDialog @@ -2015,7 +1802,7 @@ @type dict """ if result["Subcommand"] == "Query": - if self.__handleRopeError(result): + if self.handleRopeError(result): title = result["Title"] if "Location" in result: location = result["Location"] @@ -2077,7 +1864,7 @@ @param result dictionary containing the result data @type dict """ - if self.__handleRopeError(result): + if self.handleRopeError(result): title = result["Title"] if result["EntriesCount"] > 0: from MatchesDialog import MatchesDialog @@ -2182,7 +1969,7 @@ @param result dictionary containing the result data @type dict """ - if self.__handleRopeError(result): + if self.handleRopeError(result): title = result["Title"] E5MessageBox.information( @@ -2333,22 +2120,22 @@ act.setEnabled(False) self.__mainMenu.menuAction().setEnabled(False) - self.stopClient() + if self.__helpDialog is not None: + self.__helpDialog.close() + self.__helpDialog = None + if self.__historyDialog is not None: + self.__historyDialog.close() + self.__historyDialog = None + + self.sendJson("CloseProject", {}, flush=True) self.__projectopen = False self.__projectpath = '' self.__projectLanguage = "" self.__ropeConfig = {} + + self.stopClient() - # TODO: delete this or move to client -## def getProject(self): -## """ -## Public method to get a reference to the rope project object. -## -## @return reference to the rope project object (RopeProject) -## """ -## return self.__project -## def confirmBufferIsSaved(self, editor): """ Public method to check, if an editor has unsaved changes. @@ -2372,25 +2159,18 @@ self.sendJson("Validate", {}) return res - # TODO: port this - def refreshEditors(self, changes): + def refreshEditors(self, changedFiles): """ Public method to refresh modified editors. - @param changes reference to the ChangeSet object - (rope.base.change.ChangeSet) + @param changedFiles list of changed files + @type list of str """ vm = e5App().getObject("ViewManager") - - changedFiles = [] -# for resource in changes.get_changed_resources(): -# if not resource.is_folder(): -# changedFiles.append(resource.real_path) - openFiles = [Utilities.normcasepath(f) for f in vm.getOpenFilenames()] - for file in changedFiles: - normfile = Utilities.normcasepath(file) + for fileName in changedFiles: + normfile = Utilities.normcasepath(fileName) if normfile in openFiles: editor = vm.getEditor(normfile)[1] editor.refresh()
--- a/RefactoringRope/RefactoringClient.py Sat Sep 16 18:51:19 2017 +0200 +++ b/RefactoringRope/RefactoringClient.py Sun Sep 17 17:03:16 2017 +0200 @@ -46,6 +46,7 @@ self.__methodMapping = { "AbortAction": self.__abortAction, + "CloseProject": self.__closeProject, "Validate": self.__validate, "QueryReferences": self.__queryReferences, "QueryDefinition": self.__queryDefinition, @@ -54,6 +55,7 @@ "ConfigChanged": self.__configChanged, "PerformSoa": self.__performSOA, "ReportChanged": self.__reportChanged, + "History": self.__processHistory, } from FileSystemCommands import RefactoringClientFileSystemCommands @@ -64,6 +66,8 @@ self.__projectpath, fscommands=self.__fsCommands) self.__progressHandle = None + + self.__changes = {} # dict storing the retrieved changes def handleCall(self, method, params): """ @@ -123,6 +127,16 @@ """ self.__project.validate(self.__project.root) + def __closeProject(self, params): + """ + Private slot to validate the project. + + @param params dictionary containing the method parameters sent by + the server + @type dict + """ + self.__project.close() + def __getConfig(self, params): """ Private method to send some configuration data to the server. @@ -322,6 +336,80 @@ except Exception: # simply ignore it pass + + def __processHistory(self, params): + """ + Private method to process the various history related requests. + + @param params dictionary containing the method parameters sent by + the server + @type dict + """ + subcommand = params["Subcommand"] + if subcommand == "Get": + self.__changes = {} + if params["Filename"]: + # file history + resource = rope.base.libutils.path_to_resource( + self.__project, params["Filename"]) + undoList = [] + for change in reversed(self.__project.history.undo_list): + if resource in change.get_changed_resources(): + undoList.append(change) + redoList = [] + for change in self.__project.history.redo_list: + if resource in change.get_changed_resources(): + redoList.append(change) + else: + # project history + undoList = list(reversed(self.__project.history.undo_list)) + redoList = self.__project.history.redo_list + + result = {"Subcommand": "Histories"} + result["Undo"] = [] + for change in undoList: + self.__changes[id(change)] = change + result["Undo"].append([str(change), id(change)]) + result["Redo"] = [] + for change in redoList: + self.__changes[id(change)] = change + result["Redo"].append([str(change), id(change)]) + + self.sendJson("HistoryResult", result) + + elif subcommand == "GetChange": + result = { + "Subcommand": "ChangeDescription", + "Description": self.__changes[params["Id"]].get_description() + } + + self.sendJson("HistoryResult", result) + + elif subcommand in ["Undo", "Redo"]: + change = self.__changes[params["Id"]] + from ProgressHandle import ProgressHandle + self.__progressHandle = ProgressHandle(self, change.description, + False) + if subcommand == "Undo": + self.__project.history.undo( + change, task_handle=self.__progressHandle) + else: + self.__project.history.redo( + change, task_handle=self.__progressHandle) + self.__progressHandle.reset() + self.__progressHandle = None + + result = { + "Subcommand": subcommand, + "ChangedFiles": [ + res.real_path for res in change.get_changed_resources() + ], + } + + self.sendJson("HistoryResult", result) + + elif subcommand == "Clear": + self.__project.history.clear() if __name__ == '__main__': if len(sys.argv) != 4: