Skip to content

Commit

Permalink
chore: implement import formatting (#4248)
Browse files Browse the repository at this point in the history
# Description

chore: implement import formatting

## Additional Context

#3945 (comment)

## Documentation

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.

# PR Checklist

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

Co-authored-by: kevaundray <kevtheappdev@gmail.com>
Co-authored-by: jfecher <jake@aztecprotocol.com>
  • Loading branch information
3 people authored Feb 8, 2024
1 parent 158c8ce commit a8ffe0f
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 140 deletions.
117 changes: 117 additions & 0 deletions tooling/nargo_fmt/src/items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use noirc_frontend::macros_api::Span;

use crate::{
utils::{comment_len, find_comment_end},
visitor::{FmtVisitor, Shape},
};

#[derive(Debug)]
pub(crate) struct Item {
pub(crate) leading: String,
pub(crate) value: String,
pub(crate) trailing: String,
pub(crate) different_line: bool,
}

impl Item {
pub(crate) fn total_width(&self) -> usize {
comment_len(&self.leading) + self.value.chars().count() + comment_len(&self.trailing)
}

pub(crate) fn is_multiline(&self) -> bool {
self.leading.contains('\n') || self.trailing.contains('\n')
}
}

pub(crate) struct Items<'me, T> {
visitor: &'me FmtVisitor<'me>,
shape: Shape,
elements: std::iter::Peekable<std::vec::IntoIter<T>>,
last_position: u32,
end_position: u32,
}

impl<'me, T: HasItem> Items<'me, T> {
pub(crate) fn new(
visitor: &'me FmtVisitor<'me>,
shape: Shape,
span: Span,
elements: Vec<T>,
) -> Self {
Self {
visitor,
shape,
last_position: span.start() + 1,
end_position: span.end() - 1,
elements: elements.into_iter().peekable(),
}
}
}

impl<T: HasItem> Iterator for Items<'_, T> {
type Item = Item;

fn next(&mut self) -> Option<Self::Item> {
let element = self.elements.next()?;
let element_span = element.span();

let start = self.last_position;
let end = element_span.start();

let is_last = self.elements.peek().is_none();
let next_start = self.elements.peek().map_or(self.end_position, |expr| expr.start());

let (leading, different_line) = self.leading(start, end);
let expr = element.format(self.visitor, self.shape);
let trailing = self.trailing(element_span.end(), next_start, is_last);

Item { leading, value: expr, trailing, different_line }.into()
}
}

impl<'me, T> Items<'me, T> {
pub(crate) fn leading(&mut self, start: u32, end: u32) -> (String, bool) {
let mut different_line = false;

let leading = self.visitor.slice(start..end);
let leading_trimmed = leading.trim();

let starts_with_block_comment = leading_trimmed.starts_with("/*");
let ends_with_block_comment = leading_trimmed.ends_with("*/");
let starts_with_single_line_comment = leading_trimmed.starts_with("//");

if ends_with_block_comment {
let comment_end = leading_trimmed.rfind(|c| c == '/').unwrap();

if leading[comment_end..].contains('\n') {
different_line = true;
}
} else if starts_with_single_line_comment || starts_with_block_comment {
different_line = true;
};

(leading_trimmed.to_string(), different_line)
}

pub(crate) fn trailing(&mut self, start: u32, end: u32, is_last: bool) -> String {
let slice = self.visitor.slice(start..end);
let comment_end = find_comment_end(slice, is_last);
let trailing = slice[..comment_end].trim_matches(',').trim();
self.last_position = start + (comment_end as u32);
trailing.to_string()
}
}

pub(crate) trait HasItem {
fn span(&self) -> Span;

fn format(self, visitor: &FmtVisitor, shape: Shape) -> String;

fn start(&self) -> u32 {
self.span().start()
}

fn end(&self) -> u32 {
self.span().end()
}
}
1 change: 1 addition & 0 deletions tooling/nargo_fmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
/// in both placement and content during the formatting process.
mod config;
pub mod errors;
mod items;
mod rewrite;
mod utils;
mod visitor;
Expand Down
2 changes: 2 additions & 0 deletions tooling/nargo_fmt/src/rewrite.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod array;
mod expr;
mod imports;
mod infix;
mod parenthesized;
mod typ;

