Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ jobs:
components: rustfmt
- run: cargo fmt --all -- --check

runefmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: shopt -s globstar ; cargo run --bin rune -- fmt --check **/*.rn

clippy:
runs-on: ubuntu-latest
steps:
Expand Down
3 changes: 2 additions & 1 deletion crates/rune/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ emit = ["codespan-reporting"]
bench = []
workspace = ["toml", "semver", "relative-path", "serde-hashkey"]
doc = ["rust-embed", "handlebars", "pulldown-cmark", "syntect"]
cli = ["doc", "bincode", "atty", "tracing-subscriber", "anyhow/std", "clap", "webbrowser", "capture-io", "disable-io", "languageserver"]
cli = ["doc", "bincode", "atty", "tracing-subscriber", "anyhow/std", "clap", "webbrowser", "capture-io", "disable-io", "languageserver", "fmt"]
languageserver = ["lsp", "ropey", "percent-encoding", "url", "serde_json", "tokio", "workspace", "doc"]
capture-io = ["parking_lot"]
disable-io = []
fmt = []

[dependencies]
rune-macros = { version = "=0.12.3", path = "../rune-macros" }
Expand Down
8 changes: 5 additions & 3 deletions crates/rune/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ mod token;
pub(super) mod utils;
mod vis;

pub use self::attribute::Attribute;
pub use self::attribute::{AttrStyle, Attribute};
pub use self::block::Block;
pub use self::condition::Condition;
pub use self::expr::Expr;
Expand All @@ -179,7 +179,7 @@ pub use self::expr_match::{ExprMatch, ExprMatchBranch};
pub use self::expr_object::{ExprObject, FieldAssign, ObjectIdent, ObjectKey};
pub use self::expr_range::{ExprRange, ExprRangeLimits};
pub use self::expr_return::ExprReturn;
pub use self::expr_select::{ExprSelect, ExprSelectBranch};
pub use self::expr_select::{ExprSelect, ExprSelectBranch, ExprSelectPatBranch};
pub use self::expr_try::ExprTry;
pub use self::expr_tuple::ExprTuple;
pub use self::expr_unary::{ExprUnary, UnOp};
Expand Down Expand Up @@ -208,7 +208,9 @@ pub use self::lit_number::LitNumber;
pub use self::lit_str::LitStr;
pub use self::local::Local;
pub use self::macro_call::MacroCall;
pub use self::pat::{Pat, PatBinding, PatLit, PatObject, PatPath, PatTuple, PatVec};
pub use self::pat::{
Pat, PatBinding, PatIgnore, PatLit, PatObject, PatPath, PatRest, PatTuple, PatVec,
};
pub use self::path::{Path, PathKind, PathSegment, PathSegmentExpr};
pub use self::span::{ByteIndex, Span};
pub use self::spanned::{OptionSpanned, Spanned};
Expand Down
23 changes: 21 additions & 2 deletions crates/rune/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
mod benches;
mod check;
mod doc;
mod format;
mod languageserver;
mod loader;
mod run;
Expand Down Expand Up @@ -193,6 +194,8 @@ enum Command {
Bench(benches::Flags),
/// Run the designated script
Run(run::Flags),
/// Format the provided file
Fmt(format::Flags),
/// Run a language server.
LanguageServer(languageserver::Flags),
}
Expand All @@ -202,6 +205,7 @@ impl Command {
match self {
Command::Check(..) => {}
Command::Doc(..) => {}
Command::Fmt(..) => {}
Command::Test(..) => {
c.test = true;
}
Expand All @@ -219,6 +223,7 @@ impl Command {
match self {
Command::Check(..) => "Checking",
Command::Doc(..) => "Building documentation",
Command::Fmt(..) => "Formatting files",
Command::Test(..) => "Testing",
Command::Bench(..) => "Benchmarking",
Command::Run(..) => "Running",
Expand All @@ -230,6 +235,7 @@ impl Command {
match self {
Command::Check(args) => &args.shared,
Command::Doc(args) => &args.shared,
Command::Fmt(args) => &args.shared,
Command::Test(args) => &args.shared,
Command::Bench(args) => &args.shared,
Command::Run(args) => &args.shared,
Expand Down Expand Up @@ -388,8 +394,11 @@ impl Args {
options.test(true);
options.bytecode(false);
}
Command::Bench(_) | Command::Doc(..) | Command::Run(_) | Command::LanguageServer(_) => {
}
Command::Bench(_)
| Command::Doc(..)
| Command::Run(_)
| Command::LanguageServer(_)
| Command::Fmt(..) => {}
}

for option in &self.cmd.shared().compiler_options {
Expand Down Expand Up @@ -663,6 +672,16 @@ where
}
}
Command::Doc(flags) => return doc::run(io, entry, c, flags, options, entrys),
Command::Fmt(flags) => {
let mut paths = vec![];
for e in entrys {
for path in e.paths {
paths.push(path);
}
}

return format::run(io, &paths, flags);
}
Command::Test(flags) => {
for e in entrys {
for path in &e.paths {
Expand Down
101 changes: 101 additions & 0 deletions crates/rune/src/cli/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use anyhow::{Context, Result};
use clap::Parser;
use std::io::Write;
use std::path::PathBuf;

use crate::cli::{ExitCode, Io, SharedFlags};
use crate::termcolor::WriteColor;
use crate::Source;

#[derive(Parser, Debug, Clone)]
pub(super) struct Flags {
/// Exit with a non-zero exit-code even for warnings
#[arg(long)]
warnings_are_errors: bool,

#[command(flatten)]
pub(super) shared: SharedFlags,

#[arg(long)]
check: bool,
}

pub(super) fn run(io: &mut Io<'_>, paths: &[PathBuf], flags: &Flags) -> Result<ExitCode> {
let mut red = crate::termcolor::ColorSpec::new();
red.set_fg(Some(crate::termcolor::Color::Red));

let mut green = crate::termcolor::ColorSpec::new();
green.set_fg(Some(crate::termcolor::Color::Green));

let mut yellow = crate::termcolor::ColorSpec::new();
yellow.set_fg(Some(crate::termcolor::Color::Yellow));

let mut succeeded = 0;
let mut failed = 0;
let mut unchanged = 0;

for path in paths {
let source =
Source::from_path(path).with_context(|| format!("reading file: {}", path.display()))?;

match crate::fmt::layout_source(&source) {
Ok(val) => {
if val == source.as_str() {
if !flags.check {
io.stdout.set_color(&yellow)?;
write!(io.stdout, "== ")?;
io.stdout.reset()?;
writeln!(io.stdout, "{}", path.display())?;
}

unchanged += 1;
} else {
succeeded += 1;
io.stdout.set_color(&green)?;
write!(io.stdout, "++ ")?;
io.stdout.reset()?;
writeln!(io.stdout, "{}", path.display())?;
if !flags.check {
std::fs::write(path, &val)?;
}
}
}
Err(err) => {
failed += 1;
io.stdout.set_color(&red)?;
write!(io.stdout, "!! ")?;
io.stdout.reset()?;
writeln!(io.stdout, "{}: {}", path.display(), err)?;
}
}
}

io.stdout.set_color(&yellow)?;
write!(io.stdout, "{}", unchanged)?;
io.stdout.reset()?;
writeln!(io.stdout, " unchanged")?;
io.stdout.set_color(&green)?;
write!(io.stdout, "{}", succeeded)?;
io.stdout.reset()?;
writeln!(io.stdout, " succeeded")?;
io.stdout.set_color(&red)?;
write!(io.stdout, "{}", failed)?;
io.stdout.reset()?;
writeln!(io.stdout, " failed")?;

if flags.check && succeeded > 0 {
io.stdout.set_color(&red)?;
write!(
io.stdout,
"Exiting with failure due to `--check` flag and unformatted files."
)?;
io.stdout.reset()?;
return Ok(ExitCode::Failure);
}

if failed > 0 {
return Ok(ExitCode::Failure);
}

Ok(ExitCode::Success)
}
34 changes: 34 additions & 0 deletions crates/rune/src/fmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! Helper to format Rune code.

mod comments;
mod error;
mod indent_writer;
mod printer;
mod whitespace;

use crate::ast;
use crate::parse::{Parse, Parser};
use crate::{Source, SourceId};

use self::error::FormattingError;
use self::printer::Printer;

/// Format the given contents.
pub fn layout_string(contents: String) -> Result<String, FormattingError> {
let s = Source::new("<memory>", contents);
layout_source(&s)
}

/// Format the given source.
pub fn layout_source(source: &Source) -> Result<String, FormattingError> {
let mut parser = Parser::new(source.as_str(), SourceId::new(0), true);

let ast = ast::File::parse(&mut parser)?;
let mut printer: Printer = Printer::new(source)?;

printer.visit_file(&ast)?;

let res = printer.commit().trim().to_owned() + "\n";

Ok(res)
}
118 changes: 118 additions & 0 deletions crates/rune/src/fmt/comments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! Extract comments from source code.

#[cfg(test)]
mod tests;

use std::str::CharIndices;

use crate::ast::Span;

use super::error::FormattingError;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum CommentKind {
Line,
Block,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(super) struct Comment {
pub(super) kind: CommentKind,
pub(super) span: Span,
pub(super) on_new_line: bool,
}

pub(super) fn parse_comments(input: &str) -> Result<Vec<Comment>, FormattingError> {
let mut comments = Vec::new();

let mut chars = input.char_indices();

let mut in_string = false;
let mut in_char = false;
let mut in_template = false;
let mut on_new_line = true;

while let Some((idx, c)) = chars.next() {
match c {
'/' if !in_string && !in_char && !in_template => match chars.clone().next() {
Some((_, '/')) => {
let end = parse_line_comment(&mut chars);

if !input[idx..end].starts_with("///") && !input[idx..end].starts_with("//!") {
comments.push(Comment {
on_new_line,
kind: CommentKind::Line,
span: Span::new(idx, end),
});
}
}
Some((_, '*')) => {
let end = parse_block_comment(&mut chars).ok_or(FormattingError::Eof)?;

if !input[idx..end].starts_with("/**") && !input[idx..end].starts_with("/*!") {
comments.push(Comment {
on_new_line,
kind: CommentKind::Block,
span: Span::new(idx, end),
});
}
}
_ => {}
},
'"' => {
on_new_line = false;
if !in_char && !in_template {
in_string = !in_string;
}
}
'\'' => {
on_new_line = false;
if !in_string && !in_template {
in_char = !in_char;
}
}
'`' => {
on_new_line = false;
if !in_string && !in_char {
in_template = !in_template;
}
}
'\n' => {
on_new_line = true;
}
c if c.is_whitespace() => {}
_ => {
on_new_line = false;
}
}
}

Ok(comments)
}

fn parse_line_comment(chars: &mut CharIndices<'_>) -> usize {
let mut last_i = 0;

for (i, c) in chars.by_ref() {
match c {
'\n' => return i,
_ => {
last_i = i;
}
}
}

last_i + 1
}

fn parse_block_comment(chars: &mut CharIndices<'_>) -> Option<usize> {
while let Some((_, c)) = chars.next() {
if c == '*' {
if let Some((_, '/')) = chars.clone().next() {
return Some(chars.next()?.0);
}
}
}

None
}
Loading