Merge branch 'master' of ssh://git-hosting.labs.intellij.net/idea-community

This commit is contained in:
Sergey Ignatov
2018-06-20 23:42:14 +03:00
20 changed files with 1384 additions and 708 deletions
@@ -217,7 +217,7 @@ public class GlobalSearchScopesCore {
myDirectory = psiDirectory.getVirtualFile();
}
private DirectoryScope(@NotNull Project project, @NotNull VirtualFile directory, final boolean withSubdirectories) {
public DirectoryScope(@NotNull Project project, @NotNull VirtualFile directory, final boolean withSubdirectories) {
super(project);
myWithSubdirectories = withSubdirectories;
myDirectory = directory;
@@ -121,6 +121,7 @@ public class PsiInvalidElementAccessException extends RuntimeException implement
@Nullable Object trace) {
String reason = "Element: " + element.getClass();
if (!recursiveInvocation) {
reason += " #" + element.getLanguage() + " ";
String traceText = !isTrackingInvalidation() ? "disabled" :
trace != null ? "see attachment" :
"no info";
@@ -488,9 +488,25 @@ public class CodeCompletionHandlerBase {
if (context.shouldAddCompletionChar()) {
WriteAction.run(() -> addCompletionChar(context, item, editor, completionChar));
}
checkPsiTextConcistency(indicator);
return context;
}
private static void checkPsiTextConcistency(CompletionProcessEx indicator) {
PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(InjectedLanguageUtil.getTopLevelEditor(indicator.getEditor()), indicator.getProject());
if (psiFile != null) {
if (Registry.is("ide.check.stub.text.consistency") ||
ApplicationManager.getApplication().isUnitTestMode() && !ApplicationInfoImpl.isInStressTest()) {
StubTextInconsistencyException.checkStubTextConsistency(psiFile);
if (PsiDocumentManager.getInstance(psiFile.getProject()).hasUncommitedDocuments()) {
PsiDocumentManager.getInstance(psiFile.getProject()).commitAllDocuments();
StubTextInconsistencyException.checkStubTextConsistency(psiFile);
}
}
}
}
public static void afterItemInsertion(final CompletionProgressIndicator indicator, final Runnable laterRunnable) {
if (laterRunnable != null) {
ActionTracker tracker = new ActionTracker(indicator.getEditor(), indicator);
@@ -551,13 +567,6 @@ public class CodeCompletionHandlerBase {
context.stopWatching();
}
if (psiFile.isValid()) {
if (Registry.is("ide.check.stub.text.consistency") ||
ApplicationManager.getApplication().isUnitTestMode() && !ApplicationInfoImpl.isInStressTest()) {
StubTextInconsistencyException.checkStubTextConsistency(psiFile);
}
}
EditorModificationUtil.scrollToCaret(editor);
});
if (lookup != null) {
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2012 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-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.execution.ui;
import com.intellij.execution.CommonProgramRunConfigurationParameters;
@@ -95,23 +81,8 @@ public class CommonProgramParametersPanel extends JPanel implements PanelWithAnc
ExecutionBundle.message("run.configuration.program.parameters"));
ExpandableTextField expandableTextField = (ExpandableTextField)myProgramParametersComponent.getComponent().getTextField();
if (Registry.is("allow.macros.for.run.configurations")) {
expandableTextField.addExtension(new ExtendableTextComponent.Extension() {
@Override
public Icon getIcon(boolean hovered) {
return AllIcons.General.Add;
}
@Override
public String getTooltip() {
return "Insert Macros";
}
@Override
public Runnable getActionOnClick() {
return () -> MacrosDialog.show(expandableTextField,
macro -> !(macro instanceof PromptingMacro) && !(macro instanceof EditorMacro));
}
});
expandableTextField.addExtension(ExtendableTextComponent.Extension.create(AllIcons.General.Add, "Insert Macros", ()
-> MacrosDialog.show(expandableTextField, macro -> !(macro instanceof PromptingMacro) && !(macro instanceof EditorMacro))));
}
FileChooserDescriptor fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
@@ -1,6 +1,7 @@
// 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.ide;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
public abstract class RemoteDesktopService {
@@ -9,7 +10,7 @@ public abstract class RemoteDesktopService {
}
public static boolean isRemoteSession() {
return getInstance().isRemoteDesktopConnected();
return ApplicationManager.getApplication() != null && getInstance().isRemoteDesktopConnected();
}
public abstract boolean isRemoteDesktopConnected();
@@ -52,31 +52,14 @@ public class ComponentWithBrowseButton<Comp extends JComponent> extends JPanel i
setFocusable(false);
boolean inlineBrowseButton = myComponent instanceof ExtendableTextComponent && Experiments.isFeatureEnabled("inline.browse.button");
if (inlineBrowseButton) {
ExtendableTextComponent.Extension action = new ExtendableTextComponent.Extension() {
@Override
public Icon getIcon(boolean hovered) {
return hovered ? AllIcons.General.OpenDiskHover : AllIcons.General.OpenDisk;
}
@Override
public String getTooltip() {
return UIBundle.message("component.with.browse.button.browse.button.tooltip.text");
}
@Override
public Runnable getActionOnClick() {
return () -> {
for (ActionListener listener : myBrowseButton.getActionListeners()) {
listener.actionPerformed(new ActionEvent(myComponent, ActionEvent.ACTION_PERFORMED, "action"));
}
};
}
};
((ExtendableTextComponent)myComponent).addExtension(action);
((ExtendableTextComponent)myComponent).addExtension(ExtendableTextComponent.Extension.create(
AllIcons.General.OpenDisk, AllIcons.General.OpenDiskHover,
UIBundle.message("component.with.browse.button.browse.button.tooltip.text"),
this::notifyActionListeners));
new DumbAwareAction() {
@Override
public void actionPerformed(AnActionEvent e) {
action.getActionOnClick().run();
notifyActionListeners();
}
}.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)), myComponent);
}
@@ -101,6 +84,11 @@ public class ComponentWithBrowseButton<Comp extends JComponent> extends JPanel i
}
}
private void notifyActionListeners() {
ActionEvent event = new ActionEvent(myComponent, ActionEvent.ACTION_PERFORMED, "action");
for (ActionListener listener: myBrowseButton.getActionListeners()) listener.actionPerformed(event);
}
@NotNull
public final Comp getChildComponent() {
return myComponent;
@@ -111,27 +111,14 @@ public abstract class AbstractFieldPanel extends JPanel {
if (myBrowseButtonActionListener != null) {
if (Experiments.isFeatureEnabled("inline.browse.button") && myComponent instanceof ExtendableTextComponent) {
ExtendableTextComponent.Extension action = new ExtendableTextComponent.Extension() {
@Override
public Icon getIcon(boolean hovered) {
return hovered ? AllIcons.General.OpenDiskHover : AllIcons.General.OpenDisk;
}
@Override
public String getTooltip() {
return UIBundle.message("component.with.browse.button.browse.button.tooltip.text");
}
@Override
public Runnable getActionOnClick() {
return () -> myBrowseButtonActionListener.actionPerformed(new ActionEvent(myComponent, ActionEvent.ACTION_PERFORMED, "action"));
}
};
((ExtendableTextComponent)myComponent).addExtension(action);
((ExtendableTextComponent)myComponent).addExtension(ExtendableTextComponent.Extension.create(
AllIcons.General.OpenDisk, AllIcons.General.OpenDiskHover,
UIBundle.message("component.with.browse.button.browse.button.tooltip.text"),
this::notifyActionListener));
new DumbAwareAction() {
@Override
public void actionPerformed(AnActionEvent e) {
action.getActionOnClick().run();
notifyActionListener();
}
}.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)), myComponent);
@@ -164,6 +151,11 @@ public abstract class AbstractFieldPanel extends JPanel {
}
}
private void notifyActionListener() {
ActionEvent event = new ActionEvent(myComponent, ActionEvent.ACTION_PERFORMED, "action");
if (myBrowseButtonActionListener != null) myBrowseButtonActionListener.actionPerformed(event);
}
public void setBrowseButtonActionListener(ActionListener browseButtonActionListener) {
myBrowseButtonActionListener = browseButtonActionListener;
}
@@ -74,21 +74,17 @@ public class CellRendererPanel extends JPanel {
if (count == 1) {
Rectangle bounds = new Rectangle(getWidth(), getHeight());
JBInsets.removeFrom(bounds, getInsets());
Component child = getComponent(0);
child.setBounds(bounds);
if (child instanceof CellRendererPanel) {
((CellRendererPanel)child).invalidateLayout();
child.doLayout();
}
JComponent child = (JComponent)getComponent(0);
reshapeImpl(child, bounds.x, bounds.y, bounds.width, bounds.height);
invalidateLayout(child);
child.doLayout();
}
else {
invalidateLayout();
invalidateLayout(this);
super.doLayout();
for (int i = 0; i < count; i++) {
Component c = getComponent(i);
if (c instanceof CellRendererPanel) {
c.doLayout();
}
c.doLayout();
}
}
}
@@ -108,10 +104,14 @@ public class CellRendererPanel extends JPanel {
@Override
public void reshape(int x, int y, int w, int h) {
reshapeImpl(this, x, y, w, h);
}
static void reshapeImpl(JComponent component, int x, int y, int w, int h) {
// suppress per-cell "moved" and "resized" events on paint
// see Component#setBounds, Component#notifyNewBounds
AWTAccessor.getComponentAccessor().setLocation(this, x, y);
AWTAccessor.getComponentAccessor().setSize(this, w, h);
AWTAccessor.getComponentAccessor().setLocation(component, x, y);
AWTAccessor.getComponentAccessor().setSize(component, w, h);
}
public void invalidate() {
@@ -121,10 +121,10 @@ public class CellRendererPanel extends JPanel {
super.invalidate();
}
private void invalidateLayout() {
LayoutManager layout = getLayout();
private static void invalidateLayout(JComponent component) {
LayoutManager layout = component.getLayout();
if (layout instanceof LayoutManager2) {
((LayoutManager2)layout).invalidateLayout(this);
((LayoutManager2)layout).invalidateLayout(component);
}
}
@@ -0,0 +1,286 @@
// 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.ui.components.fields;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.ui.Expandable;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBScrollBar;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.fields.ExtendableTextComponent.Extension;
import com.intellij.util.Function;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.ApiStatus.Experimental;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.AncestorListener;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import static com.intellij.openapi.keymap.KeymapUtil.createTooltipText;
import static java.awt.event.InputEvent.CTRL_MASK;
import static java.beans.EventHandler.create;
import static java.util.Collections.singletonList;
import static javax.swing.KeyStroke.getKeyStroke;
import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
@Experimental
public abstract class ExpandableSupport<Source extends JComponent> implements Expandable {
private static final int MINIMAL_WIDTH = 50;
private final Source source;
private final Function<String, String> onShow;
private final Function<String, String> onHide;
private JBPopup popup;
private String title;
private String comment;
public ExpandableSupport(@NotNull Source source, Function<String, String> onShow, Function<String, String> onHide) {
this.source = source;
this.onShow = onShow != null ? onShow : Function.ID;
this.onHide = onHide != null ? onHide : Function.ID;
source.putClientProperty(Expandable.class, this);
source.addAncestorListener(create(AncestorListener.class, this, "collapse"));
source.addComponentListener(create(ComponentListener.class, this, "collapse"));
}
protected abstract boolean isEditable(@NotNull Source source);
protected abstract int getCaretPosition(@NotNull Source source);
protected abstract String getText(@NotNull Source source);
protected abstract void setText(@NotNull Source source, String text, int caret);
protected abstract Insets getInsets(@NotNull Source source);
public final String getTitle() {
return title;
}
public final void setTitle(String title) {
this.title = title;
}
public final String getComment() {
return comment;
}
public final void setComment(String comment) {
this.comment = comment;
}
@Override
public final boolean isExpanded() {
return popup != null;
}
@Override
public final void collapse() {
if (popup != null) popup.cancel();
}
@Override
public final void expand() {
if (popup != null || !source.isEnabled()) return;
Font font = source.getFont();
FontMetrics metrics = font == null ? null : source.getFontMetrics(font);
int height = metrics == null ? 16 : metrics.getHeight();
Dimension size = new Dimension(height * 32, height * 16);
JTextArea area = new JTextArea(onShow.fun(getText(source)));
area.putClientProperty(Expandable.class, this);
area.setEditable(isEditable(source));
area.setBackground(source.getBackground());
area.setForeground(source.getForeground());
area.setFont(font);
area.setWrapStyleWord(true);
area.setLineWrap(true);
setCaretPositionSafely(area, getCaretPosition(source));
UIUtil.addUndoRedoActions(area);
JBScrollPane pane = new JBScrollPane(area);
addExtension(pane, createCollapseExtension(this));
Insets insets = getInsets(source);
//TODO: support scroll pane
JBInsets.addTo(size, insets);
JBInsets.addTo(size, pane.getInsets());
if (size.width - MINIMAL_WIDTH < source.getWidth()) size.width = source.getWidth();
Point location = new Point(0, 0);
SwingUtilities.convertPointToScreen(location, source);
Rectangle screen = ScreenUtil.getScreenRectangle(source);
int bottom = screen.y - location.y + screen.height;
if (bottom < size.height) {
int top = location.y - screen.y + source.getHeight();
if (top < bottom) {
size.height = bottom;
}
else {
if (size.height > top) size.height = top;
location.y -= size.height - source.getHeight();
}
}
pane.setPreferredSize(size);
pane.setViewportBorder(BorderFactory.createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right));
popup = JBPopupFactory
.getInstance()
.createComponentPopupBuilder(pane, area)
.setMayBeParent(true) // this creates a popup as a dialog with alwaysOnTop=false
.setFocusable(true)
.setRequestFocus(true)
.setTitle(title)
.setAdText(comment)
.setLocateByContent(true)
.setCancelOnWindowDeactivation(false)
.setKeyboardActions(singletonList(Pair.create(event -> {
collapse();
Window window = UIUtil.getWindow(source);
if (window != null) {
window.dispatchEvent(
new KeyEvent(source, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), CTRL_MASK, KeyEvent.VK_ENTER, '\r'));
}
}, getKeyStroke(KeyEvent.VK_ENTER, CTRL_MASK))))
.setCancelCallback(() -> {
try {
if (isEditable(source)) {
setText(source, onHide.fun(area.getText()), area.getCaretPosition());
}
popup = null;
return true;
}
catch (Exception ignore) {
return false;
}
}).createPopup();
popup.show(new RelativePoint(location));
}
private static void setCaretPositionSafely(JTextComponent component, int caret) {
try {
component.setCaretPosition(caret);
}
catch (Exception ignored) {
}
}
public static Extension createCollapseExtension(@NotNull Expandable expandable) {
return Extension.create(AllIcons.General.CollapseComponent,
AllIcons.General.CollapseComponentHover,
createTooltipText("Collapse", "CollapseExpandableComponent"),
expandable::collapse);
}
public static Extension createExpandExtension(@NotNull Expandable expandable) {
return Extension.create(AllIcons.General.ExpandComponent,
AllIcons.General.ExpandComponentHover,
createTooltipText("Expand", "ExpandExpandableComponent"),
expandable::collapse);
}
private static void addExtension(@NotNull JScrollPane pane, @NotNull Extension extension) {
pane.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
JScrollBar vsb = pane.getVerticalScrollBar();
if (vsb != null) {
vsb.add(JBScrollBar.LEADING, new JLabel(extension.getIcon(false)) {{
setToolTipText(extension.getTooltip());
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setBorder(JBUI.Borders.empty(5, 0, 5, 5));
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent event) {
setIcon(extension.getIcon(true));
}
@Override
public void mouseExited(MouseEvent event) {
setIcon(extension.getIcon(false));
}
@Override
public void mousePressed(MouseEvent event) {
Runnable action = extension.getActionOnClick();
if (action != null) action.run();
}
});
}});
JViewport viewport = pane.getViewport();
if (viewport != null) {
Component view = viewport.getView();
if (view != null) vsb.setBackground(view.getBackground());
}
}
}
static class TextComponent<Source extends JTextComponent> extends ExpandableSupport<Source> {
TextComponent(@NotNull Source source, Function<String, String> onShow, Function<String, String> onHide) {
super(source, onShow, onHide);
}
@Override
protected boolean isEditable(@NotNull Source source) {
return source.isEditable();
}
@Override
protected int getCaretPosition(@NotNull Source source) {
return source.getCaretPosition();
}
@Override
protected String getText(@NotNull Source source) {
return source.getText();
}
@Override
protected void setText(@NotNull Source source, String text, int caret) {
source.setText(text);
setCaretPositionSafely(source, caret);
}
@Override
protected Insets getInsets(@NotNull Source source) {
Insets insets = source.getInsets();
Insets margin = source.getMargin();
if (margin != null) {
insets.top += margin.top;
insets.left += margin.left;
insets.right += margin.right;
insets.bottom += margin.bottom;
}
return insets;
}
}
static final class Area extends TextComponent<JTextArea> {
Area(@NotNull JTextArea area, Function<String, String> onShow, Function<String, String> onHide) {
super(area, onShow, onHide);
}
@Override
protected void setText(@NotNull JTextArea source, String text, int caret) {
super.setText(source, text, 0);
}
@Override
protected Insets getInsets(@NotNull JTextArea source) {
//noinspection UnnecessaryLocalVariable //TODO: support scroll pane
Insets insets = super.getInsets(source);
return insets;
}
}
}
@@ -1,50 +1,22 @@
// 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.ui.components.fields;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.Expandable;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBScrollBar;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.Function;
import com.intellij.util.execution.ParametersListUtil;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.AncestorListener;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.List;
import static com.intellij.openapi.keymap.KeymapUtil.createTooltipText;
import static java.awt.event.InputEvent.CTRL_MASK;
import static java.beans.EventHandler.create;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static javax.swing.KeyStroke.getKeyStroke;
import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
/**
* @author Sergey Malenkov
*/
public class ExpandableTextField extends ExtendableTextField implements Expandable {
private static final int MINIMAL_WIDTH = 50;
private final Function<String, String> parser;
private final Function<String, String> joiner;
private JBPopup popup;
private String title;
private final ExpandableSupport support;
/**
* Creates an expandable text field with the default line parser/joiner,
@@ -60,39 +32,23 @@ public class ExpandableTextField extends ExtendableTextField implements Expandab
* @see ParametersListUtil
*/
public ExpandableTextField(@NotNull Function<String, List<String>> parser, @NotNull Function<List<String>, String> joiner) {
this.parser = text -> StringUtil.join(parser.fun(text), "\n");
this.joiner = text -> joiner.fun(Arrays.asList(StringUtil.splitByLines(text)));
addAncestorListener(create(AncestorListener.class, this, "collapse"));
addComponentListener(create(ComponentListener.class, this, "collapse"));
Function<String, String> onShow = text -> StringUtil.join(parser.fun(text), "\n");
Function<String, String> onHide = text -> joiner.fun(asList(StringUtil.splitByLines(text)));
support = new ExpandableSupport.TextComponent<>(this, onShow, onHide);
putClientProperty("monospaced", true);
setExtensions(createExtensions());
}
protected List<ExtendableTextComponent.Extension> createExtensions() {
return singletonList(new ExtendableTextComponent.Extension() {
@Override
public Icon getIcon(boolean hovered) {
return hovered ? AllIcons.General.ExpandComponentHover : AllIcons.General.ExpandComponent;
}
@Override
public Runnable getActionOnClick() {
return ExpandableTextField.this::expand;
}
@Override
public String getTooltip() {
return createTooltipText("Expand", "ExpandExpandableComponent");
}
});
return singletonList(ExpandableSupport.createExpandExtension(support));
}
public String getTitle() {
return title;
return support.getTitle();
}
public void setTitle(String title) {
this.title = title;
support.setTitle(title);
}
@Override
@@ -103,123 +59,16 @@ public class ExpandableTextField extends ExtendableTextField implements Expandab
@Override
public void collapse() {
if (popup != null) popup.cancel();
support.collapse();
}
@Override
public boolean isExpanded() {
return popup != null;
return support.isExpanded();
}
@Override
public void expand() {
if (popup != null || !isEnabled()) return;
Font font = getFont();
FontMetrics metrics = font == null ? null : getFontMetrics(font);
int height = metrics == null ? 16 : metrics.getHeight();
Dimension size = new Dimension(height * 32, height * 16);
JTextArea area = new JTextArea(parser.fun(getText()));
area.setEditable(isEditable());
area.setBackground(getBackground());
area.setForeground(getForeground());
area.setFont(font);
area.setWrapStyleWord(true);
area.setLineWrap(true);
area.putClientProperty(Expandable.class, this);
copyCaretPosition(this, area);
UIUtil.addUndoRedoActions(area);
JBScrollPane pane = new JBScrollPane(area);
pane.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
pane.getVerticalScrollBar().setBackground(area.getBackground());
pane.getVerticalScrollBar().add(JBScrollBar.LEADING, new JLabel(AllIcons.General.CollapseComponent) {{
setToolTipText(createTooltipText("Collapse", "CollapseExpandableComponent"));
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setBorder(JBUI.Borders.empty(5, 0, 5, 5));
addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent event) {
setIcon(AllIcons.General.CollapseComponentHover);
}
@Override
public void mouseExited(MouseEvent event) {
setIcon(AllIcons.General.CollapseComponent);
}
@Override
public void mousePressed(MouseEvent event) {
collapse();
}
});
}});
Insets insets = getInsets();
Insets margins = getMargin();
JBInsets.addTo(size, insets);
JBInsets.addTo(size, margins);
JBInsets.addTo(size, pane.getInsets());
if (size.width - MINIMAL_WIDTH < getWidth()) size.width = getWidth();
Point location = new Point(0, 0);
SwingUtilities.convertPointToScreen(location, this);
Rectangle screen = ScreenUtil.getScreenRectangle(this);
int bottom = screen.y - location.y + screen.height;
if (bottom < size.height) {
int top = location.y - screen.y + getHeight();
if (top < bottom) {
size.height = bottom;
}
else {
if (size.height > top) size.height = top;
location.y -= size.height - getHeight();
}
}
pane.setPreferredSize(size);
pane.setViewportBorder(BorderFactory.createEmptyBorder(insets.top + margins.top,
insets.left + margins.left,
insets.bottom + margins.bottom,
insets.right + margins.right));
popup = JBPopupFactory.getInstance()
.createComponentPopupBuilder(pane, area)
.setMayBeParent(true) // this creates a popup as a dialog with alwaysOnTop=false
.setFocusable(true)
.setRequestFocus(true)
.setTitle(title)
.setLocateByContent(true)
.setCancelOnWindowDeactivation(false)
.setKeyboardActions(singletonList(Pair.create(event -> {
collapse();
Window window = UIUtil.getWindow(this);
if (window != null) {
window.dispatchEvent(new KeyEvent(this, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), CTRL_MASK, KeyEvent.VK_ENTER, '\r'));
}
}, getKeyStroke(KeyEvent.VK_ENTER, CTRL_MASK))))
.setCancelCallback(() -> {
try {
if (isEditable()) {
setText(joiner.fun(area.getText()));
copyCaretPosition(area, this);
}
popup = null;
return true;
}
catch (Exception ignore) {
return false;
}
}).createPopup();
popup.show(new RelativePoint(location));
}
private static void copyCaretPosition(JTextComponent source, JTextComponent destination) {
try {
destination.setCaretPosition(source.getCaretPosition());
}
catch (Exception ignored) {
}
support.expand();
}
}
@@ -3,7 +3,7 @@ package com.intellij.ui.components.fields;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.Icon;
import java.util.Collection;
import java.util.List;
@@ -58,5 +58,28 @@ public interface ExtendableTextComponent {
default String getTooltip() {
return null;
}
static Extension create(@NotNull Icon icon, String tooltip, Runnable action) {
return create(icon, icon, tooltip, action);
}
static Extension create(@NotNull Icon defaultIcon, @NotNull Icon hoveredIcon, String tooltip, Runnable action) {
return new Extension() {
@Override
public Icon getIcon(boolean hovered) {
return hovered ? hoveredIcon : defaultIcon;
}
@Override
public String getTooltip() {
return tooltip;
}
@Override
public Runnable getActionOnClick() {
return action;
}
};
}
}
}
@@ -153,6 +153,7 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer,
EditorEx editor = (EditorEx)ObjectUtils.assertNotNull(field.getEditor());
editor.setRendererMode(true);
editor.getSettings().setAnimatedScrolling(false);
editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null));
editor.getSettings().setCaretRowShown(false);
@@ -3,6 +3,7 @@ package com.intellij.structuralsearch;
import com.intellij.lang.Language;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.psi.search.SearchScope;
import com.intellij.structuralsearch.impl.matcher.compiler.StringToConstraintsTransformer;
@@ -23,8 +24,9 @@ public class MatchOptions implements JDOMExternalizable {
private boolean caseSensitiveMatch;
private FileType myFileType;
private Language myDialect;
private SearchScope scope;
private Scopes.Type scopeType;
private String scopeDescriptor;
private String pattern;
private String myPatternContext;
@@ -36,6 +38,9 @@ public class MatchOptions implements JDOMExternalizable {
@NonNls private static final String CONSTRAINT_TAG_NAME = "constraint";
@NonNls private static final String FILE_TYPE_ATTR_NAME = "type";
@NonNls private static final String DIALECT_ATTR_NAME = "dialect";
@NonNls private static final String SCOPE_TYPE = "scope_type";
@NonNls private static final String SCOPE_DESCRIPTOR = "scope_descriptor";
@NonNls public static final String INSTANCE_MODIFIER_NAME = "Instance";
@NonNls public static final String MODIFIER_ANNOTATION_NAME = "Modifier";
@@ -56,6 +61,8 @@ public class MatchOptions implements JDOMExternalizable {
myFileType = options.myFileType;
myDialect = options.myDialect;
scope = options.scope;
scopeType = options.scopeType;
scopeDescriptor = options.scopeDescriptor;
pattern = options.pattern;
myPatternContext = options.myPatternContext;
}
@@ -64,6 +71,12 @@ public class MatchOptions implements JDOMExternalizable {
return new MatchOptions(this);
}
public void initScope(Project project) {
if (scope == null && scopeType != null && scopeDescriptor != null) {
scope = Scopes.createScope(project, scopeDescriptor, scopeType);
}
}
public void addVariableConstraint(@NotNull MatchVariableConstraint constraint) {
variableConstraints.put(constraint.getName(), constraint);
}
@@ -153,8 +166,6 @@ public class MatchOptions implements JDOMExternalizable {
element.setAttribute(RECURSIVE_ATTRIBUTE_NAME,String.valueOf(recursiveSearch));
element.setAttribute(CASESENSITIVE_ATTRIBUTE_NAME,String.valueOf(caseSensitiveMatch));
//@TODO serialize scope!
if (myFileType != null) {
element.setAttribute(FILE_TYPE_ATTR_NAME, myFileType.getName());
}
@@ -163,6 +174,10 @@ public class MatchOptions implements JDOMExternalizable {
element.setAttribute(DIALECT_ATTR_NAME, myDialect.getID());
}
if (scope != null) {
element.setAttribute(SCOPE_TYPE, Scopes.getType(scope).toString()).setAttribute(SCOPE_DESCRIPTOR, Scopes.getDescriptor(scope));
}
for (final MatchVariableConstraint matchVariableConstraint : variableConstraints.values()) {
if (matchVariableConstraint.isArtificial()) continue;
final Element infoElement = new Element(CONSTRAINT_TAG_NAME);
@@ -208,7 +223,14 @@ public class MatchOptions implements JDOMExternalizable {
myDialect = Language.findLanguageByID(attr.getValue());
}
// @TODO deserialize scope
attr = element.getAttribute(SCOPE_TYPE);
if (attr != null) {
scopeType = Scopes.Type.valueOf(attr.getValue());
}
attr = element.getAttribute(SCOPE_DESCRIPTOR);
if (attr != null) {
scopeDescriptor = attr.getValue();
}
for (final Element element1 : element.getChildren(CONSTRAINT_TAG_NAME)) {
final MatchVariableConstraint constraint = new MatchVariableConstraint();
@@ -236,11 +258,9 @@ public class MatchOptions implements JDOMExternalizable {
final MatchOptions matchOptions = (MatchOptions)o;
if (caseSensitiveMatch != matchOptions.caseSensitiveMatch) return false;
//if (enableAutoIdentifySearchTarget != matchOptions.enableAutoIdentifySearchTarget) return false;
if (looseMatching != matchOptions.looseMatching) return false;
if (recursiveSearch != matchOptions.recursiveSearch) return false;
// @TODO support scope
if (scope != null ? !scope.equals(matchOptions.scope) : matchOptions.scope != null) return false;
if (!pattern.equals(matchOptions.pattern)) return false;
if (!variableConstraints.equals(matchOptions.variableConstraints)) return false;
if (myFileType != matchOptions.myFileType) return false;
@@ -254,9 +274,9 @@ public class MatchOptions implements JDOMExternalizable {
int result = (looseMatching ? 1 : 0);
result = 29 * result + (recursiveSearch ? 1 : 0);
result = 29 * result + (caseSensitiveMatch ? 1 : 0);
// @TODO support scope
result = 29 * result + pattern.hashCode();
result = 29 * result + variableConstraints.hashCode();
if (scope != null) result = 29 * result + scope.hashCode();
if (myFileType != null) result = 29 * result + myFileType.hashCode();
if (myDialect != null) result = 29 * result + myDialect.hashCode();
return result;
@@ -0,0 +1,92 @@
// 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.structuralsearch;
import com.intellij.ide.util.scopeChooser.ScopeChooserUtils;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopesCore;
import com.intellij.psi.search.ProjectScopeImpl;
import com.intellij.psi.search.SearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public final class Scopes {
private Scopes() {}
public static Type getType(@Nullable SearchScope scope) {
if (scope instanceof ProjectScopeImpl || scope == null) {
return Type.PROJECT;
}
else if (scope instanceof ModuleWithDependenciesScope) {
return Type.MODULE;
}
else if (scope instanceof GlobalSearchScopesCore.DirectoryScope) {
return Type.DIRECTORY;
}
else {
return Type.NAMED;
}
}
public static String getDescriptor(SearchScope scope) {
if (scope instanceof ProjectScopeImpl || scope == null) {
return "";
}
else if (scope instanceof ModuleWithDependenciesScope) {
return ((ModuleWithDependenciesScope)scope).getModule().getName();
}
else if (scope instanceof GlobalSearchScopesCore.DirectoryScope) {
final GlobalSearchScopesCore.DirectoryScope directoryScope = (GlobalSearchScopesCore.DirectoryScope)scope;
final String url = directoryScope.getDirectory().getPresentableUrl();
return directoryScope.isWithSubdirectories() ? "*" + url : url;
}
else {
return scope.getDisplayName();
}
}
public static SearchScope createScope(@NotNull Project project, @NotNull String descriptor, @NotNull Type scopeType) {
if (scopeType == Type.PROJECT) {
return GlobalSearchScope.projectScope(project);
}
else if (scopeType == Type.MODULE) {
final Module module = ModuleManager.getInstance(project).findModuleByName(descriptor);
if (module != null) {
return GlobalSearchScope.moduleScope(module);
}
}
else if (scopeType == Type.DIRECTORY) {
final boolean recursive = StringUtil.startsWithChar(descriptor, '*');
if (recursive) {
descriptor = descriptor.substring(1);
}
final String path = FileUtil.toSystemIndependentName(descriptor.substring(1));
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
if (virtualFile == null) return null;
return new GlobalSearchScopesCore.DirectoryScope(project, virtualFile, recursive);
}
else if (scopeType == Type.NAMED) {
return ScopeChooserUtils.findScopeByName(project, descriptor);
}
assert false;
return null;
}
public enum Type {
PROJECT,
MODULE,
DIRECTORY,
NAMED
}
}
@@ -51,6 +51,7 @@ public class ConfigurationManager implements PersistentStateComponent<Element> {
configurations.clear();
historyConfigurations.clear();
readConfigurations(state, configurations, historyConfigurations);
historyConfigurations.forEach(c -> c.getMatchOptions().initScope(myProject));
}
public void addHistoryConfiguration(Configuration configuration) {
@@ -75,6 +76,7 @@ public class ConfigurationManager implements PersistentStateComponent<Element> {
@NotNull Collection<Configuration> configurations,
@NotNull Collection<Configuration> historyConfigurations) {
for (final Configuration configuration : configurations) {
configuration.getMatchOptions().setScope(null);
saveConfiguration(element, configuration);
}
@@ -7,24 +7,17 @@ import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.FixedSizeButton;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileProvider;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.util.ui.JBUI;
import com.intellij.ui.ComboboxWithBrowseButton;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,129 +26,87 @@ import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.function.BiConsumer;
/**
* @author Bas Leijdekkers
*/
public class DirectoryComboBoxWithButtons extends JPanel {
@NotNull private final Project myProject;
@NotNull private final ComboBox<String> myDirectoryComboBox;
@NotNull private final ComboboxWithBrowseButton myDirectoryComboBox;
private boolean myRecursive = true;
private BiConsumer<VirtualFile, Boolean> myCallback;
@SuppressWarnings("WeakerAccess")
public DirectoryComboBoxWithButtons(@NotNull Project project) {
super(new BorderLayout());
myProject = project;
myDirectoryComboBox = new ComboBox<>(200);
myDirectoryComboBox.setEditable(true);
final ComboBox<Object> comboBox = new ComboBox<>(200);
myDirectoryComboBox = new ComboboxWithBrowseButton(comboBox);
myDirectoryComboBox.getComboBox().addItemListener(e -> myCallback.accept(getDirectory(), myRecursive));
comboBox.setEditable(true);
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
Component editorComponent = myDirectoryComboBox.getEditor().getEditorComponent();
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
final Component editorComponent = comboBox.getEditor().getEditorComponent();
if (editorComponent instanceof JTextField) {
JTextField field = (JTextField)editorComponent;
final JTextField field = (JTextField)editorComponent;
field.setColumns(40);
FileChooserFactory.getInstance().installFileCompletion(field, descriptor, true, null);
}
myDirectoryComboBox.setMaximumRowCount(8);
comboBox.setMaximumRowCount(8);
FixedSizeButton mySelectDirectoryButton = new FixedSizeButton(myDirectoryComboBox);
TextFieldWithBrowseButton.MyDoClickAction.addTo(mySelectDirectoryButton, myDirectoryComboBox);
mySelectDirectoryButton.setMargin(JBUI.emptyInsets());
myDirectoryComboBox.addBrowseFolderListener(project, descriptor);
mySelectDirectoryButton.addActionListener(__ -> {
FileChooser.chooseFiles(descriptor, myProject, null, null,
new FileChooser.FileChooserConsumer() {
@Override
public void consume(List<VirtualFile> files) {
ApplicationManager.getApplication().invokeLater(() -> {
IdeFocusManager.getInstance(myProject).requestFocus(myDirectoryComboBox.getEditor().getEditorComponent(), true);
myDirectoryComboBox.getEditor().setItem(files.get(0).getPresentableUrl());
});
}
@Override
public void cancelled() {
ApplicationManager.getApplication().invokeLater(() -> {
IdeFocusManager.getInstance(myProject).requestFocus(myDirectoryComboBox.getEditor().getEditorComponent(), true);
});
}
});
});
RecursiveAction
recursiveDirectoryAction = new RecursiveAction();
int mnemonicModifiers = SystemInfo.isMac ? InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK : InputEvent.ALT_DOWN_MASK;
final RecursiveAction recursiveDirectoryAction = new RecursiveAction();
final int mnemonicModifiers = SystemInfo.isMac ? InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK : InputEvent.ALT_DOWN_MASK;
recursiveDirectoryAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_Y, mnemonicModifiers)), myDirectoryComboBox);
add(myDirectoryComboBox, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(new GridLayout(1, 2));
buttonsPanel.add(mySelectDirectoryButton);
buttonsPanel.add(FindPopupPanel.createToolbar(recursiveDirectoryAction).getComponent()); //check if toolbar updates the button with no delays
add(buttonsPanel, BorderLayout.EAST);
add(FindPopupPanel.createToolbar(recursiveDirectoryAction), BorderLayout.EAST);
}
public void init(@Nullable String currentDirectory, @NotNull List<String> recentDirectories) {
if (myDirectoryComboBox.getItemCount() > 0) {
myDirectoryComboBox.removeAllItems();
}
if (currentDirectory != null && !currentDirectory.isEmpty()) {
recentDirectories.remove(currentDirectory);
myDirectoryComboBox.addItem(currentDirectory);
}
public void setCallback(BiConsumer<VirtualFile, Boolean> callback) {
myCallback = callback;
}
public void setRecentDirectories(@NotNull List<String> recentDirectories) {
@SuppressWarnings("unchecked") final JComboBox<String> comboBox = myDirectoryComboBox.getComboBox();
comboBox.removeAllItems();
for (int i = recentDirectories.size() - 1; i >= 0; i--) {
myDirectoryComboBox.addItem(recentDirectories.get(i));
}
if (myDirectoryComboBox.getItemCount() == 0) {
myDirectoryComboBox.addItem("");
comboBox.addItem(recentDirectories.get(i));
}
}
public void setCurrentDirectory(@NotNull VirtualFile directory) {
final String text = directory.getPresentableUrl();
final int count = myDirectoryComboBox.getItemCount();
for (int i = 0; i < count; i++) {
if (text.equals(myDirectoryComboBox.getItemAt(i))) {
myDirectoryComboBox.removeItemAt(i);
}
}
myDirectoryComboBox.setSelectedItem(text);
public void setDirectory(@NotNull VirtualFile directory) {
setDirectory(directory.getPresentableUrl());
}
private void setDirectory(String path) {
@SuppressWarnings("unchecked") final JComboBox<String> comboBox = myDirectoryComboBox.getComboBox();
comboBox.removeItem(path);
comboBox.insertItemAt(path, 0);
}
@Nullable
public VirtualFile getDirectory() {
String directoryName = (String)myDirectoryComboBox.getSelectedItem();
@SuppressWarnings("unchecked") final JComboBox<String> comboBox = myDirectoryComboBox.getComboBox();
final String directoryName = (String)comboBox.getSelectedItem();
if (StringUtil.isEmptyOrSpaces(directoryName)) {
return null;
}
String path = FileUtil.toSystemIndependentName(directoryName);
VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
if (virtualFile == null || !virtualFile.isDirectory()) {
virtualFile = null;
@SuppressWarnings("deprecation") VirtualFileSystem[] fileSystems = ApplicationManager.getApplication().getComponents(VirtualFileSystem.class);
for (VirtualFileSystem fs : fileSystems) {
if (fs instanceof LocalFileProvider) {
@SuppressWarnings("deprecation") VirtualFile file = ((LocalFileProvider)fs).findLocalVirtualFileByPath(path);
if (file != null && file.isDirectory()) {
if (file.getChildren().length > 0) {
virtualFile = file;
break;
}
if (virtualFile == null) {
virtualFile = file;
}
}
}
}
}
return virtualFile;
final String path = FileUtil.toSystemIndependentName(directoryName);
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);
return virtualFile == null || !virtualFile.isDirectory() ? null : virtualFile;
}
public boolean isRecursive() {
return myRecursive;
}
public void setRecursive(boolean recursive) {
myRecursive = recursive;
}
private class RecursiveAction extends ToggleAction {
RecursiveAction() {
super(FindBundle.message("find.scope.directory.recursive.checkbox"), "Recursively", AllIcons.Actions.ShowAsTree);
@@ -168,7 +119,8 @@ public class DirectoryComboBoxWithButtons extends JPanel {
@Override
public void setSelected(AnActionEvent e, boolean state) {
myRecursive = true;
myRecursive = state;
myCallback.accept(getDirectory(), myRecursive);
}
}
}
@@ -4,7 +4,6 @@ package com.intellij.structuralsearch.plugin.ui;
import com.intellij.ide.DataManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypes;
@@ -12,16 +11,20 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiElement;
import com.intellij.structuralsearch.MatchVariableConstraint;
import com.intellij.structuralsearch.SSRBundle;
import com.intellij.structuralsearch.StructuralSearchProfile;
import com.intellij.structuralsearch.impl.matcher.CompiledPattern;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.fields.IntegerField;
import com.intellij.ui.table.JBTable;
import com.intellij.ui.table.TableView;
import com.intellij.util.SmartList;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.table.JBListTable;
import com.intellij.util.ui.table.JBTableRow;
@@ -31,10 +34,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.List;
/**
@@ -43,20 +46,30 @@ import java.util.List;
public class FilterPanel {
private final JPanel myFilterPanel;
private final JBListTable myFilterTable;
private final ListTableModel<Filter> myTableModel;
@NotNull private final Project myProject;
private MatchVariableConstraint myConstraint;
final JBListTable myFilterTable;
final ListTableModel<Filter> myTableModel;
public FilterPanel(@NotNull Project project, Disposable parent) {
@NotNull private final Project myProject;
private CompiledPattern myCompiledPattern;
MatchVariableConstraint myConstraint;
StructuralSearchProfile myProfile;
final Header myHeader = new Header();
private final TextFilter myTextFilter = new TextFilter();
private final CountFilter myCountFilter = new CountFilter();
private final TypeFilter myTypeFilter = new TypeFilter();
private final ReferenceFilter myReferenceFilter = new ReferenceFilter();
public FilterPanel(@NotNull Project project, StructuralSearchProfile profile, Disposable parent) {
myProject = project;
myTableModel = new ListTableModel<>(new ColumnInfo<Filter, Filter>("") {
myProfile = profile;
myTableModel = new ListTableModel<>(new ColumnInfo[]{new ColumnInfo<Filter, Filter>("") {
@Nullable
@Override
public Filter valueOf(Filter s) {
return s;
}
});
}}, new SmartList<>());
myFilterTable = new JBListTable(new TableView<>(myTableModel), parent) {
@Override
@@ -64,17 +77,18 @@ public class FilterPanel {
return new JBTableRowRenderer() {
@Override
public JComponent getRowRendererComponent(JTable table, int row, boolean selected, boolean focused) {
return myTableModel.getRowValue(row).getRowRendererComponent();
return myTableModel.getRowValue(row).getRenderer();
}
};
}
@Override
protected JBTableRowEditor getRowEditor(int row) {
return myTableModel.getRowValue(row).getRowEditor();
return myTableModel.getRowValue(row).getEditor();
}
};
final JBTable table = myFilterTable.getTable();
table.setTableHeader(new JTableHeader());
table.setStriped(false);
myFilterPanel = ToolbarDecorator.createDecorator(table)
.disableUpDownActions()
@@ -87,39 +101,43 @@ public class FilterPanel {
showAddFilterPopup(button.getContextComponent(), point);
}
})
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
myFilterTable.stopEditing();
final int selectedRow = myFilterTable.getTable().getSelectedRow();
final Filter filter = myTableModel.getRowValue(selectedRow);
if (filter instanceof FilterAction) {
((FilterAction)filter).removeFilter();
}
}
})
.setRemoveActionUpdater(new AnActionButtonUpdater() {
@Override
public boolean isEnabled(AnActionEvent e) {
return myFilterTable.getTable().getSelectedRow() != 0;
}
})
.setPanelBorder(null)
.createPanel();
myFilterPanel.setPreferredSize(new Dimension(350, 40));
myFilterPanel.setVisible(false);
myFilterPanel.setBorder(com.intellij.util.ui.UIUtil.getTextFieldBorder());
myFilterPanel.setPreferredSize(new Dimension(350, 60));
myFilterPanel.setBorder(BorderFactory.createCompoundBorder(JBUI.Borders.empty(3, 0), myFilterPanel.getBorder()));
}
private void showAddFilterPopup(Component component, RelativePoint point) {
final DefaultActionGroup group = new DefaultActionGroup(
new AnAction("Text") {
@Override
public void actionPerformed(AnActionEvent e) {
myTableModel.addRow(new TextFilter(myConstraint));
}
},
new AnAction("Type") {
@Override
public void actionPerformed(AnActionEvent e) {
assert false : "not implemented";
}
},
new AnAction("Reference") {
@Override
public void actionPerformed(AnActionEvent e) {
assert false : "not implemented";
}
},
new AnAction("Script") {
@Override
public void actionPerformed(AnActionEvent e) {
assert false : "not implemented";
}
void showAddFilterPopup(Component component, RelativePoint point) {
myFilterTable.getTable().requestFocus();
final DefaultActionGroup group = new DefaultActionGroup();
group.add(myTextFilter);
group.add(myCountFilter);
group.add(myTypeFilter);
group.add(myReferenceFilter);
group.add(new AnAction("Script") {
@Override
public void actionPerformed(AnActionEvent e) {
assert false : "not implemented";
}
);
});
final DataContext context = DataManager.getInstance().getDataContext(component);
final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup("Add Filter", group, context,
JBPopupFactory.ActionSelectionAid.ALPHA_NUMBERING, true, null);
@@ -130,51 +148,44 @@ public class FilterPanel {
return myFilterPanel;
}
public void setFilters(MatchVariableConstraint constraint) {
myConstraint = constraint;
final List<Filter> items = new SmartList<>();
final String name = constraint.getName();
items.add(() -> {
final SimpleColoredComponent label = new SimpleColoredComponent();
label.append("Filters for $" + name + "$:", SimpleTextAttributes.GRAYED_ATTRIBUTES);
return label;
});
if (!StringUtil.isEmpty(constraint.getRegExp())) {
items.add(new TextFilter(constraint));
}
final int min = constraint.getMinCount();
final int max = constraint.getMaxCount();
if (min != 1 || max != 1) {
items.add(() -> new SimpleColoredComponent().append("count=[" + min + "," + (max == Integer.MAX_VALUE ? "∞" : max) + ']'));
}
if (items.size() <= 1) {
items.clear();
final String message;
if (Configuration.CONTEXT_VAR_NAME.equals(name)) {
message = "No filters added for the complete match.";
}
else {
message = "No Filters added for $" + name + "$.";
}
myFilterTable.getTable().getEmptyText().setText(message)
.appendSecondaryText("Add filter", SimpleTextAttributes.LINK_ATTRIBUTES,
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JBTable table = myFilterTable.getTable();
showAddFilterPopup(table, new RelativePoint(table, table.getMousePosition()));
}
});
}
myTableModel.setItems(items);
public void setProfile(StructuralSearchProfile profile) {
myProfile = profile;
}
private EditorTextField createTextComponent(String text) {
public void setCompiledPattern(CompiledPattern compiledPattern) {
myCompiledPattern = compiledPattern;
}
public void setFilters(MatchVariableConstraint constraint) {
myConstraint = constraint;
final String varName = myConstraint.getName();
final List<PsiElement> nodes = myCompiledPattern.getVariableNodes(varName);
final boolean completePattern = Configuration.CONTEXT_VAR_NAME.equals(varName);
final boolean target = myConstraint.isPartOfSearchResults();
myTableModel.setItems(new SmartList<>());
myTextFilter.init(nodes, completePattern, target);
myCountFilter.init(nodes, completePattern, target);
myTypeFilter.init(nodes, completePattern, target);
final String message = Configuration.CONTEXT_VAR_NAME.equals(varName)
? "No filters added for the complete match."
: "No Filters added for $" + varName + "$.";
myFilterTable.getTable().getEmptyText().setText(message)
.appendSecondaryText("Add filter", SimpleTextAttributes.LINK_ATTRIBUTES,
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JBTable table = myFilterTable.getTable();
showAddFilterPopup(table, new RelativePoint(table, table.getMousePosition()));
}
});
}
EditorTextField createTextComponent(String text) {
return createEditorComponent(text, "1.txt");
}
private EditorTextField createRegexComponent(String text) {
EditorTextField createRegexComponent(String text) {
return createEditorComponent(text, "1.regexp");
}
@@ -187,54 +198,83 @@ public class FilterPanel {
return new EditorTextField(text, myProject, getFileType(fileName));
}
private Document createDocument(final String fileName, final FileType fileType, String text) {
final PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(fileName, fileType, text, -1, true);
return PsiDocumentManager.getInstance(myProject).getDocument(file);
}
private static FileType getFileType(final String fileName) {
FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
if (fileType == FileTypes.UNKNOWN) fileType = FileTypes.PLAIN_TEXT;
return fileType;
}
private interface Filter {
JComponent getRowRendererComponent();
default JBTableRowEditor getRowEditor() {
return null;
private class Header implements Filter {
private final SimpleColoredComponent myLabel = new SimpleColoredComponent();
Header() {}
@Override
public int position() {
return 0;
}
@Override
public SimpleColoredComponent getRenderer() {
myLabel.clear();
final String varName = myConstraint.getName();
myLabel.append(Configuration.CONTEXT_VAR_NAME.equals(varName) ? "Filters for the Complete Match:" : "Filters for $" + varName + "$:",
SimpleTextAttributes.GRAYED_ATTRIBUTES);
return myLabel;
}
}
private class TextFilter implements Filter {
private class TextFilter extends FilterAction {
private final MatchVariableConstraint myConstraint;
boolean showHierarchy;
public TextFilter(MatchVariableConstraint constraint) {
myConstraint = constraint;
TextFilter() {
super("Text");
}
@Override
public JComponent getRowRendererComponent() {
final SimpleColoredComponent component = new SimpleColoredComponent();
component.append("text=" + myConstraint.getRegExp());
if (myConstraint.isWholeWordsOnly()) component.append(", whole words", SimpleTextAttributes.GRAYED_ATTRIBUTES);
if (myConstraint.isWithinHierarchy()) component.append(", within hierarchy", SimpleTextAttributes.GRAYED_ATTRIBUTES);
return component;
public int position() {
return 1;
}
@Override
public JBTableRowEditor getRowEditor() {
return new JBTableRowEditor() {
protected boolean hasFilter() {
return !StringUtil.isEmpty(myConstraint.getRegExp());
}
private final EditorTextField myTextField = createRegexComponent(myConstraint.getRegExp());
private final JCheckBox myWordsCheckBox = new JCheckBox("Words", myConstraint.isWholeWordsOnly());
private final JCheckBox myHierarchyCheckBox = new JCheckBox("Within type hierarchy", myConstraint.isWithinHierarchy());
public void clearFilter() {
myConstraint.setRegExp("");
myConstraint.setWholeWordsOnly(false);
myConstraint.setWithinHierarchy(false);
}
@Override
public void prepareEditor(JTable table, int row) {
final JLabel nameLabel = new JLabel("name=");
final ContextHelpLabel helpLabel = ContextHelpLabel.create("Text of the match is checked against the provided pattern.");
@Override
public boolean isApplicable(List<PsiElement> nodes, boolean completePattern, boolean target) {
showHierarchy = myProfile.isApplicableConstraint(UIUtil.TEXT_HIERARCHY, nodes, completePattern, target);
return myProfile.isApplicableConstraint(UIUtil.TEXT, nodes, completePattern, target);
}
@Override
protected void setLabel(SimpleColoredComponent component) {
myLabel.append("text=");
if (myConstraint.isInvertRegExp()) myLabel.append("!");
myLabel.append(myConstraint.getRegExp());
if (myConstraint.isWholeWordsOnly()) myLabel.append(", whole words", SimpleTextAttributes.GRAYED_ATTRIBUTES);
if (myConstraint.isWithinHierarchy()) myLabel.append(", within hierarchy", SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
@Override
public FilterEditor getEditor() {
return new FilterEditor(myConstraint) {
private final EditorTextField myTextField = createRegexComponent("");
private final JCheckBox myWordsCheckBox = new JCheckBox("Words", false);
private final JCheckBox myHierarchyCheckBox = new JCheckBox("Within type hierarchy", false);
private final JLabel myNameLabel = new JLabel("name=");
private final ContextHelpLabel myHelpLabel = ContextHelpLabel.create("Text of the match is checked against the provided pattern.");
protected void layoutComponents() {
final GroupLayout layout = new GroupLayout(this);
setLayout(layout);
layout.setAutoCreateContainerGaps(true);
@@ -243,10 +283,10 @@ public class FilterPanel {
layout.createParallelGroup()
.addGroup(
layout.createSequentialGroup()
.addComponent(nameLabel)
.addComponent(myNameLabel)
.addComponent(myTextField)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 1, 1)
.addComponent(helpLabel)
.addComponent(myHelpLabel)
)
.addGroup(
layout.createSequentialGroup()
@@ -259,9 +299,9 @@ public class FilterPanel {
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(nameLabel)
.addComponent(myNameLabel)
.addComponent(myTextField)
.addComponent(helpLabel)
.addComponent(myHelpLabel)
)
.addGroup(
layout.createParallelGroup(GroupLayout.Alignment.CENTER)
@@ -272,11 +312,18 @@ public class FilterPanel {
}
@Override
public JBTableRow getValue() {
protected void loadValues() {
myTextField.setText(myConstraint.getRegExp());
myWordsCheckBox.setSelected(myConstraint.isWholeWordsOnly());
myHierarchyCheckBox.setSelected(myConstraint.isWithinHierarchy());
myHierarchyCheckBox.setVisible(showHierarchy);
}
@Override
public void saveValues() {
myConstraint.setRegExp(myTextField.getText());
myConstraint.setWholeWordsOnly(myWordsCheckBox.isSelected());
myConstraint.setWithinHierarchy(myHierarchyCheckBox.isSelected());
return __ -> this;
}
@Override
@@ -291,4 +338,377 @@ public class FilterPanel {
};
}
}
private class CountFilter extends FilterAction {
boolean myMinZero;
boolean myMaxUnlimited;
CountFilter() {
super("Count");
}
@Override
public int position() {
return 2;
}
@Override
protected boolean hasFilter() {
return myConstraint.getMinCount() != 1 || myConstraint.getMaxCount() != 1;
}
@Override
protected void clearFilter() {
myConstraint.setMinCount(1);
myConstraint.setMaxCount(1);
}
@Override
public boolean isApplicable(List<PsiElement> nodes, boolean completePattern, boolean target) {
myMinZero = myProfile.isApplicableConstraint(UIUtil.MINIMUM_ZERO, nodes, completePattern, false);
myMaxUnlimited = myProfile.isApplicableConstraint(UIUtil.MAXIMUM_UNLIMITED, nodes, completePattern, false);
return myMinZero || myMaxUnlimited;
}
@Override
protected void setLabel(SimpleColoredComponent component) {
final int min = myConstraint.getMinCount();
final int max = myConstraint.getMaxCount();
myLabel.append("count=[" + min + "," + (max == Integer.MAX_VALUE ? "∞" : max) + ']');
}
@Override
public FilterEditor getEditor() {
return new FilterEditor(myConstraint) {
private final IntegerField myMinField = new IntegerField();
private final IntegerField myMaxField = new IntegerField();
private final JLabel myMinLabel = new JLabel("min=");
private final JLabel myMaxLabel = new JLabel("max=");
{
myMinField.getValueEditor().addListener(newValue -> {
if (myMinField.getValueEditor().isValid(newValue) && myMaxField.getValue() < newValue) myMaxField.setValue(newValue);
});
myMaxField.getValueEditor().addListener(newValue -> {
if (myMaxField.getValueEditor().isValid(newValue) && myMinField.getValue() > newValue) myMinField.setValue(newValue);
});
}
protected void layoutComponents() {
final GroupLayout layout = new GroupLayout(this);
setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addComponent(myMinLabel)
.addComponent(myMinField)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 20, 20)
.addComponent(myMaxLabel)
.addComponent(myMaxField)
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(myMinLabel)
.addComponent(myMinField)
.addComponent(myMaxLabel)
.addComponent(myMaxField)
);
}
@Override
protected void loadValues() {
myMinField.setMinValue(myMinZero ? 0 : 1);
myMinField.setDefaultValue(myMinZero ? 0 : 1);
myMinField.setDefaultValueText(myMinZero ? "0" : "1");
myMinField.setValue(myConstraint.getMinCount());
myMinField.selectAll();
myMaxField.setMaxValue(myMaxUnlimited ? Integer.MAX_VALUE : 1);
myMaxField.setDefaultValue(myMaxUnlimited ? Integer.MAX_VALUE : 1);
myMaxField.setDefaultValueText(myMaxUnlimited ? SSRBundle.message("editvarcontraints.unlimited") : "1");
myMaxField.setValue(myConstraint.getMaxCount());
myMaxField.selectAll();
}
@Override
public void saveValues() {
myConstraint.setMinCount(myMinField.getValue());
myConstraint.setMaxCount(myMaxField.getValue());
}
@Override
public JComponent getPreferredFocusedComponent() {
return myMinField;
}
@Override
public JComponent[] getFocusableComponents() {
return new JComponent[] {myMinField, myMaxField};
}
};
}
}
private class TypeFilter extends FilterAction {
TypeFilter() {
super("Type");
}
@Override
public int position() {
return 3;
}
@Override
protected boolean hasFilter() {
return !StringUtil.isEmpty(myConstraint.getNameOfExprType());
}
@Override
protected void clearFilter() {
myConstraint.setNameOfExprType("");
myConstraint.setInvertExprType(false);
myConstraint.setExprTypeWithinHierarchy(false);
}
@Override
public boolean isApplicable(List<PsiElement> nodes, boolean completePattern, boolean target) {
return myProfile.isApplicableConstraint(UIUtil.TYPE, nodes, completePattern, target);
}
@Override
protected void setLabel(SimpleColoredComponent component) {
myLabel.append("type=");
if (myConstraint.isInvertExprType()) myLabel.append("!");
myLabel.append(myConstraint.getNameOfExprType());
if (myConstraint.isExprTypeWithinHierarchy()) myLabel.append(", within hierarchy", SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
@Override
public FilterEditor getEditor() {
return new FilterEditor(myConstraint) {
private final EditorTextField myTextField = createTextComponent("");
private final JLabel myTypeLabel = new JLabel("type=");
private final JCheckBox myHierarchyCheckBox = new JCheckBox("Within type hierarchy", false);
protected void layoutComponents() {
final GroupLayout layout = new GroupLayout(this);
setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(
layout.createParallelGroup()
.addGroup(
layout.createSequentialGroup()
.addComponent(myTypeLabel)
.addComponent(myTextField)
)
.addComponent(myHierarchyCheckBox)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(myTypeLabel)
.addComponent(myTextField)
)
.addComponent(myHierarchyCheckBox)
);
}
@Override
protected void loadValues() {
myTextField.setText((myConstraint.isInvertExprType() ? "!" : "") + myConstraint.getNameOfExprType());
}
@Override
public void saveValues() {
final String text = myTextField.getText();
if (text.startsWith("!")) {
myConstraint.setNameOfExprType(text.substring(1));
myConstraint.setInvertExprType(true);
}
else {
myConstraint.setNameOfExprType(text);
myConstraint.setInvertExprType(false);
}
myConstraint.setExprTypeWithinHierarchy(myHierarchyCheckBox.isSelected());
}
@Override
public JComponent getPreferredFocusedComponent() {
return myTextField;
}
@Override
public JComponent[] getFocusableComponents() {
return new JComponent[] { myTextField };
}
};
}
}
private class ReferenceFilter extends FilterAction {
ReferenceFilter() {
super("Reference");
}
@Override
public int position() {
return 4;
}
@Override
protected boolean hasFilter() {
return !StringUtil.isEmpty(myConstraint.getReferenceConstraint());
}
@Override
protected void clearFilter() {
myConstraint.setReferenceConstraint("");
myConstraint.setInvertReference(false);
}
@Override
public boolean isApplicable(List<PsiElement> nodes, boolean completePattern, boolean target) {
return myProfile.isApplicableConstraint(UIUtil.REFERENCE, nodes, completePattern, target);
}
@Override
protected void setLabel(SimpleColoredComponent component) {
component.append("reference");
if (myConstraint.isInvertReference()) component.append("!");
component.append(myConstraint.getReferenceConstraint());
}
}
private abstract class FilterAction extends AnAction implements Filter {
protected final SimpleColoredComponent myLabel = new SimpleColoredComponent();
protected FilterAction(@Nullable String text) {
super(text);
}
public final void init(List<PsiElement> nodes, boolean completePattern, boolean target) {
if (isApplicable(nodes, completePattern, target) && !myTableModel.getItems().contains(this)) {
addFilterIfPresent();
}
else {
clearFilter();
getTemplatePresentation().setEnabledAndVisible(false);
}
}
@Override
public final void actionPerformed(AnActionEvent e) {
addFilter();
}
@Override
public final SimpleColoredComponent getRenderer() {
if (!hasFilter()) removeFilter();
myLabel.clear();
setLabel(myLabel);
return myLabel;
}
protected abstract void setLabel(SimpleColoredComponent component);
protected abstract boolean hasFilter();
public final void addFilterIfPresent() {
if (hasFilter()) {
getTemplatePresentation().setEnabledAndVisible(false);
if (myTableModel.getRowCount() == 0) {
myTableModel.addRow(myHeader);
}
myTableModel.addRow(this);
}
else {
getTemplatePresentation().setEnabledAndVisible(true);
}
}
public final void addFilter() {
getTemplatePresentation().setEnabledAndVisible(false);
final JBTable table = myFilterTable.getTable();
TableUtil.stopEditing(table);
table.setRowHeight(table.getRowHeight()); // reset
int index = 0;
for (int max = myTableModel.getRowCount(); index < max; index++) {
if (position() < myTableModel.getItem(index).position()) {
break;
}
}
if (index == 0) {
myTableModel.addRow(myHeader);
index = 1;
}
myTableModel.insertRow(index, this);
table.editCellAt(index, 0);
table.setRowSelectionInterval(index, index);
table.setColumnSelectionInterval(0, 0);
TableUtil.updateScroller(table);
final Component editorComponent = table.getEditorComponent();
if (editorComponent != null) {
table.scrollRectToVisible(editorComponent.getBounds());
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(editorComponent, true));
}
}
public final void removeFilter() {
final int index = myTableModel.indexOf(this);
if (index >= 0) myTableModel.removeRow(index);
if (myTableModel.getRowCount() == 1) myTableModel.removeRow(0); // remove header
getTemplatePresentation().setEnabledAndVisible(true);
clearFilter();
}
protected abstract void clearFilter();
public abstract boolean isApplicable(List<PsiElement> nodes, boolean completePattern, boolean target);
}
abstract static class FilterEditor extends JBTableRowEditor {
protected final MatchVariableConstraint myConstraint;
public FilterEditor(MatchVariableConstraint constraint) {
myConstraint = constraint;
}
@Override
public final JBTableRow getValue() {
saveValues();
return __ -> this;
}
@Override
public final void prepareEditor(JTable table, int row) {
layoutComponents();
loadValues();
}
protected abstract void layoutComponents();
protected abstract void loadValues();
protected abstract void saveValues();
}
private interface Filter {
int position();
SimpleColoredComponent getRenderer();
default FilterEditor getEditor() {
return null;
}
}
}
@@ -3,9 +3,9 @@ package com.intellij.structuralsearch.plugin.ui;
import com.intellij.application.options.ModulesComboBox;
import com.intellij.find.FindBundle;
import com.intellij.find.FindInProjectSettings;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.util.scopeChooser.ScopeChooserCombo;
import com.intellij.ide.util.scopeChooser.ScopeDescriptor;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.module.Module;
@@ -15,8 +15,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopesCore;
import com.intellij.psi.search.ProjectScopeImpl;
import com.intellij.psi.search.SearchScope;
import com.intellij.structuralsearch.Scopes;
import com.intellij.ui.JBCardLayout;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
@@ -26,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
/**
@@ -34,51 +36,42 @@ import java.util.function.Consumer;
class ScopePanel extends JPanel {
private final Project myProject;
private SearchScope mySelectedScope;
private SearchScope myScope;
private Scopes.Type myScopeType;
private Consumer<SearchScope> myCallback;
private final ActionToolbarImpl myToolbar;
private final JPanel myScopeDetailsPanel = new JPanel(new CardLayout());
private final JPanel myScopeDetailsPanel = new JPanel(new JBCardLayout());
private final ModulesComboBox myModulesComboBox = new ModulesComboBox();
private final DirectoryComboBoxWithButtons myDirectoryComboBox;
private final ScopeChooserCombo myScopeChooserComboBox = new ScopeChooserCombo();
private final ScopeChooserCombo myScopesComboBox = new ScopeChooserCombo();
public ScopePanel(@NotNull Project project, SearchScope selectedScope) {
public ScopePanel(@NotNull Project project) {
super(null);
myProject = project;
mySelectedScope = selectedScope;
Module[] allModules = ModuleManager.getInstance(project).getModules();
final Module[] allModules = ModuleManager.getInstance(project).getModules();
myModulesComboBox.setModules(Arrays.asList(allModules));
if (allModules.length > 0) myModulesComboBox.setSelectedModule(allModules[0]);
myScopeChooserComboBox.init(project, true, false, "",
descriptor -> IdeBundle.message("scope.class.hierarchy").equals(descriptor.getDisplay()) ||
!(descriptor.getScope() instanceof ModuleWithDependenciesScope));
myModulesComboBox.addItemListener(e -> setScopeFromUI(Scopes.Type.MODULE));
myScopesComboBox.init(project, true, false, "",
(ScopeDescriptor descriptor) -> IdeBundle.message("scope.class.hierarchy").equals(descriptor.getDisplay()) ||
!(descriptor.getScope() instanceof ModuleWithDependenciesScope));
myScopesComboBox.getComboBox().addItemListener(e -> setScopeFromUI(Scopes.Type.NAMED));
myDirectoryComboBox = new DirectoryComboBoxWithButtons(myProject);
myDirectoryComboBox.init(null, FindInProjectSettings.getInstance(myProject).getRecentDirectories()); // todo
myDirectoryComboBox.setCallback((vFile, recursive) -> setScopeFromUI(Scopes.Type.DIRECTORY));
if (selectedScope instanceof ModuleWithDependenciesScope) {
final ModuleWithDependenciesScope scope = (ModuleWithDependenciesScope)selectedScope;
myModulesComboBox.setSelectedModule(scope.getModule());
}
else if (selectedScope instanceof GlobalSearchScopesCore.DirectoryScope) {
//myDirectoryComboBox.setS;
}
else if (selectedScope != null) {
myScopeChooserComboBox.init(project, true, false, selectedScope.getDisplayName());
}
myScopeDetailsPanel.add(ScopeType.PROJECT.toString(), new JLabel());
myScopeDetailsPanel.add(ScopeType.MODULE.toString(), myModulesComboBox);
myScopeDetailsPanel.add(ScopeType.DIRECTORY.toString(), myDirectoryComboBox);
myScopeDetailsPanel.add(ScopeType.SCOPE.toString(), myScopeChooserComboBox);
myScopeDetailsPanel.add(Scopes.Type.PROJECT.toString(), new JLabel());
myScopeDetailsPanel.add(Scopes.Type.MODULE.toString(), shrinkWrap(myModulesComboBox));
myScopeDetailsPanel.add(Scopes.Type.DIRECTORY.toString(), myDirectoryComboBox);
myScopeDetailsPanel.add(Scopes.Type.NAMED.toString(), shrinkWrap(myScopesComboBox));
myScopeDetailsPanel.setBorder(JBUI.Borders.emptyBottom(UIUtil.isUnderDefaultMacTheme() ? 0 : 3));
final DefaultActionGroup scopeActionGroup = new DefaultActionGroup(
new ScopeToggleAction(FindBundle.message("find.popup.scope.project"), ScopeType.PROJECT),
new ScopeToggleAction(FindBundle.message("find.popup.scope.module"), ScopeType.MODULE),
new ScopeToggleAction(FindBundle.message("find.popup.scope.directory"), ScopeType.DIRECTORY),
new ScopeToggleAction(FindBundle.message("find.popup.scope.scope"), ScopeType.SCOPE)
new ScopeToggleAction(FindBundle.message("find.popup.scope.project"), Scopes.Type.PROJECT),
new ScopeToggleAction(FindBundle.message("find.popup.scope.module"), Scopes.Type.MODULE),
new ScopeToggleAction(FindBundle.message("find.popup.scope.directory"), Scopes.Type.DIRECTORY),
new ScopeToggleAction(FindBundle.message("find.popup.scope.scope"), Scopes.Type.NAMED)
);
myToolbar = (ActionToolbarImpl)ActionManager.getInstance().createActionToolbar("ScopePanel", scopeActionGroup, true);
myToolbar.setForceMinimumSize(true);
@@ -90,7 +83,7 @@ class ScopePanel extends JPanel {
layout.createSequentialGroup()
.addComponent(myToolbar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 25, 25)
.addComponent(myScopeDetailsPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(myScopeDetailsPanel)
);
layout.setVerticalGroup(
layout.createParallelGroup()
@@ -99,19 +92,73 @@ class ScopePanel extends JPanel {
);
}
private static JComponent shrinkWrap(JComponent component) {
final JPanel wrapper = new JPanel(new BorderLayout());
wrapper.add(component, BorderLayout.WEST);
wrapper.add(Box.createHorizontalGlue(), BorderLayout.CENTER);
return wrapper;
}
public void setRecentDirectories(@NotNull List<String> recentDirectories) {
myDirectoryComboBox.setRecentDirectories(recentDirectories);
}
public void setScope(SearchScope selectedScope) {
myScope = (selectedScope == null) ? GlobalSearchScope.projectScope(myProject) : selectedScope;
myScopeType = Scopes.getType(myScope);
if (selectedScope instanceof ModuleWithDependenciesScope) {
final ModuleWithDependenciesScope scope = (ModuleWithDependenciesScope)selectedScope;
myModulesComboBox.setSelectedModule(scope.getModule());
}
else if (selectedScope instanceof GlobalSearchScopesCore.DirectoryScope) {
final GlobalSearchScopesCore.DirectoryScope directoryScope = (GlobalSearchScopesCore.DirectoryScope)selectedScope;
final VirtualFile directory = directoryScope.getDirectory();
myDirectoryComboBox.setDirectory(directory);
myDirectoryComboBox.setRecursive(directoryScope.isWithSubdirectories());
}
else if (selectedScope != null) {
myScopesComboBox.init(myProject, true, false, selectedScope.getDisplayName());
}
myToolbar.updateActionsImmediately();
((JBCardLayout)myScopeDetailsPanel.getLayout()).show(myScopeDetailsPanel, myScopeType.toString());
}
public void setScopeCallback(Consumer<SearchScope> callback) {
myCallback = callback;
}
public SearchScope getSelectedScope() {
return mySelectedScope;
public SearchScope getScope() {
return myScope;
}
private void setScopeFromUI(Scopes.Type type) {
switch (type) {
case PROJECT:
myScope = GlobalSearchScope.projectScope(myProject);
break;
case MODULE:
final Module module = myModulesComboBox.getSelectedModule();
if (module == null) return;
myScope = GlobalSearchScope.moduleScope(module);
break;
case DIRECTORY:
final VirtualFile directory = myDirectoryComboBox.getDirectory();
if (directory == null) return;
myScope = GlobalSearchScopesCore.directoryScope(myProject, directory, myDirectoryComboBox.isRecursive());
break;
case NAMED:
myScope = myScopesComboBox.getSelectedScope();
break;
}
if (myCallback != null) myCallback.accept(myScope);
}
class ScopeToggleAction extends ToggleAction {
private final ScopeType myScopeType;
private final Scopes.Type myScopeType;
public ScopeToggleAction(@Nullable String text, ScopeType scopeType) {
public ScopeToggleAction(@Nullable String text, Scopes.Type scopeType) {
super(text, null, EmptyIcon.ICON_0);
myScopeType = scopeType;
getTemplatePresentation().setDisabledIcon(EmptyIcon.ICON_0);
@@ -119,35 +166,17 @@ class ScopePanel extends JPanel {
@Override
public boolean isSelected(AnActionEvent e) {
return myScopeType == ScopeType.fromScope(mySelectedScope);
return myScopeType == ScopePanel.this.myScopeType;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
if (state) {
switch (myScopeType) {
case PROJECT:
mySelectedScope = GlobalSearchScope.projectScope(myProject);
break;
case MODULE:
final Module module = myModulesComboBox.getSelectedModule();
if (module != null) {
mySelectedScope = GlobalSearchScope.moduleScope(module);
}
break;
case DIRECTORY:
final VirtualFile directory = myDirectoryComboBox.getDirectory();
if (directory != null) {
mySelectedScope = GlobalSearchScopesCore.directoryScope(myProject, directory, myDirectoryComboBox.isRecursive());
}
break;
case SCOPE:
mySelectedScope = myScopeChooserComboBox.getSelectedScope();
break;
}
((JBCardLayout)myScopeDetailsPanel.getLayout()).swipe(myScopeDetailsPanel, myScopeType.toString(),
ScopePanel.this.myScopeType.compareTo(myScopeType) > 0 ? JBCardLayout.SwipeDirection.FORWARD : JBCardLayout.SwipeDirection.BACKWARD);
ScopePanel.this.myScopeType = myScopeType;
setScopeFromUI(myScopeType);
myToolbar.updateActionsImmediately();
((CardLayout)myScopeDetailsPanel.getLayout()).show(myScopeDetailsPanel, myScopeType.toString());
if (myCallback != null) myCallback.accept(mySelectedScope);
}
}
@@ -156,26 +185,4 @@ class ScopePanel extends JPanel {
return true;
}
}
enum ScopeType {
PROJECT,
MODULE,
DIRECTORY,
SCOPE;
static ScopeType fromScope(SearchScope scope) {
if (scope instanceof ProjectScopeImpl || scope == null) {
return PROJECT;
}
else if (scope instanceof ModuleWithDependenciesScope) {
return MODULE;
}
else if (scope instanceof GlobalSearchScopesCore.DirectoryScope) {
return DIRECTORY;
}
else {
return SCOPE;
}
}
}
}
@@ -4,6 +4,7 @@ package com.intellij.structuralsearch.plugin.ui;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.template.impl.TemplateEditorUtil;
import com.intellij.find.FindBundle;
import com.intellij.find.FindInProjectSettings;
import com.intellij.find.FindSettings;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
@@ -27,18 +28,28 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopesCore;
import com.intellij.psi.search.SearchScope;
import com.intellij.structuralsearch.*;
import com.intellij.structuralsearch.impl.matcher.CompiledPattern;
import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler;
import com.intellij.structuralsearch.plugin.StructuralSearchPlugin;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Alarm;
import com.intellij.util.ObjectUtils;
import com.intellij.util.SmartList;
@@ -50,11 +61,13 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -62,37 +75,34 @@ import java.util.List;
*/
public class StructuralSearchDialog extends DialogWrapper {
static final Key<Boolean> STRUCTURAL_SEARCH = Key.create("STRUCTURAL_SEARCH_AREA");
protected SearchContext searchContext;
// text for search
protected EditorTextField searchCriteriaEdit;
private JCheckBox recursiveMatching;
private JCheckBox caseSensitiveMatch;
private FileTypeSelector fileTypes;
protected Configuration myConfiguration;
private JCheckBox openInNewTab;
private final Alarm myAlarm;
public static final String USER_DEFINED = SSRBundle.message("new.template.defaultname");
private boolean useLastConfiguration;
@NonNls private FileType ourFtSearchVariant = StructuralSearchUtil.getDefaultFileType();
private static Language ourDialect = null;
private static String ourContext = null;
// search info
protected SearchContext mySearchContext;
protected Configuration myConfiguration;
@NonNls private FileType myFileType = StructuralSearchUtil.getDefaultFileType();
private Language myDialect = null;
private String myContext = null;
// ui management
private final Alarm myAlarm;
private boolean myUseLastConfiguration;
private final boolean myShowScopePanel;
private final boolean myRunFindActionOnClose;
private boolean myDoingOkAction;
private String mySavedEditorText;
private JPanel myEditorPanel;
private boolean myFilterButtonEnabled = false;
private FilterPanel myFilterPanel;
// components
private JCheckBox myRecursiveMatching;
private JCheckBox myCaseSensitiveMatch;
private FileTypeSelector myFileTypesComboBox;
protected EditorTextField mySearchCriteriaEdit;
private OnePixelSplitter myEditorPanel;
private FilterPanel myFilterPanel;
private LinkComboBox myTargetComboBox;
private ScopePanel myScopePanel;
private JCheckBox myOpenInNewTab;
public StructuralSearchDialog(SearchContext searchContext) {
this(searchContext, true, true);
@@ -104,7 +114,7 @@ public class StructuralSearchDialog extends DialogWrapper {
if (showScope) setModal(false);
myShowScopePanel = showScope;
myRunFindActionOnClose = runFindActionOnClose;
this.searchContext = searchContext;
this.mySearchContext = searchContext;
setTitle(getDefaultTitle());
if (runFindActionOnClose) {
@@ -124,7 +134,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
public void setUseLastConfiguration(boolean useLastConfiguration) {
this.useLastConfiguration = useLastConfiguration;
this.myUseLastConfiguration = useLastConfiguration;
}
void setSearchPattern(final Configuration config) {
@@ -133,14 +143,9 @@ public class StructuralSearchDialog extends DialogWrapper {
}
protected EditorTextField createEditor(final SearchContext searchContext, String text) {
final FileTypeInfo info = fileTypes.getSelectedItem();
assert info != null;
final FileType fileType = info.getFileType();
final Language dialect = info.getDialect();
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(fileType);
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType);
assert profile != null;
final Document document = profile.createDocument(searchContext.getProject(), fileType, dialect, text);
final Document document = profile.createDocument(searchContext.getProject(), myFileType, myDialect, text);
document.addDocumentListener(new DocumentListener() {
@Override
public void documentChanged(final DocumentEvent event) {
@@ -148,7 +153,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
});
final EditorTextField textField = new EditorTextField(document, searchContext.getProject(), fileType) {
final EditorTextField textField = new EditorTextField(document, searchContext.getProject(), myFileType) {
@Override
protected EditorEx createEditor() {
EditorEx editorEx = super.createEditor();
@@ -158,6 +163,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
};
textField.setPreferredSize(new Dimension(850, 150));
textField.setMinimumSize(new Dimension(200, 50));
return textField;
}
@@ -200,7 +206,9 @@ public class StructuralSearchDialog extends DialogWrapper {
private boolean isCompiled() {
try {
return PatternCompiler.compilePattern(getProject(), myConfiguration.getMatchOptions(), false) != null;
final CompiledPattern compiledPattern = PatternCompiler.compilePattern(getProject(), myConfiguration.getMatchOptions(), false);
myFilterPanel.setCompiledPattern(compiledPattern);
return compiledPattern != null;
} catch (MalformedPatternException e) {
return false;
}
@@ -208,45 +216,44 @@ public class StructuralSearchDialog extends DialogWrapper {
void updateEditor() {
if (myEditorPanel != null) {
if (searchCriteriaEdit != null) {
mySavedEditorText = searchCriteriaEdit.getText();
myEditorPanel.remove(searchCriteriaEdit);
if (mySearchCriteriaEdit != null) {
mySavedEditorText = mySearchCriteriaEdit.getText();
myEditorPanel.remove(mySearchCriteriaEdit);
}
searchCriteriaEdit = createEditor(searchContext, mySavedEditorText != null ? mySavedEditorText : "");
myEditorPanel.add(searchCriteriaEdit, BorderLayout.CENTER);
mySearchCriteriaEdit = createEditor(mySearchContext, mySavedEditorText != null ? mySavedEditorText : "");
myEditorPanel.setFirstComponent(mySearchCriteriaEdit);
myEditorPanel.revalidate();
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(ourFtSearchVariant);
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType);
assert profile != null;
searchCriteriaEdit.addPropertyChangeListener(new PropertyChangeListener() {
mySearchCriteriaEdit.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
final Editor editor = searchCriteriaEdit.getEditor();
final Editor editor = mySearchCriteriaEdit.getEditor();
if (editor == null) return;
TemplateEditorUtil.setHighlighter(editor, profile.getTemplateContextType());
SubstitutionShortInfoHandler.install(editor, variableName ->
myFilterPanel.setFilters(myConfiguration.getMatchOptions().getVariableConstraint(variableName)));
myFilterPanel.setFilters(UIUtil.getOrAddVariableConstraint(Configuration.CONTEXT_VAR_NAME, myConfiguration));
//myFilterPanel.setFilters(UIUtil.getOrAddVariableConstraint(Configuration.CONTEXT_VAR_NAME, myConfiguration));
editor.putUserData(SubstitutionShortInfoHandler.CURRENT_CONFIGURATION_KEY, myConfiguration);
final Project project = searchContext.getProject();
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(searchCriteriaEdit.getDocument());
final Project project = mySearchContext.getProject();
final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(mySearchCriteriaEdit.getDocument());
if (file != null) {
DaemonCodeAnalyzer.getInstance(project).setHighlightingEnabled(file, false);
}
searchCriteriaEdit.removePropertyChangeListener(this);
mySearchCriteriaEdit.removePropertyChangeListener(this);
}
});
}
}
private void detectFileTypeAndDialect() {
final PsiFile file = searchContext.getFile();
final PsiFile file = mySearchContext.getFile();
if (file != null) {
PsiElement context = null;
if (searchContext.getEditor() != null) {
context = file.findElementAt(searchContext.getEditor().getCaretModel().getOffset());
if (mySearchContext.getEditor() != null) {
context = file.findElementAt(mySearchContext.getEditor().getCaretModel().getOffset());
if (context != null) {
context = context.getParent();
}
@@ -274,9 +281,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
}
ourFtSearchVariant = detectedFileType != null ?
detectedFileType :
StructuralSearchUtil.getDefaultFileType();
myFileType = (detectedFileType != null) ? detectedFileType : StructuralSearchUtil.getDefaultFileType();
}
}
@@ -298,19 +303,20 @@ public class StructuralSearchDialog extends DialogWrapper {
break;
}
}
myScopePanel.setScope(matchOptions.getScope());
myScopePanel.setRecentDirectories(FindInProjectSettings.getInstance(getProject()).getRecentDirectories());
final Document document = searchCriteriaEdit.getDocument();
CommandProcessor.getInstance().executeCommand(searchContext.getProject(), () -> {
final Document document = mySearchCriteriaEdit.getDocument();
CommandProcessor.getInstance().executeCommand(mySearchContext.getProject(), () -> {
ApplicationManager.getApplication().runWriteAction(() -> {
document.replaceString(0, document.getTextLength(), matchOptions.getSearchPattern());
});
}, null, null);
recursiveMatching.setSelected(isRecursiveSearchEnabled() && matchOptions.isRecursiveSearch());
caseSensitiveMatch.setSelected(matchOptions.isCaseSensitiveMatch());
fileTypes.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect(), matchOptions.getPatternContext());
final Editor editor = searchCriteriaEdit.getEditor();
myRecursiveMatching.setSelected(isRecursiveSearchEnabled() && matchOptions.isRecursiveSearch());
myCaseSensitiveMatch.setSelected(matchOptions.isCaseSensitiveMatch());
myFileTypesComboBox.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect(), matchOptions.getPatternContext());
final Editor editor = mySearchCriteriaEdit.getEditor();
if (editor != null) {
editor.putUserData(SubstitutionShortInfoHandler.CURRENT_CONFIGURATION_KEY, myConfiguration);
}
@@ -321,13 +327,13 @@ public class StructuralSearchDialog extends DialogWrapper {
}
protected void setText(String text) {
setTextForEditor(text, searchCriteriaEdit);
setTextForEditor(text, mySearchCriteriaEdit);
}
protected final void setTextForEditor(final String selection, EditorTextField editor) {
editor.setText(selection);
editor.selectAll();
final Project project = searchContext.getProject();
final Project project = mySearchContext.getProject();
final Document document = editor.getDocument();
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
documentManager.commitDocument(document);
@@ -340,7 +346,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
protected void startSearching() {
new SearchCommand(myConfiguration, searchContext).startSearching();
new SearchCommand(myConfiguration, mySearchContext).startSearching();
}
protected String getDefaultTitle() {
@@ -349,15 +355,46 @@ public class StructuralSearchDialog extends DialogWrapper {
@Override
protected JComponent createCenterPanel() {
myEditorPanel = new JPanel(new BorderLayout());
searchCriteriaEdit = createEditor(searchContext, mySavedEditorText != null ? mySavedEditorText : "");
myEditorPanel.add(BorderLayout.CENTER, searchCriteriaEdit);
myEditorPanel = new OnePixelSplitter(false, 1.0f) {
/*final Timer myTimer = com.intellij.util.ui.UIUtil.createNamedTimer("SlidingSplitter", 17);
myScopePanel = new ScopePanel(getProject(), myConfiguration.getMatchOptions().getScope());
myScopePanel.setEnabled(myShowScopePanel); // todo initiateValidation() on change
@Override
public void setSecondComponent(@Nullable JComponent component) {
if (component != null) {
final Dimension size = component.getMinimumSize();
component.setMinimumSize(new Dimension(0, size.height));
final ActionListener listener = e -> {
final int width = component.getMinimumSize().width;
if (width < size.width) {
component.setMinimumSize(new Dimension(width + 1, size.height));
myEditorPanel.revalidate();
}
else {
myTimer.stop();
}
};
for (ActionListener actionListener: myTimer.getActionListeners()) {
myTimer.removeActionListener(actionListener);
}
myTimer.addActionListener(listener);
myTimer.start();
super.setSecondComponent(component);
}
super.setSecondComponent(component);
}*/
};
myEditorPanel.setLackOfSpaceStrategy(Splitter.LackOfSpaceStrategy.HONOR_THE_SECOND_MIN_SIZE);
myEditorPanel.getDivider().setOpaque(false);
mySearchCriteriaEdit = createEditor(mySearchContext, mySavedEditorText != null ? mySavedEditorText : "");
myEditorPanel.add(BorderLayout.CENTER, mySearchCriteriaEdit);
myFilterPanel = new FilterPanel(getProject(), getDisposable());
myFilterPanel.getComponent().setMinimumSize(new Dimension(400, searchCriteriaEdit.getMinimumSize().height));
myScopePanel = new ScopePanel(getProject());
myScopePanel.setEnabled(myShowScopePanel);
myScopePanel.setScopeCallback(s -> initiateValidation());
myFilterPanel =
new FilterPanel(getProject(), StructuralSearchUtil.getProfileByFileType(myFileType), getDisposable());
myFilterPanel.getComponent().setMinimumSize(new Dimension(300, 50));
final JLabel searchTargetLabel = new JLabel(SSRBundle.message("search.target.label"));
myTargetComboBox = new LinkComboBox(SSRBundle.message("complete.match.variable.name"));
@@ -369,23 +406,25 @@ public class StructuralSearchDialog extends DialogWrapper {
layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(myEditorPanel)
.addComponent(myFilterPanel.getComponent()))
)
.addComponent(myScopePanel)
.addGroup(layout.createSequentialGroup()
.addComponent(searchTargetLabel)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 5, 5)
.addComponent(myTargetComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addComponent(myTargetComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(myEditorPanel)
.addComponent(myFilterPanel.getComponent()))
)
.addComponent(myScopePanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 2, 2)
.addGroup(layout.createParallelGroup()
.addComponent(searchTargetLabel)
.addComponent(myTargetComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addComponent(myTargetComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
)
);
updateEditor();
@@ -398,7 +437,7 @@ public class StructuralSearchDialog extends DialogWrapper {
final DefaultActionGroup historyActionGroup = new DefaultActionGroup(new AnAction(getShowHistoryIcon()) {
@Override
public void actionPerformed(AnActionEvent e) {
final SelectTemplateDialog dialog = new SelectTemplateDialog(searchContext.getProject(), true, isReplaceDialog());
final SelectTemplateDialog dialog = new SelectTemplateDialog(mySearchContext.getProject(), true, isReplaceDialog());
if (!dialog.showAndGet()) {
return;
}
@@ -415,9 +454,9 @@ public class StructuralSearchDialog extends DialogWrapper {
final JLabel label = new JLabel(SSRBundle.message("search.template"));
UIUtil.installCompleteMatchInfo(label, () -> myConfiguration);
recursiveMatching = new JCheckBox(SSRBundle.message("recursive.matching.checkbox"), true);
recursiveMatching.setVisible(isRecursiveSearchEnabled());
caseSensitiveMatch = new JCheckBox(FindBundle.message("find.popup.case.sensitive"), true);
myRecursiveMatching = new JCheckBox(SSRBundle.message("recursive.matching.checkbox"), true);
myRecursiveMatching.setVisible(isRecursiveSearchEnabled());
myCaseSensitiveMatch = new JCheckBox(FindBundle.message("find.popup.case.sensitive"), true);
final List<FileType> types = new ArrayList<>();
for (FileType fileType : StructuralSearchUtil.getSuitableFileTypes()) {
if (StructuralSearchUtil.getProfileByFileType(fileType) != null) {
@@ -425,20 +464,27 @@ public class StructuralSearchDialog extends DialogWrapper {
}
}
Collections.sort(types, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
fileTypes = new FileTypeSelector(types);
fileTypes.setMinimumAndPreferredWidth(200);
fileTypes.setSelectedItem(ourFtSearchVariant, ourDialect, ourContext);
fileTypes.addItemListener(new ItemListener() {
myFileTypesComboBox = new FileTypeSelector(types);
myFileTypesComboBox.setMinimumAndPreferredWidth(200);
myFileTypesComboBox.setSelectedItem(myFileType, myDialect, myContext);
myFileTypesComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
final FileTypeInfo item = myFileTypesComboBox.getSelectedItem();
if (item == null) return;
myFileType = item.getFileType();
myDialect = item.getDialect();
myContext = item.getContext();
updateEditor();
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType);
myFilterPanel.setProfile(profile);
initiateValidation();
}
}
});
final JLabel fileTypeLabel = new JLabel(SSRBundle.message("search.dialog.file.type.label"));
fileTypeLabel.setLabelFor(fileTypes);
fileTypeLabel.setLabelFor(myFileTypesComboBox);
final DefaultActionGroup templateActionGroup = new DefaultActionGroup(
new AnAction(SSRBundle.message("save.template.text.button")) {
@@ -450,11 +496,12 @@ public class StructuralSearchDialog extends DialogWrapper {
new AnAction(SSRBundle.message("copy.existing.template.button")) {
@Override
public void actionPerformed(AnActionEvent e) {
SelectTemplateDialog dialog = new SelectTemplateDialog(searchContext.getProject(), false, isReplaceDialog());
System.out.println("StructuralSearchDialog.actionPerformed(" + e + ")");
final SelectTemplateDialog dialog = new SelectTemplateDialog(mySearchContext.getProject(), false, isReplaceDialog());
if (!dialog.showAndGet()) {
return;
}
Configuration[] configurations = dialog.getSelectedConfigurations();
final Configuration[] configurations = dialog.getSelectedConfigurations();
if (configurations.length == 1) {
setSearchPattern(configurations[0]);
}
@@ -468,15 +515,12 @@ public class StructuralSearchDialog extends DialogWrapper {
@Override
public boolean isSelected(AnActionEvent e) {
return myFilterPanel.getComponent().isVisible();
return myEditorPanel.getSecondComponent() != null;
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
if (state) {
myFilterPanel.getComponent().setMinimumSize(new Dimension(400, searchCriteriaEdit.getMinimumSize().height));
}
myFilterPanel.getComponent().setVisible(state);
myEditorPanel.setSecondComponent(state ? myFilterPanel.getComponent() : null);
}
@Override
@@ -485,7 +529,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
};
final DefaultActionGroup optionsActionGroup = new DefaultActionGroup(filterAction, templateActionGroup);
ActionToolbarImpl optionsToolbar =
final ActionToolbarImpl optionsToolbar =
(ActionToolbarImpl)ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, optionsActionGroup, true);
optionsToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY);
optionsToolbar.setForceMinimumSize(true);
@@ -499,23 +543,23 @@ public class StructuralSearchDialog extends DialogWrapper {
.addComponent(historyToolbar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(label)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 20, Integer.MAX_VALUE)
.addComponent(recursiveMatching)
.addComponent(myRecursiveMatching)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 15, 15)
.addComponent(caseSensitiveMatch)
.addComponent(myCaseSensitiveMatch)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 15, 15)
.addComponent(fileTypeLabel)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 5, 5)
.addComponent(fileTypes, 125, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(myFileTypesComboBox, 125, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(optionsToolbar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(historyToolbar)
.addComponent(label)
.addComponent(recursiveMatching)
.addComponent(caseSensitiveMatch)
.addComponent(myRecursiveMatching)
.addComponent(myCaseSensitiveMatch)
.addComponent(fileTypeLabel)
.addComponent(fileTypes)
.addComponent(myFileTypesComboBox)
.addComponent(optionsToolbar)
);
@@ -529,9 +573,9 @@ public class StructuralSearchDialog extends DialogWrapper {
if (!myRunFindActionOnClose) return null;
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 0));
openInNewTab = new JCheckBox(SSRBundle.message("open.in.new.tab.checkbox"));
openInNewTab.setSelected(FindSettings.getInstance().isShowResultsInSeparateView());
panel.add(openInNewTab, BorderLayout.EAST);
myOpenInNewTab = new JCheckBox(SSRBundle.message("open.in.new.tab.checkbox"));
myOpenInNewTab.setSelected(FindSettings.getInstance().isShowResultsInSeparateView());
panel.add(myOpenInNewTab, BorderLayout.EAST);
return panel;
}
@@ -541,7 +585,7 @@ public class StructuralSearchDialog extends DialogWrapper {
@NotNull
private List<String> getSearchVariables() {
final Editor editor = searchCriteriaEdit.getEditor();
final Editor editor = mySearchCriteriaEdit.getEditor();
if (editor == null) {
return new SmartList<>();
}
@@ -554,7 +598,7 @@ public class StructuralSearchDialog extends DialogWrapper {
}
public final Project getProject() {
return searchContext.getProject();
return mySearchContext.getProject();
}
protected boolean isReplaceDialog() {
@@ -565,8 +609,8 @@ public class StructuralSearchDialog extends DialogWrapper {
public void show() {
StructuralSearchPlugin.getInstance(getProject()).setDialogVisible(true);
if (!useLastConfiguration) {
final Editor editor = searchContext.getEditor();
if (!myUseLastConfiguration) {
final Editor editor = mySearchContext.getEditor();
boolean setSomeText = false;
if (editor != null) {
@@ -593,7 +637,7 @@ public class StructuralSearchDialog extends DialogWrapper {
@Override
public JComponent getPreferredFocusedComponent() {
return searchCriteriaEdit;
return mySearchCriteriaEdit;
}
@Override
@@ -604,24 +648,29 @@ public class StructuralSearchDialog extends DialogWrapper {
if (!result) return;
myAlarm.cancelAllRequests();
removeUnusedVariableConstraints(myConfiguration);
super.doOKAction();
if (!myRunFindActionOnClose) return;
final SearchScope selectedScope = myScopePanel.getSelectedScope();
if (selectedScope == null) return;
final SearchScope scope = myScopePanel.getScope();
if (scope == null) return;
if (scope instanceof GlobalSearchScopesCore.DirectoryScope) {
final GlobalSearchScopesCore.DirectoryScope directoryScope = (GlobalSearchScopesCore.DirectoryScope)scope;
FindInProjectSettings.getInstance(getProject()).addDirectory(directoryScope.getDirectory().getPresentableUrl());
}
final FindSettings findSettings = FindSettings.getInstance();
findSettings.setDefaultScopeName(selectedScope.getDisplayName());
findSettings.setShowResultsInSeparateView(openInNewTab.isSelected());
findSettings.setDefaultScopeName(scope.getDisplayName());
findSettings.setShowResultsInSeparateView(myOpenInNewTab.isSelected());
try {
removeUnusedVariableConstraints(myConfiguration);
ConfigurationManager.getInstance(getProject()).addHistoryConfiguration(myConfiguration);
startSearching();
}
catch (MalformedPatternException ex) {
reportMessage(SSRBundle.message("this.pattern.is.malformed.message", ex.getMessage()), searchCriteriaEdit);
reportMessage(SSRBundle.message("this.pattern.is.malformed.message", ex.getMessage()), mySearchCriteriaEdit);
}
}
@@ -639,34 +688,42 @@ public class StructuralSearchDialog extends DialogWrapper {
protected boolean isValid() {
try {
Matcher.validate(searchContext.getProject(), getConfiguration().getMatchOptions());
Matcher.validate(mySearchContext.getProject(), getConfiguration().getMatchOptions());
}
catch (MalformedPatternException ex) {
reportMessage(SSRBundle.message("this.pattern.is.malformed.message",
(ex.getMessage() != null) ? ex.getMessage() : ""), searchCriteriaEdit);
(ex.getMessage() != null) ? ex.getMessage() : ""), mySearchCriteriaEdit);
return false;
}
catch (UnsupportedPatternException ex) {
reportMessage(SSRBundle.message("this.pattern.is.unsupported.message", ex.getMessage()), searchCriteriaEdit);
reportMessage(SSRBundle.message("this.pattern.is.unsupported.message", ex.getMessage()), mySearchCriteriaEdit);
return false;
}
catch (NoMatchFoundException e) {
reportMessage(e.getMessage(), searchCriteriaEdit);
reportMessage(e.getMessage(), mySearchCriteriaEdit);
return false;
}
reportMessage(null, searchCriteriaEdit);
reportMessage(null, mySearchCriteriaEdit);
return true;
}
protected void reportMessage(String message, EditorTextField editor) {
com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(() -> {
editor.putClientProperty("JComponent.outline", message == null ? null : "error");
editor.setToolTipText(message);
editor.repaint();
//status.setText(message);
//status.setToolTipText(message);
//status.revalidate();
//statusText.setLabelFor(editor != null ? editor.getContentComponent() : null);
protected void reportMessage(String message, JComponent component) {
com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(() -> {
component.putClientProperty("JComponent.outline", message == null ? null : "error");
component.repaint();
if (message == null) return;
final Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(message, MessageType.ERROR, null).createBalloon();
if (component == mySearchCriteriaEdit) {
balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, component.getHeight())), Balloon.Position.below);
}
else {
balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, 0)), Balloon.Position.above);
}
//balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, 0)), Balloon.Position.above);
balloon.showInCenterOf(component);
Disposer.register(myDisposable, balloon);
IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(component, true));
});
}
@@ -674,26 +731,19 @@ public class StructuralSearchDialog extends DialogWrapper {
MatchOptions options = config.getMatchOptions();
if (myShowScopePanel) {
final SearchScope scope = myScopePanel.getSelectedScope();
final SearchScope scope = myScopePanel.getScope();
if (scope != null) {
boolean searchWithinHierarchy = IdeBundle.message("scope.class.hierarchy").equals(scope.getDisplayName());
final boolean searchWithinHierarchy = IdeBundle.message("scope.class.hierarchy").equals(scope.getDisplayName());
// We need to reset search within hierarchy scope during online validation since the scope works with user participation
options.setScope(searchWithinHierarchy && !myDoingOkAction ? GlobalSearchScope.projectScope(getProject()) : scope);
}
}
options.setRecursiveSearch(isRecursiveSearchEnabled() && recursiveMatching.isSelected());
final FileTypeInfo info = fileTypes.getSelectedItem();
ourFtSearchVariant = info != null ? info.getFileType() : null;
ourDialect = info != null ? info.getDialect() : null;
ourContext = info != null ? info.getContext() : null;
FileType fileType = ourFtSearchVariant;
options.setFileType(fileType);
options.setDialect(ourDialect);
options.setPatternContext(ourContext);
options.setSearchPattern(searchCriteriaEdit.getDocument().getText());
options.setCaseSensitiveMatch(caseSensitiveMatch.isSelected());
options.setRecursiveSearch(isRecursiveSearchEnabled() && myRecursiveMatching.isSelected());
options.setFileType(myFileType);
options.setDialect(myDialect);
options.setPatternContext(myContext);
options.setSearchPattern(mySearchCriteriaEdit.getDocument().getText());
options.setCaseSensitiveMatch(myCaseSensitiveMatch.isSelected());
}
@Override
@@ -705,7 +755,7 @@ public class StructuralSearchDialog extends DialogWrapper {
public void dispose() {
StructuralSearchPlugin.getInstance(getProject()).setDialogVisible(false);
myAlarm.cancelAllRequests();
searchCriteriaEdit.removeNotify();
mySearchCriteriaEdit.removeNotify();
super.dispose();
}
@@ -1,25 +1,37 @@
<?xml version='1.0'?>
<!DOCTYPE xs:schema SYSTEM "XMLSchema.dtd" [
<!ELEMENT p ANY>
<!ELEMENT a ANY>
<!ATTLIST a href CDATA #IMPLIED>
<!ELEMENT hr ANY>
<!ELEMENT h1 ANY>
<!ELEMENT br ANY>
]>
<xs:schema targetNamespace="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/1999/xhtml">
<xs:annotation>
<xs:documentation xmlns="http://www.w3.org/1999/xhtml">
<xs:documentation>
<h1>XML Schema instance namespace</h1>
<p>See <a href="http://www.w3.org/TR/xmlschema-1/">The XML Schema draft recommendation</a> for an introduction</p>
<p>See <a href="http://www.w3.org/TR/xmlschema-1/">the XML Schema
Recommendation</a> for an introduction</p>
<hr />
$Date: 2004/06/09 12:44:55 $<br />
$Id: XMLSchema-instance.xsd,v 1.1 2004/06/09 12:44:55 max Exp $
$Date: 2001/03/16 20:25:57 $<br />
$Id: XMLSchema-instance.xsd,v 1.4 2001/03/16 20:25:57 ht Exp $
</xs:documentation>
</xs:annotation>
<xs:annotation>
<xs:documentation><p>This schema should never be used as such:
<a href="http://www.w3.org/TR/xmlschema-1/#no-xsi">the XML
Schema Recommendation</a> forbids the declaration of
attributes in this namespace</p>
</xs:documentation>
</xs:annotation>
<xs:attribute name="type">
<xs:annotation>
<xs:documentation>No definitions are provided here, as
this schema is never used as such</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="nill"/>
<xs:attribute name="nil"/>
<xs:attribute name="type"/>
<xs:attribute name="schemaLocation"/>
<xs:attribute name="noNamespaceSchemaLocation"/>
</xs:schema>