use super::{fresh_token, SyntaxNode, SyntaxToken};
use crate::lex::SyntaxKind;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Comment(pub(crate) SyntaxToken);
impl Comment {
pub(crate) fn cast(token: SyntaxToken) -> Option<Self> {
(token.kind() == SyntaxKind::COMMENT).then_some(Comment(token))
}
pub fn text(&self) -> &str {
self.0.text()
}
pub fn content(&self) -> &str {
let text = self.0.text();
let without_hash = text.strip_prefix('#').unwrap_or(text);
without_hash.strip_prefix(' ').unwrap_or(without_hash)
}
crate::nodes::ast_node_spans!("comment");
pub fn text_range(&self) -> rowan::TextRange {
self.0.text_range()
}
pub fn set_text(&mut self, text: &str) -> Result<(), CommentTextError> {
if !text.starts_with('#') {
return Err(CommentTextError::MissingHash);
}
if text.contains(['\n', '\r']) {
return Err(CommentTextError::LineBreak);
}
let parent = self
.0
.parent()
.expect("a comment token always has a parent node");
let index = self.0.index();
let new_token = fresh_token(SyntaxKind::COMMENT, text);
super::replace_children(&parent, index..index + 1, vec![new_token.into()]);
self.0 = parent
.children_with_tokens()
.nth(index)
.and_then(|element| element.into_token())
.expect("the replacement comment sits at the old comment's index");
Ok(())
}
pub fn set_content(&mut self, content: &str) -> Result<(), CommentTextError> {
self.set_text(&format!("# {content}"))
}
pub fn syntax(&self) -> &SyntaxToken {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommentTextError {
MissingHash,
LineBreak,
}
impl std::fmt::Display for CommentTextError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommentTextError::MissingHash => write!(f, "comment text must start with '#'"),
CommentTextError::LineBreak => write!(f, "comment text must not contain a line break"),
}
}
}
impl std::error::Error for CommentTextError {}
impl std::fmt::Debug for Comment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Comment")
.field("text", &self.text())
.finish()
}
}
impl std::fmt::Display for Comment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.text())
}
}
pub(crate) fn comments(node: &SyntaxNode) -> impl Iterator<Item = Comment> {
node.descendants_with_tokens()
.filter_map(|element| element.into_token())
.filter_map(Comment::cast)
}