SF.net SVN: geany: [2048] trunk

ntrel at users.sourceforge.net ntrel at xxxxx
Wed Nov 14 16:44:46 UTC 2007


Revision: 2048
          http://geany.svn.sourceforge.net/geany/?rev=2048&view=rev
Author:   ntrel
Date:     2007-11-14 08:44:45 -0800 (Wed, 14 Nov 2007)

Log Message:
-----------
Added plugin 'VC Diff', slightly modified from a patch by Yura
Siamashka (thanks). It was adapted from the SVN Diff plugin.
Currently it supports SVN, CVS and GIT.
Fixed GTK warnings when the current file has no filename and when
the current directory has no version control system.
Made some refactoring changes to make the VC_RECORD struct contain
all necessary attributes about each version control system.

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/plugins/Makefile.am

Added Paths:
-----------
    trunk/plugins/vcdiff.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2007-11-13 16:41:59 UTC (rev 2047)
+++ trunk/ChangeLog	2007-11-14 16:44:45 UTC (rev 2048)
@@ -1,3 +1,15 @@
+2007-11-14  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * plugins/vcdiff.c, plugins/Makefile.am:
+   Added plugin 'VC Diff', slightly modified from a patch by Yura
+   Siamashka (thanks). It was adapted from the SVN Diff plugin.
+   Currently it supports SVN, CVS and GIT.
+   Fixed GTK warnings when the current file has no filename and when
+   the current directory has no version control system.
+   Made some refactoring changes to make the VC_RECORD struct contain
+   all necessary attributes about each version control system.
+
+
 2007-11-13  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
 
  * plugins/filebrowser.c:

Modified: trunk/plugins/Makefile.am
===================================================================
--- trunk/plugins/Makefile.am	2007-11-13 16:41:59 UTC (rev 2047)
+++ trunk/plugins/Makefile.am	2007-11-14 16:44:45 UTC (rev 2048)
@@ -11,6 +11,7 @@
 htmlchars_la_LDFLAGS     = -module -avoid-version
 export_la_LDFLAGS        = -module -avoid-version
 svndiff_la_LDFLAGS       = -module -avoid-version
+vcdiff_la_LDFLAGS        = -module -avoid-version
 filebrowser_la_LDFLAGS   = -module -avoid-version
 
 if PLUGINS
@@ -21,6 +22,7 @@
 	htmlchars.la \
 	export.la \
 	svndiff.la \
+	vcdiff.la \
 	filebrowser.la
 
 # Plugins not to be installed
@@ -32,6 +34,7 @@
 htmlchars_la_SOURCES     = htmlchars.c
 export_la_SOURCES        = export.c
 svndiff_la_SOURCES       = svndiff.c
+vcdiff_la_SOURCES        = vcdiff.c
 filebrowser_la_SOURCES   = filebrowser.c
 
 demoplugin_la_LIBADD    = $(GTK_LIBS)
@@ -39,6 +42,7 @@
 htmlchars_la_LIBADD     = $(GTK_LIBS)
 export_la_LIBADD        = $(GTK_LIBS)
 svndiff_la_LIBADD       = $(GTK_LIBS)
+vcdiff_la_LIBADD       = $(GTK_LIBS)
 filebrowser_la_LIBADD   = $(GTK_LIBS)
 
 endif # PLUGINS

