yvvan updated this revision to Diff 194844.
yvvan retitled this revision from "[clang-format] Introduce the flag which 
allows not to shrink lines" to "[clang-format] Create a new tool for IDEs based 
on clang-format".
yvvan edited the summary of this revision.
yvvan added a reviewer: arphaman.
yvvan added a comment.
Herald added subscribers: dexonsmith, mgorny.

New approach - title and summary are updated.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D53072/new/

https://reviews.llvm.org/D53072

Files:
  include/clang/Format/Format.h
  lib/Format/ContinuationIndenter.cpp
  lib/Format/Format.cpp
  lib/Format/FormatInternal.h
  lib/Format/TokenAnalyzer.cpp
  lib/Format/TokenAnalyzer.h
  lib/Format/UnwrappedLineFormatter.cpp
  lib/Format/UnwrappedLineFormatter.h
  lib/Format/UnwrappedLineParser.cpp
  lib/Format/UnwrappedLineParser.h
  tools/CMakeLists.txt
  tools/clang-format-ide/CMakeLists.txt
  tools/clang-format-ide/ClangFormatIDE.cpp
  tools/clang-format/ClangFormat.cpp
  tools/clang-format/ClangFormat.h
  unittests/Format/FormatTest.cpp

Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -35,15 +35,16 @@
     SC_DoNotCheck
   };
 
-  std::string format(llvm::StringRef Code,
-                     const FormatStyle &Style = getLLVMStyle(),
-                     StatusCheck CheckComplete = SC_ExpectComplete) {
+  std::string format(
+      llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle(),
+      StatusCheck CheckComplete = SC_ExpectComplete,
+      ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None) {
     LLVM_DEBUG(llvm::errs() << "---\n");
     LLVM_DEBUG(llvm::errs() << Code << "\n\n");
     std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
     FormattingAttemptStatus Status;
     tooling::Replacements Replaces =
-        reformat(Style, Code, Ranges, "<stdin>", &Status);
+        reformat(Style, FormattingOptions, Code, Ranges, "<stdin>", &Status);
     if (CheckComplete != SC_DoNotCheck) {
       bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete;
       EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete)
@@ -395,6 +396,25 @@
                    Style));
 }
 
+TEST_F(FormatTest, KeepsLineBreaks) {
+  FormatStyle Style = getLLVMStyle();
+  EXPECT_EQ("if (a\n"
+            "    && b) {\n"
+            "}",
+            format("if (a\n"
+                   "    && b) {\n"
+                   "}",
+                   Style, SC_ExpectComplete,
+                   ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines));
+
+  EXPECT_EQ("[]() {\n"
+            "  foo(); }",
+            format("[]() {\n"
+                   "foo(); }",
+                   Style, SC_ExpectComplete,
+                   ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines));
+}
+
 TEST_F(FormatTest, RecognizesBinaryOperatorKeywords) {
   verifyFormat("x = (a) and (b);");
   verifyFormat("x = (a) or (b);");
Index: tools/clang-format-ide/ClangFormatIDE.cpp
===================================================================
--- tools/clang-format-ide/ClangFormatIDE.cpp
+++ tools/clang-format-ide/ClangFormatIDE.cpp
@@ -0,0 +1,102 @@
+//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements a clang-format tool that automatically formats
+/// (fragments of) C++ code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "../clang-format/ClangFormat.h"
+
+static cl::opt<bool>
+    KeepLineBreaks("keep-line-breaks",
+                   cl::desc("If set, keeps all existing line breaks"),
+                   cl::cat(ClangFormatCategory));
+
+static void PrintVersion(raw_ostream &OS) {
+  OS << clang::getClangToolFullVersion("clang-format-ide") << '\n';
+}
+
+int main(int argc, const char **argv) {
+  llvm::InitLLVM X(argc, argv);
+
+  cl::HideUnrelatedOptions(ClangFormatCategory);
+
+  cl::SetVersionPrinter(PrintVersion);
+  cl::ParseCommandLineOptions(
+      argc, argv,
+      "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.\n\n"
+      "If no arguments are specified, it formats the code from standard input\n"
+      "and writes the result to the standard output.\n"
+      "If <file>s are given, it reformats the files. If -i is specified\n"
+      "together with <file>s, the files are edited in-place. Otherwise, the\n"
+      "result is written to the standard output.\n");
+
+  if (Help) {
+    cl::PrintHelpMessage();
+    return 0;
+  }
+
+  if (DumpConfig) {
+    StringRef FileName;
+    std::unique_ptr<llvm::MemoryBuffer> Code;
+    if (FileNames.empty()) {
+      // We can't read the code to detect the language if there's no
+      // file name, so leave Code empty here.
+      FileName = AssumeFileName;
+    } else {
+      // Read in the code in case the filename alone isn't enough to
+      // detect the language.
+      ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+          MemoryBuffer::getFileOrSTDIN(FileNames[0]);
+      if (std::error_code EC = CodeOrErr.getError()) {
+        llvm::errs() << EC.message() << "\n";
+        return 1;
+      }
+      FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
+      Code = std::move(CodeOrErr.get());
+    }
+    llvm::Expected<clang::format::FormatStyle> FormatStyle =
+        clang::format::getStyle(Style, FileName, FallbackStyle,
+                                Code ? Code->getBuffer() : "");
+    if (!FormatStyle) {
+      llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
+      return 1;
+    }
+    std::string Config = clang::format::configurationAsText(*FormatStyle);
+    outs() << Config << "\n";
+    return 0;
+  }
+
+  clang::format::ExtraFormattingOptions FormattingOptions =
+      clang::format::ExtraFormattingOptions::None;
+  if (KeepLineBreaks.getNumOccurrences() != 0) {
+    FormattingOptions =
+        FormattingOptions |
+        clang::format::ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines;
+  }
+
+  bool Error = false;
+  if (FileNames.empty()) {
+    Error = clang::format::format("-", FormattingOptions);
+    return Error ? 1 : 0;
+  }
+  if (FileNames.size() != 1 &&
+      (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
+    errs() << "error: -offset, -length and -lines can only be used for "
+              "single file.\n";
+    return 1;
+  }
+  for (const auto &FileName : FileNames) {
+    if (Verbose)
+      errs() << "Formatting " << FileName << "\n";
+    Error |= clang::format::format(FileName, FormattingOptions);
+  }
+  return Error ? 1 : 0;
+}
Index: tools/clang-format-ide/CMakeLists.txt
===================================================================
--- tools/clang-format-ide/CMakeLists.txt
+++ tools/clang-format-ide/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_tool(clang-format-ide
+  ClangFormatIDE.cpp
+  )
+
+set(CLANG_FORMAT_LIB_DEPS
+  clangBasic
+  clangFormat
+  clangRewrite
+  clangToolingCore
+  )
+
+target_link_libraries(clang-format-ide
+  PRIVATE
+  ${CLANG_FORMAT_LIB_DEPS}
+  )
Index: tools/clang-format/ClangFormat.h
===================================================================
--- tools/clang-format/ClangFormat.h
+++ tools/clang-format/ClangFormat.h
@@ -0,0 +1,339 @@
+//===- ClangFormat.h - Main formatting functions for clang-format ----------==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef LLVM_CLANG_FORMAT_H
+#error ClangFormat.h can only be included once
+#else
+#define LLVM_CLANG_FORMAT_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/Format/Format.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Process.h"
+
+using namespace llvm;
+using clang::tooling::Replacements;
+
+static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
+
+// Mark all our options with this category, everything else (except for -version
+// and -help) will be hidden.
+static cl::OptionCategory ClangFormatCategory("Clang-format options");
+
+static cl::list<unsigned>
+    Offsets("offset",
+            cl::desc("Format a range starting at this byte offset.\n"
+                     "Multiple ranges can be formatted by specifying\n"
+                     "several -offset and -length pairs.\n"
+                     "Can only be used with one input file."),
+            cl::cat(ClangFormatCategory));
+static cl::list<unsigned>
+    Lengths("length",
+            cl::desc("Format a range of this length (in bytes).\n"
+                     "Multiple ranges can be formatted by specifying\n"
+                     "several -offset and -length pairs.\n"
+                     "When only a single -offset is specified without\n"
+                     "-length, clang-format will format up to the end\n"
+                     "of the file.\n"
+                     "Can only be used with one input file."),
+            cl::cat(ClangFormatCategory));
+static cl::list<std::string>
+    LineRanges("lines",
+               cl::desc("<start line>:<end line> - format a range of\n"
+                        "lines (both 1-based).\n"
+                        "Multiple ranges can be formatted by specifying\n"
+                        "several -lines arguments.\n"
+                        "Can't be used with -offset and -length.\n"
+                        "Can only be used with one input file."),
+               cl::cat(ClangFormatCategory));
+static cl::opt<std::string>
+    Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
+          cl::init(clang::format::DefaultFormatStyle),
+          cl::cat(ClangFormatCategory));
+static cl::opt<std::string>
+    FallbackStyle("fallback-style",
+                  cl::desc("The name of the predefined style used as a\n"
+                           "fallback in case clang-format is invoked with\n"
+                           "-style=file, but can not find the .clang-format\n"
+                           "file to use.\n"
+                           "Use -fallback-style=none to skip formatting."),
+                  cl::init(clang::format::DefaultFallbackStyle),
+                  cl::cat(ClangFormatCategory));
+
+static cl::opt<std::string> AssumeFileName(
+    "assume-filename",
+    cl::desc("When reading from stdin, clang-format assumes this\n"
+             "filename to look for a style config file (with\n"
+             "-style=file) and to determine the language."),
+    cl::init("<stdin>"), cl::cat(ClangFormatCategory));
+
+static cl::opt<bool> Inplace("i",
+                             cl::desc("Inplace edit <file>s, if specified."),
+                             cl::cat(ClangFormatCategory));
+
+static cl::opt<bool> OutputXML("output-replacements-xml",
+                               cl::desc("Output replacements as XML."),
+                               cl::cat(ClangFormatCategory));
+static cl::opt<bool>
+    DumpConfig("dump-config",
+               cl::desc("Dump configuration options to stdout and exit.\n"
+                        "Can be used with -style option."),
+               cl::cat(ClangFormatCategory));
+static cl::opt<unsigned>
+    Cursor("cursor",
+           cl::desc("The position of the cursor when invoking\n"
+                    "clang-format from an editor integration"),
+           cl::init(0), cl::cat(ClangFormatCategory));
+
+static cl::opt<bool> SortIncludes(
+    "sort-includes",
+    cl::desc("If set, overrides the include sorting behavior determined by the "
+             "SortIncludes style flag"),
+    cl::cat(ClangFormatCategory));
+
+static cl::opt<bool>
+    Verbose("verbose", cl::desc("If set, shows the list of processed files"),
+            cl::cat(ClangFormatCategory));
+
+static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
+                                       cl::cat(ClangFormatCategory));
+
+namespace clang {
+namespace format {
+
+static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source,
+                                 SourceManager &Sources, FileManager &Files,
+                                 llvm::vfs::InMemoryFileSystem *MemFS) {
+  MemFS->addFileNoOwn(FileName, 0, Source);
+  return Sources.createFileID(Files.getFile(FileName), SourceLocation(),
+                              SrcMgr::C_User);
+}
+
+// Parses <start line>:<end line> input to a pair of line numbers.
+// Returns true on error.
+static bool parseLineRange(StringRef Input, unsigned &FromLine,
+                           unsigned &ToLine) {
+  std::pair<StringRef, StringRef> LineRange = Input.split(':');
+  return LineRange.first.getAsInteger(0, FromLine) ||
+         LineRange.second.getAsInteger(0, ToLine);
+}
+
+static bool fillRanges(MemoryBuffer *Code,
+                       std::vector<tooling::Range> &Ranges) {
+  IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new llvm::vfs::InMemoryFileSystem);
+  FileManager Files(FileSystemOptions(), InMemoryFileSystem);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+      new DiagnosticOptions);
+  SourceManager Sources(Diagnostics, Files);
+  FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files,
+                                 InMemoryFileSystem.get());
+  if (!LineRanges.empty()) {
+    if (!Offsets.empty() || !Lengths.empty()) {
+      errs() << "error: cannot use -lines with -offset/-length\n";
+      return true;
+    }
+
+    for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
+      unsigned FromLine, ToLine;
+      if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
+        errs() << "error: invalid <start line>:<end line> pair\n";
+        return true;
+      }
+      if (FromLine > ToLine) {
+        errs() << "error: start line should be less than end line\n";
+        return true;
+      }
+      SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
+      SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
+      if (Start.isInvalid() || End.isInvalid())
+        return true;
+      unsigned Offset = Sources.getFileOffset(Start);
+      unsigned Length = Sources.getFileOffset(End) - Offset;
+      Ranges.push_back(tooling::Range(Offset, Length));
+    }
+    return false;
+  }
+
+  if (Offsets.empty())
+    Offsets.push_back(0);
+  if (Offsets.size() != Lengths.size() &&
+      !(Offsets.size() == 1 && Lengths.empty())) {
+    errs() << "error: number of -offset and -length arguments must match.\n";
+    return true;
+  }
+  for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
+    if (Offsets[i] >= Code->getBufferSize()) {
+      errs() << "error: offset " << Offsets[i] << " is outside the file\n";
+      return true;
+    }
+    SourceLocation Start =
+        Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
+    SourceLocation End;
+    if (i < Lengths.size()) {
+      if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
+        errs() << "error: invalid length " << Lengths[i]
+               << ", offset + length (" << Offsets[i] + Lengths[i]
+               << ") is outside the file.\n";
+        return true;
+      }
+      End = Start.getLocWithOffset(Lengths[i]);
+    } else {
+      End = Sources.getLocForEndOfFile(ID);
+    }
+    unsigned Offset = Sources.getFileOffset(Start);
+    unsigned Length = Sources.getFileOffset(End) - Offset;
+    Ranges.push_back(tooling::Range(Offset, Length));
+  }
+  return false;
+}
+
+static void outputReplacementXML(StringRef Text) {
+  // FIXME: When we sort includes, we need to make sure the stream is correct
+  // utf-8.
+  size_t From = 0;
+  size_t Index;
+  while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
+    outs() << Text.substr(From, Index - From);
+    switch (Text[Index]) {
+    case '\n':
+      outs() << "&#10;";
+      break;
+    case '\r':
+      outs() << "&#13;";
+      break;
+    case '<':
+      outs() << "&lt;";
+      break;
+    case '&':
+      outs() << "&amp;";
+      break;
+    default:
+      llvm_unreachable("Unexpected character encountered!");
+    }
+    From = Index + 1;
+  }
+  outs() << Text.substr(From);
+}
+
+static void outputReplacementsXML(const Replacements &Replaces) {
+  for (const auto &R : Replaces) {
+    outs() << "<replacement "
+           << "offset='" << R.getOffset() << "' "
+           << "length='" << R.getLength() << "'>";
+    outputReplacementXML(R.getReplacementText());
+    outs() << "</replacement>\n";
+  }
+}
+
+// Returns true on error.
+static bool format(
+    StringRef FileName,
+    ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None) {
+  if (!OutputXML && Inplace && FileName == "-") {
+    errs() << "error: cannot use -i when reading from stdin.\n";
+    return false;
+  }
+  // On Windows, overwriting a file with an open file mapping doesn't work,
+  // so read the whole file into memory when formatting in-place.
+  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+      !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
+                            : MemoryBuffer::getFileOrSTDIN(FileName);
+  if (std::error_code EC = CodeOrErr.getError()) {
+    errs() << EC.message() << "\n";
+    return true;
+  }
+  std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
+  if (Code->getBufferSize() == 0)
+    return false; // Empty files are formatted correctly.
+  std::vector<tooling::Range> Ranges;
+  if (fillRanges(Code.get(), Ranges))
+    return true;
+  StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
+
+  llvm::Expected<FormatStyle> FormatStyle =
+      getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer());
+  if (!FormatStyle) {
+    llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
+    return true;
+  }
+
+  if (SortIncludes.getNumOccurrences() != 0)
+    FormatStyle->SortIncludes = SortIncludes;
+  unsigned CursorPosition = Cursor;
+  Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
+                                       AssumedFileName, &CursorPosition);
+  auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
+  if (!ChangedCode) {
+    llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+    return true;
+  }
+  // Get new affected ranges after sorting `#includes`.
+  Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
+  FormattingAttemptStatus Status;
+  Replacements FormatChanges =
+      reformat(*FormatStyle, FormattingOptions, *ChangedCode, Ranges,
+               AssumedFileName, &Status);
+  Replaces = Replaces.merge(FormatChanges);
+  if (OutputXML) {
+    outs() << "<?xml version='1.0'?>\n<replacements "
+              "xml:space='preserve' incomplete_format='"
+           << (Status.FormatComplete ? "false" : "true") << "'";
+    if (!Status.FormatComplete)
+      outs() << " line='" << Status.Line << "'";
+    outs() << ">\n";
+    if (Cursor.getNumOccurrences() != 0)
+      outs() << "<cursor>"
+             << FormatChanges.getShiftedCodePosition(CursorPosition)
+             << "</cursor>\n";
+
+    outputReplacementsXML(Replaces);
+    outs() << "</replacements>\n";
+  } else {
+    IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
+        new llvm::vfs::InMemoryFileSystem);
+    FileManager Files(FileSystemOptions(), InMemoryFileSystem);
+    DiagnosticsEngine Diagnostics(
+        IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+        new DiagnosticOptions);
+    SourceManager Sources(Diagnostics, Files);
+    FileID ID = createInMemoryFile(AssumedFileName, Code.get(), Sources, Files,
+                                   InMemoryFileSystem.get());
+    Rewriter Rewrite(Sources, LangOptions());
+    tooling::applyAllReplacements(Replaces, Rewrite);
+    if (Inplace) {
+      if (Rewrite.overwriteChangedFiles())
+        return true;
+    } else {
+      if (Cursor.getNumOccurrences() != 0) {
+        outs() << "{ \"Cursor\": "
+               << FormatChanges.getShiftedCodePosition(CursorPosition)
+               << ", \"IncompleteFormat\": "
+               << (Status.FormatComplete ? "false" : "true");
+        if (!Status.FormatComplete)
+          outs() << ", \"Line\": " << Status.Line;
+        outs() << " }\n";
+      }
+      Rewrite.getEditBuffer(ID).write(outs());
+    }
+  }
+  return false;
+}
+
+} // namespace format
+} // namespace clang
+
+#endif
Index: tools/clang-format/ClangFormat.cpp
===================================================================
--- tools/clang-format/ClangFormat.cpp
+++ tools/clang-format/ClangFormat.cpp
@@ -12,326 +12,7 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Basic/DiagnosticOptions.h"
-#include "clang/Basic/FileManager.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/Version.h"
-#include "clang/Format/Format.h"
-#include "clang/Rewrite/Core/Rewriter.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/InitLLVM.h"
-#include "llvm/Support/Process.h"
-
-using namespace llvm;
-using clang::tooling::Replacements;
-
-static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
-
-// Mark all our options with this category, everything else (except for -version
-// and -help) will be hidden.
-static cl::OptionCategory ClangFormatCategory("Clang-format options");
-
-static cl::list<unsigned>
-    Offsets("offset",
-            cl::desc("Format a range starting at this byte offset.\n"
-                     "Multiple ranges can be formatted by specifying\n"
-                     "several -offset and -length pairs.\n"
-                     "Can only be used with one input file."),
-            cl::cat(ClangFormatCategory));
-static cl::list<unsigned>
-    Lengths("length",
-            cl::desc("Format a range of this length (in bytes).\n"
-                     "Multiple ranges can be formatted by specifying\n"
-                     "several -offset and -length pairs.\n"
-                     "When only a single -offset is specified without\n"
-                     "-length, clang-format will format up to the end\n"
-                     "of the file.\n"
-                     "Can only be used with one input file."),
-            cl::cat(ClangFormatCategory));
-static cl::list<std::string>
-LineRanges("lines", cl::desc("<start line>:<end line> - format a range of\n"
-                             "lines (both 1-based).\n"
-                             "Multiple ranges can be formatted by specifying\n"
-                             "several -lines arguments.\n"
-                             "Can't be used with -offset and -length.\n"
-                             "Can only be used with one input file."),
-           cl::cat(ClangFormatCategory));
-static cl::opt<std::string>
-    Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
-          cl::init(clang::format::DefaultFormatStyle),
-          cl::cat(ClangFormatCategory));
-static cl::opt<std::string>
-    FallbackStyle("fallback-style",
-                  cl::desc("The name of the predefined style used as a\n"
-                           "fallback in case clang-format is invoked with\n"
-                           "-style=file, but can not find the .clang-format\n"
-                           "file to use.\n"
-                           "Use -fallback-style=none to skip formatting."),
-                  cl::init(clang::format::DefaultFallbackStyle),
-                  cl::cat(ClangFormatCategory));
-
-static cl::opt<std::string>
-AssumeFileName("assume-filename",
-               cl::desc("When reading from stdin, clang-format assumes this\n"
-                        "filename to look for a style config file (with\n"
-                        "-style=file) and to determine the language."),
-               cl::init("<stdin>"), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> Inplace("i",
-                             cl::desc("Inplace edit <file>s, if specified."),
-                             cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> OutputXML("output-replacements-xml",
-                               cl::desc("Output replacements as XML."),
-                               cl::cat(ClangFormatCategory));
-static cl::opt<bool>
-    DumpConfig("dump-config",
-               cl::desc("Dump configuration options to stdout and exit.\n"
-                        "Can be used with -style option."),
-               cl::cat(ClangFormatCategory));
-static cl::opt<unsigned>
-    Cursor("cursor",
-           cl::desc("The position of the cursor when invoking\n"
-                    "clang-format from an editor integration"),
-           cl::init(0), cl::cat(ClangFormatCategory));
-
-static cl::opt<bool> SortIncludes(
-    "sort-includes",
-    cl::desc("If set, overrides the include sorting behavior determined by the "
-             "SortIncludes style flag"),
-    cl::cat(ClangFormatCategory));
-
-static cl::opt<bool>
-    Verbose("verbose", cl::desc("If set, shows the list of processed files"),
-            cl::cat(ClangFormatCategory));
-
-static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
-                                       cl::cat(ClangFormatCategory));
-
-namespace clang {
-namespace format {
-
-static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source,
-                                 SourceManager &Sources, FileManager &Files,
-                                 llvm::vfs::InMemoryFileSystem *MemFS) {
-  MemFS->addFileNoOwn(FileName, 0, Source);
-  return Sources.createFileID(Files.getFile(FileName), SourceLocation(),
-                              SrcMgr::C_User);
-}
-
-// Parses <start line>:<end line> input to a pair of line numbers.
-// Returns true on error.
-static bool parseLineRange(StringRef Input, unsigned &FromLine,
-                           unsigned &ToLine) {
-  std::pair<StringRef, StringRef> LineRange = Input.split(':');
-  return LineRange.first.getAsInteger(0, FromLine) ||
-         LineRange.second.getAsInteger(0, ToLine);
-}
-
-static bool fillRanges(MemoryBuffer *Code,
-                       std::vector<tooling::Range> &Ranges) {
-  IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
-      new llvm::vfs::InMemoryFileSystem);
-  FileManager Files(FileSystemOptions(), InMemoryFileSystem);
-  DiagnosticsEngine Diagnostics(
-      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
-      new DiagnosticOptions);
-  SourceManager Sources(Diagnostics, Files);
-  FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files,
-                                 InMemoryFileSystem.get());
-  if (!LineRanges.empty()) {
-    if (!Offsets.empty() || !Lengths.empty()) {
-      errs() << "error: cannot use -lines with -offset/-length\n";
-      return true;
-    }
-
-    for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
-      unsigned FromLine, ToLine;
-      if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
-        errs() << "error: invalid <start line>:<end line> pair\n";
-        return true;
-      }
-      if (FromLine > ToLine) {
-        errs() << "error: start line should be less than end line\n";
-        return true;
-      }
-      SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
-      SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
-      if (Start.isInvalid() || End.isInvalid())
-        return true;
-      unsigned Offset = Sources.getFileOffset(Start);
-      unsigned Length = Sources.getFileOffset(End) - Offset;
-      Ranges.push_back(tooling::Range(Offset, Length));
-    }
-    return false;
-  }
-
-  if (Offsets.empty())
-    Offsets.push_back(0);
-  if (Offsets.size() != Lengths.size() &&
-      !(Offsets.size() == 1 && Lengths.empty())) {
-    errs() << "error: number of -offset and -length arguments must match.\n";
-    return true;
-  }
-  for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
-    if (Offsets[i] >= Code->getBufferSize()) {
-      errs() << "error: offset " << Offsets[i] << " is outside the file\n";
-      return true;
-    }
-    SourceLocation Start =
-        Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
-    SourceLocation End;
-    if (i < Lengths.size()) {
-      if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
-        errs() << "error: invalid length " << Lengths[i]
-               << ", offset + length (" << Offsets[i] + Lengths[i]
-               << ") is outside the file.\n";
-        return true;
-      }
-      End = Start.getLocWithOffset(Lengths[i]);
-    } else {
-      End = Sources.getLocForEndOfFile(ID);
-    }
-    unsigned Offset = Sources.getFileOffset(Start);
-    unsigned Length = Sources.getFileOffset(End) - Offset;
-    Ranges.push_back(tooling::Range(Offset, Length));
-  }
-  return false;
-}
-
-static void outputReplacementXML(StringRef Text) {
-  // FIXME: When we sort includes, we need to make sure the stream is correct
-  // utf-8.
-  size_t From = 0;
-  size_t Index;
-  while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
-    outs() << Text.substr(From, Index - From);
-    switch (Text[Index]) {
-    case '\n':
-      outs() << "&#10;";
-      break;
-    case '\r':
-      outs() << "&#13;";
-      break;
-    case '<':
-      outs() << "&lt;";
-      break;
-    case '&':
-      outs() << "&amp;";
-      break;
-    default:
-      llvm_unreachable("Unexpected character encountered!");
-    }
-    From = Index + 1;
-  }
-  outs() << Text.substr(From);
-}
-
-static void outputReplacementsXML(const Replacements &Replaces) {
-  for (const auto &R : Replaces) {
-    outs() << "<replacement "
-           << "offset='" << R.getOffset() << "' "
-           << "length='" << R.getLength() << "'>";
-    outputReplacementXML(R.getReplacementText());
-    outs() << "</replacement>\n";
-  }
-}
-
-// Returns true on error.
-static bool format(StringRef FileName) {
-  if (!OutputXML && Inplace && FileName == "-") {
-    errs() << "error: cannot use -i when reading from stdin.\n";
-    return false;
-  }
-  // On Windows, overwriting a file with an open file mapping doesn't work,
-  // so read the whole file into memory when formatting in-place.
-  ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
-      !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) :
-                              MemoryBuffer::getFileOrSTDIN(FileName);
-  if (std::error_code EC = CodeOrErr.getError()) {
-    errs() << EC.message() << "\n";
-    return true;
-  }
-  std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
-  if (Code->getBufferSize() == 0)
-    return false; // Empty files are formatted correctly.
-  std::vector<tooling::Range> Ranges;
-  if (fillRanges(Code.get(), Ranges))
-    return true;
-  StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
-
-  llvm::Expected<FormatStyle> FormatStyle =
-      getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer());
-  if (!FormatStyle) {
-    llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
-    return true;
-  }
-
-  if (SortIncludes.getNumOccurrences() != 0)
-    FormatStyle->SortIncludes = SortIncludes;
-  unsigned CursorPosition = Cursor;
-  Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
-                                       AssumedFileName, &CursorPosition);
-  auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
-  if (!ChangedCode) {
-    llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
-    return true;
-  }
-  // Get new affected ranges after sorting `#includes`.
-  Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
-  FormattingAttemptStatus Status;
-  Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges,
-                                        AssumedFileName, &Status);
-  Replaces = Replaces.merge(FormatChanges);
-  if (OutputXML) {
-    outs() << "<?xml version='1.0'?>\n<replacements "
-              "xml:space='preserve' incomplete_format='"
-           << (Status.FormatComplete ? "false" : "true") << "'";
-    if (!Status.FormatComplete)
-      outs() << " line='" << Status.Line << "'";
-    outs() << ">\n";
-    if (Cursor.getNumOccurrences() != 0)
-      outs() << "<cursor>"
-             << FormatChanges.getShiftedCodePosition(CursorPosition)
-             << "</cursor>\n";
-
-    outputReplacementsXML(Replaces);
-    outs() << "</replacements>\n";
-  } else {
-    IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
-        new llvm::vfs::InMemoryFileSystem);
-    FileManager Files(FileSystemOptions(), InMemoryFileSystem);
-    DiagnosticsEngine Diagnostics(
-        IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
-        new DiagnosticOptions);
-    SourceManager Sources(Diagnostics, Files);
-    FileID ID = createInMemoryFile(AssumedFileName, Code.get(), Sources, Files,
-                                   InMemoryFileSystem.get());
-    Rewriter Rewrite(Sources, LangOptions());
-    tooling::applyAllReplacements(Replaces, Rewrite);
-    if (Inplace) {
-      if (Rewrite.overwriteChangedFiles())
-        return true;
-    } else {
-      if (Cursor.getNumOccurrences() != 0) {
-        outs() << "{ \"Cursor\": "
-               << FormatChanges.getShiftedCodePosition(CursorPosition)
-               << ", \"IncompleteFormat\": "
-               << (Status.FormatComplete ? "false" : "true");
-        if (!Status.FormatComplete)
-          outs() << ", \"Line\": " << Status.Line;
-        outs() << " }\n";
-      }
-      Rewrite.getEditBuffer(ID).write(outs());
-    }
-  }
-  return false;
-}
-
-}  // namespace format
-}  // namespace clang
+#include "ClangFormat.h"
 
 static void PrintVersion(raw_ostream &OS) {
   OS << clang::getClangToolFullVersion("clang-format") << '\n';
Index: tools/CMakeLists.txt
===================================================================
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -4,6 +4,7 @@
 add_clang_subdirectory(driver)
 add_clang_subdirectory(clang-diff)
 add_clang_subdirectory(clang-format)
+add_clang_subdirectory(clang-format-ide)
 add_clang_subdirectory(clang-format-vs)
 add_clang_subdirectory(clang-fuzzer)
 add_clang_subdirectory(clang-import-test)
Index: lib/Format/UnwrappedLineParser.h
===================================================================
--- lib/Format/UnwrappedLineParser.h
+++ lib/Format/UnwrappedLineParser.h
@@ -77,7 +77,8 @@
   UnwrappedLineParser(const FormatStyle &Style,
                       const AdditionalKeywords &Keywords,
                       unsigned FirstStartColumn, ArrayRef<FormatToken *> Tokens,
-                      UnwrappedLineConsumer &Callback);
+                      UnwrappedLineConsumer &Callback,
+                      ExtraFormattingOptions FormattingOptions);
 
   void parse();
 
