mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Report language of current file for mouse invocations of intentions popup (IDEA-CR-46747)
GitOrigin-RevId: 65667ea92fac4bef9b9a3b620b2cae0585171b4b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
846eeb2d37
commit
bfa87abd68
@@ -11,6 +11,7 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.packaging.artifacts.Artifact;
|
||||
import com.intellij.packaging.elements.PackagingElementFactory;
|
||||
import com.intellij.util.PathUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -79,7 +80,7 @@ public abstract class ArtifactEditorTestCase extends PackagingElementsTestCase {
|
||||
final Ref<Boolean> dialogShown = Ref.create(false);
|
||||
final TestDialog oldDialog = Messages.setTestDialog(new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
public int show(@NotNull String message) {
|
||||
dialogShown.set(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+97
-32
@@ -2,6 +2,7 @@
|
||||
package com.intellij.debugger.actions;
|
||||
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.engine.DebuggerUtils;
|
||||
import com.intellij.debugger.engine.SuspendContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
@@ -26,7 +27,10 @@ import com.intellij.psi.*;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.Range;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.Method;
|
||||
import com.sun.jdi.ReferenceType;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -338,42 +342,67 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
if (!targets.isEmpty()) {
|
||||
StackFrameProxyImpl frameProxy = suspendContext != null ? suspendContext.getFrameProxy() : null;
|
||||
if (frameProxy != null) {
|
||||
// sanity check
|
||||
try {
|
||||
Location location = frameProxy.location();
|
||||
MethodBytecodeUtil.visit(location.method(), location.codeIndex(), new MethodVisitor(Opcodes.API_VERSION) {
|
||||
boolean myLineMatch = false;
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
myLineMatch = lines.isWithin(line - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (myLineMatch) {
|
||||
Iterator<SmartStepTarget> iterator = targets.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SmartStepTarget e = iterator.next();
|
||||
if (e instanceof MethodSmartStepTarget &&
|
||||
DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)e).getMethod(),
|
||||
owner.replace("/", "."), name, desc,
|
||||
suspendContext.getDebugProcess())) {
|
||||
iterator.remove();
|
||||
MethodSmartStepTarget target = (MethodSmartStepTarget)e;
|
||||
// fix ordinals
|
||||
existingMethodCalls(targets, target.getMethod())
|
||||
.forEach(t -> {
|
||||
int ordinal = t.getOrdinal();
|
||||
if (ordinal > target.getOrdinal()) {
|
||||
t.setOrdinal(ordinal - 1);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
List<MethodSmartStepTarget> methodTargets =
|
||||
StreamEx.of(targets)
|
||||
.select(MethodSmartStepTarget.class)
|
||||
.filter(target -> !target.needsBreakpointRequest())
|
||||
.toList();
|
||||
visitLinesMethods(frameProxy.location(), true, lines, (opcode, owner, name, desc, itf) -> {
|
||||
if (name.startsWith("access$")) { // bridge method
|
||||
ReferenceType cls = ContainerUtil.getFirstItem(frameProxy.getVirtualMachine().classesByName(owner));
|
||||
if (cls != null) {
|
||||
Method method = DebuggerUtils.findMethod(cls, name, desc);
|
||||
if (method != null) {
|
||||
MethodBytecodeUtil.visit(method, new MethodVisitor(Opcodes.API_VERSION) {
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if ("java/lang/AbstractMethodError".equals(owner)) {
|
||||
return;
|
||||
}
|
||||
removeMatchingMethod(methodTargets, owner, name, desc, suspendContext.getDebugProcess());
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
else {
|
||||
removeMatchingMethod(methodTargets, owner, name, desc, suspendContext.getDebugProcess());
|
||||
}
|
||||
});
|
||||
if (!methodTargets.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
// remove already executed
|
||||
try {
|
||||
visitLinesMethods(frameProxy.location(), false, lines, (opcode, owner, name, desc, itf) -> {
|
||||
Iterator<SmartStepTarget> iterator = targets.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SmartStepTarget e = iterator.next();
|
||||
if (e instanceof MethodSmartStepTarget &&
|
||||
DebuggerUtilsEx.methodMatches(((MethodSmartStepTarget)e).getMethod(),
|
||||
owner.replace("/", "."), name, desc,
|
||||
suspendContext.getDebugProcess())) {
|
||||
iterator.remove();
|
||||
MethodSmartStepTarget target = (MethodSmartStepTarget)e;
|
||||
// fix ordinals
|
||||
existingMethodCalls(targets, target.getMethod())
|
||||
.forEach(t -> {
|
||||
int ordinal = t.getOrdinal();
|
||||
if (ordinal > target.getOrdinal()) {
|
||||
t.setOrdinal(ordinal - 1);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info(e);
|
||||
@@ -386,6 +415,42 @@ public class JavaSmartStepIntoHandler extends JvmSmartStepIntoHandler {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static void removeMatchingMethod(List<MethodSmartStepTarget> targets,
|
||||
String owner,
|
||||
String name,
|
||||
String desc,
|
||||
DebugProcessImpl process) {
|
||||
Iterator<MethodSmartStepTarget> iterator = targets.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (DebuggerUtilsEx.methodMatches(iterator.next().getMethod(), owner.replace("/", "."), name, desc, process)) {
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface MethodInsnVisitor {
|
||||
void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf);
|
||||
}
|
||||
|
||||
private static void visitLinesMethods(Location location, boolean full, Range<Integer> lines, MethodInsnVisitor visitor) {
|
||||
MethodBytecodeUtil.visit(location.method(), full ? Long.MAX_VALUE : location.codeIndex(), new MethodVisitor(Opcodes.API_VERSION) {
|
||||
boolean myLineMatch = false;
|
||||
|
||||
@Override
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
myLineMatch = lines.isWithin(line - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (myLineMatch) {
|
||||
visitor.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
private static StreamEx<MethodSmartStepTarget> existingMethodCalls(List<SmartStepTarget> targets, PsiMethod psiMethod) {
|
||||
return StreamEx.of(targets)
|
||||
.select(MethodSmartStepTarget.class)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class TestFilesIndex extends MapReduceIndex<Integer, Void, UsedSources> {
|
||||
@Nullable
|
||||
Collection<Integer> getTestDataFor(int testId) throws IOException {
|
||||
ForwardIndex forwardIndex = getForwardIndexMap();
|
||||
KeyCollectionForwardIndexAccessor<Integer, Void> forwardIndexAccessor = (KeyCollectionForwardIndexAccessor<Integer, Void>)getForwardIndexAccessor();
|
||||
KeyCollectionForwardIndexAccessor<Integer, Void, UsedSources> forwardIndexAccessor = (KeyCollectionForwardIndexAccessor<Integer, Void, UsedSources>)getForwardIndexAccessor();
|
||||
return forwardIndexAccessor.deserializeData(forwardIndex.get(testId));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.popup.list.GroupedItemsListRenderer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
@@ -55,7 +55,7 @@ public class ProjectTemplateList extends JPanel {
|
||||
add(myPanel, BorderLayout.CENTER);
|
||||
|
||||
GroupedItemsListRenderer<ProjectTemplate> renderer = new GroupedItemsListRenderer<ProjectTemplate>(new ListItemDescriptorAdapter<ProjectTemplate>() {
|
||||
@Nullable
|
||||
@NotNull
|
||||
@Override
|
||||
public String getTextFor(ProjectTemplate value) {
|
||||
return value.getName();
|
||||
@@ -80,12 +80,7 @@ public class ProjectTemplateList extends JPanel {
|
||||
}
|
||||
};
|
||||
myList.setCellRenderer(renderer);
|
||||
myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
updateSelection();
|
||||
}
|
||||
});
|
||||
myList.getSelectionModel().addListSelectionListener(__ -> updateSelection());
|
||||
|
||||
Messages.installHyperlinkSupport(myDescriptionPane);
|
||||
}
|
||||
@@ -104,7 +99,7 @@ public class ProjectTemplateList extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
public void setTemplates(List<ProjectTemplate> list, boolean preserveSelection) {
|
||||
public void setTemplates(List<? extends ProjectTemplate> list, boolean preserveSelection) {
|
||||
Collections.sort(list, (o1, o2) -> Comparing.compare(o1 instanceof ArchivedProjectTemplate, o2 instanceof ArchivedProjectTemplate));
|
||||
|
||||
int index = preserveSelection ? myList.getSelectedIndex() : -1;
|
||||
@@ -142,13 +137,10 @@ public class ProjectTemplateList extends JPanel {
|
||||
myList.setSelectedValue(template, true);
|
||||
}
|
||||
}
|
||||
myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
ProjectTemplate template = getSelectedTemplate();
|
||||
if (template != null) {
|
||||
PropertiesComponent.getInstance().setValue(PROJECT_WIZARD_TEMPLATE, template.getName());
|
||||
}
|
||||
myList.getSelectionModel().addListSelectionListener(__ -> {
|
||||
ProjectTemplate template = getSelectedTemplate();
|
||||
if (template != null) {
|
||||
PropertiesComponent.getInstance().setValue(PROJECT_WIZARD_TEMPLATE, template.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -166,7 +158,7 @@ public class ProjectTemplateList extends JPanel {
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public boolean setSelectedTemplate(String name) {
|
||||
boolean setSelectedTemplate(@NotNull String name) {
|
||||
ListModel model1 = myList.getModel();
|
||||
for (int j = 0; j < model1.getSize(); j++) {
|
||||
if (name.equals(((ProjectTemplate)model1.getElementAt(j)).getName())) {
|
||||
|
||||
@@ -54,8 +54,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
@@ -70,9 +68,9 @@ import java.util.*;
|
||||
public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, Disposable {
|
||||
private static final Logger LOG = Logger.getInstance(ProjectTypeStep.class);
|
||||
|
||||
public static final Convertor<FrameworkSupportInModuleProvider,String> PROVIDER_STRING_CONVERTOR =
|
||||
private static final Convertor<FrameworkSupportInModuleProvider,String> PROVIDER_STRING_CONVERTOR =
|
||||
o -> o.getId();
|
||||
public static final Function<FrameworkSupportNode, String> NODE_STRING_FUNCTION = FrameworkSupportNodeBase::getId;
|
||||
private static final Function<FrameworkSupportNode, String> NODE_STRING_FUNCTION = FrameworkSupportNodeBase::getId;
|
||||
private static final String TEMPLATES_CARD = "templates card";
|
||||
private static final String FRAMEWORKS_CARD = "frameworks card";
|
||||
private static final String PROJECT_WIZARD_GROUP = "project.wizard.group";
|
||||
@@ -106,12 +104,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
|
||||
myProjectTypeList.setModel(new CollectionListModel<>(groups));
|
||||
myProjectTypeList.setSelectionModel(new SingleSelectionModel());
|
||||
myProjectTypeList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
updateSelection();
|
||||
}
|
||||
});
|
||||
myProjectTypeList.addListSelectionListener(__ -> updateSelection());
|
||||
myProjectTypeList.setCellRenderer(new GroupedItemsListRenderer<TemplatesGroup>(new ListItemDescriptorAdapter<TemplatesGroup>() {
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -187,19 +180,9 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
}
|
||||
};
|
||||
|
||||
myProjectTypeList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
projectTypeChanged();
|
||||
}
|
||||
});
|
||||
myProjectTypeList.getSelectionModel().addListSelectionListener(__ -> projectTypeChanged());
|
||||
|
||||
myTemplatesList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
updateSelection();
|
||||
}
|
||||
});
|
||||
myTemplatesList.addListSelectionListener(__ -> updateSelection());
|
||||
|
||||
for (TemplatesGroup templatesGroup : myTemplatesMap.keySet()) {
|
||||
ModuleBuilder builder = templatesGroup.getModuleBuilder();
|
||||
@@ -239,7 +222,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
return ContainerUtil.intersects(Arrays.asList(roles), acceptable);
|
||||
}
|
||||
|
||||
public static MultiMap<TemplatesGroup, ProjectTemplate> getTemplatesMap(WizardContext context) {
|
||||
private static MultiMap<TemplatesGroup, ProjectTemplate> getTemplatesMap(WizardContext context) {
|
||||
ProjectTemplatesFactory[] factories = ProjectTemplatesFactory.EP_NAME.getExtensions();
|
||||
final MultiMap<TemplatesGroup, ProjectTemplate> groups = new MultiMap<>();
|
||||
for (ProjectTemplatesFactory factory : factories) {
|
||||
@@ -351,7 +334,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
}
|
||||
|
||||
// new TemplatesGroup selected
|
||||
public void projectTypeChanged() {
|
||||
private void projectTypeChanged() {
|
||||
TemplatesGroup group = getSelectedGroup();
|
||||
if (group == null || group == myLastSelectedGroup) return;
|
||||
myLastSelectedGroup = group;
|
||||
@@ -431,7 +414,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
showCard(TEMPLATES_CARD);
|
||||
}
|
||||
|
||||
private void setTemplatesList(TemplatesGroup group, Collection<ProjectTemplate> templates, boolean preserveSelection) {
|
||||
private void setTemplatesList(TemplatesGroup group, Collection<? extends ProjectTemplate> templates, boolean preserveSelection) {
|
||||
List<ProjectTemplate> list = new ArrayList<>(templates);
|
||||
ModuleBuilder moduleBuilder = group.getModuleBuilder();
|
||||
if (moduleBuilder != null && !(moduleBuilder instanceof TemplateModuleBuilder)) {
|
||||
@@ -463,7 +446,7 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProjectTemplate getSelectedTemplate() {
|
||||
private ProjectTemplate getSelectedTemplate() {
|
||||
return myCurrentCard == TEMPLATES_CARD ? myTemplatesList.getSelectedTemplate() : null;
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -77,14 +77,12 @@ public class UIRootConfigurationAccessor extends RootConfigurationAccessor {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getProjectSdkName(final Project project) {
|
||||
final String projectJdkName = ProjectRootManager.getInstance(project).getProjectSdkName();
|
||||
final Sdk projectJdk = getProjectSdk(project);
|
||||
if (projectJdk != null) {
|
||||
return projectJdk.getName();
|
||||
}
|
||||
else {
|
||||
final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel();
|
||||
return projectJdksModel.findSdk(projectJdkName) == null ? projectJdkName : null;
|
||||
}
|
||||
final String projectJdkName = ProjectRootManager.getInstance(project).getProjectSdkName();
|
||||
final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel();
|
||||
return projectJdkName != null && projectJdksModel.findSdk(projectJdkName) == null ? projectJdkName : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.intellij.util.NullableFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.io.HttpRequests;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.JDOMException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -74,7 +75,7 @@ public class RemoteTemplatesFactory extends ProjectTemplatesFactory {
|
||||
|
||||
@NotNull
|
||||
@TestOnly
|
||||
public static MultiMap<String, ArchivedProjectTemplate> createFromText(@NotNull String value) throws IOException, JDOMException {
|
||||
public static MultiMap<String, ArchivedProjectTemplate> createFromText(@NotNull @Language("XML") String value) throws IOException, JDOMException {
|
||||
return create(JDOMUtil.load(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,24 +19,25 @@ import com.intellij.util.containers.MultiMap;
|
||||
*/
|
||||
public class RemoteTemplatesTest extends NewProjectWizardTestCase {
|
||||
public void testParsing() throws Exception {
|
||||
MultiMap<String, ArchivedProjectTemplate> map = RemoteTemplatesFactory.createFromText("<templates>\n" +
|
||||
" <template>\n" +
|
||||
" <name>Facelets Demo</name>\n" +
|
||||
" <description><![CDATA[\n" +
|
||||
" Demonstrates IDEA support for Facelets technology\n" +
|
||||
" ]]>\n" +
|
||||
" </description>\n" +
|
||||
" <path>facelets.zip</path>\n" +
|
||||
" <moduleType>JAVA_MODULE</moduleType>\n" +
|
||||
" </template>\n" +
|
||||
" <template>\n" +
|
||||
" <name>Incompatible</name>\n" +
|
||||
" <description>Incompatible</description>\n" +
|
||||
" <path>incompatible.zip</path>\n" +
|
||||
" <moduleType>JAVA_MODULE</moduleType>\n" +
|
||||
" <requiredPlugin>unknown.plugin</requiredPlugin>\n" +
|
||||
" </template>\n" +
|
||||
"</templates>");
|
||||
MultiMap<String, ArchivedProjectTemplate> map = RemoteTemplatesFactory.createFromText(
|
||||
"<templates>\n" +
|
||||
" <template>\n" +
|
||||
" <name>Facelets Demo</name>\n" +
|
||||
" <description><![CDATA[\n" +
|
||||
" Demonstrates IDEA support for Facelets technology\n" +
|
||||
" ]]>\n" +
|
||||
" </description>\n" +
|
||||
" <path>facelets.zip</path>\n" +
|
||||
" <moduleType>JAVA_MODULE</moduleType>\n" +
|
||||
" </template>\n" +
|
||||
" <template>\n" +
|
||||
" <name>Incompatible</name>\n" +
|
||||
" <description>Incompatible</description>\n" +
|
||||
" <path>incompatible.zip</path>\n" +
|
||||
" <moduleType>JAVA_MODULE</moduleType>\n" +
|
||||
" <requiredPlugin>unknown.plugin</requiredPlugin>\n" +
|
||||
" </template>\n" +
|
||||
"</templates>");
|
||||
assertEquals(1, map.size());
|
||||
|
||||
ProjectTemplate facelets = map.values().iterator().next();
|
||||
|
||||
@@ -65,7 +65,7 @@ public class AnnotateMethodFix implements LocalQuickFix {
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
return !annotateOverriddenMethods();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+17
@@ -7,8 +7,11 @@ import com.intellij.codeInspection.dataFlow.instructions.Instruction;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -244,6 +247,20 @@ public class TrackingDfaMemoryState extends DfaMemoryStateImpl {
|
||||
return findChange(change -> change.getExpression() == expression, false);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
@Nullable
|
||||
MemoryStateChange findSubExpressionPush(@Nullable PsiExpression expression) {
|
||||
if (expression == null) return null;
|
||||
PsiElement topElement = ExpressionUtils.getPassThroughParent(expression);
|
||||
return findChange(change -> {
|
||||
PsiExpression changeExpression = change.getExpression();
|
||||
if (changeExpression == null) return false;
|
||||
return changeExpression == expression ||
|
||||
(PsiTreeUtil.isAncestor(expression, changeExpression, true) &&
|
||||
ExpressionUtils.getPassThroughParent(changeExpression) == topElement);
|
||||
}, false);
|
||||
}
|
||||
|
||||
MemoryStateChange findRelation(DfaVariableValue value, @NotNull Predicate<Relation> relationPredicate, boolean startFromSelf) {
|
||||
return findChange(change -> {
|
||||
Change varChange = change.myChanges.get(value);
|
||||
|
||||
+48
-17
@@ -9,6 +9,7 @@ import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -16,6 +17,8 @@ import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.CompositeElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
@@ -24,6 +27,7 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.BoolUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -333,9 +337,10 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
Warning caused by narrowing conversion
|
||||
Warning caused by unary minus
|
||||
Warning caused by final field initializer
|
||||
TODO: 3. Check how it works with
|
||||
Literal is not-null
|
||||
TODO: 3. Check how it works with:
|
||||
Inliners (notably: Stream API)
|
||||
Ternary operators
|
||||
Boxed numbers
|
||||
TODO: 4. Check for possible performance disasters (likely on some code patterns current algo might blow up)
|
||||
TODO: 5. Problem when interesting state doesn't reach the current condition, need to do something with this
|
||||
*/
|
||||
@@ -370,8 +375,8 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
Instruction instruction = change.myInstruction;
|
||||
if (instruction instanceof AssignInstruction && change.myTopOfStack == value) {
|
||||
PsiExpression rValue = ((AssignInstruction)instruction).getRExpression();
|
||||
CauseItem item = new CauseItem("'" + value + "' was assigned", rValue);
|
||||
MemoryStateChange push = change.findExpressionPush(rValue);
|
||||
CauseItem item = createAssignmentCause((AssignInstruction)instruction, value);
|
||||
MemoryStateChange push = change.findSubExpressionPush(rValue);
|
||||
if (push != null) {
|
||||
item.addChildren(findConstantValueCause(rValue, push, expectedValue));
|
||||
}
|
||||
@@ -385,6 +390,29 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
return new CauseItem[0];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract("_, _ -> new")
|
||||
private static CauseItem createAssignmentCause(AssignInstruction instruction, DfaValue target) {
|
||||
PsiExpression rExpression = instruction.getRExpression();
|
||||
PsiElement anchor = null;
|
||||
if (rExpression != null) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(rExpression.getParent());
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
anchor = ((PsiAssignmentExpression)parent).getOperationSign();
|
||||
}
|
||||
else if (parent instanceof PsiVariable) {
|
||||
ASTNode node = parent.getNode();
|
||||
if (node instanceof CompositeElement) {
|
||||
anchor = ((CompositeElement)node).findChildByRoleAsPsiElement(ChildRole.INITIALIZER_EQ);
|
||||
}
|
||||
}
|
||||
if (anchor == null) {
|
||||
anchor = rExpression;
|
||||
}
|
||||
}
|
||||
return new CauseItem("'" + target + "' was assigned", anchor);
|
||||
}
|
||||
|
||||
private static CauseItem[] findBooleanResultCauses(PsiExpression expression,
|
||||
MemoryStateChange history,
|
||||
boolean value) {
|
||||
@@ -578,17 +606,17 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
}
|
||||
if (instruction instanceof AssignInstruction) {
|
||||
DfaValue target = change.myTopOfStack;
|
||||
PsiExpression rValue = PsiUtil.skipParenthesizedExprDown(((AssignInstruction)instruction).getRExpression());
|
||||
PsiExpression rValue = ((AssignInstruction)instruction).getRExpression();
|
||||
CauseItem item = createAssignmentCause((AssignInstruction)instruction, target);
|
||||
if (target == value) {
|
||||
CauseItem item = new CauseItem("'" + target + "' was assigned", rValue);
|
||||
MemoryStateChange rValuePush = change.findExpressionPush(rValue);
|
||||
MemoryStateChange rValuePush = change.findSubExpressionPush(rValue);
|
||||
if (rValuePush != null) {
|
||||
item.addChildren(findRelationCause(relation.myRelationType, rValuePush, counterPartChange));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
if (target == relation.myCounterpart) {
|
||||
return new CauseItem("'" + target + "' was assigned", rValue);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
PsiExpression expression = change.getExpression();
|
||||
@@ -617,14 +645,11 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
if (factDef != null && expression != null) {
|
||||
DfaValue value = factUse.myTopOfStack;
|
||||
if (factDef.myInstruction instanceof AssignInstruction && factDef.myTopOfStack == value) {
|
||||
PsiExpression rExpression = PsiUtil.skipParenthesizedExprDown(((AssignInstruction)factDef.myInstruction).getRExpression());
|
||||
while (rExpression instanceof PsiTypeCastExpression) {
|
||||
rExpression = PsiUtil.skipParenthesizedExprDown(((PsiTypeCastExpression)rExpression).getOperand());
|
||||
}
|
||||
PsiExpression rExpression = ((AssignInstruction)factDef.myInstruction).getRExpression();
|
||||
if (rExpression != null) {
|
||||
MemoryStateChange rValuePush = factDef.findExpressionPush(rExpression);
|
||||
MemoryStateChange rValuePush = factDef.findSubExpressionPush(rExpression);
|
||||
if (rValuePush != null) {
|
||||
CauseItem assignmentItem = new CauseItem("'" + value + "' was assigned", rExpression);
|
||||
CauseItem assignmentItem = createAssignmentCause((AssignInstruction)factDef.myInstruction, value);
|
||||
Pair<MemoryStateChange, DfaNullability> rValueFact = rValuePush.findFact(rValuePush.myTopOfStack, DfaFactType.NULLABILITY);
|
||||
assignmentItem.addChildren(findNullabilityCause(rValuePush, rValueFact.first, nullability));
|
||||
return assignmentItem;
|
||||
@@ -636,6 +661,12 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
return new CauseItem("'" + expression.getText() + "' is known to be '" + nullability.getPresentationName() + "' from #ref", defExpression);
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiTypeCastExpression) {
|
||||
MemoryStateChange operandPush = factUse.findSubExpressionPush(((PsiTypeCastExpression)expression).getOperand());
|
||||
if (operandPush != null) {
|
||||
return findNullabilityCause(operandPush, factDef, nullability);
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
|
||||
PsiMethod method = call.resolveMethod();
|
||||
@@ -791,11 +822,11 @@ public class TrackingRunner extends StandardDataFlowRunner {
|
||||
CauseItem item = new CauseItem(String.format(template, rangeText), factUse);
|
||||
if (factDef != null) {
|
||||
if (factDef.myInstruction instanceof AssignInstruction && factDef.myTopOfStack == value) {
|
||||
PsiExpression rExpression = PsiUtil.skipParenthesizedExprDown(((AssignInstruction)factDef.myInstruction).getRExpression());
|
||||
PsiExpression rExpression = ((AssignInstruction)factDef.myInstruction).getRExpression();
|
||||
if (rExpression != null) {
|
||||
MemoryStateChange rValuePush = factDef.findExpressionPush(rExpression);
|
||||
MemoryStateChange rValuePush = factDef.findSubExpressionPush(rExpression);
|
||||
if (rValuePush != null) {
|
||||
CauseItem assignmentItem = new CauseItem("'" + value + "' was assigned", rExpression);
|
||||
CauseItem assignmentItem = createAssignmentCause((AssignInstruction)factDef.myInstruction, value);
|
||||
Pair<MemoryStateChange, LongRangeSet> rValueFact = rValuePush.findFact(rValuePush.myTopOfStack, DfaFactType.RANGE);
|
||||
assignmentItem.addChildren(findRangeCause(rValuePush, rValueFact.first, range, "Value is %s"));
|
||||
item.addChildren(assignmentItem);
|
||||
|
||||
+13
-15
@@ -550,21 +550,19 @@ public class RefJavaManagerImpl extends RefJavaManager {
|
||||
//TODO support suppressions by comment tag in kotlin
|
||||
private void processComments(@NotNull UElement node) {
|
||||
for (UComment comment : node.getComments()) {
|
||||
if (comment instanceof UComment) {
|
||||
PsiElement psi = comment.getSourcePsi();
|
||||
if (psi instanceof PsiDocComment) {
|
||||
final PsiDocTag[] tags = ((PsiDocComment)psi).getTags();
|
||||
for (PsiDocTag tag : tags) {
|
||||
if (Comparing.strEqual(tag.getName(), SuppressionUtilCore.SUPPRESS_INSPECTIONS_TAG_NAME)) {
|
||||
final PsiElement[] dataElements = tag.getDataElements();
|
||||
if (dataElements.length > 0) {
|
||||
final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(psi, PsiModifierListOwner.class);
|
||||
if (listOwner != null) {
|
||||
final WritableRefElement element = (WritableRefElement)myRefManager.getReference(listOwner);
|
||||
if (element != null) {
|
||||
String suppression = StringUtil.join(dataElements, PsiElement::getText, ",");
|
||||
element.addSuppression(suppression);
|
||||
}
|
||||
PsiElement psi = comment.getSourcePsi();
|
||||
if (psi instanceof PsiDocComment) {
|
||||
final PsiDocTag[] tags = ((PsiDocComment)psi).getTags();
|
||||
for (PsiDocTag tag : tags) {
|
||||
if (Comparing.strEqual(tag.getName(), SuppressionUtilCore.SUPPRESS_INSPECTIONS_TAG_NAME)) {
|
||||
final PsiElement[] dataElements = tag.getDataElements();
|
||||
if (dataElements.length > 0) {
|
||||
final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(psi, PsiModifierListOwner.class);
|
||||
if (listOwner != null) {
|
||||
final WritableRefElement element = (WritableRefElement)myRefManager.getReference(listOwner);
|
||||
if (element != null) {
|
||||
String suppression = StringUtil.join(dataElements, PsiElement::getText, ",");
|
||||
element.addSuppression(suppression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +240,10 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
|
||||
if (container instanceof PsiPackage) {
|
||||
PsiFile file = annotation.getContainingFile();
|
||||
ProjectFileIndex index = ProjectRootManager.getInstance(file.getProject()).getFileIndex();
|
||||
VirtualFile annotationRoot = index.getClassRootForFile(file.getVirtualFile());
|
||||
VirtualFile ownerRoot = index.getClassRootForFile(owner.getContainingFile().getVirtualFile());
|
||||
VirtualFile annotationFile = file.getVirtualFile();
|
||||
VirtualFile annotationRoot = annotationFile == null ? null : index.getClassRootForFile(annotationFile);
|
||||
VirtualFile ownerFile = owner.getContainingFile().getVirtualFile();
|
||||
VirtualFile ownerRoot = ownerFile == null ? null : index.getClassRootForFile(ownerFile);
|
||||
if (ownerRoot != null && !ownerRoot.equals(annotationRoot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ public class FindDfaProblemCauseFix implements LocalQuickFix, LowPriorityAction
|
||||
highlighter.getAndSet(null).dropHighlight();
|
||||
}
|
||||
})
|
||||
.setItemChosenCallback(cause -> ReadAction.run(() -> navigate(editor, file, cause.myCauseItem)))
|
||||
.setItemChosenCallback(cause -> navigate(editor, file, cause.myCauseItem))
|
||||
.createPopup();
|
||||
popup.showInBestPositionFor(editor);
|
||||
}
|
||||
|
||||
-27
@@ -20,7 +20,6 @@ import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.navigation.ChooseByNameContributorEx;
|
||||
import com.intellij.navigation.GotoClassContributor;
|
||||
import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -30,41 +29,15 @@ import com.intellij.psi.presentation.java.SymbolPresentationUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiShortNamesCache;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Processors;
|
||||
import com.intellij.util.indexing.FindSymbolParameters;
|
||||
import com.intellij.util.indexing.IdFilter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
public class DefaultClassNavigationContributor implements ChooseByNameContributorEx, GotoClassContributor {
|
||||
@Override
|
||||
@NotNull
|
||||
public String[] getNames(Project project, boolean includeNonProjectItems) {
|
||||
GlobalSearchScope scope = includeNonProjectItems ? GlobalSearchScope.allScope(project) : GlobalSearchScope.projectScope(project);
|
||||
List<String> result = new ArrayList<>();
|
||||
Processor<String> processor = Processors.cancelableCollectProcessor(result);
|
||||
|
||||
processNames(processor, scope, IdFilter.getProjectIdFilter(project, includeNonProjectItems));
|
||||
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public NavigationItem[] getItemsByName(String name, final String pattern, Project project, boolean includeNonProjectItems) {
|
||||
List<NavigationItem> result = new ArrayList<>();
|
||||
Processor<NavigationItem> processor = Processors.cancelableCollectProcessor(result);
|
||||
processElementsWithName(name, processor, FindSymbolParameters.wrap(pattern, project, includeNonProjectItems));
|
||||
|
||||
return result.isEmpty() ? NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY :
|
||||
result.toArray(NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQualifiedName(final NavigationItem item) {
|
||||
|
||||
+3
-41
@@ -30,51 +30,13 @@ import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class DefaultSymbolNavigationContributor implements ChooseByNameContributorEx, GotoClassContributor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.DefaultSymbolNavigationContributor");
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String[] getNames(Project project, boolean includeNonProjectItems) {
|
||||
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
|
||||
Set<String> set = new HashSet<>();
|
||||
Collections.addAll(set, cache.getAllMethodNames());
|
||||
Collections.addAll(set, cache.getAllFieldNames());
|
||||
Collections.addAll(set, cache.getAllClassNames());
|
||||
return ArrayUtil.toStringArray(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public NavigationItem[] getItemsByName(String name, final String pattern, Project project, boolean includeNonProjectItems) {
|
||||
GlobalSearchScope scope = includeNonProjectItems ? GlobalSearchScope.allScope(project) : GlobalSearchScope.projectScope(project);
|
||||
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
|
||||
|
||||
Condition<PsiMember> qualifiedMatcher = getQualifiedNameMatcher(pattern);
|
||||
|
||||
List<PsiMember> result = new ArrayList<>();
|
||||
for (PsiMethod method : cache.getMethodsByName(name, scope)) {
|
||||
if (!method.isConstructor() && isOpenable(method) && !hasSuperMethod(method, scope, qualifiedMatcher, pattern)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
for (PsiField field : cache.getFieldsByName(name, scope)) {
|
||||
if (isOpenable(field)) {
|
||||
result.add(field);
|
||||
}
|
||||
}
|
||||
for (PsiClass aClass : cache.getClassesByName(name, scope)) {
|
||||
if (isOpenable(aClass)) {
|
||||
result.add(aClass);
|
||||
}
|
||||
}
|
||||
PsiMember[] array = result.toArray(PsiMember.EMPTY_ARRAY);
|
||||
Arrays.sort(array, MyComparator.INSTANCE);
|
||||
return array;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getQualifiedName(NavigationItem item) {
|
||||
|
||||
@@ -29,7 +29,5 @@ internal class RangeExpression(
|
||||
|
||||
override fun calculateQuickResult(context: ExpressionContext): Result? = calculateResult(context)
|
||||
|
||||
override fun requiresCommittedPSI(): Boolean = false
|
||||
|
||||
override fun calculateLookupItems(context: ExpressionContext): Array<out LookupElement> = LookupElement.EMPTY_ARRAY
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Value is always false (x > 6 && y > 10)
|
||||
Operand #1 of &&-chain is false (x > 6)
|
||||
Left operand is 5 (x)
|
||||
'x' was assigned (5)
|
||||
'x' was assigned (=)
|
||||
*/
|
||||
|
||||
class Test {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Value is always false (foo == null)
|
||||
'foo' was assigned (=)
|
||||
One of the following happens:
|
||||
Expression cannot be null as it's newly created object (new String("foo"))
|
||||
or expression cannot be null as it's newly created object (new String("bar"))
|
||||
*/
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class Test {
|
||||
|
||||
void test(boolean b) {
|
||||
String foo = b ? new String("foo") : new String("bar");
|
||||
if (<selection>foo == null</selection>) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Value is always false (foo > 3)
|
||||
One of the following happens:
|
||||
Left operand is 1 (foo)
|
||||
'foo' was assigned (=)
|
||||
or left operand is 2 (foo)
|
||||
'foo' was assigned (=)
|
||||
*/
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class Test {
|
||||
|
||||
void test(boolean b) {
|
||||
int foo = b ? 1 : 2;
|
||||
if (<selection>foo > 3</selection>) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always true (b)
|
||||
'b' was assigned (true)
|
||||
'b' was assigned (=)
|
||||
*/
|
||||
|
||||
class Test {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always false (s == null)
|
||||
's' was assigned (s1)
|
||||
's' was assigned (=)
|
||||
Condition 's1 != null' was checked before (s1 == null)
|
||||
*/
|
||||
class Test {
|
||||
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
Value is always false (b4)
|
||||
'b4' was assigned (b3)
|
||||
'b3' was assigned (b2)
|
||||
'b2' was assigned (b1)
|
||||
'b1' was assigned (b)
|
||||
'b4' was assigned (=)
|
||||
'b3' was assigned (=)
|
||||
'b2' was assigned (=)
|
||||
'b1' was assigned (=)
|
||||
'b == false' was established from condition (b)
|
||||
*/
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
Value is always false (x == null)
|
||||
One of the following happens:
|
||||
'x' was assigned (new Object())
|
||||
'x' was assigned (=)
|
||||
Expression cannot be null as it's newly created object (new Object())
|
||||
or 'x' was assigned ("foo")
|
||||
or 'x' was assigned (=)
|
||||
Expression cannot be null as it's literal ("foo")
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
Value is always false (y == null)
|
||||
'y' was assigned (x)
|
||||
'y' was assigned (=)
|
||||
One of the following happens:
|
||||
'x' was assigned (new Object())
|
||||
'x' was assigned (=)
|
||||
Expression cannot be null as it's newly created object (new Object())
|
||||
or 'x' was assigned ("foo")
|
||||
or 'x' was assigned (=)
|
||||
Expression cannot be null as it's literal ("foo")
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
Value is always true (j >= 0)
|
||||
Left operand is >= 0 (j)
|
||||
'j' was assigned (i + 1)
|
||||
'j' was assigned (=)
|
||||
Result of '+' is >= 0 (i + 1)
|
||||
Left operand is in {-1..Integer.MAX_VALUE-1} (i)
|
||||
'i' was assigned (s.indexOf(' '))
|
||||
'i' was assigned (=)
|
||||
Value is in {-1..Integer.MAX_VALUE-1} (s.indexOf(' '))
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always false (s1 == null)
|
||||
's1' was assigned (s.trim())
|
||||
's1' was assigned (=)
|
||||
Method 'trim' is externally annotated as 'non-null' (trim)
|
||||
*/
|
||||
class Test {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always true ((s = new Object()) != null)
|
||||
's' was assigned (new Object())
|
||||
's' was assigned (=)
|
||||
Expression cannot be null as it's newly created object (new Object())
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always true (s != null)
|
||||
's' was assigned (new Object())
|
||||
's' was assigned (=)
|
||||
Expression cannot be null as it's newly created object (new Object())
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always true (null == s)
|
||||
's' was assigned (null)
|
||||
's' was assigned (=)
|
||||
*/
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
May be null (s)
|
||||
's' was assigned (loadString())
|
||||
's' was assigned (=)
|
||||
Method 'loadString' is annotated as 'nullable' (@Nullable)
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
May be null (foo)
|
||||
'foo' was assigned (getFoo())
|
||||
'foo' was assigned (=)
|
||||
Method 'getFoo' is annotated as 'nullable' (@Nullable)
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Value is always false (s == s1)
|
||||
's1' was assigned (null)
|
||||
's1' was assigned (=)
|
||||
and condition 's != null' was checked before (null == s)
|
||||
*/
|
||||
class Test {
|
||||
|
||||
@@ -6,14 +6,14 @@ Value is always true (x || (a+b)+(c+d)==10)
|
||||
Result of '+' is 10 ((a+b)+(c+d))
|
||||
Result of '+' is 3 (a+b)
|
||||
Left operand is 1 (a)
|
||||
'a' was assigned (1)
|
||||
'a' was assigned (=)
|
||||
and right operand is 2 (b)
|
||||
'b' was assigned (2)
|
||||
'b' was assigned (=)
|
||||
and result of '+' is 7 (c+d)
|
||||
Left operand is 3 (c)
|
||||
'c' was assigned (3)
|
||||
'c' was assigned (=)
|
||||
and right operand is 4 (d)
|
||||
'd' was assigned (4)
|
||||
'd' was assigned (=)
|
||||
*/
|
||||
|
||||
class Test {
|
||||
|
||||
+2
@@ -149,4 +149,6 @@ public class DataFlowInspectionTrackerTest extends LightCodeInsightFixtureTestCa
|
||||
public void testNpeSimple() { doTest(); }
|
||||
public void testNpeAnnotation() { doTest(); }
|
||||
public void testNpeWithCast() { doTest(); }
|
||||
public void testAssignTernaryNotNull() { doTest(); }
|
||||
public void testAssignTernaryNumeric() { doTest(); }
|
||||
}
|
||||
|
||||
+1
-1
@@ -287,7 +287,7 @@ public class EditorTypingAndNavigationUndoTest extends EditorUndoTestCase {
|
||||
Ref<String> message = new Ref<>();
|
||||
Messages.setTestDialog(new TestDialog() {
|
||||
@Override
|
||||
public int show(String m) {
|
||||
public int show(@NotNull String m) {
|
||||
message.set(m);
|
||||
return Messages.YES;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class GlobalUndoTest extends UndoTestCase implements TestDialog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int show(String message) {
|
||||
public int show(@NotNull String message) {
|
||||
myConfirmationWasRequested = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -127,18 +127,18 @@ public abstract class ProjectWizardTestCase<T extends AbstractProjectWizard> ext
|
||||
return createModuleFromWizard(project);
|
||||
}
|
||||
|
||||
protected Module createModuleFromWizard(@NotNull Project project) {
|
||||
private Module createModuleFromWizard(@NotNull Project project) {
|
||||
return new NewModuleAction().createModuleFromWizard(project, null, myWizard);
|
||||
}
|
||||
|
||||
protected void runWizard(@NotNull String group,
|
||||
@Nullable final String name,
|
||||
@Nullable Project project,
|
||||
@Nullable final Consumer<? super Step> adjuster) throws IOException {
|
||||
private void runWizard(@NotNull String group,
|
||||
@Nullable final String name,
|
||||
@Nullable Project project,
|
||||
@Nullable final Consumer<? super Step> adjuster) throws IOException {
|
||||
createWizard(project);
|
||||
ProjectTypeStep step = (ProjectTypeStep)myWizard.getCurrentStepObject();
|
||||
if (!step.setSelectedTemplate(group, name)) {
|
||||
throw new IllegalArgumentException(group + '/' + name + " template not found, available groups " + step.availableTemplateGroupsToString());
|
||||
throw new IllegalArgumentException(group + '/' + name + " template not found. Available groups: " + step.availableTemplateGroupsToString());
|
||||
}
|
||||
|
||||
runWizard(step1 -> {
|
||||
@@ -151,7 +151,7 @@ public abstract class ProjectWizardTestCase<T extends AbstractProjectWizard> ext
|
||||
});
|
||||
}
|
||||
|
||||
protected void runWizard(@Nullable Consumer<? super Step> adjuster) {
|
||||
private void runWizard(@Nullable Consumer<? super Step> adjuster) {
|
||||
while (true) {
|
||||
ModuleWizardStep currentStep = myWizard.getCurrentStepObject();
|
||||
if (adjuster != null) {
|
||||
@@ -236,7 +236,7 @@ public abstract class ProjectWizardTestCase<T extends AbstractProjectWizard> ext
|
||||
return ContainerUtil.getFirstItem(ImportModuleAction.createFromWizard(project, myWizard));
|
||||
}
|
||||
|
||||
private static <T> T computeInWriteSafeContext(Supplier<T> supplier) {
|
||||
private static <T> T computeInWriteSafeContext(Supplier<? extends T> supplier) {
|
||||
Ref<T> module = Ref.create();
|
||||
ApplicationManager.getApplication().invokeLater(() -> module.set(supplier.get()));
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.jps.model.library.sdk;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.JpsElement;
|
||||
import org.jetbrains.jps.model.JpsElementReference;
|
||||
import org.jetbrains.jps.model.library.JpsTypedLibrary;
|
||||
@@ -23,5 +24,6 @@ import org.jetbrains.jps.model.library.JpsTypedLibrary;
|
||||
* @author nik
|
||||
*/
|
||||
public interface JpsSdkReference<P extends JpsElement> extends JpsElementReference<JpsTypedLibrary<JpsSdk<P>>> {
|
||||
@NotNull
|
||||
String getSdkName();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public abstract class JpsNamedElementReferenceBase<S extends JpsNamedElement, T
|
||||
extends JpsCompositeElementBase<Self> implements JpsElementReference<T> {
|
||||
private static final JpsElementChildRole<JpsElementReference<? extends JpsCompositeElement>> PARENT_REFERENCE_ROLE = JpsElementChildRoleBase
|
||||
.create("parent");
|
||||
@NotNull
|
||||
protected final String myElementName;
|
||||
|
||||
protected JpsNamedElementReferenceBase(@NotNull String elementName, @NotNull JpsElementReference<? extends JpsCompositeElement> parentReference) {
|
||||
|
||||
+1
@@ -53,6 +53,7 @@ public class JpsWrappedJavaSdkReferenceImpl extends JpsCompositeElementBase<JpsW
|
||||
return new JpsWrappedJavaSdkReferenceImpl(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSdkName() {
|
||||
JpsTypedLibrary<JpsSdk<JpsDummyElement>> sdk = resolve();
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NlsCapitalizationUtil {
|
||||
|
||||
public static boolean isCapitalizationSatisfied(String value, Nls.Capitalization capitalization) {
|
||||
if (StringUtil.isEmpty(value) || capitalization == Nls.Capitalization.NotSpecified) {
|
||||
return true;
|
||||
}
|
||||
value = value.replace("&", "");
|
||||
return capitalization == Nls.Capitalization.Title
|
||||
? StringUtil.wordsToBeginFromUpperCase(value).equals(value)
|
||||
: checkSentenceCapitalization(value);
|
||||
}
|
||||
|
||||
private static boolean checkSentenceCapitalization(@NotNull String value) {
|
||||
List<String> words = StringUtil.split(value, " ");
|
||||
if (words.size() == 0) return true;
|
||||
if (Character.isLetter(words.get(0).charAt(0)) && !isCapitalizedWord(words.get(0))) return false;
|
||||
if (words.size() == 1) return true;
|
||||
int capitalized = 1;
|
||||
for (int i = 1, size = words.size(); i < size; i++) {
|
||||
String word = words.get(i);
|
||||
if (isCapitalizedWord(word)) {
|
||||
// check for abbreviations like SQL or I18n
|
||||
if (word.length() == 1 || !Character.isLowerCase(word.charAt(1))) {
|
||||
continue;
|
||||
}
|
||||
capitalized++;
|
||||
}
|
||||
}
|
||||
return capitalized / words.size() < 0.2; // allow reasonable amount of capitalized words
|
||||
}
|
||||
|
||||
private static boolean isCapitalizedWord(String word) {
|
||||
return word.length() > 0 && Character.isLetter(word.charAt(0)) && Character.isUpperCase(word.charAt(0));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String fixValue(String string, Nls.Capitalization capitalization) {
|
||||
return capitalization == Nls.Capitalization.Title
|
||||
? StringUtil.wordsToBeginFromUpperCase(string)
|
||||
: StringUtil.capitalize(StringUtil.wordsToBeginFromLowerCase(string));
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2000-2019 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.
|
||||
* 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 com.intellij.codeInspection;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
|
||||
import static com.intellij.codeInspection.NlsCapitalizationUtil.isCapitalizationSatisfied;
|
||||
|
||||
public class NlsCapitalizationUtilTest extends TestCase {
|
||||
|
||||
public void testCapitalization() {
|
||||
assertTrue(isCapitalizationSatisfied("Fix SQL issues", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Fix I18n issues", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Fix C issues", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("@charset is invalid", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Add 'this' qualifier", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Add 'this' qualifier", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Please select the configuration file (usually named IntelliLang.xml) to import.",
|
||||
Nls.Capitalization.Sentence));
|
||||
assertFalse(isCapitalizationSatisfied("Foo Bar", Nls.Capitalization.Sentence));
|
||||
assertTrue(isCapitalizationSatisfied("Foo", Nls.Capitalization.Sentence));
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,12 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.model" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.model.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.jps.model.serialization" />
|
||||
<orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="PROVIDED" />
|
||||
<orderEntry type="library" name="http-client" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="commons-imaging" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel.impl" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.ide
|
||||
|
||||
import com.intellij.ide.impl.ProjectUtil.focusProjectWindow
|
||||
@@ -72,7 +72,7 @@ internal class OpenFileHttpService : RestService() {
|
||||
|
||||
val apiRequest: OpenFileRequest
|
||||
if (request.method() === HttpMethod.POST) {
|
||||
apiRequest = gson.value.fromJson(createJsonReader(request), OpenFileRequest::class.java)
|
||||
apiRequest = gson.fromJson(createJsonReader(request), OpenFileRequest::class.java)
|
||||
}
|
||||
else {
|
||||
apiRequest = OpenFileRequest()
|
||||
@@ -82,7 +82,7 @@ internal class OpenFileHttpService : RestService() {
|
||||
apiRequest.focused = getBooleanParameter("focused", urlDecoder, true)
|
||||
}
|
||||
|
||||
val prefixLength = 1 + PREFIX.length + 1 + serviceName.length + 1
|
||||
val prefixLength = 1 + PREFIX.length + 1 + getServiceName().length + 1
|
||||
val path = urlDecoder.path()
|
||||
if (path.length > prefixLength) {
|
||||
val matcher = LINE_AND_COLUMN.matcher(path).region(prefixLength, path.length)
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.ide;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.gson.stream.MalformedJsonException;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.util.text.StringUtilRt;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.openapi.wm.IdeFrame;
|
||||
import com.intellij.ui.AppIcon;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.NettyKt;
|
||||
import com.intellij.util.net.NetUtils;
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.builtInWebServer.BuiltInWebServerKt;
|
||||
import org.jetbrains.io.Responses;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.intellij.ide.impl.ProjectUtil.showYesNoDialog;
|
||||
|
||||
/**
|
||||
* Document your service using <a href="http://apidocjs.com">apiDoc</a>. To extract big example from source code, consider to use *.coffee file near your source file.
|
||||
* (or Python/Ruby, but coffee recommended because it's plugin is lightweight). See {@link AboutHttpService} for example.
|
||||
*
|
||||
* Don't create JsonReader/JsonWriter directly, use only provided {@link #createJsonReader}, {@link #createJsonWriter} methods (to ensure that you handle in/out according to REST API guidelines).
|
||||
*
|
||||
* @see <a href="http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api">Best Practices for Designing a Pragmatic RESTful API</a>.
|
||||
*/
|
||||
public abstract class RestService extends HttpRequestHandler {
|
||||
protected static final Logger LOG = Logger.getInstance(RestService.class);
|
||||
public static final String PREFIX = "api";
|
||||
|
||||
protected final NotNullLazyValue<Gson> gson = new NotNullLazyValue<Gson>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Gson compute() {
|
||||
return new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
}
|
||||
};
|
||||
|
||||
private final LoadingCache<InetAddress, AtomicInteger> abuseCounter =
|
||||
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build(CacheLoader.from((Supplier<AtomicInteger>)AtomicInteger::new));
|
||||
|
||||
private final Cache<String, Boolean> trustedOrigins =
|
||||
CacheBuilder.newBuilder().maximumSize(1024).expireAfterWrite(1, TimeUnit.DAYS).build();
|
||||
|
||||
@Override
|
||||
public final boolean isSupported(@NotNull FullHttpRequest request) {
|
||||
if (!isMethodSupported(request.method())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String uri = request.uri();
|
||||
|
||||
if (isPrefixlessAllowed() && checkPrefix(uri, getServiceName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String serviceName = getServiceName();
|
||||
int minLength = 1 + PREFIX.length() + 1 + serviceName.length();
|
||||
if (uri.length() >= minLength &&
|
||||
uri.charAt(0) == '/' &&
|
||||
uri.regionMatches(true, 1, PREFIX, 0, PREFIX.length()) &&
|
||||
uri.regionMatches(true, 2 + PREFIX.length(), serviceName, 0, serviceName.length())) {
|
||||
if (uri.length() == minLength) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
char c = uri.charAt(minLength);
|
||||
return c == '/' || c == '?';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service url must be "/api/$serviceName", but to preserve backward compatibility, prefixless path could be also supported
|
||||
*/
|
||||
protected boolean isPrefixlessAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use human-readable name or UUID if it is an internal service.
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract String getServiceName();
|
||||
|
||||
protected boolean isMethodSupported(@NotNull HttpMethod method) {
|
||||
return method == HttpMethod.GET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean process(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) {
|
||||
try {
|
||||
AtomicInteger counter = abuseCounter.get(((InetSocketAddress)context.channel().remoteAddress()).getAddress());
|
||||
if (counter.incrementAndGet() > Registry.intValue("ide.rest.api.requests.per.minute", 30)) {
|
||||
Responses.send(Responses.orInSafeMode(HttpResponseStatus.TOO_MANY_REQUESTS, HttpResponseStatus.OK), context.channel(), request);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isHostTrusted(request, urlDecoder)) {
|
||||
Responses.send(Responses.orInSafeMode(HttpResponseStatus.FORBIDDEN, HttpResponseStatus.OK), context.channel(), request);
|
||||
return true;
|
||||
}
|
||||
|
||||
String error = execute(urlDecoder, request, context);
|
||||
if (error != null) {
|
||||
Responses.send(HttpResponseStatus.BAD_REQUEST, context.channel(), request, error);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
HttpResponseStatus status;
|
||||
// JsonReader exception
|
||||
//noinspection InstanceofCatchParameter
|
||||
if (e instanceof MalformedJsonException || (e instanceof IllegalStateException && e.getMessage().startsWith("Expected a "))) {
|
||||
LOG.warn(e);
|
||||
status = HttpResponseStatus.BAD_REQUEST;
|
||||
}
|
||||
else {
|
||||
LOG.error(e);
|
||||
status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
Responses.send(status, context.channel(), request, ExceptionUtil.getThrowableText(e));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected boolean isHostTrusted(@NotNull FullHttpRequest request, @NotNull QueryStringDecoder urlDecoder) throws InterruptedException, InvocationTargetException {
|
||||
return isHostTrusted(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #isHostTrusted(FullHttpRequest, QueryStringDecoder)}
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
// e.g. upsource trust to configured host
|
||||
protected boolean isHostTrusted(@NotNull FullHttpRequest request) throws InterruptedException, InvocationTargetException {
|
||||
if (BuiltInWebServerKt.isSignedRequest(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String referrer = NettyKt.getOrigin(request);
|
||||
if (referrer == null) {
|
||||
referrer = NettyKt.getReferrer(request);
|
||||
}
|
||||
|
||||
String host;
|
||||
try {
|
||||
host = StringUtil.nullize(referrer == null ? null : new URI(referrer).getHost());
|
||||
}
|
||||
catch (URISyntaxException ignored) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<Boolean> isTrusted = Ref.create();
|
||||
if (host != null) {
|
||||
if (NetUtils.isLocalhost(host)) {
|
||||
isTrusted.set(true);
|
||||
}
|
||||
else {
|
||||
isTrusted.set(trustedOrigins.getIfPresent(host));
|
||||
}
|
||||
}
|
||||
|
||||
if (isTrusted.isNull()) {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
AppIcon.getInstance().requestAttention(null, true);
|
||||
isTrusted.set(showYesNoDialog(
|
||||
IdeBundle.message("warning.use.rest.api", getServiceName(), ObjectUtils.chooseNotNull(host, "unknown host")),
|
||||
"title.use.rest.api"));
|
||||
if (host != null) {
|
||||
trustedOrigins.put(host, isTrusted.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
return isTrusted.get();
|
||||
}
|
||||
|
||||
protected static void activateLastFocusedFrame() {
|
||||
IdeFrame frame = IdeFocusManager.getGlobalInstance().getLastFocusedFrame();
|
||||
if (frame instanceof Window) {
|
||||
((Window)frame).toFront();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error or send response using {@link #sendOk(FullHttpRequest, ChannelHandlerContext)}, {@link #send(BufferExposingByteArrayOutputStream, HttpRequest, ChannelHandlerContext)}
|
||||
*/
|
||||
@Nullable("error text or null if successful")
|
||||
public abstract String execute(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException;
|
||||
|
||||
@NotNull
|
||||
protected static JsonReader createJsonReader(@NotNull FullHttpRequest request) {
|
||||
JsonReader reader = new JsonReader(new InputStreamReader(new ByteBufInputStream(request.content()), StandardCharsets.UTF_8));
|
||||
reader.setLenient(true);
|
||||
return reader;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static JsonWriter createJsonWriter(@NotNull OutputStream out) {
|
||||
JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
|
||||
writer.setIndent(" ");
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static Project getLastFocusedOrOpenedProject() {
|
||||
IdeFrame lastFocusedFrame = IdeFocusManager.getGlobalInstance().getLastFocusedFrame();
|
||||
Project project = lastFocusedFrame == null ? null : lastFocusedFrame.getProject();
|
||||
if (project == null) {
|
||||
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
|
||||
return openProjects.length > 0 ? openProjects[0] : null;
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
protected static void sendOk(@NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) {
|
||||
sendStatus(HttpResponseStatus.OK, HttpUtil.isKeepAlive(request), context.channel());
|
||||
}
|
||||
|
||||
protected static void sendStatus(@NotNull HttpResponseStatus status, boolean keepAlive, @NotNull Channel channel) {
|
||||
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
|
||||
HttpUtil.setContentLength(response, 0);
|
||||
Responses.addCommonHeaders(response);
|
||||
Responses.addNoCache(response);
|
||||
if (keepAlive) {
|
||||
HttpUtil.setKeepAlive(response, true);
|
||||
}
|
||||
response.headers().set("X-Frame-Options", "Deny");
|
||||
Responses.send(response, channel, !keepAlive);
|
||||
}
|
||||
|
||||
protected static void send(@NotNull BufferExposingByteArrayOutputStream byteOut, @NotNull HttpRequest request, @NotNull ChannelHandlerContext context) {
|
||||
HttpResponse response = Responses.response("application/json", Unpooled.wrappedBuffer(byteOut.getInternalBuffer(), 0, byteOut.size()));
|
||||
sendResponse(request, context, response);
|
||||
}
|
||||
|
||||
protected static void sendResponse(@NotNull HttpRequest request, @NotNull ChannelHandlerContext context, @NotNull HttpResponse response) {
|
||||
Responses.addNoCache(response);
|
||||
response.headers().set("X-Frame-Options", "Deny");
|
||||
Responses.send(response, context.channel(), request);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static String getStringParameter(@NotNull String name, @NotNull QueryStringDecoder urlDecoder) {
|
||||
return ContainerUtil.getLastItem(urlDecoder.parameters().get(name));
|
||||
}
|
||||
|
||||
protected static int getIntParameter(@NotNull String name, @NotNull QueryStringDecoder urlDecoder) {
|
||||
return StringUtilRt.parseInt(StringUtil.nullize(ContainerUtil.getLastItem(urlDecoder.parameters().get(name)), true), -1);
|
||||
}
|
||||
|
||||
protected static boolean getBooleanParameter(@NotNull String name, @NotNull QueryStringDecoder urlDecoder) {
|
||||
return getBooleanParameter(name, urlDecoder, false);
|
||||
}
|
||||
|
||||
protected static boolean getBooleanParameter(@NotNull String name, @NotNull QueryStringDecoder urlDecoder, boolean defaultValue) {
|
||||
List<String> values = urlDecoder.parameters().get(name);
|
||||
if (ContainerUtil.isEmpty(values)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
String value = values.get(values.size() - 1);
|
||||
// if just name specified, so, true
|
||||
return value.isEmpty() || Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.ide
|
||||
|
||||
import com.google.common.base.Supplier
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.common.cache.CacheLoader
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import com.google.gson.stream.MalformedJsonException
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.ide.impl.ProjectUtil.showYesNoDialog
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.util.text.StringUtilRt
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.ui.AppIcon
|
||||
import com.intellij.util.ExceptionUtil
|
||||
import com.intellij.util.io.origin
|
||||
import com.intellij.util.io.referrer
|
||||
import com.intellij.util.net.NetUtils
|
||||
import com.intellij.util.text.nullize
|
||||
import io.netty.buffer.ByteBufInputStream
|
||||
import io.netty.buffer.Unpooled
|
||||
import io.netty.channel.Channel
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.handler.codec.http.*
|
||||
import org.jetbrains.builtInWebServer.isSignedRequest
|
||||
import org.jetbrains.io.*
|
||||
import java.awt.Window
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
* Document your service using [apiDoc](http://apidocjs.com). To extract big example from source code, consider to use *.coffee file near your source file.
|
||||
* (or Python/Ruby, but coffee recommended because it's plugin is lightweight). See [AboutHttpService] for example.
|
||||
*
|
||||
* Don't create JsonReader/JsonWriter directly, use only provided [.createJsonReader], [.createJsonWriter] methods (to ensure that you handle in/out according to REST API guidelines).
|
||||
*
|
||||
* @see [Best Practices for Designing a Pragmatic REST API](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api).
|
||||
*/
|
||||
abstract class RestService : HttpRequestHandler() {
|
||||
companion object {
|
||||
@JvmField
|
||||
val LOG = logger<RestService>()
|
||||
|
||||
const val PREFIX = "api"
|
||||
|
||||
@JvmStatic
|
||||
protected fun activateLastFocusedFrame() {
|
||||
(IdeFocusManager.getGlobalInstance().lastFocusedFrame as? Window)?.toFront()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun createJsonReader(request: FullHttpRequest): JsonReader {
|
||||
val reader = JsonReader(ByteBufInputStream(request.content()).reader())
|
||||
reader.isLenient = true
|
||||
return reader
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun createJsonWriter(out: OutputStream): JsonWriter {
|
||||
val writer = JsonWriter(out.writer())
|
||||
writer.setIndent(" ")
|
||||
return writer
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getLastFocusedOrOpenedProject(): Project? {
|
||||
return IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project ?: ProjectManager.getInstance().openProjects.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun sendOk(request: FullHttpRequest, context: ChannelHandlerContext) {
|
||||
sendStatus(HttpResponseStatus.OK, HttpUtil.isKeepAlive(request), context.channel())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun sendStatus(status: HttpResponseStatus, keepAlive: Boolean, channel: Channel) {
|
||||
val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status)
|
||||
HttpUtil.setContentLength(response, 0)
|
||||
response.addCommonHeaders()
|
||||
response.addNoCache()
|
||||
if (keepAlive) {
|
||||
HttpUtil.setKeepAlive(response, true)
|
||||
}
|
||||
response.headers().set("X-Frame-Options", "Deny")
|
||||
response.send(channel, !keepAlive)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun send(byteOut: BufferExposingByteArrayOutputStream, request: HttpRequest, context: ChannelHandlerContext) {
|
||||
val response = response("application/json", Unpooled.wrappedBuffer(byteOut.internalBuffer, 0, byteOut.size()))
|
||||
sendResponse(request, context, response)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun sendResponse(request: HttpRequest, context: ChannelHandlerContext, response: HttpResponse) {
|
||||
response.addNoCache()
|
||||
response.headers().set("X-Frame-Options", "Deny")
|
||||
response.send(context.channel(), request)
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
@JvmStatic
|
||||
protected fun getStringParameter(name: String, urlDecoder: QueryStringDecoder): String? {
|
||||
return urlDecoder.parameters().get(name)?.lastOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
protected fun getIntParameter(name: String, urlDecoder: QueryStringDecoder): Int {
|
||||
return StringUtilRt.parseInt(getStringParameter(name, urlDecoder).nullize(nullizeSpaces = true), -1)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@JvmStatic
|
||||
protected fun getBooleanParameter(name: String, urlDecoder: QueryStringDecoder, defaultValue: Boolean = false): Boolean {
|
||||
val values = urlDecoder.parameters().get(name) ?: return defaultValue
|
||||
// if just name specified, so, true
|
||||
val value = values.lastOrNull() ?: return true
|
||||
return value.toBoolean()
|
||||
}
|
||||
}
|
||||
|
||||
protected val gson: Gson by lazy {
|
||||
GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create()
|
||||
}
|
||||
|
||||
private val abuseCounter = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build<InetAddress, AtomicInteger>(CacheLoader.from(Supplier { AtomicInteger() }))
|
||||
|
||||
private val trustedOrigins = CacheBuilder.newBuilder()
|
||||
.maximumSize(1024)
|
||||
.expireAfterWrite(1, TimeUnit.DAYS)
|
||||
.build<String, Boolean>()
|
||||
|
||||
/**
|
||||
* Service url must be "/api/$serviceName", but to preserve backward compatibility, prefixless path could be also supported
|
||||
*/
|
||||
protected open val isPrefixlessAllowed: Boolean
|
||||
get() = false
|
||||
|
||||
/**
|
||||
* Use human-readable name or UUID if it is an internal service.
|
||||
*/
|
||||
protected abstract fun getServiceName(): String
|
||||
|
||||
override fun isSupported(request: FullHttpRequest): Boolean {
|
||||
if (!isMethodSupported(request.method())) {
|
||||
return false
|
||||
}
|
||||
|
||||
val uri = request.uri()
|
||||
|
||||
if (isPrefixlessAllowed && checkPrefix(uri, getServiceName())) {
|
||||
return true
|
||||
}
|
||||
|
||||
val serviceName = getServiceName()
|
||||
val minLength = 1 + PREFIX.length + 1 + serviceName.length
|
||||
if (uri.length >= minLength &&
|
||||
uri[0] == '/' &&
|
||||
uri.regionMatches(1, PREFIX, 0, PREFIX.length, ignoreCase = true) &&
|
||||
uri.regionMatches(2 + PREFIX.length, serviceName, 0, serviceName.length, ignoreCase = true)) {
|
||||
if (uri.length == minLength) {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
val c = uri[minLength]
|
||||
return c == '/' || c == '?'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun isMethodSupported(method: HttpMethod): Boolean {
|
||||
return method === HttpMethod.GET
|
||||
}
|
||||
|
||||
override fun process(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): Boolean {
|
||||
try {
|
||||
val counter = abuseCounter.get((context.channel().remoteAddress() as InetSocketAddress).address)
|
||||
if (counter.incrementAndGet() > Registry.intValue("ide.rest.api.requests.per.minute", 30)) {
|
||||
HttpResponseStatus.TOO_MANY_REQUESTS.orInSafeMode(HttpResponseStatus.OK).send(context.channel(), request)
|
||||
return true
|
||||
}
|
||||
|
||||
if (!isHostTrusted(request, urlDecoder)) {
|
||||
HttpResponseStatus.FORBIDDEN.orInSafeMode(HttpResponseStatus.OK).send(context.channel(), request)
|
||||
return true
|
||||
}
|
||||
|
||||
val error = execute(urlDecoder, request, context)
|
||||
if (error != null) {
|
||||
HttpResponseStatus.BAD_REQUEST.send(context.channel(), request, error)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
val status: HttpResponseStatus?
|
||||
// JsonReader exception
|
||||
if (e is MalformedJsonException || e is IllegalStateException && e.message!!.startsWith("Expected a ")) {
|
||||
LOG.warn(e)
|
||||
status = HttpResponseStatus.BAD_REQUEST
|
||||
}
|
||||
else {
|
||||
LOG.error(e)
|
||||
status = HttpResponseStatus.INTERNAL_SERVER_ERROR
|
||||
}
|
||||
status.send(context.channel(), request, ExceptionUtil.getThrowableText(e))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, InvocationTargetException::class)
|
||||
protected open fun isHostTrusted(request: FullHttpRequest, urlDecoder: QueryStringDecoder): Boolean {
|
||||
@Suppress("DEPRECATION")
|
||||
return isHostTrusted(request)
|
||||
}
|
||||
|
||||
@Deprecated("Use {@link #isHostTrusted(FullHttpRequest, QueryStringDecoder)}")
|
||||
@Throws(InterruptedException::class, InvocationTargetException::class)
|
||||
// e.g. upsource trust to configured host
|
||||
protected open fun isHostTrusted(request: FullHttpRequest): Boolean {
|
||||
if (request.isSignedRequest()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val referrer = request.origin ?: request.referrer
|
||||
val host = try {
|
||||
if (referrer == null) null else URI(referrer).host.nullize()
|
||||
}
|
||||
catch (ignored: URISyntaxException) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (host != null) {
|
||||
if (NetUtils.isLocalhost(host)) {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
trustedOrigins.getIfPresent(host)?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isTrusted = false
|
||||
ApplicationManager.getApplication().invokeAndWait({
|
||||
AppIcon.getInstance().requestAttention(null, true)
|
||||
isTrusted = showYesNoDialog(IdeBundle.message("warning.use.rest.api", getServiceName(), host ?: "unknown host"), "title.use.rest.api")
|
||||
if (host != null) {
|
||||
trustedOrigins.put(host, isTrusted)
|
||||
}
|
||||
}, ModalityState.any())
|
||||
return isTrusted
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error or send response using [.sendOk], [.send]
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
abstract fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String?
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.util;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -65,7 +65,7 @@ public abstract class PropertiesComponent extends SimpleModificationTracker {
|
||||
|
||||
public abstract void setValues(@NonNls @NotNull String name, String[] values);
|
||||
|
||||
public static PropertiesComponent getInstance(Project project) {
|
||||
public static PropertiesComponent getInstance(@NotNull Project project) {
|
||||
return ServiceManager.getService(project, PropertiesComponent.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -306,13 +307,23 @@ public class PerformanceWatcher implements Disposable {
|
||||
for (int i = 0; i < commonPart.size() && i < stackTraceElements.length; i++) {
|
||||
StackTraceElement el1 = commonPart.get(commonPart.size() - i - 1);
|
||||
StackTraceElement el2 = stackTraceElements[stackTraceElements.length - i - 1];
|
||||
if (!el1.equals(el2)) {
|
||||
if (!compareStackTraceElements(el1, el2)) {
|
||||
return commonPart.subList(commonPart.size() - i, commonPart.size());
|
||||
}
|
||||
}
|
||||
return commonPart;
|
||||
}
|
||||
|
||||
// same as java.lang.StackTraceElement.equals, but do not care about the line number
|
||||
private static boolean compareStackTraceElements(StackTraceElement el1, StackTraceElement el2) {
|
||||
if (el1 == el2) {
|
||||
return true;
|
||||
}
|
||||
return el1.getClassName().equals(el2.getClassName()) &&
|
||||
Objects.equals(el1.getMethodName(), el2.getMethodName()) &&
|
||||
Objects.equals(el1.getFileName(), el2.getFileName());
|
||||
}
|
||||
|
||||
private class SwingThreadRunnable implements Runnable {
|
||||
private final long myCreationMillis;
|
||||
|
||||
|
||||
+2
-3
@@ -424,10 +424,9 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase i
|
||||
DefaultActionGroup actionGroup = new DefaultActionGroup();
|
||||
if (executionConsole instanceof BuildView) {
|
||||
actionGroup.addAll(((BuildView)executionConsole).getSwitchActions());
|
||||
actionGroup.add(new ShowExecutionErrorsOnlyAction((BuildView)executionConsole));
|
||||
actionGroup.add(BuildTreeFilters.createFilteringActionsGroup((BuildView)executionConsole));
|
||||
}
|
||||
DefaultExecutionResult executionResult =
|
||||
new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
|
||||
DefaultExecutionResult executionResult = new DefaultExecutionResult(executionConsole, processHandler, actionGroup.getChildren(null));
|
||||
executionResult.setRestartActions(restartActions);
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
+2
-2
@@ -554,7 +554,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
Messages.setTestDialog(new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
public int show(@NotNull String message) {
|
||||
counter.set(counter.get() + 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -566,7 +566,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
Messages.setTestDialog(new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
public int show(@NotNull String message) {
|
||||
counter.set(counter.get() + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -32,13 +32,4 @@ public abstract class Expression {
|
||||
public String getAdvertisingText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link Expression#calculateResult(com.intellij.codeInsight.template.ExpressionContext)} or
|
||||
* {@link Expression#calculateQuickResult(com.intellij.codeInsight.template.ExpressionContext)}
|
||||
* require committed PSI for their calculation or false otherwise
|
||||
*/
|
||||
public boolean requiresCommittedPSI() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,6 @@ public class TextExpression extends Expression {
|
||||
return calculateResult(expressionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext expressionContext) {
|
||||
return LookupElement.EMPTY_ARRAY;
|
||||
|
||||
@@ -244,7 +244,7 @@ public abstract class SdkType implements SdkTypeId {
|
||||
* @param parentComponent the parent component for showing the dialog.
|
||||
* @param selectedSdk current selected sdk in parentComponent
|
||||
* @param sdkCreatedCallback the callback to which the created SDK is passed.
|
||||
* @implSpec method's implementations should not add sdk to the jdkTable neither invoke {@link SdkType#setupSdkPaths}. Only create and
|
||||
* @implSpec method's implementations should not add sdk to the jdkTable neither invoke {@link SdkType#setupSdkPaths}. Only create and
|
||||
* and pass to the callback. The rest is done by {@link com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel#setupSdk(Sdk, Consumer)}
|
||||
*/
|
||||
public void showCustomCreateUI(@NotNull SdkModel sdkModel,
|
||||
|
||||
+23
-7
@@ -64,7 +64,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
|
||||
private final List<TextRange> myPreviewRangesToHighlight = new ArrayList<>();
|
||||
|
||||
private final Editor myEditor;
|
||||
private final EditorEx myEditor;
|
||||
private final CodeStyleSettings mySettings;
|
||||
private boolean myShouldUpdatePreview;
|
||||
protected static final int[] ourWrappings =
|
||||
@@ -134,7 +134,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Editor createEditor() {
|
||||
private EditorEx createEditor() {
|
||||
if (StringUtil.isEmpty(getPreviewText())) return null;
|
||||
EditorFactory editorFactory = EditorFactory.getInstance();
|
||||
Document editorDocument = editorFactory.createDocument("");
|
||||
@@ -159,7 +159,7 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
protected void updatePreview(boolean useDefaultSample) {
|
||||
if (myEditor == null) return;
|
||||
updateEditor(useDefaultSample);
|
||||
updatePreviewHighlighter((EditorEx)myEditor);
|
||||
updatePreviewHighlighter(myEditor);
|
||||
}
|
||||
|
||||
private void updateEditor(boolean useDefaultSample) {
|
||||
@@ -167,7 +167,6 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
return;
|
||||
}
|
||||
|
||||
Project project = ProjectUtil.guessCurrentProject(getPanel());
|
||||
if (myEditor.isDisposed()) return;
|
||||
|
||||
if (myLastDocumentModificationStamp != myEditor.getDocument().getModificationStamp()) {
|
||||
@@ -177,8 +176,19 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
myTextToReformat = StringUtil.convertLineSeparators(ObjectUtils.notNull(getPreviewText(), ""));
|
||||
}
|
||||
|
||||
updateEditorState(true);
|
||||
}
|
||||
|
||||
protected void setEditorText(@NotNull String text, boolean updateHighlighter) {
|
||||
myTextToReformat = StringUtil.convertLineSeparators(text);
|
||||
if (updateHighlighter) updatePreviewHighlighter(myEditor);
|
||||
updateEditorState(false);
|
||||
}
|
||||
|
||||
private void updateEditorState(boolean collectChanges) {
|
||||
int currOffs = myEditor.getScrollingModel().getVerticalScrollOffset();
|
||||
CommandProcessor.getInstance().executeCommand(project, () -> replaceText(project), null, null);
|
||||
Project project = ProjectUtil.guessCurrentProject(getPanel());
|
||||
CommandProcessor.getInstance().executeCommand(project, () -> replaceText(project, collectChanges), null, null);
|
||||
|
||||
myEditor.getSettings().setRightMargin(getAdjustedRightMargin());
|
||||
myLastDocumentModificationStamp = myEditor.getDocument().getModificationStamp();
|
||||
@@ -192,11 +202,11 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
|
||||
protected abstract int getRightMargin();
|
||||
|
||||
private void replaceText(final Project project) {
|
||||
private void replaceText(final Project project, boolean collectChanges) {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
try {
|
||||
Document beforeReformat = null;
|
||||
if (myEditor.getDocument().getTextLength() > 0) {
|
||||
if (collectChanges && myEditor.getDocument().getTextLength() > 0) {
|
||||
beforeReformat = collectChangesBeforeCurrentSettingsAppliance(project);
|
||||
}
|
||||
|
||||
@@ -596,6 +606,12 @@ public abstract class CodeStyleAbstractPanel implements Disposable, ComponentHig
|
||||
return myCurrentSettings;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected CodeStyleSettings getModelSettings() {
|
||||
CodeStyleSchemesModel model = myModel;
|
||||
return model != null ? model.getCloneSettings(model.getSelectedScheme()) : null;
|
||||
}
|
||||
|
||||
public void setupCopyFromMenu(JPopupMenu copyMenu) {
|
||||
copyMenu.removeAll();
|
||||
}
|
||||
|
||||
+1
-1
@@ -584,7 +584,7 @@ public abstract class TabbedLanguageCodeStylePanel extends CodeStyleAbstractPane
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CommonCodeStyleSettings.IndentOptions getIndentOptions(CodeStyleSettings settings) {
|
||||
protected CommonCodeStyleSettings.IndentOptions getIndentOptions(CodeStyleSettings settings) {
|
||||
return settings.getCommonSettings(getDefaultLanguage()).getIndentOptions();
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -164,6 +164,7 @@ public class ArrangementGroupingRulesControl extends JBTable {
|
||||
ArrangementGroupingComponent component = (ArrangementGroupingComponent)value;
|
||||
component.setRowIndex(row + 1);
|
||||
component.setHighlight(myRowUnderMouse == row || table.isRowSelected(row));
|
||||
component.revalidate();
|
||||
return component;
|
||||
}
|
||||
else if (value instanceof ArrangementRepresentationAware) {
|
||||
|
||||
+3
-2
@@ -311,12 +311,13 @@
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="3b679"/>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="combobox.richcopy.color.scheme"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="3b679" class="javax.swing.JComboBox" binding="myRichCopyColorSchemeComboBox">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
@@ -420,7 +421,7 @@
|
||||
</component>
|
||||
<component id="87620" class="javax.swing.JComboBox" binding="myStripTrailingSpacesCombo">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
|
||||
@@ -121,7 +121,7 @@ public abstract class AbstractViewManager implements ViewManager, BuildProgressL
|
||||
toolbarActions.removeAll();
|
||||
toolbarActions.addAll(view.createConsoleActions());
|
||||
toolbarActions.add(new PinBuildViewAction(buildsView));
|
||||
toolbarActions.add(new ShowExecutionErrorsOnlyAction(view));
|
||||
toolbarActions.add(BuildTreeFilters.createFilteringActionsGroup(view));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -11,6 +11,10 @@ import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.ui.ConsoleView;
|
||||
import com.intellij.execution.ui.ConsoleViewContentType;
|
||||
import com.intellij.execution.ui.ExecutionConsole;
|
||||
import com.intellij.ide.CommonActionsManager;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.OccurenceNavigator;
|
||||
import com.intellij.ide.OccurenceNavigatorSupport;
|
||||
import com.intellij.ide.actions.EditSourceAction;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.ide.util.treeView.NodeRenderer;
|
||||
@@ -56,10 +60,8 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
@@ -77,7 +79,7 @@ import static com.intellij.util.ui.UIUtil.getTreeSelectionForeground;
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildConsoleView, Filterable<ExecutionNode> {
|
||||
public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildConsoleView, Filterable<ExecutionNode>, OccurenceNavigator {
|
||||
private static final Logger LOG = Logger.getInstance(BuildTreeConsoleView.class);
|
||||
|
||||
@NonNls private static final String TREE = "tree";
|
||||
@@ -94,13 +96,14 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
private final Tree myTree;
|
||||
private final ExecutionNode myRootNode;
|
||||
private final ExecutionNode myBuildProgressRootNode;
|
||||
@Nullable
|
||||
private volatile Predicate<ExecutionNode> myExecutionTreeFilter;
|
||||
private final Set<Predicate<ExecutionNode>> myNodeFilters;
|
||||
private final ProblemOccurrenceNavigatorSupport myOccurrenceNavigatorSupport;
|
||||
|
||||
public BuildTreeConsoleView(Project project,
|
||||
BuildDescriptor buildDescriptor,
|
||||
@Nullable ExecutionConsole executionConsole,
|
||||
@NotNull BuildViewSettingsProvider buildViewSettingsProvider) {
|
||||
myNodeFilters = ContainerUtil.newConcurrentSet();
|
||||
myProject = project;
|
||||
myWorkingDir = FileUtil.toSystemIndependentName(buildDescriptor.getWorkingDir());
|
||||
|
||||
@@ -125,6 +128,9 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
new ConsoleViewHandler(myProject, myTree, myBuildProgressRootNode, this, executionConsole, buildViewSettingsProvider);
|
||||
myThreeComponentsSplitter.setSecondComponent(myConsoleViewHandler.getComponent());
|
||||
myPanel.add(myThreeComponentsSplitter, BorderLayout.CENTER);
|
||||
BuildTreeFilters.install(this);
|
||||
myRootNode.setFilter(getFilter());
|
||||
myOccurrenceNavigatorSupport = new ProblemOccurrenceNavigatorSupport(myTree);
|
||||
}
|
||||
|
||||
private void installContextMenu(@NotNull StartBuildEvent startBuildEvent) {
|
||||
@@ -143,8 +149,14 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
ActionUtil.copyFrom(edit, "EditSource");
|
||||
group.add(edit);
|
||||
group.addSeparator();
|
||||
group.add(new ShowExecutionErrorsOnlyAction(this));
|
||||
|
||||
group.addAll(BuildTreeFilters.createFilteringActionsGroup(this));
|
||||
group.addSeparator();
|
||||
//Initializing prev and next occurrences actions
|
||||
final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
|
||||
final AnAction prevAction = actionsManager.createPrevOccurenceAction(this);
|
||||
group.add(prevAction);
|
||||
final AnAction nextAction = actionsManager.createNextOccurenceAction(this);
|
||||
group.add(nextAction);
|
||||
PopupHandler.installPopupHandler(myTree, group, "BuildView");
|
||||
});
|
||||
}
|
||||
@@ -162,20 +174,35 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Nullable
|
||||
public Predicate<ExecutionNode> getFilter() {
|
||||
return myExecutionTreeFilter;
|
||||
return executionNode -> executionNode == getBuildProgressRootNode() ||
|
||||
executionNode.isRunning() ||
|
||||
executionNode.isFailed() ||
|
||||
myNodeFilters.stream().anyMatch(predicate -> predicate.test(executionNode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(@Nullable Predicate<ExecutionNode> executionTreeFilter) {
|
||||
myExecutionTreeFilter = executionTreeFilter;
|
||||
ExecutionNode buildProgressRootNode = getBuildProgressRootNode();
|
||||
public void addFilter(@NotNull Predicate<ExecutionNode> executionTreeFilter) {
|
||||
myNodeFilters.add(executionTreeFilter);
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFilter(@NotNull Predicate<ExecutionNode> filter) {
|
||||
myNodeFilters.remove(filter);
|
||||
updateFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Predicate<ExecutionNode> filter) {
|
||||
return myNodeFilters.contains(filter);
|
||||
}
|
||||
|
||||
private void updateFilter() {
|
||||
ExecutionNode rootElement = getRootElement();
|
||||
Predicate<ExecutionNode> predicate = executionTreeFilter == null ? null :
|
||||
node -> node == buildProgressRootNode || executionTreeFilter.test(node);
|
||||
rootElement.setFilter(predicate);
|
||||
rootElement.setFilter(getFilter());
|
||||
scheduleUpdate(rootElement);
|
||||
}
|
||||
|
||||
@@ -315,6 +342,38 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextOccurence() {
|
||||
return myOccurrenceNavigatorSupport.hasNextOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousOccurence() {
|
||||
return myOccurrenceNavigatorSupport.hasPreviousOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goNextOccurence() {
|
||||
return myOccurrenceNavigatorSupport.goNextOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goPreviousOccurence() {
|
||||
return myOccurrenceNavigatorSupport.goPreviousOccurence();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getNextOccurenceActionName() {
|
||||
return myOccurrenceNavigatorSupport.getNextOccurenceActionName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPreviousOccurenceActionName() {
|
||||
return myOccurrenceNavigatorSupport.getPreviousOccurenceActionName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TreeVisitor visitor(@NotNull ExecutionNode executionNode) {
|
||||
return path -> {
|
||||
@@ -502,7 +561,13 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
if (eventKind == MessageEvent.Kind.ERROR || eventKind == MessageEvent.Kind.WARNING) {
|
||||
SimpleNode p = parentNode;
|
||||
do {
|
||||
((ExecutionNode)p).reportChildMessageKind(eventKind);
|
||||
ExecutionNode executionNode = (ExecutionNode)p;
|
||||
boolean warningUpdate = eventKind == MessageEvent.Kind.WARNING && !executionNode.hasWarnings();
|
||||
executionNode.reportChildMessageKind(eventKind);
|
||||
if (warningUpdate) {
|
||||
executionNode.cleanUpCache();
|
||||
scheduleUpdate(executionNode);
|
||||
}
|
||||
}
|
||||
while ((p = p.getParent()) instanceof ExecutionNode);
|
||||
scheduleUpdate(getRootElement());
|
||||
@@ -745,6 +810,41 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProblemOccurrenceNavigatorSupport extends OccurenceNavigatorSupport {
|
||||
ProblemOccurrenceNavigatorSupport(final Tree tree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Navigatable createDescriptorForNode(@NotNull DefaultMutableTreeNode node) {
|
||||
Object userObject = node.getUserObject();
|
||||
if (!(userObject instanceof ExecutionNode)) {
|
||||
return null;
|
||||
}
|
||||
final ExecutionNode executionNode = (ExecutionNode)userObject;
|
||||
if (executionNode.getChildCount() > 0 || !executionNode.hasWarnings() && !executionNode.isFailed()) {
|
||||
return null;
|
||||
}
|
||||
List<Navigatable> navigatables = executionNode.getNavigatables();
|
||||
if (!navigatables.isEmpty()) {
|
||||
return navigatables.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getNextOccurenceActionName() {
|
||||
return IdeBundle.message("action.next.problem");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPreviousOccurenceActionName() {
|
||||
return IdeBundle.message("action.previous.problem");
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyTree extends Tree {
|
||||
private MyTree(TreeModel treemodel) {
|
||||
super(treemodel);
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.execution.ui.ConsoleViewContentType;
|
||||
import com.intellij.execution.ui.ExecutionConsole;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.execution.ui.actions.CloseAction;
|
||||
import com.intellij.ide.OccurenceNavigator;
|
||||
import com.intellij.ide.actions.PinActiveTabAction;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -56,7 +57,7 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public class BuildView extends CompositeView<ExecutionConsole>
|
||||
implements BuildProgressListener, ConsoleView, DataProvider, Filterable<ExecutionNode> {
|
||||
implements BuildProgressListener, ConsoleView, DataProvider, Filterable<ExecutionNode>, OccurenceNavigator {
|
||||
public static final String CONSOLE_VIEW_NAME = "consoleView";
|
||||
private final AtomicReference<StartBuildEvent> myStartBuildEventRef = new AtomicReference<>();
|
||||
private final BuildDescriptor myBuildDescriptor;
|
||||
@@ -365,17 +366,75 @@ public class BuildView extends CompositeView<ExecutionConsole>
|
||||
return getEventView() != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Predicate<ExecutionNode> getFilter() {
|
||||
BuildTreeConsoleView eventView = getEventView();
|
||||
return eventView == null ? null : eventView.getFilter();
|
||||
return eventView == null ? executionNode -> true : eventView.getFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilter(Predicate<ExecutionNode> filter) {
|
||||
public void addFilter(@NotNull Predicate<ExecutionNode> filter) {
|
||||
BuildTreeConsoleView eventView = getEventView();
|
||||
if (eventView != null) {
|
||||
eventView.setFilter(filter);
|
||||
eventView.addFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFilter(@NotNull Predicate<ExecutionNode> filter) {
|
||||
BuildTreeConsoleView eventView = getEventView();
|
||||
if (eventView != null) {
|
||||
eventView.removeFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Predicate<ExecutionNode> filter) {
|
||||
BuildTreeConsoleView eventView = getEventView();
|
||||
return eventView != null && eventView.contains(filter);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private OccurenceNavigator getOccurenceNavigator() {
|
||||
BuildTreeConsoleView eventView = getEventView();
|
||||
if (eventView != null) return eventView;
|
||||
ExecutionConsole executionConsole = getConsoleView();
|
||||
if (executionConsole instanceof OccurenceNavigator) {
|
||||
return (OccurenceNavigator)executionConsole;
|
||||
}
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextOccurence() {
|
||||
return getOccurenceNavigator().hasNextOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousOccurence() {
|
||||
return getOccurenceNavigator().hasPreviousOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goNextOccurence() {
|
||||
return getOccurenceNavigator().goNextOccurence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goPreviousOccurence() {
|
||||
return getOccurenceNavigator().goPreviousOccurence();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getNextOccurenceActionName() {
|
||||
return getOccurenceNavigator().getNextOccurenceActionName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getPreviousOccurenceActionName() {
|
||||
return getOccurenceNavigator().getPreviousOccurenceActionName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ExecutionNode extends CachingSimpleNode {
|
||||
private static final Icon NODE_ICON_DEFAULT = ICON_16;
|
||||
private static final Icon NODE_ICON_RUNNING = new AnimatedIcon.FS();
|
||||
|
||||
private final Collection<ExecutionNode> myChildrenList = new ConcurrentLinkedDeque<>(); //ContainerUtil.newSmartList();
|
||||
private final Collection<ExecutionNode> myChildrenList = new ConcurrentLinkedDeque<>();
|
||||
private final AtomicInteger myErrors = new AtomicInteger();
|
||||
private final AtomicInteger myWarnings = new AtomicInteger();
|
||||
private long startTime;
|
||||
@@ -228,6 +228,11 @@ public class ExecutionNode extends CachingSimpleNode {
|
||||
return endTime <= 0 && !isSkipped(myResult) && !isFailed(myResult);
|
||||
}
|
||||
|
||||
public boolean hasWarnings() {
|
||||
return myWarnings.get() > 0 ||
|
||||
(myResult instanceof MessageEventResult && ((MessageEventResult)myResult).getKind() == MessageEvent.Kind.WARNING);
|
||||
}
|
||||
|
||||
public boolean isFailed() {
|
||||
return isFailed(myResult) ||
|
||||
myErrors.get() > 0 ||
|
||||
@@ -295,25 +300,6 @@ public class ExecutionNode extends CachingSimpleNode {
|
||||
}
|
||||
}
|
||||
|
||||
ExecutionNode copy(ExecutionNode parent) {
|
||||
ExecutionNode copy = new ExecutionNode(myProject, parent);
|
||||
copy.startTime = startTime;
|
||||
copy.endTime = endTime;
|
||||
copy.myTitle = myTitle;
|
||||
copy.myTooltip = myTooltip;
|
||||
copy.myHint = myHint;
|
||||
copy.myResult = myResult;
|
||||
copy.myAutoExpandNode = myAutoExpandNode;
|
||||
copy.myNavigatable = myNavigatable;
|
||||
copy.myPreferredIconValue = myPreferredIconValue;
|
||||
copy.myErrors.set(myErrors.get());
|
||||
copy.myWarnings.set(myWarnings.get());
|
||||
copy.myFilter = myFilter;
|
||||
copy.myName = myName;
|
||||
copy.myClosedIcon = myClosedIcon;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private String getCurrentHint() {
|
||||
String hint = myHint;
|
||||
int warnings = myWarnings.get();
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.build;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface Filterable<T> {
|
||||
boolean isFilteringEnabled();
|
||||
|
||||
@NotNull
|
||||
Predicate<T> getFilter();
|
||||
|
||||
void setFilter(Predicate<T> filter);
|
||||
void addFilter(@NotNull Predicate<T> filter);
|
||||
|
||||
void removeFilter(@NotNull Predicate<T> filter);
|
||||
|
||||
boolean contains(@NotNull Predicate<T> filter);
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.build;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.actionSystem.ToggleAction;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public class ShowExecutionErrorsOnlyAction extends ToggleAction implements DumbAware {
|
||||
private static final Predicate<ExecutionNode> ERROR_FILTER = node -> node.isRunning() || node.isFailed();
|
||||
private final Filterable<ExecutionNode> myFilterable;
|
||||
private final String mySelectionStateKey;
|
||||
|
||||
public ShowExecutionErrorsOnlyAction(@NotNull Filterable<ExecutionNode> filterable) {
|
||||
this(filterable, "build.toolwindow.show.errors.only.selection.state");
|
||||
}
|
||||
|
||||
public ShowExecutionErrorsOnlyAction(@NotNull Filterable<ExecutionNode> filterable, @Nullable String selectionStateKey) {
|
||||
super("Show Errors Only", "Show Errors Only", AllIcons.General.Error);
|
||||
myFilterable = filterable;
|
||||
mySelectionStateKey = selectionStateKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelected(@NotNull AnActionEvent e) {
|
||||
final Presentation presentation = e.getPresentation();
|
||||
if (!Registry.is("build.view.side-by-side", true)) {
|
||||
presentation.setVisible(false);
|
||||
return false;
|
||||
}
|
||||
boolean filteringEnabled = myFilterable.isFilteringEnabled();
|
||||
presentation.setEnabledAndVisible(filteringEnabled);
|
||||
if (filteringEnabled && mySelectionStateKey != null &&
|
||||
PropertiesComponent.getInstance().getBoolean(mySelectionStateKey, true) &&
|
||||
myFilterable.getFilter() != ERROR_FILTER) {
|
||||
setSelected(e, true);
|
||||
}
|
||||
|
||||
return myFilterable.getFilter() == ERROR_FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(@NotNull AnActionEvent e, boolean state) {
|
||||
myFilterable.setFilter(state ? ERROR_FILTER : null);
|
||||
if (mySelectionStateKey != null) {
|
||||
PropertiesComponent.getInstance().setValue(mySelectionStateKey, state, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
@file:JvmName("BuildTreeFilters")
|
||||
package com.intellij.build
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.ToggleAction
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.util.function.Predicate
|
||||
|
||||
private val SUCCESSFUL_STEPS_FILTER = Predicate { node: ExecutionNode -> !node.isFailed && !node.hasWarnings() }
|
||||
private val WARNINGS_FILTER = Predicate { node: ExecutionNode -> node.hasWarnings() }
|
||||
|
||||
@ApiStatus.Experimental
|
||||
fun createFilteringActionsGroup(filterable: Filterable<ExecutionNode>): DefaultActionGroup {
|
||||
val actionGroup = DefaultActionGroup("Filters", true)
|
||||
actionGroup.templatePresentation.icon = AllIcons.Actions.Show
|
||||
actionGroup.add(WarningsToggleAction(filterable))
|
||||
actionGroup.add(SuccessfulStepsToggleAction(filterable))
|
||||
return actionGroup
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
fun install(filterable: Filterable<ExecutionNode>) {
|
||||
val filteringEnabled = filterable.isFilteringEnabled
|
||||
if (!filteringEnabled) return
|
||||
SuccessfulStepsToggleAction.install(filterable)
|
||||
WarningsToggleAction.install(filterable)
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
open class FilterToggleAction constructor(text: String,
|
||||
private val stateKey: String?,
|
||||
private val filterable: Filterable<ExecutionNode>,
|
||||
private val filter: Predicate<ExecutionNode>,
|
||||
private val defaultState: Boolean) : ToggleAction(text), DumbAware {
|
||||
override fun isSelected(e: AnActionEvent): Boolean {
|
||||
val presentation = e.presentation
|
||||
if (!Registry.`is`("build.view.side-by-side", true)) {
|
||||
presentation.isVisible = false
|
||||
return false
|
||||
}
|
||||
val filteringEnabled = filterable.isFilteringEnabled
|
||||
presentation.isEnabledAndVisible = filteringEnabled
|
||||
if (filteringEnabled && stateKey != null &&
|
||||
PropertiesComponent.getInstance().getBoolean(stateKey, defaultState) &&
|
||||
!filterable.contains(filter)) {
|
||||
setSelected(e, true)
|
||||
}
|
||||
|
||||
return filterable.contains(filter)
|
||||
}
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
if (state) {
|
||||
filterable.addFilter(filter)
|
||||
}
|
||||
else {
|
||||
filterable.removeFilter(filter)
|
||||
}
|
||||
if (stateKey != null) {
|
||||
PropertiesComponent.getInstance().setValue(stateKey, state, defaultState)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun install(filterable: Filterable<ExecutionNode>,
|
||||
filter: Predicate<ExecutionNode>,
|
||||
stateKey: String,
|
||||
defaultState: Boolean) {
|
||||
if (PropertiesComponent.getInstance().getBoolean(stateKey, defaultState) &&
|
||||
!filterable.contains(filter)) {
|
||||
filterable.addFilter(filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
class SuccessfulStepsToggleAction(filterable: Filterable<ExecutionNode>) :
|
||||
FilterToggleAction("Show Successful Steps", STATE_KEY, filterable, SUCCESSFUL_STEPS_FILTER, false), DumbAware {
|
||||
companion object {
|
||||
private const val STATE_KEY = "build.toolwindow.show.successful.steps.selection.state"
|
||||
fun install(filterable: Filterable<ExecutionNode>) {
|
||||
install(filterable, SUCCESSFUL_STEPS_FILTER, STATE_KEY, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
class WarningsToggleAction(filterable: Filterable<ExecutionNode>) :
|
||||
FilterToggleAction("Show Warnings", STATE_KEY, filterable, WARNINGS_FILTER, true), DumbAware {
|
||||
companion object {
|
||||
private const val STATE_KEY = "build.toolwindow.show.warnings.selection.state"
|
||||
fun install(filterable: Filterable<ExecutionNode>) {
|
||||
install(filterable, WARNINGS_FILTER, STATE_KEY, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-18
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.navigation.ChooseByNameContributor;
|
||||
import com.intellij.navigation.ChooseByNameContributorEx;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileTypes.FileNameMatcher;
|
||||
@@ -33,9 +34,10 @@ import com.intellij.psi.impl.source.resolve.reference.impl.providers.*;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FindSymbolParameters;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -116,13 +118,15 @@ public class FilePathCompletionContributor extends CompletionContributor {
|
||||
|
||||
|
||||
if (contextFile != null) {
|
||||
final String[] fileNames = getAllNames(project);
|
||||
final Set<String> resultNames = new TreeSet<>();
|
||||
for (String fileName : fileNames) {
|
||||
if (filenameMatchesPrefixOrType(fileName, prefix, set.getSuitableFileTypes(), parameters.getInvocationCount())) {
|
||||
Set<String> resultNames = new TreeSet<>();
|
||||
String finalPrefix = prefix;
|
||||
processAllNames(project, fileName -> {
|
||||
if (filenameMatchesPrefixOrType(fileName, finalPrefix, set.getSuitableFileTypes(),
|
||||
parameters.getInvocationCount())) {
|
||||
resultNames.add(fileName);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex();
|
||||
|
||||
@@ -180,16 +184,19 @@ public class FilePathCompletionContributor extends CompletionContributor {
|
||||
extend(CompletionType.BASIC, psiElement(), provider);
|
||||
}
|
||||
|
||||
private static boolean filenameMatchesPrefixOrType(final String fileName, final String prefix, final FileType[] suitableFileTypes, final int invocationCount) {
|
||||
final boolean prefixMatched = prefix.length() == 0 || StringUtil.startsWithIgnoreCase(fileName, prefix);
|
||||
private static boolean filenameMatchesPrefixOrType(String fileName,
|
||||
String prefix,
|
||||
FileType[] suitableFileTypes,
|
||||
int invocationCount) {
|
||||
boolean prefixMatched = prefix.length() == 0 || StringUtil.startsWithIgnoreCase(fileName, prefix);
|
||||
if (prefixMatched && (suitableFileTypes.length == 0 || invocationCount > 2)) return true;
|
||||
|
||||
if (prefixMatched) {
|
||||
final String extension = FileUtilRt.getExtension(fileName);
|
||||
String extension = FileUtilRt.getExtension(fileName);
|
||||
if (extension.length() == 0) return false;
|
||||
|
||||
for (final FileType fileType : suitableFileTypes) {
|
||||
for (final FileNameMatcher matcher : FileTypeManager.getInstance().getAssociations(fileType)) {
|
||||
for (FileType fileType : suitableFileTypes) {
|
||||
for (FileNameMatcher matcher : FileTypeManager.getInstance().getAssociations(fileType)) {
|
||||
if (FileNameMatcherEx.acceptsCharSequence(matcher, fileName)) return true;
|
||||
}
|
||||
}
|
||||
@@ -237,12 +244,15 @@ public class FilePathCompletionContributor extends CompletionContributor {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String[] getAllNames(@NotNull final Project project) {
|
||||
Set<String> names = new HashSet<>();
|
||||
final ChooseByNameContributor[] nameContributors = ChooseByNameContributor.FILE_EP_NAME.getExtensions();
|
||||
for (final ChooseByNameContributor contributor : nameContributors) {
|
||||
private static void processAllNames(@NotNull Project project, @NotNull Processor<String> processor) {
|
||||
for (ChooseByNameContributor contributor : ChooseByNameContributor.FILE_EP_NAME.getExtensionList()) {
|
||||
try {
|
||||
ContainerUtil.addAll(names, contributor.getNames(project, false));
|
||||
if (contributor instanceof ChooseByNameContributorEx) {
|
||||
((ChooseByNameContributorEx)contributor).processNames(processor, FindSymbolParameters.searchScopeFor(project, false), null);
|
||||
}
|
||||
else {
|
||||
ContainerUtil.process(contributor.getNames(project, false), processor);
|
||||
}
|
||||
}
|
||||
catch (ProcessCanceledException ex) {
|
||||
// index corruption detected, ignore
|
||||
@@ -251,8 +261,6 @@ public class FilePathCompletionContributor extends CompletionContributor {
|
||||
LOG.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtil.toStringArray(names);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
-5
@@ -38,11 +38,6 @@ public class EmptyExpression extends Expression {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -340,7 +340,7 @@ public class IntentionHintComponent implements Disposable, ScrollAwareHint {
|
||||
if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) {
|
||||
AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS);
|
||||
AnActionEvent event = AnActionEvent.createFromInputEvent(e, ActionPlaces.MOUSE_SHORTCUT, null, SimpleDataContext.getProjectContext(project));
|
||||
ActionsCollector.getInstance().record(project, action, event, null);
|
||||
ActionsCollector.getInstance().record(project, action, event, file.getLanguage());
|
||||
|
||||
showPopup(true);
|
||||
}
|
||||
|
||||
@@ -39,11 +39,6 @@ public class ConstantNode extends Expression {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
return LookupElement.EMPTY_ARRAY;
|
||||
|
||||
@@ -35,11 +35,6 @@ public class EmptyNode extends Expression {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
return null;
|
||||
|
||||
@@ -45,8 +45,4 @@ public class SelectionNode extends Expression {
|
||||
return calculateQuickResult(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,13 +832,11 @@ public class TemplateState implements Disposable {
|
||||
int start = mySegments.getSegmentStart(segmentNumber);
|
||||
int end = mySegments.getSegmentEnd(segmentNumber);
|
||||
|
||||
if(!isQuick || expressionNode.requiresCommittedPSI()) {
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
|
||||
}
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
|
||||
PsiFile psiFile = getPsiFile();
|
||||
PsiElement element = psiFile != null ? psiFile.findElementAt(start) : null;
|
||||
if (element != null && !element.isValid()) {
|
||||
element = null;
|
||||
if (element != null) {
|
||||
PsiUtilCore.ensureValid(element);
|
||||
}
|
||||
|
||||
ExpressionContext context = createExpressionContext(start);
|
||||
@@ -860,8 +858,8 @@ public class TemplateState implements Disposable {
|
||||
if (defaultValue != null && resultIsNullOrEmpty) {
|
||||
result = defaultValue.calculateResult(context);
|
||||
}
|
||||
if (element != null && !element.isValid()) {
|
||||
element = null;
|
||||
if (element != null) {
|
||||
PsiUtilCore.ensureValid(element);
|
||||
}
|
||||
if (result == null || result.equalsToText(oldValue, element)) return;
|
||||
|
||||
|
||||
@@ -47,11 +47,6 @@ public class VariableNode extends Expression {
|
||||
return TemplateManagerImpl.getTemplateState(context.getEditor()).getVariableValue(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return myInitialValue != null && myInitialValue.requiresCommittedPSI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupElement[] calculateLookupItems(ExpressionContext context) {
|
||||
if (myInitialValue == null){
|
||||
|
||||
@@ -332,7 +332,8 @@ public class ExecutorRegistryImpl extends ExecutorRegistry implements Disposable
|
||||
if (configuration instanceof CompoundRunConfiguration) {
|
||||
RunManager runManager = RunManager.getInstance(project);
|
||||
for (SettingsAndEffectiveTarget settingsAndEffectiveTarget : ((CompoundRunConfiguration)configuration).getConfigurationsWithEffectiveRunTargets()) {
|
||||
run(project, settingsAndEffectiveTarget.getConfiguration(), runManager.findSettings(configuration), dataContext);
|
||||
RunConfiguration subConfiguration = settingsAndEffectiveTarget.getConfiguration();
|
||||
run(project, subConfiguration, runManager.findSettings(subConfiguration), dataContext);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+3
-4
@@ -349,7 +349,7 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction[] getChildren(@Nullable AnActionEvent e) {
|
||||
if (e == null) return EMPTY_ARRAY;
|
||||
if (e == null || mySelectedTab == null) return EMPTY_ARRAY;
|
||||
return mySelectedTab.actions.toArray(EMPTY_ARRAY);
|
||||
}
|
||||
});
|
||||
@@ -424,7 +424,6 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
contributorsPanel.add(tab);
|
||||
myTabs.add(tab);
|
||||
});
|
||||
switchToTab(allTab);
|
||||
|
||||
return contributorsPanel;
|
||||
}
|
||||
@@ -1427,8 +1426,8 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
Boolean enabled = mySelectedTab.getContributor().map(contributor -> contributor.showInFindResults()).orElse(true);
|
||||
e.getPresentation().setEnabled(enabled);
|
||||
SearchEverywhereContributor<?> contributor = mySelectedTab == null ? null : mySelectedTab.contributor;
|
||||
e.getPresentation().setEnabled(contributor == null || contributor.showInFindResults());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-14
@@ -8,7 +8,11 @@ import com.intellij.ide.scratch.RootType;
|
||||
import com.intellij.ide.scratch.ScratchFileService;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -19,6 +23,7 @@ import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.DigestUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -28,8 +33,10 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p> Extensions root type provide a common interface for plugins to access resources that are modifiable by the user. </p>
|
||||
@@ -191,17 +198,24 @@ public class ExtensionsRootType extends RootType {
|
||||
private static List<URL> getBundledResourceUrls(@NotNull PluginId pluginId, @NotNull String path) throws IOException {
|
||||
String resourcesPath = EXTENSIONS_PATH + "/" + path;
|
||||
IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
|
||||
ClassLoader pluginClassLoader = plugin != null ? plugin.getPluginClassLoader() : null;
|
||||
Set<URL> urls = plugin == null ? null : ContainerUtil.newLinkedHashSet(ContainerUtil.toList(pluginClassLoader.getResources(resourcesPath)));
|
||||
if (urls == null) return ContainerUtil.emptyList();
|
||||
if (plugin == null) return ContainerUtil.emptyList();
|
||||
ClassLoader pluginClassLoader = plugin.getPluginClassLoader();
|
||||
final Enumeration<URL> resources = pluginClassLoader.getResources(resourcesPath);
|
||||
if (resources == null) return ContainerUtil.emptyList();
|
||||
if (plugin.getUseIdeaClassLoader()) return ContainerUtil.toList(resources);
|
||||
|
||||
PluginId corePluginId = PluginId.findId(PluginManagerCore.CORE_PLUGIN_ID);
|
||||
IdeaPluginDescriptor corePlugin = ObjectUtils.notNull(PluginManager.getPlugin(corePluginId));
|
||||
ClassLoader coreClassLoader = corePlugin.getPluginClassLoader();
|
||||
if (coreClassLoader != pluginClassLoader && !plugin.getUseIdeaClassLoader() && !pluginId.equals(corePluginId)) {
|
||||
urls.removeAll(ContainerUtil.toList(coreClassLoader.getResources(resourcesPath)));
|
||||
final Set<URL> urls = ContainerUtil.newLinkedHashSet(ContainerUtil.toList(resources));
|
||||
// exclude parent classloader resources from list
|
||||
final List<ClassLoader> dependentPluginClassLoaders = StreamEx.of(plugin.getDependentPluginIds())
|
||||
.map(PluginManager::getPlugin)
|
||||
.nonNull()
|
||||
.map(PluginDescriptor::getPluginClassLoader)
|
||||
.without(pluginClassLoader)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ClassLoader classLoader : dependentPluginClassLoaders) {
|
||||
urls.removeAll(ContainerUtil.toList(classLoader.getResources(resourcesPath)));
|
||||
}
|
||||
|
||||
return ContainerUtil.newArrayList(urls);
|
||||
}
|
||||
|
||||
@@ -269,13 +283,23 @@ public class ExtensionsRootType extends RootType {
|
||||
FileUtil.rename(file, newName);
|
||||
}
|
||||
|
||||
private void extractBundledExtensionsIfNeeded(@NotNull PluginId pluginId) throws IOException {
|
||||
if (!ApplicationManager.getApplication().isDispatchThread()) return;
|
||||
|
||||
private void extractBundledExtensionsIfNeeded(@NotNull PluginId pluginId) {
|
||||
IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId);
|
||||
if (plugin == null || !ResourceVersions.getInstance().shouldUpdateResourcesOf(plugin)) return;
|
||||
|
||||
extractBundledResources(pluginId, "");
|
||||
ResourceVersions.getInstance().resourcesUpdated(plugin);
|
||||
Task.Backgroundable extractResourcesInBackground =
|
||||
new Task.Backgroundable(null, "Extracting bundled extensions for plugin: " + pluginId.getIdString()) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
try {
|
||||
extractBundledResources(pluginId, "");
|
||||
ApplicationManager.getApplication().invokeLater(() -> ResourceVersions.getInstance().resourcesUpdated(plugin));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
LOG.warn("Failed to extract bundled extensions for plugin: " + plugin.getName(), ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
ProgressManager.getInstance().run(extractResourcesInBackground);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-6
@@ -4,8 +4,12 @@ package com.intellij.ide.extensionResources;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -29,11 +33,20 @@ public class RestoreBundledExtensionsAction extends DumbAwareAction {
|
||||
|
||||
assert file != null && pluginId != null && path != null;
|
||||
|
||||
try {
|
||||
extensionsRootType.extractBundledResources(pluginId, path);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
ExtensionsRootType.LOG.warn("Failed to extract bundled extensions for " + file.getPath(), ex);
|
||||
}
|
||||
Task.Backgroundable extractResourcesInBackground = new Task.Backgroundable(
|
||||
e.getProject(),
|
||||
"Extracting bundled extensions for plugin: " + pluginId.getIdString()) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
try {
|
||||
extensionsRootType.extractBundledResources(pluginId, path);
|
||||
VirtualFileManager.getInstance().refreshWithoutFileWatcher(true);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
ExtensionsRootType.LOG.warn("Failed to extract bundled extensions for " + file.getPath(), ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
ProgressManager.getInstance().run(extractResourcesInBackground);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -16,6 +16,10 @@ import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.PomTargetPsiElement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Processors;
|
||||
@@ -180,7 +184,8 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModelE
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(System.currentTimeMillis() - contributorStarted + "," + contributor + ",");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
NavigationItem[] itemsByName = contributor.getItemsByName(name, parameters.getLocalPatternName(), myProject, searchInLibraries);
|
||||
for (NavigationItem item : itemsByName) {
|
||||
canceled.checkCanceled();
|
||||
@@ -188,6 +193,9 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModelE
|
||||
PluginException.logPluginError(LOG, "null item from contributor " + contributor + " for name " + name, null, contributor.getClass());
|
||||
continue;
|
||||
}
|
||||
VirtualFile file = item instanceof PsiElement && !(item instanceof PomTargetPsiElement)
|
||||
? PsiUtilCore.getVirtualFile((PsiElement)item) : null;
|
||||
if (file != null && !parameters.getSearchScope().contains(file)) continue;
|
||||
|
||||
if (acceptItem(item)) {
|
||||
items.add(item);
|
||||
|
||||
-31
@@ -19,49 +19,18 @@ import com.intellij.navigation.ChooseByNameContributorEx;
|
||||
import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiFileSystemItem;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Processors;
|
||||
import com.intellij.util.indexing.FindSymbolParameters;
|
||||
import com.intellij.util.indexing.IdFilter;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultFileNavigationContributor implements ChooseByNameContributorEx, DumbAware {
|
||||
private static final Logger LOG = Logger.getInstance(DefaultFileNavigationContributor.class);
|
||||
@Override
|
||||
@NotNull
|
||||
public String[] getNames(Project project, boolean includeNonProjectItems) {
|
||||
THashSet<String> names = new THashSet<>(1000);
|
||||
IdFilter filter = IdFilter.getProjectIdFilter(project, includeNonProjectItems);
|
||||
processNames(s -> {
|
||||
names.add(s);
|
||||
return true;
|
||||
}, FindSymbolParameters.searchScopeFor(project, includeNonProjectItems), filter);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("All names retrieved2:" + names.size());
|
||||
}
|
||||
return ArrayUtil.toStringArray(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public NavigationItem[] getItemsByName(String name, final String pattern, Project project, boolean includeNonProjectItems) {
|
||||
List<NavigationItem> result = new ArrayList<>();
|
||||
Processor<NavigationItem> processor = Processors.cancelableCollectProcessor(result);
|
||||
processElementsWithName(name, processor, FindSymbolParameters.wrap(pattern, project, includeNonProjectItems));
|
||||
|
||||
return result.isEmpty() ? NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY : result.toArray(NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNames(@NotNull final Processor<String> processor, @NotNull GlobalSearchScope scope, IdFilter filter) {
|
||||
|
||||
@@ -15,13 +15,18 @@
|
||||
*/
|
||||
package com.intellij.navigation;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.indexing.FindSymbolParameters;
|
||||
import com.intellij.util.indexing.IdFilter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface ChooseByNameContributorEx extends ChooseByNameContributor {
|
||||
|
||||
void processNames(@NotNull Processor<String> processor,
|
||||
@@ -31,4 +36,24 @@ public interface ChooseByNameContributorEx extends ChooseByNameContributor {
|
||||
void processElementsWithName(@NotNull String name,
|
||||
@NotNull Processor<NavigationItem> processor,
|
||||
@NotNull FindSymbolParameters parameters);
|
||||
|
||||
/** @deprecated Use {@link #processNames(Processor, GlobalSearchScope, IdFilter)} instead */
|
||||
@Deprecated
|
||||
@Override
|
||||
@NotNull
|
||||
default String[] getNames(Project project, boolean includeNonProjectItems) {
|
||||
List<String> result = new ArrayList<>();
|
||||
processNames(result::add, FindSymbolParameters.searchScopeFor(project, includeNonProjectItems), null);
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link #processElementsWithName(String, Processor, FindSymbolParameters)} instead */
|
||||
@Deprecated
|
||||
@Override
|
||||
@NotNull
|
||||
default NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
|
||||
List<NavigationItem> result = new ArrayList<>();
|
||||
processElementsWithName(name, result::add, FindSymbolParameters.simple(project, includeNonProjectItems));
|
||||
return result.isEmpty() ? NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY : result.toArray(NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.intellij.openapi.wm.IdeFocusManager
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class JBProtocolNavigateCommand : JBProtocolCommand(NAVIGATE_COMMAND) {
|
||||
internal class JBProtocolNavigateCommand : JBProtocolCommand(NAVIGATE_COMMAND) {
|
||||
override fun perform(target: String, parameters: Map<String, String>) {
|
||||
// handles URLs of the following types:
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ public class DisposerDebugger implements UiDebuggerExtension, Disposable {
|
||||
}
|
||||
|
||||
private void queueUpdate() {
|
||||
UIUtil.invokeLaterIfNeeded(() -> myTreeBuilder.refilter());
|
||||
UIUtil.invokeLaterIfNeeded(() -> myTreeBuilder.refilterAsync());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -275,7 +275,7 @@ public class DisposerDebugger implements UiDebuggerExtension, Disposable {
|
||||
|
||||
public void clear() {
|
||||
myModificationToFilter = Disposer.getTree().getModification();
|
||||
myTreeBuilder.refilter();
|
||||
myTreeBuilder.refilterAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FileContent;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import com.intellij.util.indexing.impl.InputDataDiffBuilder;
|
||||
import com.intellij.util.indexing.impl.forward.AbstractForwardIndexAccessor;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
@@ -20,7 +19,7 @@ import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
class StubUpdatingForwardIndexAccessor extends AbstractForwardIndexAccessor<Integer, SerializedStubTree, IndexedStubs> {
|
||||
class StubUpdatingForwardIndexAccessor extends AbstractForwardIndexAccessor<Integer, SerializedStubTree, IndexedStubs , FileContent> {
|
||||
StubUpdatingForwardIndexAccessor() {super(new DataExternalizer<IndexedStubs>() {
|
||||
private volatile boolean myEnsuredStubElementTypesLoaded;
|
||||
|
||||
@@ -69,10 +68,10 @@ class StubUpdatingForwardIndexAccessor extends AbstractForwardIndexAccessor<Inte
|
||||
}
|
||||
});}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IndexedStubs convertToDataType(@NotNull InputData<Integer, SerializedStubTree> data) {
|
||||
return getIndexedStubs(data.getKeyValues());
|
||||
public IndexedStubs convertToDataType(@Nullable Map<Integer, SerializedStubTree> map,
|
||||
@Nullable FileContent content) {
|
||||
return getIndexedStubs(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -97,11 +97,6 @@ public class MyLookupExpression extends Expression {
|
||||
return new TextResult(myName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresCommittedPSI() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdvertisingText() {
|
||||
return myAdvertisementText;
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.EverythingGlobalScope;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -69,12 +68,12 @@ public class FindSymbolParameters {
|
||||
}
|
||||
|
||||
public static FindSymbolParameters wrap(@NotNull String pattern, @NotNull Project project, boolean searchInLibraries) {
|
||||
return new FindSymbolParameters(pattern, pattern, searchScopeFor(project, searchInLibraries), null);
|
||||
return new FindSymbolParameters(pattern, pattern, searchScopeFor(project, searchInLibraries),
|
||||
IdFilter.getProjectIdFilter(project, searchInLibraries));
|
||||
}
|
||||
|
||||
public static FindSymbolParameters wrap(@NotNull String pattern, @NotNull GlobalSearchScope scope) {
|
||||
IdFilter idFilter = IdFilter.getProjectIdFilter(ObjectUtils.notNull(scope.getProject()), scope.isSearchInLibraries());
|
||||
return new FindSymbolParameters(pattern, pattern, scope, idFilter);
|
||||
return new FindSymbolParameters(pattern, pattern, scope, null);
|
||||
}
|
||||
|
||||
public static FindSymbolParameters simple(@NotNull Project project, boolean searchInLibraries) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing;
|
||||
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import com.intellij.util.indexing.impl.InputDataDiffBuilder;
|
||||
import com.intellij.util.indexing.impl.MapInputDataDiffBuilder;
|
||||
import com.intellij.util.indexing.impl.forward.AbstractMapForwardIndexAccessor;
|
||||
@@ -14,8 +13,8 @@ import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
class HashIdForwardIndexAccessor<Key, Value, Input>
|
||||
extends AbstractMapForwardIndexAccessor<Key, Value, Integer>
|
||||
implements IntForwardIndexAccessor<Key, Value> {
|
||||
extends AbstractMapForwardIndexAccessor<Key, Value, Integer, Input>
|
||||
implements IntForwardIndexAccessor<Key, Value, Input> {
|
||||
private final UpdatableSnapshotInputMappingIndex<Key, Value, Input> mySnapshotInputMappingIndex;
|
||||
|
||||
HashIdForwardIndexAccessor(@NotNull UpdatableSnapshotInputMappingIndex<Key, Value, Input> snapshotInputMappingIndex) {
|
||||
@@ -36,13 +35,12 @@ class HashIdForwardIndexAccessor<Key, Value, Input>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int serializeIndexedDataToInt(@NotNull InputData<Key, Value> data) {
|
||||
return data == InputData.empty() ? 0 : ((HashedInputData)data).getHashId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer convertToDataType(@NotNull InputData<Key, Value> data) {
|
||||
return serializeIndexedDataToInt(data);
|
||||
public int convertToInt(@Nullable Map<Key, Value> map, @Nullable Input content) {
|
||||
try {
|
||||
return mySnapshotInputMappingIndex.getHashId(content);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing;
|
||||
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class HashedInputData<Key, Value> extends InputData<Key, Value> {
|
||||
private final int myHashId;
|
||||
|
||||
protected HashedInputData(@NotNull Map<Key, Value> values, int hashId) {
|
||||
super(values);
|
||||
myHashId = hashId;
|
||||
}
|
||||
|
||||
public int getHashId() {
|
||||
return myHashId;
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,10 @@ public class SharedMapForwardIndex implements ForwardIndex {
|
||||
|
||||
// only verification purpose
|
||||
@Nullable
|
||||
private final AbstractForwardIndexAccessor<?, ?, ?> myAccessor;
|
||||
private final AbstractForwardIndexAccessor<?, ?, ?, ?> myAccessor;
|
||||
|
||||
public SharedMapForwardIndex(@NotNull IndexExtension<?, ?, ?> extension,
|
||||
@Nullable AbstractForwardIndexAccessor<?, ?, ?> accessor,
|
||||
@Nullable AbstractForwardIndexAccessor<?, ?, ?, ?> accessor,
|
||||
@Nullable File verificationIndexStorageFile,
|
||||
boolean verificationIndexHasChunks) throws IOException {
|
||||
myIndexId = (ID<?, ?>)extension.getName();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing;
|
||||
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -13,5 +12,5 @@ import java.util.Map;
|
||||
@ApiStatus.Experimental
|
||||
public interface SnapshotInputMappingIndex<Key, Value, Input> extends Closeable {
|
||||
@Nullable
|
||||
InputData<Key, Value> readData(@NotNull Input content) throws IOException;
|
||||
Map<Key, Value> readData(@NotNull Input content) throws IOException;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.intellij.util.CompressionUtil;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.intellij.util.indexing.impl.DebugAssertions;
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import com.intellij.util.indexing.impl.forward.AbstractForwardIndexAccessor;
|
||||
import com.intellij.util.indexing.impl.forward.PersistentMapBasedForwardIndex;
|
||||
import com.intellij.util.io.*;
|
||||
@@ -71,13 +70,12 @@ class SnapshotInputMappings<Key, Value, Input> implements UpdatableSnapshotInput
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputData<Key, Value> readData(@NotNull Input content) throws IOException {
|
||||
public Map<Key, Value> readData(@NotNull Input content) throws IOException {
|
||||
Map<Key, Value> data = null;
|
||||
int hashId = 0;
|
||||
|
||||
if (doReadSavedPersistentData) {
|
||||
if (myContents == null || !myContents.isBusyReading() || DebugAssertions.EXTRA_SANITY_CHECKS) { // avoid blocking read, we can calculate index value
|
||||
hashId = getHashId(content);
|
||||
int hashId = getHashId(content);
|
||||
ByteArraySequence bytes = readContents(hashId);
|
||||
|
||||
if (bytes != null) {
|
||||
@@ -98,32 +96,25 @@ class SnapshotInputMappings<Key, Value, Input> implements UpdatableSnapshotInput
|
||||
}
|
||||
}
|
||||
}
|
||||
return data == null ? null : new HashedInputData<>(data, hashId);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputData<Key, Value> putData(@Nullable Input content, @NotNull InputData<Key, Value> data) throws IOException {
|
||||
int hashId;
|
||||
InputData<Key, Value> result;
|
||||
if (data instanceof HashedInputData) {
|
||||
hashId = ((HashedInputData<Key, Value>)data).getHashId();
|
||||
result = data;
|
||||
} else {
|
||||
hashId = getHashId(content);
|
||||
result = hashId == 0 ? InputData.empty() : new HashedInputData<>(data.getKeyValues(), hashId);
|
||||
}
|
||||
boolean saved = savePersistentData(data.getKeyValues(), hashId);
|
||||
public void putData(@Nullable Input content, @NotNull Map<Key, Value> data) throws IOException {
|
||||
int hashId = getHashId(content);
|
||||
boolean saved = savePersistentData(data, hashId);
|
||||
if (DebugAssertions.EXTRA_SANITY_CHECKS) {
|
||||
if (saved) {
|
||||
try {
|
||||
myIndexingTrace.put(hashId, getContentDebugData(content) + "," + ExceptionUtil.getThrowableText(new Throwable()));
|
||||
myIndexingTrace.put(hashId, getContentDebugData(content) +
|
||||
"," +
|
||||
ExceptionUtil.getThrowableText(new Throwable()));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
LOG.error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -132,7 +123,8 @@ class SnapshotInputMappings<Key, Value, Input> implements UpdatableSnapshotInput
|
||||
return "[" + content.getFile().getPath() + ";" + content.getFileType().getName() + ";" + content.getCharset() + "]";
|
||||
}
|
||||
|
||||
private int getHashId(@Nullable Input content) throws IOException {
|
||||
@Override
|
||||
public int getHashId(@Nullable Input content) throws IOException {
|
||||
return content == null ? 0 : getHashOfContent((FileContent) content);
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.indexing;
|
||||
|
||||
import com.intellij.util.indexing.impl.InputData;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -14,7 +13,9 @@ public interface UpdatableSnapshotInputMappingIndex<Key, Value, Input> extends S
|
||||
@NotNull
|
||||
Map<Key, Value> readData(int hashId) throws IOException;
|
||||
|
||||
InputData<Key, Value> putData(@NotNull Input content, @NotNull InputData<Key, Value> data) throws IOException;
|
||||
void putData(@NotNull Input content, @NotNull Map<Key, Value> data) throws IOException;
|
||||
|
||||
int getHashId(@Nullable Input content) throws IOException;
|
||||
|
||||
void flush() throws IOException;
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
protected VfsAwareMapReduceIndex(@NotNull IndexExtension<Key, Value, Input> extension,
|
||||
@NotNull IndexStorage<Key, Value> storage,
|
||||
@Nullable ForwardIndex forwardIndexMap,
|
||||
@Nullable ForwardIndexAccessor<Key, Value> forwardIndexAccessor,
|
||||
@Nullable ForwardIndexAccessor<Key, Value, ?, Input> forwardIndexAccessor,
|
||||
@Nullable SnapshotInputMappingIndex<Key, Value, Input> snapshotInputMappings) {
|
||||
super(extension, storage, forwardIndexMap, forwardIndexAccessor, null);
|
||||
SharedIndicesData.registerIndex((ID<Key, Value>)myIndexId, extension);
|
||||
@@ -89,8 +89,8 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected InputData<Key, Value> mapInput(@Nullable Input content) {
|
||||
InputData<Key, Value> data;
|
||||
protected Map<Key, Value> mapInput(@Nullable Input content) {
|
||||
Map<Key, Value> data;
|
||||
boolean containsSnapshotData = true;
|
||||
if (mySnapshotInputMappings != null && !myInMemoryMode.get() && content != null) {
|
||||
try {
|
||||
@@ -108,7 +108,7 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
data = super.mapInput(content);
|
||||
if (!containsSnapshotData) {
|
||||
try {
|
||||
return ((UpdatableSnapshotInputMappingIndex)mySnapshotInputMappings).putData(content, data);
|
||||
((UpdatableSnapshotInputMappingIndex)mySnapshotInputMappings).putData(content, data);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -140,13 +140,13 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateForwardIndex(int inputId, @NotNull InputData<Key, Value> data) throws IOException {
|
||||
protected void updateForwardIndex(int inputId, @NotNull Map<Key, Value> data, @Nullable Object forwardIndexData) throws IOException {
|
||||
if (myInMemoryMode.get()) {
|
||||
synchronized (myInMemoryKeysAndValues) {
|
||||
myInMemoryKeysAndValues.put(inputId, data.getKeyValues());
|
||||
myInMemoryKeysAndValues.put(inputId, data);
|
||||
}
|
||||
} else {
|
||||
super.updateForwardIndex(inputId, data);
|
||||
super.updateForwardIndex(inputId, data, forwardIndexData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
}
|
||||
if (getForwardIndexAccessor() instanceof AbstractMapForwardIndexAccessor) {
|
||||
ByteArraySequence serializedInputData = getForwardIndexMap().get(fileId);
|
||||
AbstractMapForwardIndexAccessor<Key, Value, ?> forwardIndexAccessor = (AbstractMapForwardIndexAccessor<Key, Value, ?>)getForwardIndexAccessor();
|
||||
AbstractMapForwardIndexAccessor<Key, Value, ?, Input> forwardIndexAccessor = (AbstractMapForwardIndexAccessor<Key, Value, ?, Input>)getForwardIndexAccessor();
|
||||
return forwardIndexAccessor.convertToInputDataMap(serializedInputData);
|
||||
}
|
||||
// in future we will get rid of forward index for SingleEntryFileBasedIndexExtension
|
||||
@@ -319,7 +319,7 @@ public class VfsAwareMapReduceIndex<Key, Value, Input> extends MapReduceIndex<Ke
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <Key, Value> ForwardIndexAccessor<Key, Value> getForwardIndexAccessor(@NotNull IndexExtension<Key, Value, ?> indexExtension) {
|
||||
private static <Key, Value, Input> ForwardIndexAccessor<Key, Value, ?, Input> getForwardIndexAccessor(@NotNull IndexExtension<Key, Value, Input> indexExtension) {
|
||||
if (!shouldCreateForwardIndex(indexExtension)) return null;
|
||||
return new MapForwardIndexAccessor<>(new InputMapExternalizer<>(indexExtension));
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
* 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 com.intellij.ide.ui.search;
|
||||
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
public final class ConfigurableHit {
|
||||
|
||||
private final Set<Configurable> myNameHits = new LinkedHashSet<>();
|
||||
private final Set<Configurable> myContentHits = new LinkedHashSet<>();
|
||||
|
||||
private final Set<Configurable> myNameFullHit = new LinkedHashSet<>();
|
||||
|
||||
ConfigurableHit() {
|
||||
}
|
||||
|
||||
public Set<Configurable> getNameHits() {
|
||||
return myNameHits;
|
||||
}
|
||||
|
||||
public Set<Configurable> getNameFullHits() {
|
||||
return myNameFullHit;
|
||||
}
|
||||
|
||||
public Set<Configurable> getContentHits() {
|
||||
return myContentHits;
|
||||
}
|
||||
|
||||
public Set<Configurable> getAll() {
|
||||
final LinkedHashSet<Configurable> all = new LinkedHashSet<>(myNameHits.size() + myContentHits.size());
|
||||
all.addAll(myNameHits);
|
||||
all.addAll(myContentHits);
|
||||
return all;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.ui.search
|
||||
|
||||
import com.intellij.openapi.options.Configurable
|
||||
import java.util.*
|
||||
|
||||
data class ConfigurableHit(val contentHits: Set<Configurable>, val nameHits: Set<Configurable>, val nameFullHits: Set<Configurable>) {
|
||||
val all: Set<Configurable>
|
||||
get() {
|
||||
val all = LinkedHashSet<Configurable>(nameHits.size + contentHits.size)
|
||||
all.addAll(nameHits)
|
||||
all.addAll(contentHits)
|
||||
return all
|
||||
}
|
||||
}
|
||||
+8
-13
@@ -1,5 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.ui.search;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -11,22 +10,22 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class SearchableOptionsRegistrar{
|
||||
public static final String SEARCHABLE_OPTIONS_XML = "searchableOptions.xml";
|
||||
|
||||
public static SearchableOptionsRegistrar getInstance(){
|
||||
public static SearchableOptionsRegistrar getInstance() {
|
||||
return ServiceManager.getService(SearchableOptionsRegistrar.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract ConfigurableHit getConfigurables(final ConfigurableGroup[] groups,
|
||||
final DocumentEvent.EventType type,
|
||||
final Set<? extends Configurable> configurables,
|
||||
final String option,
|
||||
final Project project);
|
||||
public abstract ConfigurableHit getConfigurables(@NotNull List<ConfigurableGroup> groups,
|
||||
final DocumentEvent.EventType type,
|
||||
@Nullable Set<? extends Configurable> configurables,
|
||||
@NotNull String option,
|
||||
@Nullable Project project);
|
||||
|
||||
@Nullable
|
||||
public abstract String getInnerPath(SearchableConfigurable configurable, String option);
|
||||
@@ -39,11 +38,7 @@ public abstract class SearchableOptionsRegistrar{
|
||||
|
||||
public abstract Set<String> replaceSynonyms(Set<String> options, SearchableConfigurable configurable);
|
||||
|
||||
public abstract Map<String, Set<String>> findPossibleExtension(@NotNull String prefix, final Project project);
|
||||
|
||||
|
||||
public abstract Set<String> getProcessedWordsWithoutStemming(@NotNull String text);
|
||||
|
||||
public abstract Set<String> getProcessedWords(@NotNull String text);
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.ui.ComponentTreeWatcher;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.JBTreeTraverser;
|
||||
import com.intellij.util.ui.DialogUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
@@ -183,9 +182,7 @@ public class MnemonicHelper extends ComponentTreeWatcher {
|
||||
|
||||
private static class MnemonicFixer implements ContainerListener {
|
||||
void addTo(Component component) {
|
||||
JBTreeTraverser<Component> traverser = UIUtil.uiTraverser(component)
|
||||
.expandAndFilter(o -> !(o instanceof CellRendererPane));
|
||||
for (Component c : traverser) {
|
||||
for (Component c : UIUtil.uiTraverser(component)) {
|
||||
if (c instanceof Container) ((Container)c).addContainerListener(this);
|
||||
if (c instanceof ActionButtonComponent) fixMacMnemonicKeyStroke((JComponent)c, null);
|
||||
MnemonicWrapper.getWrapper(c);
|
||||
@@ -193,9 +190,7 @@ public class MnemonicHelper extends ComponentTreeWatcher {
|
||||
}
|
||||
|
||||
void removeFrom(Component component) {
|
||||
JBTreeTraverser<Component> traverser = UIUtil.uiTraverser(component)
|
||||
.expandAndFilter(o -> !(o instanceof CellRendererPane));
|
||||
for (Container c : traverser.traverse().filter(Container.class)) {
|
||||
for (Container c : UIUtil.uiTraverser(component).filter(Container.class)) {
|
||||
c.removeContainerListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.options;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -23,7 +23,7 @@ public abstract class ShowSettingsUtil {
|
||||
|
||||
public abstract void showSettingsDialog(@Nullable Project project, @NotNull String nameToSelect);
|
||||
|
||||
public abstract void showSettingsDialog(@NotNull final Project project, final Configurable toSelect);
|
||||
public abstract void showSettingsDialog(@NotNull Project project, @Nullable Configurable toSelect);
|
||||
|
||||
public abstract <T extends Configurable> void showSettingsDialog(@Nullable Project project,
|
||||
@NotNull Class<T> configurableClass,
|
||||
@@ -47,13 +47,6 @@ public abstract class ShowSettingsUtil {
|
||||
|
||||
public abstract boolean editConfigurable(Component parent, String dimensionServiceKey, Configurable configurable);
|
||||
|
||||
/**
|
||||
* @deprecated create a new instance of configurable instead
|
||||
* to remove in IDEA 15
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract <T extends Configurable> T findProjectConfigurable(Project project, Class<T> confClass);
|
||||
|
||||
public static String getSettingsMenuName() {
|
||||
return SystemInfo.isMac ? "Preferences" : "Settings";
|
||||
}
|
||||
|
||||
@@ -1,25 +1,14 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.ui;
|
||||
|
||||
public interface TestDialog {
|
||||
TestDialog DEFAULT = new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
};
|
||||
TestDialog OK = new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
TestDialog NO = new TestDialog() {
|
||||
@Override
|
||||
public int show(String message) {
|
||||
return Messages.NO;
|
||||
}
|
||||
};
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
int show(String message);
|
||||
public interface TestDialog {
|
||||
TestDialog DEFAULT = message -> {
|
||||
throw new RuntimeException(message);
|
||||
};
|
||||
TestDialog OK = __ -> Messages.OK;
|
||||
TestDialog NO = __ -> Messages.NO;
|
||||
|
||||
int show(@NotNull String message);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.
|
||||
* 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.
|
||||
*/
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ui.speedSearch;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@@ -27,7 +16,8 @@ public interface ElementFilter<T> {
|
||||
boolean shouldBeShowing(T value);
|
||||
|
||||
interface Active<T> extends ElementFilter<T> {
|
||||
ActionCallback fireUpdate(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now);
|
||||
@NotNull
|
||||
Promise<?> fireUpdate(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now);
|
||||
|
||||
void addListener(Listener<T> listener, Disposable parent);
|
||||
|
||||
@@ -35,14 +25,9 @@ public interface ElementFilter<T> {
|
||||
Set<Listener<T>> myListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
@Override
|
||||
public ActionCallback fireUpdate(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now) {
|
||||
final ActionCallback result = new ActionCallback(myListeners.size());
|
||||
|
||||
for (final Listener<T> myListener : myListeners) {
|
||||
myListener.update(preferredSelection, adjustSelection, now).doWhenProcessed(result.createSetDoneRunnable());
|
||||
}
|
||||
|
||||
return result;
|
||||
@NotNull
|
||||
public Promise<?> fireUpdate(@Nullable T preferredSelection, boolean adjustSelection, boolean now) {
|
||||
return Promises.all(ContainerUtil.map(myListeners, listener -> listener.update(preferredSelection, adjustSelection, now)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,6 +44,7 @@ public interface ElementFilter<T> {
|
||||
}
|
||||
|
||||
interface Listener<T> {
|
||||
ActionCallback update(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now);
|
||||
@NotNull
|
||||
Promise<Void> update(@Nullable final T preferredSelection, final boolean adjustSelection, final boolean now);
|
||||
}
|
||||
}
|
||||
@@ -182,14 +182,20 @@ public class TabLabel extends JPanel implements Accessible {
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
Insets insets = super.getInsets();
|
||||
boolean hasCloseButtonOnTheLeft = false;
|
||||
if (myTabs.isEditorTabs() && UISettings.getShadowInstance().getShowCloseButton() && hasIcons()) {
|
||||
if (UISettings.getShadowInstance().getCloseTabButtonOnTheRight()) {
|
||||
insets.right -= JBUI.scale(4);
|
||||
}
|
||||
else {
|
||||
hasCloseButtonOnTheLeft = true;
|
||||
insets.left -= JBUI.scale(4);
|
||||
}
|
||||
}
|
||||
// We reserve extra space inside file icon for (*) marker but mostly this space looks like wasted one
|
||||
if (myTabs.isEditorTabs() && UISettings.getShadowInstance().getMarkModifiedTabsWithAsterisk() && !hasCloseButtonOnTheLeft) {
|
||||
insets.left -= JBUI.scale(7);
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user