forked from kenkeiras/org-rw
Compare commits
26 Commits
feat/small
...
develop
Author | SHA1 | Date | |
---|---|---|---|
8280949f23 | |||
|
691ce30a68 | ||
48de06abc7 | |||
|
d4b40e404d | ||
|
5432c23202 | ||
|
8fe3c27595 | ||
|
1dc6eb0b43 | ||
5019b44dd5 | |||
|
78bc57e55d | ||
|
d4b0d0301f | ||
|
92078617fc | ||
|
852f472374 | ||
|
570e6bb764 | ||
|
e0306bf3a5 | ||
bfe60271eb | |||
|
4af4cda44b | ||
|
5552b3324b | ||
|
f31c64c242 | ||
|
490b36887a | ||
05718e1001 | |||
|
e991074346 | ||
|
66b42e0b96 | ||
|
c6d8575ae5 | ||
|
8ca480ad77 | ||
|
0a55c64551 | ||
991781a249 |
@ -23,6 +23,26 @@ jobs:
|
|||||||
- run: pip install mypy
|
- run: pip install mypy
|
||||||
- run: mypy org_rw --check-untyped-defs
|
- run: mypy org_rw --check-untyped-defs
|
||||||
|
|
||||||
|
style-formatting:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- run: apt-get update && apt-get install -y python3-pip
|
||||||
|
- run: pip install -e .
|
||||||
|
- run: pip install black
|
||||||
|
- run: black --check .
|
||||||
|
|
||||||
|
style-sorted-imports:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- run: apt-get update && apt-get install -y python3-pip
|
||||||
|
- run: pip install -e .
|
||||||
|
- run: pip install isort
|
||||||
|
- run: isort --profile black --check .
|
||||||
|
|
||||||
stability-extra-test:
|
stability-extra-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -41,11 +41,12 @@ class ListGroupNode:
|
|||||||
self.children.append(child)
|
self.children.append(child)
|
||||||
|
|
||||||
def get_raw(self):
|
def get_raw(self):
|
||||||
return '\n'.join([c.get_raw() for c in self.children])
|
return "\n".join([c.get_raw() for c in self.children])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<List: {}>".format(len(self.children))
|
return "<List: {}>".format(len(self.children))
|
||||||
|
|
||||||
|
|
||||||
class TableNode:
|
class TableNode:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.children = []
|
self.children = []
|
||||||
@ -56,21 +57,24 @@ class TableNode:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Table: {}>".format(len(self.children))
|
return "<Table: {}>".format(len(self.children))
|
||||||
|
|
||||||
|
|
||||||
class TableSeparatorRow:
|
class TableSeparatorRow:
|
||||||
def __init__(self, orig=None):
|
def __init__(self, orig=None):
|
||||||
self.orig = orig
|
self.orig = orig
|
||||||
|
|
||||||
|
|
||||||
class TableRow:
|
class TableRow:
|
||||||
def __init__(self, cells, orig=None):
|
def __init__(self, cells, orig=None):
|
||||||
self.cells = cells
|
self.cells = cells
|
||||||
self.orig = orig
|
self.orig = orig
|
||||||
|
|
||||||
|
|
||||||
class Text:
|
class Text:
|
||||||
def __init__(self, content):
|
def __init__(self, content):
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def get_raw(self):
|
def get_raw(self):
|
||||||
return ''.join(self.content.get_raw())
|
return "".join(self.content.get_raw())
|
||||||
|
|
||||||
|
|
||||||
class ListItem:
|
class ListItem:
|
||||||
@ -105,21 +109,24 @@ class CodeBlock(BlockNode):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Code: {}>".format(len(self.lines or []))
|
return "<Code: {}>".format(len(self.lines or []))
|
||||||
|
|
||||||
DomNode = Union[DrawerNode,
|
|
||||||
PropertyNode,
|
|
||||||
ListGroupNode,
|
|
||||||
TableNode,
|
|
||||||
TableSeparatorRow,
|
|
||||||
TableRow,
|
|
||||||
Text,
|
|
||||||
ListItem,
|
|
||||||
BlockNode,
|
|
||||||
]
|
|
||||||
|
|
||||||
ContainerDomNode = Union[DrawerNode,
|
DomNode = Union[
|
||||||
ListGroupNode,
|
DrawerNode,
|
||||||
TableNode,
|
PropertyNode,
|
||||||
BlockNode,
|
ListGroupNode,
|
||||||
]
|
TableNode,
|
||||||
|
TableSeparatorRow,
|
||||||
|
TableRow,
|
||||||
|
Text,
|
||||||
|
ListItem,
|
||||||
|
BlockNode,
|
||||||
|
]
|
||||||
|
|
||||||
|
ContainerDomNode = Union[
|
||||||
|
DrawerNode,
|
||||||
|
ListGroupNode,
|
||||||
|
TableNode,
|
||||||
|
BlockNode,
|
||||||
|
]
|
||||||
|
|
||||||
from .utils import get_raw_contents
|
from .utils import get_raw_contents
|
||||||
|
142
org_rw/org_rw.py
142
org_rw/org_rw.py
@ -1,6 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Dict, Optional, TextIO
|
|
||||||
from datetime import timedelta
|
|
||||||
import collections
|
import collections
|
||||||
import difflib
|
import difflib
|
||||||
import logging
|
import logging
|
||||||
@ -9,12 +8,21 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import cast, Iterator, List, Literal, Optional, Tuple, TypedDict, Union
|
from typing import (
|
||||||
|
Dict,
|
||||||
from .types import HeadlineDict
|
Iterator,
|
||||||
|
List,
|
||||||
|
Literal,
|
||||||
|
Optional,
|
||||||
|
TextIO,
|
||||||
|
Tuple,
|
||||||
|
TypedDict,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
from . import dom
|
from . import dom
|
||||||
|
from .types import HeadlineDict
|
||||||
|
|
||||||
DEBUG_DIFF_CONTEXT = 10
|
DEBUG_DIFF_CONTEXT = 10
|
||||||
|
|
||||||
@ -95,7 +103,7 @@ PLANNING_RE = re.compile(
|
|||||||
r")+\s*"
|
r")+\s*"
|
||||||
)
|
)
|
||||||
LIST_ITEM_RE = re.compile(
|
LIST_ITEM_RE = re.compile(
|
||||||
r"(?P<indentation>\s*)((?P<bullet>[*\-+])|((?P<counter>\d|[a-zA-Z])(?P<counter_sep>[.)]))) ((?P<checkbox_indentation>\s*)\[(?P<checkbox_value>[ Xx])\])?((?P<tag_indentation>\s*)(?P<tag>.*?)::)?(?P<content>.*)"
|
r"(?P<indentation>\s*)((?P<bullet>[*\-+])|((?P<counter>\d|[a-zA-Z])(?P<counter_sep>[.)]))) ((?P<checkbox_indentation>\s*)\[(?P<checkbox_value>[ Xx])\])?((?P<tag_indentation>\s*)((?P<tag>.*?)\s::))?(?P<content>.*)"
|
||||||
)
|
)
|
||||||
|
|
||||||
IMPLICIT_LINK_RE = re.compile(r"(https?:[^<> ]*[a-zA-Z0-9])")
|
IMPLICIT_LINK_RE = re.compile(r"(https?:[^<> ]*[a-zA-Z0-9])")
|
||||||
@ -105,7 +113,7 @@ BEGIN_BLOCK_RE = re.compile(r"^\s*#\+BEGIN_(?P<subtype>[^ ]+)(?P<arguments>.*)$"
|
|||||||
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 = collections.namedtuple(
|
||||||
"CodeSnippet", ("name", "content", "result", "arguments")
|
"CodeSnippet", ("name", "content", "result", "language", "arguments")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Groupings
|
# Groupings
|
||||||
@ -329,7 +337,7 @@ class Headline:
|
|||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.title_start = title_start
|
self.title_start = title_start
|
||||||
self.title = parse_content_block([RawLine(linenum=start_line, line=title)])
|
self.title = parse_content_block([RawLine(linenum=start_line, line=title)])
|
||||||
self.state = state
|
self._state = state
|
||||||
self.tags_start = tags_start
|
self.tags_start = tags_start
|
||||||
self.shallow_tags = tags
|
self.shallow_tags = tags
|
||||||
self.contents = contents
|
self.contents = contents
|
||||||
@ -718,6 +726,42 @@ class Headline:
|
|||||||
def id(self, value):
|
def id(self, value):
|
||||||
self.set_property("ID", value)
|
self.set_property("ID", value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self) -> HeadlineState:
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@state.setter
|
||||||
|
def state(self, new_state: Union[None, str, HeadlineState]) -> None:
|
||||||
|
"""
|
||||||
|
Update the state of a Headline. If the state is a known one it will update it's TODO/DONE properties.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_state (str|HeadlineState): New state, either it's literal value or it's structure.
|
||||||
|
"""
|
||||||
|
if new_state is None:
|
||||||
|
self.is_todo = False
|
||||||
|
self.is_done = False
|
||||||
|
# TODO: Check & log if appropriate?
|
||||||
|
self._state = None
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(new_state, str):
|
||||||
|
new_state = HeadlineState(name=new_state)
|
||||||
|
|
||||||
|
state_name = new_state["name"]
|
||||||
|
if state_name in [kw["name"] for kw in self.doc.todo_keywords]:
|
||||||
|
self.is_todo = True
|
||||||
|
self.is_done = False
|
||||||
|
# TODO: Check & log if appropriate?
|
||||||
|
elif state_name in [kw["name"] for kw in self.doc.done_keywords]:
|
||||||
|
self.is_todo = False
|
||||||
|
self.is_done = True
|
||||||
|
# TODO: Check, log & if appropriate?
|
||||||
|
else:
|
||||||
|
# TODO: Should we raise a warning, raise an exception, update the is_todo/is_done?
|
||||||
|
pass
|
||||||
|
self._state = new_state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def clock(self):
|
def clock(self):
|
||||||
times = []
|
times = []
|
||||||
@ -744,11 +788,20 @@ class Headline:
|
|||||||
return times
|
return times
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tags(self):
|
def tags(self) -> list[str]:
|
||||||
if isinstance(self.parent, OrgDoc):
|
parent_tags = self.parent.tags
|
||||||
return list(self.shallow_tags)
|
if self.doc.environment.get("org-use-tag-inheritance"):
|
||||||
else:
|
accepted_tags = []
|
||||||
return list(self.shallow_tags) + self.parent.tags
|
for tag in self.doc.environment.get("org-use-tag-inheritance"):
|
||||||
|
if tag in parent_tags:
|
||||||
|
accepted_tags.append(tag)
|
||||||
|
parent_tags = accepted_tags
|
||||||
|
|
||||||
|
elif self.doc.environment.get("org-tags-exclude-from-inheritance"):
|
||||||
|
for tag in self.doc.environment.get("org-tags-exclude-from-inheritance"):
|
||||||
|
if tag in parent_tags:
|
||||||
|
parent_tags.remove(tag)
|
||||||
|
return list(self.shallow_tags) + parent_tags
|
||||||
|
|
||||||
def add_tag(self, tag: str):
|
def add_tag(self, tag: str):
|
||||||
self.shallow_tags.append(tag)
|
self.shallow_tags.append(tag)
|
||||||
@ -865,6 +918,12 @@ class Headline:
|
|||||||
sections = []
|
sections = []
|
||||||
arguments = None
|
arguments = None
|
||||||
|
|
||||||
|
names_by_line = {}
|
||||||
|
for kw in self.keywords:
|
||||||
|
if kw.key == "NAME":
|
||||||
|
names_by_line[kw.linenum] = kw.value
|
||||||
|
|
||||||
|
name = None
|
||||||
for delimiter in self.delimiters:
|
for delimiter in self.delimiters:
|
||||||
if (
|
if (
|
||||||
delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK
|
delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK
|
||||||
@ -873,6 +932,12 @@ class Headline:
|
|||||||
line_start = delimiter.linenum
|
line_start = delimiter.linenum
|
||||||
inside_code = True
|
inside_code = True
|
||||||
arguments = delimiter.arguments
|
arguments = delimiter.arguments
|
||||||
|
|
||||||
|
name_line = line_start - 1
|
||||||
|
if name_line in names_by_line:
|
||||||
|
name = names_by_line[name_line]
|
||||||
|
else:
|
||||||
|
name = None
|
||||||
elif (
|
elif (
|
||||||
delimiter.delimiter_type == DelimiterLineType.END_BLOCK
|
delimiter.delimiter_type == DelimiterLineType.END_BLOCK
|
||||||
and delimiter.type_data.subtype.lower() == "src"
|
and delimiter.type_data.subtype.lower() == "src"
|
||||||
@ -887,14 +952,26 @@ class Headline:
|
|||||||
# the content parsing must be re-thinked
|
# the content parsing must be re-thinked
|
||||||
contents = contents[:-1]
|
contents = contents[:-1]
|
||||||
|
|
||||||
|
language = None
|
||||||
|
if arguments is not None:
|
||||||
|
arguments = arguments.strip()
|
||||||
|
if " " in arguments:
|
||||||
|
language = arguments[: arguments.index(" ")]
|
||||||
|
arguments = arguments[arguments.index(" ") + 1 :]
|
||||||
|
else:
|
||||||
|
language = arguments
|
||||||
|
arguments = None
|
||||||
sections.append(
|
sections.append(
|
||||||
{
|
{
|
||||||
"line_first": start + 1,
|
"line_first": start + 1,
|
||||||
"line_last": end - 1,
|
"line_last": end - 1,
|
||||||
"content": contents,
|
"content": contents,
|
||||||
"arguments": arguments,
|
"arguments": arguments,
|
||||||
|
"language": language,
|
||||||
|
"name": name,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
name = None
|
||||||
arguments = None
|
arguments = None
|
||||||
line_start = None
|
line_start = None
|
||||||
|
|
||||||
@ -943,13 +1020,18 @@ class Headline:
|
|||||||
|
|
||||||
results = []
|
results = []
|
||||||
for section in sections:
|
for section in sections:
|
||||||
name = None
|
|
||||||
content = section["content"]
|
content = section["content"]
|
||||||
code_result = section.get("result", None)
|
code_result = section.get("result", None)
|
||||||
arguments = section.get("arguments", None)
|
arguments = section.get("arguments", None)
|
||||||
|
language = section.get("language", None)
|
||||||
|
name = section.get("name", None)
|
||||||
results.append(
|
results.append(
|
||||||
CodeSnippet(
|
CodeSnippet(
|
||||||
name=name, content=content, result=code_result, arguments=arguments
|
content=content,
|
||||||
|
result=code_result,
|
||||||
|
arguments=arguments,
|
||||||
|
language=language,
|
||||||
|
name=name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1903,7 +1985,12 @@ def tokenize_contents(contents: str) -> List[TokenItems]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Possible link close or open of description
|
# Possible link close or open of description
|
||||||
if char == "]" and len(contents) > i + 1 and in_link:
|
if (
|
||||||
|
char == "]"
|
||||||
|
and len(contents) > i + 1
|
||||||
|
and in_link
|
||||||
|
and contents[i + 1] in "]["
|
||||||
|
):
|
||||||
if contents[i + 1] == "]":
|
if contents[i + 1] == "]":
|
||||||
cut_string()
|
cut_string()
|
||||||
|
|
||||||
@ -1954,6 +2041,7 @@ def tokenize_contents(contents: str) -> List[TokenItems]:
|
|||||||
cut_string()
|
cut_string()
|
||||||
tokens.append((TOKEN_TYPE_CLOSE_MARKER, char))
|
tokens.append((TOKEN_TYPE_CLOSE_MARKER, char))
|
||||||
has_changed = True
|
has_changed = True
|
||||||
|
closes.remove(i)
|
||||||
|
|
||||||
if not has_changed:
|
if not has_changed:
|
||||||
text.append(char)
|
text.append(char)
|
||||||
@ -2044,7 +2132,7 @@ def dump_contents(raw):
|
|||||||
content = "\n".join(content_lines)
|
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 = (
|
tag = (
|
||||||
f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')}::"
|
f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')} ::"
|
||||||
if raw.tag or raw.tag_indentation
|
if raw.tag or raw.tag_indentation
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
@ -2223,6 +2311,7 @@ class OrgDoc:
|
|||||||
):
|
):
|
||||||
self.todo_keywords = [HeadlineState(name=kw) for kw in DEFAULT_TODO_KEYWORDS]
|
self.todo_keywords = [HeadlineState(name=kw) for kw in DEFAULT_TODO_KEYWORDS]
|
||||||
self.done_keywords = [HeadlineState(name=kw) for kw in DEFAULT_DONE_KEYWORDS]
|
self.done_keywords = [HeadlineState(name=kw) for kw in DEFAULT_DONE_KEYWORDS]
|
||||||
|
self.environment = environment
|
||||||
|
|
||||||
keywords_set_in_file = False
|
keywords_set_in_file = False
|
||||||
for keyword in keywords:
|
for keyword in keywords:
|
||||||
@ -2266,6 +2355,17 @@ class OrgDoc:
|
|||||||
def path(self):
|
def path(self):
|
||||||
return self._path
|
return self._path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tags(self) -> list[str]:
|
||||||
|
for kw in self.keywords:
|
||||||
|
if kw.key == "FILETAGS":
|
||||||
|
return kw.value.strip(":").split(":")
|
||||||
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def shallow_tags(self) -> list[str]:
|
||||||
|
return self.tags
|
||||||
|
|
||||||
## Querying
|
## Querying
|
||||||
def get_links(self):
|
def get_links(self):
|
||||||
for headline in self.headlines:
|
for headline in self.headlines:
|
||||||
@ -2303,7 +2403,7 @@ class OrgDoc:
|
|||||||
yield hl
|
yield hl
|
||||||
|
|
||||||
def get_code_snippets(self):
|
def get_code_snippets(self):
|
||||||
for headline in self.headlines:
|
for headline in self.getAllHeadlines():
|
||||||
yield from headline.get_code_snippets()
|
yield from headline.get_code_snippets()
|
||||||
|
|
||||||
# Writing
|
# Writing
|
||||||
@ -2314,8 +2414,8 @@ class OrgDoc:
|
|||||||
tags = ":" + ":".join(headline.shallow_tags) + ":"
|
tags = ":" + ":".join(headline.shallow_tags) + ":"
|
||||||
|
|
||||||
state = ""
|
state = ""
|
||||||
if headline.state:
|
if headline._state:
|
||||||
state = headline.state["name"] + " "
|
state = headline._state["name"] + " "
|
||||||
|
|
||||||
raw_title = token_list_to_raw(headline.title.contents)
|
raw_title = token_list_to_raw(headline.title.contents)
|
||||||
tags_padding = ""
|
tags_padding = ""
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from .org_rw import (Bold, Code, Headline, Italic, Line, RawLine, ListItem, Strike, Text,
|
from .org_rw import (
|
||||||
Underlined, Verbatim)
|
Bold,
|
||||||
|
Code,
|
||||||
from .org_rw import dump_contents
|
Headline,
|
||||||
|
Italic,
|
||||||
|
Line,
|
||||||
|
ListItem,
|
||||||
|
RawLine,
|
||||||
|
Strike,
|
||||||
|
Text,
|
||||||
|
Underlined,
|
||||||
|
Verbatim,
|
||||||
|
dump_contents,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_hl_raw_contents(doc: Headline) -> str:
|
def get_hl_raw_contents(doc: Headline) -> str:
|
||||||
|
11
scripts/apply-formatting.sh
Executable file
11
scripts/apply-formatting.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd "`dirname $0`"
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
isort --profile black .
|
||||||
|
black .
|
@ -9,6 +9,7 @@
|
|||||||
:CREATED: [2020-01-01 Wed 01:01]
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
|
#+NAME: first-code-name
|
||||||
#+BEGIN_SRC shell :results verbatim
|
#+BEGIN_SRC shell :results verbatim
|
||||||
echo "This is a test"
|
echo "This is a test"
|
||||||
echo "with two lines"
|
echo "with two lines"
|
||||||
|
13
tests/13-tags.org
Normal file
13
tests/13-tags.org
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#+TITLE: 13-Tags
|
||||||
|
#+DESCRIPTION: Simple org file to test tags
|
||||||
|
#+FILETAGS: :filetag:
|
||||||
|
|
||||||
|
* Level 1 :h1tag:
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 13-tags
|
||||||
|
:CREATED: [2020-01-01 Wed 01:01]
|
||||||
|
:END:
|
||||||
|
|
||||||
|
** Level2 :h2tag:
|
||||||
|
* Level 1-1 :otherh1tag:
|
||||||
|
** Level2 :otherh2tag:
|
@ -2,9 +2,6 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime as DT
|
from datetime import datetime as DT
|
||||||
|
|
||||||
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads, dom
|
|
||||||
import org_rw
|
|
||||||
|
|
||||||
from utils.assertions import (
|
from utils.assertions import (
|
||||||
BOLD,
|
BOLD,
|
||||||
CODE,
|
CODE,
|
||||||
@ -19,6 +16,9 @@ from utils.assertions import (
|
|||||||
Tokens,
|
Tokens,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import org_rw
|
||||||
|
from org_rw import MarkerToken, MarkerType, Timestamp, dom, dumps, load, loads
|
||||||
|
|
||||||
DIR = os.path.dirname(os.path.abspath(__file__))
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
@ -480,20 +480,22 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
snippets = list(doc.get_code_snippets())
|
snippets = list(doc.get_code_snippets())
|
||||||
self.assertEqual(len(snippets), 3)
|
self.assertEqual(len(snippets), 3)
|
||||||
|
self.assertEqual(snippets[0].name, "first-code-name")
|
||||||
|
self.assertEqual(snippets[0].language, "shell")
|
||||||
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(
|
self.assertEqual(snippets[0].arguments.split(), [":results", "verbatim"])
|
||||||
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",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.assertEqual(snippets[1].name, None)
|
||||||
|
self.assertEqual(snippets[1].language, "shell")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[1].content,
|
snippets[1].content,
|
||||||
'echo "This is another test"\n'
|
'echo "This is another test"\n'
|
||||||
@ -504,6 +506,8 @@ 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].name, None)
|
||||||
|
self.assertEqual(snippets[2].language, "c")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[2].content,
|
snippets[2].content,
|
||||||
"/* This code has to be escaped to\n"
|
"/* This code has to be escaped to\n"
|
||||||
@ -834,12 +838,12 @@ class TestSerde(unittest.TestCase):
|
|||||||
self.assertEqual(dumps(doc), orig)
|
self.assertEqual(dumps(doc), orig)
|
||||||
|
|
||||||
def test_add_todo_keywords_programatically(self):
|
def test_add_todo_keywords_programatically(self):
|
||||||
orig = '''* NEW_TODO_STATE First entry
|
orig = """* NEW_TODO_STATE First entry
|
||||||
|
|
||||||
* NEW_DONE_STATE Second entry'''
|
* NEW_DONE_STATE Second entry"""
|
||||||
doc = loads(orig, environment={
|
doc = loads(
|
||||||
'org-todo-keywords': "NEW_TODO_STATE | NEW_DONE_STATE"
|
orig, environment={"org-todo-keywords": "NEW_TODO_STATE | NEW_DONE_STATE"}
|
||||||
})
|
)
|
||||||
self.assertEqual(doc.headlines[0].is_todo, True)
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
self.assertEqual(doc.headlines[0].is_done, False)
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
|
||||||
@ -849,14 +853,14 @@ class TestSerde(unittest.TestCase):
|
|||||||
self.assertEqual(dumps(doc), orig)
|
self.assertEqual(dumps(doc), orig)
|
||||||
|
|
||||||
def test_add_todo_keywords_in_file(self):
|
def test_add_todo_keywords_in_file(self):
|
||||||
orig = '''#+TODO: NEW_TODO_STATE | NEW_DONE_STATE
|
orig = """#+TODO: NEW_TODO_STATE | NEW_DONE_STATE
|
||||||
|
|
||||||
* NEW_TODO_STATE First entry
|
* NEW_TODO_STATE First entry
|
||||||
|
|
||||||
* NEW_DONE_STATE Second entry'''
|
* NEW_DONE_STATE Second entry"""
|
||||||
doc = loads(orig, environment={
|
doc = loads(
|
||||||
'org-todo-keywords': "NEW_TODO_STATE | NEW_DONE_STATE"
|
orig, environment={"org-todo-keywords": "NEW_TODO_STATE | NEW_DONE_STATE"}
|
||||||
})
|
)
|
||||||
self.assertEqual(doc.headlines[0].is_todo, True)
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
self.assertEqual(doc.headlines[0].is_done, False)
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
|
||||||
@ -865,6 +869,161 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(dumps(doc), orig)
|
self.assertEqual(dumps(doc), orig)
|
||||||
|
|
||||||
|
def test_mimic_write_file_13(self):
|
||||||
|
with open(os.path.join(DIR, "13-tags.org")) as f:
|
||||||
|
orig = f.read()
|
||||||
|
doc = loads(orig)
|
||||||
|
|
||||||
|
self.assertEqual(dumps(doc), orig)
|
||||||
|
|
||||||
|
def test_tag_property_read_13(self):
|
||||||
|
with open(os.path.join(DIR, "13-tags.org")) as f:
|
||||||
|
orig = f.read()
|
||||||
|
doc = loads(orig)
|
||||||
|
|
||||||
|
self.assertEqual(doc.tags, ["filetag"])
|
||||||
|
|
||||||
|
h1_1, h1_2 = doc.getTopHeadlines()
|
||||||
|
self.assertEqual(sorted(h1_1.tags), ["filetag", "h1tag"])
|
||||||
|
self.assertEqual(sorted(h1_2.tags), ["filetag", "otherh1tag"])
|
||||||
|
|
||||||
|
h1_1_h2 = h1_1.children[0]
|
||||||
|
self.assertEqual(sorted(h1_1_h2.tags), ["filetag", "h1tag", "h2tag"])
|
||||||
|
|
||||||
|
h1_2_h2 = h1_2.children[0]
|
||||||
|
self.assertEqual(sorted(h1_2_h2.tags), ["filetag", "otherh1tag", "otherh2tag"])
|
||||||
|
|
||||||
|
def test_shallow_tag_property_read_13(self):
|
||||||
|
with open(os.path.join(DIR, "13-tags.org")) as f:
|
||||||
|
orig = f.read()
|
||||||
|
doc = loads(orig)
|
||||||
|
|
||||||
|
self.assertEqual(doc.shallow_tags, ["filetag"])
|
||||||
|
|
||||||
|
h1_1, h1_2 = doc.getTopHeadlines()
|
||||||
|
self.assertEqual(sorted(h1_1.shallow_tags), ["h1tag"])
|
||||||
|
self.assertEqual(sorted(h1_2.shallow_tags), ["otherh1tag"])
|
||||||
|
|
||||||
|
h1_1_h2 = h1_1.children[0]
|
||||||
|
self.assertEqual(sorted(h1_1_h2.shallow_tags), ["h2tag"])
|
||||||
|
|
||||||
|
h1_2_h2 = h1_2.children[0]
|
||||||
|
self.assertEqual(sorted(h1_2_h2.shallow_tags), ["otherh2tag"])
|
||||||
|
|
||||||
|
def test_exclude_tags_from_inheritance_property_read_13(self):
|
||||||
|
with open(os.path.join(DIR, "13-tags.org")) as f:
|
||||||
|
orig = f.read()
|
||||||
|
doc = loads(
|
||||||
|
orig,
|
||||||
|
{
|
||||||
|
"org-tags-exclude-from-inheritance": ("h1tag", "otherh2tag"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(doc.tags, ["filetag"])
|
||||||
|
|
||||||
|
h1_1, h1_2 = doc.getTopHeadlines()
|
||||||
|
self.assertEqual(sorted(h1_1.tags), ["filetag", "h1tag"])
|
||||||
|
self.assertEqual(sorted(h1_2.tags), ["filetag", "otherh1tag"])
|
||||||
|
|
||||||
|
h1_1_h2 = h1_1.children[0]
|
||||||
|
self.assertEqual(sorted(h1_1_h2.tags), ["filetag", "h2tag"])
|
||||||
|
|
||||||
|
h1_2_h2 = h1_2.children[0]
|
||||||
|
self.assertEqual(sorted(h1_2_h2.tags), ["filetag", "otherh1tag", "otherh2tag"])
|
||||||
|
|
||||||
|
def test_select_tags_to_inheritance_property_read_13(self):
|
||||||
|
with open(os.path.join(DIR, "13-tags.org")) as f:
|
||||||
|
orig = f.read()
|
||||||
|
doc = loads(
|
||||||
|
orig,
|
||||||
|
{
|
||||||
|
"org-tags-exclude-from-inheritance": ("h1tag", "otherh2tag"),
|
||||||
|
"org-use-tag-inheritance": ("h1tag",),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(doc.tags, ["filetag"])
|
||||||
|
|
||||||
|
h1_1, h1_2 = doc.getTopHeadlines()
|
||||||
|
self.assertEqual(sorted(h1_1.tags), ["h1tag"])
|
||||||
|
self.assertEqual(sorted(h1_2.tags), ["otherh1tag"])
|
||||||
|
|
||||||
|
h1_1_h2 = h1_1.children[0]
|
||||||
|
self.assertEqual(sorted(h1_1_h2.tags), ["h1tag", "h2tag"])
|
||||||
|
|
||||||
|
h1_2_h2 = h1_2.children[0]
|
||||||
|
self.assertEqual(sorted(h1_2_h2.tags), ["otherh2tag"])
|
||||||
|
|
||||||
|
def test_update_headline_from_none_to_todo(self):
|
||||||
|
orig = "* First entry"
|
||||||
|
doc = loads(orig)
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state, None)
|
||||||
|
|
||||||
|
doc.headlines[0].state = "TODO"
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "TODO")
|
||||||
|
|
||||||
|
self.assertEqual(dumps(doc), "* TODO First entry")
|
||||||
|
|
||||||
|
def test_update_headline_from_none_to_done(self):
|
||||||
|
orig = "* First entry"
|
||||||
|
doc = loads(orig)
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state, None)
|
||||||
|
|
||||||
|
doc.headlines[0].state = org_rw.HeadlineState(name="DONE")
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, True)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "DONE")
|
||||||
|
|
||||||
|
self.assertEqual(dumps(doc), "* DONE First entry")
|
||||||
|
|
||||||
|
def test_update_headline_from_todo_to_none(self):
|
||||||
|
orig = "* TODO First entry"
|
||||||
|
doc = loads(orig)
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "TODO")
|
||||||
|
|
||||||
|
doc.headlines[0].state = None
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state, None)
|
||||||
|
|
||||||
|
self.assertEqual(dumps(doc), "* First entry")
|
||||||
|
|
||||||
|
def test_update_headline_from_todo_to_done(self):
|
||||||
|
orig = "* TODO First entry"
|
||||||
|
doc = loads(orig)
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "TODO")
|
||||||
|
|
||||||
|
doc.headlines[0].state = "DONE"
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, True)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "DONE")
|
||||||
|
self.assertEqual(dumps(doc), "* DONE First entry")
|
||||||
|
|
||||||
|
def test_update_headline_from_done_to_todo(self):
|
||||||
|
orig = "* DONE First entry"
|
||||||
|
doc = loads(orig)
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, False)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, True)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "DONE")
|
||||||
|
|
||||||
|
doc.headlines[0].state = org_rw.HeadlineState(name="TODO")
|
||||||
|
self.assertEqual(doc.headlines[0].is_todo, True)
|
||||||
|
self.assertEqual(doc.headlines[0].is_done, False)
|
||||||
|
self.assertEqual(doc.headlines[0].state["name"], "TODO")
|
||||||
|
|
||||||
|
self.assertEqual(dumps(doc), "* TODO First entry")
|
||||||
|
|
||||||
|
|
||||||
def print_tree(tree, indentation=0, headline=None):
|
def print_tree(tree, indentation=0, headline=None):
|
||||||
for element in tree:
|
for element in tree:
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
"""Test the Timestamp object."""
|
"""Test the Timestamp object."""
|
||||||
|
|
||||||
import pytest
|
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from org_rw import Timestamp
|
from org_rw import Timestamp
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,17 @@ import collections
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from org_rw import (Bold, Code, Italic, Line, Strike, Text, Underlined,
|
from org_rw import (
|
||||||
Verbatim, get_raw_contents)
|
Bold,
|
||||||
|
Code,
|
||||||
|
Italic,
|
||||||
|
Line,
|
||||||
|
Strike,
|
||||||
|
Text,
|
||||||
|
Underlined,
|
||||||
|
Verbatim,
|
||||||
|
get_raw_contents,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def timestamp_to_datetime(ts):
|
def timestamp_to_datetime(ts):
|
||||||
|
Loading…
Reference in New Issue
Block a user