Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2017-05-22 10:54:53 +03:00
17 changed files with 170 additions and 41 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# The file might be automatically updated. Comments and empty lines will be removed.
kotlinPluginBuild=1.1.2-release-IJ2017.2-1
jetSignBuild=42.30
jdkBuild=u152b855.1
jdkBuild=u152b867.1
@@ -44,6 +44,7 @@ import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.javadoc.*;
import com.intellij.psi.search.EverythingGlobalScope;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
@@ -1343,6 +1344,26 @@ public class JavaDocInfoGenerator {
buffer.append("</DD></DL></DD>");
}
}
else {
PsiField field = PropertyUtil.getFieldOfGetter(method);
if (field == null) {
field = PropertyUtil.getFieldOfSetter(method);
}
if (field != null) {
PsiDocComment fieldDocComment = field.getDocComment();
if (fieldDocComment != null && !isEmptyDescription(fieldDocComment)) {
buffer.append("<DD><DL>");
buffer.append("<DT><b>");
buffer.append(CodeInsightBundle.message("javadoc.description.copied.from.field"));
buffer.append("</b>&nbsp;");
generateLink(buffer, field, field.getName(), false);
buffer.append(BR_TAG);
generateValue(buffer, fieldDocComment.getDescriptionElements(), ourEmptyElementsProvider);
buffer.append("</DD></DL></DD>");
}
}
}
}
private void generateValue(StringBuilder buffer, PsiElement[] elements, InheritDocProvider<PsiElement[]> provider) {
@@ -20,6 +20,7 @@ import com.intellij.find.FindBundle;
import com.intellij.ide.util.SuperMethodWarningUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.TransactionGuard;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Messages;
@@ -30,6 +31,7 @@ import com.intellij.psi.impl.search.ThrowSearchUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.PropertyUtil;
@@ -119,6 +121,19 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
elementsToSearch.add(parameters[idx]);
}
}
final PsiClass aClass = ReadAction.compute(method::getContainingClass);
if (aClass != null) {
FunctionalExpressionSearch.search(aClass).forEach(element -> {
if (element instanceof PsiLambdaExpression) {
PsiParameter[] parameters = ((PsiLambdaExpression)element).getParameterList().getParameters();
if (idx < parameters.length) {
elementsToSearch.add(parameters[idx]);
}
}
});
}
return PsiUtilCore.toPsiElementArray(elementsToSearch);
}
@@ -137,6 +152,9 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{
LOG.assertTrue(aClass != null); //Otherwise can not be overriden
boolean hasOverridden = OverridingMethodsSearch.search(method).findFirst() != null;
if (!hasOverridden) {
hasOverridden = FunctionalExpressionSearch.search(aClass).findFirst() != null;
}
if (hasOverridden && askWhetherShouldSearchForParameterInOverridingMethods(element, parameter)) {
return getParameterElementsToSearch(parameter, method);
}
@@ -0,0 +1,3 @@
<html><head><base href="placeholder"> <style type="text/css"> #error { background-color: #eeeeee; margin-bottom: 10px; } p { margin: 5px 0; } </style></head><body><small><b><a href="psi_element://FooBar"><code>FooBar</code></a></b></small><PRE>public&nbsp;<a href="psi_element://java.lang.String"><code>String</code></a>&nbsp;<b>getMyFoo</b>()</PRE><DD><DL><DT><b>Description copied from field:</b>&nbsp;<a href="psi_element://FooBar#myFoo"><code>myFoo</code></a><br>
foo bar baz
</DD></DL></DD></body></html>
@@ -0,0 +1,10 @@
class FooBar {
/**
* foo bar baz
*/
private String myFoo;
public String get<caret>MyFoo() {
return myFoo;
}
}
@@ -405,6 +405,10 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase {
doTestAtCaret();
}
public void testDocumentationForGetterByField() throws Exception {
doTestAtCaret();
}
public void testDumbMode() throws Exception {
DumbServiceImpl.getInstance(myProject).setDumb(true);
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -130,7 +130,7 @@ public class PathEnvironmentVariableUtil {
return Collections.emptyList();
}
List<File> result = new SmartList<>();
List<String> dirPaths = StringUtil.split(pathEnvVarValue, File.pathSeparator, true, true);
List<String> dirPaths = getPathDirs(pathEnvVarValue);
for (String dirPath : dirPaths) {
File dir = new File(dirPath);
if (dir.isAbsolute() && dir.isDirectory()) {
@@ -148,6 +148,11 @@ public class PathEnvironmentVariableUtil {
return result;
}
@NotNull
public static List<String> getPathDirs(@NotNull String pathEnvVarValue) {
return StringUtil.split(pathEnvVarValue, File.pathSeparator, true, true);
}
/**
* Alters the passed in exe path to increase probability of exe file success finding when
* spawning an external process. Modifications are performed iff the passed in exe path is
@@ -22,7 +22,7 @@ import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.event.ActionListener;
public class FieldPanel extends AbstractFieldPanel {
public class FieldPanel extends AbstractFieldPanel implements TextAccessor {
private final JTextField myTextField;
public FieldPanel() {
@@ -77,6 +77,7 @@ public class FileSystemTreeImpl implements FileSystemTree {
private final Project myProject;
private final ArrayList<Runnable> myOkActions = new ArrayList<>(2);
private final FileChooserDescriptor myDescriptor;
private final FileTreeModel myFileTreeModel;
private final AsyncTreeModel myAsyncTreeModel;
private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
@@ -99,10 +100,12 @@ public class FileSystemTreeImpl implements FileSystemTree {
myProject = project;
if (renderer == null && Registry.is("file.chooser.async.tree.model")) {
renderer = new FileRenderer().forTree();
myAsyncTreeModel = new AsyncTreeModel(new FileTreeModel(descriptor, new FileRefresher(true, 3)));
myFileTreeModel = new FileTreeModel(descriptor, new FileRefresher(true, 3));
myAsyncTreeModel = new AsyncTreeModel(myFileTreeModel);
myTreeStructure = null;
}
else {
myFileTreeModel = null;
myAsyncTreeModel = null;
myTreeStructure = new FileTreeStructure(project, descriptor);
}
@@ -244,7 +247,7 @@ public class FileSystemTreeImpl implements FileSystemTree {
public void showHiddens(boolean showHidden) {
if (myAsyncTreeModel != null) {
myDescriptor.withShowHiddenFiles(showHidden);
//TODO:refresh
if (myFileTreeModel != null) myFileTreeModel.invalidate();
}
else {
myTreeStructure.showHiddens(showHidden);
@@ -79,6 +79,17 @@ public final class FileTreeModel extends AbstractTreeModel implements Disposable
});
}
public void invalidate() {
invoker.invokeLaterIfNeeded(() -> {
if (roots != null) {
for (Root root : roots) {
root.tree.invalidate();
}
}
treeStructureChanged(state.path, null, null);
});
}
@Override
public void dispose() {
}
@@ -359,9 +359,7 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
LOG.debug("ignore updating of changed node: ", object);
return;
}
boolean isLoadingRequired = entry.isLoadingRequired();
MapBasedTree.UpdateResult<Object> update = tree.update(entry, children);
if (isLoadingRequired) return;
boolean removed = !update.getRemoved().isEmpty();
boolean inserted = !update.getInserted().isEmpty();
@@ -380,7 +378,11 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
return;
}
}
treeStructureChanged(entry, null, null);
if (!listeners.isEmpty()) {
if (removed) listeners.treeNodesRemoved(update.getEvent(AsyncTreeModel.this, entry, update.getRemoved()));
if (inserted) listeners.treeNodesInserted(update.getEvent(AsyncTreeModel.this, entry, update.getInserted()));
if (contained) listeners.treeNodesChanged(update.getEvent(AsyncTreeModel.this, entry, update.getContained()));
}
for (Entry<Object> entry : update.getContained()) {
if (!entry.isLoadingRequired()) {
loadChildren(entry, false);
@@ -57,6 +57,11 @@ public final class MapBasedTree<K, N> {
this.path = path;
}
public void invalidate() {
if (root != null) root.invalidate();
map.values().forEach(entry -> entry.invalidate());
}
public void onRemove(@NotNull Consumer<N> consumer) {
Consumer<N> old = nodeRemoved;
nodeRemoved = old == null ? consumer : old.andThen(consumer);
@@ -146,6 +151,7 @@ public final class MapBasedTree<K, N> {
}
parent.leaf = children == null;
parent.children = guard(newChildren);
parent.valid = true;
List<Entry<N>> removed = oldChildren;
List<Entry<N>> inserted = newChildren;
@@ -218,6 +224,7 @@ public final class MapBasedTree<K, N> {
private volatile boolean leaf;
private volatile List<Entry<N>> children;
private volatile N loading;
private volatile boolean valid;
private Entry(TreePath path, N parent, N node, Boolean leaf) {
super(path, node);
@@ -225,6 +232,11 @@ public final class MapBasedTree<K, N> {
this.parent = parent;
this.leaf = Boolean.TRUE.equals(leaf);
if (this.leaf) children = emptyList();
invalidate();
}
public void invalidate() {
valid = leaf;
}
public N getNode() {
@@ -240,7 +252,7 @@ public final class MapBasedTree<K, N> {
}
public boolean isLoadingRequired() {
return children == null;
return !valid || children == null;
}
public int getChildCount() {
@@ -272,6 +284,7 @@ public final class MapBasedTree<K, N> {
if (children != null) LOG.warn("MapBasedTree: rewrite loaded nodes");
this.loading = loading;
children = loading == null ? emptyList() : singletonList(new Entry<>(this, node, loading, true));
valid = true;
}
}
@@ -275,6 +275,7 @@ templates.postfix.settings.category.before=The sample code featuring selected te
templates.postfix.settings.category.after=Postfix completion invocation result will be shown here.
javadoc.description.copied.from.interface=Description copied from interface:
javadoc.description.copied.from.class=Description copied from class:
javadoc.description.copied.from.field=Description copied from field:
javadoc.deprecated=Deprecated
javadoc.since=Since:
javadoc.see.also=See Also:
@@ -30,7 +30,6 @@ import com.intellij.util.io.URLUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -49,19 +48,29 @@ public class FileUrlProvider implements SMTestLocator, DumbAware {
return Collections.emptyList();
}
final String normalizedPath = path.replace(File.separatorChar, '/');
final int lineNoSeparatorIndex = normalizedPath.lastIndexOf(':');
final String filePath;
final int lineNumber;
// if line is specified
if (lineNoSeparatorIndex > 3) { // on Windows, paths start with /C: and that colon is not a line number separator
lineNumber = StringUtil.parseInt(normalizedPath.substring(lineNoSeparatorIndex + 1), -1);
filePath = normalizedPath.substring(0, lineNoSeparatorIndex);
final int columnNumber;
int lastColonIndex = path.lastIndexOf(':');
if (lastColonIndex > 3) { // on Windows, paths start with /C: and that colon is not a line number separator
int lastValue = StringUtil.parseInt(path.substring(lastColonIndex + 1), -1);
int penultimateColonIndex = path.lastIndexOf(':', lastColonIndex - 1);
if (penultimateColonIndex > 3) {
int penultimateValue = StringUtil.parseInt(path.substring(penultimateColonIndex + 1, lastColonIndex), -1);
filePath = path.substring(0, penultimateColonIndex);
lineNumber = penultimateValue;
columnNumber = lineNumber <= 0 ? -1 : lastValue;
}
else {
filePath = path.substring(0, lastColonIndex);
lineNumber = lastValue;
columnNumber = -1;
}
} else {
filePath = path;
lineNumber = -1;
filePath = normalizedPath;
columnNumber = -1;
}
// Now we should search file with most suitable path
// here path may be absolute or relative
@@ -73,20 +82,27 @@ public class FileUrlProvider implements SMTestLocator, DumbAware {
final List<Location> locations = new ArrayList<>(2);
for (VirtualFile file : virtualFiles) {
locations.add(createLocationFor(project, file, lineNumber));
locations.add(createLocationFor(project, file, lineNumber, columnNumber));
}
return locations;
}
@Nullable
public static Location createLocationFor(@NotNull Project project, @NotNull VirtualFile virtualFile, int lineNum) {
return createLocationFor(project, virtualFile, lineNum, -1);
}
/**
* @param project Project instance
* @param virtualFile VirtualFile instance to locate
* @param lineNum one-based line number to locate inside {@code virtualFile},
* a non-positive line number doesn't change text caret position inside the file
* @param columnNum one-based column number to locate inside {@code virtualFile},
* a non-positive column number doesn't change text caret position inside the file
* @return Location instance, or null if not found
*/
@Nullable
public static Location createLocationFor(@NotNull Project project, @NotNull VirtualFile virtualFile, int lineNum) {
public static Location createLocationFor(@NotNull Project project, @NotNull VirtualFile virtualFile, int lineNum, int columnNum) {
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
if (psiFile == null) {
return null;
@@ -100,19 +116,14 @@ public class FileUrlProvider implements SMTestLocator, DumbAware {
return null;
}
final int lineCount = doc.getLineCount();
final int lineStartOffset;
final int endOffset;
if (lineNum <= lineCount) {
lineStartOffset = doc.getLineStartOffset(lineNum - 1);
endOffset = doc.getLineEndOffset(lineNum - 1);
} else {
// unknown line
lineStartOffset = 0;
endOffset = doc.getTextLength();
if (lineNum > doc.getLineCount()) {
return PsiLocation.fromPsiElement(psiFile);
}
final int lineStartOffset = doc.getLineStartOffset(lineNum - 1);
final int endOffset = doc.getLineEndOffset(lineNum - 1);
int offset = lineStartOffset;
int offset = Math.min(lineStartOffset + Math.max(columnNum - 1, 0), endOffset);
PsiElement elementAtLine = null;
while (offset <= endOffset) {
elementAtLine = psiFile.findElementAt(offset);
@@ -31,16 +31,21 @@ public class FileUrlLocationTest extends LightPlatformCodeInsightFixtureTestCase
"my_example_spec.xml",
"\n" +
"<describe>\n" +
" <a></a>\n" +
" <a id='1'></a>\n" +
"</describe>\n" +
"\n").getVirtualFile();
doTest(1, file.getPath(), 2);
doTest(16, file.getPath(), 3);
doTest(1, file.getPath(), 2, -1);
doTest(16, file.getPath(), 3, -1);
doTest(2, file.getPath(), 2, 5);
doTest(19, file.getPath(), 3, 8);
doTest(0, file.getPath(), 100, -1);
doTest(11, file.getPath(), 2, 100);
}
private void doTest(int expectedOffset, String filePath, int lineNum) {
SMTestProxy testProxy = new SMTestProxy("myTest", false, "file://" + filePath + ":" + lineNum);
private void doTest(int expectedOffset, String filePath, int lineNum, int columnNumber) {
SMTestProxy testProxy = new SMTestProxy("myTest", false, "file://" + filePath + ":" + lineNum
+ (columnNumber > 0 ? (":" + columnNumber) : ""));
testProxy.setLocator(FileUrlProvider.INSTANCE);
Location location = testProxy.getLocation(getProject(), GlobalSearchScope.allScope(getProject()));
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WindowsRegistryUtil {
private WindowsRegistryUtil() {
@@ -69,7 +71,7 @@ public class WindowsRegistryUtil {
@NotNull
public static List<String> readRegistryBranch(@NotNull String location) {
List<String> result = new ArrayList<String>();
StringBuilder output = readRegistry("reg query \"" + location + "\"");
StringBuilder output = doReadBranch(location);
if (output != null) {
for (int pos = output.indexOf(location); pos != -1; pos = output.indexOf(location, pos + location.length())) {
int pos2 = output.indexOf("\r\n", pos + location.length());
@@ -84,6 +86,26 @@ public class WindowsRegistryUtil {
}
return result;
}
@NotNull
public static List<String> readRegistryBranchValues(@NotNull String location) {
List<String> result = new ArrayList<String>();
StringBuilder output = doReadBranch(location);
if (output != null) {
// there seem to be no way to get machine-readable list of value names.
// so we are trying to make pattern as precise as possible.
Pattern pattern = Pattern.compile("^\\s{4}(.+)\\s{4}REG_\\w+\\s{4}.+$", Pattern.MULTILINE);
Matcher m = pattern.matcher(output);
while(m.find()) {
result.add(m.group(1));
}
}
return result;
}
private static StringBuilder doReadBranch(@NotNull String location) {
return readRegistry("reg query \"" + location + "\"");
}
@Nullable
public static String readRegistryDefault(@NotNull String location) {
@@ -71,7 +71,7 @@ statement.with.empty.body.problem.descriptor=<code>#ref</code> statement has emp
statement.with.empty.body.include.option=Include statement bodies that are empty code blocks
equals.between.inconvertible.types.display.name='equals()' between objects of inconvertible types
equals.between.inconvertible.types.problem.descriptor=<code>#ref()</code> between objects of inconvertible types ''{0}'' and ''{1}'' #loc
equals.called.on.suspicious.object.display.name='equals' called on suspicious object
equals.called.on.suspicious.object.display.name='equals()' called on suspicious object
equals.called.on.suspicious.object.problem.descriptor=Suspicious call ''equals'' on ''{0}'' object
non.final.field.in.equals.display.name=Non-final field referenced in 'equals()'
non.final.field.in.equals.problem.descriptor=Non-final field <code>#ref</code> accessed in 'equals()' #loc