So erstellen Sie ein IntelliJ-Plugin: Erstellen Sie einen einfachen Wörterbuchfinder

Die meisten von uns Entwicklern verwenden IntelliJ-Plattformen, entweder IDEA, PHPStorm, WebStorm, Android Studio, PyCharm, und die Liste geht weiter und weiter. Manchmal, wenn wir es verwenden, stellen wir fest, dass eine Funktion fehlt, aber wir haben keine Ahnung, wie wir diese Funktion tatsächlich hinzufügen und schließlich einfach ohne sie leben können.

In diesem Artikel werde ich beschreiben, wie wir ein einfaches Plugin für alle IntelliJ-IDEs erstellen können. Wenn Sie also eine project.dicDatei hinzufügen , wird diese automatisch als eines Ihrer Wörterbücher hinzugefügt. Außerdem wird in Paketen nach der Datei gesucht, sodass Pakete dem Wörterbuch benutzerdefinierte Wörter hinzufügen können. Eine .dicDatei ist ein einfaches Wörterbuch, bei dem jede Zeile ein Wort im Wörterbuch ist.

Das Projekt ist nur ein Beispiel, um Ihnen den Einstieg in die Entwicklung eigener Plugins zu erleichtern. Aber es ist auch eine Funktion, die mir gefehlt hat. Wenn ich ein benutzerdefiniertes Paket mit meinen eigenen Wörtern entwickle, hasse ich es, dass ich sie jedes Mal im Wörterbuch auf Projektebene hinzufügen muss.

Projekt erstellen

Beim Erstellen von Plugins für IntelliJ müssen wir die Option wählen, dies entweder in Java oder in Kotlin zu tun. Ich werde es in Java tun, da die meisten Benutzer damit vertraut sind. Da dies ein Java-Projekt ist, werden wir IntelliJ IDEA als unsere IDE verwenden.

Laut Entwicklungsleitfaden wird empfohlen, ein Projekt mithilfe von Gradle zu erstellen. Wir öffnen zunächst preferencesund prüfen, ob Gradleund Plugin DevKitPlugins installiert sind.

Nach der Installation der Plugins und dem Neustart der IDE gehen wir zum neuen Projektablauf und darunter Gradle. Hier gibt es jetzt eine Option, IntelliJ Platform Plugindie wir brauchen.

Gehen Sie dann den Rest des Projekterstellungsablaufs wie gewohnt durch - in diesem Projekt wähle ich die folgende Konfiguration.

Einrichten plugin.xml

Nachdem wir ein Projekt haben, müssen wir unsere plugin.xmlDatei einrichten und build.gradle. Die plugin.xmlDatei ist eine von IntelliJ verwendete Datei, die alle Informationen zum Plugin definiert. Dies beinhaltet den Namen, Abhängigkeiten, welche Aktionen hinzugefügt werden sollen oder ob etwas in IntelliJ erweitert werden soll. Grundsätzlich definiert diese Datei alles, was Ihr Plugin tun sollte, und ist das Stammverzeichnis Ihres Projekts. In unserer build.gradleDatei können wir einige der Werte definieren plugin.xmlund Informationen darüber, auf welcher Version von IntelliJ wir unser Plugin beim Erstellen mit Gradle testen möchten.

Beginnen wir mit der Definition unserer plugin.xmlDatei. Sie finden die Datei in src/main/resources/META-INF/plugin.xml. Wir möchten, dass unser Plugin auf allen IntelliJ-IDEs verfügbar ist, also setzen wir uns dependenciesauf com.intellij.modules.lang. Im Moment sieht unsere Datei folgendermaßen aus:

 dk.lost_world.Dictionary Dictionary GitHub com.intellij.modules.lang

Derzeit hat dies jedoch keine Logik und wir registrieren nichts auf der IntelliJ-Plattform.

Da dieses Projekt project.dicDateien in einem Projekt findet und sie als Wörterbücher in diesem Projekt registriert, müssen wir eine Komponente auf Projektebene registrieren. Diese Komponente wird aufgerufen, wenn ein Projekt geöffnet und geschlossen wird. Lassen Sie uns eine Klasse erstellen und die ProjectComponentSchnittstelle implementieren . Wenn wir mit der Maus über den Klassennamen fahren, wird uns mitgeteilt, dass die Komponente nicht registriert ist.

Wir können dann die aufgerufene Aktion aufrufen Register Project Componentund sie wird für uns in der plugin.xmlDatei registriert .

Wenn wir öffnen, sollte plugin.xmlder folgende Code hinzugefügt werden. Wenn es beim Aufrufen der Aktion nicht hinzugefügt wurde, fügen Sie es einfach manuell hinzu.

  dk.lost_world.dictionary.DictionaryProjectComponent 

