summaryrefslogtreecommitdiff
path: root/plugins/modelines
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/modelines')
-rwxr-xr-xplugins/modelines/Makefile.am38
-rwxr-xr-xplugins/modelines/gedit-modeline-plugin.c248
-rwxr-xr-xplugins/modelines/gedit-modeline-plugin.h48
-rwxr-xr-xplugins/modelines/language-mappings14
-rwxr-xr-xplugins/modelines/modeline-parser.c852
-rwxr-xr-xplugins/modelines/modeline-parser.h37
-rwxr-xr-xplugins/modelines/modelines.gedit-plugin.desktop.in8
7 files changed, 1245 insertions, 0 deletions
diff --git a/plugins/modelines/Makefile.am b/plugins/modelines/Makefile.am
new file mode 100755
index 00000000..ddcfccc8
--- /dev/null
+++ b/plugins/modelines/Makefile.am
@@ -0,0 +1,38 @@
+# Modelines Plugin
+plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(GEDIT_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+modelinesdir = $(GEDIT_PLUGINS_DATA_DIR)/modelines
+modelines_DATA = \
+ language-mappings
+
+plugin_LTLIBRARIES = libmodelines.la
+
+libmodelines_la_SOURCES = \
+ gedit-modeline-plugin.h \
+ gedit-modeline-plugin.c \
+ modeline-parser.h \
+ modeline-parser.c
+
+libmodelines_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libmodelines_la_LIBADD = $(GEDIT_LIBS)
+
+plugin_in_files = modelines.gedit-plugin.desktop.in
+%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
+
+EXTRA_DIST = \
+ $(plugin_in_files) \
+ $(modelines_DATA)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/modelines/gedit-modeline-plugin.c b/plugins/modelines/gedit-modeline-plugin.c
new file mode 100755
index 00000000..49fc2f69
--- /dev/null
+++ b/plugins/modelines/gedit-modeline-plugin.c
@@ -0,0 +1,248 @@
+/*
+ * gedit-modeline-plugin.c
+ * Emacs, Kate and Vim-style modelines support for gedit.
+ *
+ * Copyright (C) 2005-2007 - Steve Frécinaux <[email protected]>
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include "gedit-modeline-plugin.h"
+#include "modeline-parser.h"
+
+#include <gedit/gedit-debug.h>
+#include <gedit/gedit-utils.h>
+
+#define WINDOW_DATA_KEY "GeditModelinePluginWindowData"
+#define DOCUMENT_DATA_KEY "GeditModelinePluginDocumentData"
+
+typedef struct
+{
+ gulong tab_added_handler_id;
+ gulong tab_removed_handler_id;
+} WindowData;
+
+typedef struct
+{
+ gulong document_loaded_handler_id;
+ gulong document_saved_handler_id;
+} DocumentData;
+
+static void gedit_modeline_plugin_activate (GeditPlugin *plugin, GeditWindow *window);
+static void gedit_modeline_plugin_deactivate (GeditPlugin *plugin, GeditWindow *window);
+static GObject *gedit_modeline_plugin_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_param);
+static void gedit_modeline_plugin_finalize (GObject *object);
+
+GEDIT_PLUGIN_REGISTER_TYPE(GeditModelinePlugin, gedit_modeline_plugin)
+
+static void
+window_data_free (WindowData *wdata)
+{
+ g_slice_free (WindowData, wdata);
+}
+
+static void
+document_data_free (DocumentData *ddata)
+{
+ g_slice_free (DocumentData, ddata);
+}
+
+static void
+gedit_modeline_plugin_class_init (GeditModelinePluginClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GeditPluginClass *plugin_class = GEDIT_PLUGIN_CLASS (klass);
+
+ object_class->constructor = gedit_modeline_plugin_constructor;
+ object_class->finalize = gedit_modeline_plugin_finalize;
+
+ plugin_class->activate = gedit_modeline_plugin_activate;
+ plugin_class->deactivate = gedit_modeline_plugin_deactivate;
+}
+
+static GObject *
+gedit_modeline_plugin_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_param)
+{
+ GObject *object;
+ gchar *data_dir;
+
+ object = G_OBJECT_CLASS (gedit_modeline_plugin_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_param);
+
+ data_dir = gedit_plugin_get_data_dir (GEDIT_PLUGIN (object));
+
+ modeline_parser_init (data_dir);
+
+ g_free (data_dir);
+
+ return object;
+}
+
+static void
+gedit_modeline_plugin_init (GeditModelinePlugin *plugin)
+{
+ gedit_debug_message (DEBUG_PLUGINS, "GeditModelinePlugin initializing");
+}
+
+static void
+gedit_modeline_plugin_finalize (GObject *object)
+{
+ gedit_debug_message (DEBUG_PLUGINS, "GeditModelinePlugin finalizing");
+
+ modeline_parser_shutdown ();
+
+ G_OBJECT_CLASS (gedit_modeline_plugin_parent_class)->finalize (object);
+}
+
+static void
+on_document_loaded_or_saved (GeditDocument *document,
+ const GError *error,
+ GtkSourceView *view)
+{
+ modeline_parser_apply_modeline (view);
+}
+
+static void
+connect_handlers (GeditView *view)
+{
+ DocumentData *data;
+ GtkTextBuffer *doc;
+
+ doc = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ data = g_slice_new (DocumentData);
+
+ data->document_loaded_handler_id =
+ g_signal_connect (doc, "loaded",
+ G_CALLBACK (on_document_loaded_or_saved),
+ view);
+ data->document_saved_handler_id =
+ g_signal_connect (doc, "saved",
+ G_CALLBACK (on_document_loaded_or_saved),
+ view);
+
+ g_object_set_data_full (G_OBJECT (doc), DOCUMENT_DATA_KEY,
+ data, (GDestroyNotify) document_data_free);
+}
+
+static void
+disconnect_handlers (GeditView *view)
+{
+ DocumentData *data;
+ GtkTextBuffer *doc;
+
+ doc = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ data = g_object_steal_data (G_OBJECT (doc), DOCUMENT_DATA_KEY);
+
+ if (data)
+ {
+ g_signal_handler_disconnect (doc, data->document_loaded_handler_id);
+ g_signal_handler_disconnect (doc, data->document_saved_handler_id);
+
+ document_data_free (data);
+ }
+ else
+ {
+ g_warning ("Modeline handlers not found");
+ }
+}
+
+static void
+on_window_tab_added (GeditWindow *window,
+ GeditTab *tab,
+ gpointer user_data)
+{
+ connect_handlers (gedit_tab_get_view (tab));
+}
+
+static void
+on_window_tab_removed (GeditWindow *window,
+ GeditTab *tab,
+ gpointer user_data)
+{
+ disconnect_handlers (gedit_tab_get_view (tab));
+}
+
+static void
+gedit_modeline_plugin_activate (GeditPlugin *plugin,
+ GeditWindow *window)
+{
+ WindowData *wdata;
+ GList *views;
+ GList *l;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ views = gedit_window_get_views (window);
+ for (l = views; l != NULL; l = l->next)
+ {
+ connect_handlers (GEDIT_VIEW (l->data));
+ modeline_parser_apply_modeline (GTK_SOURCE_VIEW (l->data));
+ }
+ g_list_free (views);
+
+ wdata = g_slice_new (WindowData);
+
+ wdata->tab_added_handler_id =
+ g_signal_connect (window, "tab-added",
+ G_CALLBACK (on_window_tab_added), NULL);
+
+ wdata->tab_removed_handler_id =
+ g_signal_connect (window, "tab-removed",
+ G_CALLBACK (on_window_tab_removed), NULL);
+
+ g_object_set_data_full (G_OBJECT (window), WINDOW_DATA_KEY,
+ wdata, (GDestroyNotify) window_data_free);
+}
+
+static void
+gedit_modeline_plugin_deactivate (GeditPlugin *plugin,
+ GeditWindow *window)
+{
+ WindowData *wdata;
+ GList *views;
+ GList *l;
+
+ gedit_debug (DEBUG_PLUGINS);
+
+ wdata = g_object_steal_data (G_OBJECT (window), WINDOW_DATA_KEY);
+
+ g_signal_handler_disconnect (window, wdata->tab_added_handler_id);
+ g_signal_handler_disconnect (window, wdata->tab_removed_handler_id);
+
+ window_data_free (wdata);
+
+ views = gedit_window_get_views (window);
+
+ for (l = views; l != NULL; l = l->next)
+ {
+ disconnect_handlers (GEDIT_VIEW (l->data));
+
+ modeline_parser_deactivate (GTK_SOURCE_VIEW (l->data));
+ }
+
+ g_list_free (views);
+}
+
diff --git a/plugins/modelines/gedit-modeline-plugin.h b/plugins/modelines/gedit-modeline-plugin.h
new file mode 100755
index 00000000..92b01e70
--- /dev/null
+++ b/plugins/modelines/gedit-modeline-plugin.h
@@ -0,0 +1,48 @@
+/*
+ * gedit-modeline-plugin.h
+ * Emacs, Kate and Vim-style modelines support for gedit.
+ *
+ * Copyright (C) 2005-2007 - Steve Frécinaux <[email protected]>
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GEDIT_MODELINE_PLUGIN_H__
+#define __GEDIT_MODELINE_PLUGIN_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gedit/gedit-plugin.h>
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_MODELINE_PLUGIN (gedit_modeline_plugin_get_type ())
+#define GEDIT_MODELINE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GEDIT_TYPE_MODELINE_PLUGIN, GeditModelinePlugin))
+#define GEDIT_MODELINE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GEDIT_TYPE_MODELINE_PLUGIN, GeditModelinePluginClass))
+#define GEDIT_IS_MODELINE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GEDIT_TYPE_MODELINE_PLUGIN))
+#define GEDIT_IS_MODELINE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GEDIT_TYPE_MODELINE_PLUGIN))
+#define GEDIT_MODELINE_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GEDIT_TYPE_MODELINE_PLUGIN, GeditModelinePluginClass))
+
+/* Private structure type */
+typedef GeditPluginClass GeditModelinePluginClass;
+typedef GeditPlugin GeditModelinePlugin;
+
+GType gedit_modeline_plugin_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT GType register_gedit_plugin (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __GEDIT_MODELINE_PLUGIN_H__ */
diff --git a/plugins/modelines/language-mappings b/plugins/modelines/language-mappings
new file mode 100755
index 00000000..47d00296
--- /dev/null
+++ b/plugins/modelines/language-mappings
@@ -0,0 +1,14 @@
+[vim]
+cs=c-sharp
+docbk=docbook
+javascript=js
+lhaskell=haskell-literate
+spec=rpmspec
+tex=latex
+xhtml=html
+
+[emacs]
+c++=cpp
+
+[kate]
+
diff --git a/plugins/modelines/modeline-parser.c b/plugins/modelines/modeline-parser.c
new file mode 100755
index 00000000..6feafc55
--- /dev/null
+++ b/plugins/modelines/modeline-parser.c
@@ -0,0 +1,852 @@
+/*
+ * modeline-parser.c
+ * Emacs, Kate and Vim-style modelines support for gedit.
+ *
+ * Copyright (C) 2005-2007 - Steve Frécinaux <[email protected]>
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <gedit/gedit-language-manager.h>
+#include <gedit/gedit-prefs-manager.h>
+#include <gedit/gedit-debug.h>
+#include "modeline-parser.h"
+
+#define MODELINES_LANGUAGE_MAPPINGS_FILE "language-mappings"
+
+/* base dir to lookup configuration files */
+static gchar *modelines_data_dir;
+
+/* Mappings: language name -> Gedit language ID */
+static GHashTable *vim_languages;
+static GHashTable *emacs_languages;
+static GHashTable *kate_languages;
+
+typedef enum
+{
+ MODELINE_SET_NONE = 0,
+ MODELINE_SET_TAB_WIDTH = 1 << 0,
+ MODELINE_SET_INDENT_WIDTH = 1 << 1,
+ MODELINE_SET_WRAP_MODE = 1 << 2,
+ MODELINE_SET_SHOW_RIGHT_MARGIN = 1 << 3,
+ MODELINE_SET_RIGHT_MARGIN_POSITION = 1 << 4,
+ MODELINE_SET_LANGUAGE = 1 << 5,
+ MODELINE_SET_INSERT_SPACES = 1 << 6
+} ModelineSet;
+
+typedef struct _ModelineOptions
+{
+ gchar *language_id;
+
+ /* these options are similar to the GtkSourceView properties of the
+ * same names.
+ */
+ gboolean insert_spaces;
+ guint tab_width;
+ guint indent_width;
+ GtkWrapMode wrap_mode;
+ gboolean display_right_margin;
+ guint right_margin_position;
+
+ ModelineSet set;
+} ModelineOptions;
+
+#define MODELINE_OPTIONS_DATA_KEY "ModelineOptionsDataKey"
+
+static gboolean
+has_option (ModelineOptions *options,
+ ModelineSet set)
+{
+ return options->set & set;
+}
+
+void
+modeline_parser_init (const gchar *data_dir)
+{
+ modelines_data_dir = g_strdup (data_dir);
+}
+
+void
+modeline_parser_shutdown ()
+{
+ if (vim_languages != NULL)
+ g_hash_table_destroy (vim_languages);
+
+ if (emacs_languages != NULL)
+ g_hash_table_destroy (emacs_languages);
+
+ if (kate_languages != NULL)
+ g_hash_table_destroy (kate_languages);
+
+ vim_languages = NULL;
+ emacs_languages = NULL;
+ kate_languages = NULL;
+
+ g_free (modelines_data_dir);
+}
+
+static GHashTable *
+load_language_mappings_group (GKeyFile *key_file, const gchar *group)
+{
+ GHashTable *table;
+ gchar **keys;
+ gsize length = 0;
+ int i;
+
+ table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ keys = g_key_file_get_keys (key_file, group, &length, NULL);
+
+ gedit_debug_message (DEBUG_PLUGINS,
+ "%" G_GSIZE_FORMAT " mappings in group %s",
+ length, group);
+
+ for (i = 0; i < length; i++)
+ {
+ gchar *name = keys[i];
+ gchar *id = g_key_file_get_string (key_file, group, name, NULL);
+ g_hash_table_insert (table, name, id);
+ }
+ g_free (keys);
+
+ return table;
+}
+
+/* lazy loading of language mappings */
+static void
+load_language_mappings (void)
+{
+ gchar *fname;
+ GKeyFile *mappings;
+ GError *error = NULL;
+
+ fname = g_build_filename (modelines_data_dir,
+ MODELINES_LANGUAGE_MAPPINGS_FILE,
+ NULL);
+
+ mappings = g_key_file_new ();
+
+ if (g_key_file_load_from_file (mappings, fname, 0, &error))
+ {
+ gedit_debug_message (DEBUG_PLUGINS,
+ "Loaded language mappings from %s",
+ fname);
+
+ vim_languages = load_language_mappings_group (mappings, "vim");
+ emacs_languages = load_language_mappings_group (mappings, "emacs");
+ kate_languages = load_language_mappings_group (mappings, "kate");
+ }
+ else
+ {
+ gedit_debug_message (DEBUG_PLUGINS,
+ "Failed to loaded language mappings from %s: %s",
+ fname, error->message);
+
+ g_error_free (error);
+ }
+
+ g_key_file_free (mappings);
+ g_free (fname);
+}
+
+static gchar *
+get_language_id (const gchar *language_name, GHashTable *mapping)
+{
+ gchar *name;
+ gchar *language_id;
+
+ name = g_ascii_strdown (language_name, -1);
+
+ language_id = g_hash_table_lookup (mapping, name);
+
+ if (language_id != NULL)
+ {
+ g_free (name);
+ return g_strdup (language_id);
+ }
+ else
+ {
+ /* by default assume that the gtksourcevuew id is the same */
+ return name;
+ }
+}
+
+static gchar *
+get_language_id_vim (const gchar *language_name)
+{
+ if (vim_languages == NULL)
+ load_language_mappings ();
+
+ return get_language_id (language_name, vim_languages);
+}
+
+static gchar *
+get_language_id_emacs (const gchar *language_name)
+{
+ if (emacs_languages == NULL)
+ load_language_mappings ();
+
+ return get_language_id (language_name, emacs_languages);
+}
+
+static gchar *
+get_language_id_kate (const gchar *language_name)
+{
+ if (kate_languages == NULL)
+ load_language_mappings ();
+
+ return get_language_id (language_name, kate_languages);
+}
+
+static gboolean
+skip_whitespaces (gchar **s)
+{
+ while (**s != '\0' && g_ascii_isspace (**s))
+ (*s)++;
+ return **s != '\0';
+}
+
+/* Parse vi(m) modelines.
+ * Vi(m) modelines looks like this:
+ * - first form: [text]{white}{vi:|vim:|ex:}[white]{options}
+ * - second form: [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text]
+ * They can happen on the three first or last lines.
+ */
+static gchar *
+parse_vim_modeline (gchar *s,
+ ModelineOptions *options)
+{
+ gboolean in_set = FALSE;
+ gboolean neg;
+ guint intval;
+ GString *key, *value;
+
+ key = g_string_sized_new (8);
+ value = g_string_sized_new (8);
+
+ while (*s != '\0' && !(in_set && *s == ':'))
+ {
+ while (*s != '\0' && (*s == ':' || g_ascii_isspace (*s)))
+ s++;
+
+ if (*s == '\0')
+ break;
+
+ if (strncmp (s, "set ", 4) == 0 ||
+ strncmp (s, "se ", 3) == 0)
+ {
+ s = strchr(s, ' ') + 1;
+ in_set = TRUE;
+ }
+
+ neg = FALSE;
+ if (strncmp (s, "no", 2) == 0)
+ {
+ neg = TRUE;
+ s += 2;
+ }
+
+ g_string_assign (key, "");
+ g_string_assign (value, "");
+
+ while (*s != '\0' && *s != ':' && *s != '=' &&
+ !g_ascii_isspace (*s))
+ {
+ g_string_append_c (key, *s);
+ s++;
+ }
+
+ if (*s == '=')
+ {
+ s++;
+ while (*s != '\0' && *s != ':' &&
+ !g_ascii_isspace (*s))
+ {
+ g_string_append_c (value, *s);
+ s++;
+ }
+ }
+
+ if (strcmp (key->str, "ft") == 0 ||
+ strcmp (key->str, "filetype") == 0)
+ {
+ g_free (options->language_id);
+ options->language_id = get_language_id_vim (value->str);
+
+ options->set |= MODELINE_SET_LANGUAGE;
+ }
+ else if (strcmp (key->str, "et") == 0 ||
+ strcmp (key->str, "expandtab") == 0)
+ {
+ options->insert_spaces = !neg;
+ options->set |= MODELINE_SET_INSERT_SPACES;
+ }
+ else if (strcmp (key->str, "ts") == 0 ||
+ strcmp (key->str, "tabstop") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->tab_width = intval;
+ options->set |= MODELINE_SET_TAB_WIDTH;
+ }
+ }
+ else if (strcmp (key->str, "sw") == 0 ||
+ strcmp (key->str, "shiftwidth") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->indent_width = intval;
+ options->set |= MODELINE_SET_INDENT_WIDTH;
+ }
+ }
+ else if (strcmp (key->str, "wrap") == 0)
+ {
+ options->wrap_mode = neg ? GTK_WRAP_NONE : GTK_WRAP_WORD;
+
+ options->set |= MODELINE_SET_WRAP_MODE;
+ }
+ else if (strcmp (key->str, "textwidth") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->right_margin_position = intval;
+ options->display_right_margin = TRUE;
+
+ options->set |= MODELINE_SET_SHOW_RIGHT_MARGIN |
+ MODELINE_SET_RIGHT_MARGIN_POSITION;
+
+ }
+ }
+ }
+
+ g_string_free (key, TRUE);
+ g_string_free (value, TRUE);
+
+ return s;
+}
+
+/* Parse emacs modelines.
+ * Emacs modelines looks like this: "-*- key1: value1; key2: value2 -*-"
+ * They can happen on the first line, or on the second one if the first line is
+ * a shebang (#!)
+ * See http://www.delorie.com/gnu/docs/emacs/emacs_486.html
+ */
+static gchar *
+parse_emacs_modeline (gchar *s,
+ ModelineOptions *options)
+{
+ guint intval;
+ GString *key, *value;
+
+ key = g_string_sized_new (8);
+ value = g_string_sized_new (8);
+
+ while (*s != '\0')
+ {
+ while (*s != '\0' && (*s == ';' || g_ascii_isspace (*s)))
+ s++;
+ if (*s == '\0' || strncmp (s, "-*-", 3) == 0)
+ break;
+
+ g_string_assign (key, "");
+ g_string_assign (value, "");
+
+ while (*s != '\0' && *s != ':' && *s != ';' &&
+ !g_ascii_isspace (*s))
+ {
+ g_string_append_c (key, *s);
+ s++;
+ }
+
+ if (!skip_whitespaces (&s))
+ break;
+
+ if (*s != ':')
+ continue;
+ s++;
+
+ if (!skip_whitespaces (&s))
+ break;
+
+ while (*s != '\0' && *s != ';' && !g_ascii_isspace (*s))
+ {
+ g_string_append_c (value, *s);
+ s++;
+ }
+
+ gedit_debug_message (DEBUG_PLUGINS,
+ "Emacs modeline bit: %s = %s",
+ key->str, value->str);
+
+ /* "Mode" key is case insenstive */
+ if (g_ascii_strcasecmp (key->str, "Mode") == 0)
+ {
+ g_free (options->language_id);
+ options->language_id = get_language_id_emacs (value->str);
+
+ options->set |= MODELINE_SET_LANGUAGE;
+ }
+ else if (strcmp (key->str, "tab-width") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->tab_width = intval;
+ options->set |= MODELINE_SET_TAB_WIDTH;
+ }
+ }
+ else if (strcmp (key->str, "indent-offset") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->indent_width = intval;
+ options->set |= MODELINE_SET_INDENT_WIDTH;
+ }
+ }
+ else if (strcmp (key->str, "indent-tabs-mode") == 0)
+ {
+ intval = strcmp (value->str, "nil") == 0;
+ options->insert_spaces = intval;
+
+ options->set |= MODELINE_SET_INSERT_SPACES;
+ }
+ else if (strcmp (key->str, "autowrap") == 0)
+ {
+ intval = strcmp (value->str, "nil") != 0;
+ options->wrap_mode = intval ? GTK_WRAP_WORD : GTK_WRAP_NONE;
+
+ options->set |= MODELINE_SET_WRAP_MODE;
+ }
+ }
+
+ g_string_free (key, TRUE);
+ g_string_free (value, TRUE);
+
+ return *s == '\0' ? s : s + 2;
+}
+
+/*
+ * Parse kate modelines.
+ * Kate modelines are of the form "kate: key1 value1; key2 value2;"
+ * These can happen on the 10 first or 10 last lines of the buffer.
+ * See http://wiki.kate-editor.org/index.php/Modelines
+ */
+static gchar *
+parse_kate_modeline (gchar *s,
+ ModelineOptions *options)
+{
+ guint intval;
+ GString *key, *value;
+
+ key = g_string_sized_new (8);
+ value = g_string_sized_new (8);
+
+ while (*s != '\0')
+ {
+ while (*s != '\0' && (*s == ';' || g_ascii_isspace (*s)))
+ s++;
+ if (*s == '\0')
+ break;
+
+ g_string_assign (key, "");
+ g_string_assign (value, "");
+
+ while (*s != '\0' && *s != ';' && !g_ascii_isspace (*s))
+ {
+ g_string_append_c (key, *s);
+ s++;
+ }
+
+ if (!skip_whitespaces (&s))
+ break;
+ if (*s == ';')
+ continue;
+
+ while (*s != '\0' && *s != ';' &&
+ !g_ascii_isspace (*s))
+ {
+ g_string_append_c (value, *s);
+ s++;
+ }
+
+ gedit_debug_message (DEBUG_PLUGINS,
+ "Kate modeline bit: %s = %s",
+ key->str, value->str);
+
+ if (strcmp (key->str, "hl") == 0 ||
+ strcmp (key->str, "syntax") == 0)
+ {
+ g_free (options->language_id);
+ options->language_id = get_language_id_kate (value->str);
+
+ options->set |= MODELINE_SET_LANGUAGE;
+ }
+ else if (strcmp (key->str, "tab-width") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->tab_width = intval;
+ options->set |= MODELINE_SET_TAB_WIDTH;
+ }
+ }
+ else if (strcmp (key->str, "indent-width") == 0)
+ {
+ intval = atoi (value->str);
+ if (intval) options->indent_width = intval;
+ }
+ else if (strcmp (key->str, "space-indent") == 0)
+ {
+ intval = strcmp (value->str, "on") == 0 ||
+ strcmp (value->str, "true") == 0 ||
+ strcmp (value->str, "1") == 0;
+
+ options->insert_spaces = intval;
+ options->set |= MODELINE_SET_INSERT_SPACES;
+ }
+ else if (strcmp (key->str, "word-wrap") == 0)
+ {
+ intval = strcmp (value->str, "on") == 0 ||
+ strcmp (value->str, "true") == 0 ||
+ strcmp (value->str, "1") == 0;
+
+ options->wrap_mode = intval ? GTK_WRAP_WORD : GTK_WRAP_NONE;
+
+ options->set |= MODELINE_SET_WRAP_MODE;
+ }
+ else if (strcmp (key->str, "word-wrap-column") == 0)
+ {
+ intval = atoi (value->str);
+
+ if (intval)
+ {
+ options->right_margin_position = intval;
+ options->display_right_margin = TRUE;
+
+ options->set |= MODELINE_SET_RIGHT_MARGIN_POSITION |
+ MODELINE_SET_SHOW_RIGHT_MARGIN;
+ }
+ }
+ }
+
+ g_string_free (key, TRUE);
+ g_string_free (value, TRUE);
+
+ return s;
+}
+
+/* Scan a line for vi(m)/emacs/kate modelines.
+ * Line numbers are counted starting at one.
+ */
+static void
+parse_modeline (gchar *s,
+ gint line_number,
+ gint line_count,
+ ModelineOptions *options)
+{
+ gchar prev;
+
+ /* look for the beginning of a modeline */
+ for (prev = ' '; (s != NULL) && (*s != '\0'); prev = *(s++))
+ {
+ if (!g_ascii_isspace (prev))
+ continue;
+
+ if ((line_number <= 3 || line_number > line_count - 3) &&
+ (strncmp (s, "ex:", 3) == 0 ||
+ strncmp (s, "vi:", 3) == 0 ||
+ strncmp (s, "vim:", 4) == 0))
+ {
+ gedit_debug_message (DEBUG_PLUGINS, "Vim modeline on line %d", line_number);
+
+ while (*s != ':') s++;
+ s = parse_vim_modeline (s + 1, options);
+ }
+ else if (line_number <= 2 && strncmp (s, "-*-", 3) == 0)
+ {
+ gedit_debug_message (DEBUG_PLUGINS, "Emacs modeline on line %d", line_number);
+
+ s = parse_emacs_modeline (s + 3, options);
+ }
+ else if ((line_number <= 10 || line_number > line_count - 10) &&
+ strncmp (s, "kate:", 5) == 0)
+ {
+ gedit_debug_message (DEBUG_PLUGINS, "Kate modeline on line %d", line_number);
+
+ s = parse_kate_modeline (s + 5, options);
+ }
+ }
+}
+
+static gboolean
+check_previous (GtkSourceView *view,
+ ModelineOptions *previous,
+ ModelineSet set)
+{
+ GtkSourceBuffer *buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
+
+ /* Do not restore default when this is the first time */
+ if (!previous)
+ return FALSE;
+
+ /* Do not restore default when previous was not set */
+ if (!(previous->set & set))
+ return FALSE;
+
+ /* Only restore default when setting has not changed */
+ switch (set)
+ {
+ case MODELINE_SET_INSERT_SPACES:
+ return gtk_source_view_get_insert_spaces_instead_of_tabs (view) ==
+ previous->insert_spaces;
+ break;
+ case MODELINE_SET_TAB_WIDTH:
+ return gtk_source_view_get_tab_width (view) == previous->tab_width;
+ break;
+ case MODELINE_SET_INDENT_WIDTH:
+ return gtk_source_view_get_indent_width (view) == previous->indent_width;
+ break;
+ case MODELINE_SET_WRAP_MODE:
+ return gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view)) ==
+ previous->wrap_mode;
+ break;
+ case MODELINE_SET_RIGHT_MARGIN_POSITION:
+ return gtk_source_view_get_right_margin_position (view) ==
+ previous->right_margin_position;
+ break;
+ case MODELINE_SET_SHOW_RIGHT_MARGIN:
+ return gtk_source_view_get_show_right_margin (view) ==
+ previous->display_right_margin;
+ break;
+ case MODELINE_SET_LANGUAGE:
+ {
+ GtkSourceLanguage *language = gtk_source_buffer_get_language (buffer);
+
+ return (language == NULL && previous->language_id == NULL) ||
+ (language != NULL && g_strcmp0 (gtk_source_language_get_id (language),
+ previous->language_id) == 0);
+ }
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+}
+
+static void
+free_modeline_options (ModelineOptions *options)
+{
+ g_free (options->language_id);
+ g_slice_free (ModelineOptions, options);
+}
+
+void
+modeline_parser_apply_modeline (GtkSourceView *view)
+{
+ ModelineOptions options;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter, liter;
+ gint line_count;
+
+ options.language_id = NULL;
+ options.set = MODELINE_SET_NONE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+
+ line_count = gtk_text_buffer_get_line_count (buffer);
+
+ /* Parse the modelines on the 10 first lines... */
+ while ((gtk_text_iter_get_line (&iter) < 10) &&
+ !gtk_text_iter_is_end (&iter))
+ {
+ gchar *line;
+
+ liter = iter;
+ gtk_text_iter_forward_to_line_end (&iter);
+ line = gtk_text_buffer_get_text (buffer, &liter, &iter, TRUE);
+
+ parse_modeline (line,
+ 1 + gtk_text_iter_get_line (&iter),
+ line_count,
+ &options);
+
+ gtk_text_iter_forward_line (&iter);
+
+ g_free (line);
+ }
+
+ /* ...and on the 10 last ones (modelines are not allowed in between) */
+ if (!gtk_text_iter_is_end (&iter))
+ {
+ gint cur_line;
+ guint remaining_lines;
+
+ /* we are on the 11th line (count from 0) */
+ cur_line = gtk_text_iter_get_line (&iter);
+ /* g_assert (10 == cur_line); */
+
+ remaining_lines = line_count - cur_line - 1;
+
+ if (remaining_lines > 10)
+ {
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_iter_backward_lines (&iter, 9);
+ }
+ }
+
+ while (!gtk_text_iter_is_end (&iter))
+ {
+ gchar *line;
+
+ liter = iter;
+ gtk_text_iter_forward_to_line_end (&iter);
+ line = gtk_text_buffer_get_text (buffer, &liter, &iter, TRUE);
+
+ parse_modeline (line,
+ 1 + gtk_text_iter_get_line (&iter),
+ line_count,
+ &options);
+
+ gtk_text_iter_forward_line (&iter);
+
+ g_free (line);
+ }
+
+ /* Try to set language */
+ if (has_option (&options, MODELINE_SET_LANGUAGE) && options.language_id)
+ {
+ GtkSourceLanguageManager *manager;
+ GtkSourceLanguage *language;
+
+ manager = gedit_get_language_manager ();
+ language = gtk_source_language_manager_get_language
+ (manager, options.language_id);
+
+ if (language != NULL)
+ {
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer),
+ language);
+ }
+ }
+
+ ModelineOptions *previous = g_object_get_data (G_OBJECT (buffer),
+ MODELINE_OPTIONS_DATA_KEY);
+
+ /* Apply the options we got from modelines and restore defaults if
+ we set them before */
+ if (has_option (&options, MODELINE_SET_INSERT_SPACES))
+ {
+ gtk_source_view_set_insert_spaces_instead_of_tabs
+ (view, options.insert_spaces);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_INSERT_SPACES))
+ {
+ gtk_source_view_set_insert_spaces_instead_of_tabs
+ (view,
+ gedit_prefs_manager_get_insert_spaces ());
+ }
+
+ if (has_option (&options, MODELINE_SET_TAB_WIDTH))
+ {
+ gtk_source_view_set_tab_width (view, options.tab_width);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_TAB_WIDTH))
+ {
+ gtk_source_view_set_tab_width (view,
+ gedit_prefs_manager_get_tabs_size ());
+ }
+
+ if (has_option (&options, MODELINE_SET_INDENT_WIDTH))
+ {
+ gtk_source_view_set_indent_width (view, options.indent_width);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_INDENT_WIDTH))
+ {
+ gtk_source_view_set_indent_width (view, -1);
+ }
+
+ if (has_option (&options, MODELINE_SET_WRAP_MODE))
+ {
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), options.wrap_mode);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_WRAP_MODE))
+ {
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view),
+ gedit_prefs_manager_get_wrap_mode ());
+ }
+
+ if (has_option (&options, MODELINE_SET_RIGHT_MARGIN_POSITION))
+ {
+ gtk_source_view_set_right_margin_position (view, options.right_margin_position);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_RIGHT_MARGIN_POSITION))
+ {
+ gtk_source_view_set_right_margin_position (view,
+ gedit_prefs_manager_get_right_margin_position ());
+ }
+
+ if (has_option (&options, MODELINE_SET_SHOW_RIGHT_MARGIN))
+ {
+ gtk_source_view_set_show_right_margin (view, options.display_right_margin);
+ }
+ else if (check_previous (view, previous, MODELINE_SET_SHOW_RIGHT_MARGIN))
+ {
+ gtk_source_view_set_show_right_margin (view,
+ gedit_prefs_manager_get_display_right_margin ());
+ }
+
+ if (previous)
+ {
+ *previous = options;
+ previous->language_id = g_strdup (options.language_id);
+ }
+ else
+ {
+ previous = g_slice_new (ModelineOptions);
+ *previous = options;
+ previous->language_id = g_strdup (options.language_id);
+
+ g_object_set_data_full (G_OBJECT (buffer),
+ MODELINE_OPTIONS_DATA_KEY,
+ previous,
+ (GDestroyNotify)free_modeline_options);
+ }
+
+ g_free (options.language_id);
+}
+
+void
+modeline_parser_deactivate (GtkSourceView *view)
+{
+ g_object_set_data (G_OBJECT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))),
+ MODELINE_OPTIONS_DATA_KEY,
+ NULL);
+}
+
+/* vi:ts=8 */
diff --git a/plugins/modelines/modeline-parser.h b/plugins/modelines/modeline-parser.h
new file mode 100755
index 00000000..2e8559e4
--- /dev/null
+++ b/plugins/modelines/modeline-parser.h
@@ -0,0 +1,37 @@
+/*
+ * modelie-parser.h
+ * Emacs, Kate and Vim-style modelines support for gedit.
+ *
+ * Copyright (C) 2005-2007 - Steve Frécinaux <[email protected]>
+ *
+ * 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MODELINE_PARSER_H__
+#define __MODELINE_PARSER_H__
+
+#include <glib.h>
+#include <gtksourceview/gtksourceview.h>
+
+G_BEGIN_DECLS
+
+void modeline_parser_init (const gchar *data_dir);
+void modeline_parser_shutdown (void);
+void modeline_parser_apply_modeline (GtkSourceView *view);
+void modeline_parser_deactivate (GtkSourceView *view);
+
+G_END_DECLS
+
+#endif /* __MODELINE_PARSER_H__ */
diff --git a/plugins/modelines/modelines.gedit-plugin.desktop.in b/plugins/modelines/modelines.gedit-plugin.desktop.in
new file mode 100755
index 00000000..c72f0199
--- /dev/null
+++ b/plugins/modelines/modelines.gedit-plugin.desktop.in
@@ -0,0 +1,8 @@
+[Gedit Plugin]
+Module=modelines
+IAge=2
+_Name=Modelines
+_Description=Emacs, Kate and Vim-style modelines support for gedit.
+Authors=Steve Frécinaux <[email protected]>
+Copyright=Copyright © 2005 Steve Frécinaux
+Website=http://www.gedit.org