///|
let version_line : String = "Pygments version \{@pygments.version}, (c) 2006-present by Georg Brandl, Matthäus Chajdas and contributors."
///|
/// Python's `docstring_headline`: the first paragraph of a docstring,
/// joined into one line.
fn docstring_headline(doc : String) -> String {
let res = []
for line in doc.trim().split("\n") {
let l = line.trim()
if l.length() == 0 {
break
}
res.push(l.to_owned())
}
res.join(" ").trim().to_owned()
}
///|
/// Python's `str` ordering: lexicographic by code point.
fn py_compare(a : String, b : String) -> Int32 {
let xs = a.to_array()
let ys = b.to_array()
for i in 0..<@cmp.minimum(xs.length(), ys.length()) {
if xs[i] != ys[i] {
return xs[i].to_int() - ys[i].to_int()
}
}
xs.length() - ys.length()
}
///|
/// Sort key comparison on tuples of strings (Python tuple ordering).
fn compare_rows(
a : (String, String, String),
b : (String, String, String),
) -> Int32 {
let c = py_compare(a.0, b.0)
if c != 0 {
return c
}
let c = py_compare(a.1, b.1)
if c != 0 {
return c
}
py_compare(a.2, b.2)
}
///|
/// Python's `_print_list(what)`.
fn list_what(what : String) -> String {
let sb = StringBuilder()
match what {
"lexer" => {
sb.write_string("\nLexers:\n~~~~~~~\n")
let rows = @lexers.all_lexers.map(e => {
let info = e.0
(
info.aliases.join(", ") + ":",
info.name,
if info.filenames.is_empty() {
""
} else {
"(filenames " + info.filenames.join(", ") + ")"
},
)
})
rows.sort_by(compare_rows)
for r in rows {
sb.write_string("* \{r.0}\n \{r.1} \{r.2}\n")
}
}
"formatter" => {
sb.write_string("\nFormatters:\n~~~~~~~~~~~\n")
let rows = @formatters.get_all_formatters().map(f => {
(
f.aliases.join(", ") + ":",
docstring_headline(f.description),
if f.filenames.is_empty() {
""
} else {
"(filenames " + f.filenames.join(", ") + ")"
},
)
})
rows.sort_by(compare_rows)
for r in rows {
sb.write_string("* \{r.0}\n \{r.1} \{r.2}\n")
}
}
"filter" => {
sb.write_string("\nFilters:\n~~~~~~~~\n")
for name in @filters.get_all_filters() {
let doc = filter_docs.get(name).unwrap_or("")
sb.write_string("* \{name}:\n \{docstring_headline(doc)}\n")
}
}
_ => {
sb.write_string("\nStyles:\n~~~~~~~\n")
for name in @styles.get_all_styles() {
let doc = style_docs.get(name).unwrap_or("")
sb.write_string("* \{name}:\n \{docstring_headline(doc)}\n")
}
}
}
sb.to_string()
}
///|
fn json_strings(xs : Array[String]) -> Json {
Json::array(xs.map(x => Json::string(x)))
}
///|
/// Python's `_print_list_as_json(requested_items)`.
fn list_as_json(kinds : Array[String]) -> String {
let result : Map[String, Json] = Map([])
if kinds.contains("lexer") {
let info : Map[String, Json] = Map([])
for e in @lexers.all_lexers {
info[e.0.name] = Json::object(
Map::from_array([
("aliases", json_strings(e.0.aliases)),
("filenames", json_strings(e.0.filenames)),
("mimetypes", json_strings(e.0.mimetypes)),
]),
)
}
result["lexers"] = Json::object(info)
}
if kinds.contains("formatter") {
let info : Map[String, Json] = Map([])
for f in @formatters.get_all_formatters() {
info[f.name] = Json::object(
Map::from_array([
("aliases", json_strings(f.aliases)),
("filenames", json_strings(f.filenames)),
("doc", Json::string(docstring_headline(f.description))),
]),
)
}
result["formatters"] = Json::object(info)
}
if kinds.contains("filter") {
let info : Map[String, Json] = Map([])
for name in @filters.get_all_filters() {
let doc = filter_docs.get(name).unwrap_or("")
info[name] = Json::object(
Map::from_array([("doc", Json::string(docstring_headline(doc)))]),
)
}
result["filters"] = Json::object(info)
}
if kinds.contains("style") {
let info : Map[String, Json] = Map([])
for name in @styles.get_all_styles() {
let doc = style_docs.get(name).unwrap_or("")
info[name] = Json::object(
Map::from_array([("doc", Json::string(docstring_headline(doc)))]),
)
}
result["styles"] = Json::object(info)
}
py_json(Json::object(result))
}
///|
/// Python's `json.dumps` with default settings (`", "`/`": "` separators,
/// ASCII-only output).
fn py_json(j : Json) -> String {
let sb = StringBuilder()
fn go(j : Json) -> Unit {
match j {
Object(m) => {
sb.write_char('{')
let mut first = true
for k, v in m {
if !first {
sb.write_string(", ")
}
first = false
go(Json::string(k))
sb.write_string(": ")
go(v)
}
sb.write_char('}')
}
Array(xs) => {
sb.write_char('[')
for i, x in xs {
if i > 0 {
sb.write_string(", ")
}
go(x)
}
sb.write_char(']')
}
String(s) => {
sb.write_char('"')
for i in 0.. sb.write_string("\\\"")
'\\' => sb.write_string("\\\\")
'\n' => sb.write_string("\\n")
'\r' => sb.write_string("\\r")
'\t' => sb.write_string("\\t")
0x08 => sb.write_string("\\b")
0x0c => sb.write_string("\\f")
_ =>
if c < 0x20 || c > 0x7e {
let hex = c.to_string(radix=16)
sb.write_string(
"\\u" +
"0000".unsafe_substring(start=0, end=4 - hex.length()) +
hex,
)
} else {
sb.write_char(c.unsafe_to_char())
}
}
}
sb.write_char('"')
}
_ => sb.write_string(j.stringify())
}
}
go(j)
sb.to_string()
}
///|
/// Python's `_print_help(what, name)`; `None` if not found.
fn help_on(what : String, name : String) -> String? {
match what {
"lexer" =>
try @lexers.find_lexer_class_by_name(name) catch {
_ => None
} noraise {
entry => Some("Help on the \{entry.0.name} lexer:\n\{entry.0.doc}\n")
}
"formatter" =>
match @formatters.find_formatter_class(name) {
Some((f, _)) =>
Some(
"Help on the \{f.name} formatter:\n\{formatter_docs.get(f.name).unwrap_or("")}\n",
)
None => None
}
_ =>
match filter_docs.get(name) {
Some(doc) => Some("Help on the \{name} filter:\n\{doc}\n")
None => None
}
}
}