In this tip, you will see an awk program that fixes the problem of VS not indenting code in C++ as intended when previous line does not end in a semi-colon.
Introduction
Visual Studio auto-indent on C++ files does not work as I intend if the previous line does not end in a semi-colon. This can result in intolerably indented code as can be seen below. The awk program here solves it easily and can be automated for the current document via a batch file invoked via Tools->External Tools. The same file post-processing is shown below. You, of course, can modify the code to utilize the indent string you prefer and can set the block open/close characters to that which your language requires. Be sure to invoke this on a saved file, otherwise you will be prompted to choose if you wish to reload it i.e. the processed file and lose any prior changes you have edited. Please note the code has been updated 7/21/2021 to repair a bug. Please note the code has been updated 8/3/2021 to optionally indent class member access specifiers at the enclosing brace level. Also the code has been enhanced to optionally search for unmatched braces and parentheses.
BEGIN {
# to indent the member access specifiers at the same level as the enclosing brace set below to 1
# to indent the member access specifiers at the same level as the enclosed code set below to 0
MEMBER_ACCESS_ENCLOSING_BRACE_LEVEL = 1
# to search for unmatched braces or parentheses set below to 1 else 0
# when searching the current brace depth and current parentheses depth will be displayed following each line of code
SEARCH_UNMATCHED = 0
INDENT_STRING = "\t"
INCREASE_INDENT_CHARACTER = "{"; DECREASE_INDENT_CHARACTER = "}"
MEMBER_ACCESS_STRINGS[0] = "private:"; MEMBER_ACCESS_STRINGS[1] = "PRIVATE:"; MEMBER_ACCESS_STRINGS[2] = "public:"; MEMBER_ACCESS_STRINGS[3] = "PUBLIC:"; MEMBER_ACCESS_STRINGS[4] = "protected:"; MEMBER_ACCESS_STRINGS[5] = "PROTECTED:";
PRIVATE_LENGTH = 8 # length of "private:"
PUBLIC_LENGTH = 7 # length of "public:"
PROTECTED_LENGTH = 10 # length of "protected:"
OPEN_PAREN = "("; CLOSE_PAREN = ")";
INDENT_DEPTH = 0
PAREN_DEPTH = 0
NEED_NEWLINE = 0
}
END {
# to only obtain a final value of unmatched braces and parentheses the line below can be uncommented and the above value "SEARCH_UNMATCHED" set to 0
# if(SEARCH_UNMATCHED != 0) printf("\nFINAL: INDENT_DEPTH %d PAREN_DEPTH %d", INDENT_DEPTH, PAREN_DEPTH)
}
{OPEN = NUMBER_OF_OPEN(); INDENT_DEPTH += OPEN}
{CLOSE = NUMBER_OF_CLOSE(); INDENT_DEPTH -= CLOSE}
{PAREN_DEPTH += NUMBER_OF(OPEN_PAREN)}
{PAREN_DEPTH -= NUMBER_OF(CLOSE_PAREN)}
{INDENT()}
{if(SEARCH_UNMATCHED) {if (NEED_NEWLINE != 0) printf("\n"); printf("INDENT DEPTH %d PAREN DEPTH %d ", INDENT_DEPTH, PAREN_DEPTH); NEED_NEWLINE = 1} }
function NUMBER_OF_OPEN() { return NUMBER_OF(INCREASE_INDENT_CHARACTER) }
function NUMBER_OF_CLOSE() { return NUMBER_OF(DECREASE_INDENT_CHARACTER) }
function NUMBER_OF(_SUB_STRING)
{
N = 0
INDEX = 0
STRING = $0
while ((INDEX = index(STRING, _SUB_STRING)) > 0)
{
++N
if (INDEX < length(STRING))
STRING = substr(STRING, INDEX + 1)
else
break
}
return N
}
function INDENT()
{
if (NEED_NEWLINE != 0) printf("\n")
if (length($0) == 0) { printf("\n"); NEED_NEWLINE = 0 }
else
{
# find first non - blank character
if (match($0, "[^[:blank:]]"))
{
# RSTART is where the match of the pattern starts
# RLENGTH is the length of the matched pattern
# before = substr($0, 1, RSTART - 1)
# after = substr($0, RSTART + RLENGTH)
matched_pattern = substr($0, RSTART)
if (MEMBER_ACCESS_ENCLOSING_BRACE_LEVEL != 0 && MEMBER_ACCESS_CONTROL(matched_pattern) != 0)
{
for (i = 1; i < INDENT_DEPTH; ++i) printf("%s", INDENT_STRING)
printf("%s", matched_pattern)
NEED_NEWLINE = 1
}
else
{
i = 0;
if (OPEN > CLOSE) i = 1
for (; i < INDENT_DEPTH; ++i) printf("%s", INDENT_STRING)
printf("%s", matched_pattern)
NEED_NEWLINE = 1
}
}
}
}
function MEMBER_ACCESS_CONTROL(_STRING)
{
for (i = 0; i < 2; ++i)
if (substr(_STRING, 0, PRIVATE_LENGTH) == MEMBER_ACCESS_STRINGS[i]) return 1
for (i = 2; i < 4; ++i)
if (substr(_STRING, 0, PUBLIC_LENGTH) == MEMBER_ACCESS_STRINGS[i]) return 1
for (i = 4; i < 6; ++i)
if (substr(_STRING, 0, PROTECTED_LENGTH) == MEMBER_ACCESS_STRINGS[i]) return 1
return 0
}
Before processing with the above awk program liberally copied from Mr. Bruce Barnett: Correcting this manually for a five-hundred line file is intolerable.
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const unique_ptr<scalarType[]>&buffer, size_t count)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer, count);
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const csmart_unique_ptr<scalarType[]>&buffer, size_t count)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer, count);
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const csmart_unique_ptr<scalarType[]>&buffer)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer);
}
this_type& write(const void* buffer, size_t nbytes)
{
HANDLE_NO_HANDLE
if (nbytes == 0) return *this;
DEBUG_ASSERT(buffer != nullptr, nullptr_msg, ARGUMENT_VA_ARG(nbytes));
DWORD nbytes_written = 0;
osapi::WriteFile(m_handle, buffer, nbytes, &nbytes_written, NULL);
return *this;
The same lines after processing with the above awk program: Voila! All with a mere two clicks via Tools.
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const unique_ptr<scalarType[]>&buffer, size_t count)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer, count);
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const csmart_unique_ptr<scalarType[]>&buffer, size_t count)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer, count);
}
template<typename scalarType> requires CONCEPT_scalar<scalarType>
this_type& write(file_offset_type offset,
const csmart_unique_ptr<scalarType[]>&buffer)
{
HANDLE_NO_HANDLE
seekp(offset);
return write(buffer);
}
this_type& write(const void* buffer, size_t nbytes)
{
HANDLE_NO_HANDLE
if (nbytes == 0) return *this;
DEBUG_ASSERT(buffer != nullptr, nullptr_msg, ARGUMENT_VA_ARG(nbytes));
DWORD nbytes_written = 0;
osapi::WriteFile(m_handle, buffer, nbytes, &nbytes_written, NULL);
return *this;
}
this_type& write(const_byte_ptr_type buffer, size_t nbytes)
{
HANDLE_NO_HANDLE
if (nbytes == 0) return *this;
History
- 13th July, 2021: Initial version