Hvordan lage et IntelliJ-plugin - la oss bygge en enkel ordbokfinner

De fleste av oss utviklere bruker IntelliJ-plattformer, enten IDEA, PHPStorm, WebStorm, Android Studio, PyCharm, og listen fortsetter og fortsetter. Noen ganger når vi bruker den, oppdager vi imidlertid at en funksjon mangler, men vi aner ikke hvordan vi faktisk skal legge til den funksjonen og til slutt bare leve uten den.
I denne artikkelen vil jeg dekke hvordan vi kan lage et enkelt plugin for alle IntelliJ IDE- project.dic
ene, så når du legger til en fil, vil den automatisk legge den til som en av ordbøkene dine. Den vil også søke etter filen i pakker, slik at pakker kan legge til ord i ordboken. En .dic
fil er en enkel ordbok der hver linje er et ord i ordboken.
Prosjektet er bare et eksempel for å komme i gang med å utvikle dine egne plugins. Men det er faktisk også en funksjon jeg har savnet, som når jeg utvikler en tilpasset pakke med egne ord i, hater jeg at jeg må legge dem til hver gang i ordboken for prosjektnivå.
Opprette prosjektet
Når du oppretter plugins for IntelliJ, må vi velge å gjøre det i Java eller Kotlin. Jeg vil gjøre det på Java da de fleste brukere er kjent med det. Siden dette er et Java-prosjekt, vil vi bruke IntelliJ IDEA som IDE.
I følge utviklingsguiden er den anbefalte måten å lage et prosjekt på, ved å bruke Gradle. Vi starter med å åpne opp preferences
og sjekke om Gradle
og Plugin DevKit
plugins er installert.

Etter å ha installert programtilleggene og startet IDE på nytt, går vi til de nye prosjektene og under Gradle
. Her inne er det nå et alternativ IntelliJ Platform Plugin
som heter det vi trenger.

Gå deretter gjennom resten av prosjektopprettingsflyten som normalt - i dette prosjektet velger jeg følgende konfigurasjon.



Setter opp plugin.xml
Nå som vi har et prosjekt, må vi sette opp plugin.xml
filen vår og build.gradle
. Den plugin.xml
fil er en fil som brukes av IntelliJ som definerer all informasjon om tillegget. Dette inkluderer navnet, avhengigheter, hvilke handlinger det skal legge til, eller om det skal utvide noe i IntelliJ. I utgangspunktet definerer denne filen alt pluginet ditt skal gjøre, og er roten til prosjektet ditt. I build.gradle
filen vår kan vi definere noen av verdiene fra plugin.xml
, og informasjon som hvilken versjon av IntelliJ vi vil teste pluginet vårt når vi bygger med gradle.
La oss starte med å definere plugin.xml
filen vår . Du finner filen i src/main/resources/META-INF/plugin.xml
. Vi vil at pluginet vårt skal være tilgjengelig på alle IntelliJ IDE-er, så vi setter vårt dependencies
til com.intellij.modules.lang
. Akkurat nå ser filen vår slik ut:
dk.lost_world.Dictionary Dictionary GitHub com.intellij.modules.lang
Men akkurat nå har dette ingen logikk, og vi registrerer ikke noe på IntelliJ-plattformen.
Siden dette prosjektet vil finne project.dic
filer i et prosjekt og registrere dem som ordbøker i det prosjektet, må vi registrere en komponent på prosjektnivå. Denne komponenten kalles når et prosjekt åpnes og lukkes. La oss lage en klasse og implementere ProjectComponent
grensesnittet. Når vi holder markøren over klassenavnet, forteller den oss at komponenten ikke er registrert.

Vi kan da ringe handlingen som blir kalt, Register Project Component
og den vil registrere den for oss i plugin.xml
filen.

