From 5aca594659edb896404ea1308f3aaa4e25d64794 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Fri, 5 Sep 2025 13:37:26 -0400 Subject: [PATCH] Support `--indentation` in the `fmt` and `lint` commands See: https://github.com/getmanfred/mac/pull/46 Signed-off-by: Juan Cruz Viotti --- docs/format.markdown | 7 +++ docs/lint.markdown | 8 +++- src/command_fmt.cc | 11 +++-- src/command_lint.cc | 5 ++- src/main.cc | 6 ++- src/utils.cc | 9 ++++ src/utils.h | 2 + test/CMakeLists.txt | 4 ++ test/format_check_single_fail_indentation.sh | 45 ++++++++++++++++++++ test/format_check_single_pass_indentation.sh | 26 +++++++++++ test/format_single_indentation.sh | 31 ++++++++++++++ test/lint/pass_lint_fix_indentation.sh | 29 +++++++++++++ 12 files changed, 175 insertions(+), 8 deletions(-) create mode 100755 test/format_check_single_fail_indentation.sh create mode 100755 test/format_check_single_pass_indentation.sh create mode 100755 test/format_single_indentation.sh create mode 100755 test/lint/pass_lint_fix_indentation.sh diff --git a/docs/format.markdown b/docs/format.markdown index 4c9978712..6b20c009b 100644 --- a/docs/format.markdown +++ b/docs/format.markdown @@ -5,6 +5,7 @@ Formatting jsonschema fmt [schemas-or-directories...] [--check/-c] [--verbose/-v] [--extension/-e ] [--ignore/-i ] [--keep-ordering/-k] + [--indentation/-n ] ``` Schemas are code. As such, they are expected follow consistent stylistic @@ -54,6 +55,12 @@ jsonschema fmt path/to/my/schema_1.json path/to/my/schema_2.json jsonschema fmt path/to/my/schema_1.json path/to/my/schema_2.json --keep-ordering ``` +### Format JSON Schemas in-place while indenting on 4 spaces + +```sh +jsonschema fmt path/to/my/schema_1.json path/to/my/schema_2.json --indentation 4 +``` + ### Format every `.json` file in a given directory (recursively) ```sh diff --git a/docs/lint.markdown b/docs/lint.markdown index 2fed26d5c..d58f069e2 100644 --- a/docs/lint.markdown +++ b/docs/lint.markdown @@ -6,7 +6,7 @@ jsonschema lint [schemas-or-directories...] [--http/-h] [--fix/-f] [--json/-j] [--verbose/-v] [--resolve/-r ...] [--extension/-e ] [--ignore/-i ] [--exclude/-x ] [--only/-o ] [--list/-l] - [--default-dialect/-d ] [--strict/-s] + [--default-dialect/-d ] [--strict/-s] [--indentation/-n ] ``` JSON Schema is a surprisingly expressive schema language. Like with traditional @@ -133,6 +133,12 @@ jsonschema lint --extension .schema.json jsonschema lint path/to/my/schema.json --fix ``` +### Fix lint warnings on a single schema while indenting on 4 spaces + +```sh +jsonschema lint path/to/my/schema.json --fix --indentation 4 +``` + ### Fix lint warnings on a single schema while preserving keyword ordering ```sh diff --git a/src/command_fmt.cc b/src/command_fmt.cc index dae755c82..e1f7c586e 100644 --- a/src/command_fmt.cc +++ b/src/command_fmt.cc @@ -11,6 +11,7 @@ auto sourcemeta::jsonschema::cli::fmt(const sourcemeta::core::Options &options) -> int { + const auto indentation{parse_indentation(options)}; for (const auto &entry : for_each_json(options.positional(), parse_ignore(options), parse_extensions(options))) { @@ -28,10 +29,11 @@ auto sourcemeta::jsonschema::cli::fmt(const sourcemeta::core::Options &options) std::ostringstream expected; if (options.contains("keep-ordering")) { - sourcemeta::core::prettify(entry.second, expected); + sourcemeta::core::prettify(entry.second, expected, indentation); } else { sourcemeta::core::prettify(entry.second, expected, - sourcemeta::core::schema_format_compare); + sourcemeta::core::schema_format_compare, + indentation); } expected << "\n"; @@ -50,10 +52,11 @@ auto sourcemeta::jsonschema::cli::fmt(const sourcemeta::core::Options &options) std::ofstream output{entry.first}; if (options.contains("keep-ordering")) { - sourcemeta::core::prettify(entry.second, output); + sourcemeta::core::prettify(entry.second, output, indentation); } else { sourcemeta::core::prettify(entry.second, output, - sourcemeta::core::schema_format_compare); + sourcemeta::core::schema_format_compare, + indentation); } output << "\n"; diff --git a/src/command_lint.cc b/src/command_lint.cc index 20c72e996..2ce5eb46c 100644 --- a/src/command_lint.cc +++ b/src/command_lint.cc @@ -192,6 +192,7 @@ auto sourcemeta::jsonschema::cli::lint(const sourcemeta::core::Options &options) auto errors_array = sourcemeta::core::JSON::make_array(); std::vector scores; const auto dialect{default_dialect(options)}; + const auto indentation{parse_indentation(options)}; const auto custom_resolver{ resolver(options, options.contains("http"), dialect)}; @@ -243,7 +244,7 @@ auto sourcemeta::jsonschema::cli::lint(const sourcemeta::core::Options &options) if (wrapper_result == EXIT_SUCCESS) { if (copy != entry.second) { std::ofstream output{entry.first}; - sourcemeta::core::prettify(copy, output); + sourcemeta::core::prettify(copy, output, indentation); output << "\n"; } } else { @@ -298,7 +299,7 @@ auto sourcemeta::jsonschema::cli::lint(const sourcemeta::core::Options &options) } output_json_object.assign("errors", sourcemeta::core::JSON{errors_array}); - sourcemeta::core::prettify(output_json_object, std::cout); + sourcemeta::core::prettify(output_json_object, std::cout, indentation); std::cout << "\n"; } diff --git a/src/main.cc b/src/main.cc index 58396ed61..36f651761 100644 --- a/src/main.cc +++ b/src/main.cc @@ -69,6 +69,7 @@ Global Options: fmt [schemas-or-directories...] [--check/-c] [--extension/-e ] [--ignore/-i ] [--keep-ordering/-k] + [--indentation/-n ] Format the input schemas in-place or check they are formatted. This command does not support YAML schemas yet. @@ -76,13 +77,14 @@ Global Options: lint [schemas-or-directories...] [--fix/-f] [--json/-j] [--extension/-e ] [--ignore/-i ] [--exclude/-x ] [--only/-o ] [--list/-l] - [--strict/-s] + [--strict/-s] [--indentation/-n ] Lint the input schemas and potentially fix the reported issues. The --fix/-f option is not supported when passing YAML schemas. Use --json/-j to output lint errors in JSON. Use --list/-l to print a summary of all enabled rules. Use --strict/-s to enable additional opinionated strict rules. + Use --indentation/-n to keep indentation when auto-fixing bundle [--http/-h] [--extension/-e ] [--ignore/-i ] [--without-id/-w] @@ -120,6 +122,7 @@ auto jsonschema_main(const std::string &program, const std::string &command, app.flag("keep-ordering", {"k"}); app.option("extension", {"e"}); app.option("ignore", {"i"}); + app.option("indentation", {"n"}); app.parse(argc, argv, {.skip = 1}); return sourcemeta::jsonschema::cli::fmt(app); } else if (command == "inspect") { @@ -141,6 +144,7 @@ auto jsonschema_main(const std::string &program, const std::string &command, app.option("exclude", {"x"}); app.option("only", {"o"}); app.option("ignore", {"i"}); + app.option("indentation", {"n"}); app.parse(argc, argv, {.skip = 1}); return sourcemeta::jsonschema::cli::lint(app); } else if (command == "validate") { diff --git a/src/utils.cc b/src/utils.cc index a40ab0b7b..a1e0c46c9 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -330,4 +330,13 @@ auto default_dialect(const sourcemeta::core::Options &options) return std::nullopt; } +auto parse_indentation(const sourcemeta::core::Options &options) + -> std::size_t { + if (options.contains("indentation")) { + return std::stoull(std::string{options.at("indentation").front()}); + } + + return 2; +} + } // namespace sourcemeta::jsonschema::cli diff --git a/src/utils.h b/src/utils.h index 71f5763a9..0a840eee4 100644 --- a/src/utils.h +++ b/src/utils.h @@ -76,6 +76,8 @@ auto parse_ignore(const sourcemeta::core::Options &options) auto default_dialect(const sourcemeta::core::Options &options) -> std::optional; +auto parse_indentation(const sourcemeta::core::Options &options) -> std::size_t; + } // namespace sourcemeta::jsonschema::cli #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 273fd8b0f..0e680b86d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,6 +37,9 @@ add_jsonschema_test_unix(format_check_single_pass_keep_ordering) add_jsonschema_test_unix(format_directory_ignore_directory) add_jsonschema_test_unix(format_directory_ignore_file) add_jsonschema_test_unix(format_check_single_invalid) +add_jsonschema_test_unix(format_check_single_fail_indentation) +add_jsonschema_test_unix(format_check_single_pass_indentation) +add_jsonschema_test_unix(format_single_indentation) # Validate add_jsonschema_test_unix(validate/fail_instance_directory) @@ -255,6 +258,7 @@ add_jsonschema_test_unix(lint/fail_lint_default) add_jsonschema_test_unix(lint/pass_lint_json) add_jsonschema_test_unix(lint/pass_lint_fix_json) add_jsonschema_test_unix(lint/pass_lint_fix_strict) +add_jsonschema_test_unix(lint/pass_lint_fix_indentation) add_jsonschema_test_unix(lint/pass_lint_ignore_short) add_jsonschema_test_unix(lint/pass_lint_ignore_long) add_jsonschema_test_unix(lint/pass_lint_examples_fix) diff --git a/test/format_check_single_fail_indentation.sh b/test/format_check_single_fail_indentation.sh new file mode 100755 index 000000000..502e73ef0 --- /dev/null +++ b/test/format_check_single_fail_indentation.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} +EOF + +"$1" fmt "$TMP/schema.json" --indentation 4 --check 2>"$TMP/stderr.txt" && CODE="$?" || CODE="$?" +test "$CODE" = "1" || exit 1 + +cat << EOF > "$TMP/error.txt" +FAIL: $(realpath "$TMP")/schema.json +Got: +{ + "\$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} + +But expected: +{ + "\$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} + +EOF + +diff "$TMP/stderr.txt" "$TMP/error.txt" + +cat << 'EOF' > "$TMP/expected.json" +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} +EOF + +diff "$TMP/schema.json" "$TMP/expected.json" diff --git a/test/format_check_single_pass_indentation.sh b/test/format_check_single_pass_indentation.sh new file mode 100755 index 000000000..ffab28ff6 --- /dev/null +++ b/test/format_check_single_pass_indentation.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} +EOF + +"$1" fmt "$TMP/schema.json" --check --indentation 4 + +cat << 'EOF' > "$TMP/expected.json" +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": 1 +} +EOF + +diff "$TMP/schema.json" "$TMP/expected.json" diff --git a/test/format_single_indentation.sh b/test/format_single_indentation.sh new file mode 100755 index 000000000..097aac3d4 --- /dev/null +++ b/test/format_single_indentation.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "additionalProperties": false, + "title": "Hello World", + "properties": {"foo": {}, "bar": {}} +} +EOF + +"$1" fmt "$TMP/schema.json" --indentation 4 + +cat << 'EOF' > "$TMP/expected.json" +{ + "title": "Hello World", + "properties": { + "bar": {}, + "foo": {} + }, + "additionalProperties": false +} +EOF + +diff "$TMP/schema.json" "$TMP/expected.json" diff --git a/test/lint/pass_lint_fix_indentation.sh b/test/lint/pass_lint_fix_indentation.sh new file mode 100755 index 000000000..c71295819 --- /dev/null +++ b/test/lint/pass_lint_fix_indentation.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +set -o errexit +set -o nounset + +TMP="$(mktemp -d)" +clean() { rm -rf "$TMP"; } +trap clean EXIT + +cat << 'EOF' > "$TMP/schema.json" +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "type": "string", + "const": "foo", + "title": "I should not be moved up" +} +EOF + +"$1" lint "$TMP/schema.json" --fix --indentation 4 + +cat << 'EOF' > "$TMP/expected.json" +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "const": "foo", + "title": "I should not be moved up" +} +EOF + +diff "$TMP/schema.json" "$TMP/expected.json"