mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[github] IDEA-88522 Support show commit on github in Annotated view
Introduce the AnnotationGutterActionProvider extension point to let plugins add custom actions to the annotation popup. Create GithubShowCommitInBrowserFromAnnotateAction, and also move common stuff from the similar action for the log to GithubShowCommitInBrowserAction.
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
interface="com.intellij.openapi.vcs.actions.VcsQuickListContentProvider"/>
|
||||
<extensionPoint name="patch.extension" interface="com.intellij.openapi.diff.impl.patch.PatchEP" area="IDEA_PROJECT"/>
|
||||
<extensionPoint name="vcsChangesViewRefresher" interface="com.intellij.openapi.vcs.changes.ChangesViewRefresher"/>
|
||||
<extensionPoint name="vcsAnnotationGutterActionProvider" interface="com.intellij.openapi.vcs.annotate.AnnotationGutterActionProvider"/>
|
||||
<extensionPoint name="vcschangedetails" interface="com.intellij.openapi.vcs.changes.VcsChangeDetailsProvider" area="IDEA_PROJECT"/>
|
||||
|
||||
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.annotate;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implement this to add additional custom actions to the popup invoked by right-clicking on the annotation gutter.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public interface AnnotationGutterActionProvider {
|
||||
|
||||
ExtensionPointName<AnnotationGutterActionProvider> EP_NAME = ExtensionPointName.create("com.intellij.vcsAnnotationGutterActionProvider");
|
||||
|
||||
/**
|
||||
* Create an action that will be added to the annotation gutter popup.
|
||||
* @param annotation annotation which is currently shown on the gutter.
|
||||
* @return new action that can be invoked from the annotation gutter popup.
|
||||
*/
|
||||
@NotNull
|
||||
AnAction createAction(FileAnnotation annotation);
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.Separator;
|
||||
import com.intellij.openapi.actionSystem.ToggleAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
@@ -207,12 +208,6 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann
|
||||
new AnnotationPresentation(highlighting, switcher, editorGutter, gutters,
|
||||
additionalActions.toArray(new AnAction[additionalActions.size()]));
|
||||
|
||||
for (AnAction action : additionalActions) {
|
||||
if (action instanceof LineNumberListener) {
|
||||
presentation.addLineNumberListener((LineNumberListener)action);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, Color> bgColorMap = Registry.is("vcs.show.colored.annotations") ? computeBgColors(fileAnnotation) : null;
|
||||
final Map<String, Integer> historyIds = Registry.is("vcs.show.history.numbers") ? computeLineNumbers(fileAnnotation) : null;
|
||||
|
||||
@@ -248,9 +243,17 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann
|
||||
gutters.add(new HighlightedAdditionalColumn(fileAnnotation, editor, null, presentation, highlighting, bgColorMap));
|
||||
final AnnotateActionGroup actionGroup = new AnnotateActionGroup(gutters, editorGutter);
|
||||
presentation.addAction(actionGroup, 1);
|
||||
presentation.addAction(new ShowHideAdditionalInfoAction(gutters, editorGutter, actionGroup));
|
||||
gutters.add(new ExtraFieldGutter(fileAnnotation, editor, presentation, bgColorMap, actionGroup));
|
||||
|
||||
presentation.addAction(new ShowHideAdditionalInfoAction(gutters, editorGutter, actionGroup));
|
||||
addActionsFromExtensions(presentation, fileAnnotation);
|
||||
|
||||
for (AnAction action : presentation.getActions()) {
|
||||
if (action instanceof LineNumberListener) {
|
||||
presentation.addLineNumberListener((LineNumberListener)action);
|
||||
}
|
||||
}
|
||||
|
||||
for (AnnotationFieldGutter gutter : gutters) {
|
||||
final AnnotationGutterLineConvertorProxy proxy = new AnnotationGutterLineConvertorProxy(getUpToDateLineNumber, gutter);
|
||||
if (gutter.isGutterAction()) {
|
||||
@@ -263,6 +266,16 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann
|
||||
}
|
||||
}
|
||||
|
||||
private static void addActionsFromExtensions(@NotNull AnnotationPresentation presentation, @NotNull FileAnnotation fileAnnotation) {
|
||||
AnnotationGutterActionProvider[] extensions = AnnotationGutterActionProvider.EP_NAME.getExtensions();
|
||||
if (extensions.length > 0) {
|
||||
presentation.addAction(new Separator());
|
||||
}
|
||||
for (AnnotationGutterActionProvider provider : extensions) {
|
||||
presentation.addAction(provider.createAction(fileAnnotation));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Map<String, Integer> computeLineNumbers(FileAnnotation fileAnnotation) {
|
||||
final SortedList<VcsFileRevision> revisions = new SortedList<VcsFileRevision>(new Comparator<VcsFileRevision>() {
|
||||
|
||||
@@ -85,6 +85,11 @@ class AnnotationPresentation implements TextAnnotationPresentation {
|
||||
return myActions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<AnAction> getActions() {
|
||||
return myActions;
|
||||
}
|
||||
|
||||
public void addSourceSwitchListener(final Consumer<AnnotationSource> listener) {
|
||||
mySwitchAction.addSourceSwitchListener(listener);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<applicationService serviceInterface="org.jetbrains.plugins.github.GithubSettings"
|
||||
serviceImplementation="org.jetbrains.plugins.github.GithubSettings"/>
|
||||
<applicationConfigurable instance="org.jetbrains.plugins.github.ui.GitHubSettingsConfigurable"/>
|
||||
<vcsAnnotationGutterActionProvider implementation="org.jetbrains.plugins.github.ui.GithubAnnotationGutterActionProvider" />
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
@@ -28,7 +29,7 @@
|
||||
<add-to-group group-id="ProjectViewPopupMenu"/>
|
||||
<add-to-group group-id="EditorTabPopupMenu"/>
|
||||
</action>
|
||||
<action id="Github.Open.Commit.In.Browser" class="org.jetbrains.plugins.github.GithubShowCommitInBrowserAction">
|
||||
<action id="Github.Open.Commit.In.Browser" class="org.jetbrains.plugins.github.GithubShowCommitInBrowserFromLogAction">
|
||||
<add-to-group group-id="Git.LogContextMenu" />
|
||||
</action>
|
||||
<action id="Github.Create.Gist" class="org.jetbrains.plugins.github.GithubCreateGistAction">
|
||||
|
||||
@@ -1,200 +1,168 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
|
||||
|
||||
import static org.jetbrains.plugins.github.GithubUtil.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
*
|
||||
* @author oleg
|
||||
* @date 12/10/10
|
||||
*/
|
||||
public class GithubOpenInBrowserAction extends DumbAwareAction {
|
||||
public static final String CANNOT_OPEN_IN_BROWSER = "Cannot open in browser";
|
||||
private static final Logger LOG = Logger.getInstance(GithubOpenInBrowserAction.class.getName());
|
||||
|
||||
protected GithubOpenInBrowserAction() {
|
||||
super("Open in browser", "Open corresponding GitHub link in browser", GITHUB_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
if (project == null || project.isDefault() || virtualFile == null) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
|
||||
|
||||
final GitRepository gitRepository = manager.getRepositoryForFile(virtualFile);
|
||||
if (gitRepository == null) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that given repository is properly configured git repository
|
||||
if (!isRepositoryOnGitHub(gitRepository)) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeListManager changeListManager = ChangeListManager.getInstance(project);
|
||||
if (changeListManager.isUnversioned(virtualFile)) {
|
||||
setVisibleEnabled(e, true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Change change = changeListManager.getChange(virtualFile);
|
||||
if (change != null && change.getType() == Change.Type.NEW) {
|
||||
setVisibleEnabled(e, true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
setVisibleEnabled(e, true, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
final Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
while (!checkCredentials(project)) {
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final VirtualFile root = project.getBaseDir();
|
||||
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
final GitRepository gitRepository = manager.getRepositoryForFile(root);
|
||||
// Check that given repository is properly configured git repository
|
||||
final String githubRemoteUrl = findGithubRemoteUrl(gitRepository);
|
||||
|
||||
final VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
final String rootPath = root.getPath();
|
||||
final String path = virtualFile.getPath();
|
||||
if (!path.startsWith(rootPath)) {
|
||||
Messages.showErrorDialog(project, "File is not under project root", CANNOT_OPEN_IN_BROWSER);
|
||||
return;
|
||||
}
|
||||
|
||||
String branch = getBranchNameOnRemote(project, root);
|
||||
if (branch == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String relativePath = path.substring(rootPath.length());
|
||||
String urlToOpen = makeUrlToOpen(e, relativePath, branch, githubRemoteUrl);
|
||||
BrowserUtil.launchBrowser(urlToOpen);
|
||||
}
|
||||
|
||||
private static String makeUrlToOpen(@NotNull AnActionEvent e, @NotNull String relativePath, @NotNull String branch,
|
||||
@NotNull String githubRemoteUrl) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(makeGithubRepoUrlFromRemoteUrl(githubRemoteUrl)).append("/blob/").append(branch).append(relativePath);
|
||||
final Editor editor = e.getData(PlatformDataKeys.EDITOR);
|
||||
if (editor != null) {
|
||||
final int line = editor.getCaretModel().getLogicalPosition().line + 1; // lines are counted internally from 0, but from 1 on github
|
||||
builder.append("#L").append(line);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String makeGithubRepoUrlFromRemoteUrl(@NotNull String remoteUrl) {
|
||||
remoteUrl = removeEndingDotGit(remoteUrl);
|
||||
if (remoteUrl.startsWith("http")) {
|
||||
return remoteUrl;
|
||||
}
|
||||
if (remoteUrl.startsWith("git://")) {
|
||||
return "https" + remoteUrl.substring(3);
|
||||
}
|
||||
return convertFromSshToHttp(remoteUrl);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String convertFromSshToHttp(@NotNull String remoteUrl) {
|
||||
// Format: git@github.com:account/repository
|
||||
int indexOfAt = remoteUrl.indexOf("@");
|
||||
if (indexOfAt < 0) {
|
||||
throw new IllegalStateException("Invalid remote Github SSH url: " + remoteUrl);
|
||||
}
|
||||
String withoutPrefix = remoteUrl.substring(indexOfAt + 1, remoteUrl.length());
|
||||
return "https://" + withoutPrefix.replace(':', '/');
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String removeEndingDotGit(@NotNull String url) {
|
||||
final String DOT_GIT = ".git";
|
||||
if (url.endsWith(DOT_GIT)) {
|
||||
return url.substring(0, url.length() - DOT_GIT.length());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getBranchNameOnRemote(@NotNull Project project, @NotNull VirtualFile root) {
|
||||
final GitBranch tracked;
|
||||
try {
|
||||
final GitBranch current = GitBranch.current(project, root);
|
||||
if (current == null) {
|
||||
Messages.showErrorDialog(project, "Cannot find local branch", CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
tracked = current.tracked(project, root);
|
||||
if (tracked == null || !tracked.isRemote()) {
|
||||
Messages.showErrorDialog(project, "Cannot find tracked branch for branch: " + current.getFullName(), CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
Messages.showErrorDialog(project, "Error occurred while inspecting branches: " + e1, CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
String branch = tracked.getName();
|
||||
if (branch.startsWith("origin/")) {
|
||||
branch = branch.substring(7);
|
||||
}
|
||||
return branch;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
|
||||
|
||||
import static org.jetbrains.plugins.github.GithubUtil.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
*
|
||||
* @author oleg
|
||||
* @date 12/10/10
|
||||
*/
|
||||
public class GithubOpenInBrowserAction extends DumbAwareAction {
|
||||
public static final String CANNOT_OPEN_IN_BROWSER = "Cannot open in browser";
|
||||
private static final Logger LOG = Logger.getInstance(GithubOpenInBrowserAction.class.getName());
|
||||
|
||||
protected GithubOpenInBrowserAction() {
|
||||
super("Open in browser", "Open corresponding GitHub link in browser", GITHUB_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(final AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
if (project == null || project.isDefault() || virtualFile == null) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
|
||||
|
||||
final GitRepository gitRepository = manager.getRepositoryForFile(virtualFile);
|
||||
if (gitRepository == null) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that given repository is properly configured git repository
|
||||
if (!isRepositoryOnGitHub(gitRepository)) {
|
||||
setVisibleEnabled(e, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeListManager changeListManager = ChangeListManager.getInstance(project);
|
||||
if (changeListManager.isUnversioned(virtualFile)) {
|
||||
setVisibleEnabled(e, true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Change change = changeListManager.getChange(virtualFile);
|
||||
if (change != null && change.getType() == Change.Type.NEW) {
|
||||
setVisibleEnabled(e, true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
setVisibleEnabled(e, true, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
final Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
while (!checkCredentials(project)) {
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final VirtualFile root = project.getBaseDir();
|
||||
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
|
||||
if (manager == null) {
|
||||
return;
|
||||
}
|
||||
final GitRepository gitRepository = manager.getRepositoryForFile(root);
|
||||
// Check that given repository is properly configured git repository
|
||||
final String githubRemoteUrl = findGithubRemoteUrl(gitRepository);
|
||||
|
||||
final VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
final String rootPath = root.getPath();
|
||||
final String path = virtualFile.getPath();
|
||||
if (!path.startsWith(rootPath)) {
|
||||
Messages.showErrorDialog(project, "File is not under project root", CANNOT_OPEN_IN_BROWSER);
|
||||
return;
|
||||
}
|
||||
|
||||
String branch = getBranchNameOnRemote(project, root);
|
||||
if (branch == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String relativePath = path.substring(rootPath.length());
|
||||
String urlToOpen = makeUrlToOpen(e, relativePath, branch, githubRemoteUrl);
|
||||
BrowserUtil.launchBrowser(urlToOpen);
|
||||
}
|
||||
|
||||
private static String makeUrlToOpen(@NotNull AnActionEvent e, @NotNull String relativePath, @NotNull String branch,
|
||||
@NotNull String githubRemoteUrl) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(makeGithubRepoUrlFromRemoteUrl(githubRemoteUrl)).append("/blob/").append(branch).append(relativePath);
|
||||
final Editor editor = e.getData(PlatformDataKeys.EDITOR);
|
||||
if (editor != null) {
|
||||
final int line = editor.getCaretModel().getLogicalPosition().line + 1; // lines are counted internally from 0, but from 1 on github
|
||||
builder.append("#L").append(line);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getBranchNameOnRemote(@NotNull Project project, @NotNull VirtualFile root) {
|
||||
final GitBranch tracked;
|
||||
try {
|
||||
final GitBranch current = GitBranch.current(project, root);
|
||||
if (current == null) {
|
||||
Messages.showErrorDialog(project, "Cannot find local branch", CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
tracked = current.tracked(project, root);
|
||||
if (tracked == null || !tracked.isRemote()) {
|
||||
Messages.showErrorDialog(project, "Cannot find tracked branch for branch: " + current.getFullName(), CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
Messages.showErrorDialog(project, "Error occurred while inspecting branches: " + e1, CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
String branch = tracked.getName();
|
||||
if (branch.startsWith("origin/")) {
|
||||
branch = branch.substring(7);
|
||||
}
|
||||
return branch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-74
@@ -16,104 +16,35 @@
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.history.browser.GitCommit;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubShowCommitInBrowserAction extends DumbAwareAction {
|
||||
abstract class GithubShowCommitInBrowserAction extends DumbAwareAction {
|
||||
|
||||
public GithubShowCommitInBrowserAction() {
|
||||
super("Open in Browser", "Open the selected commit in browser", GithubUtil.GITHUB_ICON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
EventData eventData = collectData(e);
|
||||
e.getPresentation().setVisible(eventData != null);
|
||||
e.getPresentation().setEnabled(eventData != null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EventData collectData(AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
if (project == null || project.isDefault()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GitCommit commit = e.getData(GitVcs.GIT_COMMIT);
|
||||
if (commit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualFile root = commit.getRoot();
|
||||
GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForRoot(root);
|
||||
if (repository == null || !GithubUtil.isRepositoryOnGitHub(repository)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EventData(project, repository, commit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
EventData eventData = collectData(e);
|
||||
if (eventData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GitRepository repository = eventData.getRepository();
|
||||
protected static void openInBrowser(Project project, GitRepository repository, String revisionHash) {
|
||||
String url = GithubUtil.findGithubRemoteUrl(repository);
|
||||
if (url == null) {
|
||||
GithubUtil.LOG.info(String.format("Repository is not under GitHub. Root: %s, Remotes: %s", repository.getRoot(),
|
||||
GitUtil.getPrintableRemotes(repository.getRemotes())));
|
||||
return;
|
||||
}
|
||||
|
||||
String userAndRepository = GithubUtil.getUserAndRepositoryOrShowError(eventData.getProject(), url);
|
||||
url = GithubUtil.makeGithubRepoUrlFromRemoteUrl(url);
|
||||
String userAndRepository = GithubUtil.getUserAndRepositoryOrShowError(project, url);
|
||||
if (userAndRepository == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String githubUrl = "https://github.com/" + userAndRepository + "/commit/" + eventData.getCommit();
|
||||
String githubUrl = "https://github.com/" + userAndRepository + "/commit/" + revisionHash;
|
||||
BrowserUtil.launchBrowser(githubUrl);
|
||||
}
|
||||
|
||||
private static class EventData {
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final GitRepository myRepository;
|
||||
@NotNull private final GitCommit myCommit;
|
||||
|
||||
private EventData(@NotNull Project project, @NotNull GitRepository repository, @NotNull GitCommit commit) {
|
||||
myProject = project;
|
||||
myRepository = repository;
|
||||
myCommit = commit;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitRepository getRepository() {
|
||||
return myRepository;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitCommit getCommit() {
|
||||
return myCommit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.annotate.FileAnnotation;
|
||||
import com.intellij.openapi.vcs.annotate.LineNumberListener;
|
||||
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubShowCommitInBrowserFromAnnotateAction extends GithubShowCommitInBrowserAction implements LineNumberListener {
|
||||
|
||||
private final FileAnnotation myAnnotation;
|
||||
private int myLineNumber = -1;
|
||||
|
||||
public GithubShowCommitInBrowserFromAnnotateAction(FileAnnotation annotation) {
|
||||
super();
|
||||
myAnnotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
EventData eventData = calcData(e);
|
||||
final boolean enabled = myLineNumber != -1 && myAnnotation.getLineRevisionNumber(myLineNumber) != null;
|
||||
e.getPresentation().setEnabled(eventData != null && enabled);
|
||||
e.getPresentation().setVisible(eventData != null && GithubUtil.isRepositoryOnGitHub(eventData.getRepository()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
EventData eventData = calcData(e);
|
||||
if (eventData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final VcsRevisionNumber revisionNumber = myAnnotation.getLineRevisionNumber(myLineNumber);
|
||||
if (revisionNumber != null) {
|
||||
openInBrowser(eventData.getProject(), eventData.getRepository(), revisionNumber.asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EventData calcData(AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
VirtualFile virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
|
||||
if (project == null || virtualFile == null) {
|
||||
return null;
|
||||
}
|
||||
GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForFile(virtualFile);
|
||||
if (repository == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EventData(project, repository);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(Integer integer) {
|
||||
myLineNumber = integer;
|
||||
}
|
||||
|
||||
private static class EventData {
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final GitRepository myRepository;
|
||||
|
||||
private EventData(@NotNull Project project, @NotNull GitRepository repository) {
|
||||
myProject = project;
|
||||
myRepository = repository;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitRepository getRepository() {
|
||||
return myRepository;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.history.browser.GitCommit;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubShowCommitInBrowserFromLogAction extends GithubShowCommitInBrowserAction {
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
EventData eventData = collectData(e);
|
||||
e.getPresentation().setVisible(eventData != null && GithubUtil.isRepositoryOnGitHub(eventData.getRepository()));
|
||||
e.getPresentation().setEnabled(eventData != null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EventData collectData(AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
if (project == null || project.isDefault()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GitCommit commit = e.getData(GitVcs.GIT_COMMIT);
|
||||
if (commit == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualFile root = commit.getRoot();
|
||||
GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForRoot(root);
|
||||
if (repository == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EventData(project, repository, commit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
EventData eventData = collectData(e);
|
||||
if (eventData != null) {
|
||||
openInBrowser(eventData.getProject(), eventData.getRepository(), eventData.getCommit().getHash().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static class EventData {
|
||||
@NotNull private final Project myProject;
|
||||
@NotNull private final GitRepository myRepository;
|
||||
@NotNull private final GitCommit myCommit;
|
||||
|
||||
private EventData(@NotNull Project project, @NotNull GitRepository repository, @NotNull GitCommit commit) {
|
||||
myProject = project;
|
||||
myRepository = repository;
|
||||
myCommit = commit;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitRepository getRepository() {
|
||||
return myRepository;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GitCommit getCommit() {
|
||||
return myCommit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,391 +1,423 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.tasks.github.GithubApiUtil;
|
||||
import git4idea.config.GitVcsApplicationSettings;
|
||||
import git4idea.config.GitVersion;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.repo.GitRemote;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Various utility methods for the GutHub plugin.
|
||||
*
|
||||
* @author oleg
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubUtil {
|
||||
|
||||
public static final Icon GITHUB_ICON = IconLoader.getIcon("/org/jetbrains/plugins/github/github_icon.png");
|
||||
|
||||
public static final Logger LOG = Logger.getInstance("github");
|
||||
|
||||
static final String GITHUB_NOTIFICATION_GROUP = "github";
|
||||
|
||||
/**
|
||||
* @deprecated The host may be defined in different formats. Use {@link com.intellij.tasks.github.GithubApiUtil#getApiUrl(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getHttpsUrl() {
|
||||
return "https://" + GithubSettings.getInstance().getHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> T accessToGithubWithModalProgress(final Project project, final Computable<T> computable) {
|
||||
final Ref<T> result = new Ref<T>();
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
result.set(computable.compute());
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static void accessToGithubWithModalProgress(final Project project, final Runnable runnable) {
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean testConnection(final String url, final String login, final String password) {
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
return user != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GithubUser retrieveCurrentUserInfo(@NotNull String url, @NotNull String login, @NotNull String password) {
|
||||
try {
|
||||
JsonElement result = GithubApiUtil.getRequest(url, login, password, "/user");
|
||||
return parseUserInfo(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GithubUser parseUserInfo(@Nullable JsonElement result) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
if (!result.isJsonObject()) {
|
||||
LOG.error(String.format("Unexpected JSON result format: %s", result));
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonObject obj = (JsonObject)result;
|
||||
if (!obj.has("plan")) {
|
||||
return null;
|
||||
}
|
||||
GithubUser.Plan plan = parsePlan(obj.get("plan"));
|
||||
return new GithubUser(plan);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GithubUser.Plan parsePlan(JsonElement plan) {
|
||||
if (!plan.isJsonObject()) {
|
||||
return GithubUser.Plan.FREE;
|
||||
}
|
||||
return GithubUser.Plan.fromString(plan.getAsJsonObject().get("name").getAsString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> getAvailableRepos(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
boolean ownOnly) {
|
||||
final String request = (ownOnly ? "/user/repos" : "/user/watched");
|
||||
try {
|
||||
JsonElement result = GithubApiUtil.getRequest(url, login, password, request);
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return parseRepositoryInfos(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> parseRepositoryInfos(@NotNull JsonElement result) {
|
||||
if (!result.isJsonArray()) {
|
||||
LOG.assertTrue(result.isJsonObject(), String.format("Unexpected JSON result format: %s", result));
|
||||
return Collections.singletonList(parseSingleRepositoryInfo(result.getAsJsonObject()));
|
||||
}
|
||||
|
||||
List<RepositoryInfo> repositories = new ArrayList<RepositoryInfo>();
|
||||
for (JsonElement element : result.getAsJsonArray()) {
|
||||
LOG.assertTrue(element.isJsonObject(),
|
||||
String.format("This element should be a JsonObject: %s%nTotal JSON response: %n%s", element, result));
|
||||
repositories.add(parseSingleRepositoryInfo(element.getAsJsonObject()));
|
||||
}
|
||||
return repositories;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RepositoryInfo parseSingleRepositoryInfo(@NotNull JsonObject result) {
|
||||
String name = result.get("name").getAsString();
|
||||
String cloneUrl = result.get("clone_url").getAsString();
|
||||
String ownerName = result.get("owner").getAsJsonObject().get("login").getAsString();
|
||||
String parentName = result.has("parent") ? result.get("parent").getAsJsonObject().get("full_name").getAsString(): null;
|
||||
boolean fork = result.get("fork").getAsBoolean();
|
||||
return new RepositoryInfo(name, cloneUrl, ownerName, parentName, fork);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static RepositoryInfo getDetailedRepoInfo(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
@NotNull String owner, @NotNull String name) {
|
||||
try {
|
||||
final String request = "/repos/" + owner + "/" + name;
|
||||
JsonElement jsonObject = GithubApiUtil.getRequest(url, login, password, request);
|
||||
if (jsonObject == null) {
|
||||
LOG.info(String.format("Information about repository is unavailable. Owner: %s, Name: %s", owner, name));
|
||||
return null;
|
||||
}
|
||||
return parseSingleRepositoryInfo(jsonObject.getAsJsonObject());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(String.format("Exception was thrown when trying to retrieve information about repository. Owner: %s, Name: %s",
|
||||
owner, name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPrivateRepoAllowed(final String url, final String login, final String password) {
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
return user.getPlan().isPrivateRepoAllowed();
|
||||
}
|
||||
|
||||
public static boolean checkCredentials(final Project project) {
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
return checkCredentials(project, settings.getHost(), settings.getLogin(), settings.getPassword());
|
||||
}
|
||||
|
||||
public static boolean checkCredentials(final Project project, final String url, final String login, final String password) {
|
||||
if (StringUtil.isEmptyOrSpaces(url) || StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)){
|
||||
return false;
|
||||
}
|
||||
return accessToGithubWithModalProgress(project, new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Trying to login to GitHub");
|
||||
return testConnection(url, login, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows GitHub login settings if credentials are wrong or empty and return the list of all the watched repos by user
|
||||
* @param project
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static List<RepositoryInfo> getAvailableRepos(final Project project, final boolean ownOnly) {
|
||||
while (!checkCredentials(project)){
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Otherwise our credentials are valid and they are successfully stored in settings
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
final String validPassword = settings.getPassword();
|
||||
return accessToGithubWithModalProgress(project, new Computable<List<RepositoryInfo>>() {
|
||||
@Override
|
||||
public List<RepositoryInfo> compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Extracting info about available repositories");
|
||||
return getAvailableRepos(settings.getHost(), settings.getLogin(), validPassword, ownOnly);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows GitHub login settings if credentials are wrong or empty and return the list of all the watched repos by user
|
||||
* @param project
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static RepositoryInfo getDetailedRepositoryInfo(final Project project, final String owner, final String name) {
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
final String password = settings.getPassword();
|
||||
final Boolean validCredentials = accessToGithubWithModalProgress(project, new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Trying to login to GitHub");
|
||||
return testConnection(settings.getHost(), settings.getLogin(), password);
|
||||
}
|
||||
});
|
||||
if (validCredentials == null) {
|
||||
return null;
|
||||
}
|
||||
if (!validCredentials){
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Otherwise our credentials are valid and they are successfully stored in settings
|
||||
final String validPassword = settings.getPassword();
|
||||
return accessToGithubWithModalProgress(project, new Computable<RepositoryInfo>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepositoryInfo compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Extracting detailed info about repository ''" + name + "''");
|
||||
return getDetailedRepoInfo(settings.getHost(), settings.getLogin(), validPassword, owner, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GitRemote findGitHubRemoteBranch(@NotNull GitRepository repository) {
|
||||
// i.e. find origin which points on my github repo
|
||||
// Check that given repository is properly configured git repository
|
||||
for (GitRemote gitRemote : repository.getRemotes()) {
|
||||
if (getGithubUrl(gitRemote) != null){
|
||||
return gitRemote;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getGithubUrl(final GitRemote gitRemote){
|
||||
final GithubSettings githubSettings = GithubSettings.getInstance();
|
||||
final String host = githubSettings.getHost();
|
||||
final String username = githubSettings.getLogin();
|
||||
|
||||
// TODO this doesn't work with organizational accounts
|
||||
final String userRepoMarkerSSHProtocol = host + ":" + username + "/";
|
||||
final String userRepoMarkerOtherProtocols = host + "/" + username + "/";
|
||||
for (String pushUrl : gitRemote.getUrls()) {
|
||||
if (pushUrl.contains(userRepoMarkerSSHProtocol) || pushUrl.contains(userRepoMarkerOtherProtocols)) {
|
||||
return pushUrl;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean testGitExecutable(final Project project) {
|
||||
final GitVcsApplicationSettings settings = GitVcsApplicationSettings.getInstance();
|
||||
final String executable = settings.getPathToGit();
|
||||
final GitVersion version;
|
||||
try {
|
||||
version = GitVersion.identifyVersion(executable);
|
||||
} catch (Exception e) {
|
||||
Messages.showErrorDialog(project, e.getMessage(), GitBundle.getString("find.git.error.title"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!version.isSupported()) {
|
||||
Messages.showWarningDialog(project, GitBundle.message("find.git.unsupported.message", version.toString(), GitVersion.MIN),
|
||||
GitBundle.getString("find.git.success.title"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isRepositoryOnGitHub(@NotNull GitRepository repository) {
|
||||
return findGithubRemoteUrl(repository) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String findGithubRemoteUrl(@NotNull GitRepository repository) {
|
||||
for (GitRemote remote : repository.getRemotes()) {
|
||||
for (String url : remote.getUrls()) {
|
||||
if (isGithubUrl(url)) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isGithubUrl(@NotNull String url) {
|
||||
return url.contains("github.com");
|
||||
}
|
||||
|
||||
static void setVisibleEnabled(AnActionEvent e, boolean visible, boolean enabled) {
|
||||
e.getPresentation().setVisible(visible);
|
||||
e.getPresentation().setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getUserAndRepositoryOrShowError(@NotNull Project project, @NotNull String url) {
|
||||
int index = -1;
|
||||
if (url.startsWith(getHttpsUrl())) {
|
||||
index = url.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository name: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
index = url.substring(0, index).lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository owner: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = url.lastIndexOf(':');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository name and owner: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String repoInfo = url.substring(index + 1);
|
||||
if (repoInfo.endsWith(".git")) {
|
||||
repoInfo = repoInfo.substring(0, repoInfo.length() - 4);
|
||||
}
|
||||
return repoInfo;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.tasks.github.GithubApiUtil;
|
||||
import git4idea.config.GitVcsApplicationSettings;
|
||||
import git4idea.config.GitVersion;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.repo.GitRemote;
|
||||
import git4idea.repo.GitRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Various utility methods for the GutHub plugin.
|
||||
*
|
||||
* @author oleg
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubUtil {
|
||||
|
||||
public static final Icon GITHUB_ICON = IconLoader.getIcon("/org/jetbrains/plugins/github/github_icon.png");
|
||||
|
||||
public static final Logger LOG = Logger.getInstance("github");
|
||||
|
||||
static final String GITHUB_NOTIFICATION_GROUP = "github";
|
||||
|
||||
/**
|
||||
* @deprecated The host may be defined in different formats. Use {@link com.intellij.tasks.github.GithubApiUtil#getApiUrl(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getHttpsUrl() {
|
||||
return "https://" + GithubSettings.getInstance().getHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> T accessToGithubWithModalProgress(final Project project, final Computable<T> computable) {
|
||||
final Ref<T> result = new Ref<T>();
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
result.set(computable.compute());
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static void accessToGithubWithModalProgress(final Project project, final Runnable runnable) {
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean testConnection(final String url, final String login, final String password) {
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
return user != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GithubUser retrieveCurrentUserInfo(@NotNull String url, @NotNull String login, @NotNull String password) {
|
||||
try {
|
||||
JsonElement result = GithubApiUtil.getRequest(url, login, password, "/user");
|
||||
return parseUserInfo(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GithubUser parseUserInfo(@Nullable JsonElement result) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
if (!result.isJsonObject()) {
|
||||
LOG.error(String.format("Unexpected JSON result format: %s", result));
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonObject obj = (JsonObject)result;
|
||||
if (!obj.has("plan")) {
|
||||
return null;
|
||||
}
|
||||
GithubUser.Plan plan = parsePlan(obj.get("plan"));
|
||||
return new GithubUser(plan);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GithubUser.Plan parsePlan(JsonElement plan) {
|
||||
if (!plan.isJsonObject()) {
|
||||
return GithubUser.Plan.FREE;
|
||||
}
|
||||
return GithubUser.Plan.fromString(plan.getAsJsonObject().get("name").getAsString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> getAvailableRepos(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
boolean ownOnly) {
|
||||
final String request = (ownOnly ? "/user/repos" : "/user/watched");
|
||||
try {
|
||||
JsonElement result = GithubApiUtil.getRequest(url, login, password, request);
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return parseRepositoryInfos(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> parseRepositoryInfos(@NotNull JsonElement result) {
|
||||
if (!result.isJsonArray()) {
|
||||
LOG.assertTrue(result.isJsonObject(), String.format("Unexpected JSON result format: %s", result));
|
||||
return Collections.singletonList(parseSingleRepositoryInfo(result.getAsJsonObject()));
|
||||
}
|
||||
|
||||
List<RepositoryInfo> repositories = new ArrayList<RepositoryInfo>();
|
||||
for (JsonElement element : result.getAsJsonArray()) {
|
||||
LOG.assertTrue(element.isJsonObject(),
|
||||
String.format("This element should be a JsonObject: %s%nTotal JSON response: %n%s", element, result));
|
||||
repositories.add(parseSingleRepositoryInfo(element.getAsJsonObject()));
|
||||
}
|
||||
return repositories;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RepositoryInfo parseSingleRepositoryInfo(@NotNull JsonObject result) {
|
||||
String name = result.get("name").getAsString();
|
||||
String cloneUrl = result.get("clone_url").getAsString();
|
||||
String ownerName = result.get("owner").getAsJsonObject().get("login").getAsString();
|
||||
String parentName = result.has("parent") ? result.get("parent").getAsJsonObject().get("full_name").getAsString(): null;
|
||||
boolean fork = result.get("fork").getAsBoolean();
|
||||
return new RepositoryInfo(name, cloneUrl, ownerName, parentName, fork);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static RepositoryInfo getDetailedRepoInfo(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
@NotNull String owner, @NotNull String name) {
|
||||
try {
|
||||
final String request = "/repos/" + owner + "/" + name;
|
||||
JsonElement jsonObject = GithubApiUtil.getRequest(url, login, password, request);
|
||||
if (jsonObject == null) {
|
||||
LOG.info(String.format("Information about repository is unavailable. Owner: %s, Name: %s", owner, name));
|
||||
return null;
|
||||
}
|
||||
return parseSingleRepositoryInfo(jsonObject.getAsJsonObject());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(String.format("Exception was thrown when trying to retrieve information about repository. Owner: %s, Name: %s",
|
||||
owner, name));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPrivateRepoAllowed(final String url, final String login, final String password) {
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
return user.getPlan().isPrivateRepoAllowed();
|
||||
}
|
||||
|
||||
public static boolean checkCredentials(final Project project) {
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
return checkCredentials(project, settings.getHost(), settings.getLogin(), settings.getPassword());
|
||||
}
|
||||
|
||||
public static boolean checkCredentials(final Project project, final String url, final String login, final String password) {
|
||||
if (StringUtil.isEmptyOrSpaces(url) || StringUtil.isEmptyOrSpaces(login) || StringUtil.isEmptyOrSpaces(password)){
|
||||
return false;
|
||||
}
|
||||
return accessToGithubWithModalProgress(project, new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Trying to login to GitHub");
|
||||
return testConnection(url, login, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows GitHub login settings if credentials are wrong or empty and return the list of all the watched repos by user
|
||||
* @param project
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static List<RepositoryInfo> getAvailableRepos(final Project project, final boolean ownOnly) {
|
||||
while (!checkCredentials(project)){
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Otherwise our credentials are valid and they are successfully stored in settings
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
final String validPassword = settings.getPassword();
|
||||
return accessToGithubWithModalProgress(project, new Computable<List<RepositoryInfo>>() {
|
||||
@Override
|
||||
public List<RepositoryInfo> compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Extracting info about available repositories");
|
||||
return getAvailableRepos(settings.getHost(), settings.getLogin(), validPassword, ownOnly);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows GitHub login settings if credentials are wrong or empty and return the list of all the watched repos by user
|
||||
* @param project
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
public static RepositoryInfo getDetailedRepositoryInfo(final Project project, final String owner, final String name) {
|
||||
final GithubSettings settings = GithubSettings.getInstance();
|
||||
final String password = settings.getPassword();
|
||||
final Boolean validCredentials = accessToGithubWithModalProgress(project, new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Trying to login to GitHub");
|
||||
return testConnection(settings.getHost(), settings.getLogin(), password);
|
||||
}
|
||||
});
|
||||
if (validCredentials == null) {
|
||||
return null;
|
||||
}
|
||||
if (!validCredentials){
|
||||
final GithubLoginDialog dialog = new GithubLoginDialog(project);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Otherwise our credentials are valid and they are successfully stored in settings
|
||||
final String validPassword = settings.getPassword();
|
||||
return accessToGithubWithModalProgress(project, new Computable<RepositoryInfo>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepositoryInfo compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Extracting detailed info about repository ''" + name + "''");
|
||||
return getDetailedRepoInfo(settings.getHost(), settings.getLogin(), validPassword, owner, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GitRemote findGitHubRemoteBranch(@NotNull GitRepository repository) {
|
||||
// i.e. find origin which points on my github repo
|
||||
// Check that given repository is properly configured git repository
|
||||
for (GitRemote gitRemote : repository.getRemotes()) {
|
||||
if (getGithubUrl(gitRemote) != null){
|
||||
return gitRemote;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getGithubUrl(final GitRemote gitRemote){
|
||||
final GithubSettings githubSettings = GithubSettings.getInstance();
|
||||
final String host = githubSettings.getHost();
|
||||
final String username = githubSettings.getLogin();
|
||||
|
||||
// TODO this doesn't work with organizational accounts
|
||||
final String userRepoMarkerSSHProtocol = host + ":" + username + "/";
|
||||
final String userRepoMarkerOtherProtocols = host + "/" + username + "/";
|
||||
for (String pushUrl : gitRemote.getUrls()) {
|
||||
if (pushUrl.contains(userRepoMarkerSSHProtocol) || pushUrl.contains(userRepoMarkerOtherProtocols)) {
|
||||
return pushUrl;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean testGitExecutable(final Project project) {
|
||||
final GitVcsApplicationSettings settings = GitVcsApplicationSettings.getInstance();
|
||||
final String executable = settings.getPathToGit();
|
||||
final GitVersion version;
|
||||
try {
|
||||
version = GitVersion.identifyVersion(executable);
|
||||
} catch (Exception e) {
|
||||
Messages.showErrorDialog(project, e.getMessage(), GitBundle.getString("find.git.error.title"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!version.isSupported()) {
|
||||
Messages.showWarningDialog(project, GitBundle.message("find.git.unsupported.message", version.toString(), GitVersion.MIN),
|
||||
GitBundle.getString("find.git.success.title"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isRepositoryOnGitHub(@NotNull GitRepository repository) {
|
||||
return findGithubRemoteUrl(repository) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String findGithubRemoteUrl(@NotNull GitRepository repository) {
|
||||
for (GitRemote remote : repository.getRemotes()) {
|
||||
for (String url : remote.getUrls()) {
|
||||
if (isGithubUrl(url)) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isGithubUrl(@NotNull String url) {
|
||||
return url.contains("github.com");
|
||||
}
|
||||
|
||||
static void setVisibleEnabled(AnActionEvent e, boolean visible, boolean enabled) {
|
||||
e.getPresentation().setVisible(visible);
|
||||
e.getPresentation().setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getUserAndRepositoryOrShowError(@NotNull Project project, @NotNull String url) {
|
||||
int index = -1;
|
||||
if (url.startsWith(getHttpsUrl())) {
|
||||
index = url.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository name: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
index = url.substring(0, index).lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository owner: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = url.lastIndexOf(':');
|
||||
if (index == -1) {
|
||||
Messages.showErrorDialog(project, "Cannot extract info about repository name and owner: " + url, GithubOpenInBrowserAction.CANNOT_OPEN_IN_BROWSER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String repoInfo = url.substring(index + 1);
|
||||
if (repoInfo.endsWith(".git")) {
|
||||
repoInfo = repoInfo.substring(0, repoInfo.length() - 4);
|
||||
}
|
||||
return repoInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String makeGithubRepoUrlFromRemoteUrl(@NotNull String remoteUrl) {
|
||||
remoteUrl = removeEndingDotGit(remoteUrl);
|
||||
if (remoteUrl.startsWith("http")) {
|
||||
return remoteUrl;
|
||||
}
|
||||
if (remoteUrl.startsWith("git://")) {
|
||||
return "https" + remoteUrl.substring(3);
|
||||
}
|
||||
return convertFromSshToHttp(remoteUrl);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String convertFromSshToHttp(@NotNull String remoteUrl) {
|
||||
// Format: git@github.com:account/repository
|
||||
int indexOfAt = remoteUrl.indexOf("@");
|
||||
if (indexOfAt < 0) {
|
||||
throw new IllegalStateException("Invalid remote Github SSH url: " + remoteUrl);
|
||||
}
|
||||
String withoutPrefix = remoteUrl.substring(indexOfAt + 1, remoteUrl.length());
|
||||
return "https://" + withoutPrefix.replace(':', '/');
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String removeEndingDotGit(@NotNull String url) {
|
||||
final String DOT_GIT = ".git";
|
||||
if (url.endsWith(DOT_GIT)) {
|
||||
return url.substring(0, url.length() - DOT_GIT.length());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github.ui;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.vcs.annotate.AnnotationGutterActionProvider;
|
||||
import com.intellij.openapi.vcs.annotate.FileAnnotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.github.GithubShowCommitInBrowserFromAnnotateAction;
|
||||
|
||||
/**
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubAnnotationGutterActionProvider implements AnnotationGutterActionProvider {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AnAction createAction(@NotNull FileAnnotation annotation) {
|
||||
return new GithubShowCommitInBrowserFromAnnotateAction(annotation);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user