Added: trunk/plugins/vcdiff.c
===================================================================
--- trunk/plugins/vcdiff.c	                        (rev 0)
+++ trunk/plugins/vcdiff.c	2007-11-14 16:44:45 UTC (rev 2048)
@@ -0,0 +1,532 @@
+/*
+ *      vcdiff.c - this file is part of Geany, a fast and lightweight IDE
+ *
+ *      Copyright 2007 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
+ *      Copyright 2007 Enrico Tröger <enrico.troeger at uvena.de>
+ *      Copyright 2007 Nick Treleaven <nick.treleaven at btinternet.com>
+ *      Copyright 2007 Yura Siamashka <yurand2 at gmail.com>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* VCdiff plugin */
+/* This small plugin uses svn/git/etc. to generate a diff against the current
+ * version inside version control.
+ * Which VC program to use is detected by looking for a version control subdirectory,
+ * e.g. ".svn". */
+
+#include "geany.h"
+#include "support.h"
+#include "plugindata.h"
+#include "document.h"
+#include "filetypes.h"
+#include "utils.h"
+#include "project.h"
+#include "pluginmacros.h"
+
+PluginFields	*plugin_fields;
+GeanyData		*geany_data;
+
+
+VERSION_CHECK(27)
+
+PLUGIN_INFO(_("VC Diff"), _("Creates a patch of a file against version control."), VERSION)
+
+
+enum
+{
+	VC_COMMAND_DIFF_FILE,
+	VC_COMMAND_DIFF_DIR,
+	VC_COMMAND_DIFF_PROJECT
+};
+
+/* The addresses of these strings act as enums, their contents are not used. */
+static const gchar DIRNAME[] = "*DIRNAME*";
+static const gchar FILENAME[] = "*FILENAME*";
+static const gchar BASE_FILENAME[] = "*BASE_FILENAME*";
+
+
+static const gchar* SVN_CMD_DIFF_FILE[] = {"svn", "diff", "--non-interactive", FILENAME, NULL};
+static const gchar* SVN_CMD_DIFF_DIR[]  = {"svn", "diff", "--non-interactive", DIRNAME, NULL};
+static const gchar* SVN_CMD_DIFF_PROJECT[] = {"svn", "diff", "--non-interactive", DIRNAME, NULL};
+
+static void* SVN_COMMANDS[] = { SVN_CMD_DIFF_FILE, SVN_CMD_DIFF_DIR, SVN_CMD_DIFF_PROJECT };
+
+
+static const gchar* CVS_CMD_DIFF_FILE[] = {"cvs", "diff", "-u", BASE_FILENAME, NULL};
+static const gchar* CVS_CMD_DIFF_DIR[]  = {"cvs", "diff", "-u",NULL};
+static const gchar* CVS_CMD_DIFF_PROJECT[] = {"cvs", "diff", "-u", NULL};
+
+static void* CVS_COMMANDS[] = { CVS_CMD_DIFF_FILE, CVS_CMD_DIFF_DIR, CVS_CMD_DIFF_PROJECT };
+
+
+static const gchar* GIT_CMD_DIFF_FILE[] = {"git", "diff", BASE_FILENAME,  NULL};
+static const gchar* GIT_CMD_DIFF_DIR[]  = {"git", "diff", NULL};
+static const gchar* GIT_CMD_DIFF_PROJECT[] = {"git", "diff", NULL};
+
+static const gchar* GIT_ENV_DIFF_FILE[] = {"PAGER=cat", NULL};
+static const gchar* GIT_ENV_DIFF_DIR[]  = {"PAGER=cat", NULL};
+static const gchar* GIT_ENV_DIFF_PROJECT[] = {"PAGER=cat", NULL};
+
+static void* GIT_COMMANDS[] = { GIT_CMD_DIFF_FILE, GIT_CMD_DIFF_DIR, GIT_CMD_DIFF_PROJECT };
+static void* GIT_ENV[]      = { GIT_ENV_DIFF_FILE, GIT_ENV_DIFF_DIR, GIT_ENV_DIFF_PROJECT };
+
+
+typedef struct VC_RECORD
+{
+	void** commands;
+	void** envs;
+	const gchar *program;
+	const gchar *subdir;	// version control subdirectory, e.g. ".svn"
+	gboolean check_parents;	// check parent dirs to find subdir
+} VC_RECORD;
+
+
+static void *NO_ENV[] = {NULL, NULL, NULL};
+
+/* Adding another VC system should be as easy as adding another entry in this array. */
+static VC_RECORD VC[] = {
+	{SVN_COMMANDS, NO_ENV, "svn", ".svn", FALSE},
+	{CVS_COMMANDS, NO_ENV, "cvs", "CVS", FALSE},
+	{GIT_COMMANDS, GIT_ENV, "git", ".git", TRUE},
+};
+
+
+static gboolean find_subdir(const gchar* filename, const gchar *subdir)
+{
+	gboolean ret = FALSE;
+	gchar *base;
+	gchar *gitdir;
+	gchar *tmp;
+	gchar *base_prev = g_strdup(":");
+
+	if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+	    base = g_strdup(filename);
+	else
+	    base = g_path_get_dirname(filename);
+
+	while(strcmp(base, base_prev) != 0)
+	{
+	    gitdir = g_build_path("/", base, subdir, NULL);
+	    ret = g_file_test(gitdir, G_FILE_TEST_IS_DIR);
+	    g_free(gitdir);
+	    if (ret)
+			break;
+	    g_free(base_prev);
+	    base_prev = base;
+	    base = g_path_get_dirname(base);
+	}
+
+	g_free(base);
+	g_free(base_prev);
+	return ret;
+}
+
+
+static gboolean check_filename(const gchar* filename, VC_RECORD *vc)
+{
+	gboolean ret;
+	gchar *base;
+	gchar *dir;
+	gchar *path;
+
+	if (! filename)
+		return FALSE;
+
+	path = g_find_program_in_path(vc->program);
+	if (!path)
+		return FALSE;
+	g_free(path);
+
+	if (vc->check_parents)
+		return find_subdir(filename, vc->subdir);
+
+	if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+	    base = g_strdup(filename);
+	else
+	    base = g_path_get_dirname(filename);
+	dir = g_build_path("/", base, vc->subdir, NULL);
+
+	ret = g_file_test(dir, G_FILE_TEST_IS_DIR);
+
+	g_free(base);
+	g_free(dir);
+
+	return ret;
+}
+
+
+static void* find_cmd_env(gint cmd_type, gboolean cmd, const gchar* filename)
+{
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS(VC); i++)
+	{
+		if (check_filename(filename, &VC[i]))
+		{
+			if (cmd)
+				return VC[i].commands[cmd_type];
+			else
+				return VC[i].envs[cmd_type];
+		}
+	}
+	return NULL;
+}
+
+
+static void* get_cmd_env(gint cmd_type, gboolean cmd, const gchar* filename)
+{
+	int i;
+	gint len = 0;
+	gchar** argv;
+	gchar** ret;
+	gchar* dir;
+	gchar* base_filename;
+
+	argv = find_cmd_env(cmd_type, cmd, filename);
+	if (!argv)
+		return NULL;
+
+	if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+	{
+		dir = g_strdup(filename);
+	}
+	else
+	{
+		dir = g_path_get_dirname(filename);
+	}
+	base_filename = g_path_get_basename(filename);
+
+	while(1)
+	{
+		if (argv[len] == NULL)
+			break;
+		len++;
+	}
+	ret = g_malloc(sizeof(gchar*) * (len+1));
+	memset(ret, 0, sizeof(gchar*) * (len+1));
+	for (i = 0; i < len; i++)
+	{
+		if (argv[i] == DIRNAME)
+		{
+			ret[i] = g_strdup(dir);
+		}
+		else if (argv[i] == FILENAME)
+		{
+			ret[i] = g_strdup(filename);
+		}
+		else if (argv[i] == BASE_FILENAME)
+		{
+			ret[i] = g_strdup(base_filename);
+		}
+		else
+			ret[i] = g_strdup(argv[i]);
+	}
+	g_free(dir);
+	g_free(base_filename);
+	return ret;
+}
+
+
+static int find_by_filename(const gchar* filename)
+{
+	guint i;
+
+	for (i = 0; i < doc_array->len; i++)
+	{
+		if (doc_list[i].is_valid && doc_list[i].file_name &&
+			strcmp(doc_list[i].file_name, filename) == 0)
+			return i;
+	}
+	return -1;
+}
+
+
+/* name_prefix should be in UTF-8, and can have a path. */
+static void show_output(const gchar *std_output, const gchar *name_prefix,
+		const gchar *force_encoding)
+{
+	gchar	*text, *detect_enc = NULL;
+	gint 	idx, page;
+	GtkNotebook *book;
+	gchar	*filename;
+
+	filename = g_path_get_basename(name_prefix);
+	setptr(filename, g_strconcat(filename, ".vc.diff", NULL));
+
+	// need to convert input text from the encoding of the original file into
+	// UTF-8 because internally Geany always needs UTF-8
+	if (force_encoding)
+	{
+		text = encodings->convert_to_utf8_from_charset(
+			std_output, -1, force_encoding, TRUE);
+	}
+	else
+	{
+		text = encodings->convert_to_utf8(std_output, -1, &detect_enc);
+	}
+	if (text)
+	{
+		idx = find_by_filename(filename);
+		if ( idx == -1)
+		{
+			idx = documents->new_file(filename,
+				geany_data->filetypes[GEANY_FILETYPES_DIFF], text);
+		}
+		else
+		{
+			scintilla->set_text(doc_list[idx].sci, text);
+			book = GTK_NOTEBOOK(app->notebook);
+			page = gtk_notebook_page_num(book, GTK_WIDGET(doc_list[idx].sci));
+			gtk_notebook_set_current_page(book, page);
+			doc_list[idx].changed = FALSE;
+			documents->set_text_changed(idx);
+		}
+
+		documents->set_encoding(idx,
+			force_encoding ? force_encoding : detect_enc);
+	}
+	else
+	{
+		ui->set_statusbar(FALSE, _("Could not parse the output of the diff"));
+	}
+	g_free(text);
+	g_free(detect_enc);
+	g_free(filename);
+}
+
+
+static gchar *make_diff(const gchar *filename, gint cmd)
+{
+	gchar	*std_output = NULL;
+	gchar	*std_error = NULL;
+	gint	exit_code;
+	gchar	*text = NULL;
+	gchar   *dir;
+	gchar **env  = get_cmd_env(cmd, FALSE, filename);
+	gchar **argv = get_cmd_env(cmd, TRUE, filename);
+
+	if (!argv)
+	{
+		if (env)
+			g_strfreev(env);
+		return NULL;
+	}
+
+	if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+	{
+		dir = g_strdup(filename);
+	}
+	else
+	{
+		dir = g_path_get_dirname(filename);
+	}
+
+	if (g_spawn_sync(dir, argv, env,  G_SPAWN_SEARCH_PATH, NULL, NULL, &std_output, &std_error, &exit_code, NULL))
+	{
+		// CVS dump stuff to stderr when diff nested dirs
+		if (strcmp(argv[0], "cvs") != 0 && NZV(std_error))
+		{
+		    dialogs->show_msgbox(1,
+				_("%s exited with an error: \n%s."), argv[0], g_strstrip(std_error));
+		}
+		else if (NZV(std_output))
+		{
+			text = std_output;
+		}
+		else
+		{
+			ui->set_statusbar(FALSE, _("No changes were made."));
+		}
+	}
+	else
+	{
+		ui->set_statusbar(FALSE,
+			_("Something went really wrong."));
+	}
+	g_free(dir);
+	g_free(std_error);
+	g_strfreev(env);
+	g_strfreev(argv);
+	return text;
+}
+
+
+/* Make a diff from the current directory */
+static void vcdirectory_activated(GtkMenuItem *menuitem, gpointer gdata)
+{
+	gint	idx;
+	gchar	*base_name = NULL;
+	gchar	*locale_filename = NULL;
+	gchar	*text;
+
+	idx = documents->get_cur_idx();
+
+	g_return_if_fail(DOC_IDX_VALID(idx) && doc_list[idx].file_name != NULL);
+
+	if (doc_list[idx].changed)
+	{
+		documents->save_file(idx, FALSE);
+	}
+
+	locale_filename = utils->get_locale_from_utf8(doc_list[idx].file_name);
+	base_name = g_path_get_dirname(locale_filename);
+
+	text = make_diff(base_name, VC_COMMAND_DIFF_DIR);
+	if (text)
+	{
+		show_output(text, base_name, NULL);
+		g_free(text);
+	}
+
+	g_free(base_name);
+	g_free(locale_filename);
+}
+
+
+/* Callback if menu item for the current project was activated */
+static void vcproject_activated(GtkMenuItem *menuitem, gpointer gdata)
+{
+	gint	idx;
+	gchar	*locale_filename = NULL;
+	gchar	*text;
+
+	idx = documents->get_cur_idx();
+
+	g_return_if_fail(project != NULL && NZV(project->base_path));
+
+	if (DOC_IDX_VALID(idx) && doc_list[idx].changed && doc_list[idx].file_name != NULL)
+	{
+		documents->save_file(idx, FALSE);
+	}
+
+	locale_filename = utils->get_locale_from_utf8(project->base_path);
+	text = make_diff(locale_filename, VC_COMMAND_DIFF_PROJECT);
+	if (text)
+	{
+		show_output(text, project->name, NULL);
+		g_free(text);
+	}
+	g_free(locale_filename);
+}
+
+
+/* Callback if menu item for a single file was activated */
+static void vcfile_activated(GtkMenuItem *menuitem, gpointer gdata)
+{
+	gint	idx;
+	gchar	*locale_filename, *text;
+
+	idx = documents->get_cur_idx();
+
+	g_return_if_fail(DOC_IDX_VALID(idx) && doc_list[idx].file_name != NULL);
+
+	if (doc_list[idx].changed)
+	{
+		documents->save_file(idx, FALSE);
+	}
+
+	locale_filename = utils->get_locale_from_utf8(doc_list[idx].file_name);
+
+	text = make_diff(locale_filename, VC_COMMAND_DIFF_FILE);
+	if (text)
+	{
+		show_output(text, doc_list[idx].file_name, doc_list[idx].encoding);
+		g_free(text);
+	}
+	g_free(locale_filename);
+}
+
+
+static GtkWidget *menu_vcdiff_file = NULL;
+static GtkWidget *menu_vcdiff_dir = NULL;
+static GtkWidget *menu_vcdiff_project = NULL;
+
+static void update_menu_items()
+{
+	document	*doc;
+	gboolean	have_file;
+	gboolean    have_vc = FALSE;
+
+	doc = documents->get_current();
+	have_file = doc && doc->file_name && g_path_is_absolute(doc->file_name);
+	if (find_cmd_env(VC_COMMAND_DIFF_FILE, TRUE, doc->file_name))
+		have_vc = TRUE;
+
+	gtk_widget_set_sensitive(menu_vcdiff_file, have_vc && have_file);
+	gtk_widget_set_sensitive(menu_vcdiff_dir, have_vc && have_file);
+	gtk_widget_set_sensitive(menu_vcdiff_project,
+		project != NULL && NZV(project->base_path));
+}
+
+
+/* Called by Geany to initialize the plugin */
+void init(GeanyData *data)
+{
+	GtkWidget	*menu_vcdiff = NULL;
+	GtkWidget	*menu_vcdiff_menu = NULL;
+ 	GtkTooltips	*tooltips = NULL;
+
+	tooltips = gtk_tooltips_new();
+
+	menu_vcdiff = gtk_image_menu_item_new_with_mnemonic(_("_VCdiff"));
+	gtk_container_add(GTK_CONTAINER(data->tools_menu), menu_vcdiff);
+
+	g_signal_connect((gpointer) menu_vcdiff, "activate",
+		G_CALLBACK(update_menu_items), NULL);
+
+	menu_vcdiff_menu = gtk_menu_new ();
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_vcdiff), menu_vcdiff_menu);
+
+	// Single file
+	menu_vcdiff_file = gtk_menu_item_new_with_mnemonic(_("From Current _File"));
+	gtk_container_add(GTK_CONTAINER (menu_vcdiff_menu), menu_vcdiff_file);
+	gtk_tooltips_set_tip (tooltips, menu_vcdiff_file,
+		_("Make a diff from the current active file"), NULL);
+
+	g_signal_connect((gpointer) menu_vcdiff_file, "activate",
+		G_CALLBACK(vcfile_activated), NULL);
+
+	// Directory
+	menu_vcdiff_dir = gtk_menu_item_new_with_mnemonic(_("From Current _Directory"));
+	gtk_container_add(GTK_CONTAINER (menu_vcdiff_menu), menu_vcdiff_dir);
+	gtk_tooltips_set_tip (tooltips, menu_vcdiff_dir,
+		_("Make a diff from the directory of the current active file"), NULL);
+
+	g_signal_connect((gpointer) menu_vcdiff_dir, "activate",
+		G_CALLBACK(vcdirectory_activated), NULL);
+
+	// Project
+	menu_vcdiff_project = gtk_menu_item_new_with_mnemonic(_("From Current _Project"));
+	gtk_container_add(GTK_CONTAINER (menu_vcdiff_menu), menu_vcdiff_project);
+	gtk_tooltips_set_tip (tooltips, menu_vcdiff_project,
+		_("Make a diff from the current project's base path"), NULL);
+
+	g_signal_connect((gpointer) menu_vcdiff_project, "activate",
+		G_CALLBACK(vcproject_activated), NULL);
+
+	gtk_widget_show_all(menu_vcdiff);
+
+	plugin_fields->menu_item = menu_vcdiff;
+	plugin_fields->flags = PLUGIN_IS_DOCUMENT_SENSITIVE;
+}
+
+
+/* Called by Geany before unloading the plugin. */
+void cleanup()
+{
+	// remove the menu item added in init()
+	gtk_widget_destroy(plugin_fields->menu_item);
+}


Property changes on: trunk/plugins/vcdiff.c
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list