pub(crate) use array::rewrite as array;
pub(crate) use expr::{rewrite as expr, rewrite_sub_expr as sub_expr};
pub(crate) use imports::UseTree;
pub(crate) use infix::rewrite as infix;
pub(crate) use parenthesized::rewrite as parenthesized;
pub(crate) use typ::rewrite as typ;
7 changes: 4 additions & 3 deletions tooling/nargo_fmt/src/rewrite/array.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use noirc_frontend::{hir::resolution::errors::Span, token::Token, Expression};

use crate::{
utils::{Expr, FindToken},
items::Item,
utils::FindToken,
visitor::{expr::NewlineMode, FmtVisitor},
};

Expand Down Expand Up @@ -39,12 +40,12 @@ pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec<Expression>, array_spa
let (leading, _) = visitor.format_comment_in_block(leading);
let (trailing, _) = visitor.format_comment_in_block(trailing);

result.push(Expr { leading, value: item, trailing, different_line: false });
result.push(Item { leading, value: item, trailing, different_line: false });
}

let slice = visitor.slice(last_position..end_position);
let (comment, _) = visitor.format_comment_in_block(slice);
result.push(Expr {
result.push(Item {
leading: "".into(),
value: "".into(),
trailing: comment,
Expand Down
115 changes: 115 additions & 0 deletions tooling/nargo_fmt/src/rewrite/imports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use noirc_frontend::{PathKind, UseTreeKind};

use crate::{
items::Item,
visitor::{
expr::{format_exprs, Tactic},
FmtVisitor, Shape,
},
};

#[derive(Debug)]
pub(crate) enum UseSegment {
Ident(String, Option<String>),
List(Vec<UseTree>),
Dep,
Crate,
}

impl UseSegment {
fn rewrite(&self, visitor: &FmtVisitor, shape: Shape) -> String {
match self {
UseSegment::Ident(ident, None) => ident.clone(),
UseSegment::Ident(ident, Some(rename)) => format!("{ident} as {rename}"),
UseSegment::List(use_tree_list) => {
let mut nested_shape = shape;
nested_shape.indent.block_indent(visitor.config);

let items: Vec<_> = use_tree_list
.iter()
.map(|item| Item {
leading: String::new(),
value: item.rewrite(visitor, shape).clone(),
trailing: String::new(),
different_line: false,
})
.collect();

let list_str =
format_exprs(visitor.config, Tactic::Mixed, false, items, nested_shape, true);

if list_str.contains('\n') {
format!(
"{{\n{}{list_str}\n{}}}",
nested_shape.indent.to_string(),
shape.indent.to_string()
)
} else {
format!("{{{list_str}}}")
}
}
UseSegment::Dep => "dep".into(),
UseSegment::Crate => "crate".into(),
}
}
}

#[derive(Debug)]
pub(crate) struct UseTree {
path: Vec<UseSegment>,
}

impl UseTree {
pub(crate) fn from_ast(use_tree: noirc_frontend::UseTree) -> Self {
let mut result = UseTree { path: vec![] };

match use_tree.prefix.kind {
PathKind::Crate => result.path.push(UseSegment::Crate),
PathKind::Dep => result.path.push(UseSegment::Dep),
PathKind::Plain => {}
};

result.path.extend(
use_tree
.prefix
.segments
.into_iter()
.map(|segment| UseSegment::Ident(segment.to_string(), None)),
);

match use_tree.kind {
UseTreeKind::Path(name, alias) => {
result.path.push(UseSegment::Ident(
name.to_string(),
alias.map(|rename| rename.to_string()),
));
}
UseTreeKind::List(list) => {
let segment = UseSegment::List(list.into_iter().map(UseTree::from_ast).collect());
result.path.push(segment);
}
}

result
}

pub(crate) fn rewrite_top_level(&self, visitor: &FmtVisitor, shape: Shape) -> String {
format!("use {};", self.rewrite(visitor, shape))
}

fn rewrite(&self, visitor: &FmtVisitor, shape: Shape) -> String {
let mut result = String::new();

let mut iter = self.path.iter().peekable();
while let Some(segment) = iter.next() {
let segment_str = segment.rewrite(visitor, shape);
result.push_str(&segment_str);

if iter.peek().is_some() {
result.push_str("::");
}
}

result
}
}
Loading

0 comments on commit a8ffe0f

Please sign in to comment.