don't cache invalid injected editor in lookup (IDEA-146179, EA-54321 - assert: PsiFil...
authorpeter <peter@jetbrains.com>
Thu, 29 Oct 2015 09:57:46 +0000 (10:57 +0100)
committerpeter <peter@jetbrains.com>
Thu, 29 Oct 2015 10:27:41 +0000 (11:27 +0100)
platform/lang-api/src/com/intellij/codeInsight/lookup/Lookup.java
platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionLookupArranger.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupManager.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupDocumentSavingVetoer.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java
platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java
platform/lang-impl/src/com/intellij/codeInsight/template/impl/ListTemplatesHandler.java

index daea2078fc966ff6ad99bd78d7145ea7714afe4c..baf1b0605d296a2e8f0ec11018ab070441ee858f 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.codeInsight.lookup;
 
 import com.intellij.codeInsight.completion.PrefixMatcher;
 import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import org.jetbrains.annotations.NotNull;
@@ -26,12 +27,19 @@ import org.jetbrains.annotations.Nullable;
 import java.awt.*;
 import java.util.List;
 
+/**
+ * Represents list with suggestions shown in code completion, refactorings, live templates etc.
+ */
 public interface Lookup {
   char NORMAL_SELECT_CHAR = '\n';
   char REPLACE_SELECT_CHAR = '\t';
   char COMPLETE_STATEMENT_SELECT_CHAR = '\r';
   char AUTO_INSERT_SELECT_CHAR = (char) 0;
 
+  /**
+   * @return the offset in {@link #getTopLevelEditor()} which this lookup's left side should be aligned with. Note that if the lookup doesn't fit
+   * the screen due to its dimensions, the actual position might differ from this editor offset.
+   */
   int getLookupStart();
 
   @Nullable
@@ -51,12 +59,33 @@ public interface Lookup {
   Rectangle getCurrentItemBounds();
   boolean isPositionedAboveCaret();
 
+  /**
+   * @return leaf PSI element at this lookup's start position (see {@link #getLookupStart()}) in {@link #getPsiFile()} result.
+   */
   @Nullable
   PsiElement getPsiElement();
 
-  @NotNull 
+  /**
+   * Consider using {@link #getTopLevelEditor()} if you don't need injected editor.
+   * @return editor, possibly injected, where this lookup is shown
+   */
+  @NotNull
   Editor getEditor();
 
+  /**
+   * @return the non-injected editor where this lookup is shown
+   */
+  @NotNull
+  Editor getTopLevelEditor();
+
+  @NotNull
+  Project getProject();
+
+  /**
+   * @return PSI file, possibly injected, associated with this lookup's editor
+   * @see #getEditor()
+   */
+  @Nullable
   PsiFile getPsiFile();
 
   boolean isCompletion();
index 9f039ba59324b9719ae2455ade50690a50dd0be1..ea449e8ba1be70619ca3b8c8d1c0ae8c95560720 100644 (file)
@@ -386,7 +386,7 @@ public class CompletionLookupArranger extends LookupArranger {
       }
     }
 
-    String selectedText = lookup.getEditor().getSelectionModel().getSelectedText();
+    String selectedText = lookup.getTopLevelEditor().getSelectionModel().getSelectedText();
     int exactMatchIndex = -1;
     for (int i = 0; i < items.size(); i++) {
       LookupElement item = items.get(i);
index 123042cc4580471c1d7ac63066e5da9fe897d306..e4b9e8a86255c831ec68f2fdec44323d8a6323a7 100644 (file)
@@ -42,7 +42,7 @@ public abstract class LookupManager {
     final LookupEx lookup = getInstance(project).getActiveLookup();
     if (lookup == null) return null;
 
-    return InjectedLanguageUtil.getTopLevelEditor(lookup.getEditor()) == InjectedLanguageUtil.getTopLevelEditor(editor) ? lookup : null;
+    return lookup.getTopLevelEditor() == InjectedLanguageUtil.getTopLevelEditor(editor) ? lookup : null;
   }
 
   @Nullable
index e3cf89da17cea179a7b5e56fc68c5ba8cb55bc7c..4c6ce48e3d936dfa7b3f4705d80eacaca4edf632 100644 (file)
@@ -37,7 +37,6 @@ import com.intellij.ui.speedSearch.SpeedSearchUtil;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.containers.FList;
 import com.intellij.util.ui.EmptyIcon;
-import com.intellij.util.ui.GraphicsUtil;
 import com.intellij.util.ui.JBUI;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -87,7 +86,7 @@ public class LookupCellRenderer implements ListCellRenderer {
   private int myMaxWidth = -1;
 
   public LookupCellRenderer(LookupImpl lookup) {
-    EditorColorsScheme scheme = lookup.getEditor().getColorsScheme();
+    EditorColorsScheme scheme = lookup.getTopLevelEditor().getColorsScheme();
     myNormalFont = scheme.getFont(EditorFontType.PLAIN);
     myBoldFont = scheme.getFont(EditorFontType.BOLD);
 
@@ -110,8 +109,8 @@ public class LookupCellRenderer implements ListCellRenderer {
     myPanel.add(myTypeLabel, BorderLayout.EAST);
     myTypeLabel.setBorder(new EmptyBorder(0, 0, 0, AFTER_TYPE));
 
-    myNormalMetrics = myLookup.getEditor().getComponent().getFontMetrics(myNormalFont);
-    myBoldMetrics = myLookup.getEditor().getComponent().getFontMetrics(myBoldFont);
+    myNormalMetrics = myLookup.getTopLevelEditor().getComponent().getFontMetrics(myNormalFont);
+    myBoldMetrics = myLookup.getTopLevelEditor().getComponent().getFontMetrics(myBoldFont);
   }
 
   private boolean myIsSelected = false;
@@ -328,7 +327,7 @@ public class LookupCellRenderer implements ListCellRenderer {
   private FontMetrics getRealFontMetrics(LookupElement item, boolean bold) {
     Font customFont = myLookup.getCustomFont(item, bold);
     if (customFont != null) {
-      return myLookup.getEditor().getComponent().getFontMetrics(customFont);
+      return myLookup.getTopLevelEditor().getComponent().getFontMetrics(customFont);
     }
 
     return bold ? myBoldMetrics : myNormalMetrics;
@@ -431,7 +430,7 @@ public class LookupCellRenderer implements ListCellRenderer {
     // assume a single font can display all lookup item chars
     Set<Font> fonts = ContainerUtil.newHashSet();
     for (int i = 0; i < sampleString.length(); i++) {
-      fonts.add(EditorUtil.fontForChar(sampleString.charAt(i), Font.PLAIN, myLookup.getEditor()).getFont());
+      fonts.add(EditorUtil.fontForChar(sampleString.charAt(i), Font.PLAIN, myLookup.getTopLevelEditor()).getFont());
     }
 
     eachFont: for (Font font : fonts) {
index 8f5311dd334bf76fe990732af0eb495a4447fbf6..d40ef1f5316311c5abc6e4ed43d938f3752a9ff0 100644 (file)
@@ -19,11 +19,9 @@ import com.intellij.codeInsight.lookup.LookupEx;
 import com.intellij.codeInsight.lookup.LookupManager;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.fileEditor.FileDocumentSynchronizationVetoer;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
-import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -42,8 +40,7 @@ public class LookupDocumentSavingVetoer extends FileDocumentSynchronizationVetoe
       }
       LookupEx lookup = LookupManager.getInstance(project).getActiveLookup();
       if (lookup != null) {
-        Editor editor = InjectedLanguageUtil.getTopLevelEditor(lookup.getEditor());
-        if (editor.getDocument() == document) {
+        if (lookup.getTopLevelEditor().getDocument() == document) {
           return false;
         }
       }
index 4d1908be9ffe8069890fc6512b4cdfe1b1bf1e33..bc16f354e5ebd567aa482206b132c2f2dca9eaef 100644 (file)
@@ -26,6 +26,8 @@ import com.intellij.codeInsight.lookup.*;
 import com.intellij.featureStatistics.FeatureUsageTracker;
 import com.intellij.ide.IdeEventQueue;
 import com.intellij.ide.ui.UISettings;
+import com.intellij.injected.editor.DocumentWindow;
+import com.intellij.injected.editor.EditorWindow;
 import com.intellij.lang.LangBundle;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.application.ApplicationManager;
@@ -133,7 +135,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
     AbstractPopup.suppressMacCornerFor(getComponent());
 
     myProject = project;
-    myEditor = editor;
+    myEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
     myArranger = arranger;
     myPresentableArranger = arranger;
 
@@ -152,7 +154,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
 
     myAdComponent = new Advertiser();
 
-    myOffsets = new LookupOffsets(editor);
+    myOffsets = new LookupOffsets(myEditor);
 
     final CollectionListModel<LookupElement> model = getListModel();
     addEmptyItem(model);
@@ -532,7 +534,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
   private void insertLookupString(LookupElement item, final int prefix) {
     final String lookupString = getCaseCorrectedLookupString(item);
 
-    final Editor hostEditor = InjectedLanguageUtil.getTopLevelEditor(myEditor);
+    final Editor hostEditor = getTopLevelEditor();
     hostEditor.getCaretModel().runForEachCaret(new CaretAction() {
       @Override
       public void perform(Caret caret) {
@@ -950,7 +952,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
   @Override
   @Nullable
   public PsiFile getPsiFile() {
-    return PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
+    return PsiDocumentManager.getInstance(myProject).getPsiFile(getEditor().getDocument());
   }
 
   @Override
@@ -964,6 +966,10 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
     if (file == null) return null;
 
     int offset = getLookupStart();
+    Editor editor = getEditor();
+    if (editor instanceof EditorWindow) {
+      offset = editor.logicalPositionToOffset(((EditorWindow)editor).hostToInjected(myEditor.offsetToLogicalPosition(offset)));
+    }
     if (offset > 0) return file.findElementAt(offset - 1);
 
     return file.findElementAt(0);
@@ -972,9 +978,33 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
   @Override
   @NotNull 
   public Editor getEditor() {
+    PsiFile hostFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument());
+    if (hostFile != null) {
+      int offset = myEditor.getCaretModel().getOffset();
+      // inspired by com.intellij.codeInsight.editorActions.TypedHandler.injectedEditorIfCharTypedIsSignificant()
+      for (DocumentWindow documentWindow : InjectedLanguageUtil.getCachedInjectedDocuments(hostFile)) {
+        if (documentWindow.isValid() && documentWindow.containsRange(offset, offset)) {
+          PsiFile injectedFile = PsiDocumentManager.getInstance(myProject).getPsiFile(documentWindow);
+          return InjectedLanguageUtil.getInjectedEditorForInjectedFile(myEditor, injectedFile);
+        }
+      }
+    }
+
+    return myEditor;
+  }
+
+  @Override
+  @NotNull
+  public Editor getTopLevelEditor() {
     return myEditor;
   }
 
+  @NotNull
+  @Override
+  public Project getProject() {
+    return myProject;
+  }
+
   @Override
   public boolean isPositionedAboveCaret(){
     return myUi != null && myUi.isPositionedAboveCaret();
index 3049f6d816b0b52175d3b52a81199ffbd90f8896..cd1bcc9f2a5c426aa359e7db5f1a6c24b4d0c911 100644 (file)
@@ -126,7 +126,7 @@ class LookupUi {
     mySortingLabel.setOpaque(true);
     new ChangeLookupSorting().installOn(mySortingLabel);
     updateSorting();
-    myModalityState = ModalityState.stateForComponent(lookup.getEditor().getComponent());
+    myModalityState = ModalityState.stateForComponent(lookup.getTopLevelEditor().getComponent());
 
     addListeners();
 
@@ -210,7 +210,7 @@ class LookupUi {
     myLookup.getComponent().addFocusListener(new FocusAdapter() {
       @Override
       public void focusGained(FocusEvent e) {
-        final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myLookup.getEditor().getContentComponent(), true);
+        final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myLookup.getTopLevelEditor().getContentComponent(), true);
         IdeFocusManager.getInstance(myProject).typeAheadUntil(done);
         new Alarm(myLookup).addRequest(new Runnable() {
           @Override
@@ -253,7 +253,7 @@ class LookupUi {
   }
 
   void refreshUi(boolean selectionVisible, boolean itemsChanged, boolean reused, boolean onExplicitAction) {
-    Editor editor = myLookup.getEditor();
+    Editor editor = myLookup.getTopLevelEditor();
     if (editor.getComponent().getRootPane() == null || editor instanceof EditorWindow && !((EditorWindow)editor).isValid()) {
       return;
     }
@@ -286,7 +286,7 @@ class LookupUi {
     final JComponent lookupComponent = myLookup.getComponent();
     Dimension dim = lookupComponent.getPreferredSize();
     int lookupStart = myLookup.getLookupStart();
-    Editor editor = myLookup.getEditor();
+    Editor editor = myLookup.getTopLevelEditor();
     if (lookupStart < 0 || lookupStart > editor.getDocument().getTextLength()) {
       LOG.error(lookupStart + "; offset=" + editor.getCaretModel().getOffset() + "; element=" +
                 myLookup.getPsiElement());
index 2efdb422391b0602ca028ac38eca95354420dfc4..38b2c74eb8caaa128fa6791523e1383c83113f21 100644 (file)
@@ -204,17 +204,13 @@ public class ListTemplatesHandler implements CodeInsightActionHandler {
   }
 
   private static void showLookup(LookupImpl lookup, @Nullable Map<TemplateImpl, String> template2Argument) {
-    Editor editor = lookup.getEditor();
-    Project project = editor.getProject();
-    lookup.addLookupListener(new MyLookupAdapter(project, editor, template2Argument));
+    lookup.addLookupListener(new MyLookupAdapter(template2Argument));
     lookup.refreshUi(false, true);
     lookup.showLookup();
   }
 
   private static void showLookup(LookupImpl lookup, @NotNull PsiFile file) {
-    Editor editor = lookup.getEditor();
-    Project project = editor.getProject();
-    lookup.addLookupListener(new MyLookupAdapter(project, editor, file));
+    lookup.addLookupListener(new MyLookupAdapter(file));
     lookup.refreshUi(false, true);
     lookup.showLookup();
   }
@@ -237,21 +233,15 @@ public class ListTemplatesHandler implements CodeInsightActionHandler {
   }
 
   private static class MyLookupAdapter extends LookupAdapter {
-    private final Project myProject;
-    private final Editor myEditor;
     private final Map<TemplateImpl, String> myTemplate2Argument;
     private final PsiFile myFile;
 
-    public MyLookupAdapter(Project project, Editor editor, @Nullable Map<TemplateImpl, String> template2Argument) {
-      myProject = project;
-      myEditor = editor;
+    public MyLookupAdapter(@Nullable Map<TemplateImpl, String> template2Argument) {
       myTemplate2Argument = template2Argument;
       myFile = null;
     }
 
-    public MyLookupAdapter(Project project, Editor editor, @Nullable PsiFile file) {
-      myProject = project;
-      myEditor = editor;
+    public MyLookupAdapter(@Nullable PsiFile file) {
       myTemplate2Argument = null;
       myFile = file;
     }
@@ -260,22 +250,24 @@ public class ListTemplatesHandler implements CodeInsightActionHandler {
     public void itemSelected(final LookupEvent event) {
       FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.liveTemplates");
       final LookupElement item = event.getItem();
+      final Lookup lookup = event.getLookup();
+      final Project project = lookup.getProject();
       if (item instanceof LiveTemplateLookupElementImpl) {
         final TemplateImpl template = ((LiveTemplateLookupElementImpl)item).getTemplate();
         final String argument = myTemplate2Argument != null ? myTemplate2Argument.get(template) : null;
-        new WriteCommandAction(myProject) {
+        new WriteCommandAction(project) {
           @Override
           protected void run(@NotNull Result result) throws Throwable {
-            ((TemplateManagerImpl)TemplateManager.getInstance(myProject)).startTemplateWithPrefix(myEditor, template, null, argument);
+            ((TemplateManagerImpl)TemplateManager.getInstance(project)).startTemplateWithPrefix(lookup.getEditor(), template, null, argument);
           }
         }.execute();
       }
       else if (item instanceof CustomLiveTemplateLookupElement) {
         if (myFile != null) {
-          new WriteCommandAction(myProject) {
+          new WriteCommandAction(project) {
             @Override
             protected void run(@NotNull Result result) throws Throwable {
-              ((CustomLiveTemplateLookupElement)item).expandTemplate(myEditor, myFile);
+              ((CustomLiveTemplateLookupElement)item).expandTemplate(lookup.getEditor(), myFile);
             }
           }.execute();
         }