@@ -274,6 +275,8 @@
   // does not start at the beginning of the file.
   unsigned FirstStartColumn;
 
+  ExtraFormattingOptions FormattingOptions;
+
   friend class ScopedLineState;
   friend class CompoundStatementIndenter;
 };
Index: lib/Format/UnwrappedLineParser.cpp
===================================================================
--- lib/Format/UnwrappedLineParser.cpp
+++ lib/Format/UnwrappedLineParser.cpp
@@ -222,11 +222,10 @@
 
 } // end anonymous namespace
 
-UnwrappedLineParser::UnwrappedLineParser(const FormatStyle &Style,
-                                         const AdditionalKeywords &Keywords,
-                                         unsigned FirstStartColumn,
-                                         ArrayRef<FormatToken *> Tokens,
-                                         UnwrappedLineConsumer &Callback)
+UnwrappedLineParser::UnwrappedLineParser(
+    const FormatStyle &Style, const AdditionalKeywords &Keywords,
+    unsigned FirstStartColumn, ArrayRef<FormatToken *> Tokens,
+    UnwrappedLineConsumer &Callback, ExtraFormattingOptions FormattingOptions)
     : Line(new UnwrappedLine), MustBreakBeforeNextToken(false),
       CurrentLines(&Lines), Style(Style), Keywords(Keywords),
       CommentPragmasRegex(Style.CommentPragmas), Tokens(nullptr),
