Skip to content

Commit 4a2a42d

Browse files
committed
Feature: jump to last opened test/test subject
1 parent b89b278 commit 4a2a42d

File tree

14 files changed

+494
-54
lines changed

14 files changed

+494
-54
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ Otherwise, please find our [latest version here!](https://github.com/MoreUnit/or
3434
- "test" or "spec" prefixes (case insensitive, except for CamelCase style)
3535
- Writing tests for your Java code in Scala? Your test code does not have to be written in the same
3636
language as you production code!
37+
- Practicing TDD, having integration tests? You likely don't always have a one-to-one relationship
38+
between your test and production files...
39+
MoreUnit lets you simply jump to the last opened test file (respectively to the last opened production
40+
file). To access the command:
41+
- From the main menu: "Navigate" > "Jump to Last Opened Test" or "Jump to Last Opened Test Subject".
42+
- From the contextual menu: "Go To" > "Jump to Last Opened Test" or "Jump to Last Opened Test Subject".
43+
- From the "Find Action..." pop-up window: "Jump to Last Opened Test / Test Subject".
44+
- (You should assign it a binding as well!)
3745
- _Work in progress, see the [backlog](#backlog)..._
3846

3947

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@
3030
</ul></li>
3131
<li>Writing tests for your Java code in Scala? Your test code does not have to be written in the same</li>
3232
language as you production code!</li>
33+
<li>Practicing TDD, having integration tests? You likely don't always have a one-to-one relationship
34+
between your test and production files...<br>
35+
MoreUnit lets you simply jump to the last opened test file (respectively to the last opened production
36+
file). To access the command:</ul>
37+
<li>From the main menu: "Navigate" > "Jump to Last Opened Test" or "Jump to Last Opened Test Subject".</li>
38+
<li>From the contextual menu: "Go To" > "Jump to Last Opened Test" or "Jump to Last Opened Test Subject".</li>
39+
<li>From the "Find Action..." pop-up window: "Jump to Last Opened Test / Test Subject".</li>
40+
</ul></li>
3341
</ul>
3442
]]>
3543
</description>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.moreunit.intellij.plugin.actions;
2+
3+
import com.intellij.codeInsight.actions.BaseCodeInsightAction;
4+
import com.intellij.openapi.actionSystem.AnActionEvent;
5+
import com.intellij.openapi.actionSystem.CommonDataKeys;
6+
import com.intellij.openapi.actionSystem.Presentation;
7+
import com.intellij.openapi.editor.Editor;
8+
import com.intellij.openapi.project.Project;
9+
import com.intellij.psi.PsiFile;
10+
import com.intellij.psi.util.PsiUtilBase;
11+
import org.moreunit.intellij.plugin.files.SubjectFile;
12+
13+
public abstract class BaseJumpToTestOrCodeAction extends BaseCodeInsightAction {
14+
15+
@Override
16+
public void update(AnActionEvent event) {
17+
Presentation p = event.getPresentation();
18+
p.setEnabled(false);
19+
20+
Editor editor = event.getData(CommonDataKeys.EDITOR);
21+
Project project = event.getData(CommonDataKeys.PROJECT);
22+
if (editor == null || project == null) {
23+
return;
24+
}
25+
26+
PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project);
27+
if (psiFile == null) {
28+
return;
29+
}
30+
31+
SubjectFile subject = new SubjectFile(psiFile.getVirtualFile());
32+
33+
p.setEnabled(true);
34+
35+
if (subject.isTestFile()) {
36+
p.setText(getTextWhenTestFileSelected());
37+
p.setDescription(getDescriptionWhenTestFileSelected());
38+
} else {
39+
p.setText(getTextWhenProductionFileSelected());
40+
p.setDescription(getDescriptionWhenProductionFileSelected());
41+
}
42+
}
43+
44+
protected abstract String getDescriptionWhenProductionFileSelected();
45+
46+
protected abstract String getDescriptionWhenTestFileSelected();
47+
48+
protected abstract String getTextWhenProductionFileSelected();
49+
50+
protected abstract String getTextWhenTestFileSelected();
51+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.moreunit.intellij.plugin.actions;
2+
3+
import com.intellij.codeInsight.CodeInsightActionHandler;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
public class JumpToLastOpenedTestOrCodeAction extends BaseJumpToTestOrCodeAction {
7+
8+
@NotNull
9+
@Override
10+
protected CodeInsightActionHandler getHandler() {
11+
return new JumpToLastOpenedTestOrCodeHandler();
12+
}
13+
14+
@Override
15+
protected String getTextWhenProductionFileSelected() {
16+
return "Jump to Last Opened Test";
17+
}
18+
19+
@Override
20+
protected String getDescriptionWhenProductionFileSelected() {
21+
return "Jump to the last opened test or, is none has been opened yet, jump to a test of the selected file";
22+
}
23+
24+
@Override
25+
protected String getTextWhenTestFileSelected() {
26+
return "Jump to Last Opened Test Subject";
27+
}
28+
29+
@Override
30+
protected String getDescriptionWhenTestFileSelected() {
31+
return "Jump to the last opened non-test file or, is none has been opened yet, jump to the main subject of the selected test file";
32+
}
33+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.moreunit.intellij.plugin.actions;
2+
3+
import com.intellij.codeInsight.navigation.GotoTargetHandler;
4+
import com.intellij.openapi.editor.Editor;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.openapi.vfs.VirtualFile;
7+
import com.intellij.psi.PsiElement;
8+
import com.intellij.psi.PsiFile;
9+
import com.intellij.psi.PsiManager;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
import org.moreunit.intellij.plugin.files.SubjectFile;
13+
import org.moreunit.intellij.plugin.navigation.FileEditorHistory;
14+
import org.moreunit.intellij.plugin.navigation.ProjectFileEditorHistory;
15+
16+
import java.util.Collections;
17+
18+
public class JumpToLastOpenedTestOrCodeHandler extends GotoTargetHandler {
19+
20+
private final JumpToTestOrCodeHandler jumpToTestOrCodeHandler = new JumpToTestOrCodeHandler();
21+
22+
@Override
23+
protected String getFeatureUsedKey() {
24+
return "org.moreunit.actions.jumpToLastOpenedTest";
25+
}
26+
27+
@Override
28+
protected boolean shouldSortTargets() {
29+
return jumpToTestOrCodeHandler.shouldSortTargets();
30+
}
31+
32+
@Nullable
33+
@Override
34+
protected GotoData getSourceAndTargetElements(Editor editor, PsiFile srcFile) {
35+
Project project = editor.getProject();
36+
FileEditorHistory fileEditorHistory = project.getComponent(ProjectFileEditorHistory.class).getHistory();
37+
38+
SubjectFile subject = new SubjectFile(srcFile.getVirtualFile());
39+
40+
final VirtualFile destFile;
41+
if (subject.isTestFile()) {
42+
destFile = fileEditorHistory.getLastFocusedProdFile();
43+
} else {
44+
destFile = fileEditorHistory.getLastFocusedTestFile();
45+
}
46+
47+
if (destFile != null) {
48+
PsiFile psiFile = PsiManager.getInstance(project).findFile(destFile);
49+
return new GotoData(srcFile, new PsiElement[]{ psiFile }, Collections.<AdditionalAction>emptyList());
50+
}
51+
52+
return jumpToTestOrCodeHandler.getSourceAndTargetElements(editor, srcFile);
53+
}
54+
55+
@NotNull
56+
@Override
57+
protected String getChooserTitle(PsiElement sourceElement, String name, int length) {
58+
return jumpToTestOrCodeHandler.getChooserTitle(sourceElement, name, length);
59+
}
60+
61+
@NotNull
62+
@Override
63+
protected String getNotFoundMessage(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
64+
return jumpToTestOrCodeHandler.getNotFoundMessage(project, editor, file);
65+
}
66+
}
Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
11
package org.moreunit.intellij.plugin.actions;
22

33
import com.intellij.codeInsight.CodeInsightActionHandler;
4-
import com.intellij.codeInsight.actions.BaseCodeInsightAction;
5-
import com.intellij.openapi.actionSystem.AnActionEvent;
6-
import com.intellij.openapi.actionSystem.CommonDataKeys;
7-
import com.intellij.openapi.actionSystem.Presentation;
8-
import com.intellij.openapi.editor.Editor;
9-
import com.intellij.openapi.project.Project;
10-
import com.intellij.psi.PsiFile;
11-
import com.intellij.psi.util.PsiUtilBase;
124
import org.jetbrains.annotations.NotNull;
13-
import org.moreunit.intellij.plugin.files.SubjectFile;
145

15-
public class JumpToTestOrCodeAction extends BaseCodeInsightAction {
6+
public class JumpToTestOrCodeAction extends BaseJumpToTestOrCodeAction {
167

178
@NotNull
189
@Override
@@ -21,31 +12,22 @@ protected CodeInsightActionHandler getHandler() {
2112
}
2213

2314
@Override
24-
public void update(AnActionEvent event) {
25-
Presentation p = event.getPresentation();
26-
p.setEnabled(false);
27-
28-
Editor editor = event.getData(CommonDataKeys.EDITOR);
29-
Project project = event.getData(CommonDataKeys.PROJECT);
30-
if (editor == null || project == null) {
31-
return;
32-
}
33-
34-
PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project);
35-
if (psiFile == null) {
36-
return;
37-
}
15+
protected String getTextWhenProductionFileSelected() {
16+
return "Jump to Test";
17+
}
3818

39-
SubjectFile subject = new SubjectFile(psiFile.getVirtualFile());
19+
@Override
20+
protected String getDescriptionWhenProductionFileSelected() {
21+
return "Jump to a test of the selected file";
22+
}
4023

41-
p.setEnabled(true);
24+
@Override
25+
protected String getTextWhenTestFileSelected() {
26+
return "Jump to Test Subject";
27+
}
4228

43-
if (subject.isTestFile()) {
44-
p.setText("Jump to Test Subject");
45-
p.setDescription("Jump to the subject of the selected test file");
46-
} else {
47-
p.setText("Jump to Test");
48-
p.setDescription("Jump to a test of the selected file");
49-
}
29+
@Override
30+
protected String getDescriptionWhenTestFileSelected() {
31+
return "Jump to the subject of the selected test file";
5032
}
5133
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.moreunit.intellij.plugin.navigation;
2+
3+
import com.intellij.openapi.vfs.VirtualFile;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.moreunit.intellij.plugin.files.SubjectFile;
6+
7+
public class FileEditorHistory {
8+
9+
private VirtualFile lastFocusedTestFile;
10+
private VirtualFile lastFocusedProdFile;
11+
12+
public void fileFocused(@NotNull VirtualFile focusedFile) {
13+
if (new SubjectFile(focusedFile).isTestFile()) {
14+
lastFocusedTestFile = focusedFile;
15+
} else {
16+
lastFocusedProdFile = focusedFile;
17+
}
18+
}
19+
20+
public VirtualFile getLastFocusedTestFile() {
21+
return lastFocusedTestFile;
22+
}
23+
24+
public VirtualFile getLastFocusedProdFile() {
25+
return lastFocusedProdFile;
26+
}
27+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.moreunit.intellij.plugin.navigation;
2+
3+
import com.intellij.openapi.components.ProjectComponent;
4+
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
5+
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
6+
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
7+
import com.intellij.openapi.project.Project;
8+
import com.intellij.util.messages.MessageBus;
9+
import com.intellij.util.messages.MessageBusConnection;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
public class ProjectFileEditorHistory extends FileEditorManagerAdapter implements ProjectComponent {
13+
14+
private final FileEditorHistory history;
15+
private final MessageBus bus;
16+
private MessageBusConnection busConnection;
17+
18+
public ProjectFileEditorHistory(Project ignoredProject, MessageBus bus) {
19+
this.bus = bus;
20+
this.history = new FileEditorHistory();
21+
}
22+
23+
@Override
24+
public void initComponent() {
25+
busConnection = bus.connect();
26+
busConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, this);
27+
}
28+
29+
@Override
30+
public void disposeComponent() {
31+
busConnection.disconnect();
32+
}
33+
34+
@NotNull
35+
@Override
36+
public String getComponentName() {
37+
return "org.moreunit.navigation.projectFileEditorHistory";
38+
}
39+
40+
@Override
41+
public void selectionChanged(FileEditorManagerEvent event) {
42+
history.fileFocused(event.getNewFile());
43+
}
44+
45+
@Override
46+
public void projectOpened() {
47+
// void
48+
}
49+
50+
@Override
51+
public void projectClosed() {
52+
// void
53+
}
54+
55+
public FileEditorHistory getHistory() {
56+
return history;
57+
}
58+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
</application-components>
3333

3434
<project-components>
35-
<!-- Add your project components here -->
35+
<component>
36+
<interface-class>org.moreunit.intellij.plugin.navigation.ProjectFileEditorHistory</interface-class>
37+
<implementation-class>org.moreunit.intellij.plugin.navigation.ProjectFileEditorHistory</implementation-class>
38+
</component>
3639
</project-components>
3740

3841
<actions>
@@ -43,6 +46,13 @@
4346
<add-to-group group-id="GoToCodeGroup" anchor="after" relative-to-action="GotoTest"/>
4447
<add-to-group group-id="EditorPopupMenu.GoTo" anchor="after" relative-to-action="GotoTest"/>
4548
</action>
49+
<action id="org.moreunit.actions.jumpToLastOpenedTest"
50+
class="org.moreunit.intellij.plugin.actions.JumpToLastOpenedTestOrCodeAction"
51+
text="_Jump to Last Opened Test / Test Subject"
52+
description="Jump to the last opened test file — if in a production source file — or to the last opened production source file — if in a test file.">
53+
<add-to-group group-id="GoToCodeGroup" anchor="after" relative-to-action="org.moreunit.actions.jump"/>
54+
<add-to-group group-id="EditorPopupMenu.GoTo" anchor="after" relative-to-action="org.moreunit.actions.jump"/>
55+
</action>
4656
</actions>
4757

4858
</idea-plugin>

0 commit comments

Comments
 (0)