llvm-project/clang/unittests/Format/FormatTestJson.cpp
mydeveloperday 8b7881a084 [clang-format] Add basic support for formatting JSON
I find as I develop I'm moving between many different languages C++,C#,JavaScript all the time. As I move between the file types I like to keep `clang-format` as my formatting tool of choice. (hence why I initially added C# support  in {D58404}) I know those other languages have their own tools but I have to learn them all, and I have to work out how to configure them, and they may or may not have integration into my IDE or my source code integration.

I am increasingly finding that I'm editing additional JSON files as part of my daily work and my editor and git commit hooks are just not setup to go and run [[ https://stedolan.github.io/jq/ | jq ]], So I tend to go to  [[ https://jsonformatter.curiousconcept.com/ | JSON Formatter ]] and copy and paste back and forth. To get nicely formatted JSON. This is a painful process and I'd like a new one that causes me much less friction.

This has come up from time to time:

{D10543}
https://stackoverflow.com/questions/35856565/clang-format-a-json-file
https://bugs.llvm.org/show_bug.cgi?id=18699

I would like to stop having to do that and have formatting JSON as a first class clang-format support `Language` (even if it has minimal style settings at present).

This revision adds support for formatting JSON using the inbuilt JSON serialization library of LLVM, With limited control at present only over the indentation level

This adds an additional Language into the .clang-format file to separate the settings from your other supported languages.

Reviewed By: HazardyKnusperkeks

Differential Revision: https://reviews.llvm.org/D93528
2021-06-26 15:20:17 +01:00

198 lines
5.4 KiB
C++

//===- unittest/Format/FormatTestJson.cpp - Formatting tests for Json -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "FormatTestUtils.h"
#include "clang/Format/Format.h"
#include "llvm/Support/Debug.h"
#include "gtest/gtest.h"
#define DEBUG_TYPE "format-test-json"
namespace clang {
namespace format {
class FormatTestJson : public ::testing::Test {
protected:
static std::string format(llvm::StringRef Code, unsigned Offset,
unsigned Length, const FormatStyle &Style) {
LLVM_DEBUG(llvm::errs() << "---\n");
LLVM_DEBUG(llvm::errs() << Code << "\n\n");
tooling::Replacements Replaces;
// Mock up what ClangFormat.cpp will do for JSON by adding a variable
// to trick JSON into being JavaScript
if (Style.isJson()) {
auto Err = Replaces.add(
tooling::Replacement(tooling::Replacement("", 0, 0, "x = ")));
if (Err) {
llvm::errs() << "Bad Json variable insertion\n";
}
}
auto ChangedCode = applyAllReplacements(Code, Replaces);
if (!ChangedCode) {
llvm::errs() << "Bad Json varibale replacement\n";
}
StringRef NewCode = *ChangedCode;
std::vector<tooling::Range> Ranges(1, tooling::Range(0, NewCode.size()));
Replaces = reformat(Style, NewCode, Ranges);
auto Result = applyAllReplacements(NewCode, Replaces);
EXPECT_TRUE(static_cast<bool>(Result));
LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
return *Result;
}
static std::string
format(llvm::StringRef Code,
const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) {
return format(Code, 0, Code.size(), Style);
}
static FormatStyle getStyleWithColumns(unsigned ColumnLimit) {
FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
Style.ColumnLimit = ColumnLimit;
return Style;
}
static void
verifyFormat(llvm::StringRef Code,
const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Json)) {
EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable";
EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
}
};
TEST_F(FormatTestJson, JsonRecord) {
verifyFormat("{}");
verifyFormat("{\n"
" \"name\": 1\n"
"}");
verifyFormat("{\n"
" \"name\": \"Foo\"\n"
"}");
verifyFormat("{\n"
" \"name\": {\n"
" \"value\": 1\n"
" }\n"
"}");
verifyFormat("{\n"
" \"name\": {\n"
" \"value\": 1\n"
" },\n"
" \"name\": {\n"
" \"value\": 2\n"
" }\n"
"}");
verifyFormat("{\n"
" \"name\": {\n"
" \"value\": [\n"
" 1,\n"
" 2,\n"
" ]\n"
" }\n"
"}");
verifyFormat("{\n"
" \"name\": {\n"
" \"value\": [\n"
" \"name\": {\n"
" \"value\": 1\n"
" },\n"
" \"name\": {\n"
" \"value\": 2\n"
" }\n"
" ]\n"
" }\n"
"}");
verifyFormat(R"({
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 27,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null
})");
}
TEST_F(FormatTestJson, JsonArray) {
verifyFormat("[]");
verifyFormat("[\n"
" 1\n"
"]");
verifyFormat("[\n"
" 1,\n"
" 2\n"
"]");
verifyFormat("[\n"
" {},\n"
" {}\n"
"]");
verifyFormat("[\n"
" {\n"
" \"name\": 1\n"
" },\n"
" {}\n"
"]");
}
TEST_F(FormatTestJson, JsonNoStringSplit) {
FormatStyle Style = getLLVMStyle(FormatStyle::LK_Json);
Style.IndentWidth = 4;
verifyFormat(
"[\n"
" {\n"
" "
"\"naaaaaaaa\": \"foooooooooooooooooooooo oooooooooooooooooooooo\"\n"
" },\n"
" {}\n"
"]",
Style);
verifyFormat("[\n"
" {\n"
" "
"\"naaaaaaaa\": "
"\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooooooooooo\"\n"
" },\n"
" {}\n"
"]",
Style);
Style.ColumnLimit = 80;
verifyFormat("[\n"
" {\n"
" "
"\"naaaaaaaa\":\n"
" "
"\"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
"oooooooooooooooooooooooooo\"\n"
" },\n"
" {}\n"
"]",
Style);
}
} // namespace format
} // end namespace clang