Skip to main content

yarig/generator/
gen_adoc.rs

1use yarig_macro::add_gen_core;
2
3use crate::{cfg::CfgAdoc, comp::comp_inst::RifInst, generator::casing::ToCasing, parser::remove_rif, rifgen::EnumDef};
4
5use super::{
6    casing::Casing,
7    gen_common::{GeneratorBase, GeneratorBaseSetting, GeneratorCore},
8    trait_doc::{CellKind, GeneratorDoc, LinkKind, TableKind}
9};
10
11#[add_gen_core("adoc")]
12pub struct GeneratorAdoc {
13    /// Currrent component full name
14    comp_fname: String,
15}
16
17impl GeneratorAdoc {
18
19    pub fn new(setting: GeneratorBaseSetting, cfg: CfgAdoc) -> Self {
20        let mut core = GeneratorCore::new(0,setting);
21        if let Some(split) = cfg.split {core.setting.split = split;}
22        // Override gen_inc if defined in the extra settings
23        if let Some(gen_inc) = cfg.gen_inc {
24            core.setting.gen_inc = gen_inc;
25        }
26        if let Some(casing) = cfg.casing {
27            core.setting.casing = casing;
28        }
29        GeneratorAdoc {
30            core,
31            comp_fname: "".to_owned(),
32        }
33    }
34}
35
36impl GeneratorDoc for GeneratorAdoc {
37    const HAS_LAYOUT : bool = false;
38    const SHOW_TYPE  : bool = false;
39    const SHOW_RESET : bool = false;
40    const SHOW_SINGLE_REG : bool = false;
41    const SHOW_UNUSED : bool = true;
42
43    fn set_rif_info(&mut self, rif: &RifInst) {
44        self.comp_fname = rif.name(false);
45    }
46
47    fn write_rif_title(&mut self, idx_rif: (&str,usize), desc: &str) {
48        // println!("Title RIF {} : {} | rifmux={}", idx_rif.1, idx_rif.0, self.comp().is_rifmux);
49        if idx_rif.1 > 0 && self.comp().is_rifmux {
50            self.write("=");
51        }
52        self.write("= ");
53        if idx_rif.1 == 0 || (!self.comp().is_rifmux && idx_rif.1 == 1) {
54            self.write("[[top]]");
55        }
56        self.write(&format!("[[{}]]", idx_rif.0));
57        if desc.is_empty() {
58            self.write(&idx_rif.0.to_casing(Casing::Title));
59        } else {
60            self.write(desc);
61        }
62        self.write("\n\n");
63    }
64
65    fn write_page_title(&mut self, idx_rif: (&str,usize), idx_page: (&str, usize), desc: (String, Option<String>)) {
66        if self.comp().cnt > 1 {
67            self.write(&format!("\n=== [[{}.{}]]", idx_rif.0, idx_page.0));
68            let name = self.sanitize(idx_page.0);
69            if desc.0.is_empty() {
70                self.write(&name);
71            } else {
72                self.write(&format!("{} ({name})", desc.0));
73            }
74        } else if !desc.0.is_empty() {
75            self.write(&self.sanitize(&desc.0));
76            self.write("\n");
77        }
78        if let Some(desc_detail) = desc.1 {
79            self.write_info(&self.sanitize(&desc_detail));
80            self.write("\n");
81        }
82    }
83
84    fn write_reg_title(&mut self, idx_rif: (&str,usize), _idx_page: (&str, usize), idx_reg: (&str, usize), desc: &str, base_addr: Option<u64>) {
85        self.write("\n");
86        if self.comp().is_rifmux {
87            self.write("=");
88        }
89        if self.comp().cnt > 1 {
90            self.write("=");
91        }
92        self.write("=== ");
93        self.write(&format!("[[{}.{}]]", idx_rif.0, idx_reg.0));
94        let name = self.sanitize(idx_reg.0);
95        if desc.is_empty() {
96            self.write(&name);
97        } else {
98            self.write(&format!("{desc} ({name})"));
99        }
100        if let Some(addr) = base_addr {
101            let w = ((self.addr_width()+3) >> 2) as usize;
102            self.write(&format!(" @ 0x{addr:0w$x}"));
103        }
104        self.write("\n");
105    }
106
107    fn write_reg_summary_header(&mut self, _rif: &RifInst) {
108        self.write("\n");
109        if self.comp().is_rifmux {
110            self.write("=");
111        }
112        self.write("== Address mapping\n");
113    }
114
115    fn write_table_title(&mut self, kind: TableKind, title: &str, id: &str) {
116        let caption = match kind {
117            TableKind::Page => {
118                if self.comp().cnt > 1 {
119                    format!("{} registers address mapping", title.to_casing(Casing::Title))
120                } else {
121                    "Registers address mapping".to_owned()
122                }
123            }
124            TableKind::Field => {
125                let regname = remove_rif(self.comp_name())
126                    .to_casing(self.core.setting.casing);
127                format!("Register {regname}.{title}")
128            }
129            _ => self.sanitize(title)
130        };
131
132        self.write(".");
133        self.write(&caption);
134        self.write("\n[grid=rows,frame=none]\n");
135        self.write("[%header, cols=\"");
136        match kind {
137            TableKind::Rifmux  => self.write("2,4,9"),
138            TableKind::RifInst => self.write("2,4,9"),
139            TableKind::Page    => self.write("2,4,9"),
140            TableKind::RegInst => self.write("2,3,2,9"),
141            TableKind::Field   => self.write("1,3,1,2,8"),
142            _  => self.write("}"),
143        }
144        self.write("\"]\n");
145        if !id.is_empty() {
146            self.write(&format!("[[{id}]]\n"));
147        }
148        self.write("|===\n");
149    }
150
151    fn write_table_footer(&mut self, _kind: TableKind) {
152        self.write("|===\n\n");
153    }
154
155    fn write_reg_detail_header(&mut self, _rif: &RifInst) {
156        self.write("\n");
157        if self.comp().is_rifmux {
158            self.write("=");
159        }
160        self.write("== Registers definition\n");
161    }
162
163    fn write_table_cell(&mut self, kind: (TableKind, CellKind), span: usize, txt: &str, id: &str, _tip: Option<String>) {
164        // Call sanitize on non-description cell (already caled on description)
165        let txt = if kind.1!=CellKind::Desc {self.sanitize(txt)} else {txt.to_owned()};
166        if span > 1 {
167            self.write(&format!("{span}+a|{txt}"));
168        } else if kind.0==TableKind::FieldRsvd && txt.is_empty() {
169            match kind.1 {
170                CellKind::Inst => self.write("^|-"),
171                CellKind::Desc => self.write("e|Reserved"),
172                _s => {}
173            }
174        } else {
175            let has_link = kind.1==CellKind::Inst && !id.is_empty()
176                && (kind.0==TableKind::Rifmux || kind.0==TableKind::RifInst || kind.0==TableKind::Page);
177            if kind.1 == CellKind::Desc || has_link {
178                self.write("a");
179            }
180            self.write("|");
181            if has_link {
182                // Handle cross document reference
183                if self.setting().split && kind.0==TableKind::Rifmux {
184                    self.write(&format!("xref:{}.adoc#{id}[{txt}]", self.comp_fname));
185                } else {
186                    self.write(&format!("<<{id},{txt}>>"));
187                }
188            } else {
189                self.write(&txt);
190            }
191        }
192        self.write("\n");
193    }
194
195    fn enum_def_desc(&mut self, def: &EnumDef) -> String {
196        let mut desc = String::with_capacity(def.len() * 16);
197        desc.push_str("\n\n");
198        for entry in &def.values {
199            let entry_name = self.sanitize(&entry.name);
200            let entry_desc = self.sanitize(&entry.description.get(self.is_public()));
201            desc.push_str(&format!(" * {} - {entry_name} : {entry_desc}\n", entry.value));
202        }
203        desc
204    }
205
206    fn sanitize(&self, raw: &str) -> String {
207        let mut txt = String::with_capacity(raw.len());
208        for l in raw.split('\n') {
209            // Insert a line return after each line
210            if !txt.is_empty() {
211                txt.push_str(" +\n");
212            }
213            // Replace starting indentation by non-breaking space
214            let nb_spc = l.chars().take_while(|c| c.is_whitespace()).count();
215            txt.push_str(&" ".repeat(nb_spc));
216            //
217            if let Some(note) = l.strip_prefix("Note: ") {
218                txt.push_str("\nNOTE: ");
219                txt.push_str(note.trim());
220            }
221            else if let Some(note) = l.strip_prefix("Tip: ") {
222                txt.push_str("\nTIP: ");
223                txt.push_str(note.trim());
224            }
225            else if let Some(note) = l.strip_prefix("Important: ") {
226                txt.push_str("\nIMPORTANT: ");
227                txt.push_str(note.trim());
228            }
229            else if let Some(note) = l.strip_prefix("Note: ") {
230                txt.push_str("\nWARNING: ");
231                txt.push_str(note.trim());
232            }
233            else if let Some(note) = l.strip_prefix("Caution: ") {
234                txt.push_str("\nCAUTION: ");
235                txt.push_str(note.trim());
236            } else {
237                txt.push_str(l.trim());
238            }
239        }
240        txt
241    }
242
243    fn write_info(&mut self, info: &str) {
244        self.write(info);
245        self.write("\n\n");
246    }
247
248    fn add_link(&mut self, kind: LinkKind, id: &str) {
249        match kind {
250            LinkKind::Top   => self.write(&format!("<<{id},Back to Top>>\n")),
251            LinkKind::Page  => self.write(&format!("<<{id},Back to register summary>>\n")),
252            LinkKind::Field => {},
253        }
254    }
255
256}