mirror of
https://gitlab.gnome.org/GNOME/gitg
synced 2024-10-06 07:59:12 +00:00
Merge branch 'highlighting'
This commit is contained in:
commit
0573e0756b
|
@ -185,6 +185,7 @@ namespace GitgCommit
|
|||
d_main.button_stage.visible = patchable;
|
||||
d_main.button_discard.visible = true;
|
||||
|
||||
view.new_is_workdir = true;
|
||||
view.diff = d;
|
||||
}
|
||||
catch
|
||||
|
@ -460,6 +461,7 @@ namespace GitgCommit
|
|||
return;
|
||||
}
|
||||
|
||||
view.diff_view.new_is_workdir = false;
|
||||
view.diff_view.diff = diff;
|
||||
}
|
||||
|
||||
|
@ -573,6 +575,7 @@ namespace GitgCommit
|
|||
d_main.button_stage.visible = patchable;
|
||||
d_main.button_discard.visible = false;
|
||||
|
||||
view.new_is_workdir = false;
|
||||
view.diff = d;
|
||||
}
|
||||
catch
|
||||
|
@ -1807,6 +1810,7 @@ namespace GitgCommit
|
|||
}
|
||||
});
|
||||
|
||||
d_main.diff_view.repository = application.repository;
|
||||
d_main.diff_view.default_collapse_all = false;
|
||||
|
||||
d_main.sidebar.deselected.connect(() => {
|
||||
|
|
|
@ -20,6 +20,21 @@
|
|||
[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-diff-view-file.ui")]
|
||||
class Gitg.DiffViewFile : Gtk.Grid
|
||||
{
|
||||
private enum RegionType
|
||||
{
|
||||
ADDED,
|
||||
REMOVED,
|
||||
CONTEXT
|
||||
}
|
||||
|
||||
private struct Region
|
||||
{
|
||||
public RegionType type;
|
||||
public int buffer_line_start;
|
||||
public int source_line_start;
|
||||
public int length;
|
||||
}
|
||||
|
||||
[GtkChild( name = "expander" )]
|
||||
private Gtk.Expander d_expander;
|
||||
|
||||
|
@ -39,10 +54,21 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
private uint d_removed;
|
||||
private bool d_expanded;
|
||||
private int64 d_doffset;
|
||||
|
||||
private Gee.HashMap<int, PatchSet.Patch?> d_lines;
|
||||
|
||||
private DiffViewFileSelectable d_selectable;
|
||||
private DiffViewLinesRenderer d_old_lines;
|
||||
private DiffViewLinesRenderer d_new_lines;
|
||||
private DiffViewLinesRenderer d_sym_lines;
|
||||
private bool d_highlight;
|
||||
private Cancellable? d_higlight_cancellable;
|
||||
private Gtk.SourceBuffer? d_old_highlight_buffer;
|
||||
private Gtk.SourceBuffer? d_new_highlight_buffer;
|
||||
private bool d_old_highlight_ready;
|
||||
private bool d_new_highlight_ready;
|
||||
private Region[] d_regions;
|
||||
private bool d_constructed;
|
||||
|
||||
public bool new_is_workdir { get; construct set; }
|
||||
|
||||
public bool expanded
|
||||
{
|
||||
|
@ -100,32 +126,29 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
}
|
||||
}
|
||||
|
||||
public int maxlines
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public int maxlines { get; set; }
|
||||
public bool has_selection { get; private set; }
|
||||
public bool handle_selection { get; construct set; }
|
||||
public Ggit.DiffDelta? delta { get; construct set; }
|
||||
public Repository repository { get; construct set; }
|
||||
|
||||
public bool has_selection
|
||||
public bool highlight
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
get { return d_highlight; }
|
||||
|
||||
public Ggit.DiffDelta delta
|
||||
{
|
||||
get;
|
||||
construct set;
|
||||
}
|
||||
construct set
|
||||
{
|
||||
d_highlight = value;
|
||||
update_highlight();
|
||||
}
|
||||
|
||||
public bool handle_selection
|
||||
{
|
||||
get;
|
||||
construct set;
|
||||
default = true;
|
||||
}
|
||||
|
||||
|
||||
public DiffViewFile(Ggit.DiffDelta delta, bool handle_selection)
|
||||
public DiffViewFile(Repository repository, Ggit.DiffDelta delta, bool new_is_workdir, bool handle_selection)
|
||||
{
|
||||
Object(delta: delta, handle_selection: handle_selection);
|
||||
Object(repository: repository, new_is_workdir: new_is_workdir, delta: delta, handle_selection: handle_selection);
|
||||
}
|
||||
|
||||
public PatchSet selection
|
||||
|
@ -175,10 +198,6 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
}
|
||||
}
|
||||
|
||||
private DiffViewLinesRenderer d_old_lines;
|
||||
private DiffViewLinesRenderer d_new_lines;
|
||||
private DiffViewLinesRenderer d_sym_lines;
|
||||
|
||||
construct
|
||||
{
|
||||
var gutter = d_sourceview_hunks.get_gutter(Gtk.TextWindowType.LEFT);
|
||||
|
@ -221,6 +240,234 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
d_sourceview_hunks.draw.connect_after(sourceview_hunks_on_draw);
|
||||
}
|
||||
|
||||
protected override void dispose()
|
||||
{
|
||||
base.dispose();
|
||||
|
||||
if (d_higlight_cancellable != null)
|
||||
{
|
||||
d_higlight_cancellable.cancel();
|
||||
d_higlight_cancellable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void update_highlight()
|
||||
{
|
||||
if (!d_constructed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d_higlight_cancellable != null)
|
||||
{
|
||||
d_higlight_cancellable.cancel();
|
||||
d_higlight_cancellable = null;
|
||||
}
|
||||
|
||||
d_old_highlight_buffer = null;
|
||||
d_new_highlight_buffer = null;
|
||||
|
||||
d_old_highlight_ready = false;
|
||||
d_new_highlight_ready = false;
|
||||
|
||||
if (highlight && repository != null && delta != null)
|
||||
{
|
||||
var cancellable = new Cancellable();
|
||||
d_higlight_cancellable = cancellable;
|
||||
|
||||
init_highlighting_buffer.begin(delta.get_old_file(), false, cancellable, (obj, res) => {
|
||||
var buffer = init_highlighting_buffer.end(res);
|
||||
|
||||
if (!cancellable.is_cancelled())
|
||||
{
|
||||
d_old_highlight_buffer = buffer;
|
||||
d_old_highlight_ready = true;
|
||||
|
||||
update_highlighting_ready();
|
||||
}
|
||||
});
|
||||
|
||||
init_highlighting_buffer.begin(delta.get_new_file(), new_is_workdir, cancellable, (obj, res) => {
|
||||
var buffer = init_highlighting_buffer.end(res);
|
||||
|
||||
if (!cancellable.is_cancelled())
|
||||
{
|
||||
d_new_highlight_buffer = buffer;
|
||||
d_new_highlight_ready = true;
|
||||
|
||||
update_highlighting_ready();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
update_highlighting_ready();
|
||||
}
|
||||
}
|
||||
|
||||
private async Gtk.SourceBuffer? init_highlighting_buffer(Ggit.DiffFile file, bool from_workdir, Cancellable cancellable)
|
||||
{
|
||||
var id = file.get_oid();
|
||||
var path = file.get_path();
|
||||
|
||||
if ((id.is_zero() && !from_workdir) || (path == null && from_workdir))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sfile = new Gtk.SourceFile();
|
||||
sfile.location = repository.get_workdir().get_child(path);
|
||||
|
||||
var basename = sfile.location.get_basename();
|
||||
uint8[] content;
|
||||
|
||||
if (!from_workdir)
|
||||
{
|
||||
Ggit.Blob blob;
|
||||
|
||||
try
|
||||
{
|
||||
blob = repository.lookup<Ggit.Blob>(id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
content = blob.get_raw_content();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to read from disk
|
||||
try
|
||||
{
|
||||
string etag;
|
||||
|
||||
// Read it all into a buffer so we can guess the content type from
|
||||
// it. This isn't really nice, but it's simple.
|
||||
yield sfile.location.load_contents_async(cancellable, out content, out etag);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool uncertain;
|
||||
var content_type = GLib.ContentType.guess(basename, content, out uncertain);
|
||||
|
||||
var bytes = new Bytes(content);
|
||||
var stream = new GLib.MemoryInputStream.from_bytes(bytes);
|
||||
|
||||
var manager = Gtk.SourceLanguageManager.get_default();
|
||||
var language = manager.guess_language(basename, content_type);
|
||||
|
||||
if (language == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var buffer = new Gtk.SourceBuffer(d_sourceview_hunks.buffer.tag_table);
|
||||
|
||||
var style_scheme_manager = Gtk.SourceStyleSchemeManager.get_default();
|
||||
|
||||
buffer.language = language;
|
||||
buffer.highlight_syntax = true;
|
||||
buffer.style_scheme = style_scheme_manager.get_scheme("classic");
|
||||
|
||||
var loader = new Gtk.SourceFileLoader.from_stream(buffer, sfile, stream);
|
||||
|
||||
try
|
||||
{
|
||||
yield loader.load_async(GLib.Priority.LOW, cancellable, null);
|
||||
}
|
||||
catch (Error e)
|
||||
{
|
||||
if (!cancellable.is_cancelled())
|
||||
{
|
||||
stderr.printf(@"ERROR: failed to load $(file.get_path()) for highlighting: $(e.message)\n");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void update_highlighting_ready()
|
||||
{
|
||||
if (!d_old_highlight_ready && !d_new_highlight_ready)
|
||||
{
|
||||
// Remove highlights
|
||||
return;
|
||||
}
|
||||
else if (!d_old_highlight_ready || !d_new_highlight_ready)
|
||||
{
|
||||
// Both need to be loaded
|
||||
return;
|
||||
}
|
||||
|
||||
var buffer = d_sourceview_hunks.buffer;
|
||||
|
||||
// Go over all the source chunks and match up to old/new buffer. Then,
|
||||
// apply the tags that are applied to the highlighted source buffers.
|
||||
foreach (var region in d_regions)
|
||||
{
|
||||
Gtk.SourceBuffer? source;
|
||||
|
||||
if (region.type == RegionType.REMOVED)
|
||||
{
|
||||
source = d_old_highlight_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = d_new_highlight_buffer;
|
||||
}
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Gtk.TextIter buffer_iter, source_iter;
|
||||
|
||||
buffer.get_iter_at_line(out buffer_iter, region.buffer_line_start);
|
||||
source.get_iter_at_line(out source_iter, region.source_line_start);
|
||||
|
||||
var source_end_iter = source_iter;
|
||||
source_end_iter.forward_lines(region.length);
|
||||
|
||||
source.ensure_highlight(source_iter, source_end_iter);
|
||||
|
||||
var buffer_end_iter = buffer_iter;
|
||||
buffer_end_iter.forward_lines(region.length);
|
||||
|
||||
var source_next_iter = source_iter;
|
||||
var tags = source_iter.get_tags();
|
||||
|
||||
while (source_next_iter.forward_to_tag_toggle(null) && source_next_iter.compare(source_end_iter) < 0)
|
||||
{
|
||||
var buffer_next_iter = buffer_iter;
|
||||
buffer_next_iter.forward_chars(source_next_iter.get_offset() - source_iter.get_offset());
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
buffer.apply_tag(tag, buffer_iter, buffer_next_iter);
|
||||
}
|
||||
|
||||
source_iter = source_next_iter;
|
||||
buffer_iter = buffer_next_iter;
|
||||
|
||||
tags = source_iter.get_tags();
|
||||
}
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
buffer.apply_tag(tag, buffer_iter, buffer_end_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool sourceview_hunks_on_draw(Cairo.Context cr)
|
||||
{
|
||||
var win = d_sourceview_hunks.get_window(Gtk.TextWindowType.LEFT);
|
||||
|
@ -302,6 +549,9 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
}
|
||||
|
||||
d_expander.bind_property("expanded", this, "expanded", BindingFlags.BIDIRECTIONAL);
|
||||
|
||||
d_constructed = true;
|
||||
update_highlight();
|
||||
}
|
||||
|
||||
public void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList<Ggit.DiffLine> lines)
|
||||
|
@ -338,6 +588,13 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
/* Diff Content */
|
||||
var content = new StringBuilder();
|
||||
|
||||
var region = Region() {
|
||||
type = RegionType.CONTEXT,
|
||||
buffer_line_start = 0,
|
||||
source_line_start = 0,
|
||||
length = 0
|
||||
};
|
||||
|
||||
for (var i = 0; i < lines.size; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
|
@ -346,15 +603,21 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
var removed = false;
|
||||
var origin = line.get_origin();
|
||||
|
||||
var rtype = RegionType.CONTEXT;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case Ggit.DiffLineType.ADDITION:
|
||||
added = true;
|
||||
d_added++;
|
||||
|
||||
rtype = RegionType.ADDED;
|
||||
break;
|
||||
case Ggit.DiffLineType.DELETION:
|
||||
removed = true;
|
||||
d_removed++;
|
||||
|
||||
rtype = RegionType.REMOVED;
|
||||
break;
|
||||
case Ggit.DiffLineType.CONTEXT_EOFNL:
|
||||
case Ggit.DiffLineType.ADD_EOFNL:
|
||||
|
@ -363,6 +626,34 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
break;
|
||||
}
|
||||
|
||||
if (i == 0 || rtype != region.type)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
d_regions += region;
|
||||
}
|
||||
|
||||
int source_line_start;
|
||||
|
||||
if (rtype == RegionType.REMOVED)
|
||||
{
|
||||
source_line_start = line.get_old_lineno() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
source_line_start = line.get_new_lineno() - 1;
|
||||
}
|
||||
|
||||
region = Region() {
|
||||
type = rtype,
|
||||
buffer_line_start = buffer_line,
|
||||
source_line_start = source_line_start,
|
||||
length = 0
|
||||
};
|
||||
}
|
||||
|
||||
region.length++;
|
||||
|
||||
if (added || removed)
|
||||
{
|
||||
var offset = (size_t)line.get_content_offset();
|
||||
|
@ -397,6 +688,11 @@ class Gitg.DiffViewFile : Gtk.Grid
|
|||
buffer_line++;
|
||||
}
|
||||
|
||||
if (lines.size != 0)
|
||||
{
|
||||
d_regions += region;
|
||||
}
|
||||
|
||||
int line_hunk_start = iter.get_line();
|
||||
|
||||
buffer.insert(ref iter, (string)content.data, -1);
|
||||
|
|
|
@ -113,6 +113,9 @@ public class Gitg.DiffView : Gtk.Grid
|
|||
public bool use_gravatar { get; construct set; default = true; }
|
||||
public int tab_width { get; construct set; default = 4; }
|
||||
public bool handle_selection { get; construct set; default = false; }
|
||||
public bool highlight { get; construct set; default = true; }
|
||||
public Repository repository { get; set; }
|
||||
public bool new_is_workdir { get; set; }
|
||||
|
||||
private bool flag_get(Ggit.DiffOption f)
|
||||
{
|
||||
|
@ -346,7 +349,7 @@ public class Gitg.DiffView : Gtk.Grid
|
|||
|
||||
add_file();
|
||||
|
||||
current_file = new Gitg.DiffViewFile(delta, handle_selection);
|
||||
current_file = new Gitg.DiffViewFile(repository, delta, new_is_workdir, handle_selection);
|
||||
return 0;
|
||||
},
|
||||
|
||||
|
|
|
@ -35,7 +35,10 @@ namespace GitgDiff
|
|||
construct
|
||||
{
|
||||
d_diff = new Gitg.DiffView();
|
||||
|
||||
d_diff.show_parents = true;
|
||||
d_diff.repository = application.repository;
|
||||
|
||||
d_diff.show();
|
||||
|
||||
var settings = new Settings("org.gnome.gitg.preferences.diff");
|
||||
|
|
Loading…
Reference in a new issue