Skip to content

Commit

Permalink
feat: support config line_separator
Browse files Browse the repository at this point in the history
Add config "line_separator" which supports the following options

input: Determine line separator by the input content. This is the default.
os: Determine line separator by the operating system
lf: Use Unix Style ("\n")
cr: Use classic Max Style ("\r")
crlf: Use Windows Style ("\r\n")

Note that the default behavior is changed.
The previous behavior is "os",
but I think "input" is a more appropriate default.
  • Loading branch information
SafeteeWoW committed Apr 18, 2021
1 parent 78b3d90 commit 2aafa70
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
* text=auto eol=lf
*.a filter=lfs diff=lfs merge=lfs -text
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ if(BUILD_TESTS)
test/test_parser.cpp
test/test_args.cpp
test/test_validation.cpp
test/test_line_separator.cpp
)
set_target_properties(lua-format-test PROPERTIES LINKER_LANGUAGE CXX)

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ luarocks install --server=https://luarocks.org/dev luaformatter
in key/value fields
--line-breaks-after-function-body
Line brakes after function body
--line-separator=[line separator] input(determined by the input content),
os(Use line ending of the current
Operating system), lf(Unix style "\n"),
crlf(Windows style "\r\n"), cr(classic
Max style "\r")
Lua scripts... Lua scripts to format
"--" can be used to terminate flag options and force all following
arguments to be treated as positional options
Expand Down Expand Up @@ -182,6 +187,7 @@ double_quote_to_single_quote: false
single_quote_to_double_quote: false
spaces_around_equals_in_field: true
line_breaks_after_function_body: 1
line_separator: input
```
### Disable formatting for a line or block
Sometimes it may be useful to disable automatic formatting. This is done be putting the code between `LuaFormatter off` and `LuaFormatter on` tags:
Expand Down
23 changes: 23 additions & 0 deletions src/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Config::Config() {
node["spaces_around_equals_in_field"] = true;
node["line_breaks_after_function_body"] = 1;

node["line_separator"] = "input";

// Validators
// validate integer without 0s as configuration value
auto validate_integer = [](const std::string& key, std::any elem) {
Expand Down Expand Up @@ -117,6 +119,15 @@ Config::Config() {
}
return value;
};
auto validate_line_separator = [&](const std::string& key, std::any elem) {
(void)(key);
const auto value = std::any_cast<std::string>(elem);
if (value != "os" && value != "input" && value != "lf" && value != "cr" && value != "crlf") {
throw std::domain_error(
"[ERROR] Configuration value of line_separator should be one of os/input/lf/cr/crlf");
}
return value;
};

// Validators
validators["spaces_before_call"] = validate_integer_zero;
Expand Down Expand Up @@ -150,6 +161,7 @@ Config::Config() {
validators["spaces_inside_table_braces"] = validate_boolean;
validators["spaces_around_equals_in_field"] = validate_boolean;
validators["line_breaks_after_function_body"] = validate_integer;
validators["line_separator"] = validate_line_separator;

// DataType of every configuration field
datatype["spaces_before_call"] = 'i';
Expand Down Expand Up @@ -183,6 +195,7 @@ Config::Config() {
datatype["spaces_inside_table_braces"] = 'b';
datatype["spaces_around_equals_in_field"] = 'b';
datatype["line_breaks_after_function_body"] = 'i';
datatype["line_separator"] = 's';
}

void Config::readFromFile(const std::string& file) {
Expand Down Expand Up @@ -224,6 +237,11 @@ void Config::readFromFile(const std::string& file) {
node[key] = std::any_cast<char>(validators[key](key, value));
break;
}
case 's': {
auto value = kv.second.as<std::string>();
node[key] = std::any_cast<std::string>(validators[key](key, value));
break;
}
}
}
if (key == CTL) {
Expand Down Expand Up @@ -258,6 +276,11 @@ void Config::readFromMap(std::map<std::string, std::any>& mp) {
node[key] = std::any_cast<char>(validators[key](key, kv.second));
break;
}
case 's': {
assert(strcmp(kv.second.type().name(), "s") == 0);
node[key] = std::any_cast<std::string>(validators[key](key, kv.second));
break;
}
}
}
if (key == CTL) {
Expand Down
91 changes: 91 additions & 0 deletions src/lua-format.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "lua-format.h"

#include <iostream>

#include "FormatVisitor.h"
Expand Down Expand Up @@ -51,6 +53,34 @@ std::vector<std::pair<size_t, size_t>> findBlocksBetweenFormatSwitch(const std::
return blocks;
}

static std::string getOSLineSeparator() {
#ifdef _WIN32
return "\r\n";
#else
return "\n";
#endif
}

std::string handleLineSeparator(const std::string& original, const std::string& formatted, const Config& config) {
const auto line_separator_config = config.get<std::string>("line_separator");
auto out = formatted;
if (line_separator_config == "os") {
out = convert_line_separator(out, getOSLineSeparator());
} else if (line_separator_config == "input") {
auto line_sep = get_line_separator(original);
out = convert_line_separator(out, line_sep);
} else if (line_separator_config == "lf") {
out = convert_line_separator(out, "\n");
} else if (line_separator_config == "cr") {
out = convert_line_separator(out, "\r");
} else if (line_separator_config == "crlf") {
out = convert_line_separator(out, "\r\n");
} else {
throw std::runtime_error("Should not reach here. Invalid line_separator config");
}
return out;
}

std::string resetContentInDisableFormatBlocks(const std::string& original, const std::string& formatted) {
std::vector<std::pair<size_t, size_t>> originalBlocks = findBlocksBetweenFormatSwitch(original);
std::vector<std::pair<size_t, size_t>> formattedBlocks = findBlocksBetweenFormatSwitch(formatted);
Expand Down Expand Up @@ -84,11 +114,72 @@ std::string lua_format(std::istream& is, const Config& config) {
std::string original = os.str();
ANTLRInputStream input(original);
std::string formatted = __format(input, config);
formatted = handleLineSeparator(original, formatted, config);
return resetContentInDisableFormatBlocks(original, formatted);
}

std::string lua_format(const std::string& str, const Config& config) {
ANTLRInputStream input(str);
std::string formatted = __format(input, config);
formatted = handleLineSeparator(str, formatted, config);
return resetContentInDisableFormatBlocks(str, formatted);
}

std::string get_line_separator(const std::string& input) {
constexpr char CR = '\r';
constexpr char LF = '\n';
size_t lf_count = 0;
size_t cr_count = 0;
size_t crlf_count = 0;
const auto length = input.length();
for (size_t i = 0; i < length; i++) {
const auto cur = input[i];
if (cur == LF) {
lf_count++;
} else if (cur == CR) {
const auto next = input[i + 1];
if (next == LF) {
crlf_count++;
i++;
} else {
cr_count++;
}
}
}

std::string result;
if (lf_count >= crlf_count && lf_count >= cr_count) {
result += LF;
} else if (crlf_count >= lf_count && crlf_count >= cr_count) {
result += CR;
result += LF;
} else {
result += CR;
}
return result;
}

std::string convert_line_separator(const std::string& input, const std::string& line_sep) {
constexpr char CR = '\r';
constexpr char LF = '\n';
const auto length = input.length();

std::string result;
for (size_t i = 0; i < length; i++) {
const auto cur = input[i];
if (cur == LF) {
result += line_sep;
} else if (cur == CR) {
const auto next = input[i + 1];
if (next == LF) {
result += line_sep;
i++;
} else {
result += line_sep;
}
} else {
result += cur;
}
}
return result;
}
7 changes: 7 additions & 0 deletions src/lua-format.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@

std::string lua_format(const std::string& input, const Config& config);
std::string lua_format(std::istream& stream, const Config& config);

/// Return the line separator (LF, CRLF, CR) that appears the most
/// When ties or the input has no newline, return in the order of LF > CRLF > CR
std::string get_line_separator(const std::string& input);

/// Return input with all line separator replaced with the specified one
std::string convert_line_separator(const std::string& input, const std::string& line_sep);
23 changes: 17 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,15 @@ int main(int argc, const char* argv[]) {
args::Flag nospacesaroundequalsinfield(optspacesaroundequalsinfield, "spaces around equals sign in key/value fields",
"Do not put spaces around the equal sign in key/value fields", {"no-spaces-around-equals-in-field"});
args::ValueFlag<int> linebreaksafterfunctionbody(parser, "line breaks after function body", "Line breaks after function body", {"line-breaks-after-function-body"});


args::ValueFlag<std::string> lineseparator(parser, "line separator",
"input(determined by the input content), "
"os(Use line ending of the current Operating system), "
"lf(Unix style \"\\n\"), "
"crlf(Windows style \"\\r\\n\"), "
"cr(classic Max style \"\\r\")",
{"line-separator"});

args::PositionalList<std::string> files(parser, "Lua scripts", "Lua scripts to format");

Config config;
Expand Down Expand Up @@ -340,7 +348,11 @@ int main(int argc, const char* argv[]) {
if (linebreaksafterfunctionbody) {
argmap["line_breaks_after_function_body"] = args::get(linebreaksafterfunctionbody);
}


if (lineseparator) {
argmap["line_separator"] = args::get(lineseparator);
}

std::string configFileName = args::get(cFile);

// Automatically look for a .lua-format on the current directory
Expand Down Expand Up @@ -470,16 +482,15 @@ int main(int argc, const char* argv[]) {
continue;
}

std::ifstream ifs;
ifs.open(fileName);
std::ifstream ifs(fileName, std::ifstream::binary);

try {
std::string out = lua_format(ifs, config);

if (!inplace) {
std::cout << out;
std::cout.write(out.c_str(), out.length());
} else {
std::ofstream fout(fileName);
std::ofstream fout(fileName, std::ofstream::binary);
fout << out;
fout.close();

Expand Down
28 changes: 28 additions & 0 deletions test/test_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,31 @@ TEST_CASE("read from file", "config") {
REQUIRE("," == config.get<std::string>("table_sep"));
REQUIRE(false == config.get<bool>("extra_sep_at_table_end"));
}

TEST_CASE("line_separator", "config") {
Config config;
config.set("indent_width", 2);

config.set("line_separator", "lf");
REQUIRE("function W()\n print(1)\n print(2)\nend\n" == lua_format("function W() print(1) print(2) end", config));
config.set("line_separator", "cr");
REQUIRE("function W()\r print(1)\r print(2)\rend\r" == lua_format("function W() print(1) print(2) end", config));
config.set("line_separator", "crlf");
REQUIRE("function W()\r\n print(1)\r\n print(2)\r\nend\r\n" == lua_format("function W() print(1) print(2) end", config));

config.set("line_separator", "input");
REQUIRE("function W()\n print(1)\n print(2)\nend\n" == lua_format("function W()\n print(1)\n print(2)\n end", config));
REQUIRE("function W()\r print(1)\r print(2)\rend\r" == lua_format("function W()\r print(1)\r print(2)\r end", config));
REQUIRE("function W()\r\n print(1)\r\n print(2)\r\nend\r\n" == lua_format("function W()\r\n print(1)\r\n print(2)\r\n end", config));

config.set("line_separator", "os");
#ifdef _WIN32
REQUIRE("function W()\r\n print(1)\r\n print(2)\r\nend\r\n" == lua_format("function W()\n print(1)\n print(2)\n end", config));
REQUIRE("function W()\r\n print(1)\r\n print(2)\r\nend\r\n" == lua_format("function W()\r print(1)\r print(2)\r end", config));
REQUIRE("function W()\r\n print(1)\r\n print(2)\r\nend\r\n" == lua_format("function W()\r\n print(1)\r\n print(2)\r\n end", config));
#else
REQUIRE("function W()\n print(1)\n print(2)\nend\n" == lua_format("function W()\n print(1)\n print(2)\n end", config));
REQUIRE("function W()\n print(1)\n print(2)\nend\n" == lua_format("function W()\r print(1)\r print(2)\r end", config));
REQUIRE("function W()\n print(1)\n print(2)\nend\n" == lua_format("function W()\r\n print(1)\r\n print(2)\r\n end", config));
#endif
}
44 changes: 44 additions & 0 deletions test/test_line_separator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <catch2/catch.hpp>

#include "lua-format.h"

TEST_CASE("get_line_separator", "line_separator") {
REQUIRE(get_line_separator("\n") == "\n");
REQUIRE(get_line_separator("\r") == "\r");
REQUIRE(get_line_separator("\r\n") == "\r\n");

REQUIRE(get_line_separator("1\n2\n3\n") == "\n");
REQUIRE(get_line_separator("1\r\n2\r\n3\r\n") == "\r\n");
REQUIRE(get_line_separator("1\r2\r3\r") == "\r");

// Different separator appears equal times
REQUIRE(get_line_separator("") == "\n");
REQUIRE(get_line_separator("\r\n\n\r") == "\n");
REQUIRE(get_line_separator("\n\r") == "\n");
REQUIRE(get_line_separator("\r\n\r") == "\r\n");
REQUIRE(get_line_separator("1\r\n2\n3\r") == "\n");

// Different separator appears different times
REQUIRE(get_line_separator("1\r\n2\r\n3\n") == "\r\n");
REQUIRE(get_line_separator("1\n2\r\n3\r\n") == "\r\n");
REQUIRE(get_line_separator("1\r2\n3\n") == "\n");
REQUIRE(get_line_separator("1\n2\r3\r") == "\r");
}

TEST_CASE("convert_line_separator", "line_separator") {
REQUIRE(convert_line_separator("", "\r\n").empty());
REQUIRE(convert_line_separator("", "\n").empty());
REQUIRE(convert_line_separator("", "\r").empty());

REQUIRE(convert_line_separator("1\r\n2\r\n3\r\n", "\n") == "1\n2\n3\n");
REQUIRE(convert_line_separator("1\r\n2\r\n3\r\n", "\r") == "1\r2\r3\r");
REQUIRE(convert_line_separator("1\r\n2\r\n3\r\n", "\r\n") == "1\r\n2\r\n3\r\n");

REQUIRE(convert_line_separator("1\n2\n3\n", "\n") == "1\n2\n3\n");
REQUIRE(convert_line_separator("1\n2\n3\n", "\r") == "1\r2\r3\r");
REQUIRE(convert_line_separator("1\n2\n3\n", "\r\n") == "1\r\n2\r\n3\r\n");

REQUIRE(convert_line_separator("1\r2\r3\r", "\n") == "1\n2\n3\n");
REQUIRE(convert_line_separator("1\r2\r3\r", "\r") == "1\r2\r3\r");
REQUIRE(convert_line_separator("1\r2\r3\r", "\r\n") == "1\r\n2\r\n3\r\n");
}

0 comments on commit 2aafa70

Please sign in to comment.