Skip to content
This repository was archived by the owner on Nov 14, 2023. It is now read-only.

Commit 1d75242

Browse files
authored
added Open In Sourcegraph button for VCS / Git revisions (#22)
1 parent 1fa8d5d commit 1d75242

File tree

8 files changed

+233
-1
lines changed

8 files changed

+233
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ Please file an issue: https://github.com/sourcegraph/sourcegraph-jetbrains/issue
7676

7777
## Version History
7878

79+
#### v1.2.1
80+
- Added `Open In Sourcegraph` action to VCS History and Git Log to open a revision in the Sourcegraph diff view.
81+
7982
#### v1.2.0
8083

8184
- The search menu entry is now no longer present when no text has been selected.

build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ intellij {
1717
sandboxDirectory 'idea-sandbox'
1818
}
1919

20+
dependencies {
21+
testImplementation(platform('org.junit:junit-bom:5.7.2'))
22+
testImplementation('org.junit.jupiter:junit-jupiter')
23+
}
24+
25+
test {
26+
useJUnitPlatform()
27+
}
28+
2029
runIde {
2130
systemProperty("idea.is.internal", true)
2231
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import com.google.common.base.Strings;
2+
import java.io.UnsupportedEncodingException;
3+
import java.net.URI;
4+
import java.net.URLEncoder;
5+
6+
public class CommitViewUriBuilder {
7+
8+
public URI build(String sourcegraphBase, String revisionNumber, RepoInfo repoInfo, String productName, String productVersion) {
9+
if (Strings.isNullOrEmpty(sourcegraphBase)) {
10+
throw new RuntimeException("Missing sourcegraph URI for commit uri.");
11+
} else if (Strings.isNullOrEmpty(revisionNumber)) {
12+
throw new RuntimeException("Missing revision number for commit uri.");
13+
} else if (repoInfo == null || Strings.isNullOrEmpty(repoInfo.remoteURL)) {
14+
throw new RuntimeException("Missing remote URL for commit uri.");
15+
}
16+
17+
// this is pretty hacky but to try to build the repo string we will just try to naively parse the git remote uri. Worst case scenario this 404s
18+
URI remote = URI.create(repoInfo.remoteURL);
19+
String path = remote.getPath().replace(".git", "");
20+
21+
StringBuilder builder = new StringBuilder();
22+
try {
23+
builder.append(sourcegraphBase);
24+
builder.append(String.format("/%s%s", remote.getHost(), path));
25+
builder.append(String.format("/-/commit/%s", revisionNumber));
26+
builder.append(String.format("?editor=%s", URLEncoder.encode("JetBrains", "UTF-8")));
27+
builder.append(String.format("&version=%s", URLEncoder.encode(Util.VERSION, "UTF-8")));
28+
builder.append(String.format("&utm_product_name=%s", URLEncoder.encode(productName, "UTF-8")));
29+
builder.append(String.format("&utm_product_version=%s", URLEncoder.encode(productVersion, "UTF-8")));
30+
} catch (UnsupportedEncodingException e) {
31+
e.printStackTrace();
32+
}
33+
34+
return URI.create(builder.toString());
35+
}
36+
37+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import com.intellij.openapi.actionSystem.AnAction;
2+
import com.intellij.openapi.actionSystem.AnActionEvent;
3+
import com.intellij.openapi.application.ApplicationInfo;
4+
import com.intellij.openapi.project.DumbAware;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.openapi.vcs.VcsDataKeys;
7+
import com.intellij.openapi.vcs.history.VcsFileRevision;
8+
import com.intellij.vcs.log.VcsLog;
9+
import com.intellij.vcs.log.VcsLogDataKeys;
10+
import java.awt.Desktop;
11+
import java.io.IOException;
12+
import java.net.URI;
13+
import java.util.Optional;
14+
import org.apache.log4j.LogManager;
15+
import org.apache.log4j.Logger;
16+
import org.jetbrains.annotations.NotNull;
17+
18+
/**
19+
* Jetbrains IDE action to open a selected revision in Sourcegraph.
20+
*/
21+
public class OpenRevisionAction extends AnAction implements DumbAware {
22+
private final Logger logger = LogManager.getLogger(this.getClass());
23+
24+
private Optional<RevisionContext> getHistoryRevision(AnActionEvent e) {
25+
VcsFileRevision revision = e.getDataContext().getData(VcsDataKeys.VCS_FILE_REVISION);
26+
Project project = e.getProject();
27+
28+
if (project == null) {
29+
return Optional.empty();
30+
}
31+
if (revision == null) {
32+
return Optional.empty();
33+
}
34+
35+
String rev = revision.getRevisionNumber().toString();
36+
return Optional.of(new RevisionContext(project, rev));
37+
}
38+
39+
private Optional<RevisionContext> getLogRevision(AnActionEvent e) {
40+
VcsLog log = e.getDataContext().getData(VcsLogDataKeys.VCS_LOG);
41+
Project project = e.getProject();
42+
43+
if (project == null) {
44+
return Optional.empty();
45+
}
46+
if (log == null || log.getSelectedCommits().isEmpty()) {
47+
return Optional.empty();
48+
}
49+
50+
String rev = log.getSelectedCommits().get(0).getHash().asString();
51+
return Optional.of(new RevisionContext(project, rev));
52+
}
53+
54+
@Override
55+
public void actionPerformed(@NotNull AnActionEvent e) {
56+
// This action handles events for both log and history views, so attempt to load from any possible option.
57+
RevisionContext context = getHistoryRevision(e).map(Optional::of)
58+
.orElseGet(() -> getLogRevision(e))
59+
.orElseThrow(() -> new RuntimeException("Unable to determine revision from history or log."));
60+
61+
try {
62+
String productName = ApplicationInfo.getInstance().getVersionName();
63+
String productVersion = ApplicationInfo.getInstance().getFullVersion();
64+
RepoInfo repoInfo = Util.repoInfo(context.getProject().getProjectFilePath());
65+
66+
CommitViewUriBuilder builder = new CommitViewUriBuilder();
67+
URI uri = builder.build(Util.sourcegraphURL(context.getProject()), context.getRevisionNumber(), repoInfo, productName, productVersion);
68+
69+
// Open the URL in the browser.
70+
Desktop.getDesktop().browse(uri);
71+
} catch (IOException err) {
72+
logger.debug("failed to open browser");
73+
err.printStackTrace();
74+
}
75+
}
76+
77+
@Override
78+
public void update(@NotNull AnActionEvent e) {
79+
e.getPresentation().setEnabledAndVisible(true);
80+
}
81+
}

src/main/java/RevisionContext.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import com.intellij.openapi.project.Project;
2+
3+
public class RevisionContext {
4+
private final Project project;
5+
private final String revisionNumber;
6+
7+
public RevisionContext(Project project, String revisionNumber) {
8+
this.project = project;
9+
this.revisionNumber = revisionNumber;
10+
}
11+
12+
public Project getProject() {
13+
return project;
14+
}
15+
16+
public String getRevisionNumber() {
17+
return revisionNumber;
18+
}
19+
}

src/main/java/Util.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import java.util.Properties;
66

77
public class Util {
8-
public static String VERSION = "v1.2.0";
8+
public static String VERSION = "v1.2.1";
99

1010
// gitRemoteURL returns the remote URL for the given remote name.
1111
// e.g. "origin" -> "[email protected]:foo/bar"

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
<change-notes><![CDATA[
1717
<ul>
18+
<li>v1.2.1 - Open Revision in Sourcegraph
19+
<ul>
20+
<li>Added "Open In Sourcegraph" action to VCS History and Git Log to open a revision in the Sourcegraph diff view.</li>
21+
</ul>
22+
</li>
1823
<li>v1.2.0 - Copy link to file, search in repository, per-repository configuration, bug fixes & more
1924
<ul>
2025
<li>The search menu entry is now no longer present when no text has been selected.</li>
@@ -82,6 +87,12 @@
8287
<reference ref="sourcegraph.copy"/>
8388
<add-to-group anchor="last" group-id="EditorPopupMenu"/>
8489
</group>
90+
<action id="OpenRevisionAction" icon="/icons/icon.png" class="OpenRevisionAction" text="Open In Sourcegraph" description="Open revision diff in Sourcegraph">
91+
<add-to-group group-id="VcsHistoryActionsGroup" anchor="last"/>
92+
<add-to-group group-id="Vcs.Log.ContextMenu" anchor="last"/>
93+
<add-to-group group-id="VcsHistoryActionsGroup.Toolbar" anchor="last"/>
94+
<add-to-group group-id="VcsSelectionHistoryDialog.Popup" anchor="last"/>
95+
</action>
8596
</actions>
8697

8798
</idea-plugin>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import static org.junit.jupiter.api.Assertions.assertEquals;
2+
import static org.junit.jupiter.api.Assertions.assertThrows;
3+
4+
import java.net.URI;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.EmptySource;
8+
import org.junit.jupiter.params.provider.NullSource;
9+
10+
public class CommitViewUriBuilderTest {
11+
12+
@Test
13+
public void testBuild_AllValid() {
14+
CommitViewUriBuilder builder = new CommitViewUriBuilder();
15+
16+
RepoInfo repoInfo = new RepoInfo("", "https://github.com/sourcegraph/sourcegraph-jetbrains.git", "main");
17+
18+
URI got = builder.build("https://www.sourcegraph.com",
19+
"1fa8d5d6286c24924b55c15ed4d1a0b85ccab4d5",
20+
repoInfo,
21+
"intellij",
22+
"1.1");
23+
24+
String want = "https://www.sourcegraph.com/github.com/sourcegraph/sourcegraph-jetbrains/-/commit/1fa8d5d6286c24924b55c15ed4d1a0b85ccab4d5?editor=JetBrains&version=v1.2.0&utm_product_name=intellij&utm_product_version=1.1";
25+
assertEquals(want, got.toString());
26+
}
27+
28+
@ParameterizedTest
29+
@NullSource
30+
@EmptySource
31+
public void testBuild_MissingRevision(String revision) {
32+
CommitViewUriBuilder builder = new CommitViewUriBuilder();
33+
RepoInfo repoInfo = new RepoInfo("", "https://github.com/sourcegraph/sourcegraph-jetbrains.git", "main");
34+
35+
assertThrows(RuntimeException.class, () -> builder.build("https://www.sourcegraph.com",
36+
revision,
37+
repoInfo,
38+
"intellij",
39+
"1.1"));
40+
}
41+
42+
@ParameterizedTest
43+
@NullSource
44+
@EmptySource
45+
public void testBuild_MissingBaseUri(String baseUri) {
46+
CommitViewUriBuilder builder = new CommitViewUriBuilder();
47+
RepoInfo repoInfo = new RepoInfo("", "https://github.com/sourcegraph/sourcegraph-jetbrains.git", "main");
48+
49+
assertThrows(RuntimeException.class, () -> builder.build(baseUri,
50+
"1fa8d5d6286c24924b55c15ed4d1a0b85ccab4d5",
51+
repoInfo,
52+
"intellij",
53+
"1.1"));
54+
}
55+
@Test
56+
public void testBuild_MissingRemoteUrl() {
57+
CommitViewUriBuilder builder = new CommitViewUriBuilder();
58+
RepoInfo repoInfo = new RepoInfo("", "", "main");
59+
60+
assertThrows(RuntimeException.class, () -> builder.build("https://www.sourcegraph.com",
61+
"1fa8d5d6286c24924b55c15ed4d1a0b85ccab4d5",
62+
repoInfo,
63+
"intellij",
64+
"1.1"));
65+
66+
assertThrows(RuntimeException.class, () -> builder.build("https://www.sourcegraph.com",
67+
"1fa8d5d6286c24924b55c15ed4d1a0b85ccab4d5",
68+
null,
69+
"intellij",
70+
"1.1"));
71+
}
72+
}

0 commit comments

Comments
 (0)