[ANN] ReformatPlugin 0.1 released

http://www.intellij.org/twiki/bin/view/Main/ReformatPlugin

http://sourceforge.net/projects/tz-ip/

ReformatCode (Ctrl-Alt-L) and OptimizeImports (Ctrl-Alt-O) without a dialog. ReformatPlugin.OptimizeImports - works on the current file. ReformatPlugin.ReformatCode - works on the current file or a selection (if there's a selection). Regular actions (ReformatCode and OptimizeImports; with a dialog) are replaced by the plugin's actions. You still have the access to the regular actions (Tools menu).

Plus there's an additional action: ReformatPlugin.ReformatCodeAndOptimizeImports (Ctrl-Alt-R). It does Optimize Imports and Reformat Code on the current file (even if there's a selection).

Warning. This plugin uses non-published IDEA API.

Relevant posts/requests:

http://www.intellij.net/tracker/idea/viewSCR?publicId=2389 ("Ability to Reformat Code (Ctrl-Alt-L) and Optimize Imports (Ctrl-Alt-O) with one command")

http://www.intellij.net/forums/thread.jsp?forum=3&thread=4680&message=164804 ("Layout Code with option for Optimize Imports?")

http://www.intellij.net/forums/thread.jsp?forum=3&thread=4329&message=141644 ("Splitted Reformat Code/Optimize Imports :(")

http://www.intellij.net/forums/thread.jsp?forum=3&thread=237&message=3035 ("Layout Code and Optimize Imports")


Timur

15 comments

Hi Timur,

Nice solution to the problem.

Would you be willing to modify Ctrl-Alt-R behavior to chain also to tabifier
plugin action if that plugin is present? I.e. optimize imports, reformat
code (file or selection) and tabify (same file or selection).

Essentially, tabifier is a Psi-based code layout "postprocessor" and I am
constantly running it manually after doing Ctrl-Alt-L. One contemplated
enhancement was to hook Ctrl-Alt-L and tabify automatically. I'm glad your
plugin appeared before I went to the trouble!

If you are too busy, I would be willing to make the modification, with your
permission.

Thanks,
-Dave

"Timur Zambalayev" <itnadmin@jetbrains.com> wrote in message
news:9099769.1052711608552.JavaMail.javamailuser@localhost...

http://www.intellij.org/twiki/bin/view/Main/ReformatPlugin

>

http://sourceforge.net/projects/tz-ip/

>

ReformatCode (Ctrl-Alt-L) and OptimizeImports (Ctrl-Alt-O) without a

dialog. ReformatPlugin.OptimizeImports - works on the current file.
ReformatPlugin.ReformatCode - works on the current file or a selection (if
there's a selection). Regular actions (ReformatCode and OptimizeImports;
with a dialog) are replaced by the plugin's actions. You still have the
access to the regular actions (Tools menu).
>

Plus there's an additional action:

ReformatPlugin.ReformatCodeAndOptimizeImports (Ctrl-Alt-R). It does Optimize
Imports and Reformat Code on the current file (even if there's a selection).
>

Warning. This plugin uses non-published IDEA API.

>

Relevant posts/requests:

>

http://www.intellij.net/tracker/idea/viewSCR?publicId=2389 ("Ability to

Reformat Code (Ctrl-Alt-L) and Optimize Imports (Ctrl-Alt-O) with one
command")
>
>
http://www.intellij.net/forums/thread.jsp?forum=3&thread=4680&message=164804
("Layout Code with option for Optimize Imports?")
>
>
http://www.intellij.net/forums/thread.jsp?forum=3&thread=4329&message=141644
("Splitted Reformat Code/Optimize Imports :(")
>

http://www.intellij.net/forums/thread.jsp?forum=3&thread=237&message=3035

("Layout Code and Optimize Imports")
>
>

Timur

>


0

Dave,

How about if we do it this way.

==================================================

==================================================

==================================================

==================================================

BTW, do you need to receive events for ReformatCode with selection?

Once we agree on the interfaces and semantics, I can release a new version of the plugin.


Timur

Hi Timur,

Nice solution to the problem.

Would you be willing to modify Ctrl-Alt-R behavior to
chain also to tabifier
plugin action if that plugin is present? I.e.
optimize imports, reformat
code (file or selection) and tabify (same file or
selection).

Essentially, tabifier is a Psi-based code layout
"postprocessor" and I am
constantly running it manually after doing
Ctrl-Alt-L. One contemplated
enhancement was to hook Ctrl-Alt-L and tabify
automatically. I'm glad your
plugin appeared before I went to the trouble!

If you are too busy, I would be willing to make the
modification, with your
permission.

Thanks,
-Dave

0

Do you intent on solving the reordering of members as well with this plugin?
It would be really nice ;)