Hvis vi åpner, plugin.xml
skal følgende kode legges til. Hvis det ikke ble lagt til når du ringer til handlingen, er det bare å legge til det manuelt.
dk.lost_world.dictionary.DictionaryProjectComponent
IntelliJ filsystem
Når vi jobber med filer i IntelliJ, bruker vi en V irtual F ile S ystem (VFS). VFS gir oss en universell API for å snakke med filer, uten at vi trenger å tenke på om de er fra FTP, en HTTP-server eller bare på den lokale disken.
Ettersom pluginet vårt ser etter filer som kalles, må project.dic
det selvfølgelig snakke med V irtual F ile S ystem. Alle filene i VFS er virtuelle filer. Dette kan høres litt skremmende ut, men i virkeligheten er det bare et API for et filsystem og for en fil. Måten å tenke på det er bare at V irtual F ile S ystem er filsystemgrensesnittet ditt og Virtual Files er filene dine.
Stavekontrollinnstillinger
Ettersom IntelliJ allerede har støtte for .dic
filer og stavekontroll generelt, er det eneste vi trenger å gjøre å registrere project.dic
filene våre i stavekontrollinnstillingene.
Alle innstillingene for stavekontrollen lagres i en klasse som heter com.intellij.spellchecker.settings.SpellCheckerSettings
. For å få en forekomst av det, kan du bare ringe getInstance
metoden (de fleste IntelliJ-klassene fikk en getInstance
metode som bruker IntelliJs ServiceManager
under).
Innstillingsklassen fikk en metode som heter getCustomDictionariesPaths
som returnerer alle banene til ordbøker som er installert av brukeren.

Når vi ser på metodesignaturen, ser vi også en kommentar kalt AvailableSince
. Vi vil senere bruke verdien i denne kommentaren til å spesifisere den minste nødvendige versjonen for at pluginet skal fungere.
Når metoden returnerer en liste, kan vi ganske enkelt ringe add
til metoden for å legge til i en ny bane til en ordbok.
Kjører pluginet vårt (build.gradle)
Som vi nå vet hvordan vi legger til en ordbok i stavekontrollen, la oss legge til et lite kodeeksempel i DictionaryProjectComponent
klassen for å gjøre dette.
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"); }}
Denne koden vil registrere en project.dic
fil fra roten til prosjektet vårt når prosjektet åpnes.
For å teste ut vårt lille eksempel, må vi oppdatere build.gradle
filen vår . I intellij
delen av gradefilen legger vi til i hvilken versjon av IntelliJ vi vil bruke. Dette versjonsnummeret er det fra AvailableSince
merknaden på SpellCheckerSettings
klassen.
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}
Å kjøre runIde
kommandoen fra gradle starter en forekomst av IntelliJ av den spesifikke versjonen. Etter at IDE-testen ble startet, burde pluginet ha blitt kjørt. Hvis vi åpner opp preferences > Editor > Spelling > Dic
fagbøker, kan vi se under egendefinerte ordbøker at banen vi spesifiserte i eksemplet vårt nå er lagt til.

Vi kan nå teste pluginet vårt, så nå er det på tide å bygge det ut riktig slik at det finner project.dic
filene og registrerer dem for oss.
I DictionaryProjectComponent::projectOpened
metoden må vi først finne alle kalt filer project.dic
og registrere dem, og også legge til en fillytter, så når nye project.dic
filer legges til, blir de registrert automatisk.
Ordboksklasse
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 ne
wFile)
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!

Konklusjon
Jeg håper denne artikkelen har gitt deg mer mot til å begynne å utvikle dine egne plugins. Et av de største problemene jeg hadde mens jeg utviklet det, var å finne ut hvilke klasser jeg skulle bruke. IntelliJ har en omfattende guide som jeg vil anbefale deg å lese fra start til slutt, men mange klasser er ikke nevnt der inne. I tilfeller der du setter deg fast, har de en Gitter-chat som er veldig nyttig, og det er også folk fra IntelliJ der for å hjelpe.
Kildekoden for dette prosjektet finner du på Github, og pluginet vi opprettet er på JetBrains-markedet.