diff --git a/src/Core/RemoteRef.cs b/src/Core/RemoteRef.cs
index 00473d7..621728f 100644
--- a/src/Core/RemoteRef.cs
+++ b/src/Core/RemoteRef.cs
@@ -15,7 +15,25 @@ public override string ToString() =>
public static bool TryParse(string value, [NotNullWhen(true)] out RemoteRef? remote)
{
- Match GetMatch(string input)
+ // Convenience case for some common URL formats pasted from browser
+ if (Uri.TryCreate(value, UriKind.Absolute, out var uri))
+ {
+ var path = uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
+ if (uri.Host == "github.com" && path is [var ghOwner, var ghRepo, "blob", var ghReference, ..])
+ {
+ value = $"{uri.Host}/{ghOwner}/{ghRepo}@{ghReference}:{string.Join('/', path[4..])}";
+ }
+ else if (uri.Host == "gist.github.com" && path is [var gistOwner, var gistId, ..])
+ {
+ value = $"{uri.Host}/{gistOwner}/{gistId}";
+ }
+ else if (uri.Host == "gitlab.com" && path is [var glOwner, var glRepo, "-", "blob", var glReference, ..])
+ {
+ value = $"{uri.Host}/{glOwner}/{glRepo}@{glReference}:{string.Join('/', path[5..])}";
+ }
+ }
+
+ static Match GetMatch(string input)
{
// Try Azure DevOps first since it is more specific
var match = ParseAzureDevOpsExp().Match(input);
diff --git a/src/Tests/DownloadTests.cs b/src/Tests/DownloadTests.cs
index 7cfd898..b2d54d7 100644
--- a/src/Tests/DownloadTests.cs
+++ b/src/Tests/DownloadTests.cs
@@ -4,6 +4,32 @@ namespace Devlooped.Tests;
public class DownloadTests
{
+ [Theory]
+ [InlineData("https://gist.github.com/kzu/0ac826dc7de666546aaedd38e5965381")]
+ [InlineData("https://github.com/kzu/runfile/blob/main/run.cs")]
+ [InlineData("https://gitlab.com/kzu/runcs/-/blob/main/program.cs?ref_type=heads")]
+ public async Task DownloadPublicUnchanged(string value)
+ {
+ Assert.True(RemoteRef.TryParse(value, out var location));
+
+ var provider = DownloadProvider.Create(location);
+ var contents = await provider.GetAsync(location);
+
+ Assert.True(contents.IsSuccessStatusCode);
+
+ var etag = contents.Headers.ETag?.ToString();
+
+ location = location with
+ {
+ ETag = etag,
+ ResolvedUri = contents.OriginalUri
+ };
+
+ var refresh = await provider.GetAsync(location);
+
+ Assert.Equal(HttpStatusCode.NotModified, refresh.StatusCode);
+ }
+
[LocalTheory]
// Requires being authenticated with private kzu's GH repo
[InlineData("github.com/kzu/runfile")]
@@ -13,6 +39,7 @@ public class DownloadTests
[InlineData("github.com/kzu/runfile@211de7614")]
[InlineData("github.com/kzu/runfile@211de761455")]
// Requires running the CLI app once against this private repo and saving a PAT
+ [InlineData("https://gitlab.com/kzu/runfile/-/blob/main/program.cs?ref_type=heads")]
[InlineData("gitlab.com/kzu/runfile")]
[InlineData("gitlab.com/kzu/runfile@v0.1.0")]
[InlineData("gitlab.com/kzu/runfile@dev")]
diff --git a/src/Tests/Tests.cs b/src/Tests/RemoteRefTests.cs
similarity index 87%
rename from src/Tests/Tests.cs
rename to src/Tests/RemoteRefTests.cs
index 315ec60..639d049 100644
--- a/src/Tests/Tests.cs
+++ b/src/Tests/RemoteRefTests.cs
@@ -475,4 +475,61 @@ public void TryParse_WithInvalidOwnerRepoCharacters_ReturnsFalse(string input)
Assert.False(success);
Assert.Null(result);
}
+
+ [Theory]
+ [InlineData("https://github.com/owner/repo/blob/main/file.txt", "github.com", "owner", "repo", "main", "file.txt")]
+ [InlineData("https://github.com/microsoft/vscode/blob/main/src/vs/workbench/workbench.main.ts", "github.com", "microsoft", "vscode", "main", "src/vs/workbench/workbench.main.ts")]
+ [InlineData("https://github.com/octocat/Hello-World/blob/master/README", "github.com", "octocat", "Hello-World", "master", "README")]
+ [InlineData("https://github.com/owner/repo/blob/develop/path/to/file.cs", "github.com", "owner", "repo", "develop", "path/to/file.cs")]
+ [InlineData("https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/System.Console/src/System/Console.cs", "github.com", "dotnet", "runtime", "v8.0.0", "src/libraries/System.Console/src/System/Console.cs")]
+ public void TryParse_GitHubBlobUrls_WorksCorrectly(string input, string expectedHost, string expectedOwner, string expectedRepo, string expectedRef, string expectedPath)
+ {
+ var success = RemoteRef.TryParse(input, out var result);
+
+ Assert.True(success);
+ Assert.NotNull(result);
+ Assert.Equal(expectedHost, result.Host);
+ Assert.Equal(expectedOwner, result.Owner);
+ Assert.Equal(expectedRepo, result.Repo);
+ Assert.Equal(expectedRef, result.Ref);
+ Assert.Equal(expectedPath, result.Path);
+ Assert.NotEmpty(result.TempPath);
+ }
+
+ [Theory]
+ [InlineData("https://gist.github.com/username/123456789", "gist.github.com", "username", "123456789", null, null)]
+ [InlineData("https://gist.github.com/octocat/6cad326836d38bd6", "gist.github.com", "octocat", "6cad326836d38bd6", null, null)]
+ [InlineData("https://gist.github.com/devlooped/abc123def", "gist.github.com", "devlooped", "abc123def", null, null)]
+ public void TryParse_GistUrls_WorksCorrectly(string input, string expectedHost, string expectedOwner, string expectedRepo, string? expectedRef, string? expectedPath)
+ {
+ var success = RemoteRef.TryParse(input, out var result);
+
+ Assert.True(success);
+ Assert.NotNull(result);
+ Assert.Equal(expectedHost, result.Host);
+ Assert.Equal(expectedOwner, result.Owner);
+ Assert.Equal(expectedRepo, result.Repo);
+ Assert.Equal(expectedRef, result.Ref);
+ Assert.Equal(expectedPath, result.Path);
+ Assert.NotEmpty(result.TempPath);
+ }
+
+ [Theory]
+ [InlineData("https://gitlab.com/owner/repo/-/blob/main/file.txt", "gitlab.com", "owner", "repo", "main", "file.txt")]
+ [InlineData("https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/main.js", "gitlab.com", "gitlab-org", "gitlab", "master", "app/assets/javascripts/main.js")]
+ [InlineData("https://gitlab.com/inkscape/inkscape/-/blob/1.3.x/src/ui/widget/color-scales.cpp", "gitlab.com", "inkscape", "inkscape", "1.3.x", "src/ui/widget/color-scales.cpp")]
+ [InlineData("https://gitlab.com/fdroid/fdroidclient/-/blob/master/app/src/main/java/org/fdroid/fdroid/installer/Installer.java", "gitlab.com", "fdroid", "fdroidclient", "master", "app/src/main/java/org/fdroid/fdroid/installer/Installer.java")]
+ public void TryParse_GitLabBlobUrls_WorksCorrectly(string input, string expectedHost, string expectedOwner, string expectedRepo, string expectedRef, string expectedPath)
+ {
+ var success = RemoteRef.TryParse(input, out var result);
+
+ Assert.True(success);
+ Assert.NotNull(result);
+ Assert.Equal(expectedHost, result.Host);
+ Assert.Equal(expectedOwner, result.Owner);
+ Assert.Equal(expectedRepo, result.Repo);
+ Assert.Equal(expectedRef, result.Ref);
+ Assert.Equal(expectedPath, result.Path);
+ Assert.NotEmpty(result.TempPath);
+ }
}
\ No newline at end of file
diff --git a/src/gist/Program.cs b/src/gist/Program.cs
index 020a009..a1af550 100644
--- a/src/gist/Program.cs
+++ b/src/gist/Program.cs
@@ -28,7 +28,12 @@
if (alias != null)
args = [.. parsed.UnmatchedTokens];
-if (args.Length == 0 || !RemoteRef.TryParse("gist.github.com/" + args[0], out var location))
+RemoteRef? location = default;
+var validRef = args.Length > 0 &&
+ (RemoteRef.TryParse("gist.github.com/" + args[0], out location) ||
+ RemoteRef.TryParse(args[0], out location));
+
+if (args.Length == 0 || !validRef || location is null)
{
AnsiConsole.MarkupLine(
$"""
diff --git a/src/gist/Properties/launchSettings.json b/src/gist/Properties/launchSettings.json
index f5a605f..074ba3e 100644
--- a/src/gist/Properties/launchSettings.json
+++ b/src/gist/Properties/launchSettings.json
@@ -6,6 +6,10 @@
"gist": {
"commandName": "Project",
"commandLineArgs": "kzu/0ac826dc7de666546aaedd38e5965381"
+ },
+ "gist url": {
+ "commandName": "Project",
+ "commandLineArgs": "https://gist.github.com/kzu/0ac826dc7de666546aaedd38e5965381"
}
}
}
\ No newline at end of file
diff --git a/src/gist/gist.csproj b/src/gist/gist.csproj
index 05aaf19..1f15084 100644
--- a/src/gist/gist.csproj
+++ b/src/gist/gist.csproj
@@ -46,4 +46,6 @@
+
+
diff --git a/src/runfile/Properties/launchSettings.json b/src/runfile/Properties/launchSettings.json
index 82b8d52..bf41f8c 100644
--- a/src/runfile/Properties/launchSettings.json
+++ b/src/runfile/Properties/launchSettings.json
@@ -31,6 +31,14 @@
"commandName": "Project",
"commandLineArgs": "clean",
"workingDirectory": "C:\\Code\\WhatsApp"
+ },
+ "damian": {
+ "commandName": "Project",
+ "commandLineArgs": "https://github.com/DamianEdwards/runfile/blob/main/flat/filepath.cs"
+ },
+ "gitlab url": {
+ "commandName": "Project",
+ "commandLineArgs": "https://gitlab.com/kzu/runfile/-/blob/main/program.cs?ref_type=heads"
}
}
}
\ No newline at end of file