vazexqi / CodingSpectator

Watches and analyzes code edits in the Eclipse IDE non-invasively
http://codingspectator.cs.illinois.edu
Other
20 stars 14 forks source link

Capture the method of invoking refactorings #190

Open rajkuma1 opened 13 years ago

rajkuma1 commented 13 years ago

The refactoring logs of CodingSpectator should include the method the refactoring was invoked. There are 8 ways that I know of to invoke a refactoring:

  1. From the menu Refactor
  2. Via the shortcut key for the list of refactorings (ALT+SHIFT+T)
  3. Right click and invoke from the Refactor item
  4. Using the direct shortcut key to invoke the refactoring (e.g. ALT+SHIFT+R for the rename refactoring)
  5. Invoking the refactoring from the list of refactorings proposed by the quick assist pop-up menu (CTRL+1)
  6. A selection of quick assists can be assigned to a direct shortcut. By default, these are:
    • Rename in file: Ctrl+2, R
    • Assign to local: Ctrl+2, L
    • Assign to field: Ctrl+2, F
  7. Invoking the refactoring using the quick fix.
  8. Drag and drop

Some quick fixes will implicitly invoke refactorings. Here's the only example that we know so far (are there more?):

package a;

class C1 {
}

If C1 was not declared in package a, then there wil be a quick-fix annotation on the editor. Click on it and it will suggest two things: move the package or delete the package declaration. If you select move package, it will implicitly invoke the move refactoring (this is captured in the refactoring logs).

Currently, CodingSpectator distinguishes invocations of refactorings through quick assist from others. It might be useful to distinguish other methods of invoking refactorings as well.

vazexqi commented 13 years ago

There is another way to invoke refactorings (it might be the same category as one of those listed above): you can right-click on the structured view (Package Explorer or Outline View).

Let's classify the Package Explorer and Outline View (and possibly other views) as graphical structured view of source code. It would be interesting to see how developers prefer to invoke the refactorings:

  1. Through the text editor (keyboard shortcuts, right-click, quick fix, etc)
  2. Through the graphical structured views (right-click only as far as I know)
rajkuma1 commented 13 years ago

@vazexqi: You can invoke the refactoring menu from the graphical structured views by using ALT+SHIFT+T keyboard shortcut.

Here' my investigation notes for the evening:

There are 2 refactoring suggestions present when you invoke CTRL+1 quick assist shortcut. They are from this file: CorrectionMessages.properties

RenameRefactoringProposal_name=Rename in workspace LinkedNamesAssistProposal_description=Rename in file

org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages has a truck load of strings for being referenced for correction suggestions. org.eclipse.jdt.internal.ui.text.correction.proposals.RenameRefactoringProposal contains Mohsen's changes to say that the refactoring was invoked through quick assist. org.eclipse.jface.text.contentassist.ICompletionProposal seems to be the common base class for all the context sensitive proposals. There are other interfaces that exist for backward compatibility. org.eclipse.jface.text.contentassist.ICompletionListener are interfaces for listening to the content assist events. org.eclipse.jface.text.contentassist.ContentAssistant registers listeners for capturing events about invoking content assistant code.

QUICK ASSIST:

org.eclipse.jdt.ui.text.java.IQuickFixProcessor org.eclipse.jdt.internal.ui.text.correction.QuickFixProcessor is the concrete class for checking the problems. org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor gets the correction proposals for all the QuickFixProcessor instances in the system.

rajkuma1 commented 13 years ago

Consider a file in a JAVA project called C.java with the following contents.

public class D {
}

Now, you can invoke the quick fix from insider the editor to rename the compilation unit to D.java.

When you do this, the file gets renamed and the compilation issue gets resolved. However, eclipse doesn't seem to record this change. Also, the RenameCompilationUnit refactoring processor does not get invoked. It looks like quick fixes don't reuse refactoring classes and compute the changes themselves.

rajkuma1 commented 13 years ago

