diff --git a/src/GitHub.App/SampleData/SampleViewModels.cs b/src/GitHub.App/SampleData/SampleViewModels.cs
index 57746efe6c..1ee0298e87 100644
--- a/src/GitHub.App/SampleData/SampleViewModels.cs
+++ b/src/GitHub.App/SampleData/SampleViewModels.cs
@@ -409,6 +409,11 @@ public void SetIcon(bool isPrivate, bool isFork)
{
}
+ public UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
+ {
+ return null;
+ }
+
public string Name { get; set; }
public UriString CloneUrl { get; set; }
public string LocalPath { get; set; }
diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj
index b6ea5c21a4..23d21adf55 100644
--- a/src/GitHub.Exports/GitHub.Exports.csproj
+++ b/src/GitHub.Exports/GitHub.Exports.csproj
@@ -112,6 +112,7 @@
+
diff --git a/src/GitHub.Exports/Models/ISimpleRepositoryModel.cs b/src/GitHub.Exports/Models/ISimpleRepositoryModel.cs
index 197a09378f..e1f6ece70c 100644
--- a/src/GitHub.Exports/Models/ISimpleRepositoryModel.cs
+++ b/src/GitHub.Exports/Models/ISimpleRepositoryModel.cs
@@ -18,5 +18,6 @@ public interface ISimpleRepositoryModel : INotifyPropertyChanged
/// Updates the url information based on the local path
///
void Refresh();
+ UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1);
}
}
diff --git a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
index 9bb2672660..9e4aaa6af5 100644
--- a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
+++ b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs
@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Linq;
using GitHub.Primitives;
using GitHub.UI;
using GitHub.VisualStudio.Helpers;
@@ -10,7 +11,7 @@
namespace GitHub.Models
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
- public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, INotifyPropertySource, IEquatable
+ public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, IEquatable
{
public SimpleRepositoryModel(string name, UriString cloneUrl, string localPath = null)
{
@@ -53,6 +54,60 @@ public void Refresh()
CloneUrl = uri;
}
+ ///
+ /// Generates a http(s) url to the repository in the remote server, optionally
+ /// pointing to a specific file and specific line range in it.
+ ///
+ /// The file to generate an url to. Optional.
+ /// A specific line, or (if specifying the as well) the start of a range
+ /// The end of a line range on the specified file.
+ /// An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally
+ public UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
+ {
+ if (CloneUrl == null)
+ return null;
+
+ var sha = HeadSha;
+ // this also incidentally checks whether the repo has a valid LocalPath
+ if (String.IsNullOrEmpty(sha))
+ return CloneUrl.ToRepositoryUrl().AbsoluteUri;
+
+ if (path != null && Path.IsPathRooted(path))
+ {
+ // if the path root doesn't match the repository local path, then ignore it
+ if (!path.StartsWith(LocalPath, StringComparison.OrdinalIgnoreCase))
+ {
+ Debug.Assert(false, String.Format(CultureInfo.CurrentCulture, "GenerateUrl: path {0} doesn't match repository {1}", path, LocalPath));
+ path = null;
+ }
+ else
+ path = path.Substring(LocalPath.Length + 1);
+ }
+
+ return new UriString(GenerateUrl(CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine));
+ }
+
+ const string CommitFormat = "{0}/commit/{1}";
+ const string BlobFormat = "{0}/blob/{1}/{2}";
+ const string StartLineFormat = "{0}#L{1}";
+ const string EndLineFormat = "{0}-L{1}";
+ static string GenerateUrl(string basePath, string sha, string path, int startLine = -1, int endLine = -1)
+ {
+ if (sha == null)
+ return basePath;
+
+ if (String.IsNullOrEmpty(path))
+ return String.Format(CultureInfo.InvariantCulture, CommitFormat, basePath, sha);
+
+ var ret = String.Format(CultureInfo.InvariantCulture, BlobFormat, basePath, sha, path.Replace(@"\", "/"));
+ if (startLine < 0)
+ return ret;
+ ret = String.Format(CultureInfo.InvariantCulture, StartLineFormat, ret, startLine);
+ if (endLine < 0)
+ return ret;
+ return String.Format(CultureInfo.InvariantCulture, EndLineFormat, ret, endLine);
+ }
+
public string Name { get; }
UriString cloneUrl;
public UriString CloneUrl { get { return cloneUrl; } set { cloneUrl = value; this.RaisePropertyChange(); } }
@@ -60,6 +115,15 @@ public void Refresh()
Octicon icon;
public Octicon Icon { get { return icon; } set { icon = value; this.RaisePropertyChange(); } }
+ public string HeadSha
+ {
+ get
+ {
+ var repo = GitService.GitServiceHelper.GetRepo(LocalPath);
+ return repo?.Commits.FirstOrDefault()?.Sha ?? String.Empty;
+ }
+ }
+
///
/// Note: We don't consider CloneUrl a part of the hash code because it can change during the lifetime
/// of a repository. Equals takes care of any hash collisions because of this
diff --git a/src/GitHub.Exports/Services/IActiveDocumentSnapshot.cs b/src/GitHub.Exports/Services/IActiveDocumentSnapshot.cs
new file mode 100644
index 0000000000..60fcdca44b
--- /dev/null
+++ b/src/GitHub.Exports/Services/IActiveDocumentSnapshot.cs
@@ -0,0 +1,9 @@
+namespace GitHub.VisualStudio
+{
+ public interface IActiveDocumentSnapshot
+ {
+ string Name { get; }
+ int StartLine { get; }
+ int EndLine { get; }
+ }
+}
diff --git a/src/GitHub.VisualStudio/Base/MenuBase.cs b/src/GitHub.VisualStudio/Base/MenuBase.cs
index aa9be9181a..40fa6cd259 100644
--- a/src/GitHub.VisualStudio/Base/MenuBase.cs
+++ b/src/GitHub.VisualStudio/Base/MenuBase.cs
@@ -1,4 +1,8 @@
using System;
+using System.Globalization;
+using GitHub.Extensions;
+using GitHub.Models;
+using GitHub.Services;
namespace GitHub.VisualStudio
{
@@ -7,6 +11,8 @@ public abstract class MenuBase
readonly IServiceProvider serviceProvider;
protected IServiceProvider ServiceProvider { get { return serviceProvider; } }
+ protected ISimpleRepositoryModel ActiveRepo { get; private set; }
+
protected MenuBase()
{
}
@@ -15,5 +21,30 @@ protected MenuBase(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
+
+ void RefreshRepo()
+ {
+ ActiveRepo = ServiceProvider.GetExportedValue().ActiveRepo;
+
+ if (ActiveRepo == null)
+ {
+ var vsservices = ServiceProvider.GetExportedValue();
+ string path = vsservices?.GetActiveRepoPath() ?? String.Empty;
+ try
+ {
+ ActiveRepo = !String.IsNullOrEmpty(path) ? new SimpleRepositoryModel(path) : null;
+ }
+ catch (Exception ex)
+ {
+ VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "{0}: Error loading the repository from '{1}'. {2}", GetType(), path, ex));
+ }
+ }
+ }
+
+ protected bool IsGitHubRepo()
+ {
+ RefreshRepo();
+ return ActiveRepo?.CloneUrl?.RepositoryName != null;
+ }
}
}
diff --git a/src/GitHub.VisualStudio/Base/TeamExplorerNavigationItemBase.cs b/src/GitHub.VisualStudio/Base/TeamExplorerNavigationItemBase.cs
index 1a1493dd24..b57d4b145b 100644
--- a/src/GitHub.VisualStudio/Base/TeamExplorerNavigationItemBase.cs
+++ b/src/GitHub.VisualStudio/Base/TeamExplorerNavigationItemBase.cs
@@ -45,11 +45,9 @@ void OnThemeChanged()
{
try
{
- var color = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowTextColorKey);
- var brightness = color.GetBrightness();
- var dark = brightness > 0.5f;
-
- Icon = SharedResources.GetDrawingForIcon(octicon, dark ? Colors.DarkThemeNavigationItem : Colors.LightThemeNavigationItem, dark ? "dark" : "light");
+ var theme = Colors.DetectTheme();
+ var dark = theme == "Dark";
+ Icon = SharedResources.GetDrawingForIcon(octicon, dark ? Colors.DarkThemeNavigationItem : Colors.LightThemeNavigationItem, theme);
}
catch (ArgumentNullException)
{
diff --git a/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj b/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj
index 214d4a2917..c6f641b380 100644
--- a/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj
+++ b/src/GitHub.VisualStudio/GitHub.VisualStudio.csproj
@@ -212,8 +212,12 @@
+
+
+
+
@@ -340,6 +344,14 @@
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
Designer
@@ -360,6 +372,18 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
diff --git a/src/GitHub.VisualStudio/GitHub.VisualStudio.imagemanifest b/src/GitHub.VisualStudio/GitHub.VisualStudio.imagemanifest
index c2b9f7a2a2..f798fa065b 100644
--- a/src/GitHub.VisualStudio/GitHub.VisualStudio.imagemanifest
+++ b/src/GitHub.VisualStudio/GitHub.VisualStudio.imagemanifest
@@ -8,6 +8,8 @@
+
+
@@ -26,5 +28,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/GitHub.VisualStudio/GitHub.VisualStudio.vsct b/src/GitHub.VisualStudio/GitHub.VisualStudio.vsct
index 1b642a837a..934c23106f 100644
--- a/src/GitHub.VisualStudio/GitHub.VisualStudio.vsct
+++ b/src/GitHub.VisualStudio/GitHub.VisualStudio.vsct
@@ -38,7 +38,11 @@
-