@Override public void invoke( @NotNull final Project project, @NotNull Editor editor, @NotNull PsiFile file) { PsiDocumentManager.getInstance(project).commitAllDocuments(); DumbService.getInstance(project).setAlternativeResolveEnabled(true); try { int offset = editor.getCaretModel().getOffset(); PsiElement[] elements = underModalProgress( project, "Resolving Reference...", () -> findAllTargetElements(project, editor, offset)); FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.declaration"); if (elements.length != 1) { if (elements.length == 0 && suggestCandidates(TargetElementUtil.findReference(editor, offset)).isEmpty()) { PsiElement element = findElementToShowUsagesOf(editor, editor.getCaretModel().getOffset()); if (startFindUsages(editor, element)) { return; } // disable 'no declaration found' notification for keywords final PsiElement elementAtCaret = file.findElementAt(offset); if (elementAtCaret != null) { final NamesValidator namesValidator = LanguageNamesValidation.INSTANCE.forLanguage(elementAtCaret.getLanguage()); if (namesValidator != null && namesValidator.isKeyword(elementAtCaret.getText(), project)) { return; } } } chooseAmbiguousTarget(editor, offset, elements, file); return; } PsiElement element = elements[0]; if (element == findElementToShowUsagesOf(editor, editor.getCaretModel().getOffset()) && startFindUsages(editor, element)) { return; } PsiElement navElement = element.getNavigationElement(); navElement = TargetElementUtil.getInstance().getGotoDeclarationTarget(element, navElement); if (navElement != null) { gotoTargetElement(navElement, editor, file); } } catch (IndexNotReadyException e) { DumbService.getInstance(project) .showDumbModeNotification("Navigation is not available here during index update"); } finally { DumbService.getInstance(project).setAlternativeResolveEnabled(false); } }
// returns true if processor is run or is going to be run after showing popup public static boolean chooseAmbiguousTarget( @NotNull Editor editor, int offset, @NotNull PsiElementProcessor<PsiElement> processor, @NotNull String titlePattern, @Nullable PsiElement[] elements) { if (TargetElementUtil.inVirtualSpace(editor, offset)) { return false; } final PsiReference reference = TargetElementUtil.findReference(editor, offset); if (elements == null || elements.length == 0) { elements = reference == null ? PsiElement.EMPTY_ARRAY : PsiUtilCore.toPsiElementArray( underModalProgress( reference.getElement().getProject(), "Resolving Reference...", () -> suggestCandidates(reference))); } if (elements.length == 1) { PsiElement element = elements[0]; LOG.assertTrue(element != null); processor.execute(element); return true; } if (elements.length > 1) { String title; if (reference == null) { title = titlePattern; } else { final TextRange range = reference.getRangeInElement(); final String elementText = reference.getElement().getText(); LOG.assertTrue( range.getStartOffset() >= 0 && range.getEndOffset() <= elementText.length(), Arrays.toString(elements) + ";" + reference); final String refText = range.substring(elementText); title = MessageFormat.format(titlePattern, refText); } NavigationUtil.getPsiElementPopup( elements, new DefaultPsiElementCellRenderer(), title, processor) .showInBestPositionFor(editor); return true; } return false; }
@Nullable public static PsiElement[] findTargetElementsNoVS( Project project, Editor editor, int offset, boolean lookupAccepted) { Document document = editor.getDocument(); PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document); if (file == null) return null; PsiElement elementAt = file.findElementAt(TargetElementUtil.adjustOffset(file, document, offset)); for (GotoDeclarationHandler handler : Extensions.getExtensions(GotoDeclarationHandler.EP_NAME)) { try { PsiElement[] result = handler.getGotoDeclarationTargets(elementAt, offset, editor); if (result != null && result.length > 0) { for (PsiElement element : result) { if (element == null) { LOG.error("Null target element is returned by " + handler.getClass().getName()); return null; } } return result; } } catch (AbstractMethodError e) { LOG.error(new ExtensionException(handler.getClass())); } } int flags = TargetElementUtil.getInstance().getAllAccepted() & ~TargetElementUtil.ELEMENT_NAME_ACCEPTED; if (!lookupAccepted) { flags &= ~TargetElementUtil.LOOKUP_ITEM_ACCEPTED; } PsiElement element = TargetElementUtil.getInstance().findTargetElement(editor, flags, offset); if (element != null) { return new PsiElement[] {element}; } // if no references found in injected fragment, try outer document if (editor instanceof EditorWindow) { EditorWindow window = (EditorWindow) editor; return findTargetElementsNoVS( project, window.getDelegate(), window.getDocument().injectedToHost(offset), lookupAccepted); } return null; }
public static void doTest(@NotNull final CodeInsightTestFixture fixture) { final List<CaretPositionInfo> caretPositions = DartTestUtils.extractPositionMarkers( fixture.getProject(), fixture.getEditor().getDocument()); fixture.doHighlighting(); for (CaretPositionInfo caretPositionInfo : caretPositions) { final int line = fixture.getEditor().getDocument().getLineNumber(caretPositionInfo.caretOffset); final int column = caretPositionInfo.caretOffset - fixture.getEditor().getDocument().getLineStartOffset(line); final String fileNameAndPosition = fixture.getFile().getName() + ":" + (line + 1) + ":" + (column + 1); final PsiReference reference = TargetElementUtil.findReference(fixture.getEditor(), caretPositionInfo.caretOffset); assertNotNull("No reference in " + fileNameAndPosition, reference); final PsiElement resolve = reference.resolve(); final String actualElementPosition = getPresentableElementPosition(fixture, resolve); assertEquals( "Incorrect resolution for reference in " + fileNameAndPosition, caretPositionInfo.expected, actualElementPosition); } }
public void invoke( @NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); final int offset = TargetElementUtil.adjustOffset( file, editor.getDocument(), editor.getCaretModel().getOffset()); final PsiElement element = file.findElementAt(offset); PsiTypeElement typeElement = PsiTreeUtil.getParentOfType(element, PsiTypeElement.class); while (typeElement != null) { final PsiElement parent = typeElement.getParent(); PsiElement[] toMigrate = null; if (parent instanceof PsiVariable) { toMigrate = extractReferencedVariables(typeElement); } else if ((parent instanceof PsiMember && !(parent instanceof PsiClass)) || isClassArgument(parent)) { toMigrate = new PsiElement[] {parent}; } if (toMigrate != null && toMigrate.length > 0) { invoke(project, toMigrate, null, null, editor); return; } typeElement = PsiTreeUtil.getParentOfType(parent, PsiTypeElement.class, false); } CommonRefactoringUtil.showErrorHint( project, editor, "The caret should be positioned on type of field, variable, method or method parameter to be refactored", REFACTORING_NAME, "refactoring.migrateType"); }
@NotNull private static Collection<PsiElement> suggestCandidates(@Nullable PsiReference reference) { if (reference == null) { return Collections.emptyList(); } return TargetElementUtil.getInstance().getTargetCandidates(reference); }
private static boolean invoke( final PsiMethod method, final Project project, @Nullable final Editor editor, ChangeSignatureHandler initSubstisutor) { PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod(method, RefactoringBundle.message("to.refactor")); if (newMethod == null) return false; if (!newMethod.equals(method)) { ChangeSignatureUtil.invokeChangeSignatureOn(newMethod, project); return true; } if (!CommonRefactoringUtil.checkReadOnlyStatus(project, method)) return false; final PsiClass containingClass = method.getContainingClass(); final PsiReferenceExpression refExpr = editor != null ? TargetElementUtil.findReferenceExpression(editor) : null; final boolean allowDelegation = containingClass != null && !containingClass.isInterface(); final DialogWrapper dialog = new GosuChangeSignatureDialog( project, new GosuMethodDescriptor((IGosuMethod) method, initSubstisutor), allowDelegation, refExpr, initSubstisutor); return dialog.showAndGet(); }
private boolean performRefactoring(@Nullable String expectedError) { try { final PsiElement element = TargetElementUtil.findTargetElement( myFixture.getEditor(), TargetElementUtil.getInstance().getReferenceSearchFlags()); final PyInlineLocalHandler handler = PyInlineLocalHandler.getInstance(); handler.inlineElement(myFixture.getProject(), myFixture.getEditor(), element); if (expectedError != null) fail("expected error: '" + expectedError + "', got none"); } catch (Exception e) { if (!Comparing.equal(e.getMessage(), expectedError)) { e.printStackTrace(); } assertEquals(expectedError, e.getMessage()); return false; } return true; }
@Override public void doApplyInformationToEditor() { final boolean virtSpace = TargetElementUtil.inVirtualSpace(myEditor, myEditor.getCaretModel().getOffset()); final List<HighlightInfo> infos = virtSpace ? Collections.<HighlightInfo>emptyList() : getHighlights(); UpdateHighlightersUtil.setHighlightersToEditor( myProject, myDocument, 0, myFile.getTextLength(), infos, getColorsScheme(), getId()); }
@NotNull @VisibleForTesting public static PsiElement[] findAllTargetElements(Project project, Editor editor, int offset) { if (TargetElementUtil.inVirtualSpace(editor, offset)) { return PsiElement.EMPTY_ARRAY; } final PsiElement[] targets = findTargetElementsNoVS(project, editor, offset, true); return targets != null ? targets : PsiElement.EMPTY_ARRAY; }
public static PsiNameIdentifierOwner findElementToShowUsagesOf( @NotNull Editor editor, int offset) { PsiElement elementAt = TargetElementUtil.getInstance() .findTargetElement(editor, TargetElementUtil.ELEMENT_NAME_ACCEPTED, offset); if (elementAt instanceof PsiNameIdentifierOwner) { LOG.assertTrue(elementAt.isValid(), elementAt); return (PsiNameIdentifierOwner) elementAt; } return null; }
public void testOnClickNavigation() throws Throwable { copyOnClickClasses(); final VirtualFile file = copyFileToProject(getTestName(true) + ".xml"); myFixture.configureFromExistingVirtualFile(file); final PsiReference reference = TargetElementUtil.findReference(myFixture.getEditor(), myFixture.getCaretOffset()); assertNotNull(reference); assertInstanceOf(reference, PsiPolyVariantReference.class); final ResolveResult[] results = ((PsiPolyVariantReference) reference).multiResolve(false); assertEquals(2, results.length); for (ResolveResult result : results) { assertInstanceOf(result.getElement(), PsiMethod.class); } }
private static void invoke( final PsiMethod method, final Project project, @Nullable final Editor editor) { if (!CommonRefactoringUtil.checkReadOnlyStatus(project, method)) return; PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod(method, RefactoringBundle.message("to.refactor")); if (newMethod == null) return; if (!newMethod.equals(method)) { invoke(newMethod, project, editor); return; } if (!CommonRefactoringUtil.checkReadOnlyStatus(project, method)) return; final PsiClass containingClass = method.getContainingClass(); final PsiReferenceExpression refExpr = editor != null ? TargetElementUtil.findReferenceExpression(editor) : null; final DialogWrapper dialog = new JavaChangeSignatureDialog( project, method, containingClass != null && !containingClass.isInterface(), refExpr); dialog.show(); }
@Override public void doCollectInformation(@NotNull final ProgressIndicator progress) { @SuppressWarnings("unchecked") HighlightUsagesHandlerBase<PsiElement> handler = HighlightUsagesHandler.createCustomHandler(myEditor, myFile); if (handler != null) { List<PsiElement> targets = handler.getTargets(); handler.computeUsages(targets); final List<TextRange> readUsages = handler.getReadUsages(); for (TextRange readUsage : readUsages) { LOG.assertTrue(readUsage != null, "null text range from " + handler); } myReadAccessRanges.addAll(readUsages); final List<TextRange> writeUsages = handler.getWriteUsages(); for (TextRange writeUsage : writeUsages) { LOG.assertTrue(writeUsage != null, "null text range from " + handler); } myWriteAccessRanges.addAll(writeUsages); if (!handler.highlightReferences()) return; } int flags = TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED; PsiElement myTarget; try { myTarget = TargetElementUtil.getInstance().findTargetElement(myEditor, flags, myCaretOffset); } catch (IndexNotReadyException e) { return; } if (myTarget == null) { if (!PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument())) { // when document is committed, try to check injected stuff - it's fast Editor injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit( myEditor, myFile, myCaretOffset); myTarget = TargetElementUtil.getInstance() .findTargetElement( injectedEditor, flags, injectedEditor.getCaretModel().getOffset()); } } if (myTarget != null) { highlightTargetUsages(myTarget); } else { PsiReference ref = TargetElementUtil.findReference(myEditor); if (ref instanceof PsiPolyVariantReference) { if (!ref.getElement().isValid()) { throw new PsiInvalidElementAccessException( ref.getElement(), "Invalid element in " + ref + " of " + ref.getClass() + "; editor=" + myEditor); } ResolveResult[] results = ((PsiPolyVariantReference) ref).multiResolve(false); if (results.length > 0) { for (ResolveResult result : results) { PsiElement target = result.getElement(); if (target != null) { if (!target.isValid()) { throw new PsiInvalidElementAccessException( target, "Invalid element returned from " + ref + " of " + ref.getClass() + "; editor=" + myEditor); } highlightTargetUsages(target); } } } } } }
public void inlineElement( final Project project, final Editor editor, final PsiElement psiElement) { final PsiParameter psiParameter = (PsiParameter) psiElement; final PsiParameterList parameterList = (PsiParameterList) psiParameter.getParent(); if (!(parameterList.getParent() instanceof PsiMethod)) { return; } final int index = parameterList.getParameterIndex(psiParameter); final PsiMethod method = (PsiMethod) parameterList.getParent(); String errorMessage = getCannotInlineMessage(psiParameter, method); if (errorMessage != null) { CommonRefactoringUtil.showErrorHint( project, editor, errorMessage, RefactoringBundle.message("inline.parameter.refactoring"), null); return; } final Ref<PsiExpression> refInitializer = new Ref<PsiExpression>(); final Ref<PsiExpression> refConstantInitializer = new Ref<PsiExpression>(); final Ref<PsiCallExpression> refMethodCall = new Ref<PsiCallExpression>(); final List<PsiReference> occurrences = Collections.synchronizedList(new ArrayList<PsiReference>()); final Collection<PsiFile> containingFiles = Collections.synchronizedSet(new HashSet<PsiFile>()); containingFiles.add(psiParameter.getContainingFile()); boolean result = ReferencesSearch.search(method) .forEach( new Processor<PsiReference>() { public boolean process(final PsiReference psiReference) { PsiElement element = psiReference.getElement(); final PsiElement parent = element.getParent(); if (parent instanceof PsiCallExpression) { final PsiCallExpression methodCall = (PsiCallExpression) parent; occurrences.add(psiReference); containingFiles.add(element.getContainingFile()); final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions(); if (expressions.length <= index) return false; PsiExpression argument = expressions[index]; if (!refInitializer.isNull()) { return argument != null && PsiEquivalenceUtil.areElementsEquivalent( refInitializer.get(), argument) && PsiEquivalenceUtil.areElementsEquivalent( refMethodCall.get(), methodCall); } if (InlineToAnonymousConstructorProcessor.isConstant(argument) || getReferencedFinalField(argument) != null) { if (refConstantInitializer.isNull()) { refConstantInitializer.set(argument); } else if (!isSameConstant(argument, refConstantInitializer.get())) { return false; } } else if (!isRecursiveReferencedParameter(argument, psiParameter)) { if (!refConstantInitializer.isNull()) return false; refInitializer.set(argument); refMethodCall.set(methodCall); } } return true; } }); final PsiReference reference = TargetElementUtil.findReference(editor); final PsiReferenceExpression refExpr = reference instanceof PsiReferenceExpression ? ((PsiReferenceExpression) reference) : null; final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(refExpr, PsiCodeBlock.class); if (codeBlock != null) { final PsiElement[] defs = DefUseUtil.getDefs(codeBlock, psiParameter, refExpr); if (defs.length == 1) { final PsiElement def = defs[0]; if (def instanceof PsiReferenceExpression && PsiUtil.isOnAssignmentLeftHand((PsiExpression) def)) { final PsiExpression rExpr = ((PsiAssignmentExpression) def.getParent()).getRExpression(); if (rExpr != null) { PsiExpression toInline = InlineLocalHandler.getDefToInline(psiParameter, refExpr, codeBlock); if (toInline != null) { final PsiElement[] refs = DefUseUtil.getRefs(codeBlock, psiParameter, toInline); if (InlineLocalHandler.checkRefsInAugmentedAssignmentOrUnaryModified(refs, def) == null) { new WriteCommandAction(project) { @Override protected void run(Result result) throws Throwable { for (final PsiElement ref : refs) { InlineUtil.inlineVariable( psiParameter, rExpr, (PsiJavaCodeReferenceElement) ref); } def.getParent().delete(); } }.execute(); return; } } } } } } if (occurrences.isEmpty()) { CommonRefactoringUtil.showErrorHint( project, editor, "Method has no usages", RefactoringBundle.message("inline.parameter.refactoring"), null); return; } if (!result) { CommonRefactoringUtil.showErrorHint( project, editor, "Cannot find constant initializer for parameter", RefactoringBundle.message("inline.parameter.refactoring"), null); return; } if (!refInitializer.isNull()) { if (ApplicationManager.getApplication().isUnitTestMode()) { final InlineParameterExpressionProcessor processor = new InlineParameterExpressionProcessor( refMethodCall.get(), method, psiParameter, refInitializer.get(), method .getProject() .getUserData(InlineParameterExpressionProcessor.CREATE_LOCAL_FOR_TESTS)); processor.run(); } else { final boolean createLocal = ReferencesSearch.search(psiParameter).findAll().size() > 1; InlineParameterDialog dlg = new InlineParameterDialog( refMethodCall.get(), method, psiParameter, refInitializer.get(), createLocal); dlg.show(); } return; } if (refConstantInitializer.isNull()) { CommonRefactoringUtil.showErrorHint( project, editor, "Cannot find constant initializer for parameter", RefactoringBundle.message("inline.parameter.refactoring"), null); return; } final Ref<Boolean> isNotConstantAccessible = new Ref<Boolean>(); final PsiExpression constantExpression = refConstantInitializer.get(); constantExpression.accept( new JavaRecursiveElementVisitor() { @Override public void visitReferenceExpression(PsiReferenceExpression expression) { super.visitReferenceExpression(expression); final PsiElement resolved = expression.resolve(); if (resolved instanceof PsiMember && !PsiUtil.isAccessible((PsiMember) resolved, method, null)) { isNotConstantAccessible.set(Boolean.TRUE); } } }); if (!isNotConstantAccessible.isNull() && isNotConstantAccessible.get()) { CommonRefactoringUtil.showErrorHint( project, editor, "Constant initializer is not accessible in method body", RefactoringBundle.message("inline.parameter.refactoring"), null); return; } for (PsiReference psiReference : ReferencesSearch.search(psiParameter)) { final PsiElement element = psiReference.getElement(); if (element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression) element)) { CommonRefactoringUtil.showErrorHint( project, editor, "Inline parameter which has write usages is not supported", RefactoringBundle.message("inline.parameter.refactoring"), null); return; } } if (!ApplicationManager.getApplication().isUnitTestMode()) { String occurencesString = RefactoringBundle.message("occurrences.string", occurrences.size()); String question = RefactoringBundle.message( "inline.parameter.confirmation", psiParameter.getName(), constantExpression.getText()) + " " + occurencesString; RefactoringMessageDialog dialog = new RefactoringMessageDialog( REFACTORING_NAME, question, HelpID.INLINE_VARIABLE, "OptionPane.questionIcon", true, project); if (!dialog.showAndGet()) { return; } } final RefactoringEventData data = new RefactoringEventData(); data.addElement(psiElement.copy()); CommandProcessor.getInstance() .executeCommand( project, new Runnable() { @Override public void run() { project .getMessageBus() .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC) .refactoringStarted(REFACTORING_ID, data); SameParameterValueInspection.InlineParameterValueFix.inlineSameParameterValue( method, psiParameter, constantExpression); project .getMessageBus() .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC) .refactoringDone(REFACTORING_ID, null); } }, REFACTORING_NAME, null); }