1
0
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:
Matthias Clasen
2026-03-20 23:01:42 -04:00
parent 59421091a0
commit 3614d588fc
3 changed files with 142 additions and 14 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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))