[gnome-builder/wip/libide] libide: move c indenter into libide



commit cb338efb8b32491ee8a7d6ae8911f237928c37a5
Author: Christian Hergert <christian hergert me>
Date:   Tue Feb 17 22:48:49 2015 -0800

    libide: move c indenter into libide

 libide/Makefile.am        |    2 +
 libide/c/c-parse-helper.c |  252 ++++++++
 libide/c/c-parse-helper.h |   41 ++
 libide/c/ide-c-indenter.c | 1387 +++++++++++++++++++++++++++++++++++++++++++--
 libide/c/ide-c-indenter.h |    7 +-
 5 files changed, 1649 insertions(+), 40 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index ad64a95..7a717cb 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -142,6 +142,8 @@ libide_1_0_la_public_sources = \
 
 libide_1_0_la_SOURCES = \
        $(libide_1_0_la_public_sources) \
+       libide/c/c-parse-helper.c \
+       libide/c/c-parse-helper.h \
        libide/editorconfig/editorconfig-glib.c \
        libide/editorconfig/editorconfig-glib.h \
        libide/fuzzy/fuzzy.c \
diff --git a/libide/c/c-parse-helper.c b/libide/c/c-parse-helper.c
new file mode 100644
index 0000000..43ce7ed
--- /dev/null
+++ b/libide/c/c-parse-helper.c
@@ -0,0 +1,252 @@
+/* c-parse-helper.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "c-parser"
+
+#include <string.h>
+
+#include "c-parse-helper.h"
+
+void
+parameter_free (Parameter *p)
+{
+  if (p)
+    {
+      g_free (p->name);
+      g_free (p->type);
+      g_free (p);
+    }
+}
+
+Parameter *
+parameter_copy (const Parameter *src)
+{
+  Parameter *copy;
+
+  copy = g_new0 (Parameter, 1);
+  copy->name = g_strdup (src->name);
+  copy->type = g_strdup (src->type);
+  copy->ellipsis = src->ellipsis;
+  copy->n_star = src->n_star;
+
+  return copy;
+}
+
+gboolean
+parameter_validate (Parameter *param)
+{
+  const gchar *tmp;
+
+  if (param->ellipsis)
+    return TRUE;
+
+  if (!param->name || !param->type)
+    return FALSE;
+
+  for (tmp = param->name; *tmp; tmp = g_utf8_next_char (tmp))
+    {
+      gunichar ch = g_utf8_get_char (tmp);
+
+      switch (ch) {
+      case '_':
+      case '[':
+      case ']':
+        continue;
+
+      default:
+        if (g_unichar_isalnum (ch))
+          continue;
+        break;
+      }
+
+      return FALSE;
+    }
+
+  for (tmp = param->type; *tmp; tmp = g_utf8_next_char (tmp))
+    {
+      gunichar ch = g_utf8_get_char (tmp);
+
+      switch (ch) {
+      case '*':
+      case ' ':
+      case '_':
+        continue;
+
+      default:
+        if (g_unichar_isalnum (ch))
+          continue;
+        break;
+      }
+
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+parameter_compute (Parameter *param)
+{
+  const gchar *tmp;
+  gchar *rev;
+  guint n_star = 0;
+
+  rev = g_utf8_strreverse (param->type, -1);
+
+  for (tmp = rev; tmp; tmp = g_utf8_next_char (tmp))
+    {
+      switch (g_utf8_get_char (tmp))
+        {
+        case ' ':
+          break;
+
+        case '*':
+          n_star++;
+          break;
+
+        default:
+          if (n_star)
+            {
+              gchar *cleaned;
+
+              cleaned = g_strstrip (g_utf8_strreverse (tmp, -1));
+              g_free (param->type);
+              param->type = cleaned;
+            }
+          goto finish;
+        }
+    }
+
+finish:
+  param->n_star = n_star;
+
+  g_free (rev);
+}
+
+GSList *
+parse_parameters (const gchar *text)
+{
+  GSList *ret = NULL;
+  gchar **parts = NULL;
+  guint i;
+
+  parts = g_strsplit (text, ",", 0);
+
+  for (i = 0; parts [i]; i++)
+    {
+      const gchar *tmp;
+      const gchar *word;
+
+      word = g_strstrip (parts [i]);
+
+      if (!*word)
+        goto failure;
+
+      if (g_strcmp0 (word, "...") == 0)
+        {
+          Parameter param = { NULL, NULL, TRUE };
+          ret = g_slist_append (ret, parameter_copy (&param));
+          continue;
+        }
+
+      /*
+       * Check that each word only contains valid characters for a
+       * parameter list.
+       */
+      for (tmp = word; *tmp; tmp = g_utf8_next_char (tmp))
+        {
+          gunichar ch;
+
+          ch = g_utf8_get_char (tmp);
+
+          switch (ch)
+            {
+            case '\t':
+            case ' ':
+            case '*':
+            case '_':
+            case '[':
+            case ']':
+              break;
+
+            default:
+              if (g_unichar_isalnum (ch))
+                break;
+
+              goto failure;
+            }
+        }
+
+      if (strchr (word, '[') && strchr (word, ']'))
+        {
+          /*
+           * TODO: Special case parsing of parameters that have [] after the
+           *       name. Such as "char foo[12]" or "char foo[static 12]".
+           */
+        }
+      else
+        {
+          const gchar *name_sep;
+          Parameter param = { 0 };
+          gboolean success = FALSE;
+          gchar *reversed = NULL;
+          gchar *name_rev = NULL;
+
+          reversed = g_utf8_strreverse (word, -1);
+          name_sep = strpbrk (reversed, "\t\n *");
+
+          if (name_sep && *name_sep && *(name_sep + 1))
+            {
+              name_rev = g_strndup (reversed, name_sep - reversed);
+
+              param.name = g_strstrip (g_utf8_strreverse (name_rev, -1));
+              param.type = g_strstrip (g_utf8_strreverse (name_sep, -1));
+
+              parameter_compute (&param);
+
+              if (parameter_validate (&param))
+                {
+                  ret = g_slist_append (ret, parameter_copy (&param));
+                  success = TRUE;
+                }
+
+              g_free (param.name);
+              g_free (param.type);
+              g_free (name_rev);
+            }
+
+          g_free (reversed);
+
+          if (success)
+            continue;
+        }
+
+      goto failure;
+    }
+
+  goto cleanup;
+
+failure:
+  g_slist_foreach (ret, (GFunc)parameter_free, NULL);
+  g_clear_pointer (&ret, g_slist_free);
+
+cleanup:
+  g_strfreev (parts);
+
+  return ret;
+}
diff --git a/libide/c/c-parse-helper.h b/libide/c/c-parse-helper.h
new file mode 100644
index 0000000..418f518
--- /dev/null
+++ b/libide/c/c-parse-helper.h
@@ -0,0 +1,41 @@
+/* c-parse-helper.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef C_PARSE_HELPER_H
+#define C_PARSE_HELPER_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  gchar *type;
+  gchar *name;
+  guint  ellipsis : 1;
+  guint  n_star   : 4;
+} Parameter;
+
+gboolean   parameter_validate (Parameter       *param);
+void       parameter_free     (Parameter       *p);
+Parameter *parameter_copy     (const Parameter *src);
+GSList    *parse_parameters   (const gchar     *text);
+
+G_END_DECLS
+
+#endif /* C_PARSE_HELPER_H */
diff --git a/libide/c/ide-c-indenter.c b/libide/c/ide-c-indenter.c
index 926dd2d..292c35a 100644
--- a/libide/c/ide-c-indenter.c
+++ b/libide/c/ide-c-indenter.c
@@ -18,72 +18,1391 @@
 
 #include <glib/gi18n.h>
 
