Skip to content

Commit

Permalink
Treat tagged comments as a filter
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Mar 25, 2024
1 parent ba083ad commit aa8d353
Showing 6 changed files with 65 additions and 150 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -260,7 +260,7 @@ zune-jpeg = "0.4"


[profile.dev]
opt-level = 1 # Make debug builds run faster
opt-level = 0 # Make debug builds run faster
panic = "abort" # This leads to better optimizations and smaller binaries (and is the default in Wasm anyways).

[profile.dev.build-override]
1 change: 0 additions & 1 deletion crates/re_types/src/archetypes/image.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

201 changes: 59 additions & 142 deletions crates/re_types_builder/src/docs.rs
Original file line number Diff line number Diff line change
@@ -1,180 +1,97 @@
use std::collections::{BTreeMap, HashSet};

use anyhow::Context as _;
use camino::{Utf8Path, Utf8PathBuf};
use itertools::Itertools as _;

/// A high-level representation of a flatbuffers object's documentation.
#[derive(Debug, Clone)]
pub struct Docs {
/// General documentation for the object.
///
/// Each entry in the vector is a line of comment,
/// excluding the leading space end trailing newline,
/// i.e. the `COMMENT` from `/// COMMENT\n`
///
/// See also [`Docs::tagged_docs`].
doc: Vec<String>,

/// Tagged documentation for the object.
///
/// Each entry in the vector is a line of comment,
/// excluding the leading space end trailing newline,
/// i.e. the `COMMENT` from `/// \py COMMENT\n`
/// All docmentation lines, including the leading tag, if any.
///
/// E.g. the following will be associated with the `py` tag:
/// ```flatbuffers
/// /// \py Something something about how this fields behave in python.
/// my_field: uint32,
/// ```
/// If the tag is the empty string, it means the line is untagged.
///
/// See also [`Docs::doc`].
tagged_docs: BTreeMap<String, Vec<String>>,
/// Each line excludes the leading space and trailing newline.
/// * `/// COMMENT\n` => `("", "COMMENT")`
/// * `/// \py COMMENT\n` => `("py", "COMMENT")`.
lines: Vec<(String, String)>,
}

