Source code file content

Revision: 2

import
» Project Revision History

» Checkout URL

web-content / trunk / markoccurrences / MarkOccurrencesTutorial.html

Size: 19465 bytes, 1 line
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <title>Extending the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences
        Highlighting</title>
        <link rel="stylesheet" href="http://www.netbeans.org/netbeans.css" type="text/css">
        <meta name="description" content="Extending C/C++ Editor in NetBeans 6.0 to provide Mark Occurrences highlighting tutorial">
        <meta name="author" content="Sergey Grinev">
        <meta name="keywords" content="">
        
        <script src="../../images/js/window_opener.js" type="text/javascript"></script>
        
    </head>
    <body>
        <a name="top"></a>
        
        <h1>Extending the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences
        Highlighting</h1>
        <!-- START INTRO ---------------------------------------------------------------------------------------* -->
        <div class="articledate" style="margin-left: 0px;font-style:italic;">
            <p><em>Contributed and maintained by <a href="mailto:sergey.grinev@sun.com"><i>Sergey Grinev</i></a><br>
                    
                November 2007</em> [Revision number: V6.0-1]<br>This publication is applicable 
            to NetBeans IDE 6.0 release</p>
        </div>
        

<p><img src="../../../images/articles/60/netbeans-stamp.gif" align="right"
    alt="Content on this page applies to NetBeans IDE 6.0" 
title="Content on this page applies to the NetBeans
    IDE 6.0" border="0" height="45" width="206">In this tutorial you will see how to extend C/C++ editor functionality. 
             We will create a NetBeans module for code highlighting in the editor using NetBeans editor highlighting SPI. 
            And we will use the C/C++ Reference API to retrieve information from language model.
        </p>
        
        <!-- END INTRO -----------------------------------------------------------------------------------------* -->
    
        <!-- START Link to Support and Docs paragraph ----------------------------------------------------------* -->
        <p>For more information about working with C/C++ applications in the NetBeans IDE, see the <a
                href="/kb/trails/cnd.html">C/C++ Applications Learning Trail</a> page on the
        NetBeans web site.</p>
        <!-- END Link to Support and Docs paragraph ------------------------------------------------------------* -->
        <h2><a name="tut_req"></a>Tutorial Requirements</h2>
        
        <br>
        <p>Before you proceed, make sure you review the requirements in this section.</p>
        
        <div class="indent">
            <!-- ======================================================================================== -->
            <h3>Prerequisites</h3>
            
            <p>This tutorial assumes that you have some basic knowledge of using IDEs and programming experience
            with Java.</p>
            
            <!-- ======================================================================================== -->
            <h3>Software Needed for This Tutorial</h3>
            
            <p>Before you begin, you need to install <a href="http://download.netbeans.org/netbeans/6.0/rc1/">NetBeans 6.0</a>.
                You will need both C/C++ and Java SE support, so the best choice
                is to select the Download All option and exclude
                all modules except the Base IDE, Java SE, and C/C++ packs during installation.
            </p>
        </div>
        
        <!-- ======================================================================================== -->
        <h2><a name="p1"></a>Preparing the Projects</h2>
        
        <!-- Intro paragraph for this topic --------------------------------------------------------------------* -->
        <br>
        
        <p>We will need two projects for this tutorial. One is a NetBeans module for
        the source code of our plugin. And another
        is C++ project to test its functionality.</p>
        
        <div class="indent">
            
            <h3>Creating the NetBeans Plugin Module</h3>
            <ol>
                <li>Choose File &gt; New Project. In the New Project wizard, 
                select NetBeans Modules under Categories and Module under Projects. Click Next. 
                <li>On the Name and Location page, type <tt>MarkOccurrences</tt> in the
                Project Name field and set Project Location to an appropriate folder on your disk. 
                If they are not selected, select Standalone Module and Set as Main Project. Click Next. </li>
                <li>On the Basic Module Configuration page, type
                <tt>org.netbeans.modules.markoccurrences</tt> in the Code Name Base
                field. Click Finish. </li>
                <li>We will need certain dependencies in this project. In the
                Projects window, add each library listed in the screenshot by
                right-clicking the Libraries node and selecting the library 
                in the Add Module Dependency dialog box. C/C++ modules APIs are under
                development, so you will need to select Show Non-API Modules in
                the dialog box to see them in the Module list.
                <br><br>
                <img src="../../../images/articles/60/cnd/markoccurrences/libraries.png" title alt="Projects window showing required libraries">
               </li>
                <li>Right-click each of C/C++ modules, choose Edit, and select 
                Implementation Version.</li>
            </ol>
            <br><br>
        </div><div class="indent">
            
            <h3>Creating a Test Application</h3>
            <ol>
                <li>Choose File &gt; New Project. Select the Samples &gt; C/C++
                &gt; C/C++ category, and the Args project. Click Next.
                <li>On the Project Name and Location page, set the Project
                Location to an appropriate folder on your disk. Click Finish. 
                <li>The <tt>Args_1</tt> project is created. Open the
                <tt>arg.c</tt> source file in the editor. We will use this file to test our plugin.
            </ol>
            
        </div>
        <p align="center"><a href="#top">top</a></p>
        <!-- ======================================================================================== -->
        <h2><a name="p1"></a>Creating Highlighting Infrastructure</h2>        
        <p>Now we will use the NetBeans API to add highlighting functionality to
        the C/C++ editor.</p>
        
        <div class="indent">
            
            <h3>Creating a Highlighting Provider</h3>
                        <ol>
                <li>Right-click the
                <tt>org.netbeans.modules.markoccurrences</tt> package in the
                Source Packages node of the Mark Occurrences 
                project and choose New &gt; Java Class.</li>
                <li>Name the new class <tt>MarkOccurrencesHighlighter</tt> and click
                Finish.
                <li>Replace the code in the new
                class with the following code:
                <pre>
