mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2026-10-24 00:49:00 +03:00
gmarkup: Add another location api
Add an api to get locations for attributes. This vastly improves error reporting, to the point where it is possible to e.g. can highlight erroneous attrbutes in an SVG editor. Basic test included.
This commit is contained in:
@@ -88,6 +88,7 @@ struct _GMarkupParseContext
|
||||
|
||||
MarkupLocation pos;
|
||||
MarkupLocation tag_start;
|
||||
MarkupLocation attr_start;
|
||||
|
||||
GMarkupParseState state;
|
||||
|
||||
@@ -107,6 +108,8 @@ struct _GMarkupParseContext
|
||||
|
||||
GString **attr_names;
|
||||
GString **attr_values;
|
||||
MarkupLocation *attr_pos; /* nullable, owned */
|
||||
MarkupLocation *attr_end; /* nullable, owned */
|
||||
gint cur_attr;
|
||||
gint alloc_attrs;
|
||||
|
||||
@@ -207,6 +210,8 @@ g_markup_parse_context_new (const GMarkupParser *parser,
|
||||
context->tag_stack_gstr = NULL;
|
||||
context->attr_names = NULL;
|
||||
context->attr_values = NULL;
|
||||
context->attr_pos = NULL;
|
||||
context->attr_end = NULL;
|
||||
context->cur_attr = -1;
|
||||
context->alloc_attrs = 0;
|
||||
|
||||
@@ -303,6 +308,8 @@ g_markup_parse_context_free (GMarkupParseContext *context)
|
||||
clear_attributes (context);
|
||||
g_free (context->attr_names);
|
||||
g_free (context->attr_values);
|
||||
g_free (context->attr_pos);
|
||||
g_free (context->attr_end);
|
||||
|
||||
g_slist_free_full (context->tag_stack_gstr, string_full_free);
|
||||
g_slist_free (context->tag_stack);
|
||||
@@ -944,14 +951,18 @@ add_attribute (GMarkupParseContext *context, GString *str)
|
||||
if (context->cur_attr + 2 >= context->alloc_attrs)
|
||||
{
|
||||
context->alloc_attrs += 5; /* silly magic number */
|
||||
context->attr_names = g_realloc (context->attr_names, sizeof(GString*)*context->alloc_attrs);
|
||||
context->attr_values = g_realloc (context->attr_values, sizeof(GString*)*context->alloc_attrs);
|
||||
context->attr_names = g_realloc_n (context->attr_names, sizeof(GString*), context->alloc_attrs);
|
||||
context->attr_values = g_realloc_n (context->attr_values, sizeof(GString*), context->alloc_attrs);
|
||||
context->attr_pos = g_realloc_n (context->attr_pos, sizeof (MarkupLocation), context->alloc_attrs);
|
||||
context->attr_end = g_realloc_n (context->attr_end, sizeof (MarkupLocation), context->alloc_attrs);
|
||||
}
|
||||
context->cur_attr++;
|
||||
context->attr_names[context->cur_attr] = str;
|
||||
context->attr_values[context->cur_attr] = NULL;
|
||||
context->attr_names[context->cur_attr+1] = NULL;
|
||||
context->attr_values[context->cur_attr+1] = NULL;
|
||||
context->attr_pos[context->cur_attr] = context->attr_start;
|
||||
context->attr_end[context->cur_attr] = context->pos; /* Will be overwritten later */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1012,6 +1023,8 @@ emit_start_element (GMarkupParseContext *context,
|
||||
|
||||
attr_names[j] = context->attr_names[i]->str;
|
||||
attr_values[j] = context->attr_values[i]->str;
|
||||
context->attr_pos[j] = context->attr_pos[i];
|
||||
context->attr_end[j] = context->attr_end[i];
|
||||
j++;
|
||||
}
|
||||
attr_names[j] = NULL;
|
||||
@@ -1324,6 +1337,9 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
|
||||
case STATE_INSIDE_ATTRIBUTE_NAME:
|
||||
/* Possible next states: AFTER_ATTRIBUTE_NAME */
|
||||
|
||||
if (!context->partial_chunk || context->partial_chunk->len == 0)
|
||||
context->attr_start = context->pos;
|
||||
|
||||
advance_to_name_end (context);
|
||||
add_to_partial (context, context->start, context->iter);
|
||||
|
||||
@@ -1519,6 +1535,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
|
||||
context->attr_values[context->cur_attr] = context->partial_chunk;
|
||||
context->partial_chunk = NULL;
|
||||
advance_char (context);
|
||||
context->attr_end[context->cur_attr] = context->pos;
|
||||
context->state = STATE_BETWEEN_ATTRIBUTES;
|
||||
context->start = NULL;
|
||||
}
|
||||
@@ -2049,6 +2066,64 @@ g_markup_parse_context_get_tag_start (GMarkupParseContext *context,
|
||||
*offset = context->tag_start.offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_markup_parse_context_get_attribute_position:
|
||||
* @context: a #GMarkupParseContext
|
||||
* @attr: the index of the attribute to query
|
||||
* @start_lines: (out) (optional): return location for the line number of the attribute assignment start
|
||||
* @start_chars: (out) (optional): return location for the character number of the attribute assignment start
|
||||
* @start_offset: (out) (optional): return location for offset of the attribute assignment
|
||||
* @end_lines: (out) (optional): return location for the line number of the attribute assignment end
|
||||
* @end_chars: (out) (optional): return location for the character number of the attribute assignment end
|
||||
* @end_offset: (out) (optional): return location for offset of the attribute assignment end
|
||||
*
|
||||
* Retrieves the start and end positions of an attribute assignment
|
||||
* in a start tag.
|
||||
*
|
||||
* This function can be used in the `start_element` callback to
|
||||
* obtain location information for error reporting.
|
||||
*
|
||||
* Calling it outside of the `start_element` callback
|
||||
* has undefined results.
|
||||
*
|
||||
* Note that @line_number and @char_number are intended for human
|
||||
* readable error messages and are therefore 1-based and in Unicode
|
||||
* characters. @offset on the other hand is meant for programmatic
|
||||
* use, and thus is 0-based and in bytes.
|
||||
*
|
||||
* The information is meant to accompany the values returned by
|
||||
* [method@GLib.MarkupParseContext.get_position], and comes with the
|
||||
* same accuracy guarantees.
|
||||
*
|
||||
* Since: 2.90
|
||||
*/
|
||||
void
|
||||
g_markup_parse_context_get_attribute_position (GMarkupParseContext *context,
|
||||
unsigned int attr,
|
||||
size_t *start_lines,
|
||||
size_t *start_chars,
|
||||
size_t *start_offset,
|
||||
size_t *end_lines,
|
||||
size_t *end_chars,
|
||||
size_t *end_offset)
|
||||
{
|
||||
g_return_if_fail (context != NULL);
|
||||
g_return_if_fail (context->cur_attr >= 0 && attr <= (unsigned int) context->cur_attr);
|
||||
|
||||
if (start_lines)
|
||||
*start_lines = context->attr_pos[attr].lines;
|
||||
if (start_chars)
|
||||
*start_chars = context->attr_pos[attr].chars;
|
||||
if (start_offset)
|
||||
*start_offset = context->attr_pos[attr].offset;
|
||||
if (end_lines)
|
||||
*end_lines = context->attr_end[attr].lines;
|
||||
if (end_chars)
|
||||
*end_chars = context->attr_end[attr].chars;
|
||||
if (end_offset)
|
||||
*end_offset = context->attr_end[attr].offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_markup_parse_context_get_user_data:
|
||||
* @context: a #GMarkupParseContext
|
||||
|
||||
@@ -235,6 +235,16 @@ void g_markup_parse_context_get_tag_start (GMarkupParseContext *
|
||||
gsize *line_number,
|
||||
gsize *char_number,
|
||||
gsize *offset);
|
||||
GLIB_AVAILABLE_IN_2_90
|
||||
void g_markup_parse_context_get_attribute_position
|
||||
(GMarkupParseContext *context,
|
||||
unsigned int attr,
|
||||
size_t *start_lines,
|
||||
size_t *start_chars,
|
||||
size_t *start_offset,
|
||||
size_t *end_lines,
|
||||
size_t *end_chars,
|
||||
size_t *end_offset);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gpointer g_markup_parse_context_get_user_data (GMarkupParseContext *context);
|
||||
|
||||
@@ -25,17 +25,30 @@ static gsize tag_chars;
|
||||
static gsize tag_offset;
|
||||
|
||||
static void
|
||||
check_positions (GMarkupParseContext *context)
|
||||
assert_ordered (size_t lines1, size_t chars1, size_t offset1,
|
||||
size_t lines2, size_t chars2, size_t offset2)
|
||||
{
|
||||
g_assert_cmpuint (lines1, <=, lines2);
|
||||
if (lines1 == lines2)
|
||||
g_assert_cmpuint (chars1, <=, chars2);
|
||||
g_assert_cmpuint (offset1, <=, offset2);
|
||||
}
|
||||
|
||||
static void
|
||||
check_positions (GMarkupParseContext *context,
|
||||
const char **attr_names,
|
||||
const char **attr_values)
|
||||
{
|
||||
gsize chars, lines, offset;
|
||||
int current_lines, current_chars;
|
||||
|
||||
/* Just some basic sanity check that positions are increasing */
|
||||
|
||||
g_markup_parse_context_get_tag_start (context, &lines, &chars, &offset);
|
||||
|
||||
g_assert_cmpint (tag_lines, <=, lines);
|
||||
if (tag_lines == lines)
|
||||
g_assert_cmpint (tag_chars, <=, chars);
|
||||
g_assert_cmpint (tag_offset, <=, offset);
|
||||
assert_ordered (tag_lines, tag_chars, tag_offset,
|
||||
lines, chars, offset);
|
||||
|
||||
tag_lines = lines;
|
||||
tag_chars = chars;
|
||||
tag_offset = offset;
|
||||
@@ -45,10 +58,40 @@ check_positions (GMarkupParseContext *context)
|
||||
chars = (gsize) current_chars;
|
||||
offset = g_markup_parse_context_get_offset (context);
|
||||
|
||||
g_assert_cmpint (tag_lines, <=, lines);
|
||||
if (tag_lines == lines)
|
||||
g_assert_cmpint (tag_chars, <=, chars);
|
||||
g_assert_cmpint (tag_offset, <=, offset);
|
||||
assert_ordered (tag_lines, tag_chars, tag_offset,
|
||||
lines, chars, offset);
|
||||
|
||||
if (attr_names && attr_values)
|
||||
{
|
||||
size_t start_lines, start_chars, start_offset;
|
||||
size_t end_lines, end_chars, end_offset;
|
||||
size_t last_lines, last_chars, last_offset;
|
||||
|
||||
last_lines = tag_lines;
|
||||
last_chars = tag_chars;
|
||||
last_offset = tag_offset;
|
||||
|
||||
for (size_t i = 0; attr_names[i]; i++)
|
||||
{
|
||||
g_markup_parse_context_get_attribute_position (context, i,
|
||||
&start_lines, &start_chars, &start_offset,
|
||||
&end_lines, &end_chars, &end_offset);
|
||||
|
||||
if (i == 0)
|
||||
assert_ordered (last_lines, last_chars, last_offset,
|
||||
start_lines, start_chars, start_offset);
|
||||
|
||||
assert_ordered (start_lines, start_chars, start_offset,
|
||||
end_lines, end_chars, end_offset);
|
||||
|
||||
last_lines = end_lines;
|
||||
last_chars = end_chars;
|
||||
last_offset = end_offset;
|
||||
}
|
||||
|
||||
assert_ordered (last_lines, last_chars, last_offset,
|
||||
lines, chars, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -61,7 +104,7 @@ start_element_handler (GMarkupParseContext *context,
|
||||
{
|
||||
int i;
|
||||
|
||||
check_positions (context);
|
||||
check_positions (context, attribute_names, attribute_values);
|
||||
|
||||
indent (0);
|
||||
g_string_append_printf (string, "ELEMENT '%s'\n", element_name);
|
||||
@@ -87,7 +130,7 @@ end_element_handler (GMarkupParseContext *context,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
check_positions (context);
|
||||
check_positions (context, NULL, NULL);
|
||||
|
||||
--depth;
|
||||
indent (0);
|
||||
@@ -219,7 +262,7 @@ test_file (const gchar *filename,
|
||||
g_assert_cmpint (line, ==, 1);
|
||||
g_assert_cmpint (col, ==, 1);
|
||||
offset = g_markup_parse_context_get_offset (context);
|
||||
g_assert_cmpint (offset, ==, 0);
|
||||
g_assert_cmpuint (offset, ==, 0);
|
||||
|
||||
if (!g_markup_parse_context_parse (context, contents, -1, NULL) ||
|
||||
!g_markup_parse_context_end_parse (context, NULL))
|
||||
|
||||
Reference in New Issue
Block a user