@@ -234,7 +233,8 @@
       IncludeGuard(Style.IndentPPDirectives == FormatStyle::PPDIS_None
                        ? IG_Rejected
                        : IG_Inited),
-      IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn) {}
+      IncludeGuardToken(nullptr), FirstStartColumn(FirstStartColumn),
+      FormattingOptions(FormattingOptions) {}
 
 void UnwrappedLineParser::reset() {
   PPBranchLevel = -1;
@@ -2608,6 +2608,8 @@
   else
     readTokenWithJavaScriptASI();
   FormatTok->Previous = Previous;
+  if (FormatTok->NewlinesBefore && alwaysKeepLineBreaks(FormattingOptions))
+    FormatTok->MustBreakBefore = true;
 }
 
 void UnwrappedLineParser::distributeComments(
Index: lib/Format/UnwrappedLineFormatter.h
===================================================================
--- lib/Format/UnwrappedLineFormatter.h
+++ lib/Format/UnwrappedLineFormatter.h
@@ -32,9 +32,11 @@
                          const FormatStyle &Style,
                          const AdditionalKeywords &Keywords,
                          const SourceManager &SourceMgr,
+                         ExtraFormattingOptions FormattingOptions,
                          FormattingAttemptStatus *Status)
       : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),
-        Keywords(Keywords), SourceMgr(SourceMgr), Status(Status) {}
+        Keywords(Keywords), SourceMgr(SourceMgr),
+        FormattingOptions(FormattingOptions), Status(Status) {}
 
   /// Format the current block and return the penalty.
   unsigned format(const SmallVectorImpl<AnnotatedLine *> &Lines,
@@ -67,6 +69,7 @@
   const FormatStyle &Style;
   const AdditionalKeywords &Keywords;
   const SourceManager &SourceMgr;
+  ExtraFormattingOptions FormattingOptions;
   FormattingAttemptStatus *Status;
 };
 } // end namespace format