package org.netbeans.modules.markoccurrences;

import java.awt.Color;
import java.lang.ref.WeakReference;
import javax.swing.JEditorPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.modules.cnd.modelutil.CsmUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.cookies.EditorCookie;
import org.openide.loaders.DataObject;

public class MarkOccurrencesHighlighter implements CaretListener {
    private static final AttributeSet defaultColors = AttributesUtilities.createImmutable(StyleConstants.Background, new Color(236, 235, 163));

    public void caretUpdate(CaretEvent e) {
        bag.clear();
        bag.addHighlight(0, 5, defaultColors);
    }
    
    private final WeakReference&lt;Document&gt; weakDoc;
    public MarkOccurrencesHighlighter(Document doc) {
        bag = new OffsetsBag(doc);
        weakDoc = new WeakReference&lt;Document&gt;((Document) doc);
        DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
        JEditorPane[] panes = CsmUtilities.getOpenedPanesInEQ(dobj.getCookie(EditorCookie.class));
        if (panes != null && panes.length > 0) {
            panes[0].addCaretListener(this);
        }
    }

    private final OffsetsBag bag;
    public OffsetsBag getHighlightsBag() {
        return bag;
    }
                }</pre>
                This class does not provide any smart functionality yet. It 
                only registers a listener to caret events and highlights 
                the first symbols of the document.</li>
            </ol>
        </div>
        <div class="indent">
            
            <h3>Creating and Registering HighlightsLayerFactory</h3>
            
            <p>Now let's let NetBeans know about our new highlight 
                provider by creating <tt>HighlightsLayerFactory</tt>.</p>
            <ol>
                <li>Add a new Java class to the project sources and name it 
                MarkOccurrencesHighlightsLayerFactory.</li>
                <li>Replace the code in the new class with the following code:
                <pre>

package org.netbeans.modules.markoccurrences;

import javax.swing.text.Document;
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
import org.netbeans.spi.editor.highlighting.ZOrder;

public class MarkOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory {
    
