PageSourceSearch

https://0xc0ffee.netlify.app/assets/08-go-to-decl.html-6yGkj0-L.js

js 0xc0ffee.netlify.app collected 2026-10-03 10:22:38 UTC 32,418 bytes, 118 lines download raw bytes

1import{_ as n,c as s,e as a,o as t}from"./app-CjUkjRdW.js";const i="/assets/ref-decl-highlight-Fy0LCYz9.png",l="/assets/ref-decl-psi-Brk-Ci6p.png",o={};function c(p,e){return t(),s("div",null,e[0]||(e[0]=[a(`<h1 id="go-to-declaration" tabindex="-1"><a class="header-anchor" href="#go-to-declaration"><span>Go To Declaration</span></a></h1><p>Now that we&#39;ve got the basics in place, let&#39;s add our first actual feature: Go To Declaration. Consider this simple Nim program:</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token keyword">let</span> <span class="highlighted-word">msg</span> <span class="token operator">=</span> <span class="token string">&quot;hello&quot;</span>   <span class="token comment"># &lt;- declaration</span></span>
2<span class="line">echo <span class="highlighted-word">msg</span>            <span class="token comment"># &lt;- reference</span></span>
3<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><p>We want to be able to navigate from the identifier <code>msg</code> in <code>echo msg</code> to the declaration of <code>msg</code> on the first line. In IntelliJ terminology, the <code>msg</code> identifier on the second line is called a <em>reference</em>, which can be <em>resolved</em> to its declaration element.</p><p>The IDE knows that a PSI element is a reference if its <code>getReference()</code> method returns a <code>PsiReference</code> object. This object is <em>not</em> the target element, but rather an object that can resolve the reference to its target by invoking its <code>resolve()</code> method. The implementation of the <code>resolve()</code> method will have to locate the target element in the PSI tree, which can get complex depending on the scoping rules of the language.</p><p>Let&#39;s take a look at the AST generated by the Nim compiler for this code snippet. We can use the <code>dumpTree</code> macro from the <code>macros</code> module to print the AST:</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># dumptree.nim</span></span>
4<span class="line"><span class="token keyword">import</span> std<span class="token operator">/</span>macros</span>
5<span class="line"></span>
6<span class="line">dumpTree<span class="token operator">:</span></span>
7<span class="line">  <span class="token keyword">let</span> msg <span class="token operator">=</span> <span class="token string">&quot;hello&quot;</span></span>
8<span class="line">  echo msg</span>
9<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Macros are evaluated at compile time, so to see the output we just need to compile the program using <code>nim c dumptree.nim</code>:</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">StmtList</span>
10<span class="line">  LetSection</span>
11<span class="line">    IdentDefs</span>
12<span class="line highlighted">      Ident &quot;msg&quot;</span>
13<span class="line">      Empty</span>
14<span class="line">      StrLit &quot;hello&quot;</span>
15<span class="line">  Command</span>
16<span class="line">    Ident &quot;echo&quot;</span>
17<span class="line highlighted">    Ident &quot;msg&quot;</span>
18<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code>Ident</code> node on line 4 is the declaration of <code>msg</code>, and the one on line 9 is the reference to that declaration. In Nim, one of the places where declarations can be found is the <code>LetSection</code> node, as seen in the output above. So, we need to implement <code>getReference</code> on the element representing the reference to return a <code>PsiReference</code> object. That object will implement <code>resolve</code> to find and return the declaration under the <code>LetSection</code> node.</p><p>However, instead of creating a dedicated class for the reference, we can use the PSI class representing the identifier itself; we just need to make it implement the <code>PsiReference</code> interface. One more thing, even though the Nim AST defines both the declaration and the reference as <code>Ident</code> nodes, it will make our lives easier if we create a separate PSI class for each, e.g. <code>IdentDecl</code> and <code>IdentRef</code>. This way, we can avoid any confusion between the two, and will make identifying <code>IdentDecl</code> in the tree easier when resolving a reference.</p><h2 id="updating-the-grammar" tabindex="-1"><a class="header-anchor" href="#updating-the-grammar"><span>Updating the Grammar</span></a></h2><p>Let&#39;
18s start by updating the grammar to support the two statements in the example above. We&#39;ll use a simplified version of the Nim AST to guide us:</p><div class="language-bnf line-numbers-mode" data-highlighter="prismjs" data-ext="bnf" data-title="bnf"><pre><code><span class="line">// src/main/kotlin/khaledh/nimjet/parser/Nim.bnf</span>
19<span class="line"><span class="token operator">{</span></span>
20<span class="line">  <span class="token operator">...</span></span>
21<span class="line"><span class="token operator">}</span></span>
22<span class="line"></span>
23<span class="line">Module     <span class="token operator">::=</span> !&lt;<span class="token rule"><span class="token punctuation">&lt;</span>eof<span class="token punctuation">&gt;</span></span>&gt; StmtList</span>
24<span class="line">StmtList   <span class="token operator">::=</span> Stmt<span class="token operator">*</span></span>
25<span class="line">Stmt       <span class="token operator">::=</span> LetSection</span>
26<span class="line">             <span class="token operator">|</span> Command</span>
27<span class="line"></span>
28<span class="line">LetSection <span class="token operator">::=</span> LET IdentDecl EQ STRING_LIT</span>
29<span class="line"></span>
30<span class="line">Command    <span class="token operator">::=</span> IdentRef IdentRef</span>
31<span class="line"></span>
32<span class="line">IdentDecl  <span class="token operator">::=</span> IDENT</span>
33<span class="line">IdentRef   <span class="token operator">::=</span> IDENT</span>
34<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>You&#39;ll notice that I simplified the <code>LetSection</code> rule to allow only a single declaration for now (we&#39;ll come back to handling multiple declarations in a single <code>let</code> section later), using <code>IdentDecl</code> instead of an undifferentiated <code>IDENT</code>. The <code>Command</code> rule is also simplified to only allow two <code>IdentRef</code> elements.</p><p>If we generate the parser and PSI classes now, we&#39;ll have the following files:</p><div class="language-tree line-numbers-mode" data-highlighter="prismjs" data-ext="tree" data-title="tree"><pre><code><span class="line">src/main/gen</span>
35<span class="line">└── khaledh/nimjet</span>
36<span class="line">    ├── parser</span>
37<span class="line">    │   ├── NimElement.java</span>
38<span class="line">    │   └── NimParser.java</span>
39<span class="line">    └── psi</span>
40<span class="line">        ├── impl</span>
41<span class="line">        │   ├── CommandImpl.java</span>
42<span class="line">        │   ├── IdentDeclImpl.java</span>
43<span class="line">        │   ├── IdentRefImpl.java</span>
44<span class="line">        │   ├── LetSectionImpl.java</span>
45<span class="line">        │   ├── Stmt.java</span>
46<span class="line">        │   └── StmtListImpl.java</span>
47<span class="line">        ├── Command.java</span>
48<span class="line">        ├── IdentDecl.java</span>
49<span class="line">        ├── IdentRef.java</span>
50<span class="line">        ├── LetSection.java</span>
51<span class="line">        ├── Stmt.java</span>
52<span class="line">        └── StmtList.java</span>
53<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="reference-mixin" tabindex="-1"><a class="header-anchor" href="#reference-mixin"><span>Reference Mixin</span></a></h2><p>Our goal is to be able to navigate from a reference to its declaration. In our case, the reference element is <code>IdentRef</code>, and the declaration element is <code>IdentDecl</code>. The reference element needs to override the <code>getReference</code> method to return a <code>PsiReference</code> object. But how do we tell Grammar-Kit to generate the <code>getReference</code> method for <code>IdentRef</code>? The answer is using mixins. The way mixins work in Grammar-Kit is that we create a mixin class for a particular element, and then we tell Grammar-Kit to mix that class into the generated PSI class for that element. So, let&#39;s go ahead and create a mixin class for <code>IdentRef</code> that implements the <code>getReference</code> method:</p><div class="language-kotlin line-numbers-mode" data-highlighter="prismjs" data-ext="kt" data-title="kt"><pre><code><span class="line"><span class="token comment">// src/main/kotlin/khaledh/nimjet/psi/impl/IdentRefMixin.kt</span></span>
54<span class="line"><span class="token operator">..</span><span class="token punctuation">.</span></span>
55<span class="line"></span>
56<span class="line"><span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token function">IdentRefMixin</span><span class="token punctuation">(</span>node<span class="token operator">:</span> ASTNode<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ASTWrapperPsiElement</span><span class="token punctuation">(</span>node<span class="token punctuation">)</span><span class="token punctuation">,</span> IdentRef <span class="token punctuation">{</span></span>
57<span class="line"></span>
58<span class="line">    <span class="token keyword">
58override</span> <span class="token keyword">fun</span> <span class="token function">getReference</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> PsiReference <span class="token punctuation">{</span></span>
59<span class="line">        <span class="token keyword">return</span> <span class="token function">IdentReference</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token function">TextRange</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>textLength<span class="token punctuation">)</span><span class="token punctuation">)</span></span>
60<span class="line">    <span class="token punctuation">}</span></span>
61<span class="line"></span>
62<span class="line"><span class="token punctuation">}</span></span>
63<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We haven&#39;t implemented <code>IdentReference</code> yet; we&#39;ll do that in a moment. But first, notice that the mixin class extends <code>ASTWrapperPsiElement</code>, and implements the <code>IdentRef</code> interface generated by Grammar-Kit. Normally, this signature would be implemented by the generated <code>IdentRefImpl</code> class, but when we tell Grammar-Kit to use a mixin class for an element, it will use the mixin class as the base class for the element instead. So, the generated <code>IdentRefImpl</code> class will extend <code>IdentRefMixin</code>, and that&#39;s why the mixin class needs to take over the responsibility of extending <code>ASTWrapperPsiElement</code>.</p><p>Let&#39;s update the BNF file to use the mixin:</p><div class="language-bnf line-numbers-mode" data-highlighter="prismjs" data-ext="bnf" data-title="bnf"><pre><code><span class="line"><span class="token operator">{</span></span>
64<span class="line">  <span class="token operator">...</span></span>
65<span class="line">  mixin<span class="token operator">(</span><span class="token string">&quot;IdentRef&quot;</span><span class="token operator">)</span>=<span class="token string">&quot;khaledh.nimjet.psi.impl.IdentRefMixin&quot;</span></span>
66<span class="line"><span class="token operator">}</span></span>
67<span class="line"><span class="token operator">...</span></span>
68<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, if we generate the parser and PSI classes, we&#39;ll see that the <code>IdentRefImpl</code> class extends <code>IdentRefMixin</code> instead of <code>ASTWrapperPsiElement</code>:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre><code><span class="line"><span class="token comment">// src/main/gen/khaledh/nimjet/psi/impl/IdentRefImpl.java</span></span>
69<span class="line"><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></span>
70<span class="line"></span>
71<span class="line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">IdentRefImpl</span> <span class="token keyword">extends</span> <span class="token class-name">IdentRefMixin</span> <span class="token keyword">implements</span> <span class="token class-name">IdentRef</span> <span class="token punctuation">{</span></span>
72<span class="line">    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span></span>
73<span class="line"><span class="token punctuation">}</span></span>
74<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let&#39;s implement the <code>IdentReference</code> class, which represents the reference to an identifier. This class will implement the <code>PsiReference</code> interface, and its <code>resolve</code> method will find and return the declaration element in the PSI tree.</p><div class="language-kotlin line-numbers-mode" data-highlighter="prismjs" data-ext="kt" data-title="kt"><pre><code><span class="line"><span class="token comment">// src/main/kotlin/khaledh/nimjet/psi/IdentReference.kt</span></span>
75<span class="line"><span class="token operator">..</span><span class="token punctuation">.</span></span>
76<span class="line"></span>
77<span class="line"><span class="token keyword">class</span> <span class="token function">IdentReference</span><span class="token punctuation">(</span>element<span class="token operator">:</span> IdentRef<span class="token punctuation">,</span> textRange<span class="token operator">:</span> TextRange<span class="token punctuation">)</span></span>
78<span class="line">    <span class="token operator">:</span> PsiReferenceBase<span class="token operator">&lt;</span>IdentRef<span class="token operator">&gt;</span><span class="token punctuation">(</span>element<span class="token punctuation">,</span> textRange<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
79<span class="line"></span>
80<span class="line">    <span class="token keyword">
80override</span> <span class="token keyword">fun</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> PsiElement<span class="token operator">?</span> <span class="token punctuation">{</span></span>
81<span class="line">        <span class="token comment">// This is a naive implementation; we&#39;ll improve it later</span></span>
82<span class="line">        <span class="token keyword">val</span> identDecl <span class="token operator">=</span> PsiTreeUtil<span class="token punctuation">.</span>findChildOfType<span class="token operator">&lt;</span>IdentDecl<span class="token operator">&gt;</span><span class="token punctuation">(</span></span>
83<span class="line">            myElement<span class="token punctuation">.</span>containingFile<span class="token punctuation">,</span> IdentDecl<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java</span>
84<span class="line">        <span class="token punctuation">)</span></span>
85<span class="line">        <span class="token keyword">return</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>identDecl<span class="token operator">?</span><span class="token punctuation">.</span>name <span class="token operator">==</span> myElement<span class="token punctuation">.</span>text<span class="token punctuation">)</span> identDecl <span class="token keyword">else</span> <span class="token keyword">null</span></span>
86<span class="line">    <span class="token punctuation">}</span></span>
87<span class="line"></span>
88<span class="line"><span class="token punctuation">}</span></span>
89<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code>resolve</code> method uses the <code>PsiTreeUtil</code> utility class to find the first child of the file that is an <code>IdentDecl</code> element. It then checks if the name of the declaration matches the text of the reference, and returns the declaration element if it does.</p><p>Obviously, this is a naive implementation that works only if the first declaration in the file is the one we&#39;re looking for. Another thing I&#39;m ignoring here is Nim&#39;s insensitivity to naming style (e.g. <code>myVar</code> and <code>my_var</code> are considered the same). This is just simple way to demonstrate the concept. We&#39;ll improve it later.</p><h2 id="declaration-mixin" tabindex="-1"><a class="header-anchor" href="#declaration-mixin"><span>Declaration Mixin</span></a></h2><p>Now that we have the reference side of things set up, let&#39;s move on to the declaration side. The platform docs state that:</p><blockquote><p>Every PSI element that can be renamed or referenced needs to implement [the] <code>PsiNamedElement</code> interface.</p></blockquote><p>It also mentions, in a different section, that:</p><blockquote><p>In order to enable more advanced functionality, prefer implementing <code>PsiNameIdentifierOwner</code> over <code>PsiNamedElement</code> where possible.</p></blockquote><p>It&#39;s not clear to me in what context and what features <code>PsiNameIdentifierOwner</code> provides over <code>PsiNamedElement</code>. From what I can tell, <code>PsiNameIdentifierOwner</code> is used when the name of the element is not directly accessible from the element itself, but is instead stored in a separate child element. In our case, the actual name of the identifier is stored in the <code>IDENT</code> token, which is a child of the <code>IdentDecl</code> element. So, we&#39;ll go with <code>PsiNamedElement</code> and implement its <code>getNameIdentifier</code> method to return the <code>IDENT</code> element.</p><p>Let&#39;s create a mixin class for <code>IdentDecl</code> that implements <code>PsiNameIdentifierOwner</code>:</p><div class="language-kotlin line-numbers-mode" data-highlighter="prismjs" data-ext="kt" data-title="kt"><pre><code><span class="line"><span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token function">IdentDeclMixin</span><span class="token punctuation">(</span>node<span class="token operator">:</span> ASTNode<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ASTWrapperPsiElement</span><span class="token punctuation">(</span>node<span class="token punctuation">)</span><span class="token punctuation">,</span> IdentDecl <span class="token punctuation">{</span></span>
90<span class="line"></span>
91<span class="line">    <span class="token keyword">
91override</span> <span class="token keyword">fun</span> <span class="token function">getNameIdentifier</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> PsiElement<span class="token operator">?</span> <span class="token operator">=</span> ident</span>
92<span class="line"></span>
93<span class="line">    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> String <span class="token operator">=</span> ident<span class="token punctuation">.</span>text</span>
94<span class="line"></span>
95<span class="line">    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">setName</span><span class="token punctuation">(</span>name<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> PsiElement <span class="token punctuation">{</span></span>
96<span class="line">        <span class="token function">TODO</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">&quot;Not yet implemented&quot;</span></span><span class="token punctuation">)</span></span>
97<span class="line">    <span class="token punctuation">}</span></span>
98<span class="line"><span class="token punctuation">}</span></span>
99<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code>getNameIdentifier</code> method returns the <code>IDENT</code> token element. Let&#39;s look at how it&#39;s implemented in the generated <code>IdentDeclImpl</code> class:</p><div class="language-java line-numbers-mode" data-highlighter="prismjs" data-ext="java" data-title="java"><pre><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">IdentDeclImpl</span> <span class="token keyword">extends</span> <span class="token class-name">IdentDeclMixin</span> <span class="token keyword">implements</span> <span class="token class-name">IdentDecl</span> <span class="token punctuation">{</span></span>
100<span class="line"></span>
101<span class="line">  <span class="token keyword">public</span> <span class="token class-name">IdentDeclImpl</span><span class="token punctuation">(</span><span class="token annotation punctuation">@NotNull</span> <span class="token class-name">ASTNode</span> node<span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
102<span class="line">    <span class="token keyword">super</span><span class="token punctuation">(</span>node<span class="token punctuation">)</span><span class="token punctuation">;</span></span>
103<span class="line">  <span class="token punctuation">}</span></span>
104<span class="line"></span>
105<span class="line">  <span class="token annotation punctuation">@Override</span></span>
106<span class="line">  <span class="token annotation punctuation">@NotNull</span></span>
107<span class="line">  <span class="token keyword">public</span> <span class="token class-name">PsiElement</span> <span class="token function">getIdent</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
108<span class="line">    <span class="token keyword">return</span> <span class="token function">findNotNullChildByType</span><span class="token punctuation">(</span><span class="token constant">IDENT</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
109<span class="line">  <span class="token punctuation">}</span></span>
110<span class="line"></span>
111<span class="line"><span class="token punctuation">}</span></span>
112<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The implementation generated by Grammar-Kit has a <code>getIdent</code> method that finds the <code>IDENT</code> child token. Since we&#39;re using Kotlin, the <code>getIdent</code> method is automatically converted to a property <code>ident</code> in the Kotlin class. This is why we can access <code>ident</code> directly in the mixin class.</p><p>Let&#39;s update the BNF file to use the mixin, and also tell Grammar-Kit that <code>IdentDecl</code> implements <code>PsiNameIdentifierOwner</code>:</p><div class="language-bnf line-numbers-mode" data-highlighter="prismjs" data-ext="bnf" data-title="bnf"><pre><code><span class="line"><span class="token operator">{</span></span>
113<span class="line">  <span class="token operator">...</span></span>
114<span class="line">  implements<span class="token operator">(</span><span class="token string">&quot;IdentDecl&quot;</span><span class="token operator">)</span>=<span class="token string">&quot;com.intellij.psi.PsiNameIdentifierOwner&quot;</span></span>
115<span class="line">  mixin<span class="token operator">(</span><span class="token string">&quot;IdentDecl&quot;</span><span class="token operator">)</span>=<span class="token string">&quot;khaledh.nimjet.psi.impl.IdentDeclMixin&quot;</span></span>
116<span class="line"><span class="token operator">}</span></span>
117<span class="line"><span class="token operator">...</span></span>
118<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let&#39;s generate the parser and PSI classes and test the <strong>Go To Declaration</strong> feature in the sandbox IDE.</p><p><img src="`+i+'" alt="Ident Reference and Declaration" width="400"></p><p>
118Notice that the reference and declaration are highlighted when the cursor is on either of them. This is because the IDE recognizes that the reference can be resolved to the declaration, and it highlights the declaration when the reference is selected (and vice versa). To test the navigation feature, place the cursor on the reference and press <code>Ctrl+B</code> (or <code>Cmd+B</code> on macOS) to navigate to the declaration. We can also place the cursor on the declaration and press <code>Ctrl+B</code> to navigate to the reference. Neat!</p><p>Let&#39;s also take a look at the PSI tree:</p><p><img src="'+l+'" alt="Ident Ref and Decl PSI"></p><p>If we click on the <code>IdentRef</code> node, we can see that it has a reference to the <code>IdentDecl</code> node. Great!</p><p>In the next section, we&#39;ll improve reference resolution to take into account scoping rules, so that references are resolved to their correct declaration.</p>',49)]))}const r=n(o,[["render",c],["__file","08-go-to-decl.html.vue"]]),m=JSON.parse(`{"path":"/nimjet/08-go-to-decl.html","title":"Go To Declaration","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Updating the Grammar","slug":"updating-the-grammar","link":"#updating-the-grammar","children":[]},{"level":2,"title":"Reference Mixin","slug":"reference-mixin","link":"#reference-mixin","children":[]},{"level":2,"title":"Declaration Mixin","slug":"declaration-mixin","link":"#declaration-mixin","children":[]}],"git":{"updatedTime":1727472785000},"filePathRelative":"nimjet/08-go-to-decl.md","excerpt":"\\n<p>Now that we've got the basics in place, let's add our first actual feature: Go To\\nDeclaration. Consider this simple Nim program:</p>\\n<div class=\\"language-nim line-numbers-mode\\" data-highlighter=\\"prismjs\\" data-ext=\\"nim\\" data-title=\\"nim\\"><pre><code><span class=\\"line\\"><span class=\\"token keyword\\">let</span> <span class=\\"highlighted-word\\">msg</span> <span class=\\"token operator\\">=</span> <span class=\\"token string\\">\\"hello\\"</span>   <span class=\\"token comment\\"># &lt;- declaration</span></span>\\n<span class=\\"line\\">echo <span class=\\"highlighted-word\\">msg</span>            <span class=\\"token comment\\"># &lt;- reference</span></span>\\n<span class=\\"line\\"></span></code></pre>\\n<div class=\\"line-numbers\\" aria-hidden=\\"true\\" style=\\"counter-reset:line-number 0\\"><div class=\\"line-number\\"></div><div class=\\"line-number\\"></div></div></div>"}`);export{r as comp,m as data};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.