IntelliJ-Dateisystem

Wenn Sie mit Dateien in IntelliJ arbeiten, verwenden wir eine V irtual F ile S ystem (VFS). Das VFS bietet uns eine universelle API, um mit Dateien zu kommunizieren, ohne dass wir darüber nachdenken müssen, ob sie von FTP, einem HTTP-Server oder nur auf der lokalen Festplatte stammen.

Als unser Plugin sucht nach Dateien mit dem Namen project.dices natürlich Notwendigkeit , mit dem zu reden V irtual F ile S ystem. Alle Dateien im VFS sind virtuelle Dateien. Das mag ein wenig einschüchternd klingen, aber in Wirklichkeit ist es nur eine API für ein Dateisystem und für eine Datei. Die Art und Weise , darüber nachzudenken ist nur , dass die V irtual F ile S ystem Dateisystem - Schnittstelle ist und die virtuellen Dateien sind Ihre Dateien.

Einstellungen für die Rechtschreibprüfung

Da IntelliJ bereits Unterstützung für .dicDateien und Rechtschreibprüfung im Allgemeinen bietet, müssen wir unsere project.dicDateien nur in den Einstellungen für die Rechtschreibprüfung registrieren .

Alle Einstellungen für die Rechtschreibprüfung werden in einer Klasse namens gespeichert com.intellij.spellchecker.settings.SpellCheckerSettings. Um eine Instanz davon zu erhalten, rufen Sie einfach die getInstanceMethode auf (die meisten IntelliJ-Klassen haben eine getInstanceMethode, die die ServiceManagerdarunter liegenden IntelliJs verwendet ).

Die Einstellungsklasse hat eine Methode namens aufgerufen, getCustomDictionariesPathsdie alle Pfade zu Wörterbüchern zurückgibt, die vom Benutzer installiert werden.

Wenn wir uns die Methodensignatur ansehen, sehen wir auch eine Annotation namens AvailableSince. Wir werden den Wert in dieser Anmerkung später verwenden, um die Mindestversion anzugeben, die erforderlich ist, damit unser Plugin funktioniert.

Da die Methode eine Liste zurückgibt, können wir adddie Methode einfach aufrufen , um einen neuen Pfad zu einem Wörterbuch hinzuzufügen.

Ausführen unseres Plugins (build.gradle)

Da wir jetzt wissen, wie man der Rechtschreibprüfung ein Wörterbuch hinzufügt, fügen wir dazu ein kleines Codebeispiel in unsere DictionaryProjectComponentKlasse ein.

public class DictionaryProjectComponent implements ProjectComponent { private Project project; public DictionaryProjectComponent(Project project) { this.project = project; } @Override public void projectOpened() { SpellCheckerSettings .getInstance(project) .getCustomDictionariesPaths() .add("./project.dic"); }}

Dieser Code registriert bei project.dicjedem Öffnen des Projekts eine Datei aus dem Stammverzeichnis unseres Projekts.

Um unser kleines Beispiel zu testen, müssen wir unsere build.gradleDatei aktualisieren . Im intellijAbschnitt der Gradle-Datei fügen wir hinzu, in welcher Version von IntelliJ wir verwenden möchten. Diese Versionsnummer stammt aus der AvailableSinceAnmerkung zur SpellCheckerSettingsKlasse.

plugins { id 'java' id 'org.jetbrains.intellij' version '0.4.4'}group 'dk.lost_world'version '1.0-SNAPSHOT'sourceCompatibility = 1.8repositories { mavenCentral()}dependencies { testCompile group: 'junit', name: 'junit', version: '4.12'}// See //github.com/JetBrains/gradle-intellij-plugin/intellij { pluginName 'Dictionary' version '181.2784.17' type 'IC' downloadSources true}

Running the runIde command from gradle will start up an instance of IntelliJ of the specific version. After starting up the testing IDE our plugin should have been run. If we open up preferences > Editor > Spelling > Dictionaries we can see under custom dictionaries that the path we specified in our example is now added.

We are now able to test our plugin, so now it is time to build it out correctly so it finds the project.dic files and registers them for us.

In the DictionaryProjectComponent::projectOpened method, we need to first find all files called project.dic and register them and also add a file listener so when new project.dic files are added, they are registered automatically.

Dictionary Class

We will have a class called Dictionary, this class will contain the logic for us to register and remove files from the dictionary. The class will have the following public methods:

void registerAndNotify(Collection files)

void registerAndNotify(VirtualFile file)

void removeAndNotify(VirtualFile file)

void moveAndNotify(VirtualFile oldFile, VirtualFile newFile)

These methods will also create a notification about what happened, so the end user knows what changed with the custom dictionaries. The end file for this will look the following way:

Finding all dictionary files

