diff --git a/changelog.md b/changelog.md index 589d50b46..9d30151a2 100644 --- a/changelog.md +++ b/changelog.md @@ -28,6 +28,7 @@ - [inline_processor.py](https://github.com/jackdewinter/pymarkdown/issues/130) - [block_quote_processor.py](https://github.com/jackdewinter/pymarkdown/issues/134) - [tokenized_markdown.py](https://github.com/jackdewinter/pymarkdown/issues/136) + - [main.py + inline_helper.py + rule md027](https://github.com/jackdewinter/pymarkdown/issues/138) ### Fixed diff --git a/publish/coverage.json b/publish/coverage.json index c4b532c4c..f3a10ae41 100644 --- a/publish/coverage.json +++ b/publish/coverage.json @@ -1 +1 @@ -{"projectName": "pymarkdown", "reportSource": "pytest", "branchLevel": {"totalMeasured": 2954, "totalCovered": 2954}, "lineLevel": {"totalMeasured": 9872, "totalCovered": 9872}} \ No newline at end of file +{"projectName": "pymarkdown", "reportSource": "pytest", "branchLevel": {"totalMeasured": 2956, "totalCovered": 2956}, "lineLevel": {"totalMeasured": 9900, "totalCovered": 9900}} \ No newline at end of file diff --git a/publish/pylint_suppression.json b/publish/pylint_suppression.json index d68983f5c..fdf5f48ca 100644 --- a/publish/pylint_suppression.json +++ b/publish/pylint_suppression.json @@ -68,9 +68,8 @@ "pymarkdown/inline_helper.py": { "too-few-public-methods": 2, "too-many-instance-attributes": 2, - "too-many-arguments": 2, - "too-many-locals": 1, - "too-many-statements": 1 + "too-many-arguments": 3, + "too-many-locals": 1 }, "pymarkdown/inline_markdown_token.py": { "too-many-arguments": 7, @@ -103,8 +102,7 @@ "too-many-locals": 4 }, "pymarkdown/main.py": { - "broad-except": 2, - "too-many-branches": 1 + "broad-except": 2 }, "pymarkdown/markdown_token.py": { "too-many-public-methods": 1, @@ -162,9 +160,7 @@ "pymarkdown/plugins/rule_md_024.py": {}, "pymarkdown/plugins/rule_md_025.py": {}, "pymarkdown/plugins/rule_md_026.py": {}, - "pymarkdown/plugins/rule_md_027.py": { - "too-many-branches": 1 - }, + "pymarkdown/plugins/rule_md_027.py": {}, "pymarkdown/plugins/rule_md_028.py": {}, "pymarkdown/plugins/rule_md_029.py": {}, "pymarkdown/plugins/rule_md_030.py": { @@ -218,14 +214,12 @@ "pymarkdown/version.py": {} }, "disables-by-name": { - "too-many-arguments": 125, + "too-many-arguments": 126, "too-many-locals": 38, "too-few-public-methods": 17, "broad-except": 6, "no-self-use": 1, "too-many-instance-attributes": 15, - "too-many-statements": 1, - "too-many-branches": 2, "too-many-public-methods": 2, "no-member": 1, "invalid-unary-operand-type": 1 diff --git a/pymarkdown/inline_helper.py b/pymarkdown/inline_helper.py index 6eee44fcb..2c270a1ff 100644 --- a/pymarkdown/inline_helper.py +++ b/pymarkdown/inline_helper.py @@ -20,6 +20,8 @@ POGGER = ParserLogger(logging.getLogger(__name__)) +# pylint: disable=too-many-lines + # pylint: disable=too-few-public-methods, too-many-instance-attributes class InlineRequest: @@ -108,7 +110,10 @@ class InlineHelper: Class to helper with the parsing of inline elements. """ - __valid_email_regex = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + __valid_email_regex = ( + "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" + + "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + ) __scheme_end_character = ":" __valid_scheme_characters = f"{string.ascii_letters}{string.digits}.-+" @@ -212,62 +217,74 @@ def handle_character_reference(inline_request): and inline_request.source_text[inline_response.new_index] == InlineHelper.__numeric_character_reference_start_character ): - original_new_index = inline_response.new_index - POGGER.debug("here") - ( - inline_response.new_string, - inline_response.new_index, - inline_response.original_string, - ) = InlineHelper.__handle_numeric_character_reference( - inline_request.source_text, inline_response.new_index + InlineHelper.__handle_numeric_character_reference( + inline_request, inline_response ) - inline_response.new_string_unresolved = ( - InlineHelper.character_reference_start_character - + inline_request.source_text[ - original_new_index : inline_response.new_index - ] - ) - POGGER.debug("here-->$<--", inline_response.new_string) - POGGER.debug("here-->$<--", inline_response.new_string_unresolved) else: - POGGER.debug("there") - end_index, collected_string = ParserHelper.collect_while_one_of_characters( - inline_request.source_text, - inline_response.new_index, - InlineHelper.__ascii_letters_and_digits, - ) - if collected_string: - collected_string = f"{InlineHelper.character_reference_start_character}{collected_string}" - if ( - end_index < source_text_size - and inline_request.source_text[end_index] - == InlineHelper.__character_reference_end_character - ): - end_index += 1 - collected_string += InlineHelper.__character_reference_end_character - original_collected_string = collected_string - if collected_string in InlineHelper.__entity_map: - inline_response.original_string = collected_string - collected_string = InlineHelper.__entity_map[collected_string] - inline_response.new_string_unresolved = ( - original_collected_string - ) - inline_response.new_string, inline_response.new_index = ( - collected_string, - end_index, - ) - POGGER.debug("there-->$<--", inline_response.new_string) - POGGER.debug("there-->$<--", inline_response.new_string_unresolved) - else: - inline_response.new_string = ( - InlineHelper.character_reference_start_character - ) + InlineHelper.__handle_non_numeric_character_reference( + inline_request, inline_response, source_text_size + ) inline_response.delta_line_number, inline_response.delta_column_number = ( 0, inline_response.new_index - inline_request.next_index, ) return inline_response + @staticmethod + def __handle_numeric_character_reference(inline_request, inline_response): + original_new_index = inline_response.new_index + POGGER.debug("here") + ( + inline_response.new_string, + inline_response.new_index, + inline_response.original_string, + ) = InlineHelper.__handle_numeric_character_reference_inner( + inline_request.source_text, inline_response.new_index + ) + inline_response.new_string_unresolved = ( + InlineHelper.character_reference_start_character + + inline_request.source_text[original_new_index : inline_response.new_index] + ) + POGGER.debug("here-->$<--", inline_response.new_string) + POGGER.debug("here-->$<--", inline_response.new_string_unresolved) + + @staticmethod + def __handle_non_numeric_character_reference( + inline_request, inline_response, source_text_size + ): + POGGER.debug("there") + end_index, collected_string = ParserHelper.collect_while_one_of_characters( + inline_request.source_text, + inline_response.new_index, + InlineHelper.__ascii_letters_and_digits, + ) + if collected_string: + collected_string = ( + f"{InlineHelper.character_reference_start_character}{collected_string}" + ) + if ( + end_index < source_text_size + and inline_request.source_text[end_index] + == InlineHelper.__character_reference_end_character + ): + end_index += 1 + collected_string += InlineHelper.__character_reference_end_character + original_collected_string = collected_string + if collected_string in InlineHelper.__entity_map: + inline_response.original_string = collected_string + collected_string = InlineHelper.__entity_map[collected_string] + inline_response.new_string_unresolved = original_collected_string + inline_response.new_string, inline_response.new_index = ( + collected_string, + end_index, + ) + POGGER.debug("there-->$<--", inline_response.new_string) + POGGER.debug("there-->$<--", inline_response.new_string_unresolved) + else: + inline_response.new_string = ( + InlineHelper.character_reference_start_character + ) + @staticmethod def handle_backslashes(source_text, add_text_signature=True): """ @@ -391,6 +408,37 @@ def handle_inline_backtick(inline_request): extracted_start_backticks, end_backticks_index ) + inline_response = InlineHelper.__build_backtick_response( + inline_request, + end_backtick_start_index, + extracted_start_backticks, + new_index, + extracted_start_backticks_size, + ) + + POGGER.debug( + ">>delta_line_number>>$<<", + inline_response.delta_line_number, + ) + POGGER.debug( + ">>delta_column_number>>$<<", + inline_response.delta_column_number, + ) + if inline_response.delta_line_number == -1: + inline_response.delta_line_number, inline_response.delta_column_number = ( + 0, + inline_response.new_index - inline_request.next_index, + ) + return inline_response + + @staticmethod + def __build_backtick_response( + inline_request, + end_backtick_start_index, + extracted_start_backticks, + new_index, + extracted_start_backticks_size, + ): inline_response = InlineResponse() inline_response.delta_line_number = -1 @@ -400,54 +448,13 @@ def handle_inline_backtick(inline_request): new_index, ) else: - between_text = inline_request.source_text[ - new_index:end_backtick_start_index - ] - original_between_text = between_text - POGGER.debug( - "after_collect>$>>$>>$<<", + ( between_text, - end_backtick_start_index, - inline_request.source_text[end_backtick_start_index:], - ) - leading_whitespace, trailing_whitespace = "", "" - if ( - len(between_text) > 2 - and between_text[0] - in [ParserHelper.space_character, ParserHelper.newline_character] - and between_text[-1] - in [ParserHelper.space_character, ParserHelper.newline_character] - ): - stripped_between_attempt = between_text[1:-1] - if len(stripped_between_attempt.strip()) != 0: - leading_whitespace, trailing_whitespace = ( - between_text[0], - between_text[-1], - ) - between_text = stripped_between_attempt - - replaced_newline = ParserHelper.create_replacement_markers( - ParserHelper.newline_character, ParserHelper.space_character - ) - POGGER.debug("between_text>>$<<", between_text) - between_text = ParserHelper.escape_special_characters(between_text) - POGGER.debug("between_text>>$<<", between_text) - POGGER.debug( - "leading_whitespace>>$<<", + original_between_text, leading_whitespace, - ) - POGGER.debug( - "trailing_whitespace>>$<<", trailing_whitespace, - ) - between_text, leading_whitespace, trailing_whitespace = ( - between_text.replace(ParserHelper.newline_character, replaced_newline), - leading_whitespace.replace( - ParserHelper.newline_character, replaced_newline - ), - trailing_whitespace.replace( - ParserHelper.newline_character, replaced_newline - ), + ) = InlineHelper.__calculate_backtick_between_text( + inline_request, new_index, end_backtick_start_index ) POGGER.debug("between_text>>$<<", between_text) @@ -489,20 +496,6 @@ def handle_inline_backtick(inline_request): ) = ParserHelper.calculate_deltas( f"{original_between_text}{extracted_start_backticks}" ) - - POGGER.debug( - ">>delta_line_number>>$<<", - inline_response.delta_line_number, - ) - POGGER.debug( - ">>delta_column_number>>$<<", - inline_response.delta_column_number, - ) - if inline_response.delta_line_number == -1: - inline_response.delta_line_number, inline_response.delta_column_number = ( - 0, - inline_response.new_index - inline_request.next_index, - ) return inline_response @staticmethod @@ -514,7 +507,65 @@ def modify_end_string(end_string, removed_end_whitespace): return f"{removed_end_whitespace}{ParserHelper.newline_character}" return f"{end_string}{removed_end_whitespace}{ParserHelper.newline_character}" - # pylint: disable=too-many-arguments, too-many-locals, too-many-statements + @staticmethod + def __calculate_backtick_between_text( + inline_request, new_index, end_backtick_start_index + ): + between_text = inline_request.source_text[new_index:end_backtick_start_index] + original_between_text = between_text + POGGER.debug( + "after_collect>$>>$>>$<<", + between_text, + end_backtick_start_index, + inline_request.source_text[end_backtick_start_index:], + ) + leading_whitespace, trailing_whitespace = "", "" + if ( + len(between_text) > 2 + and between_text[0] + in [ParserHelper.space_character, ParserHelper.newline_character] + and between_text[-1] + in [ParserHelper.space_character, ParserHelper.newline_character] + ): + stripped_between_attempt = between_text[1:-1] + if len(stripped_between_attempt.strip()) != 0: + leading_whitespace, trailing_whitespace = ( + between_text[0], + between_text[-1], + ) + between_text = stripped_between_attempt + + replaced_newline = ParserHelper.create_replacement_markers( + ParserHelper.newline_character, ParserHelper.space_character + ) + POGGER.debug("between_text>>$<<", between_text) + between_text = ParserHelper.escape_special_characters(between_text) + POGGER.debug("between_text>>$<<", between_text) + POGGER.debug( + "leading_whitespace>>$<<", + leading_whitespace, + ) + POGGER.debug( + "trailing_whitespace>>$<<", + trailing_whitespace, + ) + between_text, leading_whitespace, trailing_whitespace = ( + between_text.replace(ParserHelper.newline_character, replaced_newline), + leading_whitespace.replace( + ParserHelper.newline_character, replaced_newline + ), + trailing_whitespace.replace( + ParserHelper.newline_character, replaced_newline + ), + ) + return ( + between_text, + original_between_text, + leading_whitespace, + trailing_whitespace, + ) + + # pylint: disable=too-many-arguments, too-many-locals @staticmethod def handle_line_end( next_index, @@ -575,6 +626,60 @@ def handle_line_end( is_proper_hard_break = modified_current_string[-2:] != "\\\b" POGGER.debug(">>$<<", is_proper_hard_break) + ( + current_string, + whitespace_to_add, + append_to_current_string, + end_string, + remaining_line, + ) = InlineHelper.__select_line_ending( + new_tokens, + is_proper_hard_break, + line_number, + adj_hard_column, + current_string, + removed_end_whitespace, + removed_end_whitespace_size, + whitespace_to_add, + append_to_current_string, + end_string, + remaining_line, + inline_blocks, + is_setext, + ) + + if coalesced_stack and coalesced_stack[-1].is_block_quote_start: + coalesced_stack[-1].leading_text_index += 1 + + return ( + append_to_current_string, + whitespace_to_add, + next_index + 1, + new_tokens, + remaining_line, + end_string, + current_string, + ) + + # pylint: enable=too-many-arguments, too-many-locals + + # pylint: disable=too-many-arguments + @staticmethod + def __select_line_ending( + new_tokens, + is_proper_hard_break, + line_number, + adj_hard_column, + current_string, + removed_end_whitespace, + removed_end_whitespace_size, + whitespace_to_add, + append_to_current_string, + end_string, + remaining_line, + inline_blocks, + is_setext, + ): if is_proper_hard_break: POGGER.debug(">>proper hard break") new_tokens.append( @@ -619,9 +724,6 @@ def handle_line_end( ) POGGER.debug("<bqs:{num_container_tokens}, self.__have_incremented_for_this_line={self.__have_incremented_for_this_line}") + # print(f"num->bqs:{num_container_tokens}, self.__have_incremented_for_this_line=" + \ + # f"{self.__have_incremented_for_this_line}") # if num_container_tokens in self.__bq_line_index: - # print(f"{self.__bq_line_index[num_container_tokens]}-->token>{ParserHelper.make_value_visible(token)}") + # print(f"{self.__bq_line_index[num_container_tokens]}-->token>" + \ + # f"{ParserHelper.make_value_visible(token)}") # else: # print(f"token>{ParserHelper.make_value_visible(token)}") # if self.__container_tokens: @@ -181,7 +183,8 @@ def __get_current_block_quote_prefix(self, num_container_tokens): found_block_quote_token = self.__get_last_block_quote() # if self.__debug_on: # print(f"found_block_quote_token={ParserHelper.make_value_visible(found_block_quote_token)}") - # print(f"num_container_tokens={num_container_tokens},self.__bq_line_index={self.__bq_line_index[num_container_tokens]}") + # print(f"num_container_tokens={num_container_tokens},self.__bq_line_index=" + \ + # f"{self.__bq_line_index[num_container_tokens]}") split_leading_spaces = found_block_quote_token.leading_spaces.split("\n") # if self.__debug_on: # print(f"specific_block_quote_prefix={specific_block_quote_prefix};") @@ -204,7 +207,8 @@ def __check_list_starts( found_block_quote_token == self.__container_tokens[-1] ) # if self.__debug_on: - # print(f"is_start_properly_scoped={is_start_properly_scoped};found_block_quote_token={ParserHelper.make_value_visible(found_block_quote_token)}") + # print(f"is_start_properly_scoped={is_start_properly_scoped};" + \ + # f"found_block_quote_token={ParserHelper.make_value_visible(found_block_quote_token)}") if is_start_properly_scoped: specific_block_quote_prefix = self.__get_current_block_quote_prefix( num_container_tokens @@ -231,7 +235,9 @@ def __handle_list_start(self, context, token, num_container_tokens): def __handle_new_list_item(self, context, token, num_container_tokens): # if self.__debug_on: - # print(f"num_container_tokens={num_container_tokens}, __is_paragraph_end_delayed={self.__is_paragraph_end_delayed}, self.__have_incremented_for_this_line={self.__have_incremented_for_this_line}") + # print(f"num_container_tokens={num_container_tokens}, __is_paragraph_end_delayed=" + \ + # f"{self.__is_paragraph_end_delayed}, self.__have_incremented_for_this_line=" + \ + # f"{self.__have_incremented_for_this_line}") if ( num_container_tokens and not self.__have_incremented_for_this_line @@ -345,7 +351,8 @@ def __handle_fenced_code_block_end( # if self.__debug_on: # print(f"end-container>>{ParserHelper.make_value_visible(self.__container_tokens[-1])}") # print(f"split_leading_spaces>>{ParserHelper.make_value_visible(split_leading_spaces)}") - # print(f"specific_block_quote_prefix>>:{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") + # print(f"specific_block_quote_prefix>>:" + \ + # f"{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") # print("fenced-end-error") self.report_next_token_error( context, @@ -388,7 +395,8 @@ def __handle_link_reference_definition(self, context, token, num_container_token # print(f"split_array_index>>{split_array_index}") # print(f"end-container>>{ParserHelper.make_value_visible(self.__container_tokens[-1])}") # print(f"split_leading_spaces>>{ParserHelper.make_value_visible(split_leading_spaces)}") - # print(f"specific_block_quote_prefix>>:{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") + # print(f"specific_block_quote_prefix>>:" + \ + # f"{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") # print("lrd-2-error") self.report_next_token_error( context, @@ -419,7 +427,8 @@ def __handle_link_reference_definition(self, context, token, num_container_token # print("split_array_index>>" + str(split_array_index)) # print(f"end-container>>{ParserHelper.make_value_visible(self.__container_tokens[-1])}") # print(f"split_leading_spaces>>{ParserHelper.make_value_visible(split_leading_spaces)}") - # print(f"specific_block_quote_prefix>>:{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") + # print(f"specific_block_quote_prefix>>:" + \ + # f"{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") # print("lrd-3-error") self.report_next_token_error( context, @@ -469,7 +478,8 @@ def __handle_text( # if self.__debug_on: # print(f"split_leading_spaces>>{ParserHelper.make_value_visible(split_leading_spaces)}") # print(f"split_array_index>>{ParserHelper.make_value_visible(split_array_index)}") - # print(f"specific_block_quote_prefix>>:{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") + # print(f"specific_block_quote_prefix>>:" + \ + # f"{ParserHelper.make_value_visible(specific_block_quote_prefix)}:") # print("setext-text-error") self.report_next_token_error( context, @@ -526,7 +536,6 @@ def __handle_paragraph( "\n" ) - # pylint: disable=too-many-branches def __handle_within_block_quotes(self, context, token): # if self.__debug_on: # print("__handle_within_block_quotes") @@ -572,22 +581,8 @@ def __handle_within_block_quotes(self, context, token): self.__handle_setext_heading_end( context, token, num_container_tokens, is_directly_within_block_quote ) - elif token.is_html_block or token.is_indented_code_block: - self.__last_leaf_token = token - elif ( - token.is_html_block_end - or token.is_indented_code_block_end - or token.is_atx_heading_end - ): + elif token.is_atx_heading_end: self.__last_leaf_token = None - elif token.is_fenced_code_block: - self.__handle_fenced_code_block( - context, token, num_container_tokens, is_directly_within_block_quote - ) - elif token.is_fenced_code_block_end: - self.__handle_fenced_code_block_end( - context, token, num_container_tokens, is_directly_within_block_quote - ) elif token.is_thematic_break: self.__handle_thematic_break( context, token, num_container_tokens, is_directly_within_block_quote @@ -596,7 +591,25 @@ def __handle_within_block_quotes(self, context, token): self.__handle_link_reference_definition( context, token, num_container_tokens ) + else: + self.__handle_within_block_quotes_blocks( + token, context, num_container_tokens, is_directly_within_block_quote + ) # if self.__debug_on: # print(f"{self.__bq_line_index[num_container_tokens]}<--token>{ParserHelper.make_value_visible(token)}") - # pylint: enable=too-many-branches + def __handle_within_block_quotes_blocks( + self, token, context, num_container_tokens, is_directly_within_block_quote + ): + if token.is_fenced_code_block: + self.__handle_fenced_code_block( + context, token, num_container_tokens, is_directly_within_block_quote + ) + elif token.is_fenced_code_block_end: + self.__handle_fenced_code_block_end( + context, token, num_container_tokens, is_directly_within_block_quote + ) + elif token.is_html_block or token.is_indented_code_block: + self.__last_leaf_token = token + elif token.is_html_block_end or token.is_indented_code_block_end: + self.__last_leaf_token = None