fix: handle escaped chars in doc comments
This commit is contained in:
parent
af1fd88c4d
commit
a543516ea4
@ -5,7 +5,7 @@ pub mod builtin;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{hash::Hash, ops, slice::Iter as SliceIter};
|
||||
use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
@ -573,6 +573,10 @@ impl<'attr> AttrQuery<'attr> {
|
||||
self.attrs().find_map(|attr| attr.string_value())
|
||||
}
|
||||
|
||||
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
|
||||
self.attrs().find_map(|attr| attr.string_value_unescape())
|
||||
}
|
||||
|
||||
pub fn exists(self) -> bool {
|
||||
self.attrs().next().is_some()
|
||||
}
|
||||
|
@ -1917,7 +1917,7 @@ impl ModCollector<'_, '_> {
|
||||
}
|
||||
|
||||
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
||||
let path_attr = attrs.by_key("path").string_value();
|
||||
let path_attr = attrs.by_key("path").string_value_unescape();
|
||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||
let module = &self.item_tree[module_id];
|
||||
match &module.kind {
|
||||
@ -1931,7 +1931,8 @@ impl ModCollector<'_, '_> {
|
||||
module_id,
|
||||
);
|
||||
|
||||
let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr)
|
||||
let Some(mod_dir) =
|
||||
self.mod_dir.descend_into_definition(&module.name, path_attr.as_deref())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@ -1952,8 +1953,12 @@ impl ModCollector<'_, '_> {
|
||||
ModKind::Outline => {
|
||||
let ast_id = AstId::new(self.file_id(), module.ast_id);
|
||||
let db = self.def_collector.db;
|
||||
match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr)
|
||||
{
|
||||
match self.mod_dir.resolve_declaration(
|
||||
db,
|
||||
self.file_id(),
|
||||
&module.name,
|
||||
path_attr.as_deref(),
|
||||
) {
|
||||
Ok((file_id, is_mod_rs, mod_dir)) => {
|
||||
let item_tree = db.file_item_tree(file_id.into());
|
||||
let krate = self.def_collector.def_map.krate;
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
use std::{fmt, ops};
|
||||
use std::{borrow::Cow, fmt, ops};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgExpr;
|
||||
@ -297,6 +297,20 @@ impl Attr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => match it.text.strip_prefix('r') {
|
||||
Some(it) => {
|
||||
it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed)
|
||||
}
|
||||
None => {
|
||||
it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape).map(Cow::Owned)
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path(ident)]
|
||||
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
|
||||
match self.input.as_deref()? {
|
||||
@ -346,6 +360,39 @@ impl Attr {
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape(s: &str) -> Option<String> {
|
||||
let mut res = String::with_capacity(s.len());
|
||||
let mut chars = s.chars();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '\\' {
|
||||
match chars.next()? {
|
||||
'n' => res.push('\n'),
|
||||
'r' => res.push('\r'),
|
||||
't' => res.push('\t'),
|
||||
'\\' => res.push('\\'),
|
||||
'\'' => res.push('\''),
|
||||
'"' => res.push('"'),
|
||||
'0' => res.push('\0'),
|
||||
'x' => {
|
||||
let hex = chars.by_ref().take(2).collect::<String>();
|
||||
let c = u8::from_str_radix(&hex, 16).ok()?;
|
||||
res.push(c as char);
|
||||
}
|
||||
'u' => {
|
||||
let hex = chars.by_ref().take(4).collect::<String>();
|
||||
let c = u32::from_str_radix(&hex, 16).ok()?;
|
||||
res.push(char::from_u32(c)?);
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
res.push(c);
|
||||
}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn collect_attrs(
|
||||
owner: &dyn ast::HasAttrs,
|
||||
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
|
||||
|
@ -91,8 +91,10 @@ pub fn docs_with_rangemap(
|
||||
db: &dyn DefDatabase,
|
||||
attrs: &AttrsWithOwner,
|
||||
) -> Option<(Documentation, DocsRangeMap)> {
|
||||
let docs =
|
||||
attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
|
||||
let docs = attrs
|
||||
.by_key("doc")
|
||||
.attrs()
|
||||
.filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id)));
|
||||
let indent = doc_indent(attrs);
|
||||
let mut buf = String::new();
|
||||
let mut mapping = Vec::new();
|
||||
@ -132,7 +134,7 @@ pub fn docs_with_rangemap(
|
||||
}
|
||||
|
||||
pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
|
||||
let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value());
|
||||
let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape());
|
||||
let indent = doc_indent(attrs);
|
||||
let mut buf = String::new();
|
||||
for doc in docs {
|
||||
@ -270,10 +272,9 @@ fn doc_indent(attrs: &hir::Attrs) -> usize {
|
||||
attrs
|
||||
.by_key("doc")
|
||||
.attrs()
|
||||
.filter_map(|attr| attr.string_value())
|
||||
.filter_map(|attr| attr.string_value()) // no need to use unescape version here
|
||||
.flat_map(|s| s.lines())
|
||||
.filter(|line| !line.chars().all(|c| c.is_whitespace()))
|
||||
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
|
||||
.filter_map(|line| line.chars().position(|c| !c.is_whitespace()))
|
||||
.min()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user