mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
363 lines
8 KiB
C++
363 lines
8 KiB
C++
#include "ide.h"
|
|
|
|
struct RepoDiff : DiffDlg {
|
|
FrameTop<ParentCtrl> pane;
|
|
DropList r, branch;
|
|
Button copy_hash, copy_log;
|
|
Button github;
|
|
|
|
String repo_dir;
|
|
int kind;
|
|
bool modal = false;
|
|
int line = 0;
|
|
|
|
void LoadGit();
|
|
void Load();
|
|
void Set(const String& f);
|
|
void SyncTitle();
|
|
|
|
typedef RepoDiff CLASSNAME;
|
|
|
|
RepoDiff();
|
|
};
|
|
|
|
|
|
void RepoDiff::SyncTitle()
|
|
{
|
|
String h = GetFileName(editfile);
|
|
String m = ~r.GetValue();
|
|
int q = m.FindAfter("@b \1");
|
|
if(q >= 0) {
|
|
m = m.Mid(q);
|
|
q = m.Find('\1');
|
|
if(q >= 0)
|
|
h << " " << m.Mid(0, q);
|
|
}
|
|
Title(h);
|
|
}
|
|
|
|
void RepoDiff::Set(const String& f)
|
|
{
|
|
repo_dir = f;
|
|
kind = GetRepo(repo_dir);
|
|
editfile = f;
|
|
if(kind == SVN_DIR) {
|
|
pane << r.SizePos();
|
|
WaitCursor wait;
|
|
String log = HostSys("svn log " + f);
|
|
if(log.IsVoid()) {
|
|
Exclamation("Error executing 'svn log'");
|
|
return;
|
|
}
|
|
StringStream ss(log);
|
|
while(!ss.IsEof()) {
|
|
String l = ss.GetLine();
|
|
if(l[0] == 'r') {
|
|
Vector<String> h = Split(l, '|');
|
|
if(h.GetCount() > 3) {
|
|
String rev = TrimBoth(h[0]);
|
|
String s = rev;
|
|
Vector<String> t = Split(h[2], ' ');
|
|
if(t.GetCount() > 1)
|
|
s << ' ' << t[0];
|
|
s << ' ' << TrimBoth(h[1]);
|
|
while(!ss.IsEof()) {
|
|
l = ss.GetLine();
|
|
if(l.GetCount()) {
|
|
if(l[0] != '-')
|
|
s << ": " << l;
|
|
break;
|
|
}
|
|
}
|
|
r.Add(rev, s);
|
|
}
|
|
}
|
|
}
|
|
if(r.GetCount() == 0) {
|
|
Exclamation("No parsable history for the file");
|
|
return;
|
|
}
|
|
|
|
if(r.GetCount())
|
|
r.SetIndex(0);
|
|
Load();
|
|
}
|
|
|
|
if(kind == GIT_DIR) {
|
|
pane << branch.LeftPos(0, Zx(100)).VSizePos()
|
|
<< r.HSizePos(Zx(100) + DPI(2), 2 * Zx(80) + DPI(4) + DPI(20)).VSizePos()
|
|
<< copy_hash.RightPos(0, Zx(79)).VSizePos()
|
|
<< copy_log.RightPos(Zx(80), Zx(79)).VSizePos()
|
|
<< github.RightPos(2 * Zx(80) + DPI(2), DPI(20)).VSizePos();
|
|
|
|
copy_hash.SetLabel("Copy Hash");
|
|
|
|
auto GetHash = [=] {
|
|
String h = ~~r;
|
|
String commit, path;
|
|
SplitTo(h, ':', commit, path);
|
|
return commit;
|
|
};
|
|
|
|
copy_hash << [=] {
|
|
WriteClipboardText(GetHash());
|
|
};
|
|
|
|
copy_log.SetLabel("Copy Log");
|
|
copy_log << [=] {
|
|
CopyGitRevisions(r);
|
|
};
|
|
|
|
github.SetImage(IdeImg::GitHub());
|
|
String origin = HostSys("git -C " + GetFileFolder(f) + " remote get-url origin");
|
|
if(origin.StartsWith("https://github.com/")) {
|
|
github.Enable();
|
|
origin.TrimEnd("\n");
|
|
origin.TrimEnd("\r");
|
|
origin.TrimEnd(".git");
|
|
github << [=] {
|
|
LaunchWebBrowser(origin + "/commit/" + GetHash());
|
|
};
|
|
}
|
|
else
|
|
github.Disable();
|
|
|
|
LoadBranches(branch, GetFileFolder(f));
|
|
LoadGit();
|
|
}
|
|
|
|
DiffDlg::Set(f);
|
|
|
|
SyncTitle();
|
|
}
|
|
|
|
void LoadBranches(DropList& branch, const String& dir)
|
|
{
|
|
branch.Clear();
|
|
String branches = GitCmd(dir, "branch --all");
|
|
StringStream ss(branches);
|
|
String author, date, commit;
|
|
int ci = -1;
|
|
auto Add = [&](const String& l) {
|
|
String s = l.Mid(2);
|
|
int q = s.ReverseFind('/');
|
|
if(q >= 0)
|
|
branch.Add(s, s.Mid(q));
|
|
else
|
|
branch.Add(s);
|
|
};
|
|
while(!ss.IsEof()) {
|
|
String l = ss.GetLine();
|
|
if(l.StartsWith("* ")) {
|
|
ci = branch.GetCount();
|
|
Add(l);
|
|
}
|
|
if(l.StartsWith(" "))
|
|
Add(l);
|
|
}
|
|
|
|
if(ci >= 0)
|
|
branch.SetIndex(ci);
|
|
}
|
|
|
|
void CopyGitRevisions(const DropList& dl)
|
|
{
|
|
String qtf;
|
|
for(int i = 0; i < dl.GetCount(); i++) {
|
|
String s = ~dl.GetValue(i);
|
|
s.TrimStart("\1");
|
|
MergeWith(qtf, "&", s);
|
|
}
|
|
RichText txt = ParseQTF(qtf);
|
|
ClearClipboard();
|
|
AppendClipboard(pick(txt));
|
|
}
|
|
|
|
void LoadGitRevisions(DropList& r, const String& dir, const String& branch, const String& file)
|
|
{
|
|
String gitcmd = "log --format=medium --date=short --name-only ";
|
|
if(file.GetCount())
|
|
gitcmd << " --follow ";
|
|
gitcmd << branch;
|
|
if(file.GetCount())
|
|
gitcmd << " -- " << GetFileName(file);
|
|
String log = GitCmd(dir, gitcmd);
|
|
StringStream ss(log);
|
|
String author, date, commit, path, msg;
|
|
r.ClearList();
|
|
auto AddCommit = [&] {
|
|
if(commit.GetCount()) {
|
|
String h = commit;
|
|
if(h.GetCount() > 6)
|
|
h.Trim(6);
|
|
r.Add(IsNull(file) ? commit : commit + ":" + path,
|
|
"\1[g [@b \1" + date + "\1] [@g \1" + h + "\1] [@r \1" + author + "\1]: "
|
|
"[* \1" + Join(Split(msg, CharFilterWhitespace), " ") + "\1]");
|
|
}
|
|
date = commit = author = msg = Null;
|
|
};
|
|
while(!ss.IsEof()) {
|
|
String l = TrimBoth(ss.GetLine());
|
|
if(l.GetCount() == 0 && msg.GetCount() == 0) {
|
|
while(!ss.IsEof()) {
|
|
l = ss.GetLine();
|
|
if(l.GetCount())
|
|
break;
|
|
}
|
|
msg = l;
|
|
while(!ss.IsEof()) {
|
|
l = ss.GetLine();
|
|
if(l.GetCount() == 0)
|
|
break;
|
|
msg << l;
|
|
}
|
|
}
|
|
else
|
|
if(l.TrimStart("Author:")) {
|
|
int q = l.Find('<');
|
|
author = TrimBoth(q >= 0 ? l.Mid(0, q) : l);
|
|
}
|
|
else
|
|
if(l.TrimStart("Date:"))
|
|
date = TrimBoth(l);
|
|
else
|
|
if(l.TrimStart("commit")) {
|
|
AddCommit();
|
|
commit = TrimBoth(l);
|
|
}
|
|
else
|
|
if(l.GetCount())
|
|
path = l;
|
|
}
|
|
AddCommit();
|
|
|
|
if(r.GetCount())
|
|
r.SetIndex(0);
|
|
}
|
|
|
|
void RepoDiff::LoadGit()
|
|
{
|
|
LoadGitRevisions(r, GetFileFolder(editfile), ~~branch, editfile);
|
|
Load();
|
|
}
|
|
|
|
void RepoDiff::Load()
|
|
{
|
|
if(IsNull(r))
|
|
return;
|
|
if(kind == SVN_DIR)
|
|
extfile = HostSys("svn cat " + editfile + '@' + AsString(~r));
|
|
Vector<TextCompareCtrl::Blame> bl;
|
|
if(kind == GIT_DIR) {
|
|
String h = ~~r;
|
|
String commit, path;
|
|
SplitTo(h, ':', commit, path);
|
|
if(path.GetCount())
|
|
extfile = GitCmd(repo_dir, "show " + h);
|
|
else
|
|
extfile = GitCmd(GetFileFolder(editfile), "show " + commit + ":./" + GetFileName(editfile));
|
|
extfile.Clear();
|
|
StringStream ss(GitCmd(GetFileFolder(editfile), "blame -p " + GetFileName(editfile) + " " + commit));
|
|
VectorMap<String, TextCompareCtrl::Blame> blame;
|
|
while(!ss.IsEof()) {
|
|
String ln = ss.GetLine();
|
|
Vector<String> h = Split(ln, ' ');
|
|
if(h.GetCount() == 0) break;
|
|
String hash = h[0];
|
|
TextCompareCtrl::Blame& b = blame.GetAdd(hash);
|
|
while(!ss.IsEof()) {
|
|
String ln = ss.GetLine();
|
|
if(ln.TrimStart("\t")) {
|
|
auto& m = bl.Add();
|
|
m = b;
|
|
m.hash = hash;
|
|
extfile << ln << "\n";
|
|
break;
|
|
}
|
|
if(ln.TrimStart("author "))
|
|
b.author = ln;
|
|
else
|
|
if(ln.TrimStart("summary "))
|
|
b.summary = ln;
|
|
else
|
|
if(ln.TrimStart("author-time "))
|
|
b.time = Time(1970, 1, 1) + Atoi(ln);
|
|
}
|
|
}
|
|
}
|
|
|
|
diff.Set(backup = LoadFile(editfile), extfile);
|
|
if(bl.GetCount()) {
|
|
diff.SetPos(4700);
|
|
diff.right.WhenBlame = [=](const String& hash) {
|
|
auto FindHash = [&](RepoDiff& d) {
|
|
for(int i = 0; i < d.r.GetCount(); i++) {
|
|
String h = d.r.GetKey(i);
|
|
String commit, path;
|
|
SplitTo(h, ':', commit, path);
|
|
if(commit == hash) {
|
|
d.r.SetIndex(i);
|
|
d.Load();
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
if(modal)
|
|
FindHash(*this);
|
|
else {
|
|
RepoDiff *rd = TheIde()->RunRepoDiff(editfile, line);
|
|
if(rd)
|
|
FindHash(*rd);
|
|
}
|
|
};
|
|
}
|
|
diff.right.PickBlame(pick(bl));
|
|
SyncTitle();
|
|
}
|
|
|
|
RepoDiff::RepoDiff()
|
|
{
|
|
pane.Height(EditField::GetStdHeight());
|
|
r.SetDropLines(32);
|
|
branch.SetDropLines(32);
|
|
Icon(IdeImg::SvnDiff());
|
|
diff.InsertFrameRight(pane);
|
|
r << [=] { Load(); };
|
|
branch << [=] { LoadGit(); };
|
|
Sizeable().Zoomable();
|
|
serialize_placement = false;
|
|
Rect r = TheIde()->GetWorkArea();
|
|
r.Deflate(DPI(30), DPI(40));
|
|
SetRect(r);
|
|
}
|
|
|
|
void RunRepoDiff(const String& filepath, int line)
|
|
{
|
|
if(IsNull(filepath))
|
|
return;
|
|
RepoDiff dlg;
|
|
dlg.modal = true;
|
|
dlg.Set(filepath);
|
|
if(line >= 0)
|
|
dlg.diff.left.SetCursor(line + 1);
|
|
dlg.Execute();
|
|
}
|
|
|
|
RepoDiff *Ide::RunRepoDiff(const String& filepath, int line)
|
|
{
|
|
if(IsNull(filepath))
|
|
return nullptr;
|
|
RepoDiff& dlg = CreateNewWindow<RepoDiff>();
|
|
dlg.line = line;
|
|
dlg.Set(filepath);
|
|
if(line >= 0)
|
|
dlg.diff.left.SetCursor(line + 1);
|
|
dlg.diff.WhenRightLine =
|
|
dlg.diff.WhenLeftLine = [=](int line) {
|
|
EditFile(filepath);
|
|
editor.SetCursor(editor.GetPos64(line));
|
|
editor.SetFocus();
|
|
};
|
|
dlg.OpenMain();
|
|
return &dlg;
|
|
}
|