Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Clarinet format #1609

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"components/clarinet-cli",
"components/clarinet-deployments",
"components/clarinet-files",
"components/clarinet-format",
"components/clarinet-utils",
"components/clarinet-sdk-wasm",
"components/clarity-lsp",
Expand Down
1 change: 1 addition & 0 deletions components/clarinet-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ clarity_repl = { package = "clarity-repl", path = "../clarity-repl", features =
] }
clarinet-files = { path = "../clarinet-files", features = ["cli"] }
clarity-lsp = { path = "../clarity-lsp", features = ["cli"] }
clarinet-format = { path = "../clarinet-format" }
clarinet-deployments = { path = "../clarinet-deployments", features = ["cli"] }
hiro-system-kit = { path = "../hiro-system-kit" }
stacks-network = { path = "../stacks-network" }
Expand Down
83 changes: 82 additions & 1 deletion components/clarinet-cli/src/frontend/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use clarinet_files::{
get_manifest_location, FileLocation, NetworkManifest, ProjectManifest, ProjectManifestFile,
RequirementConfig,
};
use clarinet_format::formatter::{ClarityFormatter, Settings};
use clarity_repl::analysis::call_checker::ContractAnalysis;
use clarity_repl::clarity::vm::analysis::AnalysisDatabase;
use clarity_repl::clarity::vm::costs::LimitedCostTracker;
Expand All @@ -39,7 +40,7 @@ use clarity_repl::{analysis, repl, Terminal};
use stacks_network::{self, DevnetOrchestrator};
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, prelude::*};
use std::{env, process};
use toml;

Expand Down Expand Up @@ -94,11 +95,31 @@ enum Command {
/// Get Clarity autocompletion and inline errors from your code editor (VSCode, vim, emacs, etc)
#[clap(name = "lsp", bin_name = "lsp")]
LSP,
/// Format clarity code files
#[clap(name = "format", aliases = &["fmt"], bin_name = "format")]
Formatter(Formatter),
/// Step by step debugging and breakpoints from your code editor (VSCode, vim, emacs, etc)
#[clap(name = "dap", bin_name = "dap")]
DAP,
}

#[derive(Parser, PartialEq, Clone, Debug)]
struct Formatter {
/// Path to clarity files (defaults to ./contracts)
#[clap(long = "path", short = 'p')]
pub code_path: Option<String>,
/// If specified, format only this file
#[clap(long = "file", short = 'f')]
pub file: Option<String>,
#[clap(long = "max-line-length", short = 'l')]
pub max_line_length: Option<usize>,
#[clap(long = "tabs", short = 't')]
/// indentation size, e.g. 2
pub indentation: Option<usize>,
#[clap(long = "dry-run")]
pub dry_run: bool,
}

#[derive(Subcommand, PartialEq, Clone, Debug)]
enum Devnet {
/// Generate package of all required devnet artifacts
Expand Down Expand Up @@ -1180,6 +1201,28 @@ pub fn main() {
process::exit(1);
}
},
Command::Formatter(cmd) => {
let sources = get_source_with_path(cmd.code_path, cmd.file);
let mut settings = Settings::default();

if let Some(max_line_length) = cmd.max_line_length {
settings.max_line_length = max_line_length;
}

if let Some(indentation) = cmd.indentation {
settings.indentation = clarinet_format::formatter::Indentation::Space(indentation);
}
let mut formatter = ClarityFormatter::new(settings);

for (file_path, source) in &sources {
let output = formatter.format(source);
if !cmd.dry_run {
let _ = overwrite_formatted(file_path, output);
} else {
println!("{}", output);
}
}
}
Command::Devnet(subcommand) => match subcommand {
Devnet::Package(cmd) => {
let manifest = load_manifest_or_exit(cmd.manifest_path);
Expand All @@ -1193,6 +1236,44 @@ pub fn main() {
};
}

fn overwrite_formatted(file_path: &String, output: String) -> io::Result<()> {
let mut file = fs::File::create(file_path)?;

file.write_all(output.as_bytes())?;
Ok(())
}

fn get_source_with_path(code_path: Option<String>, file: Option<String>) -> Vec<(String, String)> {
// look for files at the default code path (./contracts/) if
// cmd.code_path is not specified OR if cmd.file is not specified
let path = code_path.unwrap_or_else(|| "./contracts/".to_string());

// Collect file paths and load source code
let files: Vec<String> = match file {
Some(file_name) => vec![format!("{}", file_name)],
None => match fs::read_dir(&path) {
Ok(entries) => entries
.filter_map(Result::ok)
.filter(|entry| entry.path().is_file())
.map(|entry| entry.path().to_string_lossy().into_owned())
.collect(),
Err(message) => {
eprintln!("{}", format_err!(message));
std::process::exit(1)
}
},
};
// Map each file to its source code
files
.into_iter()
.map(|file_path| {
let source = fs::read_to_string(&file_path)
.unwrap_or_else(|_| "// Failed to read file".to_string());
(file_path, source)
})
.collect()
}

fn get_manifest_location_or_exit(path: Option<String>) -> FileLocation {
match get_manifest_location(path) {
Some(manifest_location) => manifest_location,
Expand Down
28 changes: 28 additions & 0 deletions components/clarinet-format/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "clarinet-format"
version = "0.1.0"
edition = "2021"

[dependencies]
# clarity-repl = { path = "../clarity-repl" }
clarity = { workspace = true}

[features]
default = ["cli"]
cli = [
"clarity/canonical",
"clarity/developer-mode",
"clarity/devtools",
"clarity/log",
]
wasm = [
"clarity/wasm",
"clarity/developer-mode",
"clarity/devtools",
]


[lib]
name = "clarinet_format"
path = "src/lib.rs"
crate-type = ["lib"]
Loading
Loading