<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ intellij - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ intellij - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 16 Jun 2026 17:39:41 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/intellij/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to create an IntelliJ plugin — let’s build a simple dictionary finder ]]>
                </title>
                <description>
                    <![CDATA[ By Oliver Nybroe Most of us developers use IntelliJ platforms, either IDEA, PHPStorm, WebStorm, Android Studio, PyCharm and the list goes on and on. However sometimes when we use it, we find that a feature is missing, but we have no idea how to actua... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-an-intellij-plugin-lets-build-a-simple-dictionary-finder-6c5192b449c/</link>
                <guid isPermaLink="false">66c3512dd73001a6c0054bef</guid>
                
                    <category>
                        <![CDATA[ intellij ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 05 Apr 2019 16:18:16 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*vZu_atWvhwDrpK4a2mhakg.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Oliver Nybroe</p>
<p>Most of us developers use IntelliJ platforms, either IDEA, PHPStorm, WebStorm, Android Studio, PyCharm and the list goes on and on. However sometimes when we use it, we find that a feature is missing, but we have no idea how to actually add that feature and eventually just live without it.</p>
<p>In this article I will cover how we can create a simple plugin for all of the IntelliJ IDEs so when you add a <code>project.dic</code> file, it will automatically add it as one of your dictionaries. It will also search for the file in packages, so packages can add custom words to the dictionary. A <code>.dic</code> file is a simple dictionary where each line is a word in the dictionary.</p>
<p>The project is just a sample to get you started on developing your own plugins. But it’s actually also a feature I have been missing, as when I develop a custom package with my own words in it, I hate that I have to add them each time in the project level dictionary.</p>
<h3 id="heading-creating-the-project">Creating the project</h3>
<p>When creating plugins for IntelliJ, we have to option to do it in either Java or Kotlin. I will do it in Java as most users are familiar with that. As this is a Java project, we will use IntelliJ IDEA as our IDE.</p>
<p>According to the <a target="_blank" href="https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html">development guide</a>, the recommended way to create a project is by using <a target="_blank" href="https://www.jetbrains.org/intellij/sdk/docs/tutorials/build_system.html">Gradle</a>. We start by opening up <code>preferences</code> and check if <code>Gradle</code> and <code>Plugin DevKit</code> plugins are installed.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ASJDtMw774VpAgoWCZEmkw.png" alt="Image" width="800" height="552" loading="lazy"></p>
<p>After installing the plugins and restarting the IDE, we go to the new projects flow and under <code>Gradle</code>. In here there is now an option called <code>IntelliJ Platform Plugin</code> which is the one we need.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*NTFEQjxMv7BBrHSzBj1THw.png" alt="Image" width="800" height="750" loading="lazy">
<em>Project creation flow step 1</em></p>
<p>Then go through the rest of the project creation flow as normal — in this project I choose the following configuration.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*qYo4hPtRV5aCWblW5HxzDA.png" alt="Image" width="800" height="105" loading="lazy">
<em>Project creation flow step 2</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*eqsj6ej8Qiqx4VzAEer2cQ.png" alt="Image" width="800" height="182" loading="lazy">
<em>Project creation flow step 3</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ehSxccowC6-IR1tJ9h-5iQ.png" alt="Image" width="800" height="68" loading="lazy">
<em>Project creation flow step 4</em></p>
<h3 id="heading-setting-up-pluginxml">Setting up <code>plugin.xml</code></h3>
<p>Now that we have a project, we have to setup our <code>plugin.xml</code> file and <code>build.gradle</code>. The <code>plugin.xml</code> file is a file used by IntelliJ which defines all the information about the plugin. This includes the name, dependencies, what actions it should add or if it should extend something in IntelliJ. Basically this file defines everything your plugin should do and is the root of your project. In our <code>build.gradle</code> file we can define some of the values from <code>plugin.xml</code>, and information like which version of IntelliJ we want to test our plugin on when building with gradle.</p>
<p>Let’s start by defining our <code>plugin.xml</code> file. You can find the file in <code>src/main/resources/META-INF/plugin.xml</code>. We want our plugin to be available on all IntelliJ IDE’s so we set our <code>dependencies</code> to <code>com.intellij.modules.lang</code>. Right now our file looks like this:</p>
<pre><code>&lt;idea-plugin&gt;    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>dk.lost_world.Dictionary<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span></span>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>Dictionary<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span></span>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">vendor</span> <span class="hljs-attr">email</span>=<span class="hljs-string">"olivernybroe@gmail.com"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"https://github.com/olivernybroe/intellij-Dictionary"</span>&gt;</span>GitHub<span class="hljs-tag">&lt;/<span class="hljs-name">vendor</span>&gt;</span></span>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">depends</span>&gt;</span>com.intellij.modules.lang<span class="hljs-tag">&lt;/<span class="hljs-name">depends</span>&gt;</span></span>&lt;/idea-plugin&gt;
</code></pre><p>However right now this does not have any logic, and we do not register anything to the IntelliJ platform.</p>
<p>As this project will find <code>project.dic</code> files inside a project and register them as dictionaries in that project, we will have to register a Project level component. This component will be called when a project is opened and closed. Let’s create a class and implement the <code>ProjectComponent</code> interface. When we hover over the class name it tells us that the component is not registered.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*imcg1FilSnSxkgJG6J9-0g.png" alt="Image" width="800" height="511" loading="lazy">
<em>Hints on class</em></p>
<p>We can then call the action called <code>Register Project Component</code> and it will register it for us in the <code>plugin.xml</code> file.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*b4OvFSeoPWJ6UDAtgKF_Vg.png" alt="Image" width="559" height="169" loading="lazy">
<em>Actions on class</em></p>
<p>If we open <code>plugin.xml</code> the following code should be added. If it wasn’t added when calling the action, then just add it manually.</p>
<pre><code>&lt;project-components&gt;    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">component</span>&gt;</span>        <span class="hljs-tag">&lt;<span class="hljs-name">implementation-class</span>&gt;</span>dk.lost_world.dictionary.DictionaryProjectComponent<span class="hljs-tag">&lt;/<span class="hljs-name">implementation-class</span>&gt;</span>    <span class="hljs-tag">&lt;/<span class="hljs-name">component</span>&gt;</span></span>&lt;/project-components&gt;
</code></pre><h4 id="heading-intellij-filesystem">IntelliJ Filesystem</h4>
<p>When working with files in IntelliJ, we use a <a target="_blank" href="https://www.jetbrains.org/intellij/sdk/docs/basics/virtual_file_system.html"><strong>V</strong>irtual <strong>F</strong>ile <strong>S</strong>ystem (VFS)</a>. The VFS gives us a universal API to talk with files, without us having to think about if they are from FTP, an HTTP server or just on the local disk.</p>
<p>As our plugin looks for files called <code>project.dic</code> it will of course need to talk with the <strong>V</strong>irtual <strong>F</strong>ile <strong>S</strong>ystem. All files in the VFS are <a target="_blank" href="https://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/virtual_file.html">Virtual Files</a>. This can sound a little intimidating, but in reality it is just an API for a filesystem and for a file. The way to think about it is just that the <strong>V</strong>irtual <strong>F</strong>ile <strong>S</strong>ystem is your file system interface and the Virtual Files are your files.</p>
<h4 id="heading-spell-checker-settings">Spell Checker Settings</h4>
<p>As IntelliJ already has support for <code>.dic</code> files and spell checking in general, the only thing we need to do is register our <code>project.dic</code> files in the spell checkers settings.</p>
<p>All the settings for the spell checker are saved in a class called <code>com.intellij.spellchecker.settings.SpellCheckerSettings</code>. To get an instance of it, simply call the <code>getInstance</code> method (most of the IntelliJ classes got a <code>getInstance</code> method which uses IntelliJ’s <code>ServiceManager</code> underneath).<br>The settings class got a method called <code>getCustomDictionariesPaths</code> which returns all of the paths to dictionaries which are installed by the user.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*pee95tAmC6aeTfpkOuBeew.png" alt="Image" width="800" height="79" loading="lazy">
<em>API of getCustomDictionariesPaths</em></p>
<p>When looking at the method signature, we also see an annotation called <code>AvailableSince</code>. We will later use the value in this annotation to specify the minimum required version for our plugin to work.</p>
<p>As the method returns a list, we can simply call <code>add</code> on the method to add in a new path to a dictionary.</p>
<h4 id="heading-running-our-plugin-buildgradle">Running Our Plugin (build.gradle)</h4>
<p>As we now know how to add a dictionary to the spell checker, let’s add a small code example in our <code>DictionaryProjectComponent</code> class for doing this.</p>
<pre><code>public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DictionaryProjectComponent</span> <span class="hljs-title">implements</span> <span class="hljs-title">ProjectComponent</span> </span>{    private Project project;    public DictionaryProjectComponent(Project project) {        <span class="hljs-built_in">this</span>.project = project;    }    @Override    public <span class="hljs-keyword">void</span> projectOpened() {        SpellCheckerSettings            .getInstance(project)            .getCustomDictionariesPaths()            .add(<span class="hljs-string">"./project.dic"</span>);    }}
</code></pre><p>This code will register a <code>project.dic</code> file from the root of our project whenever the project is opened.</p>
<p>To test out our little example, we need to update our <code>build.gradle</code> file. In the <code>intellij</code> section of the gradle file we add in what version of IntelliJ we want to use. This version number is the one from the <code>AvailableSince</code> annotation on the <code>SpellCheckerSettings</code> class.</p>
<pre><code>plugins {    id <span class="hljs-string">'java'</span>    id <span class="hljs-string">'org.jetbrains.intellij'</span> version <span class="hljs-string">'0.4.4'</span>}group <span class="hljs-string">'dk.lost_world'</span>version <span class="hljs-string">'1.0-SNAPSHOT'</span>sourceCompatibility = <span class="hljs-number">1.8</span>repositories {    mavenCentral()}dependencies {    testCompile group: <span class="hljs-string">'junit'</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'junit'</span>, <span class="hljs-attr">version</span>: <span class="hljs-string">'4.12'</span>}<span class="hljs-comment">// See https://github.com/JetBrains/gradle-intellij-plugin/intellij {    pluginName 'Dictionary'    version '181.2784.17'    type 'IC'    downloadSources true}</span>
</code></pre><p>Running the <code>runIde</code> 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 <code>preferences &gt; Editor &gt; Spelling &gt; Dic</code>tionaries we can see under custom dictionaries that the path we specified in our example is now added.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*69zQmR1XsQ4txhWfgT3niw.png" alt="Image" width="800" height="741" loading="lazy">
<em>Showing dictionaries preferences from IntelliJ IDE</em></p>
<p>We are now able to test our plugin, so now it is time to build it out correctly so it finds the <code>project.dic</code> files and registers them for us.</p>
<p>In the <code>DictionaryProjectComponent::projectOpened</code> method, we need to first find all files called <code>project.dic</code> and register them and also add a file listener so when new <code>project.dic</code> files are added, they are registered automatically.</p>
<h3 id="heading-dictionary-class">Dictionary Class</h3>
<p>We will have a class called <code>Dictionary</code>, this class will contain the logic for us to register and remove files from the dictionary. The class will have the following public methods:<br><code>void registerAndNotify(Collection&lt;VirtualFile&gt; files)</code><br><code>void registerAndNotify(VirtualFile file)</code><br><code>void removeAndNotify(VirtualFile file)</code><br><code>void moveAndNotify(VirtualFile oldFile, VirtualFile ne</code>wFile)</p>
<p>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:</p>
<h4 id="heading-finding-all-dictionary-files">Finding all dictionary files</h4>
<p>For finding all the dictionary files in the project called <code>project.dic</code> we use the class <code>[FilenameIndex](http://www.jetbrains.org/intellij/sdk/docs/basics/psi_cookbook.html#how-do-i-find-a-file-if-i-know-its-name-but-dont-know-the-path)</code>. The file is in the namespace <code>com.intellij.psi.search.FilenameIndex</code>, it has a method <code>getVirtualFilesByName</code> which we can use to find our <code>project.dic</code> files.</p>
<pre><code>FilenameIndex.getVirtualFilesByName(    project,    <span class="hljs-string">"project.dic"</span>,    <span class="hljs-literal">false</span>,    GlobalSearchScope.allScope(project))
</code></pre><p>This call will return all Virtual Files which matches the search criteria. We then put the return result into the Dictionary class method <code>registerAndNotify</code>.</p>
<pre><code>@Overridepublic <span class="hljs-keyword">void</span> projectOpened() {    Dictionary dictionary = <span class="hljs-keyword">new</span> Dictionary(project);    dictionary.registerAndNotify(        FilenameIndex.getVirtualFilesByName(            project,            <span class="hljs-string">"project.dic"</span>,            <span class="hljs-literal">false</span>,            GlobalSearchScope.allScope(project)        )    );}
</code></pre><p>Our code is now able to find <code>project.dic</code> files at start up and register them, if they are not already registered. It will also notify about the newly registered files.</p>
<h4 id="heading-adding-a-virtual-file-listener">Adding a Virtual File Listener</h4>
<p>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 <code>com.intellij.openapi.vfs.VirtualFileListener</code>.</p>
<p>In the docblock for the listener class we can see that to register it we can use <code>VirtualFilemanager#addVirtualFileListener</code>.<br>Let’s create a class named <code>DictionaryFileListener</code> and implement the methods which we need for our project.</p>
<p>Then we update our <code>projectOpened</code> class to also add the <code>VirtualFileListener</code>.</p>
<pre><code>@Overridepublic <span class="hljs-keyword">void</span> projectOpened() {    Dictionary dictionary = <span class="hljs-keyword">new</span> Dictionary(project);    dictionary.registerAndNotify(        FilenameIndex.getVirtualFilesByName(            project,            <span class="hljs-string">"project.dic"</span>,            <span class="hljs-literal">false</span>,            GlobalSearchScope.allScope(project)        )    );    VirtualFileManager.getInstance().addVirtualFileListener(        <span class="hljs-keyword">new</span> DictionaryFileListener(dictionary)    );}
</code></pre><p>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.</p>
<h4 id="heading-adding-plugin-information">Adding plugin information</h4>
<p>To add information about the plugin, we open the <code>build.gradle</code> file and edit the object <code>patchPluginXml</code>. In here we need to specify which build version is required for the plugin, version of the plugin, description and change notes.</p>
<pre><code>patchPluginXml {    sinceBuild intellij.version    untilBuild <span class="hljs-literal">null</span>    version project.version    pluginDescription <span class="hljs-string">""</span><span class="hljs-string">"Plugin for having a shared dictionary for all members of your project. &lt;br&gt;&lt;br&gt;It will automatically find any &lt;code&gt;project.dic&lt;/code&gt; files and add themto the list of dictionaries. &lt;br&gt;&lt;br&gt;It will also search packages for dictionary files and add them to our list of dictionaries.    "</span><span class="hljs-string">""</span>    changeNotes <span class="hljs-string">""</span><span class="hljs-string">"&lt;p&gt;0.2&lt;/p&gt;&lt;ul&gt;    &lt;li&gt;Added support for listening for when a &lt;code&gt;project.dic&lt;/code&gt; file is added, moved, deleted, copied.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;0.1&lt;/p&gt;&lt;ul&gt;    &lt;li&gt;First edition of the plugin.&lt;/li&gt;&lt;/ul&gt;    "</span><span class="hljs-string">""</span>}
</code></pre><p>We also update the <code>version</code> property to <code>'0.2'</code>of the gradle project itself. The plugin can now run on all versions since the method for registering custom dictionaries was added.</p>
<p>To test if it generates the desired output, we can run the gradle task <code>patchPluginXml</code> and under <code>build/patchedPluginXmlFiles</code> our generated <code>plugin.xml</code> file will be there.</p>
<p>Since IntelliJ version <code>2019.1</code>, <a target="_blank" href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_icon_file.html">all plugins supports icons</a>. 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 <code>pluginIcon.svg</code> as the default icon and <code>pluginIcon_dark.svg</code> for the darcula theme.</p>
<p>The plugin icons should be listed together with the <code>plugin.xml</code> file in the path <code>resources/META-INF</code>.</p>
<h4 id="heading-building-for-distribution">Building for distribution</h4>
<p>The plugin is now ready to be built and shipped. To do this we run the gradle task <code>buildPlugin</code>. Under <code>build/distributions</code> a zip file will appear which you can distribute and install manually in your IDE. Add this zip file as a <a target="_blank" href="https://github.com/olivernybroe/intellij-Dictionary/releases">release under your github repo</a>, so users have the option to download it manually from you repo.</p>
<h4 id="heading-publishing-a-plugin">Publishing a plugin</h4>
<p>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 <a target="_blank" href="https://plugins.jetbrains.com/">Plugin Repository website</a>. When in here, a dropdown from your profile name shows an option to upload a plugin.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*5BoQz8Wh4KMKZnXq6oPwlA.png" alt="Image" width="800" height="274" loading="lazy"></p>
<p>Input all the information in the dialog (you have to add a license, but that is pretty <a target="_blank" href="https://help.github.com/en/articles/licensing-a-repository">straightforward with Github</a>). Here we add the distribution zip file.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*C3i5sxGFzG70I98oWHElfA.png" alt="Image" width="800" height="810" loading="lazy"></p>
<p>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.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*vvizHF3GWpNrG6XZ4u2qWA.png" alt="Image" width="800" height="560" loading="lazy"></p>
<h4 id="heading-updating-your-plugin-via-gradle">Updating your plugin via Gradle</h4>
<p>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 <a target="_blank" href="https://hub.jetbrains.com/users/me?tab=authentification">authentification tab</a>. From here press <code>New token...</code> and add the scope <code>Plugin Repository</code>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Y-QOaZbX_IFv9DbbCAME8A.png" alt="Image" width="471" height="268" loading="lazy"></p>
<p>When pressing create you get a token. Create a file called <code>gradle.properties</code> and add the token under the key <code>intellijPublishToken</code> (remember to git ignore this file).</p>
<p>In our <code>build.gradle</code> file, we simply add the following:</p>
<pre><code>publishPlugin {    token intellijPublishToken}
</code></pre><p>And we can now run the gradle task <code>publishPlugin</code> 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.</p>
<p>After waiting some days our plugin has now been approved and can now be found in the plugin marketplace by searching for dictionary!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*vKzCf9d4QgpNVZ11j8luWA.png" alt="Image" width="800" height="507" loading="lazy"></p>
<h4 id="heading-conclusion">Conclusion</h4>
<p>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 <a target="_blank" href="https://www.jetbrains.org/intellij/sdk/docs/welcome.html">extensive guide</a> 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 <a target="_blank" href="https://gitter.im/IntelliJ-Plugin-Developers/Lobby">Gitter chat</a> which is really helpful and there are people from IntelliJ on there to help also.</p>
<p>The source code for this project can be found on <a target="_blank" href="https://github.com/olivernybroe/intellij-Dictionary">Github</a> and the plugin we created is in the <a target="_blank" href="https://plugins.jetbrains.com/plugin/12089-dictionary">JetBrains marketplace</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
