1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-24 13:52:55 +03:00
wezterm/ci/generate-docs.py

498 lines
16 KiB
Python

#!/usr/bin/env python3
import base64
import glob
import json
import os
import re
import subprocess
import sys
class Page(object):
def __init__(self, title, filename, children=None):
self.title = title
self.filename = filename
self.children = children or []
def render(self, output, depth=0, mode="mdbook"):
indent = " " * depth
bullet = "- " if depth > 0 else ""
if mode == "mdbook":
if self.filename:
output.write(f"{indent}{bullet}[{self.title}]({self.filename})\n")
elif mode == "mkdocs":
if depth > 0:
if len(self.children) == 0:
output.write(f'{indent}{bullet}"{self.title}": {self.filename}\n')
else:
output.write(f'{indent}{bullet}"{self.title}":\n')
if self.filename:
output.write(
f'{indent} {bullet}"{self.title}": {self.filename}\n'
)
for kid in self.children:
kid.render(output, depth + 1, mode)
# autogenerate an index page from the contents of a directory
class Gen(object):
def __init__(self, title, dirname, index=None, extract_title=False):
self.title = title
self.dirname = dirname
self.index = index
self.extract_title = extract_title
def render(self, output, depth=0, mode="mdbook"):
names = sorted(glob.glob(f"{self.dirname}/*.md"))
children = []
for filename in names:
title = os.path.basename(filename).rsplit(".", 1)[0]
if title == "index":
continue
if self.extract_title:
with open(filename, "r") as f:
title = f.readline().strip("#").strip()
children.append(Page(title, filename))
index_filename = f"{self.dirname}/index.md"
index_page = Page(self.title, index_filename, children=children)
index_page.render(output, depth, mode)
with open(f"{self.dirname}/index.md", "w") as idx:
if self.index:
idx.write(self.index)
idx.write("\n\n")
else:
try:
with open(f"{self.dirname}/index.markdown", "r") as f:
idx.write(f.read())
idx.write("\n\n")
except FileNotFoundError:
pass
for page in children:
idx.write(f" - [{page.title}]({os.path.basename(page.filename)})\n")
def load_scheme(scheme):
ident = re.sub(
"[^a-z0-9_]", "_", scheme["metadata"]["name"].lower().replace("+", "plus")
)
colors = scheme["colors"]["ansi"] + scheme["colors"]["brights"]
data = {
"name": scheme["metadata"]["name"],
"prefix": scheme["metadata"]["prefix"],
"ident": ident,
"fg": scheme["colors"]["foreground"],
"bg": scheme["colors"]["background"],
"metadata": scheme["metadata"],
}
# <https://github.com/asciinema/asciinema-player/wiki/Custom-terminal-themes>
css = f"""
.asciinema-theme-{ident} .asciinema-terminal {{
color: {data["fg"]};
background-color: {data["bg"]};
border-color: {data["bg"]};
}}
.asciinema-theme-{ident} .fg-bg {{
color: {data["bg"]};
}}
.asciinema-theme-{ident} .bg-fg {{
background-color: {data["fg"]};
}}
"""
if "cursor_border" in scheme["colors"]:
data["cursor"] = scheme["colors"]["cursor_border"]
css += f"""
.asciinema-theme-{ident} .cursor-b {{
background-color: {data["cursor"]} !important;
}}
"""
if "selection_fg" in scheme["colors"] and "selection_bg" in scheme["colors"]:
selection_bg = scheme["colors"]["selection_bg"]
selection_fg = scheme["colors"]["selection_fg"]
css += f"""
.asciinema-theme-{ident} .asciinema-terminal ::selection {{
color: {selection_fg};
background-color: {selection_bg};
}}
"""
for idx, color in enumerate(colors):
css += f"""
.asciinema-theme-{ident} .fg-{idx} {{
color: {color};
}}
.asciinema-theme-{ident} .bg-{idx} {{
background-color: {color};
}}
"""
data["css"] = css
return data
def screen_shot_table(scheme):
T = "gYw"
lines = [
scheme["name"],
"",
" def 40m 41m 42m 43m 44m 45m 46m 47m",
]
for fg_space in [
" m",
" 1m",
" 30m",
"1;30m",
" 31m",
"1;31m",
" 32m",
"1;32m",
" 33m",
"1;33m",
" 34m",
"1;34m",
" 35m",
"1;35m",
" 36m",
"1;36m",
" 37m",
"1;37m",
]:
fg = fg_space.strip()
line = f" {fg_space} \033[{fg} {T} "
for bg in ["40m", "41m", "42m", "43m", "44m", "45m", "46m", "47m"]:
line += f" \033[{fg}\033[{bg} {T} \033[0m"
lines.append(line)
lines.append("")
lines.append("")
screen = "\r\n".join(lines)
header = {
"version": 2,
"width": 80,
"height": 24,
"title": scheme["name"],
}
header = json.dumps(header, sort_keys=True)
data = json.dumps([0.0, "o", screen])
return base64.b64encode(f"{header}\n{data}\n".encode("UTF-8")).decode("UTF-8")
class GenColorScheme(object):
def __init__(self, title, dirname, index=None):
self.title = title
self.dirname = dirname
self.index = index
def render(self, output, depth=0, mode="mdbook"):
with open("colorschemes/data.json") as f:
scheme_data = json.load(f)
by_prefix = {}
by_name = {}
for scheme in scheme_data:
scheme = load_scheme(scheme)
prefix = scheme["prefix"]
if prefix not in by_prefix:
by_prefix[prefix] = []
by_prefix[prefix].append(scheme)
by_name[scheme["name"]] = scheme
style_filename = f"{self.dirname}/scheme.css"
with open(style_filename, "w") as style_file:
for scheme in by_name.values():
style_file.write(scheme["css"])
style_file.write("\n")
js_filename = f"{self.dirname}/scheme.js"
with open(js_filename, "w") as js_file:
data_by_scheme = {}
for scheme in by_name.values():
ident = scheme["ident"]
data = screen_shot_table(scheme)
data_by_scheme[ident] = data
js_file.write(f"SCHEME_DATA = {json.dumps(data_by_scheme)};\n")
js_file.write(
f"""
function load_scheme_player(ident) {{
var data = SCHEME_DATA[ident];
AsciinemaPlayer.create(
'data:text/plain;base64,' + data,
document.getElementById(ident + '-player'), {{
theme: ident,
autoPlay: true,
}});
}}
"""
)
children = []
for scheme_prefix in sorted(by_prefix.keys()):
scheme_filename = f"{self.dirname}/{scheme_prefix}/index.md"
os.makedirs(os.path.dirname(scheme_filename), exist_ok=True)
children.append(Page(scheme_prefix, scheme_filename))
with open(scheme_filename, "w") as idx:
idents_to_load = []
idx.write(
f"""---
title: Color Schemes with first letter "{scheme_prefix}"
---
"""
)
for scheme in by_prefix[scheme_prefix]:
title = scheme["name"]
idx.write(f"## {title}\n")
data = screen_shot_table(scheme)
ident = scheme["ident"]
idents_to_load.append(ident)
idx.write(
f"""
<div id="{ident}-player"></div>
"""
)
author = scheme["metadata"].get("author", None)
if author:
idx.write(f"Author: `{author}`<br/>\n")
origin_url = scheme["metadata"].get("origin_url", None)
if origin_url:
idx.write(f"Source: <{origin_url}><br/>\n")
version = scheme["metadata"].get("wezterm_version", None)
if version and version != "Always":
idx.write(f"{{{{since('{version}')}}}}<br/>\n")
aliases = scheme["metadata"]["aliases"]
if len(aliases) > 0:
alias_list = []
for a in aliases:
alias_list.append(f"`{a}`")
aliases = ", ".join(alias_list)
idx.write(f"This scheme is also known as {aliases}.<br/>\n")
idx.write("\nTo use this scheme, add this to your config:\n")
idx.write(
f"""
```lua
config.color_scheme = '{title}'
```
"""
)
idents_to_load = json.dumps(idents_to_load)
idx.write(
f"""
<script>
document.addEventListener("DOMContentLoaded", function() {{
{idents_to_load}.forEach(ident => load_scheme_player(ident));
}});
</script>
"""
)
index_filename = f"{self.dirname}/index.md"
index_page = Page(self.title, index_filename, children=children)
index_page.render(output, depth, mode)
with open(f"{self.dirname}/index.md", "w") as idx:
idx.write(f"{len(scheme_data)} Color schemes listed by first letter\n\n")
for page in children:
upper = page.title.upper()
idx.write(f" - [{upper}]({page.title}/index.md)\n")
TOC = [
Page(
"WezTerm",
"index.md",
children=[
Page("Features", "features.md"),
Page("Scrollback", "scrollback.md"),
Page("Quick Select Mode", "quickselect.md"),
Page("Copy Mode", "copymode.md"),
Page("Hyperlinks", "hyperlinks.md"),
Page("Shell Integration", "shell-integration.md"),
Page("iTerm Image Protocol", "imgcat.md"),
Page("SSH", "ssh.md"),
Page("Serial Ports & Arduino", "serial.md"),
Page("Multiplexing", "multiplexing.md"),
],
),
Page(
"Download",
"installation.md",
children=[
Page("Windows", "install/windows.md"),
Page("macOS", "install/macos.md"),
Page("Linux", "install/linux.md"),
Page("FreeBSD", "install/freebsd.md"),
Page("Build from source", "install/source.md"),
],
),
Page(
"Configuration",
"config/files.md",
children=[
Page("Colors & Appearance", "config/appearance.md"),
Page("Launching Programs", "config/launch.md"),
Page("Fonts", "config/fonts.md"),
Page("Font Shaping", "config/font-shaping.md"),
Page("Keyboard Concepts", "config/keyboard-concepts.md"),
Page("Key Binding", "config/keys.md"),
Page("Key Tables", "config/key-tables.md"),
Page("Default Key Assignments", "config/default-keys.md"),
Page("Keyboard Encoding", "config/key-encoding.md"),
Page("Mouse Binding", "config/mouse.md"),
GenColorScheme("Color Schemes", "colorschemes"),
Gen("Recipes", "recipes", extract_title=True),
],
),
Page(
"Full Config & Lua Reference",
"config/lua/general.md",
children=[
Gen(
"Config Options",
"config/lua/config",
),
Gen(
"module: wezterm",
"config/lua/wezterm",
),
Gen(
"module: wezterm.color",
"config/lua/wezterm.color",
),
Gen(
"module: wezterm.gui",
"config/lua/wezterm.gui",
),
Gen(
"module: wezterm.mux",
"config/lua/wezterm.mux",
),
Gen(
"module: wezterm.procinfo",
"config/lua/wezterm.procinfo",
),
Gen(
"module: wezterm.time",
"config/lua/wezterm.time",
),
Gen(
"enum: KeyAssignment",
"config/lua/keyassignment",
),
Gen(
"enum: CopyModeAssignment",
"config/lua/keyassignment/CopyMode",
),
Gen("object: Color", "config/lua/color"),
Page("object: ExecDomain", "config/lua/ExecDomain.md"),
Page("object: LocalProcessInfo", "config/lua/LocalProcessInfo.md"),
Gen("object: MuxDomain", "config/lua/MuxDomain"),
Gen("object: MuxWindow", "config/lua/mux-window"),
Gen("object: MuxTab", "config/lua/MuxTab"),
Page("object: PaneInformation", "config/lua/PaneInformation.md"),
Page("object: TabInformation", "config/lua/TabInformation.md"),
Page("object: SshDomain", "config/lua/SshDomain.md"),
Page("object: SpawnCommand", "config/lua/SpawnCommand.md"),
Gen("object: Time", "config/lua/wezterm.time/Time"),
Page("object: TlsDomainClient", "config/lua/TlsDomainClient.md"),
Page("object: TlsDomainServer", "config/lua/TlsDomainServer.md"),
Gen(
"object: Pane",
"config/lua/pane",
),
Gen(
"object: Window",
"config/lua/window",
),
Page("object: WslDomain", "config/lua/WslDomain.md"),
Gen(
"events: Gui",
"config/lua/gui-events",
),
Gen(
"events: Multiplexer",
"config/lua/mux-events",
),
Gen(
"events: Window",
"config/lua/window-events",
),
],
),
Page(
"CLI Reference",
"cli/general.md",
children=[
Gen("wezterm cli", "cli/cli"),
Page("wezterm connect", "cli/connect.md"),
Page("wezterm imgcat", "cli/imgcat.md"),
Page("wezterm ls-fonts", "cli/ls-fonts.md"),
Page("wezterm record", "cli/record.md"),
Page("wezterm replay", "cli/replay.md"),
Page("wezterm serial", "cli/serial.md"),
Page("wezterm set-working-directory", "cli/set-working-directory.md"),
Page("wezterm show-keys", "cli/show-keys.md"),
Page("wezterm ssh", "cli/ssh.md"),
Page("wezterm start", "cli/start.md"),
],
),
Page(
"Reference",
None,
children=[
Page("Escape Sequences", "escape-sequences.md"),
Page("What is a Terminal?", "what-is-a-terminal.md"),
],
),
Page(
"Get Help",
None,
children=[
Page("Troubleshooting", "troubleshooting.md"),
Page("F.A.Q.", "faq.md"),
Page("Getting Help", "help.md"),
Page("Contributing", "contributing.md"),
],
),
Page("Change Log", "changelog.md"),
Page("Sponsor", "sponsor.md"),
]
os.chdir("docs")
with open("../mkdocs.yml", "w") as f:
f.write("# this is auto-generated by docs/generate-toc.py, do not edit\n")
f.write("INHERIT: docs/mkdocs-base.yml\n")
f.write("nav:\n")
for page in TOC:
page.render(f, depth=1, mode="mkdocs")
with open("SUMMARY.md", "w") as f:
f.write("[root](index.md)\n")
for page in TOC:
page.render(f, depth=1, mode="mdbook")