<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/linter"
	"github.com/spf13/cobra"
)

// lintCmd represents the lint command
var lintCmd = &cobra.Command{
	Use:   "lint",
	Short: "Lint source file",
	Long:  `Lint source file`,
	Run: func(cmd *cobra.Command, args []string) {
		format := cmd.Flag("format").Value.String()

		switch format {
		case "json":
			fmt.Print("[")
		}

		files := args

		for index, filePath := range files {
			file, err := os.Open(filePath)
			if err != nil {
				panic(err)
			}

			lintError, err := linter.Lint(file)
			if err != nil {
				panic(err)
			}

			switch format {
			case "text":
				for _, lintError := range lintError {
					fmt.Println(formatParseError(
						filePath,
						lintError.Position,
						lintError.CodeLine,
						lintError.Message,
					))
				}
			case "json":
				normalizedFilePath, err := filepath.Abs(filePath)
				if err != nil {
					panic(err)
				}
				json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
					"Filename": normalizedFilePath,
					"Errors":   lintError,
				})
			default:
				panic("Unknown output format")
			}

			if index != len(files)-1 {
				switch format {
				case "json":
					fmt.Print(",")
				}
			}
		}

		switch format {
		case "json":
			fmt.Println("]")
		}
	},
}

func formatParseError(filePath string, pos ast.Position, line string, err string) string {
	if line != "" {
		return fmt.Sprintf(
			`%s:%d:%d
----------------------------------------------------------
%s
%s
%s
----------------------------------------------------------`, filePath, pos.Line+1, pos.Column+1, strings.Replace(line, "\t", " ", -1), pointer(pos.Column), pad(pos.Column-int(len(err)/2), err))
	}
	return fmt.Sprintf("%s:%d:%d %s", filePath, pos.Line+1, pos.Column+1, err)
}

func pad(padding int, str string) (res string) {
	if padding < 0 {
		padding = 0
	}
	res = strings.Repeat(" ", padding)
	return res + str
}

func pointer(padding int) (res string) {
	return pad(padding, "^")
}
<commit_msg>Center arrow position<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/linter"
	"github.com/spf13/cobra"
)

// lintCmd represents the lint command
var lintCmd = &cobra.Command{
	Use:   "lint",
	Short: "Lint source file",
	Long:  `Lint source file`,
	Run: func(cmd *cobra.Command, args []string) {
		format := cmd.Flag("format").Value.String()

		switch format {
		case "json":
			fmt.Print("[")
		}

		files := args

		for index, filePath := range files {
			file, err := os.Open(filePath)
			if err != nil {
				panic(err)
			}

			lintError, err := linter.Lint(file)
			if err != nil {
				panic(err)
			}

			switch format {
			case "text":
				for _, lintError := range lintError {
					fmt.Println(formatParseError(
						filePath,
						lintError.Position,
						lintError.EndPosition,
						lintError.CodeLine,
						lintError.Message,
					))
				}
			case "json":
				normalizedFilePath, err := filepath.Abs(filePath)
				if err != nil {
					panic(err)
				}
				json.NewEncoder(os.Stdout).Encode(map[string]interface{}{
					"Filename": normalizedFilePath,
					"Errors":   lintError,
				})
			default:
				panic("Unknown output format")
			}

			if index != len(files)-1 {
				switch format {
				case "json":
					fmt.Print(",")
				}
			}
		}

		switch format {
		case "json":
			fmt.Println("]")
		}
	},
}

func formatParseError(filePath string, pos ast.Position, endPos ast.Position, line string, err string) string {
	if line != "" {
		pointerPos := pos.Column

		if endPos.Line == pos.Line {
			pointerPos = int((pointerPos + endPos.Column) / 2)
		}

		return fmt.Sprintf(
			`%s:%d:%d
----------------------------------------------------------
%s
%s
%s
----------------------------------------------------------`, filePath, pos.Line+1, pos.Column+1, strings.Replace(line, "\t", " ", -1), pointer(pointerPos), pad(pos.Column-int(len(err)/2), err))
	}
	return fmt.Sprintf("%s:%d:%d %s", filePath, pos.Line+1, pos.Column+1, err)
}

func pad(padding int, str string) (res string) {
	if padding < 0 {
		padding = 0
	}
	res = strings.Repeat(" ", padding)
	return res + str
}

func pointer(padding int) (res string) {
	return pad(padding, "^")
}

{'fraction_non_alphanumeric': 0.20551724137931035, 'fraction_numerical': 0.0052873563218390806, 'mean_word_length': 2.439525691699605, 'pattern_counts': {'":': 14, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '592600', 'n_tokens_mistral': 1804, 'n_tokens_neox': 1514, 'n_words': 344}