Added initial code version
This commit is contained in:
Родитель
b1ddd3b4e1
Коммит
e15f671dfd
|
@ -0,0 +1,17 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VS4Mac.GitHistory", "VS4Mac.GitHistory\VS4Mac.GitHistory.csproj", "{B239FA78-B711-4083-8C97-1A72ACBA5215}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B239FA78-B711-4083-8C97-1A72ACBA5215}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B239FA78-B711-4083-8C97-1A72ACBA5215}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B239FA78-B711-4083-8C97-1A72ACBA5215}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B239FA78-B711-4083-8C97-1A72ACBA5215}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,35 @@
|
|||
using MonoDevelop.Components.Commands;
|
||||
using MonoDevelop.Ide;
|
||||
using MonoDevelop.Projects;
|
||||
using MonoDevelop.VersionControl;
|
||||
using VS4Mac.GitHistory.Controllers;
|
||||
using VS4Mac.GitHistory.Views;
|
||||
|
||||
namespace VS4Mac.GitHistory.Commands
|
||||
{
|
||||
public class OpenGitHistoryCommand : CommandHandler
|
||||
{
|
||||
protected override void Run()
|
||||
{
|
||||
var projectFile = IdeApp.ProjectOperations.CurrentSelectedItem as ProjectFile;
|
||||
var gitHistoryView = new GitHistoryView();
|
||||
var skiaSharpFiddleController = new GitHistoryController(gitHistoryView, projectFile);
|
||||
IdeApp.Workbench.OpenDocument(gitHistoryView, true);
|
||||
}
|
||||
|
||||
protected override void Update(CommandInfo info)
|
||||
{
|
||||
base.Update(info);
|
||||
|
||||
if (VersionControlService.IsGloballyDisabled)
|
||||
{
|
||||
info.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var projectFile = IdeApp.ProjectOperations.CurrentSelectedItem as ProjectFile;
|
||||
|
||||
info.Enabled = projectFile != null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace VS4Mac.GitHistory.Controllers.Base
|
||||
{
|
||||
public interface IController
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using MonoDevelop.Projects;
|
||||
using VS4Mac.GitHistory.Controllers.Base;
|
||||
using VS4Mac.GitHistory.Helpers;
|
||||
using VS4Mac.GitHistory.Views;
|
||||
|
||||
namespace VS4Mac.GitHistory.Controllers
|
||||
{
|
||||
public class GitHistoryController : IController
|
||||
{
|
||||
readonly IGitHistoryView _view;
|
||||
readonly ProjectFile _projectFile;
|
||||
|
||||
public GitHistoryController(IGitHistoryView view, ProjectFile projectFile)
|
||||
{
|
||||
_view = view;
|
||||
_projectFile = projectFile;
|
||||
view.SetController(this);
|
||||
}
|
||||
|
||||
public string GetUrl()
|
||||
{
|
||||
if (!InternetHelper.CheckInternetConnection())
|
||||
throw new Exception("Can not access the Git history without a internet connection.");
|
||||
|
||||
if(_projectFile != null)
|
||||
return UrlHelper.GetUrl(_projectFile);
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using CoreGraphics;
|
||||
using Gtk;
|
||||
using MonoDevelop.Components.Mac;
|
||||
using WebKit;
|
||||
|
||||
namespace VS4Mac.GitHistory.Controls
|
||||
{
|
||||
public class ExtendedWebView : WKWebView
|
||||
{
|
||||
public ExtendedWebView() : base(new CGRect(), new WKWebViewConfiguration()) { }
|
||||
|
||||
public Widget GtkWidget => gtkWidget ?? (gtkWidget = GtkMacInterop.NSViewToGtkWidget(this));
|
||||
|
||||
Widget gtkWidget;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using Foundation;
|
||||
using WebKit;
|
||||
|
||||
namespace VS4Mac.GitHistory.Controls
|
||||
{
|
||||
public class GitHistoryWebView : ExtendedWebView, IWKNavigationDelegate
|
||||
{
|
||||
public GitHistoryWebView()
|
||||
{
|
||||
NavigationDelegate = new GitHistoryNavigationDelegate();
|
||||
}
|
||||
|
||||
public WKNavigation InitialNavigation { get; set; }
|
||||
|
||||
public void OpenUrl(string uri)
|
||||
{
|
||||
var url = new NSUrl(uri);
|
||||
var request = new NSUrlRequest(url);
|
||||
InitialNavigation = LoadRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public class GitHistoryNavigationDelegate : WKNavigationDelegate
|
||||
{
|
||||
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
|
||||
{
|
||||
var gitHistoryWebView = webView as GitHistoryWebView;
|
||||
|
||||
if (navigation == gitHistoryWebView.InitialNavigation)
|
||||
{
|
||||
gitHistoryWebView.InitialNavigation = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace VS4Mac.GitHistory.Helpers
|
||||
{
|
||||
public static class InternetHelper
|
||||
{
|
||||
public static bool CheckInternetConnection()
|
||||
{
|
||||
Ping ping = new Ping();
|
||||
|
||||
PingReply pingStatus = ping.Send("www.microsoft.com", 1000);
|
||||
|
||||
if (pingStatus.Status == IPStatus.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MonoDevelop.Core;
|
||||
using MonoDevelop.Projects;
|
||||
using VS4Mac.GitHistory.Models;
|
||||
|
||||
namespace VS4Mac.GitHistory.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Based on ShowInGitHub addin by Lluis Sanchez.
|
||||
/// More information: https://github.com/slluis/ShowInGithub
|
||||
/// </summary>
|
||||
public static class UrlHelper
|
||||
{
|
||||
internal static string GetUrl(ProjectFile projectFile)
|
||||
{
|
||||
string result = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var gitDir = GetGitDir(projectFile.FilePath);
|
||||
|
||||
if (gitDir == null)
|
||||
return null;
|
||||
|
||||
var rootDir = Path.GetDirectoryName(gitDir);
|
||||
|
||||
var gitModulesFile = Path.Combine(rootDir, ".gitmodules");
|
||||
GitConfigSection submoduleSection = null;
|
||||
if (File.Exists(gitModulesFile))
|
||||
{
|
||||
var modulesConfig = new GitConfigFile();
|
||||
modulesConfig.LoadFile(gitModulesFile);
|
||||
foreach (var section in modulesConfig.Sections)
|
||||
{
|
||||
//Checking if file is inside submodule folder
|
||||
if (section.Type == "submodule" &&
|
||||
section.GetValue("path") != null &&
|
||||
Path.GetFullPath(projectFile.FilePath.FileName).StartsWith(Path.Combine(rootDir, Path.Combine(section.GetValue("path").Split('/'))), StringComparison.Ordinal))
|
||||
{
|
||||
gitDir = Path.Combine(gitDir, "modules", Path.Combine(section.GetValue("path").Split('/')));
|
||||
submoduleSection = section;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var file = new GitConfigFile();
|
||||
file.LoadFile(Path.Combine(gitDir, "config"));
|
||||
|
||||
string head = File.ReadAllText(Path.Combine(gitDir, "HEAD")).Trim();
|
||||
string branch;
|
||||
string remote = null;
|
||||
if (head.StartsWith("ref: refs/heads/", StringComparison.CurrentCulture))
|
||||
{
|
||||
branch = head.Substring(16);
|
||||
var sec = file.Sections.FirstOrDefault(s => s.Type == "branch" && s.Name == branch);
|
||||
if (sec != null)
|
||||
remote = sec.GetValue("remote");
|
||||
}
|
||||
else
|
||||
{
|
||||
branch = head;
|
||||
}
|
||||
if (string.IsNullOrEmpty(remote))
|
||||
remote = "origin";
|
||||
var rref = file.Sections.FirstOrDefault(s => s.Type == "remote" && s.Name == remote);
|
||||
if (rref == null)
|
||||
return null;
|
||||
|
||||
var url = rref.GetValue("url");
|
||||
if (url.EndsWith(".git", StringComparison.CurrentCulture))
|
||||
url = url.Substring(0, url.Length - 4);
|
||||
|
||||
string host;
|
||||
|
||||
int k = url.IndexOfAny(new[] { ':', '@' });
|
||||
if (k != -1 && url[k] == '@')
|
||||
{
|
||||
k++;
|
||||
int i = url.IndexOf(':', k);
|
||||
if (i != -1)
|
||||
host = url.Substring(k, i - k);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
|
||||
host = uri.Host;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
int j = url.IndexOf(host, StringComparison.CurrentCulture);
|
||||
var repo = url.Substring(j + host.Length + 1);
|
||||
|
||||
var fileRootDir = submoduleSection == null ? rootDir : Path.Combine(rootDir, Path.Combine(submoduleSection.GetValue("path").Split('/')));
|
||||
string subdir = projectFile.FilePath.ToRelative(fileRootDir);
|
||||
subdir = subdir.Replace('\\', '/');
|
||||
|
||||
result = "https://" + host + "/" + repo + "/blob/" + branch + "/" + subdir;
|
||||
|
||||
result = result.Replace("github.com", "github.githistory.xyz");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
LoggingService.LogError(ex.Message, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string GetGitDir(string subdir)
|
||||
{
|
||||
var r = Path.GetPathRoot(subdir);
|
||||
while (!string.IsNullOrEmpty(subdir) && subdir != r)
|
||||
{
|
||||
var gd = Path.Combine(subdir, ".git");
|
||||
if (Directory.Exists(gd))
|
||||
return gd;
|
||||
subdir = Path.GetDirectoryName(subdir);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace VS4Mac.GitHistory.Models
|
||||
{
|
||||
public class GitConfigFile
|
||||
{
|
||||
List<GitConfigSection> sections = new List<GitConfigSection> ();
|
||||
|
||||
public IEnumerable<GitConfigSection> Sections {
|
||||
get { return sections; }
|
||||
}
|
||||
|
||||
public void LoadFile (string file)
|
||||
{
|
||||
LoadContent (File.ReadAllText (file));
|
||||
}
|
||||
|
||||
public void LoadContent (string content)
|
||||
{
|
||||
var sr = new StringReader (content);
|
||||
GitConfigSection section = null;
|
||||
string line;
|
||||
while ((line = sr.ReadLine ()) != null) {
|
||||
if (line.StartsWith("[", StringComparison.CurrentCulture)) {
|
||||
section = new GitConfigSection ();
|
||||
int i = line.IndexOfAny (new [] { ' ', ']' }, 1);
|
||||
if (i == -1)
|
||||
continue;
|
||||
section.Type = line.Substring (1, i - 1);
|
||||
i = line.IndexOf ('"', i);
|
||||
if (i != -1) {
|
||||
int j = line.LastIndexOf ('"');
|
||||
if (j != -1 && j > i)
|
||||
section.Name = line.Substring (i + 1, j - i - 1);
|
||||
}
|
||||
sections.Add (section);
|
||||
} else if (line.StartsWith("\t", StringComparison.CurrentCulture) && section != null) {
|
||||
int i = line.IndexOf ('=');
|
||||
string key = line.Substring (0, i).Trim ();
|
||||
var value = line.Substring (i + 1).Trim ();
|
||||
section.SetValue (key, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void SaveFile (string file)
|
||||
{
|
||||
File.WriteAllText (file, GetContent ());
|
||||
}
|
||||
|
||||
public string GetContent ()
|
||||
{
|
||||
using (StringWriter sw = new StringWriter ()) {
|
||||
foreach (var s in sections) {
|
||||
sw.Write ("[" + s.Type);
|
||||
if (!string.IsNullOrEmpty (s.Name))
|
||||
sw.Write (" \"" + s.Name + "\"");
|
||||
sw.WriteLine ("]");
|
||||
foreach (var k in s.Keys) {
|
||||
sw.WriteLine ("\t" + k + " = " + s.GetValue (k));
|
||||
}
|
||||
}
|
||||
return sw.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Modified => sections.Any(s => s.Modified);
|
||||
}
|
||||
|
||||
public class GitConfigSection
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
internal bool Modified { get; set; }
|
||||
|
||||
List<Tuple<string,string>> properties = new List<Tuple<string,string>> ();
|
||||
|
||||
public IEnumerable<string> Keys {
|
||||
get { return properties.Select (p => p.Item1); }
|
||||
}
|
||||
|
||||
public string GetValue (string key)
|
||||
{
|
||||
return properties.Where (p => p.Item1 == key).Select (p => p.Item2).FirstOrDefault ();
|
||||
}
|
||||
|
||||
public void SetValue (string key, string value)
|
||||
{
|
||||
var i = properties.FindIndex (t => t.Item1 == key);
|
||||
if (i == -1) {
|
||||
var p = new Tuple<string, string> (key, value);
|
||||
properties.Add (p);
|
||||
Modified = true;
|
||||
} else {
|
||||
if (properties [i].Item2 != value) {
|
||||
properties [i] = new Tuple<string, string> (key, value);
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveValue (string key)
|
||||
{
|
||||
var i = properties.FindIndex (t => t.Item1 == key);
|
||||
if (i != -1) {
|
||||
properties.RemoveAt (i);
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Mono.Addins;
|
||||
using Mono.Addins.Description;
|
||||
|
||||
[assembly: Addin(
|
||||
"GitHistory",
|
||||
Namespace = "MonoDevelop",
|
||||
Version = "0.1"
|
||||
)]
|
||||
|
||||
[assembly: AddinName("Git History")]
|
||||
[assembly: AddinCategory("IDE extensions")]
|
||||
[assembly: AddinDescription("Quickly browse the history of a file from any git repository directly from VS4Mac.")]
|
||||
[assembly: AddinAuthor("Javier Suárez")]
|
||||
[assembly: AddinUrl("https://github.com/jsuarezruiz/VS4Mac-GitHistory")]
|
||||
|
||||
[assembly: CLSCompliant(false)]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Addin
|
||||
id = "MonoDevelop.AssetStudio">
|
||||
<Header>
|
||||
<Name>Git History Addin</Name>
|
||||
<Description>Quickly browse the history of a file from any git repository directly from VS4Mac.</Description>
|
||||
<Author>Javier Suárez Ruiz</Author>
|
||||
<Copyright>Javier Suárez Ruiz</Copyright>
|
||||
<Url>https://github.com/jsuarezruiz/VSMac-GitHistory</Url>
|
||||
</Header>
|
||||
<Extension path = "/MonoDevelop/Ide/Commands/VersionControl">
|
||||
<Command id = "GitHistory.Commands.OpenGitHistory"
|
||||
defaultHandler = "VS4Mac.GitHistory.Commands.OpenGitHistoryCommand"
|
||||
_label = "Show Git History" />
|
||||
</Extension>
|
||||
<Extension path = "/MonoDevelop/Ide/ContextMenu/ProjectPad/VersionControl">
|
||||
<SeparatorItem insertafter="MonoDevelop.VersionControl.Commands.SolutionStatus"/>
|
||||
<Condition id="ItemType" value="IFileItem">
|
||||
<CommandItem id = "GitHistory.Commands.OpenGitHistory" />
|
||||
</Condition>
|
||||
</Extension>
|
||||
</Addin>
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MonoDevelop.Addins" Version="0.4.7" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AddinReference Include="MonoDevelop.VersionControl" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,9 @@
|
|||
using VS4Mac.GitHistory.Controllers.Base;
|
||||
|
||||
namespace VS4Mac.GitHistory.Views.Base
|
||||
{
|
||||
public interface IView
|
||||
{
|
||||
void SetController(IController controller);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using MonoDevelop.Core;
|
||||
using MonoDevelop.Ide.Gui;
|
||||
using VS4Mac.GitHistory.Controllers;
|
||||
using VS4Mac.GitHistory.Controllers.Base;
|
||||
using VS4Mac.GitHistory.Controls;
|
||||
using VS4Mac.GitHistory.Views.Base;
|
||||
using Xwt;
|
||||
using Xwt.Drawing;
|
||||
|
||||
namespace VS4Mac.GitHistory.Views
|
||||
{
|
||||
public interface IGitHistoryView : IView
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class GitHistoryView : AbstractXwtViewContent, IGitHistoryView
|
||||
{
|
||||
VBox _mainBox;
|
||||
GitHistoryWebView _gitHistoryWebView;
|
||||
Widget _xwtGitHistoryWebView;
|
||||
Label _errorLabel;
|
||||
|
||||
GitHistoryController _controller;
|
||||
|
||||
public GitHistoryView()
|
||||
{
|
||||
Init();
|
||||
BuildGui();
|
||||
}
|
||||
|
||||
public override Widget Widget => _mainBox;
|
||||
|
||||
public override bool IsViewOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsFile
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
_mainBox = new VBox();
|
||||
_gitHistoryWebView = new GitHistoryWebView();
|
||||
|
||||
_errorLabel = new Label
|
||||
{
|
||||
Font = Font.SystemSansSerifFont.WithSize(18),
|
||||
TextColor = Styles.SecondaryTextColor,
|
||||
HorizontalPlacement = WidgetPlacement.Center,
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
void BuildGui()
|
||||
{
|
||||
ContentName = "Git History";
|
||||
|
||||
_xwtGitHistoryWebView = Toolkit.CurrentEngine.WrapWidget(_gitHistoryWebView);
|
||||
_mainBox.PackStart(_xwtGitHistoryWebView, true);
|
||||
_mainBox.PackStart(_errorLabel, true);
|
||||
}
|
||||
|
||||
public void SetController(IController controller)
|
||||
{
|
||||
_controller = (GitHistoryController)controller;
|
||||
|
||||
LoadUrl();
|
||||
}
|
||||
|
||||
void LoadUrl()
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = _controller.GetUrl();
|
||||
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
_gitHistoryWebView.OpenUrl(url);
|
||||
else
|
||||
ShowError("Can not get the Git history of the file.");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
LoggingService.LogError(ex.Message, ex);
|
||||
ShowError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowError (string errorMessage)
|
||||
{
|
||||
_errorLabel.Text = errorMessage;
|
||||
_errorLabel.Visible = true;
|
||||
_xwtGitHistoryWebView.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче