Customized Rendering of Class Members in the NetBeans Java Editor

Some small experiments with the Highlighting SPI, which I discussed yesterday, in response to Casper Bang's comments at the end of that blog entry:
I am curious Geertjan, would this be the API to use, if one wanted to implement a feature which could filter away the noise of non-essential code.

Say I wanted to be able to toggle a button up in the editor toolbar, which would cause all System.out.print/Logger statements as well as catch blocks to be rendered almost invisible (until my caret is on that line).

I often find myself using too few logging statements and uniting exception handling into one catch, just to avoid this "noise" which obscures what my code is actually trying to do.

In the first experiment, I have changed the background color of all methods in my class (but not the methods of inner classes), as can be seen here:

Notice that the constructor is not treated as a method. And here, the same class is shown, this time with the methods greyed out (i.e., I changed their foreground color, which is different to commenting out the code, because here we are only interested in rendering of the code, not in adding the // to the front of the lines):

Here is the AttributeSet for the above:

private static final AttributeSet defaultColors =
            AttributesUtilities.createImmutable(StyleConstants.Foreground,
            new Color(150, 150, 150));

The change (i.e., the new background color or foreground color) is performed when the caret changes (see the CaretListener in yesterday's blog entry), but could be done in other ways too, such as via the ModuleInstall class, for example, although that would affect performance at start up, so be careful.

The pieces that make all this possible are the aforementioned Highlighting SPI, together with the Java Sources API, and other supporting APIs which, in NetBeans terms, are collectively referred to as the "Retouche APIs".

Starting from the code presented in yesterday's blog entry, I only changed the scheduleUpdate() method, as follows:

public void scheduleUpdate() {
    if (task == null) {
        task = RequestProcessor.getDefault().create(new Runnable() {

            public void run() {
                Document doc = comp.getDocument();
                //Get the JavaSource object from the java.swing.text.Document,
                //which, in turn, is obtained from the JTextComponent that
                //represents the editor (see the constructor of
                //MarkOccurrencesHighlighter class in yesterday's blog entry):
                JavaSource javaSource = JavaSource.forDocument(doc);
                try {
                    javaSource.runUserActionTask(new Task<CompilationController>() {

                        public void run(CompilationController compilationController) throws Exception {
                            compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
                            Document document = compilationController.getDocument();
                            if (document != null) {
                                new MemberVisitor(compilationController).scan(compilationController.getCompilationUnit(), null);
                            }
                        }
                    }, true);
                } catch (IOException ex) {
                    Exceptions.printStackTrace(ex);
                }
            }
        }, true);
        task.setPriority(Thread.MIN_PRIORITY);
    }
    task.cancel();
    task.schedule(DELAY);
}

Here's the MemberVisitor:

private static class MemberVisitor extends TreePathScanner {

    private CompilationInfo info;

    public MemberVisitor(CompilationInfo info) {
        this.info = info;
    }

    @Override
    public Void visitClass(ClassTree t, Void v) {
        Element el = info.getTrees().getElement(getCurrentPath());
        TypeElement te = (TypeElement) el;
        List enclosedElements = te.getEnclosedElements();
        for (int i = 0; i < enclosedElements.size(); i++) {
            Element encEl = (Element) enclosedElements.get(i);
            Trees trees = info.getTrees();
            CompilationUnitTree cUnit = info.getCompilationUnit();
            if (encEl.getKind() == ElementKind.METHOD) {
                //Get a new Tree, specifically for our found elements:
                Tree tree = trees.getTree(encEl);
                int startPosition = (int) info.getTrees().getSourcePositions().getStartPosition(cUnit, tree);
                int endPosition = (int) info.getTrees().getSourcePositions().getEndPosition(cUnit, tree);
                //Add the start position and end position,
                //together with colors, to the Highlighting SPI's OffsetsBag:
                bag.addHighlight(startPosition, endPosition, defaultColors);
            }
        }
        return null;
    }
}

So, those are my first small steps in using the Highlighting SPI to render the class members in the Java editor. Other experiments will follow, but this should give an idea of what is needed if you want to explore this part of the NetBeans APIs. "Simply" identify the elements you'd like to handle differently and then add them to the Highlighting SPI's highlighting bag.

Essential reading for programmatically customizing the Java editor in NetBeans IDE:

Comments:

Cool stuff.

I can imagine a notion of code layers:

- static vs. non static
- private vs. protected vs. public vs. package
- synchronized vs. not

which can be highlighted based on some check boxes. By assigning same foreground as background you can effectively hide those layers.

I wonder if you could add an ability to color the elements in your project that can be selected using Jackpot query language:

http://jackpot.netbeans.org/docs/rule-language.html

Posted by Sandip on May 19, 2008 at 10:54 AM PDT #

Jackpot, Schlieman, Retouche, NetBeans Lexer, Java Compilation API... it is a bit confusing and seems to overlap?!

Anyway, awesome info. I am now filtering SOP and logging statements via the following method, a slightly modified version of your code. I still have some issues getting the outer-most parenthesizes pair highlighted and not really satisfied with lag and flickering.

@Override
public Object visitMethodInvocation(MethodInvocationTree methodInvocationTree, Object obj)
{
Trees trees = compilationInfo.getTrees();
SourcePositions sourcePos = trees.getSourcePositions();
CompilationUnitTree compUnit = compilationInfo.getCompilationUnit();

int startPos = (int) sourcePos.getStartPosition(compUnit, methodInvocationTree.getMethodSelect() );
int endPos = (int) sourcePos.getEndPosition(compUnit, methodInvocationTree.getMethodSelect() );
Element element = trees.getElement( getCurrentPath() );

if(element != null && element.getKind() == ElementKind.METHOD )
{
if(startPos != javax.tools.Diagnostic.NOPOS && endPos != javax.tools.Diagnostic.NOPOS)
{
// Highlight (suppress) the method call
bag.addHighlight(startPos, endPos, defaultColors);

// Highlight (suppress) everything in the argument list
for(ExpressionTree tree : methodInvocationTree.getArguments())
{
startPos = (int) sourcePos.getStartPosition(compUnit, tree);
endPos = (int) sourcePos.getEndPosition(compUnit, tree);
bag.addHighlight(startPos, endPos, defaultColors);
}
}
}
}

Posted by Casper on May 19, 2008 at 01:10 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today