use std::fmt::Write;
pub struct EmmyLuaEmitter {
output: String,
write_file: bool,
}
impl EmmyLuaEmitter {
pub fn new(write_file: bool) -> Self {
Self {
output: String::new(),
write_file,
}
}
pub fn write_line(&mut self, line: &str) {
self.output.push_str(line);
self.output.push('\n');
}
pub fn blank_line(&mut self) {
self.output.push('\n');
}
pub fn write_doc_comment(&mut self, text: &str) {
for line in text.lines() {
let _ = writeln!(self.output, "--- {}", line);
}
}
pub fn write_class(&mut self, name: &str) {
let _ = writeln!(
self.output,
"---@class{} {}",
if self.write_file { "(file)" } else { "" },
name
);
}
#[allow(dead_code)]
pub fn write_class_extends(&mut self, name: &str, parent: &str) {
let _ = writeln!(
self.output,
"---@class{} {} : {}",
if self.write_file { "(file)" } else { "" },
name,
parent
);
}
pub fn write_field(&mut self, name: &str, ty: &str, description: Option<&str>) {
if let Some(desc) = description {
for line in desc.lines() {
let _ = writeln!(self.output, "--- {}", line);
}
}
let formatted_name = if needs_bracket_notation(name) {
format!("[\"{}\"]", name)
} else {
name.to_string()
};
let _ = writeln!(self.output, "---@field {} {}", formatted_name, ty);
}
pub fn write_alias_header(&mut self, name: &str) {
let _ = writeln!(
self.output,
"---@alias{} {}",
if self.write_file { "(file)" } else { "" },
name
);
}
pub fn write_alias_variant(&mut self, value: &str, description: Option<&str>) {
match description {
Some(desc) => {
let _ = writeln!(self.output, "---| \"{}\" # {}", value, desc);
}
None => {
let _ = writeln!(self.output, "---| \"{}\"", value);
}
}
}
pub fn write_alias_type_variant(&mut self, ty: &str, description: Option<&str>) {
match description {
Some(desc) => {
let _ = writeln!(self.output, "---| {} # {}", ty, desc);
}
None => {
let _ = writeln!(self.output, "---| {}", ty);
}
}
}
pub fn write_local_placeholder(&mut self, name: &str) {
let _ = writeln!(self.output, "local {} = {{}}", name);
}
pub fn finish(self) -> String {
self.output
}
}
fn needs_bracket_notation(name: &str) -> bool {
if name.is_empty() {
return true;
}
let first = name.chars().next().unwrap();
if !first.is_ascii_alphabetic() && first != '_' {
return true;
}
!name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}