"""Anchors the way the web reader writes them: block index plus character offsets into the block's raw text (whitespace as in the file).""" from diora_tui import blocks, marks from diora_tui.blocks import collapse_map, load_flat_book BODY = ( "
\n Erster Satz mit\n Umbruch. Kursiv Ende.\n
\n" "Zweiter Absatz.
" ) def test_collapse_map(): raw = "\n ab cd \n" norm_of_raw, raw_of_norm, length = collapse_map(raw) assert length == 5 and "".join(raw[i] if raw[i] != " " else " " for i in raw_of_norm) == "ab cd" assert raw_of_norm == [3, 4, 5, 8, 9] # a, b, the first space of the run, c, d assert norm_of_raw[3] == 0 and norm_of_raw[5] == 2 and norm_of_raw[len(raw)] == 5 def test_anchor_counts_in_raw_text_and_resolves_back(epub): book = load_flat_book(epub([BODY])) idx = marks.MarkIndex(book) para = next(i for i, b in enumerate(book.blocks) if b.text.startswith("Erster")) text = book.blocks[para].text assert text == "Erster Satz mit Umbruch. Kursiv Ende." s = text.index("Satz") e = text.index("Umbruch") + len("Umbruch") anchor = idx.anchor((para, s), (para, e)) raw = idx.maps(para).raw assert raw[anchor["startChar"]:anchor["endChar"]] == "Satz mit\n Umbruch" # raw offsets: whitespace as in the file assert anchor["startBlockIndex"] == anchor["endBlockIndex"] == para and anchor["type"] == "epub" assert anchor["quote"] == "Satz mit\n Umbruch" and anchor["chapterSrc"].endswith("c0.xhtml") spans = idx.spans([{"type": "highlight", "color": "green", "anchor": anchor}]) assert list(spans) == [para] and spans[para][0][:2] == (s, e) def test_range_over_blocks_and_notes_and_bad_anchors(epub): book = load_flat_book(epub([BODY])) idx = marks.MarkIndex(book) first = next(i for i, b in enumerate(book.blocks) if b.text.startswith("Erster")) last = first + 1 anchor = idx.anchor((first, 7), (last, 6)) spans = idx.spans([{"type": "highlight", "anchor": anchor}]) assert spans[first][0][:2] == (7, len(book.blocks[first].text)) and spans[last][0][:2] == (0, 6) note = {"type": "note", "anchor": idx.anchor((first, 0), (first, 6))} assert idx.spans([note])[first][0][:2] == (0, 6) junk = [{"anchor": {"startBlockIndex": 9999}}, {"anchor": None}, {"anchor": {"startBlockIndex": "x"}}, {}] assert idx.spans(junk) == {} def test_a_footnote_marker_with_spaces_cannot_be_addressed_so_the_quote_is_used(epub): body = 'Vor 1 nach dem Marker.
Fuß
' book = load_flat_book(epub([body])) idx = marks.MarkIndex(book) assert idx.maps(0) is None assert idx.anchor((0, 0), (0, 3)) is None # refused: not exact spans = idx.spans([{"type": "highlight", "anchor": {"startBlockIndex": 0, "endBlockIndex": 0, "startChar": 3, "endChar": 9, "quote": "nach dem"}}]) t = book.blocks[0].text assert spans[0][0][:2] == (t.index("nach dem"), t.index("nach dem") + 8) def test_words(): t = "ab cd e" assert marks.word_starts(t) == [0, 4, 7] assert marks.next_word(t, 0) == 4 and marks.next_word(t, 7) is None assert marks.prev_word(t, 4) == 0 and marks.prev_word(t, 0) is None assert marks.word_at(t, 2) == (4, 6) # in the gap: the next word def test_an_old_xpath_anchor_is_found_by_its_quote(epub): book = load_flat_book(epub([BODY])) idx = marks.MarkIndex(book) old = {"type": "note", "anchor": {"type": "epub", "chapterSrc": book.chapter_srcs[0], "startXpath": "/p[1]", "startOffset": 3, "endXpath": "/p[1]", "endOffset": 8, "quote": "Zweiter Absatz"}} spans = idx.spans([old]) (bi, [(s, e, _)]), = spans.items() assert book.blocks[bi].text[s:e] == "Zweiter Absatz" assert idx.spans([{"anchor": {"type": "epub", "chapterSrc": "elsewhere.xhtml", "quote": "x"}}]) == {}