1c81bd91b3f6ad89d9482b135bc15dd6db9c49f3
[idea/community.git] / platform / platform-tests / testSrc / com / intellij / openapi / fileEditor / FileEditorManagerTest.java
1 // Copyright 2000-2020 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.
2 package com.intellij.openapi.fileEditor;
3
4 import com.intellij.ide.ui.UISettings;
5 import com.intellij.ide.ui.UISettingsState;
6 import com.intellij.mock.Mock;
7 import com.intellij.openapi.editor.*;
8 import com.intellij.openapi.fileEditor.impl.EditorWindow;
9 import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
10 import com.intellij.openapi.project.DumbAware;
11 import com.intellij.openapi.project.DumbServiceImpl;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.vfs.VirtualFile;
14 import com.intellij.pom.Navigatable;
15 import com.intellij.testFramework.EditorTestUtil;
16 import com.intellij.testFramework.FileEditorManagerTestCase;
17 import com.intellij.testFramework.PlatformTestUtil;
18 import com.intellij.util.containers.ContainerUtil;
19 import com.intellij.util.ui.UIUtil;
20 import org.jetbrains.annotations.NotNull;
21
22 import javax.swing.*;
23 import java.util.Arrays;
24 import java.util.List;
25
26 public class FileEditorManagerTest extends FileEditorManagerTestCase {
27   public void testTabOrder() throws Exception {
28
29     openFiles(STRING);
30     assertOpenFiles("1.txt", "foo.xml", "2.txt", "3.txt");
31   }
32
33   @Override
34   protected void tearDown() throws Exception {
35     try {
36       UISettingsState template = new UISettingsState();
37       UISettingsState uiSettings = UISettings.getInstance().getState();
38       uiSettings.setEditorTabLimit(template.getEditorTabLimit());
39       uiSettings.setReuseNotModifiedTabs(template.getReuseNotModifiedTabs());
40       uiSettings.setEditorTabPlacement(template.getEditorTabPlacement());
41     }
42     catch (Throwable e) {
43       addSuppressedException(e);
44     }
45     finally {
46       super.tearDown();
47     }
48   }
49
50   public void testTabLimit() throws Exception {
51     UISettings.getInstance().getState().setEditorTabLimit(2);
52     openFiles(STRING);
53     // note that foo.xml is pinned
54     assertOpenFiles("foo.xml", "3.txt");
55   }
56
57   public void testSingleTabLimit() throws Exception {
58     UISettings.getInstance().getState().setEditorTabLimit(1);
59     openFiles(STRING.replace("pinned=\"true\"", "pinned=\"false\""));
60     assertOpenFiles("3.txt");
61
62     myManager.closeAllFiles();
63
64     openFiles(STRING);
65     // note that foo.xml is pinned
66     assertOpenFiles("foo.xml");
67     myManager.openFile(getFile("/src/3.txt"), true);
68     assertOpenFiles("3.txt", "foo.xml");//limit is still 1 but pinned prevent closing tab and actual tab number may exceed the limit
69
70     myManager.closeAllFiles();
71
72     myManager.openFile(getFile("/src/3.txt"), true);
73     myManager.openFile(getFile("/src/foo.xml"), true);
74     assertOpenFiles("foo.xml");
75     callTrimToSize();
76     assertOpenFiles("foo.xml");
77   }
78
79   public void testReuseNotModifiedTabs() {
80     UISettingsState uiSettings = UISettings.getInstance().getState();
81     uiSettings.setEditorTabLimit(2);
82     uiSettings.setReuseNotModifiedTabs(false);
83
84     myManager.openFile(getFile("/src/3.txt"), true);
85     myManager.openFile(getFile("/src/foo.xml"), true);
86     assertOpenFiles("3.txt", "foo.xml");
87     uiSettings.setEditorTabLimit(1);
88     callTrimToSize();
89     assertOpenFiles("foo.xml");
90     uiSettings.setEditorTabLimit(2);
91
92     myManager.closeAllFiles();
93
94     uiSettings.setReuseNotModifiedTabs(true);
95     myManager.openFile(getFile("/src/3.txt"), true);
96     assertOpenFiles("3.txt");
97     myManager.openFile(getFile("/src/foo.xml"), true);
98     assertOpenFiles("foo.xml");
99   }
100
101   private void callTrimToSize() {
102     for (EditorsSplitters each : myManager.getAllSplitters()) {
103       each.trimToSize();
104     }
105   }
106
107   public void testOpenRecentEditorTab() throws Exception {
108     FileEditorProvider.EP_FILE_EDITOR_PROVIDER
109       .getPoint(null).registerExtension(new MyFileEditorProvider(), myFixture.getTestRootDisposable());
110
111     openFiles("  <component name=\"FileEditorManager\">\n" +
112               "    <leaf>\n" +
113               "      <file pinned=\"false\" current=\"true\" current-in-tab=\"true\">\n" +
114               "        <entry selected=\"true\" file=\"file://$PROJECT_DIR$/src/1.txt\">\n" +
115               "          <provider editor-type-id=\"mock\" selected=\"true\">\n" +
116               "            <state />\n" +
117               "          </provider>\n" +
118               "          <provider editor-type-id=\"text-editor\">\n" +
119               "            <state/>\n" +
120               "          </provider>\n" +
121               "        </entry>\n" +
122               "      </file>\n" +
123               "    </leaf>\n" +
124               "  </component>\n");
125     FileEditor[] selectedEditors = myManager.getSelectedEditors();
126     assertEquals(1, selectedEditors.length);
127     assertEquals("mockEditor", selectedEditors[0].getName());
128   }
129
130   public void testTrackSelectedEditor() {
131     FileEditorProvider.EP_FILE_EDITOR_PROVIDER
132       .getPoint(null).registerExtension(new MyFileEditorProvider(), myFixture.getTestRootDisposable());
133     VirtualFile file = getFile("/src/1.txt");
134     assertNotNull(file);
135     FileEditor[] editors = myManager.openFile(file, true);
136     assertEquals(2, editors.length);
137     assertEquals("Text", myManager.getSelectedEditor(file).getName());
138     myManager.setSelectedEditor(file, "mock");
139     assertEquals("mockEditor", myManager.getSelectedEditor(file).getName());
140
141     VirtualFile file1 = getFile("/src/2.txt");
142     myManager.openFile(file1, true);
143     assertEquals("mockEditor", myManager.getSelectedEditor(file).getName());
144   }
145
146   public void testWindowClosingRetainsOtherWindows() {
147     VirtualFile file = getFile("/src/1.txt");
148     assertNotNull(file);
149     myManager.openFile(file, false);
150     EditorWindow primaryWindow = myManager.getCurrentWindow();
151     assertNotNull(primaryWindow);
152     myManager.createSplitter(SwingConstants.VERTICAL, primaryWindow);
153     EditorWindow secondaryWindow = myManager.getNextWindow(primaryWindow);
154     assertNotNull(secondaryWindow);
155     myManager.createSplitter(SwingConstants.VERTICAL, secondaryWindow);
156     myManager.closeFile(file, primaryWindow);
157     assertEquals(2, myManager.getWindows().length);
158   }
159
160   public void testOpenFileInTablessSplitter() {
161     VirtualFile file1 = getFile("/src/1.txt");
162     assertNotNull(file1);
163     file1.putUserData(EditorWindow.INITIAL_INDEX_KEY, null);
164     myManager.openFile(file1, false);
165     VirtualFile file2 = getFile("/src/2.txt");
166     file2.putUserData(EditorWindow.INITIAL_INDEX_KEY, null);
167     assertNotNull(file2);
168     myManager.openFile(file2, true);
169     EditorWindow primaryWindow = myManager.getCurrentWindow();//1.txt and selected 2.txt
170     assertNotNull(primaryWindow);
171     myManager.createSplitter(SwingConstants.VERTICAL, primaryWindow);
172     EditorWindow secondaryWindow = myManager.getNextWindow(primaryWindow);//2.txt only, selected and focused
173     assertNotNull(secondaryWindow);
174     UISettings.getInstance().setEditorTabPlacement(UISettings.TABS_NONE);
175     myManager.openFileWithProviders(file1, true, true);//Here we have to ignore 'searchForSplitter'
176     assertEquals(2, primaryWindow.getTabCount());
177     assertEquals(2, secondaryWindow.getTabCount());
178     assertOrderedEquals(primaryWindow.getFiles(), file1, file2);
179     assertOrderedEquals(secondaryWindow.getFiles(), file2, file1);
180   }
181
182   public void testStoringCaretStateForFileWithFoldingsWithNoTabs() {
183     UISettings.getInstance().setEditorTabPlacement(UISettings.TABS_NONE);
184     VirtualFile file = getFile("/src/Test.java");
185     assertNotNull(file);
186     FileEditor[] editors = myManager.openFile(file, false);
187     assertEquals(1, editors.length);
188     assertTrue(editors[0] instanceof TextEditor);
189     Editor editor = ((TextEditor)editors[0]).getEditor();
190     EditorTestUtil.waitForLoading(editor);
191     final FoldingModel foldingModel = editor.getFoldingModel();
192     assertEquals(2, foldingModel.getAllFoldRegions().length);
193     foldingModel.runBatchFoldingOperation(() -> {
194       for (FoldRegion region : foldingModel.getAllFoldRegions()) {
195         region.setExpanded(false);
196       }
197     });
198     int textLength = editor.getDocument().getTextLength();
199     editor.getCaretModel().moveToOffset(textLength);
200     editor.getSelectionModel().setSelection(textLength - 1, textLength);
201
202     myManager.openFile(getFile("/src/1.txt"), false);
203     assertEquals(1, myManager.getEditors(file).length);
204     editors = myManager.openFile(file, false);
205
206     assertEquals(1, editors.length);
207     assertTrue(editors[0] instanceof TextEditor);
208     editor = ((TextEditor)editors[0]).getEditor();
209     EditorTestUtil.waitForLoading(editor);
210     assertEquals(textLength, editor.getCaretModel().getOffset());
211     assertEquals(textLength - 1, editor.getSelectionModel().getSelectionStart());
212     assertEquals(textLength, editor.getSelectionModel().getSelectionEnd());
213   }
214
215   public void testOpenInDumbMode() {
216     FileEditorProvider.EP_FILE_EDITOR_PROVIDER
217       .getPoint(null).registerExtension(new MyFileEditorProvider(), myFixture.getTestRootDisposable());
218     FileEditorProvider.EP_FILE_EDITOR_PROVIDER.getPoint(null).registerExtension(new DumbAwareProvider(), myFixture.getTestRootDisposable());
219     try {
220       DumbServiceImpl.getInstance(getProject()).setDumb(true);
221       VirtualFile file = getFile("/src/foo.bar");
222       assertEquals(1, myManager.openFile(file, false).length);
223       DumbServiceImpl.getInstance(getProject()).setDumb(false);
224       UIUtil.dispatchAllInvocationEvents();
225       assertEquals(2, myManager.getAllEditors(file).length);
226       //assertFalse(FileEditorManagerImpl.isDumbAware(editors[0]));
227     }
228     finally {
229       DumbServiceImpl.getInstance(getProject()).setDumb(false);
230     }
231   }
232
233   public void testOpenSpecificTextEditor() {
234     FileEditorProvider.EP_FILE_EDITOR_PROVIDER.getPoint(null)
235       .registerExtension(new MyTextEditorProvider("one", 1), myFixture.getTestRootDisposable());
236     FileEditorProvider.EP_FILE_EDITOR_PROVIDER.getPoint(null)
237       .registerExtension(new MyTextEditorProvider("two", 2), myFixture.getTestRootDisposable());
238     Project project = getProject();
239     VirtualFile file = getFile("/src/Test.java");
240     myManager.openTextEditor(new OpenFileDescriptor(project, file, 1), true);
241     assertEquals("one", myManager.getSelectedEditor(file).getName());
242     myManager.openTextEditor(new OpenFileDescriptor(project, file, 2), true);
243     assertEquals("two", myManager.getSelectedEditor(file).getName());
244   }
245
246   private static final String STRING = "<component name=\"FileEditorManager\">\n" +
247                                        "    <leaf>\n" +
248                                        "      <file pinned=\"false\" current=\"false\" current-in-tab=\"false\">\n" +
249                                        "        <entry file=\"file://$PROJECT_DIR$/src/1.txt\">\n" +
250                                        "          <provider selected=\"true\" editor-type-id=\"text-editor\">\n" +
251                                        "            <state line=\"0\" column=\"0\" selection-start=\"0\" selection-end=\"0\" vertical-scroll-proportion=\"0.0\">\n" +
252                                        "            </state>\n" +
253                                        "          </provider>\n" +
254                                        "        </entry>\n" +
255                                        "      </file>\n" +
256                                        "      <file pinned=\"true\" current=\"false\" current-in-tab=\"false\">\n" +
257                                        "        <entry file=\"file://$PROJECT_DIR$/src/foo.xml\">\n" +
258                                        "          <provider selected=\"true\" editor-type-id=\"text-editor\">\n" +
259                                        "            <state line=\"0\" column=\"0\" selection-start=\"0\" selection-end=\"0\" vertical-scroll-proportion=\"0.0\">\n" +
260                                        "            </state>\n" +
261                                        "          </provider>\n" +
262                                        "        </entry>\n" +
263                                        "      </file>\n" +
264                                        "      <file pinned=\"false\" current=\"true\" current-in-tab=\"true\">\n" +
265                                        "        <entry file=\"file://$PROJECT_DIR$/src/2.txt\">\n" +
266                                        "          <provider selected=\"true\" editor-type-id=\"text-editor\">\n" +
267                                        "            <state line=\"0\" column=\"0\" selection-start=\"0\" selection-end=\"0\" vertical-scroll-proportion=\"0.0\">\n" +
268                                        "            </state>\n" +
269                                        "          </provider>\n" +
270                                        "        </entry>\n" +
271                                        "      </file>\n" +
272                                        "      <file pinned=\"false\" current=\"false\" current-in-tab=\"false\">\n" +
273                                        "        <entry file=\"file://$PROJECT_DIR$/src/3.txt\">\n" +
274                                        "          <provider selected=\"true\" editor-type-id=\"text-editor\">\n" +
275                                        "            <state line=\"0\" column=\"0\" selection-start=\"0\" selection-end=\"0\" vertical-scroll-proportion=\"0.0\">\n" +
276                                        "            </state>\n" +
277                                        "          </provider>\n" +
278                                        "        </entry>\n" +
279                                        "      </file>\n" +
280                                        "    </leaf>\n" +
281                                        "  </component>\n";
282
283   private void assertOpenFiles(String... fileNames) {
284     List<String> names = ContainerUtil.map(myManager.getSplitters().getEditorComposites(), composite -> composite.getFile().getName());
285     assertEquals(Arrays.asList(fileNames), names);
286   }
287
288   @Override
289   protected String getTestDataPath() {
290     return PlatformTestUtil.getPlatformTestDataPath() + "fileEditorManager";
291   }
292
293   static class MyFileEditorProvider implements FileEditorProvider {
294     @NotNull
295     @Override
296     public String getEditorTypeId() {
297       return "mock";
298     }
299
300     @Override
301     public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
302       return true;
303     }
304
305     @NotNull
306     @Override
307     public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
308       return new Mock.MyFileEditor() {
309         @Override
310         public boolean isValid() {
311           return true;
312         }
313
314         @NotNull
315         @Override
316         public JComponent getComponent() {
317           return new JLabel();
318         }
319
320         @NotNull
321         @Override
322         public String getName() {
323           return "mockEditor";
324         }
325       };
326     }
327
328     @Override
329     public void disposeEditor(@NotNull FileEditor editor) {
330     }
331
332     @NotNull
333     @Override
334     public FileEditorPolicy getPolicy() {
335       return FileEditorPolicy.PLACE_AFTER_DEFAULT_EDITOR;
336     }
337   }
338
339   private static class DumbAwareProvider extends MyFileEditorProvider implements DumbAware {
340     @NotNull
341     @Override
342     public String getEditorTypeId() {
343       return "dumbAware";
344     }
345   }
346
347   private static class MyTextEditorProvider implements FileEditorProvider, DumbAware {
348     private final String myId;
349     private final int myTargetOffset;
350
351     private MyTextEditorProvider(String id, int targetOffset) {
352       myId = id;
353       myTargetOffset = targetOffset;
354     }
355
356     @Override
357     public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
358       return true;
359     }
360
361     @NotNull
362     @Override
363     public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
364       return new MyTextEditor(FileDocumentManager.getInstance().getDocument(file), myId, myTargetOffset);
365     }
366
367     @NotNull
368     @Override
369     public String getEditorTypeId() {
370       return myId;
371     }
372
373     @NotNull
374     @Override
375     public FileEditorPolicy getPolicy() {
376       return FileEditorPolicy.HIDE_DEFAULT_EDITOR;
377     }
378   }
379
380   private static class MyTextEditor extends Mock.MyFileEditor implements TextEditor {
381     private final Editor myEditor;
382     private final String myName;
383     private final int myTargetOffset;
384
385     private MyTextEditor(Document document, String name, int targetOffset) {
386       myEditor = EditorFactory.getInstance().createEditor(document);
387       myName = name;
388       myTargetOffset = targetOffset;
389     }
390
391     @Override
392     public void dispose() {
393       try {
394         EditorFactory.getInstance().releaseEditor(myEditor);
395       }
396       finally {
397         super.dispose();
398       }
399     }
400
401     @NotNull
402     @Override
403     public JComponent getComponent() {
404       return new JLabel();
405     }
406
407     @NotNull
408     @Override
409     public String getName() {
410       return myName;
411     }
412
413     @NotNull
414     @Override
415     public Editor getEditor() {
416       return myEditor;
417     }
418
419     @Override
420     public boolean canNavigateTo(@NotNull Navigatable navigatable) {
421       return navigatable instanceof OpenFileDescriptor && ((OpenFileDescriptor)navigatable).getOffset() == myTargetOffset;
422     }
423
424     @Override
425     public void navigateTo(@NotNull Navigatable navigatable) {}
426   }
427 }
428