Index: lib/Format/UnwrappedLineFormatter.cpp
===================================================================
--- lib/Format/UnwrappedLineFormatter.cpp
+++ lib/Format/UnwrappedLineFormatter.cpp
@@ -682,11 +682,12 @@
 /// Base class for classes that format one \c AnnotatedLine.
 class LineFormatter {
 public:
-  LineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces,
-                const FormatStyle &Style,
-                UnwrappedLineFormatter *BlockFormatter)
+  LineFormatter(
+      ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces,
+      const FormatStyle &Style, UnwrappedLineFormatter *BlockFormatter,
+      ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None)
       : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style),
-        BlockFormatter(BlockFormatter) {}
+        BlockFormatter(BlockFormatter), FormattingOptions(FormattingOptions) {}
   virtual ~LineFormatter() {}
 
   /// Formats an \c AnnotatedLine and returns the penalty.
@@ -726,7 +727,8 @@
       // assert so that we can simply call this function for all tokens.
       return true;
 
-    if (NewLine) {
+    if (NewLine || (Previous.Children[0]->First->MustBreakBefore &&
+                    alwaysKeepLineBreaks(FormattingOptions))) {
       int AdditionalIndent = State.Stack.back().Indent -
                              Previous.Children[0]->Level * Style.IndentWidth;
 
@@ -776,6 +778,7 @@
   WhitespaceManager *Whitespaces;
   const FormatStyle &Style;
   UnwrappedLineFormatter *BlockFormatter;
+  ExtraFormattingOptions FormattingOptions;
 };
 
 /// Formatter that keeps the existing line breaks.
@@ -784,8 +787,10 @@
   NoColumnLimitLineFormatter(ContinuationIndenter *Indenter,
                              WhitespaceManager *Whitespaces,
                              const FormatStyle &Style,
-                             UnwrappedLineFormatter *BlockFormatter)
-      : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter) {}
+                             UnwrappedLineFormatter *BlockFormatter,
+                             ExtraFormattingOptions FormattingOptions)
+      : LineFormatter(Indenter, Whitespaces, Style, BlockFormatter,
+                      FormattingOptions) {}
 
   /// Formats the line, simply keeping all of the input's line breaking
   /// decisions.
