From 341ec5eafac1c4a236cbd60446043f5b6943adde Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 9 Apr 2019 21:00:36 +0200 Subject: [PATCH 01/11] A crude prototype of inline (parameter) hints. --- .../lib2/highlighting/HighlightsList.java | 5 +- .../lib2/view/HighlightsViewFactory.java | 4 +- .../view/PrependedTextHighlightsView.java | 104 ++++++++++++++++++ .../semantic/SemanticHighlighterBase.java | 27 ++++- .../editor/base/semantic/MarkOccDetTest.java | 3 +- .../java/editor/base/semantic/TestBase.java | 2 +- .../semantic/HighlightsLayerFactoryImpl.java | 1 + .../editor/semantic/SemanticHighlighter.java | 27 ++++- 8 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java 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 1b764078727d..335e13d9ae80 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; @@ -157,12 +158,14 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff // Extends beyond first highlight Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont); + Object firstPrependText = firstAttrs.getAttribute("virtual-text-prepend"); 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 = attrs != null ? attrs.getAttribute("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/HighlightsViewFactory.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java index 1b2184b3d5ba..3574df72d454 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 @@ -218,8 +218,8 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim } AttributeSet attrs = hList.cutSameFont(defaultFont, limitOffset, wsEndOffset, docText); int length = hList.startOffset() - startOffset; - HighlightsView view = new HighlightsView(length, attrs); - if (origView instanceof HighlightsView && origView.getLength() == length) { // Reuse + HighlightsView view = attrs != null && attrs.getAttribute("virtual-text-prepend") != null ? new PrependedTextHighlightsView(length, attrs) : new HighlightsView(length, attrs); + if (origView != null && origView.getClass() == HighlightsView.class && origView.getLength() == length) { // Reuse XXX: reuse disabled for PrependedTextHighlightsView! HighlightsView origHView = (HighlightsView) origView; TextLayout origTextLayout = origHView.getTextLayout(); if (origTextLayout != null) { diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java new file mode 100644 index 000000000000..d156c3eed14b --- /dev/null +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java @@ -0,0 +1,104 @@ +/* + * 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.Bias; +import org.netbeans.spi.editor.highlighting.HighlightsSequence; + +/** + * TODO + */ + +public class PrependedTextHighlightsView extends HighlightsView { + + private TextLayout prependedTextLayout; + private double leftShift; + private double prependedTextWidth; + private double moveup; + + public PrependedTextHighlightsView(int length, AttributeSet attributes) { + super(length, attributes); + } + + @Override + void setTextLayout(TextLayout textLayout, float width) { + Font font = ViewUtils.getFont(getAttributes(), getDocumentView().op.getDefaultFont()); + font = font.deriveFont((float) (font.getSize2D() * 0.75)); + prependedTextLayout = getDocumentView().op.createTextLayout((String) getAttributes().getAttribute("virtual-text-prepend"), font); + Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! + double em = getDocumentView().op.createTextLayout("m", font).getBounds().getWidth(); + leftShift = em / 2; + prependedTextWidth = textBounds.getWidth() + em; + double actualTextHeight = textLayout.getBounds().getHeight(); + moveup = (actualTextHeight - textBounds.getHeight()) / 2; +// double actualTextHeight = textLayout.getAscent(); +// moveup = (actualTextHeight - prependedTextLayout.getAscent()) / 2; + super.setTextLayout(textLayout, (float) (width + prependedTextWidth)); + } + + //span + //view to model??? + //pain + //break + + + @Override + public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) { + Shape res = super.modelToViewChecked(offset, alloc, bias); + if (bias == Bias.Forward || offset > getStartOffset()) { //TODO: seems not to have any effect? + Rectangle2D rect = ViewUtils.shapeAsRect(res); + Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! + rect.setRect(rect.getX() + textBounds.getWidth(), rect.getY(), rect.getWidth(), rect.getHeight()); + return rect; + } + return res; + } + + @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()); + super.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(); + System.err.println("attrs=" + attrs); + 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() - moveup, prependedTextWidth - 2 * leftShift, span.getHeight()); +// g.drawRoundRect((int) span.getX(), (int) span.getY(), (int) span.getWidth(), (int) span.getHeight(), 2, 2); + HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView()); + } + +} 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 c5b2015db6dc..9065d7ba979b 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.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 @@ protected boolean process(CompilationInfo info, final Document doc, ErrorDescrip return true; if (computeUnusedImports) { - setter.setHighlights(doc, imports); + setter.setHighlights(doc, imports, v.preText); } setter.setColorings(doc, newColoring); @@ -406,6 +407,7 @@ private static class DetectorVisitor extends CancellableTreePathScanner> type2Uses; private Map> tree2Tokens; private List contextKeywords; + private Map preText; private TokenList tl; private long memberSelectBypass = -1; private SourcePositions sourcePositions; @@ -419,6 +421,7 @@ private DetectorVisitor(org.netbeans.api.java.source.CompilationInfo info, final type2Uses = new HashMap>(); tree2Tokens = new IdentityHashMap>(); contextKeywords = new ArrayList<>(); + preText = new HashMap<>(); tl = new TokenList(info, doc, cancel); @@ -1060,11 +1063,31 @@ public Void visitMemberReference(MemberReferenceTree node, Void p) { 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 = sourcePositions.getEndPosition(info.getCompilationUnit(), node); + preText.put(new int[] {(int) start, (int) end}, + ((ExecutableElement) invoked).getParameters().get(pos).getSimpleName() + ":"); + } + } + } + return super.visitLiteral(node, p); + } + } public static interface ErrorDescriptionSetter { - public void setHighlights(Document doc, Collection highlights); + public void setHighlights(Document doc, Collection highlights, Map preText); public void setColorings(Document doc, Map colorings); } } 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 c9fb1b19ee40..cd07ba613aa4 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 @@ protected void process(CompilationInfo info, Document doc, SchedulerEvent event) }.processImpl(info, MarkOccurencesSettings.getCurrentNode(), doc, offset); if (spans != null) { - setter.setHighlights(doc, spans); + setter.setHighlights(doc, spans, Collections.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 7aa4cb397621..a095df04e724 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 @@ -251,7 +251,7 @@ public void setErrors(Document doc, List errs, List highlights) { + public void setHighlights(Document doc, Collection highlights, Map preText) { for (int[] h : highlights) { this.highlights.add(new HighlightImpl(doc, h[0], h[1], EnumSet.of(getColoringAttribute()))); } 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 7dc9318eef86..03a98b4267ec 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 HighlightsLayer[] createLayers(Context context) { 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 feae3f3628f6..065d8f80b045 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.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 @@ protected boolean process(final CompilationInfo info, final Document doc) { public void setErrors(Document doc, List errors, List allUnusedImports) {} - public void setHighlights(final Document doc, final Collection highlights) { + public void setHighlights(final Document doc, final Collection highlights, Map preText) { SwingUtilities.invokeLater(new Runnable() { public void run() { OffsetsBag bag = new OffsetsBag(doc); @@ -69,6 +71,12 @@ public void run() { bag.addHighlight(highlight[0], highlight[1], ColoringManager.getColoringImpl(unused)); } getImportHighlightsBag(doc).setHighlights(bag); + + OffsetsBag preTextBag = new OffsetsBag(doc); + for (Entry 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 @@ static OffsetsBag getImportHighlightsBag(Document doc) { 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; + } + } From ef3564acb7880a7496c8972de87cebc6bb32ad0a Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 10 Apr 2019 21:26:26 +0200 Subject: [PATCH 02/11] Improving appearance of the inline hints. --- .../modules/editor/lib2/view/DocumentViewOp.java | 6 ++++++ .../lib2/view/PrependedTextHighlightsView.java | 15 +++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) 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 a226a303e8eb..eba7f837ca41 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 fontInfos = new HashMap(4); private Font defaultFont; + private Font defaultHintFont; private boolean fontRenderContextFromPaint; @@ -1060,6 +1061,7 @@ private void updateCharMetrics() { // Update default row height and other params 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; @@ -1259,6 +1261,10 @@ public Font getDefaultFont() { 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/PrependedTextHighlightsView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java index d156c3eed14b..06b94484debb 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java @@ -39,7 +39,7 @@ public class PrependedTextHighlightsView extends HighlightsView { private TextLayout prependedTextLayout; private double leftShift; private double prependedTextWidth; - private double moveup; + private double heighCorrection; public PrependedTextHighlightsView(int length, AttributeSet attributes) { super(length, attributes); @@ -47,17 +47,13 @@ public PrependedTextHighlightsView(int length, AttributeSet attributes) { @Override void setTextLayout(TextLayout textLayout, float width) { - Font font = ViewUtils.getFont(getAttributes(), getDocumentView().op.getDefaultFont()); - font = font.deriveFont((float) (font.getSize2D() * 0.75)); + DocumentViewOp op = getDocumentView().op; + Font font = ViewUtils.getFont(getAttributes(), op.getDefaultHintFont()); prependedTextLayout = getDocumentView().op.createTextLayout((String) getAttributes().getAttribute("virtual-text-prepend"), font); Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! - double em = getDocumentView().op.createTextLayout("m", font).getBounds().getWidth(); + double em = op.getDefaultCharWidth(); leftShift = em / 2; prependedTextWidth = textBounds.getWidth() + em; - double actualTextHeight = textLayout.getBounds().getHeight(); - moveup = (actualTextHeight - textBounds.getHeight()) / 2; -// double actualTextHeight = textLayout.getAscent(); -// moveup = (actualTextHeight - prependedTextLayout.getAscent()) / 2; super.setTextLayout(textLayout, (float) (width + prependedTextWidth)); } @@ -90,13 +86,12 @@ public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) { if (highlights.moveNext()) { AttributeSet attrs = highlights.getAttributes(); - System.err.println("attrs=" + attrs); 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() - moveup, prependedTextWidth - 2 * leftShift, span.getHeight()); + span.setRect(span.getX() + leftShift, span.getY(), prependedTextWidth - 2 * leftShift, span.getHeight()); // g.drawRoundRect((int) span.getX(), (int) span.getY(), (int) span.getWidth(), (int) span.getHeight(), 2, 2); HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView()); } From a3df085565a0c4a022b5149bd8597999b046c912 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 10 Apr 2019 21:28:30 +0200 Subject: [PATCH 03/11] Generalizing highlighting test infrastructure. --- .../nbproject/project.properties | 2 +- .../editor/base/semantic/DetectorTest.java | 13 ++ .../java/editor/base/semantic/TestBase.java | 111 ++++++++++++------ 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/java/java.editor.base/nbproject/project.properties b/java/java.editor.base/nbproject/project.properties index eb59ba01f553..8d8f77e133ab 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/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 522350944716..c3e754700dcf 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 @@ -411,6 +411,19 @@ protected boolean process(CompilationInfo info, Document doc) { }); } + 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/TestBase.java b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/TestBase.java index a095df04e724..6719ce7e10f6 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,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.util.ArrayList; @@ -95,23 +96,76 @@ protected void performTest(String fileName, final Performer performer) throws Ex } 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 OuputStreamWriter(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 +177,7 @@ protected void performTest(String fileName, final Performer performer, boolean d testBuildTo.mkdirs(); - FileObject srcRoot = FileUtil.toFileObject(testSource.getParentFile()); + FileObject srcRoot = testSourceFO.getParent(); SourceUtilsTestUtil.prepareTest(srcRoot,FileUtil.toFileObject(testBuildTo), cache); if (doCompileRecursively) { @@ -162,8 +216,7 @@ public void run(CompilationController parameter) { 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 +225,8 @@ public void run(CompilationController parameter) { } 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() { @@ -273,4 +305,11 @@ public String findMIMEType(FileObject fo) { } } + protected interface Input { + public FileObject prepare() throws Exception; + } + + protected interface Validator { + public void validate(String actual) throws Exception; + } } From a8abb2e56dbd5709b518dcc09949153b68b85f34 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sat, 13 Apr 2019 22:19:16 +0200 Subject: [PATCH 04/11] Adding option to disable/enable the inline hints. --- .../modules/editor/actions/Bundle.properties | 1 + .../editor/actions/ShowInlineHintsAction.java | 37 +++++++++++++++++++ .../lib2/highlighting/HighlightsList.java | 5 ++- .../editor/lib2/view/DocumentViewOp.java | 10 ++++- .../lib2/view/HighlightsViewFactory.java | 5 ++- .../lib2/highlighting/HighlightsListTest.java | 22 +++++------ 6 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 ide/editor.actions/src/org/netbeans/modules/editor/actions/ShowInlineHintsAction.java 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 857bfa7dbf29..17b02bc04830 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 000000000000..a97a8efeaa1b --- /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/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java index 335e13d9ae80..5152d0c4cd2a 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 @@ -118,9 +118,10 @@ public void add(HighlightItem item) { * @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); @@ -158,7 +159,7 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff // Extends beyond first highlight Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont); - Object firstPrependText = firstAttrs.getAttribute("virtual-text-prepend"); + Object firstPrependText = usePrependText ? firstAttrs.getAttribute("virtual-text-prepend") : null; int index = 1; while (true) { item = get(index); 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 eba7f837ca41..80f1b9ac14d1 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 @@ -315,6 +315,7 @@ public final class DocumentViewOp boolean asTextField; private boolean guideLinesEnable; + private boolean inlineHintsEnable; private int indentLevelSize; @@ -922,10 +923,13 @@ public void run() { // 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) { @@ -1167,6 +1171,10 @@ public boolean isGuideLinesEnable() { return guideLinesEnable && !asTextField; } + public boolean isInlineHintsEnable() { + return inlineHintsEnable; + } + public int getIndentLevelSize() { return indentLevelSize; } 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 3574df72d454..9ac388fb609a 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 @@ -216,9 +216,10 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim } } - 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 = attrs != null && attrs.getAttribute("virtual-text-prepend") != null ? new PrependedTextHighlightsView(length, attrs) : new HighlightsView(length, attrs); + HighlightsView view = attrs != null && inlineHints && attrs.getAttribute("virtual-text-prepend") != null ? new PrependedTextHighlightsView(length, attrs) : new HighlightsView(length, attrs); if (origView != null && origView.getClass() == HighlightsView.class && origView.getLength() == length) { // Reuse XXX: reuse disabled for PrependedTextHighlightsView! HighlightsView origHView = (HighlightsView) origView; TextLayout origTextLayout = origHView.getTextLayout(); 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 f73e857de0e8..6e9082171f79 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 @@ -67,7 +67,7 @@ public void testSimple() throws Exception { 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 +78,21 @@ public void testSimple() throws Exception { 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 +103,7 @@ public void testSimple() throws Exception { 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 +111,7 @@ public void testSimple() throws Exception { 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 +119,22 @@ public void testSimple() throws Exception { 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); From d8336724e233841e531d023aafe153cc3efae9c8 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 14 Apr 2019 07:37:33 +0200 Subject: [PATCH 05/11] Preventing NPE while preparing compound highlights, adding simple tests. --- .../lib2/highlighting/HighlightsList.java | 4 +- .../lib2/highlighting/HighlightsListTest.java | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) 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 5152d0c4cd2a..1e925fa5811b 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 @@ -159,13 +159,13 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff // Extends beyond first highlight Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont); - Object firstPrependText = usePrependText ? firstAttrs.getAttribute("virtual-text-prepend") : null; + Object firstPrependText = usePrependText && firstAttrs != null ? firstAttrs.getAttribute("virtual-text-prepend") : null; int index = 1; while (true) { item = get(index); AttributeSet attrs = item.getAttributes(); Font font = ViewUtils.getFont(attrs, defaultFont); - Object prependText = attrs != null ? attrs.getAttribute("virtual-text-prepend") : null; + Object prependText = usePrependText && attrs != null ? attrs.getAttribute("virtual-text-prepend") : null; if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText)) { // Stop at itemEndOffset if (index == 1) { // Just single attribute set cutStartItems(1); 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 6e9082171f79..717dfb1ac51b 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 @@ -151,6 +151,93 @@ private HighlightsList highlightsListSimple(Document doc) { 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], + "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 From b372428624f4e0a77c222e8b272e7fa276ce09ff Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 14 Apr 2019 08:02:32 +0200 Subject: [PATCH 06/11] Adding simple tests for the parameter name hints. --- .../editor/base/semantic/DetectorTest.java | 33 +++++++++++++++++++ .../editor/base/semantic/HighlightImpl.java | 19 ++++++++++- .../java/editor/base/semantic/TestBase.java | 14 +++++++- 3 files changed, 64 insertions(+), 2 deletions(-) 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 c3e754700dcf..b4bdcf0b8a3e 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,39 @@ public void testGenericBoundIsClassUse() throws Exception { 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, 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:112-0:118\n" + + "[PRIVATE, METHOD, DECLARATION], 0:142-0:150\n" + + "[PRIVATE, METHOD, UNUSED, DECLARATION], 0:194-0:198\n" + + "[PUBLIC, METHOD], 0:210-0:213\n" + + "[param1:], 0:214-0:216\n" + + "[param2:], 0:218-0:219\n" + + "[PRIVATE, METHOD], 0:221-0:229\n" + + "[param4:], 0:233-0:237\n" + + "[param5:], 0:239-0:243\n"); + } + @RandomlyFails public void testBLE91246() throws Exception { final boolean wasThrown[] = new boolean[1]; 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 98c5a3d6e235..dffd3e5ec01b 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 colorings; + private String textPrepend; public HighlightImpl(Document doc, Token token, Collection colorings) { this.doc = doc; @@ -55,6 +56,14 @@ public HighlightImpl(Document doc, int start, int end, Collection highlights = new TreeSet(new Comparator() { public int compare(HighlightImpl o1, HighlightImpl o2) { @@ -287,6 +294,11 @@ public void setHighlights(Document doc, Collection highlights, Map e : preText.entrySet()) { + this.highlights.add(new HighlightImpl(doc, e.getKey()[0], e.getKey()[1], e.getValue())); + } + } } @Override From f1a0d4cd8c9a0eb1c09a7bca807e205b1462a4b9 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 19 Apr 2019 21:35:47 +0200 Subject: [PATCH 07/11] Fixing varargs usage. --- .../semantic/SemanticHighlighterBase.java | 4 ++- .../editor/base/semantic/DetectorTest.java | 34 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) 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 9065d7ba979b..e8edfbb4107f 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 @@ -1075,8 +1075,10 @@ public Void visitLiteral(LiteralTree node, Void p) { if (invoked != null && (invoked.getKind() == ElementKind.METHOD || invoked.getKind() == ElementKind.CONSTRUCTOR)) { long start = sourcePositions.getStartPosition(info.getCompilationUnit(), node); long end = sourcePositions.getEndPosition(info.getCompilationUnit(), node); + ExecutableElement invokedMethod = (ExecutableElement) invoked; + pos = Math.min(pos, invokedMethod.getParameters().size() - 1); preText.put(new int[] {(int) start, (int) end}, - ((ExecutableElement) invoked).getParameters().get(pos).getSimpleName() + ":"); + invokedMethod.getParameters().get(pos).getSimpleName() + ":"); } } } 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 b4bdcf0b8a3e..57b6bcb3c728 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 @@ -257,13 +257,15 @@ public void testParameterNames() throws Exception { performTest("Test.java", "package test;" + "public class Test {" + - " public void api(String param1, int param2, int param3, float param4, Object param5) {" + + " 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" + @@ -274,15 +276,27 @@ public void testParameterNames() throws Exception { "[PARAMETER, DECLARATION], 0:83-0:89\n" + "[PARAMETER, DECLARATION], 0:97-0:103\n" + "[PUBLIC, CLASS], 0:105-0:111\n" + - "[PARAMETER, DECLARATION], 0:112-0:118\n" + - "[PRIVATE, METHOD, DECLARATION], 0:142-0:150\n" + - "[PRIVATE, METHOD, UNUSED, DECLARATION], 0:194-0:198\n" + - "[PUBLIC, METHOD], 0:210-0:213\n" + - "[param1:], 0:214-0:216\n" + - "[param2:], 0:218-0:219\n" + - "[PRIVATE, METHOD], 0:221-0:229\n" + - "[param4:], 0:233-0:237\n" + - "[param5:], 0:239-0:243\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:219\n" + + "[param2:], 0:221-0:222\n" + + "[PRIVATE, METHOD], 0:224-0:232\n" + + "[param4:], 0:236-0:240\n" + + "[PUBLIC, METHOD], 0:250-0:253\n" + + "[param1:], 0:254-0:256\n" + + "[param2:], 0:258-0:259\n" + + "[PRIVATE, METHOD], 0:261-0:269\n" + + "[param4:], 0:273-0:277\n" + + "[param5:], 0:279-0:283\n" + + "[PUBLIC, METHOD], 0:293-0:296\n" + + "[param1:], 0:297-0:299\n" + + "[param2:], 0:301-0:302\n" + + "[PRIVATE, METHOD], 0:304-0:312\n" + + "[param4:], 0:316-0:320\n" + + "[param5:], 0:322-0:326\n" + + "[param5:], 0:328-0:332\n"); } @RandomlyFails From 124348405550fe793399469bed43450bf48290eb Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 24 Apr 2019 07:53:31 +0200 Subject: [PATCH 08/11] Improving the view structure by creating a wrapper view over the existing highlight view. --- .../lib2/view/HighlightsViewFactory.java | 10 +- .../lib2/view/ParagraphViewChildren.java | 4 +- .../view/PrependedTextHighlightsView.java | 99 ----------- .../editor/lib2/view/PrependedTextView.java | 155 ++++++++++++++++++ 4 files changed, 164 insertions(+), 104 deletions(-) delete mode 100644 ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java create mode 100644 ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java 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 9ac388fb609a..9815b58072db 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 @@ -219,7 +219,10 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim boolean inlineHints = documentView().op.isInlineHintsEnable(); AttributeSet attrs = hList.cutSameFont(defaultFont, limitOffset, wsEndOffset, docText, inlineHints); int length = hList.startOffset() - startOffset; - HighlightsView view = attrs != null && inlineHints && attrs.getAttribute("virtual-text-prepend") != null ? new PrependedTextHighlightsView(length, attrs) : new HighlightsView(length, attrs); + EditorView view = new HighlightsView(length, attrs); + if (attrs != null && inlineHints && attrs.getAttribute("virtual-text-prepend") != null) { + view = new PrependedTextView(documentView().op, attrs, view); + } if (origView != null && origView.getClass() == HighlightsView.class && origView.getLength() == length) { // Reuse XXX: reuse disabled for PrependedTextHighlightsView! HighlightsView origHView = (HighlightsView) origView; TextLayout origTextLayout = origHView.getTextLayout(); @@ -239,8 +242,9 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim Font origFont = ViewUtils.getFont(origView.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); } } 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 d67c36a9e07c..38c2787ed78b 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 @@ void replace(ParagraphView pView, int index, int removeCount, View[] addedViews) 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/PrependedTextHighlightsView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java deleted file mode 100644 index 06b94484debb..000000000000 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextHighlightsView.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.Bias; -import org.netbeans.spi.editor.highlighting.HighlightsSequence; - -/** - * TODO - */ - -public class PrependedTextHighlightsView extends HighlightsView { - - private TextLayout prependedTextLayout; - private double leftShift; - private double prependedTextWidth; - private double heighCorrection; - - public PrependedTextHighlightsView(int length, AttributeSet attributes) { - super(length, attributes); - } - - @Override - void setTextLayout(TextLayout textLayout, float width) { - DocumentViewOp op = getDocumentView().op; - Font font = ViewUtils.getFont(getAttributes(), op.getDefaultHintFont()); - prependedTextLayout = getDocumentView().op.createTextLayout((String) getAttributes().getAttribute("virtual-text-prepend"), font); - Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! - double em = op.getDefaultCharWidth(); - leftShift = em / 2; - prependedTextWidth = textBounds.getWidth() + em; - super.setTextLayout(textLayout, (float) (width + prependedTextWidth)); - } - - //span - //view to model??? - //pain - //break - - - @Override - public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) { - Shape res = super.modelToViewChecked(offset, alloc, bias); - if (bias == Bias.Forward || offset > getStartOffset()) { //TODO: seems not to have any effect? - Rectangle2D rect = ViewUtils.shapeAsRect(res); - Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! - rect.setRect(rect.getX() + textBounds.getWidth(), rect.getY(), rect.getWidth(), rect.getHeight()); - return rect; - } - return res; - } - - @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()); - super.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()); -// g.drawRoundRect((int) span.getX(), (int) span.getY(), (int) span.getWidth(), (int) span.getHeight(), 2, 2); - HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView()); - } - -} 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 000000000000..a5c2909d6458 --- /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 class PrependedTextView extends EditorView { + + private final TextLayout prependedTextLayout; + private final double leftShift; + private final double prependedTextWidth; + private final AttributeSet attributes; + private final EditorView delegate; + + public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView delegate) { + super(null); + Font font = ViewUtils.getFont(getAttributes(), op.getDefaultHintFont()); + prependedTextLayout = op.createTextLayout((String) attributes.getAttribute("virtual-text-prepend"), font); + Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! + double em = op.getDefaultCharWidth(); + leftShift = em / 2; + prependedTextWidth = Math.ceil(textBounds.getWidth() + em); + this.attributes = attributes; + this.delegate = delegate; + } + + @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; + } + +} From 6c9556c862fdf4216364d756fa5e739915431a72 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 7 May 2019 07:09:42 +0200 Subject: [PATCH 09/11] Cleaning the views implementation. --- ide/editor.lib2/apichanges.xml | 13 +++++++++ ide/editor.lib2/nbproject/project.properties | 2 +- .../lib2/view/HighlightsViewFactory.java | 28 +++++++++++++------ .../editor/lib2/view/PrependedTextView.java | 12 ++++---- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/ide/editor.lib2/apichanges.xml b/ide/editor.lib2/apichanges.xml index 6ce4e6ad4aea..a8a1c734347d 100644 --- a/ide/editor.lib2/apichanges.xml +++ b/ide/editor.lib2/apichanges.xml @@ -83,6 +83,19 @@ is the proper place. + + Prepended text for highlights + + + + + +

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.

