verible: formatting: make line terminator character configurable
Precise control of file contents is required. This makes verible handle
files in `binary` mode, effectively disabling the platform-specific
hooks that for example translate \n into \r\n for DOS systems.
Let's introduce the line_terminator flag into BasicFormatStyle so that
users can decide which line terminator they want to use. Current options
are \n (LF) or \r\n (CRLF). LF is the default configuration.
diff --git a/verible/common/formatting/basic-format-style-init.cc b/verible/common/formatting/basic-format-style-init.cc
index b0aa52e..ab84745 100644
--- a/verible/common/formatting/basic-format-style-init.cc
+++ b/verible/common/formatting/basic-format-style-init.cc
@@ -37,6 +37,9 @@
ABSL_FLAG(int, line_break_penalty, 2,
"Penalty added to solution for each introduced line break.");
+ABSL_FLAG(verible::LineTerminatorStyle, line_terminator,
+ verible::LineTerminatorStyle::kLF, "Line terminator");
+
namespace verible {
void InitializeFromFlags(BasicFormatStyle *style) {
#define STYLE_FROM_FLAG(name) style->name = absl::GetFlag(FLAGS_##name)
@@ -47,6 +50,7 @@
STYLE_FROM_FLAG(column_limit);
STYLE_FROM_FLAG(over_column_limit_penalty);
STYLE_FROM_FLAG(line_break_penalty);
+ STYLE_FROM_FLAG(line_terminator);
#undef STYLE_FROM_FLAG
}
diff --git a/verible/common/formatting/basic-format-style.cc b/verible/common/formatting/basic-format-style.cc
index 5c1d7f1..9cc5d45 100644
--- a/verible/common/formatting/basic-format-style.cc
+++ b/verible/common/formatting/basic-format-style.cc
@@ -48,4 +48,39 @@
return stream.str();
}
+static const verible::EnumNameMap<LineTerminatorStyle> &
+LineTerminatorStyleStrings() {
+ static const verible::EnumNameMap<LineTerminatorStyle>
+ kLineTerminatorStyleStringMap({
+ {"CRLF", LineTerminatorStyle::kCRLF},
+ {"LF", LineTerminatorStyle::kLF},
+ });
+ return kLineTerminatorStyleStringMap;
+}
+
+void EmitLineTerminator(LineTerminatorStyle style, std::ostream &stream) {
+ switch (style) {
+ case LineTerminatorStyle::kLF:
+ stream << "\n";
+ break;
+ case LineTerminatorStyle::kCRLF:
+ stream << "\r\n";
+ break;
+ }
+}
+
+std::ostream &operator<<(std::ostream &stream, LineTerminatorStyle style) {
+ return LineTerminatorStyleStrings().Unparse(style, stream);
+}
+
+bool AbslParseFlag(std::string_view text, LineTerminatorStyle *mode,
+ std::string *error) {
+ return LineTerminatorStyleStrings().Parse(text, mode, error,
+ "LineTerminatorStyle");
+}
+
+std::string AbslUnparseFlag(const LineTerminatorStyle &mode) {
+ return std::string{LineTerminatorStyleStrings().EnumName(mode)};
+}
+
} // namespace verible
diff --git a/verible/common/formatting/basic-format-style.h b/verible/common/formatting/basic-format-style.h
index 58a73ea..f01825c 100644
--- a/verible/common/formatting/basic-format-style.h
+++ b/verible/common/formatting/basic-format-style.h
@@ -21,6 +21,21 @@
namespace verible {
+enum class LineTerminatorStyle {
+ // Line Feed `\n` (UNIX Style)
+ kLF,
+ // Carriage return + Line Feed `\r\n` (DOS Style)
+ kCRLF,
+};
+
+void EmitLineTerminator(LineTerminatorStyle style, std::ostream &stream);
+
+std::ostream &operator<<(std::ostream &stream, LineTerminatorStyle style);
+
+bool AbslParseFlag(std::string_view, LineTerminatorStyle *, std::string *);
+
+std::string AbslUnparseFlag(const LineTerminatorStyle &);
+
// Style configuration common to all languages.
struct BasicFormatStyle {
// Each indentation level adds this many spaces.
@@ -42,6 +57,9 @@
// Penalty added to solution for each introduced line break.
int line_break_penalty = 2;
+ // Line terminator character sequence
+ LineTerminatorStyle line_terminator = LineTerminatorStyle::kLF;
+
// -- Note: when adding new fields, add them in basic_format_style_init.cc
};
diff --git a/verible/verilog/formatting/BUILD b/verible/verilog/formatting/BUILD
index e6654b5..0940ef8 100644
--- a/verible/verilog/formatting/BUILD
+++ b/verible/verilog/formatting/BUILD
@@ -215,6 +215,7 @@
srcs = ["comment-controls.cc"],
hdrs = ["comment-controls.h"],
deps = [
+ "//verible/common/formatting:basic-format-style",
"//verible/common/strings:comment-utils",
"//verible/common/strings:display-utils",
"//verible/common/strings:line-column-map",
@@ -223,7 +224,6 @@
"//verible/common/text:token-stream-view",
"//verible/common/util:logging",
"//verible/common/util:range",
- "//verible/common/util:spacer",
"//verible/verilog/parser:verilog-parser",
"//verible/verilog/parser:verilog-token-classifications",
"//verible/verilog/parser:verilog-token-enum",
@@ -236,6 +236,7 @@
srcs = ["comment-controls_test.cc"],
deps = [
":comment-controls",
+ "//verible/common/formatting:basic-format-style",
"//verible/common/strings:line-column-map",
"//verible/common/strings:position",
"//verible/common/text:token-info-test-util",
diff --git a/verible/verilog/formatting/comment-controls.cc b/verible/verilog/formatting/comment-controls.cc
index 8cc4362..4323c72 100644
--- a/verible/verilog/formatting/comment-controls.cc
+++ b/verible/verilog/formatting/comment-controls.cc
@@ -23,6 +23,7 @@
#include "absl/strings/str_split.h"
#include "absl/strings/strip.h"
+#include "verible/common/formatting/basic-format-style.h"
#include "verible/common/strings/comment-utils.h"
#include "verible/common/strings/display-utils.h"
#include "verible/common/strings/line-column-map.h"
@@ -31,7 +32,6 @@
#include "verible/common/text/token-stream-view.h"
#include "verible/common/util/logging.h"
#include "verible/common/util/range.h"
-#include "verible/common/util/spacer.h"
#include "verible/verilog/parser/verilog-parser.h"
#include "verible/verilog/parser/verilog-token-classifications.h"
#include "verible/verilog/parser/verilog-token-enum.h"
@@ -121,7 +121,7 @@
void FormatWhitespaceWithDisabledByteRanges(
std::string_view text_base, std::string_view space_text,
const ByteOffsetSet &disabled_ranges, bool include_disabled_ranges,
- std::ostream &stream) {
+ std::ostream &stream, verible::LineTerminatorStyle line_terminator_style) {
VLOG(3) << __FUNCTION__;
CHECK(verible::IsSubRange(space_text, text_base));
const int start = std::distance(text_base.begin(), space_text.begin());
@@ -136,7 +136,7 @@
if (space_text.empty() && start != 0) {
if (!disabled_ranges.Contains(start)) {
VLOG(3) << "output: 1*\"\\n\" (empty space text)";
- stream << '\n';
+ verible::EmitLineTerminator(line_terminator_style, stream);
return;
}
}
@@ -159,7 +159,9 @@
text_base.substr(range.first, range.second - range.first));
const size_t newline_count = NewlineCount(enabled);
VLOG(3) << "output: " << newline_count << "*\"\\n\" (formatted)";
- stream << verible::Spacer(newline_count, '\n');
+ for (size_t i = 0; i < newline_count; i++) {
+ verible::EmitLineTerminator(line_terminator_style, stream);
+ }
partially_enabled = true;
total_enabled_newlines += newline_count;
}
@@ -177,7 +179,7 @@
// Print at least one newline if some subrange was format-enabled.
if (partially_enabled && total_enabled_newlines == 0 && start != 0) {
VLOG(3) << "output: 1*\"\\n\"";
- stream << '\n';
+ verible::EmitLineTerminator(line_terminator_style, stream);
}
}
diff --git a/verible/verilog/formatting/comment-controls.h b/verible/verilog/formatting/comment-controls.h
index ed23479..edfcac1 100644
--- a/verible/verilog/formatting/comment-controls.h
+++ b/verible/verilog/formatting/comment-controls.h
@@ -18,6 +18,7 @@
#include <ostream>
#include <string_view>
+#include "verible/common/formatting/basic-format-style.h"
#include "verible/common/strings/line-column-map.h"
#include "verible/common/strings/position.h" // for ByteOffsetSet, LineNumberSet
#include "verible/common/text/token-stream-view.h"
@@ -48,7 +49,7 @@
void FormatWhitespaceWithDisabledByteRanges(
std::string_view text_base, std::string_view space_text,
const verible::ByteOffsetSet &disabled_ranges, bool include_disabled_ranges,
- std::ostream &stream);
+ std::ostream &stream, verible::LineTerminatorStyle line_terminator_style);
} // namespace formatter
} // namespace verilog
diff --git a/verible/verilog/formatting/comment-controls_test.cc b/verible/verilog/formatting/comment-controls_test.cc
index 39fada2..2a3bbd5 100644
--- a/verible/verilog/formatting/comment-controls_test.cc
+++ b/verible/verilog/formatting/comment-controls_test.cc
@@ -22,6 +22,7 @@
#include "absl/strings/str_join.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "verible/common/formatting/basic-format-style.h"
#include "verible/common/strings/line-column-map.h"
#include "verible/common/strings/position.h"
#include "verible/common/text/token-info-test-util.h"
@@ -328,7 +329,8 @@
const std::string_view foo("foo"), bar("bar");
std::ostringstream stream;
EXPECT_DEATH(
- FormatWhitespaceWithDisabledByteRanges(foo, bar, {}, true, stream),
+ FormatWhitespaceWithDisabledByteRanges(foo, bar, {}, true, stream,
+ verible::LineTerminatorStyle::kLF),
"IsSubRange");
}
@@ -388,7 +390,8 @@
test.substring_range.first,
test.substring_range.second - test.substring_range.first);
FormatWhitespaceWithDisabledByteRanges(test.full_text, substr,
- test.disabled_ranges, true, stream);
+ test.disabled_ranges, true, stream,
+ verible::LineTerminatorStyle::kLF);
EXPECT_EQ(stream.str(), test.expected)
<< "text: \"" << test.full_text << "\", sub: \"" << substr
<< "\", disabled: " << test.disabled_ranges;
diff --git a/verible/verilog/formatting/formatter.cc b/verible/verilog/formatting/formatter.cc
index 22bcb56..e0ee77e 100644
--- a/verible/verilog/formatting/formatter.cc
+++ b/verible/verilog/formatting/formatter.cc
@@ -993,7 +993,7 @@
full_text.substr(position, front_offset - position));
FormatWhitespaceWithDisabledByteRanges(full_text, leading_whitespace,
disabled_ranges_, include_disabled,
- stream);
+ stream, style_.line_terminator);
// When front of first token is format-disabled, the previous call will
// already cover the space up to the front token, in which case,
@@ -1010,7 +1010,7 @@
const std::string_view trailing_whitespace(full_text.substr(position));
FormatWhitespaceWithDisabledByteRanges(full_text, trailing_whitespace,
disabled_ranges_, include_disabled,
- stream);
+ stream, style_.line_terminator);
}
} // namespace formatter
diff --git a/verible/verilog/tools/formatter/BUILD b/verible/verilog/tools/formatter/BUILD
index 976c703..cfbcc97 100644
--- a/verible/verilog/tools/formatter/BUILD
+++ b/verible/verilog/tools/formatter/BUILD
@@ -132,3 +132,11 @@
args = ["$(location :verible-verilog-format)"],
data = [":verible-verilog-format"],
)
+
+sh_test_with_runfiles_lib(
+ name = "format-line_terminator_test",
+ size = "small",
+ srcs = ["format_line_terminator_test.sh"],
+ args = ["$(location :verible-verilog-format)"],
+ data = [":verible-verilog-format"],
+)
diff --git a/verible/verilog/tools/formatter/format_line_terminator_test.sh b/verible/verilog/tools/formatter/format_line_terminator_test.sh
new file mode 100755
index 0000000..e5c8b7c
--- /dev/null
+++ b/verible/verilog/tools/formatter/format_line_terminator_test.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# Copyright 2017-2025 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare -r MY_INPUT_FILE="${TEST_TMPDIR}/myinput.txt"
+declare -r MY_OUTPUT_FILE="${TEST_TMPDIR}/myoutput.txt"
+declare -r MY_EXPECT_FILE="${TEST_TMPDIR}/myexpect.txt"
+
+# Get tool from argument
+[[ "$#" == 1 ]] || {
+ echo "Expecting 1 positional argument, verible-verilog-format path."
+ exit 1
+}
+formatter="$(rlocation ${TEST_WORKSPACE}/${1})"
+
+# Create files with LF and CRLF line terminators. They are properly formatted, so running
+# the formatter should only change line endings
+printf "// some comment\n/* some other comment */\nmodule m;\nendmodule\n" > "${MY_INPUT_FILE}LF"
+printf "// some comment\r\n/* some other comment */\r\nmodule m;\r\nendmodule\r\n" > "${MY_INPUT_FILE}CRLF"
+
+
+for newline in LF CRLF; do
+ cp "${MY_INPUT_FILE}$newline" "${MY_EXPECT_FILE}$newline"
+done
+
+# Test any combination of input line terminators and output line terminators.
+# Test both inline formatting and standard output
+for original_newline in LF CRLF; do
+ for target_newline in LF CRLF; do
+ PROPER_INPUT_FILE="${MY_INPUT_FILE}$original_newline"
+ PROPER_EXPECT_FILE="${MY_EXPECT_FILE}$target_newline"
+
+ ${formatter} --line_terminator=$target_newline $PROPER_INPUT_FILE > ${MY_OUTPUT_FILE}
+ cmp ${MY_OUTPUT_FILE} $PROPER_EXPECT_FILE || exit 1
+
+ cp $PROPER_INPUT_FILE ${MY_OUTPUT_FILE}
+ ${formatter} --line_terminator=$target_newline --inplace ${MY_OUTPUT_FILE}
+ cmp ${MY_OUTPUT_FILE} $PROPER_EXPECT_FILE || exit 2
+ done
+done
+
+echo "PASS"