forked from kenkeiras/org-rw
Merge branch 'dev/render-as-dom' into develop
This commit is contained in:
commit
99e44fd8b2
@ -27,7 +27,7 @@ class PropertyNode:
|
|||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{{{}: {}}".format(self.key, self.value)
|
return "{{{}: {}}}".format(self.key, self.value)
|
||||||
|
|
||||||
|
|
||||||
class ListGroupNode:
|
class ListGroupNode:
|
||||||
@ -89,11 +89,12 @@ class BlockNode:
|
|||||||
|
|
||||||
|
|
||||||
class CodeBlock(BlockNode):
|
class CodeBlock(BlockNode):
|
||||||
def __init__(self, header, subtype):
|
def __init__(self, header, subtype, arguments):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.header = header
|
self.header = header
|
||||||
self.lines = None
|
self.lines = None
|
||||||
self.subtype = subtype
|
self.subtype = subtype
|
||||||
|
self.arguments = arguments
|
||||||
|
|
||||||
def set_lines(self, lines):
|
def set_lines(self, lines):
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
|
304
org_rw/org_rw.py
304
org_rw/org_rw.py
@ -92,10 +92,10 @@ LIST_ITEM_RE = re.compile(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Org-Babel
|
# Org-Babel
|
||||||
BEGIN_BLOCK_RE = re.compile(r"^\s*#\+BEGIN_(?P<subtype>[^ ]+)(?P<content>.*)$", re.I)
|
BEGIN_BLOCK_RE = re.compile(r"^\s*#\+BEGIN_(?P<subtype>[^ ]+)(?P<arguments>.*)$", re.I)
|
||||||
END_BLOCK_RE = re.compile(r"^\s*#\+END_(?P<subtype>[^ ]+)\s*$", re.I)
|
END_BLOCK_RE = re.compile(r"^\s*#\+END_(?P<subtype>[^ ]+)\s*$", re.I)
|
||||||
RESULTS_DRAWER_RE = re.compile(r"^\s*:results:\s*$", re.I)
|
RESULTS_DRAWER_RE = re.compile(r"^\s*:results:\s*$", re.I)
|
||||||
CodeSnippet = collections.namedtuple("CodeSnippet", ("name", "content", "result"))
|
CodeSnippet = collections.namedtuple("CodeSnippet", ("name", "content", "result", "arguments"))
|
||||||
|
|
||||||
# Groupings
|
# Groupings
|
||||||
NON_FINISHED_GROUPS = (type(None), dom.ListGroupNode, dom.ResultsDrawerNode, dom.PropertyDrawerNode)
|
NON_FINISHED_GROUPS = (type(None), dom.ListGroupNode, dom.ResultsDrawerNode, dom.PropertyDrawerNode)
|
||||||
@ -154,6 +154,26 @@ class RangeInRaw:
|
|||||||
contents.insert(start_idx + i + 1, element)
|
contents.insert(start_idx + i + 1, element)
|
||||||
|
|
||||||
|
|
||||||
|
def unescape_block_lines(lines: str) -> str:
|
||||||
|
"""
|
||||||
|
Remove leading ',' from block_lines if they escape `*` characters.
|
||||||
|
"""
|
||||||
|
i = 0
|
||||||
|
lines = lines.split('\n')
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i]
|
||||||
|
if (line.lstrip(' ').startswith(',')
|
||||||
|
and line.lstrip(' ,').startswith('*')
|
||||||
|
):
|
||||||
|
# Remove leading ','
|
||||||
|
lead_pos = line.index(',')
|
||||||
|
line = line[:lead_pos] + line[lead_pos + 1:]
|
||||||
|
lines[i] = line
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
def get_links_from_content(content):
|
def get_links_from_content(content):
|
||||||
in_link = False
|
in_link = False
|
||||||
in_description = False
|
in_description = False
|
||||||
@ -356,7 +376,7 @@ class Headline:
|
|||||||
end = line.linenum
|
end = line.linenum
|
||||||
|
|
||||||
lines = self.get_lines_between(start + 1, end)
|
lines = self.get_lines_between(start + 1, end)
|
||||||
contents = "\n".join(lines)
|
contents = unescape_block_lines("\n".join(lines))
|
||||||
if contents.endswith("\n"):
|
if contents.endswith("\n"):
|
||||||
# This is not ideal, but to avoid having to do this maybe
|
# This is not ideal, but to avoid having to do this maybe
|
||||||
# the content parsing must be re-thinked
|
# the content parsing must be re-thinked
|
||||||
@ -376,18 +396,32 @@ class Headline:
|
|||||||
current_node.append(dom.PropertyNode(line.key, line.value))
|
current_node.append(dom.PropertyNode(line.key, line.value))
|
||||||
|
|
||||||
elif isinstance(line, Text):
|
elif isinstance(line, Text):
|
||||||
if isinstance(current_node, dom.BlockNode):
|
tree_up = list(indentation_tree)
|
||||||
current_node.append(dom.Text(line))
|
while len(tree_up) > 0:
|
||||||
elif isinstance(current_node, dom.DrawerNode):
|
node = tree_up[-1]
|
||||||
current_node.append(dom.Text(line))
|
if (isinstance(node, dom.BlockNode)
|
||||||
|
or isinstance(node, dom.DrawerNode)
|
||||||
|
):
|
||||||
|
node.append(dom.Text(line))
|
||||||
|
current_node = node
|
||||||
|
contents = []
|
||||||
|
break
|
||||||
|
elif ((not isinstance(node, dom.TableNode)) and
|
||||||
|
(type(node) not in NON_FINISHED_GROUPS)
|
||||||
|
):
|
||||||
|
raise NotImplementedError('Not implemented node type: {} (headline_id={}, line={}, doc={})'.format(
|
||||||
|
node,
|
||||||
|
self.id,
|
||||||
|
line.linenum,
|
||||||
|
self.doc.path,
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
tree_up.pop(-1)
|
||||||
else:
|
else:
|
||||||
if isinstance(current_node, dom.TableNode):
|
|
||||||
pass # No problem here
|
|
||||||
elif type(current_node) not in NON_FINISHED_GROUPS:
|
|
||||||
raise NotImplementedError('Not implemented node type: {}'.format(current_node))
|
|
||||||
current_node = None
|
current_node = None
|
||||||
contents = []
|
contents = []
|
||||||
tree.append(dom.Text(text_to_dom(line.contents, line)))
|
tree.append(dom.Text(text_to_dom(line.contents, line)))
|
||||||
|
indentation_tree = tree_up
|
||||||
|
|
||||||
elif isinstance(line, ListItem):
|
elif isinstance(line, ListItem):
|
||||||
if (current_node is None
|
if (current_node is None
|
||||||
@ -395,9 +429,12 @@ class Headline:
|
|||||||
or isinstance(current_node, dom.BlockNode)
|
or isinstance(current_node, dom.BlockNode)
|
||||||
or isinstance(current_node, dom.DrawerNode)
|
or isinstance(current_node, dom.DrawerNode)
|
||||||
):
|
):
|
||||||
|
was_node = current_node
|
||||||
current_node = dom.ListGroupNode()
|
current_node = dom.ListGroupNode()
|
||||||
if current_node is None:
|
if was_node is None:
|
||||||
tree.append(current_node)
|
tree.append(current_node)
|
||||||
|
else:
|
||||||
|
was_node.append(current_node)
|
||||||
indentation_tree.append(current_node)
|
indentation_tree.append(current_node)
|
||||||
if not isinstance(current_node, dom.ListGroupNode):
|
if not isinstance(current_node, dom.ListGroupNode):
|
||||||
if not isinstance(current_node, dom.ListGroupNode):
|
if not isinstance(current_node, dom.ListGroupNode):
|
||||||
@ -420,18 +457,22 @@ class Headline:
|
|||||||
current_node = sublist
|
current_node = sublist
|
||||||
indentation_tree.append(current_node)
|
indentation_tree.append(current_node)
|
||||||
|
|
||||||
while len(indentation_tree) > 0 and (
|
while len(indentation_tree) > 0:
|
||||||
(len(indentation_tree[-1].children) > 0)
|
list_children = [
|
||||||
and len(
|
|
||||||
[
|
|
||||||
c
|
c
|
||||||
for c in indentation_tree[-1].children
|
for c in indentation_tree[-1].children
|
||||||
if isinstance(c, dom.ListItem)
|
if isinstance(c, dom.ListItem)
|
||||||
][-1].orig.indentation
|
]
|
||||||
)
|
|
||||||
> len(line.indentation)
|
if (len(list_children) == 0):
|
||||||
):
|
break
|
||||||
rem = indentation_tree.pop()
|
if ((len(list_children[-1].orig.indentation)
|
||||||
|
<= len(line.indentation))):
|
||||||
|
# No more breaking out of lists, it's indentation
|
||||||
|
# is less than ours
|
||||||
|
break
|
||||||
|
|
||||||
|
rem = indentation_tree.pop(-1)
|
||||||
if len(indentation_tree) == 0:
|
if len(indentation_tree) == 0:
|
||||||
indentation_tree.append(rem)
|
indentation_tree.append(rem)
|
||||||
current_node = rem
|
current_node = rem
|
||||||
@ -448,9 +489,15 @@ class Headline:
|
|||||||
tree.append(current_node)
|
tree.append(current_node)
|
||||||
# TODO: Allow indentation of this element inside others
|
# TODO: Allow indentation of this element inside others
|
||||||
indentation_tree = [current_node]
|
indentation_tree = [current_node]
|
||||||
if not isinstance(current_node, dom.TableNode):
|
elif not isinstance(current_node, dom.TableNode):
|
||||||
if not isinstance(current_node, dom.TableNode):
|
if isinstance(current_node, dom.ListGroupNode):
|
||||||
logging.warning("Expected a {}, found: {} on line {}".format(dom.TableNode, current_node, line.linenum))
|
# As an item inside a list
|
||||||
|
list_node = current_node
|
||||||
|
current_node = dom.TableNode()
|
||||||
|
list_node.append(current_node)
|
||||||
|
indentation_tree.append(current_node)
|
||||||
|
else:
|
||||||
|
logging.debug("Expected a {}, found: {} on line {}".format(dom.TableNode, current_node, line.linenum))
|
||||||
# This can happen. Frequently inside a LogDrawer
|
# This can happen. Frequently inside a LogDrawer
|
||||||
|
|
||||||
if len(line.cells) > 0 and len(line.cells[0]) > 0 and line.cells[0][0] == '-':
|
if len(line.cells) > 0 and len(line.cells[0]) > 0 and line.cells[0][0] == '-':
|
||||||
@ -464,7 +511,7 @@ class Headline:
|
|||||||
and line.delimiter_type == DelimiterLineType.BEGIN_BLOCK
|
and line.delimiter_type == DelimiterLineType.BEGIN_BLOCK
|
||||||
):
|
):
|
||||||
assert type(current_node) in NON_FINISHED_GROUPS
|
assert type(current_node) in NON_FINISHED_GROUPS
|
||||||
current_node = dom.CodeBlock(line, line.type_data.subtype)
|
current_node = dom.CodeBlock(line, line.type_data.subtype, line.arguments)
|
||||||
|
|
||||||
elif isinstance(line, Keyword):
|
elif isinstance(line, Keyword):
|
||||||
logging.warning("Keywords not implemented on `as_dom()`")
|
logging.warning("Keywords not implemented on `as_dom()`")
|
||||||
@ -512,8 +559,6 @@ class Headline:
|
|||||||
tree_up.pop(-1)
|
tree_up.pop(-1)
|
||||||
else:
|
else:
|
||||||
raise Exception('Unexpected node ({}) on headline (id={}), line {}'.format(current_node, self.id, linenum))
|
raise Exception('Unexpected node ({}) on headline (id={}), line {}'.format(current_node, self.id, linenum))
|
||||||
if self.id == 'd07fcf27-d6fc-41e3-a9d0-b2e2902aec23':
|
|
||||||
print("Found node:", current_node)
|
|
||||||
current_node = None
|
current_node = None
|
||||||
elif content.strip().upper() == ":RESULTS:":
|
elif content.strip().upper() == ":RESULTS:":
|
||||||
assert current_node is None
|
assert current_node is None
|
||||||
@ -534,14 +579,26 @@ class Headline:
|
|||||||
last_line = None
|
last_line = None
|
||||||
|
|
||||||
for li in self.list_items:
|
for li in self.list_items:
|
||||||
if last_line == li.linenum - 1:
|
if last_line is None:
|
||||||
|
lists.append([li])
|
||||||
|
else:
|
||||||
|
num_lines = li.linenum - (last_line + 1)
|
||||||
|
lines_between = ''.join(['\n' + l
|
||||||
|
for l in self.get_lines_between(last_line + 1, li.linenum)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only empty lines
|
||||||
|
if ((num_lines == lines_between.count('\n'))
|
||||||
|
and (len(lines_between.strip()) == 0)
|
||||||
|
):
|
||||||
lists[-1].append(li)
|
lists[-1].append(li)
|
||||||
else:
|
else:
|
||||||
lists.append([li])
|
lists.append([li])
|
||||||
|
|
||||||
last_line = li.linenum
|
last_line = li.linenum + sum(c.count('\n') for c in li.content)
|
||||||
return lists
|
return lists
|
||||||
|
|
||||||
|
# @DEPRECATED: use `get_lists`
|
||||||
def getLists(self):
|
def getLists(self):
|
||||||
return self.get_lists()
|
return self.get_lists()
|
||||||
|
|
||||||
@ -623,6 +680,9 @@ class Headline:
|
|||||||
else:
|
else:
|
||||||
return list(self.shallow_tags) + self.parent.tags
|
return list(self.shallow_tags) + self.parent.tags
|
||||||
|
|
||||||
|
def add_tag(self, tag: str):
|
||||||
|
self.shallow_tags.append(tag)
|
||||||
|
|
||||||
def get_property(self, name: str, default=None):
|
def get_property(self, name: str, default=None):
|
||||||
for prop in self.properties:
|
for prop in self.properties:
|
||||||
if prop.key == name:
|
if prop.key == name:
|
||||||
@ -676,18 +736,14 @@ class Headline:
|
|||||||
|
|
||||||
for lst in self.get_lists():
|
for lst in self.get_lists():
|
||||||
for item in lst:
|
for item in lst:
|
||||||
|
if item.tag:
|
||||||
|
yield from get_links_from_content(item.tag)
|
||||||
yield from get_links_from_content(item.content)
|
yield from get_links_from_content(item.content)
|
||||||
|
|
||||||
def get_lines_between(self, start, end):
|
def get_lines_between(self, start, end):
|
||||||
for line in self.contents:
|
for line in self.contents:
|
||||||
if start <= line.linenum < end:
|
if start <= line.linenum < end:
|
||||||
text = []
|
yield "".join(line.get_raw())
|
||||||
for item in line.contents:
|
|
||||||
if isinstance(item, str):
|
|
||||||
text.append(item)
|
|
||||||
elif isinstance(item, MarkerType):
|
|
||||||
text.append(ModeToMarker[item])
|
|
||||||
yield "".join(text)
|
|
||||||
|
|
||||||
def get_contents(self, format):
|
def get_contents(self, format):
|
||||||
if format == "raw":
|
if format == "raw":
|
||||||
@ -737,17 +793,19 @@ class Headline:
|
|||||||
inside_code = False
|
inside_code = False
|
||||||
|
|
||||||
sections = []
|
sections = []
|
||||||
|
arguments = None
|
||||||
|
|
||||||
for delimiter in self.delimiters:
|
for delimiter in self.delimiters:
|
||||||
if delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK and delimiter.type_data.subtype.lower() == "src":
|
if delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK and delimiter.type_data.subtype.lower() == "src":
|
||||||
line_start = delimiter.linenum
|
line_start = delimiter.linenum
|
||||||
inside_code = True
|
inside_code = True
|
||||||
|
arguments = delimiter.arguments
|
||||||
elif delimiter.delimiter_type == DelimiterLineType.END_BLOCK and delimiter.type_data.subtype.lower() == "src":
|
elif delimiter.delimiter_type == DelimiterLineType.END_BLOCK and delimiter.type_data.subtype.lower() == "src":
|
||||||
inside_code = False
|
inside_code = False
|
||||||
start, end = line_start, delimiter.linenum
|
start, end = line_start, delimiter.linenum
|
||||||
|
|
||||||
lines = self.get_lines_between(start + 1, end)
|
lines = self.get_lines_between(start + 1, end)
|
||||||
contents = "\n".join(lines)
|
contents = unescape_block_lines("\n".join(lines))
|
||||||
if contents.endswith("\n"):
|
if contents.endswith("\n"):
|
||||||
# This is not ideal, but to avoid having to do this maybe
|
# This is not ideal, but to avoid having to do this maybe
|
||||||
# the content parsing must be re-thinked
|
# the content parsing must be re-thinked
|
||||||
@ -758,8 +816,10 @@ class Headline:
|
|||||||
"line_first": start + 1,
|
"line_first": start + 1,
|
||||||
"line_last": end - 1,
|
"line_last": end - 1,
|
||||||
"content": contents,
|
"content": contents,
|
||||||
|
"arguments": arguments,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
arguments = None
|
||||||
line_start = None
|
line_start = None
|
||||||
|
|
||||||
for kword in self.keywords:
|
for kword in self.keywords:
|
||||||
@ -810,10 +870,40 @@ class Headline:
|
|||||||
name = None
|
name = None
|
||||||
content = section["content"]
|
content = section["content"]
|
||||||
code_result = section.get("result", None)
|
code_result = section.get("result", None)
|
||||||
results.append(CodeSnippet(name=name, content=content, result=code_result))
|
arguments = section.get("arguments", None)
|
||||||
|
results.append(CodeSnippet(name=name, content=content, result=code_result, arguments=arguments))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def create_headline_at_end(self) -> Headline:
|
||||||
|
headline = Headline(
|
||||||
|
start_line=1,
|
||||||
|
depth=self.depth + 1,
|
||||||
|
orig=None,
|
||||||
|
properties=[],
|
||||||
|
keywords=[],
|
||||||
|
priority_start=None,
|
||||||
|
priority=None,
|
||||||
|
title_start=None,
|
||||||
|
title="",
|
||||||
|
state="",
|
||||||
|
tags_start=None,
|
||||||
|
tags=[],
|
||||||
|
contents=[],
|
||||||
|
children=[],
|
||||||
|
structural=[],
|
||||||
|
delimiters=[],
|
||||||
|
list_items=[],
|
||||||
|
table_rows=[],
|
||||||
|
parent=self,
|
||||||
|
is_todo=False,
|
||||||
|
is_done=False,
|
||||||
|
spacing=" ",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.children.append(headline)
|
||||||
|
return headline
|
||||||
|
|
||||||
|
|
||||||
RawLine = collections.namedtuple("RawLine", ("linenum", "line"))
|
RawLine = collections.namedtuple("RawLine", ("linenum", "line"))
|
||||||
Keyword = collections.namedtuple(
|
Keyword = collections.namedtuple(
|
||||||
@ -823,22 +913,34 @@ Property = collections.namedtuple(
|
|||||||
"Property", ("linenum", "match", "key", "value", "options")
|
"Property", ("linenum", "match", "key", "value", "options")
|
||||||
)
|
)
|
||||||
|
|
||||||
ListItem = collections.namedtuple(
|
class ListItem:
|
||||||
"ListItem",
|
def __init__(self,
|
||||||
(
|
linenum, match,
|
||||||
"linenum",
|
indentation,
|
||||||
"match",
|
bullet, counter, counter_sep,
|
||||||
"indentation",
|
checkbox_indentation, checkbox_value,
|
||||||
"bullet",
|
tag_indentation, tag,
|
||||||
"counter",
|
content,
|
||||||
"counter_sep",
|
):
|
||||||
"checkbox_indentation",
|
self.linenum = linenum
|
||||||
"checkbox_value",
|
self.match = match
|
||||||
"tag_indentation",
|
self.indentation = indentation
|
||||||
"tag",
|
self.bullet = bullet
|
||||||
"content",
|
self.counter = counter
|
||||||
),
|
self.counter_sep = counter_sep
|
||||||
)
|
self.checkbox_indentation = checkbox_indentation
|
||||||
|
self.checkbox_value = checkbox_value
|
||||||
|
self.tag_indentation = tag_indentation
|
||||||
|
self.tag = tag
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text_start_pos(self):
|
||||||
|
return len(self.indentation) + 1 # Indentation + bullet
|
||||||
|
|
||||||
|
def append_line(self, line):
|
||||||
|
self.content += parse_content_block('\n' + line).contents
|
||||||
|
|
||||||
TableRow = collections.namedtuple(
|
TableRow = collections.namedtuple(
|
||||||
"TableRow",
|
"TableRow",
|
||||||
(
|
(
|
||||||
@ -952,7 +1054,7 @@ BlockDelimiterTypeData = collections.namedtuple(
|
|||||||
)
|
)
|
||||||
|
|
||||||
DelimiterLine = collections.namedtuple(
|
DelimiterLine = collections.namedtuple(
|
||||||
"DelimiterLine", ("linenum", "line", "delimiter_type", "type_data")
|
"DelimiterLine", ("linenum", "line", "delimiter_type", "type_data", "arguments")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1000,6 +1102,8 @@ def token_from_type(tok_type):
|
|||||||
|
|
||||||
class TimeRange:
|
class TimeRange:
|
||||||
def __init__(self, start_time: OrgTime, end_time: OrgTime):
|
def __init__(self, start_time: OrgTime, end_time: OrgTime):
|
||||||
|
assert start_time is not None
|
||||||
|
assert end_time is not None
|
||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.end_time = end_time
|
self.end_time = end_time
|
||||||
|
|
||||||
@ -1540,13 +1644,19 @@ def parse_contents(raw_contents: List[RawLine]):
|
|||||||
return [parse_content_block(block) for block in blocks]
|
return [parse_content_block(block) for block in blocks]
|
||||||
|
|
||||||
|
|
||||||
def parse_content_block(raw_contents: List[RawLine]):
|
def parse_content_block(raw_contents: Union[List[RawLine],str]):
|
||||||
contents_buff = []
|
contents_buff = []
|
||||||
|
if isinstance(raw_contents, str):
|
||||||
|
contents_buff.append(raw_contents)
|
||||||
|
else:
|
||||||
for line in raw_contents:
|
for line in raw_contents:
|
||||||
contents_buff.append(line.line)
|
contents_buff.append(line.line)
|
||||||
|
|
||||||
contents = "\n".join(contents_buff)
|
contents = "\n".join(contents_buff)
|
||||||
tokens = tokenize_contents(contents)
|
tokens = tokenize_contents(contents)
|
||||||
|
if isinstance(raw_contents, str):
|
||||||
|
current_line = None
|
||||||
|
else:
|
||||||
current_line = raw_contents[0].linenum
|
current_line = raw_contents[0].linenum
|
||||||
|
|
||||||
contents = []
|
contents = []
|
||||||
@ -1574,7 +1684,9 @@ def dump_contents(raw):
|
|||||||
|
|
||||||
elif isinstance(raw, ListItem):
|
elif isinstance(raw, ListItem):
|
||||||
bullet = raw.bullet if raw.bullet else raw.counter + raw.counter_sep
|
bullet = raw.bullet if raw.bullet else raw.counter + raw.counter_sep
|
||||||
content = token_list_to_raw(raw.content)
|
content_full = token_list_to_raw(raw.content)
|
||||||
|
content_lines = content_full.split('\n')
|
||||||
|
content = '\n'.join(content_lines)
|
||||||
checkbox = f"[{raw.checkbox_value}]" if raw.checkbox_value else ""
|
checkbox = f"[{raw.checkbox_value}]" if raw.checkbox_value else ""
|
||||||
tag = f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')}::" if raw.tag or raw.tag_indentation else ""
|
tag = f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')}::" if raw.tag or raw.tag_indentation else ""
|
||||||
return (
|
return (
|
||||||
@ -1804,7 +1916,12 @@ class OrgDoc:
|
|||||||
if headline.state:
|
if headline.state:
|
||||||
state = headline.state + " "
|
state = headline.state + " "
|
||||||
|
|
||||||
yield "*" * headline.depth + headline.spacing + state + token_list_to_raw(headline.title.contents) + tags
|
raw_title = token_list_to_raw(headline.title.contents)
|
||||||
|
tags_padding = ""
|
||||||
|
if not raw_title.endswith(" ") and tags:
|
||||||
|
tags_padding = " "
|
||||||
|
|
||||||
|
yield "*" * headline.depth + headline.spacing + state + raw_title + tags_padding + tags
|
||||||
|
|
||||||
planning = headline.get_planning_line()
|
planning = headline.get_planning_line()
|
||||||
if planning is not None:
|
if planning is not None:
|
||||||
@ -1963,19 +2080,19 @@ class OrgDocReader:
|
|||||||
|
|
||||||
def add_list_item_line(self, linenum: int, match: re.Match) -> int:
|
def add_list_item_line(self, linenum: int, match: re.Match) -> int:
|
||||||
li = ListItem(
|
li = ListItem(
|
||||||
linenum,
|
linenum=linenum,
|
||||||
match,
|
match=match,
|
||||||
match.group("indentation"),
|
indentation=match.group("indentation"),
|
||||||
match.group("bullet"),
|
bullet=match.group("bullet"),
|
||||||
match.group("counter"),
|
counter=match.group("counter"),
|
||||||
match.group("counter_sep"),
|
counter_sep=match.group("counter_sep"),
|
||||||
match.group("checkbox_indentation"),
|
checkbox_indentation=match.group("checkbox_indentation"),
|
||||||
match.group("checkbox_value"),
|
checkbox_value=match.group("checkbox_value"),
|
||||||
match.group("tag_indentation"),
|
tag_indentation=match.group("tag_indentation"),
|
||||||
parse_content_block(
|
tag=parse_content_block(
|
||||||
[RawLine(linenum=linenum, line=match.group("tag"))]
|
[RawLine(linenum=linenum, line=match.group("tag"))]
|
||||||
).contents if match.group("tag") else None,
|
).contents if match.group("tag") else None,
|
||||||
parse_content_block(
|
content=parse_content_block(
|
||||||
[RawLine(linenum=linenum, line=match.group("content"))]
|
[RawLine(linenum=linenum, line=match.group("content"))]
|
||||||
).contents,
|
).contents,
|
||||||
)
|
)
|
||||||
@ -1984,6 +2101,7 @@ class OrgDocReader:
|
|||||||
self.list_items.append(li)
|
self.list_items.append(li)
|
||||||
else:
|
else:
|
||||||
self.headline_hierarchy[-1]["list_items"].append(li)
|
self.headline_hierarchy[-1]["list_items"].append(li)
|
||||||
|
return li
|
||||||
|
|
||||||
def add_table_line(self, linenum: int, line: str) -> int:
|
def add_table_line(self, linenum: int, line: str) -> int:
|
||||||
chunks = line.split('|')
|
chunks = line.split('|')
|
||||||
@ -2033,7 +2151,7 @@ class OrgDocReader:
|
|||||||
|
|
||||||
def add_begin_block_line(self, linenum: int, match: re.Match):
|
def add_begin_block_line(self, linenum: int, match: re.Match):
|
||||||
line = DelimiterLine(linenum, match.group(0), DelimiterLineType.BEGIN_BLOCK,
|
line = DelimiterLine(linenum, match.group(0), DelimiterLineType.BEGIN_BLOCK,
|
||||||
BlockDelimiterTypeData(match.group("subtype")))
|
BlockDelimiterTypeData(match.group("subtype")), match.group('arguments'))
|
||||||
if len(self.headline_hierarchy) == 0:
|
if len(self.headline_hierarchy) == 0:
|
||||||
self.delimiters.append(line)
|
self.delimiters.append(line)
|
||||||
else:
|
else:
|
||||||
@ -2041,7 +2159,7 @@ class OrgDocReader:
|
|||||||
|
|
||||||
def add_end_block_line(self, linenum: int, match: re.Match):
|
def add_end_block_line(self, linenum: int, match: re.Match):
|
||||||
line = DelimiterLine(linenum, match.group(0), DelimiterLineType.END_BLOCK,
|
line = DelimiterLine(linenum, match.group(0), DelimiterLineType.END_BLOCK,
|
||||||
BlockDelimiterTypeData(match.group("subtype")))
|
BlockDelimiterTypeData(match.group("subtype")), None)
|
||||||
if len(self.headline_hierarchy) == 0:
|
if len(self.headline_hierarchy) == 0:
|
||||||
self.delimiters.append(line)
|
self.delimiters.append(line)
|
||||||
else:
|
else:
|
||||||
@ -2093,6 +2211,25 @@ class OrgDocReader:
|
|||||||
reader = enumerate(lines)
|
reader = enumerate(lines)
|
||||||
in_drawer = False
|
in_drawer = False
|
||||||
in_block = False
|
in_block = False
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
|
|
||||||
|
def add_raw_line_with_possible_indentation(linenum, line):
|
||||||
|
added = False
|
||||||
|
nonlocal list_item
|
||||||
|
nonlocal list_item_indentation
|
||||||
|
if list_item:
|
||||||
|
if ((line[:list_item.text_start_pos].strip() == '')
|
||||||
|
or (len(line.strip()) == 0)
|
||||||
|
):
|
||||||
|
list_item.append_line(line)
|
||||||
|
added = True
|
||||||
|
else:
|
||||||
|
list_item = None
|
||||||
|
list_item_indentation = None
|
||||||
|
|
||||||
|
if not added:
|
||||||
|
self.add_raw_line(linenum, line)
|
||||||
|
|
||||||
for lnum, line in reader:
|
for lnum, line in reader:
|
||||||
linenum = lnum + 1
|
linenum = lnum + 1
|
||||||
@ -2101,41 +2238,58 @@ class OrgDocReader:
|
|||||||
if m := END_BLOCK_RE.match(line):
|
if m := END_BLOCK_RE.match(line):
|
||||||
self.add_end_block_line(linenum, m)
|
self.add_end_block_line(linenum, m)
|
||||||
in_block = False
|
in_block = False
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
else:
|
else:
|
||||||
self.add_raw_line(linenum, line)
|
add_raw_line_with_possible_indentation(linenum, line)
|
||||||
|
|
||||||
elif m := HEADLINE_RE.match(line):
|
elif m := HEADLINE_RE.match(line):
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
self.add_headline(linenum, m)
|
self.add_headline(linenum, m)
|
||||||
elif m := LIST_ITEM_RE.match(line):
|
elif m := LIST_ITEM_RE.match(line):
|
||||||
self.add_list_item_line(linenum, m)
|
list_item = self.add_list_item_line(linenum, m)
|
||||||
|
list_item_indentation = m.group("indentation")
|
||||||
elif m := RAW_LINE_RE.match(line):
|
elif m := RAW_LINE_RE.match(line):
|
||||||
self.add_raw_line(linenum, line)
|
add_raw_line_with_possible_indentation(linenum, line)
|
||||||
# Org-babel
|
# Org-babel
|
||||||
elif m := BEGIN_BLOCK_RE.match(line):
|
elif m := BEGIN_BLOCK_RE.match(line):
|
||||||
self.add_begin_block_line(linenum, m)
|
self.add_begin_block_line(linenum, m)
|
||||||
in_block = True
|
in_block = True
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
elif m := END_BLOCK_RE.match(line):
|
elif m := END_BLOCK_RE.match(line):
|
||||||
self.add_end_block_line(linenum, m)
|
self.add_end_block_line(linenum, m)
|
||||||
in_block = False
|
in_block = False
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
# Generic properties
|
# Generic properties
|
||||||
elif m := KEYWORDS_RE.match(line):
|
elif m := KEYWORDS_RE.match(line):
|
||||||
self.add_keyword_line(linenum, m)
|
self.add_keyword_line(linenum, m)
|
||||||
elif m := DRAWER_END_RE.match(line):
|
elif m := DRAWER_END_RE.match(line):
|
||||||
self.add_drawer_end_line(linenum, line, m)
|
self.add_drawer_end_line(linenum, line, m)
|
||||||
in_drawer = False
|
in_drawer = False
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
elif (not in_drawer) and (m := DRAWER_START_RE.match(line)):
|
elif (not in_drawer) and (m := DRAWER_START_RE.match(line)):
|
||||||
self.add_property_drawer_line(linenum, line, m)
|
self.add_property_drawer_line(linenum, line, m)
|
||||||
in_drawer = True
|
in_drawer = True
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
elif (not in_drawer) and (m := RESULTS_DRAWER_RE.match(line)):
|
elif (not in_drawer) and (m := RESULTS_DRAWER_RE.match(line)):
|
||||||
self.add_results_drawer_line(linenum, line, m)
|
self.add_results_drawer_line(linenum, line, m)
|
||||||
in_drawer = True
|
in_drawer = True
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
elif m := NODE_PROPERTIES_RE.match(line):
|
elif m := NODE_PROPERTIES_RE.match(line):
|
||||||
self.add_node_properties_line(linenum, m)
|
self.add_node_properties_line(linenum, m)
|
||||||
elif line.strip().startswith('|'):
|
elif line.strip().startswith('|'):
|
||||||
self.add_table_line(linenum, line)
|
self.add_table_line(linenum, line)
|
||||||
|
list_item_indentation = None
|
||||||
|
list_item = None
|
||||||
# Not captured
|
# Not captured
|
||||||
else:
|
else:
|
||||||
self.add_raw_line(linenum, line)
|
add_raw_line_with_possible_indentation(linenum, line)
|
||||||
except:
|
except:
|
||||||
logging.error("Error line {}: {}".format(linenum + 1, line))
|
logging.error("Error line {}: {}".format(linenum + 1, line))
|
||||||
raise
|
raise
|
||||||
|
@ -36,3 +36,16 @@ exit 0 # Comment
|
|||||||
This is another test
|
This is another test
|
||||||
with two lines too
|
with two lines too
|
||||||
:end:
|
:end:
|
||||||
|
|
||||||
|
* Escaped code
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 04-code-escaped-code-id
|
||||||
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
|
:END:
|
||||||
|
|
||||||
|
#+BEGIN_SRC c :results drawer
|
||||||
|
/* This code has to be escaped to
|
||||||
|
,* avoid confusion with new headlines.
|
||||||
|
,*/
|
||||||
|
main(){}
|
||||||
|
#+END_SRC
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#+TITLE: 06-Links
|
#+TITLE: 06-Lists
|
||||||
#+DESCRIPTION: Simple org file to test links
|
#+DESCRIPTION: Simple org file to test lists
|
||||||
#+TODO: TODO(t) PAUSED(p) | DONE(d)
|
#+TODO: TODO(t) PAUSED(p) | DONE(d)
|
||||||
|
|
||||||
|
|
||||||
@ -51,3 +51,25 @@ Also with markup
|
|||||||
|
|
||||||
- _Key_ :: _Value_
|
- _Key_ :: _Value_
|
||||||
- /Key/ 2 :: /Value/ 2
|
- /Key/ 2 :: /Value/ 2
|
||||||
|
|
||||||
|
* List with multiline elements
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 07-list-with-multiline-elements
|
||||||
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
|
:END:
|
||||||
|
|
||||||
|
- This is a list item...
|
||||||
|
that spans multiple lines
|
||||||
|
|
||||||
|
- This is another list item...
|
||||||
|
that has content on multiple lines
|
||||||
|
|
||||||
|
Text after a multiline element
|
||||||
|
|
||||||
|
- This is another
|
||||||
|
multiline list
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
With a block element inside
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
@ -16,3 +16,18 @@
|
|||||||
| Content2-1 | Content2-2 | Content2-3 |
|
| Content2-1 | Content2-2 | Content2-3 |
|
||||||
Content after the table.
|
Content after the table.
|
||||||
|
|
||||||
|
** Indented table
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 10-table-test-id-02-indented
|
||||||
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
|
:END:
|
||||||
|
|
||||||
|
- This table is indented inside a list item.
|
||||||
|
- Item before in list
|
||||||
|
|
||||||
|
| Header1 | Header2 | Header3 |
|
||||||
|
|------------+------------+------------|
|
||||||
|
| Content1-1 | Content1-2 | Content1-3 (last cell unclosed)
|
||||||
|
| Content2-1 | Content2-2 | Content2-3 |
|
||||||
|
- Item after in list
|
||||||
|
- This item happens after the indented table.
|
||||||
|
16
tests/11-nested-lists.org
Normal file
16
tests/11-nested-lists.org
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#+TITLE: 11-Nested lists
|
||||||
|
#+DESCRIPTION: Simple org file to test nested lists
|
||||||
|
#+TODO: TODO(t) PAUSED(p) | DONE(d)
|
||||||
|
|
||||||
|
* Nested lists
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 11-nested-lists
|
||||||
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
|
:END:
|
||||||
|
- 1
|
||||||
|
- 1.1
|
||||||
|
- 1.2
|
||||||
|
- 2
|
||||||
|
- 2.1
|
||||||
|
- 2.2
|
||||||
|
- 3
|
@ -4,7 +4,8 @@ import unittest
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
from datetime import datetime as DT
|
from datetime import datetime as DT
|
||||||
|
|
||||||
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads
|
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads, dom
|
||||||
|
import org_rw
|
||||||
|
|
||||||
from utils.assertions import (BOLD, CODE, HL, ITALIC, SPAN, STRIKE, UNDERLINED,
|
from utils.assertions import (BOLD, CODE, HL, ITALIC, SPAN, STRIKE, UNDERLINED,
|
||||||
VERBATIM, WEB_LINK, Doc, Tokens)
|
VERBATIM, WEB_LINK, Doc, Tokens)
|
||||||
@ -433,13 +434,14 @@ class TestSerde(unittest.TestCase):
|
|||||||
doc = load(f)
|
doc = load(f)
|
||||||
|
|
||||||
snippets = list(doc.get_code_snippets())
|
snippets = list(doc.get_code_snippets())
|
||||||
self.assertEqual(len(snippets), 2)
|
self.assertEqual(len(snippets), 3)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[0].content,
|
snippets[0].content,
|
||||||
'echo "This is a test"\n'
|
'echo "This is a test"\n'
|
||||||
+ 'echo "with two lines"\n'
|
+ 'echo "with two lines"\n'
|
||||||
+ "exit 0 # Exit successfully",
|
+ "exit 0 # Exit successfully",
|
||||||
)
|
)
|
||||||
|
self.assertEqual(snippets[0].arguments.split(), ['shell', ':results', 'verbatim'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[0].result,
|
snippets[0].result,
|
||||||
"This is a test\n" + "with two lines",
|
"This is a test\n" + "with two lines",
|
||||||
@ -455,6 +457,14 @@ class TestSerde(unittest.TestCase):
|
|||||||
snippets[1].result, "This is another test\n" + "with two lines too"
|
snippets[1].result, "This is another test\n" + "with two lines too"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
snippets[2].content,
|
||||||
|
'/* This code has to be escaped to\n'
|
||||||
|
+ ' * avoid confusion with new headlines.\n'
|
||||||
|
+ ' */\n'
|
||||||
|
+ 'main(){}',
|
||||||
|
)
|
||||||
|
|
||||||
def test_mimic_write_file_05(self):
|
def test_mimic_write_file_05(self):
|
||||||
with open(os.path.join(DIR, "05-dates.org")) as f:
|
with open(os.path.join(DIR, "05-dates.org")) as f:
|
||||||
orig = f.read()
|
orig = f.read()
|
||||||
@ -551,7 +561,7 @@ class TestSerde(unittest.TestCase):
|
|||||||
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
"markup",
|
"markup",
|
||||||
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
".",
|
".", "\n"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -567,7 +577,7 @@ class TestSerde(unittest.TestCase):
|
|||||||
self.assertEqual(lists2[0][0].counter, "1")
|
self.assertEqual(lists2[0][0].counter, "1")
|
||||||
self.assertEqual(lists2[0][0].counter_sep, ".")
|
self.assertEqual(lists2[0][0].counter_sep, ".")
|
||||||
|
|
||||||
self.assertEqual(lists2[0][1].content, ["Second element"])
|
self.assertEqual(lists2[0][1].content, ["Second element", "\n"])
|
||||||
self.assertEqual(lists2[0][1].counter, "2")
|
self.assertEqual(lists2[0][1].counter, "2")
|
||||||
self.assertEqual(lists2[0][1].counter_sep, ".")
|
self.assertEqual(lists2[0][1].counter_sep, ".")
|
||||||
|
|
||||||
@ -575,10 +585,24 @@ class TestSerde(unittest.TestCase):
|
|||||||
self.assertEqual(lists2[1][0].counter, "1")
|
self.assertEqual(lists2[1][0].counter, "1")
|
||||||
self.assertEqual(lists2[1][0].counter_sep, ")")
|
self.assertEqual(lists2[1][0].counter_sep, ")")
|
||||||
|
|
||||||
self.assertEqual(lists2[1][1].content, ["Second element"])
|
self.assertEqual(lists2[1][1].content, ["Second element", "\n"])
|
||||||
self.assertEqual(lists2[1][1].counter, "2")
|
self.assertEqual(lists2[1][1].counter, "2")
|
||||||
self.assertEqual(lists2[1][1].counter_sep, ")")
|
self.assertEqual(lists2[1][1].counter_sep, ")")
|
||||||
|
|
||||||
|
hl4 = doc.getTopHeadlines()[3]
|
||||||
|
# ...
|
||||||
|
lists4 = hl4.getLists()
|
||||||
|
print(lists4)
|
||||||
|
self.assertEqual(len(lists4), 2)
|
||||||
|
|
||||||
|
self.assertEqual(lists4[0][0].content, ["This is a list item...", "\n that spans multiple lines", "\n"])
|
||||||
|
self.assertEqual(lists4[0][0].bullet, "-")
|
||||||
|
self.assertEqual(lists4[0][1].content, ["This is another list item...", "\n that has content on multiple lines", "\n"])
|
||||||
|
self.assertEqual(lists4[0][1].bullet, "-")
|
||||||
|
|
||||||
|
self.assertEqual(lists4[1][0].content, ["This is another", "\n multiline list", "\n"])
|
||||||
|
self.assertEqual(lists4[1][0].bullet, "-")
|
||||||
|
|
||||||
def test_org_roam_07(self):
|
def test_org_roam_07(self):
|
||||||
with open(os.path.join(DIR, "07-org-roam-v2.org")) as f:
|
with open(os.path.join(DIR, "07-org-roam-v2.org")) as f:
|
||||||
orig = f.read()
|
orig = f.read()
|
||||||
@ -658,3 +682,99 @@ class TestSerde(unittest.TestCase):
|
|||||||
self.assertEqual(first_table[0].cells[1].strip(), 'Header2')
|
self.assertEqual(first_table[0].cells[1].strip(), 'Header2')
|
||||||
self.assertEqual(first_table[0].cells[2].strip(), 'Header3')
|
self.assertEqual(first_table[0].cells[2].strip(), 'Header3')
|
||||||
|
|
||||||
|
hl = hl.children[0]
|
||||||
|
|
||||||
|
tables = hl.get_tables()
|
||||||
|
first_table = tables[0]
|
||||||
|
self.assertEqual(len(first_table), 4)
|
||||||
|
|
||||||
|
print(first_table[0])
|
||||||
|
self.assertEqual(len(first_table[0].cells), 3)
|
||||||
|
self.assertEqual(first_table[0].cells[0].strip(), 'Header1')
|
||||||
|
self.assertEqual(first_table[0].cells[1].strip(), 'Header2')
|
||||||
|
self.assertEqual(first_table[0].cells[2].strip(), 'Header3')
|
||||||
|
|
||||||
|
def test_tables_html_file_10(self):
|
||||||
|
with open(os.path.join(DIR, "10-tables.org")) as f:
|
||||||
|
doc = load(f)
|
||||||
|
|
||||||
|
hl = doc.getTopHeadlines()[0]
|
||||||
|
|
||||||
|
tree = hl.as_dom()
|
||||||
|
non_props = [
|
||||||
|
item
|
||||||
|
for item in tree
|
||||||
|
if not isinstance(item, dom.PropertyDrawerNode)
|
||||||
|
]
|
||||||
|
self.assertTrue(isinstance(non_props[0], dom.Text)
|
||||||
|
and isinstance(non_props[1], dom.TableNode)
|
||||||
|
and isinstance(non_props[2], dom.Text),
|
||||||
|
'Expected <Text><Table><Text>')
|
||||||
|
|
||||||
|
|
||||||
|
hl = hl.children[0]
|
||||||
|
tree = hl.as_dom()
|
||||||
|
non_props = [
|
||||||
|
item
|
||||||
|
for item in tree
|
||||||
|
if not (isinstance(item, dom.PropertyDrawerNode)
|
||||||
|
or isinstance(item, dom.Text))
|
||||||
|
]
|
||||||
|
print_tree(non_props)
|
||||||
|
self.assertTrue(len(non_props) == 1,
|
||||||
|
'Expected <List>, with only (1) element')
|
||||||
|
|
||||||
|
def test_nested_lists_html_file_11(self):
|
||||||
|
with open(os.path.join(DIR, "11-nested-lists.org")) as f:
|
||||||
|
doc = load(f)
|
||||||
|
|
||||||
|
hl = doc.getTopHeadlines()[0]
|
||||||
|
|
||||||
|
tree = hl.as_dom()
|
||||||
|
non_props = [
|
||||||
|
item
|
||||||
|
for item in tree
|
||||||
|
if not isinstance(item, dom.PropertyDrawerNode)
|
||||||
|
]
|
||||||
|
print_tree(non_props)
|
||||||
|
self.assertTrue((len(non_props) == 1) and (isinstance(non_props[0], dom.ListGroupNode)),
|
||||||
|
'Expected only <List> as top level')
|
||||||
|
|
||||||
|
dom_list = non_props[0]
|
||||||
|
children = dom_list.children
|
||||||
|
self.assertTrue(len(children) == 5, 'Expected 5 items inside <List>, 3 texts and 2 sublists')
|
||||||
|
|
||||||
|
# Assert texts
|
||||||
|
self.assertEqual(children[0].content, ['1'])
|
||||||
|
self.assertEqual(children[2].content, ['2'])
|
||||||
|
self.assertEqual(children[4].content[0], '3') # Might be ['3', '\n'] but shouldn't be a breaking change
|
||||||
|
|
||||||
|
# Assert lists
|
||||||
|
self.assertTrue(isinstance(children[1], dom.ListGroupNode), 'Expected sublist inside "1"')
|
||||||
|
self.assertEqual(children[1].children[0].content, ['1.1'])
|
||||||
|
self.assertEqual(children[1].children[1].content, ['1.2'])
|
||||||
|
self.assertTrue(isinstance(children[3], dom.ListGroupNode), 'Expected sublist inside "2"')
|
||||||
|
self.assertEqual(children[3].children[0].content, ['2.1'])
|
||||||
|
self.assertEqual(children[3].children[1].content, ['2.2'])
|
||||||
|
|
||||||
|
|
||||||
|
def print_tree(tree, indentation=0, headline=None):
|
||||||
|
for element in tree:
|
||||||
|
print(" " * indentation * 2, "EL:", element)
|
||||||
|
if "children" in dir(element):
|
||||||
|
if len(element.children) > 0:
|
||||||
|
print_element(element.children, indentation + 1, headline)
|
||||||
|
print()
|
||||||
|
|
||||||
|
elif "content" in dir(element):
|
||||||
|
for content in element.content:
|
||||||
|
print_element(content, indentation + 1, headline)
|
||||||
|
|
||||||
|
|
||||||
|
def print_element(element, indentation, headline):
|
||||||
|
if isinstance(element, org_rw.Link):
|
||||||
|
print(" " * indentation * 2, "Link:", element.get_raw())
|
||||||
|
elif isinstance(element, str):
|
||||||
|
print(" " * indentation * 2, "Str[" + element.replace('\n', '<NL>') + "]", type(element))
|
||||||
|
else:
|
||||||
|
print_tree(element, indentation, headline)
|
||||||
|
Loading…
Reference in New Issue
Block a user