Skip to content

Commit

Permalink
feat: #111 - Add JSON formatting to Rust CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret authored Apr 28, 2020
1 parent 468dd77 commit ce9b5a6
Show file tree
Hide file tree
Showing 37 changed files with 1,128 additions and 280 deletions.
2 changes: 1 addition & 1 deletion packages/dprint-plugin-jsonc/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl FormatContext {
console_error_panic_hook::set_once();

let global_config = resolve_global_config(&js_map_to_hash_map(&js_global_config)).config;
let config_result = resolve_config(&js_map_to_hash_map(&js_config), &global_config);
let config_result = resolve_config(js_map_to_hash_map(&js_config), &global_config);
FormatContext {
configuration: config_result.config,
diagnostics: config_result.diagnostics,
Expand Down
5 changes: 3 additions & 2 deletions packages/dprint-plugin-typescript/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use dprint_plugin_typescript::configuration::*;
use dprint_plugin_typescript::Formatter;
use wasm_bindgen::prelude::*;
use std::collections::HashMap;
use std::path::PathBuf;

#[wasm_bindgen]
pub struct FormatContext {
Expand All @@ -22,7 +23,7 @@ impl FormatContext {
console_error_panic_hook::set_once();

let global_config = resolve_global_config(&js_map_to_hash_map(&js_global_config)).config;
let config_result = resolve_config(&js_map_to_hash_map(&js_config), &global_config);
let config_result = resolve_config(js_map_to_hash_map(&js_config), &global_config);
let formatter = dprint_plugin_typescript::Formatter::new(config_result.config.clone());
FormatContext {
configuration: config_result.config,
Expand All @@ -42,7 +43,7 @@ impl FormatContext {
}

pub fn format(&self, file_path: &str, file_text: &str) -> Result<Option<String>, JsValue> {
match self.formatter.format_text(file_path, file_text) {
match self.formatter.format_text(&PathBuf::from(file_path), file_text) {
Ok(result) => {
Ok(if result == file_text {
None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function getMissingProjectTypeDiagnostic(config: Configuration): Configur
return {
propertyName,
message: `The "${propertyName}" field is missing. You may specify any of the following possible values in the configuration file according to your `
+ `conscience and that will supress this warning.\n\n`
+ `conscience and that will suppress this warning.\n\n`
+ projectTypeInfo.values.map(value => ` * ${value.name} ${" ".repeat(largestValueName - value.name.length)}${value.description}`).join("\n"),
};
}
2 changes: 1 addition & 1 deletion packages/dprint/src/tests/cli/runCliTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ module.exports.config = {
});

const projectTypeMissingWarningText = `[dprint.config.js]: The "projectType" field is missing. You may specify any of the following possible values `
+ `in the configuration file according to your conscience and that will supress this warning.\n\n`
+ `in the configuration file according to your conscience and that will suppress this warning.\n\n`
+ ` * openSource Dprint is formatting an open source project.\n`
+ ` * commercialSponsored Dprint is formatting a closed source commercial project and your company sponsored dprint.\n`
+ ` * commercialDidNotSponsor Dprint is formatting a closed source commercial project and you want to forever enshrine your name `
Expand Down
10 changes: 6 additions & 4 deletions packages/playground/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ extern crate console_error_panic_hook;
extern crate dprint_plugin_typescript;
extern crate dprint_core;

use std::collections::HashMap;
use std::path::PathBuf;

use dprint_core::configuration::*;
use dprint_plugin_typescript::configuration::*;
use wasm_bindgen::prelude::*;
use std::collections::HashMap;

#[wasm_bindgen]
pub fn resolve_config(configuration: &js_sys::Map) -> String {
Expand All @@ -21,7 +23,7 @@ pub fn format_text(file_text: &str, configuration: &js_sys::Map) -> String {

let configuration = resolve_to_typescript_config(&configuration);
let formatter = dprint_plugin_typescript::Formatter::new(configuration);
match formatter.format_text("./file.tsx", file_text) {
match formatter.format_text(&PathBuf::from("./file.tsx"), file_text) {
Ok(result) => result,
Err(error) => String::from(error),
}
Expand All @@ -39,5 +41,5 @@ fn resolve_to_typescript_config(configuration: &js_sys::Map) -> Configuration {
}

let global_config = resolve_global_config(&HashMap::new()).config;
return dprint_plugin_typescript::configuration::resolve_config(&hash_map, &global_config).config;
}
return dprint_plugin_typescript::configuration::resolve_config(hash_map, &global_config).config;
}
1 change: 1 addition & 0 deletions packages/rust-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod condition_resolvers;
pub mod conditions;
pub mod parser_helpers;
pub mod tokens;
pub mod plugins;

pub use print_items::*;
pub use write_items::*;
Expand Down
59 changes: 59 additions & 0 deletions packages/rust-core/src/plugins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use core::slice::{Iter, IterMut};
use std::path::PathBuf;
use std::collections::HashMap;
use super::configuration::{ConfigurationDiagnostic, GlobalConfiguration};

/// Plugin that can be implemented for use in the CLI.
pub trait Plugin : std::marker::Sync {
/// The name of the plugin.
fn name(&self) -> &'static str;
/// The version of the plugin.
fn version(&self) -> &'static str;
/// Gets the possible keys that can be used in the configuration JSON.
fn config_keys(&self) -> Vec<String>;
/// Initializes the plugin.
fn initialize(&mut self, plugin_config: HashMap<String, String>, global_config: &GlobalConfiguration);
/// Gets whether the specified file should be formatted.
fn should_format_file(&self, file_path: &PathBuf, file_text: &str) -> bool;
/// Gets the configuration as a collection of key value pairs.
fn get_resolved_config(&self) -> String;
/// Gets the configuration diagnostics.
fn get_configuration_diagnostics(&self) -> &Vec<ConfigurationDiagnostic>;
/// Formats the text in memory based on the file path and file text.
fn format_text(&self, file_path: &PathBuf, file_text: &str) -> Result<String, String>;
}

/// A formatter constructed from a collection of plugins.
pub struct Formatter {
plugins: Vec<Box<dyn Plugin>>,
}

impl Formatter {
/// Creates a new formatter
pub fn new(plugins: Vec<Box<dyn Plugin>>) -> Formatter {
Formatter { plugins }
}

/// Iterates over the plugins.
pub fn iter_plugins(&self) -> Iter<'_, Box<dyn Plugin>> {
self.plugins.iter()
}

/// Iterates over the plugins with a mutable iterator.
pub fn iter_plugins_mut(&mut self) -> IterMut<'_, Box<dyn Plugin>> {
self.plugins.iter_mut()
}

/// Formats the file text with one of the plugins.
///
/// Returns the string when a plugin formatted or error. Otherwise None when no plugin was found.
pub fn format_text(&self, file_path: &PathBuf, file_text: &str) -> Result<Option<String>, String> {
for plugin in self.plugins.iter() {
if plugin.should_format_file(file_path, file_text) {
return plugin.format_text(file_path, file_text).map(|x| Some(x));
}
}

Ok(None)
}
}
4 changes: 2 additions & 2 deletions packages/rust-development/Cargo.lock

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

8 changes: 4 additions & 4 deletions packages/rust-development/src/file_system_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::fs::{self};
use std::path::Path;
use std::path::PathBuf;
use super::*;

pub fn get_specs_in_dir(path: &Path, parse_spec_options: &ParseSpecOptions) -> Vec<(String, Spec)> {
pub fn get_specs_in_dir(path: &PathBuf, parse_spec_options: &ParseSpecOptions) -> Vec<(String, Spec)> {
let mut result: Vec<(String, Spec)> = Vec::new();
let spec_files = get_files_in_dir_recursive(&path);
for (file_path, text) in spec_files {
Expand All @@ -23,10 +23,10 @@ pub fn get_specs_in_dir(path: &Path, parse_spec_options: &ParseSpecOptions) -> V
}
}

pub fn get_files_in_dir_recursive(path: &Path) -> Vec<(String, String)> {
pub fn get_files_in_dir_recursive(path: &PathBuf) -> Vec<(String, String)> {
return read_dir_recursively(path);

fn read_dir_recursively(dir_path: &Path) -> Vec<(String, String)> {
fn read_dir_recursively(dir_path: &PathBuf) -> Vec<(String, String)> {
let mut result = Vec::new();

for entry in dir_path.read_dir().expect("read dir failed") {
Expand Down
16 changes: 8 additions & 8 deletions packages/rust-development/src/spec_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use std::fs::{self};

use super::*;
Expand All @@ -19,10 +19,10 @@ pub struct RunSpecsOptions {
}

pub fn run_specs(
directory_path: &Path,
directory_path: &PathBuf,
parse_spec_options: &ParseSpecOptions,
run_spec_options: &RunSpecsOptions,
format_text: impl Fn(&str, &str, &HashMap<String, String>) -> Result<String, String>
format_text: impl Fn(&PathBuf, &str, &HashMap<String, String>) -> Result<String, String>
) {
#[cfg(not(debug_assertions))]
assert_not_fix_failures(run_spec_options);
Expand All @@ -31,23 +31,23 @@ pub fn run_specs(
let test_count = specs.len();
let mut failed_tests = Vec::new();

for (file_path, spec) in specs.iter().filter(|(_, spec)| !spec.skip) {
for (file_path, spec) in specs.into_iter().filter(|(_, spec)| !spec.skip) {
#[cfg(not(debug_assertions))]
assert_spec_not_only(&spec);

let format = |file_text: &str| {
format_text(&spec.file_name, &file_text, &spec.config)
format_text(&PathBuf::from(&spec.file_name), &file_text, &spec.config)
.expect(format!("Could not parse spec '{}' in {}", spec.message, file_path).as_str())
};

let result = format(&spec.file_text);
if result != spec.expected_text {
if run_spec_options.fix_failures {
// very rough, but good enough
let file_path = Path::new(file_path);
let file_text = fs::read_to_string(file_path).expect("Expected to read the file.");
let file_path = PathBuf::from(&file_path);
let file_text = fs::read_to_string(&file_path).expect("Expected to read the file.");
let file_text = file_text.replace(&spec.expected_text.replace("\n", "\r\n"), &result.replace("\n", "\r\n"));
fs::write(file_path, file_text).expect("Expected to write to file.");
fs::write(&file_path, file_text).expect("Expected to write to file.");
} else {
failed_tests.push(FailedTestResult {
file_path: file_path.clone(),
Expand Down
1 change: 1 addition & 0 deletions packages/rust-dprint-plugin-jsonc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ repository = "https://github.com/dprint/dprint"
dprint-core = { path = "../rust-core", version = "0.15.0" }
jsonc-parser = { version = "0.5.0" }
serde = { version = "1.0.88", features = ["derive"] }
serde_json = "1.0"

[dev-dependencies]
dprint-development = { path = "../rust-development" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ use super::Configuration;
///
/// let jsonc_config_map = HashMap::new(); // get a collection of k/v pairs from somewhere
/// let config_result = resolve_config(
/// &jsonc_config_map,
/// jsonc_config_map,
/// &global_config_result.config
/// );
///
/// // check config_result.diagnostics here and use config_result.config
/// ```
pub fn resolve_config(config: &HashMap<String, String>, global_config: &GlobalConfiguration) -> ResolveConfigurationResult<Configuration> {
pub fn resolve_config(config: HashMap<String, String>, global_config: &GlobalConfiguration) -> ResolveConfigurationResult<Configuration> {
let mut diagnostics = Vec::new();
let mut config = config.clone();
let mut config = config;

let resolved_config = Configuration {
line_width: get_value(&mut config, "lineWidth", global_config.line_width.unwrap_or(DEFAULT_GLOBAL_CONFIGURATION.line_width), &mut diagnostics),
Expand Down
2 changes: 2 additions & 0 deletions packages/rust-dprint-plugin-jsonc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod configuration;
mod format_text;
mod parser;
mod plugin;

pub use format_text::format_text;
pub use plugin::JsoncPlugin;
55 changes: 55 additions & 0 deletions packages/rust-dprint-plugin-jsonc/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::collections::HashMap;
use dprint_core::configuration::{ConfigurationDiagnostic, ResolveConfigurationResult, GlobalConfiguration};
use std::path::PathBuf;
use dprint_core::plugins::*;
use super::configuration::{Configuration, resolve_config};
use super::format_text::format_text;

/// JSONC Dprint CLI Plugin.
pub struct JsoncPlugin {
resolve_config_result: Option<ResolveConfigurationResult<Configuration>>,
}

impl JsoncPlugin {
pub fn new() -> JsoncPlugin {
JsoncPlugin {
resolve_config_result: None,
}
}

fn get_resolved_config_result(&self) -> &ResolveConfigurationResult<Configuration> {
self.resolve_config_result.as_ref().expect("Plugin must be initialized.")
}
}

impl Plugin for JsoncPlugin {
fn name(&self) -> &'static str { env!("CARGO_PKG_NAME") }
fn version(&self) -> &'static str { env!("CARGO_PKG_VERSION") }
fn config_keys(&self) -> Vec<String> { vec![String::from("json"), String::from("jsonc")] }

fn initialize(&mut self, plugin_config: HashMap<String, String>, global_config: &GlobalConfiguration) {
self.resolve_config_result = Some(resolve_config(plugin_config, &global_config));
}

fn should_format_file(&self, file_path: &PathBuf, _: &str) -> bool {
if let Some(ext) = file_path.extension().and_then(|e| e.to_str()) {
String::from(ext).to_lowercase() == "json"
} else {
false
}
}

fn get_resolved_config(&self) -> String {
let config = &self.get_resolved_config_result().config;
serde_json::to_string(config).unwrap()
}

fn get_configuration_diagnostics(&self) -> &Vec<ConfigurationDiagnostic> {
&self.get_resolved_config_result().diagnostics
}

fn format_text(&self, _: &PathBuf, file_text: &str) -> Result<String, String> {
let config = &self.get_resolved_config_result().config;
format_text(file_text, config)
}
}
6 changes: 3 additions & 3 deletions packages/rust-dprint-plugin-jsonc/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate dprint_development;
//#[macro_use] extern crate debug_here;

use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
// use std::time::Instant;

use dprint_core::configuration::*;
Expand All @@ -18,11 +18,11 @@ fn test_specs() {
let global_config = resolve_global_config(&HashMap::new()).config;

run_specs(
&Path::new("./tests/specs"),
&PathBuf::from("./tests/specs"),
&ParseSpecOptions { default_file_name: "file.json" },
&RunSpecsOptions { fix_failures: false, format_twice: true },
move |_, file_text, spec_config| {
let config_result = resolve_config(&spec_config, &global_config);
let config_result = resolve_config(spec_config.clone(), &global_config);
ensure_no_diagnostics(&config_result.diagnostics);

format_text(&file_text, &config_result.config)
Expand Down
3 changes: 2 additions & 1 deletion packages/rust-dprint-plugin-typescript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "dprint-plugin-typescript"
description = "TypeScript formatter for dprint."
keywords = ["formatting", "formatter", "typescript"]
version = "0.13.2"
version = "0.14.0"
authors = ["David Sherret <[email protected]>"]
edition = "2018"
license = "MIT"
Expand All @@ -15,6 +15,7 @@ swc_common = "=0.5.9"
swc_ecma_ast = "=0.18.1"
swc_ecma_parser = "=0.21.9"
serde = { version = "1.0.88", features = ["derive"] }
serde_json = "1.0"

[dev-dependencies]
dprint-development = { path = "../rust-development" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ impl ConfigurationBuilder {
/// Gets the final configuration that can be used to format a file.
pub fn build(&self) -> Configuration {
if let Some(global_config) = &self.global_config {
resolve_config(&self.config, global_config).config
resolve_config(self.config.clone(), global_config).config
} else {
let global_config = resolve_global_config(&HashMap::new()).config;
resolve_config(&self.config, &global_config).config
resolve_config(self.config.clone(), &global_config).config
}
}

Expand Down
Loading

0 comments on commit ce9b5a6

Please sign in to comment.