impl Docs {
pub fn from_raw_docs(
filepath: &Utf8Path,
docs: Option<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<&'_ str>>>,
) -> Self {
// Contents of all the files included using `\include:<path>`.
let mut included_files = BTreeMap::default();

let include_file = |included_files: &mut BTreeMap<_, _>, raw_path: &str| {
let path: Utf8PathBuf = raw_path
.parse()
.with_context(|| format!("couldn't parse included path: {raw_path:?}"))
.unwrap();

let path = filepath.parent().unwrap().join(path);

included_files
.entry(path.clone())
.or_insert_with(|| {
std::fs::read_to_string(&path)
.with_context(|| {
format!("couldn't parse read file at included path: {path:?}")
})
.unwrap()
})
.clone()
};

// language-agnostic docs
let doc = docs
.into_iter()
.flat_map(|doc| doc.into_iter())
// NOTE: discard tagged lines!
.filter(|line| !line.trim().starts_with('\\'))
.flat_map(|line| {
assert!(!line.ends_with('\n'));
assert!(!line.ends_with('\r'));

if let Some((_, path)) = line.split_once("\\include:") {
include_file(&mut included_files, path)
.lines()
.map(|line| line.to_owned())
.collect_vec()
} else if let Some(line) = line.strip_prefix(' ') {
// Removed space between `///` and comment.
vec![line.to_owned()]
let parse_line = |line: &str| {
if let Some(line) = line.strip_prefix(" \\") {
// \tagged comment
let tag = line.split_whitespace().next().unwrap().to_owned();
let line = &line[tag.len()..];
if let Some(line) = line.strip_prefix(' ') {
// Removed space between tag and comment.
(tag, line.to_owned())
} else {
assert!(
line.is_empty(),
"{filepath}: Comments should start with a single space; found {line:?}"
);
vec![line.to_owned()]
assert!(line.is_empty());
(tag, String::new())
}
})
.collect::<Vec<_>>();

// tagged docs, e.g. `\py this only applies to python!`
let tagged_docs = {
let tagged_lines = docs
.into_iter()
.flat_map(|doc| doc.into_iter())
// NOTE: discard _un_tagged lines!
.filter_map(|line| {
let trimmed = line.trim();
trimmed.starts_with('\\').then(|| {
let tag = trimmed.split_whitespace().next().unwrap();
let line = &trimmed[tag.len()..];
let tag = tag[1..].to_owned();
if let Some(line) = line.strip_prefix(' ') {
// Removed space between tag and comment.
(tag, line.to_owned())
} else {
assert!(line.is_empty());
(tag, String::default())
}
})
})
.flat_map(|(tag, line)| {
if let Some((_, path)) = line.split_once("\\include:") {
include_file(&mut included_files, path)
.lines()
.map(|line| (tag.clone(), line.to_owned()))
.collect_vec()
} else {
vec![(tag, line)]
}
})
.collect::<Vec<_>>();

let all_tags: HashSet<_> = tagged_lines.iter().map(|(tag, _)| tag).collect();
let mut tagged_docs = BTreeMap::new();

for cur_tag in all_tags {
} else if let Some(line) = line.strip_prefix(' ') {
// Removed space between `///` and comment.
(String::new(), line.to_owned())
} else {
assert!(
matches!(cur_tag.as_str(), "example" | "py" | "rs" | "cpp"),
"Unsupported tag: \\{cur_tag}"
);

tagged_docs.insert(
cur_tag.clone(),
tagged_lines
.iter()
.filter(|(tag, _)| cur_tag == tag)
.map(|(_, line)| line.clone())
.collect(),
line.is_empty(),
"Comments should start with a single space; found {line:?}"
);
(String::new(), String::new())
}

tagged_docs
};

Self { doc, tagged_docs }
let lines: Vec<(String, String)> = docs
.into_iter()
.flat_map(|doc| doc.into_iter())
.map(parse_line)
.collect();

for (tag, comment) in &lines {
assert!(
matches!(tag.as_str(), "" | "example" | "cpp" | "py" | "rs"),
"Unsupported tag: '\\{tag} {comment}'"
);
}

Self { lines }
}

/// Get all doc lines that start with the given tag.
///
/// For instance, pass `"example"` to get all lines that start with `"\example"`.
pub fn doc_lines_tagged(&self, tag: &str) -> Vec<&str> {
if let Some(lines) = self.tagged_docs.get(tag) {
lines.iter().map(|s| s.as_str()).collect()
} else {
Vec::new()
}
self.lines_with_tag_matching(|t| t == tag)
}

/// Get all doc lines that are untagged.
pub fn untagged(&self) -> Vec<String> {
self.doc_lines_for_untagged_and("")
self.lines_with_tag_matching(|t| t.is_empty())
.iter()
.map(|&s| s.to_owned())
.collect()
}

/// Get all doc lines that are untagged, or match the given tag.
///
/// For instance, pass `"py"` to get all lines that are untagged or starta with `"\py"`.
pub fn doc_lines_for_untagged_and(&self, tag: &str) -> Vec<String> {
let mut lines = self.doc.clone();
self.lines_with_tag_matching(|t| t.is_empty() || t == tag)
.iter()
.map(|&s| s.to_owned())
.collect()
}

lines.extend(
self.tagged_docs
.get(tag)
.unwrap_or(&Vec::new())
.iter()
.cloned(),
);
pub fn lines_with_tag_matching(&self, include_tag: impl Fn(&str) -> bool) -> Vec<&str> {
let mut lines: Vec<&str> = self
.lines
.iter()
.filter_map(|(tag, line)| {
if include_tag(tag) {
Some(line.as_str())
} else {
None
}
})
.collect();

// NOTE: remove duplicated blank lines.
lines.dedup();
8 changes: 4 additions & 4 deletions crates/re_types_builder/src/objects.rs
Original file line number Diff line number Diff line change
@@ -327,7 +327,7 @@ impl Object {
"Bad filepath: {filepath:?}"
);

let docs = Docs::from_raw_docs(&filepath, obj.documentation());
let docs = Docs::from_raw_docs(obj.documentation());
let attrs = Attributes::from_raw_attrs(obj.attributes());
let kind = ObjectKind::from_pkg_name(&pkg_name, &attrs);

@@ -407,7 +407,7 @@ impl Object {
.unwrap();
let filepath = filepath_from_declaration_file(include_dir_path, &virtpath);

let docs = Docs::from_raw_docs(&filepath, enm.documentation());
let docs = Docs::from_raw_docs(enm.documentation());
let attrs = Attributes::from_raw_attrs(enm.attributes());
let kind = ObjectKind::from_pkg_name(&pkg_name, &attrs);

@@ -647,7 +647,7 @@ impl ObjectField {
.unwrap();
let filepath = filepath_from_declaration_file(include_dir_path, &virtpath);

let docs = Docs::from_raw_docs(&filepath, field.documentation());
let docs = Docs::from_raw_docs(field.documentation());

let attrs = Attributes::from_raw_attrs(field.attributes());

@@ -695,7 +695,7 @@ impl ObjectField {
.unwrap();
let filepath = filepath_from_declaration_file(include_dir_path, &virtpath);

let docs = Docs::from_raw_docs(&filepath, val.documentation());
let docs = Docs::from_raw_docs(val.documentation());

let attrs = Attributes::from_raw_attrs(val.attributes());

2 changes: 1 addition & 1 deletion rerun_cpp/src/rerun/archetypes/image.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion rerun_py/rerun_sdk/rerun/archetypes/image.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aa8d353

Please sign in to comment.