Ditaa-based diagrams
This commit is contained in:
parent
49c97e4997
commit
82cbcee377
|
@ -0,0 +1,3 @@
|
|||
[ -d .venv ] || python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install preserves
|
|
@ -1 +1,3 @@
|
|||
book/
|
||||
.venv/
|
||||
.mdbook-ditaa.sqlite
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
´³schema·³version‘³definitions·³Book´³dict·±sections´³named³sections´³seqof´³refµ„³BookItem„„„±__non_exhaustive´³lit³null„„„³Chapter´³dict·±name´³named³name´³atom³String„„±path´³named³path´³refµ„³ MaybePath„„±number´³named³number´³refµ„³MaybeSectionNumber„„±content´³named³content´³atom³String„„± sub_items´³named³ sub_items´³seqof´³refµ„³BookItem„„„±source_path´³named³source_path´³refµ„³ MaybePath„„±parent_names´³named³parent_names´³seqof´³atom³String„„„„„³Context´³dict·±root´³named³root´³atom³String„„±config´³named³config³any„±renderer´³named³renderer´³atom³String„„±mdbook_version´³named³mdbook_version´³atom³String„„„„³BookItem´³orµµ±chapter´³dict·±Chapter´³named³value´³refµ„³Chapter„„„„„µ± separator´³lit± Separator„„µ± partTitle´³dict·± PartTitle´³named³value´³atom³String„„„„„„„³ MaybePath´³orµµ±absent´³lit³null„„µ±present´³atom³String„„„„³PreprocessorInput´³tupleµ´³named³context´³refµ„³Context„„´³named³book´³refµ„³Book„„„„³MaybeSectionNumber´³orµµ±absent´³lit³null„„µ±present´³seqof´³atom³
SignedInteger„„„„„„³embeddedType€„„
|
|
@ -0,0 +1,34 @@
|
|||
version 1 .
|
||||
|
||||
PreprocessorInput = [@context Context, @book Book] .
|
||||
|
||||
Context = {
|
||||
"root": @root string,
|
||||
"config": @config any,
|
||||
"renderer": @renderer string,
|
||||
"mdbook_version": @mdbook_version string,
|
||||
} .
|
||||
|
||||
Book = {
|
||||
"sections": @sections [BookItem ...],
|
||||
"__non_exhaustive": =null,
|
||||
} .
|
||||
|
||||
BookItem =
|
||||
/ @chapter { "Chapter": @value Chapter }
|
||||
/ @separator "Separator"
|
||||
/ @partTitle { "PartTitle": @value string }
|
||||
.
|
||||
|
||||
Chapter = {
|
||||
"name": @name string,
|
||||
"content": @content string,
|
||||
"number": @number MaybeSectionNumber,
|
||||
"sub_items": @sub_items [BookItem ...],
|
||||
"path": @path MaybePath,
|
||||
"source_path": @source_path MaybePath,
|
||||
"parent_names": @parent_names [string ...],
|
||||
} .
|
||||
|
||||
MaybeSectionNumber = @absent =null / @present [int ...] .
|
||||
MaybePath = @absent =null / @present string .
|
|
@ -4,3 +4,6 @@ language = "en"
|
|||
multilingual = false
|
||||
src = "src"
|
||||
title = "The Synit Manual"
|
||||
|
||||
[preprocessor.ditaa]
|
||||
command = "./mdbook-ditaa figures/ditaa"
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- python -*-
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
from preserves import preserve, parse, stringify
|
||||
from preserves.schema import load_schema_file, extend
|
||||
|
||||
cache = sqlite3.connect('.mdbook-ditaa.sqlite')
|
||||
try:
|
||||
cache.cursor().execute('CREATE TABLE diagrams(source text, svg text)')
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
schema = load_schema_file('./book.prb').book
|
||||
except FileNotFoundError:
|
||||
subprocess.check_output(['sh', '-c', 'preserves-schemac --no-bundle .:book.prs > book.prb'])
|
||||
schema = load_schema_file('./book.prb').book
|
||||
|
||||
@extend(schema.Book)
|
||||
def expand(self, context):
|
||||
for i in self.sections:
|
||||
i.expand(context)
|
||||
|
||||
@extend(schema.BookItem.chapter)
|
||||
def expand(self, context):
|
||||
self.value.expand(context)
|
||||
|
||||
@extend(schema.BookItem._ALL)
|
||||
def expand(self, context):
|
||||
pass
|
||||
|
||||
@extend(schema.Chapter)
|
||||
def expand(self, context):
|
||||
if self.source_path.VARIANT.name == 'present':
|
||||
directory = os.path.dirname(self.source_path.value)
|
||||
self.content = expand_codeblock(DITAA_TAG, expand_ditaa, self.content, context, directory)
|
||||
for i in self.sub_items:
|
||||
i.expand(context)
|
||||
|
||||
def expand_codeblock(language_re, expander, s, *args):
|
||||
return re.sub('^```' + language_re + '\n(.*?)\n```\n', lambda m: expander(m, *args), s, 0, re.M | re.S)
|
||||
|
||||
figure_count = 0
|
||||
def next_filename():
|
||||
global figure_count
|
||||
figure_count = figure_count + 1
|
||||
return f'figure_{figure_count}'
|
||||
|
||||
DITAA_TAG = r'ditaa(?: ((?:\w|-)+))?'
|
||||
def expand_ditaa(m, context, directory):
|
||||
sourcedir = context['config']['book']['src']
|
||||
baseurl = context['config']['output']['html']['site-url']
|
||||
filename = os.path.join(directory, (m.group(1) or next_filename()) + '.svg')
|
||||
sys.stderr.write(f'ditaa {filename} ...')
|
||||
|
||||
ditaa_source = m.group(2)
|
||||
|
||||
cached = list(cache.cursor().execute('SELECT svg FROM diagrams WHERE source = ?', (ditaa_source,)))
|
||||
if cached:
|
||||
svg = cached[0][0]
|
||||
else:
|
||||
svg = subprocess.check_output(['ditaa', '--svg', '-'], input=ditaa_source.encode('utf-8'))
|
||||
svg = svg.decode('utf-8')
|
||||
cache.cursor().execute('INSERT INTO diagrams VALUES (?, ?)', (ditaa_source, svg))
|
||||
cache.cursor().execute('COMMIT')
|
||||
|
||||
svgfilename = os.path.join(sourcedir, output_prefix, filename)
|
||||
os.makedirs(os.path.dirname(svgfilename), exist_ok=True)
|
||||
|
||||
need_write = True
|
||||
if os.path.exists(svgfilename):
|
||||
with open(svgfilename, 'rt') as f:
|
||||
existing = f.read()
|
||||
if existing == svg:
|
||||
need_write = False
|
||||
if need_write:
|
||||
sys.stderr.write('updated\n')
|
||||
with open(svgfilename, 'wt') as f:
|
||||
f.write(svg)
|
||||
else:
|
||||
sys.stderr.write('unchanged\n')
|
||||
|
||||
return f'<p><img class="ditaa" alt="{filename}" src="{os.path.join(baseurl, output_prefix, filename)}"></p>\n'
|
||||
|
||||
def structure(items):
|
||||
answer = []
|
||||
for i in items:
|
||||
if i.VARIANT.name == 'chapter':
|
||||
a = structure(i.value.sub_items)
|
||||
answer.append({ i.value.name: a })
|
||||
return answer
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[-2:-1] == ['supports']:
|
||||
sys.exit(0 if sys.argv[-1] == 'html' else 1)
|
||||
output_prefix = sys.argv[1] if len(sys.argv) > 1 else 'figures'
|
||||
|
||||
raw = parse(sys.stdin.read())
|
||||
# sys.stderr.write(stringify(raw, indent=2) + '\n')
|
||||
i = schema.PreprocessorInput.decode(raw)
|
||||
# sys.stderr.write(stringify(preserve(i.context), indent=2) + '\n')
|
||||
i.book.expand(i.context)
|
||||
print(stringify(preserve(i.book), with_commas=True))
|
|
@ -0,0 +1 @@
|
|||
ditaa/
|
|
@ -6,6 +6,31 @@ All processes in the system are arranged into a [supervision
|
|||
tree](../glossary.md#supervision-tree), conceptually rooted at the [system
|
||||
bus](./system-bus.md) (NB. not at PID 1).
|
||||
|
||||
```ditaa system-supervision-tree
|
||||
(Example)
|
||||
+----------------------------------+
|
||||
|Root System Bus (syndicate–server)|
|
||||
+----------------+-----------------+
|
||||
|
|
||||
+--------+--------+---------+----------+---------------+
|
||||
| | | | | |
|
||||
+--+--+ +---+---+ +--+--+ +----+----+ +---+---+ +-----+-----+
|
||||
|init | |console| |udevd| |Network | |Wifi | |Session bus|
|
||||
+-----+ |getty | +-----+ |Interface| |Daemon | ... +-----+-----+
|
||||
+-------+ |Monitor | |(wlan0)| |
|
||||
|Daemon | +-------+ |
|
||||
+---------+ |
|
||||
|
|
||||
+----------+----------------------------+
|
||||
| | |
|
||||
+---+---+ +--+--+ +----+---+
|
||||
|Browser| |Email| . . . |X Server|
|
||||
+-------+ +-----+ +--------+
|
||||
|
||||
```
|
||||
|
||||
Here's an example of `ps` output from a Synit prototype running on a mobile phone:
|
||||
|
||||
PID TTY STAT TIME COMMAND
|
||||
1 ? Ssl 0:00 /sbin/synit-pid1
|
||||
1034 ? Sl 0:00 /usr/bin/syndicate-server --inferior --config /etc/syndicate/boot
|
||||
|
|
Loading…
Reference in New Issue