/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java

https://bitbucket.org/nbargnesi/idea
Java | 631 lines | 522 code | 65 blank | 44 comment | 158 complexity | 29d25a21caee09eb97695fb7f649f8f9 MD5 | raw file
  1. /*
  2. * Copyright 2000-2011 JetBrains s.r.o.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.intellij.refactoring.introduce.inplace;
  17. import com.intellij.codeInsight.highlighting.HighlightManager;
  18. import com.intellij.codeInsight.template.TextResult;
  19. import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
  20. import com.intellij.codeInsight.template.impl.TemplateState;
  21. import com.intellij.openapi.application.ApplicationManager;
  22. import com.intellij.openapi.application.Result;
  23. import com.intellij.openapi.command.CommandProcessor;
  24. import com.intellij.openapi.command.WriteCommandAction;
  25. import com.intellij.openapi.command.impl.StartMarkAction;
  26. import com.intellij.openapi.editor.*;
  27. import com.intellij.openapi.editor.colors.EditorColors;
  28. import com.intellij.openapi.editor.colors.EditorColorsManager;
  29. import com.intellij.openapi.editor.event.DocumentAdapter;
  30. import com.intellij.openapi.editor.event.DocumentEvent;
  31. import com.intellij.openapi.editor.ex.EditorEx;
  32. import com.intellij.openapi.editor.markup.RangeHighlighter;
  33. import com.intellij.openapi.editor.markup.TextAttributes;
  34. import com.intellij.openapi.fileTypes.FileType;
  35. import com.intellij.openapi.project.Project;
  36. import com.intellij.openapi.ui.popup.Balloon;
  37. import com.intellij.openapi.util.Key;
  38. import com.intellij.openapi.util.Pair;
  39. import com.intellij.openapi.util.Ref;
  40. import com.intellij.openapi.util.TextRange;
  41. import com.intellij.psi.*;
  42. import com.intellij.psi.search.searches.ReferencesSearch;
  43. import com.intellij.psi.util.PsiTreeUtil;
  44. import com.intellij.refactoring.RefactoringBundle;
  45. import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
  46. import com.intellij.refactoring.util.CommonRefactoringUtil;
  47. import com.intellij.ui.DottedBorder;
  48. import com.intellij.util.ui.PositionTracker;
  49. import org.jetbrains.annotations.NotNull;
  50. import org.jetbrains.annotations.Nullable;
  51. import javax.swing.*;
  52. import javax.swing.border.EmptyBorder;
  53. import javax.swing.border.LineBorder;
  54. import java.awt.*;
  55. import java.util.*;
  56. import java.util.List;
  57. /**
  58. * User: anna
  59. * Date: 3/15/11
  60. */
  61. public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner, E extends PsiElement> extends
  62. InplaceVariableIntroducer<E> {
  63. protected V myLocalVariable;
  64. protected RangeMarker myLocalMarker;
  65. protected final String myExprText;
  66. private final String myLocalName;
  67. public static final Key<AbstractInplaceIntroducer> ACTIVE_INTRODUCE = Key.create("ACTIVE_INTRODUCE");
  68. private EditorEx myPreview;
  69. private final JComponent myPreviewComponent;
  70. private DocumentAdapter myDocumentAdapter;
  71. protected final JPanel myWholePanel;
  72. private boolean myFinished = false;
  73. public AbstractInplaceIntroducer(Project project,
  74. Editor editor,
  75. E expr,
  76. @Nullable V localVariable,
  77. E[] occurrences,
  78. String title,
  79. final FileType languageFileType) {
  80. super(null, editor, project, title, occurrences, expr);
  81. myLocalVariable = localVariable;
  82. if (localVariable != null) {
  83. final PsiElement nameIdentifier = localVariable.getNameIdentifier();
  84. if (nameIdentifier != null) {
  85. myLocalMarker = createMarker(nameIdentifier);
  86. }
  87. }
  88. else {
  89. myLocalMarker = null;
  90. }
  91. myExprText = getExpressionText(expr);
  92. myLocalName = localVariable != null ? localVariable.getName() : null;
  93. myPreview =
  94. (EditorEx)EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), project, languageFileType, true);
  95. myPreview.setOneLineMode(true);
  96. final EditorSettings settings = myPreview.getSettings();
  97. settings.setAdditionalLinesCount(0);
  98. settings.setAdditionalColumnsCount(1);
  99. settings.setRightMarginShown(false);
  100. settings.setFoldingOutlineShown(false);
  101. settings.setLineNumbersShown(false);
  102. settings.setLineMarkerAreaShown(false);
  103. settings.setIndentGuidesShown(false);
  104. settings.setVirtualSpace(false);
  105. myPreview.setHorizontalScrollbarVisible(false);
  106. myPreview.setVerticalScrollbarVisible(false);
  107. myPreview.setCaretEnabled(false);
  108. settings.setLineCursorWidth(1);
  109. final Color bg = myPreview.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR);
  110. myPreview.setBackgroundColor(bg);
  111. myPreview.setBorder(BorderFactory.createCompoundBorder(new DottedBorder(Color.gray), new LineBorder(bg, 2)));
  112. myPreviewComponent = new JPanel(new BorderLayout());
  113. myPreviewComponent.add(myPreview.getComponent(), BorderLayout.CENTER);
  114. myPreviewComponent.setBorder(new EmptyBorder(2, 2, 6, 2));
  115. myWholePanel = new JPanel(new GridBagLayout());
  116. myWholePanel.setBorder(null);
  117. showDialogAdvertisement(getActionName());
  118. }
  119. @Nullable
  120. protected String getExpressionText(E expr) {
  121. return expr != null ? expr.getText() : null;
  122. }
  123. protected final void setPreviewText(final String text) {
  124. if (myPreview == null) return; //already disposed
  125. ApplicationManager.getApplication().runWriteAction(new Runnable() {
  126. @Override
  127. public void run() {
  128. myPreview.getDocument().replaceString(0, myPreview.getDocument().getTextLength(), text);
  129. }
  130. });
  131. }
  132. protected final JComponent getPreviewComponent() {
  133. return myPreviewComponent;
  134. }
  135. protected final Editor getPreviewEditor() {
  136. return myPreview;
  137. }
  138. @Override
  139. protected StartMarkAction startRename() throws StartMarkAction.AlreadyStartedException {
  140. return StartMarkAction.start(myEditor, myProject, getCommandName());
  141. }
  142. /**
  143. * Returns ID of the action the shortcut of which is used to show the non-in-place refactoring dialog.
  144. *
  145. * @return action ID
  146. */
  147. protected abstract String getActionName();
  148. /**
  149. * Creates an initial version of the declaration for the introduced element. Note that this method is not called in a write action
  150. * and most likely needs to create one itself.
  151. *
  152. * @param replaceAll whether all occurrences are going to be replaced
  153. * @param names the suggested names for the declaration
  154. * @return the declaration
  155. */
  156. @Nullable
  157. protected abstract V createFieldToStartTemplateOn(boolean replaceAll, String[] names);
  158. /**
  159. * Returns the suggested names for the introduced element.
  160. *
  161. * @param replaceAll whether all occurrences are going to be replaced
  162. * @param variable introduced element declaration, if already created.
  163. * @return the suggested names
  164. */
  165. protected abstract String[] suggestNames(boolean replaceAll, @Nullable V variable);
  166. protected abstract void performIntroduce();
  167. protected void performPostIntroduceTasks() {}
  168. public abstract boolean isReplaceAllOccurrences();
  169. public abstract void setReplaceAllOccurrences(boolean allOccurrences);
  170. protected abstract JComponent getComponent();
  171. protected abstract void saveSettings(@NotNull V variable);
  172. protected abstract V getVariable();
  173. public abstract E restoreExpression(PsiFile containingFile, V variable, RangeMarker marker, String exprText);
  174. /**
  175. * Begins the in-place refactoring operation.
  176. *
  177. * @return true if the in-place refactoring was successfully started, false if it failed to start and a dialog should be shown instead.
  178. */
  179. public boolean startInplaceIntroduceTemplate() {
  180. final boolean replaceAllOccurrences = isReplaceAllOccurrences();
  181. final Ref<Boolean> result = new Ref<Boolean>();
  182. CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
  183. public void run() {
  184. final String[] names = suggestNames(replaceAllOccurrences, getLocalVariable());
  185. final V variable = createFieldToStartTemplateOn(replaceAllOccurrences, names);
  186. boolean started = false;
  187. if (variable != null) {
  188. int caretOffset = getCaretOffset();
  189. myEditor.getCaretModel().moveToOffset(caretOffset);
  190. myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
  191. final LinkedHashSet<String> nameSuggestions = new LinkedHashSet<String>();
  192. nameSuggestions.add(variable.getName());
  193. nameSuggestions.addAll(Arrays.asList(names));
  194. initOccurrencesMarkers();
  195. setElementToRename(variable);
  196. updateTitle(getVariable());
  197. started = AbstractInplaceIntroducer.super.performInplaceRefactoring(nameSuggestions);
  198. if (started) {
  199. myDocumentAdapter = new DocumentAdapter() {
  200. @Override
  201. public void documentChanged(DocumentEvent e) {
  202. if (myPreview == null) return;
  203. final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
  204. if (templateState != null) {
  205. final TextResult value = templateState.getVariableValue(InplaceRefactoring.PRIMARY_VARIABLE_NAME);
  206. if (value != null) {
  207. updateTitle(getVariable(), value.getText());
  208. }
  209. }
  210. }
  211. };
  212. myEditor.getDocument().addDocumentListener(myDocumentAdapter);
  213. updateTitle(getVariable());
  214. if (TemplateManagerImpl.getTemplateState(myEditor) != null) {
  215. myEditor.putUserData(ACTIVE_INTRODUCE, AbstractInplaceIntroducer.this);
  216. }
  217. }
  218. }
  219. result.set(started);
  220. if (!started) {
  221. finish(true);
  222. }
  223. }
  224. }, getCommandName(), getCommandName());
  225. return result.get();
  226. }
  227. protected int getCaretOffset() {
  228. RangeMarker r;
  229. if (myLocalMarker != null) {
  230. final PsiReference reference = myExpr != null ? myExpr.getReference() : null;
  231. if (reference != null && reference.resolve() == myLocalVariable) {
  232. r = myExprMarker;
  233. } else {
  234. r = myLocalMarker;
  235. }
  236. }
  237. else {
  238. r = myExprMarker;
  239. }
  240. return r != null ? r.getStartOffset() : 0;
  241. }
  242. protected void updateTitle(@Nullable V variable, String value) {
  243. if (variable == null) return;
  244. final String variableText = variable.getText();
  245. final PsiElement identifier = variable.getNameIdentifier();
  246. if (identifier != null) {
  247. final int startOffsetInParent = identifier.getStartOffsetInParent();
  248. setPreviewText(variableText.substring(0, startOffsetInParent) + value + variableText.substring(startOffsetInParent + identifier.getTextLength()));
  249. } else {
  250. setPreviewText(variableText.replaceFirst(variable.getName(), value));
  251. }
  252. revalidate();
  253. }
  254. protected void updateTitle(@Nullable V variable) {
  255. if (variable == null) return;
  256. setPreviewText(variable.getText());
  257. revalidate();
  258. }
  259. protected void revalidate() {
  260. myWholePanel.revalidate();
  261. if (myTarget != null) {
  262. myBalloon.revalidate(new PositionTracker.Static<Balloon>(myTarget));
  263. }
  264. }
  265. private boolean myShouldSelect = true;
  266. @Override
  267. protected boolean shouldSelectAll() {
  268. return myShouldSelect;
  269. }
  270. public void restartInplaceIntroduceTemplate() {
  271. Runnable restartTemplateRunnable = new Runnable() {
  272. public void run() {
  273. final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
  274. if (templateState != null) {
  275. myEditor.putUserData(INTRODUCE_RESTART, true);
  276. try {
  277. final TextRange range = templateState.getCurrentVariableRange();
  278. if (range != null && range.isEmpty()) {
  279. final String[] names = suggestNames(isReplaceAllOccurrences(), getLocalVariable());
  280. ApplicationManager.getApplication().runWriteAction(new Runnable() {
  281. public void run() {
  282. myEditor.getDocument().insertString(myEditor.getCaretModel().getOffset(), names[0]);
  283. }
  284. });
  285. }
  286. templateState.gotoEnd(true);
  287. try {
  288. myShouldSelect = false;
  289. startInplaceIntroduceTemplate();
  290. }
  291. finally {
  292. myShouldSelect = true;
  293. }
  294. }
  295. finally {
  296. myEditor.putUserData(INTRODUCE_RESTART, false);
  297. }
  298. }
  299. updateTitle(getVariable());
  300. }
  301. };
  302. CommandProcessor.getInstance().executeCommand(myProject, restartTemplateRunnable, getCommandName(), getCommandName());
  303. }
  304. @Override
  305. protected void restoreSelection() {
  306. if (!shouldSelectAll()) {
  307. myEditor.getSelectionModel().removeSelection();
  308. }
  309. }
  310. public String getInputName() {
  311. return myInsertedName;
  312. }
  313. @Override
  314. public void finish(boolean success) {
  315. myFinished = true;
  316. final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
  317. if (templateState != null) {
  318. myEditor.putUserData(ACTIVE_INTRODUCE, null);
  319. }
  320. if (myDocumentAdapter != null) {
  321. myEditor.getDocument().removeDocumentListener(myDocumentAdapter);
  322. }
  323. if (myBalloon == null) {
  324. releaseIfNotRestart();
  325. }
  326. super.finish(success);
  327. if (success) {
  328. PsiDocumentManager.getInstance(myProject).commitAllDocuments();
  329. final V variable = getVariable();
  330. if (variable == null) {
  331. return;
  332. }
  333. restoreState(variable);
  334. }
  335. }
  336. @Override
  337. protected void releaseResources() {
  338. super.releaseResources();
  339. if (myPreview == null) return;
  340. EditorFactory.getInstance().releaseEditor(myPreview);
  341. myPreview = null;
  342. }
  343. @Override
  344. protected void addReferenceAtCaret(Collection<PsiReference> refs) {
  345. final V variable = getLocalVariable();
  346. if (variable != null) {
  347. for (PsiReference reference : ReferencesSearch.search(variable)) {
  348. refs.add(reference);
  349. }
  350. } else {
  351. refs.clear();
  352. }
  353. }
  354. @Override
  355. protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
  356. if (isReplaceAllOccurrences()) {
  357. for (E expression : getOccurrences()) {
  358. stringUsages.add(Pair.<PsiElement, TextRange>create(expression, new TextRange(0, expression.getTextLength())));
  359. }
  360. } else if (getExpr() != null) {
  361. correctExpression();
  362. stringUsages.add(Pair.<PsiElement, TextRange>create(getExpr(), new TextRange(0, getExpr().getTextLength())));
  363. }
  364. final V localVariable = getLocalVariable();
  365. if (localVariable != null) {
  366. final PsiElement nameIdentifier = localVariable.getNameIdentifier();
  367. if (nameIdentifier != null) {
  368. int length = nameIdentifier.getTextLength();
  369. stringUsages.add(Pair.<PsiElement, TextRange>create(nameIdentifier, new TextRange(0, length)));
  370. }
  371. }
  372. }
  373. protected void correctExpression() {}
  374. @Override
  375. protected void addHighlights(@NotNull Map<TextRange, TextAttributes> ranges,
  376. @NotNull Editor editor,
  377. @NotNull Collection<RangeHighlighter> highlighters,
  378. @NotNull HighlightManager highlightManager) {
  379. final TextAttributes attributes =
  380. EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
  381. final V variable = getVariable();
  382. if (variable != null) {
  383. final String name = variable.getName();
  384. LOG.assertTrue(name != null, variable);
  385. final int variableNameLength = name.length();
  386. if (isReplaceAllOccurrences()) {
  387. for (RangeMarker marker : getOccurrenceMarkers()) {
  388. final int startOffset = marker.getStartOffset();
  389. highlightManager.addOccurrenceHighlight(editor, startOffset, startOffset + variableNameLength, attributes, 0, highlighters, null);
  390. }
  391. }
  392. else if (getExpr() != null) {
  393. final int startOffset = getExprMarker().getStartOffset();
  394. highlightManager.addOccurrenceHighlight(editor, startOffset, startOffset + variableNameLength, attributes, 0, highlighters, null);
  395. }
  396. }
  397. for (RangeHighlighter highlighter : highlighters) {
  398. highlighter.setGreedyToLeft(true);
  399. highlighter.setGreedyToRight(true);
  400. }
  401. }
  402. protected void restoreState(final V psiField) {
  403. ApplicationManager.getApplication().runWriteAction(new Runnable() {
  404. public void run() {
  405. final PsiFile containingFile = psiField.getContainingFile();
  406. final RangeMarker exprMarker = getExprMarker();
  407. if (exprMarker != null) {
  408. myExpr = restoreExpression(containingFile, psiField, exprMarker, myExprText);
  409. if (myExpr != null && myExpr.isPhysical()) {
  410. myExprMarker = createMarker(myExpr);
  411. }
  412. }
  413. if (myLocalMarker != null) {
  414. final PsiElement refVariableElement = containingFile.findElementAt(myLocalMarker.getStartOffset());
  415. if (refVariableElement != null) {
  416. final PsiElement parent = refVariableElement.getParent();
  417. if (parent instanceof PsiNamedElement) {
  418. ((PsiNamedElement)parent).setName(myLocalName);
  419. }
  420. }
  421. final V localVariable = getLocalVariable();
  422. if (localVariable != null && localVariable.isPhysical()) {
  423. myLocalVariable = localVariable;
  424. final PsiElement nameIdentifier = localVariable.getNameIdentifier();
  425. if (nameIdentifier != null) {
  426. myLocalMarker = createMarker(nameIdentifier);
  427. }
  428. }
  429. }
  430. final List<RangeMarker> occurrenceMarkers = getOccurrenceMarkers();
  431. for (int i = 0, occurrenceMarkersSize = occurrenceMarkers.size(); i < occurrenceMarkersSize; i++) {
  432. RangeMarker marker = occurrenceMarkers.get(i);
  433. if (getExprMarker() != null && marker.getStartOffset() == getExprMarker().getStartOffset() && myExpr != null) {
  434. myOccurrences[i] = myExpr;
  435. continue;
  436. }
  437. final E psiExpression =
  438. restoreExpression(containingFile, psiField, marker, getLocalVariable() != null ? myLocalName : myExprText);
  439. if (psiExpression != null) {
  440. myOccurrences[i] = psiExpression;
  441. }
  442. }
  443. myOccurrenceMarkers = null;
  444. deleteTemplateField(psiField);
  445. }
  446. });
  447. }
  448. protected void deleteTemplateField(V psiField) {
  449. if (psiField.isValid()) {
  450. psiField.delete();
  451. }
  452. }
  453. @Override
  454. protected boolean performRefactoring() {
  455. final String newName = getInputName();
  456. if (getLocalVariable() == null && myExpr == null ||
  457. newName == null ||
  458. getLocalVariable() != null && !getLocalVariable().isValid() ||
  459. myExpr != null && !myExpr.isValid()) {
  460. super.moveOffsetAfter(false);
  461. return false;
  462. }
  463. if (getLocalVariable() != null) {
  464. new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
  465. @Override
  466. protected void run(Result result) throws Throwable {
  467. getLocalVariable().setName(myLocalName);
  468. }
  469. }.execute();
  470. }
  471. if (!isIdentifier(newName, myExpr != null ? myExpr.getLanguage() : getLocalVariable().getLanguage())) return false;
  472. CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
  473. @Override
  474. public void run() {
  475. performIntroduce();
  476. }
  477. }, getCommandName(), getCommandName());
  478. V variable = getVariable();
  479. if (variable != null) {
  480. saveSettings(variable);
  481. }
  482. return false;
  483. }
  484. @Override
  485. protected void moveOffsetAfter(boolean success) {
  486. if (getLocalVariable() != null && getLocalVariable().isValid()) {
  487. myEditor.getCaretModel().moveToOffset(getLocalVariable().getTextOffset());
  488. myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
  489. }
  490. else if (getExprMarker() != null) {
  491. final RangeMarker exprMarker = getExprMarker();
  492. if (exprMarker.isValid()) {
  493. myEditor.getCaretModel().moveToOffset(exprMarker.getStartOffset());
  494. myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
  495. }
  496. }
  497. super.moveOffsetAfter(success);
  498. if (myLocalMarker != null && !isRestart()) {
  499. myLocalMarker.dispose();
  500. }
  501. if (success) {
  502. performPostIntroduceTasks();
  503. }
  504. }
  505. public V getLocalVariable() {
  506. if (myLocalVariable != null && myLocalVariable.isValid()) {
  507. return myLocalVariable;
  508. }
  509. if (myLocalMarker != null) {
  510. V variable = getVariable();
  511. PsiFile containingFile;
  512. if (variable != null) {
  513. containingFile = variable.getContainingFile();
  514. } else {
  515. containingFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
  516. }
  517. PsiNameIdentifierOwner identifierOwner = PsiTreeUtil.getParentOfType(containingFile.findElementAt(myLocalMarker.getStartOffset()),
  518. PsiNameIdentifierOwner.class, false);
  519. return identifierOwner != null && identifierOwner.getClass() == myLocalVariable.getClass() ? (V)identifierOwner : null;
  520. }
  521. return myLocalVariable;
  522. }
  523. public void stopIntroduce(Editor editor) {
  524. final TemplateState templateState = TemplateManagerImpl.getTemplateState(editor);
  525. if (templateState != null) {
  526. final Runnable runnable = new Runnable() {
  527. public void run() {
  528. templateState.gotoEnd(true);
  529. }
  530. };
  531. CommandProcessor.getInstance().executeCommand(myProject, runnable, getCommandName(), getCommandName());
  532. }
  533. }
  534. @Override
  535. protected void navigateToAlreadyStarted(Document oldDocument, int exitCode) {
  536. finish(true);
  537. super.navigateToAlreadyStarted(oldDocument, exitCode);
  538. }
  539. @Override
  540. protected void showBalloon() {
  541. if (myFinished) return;
  542. super.showBalloon();
  543. }
  544. public boolean startsOnTheSameElement(E expr, V localVariable) {
  545. if (myExprMarker != null && myExprMarker.isValid() && expr != null && myExprMarker.getStartOffset() == expr.getTextOffset()) {
  546. return true;
  547. }
  548. if (myLocalMarker != null &&
  549. myLocalMarker.isValid() &&
  550. localVariable != null &&
  551. myLocalMarker.getStartOffset() == localVariable.getTextOffset()) {
  552. return true;
  553. }
  554. return false;
  555. }
  556. public static void unableToStartWarning(Project project, Editor editor, AbstractInplaceIntroducer introducer) {
  557. String message = RefactoringBundle
  558. .getCannotRefactorMessage(introducer.getCommandName() + " is not finished yet. Unable to start a refactoring");
  559. CommonRefactoringUtil.showErrorHint(project, editor, message, null, null);
  560. }
  561. @Nullable
  562. public static AbstractInplaceIntroducer getActiveIntroducer(@Nullable Editor editor) {
  563. if (editor == null) return null;
  564. return editor.getUserData(ACTIVE_INTRODUCE);
  565. }
  566. }