51 Replies Last post: Jun 15, 2009 3:02 PM by Dmitry Jemerov  
Guest
Currently Being Moderated

Jan 6, 2005 10:05 PM

Type refactoring

Hi,

 

A long time ago I've asked for a refactoring, that safely can change the

type of a variable according to its usages. Since Jetbrains obviously don't

show interest to implement it, I want to know from those whole already know

the OpenApi well, whether it is open enough to write a plugin myself.

 

- I need to get the "object" at the cursor position

- if it is no variable, I return

- then I need to find all "usages" to detect the upper-most and lowest type

in the hierarchy

- then I would like to show a dialog box showing all allowed types and let

the user select it

 

--

Thanks in advance,

Tom

 

Dmitry Kashin IDEA Plugin Contest Winner 1,052 posts since
Aug 23, 2002
Currently Being Moderated
Jan 7, 2005 12:32 AM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

Look's like it planned for Irida already

 

TIA,

Dmitry

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 7, 2005 4:10 AM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

I would definitely think it's possible to write such a refactoring yourself. You should probably look at the source of some simple plugins to get a feel for it. And you'll want to install the PsiViewer plugin, which although a little buggy is invaluable to learn the PSI (program structure interface - sort of an abstract syntax tree).

 

I think you haven't described even half of the things you need to do for such a plugin, but here's how I would start:


import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.util.IncorrectOperationException;

public class MyAction extends AnAction {
     public void actionPerformed(AnActionEvent e) {
          // first get Psi element at cursor position.
          final DataContext dataContext = e.getDataContext();
          final Editor editor = (Editor)dataContext.getData(DataConstants.EDITOR);
          final OpenFileDescriptor descriptor =
                  (OpenFileDescriptor)dataContext.getData(DataConstants.OPEN_FILE_DESCRIPTOR);
          final Project project = (Project)dataContext.getData(DataConstants.PROJECT);
          final PsiManager psiManager = PsiManager.getInstance(project);
          final PsiFile file = psiManager.findFile(descriptor.getFile());
          final CaretModel caretModel = editor.getCaretModel();
          final PsiElement element = file.findElementAt(caretModel.getOffset());

          // check if it's a variable
          if (!(element instanceof PsiJavaToken)) {
               return;
          }
          final PsiJavaToken token = (PsiJavaToken)element;
          final PsiElement parent = token.getParent();
          if (!(parent instanceof PsiVariable)) {
               return;
          }
          final PsiVariable variable = (PsiVariable)parent;

          // search for usages.
          final PsiSearchHelper searchHelper = psiManager.getSearchHelper();
          final PsiReference[] references =
                  searchHelper.findReferences(variable, variable.getUseScope(), false);


          // replace type with something else (actually probably needs to be a write action or something).
          final PsiElementFactory elementFactory = psiManager.getElementFactory();
          final PsiClassType type = elementFactory.createTypeByFQClassName("java.lang.String",
                  variable.getUseScope());
          final PsiTypeElement typeElement = elementFactory.createTypeElement(type);
          try {
               variable.getTypeElement().replace(typeElement);
          } catch (IncorrectOperationException e1) {
               throw new RuntimeException(e1);
          }
     }
}

Note that I'm not certain this is the best way to do such a thing and I have not even compiled this code yet. It's just something to get you started, cause some things can be hard to find in the openapi.

 

Good luck!

Bas

 

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 8, 2005 2:47 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

An EditorAction will certainly work. I should of thought of that myself, but I was a bit confused by the name. It seems to be just for actions in the editor, but in fact it's probably much easier to always use EditorAction and not AnAction.

 

You will probably know this already, but you can also run write actions with something like this.


ApplicationManager.getApplication().runWriteAction(new Runnable(){
  public void run() {
    // do the psi change here.
  }
});

Bas

Dmitry Kashin IDEA Plugin Contest Winner 1,052 posts since
Aug 23, 2002
Currently Being Moderated
Jan 7, 2005 11:19 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

One more time:

"This refactoring will be present in Irida"!!!

Don't lose you time to already implemented features, look into missing

 

TIA,

Dmitry

Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 8, 2005 3:09 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

I'm agree with Dmitry

http://www.jetbrains.net/jira/browse/IDEA-42

 

