# SPDX-FileCopyrightText: © 2024 Christian BUHTZ <c.buhtz@posteo.jp>
#
# SPDX-License-Identifier: CC0-1.0
#
# This file is part of the program "Hyperorg". Unlike the program, this file is
# licensed under Creative Commons Zero v1 (CC0-1.0). See folder LICENSES or go
# to <https://creativecommons.org/publicdomain/zero/1.0/>
:PROPERTIES:
:ID:       8cc00b3a-255e-456f-9e12-95c21de00b36
:END:
#+title: Hyperorg Design Concept
#+date: [2025-09-12 15:03]
* Introduction
This documents describe the design concept behind [[id:d0a0d7c9-8aa2-4377-b20b-ab54c93b49d6][Hyperorg]]. It contains
technical details and reasonate some design decisions. These are not hard
rules and there is always the opportunity to modify the concept for good
reasons.

Hyperorg...
- ... tries to mimik the behavior of ~org-roam-export-to-html~.
- ... do not copy the exact HTML output of ~org-roam-export-to-html~.
- ... should not break when encountering unsupported or unknown org-syntax.

* Basics
** Org document
An org document consists of a header and a body. The header with its
=PROPERTIES= drawer is handled by the extern =orgparse= package. The body is
handled by Hyperorg itself.

** Elements and Runs
From top to bottom the body of an org file contains /elements/
(=elements.py=). Imagine it like a stack on a vertical axis.  Each /element/ can
contain several /runs/ (~class Run~). They are organized side by side in an
horizontal axis.  Everything that needs to have its own line and generates a
/newline/ is an /element/.  See file =textruns.py= and =elements.py= for insights.

Technically each /element/ class is also a /run/ (direct or indirect derived from
~class Run~). The distinguishing between /run/ and /element/ happens on a concept
level but not in the itself, despite it affects the organization of code and
files.

A paragraph (~class Paragraph~) for example is an /element/. The text contained in
a /paragraph/ is organized into /runs/. The most simplest run is just a string
(~class str~). In case the text has bold markup it will be represented by a
/markup/ run (~class Markup~).

*** Example
#+begin_src org
This is a raw paragraph
with two lines.

The second paragraph contain *bold text* as an example.
#+end_src

The first paragraph will result in an ~class Paragraph~ instance containing the
text =This is a raw paragraph with two lines.= as a simple string. Note that
there is no newline character.

The second is a ~Paragraph~ with a list of three /runs/: The string "The second
paragraph contain ". As second a /markup/ run (~class Markup~) of type =IMPORTANT=
(result in *bold* rendering). As third, again a string /run/ with "as an example.".


** Nested Runs
An example for nested runs is a link with a label.

#+begin_src org
very *cool and /free/*
#+end_src

This is ~Paragraph~ with two elements: The ~str~ "very " and a ~Markup~ (type
=IMPORTANT=) with "cool and free". But that ~Markup~ contain two entries itself:
The string "cool and " and another ~Markup~ (type =EMPHASIZED=) holding the string
"free".

* The code base
- /Runs/ are defined in =textruns.py=.
- /Elements/ are defined in =elements.py=.
- Parsing and creating /elements/ happens in
  ~parser.py::OrgContentParser.result()~.
- Parsing and creating /runs/ happens in =textruns.py=.

** The parsing process
This section describe the parsing process and its steps. Not all details and
cases are covered. The goal is just to give an idea about the parsing workflow.

1. The raw org-file is read by `orgparser` (an external package) first. That
   component does parse the meta data (e.g. `PROPERTIES` drawer) of the org
   file and additionally provide the org-files body as a simple list of lines.
   Location in code is ~reader.py::Reader.read_org_files()~. Org files (and org
   sections with their own =ORGID=) are described as /nodes/. So final result of
   the parsing process will be a list of ~Node~ (see file =content.py=)
   instances. The ~Reader~ will create a ~Node~ instance for each org file.
2. The ~Node~ instance itself will handle the parsing of the content in its body
   (see ~content.py::Node.parse_lines()~). It calls
   ~parser.py::OrgContentParser.result()~ for help in this process.
3. The ~OrgContentParser~ iterates over each line of the body and create /elements/
   (e.g. ~Paragraph~, ~Figure~, ...) of it. But the inner content of an /element/ is
   not parsed in this location.
4. Parsing of the inner content of an /element/, like a ~Pargraph~ with its markup
   or inline images, is handled by the /element/ itself. Because an /element/
   consists of several /runs/ that parsing is handled in =textruns.py=.
