1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-26 08:25:50 +03:00
wezterm/ci/generate-docs.py
Wez Furlong b904ed7677
breaking(ish): pane.get_current_working_dir now returns Url
Previously we'd return the Url string.  Now we provide a Url
object that provides access to the various elements of the Url.

This will cause slightly breakage for folks that were treating
it as a string in their status event handlers, for example.

The docs have been updated to show how to run with both this
new Url object and also continue to run on older versions of
wezterm.

They now also show how to manually percent decode the url
for older versions of wezterm.

refs: https://github.com/wez/wezterm/discussions/4157
refs: https://github.com/wez/wezterm/issues/4000
2023-08-25 06:59:09 -07:00

502 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(
"module: wezterm.url",
"config/lua/wezterm.url",
),
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")