    public static MarkOccurrencesHighlighter getMarkOccurrencesHighlighter(Document doc) {
        MarkOccurrencesHighlighter highlighter = (MarkOccurrencesHighlighter)doc.getProperty(MarkOccurrencesHighlighter.class);
        if (highlighter == null) {
            doc.putProperty(MarkOccurrencesHighlighter.class, highlighter = new MarkOccurrencesHighlighter(doc));
        }
        return highlighter;
    }

    public HighlightsLayer[] createLayers(Context context) {
        return new HighlightsLayer[] {
                HighlightsLayer.create(
                    MarkOccurrencesHighlighter.class.getName(), 
                    ZOrder.CARET_RACK.forPosition(2000),
                    true,
                    getMarkOccurrencesHighlighter(context.getDocument()).getHighlightsBag())
        };
    }
                }</pre>
                <li>We have provided an implementation of 
                <tt>HighlightsLayerFactory</tt> that creates just one highlighting layer 
                with data provided by the <tt>MarkOccurrencesHighlighter</tt>
                class.
                Now we need to register this factory in <tt>layer.xml</tt>. 
                Open the <tt>layer.xml</tt> in the
                <tt>org.netbeans.modules.markoccurrences</tt> package and 
                change its content to the following:
                <pre>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd"&gt;
