Clang-format line breaks
Asked Answered
M

5

82

I'm looking for a clang-format setting to prevent the tool from removing line breaks.

For example, I have my ColumnLimit set to 120, and here's what happens when I reformat some sample code.

Before:

#include <vector>
#include <string>

std::vector<std::string> get_vec()
{
   return std::vector<std::string> {
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines"
   };
}

int main()
{
   auto vec = get_vec();
}

After:

#include <vector>
#include <string>

std::vector<std::string> get_vec()
{
   return std::vector<std::string>{"this is a test", "some of the lines are longer", "than other, but I would like",
         "to keep them on separate lines"};
}

int main()
{
   auto vec = get_vec();
}

What I would like is that the tool breaks lines that are over 120 characters, but doesn't decide to combine lines just because they are less than 120 characters.

Is there such an option? Nothing in the docs stood out to me.

Multicolor answered 11/11, 2015 at 17:56 Comment(7)
For your particular example, set AllowShortFunctionsOnASingleLine: None will work.Variform
Well that stops it from unwrapping main but I'm more concerned about the vector initialization in general. If the vector was initialized this way in another (longer) function, it would still get unwrapped.Multicolor
I'm wondering if tweaking the various "penalty" options would help, but they all seem to be penalties associated with breaking lines, not "unbreaking" them.Multicolor
Can you give an example where the above setting doesn't work as expected? The tool no longer shorten your functions, and should respect the column limit. The vector initialization should also work.Variform
I updated the example in the question. With AllowShortFunctionsOnASingleLine: None, main is no longer unwrapped, but as you can see the vector initialization still gets messed up.Multicolor
unrelated to your question, but the return statement could be more simply return {"this is a test", ...Ochrea
I add a comment //- to the line break that I want to preserve.Younglove
O
71

So, having messed around in the clang format code and made some patches, here's my two cents:

  • Clang format is based on,

    • parsing the AST using libclang, which basically eliminates all whitespace
    • breaking up the token sequence into "unwrapped lines" which are like "logical" code lines
    • Applying rules / configuration info to sometimes split up "unwrapped lines" into smaller units
    • Spit it all back out again with new whitespace / indentation

    It's not easy to make it respect the original whitepsace, that sort of gets tossed when you first parse the code.

  • You can control where it places line breaks, most easily, by

    • setting the column limit
    • using the "bin pack parameters" options
    • setting penalties for various kinds of breaks -- break after return type of a function, break before first call parameter, break a string literal, break a comment...
    • placing comments at the end of a line (clang format cannot remove the comment and must therefore split the line)
    • use the clang-format off / on directives

Here's one thing you could try:

std::vector<std::string> get_vec()
{
   return std::vector<std::string> {   //
      "this is a test",                //
      "some of the lines are longer",  //
      "than other, but I would like",  //
      "to keep them on separate lines" //
   };
}

The advantage of this over // clang-format off is that, if you later change the tab width or some other option, those code lines will still get those formatting changes so you don't need to manually go into the // clang-format off regions to fix it. However it's still a bit of a hack, YMMV.

Ultimately, clang-format is very much about imposing a uniform format over an entire code base, making sure that all string literals are formatted in the same style everywhere in your program. If you want to have micro-level control over line-break decisions, that's not really in the spirit of the tool, and you'll have to do things like disable it.

This can sometimes be frustrating esp. when you want to do things with arrays and have columns aligned or something -- for instance, here's some natural code from lua C api:

static luaL_Reg const methods[] = {
    {"matches",               &dispatch::intf_match_unit},
    {"to_recall",             &dispatch::intf_put_recall_unit},
    {"to_map",                &dispatch::intf_put_unit},
    {"erase",                 &dispatch::intf_erase_unit},
    {"clone",                 intf_copy_unit},
    {"extract",               &dispatch::intf_extract_unit},
    {"advance",               intf_advance_unit},
};

When clang-format runs over that, it's generally not going to align the right column, its going to place it a fixed number of spaces after the commas and there's not much you can do about it afaik.

Or, if you have 4 x 4 matrix for use with OpenGL:

      constexpr float shadow_skew_hardcoded[16] =
        { 1.0f, 0.0f, 0.0f, 0.0f,
          0.5f, 0.5f, 0.0f, 0.0f,
          0.0f, 0.0f, 1.0f, 0.0f,
          0.0f, 0.0f, 0.0f, 1.0f };

If you let clang-format run over things like this it's just going to mangle them, and afaik there's no easy way to make it format them nicely, so you just have to resort either to the "lots of trivial comments" hack, or use clang-format off when you have something like this. These are just intrinsic limitations of the tool. If you aren't happy ever to have to do things like that then it's probably not the tool for you.

Oballa answered 18/12, 2015 at 19:11 Comment(4)
Great answer. Your suggestion seems a little bit better than // clang-format off, and the explanation as to why it behaves this way makes a lot of sense.Multicolor
ColumnLimit 0 still preserve the line breaks. How explain this?Gymnastic
I had to adapt your technique as I was using a multiline #define macro. What did the trick was ending each line with ` /**/\`Hilliard
@Gymnastic How about you want to keep max line length to 120 also?Anachronous
R
21

Add a comma after the last string. This tells clang-format to format it vertically. Ex: https://godbolt.org/z/bZxr__ Right click > format text

#include <string>
#include <vector>

std::vector<std::string> get_vec() {
  return std::vector<std::string>{
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines", // comma here after last element
  };
}

int main() { auto vec = get_vec(); }
Repertoire answered 29/5, 2019 at 8:36 Comment(3)
I like it, but this is very non-obvious to me. Why does it work? Is there a style option that controls it?Lighthearted
Trailing commas in enums do the same trickGareth
Sadly this doesn't work for function calls since you can't have a dangling comma yet.Begat
S
20

I'm not sure that you clang-format to do exactly what you want, but it is possible to tell clang-format to leave sections of code alone. I use this for exactly the sort of scenario you're talking about, blocks of code where a very particular formatting makes it easier to read.

std::vector<std::string> get_vec()
{
   // clang-format off
   return std::vector<std::string> {
      "this is a test",
      "some of the lines are longer",
      "than other, but I would like",
      "to keep them on separate lines"
   };
   // clang-format on
}

See: http://clang.llvm.org/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code

Sammy answered 18/12, 2015 at 10:12 Comment(2)
I'm not saying this is a bad answer but what an absolutely horrible solution to the problem. Clutter your code with comments to make the automatic formatting work.Elfreda
Perhaps, but in practice I've used it rarely where I want to break the formatting rules. I usually appreciate having an out like this, and don't appreciate it being abused by 'cluttering your code'. Using a tool badly is often on the userSammy
S
11

I didn't see anything currently in the documentation that would allow you to do that.

Setting the ColumnLimit to 0 will still preserve the text wrapping.

clang-format-mp-3.4 test.c -style="{ ColumnLimit: 0 }"

#include <vector>
#include <memory>
#include <string>

int main() {
  std::vector<std::string> vec = {
    "this is a test",
    "with some strings",
    "that I want on separate lines"
  };
}
Schuller answered 17/12, 2015 at 16:20 Comment(3)
He also wants to set ColumnLimit to 120?Communard
Right. This preserves my wrapping but does nothing to fix lines that are too long (which is a major advantage of the tool, IMO).Multicolor
This is a compromise, but a no-brainer for me. I'm happy to let the programmers wrap at 100 manually, if clang-format does not mangle with the new lines in my array definitions, in my more complex std::cout expressions, my enum, my parameter lists, ...Merat
T
1

With these rules in .clang-format

BasedOnStyle: LLVM
AlignAfterOpenBracket: AlwaysBreak
AllowShortBlocksOnASingleLine: Empty
BreakConstructorInitializers: AfterColon
BinPackArguments: false  // Important for this case
BinPackParameters: false  // Important for this case
AlignEscapedNewlines: DontAlign
SpacesBeforeTrailingComments: 2
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None
ContinuationIndentWidth: 2
IndentWidth: 2
Standard: c++17
UseTab: Never

I got formatting that is close to expected result

#include <string>
#include <vector>

std::vector<std::string> get_vec() {
  return std::vector<std::string>{
    "this is a test",
    "some of the lines are longer",
    "than other, but I would like",
    "to keep them on separate lines"};
}

int main() {
  auto vec = get_vec();
}

However, it will not work if you want to set ColumnLimit to non-zero value:

  ValidateVisitor(clang::CompilerInstance *Compiler) :
      Compiler(Compiler), Context(&Compiler->getASTContext()),
      SM(&Context->getSourceManager()) {
  }
Transmit answered 5/8, 2021 at 19:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.