mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
WEB-17977 Feature: Configure Patterns for Automatic Nesting of Transpiled Files
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,4 +20,6 @@ package com.intellij.ide.projectView;
|
||||
*/
|
||||
public interface ProjectViewSettings extends ViewSettings {
|
||||
boolean isShowExcludedFiles();
|
||||
|
||||
default boolean isUseFileNestingRules() {return true;}
|
||||
}
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ide.projectView.impl;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.projectView.impl.ProjectViewFileNestingService.NestingRule;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.intellij.ui.DumbAwareActionButton;
|
||||
import com.intellij.ui.ToolbarDecorator;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.table.TableView;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ui.ColumnInfo;
|
||||
import com.intellij.util.ui.ElementProducer;
|
||||
import com.intellij.util.ui.ListTableModel;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FileNestingInProjectViewDialog extends DialogWrapper {
|
||||
|
||||
private final JBCheckBox myUseNestingRulesCheckBox;
|
||||
private final JPanel myRulesPanel;
|
||||
private final TableView<NestingRule> myTable;
|
||||
|
||||
protected FileNestingInProjectViewDialog(@NotNull final Project project) {
|
||||
super(project);
|
||||
setTitle(IdeBundle.message("file.nesting.dialog.title"));
|
||||
|
||||
myUseNestingRulesCheckBox = new JBCheckBox(IdeBundle.message("use.file.nesting.rules.checkbox"));
|
||||
myUseNestingRulesCheckBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
UIUtil.setEnabled(myRulesPanel, myUseNestingRulesCheckBox.isSelected(), true);
|
||||
}
|
||||
});
|
||||
|
||||
myTable = createTable();
|
||||
myRulesPanel = createRulesPanel(myTable);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return "project.view.file.nesting.dialog";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
final JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
mainPanel.add(myUseNestingRulesCheckBox, BorderLayout.NORTH);
|
||||
mainPanel.add(myRulesPanel, BorderLayout.CENTER);
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
private static JPanel createRulesPanel(@NotNull final TableView<NestingRule> table) {
|
||||
final ToolbarDecorator toolbarDecorator =
|
||||
ToolbarDecorator.createDecorator(table,
|
||||
new ElementProducer<NestingRule>() {
|
||||
@Override
|
||||
public boolean canCreateElement() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NestingRule createElement() {
|
||||
return new NestingRule();
|
||||
}
|
||||
})
|
||||
.disableUpDownActions()
|
||||
.addExtraAction(
|
||||
new DumbAwareActionButton(IdeBundle.message("file.nesting.use.default.ruleset"), AllIcons.Actions.Reset_to_default) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final List<NestingRule> rules = new ArrayList<>();
|
||||
for (NestingRule rule : ProjectViewFileNestingService.DEFAULT_NESTING_RULES) {
|
||||
rules.add(new NestingRule(rule.getParentFileSuffix(), rule.getChildFileSuffix()));
|
||||
}
|
||||
table.getListTableModel().setItems(rules);
|
||||
}
|
||||
});
|
||||
return toolbarDecorator.createPanel();
|
||||
}
|
||||
|
||||
private static TableView<NestingRule> createTable() {
|
||||
ListTableModel<NestingRule> model = new ListTableModel<>(
|
||||
new ColumnInfo<NestingRule, String>("Parent file suffix") {
|
||||
@Override
|
||||
public boolean isCellEditable(NestingRule rule) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueOf(NestingRule rule) {
|
||||
return rule.getParentFileSuffix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(NestingRule rule, String value) {
|
||||
rule.setParentFileSuffix(value.trim());
|
||||
}
|
||||
},
|
||||
new ColumnInfo<NestingRule, String>("Child file suffix") {
|
||||
@Override
|
||||
public boolean isCellEditable(NestingRule rule) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueOf(NestingRule rule) {
|
||||
return rule.getChildFileSuffix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(NestingRule rule, String value) {
|
||||
rule.setChildFileSuffix(value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return new TableView<>(model);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ValidationInfo doValidate() {
|
||||
if (!myUseNestingRulesCheckBox.isSelected()) return null;
|
||||
|
||||
List<NestingRule> items = myTable.getListTableModel().getItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
final NestingRule rule = items.get(i);
|
||||
final int row = i + 1;
|
||||
if (rule.getParentFileSuffix().isEmpty()) {
|
||||
return new ValidationInfo("Parent file suffix must not be empty (see row " + row + ")", null);
|
||||
}
|
||||
if (rule.getChildFileSuffix().isEmpty()) {
|
||||
return new ValidationInfo("Child file suffix must not be empty (see row " + row + ")", null);
|
||||
}
|
||||
if (rule.getChildFileSuffix().equals(rule.getParentFileSuffix())) {
|
||||
return new ValidationInfo(
|
||||
"Parent and child file suffixes must not be equal ('" + rule.getParentFileSuffix() + "', see row " + row + ")", null);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
myTable.stopEditing();
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
public void reset(boolean useFileNestingRules) {
|
||||
myUseNestingRulesCheckBox.setSelected(useFileNestingRules);
|
||||
UIUtil.setEnabled(myRulesPanel, myUseNestingRulesCheckBox.isSelected(), true);
|
||||
|
||||
final List<NestingRule> rules = new ArrayList<>();
|
||||
for (NestingRule rule : ProjectViewFileNestingService.getInstance().getRules()) {
|
||||
rules.add(new NestingRule(rule.getParentFileSuffix(), rule.getChildFileSuffix()));
|
||||
}
|
||||
myTable.getListTableModel().setItems(rules);
|
||||
}
|
||||
|
||||
public void apply(@NotNull final Consumer<Boolean> useNestingRulesOptionConsumer) {
|
||||
useNestingRulesOptionConsumer.consume(myUseNestingRulesCheckBox.isSelected());
|
||||
|
||||
if (myUseNestingRulesCheckBox.isSelected()) {
|
||||
final List<NestingRule> result = new ArrayList<>();
|
||||
for (NestingRule rule : myTable.getListTableModel().getItems()) {
|
||||
result.add(new NestingRule(rule.getParentFileSuffix(), rule.getChildFileSuffix()));
|
||||
}
|
||||
ProjectViewFileNestingService.getInstance().setRules(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-67
@@ -15,15 +15,13 @@
|
||||
*/
|
||||
package com.intellij.ide.projectView.impl;
|
||||
|
||||
import com.intellij.ide.projectView.ProjectViewNestingRulesProvider;
|
||||
import com.intellij.ide.projectView.TreeStructureProvider;
|
||||
import com.intellij.ide.projectView.ViewSettings;
|
||||
import com.intellij.ide.projectView.*;
|
||||
import com.intellij.ide.projectView.impl.ProjectViewFileNestingService.NestingRule;
|
||||
import com.intellij.ide.projectView.impl.nodes.NestingTreeNode;
|
||||
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
|
||||
import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -48,50 +46,49 @@ import java.util.*;
|
||||
* {@code com.intellij.projectViewNestingRulesProvider} extensions.
|
||||
*
|
||||
* @see ProjectViewNestingRulesProvider
|
||||
* @see ProjectViewFileNestingService
|
||||
* @see FileNestingInProjectViewDialog
|
||||
*/
|
||||
public class NestingTreeStructureProvider implements TreeStructureProvider, DumbAware {
|
||||
|
||||
private static ExtensionPointName<ProjectViewNestingRulesProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.projectViewNestingRulesProvider");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(NestingTreeStructureProvider.class);
|
||||
|
||||
private int myBaseListHashCode = 0;
|
||||
private Set<NestingRule> myNestingRules;
|
||||
|
||||
@NotNull
|
||||
private Collection<NestingRule> getNestingRules() {
|
||||
if (myNestingRules == null) {
|
||||
final List<NestingRule> baseRules = ProjectViewFileNestingService.getInstance().getRules();
|
||||
final int baseRulesHashCode = baseRules.hashCode();
|
||||
|
||||
if (myNestingRules == null || myBaseListHashCode != baseRulesHashCode) {
|
||||
myNestingRules = new THashSet<>();
|
||||
myBaseListHashCode = baseRulesHashCode;
|
||||
|
||||
final MultiMap<String, String> childToParentSuffix = new MultiMap<>();
|
||||
final MultiMap<String, String> parentToChildSuffix = new MultiMap<>();
|
||||
|
||||
final ProjectViewNestingRulesProvider.Consumer consumer = new ProjectViewNestingRulesProvider.Consumer() {
|
||||
@Override
|
||||
public void addNestingRule(@NotNull final String parentFileSuffix, @NotNull final String childFileSuffix) {
|
||||
LOG.assertTrue(!parentFileSuffix.isEmpty() && !childFileSuffix.isEmpty(), "file suffix must not be empty");
|
||||
LOG.assertTrue(!parentFileSuffix.equals(childFileSuffix), "parent and child suffixes must be different: " + parentFileSuffix);
|
||||
for (NestingRule rule : baseRules) {
|
||||
final String parentFileSuffix = rule.getParentFileSuffix();
|
||||
final String childFileSuffix = rule.getChildFileSuffix();
|
||||
if (parentFileSuffix.isEmpty() || childFileSuffix.isEmpty()) continue; // shouldn't happen, checked on component loading and in UI
|
||||
if (parentFileSuffix.equals(childFileSuffix)) continue; // shouldn't happen, checked on component loading and in UI
|
||||
|
||||
myNestingRules.add(new NestingRule(parentFileSuffix, childFileSuffix));
|
||||
childToParentSuffix.putValue(childFileSuffix, parentFileSuffix);
|
||||
parentToChildSuffix.putValue(parentFileSuffix, childFileSuffix);
|
||||
myNestingRules.add(rule);
|
||||
childToParentSuffix.putValue(childFileSuffix, parentFileSuffix);
|
||||
parentToChildSuffix.putValue(parentFileSuffix, childFileSuffix);
|
||||
|
||||
// for all cases like A -> B -> C we also add a rule A -> C
|
||||
for (String s : parentToChildSuffix.get(childFileSuffix)) {
|
||||
myNestingRules.add(new NestingRule(parentFileSuffix, s));
|
||||
parentToChildSuffix.putValue(parentFileSuffix, s);
|
||||
childToParentSuffix.putValue(s, parentFileSuffix);
|
||||
}
|
||||
|
||||
for (String s : childToParentSuffix.get(parentFileSuffix)) {
|
||||
myNestingRules.add(new NestingRule(s, childFileSuffix));
|
||||
parentToChildSuffix.putValue(s, childFileSuffix);
|
||||
childToParentSuffix.putValue(childFileSuffix, s);
|
||||
}
|
||||
// for all cases like A -> B -> C we also add a rule A -> C
|
||||
for (String s : parentToChildSuffix.get(childFileSuffix)) {
|
||||
myNestingRules.add(new NestingRule(parentFileSuffix, s));
|
||||
parentToChildSuffix.putValue(parentFileSuffix, s);
|
||||
childToParentSuffix.putValue(s, parentFileSuffix);
|
||||
}
|
||||
};
|
||||
|
||||
for (ProjectViewNestingRulesProvider provider : EP_NAME.getExtensions()) {
|
||||
provider.addFileNestingRules(consumer);
|
||||
for (String s : childToParentSuffix.get(parentFileSuffix)) {
|
||||
myNestingRules.add(new NestingRule(s, childFileSuffix));
|
||||
parentToChildSuffix.putValue(s, childFileSuffix);
|
||||
childToParentSuffix.putValue(childFileSuffix, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +110,7 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
public Collection<AbstractTreeNode> modify(@NotNull final AbstractTreeNode parent,
|
||||
@NotNull final Collection<AbstractTreeNode> children,
|
||||
final ViewSettings settings) {
|
||||
if (!(settings instanceof ProjectViewSettings) || !((ProjectViewSettings)settings).isUseFileNestingRules()) return children;
|
||||
if (!(parent instanceof PsiDirectoryNode)) return children;
|
||||
|
||||
final Collection<NestingRule> rules = getNestingRules();
|
||||
@@ -153,6 +151,11 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
@NotNull final VirtualFile parentFile) {
|
||||
LOG.assertTrue(!parentFile.isDirectory());
|
||||
|
||||
final AbstractProjectViewPane pane = ProjectView.getInstance(project).getProjectViewPaneById(ProjectViewPane.ID);
|
||||
if (pane instanceof ProjectViewPane) {
|
||||
if (!((ProjectViewPane)pane).isUseFileNestingRules()) return Collections.emptyList();
|
||||
}
|
||||
|
||||
final VirtualFile dir = parentFile.getParent();
|
||||
if (dir == null) return Collections.emptyList();
|
||||
|
||||
@@ -181,8 +184,8 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
final boolean matchesParent = c.first;
|
||||
|
||||
if (matchesParent) {
|
||||
final String baseName = childName.substring(0, childName.length() - rule.myParentFileSuffix.length());
|
||||
if (parentFile.getName().equals(baseName + rule.myChildFileSuffix)) {
|
||||
final String baseName = childName.substring(0, childName.length() - rule.getParentFileSuffix().length());
|
||||
if (parentFile.getName().equals(baseName + rule.getChildFileSuffix())) {
|
||||
return Collections.emptyList(); // parentFile itself appears to be a child of childFile
|
||||
}
|
||||
}
|
||||
@@ -195,8 +198,8 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
final boolean matchesChild = c.second;
|
||||
|
||||
if (matchesChild) {
|
||||
final String baseName = childName.substring(0, childName.length() - rule.myChildFileSuffix.length());
|
||||
if (parentFile.getName().equals(baseName + rule.myParentFileSuffix)) {
|
||||
final String baseName = childName.substring(0, childName.length() - rule.getChildFileSuffix().length());
|
||||
if (parentFile.getName().equals(baseName + rule.getParentFileSuffix())) {
|
||||
result.add(new ChildFileInfo(child, baseName));
|
||||
}
|
||||
}
|
||||
@@ -274,14 +277,14 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
}
|
||||
|
||||
if (matchesParent) {
|
||||
final String baseName = fileName.substring(0, fileName.length() - rule.myParentFileSuffix.length());
|
||||
final String baseName = fileName.substring(0, fileName.length() - rule.getParentFileSuffix().length());
|
||||
final Edge<PsiFileNode> edge = getOrCreateEdge(baseNameAndRuleToEdge, baseName, rule);
|
||||
edge.from = (PsiFileNode)node;
|
||||
updateInfoIfEdgeComplete(parentToChildren, allChildNodes, edge);
|
||||
}
|
||||
|
||||
if (matchesChild) {
|
||||
final String baseName = fileName.substring(0, fileName.length() - rule.myChildFileSuffix.length());
|
||||
final String baseName = fileName.substring(0, fileName.length() - rule.getChildFileSuffix().length());
|
||||
final Edge<PsiFileNode> edge = getOrCreateEdge(baseNameAndRuleToEdge, baseName, rule);
|
||||
edge.to = (PsiFileNode)node;
|
||||
updateInfoIfEdgeComplete(parentToChildren, allChildNodes, edge);
|
||||
@@ -293,11 +296,11 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
}
|
||||
|
||||
private static Couple<Boolean> checkMatchingAsParentOrChild(@NotNull final NestingRule rule, @NotNull final String fileName) {
|
||||
boolean matchesParent = !fileName.equals(rule.myParentFileSuffix) && fileName.endsWith(rule.myParentFileSuffix);
|
||||
boolean matchesChild = !fileName.equals(rule.myChildFileSuffix) && fileName.endsWith(rule.myChildFileSuffix);
|
||||
boolean matchesParent = !fileName.equals(rule.getParentFileSuffix()) && fileName.endsWith(rule.getParentFileSuffix());
|
||||
boolean matchesChild = !fileName.equals(rule.getChildFileSuffix()) && fileName.endsWith(rule.getChildFileSuffix());
|
||||
|
||||
if (matchesParent && matchesChild) {
|
||||
if (rule.myParentFileSuffix.length() > rule.myChildFileSuffix.length()) {
|
||||
if (rule.getParentFileSuffix().length() > rule.getChildFileSuffix().length()) {
|
||||
matchesChild = false;
|
||||
}
|
||||
else {
|
||||
@@ -334,32 +337,6 @@ public class NestingTreeStructureProvider implements TreeStructureProvider, Dumb
|
||||
}
|
||||
}
|
||||
|
||||
private static class NestingRule {
|
||||
@NotNull private final String myParentFileSuffix;
|
||||
@NotNull private final String myChildFileSuffix;
|
||||
|
||||
public NestingRule(@NotNull String parentFileSuffix, @NotNull String childFileSuffix) {
|
||||
myParentFileSuffix = parentFileSuffix;
|
||||
myChildFileSuffix = childFileSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myParentFileSuffix + "->" + myChildFileSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof NestingRule &&
|
||||
myParentFileSuffix.equals(((NestingRule)o).myParentFileSuffix) &&
|
||||
myChildFileSuffix.equals(((NestingRule)o).myChildFileSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myParentFileSuffix.hashCode() + 239 * myChildFileSuffix.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Edge<T> {
|
||||
@Nullable private T from;
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ide.projectView.impl;
|
||||
|
||||
import com.intellij.ide.projectView.ProjectViewNestingRulesProvider;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.util.containers.SortedList;
|
||||
import com.intellij.util.xmlb.annotations.AbstractCollection;
|
||||
import com.intellij.util.xmlb.annotations.Attribute;
|
||||
import com.intellij.util.xmlb.annotations.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds data used by {@link NestingTreeStructureProvider} and {@link FileNestingInProjectViewDialog}.
|
||||
*/
|
||||
@State(
|
||||
name = "ProjectViewFileNesting",
|
||||
storages = @Storage("ui.lnf.xml")
|
||||
)
|
||||
public class ProjectViewFileNestingService implements PersistentStateComponent<ProjectViewFileNestingService.MyState> {
|
||||
private static final Logger LOG = Logger.getInstance(ProjectViewFileNestingService.class);
|
||||
|
||||
private static final ExtensionPointName<ProjectViewNestingRulesProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.projectViewNestingRulesProvider");
|
||||
|
||||
public static final NestingRule[] DEFAULT_NESTING_RULES = loadDefaultNestingRules();
|
||||
|
||||
private MyState myState = new MyState();
|
||||
|
||||
@NotNull
|
||||
public static ProjectViewFileNestingService getInstance() {
|
||||
return ServiceManager.getService(ProjectViewFileNestingService.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static NestingRule[] loadDefaultNestingRules() {
|
||||
final List<NestingRule> result = new SortedList<>(Comparator.comparing(o -> o.getParentFileSuffix()));
|
||||
|
||||
final ProjectViewNestingRulesProvider.Consumer consumer = new ProjectViewNestingRulesProvider.Consumer() {
|
||||
@Override
|
||||
public void addNestingRule(@NotNull final String parentFileSuffix, @NotNull final String childFileSuffix) {
|
||||
LOG.assertTrue(!parentFileSuffix.isEmpty() && !childFileSuffix.isEmpty(), "file suffix must not be empty");
|
||||
LOG.assertTrue(!parentFileSuffix.equals(childFileSuffix), "parent and child suffixes must be different: " + parentFileSuffix);
|
||||
result.add(new NestingRule(parentFileSuffix, childFileSuffix));
|
||||
}
|
||||
};
|
||||
|
||||
for (ProjectViewNestingRulesProvider provider : EP_NAME.getExtensions()) {
|
||||
provider.addFileNestingRules(consumer);
|
||||
}
|
||||
|
||||
return result.toArray(new NestingRule[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MyState getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(@NotNull final MyState state) {
|
||||
myState = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* This list of rules is used for serialization and for UI.
|
||||
* See also {@link NestingTreeStructureProvider}, it adjusts this list of rules to match its needs
|
||||
*/
|
||||
@NotNull
|
||||
public List<NestingRule> getRules() {
|
||||
return myState.myRules;
|
||||
}
|
||||
|
||||
public void setRules(@NotNull final List<NestingRule> rules) {
|
||||
myState.myRules.clear();
|
||||
myState.myRules.addAll(rules);
|
||||
}
|
||||
|
||||
public static class MyState {
|
||||
@Tag("nesting-rules")
|
||||
@AbstractCollection(surroundWithTag = false)
|
||||
public List<NestingRule> myRules = new SortedList<>(Comparator.comparing(o -> o.getParentFileSuffix()));
|
||||
|
||||
public MyState() {
|
||||
myRules.addAll(Arrays.asList(DEFAULT_NESTING_RULES));
|
||||
}
|
||||
}
|
||||
|
||||
public static class NestingRule {
|
||||
@NotNull private String myParentFileSuffix;
|
||||
|
||||
@NotNull private String myChildFileSuffix;
|
||||
|
||||
@SuppressWarnings("unused") // used by serializer
|
||||
public NestingRule() {
|
||||
this("", "");
|
||||
}
|
||||
|
||||
public NestingRule(@NotNull String parentFileSuffix, @NotNull String childFileSuffix) {
|
||||
myParentFileSuffix = parentFileSuffix;
|
||||
myChildFileSuffix = childFileSuffix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Attribute("parent-file-suffix")
|
||||
public String getParentFileSuffix() {
|
||||
return myParentFileSuffix;
|
||||
}
|
||||
|
||||
public void setParentFileSuffix(@NotNull final String parentFileSuffix) {
|
||||
myParentFileSuffix = parentFileSuffix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Attribute("child-file-suffix")
|
||||
public String getChildFileSuffix() {
|
||||
return myChildFileSuffix;
|
||||
}
|
||||
|
||||
public void setChildFileSuffix(@NotNull final String childFileSuffix) {
|
||||
myChildFileSuffix = childFileSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myParentFileSuffix + "->" + myChildFileSuffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof NestingRule &&
|
||||
myParentFileSuffix.equals(((NestingRule)o).myParentFileSuffix) &&
|
||||
myChildFileSuffix.equals(((NestingRule)o).myChildFileSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myParentFileSuffix.hashCode() + 239 * myChildFileSuffix.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,7 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.actionSystem.ToggleAction;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
@@ -51,7 +52,10 @@ import java.awt.*;
|
||||
public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
@NonNls public static final String ID = "ProjectPane";
|
||||
public static final String SHOW_EXCLUDED_FILES_OPTION = "show-excluded-files";
|
||||
private static final String USE_FILE_NESTING_RULES = "use-file-nesting-rules";
|
||||
|
||||
private boolean myShowExcludedFiles = true;
|
||||
private boolean myUseFileNestingRules = true;
|
||||
|
||||
public ProjectViewPane(Project project) {
|
||||
super(project);
|
||||
@@ -116,6 +120,8 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
super.readExternal(element);
|
||||
String showExcludedOption = JDOMExternalizerUtil.readField(element, SHOW_EXCLUDED_FILES_OPTION);
|
||||
myShowExcludedFiles = showExcludedOption == null || Boolean.parseBoolean(showExcludedOption);
|
||||
String useFileNestingRules = JDOMExternalizerUtil.readField(element, USE_FILE_NESTING_RULES);
|
||||
myUseFileNestingRules = useFileNestingRules == null || Boolean.parseBoolean(useFileNestingRules);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,11 +130,19 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
if (!myShowExcludedFiles) {
|
||||
JDOMExternalizerUtil.writeField(element, SHOW_EXCLUDED_FILES_OPTION, String.valueOf(false));
|
||||
}
|
||||
if (!myUseFileNestingRules) {
|
||||
JDOMExternalizerUtil.writeField(element, USE_FILE_NESTING_RULES, String.valueOf(false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToolbarActions(DefaultActionGroup actionGroup) {
|
||||
actionGroup.addAction(new ShowExcludedFilesAction()).setAsSecondary(true);
|
||||
actionGroup.addAction(new ConfigureFilesNestingAction()).setAsSecondary(true);
|
||||
}
|
||||
|
||||
public boolean isUseFileNestingRules() {
|
||||
return myUseFileNestingRules;
|
||||
}
|
||||
|
||||
// should be first
|
||||
@@ -186,6 +200,11 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
return myShowExcludedFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseFileNestingRules() {
|
||||
return myUseFileNestingRules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToBuildChildrenInBackground(Object element) {
|
||||
return Registry.is("ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground");
|
||||
@@ -218,4 +237,27 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
presentation.setEnabledAndVisible(projectView.getCurrentProjectViewPane() == ProjectViewPane.this);
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfigureFilesNestingAction extends DumbAwareAction {
|
||||
private ConfigureFilesNestingAction() {
|
||||
super(IdeBundle.message("action.file.nesting.in.project.view"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull final AnActionEvent e) {
|
||||
final ProjectView projectView = ProjectView.getInstance(myProject);
|
||||
e.getPresentation().setEnabledAndVisible(projectView.getCurrentProjectViewPane() == ProjectViewPane.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
final FileNestingInProjectViewDialog dialog = new FileNestingInProjectViewDialog(myProject);
|
||||
dialog.reset(myUseFileNestingRules);
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
dialog.apply(useFileNestingRules -> myUseFileNestingRules = useFileNestingRules);
|
||||
updateFromRoot(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,6 +583,10 @@ action.show.libraries.contents=Show Libraries Contents
|
||||
action.show.hide.library.contents=Show/Hide Library Contents
|
||||
action.show.excluded.files=Show Excluded Files
|
||||
action.show.hide.excluded.files=Show/Hide Excluded Files
|
||||
action.file.nesting.in.project.view=File Nesting...
|
||||
file.nesting.dialog.title=File Nesting
|
||||
use.file.nesting.rules.checkbox=&Show files with the same names as nested according to the rules below
|
||||
file.nesting.use.default.ruleset=Use the default ruleset
|
||||
action.manual.order=Manual Order
|
||||
action.sort.by.type=Sort by Type
|
||||
action.show.structure=Show Structure
|
||||
|
||||
@@ -1028,6 +1028,8 @@
|
||||
order="last"/>
|
||||
<stripTrailingSpacesFilterFactory implementation="com.intellij.psi.codeStyle.KeepTrailingSpacesOnEmptyLinesFilterFactory"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.ide.projectView.impl.ProjectViewFileNestingService"
|
||||
serviceImplementation="com.intellij.ide.projectView.impl.ProjectViewFileNestingService"/>
|
||||
<treeStructureProvider implementation="com.intellij.ide.projectView.impl.NestingTreeStructureProvider" order="last"/>
|
||||
<refactoring.safeDeleteProcessor implementation="com.intellij.refactoring.safeDelete.RelatedFilesSafeDeleteProcessorDelegate"/>
|
||||
<automaticRenamerFactory implementation="com.intellij.refactoring.rename.RelatedFilesRenamerFactory"/>
|
||||
|
||||
Reference in New Issue
Block a user