+
+ +
Added category to EditorActionRegistration diff --git a/ide/editor.lib2/nbproject/project.properties b/ide/editor.lib2/nbproject/project.properties index 31147b960d4c..f3f622d22629 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/view/HighlightsViewFactory.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java index 9815b58072db..c53a959359fe 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.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 EditorView createView(int startOffset, int limitOffset, boolean forcedLim } 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 EditorView createView(int startOffset, int limitOffset, boolean forcedLim 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 @@ -219,12 +221,10 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim boolean inlineHints = documentView().op.isInlineHintsEnable(); AttributeSet attrs = hList.cutSameFont(defaultFont, limitOffset, wsEndOffset, docText, inlineHints); int length = hList.startOffset() - startOffset; - EditorView view = new HighlightsView(length, attrs); - if (attrs != null && inlineHints && attrs.getAttribute("virtual-text-prepend") != null) { - view = new PrependedTextView(documentView().op, attrs, view); - } - if (origView != null && origView.getClass() == HighlightsView.class && origView.getLength() == length) { // Reuse XXX: reuse disabled for PrependedTextHighlightsView! - 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)) { @@ -239,7 +239,7 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim } } 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(); HighlightsView hv = (HighlightsView) (view instanceof PrependedTextView ? ((PrependedTextView) view).getDelegate() : view); @@ -254,6 +254,16 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim } } + private @NonNull EditorView wrapWithPrependedText(@NonNull EditorView origView, @NullAllowed AttributeSet attrs) { + boolean inlineHints = documentView().op.isInlineHintsEnable(); + + if (attrs != null && inlineHints && attrs.getAttribute("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/PrependedTextView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java index a5c2909d6458..bb5e604dbcd7 100644 --- 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 @@ -35,24 +35,24 @@ /** * TODO */ -public class PrependedTextView extends EditorView { +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; - private final AttributeSet attributes; - private final EditorView delegate; public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView delegate) { super(null); - Font font = ViewUtils.getFont(getAttributes(), op.getDefaultHintFont()); + this.attributes = attributes; + this.delegate = delegate; + Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont()); prependedTextLayout = op.createTextLayout((String) attributes.getAttribute("virtual-text-prepend"), font); Rectangle2D textBounds = prependedTextLayout.getBounds(); //TODO: allocation! double em = op.getDefaultCharWidth(); leftShift = em / 2; prependedTextWidth = Math.ceil(textBounds.getWidth() + em); - this.attributes = attributes; - this.delegate = delegate; } @Override From 591e75bf3aac92e268270f9a4c117617d6eb0d18 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 12 May 2019 06:47:06 +0200 Subject: [PATCH 10/11] Fix behavior of String literals which contain embedded escape sequences. --- ide/editor.lib2/apichanges.xml | 4 +++- .../base/semantic/SemanticHighlighterBase.java | 2 +- .../editor/base/semantic/DetectorTest.java | 18 +++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ide/editor.lib2/apichanges.xml b/ide/editor.lib2/apichanges.xml index a8a1c734347d..9d12ad2c15b5 100644 --- a/ide/editor.lib2/apichanges.xml +++ b/ide/editor.lib2/apichanges.xml @@ -92,7 +92,9 @@ is the proper place.

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.

+ 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.

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 e8edfbb4107f..58c7a053ed7f 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 @@ -1074,7 +1074,7 @@ public Void visitLiteral(LiteralTree node, Void p) { 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 = sourcePositions.getEndPosition(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}, 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 57b6bcb3c728..1f25ff4d3cbd 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 @@ -280,23 +280,23 @@ public void testParameterNames() throws Exception { "[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:219\n" + + "[param1:], 0:217-0:218\n" + "[param2:], 0:221-0:222\n" + "[PRIVATE, METHOD], 0:224-0:232\n" + - "[param4:], 0:236-0:240\n" + + "[param4:], 0:236-0:237\n" + "[PUBLIC, METHOD], 0:250-0:253\n" + - "[param1:], 0:254-0:256\n" + + "[param1:], 0:254-0:255\n" + "[param2:], 0:258-0:259\n" + "[PRIVATE, METHOD], 0:261-0:269\n" + - "[param4:], 0:273-0:277\n" + - "[param5:], 0:279-0:283\n" + + "[param4:], 0:273-0:274\n" + + "[param5:], 0:279-0:280\n" + "[PUBLIC, METHOD], 0:293-0:296\n" + - "[param1:], 0:297-0:299\n" + + "[param1:], 0:297-0:298\n" + "[param2:], 0:301-0:302\n" + "[PRIVATE, METHOD], 0:304-0:312\n" + - "[param4:], 0:316-0:320\n" + - "[param5:], 0:322-0:326\n" + - "[param5:], 0:328-0:332\n"); + "[param4:], 0:316-0:317\n" + + "[param5:], 0:322-0:323\n" + + "[param5:], 0:328-0:329\n"); } @RandomlyFails From 2247540a0c12b25a6260c891a40181eb77426f6b Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 3 Jun 2019 21:46:48 +0200 Subject: [PATCH 11/11] Resolving review comments Junichi --- ide/editor.lib2/apichanges.xml | 2 +- .../modules/editor/lib2/highlighting/HighlightsList.java | 4 ++-- .../modules/editor/lib2/view/HighlightsViewFactory.java | 2 +- .../netbeans/modules/editor/lib2/view/PrependedTextView.java | 2 +- .../src/org/netbeans/modules/editor/lib2/view/ViewUtils.java | 4 +++- .../modules/editor/lib2/highlighting/HighlightsListTest.java | 3 ++- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ide/editor.lib2/apichanges.xml b/ide/editor.lib2/apichanges.xml index 9d12ad2c15b5..03211f4ff722 100644 --- a/ide/editor.lib2/apichanges.xml +++ b/ide/editor.lib2/apichanges.xml @@ -87,7 +87,7 @@ is the proper place. Prepended text for highlights - +

If AttributeSet returned from HighlightsSequence contains key "virtual-text-prepend" 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 1e925fa5811b..a97a9598ed37 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 @@ -159,13 +159,13 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff // Extends beyond first highlight Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont); - Object firstPrependText = usePrependText && firstAttrs != null ? firstAttrs.getAttribute("virtual-text-prepend") : null; + 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); - Object prependText = usePrependText && attrs != null ? attrs.getAttribute("virtual-text-prepend") : null; + 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); 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 c53a959359fe..fce916dd5bba 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 @@ -257,7 +257,7 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim private @NonNull EditorView wrapWithPrependedText(@NonNull EditorView origView, @NullAllowed AttributeSet attrs) { boolean inlineHints = documentView().op.isInlineHintsEnable(); - if (attrs != null && inlineHints && attrs.getAttribute("virtual-text-prepend") instanceof String) { + if (attrs != null && inlineHints && attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String) { return new PrependedTextView(documentView().op, attrs, origView); } 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 index bb5e604dbcd7..472c8f2bc63f 100644 --- 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 @@ -48,7 +48,7 @@ public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView this.attributes = attributes; this.delegate = delegate; Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont()); - prependedTextLayout = op.createTextLayout((String) attributes.getAttribute("virtual-text-prepend"), font); + 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; 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 836fd9250673..bda7b0751759 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 @@ */ 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 717dfb1ac51b..86e06938767b 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.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; @@ -162,7 +163,7 @@ public void testSplitPrependText() throws Exception { AttributeSet attrs2 = AttributesUtilities.createImmutable( StyleConstants.Foreground, Color.RED, StyleConstants.FontFamily, fontNames[0], - "virtual-text-prepend", "test"); + ViewUtils.KEY_VIRTUAL_TEXT_PREPEND, "test"); AttributeSet attrs3 = AttributesUtilities.createImmutable( StyleConstants.Foreground, Color.RED, StyleConstants.FontFamily, fontNames[1]);