@@ -821,7 +826,8 @@
     LineState State =
         Indenter->getInitialState(FirstIndent, FirstStartColumn, &Line, DryRun);
     while (State.NextToken) {
-      formatChildren(State, /*Newline=*/false, DryRun, Penalty);
+      bool Newline = false;
+      formatChildren(State, Newline, DryRun, Penalty);
       Indenter->addTokenToState(
           State, /*Newline=*/State.NextToken->MustBreakBefore, DryRun);
     }
@@ -1076,8 +1082,9 @@
             !Style.JavaScriptWrapImports)) ||
           (Style.isCSharp() &&
            TheLine.InPPDirective); // don't split #regions in C#
-      if (Style.ColumnLimit == 0)
-        NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this)
+      if (Style.ColumnLimit == 0 || alwaysKeepLineBreaks(FormattingOptions))
+        NoColumnLimitLineFormatter(Indenter, Whitespaces, Style, this,
+                                   FormattingOptions)
             .formatLine(TheLine, NextStartColumn + Indent,
                         FirstLine ? FirstStartColumn : 0, DryRun);
       else if (FitsIntoOneLine)
Index: lib/Format/TokenAnalyzer.h
===================================================================
--- lib/Format/TokenAnalyzer.h
+++ lib/Format/TokenAnalyzer.h
@@ -83,7 +83,9 @@
 
 class TokenAnalyzer : public UnwrappedLineConsumer {
 public:
-  TokenAnalyzer(const Environment &Env, const FormatStyle &Style);
+  TokenAnalyzer(
+      const Environment &Env, const FormatStyle &Style,
+      ExtraFormattingOptions FormattingOptions = ExtraFormattingOptions::None);
 
   std::pair<tooling::Replacements, unsigned> process();
 
@@ -104,6 +106,7 @@
   AffectedRangeManager AffectedRangeMgr;
   SmallVector<SmallVector<UnwrappedLine, 16>, 2> UnwrappedLines;
   encoding::Encoding Encoding;
+  ExtraFormattingOptions FormattingOptions;
 };
 
 } // end namespace format
