IDE 2.3.6, Allow Serial.print(); Serial.print(); on same line

Is there a way to change the .clang-format file to allow Auto Format to leave lines:

  Serial.print("Var Value is:"); Serial.println(var);

on the same line so it does NOT reformat as:

  Serial.print("Var Value is:");
  Serial.println(var);

I have a fairly good size project and I prefer to keep all of the Debug Prints on a single line... It makes it SO MUCH EASIER to comment them out, and doesn't clutter up the code so much.

I would also like to allow this for the 'switch' statement where the code is:

void morse(byte letter) {
switch (letter) {
   case 'a': {dot(); dash(); break;}
   case 'b': {dash(); dot(); dot(); dot(); break;}

I don't want the Auto Format to change it to:

void morse(byte letter) {
  switch (letter) {
    case 'a':
      {
        dot();
        dash();
        break;
      }
    case 'b':
      {
        dash();
        dot();
        dot();
        dot();
        break;
      }

I can't seem to identify anything .clang-format file that might allow it.

Sir Michael

Try adding unnecessary whitespace. The IDE seems to skip over those when I autoformat. [edit] but not always.

I am fairly new to arduino too but try formatting the string before you print it. Using snprintf or sprintf you can format a string and and print it into another string if that makes sense. It is similar to fprintf where you are printing to a file or the wrapper printf which prints to stdout. You can search for them on the GNU C Library documentation https://sourceware.org/glibc/manual/latest/html_mono/libc.html#Opening-a-Directory. This will show you how to format the function, what it returns, and other useful information. You can search for any string on that website because it is the entire documentation on one static html page so a simple ctrl + f with your search parameter works fine.

OP is referring to in-code format (whitespace).

I do not immediately see an option in Clang-Format Style Options — Clang 21.0.0git documentation.

The workaround would be

void setup()
{
  // clang-format off
  Serial.print("Hallo"); Serial.println(" world");
  // clang-format on
}

void loop()
{
  // put your main code here, to run repeatedly:
}

The prints will be untouched.

Depending what type of Arduino you are using, you could change them to

Serial.printf("Var Value is:%d", var);

You could use this method:

#define DEBUG
...
#ifdef DEBUG
  Serial.print("Var Value is:");
  Serial.println(var);
#endif

Then, if you comment out just that #define, all your debug prints will be ignored.

You don't need those {} inside the swich-case statement. In fact, doing so can cause problems sometimes.

What about something like this?

void send(char m[]) {
  for (char j : m) {
    switch (j) {
      case '.':
        dot();
        break;
      case '-':
        dash();
        break;
    }
  }
}

void morse(byte letter) {
  switch (letter) {
    case 'a':
        send(".-");
        break;
    case 'b':
        send("-...");
        break;

The issue is not format of the output, rather, format inside the IDE.

I feel your pain, @SirMichael

// https://upload.wikimedia.org/wikipedia/commons/b/b5/International_Morse_Code.svg

char message[] = {"Hello, World!"}; // punctuation is ignored
byte oneElement = 100; //  element time in microseconds
byte ditPin = 8; // wht LED
byte dahPin = 9; // red LED
byte buzPin = 11;
int ditFrq = 440;
int dahFrq = 220;

/*
  - Morse timing...
  - One element for DIT/DOT
  - One element for intra-element spacing
  - Three elements for DAH/DASH
  - Three elements for intra-character spacing
  - Seven elements for intra-word spacing
  - Punctuation is ignored
*/

void setup() {
  Serial.begin(115200);
  pinMode(ditPin, OUTPUT);
  pinMode(dahPin, OUTPUT);
  delay(1000); // delay visual start

  for (int i = 0; i < strlen(message); i++) {
    Serial.print(message[i]); // print character in Serial Monitor
    xlat(message[i]); // translate char into morse
  }
}

void loop() {} // to repeat send, move xlat() into loop()

void dit() { // "dot" element
  key(1, ditPin);
}

void dah() { // "dash" element
  key (3, dahPin);
}

void key(byte elements, int led) { // blink LED and sound buzzer
  elements == 1 ?  tone(buzPin, ditFrq) : tone(buzPin, dahFrq); // dit or dah tone
  digitalWrite(led, HIGH);
  delay(elements * oneElement);
  noTone(buzPin);
  digitalWrite(led, LOW);
  delay(oneElement);
}

void ieg() {
  delay(1 * oneElement); // intra-element gap - built-in to each element
}
void icg() {
  delay(3 * oneElement); // intra-character gap - place between characters
}
void iwg() {
  delay(7 * oneElement); // intra-word gap - place between words
}

void xlat(char i) {
  switch (i) { // UPPER CASE cascade to lower case
    case 'A': case 'a': dit(); dah(); break;
    case 'B': case 'b': dah(); dit(); dit(); dit(); break;
    case 'C': case 'c': dah(); dit(); break;
    case 'D': case 'd': dah(); dit(); dit(); break;
    case 'E': case 'e': dit(); break;
    case 'F': case 'f': dit(); dit(); dah(); dit(); break;
    case 'G': case 'g': dah(); dah(); dit(); break;
    case 'H': case 'h': dit(); dit(); dit(); dit(); break;
    case 'I': case 'i': dit(); dit(); break;
    case 'J': case 'j': dit(); dah(); dah(); dah(); break;
    case 'K': case 'k': dah(); dit(); dah(); break;
    case 'L': case 'l': dit(); dah(); dit(); dit(); break;
    case 'M': case 'm': dah(); dah(); break;
    case 'N': case 'n': dah(); dit(); break;
    case 'O': case 'o': dah(); dah(); dah(); break;
    case 'P': case 'p': dit(); dah(); dah(); dit(); break;
    case 'Q': case 'q': dah(); dah(); dit(); dah(); break;
    case 'R': case 'r': dit(); dah(); dit(); break;
    case 'S': case 's': dit(); dit(); dit(); break;
    case 'T': case 't': dah(); break;
    case 'U': case 'u': dit(); dit(); dah(); break;
    case 'V': case 'v': dit(); dit(); dit(); dah(); break;
    case 'W': case 'w': dit(); dah(); dah(); break;
    case 'X': case 'x': dah(); dit(); dit(); dah(); break;
    case 'Y': case 'y': dah(); dit(); dah(); dah(); break;
    case 'Z': case 'z': dah(); dah(); dit(); dit(); break;
    case ' ': iwg(); break; // intra-word gap (space)
    case '0': dah(); dah(); dah(); dah(); dah(); break;
    case '1': dit(); dah(); dah(); dah(); dah(); break;
    case '2': dit(); dit(); dah(); dah(); dah(); break;
    case '3': dit(); dit(); dit(); dah(); dah(); break;
    case '4': dit(); dit(); dit(); dit(); dah(); break;
    case '5': dit(); dit(); dit(); dit(); dit(); break;
    case '6': dah(); dit(); dit(); dit(); dit(); break;
    case '7': dah(); dah(); dit(); dit(); dit(); break;
    case '8': dah(); dah(); dah(); dit(); dit(); break;
    case '9': dah(); dah(); dah(); dah(); dit(); break;
    default: break; // no punctuation
  }
  icg(); // intra-character gap
}

hum - it's not having them and using local variables in the case that creates the issue. having those doesn't hurt (as long as the programmer understands what compound statements are for)

1 Like

ClangFormat enforces this structure by design and the maintainers have consistently rejected feature requests to support multi-statement lines, considering them not a general practice practice (or even a bad one)

An example @ [cfe-dev] clang-format: Multiple statements in line

I'm not interested in reformatting the print statements to sprintf or going through all of that, when I have multiple debugging printouts, other formats don't help. I have many instances of things like:

Serial.print(F(" NewMode = ")); Serial.print(ModeToString(NewMode));Serial.print(F(" LastMode = ")); Serial.println(ModeToString(LastMode));

all on one line that allows me to comment out one line rather than many.

Looks like the

// clang-format off
  Serial.print("Hallo"); Serial.println(" world");
// clang-format on

is the only solution. I can't do it everywhere, not worth it.
Not answer that I'd hoped for...

Jackson, thank you for the comment. I was sure that I'm not the first to ask.

Sir Michael

Most style guides discourage multiple statements on a single line because it reduces code clarity and maintainability. Automatically allowing it would encourage less readable code...

Also, as this is not in high demand, it's seen as an orphaned feature risk (adding a feature that is used by very few people or in very specific cases, which then doesn’t get proper ongoing support or updates. Over time, as the main codebase evolves, this rarely used feature might break or become incompatible because maintainers focus on the core functionality).

That being said, you could have a simple templated debug function that would let you do what you want in one call, so will stay on one line

void debug() {
  Serial.println();
}

template<typename T, typename... Args>
void debug(T first, Args... args) {
  Serial.print(first);
  Serial.write(' ');
  debug(args...);
}

int f() {
  return 42;
}

void setup() {
  Serial.begin(115200);
  debug("Hi there");
  debug("first", F("second"));
  debug(F("Hello =>"), 2, 3, f());
}

void loop() {}

➜ you would call

debug(F(" NewMode ="), ModeToString(NewMode), F("LastMode ="), ModeToString(LastMode));

now you have one line :slight_smile:


For switch-case formatting, the closest applicable option is probably to explore what you can do with AllowShortBlocksOnASingleLine: Always , which allows braces with content to stay on a single line. However, ClangFormat will still place the case label on its own line and indent the block as it's the typical way people do things.

1 Like

Jackson,

I LIKE that solution!
Quick Question: Can you explain the:

template<typename T, typename... Args>

Also, why the:

  Serial.print(first);  
  Serial.write(' ');  //Should this be Serial.print(' '); also??? 

Anyway, I can use:

debug(F("Hello =>"), F("Byte:"), bValue, F("int:"), iValue, F("String:"), sValue, F("float:"), fValue);

and it's easy to construct and comment out! And I don't need to add separation spaces.

Many Thanks!

Sir Michael

When i can not force the IDE to allow multiple lines, i create a ".h" file with the taaaall lines of code, leaving my main code with one line to a function call.

I'm not sure I understand.

Is that a solution because you refrain from auto formatting the entire content of that .h file (either by simply refraining from doing so, or else by adding a ClangFormat control comment a la post #5, or just remembering to not trigger an "Auto Format" operation while the tab for the .h file is selected)?

The "print many" template first came up two months ago in this forum topic: Simple Macro program I think

print just calls write

size_t Print::print(char c)
{
  return write(c);
}

so you save a function call. write is the underlying interface that actually does the writing of bytes; print and println add the overloads for all the different types, including integers with HEX, floating point with decimal places, F()-strings, etc

I'll need to reformat my Serial.print() statement into the debug() format, but it allows me to more easily construct the debug statements that I need to troubleshoot my code, and when I'm done, I can comment out one line to get rid of it, or alternatively for those that are not commented out, I can add a single "return" in the debug() function and it won't print anything out.

It's still a bit if work for me, but I like the solution.

Sir Michael

Sorry, I should have made it more clear that my reply was in response to what @xfpd wrote:

I apologize for the confusion. I fully understand your situation.

I autoformat the ".h" which becomes clang standard formatting (every line, only one command... 36 lines * 6 commands per line). In my case with Post #8, the .h would be (36 lines * 6 commands per line) tall, leaving a few lines of the algorithm and some housekeeping in the .ino.

  • FYI

//Miscellaneous
//================================================
//See https://github.com/janelia-arduino/Streaming to use a streaming libray
//example: Serial << "Hex: " << _HEX (a) << endl;
//
//the following can be useful too.
//https://forum.arduino.cc/t/sketch-print-concatenated-variables/1376887/2
//https://youtu.be/I-hZkUa9mIs?si=4sJoqrP3n8m0GkOc
//examples:
//Serial << "Hello World, " << "This is my variable: " << 5 << "\n";
//Serial << F("millis() is currently = ") << millis() << F(" bigNumber = ") << bigNumber << "\n";
//
template<typename TYPE>
inline Print &operator<<(Print &Outport, TYPE arg)
{
  Outport.print(arg);
  return Outport;
}