hax_frontend_exporter/
comments.rs

1use crate::prelude::*;
2use rustc_lexer::TokenKind;
3use std::fs;
4
5/// Returns a list of (spanned) comments found in file `path`, or an
6/// error if the file at `path` could not be open.
7pub fn comments_of_file(path: PathBuf) -> std::io::Result<Vec<(Span, String)>> {
8    fn clean_comment(comment: &str) -> &str {
9        let comment = if let Some(comment) = comment.strip_prefix("/*") {
10            comment
11                .strip_suffix("*/")
12                .expect("A comment that starts with `/*` should always ends with `*/`")
13        } else {
14            comment
15                .strip_prefix("//")
16                .expect("A comment has to start with `//` or `/*`")
17        };
18        comment.strip_prefix("!").unwrap_or(comment)
19    }
20    let source = &fs::read_to_string(&path)?;
21
22    let mut comments = vec![];
23    let (mut pos, mut line, mut col) = (0, 0, 0);
24    for token in rustc_lexer::tokenize(source, rustc_lexer::FrontmatterAllowed::Yes) {
25        let len = token.len as usize;
26        let sub = &source[pos..(pos + len)];
27        let lo = Loc { line, col };
28        line += sub.chars().filter(|c| matches!(c, '\n')).count();
29        pos += len;
30        if lo.line != line {
31            col = sub.chars().rev().take_while(|c| !matches!(c, '\n')).count();
32        } else {
33            col += len;
34        }
35
36        if let TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } = token.kind {
37            if !sub.starts_with("///") && !sub.starts_with("/**") {
38                let span = Span {
39                    lo,
40                    hi: Loc { line, col },
41                    filename: FileName::Real(RealFileName::LocalPath(path.clone())),
42                    rust_span_data: None,
43                };
44                comments.push((span, clean_comment(sub).to_string()));
45            }
46        }
47    }
48    Ok(comments)
49}