mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Parse doc comments
This commit is contained in:
parent
4d8cbc4811
commit
3dac73d8d0
5 changed files with 138 additions and 67 deletions
|
@ -407,7 +407,7 @@ pub fn empty_line_before_expr<'a>(expr: &'a Expr<'a>) -> bool {
|
||||||
has_at_least_one_newline = true;
|
has_at_least_one_newline = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommentOrNewline::LineComment(_) => {}
|
CommentOrNewline::LineComment(_) | CommentOrNewline::DocComment(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,11 @@ where
|
||||||
LineComment(comment) => {
|
LineComment(comment) => {
|
||||||
fmt_comment(buf, comment, indent);
|
fmt_comment(buf, comment, indent);
|
||||||
|
|
||||||
|
encountered_comment = true;
|
||||||
|
}
|
||||||
|
DocComment(docs) => {
|
||||||
|
fmt_docs(buf, docs, indent);
|
||||||
|
|
||||||
encountered_comment = true;
|
encountered_comment = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,12 +75,16 @@ where
|
||||||
buf.push('#');
|
buf.push('#');
|
||||||
buf.push_str(comment);
|
buf.push_str(comment);
|
||||||
}
|
}
|
||||||
|
DocComment(docs) => {
|
||||||
|
buf.push_str("##");
|
||||||
|
buf.push_str(docs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match iter.peek() {
|
match iter.peek() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(next_space) => match next_space {
|
Some(next_space) => match next_space {
|
||||||
Newline => {}
|
Newline => {}
|
||||||
LineComment(_) => {
|
LineComment(_) | DocComment(_) => {
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -96,6 +105,9 @@ where
|
||||||
LineComment(comment) => {
|
LineComment(comment) => {
|
||||||
fmt_comment(buf, comment, indent);
|
fmt_comment(buf, comment, indent);
|
||||||
}
|
}
|
||||||
|
DocComment(docs) => {
|
||||||
|
fmt_docs(buf, docs, indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,9 +119,17 @@ fn fmt_comment<'a>(buf: &mut String<'a>, comment: &'a str, indent: u16) {
|
||||||
newline(buf, indent);
|
newline(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_docs<'a>(buf: &mut String<'a>, docs: &'a str, indent: u16) {
|
||||||
|
buf.push_str("##");
|
||||||
|
buf.push_str(docs);
|
||||||
|
|
||||||
|
newline(buf, indent);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_comment<'a>(space: &'a CommentOrNewline<'a>) -> bool {
|
pub fn is_comment<'a>(space: &'a CommentOrNewline<'a>) -> bool {
|
||||||
match space {
|
match space {
|
||||||
CommentOrNewline::Newline => false,
|
CommentOrNewline::Newline => false,
|
||||||
CommentOrNewline::LineComment(_) => true,
|
CommentOrNewline::LineComment(_) => true,
|
||||||
|
CommentOrNewline::DocComment(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_builtins::std::Mode;
|
use roc_builtins::std::{Mode, StdLib};
|
||||||
use roc_builtins::std::StdLib;
|
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::Constraint;
|
||||||
use roc_can::def::Declaration;
|
use roc_can::def::Declaration;
|
||||||
use roc_can::module::{canonicalize_module_defs, ModuleOutput};
|
use roc_can::module::{canonicalize_module_defs, ModuleOutput};
|
||||||
|
|
|
@ -294,6 +294,7 @@ pub enum AssignedField<'a, Val> {
|
||||||
pub enum CommentOrNewline<'a> {
|
pub enum CommentOrNewline<'a> {
|
||||||
Newline,
|
Newline,
|
||||||
LineComment(&'a str),
|
LineComment(&'a str),
|
||||||
|
DocComment(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -202,6 +202,13 @@ pub fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>]
|
||||||
spaces(true, min_indent)
|
spaces(true, min_indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum LineState {
|
||||||
|
Normal,
|
||||||
|
Comment,
|
||||||
|
DocComment,
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn spaces<'a>(
|
fn spaces<'a>(
|
||||||
require_at_least_one: bool,
|
require_at_least_one: bool,
|
||||||
|
@ -213,85 +220,129 @@ fn spaces<'a>(
|
||||||
let mut space_list = Vec::new_in(arena);
|
let mut space_list = Vec::new_in(arena);
|
||||||
let mut chars_parsed = 0;
|
let mut chars_parsed = 0;
|
||||||
let mut comment_line_buf = String::new_in(arena);
|
let mut comment_line_buf = String::new_in(arena);
|
||||||
let mut is_parsing_comment = false;
|
let mut line_state = LineState::Normal;
|
||||||
let mut state = state;
|
let mut state = state;
|
||||||
let mut any_newlines = false;
|
let mut any_newlines = false;
|
||||||
|
|
||||||
for ch in chars {
|
for ch in chars {
|
||||||
chars_parsed += 1;
|
chars_parsed += 1;
|
||||||
|
|
||||||
if is_parsing_comment {
|
match line_state {
|
||||||
match ch {
|
LineState::Normal => {
|
||||||
' ' => {
|
match ch {
|
||||||
// If we're in a line comment, this won't affect indentation anyway.
|
' ' => {
|
||||||
state = state.advance_without_indenting(1)?;
|
// Don't check indentation here; it might not be enough
|
||||||
|
// indentation yet, but maybe it will be after more spaces happen!
|
||||||
|
state = state.advance_spaces(1)?;
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
// No need to check indentation because we're about to reset it anyway.
|
||||||
|
state = state.newline()?;
|
||||||
|
|
||||||
comment_line_buf.push(ch);
|
// Newlines only get added to the list when they're outside comments.
|
||||||
}
|
space_list.push(Newline);
|
||||||
'\n' => {
|
|
||||||
state = state.newline()?;
|
|
||||||
|
|
||||||
// This was a newline, so end this line comment.
|
any_newlines = true;
|
||||||
space_list.push(LineComment(comment_line_buf.into_bump_str()));
|
}
|
||||||
comment_line_buf = String::new_in(arena);
|
'#' => {
|
||||||
|
// Check indentation to make sure we were indented enough
|
||||||
|
// before this comment began.
|
||||||
|
state = state
|
||||||
|
.check_indent(min_indent)
|
||||||
|
.map_err(|(fail, _)| (fail, original_state.clone()))?
|
||||||
|
.advance_without_indenting(1)?;
|
||||||
|
|
||||||
is_parsing_comment = false;
|
// We're now parsing a line comment!
|
||||||
}
|
line_state = LineState::Comment;
|
||||||
nonblank => {
|
}
|
||||||
// Chars can have btye lengths of more than 1!
|
nonblank => {
|
||||||
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
return if require_at_least_one && chars_parsed <= 1 {
|
||||||
|
// We've parsed 1 char and it was not a space,
|
||||||
|
// but we require parsing at least one space!
|
||||||
|
Err(unexpected(nonblank, 0, state.clone(), state.attempting))
|
||||||
|
} else {
|
||||||
|
// First make sure we were indented enough!
|
||||||
|
//
|
||||||
|
// (We only do this if we've encountered any newlines.
|
||||||
|
// Otherwise, we assume indentation is already correct.
|
||||||
|
// It's actively important for correctness that we skip
|
||||||
|
// this check if there are no newlines, because otherwise
|
||||||
|
// we would have false positives for single-line defs.)
|
||||||
|
if any_newlines {
|
||||||
|
state = state
|
||||||
|
.check_indent(min_indent)
|
||||||
|
.map_err(|(fail, _)| (fail, original_state))?;
|
||||||
|
}
|
||||||
|
|
||||||
comment_line_buf.push(nonblank);
|
Ok((space_list.into_bump_slice(), state))
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
LineState::Comment => {
|
||||||
match ch {
|
match ch {
|
||||||
' ' => {
|
' ' => {
|
||||||
// Don't check indentation here; it might not be enough
|
// If we're in a line comment, this won't affect indentation anyway.
|
||||||
// indentation yet, but maybe it will be after more spaces happen!
|
state = state.advance_without_indenting(1)?;
|
||||||
state = state.advance_spaces(1)?;
|
|
||||||
}
|
|
||||||
'\n' => {
|
|
||||||
// No need to check indentation because we're about to reset it anyway.
|
|
||||||
state = state.newline()?;
|
|
||||||
|
|
||||||
// Newlines only get added to the list when they're outside comments.
|
match comment_line_buf.chars().next() {
|
||||||
space_list.push(Newline);
|
Some('#') => {
|
||||||
|
// This is a comment begining with `## ` - that is,
|
||||||
|
// a doc comment.
|
||||||
|
//
|
||||||
|
// (The space is important; otherwise, this is not
|
||||||
|
// a doc comment, but rather something like a
|
||||||
|
// big separator block, e.g. ############)
|
||||||
|
line_state = LineState::DocComment;
|
||||||
|
|
||||||
any_newlines = true;
|
// This is now the beginning of the doc comment.
|
||||||
}
|
comment_line_buf.clear();
|
||||||
'#' => {
|
}
|
||||||
// Check indentation to make sure we were indented enough
|
_ => {
|
||||||
// before this comment began.
|
comment_line_buf.push(ch);
|
||||||
state = state
|
}
|
||||||
.check_indent(min_indent)
|
|
||||||
.map_err(|(fail, _)| (fail, original_state.clone()))?
|
|
||||||
.advance_without_indenting(1)?;
|
|
||||||
|
|
||||||
// We're now parsing a line comment!
|
|
||||||
is_parsing_comment = true;
|
|
||||||
}
|
|
||||||
nonblank => {
|
|
||||||
return if require_at_least_one && chars_parsed <= 1 {
|
|
||||||
// We've parsed 1 char and it was not a space,
|
|
||||||
// but we require parsing at least one space!
|
|
||||||
Err(unexpected(nonblank, 0, state.clone(), state.attempting))
|
|
||||||
} else {
|
|
||||||
// First make sure we were indented enough!
|
|
||||||
//
|
|
||||||
// (We only do this if we've encountered any newlines.
|
|
||||||
// Otherwise, we assume indentation is already correct.
|
|
||||||
// It's actively important for correctness that we skip
|
|
||||||
// this check if there are no newlines, because otherwise
|
|
||||||
// we would have false positives for single-line defs.)
|
|
||||||
if any_newlines {
|
|
||||||
state = state
|
|
||||||
.check_indent(min_indent)
|
|
||||||
.map_err(|(fail, _)| (fail, original_state))?;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
state = state.newline()?;
|
||||||
|
|
||||||
Ok((space_list.into_bump_slice(), state))
|
// This was a newline, so end this line comment.
|
||||||
};
|
space_list.push(LineComment(comment_line_buf.into_bump_str()));
|
||||||
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
|
line_state = LineState::Normal;
|
||||||
|
}
|
||||||
|
nonblank => {
|
||||||
|
// Chars can have btye lengths of more than 1!
|
||||||
|
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
||||||
|
|
||||||
|
comment_line_buf.push(nonblank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LineState::DocComment => {
|
||||||
|
match ch {
|
||||||
|
' ' => {
|
||||||
|
// If we're in a doc comment, this won't affect indentation anyway.
|
||||||
|
state = state.advance_without_indenting(1)?;
|
||||||
|
|
||||||
|
comment_line_buf.push(ch);
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
state = state.newline()?;
|
||||||
|
|
||||||
|
// This was a newline, so end this doc comment.
|
||||||
|
space_list.push(DocComment(comment_line_buf.into_bump_str()));
|
||||||
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
|
line_state = LineState::Normal;
|
||||||
|
}
|
||||||
|
nonblank => {
|
||||||
|
// Chars can have btye lengths of more than 1!
|
||||||
|
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
||||||
|
|
||||||
|
comment_line_buf.push(nonblank);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue