gitlab.com/greut/eclint@v0.5.2-0.20240402114752-14681fe6e0bf/definition.go (about)

     1  package eclint
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/editorconfig/editorconfig-core-go/v2"
    10  )
    11  
    12  // ErrNotImplemented represents a missing feature.
    13  var ErrNotImplemented = errors.New("not implemented yet, PRs are welcome")
    14  
    15  // definition contains the fields that aren't native to EditorConfig.Definition.
    16  type definition struct {
    17  	editorconfig.Definition
    18  	BlockCommentStart  []byte
    19  	BlockComment       []byte
    20  	BlockCommentEnd    []byte
    21  	MaxLength          int
    22  	TabWidth           int
    23  	IndentSize         int
    24  	LastLine           []byte
    25  	LastIndex          int
    26  	InsideBlockComment bool
    27  }
    28  
    29  func newDefinition(d *editorconfig.Definition) (*definition, error) { //nolint:cyclop
    30  	def := &definition{
    31  		Definition: *d,
    32  		TabWidth:   d.TabWidth,
    33  	}
    34  
    35  	// Backward compatibility with buggy core.
    36  	if def.Charset == "utf-8 bom" {
    37  		def.Charset = "utf-8-bom"
    38  	}
    39  
    40  	if d.IndentSize != "" && d.IndentSize != UnsetValue {
    41  		is, err := strconv.Atoi(d.IndentSize)
    42  		if err != nil {
    43  			return nil, fmt.Errorf("cannot convert indentsize %q to int: %w", d.IndentSize, err)
    44  		}
    45  
    46  		def.IndentSize = is
    47  	}
    48  
    49  	if def.IndentStyle != "" && def.IndentStyle != UnsetValue { //nolint:nestif
    50  		bs, ok := def.Raw["block_comment_start"]
    51  		if ok && bs != "" && bs != UnsetValue {
    52  			def.BlockCommentStart = []byte(bs)
    53  			bc, ok := def.Raw["block_comment"]
    54  
    55  			if ok && bc != "" && bs != UnsetValue {
    56  				def.BlockComment = []byte(bc)
    57  			}
    58  
    59  			be, ok := def.Raw["block_comment_end"]
    60  			if !ok || be == "" || be == UnsetValue {
    61  				return nil, fmt.Errorf(
    62  					"%w: .editorconfig: block_comment_end was expected, none were found",
    63  					ErrConfiguration,
    64  				)
    65  			}
    66  
    67  			def.BlockCommentEnd = []byte(be)
    68  		}
    69  	}
    70  
    71  	if mll, ok := def.Raw["max_line_length"]; ok && mll != "off" && mll != UnsetValue {
    72  		ml, er := strconv.Atoi(mll)
    73  		if er != nil || ml < 0 {
    74  			return nil, fmt.Errorf(
    75  				"%w: .editorconfig: max_line_length expected a non-negative number, got %q",
    76  				ErrConfiguration,
    77  				mll,
    78  			)
    79  		}
    80  
    81  		def.MaxLength = ml
    82  
    83  		if def.TabWidth <= 0 {
    84  			def.TabWidth = DefaultTabWidth
    85  		}
    86  	}
    87  
    88  	return def, nil
    89  }
    90  
    91  // EOL returns the byte value of the given definition.
    92  func (def *definition) EOL() ([]byte, error) {
    93  	switch def.EndOfLine {
    94  	case editorconfig.EndOfLineCr:
    95  		return []byte{cr}, nil
    96  	case editorconfig.EndOfLineCrLf:
    97  		return []byte{cr, lf}, nil
    98  	case editorconfig.EndOfLineLf:
    99  		return []byte{lf}, nil
   100  	default:
   101  		return nil, fmt.Errorf("%w: unsupported EndOfLine value %s", ErrConfiguration, def.EndOfLine)
   102  	}
   103  }
   104  
   105  // OverrideDefinitionUsingPrefix is an helper that takes the prefixed values.
   106  //
   107  // It replaces those values into the nominal ones. That way a tool could a
   108  // different set of definition than the real editor would.
   109  func OverrideDefinitionUsingPrefix(def *editorconfig.Definition, prefix string) error {
   110  	for k, v := range def.Raw {
   111  		if strings.HasPrefix(k, prefix) {
   112  			nk := k[len(prefix):]
   113  			def.Raw[nk] = v
   114  
   115  			switch nk {
   116  			case "indent_style":
   117  				def.IndentStyle = v
   118  			case "indent_size":
   119  				def.IndentSize = v
   120  			case "charset":
   121  				def.Charset = v
   122  			case "end_of_line":
   123  				def.EndOfLine = v
   124  			case "tab_width":
   125  				i, err := strconv.Atoi(v)
   126  				if err != nil {
   127  					return fmt.Errorf("tab_width cannot be set. %w", err)
   128  				}
   129  
   130  				def.TabWidth = i
   131  			case "trim_trailing_whitespace":
   132  				return fmt.Errorf("%v cannot be overridden: %w", nk, ErrNotImplemented)
   133  			case "insert_final_newline":
   134  				return fmt.Errorf("%v cannot be overridden: %w", nk, ErrNotImplemented)
   135  			}
   136  		}
   137  	}
   138  
   139  	return nil
   140  }