Index: lib/Format/TokenAnalyzer.cpp
===================================================================
--- lib/Format/TokenAnalyzer.cpp
+++ lib/Format/TokenAnalyzer.cpp
@@ -48,12 +48,14 @@
   }
 }
 
-TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style)
+TokenAnalyzer::TokenAnalyzer(const Environment &Env, const FormatStyle &Style,
+                             ExtraFormattingOptions FormattingOptions)
     : Style(Style), Env(Env),
       AffectedRangeMgr(Env.getSourceManager(), Env.getCharRanges()),
       UnwrappedLines(1),
       Encoding(encoding::detectEncoding(
-          Env.getSourceManager().getBufferData(Env.getFileID()))) {
+          Env.getSourceManager().getBufferData(Env.getFileID()))),
+      FormattingOptions(FormattingOptions) {
   LLVM_DEBUG(
       llvm::dbgs() << "File encoding: "
                    << (Encoding == encoding::Encoding_UTF8 ? "UTF8" : "unknown")
@@ -68,7 +70,8 @@
                           Env.getFirstStartColumn(), Style, Encoding);
 
   UnwrappedLineParser Parser(Style, Tokens.getKeywords(),
-                             Env.getFirstStartColumn(), Tokens.lex(), *this);
+                             Env.getFirstStartColumn(), Tokens.lex(), *this,
+                             FormattingOptions);
   Parser.parse();
   assert(UnwrappedLines.rbegin()->empty());
   unsigned Penalty = 0;