&lt;filesystem&gt;
    &lt;folder name="Editors"&gt;
        &lt;folder name="text"&gt;
            &lt;folder name="x-c++"&gt;
                &lt;file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" /&gt;
            &lt;/folder&gt;
            &lt;folder name="x-c"&gt;
                &lt;file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance" /&gt;
            &lt;/folder&gt;
        &lt;/folder&gt;
    &lt;/folder&gt;
                &lt;/filesystem&gt;</pre>
                </li>
                </ol>
                <p>Now we are ready for first run of our highlighter.</p> 
                <ol>
                <li>Build the project.</li>
                <li>When the project has successfully built, run it.</li>
                <li>Open the <tt>Args</tt> project we created in the previous
                section.</li>
                <li>Open the <tt>args.c</tt> file in the editor and click
                anywhere in the file. The highlighting will look like the
                following example:
                <br><br>
                <img src="../../../images/articles/60/cnd/markoccurrences/basic-hl.png" alt="Example of the
                highlighting"></li>
                </ol>
            <p>Great. Our highlighter works. Now let's teach it to be more useful.
            </p>
        </div>
        <p align="center"><a href="#top">top</a></p>
        <!-- ======================================================================================== -->
        <h2><a name="codemodel"></a>Gathering Information from the C/C++ Language
        Model</h2>
        
        <div class="indent">
            
            <ol>
                <li>In the <tt>MarkOccurrencesHighlighter.java</tt> class, remove our draft
                <tt>caretUpdate()</tt> implementation and add the following code:
                <pre>    
    private WeakReference&lt;CsmFile&gt; weakFile;
    public void caretUpdate(CaretEvent e) {
        bag.clear();
        CsmFile file = getCsmFile();
        if (file != null) {
            CsmReference ref = CsmReferenceResolver.getDefault().findReference(file, e.getDot());
            if (ref!=null && ref.getReferencedObject()!=null) {
                Collection&lt;CsmReference&gt; out = CsmReferenceRepository.getDefault().getReferences(ref.getReferencedObject(), file, true);
                for (CsmReference csmReference : out) {
                    bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset(), defaultColors);
                }
            }
        }
    }
    private CsmFile getCsmFile() {
        if (weakFile == null || weakFile.get() == null) {
            if (weakDoc == null || weakDoc.get() == null) {
                return null;
            }
            DataObject dobj = NbEditorUtilities.getDataObject(weakDoc.get());
            CsmFile file = CsmUtilities.getCsmFile(dobj, false);
            if (file != null) {
                weakFile = new WeakReference&lt;CsmFile&gt;(file);
            } else {
                return null;
            }
        }
        return weakFile.get();
    }</pre>
                In the <tt>caretUpdate()</tt> method we use 
                <tt>CsmReferenceResolver</tt> to find references to the 
                language entity under the cursor. If there is a valid entity 
                there, we ask <tt>CsmReferenceRepository</tt> about all 
                occurrences of the same entity in the 
                file and store their offsets. The <tt>getCsmFile()</tt> method is 
                a plumbing code to be sure we don't keep any language model data.
                <li>Press Ctrl-Shift-I to fix imports (or right-click and choose Fix Imports).
                <li>Build and run the project.
                <li>If you put the cursor on the <tt>argc</tt> parameter of 
                <tt>main()</tt>, you will see the highlighting shown below:
                <br><br>
                <img src="../../../images/articles/60/cnd/markoccurrences/marked-occurrences.png " alt="Example of the code with marked occurrences" width="516" height="306">
                </li>
                <li>Click in the different places in the file to see how mark occurrences is
                working. You may want to try more complex projects to see how it works with classes, macros, etc.
            </ol>
        </div>
        <p align="center"><a href="#top">top</a></p>
        <!-- ======================================================================================== -->
        <h2><a name="improving_performance"></a>Improving Performance</h2>
        
                 
            <p>Our current code is good enough for static text, but it can 
            produce severe delays during editing of the file. The delays
            happen because we start searching immediately after
            each key press. To solve this issue we will delay the task for 
            analyzing code, and if the cursor position was changed before the
            task started, we will cancel it and reschedule.</b></p>
            
            <ol>
                <li>In the <tt>MarkOccurrencesHighlighter.java</tt> class, change 
                the previous <tt>caretUpdate()</tt> implementation to the
                following  code:
                <pre>
    public void caretUpdate(CaretEvent e) {
        bag.clear();
        lastCaret = e.getDot();
        scheduleUpdate();
    }

    private int lastCaret;
    private RequestProcessor.Task task = null;
    private final static int DELAY = 1000;
    
    public void scheduleUpdate() {
        if (task==null) {
            task = RequestProcessor.getDefault().create(new Runnable() {
                public void run() {
                    CsmFile file = getCsmFile();
                    if (file != null) {
                        CsmReference ref = CsmReferenceResolver.getDefault().findReference(file, lastCaret);
                        if (ref!=null && ref.getReferencedObject()!=null) {
                            Collection&lt;CsmReference&gt; out = CsmReferenceRepository.getDefault().getReferences(ref.getReferencedObject(), file, true);
                            for (CsmReference csmReference : out) {
                                bag.addHighlight(csmReference.getStartOffset(), csmReference.getEndOffset(), defaultColors);
                            }
                        }
                    }
                }
            }, true);
            task.setPriority(Thread.MIN_PRIORITY);
        }
        task.cancel();
        task.schedule(DELAY);
    }</pre>
                In this code block we use
                <tt>org.openide.util.RequestProcessor</tt> to handle our code
                 analyzing task. If we get several caret updates we will just cancel
                 the previous task, 
                remember the cursor position, and reschedule the task for a 
                later time.
                <li>Fix imports, then build and run the  project.
                <li>Now you won't notice any delays when typing a large chunk of code.
            </ol>
            <p align="center"><a href="#top">top</a></p>
        <!-- ======================================================================================== -->
        <h2><a name="improving_performance"></a>Downloads</h2>
      
            
            <ul>
                <li>Sources of the tutorial: <a
                href="http://cnd.netbeans.org/markoccurrences/mark-occurences.tar.gz">mark-occurences.tar.gz</a>.
            </ul>
        <div class="feedback-box" ><a
                href="/about/contact_form.html?to=5&amp;subject=Feedback: Extending
                the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences
                Highlighting">Send Us Your Feedback</a><br style="clear:both;">
            
        </div>
        
    </body>
</html>

Project Features

About this Project

CND was started in November 2009, is owned by DimaZh, and has 197 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20160708.bf2ac18). © 2014, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
 
 
Close
loading
Please Confirm
Close