+#include "c-parse-helper.h"
 #include "ide-c-indenter.h"
 
-typedef struct
-{
-  void *foo;
-} IdeCIndenterPrivate;
+#define ITER_INIT_LINE_START(iter, other) \
+  gtk_text_buffer_get_iter_at_line( \
+    gtk_text_iter_get_buffer(other), \
+    (iter), \
+    gtk_text_iter_get_line(other))
+
+#define ENTRY
+#define EXIT return
+#define GOTO(_l) goto _l
+#define RETURN(_v) return _v
 
-G_DEFINE_TYPE_WITH_PRIVATE (IdeCIndenter, ide_c_indenter, IDE_TYPE_INDENTER)
+struct _IdeCIndenter
+{
+  IdeIndenter parent_instance;
 
-enum {
-  PROP_0,
-  LAST_PROP
+  gint  scope_indent;
+  gint  condition_indent;
+  gint  directive_indent;
+  guint space_before_paren : 1;
 };
 
-static GParamSpec *gParamSpecs [LAST_PROP];
+G_DEFINE_TYPE (IdeCIndenter, ide_c_indenter, IDE_TYPE_INDENTER)
 
-static void
-ide_c_indenter_finalize (GObject *object)
+static gunichar
+text_iter_peek_next_char (const GtkTextIter *location)
 {
-  IdeCIndenter *self = (IdeCIndenter *)object;
-  IdeCIndenterPrivate *priv = ide_c_indenter_get_instance_private (self);
+  GtkTextIter iter = *location;
+
+  if (gtk_text_iter_forward_char (&iter))
+    return gtk_text_iter_get_char (&iter);
 
-  G_OBJECT_CLASS (ide_c_indenter_parent_class)->finalize (object);
+  return 0;
 }
 
-static void
-ide_c_indenter_get_property (GObject    *object,
-                             guint       prop_id,
-                             GValue     *value,
-                             GParamSpec *pspec)
+static gunichar
+text_iter_peek_prev_char (const GtkTextIter *location)
 {
-  IdeCIndenter *self = IDE_C_INDENTER (object);
+  GtkTextIter iter = *location;
+
+  if (gtk_text_iter_backward_char (&iter))
+    return gtk_text_iter_get_char (&iter);
+
+  return 0;
+}
+
+static inline void
+build_indent (IdeCIndenter *c,
+              guint                  line_offset,
+              GtkTextIter           *matching_line,
+              GString               *str)
+{
+  GtkTextIter iter;
+  gunichar ch;
+
+  if (!line_offset)
+    return;
 
-  switch (prop_id)
+  gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (matching_line),
+                                    &iter,
+                                    gtk_text_iter_get_line (matching_line));
+
+  do {
+    ch = gtk_text_iter_get_char (&iter);
+
+    switch (ch)
+      {
+      case '\t':
+      case ' ':
+        g_string_append_unichar (str, ch);
+        break;
+
+      default:
+        g_string_append_c (str, ' ');
+        break;
+      }
+  } while (gtk_text_iter_forward_char (&iter) &&
+           (gtk_text_iter_compare (&iter, matching_line) <= 0) &&
+           (str->len < line_offset));
+
+  while (str->len < line_offset)
+    g_string_append_c (str, ' ');
+}
+
+static gboolean
+iter_ends_c89_comment (const GtkTextIter *iter)
+{
+  if (gtk_text_iter_get_char (iter) == '/')
     {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      GtkTextIter tmp;
+
+      tmp = *iter;
+
+      if (gtk_text_iter_backward_char (&tmp) &&
+          ('*' == gtk_text_iter_get_char (&tmp)))
+        return TRUE;
     }
+
+  return FALSE;
 }
 
