2 new plugin ideas
I had two ideas for plugins, I might write them, but if someone else wanted to, that would be cool too. Also, either of them might already be implemented somewhere and I just didn't see it.
1. Live Rearrange Plugin
I press and hold CtrlShiftR, or some key, and a window that looks like the Recent Files List (in that it's undecorated and small) comes up, showing the current class's PSI. While still holding down CtrlShiftR, I can drag and drop items within the structure. This will be useful until the IDEA Structure view allows for drag and drop.
2. Permissions Modifier Plugin
Allows you to define a set of keys in the keymap that toggle or set different permissions flags for the field, method, or class that the cursor is in, or that is in the selection. For example, if I have:
private Thing getThing() {..}
I could have the cursor anywhere on that line before the {, and I press CtrlShiftP, and I get:
public Thing getThing() {..}
Then I press CtrlShiftS, and I get:
public synchronized Thing getThing() {..}
I press CtrlShiftS again, and get:
public Thing getThing() {..}
If anyone has any comments on these, or ideas, or if someone wants to implement them, let me know (so I know not to try first :).
请先登录再写评论。
Try the ToggleKeywordPlugin for the second request. It only works when you are on top of the keyword though. Still better than nothing and you could modify the source of the plugin to do it anywhere on the line like you proposed.
Jacques
I've actually started writing this plugin, and it's going to be much more flexible than the toggle keyword plugin.
>> 1. Live Rearrange Plugin
Did you say, "Rearrange?" :)
The Rearranger plugin has an optional file structure popup, which shows a tree view of the proposed rearrangement of classes and class members, including nesting levels of extracted methods. It was intended as a preview of the proposed rearrangement; you can OK or cancel it. I'm modifying it now to show the rules that caused items to be moved; thus it acts as a sort of rearrangement debugger.
In theory I could modify it to show a flat list (essentially identical to the ctrl-F12 file structure popup) and let you drag things around, then complete the rearrangement. Or perhaps I could leave it as a tree, so that by moving a method all its related (extracted) methods are moved with it. (That would for example keep overloaded methods together and getter/setters together, if those rearrangement options were checked.)
Would you need to drag multiple items at a time -- e.g. a multiple line selection to a new position -- or is one line at a time sufficient?
Larger question - do you use the Rearranger plugin? Does it lack some capability that is causing you to want to rearrange things by hand? I'm wondering what caused you to come up with this plugin idea.
Thanks,
-Dave
I would like to use no features of the current Rearranger for this. I don't want anything automatically rearranged, I want it all done manually. I'd like a tree for the current PsiFile, looking like this:
- class Class1 {
- private int f
- private int g
- public String x(int y)
- public void setSomething(Something s)
- private class InnerClass {
- private int h
- class Class2 {
- private int g
I'd like all nodes that the cursor is not currently in (in the editor), to be collapsed. So if my cursor were in the method String x(int y):
- class Class1 {
- private int f
- private int g
- public String x(int y)
- public void setSomething(Something s)
+ private class InnerClass {
+ class Class2 {
Note that if the file only has one toplevel class, I'd like it to be flatter:
- private int f
- etc.
- private class InnerClass {
- private int h
All I want is to be able to drag items with one click-drag, and to be able to quickly and easily open and close the window (I prefer the window goes away when you release the key, but pressing it again would be okay too).
I'd be happy with only being able to drag to a different position in the same parent, for now, but more advanced features like being able to drag a field from an inner class to the outer class would be cool too.
It is important to have the standard IDEA method/class/interface/field icons next to each tree node, too.
If this is too much of a request, or you don't have time, I might want to write it on my own anyway. Just let me know.
I missed two of your questions.
1. Dragging one at a time is fine, but eventually in later versions I'd like to be able to move a selection.
2. I don't use Rearranger because I like to decide the structure of my file myself, both because different classes require different layouts, for clarity of reading and maintaining the code, and also because automatic rearrangement makes me lose some of my mental image of the file.
Hi Keith,
Much of what you ask for is already implemented, or would be easy to do in the Rearranger plugin.
- no automatic rearrangement is the default behavior (no rules)
- the file structure popup is in tree form, like your first example; only difference is that in the last official version, it doesn't expand inner classes. (In the version on my laptop, I think it does.)
- I wasn't sure what you meant by your example for expanding only nodes that the cursor is in. It would be simple to determine the current cursor location and expand only that node. Your example was "cursor in String x()" method but the expanded nodes on the tree were the InnerClass and Class2, neither of which contain the cursor. Did I miss something?
- Simplifying the tree by removing a single top level is trivial.
- Standard IDEA icons appear at each node.
What is left to do is the drag and drop. I don't think it would be easy to move items between levels; that's like a pull member up/push member down refactoring operation. Moving them within a class would be easy, though. Doesn't sound hard.
Would you mind testing the Rearranger's popup to see if it meets your basic expectations of a single keystroke (Ctrl-Alt-R) and fast response? If you install and make no configuration changes, it won't rearrange anything. You just need to go to the General option pane and request "Confirm before rearranging." Then, after Ctrl-Alt-R, just cancel the popup dialog.
It could be modified so that, perhaps with a different keystroke or when a general checkbox "Manual Rearrangement" checked, a dialog with the features you described (drag/drop, node expansion criteria, etc.) is presented.
I'm not sure if the dialog can be dismissed when the keystroke is released; I think that the plugin is just called when the keystroke is pressed, and has no hook to see the release. Maybe you know a way. (And maybe, since you have a close family relative :) who is an expert on Java threads, you can help me with http://www.intellij.net/forums/thread.jsp?forum=23&thread=51324&message=646192. IntelliJ is probably too busy to tell me it's not possible...)
As for my time, I'm a bit behind where I want to be in releasing new versions of both my plugins. But if all goes well I could do it fairly soon. What's your time frame for completion - days, weeks, or minutes? :)
If not, you're welcome to scavenge whatever might be helpful from the existing plugin code. Though you should probably wait for the next release -- code is much cleaner. I'd be embarrassed to have you see the existing code! :)
-Dave
By "expanded" I mean "the children are visible." In my example, InnerClass and Class2 are collapsed.
The default keystroke is CtrlAltShift+R, it took me a while to find it (I think it should maybe be in the Refactoring menu).
The dialog should only be as tall as the number of tree nodes in the current tree, (up to a certain height, then it should scroll).
The keystroke pressed again should definitely close the dialog. I think there should be no OK/Cancel buttons - none of the manual changes are going to be manually undoable pretty easy, or with Edit->Undo.
I'd prefer if the window looked more like the Ctrl+E dialog, so it wasn't a whole new window, more like a giant tooltip.
I like the checkboxes, but I'd prefer if they were toolbar buttons like in IDEA's Override dialog. Also I'd like an option to show the signature as "String something(int x)" instead of "something(int x): String".
Also, like I said, I'd like any root nodes with no siblings to be hidden.
I suggest a different Action.
Have you heard of SwingUtilities.invokeLater(Runnable) or invokeAndWait? They run the given Runnable in the Swing event thread. I don't completely understand your problem, but it seems that you want to run a write action and update a progress bar, simultaneously, but both must be done in the Swing event thread.
Here's what I suggest. Essentially you are splitting up the rearranging task file by file, and updating the progress bar after each file is rearranged. Start a new thread:
To run the batch rearranging, you would start that thread, and keep track of which files need to be rearranged in the outer class of that thread, or whatever.
Did you try something like this? If not, you might want to try it. It's a pretty bad limitation that write actions must be done in the Swing event thread, but I bet it prevents some deadlocks.
I think maybe I would get this done myself within 2 or 3 weeks, but no earlier than in a week, since I have a bunch of stuff to do for the next week.
Thanks!
- re "expanded" - sorry, I misread your tree. "+" nodes are collapsed, "-" are expanded. All makes sense.
- sorry about the keystroke too. I have "Reformat" plugin installed also, Ctrl-Alt-R then does a code reformat, optimize imports, runs rearranger and tabifier. You're right, by itself Rearranger is ctrl-shift-alt-R.
- I don't know if the "keystroke pressed again closes dialog" behavior is possible in a plugin. Probably is if you know what to hook; I just haven't seen it done. (CtrlE and CtrlF12 dialogs don't work that way.) Anybody have experience with this?
- checkboxes vs buttons, hiding root nodes, etc. are relatively minor changes. If we can find reasonable icons for the buttons, that is. :)
- re: Swing thread. Yes, I discovered the invokeLater() method. Problem essentially is that the Swing thread cannot return (because I won't let it) from the runWriteAction() method in the plugin. I don't know if it is safe to do so. Returning before the plugin is done updating the current document could allow a user to make manual changes to the file before the plugin finished its updates (possible if the plugin takes several seconds). But as long as I won't let the SDT return, nothing gets painted. I either have to manually force the SDT to do its Swing work without returning from runWriteAction() , let it return, or get another thread to do Swing work temporarily. I'm afraid it will have to be the 2nd option.
- I'll touch base with you in a week, when you're done with your immediate work, to see who's got time. It will probably be you. :)
Thanks for your suggestions & help!
-Dave
It should be, but the problem might be programmatic access to the keymaps - can you do that? You could surely bind the key in your Swing window if you knew what it was, right?
Did you try my suggestion? It parses the directory file by file, updating the UI between each file. That is, if it works right.
Better the Code menu, because rearranging is no refactoring.
Tom
Keith Lea wrote:
+1
Well, (automatic) Rearranger is ok, but sometimes a plain manually one
would be enough/better - because defining rules is a relatively hard job.
Tom
- regarding keystrokes. The plugin.xml file defines an association between actions and plugin activation; actions are keyboard shortcuts or menu items. I'm not aware of a way to hook keystrokes directly. There would have to be some XML to define that. Otherwise the plugin is inactive (e.g. has no Swing window) until invoked by IDEA. Perhaps it can be done, but I've never read anything to that effect. Maybe the plugin can hook something when instantiated.
Any plugin writers or somebody from JetBrains care to comment? We want to write a plugin that brings up a window like Ctrl-E does on a certain keystroke, and closes it when the keystroke is released. Alternatively, we need to be informed of other keystrokes or actions (such as pressing Esc or clicking in a different window) which would close the plugin's window.
- I did try your suggestion. Problem was that I had a "Cancel" button on the progress dialog that was inoperative. The mouse click was queued, but there's no way that I know of to force the Swing thread to pick it up without simply releasing the Swing thread and letting it return to its normal work loop. So the progress bar was updated (though there were some painting problems, which could probably be overcome) but the Cancel button, the dialog "close" button, etc. were all inoperative.
I understand how keymaps work, but I'm sure you can listen for events in your own Swing windows. You could also add a listener for loss of focus.
Those buttons should work, but it should take a whole iteration for them to be executed. I don't know why they wouldn't be executed at all.
- Ah, I understand now what you're proposing. Yes, I can hook keystrokes in my own window. What I don't know is if I will be guaranteed to see the release of the keystroke that fired my plugin. That keystroke press event went to an IDEA window. Assuming even that my window pops up before the keystroke is released, would the release event go to the original window or to mine? I would expect the former. I recollect some design goal to send matching press and release events to the same listener, but now I can't remember if it was Swing, X or some other windowing system from my murky past. :) Have to experiment. [This is all about implementing the "window goes away when you release the key" feature.]
- I'll check my old test code again. Maybe it did not match your suggested solution after all, or something else prevented the cancel and close buttons from operating.
Thanks for all your help.
-Dave
Keith,
I've begun work on the manual rearranging capability. Drag/drop is working, which was the one that most concerned me. I'll try to implement as many of your other feature details as possible.
-Dave
That's great, I haven't had time to work on it myself, I've been busier than I thought I would be. Let me know when it's usable and I'd be glad to test it.
Excellent! I'm looking forward to the next release.
Tom
Hi Keith,
Perhaps you (or another reader of this thread) can advise me on a Swing issue. I'm not a Swing guy. :)
I'm trying to implement the ctrl-E type popup for the live rearranger. I have a choice of containers to use; I've tried JWindow, JOptionPane, and Popup. Each has advantages and disadvantages.
Popup was my #1 choice until I discovered that tooltips don't work over the nice new icons (which replace the clunky old checkboxes). I guess that is because tooltips are themselves implemented with a Popup. However, focus issues were handled nicely - click elsewhere and the popup went away.
JOptionPane is a heavy dialog, with title bar; you'd have to press "OK" or the close box to finish. I know you wouldn't like that.
JWindow shows tooltips and has no extraneous title bar/close box/OK buttons, but I haven't found an easy way to make it go away. I don't get focus events, or mouse events outside the window, so it looks like I'd have to add a button to close the window manually. Which you wouldn't like. :)
Am I overlooking another container that would work better?
Is there a way to enable tooltips over a Popup, or should I just do that manually? (I don't know if two popups can be displayed simultaneously; might be that when the second appears, the first is closed.)
Thanks -
-Dave
Hi Dave,
I don't see why using a Popup would preclude tooltips... JPopupMenu uses
a Popup and tooltips work just fine on menu items. Are you creating the
Popup with the PopupFactory? This might influence things.
If you still have no luck with Popups, have you tried a JFrame with no
decoration? JFrame.setUndecorated(false) will remove the border and
buttons.
N.
Dave Kriewall wrote:
sorry, of course that should be JFrame.setUndecorated(true) :$
Nathan Brown wrote:
>> Hi Keith,
>>
>> Perhaps you (or another reader of this thread) can advise me on a
>> Swing issue. I'm not a Swing guy. :)
>>
>> I'm trying to implement the ctrl-E type popup for the live
>> rearranger. I have a choice of containers to use; I've tried JWindow,
>> JOptionPane, and Popup. Each has advantages and disadvantages.
>>
>> Popup was my #1 choice until I discovered that tooltips don't work
>> over the nice new icons (which replace the clunky old checkboxes). I
>> guess that is because tooltips are themselves implemented with a
>> Popup. However, focus issues were handled nicely - click elsewhere
>> and the popup went away.
>>
>> JOptionPane is a heavy dialog, with title bar; you'd have to press
>> "OK" or the close box to finish. I know you wouldn't like that.
>>
>> JWindow shows tooltips and has no extraneous title bar/close box/OK
>> buttons, but I haven't found an easy way to make it go away. I don't
>> get focus events, or mouse events outside the window, so it looks like
>> I'd have to add a button to close the window manually. Which you
>> wouldn't like. :)
>>
>> Am I overlooking another container that would work better?
>>
>> Is there a way to enable tooltips over a Popup, or should I just do
>> that manually? (I don't know if two popups can be displayed
>> simultaneously; might be that when the second appears, the first is
>> closed.)
>>
>> Thanks -
>> -Dave
You can use JFrame and setUndecorated, and add a WindowFocusListener to determine when the window's focus is lost.
JFrame works fine, but a new task appears in the Windows task bar when it's displayed. This seems undesirable.
Does this happen even when setUndecorated is set? If so, you could use JDialog, and do the same focus listener thing, I think.
Yeah, even when undecorated.
I've been playing with JDialog today for two reasons - it avoids the task bar entry, plus modality is taken care of. However, a modal JDialog doesn't ever lose focus; mouse clicks outside the dialog ring the bell but I can't seem to hook them anywhere. And no window focus events come through. Some low level code is intercepting the mouse click.
I haven't tried a non-modal JDialog yet; it might solve the focus listener problem, but it leaves the problem of not letting the Swing thread return until the dialog closes (i.e. simulating modality). I saw how Sun implements that in their dialog code; if my code can get access to the same methods, I could plagiarize. (My specialty. :)
There are examples of how to do this in several plugins:
TabSwitch, SmartIntroduce, UnitTest (that all came from TabSwitch originally. I am too a master at plagiarizing ;)
In UnitTest the class is named SelectDialog. It uses the DialogWrapper (It is used for the choice menus you get when you select a template to create a test entity).
As far as handling the returning control to the swing thread you just have to have the "after" action be in a runnable that you pass to your dialog. On confirmation the dialog will run that runnable. If the users cancel (clicks outside, click on cancel) the dialog goes away.
Jacques
Attachment(s):
SelectDialog.java
This is getting annoying :) -- Swing seems determined to frustrate our plans!
Jacques, I took your code but in running it I see that DialogWrapper merely calls a modal JDialog. If I click outside, the bell is rung. I installed your JUnit plugin to verify; if I click outside, I get a bell; the dialog doesn't close.
It looks like:
(1) if I want modality, I have to use JDialog with setModal(true). The code that JDialog uses to keep the Swing thread busy handling events without returning is package private to AWT; I can't call it myself. FYI, the code in question is:
2). If I use JDialog, I can't get mouse clicks outside the dialog at all. I think this is because JDialog extends Dialog, which is implemented as a Windows dialog. (I suppose it could be different on other platforms.) If Windows determines that the dialog is modal, it intercepts mouse clicks outside the dialog and rings the bell. The mouse event never even gets to the JVM. The only way to close a JDialog, then, is to click on a button like OK/Cancel, or close the window.
Keith, unless you can think of something else to try, I think I'll just do a dialog for now so we can move on with testing. You can probably get away with just hitting 'Enter' to close the dialog. Someday we can replace it with something better.
On the good news front - turns out Alt-R keystroke is not already mapped to something. So that will be the default for Live Rearranger.
-Dave
Why do you want modality? Couldn't you use JDialog with setModal(false)? Also, did you look into JInternalFrame? I don't know if it can be used outside a JDesktopPane.
I guess Murphy's Law operated in my favor for a change. Right after the last message I sent, I found another helpful feature. :)
So now, using a modal JDialog, I am able to get most of the behavior you described:
- Press Alt-R and hold -- the dialog comes up; release and the dialog disappears.
- Press Alt-R and release before the dialog comes up; then any keystroke (except space and tab) removes the dialog. (Space and tab I thought would be useful for toggling checkbox or selection state.)
- Only thing I can't handle is a mouse click outside the dialog -- at least not on Windows.
I added a temporary KeyEventDispatcher to intercept all the keystrokes when the dialog is visible. Any key release (except space or tab) closes the dialog.
I did take a look at JInternalFrame early on; it didn't give me any visible benefit over JFrame.
I think I need modality only because of the way I designed the solution. When a drag & drop occurs, I exchange a pair of entries in a tree. When the dialog is exited, I create a new file based on the order of the entries in the tree. So I don't want the dialog exited until all the manual rearranging has taken place. (To make changes to the editor document as drag & drop operations occur would be pretty sluggish, I think.)
Sorry if this explanation isn't very clear, I'm a bit rushed to leave now -
-Dave
Hi Keith,
Sorry, my last post was unclear and incomplete.
JInternalFrame would avoid the "task in the taskbar" problem if I could find a desktop pane to attach it to. (In my experiments of course I was creating my own so it looked just like JFrame.) The only container I have found in IDEA is openapi.wm.WindowManager's suggestParentWindow() method, which does return a Window. So I'll try my JFrame code as JInternalFrame inside that Window's content pane. I'll bet it will work.
After giving it more thought, I guess I don't need modality. I'll just perform the actual rearrangement of the document when I decide to close the popup window. There's no danger of the underlying document getting out of sync with what's in the popup window (if I let the Swing dispatch thread return from plugin to IDEA); the user can't change the document without an appropriate key or mouse event, and those will trigger closing the popup.
In any case, it looks like the JDialog approach won't work after all. Drag and drop appeared to stop working in the JDialog. (same JComponent in JFrame did drag&drop just fine.) Strange.
Thanks for making me think. :)
-Dave
Okay, cool. Couldn't you add a listener to the PSI tree and update the window when it changes?