Skip to content

Commit

Permalink
feat(rescript): support rescript format
Browse files Browse the repository at this point in the history
  • Loading branch information
hougesen committed Mar 18, 2024
1 parent e7a8fd4 commit ee24b4d
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ mdsf init
| OCaml | `ocamlformat` |
| Protobuf | `clang-format` |
| Python | `ruff`, `black`, `blue`, `yapf`, `autopep8`, `isort`, `usort` |
| ReScript | `rescript_format` |
| Roc | `roc_format` |
| Ruby | `rubocop` |
| Rust | `rustfmt` |
Expand Down
40 changes: 40 additions & 0 deletions schemas/v0.0.1/mdsf.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@
}
]
},
"rescript": {
"default": {
"enabled": true,
"formatter": "rescript_format"
},
"allOf": [
{
"$ref": "#/definitions/Lang_for_ReScript"
}
]
},
"roc": {
"default": {
"enabled": true,
Expand Down Expand Up @@ -738,6 +749,18 @@
}
}
},
"Lang_for_ReScript": {
"type": "object",
"required": ["enabled", "formatter"],
"properties": {
"enabled": {
"type": "boolean"
},
"formatter": {
"$ref": "#/definitions/MdsfFormatter_for_ReScript"
}
}
},
"Lang_for_Roc": {
"type": "object",
"required": ["enabled", "formatter"],
Expand Down Expand Up @@ -1178,6 +1201,19 @@
}
]
},
"MdsfFormatter_for_ReScript": {
"anyOf": [
{
"$ref": "#/definitions/ReScript"
},
{
"type": "array",
"items": {
"$ref": "#/definitions/MdsfFormatter_for_ReScript"
}
}
]
},
"MdsfFormatter_for_Roc": {
"anyOf": [
{
Expand Down Expand Up @@ -1328,6 +1364,10 @@
"type": "string",
"enum": ["ruff", "black", "yapf", "blue", "autopep8", "isort", "usort"]
},
"ReScript": {
"type": "string",
"enum": ["rescript_format"]
},
"Roc": {
"type": "string",
"enum": ["roc_format"]
Expand Down
10 changes: 7 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::languages::{
blade::Blade, c::C, cpp::Cpp, crystal::Crystal, csharp::CSharp, css::Css, dart::Dart,
elixir::Elixir, elm::Elm, gleam::Gleam, go::Go, graphql::GraphQL, html::Html, java::Java,
javascript::JavaScript, json::Json, just::Just, lua::Lua, markdown::Markdown, nim::Nim,
objective_c::ObjectiveC, ocaml::OCaml, protobuf::Protobuf, python::Python, roc::Roc,
ruby::Ruby, rust::Rust, shell::Shell, sql::Sql, toml::Toml, typescript::TypeScript, vue::Vue,
yaml::Yaml, zig::Zig, Lang,
objective_c::ObjectiveC, ocaml::OCaml, protobuf::Protobuf, python::Python, rescript::ReScript,
roc::Roc, ruby::Ruby, rust::Rust, shell::Shell, sql::Sql, toml::Toml, typescript::TypeScript,
vue::Vue, yaml::Yaml, zig::Zig, Lang,
};

#[derive(Debug, serde::Serialize, serde::Deserialize, JsonSchema)]
Expand Down Expand Up @@ -88,6 +88,9 @@ pub struct MdsfConfig {
#[serde(default)]
pub python: Lang<Python>,

#[serde(default)]
pub rescript: Lang<ReScript>,

#[serde(default)]
pub roc: Lang<Roc>,

Expand Down Expand Up @@ -149,6 +152,7 @@ impl Default for MdsfConfig {
ocaml: Lang::<OCaml>::default(),
protobuf: Lang::<Protobuf>::default(),
python: Lang::<Python>::default(),
rescript: Lang::<ReScript>::default(),
roc: Lang::<Roc>::default(),
ruby: Lang::<Ruby>::default(),
rust: Lang::<Rust>::default(),
Expand Down
2 changes: 2 additions & 0 deletions src/formatters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub mod mix_format;
pub mod nimpretty;
pub mod ocamlformat;
pub mod prettier;
pub mod rescript_format;
pub mod roc_format;
pub mod rubocop;
pub mod ruff;
Expand Down Expand Up @@ -132,6 +133,7 @@ pub fn format_snippet(config: &MdsfConfig, language: &Language, code: &str) -> S
Language::Vue => config.vue.format(snippet_path),
Language::Yaml => config.yaml.format(snippet_path),
Language::Zig => config.zig.format(snippet_path),
Language::ReScript => config.rescript.format(snippet_path),
} {
let mut f = formatted_code.trim().to_owned();

Expand Down
3 changes: 1 addition & 2 deletions src/formatters/ocamlformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ pub fn format_using_ocamlformat(

cmd.arg("--ignore-invalid-option")
.arg("--inplace")
.arg("--quiet")
.arg("--enable-outside-detected-project")
.arg(snippet_path);

Expand All @@ -25,7 +24,7 @@ mod test_ocamlformat {
#[test]
fn it_should_format_ocaml() {
let input = "
let add a b = a + b
let add a b = a + b
";
let expected_output = "let add a b = a + b
";
Expand Down
75 changes: 75 additions & 0 deletions src/formatters/rescript_format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::runners::{setup_npm_script, JavaScriptRuntime};

use super::execute_command;

#[inline]
fn set_rescript_format_args(cmd: &mut std::process::Command, snippet_path: &std::path::Path) {
cmd.arg("format").arg(snippet_path);
}

#[inline]
fn invote_rescript_format(
mut cmd: std::process::Command,
snippet_path: &std::path::Path,
) -> std::io::Result<(bool, Option<String>)> {
set_rescript_format_args(&mut cmd, snippet_path);

execute_command(&mut cmd, snippet_path)
}

#[inline]
pub fn format_using_rescript_format(
snippet_path: &std::path::Path,
) -> std::io::Result<(bool, Option<String>)> {
invote_rescript_format(
setup_npm_script(JavaScriptRuntime::default(), "rescript"),
snippet_path,
)
}

#[cfg(test)]
mod test_rescript_format {
use crate::{formatters::setup_snippet, languages::Language};

#[test]
fn it_should_format_rescript() {
let input = r#"module Button = {
@react.component
let make = (~count) => {
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => n->Int.toString ++ " times"
}
let text = `Click me ${times}`
<button> {text->React.string} </button>
}
}"#;

let expected_output = r#"module Button = {
@react.component
let make = (~count) => {
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => n->Int.toString ++ " times"
}
let text = `Click me ${times}`
<button> {text->React.string} </button>
}
}
"#;

let snippet = setup_snippet(input, Language::ReScript.to_file_ext())
.expect("it to create a snippet file");

let output = super::format_using_rescript_format(snippet.path())
.expect("it to be successful")
.1
.expect("it to be some");

assert_eq!(expected_output, output);
}
}
4 changes: 4 additions & 0 deletions src/languages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub enum Language {
OCaml,
Protobuf,
Python,
ReScript,
Roc,
Ruby,
Rust,
Expand Down Expand Up @@ -76,6 +77,7 @@ pub mod objective_c;
pub mod ocaml;
pub mod protobuf;
pub mod python;
pub mod rescript;
pub mod roc;
pub mod ruby;
pub mod rust;
Expand Down Expand Up @@ -134,6 +136,7 @@ impl Language {
"zig" => Some(Self::Zig),
"graphql" | "gql" => Some(Self::GraphQL),
"elm" => Some(Self::Elm),
"rescript" => Some(Self::ReScript),
_ => None,
}
}
Expand Down Expand Up @@ -177,6 +180,7 @@ impl Language {
Self::Elm => ".elm",
Self::Blade => ".blade.php",
Self::OCaml => ".ml",
Self::ReScript => ".res",
}
}
}
Expand Down
120 changes: 120 additions & 0 deletions src/languages/rescript.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use schemars::JsonSchema;

use crate::formatters::{rescript_format::format_using_rescript_format, MdsfFormatter};

use super::{Lang, LanguageFormatter};

#[derive(Debug, Default, serde::Serialize, serde::Deserialize, JsonSchema)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub enum ReScript {
#[default]
#[serde(rename = "rescript_format")]
ReScriptFormat,
}

impl Default for Lang<ReScript> {
#[inline]
fn default() -> Self {
Self {
enabled: true,
formatter: MdsfFormatter::<ReScript>::default(),
}
}
}

impl Default for MdsfFormatter<ReScript> {
#[inline]
fn default() -> Self {
Self::Single(ReScript::ReScriptFormat)
}
}

impl LanguageFormatter for ReScript {
#[inline]
fn format_snippet(
&self,
snippet_path: &std::path::Path,
) -> std::io::Result<(bool, Option<String>)> {
match self {
Self::ReScriptFormat => format_using_rescript_format(snippet_path),
}
}
}

#[cfg(test)]
mod test_rescript {
use crate::{
formatters::{setup_snippet, MdsfFormatter},
languages::Lang,
};

use super::ReScript;

const INPUT: &str = r#"module Button = {
@react.component
let make = (~count) => {
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => n->Int.toString ++ " times"
}
let text = `Click me ${times}`
<button> {text->React.string} </button>
}
}"#;

