Како се креира додатак ИнтеллиЈ - направимо једноставан претраживач речника

Већина нас програмера користи ИнтеллиЈ платформе, било ИДЕА, ПХПСторм, ВебСторм, Андроид Студио, ПиЦхарм и листа се наставља и наставља. Међутим, понекад када је користимо, откријемо да нека функција недостаје, али немамо појма како је заправо додати и на крају само живети без ње.

У овом чланку ћу описати како можемо створити једноставан додатак за све ИнтеллиЈ ИДЕ-ове, па ће га, када додате project.dicдатотеку, аутоматски додати као један од ваших речника. Такође ће тражити датотеку у пакетима, тако да пакети могу да додају прилагођене речи у речник. .dicФајл је једноставна рјечник где свака линија је реч у речнику.

Пројекат је само пример за започињање развоја властитих додатака. Али то је у ствари и карактеристика која ми је недостајала, јер када развијам прилагођени пакет са сопственим речима, мрзим што их морам додавати сваки пут у речник на нивоу пројекта.

Израда пројекта

Када креирамо додатке за ИнтеллиЈ, морамо да то урадимо на Јави или Котлину. Урадићу то на Јави пошто је већина корисника то упозната. Како је ово Јава пројекат, користићемо ИнтеллиЈ ИДЕА као свој ИДЕ.

Према развојном водичу, препоручени начин израде пројекта је коришћење Градле-а. Почињемо отварањем preferencesи проверавамо да ли су Gradleи Plugin DevKitдодаци инсталирани.

Након инсталирања додатака и поновног покретања ИДЕ-а, прелазимо на ток нових пројеката и испод Gradle. Овде се сада зове опција IntelliJ Platform Pluginкоја нам је потребна.

Затим прођите кроз остатак процеса стварања пројекта као и обично - у овом пројекту бирам следећу конфигурацију.

Постављање plugin.xml

Сада када имамо пројекат, морамо да подесимо нашу plugin.xmlдатотеку и build.gradle. plugin.xmlФајл је фајл користи ИнтеллиЈ који дефинише све информације о додатку. То укључује име, зависности, радње које треба да дода или ако треба да прошири нешто у ИнтеллиЈ-у. У основи ова датотека дефинише све што ваш додатак треба да ради и представља корен вашег пројекта. У нашој build.gradleдатотеци можемо дефинисати неке вредности plugin.xmlи информације попут тога коју верзију ИнтеллиЈ-а желимо да тестирамо на додатку приликом градње помоћу градле-а.

Почнимо са дефинисањем наше plugin.xmlдатотеке. Датотеку можете пронаћи у src/main/resources/META-INF/plugin.xml. Желимо да наш додатак буде доступан на свим ИнтеллиЈ ИДЕ-има, па смо га поставили dependenciesна com.intellij.modules.lang. Тренутно наша датотека изгледа овако:

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

Међутим, тренутно ово нема никакве логике и не региструјемо ништа на ИнтеллиЈ платформи.

Како ће овај пројекат пронаћи project.dicдатотеке унутар пројекта и регистровати их као речнике у том пројекту, мораћемо да региструјемо компоненту на нивоу пројекта. Ова компонента ће се позвати када се пројекат отвори и затвори. Направимо класу и применимо ProjectComponentинтерфејс. Када задржимо показивач изнад имена класе, говори нам да компонента није регистрована.

Тада акцију можемо назвати позваном Register Project Componentи она ће нам је регистровати у plugin.xmlдатотеци.

Ако отворимо plugin.xml, треба додати следећи код. Ако није додато приликом позивања акције, само га додајте ручно.

  dk.lost_world.dictionary.DictionaryProjectComponent 

ИнтеллиЈ фајл систем

Када радите са датотекама на ИнтеллиЈ, користимо В иртуал П Иле Н ИСТЕМ (ОСС). ВФС нам даје универзални АПИ за разговор са датотекама, а да не морамо да размишљамо о томе да ли су са ФТП-а, ХТТП сервера или само на локалном диску.

Као наши плугин тражи фајлове зове project.dicто ће, наравно, морам да разговарам са В иртуал П Иле Н ИСТЕМ. Све датотеке у ВФС-у су виртуелне датотеке. Ово може звучати помало застрашујуће, али у стварности то је само АПИ за систем датотека и за датотеку. Начин на који се мисли о томе је само да је в иртуал Ф Иле С ИСТЕМ је ваш интерфејс фајл систем и Виртуал фајлови су ти фајлови.

Подешавања провере правописа

Како ИнтеллиЈ већ има подршку за .dicдатотеке и проверу правописа уопште, једино што треба да урадимо је да региструјемо своје project.dicдатотеке у подешавањима провере правописа.

Сва подешавања за проверу правописа чувају се у класи која се зове com.intellij.spellchecker.settings.SpellCheckerSettings. Да бисте добили његову инстанцу, једноставно позовите getInstanceметоду (већина класа ИнтеллиЈ има getInstanceметод који користи ИнтеллиЈ-ове ServiceManagerиспод).

Класа подешавања је добила позвану методу getCustomDictionariesPathsкоја враћа све путање до речника које је корисник инсталирао.

Када гледамо потпис методе, такође видимо анотацију која се зове AvailableSince. Касније ћемо користити вредност у овој напомени да одредимо минималну потребну верзију за рад нашег додатка.

Како метода враћа листу, можемо једноставно позвати addметоду да дода нову путању до речника.

Покретање нашег додатка (буилд.градле)

Како сада знамо како да додамо речник у проверу правописа, додајмо мали пример кода у нашу 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одељак датотеке градле додајемо у којој верзији ИнтеллиЈ-а желимо да користимо. Овај број верзије је онај из 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!

Закључак

Надам се да вам је овај чланак дао више храбрости да започнете са развојем властитих додатака. Један од највећих проблема који сам имао током развоја био је да откријем које часове да користим. ИнтеллиЈ има опширан водич који бих вам препоручио да прочитате од почетка до краја, али тамо многи часови нису поменути. У случајевима када запнете, они имају Гиттер цхат који је заиста користан, а ту су и људи из ИнтеллиЈ-а који вам помажу.

Изворни код за овај пројекат може се наћи на Гитхуб-у, а додатак који смо креирали налази се на ЈетБраинс тржишту.