Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Comments on nodes #1744

Merged
merged 20 commits into from
Aug 2, 2021
117 changes: 114 additions & 3 deletions src/rust/ide/lib/ast/impl/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,132 @@ use crate::crumbs::AmbiguousCrumb;
use crate::crumbs::Located;
use crate::crumbs::MatchCrumb;
use crate::known;
use crate::Shifted;



// ===============
// === Imports ===
// ===============
// ==================================
// === Recognized Macros Keywords ===
// ==================================

/// The keyword introducing a disabled code line.
pub const DISABLING_COMMENT_INTRODUCER:&str = "#";

/// The keyword introducing a documentation block.
pub const DOCUMENTATION_COMMENT_INTRODUCER:&str = "##";

/// The keyword introducing an qualified import declaration. See:
/// https://dev.enso.org/docs/enso/syntax/imports.html#import-syntax
pub const QUALIFIED_IMPORT_KEYWORD:&str = "import";

/// The keyword introducing an unqualified import declaration.
pub const UNQUALIFIED_IMPORT_KEYWORD:&str = "from";

/// The keyword introducing an unqualified export declaration.
pub const QUALIFIED_EXPORT_KEYWORD:&str = "export";



// ================
// === Comments ===
// ================

// === Disable Comments ===

/// Try Interpreting the line as disabling comment. Return the text after `#`.
pub fn as_disable_comment(ast:&Ast) -> Option<String> {
let r#match = crate::known::Match::try_from(ast).ok()?;
mwu-tow marked this conversation as resolved.
Show resolved Hide resolved
let first_segment = &r#match.segs.head;
if crate::identifier::name(&first_segment.head) == Some(DISABLING_COMMENT_INTRODUCER) {
Some(first_segment.body.repr())
} else {
None
}
}

/// Check if this AST is a disabling comment.
pub fn is_disable_comment(ast:&Ast) -> bool {
as_disable_comment(ast).is_some()
}


// === Documentation Comments ===

/// Ast known to be a documentation comment.
#[derive(Clone,Debug)]
pub struct DocCommentInfo {
ast : known::Match,
body : crate::MacroPatternMatch<Shifted<Ast>>,
/// The absolute indent of the block that contains the line with documentation comment.
pub block_indent : usize,
}

impl DocCommentInfo {
/// Try constructing from AST, return None if this is not a documentation comment.
pub fn new(ast:&Ast) -> Option<Self> {
Self::new_indented(ast,0)
}

/// Creates a documentation from Ast and information about indentation of the block it belongs
/// to.
pub fn new_indented(ast:&Ast, block_indent:usize) -> Option<Self> {
let ast = crate::known::Match::try_from(ast).ok()?;
let first_segment = &ast.segs.head;
let introducer = crate::identifier::name(&first_segment.head)?;
let introducer_matches = introducer == DOCUMENTATION_COMMENT_INTRODUCER;
let body = first_segment.body.clone_ref();
introducer_matches.then(|| DocCommentInfo {ast,body,block_indent})
}

/// Get the documentation comment's AST.
pub fn ast(&self) -> known::Match {
self.ast.clone_ref()
}

/// Get the documentation text.
pub fn text(&self) -> String {
// This gets us documentation text, however non-first lines have indent whitespace
// maintained.
let repr = self.body.repr();
let indent = self.block_indent + DOCUMENTATION_COMMENT_INTRODUCER.len();
let old = format!("\n{}", " ".repeat(indent));
let new = "\n";
repr.replace(&old,new)
}

/// Get the documentation text.
mwu-tow marked this conversation as resolved.
Show resolved Hide resolved
pub fn pretty_print_text(text:&str) -> String {
let mut lines = text.lines();
let first_line = lines.next().map(|line| iformat!("##{line}"));
let other_lines = lines .map(|line| iformat!(" {line}"));
let mut out_lines = first_line.into_iter().chain(other_lines);
out_lines.join("\n")
}
}

impl AsRef<Ast> for DocCommentInfo {
fn as_ref(&self) -> &Ast {
self.ast.ast()
}
}

impl Display for DocCommentInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,"{}",self.text())
}
}

/// Check if given Ast stores a documentation comment.
pub fn is_documentation_comment(ast:&Ast) -> bool {
DocCommentInfo::new(ast).is_some()
}



// ===============
// === Imports ===
// ===============

/// If the given AST node is an import declaration, returns it as a Match (which is the only shape
/// capable of storing import declarations). Returns `None` otherwise.
pub fn ast_as_import_match(ast:&Ast) -> Option<known::Match> {
Expand Down
Loading