Πώς να δημιουργήσετε μια προσθήκη IntelliJ - ας δημιουργήσουμε ένα απλό εργαλείο εύρεσης λεξικού

Οι περισσότεροι από εμάς προγραμματιστές χρησιμοποιούν πλατφόρμες IntelliJ, είτε IDEA, PHPStorm, WebStorm, Android Studio, PyCharm και η λίστα συνεχίζεται και συνεχίζεται. Ωστόσο, μερικές φορές όταν τη χρησιμοποιούμε, διαπιστώνουμε ότι λείπει μια δυνατότητα, αλλά δεν έχουμε ιδέα πώς να προσθέσουμε πραγματικά αυτήν τη δυνατότητα και τελικά να ζήσουμε χωρίς αυτήν.

Σε αυτό το άρθρο θα καλύψω πώς μπορούμε να δημιουργήσουμε ένα απλό plugin για όλα τα IDE IntelliJ, οπότε όταν προσθέτετε ένα project.dicαρχείο, θα το προσθέσει αυτόματα ως ένα από τα λεξικά σας. Θα αναζητήσει επίσης το αρχείο σε πακέτα, έτσι ώστε τα πακέτα να μπορούν να προσθέσουν προσαρμοσμένες λέξεις στο λεξικό. Ένα .dicαρχείο είναι ένα απλό λεξικό όπου κάθε γραμμή είναι μια λέξη στο λεξικό.

Το έργο είναι απλώς ένα δείγμα για να ξεκινήσετε να αναπτύσσετε τα δικά σας πρόσθετα. Αλλά στην πραγματικότητα είναι επίσης ένα χαρακτηριστικό που μου λείπει, καθώς όταν αναπτύσσω ένα προσαρμοσμένο πακέτο με τα δικά μου λόγια, μισώ ότι πρέπει να τα προσθέτω κάθε φορά στο λεξικό επιπέδου έργου.

Δημιουργία του έργου

Κατά τη δημιουργία προσθηκών για το IntelliJ, πρέπει να το κάνουμε είτε σε Java είτε σε Kotlin. Θα το κάνω στην Java, καθώς οι περισσότεροι χρήστες είναι εξοικειωμένοι με αυτό. Καθώς πρόκειται για έργο Java, θα χρησιμοποιήσουμε το IntelliJ IDEA ως IDE μας.

Σύμφωνα με τον οδηγό ανάπτυξης, ο προτεινόμενος τρόπος δημιουργίας ενός έργου είναι με τη χρήση του Gradle. Ξεκινάμε ανοίγοντας preferencesκαι ελέγχουμε αν Gradleκαι Plugin DevKitέχουν εγκατασταθεί πρόσθετα.

Μετά την εγκατάσταση των προσθηκών και την επανεκκίνηση του IDE, πηγαίνουμε στη νέα ροή έργων και κάτω Gradle. Εδώ υπάρχει τώρα μια επιλογή που ονομάζεται IntelliJ Platform Pluginπου είναι αυτή που χρειαζόμαστε.

Στη συνέχεια, περάστε την υπόλοιπη ροή δημιουργίας του έργου ως κανονική - σε αυτό το έργο επιλέγω την ακόλουθη διαμόρφωση.

Εγκαθιστώ plugin.xml

Τώρα που έχουμε ένα έργο, πρέπει να ρυθμίσουμε το plugin.xmlαρχείο μας και build.gradle. Το plugin.xmlαρχείο είναι ένα αρχείο που χρησιμοποιείται από την IntelliJ και καθορίζει όλες τις πληροφορίες σχετικά με την προσθήκη. Αυτό περιλαμβάνει το όνομα, τις εξαρτήσεις, τις ενέργειες που πρέπει να προσθέσει ή εάν πρέπει να επεκτείνει κάτι στο IntelliJ. Βασικά αυτό το αρχείο καθορίζει ό, τι πρέπει να κάνει η προσθήκη σας και είναι η ρίζα του έργου σας. Στο build.gradleαρχείο μας μπορούμε να ορίσουμε μερικές από τις τιμές plugin.xmlκαι πληροφορίες όπως από ποια έκδοση του IntelliJ θέλουμε να δοκιμάσουμε την προσθήκη μας κατά την κατασκευή με gradle.

Ας ξεκινήσουμε καθορίζοντας το plugin.xmlαρχείο μας . Μπορείτε να βρείτε το αρχείο στο src/main/resources/META-INF/plugin.xml. Θέλουμε plugin μας να είναι διαθέσιμες σε όλα τα IntelliJ IDE, έτσι θέτουμε μας dependenciesγια να com.intellij.modules.lang. Αυτή τη στιγμή το αρχείο μας μοιάζει με αυτό:

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

Ωστόσο, αυτή τη στιγμή αυτό δεν έχει λογική και δεν καταχωρούμε τίποτα στην πλατφόρμα IntelliJ.

Καθώς αυτό το έργο θα βρει project.dicαρχεία μέσα σε ένα έργο και θα τα καταχωρίσει ως λεξικά σε αυτό το έργο, θα πρέπει να καταχωρήσουμε ένα στοιχείο επιπέδου έργου. Αυτό το στοιχείο θα καλείται όταν ένα έργο ανοίξει και κλείσει. Ας δημιουργήσουμε μια τάξη και να εφαρμόσουμε τη ProjectComponentδιεπαφή. Όταν τοποθετούμε το δείκτη του ποντικιού πάνω από το όνομα της τάξης, μας λέει ότι το στοιχείο δεν είναι καταχωρημένο.