Thanks!

Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 8, 2005 6:25 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

Yes, you are right!

 

Good luck!

BTW, looking into JIRA issue and this SCR i saw, that this refactoring may refactor:

1. Type of variable/field.

  1.1 Type in usage context.

 

2. Type of getter/setter

  2.1 Type of ussage context

  2.2 Type of overrided context

  2.3 Type of caused implementation in case of interface implementation.

 

So, seems to not very simple plugin?

 

Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 8, 2005 10:31 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

Hi,

 

Left or right type definition?

Becouse i'm thinking that changing left type is impossible, and right type is no need to be refactored.

 

For example:


void boo() {
  List list = new ArrayList();
  Object o = list.get(0);
  foo(list);
  poo(list);
}

void foo(Collection list) {
  // ...
}

void poo(Object object) {
  // ...
}

 

If you change ArrayList to one of implementation of List - this is not can re Refactoring. You just change it by CtrlShitSpace after 'new' token.

 

If you will change List, then:

1. Find all usages of variable

2. Calculate by some magic algorithm scope of available types (in this example scope of method poo is larger that scope of method foo, and you must take scope of foo, but also list.get is using, so you can't use Collection, only List and all above)

 

Now, even you call this as refactor, i still think that it is not pure refactoring. So, can you explain that code you will refactor by plugin?

 

IFAIK, plugin, about you said - it some smart type sugegsting and scope detection by variable usage. As CtrlShiftSpace after 'new':)

 

Thanks!

Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 9, 2005 2:33 PM in response to: Thomas Singer (MoTJ)
Re: How to get method parameter type?

Did you have PsiViewer enabled in IDEA?

AFIAK you will get only PsiIdentifier in this method parameters list. But you have PsiMethodCallExpression, then you can found PsiMethod and get from it PsiParameterList the PsiTypeElement of parameter type.

 

Thanks!

 

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 9, 2005 9:36 PM in response to: Thomas Singer (MoTJ)
Re: How to get method parameter type?

I don't have IDEA in front of me right now, but calling resolve() on a PsiReferenceExpression returns the object the reference refers to, a PsiMethod in this case. You can query that for further info. Does that help?

 

Bas

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 10, 2005 1:12 PM in response to: Thomas Singer (MoTJ)
Re: How to get method parameter type?

Ah sorry, didn't quite understand which reference you had there. Ok, when you have the reference to "coll", you then want the reference to the "doSomethingElse" method in this case. I would do it like this:


final PsiElement parent = reference.getParent();
if (parent instanceof PsiExpressionList) {
     // ref is used as a parameter in a method call

     final PsiExpressionList list = (PsiExpressionList)parent;
     final PsiExpression[] expressions = list.getExpressions();
     int index = -1;
     for (int j = 0; j < expressions.length; j++) {
          if (expressions[j] == reference) {
               index = j; // reference is the j-th parameter
               break;
          }
     }

     if (index > 0) {
          final PsiElement parentsParent = parent.getParent();
          if (parentsParent instanceof PsiMethodCallExpression) {
               final PsiMethodCallExpression expression = (PsiMethodCallExpression)parentsParent;
               final PsiReferenceExpression methodReference = expression.getMethodExpression();
               final PsiMethod method = (PsiMethod)methodReference.resolve();
               final PsiParameterList parameterList = method.getParameterList();
               final PsiParameter[] parameters = parameterList.getParameters();
               PsiType type = parameters[index].getType();
          }
     }
}

Note again that this code was never compiled and may not fulfill its intended purpose.

 

Bas

Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 9, 2005 2:23 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

Tom,

 

It can't be Collection, as i understand SRC correctly. The Collection have not method get, but it used in this sample.

 

I'm wrong?

 

Thanks!

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 10, 2005 6:37 PM in response to: Thomas Singer (MoTJ)
Re: How to check existance of method in class hierarchy

Hi Thomas,

 

List coll;
> coll = new ArrayList();
>      
> System.out.println(coll.size());

I now wanna check, in what PsiTypes, the method

'size()' is defined. I have

the PsiMethodCallExpression, which can return the

PsiMethod, which can

return the containing PsiClass. In other words, I

have a PsiMethod and want

to get all super-types and implemented interfaces,

