[gnome-builder/wip/slaf/xml-pack: 11/11] xml-pack: get completion items



commit abd420517d513c23251b56402419a10c5e09527c
Author: Sebastien Lafargue <slafargue gnome org>
Date:   Tue Jun 27 23:50:09 2017 +0200

    xml-pack: get completion items

 plugins/xml-pack/ide-xml-completion-provider.c |  623 ++++++++++++++++++++++--
 plugins/xml-pack/ide-xml-parser-generic.c      |    2 +-
 plugins/xml-pack/ide-xml-path.c                |    2 +-
 plugins/xml-pack/ide-xml-service.c             |   89 +++-
 plugins/xml-pack/ide-xml-symbol-node.c         |    1 -
 5 files changed, 663 insertions(+), 54 deletions(-)
---
diff --git a/plugins/xml-pack/ide-xml-completion-provider.c b/plugins/xml-pack/ide-xml-completion-provider.c
index 59d96aa..7356688 100644
--- a/plugins/xml-pack/ide-xml-completion-provider.c
+++ b/plugins/xml-pack/ide-xml-completion-provider.c
@@ -33,7 +33,48 @@ struct _IdeXmlCompletionProvider
   IdeObject parent_instance;
 };
 
-static void completion_provider_init (GtkSourceCompletionProviderIface *);
+typedef struct _MatchingState
+{
+  GArray           *stack;
+  IdeXmlSymbolNode *parent_node;
+  IdeXmlSymbolNode *candidate_node;
+  IdeXmlPosition   *position;
+  IdeXmlRngDefine  *define;
+  GPtrArray        *children;
+  GPtrArray        *items;
+  const gchar      *prefix;
+  gint              child_cursor;
+  gint              define_cursor;
+
+  guint             is_initial_state : 1;
+  guint             retry : 1;
+} MatchingState;
+
+typedef struct _StateStackItem
+{
+  GPtrArray        *children;
+  IdeXmlSymbolNode *candidate_node;
+} StateStackItem;
+
+typedef struct
+{
+  IdeXmlCompletionProvider    *self;
+  GtkSourceCompletionContext  *completion_context;
+  IdeFile                     *ifile;
+  IdeBuffer                   *buffer;
+  gint                         line;
+  gint                         line_offset;
+} PopulateState;
+
+typedef struct _CompletionItem
+{
+  gchar *label;
+  gchar *content;
+} CompletionItem;
+
+static void      completion_provider_init (GtkSourceCompletionProviderIface *);
+static gboolean  process_matching_state   (MatchingState                    *state,
+                                           IdeXmlRngDefine                  *define);
 
 G_DEFINE_DYNAMIC_TYPE_EXTENDED (IdeXmlCompletionProvider,
                                 ide_xml_completion_provider,
@@ -49,16 +90,6 @@ enum {
 
 static GParamSpec *properties [N_PROPS];
 
-typedef struct
-{
-  IdeXmlCompletionProvider    *self;
-  GtkSourceCompletionContext  *completion_context;
-  IdeFile                     *ifile;
-  IdeBuffer                   *buffer;
-  gint                         line;
-  gint                         line_offset;
-} PopulateState;
-
 static void
 populate_state_free (PopulateState *state)
 {
@@ -70,6 +101,112 @@ populate_state_free (PopulateState *state)
   g_object_unref (state->buffer);
 }
 
+static GPtrArray *
+copy_children (GPtrArray *children)
+{
+  GPtrArray *copy;
+
+  g_assert (children != NULL);
+
+  copy = g_ptr_array_new ();
+  for (gint i = 0; i < children->len; ++i)
+    g_ptr_array_add (copy, g_ptr_array_index (children, i));
+
+  return copy;
+}
+
+static void
+state_stack_item_free (gpointer *data)
+{
+  StateStackItem *item;
+
+  g_assert (data != NULL);
+
+  item = (StateStackItem *)data;
+  g_ptr_array_unref (item->children);
+}
+
+static GArray *
+state_stack_new (void)
+{
+  GArray *stack;
+
+  stack = g_array_new (FALSE, TRUE, sizeof (StateStackItem));
+  g_array_set_clear_func (stack, (GDestroyNotify)state_stack_item_free);
+
+  return stack;
+}
+
+static void
+state_stack_push (MatchingState *state)
+{
+  StateStackItem item;
+
+  g_assert (state->stack != NULL);
+
+  item.children = copy_children (state->children);
+  item.candidate_node = state->candidate_node;
+
+  g_array_append_val (state->stack, item);
+}
+
+static gboolean
+state_stack_pop (MatchingState *state)
+{
+  StateStackItem *item;
+  guint len;
+
+  g_assert (state->stack != NULL);
+
+  len = state->stack->len;
+  if (len == 0)
+    return FALSE;
+
+  item = &g_array_index (state->stack, StateStackItem, len - 1);
+  g_clear_pointer (&state->children, g_ptr_array_unref);
+
+  state->children = item->children;
+  state->candidate_node = item->candidate_node;
+
+  g_array_remove_index (state->stack, len - 1);
+  return TRUE;
+}
+
+static gboolean
+state_stack_drop (MatchingState *state)
+{
+  guint len;
+
+  g_assert (state->stack != NULL);
+
+  len = state->stack->len;
+  if (len == 0)
+    return FALSE;
+
+  g_array_remove_index (state->stack, len - 1);
+  return TRUE;
+}
+
+static gboolean
+state_stack_copy (MatchingState *state)
+{
+  StateStackItem *item;
+  guint len;
+
+  g_assert (state->stack != NULL);
+
+  len = state->stack->len;
+  if (len == 0)
+    return FALSE;
+
+  item = &g_array_index (state->stack, StateStackItem, len - 1);
+  state->children = copy_children (item->children);
+  state->candidate_node = item->candidate_node;
+
+  g_array_remove_index (state->stack, len - 1);
+  return TRUE;
+}
+
 static IdeXmlPath *
 get_path (IdeXmlSymbolNode *node,
           IdeXmlSymbolNode *root_node)
@@ -226,6 +363,402 @@ get_matching_candidates (IdeXmlCompletionProvider *self,
   return candidates;
 }
 
+static CompletionItem *
+completion_item_new (const gchar *label,
+                     const gchar *content)
+{
+  CompletionItem *item;
+
+  g_assert (!ide_str_empty0 (label));
+  g_assert (!ide_str_empty0 (content));
+
+  item = g_slice_new0 (CompletionItem);
+
+  item->label = g_strdup (label);
+  item->content = g_strdup (content);
+
+  return item;
+}
+
+static void
+completion_item_free (CompletionItem *item)
+{
+  g_clear_pointer (&item->label, g_free);
+  g_clear_pointer (&item->content, g_free);
+}
+
+static MatchingState *
+matching_state_new (IdeXmlPosition  *position,
+                    IdeXmlRngDefine *define,
+                    GPtrArray       *items)
+{
+  MatchingState *state;
+  const gchar *prefix;
+
+  g_assert (IDE_IS_XML_SYMBOL_NODE (position->node));
+  g_assert (define != NULL);
+
+  state = g_slice_new0 (MatchingState);
+
+  state->parent_node = NULL;
+  state->position = position;
+  state->define = define;
+  state->items = items;
+  state->candidate_node = ide_xml_position_get_child_node (position);
+
+  state->children = g_ptr_array_new ();
+  state->stack = state_stack_new ();
+
+  prefix = ide_xml_position_get_prefix (position);
+  state->prefix = (prefix != NULL) ? g_strdup (prefix) : NULL;
+
+  return state;
+}
+
+static MatchingState *
+matching_state_copy (MatchingState *state)
+{
+  MatchingState *new_state;
+
+  new_state = g_slice_new0 (MatchingState);
+
+  new_state->parent_node = (state->parent_node != NULL) ? g_object_ref (state->parent_node) : NULL;
+  new_state->candidate_node = (state->candidate_node != NULL) ? g_object_ref (state->candidate_node) : NULL;
+  new_state->position = state->position;
+
+  new_state->define = state->define;
+  new_state->define_cursor = state->define_cursor;
+
+  new_state->child_cursor = state->child_cursor;
+
+  new_state->prefix = (state->prefix != NULL) ? g_strdup (state->prefix) : NULL;
+  new_state->items = state->items;
+
+  if (state->children != NULL)
+    {
+      new_state->children = g_ptr_array_new ();
+      for (gint i = 0; i < state->children->len; ++i)
+        g_ptr_array_add (new_state->children, g_ptr_array_index (state->children, i));
+    }
+
+  new_state->items = state->items;
+
+  return new_state;
+}
+
+static void
+matching_state_free (MatchingState *state)
+{
+  g_clear_object (&state->parent_node);
+  g_clear_object (&state->candidate_node);
+
+  g_clear_pointer (&state->prefix, g_free);
+  g_clear_pointer (&state->children, g_ptr_array_unref);
+  g_clear_pointer (&state->stack, g_array_unref);
+}
+
+static MatchingState *
+create_initial_matching_state (IdeXmlPosition  *position,
+                               IdeXmlRngDefine *define,
+                               GPtrArray       *items)
+{
+  MatchingState *state;
+  IdeXmlSymbolNode *node, *pos_node, *candidate_node;
+  guint nb_nodes;
+  gint child_pos;
+
+  g_assert (define != NULL);
+  g_assert (items != NULL);
+
+  state = matching_state_new (position, define, items);
+  child_pos = ide_xml_position_get_child_pos (position);
+
+  candidate_node = ide_xml_position_get_child_node (position);
+  pos_node = ide_xml_position_get_node (position);
+  g_assert (IDE_IS_XML_SYMBOL_NODE (pos_node));
+
+  nb_nodes = ide_xml_symbol_node_get_n_direct_children (pos_node);
+  for (gint i = 0; i < nb_nodes; ++i)
+    {
+      /* Inject a fake node at child_pos */
+      if (child_pos == i)
+        g_ptr_array_add (state->children, candidate_node);
+
+      node = (IdeXmlSymbolNode *)ide_xml_symbol_node_get_nth_direct_child (pos_node, i);
+      g_ptr_array_add (state->children, node);
+    }
+
+  state->candidate_node = g_object_ref (candidate_node);
+  state->is_initial_state = TRUE;
+  return state;
+}
+
+static gboolean
+is_define_equal_node (IdeXmlRngDefine  *define,
+                      IdeXmlSymbolNode *node)
+{
+  g_assert (define != NULL);
+  g_assert (IDE_IS_XML_SYMBOL_NODE (node));
+
+  return ide_str_equal0 (ide_xml_symbol_node_get_element_name (node), define->name);
+}
+
+static gboolean
+is_element_matching (MatchingState *state)
+{
+  IdeXmlSymbolNode *node;
+  CompletionItem *item;
+  const gchar *name;
+
+  g_assert (state->define->type == IDE_XML_RNG_DEFINE_ELEMENT);
+
+  /* XXX: we skip element without a name for now */
+  if (ide_str_empty0 ((gchar *)state->define->name))
+    return FALSE;
+
+  if (state->children->len == 0)
+    return FALSE;
+
+  state->retry = FALSE;
+  node = g_ptr_array_index (state->children, 0);
+
+  if (state->candidate_node == node)
+    {
+      name = (gchar *)state->define->name;
+      if (ide_str_empty0 (state->prefix) || g_str_has_prefix ((gchar *)state->define->name, state->prefix))
+        {
+          state->candidate_node = NULL;
+          state->retry = TRUE;
+
+          item = completion_item_new (name, name);
+          g_ptr_array_add (state->items, item);
+
+          return TRUE;
+        }
+      else
+        return FALSE;
+    }
+  else if (is_define_equal_node (state->define, node))
+    {
+      g_ptr_array_remove_index (state->children, 0);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+is_choice_matching (MatchingState *state)
+{
+  IdeXmlRngDefine *defines;
+
+  g_assert (state->define->type == IDE_XML_RNG_DEFINE_CHOICE);
+
+  if (NULL == (defines = state->define->content))
+    return TRUE;
+
+  state_stack_push (state);
+
+  while (defines != NULL)
+    {
+      if (process_matching_state (state, defines))
+        {
+          if (state->retry)
+            {
+              state->retry = FALSE;
+            }
+          else
+            {
+              state_stack_drop (state);
+              return TRUE;
+            }
+        }
+
+      if (NULL != (defines = defines->next))
+        {
+          state_stack_copy (state);
+        }
+      else
+        {
+          state_stack_pop (state);
+        }
+    }
+
+  state->retry = FALSE;
+  return FALSE;
+}
+
+static gboolean
+is_n_matching (MatchingState *state)
+{
+  IdeXmlRngDefine *defines;
+  IdeXmlRngDefineType type = state->define->type;
+  gboolean is_child_matching;
+  gboolean is_matching = FALSE;
+
+  g_assert (type == IDE_XML_RNG_DEFINE_ZEROORMORE ||
+            type == IDE_XML_RNG_DEFINE_ONEORMORE ||
+            type == IDE_XML_RNG_DEFINE_OPTIONAL);
+
+  /* Only ZeroOrMore or optionnal match if there's no children */
+  if (NULL == (defines = state->define->content))
+    return !(type == IDE_XML_RNG_DEFINE_ONEORMORE);
+
+  state_stack_push (state);
+
+loop:
+  is_child_matching = TRUE;
+  while (defines != NULL)
+    {
+      if (!process_matching_state (state, defines))
+        {
+          is_child_matching = FALSE;
+          break;
+        }
+
+      defines = defines->next;
+    }
+
+  if (is_child_matching)
+    {
+      is_matching = TRUE;
+      state_stack_drop (state);
+      if ((type == IDE_XML_RNG_DEFINE_ONEORMORE || type == IDE_XML_RNG_DEFINE_ZEROORMORE) &&
+          state->candidate_node != NULL)
+        {
+          state_stack_push (state);
+          goto loop;
+        }
+    }
+  else
+    {
+      state_stack_pop (state);
+    }
+
+  state->retry = FALSE;
+  if (type == IDE_XML_RNG_DEFINE_OPTIONAL || type == IDE_XML_RNG_DEFINE_ZEROORMORE)
+    return TRUE;
+
+  return is_matching;
+}
+
+static gboolean
+is_group_matching (MatchingState *state)
+{
+  IdeXmlRngDefine *defines;
+  gboolean is_matching = TRUE;
+
+  g_assert (state->define->type == IDE_XML_RNG_DEFINE_GROUP ||
+            state->define->type == IDE_XML_RNG_DEFINE_ELEMENT);
+
+  if (NULL == (defines = state->define->content))
+    return TRUE;
+
+  state_stack_push (state);
+  while (defines != NULL)
+    {
+      if (!process_matching_state (state, defines))
+        {
+          is_matching = FALSE;
+          break;
+        }
+
+      if (NULL != (defines = defines->next))
+        {
+          state_stack_drop (state);
+          state_stack_push (state);
+        }
+    }
+
+  state->retry = FALSE;
+  if (is_matching)
+    {
+      state_stack_drop (state);
+      return TRUE;
+    }
+
+  return is_matching;
+}
+
+static gboolean
+process_matching_state (MatchingState   *state,
+                        IdeXmlRngDefine *define)
+{
+  IdeXmlRngDefine *old_define;
+  IdeXmlRngDefineType type;
+  gboolean is_matching = FALSE;
+
+  g_assert (state != NULL);
+  g_assert (define != NULL);
+
+  if (state->candidate_node == NULL)
+    return TRUE;
+
+  old_define = state->define;
+  state->define = define;
+
+  if (state->is_initial_state)
+    {
+      state->is_initial_state = FALSE;
+      type = IDE_XML_RNG_DEFINE_GROUP;
+    }
+  else
+    type = define->type;
+
+  switch (type)
+    {
+    case IDE_XML_RNG_DEFINE_ELEMENT:
+      is_matching = is_element_matching (state);
+      break;
+
+    case IDE_XML_RNG_DEFINE_NOOP:
+    case IDE_XML_RNG_DEFINE_NOTALLOWED:
+    case IDE_XML_RNG_DEFINE_TEXT:
+    case IDE_XML_RNG_DEFINE_DATATYPE:
+    case IDE_XML_RNG_DEFINE_VALUE:
+    case IDE_XML_RNG_DEFINE_EMPTY:
+    case IDE_XML_RNG_DEFINE_ATTRIBUTE:
+    case IDE_XML_RNG_DEFINE_START:
+    case IDE_XML_RNG_DEFINE_PARAM:
+    case IDE_XML_RNG_DEFINE_EXCEPT:
+    case IDE_XML_RNG_DEFINE_LIST:
+      is_matching = FALSE;
+      break;
+
+    case IDE_XML_RNG_DEFINE_DEFINE:
+    case IDE_XML_RNG_DEFINE_REF:
+    case IDE_XML_RNG_DEFINE_PARENTREF:
+    case IDE_XML_RNG_DEFINE_EXTERNALREF:
+      is_matching = process_matching_state (state, define->content);
+      break;
+
+    case IDE_XML_RNG_DEFINE_ZEROORMORE:
+    case IDE_XML_RNG_DEFINE_ONEORMORE:
+    case IDE_XML_RNG_DEFINE_OPTIONAL:
+      is_matching = is_n_matching (state);
+      break;
+
+    case IDE_XML_RNG_DEFINE_CHOICE:
+      is_matching = is_choice_matching (state);
+      break;
+
+    case IDE_XML_RNG_DEFINE_INTERLEAVE:
+      is_matching = FALSE;
+      break;
+
+    case IDE_XML_RNG_DEFINE_GROUP:
+      is_matching = is_group_matching (state);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  state->define = old_define;
+
+  return is_matching;
+}
+
 static void
 populate_cb (GObject      *object,
              GAsyncResult *result,
@@ -235,17 +768,21 @@ populate_cb (GObject      *object,
   PopulateState *state = (PopulateState *)user_data;
   IdeXmlCompletionProvider *self = state->self;
   g_autoptr (IdeXmlPosition) position = NULL;
-  IdeXmlSymbolNode *root_node, *node;
+  IdeXmlSymbolNode *root_node, *node, *candidate_node;
   IdeXmlAnalysis *analysis;
   IdeXmlPositionKind kind;
+  IdeXmlPositionDetail detail;
   g_autoptr (IdeXmlPath) path = NULL;
   GtkSourceCompletionItem *item;
-  g_autofree gchar *text = NULL;
-  g_autofree gchar *label = NULL;
   g_autoptr (GList) results = NULL;
   GPtrArray *schemas;
   g_autoptr (GPtrArray) candidates = NULL;
+  IdeXmlRngDefine *def;
+  MatchingState *initial_state;
+  g_autoptr (GPtrArray) items = NULL;
+  CompletionItem *completion_item;
   GError *error = NULL;
+  gint child_pos;
 
   g_assert (IDE_IS_XML_COMPLETION_PROVIDER (self));
   g_assert (IDE_IS_XML_SERVICE (service));
@@ -256,29 +793,57 @@ populate_cb (GObject      *object,
   root_node = ide_xml_analysis_get_root_node (analysis);
   node = ide_xml_position_get_node (position);
   kind = ide_xml_position_get_kind (position);
+  detail = ide_xml_position_get_detail (position);
+  child_pos = ide_xml_position_get_child_pos (position);
+    }
 
   path = get_path (node, root_node);
 
-  if (schemas != NULL)
-    candidates = get_matching_candidates (self, schemas, path);
 
+  if (schemas == NULL)
+    goto cleanup;
+
+  candidates = get_matching_candidates (self, schemas, path);
+  if (candidates != NULL)
+    {
+
+      if (child_pos != -1)
+        {
+          candidate_node = ide_xml_symbol_node_new ("internal", NULL, "", IDE_SYMBOL_XML_ELEMENT);
+          ide_xml_position_set_child_node (position, candidate_node);
+        }
+
+      items = g_ptr_array_new_with_free_func ((GDestroyNotify)completion_item_free);
+      for (gint i = 0; i < candidates->len; ++i)
+        {
+          def = g_ptr_array_index (candidates, i);
+          ide_xml_rng_define_dump_tree (def, FALSE);
+
+          initial_state = create_initial_matching_state (position, def, items);
+          process_matching_state (initial_state, def);
+          matching_state_free (initial_state);
 
-  text = g_strdup ("xml item text");
-  label = g_strdup ("xml item label");
-  item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM,
-                       "text", text,
-                       "label", label,
-                       NULL);
+          printf ("----------\n");
+        }
 
-  ide_xml_position_print (position);
-  ide_xml_path_dump (path);
+      for (gint j = 0; j < items->len; ++j)
+        {
+          completion_item = g_ptr_array_index (items, j);
+          item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM,
+                               "text", completion_item->content,
+                               "label", completion_item->label,
+                               NULL);
 
-  results = g_list_prepend (results, item);
-  gtk_source_completion_context_add_proposals (state->completion_context,
-                                               GTK_SOURCE_COMPLETION_PROVIDER (self),
-                                               results,
-                                               TRUE);
+          results = g_list_prepend (results, item);
+        }
+
+      gtk_source_completion_context_add_proposals (state->completion_context,
+                                                   GTK_SOURCE_COMPLETION_PROVIDER (self),
+                                                   results,
+                                                   TRUE);
+    }
 
+cleanup:
   populate_state_free (state);
 }
 
diff --git a/plugins/xml-pack/ide-xml-parser-generic.c b/plugins/xml-pack/ide-xml-parser-generic.c
index 0d6d307..b2d75b9 100644
--- a/plugins/xml-pack/ide-xml-parser-generic.c
+++ b/plugins/xml-pack/ide-xml-parser-generic.c
@@ -67,7 +67,7 @@ ide_xml_parser_generic_start_element_sax_cb (ParserState    *state,
   attr = collect_attributes (self, (const gchar **)attributes);
   label = g_strconcat ((const gchar *)name, attr, NULL);
 
-  node = ide_xml_symbol_node_new (label, NULL, NULL, IDE_SYMBOL_XML_ELEMENT);
+  node = ide_xml_symbol_node_new (label, NULL, (gchar *)name, IDE_SYMBOL_XML_ELEMENT);
   g_object_set (node, "use-markup", TRUE, NULL);
 
   state->attributes = (const gchar **)attributes;
diff --git a/plugins/xml-pack/ide-xml-path.c b/plugins/xml-pack/ide-xml-path.c
index e220d48..63bb167 100644
--- a/plugins/xml-pack/ide-xml-path.c
+++ b/plugins/xml-pack/ide-xml-path.c
@@ -50,7 +50,7 @@ ide_xml_path_dump (IdeXmlPath *self)
   for (gint i = 0; i < self->nodes->len; ++i)
     {
       node = g_ptr_array_index (self->nodes, i);
-      ide_xml_symbol_node_print (node, TRUE, TRUE);
+      ide_xml_symbol_node_print (node, 0, FALSE, TRUE, TRUE);
     }
 }
 
diff --git a/plugins/xml-pack/ide-xml-service.c b/plugins/xml-pack/ide-xml-service.c
index 50b3def..631fc0f 100644
--- a/plugins/xml-pack/ide-xml-service.c
+++ b/plugins/xml-pack/ide-xml-service.c
@@ -629,24 +629,30 @@ position_state_free (PositionState *state)
 static IdeXmlPosition *
 get_position (IdeXmlService   *self,
               IdeXmlAnalysis  *analysis,
+              GtkTextBuffer   *buffer,
               gint             line,
               gint             line_offset)
 {
   IdeXmlPosition *position;
   IdeXmlSymbolNode *root_node;
-  IdeXmlSymbolNodeRelativePosition rel_pos;
-  IdeXmlPositionKind candidate_kind;
-  IdeXmlSymbolNode *current_node;
-  IdeXmlSymbolNode *child_node;
-  IdeXmlSymbolNode *candidate_node;
+  IdeXmlSymbolNode *current_node, *child_node, *candidate_node;
+  IdeXmlSymbolNode *previous_node = NULL;
   IdeXmlSymbolNode *previous_sibling_node = NULL;
   IdeXmlSymbolNode *next_sibling_node = NULL;
+  IdeXmlSymbolNodeRelativePosition rel_pos;
+  IdeXmlPositionKind candidate_kind;
+  IdeXmlPositionDetail detail = IDE_XML_POSITION_DETAIL_NONE;
+  g_autofree gchar *prefix = NULL;
+  GtkTextIter start, end;
+  gint start_line, start_line_offset;
   guint n_children;
   gint child_pos = -1;
   gint n = 0;
+  gboolean has_prefix = FALSE;
 
   g_assert (IDE_IS_XML_SERVICE (self));
   g_assert (analysis != NULL);
+  g_assert (GTK_IS_TEXT_BUFFER (buffer));
 
   current_node = root_node = ide_xml_analysis_get_root_node (analysis);
   while (TRUE)
@@ -658,6 +664,7 @@ loop:
       for (n = 0; n < n_children; ++n)
         {
           child_node = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_direct_child (current_node, n));
+          child_pos = n;
           rel_pos = ide_xml_symbol_node_compare_location (child_node, line, line_offset);
           printf ("node:%s rel pos:%d\n", ide_xml_symbol_node_get_element_name (child_node), rel_pos);
           switch (rel_pos)
@@ -665,34 +672,37 @@ loop:
             case IDE_XML_SYMBOL_NODE_RELATIVE_POSITION_IN_START_TAG:
               candidate_node = child_node;
               candidate_kind = IDE_XML_POSITION_KIND_IN_START_TAG;
+              detail = IDE_XML_POSITION_DETAIL_IN_NAME;
+              has_prefix = TRUE;
               goto result;
 
             case IDE_XML_SYMBOL_NODE_RELATIVE_POSITION_IN_END_TAG:
               candidate_node = child_node;
               candidate_kind = IDE_XML_POSITION_KIND_IN_END_TAG;
+              detail = IDE_XML_POSITION_DETAIL_IN_NAME;
+              has_prefix = TRUE;
               goto result;
 
             case IDE_XML_SYMBOL_NODE_RELATIVE_POSITION_BEFORE:
-              child_pos = n;
               candidate_node = current_node;
               candidate_kind = IDE_XML_POSITION_KIND_IN_CONTENT;
               goto result;
 
             case IDE_XML_SYMBOL_NODE_RELATIVE_POSITION_AFTER:
+              previous_node = child_node;
+
               if (n == (n_children - 1))
                 {
-                  child_pos = (n_children + 1);
+                  child_pos = n_children;
                   candidate_node = current_node;
                   candidate_kind = IDE_XML_POSITION_KIND_IN_CONTENT;
                   goto result;
                 }
-              else
-                continue;
 
               break;
 
             case IDE_XML_SYMBOL_NODE_RELATIVE_POSITION_IN_CONTENT:
-              current_node = child_node;
+              current_node = previous_node = child_node;
               goto loop;
 
             case IDE_XML_POSITION_KIND_UNKNOW:
@@ -709,25 +719,60 @@ result:
       candidate_node = root_node;
       candidate_kind = IDE_XML_POSITION_KIND_IN_CONTENT;
     }
+  else if (candidate_kind == IDE_XML_POSITION_KIND_IN_CONTENT &&
+           previous_node != NULL &&
+           ide_xml_symbol_node_get_state (previous_node) == IDE_XML_SYMBOL_NODE_STATE_NOT_CLOSED)
+    {
+      candidate_node = previous_node;
+      /* TODO: fetch detail and more infos */
+      /* TODO: detect the IN_END_TAG case */
+      candidate_kind = IDE_XML_POSITION_KIND_IN_START_TAG;
+      detail = IDE_XML_POSITION_DETAIL_IN_NAME;
+      has_prefix = TRUE;
+    }
+
+  if (has_prefix)
+    {
+      ide_xml_symbol_node_get_location (candidate_node, &start_line, &start_line_offset, NULL, NULL, NULL);
+      gtk_text_buffer_get_iter_at_line_index (buffer, &start, start_line - 1, start_line_offset - 1);
+      gtk_text_buffer_get_iter_at_line_index (buffer, &end, line - 1, line_offset - 1);
 
-  position = ide_xml_position_new (candidate_node, candidate_kind);
-  ide_xml_position_set_analysis (position, analysis);
+      if (gtk_text_iter_get_char (&start) == '<')
+        gtk_text_iter_forward_char (&start);
+
+      if (!gtk_text_iter_equal (&start, &end))
+        prefix = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+    }
 
   if (candidate_kind == IDE_XML_POSITION_KIND_IN_CONTENT)
     {
-      if (child_pos > 0)
-        {
-        previous_sibling_node = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_direct_child 
(candidate_node, child_pos - 1));
-          printf ("previous sibling:%p at %d\n", previous_sibling_node, child_pos - 1);
-        }
+      position = ide_xml_position_new (candidate_node, prefix, candidate_kind, detail);
+      ide_xml_position_set_analysis (position, analysis);
+      ide_xml_position_set_child_pos (position, child_pos);
+    }
+  else if (candidate_kind == IDE_XML_POSITION_KIND_IN_START_TAG ||
+           candidate_kind == IDE_XML_POSITION_KIND_IN_END_TAG)
+    {
+      child_node = candidate_node;
+      candidate_node = ide_xml_symbol_node_get_parent (child_node);
 
-      if (child_pos <= n_children)
-        next_sibling_node = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_direct_child (candidate_node, 
child_pos));
+      position = ide_xml_position_new (candidate_node, prefix, candidate_kind, detail);
+      ide_xml_position_set_analysis (position, analysis);
+      ide_xml_position_set_child_node (position, child_node);
+    }
+  else
+    g_assert_not_reached ();
 
-      ide_xml_position_set_siblings (position, previous_sibling_node, next_sibling_node);
-      ide_xml_position_set_child_pos (position, child_pos);
+  if (child_pos > 0)
+    {
+    previous_sibling_node = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_direct_child (candidate_node, 
child_pos - 1));
     }
 
+  if (child_pos < n_children)
+    next_sibling_node = IDE_XML_SYMBOL_NODE (ide_xml_symbol_node_get_nth_direct_child (candidate_node, 
child_pos));
+
+  ide_xml_position_set_siblings (position, previous_sibling_node, next_sibling_node);
+
   return position;
 }
 
@@ -751,7 +796,7 @@ ide_xml_service_get_position_from_cursor_cb (GObject      *object,
   analysis = ide_xml_service_get_analysis_finish (self, result, &error);
   if (analysis != NULL)
     {
-      position = get_position (self, analysis, state->line, state->line_offset);
+      position = get_position (self, analysis, (GtkTextBuffer *)state->buffer, state->line, 
state->line_offset);
       g_task_return_pointer (task, position, g_object_unref);
     }
   else
diff --git a/plugins/xml-pack/ide-xml-symbol-node.c b/plugins/xml-pack/ide-xml-symbol-node.c
index b58795c..85a4934 100644
--- a/plugins/xml-pack/ide-xml-symbol-node.c
+++ b/plugins/xml-pack/ide-xml-symbol-node.c
@@ -433,7 +433,6 @@ ide_xml_symbol_node_set_location (IdeXmlSymbolNode *self,
 {
   g_return_if_fail (IDE_IS_XML_SYMBOL_NODE (self));
   g_return_if_fail (G_IS_FILE (file) || file == NULL);
-  g_return_if_fail (size >= 2);
 
   g_clear_object (&self->file);
   if (file != NULL)


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