diff --git a/ide/editor.completion/src/org/netbeans/modules/editor/completion/Bundle.properties b/ide/editor.completion/src/org/netbeans/modules/editor/completion/Bundle.properties index 0d7507b05634..8647551ec3b6 100644 --- a/ide/editor.completion/src/org/netbeans/modules/editor/completion/Bundle.properties +++ b/ide/editor.completion/src/org/netbeans/modules/editor/completion/Bundle.properties @@ -52,5 +52,5 @@ COMPL_CANCEL_ICON_BASE=org/netbeans/modules/editor/completion/resources/open_in_ completion-show-main_menu_item=Complete &Code... documentation-show-main_menu_item=Show &Documentation -tooltip-show-main_menu_item=&Show Method Parameters +tooltip-show-main_menu_item=&Show Method Parameters and Typeinfos diff --git a/ide/editor.lib/src/org/netbeans/editor/Bundle.properties b/ide/editor.lib/src/org/netbeans/editor/Bundle.properties index 6414eb991890..36e909c60b43 100644 --- a/ide/editor.lib/src/org/netbeans/editor/Bundle.properties +++ b/ide/editor.lib/src/org/netbeans/editor/Bundle.properties @@ -151,7 +151,7 @@ documentation-show=Show Documentation Popup show-popup-menu=Show Popup Menu # Alternate name key for show-popup-menu action org.openide.actions.PopupAction=Show Popup Menu -tooltip-show=Show Method Parameters +tooltip-show=Show Method Parameters and Typeinfos run-macro=Run Macro split-line=Split Line start-macro-recording=Start Macro Recording diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/JavaTooltipTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/JavaTooltipTask.java index 81a9308ced65..c8761de6661c 100644 --- a/java/java.completion/src/org/netbeans/modules/java/completion/JavaTooltipTask.java +++ b/java/java.completion/src/org/netbeans/modules/java/completion/JavaTooltipTask.java @@ -19,31 +19,32 @@ package org.netbeans.modules.java.completion; +import com.sun.source.tree.*; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; - import javax.lang.model.element.Element; -import static javax.lang.model.element.ElementKind.*; import javax.lang.model.element.ExecutableElement; -import static javax.lang.model.element.Modifier.*; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.*; import javax.lang.model.util.Types; - -import com.sun.source.tree.*; -import com.sun.source.util.SourcePositions; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; - import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.source.*; +import org.netbeans.api.lexer.TokenSequence; import org.openide.util.NbBundle; +import static java.util.Arrays.asList; +import static javax.lang.model.element.ElementKind.*; +import static javax.lang.model.element.Modifier.*; + /** * * @author Dusan Balek @@ -85,6 +86,20 @@ public int getTooltipOffset() { @Override protected void resolve(CompilationController controller) throws IOException { + Integer varOffset = findVarAtCaret(controller); + if(varOffset != null && varOffset <= caretOffset && (varOffset + 3) >= caretOffset) { + controller.toPhase(JavaSource.Phase.RESOLVED); + Trees trees = controller.getTrees(); + TreeUtilities treeUtilities = controller.getTreeUtilities(); + TreePath caretPath = treeUtilities.pathFor(varOffset + 1); + TypeMirror typeMirror = trees.getTypeMirror(caretPath); + if (typeMirror.getKind() != TypeKind.ERROR) { + toolTipOffset = controller.getSnapshot().getOriginalOffset(varOffset); + anchorOffset = toolTipOffset; + toolTipData = asList(asList(controller.getTypeUtilities().getTypeName(typeMirror).toString())); + } + } + Env env = getCompletionEnvironment(controller, true); if (env == null) { return; @@ -226,6 +241,21 @@ public boolean accept(Element e, TypeMirror t) { } } + private Integer findVarAtCaret(CompilationController controller) { + TokenSequence ts = controller.getTokenHierarchy().tokenSequence(JavaTokenId.language()); + int offset2 = controller.getSnapshot().getEmbeddedOffset(caretOffset); + if (offset2 >= 0 && offset2 <= controller.getText().length()) { + ts.move(offset2); + if(ts.moveNext() && ts.token().id() == JavaTokenId.VAR) { + return ts.offset(); + } + if(ts.movePrevious() && ts.token().id() == JavaTokenId.VAR) { + return ts.offset(); + } + } + return null; + } + private List> getMatchingParams(CompilationInfo info, TypeMirror type, Iterable elements, String name, TypeMirror[] argTypes, Types types) { List> ret = new ArrayList<>(); TypeUtilities tu = info.getTypeUtilities(); diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds1.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds1.pass new file mode 100644 index 000000000000..e11989778f72 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds1.pass @@ -0,0 +1,5 @@ +Query offset: 901 +Anchor offset: 0 +Tooltip offset: 0 +Tooltip index: 0 + diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds2.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds2.pass new file mode 100644 index 000000000000..ed379bb0c399 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds2.pass @@ -0,0 +1,10 @@ +Query offset: 902 +Anchor offset: 902 +Tooltip offset: 902 +Tooltip index: 0 + +[ + [ + Iterator + [ +] diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds3.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds3.pass new file mode 100644 index 000000000000..75372299454d --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds3.pass @@ -0,0 +1,10 @@ +Query offset: 903 +Anchor offset: 902 +Tooltip offset: 902 +Tooltip index: 0 + +[ + [ + Iterator + [ +] diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds4.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds4.pass new file mode 100644 index 000000000000..8fd4ac73b48b --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds4.pass @@ -0,0 +1,10 @@ +Query offset: 905 +Anchor offset: 902 +Tooltip offset: 902 +Tooltip index: 0 + +[ + [ + Iterator + [ +] diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds5.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds5.pass new file mode 100644 index 000000000000..468a8396b489 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleBounds5.pass @@ -0,0 +1,5 @@ +Query offset: 906 +Anchor offset: 0 +Tooltip offset: 0 +Tooltip index: 0 + diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType1.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType1.pass new file mode 100644 index 000000000000..55a455c6bf89 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType1.pass @@ -0,0 +1,10 @@ +Query offset: 1063 +Anchor offset: 1062 +Tooltip offset: 1062 +Tooltip index: 0 + +[ + [ + String + [ +] diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType2.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType2.pass new file mode 100644 index 000000000000..8763003abb98 --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType2.pass @@ -0,0 +1,10 @@ +Query offset: 1088 +Anchor offset: 1086 +Tooltip offset: 1086 +Tooltip index: 0 + +[ + [ + X + [ +] diff --git a/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType3.pass b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType3.pass new file mode 100644 index 000000000000..f13bde09e05e --- /dev/null +++ b/java/java.completion/test/unit/data/goldenfiles/org/netbeans/modules/java/completion/JavaTooltipTaskTest/17/VarSampleType3.pass @@ -0,0 +1,10 @@ +Query offset: 1172 +Anchor offset: 1172 +Tooltip offset: 1172 +Tooltip index: 0 + +[ + [ + X1 & X2 + [ +] diff --git a/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/VarSample.java b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/VarSample.java new file mode 100644 index 000000000000..46aff8e0314a --- /dev/null +++ b/java/java.completion/test/unit/data/org/netbeans/modules/java/completion/data/VarSample.java @@ -0,0 +1,60 @@ +/* + * 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 test; + +public class Test { + + private void test(java.util.List l) { + var v1 = l.iterator(); + } + + public static class X { + + public static void main(String[] args) { + X x = new X<>(); + var a = ""; + var y = x.run1() + .run2() + .run3(); + var z = x.x("Hallo", 42); + } + + public Z x(Y y, X x) { + return null; + } + + public X run1() { + return null; + } + + public X run2() { + return null; + } + + public X run3() { + return null; + } + + interface X1 { + } + + interface X2 { + } + } +} diff --git a/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaTooltipTaskTest.java b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaTooltipTaskTest.java new file mode 100644 index 000000000000..9c2cb722219d --- /dev/null +++ b/java/java.completion/test/unit/src/org/netbeans/modules/java/completion/JavaTooltipTaskTest.java @@ -0,0 +1,125 @@ +/* + * 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.java.completion; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.Collections; +import java.util.List; +import javax.swing.text.Document; +import org.netbeans.api.java.lexer.JavaTokenId; +import org.netbeans.api.lexer.Language; +import org.netbeans.modules.parsing.api.ParserManager; +import org.netbeans.modules.parsing.api.Source; +import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; + +import static junit.framework.TestCase.assertNotNull; + +public class JavaTooltipTaskTest extends CompletionTestBaseBase { + + public JavaTooltipTaskTest(String testName) { + super(testName, "org/netbeans/modules/java/completion/JavaTooltipTaskTest"); + } + + public void testVarBounds() throws Exception { + assertResult("17", "VarSample", "VarSampleBounds1.pass", 901); + assertResult("17", "VarSample", "VarSampleBounds2.pass", 902); + assertResult("17", "VarSample", "VarSampleBounds3.pass", 903); + assertResult("17", "VarSample", "VarSampleBounds4.pass", 905); + assertResult("17", "VarSample", "VarSampleBounds5.pass", 906); + } + + public void testVarTypes() throws Exception { + assertResult("17", "VarSample", "VarSampleType1.pass", 1063); + assertResult("17", "VarSample", "VarSampleType2.pass", 1088); + assertResult("17", "VarSample", "VarSampleType3.pass", 1172); + } + + private void assertResult(String sourceLevel, String source, String goldenFileName, int pos) throws Exception { + this.sourceLevel.set(sourceLevel); + File testSource = new File(getWorkDir(), "test/Test.java"); + testSource.getParentFile().mkdirs(); + copyToWorkDir(new File(getDataDir(), "org/netbeans/modules/java/completion/data/" + source + ".java"), testSource); + FileObject testSourceFO = FileUtil.toFileObject(testSource); + assertNotNull(testSourceFO); + DataObject testSourceDO = DataObject.find(testSourceFO); + assertNotNull(testSourceDO); + EditorCookie ec = (EditorCookie) testSourceDO.getCookie(EditorCookie.class); + assertNotNull(ec); + final Document doc = ec.openDocument(); + assertNotNull(doc); + doc.putProperty(Language.class, JavaTokenId.language()); + doc.putProperty("mimeType", "text/x-java"); + Source s = Source.create(doc); + JavaTooltipTask task = JavaTooltipTask.create(pos, () -> false); + ParserManager.parse(Collections.singletonList(s), task); + + String reference = getGoldFileContents(goldenFileName); + StringWriter result = new StringWriter(); + result.write("Query offset: "); + result.write(String.valueOf(pos)); + result.write("\n"); + result.write("Anchor offset: "); + result.write(String.valueOf(task.getAnchorOffset())); + result.write("\n"); + result.write("Tooltip offset: "); + result.write(String.valueOf(task.getTooltipOffset())); + result.write("\n"); + result.write("Tooltip index: "); + result.write(String.valueOf(task.getTooltipIndex())); + result.write("\n\n"); + + if (task.getTooltipData() != null) { + result.write("[\n"); + for (List element : task.getTooltipData()) { + result.write("\t[\n"); + for (String type : element) { + result.write("\t\t"); + result.write(type); + result.write("\n"); + } + result.write("\t[\n"); + } + result.write("]\n"); + } + + assertEquals(reference, result.toString()); + } + + @SuppressWarnings("NestedAssignment") + private String getGoldFileContents(String goldenFileName) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + File goldenFile = getGoldenFile(goldenFileName); + try(InputStream is = new FileInputStream(goldenFile)) { + int read; + byte[] buffer = new byte[1024]; + while((read = is.read(buffer)) > 0) { + baos.write(buffer, 0, read); + } + } + return baos.toString("UTF-8"); + } +}