notionary 0.2.27__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary/__init__.py +5 -20
- notionary/blocks/__init__.py +4 -4
- notionary/blocks/client.py +90 -216
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
- notionary/blocks/rich_text/models.py +178 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
- notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
- notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
- notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
- notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
- notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
- notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
- notionary/blocks/rich_text/rich_text_patterns.py +42 -0
- notionary/blocks/schemas.py +778 -0
- notionary/comments/__init__.py +1 -22
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +38 -0
- notionary/comments/models.py +5 -127
- notionary/comments/schemas.py +240 -0
- notionary/comments/service.py +34 -0
- notionary/data_source/http/client.py +11 -0
- notionary/data_source/http/data_source_instance_client.py +104 -0
- notionary/data_source/properties/schemas.py +402 -0
- notionary/data_source/query/builder.py +448 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +302 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schema/registry.py +104 -0
- notionary/data_source/schema/service.py +136 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +377 -0
- notionary/database/client.py +30 -135
- notionary/database/database_metadata_update_client.py +19 -0
- notionary/database/schemas.py +29 -0
- notionary/database/service.py +168 -0
- notionary/exceptions/__init__.py +33 -0
- notionary/exceptions/api.py +41 -0
- notionary/exceptions/base.py +2 -0
- notionary/exceptions/block_parsing.py +16 -0
- notionary/exceptions/data_source/__init__.py +6 -0
- notionary/exceptions/data_source/builder.py +182 -0
- notionary/exceptions/data_source/properties.py +34 -0
- notionary/exceptions/properties.py +58 -0
- notionary/exceptions/search.py +57 -0
- notionary/file_upload/client.py +18 -30
- notionary/file_upload/models.py +7 -8
- notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
- notionary/http/client.py +204 -0
- notionary/http/models.py +50 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +73 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +226 -0
- notionary/page/content/markdown/nodes/__init__.py +52 -0
- notionary/page/content/markdown/nodes/audio.py +23 -0
- notionary/page/content/markdown/nodes/base.py +12 -0
- notionary/page/content/markdown/nodes/bookmark.py +25 -0
- notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
- notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
- notionary/page/content/markdown/nodes/callout.py +34 -0
- notionary/page/content/markdown/nodes/code.py +28 -0
- notionary/page/content/markdown/nodes/columns.py +69 -0
- notionary/page/content/markdown/nodes/container.py +64 -0
- notionary/page/content/markdown/nodes/divider.py +14 -0
- notionary/page/content/markdown/nodes/embed.py +23 -0
- notionary/page/content/markdown/nodes/equation.py +19 -0
- notionary/page/content/markdown/nodes/file.py +23 -0
- notionary/page/content/markdown/nodes/heading.py +36 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +38 -0
- notionary/page/content/markdown/nodes/paragraph.py +14 -0
- notionary/page/content/markdown/nodes/pdf.py +23 -0
- notionary/page/content/markdown/nodes/quote.py +27 -0
- notionary/page/content/markdown/nodes/space.py +14 -0
- notionary/page/content/markdown/nodes/table.py +45 -0
- notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
- notionary/page/content/markdown/nodes/todo.py +38 -0
- notionary/page/content/markdown/nodes/toggle.py +27 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +126 -0
- notionary/page/content/parser/factory.py +210 -0
- notionary/page/content/parser/parsers/__init__.py +58 -0
- notionary/page/content/parser/parsers/audio.py +40 -0
- notionary/page/content/parser/parsers/base.py +30 -0
- notionary/page/content/parser/parsers/bookmark.py +33 -0
- notionary/page/content/parser/parsers/breadcrumb.py +33 -0
- notionary/page/content/parser/parsers/bulleted_list.py +85 -0
- notionary/page/content/parser/parsers/callout.py +100 -0
- notionary/page/content/parser/parsers/caption.py +55 -0
- notionary/page/content/parser/parsers/code.py +81 -0
- notionary/page/content/parser/parsers/column.py +76 -0
- notionary/page/content/parser/parsers/column_list.py +81 -0
- notionary/page/content/parser/parsers/divider.py +33 -0
- notionary/page/content/parser/parsers/embed.py +33 -0
- notionary/page/content/parser/parsers/equation.py +65 -0
- notionary/page/content/parser/parsers/file.py +42 -0
- notionary/page/content/parser/parsers/heading.py +115 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +89 -0
- notionary/page/content/parser/parsers/paragraph.py +37 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +125 -0
- notionary/page/content/parser/parsers/space.py +41 -0
- notionary/page/content/parser/parsers/table.py +144 -0
- notionary/page/content/parser/parsers/table_of_contents.py +32 -0
- notionary/page/content/parser/parsers/todo.py +96 -0
- notionary/page/content/parser/parsers/toggle.py +70 -0
- notionary/page/content/parser/parsers/video.py +42 -0
- notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
- notionary/page/content/parser/post_processing/port.py +9 -0
- notionary/page/content/parser/post_processing/service.py +16 -0
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +78 -0
- notionary/page/content/renderer/context.py +51 -0
- notionary/page/content/renderer/factory.py +231 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
- notionary/page/content/renderer/post_processing/port.py +7 -0
- notionary/page/content/renderer/post_processing/service.py +15 -0
- notionary/page/content/renderer/renderers/__init__.py +55 -0
- notionary/page/content/renderer/renderers/audio.py +31 -0
- notionary/page/content/renderer/renderers/base.py +31 -0
- notionary/page/content/renderer/renderers/bookmark.py +25 -0
- notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
- notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
- notionary/page/content/renderer/renderers/callout.py +50 -0
- notionary/page/content/renderer/renderers/captioned_block.py +58 -0
- notionary/page/content/renderer/renderers/code.py +34 -0
- notionary/page/content/renderer/renderers/column.py +53 -0
- notionary/page/content/renderer/renderers/column_list.py +44 -0
- notionary/page/content/renderer/renderers/divider.py +22 -0
- notionary/page/content/renderer/renderers/embed.py +25 -0
- notionary/page/content/renderer/renderers/equation.py +37 -0
- notionary/page/content/renderer/renderers/fallback.py +24 -0
- notionary/page/content/renderer/renderers/file.py +40 -0
- notionary/page/content/renderer/renderers/heading.py +95 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +42 -0
- notionary/page/content/renderer/renderers/paragraph.py +40 -0
- notionary/page/content/renderer/renderers/pdf.py +31 -0
- notionary/page/content/renderer/renderers/quote.py +49 -0
- notionary/page/content/renderer/renderers/table.py +115 -0
- notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
- notionary/page/content/renderer/renderers/table_row.py +17 -0
- notionary/page/content/renderer/renderers/todo.py +56 -0
- notionary/page/content/renderer/renderers/toggle.py +52 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +68 -0
- notionary/page/content/syntax/__init__.py +4 -0
- notionary/page/content/syntax/grammar.py +10 -0
- notionary/page/content/syntax/models.py +66 -0
- notionary/page/content/syntax/registry.py +393 -0
- notionary/page/page_context.py +7 -16
- notionary/page/page_http_client.py +15 -0
- notionary/page/page_metadata_update_client.py +19 -0
- notionary/page/properties/client.py +144 -0
- notionary/page/properties/factory.py +26 -0
- notionary/page/properties/models.py +308 -0
- notionary/page/properties/service.py +261 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +225 -0
- notionary/shared/entity/client.py +29 -0
- notionary/shared/entity/dto_parsers.py +53 -0
- notionary/shared/entity/entity_metadata_update_client.py +41 -0
- notionary/shared/entity/schemas.py +45 -0
- notionary/shared/entity/service.py +171 -0
- notionary/shared/models/cover.py +20 -0
- notionary/shared/models/file.py +21 -0
- notionary/shared/models/icon.py +28 -0
- notionary/shared/models/parent.py +41 -0
- notionary/shared/properties/type.py +30 -0
- notionary/shared/typings.py +3 -0
- notionary/user/__init__.py +4 -8
- notionary/user/base.py +138 -0
- notionary/user/bot.py +70 -0
- notionary/user/client.py +22 -111
- notionary/user/person.py +41 -0
- notionary/user/schemas.py +67 -0
- notionary/user/service.py +65 -0
- notionary/utils/date.py +51 -0
- notionary/utils/decorators.py +122 -0
- notionary/utils/fuzzy.py +68 -0
- notionary/utils/mixins/logging.py +58 -0
- notionary/utils/pagination.py +100 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +4 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/__init__.py +3 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +61 -0
- notionary/workspace/query/service.py +100 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- notionary-0.3.0.dist-info/METADATA +201 -0
- notionary-0.3.0.dist-info/RECORD +209 -0
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- notionary/blocks/_bootstrap.py +0 -271
- notionary/blocks/audio/__init__.py +0 -11
- notionary/blocks/audio/audio_element.py +0 -158
- notionary/blocks/audio/audio_markdown_node.py +0 -24
- notionary/blocks/audio/audio_models.py +0 -10
- notionary/blocks/base_block_element.py +0 -42
- notionary/blocks/bookmark/__init__.py +0 -12
- notionary/blocks/bookmark/bookmark_element.py +0 -83
- notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
- notionary/blocks/bookmark/bookmark_models.py +0 -15
- notionary/blocks/breadcrumbs/__init__.py +0 -15
- notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
- notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
- notionary/blocks/bulleted_list/__init__.py +0 -15
- notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
- notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
- notionary/blocks/callout/__init__.py +0 -12
- notionary/blocks/callout/callout_element.py +0 -99
- notionary/blocks/callout/callout_markdown_node.py +0 -19
- notionary/blocks/callout/callout_models.py +0 -33
- notionary/blocks/child_database/__init__.py +0 -14
- notionary/blocks/child_database/child_database_element.py +0 -59
- notionary/blocks/child_database/child_database_models.py +0 -12
- notionary/blocks/child_page/__init__.py +0 -9
- notionary/blocks/child_page/child_page_element.py +0 -94
- notionary/blocks/child_page/child_page_models.py +0 -12
- notionary/blocks/code/__init__.py +0 -11
- notionary/blocks/code/code_element.py +0 -149
- notionary/blocks/code/code_markdown_node.py +0 -80
- notionary/blocks/code/code_models.py +0 -94
- notionary/blocks/column/__init__.py +0 -25
- notionary/blocks/column/column_element.py +0 -65
- notionary/blocks/column/column_list_element.py +0 -52
- notionary/blocks/column/column_list_markdown_node.py +0 -34
- notionary/blocks/column/column_markdown_node.py +0 -42
- notionary/blocks/column/column_models.py +0 -26
- notionary/blocks/divider/__init__.py +0 -12
- notionary/blocks/divider/divider_element.py +0 -41
- notionary/blocks/divider/divider_markdown_node.py +0 -11
- notionary/blocks/divider/divider_models.py +0 -12
- notionary/blocks/embed/__init__.py +0 -12
- notionary/blocks/embed/embed_element.py +0 -98
- notionary/blocks/embed/embed_markdown_node.py +0 -19
- notionary/blocks/embed/embed_models.py +0 -14
- notionary/blocks/equation/__init__.py +0 -13
- notionary/blocks/equation/equation_element.py +0 -133
- notionary/blocks/equation/equation_element_markdown_node.py +0 -23
- notionary/blocks/equation/equation_models.py +0 -11
- notionary/blocks/file/__init__.py +0 -23
- notionary/blocks/file/file_element.py +0 -133
- notionary/blocks/file/file_element_markdown_node.py +0 -24
- notionary/blocks/file/file_element_models.py +0 -39
- notionary/blocks/heading/__init__.py +0 -19
- notionary/blocks/heading/heading_element.py +0 -112
- notionary/blocks/heading/heading_markdown_node.py +0 -16
- notionary/blocks/heading/heading_models.py +0 -29
- notionary/blocks/image_block/__init__.py +0 -11
- notionary/blocks/image_block/image_element.py +0 -130
- notionary/blocks/image_block/image_markdown_node.py +0 -25
- notionary/blocks/image_block/image_models.py +0 -10
- notionary/blocks/markdown/markdown_builder.py +0 -525
- notionary/blocks/markdown/markdown_document_model.py +0 -0
- notionary/blocks/markdown/markdown_node.py +0 -25
- notionary/blocks/mixins/captions/__init__.py +0 -4
- notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
- notionary/blocks/mixins/captions/caption_mixin.py +0 -92
- notionary/blocks/mixins/file_upload/__init__.py +0 -3
- notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
- notionary/blocks/models.py +0 -174
- notionary/blocks/numbered_list/__init__.py +0 -16
- notionary/blocks/numbered_list/numbered_list_element.py +0 -65
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
- notionary/blocks/numbered_list/numbered_list_models.py +0 -17
- notionary/blocks/paragraph/__init__.py +0 -15
- notionary/blocks/paragraph/paragraph_element.py +0 -58
- notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
- notionary/blocks/paragraph/paragraph_models.py +0 -16
- notionary/blocks/pdf/__init__.py +0 -11
- notionary/blocks/pdf/pdf_element.py +0 -146
- notionary/blocks/pdf/pdf_markdown_node.py +0 -24
- notionary/blocks/pdf/pdf_models.py +0 -11
- notionary/blocks/quote/__init__.py +0 -14
- notionary/blocks/quote/quote_element.py +0 -75
- notionary/blocks/quote/quote_markdown_node.py +0 -16
- notionary/blocks/quote/quote_models.py +0 -18
- notionary/blocks/registry/__init__.py +0 -3
- notionary/blocks/registry/block_registry.py +0 -150
- notionary/blocks/rich_text/__init__.py +0 -33
- notionary/blocks/rich_text/rich_text_models.py +0 -221
- notionary/blocks/rich_text/text_inline_formatter.py +0 -456
- notionary/blocks/syntax_prompt_builder.py +0 -137
- notionary/blocks/table/__init__.py +0 -19
- notionary/blocks/table/table_element.py +0 -225
- notionary/blocks/table/table_markdown_node.py +0 -42
- notionary/blocks/table/table_models.py +0 -28
- notionary/blocks/table_of_contents/__init__.py +0 -17
- notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
- notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
- notionary/blocks/todo/__init__.py +0 -12
- notionary/blocks/todo/todo_element.py +0 -81
- notionary/blocks/todo/todo_markdown_node.py +0 -21
- notionary/blocks/todo/todo_models.py +0 -18
- notionary/blocks/toggle/__init__.py +0 -12
- notionary/blocks/toggle/toggle_element.py +0 -112
- notionary/blocks/toggle/toggle_markdown_node.py +0 -31
- notionary/blocks/toggle/toggle_models.py +0 -17
- notionary/blocks/toggleable_heading/__init__.py +0 -11
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
- notionary/blocks/types.py +0 -130
- notionary/blocks/video/__init__.py +0 -11
- notionary/blocks/video/video_element.py +0 -187
- notionary/blocks/video/video_element_models.py +0 -10
- notionary/blocks/video/video_markdown_node.py +0 -26
- notionary/database/__init__.py +0 -4
- notionary/database/database.py +0 -480
- notionary/database/database_filter_builder.py +0 -173
- notionary/database/database_provider.py +0 -227
- notionary/database/exceptions.py +0 -13
- notionary/database/models.py +0 -337
- notionary/database/notion_database.py +0 -487
- notionary/file_upload/__init__.py +0 -7
- notionary/page/client.py +0 -124
- notionary/page/markdown_whitespace_processor.py +0 -129
- notionary/page/models.py +0 -322
- notionary/page/notion_page.py +0 -712
- notionary/page/page_content_deleting_service.py +0 -117
- notionary/page/page_content_writer.py +0 -80
- notionary/page/property_formatter.py +0 -99
- notionary/page/reader/handler/__init__.py +0 -19
- notionary/page/reader/handler/base_block_renderer.py +0 -44
- notionary/page/reader/handler/block_processing_context.py +0 -35
- notionary/page/reader/handler/block_rendering_context.py +0 -48
- notionary/page/reader/handler/column_list_renderer.py +0 -51
- notionary/page/reader/handler/column_renderer.py +0 -60
- notionary/page/reader/handler/equation_renderer.py +0 -0
- notionary/page/reader/handler/line_renderer.py +0 -73
- notionary/page/reader/handler/numbered_list_renderer.py +0 -85
- notionary/page/reader/handler/toggle_renderer.py +0 -69
- notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
- notionary/page/reader/page_content_retriever.py +0 -81
- notionary/page/search_filter_builder.py +0 -132
- notionary/page/utils.py +0 -60
- notionary/page/writer/handler/__init__.py +0 -24
- notionary/page/writer/handler/code_handler.py +0 -72
- notionary/page/writer/handler/column_handler.py +0 -141
- notionary/page/writer/handler/column_list_handler.py +0 -139
- notionary/page/writer/handler/equation_handler.py +0 -74
- notionary/page/writer/handler/line_handler.py +0 -35
- notionary/page/writer/handler/line_processing_context.py +0 -54
- notionary/page/writer/handler/regular_line_handler.py +0 -86
- notionary/page/writer/handler/table_handler.py +0 -66
- notionary/page/writer/handler/toggle_handler.py +0 -159
- notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
- notionary/page/writer/markdown_to_notion_converter.py +0 -139
- notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
- notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
- notionary/page/writer/notion_text_length_processor.py +0 -150
- notionary/schemas/__init__.py +0 -3
- notionary/schemas/base.py +0 -73
- notionary/shared/__init__.py +0 -3
- notionary/shared/name_to_id_resolver.py +0 -203
- notionary/telemetry/__init__.py +0 -19
- notionary/telemetry/service.py +0 -136
- notionary/telemetry/views.py +0 -73
- notionary/user/base_notion_user.py +0 -53
- notionary/user/models.py +0 -84
- notionary/user/notion_bot_user.py +0 -226
- notionary/user/notion_user.py +0 -255
- notionary/user/notion_user_manager.py +0 -101
- notionary/util/__init__.py +0 -15
- notionary/util/concurrency_limiter.py +0 -0
- notionary/util/factory_decorator.py +0 -0
- notionary/util/factory_only.py +0 -37
- notionary/util/fuzzy.py +0 -75
- notionary/util/logging_mixin.py +0 -59
- notionary/util/page_id_utils.py +0 -27
- notionary/util/singleton.py +0 -18
- notionary/util/singleton_metaclass.py +0 -22
- notionary/workspace.py +0 -105
- notionary-0.2.27.dist-info/METADATA +0 -270
- notionary-0.2.27.dist-info/RECORD +0 -202
- /notionary/{database → user}/factory.py +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DividerMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
|
|
11
|
+
@override
|
|
12
|
+
def to_markdown(self) -> str:
|
|
13
|
+
divider_syntax = self._syntax_registry.get_divider_syntax()
|
|
14
|
+
return divider_syntax.start_delimiter
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EmbedMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
embed_syntax = self._syntax_registry.get_embed_syntax()
|
|
22
|
+
base_markdown = f"{embed_syntax.start_delimiter}{self.url}{embed_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EquationMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, expression: str, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
self.expression = expression
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
def to_markdown(self) -> str:
|
|
14
|
+
expr = self.expression.strip()
|
|
15
|
+
equation_syntax = self._syntax_registry.get_equation_syntax()
|
|
16
|
+
if not expr:
|
|
17
|
+
return f"{equation_syntax.start_delimiter}{equation_syntax.end_delimiter}"
|
|
18
|
+
|
|
19
|
+
return f"{equation_syntax.start_delimiter}{expr}{equation_syntax.end_delimiter}"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
file_syntax = self._syntax_registry.get_file_syntax()
|
|
22
|
+
base_markdown = f"{file_syntax.start_delimiter}{self.url}{file_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HeadingMarkdownNode(ContainerNode):
|
|
9
|
+
MIN_LEVEL = 1
|
|
10
|
+
MAX_LEVEL = 3
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
text: str,
|
|
15
|
+
level: int = 1,
|
|
16
|
+
children: list[MarkdownNode] | None = None,
|
|
17
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
20
|
+
self.text = text
|
|
21
|
+
self.level = self._validate_level(level)
|
|
22
|
+
self.children = children or []
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
def to_markdown(self) -> str:
|
|
26
|
+
heading_prefix = self._get_heading_prefix()
|
|
27
|
+
result = f"{heading_prefix} {self.text}"
|
|
28
|
+
result += self.render_children()
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
def _validate_level(self, level: int) -> int:
|
|
32
|
+
return max(self.MIN_LEVEL, min(self.MAX_LEVEL, level))
|
|
33
|
+
|
|
34
|
+
def _get_heading_prefix(self) -> str:
|
|
35
|
+
delimiter = self._syntax_registry.get_heading_syntax().start_delimiter
|
|
36
|
+
return delimiter * self.level
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ImageMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
image_syntax = self._syntax_registry.get_image_syntax()
|
|
22
|
+
base_markdown = f"{image_syntax.start_delimiter}{self.url}{image_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CaptionMarkdownNodeMixin:
|
|
5
|
+
_syntax_registry: SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
def _append_caption_to_markdown(self, base_markdown: str, caption: str | None) -> str:
|
|
8
|
+
if not caption:
|
|
9
|
+
return base_markdown
|
|
10
|
+
|
|
11
|
+
caption_syntax = self._syntax_registry.get_caption_syntax()
|
|
12
|
+
return f"{base_markdown}\n{caption_syntax.start_delimiter} {caption}"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NumberedListMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
texts: list[str],
|
|
12
|
+
children: list[MarkdownNode | None] | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
):
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.texts = texts
|
|
17
|
+
self.children = children or []
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
list_items = [self._render_list_item(index, text) for index, text in enumerate(self.texts)]
|
|
22
|
+
return "\n".join(list_items)
|
|
23
|
+
|
|
24
|
+
def _render_list_item(self, index: int, text: str) -> str:
|
|
25
|
+
item_number = index + 1
|
|
26
|
+
item_line = f"{item_number}. {text}"
|
|
27
|
+
|
|
28
|
+
child = self._get_child_for_item(index)
|
|
29
|
+
if child:
|
|
30
|
+
child_content = self.render_child(child)
|
|
31
|
+
return f"{item_line}\n{child_content}"
|
|
32
|
+
|
|
33
|
+
return item_line
|
|
34
|
+
|
|
35
|
+
def _get_child_for_item(self, index: int) -> MarkdownNode | None:
|
|
36
|
+
if not self.children or index >= len(self.children):
|
|
37
|
+
return None
|
|
38
|
+
return self.children[index]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ParagraphMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, text: str, syntax_registry: SyntaxRegistry | None = None):
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
self.text = text
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
def to_markdown(self) -> str:
|
|
14
|
+
return self.text
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PdfMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
pdf_syntax = self._syntax_registry.get_pdf_syntax()
|
|
22
|
+
base_markdown = f"{pdf_syntax.start_delimiter}{self.url}{pdf_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QuoteMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
text: str,
|
|
12
|
+
children: list[MarkdownNode] | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.text = text
|
|
17
|
+
self.children = children or []
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
quote_delimiter = self._get_quote_delimiter()
|
|
22
|
+
result = f"{quote_delimiter}{self.text}"
|
|
23
|
+
result += self.render_children()
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
def _get_quote_delimiter(self) -> str:
|
|
27
|
+
return self._syntax_registry.get_quote_syntax().start_delimiter
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SpaceMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
|
|
11
|
+
@override
|
|
12
|
+
def to_markdown(self) -> str:
|
|
13
|
+
space_syntax = self._syntax_registry.get_space_syntax()
|
|
14
|
+
return space_syntax.start_delimiter
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TableMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(
|
|
9
|
+
self, headers: list[str], rows: list[list[str]], syntax_registry: SyntaxRegistry | None = None
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
12
|
+
self._validate_input(headers, rows)
|
|
13
|
+
self.headers = headers
|
|
14
|
+
self.rows = rows
|
|
15
|
+
|
|
16
|
+
def _validate_input(self, headers: list[str], rows: list[list[str]]) -> None:
|
|
17
|
+
if not headers:
|
|
18
|
+
raise ValueError("headers must not be empty")
|
|
19
|
+
if not all(isinstance(row, list) for row in rows):
|
|
20
|
+
raise ValueError("rows must be a list of lists")
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
def to_markdown(self) -> str:
|
|
24
|
+
header = self._build_header_row()
|
|
25
|
+
separator = self._build_separator_row()
|
|
26
|
+
data_rows = self._build_data_rows()
|
|
27
|
+
return "\n".join([header, separator, *data_rows])
|
|
28
|
+
|
|
29
|
+
def _build_header_row(self) -> str:
|
|
30
|
+
return self._format_row(self.headers)
|
|
31
|
+
|
|
32
|
+
def _format_row(self, cells: list[str]) -> str:
|
|
33
|
+
table_syntax = self._syntax_registry.get_table_syntax()
|
|
34
|
+
delimiter = table_syntax.start_delimiter
|
|
35
|
+
joined_cells = f" {delimiter} ".join(cells)
|
|
36
|
+
return f"{delimiter} {joined_cells} {delimiter}"
|
|
37
|
+
|
|
38
|
+
def _build_separator_row(self) -> str:
|
|
39
|
+
table_syntax = self._syntax_registry.get_table_syntax()
|
|
40
|
+
col_count = len(self.headers)
|
|
41
|
+
separators = [table_syntax.end_delimiter] * col_count
|
|
42
|
+
return self._format_row(separators)
|
|
43
|
+
|
|
44
|
+
def _build_data_rows(self) -> list[str]:
|
|
45
|
+
return [self._format_row(row) for row in self.rows]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TableOfContentsMarkdownNode(MarkdownNode):
|
|
8
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
9
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
10
|
+
|
|
11
|
+
@override
|
|
12
|
+
def to_markdown(self) -> str:
|
|
13
|
+
toc_syntax = self._syntax_registry.get_table_of_contents_syntax()
|
|
14
|
+
return toc_syntax.start_delimiter
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TodoMarkdownNode(ContainerNode):
|
|
9
|
+
VALID_MARKER = "-"
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
text: str,
|
|
14
|
+
checked: bool = False,
|
|
15
|
+
marker: str = "-",
|
|
16
|
+
children: list[MarkdownNode] | None = None,
|
|
17
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
18
|
+
):
|
|
19
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
20
|
+
self.text = text
|
|
21
|
+
self.checked = checked
|
|
22
|
+
self.marker = marker
|
|
23
|
+
self.children = children or []
|
|
24
|
+
|
|
25
|
+
@override
|
|
26
|
+
def to_markdown(self) -> str:
|
|
27
|
+
validated_marker = self._get_validated_marker()
|
|
28
|
+
checkbox_state = self._get_checkbox_state()
|
|
29
|
+
result = f"{validated_marker}{checkbox_state} {self.text}"
|
|
30
|
+
result += self.render_children()
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
def _get_validated_marker(self) -> str:
|
|
34
|
+
return self.marker if self.marker == self.VALID_MARKER else self.VALID_MARKER
|
|
35
|
+
|
|
36
|
+
def _get_checkbox_state(self) -> str:
|
|
37
|
+
todo_syntax = self._syntax_registry.get_todo_syntax()
|
|
38
|
+
return todo_syntax.end_delimiter if self.checked else todo_syntax.start_delimiter
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.container import ContainerNode
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ToggleMarkdownNode(ContainerNode):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
title: str,
|
|
12
|
+
children: list[MarkdownNode] | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
):
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.title = title
|
|
17
|
+
self.children = children or []
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
toggle_delimiter = self._get_toggle_delimiter()
|
|
22
|
+
result = f"{toggle_delimiter} {self.title}"
|
|
23
|
+
result += self.render_children()
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
def _get_toggle_delimiter(self) -> str:
|
|
27
|
+
return self._syntax_registry.get_toggle_syntax().start_delimiter
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.page.content.markdown.nodes.base import MarkdownNode
|
|
4
|
+
from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
|
|
5
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VideoMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
url: str,
|
|
12
|
+
caption: str | None = None,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
) -> None:
|
|
15
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
16
|
+
self.url = url
|
|
17
|
+
self.caption = caption
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def to_markdown(self) -> str:
|
|
21
|
+
video_syntax = self._syntax_registry.get_video_syntax()
|
|
22
|
+
base_markdown = f"{video_syntax.start_delimiter}{self.url}{video_syntax.end_delimiter}"
|
|
23
|
+
return self._append_caption_to_markdown(base_markdown, self.caption)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
|
|
5
|
+
from notionary.blocks.schemas import BlockCreatePayload
|
|
6
|
+
from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ParentBlockContext:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
block: BlockCreatePayload,
|
|
13
|
+
child_lines: list[str],
|
|
14
|
+
child_blocks: list[BlockCreatePayload] | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
self.block = block
|
|
17
|
+
self.child_lines = child_lines
|
|
18
|
+
self.child_blocks = child_blocks if child_blocks is not None else []
|
|
19
|
+
|
|
20
|
+
def add_child_line(self, content: str) -> None:
|
|
21
|
+
self.child_lines.append(content)
|
|
22
|
+
|
|
23
|
+
def add_child_block(self, block: BlockCreatePayload) -> None:
|
|
24
|
+
self.child_blocks.append(block)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_ParseChildrenCallback = Callable[[str], Awaitable[list[BlockCreatePayload]]]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BlockParsingContext:
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
line: str,
|
|
34
|
+
result_blocks: list[BlockCreatePayload],
|
|
35
|
+
parent_stack: list[ParentBlockContext],
|
|
36
|
+
parse_children_callback: _ParseChildrenCallback | None = None,
|
|
37
|
+
all_lines: list[str] | None = None,
|
|
38
|
+
current_line_index: int | None = None,
|
|
39
|
+
lines_consumed: int = 0,
|
|
40
|
+
is_previous_line_empty: bool = False,
|
|
41
|
+
markdown_grammar: MarkdownGrammar | None = None,
|
|
42
|
+
) -> None:
|
|
43
|
+
self.line = line
|
|
44
|
+
self.result_blocks = result_blocks
|
|
45
|
+
self.parent_stack = parent_stack
|
|
46
|
+
self.parse_children_callback = parse_children_callback
|
|
47
|
+
self.all_lines = all_lines
|
|
48
|
+
self.current_line_index = current_line_index
|
|
49
|
+
self.lines_consumed = lines_consumed
|
|
50
|
+
self.is_previous_line_empty = is_previous_line_empty
|
|
51
|
+
markdown_grammar = markdown_grammar or MarkdownGrammar()
|
|
52
|
+
self._spaces_per_nesting_level = markdown_grammar.spaces_per_nesting_level
|
|
53
|
+
|
|
54
|
+
async def parse_nested_markdown(self, text: str) -> list[BlockCreatePayload]:
|
|
55
|
+
if not self._can_parse_children(text):
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
return await self.parse_children_callback(text)
|
|
59
|
+
|
|
60
|
+
def _can_parse_children(self, text: str) -> bool:
|
|
61
|
+
return self.parse_children_callback is not None and bool(text)
|
|
62
|
+
|
|
63
|
+
def get_remaining_lines(self) -> list[str]:
|
|
64
|
+
if not self._has_remaining_lines():
|
|
65
|
+
return []
|
|
66
|
+
return self.all_lines[self.current_line_index + 1 :]
|
|
67
|
+
|
|
68
|
+
def _has_remaining_lines(self) -> bool:
|
|
69
|
+
return self.all_lines is not None and self.current_line_index is not None
|
|
70
|
+
|
|
71
|
+
def is_inside_parent_context(self) -> bool:
|
|
72
|
+
return len(self.parent_stack) > 0
|
|
73
|
+
|
|
74
|
+
def get_line_indentation_level(self, line: str | None = None) -> int:
|
|
75
|
+
target_line = self._get_target_line(line)
|
|
76
|
+
leading_spaces = self._count_leading_spaces(target_line)
|
|
77
|
+
return self._calculate_indentation_level(leading_spaces)
|
|
78
|
+
|
|
79
|
+
def _get_target_line(self, line: str | None) -> str:
|
|
80
|
+
return line if line is not None else self.line
|
|
81
|
+
|
|
82
|
+
def _count_leading_spaces(self, line: str) -> int:
|
|
83
|
+
return len(line) - len(line.lstrip())
|
|
84
|
+
|
|
85
|
+
def _calculate_indentation_level(self, leading_spaces: int) -> int:
|
|
86
|
+
return leading_spaces // self._spaces_per_nesting_level
|
|
87
|
+
|
|
88
|
+
def collect_indented_child_lines(self, parent_indent_level: int) -> list[str]:
|
|
89
|
+
child_lines = []
|
|
90
|
+
expected_child_indent = parent_indent_level + 1
|
|
91
|
+
|
|
92
|
+
for line in self.get_remaining_lines():
|
|
93
|
+
if self._should_include_line_as_child(line, expected_child_indent):
|
|
94
|
+
child_lines.append(line)
|
|
95
|
+
else:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
return child_lines
|
|
99
|
+
|
|
100
|
+
def _should_include_line_as_child(self, line: str, expected_indent: int) -> bool:
|
|
101
|
+
if self._is_empty_line(line):
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
line_indent = self.get_line_indentation_level(line)
|
|
105
|
+
return line_indent >= expected_indent
|
|
106
|
+
|
|
107
|
+
def _is_empty_line(self, line: str) -> bool:
|
|
108
|
+
return not line.strip()
|
|
109
|
+
|
|
110
|
+
def strip_indentation_level(self, lines: list[str], levels: int = 1) -> list[str]:
|
|
111
|
+
return [self._strip_line_indentation(line, levels) for line in lines]
|
|
112
|
+
|
|
113
|
+
def _strip_line_indentation(self, line: str, levels: int) -> str:
|
|
114
|
+
if self._is_empty_line(line):
|
|
115
|
+
return line
|
|
116
|
+
|
|
117
|
+
spaces_to_remove = self._calculate_spaces_to_remove(levels)
|
|
118
|
+
return self._remove_leading_spaces(line, spaces_to_remove)
|
|
119
|
+
|
|
120
|
+
def _calculate_spaces_to_remove(self, levels: int) -> int:
|
|
121
|
+
return self._spaces_per_nesting_level * levels
|
|
122
|
+
|
|
123
|
+
def _remove_leading_spaces(self, line: str, spaces: int) -> str:
|
|
124
|
+
if len(line) < spaces:
|
|
125
|
+
return line
|
|
126
|
+
return line[spaces:]
|