Support comments on attributes (#5850)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-03-20 16:23:20 +13:00
committed by GitHub
parent 79be72c5f0
commit 461a2c3ab2
319 changed files with 37650 additions and 4752 deletions

View File

@ -8,7 +8,7 @@ use tower_lsp::lsp_types::{
use crate::{
execution::annotations,
parsing::{
ast::types::{Annotation, Node, NonCodeNode, NonCodeValue, VariableKind},
ast::types::{Annotation, Node, NonCodeNode, VariableKind},
token::NumericSuffix,
},
ModuleId,
@ -36,7 +36,7 @@ impl CollectionVisitor {
.unwrap();
self.id += 1;
for (i, n) in parsed.body.iter().enumerate() {
for n in &parsed.body {
match n {
crate::parsing::ast::types::BodyItem::ImportStatement(import) if !import.visibility.is_default() => {
// Only supports glob imports for now.
@ -63,12 +63,11 @@ impl CollectionVisitor {
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
};
// FIXME this association of metadata with items is pretty flaky.
if i == 0 {
dd.with_meta(&parsed.non_code_meta.start_nodes, &var.outer_attrs);
} else if let Some(meta) = parsed.non_code_meta.non_code_nodes.get(&(i - 1)) {
dd.with_meta(meta, &var.outer_attrs);
dd.with_meta(&var.outer_attrs);
for a in &var.outer_attrs {
dd.with_comments(&a.pre_comments);
}
dd.with_comments(n.get_comments());
self.result.push(dd);
}
@ -80,12 +79,11 @@ impl CollectionVisitor {
};
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name));
// FIXME this association of metadata with items is pretty flaky.
if i == 0 {
dd.with_meta(&parsed.non_code_meta.start_nodes, &ty.outer_attrs);
} else if let Some(meta) = parsed.non_code_meta.non_code_nodes.get(&(i - 1)) {
dd.with_meta(meta, &ty.outer_attrs);
dd.with_meta(&ty.outer_attrs);
for a in &ty.outer_attrs {
dd.with_comments(&a.pre_comments);
}
dd.with_comments(n.get_comments());
self.result.push(dd);
}
@ -172,11 +170,19 @@ impl DocData {
}
}
fn with_meta(&mut self, meta: &[Node<NonCodeNode>], attrs: &[Node<Annotation>]) {
fn with_meta(&mut self, attrs: &[Node<Annotation>]) {
match self {
DocData::Fn(f) => f.with_meta(meta, attrs),
DocData::Const(c) => c.with_meta(meta, attrs),
DocData::Ty(t) => t.with_meta(meta, attrs),
DocData::Fn(f) => f.with_meta(attrs),
DocData::Const(c) => c.with_meta(attrs),
DocData::Ty(t) => t.with_meta(attrs),
}
}
fn with_comments(&mut self, comments: &[String]) {
match self {
DocData::Fn(f) => f.with_comments(comments),
DocData::Const(c) => c.with_comments(comments),
DocData::Ty(t) => t.with_comments(comments),
}
}
@ -640,55 +646,20 @@ trait ApplyMeta {
fn doc_hidden(&mut self, doc_hidden: bool);
fn impl_kind(&mut self, impl_kind: annotations::Impl);
fn with_meta(&mut self, meta: &[Node<NonCodeNode>], attrs: &[Node<Annotation>]) {
for attr in attrs {
if let Annotation {
name: None,
properties: Some(props),
..
} = &attr.inner
{
for p in props {
match &*p.key.name {
annotations::IMPL => {
if let Some(s) = p.value.ident_name() {
self.impl_kind(annotations::Impl::from_str(s).unwrap());
}
}
"deprecated" => {
if let Some(b) = p.value.literal_bool() {
self.deprecated(b);
}
}
"doc_hidden" => {
if let Some(b) = p.value.literal_bool() {
self.doc_hidden(b);
}
}
_ => {}
}
}
}
}
let mut comments = Vec::new();
for m in meta {
match &m.value {
NonCodeValue::BlockComment { value, .. } | NonCodeValue::NewLineBlockComment { value, .. } => {
comments.push(value)
}
_ => {}
}
fn with_comments(&mut self, comments: &[String]) {
if comments.iter().all(|s| s.is_empty()) {
return;
}
let mut summary = None;
let mut description = None;
let mut example: Option<(String, ExampleProperties)> = None;
let mut examples = Vec::new();
for l in comments.into_iter().filter(|l| l.starts_with('/')).map(|l| {
if let Some(ll) = l.strip_prefix("/ ") {
for l in comments.iter().filter(|l| l.starts_with("///")).map(|l| {
if let Some(ll) = l.strip_prefix("/// ") {
ll
} else {
&l[1..]
&l[3..]
}
}) {
if description.is_none() && summary.is_none() {
@ -763,6 +734,38 @@ trait ApplyMeta {
examples,
);
}
fn with_meta(&mut self, attrs: &[Node<Annotation>]) {
for attr in attrs {
if let Annotation {
name: None,
properties: Some(props),
..
} = &attr.inner
{
for p in props {
match &*p.key.name {
annotations::IMPL => {
if let Some(s) = p.value.ident_name() {
self.impl_kind(annotations::Impl::from_str(s).unwrap());
}
}
"deprecated" => {
if let Some(b) = p.value.literal_bool() {
self.deprecated(b);
}
}
"doc_hidden" => {
if let Some(b) = p.value.literal_bool() {
self.doc_hidden(b);
}
}
_ => {}
}
}
}
}
}
}
impl ApplyMeta for ConstData {