diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d04420..71d9df7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,4 +55,12 @@ All Notable changes to `Open in Git host` will be documented in this file
## 2.0.1 - 2017-09-30
-- Fixed encoding issue when URL contains non-ASCII characters. #40
\ No newline at end of file
+- Fixed encoding issue when URL contains non-ASCII characters. #40
+
+## 2.1.0 - 2017-11-05
+
+- Code refactor
+- Separated shortcuts for opening in the browser and copying to the clipboard #47
+- Rename plugin to GitLink #46
+- Make default branch customisable #45
+- Add custom URL factory #44
\ No newline at end of file
diff --git a/README.md b/README.md
index bc99234..e5c883a 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,20 @@
-# Open in Git host
-
-[![Build Status](https://travis-ci.org/ben-gibson/jetbrains-open-in-git-host.svg?branch=master)](https://travis-ci.org/ben-gibson/jetbrains-open-in-git-host)
-[![Join the chat at https://gitter.im/jetbrains-open-in-git-host/Lobby](https://badges.gitter.im/jetbrains-open-in-git-host/Lobby.svg)](https://gitter.im/jetbrains-open-in-git-host/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-A Jetbrains plugin that opens a local file under Git version control in its remote host using the default browser.
-It can also optionally copy the URL to the clipboard.
+
+ GitLink
+
+
+
+
+ A Jetbrains plugin that provides shortcuts to open a file or commit in Stash, GitHub, BitBucket or GitLab using the default browser or copy the link to the clipboard.
+
+
+
+
+
+
+
+
+
Installation
-------------------------------------------------------------------------------
@@ -12,14 +22,14 @@ Installation
This plugin is published on the
[JetBrains Plugin Repository](https://plugins.jetbrains.com/plugin/8183):
- Preferences → Plugins → Browse Repositories → Search for "Open in Git host"
+ Preferences → Plugins → Browse Repositories → Search for "GitLink"
### From Source
Clone this repository:
- $ git clone https://github.com/ben-gibson/jetbrains-open-in-git-host
- $ cd jetbrains-open-in-git-host
+ $ git clone https://github.com/ben-gibson/GitLink
+ $ cd GitLink
Update the permissions:
@@ -29,7 +39,7 @@ Build the plugin zip file:
$ ./gradlew buildPlugin
-Install the plugin from `./build/distributions/RemoteRepositoryMapper.zip`:
+Install the plugin from `./build/distributions/GitLink-2.*.zip`:
Preferences → Plugins → Install plugin from disk
@@ -44,7 +54,7 @@ Update the permissions:
Execute an IntelliJ IDEA instance with the plugin you're developing installed:
$ ./gradlew runIdea
-
+
Run the tests:
$ ./gradlew test
@@ -52,21 +62,24 @@ Run the tests:
Usage
-------------------------------------------------------------------------------
-After installing the plugin set your remote host in the preferences:
+After installing the plugin set your remote host (GitHub, GitLab, BitBucket, Stash) and enabled extensions in the preferences:
- Preferences → Other Settings → Open in Git host
+ Preferences → Other Settings → GitLink
Make sure you have registered your projects root under the version control preferences:
Preferences → Version Control (see unregistered roots)
-Open a project file that is under Git version control in the editor:
+To open the current file in the default browser:
+
+ View → Open in (your selected host) or
+ Select in... → Browser (GitLink)
+
+To copy the link to you clipboard:
- View → Open in Git host or
- Select in... → Open in Git host
+ View → Copy (your selected host) link to clipboard
-The current branch is used unless it does not exist in the remote host in which case it defaults to using the master branch.
-The resulting link can be copied to the clipboard depending on your plugin preferences.
+The current branch is used unless it does not exist on the remote in which case it defaults to your preferred branch as defined in the plugin settings.
Change log
-------------------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index eb1a51d..66fd3cb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,10 +36,10 @@ sourceSets {
intellij {
plugins 'git4idea'
- pluginName 'Open in Git host'
+ pluginName 'GitLink'
updateSinceUntilBuild false
sameSinceUntilBuild false
}
group 'org.jetbrains'
-version '2.0.1'
\ No newline at end of file
+version '2.1.0'
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8775aea..4ade5ba 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Aug 22 17:18:30 BST 2017
+#Sat Sep 30 01:29:20 BST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
diff --git a/resources/Icons/Custom/Custom.png b/resources/Icons/Custom/Custom.png
new file mode 100644
index 0000000..aa5aecd
Binary files /dev/null and b/resources/Icons/Custom/Custom.png differ
diff --git a/resources/Icons/Custom/Custom@2x.png b/resources/Icons/Custom/Custom@2x.png
new file mode 100644
index 0000000..c8ae6fb
Binary files /dev/null and b/resources/Icons/Custom/Custom@2x.png differ
diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index 02be7ea..edea675 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -1,32 +1,33 @@
-
+
uk.co.ben-gibson.remote.repository.mapper
- Open in Git host
- 2.0.1
- Ben Gibson
+ GitLink
+ 2.1.0
+ Ben Gibson
- After installing select your remote host and the extensions you want enabled in Settings → Other Settings → Open in Git host
- (currently supports the hosts GitHub, Stash, BitBucket and GitLab). Make sure you have registered your project's root under the version control settings.
+ After installing select your remote host in Settings → Other Settings → GitLink
+ (currently supports GitHub, Stash, BitBucket and GitLab). Make sure you have registered your project's root under the version control settings.
Preferences → Version Control (see unregistered roots)
- To use, open a file that is under Git version control in the editor and select View → Open in Git host. You can
+ To open a file in the default browser select View → Open in (your selected host). You can
also access this action through the Select target menu under Navigate → Select in. If you want to view
a specific commit, you can do this by right clicking the commit from the VCS log tool window and selecting
- Open in Git host.
+ Open in (your selected host).
- Note: The current branch is used unless it does not exist in your remote host in which case it defaults to the
- master branch.
+ Note: The current branch is used unless it does not exist on the remote in which case your preferred default,
+ defined in the settings, is used.
]]>
-
- Fixed encoding issue when URL contains non-ASCII characters. #40
-
+ Code refactor
+ Separate shortcuts for opening in the browser and copying to the clipboard #47.
+ Rename project to GitLink #46.
+ Make default branch customisable #45.
+ Add custom URL factory #44.
]]>
@@ -44,31 +45,51 @@
-
-
-
+
+
+
+
+
+
-
+
diff --git a/src/uk/co/ben_gibson/git/link/Container.java b/src/uk/co/ben_gibson/git/link/Container.java
new file mode 100644
index 0000000..a900308
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Container.java
@@ -0,0 +1,190 @@
+package uk.co.ben_gibson.git.link;
+
+import com.intellij.ide.browsers.BrowserLauncher;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.Project;
+import uk.co.ben_gibson.git.link.Git.RepositoryFactory;
+import uk.co.ben_gibson.git.link.Logger.Handlers.DiagnosticLogHandler;
+import uk.co.ben_gibson.git.link.Logger.Logger;
+import uk.co.ben_gibson.git.link.Url.Modifier.HttpsUrlModifier;
+import uk.co.ben_gibson.git.link.Url.Modifier.UrlModifier;
+import uk.co.ben_gibson.git.link.Url.Factory.*;
+import uk.co.ben_gibson.git.link.Logger.Handlers.EventLogHandler;
+import uk.co.ben_gibson.git.link.Url.Handler.CopyToClipboardHandler;
+import uk.co.ben_gibson.git.link.Url.Handler.OpenInBrowserHandler;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ * Dependency container.
+ *
+ * There doesn't seem to be much documentation around Intellij's service manager. Services seem to be
+ * registered through the plugin.xml but there isn't any information about handling services that have complex dependencies.
+ * This acts as a simple alternative.
+ */
+public class Container
+{
+ private Hashtable applicationServices;
+ private Hashtable> projectServices;
+
+ public Container()
+ {
+ this.applicationServices = new Hashtable<>();
+ this.projectServices = new Hashtable<>();
+ }
+
+ /**
+ * If the plugins project configuration changes we need to flush the lazy load cache.
+ */
+ public void flushProjectCache(Project project)
+ {
+ this.projectServices.remove(project);
+ }
+
+ public Plugin plugin()
+ {
+ if (!this.hasApplicationService(Plugin.class)) {
+ this.registerApplicationService(
+ new Plugin(PluginManager.getPlugin(PluginId.getId("uk.co.ben-gibson.remote.repository.mapper")))
+ );
+ }
+
+ return (Plugin)this.applicationService(Plugin.class);
+ }
+
+ public Preferences preferences(Project project)
+ {
+ return ServiceManager.getService(project, Preferences.class);
+ }
+
+ public UrlFactoryProvider urlFactoryProvider(Project project)
+ {
+ if (!this.hasProjectService(project, UrlFactoryProvider.class)) {
+
+ Preferences preferences = this.preferences(project);
+
+ UrlFactoryProvider provider = new UrlFactoryProvider();
+
+ provider.registerFactory(new GitHubUrlFactory());
+ provider.registerFactory(new BitBucketUrlFactory());
+ provider.registerFactory(new StashUrlFactory());
+ provider.registerFactory(
+ new CustomUrlFactory(preferences.getCustomFileUrlTemplate(), preferences.getCustomCommitUrlTemplate())
+ );
+
+ this.registerProjectService(project, provider);
+ }
+
+ return (UrlFactoryProvider)this.projectService(project, UrlFactoryProvider.class);
+ }
+
+ public OpenInBrowserHandler openInBrowserHandler()
+ {
+ if (!this.hasApplicationService(OpenInBrowserHandler.class)) {
+ this.registerApplicationService(new OpenInBrowserHandler(BrowserLauncher.getInstance()));
+ }
+
+ return (OpenInBrowserHandler)this.applicationService(OpenInBrowserHandler.class);
+ }
+
+ public CopyToClipboardHandler copyToClipboardHandler()
+ {
+ if (!this.hasApplicationService(CopyToClipboardHandler.class)) {
+ this.registerApplicationService(new CopyToClipboardHandler(Toolkit.getDefaultToolkit()));
+ }
+
+ return (CopyToClipboardHandler)this.applicationService(CopyToClipboardHandler.class);
+ }
+
+ public Logger logger(Project project)
+ {
+ if (!this.hasProjectService(project, Logger.class)) {
+
+ Logger logger = new Logger();
+
+ logger.registerHandler(new EventLogHandler(this.plugin(), this.preferences(project).getEnableVerboseEventLog()));
+
+ logger.registerHandler(
+ new DiagnosticLogHandler(com.intellij.openapi.diagnostic.Logger.getInstance(this.plugin().displayName()))
+ );
+
+ this.registerProjectService(project, logger);
+ }
+
+ return (Logger)this.projectService(project, Logger.class);
+ }
+
+ public List urlModifiers()
+ {
+ if (!this.hasApplicationService(UrlModifier.class)) {
+
+ List modifiers = new ArrayList<>();
+
+ modifiers.add(new HttpsUrlModifier());
+
+ this.applicationServices.put(UrlModifier.class, modifiers);
+ }
+
+ return (List)this.applicationService(UrlModifier.class);
+ }
+
+ public Runner runner()
+ {
+ if (!this.hasApplicationService(Runner.class)) {
+ this.registerApplicationService(new Runner());
+ }
+
+ return (Runner)this.applicationService(Runner.class);
+ }
+
+ public RepositoryFactory repositoryFactory()
+ {
+ if (!this.hasApplicationService(RepositoryFactory.class)) {
+ this.registerApplicationService(new RepositoryFactory());
+ }
+
+ return (RepositoryFactory)this.applicationService(RepositoryFactory.class);
+ }
+
+ private void registerApplicationService(Object service)
+ {
+ this.applicationServices.put(service.getClass(), service);
+ }
+
+ private Object applicationService(Class serviceClass)
+ {
+ return this.applicationServices.get(serviceClass);
+ }
+
+ private Boolean hasApplicationService(Class serviceClass)
+ {
+ return this.applicationServices.containsKey(serviceClass);
+ }
+
+ private void registerProjectService(Project project, Object service)
+ {
+ if (!this.projectServices.containsKey(project)) {
+ this.projectServices.put(project, new Hashtable<>());
+ }
+
+ this.projectServices.get(project).put(service.getClass(), service);
+ }
+
+ private Object projectService(Project project, Class serviceClass)
+ {
+ if (!this.projectServices.containsKey(project)) {
+ return null;
+ }
+
+ return this.projectServices.get(project).get(serviceClass);
+ }
+
+ private Boolean hasProjectService(Project project, Class serviceClass)
+ {
+ return this.projectServices.containsKey(project) && this.projectServices.get(project).containsKey(serviceClass);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Exception/GitLinkException.java b/src/uk/co/ben_gibson/git/link/Exception/GitLinkException.java
new file mode 100644
index 0000000..3fa50e0
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Exception/GitLinkException.java
@@ -0,0 +1,12 @@
+package uk.co.ben_gibson.git.link.Exception;
+
+/**
+ * A base exception from which all exceptions from this plugin should extend!
+ */
+public abstract class GitLinkException extends Exception
+{
+ public GitLinkException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Branch.java b/src/uk/co/ben_gibson/git/link/Git/Branch.java
similarity index 64%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Branch.java
rename to src/uk/co/ben_gibson/git/link/Git/Branch.java
index d3fc615..19b55c9 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Branch.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Branch.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
public class Branch
{
@@ -18,4 +18,9 @@ public String toString()
{
return this.name;
}
+
+ public Boolean equals(Branch branch)
+ {
+ return this.toString().equals(branch.toString());
+ }
}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Commit.java b/src/uk/co/ben_gibson/git/link/Git/Commit.java
similarity index 87%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Commit.java
rename to src/uk/co/ben_gibson/git/link/Git/Commit.java
index 96d6e3a..9cd72b1 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Commit.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Commit.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
import com.intellij.vcs.log.VcsFullCommitDetails;
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Exception/BranchException.java b/src/uk/co/ben_gibson/git/link/Git/Exception/BranchException.java
similarity index 61%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Exception/BranchException.java
rename to src/uk/co/ben_gibson/git/link/Git/Exception/BranchException.java
index 336b14a..371a32a 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Exception/BranchException.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Exception/BranchException.java
@@ -1,8 +1,8 @@
-package uk.co.ben_gibson.open.in.git.host.Git.Exception;
+package uk.co.ben_gibson.git.link.Git.Exception;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
-public class BranchException extends OpenInGitHostException
+public class BranchException extends GitLinkException
{
private BranchException(String message)
{
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Exception/RemoteException.java b/src/uk/co/ben_gibson/git/link/Git/Exception/RemoteException.java
similarity index 65%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Exception/RemoteException.java
rename to src/uk/co/ben_gibson/git/link/Git/Exception/RemoteException.java
index ab394df..e8dcdf9 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Exception/RemoteException.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Exception/RemoteException.java
@@ -1,12 +1,12 @@
-package uk.co.ben_gibson.open.in.git.host.Git.Exception;
+package uk.co.ben_gibson.git.link.Git.Exception;
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
/**
* Thrown when a remote could not be found.
*/
-public class RemoteException extends OpenInGitHostException
+public class RemoteException extends GitLinkException
{
private RemoteException(String message)
{
diff --git a/src/uk/co/ben_gibson/git/link/Git/Exception/RepositoryNotFoundException.java b/src/uk/co/ben_gibson/git/link/Git/Exception/RepositoryNotFoundException.java
new file mode 100644
index 0000000..dc8e29d
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Git/Exception/RepositoryNotFoundException.java
@@ -0,0 +1,14 @@
+package uk.co.ben_gibson.git.link.Git.Exception;
+
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+
+/**
+ * Thrown when the repository could not be found.
+ */
+public class RepositoryNotFoundException extends GitLinkException
+{
+ public RepositoryNotFoundException()
+ {
+ super("Git repository not found!");
+ }
+}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/File.java b/src/uk/co/ben_gibson/git/link/Git/File.java
similarity index 67%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/File.java
rename to src/uk/co/ben_gibson/git/link/Git/File.java
index 83a6d05..d4efc2b 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/File.java
+++ b/src/uk/co/ben_gibson/git/link/Git/File.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
import com.intellij.openapi.vfs.VirtualFile;
@@ -7,7 +7,7 @@
*/
public class File
{
- private String path; // A path from the root of the repository.
+ private String path; // A pathWithName from the root of the repository.
private VirtualFile file; // The underlying file.
public File(String path, VirtualFile file)
@@ -21,6 +21,11 @@ public File(String path, VirtualFile file)
}
public String path()
+ {
+ return this.path.substring(0, (this.path.length() - this.name().length()));
+ }
+
+ public String pathWithName()
{
return this.path;
}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Remote.java b/src/uk/co/ben_gibson/git/link/Git/Remote.java
similarity index 91%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Remote.java
rename to src/uk/co/ben_gibson/git/link/Git/Remote.java
index f47adbe..21f1ab9 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Remote.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Remote.java
@@ -1,12 +1,13 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
import com.intellij.openapi.util.text.StringUtil;
import git4idea.GitLocalBranch;
import git4idea.commands.Git;
import git4idea.commands.GitCommandResult;
import git4idea.repo.GitRemote;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.BranchException;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Exception.BranchException;
+
import java.net.MalformedURLException;
import java.net.URL;
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/RemoteHost.java b/src/uk/co/ben_gibson/git/link/Git/RemoteHost.java
similarity index 81%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/RemoteHost.java
rename to src/uk/co/ben_gibson/git/link/Git/RemoteHost.java
index bd8924d..06b9f46 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/RemoteHost.java
+++ b/src/uk/co/ben_gibson/git/link/Git/RemoteHost.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
import com.intellij.openapi.util.IconLoader;
import javax.swing.*;
@@ -11,7 +11,8 @@ public enum RemoteHost
GIT_HUB("GitHub", "/Icons/GitHub/GitHub.png"),
STASH("Stash", "/Icons/Bitbucket/Bitbucket.png"),
BITBUCKET("Bitbucket", "/Icons/Bitbucket/Bitbucket.png"),
- GITLAB("GitLab", "/Icons/GitLab/GitLab.png");
+ GITLAB("GitLab", "/Icons/GitLab/GitLab.png"),
+ CUSTOM("Custom", "/Icons/Custom/Custom.png");
private final String name;
private final String icon;
@@ -27,6 +28,11 @@ public String toString()
return this.name;
}
+ public boolean custom()
+ {
+ return (this == CUSTOM);
+ }
+
public boolean gitHub()
{
return (this == GIT_HUB);
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Git/Repository.java b/src/uk/co/ben_gibson/git/link/Git/Repository.java
similarity index 92%
rename from src/uk/co/ben_gibson/open/in/git/host/Git/Repository.java
rename to src/uk/co/ben_gibson/git/link/Git/Repository.java
index 3792ab4..5a6837b 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Git/Repository.java
+++ b/src/uk/co/ben_gibson/git/link/Git/Repository.java
@@ -1,14 +1,13 @@
-package uk.co.ben_gibson.open.in.git.host.Git;
+package uk.co.ben_gibson.git.link.Git;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitLocalBranch;
import git4idea.GitRemoteBranch;
import git4idea.commands.Git;
-import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.BranchException;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Exception.BranchException;
/**
* Represents a git repository.
diff --git a/src/uk/co/ben_gibson/git/link/Git/RepositoryFactory.java b/src/uk/co/ben_gibson/git/link/Git/RepositoryFactory.java
new file mode 100644
index 0000000..d3138f0
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Git/RepositoryFactory.java
@@ -0,0 +1,34 @@
+package uk.co.ben_gibson.git.link.Git;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import git4idea.GitUtil;
+import git4idea.commands.GitImpl;
+import git4idea.repo.GitRepository;
+import uk.co.ben_gibson.git.link.Git.Exception.RepositoryNotFoundException;
+
+public class RepositoryFactory
+{
+ public Repository create(Project project, VirtualFile file, Branch defaultBranch) throws RepositoryNotFoundException
+ {
+ GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForFile(file);
+
+ if (repository == null) {
+ throw new RepositoryNotFoundException();
+ }
+
+ return new Repository(new GitImpl(), repository, defaultBranch);
+ }
+
+ public Repository create(Project project, VcsFullCommitDetails commitDetails, Branch defaultBranch) throws RepositoryNotFoundException
+ {
+ GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForRoot(commitDetails.getRoot());
+
+ if (repository == null) {
+ throw new RepositoryNotFoundException();
+ }
+
+ return new Repository(new GitImpl(), repository, defaultBranch);
+ }
+}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/DiagnosticLogHandler.java b/src/uk/co/ben_gibson/git/link/Logger/Handlers/DiagnosticLogHandler.java
similarity index 84%
rename from src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/DiagnosticLogHandler.java
rename to src/uk/co/ben_gibson/git/link/Logger/Handlers/DiagnosticLogHandler.java
index 6066695..430caf6 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/DiagnosticLogHandler.java
+++ b/src/uk/co/ben_gibson/git/link/Logger/Handlers/DiagnosticLogHandler.java
@@ -1,7 +1,7 @@
-package uk.co.ben_gibson.open.in.git.host.Logger.Handlers;
+package uk.co.ben_gibson.git.link.Logger.Handlers;
import com.intellij.openapi.diagnostic.Logger;
-import uk.co.ben_gibson.open.in.git.host.Logger.LogMessage;
+import uk.co.ben_gibson.git.link.Logger.LogMessage;
/**
* Decorates the intellij diagnostic logger.
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/EventLogHandler.java b/src/uk/co/ben_gibson/git/link/Logger/Handlers/EventLogHandler.java
similarity index 87%
rename from src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/EventLogHandler.java
rename to src/uk/co/ben_gibson/git/link/Logger/Handlers/EventLogHandler.java
index 4f12a3e..b4b47f3 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/EventLogHandler.java
+++ b/src/uk/co/ben_gibson/git/link/Logger/Handlers/EventLogHandler.java
@@ -1,10 +1,10 @@
-package uk.co.ben_gibson.open.in.git.host.Logger.Handlers;
+package uk.co.ben_gibson.git.link.Logger.Handlers;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
-import uk.co.ben_gibson.open.in.git.host.Logger.LogMessage;
-import uk.co.ben_gibson.open.in.git.host.Plugin;
+import uk.co.ben_gibson.git.link.Logger.LogMessage;
+import uk.co.ben_gibson.git.link.Plugin;
/**
* Logs messages to the event log window.
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/LogHandler.java b/src/uk/co/ben_gibson/git/link/Logger/Handlers/LogHandler.java
similarity index 63%
rename from src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/LogHandler.java
rename to src/uk/co/ben_gibson/git/link/Logger/Handlers/LogHandler.java
index a7d46d1..80a90a0 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Logger/Handlers/LogHandler.java
+++ b/src/uk/co/ben_gibson/git/link/Logger/Handlers/LogHandler.java
@@ -1,6 +1,6 @@
-package uk.co.ben_gibson.open.in.git.host.Logger.Handlers;
+package uk.co.ben_gibson.git.link.Logger.Handlers;
-import uk.co.ben_gibson.open.in.git.host.Logger.LogMessage;
+import uk.co.ben_gibson.git.link.Logger.LogMessage;
/**
* Handles a log message in some way.
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Logger/LogMessage.java b/src/uk/co/ben_gibson/git/link/Logger/LogMessage.java
similarity index 95%
rename from src/uk/co/ben_gibson/open/in/git/host/Logger/LogMessage.java
rename to src/uk/co/ben_gibson/git/link/Logger/LogMessage.java
index 8b6e81d..1fd5e2a 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Logger/LogMessage.java
+++ b/src/uk/co/ben_gibson/git/link/Logger/LogMessage.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.Logger;
+package uk.co.ben_gibson.git.link.Logger;
/**
* Represents a log message.
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Logger/Logger.java b/src/uk/co/ben_gibson/git/link/Logger/Logger.java
similarity index 66%
rename from src/uk/co/ben_gibson/open/in/git/host/Logger/Logger.java
rename to src/uk/co/ben_gibson/git/link/Logger/Logger.java
index ecf3cf7..a5080c2 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Logger/Logger.java
+++ b/src/uk/co/ben_gibson/git/link/Logger/Logger.java
@@ -1,8 +1,6 @@
-package uk.co.ben_gibson.open.in.git.host.Logger;
-
-import uk.co.ben_gibson.open.in.git.host.Exception.InvalidConfigurationException;
-import uk.co.ben_gibson.open.in.git.host.Logger.Handlers.LogHandler;
+package uk.co.ben_gibson.git.link.Logger;
+import uk.co.ben_gibson.git.link.Logger.Handlers.LogHandler;
import java.util.ArrayList;
import java.util.List;
@@ -30,11 +28,7 @@ public void error(String message)
public void exception(Exception exception)
{
- if (exception instanceof InvalidConfigurationException) {
- this.log(LogMessage.warning(exception.getMessage()));
- } else {
- this.log(LogMessage.error(exception.getMessage()));
- }
+ this.log(LogMessage.error(exception.getMessage()));
}
public void registerHandler(LogHandler handler)
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Plugin.java b/src/uk/co/ben_gibson/git/link/Plugin.java
similarity index 53%
rename from src/uk/co/ben_gibson/open/in/git/host/Plugin.java
rename to src/uk/co/ben_gibson/git/link/Plugin.java
index 744a572..30b742a 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/Plugin.java
+++ b/src/uk/co/ben_gibson/git/link/Plugin.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host;
+package uk.co.ben_gibson.git.link;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
@@ -7,18 +7,11 @@
*/
public class Plugin
{
- private String name;
- private String version;
-
- public Plugin(String name, String version)
- {
- this.name = name;
- this.version = version;
- }
+ private IdeaPluginDescriptor pluginDescriptor;
public Plugin(IdeaPluginDescriptor pluginDescriptor)
{
- this(pluginDescriptor.getName(), pluginDescriptor.getVersion());
+ this.pluginDescriptor = pluginDescriptor;
}
public String toString() {
@@ -27,11 +20,16 @@ public String toString() {
public String displayName()
{
- return name;
+ return this.pluginDescriptor.getName();
}
public String version()
{
- return version;
+ return this.pluginDescriptor.getVersion();
+ }
+
+ public String issueTracker()
+ {
+ return this.pluginDescriptor.getVendorUrl().concat("/issues");
}
}
diff --git a/src/uk/co/ben_gibson/git/link/Preferences.java b/src/uk/co/ben_gibson/git/link/Preferences.java
new file mode 100644
index 0000000..c40e3b7
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Preferences.java
@@ -0,0 +1,113 @@
+package uk.co.ben_gibson.git.link;
+
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.util.xmlb.XmlSerializerUtil;
+import uk.co.ben_gibson.git.link.Url.Modifier.UrlModifier;
+import java.util.ArrayList;
+import java.util.List;
+
+@State(name = "Preferences",
+ storages = {@Storage("GitLinkConfig.xml")}
+)
+
+/*
+ * Plugin preferences - Getters and Setters required by PersistentStateComponent
+ */
+public class Preferences implements PersistentStateComponent
+{
+ private RemoteHost remoteHost = RemoteHost.GIT_HUB;
+ private boolean enableVerboseEventLog = false;
+ private List enabledModifiers = new ArrayList<>();
+ private Branch defaultBranch = new Branch("master");
+ private String customFileUrlTemplate = "";
+ private String customCommitUrlTemplate = "";
+
+ public boolean isModifierEnabled(UrlModifier modifier)
+ {
+ return this.enabledModifiers.contains(modifier.getClass().getName());
+ }
+
+ public List getEnabledModifiers()
+ {
+ return this.enabledModifiers;
+ }
+
+ public void setEnabledModifiers(List enabledModifiers)
+ {
+ this.enabledModifiers = enabledModifiers;
+ }
+
+ public void enableModifier(UrlModifier modifier)
+ {
+ this.enabledModifiers.add(modifier.getClass().getName());
+ }
+
+ public void disableModifier(UrlModifier modifier)
+ {
+ this.enabledModifiers.remove(modifier.getClass().getName());
+ }
+
+ public boolean getEnableVerboseEventLog()
+ {
+ return this.enableVerboseEventLog;
+ }
+
+ public void setEnableVerboseEventLog(boolean enableVerboseEventLog)
+ {
+ this.enableVerboseEventLog = enableVerboseEventLog;
+ }
+
+ public void setRemoteHost(RemoteHost remoteHost)
+ {
+ this.remoteHost = remoteHost;
+ }
+
+ public RemoteHost getRemoteHost()
+ {
+ return this.remoteHost;
+ }
+
+ public void loadState(Preferences state)
+ {
+ XmlSerializerUtil.copyBean(state, this);
+ }
+
+ public Branch getDefaultBranch()
+ {
+ return this.defaultBranch;
+ }
+
+ public void setDefaultBranch(Branch defaultBranch)
+ {
+ this.defaultBranch = defaultBranch;
+ }
+
+ public String getCustomFileUrlTemplate()
+ {
+ return customFileUrlTemplate;
+ }
+
+ public void setCustomFileUrlTemplate(String customFileUrlTemplate)
+ {
+ this.customFileUrlTemplate = customFileUrlTemplate;
+ }
+
+ public String getCustomCommitUrlTemplate()
+ {
+ return customCommitUrlTemplate;
+ }
+
+ public void setCustomCommitUrlTemplate(String customCommitUrlTemplate)
+ {
+ this.customCommitUrlTemplate = customCommitUrlTemplate;
+ }
+
+ public Preferences getState()
+ {
+ return this;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/RemoteUrlGenerator.java b/src/uk/co/ben_gibson/git/link/RemoteUrlGenerator.java
new file mode 100644
index 0000000..899f788
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/RemoteUrlGenerator.java
@@ -0,0 +1,10 @@
+package uk.co.ben_gibson.git.link;
+
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+
+import java.net.URL;
+
+public interface RemoteUrlGenerator
+{
+ URL generateRemoteURL() throws GitLinkException;
+}
diff --git a/src/uk/co/ben_gibson/git/link/Runner.java b/src/uk/co/ben_gibson/git/link/Runner.java
new file mode 100644
index 0000000..ee1d291
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Runner.java
@@ -0,0 +1,120 @@
+package uk.co.ben_gibson.git.link;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import org.jetbrains.annotations.NotNull;
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+import uk.co.ben_gibson.git.link.Git.Commit;
+import uk.co.ben_gibson.git.link.Git.Exception.RepositoryNotFoundException;
+import uk.co.ben_gibson.git.link.Git.Repository;
+import uk.co.ben_gibson.git.link.Logger.Logger;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+import uk.co.ben_gibson.git.link.Url.Modifier.UrlModifier;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+import java.net.URL;
+
+/**
+ * Acts as a facade for the plugin.
+ */
+public class Runner
+{
+ public void runForFile(Project project, VirtualFile file, UrlHandler handler)
+ {
+ Container container = this.container();
+ Preferences preferences = container.preferences(project);
+ Repository repository;
+
+ try {
+ repository = container.repositoryFactory().create(project, file, preferences.getDefaultBranch());
+ } catch (RepositoryNotFoundException e) {
+ container.logger(project).warning("Git repository not found, make sure you have registered your version control root: Preferences → Version Control");
+ return;
+ }
+
+ Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
+
+ Integer caretPosition = (editor != null) ? editor.getCaretModel().getLogicalPosition().line + 1 : null;
+
+ this.run(project, handler, () -> {
+
+ UrlFactory remoteUrlFactory = container.urlFactoryProvider(project).urlFactory(preferences.getRemoteHost());
+
+ return remoteUrlFactory.createUrl(
+ new FileDescription(
+ repository.origin(),
+ repository.currentBranch(),
+ repository.fileFromVirtualFile(file),
+ caretPosition
+ )
+ );
+ });
+ }
+
+ public void runForCommit(Project project, VcsFullCommitDetails commitDetails, UrlHandler handler)
+ {
+ Container container = this.container();
+ Preferences preferences = container.preferences(project);
+ Repository repository;
+
+ try {
+ repository = container.repositoryFactory().create(project, commitDetails, preferences.getDefaultBranch());
+ } catch (RepositoryNotFoundException e) {
+ container.logger(project).warning("Git repository not found, make sure you have registered your version control root: Preferences → Version Control");
+ return;
+ }
+
+ this.run(project, handler, () -> {
+ UrlFactory remoteUrlFactory = container.urlFactoryProvider(project).urlFactory(preferences.getRemoteHost());
+
+ return remoteUrlFactory.createUrl(new CommitDescription(repository.origin(), new Commit(commitDetails)));
+ });
+ }
+
+ private void run(Project project, UrlHandler handler, RemoteUrlGenerator generator)
+ {
+ Container container = this.container();
+ Preferences preferences = container.preferences(project);
+ Logger logger = container.logger(project);
+
+ Task.Backgroundable task = new Task.Backgroundable(project, "GitLink - Processing") {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ try {
+
+ URL remoteUrl = generator.generateRemoteURL();
+
+ logger.notice(String.format("Generated URL '%s'", remoteUrl.toString()));
+
+ for (UrlModifier urlModifier: container.urlModifiers()) {
+ if (preferences.isModifierEnabled(urlModifier)) {
+ remoteUrl = urlModifier.modify(remoteUrl);
+ logger.notice(String.format("Applied modifier '%s' - '%s'", urlModifier.name(), remoteUrl.toString()));
+ }
+ }
+
+ logger.notice(String.format("Running URL handler '%s'", handler.name()));
+
+ handler.handle(generator.generateRemoteURL());
+
+ } catch (GitLinkException e) {
+ logger.exception(e);
+ }
+ }
+ };
+
+ task.queue();
+ }
+
+ protected Container container()
+ {
+ return ServiceManager.getService(Container.class);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Action.java b/src/uk/co/ben_gibson/git/link/UI/Action/Action.java
new file mode 100644
index 0000000..e8e878f
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Action.java
@@ -0,0 +1,60 @@
+package uk.co.ben_gibson.git.link.UI.Action;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.Presentation;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Container;
+import uk.co.ben_gibson.git.link.Logger.Logger;
+import uk.co.ben_gibson.git.link.Preferences;
+
+public abstract class Action extends AnAction
+{
+ protected abstract boolean shouldActionBeEnabled(AnActionEvent event);
+ protected abstract String displayName(RemoteHost remoteHost);
+
+ public abstract void actionPerformed(Project project, AnActionEvent event);
+
+ public void actionPerformed(AnActionEvent event)
+ {
+ Project project = event.getProject();
+
+ Container container = this.container();
+ Preferences settings = container.preferences(project);
+ Logger logger = container().logger(project);
+
+ if (project == null) {
+ return;
+ }
+
+ logger.notice(String.format("Running '%s' action.", this.displayName(settings.getRemoteHost())));
+
+ this.actionPerformed(project, event);
+ }
+
+ public void update(AnActionEvent event)
+ {
+ super.update(event);
+
+ if (event.getProject() == null) {
+ event.getPresentation().setEnabled(false);
+ return;
+ }
+
+ RemoteHost remoteHost = this.container().preferences(event.getProject()).getRemoteHost();
+
+ Presentation presentation = event.getPresentation();
+
+ presentation.setText(this.displayName(remoteHost));
+ presentation.setIcon(remoteHost.icon());
+
+ presentation.setEnabledAndVisible(this.shouldActionBeEnabled(event));
+ }
+
+ protected Container container()
+ {
+ return ServiceManager.getService(Container.class);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Menu/BrowserMenuAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/BrowserMenuAction.java
new file mode 100644
index 0000000..a28ff4e
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/BrowserMenuAction.java
@@ -0,0 +1,17 @@
+package uk.co.ben_gibson.git.link.UI.Action.Menu;
+
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+
+public class BrowserMenuAction extends MenuAction
+{
+ protected String displayName(RemoteHost remoteHost)
+ {
+ return String.format("Open in %s", remoteHost.toString());
+ }
+
+ UrlHandler remoteUrlHandler()
+ {
+ return container().openInBrowserHandler();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Menu/ClipboardMenuAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/ClipboardMenuAction.java
new file mode 100644
index 0000000..0ed26a7
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/ClipboardMenuAction.java
@@ -0,0 +1,17 @@
+package uk.co.ben_gibson.git.link.UI.Action.Menu;
+
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+
+public class ClipboardMenuAction extends MenuAction
+{
+ protected String displayName(RemoteHost remoteHost)
+ {
+ return String.format("Copy %s link to clipboard", remoteHost.toString());
+ }
+
+ UrlHandler remoteUrlHandler()
+ {
+ return container().copyToClipboardHandler();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Menu/MenuAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/MenuAction.java
new file mode 100644
index 0000000..2b80d4c
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Menu/MenuAction.java
@@ -0,0 +1,32 @@
+package uk.co.ben_gibson.git.link.UI.Action.Menu;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+import uk.co.ben_gibson.git.link.UI.Action.Action;
+
+/**
+ * An action triggered from the view or right click menu.
+ */
+abstract class MenuAction extends Action
+{
+ abstract UrlHandler remoteUrlHandler();
+
+ public void actionPerformed(Project project, AnActionEvent event)
+ {
+ VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE);
+
+ if (file == null || project == null) {
+ return;
+ }
+
+ this.container().runner().runForFile(project, file, this.remoteUrlHandler());
+ }
+
+ protected boolean shouldActionBeEnabled(AnActionEvent event)
+ {
+ return (event.getData(CommonDataKeys.VIRTUAL_FILE) != null);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/BrowserVcsAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/BrowserVcsAction.java
new file mode 100644
index 0000000..e552ff9
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/BrowserVcsAction.java
@@ -0,0 +1,17 @@
+package uk.co.ben_gibson.git.link.UI.Action.Vcs;
+
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+
+public class BrowserVcsAction extends VcsLogAction
+{
+ protected String displayName(RemoteHost remoteHost)
+ {
+ return String.format("Open commit in %s", remoteHost.toString());
+ }
+
+ UrlHandler remoteUrlHandler()
+ {
+ return container().openInBrowserHandler();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/ClipboardVcsAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/ClipboardVcsAction.java
new file mode 100644
index 0000000..53c578c
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/ClipboardVcsAction.java
@@ -0,0 +1,17 @@
+package uk.co.ben_gibson.git.link.UI.Action.Vcs;
+
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+
+public class ClipboardVcsAction extends VcsLogAction
+{
+ protected String displayName(RemoteHost remoteHost)
+ {
+ return String.format("Copy %s commit link to clipboard", remoteHost.toString());
+ }
+
+ UrlHandler remoteUrlHandler()
+ {
+ return container().copyToClipboardHandler();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/VcsLogAction.java b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/VcsLogAction.java
new file mode 100644
index 0000000..b4a0c1f
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Action/Vcs/VcsLogAction.java
@@ -0,0 +1,44 @@
+package uk.co.ben_gibson.git.link.UI.Action.Vcs;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.vcs.log.VcsFullCommitDetails;
+import com.intellij.vcs.log.VcsLog;
+import com.intellij.vcs.log.VcsLogDataKeys;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+import uk.co.ben_gibson.git.link.UI.Action.Action;
+import java.util.List;
+
+/**
+ * An action triggered from an VCS log toolbar.
+ */
+abstract class VcsLogAction extends Action
+{
+ abstract UrlHandler remoteUrlHandler();
+
+ public void actionPerformed(Project project, AnActionEvent event)
+ {
+ VcsLog vcsLog = event.getData(VcsLogDataKeys.VCS_LOG);
+
+ if (vcsLog == null) {
+ return;
+ }
+
+ VcsFullCommitDetails commit = vcsLog.getSelectedDetails().get(0);
+
+ this.container().runner().runForCommit(project, commit, this.remoteUrlHandler());
+ }
+
+ protected boolean shouldActionBeEnabled(AnActionEvent event)
+ {
+ VcsLog log = event.getData(VcsLogDataKeys.VCS_LOG);
+
+ if (log == null) {
+ return false;
+ }
+
+ List commits = log.getSelectedDetails();
+
+ return commits.size() == 1;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Settings/ConfigurableSettings.java b/src/uk/co/ben_gibson/git/link/UI/Settings/ConfigurableSettings.java
new file mode 100644
index 0000000..1c252c5
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Settings/ConfigurableSettings.java
@@ -0,0 +1,66 @@
+package uk.co.ben_gibson.git.link.UI.Settings;
+
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import uk.co.ben_gibson.git.link.Container;
+import javax.swing.*;
+
+public class ConfigurableSettings implements Configurable
+{
+ private final Container container;
+ private final Project project;
+ private uk.co.ben_gibson.git.link.UI.Settings.Settings ui;
+
+ public ConfigurableSettings(Project project, Container container)
+ {
+ this.container = container;
+ this.project = project;
+ }
+
+ public JComponent createComponent()
+ {
+ this.ui = new uk.co.ben_gibson.git.link.UI.Settings.Settings(
+ this.container.preferences(this.project),
+ this.container.urlModifiers(),
+ this.container.plugin()
+ );
+
+ return ui.getRootPanel();
+ }
+
+ @Override
+ public void disposeUIResources()
+ {
+ this.ui = null;
+ }
+
+ public String getHelpTopic()
+ {
+ return this.container.plugin().displayName();
+ }
+
+ public String getDisplayName()
+ {
+ return this.container.plugin().displayName();
+ }
+
+ public boolean isModified()
+ {
+ return this.ui.isModified();
+ }
+
+ public void apply() throws ConfigurationException
+ {
+ this.ui.apply();
+
+ // We need to flush the container when the plugins configuration has changed as many project level services
+ // are constructed with values derived from the settings.
+ this.container.flushProjectCache(this.project);
+ }
+
+ public void reset()
+ {
+ this.ui.reset();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.form b/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.form
new file mode 100644
index 0000000..9847e68
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.form
@@ -0,0 +1,243 @@
+
+
diff --git a/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.java b/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.java
new file mode 100644
index 0000000..8bbd693
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Settings/Settings.java
@@ -0,0 +1,249 @@
+package uk.co.ben_gibson.git.link.UI.Settings;
+
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.ui.EnumComboBoxModel;
+import com.intellij.ui.Gray;
+import com.intellij.ui.components.JBCheckBox;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.Spacer;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Plugin;
+import uk.co.ben_gibson.git.link.Preferences;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Modifier.UrlModifier;
+import javax.swing.*;
+import java.awt.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Settings
+{
+ private JPanel rootPanel;
+ private JComboBox hostSelect;
+ private JTextField defaultBranchTextField;
+ private JTextField customFileUrlTemplateTextField;
+ private JTextField customCommitUrlTemplateTextField;
+ private JPanel customURLPanel;
+ private JCheckBox verboseLoggingCheckBox;
+ private JLabel customFileUrlLabel;
+ private JLabel customCommitUrlLabel;
+ private JLabel projectSettingsLabel;
+ private JLabel customUrlLabel;
+ private JPanel urlModifierCheckBoxPanel;
+ private JLabel featureRequestLabel;
+ private JLabel pluginDetailsLabel;
+ private Preferences preferences;
+ private Map urlModifierCheckBoxes = new HashMap<>();
+
+ public Settings(Preferences preferences, List urlModifiers, Plugin plugin)
+ {
+ this.preferences = preferences;
+
+ $$$setupUI$$$();
+ this.hostSelect.setModel(new EnumComboBoxModel<>(RemoteHost.class));
+ this.defaultBranchTextField.setText(this.preferences.getDefaultBranch().toString());
+ this.customURLPanel.setVisible(this.preferences.getRemoteHost().custom());
+
+ this.applyLabelHelpTextStlye(this.customFileUrlLabel);
+ this.applyLabelHelpTextStlye(this.customCommitUrlLabel);
+ this.applyLabelHeadingStlye(this.projectSettingsLabel);
+ this.applyLabelHeadingStlye(this.customUrlLabel);
+
+ this.hostSelect.addActionListener(e -> {
+ RemoteHost host = (((RemoteHost) hostSelect.getSelectedItem()));
+ Settings.this.customURLPanel.setVisible((host != null && host.custom()));
+ });
+
+ for (UrlModifier modifier : urlModifiers) {
+ JBCheckBox checkBox = new JBCheckBox(modifier.name());
+ this.urlModifierCheckBoxes.put(modifier, checkBox);
+ this.urlModifierCheckBoxPanel.add(checkBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ }
+
+ this.pluginDetailsLabel.setText(plugin.toString());
+ this.featureRequestLabel.setText(String.format("Submit feature requests and bug reports to %s", plugin.issueTracker()));
+ this.applyLabelHelpTextStlye(this.pluginDetailsLabel);
+ this.applyLabelHelpTextStlye(this.featureRequestLabel);
+
+ }
+
+ public JPanel getRootPanel()
+ {
+ return rootPanel;
+ }
+
+ public boolean isModified()
+ {
+ for (Map.Entry entry : this.urlModifierCheckBoxes.entrySet()) {
+ if (entry.getValue().isSelected() != this.preferences.isModifierEnabled(entry.getKey())) {
+ return true;
+ }
+ }
+
+ return
+ this.verboseLoggingCheckBox.isSelected() != this.preferences.getEnableVerboseEventLog() ||
+ this.hostSelect.getSelectedItem() != this.preferences.getRemoteHost() ||
+ !this.preferences.getDefaultBranch().equals(new Branch(this.defaultBranchTextField.getText())) ||
+ !this.preferences.getCustomFileUrlTemplate().equals(this.customFileUrlTemplateTextField.getText()) ||
+ !this.preferences.getCustomCommitUrlTemplate().equals(this.customCommitUrlTemplateTextField.getText());
+ }
+
+ public void apply() throws ConfigurationException
+ {
+ RemoteHost remoteHost = (RemoteHost) this.hostSelect.getSelectedItem();
+
+ if (remoteHost != null && remoteHost.custom()) {
+
+ try {
+ URL url = new URL(this.customFileUrlTemplateTextField.getText());
+ this.preferences.setCustomFileUrlTemplate(url.toString());
+ } catch (MalformedURLException exception) {
+ throw new ConfigurationException("Invalid URL provided for the custom file URL");
+ }
+
+ try {
+ URL url = new URL(this.customCommitUrlTemplateTextField.getText());
+ this.preferences.setCustomCommitUrlTemplate(url.toString());
+ } catch (MalformedURLException exception) {
+ throw new ConfigurationException("Invalid URL provided for the custom commit URL");
+ }
+ }
+
+ if (this.defaultBranchTextField.getText().isEmpty()) {
+ throw new ConfigurationException("Default branch is required");
+ }
+
+ this.preferences.setDefaultBranch(new Branch(this.defaultBranchTextField.getText()));
+ this.preferences.setEnableVerboseEventLog(this.verboseLoggingCheckBox.isSelected());
+ this.preferences.setRemoteHost(remoteHost);
+
+ for (Map.Entry entry : this.urlModifierCheckBoxes.entrySet()) {
+ if (entry.getValue().isSelected()) {
+ this.preferences.enableModifier(entry.getKey());
+ } else {
+ this.preferences.disableModifier(entry.getKey());
+ }
+ }
+ }
+
+ public void reset()
+ {
+ this.defaultBranchTextField.setText(this.preferences.getDefaultBranch().toString());
+ this.verboseLoggingCheckBox.setSelected(this.preferences.getEnableVerboseEventLog());
+ this.hostSelect.setSelectedItem(this.preferences.getRemoteHost());
+ this.customFileUrlTemplateTextField.setText(this.preferences.getCustomFileUrlTemplate());
+ this.customCommitUrlTemplateTextField.setText(this.preferences.getCustomCommitUrlTemplate());
+
+ for (Map.Entry entry : this.urlModifierCheckBoxes.entrySet()) {
+ entry.getValue().setSelected(this.preferences.isModifierEnabled(entry.getKey()));
+ }
+ }
+
+ private void applyLabelHeadingStlye(JLabel label)
+ {
+ label.setFont(new Font(null, Font.PLAIN, 12));
+ label.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Gray._200));
+ }
+
+ private void applyLabelHelpTextStlye(JLabel label)
+ {
+ label.setFont(new Font(null, Font.ITALIC, 11));
+ }
+
+ private void createUIComponents()
+ {
+ this.urlModifierCheckBoxPanel = new JPanel();
+ this.urlModifierCheckBoxPanel.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1));
+ }
+
+ /**
+ * Method generated by IntelliJ IDEA GUI Designer
+ * >>> IMPORTANT!! <<<
+ * DO NOT edit this method OR call it in your code!
+ *
+ * @noinspection ALL
+ */
+ private void $$$setupUI$$$()
+ {
+ createUIComponents();
+ rootPanel = new JPanel();
+ rootPanel.setLayout(new FormLayout("fill:d:grow", "center:max(d;4px):noGrow,top:3dlu:noGrow,center:d:noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:max(d;4px):noGrow,top:3dlu:noGrow,center:d:grow"));
+ final JPanel panel1 = new JPanel();
+ panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
+ CellConstraints cc = new CellConstraints();
+ rootPanel.add(panel1, cc.xy(1, 3));
+ final JLabel label1 = new JLabel();
+ label1.setText("Hosts");
+ panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(100, -1), null, null, 0, false));
+ hostSelect = new JComboBox();
+ panel1.add(hostSelect, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ customURLPanel = new JPanel();
+ customURLPanel.setLayout(new GridLayoutManager(6, 2, new Insets(0, 0, 0, 0), -1, -1));
+ customURLPanel.setVisible(true);
+ rootPanel.add(customURLPanel, cc.xy(1, 11));
+ final JLabel label2 = new JLabel();
+ label2.setText("File");
+ customURLPanel.add(label2, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(100, -1), null, null, 0, false));
+ customFileUrlTemplateTextField = new JTextField();
+ customURLPanel.add(customFileUrlTemplateTextField, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ customFileUrlLabel = new JLabel();
+ customFileUrlLabel.setHorizontalAlignment(10);
+ customFileUrlLabel.setHorizontalTextPosition(11);
+ customFileUrlLabel.setText("e.g. https://example.com/{repository}/blob/{branch}#{line} ");
+ customFileUrlLabel.setVerticalTextPosition(0);
+ customFileUrlLabel.setVisible(true);
+ customURLPanel.add(customFileUrlLabel, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_NORTHEAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JLabel label3 = new JLabel();
+ label3.setText("Commit");
+ customURLPanel.add(label3, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ customCommitUrlTemplateTextField = new JTextField();
+ customURLPanel.add(customCommitUrlTemplateTextField, new GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ customCommitUrlLabel = new JLabel();
+ customCommitUrlLabel.setText("e.g. https://example.com/{repository}/{commit}#{line} ");
+ customURLPanel.add(customCommitUrlLabel, new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_NORTHEAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final Spacer spacer1 = new Spacer();
+ customURLPanel.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 10), null, null, 0, false));
+ customUrlLabel = new JLabel();
+ customUrlLabel.setText("Custom URL");
+ customURLPanel.add(customUrlLabel, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JPanel panel2 = new JPanel();
+ panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
+ rootPanel.add(panel2, cc.xy(1, 5));
+ final JLabel label4 = new JLabel();
+ label4.setText("Default Branch");
+ panel2.add(label4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(100, -1), null, null, 0, false));
+ defaultBranchTextField = new JTextField();
+ panel2.add(defaultBranchTextField, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
+ final JPanel panel3 = new JPanel();
+ panel3.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1));
+ rootPanel.add(panel3, cc.xy(1, 7));
+ verboseLoggingCheckBox = new JCheckBox();
+ verboseLoggingCheckBox.setText("Enable verbose logging");
+ panel3.add(verboseLoggingCheckBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final Spacer spacer2 = new Spacer();
+ panel3.add(spacer2, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
+ final Spacer spacer3 = new Spacer();
+ panel3.add(spacer3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 10), null, null, 0, false));
+ final Spacer spacer4 = new Spacer();
+ rootPanel.add(spacer4, cc.xy(1, 13, CellConstraints.DEFAULT, CellConstraints.FILL));
+ projectSettingsLabel = new JLabel();
+ projectSettingsLabel.setText("Project Settings");
+ projectSettingsLabel.setVerticalAlignment(0);
+ rootPanel.add(projectSettingsLabel, cc.xy(1, 1, CellConstraints.DEFAULT, CellConstraints.CENTER));
+ rootPanel.add(urlModifierCheckBoxPanel, cc.xy(1, 9));
+ }
+
+ /**
+ * @noinspection ALL
+ */
+ public JComponent $$$getRootComponent$$$()
+ {
+ return rootPanel;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/UI/Target/BrowserSelectInTarget.java b/src/uk/co/ben_gibson/git/link/UI/Target/BrowserSelectInTarget.java
new file mode 100644
index 0000000..842895e
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/UI/Target/BrowserSelectInTarget.java
@@ -0,0 +1,49 @@
+package uk.co.ben_gibson.git.link.UI.Target;
+
+import com.intellij.ide.SelectInContext;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import uk.co.ben_gibson.git.link.Container;
+
+public class BrowserSelectInTarget implements com.intellij.ide.SelectInTarget
+{
+ public boolean canSelect(SelectInContext context)
+ {
+ return true;
+ }
+
+ public void selectIn(SelectInContext context, boolean requestFocus)
+ {
+ Container container = this.container();
+ Project project = context.getProject();
+ VirtualFile file = context.getVirtualFile();
+
+ container.runner().runForFile(project, file, this.container().openInBrowserHandler());
+ }
+
+ protected Container container()
+ {
+ return ServiceManager.getService(Container.class);
+ }
+
+ public String getToolWindowId()
+ {
+ return null;
+ }
+
+ public String getMinorViewId()
+ {
+ return null;
+ }
+
+ public float getWeight()
+ {
+ return 0;
+ }
+
+ public String toString()
+ {
+ return "Browser (GitLink)";
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/AbstractUrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/AbstractUrlFactory.java
new file mode 100644
index 0000000..09c5546
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/AbstractUrlFactory.java
@@ -0,0 +1,40 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+abstract class AbstractUrlFactory implements UrlFactory
+{
+ URL buildURL(Remote remote, String path, String query, String fragment) throws UrlFactoryException, RemoteException
+ {
+ URL host = remote.url();
+
+ try {
+
+ URI uri = new URI(host.getProtocol(), host.getHost(), path, query, fragment);
+
+ return uri.toURL();
+
+ } catch (URISyntaxException | MalformedURLException e) {
+ throw UrlFactoryException.cannotCreateUrl(e.getMessage());
+ }
+ }
+
+ String cleanPath(String path)
+ {
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+
+ if (path.endsWith("/")) {
+ path = path.substring(0, (path.length() - 1));
+ }
+
+ return path;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/BitBucketUrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/BitBucketUrlFactory.java
new file mode 100644
index 0000000..c14a55c
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/BitBucketUrlFactory.java
@@ -0,0 +1,42 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.File;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.URL;
+
+public class BitBucketUrlFactory extends AbstractUrlFactory
+{
+ public URL createUrl(CommitDescription description) throws UrlFactoryException, RemoteException
+ {
+ Remote remote = description.remote();
+
+ String path = String.format("/%s/commits/%s", this.cleanPath(remote.url().getPath()), description.commitHash());
+
+ return this.buildURL(remote, path, null, null);
+ }
+
+ public URL createUrl(FileDescription description) throws UrlFactoryException, RemoteException
+ {
+ Remote remote = description.remote();
+ File file = description.file();
+ String path = String.format("/%s/src/HEAD/%s", this.cleanPath(remote.url().getPath()), this.cleanPath(file.pathWithName()));
+ String query = String.format("at=%s", description.branch());
+ String fragment = null;
+
+ if (description.hasLineNumber()) {
+ fragment = String.format("%s-%s", file.name(), description.lineNumber());
+ }
+
+ return this.buildURL(remote, path, query, fragment);
+ }
+
+ public boolean supports(RemoteHost host)
+ {
+ return host.bitbucket();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/CustomUrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/CustomUrlFactory.java
new file mode 100644
index 0000000..c1e9500
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/CustomUrlFactory.java
@@ -0,0 +1,51 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class CustomUrlFactory extends AbstractUrlFactory
+{
+ private String fileUrlTemplate;
+ private String commitUrlTemplate;
+
+ public CustomUrlFactory(String fileUrlTemplate, String commitUrlTemplate)
+ {
+ this.fileUrlTemplate = fileUrlTemplate;
+ this.commitUrlTemplate = commitUrlTemplate;
+ }
+
+ public URL createUrl(CommitDescription description) throws UrlFactoryException, RemoteException
+ {
+ String url = this.commitUrlTemplate.replace("{commit}", description.commitHash());
+
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ throw UrlFactoryException.cannotCreateUrl(String.format("Custom url '%s' is invalid.", url));
+ }
+ }
+
+ public URL createUrl(FileDescription description) throws UrlFactoryException, RemoteException
+ {
+ String url = this.fileUrlTemplate.replace("{branch}", description.branch().toString());
+ url = url.replace("{filePath}", this.cleanPath(description.file().path()));
+ url = url.replace("{fileName}", description.file().name());
+ url = url.replace("{line}", description.hasLineNumber() ? description.lineNumber().toString() : "");
+
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ throw UrlFactoryException.cannotCreateUrl(String.format("Custom url '%s' is invalid.", url));
+ }
+ }
+
+ public boolean supports(RemoteHost host)
+ {
+ return host.custom();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/Description/CommitDescription.java b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/CommitDescription.java
new file mode 100644
index 0000000..6d271f9
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/CommitDescription.java
@@ -0,0 +1,24 @@
+package uk.co.ben_gibson.git.link.Url.Factory.Description;
+
+import uk.co.ben_gibson.git.link.Git.Commit;
+import uk.co.ben_gibson.git.link.Git.Remote;
+
+/**
+ * Describes a commit that can be used to create a URL.
+ */
+public class CommitDescription extends GitDescription
+{
+ private Commit commit;
+
+ public CommitDescription(Remote remote, Commit commit)
+ {
+ super(remote);
+
+ this.commit = commit;
+ }
+
+ public String commitHash()
+ {
+ return this.commit.hash();
+ }
+}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteFileDescription.java b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/FileDescription.java
similarity index 54%
rename from src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteFileDescription.java
rename to src/uk/co/ben_gibson/git/link/Url/Factory/Description/FileDescription.java
index fb9adc0..c07f5e7 100644
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteFileDescription.java
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/FileDescription.java
@@ -1,19 +1,19 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description;
+package uk.co.ben_gibson.git.link.Url.Factory.Description;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.File;
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.File;
/**
- * Describes a remote file that a URL can be created to.
+ * Describes a file that can be used to create a URL.
*/
-public class RemoteFileDescription extends RemoteDescription
+public class FileDescription extends GitDescription
{
private Branch branch;
private File file;
private Integer lineNumber;
- public RemoteFileDescription(Remote remote, Branch branch, File file, Integer lineNumber)
+ public FileDescription(Remote remote, Branch branch, File file, Integer lineNumber)
{
super(remote);
@@ -22,7 +22,6 @@ public RemoteFileDescription(Remote remote, Branch branch, File file, Integer li
this.lineNumber = lineNumber;
}
-
public Branch branch()
{
return this.branch;
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/Description/GitDescription.java b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/GitDescription.java
new file mode 100644
index 0000000..47bee36
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/Description/GitDescription.java
@@ -0,0 +1,18 @@
+package uk.co.ben_gibson.git.link.Url.Factory.Description;
+
+import uk.co.ben_gibson.git.link.Git.Remote;
+
+abstract public class GitDescription
+{
+ private Remote remote;
+
+ GitDescription(Remote remote)
+ {
+ this.remote = remote;
+ }
+
+ public Remote remote()
+ {
+ return remote;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/Exception/UrlFactoryException.java b/src/uk/co/ben_gibson/git/link/Url/Factory/Exception/UrlFactoryException.java
new file mode 100644
index 0000000..4069905
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/Exception/UrlFactoryException.java
@@ -0,0 +1,22 @@
+package uk.co.ben_gibson.git.link.Url.Factory.Exception;
+
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+
+public class UrlFactoryException extends GitLinkException
+{
+ private UrlFactoryException(String message)
+ {
+ super(message);
+ }
+
+ public static UrlFactoryException unsupportedRemoteHost(RemoteHost host)
+ {
+ return new UrlFactoryException(String.format("The remote host '%s' is not supported", host.name()));
+ }
+
+ public static UrlFactoryException cannotCreateUrl(String reason)
+ {
+ return new UrlFactoryException(String.format("Cannot create url (%s)", reason));
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/GitHubUrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/GitHubUrlFactory.java
new file mode 100644
index 0000000..7b45018
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/GitHubUrlFactory.java
@@ -0,0 +1,40 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.URL;
+
+public class GitHubUrlFactory extends AbstractUrlFactory
+{
+ public URL createUrl(CommitDescription description) throws UrlFactoryException, RemoteException
+ {
+ Remote remote = description.remote();
+
+ String path = String.format("/%s/commit/%s", this.cleanPath(remote.url().getPath()), description.commitHash());
+
+ return this.buildURL(remote, path, null, null);
+ }
+
+ public URL createUrl(FileDescription description) throws UrlFactoryException, RemoteException
+ {
+ Remote remote = description.remote();
+
+ String path = String.format("/%s/blob/%s/%s", this.cleanPath(remote.url().getPath()), description.branch(), this.cleanPath(description.file().pathWithName()));
+ String fragment = null;
+
+ if (description.hasLineNumber()) {
+ fragment = String.format("L%d", description.lineNumber());
+ }
+
+ return this.buildURL(remote, path, null, fragment);
+ }
+
+ public boolean supports(RemoteHost host)
+ {
+ return host.gitHub() || host.gitLab();
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/StashUrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/StashUrlFactory.java
new file mode 100644
index 0000000..7de189e
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/StashUrlFactory.java
@@ -0,0 +1,62 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.URL;
+
+public class StashUrlFactory extends AbstractUrlFactory
+{
+ public URL createUrl(CommitDescription description) throws UrlFactoryException, RemoteException
+ {
+ Remote remote = description.remote();
+
+ String[] parts = this.getParts(remote.url());
+
+ String project = parts[1];
+ String repository = parts[2];
+
+ String path = String.format("/projects/%s/repos/%s/commits/%s", project, repository, description.commitHash());
+
+ return this.buildURL(remote, path, null, null);
+ }
+
+ public URL createUrl(FileDescription description) throws UrlFactoryException, RemoteException
+ {
+ String[] parts = this.getParts(description.remote().url());
+
+ String project = parts[1];
+ String repository = parts[2];
+
+ String path = String.format("/projects/%s/repos/%s/browse/%s", project, repository, description.file().pathWithName());
+ String query = String.format("at=refs/heads/%s", description.branch());
+ String fragment = null;
+
+ if (description.hasLineNumber()) {
+ fragment = description.lineNumber().toString();
+ }
+
+ return this.buildURL(description.remote(), path, query, fragment);
+ }
+
+ public boolean supports(RemoteHost host)
+ {
+ return host.stash();
+ }
+
+ private String[] getParts(URL url) throws UrlFactoryException
+ {
+ String[] parts = url.getPath().split("/", 3);
+
+ if (parts.length < 3) {
+ throw UrlFactoryException.cannotCreateUrl(
+ String.format("Could not determine Stash project or repository from URL '%s'", url)
+ );
+ }
+
+ return parts;
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactory.java b/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactory.java
new file mode 100644
index 0000000..478dd01
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactory.java
@@ -0,0 +1,15 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.net.URL;
+
+public interface UrlFactory
+{
+ URL createUrl(CommitDescription description) throws UrlFactoryException, RemoteException;
+ URL createUrl(FileDescription description) throws UrlFactoryException, RemoteException;
+ boolean supports(RemoteHost host);
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactoryProvider.java b/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactoryProvider.java
new file mode 100644
index 0000000..17b2427
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Factory/UrlFactoryProvider.java
@@ -0,0 +1,30 @@
+package uk.co.ben_gibson.git.link.Url.Factory;
+
+import uk.co.ben_gibson.git.link.Git.RemoteHost;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides a URL factory for a given remote git host.
+ */
+public class UrlFactoryProvider
+{
+ private List factories = new ArrayList();
+
+ public void registerFactory(UrlFactory factory)
+ {
+ this.factories.add(factory);
+ }
+
+ public UrlFactory urlFactory(RemoteHost host) throws UrlFactoryException
+ {
+ for (UrlFactory factory : this.factories) {
+ if (factory.supports(host)) {
+ return factory;
+ }
+ }
+
+ throw UrlFactoryException.unsupportedRemoteHost(host);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Handler/CopyToClipboardHandler.java b/src/uk/co/ben_gibson/git/link/Url/Handler/CopyToClipboardHandler.java
new file mode 100644
index 0000000..afb5213
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Handler/CopyToClipboardHandler.java
@@ -0,0 +1,28 @@
+package uk.co.ben_gibson.git.link.Url.Handler;
+
+import java.awt.*;
+import java.awt.datatransfer.StringSelection;
+import java.net.URL;
+
+/**
+ * Copies a URL to the clipboard.
+ */
+public class CopyToClipboardHandler implements UrlHandler
+{
+ private Toolkit toolkit;
+
+ public CopyToClipboardHandler(Toolkit toolkit)
+ {
+ this.toolkit = toolkit;
+ }
+
+ public void handle(URL url)
+ {
+ this.toolkit.getSystemClipboard().setContents(new StringSelection(url.toString()), null);
+ }
+
+ public String name()
+ {
+ return "Copy to clipboard";
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Handler/Exception/UrlHandlerException.java b/src/uk/co/ben_gibson/git/link/Url/Handler/Exception/UrlHandlerException.java
new file mode 100644
index 0000000..27bbcc6
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Handler/Exception/UrlHandlerException.java
@@ -0,0 +1,14 @@
+package uk.co.ben_gibson.git.link.Url.Handler.Exception;
+
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+
+/**
+ * Thrown when a handler cannot handle the URL.
+ */
+public class UrlHandlerException extends GitLinkException
+{
+ public UrlHandlerException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Handler/OpenInBrowserHandler.java b/src/uk/co/ben_gibson/git/link/Url/Handler/OpenInBrowserHandler.java
new file mode 100644
index 0000000..484fa61
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Handler/OpenInBrowserHandler.java
@@ -0,0 +1,32 @@
+package uk.co.ben_gibson.git.link.Url.Handler;
+
+import com.intellij.ide.browsers.BrowserLauncher;
+import uk.co.ben_gibson.git.link.Url.Handler.Exception.UrlHandlerException;
+import java.net.URL;
+
+/**
+ * Opens a URL in the default browser.
+ */
+public class OpenInBrowserHandler implements UrlHandler
+{
+ private BrowserLauncher browserLauncher;
+
+ public OpenInBrowserHandler(BrowserLauncher browserLauncher)
+ {
+ this.browserLauncher = browserLauncher;
+ }
+
+ public void handle(URL url) throws UrlHandlerException
+ {
+ try {
+ this.browserLauncher.open(url.toURI().toASCIIString());
+ } catch (Exception e) {
+ throw new UrlHandlerException(e.getMessage());
+ }
+ }
+
+ public String name()
+ {
+ return "Open in browser";
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Handler/UrlHandler.java b/src/uk/co/ben_gibson/git/link/Url/Handler/UrlHandler.java
new file mode 100644
index 0000000..c41318c
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Handler/UrlHandler.java
@@ -0,0 +1,14 @@
+package uk.co.ben_gibson.git.link.Url.Handler;
+
+import uk.co.ben_gibson.git.link.Url.Handler.Exception.UrlHandlerException;
+import java.net.URL;
+
+/**
+ * Handles a URL in some way e.g. copies it the clipboard.
+ */
+public interface UrlHandler
+{
+ void handle(URL url) throws UrlHandlerException;
+
+ String name();
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Modifier/Exception/ModifierException.java b/src/uk/co/ben_gibson/git/link/Url/Modifier/Exception/ModifierException.java
new file mode 100644
index 0000000..14af051
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Modifier/Exception/ModifierException.java
@@ -0,0 +1,11 @@
+package uk.co.ben_gibson.git.link.Url.Modifier.Exception;
+
+import uk.co.ben_gibson.git.link.Exception.GitLinkException;
+
+public class ModifierException extends GitLinkException
+{
+ public ModifierException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Modifier/HttpsUrlModifier.java b/src/uk/co/ben_gibson/git/link/Url/Modifier/HttpsUrlModifier.java
new file mode 100644
index 0000000..7c9d6ce
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Modifier/HttpsUrlModifier.java
@@ -0,0 +1,38 @@
+package uk.co.ben_gibson.git.link.Url.Modifier;
+
+import uk.co.ben_gibson.git.link.Url.Modifier.Exception.ModifierException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * Modifies a URL by forcing the protocol to use HTTPS.
+ */
+public class HttpsUrlModifier implements UrlModifier
+{
+ public URL modify(URL url) throws ModifierException
+ {
+ if (url.getProtocol().equals("https")) {
+ return url;
+ }
+
+ try {
+
+ String file = url.getFile();
+
+ if (url.getRef() != null) {
+ file = file.concat("#" + url.getRef());
+ }
+
+ return new URL("https", url.getHost(), url.getPort(), file);
+ } catch (MalformedURLException e) {
+ throw new ModifierException(e.getMessage());
+ }
+ }
+
+ public String name()
+ {
+ return "Force HTTPS";
+ }
+}
diff --git a/src/uk/co/ben_gibson/git/link/Url/Modifier/UrlModifier.java b/src/uk/co/ben_gibson/git/link/Url/Modifier/UrlModifier.java
new file mode 100644
index 0000000..34498b2
--- /dev/null
+++ b/src/uk/co/ben_gibson/git/link/Url/Modifier/UrlModifier.java
@@ -0,0 +1,14 @@
+package uk.co.ben_gibson.git.link.Url.Modifier;
+
+import uk.co.ben_gibson.git.link.Url.Modifier.Exception.ModifierException;
+import java.net.URL;
+
+/**
+ * Modify a URL in some way.
+ */
+public interface UrlModifier
+{
+ URL modify(URL url) throws ModifierException;
+
+ String name();
+}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Configuration.java b/src/uk/co/ben_gibson/open/in/git/host/Configuration.java
deleted file mode 100644
index c09df14..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Configuration.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host;
-
-import com.intellij.openapi.options.Configurable;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.ComboBox;
-import com.intellij.ui.EnumComboBoxModel;
-import com.intellij.ui.components.JBCheckBox;
-import com.intellij.ui.components.JBLabel;
-import uk.co.ben_gibson.open.in.git.host.Extension.Extension;
-import uk.co.ben_gibson.open.in.git.host.Git.RemoteHost;
-import com.intellij.openapi.options.ConfigurationException;
-import javax.swing.*;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class Configuration implements Configurable
-{
- private static final String LABEL_HOSTS = "Hosts";
- private static final String FORCE_SSL = "Force SSL";
- private static final String EVENT_LOGGING = "Enable verbose event logging";
- private static final String LABEL_EXTENSIONS = "Extensions";
- private static final String LABEL_OPTIONS = "Options";
-
- private Settings settings;
-
- private JBCheckBox enableVerboseEventLogging;
- private JBCheckBox forceSSLCheckBox;
- private ComboBox hostsComboBox;
- private Container container;
-
- private Map extensionCheckBoxes = new HashMap<>();
-
- /**
- * Needs cleaning up - dumping ground for preferences UI.
- *
- * Now idea how to inject the dependencies as this is wired up in the plugin.xml and auto-magically created :(
- */
- public Configuration(Project project, Container container)
- {
- this.container = container;
- this.settings = container.settings(project);
-
- this.enableVerboseEventLogging = new JBCheckBox(EVENT_LOGGING);
- this.forceSSLCheckBox = new JBCheckBox(FORCE_SSL);
- this.hostsComboBox = new ComboBox(new EnumComboBoxModel<>(RemoteHost.class), 200);
-
- for (Extension extension: this.container.registeredExtensions()) {
- extensionCheckBoxes.put(extension, new JBCheckBox(extension.displayName()));
- }
- }
-
- public JComponent createComponent()
- {
- this.reset();
-
- JPanel panel = new JPanel();
-
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
-
- this.hostsComboBox.setAlignmentX(0.0f);
-
- this.addToPanel(panel, new JBLabel(LABEL_HOSTS));
- this.addToPanel(panel, this.hostsComboBox);
- this.addSpacing(panel);
- this.addToPanel(panel, new JBLabel(LABEL_EXTENSIONS));
-
- for (JCheckBox extensionCheckBox : this.extensionCheckBoxes.values()) {
- this.addToPanel(panel, extensionCheckBox);
- }
-
- this.addSpacing(panel);
-
- this.addToPanel(panel, new JBLabel(LABEL_OPTIONS));
- this.addToPanel(panel, this.enableVerboseEventLogging);
- this.addToPanel(panel, this.forceSSLCheckBox);
-
- return panel;
- }
-
- public boolean isModified()
- {
- for (Map.Entry entry : this.extensionCheckBoxes.entrySet()) {
- Extension extension = entry.getKey();
- JBCheckBox checkBox = entry.getValue();
-
- if (checkBox.isSelected() != this.settings.isExtensionEnabled(extension)) {
- return true;
- }
- }
-
- return
- this.forceSSLCheckBox.isSelected() != this.settings.getForceSSL() ||
- this.enableVerboseEventLogging.isSelected() != this.settings.getEnableVerboseEventLog() ||
- this.hostsComboBox.getSelectedItem() != this.settings.getRemoteHost();
- }
-
- public void apply() throws ConfigurationException
- {
- this.settings.setForceSSL(this.forceSSLCheckBox.isSelected());
- this.settings.setEnableVerboseEventLog(this.enableVerboseEventLogging.isSelected());
- this.settings.setRemoteHost((RemoteHost) this.hostsComboBox.getSelectedItem());
-
- List enabledExtensions = new ArrayList<>();
-
- for (Map.Entry entry : this.extensionCheckBoxes.entrySet()) {
- Extension extension = entry.getKey();
- JBCheckBox checkBox = entry.getValue();
-
- if (checkBox.isSelected()) {
- enabledExtensions.add(extension.getClass().getName());
- }
- }
-
- this.settings.setEnabledExtensions(enabledExtensions);
- }
-
- public void reset()
- {
- this.enableVerboseEventLogging.setSelected(this.settings.getEnableVerboseEventLog());
- this.forceSSLCheckBox.setSelected(this.settings.getForceSSL());
- this.hostsComboBox.setSelectedItem(this.settings.getRemoteHost());
-
- for (Map.Entry entry : this.extensionCheckBoxes.entrySet()) {
-
- Extension extension = entry.getKey();
- JBCheckBox checkBox = entry.getValue();
-
- checkBox.setSelected(this.settings.isExtensionEnabled(extension));
- }
- }
-
- public void disposeUIResources()
- {
- this.enableVerboseEventLogging = null;
- this.forceSSLCheckBox = null;
- this.hostsComboBox = null;
- this.extensionCheckBoxes = null;
- }
-
- public String getHelpTopic()
- {
- return "Open In Git Host";
- }
-
- public String getDisplayName()
- {
- return this.container.plugin().displayName();
- }
-
- private void addSpacing(JPanel panel)
- {
- JPanel spacing = new JPanel();
-
- spacing.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
-
- this.addToPanel(panel, spacing);
- }
-
- private void addToPanel(JPanel panel, JComponent component)
- {
- component.setMaximumSize(component.getPreferredSize());
-
- panel.add(component);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Container.java b/src/uk/co/ben_gibson/open/in/git/host/Container.java
deleted file mode 100644
index e3b5889..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Container.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host;
-
-import com.intellij.ide.browsers.BrowserLauncher;
-import com.intellij.ide.plugins.PluginManager;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.extensions.PluginId;
-import com.intellij.openapi.project.Project;
-import uk.co.ben_gibson.open.in.git.host.Extension.CopyToClipboardExtension;
-import uk.co.ben_gibson.open.in.git.host.Extension.Extension;
-import uk.co.ben_gibson.open.in.git.host.Extension.ExtensionRunner;
-import uk.co.ben_gibson.open.in.git.host.Extension.OpenInBrowserExtension;
-import uk.co.ben_gibson.open.in.git.host.Git.RemoteHost;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.*;
-import uk.co.ben_gibson.open.in.git.host.Logger.Handlers.DiagnosticLogHandler;
-import uk.co.ben_gibson.open.in.git.host.Logger.Handlers.EventLogHandler;
-import uk.co.ben_gibson.open.in.git.host.Logger.Logger;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Dependency container.
- *
- * There doesn't seem to be much documentation around Intellij's service manager. Services seem to be
- * registered through the plugin.xml but there isn't any information about handling services that have complex dependencies.
- * This acts as a simple alternative.
- */
-public class Container
-{
- private Plugin plugin;
- private List extensions;
- private RemoteUrlFactoryProvider remoteUrlFactoryProvider;
-
- public Plugin plugin()
- {
- if (this.plugin == null) {
-
- PluginId pluginId = PluginId.getId("uk.co.ben-gibson.remote.repository.mapper");
-
- this.plugin = new Plugin(PluginManager.getPlugin(pluginId));
- }
-
- return this.plugin;
- }
-
- public Settings settings(Project project)
- {
- return ServiceManager.getService(project, Settings.class);
- }
-
- public RemoteUrlFactoryProvider remoteUrlFactoryProvider()
- {
- if (this.remoteUrlFactoryProvider == null) {
-
- this.remoteUrlFactoryProvider = new RemoteUrlFactoryProvider();
-
- this.remoteUrlFactoryProvider.registerFactory(new GitHubRemoteUrlFactory());
- this.remoteUrlFactoryProvider.registerFactory(new BitBucketRemoteUrlFactory());
- this.remoteUrlFactoryProvider.registerFactory(new StashRemoteUrlFactory());
- }
-
- return this.remoteUrlFactoryProvider;
- }
-
- public RemoteUrlFactory remoteUrlFactory(Project project) throws RemoteUrlFactoryException
- {
- return this.remoteUrlFactoryProvider().remoteUrlFactoryForHost(this.remoteHost(project));
- }
-
- public RemoteHost remoteHost(Project project)
- {
- return this.settings(project).getRemoteHost();
- }
-
- public Logger logger(Project project)
- {
- Logger logger = new Logger();
-
- logger.registerHandler(new EventLogHandler(this.plugin(), this.settings(project).getEnableVerboseEventLog()));
-
- logger.registerHandler(
- new DiagnosticLogHandler(com.intellij.openapi.diagnostic.Logger.getInstance(this.plugin().displayName()))
- );
-
- return logger;
- }
-
- public List registeredExtensions()
- {
- if (this.extensions == null) {
- this.extensions = new ArrayList<>();
- this.extensions.add(new OpenInBrowserExtension(BrowserLauncher.getInstance()));
- this.extensions.add(new CopyToClipboardExtension(Toolkit.getDefaultToolkit()));
- }
-
- return this.extensions;
- }
-
- public List enabledExtensions(Project project)
- {
- Settings settings = this.settings(project);
-
- List enabledExtensions = new ArrayList<>();
-
- for (Extension extension: this.registeredExtensions()) {
- if (settings.isExtensionEnabled(extension)) {
- enabledExtensions.add(extension);
- }
- }
-
- return enabledExtensions;
- }
-
- public ExtensionRunner extensionRunner(Project project)
- {
- return new ExtensionRunner(this.logger(project), this.enabledExtensions(project));
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Exception/InvalidConfigurationException.java b/src/uk/co/ben_gibson/open/in/git/host/Exception/InvalidConfigurationException.java
deleted file mode 100644
index dad6c0f..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Exception/InvalidConfigurationException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Exception;
-
-/**
- * Thrown when some configuration is invalid for use with the plugin.
- */
-public interface InvalidConfigurationException
-{
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Exception/OpenInGitHostException.java b/src/uk/co/ben_gibson/open/in/git/host/Exception/OpenInGitHostException.java
deleted file mode 100644
index 14f209b..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Exception/OpenInGitHostException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Exception;
-
-/**
- * A base exception from which all exceptions from this library should extend!
- */
-public abstract class OpenInGitHostException extends Exception
-{
- public OpenInGitHostException(String message)
- {
- super(message);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Extension/CopyToClipboardExtension.java b/src/uk/co/ben_gibson/open/in/git/host/Extension/CopyToClipboardExtension.java
deleted file mode 100644
index 0843127..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Extension/CopyToClipboardExtension.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Extension;
-
-import java.awt.*;
-import java.awt.datatransfer.StringSelection;
-import java.net.URL;
-
-/**
- * An extension to copy a remote git url to the clipboard.
- */
-public class CopyToClipboardExtension implements Extension
-{
- private Toolkit toolkit;
-
- public CopyToClipboardExtension(Toolkit toolkit)
- {
- this.toolkit = toolkit;
- }
-
- public void run(URL remoteUrl)
- {
- this.toolkit.getSystemClipboard().setContents(new StringSelection(remoteUrl.toString()), null);
- }
-
- public String displayName()
- {
- return "Copy to Clipboard";
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Extension/Exception/ExtensionException.java b/src/uk/co/ben_gibson/open/in/git/host/Extension/Exception/ExtensionException.java
deleted file mode 100644
index 91e7ea7..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Extension/Exception/ExtensionException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Extension.Exception;
-
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-
-public class ExtensionException extends OpenInGitHostException
-{
- public ExtensionException(String message)
- {
- super(message);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Extension/Extension.java b/src/uk/co/ben_gibson/open/in/git/host/Extension/Extension.java
deleted file mode 100644
index 7df4d41..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Extension/Extension.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Extension;
-
-import uk.co.ben_gibson.open.in.git.host.Extension.Exception.ExtensionException;
-
-import java.net.URL;
-
-/**
- * An extension of the plugin.
- */
-public interface Extension
-{
- void run(URL remoteUrl) throws ExtensionException;
-
- String displayName();
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Extension/ExtensionRunner.java b/src/uk/co/ben_gibson/open/in/git/host/Extension/ExtensionRunner.java
deleted file mode 100644
index dd12ef4..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Extension/ExtensionRunner.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Extension;
-
-import uk.co.ben_gibson.open.in.git.host.Extension.Exception.ExtensionException;
-import uk.co.ben_gibson.open.in.git.host.Logger.Logger;
-import java.net.URL;
-import java.util.List;
-
-public class ExtensionRunner
-{
- private Logger logger;
- private List extensions;
-
- public ExtensionRunner(Logger logger, List extensions)
- {
- this.logger = logger;
- this.extensions = extensions;
- }
-
- public void run(URL remoteUrl) throws ExtensionException
- {
- logger.notice(String.format("Running extensions with url '%s'", remoteUrl.toString()));
-
- if (this.extensions.isEmpty()) {
- logger.warning("You have no extensions enabled, enable some: Preferences → Open in Git host");
- }
-
- for (Extension extension: this.extensions) {
- logger.notice(String.format("Running extension '%s'", extension.displayName()));
- extension.run(remoteUrl);
- }
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Extension/OpenInBrowserExtension.java b/src/uk/co/ben_gibson/open/in/git/host/Extension/OpenInBrowserExtension.java
deleted file mode 100644
index 26b7a60..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Extension/OpenInBrowserExtension.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.Extension;
-
-import com.intellij.ide.browsers.BrowserLauncher;
-import uk.co.ben_gibson.open.in.git.host.Extension.Exception.ExtensionException;
-import java.net.URL;
-
-/**
- * An extension to open a remote git url in the browser.
- */
-public class OpenInBrowserExtension implements Extension
-{
- private BrowserLauncher browserLauncher;
-
- public OpenInBrowserExtension(BrowserLauncher browserLauncher)
- {
- this.browserLauncher = browserLauncher;
- }
-
- public void run(URL remoteUrl) throws ExtensionException
- {
- try {
- this.browserLauncher.open(remoteUrl.toURI().toASCIIString());
- } catch (Exception e) {
- throw new ExtensionException(e.getMessage());
- }
-
- }
-
- public String displayName()
- {
- return "Open in Browser";
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/AbstractRemoteUrlFactory.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/AbstractRemoteUrlFactory.java
deleted file mode 100644
index f16e065..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/AbstractRemoteUrlFactory.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-abstract class AbstractRemoteUrlFactory implements RemoteUrlFactory
-{
- URL buildURL(Remote remote, String path, String query, String fragment, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- URL host = remote.url();
-
- String protocol = (forceSSL) ? "https" : host.getProtocol();
-
- try {
-
- URI uri = new URI(protocol, host.getHost(), path, query, fragment);
-
- return uri.toURL();
-
- } catch (URISyntaxException | MalformedURLException e) {
- throw RemoteUrlFactoryException.cannotCreateRemoteUrl(e.getMessage());
- }
- }
-
- String cleanPath(String path)
- {
- if (path.startsWith("/")) {
- path = path.substring(1);
- }
-
- if (path.endsWith("/")) {
- path = path.substring(0, (path.length() - 1));
- }
-
- return path;
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/BitBucketRemoteUrlFactory.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/BitBucketRemoteUrlFactory.java
deleted file mode 100644
index 5fb2c92..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/BitBucketRemoteUrlFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.*;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.net.URL;
-
-public class BitBucketRemoteUrlFactory extends AbstractRemoteUrlFactory
-{
- public URL createUrl(RemoteCommitDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- Remote remote = description.remote();
-
- String path = String.format("/%s/commits/%s", this.cleanPath(remote.url().getPath()), description.commitHash());
-
- return this.buildURL(remote, path, null, null, forceSSL);
- }
-
- public URL createUrl(RemoteFileDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- Remote remote = description.remote();
- File file = description.file();
- String path = String.format("/%s/src/HEAD/%s", this.cleanPath(remote.url().getPath()), this.cleanPath(file.path()));
- String query = String.format("at=%s", description.branch());
- String fragment = null;
-
- if (description.hasLineNumber()) {
- fragment = String.format("%s-%s", file.name(), description.lineNumber());
- }
-
- return this.buildURL(remote, path, query, fragment, forceSSL);
- }
-
- public boolean supports(RemoteHost host)
- {
- return host.bitbucket();
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteCommitDescription.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteCommitDescription.java
deleted file mode 100644
index 0b813db..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteCommitDescription.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description;
-
-import uk.co.ben_gibson.open.in.git.host.Git.Commit;
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
-
-/**
- * Describes a remote commit that a URL can be created to.
- */
-public class RemoteCommitDescription extends RemoteDescription
-{
- private Commit commit;
-
- public RemoteCommitDescription(Remote remote, Commit commit)
- {
- super(remote);
-
- this.commit = commit;
- }
-
- public String commitHash()
- {
- return this.commit.hash();
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteDescription.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteDescription.java
deleted file mode 100644
index d276293..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Description/RemoteDescription.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description;
-
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
-
-abstract public class RemoteDescription
-{
- private Remote remote;
-
- RemoteDescription(Remote remote)
- {
- this.remote = remote;
- }
-
- public Remote remote()
- {
- return remote;
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Exception/RemoteUrlFactoryException.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Exception/RemoteUrlFactoryException.java
deleted file mode 100644
index daf02a3..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/Exception/RemoteUrlFactoryException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception;
-
-import uk.co.ben_gibson.open.in.git.host.Git.RemoteHost;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-
-public class RemoteUrlFactoryException extends OpenInGitHostException
-{
- private RemoteUrlFactoryException(String message)
- {
- super(message);
- }
-
- public static RemoteUrlFactoryException unsupportedRemoteHost(RemoteHost host)
- {
- return new RemoteUrlFactoryException(String.format("The remote host '%s' is not supported", host.name()));
- }
-
- public static RemoteUrlFactoryException cannotCreateRemoteUrl(String reason)
- {
- return new RemoteUrlFactoryException(String.format("Cannot create remote url (%s)", reason));
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/GitHubRemoteUrlFactory.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/GitHubRemoteUrlFactory.java
deleted file mode 100644
index fffecd9..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/GitHubRemoteUrlFactory.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.*;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.net.URL;
-
-public class GitHubRemoteUrlFactory extends AbstractRemoteUrlFactory
-{
- public URL createUrl(RemoteCommitDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- Remote remote = description.remote();
-
- String path = String.format("/%s/commit/%s", this.cleanPath(remote.url().getPath()), description.commitHash());
-
- return this.buildURL(remote, path, null, null, forceSSL);
- }
-
- public URL createUrl(RemoteFileDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- Remote remote = description.remote();
-
- String path = String.format("/%s/blob/%s/%s", this.cleanPath(remote.url().getPath()), description.branch(), this.cleanPath(description.file().path()));
- String fragment = null;
-
- if (description.hasLineNumber()) {
- fragment = String.format("L%d", description.lineNumber());
- }
-
- return this.buildURL(remote, path, null, fragment, forceSSL);
- }
-
- public boolean supports(RemoteHost host)
- {
- return host.gitHub() || host.gitLab();
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactory.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactory.java
deleted file mode 100644
index 5a5d576..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactory.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.*;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-
-import java.net.URL;
-
-public interface RemoteUrlFactory
-{
- URL createUrl(RemoteCommitDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException;
- URL createUrl(RemoteFileDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException;
- boolean supports(RemoteHost host);
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactoryProvider.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactoryProvider.java
deleted file mode 100644
index 5cf2a3c..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/RemoteUrlFactoryProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.RemoteHost;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class RemoteUrlFactoryProvider
-{
- private List factories = new ArrayList();
-
- public void registerFactory(RemoteUrlFactory factory)
- {
- this.factories.add(factory);
- }
-
- public RemoteUrlFactory remoteUrlFactoryForHost(RemoteHost host) throws RemoteUrlFactoryException
- {
- for (RemoteUrlFactory factory : this.factories) {
- if (factory.supports(host)) {
- return factory;
- }
- }
-
- throw RemoteUrlFactoryException.unsupportedRemoteHost(host);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/StashRemoteUrlFactory.java b/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/StashRemoteUrlFactory.java
deleted file mode 100644
index 34c36ae..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/RemoteUrlFactory/StashRemoteUrlFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory;
-
-import uk.co.ben_gibson.open.in.git.host.Git.*;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import java.net.URL;
-
-public class StashRemoteUrlFactory extends AbstractRemoteUrlFactory
-{
- public URL createUrl(RemoteCommitDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- Remote remote = description.remote();
-
- String[] parts = this.detailsFromRemoteUrl(remote.url());
-
- String project = parts[1];
- String repository = parts[2];
-
- String path = String.format("/projects/%s/repos/%s/commits/%s", project, repository, description.commitHash());
-
- return this.buildURL(remote, path, null, null, forceSSL);
- }
-
- public URL createUrl(RemoteFileDescription description, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException
- {
- String[] parts = this.detailsFromRemoteUrl(description.remote().url());
-
- String project = parts[1];
- String repository = parts[2];
-
- String path = String.format("/projects/%s/repos/%s/browse/%s", project, repository, description.file().path());
- String query = String.format("at=refs/heads/%s", description.branch());
- String fragment = null;
-
- if (description.hasLineNumber()) {
- fragment = description.lineNumber().toString();
- }
-
- return this.buildURL(description.remote(), path, query, fragment, forceSSL);
- }
-
- public boolean supports(RemoteHost host)
- {
- return host.stash();
- }
-
- private String[] detailsFromRemoteUrl(URL url) throws RemoteUrlFactoryException
- {
- String[] parts = url.getPath().split("/", 3);
-
- if (parts.length < 3) {
- throw RemoteUrlFactoryException.cannotCreateRemoteUrl(
- String.format("Could not determine Stash project or repository from remote URL '%s'", url)
- );
- }
-
- return parts;
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/Settings.java b/src/uk/co/ben_gibson/open/in/git/host/Settings.java
deleted file mode 100644
index 02fefd2..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/Settings.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host;
-
-import uk.co.ben_gibson.open.in.git.host.Extension.Extension;
-import uk.co.ben_gibson.open.in.git.host.Git.RemoteHost;
-import com.intellij.openapi.components.PersistentStateComponent;
-import com.intellij.openapi.components.State;
-import com.intellij.openapi.components.Storage;
-import com.intellij.openapi.components.StoragePathMacros;
-import com.intellij.util.xmlb.XmlSerializerUtil;
-import java.util.ArrayList;
-import java.util.List;
-
-@State(name = "uk.co.ben_gibson.open.in.git.host.Settings",
- storages = {@Storage(id = "default", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/settings.xml")}
-)
-
-/*
- * Plugin settings - Getters and Setters required by PersistentStateComponent
- */
-public class Settings implements PersistentStateComponent
-{
- private RemoteHost remoteHost = RemoteHost.GIT_HUB;
- private boolean enableVerboseEventLog = false;
- private boolean forceSSL = false;
- private List enabledExtensions = new ArrayList<>();
-
- public boolean isExtensionEnabled(Extension extension)
- {
- return this.enabledExtensions.contains(extension.getClass().getName());
- }
-
- public List getEnabledExtensions()
- {
- return this.enabledExtensions;
- }
-
- public void setEnabledExtensions(List enabledExtensions)
- {
- this.enabledExtensions = enabledExtensions;
- }
-
- public boolean getForceSSL()
- {
- return this.forceSSL;
- }
-
- public void setForceSSL(boolean forceSSL)
- {
- this.forceSSL = forceSSL;
- }
-
- public boolean getEnableVerboseEventLog()
- {
- return this.enableVerboseEventLog;
- }
-
- public void setEnableVerboseEventLog(boolean enableVerboseEventLog)
- {
- this.enableVerboseEventLog = enableVerboseEventLog;
- }
-
- public void setRemoteHost(RemoteHost remoteHost)
- {
- this.remoteHost = remoteHost;
- }
-
- public RemoteHost getRemoteHost()
- {
- return this.remoteHost;
- }
-
- public void loadState(Settings state)
- {
- XmlSerializerUtil.copyBean(state, this);
- }
-
- public Settings getState()
- {
- return this;
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Action.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Action.java
deleted file mode 100644
index 4e52224..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Action.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Action;
-
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.project.Project;
-import uk.co.ben_gibson.open.in.git.host.Container;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-import uk.co.ben_gibson.open.in.git.host.Logger.Logger;
-
-abstract class Action extends AnAction
-{
- protected abstract boolean shouldActionBeEnabled(AnActionEvent event);
-
- abstract void actionPerformed(Project project, AnActionEvent event) throws OpenInGitHostException;
-
- public void actionPerformed(AnActionEvent event)
- {
- Project project = event.getProject();
-
- Logger logger = container().logger(project);
-
- if (project == null) {
- return;
- }
-
- try {
- this.actionPerformed(project, event);
- } catch (OpenInGitHostException exception) {
- logger.exception(exception);
- }
- }
-
- public void update(AnActionEvent event)
- {
- super.update(event);
-
- if (event.getProject() == null) {
- event.getPresentation().setEnabled(false);
- return;
- }
-
- event.getPresentation().setEnabledAndVisible(this.shouldActionBeEnabled(event));
-
- if (event.getPresentation().isEnabled()) {
- event.getPresentation().setIcon(this.container().remoteHost(event.getProject()).icon());
- }
- }
-
- Container container()
- {
- return ServiceManager.getService(Container.class);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Exception/ActionException.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Exception/ActionException.java
deleted file mode 100644
index 9abb16c..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/Exception/ActionException.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Action.Exception;
-
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-
-public class ActionException extends OpenInGitHostException
-{
- private ActionException(String message)
- {
- super(message);
- }
-
- public static ActionException fileNotFound()
- {
- return new ActionException("File not be found from action event");
- }
-
- public static ActionException vcsLogNotFound()
- {
- return new ActionException("VCS log not be found from action event");
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/MenuAction.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Action/MenuAction.java
deleted file mode 100644
index b1673ac..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/MenuAction.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Action;
-
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.Task;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import git4idea.GitUtil;
-import git4idea.commands.GitImpl;
-import git4idea.repo.GitRepository;
-import org.jetbrains.annotations.NotNull;
-import uk.co.ben_gibson.open.in.git.host.Container;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.UI.Exception.RepositoryNotFoundException;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Repository;
-import uk.co.ben_gibson.open.in.git.host.UI.Action.Exception.ActionException;
-import java.net.URL;
-
-/**
- * An action triggered from the view or right click menu.
- */
-public class MenuAction extends Action
-{
- public void actionPerformed(Project project, AnActionEvent event) throws OpenInGitHostException
- {
- Container container = this.container();
- VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE);
-
- if (file == null) {
- throw ActionException.fileNotFound();
- }
-
- Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
- GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForFile(file);
-
- if (repository == null) {
- throw new RepositoryNotFoundException();
- }
-
- Integer caretPosition = (editor != null) ? editor.getCaretModel().getLogicalPosition().line + 1 : null;
-
- Repository repo = new Repository(new GitImpl(), repository, Branch.master());
-
- Task.Backgroundable task = new Task.Backgroundable(null, "Opening File In Git Host") {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- try {
-
- RemoteFileDescription description = new RemoteFileDescription(
- repo.origin(),
- repo.currentBranch(),
- repo.fileFromVirtualFile(file),
- caretPosition
- );
-
- URL remoteUrl = container.remoteUrlFactory(project).createUrl(
- description,
- container.settings(project).getForceSSL()
- );
-
- container.extensionRunner(project).run(remoteUrl);
-
- } catch (OpenInGitHostException e) {
- container.logger(project).exception(e);
- }
- }
- };
-
- task.queue();
- }
-
- protected boolean shouldActionBeEnabled(AnActionEvent event)
- {
- return (event.getData(CommonDataKeys.VIRTUAL_FILE) != null);
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/VcsLogAction.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Action/VcsLogAction.java
deleted file mode 100644
index 2f594b4..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Action/VcsLogAction.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Action;
-
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.Task;
-import com.intellij.openapi.project.Project;
-import com.intellij.vcs.log.VcsFullCommitDetails;
-import com.intellij.vcs.log.VcsLog;
-import com.intellij.vcs.log.VcsLogDataKeys;
-import git4idea.GitUtil;
-import git4idea.commands.GitImpl;
-import git4idea.repo.GitRepository;
-import org.jetbrains.annotations.NotNull;
-import uk.co.ben_gibson.open.in.git.host.Container;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.UI.Exception.RepositoryNotFoundException;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Commit;
-import uk.co.ben_gibson.open.in.git.host.Git.Repository;
-import uk.co.ben_gibson.open.in.git.host.UI.Action.Exception.ActionException;
-import java.net.URL;
-import java.util.List;
-
-/**
- * An action triggered from an VCS log toolbar.
- */
-public class VcsLogAction extends Action
-{
- public void actionPerformed(Project project, AnActionEvent event) throws OpenInGitHostException
- {
- Container container = this.container();
- VcsLog vcsLog = event.getData(VcsLogDataKeys.VCS_LOG);
-
- if (vcsLog == null) {
- throw ActionException.vcsLogNotFound();
- }
-
- VcsFullCommitDetails commit = vcsLog.getSelectedDetails().get(0);
-
- GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForRoot(commit.getRoot());
-
- if (repository == null) {
- throw new RepositoryNotFoundException();
- }
-
- Repository repo = new Repository(new GitImpl(), repository, Branch.master());
-
- Task.Backgroundable task = new Task.Backgroundable(null, "Opening Commit In Git Host") {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- try {
-
- RemoteCommitDescription description = new RemoteCommitDescription(repo.origin(), new Commit(commit));
-
- URL remoteUrl = container.remoteUrlFactory(project).createUrl(
- description,
- container.settings(project).getForceSSL()
- );
-
- container.extensionRunner(project).run(remoteUrl);
-
- } catch (OpenInGitHostException e) {
- container.logger(project).exception(e);
- }
- }
- };
-
- task.queue();
- }
-
- protected boolean shouldActionBeEnabled(AnActionEvent event)
- {
- VcsLog log = event.getData(VcsLogDataKeys.VCS_LOG);
-
- if (log == null) {
- return false;
- }
-
- List commits = log.getSelectedDetails();
-
- return commits.size() == 1;
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Exception/RepositoryNotFoundException.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Exception/RepositoryNotFoundException.java
deleted file mode 100644
index 29c4819..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Exception/RepositoryNotFoundException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Exception;
-
-import uk.co.ben_gibson.open.in.git.host.Exception.InvalidConfigurationException;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-
-/**
- * Thrown when the git repository could not be found.
- */
-public class RepositoryNotFoundException extends OpenInGitHostException implements InvalidConfigurationException
-{
- public RepositoryNotFoundException()
- {
- super("Git repository not found, make sure you have registered your version control root: Preferences → Version Control");
- }
-}
diff --git a/src/uk/co/ben_gibson/open/in/git/host/UI/Target/SelectInTarget.java b/src/uk/co/ben_gibson/open/in/git/host/UI/Target/SelectInTarget.java
deleted file mode 100644
index be98984..0000000
--- a/src/uk/co/ben_gibson/open/in/git/host/UI/Target/SelectInTarget.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.UI.Target;
-
-import com.intellij.ide.SelectInContext;
-import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.fileEditor.FileEditorManager;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.Task;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.vfs.VirtualFile;
-import git4idea.GitUtil;
-import git4idea.commands.GitImpl;
-import git4idea.repo.GitRepository;
-import org.jetbrains.annotations.NotNull;
-import uk.co.ben_gibson.open.in.git.host.Container;
-import uk.co.ben_gibson.open.in.git.host.Logger.Logger;
-import uk.co.ben_gibson.open.in.git.host.UI.Exception.RepositoryNotFoundException;
-import uk.co.ben_gibson.open.in.git.host.Exception.OpenInGitHostException;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Repository;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import java.net.URL;
-
-/**
- * Extends the select in target menu.
- */
-public class SelectInTarget implements com.intellij.ide.SelectInTarget
-{
- public boolean canSelect(SelectInContext context)
- {
- return true;
- }
-
- public void selectIn(SelectInContext context, boolean requestFocus)
- {
- Container container = this.container();
- Project project = context.getProject();
- VirtualFile file = context.getVirtualFile();
-
- GitRepository repository = GitUtil.getRepositoryManager(project).getRepositoryForFile(file);
-
- Logger logger = container.logger(project);
-
- try {
-
- if (repository == null) {
- throw new RepositoryNotFoundException();
- }
-
- Repository repo = new Repository(new GitImpl(), repository, Branch.master());
-
- Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
-
- Integer caretPosition = (editor != null) ? editor.getCaretModel().getLogicalPosition().line + 1 : null;
-
- Task.Backgroundable task = new Task.Backgroundable(null, "Opening Target In Git Host") {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- try {
-
- RemoteFileDescription description = new RemoteFileDescription(
- repo.origin(),
- repo.currentBranch(),
- repo.fileFromVirtualFile(file),
- caretPosition
- );
-
- URL remoteUrl = container.remoteUrlFactory(project).createUrl(
- description,
- container.settings(project).getForceSSL()
- );
-
- container.extensionRunner(project).run(remoteUrl);
-
- } catch (OpenInGitHostException e) {
- logger.exception(e);
- }
- }
- };
-
- task.queue();
-
- } catch (OpenInGitHostException exception) {
- logger.exception(exception);
- }
- }
-
- private Container container()
- {
- return ServiceManager.getService(Container.class);
- }
-
- public String getToolWindowId()
- {
- return null;
- }
-
- public String getMinorViewId()
- {
- return null;
- }
-
- public float getWeight()
- {
- return 0;
- }
-
- public String toString()
- {
- return "Open in Git Host";
- }
-}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/BranchTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Git/BranchTest.java
similarity index 77%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/BranchTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Git/BranchTest.java
index 89c7083..a90a73e 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/BranchTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Git/BranchTest.java
@@ -1,7 +1,7 @@
-package uk.co.ben_gibson.open.in.git.host.test.Git;
+package uk.co.ben_gibson.git.link.test.Git;
import junit.framework.TestCase;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Branch;
public class BranchTest extends TestCase
{
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/CommitTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Git/CommitTest.java
similarity index 86%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/CommitTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Git/CommitTest.java
index 40e8f4b..6f2ed92 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/CommitTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Git/CommitTest.java
@@ -1,9 +1,9 @@
-package uk.co.ben_gibson.open.in.git.host.test.Git;
+package uk.co.ben_gibson.git.link.test.Git;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import junit.framework.TestCase;
-import uk.co.ben_gibson.open.in.git.host.Git.Commit;
+import uk.co.ben_gibson.git.link.Git.Commit;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/FileTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Git/FileTest.java
similarity index 81%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/FileTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Git/FileTest.java
index 551d748..cf6eb98 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/FileTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Git/FileTest.java
@@ -1,8 +1,8 @@
-package uk.co.ben_gibson.open.in.git.host.test.Git;
+package uk.co.ben_gibson.git.link.test.Git;
import com.intellij.openapi.vfs.VirtualFile;
import junit.framework.TestCase;
-import uk.co.ben_gibson.open.in.git.host.Git.File;
+import uk.co.ben_gibson.git.link.Git.File;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -12,7 +12,7 @@ public void testReturnsPath()
{
File file = file("foo/bar.java", "bar.java");
- assertSame("foo/bar.java", file.path());
+ assertSame("foo/bar.java", file.pathWithName());
}
public void testReturnsName()
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/RemoteTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Git/RemoteTest.java
similarity index 92%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/RemoteTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Git/RemoteTest.java
index 7f0d844..8945d8b 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Git/RemoteTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Git/RemoteTest.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.test.Git;
+package uk.co.ben_gibson.git.link.test.Git;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
@@ -8,8 +8,8 @@
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.Git.Remote;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.Remote;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/PluginTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/PluginTest.java
new file mode 100644
index 0000000..04100b2
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/PluginTest.java
@@ -0,0 +1,50 @@
+package uk.co.ben_gibson.git.link.test;
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import junit.framework.TestCase;
+import uk.co.ben_gibson.git.link.Plugin;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PluginTest extends TestCase
+{
+ public void testToString()
+ {
+ Plugin plugin = new Plugin(this.mockPluginDescription("foo", "v2.2", "https://example.com"));
+
+ assertEquals(plugin.toString(), "foo(v2.2)");
+ }
+
+ public void testReturnsVersion()
+ {
+ Plugin plugin = new Plugin(this.mockPluginDescription("foo", "v2.2", "https://example.com"));
+
+ assertEquals(plugin.version(), "v2.2");
+ }
+
+ public void testReturnsDisplayName()
+ {
+ Plugin plugin = new Plugin(this.mockPluginDescription("foo", "v2.2", "https://example.com"));
+
+ assertEquals(plugin.displayName(), "foo");
+ }
+
+ public void testIssueTracker()
+ {
+ Plugin plugin = new Plugin(this.mockPluginDescription("foo", "v2.2", "https://example.com"));
+
+ assertEquals(plugin.issueTracker(), "https://example.com/issues");
+ }
+
+ private IdeaPluginDescriptor mockPluginDescription(String name, String version, String vendorUrl)
+ {
+ IdeaPluginDescriptor pluginDescriptor = mock(IdeaPluginDescriptor.class);
+
+ when(pluginDescriptor.getName()).thenReturn(name);
+ when(pluginDescriptor.getVersion()).thenReturn(version);
+ when(pluginDescriptor.getVendorUrl()).thenReturn(vendorUrl);
+
+ return pluginDescriptor;
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/BitBucketUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/BitBucketUrlFactoryTest.java
new file mode 100644
index 0000000..29c0012
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/BitBucketUrlFactoryTest.java
@@ -0,0 +1,80 @@
+package uk.co.ben_gibson.git.link.test.Url.Factory;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Url.Factory.BitBucketUrlFactory;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+import java.net.MalformedURLException;
+
+public class BitBucketUrlFactoryTest extends UrlFactoryTest
+{
+ @DataProvider
+ public static Object[][] commitProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new CommitDescription(
+ mockRemote("https://example@bitbucket.org/foo bar/bar"),
+ mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://bitbucket.org/foo%20bar/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ {
+ new CommitDescription(
+ mockRemote("https://bitbucket.org/foo bar/bar"),
+ mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://bitbucket.org/foo%20bar/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ {
+ new CommitDescription(
+ mockRemote("http://foo-bar.com/foo/bar"),
+ mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
+ ),
+ "http://foo-bar.com/foo/bar/commits/0df948a98048a3b30911d974414dfe0ef22a1724"
+ },
+ };
+ }
+
+ @DataProvider
+ public static Object[][] fileProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new FileDescription(
+ mockRemote("https://bitbucket.org/foo/bar/"),
+ Branch.master(),
+ mockFile("src/Bar.java", "Bar.java"),
+ 10
+ ),
+ "https://bitbucket.org/foo/bar/src/HEAD/src/Bar.java?at=master#Bar.java-10"
+ },
+ {
+ new FileDescription(
+ mockRemote("http://example@bitbucket.org/foo/bar/"),
+ new Branch("dev"),
+ mockFile("src/Bar.java", "Bar.java"),
+ null
+ ),
+ "http://bitbucket.org/foo/bar/src/HEAD/src/Bar.java?at=dev"
+ },
+ {
+ new FileDescription(
+ mockRemote("https://foo-bar.org/foo bar/bar"),
+ new Branch("dev-wip[2.0.0]"),
+ mockFile("/src/foo/Foo Bar Baz.java", "Foo Bar Baz.java"),
+ null
+ ),
+ "https://foo-bar.org/foo%20bar/bar/src/HEAD/src/foo/Foo%20Bar%20Baz.java?at=dev-wip[2.0.0]"
+ },
+ };
+ }
+
+ public UrlFactory remoteUrlFactory()
+ {
+ return new BitBucketUrlFactory();
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/CustomUrlFactortTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/CustomUrlFactortTest.java
new file mode 100644
index 0000000..e2e60db
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/CustomUrlFactortTest.java
@@ -0,0 +1,68 @@
+package uk.co.ben_gibson.git.link.test.Url.Factory;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Url.Factory.CustomUrlFactory;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+
+import java.net.MalformedURLException;
+
+public class CustomUrlFactortTest extends UrlFactoryTest
+{
+ @DataProvider
+ public static Object[][] commitProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new CommitDescription(
+ UrlFactoryTest.mockRemote("https://example.com/foo bar/baz"),
+ UrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://custom.host.com/custom-project/commit/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ {
+ new CommitDescription(
+ UrlFactoryTest.mockRemote("http://custom.example.com/foo/bar"),
+ UrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://custom.host.com/custom-project/commit/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ };
+ }
+
+ @DataProvider
+ public static Object[][] fileProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new FileDescription(
+ UrlFactoryTest.mockRemote("https://example.com/foo/bar"),
+ Branch.master(),
+ UrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
+ 10
+ ),
+ "https://custom.host.com/custom-project/master/src/Bar.java#10"
+ },
+ {
+ new FileDescription(
+ UrlFactoryTest.mockRemote("http://example.com/foo bar/baz"),
+ new Branch("dev-wip-[2.0.0]"),
+ UrlFactoryTest.mockFile("src/Foo Bar.java", "Foo Bar.java"),
+ null
+ ),
+ "https://custom.host.com/custom-project/dev-wip-[2.0.0]/src/Foo Bar.java#"
+ },
+ };
+ }
+
+ public UrlFactory remoteUrlFactory()
+ {
+ return new CustomUrlFactory(
+ "https://custom.host.com/custom-project/{branch}/{filePath}/{fileName}#{line}",
+ "https://custom.host.com/custom-project/commit/{commit}"
+ );
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/GitHubUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/GitHubUrlFactoryTest.java
new file mode 100644
index 0000000..e17a3d7
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/GitHubUrlFactoryTest.java
@@ -0,0 +1,90 @@
+package uk.co.ben_gibson.git.link.test.Url.Factory;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.GitHubUrlFactory;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+
+import java.net.MalformedURLException;
+
+public class GitHubUrlFactoryTest extends UrlFactoryTest
+{
+ @DataProvider
+ public static Object[][] commitProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new CommitDescription(
+ mockRemote("https://github.com/foo/bar"),
+ mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://github.com/foo/bar/commit/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ {
+ new CommitDescription(
+ mockRemote("https://github.com/foo bar/baz"),
+ mockCommit("40ec791cdd904557793e200c93f3118043ec18af")
+ ),
+ "https://github.com/foo%20bar/baz/commit/40ec791cdd904557793e200c93f3118043ec18af"
+ },
+ {
+ new CommitDescription(
+ mockRemote("http://github.com/foo/bar"),
+ mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
+ ),
+ "http://github.com/foo/bar/commit/0df948a98048a3b30911d974414dfe0ef22a1724"
+ },
+ };
+ }
+
+ @DataProvider
+ public static Object[][] fileProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new FileDescription(
+ mockRemote("https://github.com/foo/bar"),
+ Branch.master(),
+ mockFile("src/Bar.java", "Bar.java"),
+ 10
+ ),
+ "https://github.com/foo/bar/blob/master/src/Bar.java#L10"
+ },
+ {
+ new FileDescription(
+ mockRemote("https://github.com/foo/bar"),
+ new Branch("feature-foo-[PRO-123]"),
+ mockFile("/src/Bar Bar/Baz.java", "Baz.java"),
+ null
+ ),
+ "https://github.com/foo/bar/blob/feature-foo-%5BPRO-123%5D/src/Bar%20Bar/Baz.java"
+ },
+ {
+ new FileDescription(
+ mockRemote("http://github.com/foo/bar"),
+ new Branch("dev"),
+ mockFile("/src/Bar Bar/Baz.java", "Baz.java"),
+ null
+ ),
+ "http://github.com/foo/bar/blob/dev/src/Bar%20Bar/Baz.java"
+ },
+ {
+ new FileDescription(
+ mockRemote("https://github.com/foo bar/baz"),
+ Branch.master(),
+ mockFile("需求0920-2017/0928.sql", "0928.sql"),
+ 15
+ ),
+ "https://github.com/foo%20bar/baz/blob/master/需求0920-2017/0928.sql#L15"
+ },
+ };
+ }
+
+ public UrlFactory remoteUrlFactory()
+ {
+ return new GitHubUrlFactory();
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/StashUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/StashUrlFactoryTest.java
new file mode 100644
index 0000000..1f016db
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/StashUrlFactoryTest.java
@@ -0,0 +1,64 @@
+package uk.co.ben_gibson.git.link.test.Url.Factory;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import uk.co.ben_gibson.git.link.Git.Branch;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+import uk.co.ben_gibson.git.link.Url.Factory.StashUrlFactory;
+import java.net.MalformedURLException;
+
+public class StashUrlFactoryTest extends UrlFactoryTest
+{
+ @DataProvider
+ public static Object[][] commitProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new CommitDescription(
+ UrlFactoryTest.mockRemote("https://stash.example.com/foo bar/baz"),
+ UrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "https://stash.example.com/projects/foo%20bar/repos/baz/commits/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ {
+ new CommitDescription(
+ UrlFactoryTest.mockRemote("http://stash.example.com/foo/bar"),
+ UrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
+ ),
+ "http://stash.example.com/projects/foo/repos/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3"
+ },
+ };
+ }
+
+ @DataProvider
+ public static Object[][] fileProvider() throws MalformedURLException, RemoteException
+ {
+ return new Object[][] {
+ {
+ new FileDescription(
+ UrlFactoryTest.mockRemote("https://stash.example.com/foo/bar"),
+ Branch.master(),
+ UrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
+ 10
+ ),
+ "https://stash.example.com/projects/foo/repos/bar/browse/src/Bar.java?at=refs/heads/master#10",
+ },
+ {
+ new FileDescription(
+ UrlFactoryTest.mockRemote("http://stash.example.com/foo bar/baz"),
+ new Branch("dev-wip-[2.0.0]"),
+ UrlFactoryTest.mockFile("src/Foo Bar.java", "Foo Bar.java"),
+ null
+ ),
+ "http://stash.example.com/projects/foo%20bar/repos/baz/browse/src/Foo%20Bar.java?at=refs/heads/dev-wip-[2.0.0]"
+ },
+ };
+ }
+
+ public UrlFactory remoteUrlFactory()
+ {
+ return new StashUrlFactory();
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/UrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/UrlFactoryTest.java
new file mode 100644
index 0000000..4218cba
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Factory/UrlFactoryTest.java
@@ -0,0 +1,76 @@
+package uk.co.ben_gibson.git.link.test.Url.Factory;
+
+import com.intellij.openapi.vfs.VirtualFile;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import uk.co.ben_gibson.git.link.Git.Commit;
+import uk.co.ben_gibson.git.link.Git.Exception.RemoteException;
+import uk.co.ben_gibson.git.link.Git.File;
+import uk.co.ben_gibson.git.link.Git.Remote;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.CommitDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Description.FileDescription;
+import uk.co.ben_gibson.git.link.Url.Factory.Exception.UrlFactoryException;
+import uk.co.ben_gibson.git.link.Url.Factory.UrlFactory;
+import java.net.MalformedURLException;
+import java.net.URL;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(DataProviderRunner.class)
+public abstract class UrlFactoryTest extends TestCase
+{
+ public abstract UrlFactory remoteUrlFactory();
+
+ @Test
+ @UseDataProvider("commitProvider")
+ public void testCanCreateUrlToCommit(CommitDescription description, String expected) throws UrlFactoryException, RemoteException, MalformedURLException
+ {
+ UrlFactory factory = this.remoteUrlFactory();
+
+ URL url = factory.createUrl(description);
+
+ assertEquals(expected, url.toString());
+ }
+
+ @Test
+ @UseDataProvider("fileProvider")
+ public void testCanCreateUrlToFile(FileDescription description, String expected) throws UrlFactoryException, RemoteException, MalformedURLException
+ {
+ UrlFactory factory = this.remoteUrlFactory();
+
+ URL url = factory.createUrl(description);
+
+ assertEquals(expected, url.toString());
+ }
+
+ static Remote mockRemote(String originUrl) throws MalformedURLException, RemoteException
+ {
+ Remote remote = mock(Remote.class);
+
+ when(remote.url()).thenReturn(new URL(originUrl));
+
+ return remote;
+ }
+
+ static Commit mockCommit(String hash)
+ {
+ Commit commit = mock(Commit.class);
+
+ when(commit.hash()).thenReturn(hash);
+
+ return commit;
+ }
+
+ static File mockFile(String path, String name)
+ {
+ VirtualFile virtualFile = mock(VirtualFile.class);
+
+ when(virtualFile.getName()).thenReturn(name);
+ when(virtualFile.getPath()).thenReturn(path);
+
+ return new File(path, virtualFile);
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/CopyToClipboardExtensionTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/CopyToClipboardHandlerTest.java
similarity index 56%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/CopyToClipboardExtensionTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/CopyToClipboardHandlerTest.java
index b9ea4bd..529b512 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/CopyToClipboardExtensionTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/CopyToClipboardHandlerTest.java
@@ -1,19 +1,20 @@
-package uk.co.ben_gibson.open.in.git.host.test.Extension;
+package uk.co.ben_gibson.git.link.test.Url.Handler;
import junit.framework.TestCase;
-import uk.co.ben_gibson.open.in.git.host.Extension.CopyToClipboardExtension;
-import uk.co.ben_gibson.open.in.git.host.Extension.Exception.ExtensionException;
-import uk.co.ben_gibson.open.in.git.host.Extension.Extension;
+import uk.co.ben_gibson.git.link.Url.Handler.Exception.UrlHandlerException;
+import uk.co.ben_gibson.git.link.Url.Handler.CopyToClipboardHandler;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.StringSelection;
import java.net.MalformedURLException;
import java.net.URL;
import static org.mockito.Mockito.*;
-public class CopyToClipboardExtensionTest extends TestCase
+public class CopyToClipboardHandlerTest extends TestCase
{
- public void testCopiesUrlToClipboard() throws MalformedURLException, ExtensionException
+ public void testCopiesUrlToClipboard() throws MalformedURLException, UrlHandlerException
{
URL url = new URL("https://example.com");
@@ -22,9 +23,9 @@ public void testCopiesUrlToClipboard() throws MalformedURLException, ExtensionEx
when(toolkit.getSystemClipboard()).thenReturn(clipboard);
- Extension extension = new CopyToClipboardExtension(toolkit);
+ UrlHandler handler = new CopyToClipboardHandler(toolkit);
- extension.run(url);
+ handler.handle(url);
verify(clipboard, times(1)).setContents(any(StringSelection.class), isNull());
}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/OpenInBrowserExtensionTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/OpenInBrowserHandlerTest.java
similarity index 71%
rename from tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/OpenInBrowserExtensionTest.java
rename to tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/OpenInBrowserHandlerTest.java
index 7ae769e..18100cf 100644
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/Extension/OpenInBrowserExtensionTest.java
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Handler/OpenInBrowserHandlerTest.java
@@ -1,4 +1,4 @@
-package uk.co.ben_gibson.open.in.git.host.test.Extension;
+package uk.co.ben_gibson.git.link.test.Url.Handler;
import com.intellij.ide.browsers.BrowserLauncher;
import com.tngtech.java.junit.dataprovider.DataProvider;
@@ -7,25 +7,25 @@
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
-import uk.co.ben_gibson.open.in.git.host.Extension.Exception.ExtensionException;
-import uk.co.ben_gibson.open.in.git.host.Extension.Extension;
-import uk.co.ben_gibson.open.in.git.host.Extension.OpenInBrowserExtension;
+import uk.co.ben_gibson.git.link.Url.Handler.Exception.UrlHandlerException;
+import uk.co.ben_gibson.git.link.Url.Handler.UrlHandler;
+import uk.co.ben_gibson.git.link.Url.Handler.OpenInBrowserHandler;
import static org.mockito.Mockito.*;
import java.net.MalformedURLException;
import java.net.URL;
@RunWith(DataProviderRunner.class)
-public class OpenInBrowserExtensionTest extends TestCase
+public class OpenInBrowserHandlerTest extends TestCase
{
@Test
@UseDataProvider("urlProvider")
- public void testOpensInBrowser(URL url, String expected) throws MalformedURLException, ExtensionException
+ public void testOpensInBrowser(URL url, String expected) throws MalformedURLException, UrlHandlerException
{
BrowserLauncher browserLauncher = mock(BrowserLauncher.class);
- Extension extension = new OpenInBrowserExtension(browserLauncher);
+ UrlHandler handler = new OpenInBrowserHandler(browserLauncher);
- extension.run(url);
+ handler.handle(url);
verify(browserLauncher, times(1)).open(expected);
}
diff --git a/tests/unit/uk/co/ben_gibson/git/link/test/Url/Modifier/HttpsUrlModifierTest.java b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Modifier/HttpsUrlModifierTest.java
new file mode 100644
index 0000000..221d02f
--- /dev/null
+++ b/tests/unit/uk/co/ben_gibson/git/link/test/Url/Modifier/HttpsUrlModifierTest.java
@@ -0,0 +1,49 @@
+package uk.co.ben_gibson.git.link.test.Url.Modifier;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import uk.co.ben_gibson.git.link.Url.Modifier.Exception.ModifierException;
+import uk.co.ben_gibson.git.link.Url.Modifier.HttpsUrlModifier;
+import uk.co.ben_gibson.git.link.Url.Modifier.UrlModifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+@RunWith(DataProviderRunner.class)
+public class HttpsUrlModifierTest extends TestCase
+{
+ @Test
+ @UseDataProvider("urlProvider")
+ public void testForcesHttpsProtocol(URL url, String expected) throws ModifierException
+ {
+ UrlModifier modifier = new HttpsUrlModifier();
+
+ assertEquals(expected, modifier.modify(url).toString());
+ }
+
+ @DataProvider
+ public static Object[][] urlProvider() throws MalformedURLException
+ {
+ return new Object[][] {
+ {
+ new URL("http://example.com"),
+ "https://example.com",
+ },
+ {
+ new URL("https://example.com"),
+ "https://example.com",
+ },
+ {
+ new URL("http://github.com/foo%20bar/baz/blob/master/需求0920-2017/0928.sql#L15"),
+ "https://github.com/foo%20bar/baz/blob/master/需求0920-2017/0928.sql#L15"
+ },
+ {
+ new URL( "http://stash.example.com/projects/foo/repos/bar/browse/src/Bar.java?at=refs/heads/master#10"),
+ "https://stash.example.com/projects/foo/repos/bar/browse/src/Bar.java?at=refs/heads/master#10"
+ },
+ };
+ }
+}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/PluginTest.java b/tests/unit/uk/co/ben_gibson/open/in/git/host/test/PluginTest.java
deleted file mode 100644
index 8b3ce1a..0000000
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/PluginTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.test;
-
-import com.intellij.ide.plugins.IdeaPluginDescriptor;
-import junit.framework.TestCase;
-import uk.co.ben_gibson.open.in.git.host.Plugin;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class PluginTest extends TestCase
-{
- public void testToString()
- {
- Plugin plugin = new Plugin("foo", "v2.2");
-
- assertEquals(plugin.toString(), "foo(v2.2)");
- }
-
- public void testReturnsVersion()
- {
- Plugin plugin = new Plugin("foo", "v2.2");
-
- assertEquals(plugin.version(), "v2.2");
- }
-
- public void testReturnsDisplayName()
- {
- Plugin plugin = new Plugin("foo", "v2.2");
-
- assertEquals(plugin.displayName(), "foo");
- }
-
- public void testCanBeCreatedFromIdeaPluginDescriptor()
- {
-
- IdeaPluginDescriptor pluginDescriptor = mock(IdeaPluginDescriptor.class);
-
- when(pluginDescriptor.getName()).thenReturn("bar");
- when(pluginDescriptor.getVersion()).thenReturn("v1.0.2");
-
- Plugin plugin = new Plugin(pluginDescriptor);
-
- assertEquals(plugin.displayName(), "bar");
- assertEquals(plugin.version(), "v1.0.2");
- }
-}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/BitBucketRemoteUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/BitBucketRemoteUrlFactoryTest.java
deleted file mode 100644
index b792956..0000000
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/BitBucketRemoteUrlFactoryTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.test.RemoteUrlFactory;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.RemoteUrlFactory;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.BitBucketRemoteUrlFactory;
-
-import java.net.MalformedURLException;
-
-public class BitBucketRemoteUrlFactoryTest extends RemoteUrlFactoryTest
-{
- @DataProvider
- public static Object[][] commitProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("https://example@bitbucket.org/foo bar/bar"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "https://bitbucket.org/foo%20bar/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("https://bitbucket.org/foo bar/bar"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "https://bitbucket.org/foo%20bar/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://foo-bar.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
- ),
- "http://foo-bar.com/foo/bar/commits/0df948a98048a3b30911d974414dfe0ef22a1724",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://bitbucket.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
- ),
- "https://bitbucket.com/foo/bar/commits/0df948a98048a3b30911d974414dfe0ef22a1724",
- true
- },
- };
- }
-
- @DataProvider
- public static Object[][] fileProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://bitbucket.org/foo/bar/"),
- Branch.master(),
- RemoteUrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
- 10
- ),
- "https://bitbucket.org/foo/bar/src/HEAD/src/Bar.java?at=master#Bar.java-10",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("http://example@bitbucket.org/foo/bar/"),
- new Branch("dev"),
- RemoteUrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
- null
- ),
- "http://bitbucket.org/foo/bar/src/HEAD/src/Bar.java?at=dev",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("http://foo-bar.org/foo bar/bar"),
- new Branch("dev-wip[2.0.0]"),
- RemoteUrlFactoryTest.mockFile("/src/foo/Foo Bar Baz.java", "Foo Bar Baz.java"),
- null
- ),
- "https://foo-bar.org/foo%20bar/bar/src/HEAD/src/foo/Foo%20Bar%20Baz.java?at=dev-wip[2.0.0]",
- true
- },
- };
- }
-
- public RemoteUrlFactory remoteUrlFactory()
- {
- return new BitBucketRemoteUrlFactory();
- }
-}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/GitHubRemoteUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/GitHubRemoteUrlFactoryTest.java
deleted file mode 100644
index 339435c..0000000
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/GitHubRemoteUrlFactoryTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.test.RemoteUrlFactory;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.RemoteUrlFactory;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.GitHubRemoteUrlFactory;
-
-import java.net.MalformedURLException;
-
-public class GitHubRemoteUrlFactoryTest extends RemoteUrlFactoryTest
-{
- @DataProvider
- public static Object[][] commitProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "https://github.com/foo/bar/commit/bd7ab0f04151e7409d77caa296617f97352f36d3",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo bar/baz"),
- RemoteUrlFactoryTest.mockCommit("40ec791cdd904557793e200c93f3118043ec18af")
- ),
- "https://github.com/foo%20bar/baz/commit/40ec791cdd904557793e200c93f3118043ec18af",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://github.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
- ),
- "http://github.com/foo/bar/commit/0df948a98048a3b30911d974414dfe0ef22a1724",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://github.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("0df948a98048a3b30911d974414dfe0ef22a1724")
- ),
- "https://github.com/foo/bar/commit/0df948a98048a3b30911d974414dfe0ef22a1724",
- true
- },
- };
- }
-
- @DataProvider
- public static Object[][] fileProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo/bar"),
- Branch.master(),
- RemoteUrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
- 10
- ),
- "https://github.com/foo/bar/blob/master/src/Bar.java#L10",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo/bar"),
- new Branch("feature-foo-[PRO-123]"),
- RemoteUrlFactoryTest.mockFile("/src/Bar Bar/Baz.java", "Baz.java"),
- null
- ),
- "https://github.com/foo/bar/blob/feature-foo-%5BPRO-123%5D/src/Bar%20Bar/Baz.java",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("http://github.com/foo/bar"),
- new Branch("dev"),
- RemoteUrlFactoryTest.mockFile("/src/Bar Bar/Baz.java", "Baz.java"),
- null
- ),
- "http://github.com/foo/bar/blob/dev/src/Bar%20Bar/Baz.java",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo bar/baz"),
- Branch.master(),
- RemoteUrlFactoryTest.mockFile("resources/Bar Baz.java", "Bar Baz.java"),
- null
- ),
- "https://github.com/foo%20bar/baz/blob/master/resources/Bar%20Baz.java",
- true
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://github.com/foo bar/baz"),
- Branch.master(),
- RemoteUrlFactoryTest.mockFile("需求0920-2017/0928.sql", "0928.sql"),
- 15
- ),
- "https://github.com/foo%20bar/baz/blob/master/需求0920-2017/0928.sql#L15",
- true
- },
- };
- }
-
- public RemoteUrlFactory remoteUrlFactory()
- {
- return new GitHubRemoteUrlFactory();
- }
-}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/RemoteUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/RemoteUrlFactoryTest.java
deleted file mode 100644
index 039e0f0..0000000
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/RemoteUrlFactoryTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.test.RemoteUrlFactory;
-
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import junit.framework.TestCase;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import uk.co.ben_gibson.open.in.git.host.Git.*;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Exception.RemoteUrlFactoryException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.RemoteUrlFactory;
-import java.net.MalformedURLException;
-import java.net.URL;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(DataProviderRunner.class)
-public abstract class RemoteUrlFactoryTest extends TestCase
-{
- public abstract RemoteUrlFactory remoteUrlFactory();
-
- @Test
- @UseDataProvider("commitProvider")
- public void testCanCreateUrlToCommit(RemoteCommitDescription description, String expected, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException, MalformedURLException
- {
- RemoteUrlFactory factory = this.remoteUrlFactory();
-
- URL url = factory.createUrl(description, forceSSL);
-
- assertEquals(expected, url.toString());
- }
-
- @Test
- @UseDataProvider("fileProvider")
- public void testCanCreateUrlToFile(RemoteFileDescription description, String expected, boolean forceSSL) throws RemoteUrlFactoryException, RemoteException, MalformedURLException
- {
- RemoteUrlFactory factory = this.remoteUrlFactory();
-
- URL url = factory.createUrl(description, forceSSL);
-
- assertEquals(expected, url.toString());
- }
-
- static Remote mockRemote(String originUrl) throws MalformedURLException, RemoteException
- {
- Remote remote = mock(Remote.class);
-
- when(remote.url()).thenReturn(new URL(originUrl));
-
- return remote;
- }
-
- static Commit mockCommit(String hash)
- {
- Commit commit = mock(Commit.class);
-
- when(commit.hash()).thenReturn(hash);
-
- return commit;
- }
-
- static File mockFile(String path, String name)
- {
- File file = mock(File.class);
-
- when(file.name()).thenReturn(name);
- when(file.path()).thenReturn(path);
-
- return file;
- }
-}
diff --git a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/StashRemoteUrlFactoryTest.java b/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/StashRemoteUrlFactoryTest.java
deleted file mode 100644
index 196eb9d..0000000
--- a/tests/unit/uk/co/ben_gibson/open/in/git/host/test/RemoteUrlFactory/StashRemoteUrlFactoryTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package uk.co.ben_gibson.open.in.git.host.test.RemoteUrlFactory;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import uk.co.ben_gibson.open.in.git.host.Git.Branch;
-import uk.co.ben_gibson.open.in.git.host.Git.Exception.RemoteException;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteCommitDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.Description.RemoteFileDescription;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.RemoteUrlFactory;
-import uk.co.ben_gibson.open.in.git.host.RemoteUrlFactory.StashRemoteUrlFactory;
-
-import java.net.MalformedURLException;
-
-public class StashRemoteUrlFactoryTest extends RemoteUrlFactoryTest
-{
- @DataProvider
- public static Object[][] commitProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("https://stash.example.com/foo bar/baz"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "https://stash.example.com/projects/foo%20bar/repos/baz/commits/bd7ab0f04151e7409d77caa296617f97352f36d3",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://stash.example.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "http://stash.example.com/projects/foo/repos/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3",
- false
- },
- {
- new RemoteCommitDescription(
- RemoteUrlFactoryTest.mockRemote("http://stash.example.com/foo/bar"),
- RemoteUrlFactoryTest.mockCommit("bd7ab0f04151e7409d77caa296617f97352f36d3")
- ),
- "https://stash.example.com/projects/foo/repos/bar/commits/bd7ab0f04151e7409d77caa296617f97352f36d3",
- true
- },
- };
- }
-
- @DataProvider
- public static Object[][] fileProvider() throws MalformedURLException, RemoteException
- {
- return new Object[][] {
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("https://stash.example.com/foo/bar"),
- Branch.master(),
- RemoteUrlFactoryTest.mockFile("src/Bar.java", "Bar.java"),
- 10
- ),
- "https://stash.example.com/projects/foo/repos/bar/browse/src/Bar.java?at=refs/heads/master#10",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("http://stash.example.com/foo bar/baz"),
- new Branch("dev-wip-[2.0.0]"),
- RemoteUrlFactoryTest.mockFile("src/Foo Bar.java", "Foo Bar.java"),
- null
- ),
- "http://stash.example.com/projects/foo%20bar/repos/baz/browse/src/Foo%20Bar.java?at=refs/heads/dev-wip-[2.0.0]",
- false
- },
- {
- new RemoteFileDescription(
- RemoteUrlFactoryTest.mockRemote("http://stash.example.com/foo/bar"),
- new Branch("dev"),
- RemoteUrlFactoryTest.mockFile("Foo.java", "Foo.java"),
- null
- ),
- "https://stash.example.com/projects/foo/repos/bar/browse/Foo.java?at=refs/heads/dev",
- true
- },
- };
- }
-
- public RemoteUrlFactory remoteUrlFactory()
- {
- return new StashRemoteUrlFactory();
- }
-}