From 1175173c0d8f6787bf6b789b8e5f6e4cd0040411 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 8 Feb 2015 21:26:24 +0000 Subject: Handle nested lists in markdown writer --- mammoth/writers/markdown.py | 66 +++++++++++++++++++++++++++++++---------- tests/writers/markdown_tests.py | 24 +++++++++++++++ 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/mammoth/writers/markdown.py b/mammoth/writers/markdown.py index 948e197..9424d45 100644 --- a/mammoth/writers/markdown.py +++ b/mammoth/writers/markdown.py @@ -4,25 +4,35 @@ import re class _WriterOutput(object): - def __init__(self, start, end, anchor_position=None, markdown_state=None): + def __init__(self, start, end, anchor_position=None): + if not callable(end): + end_output = end + end = lambda: end_output + self.start = start self.end = end self.anchor_position = anchor_position - self.markdown_state = markdown_state class _MarkdownState(object): - def __init__(self, list_state=None): + def __init__(self): + self._list_state_stack = [] + self.list_state = None + self.list_item_has_closed = False + + def update_list_state(self, list_state): + self._list_state_stack.append(self.list_state) self.list_state = list_state - def updated_list_state(self, list_state): - return _MarkdownState(list_state) + def pop_list_state(self): + self.list_state = self._list_state_stack.pop() class _MarkdownListState(object): - def __init__(self, ordered): + def __init__(self, ordered, indentation): self.ordered = ordered self.count = 0 + self.indentation = indentation def _symmetric_wrapped(end): @@ -60,15 +70,33 @@ def _image(attributes, markdown_state): def _list(ordered): def call(attributes, markdown_state): - list_state = _MarkdownListState(ordered=ordered) - markdown_state = markdown_state.updated_list_state(list_state) - return _WriterOutput("", "\n", markdown_state=markdown_state) + if markdown_state.list_state is None: + start = "" + end_text = "\n" + indentation = 0 + else: + start = "\n" + end_text = "" + indentation = markdown_state.list_state.indentation + 1 + + def end(): + markdown_state.pop_list_state() + return end_text + + markdown_state.update_list_state(_MarkdownListState( + ordered=ordered, + indentation=indentation, + )) + + return _WriterOutput(start, end) return call def _list_item(attributes, markdown_state): - list_state = markdown_state.list_state or _MarkdownListState(ordered=False) + markdown_state.list_item_has_closed = False + + list_state = markdown_state.list_state or _MarkdownListState(ordered=False, indentation=0) list_state.count += 1 if list_state.ordered: @@ -76,7 +104,14 @@ def _list_item(attributes, markdown_state): else: bullet = "-" - return _WriterOutput(bullet + " ", "\n") + def end(): + if markdown_state.list_item_has_closed: + return "" + else: + markdown_state.list_item_has_closed = True + return "\n" + + return _WriterOutput(("\t" * list_state.indentation) + bullet + " ", end) def _init_writers(): @@ -117,9 +152,7 @@ class MarkdownWriter(object): attributes = {} output = _writers.get(name, _default_writer)(attributes, self._markdown_state) - self._element_stack.append((output.end, self._markdown_state)) - if output.markdown_state is not None: - self._markdown_state = output.markdown_state + self._element_stack.append(output.end) anchor_before_start = output.anchor_position == "before" if anchor_before_start: @@ -133,8 +166,9 @@ class MarkdownWriter(object): def end(self, name): - end, markdown_state = self._element_stack.pop() - self._fragments.append(end) + end = self._element_stack.pop() + output = end() + self._fragments.append(output) def self_closing(self, name, attributes=None): self.start(name, attributes) diff --git a/tests/writers/markdown_tests.py b/tests/writers/markdown_tests.py index 05db8ff..27d6d56 100644 --- a/tests/writers/markdown_tests.py +++ b/tests/writers/markdown_tests.py @@ -175,6 +175,30 @@ def ul_element_is_written_as_unordered_list_using_hyphens_as_bullets(): writer.end("li") writer.end("ul") assert_equal("- Fruit\n- Condiments\n\n", writer.as_string()) + + +@istest +def numbering_is_separate_for_nested_list_and_parent_list(): + writer = _create_writer() + writer.start("ol") + + writer.start("li") + writer.text("Fruit") + writer.start("ol") + writer.start("li") + writer.text("Apple") + writer.end("li") + writer.start("li") + writer.text("Banana") + writer.end("li") + writer.end("ol") + writer.end("li") + + writer.start("li") + writer.text("Condiments") + writer.end("li") + writer.end("ol") + assert_equal("1. Fruit\n\t1. Apple\n\t2. Banana\n2. Condiments\n\n", writer.as_string()) -- cgit v1.2.3