mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IDEA-207961 Copy TBX reference URL: support multiple paths, testes provided
This commit is contained in:
@@ -15,7 +15,7 @@ import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JavaVirtualFileQualifiedNameProvider implements CopyReferenceAction.VirtualFileQualifiedNameProvider {
|
||||
public class JavaVirtualFileQualifiedNameProvider implements VirtualFileQualifiedNameProvider {
|
||||
@Nullable
|
||||
@Override
|
||||
public String getQualifiedName(@NotNull Project project, @NotNull VirtualFile virtualFile) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class X<caret> {
|
||||
String s = "x/x.txt";
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
text
|
||||
@@ -0,0 +1 @@
|
||||
text
|
||||
@@ -0,0 +1,4 @@
|
||||
class C2 {
|
||||
public <caret>C2(int i) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class C2 {
|
||||
<selection><caret>public C2(int i) {
|
||||
}
|
||||
</selection>
|
||||
<selection><caret>public C2(int i) {
|
||||
}
|
||||
</selection>
|
||||
<selection><caret>public C2(int i) {
|
||||
}
|
||||
</selection>
|
||||
<selection><caret>public C2(int i) {
|
||||
}
|
||||
</selection>
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class XXX {
|
||||
void f() {
|
||||
new C2();
|
||||
new <caret>
|
||||
}
|
||||
}
|
||||
class C2 {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
module DidYouMean
|
||||
module Correctabl<caret>e
|
||||
def original_message
|
||||
method(:to_s).super_method.call
|
||||
end
|
||||
|
||||
def to_s
|
||||
msg = super.dup
|
||||
|
||||
if !cause.respond_to?(:corrections) || cause.corrections.empty?
|
||||
msg << DidYouMean.formatter.message_for(corrections)
|
||||
end
|
||||
|
||||
msg
|
||||
rescue
|
||||
super
|
||||
end
|
||||
|
||||
def corrections
|
||||
spell_checker.corrections
|
||||
end
|
||||
|
||||
def spell_checker
|
||||
@spell_checker ||= SPELL_CHECKERS[self.class.to_s].new(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
class X<caret> {
|
||||
String s = "";
|
||||
<selection>
|
||||
String s = "";
|
||||
String s = "";
|
||||
String s = "";
|
||||
</selection>
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
|
||||
import junit.framework.TestCase
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
|
||||
class CreateTBXReferenceTest : LightPlatformCodeInsightFixtureTestCase() {
|
||||
override fun getTestDataPath(): String {
|
||||
return "${JavaTestUtil.getJavaTestDataPath()}/codeInsight/copyTBXReference/"
|
||||
}
|
||||
|
||||
fun testMethodReference() {
|
||||
doTest("jetbrains://idea/navigate/reference?project=light_temp&fqn=C2#C2")
|
||||
}
|
||||
|
||||
fun testSelection() {
|
||||
doTest("jetbrains://idea/navigate/reference?project=light_temp&fqn=X&selection=3:3-7:3")
|
||||
}
|
||||
|
||||
fun testMultipleSelections() {
|
||||
doTest(
|
||||
"jetbrains://idea/navigate/reference?project=light_temp&path=MultipleSelections.java:11:5&selection1=2:5-4:5&selection2=5:5-7:5&selection3=8:5-10:5&selection4=11:5-13:5")
|
||||
}
|
||||
|
||||
fun testRubyClassReference() {
|
||||
doTest("jetbrains://idea/navigate/reference?project=light_temp&path=RubyClassReference.rb:2:20",
|
||||
getTestName(false) + ".rb")
|
||||
}
|
||||
|
||||
fun testPathWithLocation() {
|
||||
doTest("jetbrains://idea/navigate/reference?project=light_temp&path=PathWithLocation.java:4:13")
|
||||
}
|
||||
|
||||
fun testMultipleFiles() {
|
||||
val file1 = myFixture.configureByFile("File1.java")
|
||||
val file2 = myFixture.configureByFile("File2.java")
|
||||
|
||||
doTest("jetbrains://idea/navigate/reference?project=light_temp&path1=File1.java&path2=File2.java",
|
||||
fileName = null,
|
||||
additionalDataContext = mapOf(LangDataKeys.PSI_ELEMENT_ARRAY.name to arrayOf(file1, file2)))
|
||||
}
|
||||
|
||||
private fun doTest(expectedURL: String,
|
||||
fileName: String? = getTestName(false) + ".java",
|
||||
additionalDataContext: Map<String, Any> = hashMapOf()) {
|
||||
fileName?.let { myFixture.configureByFile(fileName) }
|
||||
|
||||
var dataContext = DataManager.getInstance().getDataContext(myFixture.editor.component)
|
||||
dataContext = SimpleDataContext.getSimpleContext(additionalDataContext, dataContext)
|
||||
val action = ActionManager.getInstance().getAction("CopyTBXReference")
|
||||
|
||||
action.actionPerformed(
|
||||
AnActionEvent(null, dataContext, ActionPlaces.MAIN_MENU, action.templatePresentation, ActionManager.getInstance(), 0))
|
||||
|
||||
TestCase.assertEquals(expectedURL, CopyPasteManager.getInstance().contents?.getTransferData(DataFlavor.stringFlavor))
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,37 @@
|
||||
// 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.actions;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.IdentifierUtil;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.dnd.FileCopyPasteUtil;
|
||||
import com.intellij.ide.scratch.RootType;
|
||||
import com.intellij.ide.scratch.ScratchFileService;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.ex.StatusBarEx;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileSystemItem;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.ide.actions.CopyReferenceUtil.*;
|
||||
|
||||
/**
|
||||
* @author Alexey
|
||||
*/
|
||||
public class CopyReferenceAction extends DumbAwareAction {
|
||||
public static final DataFlavor ourFlavor = FileCopyPasteUtil.createJvmDataFlavor(MyTransferable.class);
|
||||
public static final DataFlavor ourFlavor = FileCopyPasteUtil.createJvmDataFlavor(CopyReferenceFQNTransferable.class);
|
||||
|
||||
public CopyReferenceAction() {
|
||||
super();
|
||||
@@ -95,7 +76,11 @@ public class CopyReferenceAction extends DumbAwareAction {
|
||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
List<PsiElement> elements = getElementsToCopy(editor, dataContext);
|
||||
|
||||
if (!doCopy(elements, project, editor) && editor != null && project != null) {
|
||||
String copy = CopyReferenceUtil.doCopy(elements, editor);
|
||||
if (copy != null) {
|
||||
CopyPasteManager.getInstance().setContents(new CopyReferenceFQNTransferable(copy));
|
||||
setStatusBarText(project, IdeBundle.message("message.reference.to.fqn.has.been.copied", copy));
|
||||
} else if (editor != null && project != null) {
|
||||
Document document = editor.getDocument();
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getCachedPsiFile(document);
|
||||
if (file != null) {
|
||||
@@ -106,216 +91,23 @@ public class CopyReferenceAction extends DumbAwareAction {
|
||||
return;
|
||||
}
|
||||
|
||||
HighlightManager highlightManager = HighlightManager.getInstance(project);
|
||||
EditorColorsManager manager = EditorColorsManager.getInstance();
|
||||
TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
if (elements.size() == 1 && editor != null && project != null) {
|
||||
PsiElement element = elements.get(0);
|
||||
PsiElement nameIdentifier = IdentifierUtil.getNameIdentifier(element);
|
||||
if (nameIdentifier != null) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{nameIdentifier}, attributes, true, null);
|
||||
} else {
|
||||
PsiReference reference = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
|
||||
if (reference != null) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiReference[]{reference}, attributes, true, null);
|
||||
} else if (element != PsiDocumentManager.getInstance(project).getCachedPsiFile(editor.getDocument())) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{element}, attributes, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> getElementsToCopy(@Nullable final Editor editor, final DataContext dataContext) {
|
||||
List<PsiElement> elements = ContainerUtil.newArrayList();
|
||||
if (editor != null) {
|
||||
PsiReference reference = TargetElementUtil.findReference(editor);
|
||||
if (reference != null) {
|
||||
ContainerUtil.addIfNotNull(elements, reference.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
PsiElement[] psiElements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(dataContext);
|
||||
if (psiElements != null) {
|
||||
Collections.addAll(elements, psiElements);
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
ContainerUtil.addIfNotNull(elements, CommonDataKeys.PSI_ELEMENT.getData(dataContext));
|
||||
}
|
||||
|
||||
if (elements.isEmpty() && editor == null) {
|
||||
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
|
||||
if (project != null && files != null) {
|
||||
for (VirtualFile file : files) {
|
||||
ContainerUtil.addIfNotNull(elements, PsiManager.getInstance(project).findFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ContainerUtil.mapNotNull(elements, element -> element instanceof PsiFile && !((PsiFile)element).getViewProvider().isPhysical() ? null : adjustElement(element));
|
||||
}
|
||||
|
||||
private static PsiElement adjustElement(PsiElement element) {
|
||||
PsiElement adjustedElement = QualifiedNameProviderUtil.adjustElementToCopy(element);
|
||||
return adjustedElement != null ? adjustedElement : element;
|
||||
highlight(editor, project, elements);
|
||||
}
|
||||
|
||||
public static boolean doCopy(final PsiElement element, final Project project) {
|
||||
return doCopy(Arrays.asList(element), project, null);
|
||||
return doCopy(Arrays.asList(element), project);
|
||||
}
|
||||
|
||||
private static boolean doCopy(List<? extends PsiElement> elements, @Nullable final Project project, @Nullable Editor editor) {
|
||||
if (elements.isEmpty()) return false;
|
||||
|
||||
List<String> fqns = ContainerUtil.newArrayList();
|
||||
for (PsiElement element : elements) {
|
||||
String fqn = elementToFqn(element, editor);
|
||||
if (fqn == null) return false;
|
||||
|
||||
fqns.add(fqn);
|
||||
}
|
||||
|
||||
String toCopy = StringUtil.join(fqns, "\n");
|
||||
|
||||
CopyPasteManager.getInstance().setContents(new MyTransferable(toCopy));
|
||||
|
||||
private static boolean doCopy(List<? extends PsiElement> elements, @Nullable final Project project) {
|
||||
String toCopy = CopyReferenceUtil.doCopy(elements, null);
|
||||
CopyPasteManager.getInstance().setContents(new CopyReferenceFQNTransferable(toCopy));
|
||||
setStatusBarText(project, IdeBundle.message("message.reference.to.fqn.has.been.copied", toCopy));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void setStatusBarText(Project project, String message) {
|
||||
if (project != null) {
|
||||
final StatusBarEx statusBar = (StatusBarEx)WindowManager.getInstance().getStatusBar(project);
|
||||
if (statusBar != null) {
|
||||
statusBar.setInfo(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyTransferable implements Transferable {
|
||||
private final String fqn;
|
||||
|
||||
MyTransferable(String fqn) {
|
||||
this.fqn = fqn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[]{ourFlavor, DataFlavor.stringFlavor};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
return ArrayUtilRt.find(getTransferDataFlavors(), flavor) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||
if (isDataFlavorSupported(flavor)) {
|
||||
return fqn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String elementToFqn(@Nullable final PsiElement element) {
|
||||
return elementToFqn(element, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String elementToFqn(@Nullable final PsiElement element, @Nullable Editor editor) {
|
||||
String result = getQualifiedNameFromProviders(element);
|
||||
if (result != null) return result;
|
||||
|
||||
if (editor != null) { //IDEA-70346
|
||||
PsiReference reference = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
|
||||
if (reference != null) {
|
||||
result = getQualifiedNameFromProviders(reference.resolve());
|
||||
if (result != null) return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (element instanceof PsiFile) {
|
||||
return FileUtil.toSystemIndependentName(getFileFqn((PsiFile)element));
|
||||
}
|
||||
if (element instanceof PsiDirectory) {
|
||||
return FileUtil.toSystemIndependentName(getVirtualFileFqn(((PsiDirectory)element).getVirtualFile(), element.getProject()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getQualifiedNameFromProviders(@Nullable PsiElement element) {
|
||||
if (element == null) return null;
|
||||
DumbService.getInstance(element.getProject()).setAlternativeResolveEnabled(true);
|
||||
try {
|
||||
return QualifiedNameProviderUtil.getQualifiedName(element);
|
||||
}
|
||||
finally {
|
||||
DumbService.getInstance(element.getProject()).setAlternativeResolveEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getFileFqn(final PsiFile file) {
|
||||
final VirtualFile virtualFile = file.getVirtualFile();
|
||||
return virtualFile == null ? file.getName() : getVirtualFileFqn(virtualFile, file.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getVirtualFileFqn(@NotNull VirtualFile virtualFile, @NotNull Project project) {
|
||||
for (VirtualFileQualifiedNameProvider provider : VirtualFileQualifiedNameProvider.EP_NAME.getExtensionList()) {
|
||||
String qualifiedName = provider.getQualifiedName(project, virtualFile);
|
||||
if (qualifiedName != null) {
|
||||
return qualifiedName;
|
||||
}
|
||||
}
|
||||
|
||||
Module module = ProjectFileIndex.getInstance(project).getModuleForFile(virtualFile, false);
|
||||
if (module != null) {
|
||||
for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) {
|
||||
String relativePath = VfsUtilCore.getRelativePath(virtualFile, root);
|
||||
if (relativePath != null) {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String relativePath = VfsUtilCore.getRelativePath(virtualFile, project.getBaseDir());
|
||||
if (relativePath != null) {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
RootType rootType = RootType.forFile(virtualFile);
|
||||
if (rootType != null) {
|
||||
VirtualFile scratchRootVirtualFile =
|
||||
VfsUtil.findFileByIoFile(new File(ScratchFileService.getInstance().getRootPath(rootType)), false);
|
||||
if (scratchRootVirtualFile != null) {
|
||||
String scratchRelativePath = VfsUtilCore.getRelativePath(virtualFile, scratchRootVirtualFile);
|
||||
if (scratchRelativePath != null) {
|
||||
return scratchRelativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return virtualFile.getPath();
|
||||
}
|
||||
|
||||
public interface VirtualFileQualifiedNameProvider {
|
||||
ExtensionPointName<VirtualFileQualifiedNameProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.virtualFileQualifiedNameProvider");
|
||||
|
||||
/**
|
||||
* @return {@code virtualFile} fqn (relative path for example) or null if not handled by this provider
|
||||
*/
|
||||
@Nullable
|
||||
String getQualifiedName(@NotNull Project project, @NotNull VirtualFile virtualFile);
|
||||
return CopyReferenceUtil.elementToFqn(element, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.actions;
|
||||
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
|
||||
class CopyReferenceFQNTransferable implements Transferable {
|
||||
private final String fqn;
|
||||
|
||||
CopyReferenceFQNTransferable(String fqn) {
|
||||
this.fqn = fqn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[]{CopyReferenceAction.ourFlavor, DataFlavor.stringFlavor};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
return ArrayUtilRt.find(getTransferDataFlavors(), flavor) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||
if (isDataFlavorSupported(flavor)) {
|
||||
return fqn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.actions;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.IdentifierUtil;
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.ide.scratch.RootType;
|
||||
import com.intellij.ide.scratch.ScratchFileService;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.ex.StatusBarEx;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CopyReferenceUtil {
|
||||
static void highlight(Editor editor, Project project, List<PsiElement> elements) {
|
||||
HighlightManager highlightManager = HighlightManager.getInstance(project);
|
||||
EditorColorsManager manager = EditorColorsManager.getInstance();
|
||||
TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
if (elements.size() == 1 && editor != null && project != null) {
|
||||
PsiElement element = elements.get(0);
|
||||
PsiElement nameIdentifier = IdentifierUtil.getNameIdentifier(element);
|
||||
if (nameIdentifier != null) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{nameIdentifier}, attributes, true, null);
|
||||
}
|
||||
else {
|
||||
PsiReference reference = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
|
||||
if (reference != null) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiReference[]{reference}, attributes, true, null);
|
||||
}
|
||||
else if (element != PsiDocumentManager.getInstance(project).getCachedPsiFile(editor.getDocument())) {
|
||||
highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{element}, attributes, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<PsiElement> getElementsToCopy(@Nullable final Editor editor, final DataContext dataContext) {
|
||||
List<PsiElement> elements = ContainerUtil.newArrayList();
|
||||
if (editor != null) {
|
||||
PsiReference reference = TargetElementUtil.findReference(editor);
|
||||
if (reference != null) {
|
||||
ContainerUtil.addIfNotNull(elements, reference.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
PsiElement[] psiElements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(dataContext);
|
||||
if (psiElements != null) {
|
||||
Collections.addAll(elements, psiElements);
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
ContainerUtil.addIfNotNull(elements, CommonDataKeys.PSI_ELEMENT.getData(dataContext));
|
||||
}
|
||||
|
||||
if (elements.isEmpty() && editor == null) {
|
||||
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);
|
||||
if (project != null && files != null) {
|
||||
for (VirtualFile file : files) {
|
||||
ContainerUtil.addIfNotNull(elements, PsiManager.getInstance(project).findFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ContainerUtil.mapNotNull(elements, element -> element instanceof PsiFile && !((PsiFile)element).getViewProvider().isPhysical()
|
||||
? null
|
||||
: adjustElement(element));
|
||||
}
|
||||
|
||||
static PsiElement adjustElement(PsiElement element) {
|
||||
PsiElement adjustedElement = QualifiedNameProviderUtil.adjustElementToCopy(element);
|
||||
return adjustedElement != null ? adjustedElement : element;
|
||||
}
|
||||
|
||||
static void setStatusBarText(Project project, String message) {
|
||||
if (project != null) {
|
||||
final StatusBarEx statusBar = (StatusBarEx)WindowManager.getInstance().getStatusBar(project);
|
||||
if (statusBar != null) {
|
||||
statusBar.setInfo(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getQualifiedNameFromProviders(@Nullable PsiElement element) {
|
||||
if (element == null) return null;
|
||||
DumbService.getInstance(element.getProject()).setAlternativeResolveEnabled(true);
|
||||
try {
|
||||
return QualifiedNameProviderUtil.getQualifiedName(element);
|
||||
}
|
||||
finally {
|
||||
DumbService.getInstance(element.getProject()).setAlternativeResolveEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
static String doCopy(List<? extends PsiElement> elements, @Nullable Editor editor) {
|
||||
if (elements.isEmpty()) return null;
|
||||
|
||||
List<String> fqns = ContainerUtil.newArrayList();
|
||||
for (PsiElement element : elements) {
|
||||
String fqn = elementToFqn(element, editor);
|
||||
if (fqn == null) return null;
|
||||
|
||||
fqns.add(fqn);
|
||||
}
|
||||
|
||||
return StringUtil.join(fqns, "\n");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String elementToFqn(@Nullable final PsiElement element, @Nullable Editor editor) {
|
||||
String result = getQualifiedNameFromProviders(element);
|
||||
if (result != null) return result;
|
||||
|
||||
if (editor != null) { //IDEA-70346
|
||||
PsiReference reference = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
|
||||
if (reference != null) {
|
||||
result = getQualifiedNameFromProviders(reference.resolve());
|
||||
if (result != null) return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (element instanceof PsiFile) {
|
||||
return FileUtil.toSystemIndependentName(getFileFqn((PsiFile)element));
|
||||
}
|
||||
if (element instanceof PsiDirectory) {
|
||||
return FileUtil.toSystemIndependentName(getVirtualFileFqn(((PsiDirectory)element).getVirtualFile(), element.getProject()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String getFileFqn(final PsiFile file) {
|
||||
final VirtualFile virtualFile = file.getVirtualFile();
|
||||
return virtualFile == null ? file.getName() : getVirtualFileFqn(virtualFile, file.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getVirtualFileFqn(@NotNull VirtualFile virtualFile, @NotNull Project project) {
|
||||
for (VirtualFileQualifiedNameProvider provider : VirtualFileQualifiedNameProvider.EP_NAME.getExtensionList()) {
|
||||
String qualifiedName = provider.getQualifiedName(project, virtualFile);
|
||||
if (qualifiedName != null) {
|
||||
return qualifiedName;
|
||||
}
|
||||
}
|
||||
|
||||
Module module = ProjectFileIndex.getInstance(project).getModuleForFile(virtualFile, false);
|
||||
if (module != null) {
|
||||
for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) {
|
||||
String relativePath = VfsUtilCore.getRelativePath(virtualFile, root);
|
||||
if (relativePath != null) {
|
||||
return relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String relativePath = VfsUtilCore.getRelativePath(virtualFile, project.getBaseDir());
|
||||
if (relativePath != null) {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
RootType rootType = RootType.forFile(virtualFile);
|
||||
if (rootType != null) {
|
||||
VirtualFile scratchRootVirtualFile =
|
||||
VfsUtil.findFileByIoFile(new File(ScratchFileService.getInstance().getRootPath(rootType)), false);
|
||||
if (scratchRootVirtualFile != null) {
|
||||
String scratchRelativePath = VfsUtilCore.getRelativePath(virtualFile, scratchRootVirtualFile);
|
||||
if (scratchRelativePath != null) {
|
||||
return scratchRelativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return virtualFile.getPath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.actions
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.ide.actions.CopyReferenceUtil.*
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.JetBrainsProtocolHandler
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.FQN_KEY
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.NAVIGATE_COMMAND
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.PATH_KEY
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.PROJECT_NAME_KEY
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.REFERENCE_TARGET
|
||||
import com.intellij.openapi.project.impl.JBProtocolNavigateCommand.Companion.SELECTION
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.PlatformUtils.*
|
||||
import java.awt.datatransfer.StringSelection
|
||||
import java.util.stream.Collectors
|
||||
import java.util.stream.IntStream
|
||||
|
||||
class CopyTBXReferenceAction : DumbAwareAction() {
|
||||
init {
|
||||
isEnabledInModalContext = true
|
||||
setInjectedContext(true)
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
var plural = false
|
||||
var enabled: Boolean
|
||||
var paths = false
|
||||
|
||||
val dataContext = e.dataContext
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext)
|
||||
if (editor != null && FileDocumentManager.getInstance().getFile(editor.document) != null) {
|
||||
enabled = true
|
||||
}
|
||||
else {
|
||||
val elements = getElementsToCopy(editor, dataContext)
|
||||
enabled = !elements.isEmpty()
|
||||
plural = elements.size > 1
|
||||
paths = elements.stream().allMatch { el -> el is PsiFileSystemItem && getQualifiedNameFromProviders(el) == null }
|
||||
}
|
||||
|
||||
enabled = enabled && (ActionPlaces.MAIN_MENU == e.place)
|
||||
e.presentation.isEnabled = enabled
|
||||
if (ActionPlaces.isPopupPlace(e.place)) {
|
||||
e.presentation.isVisible = enabled
|
||||
}
|
||||
else {
|
||||
e.presentation.isVisible = true
|
||||
}
|
||||
e.presentation.text = if (paths)
|
||||
|
||||
if (plural) "Cop&y Toolbox Relative Paths URL" else "Cop&y Toolbox Relative Path URL"
|
||||
else if (plural) "Cop&y Toolbox References URL" else "Cop&y Toolbox Reference URL"
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val dataContext = e.dataContext
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext)
|
||||
val project = CommonDataKeys.PROJECT.getData(dataContext)
|
||||
val elements = getElementsToCopy(editor, dataContext)
|
||||
|
||||
if (project == null) {
|
||||
LOG.warn("'Copy TBX Reference' action cannot find project.")
|
||||
return
|
||||
}
|
||||
|
||||
var copy = createJetbrainsLink(project, elements, editor)
|
||||
if (copy != null) {
|
||||
CopyPasteManager.getInstance().setContents(CopyReferenceFQNTransferable(copy))
|
||||
setStatusBarText(project, IdeBundle.message("message.reference.to.fqn.has.been.copied", copy))
|
||||
}
|
||||
else if (editor != null) {
|
||||
val document = editor.document
|
||||
val file = PsiDocumentManager.getInstance(project).getCachedPsiFile(document)
|
||||
if (file != null) {
|
||||
val logicalPosition = editor.caretModel.logicalPosition
|
||||
val path = "${getFileFqn(file)}:${logicalPosition.line + 1}:${logicalPosition.column + 1}"
|
||||
copy = createLink(editor, project, createRefs(true, path, ""))
|
||||
CopyPasteManager.getInstance().setContents(StringSelection(copy))
|
||||
setStatusBarText(project, "$copy has been copied")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
highlight(editor, project, elements)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(CopyTBXReferenceAction::class.java)
|
||||
private const val JETBRAINS_NAVIGATE = JetBrainsProtocolHandler.PROTOCOL
|
||||
private val IDE_TAGS = mapOf(IDEA_PREFIX to "idea",
|
||||
IDEA_CE_PREFIX to "idea",
|
||||
APPCODE_PREFIX to "appcode",
|
||||
CLION_PREFIX to "clion",
|
||||
PYCHARM_PREFIX to "pycharm",
|
||||
PYCHARM_CE_PREFIX to "pycharm",
|
||||
PYCHARM_EDU_PREFIX to "pycharm",
|
||||
PHP_PREFIX to "php-storm",
|
||||
RUBY_PREFIX to "rubymine",
|
||||
WEB_PREFIX to "web-storm",
|
||||
RIDER_PREFIX to "rd",
|
||||
GOIDE_PREFIX to "goland")
|
||||
|
||||
fun createJetbrainsLink(project: Project, elements: List<PsiElement>, editor: Editor?): String? {
|
||||
val refsParameters =
|
||||
IntArray(elements.size) { i -> i }
|
||||
.associateBy({ it }, { elementToFqn(elements[it], editor) })
|
||||
.filter { it.value != null }
|
||||
.entries
|
||||
.ifEmpty { return null }
|
||||
.joinToString("") { createRefs(isFile(elements[it.key]), it.value, parameterIndex(it.key, elements.size)) }
|
||||
|
||||
return createLink(editor, project, refsParameters)
|
||||
}
|
||||
|
||||
private fun isFile(element: PsiElement) = element is PsiFileSystemItem && getQualifiedNameFromProviders(element) == null
|
||||
|
||||
private fun parameterIndex(index: Int, size: Int) = if (size == 1) "" else "${index + 1}"
|
||||
|
||||
private fun createRefs(isFile: Boolean, reference: String?, index: String) = "&${if (isFile) PATH_KEY else FQN_KEY}${index}=$reference"
|
||||
|
||||
private fun createLink(editor: Editor?, project: Project, refsParameters: String?): String? {
|
||||
val tool = IDE_TAGS[PlatformUtils.getPlatformPrefix()]
|
||||
if (tool == null) {
|
||||
LOG.warn("Cannot find TBX tool for IDE: ${PlatformUtils.getPlatformPrefix()}")
|
||||
return null
|
||||
}
|
||||
|
||||
val selectionParameters = getSelectionParameters(editor) ?: ""
|
||||
val projectParameter = "$PROJECT_NAME_KEY=${project.name}"
|
||||
|
||||
return "$JETBRAINS_NAVIGATE$tool/$NAVIGATE_COMMAND/$REFERENCE_TARGET?$projectParameter$refsParameters$selectionParameters"
|
||||
}
|
||||
|
||||
private fun getSelectionParameters(editor: Editor?): String? {
|
||||
if (editor == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed()
|
||||
if (editor.caretModel.supportsMultipleCarets()) {
|
||||
val carets = editor.caretModel.allCarets
|
||||
return IntStream.range(0, carets.size).mapToObj { i -> getSelectionParameters(editor, carets[i], parameterIndex(i, carets.size)) }
|
||||
.filter { it != null }.collect(Collectors.joining())
|
||||
}
|
||||
else {
|
||||
return getSelectionParameters(editor, editor.caretModel.currentCaret, "")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectionParameters(editor: Editor, caret: Caret, index: String): String? =
|
||||
getSelectionRange(editor, caret)?.let { "&$SELECTION$index=$it" }
|
||||
|
||||
private fun getSelectionRange(editor: Editor, caret: Caret): String? {
|
||||
if (!caret.hasSelection()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val selectionStart = editor.visualToLogicalPosition(caret.selectionStartPosition)
|
||||
val selectionEnd = editor.visualToLogicalPosition(caret.selectionEndPosition)
|
||||
|
||||
return String.format("%d:%d-%d:%d",
|
||||
selectionStart.line + 1,
|
||||
selectionStart.column + 1,
|
||||
selectionEnd.line + 1,
|
||||
selectionEnd.column + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ide.actions;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface VirtualFileQualifiedNameProvider {
|
||||
ExtensionPointName<VirtualFileQualifiedNameProvider> EP_NAME =
|
||||
ExtensionPointName.create("com.intellij.virtualFileQualifiedNameProvider");
|
||||
|
||||
/**
|
||||
* @return {@code virtualFile} fqn (relative path for example) or null if not handled by this provider
|
||||
*/
|
||||
@Nullable
|
||||
String getQualifiedName(@NotNull Project project, @NotNull VirtualFile virtualFile);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.intellij.ide.util.gotoByName.*
|
||||
import com.intellij.navigation.NavigationItem
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.JBProtocolCommand
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
@@ -17,14 +18,13 @@ import com.intellij.openapi.project.ProjectManagerListener
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.util.messages.MessageBusConnection
|
||||
|
||||
class JBProtocolNavigateCommand : JBProtocolCommand("navigate") {
|
||||
class JBProtocolNavigateCommand : JBProtocolCommand(NAVIGATE_COMMAND) {
|
||||
override fun perform(target: String, parameters: Map<String, String>) {
|
||||
// handles URLs of the following types:
|
||||
|
||||
// jetbrains://idea/navigate/reference?project=IDEA&reference=com.intellij.openapi.project.impl.JBProtocolNavigateCommand[.perform][#perform]
|
||||
// [&selectionX=25:5-26:6]+
|
||||
|
||||
// jetbrains://idea/navigate/path?project=IDEA&path=com/intellij/openapi/project/impl/JBProtocolNavigateCommand.kt:23:1
|
||||
// jetbrains://idea/navigate/reference?project=IDEA
|
||||
// [&reference=com.intellij.openapi.project.impl.JBProtocolNavigateCommand[.perform][#perform]]+
|
||||
// [&path=com/intellij/openapi/project/impl/JBProtocolNavigateCommand.kt:23:1]+
|
||||
// [&selectionX=25:5-26:6]+
|
||||
|
||||
val projectName = parameters[PROJECT_NAME_KEY]
|
||||
@@ -32,36 +32,22 @@ class JBProtocolNavigateCommand : JBProtocolCommand("navigate") {
|
||||
return
|
||||
}
|
||||
|
||||
val model: (Project) -> ChooseByNameModel
|
||||
val pattern: String
|
||||
|
||||
when (target) {
|
||||
REFERENCE_TARGET -> {
|
||||
pattern = (parameters[FQN_KEY] ?: return)
|
||||
model = { project -> GotoSymbolModel2(project) }
|
||||
}
|
||||
FILE_TARGET -> {
|
||||
pattern = (parameters[PATH_KEY] ?: return)
|
||||
model = { it -> GotoFileModel(it) }
|
||||
}
|
||||
else -> return
|
||||
if (target != REFERENCE_TARGET) {
|
||||
LOG.warn("JB navigate action supports only reference target, got $target")
|
||||
return
|
||||
}
|
||||
|
||||
val recentProjectsActions = RecentProjectsManager.getInstance().getRecentProjectsActions(false)
|
||||
for (recentProjectAction in recentProjectsActions) {
|
||||
if (recentProjectAction is ReopenProjectAction) {
|
||||
if (recentProjectAction.projectName == projectName) {
|
||||
val project = ProjectManager.getInstance().openProjects.find { project -> project.name == projectName }
|
||||
val selections = parseSelections(parameters)
|
||||
|
||||
if (project != null) {
|
||||
navigateToFile(project, model(project), pattern, selections)
|
||||
}
|
||||
else {
|
||||
ProjectManager.getInstance().openProjects.find { project -> project.name == projectName }?.let {
|
||||
findAndNavigateToReference(it, parameters)
|
||||
} ?: run {
|
||||
RecentProjectsManagerBase.getInstanceEx().doOpenProject(recentProjectAction.projectPath, null, false)
|
||||
|
||||
val appConnection = ApplicationManager.getApplication().messageBus.connect()
|
||||
appConnection.subscribe(ProjectManager.TOPIC, NavigatableProjectListener(model, pattern, selections, appConnection))
|
||||
appConnection.subscribe(ProjectManager.TOPIC, NavigatableProjectListener(appConnection, parameters))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,21 +55,36 @@ class JBProtocolNavigateCommand : JBProtocolCommand("navigate") {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PROJECT_NAME_KEY = "project"
|
||||
private const val SELECTION = "selection"
|
||||
private val LOG = Logger.getInstance(JBProtocolNavigateCommand::class.java)
|
||||
const val NAVIGATE_COMMAND = "navigate"
|
||||
const val PROJECT_NAME_KEY = "project"
|
||||
const val REFERENCE_TARGET = "reference"
|
||||
const val PATH_KEY = "path"
|
||||
const val FQN_KEY = "fqn"
|
||||
const val SELECTION = "selection"
|
||||
|
||||
private const val REFERENCE_TARGET = "reference"
|
||||
private const val FQN_KEY = "fqn"
|
||||
private fun findAndNavigateToReference(project: Project, parameters: Map<String, String>) {
|
||||
val selections = parseSelections(parameters)
|
||||
navigateToReference(parameters, project, selections, FQN_KEY, GotoSymbolModel2(project))
|
||||
navigateToReference(parameters, project, selections, PATH_KEY, GotoFileModel(project))
|
||||
|
||||
private const val FILE_TARGET = "file"
|
||||
private const val PATH_KEY = "path"
|
||||
}
|
||||
|
||||
private fun navigateToFile(project: Project,
|
||||
gotoModel: ChooseByNameModel,
|
||||
pattern: String,
|
||||
selections: List<Pair<LogicalPosition, LogicalPosition>>) {
|
||||
val model = JBProtocolNavigateChooseByNameViewModel(project, gotoModel)
|
||||
DefaultChooseByNameItemProvider.filterElements(model, pattern, true, EmptyProgressIndicator(), null) {
|
||||
private fun navigateToReference(parameters: Map<String, String>,
|
||||
project: Project,
|
||||
selections: List<Pair<LogicalPosition, LogicalPosition>>,
|
||||
parameterKey: String,
|
||||
model: ChooseByNameModel) {
|
||||
parameters.filter { it.key.startsWith(parameterKey) }.forEach {
|
||||
navigate(model, it.value, project, selections)
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigate(model: ChooseByNameModel,
|
||||
pattern: String,
|
||||
project: Project,
|
||||
selections: List<Pair<LogicalPosition, LogicalPosition>>) {
|
||||
DefaultChooseByNameItemProvider.filterElements(MySearchModel(project, model), pattern, true, EmptyProgressIndicator(), null) {
|
||||
if (it !is NavigationItem) {
|
||||
return@filterElements true
|
||||
}
|
||||
@@ -131,19 +132,16 @@ class JBProtocolNavigateCommand : JBProtocolCommand("navigate") {
|
||||
}
|
||||
}
|
||||
|
||||
private class NavigatableProjectListener(private val model: (Project) -> ChooseByNameModel,
|
||||
private val pattern: String,
|
||||
private val selections: List<Pair<LogicalPosition, LogicalPosition>>,
|
||||
private val appConnection: MessageBusConnection) : ProjectManagerListener {
|
||||
private class NavigatableProjectListener(private val appConnection: MessageBusConnection,
|
||||
private val parameters: Map<String, String>) : ProjectManagerListener {
|
||||
override fun projectOpened(project: Project) {
|
||||
navigateToFile(project, model(project), pattern, selections)
|
||||
findAndNavigateToReference(project, parameters)
|
||||
appConnection.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
//todo duplicates
|
||||
private class JBProtocolNavigateChooseByNameViewModel(private val project: Project,
|
||||
private val model: ChooseByNameModel) : ChooseByNameViewModel {
|
||||
//todo duplicate
|
||||
private class MySearchModel(private val project: Project, private val model: ChooseByNameModel) : ChooseByNameViewModel {
|
||||
override fun canShowListForEmptyPattern() = false
|
||||
|
||||
override fun isSearchInAnyPlace(): Boolean = false
|
||||
|
||||
@@ -354,6 +354,8 @@ action.CopyPaths.text=C_opy Paths
|
||||
action.CopyPaths.description=Copy paths corresponding to selected files or directories to clipboard
|
||||
action.CopyReference.text=Cop_y Reference
|
||||
action.CopyReference.description=Copy reference to selected class, method or function, or a relative path to selected file
|
||||
action.CopyTBXReference.text=Copy _Toolbox Reference URL
|
||||
action.CopyTBXReference.description=Copy Toolbox reference URL to selected class, method or function, or a relative path to selected file
|
||||
action.CopySettingsPath.text=Copy Settings Path
|
||||
action.CopySettingsPath.mac.text=Copy Preferences Path
|
||||
action.CopySettingsPath.description=Copy relative path to selected configurable option
|
||||
|
||||
@@ -667,7 +667,7 @@
|
||||
|
||||
<extensionPoint name="qualifiedNameProvider" interface="com.intellij.ide.actions.QualifiedNameProvider"/>
|
||||
<extensionPoint name="virtualFileQualifiedNameProvider"
|
||||
interface="com.intellij.ide.actions.CopyReferenceAction$VirtualFileQualifiedNameProvider"/>
|
||||
interface="com.intellij.ide.actions.VirtualFileQualifiedNameProvider"/>
|
||||
|
||||
<extensionPoint name="completionData"
|
||||
beanClass="com.intellij.codeInsight.completion.CompletionDataEP"/>
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
<add-to-group group-id="EditorPopupMenu" anchor="after" relative-to-action="$Copy"/>
|
||||
</action>
|
||||
|
||||
<action id="CopyTBXReference" class="com.intellij.ide.actions.CopyTBXReferenceAction" >
|
||||
<add-to-group group-id="CutCopyPasteGroup" anchor="after" relative-to-action="CopyReference"/>
|
||||
</action>
|
||||
|
||||
<action id="CopyAsRichText" class="com.intellij.openapi.editor.richcopy.CopyAsRichTextAction">
|
||||
<add-to-group group-id="CutCopyPasteGroup" anchor="after" relative-to-action="CopyPaths"/>
|
||||
<add-to-group group-id="EditorPopupMenu" anchor="after" relative-to-action="$Copy"/>
|
||||
|
||||
@@ -601,6 +601,9 @@
|
||||
<action id="CopyReference">
|
||||
<keyboard-shortcut first-keystroke="control alt shift C"/>
|
||||
</action>
|
||||
<action id="CopyTBXReference">
|
||||
<keyboard-shortcut first-keystroke="control alt shift T"/>
|
||||
</action>
|
||||
<action id="EditorPasteFromX11">
|
||||
<mouse-shortcut keystroke="button2"/>
|
||||
</action>
|
||||
|
||||
Reference in New Issue
Block a user