Skip to content

Commit

Permalink
Implement var type information display as tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasblaesing committed Dec 8, 2022
1 parent bb3bdff commit 60fb7d5
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 1 addition & 1 deletion ide/editor.lib/src/org/netbeans/editor/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -226,6 +241,21 @@ public boolean accept(Element e, TypeMirror t) {
}
}

private Integer findVarAtCaret(CompilationController controller) {
TokenSequence<JavaTokenId> 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<List<String>> getMatchingParams(CompilationInfo info, TypeMirror type, Iterable<? extends Element> elements, String name, TypeMirror[] argTypes, Types types) {
List<List<String>> ret = new ArrayList<>();
TypeUtilities tu = info.getTypeUtilities();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Query offset: 901
Anchor offset: 0
Tooltip offset: 0
Tooltip index: 0

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 902
Anchor offset: 902
Tooltip offset: 902
Tooltip index: 0

[
[
Iterator<String>
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 903
Anchor offset: 902
Tooltip offset: 902
Tooltip index: 0

[
[
Iterator<String>
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 905
Anchor offset: 902
Tooltip offset: 902
Tooltip index: 0

[
[
Iterator<String>
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Query offset: 906
Anchor offset: 0
Tooltip offset: 0
Tooltip index: 0

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 1063
Anchor offset: 1062
Tooltip offset: 1062
Tooltip index: 0

[
[
String
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 1088
Anchor offset: 1086
Tooltip offset: 1086
Tooltip index: 0

[
[
X<Double>
[
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Query offset: 1172
Anchor offset: 1172
Tooltip offset: 1172
Tooltip index: 0

[
[
X1 & X2
[
]
Original file line number Diff line number Diff line change
@@ -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<String> l) {
var v1 = l.iterator();
}

public static class X<T> {

public static void main(String[] args) {
X<String> x = new X<>();
var a = "";
var y = x.run1()
.run2()
.run3();
var z = x.x("Hallo", 42);
}

public <X, Y, Z extends X1 & X2> Z x(Y y, X x) {
return null;
}

public X<Integer> run1() {
return null;
}

public X<String> run2() {
return null;
}

public X<Double> run3() {
return null;
}

interface X1 {
}

interface X2 {
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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");
}
}

0 comments on commit 60fb7d5

Please sign in to comment.