כיצד ליצור תוסף IntelliJ - בואו לבנות מאתר מילונים פשוט

רובנו המפתחים משתמשים בפלטפורמות IntelliJ, IDEA, PHPStorm, WebStorm, Android Studio, PyCharm והרשימה עוד ארוכה. עם זאת, לפעמים כאשר אנו משתמשים בה, אנו מגלים שחסר תכונה, אך אין לנו מושג כיצד להוסיף את התכונה הזו ובסופו של דבר פשוט לחיות בלעדיה.

במאמר זה אעסוק כיצד נוכל ליצור תוסף פשוט לכל ה- IDE של IntelliJ ולכן כשאתה מוסיף project.dicקובץ, הוא יוסיף אותו אוטומטית כאחד המילונים שלך. הוא גם יחפש את הקובץ בחבילות, כך שחבילות יכולות להוסיף מילים מותאמות אישית למילון. .dicהקובץ הוא מילון פשוט שבו כל שורה היא מילה במילון.

הפרויקט הוא רק דוגמה שתוכל להתחיל לפתח תוספים משלך. אבל זו בעצם גם תכונה שחסרה לי, כמו שכשאני מפתח חבילת מותאמת אישית עם מילים משלי, אני שונא שאני צריך להוסיף אותם בכל פעם במילון ברמת הפרויקט.

יצירת הפרויקט

בעת יצירת תוספים עבור IntelliJ, עלינו אפשרות לעשות זאת ב- Java או ב- Kotlin. אני אעשה את זה בג'אווה כיוון שרוב המשתמשים מכירים את זה. מכיוון שמדובר בפרויקט 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 אנו רוצים לבדוק את התוסף שלנו בעת בנייה עם דרגה.

נתחיל בהגדרת plugin.xmlהקובץ שלנו . אתה יכול למצוא את הקובץ ב src/main/resources/META-INF/plugin.xml. אנחנו רוצים התוסף שלנו להיות נגיש לכל 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 irtual F ile S ystem (VFS). ה- VFS נותן לנו ממשק API אוניברסלי לשיחה עם קבצים, מבלי שנצטרך לחשוב אם הם מ- FTP, משרת HTTP או סתם על הדיסק המקומי.

כמו המראה התוסף שלנו עבור קבצים בשם project.dicזה יהיה כמובן צורך לדבר עם V irtual F ile S ystem. כל הקבצים ב- VFS הם קבצים וירטואליים. זה יכול להישמע קצת מאיים, אבל במציאות זה רק API למערכת קבצים ולקובץ. הדרך לחשוב על זה היא רק ש- V irtual F ile S ystem הוא ממשק מערכת הקבצים שלך והקבצים הווירטואליים הם הקבצים שלך.

הגדרות בודק איות

מכיוון של- 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}

הפעלת runIdeהפקודה מ- Gradle תפעיל מופע של IntelliJ של הגרסה הספציפית. לאחר הפעלת ה- IDE הבדיקה צריך היה להפעיל את התוסף שלנו. אם אנו פותחים preferences > Editor > Spelling > Dicמילונים אנו יכולים לראות תחת מילונים מותאמים אישית שהנתיב שציינו בדוגמה שלנו מתווסף כעת.

כעת אנו יכולים לבדוק את התוסף שלנו, אז עכשיו הגיע הזמן לבנות אותו בצורה נכונה כך שהוא ימצא את project.dicהקבצים וירשם אותם עבורנו.

בשנתי ה DictionaryProjectComponent::projectOpenedהשיטה, ראשית עלינו למצוא את כל הקבצים בשם project.dicולרשום אותם וגם להוסיף מאזין קובץ ולכן כאשר החדש project.dicקבצים מתווספים, הם רשום אוטומטית.

כיתת מילון

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.