For finding all the dictionary files in the project called project.dic we use the class FilenameIndex. The file is in the namespace com.intellij.psi.search.FilenameIndex, it has a method getVirtualFilesByName which we can use to find our project.dic files.

FilenameIndex.getVirtualFilesByName( project, "project.dic", false, GlobalSearchScope.allScope(project))

This call will return all Virtual Files which matches the search criteria. We then put the return result into the Dictionary class method registerAndNotify.

@Overridepublic void projectOpened() { Dictionary dictionary = new Dictionary(project); dictionary.registerAndNotify( FilenameIndex.getVirtualFilesByName( project, "project.dic", false, GlobalSearchScope.allScope(project) ) );}

Our code is now able to find project.dic files at start up and register them, if they are not already registered. It will also notify about the newly registered files.

Adding a Virtual File Listener

The next part is for us to listen for changes in virtual files. To do this we need a listener. For this we need the com.intellij.openapi.vfs.VirtualFileListener.

In the docblock for the listener class we can see that to register it we can use VirtualFilemanager#addVirtualFileListener.

Let’s create a class named DictionaryFileListener and implement the methods which we need for our project.

Then we update our projectOpened class to also add the VirtualFileListener.

@Overridepublic void projectOpened() { Dictionary dictionary = new Dictionary(project); dictionary.registerAndNotify( FilenameIndex.getVirtualFilesByName( project, "project.dic", false, GlobalSearchScope.allScope(project) ) ); VirtualFileManager.getInstance().addVirtualFileListener( new DictionaryFileListener(dictionary) );}

Our plugin is now able to find our dictionary files at startup, but also listen for if a dictionary file is added later on. The next thing we need is to add information for our plugin listing.

Adding plugin information

To add information about the plugin, we open the build.gradle file and edit the object patchPluginXml. In here we need to specify which build version is required for the plugin, version of the plugin, description and change notes.

patchPluginXml { sinceBuild intellij.version untilBuild null version project.version pluginDescription """Plugin for having a shared dictionary for all members of your project.

It will automatically find any project.dic files and add themto the list of dictionaries.

It will also search packages for dictionary files and add them to our list of dictionaries. """ changeNotes """

0.2

  • Added support for listening for when a project.dic file is added, moved, deleted, copied.

0.1

  • First edition of the plugin.
"""}

We also update the version property to '0.2'of the gradle project itself. The plugin can now run on all versions since the method for registering custom dictionaries was added.

To test if it generates the desired output, we can run the gradle task patchPluginXml and under build/patchedPluginXmlFiles our generated plugin.xml file will be there.

Since IntelliJ version 2019.1, all plugins supports icons. As this is fairly new a lot of plugins do not have an icon, and your plugin can stand out a lot by having one. The naming convention is pluginIcon.svg as the default icon and pluginIcon_dark.svg for the darcula theme.

The plugin icons should be listed together with the plugin.xml file in the path resources/META-INF.

Building for distribution

The plugin is now ready to be built and shipped. To do this we run the gradle task buildPlugin. Under build/distributions a zip file will appear which you can distribute and install manually in your IDE. Add this zip file as a release under your github repo, so users have the option to download it manually from you repo.

Publishing a plugin

To publish our plugin so it can be downloaded directly from IntelliJ’s plugin repository, we need to login on our JetBrains account on the Plugin Repository website. When in here, a dropdown from your profile name shows an option to upload a plugin.

Input all the information in the dialog (you have to add a license, but that is pretty straightforward with Github). Here we add the distribution zip file.

When you submit the form, you can now see your plugin in the plugin repository. However other users do not have access to it before IntelliJ has approved it. Approving your plugin normally takes 2–3 days.

Updating your plugin via Gradle

After the plugin has been created, we can update it programmatically. To do this the best practice is to create a token. Open up jetbrains hub and go to the authentification tab. From here press New token... and add the scope Plugin Repository.

When pressing create you get a token. Create a file called gradle.properties and add the token under the key intellijPublishToken (remember to git ignore this file).

In our build.gradle file, we simply add the following:

publishPlugin { token intellijPublishToken}

And we can now run the gradle task publishPlugin for publishing our new version. All versions numbers have to be unique or else it will fail updating. When an update is created, you have to wait 2–3 days again for them to approve the update.

After waiting some days our plugin has now been approved and can now be found in the plugin marketplace by searching for dictionary!

Conclusion

I hope this article has given you more courage to start developing your own plugins. One of the biggest problems I had while developing it was to find out which classes to use. IntelliJ has an extensive guide which I would recommend that you read from start to end, however a lot of classes are not mentioned in there. In cases where you get stuck, they have a Gitter chat which is really helpful and there are people from IntelliJ on there to help also.

The source code for this project can be found on Github and the plugin we created is in the JetBrains marketplace.