Jacques

0

:) I added it to the todo list (at http://sourceforge.net/tracker/?atid=559741&group_id=80426 ). Though I assigned it (honestly) the lowest priority. BTW, here's the request in the tracker: http://www.intellij.net/tracker/idea/viewSCR?publicId=4733.

Other requests I'll probably try to do:
http://www.intellij.net/tracker/idea/viewSCR?publicId=2393
http://www.intellij.net/tracker/idea/viewSCR?publicId=3228
http://www.intellij.net/tracker/idea/viewSCR?publicId=2885


Timur

Do you intent on solving the reordering of members as
well with this plugin?
It would be really nice ;)

Jacques

0

That's great, (even though I think the automatic reordering would be a lot more valuable than the others but it is just me. It is also significantly more difficult).

BTW I saw you are doing mock testing of the openapi as well. Maybe we can combine our efforts and provide a plugin testing framework before they release their test harness that could include a nice ant build file, multi version (3.0 and 3.5) api wrapper and the works.
What do you think?

0

That's great, (even though I think the automatic
reordering would be a lot more valuable than the
others but it is just me. It is also significantly
more difficult).


Yes, the main reason was that it's a difficult task and will probably take a lot of time.

BTW I saw you are doing mock testing of the openapi
as well. Maybe we can combine our efforts and provide
a plugin testing framework before they release their
test harness that could include a nice ant build
file, multi version (3.0 and 3.5) api wrapper and the
works.
What do you think?


"mock testing of the openapi". Yes, I wrote mocks for editor action related classes (EditorFactory, Document, Editor, CaretModel, etc) and those classes are covered well. I was able to test everything I wanted for CamelPlugin and CommentPlugin.

A few words about the (editor factory) mocks I wrote.
They're real implementations meaning that they can take any input. Of course, I didn't worry about the performance (for mock implementations) at all. With them it's possible to test the plugin code outside IDEA (currently if you want to reload a plugin you have to restart IDEA). I also wrote lots of tests that take abstract editorFactory-related classes/interfaces (either mock or real thing inside the containter/IDEA) and test different assumptions. So I can absolutely sure that the mocks work the same way as the real thing. This also gives me the ability to test quickly new builds both Ariadna and Aurora (to see if there are any differences in behavior).

"Maybe we can combine our efforts and provide a plugin testing framework". Definitely. Maybe we can start with writing something (like what's currently covered, coming up with the plan, etc) at intellij.org. I'll try to write something about the mocks I wrote in more detail there.

"before they release their test harness". You mean JetBrains are going to release their test harness?


Timur

0

See below the latest version (also at http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tz-ip/tz-ip/MockTests/src/main/org/ideaplugins/reformat/ReformatListener.java?rev=1.1&only_with_tag=HEAD&content-type=text/vnd.viewcvs-markup ).

I will release it today in a new version (if no objections). Another change is for Ctrl-Alt-R: Optimize, then Reformat.

=======================================================

=======================================================

0

I did the same for the JUnitTestPlugin. mock the hell out the api and have "invitro" test to test assumptions.

2 things:
Request I filed for a test harness slated for Aurora: http://www.intellij.net/tracker/idea/viewSCR?publicId=7898

How to use another instance to run the tests. I have documented some of my findings about debugging/testing plugins at http://www.intellij.org/twiki/bin/view/Main/PluginTesting
In the JUnitTestPlugin I tried the following:
I have a test project that the plugin knows about. When this project is opened special test actions are made available that run the "invitro"/"in container" test suite. I tried to run that test suite in the initComponent() but I got some race condition with the initialization of the other components (especially PSI, hence the part of my request about explicit dependency definition). So unfortunately it isn't completely automated. However my build.xml allow me to launch a second instance of IDEA on the test project and I just have to activate the test actions and all invitro tests are run.

Maybe we can add to the PluginTesting page.

I also just finished a "preprocessor filter" for ant that simulate the old #ifdef:

You encapsulate your specific version code in the following construct.

In your build if you define Ariadna instead the file will be modified to

And vise a versa. I am going to add it to my build.xml that might be something we want to publish as well.

Jacques

0

I'll add to that page (when I have time).

We probably should start (slowly) a new SourceForge project for this (mock implementations, maybe utilities)?

Differences between Ariadna and Aurora. Other options:
1) Put the classes in separate packages, filter out in the compile task. Cons: more classes, two binaries, two builds.
2) Use of reflection. Pros: one build. Cons: more non-obvious code. That's what I did for http://www.intellij.net/tracker/idea/viewSCR?publicId=12132 . I'll remove the code once #815 released.


