Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

An awk Program to Indent Source

5.00/5 (4 votes)
3 Aug 2021CPOL1 min read 11.2K  
On certain occasions, Visual Studio does not indent as intended so here is an awk program which does the trick.
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.

C++
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.

C++
}
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.

C++
}
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)