blob: cec5ba26972167303758956de392861f1e585332 [file] [log] [blame]
/*
* 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.CommonDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.GitUtil;
import git4idea.repo.GitRepository;
import git4idea.repo.GitRepositoryManager;
import icons.GithubIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.github.util.GithubNotifications;
import org.jetbrains.plugins.github.util.GithubUrlUtil;
import org.jetbrains.plugins.github.util.GithubUtil;
import static org.jetbrains.plugins.github.util.GithubUtil.setVisibleEnabled;
/**
* 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";
protected GithubOpenInBrowserAction() {
super("Open on GitHub", "Open corresponding link in browser", GithubIcons.Github_icon);
}
@Override
public void update(final AnActionEvent e) {
Project project = e.getData(CommonDataKeys.PROJECT);
VirtualFile virtualFile = e.getData(CommonDataKeys.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;
}
if (!GithubUtil.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);
}
@Override
public void actionPerformed(final AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
final VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
final Editor editor = e.getData(CommonDataKeys.EDITOR);
if (virtualFile == null || project == null || project.isDisposed()) {
return;
}
String urlToOpen = getGithubUrl(project, virtualFile, editor);
if (urlToOpen != null) {
BrowserUtil.browse(urlToOpen);
}
}
@Nullable
public static String getGithubUrl(@NotNull Project project, @NotNull VirtualFile virtualFile, @Nullable Editor editor) {
GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
final GitRepository repository = manager.getRepositoryForFile(virtualFile);
if (repository == null) {
StringBuilder details = new StringBuilder("file: " + virtualFile.getPresentableUrl() + "; Git repositories: ");
for (GitRepository repo : manager.getRepositories()) {
details.append(repo.getPresentableUrl()).append("; ");
}
GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't find git repository", details.toString());
return null;
}
final String githubRemoteUrl = GithubUtil.findGithubRemoteUrl(repository);
if (githubRemoteUrl == null) {
GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't find github remote");
return null;
}
final String rootPath = repository.getRoot().getPath();
final String path = virtualFile.getPath();
if (!path.startsWith(rootPath)) {
GithubNotifications
.showError(project, CANNOT_OPEN_IN_BROWSER, "File is not under repository root", "Root: " + rootPath + ", file: " + path);
return null;
}
String branch = getBranchNameOnRemote(project, repository);
if (branch == null) {
return null;
}
String relativePath = path.substring(rootPath.length());
String urlToOpen = makeUrlToOpen(editor, relativePath, branch, githubRemoteUrl);
if (urlToOpen == null) {
GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't create properly url", githubRemoteUrl);
return null;
}
return urlToOpen;
}
@Nullable
private static String makeUrlToOpen(@Nullable Editor editor,
@NotNull String relativePath,
@NotNull String branch,
@NotNull String githubRemoteUrl) {
final StringBuilder builder = new StringBuilder();
final String githubRepoUrl = GithubUrlUtil.makeGithubRepoUrlFromRemoteUrl(githubRemoteUrl);
if (githubRepoUrl == null) {
return null;
}
if (StringUtil.isEmptyOrSpaces(relativePath)) {
builder.append(githubRepoUrl).append("/tree/").append(branch);
}
else {
builder.append(githubRepoUrl).append("/blob/").append(branch).append(relativePath);
}
if (editor != null && editor.getDocument().getLineCount() >= 1) {
// lines are counted internally from 0, but from 1 on github
SelectionModel selectionModel = editor.getSelectionModel();
final int begin = editor.getDocument().getLineNumber(selectionModel.getSelectionStart()) + 1;
final int selectionEnd = selectionModel.getSelectionEnd();
int end = editor.getDocument().getLineNumber(selectionEnd) + 1;
if (editor.getDocument().getLineStartOffset(end - 1) == selectionEnd) {
end -= 1;
}
builder.append("#L").append(begin).append('-').append(end);
}
return builder.toString();
}
@Nullable
public static String getBranchNameOnRemote(@NotNull Project project, @NotNull GitRepository repository) {
GitLocalBranch currentBranch = repository.getCurrentBranch();
if (currentBranch == null) {
GithubNotifications.showError(project, CANNOT_OPEN_IN_BROWSER,
"Can't open the file on GitHub when repository is on detached HEAD. Please checkout a branch.");
return null;
}
GitRemoteBranch tracked = currentBranch.findTrackedBranch(repository);
if (tracked == null) {
GithubNotifications
.showError(project, CANNOT_OPEN_IN_BROWSER, "Can't open the file on GitHub when current branch doesn't have a tracked branch.",
"Current branch: " + currentBranch + ", tracked info: " + repository.getBranchTrackInfos());
return null;
}
return tracked.getNameForRemoteOperations();
}
}