When I invoke the completion popup via CTRL + 1 and pick an action to perform, line 933 gets executed. This holds good for my previous comment.

Now, let's say that you type sysout inside a method and hit CTRL + 1, you'll see the following:

  1. Create local variable 'sysout'.
  2. Create field 'sysout'.
  3. Create parameter 'sysout'
  4. Remove assignment
  5. Rename in file

When in this state, the user can do one of the 2 actions:

  1. Hits the ESC key and quits the suggestions. In this case, the org.eclipse.jface.text.contentassist.CompletionProposalPopup.verifyKey(VerifyEvent) would execute to line

    switch (key) { case 0x1B: // Esc e.doit = false; hide(); break; }

No further action is taken.

  1. Hits the ENTER key. In this case, the verifyKey method would execute this part of the switch:

    case '\n': // Ctrl-Enter on w2k case '\r': // Enter e.doit= false; insertSelectedProposalWithMask(e.stateMask); break;

insertSelectedProposalWithMask is executed. In this method, the ICompletionProposal that was selected by the user is fetched and the change is performed.

There is a subtle difference between the correction performed when invoked via CTRL + 1 and CTRL + SPACE. Inside the method org.eclipse.jface.text.contentassist.CompletionProposalPopup.insertProposal(ICompletionProposal, char, int, int), the instance of the proposal called is different.

In the case of CTRL + 1 invocation, the proposal is an instance of org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeCorrectionProposal. So, line 933 is executed.

Now, consider this scenario of invoking CTRL + SPACE to kick off the template proposal. In this case, the flow is the same, except that an instance of org.eclipse.jface.text.contentassist.ICompletionProposalExtension2 is executed in line 927.

Another way to look at this is org.eclipse.jface.text.contentassist.ContentAssistant.isAutoInserting() is true when invoked through CTRL + SPACE.

I see two possibilities here:

  1. Let's take jface and modify the CompletionProposalPopup class.
  2. Play around with org.eclipse.jface.text.contentassist.ContentAssistant and see if we can add ourselves as a listener. Refer org.eclipse.jface.text.contentassist.ContentAssistant.addContentAssistListener(IContentAssistListener, int).
vazexqi commented 13 years ago

Why was the issue closed? Was it incorporated into a different issue? Or did we choose to use a different approach? Or is this issue no longer important?

Anyway, my question about the previous comment is: is there a way that we can hook into the invocation mechanism without touching the jface code? JFace is quite low-level and is depended on by many other plug-ins so it is very risky to modify.

rajkuma1 commented 13 years ago

I didn't close this issue. I don't know how that happened.

rajkuma1 commented 13 years ago
rajkuma1 commented 13 years ago

Notes for the evening: When eclipse first starts up, org.eclipse.jdt.ui.actions.RefactorActionGroup.RefactorActionGroup(IWorkbenchSite, ISelectionProvider) is created. From then on, it's cached. It is here that the refactoring actions are created.

One of the implementations, org.eclipse.jdt.ui.actions.RenameAction has been touched by @vazexqi and @reprogrammer to know if the invocation was through quickAssist.

/org.eclipse.jdt.ui holds references to the menu items that are present on the right click action.

RenameInformationPopup_EnterNewName=Enter new name, press {0} to refactor

Here's the stack trace when the initialization of the org.eclipse.jdt.ui.actions.RefactorActionGroup takes place:

Thread [main] (Suspended (breakpoint at line 66 in RenameAction))   
RenameAction.<init>(IWorkbenchSite) line: 66    
RefactorActionGroup.<init>(IWorkbenchSite, ISelectionProvider) line: 371    
RefactorActionGroup.<init>(IViewPart) line: 205 
JavaNavigatorRefactorActionProvider.init(ICommonActionExtensionSite) line: 60   
NavigatorActionService$6.run() line: 373    
SafeRunner.run(ISafeRunnable) line: 42  
NavigatorActionService.initialize(String, String, CommonActionProvider) line: 369   
NavigatorActionService.access$2(NavigatorActionService, String, String, CommonActionProvider) line: 366 
NavigatorActionService$5.run() line: 351    
SafeRunner.run(ISafeRunnable) line: 42  
NavigatorActionService.getActionProviderInstance(CommonActionProviderDescriptor) line: 347  
NavigatorActionService$3.run() line: 257    
SafeRunner.run(ISafeRunnable) line: 42  
NavigatorActionService.fillActionBars(IActionBars) line: 253    
CommonNavigatorManager.selectionChanged(SelectionChangedEvent) line: 222    
Viewer$2.run() line: 162    
SafeRunner.run(ISafeRunnable) line: 42  
JFaceUtil$1.run(ISafeRunnable) line: 49 
SafeRunnable.run(ISafeRunnable) line: 175   
CommonViewer(Viewer).fireSelectionChanged(SelectionChangedEvent) line: 160  
CommonViewer(StructuredViewer).updateSelection(ISelection) line: 2162   
CommonViewer(StructuredViewer).handleSelect(SelectionEvent) line: 1190  
CommonViewer.handleSelect(SelectionEvent) line: 478 
StructuredViewer$4.widgetSelected(SelectionEvent) line: 1220    
OpenStrategy.fireSelectionEvent(SelectionEvent) line: 228   
OpenStrategy.access$4(OpenStrategy, SelectionEvent) line: 222   
OpenStrategy$1.handleEvent(Event) line: 389 
EventTable.sendEvent(Event) line: 84    
Tree(Widget).sendEvent(Event) line: 1258    
Display.runDeferredEvents() line: 3540  
Display.readAndDispatch() line: 3161    
Workbench.runEventLoop(Window$IExceptionHandler, Display) line: 2640    
Workbench.runUI() line: 2604    
Workbench.access$4(Workbench) line: 2438    
Workbench$7.run() line: 671 
Realm.runWithDefault(Realm, Runnable) line: 332 
Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 664    
PlatformUI.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 149   
IDEApplication.start(IApplicationContext) line: 115 
EclipseAppHandle.run(Object) line: 196  
EclipseAppLauncher.runApplication(Object) line: 110 
EclipseAppLauncher.start(Object) line: 79   
EclipseStarter.run(Object) line: 369    
EclipseStarter.run(String[], Runnable) line: 179    
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39  
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25  
Method.invoke(Object, Object...) line: 597  
Main.invokeFramework(String[], URL[]) line: 619 
Main.basicRun(String[]) line: 574   
Main.run(String[]) line: 1407   
Main.main(String[]) line: 1383  
rajkuma1 commented 13 years ago

@vazexqi guess I found why the issue closes. When you hit the Comment & Close button on chrome on ubuntu, the issue is closed. The earlier behavior was, the issue wouldn't be closed :)

rajkuma1 commented 13 years ago

When eclipse first starts up, org.eclipse.jdt.ui.actions.RefactorActionGroup.RefactorActionGroup(IWorkbenchSite, ISelectionProvider) is created. From then on, it's cached. It is here that the refactoring actions are created.

One of the implementations, org.eclipse.jdt.ui.actions.RenameAction has been touched by @vazexqi and @reprogrammer to know if the invocation was through quickAssist.

When refactoring menu is invoked through SHIFT + CTRL + T, org.eclipse.jdt.ui.actions.RefactorActionGroup.fillQuickMenu(IMenuManager) gets invoked. This is due to the call from org.eclipse.ui.actions.QuickMenuCreator.createMenu() When I right click on the project to see the menu options, this is what happens: org.eclipse.jdt.internal.ui.navigator.JavaNavigatorRefactorActionProvider.fillContextMenu(IMenuManager) gets invoked.

vazexqi commented 13 years ago

@reprogrammer and I found out that the drag-and-drop mechanism for Move Refactoring is called through this the ReorgMoveStarter class.

This information might be useful to get started on capturing the drag-and-drop invocation mechanism.