<commit_before>// Copyright 2020 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// ----------------

// Package dumbindent formats C (and C-like) programs.
//
// It is similar in concept to pretty-printers like `indent` or `clang-format`.
// It is much dumber (it will not add line breaks or otherwise re-flow lines of
// code, not to fit within an 80 character limit nor for any other reason) but
// it can therefore be much, much faster at the basic task of automatically
// indenting nested blocks. The output isn't 'perfect', but it's usually
// sufficiently readable if the input already has sensible line breaks.
//
// See `cmd/dumbindent/main.go` in this repository for an example where
// `dumbindent` was 70 times faster than `clang-format`.
//
// There are no configuration options (e.g. tabs versus spaces).
//
// Known bug: it cannot handle /* slash-star comments */ or multi-line strings
// yet. This is tracked at https://github.com/google/wuffs/issues/31
package dumbindent

import (
	"bytes"
	"errors"
)

// 'Constants', but their type is []byte, not string.
var (
	externC = []byte("extern \"C\"")
	spaces  = []byte("                                ")
)

// Global state.
var (
	nBraces   int  // The number of unbalanced '{'s.
	nParens   int  // The number of unbalanced '('s.
	openBrace bool // Whether the previous line ends with '{'.
	hanging   bool // Whether the previous line ends with '=' or '\\'.
)

// hangingBytes is a look-up table for updating the hanging global variable.
var hangingBytes = [256]bool{
	'=':  true,
	'\\': true,
}

// Format formats the C (or C-like) program in src.
func Format(src []byte) (dst []byte, retErr error) {
	dst = make([]byte, 0, len(src)+(len(src)/2))
	blankLine := false
	for line, remaining := src, []byte(nil); len(src) > 0; src = remaining {
		line, remaining = src, nil
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, remaining = line[:i], line[i+1:]
		}
		line = trimSpace(line)

		// Collapse 2 or more consecutive blank lines into 1. Also strip any
		// blank lines:
		//  - immediately after a '{',
		//  - immediately before a '}',
		//  - at the end of file.
		if len(line) == 0 {
			blankLine = true
			continue
		}
		if blankLine {
			blankLine = false
			if !openBrace && (line[0] != '}') {
				dst = append(dst, '\n')
			}
		}

		// Preprocessor lines (#ifdef, #pragma, etc) are never indented.
		if (line[0] == '#') ||
			((line[0] == 'e') && bytes.HasPrefix(line, externC)) ||
			((line[0] == '}') && bytes.HasSuffix(line, externC)) {
			dst = append(dst, line...)
			dst = append(dst, '\n')
			openBrace = false
			hanging = lastNonWhiteSpace(line) == '\\'
			continue
		}

		// Account for leading '}'s before we print the line's indentation.
		closeBraces := 0
		for ; (closeBraces < len(line)) && line[closeBraces] == '}'; closeBraces++ {
		}
		nBraces -= closeBraces

		// When debugging, uncomment these (and import "strconv") to prefix
		// every non-blank line with the global state.
		//
		// dst = append(dst, strconv.Itoa(nBraces)...)
		// dst = append(dst, ',')
		// dst = append(dst, strconv.Itoa(nParens)...)
		// if openBrace {
		//   dst = append(dst, '{')<commit_msg>Add dumbindent comment about `extern "C"`<commit_after>// Copyright 2020 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// ----------------

// Package dumbindent formats C (and C-like) programs.
//
// It is similar in concept to pretty-printers like `indent` or `clang-format`.
// It is much dumber (it will not add line breaks or otherwise re-flow lines of
// code, not to fit within an 80 character limit nor for any other reason) but
// it can therefore be much, much faster at the basic task of automatically
// indenting nested blocks. The output isn't 'perfect', but it's usually
// sufficiently readable if the input already has sensible line breaks.
//
// See `cmd/dumbindent/main.go` in this repository for an example where
// `dumbindent` was 70 times faster than `clang-format`.
//
// There are no configuration options (e.g. tabs versus spaces).
//
// Known bug: it cannot handle /* slash-star comments */ or multi-line strings
// yet. This is tracked at https://github.com/google/wuffs/issues/31
package dumbindent

import (
	"bytes"
	"errors"
)

// 'Constants', but their type is []byte, not string.
var (
	externC = []byte("extern \"C\"")
	spaces  = []byte("                                ")
)

// Global state.
var (
	nBraces   int  // The number of unbalanced '{'s.
	nParens   int  // The number of unbalanced '('s.
	openBrace bool // Whether the previous line ends with '{'.
	hanging   bool // Whether the previous line ends with '=' or '\\'.
)

// hangingBytes is a look-up table for updating the hanging global variable.
var hangingBytes = [256]bool{
	'=':  true,
	'\\': true,
}

// Format formats the C (or C-like) program in src.
func Format(src []byte) (dst []byte, retErr error) {
	dst = make([]byte, 0, len(src)+(len(src)/2))
	blankLine := false
	for line, remaining := src, []byte(nil); len(src) > 0; src = remaining {
		line, remaining = src, nil
		if i := bytes.IndexByte(line, '\n'); i >= 0 {
			line, remaining = line[:i], line[i+1:]
		}
		line = trimSpace(line)

		// Collapse 2 or more consecutive blank lines into 1. Also strip any
		// blank lines:
		//  - immediately after a '{',
		//  - immediately before a '}',
		//  - at the end of file.
		if len(line) == 0 {
			blankLine = true
			continue
		}
		if blankLine {
			blankLine = false
			if !openBrace && (line[0] != '}') {
				dst = append(dst, '\n')
			}
		}

		// Preprocessor lines (#ifdef, #pragma, etc) are never indented.
		//
		// The '{' and '}' for an `extern "C"` are also special cased not to
		// change indentation inside the block. This assumes that the closing
		// brace is followed by a `// extern "C"` comment.
		if (line[0] == '#') ||
			((line[0] == 'e') && bytes.HasPrefix(line, externC)) ||
			((line[0] == '}') && bytes.HasSuffix(line, externC)) {
			dst = append(dst, line...)
			dst = append(dst, '\n')
			openBrace = false
			hanging = lastNonWhiteSpace(line) == '\\'
			continue
		}

		// Account for leading '}'s before we print the line's indentation.
		closeBraces := 0
		for ; (closeBraces < len(line)) && line[closeBraces] == '}'; closeBraces++ {
		}
		nBraces -= closeBraces

		// When debugging, uncomment these (and import "strconv") to prefix
		// every non-blank line with the global state.
		//
		// dst = append(dst, strconv.Itoa(nBraces)...)
		// dst = append(dst, ',')
		// dst = append(dst, strconv.Itoa(nParens)...)
		// if openBrace {
		//   dst = append(dst, '{')
{'fraction_non_alphanumeric': 0.14804139918773745, 'fraction_numerical': 0.007860605266605529, 'mean_word_length': 2.8653164556962025, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1435867', 'n_tokens_mistral': 2553, 'n_tokens_neox': 2346, 'n_words': 913}