netbeans-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From geert...@apache.org
Subject [netbeans] branch master updated: Inline parameter name hints for Java (#1247)
Date Fri, 14 Jun 2019 07:05:54 GMT
This is an automated email from the ASF dual-hosted git repository.

geertjan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 8971234  Inline parameter name hints for Java (#1247)
8971234 is described below

commit 89712349b8367ba74d1bc3d956ebe9c98d91cd3c
Author: Jan Lahoda <jlahoda@netbeans.org>
AuthorDate: Fri Jun 14 09:05:49 2019 +0200

    Inline parameter name hints for Java (#1247)
    
    * A crude prototype of inline (parameter) hints.
    
    * Improving appearance of the inline hints.
    
    * Generalizing highlighting test infrastructure.
    
    * Adding option to disable/enable the inline hints.
    
    * Preventing NPE while preparing compound highlights, adding simple tests.
    
    * Adding simple tests for the parameter name hints.
    
    * Fixing varargs usage.
    
    * Improving the view structure by creating a wrapper view over the existing highlight view.
    
    * Cleaning the views implementation.
    
    * Fix behavior of String literals which contain embedded escape sequences.
    
    * Resolving review comments Junichi
---
 .../modules/editor/actions/Bundle.properties       |   1 +
 .../editor/actions/ShowInlineHintsAction.java      |  37 +++++
 ide/editor.lib2/apichanges.xml                     |  15 ++
 ide/editor.lib2/nbproject/project.properties       |   2 +-
 .../editor/lib2/highlighting/HighlightsList.java   |   8 +-
 .../modules/editor/lib2/view/DocumentViewOp.java   |  16 ++-
 .../editor/lib2/view/HighlightsViewFactory.java    |  33 +++--
 .../editor/lib2/view/ParagraphViewChildren.java    |   4 +-
 .../editor/lib2/view/PrependedTextView.java        | 155 +++++++++++++++++++++
 .../modules/editor/lib2/view/ViewUtils.java        |   4 +-
 .../lib2/highlighting/HighlightsListTest.java      | 110 +++++++++++++--
 java/java.editor.base/nbproject/project.properties |   2 +-
 .../base/semantic/SemanticHighlighterBase.java     |  29 +++-
 .../java/editor/base/semantic/DetectorTest.java    |  60 ++++++++
 .../java/editor/base/semantic/HighlightImpl.java   |  19 ++-
 .../java/editor/base/semantic/MarkOccDetTest.java  |   3 +-
 .../java/editor/base/semantic/TestBase.java        | 125 ++++++++++++-----
 .../semantic/HighlightsLayerFactoryImpl.java       |   1 +
 .../java/editor/semantic/SemanticHighlighter.java  |  27 +++-
 19 files changed, 581 insertions(+), 70 deletions(-)

diff --git a/ide/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties b/ide/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties
index 857bfa7..17b02bc 100644
--- a/ide/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties
+++ b/ide/editor.actions/src/org/netbeans/modules/editor/actions/Bundle.properties
@@ -44,6 +44,7 @@ caret-next-word=Insertion Point to Next Word
 caret-previous-word=Insertion Point to Previous Word
 selection-next-word=Extend Selection to Next Word
 selection-previous-word=Extend Selection to Previous Word
+toggle-inline-hints=Show Inline &Hints
 toggle-lines-view=Show &Indent Guide Lines
 clipboard-lines=Paste as Lines
 remove-last-caret=Remove Last Caret
diff --git a/ide/editor.actions/src/org/netbeans/modules/editor/actions/ShowInlineHintsAction.java b/ide/editor.actions/src/org/netbeans/modules/editor/actions/ShowInlineHintsAction.java
new file mode 100644
index 0000000..a97a8ef
--- /dev/null
+++ b/ide/editor.actions/src/org/netbeans/modules/editor/actions/ShowInlineHintsAction.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.editor.actions;
+
+import java.awt.event.ActionEvent;
+import javax.swing.AbstractAction;
+import org.netbeans.api.editor.EditorActionRegistration;
+
+@EditorActionRegistration(name="toggle-inline-hints",
+                          menuPath="View",
+                          menuPosition=899,
+                          preferencesKey=ShowInlineHintsAction.KEY_LINES,
+                          preferencesDefault=ShowInlineHintsAction.DEF_LINES)
+public class ShowInlineHintsAction extends AbstractAction {
+    public static final String KEY_LINES = "enable.inline.hints";
+    public static final boolean DEF_LINES = false;
+    @Override
+    public void actionPerformed(ActionEvent e) {
+    }
+
+}
diff --git a/ide/editor.lib2/apichanges.xml b/ide/editor.lib2/apichanges.xml
index 6ce4e6a..03211f4 100644
--- a/ide/editor.lib2/apichanges.xml
+++ b/ide/editor.lib2/apichanges.xml
@@ -83,6 +83,21 @@ is the proper place.
     <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        <change id="PrependedTextOpt">
+            <summary>Prepended text for highlights</summary>
+            <version major="2" minor="24"/>
+            <date day="14" month="5" year="2019"/>
+            <author login="jlahoda"/>
+            <compatibility binary="compatible" source="compatible" semantic="compatible" addition="yes" deprecation="no" deletion="no" modification="no" />
+            <description>
+                <p>If AttributeSet returned from HighlightsSequence contains key "virtual-text-prepend"
+                   with a value of type String, the UI may optionally render the value as a
+                   virtual text before the text of the highlight. It is recommended to make the
+                   span of length 1, to avoid problems when the AttributeSet with "virtual-test-prepend"
+                   is merged with other AttributeSets, which could lead to duplication.</p>
+            </description>
+            <class name="HighlightsSequence" package="org.netbeans.spi.editor.highlighting"/>
+        </change>
         <change id="EditorActionRegistration.category">
             <summary>Added category to EditorActionRegistration</summary>
             <version major="2" minor="15"/>
diff --git a/ide/editor.lib2/nbproject/project.properties b/ide/editor.lib2/nbproject/project.properties
index 31147b9..f3f622d 100644
--- a/ide/editor.lib2/nbproject/project.properties
+++ b/ide/editor.lib2/nbproject/project.properties
@@ -18,7 +18,7 @@
 is.autoload=true
 javac.source=1.7
 javac.compilerargs=-Xlint:unchecked
-spec.version.base=2.23.0
+spec.version.base=2.24.0
 
 javadoc.arch=${basedir}/arch.xml
 javadoc.apichanges=${basedir}/apichanges.xml
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java
index 1b76407..a97a959 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java
@@ -20,6 +20,7 @@
 package org.netbeans.modules.editor.lib2.highlighting;
 
 import java.awt.Font;
+import java.util.Objects;
 import java.util.logging.Logger;
 import javax.swing.text.AttributeSet;
 import org.netbeans.lib.editor.util.ArrayUtilities;
@@ -117,9 +118,10 @@ public final class HighlightsList {
      * @param wsEndOffset whitespace end offset must be lower than or equal to maxEndOffset
      *  and when exceeded a first whitespace char in docText means that the cutting will end there.
      * @param docText document text in order properly handle wsEndOffset parameter.
+     * @param usePrependText reflect the prepended text setting.
      * @return either simple or compound attribute set.
      */
-    public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOffset, CharSequence docText) {
+    public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOffset, CharSequence docText, boolean usePrependText) {
         assert (maxEndOffset <= endOffset()) :
                 "maxEndOffset=" + maxEndOffset + " > endOffset()=" + endOffset() + ", " + this; // NOI18N
         HighlightItem item = get(0);
@@ -157,12 +159,14 @@ public final class HighlightsList {
 
         // Extends beyond first highlight
         Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont);
+        Object firstPrependText = usePrependText && firstAttrs != null ? firstAttrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null;
         int index = 1;
         while (true) {
             item = get(index);
             AttributeSet attrs = item.getAttributes();
             Font font = ViewUtils.getFont(attrs, defaultFont);
-            if (!font.equals(firstFont)) { // Stop at itemEndOffset
+            Object prependText = usePrependText && attrs != null ? attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null;
+            if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText)) { // Stop at itemEndOffset
                 if (index == 1) { // Just single attribute set
                     cutStartItems(1);
                     startOffset = itemEndOffset; // end offset of first item
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
index a226a30..80f1b9a 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java
@@ -291,6 +291,7 @@ public final class DocumentViewOp
     private Map<Font,FontInfo> fontInfos = new HashMap<Font, FontInfo>(4);
     
     private Font defaultFont;
+    private Font defaultHintFont;
     
     private boolean fontRenderContextFromPaint;
 
@@ -314,6 +315,7 @@ public final class DocumentViewOp
     boolean asTextField;
     
     private boolean guideLinesEnable;
+    private boolean inlineHintsEnable;
     
     private int indentLevelSize;
     
@@ -921,10 +923,13 @@ public final class DocumentViewOp
         // Line height correction
         float lineHeightCorrectionOrig = rowHeightCorrection;
         rowHeightCorrection = prefs.getFloat(SimpleValueNames.LINE_HEIGHT_CORRECTION, 1.0f);
+        boolean inlineHintsEnableOrig = inlineHintsEnable;
+        inlineHintsEnable = Boolean.TRUE.equals(prefs.getBoolean("enable.inline.hints", false)); // NOI18N
         boolean updateMetrics = (rowHeightCorrection != lineHeightCorrectionOrig);
         boolean releaseChildren = nonInitialUpdate && 
                 ((nonPrintableCharactersVisible != nonPrintableCharactersVisibleOrig) ||
-                 (rowHeightCorrection != lineHeightCorrectionOrig));  
+                 (rowHeightCorrection != lineHeightCorrectionOrig) ||
+                 (inlineHintsEnable != inlineHintsEnableOrig));
         indentLevelSize = getIndentSize();
         tabSize = prefs.getInt(SimpleValueNames.TAB_SIZE, EditorPreferencesDefaults.defaultTabSize);
         if (updateMetrics) {
@@ -1060,6 +1065,7 @@ public final class DocumentViewOp
             fontInfos.put(null, defaultFontInfo); // Alternative way to find default font info
             updateRowHeight(defaultFontInfo, true);
             defaultFont = font;
+            defaultHintFont = font.deriveFont((float) (font.getSize2D() * 0.75));
             defaultCharWidth = defaultFontInfo.charWidth;
             
             tabTextLayout = null;
@@ -1165,6 +1171,10 @@ public final class DocumentViewOp
         return guideLinesEnable && !asTextField;
     }
 
+    public boolean isInlineHintsEnable() {
+        return inlineHintsEnable;
+    }
+
     public int getIndentLevelSize() {
         return indentLevelSize;
     }
@@ -1259,6 +1269,10 @@ public final class DocumentViewOp
         return defaultFont;
     }
 
+    public Font getDefaultHintFont() {
+        return defaultHintFont;
+    }
+
     public float getDefaultRowHeight() {
         checkSettingsInfo();
         return defaultRowHeightInt;
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java
index 1b2184b..fce916d 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java
@@ -28,6 +28,8 @@ import javax.swing.event.ChangeListener;
 import javax.swing.text.AttributeSet;
 import javax.swing.text.Element;
 import javax.swing.text.View;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.lib.editor.util.CharSequenceUtilities;
 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
 import org.netbeans.modules.editor.lib2.highlighting.DirectMergeContainer;
@@ -188,7 +190,7 @@ public final class HighlightsViewFactory extends EditorViewFactory implements Hi
         }
         if (startOffset == lineEndOffset - 1) {
             AttributeSet attrs = hList.cutSingleChar();
-            return new NewlineView(attrs);
+            return wrapWithPrependedText(new NewlineView(attrs), attrs);
         } else { // Regular view with possible highlight(s) or tab view
             updateTabsAndHighlightsAndRTL(startOffset);
             if (charType == TAB_CHAR_TYPE) {
@@ -198,7 +200,7 @@ public final class HighlightsViewFactory extends EditorViewFactory implements Hi
                     limitOffset = tabsEndOffset;
                 }
                 attrs = hList.cut(limitOffset);
-                return new TabView(limitOffset - startOffset, attrs);
+                return wrapWithPrependedText(new TabView(limitOffset - startOffset, attrs), attrs);
 
             } else { // Create regular view with either LTR or RTL text
                 limitOffset = Math.min(limitOffset, nextTabOrRTLOffset); // nextTabOrRTLOffset < lineEndOffset 
@@ -216,11 +218,13 @@ public final class HighlightsViewFactory extends EditorViewFactory implements Hi
                     }
                             
                 }
-                AttributeSet attrs = hList.cutSameFont(defaultFont, limitOffset, wsEndOffset, docText);
+                boolean inlineHints = documentView().op.isInlineHintsEnable();
+                AttributeSet attrs = hList.cutSameFont(defaultFont, limitOffset, wsEndOffset, docText, inlineHints);
                 int length = hList.startOffset() - startOffset;
-                HighlightsView view = new HighlightsView(length, attrs);
-                if (origView instanceof HighlightsView && origView.getLength() == length) { // Reuse
-                    HighlightsView origHView = (HighlightsView) origView;
+                EditorView view = wrapWithPrependedText(new HighlightsView(length, attrs), attrs);
+                EditorView origViewUnwrapped = origView instanceof PrependedTextView ? ((PrependedTextView) origView).getDelegate() : origView;
+                if (origViewUnwrapped != null && origViewUnwrapped.getClass() == HighlightsView.class && origViewUnwrapped.getLength() == length) {
+                    HighlightsView origHView = (HighlightsView) origViewUnwrapped;
                     TextLayout origTextLayout = origHView.getTextLayout();
                     if (origTextLayout != null) {
                         if (ViewHierarchyImpl.CHECK_LOG.isLoggable(Level.FINE)) {
@@ -235,11 +239,12 @@ public final class HighlightsViewFactory extends EditorViewFactory implements Hi
                             }
                         }
                         Font font = ViewUtils.getFont(attrs, defaultFont);
-                        Font origFont = ViewUtils.getFont(origView.getAttributes(), defaultFont);
+                        Font origFont = ViewUtils.getFont(origViewUnwrapped.getAttributes(), defaultFont);
                         if (font != null && font.equals(origFont)) {
                             float origWidth = origHView.getWidth();
-                            view.setTextLayout(origTextLayout, origWidth);
-                            view.setBreakInfo(origHView.getBreakInfo());
+                            HighlightsView hv = (HighlightsView) (view instanceof PrependedTextView ? ((PrependedTextView) view).getDelegate() : view);
+                            hv.setTextLayout(origTextLayout, origWidth);
+                            hv.setBreakInfo(origHView.getBreakInfo());
                             ViewStats.incrementTextLayoutReused(length);
                         }
                     }
@@ -249,6 +254,16 @@ public final class HighlightsViewFactory extends EditorViewFactory implements Hi
         }
     }
 
+    private @NonNull EditorView wrapWithPrependedText(@NonNull EditorView origView, @NullAllowed AttributeSet attrs) {
+        boolean inlineHints = documentView().op.isInlineHintsEnable();
+
+        if (attrs != null && inlineHints && attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String) {
+            return new PrependedTextView(documentView().op, attrs, origView);
+        }
+
+        return origView;
+    }
+
     private void updateTabsAndHighlightsAndRTL(int offset) {
         if (offset >= nextTabOrRTLOffset) { // Update nextTabOrRTLOffset
             // Determine situation right at offset
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
index d67c36a..38c2787 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
@@ -168,8 +168,8 @@ final class ParagraphViewChildren extends ViewChildren<EditorView> {
                 view.setRawEndOffset(relEndOffset); // Below offset-gap
                 view.setParent(pView);
                 // Possibly assign text layout
-                if (view instanceof HighlightsView) {
-                    HighlightsView hView = (HighlightsView) view;
+                if (view instanceof HighlightsView || (view instanceof PrependedTextView && ((PrependedTextView) view).getDelegate() instanceof HighlightsView)) {
+                    HighlightsView hView = (HighlightsView) (view instanceof HighlightsView ? view : ((PrependedTextView) view).getDelegate());
                     // Fill in text layout if necessary
                     if (hView.getTextLayout() == null) { // Fill in text layout
                         if (docText == null) {
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java
new file mode 100644
index 0000000..472c8f2
--- /dev/null
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.editor.lib2.view;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Position;
+import javax.swing.text.Position.Bias;
+import javax.swing.text.View;
+import org.netbeans.spi.editor.highlighting.HighlightsSequence;
+
+/**
+ * TODO
+ */
+public final class PrependedTextView extends EditorView {
+
+    private final AttributeSet attributes;
+    private final EditorView delegate;
+    private final TextLayout prependedTextLayout;
+    private final double leftShift;
+    private final double prependedTextWidth;
+
+    public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView delegate) {
+        super(null);
+        this.attributes = attributes;
+        this.delegate = delegate;
+        Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont());
+        prependedTextLayout = op.createTextLayout((String) attributes.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND), font);
+        Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation!
+        double em = op.getDefaultCharWidth();
+        leftShift = em / 2;
+        prependedTextWidth = Math.ceil(textBounds.getWidth() + em);
+    }
+
+    @Override
+    public float getPreferredSpan(int axis) {
+        float superSpan = delegate.getPreferredSpan(axis);
+        if (axis == View.X_AXIS) {
+            superSpan += prependedTextWidth;
+        }
+        return superSpan;
+    }
+
+    @Override
+    public AttributeSet getAttributes() {
+        return attributes;
+    }
+    
+    @Override
+    public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) {
+        Shape res = delegate.modelToViewChecked(offset, alloc, bias);
+        Rectangle2D rect = ViewUtils.shapeAsRect(res);
+        rect.setRect(rect.getX() + prependedTextWidth, rect.getY(), rect.getWidth(), rect.getHeight());
+        return rect;
+    }
+
+    @Override
+    public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) {
+        Rectangle2D span = ViewUtils.shapeAsRect(hViewAlloc);
+        span.setRect(span.getX() + prependedTextWidth, span.getY(), span.getWidth() - prependedTextWidth, span.getHeight());
+        delegate.paint(g, span, clipBounds);
+        span.setRect(span.getX() - prependedTextWidth, span.getY(), prependedTextWidth, span.getHeight());
+
+        HighlightsSequence highlights = getDocumentView().getPaintHighlights(this, 0);
+
+        if (highlights.moveNext()) {
+            AttributeSet attrs = highlights.getAttributes();
+            HighlightsViewUtils.fillBackground(g, span, attrs, getDocumentView().getTextComponent());
+            HighlightsViewUtils.paintBackgroundHighlights(g, span, attrs, getDocumentView()); //TODO: clear some attributes (like boxes)???
+        }
+
+        g.setColor(Color.gray);
+        span.setRect(span.getX() + leftShift, span.getY(), prependedTextWidth - 2 * leftShift, span.getHeight());
+        HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView());
+    }
+
+    ParagraphView getParagraphView() {
+        return (ParagraphView) getParent();
+    }
+
+    DocumentView getDocumentView() {
+        ParagraphView paragraphView = getParagraphView();
+        return (paragraphView != null) ? paragraphView.getDocumentView() : null;
+    }
+
+    @Override
+    public int getRawEndOffset() {
+        return delegate.getRawEndOffset();
+    }
+
+    @Override
+    public void setRawEndOffset(int offset) {
+        delegate.setRawEndOffset(offset);
+    }
+
+    @Override
+    public int viewToModelChecked(double x, double y, Shape alloc, Position.Bias[] biasReturn) {
+        Rectangle2D bounds = ViewUtils.shapeAsRect(alloc);
+        bounds.setRect(bounds.getX() + prependedTextWidth, bounds.getY(),
+                       bounds.getWidth() - prependedTextWidth, bounds.getHeight());
+        if (x <= bounds.getX()) {
+            return getStartOffset();
+        }
+        return delegate.viewToModelChecked(x, y, bounds, biasReturn);
+    }
+
+    @Override
+    public int getLength() {
+        return delegate.getLength();
+    }
+
+    @Override
+    public int getStartOffset() {
+        return delegate.getStartOffset();
+    }
+
+    @Override
+    public int getEndOffset() {
+        return delegate.getEndOffset();
+    }
+
+    @Override
+    public void setParent(View parent) {
+        super.setParent(parent);
+        delegate.setParent(parent);
+    }
+
+    EditorView getDelegate() {
+        return delegate;
+    }
+
+}
diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
index 836fd92..bda7b07 100644
--- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
+++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
@@ -43,7 +43,9 @@ import org.netbeans.modules.editor.lib2.highlighting.CompoundAttributes;
  */
 
 public final class ViewUtils {
-    
+
+    public static final String KEY_VIRTUAL_TEXT_PREPEND = "virtual-text-prepend"; //NOI18N
+
     private ViewUtils() { // No instances
     }
 
diff --git a/ide/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsListTest.java b/ide/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsListTest.java
index f73e857..86e0693 100644
--- a/ide/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsListTest.java
+++ b/ide/editor.lib2/test/unit/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsListTest.java
@@ -26,6 +26,7 @@ import javax.swing.text.PlainDocument;
 import javax.swing.text.StyleConstants;
 import org.junit.Test;
 import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.modules.editor.lib2.view.ViewUtils;
 import org.netbeans.spi.editor.highlighting.HighlightsContainer;
 import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
 
@@ -67,7 +68,7 @@ public class HighlightsListTest {
 
         HighlightsList hList = highlightsListSimple(doc);
         // Fetch first
-        AttributeSet attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        AttributeSet attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert (attrs instanceof CompoundAttributes) : "Non-CompoundAttributes attrs=" + attrs;
         CompoundAttributes cAttrs = (CompoundAttributes) attrs;
         assert (cAttrs.startOffset() == 0) : "startOffset=" + cAttrs.startOffset();
@@ -78,21 +79,21 @@ public class HighlightsListTest {
         assertItem(items[2], 6, null);
         // Fetch next
         assert (hList.startOffset() == 6);
-        attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert attrs == attrSets[1];
         assert (hList.startOffset() == 8);
-        attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == null);
         assert (hList.startOffset() == 10);
         
         
         hList = highlightsListSimple(doc);
-        attrs = hList.cutSameFont(defaultFont, 2, 2, null);
+        attrs = hList.cutSameFont(defaultFont, 2, 2, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == null);
-        attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert (hList.startOffset() == 6);
         assert (attrs instanceof CompoundAttributes) : "Non-CompoundAttributes attrs=" + attrs;
         cAttrs = (CompoundAttributes) attrs;
@@ -103,7 +104,7 @@ public class HighlightsListTest {
         assertItem(items[1], 6, null);
         
         hList = highlightsListSimple(doc);
-        attrs = hList.cutSameFont(defaultFont, 3, 3, null);
+        attrs = hList.cutSameFont(defaultFont, 3, 3, null, false);
         cAttrs = (CompoundAttributes) attrs;
         assert (cAttrs.startOffset() == 0) : "startOffset=" + cAttrs.startOffset();
         items = cAttrs.highlightItems();
@@ -111,7 +112,7 @@ public class HighlightsListTest {
         assertItem(items[0], 2, null);
         assertItem(items[1], 3, attrSets[0]);
         // Next
-        attrs = hList.cutSameFont(defaultFont, 5, 5, null);
+        attrs = hList.cutSameFont(defaultFont, 5, 5, null, false);
         cAttrs = (CompoundAttributes) attrs;
         assert (cAttrs.startOffset() == 3) : "startOffset=" + cAttrs.startOffset();
         items = cAttrs.highlightItems();
@@ -119,22 +120,22 @@ public class HighlightsListTest {
         assertItem(items[0], 4, attrSets[0]);
         assertItem(items[1], 5, null);
         // Next
-        attrs = hList.cutSameFont(defaultFont, 7, 7, null);
+        attrs = hList.cutSameFont(defaultFont, 7, 7, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == null);
         assert (hList.startOffset() == 6);
         // Next
-        attrs = hList.cutSameFont(defaultFont, 7, 7, null);
+        attrs = hList.cutSameFont(defaultFont, 7, 7, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == attrSets[1]);
         assert (hList.startOffset() == 7);
         // Next
-        attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == attrSets[1]);
         assert (hList.startOffset() == 8);
         // Next
-        attrs = hList.cutSameFont(defaultFont, 10, 10, null);
+        attrs = hList.cutSameFont(defaultFont, 10, 10, null, false);
         assert !(attrs instanceof CompoundAttributes);
         assert (attrs == null);
         assert (hList.startOffset() == 10);
@@ -151,6 +152,93 @@ public class HighlightsListTest {
         return reader.highlightsList();
     }
     
+    @Test
+    public void testSplitPrependText() throws Exception {
+        Document doc = document();
+
+        OffsetsBag bag = new OffsetsBag(doc);
+        AttributeSet attrs1 = AttributesUtilities.createImmutable(
+                StyleConstants.Foreground, Color.RED,
+                StyleConstants.FontFamily, fontNames[0]);
+        AttributeSet attrs2 = AttributesUtilities.createImmutable(
+                StyleConstants.Foreground, Color.RED,
+                StyleConstants.FontFamily, fontNames[0],
+                ViewUtils.KEY_VIRTUAL_TEXT_PREPEND, "test");
+        AttributeSet attrs3 = AttributesUtilities.createImmutable(
+                StyleConstants.Foreground, Color.RED,
+                StyleConstants.FontFamily, fontNames[1]);
+
+        bag.addHighlight(0, 2, attrs1);
+        bag.addHighlight(2, 4, attrs2);
+        bag.addHighlight(4, 6, attrs1);
+        bag.addHighlight(8, 10, attrs3);
+
+        int end = 14;
+        DirectMergeContainer dmc = new DirectMergeContainer(new HighlightsContainer[]{ bag }, true);
+
+        {
+        HighlightsReader reader = new HighlightsReader(dmc, 0, end);
+        reader.readUntil(end);
+        HighlightsList hList = reader.highlightsList();
+
+        // Fetch first
+        AttributeSet attrs = hList.cutSameFont(defaultFont, end, end, null, false);
+        assert (attrs instanceof CompoundAttributes) : "Non-CompoundAttributes attrs=" + attrs;
+        CompoundAttributes cAttrs = (CompoundAttributes) attrs;
+        assert (cAttrs.startOffset() == 0) : "startOffset=" + cAttrs.startOffset();
+        HighlightItem[] items = cAttrs.highlightItems();
+        assert (items.length == 4);
+        assertItem(items[0], 2, attrs1);
+        assertItem(items[1], 4, attrs2);
+        assertItem(items[2], 6, attrs1);
+        assertItem(items[3], 8, null);
+        // Fetch next
+        assert (hList.startOffset() == 8);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, false);
+        assert !(attrs instanceof CompoundAttributes);
+        assert attrs == attrs3;
+        assert (hList.startOffset() == 10);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, false);
+        assert !(attrs instanceof CompoundAttributes);
+        assert (attrs == null);
+        assert (hList.startOffset() == 14);
+        }
+
+        {
+        HighlightsReader reader = new HighlightsReader(dmc, 0, end);
+        reader.readUntil(end);
+        HighlightsList hList = reader.highlightsList();
+
+        // Fetch first
+        AttributeSet attrs = hList.cutSameFont(defaultFont, end, end, null, true);
+        assert !(attrs instanceof CompoundAttributes) : "CompoundAttributes attrs=" + attrs;
+        assert attrs == attrs1;
+        assert (hList.startOffset() == 2);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, true);
+        assert !(attrs instanceof CompoundAttributes);
+        assert (attrs == attrs2);
+        assert (hList.startOffset() == 4);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, true);
+        assert (attrs instanceof CompoundAttributes) : "Non-CompoundAttributes attrs=" + attrs;
+        CompoundAttributes cAttrs = (CompoundAttributes) attrs;
+        assert (cAttrs.startOffset() == 4) : "startOffset=" + cAttrs.startOffset();
+        HighlightItem[] items = cAttrs.highlightItems();
+        assert (items.length == 2);
+        assertItem(items[0], 6, attrs1);
+        assertItem(items[1], 8, null);
+        // Fetch next
+        assert (hList.startOffset() == 8);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, true);
+        assert !(attrs instanceof CompoundAttributes);
+        assert attrs == attrs3;
+        assert (hList.startOffset() == 10);
+        attrs = hList.cutSameFont(defaultFont, end, end, null, true);
+        assert !(attrs instanceof CompoundAttributes);
+        assert (attrs == null);
+        assert (hList.startOffset() == 14);
+        }
+    }
+
     private static void assertItem(HighlightItem item, int endOffset, AttributeSet attrs) {
         assert (item.getEndOffset() == endOffset) : "itemEndOffset=" + item.getEndOffset() + " != endOffset=" + endOffset; // NOI18N
         assert (item.getAttributes() == attrs) : "itemAttrs=" + item.getAttributes() + " != attrs=" + attrs; // NOI18N
diff --git a/java/java.editor.base/nbproject/project.properties b/java/java.editor.base/nbproject/project.properties
index eb59ba01..8d8f77e 100644
--- a/java/java.editor.base/nbproject/project.properties
+++ b/java/java.editor.base/nbproject/project.properties
@@ -16,7 +16,7 @@
 # under the License.
 spec.version.base=2.70.0
 is.autoload=true
-javac.source=1.7
+javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
 
 test.config.semantic.includes=\
diff --git a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/SemanticHighlighterBase.java b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/SemanticHighlighterBase.java
index c5b2015..58c7a05 100644
--- a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/SemanticHighlighterBase.java
+++ b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/SemanticHighlighterBase.java
@@ -27,6 +27,7 @@ import com.sun.source.tree.EnhancedForLoopTree;
 import com.sun.source.tree.ExportsTree;
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.LiteralTree;
 import com.sun.source.tree.MemberReferenceTree;
 import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.MethodInvocationTree;
@@ -319,7 +320,7 @@ public abstract class SemanticHighlighterBase extends JavaParserResultTask {
             return true;
         
         if (computeUnusedImports) {
-            setter.setHighlights(doc, imports);
+            setter.setHighlights(doc, imports, v.preText);
         }
 
         setter.setColorings(doc, newColoring);
@@ -406,6 +407,7 @@ public abstract class SemanticHighlighterBase extends JavaParserResultTask {
         private Map<Element, List<Use>> type2Uses;        
         private Map<Tree, List<Token>> tree2Tokens;
         private List<Token> contextKeywords;
+        private Map<int[], String> preText;
         private TokenList tl;
         private long memberSelectBypass = -1;        
         private SourcePositions sourcePositions;
@@ -419,6 +421,7 @@ public abstract class SemanticHighlighterBase extends JavaParserResultTask {
             type2Uses = new HashMap<Element, List<Use>>();
             tree2Tokens = new IdentityHashMap<Tree, List<Token>>();
             contextKeywords = new ArrayList<>();
+            preText = new HashMap<>();
 
             tl = new TokenList(info, doc, cancel);
             
@@ -1060,11 +1063,33 @@ public abstract class SemanticHighlighterBase extends JavaParserResultTask {
             return null;
         }
 
+        @Override
+        public Void visitLiteral(LiteralTree node, Void p) {
+            TreePath pp = getCurrentPath().getParentPath();
+            if (pp.getLeaf() != null &&
+                pp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
+                MethodInvocationTree inv = (MethodInvocationTree) pp.getLeaf();
+                int pos = inv.getArguments().indexOf(node);
+                if (pos != (-1)) {
+                    Element invoked = info.getTrees().getElement(pp);
+                    if (invoked != null && (invoked.getKind() == ElementKind.METHOD || invoked.getKind() == ElementKind.CONSTRUCTOR)) {
+                        long start = sourcePositions.getStartPosition(info.getCompilationUnit(), node);
+                        long end = start + 1;
+                        ExecutableElement invokedMethod = (ExecutableElement) invoked;
+                        pos = Math.min(pos, invokedMethod.getParameters().size() - 1);
+                        preText.put(new int[] {(int) start, (int) end},
+                                    invokedMethod.getParameters().get(pos).getSimpleName() + ":");
+                    }
+                }
+            }
+            return super.visitLiteral(node, p);
+        }
+
     }
 
     public static interface ErrorDescriptionSetter {
         
-        public void setHighlights(Document doc, Collection<int[]> highlights);
+        public void setHighlights(Document doc, Collection<int[]> highlights, Map<int[], String> preText);
         public void setColorings(Document doc, Map<Token, Coloring> colorings);
     }    
 }
diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
index 5223509..1f25ff4 100644
--- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
@@ -252,6 +252,53 @@ public class DetectorTest extends TestBase {
         performTest("GenericBoundIsClassUse");
     }
 
+    public void testParameterNames() throws Exception {
+        setShowPrependedText(true);
+        performTest("Test.java",
+                    "package test;" +
+                    "public class Test {" +
+                    "    public void api(String param1, int param2, int param3, float param4, Object... param5) {" +
+                    "    }" +
+                    "    private int getValue() {" +
+                    "        return -1;" +
+                    "    }" +
+                    "    private void test() {" +
+                    "        api(\"\", 2, getValue(), 1.0f);" +
+                    "        api(\"\", 2, getValue(), 1.0f, null);" +
+                    "        api(\"\", 2, getValue(), 1.0f, null, null);" +
+                    "    }" +
+                    "}",
+                    "[PUBLIC, CLASS, DECLARATION], 0:26-0:30\n" +
+                    "[PUBLIC, METHOD, DECLARATION], 0:48-0:51\n" +
+                    "[PUBLIC, CLASS], 0:52-0:58\n" +
+                    "[PARAMETER, DECLARATION], 0:59-0:65\n" +
+                    "[PARAMETER, DECLARATION], 0:71-0:77\n" +
+                    "[PARAMETER, DECLARATION], 0:83-0:89\n" +
+                    "[PARAMETER, DECLARATION], 0:97-0:103\n" +
+                    "[PUBLIC, CLASS], 0:105-0:111\n" +
+                    "[PARAMETER, DECLARATION], 0:115-0:121\n" +
+                    "[PRIVATE, METHOD, DECLARATION], 0:145-0:153\n" +
+                    "[PRIVATE, METHOD, UNUSED, DECLARATION], 0:197-0:201\n" +
+                    "[PUBLIC, METHOD], 0:213-0:216\n" +
+                    "[param1:], 0:217-0:218\n" +
+                    "[param2:], 0:221-0:222\n" +
+                    "[PRIVATE, METHOD], 0:224-0:232\n" +
+                    "[param4:], 0:236-0:237\n" +
+                    "[PUBLIC, METHOD], 0:250-0:253\n" +
+                    "[param1:], 0:254-0:255\n" +
+                    "[param2:], 0:258-0:259\n" +
+                    "[PRIVATE, METHOD], 0:261-0:269\n" +
+                    "[param4:], 0:273-0:274\n" +
+                    "[param5:], 0:279-0:280\n" +
+                    "[PUBLIC, METHOD], 0:293-0:296\n" +
+                    "[param1:], 0:297-0:298\n" +
+                    "[param2:], 0:301-0:302\n" +
+                    "[PRIVATE, METHOD], 0:304-0:312\n" +
+                    "[param4:], 0:316-0:317\n" +
+                    "[param5:], 0:322-0:323\n" +
+                    "[param5:], 0:328-0:329\n");
+    }
+
     @RandomlyFails
     public void testBLE91246() throws Exception {
         final boolean wasThrown[] = new boolean[1];
@@ -411,6 +458,19 @@ public class DetectorTest extends TestBase {
         });
     }
     
+    private void performTest(String fileName, String content, String expected) throws Exception {
+        performTest(fileName, content, new Performer() {
+            public void compute(CompilationController parameter, Document doc, final ErrorDescriptionSetter setter) {
+                new SemanticHighlighterBase() {
+                    @Override
+                    protected boolean process(CompilationInfo info, Document doc) {
+                        return process(info, doc, setter);
+                    }
+                }.process(parameter, doc);
+            }
+        }, false, expected);
+    }
+
     private FileObject testSourceFO;
     
     static {
diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/HighlightImpl.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/HighlightImpl.java
index 98c5a3d..dffd3e5 100644
--- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/HighlightImpl.java
+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/HighlightImpl.java
@@ -40,6 +40,7 @@ public final class HighlightImpl {
     private int start;
     private int end;
     private Collection<ColoringAttributes> colorings;
+    private String textPrepend;
     
     public HighlightImpl(Document doc, Token token, Collection<ColoringAttributes> colorings) {
         this.doc       = doc;
@@ -55,6 +56,14 @@ public final class HighlightImpl {
         this.colorings = colorings;
     }
     
+    public HighlightImpl(Document doc, int start, int end, String textPrepend) {
+        this.doc = doc;
+        this.start = start;
+        this.end = end;
+        this.colorings = ColoringAttributes.empty();
+        this.textPrepend = textPrepend;
+    }
+
     public int getStart() {
         return start;
     }
@@ -88,7 +97,15 @@ public final class HighlightImpl {
                 result.append(attribute.name());
             }
         }
-        
+
+        if (textPrepend != null) {
+            if (!first) {
+                result.append(", ");
+            }
+
+            result.append(textPrepend);
+        }
+
         result.append("]");
         
         return result.toString();
diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java
index c9fb1b1..cd07ba6 100644
--- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java
+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/MarkOccDetTest.java
@@ -18,6 +18,7 @@
  */
 package org.netbeans.modules.java.editor.base.semantic;
 
+import java.util.Collections;
 import java.util.List;
 import javax.swing.text.Document;
 import javax.swing.text.StyledDocument;
@@ -354,7 +355,7 @@ public class MarkOccDetTest extends TestBase {
                 }.processImpl(info, MarkOccurencesSettings.getCurrentNode(), doc, offset);
                 
                 if (spans != null) {
-                    setter.setHighlights(doc, spans);
+                    setter.setHighlights(doc, spans, Collections.<int[], String>emptyMap());
                 }
             }
         }, doCompileRecursively);
diff --git a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/TestBase.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/TestBase.java
index 7aa4cb3..7b2aca5 100644
--- a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/TestBase.java
+++ b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/TestBase.java
@@ -26,6 +26,8 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
 import java.io.Writer;
 import java.net.URL;
 import java.util.ArrayList;
@@ -95,23 +97,76 @@ public abstract class TestBase extends NbTestCase {
     }
     
     protected void performTest(String fileName, final Performer performer, boolean doCompileRecursively) throws Exception {
+        performTest(() -> {
+            File wd = getWorkDir();
+            File testSource = new File(wd, "test/" + fileName + ".java");
+
+            testSource.getParentFile().mkdirs();
+
+            File dataFolder = new File(getDataDir(), "org/netbeans/modules/java/editor/base/semantic/data/");
+
+            for (File f : dataFolder.listFiles()) {
+                copyToWorkDir(f, new File(wd, "test/" + f.getName()));
+            }
+
+            return FileUtil.toFileObject(testSource);
+        }, performer, doCompileRecursively,
+        actual -> {
+            File output = new File(getWorkDir(), getName() + ".out");
+            try (Writer out2File = new FileWriter(output)) {
+                out2File.append(actual);
+            }
+
+            boolean wasException = true;
+
+            try {
+                File goldenFile = getGoldenFile();
+                File diffFile = new File(getWorkDir(), getName() + ".diff");
+
+                assertFile(output, goldenFile, diffFile);
+                wasException = false;
+            } finally {
+                if (wasException && SHOW_GUI_DIFF) {
+                    try {
+                        String name = getClass().getName();
+
+                        name = name.substring(name.lastIndexOf('.') + 1);
+
+                        ShowGoldenFiles.run(name, getName(), fileName);
+
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        });
+    }
+
+    protected void performTest(String filename, String content, final Performer performer, boolean doCompileRecursively, String expected) throws Exception {
+        performTest(() -> {
+            FileObject wd = FileUtil.toFileObject(getWorkDir());
+            FileObject result = FileUtil.createData(wd, filename);
+
+            try (Writer out = new OutputStreamWriter(result.getOutputStream())) {
+                out.write(content);
+            }
+
+            return result;
+        }, performer, doCompileRecursively,
+        actual -> {
+            assertEquals(expected, actual);
+        });
+    }
+
+    protected void performTest(Input input, final Performer performer, boolean doCompileRecursively, Validator validator) throws Exception {
         SourceUtilsTestUtil.prepareTest(new String[] {"org/netbeans/modules/java/editor/resources/layer.xml"}, new Object[] {new MIMEResolverImpl()});
         
 	FileObject scratch = SourceUtilsTestUtil.makeScratchDir(this);
 	FileObject cache   = scratch.createFolder("cache");
 	
         File wd         = getWorkDir();
-        File testSource = new File(wd, "test/" + fileName + ".java");
-        
-        testSource.getParentFile().mkdirs();
-        
-        File dataFolder = new File(getDataDir(), "org/netbeans/modules/java/editor/base/semantic/data/");
-        
-        for (File f : dataFolder.listFiles()) {
-            copyToWorkDir(f, new File(wd, "test/" + f.getName()));
-        }
-        
-        testSourceFO = FileUtil.toFileObject(testSource);
+
+        testSourceFO = input.prepare();
 
         assertNotNull(testSourceFO);
 
@@ -123,7 +178,7 @@ public abstract class TestBase extends NbTestCase {
         
         testBuildTo.mkdirs();
         
-        FileObject srcRoot = FileUtil.toFileObject(testSource.getParentFile());
+        FileObject srcRoot = testSourceFO.getParent();
         SourceUtilsTestUtil.prepareTest(srcRoot,FileUtil.toFileObject(testBuildTo), cache);
         
         if (doCompileRecursively) {
@@ -162,8 +217,7 @@ public abstract class TestBase extends NbTestCase {
 	
         l.await();
                 
-        File output = new File(getWorkDir(), getName() + ".out");
-        Writer out = new FileWriter(output);
+        StringWriter out = new StringWriter();
         
         for (HighlightImpl h : highlights) {
             out.write(h.getHighlightTestData());
@@ -172,29 +226,8 @@ public abstract class TestBase extends NbTestCase {
         }
         
         out.close();
-                
-        boolean wasException = true;
         
-        try {
-            File goldenFile = getGoldenFile();
-            File diffFile = new File(getWorkDir(), getName() + ".diff");
-            
-            assertFile(output, goldenFile, diffFile);
-            wasException = false;
-        } finally {
-            if (wasException && SHOW_GUI_DIFF) {
-                try {
-                    String name = getClass().getName();
-                    
-                    name = name.substring(name.lastIndexOf('.') + 1);
-                    
-                    ShowGoldenFiles.run(name, getName(), fileName);
-                    
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        }
+        validator.validate(out.toString());
     }
     
     protected ColoringAttributes getColoringAttribute() {
@@ -239,6 +272,12 @@ public abstract class TestBase extends NbTestCase {
         this.sourceLevel = sourceLevel;
     }
 
+    private boolean showPrependedText;
+
+    protected final void setShowPrependedText(boolean showPrependedText) {
+        this.showPrependedText = showPrependedText;
+    }
+
     final class ErrorDescriptionSetterImpl implements SemanticHighlighterBase.ErrorDescriptionSetter {
         private final Set<HighlightImpl> highlights = new TreeSet<HighlightImpl>(new Comparator<HighlightImpl>() {
             public int compare(HighlightImpl o1, HighlightImpl o2) {
@@ -251,10 +290,15 @@ public abstract class TestBase extends NbTestCase {
         }
     
         @Override
-        public void setHighlights(Document doc, Collection<int[]> highlights) {
+        public void setHighlights(Document doc, Collection<int[]> highlights, Map<int[], String> preText) {
             for (int[] h : highlights) {
                 this.highlights.add(new HighlightImpl(doc, h[0], h[1], EnumSet.of(getColoringAttribute())));
             }
+            if (showPrependedText) {
+                for (Entry<int[], String> e : preText.entrySet()) {
+                    this.highlights.add(new HighlightImpl(doc, e.getKey()[0], e.getKey()[1], e.getValue()));
+                }
+            }
         }
 
         @Override
@@ -273,4 +317,11 @@ public abstract class TestBase extends NbTestCase {
         }
     }
     
+    protected interface Input {
+        public FileObject prepare() throws Exception;
+    }
+
+    protected interface Validator {
+        public void validate(String actual) throws Exception;
+    }
 }
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/semantic/HighlightsLayerFactoryImpl.java b/java/java.editor/src/org/netbeans/modules/java/editor/semantic/HighlightsLayerFactoryImpl.java
index 7dc9318..03a98b4 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/semantic/HighlightsLayerFactoryImpl.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/semantic/HighlightsLayerFactoryImpl.java
@@ -38,6 +38,7 @@ public class HighlightsLayerFactoryImpl implements HighlightsLayerFactory {
         return new HighlightsLayer[] {
             HighlightsLayer.create(SemanticHighlighter.class.getName() + "-1", ZOrder.SYNTAX_RACK.forPosition(1000), false,semantic),
             HighlightsLayer.create(SemanticHighlighter.class.getName() + "-2", ZOrder.SYNTAX_RACK.forPosition(1500), false, SemanticHighlighter.getImportHighlightsBag(context.getDocument())),
+            HighlightsLayer.create(SemanticHighlighter.class.getName() + "-3", ZOrder.SYNTAX_RACK.forPosition(1600), false, SemanticHighlighter.getPreTextBag(context.getDocument())),
             //the mark occurrences layer should be "above" current row and "below" the search layers:
             HighlightsLayer.create(MarkOccurrencesHighlighter.class.getName(), ZOrder.SHOW_OFF_RACK.forPosition(20), true, MarkOccurrencesHighlighter.getHighlightsBag(context.getDocument())),
             //"above" mark occurrences, "below" search layers:
diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/semantic/SemanticHighlighter.java b/java/java.editor/src/org/netbeans/modules/java/editor/semantic/SemanticHighlighter.java
index feae3f3..065d8f8 100644
--- a/java/java.editor/src/org/netbeans/modules/java/editor/semantic/SemanticHighlighter.java
+++ b/java/java.editor/src/org/netbeans/modules/java/editor/semantic/SemanticHighlighter.java
@@ -23,12 +23,14 @@ import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import javax.swing.SwingUtilities;
 import javax.swing.text.Document;
+import org.netbeans.api.editor.settings.AttributesUtilities;
 
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.api.java.source.TreePathHandle;
@@ -60,7 +62,7 @@ public class SemanticHighlighter extends SemanticHighlighterBase {
         
         public void setErrors(Document doc, List<ErrorDescription> errors, List<TreePathHandle> allUnusedImports) {}
         
-        public void setHighlights(final Document doc, final Collection<int[]> highlights) {
+        public void setHighlights(final Document doc, final Collection<int[]> highlights, Map<int[], String> preText) {
             SwingUtilities.invokeLater(new Runnable() {
                 public void run() {
                     OffsetsBag bag = new OffsetsBag(doc);
@@ -69,6 +71,12 @@ public class SemanticHighlighter extends SemanticHighlighterBase {
                         bag.addHighlight(highlight[0], highlight[1], ColoringManager.getColoringImpl(unused));
                     }
                     getImportHighlightsBag(doc).setHighlights(bag);
+                    
+                    OffsetsBag preTextBag = new OffsetsBag(doc);
+                    for (Entry<int[], String> e : preText.entrySet()) {
+                        preTextBag.addHighlight(e.getKey()[0], e.getKey()[1], AttributesUtilities.createImmutable("virtual-text-prepend", e.getValue()));
+                    }
+                    getPreTextBag(doc).setHighlights(preTextBag);
                 }
             });
         }
@@ -109,4 +117,21 @@ public class SemanticHighlighter extends SemanticHighlighterBase {
         return bag;
     }
 
+    private static final Object KEY_PRE_TEXT = new Object();
+    static OffsetsBag getPreTextBag(Document doc) {
+        OffsetsBag bag = (OffsetsBag) doc.getProperty(KEY_PRE_TEXT);
+        
+        if (bag == null) {
+            doc.putProperty(KEY_PRE_TEXT, bag = new OffsetsBag(doc));
+            
+            Object stream = doc.getProperty(Document.StreamDescriptionProperty);
+            
+            if (stream instanceof DataObject) {
+//                TimesCollector.getDefault().reportReference(((DataObject) stream).getPrimaryFile(), "ImportsHighlightsBag", "[M] Imports Highlights Bag", bag);
+            }
+        }
+        
+        return bag;
+    }
+
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Mime
View raw message