Index: lib/Format/FormatInternal.h
===================================================================
--- lib/Format/FormatInternal.h
+++ lib/Format/FormatInternal.h
@@ -73,6 +73,7 @@
 reformat(const FormatStyle &Style, StringRef Code,
          ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
          unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName,
+         ExtraFormattingOptions FormattingOptions,
          FormattingAttemptStatus *Status);
 
 } // namespace internal
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -1239,8 +1239,9 @@
 class Formatter : public TokenAnalyzer {
 public:
   Formatter(const Environment &Env, const FormatStyle &Style,
+            ExtraFormattingOptions FormattingOptions,
             FormattingAttemptStatus *Status)
-      : TokenAnalyzer(Env, Style), Status(Status) {}
+      : TokenAnalyzer(Env, Style, FormattingOptions), Status(Status) {}
 
   std::pair<tooling::Replacements, unsigned>
   analyze(TokenAnnotator &Annotator,
@@ -1263,7 +1264,7 @@
     unsigned Penalty =
         UnwrappedLineFormatter(&Indenter, &Whitespaces, Style,
                                Tokens.getKeywords(), Env.getSourceManager(),
-                               Status)
+                               FormattingOptions, Status)
             .format(AnnotatedLines, /*DryRun=*/false,
                     /*AdditionalIndent=*/0,
                     /*FixBadIndentation=*/false,
@@ -2248,6 +2249,7 @@
 reformat(const FormatStyle &Style, StringRef Code,
          ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
          unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName,
+         ExtraFormattingOptions FormattingOptions,
          FormattingAttemptStatus *Status) {
   FormatStyle Expanded = expandPresets(Style);
   if (Expanded.DisableFormat)
@@ -2281,7 +2283,7 @@
     });
 
   Passes.emplace_back([&](const Environment &Env) {
-    return Formatter(Env, Expanded, Status).process();
+    return Formatter(Env, Expanded, FormattingOptions, Status).process();
   });
 
   auto Env =
@@ -2318,7 +2320,21 @@
   return internal::reformat(Style, Code, Ranges,
                             /*FirstStartColumn=*/0,
                             /*NextStartColumn=*/0,
-                            /*LastStartColumn=*/0, FileName, Status)
+                            /*LastStartColumn=*/0, FileName,
+                            ExtraFormattingOptions::None, Status)
+      .first;
+}
+
+tooling::Replacements reformat(const FormatStyle &Style,
+                               ExtraFormattingOptions FormattingOptions,
+                               StringRef Code, ArrayRef<tooling::Range> Ranges,
+                               StringRef FileName,
+                               FormattingAttemptStatus *Status) {
+  return internal::reformat(Style, Code, Ranges,
+                            /*FirstStartColumn=*/0,
+                            /*NextStartColumn=*/0,
+                            /*LastStartColumn=*/0, FileName, FormattingOptions,
+                            Status)
       .first;
 }
 
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -1563,6 +1563,7 @@
   std::pair<tooling::Replacements, unsigned> Fixes = internal::reformat(
       RawStringStyle, RawText, {tooling::Range(0, RawText.size())},
       FirstStartColumn, NextStartColumn, LastStartColumn, "<stdin>",
+      ExtraFormattingOptions::None,
       /*Status=*/nullptr);
 
   auto NewCode = applyAllReplacements(RawText, Fixes.first);
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -2089,6 +2089,31 @@
   unsigned Line = 0;
 };
 
+enum class ExtraFormattingOptions {
+  None = 0x0,
+  KeepLineBreaksForNonEmptyLines = 0x1
+};
+
+inline ExtraFormattingOptions operator|(ExtraFormattingOptions lhs,
+                                        ExtraFormattingOptions rhs) {
+  using T = std::underlying_type_t<ExtraFormattingOptions>;
+  return static_cast<ExtraFormattingOptions>(static_cast<T>(lhs) |
+                                             static_cast<T>(rhs));
+}
+
+inline ExtraFormattingOptions operator&(ExtraFormattingOptions lhs,
+                                        ExtraFormattingOptions rhs) {
+  using T = std::underlying_type_t<ExtraFormattingOptions>;
+  return static_cast<ExtraFormattingOptions>(static_cast<T>(lhs) &
+                                             static_cast<T>(rhs));
+}
+
+inline bool alwaysKeepLineBreaks(ExtraFormattingOptions FormattingOptions) {
+  return (FormattingOptions &
+          ExtraFormattingOptions::KeepLineBreaksForNonEmptyLines) !=
+         ExtraFormattingOptions::None;
+}
+
 /// Reformats the given \p Ranges in \p Code.
 ///
 /// Each range is extended on either end to its next bigger logic unit, i.e.
@@ -2113,6 +2138,12 @@
                                StringRef FileName,
                                bool *IncompleteFormat);
 
+tooling::Replacements reformat(const FormatStyle &Style,
+                               ExtraFormattingOptions FormattingOptions,
+                               StringRef Code, ArrayRef<tooling::Range> Ranges,
+                               StringRef FileName = "<stdin>",
+                               FormattingAttemptStatus *Status = nullptr);
+
 /// Clean up any erroneous/redundant code in the given \p Ranges in \p
 /// Code.
 ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to