Use Comment wrapper

This commit is contained in:
Adolfo Ochagavía 2018-10-11 16:25:35 +02:00
parent 27a86cb7df
commit f88e13f539
4 changed files with 78 additions and 36 deletions

View file

@ -58,14 +58,19 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit {
}
pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> {
let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().filter(|it| it.kind() == COMMENT)?;
let prefix = comment_preffix(comment)?;
if offset < comment.range().start() + TextUnit::of_str(prefix) {
let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().and_then(|it| ast::Comment::cast(it))?;
if let ast::CommentFlavor::Multiline = comment.flavor() {
return None;
}
let indent = node_indent(file, comment)?;
let inserted = format!("\n{}{}", indent, prefix);
let prefix = comment.prefix();
if offset < comment.syntax().range().start() + TextUnit::of_str(prefix) + TextUnit::from(1) {
return None;
}
let indent = node_indent(file, comment.syntax())?;
let inserted = format!("\n{}{} ", indent, prefix);
let cursor_position = offset + TextUnit::of_str(&inserted);
let mut edit = EditBuilder::new();
edit.insert(offset, inserted);
@ -75,20 +80,6 @@ pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> {
})
}
fn comment_preffix(comment: SyntaxNodeRef) -> Option<&'static str> {
let text = comment.leaf_text().unwrap();
let res = if text.starts_with("///") {
"/// "
} else if text.starts_with("//!") {
"//! "
} else if text.starts_with("//") {
"// "
} else {
return None;
};
Some(res)
}
fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> {
let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) {
LeafAtOffset::Between(l, r) => {
@ -166,31 +157,27 @@ fn remove_newline(
// Removes: comma, newline (incl. surrounding whitespace)
// Adds: a single whitespace
edit.replace(range, " ".to_string());
} else if prev.kind() == COMMENT && next.kind() == COMMENT {
} else if let (Some(_), Some(next)) = (ast::Comment::cast(prev), ast::Comment::cast(next)) {
// Removes: newline (incl. surrounding whitespace), start of the next comment
// FIXME: I guess it is safe to unwrap here? A comment always has text, right?
let comment_text = next.leaf_text().unwrap().as_str();
let comment_start_length = comment_start_length(comment_text);
let comment_text = next.text();
if let Some(newline_pos) = comment_text.find('\n') {
// Special case for multi-line c-like comments: join the comment content but
// keep the leading `/*`
let newline_offset = next.range().start()
let newline_offset = next.syntax().range().start()
+ TextUnit::from(newline_pos as u32)
+ TextUnit::of_char('\n');
edit.insert(newline_offset, "/*".to_string());
edit.delete(TextRange::from_to(
node.range().start(),
next.range().start() + comment_start_length
next.syntax().range().start() + TextUnit::of_str(next.prefix())
));
} else {
// Single-line comments
edit.delete(TextRange::from_to(
node.range().start(),
next.range().start() + comment_start_length
next.syntax().range().start() + TextUnit::of_str(next.prefix())
));
}
} else {
@ -205,7 +192,7 @@ fn remove_newline(
}
}
// FIXME: do we ever reach this point? What does it mean to be here? I think we should document it
// The node is either the first or the last in the file
let suff = &node_text[TextRange::from_to(
offset - node.range().start() + TextUnit::of_char('\n'),
TextUnit::of_str(node_text),
@ -218,13 +205,6 @@ fn remove_newline(
);
}
// Return the start length of the comment (e.g. 2 for `//` and 3 for `//!`)
fn comment_start_length(_text: &str) -> TextUnit {
// TODO: use the parser here instead of reimplementing comment parsing?
// Otherwise, reimplement comment parsing :)
return TextUnit::from(2);
}
fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
match (left, right) {
(COMMA, R_PAREN) | (COMMA, R_BRACK) => true,

View file

@ -227,6 +227,24 @@ impl<'a> AstNode<'a> for CastExpr<'a> {
impl<'a> CastExpr<'a> {}
// Comment
#[derive(Debug, Clone, Copy)]
pub struct Comment<'a> {
syntax: SyntaxNodeRef<'a>,
}
impl<'a> AstNode<'a> for Comment<'a> {
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
match syntax.kind() {
COMMENT => Some(Comment { syntax }),
_ => None,
}
}
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
}
impl<'a> Comment<'a> {}
// Condition
#[derive(Debug, Clone, Copy)]
pub struct Condition<'a> {

View file

@ -99,6 +99,49 @@ impl<'a> Lifetime<'a> {
}
}
impl<'a> Comment<'a> {
pub fn text(&self) -> SmolStr {
self.syntax().leaf_text().unwrap().clone()
}
pub fn flavor(&self) -> CommentFlavor {
let text = self.text();
if text.starts_with("///") {
CommentFlavor::Doc
} else if text.starts_with("//!") {
CommentFlavor::ModuleDoc
} else if text.starts_with("//") {
CommentFlavor::Line
} else {
CommentFlavor::Multiline
}
}
pub fn prefix(&self) -> &'static str {
self.flavor().prefix()
}
}
#[derive(Debug)]
pub enum CommentFlavor {
Line,
Doc,
ModuleDoc,
Multiline
}
impl CommentFlavor {
pub fn prefix(&self) -> &'static str {
use self::CommentFlavor::*;
match *self {
Line => "//",
Doc => "///",
ModuleDoc => "//!",
Multiline => "/*"
}
}
}
impl<'a> Name<'a> {
pub fn text(&self) -> SmolStr {
let ident = self.syntax().first_child()

View file

@ -537,5 +537,6 @@ Grammar(
"PathSegment": (
options: [ "NameRef" ]
),
"Comment": (),
},
)