const EXTENSION: &str = crate::languages::Language::ReScript.to_file_ext();

#[test]
fn it_should_be_enabled_by_default() {
assert!(Lang::<ReScript>::default().enabled);
}

#[test]
fn it_should_not_format_when_enabled_is_false() {
let snippet = setup_snippet(INPUT, EXTENSION).expect("it to save the file");
let snippet_path = snippet.path();

assert!(Lang::<ReScript> {
enabled: false,
formatter: MdsfFormatter::Single(ReScript::default())
}
.format(snippet_path)
.expect("it to not fail")
.is_none());
}

#[test]
fn test_rescript_format() {
let l = Lang::<ReScript> {
enabled: true,
formatter: MdsfFormatter::Single(ReScript::ReScriptFormat),
};

let snippet = setup_snippet(INPUT, EXTENSION).expect("it to save the file");
let snippet_path = snippet.path();

let output = l
.format(snippet_path)
.expect("it to not fail")
.expect("it to be a snippet");

let expected_output = r#"module Button = {
@react.component
let make = (~count) => {
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => n->Int.toString ++ " times"
}
let text = `Click me ${times}`
<button> {text->React.string} </button>
}
}
"#;

assert_eq!(output, expected_output);
}
}

0 comments on commit ee24b4d

Please sign in to comment.