This article "DeNA IP Platform Division Advent Calendar 2017" This is the article on the 19th day.
In my team, all server-side engineers are coding using IntelliJ IDEA. Recently, I suddenly wondered "How do I make a plug-in for IntelliJ IDEA?", And while studying, I tried to make something like a desktop mascot that appears on the IDE, so I'd like to summarize the flow.
When you start the IDE, strange creatures will appear. Experience points (Exp) will accumulate as you code. (I wanted to add a function that supports coding a little more, but that's okay)
You need IntelliJ IDEA to make a plugin. (Community Edition is also acceptable) It seems that debugging during plug-in development will be easier if the source code of IntelliJ IDEA Community Edition is also dropped, but I did not use it this time. The detailed procedure is described in Official document, so I will omit it.
[File> New Project> IntelliJ Platform Plugin> Next] on IntelliJ IDEA Give the project name an appropriate name. (This time is Nunyu)
The base of the plug-in is the following three types of components.
--Application level components: Components that are initialized when the IDE starts --Project level components: Components created for each project on the IDE --Module level components: Components created for each module on the IDE
This time, I want the mascot to appear all the time across the project when the IDE starts, so I will create it with the Application level component. You can create a new one by right-clicking on the Project panel and selecting [New> Plugin DevKit> Application Component]. The component name is "Nunyu".
In fact, the plugin is now running on the IDE! Component initialization process Let's log and run it with ʻinitComponent`.
src/Nunyu.java
import com.intellij.openapi.diagnostic.Logger;
...
@Override
public void initComponent() {
Logger.getInstance(Nunyu.class).info("Nunyu");
}
...
To execute it, select [Run> Debug] from the menu. Then another IntelliJ IDEA will be launched and the plug-in will run in it. And you can see that there is a message in the debug console.
Let the component inherit JWindow
to display the window at initialization and render the image. This is the story of Swing
.
src/Nunyu.java
import com.intellij.openapi.components.ApplicationComponent;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Nunyu extends JWindow implements ApplicationComponent {
private Image image;
private ImagePanel imagePanel;
public Nunyu() {
}
@Override
public void initComponent() {
initWindow();
makeDraggable();
}
public void initWindow() {
image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/images/nunyu-alpha.png "));
setSize(100, 100);
setAlwaysOnTop(true);
setBackground(new Color(1, 0, 0, 0));
imagePanel = new ImagePanel();
add(imagePanel);
setVisible(true);
}
public void makeDraggable() {
final Point startPos = new Point();
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
startPos.setLocation(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
setLocation(e.getXOnScreen() - startPos.x, e.getYOnScreen() - startPos.y);
}
});
}
public class ImagePanel extends JPanel {
public ImagePanel() {
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
@Override
public void disposeComponent() {
// TODO: insert component disposal logic here
}
@Override
@NotNull
public String getComponentName() {
return "Nunyu";
}
}
--I want the background to be missing like a mascot, so use setBackground (new Color (0, 0, 0, 0))
to make the window transparent.
--Prepare transparent PNG in resources / images / nunyu-alpha.png
, set it tosetOpaque (false)
and render it.
--I want you to always display it at the top, so setAlwaysOnTop (true)
--I want to be able to drag the mascot, so look at the mouse event and implement it a bit makeDraggable ()
Now when you run it, you'll see a mascot that you can drag!
At this rate, even if the IDE loses focus, the mascot will remain displayed at the top and will not disappear. It's annoying. So, when the IDE loses focus, I will add a process to erase the mascot at the same time.
src/Nunyu.java
@Override
public void initComponent() {
initWindow();
setupFocusEvent();
}
...
public void setupFocusEvent() {
WindowManager.getInstance().addListener(new WindowManagerListener() {
@Override
public void frameCreated(IdeFrame frame) {
WindowManager.getInstance().getFrame(frame.getProject()).addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(WindowEvent e) {
setVisible(true);
}
@Override
public void windowLostFocus(WindowEvent e) {
if (e.getOppositeWindow() == null) {
setVisible(false);
}
}
});
}
@Override
public void beforeFrameReleased(IdeFrame frame) {
}
});
}
Add a WindowManagerListener
listener and add a WindowFocusListener
to that window each time a frame is created in the IDE. In this listener, implement windowGainedFocus
, which fires when the window gets focus, and windowLostFocus
, which fires when the window loses focus.
I want to display the mascot when the window on the IDE gets focus, so I can call setVisible (true)
. Conversely, if you lose focus, you can use ʻe.getOppositeWindow ()to get the window that will be the next focus, but if this is
null, the IDE will not have focus, so
setVisible. Call (false) `.
Now, next time I code in the editor, I will try to give the mascot experience points. This requires hooking editor keystrokes, but let's extend ʻeditorTypedHandler` to achieve this.
src/MyTypedHandler.java
import com.intellij.codeInsight.editorActions.AutoFormatTypedHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import org.jetbrains.annotations.NotNull;
public class MyTypedHandler extends AutoFormatTypedHandler {
public MyTypedHandler(TypedActionHandler originalHandler) {
super(originalHandler);
}
@Override
public void execute(@NotNull Editor editor, char charTyped, @NotNull DataContext dataContext) {
super.execute(editor, charTyped, dataContext);
Logger.getInstance(Nunyu.class).info("Typed: " + charTyped);
}
}
resources/META-INF/plugin.xml
...
<extensions defaultExtensionNs="com.intellij">
<editorTypedHandler implementationClass="MyTypedHandler"/>
</extensions>
...
When any character is typed in the editor, ʻexecute ()is called. Try to output the pressed key to the log while calling
super.execute ()as it is so as not to change the default behavior. Try running it now and edit something on the IDE that launches. You can see that the key you entered appears in the info log. This is how to hook this key, but it seems like [There is also a way to use
TypedHandlerDelegate`](https://qiita.com/shiraji/items/5a7a1654dc7b6da20a60), but when I tried it, I tried it in the IDE. When AutoCompletionPopup came out, it seemed that the event would not come over, so it didn't work. (Honestly, I don't know what is the correct answer.)
Even if the mascot can give experience points, it is sad that it is reset when the IDE is restarted. Therefore, I will try to make it possible to perpetuate the experience value given next.
Use PersistentStateComponent
to persist data in the plugin.
src/MyService.java
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
@State(
name = "MyService",
storages = {
@Storage("nunyu.xml"),
}
)
class MyService implements PersistentStateComponent<MyService.State> {
static class State {
public Integer exp = 0;
}
State myState;
public MyService() {
this.myState = new State();
}
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state;
}
public static MyService getInstance() {
return ServiceManager.getService(MyService.class);
}
}
I implemented a box that simply has the integer ʻexp. You can set the destination to be persisted with the
@State` annotation.
Also, don't forget to mention in plugin.xml
to use this service.
resources/META-INF/plugin.xml
...
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceInterface="MyService" serviceImplementation="MyService"/>
</extensions>
...
Register as ʻapplicationServiceas it will be used by components at the application level. Please note that there are other
projectService` etc.
First of all, let's start by displaying the experience points on the screen.
Rewrite the paintComponent ()
method of the ʻImagePanel class to display the
ʻexpof
MyService. Also, prepare the
repaint ()` method so that you can redraw each time your experience points increase.
src/Nunyu.java
...
public void repaint() {
imagePanel.repaint();
}
...
public class ImagePanel extends JPanel {
...
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
g.drawString("Exp. " + MyService.getInstance().myState.exp, 0, 10);
}
}
...
Next, write a process to increase the experience value at the time of key input and redraw.
src/MyService.java
...
public void increment() {
myState.exp++;
}
...
src/MyTypedHandler.java
...
@Override
public void execute(@NotNull Editor editor, char charTyped, @NotNull DataContext dataContext) {
super.execute(editor, charTyped, dataContext);
ServiceManager.getService(MyService.class).increment();
ApplicationManager.getApplication().getComponent(Nunyu.class).repaint();
}
...
That's it. As you code, your experience points will increase steadily!
By the way, I made a plug-in that was of no use (laughs), but by proceeding through trial and error, I somehow understood something like how to make a plug-in. It was quite difficult to find information from the documents, and I couldn't find anything like "What should I do if I want to do something like this ?!", and even such a simple one was very difficult. There are a lot of plugins on the market, so you can search for useful code from them, or Search in the community. community & topic = 200366979 & utf8 =% E2% 9C% 93) and you may or may not find the information you are looking for! This article was the end of a struggle, but if you have any suggestions, please comment.
Recommended Posts