Timur

0

Yep I thought of that one too. I think that JetBrains need to be more conscious about backward compatibility.
It was before the big brouhaha, so maybe now we can talk about it again. Did you see my still-born thread on the topic http://www.intellij.net/forums/thread.jsp?forum=23&thread=28440&tstart=0&trange=100 . Maybe you could post on it and we could try to get some meaningful debate on that with JetBrains.

Jacques

0

Timur,

Sorry for the delay in responding.

So your idea is that I will simply register with your plugin to be called
after you've invoked IDEA's functions? Should work, but I have a few
thoughts.

1) I need not a PsiFile but an Editor and a DataContext (see method
executeWriteAction().) The editor allows me to obtain the current
selection. Given your code, how would I determine the Editor and
DataContext?

2) I'll call your ReformatManager.getInstance() to get a reference to the
singleton but will have to write some reflection code to see if your
ReformatManager class is present.

3) Yes, I need the selection (if there is one) to be intact when I'm
invoked.

Thanks,
-Dave

"Timur Zambalayev" <itnadmin@jetbrains.com> wrote in message
news:10723565.1052776404554.JavaMail.javamailuser@localhost...

Dave,

>

How about if we do it this way.

>

==================================================

 package org.ideaplugins.reformat;
>
> import com.intellij.psi.PsiFile;
>
> public interface ReformatListener {
>
>     /**
>      * Will be called after codeStyleManager.reformat(psiFile) for
ReformatPlugin.ReformatCodeAndOptimizeImports and
>      * ReformatPlugin.ReformatCode with no selection.
>      *
>      * @param psiFile
>      */
>     void reformat(PsiFile psiFile);
>
> }
> ]]>

==================================================

 package org.ideaplugins.reformat;
>
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Collections;
>
> public final class ReformatManager {
>
>     private static final ReformatManager REFORMAT_MANAGER = new
ReformatManager();
>
>     private final Collection listenerCollection = new ArrayList(1);
>     private final Collection unmodifiableListenerCollection =
Collections.unmodifiableCollection(listenerCollection);
>
>     private ReformatManager() {
>     }
>
>     public static ReformatManager getInstance() {
>         return REFORMAT_MANAGER;
>     }
>
>     public void addReformatListener(ReformatListener reformatListener) {
>         listenerCollection.add(reformatListener);
>     }
>
>     public Collection getReformatListeners() {
>         return unmodifiableListenerCollection;
>     }
>
> }
> ]]>

==================================================

         ReformatManager.getInstance().addReformatListener(new
ReformatListener() {
>             public void reformat(PsiFile psiFile) {
>                 // ...
>             }
>         });
> ]]>

==================================================

>

BTW, do you need to receive events for ReformatCode with selection?

>

Once we agree on the interfaces and semantics, I can release a new version

of the plugin.
>
>

Timur

>
>


0

Dave,

Another attempt. See below. Also:
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tz-ip/tz-ip/MockTests/src/main/org/ideaplugins/reformat/

1) Provided access to editor and dataContext in ReformatEvent.
2) reformatManager.isActive(). I'll activate it in initComponent().
3) You'll receive events after reformatRange too.

Tell me if it's ok for you. BTW, everything is checked in so you can also try it via cvs
( http://sourceforge.net/projects/tz-ip/ ).


=======================================================

=======================================================

=======================================================

=======================================================

=======================================================


Timur

Timur,

Sorry for the delay in responding.

So your idea is that I will simply register with your
plugin to be called
after you've invoked IDEA's functions? Should work,
but I have a few
thoughts.

1) I need not a PsiFile but an Editor and a
DataContext (see method
executeWriteAction().) The editor allows me to
obtain the current
selection. Given your code, how would I determine
the Editor and
DataContext?

2) I'll call your ReformatManager.getInstance() to
get a reference to the
singleton but will have to write some reflection code
to see if your
ReformatManager class is present.

3) Yes, I need the selection (if there is one) to be
intact when I'm
invoked.

Thanks,
-Dave

0

How should we call this sourceforge project:
IdeaUtil,
IdeaPluginApi
IdeaPluginUtil

I already have my utils and mocks in a separate tree so it would be very easy for me start it.

I propose the top level after intellijs but with org instead of com:

org.intellij.openapi.vcs.AuroraVcs

Jacques

0

