From 53ba6368eb56d5fa3be1f9d4bf1a4226035b82ac Mon Sep 17 00:00:00 2001 From: Dieter Verfaillie Date: Sun, 15 Mar 2015 20:31:44 +0100 Subject: scanner: allow multiline annotations Allow `identifier`, `parameter` and `tag` part `annotations` fields to span multiple lines https://bugzilla.gnome.org/show_bug.cgi?id=676133 --- giscanner/annotationparser.py | 99 ++-- .../annotationparser/gi/identifier_symbol.xml | 14 +- tests/scanner/annotationparser/gi/parameter.xml | 21 +- .../gi/syntax_multiline_annotations.xml | 619 ++++++++++++++++++++- tests/scanner/annotationparser/gi/tag.xml | 19 +- 5 files changed, 678 insertions(+), 94 deletions(-) diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index e093f340..f69ff250 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -61,12 +61,12 @@ specific order: comment block which consists of: * a required `identifier_name` field - * an optional `annotations` field + * an optional `annotations` field, optionally spanning multiple lines #. Zero or more `parameter` parts, each consisting of: * a required `parameter_name` field - * an optional `annotations` field + * an optional `annotations` field, optionally spanning multiple lines * a required `description` field (can be the empty string) #. One optional `comment block description` part which must begin with at @@ -75,7 +75,7 @@ specific order: #. Zero or more `tag` parts, each consisting of: * a required `tag_name` field - * an optional `annotations` field + * an optional `annotations` field, optionally spanning multiple lines * an optional `value` field * a required `description` field (can be the empty string) @@ -95,6 +95,7 @@ Additionally, the following restrictions are in effect: * the `comment_block_description` part * `parameter description` and `tag description` fields + * `identifier`, `parameter` and `tag` part `annotations` fields #. Taking the above restrictions into account, spanning multiple paragraphs is limited to the `comment block description` part and `tag description` fields. @@ -507,13 +508,16 @@ class GtkDocAnnotations(OrderedDict): __slots__ = ('position') - def __init__(self, position=None): - OrderedDict.__init__(self) + def __init__(self, position=None, sequence=None): + OrderedDict.__init__(self, sequence) #: A :class:`giscanner.message.Position` instance specifying the location of the #: annotations in the source file or :const:`None`. self.position = position + def __copy__(self): + return GtkDocAnnotations(self.position, self) + class GtkDocAnnotatable(object): ''' @@ -1071,10 +1075,12 @@ class GtkDocCommentBlock(GtkDocAnnotatable): #: Result object returned by :class:`GtkDocCommentBlockParser`._parse_annotations() -_ParseAnnotationsResult = namedtuple('Result', ['success', 'annotations', 'start_pos', 'end_pos']) +_ParseAnnotationsResult = namedtuple('Result', ['success', 'annotations', 'annotations_changed', + 'start_pos', 'end_pos']) #: Result object returned by :class:`GtkDocCommentBlockParser`._parse_fields() -_ParseFieldsResult = namedtuple('Result', ['success', 'annotations', 'description']) +_ParseFieldsResult = namedtuple('Result', ['success', 'annotations', 'annotations_changed', + 'description']) class GtkDocCommentBlockParser(object): @@ -1459,6 +1465,7 @@ class GtkDocCommentBlockParser(object): result.start('tag_name') + column_offset, line, tag_fields.strip(), + None, False, False) @@ -1592,8 +1599,12 @@ class GtkDocCommentBlockParser(object): if in_part in [PART_IDENTIFIER, PART_DESCRIPTION]: if not comment_block.description: if in_part == PART_IDENTIFIER: - self._validate_multiline_annotation_continuation(line, original_line, - column_offset, position) + r = self._parse_annotations(position, column_offset, original_line, line, + comment_block.annotations) + + if r.success and r.annotations_changed: + comment_block.annotations = r.annotations + continue if comment_block.description is None: comment_block.description = line else: @@ -1601,8 +1612,12 @@ class GtkDocCommentBlockParser(object): continue elif in_part in [PART_PARAMETERS, PART_TAGS]: if not current_part.description: - self._validate_multiline_annotation_continuation(line, original_line, - column_offset, position) + r = self._parse_fields(position, column_offset, original_line, line, + current_part.annotations) + if r.success and r.annotations_changed: + current_part.annotations = r.annotations + current_part.description = r.description + continue if current_part.description is None: current_part.description = line else: @@ -1646,34 +1661,6 @@ class GtkDocCommentBlockParser(object): else: part.description = part.description.strip() - def _validate_multiline_annotation_continuation(self, line, original_line, - column_offset, position): - ''' - Validate annotatable parts' source text ensuring annotations don't span multiple lines. - For example, the following comment block would result in a warning being emitted for - the forth line:: - - /** - * shiny_function: - * @array_: (out caller-allocates) (array) - * (element-type utf8) (transfer full): A beautiful array - */ - - :param line: line to validate, stripped from ("``*/``") at start of the line. - :param original_line: original line (including ("``*/``")) being validated - :param column_offset: number of characters stripped from `line` when ("``*/``") - was removed - :param position: :class:`giscanner.message.Position` of `line` in the source file - ''' - - result = self._parse_annotations(position, column_offset, original_line, line) - - if result.success and result.annotations: - marker = ' ' * (result.start_pos + column_offset) + '^' - error('ignoring invalid multiline annotation continuation:\n%s\n%s' % - (original_line, marker), - position) - def _parse_annotation_options_list(self, position, column, line, options): ''' Parse annotation options into a list. For example:: @@ -1831,7 +1818,8 @@ class GtkDocCommentBlockParser(object): return ann_name, ann_options - def _parse_annotations(self, position, column, line, fields, parse_options=True): + def _parse_annotations(self, position, column, line, fields, + annotations=None, parse_options=True): ''' Parse annotations into a :class:`GtkDocAnnotations` object. @@ -1839,6 +1827,7 @@ class GtkDocCommentBlockParser(object): :param column: start column of the `annotations` in the source file :param line: complete source line :param fields: string containing the fields to parse + :param annotations: a :class:`GtkDocAnnotations` object :param parse_options: whether options will be parsed into a :class:`GtkDocAnnotations` object or into a :class:`list` :returns: if `parse_options` evaluates to True a :class:`GtkDocAnnotations` object, @@ -1847,10 +1836,15 @@ class GtkDocCommentBlockParser(object): ''' if parse_options: - parsed_annotations = GtkDocAnnotations(position) + if annotations is None: + parsed_annotations = GtkDocAnnotations(position) + else: + parsed_annotations = annotations.copy() else: parsed_annotations = [] + parsed_annotations_changed = False + i = 0 parens_level = 0 prev_char = '' @@ -1872,7 +1866,7 @@ class GtkDocCommentBlockParser(object): error('unexpected parentheses, annotations will be ignored:\n%s\n%s' % (line, marker), position) - return _ParseAnnotationsResult(False, None, None, None) + return _ParseAnnotationsResult(False, None, None, None, None) elif parens_level > 1: char_buffer.append(cur_char) elif cur_char == ANN_RPAR: @@ -1883,13 +1877,13 @@ class GtkDocCommentBlockParser(object): error('unexpected parentheses, annotations will be ignored:\n%s\n%s' % (line, marker), position) - return _ParseAnnotationsResult(False, None, None, None) + return _ParseAnnotationsResult(False, None, None, None, None) elif parens_level < 0: marker = ' ' * (column + i) + '^' error('unbalanced parentheses, annotations will be ignored:\n%s\n%s' % (line, marker), position) - return _ParseAnnotationsResult(False, None, None, None) + return _ParseAnnotationsResult(False, None, None, None, None) elif parens_level == 0: end_pos = i + 1 @@ -1904,8 +1898,10 @@ class GtkDocCommentBlockParser(object): error('multiple "%s" annotations:\n%s\n%s' % (name, line, marker), position) parsed_annotations[name] = options + parsed_annotations_changed = True else: parsed_annotations.append(''.join(char_buffer).strip()) + parsed_annotations_changed = True char_buffer = [] else: @@ -1926,12 +1922,13 @@ class GtkDocCommentBlockParser(object): error('unbalanced parentheses, annotations will be ignored:\n%s\n%s' % (line, marker), position) - return _ParseAnnotationsResult(False, None, None, None) + return _ParseAnnotationsResult(False, None, None, None, None) else: - return _ParseAnnotationsResult(True, parsed_annotations, start_pos, end_pos) + return _ParseAnnotationsResult(True, parsed_annotations, parsed_annotations_changed, + start_pos, end_pos) - def _parse_fields(self, position, column, line, fields, parse_options=True, - validate_description_field=True): + def _parse_fields(self, position, column, line, fields, annotations=None, + parse_options=True, validate_description_field=True): ''' Parse annotations out of field data. For example:: @@ -1953,7 +1950,8 @@ class GtkDocCommentBlockParser(object): :const:`None` and a string holding the remaining fields ''' description_field = '' - result = self._parse_annotations(position, column, line, fields, parse_options) + result = self._parse_annotations(position, column, line, fields, + annotations, parse_options) if result.success: description_field = fields[result.end_pos:].strip() @@ -1968,7 +1966,8 @@ class GtkDocCommentBlockParser(object): (marker_position + 1, line, marker), position) - return _ParseFieldsResult(result.success, result.annotations, description_field) + return _ParseFieldsResult(result.success, result.annotations, result.annotations_changed, + description_field) class GtkDocCommentBlockWriter(object): diff --git a/tests/scanner/annotationparser/gi/identifier_symbol.xml b/tests/scanner/annotationparser/gi/identifier_symbol.xml index 522f1fa8..fd611cd9 100644 --- a/tests/scanner/annotationparser/gi/identifier_symbol.xml +++ b/tests/scanner/annotationparser/gi/identifier_symbol.xml @@ -393,6 +393,9 @@ skip + + foreign + @@ -414,20 +417,13 @@ first parameter - (foreign) -Annotations spanning multiple lines are not valid + Annotations spanning multiple lines are not valid - - 3: Error: Test: ignoring invalid multiline annotation continuation: - * (foreign) - ^ - /** - * test_multiline_annotations_on_identifier: (skip) + * test_multiline_annotations_on_identifier: (skip) (foreign) * @param1: (allow-none) (transfer full): first parameter * - * (foreign) * Annotations spanning multiple lines are not valid */ diff --git a/tests/scanner/annotationparser/gi/parameter.xml b/tests/scanner/annotationparser/gi/parameter.xml index 43f97b3f..51ba6f5e 100644 --- a/tests/scanner/annotationparser/gi/parameter.xml +++ b/tests/scanner/annotationparser/gi/parameter.xml @@ -138,23 +138,24 @@ allow-none + + transfer + + + + - -(transfer full): first parameter + first parameter Annotations spanning multiple lines are not valid - - 4: Error: Test: ignoring invalid multiline annotation continuation: - * (transfer full): first parameter - ^ - /** * test_multiline_annotations_on_parameter: - * @param1: (allow-none): - * (transfer full): first parameter + * @param1: (allow-none) (transfer full): first parameter * * Annotations spanning multiple lines are not valid */ @@ -202,7 +203,7 @@ * anjuta_async_notify_get_error: * * @self: An #AnjutaAsyncNotify object - * @error: Return location for the error set by the called interface to which + * @error: Return location for the error set by the called interface to which * this object was passed. If no error is set, @error is set to NULL. * * Gets the error set on @self. diff --git a/tests/scanner/annotationparser/gi/syntax_multiline_annotations.xml b/tests/scanner/annotationparser/gi/syntax_multiline_annotations.xml index 4aa92e42..c2897948 100644 --- a/tests/scanner/annotationparser/gi/syntax_multiline_annotations.xml +++ b/tests/scanner/annotationparser/gi/syntax_multiline_annotations.xml @@ -2,6 +2,40 @@ + + /** + * regress_forced_method: + * (skip) + * (method) + * @obj: A #RegressTestObj + */ + + + + regress_forced_method + + + skip + + + method + + + + + + obj + A #RegressTestObj + + + + + /** + * regress_forced_method: (skip) (method) + * @obj: A #RegressTestObj + */ + + /** * regress_forced_method: (skip) @@ -16,6 +50,9 @@ skip + + method + @@ -24,19 +61,11 @@ A #RegressTestObj - (method) - - 3: Error: Test: ignoring invalid multiline annotation continuation: - * (method) - ^ - /** - * regress_forced_method: (skip) + * regress_forced_method: (skip) (method) * @obj: A #RegressTestObj - * - * (method) */ @@ -54,6 +83,9 @@ skip + + method + @@ -62,19 +94,574 @@ A #RegressTestObj - (method) + + + /** + * regress_forced_method: (skip) (method) + * @obj: A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * (skip) (method) + * @obj: A #RegressTestObj + */ + + + + regress_forced_method + + + skip + + + method + + + + + + obj + A #RegressTestObj + + + + + /** + * regress_forced_method: (skip) (method) + * @obj: A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * @obj: + * (skip) + * (nullable): A #RegressTestObj + */ + + + + regress_forced_method + + + + obj + + + skip + + + nullable + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * @obj: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * @obj: + * (skip) + * (nullable): + * A #RegressTestObj + */ + + + + regress_forced_method + + + + obj + + + skip + + + nullable + + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * @obj: (skip) (nullable): + * A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * @obj: (skip) + * (nullable): A #RegressTestObj + */ + + + + regress_forced_method + + + + obj + + + skip + + + nullable + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * @obj: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * @obj: (skip) + * (nullable) A #RegressTestObj + */ + + + + regress_forced_method + + + + obj + + + skip + + + nullable + + + A #RegressTestObj + + - 3: Error: Test: ignoring invalid multiline annotation continuation: - * (method) - ^ + 4: Warning: Test: missing ":" at column 18: + * (nullable) A #RegressTestObj + ^ /** - * regress_forced_method: (skip) - * @obj: A #RegressTestObj + * regress_forced_method: + * @obj: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * @obj: (skip) + * (skip): A #RegressTestObj + */ + + + + regress_forced_method + + + + obj + + + skip + + + A #RegressTestObj + + + + + 4: Error: Test: multiple "skip" annotations: + * (skip): A #RegressTestObj + ^ + + + + /** + * regress_forced_method: + * @obj: (skip): A #RegressTestObj + */ + + + + /** + * regress_forced_method: * - * (method) + * Returns: + * (skip) + * (nullable): A #RegressTestObj + */ + + + + regress_forced_method + + + + returns + + + skip + + + nullable + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * + * Returns: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * + * Returns: + * (skip) + * (nullable): + * A #RegressTestObj + */ + + + + regress_forced_method + + + + returns + + + skip + + + nullable + + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * + * Returns: (skip) (nullable): + * A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * + * Returns: (skip) + * (nullable): A #RegressTestObj + */ + + + + regress_forced_method + + + + returns + + + skip + + + nullable + + + A #RegressTestObj + + + + + /** + * regress_forced_method: + * + * Returns: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * + * Returns: (skip) + * (nullable) A #RegressTestObj + */ + + + + regress_forced_method + + + + returns + + + skip + + + nullable + + + A #RegressTestObj + + + + + 5: Warning: Test: missing ":" at column 18: + * (nullable) A #RegressTestObj + ^ + + + /** + * regress_forced_method: + * + * Returns: (skip) (nullable): A #RegressTestObj + */ + + + + /** + * regress_forced_method: + * + * Returns: (skip) + * (skip): A #RegressTestObj + */ + + + + regress_forced_method + + + + returns + + + skip + + + A #RegressTestObj + + + + + 5: Error: Test: multiple "skip" annotations: + * (skip): A #RegressTestObj + ^ + + + + /** + * regress_forced_method: + * + * Returns: (skip): A #RegressTestObj + */ + + + + /** + * gtk_window_set_has_frame: + * @window: a #GtkWindow + * @setting: a boolean + * + * (Note: this is a special-purpose function for the framebuffer port, + * that causes GTK+ to draw its own window border. For most applications, + * you want gtk_window_set_decorated() instead, which tells the window + * manager whether to draw the window border.) + * + * If this function is called on a window with setting of %TRUE, before + * it is realized or showed, it will have a "frame" window around + * @window->window, accessible in @window->frame. Using the signal + * frame_event you can receive all events targeted at the frame. + * + * This function is used by the linux-fb port to implement managed + * windows, but it could conceivably be used by X-programs that + * want to do their own window decorations. + * + * Deprecated: 2.24: This function will be removed in GTK+ 3 + **/ + + + + gtk_window_set_has_frame + + + + window + a #GtkWindow + + + setting + a boolean + + + (Note: this is a special-purpose function for the framebuffer port, + that causes GTK+ to draw its own window border. For most applications, + you want gtk_window_set_decorated() instead, which tells the window + manager whether to draw the window border.) + +If this function is called on a window with setting of %TRUE, before +it is realized or showed, it will have a "frame" window around +@window->window, accessible in @window->frame. Using the signal +frame_event you can receive all events targeted at the frame. + +This function is used by the linux-fb port to implement managed +windows, but it could conceivably be used by X-programs that +want to do their own window decorations. + + + deprecated + 2.24 + This function will be removed in GTK+ 3 + + + + + /** + * gtk_window_set_has_frame: + * @window: a #GtkWindow + * @setting: a boolean + * + * (Note: this is a special-purpose function for the framebuffer port, + * that causes GTK+ to draw its own window border. For most applications, + * you want gtk_window_set_decorated() instead, which tells the window + * manager whether to draw the window border.) + * + * If this function is called on a window with setting of %TRUE, before + * it is realized or showed, it will have a "frame" window around + * @window->window, accessible in @window->frame. Using the signal + * frame_event you can receive all events targeted at the frame. + * + * This function is used by the linux-fb port to implement managed + * windows, but it could conceivably be used by X-programs that + * want to do their own window decorations. + * + * Deprecated: 2.24: This function will be removed in GTK+ 3 + */ + + + + /** + * gtk_window_set_has_frame: + * @window: a #GtkWindow + * @setting: + * (Note: this is a special-purpose function for the framebuffer port, + * that causes GTK+ to draw its own window border. For most applications, + * you want gtk_window_set_decorated() instead, which tells the window + * manager whether to draw the window border.) + * + * If this function is called on a window with setting of %TRUE, before + * it is realized or showed, it will have a "frame" window around + * @window->window, accessible in @window->frame. Using the signal + * frame_event you can receive all events targeted at the frame. + * + * This function is used by the linux-fb port to implement managed + * windows, but it could conceivably be used by X-programs that + * want to do their own window decorations. + * + * Deprecated: 2.24: This function will be removed in GTK+ 3 + **/ + + + + gtk_window_set_has_frame + + + + window + a #GtkWindow + + + setting + (Note: this is a special-purpose function for the framebuffer port, + that causes GTK+ to draw its own window border. For most applications, + you want gtk_window_set_decorated() instead, which tells the window + manager whether to draw the window border.) + + + If this function is called on a window with setting of %TRUE, before +it is realized or showed, it will have a "frame" window around +@window->window, accessible in @window->frame. Using the signal +frame_event you can receive all events targeted at the frame. + +This function is used by the linux-fb port to implement managed +windows, but it could conceivably be used by X-programs that +want to do their own window decorations. + + + deprecated + 2.24 + This function will be removed in GTK+ 3 + + + + + 5: Error: Test: unbalanced parentheses, annotations will be ignored: + * (Note: this is a special-purpose function for the framebuffer port, + ^ + + + /** + * gtk_window_set_has_frame: + * @window: a #GtkWindow + * @setting: (Note: this is a special-purpose function for the framebuffer port, + * that causes GTK+ to draw its own window border. For most applications, + * you want gtk_window_set_decorated() instead, which tells the window + * manager whether to draw the window border.) + * + * If this function is called on a window with setting of %TRUE, before + * it is realized or showed, it will have a "frame" window around + * @window->window, accessible in @window->frame. Using the signal + * frame_event you can receive all events targeted at the frame. + * + * This function is used by the linux-fb port to implement managed + * windows, but it could conceivably be used by X-programs that + * want to do their own window decorations. + * + * Deprecated: 2.24: This function will be removed in GTK+ 3 */ diff --git a/tests/scanner/annotationparser/gi/tag.xml b/tests/scanner/annotationparser/gi/tag.xml index 8480d4b4..7dd7bb19 100644 --- a/tests/scanner/annotationparser/gi/tag.xml +++ b/tests/scanner/annotationparser/gi/tag.xml @@ -187,25 +187,26 @@ Moo: anything allow-none + + transfer + + + + - -(transfer full): something + something - - 7: Error: Test: ignoring invalid multiline annotation continuation: - * (transfer full): something - ^ - /** * test_multiline_annotations_on_tag: * * Annotations spanning multiple lines are not valid * - * Returns: (allow-none): - * (transfer full): something + * Returns: (allow-none) (transfer full): something */ -- cgit v1.2.1