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

Reworking nufmt #7

Merged
merged 5 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
✨ parse a file and fmt stripping all whitespace
  • Loading branch information
AucaCoyan committed May 30, 2023
commit d1e15cdfaa502f19b0fddb822279c96b7c0231bc
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ anyhow = "1.0.71"
clap = { version = "4.3.0", optional = true, features = ["unicode", "derive"] }
env_logger = "0.10.0"
log = "0.4.17"
nu-parser = "0.80.0"
nu-protocol = "0.80.0"

[dev-dependencies]
Expand Down
115 changes: 111 additions & 4 deletions src/formatting.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,131 @@
use crate::{config::Config, FileName, Input, Session};
use crate::{config::Config, Input, Session};
use log::{debug, trace};
use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::engine::{self, StateWorkingSet};
use std::fs::File;
use std::io::Write;

impl<'b, T: Write + 'b> Session<'b, T> {
pub fn format_input_inner(&mut self, input: Input) {
println!("formatting ...💭");
let format_result = format_project(input, &self.config);
format_project(input, &self.config);
}
}

// Format an entire crate (or subset of the module tree)
//
// TODO: It is possible that this fucntion return some value.
// For example a Vec[u8], or a String to pass to another function
// which writes the file, or add the indentation.
fn format_project(input: Input, config: &Config) {
debug!("using config:{:?}", config);
// nice place to measure parsing and formatting time
// let mut timer = Timer::start();
// parsing starts

let main_file = input.file_name();
let input_is_stdin = main_file == FileName::Stdin;
let main_file_as_str = main_file.unwrap().as_path().to_str().unwrap();
// TODO: if input is stdin, format the string
// let input_is_stdin = main_file == Input::Text;

// parsing starts
let contents = input.contents();

let engine_state = engine::EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);

let parsed_block = parse(&mut working_set, Some(main_file_as_str), &contents, false);
trace!("parsed block:\n{:?}\n", &parsed_block);
// flat is a list of (Span , Flatshape)
//
// Span is the piece of code. You can stringfy the contents.
// Flatshape is an enum of the type of token read by the AST.
let flat = flatten_block(&working_set, &parsed_block);
trace!("flattened block:\n{:#?}\n", &flat);
// timer = timer.done_parsing()

// formatting starts
let mut writer = File::create(main_file.unwrap()).unwrap();
let mut out: Vec<u8> = vec![];

for (span, shape) in flat {
let mut c_bites = working_set.get_span_contents(span);
let content = String::from_utf8_lossy(c_bites).to_string();
trace!("shape is {shape}");
trace!("shape contents: {:?}", &content);
match shape {
// if its one of these types, just do nothing. Write it away.
FlatShape::String | FlatShape::Int | FlatShape::Nothing => out.extend(c_bites),
FlatShape::List | FlatShape::Record => {
c_bites = trim_ascii_whitespace(c_bites);
let printable = String::from_utf8_lossy(c_bites).to_string();
trace!("stripped the whitespace, result: {:?}", printable);
out.extend(c_bites)
}
FlatShape::Pipe => {
// here you don't have to strip the whitespace.
// The pipe is just a pipe `|`.
//
// return the pipe AND a space after that
out.extend("| ".to_string().bytes())
}
FlatShape::External => {
// External are some key commands
//
// List of what I've found: seq, each, str,
out.extend(c_bites);
// It doen't have a space after it. You have to add it here.
out.extend([b' '].iter());
}
FlatShape::ExternalArg => {
// This shape is the argument of an External command (see previous case).
//
// As a result, ExternalArg may be an entire expression.
// like: "{ |row|\r\n let row_data = (seq ... r\n}"
out.extend(c_bites);
// It doen't have a space after it. You have to add it here.
out.extend([b' '].iter());
}
FlatShape::Garbage => {
// Garbage is not garbage at all
//
// IDK what is it. I groups a bunch of commands like let my_var = 3
out.extend(c_bites);
out = insert_newline(out);
}

_ => (),
}
}
// writing

// just before writing, append a new line to the file.
out = insert_newline(out);
// TODO: check if the last byte is already b'\n'

// now write the file
let file_bites = out.as_slice();
trace!("writing {:?}", out);
writer
.write_all(file_bites)
.expect("something went wrong writing");
trace!("written")

// timer = timer.done_formatting()
}

fn insert_newline(mut bytes: Vec<u8>) -> Vec<u8> {
// If I need cfg windows, then I need \r\n
// let newline = vec![b'\r', b'\n'];
let newline = vec![b'\n'];
bytes.extend(newline.iter());
bytes
}

pub fn trim_ascii_whitespace(x: &[u8]) -> &[u8] {
let from = match x.iter().position(|x| !x.is_ascii_whitespace()) {
Some(i) => i,
None => return &x[0..0],
};
let to = x.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap();
&x[from..=to]
}
19 changes: 15 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,33 @@ impl<'b, T: Write + 'b> Session<'b, T> {
}
}

#[derive(PartialEq)]
// This is getting deprecated. I think
#[derive(Debug, PartialEq)]
pub enum FileName {
Real(PathBuf),
Stdin,
}

#[derive(Debug)]
pub enum Input {
File(PathBuf),
Text(String),
}

impl Input {
fn file_name(&self) -> FileName {
fn file_name(&self) -> Option<&PathBuf> {
match *self {
Input::File(ref file) => FileName::Real(file.clone()),
Input::Text(..) => FileName::Stdin,
Input::File(ref file) => Some(file),
Input::Text(..) => None,
}
}

fn contents(&self) -> Vec<u8> {
match self {
Input::File(path) => std::fs::read(path).expect(
format!("something went wrong reading the file {}", path.display()).as_str(),
),
Input::Text(string) => string.as_bytes().to_vec(),
}
}
}
Expand Down