SSR: Added context to the file type selector in the Structural Search dialog (IDEA-193238)

This commit is contained in:
Pavel Dolgov
2018-06-08 15:05:47 +03:00
parent 9fb79f89bf
commit 241fdd2fdc
4 changed files with 105 additions and 77 deletions
@@ -21,11 +21,19 @@ public class FileTypeInfo {
private final FileType myFileType;
private final Language myDialect;
private final String myContext;
private final boolean myEnabled;
private final String myDescription;
public FileTypeInfo(@NotNull FileType fileType, @Nullable Language dialect, boolean duplicated) {
public FileTypeInfo(@NotNull FileType fileType,
@Nullable Language dialect,
@Nullable String context,
boolean enabled,
boolean duplicated) {
myFileType = fileType;
myDialect = dialect;
myContext = context;
myEnabled = enabled;
myDescription = getDescription(fileType, duplicated);
}
@@ -39,8 +47,24 @@ public class FileTypeInfo {
return myDialect;
}
@Nullable
public String getContext() {
return myContext;
}
@NotNull
public String getText() {
if (myDialect != null) {
return myDialect.getDisplayName();
}
if (myContext != null) {
return myContext + " Context";
}
return myFileType.getName();
}
@NotNull
public String getSearchText() {
if (myDialect != null) {
return myDialect.getDisplayName();
}
@@ -49,14 +73,27 @@ public class FileTypeInfo {
@NotNull
public String getFullText() {
if (myDialect == null) {
return myDescription;
if (myDialect != null) {
return myDescription + " - " + myDialect.getDisplayName();
}
return myDescription + " - " + myDialect.getDisplayName();
if (myContext != null) {
return myDescription + " - " + myContext + " Context";
}
return myDescription;
}
public boolean isDialect() {
return myDialect != null;
public boolean isNested() {
return myDialect != null || myContext != null;
}
public boolean isEnabled() {
return myEnabled;
}
public boolean isEqualTo(@NotNull FileType fileType, @Nullable Language dialect, @Nullable String context) {
return Objects.equals(myFileType, fileType) &&
Objects.equals(myDialect, dialect) &&
Objects.equals(myContext, context);
}
@NotNull
@@ -75,11 +112,17 @@ public class FileTypeInfo {
if (!(o instanceof FileTypeInfo)) return false;
FileTypeInfo info = (FileTypeInfo)o;
return Objects.equals(myFileType, info.myFileType) &&
Objects.equals(myDialect, info.myDialect);
Objects.equals(myDialect, info.myDialect) &&
Objects.equals(myContext, info.myContext);
}
@Override
public int hashCode() {
return Objects.hash(myFileType, myDialect);
return Objects.hash(myFileType, myDialect, myContext);
}
@Override
public String toString() {
return getFullText();
}
}
@@ -6,6 +6,9 @@ import com.intellij.lang.LanguageUtil;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.structuralsearch.StructuralSearchProfile;
import com.intellij.structuralsearch.StructuralSearchProfileBase;
import com.intellij.structuralsearch.StructuralSearchUtil;
import com.intellij.ui.ComboboxSpeedSearch;
import com.intellij.ui.LayeredIcon;
import com.intellij.ui.ListCellRendererWrapper;
@@ -43,35 +46,65 @@ public class FileTypeSelector extends ComboBox<FileTypeInfo> {
return info != null ? info.getFileType() : null;
}
public void setSelectedItem(@NotNull FileType type, @Nullable Language dialect) {
public void setSelectedItem(@NotNull FileType type, @Nullable Language dialect, @Nullable String context) {
DefaultComboBoxModel<FileTypeInfo> model = (DefaultComboBoxModel<FileTypeInfo>)getModel();
for (int i = 0; i < model.getSize(); i++) {
FileTypeInfo info = model.getElementAt(i);
if (info.getFileType() == type && info.getDialect() == dialect) {
if (info.isEqualTo(type, dialect, context)) {
setSelectedItem(info);
return;
}
}
}
@Override
public void setSelectedItem(Object anObject) {
if (anObject instanceof FileTypeInfo) {
final FileTypeInfo selectedInfo = (FileTypeInfo)anObject;
if (!selectedInfo.isEnabled()) {
final MyComboBoxModel model = (MyComboBoxModel)getModel();
final int index = model.getIndexOf(selectedInfo);
if (index >= 0 && index + 1 < model.getSize()) {
final FileTypeInfo nextInfo = model.getElementAt(index + 1);
super.setSelectedItem(nextInfo);
return;
}
}
}
super.setSelectedItem(anObject);
}
@NotNull
private static DefaultComboBoxModel<FileTypeInfo> createModel(List<FileType> types) {
final List<FileTypeInfo> infos = new ArrayList<>();
for (FileType fileType : types) {
boolean duplicated = isDuplicated(fileType, types);
infos.add(new FileTypeInfo(fileType, null, duplicated));
final boolean duplicated = isDuplicated(fileType, types);
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(fileType);
if (profile instanceof StructuralSearchProfileBase) {
final String[] contextNames = ((StructuralSearchProfileBase)profile).getContextNames();
if (contextNames.length != 0) {
infos.add(new FileTypeInfo(fileType, null, null, false, duplicated));
for (String contextName: contextNames) {
infos.add(new FileTypeInfo(fileType, null, contextName, true, duplicated));
}
continue; // proceed with the next file type
}
}
infos.add(new FileTypeInfo(fileType, null, null, true, duplicated));
if (fileType instanceof LanguageFileType) {
final Language language = ((LanguageFileType)fileType).getLanguage();
final Language[] languageDialects = LanguageUtil.getLanguageDialects(language);
Arrays.sort(languageDialects, Comparator.comparing(Language::getDisplayName));
for (Language dialect : languageDialects) {
infos.add(new FileTypeInfo(fileType, dialect, duplicated));
infos.add(new FileTypeInfo(fileType, dialect, null, true, duplicated));
}
}
}
return new DefaultComboBoxModel<>(infos.toArray(FileTypeInfo.EMPTY_ARRAY));
return new MyComboBoxModel(infos);
}
private static boolean isDuplicated(@NotNull FileType fileType, @NotNull List<FileType> types) {
@@ -84,13 +117,19 @@ public class FileTypeSelector extends ComboBox<FileTypeInfo> {
return false;
}
private static class MyComboBoxModel extends DefaultComboBoxModel<FileTypeInfo> {
public MyComboBoxModel(List<FileTypeInfo> infos) {
super(infos.toArray(FileTypeInfo.EMPTY_ARRAY));
}
}
private static class MyCellRenderer extends ListCellRendererWrapper<FileTypeInfo> {
private static final Icon EMPTY_ICON = EmptyIcon.ICON_18;
private static final Icon WIDE_EMPTY_ICON = JBUI.scale(EmptyIcon.create(32, 18));
@Override
public void customize(JList list, FileTypeInfo info, int index, boolean selected, boolean hasFocus) {
if (info.isDialect() && index >= 0) {
if (info.isNested() && index >= 0) {
setIcon(WIDE_EMPTY_ICON);
setText(info.getText());
}
@@ -117,7 +156,7 @@ public class FileTypeSelector extends ComboBox<FileTypeInfo> {
@Override
protected String getElementText(Object element) {
return ((FileTypeInfo)element).getText();
return ((FileTypeInfo)element).getSearchText();
}
}
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.TextRange;
@@ -68,7 +67,6 @@ public class SearchDialog extends DialogWrapper {
private JCheckBox caseSensitiveMatch;
private FileTypeSelector fileTypes;
private JComboBox<String> contexts;
private JLabel status;
private JLabel statusText;
@@ -210,31 +208,23 @@ public class SearchDialog extends DialogWrapper {
Collections.sort(types, (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()));
fileTypes = new FileTypeSelector(types);
contexts = new ComboBox<>(60);
final JLabel jLabel = new JLabel(SSRBundle.message("search.dialog.file.type.label"));
final JLabel jLabel2 = new JLabel(SSRBundle.message("search.dialog.context.label"));
searchOptions.add(UIUtil.createOptionLine(jLabel, fileTypes, jLabel2, contexts));
searchOptions.add(UIUtil.createOptionLine(jLabel, fileTypes));
jLabel.setLabelFor(fileTypes);
jLabel2.setLabelFor(contexts);
detectFileTypeAndDialect();
fileTypes.setSelectedItem(ourFtSearchVariant, ourDialect);
fileTypes.setSelectedItem(ourFtSearchVariant, ourDialect, ourContext);
fileTypes.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
updateContexts();
updateEditor();
initiateValidation();
}
}
});
contexts.setSelectedItem(ourContext);
updateContexts();
}
void updateEditor() {
@@ -250,23 +240,6 @@ public class SearchDialog extends DialogWrapper {
}
}
void updateContexts() {
final FileType fileType = fileTypes.getSelectedFileType();
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(fileType);
if (profile instanceof StructuralSearchProfileBase) {
final String[] contextNames = ((StructuralSearchProfileBase)profile).getContextNames();
if (contextNames.length > 0) {
contexts.setModel(new DefaultComboBoxModel<>(contextNames));
contexts.setSelectedItem(contextNames[0]);
contexts.setEnabled(true);
return;
}
}
contexts.setSelectedItem(null);
contexts.setEnabled(false);
}
private void detectFileTypeAndDialect() {
final PsiFile file = searchContext.getFile();
if (file != null) {
@@ -321,10 +294,7 @@ public class SearchDialog extends DialogWrapper {
recursiveMatching.setSelected(isRecursiveSearchEnabled() && matchOptions.isRecursiveSearch());
caseSensitiveMatch.setSelected(matchOptions.isCaseSensitiveMatch());
fileTypes.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect());
if (matchOptions.getPatternContext() != null) {
contexts.setSelectedItem(matchOptions.getPatternContext());
}
fileTypes.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect(), matchOptions.getPatternContext());
searchCriteriaEdit.putUserData(SubstitutionShortInfoHandler.CURRENT_CONFIGURATION_KEY, myConfiguration);
}
@@ -706,7 +676,7 @@ public class SearchDialog extends DialogWrapper {
final FileTypeInfo info = fileTypes.getSelectedItem();
ourFtSearchVariant = info != null ? info.getFileType() : null;
ourDialect = info != null ? info.getDialect() : null;
ourContext = (String)contexts.getSelectedItem();
ourContext = info != null ? info.getContext() : null;
FileType fileType = ourFtSearchVariant;
options.setFileType(fileType);
options.setDialect(ourDialect);
@@ -27,7 +27,6 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
@@ -77,7 +76,6 @@ public class StructuralSearchDialog extends DialogWrapper {
private JCheckBox caseSensitiveMatch;
private FileTypeSelector fileTypes;
private JComboBox<String> contexts;
protected Configuration myConfiguration;
private JCheckBox openInNewTab;
@@ -223,23 +221,6 @@ public class StructuralSearchDialog extends DialogWrapper {
}
}
void updateContexts() {
final FileType fileType = fileTypes.getSelectedFileType();
final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(fileType);
if (profile instanceof StructuralSearchProfileBase) {
final String[] contextNames = ((StructuralSearchProfileBase)profile).getContextNames();
if (contextNames.length > 0) {
contexts.setModel(new DefaultComboBoxModel<>(contextNames));
contexts.setSelectedItem(contextNames[0]);
contexts.setEnabled(true);
return;
}
}
contexts.setSelectedItem(null);
contexts.setEnabled(false);
}
private void detectFileTypeAndDialect() {
final PsiFile file = searchContext.getFile();
if (file != null) {
@@ -299,10 +280,7 @@ public class StructuralSearchDialog extends DialogWrapper {
recursiveMatching.setSelected(isRecursiveSearchEnabled() && matchOptions.isRecursiveSearch());
caseSensitiveMatch.setSelected(matchOptions.isCaseSensitiveMatch());
fileTypes.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect());
if (matchOptions.getPatternContext() != null) {
contexts.setSelectedItem(matchOptions.getPatternContext());
}
fileTypes.setSelectedItem(matchOptions.getFileType(), matchOptions.getDialect(), matchOptions.getPatternContext());
final Editor editor = searchCriteriaEdit.getEditor();
if (editor != null) {
editor.putUserData(SubstitutionShortInfoHandler.CURRENT_CONFIGURATION_KEY, myConfiguration);
@@ -402,12 +380,11 @@ 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);
fileTypes.setSelectedItem(ourFtSearchVariant, ourDialect, ourContext);
fileTypes.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
updateContexts();
updateEditor();
initiateValidation();
}
@@ -415,7 +392,6 @@ public class StructuralSearchDialog extends DialogWrapper {
});
final JLabel fileTypeLabel = new JLabel(SSRBundle.message("search.dialog.file.type.label"));
fileTypeLabel.setLabelFor(fileTypes);
contexts = new ComboBox<>(60); // todo remove me, dummy
final DefaultActionGroup templateActionGroup = new DefaultActionGroup(
new AnAction(SSRBundle.message("save.template.text.button")) {
@@ -653,7 +629,7 @@ public class StructuralSearchDialog extends DialogWrapper {
final FileTypeInfo info = fileTypes.getSelectedItem();
ourFtSearchVariant = info != null ? info.getFileType() : null;
ourDialect = info != null ? info.getDialect() : null;
ourContext = (String)contexts.getSelectedItem();
ourContext = info != null ? info.getContext() : null;
FileType fileType = ourFtSearchVariant;
options.setFileType(fileType);
options.setDialect(ourDialect);