///|
/// Transformations for turning a theme document into stable variants without
/// losing descriptions or aliases.
pub enum ThemeTransform {
Prefix(String)
Rename(String, String)
FilterKind(TokenKind)
DropPrefix(String)
} derive(Debug, Eq)
///|
pub fn ThemeTransform::prefix(value : String) -> ThemeTransform {
Prefix(value)
}
///|
pub fn ThemeTransform::rename(from : String, to : String) -> ThemeTransform {
Rename(from, to)
}
///|
pub fn ThemeTransform::filter_kind(kind : TokenKind) -> ThemeTransform {
FilterKind(kind)
}
///|
pub fn ThemeTransform::drop_prefix(value : String) -> ThemeTransform {
DropPrefix(value)
}
///|
pub fn ThemeDocument::map_paths(
self : ThemeDocument,
transform : ThemeTransform,
) -> ThemeDocument {
let tokens : Array[ThemeToken] = []
for token in self.tokens {
match transform_token(token, transform) {
Some(mapped) => tokens.push(mapped)
None => continue
}
}
let aliases : Array[ThemeAlias] = []
for link in self.aliases {
let name = transform_path(link.name.canonical(), transform)
let target = transform_path(link.target.canonical(), transform)
match (name, target) {
(Some(mapped_name), Some(mapped_target)) =>
aliases.push({
name: TokenPath::from_string(mapped_name),
target: TokenPath::from_string(mapped_target),
})
_ => continue
}
}
{ tokens, aliases, provenance: self.provenance }.sorted()
}
///|
pub fn ThemeDocument::select_prefix(
self : ThemeDocument,
prefix : String,
) -> ThemeDocument {
self.select_prefix_kind(prefix)
}
///|
pub fn ThemeDocument::select_prefix_kind(
self : ThemeDocument,
prefix : String,
) -> ThemeDocument {
let wanted = TokenPath::from_string(prefix).canonical()
let tokens = self.tokens.filter(fn(token) {
token.path.canonical() == wanted ||
token.path.canonical().has_prefix(wanted + ".")
})
{ tokens, aliases: self.aliases, provenance: self.provenance }
}
///|
pub fn ThemeDocument::with_prefix(
self : ThemeDocument,
prefix : String,
) -> ThemeDocument {
self.map_paths(ThemeTransform::prefix(prefix))
}
///|
pub fn ThemeDocument::without_prefix(
self : ThemeDocument,
prefix : String,
) -> ThemeDocument {
self.map_paths(ThemeTransform::drop_prefix(prefix))
}
///|
pub fn ThemeDocument::only_kind(
self : ThemeDocument,
kind : TokenKind,
) -> ThemeDocument {
self.map_paths(ThemeTransform::filter_kind(kind))
}
///|
pub fn ThemeDocument::rename_prefix(
self : ThemeDocument,
from : String,
to : String,
) -> ThemeDocument {
self.map_paths(ThemeTransform::rename(from, to))
}
///|
pub fn ThemeDocument::kind_counts(self : ThemeDocument) -> Array[Int] {
let mut colors = 0
let mut numbers = 0
let mut texts = 0
let mut booleans = 0
for token in self.tokens {
match token.kind {
Color => colors += 1
Number => numbers += 1
Text => texts += 1
Boolean => booleans += 1
}
}
[colors, numbers, texts, booleans]
}
///|
pub fn ThemeDocument::kind_label_counts(self : ThemeDocument) -> Array[String] {
let counts = self.kind_counts()
[
"color=" + counts[0].to_string(),
"number=" + counts[1].to_string(),
"text=" + counts[2].to_string(),
"boolean=" + counts[3].to_string(),
]
}
///|
pub fn ThemeDocument::descriptions(self : ThemeDocument) -> Array[String] {
self.tokens.map(fn(token) { token.description })
}
///|
pub fn ThemeDocument::undocumented_paths(self : ThemeDocument) -> Array[String] {
self.tokens
.filter(fn(token) { token.description.trim().to_owned() == "" })
.map(fn(token) { token.path.canonical() })
}
///|
pub fn ThemeDocument::documentation_rate(self : ThemeDocument) -> Int {
if self.tokens.length() == 0 {
100
} else {
(self.tokens.length() - self.undocumented_paths().length()) *
100 /
self.tokens.length()
}
}
///|
pub fn ThemeDocument::has_duplicate_paths(self : ThemeDocument) -> Bool {
for left_index in 0.. String {
let suffix = if self.tokens.length() == 0 { "" } else { "\n" }
self.sorted().paths().join("\n") + suffix
}
///|
pub fn ThemeDocument::to_summary_markdown(self : ThemeDocument) -> String {
"## Theme summary\n\n" +
"Tokens: " +
self.token_count().to_string() +
"\n\n" +
"Aliases: " +
self.aliases.length().to_string() +
"\n\n" +
"Kinds: " +
self.kind_label_counts().join(", ") +
"\n\n" +
"Documentation: " +
self.documentation_rate().to_string() +
"%"
}
///|
fn transform_token(
token : ThemeToken,
transform : ThemeTransform,
) -> ThemeToken? {
match transform {
FilterKind(kind) if token.kind != kind => return None
_ => ()
}
match transform_path(token.path.canonical(), transform) {
None => None
Some(path) => Some({ ..token, path: TokenPath::from_string(path) })
}
}
///|
fn transform_path(path : String, transform : ThemeTransform) -> String? {
match transform {
Prefix(prefix) =>
Some(
if prefix.trim().to_owned() == "" {
path
} else {
prefix + "." + path
},
)
Rename(from, to) => {
let old = TokenPath::from_string(from).canonical()
if path == old {
Some(to)
} else if path.has_prefix(old + ".") {
Some(to + path[old.length():].to_owned())
} else {
Some(path)
}
}
FilterKind(_) => Some(path)
DropPrefix(prefix) => {
let old = TokenPath::from_string(prefix).canonical()
if path == old {
None
} else if path.has_prefix(old + ".") {
Some(path[old.length() + 1:].to_owned())
} else {
Some(path)
}
}
}
}