Στη συνέχεια μπορούμε να καλέσουμε την ενέργεια που ονομάζεται Register Project Componentκαι θα την καταχωρήσει στο plugin.xmlαρχείο.

Εάν ανοίξουμε plugin.xmlτον ακόλουθο κώδικα θα πρέπει να προστεθεί. Εάν δεν προστέθηκε κατά την κλήση της ενέργειας, απλώς προσθέστε την χειροκίνητα

  dk.lost_world.dictionary.DictionaryProjectComponent 

Σύστημα αρχείων IntelliJ

Όταν συνεργαζόμαστε με αρχεία στο IntelliJ, χρησιμοποιούμε ένα σύστημα V Itual F ile S (VFS). Το VFS μας παρέχει ένα καθολικό API για να μιλάμε με αρχεία, χωρίς να χρειάζεται να σκεφτόμαστε εάν προέρχονται από FTP, έναν διακομιστή HTTP ή μόνο στον τοπικό δίσκο.

Όπως plugin εμφάνιση μας για τα αρχεία που ονομάζεται project.dicαυτό φυσικά θα πρέπει να μιλήσετε με τον V irtual F ile S ΥΣΤΗΜΑΤΟΣ. Όλα τα αρχεία στο VFS είναι εικονικά αρχεία. Αυτό μπορεί να ακούγεται λίγο εκφοβιστικό, αλλά στην πραγματικότητα είναι απλώς ένα API για ένα σύστημα αρχείων και για ένα αρχείο. Ο τρόπος για να το σκεφτώ είναι ακριβώς ότι η V irtual F ile S συστήματος που αντιβαίνει είναι διεπαφή του συστήματος αρχείων σας και τα εικονικά αρχεία είναι αρχεία σας.

Ρυθμίσεις ορθογραφικού ελέγχου

Καθώς το IntelliJ έχει ήδη υποστήριξη για .dicαρχεία και ορθογραφικό έλεγχο γενικά, το μόνο που πρέπει να κάνουμε είναι να καταχωρίσουμε τα project.dicαρχεία μας στις ρυθμίσεις ορθογραφικού ελέγχου.

Όλες οι ρυθμίσεις για τον ορθογραφικό έλεγχο αποθηκεύονται σε μια τάξη που ονομάζεται com.intellij.spellchecker.settings.SpellCheckerSettings. Για να λάβετε μια παρουσία, απλώς καλέστε τη getInstanceμέθοδο (τα περισσότερα από τα μαθήματα IntelliJ πήραν μια getInstanceμέθοδο που χρησιμοποιεί το IntelliJ ServiceManagerαπό κάτω).

Η κλάση ρυθμίσεων πήρε μια μέθοδο που ονομάζεται getCustomDictionariesPathsπου επιστρέφει όλες τις διαδρομές στα λεξικά που εγκαθίστανται από τον χρήστη.

Όταν εξετάζουμε την υπογραφή μεθόδου, βλέπουμε επίσης έναν σχολιασμό που ονομάζεται AvailableSince. Θα χρησιμοποιήσουμε αργότερα την τιμή σε αυτόν τον σχολιασμό για να καθορίσουμε την ελάχιστη απαιτούμενη έκδοση για να λειτουργήσει η προσθήκη μας.

Καθώς η μέθοδος επιστρέφει μια λίστα, μπορούμε απλώς να καλέσουμε addτη μέθοδο για να προσθέσουμε μια νέα διαδρομή σε ένα λεξικό.

Εκτέλεση της προσθήκης μας (build.gradle)

Όπως τώρα γνωρίζουμε πώς να προσθέσουμε ένα λεξικό στον ορθογραφικό έλεγχο, ας προσθέσουμε ένα μικρό παράδειγμα κώδικα στην DictionaryProjectComponentτάξη μας για να το κάνουμε αυτό.

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"); }}

Αυτός ο κωδικός θα καταγράφει ένα project.dicαρχείο από τη ρίζα του έργου μας κάθε φορά που το έργο ανοίγει.

Για να δοκιμάσουμε το μικρό μας παράδειγμα, πρέπει να ενημερώσουμε το build.gradleαρχείο μας . Στην intellijενότητα του αρχείου gradle προσθέτουμε σε ποια έκδοση του IntelliJ θέλουμε να χρησιμοποιήσουμε. Αυτός ο αριθμός έκδοσης είναι αυτός από τον AvailableSinceσχολιασμό στην SpellCheckerSettingsτάξη.

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!

συμπέρασμα

Ελπίζω αυτό το άρθρο να σας δώσει περισσότερο θάρρος για να ξεκινήσετε να αναπτύσσετε τα δικά σας πρόσθετα. Ένα από τα μεγαλύτερα προβλήματα που είχα κατά την ανάπτυξη ήταν να μάθω ποιες τάξεις να χρησιμοποιήσω. Το IntelliJ διαθέτει έναν εκτεταμένο οδηγό τον οποίο θα συνιστούσα να διαβάσετε από την αρχή έως το τέλος, ωστόσο πολλές τάξεις δεν αναφέρονται εκεί. Σε περίπτωση που έχετε κολλήσει, έχουν μια συνομιλία Gitter που είναι πραγματικά χρήσιμη και υπάρχουν άνθρωποι από το IntelliJ εκεί για να βοηθήσουν επίσης.

Ο πηγαίος κώδικας για αυτό το έργο μπορεί να βρεθεί στο Github και η προσθήκη που δημιουργήσαμε βρίσκεται στην αγορά JetBrains.