"Fossies" - the Fresh Open Source Software Archive

Member "rustc-1.60.0-src/src/librustdoc/theme.rs" (4 Apr 2022, 7439 Bytes) of package /linux/misc/rustc-1.60.0-src.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Rust source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "theme.rs": 1.58.1_vs_1.59.0.

    1 use rustc_data_structures::fx::FxHashSet;
    2 use std::fs;
    3 use std::hash::{Hash, Hasher};
    4 use std::path::Path;
    5 
    6 use rustc_errors::Handler;
    7 
    8 #[cfg(test)]
    9 mod tests;
   10 
   11 #[derive(Debug, Clone, Eq)]
   12 crate struct CssPath {
   13     crate name: String,
   14     crate children: FxHashSet<CssPath>,
   15 }
   16 
   17 // This PartialEq implementation IS NOT COMMUTATIVE!!!
   18 //
   19 // The order is very important: the second object must have all first's rules.
   20 // However, the first is not required to have all of the second's rules.
   21 impl PartialEq for CssPath {
   22     fn eq(&self, other: &CssPath) -> bool {
   23         if self.name != other.name {
   24             false
   25         } else {
   26             for child in &self.children {
   27                 if !other.children.iter().any(|c| child == c) {
   28                     return false;
   29                 }
   30             }
   31             true
   32         }
   33     }
   34 }
   35 
   36 impl Hash for CssPath {
   37     fn hash<H: Hasher>(&self, state: &mut H) {
   38         self.name.hash(state);
   39         for x in &self.children {
   40             x.hash(state);
   41         }
   42     }
   43 }
   44 
   45 impl CssPath {
   46     fn new(name: String) -> CssPath {
   47         CssPath { name, children: FxHashSet::default() }
   48     }
   49 }
   50 
   51 /// All variants contain the position they occur.
   52 #[derive(Debug, Clone, Copy)]
   53 enum Events {
   54     StartLineComment(usize),
   55     StartComment(usize),
   56     EndComment(usize),
   57     InBlock(usize),
   58     OutBlock(usize),
   59 }
   60 
   61 impl Events {
   62     fn get_pos(&self) -> usize {
   63         match *self {
   64             Events::StartLineComment(p)
   65             | Events::StartComment(p)
   66             | Events::EndComment(p)
   67             | Events::InBlock(p)
   68             | Events::OutBlock(p) => p,
   69         }
   70     }
   71 
   72     fn is_comment(&self) -> bool {
   73         matches!(
   74             self,
   75             Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_)
   76         )
   77     }
   78 }
   79 
   80 fn previous_is_line_comment(events: &[Events]) -> bool {
   81     matches!(events.last(), Some(&Events::StartLineComment(_)))
   82 }
   83 
   84 fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
   85     if let Some(&Events::StartComment(_)) = events.last() {
   86         return false;
   87     }
   88     v[pos + 1] == b'/'
   89 }
   90 
   91 fn load_css_events(v: &[u8]) -> Vec<Events> {
   92     let mut pos = 0;
   93     let mut events = Vec::with_capacity(100);
   94 
   95     while pos + 1 < v.len() {
   96         match v[pos] {
   97             b'/' if v[pos + 1] == b'*' => {
   98                 events.push(Events::StartComment(pos));
   99                 pos += 1;
  100             }
  101             b'/' if is_line_comment(pos, v, &events) => {
  102                 events.push(Events::StartLineComment(pos));
  103                 pos += 1;
  104             }
  105             b'\n' if previous_is_line_comment(&events) => {
  106                 events.push(Events::EndComment(pos));
  107             }
  108             b'*' if v[pos + 1] == b'/' => {
  109                 events.push(Events::EndComment(pos + 2));
  110                 pos += 1;
  111             }
  112             b'{' if !previous_is_line_comment(&events) => {
  113                 if let Some(&Events::StartComment(_)) = events.last() {
  114                     pos += 1;
  115                     continue;
  116                 }
  117                 events.push(Events::InBlock(pos + 1));
  118             }
  119             b'}' if !previous_is_line_comment(&events) => {
  120                 if let Some(&Events::StartComment(_)) = events.last() {
  121                     pos += 1;
  122                     continue;
  123                 }
  124                 events.push(Events::OutBlock(pos + 1));
  125             }
  126             _ => {}
  127         }
  128         pos += 1;
  129     }
  130     events
  131 }
  132 
  133 fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
  134     while *pos < events.len() {
  135         if !events[*pos].is_comment() {
  136             return Some(events[*pos]);
  137         }
  138         *pos += 1;
  139     }
  140     None
  141 }
  142 
  143 fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
  144     let mut ret = Vec::with_capacity(3);
  145 
  146     ret.push(events[pos].get_pos());
  147     if pos > 0 {
  148         pos -= 1;
  149     }
  150     loop {
  151         if pos < 1 || !events[pos].is_comment() {
  152             let x = events[pos].get_pos();
  153             if *ret.last().unwrap() != x {
  154                 ret.push(x);
  155             } else {
  156                 ret.push(0);
  157             }
  158             break;
  159         }
  160         ret.push(events[pos].get_pos());
  161         pos -= 1;
  162     }
  163     if ret.len() & 1 != 0 && events[pos].is_comment() {
  164         ret.push(0);
  165     }
  166     ret.iter().rev().cloned().collect()
  167 }
  168 
  169 fn build_rule(v: &[u8], positions: &[usize]) -> String {
  170     minifier::css::minify(
  171         &positions
  172             .chunks(2)
  173             .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or(""))
  174             .collect::<String>()
  175             .trim()
  176             .replace('\n', " ")
  177             .replace('/', "")
  178             .replace('\t', " ")
  179             .replace('{', "")
  180             .replace('}', "")
  181             .split(' ')
  182             .filter(|s| !s.is_empty())
  183             .collect::<Vec<&str>>()
  184             .join(" "),
  185     )
  186     .unwrap_or_else(|_| String::new())
  187 }
  188 
  189 fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet<CssPath> {
  190     let mut paths = Vec::with_capacity(50);
  191 
  192     while *pos < events.len() {
  193         if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
  194             *pos += 1;
  195             break;
  196         }
  197         if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
  198             paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
  199             *pos += 1;
  200         }
  201         while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
  202             if let Some(ref mut path) = paths.last_mut() {
  203                 for entry in inner(v, events, pos).iter() {
  204                     path.children.insert(entry.clone());
  205                 }
  206             }
  207         }
  208         if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
  209             *pos += 1;
  210         }
  211     }
  212     paths.iter().cloned().collect()
  213 }
  214 
  215 crate fn load_css_paths(v: &[u8]) -> CssPath {
  216     let events = load_css_events(v);
  217     let mut pos = 0;
  218 
  219     let mut parent = CssPath::new("parent".to_owned());
  220     parent.children = inner(v, &events, &mut pos);
  221     parent
  222 }
  223 
  224 crate fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
  225     if against.name == other.name {
  226         for child in &against.children {
  227             let mut found = false;
  228             let mut found_working = false;
  229             let mut tmp = Vec::new();
  230 
  231             for other_child in &other.children {
  232                 if child.name == other_child.name {
  233                     if child != other_child {
  234                         get_differences(child, other_child, &mut tmp);
  235                     } else {
  236                         found_working = true;
  237                     }
  238                     found = true;
  239                     break;
  240                 }
  241             }
  242             if !found {
  243                 v.push(format!("  Missing \"{}\" rule", child.name));
  244             } else if !found_working {
  245                 v.extend(tmp.iter().cloned());
  246             }
  247         }
  248     }
  249 }
  250 
  251 crate fn test_theme_against<P: AsRef<Path>>(
  252     f: &P,
  253     against: &CssPath,
  254     diag: &Handler,
  255 ) -> (bool, Vec<String>) {
  256     let data = match fs::read(f) {
  257         Ok(c) => c,
  258         Err(e) => {
  259             diag.struct_err(&e.to_string()).emit();
  260             return (false, vec![]);
  261         }
  262     };
  263 
  264     let paths = load_css_paths(&data);
  265     let mut ret = vec![];
  266     get_differences(against, &paths, &mut ret);
  267     (true, ret)
  268 }