|
2 | 2 | using System.Diagnostics; |
3 | 3 | using System.Globalization; |
4 | 4 | using System.IO; |
| 5 | +using System.Linq; |
5 | 6 | using GitHub.Primitives; |
6 | 7 | using GitHub.UI; |
7 | 8 | using GitHub.VisualStudio.Helpers; |
|
10 | 11 | namespace GitHub.Models |
11 | 12 | { |
12 | 13 | [DebuggerDisplay("{DebuggerDisplay,nq}")] |
13 | | - public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, INotifyPropertySource, IEquatable<SimpleRepositoryModel> |
| 14 | + public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, IEquatable<SimpleRepositoryModel> |
14 | 15 | { |
15 | 16 | public SimpleRepositoryModel(string name, UriString cloneUrl, string localPath = null) |
16 | 17 | { |
@@ -53,13 +54,76 @@ public void Refresh() |
53 | 54 | CloneUrl = uri; |
54 | 55 | } |
55 | 56 |
|
| 57 | + /// <summary> |
| 58 | + /// Generates a http(s) url to the repository in the remote server, optionally |
| 59 | + /// pointing to a specific file and specific line range in it. |
| 60 | + /// </summary> |
| 61 | + /// <param name="path">The file to generate an url to. Optional.</param> |
| 62 | + /// <param name="startLine">A specific line, or (if specifying the <paramref name="endLine"/> as well) the start of a range</param> |
| 63 | + /// <param name="endLine">The end of a line range on the specified file.</param> |
| 64 | + /// <returns>An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally</returns> |
| 65 | + public UriString GenerateUrl(string path = null, int startLine = -1, int endLine = -1) |
| 66 | + { |
| 67 | + if (CloneUrl == null) |
| 68 | + return null; |
| 69 | + |
| 70 | + var sha = HeadSha; |
| 71 | + // this also incidentally checks whether the repo has a valid LocalPath |
| 72 | + if (String.IsNullOrEmpty(sha)) |
| 73 | + return CloneUrl.ToRepositoryUrl().AbsoluteUri; |
| 74 | + |
| 75 | + if (path != null && Path.IsPathRooted(path)) |
| 76 | + { |
| 77 | + // if the path root doesn't match the repository local path, then ignore it |
| 78 | + if (!path.StartsWith(LocalPath, StringComparison.OrdinalIgnoreCase)) |
| 79 | + { |
| 80 | + Debug.Assert(false, String.Format(CultureInfo.CurrentCulture, "GenerateUrl: path {0} doesn't match repository {1}", path, LocalPath)); |
| 81 | + path = null; |
| 82 | + } |
| 83 | + else |
| 84 | + path = path.Substring(LocalPath.Length + 1); |
| 85 | + } |
| 86 | + |
| 87 | + return new UriString(GenerateUrl(CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine)); |
| 88 | + } |
| 89 | + |
| 90 | + const string CommitFormat = "{0}/commit/{1}"; |
| 91 | + const string BlobFormat = "{0}/blob/{1}/{2}"; |
| 92 | + const string StartLineFormat = "{0}#L{1}"; |
| 93 | + const string EndLineFormat = "{0}-L{1}"; |
| 94 | + static string GenerateUrl(string basePath, string sha, string path, int startLine = -1, int endLine = -1) |
| 95 | + { |
| 96 | + if (sha == null) |
| 97 | + return basePath; |
| 98 | + |
| 99 | + if (String.IsNullOrEmpty(path)) |
| 100 | + return String.Format(CultureInfo.InvariantCulture, CommitFormat, basePath, sha); |
| 101 | + |
| 102 | + var ret = String.Format(CultureInfo.InvariantCulture, BlobFormat, basePath, sha, path.Replace(@"\", "/")); |
| 103 | + if (startLine < 0) |
| 104 | + return ret; |
| 105 | + ret = String.Format(CultureInfo.InvariantCulture, StartLineFormat, ret, startLine); |
| 106 | + if (endLine < 0) |
| 107 | + return ret; |
| 108 | + return String.Format(CultureInfo.InvariantCulture, EndLineFormat, ret, endLine); |
| 109 | + } |
| 110 | + |
56 | 111 | public string Name { get; } |
57 | 112 | UriString cloneUrl; |
58 | 113 | public UriString CloneUrl { get { return cloneUrl; } set { cloneUrl = value; this.RaisePropertyChange(); } } |
59 | 114 | public string LocalPath { get; } |
60 | 115 | Octicon icon; |
61 | 116 | public Octicon Icon { get { return icon; } set { icon = value; this.RaisePropertyChange(); } } |
62 | 117 |
|
| 118 | + public string HeadSha |
| 119 | + { |
| 120 | + get |
| 121 | + { |
| 122 | + var repo = GitService.GitServiceHelper.GetRepo(LocalPath); |
| 123 | + return repo?.Commits.FirstOrDefault()?.Sha ?? String.Empty; |
| 124 | + } |
| 125 | + } |
| 126 | + |
63 | 127 | /// <summary> |
64 | 128 | /// Note: We don't consider CloneUrl a part of the hash code because it can change during the lifetime |
65 | 129 | /// of a repository. Equals takes care of any hash collisions because of this |
|
0 commit comments