-static void
-ide_c_indenter_set_property (GObject      *object,
-                             guint         prop_id,
-                             const GValue *value,
-                             GParamSpec   *pspec)
+static gboolean
+non_space_predicate (gunichar ch,
+                     gpointer user_data)
+{
+  return !g_unichar_isspace (ch);
+}
+
+static gboolean
+line_is_whitespace_until (GtkTextIter *iter)
+{
+  GtkTextIter cur;
+
+  gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (iter),
+                                    &cur,
+                                    gtk_text_iter_get_line (iter));
+
+  for (;
+       gtk_text_iter_compare (&cur, iter) < 0;
+       gtk_text_iter_forward_char (&cur))
+    {
+      if (!g_unichar_isspace (gtk_text_iter_get_char (&cur)))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+backward_find_keyword (GtkTextIter *iter,
+                       const gchar *keyword,
+                       GtkTextIter *limit)
+{
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  /*
+   * If we find the keyword, check to see that the character before it
+   * is either a newline or some other space character. (ie, not part of a
+   * function name like foo_do().
+   */
+  if (gtk_text_iter_backward_search (iter, keyword, GTK_TEXT_SEARCH_TEXT_ONLY,
+                                     &begin, &end, limit))
+    {
+      GtkTextIter copy;
+      gunichar ch;
+
+      gtk_text_iter_assign (&copy, &begin);
+
+      if (!gtk_text_iter_backward_char (&copy) ||
+          !(ch = gtk_text_iter_get_char (&copy)) ||
+          g_unichar_isspace (ch))
+        {
+          gtk_text_iter_assign (iter, &begin);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+backward_find_condition_keyword (GtkTextIter *iter)
+{
+  GtkTextIter line_start;
+
+  ITER_INIT_LINE_START (&line_start, iter);
+
+  if (backward_find_keyword (iter, "else if", &line_start) ||
+      backward_find_keyword (iter, "else", &line_start) ||
+      backward_find_keyword (iter, "if", &line_start) ||
+      backward_find_keyword (iter, "do", &line_start) ||
+      backward_find_keyword (iter, "while", &line_start) ||
+      backward_find_keyword (iter, "switch", &line_start) ||
+      backward_find_keyword (iter, "for", &line_start))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gchar *
+backward_last_word (GtkTextIter *iter,
+                    GtkTextIter *begin)
+{
+  gtk_text_iter_assign (begin, iter);
+
+  if (gtk_text_iter_backward_word_start (begin))
+    {
+      GtkTextIter end;
+
+      gtk_text_iter_assign (&end, begin);
+
+      if (gtk_text_iter_ends_word (&end) ||
+          gtk_text_iter_forward_word_end (&end))
+        return gtk_text_iter_get_slice (begin, &end);
+    }
+
+  return NULL;
+}
+
+static gboolean
+backward_before_c89_comment (GtkTextIter *iter)
+{
+  GtkTextIter copy;
+  GtkTextIter match_start;
+  GtkTextIter match_end;
+  gunichar ch;
+
+  gtk_text_iter_assign (&copy, iter);
+
+  while (g_unichar_isspace (gtk_text_iter_get_char (iter)))
+    if (!gtk_text_iter_backward_char (iter))
+      GOTO (cleanup);
+
+  if (!(ch = gtk_text_iter_get_char (iter)) ||
+      (ch != '/') ||
+      !gtk_text_iter_backward_char (iter) ||
+      !(ch = gtk_text_iter_get_char (iter)) ||
+      (ch != '*') ||
+      !gtk_text_iter_backward_search (iter, "/*",
+                                      GTK_TEXT_SEARCH_TEXT_ONLY,
+                                      &match_start, &match_end, NULL) ||
+      !gtk_text_iter_backward_find_char (&match_start, non_space_predicate,
+                                         NULL, NULL))
+    GOTO (cleanup);
+
+  gtk_text_iter_assign (iter, &match_start);
+
+  return TRUE;
+
+cleanup:
+  gtk_text_iter_assign (iter, &copy);
+
+  return FALSE;
+}
+
+static gboolean
+backward_find_matching_char (GtkTextIter *iter,
+                             gunichar     ch)
+{
+  GtkTextIter copy;
+  gunichar match;
+  gunichar cur;
+  guint count = 1;
+
+  switch (ch) {
+  case ')':
+    match = '(';
+    break;
+  case '}':
+    match = '{';
+    break;
+  case '[':
+    match = ']';
+    break;
+  default:
+    g_assert_not_reached ();
+    break;
+  }
+
+  gtk_text_iter_assign (&copy, iter);
+
+  while (gtk_text_iter_backward_char (iter))
+    {
+      cur = gtk_text_iter_get_char (iter);
+
+      if ((cur == '\'') || (cur == '"'))
+        {
+          gunichar strcur = 0;
+
+          while (gtk_text_iter_backward_char (iter))
+            {
+              strcur = gtk_text_iter_get_char (iter);
+              if (strcur == cur)
+                {
+                  GtkTextIter copy2 = *iter;
+
+                  /* check if the character before this is an escape char */
+                  if (gtk_text_iter_backward_char (&copy2) &&
+                      ('\\' == gtk_text_iter_get_char (&copy2)))
+                    continue;
+
+                  break;
+                }
+            }
+
+          if (strcur != cur)
+            break;
+        }
+      else if ((cur == '/') && iter_ends_c89_comment (iter))
+        {
+          GtkTextIter tmp = *iter;
+
+          if (backward_before_c89_comment (&tmp))
+            {
+              *iter = tmp;
+              cur = gtk_text_iter_get_char (iter);
+            }
+        }
+
+      if (cur == match)
+        {
+          if (--count == 0)
+            return TRUE;
+        }
+      else if (cur == ch)
+        count++;
+    }
+
+  gtk_text_iter_assign (iter, &copy);
+
+  return FALSE;
+}
+
+static gboolean
+line_is_space (GtkTextIter *iter)
+{
+  GtkTextIter begin;
+
+  gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (iter),
+                                    &begin,
+                                    gtk_text_iter_get_line (iter));
+
+  for (;
+       gtk_text_iter_compare (&begin, iter) < 0;
+       gtk_text_iter_forward_char (&begin))
+    {
+      if (!g_unichar_isspace (gtk_text_iter_get_char (&begin)))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+starts_line_space_ok (GtkTextIter *iter)
+{
+  GtkTextIter tmp;
+
+  gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (iter),
+                                    &tmp,
+                                    gtk_text_iter_get_line (iter));
+
+  for (;
+       gtk_text_iter_compare (&tmp, iter) < 0;
+       gtk_text_iter_forward_char (&tmp))
+    {
+      if (!g_unichar_isspace (gtk_text_iter_get_char (&tmp)))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+backward_find_stmt_expr (GtkTextIter *iter)
+{
+  return FALSE;
+}
+
+static gboolean
+backward_to_line_first_char (GtkTextIter *iter)
+{
+  GtkTextIter tmp;
+
+  gtk_text_buffer_get_iter_at_line (gtk_text_iter_get_buffer (iter),
+                                    &tmp,
+                                    gtk_text_iter_get_line (iter));
+
+  while (gtk_text_iter_compare (&tmp, iter) <= 0)
+    {
+      gunichar ch = gtk_text_iter_get_char (&tmp);
+
+      if (!g_unichar_isspace (ch))
+        {
+          gtk_text_iter_assign (iter, &tmp);
+          return TRUE;
+        }
+
+      if (!gtk_text_iter_forward_char (&tmp))
+        break;
+    }
+
+  return FALSE;
+}
+
+/**
+ * text_iter_in_c89_comment:
+ * @location: (in): A #GtkTextIter containing the target location.
+ *
+ * The algorith for this is unfortunately trickier than one would expect.
+ * Because we could always still have context if we walk backwards that
+ * would let us know if we are in a string, we just start from the beginning
+ * of the buffer and try to skip forward until we get to our target
+ * position.
+ *
+ * Returns: %TRUE if we think we are in a c89 comment, otherwise %FALSE.
+ */
+static gboolean
+in_c89_comment (const GtkTextIter *location,
+                GtkTextIter       *match_begin)
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter iter;
+  GtkTextIter after_location;
+
+  buffer = gtk_text_iter_get_buffer (location);
+  gtk_text_buffer_get_start_iter (buffer, &iter);
+
+  after_location = *location;
+  gtk_text_iter_forward_char (&after_location);
+
+  do
+    {
+      gunichar ch;
+
+      if (gtk_text_iter_compare (&iter, location) > 0)
+        break;
+
+      ch = gtk_text_iter_get_char (&iter);
+
+      /* skip past the c89 comment */
+      if ((ch == '/') && (text_iter_peek_next_char (&iter) == '*'))
+        {
+          GtkTextIter saved = iter;
+
+          if (!gtk_text_iter_forward_chars (&iter, 2) ||
+              !gtk_text_iter_forward_search (&iter, "*/",
+                                             GTK_TEXT_SEARCH_TEXT_ONLY,
+                                             NULL, &iter, &after_location))
+            {
+              *match_begin = saved;
+              RETURN (TRUE);
+            }
+        }
+
+      /* skip past a string or character */
+      if ((ch == '\'') || (ch == '"'))
+        {
+          const gchar *match = (ch == '\'') ? "'" : "\"";
+
+        again:
+          if (!gtk_text_iter_forward_search (&iter, match,
+                                             GTK_TEXT_SEARCH_TEXT_ONLY,
+                                             NULL, NULL, NULL))
+            return FALSE;
+
+          /* this one is escaped, keep looking */
+          if (text_iter_peek_prev_char (&iter) == '\\')
+            {
+              if (!gtk_text_iter_forward_char (&iter))
+                return FALSE;
+              goto again;
+            }
+        }
+
+      /* skip past escaped character */
+      if (ch == '\\')
+        {
+          if (!gtk_text_iter_forward_char (&iter))
+            return FALSE;
+        }
+    }
+  while (gtk_text_iter_forward_char (&iter));
+
+  return FALSE;
+}
+
+static gchar *
+c_indenter_indent (IdeCIndenter *c,
+                                  GtkTextView           *view,
+                                  GtkTextBuffer         *buffer,
+                                  GtkTextIter           *iter)
+{
+  GtkTextIter cur;
+  GtkTextIter match_begin;
+  gunichar ch;
+  GString *str;
+  gchar *ret = NULL;
+  gchar *last_word = NULL;
+
+  ENTRY;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+
+  /*
+   * Save our current iter position to restore it later.
+   */
+  gtk_text_iter_assign (&cur, iter);
+
+  /*
+   * Move to before the character just inserted.
+   */
+  gtk_text_iter_backward_char (iter);
+
+  /*
+   * Create the buffer for our indentation string.
+   */
+  str = g_string_new (NULL);
+
+  /*
+   * Move backwards to the last non-space character inserted. We need to
+   * start by moving back one character to get to the pre-newline insertion
+   * point.
+   */
+  if (g_unichar_isspace (gtk_text_iter_get_char (iter)))
+    if (!gtk_text_iter_backward_find_char (iter, non_space_predicate, NULL, NULL))
+      GOTO (cleanup);
+
+  /*
+   * Get our last non \n character entered.
+   */
+  ch = gtk_text_iter_get_char (iter);
+
+  /*
+   * If we are in a c89 multi-line comment, try to match the previous comment
+   * line. Function will leave iter at original position unless it matched.
+   * If so, it will be at the beginning of the comment.
+   */
+  if (in_c89_comment (iter, &match_begin))
+    {
+      guint offset;
+
+      gtk_text_iter_assign (iter, &match_begin);
+      offset = gtk_text_iter_get_line_offset (iter);
+      build_indent (c, offset + 1, iter, str);
+      g_string_append (str, "* ");
+      GOTO (cleanup);
+    }
+
+  /*
+   * If the next thing looking backwards is a complete c89 comment, let's
+   * move the iter to before the comment so that we can work with the syntax
+   * that is before it.
+   */
+  if (backward_before_c89_comment (iter))
+    gtk_text_iter_assign (&cur, iter);
+
+  /*
+   * Get our new character as we possibely moved.
+   */
+  ch = gtk_text_iter_get_char (iter);
+
+  /*
+   * We could be:
+   *   - In a parameter list for a function declaration.
+   *   - In an argument list for a function call.
+   *   - Defining enum fields.
+   *   - XXX: bunch more.
+   */
+  if (ch == ',')
+    {
+      guint offset;
+
+      if (!backward_find_matching_char (iter, ')') &&
+          !backward_find_matching_char (iter, '}'))
+        GOTO (cleanup);
+
+      offset = gtk_text_iter_get_line_offset (iter);
+
+      if (gtk_text_iter_get_char (iter) == '(')
+        offset++;
+      else if (gtk_text_iter_get_char (iter) == '{')
+        {
+          /*
+           * Handle the case where { is not the first character,
+           * like "enum {".
+           */
+          if (backward_to_line_first_char (iter))
+            offset = gtk_text_iter_get_line_offset (iter);
+          offset += c->scope_indent;
+        }
+
+      build_indent (c, offset, iter, str);
+      GOTO (cleanup);
+    }
+
+  /*
+   * Looks like the last line was a statement or expression. Let's try to
+   * find the beginning of it.
+   */
+  if (ch == ';')
+    {
+      guint offset;
+
+      if (backward_find_stmt_expr (iter))
+        {
+          offset = gtk_text_iter_get_line_offset (iter);
+          build_indent (c, offset, iter, str);
+          GOTO (cleanup);
+        }
+    }
+
+  /*
+   * Maybe we are in a conditional.
+   *
+   * TODO: This technically isn't right since it is perfectly reasonable to
+   * end a line on a ) but not be done with the entire conditional.
+   */
+  if ((ch != ')') && backward_find_matching_char (iter, ')'))
+    {
+      guint offset;
+
+      offset = gtk_text_iter_get_line_offset (iter);
+      build_indent (c, offset + 1, iter, str);
+      GOTO (cleanup);
+    }
+
+  /*
+   * If we just ended a scope, we need to look for the matching scope
+   * before it.
+   */
+  if (ch == '}')
+    {
+      GtkTextIter copy;
+
+      gtk_text_iter_assign (&copy, iter);
+
+      if (gtk_text_iter_forward_char (iter))
+        {
+          guint offset = gtk_text_iter_get_line_offset (iter) - 1;
+
+          if (backward_find_matching_char (iter, '}'))
+            {
+              offset = gtk_text_iter_get_line_offset (iter);
+              offset += c->scope_indent;
+            }
+
+          build_indent (c, offset, iter, str);
+          GOTO (cleanup);
+        }
+
+      gtk_text_iter_assign (iter, &copy);
+    }
+
+  /*
+   * Check to see if we just finished a conditional.
+   */
+  if (ch == ')')
+    {
+      GtkTextIter copy;
+
+      gtk_text_iter_assign (&copy, iter);
+
+      if (backward_find_matching_char (iter, ')') &&
+          backward_find_condition_keyword (iter))
+        {
+          guint offset = gtk_text_iter_get_line_offset (iter);
+          build_indent (c, offset + c->condition_indent, iter, str);
+          GOTO (cleanup);
+        }
+
+      gtk_text_iter_assign (iter, &copy);
+    }
+
+  /*
+   * Check to see if we are after else or do. Skip if we see '{'
+   * so that we can fallback to regular scoping rules.
+   */
+  last_word = backward_last_word (iter, &match_begin);
+  if ((ch != '{') &&
+      ((g_strcmp0 (last_word, "else") == 0) ||
+       (g_strcmp0 (last_word, "do") == 0)))
+    {
+      guint offset;
+
+      if (!line_is_whitespace_until (&match_begin))
+        backward_to_line_first_char (&match_begin);
+
+      offset = gtk_text_iter_get_line_offset (&match_begin);
+      build_indent (c, offset + c->scope_indent, iter, str);
+      GOTO (cleanup);
+    }
+
+  /*
+   * Work our way back to the most recent scope. Then apply our scope
+   * indentation to that.
+   */
+  if (ch == '{' || backward_find_matching_char (iter, '}'))
+    {
+      if (line_is_space (iter))
+        {
+          guint offset;
+
+          offset = gtk_text_iter_get_line_offset (iter);
+          build_indent (c, offset + c->scope_indent, iter, str);
+          GOTO (cleanup);
+        }
+      else
+        {
+          if (backward_to_line_first_char (iter))
+            {
+              guint offset;
+
+              offset = gtk_text_iter_get_line_offset (iter);
+              build_indent (c, offset + c->scope_indent, iter, str);
+              GOTO (cleanup);
+            }
+        }
+    }
+
+cleanup:
+  gtk_text_iter_assign (iter, &cur);
+  g_free (last_word);
+
+  ret = g_string_free (str, FALSE);
+
+  RETURN (ret);
+}
+
+static gchar *
+maybe_close_comment (IdeCIndenter *c,
+                     GtkTextIter           *begin,
+                     GtkTextIter           *end)
+{
+  GtkTextIter copy;
+  GtkTextIter begin_comment;
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+
+  gtk_text_iter_assign (&copy, begin);
+
+  /*
+   * Walk backwards ensuring we just inserted a '/' and that it was after
+   * a '* ' sequence.
+   */
+  if (in_c89_comment (begin, &begin_comment) &&
+      gtk_text_iter_backward_char (begin) &&
+      ('/' == gtk_text_iter_get_char (begin)) &&
+      gtk_text_iter_backward_char (begin) &&
+      (' ' == gtk_text_iter_get_char (begin)) &&
+      gtk_text_iter_backward_char (begin) &&
+      ('*' == gtk_text_iter_get_char (begin)))
+    ret = g_strdup ("*/");
+  else
+    gtk_text_iter_assign (begin, &copy);
+
+  return ret;
+}
+
+static gchar *
+maybe_unindent_brace (IdeCIndenter *c,
+                      GtkTextIter           *begin,
+                      GtkTextIter           *end)
+{
+  GtkTextIter saved;
+  gchar *ret = NULL;
+
+  ENTRY;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+
+  gtk_text_iter_assign (&saved, begin);
+
+  if (gtk_text_iter_backward_char (begin) &&
+      gtk_text_iter_backward_char (end) &&
+      backward_find_matching_char (begin, '}') &&
+      line_is_whitespace_until (end) &&
+      ((gtk_text_iter_get_offset (begin) + 1) !=
+        gtk_text_iter_get_offset (end)))
+    {
+      GString *str;
+      guint offset;
+
+      /*
+       * Handle the case where { is not the first non-whitespace
+       * character on the line.
+       */
+      if (!starts_line_space_ok (begin))
+        backward_to_line_first_char (begin);
+
+      offset = gtk_text_iter_get_line_offset (begin);
+      str = g_string_new (NULL);
+      build_indent (c, offset, begin, str);
+      g_string_append_c (str, '}');
+
+      gtk_text_iter_assign (begin, &saved);
+      while (!gtk_text_iter_starts_line (begin))
+        gtk_text_iter_backward_char (begin);
+
+      gtk_text_iter_assign (end, &saved);
+
+      ret = g_string_free (str, FALSE);
+    }
+
+  if (!ret)
+    {
+      gtk_text_iter_assign (begin, &saved);
+      gtk_text_iter_assign (end, &saved);
+    }
+
+  RETURN (ret);
+}
+
+static gchar *
+maybe_unindent_hash (IdeCIndenter *c,
+                     GtkTextIter           *begin,
+                     GtkTextIter           *end)
+{
+  GtkTextIter saved;
+  gchar *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+
+  gtk_text_iter_assign (&saved, begin);
+
+  if (gtk_text_iter_backward_char (begin) &&
+      ('#' == gtk_text_iter_get_char (begin)) &&
+      line_is_whitespace_until (begin))
+    {
+      if (c->directive_indent == G_MININT)
+        {
+          while (!gtk_text_iter_starts_line (begin))
+            gtk_text_iter_backward_char (begin);
+          ret = g_strdup ("#");
+        }
+      else
+        {
+          /* TODO: Handle indent when not fully unindenting. */
+        }
+    }
+
+  if (!ret)
+    gtk_text_iter_assign (begin, &saved);
+
+  return ret;
+}
+
+static gboolean
+line_starts_with_fuzzy (const GtkTextIter *iter,
+                        const gchar       *prefix)
+{
+  GtkTextIter begin;
+  GtkTextIter end;
+  gboolean ret;
+  gchar *line;
+
+  ITER_INIT_LINE_START (&begin, iter);
+  ITER_INIT_LINE_START (&end, iter);
+
+  while (!gtk_text_iter_ends_line (&end))
+    if (!gtk_text_iter_forward_char (&end))
+      return FALSE;
+
+  line = g_strstrip (gtk_text_iter_get_slice (&begin, &end));
+  ret = g_str_has_prefix (line, prefix);
+  g_free (line);
+
+  return ret;
+}
+
+#if 0
+static gchar *
+maybe_space_before_paren (IdeCIndenter *c,
+                          GtkTextIter           *begin,
+                          GtkTextIter           *end)
+{
+  GtkTextIter match_begin;
+  GtkTextIter copy;
+  gunichar ch;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+
+  if (!c->priv->space_before_paren)
+    return NULL;
+
+  if (in_c89_comment (begin, &match_begin))
+    return NULL;
+
+  /* ignore preprocessor #define */
+  if (line_starts_with_fuzzy (begin, "#"))
+    return NULL;
+
+  gtk_text_iter_assign (&copy, begin);
+
+  /*
+   * Move back to the character just inserted.
+   */
+  if (gtk_text_iter_backward_char (begin) &&
+      (ch = gtk_text_iter_get_char (begin)) &&
+      (ch == '(') &&
+      gtk_text_iter_backward_char (begin) &&
+      (ch = gtk_text_iter_get_char (begin)) &&
+      !g_unichar_isspace (ch) &&
+      g_unichar_isalnum (ch))
+    {
+      gtk_text_iter_forward_char (begin);
+      return g_strdup (" (");
+    }
+
+  gtk_text_iter_assign (begin, &copy);
+
+  return NULL;
+}
+#endif
+
+static gchar *
+format_parameter (const Parameter *param,
+                  guint            max_type,
+                  guint            max_star)
+{
+  GString *str;
+  guint i;
+
+  if (param->ellipsis)
+    return g_strdup ("...");
+
+  str = g_string_new (param->type);
+
+  for (i = str->len; i < max_type; i++)
+    g_string_append_c (str, ' ');
+
+  g_string_append_c (str, ' ');
+
+  for (i = max_star; i > 0; i--)
+    {
+      if (i <= param->n_star)
+        g_string_append_c (str, '*');
+      else
+        g_string_append_c (str, ' ');
+    }
+
+  g_string_append (str, param->name);
+
+  return g_string_free (str, FALSE);
+}
+
+static gchar *
+format_parameters (GtkTextIter *begin,
+                   GSList      *params)
+{
+  GtkTextIter line_start;
+  GtkTextIter first_char;
+  GString *str;
+  GSList *iter;
+  gchar *slice;
+  gchar *join_str;
+  guint max_star = 0;
+  guint max_type = 0;
+
+  for (iter = params; iter; iter = iter->next)
+    {
+      Parameter *p = iter->data;
+
+      if (p->n_star)
+        max_star = MAX (max_star, p->n_star);
+      if (p->type)
+        max_type = MAX (max_type, strlen (p->type));
+    }
+
+  str = g_string_new (NULL);
+
+  ITER_INIT_LINE_START (&line_start, begin);
+
+  gtk_text_iter_assign (&first_char, begin);
+  backward_to_line_first_char (&first_char);
+
+  slice = gtk_text_iter_get_slice (&line_start, &first_char);
+  str = g_string_new (",\n");
+  g_string_append (str, slice);
+  g_free (slice);
+
+  while (gtk_text_iter_compare (&first_char, begin) < 0)
+    {
+      g_string_append (str, " ");
+      if (!gtk_text_iter_forward_char (&first_char))
+        break;
+    }
+
+  join_str = g_string_free (str, FALSE);
+  str = g_string_new (NULL);
+
+  for (iter = params; iter; iter = iter->next)
+    {
+      gchar *param_str;
+
+      if (iter != params)
+        g_string_append (str, join_str);
+
+      param_str = format_parameter (iter->data, max_type, max_star);
+      g_string_append (str, param_str);
+    }
+
+  g_free (join_str);
+
+  return g_string_free (str, FALSE);
+}
+
+static gchar *
+maybe_align_parameters (IdeCIndenter *c,
+                        GtkTextIter           *begin,
+                        GtkTextIter           *end)
 {
-  IdeCIndenter *self = IDE_C_INDENTER (object);
+  GtkTextIter match_begin;
+  GtkTextIter copy;
+  GSList *params = NULL;
+  gchar *ret = NULL;
+  gchar *text = NULL;
 
-  switch (prop_id)
+  ENTRY;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+  g_return_val_if_fail (begin, NULL);
+  g_return_val_if_fail (end, NULL);
+
+  if (in_c89_comment (begin, &match_begin))
+    RETURN (NULL);
+
+  gtk_text_iter_assign (&copy, begin);
+
+  if (gtk_text_iter_backward_char (begin) &&
+      backward_find_matching_char (begin, ')') &&
+      gtk_text_iter_forward_char (begin) &&
+      gtk_text_iter_backward_char (end) &&
+      (gtk_text_iter_compare (begin, end) < 0) &&
+      (text = gtk_text_iter_get_slice (begin, end)) &&
+      (params = parse_parameters (text)) &&
+      (params->next != NULL))
+    ret = format_parameters (begin, params);
+
+  g_slist_foreach (params, (GFunc)parameter_free, NULL);
+  g_slist_free (params);
+
+  if (!ret)
+    {
+      gtk_text_iter_assign (begin, &copy);
+      gtk_text_iter_assign (end, &copy);
+    }
+
+  g_free (text);
+
+  RETURN (ret);
+}
+
+#if 0
+static gchar *
+maybe_add_brace (IdeCIndenter *c,
+                 GtkTextIter           *begin,
+                 GtkTextIter           *end,
+                 gint                  *cursor_offset)
+{
+  GtkTextIter iter;
+
+  gtk_text_iter_assign (&iter, begin);
+
+  if (gtk_text_iter_backward_char (&iter) &&
+      (gtk_text_iter_get_char (&iter) == '{') &&
+      (gtk_text_iter_get_char (begin) != '}'))
+    {
+      GtkTextIter copy = iter;
+
+      gtk_text_iter_assign (&copy, &iter);
+
+      if (gtk_text_iter_backward_word_start (&copy))
+        {
+          GtkTextIter copy2 = copy;
+
+          if (gtk_text_iter_forward_word_end (&copy2))
+            {
+              gchar *word;
+
+              word = gtk_text_iter_get_slice (&copy, &copy2);
+
+              if ((g_strcmp0 (word, "enum") == 0) ||
+                  (g_strcmp0 (word, "struct") == 0))
+                {
+                  *cursor_offset = -2;
+                  g_free (word);
+                  return g_strdup ("};");
+                }
+
+              g_free (word);
+            }
+        }
+
+      *cursor_offset = -1;
+      return g_strdup ("}");
+    }
+
+  return NULL;
+}
+#endif
+
+static gboolean
+line_is_case (const GtkTextIter *line)
+{
+  return (line_starts_with_fuzzy (line, "case ") ||
+          line_starts_with_fuzzy (line, "default:"));
+}
+
+static gboolean
+str_maybe_label (const gchar *str)
+{
+  const gchar *ptr = str;
+
+  if (g_strcmp0 (str, "default:") == 0)
+    return FALSE;
+
+  for (; *ptr; ptr = g_utf8_next_char (ptr))
+    {
+      gunichar ch = g_utf8_get_char (ptr);
+
+      switch (ch)
+        {
+        case ':':
+        case '_':
+          break;
+        default:
+          if (!g_unichar_isalnum (ch))
+            return FALSE;
+        }
+    }
+
+  return (*str != '\0');
+}
+
+static gboolean
+line_is_label (const GtkTextIter *line)
+{
+  GtkTextIter begin;
+  GtkTextIter end;
+  gchar *text;
+  gchar **parts;
+  guint i;
+  guint count = 0;
+
+  gtk_text_iter_assign (&begin, line);
+  while (!gtk_text_iter_starts_line (&begin))
+    if (!gtk_text_iter_backward_char (&begin))
+      return FALSE;
+
+  gtk_text_iter_assign (&end, line);
+  while (!gtk_text_iter_ends_line (&end))
+    if (!gtk_text_iter_forward_char (&end))
+      return FALSE;
+
+  text = gtk_text_iter_get_slice (&begin, &end);
+  g_strdelimit (text, "\t", ' ');
+  parts = g_strsplit (text, " ", 0);
+
+  for (i = 0; parts [i]; i++)
+    {
+      g_strstrip (parts [i]);
+      if (*parts [i])
+        count++;
+    }
+
+  if (count > 1)
+    return FALSE;
+
+  count = 0;
+
+  for (i = 0; parts [i]; i++)
+    {
+      g_strstrip (parts [i]);
+      if (str_maybe_label (parts [i]))
+        count++;
+    }
+
+  g_free (text);
+  g_strfreev (parts);
+
+  return (count == 1);
+}
+
+static gchar *
+maybe_unindent_case_label (IdeCIndenter *c,
+                           GtkTextIter           *begin,
+                           GtkTextIter           *end)
+{
+  GtkTextIter match_begin;
+  GtkTextIter iter;
+
+  ENTRY;
+
+  gtk_text_iter_assign (&iter, begin);
+
+  if (in_c89_comment (begin, &match_begin))
+    RETURN (NULL);
+
+  if (!gtk_text_iter_backward_char (&iter))
+    RETURN (NULL);
+
+  if (line_is_case (&iter))
+    {
+      if (backward_find_matching_char (&iter, '}'))
+        {
+          if (line_is_whitespace_until (&iter))
+            {
+              GString *str;
+              guint offset;
+
+              str = g_string_new (NULL);
+              offset = gtk_text_iter_get_line_offset (&iter);
+              build_indent (c, offset, &iter, str);
+              while (!gtk_text_iter_starts_line (begin))
+                gtk_text_iter_backward_char (begin);
+              gtk_text_iter_assign (end, begin);
+              while (g_unichar_isspace (gtk_text_iter_get_char (end)))
+                if (!gtk_text_iter_forward_char (end))
+                  RETURN (NULL);
+              return g_string_free (str, FALSE);
+            }
+          else
+            {
+              if (backward_to_line_first_char (&iter))
+                {
+#if 0
+                  TODO ("Deal with nested {");
+#endif
+                }
+            }
+        }
+    }
+  else if (line_is_label (&iter))
     {
+#if 0
+      TODO ("allow configurable label indent");
+#endif
+
+      ITER_INIT_LINE_START (begin, &iter);
+      ITER_INIT_LINE_START (end, &iter);
+
+      while (g_unichar_isspace (gtk_text_iter_get_char (end)))
+        if (!gtk_text_iter_forward_char (end))
+          return NULL;
+
+      return g_strdup ("");
+    }
+
+  RETURN (NULL);
+}
+
+static gboolean
+ide_c_indenter_is_trigger (IdeIndenter *indenter,
+                           GdkEventKey *event)
+{
+  switch (event->keyval)
+    {
+    case GDK_KEY_KP_Enter:
+    case GDK_KEY_Return:
+      if ((event->state & GDK_SHIFT_MASK) != 0)
+        return FALSE;
+      /* Fall through */
+
+    case GDK_KEY_braceright:
+    case GDK_KEY_colon:
+    case GDK_KEY_numbersign:
+    case GDK_KEY_parenright:
+    case GDK_KEY_slash:
+      return TRUE;
+
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      return FALSE;
     }
 }
 
+static gchar *
+ide_c_indenter_format (IdeIndenter    *indenter,
+                       GtkTextView    *view,
+                       GtkTextIter    *begin,
+                       GtkTextIter    *end,
+                       gint           *cursor_offset,
+                       GdkEventKey    *event)
+{
+  IdeCIndenter *c = (IdeCIndenter *)indenter;
+  GtkTextIter begin_copy;
+  gchar *ret = NULL;
+  GtkTextBuffer *buffer;
+
+  g_return_val_if_fail (IDE_IS_C_INDENTER (c), NULL);
+
+  buffer = gtk_text_view_get_buffer (view);
+
+  switch (event->keyval) {
+  case GDK_KEY_Return:
+  case GDK_KEY_KP_Enter:
+    gtk_text_iter_assign (&begin_copy, begin);
+    ret = c_indenter_indent (c, view, buffer, begin);
+    gtk_text_iter_assign (begin, &begin_copy);
+
+    /*
+     * If we are inserting a newline right before a closing brace (for example
+     * after {<cursor>}, we need to indent and then maybe unindent the }.
+     */
+    if (gtk_text_iter_get_char (begin) == '}')
+      {
+        GtkTextIter iter;
+        GString *str;
+        gchar *tmp = ret;
+        guint offset = 0;
+
+        str = g_string_new (NULL);
+
+        gtk_text_iter_assign (&iter, begin);
+        if (backward_find_matching_char (&iter, '}'))
+          {
+            if (line_is_whitespace_until (&iter))
+              offset = gtk_text_iter_get_line_offset (&iter);
+            else if (backward_to_line_first_char (&iter))
+              offset = gtk_text_iter_get_line_offset (&iter);
+            build_indent (c, offset, &iter, str);
+            g_string_prepend (str, "\n");
+            g_string_prepend (str, ret);
+
+            *cursor_offset = -(str->len - strlen (ret));
+
+            ret = g_string_free (str, FALSE);
+            g_free (tmp);
+          }
+      }
+
+    break;
+
+  case GDK_KEY_braceright:
+    /*
+     * Probably need to unindent this line.
+     * TODO: Maybe overwrite character.
+     */
+    ret = maybe_unindent_brace (c, begin, end);
+    break;
+
+  case GDK_KEY_colon:
+    /*
+     * If this is a label or a case, adjust indentation.
+     */
+    ret = maybe_unindent_case_label (c, begin, end);
+    break;
+
+  case GDK_KEY_numbersign:
+    /*
+     * If this is a preprocessor directive, adjust indentation.
+     */
+    ret = maybe_unindent_hash (c, begin, end);
+    break;
+
+  case GDK_KEY_parenright:
+    /*
+     * If we are closing a function declaration, adjust the spacing of
+     * parameters so that *'s are aligned.
+     */
+    ret = maybe_align_parameters (c, begin, end);
+    break;
+
+  case GDK_KEY_slash:
+    /*
+     * Check to see if we are right after a "* " and typing "/" while inside
+     * of a multi-line comment. Probably just want to close the comment.
+     */
+    ret = maybe_close_comment (c, begin, end);
+    break;
+
+  default:
+    break;
+  }
+
+  return ret;
+}
+
 static void
 ide_c_indenter_class_init (IdeCIndenterClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeIndenterClass *indenter_class = IDE_INDENTER_CLASS (klass);
 
-  object_class->finalize = ide_c_indenter_finalize;
-  object_class->get_property = ide_c_indenter_get_property;
-  object_class->set_property = ide_c_indenter_set_property;
+  indenter_class->is_trigger = ide_c_indenter_is_trigger;
+  indenter_class->format = ide_c_indenter_format;
 }
 
 static void
 ide_c_indenter_init (IdeCIndenter *self)
 {
+  self->condition_indent = 2;
+  self->scope_indent = 2;
+  self->directive_indent = G_MININT;
+  self->space_before_paren = TRUE;
 }
diff --git a/libide/c/ide-c-indenter.h b/libide/c/ide-c-indenter.h
index a7e8ce8..ec9938a 100644
--- a/libide/c/ide-c-indenter.h
+++ b/libide/c/ide-c-indenter.h
@@ -25,12 +25,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_C_INDENTER (ide_c_indenter_get_type())
 
-G_DECLARE_DERIVABLE_TYPE (IdeCIndenter, ide_c_indenter, IDE, C_INDENTER, IdeIndenter)
-
-struct _IdeCIndenterClass
-{
-  IdeIndenterClass parent;
-};
+G_DECLARE_FINAL_TYPE (IdeCIndenter, ide_c_indenter, IDE, C_INDENTER, IdeIndenter)
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]