mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Custom folding region navigation (IDEA-81770)
This commit is contained in:
@@ -6,7 +6,6 @@ import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -132,7 +131,7 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements D
|
||||
* @param node The node which may contain custom region start.
|
||||
* @return True if the node marks a custom region start.
|
||||
*/
|
||||
protected final boolean isCustomRegionStart(ASTNode node) {
|
||||
public final boolean isCustomRegionStart(ASTNode node) {
|
||||
if (isCustomFoldingCandidate(node)) {
|
||||
String nodeText = node.getText();
|
||||
CustomFoldingProvider defaultProvider = getDefaultProvider(nodeText);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.intellij.lang.customFolding;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
/**
|
||||
* @author Rustam Vishnyakov
|
||||
*/
|
||||
public class GotoCustomRegionAction extends AnAction implements DumbAware {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final Project project = e.getProject();
|
||||
final Editor editor = e.getData(PlatformDataKeys.EDITOR);
|
||||
if (Boolean.TRUE.equals(e.getData(PlatformDataKeys.IS_MODAL_CONTEXT))) {
|
||||
return;
|
||||
}
|
||||
if (project != null && editor != null) {
|
||||
if (DumbService.getInstance(project).isDumb()) {
|
||||
DumbService.getInstance(project).showDumbModeNotification(IdeBundle.message("goto.custom.region.message.dumb.mode"));
|
||||
return;
|
||||
}
|
||||
CommandProcessor processor = CommandProcessor.getInstance();
|
||||
processor.executeCommand(
|
||||
project,
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
GotoCustomRegionDialog dialog = new GotoCustomRegionDialog(project, editor);
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
PsiElement navigationElement = dialog.getNavigationElement();
|
||||
if (navigationElement != null) {
|
||||
navigateTo(editor, navigationElement);
|
||||
IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
IdeBundle.message("goto.custom.region.command"),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
Presentation presentation = e.getPresentation();
|
||||
presentation.setText("Custom Region...");
|
||||
final Editor editor = e.getData(PlatformDataKeys.EDITOR);
|
||||
final Project project = e.getProject();
|
||||
boolean isAvailable = editor != null && project != null;
|
||||
presentation.setEnabled(isAvailable);
|
||||
presentation.setVisible(isAvailable);
|
||||
}
|
||||
|
||||
private static void navigateTo(Editor editor, PsiElement element) {
|
||||
int offset = element.getTextRange().getStartOffset();
|
||||
if (offset >= 0 && offset < editor.getDocument().getTextLength()) {
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
|
||||
editor.getSelectionModel().removeSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.lang.customFolding.GotoCustomRegionDialog">
|
||||
<grid id="27dc6" binding="myContentPane" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<scrollpane id="7557d" class="com.intellij.ui.components.JBScrollPane" binding="myScrollPane">
|
||||
<constraints border-constraint="Center"/>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="2f5d5" class="com.intellij.ui.components.JBList" binding="myRegionsList" custom-create="true">
|
||||
<constraints/>
|
||||
<properties>
|
||||
<selectionMode value="0"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</scrollpane>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.intellij.lang.customFolding;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.folding.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.psi.FileViewProvider;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Rustam Vishnyakov
|
||||
*/
|
||||
public class GotoCustomRegionDialog extends DialogWrapper {
|
||||
private JBList myRegionsList;
|
||||
private JPanel myContentPane;
|
||||
private JBScrollPane myScrollPane;
|
||||
private final Editor myEditor;
|
||||
private final Project myProject;
|
||||
|
||||
protected GotoCustomRegionDialog(@Nullable Project project, @NotNull Editor editor) {
|
||||
super(project);
|
||||
myEditor = editor;
|
||||
myProject = project;
|
||||
Collection<FoldingDescriptor> descriptors = getCustomFoldingDescriptors();
|
||||
init();
|
||||
if (descriptors.size() == 0) {
|
||||
myScrollPane.setVisible(false);
|
||||
myContentPane.add(new JLabel(IdeBundle.message("goto.custom.region.message.unavailable")), BorderLayout.NORTH);
|
||||
setOKActionEnabled(false);
|
||||
}
|
||||
else {
|
||||
myRegionsList.setModel(new MyListModel(orderByPosition(descriptors)));
|
||||
myRegionsList.setSelectedIndex(0);
|
||||
}
|
||||
setTitle(IdeBundle.message("goto.custom.region.command"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
if (!myRegionsList.isEmpty()) {
|
||||
return myRegionsList;
|
||||
}
|
||||
return super.getPreferredFocusedComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myContentPane;
|
||||
}
|
||||
|
||||
private Collection<FoldingDescriptor> getCustomFoldingDescriptors() {
|
||||
Set<FoldingDescriptor> foldingDescriptors = new HashSet<FoldingDescriptor>();
|
||||
final Document document = myEditor.getDocument();
|
||||
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
|
||||
PsiFile file = documentManager != null ? documentManager.getPsiFile(document) : null;
|
||||
if (file != null) {
|
||||
final FileViewProvider viewProvider = file.getViewProvider();
|
||||
for (final Language language : viewProvider.getLanguages()) {
|
||||
final PsiFile psi = viewProvider.getPsi(language);
|
||||
final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(language);
|
||||
if (psi != null) {
|
||||
for (FoldingDescriptor descriptor : LanguageFolding.buildFoldingDescriptors(foldingBuilder, psi, document, false)) {
|
||||
CustomFoldingBuilder customFoldingBuilder = getCustomFoldingBuilder(foldingBuilder, descriptor);
|
||||
if (customFoldingBuilder != null) {
|
||||
if (customFoldingBuilder.isCustomRegionStart(descriptor.getElement())) {
|
||||
foldingDescriptors.add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return foldingDescriptors;
|
||||
}
|
||||
|
||||
private static Collection<FoldingDescriptor> orderByPosition(Collection<FoldingDescriptor> descriptors) {
|
||||
List<FoldingDescriptor> sorted = new ArrayList<FoldingDescriptor>(descriptors.size());
|
||||
sorted.addAll(descriptors);
|
||||
Collections.sort(sorted, new Comparator<FoldingDescriptor>() {
|
||||
@Override
|
||||
public int compare(FoldingDescriptor descriptor1, FoldingDescriptor descriptor2) {
|
||||
int pos1 = descriptor1.getElement().getTextRange().getStartOffset();
|
||||
int pos2 = descriptor2.getElement().getTextRange().getStartOffset();
|
||||
return pos1 - pos2;
|
||||
}
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
myRegionsList = new JBList();
|
||||
myRegionsList.setPreferredSize(new Dimension(200, 100));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CustomFoldingBuilder getCustomFoldingBuilder(FoldingBuilder builder, FoldingDescriptor descriptor) {
|
||||
if (builder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)builder;
|
||||
FoldingBuilder originalBuilder = descriptor.getElement().getUserData(CompositeFoldingBuilder.FOLDING_BUILDER);
|
||||
if (originalBuilder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)originalBuilder;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class MyListModel extends DefaultListModel {
|
||||
private MyListModel(Collection<FoldingDescriptor> descriptors) {
|
||||
for (FoldingDescriptor descriptor : descriptors) {
|
||||
super.addElement(new MyFoldingDescriptorWrapper(descriptor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyFoldingDescriptorWrapper {
|
||||
private final @NotNull FoldingDescriptor myDescriptor;
|
||||
|
||||
private MyFoldingDescriptorWrapper(@NotNull FoldingDescriptor descriptor) {
|
||||
myDescriptor = descriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FoldingDescriptor getDescriptor() {
|
||||
return myDescriptor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String toString() {
|
||||
return myDescriptor.getPlaceholderText();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getNavigationElement() {
|
||||
Object selection = myRegionsList.getSelectedValue();
|
||||
if (selection instanceof MyFoldingDescriptorWrapper) {
|
||||
return ((MyFoldingDescriptorWrapper)selection).getDescriptor().getElement().getPsi();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1090,3 +1090,6 @@ whatsnew.action.custom.text=What''s _New in {0}
|
||||
whatsnew.action.custom.description=Find out about the new features in this version of {0}
|
||||
diff.dialog.title=Diff Between ''{0}'' and ''{1}''
|
||||
|
||||
goto.custom.region.command=Go to Custom Region
|
||||
goto.custom.region.message.dumb.mode=Go to Custom Region action is not available until indices are built.
|
||||
goto.custom.region.message.unavailable=There are no custom folding regions in the current file.
|
||||
@@ -406,6 +406,9 @@
|
||||
<action id="GotoLine">
|
||||
<keyboard-shortcut first-keystroke="control G"/>
|
||||
</action>
|
||||
<action id="GotoCustomRegion">
|
||||
<keyboard-shortcut first-keystroke="control shift G"/>
|
||||
</action>
|
||||
<action id="FindInPath">
|
||||
<keyboard-shortcut first-keystroke="control shift F"/>
|
||||
</action>
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
<action id="GotoClass" class="com.intellij.ide.actions.GotoClassAction"/>
|
||||
<action id="GotoFile" class="com.intellij.ide.actions.GotoFileAction"/>
|
||||
<action id="GotoSymbol" class="com.intellij.ide.actions.GotoSymbolAction"/>
|
||||
<action id="GotoCustomRegion" class="com.intellij.lang.customFolding.GotoCustomRegionAction"/>
|
||||
|
||||
<add-to-group group-id="GoToMenu" anchor="first"/>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user