14 import rope |
14 import rope |
15 import rope.base.libutils |
15 import rope.base.libutils |
16 import rope.base.project |
16 import rope.base.project |
17 import rope.base.exceptions |
17 import rope.base.exceptions |
18 |
18 |
|
19 import rope.refactor.rename |
|
20 ##import rope.refactor.extract |
|
21 ##import rope.refactor.usefunction |
|
22 ##import rope.refactor.inline |
|
23 ##import rope.refactor.move |
|
24 ##import rope.refactor.change_signature |
|
25 ##import rope.refactor.introduce_factory |
|
26 ##import rope.refactor.introduce_parameter |
|
27 ##import rope.refactor.method_object |
|
28 ##import rope.refactor.encapsulate_field |
|
29 ##import rope.refactor.localtofield |
|
30 ##import rope.refactor.topackage |
|
31 ##from rope.refactor.importutils import ImportOrganizer |
|
32 |
19 import rope.contrib.findit |
33 import rope.contrib.findit |
|
34 ##import rope.contrib.finderrors |
20 |
35 |
21 from PyQt4.QtCore import QObject, SIGNAL |
36 from PyQt4.QtCore import QObject, SIGNAL |
22 from PyQt4.QtGui import QMenu, QApplication, QMessageBox |
37 from PyQt4.QtGui import QMenu, QApplication, QMessageBox |
23 |
38 |
24 from E5Gui.E5Application import e5App |
39 from E5Gui.E5Application import e5App |
68 def initActions(self): |
85 def initActions(self): |
69 """ |
86 """ |
70 Public method to define the refactoring actions. |
87 Public method to define the refactoring actions. |
71 """ |
88 """ |
72 self.actions = [] |
89 self.actions = [] |
|
90 |
|
91 ##################################################### |
|
92 ## Rename refactoring actions |
|
93 ##################################################### |
|
94 |
|
95 self.refactoringRenameAct = E5Action(self.trUtf8('Rename'), |
|
96 self.trUtf8('&Rename'), |
|
97 0, 0, |
|
98 self,'refactoring_rename') |
|
99 self.refactoringRenameAct.setStatusTip(self.trUtf8( |
|
100 'Rename the highlighted object')) |
|
101 self.refactoringRenameAct.setWhatsThis(self.trUtf8( |
|
102 """<b>Rename</b>""" |
|
103 """<p>Rename the highlighted Python object.</p>""" |
|
104 )) |
|
105 if self.__newStyle: |
|
106 self.refactoringRenameAct.triggered[()].connect( |
|
107 self.__rename) |
|
108 else: |
|
109 self.connect(self.refactoringRenameAct, SIGNAL('triggered()'), |
|
110 self.__rename) |
|
111 self.actions.append(self.refactoringRenameAct) |
|
112 |
|
113 self.refactoringRenameLocalAct = E5Action(self.trUtf8('Local Rename'), |
|
114 self.trUtf8('&Local Rename'), |
|
115 0, 0, |
|
116 self,'refactoring_rename_local') |
|
117 self.refactoringRenameLocalAct.setStatusTip(self.trUtf8( |
|
118 'Rename the highlighted object in the current module only')) |
|
119 self.refactoringRenameLocalAct.setWhatsThis(self.trUtf8( |
|
120 """<b>Local Rename</b>""" |
|
121 """<p>Rename the highlighted Python object in the current""" |
|
122 """ module only.</p>""" |
|
123 )) |
|
124 if self.__newStyle: |
|
125 self.refactoringRenameLocalAct.triggered[()].connect( |
|
126 self.__renameLocal) |
|
127 else: |
|
128 self.connect(self.refactoringRenameLocalAct, |
|
129 SIGNAL('triggered()'), self.__renameLocal) |
|
130 self.actions.append(self.refactoringRenameLocalAct) |
|
131 |
|
132 self.refactoringRenameModuleAct = E5Action( |
|
133 self.trUtf8('Rename Current Module'), |
|
134 self.trUtf8('Rename Current Module'), |
|
135 0, 0, |
|
136 self,'refactoring_rename_module') |
|
137 self.refactoringRenameModuleAct.setStatusTip(self.trUtf8( |
|
138 'Rename the current module')) |
|
139 self.refactoringRenameModuleAct.setWhatsThis(self.trUtf8( |
|
140 """<b>Rename Current Module</b>""" |
|
141 """<p>Rename the current module.</p>""" |
|
142 )) |
|
143 if self.__newStyle: |
|
144 self.refactoringRenameModuleAct.triggered[()].connect( |
|
145 self.__renameModule) |
|
146 else: |
|
147 self.connect(self.refactoringRenameModuleAct, |
|
148 SIGNAL('triggered()'), self.__renameModule) |
|
149 self.actions.append(self.refactoringRenameModuleAct) |
|
150 |
|
151 self.refactoringChangeOccurrencesAct = E5Action( |
|
152 self.trUtf8('Change Occurrences'), |
|
153 self.trUtf8('Change &Occurrences'), |
|
154 0, 0, |
|
155 self,'refactoring_change_occurrences') |
|
156 self.refactoringChangeOccurrencesAct.setStatusTip(self.trUtf8( |
|
157 'Change all occurrences in the local scope')) |
|
158 self.refactoringChangeOccurrencesAct.setWhatsThis(self.trUtf8( |
|
159 """<b>Change Occurrences</b>""" |
|
160 """<p>Change all occurrences in the local scope.</p>""" |
|
161 )) |
|
162 if self.__newStyle: |
|
163 self.refactoringChangeOccurrencesAct.triggered[()].connect( |
|
164 self.__changeOccurrences) |
|
165 else: |
|
166 self.connect(self.refactoringChangeOccurrencesAct, |
|
167 SIGNAL('triggered()'), self.__changeOccurrences) |
|
168 self.actions.append(self.refactoringChangeOccurrencesAct) |
73 |
169 |
74 ##################################################### |
170 ##################################################### |
75 ## Query actions |
171 ## Query actions |
76 ##################################################### |
172 ##################################################### |
77 |
173 |
193 smenu = menu.addMenu(self.trUtf8("&Query")) |
289 smenu = menu.addMenu(self.trUtf8("&Query")) |
194 smenu.addAction(self.queryReferencesAct) |
290 smenu.addAction(self.queryReferencesAct) |
195 smenu.addAction(self.queryDefinitionAct) |
291 smenu.addAction(self.queryDefinitionAct) |
196 smenu.addAction(self.queryImplementationsAct) |
292 smenu.addAction(self.queryImplementationsAct) |
197 |
293 |
|
294 smenu = menu.addMenu(self.trUtf8("&Refactoring")) |
|
295 if self.__newStyle: |
|
296 smenu.aboutToShow.connect(self.__showRefactoringMenu) |
|
297 else: |
|
298 self.connect(smenu, SIGNAL("aboutToShow()"), |
|
299 self.__showRefactoringMenu) |
|
300 smenu.addAction(self.refactoringRenameAct) |
|
301 smenu.addAction(self.refactoringRenameLocalAct) |
|
302 smenu.addAction(self.refactoringChangeOccurrencesAct) |
|
303 smenu.addSeparator() |
|
304 smenu.addAction(self.refactoringRenameModuleAct) |
|
305 smenu.addSeparator() |
|
306 |
198 menu.addSeparator() |
307 menu.addSeparator() |
199 menu.addAction(self.refactoringEditConfigAct) |
308 menu.addAction(self.refactoringEditConfigAct) |
200 menu.addAction(self.refactoringHelpAct) |
309 menu.addAction(self.refactoringHelpAct) |
201 |
310 |
202 self.__mainMenu = menu |
311 self.__mainMenu = menu |
212 """ |
321 """ |
213 E5MessageBox.about(self.__ui, |
322 E5MessageBox.about(self.__ui, |
214 self.trUtf8("About rope"), |
323 self.trUtf8("About rope"), |
215 self.trUtf8("{0}\nVersion {1}\n\n{2}".format( |
324 self.trUtf8("{0}\nVersion {1}\n\n{2}".format( |
216 rope.INFO, rope.VERSION, rope.COPYRIGHT))) |
325 rope.INFO, rope.VERSION, rope.COPYRIGHT))) |
|
326 |
|
327 def __canUndo(self): |
|
328 """ |
|
329 Private slot to check, if there are changes to be undone. |
|
330 |
|
331 @return flag indicating, that undoable changes are available (boolean) |
|
332 """ |
|
333 return self.__project is not None and \ |
|
334 len(self.__project.history.undo_list) > 0 |
|
335 |
|
336 def __canRedo(self): |
|
337 """ |
|
338 Private slot to check, if there are changes to be redone. |
|
339 |
|
340 @return flag indicating, that redoable changes are available (boolean) |
|
341 """ |
|
342 return self.__project is not None and \ |
|
343 len(self.__project.history.redo_list) > 0 |
|
344 |
|
345 def __getFileUndoList(self, resource): |
|
346 """ |
|
347 Private slot to get a list of undoable changes. |
|
348 |
|
349 @param resource file resource to filter against |
|
350 (rope.base.resources.File) |
|
351 @return list of change objects (list of rope.base.change.Change) |
|
352 """ |
|
353 undoList = [] |
|
354 for change in self.__project.history.undo_list: |
|
355 if resource in change.get_changed_resources(): |
|
356 undoList.append(change) |
|
357 return undoList |
|
358 |
|
359 def __getFileRedoList(self, resource): |
|
360 """ |
|
361 Private slot to get a list of redoable changes. |
|
362 |
|
363 @param resource file resource to filter against |
|
364 (rope.base.resources.File) |
|
365 @return list of change objects (list of rope.base.change.Change) |
|
366 """ |
|
367 redoList = [] |
|
368 for change in self.__project.history.redo_list: |
|
369 if resource in change.get_changed_resources(): |
|
370 redoList.append(change) |
|
371 return redoList |
|
372 |
|
373 def __canUndoFile(self, resource): |
|
374 """ |
|
375 Private slot to check, if there are undoable changes for a resource. |
|
376 |
|
377 @param resource file resource to check against |
|
378 (rope.base.resources.File) |
|
379 @return flag indicating, that undoable changes are available (boolean) |
|
380 """ |
|
381 return self.__canUndo() and len(self.__getFileUndoList(resource)) > 0 |
|
382 |
|
383 def __canRedoFile(self, resource): |
|
384 """ |
|
385 Private slot to check, if there are redoable changes for a resource. |
|
386 |
|
387 @param resource file resource to check against |
|
388 (rope.base.resources.File) |
|
389 @return flag indicating, that redoable changes are available (boolean) |
|
390 """ |
|
391 return self.__canRedo() and len(self.__getFileRedoList(resource)) > 0 |
|
392 |
|
393 def __showRefactoringMenu(self): |
|
394 """ |
|
395 Private slot called before the refactoring menu is shown. |
|
396 """ |
|
397 # TODO: enable these once undo/redo has been implemented |
|
398 ## self.refactoringUndoAct.setEnabled(self.__canUndo()) |
|
399 ## self.refactoringRedoAct.setEnabled(self.__canRedo()) |
217 |
400 |
218 def handleRopeError(self, err, title, handle=None): |
401 def handleRopeError(self, err, title, handle=None): |
219 """ |
402 """ |
220 Public slot to handle a rope error. |
403 Public slot to handle a rope error. |
221 |
404 |
237 err.lineno) |
420 err.lineno) |
238 else: |
421 else: |
239 E5MessageBox.warning(self.__ui, title, |
422 E5MessageBox.warning(self.__ui, title, |
240 self.trUtf8("Rope error: {0}").format(str(err))) |
423 self.trUtf8("Rope error: {0}").format(str(err))) |
241 |
424 |
|
425 ################################################################## |
|
426 ## slots below implement the various refactorings |
|
427 ################################################################## |
|
428 |
|
429 ##################################################### |
|
430 ## Rename refactorings |
|
431 ##################################################### |
|
432 |
|
433 def __rename(self): |
|
434 """ |
|
435 Private slot to handle the Rename action. |
|
436 """ |
|
437 self.__doRename(self.trUtf8('Rename')) |
|
438 |
|
439 def __renameLocal(self): |
|
440 """ |
|
441 Private slot to handle the Local Rename action. |
|
442 """ |
|
443 self.__doRename(self.trUtf8('Local Rename'), isLocal=True) |
|
444 |
|
445 def __renameModule(self): |
|
446 """ |
|
447 Private slot to handle the Rename Current Module action. |
|
448 """ |
|
449 self.__doRename(self.trUtf8('Rename Current Module'), |
|
450 renameModule=True) |
|
451 |
|
452 def __doRename(self, title, isLocal=False, renameModule=False): |
|
453 """ |
|
454 Private method to perform the various renaming refactorings. |
|
455 |
|
456 @param title title of the refactoring (string) |
|
457 @param isLocal flag indicating to restrict refactoring to |
|
458 the local file (boolean) |
|
459 @param renameModule flag indicating a module rename refactoring |
|
460 (boolean) |
|
461 """ |
|
462 aw = e5App().getObject("ViewManager").activeWindow() |
|
463 |
|
464 if aw is None: |
|
465 return |
|
466 |
|
467 if not renameModule and not aw.hasSelectedText(): |
|
468 # no selection available |
|
469 E5MessageBox.warning(self.__ui, title, |
|
470 self.trUtf8("Highlight the declaration you want to rename" |
|
471 " and try again.")) |
|
472 return |
|
473 |
|
474 if isLocal: |
|
475 if not self.confirmBufferIsSaved(aw): |
|
476 return |
|
477 else: |
|
478 if not self.confirmAllBuffersSaved(): |
|
479 return |
|
480 |
|
481 filename = aw.getFileName() |
|
482 if renameModule: |
|
483 offset = None |
|
484 else: |
|
485 line, index, line1, index1 = aw.getSelection() |
|
486 if line != line1: |
|
487 # selection span more than one line |
|
488 E5MessageBox.warning(self.__ui, title, |
|
489 self.trUtf8("The selection must not extend beyond" |
|
490 " one line.")) |
|
491 return |
|
492 index = int(index + (index1 - index) / 2) |
|
493 # keep it inside the object |
|
494 offset = aw.positionFromLineIndex(line, index) |
|
495 |
|
496 resource = rope.base.libutils.path_to_resource( |
|
497 self.__project, filename) |
|
498 try: |
|
499 renamer = rope.refactor.rename.Rename( |
|
500 self.__project, resource, offset) |
|
501 except Exception as err: |
|
502 self.handleRopeError(err, title) |
|
503 return |
|
504 |
|
505 if isLocal: |
|
506 localResource = resource |
|
507 else: |
|
508 localResource = None |
|
509 self.dlg = RenameDialog(self, title, renamer, resource=localResource, |
|
510 parent=self.__ui) |
|
511 self.dlg.show() |
|
512 |
|
513 def __changeOccurrences(self): |
|
514 """ |
|
515 Private slot to perform the Change Occurrences refactoring. |
|
516 """ |
|
517 aw = e5App().getObject("ViewManager").activeWindow() |
|
518 |
|
519 if aw is None: |
|
520 return |
|
521 |
|
522 title = self.trUtf8("Change Occurrences") |
|
523 if not aw.hasSelectedText(): |
|
524 # no selection available |
|
525 E5MessageBox.warning(self.__ui, title, |
|
526 self.trUtf8("Highlight an occurrence to be changed" |
|
527 " and try again.")) |
|
528 return |
|
529 |
|
530 if not self.confirmBufferIsSaved(aw): |
|
531 return |
|
532 |
|
533 filename = aw.getFileName() |
|
534 line, index, line1, index1 = aw.getSelection() |
|
535 offset = aw.positionFromLineIndex(line, index) |
|
536 |
|
537 resource = rope.base.libutils.path_to_resource( |
|
538 self.__project, filename) |
|
539 try: |
|
540 renamer = rope.refactor.rename.ChangeOccurrences( |
|
541 self.__project, resource, offset) |
|
542 except Exception as err: |
|
543 self.handleRopeError(err, title) |
|
544 return |
|
545 |
|
546 self.dlg = ChangeOccurrencesDialog(self, title, renamer, |
|
547 parent=self.__ui) |
|
548 self.dlg.show() |
|
549 |
242 ##################################################### |
550 ##################################################### |
243 ## Find actions |
551 ## Find actions |
244 ##################################################### |
552 ##################################################### |
245 |
553 |
246 def __queryReferences(self): |
554 def __queryReferences(self): |