which contain this method

as well.

 

You might want to take a look at the com.intellij.psi.util.PsiSuperMethodUtil class. It has some utility methods which may be useful to you.

 

I've skimmed the IntentionPowerPack-sources but could

not find something

similar.

 

BTW, what is the difference between PsiType, PsiClass

and PsiClassType? How

to convert from a PsiClass to a PsiType?

 

I don't know too much about it but PsiClassType is a subclass of PsiType with some extra methods for classes. PsiType also describes primitives. As far as I can tell PsiType and PsiClassType are light weight classes for type checking and PsiClass provides meta information about classes.

 

To convert from a PsiClass to a PsiType:


PsiClass aClass = ...;
PsiClassType type = aClass.getManager().getElementFactory().createType(aClass);

 

The documentation provided by Jetbrains is very week

(or I did not find it);

just got the non-description-version of the

OpenAPI-Javadoc.

 

Psi documentation is getter better actually:-) It still has a long way to go, but since it has been officially opened documentation has started to appear here and there where there used to be nothing. Existing plugin sources continue to be the best source of information though. Some stuff can also be found on the www.intellij.org wiki, but that's mostly out of date and not structured very well. Alain Ravet had the idea some time ago of building an openapi wiki, but the response was low.

 

Bas

 

Bas Leijdekkers JetBrains 2,223 posts since
Aug 19, 2002
Currently Being Moderated
Jan 11, 2005 4:05 PM in response to: Thomas Singer (MoTJ)
Re: Place actions in menus

Hi Tom,

 

I guess I can answer one more time:-)

 

Thomas Singer (MoTJ) wrote:

How to place an action in the Refactor menu?

 

The easiest option is to specify your action in plugin.xml:


<actions>
  <group>
    <action class="singer.thomas.MyAction" text="_Change Type" id="change_type"></action>
    <add-to-group group-id="RefactoringMenu" anchor="after" relative-to-action="ChangeSignature"></add-to-group>
  </group>
</actions>

You can also add it programmatically:


final ActionManager actionManager = ActionManager.getInstance();
final MyAction action = new MyAction();
action.getTemplatePresentation().setText("Change Type", true);
actionManager.registerAction("change_type", action);
final DefaultActionGroup group = (DefaultActionGroup)actionManager.getAction("RefactoringMenu");
group.add(action, new Constraints(Anchor.AFTER, "ChangeSignature"));

 

Where I can find all the

constants for the menus?

 

There basically impossible to find these days. IDEA used to come with some small plugin examples and documentation. I think it's not there anymore since somewhere at the start of the Pallada EAP. I have attached the files in a zip to this message. Look in ActionManager.xml for all action constants.

 

Bas

 

Attachments:
Alexey Efimov IDEA Plugin Contest Winner 1,410 posts since
Aug 21, 2002
Currently Being Moderated
Jan 13, 2005 10:06 PM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

I guess, just find them in current class/suppers/interfaces.

This is not referencable items in call context, i suppose.

But, looking on Ctrl+Space completion, i think, that some easy way presented... Maybe some one from JB help?

Alexei Orishchenko Novice 109 posts since
Oct 12, 2005
Currently Being Moderated
Jun 13, 2009 1:24 AM in response to: Thomas Singer (MoTJ)
Re: Type refactoring

How to create a PsiClass by type name with generics (for example, "List<String>")?

 

P.S. The code below returns null

 

     PsiManager.getInstance(project).findClass("List<String>", scope)

Dmitry Jemerov Master 12,528 posts since
Aug 19, 2002
Currently Being Moderated
Jun 15, 2009 3:15 PM in response to: Alexei Orishchenko
Re: Type refactoring

Hello Alexei,

 

There is no way to create such a PsiClass. What do you need to accomplish?

 

How to create a PsiClass by type name with generics (for example,

"List<String>")?

 

P.S. The code below returns null

 

PsiManager.getInstance(project).findClass("List<String>", scope)

 

---

Original message URL:

http://www.jetbrains.net/devnet/message/5239985#5239985

--

Dmitry Jemerov

Development Lead

JetBrains, Inc.

http://www.jetbrains.com/

"Develop with Pleasure!"

 

 

 

More Like This

  • Retrieving data ...