[geany/geany-plugins] 59118d: Merge pull request #531 from techee/sidebar_undo
Frank Lanitz
git-noreply at xxxxx
Sat Aug 19 13:46:12 UTC 2017
Branch: refs/heads/master
Author: Frank Lanitz <frank at frank.uvena.de>
Committer: GitHub <noreply at github.com>
Date: Sat, 19 Aug 2017 13:46:12 UTC
Commit: 59118d86ef8853943dec74c9f6d6fad9a535931a
https://github.com/geany/geany-plugins/commit/59118d86ef8853943dec74c9f6d6fad9a535931a
Log Message:
-----------
Merge pull request #531 from techee/sidebar_undo
git-changebar: Add the possibility to undo hunk at cursor position
Modified Paths:
--------------
git-changebar/README
git-changebar/src/gcb-plugin.c
Modified: git-changebar/README
8 lines changed, 6 insertions(+), 2 deletions(-)
===================================================================
@@ -8,8 +8,8 @@ Git Change Bar
About
=====
-This plugin highlights uncommitted changes to files tracked with Git, and
-allows to navigate through the hunks.
+This plugin highlights uncommitted changes to files tracked with Git,
+allows to navigate through the hunks and undo them.
Requirements
@@ -38,6 +38,10 @@ To navigate through the hunks of the current file, you need to configure the
plugin's *Go to next hunk* and *Go to previous hunk* keybindings in Geany's
preferences dialog.
+Hunks can also be undone by either setting a cursor at a line with a hunk and
+invoking the *Undo hunk at the cursor position* or by right-clicking in the
+editor where the hunk is located and selecting *Undo Git hunk* from the popup
+menu.
License
=======
Modified: git-changebar/src/gcb-plugin.c
249 lines changed, 240 insertions(+), 9 deletions(-)
===================================================================
@@ -68,7 +68,13 @@ PLUGIN_SET_TRANSLATABLE_INFO (
#define RESOURCES_ALLOCATED_QTAG \
(g_quark_from_string (PLUGIN"/git-resources-allocated"))
+#define UNDO_LINE_QTAG \
+ (g_quark_from_string (PLUGIN"/git-undo-line"))
+#define DOC_ID_QTAG \
+ (g_quark_from_string (PLUGIN"/git-doc-id"))
+#define REMOVED_MARKER_POS(pos) \
+ ((pos) == 0 ? 0 : (pos) - 1)
enum {
MARKER_LINE_ADDED,
@@ -80,6 +86,7 @@ enum {
enum {
KB_GOTO_PREV_HUNK,
KB_GOTO_NEXT_HUNK,
+ KB_UNDO_HUNK,
KB_COUNT
};
@@ -117,6 +124,16 @@ struct GotoNextHunkData {
gint next_line;
};
+typedef struct UndoHunkData UndoHunkData;
+struct UndoHunkData {
+ guint doc_id;
+ gint line;
+ gboolean found;
+ gint old_start;
+ gint old_lines;
+ gint new_start;
+ gint new_lines;
+};
static void on_git_repo_changed (GFileMonitor *monitor,
GFile *file,
@@ -155,6 +172,7 @@ static GAsyncQueue *G_queue = NULL;
static GThread *G_thread = NULL;
static gulong G_source_id = 0;
static gboolean G_monitoring_enabled = TRUE;
+static GtkWidget *G_undo_menu_item = NULL;
static struct {
gint num;
gint style;
@@ -591,7 +609,7 @@ release_resources (ScintillaObject *sci)
/* checks whether @encoding needs to be converted to UTF-8 */
static gboolean
-encoding_needs_coversion (const gchar *encoding)
+encoding_needs_conversion (const gchar *encoding)
{
return (encoding &&
! utils_str_equal (encoding, "UTF-8") &&
@@ -694,7 +712,7 @@ diff_buf_to_doc (const git_buf *old_buf,
free_buf = add_utf8_bom (&buf, &len, free_buf);
}
/* convert the buffer back to in-file encoding if necessary */
- if (encoding_needs_coversion (doc->encoding)) {
+ if (encoding_needs_conversion (doc->encoding)) {
free_buf = convert_encoding_inplace (&buf, &len, free_buf,
doc->encoding, "UTF-8", NULL);
}
@@ -720,16 +738,17 @@ diff_hunk_cb (const git_diff_delta *delta,
void *data)
{
ScintillaObject *sci = data;
+ gint line;
if (hunk->new_lines > 0) {
- gint line;
guint marker = hunk->old_lines > 0 ? MARKER_LINE_CHANGED : MARKER_LINE_ADDED;
for (line = hunk->new_start; line < hunk->new_start + hunk->new_lines; line++) {
scintilla_send_message (sci, SCI_MARKERADD, line - 1, G_markers[marker].num);
}
} else {
- scintilla_send_message (sci, SCI_MARKERADD, hunk->new_start - 1,
+ line = REMOVED_MARKER_POS (hunk->new_start);
+ scintilla_send_message (sci, SCI_MARKERADD, line,
G_markers[MARKER_LINE_REMOVED].num);
}
@@ -773,7 +792,7 @@ get_widget_for_buf_range (GeanyDocument *doc,
}
/* convert the buffer to UTF-8 if necessary */
- if (encoding_needs_coversion (doc->encoding)) {
+ if (encoding_needs_conversion (doc->encoding)) {
free_buf = convert_encoding_inplace (&buf, &buf_len, free_buf,
"UTF-8", doc->encoding, NULL);
}
@@ -812,6 +831,12 @@ get_widget_for_buf_range (GeanyDocument *doc,
return GTK_WIDGET (sci);
}
+static gboolean
+is_first_line_removed (gint line, gint new_hunk_start, gint new_hunk_lines)
+{
+ return line == 1 && new_hunk_start == 0 && new_hunk_lines == 0;
+}
+
static int
tooltip_diff_hunk_cb (const git_diff_delta *delta,
const git_diff_hunk *hunk,
@@ -824,8 +849,9 @@ tooltip_diff_hunk_cb (const git_diff_delta *delta,
}
if (hunk->old_lines > 0 &&
- thd->line >= hunk->new_start &&
- thd->line < hunk->new_start + MAX (1, hunk->new_lines)) {
+ (is_first_line_removed (thd->line, hunk->new_start, hunk->new_lines) ||
+ (thd->line >= hunk->new_start &&
+ thd->line < hunk->new_start + MAX (1, hunk->new_lines)))) {
GtkWidget *old = get_widget_for_buf_range (thd->doc, thd->buf,
hunk->old_start - 1,
hunk->old_lines);
@@ -900,6 +926,8 @@ update_diff (const gchar *path,
}
}
+ gtk_widget_set_visible (G_undo_menu_item, contents != NULL);
+
if (contents && (allocated || allocate_resources (sci))) {
diff_buf_to_doc (contents, doc, diff_hunk_cb, sci);
} else if (! contents && allocated) {
@@ -962,6 +990,8 @@ update_diff_push (GeanyDocument *doc,
{
g_return_if_fail (DOC_VALID (doc));
+ gtk_widget_hide (G_undo_menu_item);
+
if (G_source_id) {
g_source_remove (G_source_id);
G_source_id = 0;
@@ -1036,13 +1066,13 @@ goto_next_hunk_diff_hunk_cb (const git_diff_delta *delta,
if (data->next_line >= 0) {
return 1;
} else if (data->line < hunk->new_start - 1) {
- data->next_line = hunk->new_start - 1;
+ data->next_line = REMOVED_MARKER_POS (hunk->new_start);
}
break;
case KB_GOTO_PREV_HUNK:
if (data->line > hunk->new_start - 1 + MAX (hunk->new_lines - 1, 0)) {
- data->next_line = hunk->new_start - 1;
+ data->next_line = REMOVED_MARKER_POS (hunk->new_start);
}
break;
}
@@ -1089,6 +1119,194 @@ on_kb_goto_next_hunk (guint kb)
}
}
+static void
+insert_buf_range (GeanyDocument *doc,
+ const git_buf *old_contents,
+ gint pos,
+ gint old_start,
+ gint old_lines)
+{
+ ScintillaObject *old_sci = editor_create_widget (doc->editor);
+ gchar *old_buf = old_contents->ptr;
+ gsize old_buf_len = old_contents->size;
+ gboolean free_buf = FALSE;
+ gint old_pos_start;
+ gint old_pos_end;
+ gchar *old_range;
+
+ /* convert the buffer to UTF-8 if necessary */
+ if (encoding_needs_conversion (doc->encoding)) {
+ free_buf = convert_encoding_inplace (&old_buf, &old_buf_len, free_buf,
+ "UTF-8", doc->encoding, NULL);
+ }
+
+ scintilla_send_message (old_sci, SCI_ADDTEXT, old_buf_len, (glong) old_buf);
+
+ old_pos_start = sci_get_position_from_line (old_sci, old_start);
+ old_pos_end = sci_get_position_from_line (old_sci, old_start + old_lines);
+ old_range = sci_get_contents_range (old_sci, old_pos_start, old_pos_end);
+
+ sci_insert_text (doc->editor->sci, pos, old_range);
+
+ g_free (old_range);
+
+ if (free_buf) {
+ g_free (old_buf);
+ }
+
+ g_object_ref_sink (old_sci);
+ g_object_unref (old_sci);
+}
+
+static int
+undo_hunk_diff_hunk_cb (const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *udata)
+{
+ UndoHunkData *data = udata;
+
+ if (is_first_line_removed (data->line, hunk->new_start, hunk->new_lines) ||
+ (data->line >= hunk->new_start &&
+ data->line < hunk->new_start + MAX (1, hunk->new_lines))) {
+ data->old_start = hunk->old_start;
+ data->old_lines = hunk->old_lines;
+ data->new_start = hunk->new_start;
+ data->new_lines = hunk->new_lines;
+ data->found = TRUE;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+undo_hunk_cb (const gchar *path,
+ git_buf *contents,
+ gpointer udata)
+{
+ UndoHunkData *data = udata;
+ GeanyDocument *doc = document_get_current ();
+
+ if (doc && doc->id == data->doc_id && contents) {
+ diff_buf_to_doc (contents, doc, undo_hunk_diff_hunk_cb, data);
+
+ if (data->found) {
+ ScintillaObject *sci = doc->editor->sci;
+ gint line = data->new_start - (data->new_lines ? 1 : 0);
+ gint pos = sci_get_position_from_line (sci, line);
+
+ sci_start_undo_action (sci);
+
+ if (data->new_lines > 0) {
+ sci_set_target_start (sci, pos);
+ pos = sci_get_position_from_line (sci, line + data->new_lines);
+ sci_set_target_end (sci, pos);
+ sci_replace_target (sci, "", FALSE);
+ }
+
+ if (data->old_lines > 0) {
+ pos = sci_get_position_from_line (sci, line);
+ insert_buf_range (doc, contents, pos,
+ data->old_start - 1,
+ data->old_lines);
+
+ pos = sci_get_position_from_line (sci, line + data->old_lines);
+ sci_set_current_position (sci, pos, FALSE);
+ }
+
+ scintilla_send_message (sci, SCI_SCROLLRANGE,
+ sci_get_position_from_line (sci, line),
+ pos);
+
+ sci_end_undo_action (sci);
+ }
+ }
+
+ g_slice_free1 (sizeof *data, data);
+}
+
+static void
+undo_hunk (GeanyDocument *doc,
+ gint line)
+{
+ UndoHunkData *data = g_slice_alloc (sizeof *data);
+
+ data->doc_id = doc->id;
+ data->line = line + 1;
+ data->found = FALSE;
+
+ get_cached_blob_contents_async (doc->real_path, doc->id, FALSE,
+ undo_hunk_cb, data);
+}
+
+static void
+on_kb_undo_hunk (guint kb)
+{
+ GeanyDocument *doc = document_get_current ();
+
+ if (doc) {
+ undo_hunk (doc, sci_get_current_line (doc->editor->sci));
+ }
+}
+
+static void
+on_undo_hunk_activate (GtkWidget *widget,
+ gpointer user_data)
+{
+ GeanyDocument *doc = document_get_current ();
+ gpointer doc_id = g_object_get_qdata (G_OBJECT (widget), DOC_ID_QTAG);
+
+ if (doc && doc->id == GPOINTER_TO_UINT (doc_id) &&
+ gtk_widget_get_sensitive (widget)) {
+ gpointer line = g_object_get_qdata (G_OBJECT (widget), UNDO_LINE_QTAG);
+
+ undo_hunk (doc, GPOINTER_TO_INT (line));
+ }
+}
+
+static void
+check_undo_hunk_cb (const gchar *path,
+ git_buf *contents,
+ gpointer udata)
+{
+ UndoHunkData *data = udata;
+ GeanyDocument *doc = document_get_current ();
+
+ if (doc && doc->id == data->doc_id && contents) {
+ diff_buf_to_doc (contents, doc, undo_hunk_diff_hunk_cb, data);
+ if (data->found) {
+ gtk_widget_set_sensitive (G_undo_menu_item, TRUE);
+ g_object_set_qdata (G_OBJECT (G_undo_menu_item), UNDO_LINE_QTAG,
+ GINT_TO_POINTER (data->line - 1));
+ g_object_set_qdata (G_OBJECT (G_undo_menu_item), DOC_ID_QTAG,
+ GUINT_TO_POINTER (data->doc_id));
+ }
+ }
+
+ g_slice_free1 (sizeof *data, data);
+}
+
+static void
+on_update_editor_menu (GObject *object,
+ const gchar *word,
+ gint pos,
+ GeanyDocument *doc,
+ gpointer user_data)
+{
+ gtk_widget_set_sensitive (G_undo_menu_item, FALSE);
+
+ if (doc) {
+ UndoHunkData *data = g_slice_alloc (sizeof *data);
+
+ data->doc_id = doc->id;
+ data->line = sci_get_line_from_position (doc->editor->sci, pos) + 1;
+ data->found = FALSE;
+
+ get_cached_blob_contents_async (doc->real_path, doc->id, FALSE,
+ check_undo_hunk_cb, data);
+ }
+}
+
/* --- configuration loading and saving --- */
static void
@@ -1270,14 +1488,25 @@ plugin_init (GeanyData *data)
load_config ();
+ G_undo_menu_item = gtk_menu_item_new_with_label (_("Undo Git hunk"));
+ g_signal_connect (G_undo_menu_item, "activate",
+ G_CALLBACK (on_undo_hunk_activate), NULL);
+ gtk_container_add (GTK_CONTAINER (data->main_widgets->editor_menu),
+ G_undo_menu_item);
+
kb_group = plugin_set_key_group (geany_plugin, PLUGIN, KB_COUNT, NULL);
keybindings_set_item (kb_group, KB_GOTO_PREV_HUNK, on_kb_goto_next_hunk, 0, 0,
"goto-prev-hunk", _("Go to the previous hunk"), NULL);
keybindings_set_item (kb_group, KB_GOTO_NEXT_HUNK, on_kb_goto_next_hunk, 0, 0,
"goto-next-hunk", _("Go to the next hunk"), NULL);
+ keybindings_set_item (kb_group, KB_UNDO_HUNK, on_kb_undo_hunk, 0, 0,
+ "undo-hunk", _("Undo hunk at the cursor position"),
+ G_undo_menu_item);
plugin_signal_connect (geany_plugin, NULL, "editor-notify", TRUE,
G_CALLBACK (on_editor_notify), NULL);
+ plugin_signal_connect (geany_plugin, NULL, "update-editor-menu", TRUE,
+ G_CALLBACK (on_update_editor_menu), NULL);
plugin_signal_connect (geany_plugin, NULL, "document-activate", TRUE,
G_CALLBACK (on_document_activate), NULL);
plugin_signal_connect (geany_plugin, NULL, "document-reload", TRUE,
@@ -1299,6 +1528,8 @@ plugin_cleanup (void)
{
guint i = 0;
+ gtk_widget_destroy (G_undo_menu_item);
+
if (G_source_id) {
g_source_remove (G_source_id);
G_source_id = 0;
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Plugins-Commits
mailing list