Correction. The package will be:
org.intellij.psi.codeStyle
not
org.ideaplugins.reformat

0

Hi Timur,

Yes, this looks workable. Will you upload a new version of ReformatPlugin
that includes these classes and activates reformatManager? I'll hook in at
that point.

Thanks very much! -

-Dave

"Timur Zambalayev" <itnadmin@jetbrains.com> wrote in message
news:11815874.1052867285898.JavaMail.jrun@is.intellij.net...

Dave,

>

Another attempt. See below. Also:

>
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/tz-ip/tz-ip/MockTests/src/main/org/ideaplugins/reformat/
>

1) Provided access to editor and dataContext in ReformatEvent.
2) reformatManager.isActive(). I'll activate it in initComponent().
3) You'll receive events after reformatRange too.

>

Tell me if it's ok for you. BTW, everything is checked in so you can also

try it via cvs

( http://sourceforge.net/projects/tz-ip/ ).

>
>

=======================================================

 package org.ideaplugins.reformat;
>
> import com.intellij.openapi.actionSystem.DataContext;
> import com.intellij.openapi.editor.Editor;
> import com.intellij.psi.PsiElement;
>
> public class ReformatEvent {
>
>     private final PsiElement psiElement;
>     private final Editor editor;
>     private final DataContext dataContext;
>
>     public ReformatEvent(PsiElement psiElement, Editor editor, DataContext
dataContext) {
>         this.psiElement = psiElement;
>         this.editor = editor;
>         this.dataContext = dataContext;
>     }
>
>     public final PsiElement getPsiElement() {
>         return psiElement;
>     }
>
>     public final Editor getEditor() {
>         return editor;
>     }
>
>     public final DataContext getDataContext() {
>         return dataContext;
>     }
>
> }
> ]]>

=======================================================

 package org.ideaplugins.reformat;
>
> import com.intellij.openapi.actionSystem.DataContext;
> import com.intellij.openapi.editor.Editor;
> import com.intellij.psi.PsiElement;
>
> public final class ReformatRangeEvent extends ReformatEvent {
>
>     private final int startOffset;
>     private final int endOffset;
>
>     public ReformatRangeEvent(PsiElement psiElement, Editor editor,
DataContext dataContext, int startOffset, int endOffset) {
>         super(psiElement, editor, dataContext);
>         this.startOffset = startOffset;
>         this.endOffset = endOffset;
>     }
>
>     public int getStartOffset() {
>         return startOffset;
>     }
>
>     public int getEndOffset() {
>         return endOffset;
>     }
>
> }
> ]]>

=======================================================

 package org.ideaplugins.reformat;
>
> public interface ReformatListener {
>
>     /**
>      * Will be called after codeStyleManager.reformat(psiElement) or
>      * codeStyleManager.reformatRange(psiElement, startOffset, endOffset).
>      */
>     void psiElementReformatted(ReformatEvent reformatEvent);
>
> }
> ]]>

=======================================================

 package org.ideaplugins.reformat;
>
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Collections;
>
> public final class ReformatManager {
>
>     private static final ReformatManager REFORMAT_MANAGER = new
ReformatManager();
>
>     private final Collection listenerCollection = new ArrayList(1);
>     private final Collection unmodifiableListenerCollection =
Collections.unmodifiableCollection(listenerCollection);
>
>     private boolean active;
>
>     private ReformatManager() {
>     }
>
>     public void activate() {
>         active = true;
>     }
>
>     public Collection getReformatListeners() {
>         return unmodifiableListenerCollection;
>     }
>
>     // public API for clients:
>
>     public static ReformatManager getInstance() {
>         return REFORMAT_MANAGER;
>     }
>
>     public void addReformatListener(ReformatListener reformatListener) {
>         listenerCollection.add(reformatListener);
>     }
>
>     public boolean isActive() {
>         return active;
>     }
>
> }
> ]]>

=======================================================

>
>

Timur

>
>

Timur,

>

Sorry for the delay in responding.

>

So your idea is that I will simply register with your
plugin to be called
after you've invoked IDEA's functions? Should work,
but I have a few
thoughts.

>

1) I need not a PsiFile but an Editor and a
DataContext (see method
executeWriteAction().) The editor allows me to
obtain the current
selection. Given your code, how would I determine
the Editor and
DataContext?

>

2) I'll call your ReformatManager.getInstance() to
get a reference to the
singleton but will have to write some reflection code
to see if your
ReformatManager class is present.

>

3) Yes, I need the selection (if there is one) to be
intact when I'm
invoked.

>

Thanks,
-Dave

>


0

Please sign in to leave a comment.